diff options
Diffstat (limited to 'src')
288 files changed, 39396 insertions, 17775 deletions
diff --git a/src/.asan-blacklist b/src/.asan-blacklist index 928d81bd5a..9d7f721a88 100644 --- a/src/.asan-blacklist +++ b/src/.asan-blacklist @@ -1,3 +1,7 @@ # multiqueue.h pointer arithmetic is not accepted by asan fun:multiqueue_node_data fun:tv_dict_watcher_node_data + +# Allocation in loop_schedule_deferred() is freed by loop_deferred_event(), but +# this sometimes does not happen during teardown. +func:loop_schedule_deferred diff --git a/src/clint.py b/src/clint.py index 69a061d2ab..79ab91ebe1 100755 --- a/src/clint.py +++ b/src/clint.py @@ -201,6 +201,7 @@ _ERROR_CATEGORIES = [ 'runtime/printf', 'runtime/printf_format', 'runtime/threadsafe_fn', + 'runtime/deprecated', 'syntax/parenthesis', 'whitespace/alignment', 'whitespace/blank_line', @@ -2123,8 +2124,10 @@ def CheckExpressionAlignment(filename, clean_lines, linenum, error, startpos=0): + (level_starts[depth][2] == '{')): if depth not in ignore_error_levels: error(filename, linenum, 'whitespace/alignment', 2, - 'Inner expression should be aligned ' - 'as opening brace + 1 (+ 2 in case of {)') + ('Inner expression should be aligned ' + 'as opening brace + 1 (+ 2 in case of {{). ' + 'Relevant opening is on line {0!r}').format( + level_starts[depth][3])) prev_line_start = pos elif brace == 'e': pass @@ -2141,7 +2144,8 @@ def CheckExpressionAlignment(filename, clean_lines, linenum, error, startpos=0): ignore_error_levels.add(depth) line_ended_with_opening = ( pos == len(line) - 2 * (line.endswith(' \\')) - 1) - level_starts[depth] = (pos, line_ended_with_opening, brace) + level_starts[depth] = (pos, line_ended_with_opening, brace, + linenum) if line_ended_with_opening: depth_line_starts[depth] = (prev_line_start, brace) else: @@ -2531,6 +2535,7 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): r'(?<!\bkhash_t)' r'(?<!\bkbtree_t)' r'(?<!\bkbitr_t)' + r'(?<!\bPMap)' r'\((?:const )?(?:struct )?[a-zA-Z_]\w*(?: *\*(?:const)?)*\)' r' +' r'-?(?:\*+|&)?(?:\w+|\+\+|--|\()', cast_line) @@ -2613,7 +2618,8 @@ def CheckBraces(filename, clean_lines, linenum, error): func_start_linenum += 1 else: - if clean_lines.lines[func_start_linenum].endswith('{'): + func_start = clean_lines.lines[func_start_linenum] + if not func_start.startswith('enum ') and func_start.endswith('{'): error(filename, func_start_linenum, 'readability/braces', 5, 'Brace starting function body must be placed ' @@ -3198,6 +3204,14 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension, if match: error(filename, linenum, 'runtime/printf', 4, 'Use xstrlcat or snprintf instead of %s' % match.group(1)) + if not Search(r'eval/typval\.[ch]$', filename): + match = Search(r'(?:\.|->)' + r'(?:lv_(?:first|last|refcount|len|watch|idx(?:_item)?' + r'|copylist|lock)' + r'|li_(?:next|prev|tv))\b', line) + if match: + error(filename, linenum, 'runtime/deprecated', 4, + 'Accessing list_T internals directly is prohibited') # Check for suspicious usage of "if" like # } if (a == b) { diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index c46c0bed6d..606baff619 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -10,6 +10,14 @@ if(USE_GCOV) endif() endif() +if(WIN32) + # tell MinGW compiler to enable wmain + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -municode") +elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework CoreFoundation") + set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -framework CoreFoundation") +endif() + set(TOUCHES_DIR ${PROJECT_BINARY_DIR}/touches) set(GENERATOR_DIR ${CMAKE_CURRENT_LIST_DIR}/generators) set(GENERATED_DIR ${PROJECT_BINARY_DIR}/src/nvim/auto) @@ -81,6 +89,8 @@ foreach(subdir event eval lua + viml + viml/parser ) if(${subdir} MATCHES "tui" AND NOT FEAT_TUI) continue() @@ -111,6 +121,9 @@ foreach(sfile ${NVIM_SOURCES}) if(WIN32 AND ${f} MATCHES "^(pty_process_unix.c)$") list(APPEND to_remove ${sfile}) endif() + if(NOT WIN32 AND ${f} MATCHES "^(pty_process_win.c)$") + list(APPEND to_remove ${sfile}) + endif() endforeach() list(REMOVE_ITEM NVIM_SOURCES ${to_remove}) @@ -156,13 +169,13 @@ if(NOT MSVC) endif() endif() -if(DEFINED MIN_LOG_LEVEL) +if(NOT "${MIN_LOG_LEVEL}" MATCHES "^$") add_definitions(-DMIN_LOG_LEVEL=${MIN_LOG_LEVEL}) endif() get_directory_property(gen_cdefs COMPILE_DEFINITIONS) foreach(gen_cdef ${gen_cdefs} DO_NOT_DEFINE_EMPTY_ATTRIBUTES) - if(NOT "${gen_cdef}" MATCHES "INCLUDE_GENERATED_DECLARATIONS") + if(NOT ${gen_cdef} MATCHES "INCLUDE_GENERATED_DECLARATIONS") list(APPEND gen_cflags "-D${gen_cdef}") endif() endforeach() @@ -174,6 +187,10 @@ get_directory_property(gen_includes INCLUDE_DIRECTORIES) foreach(gen_include ${gen_includes} ${LUA_PREFERRED_INCLUDE_DIRS}) list(APPEND gen_cflags "-I${gen_include}") endforeach() +if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_OSX_SYSROOT) + list(APPEND gen_cflags "-isysroot") + list(APPEND gen_cflags "${CMAKE_OSX_SYSROOT}") +endif() string(TOUPPER "${CMAKE_BUILD_TYPE}" build_type) separate_arguments(C_FLAGS_ARRAY UNIX_COMMAND ${CMAKE_C_FLAGS}) separate_arguments(C_FLAGS_${build_type}_ARRAY UNIX_COMMAND ${CMAKE_C_FLAGS_${build_type}}) @@ -350,6 +367,10 @@ if(Iconv_LIBRARIES) list(APPEND NVIM_LINK_LIBRARIES ${Iconv_LIBRARIES}) endif() +if(WIN32) + list(APPEND NVIM_LINK_LIBRARIES ${WINPTY_LIBRARIES}) +endif() + # Put these last on the link line, since multiple things may depend on them. list(APPEND NVIM_LINK_LIBRARIES ${LIBUV_LIBRARIES} @@ -415,6 +436,7 @@ if(WIN32) COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/tee.exe" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/tidy.exe" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/win32yank.exe" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ + COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/winpty-agent.exe" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/D3Dcompiler_47.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/libEGL.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ @@ -428,10 +450,13 @@ if(WIN32) COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/Qt5Network.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/Qt5Svg.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/Qt5Widgets.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ + COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/winpty.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/platforms/qwindows.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/platforms/ ) add_dependencies(nvim_runtime_deps external_blobs) +else() + add_custom_target(nvim_runtime_deps) # Stub target to avoid CMP0046. endif() add_library( @@ -526,11 +551,7 @@ endfunction() set(NO_SINGLE_CHECK_HEADERS os/win_defs.h - regexp_defs.h - syntax_defs.h - terminal.h - undo.h - undo_defs.h + os/pty_process_win.h ) foreach(hfile ${NVIM_HEADERS}) get_test_target(test-includes "${hfile}" relative_path texe) diff --git a/src/nvim/README.md b/src/nvim/README.md index 3032913500..d668db0cdc 100644 --- a/src/nvim/README.md +++ b/src/nvim/README.md @@ -1,14 +1,13 @@ -## Source code overview +Nvim core +========= -This document is an overview of how Nvim works internally, focusing on parts -that are different from Vim. Since Nvim inherited from Vim, some information in -[its README](https://raw.githubusercontent.com/vim/vim/master/src/README.txt) -still applies. +Module-specific details are documented at the top of each module (`terminal.c`, +`screen.c`, …). -For module-specific details, read the source code. Some files are extensively -commented at the top (e.g. terminal.c, screen.c). +See `:help dev` for guidelines. -### Source file name conventions +Filename conventions +-------------------- The source files use extensions to hint about their purpose. @@ -19,12 +18,98 @@ The source files use extensions to hint about their purpose. - `*.h.generated.h` - exported functions’ declarations. - `*.c.generated.h` - static functions’ declarations. -### Top-level program loops +Logs +---- -Let's understand what a Vim-like program does by analyzing the workflow of -a typical editing session: +Low-level log messages sink to `$NVIM_LOG_FILE`. -01. Vim dispays the welcome screen +You can use `LOG_CALLSTACK();` anywhere in the source to log the current +stacktrace. To log in an alternate file, e.g. stderr, use +`LOG_CALLSTACK_TO_FILE(FILE*)`. (Currently Linux-only.) + +UI events are logged at level 0 (`DEBUG_LOG_LEVEL`). + + rm -rf build/ + make CMAKE_EXTRA_FLAGS="-DMIN_LOG_LEVEL=0" + +Build with ASAN +--------------- + +Building Nvim with Clang sanitizers (Address Sanitizer: ASan, Undefined +Behavior Sanitizer: UBSan, Memory Sanitizer: MSan, Thread Sanitizer: TSan) is +a good way to catch undefined behavior, leaks and other errors as soon as they +happen. It's significantly faster than Valgrind. + +Requires clang 3.4 or later: + + clang --version + +Build Nvim with sanitizer instrumentation: + + CC=clang make CMAKE_EXTRA_FLAGS="-DCLANG_ASAN_UBSAN=ON" + +Create a directory to store logs: + + mkdir -p "$HOME/logs" + +Enable the sanitizer(s) via these environment variables: + + # Change to detect_leaks=1 to detect memory leaks (slower). + export ASAN_OPTIONS="detect_leaks=0:log_path=$HOME/logs/asan" + export ASAN_SYMBOLIZER_PATH=/usr/lib/llvm-5.0/bin/llvm-symbolizer + + export MSAN_SYMBOLIZER_PATH=/usr/lib/llvm-5.0/bin/llvm-symbolizer + export TSAN_OPTIONS="external_symbolizer_path=/usr/lib/llvm-5.0/bin/llvm-symbolizer log_path=${HOME}/logs/tsan" + +Logs will be written to `${HOME}/logs/*san.PID`. + +TUI debugging +------------- + +### TUI troubleshoot + +Nvim logs its internal terminfo state at 'verbose' level 3. This makes it +possible to see exactly what terminfo values Nvim is using on any system. + + nvim -V3log + +### TUI trace + +The ancient `script` command is still the "state of the art" for tracing +terminal behavior. The libvterm `vterm-dump` utility formats the result for +human-readability. + +Record a Nvim terminal session and format it with `vterm-dump`: + + script foo + ./build/bin/nvim -u NONE + # Exit the script session with CTRL-d + + # Use `vterm-dump` utility to format the result. + ./.deps/usr/bin/vterm-dump foo > bar + +Then you can compare `bar` with another session, to debug TUI behavior. + +### TUI redraw + +Set the 'writedelay' option to see where and when the UI is painted. + + :set writedelay=1 + +### Terminal reference + +- `man terminfo` +- http://bazaar.launchpad.net/~libvterm/libvterm/trunk/view/head:/doc/seqs.txt +- http://invisible-island.net/xterm/ctlseqs/ctlseqs.html + +Nvim lifecycle +-------------- + +Following describes how Nvim processes input. + +Consider a typical Vim-like editing session: + +01. Vim displays the welcome screen 02. User types: `:` 03. Vim enters command-line mode 04. User types: `edit README.txt<CR>` @@ -154,7 +239,8 @@ modes managed by the `state_enter` loop: - insert mode: `insert_{enter,check,execute}()`(`edit.c`) - terminal mode: `terminal_{enter,execute}()`(`terminal.c`) -### Async event support +Async event support +------------------- One of the features Nvim added is the support for handling arbitrary asynchronous events, which can include: diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 82de8fd4a2..af723639c5 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -399,7 +399,11 @@ void nvim_buf_set_lines(uint64_t channel_id, // Only adjust marks if we managed to switch to a window that holds // the buffer, otherwise line numbers will be invalid. if (save_curbuf.br_buf == NULL) { - mark_adjust((linenr_T)start, (linenr_T)(end - 1), MAXLNUM, extra, false); + mark_adjust((linenr_T)start, + (linenr_T)(end - 1), + MAXLNUM, + (long)extra, + false); } changed_lines((linenr_T)start, 0, (linenr_T)end, (long)extra); @@ -439,6 +443,7 @@ Object nvim_buf_get_var(Buffer buffer, String name, Error *err) /// Gets a changed tick of a buffer /// /// @param[in] buffer Buffer handle. +/// @param[out] err Error details, if any /// /// @return `b:changedtick` value. Integer nvim_buf_get_changedtick(Buffer buffer, Error *err) @@ -453,14 +458,14 @@ Integer nvim_buf_get_changedtick(Buffer buffer, Error *err) return buf->b_changedtick; } -/// Get a list of dictionaries describing buffer-local mappings -/// Note that the buffer key in the dictionary will represent the buffer -/// handle where the mapping is present +/// Gets a list of dictionaries describing buffer-local mappings. +/// The "buffer" key in the returned dictionary reflects the buffer +/// handle where the mapping is present. /// -/// @param mode The abbreviation for the mode -/// @param buffer_id Buffer handle +/// @param mode Mode short-name ("n", "i", "v", ...) +/// @param buffer Buffer handle /// @param[out] err Error details, if any -/// @returns An array of maparg() like dictionaries describing mappings +/// @returns Array of maparg()-like dictionaries describing mappings ArrayOf(Dictionary) nvim_buf_get_keymap(Buffer buffer, String mode, Error *err) FUNC_API_SINCE(3) { @@ -739,31 +744,31 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err) /// Adds a highlight to buffer. /// -/// This can be used for plugins which dynamically generate highlights to a -/// buffer (like a semantic highlighter or linter). The function adds a single +/// Useful for plugins that dynamically generate highlights to a buffer +/// (like a semantic highlighter or linter). The function adds a single /// highlight to a buffer. Unlike matchaddpos() highlights follow changes to /// line numbering (as lines are inserted/removed above the highlighted line), /// like signs and marks do. /// -/// "src_id" is useful for batch deletion/updating of a set of highlights. When -/// called with src_id = 0, an unique source id is generated and returned. -/// Succesive calls can pass in it as "src_id" to add new highlights to the same -/// source group. All highlights in the same group can then be cleared with -/// nvim_buf_clear_highlight. If the highlight never will be manually deleted -/// pass in -1 for "src_id". +/// `src_id` is useful for batch deletion/updating of a set of highlights. When +/// called with `src_id = 0`, an unique source id is generated and returned. +/// Successive calls can pass that `src_id` to associate new highlights with +/// the same source group. All highlights in the same group can be cleared +/// with `nvim_buf_clear_highlight`. If the highlight never will be manually +/// deleted, pass `src_id = -1`. /// -/// If "hl_group" is the empty string no highlight is added, but a new src_id +/// If `hl_group` is the empty string no highlight is added, but a new `src_id` /// is still returned. This is useful for an external plugin to synchrounously -/// request an unique src_id at initialization, and later asynchronously add and -/// clear highlights in response to buffer changes. +/// request an unique `src_id` at initialization, and later asynchronously add +/// and clear highlights in response to buffer changes. /// /// @param buffer Buffer handle /// @param src_id Source group to use or 0 to use a new group, /// or -1 for ungrouped highlight /// @param hl_group Name of the highlight group to use -/// @param line Line to highlight -/// @param col_start Start of range of columns to highlight -/// @param col_end End of range of columns to highlight, +/// @param line Line to highlight (zero-indexed) +/// @param col_start Start of (byte-indexed) column range to highlight +/// @param col_end End of (byte-indexed) column range to highlight, /// or -1 to highlight to end of line /// @param[out] err Error details, if any /// @return The src_id that was used @@ -793,7 +798,11 @@ Integer nvim_buf_add_highlight(Buffer buffer, col_end = MAXCOL; } - int hlg_id = syn_name2id((char_u *)(hl_group.data ? hl_group.data : "")); + int hlg_id = 0; + if (hl_group.size > 0) { + hlg_id = syn_check_group((char_u *)hl_group.data, (int)hl_group.size); + } + src_id = bufhl_add_hl(buf, (int)src_id, hlg_id, (linenr_T)line+1, (colnr_T)col_start+1, (colnr_T)col_end); return src_id; @@ -801,7 +810,7 @@ Integer nvim_buf_add_highlight(Buffer buffer, /// Clears highlights from a given source group and a range of lines /// -/// To clear a source group in the entire buffer, pass in 1 and -1 to +/// To clear a source group in the entire buffer, pass in 0 and -1 to /// line_start and line_end respectively. /// /// @param buffer Buffer handle diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index 2144c80d6a..390b7e8363 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -47,7 +47,7 @@ typedef enum { /// Internal call from lua code #define LUA_INTERNAL_CALL (VIML_INTERNAL_CALL + 1) -static inline bool is_internal_call(uint64_t channel_id) +static inline bool is_internal_call(const uint64_t channel_id) REAL_FATTR_ALWAYS_INLINE REAL_FATTR_CONST; /// Check whether call is internal diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index d401ae52a0..12a4279dd7 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -12,6 +12,7 @@ #include "nvim/api/private/handle.h" #include "nvim/msgpack_rpc/helpers.h" #include "nvim/ascii.h" +#include "nvim/assert.h" #include "nvim/vim.h" #include "nvim/buffer.h" #include "nvim/window.h" @@ -25,6 +26,7 @@ #include "nvim/version.h" #include "nvim/lib/kvec.h" #include "nvim/getchar.h" +#include "nvim/ui.h" /// Helper structure for vim_to_object typedef struct { @@ -37,7 +39,71 @@ typedef struct { # include "api/private/ui_events_metadata.generated.h" #endif +/// Start block that may cause VimL exceptions while evaluating another code +/// +/// Used when caller is supposed to be operating when other VimL code is being +/// processed and that “other VimL code” must not be affected. +/// +/// @param[out] tstate Location where try state should be saved. +void try_enter(TryState *const tstate) +{ + // TODO(ZyX-I): Check whether try_enter()/try_leave() may use + // enter_cleanup()/leave_cleanup(). Or + // save_dbg_stuff()/restore_dbg_stuff(). + *tstate = (TryState) { + .current_exception = current_exception, + .msg_list = (const struct msglist *const *)msg_list, + .private_msg_list = NULL, + .trylevel = trylevel, + .got_int = got_int, + .need_rethrow = need_rethrow, + .did_emsg = did_emsg, + }; + msg_list = &tstate->private_msg_list; + current_exception = NULL; + trylevel = 1; + got_int = false; + need_rethrow = false; + did_emsg = false; +} + +/// End try block, set the error message if any and restore previous state +/// +/// @warning Return is consistent with most functions (false on error), not with +/// try_end (true on error). +/// +/// @param[in] tstate Previous state to restore. +/// @param[out] err Location where error should be saved. +/// +/// @return false if error occurred, true otherwise. +bool try_leave(const TryState *const tstate, Error *const err) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + const bool ret = !try_end(err); + assert(trylevel == 0); + assert(!need_rethrow); + assert(!got_int); + assert(!did_emsg); + assert(msg_list == &tstate->private_msg_list); + assert(*msg_list == NULL); + assert(current_exception == NULL); + msg_list = (struct msglist **)tstate->msg_list; + current_exception = tstate->current_exception; + trylevel = tstate->trylevel; + got_int = tstate->got_int; + need_rethrow = tstate->need_rethrow; + did_emsg = tstate->did_emsg; + return ret; +} + /// Start block that may cause vimscript exceptions +/// +/// Each try_start() call should be mirrored by try_end() call. +/// +/// To be used as a replacement of `:try … catch … endtry` in C code, in cases +/// when error flag could not already be set. If there may be pending error +/// state at the time try_start() is executed which needs to be preserved, +/// try_enter()/try_leave() pair should be used instead. void try_start(void) { ++trylevel; @@ -50,7 +116,9 @@ void try_start(void) /// @return true if an error occurred bool try_end(Error *err) { - --trylevel; + // Note: all globals manipulated here should be saved/restored in + // try_enter/try_leave. + trylevel--; // Without this it stops processing all subsequent VimL commands and // generates strange error messages if I e.g. try calling Test() in a @@ -58,7 +126,7 @@ bool try_end(Error *err) did_emsg = false; if (got_int) { - if (did_throw) { + if (current_exception) { // If we got an interrupt, discard the current exception discard_current_exception(); } @@ -77,7 +145,7 @@ bool try_end(Error *err) if (should_free) { xfree(msg); } - } else if (did_throw) { + } else if (current_exception) { api_set_error(err, kErrorTypeException, "%s", current_exception->value); discard_current_exception(); } @@ -95,7 +163,7 @@ Object dict_get_value(dict_T *dict, String key, Error *err) dictitem_T *const di = tv_dict_find(dict, key.data, (ptrdiff_t)key.size); if (di == NULL) { - api_set_error(err, kErrorTypeValidation, "Key not found"); + api_set_error(err, kErrorTypeValidation, "Key '%s' not found", key.data); return (Object)OBJECT_INIT; } @@ -498,7 +566,7 @@ static inline void typval_encode_dict_end(EncodedData *const edata) typval_encode_dict_end(edata) #define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \ - TYPVAL_ENCODE_CONV_NIL() + TYPVAL_ENCODE_CONV_NIL(val) #define TYPVAL_ENCODE_SCOPE static #define TYPVAL_ENCODE_NAME object @@ -600,6 +668,22 @@ tabpage_T *find_tab_by_handle(Tabpage tabpage, Error *err) return rv; } +/// Allocates a String consisting of a single char. Does not support multibyte +/// characters. The resulting string is also NUL-terminated, to facilitate +/// interoperating with code using C strings. +/// +/// @param char the char to convert +/// @return the resulting String, if the input char was NUL, an +/// empty String is returned +String cchar_to_string(char c) +{ + char buf[] = { c, NUL }; + return (String) { + .data = xmemdupz(buf, 1), + .size = (c != NUL) ? 1 : 0 + }; +} + /// Copies a C string into a String (binary safe string, characters + length). /// The resulting string is also NUL-terminated, to facilitate interoperating /// with code using C strings. @@ -620,6 +704,23 @@ String cstr_to_string(const char *str) }; } +/// Copies buffer to an allocated String. +/// The resulting string is also NUL-terminated, to facilitate interoperating +/// with code using C strings. +/// +/// @param buf the buffer to copy +/// @param size length of the buffer +/// @return the resulting String, if the input string was NULL, an +/// empty String is returned +String cbuf_to_string(const char *buf, size_t size) + FUNC_ATTR_NONNULL_ALL +{ + return (String) { + .data = xmemdupz(buf, size), + .size = size + }; +} + /// Creates a String using the given C string. Unlike /// cstr_to_string this function DOES NOT copy the C string. /// @@ -660,12 +761,8 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) case kObjectTypeWindow: case kObjectTypeTabpage: case kObjectTypeInteger: - if (obj.data.integer > VARNUMBER_MAX - || obj.data.integer < VARNUMBER_MIN) { - api_set_error(err, kErrorTypeValidation, "Integer value outside range"); - return false; - } - + STATIC_ASSERT(sizeof(obj.data.integer) <= sizeof(varnumber_T), + "Integer size must be <= VimL number size"); tv->v_type = VAR_NUMBER; tv->vval.v_number = (varnumber_T)obj.data.integer; break; @@ -686,22 +783,20 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) break; case kObjectTypeArray: { - list_T *const list = tv_list_alloc(); + list_T *const list = tv_list_alloc((ptrdiff_t)obj.data.array.size); for (uint32_t i = 0; i < obj.data.array.size; i++) { Object item = obj.data.array.items[i]; - listitem_T *li = tv_list_item_alloc(); + typval_T li_tv; - if (!object_to_vim(item, &li->li_tv, err)) { - // cleanup - tv_list_item_free(li); + if (!object_to_vim(item, &li_tv, err)) { tv_list_free(list); return false; } - tv_list_append(list, li); + tv_list_append_owned_tv(list, li_tv); } - list->lv_refcount++; + tv_list_ref(list); tv->v_type = VAR_LIST; tv->vval.v_list = list; @@ -860,6 +955,12 @@ static void init_ui_event_metadata(Dictionary *metadata) msgpack_rpc_to_object(&unpacked.data, &ui_events); msgpack_unpacked_destroy(&unpacked); PUT(*metadata, "ui_events", ui_events); + Array ui_options = ARRAY_DICT_INIT; + ADD(ui_options, STRING_OBJ(cstr_to_string("rgb"))); + for (UIExtension i = 0; i < kUIExtCount; i++) { + ADD(ui_options, STRING_OBJ(cstr_to_string(ui_ext_names[i]))); + } + PUT(*metadata, "ui_options", ARRAY_OBJ(ui_options)); } static void init_error_type_metadata(Dictionary *metadata) diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 159b9d5c2a..0634764f13 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -6,6 +6,7 @@ #include "nvim/api/private/defs.h" #include "nvim/vim.h" #include "nvim/memory.h" +#include "nvim/ex_eval.h" #include "nvim/lib/kvec.h" #define OBJECT_OBJ(o) o @@ -82,6 +83,20 @@ #define api_free_window(value) #define api_free_tabpage(value) +/// Structure used for saving state for :try +/// +/// Used when caller is supposed to be operating when other VimL code is being +/// processed and that “other VimL code” must not be affected. +typedef struct { + except_T *current_exception; + struct msglist *private_msg_list; + const struct msglist *const *msg_list; + int trylevel; + int got_int; + int need_rethrow; + int did_emsg; +} TryState; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/private/helpers.h.generated.h" #endif diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 573be23d8e..4870c3fb8a 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -43,10 +43,10 @@ void remote_ui_disconnect(uint64_t channel_id) return; } UIData *data = ui->data; - // destroy pending screen updates - api_free_array(data->buffer); + api_free_array(data->buffer); // Destroy pending screen updates. pmap_del(uint64_t)(connected_uis, channel_id); xfree(ui->data); + ui->data = NULL; // Flag UI as "stopped". ui_detach_impl(ui); xfree(ui); } @@ -86,6 +86,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, ui->put = remote_ui_put; ui->bell = remote_ui_bell; ui->visual_bell = remote_ui_visual_bell; + ui->default_colors_set = remote_ui_default_colors_set; ui->update_fg = remote_ui_update_fg; ui->update_bg = remote_ui_update_bg; ui->update_sp = remote_ui_update_sp; @@ -93,6 +94,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, ui->suspend = remote_ui_suspend; ui->set_title = remote_ui_set_title; ui->set_icon = remote_ui_set_icon; + ui->option_set = remote_ui_option_set; ui->event = remote_ui_event; memset(ui->ui_ext, 0, sizeof(ui->ui_ext)); @@ -174,18 +176,6 @@ void nvim_ui_set_option(uint64_t channel_id, String name, static void ui_set_option(UI *ui, String name, Object value, Error *error) { -#define UI_EXT_OPTION(o, e) \ - do { \ - if (strequal(name.data, #o)) { \ - if (value.type != kObjectTypeBoolean) { \ - api_set_error(error, kErrorTypeValidation, #o " must be a Boolean"); \ - return; \ - } \ - ui->ui_ext[(e)] = value.data.boolean; \ - return; \ - } \ - } while (0) - if (strequal(name.data, "rgb")) { if (value.type != kObjectTypeBoolean) { api_set_error(error, kErrorTypeValidation, "rgb must be a Boolean"); @@ -195,13 +185,21 @@ static void ui_set_option(UI *ui, String name, Object value, Error *error) return; } - UI_EXT_OPTION(ext_cmdline, kUICmdline); - UI_EXT_OPTION(ext_popupmenu, kUIPopupmenu); - UI_EXT_OPTION(ext_tabline, kUITabline); - UI_EXT_OPTION(ext_wildmenu, kUIWildmenu); + for (UIExtension i = 0; i < kUIExtCount; i++) { + if (strequal(name.data, ui_ext_names[i])) { + if (value.type != kObjectTypeBoolean) { + snprintf((char *)IObuff, IOSIZE, "%s must be a Boolean", + ui_ext_names[i]); + api_set_error(error, kErrorTypeValidation, (char *)IObuff); + return; + } + ui->ui_ext[i] = value.data.boolean; + return; + } + } if (strequal(name.data, "popupmenu_external")) { - // LEGACY: Deprecated option, use `ui_ext` instead. + // LEGACY: Deprecated option, use `ext_cmdline` instead. if (value.type != kObjectTypeBoolean) { api_set_error(error, kErrorTypeValidation, "popupmenu_external must be a Boolean"); @@ -215,6 +213,7 @@ static void ui_set_option(UI *ui, String name, Object value, Error *error) #undef UI_EXT_OPTION } +/// Pushes data into UI.UIData, to be consumed later by remote_ui_flush(). static void push_call(UI *ui, char *name, Array args) { Array call = ARRAY_DICT_INIT; @@ -241,39 +240,7 @@ static void push_call(UI *ui, char *name, Array args) static void remote_ui_highlight_set(UI *ui, HlAttrs attrs) { Array args = ARRAY_DICT_INIT; - Dictionary hl = ARRAY_DICT_INIT; - - if (attrs.bold) { - PUT(hl, "bold", BOOLEAN_OBJ(true)); - } - - if (attrs.underline) { - PUT(hl, "underline", BOOLEAN_OBJ(true)); - } - - if (attrs.undercurl) { - PUT(hl, "undercurl", BOOLEAN_OBJ(true)); - } - - if (attrs.italic) { - PUT(hl, "italic", BOOLEAN_OBJ(true)); - } - - if (attrs.reverse) { - PUT(hl, "reverse", BOOLEAN_OBJ(true)); - } - - if (attrs.foreground != -1) { - PUT(hl, "foreground", INTEGER_OBJ(attrs.foreground)); - } - - if (attrs.background != -1) { - PUT(hl, "background", INTEGER_OBJ(attrs.background)); - } - - if (attrs.special != -1) { - PUT(hl, "special", INTEGER_OBJ(attrs.special)); - } + Dictionary hl = hlattrs2dict(&attrs, ui->rgb); ADD(args, DICTIONARY_OBJ(hl)); push_call(ui, "highlight_set", args); @@ -283,7 +250,7 @@ static void remote_ui_flush(UI *ui) { UIData *data = ui->data; if (data->buffer.size > 0) { - channel_send_event(data->channel_id, "redraw", data->buffer); + rpc_send_event(data->channel_id, "redraw", data->buffer); data->buffer = (Array)ARRAY_DICT_INIT; } } diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index 1b5d17584f..c599b0ce72 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -47,17 +47,22 @@ void visual_bell(void) void flush(void) FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL; void update_fg(Integer fg) - FUNC_API_SINCE(3); + FUNC_API_SINCE(3) FUNC_API_BRIDGE_IMPL; void update_bg(Integer bg) - FUNC_API_SINCE(3); + FUNC_API_SINCE(3) FUNC_API_BRIDGE_IMPL; void update_sp(Integer sp) - FUNC_API_SINCE(3); + FUNC_API_SINCE(3) FUNC_API_BRIDGE_IMPL; +void default_colors_set(Integer rgb_fg, Integer rgb_bg, Integer rgb_sp, + Integer cterm_fg, Integer cterm_bg) + FUNC_API_SINCE(4); void suspend(void) FUNC_API_SINCE(3) FUNC_API_BRIDGE_IMPL; void set_title(String title) FUNC_API_SINCE(3); void set_icon(String icon) FUNC_API_SINCE(3); +void option_set(String name, Object value) + FUNC_API_SINCE(4); void popupmenu_show(Array items, Integer selected, Integer row, Integer col) FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; @@ -65,7 +70,30 @@ void popupmenu_hide(void) FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; void popupmenu_select(Integer selected) FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; + void tabline_update(Tabpage current, Array tabs) FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; +void cmdline_show(Array content, Integer pos, String firstc, String prompt, + Integer indent, Integer level) + FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; +void cmdline_pos(Integer pos, Integer level) + FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; +void cmdline_special_char(String c, Boolean shift, Integer level) + FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; +void cmdline_hide(Integer level) + FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; +void cmdline_block_show(Array lines) + FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; +void cmdline_block_append(Array lines) + FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; +void cmdline_block_hide(void) + FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; + +void wildmenu_show(Array items) + FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; +void wildmenu_select(Integer selected) + FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; +void wildmenu_hide(void) + FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; #endif // NVIM_API_UI_EVENTS_IN_H diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 80efe86ea3..962081cc23 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -33,6 +33,10 @@ #include "nvim/syntax.h" #include "nvim/getchar.h" #include "nvim/os/input.h" +#include "nvim/os/process.h" +#include "nvim/viml/parser/expressions.h" +#include "nvim/viml/parser/parser.h" +#include "nvim/ui.h" #define LINE_BUFFER_SIZE 4096 @@ -41,20 +45,63 @@ #endif /// Executes an ex-command. -/// On VimL error: Returns the VimL error; v:errmsg is not updated. +/// +/// On parse error: forwards the Vim error; does not update v:errmsg. +/// On runtime error: forwards the Vim error; does not update v:errmsg. /// /// @param command Ex-command string -/// @param[out] err Error details (including actual VimL error), if any +/// @param[out] err Error details (Vim error), if any void nvim_command(String command, Error *err) FUNC_API_SINCE(1) { - // Run the command try_start(); do_cmdline_cmd(command.data); update_screen(VALID); try_end(err); } +/// Gets a highlight definition by name. +/// +/// @param name Highlight group name +/// @param rgb Export RGB colors +/// @param[out] err Error details, if any +/// @return Highlight definition map +/// @see nvim_get_hl_by_id +Dictionary nvim_get_hl_by_name(String name, Boolean rgb, Error *err) + FUNC_API_SINCE(3) +{ + Dictionary result = ARRAY_DICT_INIT; + int id = syn_name2id((const char_u *)name.data); + + if (id == 0) { + api_set_error(err, kErrorTypeException, "Invalid highlight name: %s", + name.data); + return result; + } + result = nvim_get_hl_by_id(id, rgb, err); + return result; +} + +/// Gets a highlight definition by id. |hlID()| +/// +/// @param hl_id Highlight id as returned by |hlID()| +/// @param rgb Export RGB colors +/// @param[out] err Error details, if any +/// @return Highlight definition map +/// @see nvim_get_hl_by_name +Dictionary nvim_get_hl_by_id(Integer hl_id, Boolean rgb, Error *err) + FUNC_API_SINCE(3) +{ + Dictionary dic = ARRAY_DICT_INIT; + if (syn_get_final_id((int)hl_id) == 0) { + api_set_error(err, kErrorTypeException, + "Invalid highlight id: %" PRId64, hl_id); + return dic; + } + int attrcode = syn_id2attr((int)hl_id); + return hl_get_attr_by_id(attrcode, rgb, err); +} + /// Passes input keys to Nvim. /// On VimL error: Does not fail, but updates v:errmsg. /// @@ -125,7 +172,10 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_csi) /// On VimL error: Does not fail, but updates v:errmsg. /// /// Unlike `nvim_feedkeys`, this uses a lower-level input buffer and the call -/// is not deferred. This is the most reliable way to emulate real user input. +/// is not deferred. This is the most reliable way to send real user input. +/// +/// @note |keycodes| like <CR> are translated, so "<" is special. +/// To input a literal "<", send <LT>. /// /// @param keys to be typed /// @return Number of bytes actually written (can be fewer than @@ -136,9 +186,13 @@ Integer nvim_input(String keys) return (Integer)input_enqueue(keys); } -/// Replaces terminal codes and key codes (<CR>, <Esc>, ...) in a string with +/// Replaces terminal codes and |keycodes| (<CR>, <Esc>, ...) in a string with /// the internal representation. /// +/// @param str String to be converted. +/// @param from_part Legacy Vim parameter. Usually true. +/// @param do_lt Also translate <lt>. Ignored if `special` is false. +/// @param special Replace |keycodes|, e.g. <CR> becomes a "\n" char. /// @see replace_termcodes /// @see cpoptions String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, @@ -151,29 +205,54 @@ String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, } char *ptr = NULL; - // Set 'cpoptions' the way we want it. - // FLAG_CPO_BSLASH set - backslashes are *not* treated specially - // FLAG_CPO_KEYCODE set - keycodes are *not* reverse-engineered - // FLAG_CPO_SPECI unset - <Key> sequences *are* interpreted - // The third from end parameter of replace_termcodes() is true so that the - // <lt> sequence is recognised - needed for a real backslash. replace_termcodes((char_u *)str.data, str.size, (char_u **)&ptr, from_part, do_lt, special, CPO_TO_CPO_FLAGS); return cstr_as_string(ptr); } -String nvim_command_output(String str, Error *err) +/// Executes an ex-command and returns its (non-error) output. +/// Shell |:!| output is not captured. +/// +/// On parse error: forwards the Vim error; does not update v:errmsg. +/// On runtime error: forwards the Vim error; does not update v:errmsg. +/// +/// @param command Ex-command string +/// @param[out] err Error details (Vim error), if any +String nvim_command_output(String command, Error *err) FUNC_API_SINCE(1) { - do_cmdline_cmd("redir => v:command_output"); - nvim_command(str, err); - do_cmdline_cmd("redir END"); + const int save_msg_silent = msg_silent; + garray_T *const save_capture_ga = capture_ga; + garray_T capture_local; + ga_init(&capture_local, 1, 80); + + try_start(); + msg_silent++; + capture_ga = &capture_local; + do_cmdline_cmd(command.data); + capture_ga = save_capture_ga; + msg_silent = save_msg_silent; + try_end(err); if (ERROR_SET(err)) { - return (String)STRING_INIT; + goto theend; } - return cstr_to_string((char *)get_vim_var_str(VV_COMMAND_OUTPUT)); + if (capture_local.ga_len > 1) { + // redir always(?) prepends a newline; remove it. + char *s = capture_local.ga_data; + assert(s[0] == '\n'); + memmove(s, s + 1, (size_t)capture_local.ga_len); + s[capture_local.ga_len - 1] = '\0'; + return (String) { // Caller will free the memory. + .data = s, + .size = (size_t)(capture_local.ga_len - 1), + }; + } + +theend: + ga_clear(&capture_local); + return (String)STRING_INIT; } /// Evaluates a VimL expression (:help expression). @@ -257,12 +336,11 @@ free_vim_args: return rv; } -/// Execute lua code. Parameters might be passed, they are available inside -/// the chunk as `...`. The chunk can return a value. +/// Execute lua code. Parameters (if any) are available as `...` inside the +/// chunk. The chunk can return a value. /// -/// To evaluate an expression, it must be prefixed with "return ". For -/// instance, to call a lua function with arguments sent in and get its -/// return value back, use the code "return my_function(...)". +/// Only statements are executed. To evaluate an expression, prefix it +/// with `return`: return my_function(...) /// /// @param code lua code to execute /// @param args Arguments to the code @@ -282,15 +360,15 @@ Object nvim_execute_lua(String code, Array args, Error *err) /// @param text Some text /// @param[out] err Error details, if any /// @return Number of cells -Integer nvim_strwidth(String str, Error *err) +Integer nvim_strwidth(String text, Error *err) FUNC_API_SINCE(1) { - if (str.size > INT_MAX) { + if (text.size > INT_MAX) { api_set_error(err, kErrorTypeValidation, "String length is too high"); return 0; } - return (Integer) mb_string2cells((char_u *) str.data); + return (Integer)mb_string2cells((char_u *)text.data); } /// Gets the paths contained in 'runtimepath'. @@ -425,29 +503,18 @@ void nvim_del_var(String name, Error *err) dict_set_var(&globvardict, name, NIL, true, false, err); } -/// Sets a global variable -/// /// @deprecated -/// -/// @param name Variable name -/// @param value Variable value -/// @param[out] err Error details, if any +/// @see nvim_set_var /// @return Old value or nil if there was no previous value. -/// -/// @warning It may return nil if there was no previous value -/// or if previous value was `v:null`. +/// @warning May return nil if there was no previous value +/// OR if previous value was `v:null`. Object vim_set_var(String name, Object value, Error *err) { return dict_set_var(&globvardict, name, value, false, true, err); } -/// Removes a global variable -/// /// @deprecated -/// -/// @param name Variable name -/// @param[out] err Error details, if any -/// @return Old value +/// @see nvim_del_var Object vim_del_var(String name, Error *err) { return dict_set_var(&globvardict, name, NIL, true, true, err); @@ -486,7 +553,8 @@ void nvim_set_option(String name, Object value, Error *err) set_option_to(NULL, SREQ_GLOBAL, name, value, err); } -/// Writes a message to vim output buffer +/// Writes a message to the Vim output buffer. Does not append "\n", the +/// message is buffered (won't display) until a linefeed is written. /// /// @param str Message void nvim_out_write(String str) @@ -495,7 +563,8 @@ void nvim_out_write(String str) write_msg(str, false); } -/// Writes a message to vim error buffer +/// Writes a message to the Vim error buffer. Does not append "\n", the +/// message is buffered (won't display) until a linefeed is written. /// /// @param str Message void nvim_err_write(String str) @@ -504,8 +573,8 @@ void nvim_err_write(String str) write_msg(str, true); } -/// Writes a message to vim error buffer. Appends a linefeed to ensure all -/// contents are written. +/// Writes a message to the Vim error buffer. Appends "\n", so the buffer is +/// flushed (and displayed). /// /// @param str Message /// @see nvim_err_write() @@ -549,7 +618,7 @@ Buffer nvim_get_current_buf(void) /// Sets the current buffer /// -/// @param id Buffer handle +/// @param buffer Buffer handle /// @param[out] err Error details, if any void nvim_set_current_buf(Buffer buffer, Error *err) FUNC_API_SINCE(1) @@ -603,7 +672,7 @@ Window nvim_get_current_win(void) /// Sets the current window /// -/// @param handle Window handle +/// @param window Window handle void nvim_set_current_win(Window window, Error *err) FUNC_API_SINCE(1) { @@ -656,7 +725,7 @@ Tabpage nvim_get_current_tabpage(void) /// Sets the current tabpage /// -/// @param handle Tabpage handle +/// @param tabpage Tabpage handle /// @param[out] err Error details, if any void nvim_set_current_tabpage(Tabpage tabpage, Error *err) FUNC_API_SINCE(1) @@ -688,7 +757,7 @@ void nvim_subscribe(uint64_t channel_id, String event) char e[METHOD_MAXLEN + 1]; memcpy(e, event.data, length); e[length] = NUL; - channel_subscribe(channel_id, e); + rpc_subscribe(channel_id, e); } /// Unsubscribes to event broadcasts @@ -704,7 +773,7 @@ void nvim_unsubscribe(uint64_t channel_id, String event) char e[METHOD_MAXLEN + 1]; memcpy(e, event.data, length); e[length] = NUL; - channel_unsubscribe(channel_id, e); + rpc_unsubscribe(channel_id, e); } Integer nvim_get_color_by_name(String name) @@ -726,9 +795,8 @@ Dictionary nvim_get_color_map(void) } -/// Gets the current mode. -/// mode: Mode string. |mode()| -/// blocking: true if Nvim is waiting for input. +/// Gets the current mode. |mode()| +/// "blocking" is true if Nvim is waiting for input. /// /// @returns Dictionary { "mode": String, "blocking": Boolean } Dictionary nvim_get_mode(void) @@ -744,17 +812,21 @@ Dictionary nvim_get_mode(void) return rv; } -/// Get a list of dictionaries describing global (i.e. non-buffer) mappings -/// Note that the "buffer" key will be 0 to represent false. +/// Gets a list of dictionaries describing global (non-buffer) mappings. +/// The "buffer" key in the returned dictionary is always zero. /// -/// @param mode The abbreviation for the mode -/// @returns An array of maparg() like dictionaries describing mappings +/// @param mode Mode short-name ("n", "i", "v", ...) +/// @returns Array of maparg()-like dictionaries describing mappings ArrayOf(Dictionary) nvim_get_keymap(String mode) FUNC_API_SINCE(3) { return keymap_array(mode, NULL); } +/// Returns a 2-tuple (Array), where item 0 is the current channel id and item +/// 1 is the |api-metadata| map (Dictionary). +/// +/// @returns 2-tuple [{channel-id}, {api-metadata}] Array nvim_get_api_info(uint64_t channel_id) FUNC_API_SINCE(1) FUNC_API_ASYNC FUNC_API_REMOTE_ONLY { @@ -857,6 +929,460 @@ theend: return rv; } +typedef struct { + ExprASTNode **node_p; + Object *ret_node_p; +} ExprASTConvStackItem; + +/// @cond DOXYGEN_NOT_A_FUNCTION +typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack; +/// @endcond + +/// Parse a VimL expression +/// +/// @param[in] expr Expression to parse. Is always treated as a single line. +/// @param[in] flags Flags: +/// +/// - "m" if multiple expressions in a row are allowed (only +/// the first one will be parsed), +/// - "E" if EOC tokens are not allowed (determines whether +/// they will stop parsing process or be recognized as an +/// operator/space, though also yielding an error). +/// - "l" when needing to start parsing with lvalues for +/// ":let" or ":for". +/// +/// Common flag sets: +/// - "m" to parse like for ":echo". +/// - "E" to parse like for "<C-r>=". +/// - empty string for ":call". +/// - "lm" to parse for ":let". +/// @param[in] highlight If true, return value will also include "highlight" +/// key containing array of 4-tuples (arrays) (Integer, +/// Integer, Integer, String), where first three numbers +/// define the highlighted region and represent line, +/// starting column and ending column (latter exclusive: +/// one should highlight region [start_col, end_col)). +/// +/// @return AST: top-level dictionary with these keys: +/// +/// "error": Dictionary with error, present only if parser saw some +/// error. Contains the following keys: +/// +/// "message": String, error message in printf format, translated. +/// Must contain exactly one "%.*s". +/// "arg": String, error message argument. +/// +/// "len": Amount of bytes successfully parsed. With flags equal to "" +/// that should be equal to the length of expr string. +/// +/// @note: “Sucessfully parsed” here means “participated in AST +/// creation”, not “till the first error”. +/// +/// "ast": AST, either nil or a dictionary with these keys: +/// +/// "type": node type, one of the value names from ExprASTNodeType +/// stringified without "kExprNode" prefix. +/// "start": a pair [line, column] describing where node is “started” +/// where "line" is always 0 (will not be 0 if you will be +/// using nvim_parse_viml() on e.g. ":let", but that is not +/// present yet). Both elements are Integers. +/// "len": “length” of the node. This and "start" are there for +/// debugging purposes primary (debugging parser and providing +/// debug information). +/// "children": a list of nodes described in top/"ast". There always +/// is zero, one or two children, key will not be present +/// if node has no children. Maximum number of children +/// may be found in node_maxchildren array. +/// +/// Local values (present only for certain nodes): +/// +/// "scope": a single Integer, specifies scope for "Option" and +/// "PlainIdentifier" nodes. For "Option" it is one of +/// ExprOptScope values, for "PlainIdentifier" it is one of +/// ExprVarScope values. +/// "ident": identifier (without scope, if any), present for "Option", +/// "PlainIdentifier", "PlainKey" and "Environment" nodes. +/// "name": Integer, register name (one character) or -1. Only present +/// for "Register" nodes. +/// "cmp_type": String, comparison type, one of the value names from +/// ExprComparisonType, stringified without "kExprCmp" +/// prefix. Only present for "Comparison" nodes. +/// "ccs_strategy": String, case comparison strategy, one of the +/// value names from ExprCaseCompareStrategy, +/// stringified without "kCCStrategy" prefix. Only +/// present for "Comparison" nodes. +/// "augmentation": String, augmentation type for "Assignment" nodes. +/// Is either an empty string, "Add", "Subtract" or +/// "Concat" for "=", "+=", "-=" or ".=" respectively. +/// "invert": Boolean, true if result of comparison needs to be +/// inverted. Only present for "Comparison" nodes. +/// "ivalue": Integer, integer value for "Integer" nodes. +/// "fvalue": Float, floating-point value for "Float" nodes. +/// "svalue": String, value for "SingleQuotedString" and +/// "DoubleQuotedString" nodes. +Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, + Error *err) + FUNC_API_SINCE(4) FUNC_API_ASYNC +{ + int pflags = 0; + for (size_t i = 0 ; i < flags.size ; i++) { + switch (flags.data[i]) { + case 'm': { pflags |= kExprFlagsMulti; break; } + case 'E': { pflags |= kExprFlagsDisallowEOC; break; } + case 'l': { pflags |= kExprFlagsParseLet; break; } + case NUL: { + api_set_error(err, kErrorTypeValidation, "Invalid flag: '\\0' (%u)", + (unsigned)flags.data[i]); + return (Dictionary)ARRAY_DICT_INIT; + } + default: { + api_set_error(err, kErrorTypeValidation, "Invalid flag: '%c' (%u)", + flags.data[i], (unsigned)flags.data[i]); + return (Dictionary)ARRAY_DICT_INIT; + } + } + } + ParserLine plines[] = { + { + .data = expr.data, + .size = expr.size, + .allocated = false, + }, + { NULL, 0, false }, + }; + ParserLine *plines_p = plines; + ParserHighlight colors; + kvi_init(colors); + ParserHighlight *const colors_p = (highlight ? &colors : NULL); + ParserState pstate; + viml_parser_init( + &pstate, parser_simple_get_line, &plines_p, colors_p); + ExprAST east = viml_pexpr_parse(&pstate, pflags); + + const size_t ret_size = ( + 2 // "ast", "len" + + (size_t)(east.err.msg != NULL) // "error" + + (size_t)highlight // "highlight" + + 0); + Dictionary ret = { + .items = xmalloc(ret_size * sizeof(ret.items[0])), + .size = 0, + .capacity = ret_size, + }; + ret.items[ret.size++] = (KeyValuePair) { + .key = STATIC_CSTR_TO_STRING("ast"), + .value = NIL, + }; + ret.items[ret.size++] = (KeyValuePair) { + .key = STATIC_CSTR_TO_STRING("len"), + .value = INTEGER_OBJ((Integer)(pstate.pos.line == 1 + ? plines[0].size + : pstate.pos.col)), + }; + if (east.err.msg != NULL) { + Dictionary err_dict = { + .items = xmalloc(2 * sizeof(err_dict.items[0])), + .size = 2, + .capacity = 2, + }; + err_dict.items[0] = (KeyValuePair) { + .key = STATIC_CSTR_TO_STRING("message"), + .value = STRING_OBJ(cstr_to_string(east.err.msg)), + }; + if (east.err.arg == NULL) { + err_dict.items[1] = (KeyValuePair) { + .key = STATIC_CSTR_TO_STRING("arg"), + .value = STRING_OBJ(STRING_INIT), + }; + } else { + err_dict.items[1] = (KeyValuePair) { + .key = STATIC_CSTR_TO_STRING("arg"), + .value = STRING_OBJ(((String) { + .data = xmemdupz(east.err.arg, (size_t)east.err.arg_len), + .size = (size_t)east.err.arg_len, + })), + }; + } + ret.items[ret.size++] = (KeyValuePair) { + .key = STATIC_CSTR_TO_STRING("error"), + .value = DICTIONARY_OBJ(err_dict), + }; + } + if (highlight) { + Array hl = (Array) { + .items = xmalloc(kv_size(colors) * sizeof(hl.items[0])), + .capacity = kv_size(colors), + .size = kv_size(colors), + }; + for (size_t i = 0 ; i < kv_size(colors) ; i++) { + const ParserHighlightChunk chunk = kv_A(colors, i); + Array chunk_arr = (Array) { + .items = xmalloc(4 * sizeof(chunk_arr.items[0])), + .capacity = 4, + .size = 4, + }; + chunk_arr.items[0] = INTEGER_OBJ((Integer)chunk.start.line); + chunk_arr.items[1] = INTEGER_OBJ((Integer)chunk.start.col); + chunk_arr.items[2] = INTEGER_OBJ((Integer)chunk.end_col); + chunk_arr.items[3] = STRING_OBJ(cstr_to_string(chunk.group)); + hl.items[i] = ARRAY_OBJ(chunk_arr); + } + ret.items[ret.size++] = (KeyValuePair) { + .key = STATIC_CSTR_TO_STRING("highlight"), + .value = ARRAY_OBJ(hl), + }; + } + kvi_destroy(colors); + + // Walk over the AST, freeing nodes in process. + ExprASTConvStack ast_conv_stack; + kvi_init(ast_conv_stack); + kvi_push(ast_conv_stack, ((ExprASTConvStackItem) { + .node_p = &east.root, + .ret_node_p = &ret.items[0].value, + })); + while (kv_size(ast_conv_stack)) { + ExprASTConvStackItem cur_item = kv_last(ast_conv_stack); + ExprASTNode *const node = *cur_item.node_p; + if (node == NULL) { + assert(kv_size(ast_conv_stack) == 1); + kv_drop(ast_conv_stack, 1); + } else { + if (cur_item.ret_node_p->type == kObjectTypeNil) { + const size_t ret_node_items_size = (size_t)( + 3 // "type", "start" and "len" + + (node->children != NULL) // "children" + + (node->type == kExprNodeOption + || node->type == kExprNodePlainIdentifier) // "scope" + + (node->type == kExprNodeOption + || node->type == kExprNodePlainIdentifier + || node->type == kExprNodePlainKey + || node->type == kExprNodeEnvironment) // "ident" + + (node->type == kExprNodeRegister) // "name" + + (3 // "cmp_type", "ccs_strategy", "invert" + * (node->type == kExprNodeComparison)) + + (node->type == kExprNodeInteger) // "ivalue" + + (node->type == kExprNodeFloat) // "fvalue" + + (node->type == kExprNodeDoubleQuotedString + || node->type == kExprNodeSingleQuotedString) // "svalue" + + (node->type == kExprNodeAssignment) // "augmentation" + + 0); + Dictionary ret_node = { + .items = xmalloc(ret_node_items_size * sizeof(ret_node.items[0])), + .capacity = ret_node_items_size, + .size = 0, + }; + *cur_item.ret_node_p = DICTIONARY_OBJ(ret_node); + } + Dictionary *ret_node = &cur_item.ret_node_p->data.dictionary; + if (node->children != NULL) { + const size_t num_children = 1 + (node->children->next != NULL); + Array children_array = { + .items = xmalloc(num_children * sizeof(children_array.items[0])), + .capacity = num_children, + .size = num_children, + }; + for (size_t i = 0; i < num_children; i++) { + children_array.items[i] = NIL; + } + ret_node->items[ret_node->size++] = (KeyValuePair) { + .key = STATIC_CSTR_TO_STRING("children"), + .value = ARRAY_OBJ(children_array), + }; + kvi_push(ast_conv_stack, ((ExprASTConvStackItem) { + .node_p = &node->children, + .ret_node_p = &children_array.items[0], + })); + } else if (node->next != NULL) { + kvi_push(ast_conv_stack, ((ExprASTConvStackItem) { + .node_p = &node->next, + .ret_node_p = cur_item.ret_node_p + 1, + })); + } else if (node != NULL) { + kv_drop(ast_conv_stack, 1); + ret_node->items[ret_node->size++] = (KeyValuePair) { + .key = STATIC_CSTR_TO_STRING("type"), + .value = STRING_OBJ(cstr_to_string(east_node_type_tab[node->type])), + }; + Array start_array = { + .items = xmalloc(2 * sizeof(start_array.items[0])), + .capacity = 2, + .size = 2, + }; + start_array.items[0] = INTEGER_OBJ((Integer)node->start.line); + start_array.items[1] = INTEGER_OBJ((Integer)node->start.col); + ret_node->items[ret_node->size++] = (KeyValuePair) { + .key = STATIC_CSTR_TO_STRING("start"), + .value = ARRAY_OBJ(start_array), + }; + ret_node->items[ret_node->size++] = (KeyValuePair) { + .key = STATIC_CSTR_TO_STRING("len"), + .value = INTEGER_OBJ((Integer)node->len), + }; + switch (node->type) { + case kExprNodeDoubleQuotedString: + case kExprNodeSingleQuotedString: { + ret_node->items[ret_node->size++] = (KeyValuePair) { + .key = STATIC_CSTR_TO_STRING("svalue"), + .value = STRING_OBJ(((String) { + .data = node->data.str.value, + .size = node->data.str.size, + })), + }; + break; + } + case kExprNodeOption: { + ret_node->items[ret_node->size++] = (KeyValuePair) { + .key = STATIC_CSTR_TO_STRING("scope"), + .value = INTEGER_OBJ(node->data.opt.scope), + }; + ret_node->items[ret_node->size++] = (KeyValuePair) { + .key = STATIC_CSTR_TO_STRING("ident"), + .value = STRING_OBJ(((String) { + .data = xmemdupz(node->data.opt.ident, + node->data.opt.ident_len), + .size = node->data.opt.ident_len, + })), + }; + break; + } + case kExprNodePlainIdentifier: { + ret_node->items[ret_node->size++] = (KeyValuePair) { + .key = STATIC_CSTR_TO_STRING("scope"), + .value = INTEGER_OBJ(node->data.var.scope), + }; + ret_node->items[ret_node->size++] = (KeyValuePair) { + .key = STATIC_CSTR_TO_STRING("ident"), + .value = STRING_OBJ(((String) { + .data = xmemdupz(node->data.var.ident, + node->data.var.ident_len), + .size = node->data.var.ident_len, + })), + }; + break; + } + case kExprNodePlainKey: { + ret_node->items[ret_node->size++] = (KeyValuePair) { + .key = STATIC_CSTR_TO_STRING("ident"), + .value = STRING_OBJ(((String) { + .data = xmemdupz(node->data.var.ident, + node->data.var.ident_len), + .size = node->data.var.ident_len, + })), + }; + break; + } + case kExprNodeEnvironment: { + ret_node->items[ret_node->size++] = (KeyValuePair) { + .key = STATIC_CSTR_TO_STRING("ident"), + .value = STRING_OBJ(((String) { + .data = xmemdupz(node->data.env.ident, + node->data.env.ident_len), + .size = node->data.env.ident_len, + })), + }; + break; + } + case kExprNodeRegister: { + ret_node->items[ret_node->size++] = (KeyValuePair) { + .key = STATIC_CSTR_TO_STRING("name"), + .value = INTEGER_OBJ(node->data.reg.name), + }; + break; + } + case kExprNodeComparison: { + ret_node->items[ret_node->size++] = (KeyValuePair) { + .key = STATIC_CSTR_TO_STRING("cmp_type"), + .value = STRING_OBJ(cstr_to_string( + eltkn_cmp_type_tab[node->data.cmp.type])), + }; + ret_node->items[ret_node->size++] = (KeyValuePair) { + .key = STATIC_CSTR_TO_STRING("ccs_strategy"), + .value = STRING_OBJ(cstr_to_string( + ccs_tab[node->data.cmp.ccs])), + }; + ret_node->items[ret_node->size++] = (KeyValuePair) { + .key = STATIC_CSTR_TO_STRING("invert"), + .value = BOOLEAN_OBJ(node->data.cmp.inv), + }; + break; + } + case kExprNodeFloat: { + ret_node->items[ret_node->size++] = (KeyValuePair) { + .key = STATIC_CSTR_TO_STRING("fvalue"), + .value = FLOAT_OBJ(node->data.flt.value), + }; + break; + } + case kExprNodeInteger: { + ret_node->items[ret_node->size++] = (KeyValuePair) { + .key = STATIC_CSTR_TO_STRING("ivalue"), + .value = INTEGER_OBJ((Integer)( + node->data.num.value > API_INTEGER_MAX + ? API_INTEGER_MAX + : (Integer)node->data.num.value)), + }; + break; + } + case kExprNodeAssignment: { + const ExprAssignmentType asgn_type = node->data.ass.type; + ret_node->items[ret_node->size++] = (KeyValuePair) { + .key = STATIC_CSTR_TO_STRING("augmentation"), + .value = STRING_OBJ( + asgn_type == kExprAsgnPlain + ? (String)STRING_INIT + : cstr_to_string(expr_asgn_type_tab[asgn_type])), + }; + break; + } + case kExprNodeMissing: + case kExprNodeOpMissing: + case kExprNodeTernary: + case kExprNodeTernaryValue: + case kExprNodeSubscript: + case kExprNodeListLiteral: + case kExprNodeUnaryPlus: + case kExprNodeBinaryPlus: + case kExprNodeNested: + case kExprNodeCall: + case kExprNodeComplexIdentifier: + case kExprNodeUnknownFigure: + case kExprNodeLambda: + case kExprNodeDictLiteral: + case kExprNodeCurlyBracesIdentifier: + case kExprNodeComma: + case kExprNodeColon: + case kExprNodeArrow: + case kExprNodeConcat: + case kExprNodeConcatOrSubscript: + case kExprNodeOr: + case kExprNodeAnd: + case kExprNodeUnaryMinus: + case kExprNodeBinaryMinus: + case kExprNodeNot: + case kExprNodeMultiplication: + case kExprNodeDivision: + case kExprNodeMod: { + break; + } + } + assert(cur_item.ret_node_p->data.dictionary.size + == cur_item.ret_node_p->data.dictionary.capacity); + xfree(*cur_item.node_p); + *cur_item.node_p = NULL; + } + } + } + kvi_destroy(ast_conv_stack); + + assert(ret.size == ret.capacity); + // Should be a no-op actually, leaving it in case non-nodes will need to be + // freed later. + viml_pexpr_free_ast(east); + viml_parser_destroy(&pstate); + return ret; +} + /// Writes a message to vim output or error buffer. The string is split /// and flushed after each newline. Incomplete lines are kept for writing @@ -944,3 +1470,95 @@ Float nvim__id_float(Float flt) { return flt; } + +/// Gets a list of dictionaries representing attached UIs. +/// +/// @return Array of UI dictionaries +Array nvim_list_uis(void) + FUNC_API_SINCE(4) +{ + return ui_array(); +} + +/// Gets the immediate children of process `pid`. +/// +/// @return Array of child process ids, empty if process not found. +Array nvim_get_proc_children(Integer pid, Error *err) + FUNC_API_SINCE(4) +{ + Array rvobj = ARRAY_DICT_INIT; + int *proc_list = NULL; + + if (pid <= 0 || pid > INT_MAX) { + api_set_error(err, kErrorTypeException, "Invalid pid: %" PRId64, pid); + goto end; + } + + size_t proc_count; + int rv = os_proc_children((int)pid, &proc_list, &proc_count); + if (rv != 0) { + // syscall failed (possibly because of kernel options), try shelling out. + DLOG("fallback to vim._os_proc_children()"); + Array a = ARRAY_DICT_INIT; + ADD(a, INTEGER_OBJ(pid)); + String s = cstr_to_string("return vim._os_proc_children(select(1, ...))"); + Object o = nvim_execute_lua(s, a, err); + api_free_string(s); + api_free_array(a); + if (o.type == kObjectTypeArray) { + rvobj = o.data.array; + } else if (!ERROR_SET(err)) { + api_set_error(err, kErrorTypeException, + "Failed to get process children. pid=%" PRId64 " error=%d", + pid, rv); + } + goto end; + } + + for (size_t i = 0; i < proc_count; i++) { + ADD(rvobj, INTEGER_OBJ(proc_list[i])); + } + +end: + xfree(proc_list); + return rvobj; +} + +/// Gets info describing process `pid`. +/// +/// @return Map of process properties, or NIL if process not found. +Object nvim_get_proc(Integer pid, Error *err) + FUNC_API_SINCE(4) +{ + Object rvobj = OBJECT_INIT; + rvobj.data.dictionary = (Dictionary)ARRAY_DICT_INIT; + rvobj.type = kObjectTypeDictionary; + + if (pid <= 0 || pid > INT_MAX) { + api_set_error(err, kErrorTypeException, "Invalid pid: %" PRId64, pid); + return NIL; + } +#ifdef WIN32 + rvobj.data.dictionary = os_proc_info((int)pid); + if (rvobj.data.dictionary.size == 0) { // Process not found. + return NIL; + } +#else + // Cross-platform process info APIs are miserable, so use `ps` instead. + Array a = ARRAY_DICT_INIT; + ADD(a, INTEGER_OBJ(pid)); + String s = cstr_to_string("return vim._os_proc_info(select(1, ...))"); + Object o = nvim_execute_lua(s, a, err); + api_free_string(s); + api_free_array(a); + if (o.type == kObjectTypeArray && o.data.array.size == 0) { + return NIL; // Process not found. + } else if (o.type == kObjectTypeDictionary) { + rvobj.data.dictionary = o.data.dictionary; + } else if (!ERROR_SET(err)) { + api_set_error(err, kErrorTypeException, + "Failed to get process info. pid=%" PRId64, pid); + } +#endif + return rvobj; +} diff --git a/src/nvim/arabic.c b/src/nvim/arabic.c index 1ef51d2a2a..e120e6d492 100644 --- a/src/nvim/arabic.c +++ b/src/nvim/arabic.c @@ -393,491 +393,141 @@ static bool A_is_f(int cur_c) // Change shape - from ISO-8859-6/Isolated to Form-B Isolated static int chg_c_a2s(int cur_c) { - int tempc; - switch (cur_c) { - case a_HAMZA: - tempc = a_s_HAMZA; - break; - - case a_ALEF_MADDA: - tempc = a_s_ALEF_MADDA; - break; - - case a_ALEF_HAMZA_ABOVE: - tempc = a_s_ALEF_HAMZA_ABOVE; - break; - - case a_WAW_HAMZA: - tempc = a_s_WAW_HAMZA; - break; - - case a_ALEF_HAMZA_BELOW: - tempc = a_s_ALEF_HAMZA_BELOW; - break; - - case a_YEH_HAMZA: - tempc = a_s_YEH_HAMZA; - break; - - case a_ALEF: - tempc = a_s_ALEF; - break; - - case a_TEH_MARBUTA: - tempc = a_s_TEH_MARBUTA; - break; - - case a_DAL: - tempc = a_s_DAL; - break; - - case a_THAL: - tempc = a_s_THAL; - break; - - case a_REH: - tempc = a_s_REH; - break; - - case a_ZAIN: - tempc = a_s_ZAIN; - break; - - case a_TATWEEL: // exceptions - tempc = cur_c; - break; - - case a_WAW: - tempc = a_s_WAW; - break; - - case a_ALEF_MAKSURA: - tempc = a_s_ALEF_MAKSURA; - break; - - case a_BEH: - tempc = a_s_BEH; - break; - - case a_TEH: - tempc = a_s_TEH; - break; - - case a_THEH: - tempc = a_s_THEH; - break; - - case a_JEEM: - tempc = a_s_JEEM; - break; - - case a_HAH: - tempc = a_s_HAH; - break; - - case a_KHAH: - tempc = a_s_KHAH; - break; - - case a_SEEN: - tempc = a_s_SEEN; - break; - - case a_SHEEN: - tempc = a_s_SHEEN; - break; - - case a_SAD: - tempc = a_s_SAD; - break; - - case a_DAD: - tempc = a_s_DAD; - break; - - case a_TAH: - tempc = a_s_TAH; - break; - - case a_ZAH: - tempc = a_s_ZAH; - break; - - case a_AIN: - tempc = a_s_AIN; - break; - - case a_GHAIN: - tempc = a_s_GHAIN; - break; - - case a_FEH: - tempc = a_s_FEH; - break; - - case a_QAF: - tempc = a_s_QAF; - break; - - case a_KAF: - tempc = a_s_KAF; - break; - - case a_LAM: - tempc = a_s_LAM; - break; - - case a_MEEM: - tempc = a_s_MEEM; - break; - - case a_NOON: - tempc = a_s_NOON; - break; - - case a_HEH: - tempc = a_s_HEH; - break; - - case a_YEH: - tempc = a_s_YEH; - break; - - default: - tempc = 0; + case a_HAMZA: return a_s_HAMZA; + case a_ALEF_MADDA: return a_s_ALEF_MADDA; + case a_ALEF_HAMZA_ABOVE: return a_s_ALEF_HAMZA_ABOVE; + case a_WAW_HAMZA: return a_s_WAW_HAMZA; + case a_ALEF_HAMZA_BELOW: return a_s_ALEF_HAMZA_BELOW; + case a_YEH_HAMZA: return a_s_YEH_HAMZA; + case a_ALEF: return a_s_ALEF; + case a_TEH_MARBUTA: return a_s_TEH_MARBUTA; + case a_DAL: return a_s_DAL; + case a_THAL: return a_s_THAL; + case a_REH: return a_s_REH; + case a_ZAIN: return a_s_ZAIN; + case a_TATWEEL: return cur_c; // exceptions + case a_WAW: return a_s_WAW; + case a_ALEF_MAKSURA: return a_s_ALEF_MAKSURA; + case a_BEH: return a_s_BEH; + case a_TEH: return a_s_TEH; + case a_THEH: return a_s_THEH; + case a_JEEM: return a_s_JEEM; + case a_HAH: return a_s_HAH; + case a_KHAH: return a_s_KHAH; + case a_SEEN: return a_s_SEEN; + case a_SHEEN: return a_s_SHEEN; + case a_SAD: return a_s_SAD; + case a_DAD: return a_s_DAD; + case a_TAH: return a_s_TAH; + case a_ZAH: return a_s_ZAH; + case a_AIN: return a_s_AIN; + case a_GHAIN: return a_s_GHAIN; + case a_FEH: return a_s_FEH; + case a_QAF: return a_s_QAF; + case a_KAF: return a_s_KAF; + case a_LAM: return a_s_LAM; + case a_MEEM: return a_s_MEEM; + case a_NOON: return a_s_NOON; + case a_HEH: return a_s_HEH; + case a_YEH: return a_s_YEH; } - - return tempc; + return 0; } // Change shape - from ISO-8859-6/Isolated to Initial static int chg_c_a2i(int cur_c) { - int tempc; - switch (cur_c) { - case a_YEH_HAMZA: - tempc = a_i_YEH_HAMZA; - break; - - case a_HAMZA: // exceptions - tempc = a_s_HAMZA; - break; - - case a_ALEF_MADDA: // exceptions - tempc = a_s_ALEF_MADDA; - break; - - case a_ALEF_HAMZA_ABOVE: // exceptions - tempc = a_s_ALEF_HAMZA_ABOVE; - break; - - case a_WAW_HAMZA: // exceptions - tempc = a_s_WAW_HAMZA; - break; - - case a_ALEF_HAMZA_BELOW: // exceptions - tempc = a_s_ALEF_HAMZA_BELOW; - break; - - case a_ALEF: // exceptions - tempc = a_s_ALEF; - break; - - case a_TEH_MARBUTA: // exceptions - tempc = a_s_TEH_MARBUTA; - break; - - case a_DAL: // exceptions - tempc = a_s_DAL; - break; - - case a_THAL: // exceptions - tempc = a_s_THAL; - break; - - case a_REH: // exceptions - tempc = a_s_REH; - break; - - case a_ZAIN: // exceptions - tempc = a_s_ZAIN; - break; - - case a_TATWEEL: // exceptions - tempc = cur_c; - break; - - case a_WAW: // exceptions - tempc = a_s_WAW; - break; - - case a_ALEF_MAKSURA: // exceptions - tempc = a_s_ALEF_MAKSURA; - break; - - case a_BEH: - tempc = a_i_BEH; - break; - - case a_TEH: - tempc = a_i_TEH; - break; - - case a_THEH: - tempc = a_i_THEH; - break; - - case a_JEEM: - tempc = a_i_JEEM; - break; - - case a_HAH: - tempc = a_i_HAH; - break; - - case a_KHAH: - tempc = a_i_KHAH; - break; - - case a_SEEN: - tempc = a_i_SEEN; - break; - - case a_SHEEN: - tempc = a_i_SHEEN; - break; - - case a_SAD: - tempc = a_i_SAD; - break; - - case a_DAD: - tempc = a_i_DAD; - break; - - case a_TAH: - tempc = a_i_TAH; - break; - - case a_ZAH: - tempc = a_i_ZAH; - break; - - case a_AIN: - tempc = a_i_AIN; - break; - - case a_GHAIN: - tempc = a_i_GHAIN; - break; - - case a_FEH: - tempc = a_i_FEH; - break; - - case a_QAF: - tempc = a_i_QAF; - break; - - case a_KAF: - tempc = a_i_KAF; - break; - - case a_LAM: - tempc = a_i_LAM; - break; - - case a_MEEM: - tempc = a_i_MEEM; - break; - - case a_NOON: - tempc = a_i_NOON; - break; - - case a_HEH: - tempc = a_i_HEH; - break; - - case a_YEH: - tempc = a_i_YEH; - break; - - default: - tempc = 0; + case a_YEH_HAMZA: return a_i_YEH_HAMZA; + case a_HAMZA: return a_s_HAMZA; // exceptions + case a_ALEF_MADDA: return a_s_ALEF_MADDA; // exceptions + case a_ALEF_HAMZA_ABOVE: return a_s_ALEF_HAMZA_ABOVE; // exceptions + case a_WAW_HAMZA: return a_s_WAW_HAMZA; // exceptions + case a_ALEF_HAMZA_BELOW: return a_s_ALEF_HAMZA_BELOW; // exceptions + case a_ALEF: return a_s_ALEF; // exceptions + case a_TEH_MARBUTA: return a_s_TEH_MARBUTA; // exceptions + case a_DAL: return a_s_DAL; // exceptions + case a_THAL: return a_s_THAL; // exceptions + case a_REH: return a_s_REH; // exceptions + case a_ZAIN: return a_s_ZAIN; // exceptions + case a_TATWEEL: return cur_c; // exceptions + case a_WAW: return a_s_WAW; // exceptions + case a_ALEF_MAKSURA: return a_s_ALEF_MAKSURA; // exceptions + case a_BEH: return a_i_BEH; + case a_TEH: return a_i_TEH; + case a_THEH: return a_i_THEH; + case a_JEEM: return a_i_JEEM; + case a_HAH: return a_i_HAH; + case a_KHAH: return a_i_KHAH; + case a_SEEN: return a_i_SEEN; + case a_SHEEN: return a_i_SHEEN; + case a_SAD: return a_i_SAD; + case a_DAD: return a_i_DAD; + case a_TAH: return a_i_TAH; + case a_ZAH: return a_i_ZAH; + case a_AIN: return a_i_AIN; + case a_GHAIN: return a_i_GHAIN; + case a_FEH: return a_i_FEH; + case a_QAF: return a_i_QAF; + case a_KAF: return a_i_KAF; + case a_LAM: return a_i_LAM; + case a_MEEM: return a_i_MEEM; + case a_NOON: return a_i_NOON; + case a_HEH: return a_i_HEH; + case a_YEH: return a_i_YEH; } - - return tempc; + return 0; } // Change shape - from ISO-8859-6/Isolated to Medial static int chg_c_a2m(int cur_c) { - int tempc; - switch (cur_c) { - case a_HAMZA: // exception - tempc = a_s_HAMZA; - break; - - case a_ALEF_MADDA: // exception - tempc = a_f_ALEF_MADDA; - break; - - case a_ALEF_HAMZA_ABOVE: // exception - tempc = a_f_ALEF_HAMZA_ABOVE; - break; - - case a_WAW_HAMZA: // exception - tempc = a_f_WAW_HAMZA; - break; - - case a_ALEF_HAMZA_BELOW: // exception - tempc = a_f_ALEF_HAMZA_BELOW; - break; - - case a_YEH_HAMZA: - tempc = a_m_YEH_HAMZA; - break; - - case a_ALEF: // exception - tempc = a_f_ALEF; - break; - - case a_BEH: - tempc = a_m_BEH; - break; - - case a_TEH_MARBUTA: // exception - tempc = a_f_TEH_MARBUTA; - break; - - case a_TEH: - tempc = a_m_TEH; - break; - - case a_THEH: - tempc = a_m_THEH; - break; - - case a_JEEM: - tempc = a_m_JEEM; - break; - - case a_HAH: - tempc = a_m_HAH; - break; - - case a_KHAH: - tempc = a_m_KHAH; - break; - - case a_DAL: // exception - tempc = a_f_DAL; - break; - - case a_THAL: // exception - tempc = a_f_THAL; - break; - - case a_REH: // exception - tempc = a_f_REH; - break; - - case a_ZAIN: // exception - tempc = a_f_ZAIN; - break; - - case a_SEEN: - tempc = a_m_SEEN; - break; - - case a_SHEEN: - tempc = a_m_SHEEN; - break; - - case a_SAD: - tempc = a_m_SAD; - break; - - case a_DAD: - tempc = a_m_DAD; - break; - - case a_TAH: - tempc = a_m_TAH; - break; - - case a_ZAH: - tempc = a_m_ZAH; - break; - - case a_AIN: - tempc = a_m_AIN; - break; - - case a_GHAIN: - tempc = a_m_GHAIN; - break; - - case a_TATWEEL: // exception - tempc = cur_c; - break; - - case a_FEH: - tempc = a_m_FEH; - break; - - case a_QAF: - tempc = a_m_QAF; - break; - - case a_KAF: - tempc = a_m_KAF; - break; - - case a_LAM: - tempc = a_m_LAM; - break; - - case a_MEEM: - tempc = a_m_MEEM; - break; - - case a_NOON: - tempc = a_m_NOON; - break; - - case a_HEH: - tempc = a_m_HEH; - break; - - case a_WAW: // exception - tempc = a_f_WAW; - break; - - case a_ALEF_MAKSURA: // exception - tempc = a_f_ALEF_MAKSURA; - break; - - case a_YEH: - tempc = a_m_YEH; - break; - - default: - tempc = 0; + case a_HAMZA: return a_s_HAMZA; // exception + case a_ALEF_MADDA: return a_f_ALEF_MADDA; // exception + case a_ALEF_HAMZA_ABOVE: return a_f_ALEF_HAMZA_ABOVE; // exception + case a_WAW_HAMZA: return a_f_WAW_HAMZA; // exception + case a_ALEF_HAMZA_BELOW: return a_f_ALEF_HAMZA_BELOW; // exception + case a_YEH_HAMZA: return a_m_YEH_HAMZA; + case a_ALEF: return a_f_ALEF; // exception + case a_BEH: return a_m_BEH; + case a_TEH_MARBUTA: return a_f_TEH_MARBUTA; // exception + case a_TEH: return a_m_TEH; + case a_THEH: return a_m_THEH; + case a_JEEM: return a_m_JEEM; + case a_HAH: return a_m_HAH; + case a_KHAH: return a_m_KHAH; + case a_DAL: return a_f_DAL; // exception + case a_THAL: return a_f_THAL; // exception + case a_REH: return a_f_REH; // exception + case a_ZAIN: return a_f_ZAIN; // exception + case a_SEEN: return a_m_SEEN; + case a_SHEEN: return a_m_SHEEN; + case a_SAD: return a_m_SAD; + case a_DAD: return a_m_DAD; + case a_TAH: return a_m_TAH; + case a_ZAH: return a_m_ZAH; + case a_AIN: return a_m_AIN; + case a_GHAIN: return a_m_GHAIN; + case a_TATWEEL: return cur_c; // exception + case a_FEH: return a_m_FEH; + case a_QAF: return a_m_QAF; + case a_KAF: return a_m_KAF; + case a_LAM: return a_m_LAM; + case a_MEEM: return a_m_MEEM; + case a_NOON: return a_m_NOON; + case a_HEH: return a_m_HEH; + case a_WAW: return a_f_WAW; // exception + case a_ALEF_MAKSURA: return a_f_ALEF_MAKSURA; // exception + case a_YEH: return a_m_YEH; } - - return tempc; + return 0; } // Change shape - from ISO-8859-6/Isolated to final static int chg_c_a2f(int cur_c) { - int tempc; - // NOTE: these encodings need to be accounted for // // a_f_ALEF_MADDA; @@ -888,280 +538,87 @@ static int chg_c_a2f(int cur_c) // a_f_LAM_ALEF_HAMZA_BELOW; switch (cur_c) { - case a_HAMZA: // exception - tempc = a_s_HAMZA; - break; - - case a_ALEF_MADDA: - tempc = a_f_ALEF_MADDA; - break; - - case a_ALEF_HAMZA_ABOVE: - tempc = a_f_ALEF_HAMZA_ABOVE; - break; - - case a_WAW_HAMZA: - tempc = a_f_WAW_HAMZA; - break; - - case a_ALEF_HAMZA_BELOW: - tempc = a_f_ALEF_HAMZA_BELOW; - break; - - case a_YEH_HAMZA: - tempc = a_f_YEH_HAMZA; - break; - - case a_ALEF: - tempc = a_f_ALEF; - break; - - case a_BEH: - tempc = a_f_BEH; - break; - - case a_TEH_MARBUTA: - tempc = a_f_TEH_MARBUTA; - break; - - case a_TEH: - tempc = a_f_TEH; - break; - - case a_THEH: - tempc = a_f_THEH; - break; - - case a_JEEM: - tempc = a_f_JEEM; - break; - - case a_HAH: - tempc = a_f_HAH; - break; - - case a_KHAH: - tempc = a_f_KHAH; - break; - - case a_DAL: - tempc = a_f_DAL; - break; - - case a_THAL: - tempc = a_f_THAL; - break; - - case a_REH: - tempc = a_f_REH; - break; - - case a_ZAIN: - tempc = a_f_ZAIN; - break; - - case a_SEEN: - tempc = a_f_SEEN; - break; - - case a_SHEEN: - tempc = a_f_SHEEN; - break; - - case a_SAD: - tempc = a_f_SAD; - break; - - case a_DAD: - tempc = a_f_DAD; - break; - - case a_TAH: - tempc = a_f_TAH; - break; - - case a_ZAH: - tempc = a_f_ZAH; - break; - - case a_AIN: - tempc = a_f_AIN; - break; - - case a_GHAIN: - tempc = a_f_GHAIN; - break; - - case a_TATWEEL: // exception - tempc = cur_c; - break; - - case a_FEH: - tempc = a_f_FEH; - break; - - case a_QAF: - tempc = a_f_QAF; - break; - - case a_KAF: - tempc = a_f_KAF; - break; - - case a_LAM: - tempc = a_f_LAM; - break; - - case a_MEEM: - tempc = a_f_MEEM; - break; - - case a_NOON: - tempc = a_f_NOON; - break; - - case a_HEH: - tempc = a_f_HEH; - break; - - case a_WAW: - tempc = a_f_WAW; - break; - - case a_ALEF_MAKSURA: - tempc = a_f_ALEF_MAKSURA; - break; - - case a_YEH: - tempc = a_f_YEH; - break; - - default: - tempc = 0; + case a_HAMZA: return a_s_HAMZA; // exception + case a_ALEF_MADDA: return a_f_ALEF_MADDA; + case a_ALEF_HAMZA_ABOVE: return a_f_ALEF_HAMZA_ABOVE; + case a_WAW_HAMZA: return a_f_WAW_HAMZA; + case a_ALEF_HAMZA_BELOW: return a_f_ALEF_HAMZA_BELOW; + case a_YEH_HAMZA: return a_f_YEH_HAMZA; + case a_ALEF: return a_f_ALEF; + case a_BEH: return a_f_BEH; + case a_TEH_MARBUTA: return a_f_TEH_MARBUTA; + case a_TEH: return a_f_TEH; + case a_THEH: return a_f_THEH; + case a_JEEM: return a_f_JEEM; + case a_HAH: return a_f_HAH; + case a_KHAH: return a_f_KHAH; + case a_DAL: return a_f_DAL; + case a_THAL: return a_f_THAL; + case a_REH: return a_f_REH; + case a_ZAIN: return a_f_ZAIN; + case a_SEEN: return a_f_SEEN; + case a_SHEEN: return a_f_SHEEN; + case a_SAD: return a_f_SAD; + case a_DAD: return a_f_DAD; + case a_TAH: return a_f_TAH; + case a_ZAH: return a_f_ZAH; + case a_AIN: return a_f_AIN; + case a_GHAIN: return a_f_GHAIN; + case a_TATWEEL: return cur_c; // exception + case a_FEH: return a_f_FEH; + case a_QAF: return a_f_QAF; + case a_KAF: return a_f_KAF; + case a_LAM: return a_f_LAM; + case a_MEEM: return a_f_MEEM; + case a_NOON: return a_f_NOON; + case a_HEH: return a_f_HEH; + case a_WAW: return a_f_WAW; + case a_ALEF_MAKSURA: return a_f_ALEF_MAKSURA; + case a_YEH: return a_f_YEH; } - - return tempc; + return 0; } // Change shape - from Initial to Medial static int chg_c_i2m(int cur_c) { - int tempc; - switch (cur_c) { - case a_i_YEH_HAMZA: - tempc = a_m_YEH_HAMZA; - break; - - case a_i_BEH: - tempc = a_m_BEH; - break; - - case a_i_TEH: - tempc = a_m_TEH; - break; - - case a_i_THEH: - tempc = a_m_THEH; - break; - - case a_i_JEEM: - tempc = a_m_JEEM; - break; - - case a_i_HAH: - tempc = a_m_HAH; - break; - - case a_i_KHAH: - tempc = a_m_KHAH; - break; - - case a_i_SEEN: - tempc = a_m_SEEN; - break; - - case a_i_SHEEN: - tempc = a_m_SHEEN; - break; - - case a_i_SAD: - tempc = a_m_SAD; - break; - - case a_i_DAD: - tempc = a_m_DAD; - break; - - case a_i_TAH: - tempc = a_m_TAH; - break; - - case a_i_ZAH: - tempc = a_m_ZAH; - break; - - case a_i_AIN: - tempc = a_m_AIN; - break; - - case a_i_GHAIN: - tempc = a_m_GHAIN; - break; - - case a_i_FEH: - tempc = a_m_FEH; - break; - - case a_i_QAF: - tempc = a_m_QAF; - break; - - case a_i_KAF: - tempc = a_m_KAF; - break; - - case a_i_LAM: - tempc = a_m_LAM; - break; - - case a_i_MEEM: - tempc = a_m_MEEM; - break; - - case a_i_NOON: - tempc = a_m_NOON; - break; - - case a_i_HEH: - tempc = a_m_HEH; - break; - - case a_i_YEH: - tempc = a_m_YEH; - break; - - default: - tempc = 0; + case a_i_YEH_HAMZA: return a_m_YEH_HAMZA; + case a_i_BEH: return a_m_BEH; + case a_i_TEH: return a_m_TEH; + case a_i_THEH: return a_m_THEH; + case a_i_JEEM: return a_m_JEEM; + case a_i_HAH: return a_m_HAH; + case a_i_KHAH: return a_m_KHAH; + case a_i_SEEN: return a_m_SEEN; + case a_i_SHEEN: return a_m_SHEEN; + case a_i_SAD: return a_m_SAD; + case a_i_DAD: return a_m_DAD; + case a_i_TAH: return a_m_TAH; + case a_i_ZAH: return a_m_ZAH; + case a_i_AIN: return a_m_AIN; + case a_i_GHAIN: return a_m_GHAIN; + case a_i_FEH: return a_m_FEH; + case a_i_QAF: return a_m_QAF; + case a_i_KAF: return a_m_KAF; + case a_i_LAM: return a_m_LAM; + case a_i_MEEM: return a_m_MEEM; + case a_i_NOON: return a_m_NOON; + case a_i_HEH: return a_m_HEH; + case a_i_YEH: return a_m_YEH; } - - return tempc; + return 0; } // Change shape - from Final to Medial static int chg_c_f2m(int cur_c) { - int tempc; - switch (cur_c) { // NOTE: these encodings are multi-positional, no ? // case a_f_ALEF_MADDA: // case a_f_ALEF_HAMZA_ABOVE: // case a_f_ALEF_HAMZA_BELOW: - case a_f_YEH_HAMZA: - tempc = a_m_YEH_HAMZA; - break; - + case a_f_YEH_HAMZA: return a_m_YEH_HAMZA; case a_f_WAW_HAMZA: // exceptions case a_f_ALEF: case a_f_TEH_MARBUTA: @@ -1171,165 +628,60 @@ static int chg_c_f2m(int cur_c) case a_f_ZAIN: case a_f_WAW: case a_f_ALEF_MAKSURA: - tempc = cur_c; - break; - - case a_f_BEH: - tempc = a_m_BEH; - break; - - case a_f_TEH: - tempc = a_m_TEH; - break; - - case a_f_THEH: - tempc = a_m_THEH; - break; - - case a_f_JEEM: - tempc = a_m_JEEM; - break; - - case a_f_HAH: - tempc = a_m_HAH; - break; - - case a_f_KHAH: - tempc = a_m_KHAH; - break; - - case a_f_SEEN: - tempc = a_m_SEEN; - break; - - case a_f_SHEEN: - tempc = a_m_SHEEN; - break; - - case a_f_SAD: - tempc = a_m_SAD; - break; - - case a_f_DAD: - tempc = a_m_DAD; - break; - - case a_f_TAH: - tempc = a_m_TAH; - break; - - case a_f_ZAH: - tempc = a_m_ZAH; - break; - - case a_f_AIN: - tempc = a_m_AIN; - break; - - case a_f_GHAIN: - tempc = a_m_GHAIN; - break; - - case a_f_FEH: - tempc = a_m_FEH; - break; - - case a_f_QAF: - tempc = a_m_QAF; - break; - - case a_f_KAF: - tempc = a_m_KAF; - break; - - case a_f_LAM: - tempc = a_m_LAM; - break; - - case a_f_MEEM: - tempc = a_m_MEEM; - break; - - case a_f_NOON: - tempc = a_m_NOON; - break; - - case a_f_HEH: - tempc = a_m_HEH; - break; - - case a_f_YEH: - tempc = a_m_YEH; - break; - + return cur_c; + case a_f_BEH: return a_m_BEH; + case a_f_TEH: return a_m_TEH; + case a_f_THEH: return a_m_THEH; + case a_f_JEEM: return a_m_JEEM; + case a_f_HAH: return a_m_HAH; + case a_f_KHAH: return a_m_KHAH; + case a_f_SEEN: return a_m_SEEN; + case a_f_SHEEN: return a_m_SHEEN; + case a_f_SAD: return a_m_SAD; + case a_f_DAD: return a_m_DAD; + case a_f_TAH: return a_m_TAH; + case a_f_ZAH: return a_m_ZAH; + case a_f_AIN: return a_m_AIN; + case a_f_GHAIN: return a_m_GHAIN; + case a_f_FEH: return a_m_FEH; + case a_f_QAF: return a_m_QAF; + case a_f_KAF: return a_m_KAF; + case a_f_LAM: return a_m_LAM; + case a_f_MEEM: return a_m_MEEM; + case a_f_NOON: return a_m_NOON; + case a_f_HEH: return a_m_HEH; + case a_f_YEH: return a_m_YEH; // NOTE: these encodings are multi-positional, no ? // case a_f_LAM_ALEF_MADDA_ABOVE: // case a_f_LAM_ALEF_HAMZA_ABOVE: // case a_f_LAM_ALEF_HAMZA_BELOW: // case a_f_LAM_ALEF: - default: - tempc = 0; } - - return tempc; + return 0; } // Change shape - from Combination (2 char) to an Isolated. static int chg_c_laa2i(int hid_c) { - int tempc; - switch (hid_c) { - case a_ALEF_MADDA: - tempc = a_s_LAM_ALEF_MADDA_ABOVE; - break; - - case a_ALEF_HAMZA_ABOVE: - tempc = a_s_LAM_ALEF_HAMZA_ABOVE; - break; - - case a_ALEF_HAMZA_BELOW: - tempc = a_s_LAM_ALEF_HAMZA_BELOW; - break; - - case a_ALEF: - tempc = a_s_LAM_ALEF; - break; - - default: - tempc = 0; + case a_ALEF_MADDA: return a_s_LAM_ALEF_MADDA_ABOVE; + case a_ALEF_HAMZA_ABOVE: return a_s_LAM_ALEF_HAMZA_ABOVE; + case a_ALEF_HAMZA_BELOW: return a_s_LAM_ALEF_HAMZA_BELOW; + case a_ALEF: return a_s_LAM_ALEF; } - - return tempc; + return 0; } // Change shape - from Combination-Isolated to Final. static int chg_c_laa2f(int hid_c) { - int tempc; - switch (hid_c) { - case a_ALEF_MADDA: - tempc = a_f_LAM_ALEF_MADDA_ABOVE; - break; - - case a_ALEF_HAMZA_ABOVE: - tempc = a_f_LAM_ALEF_HAMZA_ABOVE; - break; - - case a_ALEF_HAMZA_BELOW: - tempc = a_f_LAM_ALEF_HAMZA_BELOW; - break; - - case a_ALEF: - tempc = a_f_LAM_ALEF; - break; - - default: - tempc = 0; + case a_ALEF_MADDA: return a_f_LAM_ALEF_MADDA_ABOVE; + case a_ALEF_HAMZA_ABOVE: return a_f_LAM_ALEF_HAMZA_ABOVE; + case a_ALEF_HAMZA_BELOW: return a_f_LAM_ALEF_HAMZA_BELOW; + case a_ALEF: return a_f_LAM_ALEF; } - - return tempc; + return 0; } // Do "half-shaping" on character "c". Return zero if no shaping. diff --git a/src/nvim/ascii.h b/src/nvim/ascii.h index adde91f9ec..ff6840d690 100644 --- a/src/nvim/ascii.h +++ b/src/nvim/ascii.h @@ -3,6 +3,7 @@ #include <stdbool.h> +#include "nvim/macros.h" #include "nvim/func_attr.h" #include "nvim/os/os_defs.h" @@ -98,6 +99,10 @@ static inline bool ascii_isxdigit(int) REAL_FATTR_CONST REAL_FATTR_ALWAYS_INLINE; +static inline bool ascii_isident(int) + REAL_FATTR_CONST + REAL_FATTR_ALWAYS_INLINE; + static inline bool ascii_isbdigit(int) REAL_FATTR_CONST REAL_FATTR_ALWAYS_INLINE; @@ -138,6 +143,14 @@ static inline bool ascii_isxdigit(int c) || (c >= 'A' && c <= 'F'); } +/// Checks if `c` is an “identifier” character +/// +/// That is, whether it is alphanumeric character or underscore. +static inline bool ascii_isident(int c) +{ + return ASCII_ISALNUM(c) || c == '_'; +} + /// Checks if `c` is a binary digit, that is, 0-1. /// /// @see {ascii_isdigit} diff --git a/src/nvim/aucmd.c b/src/nvim/aucmd.c new file mode 100644 index 0000000000..fc421116ea --- /dev/null +++ b/src/nvim/aucmd.c @@ -0,0 +1,41 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +#include "nvim/os/os.h" +#include "nvim/fileio.h" +#include "nvim/vim.h" +#include "nvim/main.h" +#include "nvim/ui.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "aucmd.c.generated.h" +#endif + +static void focusgained_event(void **argv) +{ + bool *gainedp = argv[0]; + do_autocmd_focusgained(*gainedp); + xfree(gainedp); +} +void aucmd_schedule_focusgained(bool gained) +{ + bool *gainedp = xmalloc(sizeof(*gainedp)); + *gainedp = gained; + loop_schedule_deferred(&main_loop, + event_create(focusgained_event, 1, gainedp)); +} + +static void do_autocmd_focusgained(bool gained) + FUNC_ATTR_NONNULL_ALL +{ + static bool recursive = false; + + if (recursive) { + return; // disallow recursion + } + recursive = true; + apply_autocmds((gained ? EVENT_FOCUSGAINED : EVENT_FOCUSLOST), + NULL, NULL, false, curbuf); + recursive = false; +} + diff --git a/src/nvim/aucmd.h b/src/nvim/aucmd.h new file mode 100644 index 0000000000..6570ba7a92 --- /dev/null +++ b/src/nvim/aucmd.h @@ -0,0 +1,9 @@ +#ifndef NVIM_AUCMD_H +#define NVIM_AUCMD_H + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "aucmd.h.generated.h" +#endif + +#endif // NVIM_AUCMD_H + diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua index 68a47c244f..7dfaf54ff0 100644 --- a/src/nvim/auevents.lua +++ b/src/nvim/auevents.lua @@ -19,6 +19,8 @@ return { 'BufWriteCmd', -- write buffer using command 'BufWritePost', -- after writing a buffer 'BufWritePre', -- before writing a buffer + 'CmdLineEnter', -- after entering cmdline mode + 'CmdLineLeave', -- before leaving cmdline mode 'CmdUndefined', -- command undefined 'CmdWinEnter', -- after entering the cmdline window 'CmdWinLeave', -- before leaving the cmdline window diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index b5ca6543c5..0cd6f628b5 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -64,7 +64,6 @@ #include "nvim/spell.h" #include "nvim/strings.h" #include "nvim/syntax.h" -#include "nvim/terminal.h" #include "nvim/ui.h" #include "nvim/undo.h" #include "nvim/version.h" @@ -91,6 +90,57 @@ static char *e_auabort = N_("E855: Autocommands caused command to abort"); // Number of times free_buffer() was called. static int buf_free_count = 0; +// Read data from buffer for retrying. +static int +read_buffer( + int read_stdin, // read file from stdin, otherwise fifo + exarg_T *eap, // for forced 'ff' and 'fenc' or NULL + int flags) // extra flags for readfile() +{ + int retval = OK; + linenr_T line_count; + + // + // Read from the buffer which the text is already filled in and append at + // the end. This makes it possible to retry when 'fileformat' or + // 'fileencoding' was guessed wrong. + // + line_count = curbuf->b_ml.ml_line_count; + retval = readfile( + read_stdin ? NULL : curbuf->b_ffname, + read_stdin ? NULL : curbuf->b_fname, + (linenr_T)line_count, (linenr_T)0, (linenr_T)MAXLNUM, eap, + flags | READ_BUFFER); + if (retval == OK) { + // Delete the binary lines. + while (--line_count >= 0) { + ml_delete((linenr_T)1, false); + } + } else { + // Delete the converted lines. + while (curbuf->b_ml.ml_line_count > line_count) { + ml_delete(line_count, false); + } + } + // Put the cursor on the first line. + curwin->w_cursor.lnum = 1; + curwin->w_cursor.col = 0; + + if (read_stdin) { + // Set or reset 'modified' before executing autocommands, so that + // it can be changed there. + if (!readonlymode && !BUFEMPTY()) { + changed(); + } else if (retval != FAIL) { + unchanged(curbuf, false); + } + + apply_autocmds_retval(EVENT_STDINREADPOST, NULL, NULL, false, + curbuf, &retval); + } + return retval; +} + /* * Open current buffer, that is: open the memfile and read the file into * memory. @@ -106,6 +156,7 @@ open_buffer ( int retval = OK; bufref_T old_curbuf; long old_tw = curbuf->b_p_tw; + int read_fifo = false; /* * The 'readonly' flag is only set when BF_NEVERLOADED is being reset. @@ -156,13 +207,45 @@ open_buffer ( if (curbuf->b_ffname != NULL) { int old_msg_silent = msg_silent; +#ifdef UNIX + int save_bin = curbuf->b_p_bin; + int perm; + + perm = os_getperm((const char *)curbuf->b_ffname); + if (perm >= 0 && (0 +# ifdef S_ISFIFO + || S_ISFIFO(perm) +# endif +# ifdef S_ISSOCK + || S_ISSOCK(perm) +# endif +# ifdef OPEN_CHR_FILES + || (S_ISCHR(perm) + && is_dev_fd_file(curbuf->b_ffname)) +# endif + ) + ) { + read_fifo = true; + } + if (read_fifo) { + curbuf->b_p_bin = true; + } +#endif 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); + flags | READ_NEW | (read_fifo ? READ_FIFO : 0)); +#ifdef UNIX + if (read_fifo) { + curbuf->b_p_bin = save_bin; + if (retval == OK) { + retval = read_buffer(false, eap, flags); + } + } +#endif msg_silent = old_msg_silent; // Help buffer is filtered. @@ -171,7 +254,6 @@ open_buffer ( } } else if (read_stdin) { int save_bin = curbuf->b_p_bin; - linenr_T line_count; /* * First read the text in binary mode into the buffer. @@ -185,41 +267,13 @@ open_buffer ( flags | (READ_NEW + READ_STDIN)); curbuf->b_p_bin = save_bin; if (retval == OK) { - line_count = curbuf->b_ml.ml_line_count; - retval = readfile(NULL, NULL, (linenr_T)line_count, - (linenr_T)0, (linenr_T)MAXLNUM, eap, - flags | READ_BUFFER); - if (retval == OK) { - /* Delete the binary lines. */ - while (--line_count >= 0) - ml_delete((linenr_T)1, FALSE); - } else { - /* Delete the converted lines. */ - while (curbuf->b_ml.ml_line_count > line_count) - ml_delete(line_count, FALSE); - } - /* Put the cursor on the first line. */ - curwin->w_cursor.lnum = 1; - curwin->w_cursor.col = 0; - - // Set or reset 'modified' before executing autocommands, so that - // it can be changed there. - if (!readonlymode && !bufempty()) { - changed(); - } else if (retval == OK) { - unchanged(curbuf, false); - } - - if (retval == OK) { - apply_autocmds_retval(EVENT_STDINREADPOST, NULL, NULL, false, - curbuf, &retval); - } + retval = read_buffer(true, eap, flags); } } /* if first time loading this buffer, init b_chartab[] */ if (curbuf->b_flags & BF_NEVERLOADED) { - (void)buf_init_chartab(curbuf, FALSE); + (void)buf_init_chartab(curbuf, false); parse_cino(curbuf); } @@ -234,7 +288,7 @@ open_buffer ( || modified_was_set // ":set modified" used in autocmd || (aborting() && vim_strchr(p_cpo, CPO_INTMOD) != NULL)) { changed(); - } else if (retval == OK && !read_stdin) { + } else if (retval != FAIL && !read_stdin && !read_fifo) { unchanged(curbuf, false); } save_file_ff(curbuf); // keep this fileformat @@ -416,8 +470,8 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) return; } - /* When the buffer becomes hidden, but is not unloaded, trigger - * BufHidden */ + // When the buffer becomes hidden, but is not unloaded, trigger + // BufHidden if (!unload_buf) { buf->b_locked++; if (apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname, false, @@ -1148,8 +1202,8 @@ do_buffer ( */ while (buf == curbuf && !(curwin->w_closing || curwin->w_buffer->b_locked > 0) - && (firstwin != lastwin || first_tabpage->tp_next != NULL)) { - if (win_close(curwin, FALSE) == FAIL) + && (!ONE_WINDOW || first_tabpage->tp_next != NULL)) { + if (win_close(curwin, false) == FAIL) break; } @@ -1333,31 +1387,40 @@ void set_curbuf(buf_T *buf, int action) /* Don't restart Select mode after switching to another buffer. */ VIsual_reselect = FALSE; - /* close_windows() or apply_autocmds() may change curbuf */ + // close_windows() or apply_autocmds() may change curbuf and wipe out "buf" prevbuf = curbuf; - bufref_T bufref; - set_bufref(&bufref, prevbuf); + bufref_T newbufref; + bufref_T prevbufref; + set_bufref(&prevbufref, prevbuf); + set_bufref(&newbufref, buf); + // Autocommands may delete the curren buffer and/or the buffer we wan to go + // to. In those cases don't close the buffer. if (!apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, false, curbuf) - || (bufref_valid(&bufref) && !aborting())) { + || (bufref_valid(&prevbufref) && bufref_valid(&newbufref) + && !aborting())) { if (prevbuf == curwin->w_buffer) { reset_synblock(curwin); } if (unload) { close_windows(prevbuf, false); } - if (bufref_valid(&bufref) && !aborting()) { + if (bufref_valid(&prevbufref) && !aborting()) { win_T *previouswin = curwin; - if (prevbuf == curbuf) - u_sync(FALSE); - close_buffer(prevbuf == curwin->w_buffer ? curwin : NULL, prevbuf, - unload ? action : (action == DOBUF_GOTO - && !P_HID(prevbuf) - && !bufIsChanged( - prevbuf)) ? DOBUF_UNLOAD : 0, FALSE); - if (curwin != previouswin && win_valid(previouswin)) - /* autocommands changed curwin, Grr! */ + if (prevbuf == curbuf) { + u_sync(false); + } + close_buffer(prevbuf == curwin->w_buffer ? curwin : NULL, + prevbuf, + unload + ? action + : (action == DOBUF_GOTO && !buf_hide(prevbuf) + && !bufIsChanged(prevbuf)) ? DOBUF_UNLOAD : 0, + false); + if (curwin != previouswin && win_valid(previouswin)) { + // autocommands changed curwin, Grr! curwin = previouswin; + } } } /* An autocommand may have deleted "buf", already entered it (e.g., when @@ -1409,12 +1472,6 @@ void enter_buffer(buf_T *buf) /* mark cursor position as being invalid */ curwin->w_valid = 0; - if (buf->terminal) { - terminal_resize(buf->terminal, - (uint16_t)curwin->w_width, - (uint16_t)curwin->w_height); - } - /* Make sure the buffer is loaded. */ if (curbuf->b_ml.ml_mfp == NULL) { /* need to load the file */ /* If there is no filetype, allow for detecting one. Esp. useful for @@ -1568,7 +1625,7 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags) && curbuf != NULL && curbuf->b_ffname == NULL && curbuf->b_nwindows <= 1 - && (curbuf->b_ml.ml_mfp == NULL || bufempty())) { + && (curbuf->b_ml.ml_mfp == NULL || BUFEMPTY())) { buf = curbuf; /* It's like this buffer is deleted. Watch out for autocommands that * change curbuf! If that happens, allocate a new buffer anyway. */ @@ -1727,6 +1784,7 @@ void free_buf_options(buf_T *buf, int free_p_ff) clear_string_option(&buf->b_p_flp); clear_string_option(&buf->b_p_isk); clear_string_option(&buf->b_p_keymap); + keymap_ga_clear(&buf->b_kmap_ga); ga_clear(&buf->b_kmap_ga); clear_string_option(&buf->b_p_com); clear_string_option(&buf->b_p_cms); @@ -1760,6 +1818,7 @@ void free_buf_options(buf_T *buf, int free_p_ff) buf->b_p_ul = NO_LOCAL_UNDOLEVEL; clear_string_option(&buf->b_p_lw); clear_string_option(&buf->b_p_bkc); + clear_string_option(&buf->b_p_menc); } @@ -1822,7 +1881,7 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit) // If 'switchbuf' contains "split", "vsplit" or "newtab" and the // current buffer isn't empty: open new tab or window if (wp == NULL && (swb_flags & (SWB_VSPLIT | SWB_SPLIT | SWB_NEWTAB)) - && !bufempty()) { + && !BUFEMPTY()) { if (swb_flags & SWB_NEWTAB) { tabpage_new(); } else if (win_split(0, (swb_flags & SWB_VSPLIT) ? WSP_VERT : 0) @@ -2624,7 +2683,7 @@ void buflist_altfpos(win_T *win) } /// Check that "ffname" is not the same file as current file. -/// Fname must have a full path (expanded by path_get_absolute_path()). +/// Fname must have a full path (expanded by path_to_absolute()). /// /// @param ffname full path name to check bool otherfile(char_u *ffname) @@ -2634,7 +2693,7 @@ bool otherfile(char_u *ffname) } /// 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()). +/// Fname must have a full path (expanded by path_to_absolute()). /// /// @param buf buffer to check /// @param ffname full path name to check @@ -3014,8 +3073,8 @@ static bool ti_change(char_u *str, char_u **last) /// Set current window title void resettitle(void) { - ui_call_set_title(cstr_as_string((char *)lasttitle)); ui_call_set_icon(cstr_as_string((char *)lasticon)); + ui_call_set_title(cstr_as_string((char *)lasttitle)); ui_flush(); } @@ -4358,12 +4417,12 @@ do_arg_all ( } wp->w_arg_idx = i; - if (i == opened_len && !keep_tabs) { /* close this window */ - if (P_HID(buf) || forceit || buf->b_nwindows > 1 + if (i == opened_len && !keep_tabs) { // close this window + if (buf_hide(buf) || forceit || buf->b_nwindows > 1 || !bufIsChanged(buf)) { /* If the buffer was changed, and we would like to hide it, * try autowriting. */ - if (!P_HID(buf) && buf->b_nwindows <= 1 && bufIsChanged(buf)) { + if (!buf_hide(buf) && buf->b_nwindows <= 1 && bufIsChanged(buf)) { bufref_T bufref; set_bufref(&bufref, buf); (void)autowrite(buf, false); @@ -4373,15 +4432,17 @@ do_arg_all ( continue; } } - /* don't close last window */ - if (firstwin == lastwin - && (first_tabpage->tp_next == NULL || !had_tab)) - use_firstwin = TRUE; - else { - win_close(wp, !P_HID(buf) && !bufIsChanged(buf)); - /* check if autocommands removed the next window */ - if (!win_valid(wpnext)) - wpnext = firstwin; /* start all over... */ + // don't close last window + if (ONE_WINDOW + && (first_tabpage->tp_next == NULL || !had_tab)) { + use_firstwin = true; + } else { + win_close(wp, !buf_hide(buf) && !bufIsChanged(buf)); + // check if autocommands removed the next window + if (!win_valid(wpnext)) { + // start all over... + wpnext = firstwin; + } } } } @@ -4410,11 +4471,12 @@ do_arg_all ( last_curwin = curwin; last_curtab = curtab; win_enter(lastwin, false); - /* ":drop all" should re-use an empty window to avoid "--remote-tab" - * leaving an empty tab page when executed locally. */ - if (keep_tabs && bufempty() && curbuf->b_nwindows == 1 - && curbuf->b_ffname == NULL && !curbuf->b_changed) - use_firstwin = TRUE; + // ":drop all" should re-use an empty window to avoid "--remote-tab" + // leaving an empty tab page when executed locally. + if (keep_tabs && BUFEMPTY() && curbuf->b_nwindows == 1 + && curbuf->b_ffname == NULL && !curbuf->b_changed) { + use_firstwin = true; + } for (i = 0; i < count && i < opened_len && !got_int; ++i) { if (alist == &global_alist && i == global_alist.al_ga.ga_len - 1) @@ -4453,14 +4515,15 @@ do_arg_all ( new_curwin = curwin; new_curtab = curtab; } - (void)do_ecmd(0, alist_name(&AARGLIST(alist)[i]), NULL, NULL, - ECMD_ONE, - ((P_HID(curwin->w_buffer) - || bufIsChanged(curwin->w_buffer)) ? ECMD_HIDE : 0) - + ECMD_OLDBUF, curwin); - if (use_firstwin) - ++autocmd_no_leave; - use_firstwin = FALSE; + (void)do_ecmd(0, alist_name(&AARGLIST(alist)[i]), NULL, NULL, ECMD_ONE, + ((buf_hide(curwin->w_buffer) + || bufIsChanged(curwin->w_buffer)) + ? ECMD_HIDE : 0) + ECMD_OLDBUF, + curwin); + if (use_firstwin) { + autocmd_no_leave++; + } + use_firstwin = false; } os_breakcheck(); @@ -4538,7 +4601,7 @@ void ex_buffer_all(exarg_T *eap) - tabline_height() : wp->w_width != Columns) || (had_tab > 0 && wp != firstwin)) - && firstwin != lastwin + && !ONE_WINDOW && !(wp->w_closing || wp->w_buffer->b_locked > 0) ) { win_close(wp, FALSE); @@ -4647,14 +4710,14 @@ void ex_buffer_all(exarg_T *eap) * Close superfluous windows. */ for (wp = lastwin; open_wins > count; ) { - r = (P_HID(wp->w_buffer) || !bufIsChanged(wp->w_buffer) - || autowrite(wp->w_buffer, FALSE) == OK); + r = (buf_hide(wp->w_buffer) || !bufIsChanged(wp->w_buffer) + || autowrite(wp->w_buffer, false) == OK); if (!win_valid(wp)) { /* BufWrite Autocommands made the window invalid, start over */ wp = lastwin; } else if (r) { - win_close(wp, !P_HID(wp->w_buffer)); - --open_wins; + win_close(wp, !buf_hide(wp->w_buffer)); + open_wins--; wp = lastwin; } else { wp = wp->w_prev; @@ -5221,6 +5284,44 @@ int bufhl_add_hl(buf_T *buf, return src_id; } +/// Add highlighting to a buffer, bounded by two cursor positions, +/// with an offset. +/// +/// @param buf Buffer to add highlights to +/// @param src_id src_id to use or 0 to use a new src_id group, +/// or -1 for ungrouped highlight. +/// @param hl_id Highlight group id +/// @param pos_start Cursor position to start the hightlighting at +/// @param pos_end Cursor position to end the highlighting at +/// @param offset Move the whole highlighting this many columns to the right +void bufhl_add_hl_pos_offset(buf_T *buf, + int src_id, + int hl_id, + lpos_T pos_start, + lpos_T pos_end, + colnr_T offset) +{ + colnr_T hl_start = 0; + colnr_T hl_end = 0; + + for (linenr_T lnum = pos_start.lnum; lnum <= pos_end.lnum; lnum ++) { + if (pos_start.lnum < lnum && lnum < pos_end.lnum) { + hl_start = offset; + hl_end = MAXCOL; + } else if (lnum == pos_start.lnum && lnum < pos_end.lnum) { + hl_start = pos_start.col + offset + 1; + hl_end = MAXCOL; + } else if (pos_start.lnum < lnum && lnum == pos_end.lnum) { + hl_start = offset; + hl_end = pos_end.col + offset; + } else if (pos_start.lnum == lnum && pos_end.lnum == lnum) { + hl_start = pos_start.col + offset + 1; + hl_end = pos_end.col + offset; + } + (void)bufhl_add_hl(buf, src_id, hl_id, lnum, hl_start, hl_end); + } +} + /// Clear bufhl highlights from a given source group and range of lines. /// /// @param buf The buffer to remove highlights from diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 559dffb945..8de4286216 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -603,6 +603,7 @@ struct file_buffer { char_u *b_p_bt; ///< 'buftype' int b_has_qf_entry; ///< quickfix exists for buffer int b_p_bl; ///< 'buflisted' + long b_p_channel; ///< 'channel' int b_p_cin; ///< 'cindent' char_u *b_p_cino; ///< 'cinoptions' char_u *b_p_cink; ///< 'cinkeys' @@ -636,6 +637,7 @@ struct file_buffer { uint32_t b_p_fex_flags; ///< flags for 'formatexpr' char_u *b_p_kp; ///< 'keywordprg' int b_p_lisp; ///< 'lisp' + char_u *b_p_menc; ///< 'makeencoding' char_u *b_p_mps; ///< 'matchpairs' int b_p_ml; ///< 'modeline' int b_p_ml_nobin; ///< b_p_ml saved for binary mode @@ -716,6 +718,7 @@ struct file_buffer { int b_ind_hash_comment; int b_ind_cpp_namespace; int b_ind_if_for_while; + int b_ind_cpp_extern_c; linenr_T b_no_eol_lnum; /* non-zero lnum when last line of next binary * write should not have an end-of-line */ diff --git a/src/nvim/bufhl_defs.h b/src/nvim/bufhl_defs.h index 24bd6b7f29..14b1afa7d9 100644 --- a/src/nvim/bufhl_defs.h +++ b/src/nvim/bufhl_defs.h @@ -29,6 +29,6 @@ typedef struct { } BufhlLineInfo; #define BUFHL_CMP(a, b) ((int)(((a)->line - (b)->line))) -KBTREE_INIT(bufhl, BufhlLine *, BUFHL_CMP, 10) +KBTREE_INIT(bufhl, BufhlLine *, BUFHL_CMP, 10) // -V512 typedef kbtree_t(bufhl) BufhlInfo; #endif // NVIM_BUFHL_DEFS_H diff --git a/src/nvim/channel.c b/src/nvim/channel.c new file mode 100644 index 0000000000..2e32af2e9a --- /dev/null +++ b/src/nvim/channel.c @@ -0,0 +1,756 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +#include "nvim/api/ui.h" +#include "nvim/channel.h" +#include "nvim/eval.h" +#include "nvim/eval/encode.h" +#include "nvim/event/socket.h" +#include "nvim/msgpack_rpc/channel.h" +#include "nvim/msgpack_rpc/server.h" +#include "nvim/os/shell.h" +#include "nvim/path.h" +#include "nvim/ascii.h" + +static bool did_stdio = false; +PMap(uint64_t) *channels = NULL; + +/// next free id for a job or rpc channel +/// 1 is reserved for stdio channel +/// 2 is reserved for stderr channel +static uint64_t next_chan_id = CHAN_STDERR+1; + + +typedef struct { + Channel *chan; + Callback *callback; + const char *type; + list_T *received; + int status; +} ChannelEvent; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "channel.c.generated.h" +#endif +/// Teardown the module +void channel_teardown(void) +{ + if (!channels) { + return; + } + + Channel *channel; + + map_foreach_value(channels, channel, { + channel_close(channel->id, kChannelPartAll, NULL); + }); +} + +/// Closes a channel +/// +/// @param id The channel id +/// @return true if successful, false otherwise +bool channel_close(uint64_t id, ChannelPart part, const char **error) +{ + Channel *chan; + Process *proc; + + const char *dummy; + if (!error) { + error = &dummy; + } + + if (!(chan = find_channel(id))) { + if (id < next_chan_id) { + // allow double close, even though we can't say what parts was valid. + return true; + } + *error = (const char *)e_invchan; + return false; + } + + bool close_main = false; + if (part == kChannelPartRpc || part == kChannelPartAll) { + close_main = true; + if (chan->is_rpc) { + rpc_close(chan); + } else if (part == kChannelPartRpc) { + *error = (const char *)e_invstream; + return false; + } + } else if ((part == kChannelPartStdin || part == kChannelPartStdout) + && chan->is_rpc) { + *error = (const char *)e_invstreamrpc; + return false; + } + + switch (chan->streamtype) { + case kChannelStreamSocket: + if (!close_main) { + *error = (const char *)e_invstream; + return false; + } + stream_may_close(&chan->stream.socket); + break; + + case kChannelStreamProc: + proc = (Process *)&chan->stream.proc; + if (part == kChannelPartStdin || close_main) { + stream_may_close(&proc->in); + } + if (part == kChannelPartStdout || close_main) { + stream_may_close(&proc->out); + } + if (part == kChannelPartStderr || part == kChannelPartAll) { + stream_may_close(&proc->err); + } + if (proc->type == kProcessTypePty && part == kChannelPartAll) { + pty_process_close_master(&chan->stream.pty); + } + + break; + + case kChannelStreamStdio: + if (part == kChannelPartStdin || close_main) { + stream_may_close(&chan->stream.stdio.in); + } + if (part == kChannelPartStdout || close_main) { + stream_may_close(&chan->stream.stdio.out); + } + if (part == kChannelPartStderr) { + *error = (const char *)e_invstream; + return false; + } + break; + + case kChannelStreamStderr: + if (part != kChannelPartAll && part != kChannelPartStderr) { + *error = (const char *)e_invstream; + return false; + } + if (!chan->stream.err.closed) { + chan->stream.err.closed = true; + // Don't close on exit, in case late error messages + if (!exiting) { + fclose(stderr); + } + channel_decref(chan); + } + break; + + case kChannelStreamInternal: + if (!close_main) { + *error = (const char *)e_invstream; + return false; + } + break; + } + + return true; +} + +/// Initializes the module +void channel_init(void) +{ + channels = pmap_new(uint64_t)(); + channel_alloc(kChannelStreamStderr); + rpc_init(); + remote_ui_init(); +} + +/// Allocates a channel. +/// +/// Channel is allocated with refcount 1, which should be decreased +/// when the underlying stream closes. +static Channel *channel_alloc(ChannelStreamType type) +{ + Channel *chan = xcalloc(1, sizeof(*chan)); + if (type == kChannelStreamStdio) { + chan->id = CHAN_STDIO; + } else if (type == kChannelStreamStderr) { + chan->id = CHAN_STDERR; + } else { + chan->id = next_chan_id++; + } + chan->events = multiqueue_new_child(main_loop.events); + chan->refcount = 1; + chan->streamtype = type; + pmap_put(uint64_t)(channels, chan->id, chan); + return chan; +} + +/// Not implemented, only logging for now +void channel_create_event(Channel *chan, const char *ext_source) +{ +#if MIN_LOG_LEVEL <= INFO_LOG_LEVEL + const char *stream_desc; + const char *mode_desc; + const char *source; + + switch (chan->streamtype) { + case kChannelStreamProc: + if (chan->stream.proc.type == kProcessTypePty) { + stream_desc = "pty job"; + } else { + stream_desc = "job"; + } + break; + + case kChannelStreamStdio: + stream_desc = "stdio"; + break; + + case kChannelStreamSocket: + stream_desc = "socket"; + break; + + case kChannelStreamInternal: + stream_desc = "socket (internal)"; + break; + + default: + stream_desc = "?"; + } + + if (chan->is_rpc) { + mode_desc = ", rpc"; + } else if (chan->term) { + mode_desc = ", terminal"; + } else { + mode_desc = ""; + } + + if (ext_source) { + // TODO(bfredl): in a future improved traceback solution, + // external events should be included. + source = ext_source; + } else { + eval_fmt_source_name_line((char *)IObuff, sizeof(IObuff)); + source = (const char *)IObuff; + } + + ILOG("new channel %" PRIu64 " (%s%s): %s", chan->id, stream_desc, + mode_desc, source); +#else + (void)chan; + (void)ext_source; +#endif +} + +void channel_incref(Channel *channel) +{ + channel->refcount++; +} + +void channel_decref(Channel *channel) +{ + if (!(--channel->refcount)) { + multiqueue_put(main_loop.fast_events, free_channel_event, 1, channel); + } +} + +void callback_reader_free(CallbackReader *reader) +{ + callback_free(&reader->cb); + if (reader->buffered) { + ga_clear(&reader->buffer); + } +} + +void callback_reader_start(CallbackReader *reader) +{ + if (reader->buffered) { + ga_init(&reader->buffer, sizeof(char *), 32); + ga_grow(&reader->buffer, 32); + } +} + +static void free_channel_event(void **argv) +{ + Channel *channel = argv[0]; + if (channel->is_rpc) { + rpc_free(channel); + } + + callback_reader_free(&channel->on_stdout); + callback_reader_free(&channel->on_stderr); + callback_free(&channel->on_exit); + + pmap_del(uint64_t)(channels, channel->id); + multiqueue_free(channel->events); + xfree(channel); +} + +static void channel_destroy_early(Channel *chan) +{ + if ((chan->id != --next_chan_id)) { + abort(); + } + + if ((--chan->refcount != 0)) { + abort(); + } + + free_channel_event((void **)&chan); +} + + +static void close_cb(Stream *stream, void *data) +{ + channel_decref(data); +} + +Channel *channel_job_start(char **argv, CallbackReader on_stdout, + CallbackReader on_stderr, Callback on_exit, + bool pty, bool rpc, bool detach, const char *cwd, + uint16_t pty_width, uint16_t pty_height, + char *term_name, varnumber_T *status_out) +{ + Channel *chan = channel_alloc(kChannelStreamProc); + chan->on_stdout = on_stdout; + chan->on_stderr = on_stderr; + chan->on_exit = on_exit; + chan->is_rpc = rpc; + + if (pty) { + if (detach) { + EMSG2(_(e_invarg2), "terminal/pty job cannot be detached"); + shell_free_argv(argv); + xfree(term_name); + channel_destroy_early(chan); + *status_out = 0; + return NULL; + } + chan->stream.pty = pty_process_init(&main_loop, chan); + if (pty_width > 0) { + chan->stream.pty.width = pty_width; + } + if (pty_height > 0) { + chan->stream.pty.height = pty_height; + } + if (term_name) { + chan->stream.pty.term_name = term_name; + } + } else { + chan->stream.uv = libuv_process_init(&main_loop, chan); + } + + Process *proc = (Process *)&chan->stream.proc; + proc->argv = argv; + proc->cb = channel_process_exit_cb; + proc->events = chan->events; + proc->detach = detach; + proc->cwd = cwd; + + char *cmd = xstrdup(proc->argv[0]); + bool has_out, has_err; + if (proc->type == kProcessTypePty) { + has_out = true; + has_err = false; + } else { + has_out = chan->is_rpc || callback_reader_set(chan->on_stdout); + has_err = callback_reader_set(chan->on_stderr); + } + int status = process_spawn(proc, true, has_out, has_err); + if (status) { + EMSG3(_(e_jobspawn), os_strerror(status), cmd); + xfree(cmd); + if (proc->type == kProcessTypePty) { + xfree(chan->stream.pty.term_name); + } + channel_destroy_early(chan); + *status_out = proc->status; + return NULL; + } + xfree(cmd); + + wstream_init(&proc->in, 0); + if (has_out) { + rstream_init(&proc->out, 0); + } + + if (chan->is_rpc) { + // the rpc takes over the in and out streams + rpc_start(chan); + } else { + if (has_out) { + callback_reader_start(&chan->on_stdout); + rstream_start(&proc->out, on_job_stdout, chan); + } + } + + if (has_err) { + callback_reader_start(&chan->on_stderr); + rstream_init(&proc->err, 0); + rstream_start(&proc->err, on_job_stderr, chan); + } + + *status_out = (varnumber_T)chan->id; + return chan; +} + + +uint64_t channel_connect(bool tcp, const char *address, + bool rpc, CallbackReader on_output, + int timeout, const char **error) +{ + Channel *channel; + + if (!tcp && rpc) { + char *path = fix_fname(address); + bool loopback = server_owns_pipe_address(path); + xfree(path); + if (loopback) { + // Create a loopback channel. This avoids deadlock if nvim connects to + // its own named pipe. + channel = channel_alloc(kChannelStreamInternal); + rpc_start(channel); + goto end; + } + } + + channel = channel_alloc(kChannelStreamSocket); + if (!socket_connect(&main_loop, &channel->stream.socket, + tcp, address, timeout, error)) { + channel_destroy_early(channel); + return 0; + } + + channel->stream.socket.internal_close_cb = close_cb; + channel->stream.socket.internal_data = channel; + wstream_init(&channel->stream.socket, 0); + rstream_init(&channel->stream.socket, 0); + + if (rpc) { + rpc_start(channel); + } else { + channel->on_stdout = on_output; + callback_reader_start(&channel->on_stdout); + rstream_start(&channel->stream.socket, on_socket_output, channel); + } + +end: + channel_create_event(channel, address); + return channel->id; +} + +/// Creates an RPC channel from a tcp/pipe socket connection +/// +/// @param watcher The SocketWatcher ready to accept the connection +void channel_from_connection(SocketWatcher *watcher) +{ + Channel *channel = channel_alloc(kChannelStreamSocket); + socket_watcher_accept(watcher, &channel->stream.socket); + channel->stream.socket.internal_close_cb = close_cb; + channel->stream.socket.internal_data = channel; + wstream_init(&channel->stream.socket, 0); + rstream_init(&channel->stream.socket, 0); + rpc_start(channel); + channel_create_event(channel, watcher->addr); +} + +/// Creates an API channel from stdin/stdout. This is used when embedding +/// Neovim +uint64_t channel_from_stdio(bool rpc, CallbackReader on_output, + const char **error) + FUNC_ATTR_NONNULL_ALL +{ + if (!headless_mode) { + *error = _("can only be opened in headless mode"); + return 0; + } + + if (did_stdio) { + *error = _("channel was already open"); + return 0; + } + did_stdio = true; + + Channel *channel = channel_alloc(kChannelStreamStdio); + + rstream_init_fd(&main_loop, &channel->stream.stdio.in, 0, 0); + wstream_init_fd(&main_loop, &channel->stream.stdio.out, 1, 0); + + if (rpc) { + rpc_start(channel); + } else { + channel->on_stdout = on_output; + callback_reader_start(&channel->on_stdout); + rstream_start(&channel->stream.stdio.in, on_stdio_input, channel); + } + + return channel->id; +} + +/// @param data will be consumed +size_t channel_send(uint64_t id, char *data, size_t len, const char **error) +{ + Channel *chan = find_channel(id); + if (!chan) { + EMSG(_(e_invchan)); + goto err; + } + + if (chan->streamtype == kChannelStreamStderr) { + if (chan->stream.err.closed) { + *error = _("Can't send data to closed stream"); + goto err; + } + // unbuffered write + size_t written = fwrite(data, len, 1, stderr); + xfree(data); + return len * written; + } + + + Stream *in = channel_instream(chan); + if (in->closed) { + *error = _("Can't send data to closed stream"); + goto err; + } + + if (chan->is_rpc) { + *error = _("Can't send raw data to rpc channel"); + goto err; + } + + WBuffer *buf = wstream_new_buffer(data, len, 1, xfree); + return wstream_write(in, buf) ? len : 0; + +err: + xfree(data); + return 0; +} + +/// Convert binary byte array to a readfile()-style list +/// +/// @param[in] buf Array to convert. +/// @param[in] len Array length. +/// +/// @return [allocated] Converted list. +static inline list_T *buffer_to_tv_list(const char *const buf, const size_t len) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE +{ + list_T *const l = tv_list_alloc(kListLenMayKnow); + // Empty buffer should be represented by [''], encode_list_write() thinks + // empty list is fine for the case. + tv_list_append_string(l, "", 0); + encode_list_write(l, buf, len); + return l; +} + +// vimscript job callbacks must be executed on Nvim main loop +static inline void process_channel_event(Channel *chan, Callback *callback, + const char *type, char *buf, + size_t count, int status) +{ + assert(callback); + ChannelEvent *event_data = xmalloc(sizeof(*event_data)); + event_data->received = NULL; + if (buf) { + event_data->received = buffer_to_tv_list(buf, count); + } else { + event_data->status = status; + } + channel_incref(chan); // Hold on ref to callback + event_data->chan = chan; + event_data->callback = callback; + event_data->type = type; + + multiqueue_put(chan->events, on_channel_event, 1, event_data); +} + +void on_job_stdout(Stream *stream, RBuffer *buf, size_t count, + void *data, bool eof) +{ + Channel *chan = data; + on_channel_output(stream, chan, buf, count, eof, &chan->on_stdout, "stdout"); +} + +void on_job_stderr(Stream *stream, RBuffer *buf, size_t count, + void *data, bool eof) +{ + Channel *chan = data; + on_channel_output(stream, chan, buf, count, eof, &chan->on_stderr, "stderr"); +} + +static void on_socket_output(Stream *stream, RBuffer *buf, size_t count, + void *data, bool eof) +{ + Channel *chan = data; + on_channel_output(stream, chan, buf, count, eof, &chan->on_stdout, "data"); +} + +static void on_stdio_input(Stream *stream, RBuffer *buf, size_t count, + void *data, bool eof) +{ + Channel *chan = data; + on_channel_output(stream, chan, buf, count, eof, &chan->on_stdout, "stdin"); +} + +/// @param type must have static lifetime +static void on_channel_output(Stream *stream, Channel *chan, RBuffer *buf, + size_t count, bool eof, CallbackReader *reader, + const char *type) +{ + // stub variable, to keep reading consistent with the order of events, only + // consider the count parameter. + size_t r; + char *ptr = rbuffer_read_ptr(buf, &r); + + if (eof) { + if (reader->buffered) { + if (reader->cb.type != kCallbackNone) { + process_channel_event(chan, &reader->cb, type, reader->buffer.ga_data, + (size_t)reader->buffer.ga_len, 0); + } else if (reader->self) { + if (tv_dict_find(reader->self, type, -1) == NULL) { + list_T *data = buffer_to_tv_list(reader->buffer.ga_data, + (size_t)reader->buffer.ga_len); + tv_dict_add_list(reader->self, type, strlen(type), data); + } else { + // can't display error message now, defer it. + channel_incref(chan); + multiqueue_put(chan->events, on_buffered_error, 2, chan, type); + } + } else { + abort(); + } + ga_clear(&reader->buffer); + } else if (reader->cb.type != kCallbackNone) { + process_channel_event(chan, &reader->cb, type, ptr, 0, 0); + } + return; + } + + // The order here matters, the terminal must receive the data first because + // process_channel_event will modify the read buffer(convert NULs into NLs) + if (chan->term) { + terminal_receive(chan->term, ptr, count); + } + + rbuffer_consumed(buf, count); + if (reader->buffered) { + ga_concat_len(&reader->buffer, ptr, count); + } else if (callback_reader_set(*reader)) { + process_channel_event(chan, &reader->cb, type, ptr, count, 0); + } +} + +static void on_buffered_error(void **args) +{ + Channel *chan = (Channel *)args[0]; + const char *stream = (const char *)args[1]; + EMSG3(_(e_streamkey), stream, chan->id); + channel_decref(chan); +} + +static void channel_process_exit_cb(Process *proc, int status, void *data) +{ + Channel *chan = data; + if (chan->term) { + char msg[sizeof("\r\n[Process exited ]") + NUMBUFLEN]; + snprintf(msg, sizeof msg, "\r\n[Process exited %d]", proc->status); + terminal_close(chan->term, msg); + } + + // if status is -1 the process did not really exit, + // we just closed the handle onto a detached process + if (status >= 0) { + process_channel_event(chan, &chan->on_exit, "exit", NULL, 0, status); + } + + channel_decref(chan); +} + +static void on_channel_event(void **args) +{ + ChannelEvent *ev = (ChannelEvent *)args[0]; + + typval_T argv[4]; + + argv[0].v_type = VAR_NUMBER; + argv[0].v_lock = VAR_UNLOCKED; + argv[0].vval.v_number = (varnumber_T)ev->chan->id; + + if (ev->received) { + argv[1].v_type = VAR_LIST; + argv[1].v_lock = VAR_UNLOCKED; + argv[1].vval.v_list = ev->received; + tv_list_ref(argv[1].vval.v_list); + } else { + argv[1].v_type = VAR_NUMBER; + argv[1].v_lock = VAR_UNLOCKED; + argv[1].vval.v_number = ev->status; + } + + argv[2].v_type = VAR_STRING; + argv[2].v_lock = VAR_UNLOCKED; + argv[2].vval.v_string = (uint8_t *)ev->type; + + typval_T rettv = TV_INITIAL_VALUE; + callback_call(ev->callback, 3, argv, &rettv); + tv_clear(&rettv); + channel_decref(ev->chan); + xfree(ev); +} + + +/// Open terminal for channel +/// +/// Channel `chan` is assumed to be an open pty channel, +/// and curbuf is assumed to be a new, unmodified buffer. +void channel_terminal_open(Channel *chan) +{ + TerminalOptions topts; + topts.data = chan; + topts.width = chan->stream.pty.width; + topts.height = chan->stream.pty.height; + topts.write_cb = term_write; + topts.resize_cb = term_resize; + topts.close_cb = term_close; + curbuf->b_p_channel = (long)chan->id; // 'channel' option + Terminal *term = terminal_open(topts); + chan->term = term; + channel_incref(chan); +} + +static void term_write(char *buf, size_t size, void *data) +{ + Channel *chan = data; + if (chan->stream.proc.in.closed) { + // If the backing stream was closed abruptly, there may be write events + // ahead of the terminal close event. Just ignore the writes. + ILOG("write failed: stream is closed"); + return; + } + WBuffer *wbuf = wstream_new_buffer(xmemdup(buf, size), size, 1, xfree); + wstream_write(&chan->stream.proc.in, wbuf); +} + +static void term_resize(uint16_t width, uint16_t height, void *data) +{ + Channel *chan = data; + pty_process_resize(&chan->stream.pty, width, height); +} + +static inline void term_delayed_free(void **argv) +{ + Channel *chan = argv[0]; + if (chan->stream.proc.in.pending_reqs || chan->stream.proc.out.pending_reqs) { + multiqueue_put(chan->events, term_delayed_free, 1, chan); + return; + } + + terminal_destroy(chan->term); + chan->term = NULL; + channel_decref(chan); +} + +static void term_close(void *data) +{ + Channel *chan = data; + process_stop(&chan->stream.proc); + multiqueue_put(chan->events, term_delayed_free, 1, data); +} + diff --git a/src/nvim/channel.h b/src/nvim/channel.h new file mode 100644 index 0000000000..b856d197f1 --- /dev/null +++ b/src/nvim/channel.h @@ -0,0 +1,134 @@ +#ifndef NVIM_CHANNEL_H +#define NVIM_CHANNEL_H + +#include "nvim/main.h" +#include "nvim/event/socket.h" +#include "nvim/event/process.h" +#include "nvim/os/pty_process.h" +#include "nvim/event/libuv_process.h" +#include "nvim/eval/typval.h" +#include "nvim/msgpack_rpc/channel_defs.h" + +#define CHAN_STDIO 1 +#define CHAN_STDERR 2 + +typedef enum { + kChannelStreamProc, + kChannelStreamSocket, + kChannelStreamStdio, + kChannelStreamStderr, + kChannelStreamInternal +} ChannelStreamType; + +typedef enum { + kChannelPartStdin, + kChannelPartStdout, + kChannelPartStderr, + kChannelPartRpc, + kChannelPartAll +} ChannelPart; + + +typedef struct { + Stream in; + Stream out; +} StdioPair; + +typedef struct { + bool closed; +} StderrState; + +typedef struct { + Callback cb; + dict_T *self; + garray_T buffer; + bool buffered; +} CallbackReader; + +#define CALLBACK_READER_INIT ((CallbackReader){ .cb = CALLBACK_NONE, \ + .self = NULL, \ + .buffer = GA_EMPTY_INIT_VALUE, \ + .buffered = false }) +static inline bool callback_reader_set(CallbackReader reader) +{ + return reader.cb.type != kCallbackNone || reader.self; +} + +struct Channel { + uint64_t id; + size_t refcount; + MultiQueue *events; + + ChannelStreamType streamtype; + union { + Process proc; + LibuvProcess uv; + PtyProcess pty; + Stream socket; + StdioPair stdio; + StderrState err; + } stream; + + bool is_rpc; + RpcState rpc; + Terminal *term; + + CallbackReader on_stdout; + CallbackReader on_stderr; + Callback on_exit; +}; + +EXTERN PMap(uint64_t) *channels; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "channel.h.generated.h" +#endif + +/// @returns Channel with the id or NULL if not found +static inline Channel *find_channel(uint64_t id) +{ + return pmap_get(uint64_t)(channels, id); +} + +static inline Stream *channel_instream(Channel *chan) + FUNC_ATTR_NONNULL_ALL +{ + switch (chan->streamtype) { + case kChannelStreamProc: + return &chan->stream.proc.in; + + case kChannelStreamSocket: + return &chan->stream.socket; + + case kChannelStreamStdio: + return &chan->stream.stdio.out; + + case kChannelStreamInternal: + case kChannelStreamStderr: + abort(); + } + abort(); +} + +static inline Stream *channel_outstream(Channel *chan) + FUNC_ATTR_NONNULL_ALL +{ + switch (chan->streamtype) { + case kChannelStreamProc: + return &chan->stream.proc.out; + + case kChannelStreamSocket: + return &chan->stream.socket; + + case kChannelStreamStdio: + return &chan->stream.stdio.in; + + case kChannelStreamInternal: + case kChannelStreamStderr: + abort(); + } + abort(); +} + + +#endif // NVIM_CHANNEL_H diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 48db1030a6..980b4ed426 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -762,7 +762,7 @@ bool vim_isIDc(int c) } /// Check that "c" is a keyword character: -/// Letters and characters from 'iskeyword' option for current buffer. +/// Letters and characters from 'iskeyword' option for the current buffer. /// For multi-byte characters mb_get_class() is used (builtin rules). /// /// @param c character to check @@ -981,10 +981,8 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he mb_ptr_adv(s); c = *s; - if (!((c != NUL) - && (vim_isbreak(c) - || (!vim_isbreak(c) - && ((col2 == col) || !vim_isbreak(*ps)))))) { + if (!(c != NUL + && (vim_isbreak(c) || col2 == col || !vim_isbreak(*ps)))) { break; } @@ -1613,141 +1611,145 @@ bool vim_isblankline(char_u *lbuf) /// If maxlen > 0, check at a maximum maxlen chars. /// /// @param start -/// @param prep Returns type of number 0 = decimal, 'x' or 'X' is hex, -/// '0' = octal, 'b' or 'B' is bin +/// @param prep Returns guessed type of number 0 = decimal, 'x' or 'X' is +/// hexadecimal, '0' = octal, 'b' or 'B' is binary. When using +/// STR2NR_FORCE is always zero. /// @param len Returns the detected length of number. -/// @param what Recognizes what number passed. +/// @param what Recognizes what number passed, @see ChStr2NrFlags. /// @param nptr Returns the signed result. /// @param unptr Returns the unsigned result. /// @param maxlen Max length of string to check. void vim_str2nr(const char_u *const start, int *const prep, int *const len, const int what, varnumber_T *const nptr, uvarnumber_T *const unptr, const int maxlen) + FUNC_ATTR_NONNULL_ARG(1) { - const char_u *ptr = start; + const char *ptr = (const char *)start; +#define STRING_ENDED(ptr) \ + (!(maxlen == 0 || (int)((ptr) - (const char *)start) < maxlen)) int pre = 0; // default is decimal - bool negative = false; + const bool negative = (ptr[0] == '-'); uvarnumber_T un = 0; - if (ptr[0] == '-') { - negative = true; + if (negative) { ptr++; } - // Recognize hex, octal and bin. - if ((ptr[0] == '0') && (ptr[1] != '8') && (ptr[1] != '9') - && (maxlen == 0 || maxlen > 1)) { + if (what & STR2NR_FORCE) { + // When forcing main consideration is skipping the prefix. Octal and decimal + // numbers have no prefixes to skip. pre is not set. + switch ((unsigned)what & (~(unsigned)STR2NR_FORCE)) { + case STR2NR_HEX: { + if (!STRING_ENDED(ptr + 2) + && ptr[0] == '0' + && (ptr[1] == 'x' || ptr[1] == 'X') + && ascii_isxdigit(ptr[2])) { + ptr += 2; + } + goto vim_str2nr_hex; + } + case STR2NR_BIN: { + if (!STRING_ENDED(ptr + 2) + && ptr[0] == '0' + && (ptr[1] == 'b' || ptr[1] == 'B') + && ascii_isbdigit(ptr[2])) { + ptr += 2; + } + goto vim_str2nr_bin; + } + case STR2NR_OCT: { + goto vim_str2nr_oct; + } + case 0: { + goto vim_str2nr_dec; + } + default: { + assert(false); + } + } + } else if ((what & (STR2NR_HEX|STR2NR_OCT|STR2NR_BIN)) + && !STRING_ENDED(ptr + 1) + && ptr[0] == '0' && ptr[1] != '8' && ptr[1] != '9') { pre = ptr[1]; - + // Detect hexadecimal: 0x or 0X follwed by hex digit if ((what & STR2NR_HEX) - && ((pre == 'X') || (pre == 'x')) - && ascii_isxdigit(ptr[2]) - && (maxlen == 0 || maxlen > 2)) { - // hexadecimal + && !STRING_ENDED(ptr + 2) + && (pre == 'X' || pre == 'x') + && ascii_isxdigit(ptr[2])) { ptr += 2; - } else if ((what & STR2NR_BIN) - && ((pre == 'B') || (pre == 'b')) - && ascii_isbdigit(ptr[2]) - && (maxlen == 0 || maxlen > 2)) { - // binary + goto vim_str2nr_hex; + } + // Detect binary: 0b or 0B follwed by 0 or 1 + if ((what & STR2NR_BIN) + && !STRING_ENDED(ptr + 2) + && (pre == 'B' || pre == 'b') + && ascii_isbdigit(ptr[2])) { ptr += 2; - } else { - // decimal or octal, default is decimal - pre = 0; - - if (what & STR2NR_OCT) { - // Don't interpret "0", "08" or "0129" as octal. - for (int n = 1; ascii_isdigit(ptr[n]); ++n) { - if (ptr[n] > '7') { - // can't be octal - pre = 0; - break; - } - if (ptr[n] >= '0') { - // assume octal - pre = '0'; - } - if (n == maxlen) { - break; - } - } + goto vim_str2nr_bin; + } + // Detect octal number: zero followed by octal digits without '8' or '9' + pre = 0; + if (!(what & STR2NR_OCT) + || !('0' <= ptr[1] && ptr[1] <= '7')) { + goto vim_str2nr_dec; + } + for (int i = 2; !STRING_ENDED(ptr + i) && ascii_isdigit(ptr[i]); i++) { + if (ptr[i] > '7') { + goto vim_str2nr_dec; } } + pre = '0'; + goto vim_str2nr_oct; + } else { + goto vim_str2nr_dec; } // Do the string-to-numeric conversion "manually" to avoid sscanf quirks. - int n = 1; - if ((pre == 'B') || (pre == 'b') || what == STR2NR_BIN + STR2NR_FORCE) { - // bin - if (pre != 0) { - n += 2; // skip over "0b" + assert(false); // Should’ve used goto earlier. +#define PARSE_NUMBER(base, cond, conv) \ + do { \ + while (!STRING_ENDED(ptr) && (cond)) { \ + /* avoid ubsan error for overflow */ \ + if (un < UVARNUMBER_MAX / base) { \ + un = base * un + (uvarnumber_T)(conv); \ + } else { \ + un = UVARNUMBER_MAX; \ + } \ + ptr++; \ + } \ + } while (0) + switch (pre) { + case 'b': + case 'B': { +vim_str2nr_bin: + PARSE_NUMBER(2, (*ptr == '0' || *ptr == '1'), (*ptr - '0')); + break; } - while ('0' <= *ptr && *ptr <= '1') { - // avoid ubsan error for overflow - if (un < UVARNUMBER_MAX / 2) { - un = 2 * un + (uvarnumber_T)(*ptr - '0'); - } else { - un = UVARNUMBER_MAX; - } - ptr++; - if (n++ == maxlen) { - break; - } - } - } else if ((pre == '0') || what == STR2NR_OCT + STR2NR_FORCE) { - // octal - while ('0' <= *ptr && *ptr <= '7') { - // avoid ubsan error for overflow - if (un < UVARNUMBER_MAX / 8) { - un = 8 * un + (uvarnumber_T)(*ptr - '0'); - } else { - un = UVARNUMBER_MAX; - } - ptr++; - if (n++ == maxlen) { - break; - } + case '0': { +vim_str2nr_oct: + PARSE_NUMBER(8, ('0' <= *ptr && *ptr <= '7'), (*ptr - '0')); + break; } - } else if ((pre == 'X') || (pre == 'x') - || what == STR2NR_HEX + STR2NR_FORCE) { - // hex - if (pre != 0) { - n += 2; // skip over "0x" - } - while (ascii_isxdigit(*ptr)) { - // avoid ubsan error for overflow - if (un < UVARNUMBER_MAX / 16) { - un = 16 * un + (uvarnumber_T)hex2nr(*ptr); - } else { - un = UVARNUMBER_MAX; - } - ptr++; - if (n++ == maxlen) { - break; - } + case 0: { +vim_str2nr_dec: + PARSE_NUMBER(10, (ascii_isdigit(*ptr)), (*ptr - '0')); + break; } - } else { - // decimal - while (ascii_isdigit(*ptr)) { - // avoid ubsan error for overflow - if (un < UVARNUMBER_MAX / 10) { - un = 10 * un + (uvarnumber_T)(*ptr - '0'); - } else { - un = UVARNUMBER_MAX; - } - ptr++; - if (n++ == maxlen) { - break; - } + case 'x': + case 'X': { +vim_str2nr_hex: + PARSE_NUMBER(16, (ascii_isxdigit(*ptr)), (hex2nr(*ptr))); + break; } } +#undef PARSE_NUMBER if (prep != NULL) { *prep = pre; } if (len != NULL) { - *len = (int)(ptr - start); + *len = (int)(ptr - (const char *)start); } if (nptr != NULL) { @@ -1769,6 +1771,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, if (unptr != NULL) { *unptr = un; } +#undef STRING_ENDED } /// Return the value of a single hex character. diff --git a/src/nvim/charset.h b/src/nvim/charset.h index c69582c4c6..eb64b6128a 100644 --- a/src/nvim/charset.h +++ b/src/nvim/charset.h @@ -4,6 +4,7 @@ #include "nvim/types.h" #include "nvim/pos.h" #include "nvim/buffer_defs.h" +#include "nvim/eval/typval.h" /// Return the folded-case equivalent of the given character /// @@ -15,6 +16,21 @@ ?((int)(uint8_t)(c)) \ :((int)(c))) +/// Flags for vim_str2nr() +typedef enum { + STR2NR_DEC = 0, + STR2NR_BIN = (1 << 0), ///< Allow binary numbers. + STR2NR_OCT = (1 << 1), ///< Allow octal numbers. + STR2NR_HEX = (1 << 2), ///< Allow hexadecimal numbers. + /// Force one of the above variants. + /// + /// STR2NR_FORCE|STR2NR_DEC is actually not different from supplying zero + /// as flags, but still present for completeness. + STR2NR_FORCE = (1 << 3), + /// Recognize all formats vim_str2nr() can recognize. + STR2NR_ALL = STR2NR_BIN | STR2NR_OCT | STR2NR_HEX, +} ChStr2NrFlags; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "charset.h.generated.h" #endif diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index 60002f3cea..0e97e2203f 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -375,17 +375,30 @@ void check_cursor_col_win(win_T *win) win->w_cursor.col = 0; } - /* If virtual editing is on, we can leave the cursor on the old position, - * only we must set it to virtual. But don't do it when at the end of the - * line. */ - if (oldcol == MAXCOL) + // If virtual editing is on, we can leave the cursor on the old position, + // only we must set it to virtual. But don't do it when at the end of the + // line. + if (oldcol == MAXCOL) { win->w_cursor.coladd = 0; - else if (ve_flags == VE_ALL) { - if (oldcoladd > win->w_cursor.col) + } else if (ve_flags == VE_ALL) { + if (oldcoladd > win->w_cursor.col) { win->w_cursor.coladd = oldcoladd - win->w_cursor.col; - else - /* avoid weird number when there is a miscalculation or overflow */ + + // Make sure that coladd is not more than the char width. + // Not for the last character, coladd is then used when the cursor + // is actually after the last character. + if (win->w_cursor.col + 1 < len && win->w_cursor.coladd > 0) { + int cs, ce; + + getvcol(win, &win->w_cursor, &cs, NULL, &ce); + if (win->w_cursor.coladd > ce - cs) { + win->w_cursor.coladd = ce - cs; + } + } + } else { + // avoid weird number when there is a miscalculation or overflow win->w_cursor.coladd = 0; + } } } diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c index 97fc3a3ca3..b45e7002f7 100644 --- a/src/nvim/cursor_shape.c +++ b/src/nvim/cursor_shape.c @@ -102,11 +102,14 @@ char_u *parse_shape_opt(int what) } while (*modep != NUL) { colonp = vim_strchr(modep, ':'); - if (colonp == NULL) + commap = vim_strchr(modep, ','); + + if (colonp == NULL || (commap != NULL && commap < colonp)) { return (char_u *)N_("E545: Missing colon"); - if (colonp == modep) + } + if (colonp == modep) { return (char_u *)N_("E546: Illegal mode"); - commap = vim_strchr(modep, ','); + } // Repeat for all modes before the colon. // For the 'a' mode, we loop to handle all the modes. diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 7da6665cb7..0ee1c3815d 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -135,6 +135,20 @@ void diff_buf_add(buf_T *buf) EMSGN(_("E96: Cannot diff more than %" PRId64 " buffers"), DB_COUNT); } +/// +/// Remove all buffers to make diffs for. +/// +static void diff_buf_clear(void) +{ + for (int i = 0; i < DB_COUNT; i++) { + if (curtab->tp_diffbuf[i] != NULL) { + curtab->tp_diffbuf[i] = NULL; + curtab->tp_diff_invalid = true; + diff_redraw(true); + } + } +} + /// Find buffer "buf" in the list of diff buffers for the current tab page. /// /// @param buf The buffer to find. @@ -841,12 +855,13 @@ void ex_diffpatch(exarg_T *eap) { char_u *buf = NULL; win_T *old_curwin = curwin; - char_u *newname = NULL; // name of patched file buffer + char_u *newname = NULL; // name of patched file buffer + char_u *esc_name = NULL; #ifdef UNIX char_u dirbuf[MAXPATHL]; char_u *fullname = NULL; -#endif // ifdef UNIX +#endif // We need two temp file names. // Name of original temp file. char_u *tmp_orig = vim_tempname(); @@ -866,21 +881,21 @@ void ex_diffpatch(exarg_T *eap) #ifdef UNIX // Get the absolute path of the patchfile, changing directory below. - fullname = (char_u *)FullName_save((char *)eap->arg, FALSE); -#endif // ifdef UNIX + fullname = (char_u *)FullName_save((char *)eap->arg, false); +#endif + esc_name = vim_strsave_shellescape( #ifdef UNIX - size_t buflen = STRLEN(tmp_orig) - + (fullname != NULL ? STRLEN(fullname) : STRLEN(eap->arg)) - + STRLEN(tmp_new) + 16; -#else - size_t buflen = STRLEN(tmp_orig) + (STRLEN(eap->arg)) + STRLEN(tmp_new) + 16; -#endif // ifdef UNIX - + fullname != NULL ? fullname : +#endif + eap->arg, true, true); + if (esc_name == NULL) { + goto theend; + } + size_t buflen = STRLEN(tmp_orig) + STRLEN(esc_name) + STRLEN(tmp_new) + 16; buf = xmalloc(buflen); #ifdef UNIX - // Temporarily chdir to /tmp, to avoid patching files in the current // directory when the patch file contains more than one patch. When we // have our own temp dir use that instead, it will be cleaned up when we @@ -897,26 +912,21 @@ void ex_diffpatch(exarg_T *eap) os_chdir(tempdir); shorten_fnames(TRUE); } -#endif // ifdef UNIX +#endif if (*p_pex != NUL) { // Use 'patchexpr' to generate the new file. #ifdef UNIX - eval_patch((char *) tmp_orig, - (char *) (fullname != NULL ? fullname : eap->arg), - (char *) tmp_new); + eval_patch((char *)tmp_orig, + (char *)(fullname != NULL ? fullname : eap->arg), + (char *)tmp_new); #else - eval_patch((char *) tmp_orig, (char *) eap->arg, (char *) tmp_new); -#endif // ifdef UNIX + eval_patch((char *)tmp_orig, (char *)eap->arg, (char *)tmp_new); +#endif } else { // Build the patch command and execute it. Ignore errors. -#ifdef UNIX - vim_snprintf((char *)buf, buflen, "patch -o %s %s < \"%s\"", - tmp_new, tmp_orig, fullname != NULL ? fullname : eap->arg); -#else - vim_snprintf((char *)buf, buflen, "patch -o %s %s < \"%s\"", - tmp_new, tmp_orig, eap->arg); -#endif // ifdef UNIX + vim_snprintf((char *)buf, buflen, "patch -o %s %s < %s", + tmp_new, tmp_orig, esc_name); block_autocmds(); // Avoid ShellCmdPost stuff (void)call_shell(buf, kShellOptFilter, NULL); unblock_autocmds(); @@ -929,10 +939,7 @@ void ex_diffpatch(exarg_T *eap) } shorten_fnames(TRUE); } -#endif // ifdef UNIX - - // patch probably has written over the screen - redraw_later(CLEAR); +#endif // Delete any .orig or .rej file created. STRCPY(buf, tmp_new); @@ -998,7 +1005,8 @@ theend: xfree(buf); #ifdef UNIX xfree(fullname); -#endif // ifdef UNIX +#endif + xfree(esc_name); } /// Split the window and edit another file, setting options to show the diffs. @@ -1033,10 +1041,7 @@ void ex_diffsplit(exarg_T *eap) if (bufref_valid(&old_curbuf)) { // Move the cursor position to that of the old window. curwin->w_cursor.lnum = diff_get_corresponding_line( - old_curbuf.br_buf, - old_curwin->w_cursor.lnum, - curbuf, - curwin->w_cursor.lnum); + old_curbuf.br_buf, old_curwin->w_cursor.lnum); } } // Now that lines are folded scroll to show the cursor at the same @@ -1053,6 +1058,20 @@ void ex_diffthis(exarg_T *eap) diff_win_options(curwin, TRUE); } +static void set_diff_option(win_T *wp, int value) +{ + win_T *old_curwin = curwin; + + curwin = wp; + curbuf = curwin->w_buffer; + curbuf_lock++; + set_option_value("diff", (long)value, NULL, OPT_LOCAL); + curbuf_lock--; + curwin = old_curwin; + curbuf = curwin->w_buffer; +} + + /// Set options in window "wp" for diff mode. /// /// @param addbuf Add buffer to diff. @@ -1110,10 +1129,10 @@ void diff_win_options(win_T *wp, int addbuf) do_cmdline_cmd("set sbo+=hor"); } - // Saved the current values, to be restored in ex_diffoff(). - wp->w_p_diff_saved = TRUE; + // Save the current values, to be restored in ex_diffoff(). + wp->w_p_diff_saved = true; - wp->w_p_diff = true; + set_diff_option(wp, true); if (addbuf) { diff_buf_add(wp->w_buffer); @@ -1134,7 +1153,7 @@ void ex_diffoff(exarg_T *eap) // Set 'diff' off. If option values were saved in // diff_win_options(), restore the ones whose settings seem to have // been left over from diff mode. - wp->w_p_diff = false; + set_diff_option(wp, false); if (wp->w_p_diff_saved) { if (wp->w_p_scb) { @@ -1150,7 +1169,9 @@ void ex_diffoff(exarg_T *eap) } free_string_option(wp->w_p_fdm); - wp->w_p_fdm = vim_strsave(wp->w_p_fdm_save); + wp->w_p_fdm = vim_strsave(*wp->w_p_fdm_save + ? wp->w_p_fdm_save + : (char_u *)"manual"); if (wp->w_p_fdc == diff_foldcolumn) { wp->w_p_fdc = wp->w_p_fdc_save; } @@ -1178,6 +1199,11 @@ void ex_diffoff(exarg_T *eap) diffwin |= wp->w_p_diff; } + // Also remove hidden buffers from the list. + if (eap->forceit) { + diff_buf_clear(); + } + // Remove "hor" from from 'scrollopt' if there are no diff windows left. if (!diffwin && (vim_strchr(p_sbo, 'h') != NULL)) { do_cmdline_cmd("set sbo-=hor"); @@ -2255,7 +2281,7 @@ void ex_diffgetput(exarg_T *eap) } } - buf_empty = bufempty(); + buf_empty = BUFEMPTY(); added = 0; for (i = 0; i < count; ++i) { @@ -2463,25 +2489,17 @@ int diff_move_to(int dir, long count) return OK; } -/// Finds the corresponding line in a diff. -/// -/// @param buf1 -/// @param lnum1 -/// @param buf2 -/// @param lnum3 -/// -/// @return The corresponding line. -linenr_T diff_get_corresponding_line(buf_T *buf1, linenr_T lnum1, buf_T *buf2, - linenr_T lnum3) +/// Return the line number in the current window that is closest to "lnum1" in +/// "buf1" in diff mode. +static linenr_T diff_get_corresponding_line_int(buf_T *buf1, linenr_T lnum1) { int idx1; int idx2; diff_T *dp; int baseline = 0; - linenr_T lnum2; idx1 = diff_buf_idx(buf1); - idx2 = diff_buf_idx(buf2); + idx2 = diff_buf_idx(curbuf); if ((idx1 == DB_COUNT) || (idx2 == DB_COUNT) @@ -2501,15 +2519,9 @@ linenr_T diff_get_corresponding_line(buf_T *buf1, linenr_T lnum1, buf_T *buf2, for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) { if (dp->df_lnum[idx1] > lnum1) { - lnum2 = lnum1 - baseline; - - // don't end up past the end of the file - if (lnum2 > buf2->b_ml.ml_line_count) { - lnum2 = buf2->b_ml.ml_line_count; - } - - return lnum2; - } else if ((dp->df_lnum[idx1] + dp->df_count[idx1]) > lnum1) { + return lnum1 - baseline; + } + if ((dp->df_lnum[idx1] + dp->df_count[idx1]) > lnum1) { // Inside the diffblock baseline = lnum1 - dp->df_lnum[idx1]; @@ -2518,30 +2530,42 @@ linenr_T diff_get_corresponding_line(buf_T *buf1, linenr_T lnum1, buf_T *buf2, } return dp->df_lnum[idx2] + baseline; - } else if ((dp->df_lnum[idx1] == lnum1) - && (dp->df_count[idx1] == 0) - && (dp->df_lnum[idx2] <= lnum3) - && ((dp->df_lnum[idx2] + dp->df_count[idx2]) > lnum3)) { + } + if ((dp->df_lnum[idx1] == lnum1) + && (dp->df_count[idx1] == 0) + && (dp->df_lnum[idx2] <= curwin->w_cursor.lnum) + && ((dp->df_lnum[idx2] + dp->df_count[idx2]) + > curwin->w_cursor.lnum)) { // Special case: if the cursor is just after a zero-count // block (i.e. all filler) and the target cursor is already // inside the corresponding block, leave the target cursor // unmoved. This makes repeated CTRL-W W operations work // as expected. - return lnum3; + return curwin->w_cursor.lnum; } baseline = (dp->df_lnum[idx1] + dp->df_count[idx1]) - - (dp->df_lnum[idx2] + dp->df_count[idx2]); + - (dp->df_lnum[idx2] + dp->df_count[idx2]); } // If we get here then the cursor is after the last diff - lnum2 = lnum1 - baseline; + return lnum1 - baseline; +} + +/// Finds the corresponding line in a diff. +/// +/// @param buf1 +/// @param lnum1 +/// +/// @return The corresponding line. +linenr_T diff_get_corresponding_line(buf_T *buf1, linenr_T lnum1) +{ + linenr_T lnum = diff_get_corresponding_line_int(buf1, lnum1); // don't end up past the end of the file - if (lnum2 > buf2->b_ml.ml_line_count) { - lnum2 = buf2->b_ml.ml_line_count; + if (lnum > curbuf->b_ml.ml_line_count) { + return curbuf->b_ml.ml_line_count; } - - return lnum2; + return lnum; } /// For line "lnum" in the current window find the equivalent lnum in window diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c index 32e71f61f7..bc4c12e0b7 100644 --- a/src/nvim/digraph.c +++ b/src/nvim/digraph.c @@ -793,6 +793,7 @@ static digr_T digraphdefault[] = { '/', '-', 0x2020 }, { '/', '=', 0x2021 }, { '.', '.', 0x2025 }, + { ',', '.', 0x2026 }, { '%', '0', 0x2030 }, { '1', '\'', 0x2032 }, { '2', '\'', 0x2033 }, @@ -1695,7 +1696,7 @@ static void printdigraph(digr_T *dp) } } - p = buf; + p = &buf[0]; *p++ = dp->char1; *p++ = dp->char2; *p++ = ' '; @@ -1840,6 +1841,16 @@ void ex_loadkeymap(exarg_T *eap) status_redraw_curbuf(); } +/// Frees the buf_T.b_kmap_ga field of a buffer. +void keymap_ga_clear(garray_T *kmap_ga) +{ + kmap_T *kp = (kmap_T *)kmap_ga->ga_data; + for (int i = 0; i < kmap_ga->ga_len; i++) { + xfree(kp[i].from); + xfree(kp[i].to); + } +} + /// Stop using 'keymap'. static void keymap_unload(void) { @@ -1857,12 +1868,11 @@ static void keymap_unload(void) // clear the ":lmap"s kp = (kmap_T *)curbuf->b_kmap_ga.ga_data; - for (int i = 0; i < curbuf->b_kmap_ga.ga_len; ++i) { + for (int i = 0; i < curbuf->b_kmap_ga.ga_len; i++) { vim_snprintf((char *)buf, sizeof(buf), "<buffer> %s", kp[i].from); - (void)do_map(1, buf, LANGMAP, FALSE); - xfree(kp[i].from); - xfree(kp[i].to); + (void)do_map(1, buf, LANGMAP, false); } + keymap_ga_clear(&curbuf->b_kmap_ga); p_cpo = save_cpo; diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 08a2f42f74..b772a944f4 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -462,7 +462,7 @@ static void insert_enter(InsertState *s) // Always update o_lnum, so that a "CTRL-O ." that adds a line // still puts the cursor back after the inserted text. - if (ins_at_eol && gchar_cursor() == NUL) { + if (ins_at_eol) { o_lnum = curwin->w_cursor.lnum; } @@ -974,12 +974,8 @@ static int insert_handle_key(InsertState *s) multiqueue_process_events(main_loop.events); break; - case K_FOCUSGAINED: // Neovim has been given focus - apply_autocmds(EVENT_FOCUSGAINED, NULL, NULL, false, curbuf); - break; - - case K_FOCUSLOST: // Neovim has lost focus - apply_autocmds(EVENT_FOCUSLOST, NULL, NULL, false, curbuf); + case K_COMMAND: // some command + do_cmdline(NULL, getcmdkeycmd, NULL, 0); break; case K_HOME: // <Home> @@ -1935,7 +1931,7 @@ bool vim_is_ctrl_x_key(int c) case CTRL_X_EVAL: return (c == Ctrl_P || c == Ctrl_N); } - EMSG(_(e_internal)); + internal_error("vim_is_ctrl_x_key()"); return false; } @@ -2406,6 +2402,7 @@ void set_completion(colnr_T startcol, list_T *list) ins_compl_prep(' '); } ins_compl_clear(); + ins_compl_free(); compl_direction = FORWARD; if (startcol > curwin->w_cursor.col) @@ -3166,8 +3163,7 @@ static bool 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_EVENT - || c == K_FOCUSGAINED || c == K_FOCUSLOST) { + || c == K_MOUSELEFT || c == K_MOUSERIGHT || c == K_EVENT) { return retval; } @@ -3544,19 +3540,19 @@ theend: /* * Add completions from a list. */ -static void ins_compl_add_list(list_T *list) +static void ins_compl_add_list(list_T *const list) { - listitem_T *li; int dir = compl_direction; - /* Go through the List with matches and add each of them. */ - for (li = list->lv_first; li != NULL; li = li->li_next) { - if (ins_compl_add_tv(&li->li_tv, dir) == OK) - /* if dir was BACKWARD then honor it just once */ + // Go through the List with matches and add each of them. + TV_LIST_ITER(list, li, { + if (ins_compl_add_tv(TV_LIST_ITEM_TV(li), dir) == OK) { + // If dir was BACKWARD then honor it just once. dir = FORWARD; - else if (did_emsg) + } else if (did_emsg) { break; - } + } + }); } /* @@ -3608,6 +3604,8 @@ int ins_compl_add_tv(typval_T *const tv, const Direction dir) cptext[CPT_MENU] = tv_dict_get_string(tv->vval.v_dict, "menu", true); cptext[CPT_KIND] = tv_dict_get_string(tv->vval.v_dict, "kind", true); cptext[CPT_INFO] = tv_dict_get_string(tv->vval.v_dict, "info", true); + cptext[CPT_USER_DATA] = tv_dict_get_string(tv->vval.v_dict, + "user_data", true); icase = (bool)tv_dict_get_number(tv->vval.v_dict, "icase"); adup = (bool)tv_dict_get_number(tv->vval.v_dict, "dup"); @@ -4051,8 +4049,9 @@ static void ins_compl_insert(int in_compl_func) // Set completed item. // { word, abbr, menu, kind, info } dict_T *dict = tv_dict_alloc(); - tv_dict_add_str(dict, S_LEN("word"), - (const char *)EMPTY_IF_NULL(compl_shown_match->cp_str)); + tv_dict_add_str( + dict, S_LEN("word"), + (const char *)EMPTY_IF_NULL(compl_shown_match->cp_str)); tv_dict_add_str( dict, S_LEN("abbr"), (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_ABBR])); @@ -4065,6 +4064,9 @@ static void ins_compl_insert(int in_compl_func) tv_dict_add_str( dict, S_LEN("info"), (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_INFO])); + tv_dict_add_str( + dict, S_LEN("user_data"), + (const char *)EMPTY_IF_NULL(compl_shown_match->cp_text[CPT_USER_DATA])); set_vim_var_dict(VV_COMPLETED_ITEM, dict); if (!in_compl_func) { compl_curr_match = compl_shown_match; @@ -4689,7 +4691,7 @@ static int ins_complete(int c, bool enable_pum) line = ml_get(curwin->w_cursor.lnum); compl_pattern = vim_strnsave(line + compl_col, compl_length); } else { - EMSG2(_(e_intern2), "ins_complete()"); + internal_error("ins_complete()"); return FAIL; } @@ -5243,28 +5245,27 @@ insertchar ( buf[0] = c; i = 1; - if (textwidth > 0) + if (textwidth > 0) { virtcol = get_nolist_virtcol(); - /* - * Stop the string when: - * - no more chars available - * - finding a special character (command key) - * - buffer is full - * - running into the 'textwidth' boundary - * - need to check for abbreviation: A non-word char after a word-char - */ - while ( (c = vpeekc()) != NUL - && !ISSPECIAL(c) - && (!has_mbyte || MB_BYTE2LEN_CHECK(c) == 1) - && i < INPUT_BUFLEN - && (textwidth == 0 - || (virtcol += byte2cells(buf[i - 1])) < (colnr_T)textwidth) - && !(!no_abbr && !vim_iswordc(c) && vim_iswordc(buf[i - 1]))) { + } + // Stop the string when: + // - no more chars available + // - finding a special character (command key) + // - buffer is full + // - running into the 'textwidth' boundary + // - need to check for abbreviation: A non-word char after a word-char + while ((c = vpeekc()) != NUL + && !ISSPECIAL(c) + && (!has_mbyte || MB_BYTE2LEN_CHECK(c) == 1) + && i < INPUT_BUFLEN + && !(p_fkmap && KeyTyped) // Farsi mode mapping moves cursor + && (textwidth == 0 + || (virtcol += byte2cells(buf[i - 1])) < (colnr_T)textwidth) + && !(!no_abbr && !vim_iswordc(c) && vim_iswordc(buf[i - 1]))) { c = vgetc(); - if (p_hkmap && KeyTyped) - c = hkmap(c); /* Hebrew mode mapping */ - if (p_fkmap && KeyTyped) - c = fkmap(c); /* Farsi mode mapping */ + if (p_hkmap && KeyTyped) { + c = hkmap(c); // Hebrew mode mapping + } buf[i++] = c; } @@ -6074,27 +6075,30 @@ void free_last_insert(void) #endif -/* - * Add character "c" to buffer "s". Escape the special meaning of K_SPECIAL - * and CSI. Handle multi-byte characters. - * Returns a pointer to after the added bytes. - */ +/// Add character "c" to buffer "s" +/// +/// Escapes the special meaning of K_SPECIAL and CSI, handles multi-byte +/// characters. +/// +/// @param[in] c Character to add. +/// @param[out] s Buffer to add to. Must have at least MB_MAXBYTES + 1 bytes. +/// +/// @return Pointer to after the added bytes. char_u *add_char2buf(int c, char_u *s) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { char_u temp[MB_MAXBYTES + 1]; - int i; - int len; - - len = (*mb_char2bytes)(c, temp); - for (i = 0; i < len; ++i) { + const int len = utf_char2bytes(c, temp); + for (int i = 0; i < len; i++) { c = temp[i]; - /* Need to escape K_SPECIAL and CSI like in the typeahead buffer. */ + // Need to escape K_SPECIAL and CSI like in the typeahead buffer. if (c == K_SPECIAL) { *s++ = K_SPECIAL; *s++ = KS_SPECIAL; *s++ = KE_FILLER; - } else + } else { *s++ = c; + } } return s; } @@ -6912,7 +6916,9 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty) if (try_match && *look == keytyped) { return true; } - look++; + if (*look != NUL) { + look++; + } } /* @@ -7478,13 +7484,11 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) int oldState; int cpc[MAX_MCO]; /* composing characters */ - /* - * can't delete anything in an empty file - * can't backup past first character in buffer - * can't backup past starting point unless 'backspace' > 1 - * can backup to a previous line if 'backspace' == 0 - */ - if (bufempty() + // can't delete anything in an empty file + // can't backup past first character in buffer + // can't backup past starting point unless 'backspace' > 1 + // can backup to a previous line if 'backspace' == 0 + if (BUFEMPTY() || (!revins_on && ((curwin->w_cursor.lnum == 1 && curwin->w_cursor.col == 0) || (!can_bs(BS_START) diff --git a/src/nvim/edit.h b/src/nvim/edit.h index 0d61f26bcc..433a941295 100644 --- a/src/nvim/edit.h +++ b/src/nvim/edit.h @@ -6,11 +6,12 @@ /* * Array indexes used for cptext argument of ins_compl_add(). */ -#define CPT_ABBR 0 /* "abbr" */ -#define CPT_MENU 1 /* "menu" */ -#define CPT_KIND 2 /* "kind" */ -#define CPT_INFO 3 /* "info" */ -#define CPT_COUNT 4 /* Number of entries */ +#define CPT_ABBR 0 // "abbr" +#define CPT_MENU 1 // "menu" +#define CPT_KIND 2 // "kind" +#define CPT_INFO 3 // "info" +#define CPT_USER_DATA 4 // "user data" +#define CPT_COUNT 5 // Number of entries typedef int (*IndentGetter)(void); diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 18aa5bf763..0f7a1eb004 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -24,6 +24,7 @@ #endif #include "nvim/eval.h" #include "nvim/buffer.h" +#include "nvim/channel.h" #include "nvim/charset.h" #include "nvim/cursor.h" #include "nvim/diff.h" @@ -47,6 +48,7 @@ #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/memory.h" +#include "nvim/menu.h" #include "nvim/message.h" #include "nvim/misc1.h" #include "nvim/keymap.h" @@ -108,9 +110,6 @@ #define DICT_MAXNEST 100 /* maximum nesting of lists and dicts */ -#define DO_NOT_FREE_CNT 99999 /* refcount for dict or list that should not - be freed. */ - #define AUTOLOAD_CHAR '#' /* Character used as separator in autoload function/variable names. */ @@ -216,6 +215,15 @@ static garray_T ga_scripts = {0, 0, sizeof(scriptvar_T *), 4, NULL}; static int echo_attr = 0; /* attributes used for ":echo" */ +/// Describe data to return from find_some_match() +typedef enum { + kSomeMatch, ///< Data for match(). + kSomeMatchEnd, ///< Data for matchend(). + kSomeMatchList, ///< Data for matchlist(). + kSomeMatchStr, ///< Data for matchstr(). + kSomeMatchStrPos, ///< Data for matchstrpos(). +} SomeMatchType; + /// trans_function_name() flags typedef enum { TFN_INT = 1, ///< May use internal function name @@ -333,7 +341,7 @@ static struct vimvar { // VV_SEND_SERVER "servername" // VV_REG "register" // VV_OP "operator" - VV(VV_COUNT, "count", VAR_NUMBER, VV_COMPAT+VV_RO), + VV(VV_COUNT, "count", VAR_NUMBER, VV_RO), VV(VV_COUNT1, "count1", VAR_NUMBER, VV_RO), VV(VV_PREVCOUNT, "prevcount", VAR_NUMBER, VV_RO), VV(VV_ERRMSG, "errmsg", VAR_STRING, VV_COMPAT), @@ -364,6 +372,7 @@ static struct vimvar { VV(VV_DYING, "dying", VAR_NUMBER, VV_RO), VV(VV_EXCEPTION, "exception", VAR_STRING, VV_RO), VV(VV_THROWPOINT, "throwpoint", VAR_STRING, VV_RO), + VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO), VV(VV_REG, "register", VAR_STRING, VV_RO), VV(VV_CMDBANG, "cmdbang", VAR_NUMBER, VV_RO), VV(VV_INSERTMODE, "insertmode", VAR_STRING, VV_RO), @@ -393,7 +402,6 @@ static struct vimvar { VV(VV_OLDFILES, "oldfiles", VAR_LIST, 0), VV(VV_WINDOWID, "windowid", VAR_NUMBER, VV_RO_SBX), VV(VV_PROGPATH, "progpath", VAR_STRING, VV_RO), - VV(VV_COMMAND_OUTPUT, "command_output", VAR_STRING, 0), VV(VV_COMPLETED_ITEM, "completed_item", VAR_DICT, VV_RO), VV(VV_OPTION_NEW, "option_new", VAR_STRING, VV_RO), VV(VV_OPTION_OLD, "option_old", VAR_STRING, VV_RO), @@ -436,31 +444,6 @@ static ScopeDictDictItem vimvars_var; #define vimvarht vimvardict.dv_hashtab typedef struct { - union { - LibuvProcess uv; - PtyProcess pty; - } proc; - Stream in, out, err; // Initialized in common_job_start(). - Terminal *term; - bool stopped; - bool exited; - bool rpc; - int refcount; - Callback on_stdout, on_stderr, on_exit; - varnumber_T *status_ptr; - uint64_t id; - MultiQueue *events; -} TerminalJobData; - -typedef struct { - TerminalJobData *data; - Callback *callback; - const char *type; - list_T *received; - int status; -} JobEvent; - -typedef struct { TimeWatcher tw; int timer_id; int repeat_count; @@ -512,7 +495,6 @@ typedef enum { #define FNE_INCL_BR 1 /* find_name_end(): include [] in name */ #define FNE_CHECK_START 2 /* find_name_end(): check name starts with valid character */ -static PMap(uint64_t) *jobs = NULL; static uint64_t last_timer_id = 0; static PMap(uint64_t) *timers = NULL; @@ -555,7 +537,6 @@ void eval_init(void) { vimvars[VV_VERSION].vv_nr = VIM_VERSION_100; - jobs = pmap_new(uint64_t)(); timers = pmap_new(uint64_t)(); struct vimvar *p; @@ -587,9 +568,9 @@ void eval_init(void) dict_T *const msgpack_types_dict = tv_dict_alloc(); for (size_t i = 0; i < ARRAY_SIZE(msgpack_type_names); i++) { - list_T *const type_list = tv_list_alloc(); - type_list->lv_lock = VAR_FIXED; - type_list->lv_refcount = 1; + list_T *const type_list = tv_list_alloc(0); + tv_list_set_lock(type_list, VAR_FIXED); + tv_list_ref(type_list); dictitem_T *const di = tv_dict_item_alloc(msgpack_type_names[i]); di->di_flags |= DI_FLAGS_RO|DI_FLAGS_FIX; di->di_tv = (typval_T) { @@ -610,7 +591,8 @@ void eval_init(void) dict_T *v_event = tv_dict_alloc(); v_event->dv_lock = VAR_FIXED; set_vim_var_dict(VV_EVENT, v_event); - set_vim_var_list(VV_ERRORS, tv_list_alloc()); + set_vim_var_list(VV_ERRORS, tv_list_alloc(kListLenUnknown)); + set_vim_var_nr(VV_STDERR, CHAN_STDERR); set_vim_var_nr(VV_SEARCHFORWARD, 1L); set_vim_var_nr(VV_HLSEARCH, 1L); set_vim_var_nr(VV_COUNT1, 1); @@ -1037,7 +1019,7 @@ char_u *eval_to_string(char_u *arg, char_u **nextcmd, int convert) ga_init(&ga, (int)sizeof(char), 80); if (tv.vval.v_list != NULL) { tv_list_join(&ga, tv.vval.v_list, "\n"); - if (tv.vval.v_list->lv_len > 0) { + if (tv_list_len(tv.vval.v_list) > 0) { ga_append(&ga, NL); } } @@ -1125,10 +1107,11 @@ static void restore_vimvar(int idx, typval_T *save_tv) vimvars[idx].vv_tv = *save_tv; if (vimvars[idx].vv_type == VAR_UNKNOWN) { hi = hash_find(&vimvarht, vimvars[idx].vv_di.di_key); - if (HASHITEM_EMPTY(hi)) - EMSG2(_(e_intern2), "restore_vimvar()"); - else + if (HASHITEM_EMPTY(hi)) { + internal_error("restore_vimvar()"); + } else { hash_remove(&vimvarht, hi); + } } } @@ -1166,27 +1149,31 @@ list_T *eval_spell_expr(char_u *badword, char_u *expr) return list; } -/* - * "list" is supposed to contain two items: a word and a number. Return the - * word in "pp" and the number as the return value. - * Return -1 if anything isn't right. - * Used to get the good word and score from the eval_spell_expr() result. - */ -int get_spellword(list_T *list, const char **pp) +/// Get spell word from an entry from spellsuggest=expr: +/// +/// Entry in question is supposed to be a list (to be checked by the caller) +/// with two items: a word and a score represented as an unsigned number +/// (whether it actually is unsigned is not checked). +/// +/// Used to get the good word and score from the eval_spell_expr() result. +/// +/// @param[in] list List to get values from. +/// @param[out] ret_word Suggested word. Not initialized if return value is +/// -1. +/// +/// @return -1 in case of error, score otherwise. +int get_spellword(list_T *const list, const char **ret_word) { - listitem_T *li; - - li = list->lv_first; - if (li == NULL) { + if (tv_list_len(list) != 2) { + EMSG(_("E5700: Expression from 'spellsuggest' must yield lists with " + "exactly two values")); return -1; } - *pp = tv_get_string(&li->li_tv); - - li = li->li_next; - if (li == NULL) { + *ret_word = tv_list_find_str(list, 0); + if (*ret_word == NULL) { return -1; } - return tv_get_number(&li->li_tv); + return tv_list_find_nr(list, -1, NULL); } @@ -1292,7 +1279,7 @@ varnumber_T call_func_retnr(char_u *func, int argc, char *call_func_retstr(const char *const func, const int argc, const char_u *const *const argv, const bool safe) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC { typval_T rettv; // All arguments are passed as strings, no conversion to number. @@ -1527,9 +1514,6 @@ ex_let_vars ( ) { char_u *arg = arg_start; - list_T *l; - int i; - listitem_T *item; typval_T ltv; if (*arg != '[') { @@ -1541,46 +1525,52 @@ ex_let_vars ( return OK; } - /* - * ":let [v1, v2] = list" or ":for [v1, v2] in listlist" - */ - if (tv->v_type != VAR_LIST || (l = tv->vval.v_list) == NULL) { + // ":let [v1, v2] = list" or ":for [v1, v2] in listlist" + if (tv->v_type != VAR_LIST) { EMSG(_(e_listreq)); return FAIL; } + list_T *const l = tv->vval.v_list; - i = tv_list_len(l); - if (semicolon == 0 && var_count < i) { + const int len = tv_list_len(l); + if (semicolon == 0 && var_count < len) { EMSG(_("E687: Less targets than List items")); return FAIL; } - if (var_count - semicolon > i) { + if (var_count - semicolon > len) { EMSG(_("E688: More targets than List items")); return FAIL; } + // List l may actually be NULL, but it should fail with E688 or even earlier + // if you try to do ":let [] = v:_null_list". + assert(l != NULL); - item = l->lv_first; + listitem_T *item = tv_list_first(l); + size_t rest_len = tv_list_len(l); while (*arg != ']') { arg = skipwhite(arg + 1); - arg = ex_let_one(arg, &item->li_tv, TRUE, (char_u *)",;]", nextchars); - item = item->li_next; - if (arg == NULL) + arg = ex_let_one(arg, TV_LIST_ITEM_TV(item), true, (const char_u *)",;]", + nextchars); + if (arg == NULL) { return FAIL; + } + rest_len--; + item = TV_LIST_ITEM_NEXT(l, item); arg = skipwhite(arg); if (*arg == ';') { /* Put the rest of the list (may be empty) in the var after ';'. * Create a new list for this. */ - l = tv_list_alloc(); + list_T *const rest_list = tv_list_alloc(rest_len); while (item != NULL) { - tv_list_append_tv(l, &item->li_tv); - item = item->li_next; + tv_list_append_tv(rest_list, TV_LIST_ITEM_TV(item)); + item = TV_LIST_ITEM_NEXT(l, item); } ltv.v_type = VAR_LIST; - ltv.v_lock = 0; - ltv.vval.v_list = l; - l->lv_refcount = 1; + ltv.v_lock = VAR_UNLOCKED; + ltv.vval.v_list = rest_list; + tv_list_ref(rest_list); arg = ex_let_one(skipwhite(arg + 1), <v, false, (char_u *)"]", nextchars); @@ -1590,7 +1580,7 @@ ex_let_vars ( } break; } else if (*arg != ',' && *arg != ']') { - EMSG2(_(e_intern2), "ex_let_vars()"); + internal_error("ex_let_vars()"); return FAIL; } } @@ -2095,6 +2085,8 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, * Loop until no more [idx] or .key is following. */ lp->ll_tv = &v->di_tv; + var1.v_type = VAR_UNKNOWN; + var2.v_type = VAR_UNKNOWN; while (*p == '[' || (*p == '.' && lp->ll_tv->v_type == VAR_DICT)) { if (!(lp->ll_tv->v_type == VAR_LIST && lp->ll_tv->vval.v_list != NULL) && !(lp->ll_tv->v_type == VAR_DICT @@ -2145,9 +2137,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, if (!quiet) { EMSG(_(e_dictrange)); } - if (!empty1) { - tv_clear(&var1); - } + tv_clear(&var1); return NULL; } if (rettv != NULL && (rettv->v_type != VAR_LIST @@ -2155,9 +2145,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, if (!quiet) { emsgf(_("E709: [:] requires a List value")); } - if (!empty1) { - tv_clear(&var1); - } + tv_clear(&var1); return NULL; } p = skipwhite(p + 1); @@ -2166,16 +2154,12 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } else { lp->ll_empty2 = false; if (eval1(&p, &var2, true) == FAIL) { // Recursive! - if (!empty1) { - tv_clear(&var1); - } + tv_clear(&var1); return NULL; } if (!tv_check_str(&var2)) { // Not a number or string. - if (!empty1) { - tv_clear(&var1); - } + tv_clear(&var1); tv_clear(&var2); return NULL; } @@ -2188,12 +2172,8 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, if (!quiet) { emsgf(_(e_missbrac)); } - if (!empty1) { - tv_clear(&var1); - } - if (lp->ll_range && !lp->ll_empty2) { - tv_clear(&var2); - } + tv_clear(&var1); + tv_clear(&var2); return NULL; } @@ -2205,10 +2185,6 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, if (len == -1) { // "[key]": get key from "var1" key = (char_u *)tv_get_string(&var1); // is number or string - if (key == NULL) { - tv_clear(&var1); - return NULL; - } } lp->ll_list = NULL; lp->ll_dict = lp->ll_tv->vval.v_dict; @@ -2251,9 +2227,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, if (!quiet) { emsgf(_(e_dictkey), key); } - if (len == -1) { - tv_clear(&var1); - } + tv_clear(&var1); return NULL; } if (len == -1) { @@ -2261,32 +2235,28 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } else { lp->ll_newkey = vim_strnsave(key, len); } - if (len == -1) { - tv_clear(&var1); - } + tv_clear(&var1); break; // existing variable, need to check if it can be changed } else if (!(flags & GLV_READ_ONLY) && var_check_ro(lp->ll_di->di_flags, (const char *)name, (size_t)(p - name))) { - if (len == -1) { - tv_clear(&var1); - } + tv_clear(&var1); return NULL; } - if (len == -1) { - tv_clear(&var1); - } + tv_clear(&var1); lp->ll_tv = &lp->ll_di->di_tv; } else { // Get the number and item for the only or first index of the List. if (empty1) { lp->ll_n1 = 0; } else { - lp->ll_n1 = (long)tv_get_number(&var1); // Is number or string. - tv_clear(&var1); + // Is number or string. + lp->ll_n1 = (long)tv_get_number(&var1); } + tv_clear(&var1); + lp->ll_dict = NULL; lp->ll_list = lp->ll_tv->vval.v_list; lp->ll_li = tv_list_find(lp->ll_list, lp->ll_n1); @@ -2297,9 +2267,7 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } } if (lp->ll_li == NULL) { - if (lp->ll_range && !lp->ll_empty2) { - tv_clear(&var2); - } + tv_clear(&var2); if (!quiet) { EMSGN(_(e_listidx), lp->ll_n1); } @@ -2337,10 +2305,11 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } } - lp->ll_tv = &lp->ll_li->li_tv; + lp->ll_tv = TV_LIST_ITEM_TV(lp->ll_li); } } + tv_clear(&var1); return p; } @@ -2402,47 +2371,55 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int ll_n1 = lp->ll_n1; // Check whether any of the list items is locked - for (listitem_T *ri = rettv->vval.v_list->lv_first; + for (listitem_T *ri = tv_list_first(rettv->vval.v_list); ri != NULL && ll_li != NULL; ) { - if (tv_check_lock(ll_li->li_tv.v_lock, (const char *)lp->ll_name, + if (tv_check_lock(TV_LIST_ITEM_TV(ll_li)->v_lock, + (const char *)lp->ll_name, TV_CSTRING)) { return; } - ri = ri->li_next; + ri = TV_LIST_ITEM_NEXT(rettv->vval.v_list, ri); if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == ll_n1)) { break; } - ll_li = ll_li->li_next; + ll_li = TV_LIST_ITEM_NEXT(lp->ll_list, ll_li); ll_n1++; } /* * Assign the List values to the list items. */ - for (ri = rettv->vval.v_list->lv_first; ri != NULL; ) { + for (ri = tv_list_first(rettv->vval.v_list); ri != NULL; ) { if (op != NULL && *op != '=') { - eexe_mod_op(&lp->ll_li->li_tv, &ri->li_tv, (const char *)op); + eexe_mod_op(TV_LIST_ITEM_TV(lp->ll_li), TV_LIST_ITEM_TV(ri), + (const char *)op); } else { - tv_clear(&lp->ll_li->li_tv); - tv_copy(&ri->li_tv, &lp->ll_li->li_tv); + tv_clear(TV_LIST_ITEM_TV(lp->ll_li)); + tv_copy(TV_LIST_ITEM_TV(ri), TV_LIST_ITEM_TV(lp->ll_li)); } - ri = ri->li_next; - if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1)) + ri = TV_LIST_ITEM_NEXT(rettv->vval.v_list, ri); + if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1)) { break; - if (lp->ll_li->li_next == NULL) { + } + assert(lp->ll_li != NULL); + if (TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li) == NULL) { // Need to add an empty item. tv_list_append_number(lp->ll_list, 0); - assert(lp->ll_li->li_next); + // ll_li may have become invalid after append, don’t use it. + lp->ll_li = tv_list_last(lp->ll_list); // Valid again. + } else { + lp->ll_li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); } - lp->ll_li = lp->ll_li->li_next; - ++lp->ll_n1; + lp->ll_n1++; } - if (ri != NULL) + if (ri != NULL) { EMSG(_("E710: List value has more items than target")); - else if (lp->ll_empty2 - ? (lp->ll_li != NULL && lp->ll_li->li_next != NULL) - : lp->ll_n1 != lp->ll_n2) + } else if (lp->ll_empty2 + ? (lp->ll_li != NULL + && TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li) != NULL) + : lp->ll_n1 != lp->ll_n2) { EMSG(_("E711: List value has not enough items")); + } } else { typval_T oldtv = TV_INITIAL_VALUE; dict_T *dict = lp->ll_dict; @@ -2487,9 +2464,11 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, notify: if (watched) { if (oldtv.v_type == VAR_UNKNOWN) { + assert(lp->ll_newkey != NULL); tv_dict_watcher_notify(dict, (char *)lp->ll_newkey, lp->ll_tv, NULL); } else { dictitem_T *di = lp->ll_di; + assert(di->di_key != NULL); tv_dict_watcher_notify(dict, (char *)di->di_key, lp->ll_tv, &oldtv); tv_clear(&oldtv); } @@ -2541,7 +2520,7 @@ void *eval_for_line(const char_u *arg, bool *errp, char_u **nextcmdp, int skip) * list being used in "tv". */ fi->fi_list = l; tv_list_watch_add(l, &fi->fi_lw); - fi->fi_lw.lw_item = l->lv_first; + fi->fi_lw.lw_item = tv_list_first(l); } } } @@ -2559,21 +2538,18 @@ void *eval_for_line(const char_u *arg, bool *errp, char_u **nextcmdp, int skip) * Return TRUE when a valid item was found, FALSE when at end of list or * something wrong. */ -int next_for_item(void *fi_void, char_u *arg) +bool next_for_item(void *fi_void, char_u *arg) { - forinfo_T *fi = (forinfo_T *)fi_void; - int result; - listitem_T *item; + forinfo_T *fi = (forinfo_T *)fi_void; - item = fi->fi_lw.lw_item; - if (item == NULL) - result = FALSE; - else { - fi->fi_lw.lw_item = item->li_next; - result = (ex_let_vars(arg, &item->li_tv, TRUE, - fi->fi_semicolon, fi->fi_varcount, NULL) == OK); + listitem_T *item = fi->fi_lw.lw_item; + if (item == NULL) { + return false; + } else { + fi->fi_lw.lw_item = TV_LIST_ITEM_NEXT(fi->fi_list, item); + return (ex_let_vars(arg, TV_LIST_ITEM_TV(item), true, + fi->fi_semicolon, fi->fi_varcount, NULL) == OK); } - return result; } // TODO(ZyX-I): move to eval/ex_cmds @@ -2897,7 +2873,10 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) } *name_end = cc; } else if ((lp->ll_list != NULL - && tv_check_lock(lp->ll_list->lv_lock, (const char *)lp->ll_name, + // ll_list is not NULL when lvalue is not in a list, NULL lists + // yield E689. + && tv_check_lock(tv_list_locked(lp->ll_list), + (const char *)lp->ll_name, lp->ll_name_len)) || (lp->ll_dict != NULL && tv_check_lock(lp->ll_dict->dv_lock, @@ -2905,27 +2884,26 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) lp->ll_name_len))) { return FAIL; } else if (lp->ll_range) { - listitem_T *li; - listitem_T *ll_li = lp->ll_li; - int ll_n1 = lp->ll_n1; - - while (ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= ll_n1)) { - li = ll_li->li_next; - if (tv_check_lock(ll_li->li_tv.v_lock, (const char *)lp->ll_name, + assert(lp->ll_list != NULL); + // Delete a range of List items. + listitem_T *const first_li = lp->ll_li; + listitem_T *last_li = first_li; + for (;;) { + listitem_T *const li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); + if (tv_check_lock(TV_LIST_ITEM_TV(lp->ll_li)->v_lock, + (const char *)lp->ll_name, lp->ll_name_len)) { return false; } - ll_li = li; - ll_n1++; - } - - /* Delete a range of List items. */ - while (lp->ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) { - li = lp->ll_li->li_next; - tv_list_item_remove(lp->ll_list, lp->ll_li); lp->ll_li = li; - ++lp->ll_n1; + lp->ll_n1++; + if (lp->ll_li == NULL || (!lp->ll_empty2 && lp->ll_n2 < lp->ll_n1)) { + break; + } else { + last_li = lp->ll_li; + } } + tv_list_remove_items(lp->ll_list, first_li, last_li); } else { if (lp->ll_list != NULL) { // unlet a List item. @@ -2933,6 +2911,7 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit) } else { // unlet a Dictionary item. dict_T *d = lp->ll_dict; + assert(d != NULL); dictitem_T *di = lp->ll_di; bool watched = tv_dict_is_watched(d); char *key = NULL; @@ -2987,7 +2966,7 @@ int do_unlet(const char *const name, const size_t name_len, const int forceit) d = di->di_tv.vval.v_dict; } if (d == NULL) { - EMSG2(_(e_intern2), "do_unlet()"); + internal_error("do_unlet()"); return FAIL; } hashitem_T *hi = hash_find(ht, (const char_u *)varname); @@ -3070,13 +3049,13 @@ static int do_lock_var(lval_T *lp, char_u *const name_end, const int deep, /* (un)lock a range of List items. */ while (li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) { - tv_item_lock(&li->li_tv, deep, lock); - li = li->li_next; - ++lp->ll_n1; + tv_item_lock(TV_LIST_ITEM_TV(li), deep, lock); + li = TV_LIST_ITEM_NEXT(lp->ll_list, li); + lp->ll_n1++; } } else if (lp->ll_list != NULL) { // (un)lock a List item. - tv_item_lock(&lp->ll_li->li_tv, deep, lock); + tv_item_lock(TV_LIST_ITEM_TV(lp->ll_li), deep, lock); } else { // (un)lock a Dictionary item. tv_item_lock(&lp->ll_di->di_tv, deep, lock); @@ -4239,11 +4218,17 @@ static int eval7( // use its contents. s = deref_func_name((const char *)s, &len, &partial, !evaluate); + // Need to make a copy, in case evaluating the arguments makes + // the name invalid. + s = xmemdupz(s, len); + // Invoke the function. ret = get_func_tv(s, len, rettv, arg, curwin->w_cursor.lnum, curwin->w_cursor.lnum, &len, evaluate, partial, NULL); + xfree(s); + // If evaluate is false rettv->v_type was not set in // get_func_tv, but it's needed in handle_subscript() to parse // what follows. So set it here. @@ -4529,18 +4514,18 @@ eval_index ( if (!empty2 && (n2 < 0 || n2 + 1 < n1)) { n2 = -1; } - l = tv_list_alloc(); + l = tv_list_alloc(n2 - n1 + 1); item = tv_list_find(rettv->vval.v_list, n1); while (n1++ <= n2) { - tv_list_append_tv(l, &item->li_tv); - item = item->li_next; + tv_list_append_tv(l, TV_LIST_ITEM_TV(item)); + item = TV_LIST_ITEM_NEXT(rettv->vval.v_list, item); } tv_clear(rettv); rettv->v_type = VAR_LIST; rettv->vval.v_list = l; - l->lv_refcount++; + tv_list_ref(l); } else { - tv_copy(&tv_list_find(rettv->vval.v_list, n1)->li_tv, &var1); + tv_copy(TV_LIST_ITEM_TV(tv_list_find(rettv->vval.v_list, n1)), &var1); tv_clear(rettv); *rettv = var1; } @@ -4885,35 +4870,34 @@ void partial_unref(partial_T *pt) static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate) { list_T *l = NULL; - typval_T tv; - listitem_T *item; if (evaluate) { - l = tv_list_alloc(); + l = tv_list_alloc(kListLenShouldKnow); } *arg = skipwhite(*arg + 1); while (**arg != ']' && **arg != NUL) { - if (eval1(arg, &tv, evaluate) == FAIL) /* recursive! */ + typval_T tv; + if (eval1(arg, &tv, evaluate) == FAIL) { // Recursive! goto failret; + } if (evaluate) { - item = tv_list_item_alloc(); - item->li_tv = tv; - item->li_tv.v_lock = 0; - tv_list_append(l, item); + tv.v_lock = VAR_UNLOCKED; + tv_list_append_owned_tv(l, tv); } - if (**arg == ']') + if (**arg == ']') { break; + } if (**arg != ',') { - EMSG2(_("E696: Missing comma in List: %s"), *arg); + emsgf(_("E696: Missing comma in List: %s"), *arg); goto failret; } *arg = skipwhite(*arg + 1); } if (**arg != ']') { - EMSG2(_("E697: Missing end of List ']': %s"), *arg); + emsgf(_("E697: Missing end of List ']': %s"), *arg); failret: if (evaluate) { tv_list_free(l); @@ -4925,7 +4909,7 @@ failret: if (evaluate) { rettv->v_type = VAR_LIST; rettv->vval.v_list = l; - ++l->lv_refcount; + tv_list_ref(l); } return OK; @@ -5136,12 +5120,12 @@ bool garbage_collect(bool testing) // named functions (matters for closures) ABORTING(set_ref_in_functions(copyID)); - // Jobs + // Channels { - TerminalJobData *data; - map_foreach_value(jobs, data, { - set_ref_in_callback(&data->on_stdout, copyID, NULL, NULL); - set_ref_in_callback(&data->on_stderr, copyID, NULL, NULL); + Channel *data; + map_foreach_value(channels, data, { + set_ref_in_callback_reader(&data->on_stdout, copyID, NULL, NULL); + set_ref_in_callback_reader(&data->on_stderr, copyID, NULL, NULL); set_ref_in_callback(&data->on_exit, copyID, NULL, NULL); }) } @@ -5193,6 +5177,8 @@ bool garbage_collect(bool testing) ABORTING(set_ref_list)(sub.additional_elements, copyID); } + ABORTING(set_ref_in_quickfix)(copyID); + bool did_free = false; if (!abort) { // 2. Free lists and dictionaries that are not referenced. @@ -5260,8 +5246,8 @@ static int free_unref_items(int copyID) // But don't free a list that has a watcher (used in a for loop), these // are not referenced anywhere. for (list_T *ll = gc_first_list; ll != NULL; ll = ll->lv_used_next) { - if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK) - && ll->lv_watch == NULL) { + if ((tv_list_copyid(ll) & COPYID_MASK) != (copyID & COPYID_MASK) + && !tv_list_has_watchers(ll)) { // Free the List and ordinary items it contains, but don't recurse // into Lists and Dictionaries, they will be in the list of dicts // or list of lists. @@ -5281,7 +5267,7 @@ static int free_unref_items(int copyID) for (ll = gc_first_list; ll != NULL; ll = ll_next) { ll_next = ll->lv_used_next; if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK) - && ll->lv_watch == NULL) { + && !tv_list_has_watchers(ll)) { // Free the List and ordinary items it contains, but don't recurse // into Lists and Dictionaries, they will be in the list of dicts // or list of lists. @@ -5346,15 +5332,16 @@ bool set_ref_in_list(list_T *l, int copyID, ht_stack_T **ht_stack) list_T *cur_l = l; for (;;) { - if (!abort) { - // Mark each item in the list. If the item contains a hashtab - // it is added to ht_stack, if it contains a list it is added to - // list_stack. - for (listitem_T *li = cur_l->lv_first; !abort && li != NULL; - li = li->li_next) { - abort = set_ref_in_item(&li->li_tv, copyID, ht_stack, &list_stack); + // Mark each item in the list. If the item contains a hashtab + // it is added to ht_stack, if it contains a list it is added to + // list_stack. + TV_LIST_ITER(cur_l, li, { + if (abort) { + break; } - } + abort = set_ref_in_item(TV_LIST_ITEM_TV(li), copyID, ht_stack, + &list_stack); + }); if (list_stack == NULL) { break; @@ -5699,10 +5686,6 @@ static int get_function_args(char_u **argp, char_u endchar, garray_T *newargs, c = *p; *p = NUL; arg = vim_strsave(arg); - if (arg == NULL) { - *p = c; - goto err_ret; - } // Check for duplicate argument name. for (i = 0; i < newargs->ga_len; i++) { @@ -5826,10 +5809,6 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate) fp = (ufunc_T *)xcalloc(1, sizeof(ufunc_T) + STRLEN(name)); pt = (partial_T *)xcalloc(1, sizeof(partial_T)); - if (pt == NULL) { - xfree(fp); - goto errret; - } ga_init(&newlines, (int)sizeof(char_u *), 1); ga_grow(&newlines, 1); @@ -5957,6 +5936,14 @@ static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate) } #ifdef INCLUDE_GENERATED_DECLARATIONS + +#ifdef _MSC_VER +// This prevents MSVC from replacing the functions with intrinsics, +// and causing errors when trying to get their addresses in funcs.generated.h +#pragma function (ceil) +#pragma function (floor) +#endif + # include "funcs.generated.h" #endif @@ -6215,13 +6202,9 @@ static char_u *fname_trans_sid(const char_u *const name, fname = fname_buf; } else { fname = xmalloc(i + STRLEN(name + llen) + 1); - if (fname == NULL) { - *error = ERROR_OTHER; - } else { - *tofree = fname; - memmove(fname, fname_buf, (size_t)i); - STRCPY(fname + i, name + llen); - } + *tofree = fname; + memmove(fname, fname_buf, (size_t)i); + STRCPY(fname + i, name + llen); } } else { fname = (char_u *)name; @@ -6302,9 +6285,6 @@ call_func( // Make a copy of the name, if it comes from a funcref variable it could // be changed or deleted in the called function. name = vim_strnsave(funcname, len); - if (name == NULL) { - return ret; - } fname = fname_trans_sid(name, fname_buf, &tofree, &error); @@ -6566,12 +6546,10 @@ static void f_abs(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - list_T *l; - - rettv->vval.v_number = 1; /* Default: Failed */ + rettv->vval.v_number = 1; // Default: failed. if (argvars[0].v_type == VAR_LIST) { - if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, "add() argument", TV_TRANSLATE)) { + list_T *const l = argvars[0].vval.v_list; + if (!tv_check_lock(tv_list_locked(l), N_("add() argument"), TV_TRANSLATE)) { tv_list_append_tv(l, &argvars[1]); tv_copy(&argvars[0], rettv); } @@ -6622,9 +6600,10 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr) && u_save(lnum, lnum + 1) == OK) { if (argvars[1].v_type == VAR_LIST) { l = argvars[1].vval.v_list; - if (l == NULL) + if (l == NULL) { return; - li = l->lv_first; + } + li = tv_list_first(l); } for (;; ) { if (l == NULL) { @@ -6632,7 +6611,7 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else if (li == NULL) { break; // End of list. } else { - tv = &li->li_tv; // Append item from list. + tv = TV_LIST_ITEM_TV(li); // Append item from list. } const char *const line = tv_get_string_chk(tv); if (line == NULL) { // Type error. @@ -6644,7 +6623,7 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (l == NULL) { break; } - li = li->li_next; + li = TV_LIST_ITEM_NEXT(l, li); } appended_lines_mark(lnum, added); @@ -6697,7 +6676,7 @@ static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr) } rettv->v_type = VAR_STRING; } else { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, ARGCOUNT); for (idx = 0; idx < ARGCOUNT; idx++) { tv_list_append_string(rettv->vval.v_list, (const char *)alist_name(&ARGLIST[idx]), -1); @@ -6726,6 +6705,39 @@ static void prepare_assert_error(garray_T *gap) } } +// Append "str" to "gap", escaping unprintable characters. +// Changes NL to \n, CR to \r, etc. +static void ga_concat_esc(garray_T *gap, char_u *str) +{ + char_u *p; + char_u buf[NUMBUFLEN]; + + if (str == NULL) { + ga_concat(gap, (char_u *)"NULL"); + return; + } + + for (p = str; *p != NUL; p++) { + switch (*p) { + case BS: ga_concat(gap, (char_u *)"\\b"); break; + case ESC: ga_concat(gap, (char_u *)"\\e"); break; + case FF: ga_concat(gap, (char_u *)"\\f"); break; + case NL: ga_concat(gap, (char_u *)"\\n"); break; + case TAB: ga_concat(gap, (char_u *)"\\t"); break; + case CAR: ga_concat(gap, (char_u *)"\\r"); break; + case '\\': ga_concat(gap, (char_u *)"\\\\"); break; + default: + if (*p < ' ') { + vim_snprintf((char *)buf, NUMBUFLEN, "\\x%02x", *p); + ga_concat(gap, buf); + } else { + ga_append(gap, *p); + } + break; + } + } +} + // Fill "gap" with information about an assert error. static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_str, typval_T *exp_tv, @@ -6740,28 +6752,30 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, } else { if (atype == ASSERT_MATCH || atype == ASSERT_NOTMATCH) { ga_concat(gap, (char_u *)"Pattern "); + } else if (atype == ASSERT_NOTEQUAL) { + ga_concat(gap, (char_u *)"Expected not equal to "); } else { ga_concat(gap, (char_u *)"Expected "); } if (exp_str == NULL) { - tofree = (char_u *) encode_tv2string(exp_tv, NULL); - ga_concat(gap, tofree); + tofree = (char_u *)encode_tv2string(exp_tv, NULL); + ga_concat_esc(gap, tofree); xfree(tofree); } else { - ga_concat(gap, exp_str); + ga_concat_esc(gap, exp_str); } - tofree = (char_u *)encode_tv2string(got_tv, NULL); - if (atype == ASSERT_MATCH) { - ga_concat(gap, (char_u *)" does not match "); - } else if (atype == ASSERT_NOTMATCH) { - ga_concat(gap, (char_u *)" does match "); - } else if (atype == ASSERT_NOTEQUAL) { - ga_concat(gap, (char_u *)" differs from "); - } else { - ga_concat(gap, (char_u *)" but got "); + if (atype != ASSERT_NOTEQUAL) { + if (atype == ASSERT_MATCH) { + ga_concat(gap, (char_u *)" does not match "); + } else if (atype == ASSERT_NOTMATCH) { + ga_concat(gap, (char_u *)" does match "); + } else { + ga_concat(gap, (char_u *)" but got "); + } + tofree = (char_u *)encode_tv2string(got_tv, NULL); + ga_concat_esc(gap, tofree); + xfree(tofree); } - ga_concat(gap, tofree); - xfree(tofree); } } @@ -6772,7 +6786,7 @@ static void assert_error(garray_T *gap) if (vp->vv_type != VAR_LIST || vimvars[VV_ERRORS].vv_list == NULL) { // Make sure v:errors is a list. - set_vim_var_list(VV_ERRORS, tv_list_alloc()); + set_vim_var_list(VV_ERRORS, tv_list_alloc(1)); } tv_list_append_string(vimvars[VV_ERRORS].vv_list, (const char *)gap->ga_data, (ptrdiff_t)gap->ga_len); @@ -7142,8 +7156,7 @@ static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) && argvars[1].v_type != VAR_UNKNOWN && tv_get_number_chk(&argvars[1], &error) != 0 && !error - && (name = tv_get_string_chk(&argvars[0])) != NULL - && !error) { + && (name = tv_get_string_chk(&argvars[0])) != NULL) { buf = buflist_new((char_u *)name, NULL, 1, 0); } @@ -7249,30 +7262,26 @@ static void f_byteidxcomp(typval_T *argvars, typval_T *rettv, FunPtr fptr) int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv) { - listitem_T *item; typval_T argv[MAX_FUNC_ARGS + 1]; int argc = 0; int dummy; int r = 0; - for (item = args->vval.v_list->lv_first; item != NULL; - item = item->li_next) { + TV_LIST_ITER(args->vval.v_list, item, { if (argc == MAX_FUNC_ARGS - (partial == NULL ? 0 : partial->pt_argc)) { EMSG(_("E699: Too many arguments")); - break; + goto func_call_skip_call; } - /* Make a copy of each argument. This is needed to be able to set - * v_lock to VAR_FIXED in the copy without changing the original list. - */ - tv_copy(&item->li_tv, &argv[argc++]); - } + // Make a copy of each argument. This is needed to be able to set + // v_lock to VAR_FIXED in the copy without changing the original list. + tv_copy(TV_LIST_ITEM_TV(item), &argv[argc++]); + }); - if (item == NULL) { - r = call_func(name, (int)STRLEN(name), rettv, argc, argv, NULL, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &dummy, true, partial, selfdict); - } + r = call_func(name, (int)STRLEN(name), rettv, argc, argv, NULL, + curwin->w_cursor.lnum, curwin->w_cursor.lnum, + &dummy, true, partial, selfdict); +func_call_skip_call: // Free the arguments. while (argc > 0) { tv_clear(&argv[--argc]); @@ -7326,6 +7335,76 @@ static void f_changenr(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = curbuf->b_u_seq_cur; } +// "chanclose(id[, stream])" function +static void f_chanclose(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + + if (check_restricted() || check_secure()) { + return; + } + + if (argvars[0].v_type != VAR_NUMBER || (argvars[1].v_type != VAR_STRING + && argvars[1].v_type != VAR_UNKNOWN)) { + EMSG(_(e_invarg)); + return; + } + + ChannelPart part = kChannelPartAll; + if (argvars[1].v_type == VAR_STRING) { + char *stream = (char *)argvars[1].vval.v_string; + if (!strcmp(stream, "stdin")) { + part = kChannelPartStdin; + } else if (!strcmp(stream, "stdout")) { + part = kChannelPartStdout; + } else if (!strcmp(stream, "stderr")) { + part = kChannelPartStderr; + } else if (!strcmp(stream, "rpc")) { + part = kChannelPartRpc; + } else { + EMSG2(_("Invalid channel stream \"%s\""), stream); + return; + } + } + const char *error; + rettv->vval.v_number = channel_close(argvars[0].vval.v_number, part, &error); + if (!rettv->vval.v_number) { + EMSG(error); + } +} + +// "chansend(id, data)" function +static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + + if (check_restricted() || check_secure()) { + return; + } + + if (argvars[0].v_type != VAR_NUMBER || argvars[1].v_type == VAR_UNKNOWN) { + // First argument is the channel id and second is the data to write + EMSG(_(e_invarg)); + return; + } + + ptrdiff_t input_len = 0; + char *input = save_tv_as_string(&argvars[1], &input_len, false); + if (!input) { + // Either the error has been handled by save_tv_as_string(), + // or there is no input to send. + return; + } + uint64_t id = argvars[0].vval.v_number; + const char *error = NULL; + rettv->vval.v_number = channel_send(id, input, input_len, &error); + if (error) { + EMSG(error); + } +} + /* * "char2nr(string)" function */ @@ -7419,7 +7498,7 @@ static void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (!undo_allowed()) return; - if (argvars[1].v_type != VAR_LIST || argvars[1].vval.v_list == NULL) { + if (argvars[1].v_type != VAR_LIST) { EMSG(_(e_invarg)); return; } @@ -7527,7 +7606,7 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) long idx; if ((l = argvars[0].vval.v_list) != NULL) { - li = l->lv_first; + li = tv_list_first(l); if (argvars[2].v_type != VAR_UNKNOWN) { bool error = false; @@ -7545,9 +7624,11 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr) li = NULL; } - for (; li != NULL; li = li->li_next) - if (tv_equal(&li->li_tv, &argvars[1], ic, FALSE)) - ++n; + for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { + if (tv_equal(TV_LIST_ITEM_TV(li), &argvars[1], ic, false)) { + n++; + } + } } } else if (argvars[0].v_type == VAR_DICT) { int todo; @@ -7691,7 +7772,7 @@ static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr) } const char *const name = tv_get_string(&argvars[0]); - if (name == NULL || *name == NUL) { + if (*name == NUL) { EMSG(_(e_invarg)); return; } @@ -7865,34 +7946,51 @@ static void f_empty(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool n = true; switch (argvars[0].v_type) { - case VAR_STRING: - case VAR_FUNC: - n = argvars[0].vval.v_string == NULL - || *argvars[0].vval.v_string == NUL; - break; - case VAR_PARTIAL: - n = false; - break; - case VAR_NUMBER: - n = argvars[0].vval.v_number == 0; - break; - case VAR_FLOAT: - n = argvars[0].vval.v_float == 0.0; - break; - case VAR_LIST: - n = argvars[0].vval.v_list == NULL - || argvars[0].vval.v_list->lv_first == NULL; - break; - case VAR_DICT: - n = argvars[0].vval.v_dict == NULL - || argvars[0].vval.v_dict->dv_hashtab.ht_used == 0; - break; - case VAR_SPECIAL: - n = argvars[0].vval.v_special != kSpecialVarTrue; - break; - case VAR_UNKNOWN: - EMSG2(_(e_intern2), "f_empty(UNKNOWN)"); - break; + case VAR_STRING: + case VAR_FUNC: { + n = argvars[0].vval.v_string == NULL + || *argvars[0].vval.v_string == NUL; + break; + } + case VAR_PARTIAL: { + n = false; + break; + } + case VAR_NUMBER: { + n = argvars[0].vval.v_number == 0; + break; + } + case VAR_FLOAT: { + n = argvars[0].vval.v_float == 0.0; + break; + } + case VAR_LIST: { + n = (tv_list_len(argvars[0].vval.v_list) == 0); + break; + } + case VAR_DICT: { + n = (tv_dict_len(argvars[0].vval.v_dict) == 0); + break; + } + case VAR_SPECIAL: { + // Using switch to get warning if SpecialVarValue receives more values. + switch (argvars[0].vval.v_special) { + case kSpecialVarTrue: { + n = false; + break; + } + case kSpecialVarFalse: + case kSpecialVarNull: { + n = true; + break; + } + } + break; + } + case VAR_UNKNOWN: { + internal_error("f_empty(UNKNOWN)"); + break; + } } rettv->vval.v_number = n; @@ -7956,17 +8054,22 @@ static void f_executable(typval_T *argvars, typval_T *rettv, FunPtr fptr) && os_can_exe((const char_u *)name, NULL, false))); } +typedef struct { + const list_T *const l; + const listitem_T *li; +} GetListLineCookie; + static char_u *get_list_line(int c, void *cookie, int indent) { - const listitem_T **const p = (const listitem_T **)cookie; - const listitem_T *item = *p; + GetListLineCookie *const p = (GetListLineCookie *)cookie; + const listitem_T *const item = p->li; if (item == NULL) { return NULL; } char buf[NUMBUFLEN]; - const char *const s = tv_get_string_buf_chk(&item->li_tv, buf); - *p = item->li_next; + const char *const s = tv_get_string_buf_chk(TV_LIST_ITEM_TV(item), buf); + p->li = TV_LIST_ITEM_NEXT(p->l, item); return (char_u *)(s == NULL ? NULL : xstrdup(s)); } @@ -8008,11 +8111,14 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) do_cmdline_cmd(tv_get_string(&argvars[0])); } else if (argvars[0].vval.v_list != NULL) { list_T *const list = argvars[0].vval.v_list; - list->lv_refcount++; - listitem_T *const item = list->lv_first; - do_cmdline(NULL, get_list_line, (void *)&item, + tv_list_ref(list); + GetListLineCookie cookie = { + .l = list, + .li = tv_list_first(list), + }; + do_cmdline(NULL, get_list_line, (void *)&cookie, DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED); - list->lv_refcount--; + tv_list_unref(list); } msg_silent = save_msg_silent; emsg_silent = save_emsg_silent; @@ -8020,8 +8126,7 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) ga_append(capture_ga, NUL); rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strsave(capture_ga->ga_data); - ga_clear(capture_ga); + rettv->vval.v_string = capture_ga->ga_data; capture_ga = save_capture_ga; } @@ -8130,7 +8235,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) result = eval_vars((char_u *)s, (char_u *)s, &len, NULL, &errormsg, NULL); emsg_off--; if (rettv->v_type == VAR_LIST) { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, (result != NULL)); if (result != NULL) { tv_list_append_string(rettv->vval.v_list, (const char *)result, -1); } @@ -8153,8 +8258,8 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = ExpandOne(&xpc, (char_u *)s, NULL, options, WILD_ALL); } else { - tv_list_alloc_ret(rettv); ExpandOne(&xpc, (char_u *)s, NULL, options, WILD_ALL_KEEP); + tv_list_alloc_ret(rettv, xpc.xp_numfiles); for (int i = 0; i < xpc.xp_numfiles; i++) { tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i], -1); @@ -8167,6 +8272,19 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } + +/// "menu_get(path [, modes])" function +static void f_menu_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + tv_list_alloc_ret(rettv, kListLenMayKnow); + int modes = MENU_ALL_MODES; + if (argvars[1].v_type == VAR_STRING) { + const char_u *const strmodes = (char_u *)tv_get_string(&argvars[1]); + modes = get_menu_cmd_modes(strmodes, false, NULL, NULL); + } + menu_get((char_u *)tv_get_string(&argvars[0]), modes, rettv->vval.v_list); +} + /* * "extend(list, list [, idx])" function * "extend(dict, dict [, action])" function @@ -8181,14 +8299,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *const l1 = argvars[0].vval.v_list; list_T *const l2 = argvars[1].vval.v_list; - if (l1 == NULL) { - const bool locked = tv_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE); - (void)locked; - assert(locked == true); - } else if (l2 == NULL) { - // Do nothing - tv_copy(&argvars[0], rettv); - } else if (!tv_check_lock(l1->lv_lock, arg_errmsg, TV_TRANSLATE)) { + if (!tv_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) { listitem_T *item; if (argvars[2].v_type != VAR_UNKNOWN) { before = (long)tv_get_number_chk(&argvars[2], &error); @@ -8196,7 +8307,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; // Type error; errmsg already given. } - if (before == l1->lv_len) { + if (before == tv_list_len(l1)) { item = NULL; } else { item = tv_list_find(l1, before); @@ -8205,8 +8316,9 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } } - } else + } else { item = NULL; + } tv_list_extend(l1, l2, item); tv_copy(&argvars[0], rettv); @@ -8325,7 +8437,7 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) } if (count < 0) { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenUnknown); } if (*fname != NUL && !error) { @@ -8358,7 +8470,6 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) static void filter_map(typval_T *argvars, typval_T *rettv, int map) { typval_T *expr; - listitem_T *li, *nli; list_T *l = NULL; dictitem_T *di; hashtab_T *ht; @@ -8376,11 +8487,14 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) int idx = 0; if (argvars[0].v_type == VAR_LIST) { + tv_copy(&argvars[0], rettv); if ((l = argvars[0].vval.v_list) == NULL - || (!map && tv_check_lock(l->lv_lock, arg_errmsg, TV_TRANSLATE))) { + || (!map && tv_check_lock(tv_list_locked(l), arg_errmsg, + TV_TRANSLATE))) { return; } } else if (argvars[0].v_type == VAR_DICT) { + tv_copy(&argvars[0], rettv); if ((d = argvars[0].vval.v_dict) == NULL || (!map && tv_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) { return; @@ -8439,18 +8553,21 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) } else { vimvars[VV_KEY].vv_type = VAR_NUMBER; - for (li = l->lv_first; li != NULL; li = nli) { + for (listitem_T *li = tv_list_first(l); li != NULL;) { if (map - && tv_check_lock(li->li_tv.v_lock, arg_errmsg, TV_TRANSLATE)) { + && tv_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg, + TV_TRANSLATE)) { break; } - nli = li->li_next; vimvars[VV_KEY].vv_nr = idx; - if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL - || did_emsg) + if (filter_map_one(TV_LIST_ITEM_TV(li), expr, map, &rem) == FAIL + || did_emsg) { break; + } if (!map && rem) { - tv_list_item_remove(l, li); + li = tv_list_item_remove(l, li); + } else { + li = TV_LIST_ITEM_NEXT(l, li); } idx++; } @@ -8461,8 +8578,6 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map) did_emsg |= save_did_emsg; } - - tv_copy(&argvars[0], rettv); } static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp) @@ -8693,10 +8808,9 @@ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr) foldstart = (linenr_T)get_vim_var_nr(VV_FOLDSTART); foldend = (linenr_T)get_vim_var_nr(VV_FOLDEND); dashes = get_vim_var_str(VV_FOLDDASHES); - if (foldstart > 0 && foldend <= curbuf->b_ml.ml_line_count - && dashes != NULL) { - /* Find first non-empty line in the fold. */ - for (lnum = foldstart; lnum < foldend; ++lnum) { + if (foldstart > 0 && foldend <= curbuf->b_ml.ml_line_count) { + // Find first non-empty line in the fold. + for (lnum = foldstart; lnum < foldend; lnum++) { if (!linewhite(lnum)) { break; } @@ -8819,10 +8933,8 @@ static void common_function(typval_T *argvars, typval_T *rettv, snprintf(sid_buf, sizeof(sid_buf), "<SNR>%" PRId64 "_", (int64_t)current_SID); name = xmalloc(STRLEN(sid_buf) + STRLEN(s + off) + 1); - if (name != NULL) { - STRCPY(name, sid_buf); - STRCAT(name, s + off); - } + STRCPY(name, sid_buf); + STRCAT(name, s + off); } else { name = vim_strsave(s); } @@ -8857,7 +8969,7 @@ static void common_function(typval_T *argvars, typval_T *rettv, goto theend; } list = argvars[arg_idx].vval.v_list; - if (list == NULL || list->lv_len == 0) { + if (tv_list_len(list) == 0) { arg_idx = 0; } } @@ -8868,25 +8980,18 @@ static void common_function(typval_T *argvars, typval_T *rettv, // result is a VAR_PARTIAL if (arg_idx > 0 || (arg_pt != NULL && arg_pt->pt_argc > 0)) { const int arg_len = (arg_pt == NULL ? 0 : arg_pt->pt_argc); - const int lv_len = (list == NULL ? 0 : list->lv_len); + const int lv_len = tv_list_len(list); pt->pt_argc = arg_len + lv_len; pt->pt_argv = xmalloc(sizeof(pt->pt_argv[0]) * pt->pt_argc); - if (pt->pt_argv == NULL) { - xfree(pt); - xfree(name); - goto theend; - } int i = 0; for (; i < arg_len; i++) { tv_copy(&arg_pt->pt_argv[i], &pt->pt_argv[i]); } if (lv_len > 0) { - for (listitem_T *li = list->lv_first; - li != NULL; - li = li->li_next) { - tv_copy(&li->li_tv, &pt->pt_argv[i++]); - } + TV_LIST_ITER(list, li, { + tv_copy(TV_LIST_ITEM_TV(li), &pt->pt_argv[i++]); + }); } } @@ -8972,7 +9077,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) li = tv_list_find(l, tv_get_number_chk(&argvars[1], &error)); if (!error && li != NULL) { - tv = &li->li_tv; + tv = TV_LIST_ITEM_TV(li); } } } else if (argvars[0].v_type == VAR_DICT) { @@ -9013,7 +9118,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else if (strcmp(what, "args") == 0) { rettv->v_type = VAR_LIST; - if (tv_list_alloc_ret(rettv) != NULL) { + if (tv_list_alloc_ret(rettv, pt->pt_argc) != NULL) { for (int i = 0; i < pt->pt_argc; i++) { tv_list_append_tv(rettv->vval.v_list, &pt->pt_argv[i]); } @@ -9037,8 +9142,10 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// Returns information about signs placed in a buffer as list of dicts. -static void get_buffer_signs(buf_T *buf, list_T *l) +static list_T *get_buffer_signs(buf_T *buf) + FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { + list_T *const l = tv_list_alloc(kListLenMayKnow); for (signlist_T *sign = buf->b_signlist; sign; sign = sign->next) { dict_T *const d = tv_dict_alloc(); @@ -9049,6 +9156,7 @@ static void get_buffer_signs(buf_T *buf, list_T *l) tv_list_append_dict(l, d); } + return l; } /// Returns buffer options, variables and other attributes in a dictionary. @@ -9059,7 +9167,8 @@ static dict_T *get_buffer_info(buf_T *buf) tv_dict_add_nr(dict, S_LEN("bufnr"), buf->b_fnum); tv_dict_add_str(dict, S_LEN("name"), buf->b_ffname != NULL ? (const char *)buf->b_ffname : ""); - tv_dict_add_nr(dict, S_LEN("lnum"), buflist_findlnum(buf)); + tv_dict_add_nr(dict, S_LEN("lnum"), + buf == curbuf ? curwin->w_cursor.lnum : buflist_findlnum(buf)); tv_dict_add_nr(dict, S_LEN("loaded"), buf->b_ml.ml_mfp != NULL); tv_dict_add_nr(dict, S_LEN("listed"), buf->b_p_bl); tv_dict_add_nr(dict, S_LEN("changed"), bufIsChanged(buf)); @@ -9071,7 +9180,7 @@ static dict_T *get_buffer_info(buf_T *buf) tv_dict_add_dict(dict, S_LEN("variables"), buf->b_vars); // List of windows displaying this buffer - list_T *const windows = tv_list_alloc(); + list_T *const windows = tv_list_alloc(kListLenMayKnow); FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->w_buffer == buf) { tv_list_append_number(windows, (varnumber_T)wp->handle); @@ -9081,9 +9190,7 @@ static dict_T *get_buffer_info(buf_T *buf) if (buf->b_signlist != NULL) { // List of signs placed in this buffer - list_T *const signs = tv_list_alloc(); - get_buffer_signs(buf, signs); - tv_dict_add_list(dict, S_LEN("signs"), signs); + tv_dict_add_list(dict, S_LEN("signs"), get_buffer_signs(buf)); } return dict; @@ -9097,7 +9204,7 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool sel_buflisted = false; bool sel_bufloaded = false; - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenMayKnow); // List of all the buffers or selected buffers if (argvars[0].v_type == VAR_DICT) { @@ -9141,9 +9248,7 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) } dict_T *const d = get_buffer_info(buf); - if (d != NULL) { - tv_list_append_dict(rettv->vval.v_list, d); - } + tv_list_append_dict(rettv->vval.v_list, d); if (argbuf != NULL) { return; } @@ -9158,35 +9263,33 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retlist, typval_T *rettv) { - char_u *p; - - rettv->v_type = VAR_STRING; + rettv->v_type = (retlist ? VAR_LIST : VAR_STRING); rettv->vval.v_string = NULL; - if (retlist) { - tv_list_alloc_ret(rettv); - } - if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0) + if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0 || end < start) { + if (retlist) { + tv_list_alloc_ret(rettv, 0); + } return; + } - if (!retlist) { - if (start >= 1 && start <= buf->b_ml.ml_line_count) - p = ml_get_buf(buf, start, FALSE); - else - p = (char_u *)""; - rettv->vval.v_string = vim_strsave(p); - } else { - if (end < start) - return; - - if (start < 1) + if (retlist) { + if (start < 1) { start = 1; - if (end > buf->b_ml.ml_line_count) + } + if (end > buf->b_ml.ml_line_count) { end = buf->b_ml.ml_line_count; + } + tv_list_alloc_ret(rettv, end - start + 1); while (start <= end) { tv_list_append_string(rettv->vval.v_list, (const char *)ml_get_buf(buf, start++, false), -1); } + } else { + rettv->v_type = VAR_STRING; + rettv->vval.v_string = ((start >= 1 && start <= buf->b_ml.ml_line_count) + ? vim_strsave(ml_get_buf(buf, start, false)) + : NULL); } } @@ -9511,14 +9614,12 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) theend: pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context); - tv_list_alloc_ret(rettv); - if (pat != NULL) { - ExpandOne(&xpc, pat, NULL, options, WILD_ALL_KEEP); + ExpandOne(&xpc, pat, NULL, options, WILD_ALL_KEEP); + tv_list_alloc_ret(rettv, xpc.xp_numfiles); - for (int i = 0; i < xpc.xp_numfiles; i++) { - tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i], - -1); - } + for (int i = 0; i < xpc.xp_numfiles; i++) { + tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i], + -1); } xfree(pat); ExpandCleanup(&xpc); @@ -9808,7 +9909,7 @@ static void f_getline(typval_T *argvars, typval_T *rettv, FunPtr fptr) const linenr_T lnum = tv_get_lnum(argvars); if (argvars[1].v_type == VAR_UNKNOWN) { - end = 0; + end = lnum; retlist = false; } else { end = tv_get_lnum(&argvars[1]); @@ -9822,7 +9923,7 @@ static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, typval_T *rettv) { if (what_arg->v_type == VAR_UNKNOWN) { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenMayKnow); if (is_qf || wp != NULL) { (void)get_errorlist(wp, -1, rettv->vval.v_list); } @@ -9857,7 +9958,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) matchitem_T *cur = curwin->w_match_head; int i; - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenMayKnow); while (cur != NULL) { dict_T *dict = tv_dict_alloc(); if (cur->match.regprog == NULL) { @@ -9870,7 +9971,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (llpos->lnum == 0) { break; } - list_T *l = tv_list_alloc(); + list_T *const l = tv_list_alloc(1 + (llpos->col > 0 ? 2 : 0)); tv_list_append_number(l, (varnumber_T)llpos->lnum); if (llpos->col > 0) { tv_list_append_number(l, (varnumber_T)llpos->col); @@ -9919,7 +10020,7 @@ static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos) fp = var2fpos(&argvars[0], true, &fnum); } - list_T *l = tv_list_alloc_ret(rettv); + list_T *const l = tv_list_alloc_ret(rettv, 4 + (!!getcurpos)); tv_list_append_number(l, (fnum != -1) ? (varnumber_T)fnum : (varnumber_T)0); tv_list_append_number(l, ((fp != NULL) ? (varnumber_T)fp->lnum @@ -9992,12 +10093,12 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (return_list) { rettv->v_type = VAR_LIST; - rettv->vval.v_list = + rettv->vval.v_list = get_reg_contents(regname, (arg2 ? kGRegExprSrc : 0) | kGRegList); if (rettv->vval.v_list == NULL) { - rettv->vval.v_list = tv_list_alloc(); + rettv->vval.v_list = tv_list_alloc(0); } - rettv->vval.v_list->lv_refcount++; + tv_list_ref(rettv->vval.v_list); } else { rettv->v_type = VAR_STRING; rettv->vval.v_string = get_reg_contents(regname, arg2 ? kGRegExprSrc : 0); @@ -10045,7 +10146,7 @@ static dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx) tv_dict_add_nr(dict, S_LEN("tabnr"), tp_idx); - list_T *const l = tv_list_alloc(); + list_T *const l = tv_list_alloc(kListLenMayKnow); FOR_ALL_WINDOWS_IN_TAB(wp, tp) { tv_list_append_number(l, (varnumber_T)wp->handle); } @@ -10062,7 +10163,9 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) { tabpage_T *tparg = NULL; - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, (argvars[0].v_type == VAR_UNKNOWN + ? 1 + : kListLenMayKnow)); if (argvars[0].v_type != VAR_UNKNOWN) { // Information about one tab page @@ -10080,9 +10183,7 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) continue; } dict_T *const d = get_tabpage_info(tp, tpnr); - if (d != NULL) { - tv_list_append_dict(rettv->vval.v_list, d); - } + tv_list_append_dict(rettv->vval.v_list, d); if (tparg != NULL) { return; } @@ -10163,7 +10264,7 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) { win_T *wparg = NULL; - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenMayKnow); if (argvars[0].v_type != VAR_UNKNOWN) { wparg = win_id2wp(argvars); @@ -10184,9 +10285,7 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) } winnr++; dict_T *const d = get_win_info(wp, tabnr, winnr); - if (d != NULL) { - tv_list_append_dict(rettv->vval.v_list, d); - } + tv_list_append_dict(rettv->vval.v_list, d); if (wparg != NULL) { // found information about a specific window return; @@ -10390,9 +10489,9 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = ExpandOne( &xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options, WILD_ALL); } else { - tv_list_alloc_ret(rettv); ExpandOne(&xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options, WILD_ALL_KEEP); + tv_list_alloc_ret(rettv, xpc.xp_numfiles); for (int i = 0; i < xpc.xp_numfiles; i++) { tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i], -1); @@ -10441,7 +10540,7 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (rettv->v_type == VAR_STRING) { rettv->vval.v_string = ga_concat_strings_sep(&ga, "\n"); } else { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, ga.ga_len); for (int i = 0; i < ga.ga_len; i++) { tv_list_append_string(rettv->vval.v_list, ((const char **)(ga.ga_data))[i], -1); @@ -10580,6 +10679,9 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) "windows", "winaltkeys", "writebackup", +#if defined(HAVE_WSL) + "wsl", +#endif "nvim", }; @@ -10616,6 +10718,10 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) n = has_nvim_version(name + 5); } else if (STRICMP(name, "vim_starting") == 0) { n = (starting != 0); + } else if (STRICMP(name, "ttyin") == 0) { + n = stdin_isatty; + } else if (STRICMP(name, "ttyout") == 0) { + n = stdout_isatty; } else if (STRICMP(name, "multi_byte_encoding") == 0) { n = has_mbyte != 0; #if defined(USE_ICONV) && defined(DYNAMIC_ICONV) @@ -10635,6 +10741,17 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) n = true; } + if (STRICMP(name, "ruby") == 0 && n == true) { + char *rubyhost = call_func_retstr("provider#ruby#Detect", 0, NULL, true); + if (rubyhost) { + if (*rubyhost == NUL) { + // Invalid rubyhost executable. Gem is probably not installed. + n = false; + } + xfree(rubyhost); + } + } + rettv->vval.v_number = n; } @@ -10954,39 +11071,42 @@ static void f_indent(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - list_T *l; - listitem_T *item; long idx = 0; - int ic = FALSE; + bool ic = false; rettv->vval.v_number = -1; if (argvars[0].v_type != VAR_LIST) { EMSG(_(e_listreq)); return; } - l = argvars[0].vval.v_list; + list_T *const l = argvars[0].vval.v_list; if (l != NULL) { - item = l->lv_first; + listitem_T *item = tv_list_first(l); if (argvars[2].v_type != VAR_UNKNOWN) { bool error = false; - // Start at specified item. Use the cached index that tv_list_find() - // sets, so that a negative number also works. - item = tv_list_find(l, tv_get_number_chk(&argvars[2], &error)); - idx = l->lv_idx; - if (argvars[3].v_type != VAR_UNKNOWN) { - ic = tv_get_number_chk(&argvars[3], &error); - } - if (error) { + // Start at specified item. + idx = tv_list_uidx(l, tv_get_number_chk(&argvars[2], &error)); + if (error || idx == -1) { item = NULL; + } else { + item = tv_list_find(l, idx); + assert(item != NULL); + } + if (argvars[3].v_type != VAR_UNKNOWN) { + ic = !!tv_get_number_chk(&argvars[3], &error); + if (error) { + item = NULL; + } } } - for (; item != NULL; item = item->li_next, ++idx) - if (tv_equal(&item->li_tv, &argvars[1], ic, FALSE)) { + for (; item != NULL; item = TV_LIST_ITEM_NEXT(l, item), idx++) { + if (tv_equal(TV_LIST_ITEM_TV(item), &argvars[1], ic, false)) { rettv->vval.v_number = idx; break; } + } } } @@ -11010,16 +11130,18 @@ void get_user_input(const typval_T *const argvars, const char *defstr = ""; const char *cancelreturn = NULL; const char *xp_name = NULL; + Callback input_callback = { .type = kCallbackNone }; char prompt_buf[NUMBUFLEN]; char defstr_buf[NUMBUFLEN]; char cancelreturn_buf[NUMBUFLEN]; char xp_name_buf[NUMBUFLEN]; + char def[1] = { 0 }; if (argvars[0].v_type == VAR_DICT) { if (argvars[1].v_type != VAR_UNKNOWN) { emsgf(_("E5050: {opts} must be the only argument")); return; } - const dict_T *const dict = argvars[0].vval.v_dict; + dict_T *const dict = argvars[0].vval.v_dict; prompt = tv_dict_get_string_buf_chk(dict, S_LEN("prompt"), prompt_buf, ""); if (prompt == NULL) { return; @@ -11028,7 +11150,6 @@ void get_user_input(const typval_T *const argvars, if (defstr == NULL) { return; } - char def[1] = { 0 }; cancelreturn = tv_dict_get_string_buf_chk(dict, S_LEN("cancelreturn"), cancelreturn_buf, def); if (cancelreturn == NULL) { // error @@ -11045,6 +11166,9 @@ void get_user_input(const typval_T *const argvars, if (xp_name == def) { // default to NULL xp_name = NULL; } + if (!tv_dict_get_callback(dict, S_LEN("highlight"), &input_callback)) { + return; + } } else { prompt = tv_get_string_buf_chk(&argvars[0], prompt_buf); if (prompt == NULL) { @@ -11087,28 +11211,30 @@ void get_user_input(const typval_T *const argvars, cmd_silent = false; // Want to see the prompt. // Only the part of the message after the last NL is considered as - // prompt for the command line. - const char *p = strrchr(prompt, '\n'); - if (p == NULL) { - p = prompt; - } else { - p++; - msg_start(); - msg_clr_eos(); - msg_puts_attr_len(prompt, p - prompt, echo_attr); - msg_didout = false; - msg_starthere(); + // prompt for the command line, unlsess cmdline is externalized + const char *p = prompt; + if (!ui_is_external(kUICmdline)) { + const char *lastnl = strrchr(prompt, '\n'); + if (lastnl != NULL) { + p = lastnl+1; + msg_start(); + msg_clr_eos(); + msg_puts_attr_len(prompt, p - prompt, echo_attr); + msg_didout = false; + msg_starthere(); + } } cmdline_row = msg_row; stuffReadbuffSpec(defstr); - int save_ex_normal_busy = ex_normal_busy; + const int save_ex_normal_busy = ex_normal_busy; ex_normal_busy = 0; rettv->vval.v_string = - getcmdline_prompt(inputsecret_flag ? NUL : '@', (char_u *)p, echo_attr, - xp_type, (char_u *)xp_arg); + (char_u *)getcmdline_prompt(inputsecret_flag ? NUL : '@', p, echo_attr, + xp_type, xp_arg, input_callback); ex_normal_busy = save_ex_normal_busy; + callback_free(&input_callback); if (rettv->vval.v_string == NULL && cancelreturn != NULL) { rettv->vval.v_string = (char_u *)xstrdup(cancelreturn); @@ -11144,11 +11270,10 @@ static void f_inputdialog(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - listitem_T *li; int selected; int mouse_used; - if (argvars[0].v_type != VAR_LIST || argvars[0].vval.v_list == NULL) { + if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listarg), "inputlist()"); return; } @@ -11159,15 +11284,16 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) msg_scroll = TRUE; msg_clr_eos(); - for (li = argvars[0].vval.v_list->lv_first; li != NULL; li = li->li_next) { - msg_puts(tv_get_string(&li->li_tv)); + TV_LIST_ITER_CONST(argvars[0].vval.v_list, li, { + msg_puts(tv_get_string(TV_LIST_ITEM_TV(li))); msg_putchar('\n'); - } + }); - /* Ask for choice. */ + // Ask for choice. selected = prompt_for_number(&mouse_used); - if (mouse_used) + if (mouse_used) { selected -= lines_left; + } rettv->vval.v_number = selected; } @@ -11223,9 +11349,8 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listarg), "insert()"); - } else if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, N_("insert() argument"), - TV_TRANSLATE)) { + } else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), + N_("insert() argument"), TV_TRANSLATE)) { long before = 0; if (argvars[2].v_type != VAR_UNKNOWN) { before = tv_get_number_chk(&argvars[2], &error); @@ -11236,7 +11361,7 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) } listitem_T *item = NULL; - if (before != l->lv_len) { + if (before != tv_list_len(l)) { item = tv_list_find(l, before); if (item == NULL) { EMSGN(_(e_listidx), before); @@ -11300,7 +11425,7 @@ static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_dictkey), lv.ll_newkey); } else if (lv.ll_list != NULL) { // List item. - rettv->vval.v_number = tv_islocked(&lv.ll_li->li_tv); + rettv->vval.v_number = tv_islocked(TV_LIST_ITEM_TV(lv.ll_li)); } else { // Dictionary item. rettv->vval.v_number = tv_islocked(&lv.ll_di->di_tv); @@ -11329,43 +11454,41 @@ static void dict_list(typval_T *const tv, typval_T *const rettv, return; } - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, tv_dict_len(tv->vval.v_dict)); TV_DICT_ITER(tv->vval.v_dict, di, { - listitem_T *const li = tv_list_item_alloc(); - tv_list_append(rettv->vval.v_list, li); + typval_T tv = { .v_lock = VAR_UNLOCKED }; switch (what) { case kDictListKeys: { - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = VAR_UNLOCKED; - li->li_tv.vval.v_string = vim_strsave(di->di_key); + tv.v_type = VAR_STRING; + tv.vval.v_string = vim_strsave(di->di_key); break; } case kDictListValues: { - tv_copy(&di->di_tv, &li->li_tv); + tv_copy(&di->di_tv, &tv); break; } case kDictListItems: { // items() - list_T *const sub_l = tv_list_alloc(); - li->li_tv.v_type = VAR_LIST; - li->li_tv.v_lock = VAR_UNLOCKED; - li->li_tv.vval.v_list = sub_l; - sub_l->lv_refcount++; - - listitem_T *sub_li = tv_list_item_alloc(); - tv_list_append(sub_l, sub_li); - sub_li->li_tv.v_type = VAR_STRING; - sub_li->li_tv.v_lock = VAR_UNLOCKED; - sub_li->li_tv.vval.v_string = vim_strsave(di->di_key); - - sub_li = tv_list_item_alloc(); - tv_list_append(sub_l, sub_li); - tv_copy(&di->di_tv, &sub_li->li_tv); + list_T *const sub_l = tv_list_alloc(2); + tv.v_type = VAR_LIST; + tv.vval.v_list = sub_l; + tv_list_ref(sub_l); + + tv_list_append_owned_tv(sub_l, (typval_T) { + .v_type = VAR_STRING, + .v_lock = VAR_UNLOCKED, + .vval.v_string = (char_u *)xstrdup((const char *)di->di_key), + }); + + tv_list_append_tv(sub_l, &di->di_tv); + break; } } + + tv_list_append_owned_tv(rettv->vval.v_list, tv); }); } @@ -11387,68 +11510,6 @@ static void f_items(typval_T *argvars, typval_T *rettv, FunPtr fptr) dict_list(argvars, rettv, 2); } -// "jobclose(id[, stream])" function -static void f_jobclose(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = 0; - - if (check_restricted() || check_secure()) { - return; - } - - if (argvars[0].v_type != VAR_NUMBER || (argvars[1].v_type != VAR_STRING - && argvars[1].v_type != VAR_UNKNOWN)) { - EMSG(_(e_invarg)); - return; - } - - TerminalJobData *data = find_job(argvars[0].vval.v_number); - if (!data) { - EMSG(_(e_invjob)); - return; - } - - Process *proc = (Process *)&data->proc; - - if (argvars[1].v_type == VAR_STRING) { - char *stream = (char *)argvars[1].vval.v_string; - if (!strcmp(stream, "stdin")) { - if (data->rpc) { - EMSG(_("Invalid stream on rpc job, use jobclose(id, 'rpc')")); - } else { - process_close_in(proc); - } - } else if (!strcmp(stream, "stdout")) { - if (data->rpc) { - EMSG(_("Invalid stream on rpc job, use jobclose(id, 'rpc')")); - } else { - process_close_out(proc); - } - } else if (!strcmp(stream, "stderr")) { - process_close_err(proc); - } else if (!strcmp(stream, "rpc")) { - if (data->rpc) { - channel_close(data->id); - } else { - EMSG(_("Invalid job stream: Not an rpc job")); - } - } else { - EMSG2(_("Invalid job stream \"%s\""), stream); - } - } else { - if (data->rpc) { - channel_close(data->id); - process_close_err(proc); - } else { - process_close_streams(proc); - if (proc->type == kProcessTypePty) { - pty_process_close_master(&data->proc.pty); - } - } - } -} - // "jobpid(id)" function static void f_jobpid(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -11464,61 +11525,15 @@ static void f_jobpid(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - TerminalJobData *data = find_job(argvars[0].vval.v_number); + Channel *data = find_job(argvars[0].vval.v_number, true); if (!data) { - EMSG(_(e_invjob)); return; } - Process *proc = (Process *)&data->proc; + Process *proc = (Process *)&data->stream.proc; rettv->vval.v_number = proc->pid; } -// "jobsend()" function -static void f_jobsend(typval_T *argvars, typval_T *rettv, FunPtr fptr) -{ - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = 0; - - if (check_restricted() || check_secure()) { - return; - } - - if (argvars[0].v_type != VAR_NUMBER || argvars[1].v_type == VAR_UNKNOWN) { - // First argument is the job id and second is the string or list to write - // to the job's stdin - EMSG(_(e_invarg)); - return; - } - - TerminalJobData *data = find_job(argvars[0].vval.v_number); - if (!data) { - EMSG(_(e_invjob)); - return; - } - - if (((Process *)&data->proc)->in->closed) { - EMSG(_("Can't send data to the job: stdin is closed")); - return; - } - - if (data->rpc) { - EMSG(_("Can't send raw data to rpc channel")); - return; - } - - ptrdiff_t input_len = 0; - char *input = save_tv_as_string(&argvars[1], &input_len, false); - if (!input) { - // Either the error has been handled by save_tv_as_string(), or there is no - // input to send. - return; - } - - WBuffer *buf = wstream_new_buffer(input, input_len, 1, xfree); - rettv->vval.v_number = wstream_write(data->proc.uv.process.in, buf); -} - // "jobresize(job, width, height)" function static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -11537,19 +11552,18 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr) } - TerminalJobData *data = find_job(argvars[0].vval.v_number); + Channel *data = find_job(argvars[0].vval.v_number, true); if (!data) { - EMSG(_(e_invjob)); return; } - if (data->proc.uv.process.type != kProcessTypePty) { - EMSG(_(e_jobnotpty)); + if (data->stream.proc.type != kProcessTypePty) { + EMSG(_(e_channotpty)); return; } - pty_process_resize(&data->proc.pty, argvars[1].vval.v_number, - argvars[2].vval.v_number); + pty_process_resize(&data->stream.pty, argvars[1].vval.v_number, + argvars[2].vval.v_number); rettv->vval.v_number = 1; } @@ -11569,15 +11583,13 @@ static char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable) } list_T *argl = cmd_tv->vval.v_list; - int argc = argl->lv_len; + int argc = tv_list_len(argl); if (!argc) { EMSG(_(e_invarg)); // List must have at least one item. return NULL; } - assert(argl->lv_first); - - const char *exe = tv_get_string_chk(&argl->lv_first->li_tv); + const char *exe = tv_get_string_chk(TV_LIST_ITEM_TV(tv_list_first(argl))); if (!exe || !os_can_exe((const char_u *)exe, NULL, true)) { if (exe && executable) { *executable = false; @@ -11592,15 +11604,15 @@ static char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable) // Build the argument vector int i = 0; char **argv = xcalloc(argc + 1, sizeof(char *)); - for (listitem_T *arg = argl->lv_first; arg != NULL; arg = arg->li_next) { - const char *a = tv_get_string_chk(&arg->li_tv); + TV_LIST_ITER_CONST(argl, arg, { + const char *a = tv_get_string_chk(TV_LIST_ITEM_TV(arg)); if (!a) { // Did emsg in tv_get_string_chk; just deallocate argv. shell_free_argv(argv); return NULL; } argv[i++] = xstrdup(a); - } + }); return argv; } @@ -11634,8 +11646,8 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool detach = false; bool rpc = false; bool pty = false; - Callback on_stdout = CALLBACK_NONE; - Callback on_stderr = CALLBACK_NONE; + CallbackReader on_stdout = CALLBACK_READER_INIT, + on_stderr = CALLBACK_READER_INIT; Callback on_exit = CALLBACK_NONE; char *cwd = NULL; if (argvars[1].v_type == VAR_DICT) { @@ -11667,32 +11679,21 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit, - pty, rpc, detach, cwd); - Process *proc = (Process *)&data->proc; + uint16_t width = 0, height = 0; + char *term_name = NULL; if (pty) { - uint16_t width = (uint16_t)tv_dict_get_number(job_opts, "width"); - if (width > 0) { - data->proc.pty.width = width; - } - uint16_t height = (uint16_t)tv_dict_get_number(job_opts, "height"); - if (height > 0) { - data->proc.pty.height = height; - } - char *term = tv_dict_get_string(job_opts, "TERM", true); - if (term) { - data->proc.pty.term_name = term; - } + width = (uint16_t)tv_dict_get_number(job_opts, "width"); + height = (uint16_t)tv_dict_get_number(job_opts, "height"); + term_name = tv_dict_get_string(job_opts, "TERM", true); } - if (!rpc && on_stdout.type == kCallbackNone) { - proc->out = NULL; - } - if (on_stderr.type == kCallbackNone) { - proc->err = NULL; + Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, pty, + rpc, detach, cwd, width, height, term_name, + &rettv->vval.v_number); + if (chan) { + channel_create_event(chan, NULL); } - common_job_start(data, rettv); } // "jobstop()" function @@ -11712,14 +11713,12 @@ static void f_jobstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) } - TerminalJobData *data = find_job(argvars[0].vval.v_number); + Channel *data = find_job(argvars[0].vval.v_number, true); if (!data) { - EMSG(_(e_invjob)); return; } - process_stop((Process *)&data->proc); - data->stopped = true; + process_stop((Process *)&data->stream.proc); rettv->vval.v_number = 1; } @@ -11739,30 +11738,34 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } + list_T *args = argvars[0].vval.v_list; - list_T *rv = tv_list_alloc(); + Channel **jobs = xcalloc(tv_list_len(args), sizeof(*jobs)); ui_busy_start(); MultiQueue *waiting_jobs = multiqueue_new_parent(loop_on_put, &main_loop); // For each item in the input list append an integer to the output list. -3 // is used to represent an invalid job id, -2 is for a interrupted job and // -1 for jobs that were skipped or timed out. - for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { - TerminalJobData *data = NULL; - if (arg->li_tv.v_type != VAR_NUMBER - || !(data = find_job(arg->li_tv.vval.v_number))) { - tv_list_append_number(rv, -3); + + int i = 0; + TV_LIST_ITER_CONST(args, arg, { + Channel *chan = NULL; + if (TV_LIST_ITEM_TV(arg)->v_type != VAR_NUMBER + || !(chan = find_job(TV_LIST_ITEM_TV(arg)->vval.v_number, false))) { + jobs[i] = NULL; } else { - // append the list item and set the status pointer so we'll collect the - // status code when the job exits - tv_list_append_number(rv, -1); - data->status_ptr = &rv->lv_last->li_tv.vval.v_number; - // Process any pending events for the job because we'll temporarily - // replace the parent queue - multiqueue_process_events(data->events); - multiqueue_replace_parent(data->events, waiting_jobs); + jobs[i] = chan; + channel_incref(chan); + if (chan->stream.proc.status < 0) { + // Process any pending events for the job because we'll temporarily + // replace the parent queue + multiqueue_process_events(chan->events); + multiqueue_replace_parent(chan->events, waiting_jobs); + } } - } + i++; + }); int remaining = -1; uint64_t before = 0; @@ -11771,24 +11774,21 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) before = os_hrtime(); } - for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { - TerminalJobData *data = NULL; + for (i = 0; i < tv_list_len(args); i++) { if (remaining == 0) { // timed out break; } - if (arg->li_tv.v_type != VAR_NUMBER - || !(data = find_job(arg->li_tv.vval.v_number))) { + + // if the job already exited, but wasn't freed yet + if (jobs[i] == NULL || jobs[i]->stream.proc.status >= 0) { continue; } - int status = process_wait((Process *)&data->proc, remaining, waiting_jobs); + + int status = process_wait(&jobs[i]->stream.proc, remaining, + waiting_jobs); if (status < 0) { // interrupted or timed out, skip remaining jobs. - if (status == -2) { - // set the status so the user can distinguish between interrupted and - // skipped/timeout jobs. - *data->status_ptr = -2; - } break; } if (remaining > 0) { @@ -11801,32 +11801,26 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { - TerminalJobData *data = NULL; - if (arg->li_tv.v_type != VAR_NUMBER - || !(data = find_job(arg->li_tv.vval.v_number))) { - continue; - } - // remove the status pointer because the list may be freed before the - // job exits - data->status_ptr = NULL; - } + list_T *const rv = tv_list_alloc(tv_list_len(args)); // restore the parent queue for any jobs still alive - for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { - TerminalJobData *data = NULL; - if (arg->li_tv.v_type != VAR_NUMBER - || !(data = pmap_get(uint64_t)(jobs, arg->li_tv.vval.v_number))) { + for (i = 0; i < tv_list_len(args); i++) { + if (jobs[i] == NULL) { + tv_list_append_number(rv, -3); continue; } // restore the parent queue for the job - multiqueue_process_events(data->events); - multiqueue_replace_parent(data->events, main_loop.events); + multiqueue_process_events(jobs[i]->events); + multiqueue_replace_parent(jobs[i]->events, main_loop.events); + + tv_list_append_number(rv, jobs[i]->stream.proc.status); + channel_decref(jobs[i]); } multiqueue_free(waiting_jobs); + xfree(jobs); ui_busy_stop(); - rv->lv_refcount++; + tv_list_ref(rv); rettv->v_type = VAR_LIST; rettv->vval.v_list = rv; } @@ -11840,9 +11834,6 @@ static void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_(e_listreq)); return; } - if (argvars[0].vval.v_list == NULL) { - return; - } const char *const sep = (argvars[1].v_type == VAR_UNKNOWN ? " " : tv_get_string_chk(&argvars[1])); @@ -12111,15 +12102,17 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) mode = get_map_mode((char_u **)&which, 0); - keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, false, + keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, true, CPO_TO_CPO_FLAGS); rhs = check_map(keys, mode, exact, false, abbr, &mp, &buffer_local); xfree(keys_buf); if (!get_dict) { - /* Return a string. */ - if (rhs != NULL) - rettv->vval.v_string = str2special_save(rhs, FALSE); + // Return a string. + if (rhs != NULL) { + rettv->vval.v_string = (char_u *)str2special_save( + (const char *)rhs, false, false); + } } else { tv_dict_alloc_ret(rettv); @@ -12154,7 +12147,8 @@ void mapblock_fill_dict(dict_T *const dict, bool compatible) FUNC_ATTR_NONNULL_ALL { - char_u *lhs = str2special_save(mp->m_keys, true); + char *const lhs = str2special_save((const char *)mp->m_keys, + compatible, !compatible); char *const mapmode = map_mode_to_chars(mp->m_mode); varnumber_T noremap_value; @@ -12168,18 +12162,21 @@ void mapblock_fill_dict(dict_T *const dict, noremap_value = mp->m_noremap == REMAP_SCRIPT ? 2 : !!mp->m_noremap; } - tv_dict_add_str(dict, S_LEN("lhs"), (const char *)lhs); - tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str); + if (compatible) { + tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str); + } else { + tv_dict_add_allocated_str(dict, S_LEN("rhs"), + str2special_save((const char *)mp->m_str, false, + true)); + } + tv_dict_add_allocated_str(dict, S_LEN("lhs"), lhs); tv_dict_add_nr(dict, S_LEN("noremap"), noremap_value); tv_dict_add_nr(dict, S_LEN("expr"), mp->m_expr ? 1 : 0); tv_dict_add_nr(dict, S_LEN("silent"), mp->m_silent ? 1 : 0); tv_dict_add_nr(dict, S_LEN("sid"), (varnumber_T)mp->m_script_ID); tv_dict_add_nr(dict, S_LEN("buffer"), (varnumber_T)buffer_value); tv_dict_add_nr(dict, S_LEN("nowait"), mp->m_nowait ? 1 : 0); - tv_dict_add_str(dict, S_LEN("mode"), mapmode); - - xfree(lhs); - xfree(mapmode); + tv_dict_add_allocated_str(dict, S_LEN("mode"), mapmode); } /* @@ -12207,7 +12204,8 @@ static void f_mapcheck(typval_T *argvars, typval_T *rettv, FunPtr fptr) } -static void find_some_match(typval_T *argvars, typval_T *rettv, int type) +static void find_some_match(typval_T *const argvars, typval_T *const rettv, + const SomeMatchType type) { char_u *str = NULL; long len = 0; @@ -12228,25 +12226,38 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) p_cpo = (char_u *)""; rettv->vval.v_number = -1; - if (type == 3 || type == 4) { - // type 3: return empty list when there are no matches. - // type 4: return ["", -1, -1, -1] - tv_list_alloc_ret(rettv); - if (type == 4) { + switch (type) { + // matchlist(): return empty list when there are no matches. + case kSomeMatchList: { + tv_list_alloc_ret(rettv, kListLenMayKnow); + break; + } + // matchstrpos(): return ["", -1, -1, -1] + case kSomeMatchStrPos: { + tv_list_alloc_ret(rettv, 4); tv_list_append_string(rettv->vval.v_list, "", 0); tv_list_append_number(rettv->vval.v_list, -1); tv_list_append_number(rettv->vval.v_list, -1); tv_list_append_number(rettv->vval.v_list, -1); + break; + } + case kSomeMatchStr: { + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + break; + } + case kSomeMatch: + case kSomeMatchEnd: { + // Do nothing: zero is default. + break; } - } else if (type == 2) { - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; } if (argvars[0].v_type == VAR_LIST) { - if ((l = argvars[0].vval.v_list) == NULL) + if ((l = argvars[0].vval.v_list) == NULL) { goto theend; - li = l->lv_first; + } + li = tv_list_first(l); } else { expr = str = (char_u *)tv_get_string(&argvars[0]); len = (long)STRLEN(str); @@ -12266,11 +12277,11 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) goto theend; } if (l != NULL) { - li = tv_list_find(l, start); - if (li == NULL) { + idx = tv_list_uidx(l, start); + if (idx == -1) { goto theend; } - idx = l->lv_idx; // Use the cached index. + li = tv_list_find(l, idx); } else { if (start < 0) start = 0; @@ -12306,7 +12317,8 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) break; } xfree(tofree); - tofree = expr = str = (char_u *)encode_tv2echo(&li->li_tv, NULL); + tofree = expr = str = (char_u *)encode_tv2echo(TV_LIST_ITEM_TV(li), + NULL); if (str == NULL) { break; } @@ -12321,8 +12333,8 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) /* Advance to just after the match. */ if (l != NULL) { - li = li->li_next; - ++idx; + li = TV_LIST_ITEM_NEXT(l, li); + idx++; } else { startcol = (colnr_T)(regmatch.startp[0] + (*mb_ptr2len)(regmatch.startp[0]) - str); @@ -12334,62 +12346,76 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type) } if (match) { - if (type == 4) { - listitem_T *li1 = rettv->vval.v_list->lv_first; - listitem_T *li2 = li1->li_next; - listitem_T *li3 = li2->li_next; - listitem_T *li4 = li3->li_next; - xfree(li1->li_tv.vval.v_string); - - int rd = (int)(regmatch.endp[0] - regmatch.startp[0]); - li1->li_tv.vval.v_string = vim_strnsave(regmatch.startp[0], rd); - li3->li_tv.vval.v_number = (varnumber_T)(regmatch.startp[0] - expr); - li4->li_tv.vval.v_number = (varnumber_T)(regmatch.endp[0] - expr); - if (l != NULL) { - li2->li_tv.vval.v_number = (varnumber_T)idx; + switch (type) { + case kSomeMatchStrPos: { + list_T *const ret_l = rettv->vval.v_list; + listitem_T *li1 = tv_list_first(ret_l); + listitem_T *li2 = TV_LIST_ITEM_NEXT(ret_l, li1); + listitem_T *li3 = TV_LIST_ITEM_NEXT(ret_l, li2); + listitem_T *li4 = TV_LIST_ITEM_NEXT(ret_l, li3); + xfree(TV_LIST_ITEM_TV(li1)->vval.v_string); + + const size_t rd = (size_t)(regmatch.endp[0] - regmatch.startp[0]); + TV_LIST_ITEM_TV(li1)->vval.v_string = xmemdupz( + (const char *)regmatch.startp[0], rd); + TV_LIST_ITEM_TV(li3)->vval.v_number = (varnumber_T)( + regmatch.startp[0] - expr); + TV_LIST_ITEM_TV(li4)->vval.v_number = (varnumber_T)( + regmatch.endp[0] - expr); + if (l != NULL) { + TV_LIST_ITEM_TV(li2)->vval.v_number = (varnumber_T)idx; + } + break; } - } else if (type == 3) { - int i; - - /* return list with matched string and submatches */ - for (i = 0; i < NSUBEXP; ++i) { - if (regmatch.endp[i] == NULL) { - tv_list_append_string(rettv->vval.v_list, NULL, 0); - } else { - tv_list_append_string(rettv->vval.v_list, - (const char *)regmatch.startp[i], - (regmatch.endp[i] - regmatch.startp[i])); + case kSomeMatchList: { + // Return list with matched string and submatches. + for (int i = 0; i < NSUBEXP; i++) { + if (regmatch.endp[i] == NULL) { + tv_list_append_string(rettv->vval.v_list, NULL, 0); + } else { + tv_list_append_string(rettv->vval.v_list, + (const char *)regmatch.startp[i], + (regmatch.endp[i] - regmatch.startp[i])); + } } + break; } - } else if (type == 2) { - // Return matched string. - if (l != NULL) { - tv_copy(&li->li_tv, rettv); - } else { - rettv->vval.v_string = (char_u *)xmemdupz( - (const char *)regmatch.startp[0], - (size_t)(regmatch.endp[0] - regmatch.startp[0])); + case kSomeMatchStr: { + // Return matched string. + if (l != NULL) { + tv_copy(TV_LIST_ITEM_TV(li), rettv); + } else { + rettv->vval.v_string = (char_u *)xmemdupz( + (const char *)regmatch.startp[0], + (size_t)(regmatch.endp[0] - regmatch.startp[0])); + } + break; } - } else if (l != NULL) { - rettv->vval.v_number = idx; - } else { - if (type != 0) { - rettv->vval.v_number = - (varnumber_T)(regmatch.startp[0] - str); - } else { - rettv->vval.v_number = - (varnumber_T)(regmatch.endp[0] - str); + case kSomeMatch: + case kSomeMatchEnd: { + if (l != NULL) { + rettv->vval.v_number = idx; + } else { + if (type == kSomeMatch) { + rettv->vval.v_number = + (varnumber_T)(regmatch.startp[0] - str); + } else { + rettv->vval.v_number = + (varnumber_T)(regmatch.endp[0] - str); + } + rettv->vval.v_number += (varnumber_T)(str - expr); + } + break; } - rettv->vval.v_number += (varnumber_T)(str - expr); } } vim_regfree(regmatch.regprog); } - if (type == 4 && l == NULL) { + if (type == kSomeMatchStrPos && l == NULL) { // matchstrpos() without a list: drop the second item - tv_list_item_remove(rettv->vval.v_list, - rettv->vval.v_list->lv_first->li_next); + list_T *const ret_l = rettv->vval.v_list; + tv_list_item_remove(ret_l, TV_LIST_ITEM_NEXT(ret_l, tv_list_first(ret_l))); } theend: @@ -12402,7 +12428,7 @@ theend: */ static void f_match(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - find_some_match(argvars, rettv, 1); + find_some_match(argvars, rettv, kSomeMatch); } /* @@ -12445,7 +12471,7 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } if (id >= 1 && id <= 3) { - EMSGN("E798: ID is reserved for \":match\": %" PRId64, id); + EMSGN(_("E798: ID is reserved for \":match\": %" PRId64), id); return; } @@ -12455,56 +12481,56 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = -1; + rettv->vval.v_number = -1; - char buf[NUMBUFLEN]; - const char *const group = tv_get_string_buf_chk(&argvars[0], buf); - if (group == NULL) { - return; - } + char buf[NUMBUFLEN]; + const char *const group = tv_get_string_buf_chk(&argvars[0], buf); + if (group == NULL) { + return; + } - if (argvars[1].v_type != VAR_LIST) { - EMSG2(_(e_listarg), "matchaddpos()"); - return; - } + if (argvars[1].v_type != VAR_LIST) { + EMSG2(_(e_listarg), "matchaddpos()"); + return; + } - list_T *l; - l = argvars[1].vval.v_list; - if (l == NULL) { - return; - } + list_T *l; + l = argvars[1].vval.v_list; + if (l == NULL) { + return; + } - bool error = false; - int prio = 10; - int id = -1; - const char *conceal_char = NULL; + bool error = false; + int prio = 10; + int id = -1; + const char *conceal_char = NULL; - if (argvars[2].v_type != VAR_UNKNOWN) { - prio = tv_get_number_chk(&argvars[2], &error); - if (argvars[3].v_type != VAR_UNKNOWN) { - id = tv_get_number_chk(&argvars[3], &error); - if (argvars[4].v_type != VAR_UNKNOWN) { - if (argvars[4].v_type != VAR_DICT) { - EMSG(_(e_dictreq)); - return; - } - dictitem_T *di; - if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal"))) - != NULL) { - conceal_char = tv_get_string(&di->di_tv); - } + if (argvars[2].v_type != VAR_UNKNOWN) { + prio = tv_get_number_chk(&argvars[2], &error); + if (argvars[3].v_type != VAR_UNKNOWN) { + id = tv_get_number_chk(&argvars[3], &error); + if (argvars[4].v_type != VAR_UNKNOWN) { + if (argvars[4].v_type != VAR_DICT) { + EMSG(_(e_dictreq)); + return; + } + dictitem_T *di; + if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal"))) + != NULL) { + conceal_char = tv_get_string(&di->di_tv); } } } - if (error == true) { - return; - } + } + if (error == true) { + return; + } - // id == 3 is ok because matchaddpos() is supposed to substitute :3match - if (id == 1 || id == 2) { - EMSGN("E798: ID is reserved for \"match\": %" PRId64, id); - return; - } + // id == 3 is ok because matchaddpos() is supposed to substitute :3match + if (id == 1 || id == 2) { + EMSGN(_("E798: ID is reserved for \"match\": %" PRId64), id); + return; + } rettv->vval.v_number = match_add(curwin, group, NULL, prio, id, l, conceal_char); @@ -12515,14 +12541,16 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - tv_list_alloc_ret(rettv); + const int id = tv_get_number(&argvars[0]); - int id = tv_get_number(&argvars[0]); + tv_list_alloc_ret(rettv, (id >= 1 && id <= 3 + ? 2 + : 0)); if (id >= 1 && id <= 3) { - matchitem_T *m; + matchitem_T *const m = (matchitem_T *)get_match(curwin, id); - if ((m = (matchitem_T *)get_match(curwin, id)) != NULL) { + if (m != NULL) { tv_list_append_string(rettv->vval.v_list, (const char *)syn_id2name(m->hlg_id), -1); tv_list_append_string(rettv->vval.v_list, (const char *)m->pattern, -1); @@ -12547,7 +12575,7 @@ static void f_matchdelete(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_matchend(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - find_some_match(argvars, rettv, 0); + find_some_match(argvars, rettv, kSomeMatchEnd); } /* @@ -12555,7 +12583,7 @@ static void f_matchend(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_matchlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - find_some_match(argvars, rettv, 3); + find_some_match(argvars, rettv, kSomeMatchList); } /* @@ -12563,13 +12591,13 @@ static void f_matchlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_matchstr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - find_some_match(argvars, rettv, 2); + find_some_match(argvars, rettv, kSomeMatchStr); } /// "matchstrpos()" function static void f_matchstrpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - find_some_match(argvars, rettv, 4); + find_some_match(argvars, rettv, kSomeMatchStrPos); } /// Get maximal/minimal number value in a list or dictionary @@ -12585,41 +12613,41 @@ static void max_min(const typval_T *const tv, typval_T *const rettv, const bool domax) FUNC_ATTR_NONNULL_ALL { - varnumber_T n = 0; bool error = false; + rettv->vval.v_number = 0; + varnumber_T n = (domax ? VARNUMBER_MIN : VARNUMBER_MAX); if (tv->v_type == VAR_LIST) { - const list_T *const l = tv->vval.v_list; - if (tv_list_len(l) != 0) { - n = tv_get_number_chk(&l->lv_first->li_tv, &error); - for (const listitem_T *li = l->lv_first->li_next; li != NULL && !error; - li = li->li_next) { - const varnumber_T i = tv_get_number_chk(&li->li_tv, &error); - if (domax ? i > n : i < n) { - n = i; - } - } + if (tv_list_len(tv->vval.v_list) == 0) { + return; } + TV_LIST_ITER_CONST(tv->vval.v_list, li, { + const varnumber_T i = tv_get_number_chk(TV_LIST_ITEM_TV(li), &error); + if (error) { + return; + } + if (domax ? i > n : i < n) { + n = i; + } + }); } else if (tv->v_type == VAR_DICT) { - if (tv->vval.v_dict != NULL) { - bool first = true; - TV_DICT_ITER(tv->vval.v_dict, di, { - const varnumber_T i = tv_get_number_chk(&di->di_tv, &error); - if (error) { - break; - } - if (first) { - n = i; - first = true; - } else if (domax ? i > n : i < n) { - n = i; - } - }); + if (tv_dict_len(tv->vval.v_dict) == 0) { + return; } + TV_DICT_ITER(tv->vval.v_dict, di, { + const varnumber_T i = tv_get_number_chk(&di->di_tv, &error); + if (error) { + return; + } + if (domax ? i > n : i < n) { + n = i; + } + }); } else { EMSG2(_(e_listdictarg), domax ? "max()" : "min()"); + return; } - rettv->vval.v_number = error ? 0 : n; + rettv->vval.v_number = n; } /* @@ -12704,23 +12732,20 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_listarg), "msgpackdump()"); return; } - list_T *ret_list = tv_list_alloc_ret(rettv); - const list_T *list = argvars[0].vval.v_list; - if (list == NULL) { - return; - } + list_T *const ret_list = tv_list_alloc_ret(rettv, kListLenMayKnow); + list_T *const list = argvars[0].vval.v_list; msgpack_packer *lpacker = msgpack_packer_new(ret_list, &encode_list_write); const char *const msg = _("msgpackdump() argument, index %i"); // Assume that translation will not take more then 4 times more space char msgbuf[sizeof("msgpackdump() argument, index ") * 4 + NUMBUFLEN]; int idx = 0; - for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - vim_snprintf(msgbuf, sizeof(msgbuf), (char *) msg, idx); + TV_LIST_ITER(list, li, { + vim_snprintf(msgbuf, sizeof(msgbuf), (char *)msg, idx); idx++; - if (encode_vim_to_msgpack(lpacker, &li->li_tv, msgbuf) == FAIL) { + if (encode_vim_to_msgpack(lpacker, TV_LIST_ITEM_TV(li), msgbuf) == FAIL) { break; } - } + }); msgpack_packer_free(lpacker); } @@ -12732,12 +12757,12 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_listarg), "msgpackparse()"); return; } - list_T *ret_list = tv_list_alloc_ret(rettv); - const list_T *list = argvars[0].vval.v_list; - if (list == NULL || list->lv_first == NULL) { + list_T *const ret_list = tv_list_alloc_ret(rettv, kListLenMayKnow); + const list_T *const list = argvars[0].vval.v_list; + if (tv_list_len(list) == 0) { return; } - if (list->lv_first->li_tv.v_type != VAR_STRING) { + if (TV_LIST_ITEM_TV(tv_list_first(list))->v_type != VAR_STRING) { EMSG2(_(e_invarg2), "List item is not a string"); return; } @@ -12777,13 +12802,12 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr) goto f_msgpackparse_exit; } if (result == MSGPACK_UNPACK_SUCCESS) { - listitem_T *li = tv_list_item_alloc(); - li->li_tv.v_type = VAR_UNKNOWN; - tv_list_append(ret_list, li); - if (msgpack_to_vim(unpacked.data, &li->li_tv) == FAIL) { + typval_T tv = { .v_type = VAR_UNKNOWN }; + if (msgpack_to_vim(unpacked.data, &tv) == FAIL) { EMSG2(_(e_invarg2), "Failed to convert msgpack string"); goto f_msgpackparse_exit; } + tv_list_append_owned_tv(ret_list, tv); } if (result == MSGPACK_UNPACK_CONTINUE) { if (rlret == OK) { @@ -12989,7 +13013,7 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else if (stride > 0 ? end + 1 < start : end - 1 > start) { emsgf(_("E727: Start past end")); } else { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, (end - start) / stride); for (i = start; stride > 0 ? i <= end : i >= end; i += stride) { tv_list_append_number(rettv->vval.v_list, (varnumber_T)i); } @@ -13010,9 +13034,6 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) long prevlen = 0; /* length of data in prev */ long prevsize = 0; /* size of prev buffer */ long maxline = MAXLNUM; - long cnt = 0; - char_u *p; /* position in buf */ - char_u *start; /* start of current line */ if (argvars[1].v_type != VAR_UNKNOWN) { if (strcmp(tv_get_string(&argvars[1]), "b") == 0) { @@ -13023,7 +13044,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - tv_list_alloc_ret(rettv); + list_T *const l = tv_list_alloc_ret(rettv, kListLenUnknown); // Always open the file in binary mode, library functions have a mind of // their own about CR-LF conversion. @@ -13033,19 +13054,20 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - while (cnt < maxline || maxline < 0) { + while (maxline < 0 || tv_list_len(l) < maxline) { readlen = (int)fread(buf, 1, io_size, fd); - /* This for loop processes what was read, but is also entered at end - * of file so that either: - * - an incomplete line gets written - * - a "binary" file gets an empty line at the end if it ends in a - * newline. */ + // This for loop processes what was read, but is also entered at end + // of file so that either: + // - an incomplete line gets written + // - a "binary" file gets an empty line at the end if it ends in a + // newline. + char_u *p; // Position in buf. + char_u *start; // Start of current line. for (p = buf, start = buf; p < buf + readlen || (readlen <= 0 && (prevlen > 0 || binary)); - ++p) { + p++) { if (*p == '\n' || readlen <= 0) { - listitem_T *li; char_u *s = NULL; size_t len = p - start; @@ -13072,22 +13094,32 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) prevlen = prevsize = 0; } - li = tv_list_item_alloc(); - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = s; - tv_list_append(rettv->vval.v_list, li); - - start = p + 1; /* step over newline */ - if ((++cnt >= maxline && maxline >= 0) || readlen <= 0) + tv_list_append_owned_tv(l, (typval_T) { + .v_type = VAR_STRING, + .v_lock = VAR_UNLOCKED, + .vval.v_string = s, + }); + + start = p + 1; // Step over newline. + if (maxline < 0) { + if (tv_list_len(l) > -maxline) { + assert(tv_list_len(l) == 1 + (-maxline)); + tv_list_item_remove(l, tv_list_first(l)); + } + } else if (tv_list_len(l) >= maxline) { + assert(tv_list_len(l) == maxline); break; - } else if (*p == NUL) + } + if (readlen <= 0) { + break; + } + } else if (*p == NUL) { *p = '\n'; - /* Check for utf8 "bom"; U+FEFF is encoded as EF BB BF. Do this - * when finding the BF and check the previous two bytes. */ - else if (*p == 0xbf && enc_utf8 && !binary) { - /* Find the two bytes before the 0xbf. If p is at buf, or buf - * + 1, these may be in the "prev" string. */ + // Check for utf8 "bom"; U+FEFF is encoded as EF BB BF. Do this + // when finding the BF and check the previous two bytes. + } else if (*p == 0xbf && !binary) { + // Find the two bytes before the 0xbf. If p is at buf, or buf + 1, + // these may be in the "prev" string. char_u back1 = p >= buf + 1 ? p[-1] : prevlen >= 1 ? prev[prevlen - 1] : NUL; char_u back2 = p >= buf + 2 ? p[-2] @@ -13121,8 +13153,9 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } /* for */ - if ((cnt >= maxline && maxline >= 0) || readlen <= 0) + if ((maxline >= 0 && tv_list_len(l) >= maxline) || readlen <= 0) { break; + } if (start < p) { /* There's part of a line in buf, store it in "prev". */ if (p - start + prevlen >= prevsize) { @@ -13146,16 +13179,6 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } /* while */ - /* - * For a negative line count use only the lines at the end of the file, - * free the rest. - */ - if (maxline < 0) - while (cnt > -maxline) { - tv_list_item_remove(rettv->vval.v_list, rettv->vval.v_list->lv_first); - cnt--; - } - xfree(prev); fclose(fd); } @@ -13169,9 +13192,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// @return OK In case of success, FAIL in case of error static int list2proftime(typval_T *arg, proftime_T *tm) FUNC_ATTR_NONNULL_ALL { - if (arg->v_type != VAR_LIST - || arg->vval.v_list == NULL - || arg->vval.v_list->lv_len != 2) { + if (arg->v_type != VAR_LIST || tv_list_len(arg->vval.v_list) != 2) { return FAIL; } @@ -13236,7 +13257,7 @@ static void f_reltime(typval_T *argvars, typval_T *rettv, FunPtr fptr) STATIC_ASSERT(sizeof(u.prof) == sizeof(u) && sizeof(u.split) == sizeof(u), "type punning will produce incorrect results on this platform"); - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, 2); tv_list_append_number(rettv->vval.v_list, u.split.high); tv_list_append_number(rettv->vval.v_list, u.split.low); } @@ -13295,8 +13316,8 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listdictarg), "remove()"); - } else if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, arg_errmsg, TV_TRANSLATE)) { + } else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), + arg_errmsg, TV_TRANSLATE)) { bool error = false; idx = tv_get_number_chk(&argvars[1], &error); @@ -13307,8 +13328,8 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { if (argvars[2].v_type == VAR_UNKNOWN) { // Remove one item, return its value. - tv_list_remove_items(l, item, item); - *rettv = item->li_tv; + tv_list_drop_items(l, item, item); + *rettv = *TV_LIST_ITEM_TV(item); xfree(item); } else { // Remove range of items, return list with values. @@ -13320,21 +13341,17 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { int cnt = 0; - for (li = item; li != NULL; li = li->li_next) { - ++cnt; - if (li == item2) + for (li = item; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { + cnt++; + if (li == item2) { break; + } } if (li == NULL) { // Didn't find "item2" after "item". emsgf(_(e_invrange)); } else { - tv_list_remove_items(l, item, item2); - l = tv_list_alloc_ret(rettv); - l->lv_first = item; - l->lv_last = item2; - item->li_prev = NULL; - item2->li_next = NULL; - l->lv_len = cnt; + tv_list_move_items(l, item, item2, tv_list_alloc_ret(rettv, cnt), + cnt); } } } @@ -13364,7 +13381,7 @@ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr) { varnumber_T n = tv_get_number(&argvars[1]); if (argvars[0].v_type == VAR_LIST) { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, (n > 0) * n * tv_list_len(argvars[0].vval.v_list)); while (n-- > 0) { tv_list_extend(rettv->vval.v_list, argvars[0].vval.v_list, NULL); } @@ -13479,7 +13496,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr) q[-1] = NUL; q = (char *)path_tail((char_u *)p); } - if (q > p && !path_is_absolute_path((const char_u *)buf)) { + if (q > p && !path_is_absolute((const char_u *)buf)) { // Symlink is relative to directory of argument. Replace the // symlink with the resolved name in the same directory. const size_t p_len = strlen(p); @@ -13570,21 +13587,12 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *l; if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listarg), "reverse()"); - } else if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, N_("reverse() argument"), - TV_TRANSLATE)) { - listitem_T *li = l->lv_last; - l->lv_first = l->lv_last = NULL; - l->lv_len = 0; - while (li != NULL) { - listitem_T *const ni = li->li_prev; - tv_list_append(l, li); - li = ni; - } + } else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), + N_("reverse() argument"), TV_TRANSLATE)) { + tv_list_reverse(l); rettv->vval.v_list = l; rettv->v_type = VAR_LIST; - l->lv_refcount++; - l->lv_idx = l->lv_len - l->lv_idx - 1; + tv_list_ref(l); } } @@ -13767,9 +13775,8 @@ static void f_rpcnotify(typval_T *argvars, typval_T *rettv, FunPtr fptr) ADD(args, vim_to_object(tv)); } - if (!channel_send_event((uint64_t)argvars[0].vval.v_number, - tv_get_string(&argvars[1]), - args)) { + if (!rpc_send_event((uint64_t)argvars[0].vval.v_number, + tv_get_string(&argvars[1]), args)) { EMSG2(_(e_invarg2), "Channel doesn't exist"); return; } @@ -13807,7 +13814,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr) scid_T save_current_SID; uint8_t *save_sourcing_name, *save_autocmd_fname, *save_autocmd_match; linenr_T save_sourcing_lnum; - int save_autocmd_fname_full, save_autocmd_bufnr; + int save_autocmd_bufnr; void *save_funccalp; if (l_provider_call_nesting) { @@ -13818,26 +13825,22 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr) save_sourcing_lnum = sourcing_lnum; save_autocmd_fname = autocmd_fname; save_autocmd_match = autocmd_match; - save_autocmd_fname_full = autocmd_fname_full; save_autocmd_bufnr = autocmd_bufnr; save_funccalp = save_funccal(); - // + current_SID = provider_caller_scope.SID; sourcing_name = provider_caller_scope.sourcing_name; sourcing_lnum = provider_caller_scope.sourcing_lnum; autocmd_fname = provider_caller_scope.autocmd_fname; autocmd_match = provider_caller_scope.autocmd_match; - autocmd_fname_full = provider_caller_scope.autocmd_fname_full; autocmd_bufnr = provider_caller_scope.autocmd_bufnr; restore_funccal(provider_caller_scope.funccalp); } Error err = ERROR_INIT; - Object result = channel_send_call((uint64_t)argvars[0].vval.v_number, - tv_get_string(&argvars[1]), - args, - &err); + Object result = rpc_send_call((uint64_t)argvars[0].vval.v_number, + tv_get_string(&argvars[1]), args, &err); if (l_provider_call_nesting) { current_SID = save_current_SID; @@ -13845,7 +13848,6 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr) sourcing_lnum = save_sourcing_lnum; autocmd_fname = save_autocmd_fname; autocmd_match = save_autocmd_match; - autocmd_fname_full = save_autocmd_fname_full; autocmd_bufnr = save_autocmd_bufnr; restore_funccal(save_funccalp); } @@ -13885,14 +13887,17 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) int argsl = 0; if (argvars[1].v_type == VAR_LIST) { args = argvars[1].vval.v_list; - argsl = args->lv_len; + argsl = tv_list_len(args); // Assert that all list items are strings - for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { - if (arg->li_tv.v_type != VAR_STRING) { - EMSG(_(e_invarg)); + int i = 0; + TV_LIST_ITER_CONST(args, arg, { + if (TV_LIST_ITEM_TV(arg)->v_type != VAR_STRING) { + emsgf(_("E5010: List item %d of the second argument is not a string"), + i); return; } - } + i++; + }); } if (argvars[0].vval.v_string == NULL || argvars[0].vval.v_string[0] == NUL) { @@ -13910,18 +13915,21 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) int i = 1; // Copy arguments to the vector if (argsl > 0) { - for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { - argv[i++] = xstrdup(tv_get_string(&arg->li_tv)); - } + TV_LIST_ITER_CONST(args, arg, { + argv[i++] = xstrdup(tv_get_string(TV_LIST_ITEM_TV(arg))); + }); } // The last item of argv must be NULL argv[i] = NULL; - TerminalJobData *data = common_job_init(argv, CALLBACK_NONE, CALLBACK_NONE, - CALLBACK_NONE, false, true, false, - NULL); - common_job_start(data, rettv); + Channel *chan = channel_job_start(argv, CALLBACK_READER_INIT, + CALLBACK_READER_INIT, CALLBACK_NONE, + false, true, false, NULL, 0, 0, NULL, + &rettv->vval.v_number); + if (chan) { + channel_create_event(chan, NULL); + } } // "rpcstop()" function @@ -13941,10 +13949,16 @@ static void f_rpcstop(typval_T *argvars, typval_T *rettv, FunPtr fptr) } // if called with a job, stop it, else closes the channel - if (pmap_get(uint64_t)(jobs, argvars[0].vval.v_number)) { + uint64_t id = argvars[0].vval.v_number; + if (find_job(id, false)) { f_jobstop(argvars, rettv, NULL); } else { - rettv->vval.v_number = channel_close(argvars[0].vval.v_number); + const char *error; + rettv->vval.v_number = channel_close(argvars[0].vval.v_number, + kChannelPartRpc, &error); + if (!rettv->vval.v_number) { + EMSG(error); + } } } @@ -14134,7 +14148,7 @@ static void f_searchpairpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) int lnum = 0; int col = 0; - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, 2); if (searchpair_cmn(argvars, &match_pos) > 0) { lnum = match_pos.lnum; @@ -14176,6 +14190,8 @@ do_searchpair ( int nest = 1; int options = SEARCH_KEEP; proftime_T tm; + size_t pat2_len; + size_t pat3_len; /* Make 'cpoptions' empty, the 'l' flag should not be used here. */ save_cpo = p_cpo; @@ -14184,18 +14200,22 @@ do_searchpair ( /* Set the time limit, if there is one. */ tm = profile_setlimit(time_limit); - /* Make two search patterns: start/end (pat2, for in nested pairs) and - * start/middle/end (pat3, for the top pair). */ - pat2 = xmalloc(STRLEN(spat) + STRLEN(epat) + 15); - pat3 = xmalloc(STRLEN(spat) + STRLEN(mpat) + STRLEN(epat) + 23); - sprintf((char *)pat2, "\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat); - if (*mpat == NUL) + // Make two search patterns: start/end (pat2, for in nested pairs) and + // start/middle/end (pat3, for the top pair). + pat2_len = STRLEN(spat) + STRLEN(epat) + 17; + pat2 = xmalloc(pat2_len); + pat3_len = STRLEN(spat) + STRLEN(mpat) + STRLEN(epat) + 25; + pat3 = xmalloc(pat3_len); + snprintf((char *)pat2, pat2_len, "\\m\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat); + if (*mpat == NUL) { STRCPY(pat3, pat2); - else - sprintf((char *)pat3, "\\(%s\\m\\)\\|\\(%s\\m\\)\\|\\(%s\\m\\)", - spat, epat, mpat); - if (flags & SP_START) + } else { + snprintf((char *)pat3, pat3_len, + "\\m\\(%s\\m\\)\\|\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat, mpat); + } + if (flags & SP_START) { options |= SEARCH_START; + } save_cursor = curwin->w_cursor; pos = curwin->w_cursor; @@ -14296,18 +14316,14 @@ do_searchpair ( static void f_searchpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { pos_T match_pos; - int lnum = 0; - int col = 0; - int n; int flags = 0; - tv_list_alloc_ret(rettv); + const int n = search_cmn(argvars, &match_pos, &flags); - n = search_cmn(argvars, &match_pos, &flags); - if (n > 0) { - lnum = match_pos.lnum; - col = match_pos.col; - } + tv_list_alloc_ret(rettv, 2 + (!!(flags & SP_SUBPAT))); + + const int lnum = (n > 0 ? match_pos.lnum : 0); + const int col = (n > 0 ? match_pos.col : 0); tv_list_append_number(rettv->vval.v_list, (varnumber_T)lnum); tv_list_append_number(rettv->vval.v_list, (varnumber_T)col); @@ -14323,13 +14339,9 @@ static void f_serverlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) char **addrs = server_address_list(&n); // Copy addrs into a linked list. - list_T *l = tv_list_alloc_ret(rettv); + list_T *const l = tv_list_alloc_ret(rettv, n); for (size_t i = 0; i < n; i++) { - listitem_T *li = tv_list_item_alloc(); - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = (char_u *)addrs[i]; - tv_list_append(l, li); + tv_list_append_allocated_string(l, addrs[i]); } xfree(addrs); } @@ -14537,25 +14549,26 @@ static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *line = NULL; if (argvars[1].v_type == VAR_LIST) { l = argvars[1].vval.v_list; - li = l->lv_first; + li = tv_list_first(l); } else { line = tv_get_string_chk(&argvars[1]); } - /* default result is zero == OK */ + // Default result is zero == OK. for (;; ) { - if (l != NULL) { + if (argvars[1].v_type == VAR_LIST) { // List argument, get next string. if (li == NULL) { break; } - line = tv_get_string_chk(&li->li_tv); - li = li->li_next; + line = tv_get_string_chk(TV_LIST_ITEM_TV(li)); + li = TV_LIST_ITEM_NEXT(l, li); } - rettv->vval.v_number = 1; /* FAIL */ - if (line == NULL || lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1) + rettv->vval.v_number = 1; // FAIL + if (line == NULL || lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1) { break; + } /* When coming here from Insert mode, sync undo, so that this can be * undone separately from what was previously inserted. */ @@ -14627,7 +14640,8 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) return; } const char *const act = tv_get_string_chk(action_arg); - if ((*act == 'a' || *act == 'r' || *act == ' ') && act[1] == NUL) { + if ((*act == 'a' || *act == 'r' || *act == ' ' || *act == 'f') + && act[1] == NUL) { action = *act; } else { EMSG2(_(e_invact), act); @@ -14656,8 +14670,8 @@ skip_args: title = (wp ? "setloclist()" : "setqflist()"); } - list_T *l = list_arg->vval.v_list; - if (l && set_errorlist(wp, l, action, (char_u *)title, d) == OK) { + list_T *const l = list_arg->vval.v_list; + if (set_errorlist(wp, l, action, (char_u *)title, d) == OK) { rettv->vval.v_number = 0; } } @@ -14682,8 +14696,6 @@ static void f_setloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - list_T *l; - listitem_T *li; dict_T *d; list_T *s = NULL; @@ -14692,95 +14704,89 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG(_(e_listreq)); return; } - if ((l = argvars[0].vval.v_list) != NULL) { - - /* To some extent make sure that we are dealing with a list from - * "getmatches()". */ - li = l->lv_first; - while (li != NULL) { - if (li->li_tv.v_type != VAR_DICT - || (d = li->li_tv.vval.v_dict) == NULL) { - EMSG(_(e_invarg)); - return; - } - if (!(tv_dict_find(d, S_LEN("group")) != NULL - && (tv_dict_find(d, S_LEN("pattern")) != NULL - || tv_dict_find(d, S_LEN("pos1")) != NULL) - && tv_dict_find(d, S_LEN("priority")) != NULL - && tv_dict_find(d, S_LEN("id")) != NULL)) { - EMSG(_(e_invarg)); - return; - } - li = li->li_next; + list_T *const l = argvars[0].vval.v_list; + // To some extent make sure that we are dealing with a list from + // "getmatches()". + int i = 0; + TV_LIST_ITER_CONST(l, li, { + if (TV_LIST_ITEM_TV(li)->v_type != VAR_DICT + || (d = TV_LIST_ITEM_TV(li)->vval.v_dict) == NULL) { + emsgf(_("E474: List item %d is either not a dictionary " + "or an empty one"), i); + return; } + if (!(tv_dict_find(d, S_LEN("group")) != NULL + && (tv_dict_find(d, S_LEN("pattern")) != NULL + || tv_dict_find(d, S_LEN("pos1")) != NULL) + && tv_dict_find(d, S_LEN("priority")) != NULL + && tv_dict_find(d, S_LEN("id")) != NULL)) { + emsgf(_("E474: List item %d is missing one of the required keys"), i); + return; + } + i++; + }); + + clear_matches(curwin); + bool match_add_failed = false; + TV_LIST_ITER_CONST(l, li, { + int i = 0; - clear_matches(curwin); - li = l->lv_first; - bool match_add_failed = false; - while (li != NULL) { - int i = 0; + d = TV_LIST_ITEM_TV(li)->vval.v_dict; + dictitem_T *const di = tv_dict_find(d, S_LEN("pattern")); + if (di == NULL) { + if (s == NULL) { + s = tv_list_alloc(9); + } - d = li->li_tv.vval.v_dict; - dictitem_T *const di = tv_dict_find(d, S_LEN("pattern")); - if (di == NULL) { - if (s == NULL) { - s = tv_list_alloc(); - if (s == NULL) { + // match from matchaddpos() + for (i = 1; i < 9; i++) { + char buf[5]; + snprintf(buf, sizeof(buf), "pos%d", i); + dictitem_T *const pos_di = tv_dict_find(d, buf, -1); + if (pos_di != NULL) { + if (pos_di->di_tv.v_type != VAR_LIST) { return; } - } - - // match from matchaddpos() - for (i = 1; i < 9; i++) { - char buf[5]; - snprintf(buf, sizeof(buf), "pos%d", i); - dictitem_T *const pos_di = tv_dict_find(d, buf, -1); - if (pos_di != NULL) { - if (pos_di->di_tv.v_type != VAR_LIST) { - return; - } - tv_list_append_tv(s, &pos_di->di_tv); - s->lv_refcount++; - } else { - break; - } + tv_list_append_tv(s, &pos_di->di_tv); + tv_list_ref(s); + } else { + break; } } + } - // Note: there are three number buffers involved: - // - group_buf below. - // - numbuf in tv_dict_get_string(). - // - mybuf in tv_get_string(). - // - // If you change this code make sure that buffers will not get - // accidentally reused. - char group_buf[NUMBUFLEN]; - const char *const group = tv_dict_get_string_buf(d, "group", group_buf); - const int priority = (int)tv_dict_get_number(d, "priority"); - const int id = (int)tv_dict_get_number(d, "id"); - dictitem_T *const conceal_di = tv_dict_find(d, S_LEN("conceal")); - const char *const conceal = (conceal_di != NULL - ? tv_get_string(&conceal_di->di_tv) - : NULL); - if (i == 0) { - if (match_add(curwin, group, - tv_dict_get_string(d, "pattern", false), - priority, id, NULL, conceal) != id) { - match_add_failed = true; - } - } else { - if (match_add(curwin, group, NULL, priority, id, s, conceal) != id) { - match_add_failed = true; - } - tv_list_unref(s); - s = NULL; + // Note: there are three number buffers involved: + // - group_buf below. + // - numbuf in tv_dict_get_string(). + // - mybuf in tv_get_string(). + // + // If you change this code make sure that buffers will not get + // accidentally reused. + char group_buf[NUMBUFLEN]; + const char *const group = tv_dict_get_string_buf(d, "group", group_buf); + const int priority = (int)tv_dict_get_number(d, "priority"); + const int id = (int)tv_dict_get_number(d, "id"); + dictitem_T *const conceal_di = tv_dict_find(d, S_LEN("conceal")); + const char *const conceal = (conceal_di != NULL + ? tv_get_string(&conceal_di->di_tv) + : NULL); + if (i == 0) { + if (match_add(curwin, group, + tv_dict_get_string(d, "pattern", false), + priority, id, NULL, conceal) != id) { + match_add_failed = true; } - li = li->li_next; - } - if (!match_add_failed) { - rettv->vval.v_number = 0; + } else { + if (match_add(curwin, group, NULL, priority, id, s, conceal) != id) { + match_add_failed = true; + } + tv_list_unref(s); + s = NULL; } + }); + if (!match_add_failed) { + rettv->vval.v_number = 0; } } @@ -14897,7 +14903,7 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[1].v_type == VAR_LIST) { list_T *ll = argvars[1].vval.v_list; // If the list is NULL handle like an empty list. - int len = ll == NULL ? 0 : ll->lv_len; + const int len = tv_list_len(ll); // First half: use for pointers to result lines; second half: use for // pointers to allocated copies. @@ -14906,11 +14912,9 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) char **allocval = lstval + len + 2; char **curallocval = allocval; - for (listitem_T *li = ll == NULL ? NULL : ll->lv_first; - li != NULL; - li = li->li_next) { + TV_LIST_ITER_CONST(ll, li, { char buf[NUMBUFLEN]; - *curval = tv_get_string_buf_chk(&li->li_tv, buf); + *curval = tv_get_string_buf_chk(TV_LIST_ITEM_TV(li), buf); if (*curval == NULL) { goto free_lstval; } @@ -14922,7 +14926,7 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr) curallocval++; } curval++; - } + }); *curval++ = NULL; write_reg_contents_lst(regname, (char_u **)lstval, append, yank_type, @@ -15118,18 +15122,22 @@ static void f_sockconnect(typval_T *argvars, typval_T *rettv, FunPtr fptr) } bool rpc = false; + CallbackReader on_data = CALLBACK_READER_INIT; if (argvars[2].v_type == VAR_DICT) { dict_T *opts = argvars[2].vval.v_dict; rpc = tv_dict_get_number(opts, "rpc") != 0; - } - if (!rpc) { - EMSG2(_(e_invarg2), "rpc option must be true"); - return; + if (!tv_dict_get_callback(opts, S_LEN("on_data"), &on_data.cb)) { + return; + } + on_data.buffered = tv_dict_get_number(opts, "data_buffered"); + if (on_data.buffered && on_data.cb.type == kCallbackNone) { + on_data.self = opts; + } } const char *error = NULL; - uint64_t id = channel_connect(tcp, address, 50, &error); + uint64_t id = channel_connect(tcp, address, rpc, on_data, 50, &error); if (error) { EMSG2(_("connection failed: %s"), error); @@ -15139,12 +15147,6 @@ static void f_sockconnect(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->v_type = VAR_NUMBER; } -/// struct used in the array that's given to qsort() -typedef struct { - listitem_T *item; - int idx; -} sortItem_T; - /// struct storing information about current sort typedef struct { int item_compare_ic; @@ -15165,11 +15167,11 @@ static sortinfo_T *sortinfo = NULL; */ static int item_compare(const void *s1, const void *s2, bool keep_zero) { - sortItem_T *const si1 = (sortItem_T *)s1; - sortItem_T *const si2 = (sortItem_T *)s2; + ListSortItem *const si1 = (ListSortItem *)s1; + ListSortItem *const si2 = (ListSortItem *)s2; - typval_T *const tv1 = &si1->item->li_tv; - typval_T *const tv2 = &si2->item->li_tv; + typval_T *const tv1 = TV_LIST_ITEM_TV(si1->item); + typval_T *const tv2 = TV_LIST_ITEM_TV(si2->item); int res; @@ -15241,6 +15243,8 @@ item_compare_end: // When the result would be zero, compare the item indexes. Makes the // sort stable. if (res == 0 && !keep_zero) { + // WARNING: When using uniq si1 and si2 are actually listitem_T **, no + // indexes are there. res = si1->idx > si2->idx ? 1 : -1; } return res; @@ -15258,7 +15262,7 @@ static int item_compare_not_keeping_zero(const void *s1, const void *s2) static int item_compare2(const void *s1, const void *s2, bool keep_zero) { - sortItem_T *si1, *si2; + ListSortItem *si1, *si2; int res; typval_T rettv; typval_T argv[3]; @@ -15271,8 +15275,8 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) return 0; } - si1 = (sortItem_T *)s1; - si2 = (sortItem_T *)s2; + si1 = (ListSortItem *)s1; + si2 = (ListSortItem *)s2; if (partial == NULL) { func_name = sortinfo->item_compare_func; @@ -15282,8 +15286,8 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) // Copy the values. This is needed to be able to set v_lock to VAR_FIXED // in the copy without changing the original list items. - tv_copy(&si1->item->li_tv, &argv[0]); - tv_copy(&si2->item->li_tv, &argv[1]); + tv_copy(TV_LIST_ITEM_TV(si1->item), &argv[0]); + tv_copy(TV_LIST_ITEM_TV(si2->item), &argv[1]); rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this res = call_func((const char_u *)func_name, @@ -15306,6 +15310,8 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) // When the result would be zero, compare the pointers themselves. Makes // the sort stable. if (res == 0 && !keep_zero) { + // WARNING: When using uniq si1 and si2 are actually listitem_T **, no + // indexes are there. res = si1->idx > si2->idx ? 1 : -1; } @@ -15327,9 +15333,7 @@ static int item_compare2_not_keeping_zero(const void *s1, const void *s2) */ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) { - list_T *l; - listitem_T *li; - sortItem_T *ptrs; + ListSortItem *ptrs; long len; long i; @@ -15346,13 +15350,13 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listarg), sort ? "sort()" : "uniq()"); } else { - l = argvars[0].vval.v_list; - if (l == NULL || tv_check_lock(l->lv_lock, arg_errmsg, TV_TRANSLATE)) { - goto theend; + list_T *const l = argvars[0].vval.v_list; + if (tv_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) { + goto theend; } rettv->vval.v_list = l; rettv->v_type = VAR_LIST; - ++l->lv_refcount; + tv_list_ref(l); len = tv_list_len(l); if (len <= 1) { @@ -15418,80 +15422,45 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) } } - /* Make an array with each entry pointing to an item in the List. */ - ptrs = xmalloc((size_t)(len * sizeof (sortItem_T))); + // Make an array with each entry pointing to an item in the List. + ptrs = xmalloc((size_t)(len * sizeof(ListSortItem))); - i = 0; if (sort) { - // sort(): ptrs will be the list to sort. - for (li = l->lv_first; li != NULL; li = li->li_next) { - ptrs[i].item = li; - ptrs[i].idx = i; - i++; - } - info.item_compare_func_err = false; - // Test the compare function. - if ((info.item_compare_func != NULL - || info.item_compare_partial != NULL) - && item_compare2_not_keeping_zero(&ptrs[0], &ptrs[1]) - == ITEM_COMPARE_FAIL) { + tv_list_item_sort(l, ptrs, + ((info.item_compare_func == NULL + && info.item_compare_partial == NULL) + ? item_compare_not_keeping_zero + : item_compare2_not_keeping_zero), + &info.item_compare_func_err); + if (info.item_compare_func_err) { EMSG(_("E702: Sort compare function failed")); - } else { - // Sort the array with item pointers. - qsort(ptrs, (size_t)len, sizeof (sortItem_T), - (info.item_compare_func == NULL - && info.item_compare_partial == NULL ? - item_compare_not_keeping_zero : - item_compare2_not_keeping_zero)); - - if (!info.item_compare_func_err) { - // Clear the list and append the items in the sorted order. - l->lv_first = NULL; - l->lv_last = NULL; - l->lv_idx_item = NULL; - l->lv_len = 0; - - for (i = 0; i < len; i++) { - tv_list_append(l, ptrs[i].item); - } - } } } else { - int (*item_compare_func_ptr)(const void *, const void *); + ListSorter item_compare_func_ptr; // f_uniq(): ptrs will be a stack of items to remove. info.item_compare_func_err = false; if (info.item_compare_func != NULL || info.item_compare_partial != NULL) { - item_compare_func_ptr = item_compare2_keeping_zero; + item_compare_func_ptr = item_compare2_keeping_zero; } else { - item_compare_func_ptr = item_compare_keeping_zero; + item_compare_func_ptr = item_compare_keeping_zero; } - for (li = l->lv_first; li != NULL && li->li_next != NULL; li = li->li_next) { - if (item_compare_func_ptr(&li, &li->li_next) == 0) { - ptrs[i++].item = li; - } - if (info.item_compare_func_err) { - EMSG(_("E882: Uniq compare function failed")); - break; - } - } - - if (!info.item_compare_func_err) { - while (--i >= 0) { - assert(ptrs[i].item->li_next); - li = ptrs[i].item->li_next; - ptrs[i].item->li_next = li->li_next; - if (li->li_next != NULL) { - li->li_next->li_prev = ptrs[i].item; - } else { - l->lv_last = ptrs[i].item; + int idx = 0; + for (listitem_T *li = TV_LIST_ITEM_NEXT(l, tv_list_first(l)) + ; li != NULL;) { + listitem_T *const prev_li = TV_LIST_ITEM_PREV(l, li); + if (item_compare_func_ptr(&prev_li, &li) == 0) { + if (info.item_compare_func_err) { + EMSG(_("E882: Uniq compare function failed")); + break; } - tv_list_watch_fix(l, li); - tv_list_item_free(li); - l->lv_len--; + li = tv_list_item_remove(l, li); + } else { + idx++; + li = TV_LIST_ITEM_NEXT(l, li); } } } @@ -15509,6 +15478,39 @@ static void f_sort(typval_T *argvars, typval_T *rettv, FunPtr fptr) do_sort_uniq(argvars, rettv, true); } +/// "stdioopen()" function +static void f_stdioopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + if (argvars[0].v_type != VAR_DICT) { + EMSG(_(e_invarg)); + return; + } + + + bool rpc = false; + CallbackReader on_stdin = CALLBACK_READER_INIT; + dict_T *opts = argvars[0].vval.v_dict; + rpc = tv_dict_get_number(opts, "rpc") != 0; + + if (!tv_dict_get_callback(opts, S_LEN("on_stdin"), &on_stdin.cb)) { + return; + } + on_stdin.buffered = tv_dict_get_number(opts, "stdin_buffered"); + if (on_stdin.buffered && on_stdin.cb.type == kCallbackNone) { + on_stdin.self = opts; + } + + const char *error; + uint64_t id = channel_from_stdio(rpc, on_stdin, &error); + if (!id) { + EMSG2(e_stdiochan2, error); + } + + + rettv->vval.v_number = (varnumber_T)id; + rettv->v_type = VAR_NUMBER; +} + /// "uniq({list})" function static void f_uniq(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -15549,13 +15551,12 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) hlf_T attr = HLF_COUNT; size_t len = 0; - tv_list_alloc_ret(rettv); - if (argvars[0].v_type == VAR_UNKNOWN) { // Find the start and length of the badly spelled word. len = spell_move_to(curwin, FORWARD, true, true, &attr); if (len != 0) { word = (char *)get_cursor_pos_ptr(); + curwin->w_set_curswant = true; } } else if (curwin->w_p_spell && *curbuf->b_s.b_p_spl != NUL) { const char *str = tv_get_string_chk(&argvars[0]); @@ -15575,6 +15576,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr) } assert(len <= INT_MAX); + tv_list_alloc_ret(rettv, 2); tv_list_append_string(rettv->vval.v_list, word, len); tv_list_append_string(rettv->vval.v_list, (attr == HLF_SPB ? "bad" @@ -15591,41 +15593,36 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr) { bool typeerr = false; int maxcount; - garray_T ga; - listitem_T *li; + garray_T ga = GA_EMPTY_INIT_VALUE; bool need_capital = false; - tv_list_alloc_ret(rettv); - if (curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) { const char *const str = tv_get_string(&argvars[0]); if (argvars[1].v_type != VAR_UNKNOWN) { maxcount = tv_get_number_chk(&argvars[1], &typeerr); if (maxcount <= 0) { - return; + goto f_spellsuggest_return; } if (argvars[2].v_type != VAR_UNKNOWN) { need_capital = tv_get_number_chk(&argvars[2], &typeerr); if (typeerr) { - return; + goto f_spellsuggest_return; } } - } else + } else { maxcount = 25; + } spell_suggest_list(&ga, (char_u *)str, maxcount, need_capital, false); + } - for (int i = 0; i < ga.ga_len; i++) { - char *p = ((char **)ga.ga_data)[i]; - - li = tv_list_item_alloc(); - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = (char_u *)p; - tv_list_append(rettv->vval.v_list, li); - } - ga_clear(&ga); +f_spellsuggest_return: + tv_list_alloc_ret(rettv, (ptrdiff_t)ga.ga_len); + for (int i = 0; i < ga.ga_len; i++) { + char *const p = ((char **)ga.ga_data)[i]; + tv_list_append_allocated_string(rettv->vval.v_list, p); } + ga_clear(&ga); } static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) @@ -15657,10 +15654,11 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) pat = "[\\x01- ]\\+"; } - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenMayKnow); - if (typeerr) + if (typeerr) { return; + } regmatch.regprog = vim_regcomp((char_u *)pat, RE_MAGIC + RE_STRING); if (regmatch.regprog != NULL) { @@ -15677,7 +15675,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr) } else { end = str + strlen(str); } - if (keepempty || end > str || (rettv->vval.v_list->lv_len > 0 + if (keepempty || end > str || (tv_list_len(rettv->vval.v_list) > 0 && *str != NUL && match && end < (const char *)regmatch.endp[0])) { @@ -16223,7 +16221,7 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr) break; } case 'n': { // name - p = get_highlight_name(NULL, id - 1); + p = get_highlight_name_ext(NULL, id - 1, false); break; } case 'r': { // reverse @@ -16287,7 +16285,6 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) memset(str, NUL, sizeof(str)); - tv_list_alloc_ret(rettv); if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0 && (size_t)col <= STRLEN(ml_get(lnum)) && curwin->w_p_cole > 0) { (void)syn_get_id(curwin, lnum, col, false, NULL, false); @@ -16296,18 +16293,16 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr) // get the conceal character if ((syntax_flags & HL_CONCEAL) && curwin->w_p_cole < 3) { cchar = syn_get_sub_char(); - if (cchar == NUL && curwin->w_p_cole == 1 && lcs_conceal != NUL) { - cchar = lcs_conceal; + if (cchar == NUL && curwin->w_p_cole == 1) { + cchar = (lcs_conceal == NUL) ? ' ' : lcs_conceal; } if (cchar != NUL) { - if (has_mbyte) - (*mb_char2bytes)(cchar, str); - else - str[0] = cchar; + utf_char2bytes(cchar, str); } } } + tv_list_alloc_ret(rettv, 3); tv_list_append_number(rettv->vval.v_list, (syntax_flags & HL_CONCEAL) != 0); // -1 to auto-determine strlen tv_list_append_string(rettv->vval.v_list, (const char *)str, -1); @@ -16330,7 +16325,7 @@ static void f_synstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) && lnum <= curbuf->b_ml.ml_line_count && col >= 0 && (size_t)col <= STRLEN(ml_get(lnum))) { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenMayKnow); (void)syn_get_id(curwin, lnum, col, false, NULL, true); int id; @@ -16346,7 +16341,7 @@ static list_T *string_to_list(const char *str, size_t len, const bool keepempty) if (!keepempty && str[len - 1] == NL) { len--; } - list_T *const list = tv_list_alloc(); + list_T *const list = tv_list_alloc(kListLenMayKnow); encode_list_write(list, str, len); return list; } @@ -16392,7 +16387,7 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, if (res == NULL) { if (retlist) { // return an empty list when there's no output - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, 0); } else { rettv->vval.v_string = (char_u *) xstrdup(""); } @@ -16405,7 +16400,7 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, keepempty = tv_get_number(&argvars[2]); } rettv->vval.v_list = string_to_list(res, nread, (bool)keepempty); - rettv->vval.v_list->lv_refcount++; + tv_list_ref(rettv->vval.v_list); rettv->v_type = VAR_LIST; xfree(res); @@ -16458,7 +16453,7 @@ static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } if (wp != NULL) { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenMayKnow); while (wp != NULL) { tv_list_append_number(rettv->vval.v_list, wp->w_buffer->b_fnum); wp = wp->w_next; @@ -16556,7 +16551,7 @@ static void f_tagfiles(typval_T *argvars, typval_T *rettv, FunPtr fptr) char *fname; tagname_T tn; - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenUnknown); fname = xmalloc(MAXPATHL); bool first = true; @@ -16585,8 +16580,8 @@ static void f_taglist(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[1].v_type != VAR_UNKNOWN) { fname = tv_get_string(&argvars[1]); } - (void)get_tags(tv_list_alloc_ret(rettv), (char_u *)tag_pattern, - (char_u *)fname); + (void)get_tags(tv_list_alloc_ret(rettv, kListLenUnknown), + (char_u *)tag_pattern, (char_u *)fname); } /* @@ -16625,8 +16620,9 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - Callback on_stdout = CALLBACK_NONE, on_stderr = CALLBACK_NONE, - on_exit = CALLBACK_NONE; + CallbackReader on_stdout = CALLBACK_READER_INIT, + on_stderr = CALLBACK_READER_INIT; + Callback on_exit = CALLBACK_NONE; dict_T *job_opts = NULL; const char *cwd = "."; if (argvars[1].v_type == VAR_DICT) { @@ -16649,23 +16645,17 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit, - true, false, false, cwd); - data->proc.pty.width = curwin->w_width; - data->proc.pty.height = curwin->w_height; - data->proc.pty.term_name = xstrdup("xterm-256color"); - if (!common_job_start(data, rettv)) { + uint16_t term_width = MAX(0, curwin->w_width - win_col_off(curwin)); + Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, + true, false, false, cwd, + term_width, curwin->w_height, + xstrdup("xterm-256color"), + &rettv->vval.v_number); + if (rettv->vval.v_number <= 0) { return; } - TerminalOptions topts; - topts.data = data; - topts.width = curwin->w_width; - topts.height = curwin->w_height; - topts.write_cb = term_write; - topts.resize_cb = term_resize; - topts.close_cb = term_close; - int pid = data->proc.pty.process.pid; + int pid = chan->stream.pty.process.pid; char buf[1024]; // format the title with the pid to conform with the term:// URI @@ -16676,18 +16666,16 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) (void)setfname(curbuf, (char_u *)buf, NULL, true); // Save the job id and pid in b:terminal_job_{id,pid} Error err = ERROR_INIT; + // deprecated: use 'channel' buffer option dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_id"), - INTEGER_OBJ(rettv->vval.v_number), false, false, &err); + INTEGER_OBJ(chan->id), false, false, &err); api_clear_error(&err); dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_pid"), INTEGER_OBJ(pid), false, false, &err); api_clear_error(&err); - Terminal *term = terminal_open(topts); - data->term = term; - data->refcount++; - - return; + channel_terminal_open(chan); + channel_create_event(chan, NULL); } // "test_garbagecollect_now()" function @@ -16699,6 +16687,18 @@ static void f_test_garbagecollect_now(typval_T *argvars, garbage_collect(true); } +// "test_write_list_log()" function +static void f_test_write_list_log(typval_T *const argvars, + typval_T *const rettv, + FunPtr fptr) +{ + const char *const fname = tv_get_string_chk(&argvars[0]); + if (fname == NULL) { + return; + } + list_write_log(fname); +} + bool callback_from_typval(Callback *const callback, typval_T *const arg) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { @@ -16720,30 +16720,6 @@ bool callback_from_typval(Callback *const callback, typval_T *const arg) return true; } -/// Unref/free callback -void callback_free(Callback *const callback) - FUNC_ATTR_NONNULL_ALL -{ - switch (callback->type) { - case kCallbackFuncref: { - func_unref(callback->data.funcref); - xfree(callback->data.funcref); - break; - } - case kCallbackPartial: { - partial_unref(callback->data.partial); - break; - } - case kCallbackNone: { - break; - } - default: { - abort(); - } - } - callback->type = kCallbackNone; -} - bool callback_call(Callback *const callback, const int argcount_in, typval_T *const argvars_in, typval_T *const rettv) FUNC_ATTR_NONNULL_ALL @@ -16798,6 +16774,23 @@ static bool set_ref_in_callback(Callback *callback, int copyID, return false; } +static bool set_ref_in_callback_reader(CallbackReader *reader, int copyID, + ht_stack_T **ht_stack, + list_stack_T **list_stack) +{ + if (set_ref_in_callback(&reader->cb, copyID, ht_stack, list_stack)) { + return true; + } + + if (reader->self) { + typval_T tv; + tv.v_type = VAR_DICT; + tv.vval.v_dict = reader->self; + return set_ref_in_item(&tv, copyID, ht_stack, list_stack); + } + return false; +} + static void add_timer_info(typval_T *rettv, timer_T *timer) { list_T *list = rettv->vval.v_list; @@ -16841,7 +16834,9 @@ static void add_timer_info_all(typval_T *rettv) /// "timer_info([timer])" function static void f_timer_info(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, (argvars[0].v_type != VAR_UNKNOWN + ? 1 + : timers->table->n_occupied)); if (argvars[0].v_type != VAR_UNKNOWN) { if (argvars[0].v_type != VAR_NUMBER) { EMSG(_(e_number_exp)); @@ -17165,7 +17160,7 @@ static void f_type(typval_T *argvars, typval_T *rettv, FunPtr fptr) break; } case VAR_UNKNOWN: { - EMSG2(_(e_intern2), "f_type(UNKNOWN)"); + internal_error("f_type(UNKNOWN)"); break; } } @@ -17201,7 +17196,6 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_dict_alloc_ret(rettv); dict_T *dict = rettv->vval.v_dict; - list_T *list; tv_dict_add_nr(dict, S_LEN("synced"), (varnumber_T)curbuf->b_u_synced); tv_dict_add_nr(dict, S_LEN("seq_last"), (varnumber_T)curbuf->b_u_seq_last); @@ -17211,9 +17205,7 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_dict_add_nr(dict, S_LEN("time_cur"), (varnumber_T)curbuf->b_u_time_cur); tv_dict_add_nr(dict, S_LEN("save_cur"), (varnumber_T)curbuf->b_u_save_nr_cur); - list = tv_list_alloc(); - u_eval_tree(curbuf->b_u_oldhead, list); - tv_dict_add_list(dict, S_LEN("entries"), list); + tv_dict_add_list(dict, S_LEN("entries"), u_eval_tree(curbuf->b_u_oldhead)); } /* @@ -17272,7 +17264,7 @@ static void f_wildmenumode(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "win_findbuf()" function static void f_win_findbuf(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - tv_list_alloc_ret(rettv); + tv_list_alloc_ret(rettv, kListLenMayKnow); win_findbuf(argvars, rettv->vval.v_list); } @@ -17291,8 +17283,7 @@ static void f_win_gotoid(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// "win_id2tabwin()" function static void f_win_id2tabwin(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - tv_list_alloc_ret(rettv); - win_id2tabwin(argvars, rettv->vval.v_list); + win_id2tabwin(argvars, rettv); } /// "win_id2win()" function @@ -17454,7 +17445,7 @@ static void f_winsaveview(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_dict_add_nr(dict, S_LEN("skipcol"), (varnumber_T)curwin->w_skipcol); } -/// Writes list of strings to file +/// Write "list" of strings to file "fd". /// /// @param fp File to write to. /// @param[in] list List to write. @@ -17463,10 +17454,11 @@ static void f_winsaveview(typval_T *argvars, typval_T *rettv, FunPtr fptr) /// @return true in case of success, false otherwise. static bool write_list(FileDescriptor *const fp, const list_T *const list, const bool binary) + FUNC_ATTR_NONNULL_ARG(1) { int error = 0; - for (const listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - const char *const s = tv_get_string_chk(&li->li_tv); + TV_LIST_ITER_CONST(list, li, { + const char *const s = tv_get_string_chk(TV_LIST_ITEM_TV(li)); if (s == NULL) { return false; } @@ -17493,14 +17485,14 @@ static bool write_list(FileDescriptor *const fp, const list_T *const list, } } } - if (!binary || li->li_next != NULL) { + if (!binary || TV_LIST_ITEM_NEXT(list, li) != NULL) { const ptrdiff_t written = file_write(fp, "\n", 1); if (written < 0) { error = (int)written; goto write_list_error; } } - } + }); if ((error = file_flush(fp)) != 0) { goto write_list_error; } @@ -17510,56 +17502,29 @@ write_list_error: return false; } -/// Initializes a static list with 10 items. -void init_static_list(staticList10_T *sl) -{ - list_T *l = &sl->sl_list; - - memset(sl, 0, sizeof(staticList10_T)); - l->lv_first = &sl->sl_items[0]; - l->lv_last = &sl->sl_items[9]; - l->lv_refcount = DO_NOT_FREE_CNT; - l->lv_lock = VAR_FIXED; - sl->sl_list.lv_len = 10; - - for (int i = 0; i < 10; i++) { - listitem_T *li = &sl->sl_items[i]; - - if (i == 0) { - li->li_prev = NULL; - } else { - li->li_prev = li - 1; - } - if (i == 9) { - li->li_next = NULL; - } else { - li->li_next = li + 1; - } - } -} - /// Saves a typval_T as a string. /// -/// For lists, replaces NLs with NUL and separates items with NLs. +/// For lists or buffers, replaces NLs with NUL and separates items with NLs. /// -/// @param[in] tv A value to store as a string. -/// @param[out] len The length of the resulting string or -1 on error. +/// @param[in] tv Value to store as a string. +/// @param[out] len Length of the resulting string or -1 on error. /// @param[in] endnl If true, the output will end in a newline (if a list). /// @returns an allocated string if `tv` represents a VimL string, list, or /// number; NULL otherwise. static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { + *len = 0; if (tv->v_type == VAR_UNKNOWN) { - *len = 0; return NULL; } - // For types other than list, let tv_get_string_buf_chk() get the value or + // For other types, let tv_get_string_buf_chk() get the value or // print an error. - if (tv->v_type != VAR_LIST) { + if (tv->v_type != VAR_LIST && tv->v_type != VAR_NUMBER) { const char *ret = tv_get_string_chk(tv); - if (ret && (*len = strlen(ret))) { + if (ret) { + *len = strlen(ret); return xmemdupz(ret, (size_t)(*len)); } else { *len = -1; @@ -17567,12 +17532,44 @@ static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) } } + if (tv->v_type == VAR_NUMBER) { // Treat number as a buffer-id. + buf_T *buf = buflist_findnr(tv->vval.v_number); + if (buf) { + for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { + for (char_u *p = ml_get_buf(buf, lnum, false); *p != NUL; p++) { + *len += 1; + } + *len += 1; + } + } else { + EMSGN(_(e_nobufnr), tv->vval.v_number); + *len = -1; + return NULL; + } + + if (*len == 0) { + return NULL; + } + + char *ret = xmalloc(*len + 1); + char *end = ret; + for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { + for (char_u *p = ml_get_buf(buf, lnum, false); *p != NUL; p++) { + *end++ = (*p == '\n') ? NUL : *p; + } + *end++ = '\n'; + } + *end = NUL; + *len = end - ret; + return ret; + } + + assert(tv->v_type == VAR_LIST); // Pre-calculate the resulting length. - *len = 0; list_T *list = tv->vval.v_list; - for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - *len += strlen(tv_get_string(&li->li_tv)) + 1; - } + TV_LIST_ITER_CONST(list, li, { + *len += strlen(tv_get_string(TV_LIST_ITEM_TV(li))) + 1; + }); if (*len == 0) { return NULL; @@ -17580,14 +17577,14 @@ static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) char *ret = xmalloc(*len + endnl); char *end = ret; - for (listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { - for (const char *s = tv_get_string(&li->li_tv); *s != NUL; s++) { + TV_LIST_ITER_CONST(list, li, { + for (const char *s = tv_get_string(TV_LIST_ITEM_TV(li)); *s != NUL; s++) { *end++ = (*s == '\n') ? NUL : *s; } - if (endnl || li->li_next != NULL) { + if (endnl || TV_LIST_ITEM_NEXT(list, li) != NULL) { *end++ = '\n'; } - } + }); *end = NUL; *len = end - ret; return ret; @@ -17627,9 +17624,6 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr) EMSG2(_(e_listarg), "writefile()"); return; } - if (argvars[0].vval.v_list == NULL) { - return; - } bool binary = false; bool append = false; @@ -17734,9 +17728,9 @@ pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum, // We accept "$" for the column number: last column. li = tv_list_find(l, 1L); - if (li != NULL && li->li_tv.v_type == VAR_STRING - && li->li_tv.vval.v_string != NULL - && STRCMP(li->li_tv.vval.v_string, "$") == 0) { + if (li != NULL && TV_LIST_ITEM_TV(li)->v_type == VAR_STRING + && TV_LIST_ITEM_TV(li)->vval.v_string != NULL + && STRCMP(TV_LIST_ITEM_TV(li)->vval.v_string, "$") == 0) { pos.col = len + 1; } @@ -17813,17 +17807,18 @@ pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum, */ static int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp) { - list_T *l = arg->vval.v_list; + list_T *l; long i = 0; long n; - /* List must be: [fnum, lnum, col, coladd, curswant], where "fnum" is only - * there when "fnump" isn't NULL; "coladd" and "curswant" are optional. */ + // List must be: [fnum, lnum, col, coladd, curswant], where "fnum" is only + // there when "fnump" isn't NULL; "coladd" and "curswant" are optional. if (arg->v_type != VAR_LIST - || l == NULL - || l->lv_len < (fnump == NULL ? 2 : 3) - || l->lv_len > (fnump == NULL ? 4 : 5)) + || (l = arg->vval.v_list) == NULL + || tv_list_len(l) < (fnump == NULL ? 2 : 3) + || tv_list_len(l) > (fnump == NULL ? 4 : 5)) { return FAIL; + } if (fnump != NULL) { n = tv_list_find_nr(l, i++, NULL); // fnum @@ -18248,7 +18243,7 @@ void set_vim_var_list(const VimVarIndex idx, list_T *const val) vimvars[idx].vv_type = VAR_LIST; vimvars[idx].vv_list = val; if (val != NULL) { - val->lv_refcount++; + tv_list_ref(val); } } @@ -18548,47 +18543,39 @@ void set_selfdict(typval_T *rettv, dict_T *selfdict) // Turn "dict.Func" into a partial for "Func" with "dict". if (fp != NULL && (fp->uf_flags & FC_DICT)) { partial_T *pt = (partial_T *)xcalloc(1, sizeof(partial_T)); - - if (pt != NULL) { - pt->pt_refcount = 1; - pt->pt_dict = selfdict; - (selfdict->dv_refcount)++; - pt->pt_auto = true; - if (rettv->v_type == VAR_FUNC || rettv->v_type == VAR_STRING) { - // Just a function: Take over the function name and use selfdict. - pt->pt_name = rettv->vval.v_string; + pt->pt_refcount = 1; + pt->pt_dict = selfdict; + (selfdict->dv_refcount)++; + pt->pt_auto = true; + if (rettv->v_type == VAR_FUNC || rettv->v_type == VAR_STRING) { + // Just a function: Take over the function name and use selfdict. + pt->pt_name = rettv->vval.v_string; + } else { + partial_T *ret_pt = rettv->vval.v_partial; + int i; + + // Partial: copy the function name, use selfdict and copy + // args. Can't take over name or args, the partial might + // be referenced elsewhere. + if (ret_pt->pt_name != NULL) { + pt->pt_name = vim_strsave(ret_pt->pt_name); + func_ref(pt->pt_name); } else { - partial_T *ret_pt = rettv->vval.v_partial; - int i; - - // Partial: copy the function name, use selfdict and copy - // args. Can't take over name or args, the partial might - // be referenced elsewhere. - if (ret_pt->pt_name != NULL) { - pt->pt_name = vim_strsave(ret_pt->pt_name); - func_ref(pt->pt_name); - } else { - pt->pt_func = ret_pt->pt_func; - func_ptr_ref(pt->pt_func); - } - if (ret_pt->pt_argc > 0) { - size_t arg_size = sizeof(typval_T) * ret_pt->pt_argc; - pt->pt_argv = (typval_T *)xmalloc(arg_size); - if (pt->pt_argv == NULL) { - // out of memory: drop the arguments - pt->pt_argc = 0; - } else { - pt->pt_argc = ret_pt->pt_argc; - for (i = 0; i < pt->pt_argc; i++) { - tv_copy(&ret_pt->pt_argv[i], &pt->pt_argv[i]); - } - } + pt->pt_func = ret_pt->pt_func; + func_ptr_ref(pt->pt_func); + } + if (ret_pt->pt_argc > 0) { + size_t arg_size = sizeof(typval_T) * ret_pt->pt_argc; + pt->pt_argv = (typval_T *)xmalloc(arg_size); + pt->pt_argc = ret_pt->pt_argc; + for (i = 0; i < pt->pt_argc; i++) { + tv_copy(&ret_pt->pt_argv[i], &pt->pt_argv[i]); } - partial_unref(ret_pt); } - rettv->v_type = VAR_PARTIAL; - rettv->vval.v_partial = pt; + partial_unref(ret_pt); } + rettv->v_type = VAR_PARTIAL; + rettv->vval.v_partial = pt; } } @@ -18652,7 +18639,7 @@ static dictitem_T *find_var_in_ht(hashtab_T *const ht, case 'l': return (current_funccal == NULL ? NULL : (dictitem_T *)¤t_funccal->l_vars_var); case 'a': return (current_funccal == NULL - ? NULL : (dictitem_T *)¤t_funccal->l_avars_var); + ? NULL : (dictitem_T *)&get_funccal()->l_avars_var); } return NULL; } @@ -19052,7 +19039,7 @@ static void set_var(const char *name, const size_t name_len, typval_T *const tv, } return; } else if (v->di_tv.v_type != tv->v_type) { - EMSG2(_(e_intern2), "set_var()"); + internal_error("set_var()"); } } @@ -19072,6 +19059,9 @@ static void set_var(const char *name, const size_t name_len, typval_T *const tv, return; } + // Make sure dict is valid + assert(dict != NULL); + v = xmalloc(sizeof(dictitem_T) + strlen(varname)); STRCPY(v->di_key, varname); if (tv_dict_add(dict, v) == FAIL) { @@ -19289,12 +19279,12 @@ int var_item_copy(const vimconv_T *const conv, case VAR_LIST: to->v_type = VAR_LIST; to->v_lock = 0; - if (from->vval.v_list == NULL) + if (from->vval.v_list == NULL) { to->vval.v_list = NULL; - else if (copyID != 0 && from->vval.v_list->lv_copyID == copyID) { - /* use the copy made earlier */ - to->vval.v_list = from->vval.v_list->lv_copylist; - ++to->vval.v_list->lv_refcount; + } else if (copyID != 0 && tv_list_copyid(from->vval.v_list) == copyID) { + // Use the copy made earlier. + to->vval.v_list = tv_list_latest_copy(from->vval.v_list); + tv_list_ref(to->vval.v_list); } else { to->vval.v_list = tv_list_copy(conv, from->vval.v_list, deep, copyID); } @@ -19319,7 +19309,7 @@ int var_item_copy(const vimconv_T *const conv, } break; case VAR_UNKNOWN: - EMSG2(_(e_intern2), "var_item_copy(UNKNOWN)"); + internal_error("var_item_copy(UNKNOWN)"); ret = FAIL; } --recurse; @@ -19387,14 +19377,10 @@ void ex_echo(exarg_T *eap) } msg_putchar_attr((uint8_t)(*p), echo_attr); } else { - if (has_mbyte) { - int i = (*mb_ptr2len)((const char_u *)p); + int i = (*mb_ptr2len)((const char_u *)p); - (void)msg_outtrans_len_attr((char_u *)p, i, echo_attr); - p += i - 1; - } else { - (void)msg_outtrans_len_attr((char_u *)p, 1, echo_attr); - } + (void)msg_outtrans_len_attr((char_u *)p, i, echo_attr); + p += i - 1; } } } @@ -19519,18 +19505,22 @@ static const char *find_option_end(const char **const arg, int *const opt_flags) } else if (*p == 'l' && p[1] == ':') { *opt_flags = OPT_LOCAL; p += 2; - } else + } else { *opt_flags = 0; + } - if (!ASCII_ISALPHA(*p)) + if (!ASCII_ISALPHA(*p)) { return NULL; + } *arg = p; - if (p[0] == 't' && p[1] == '_' && p[2] != NUL && p[3] != NUL) - p += 4; /* termcap option */ - else - while (ASCII_ISALPHA(*p)) - ++p; + if (p[0] == 't' && p[1] == '_' && p[2] != NUL && p[3] != NUL) { + p += 4; // t_xx/termcap option + } else { + while (ASCII_ISALPHA(*p)) { + p++; + } + } return p; } @@ -19564,6 +19554,7 @@ void ex_function(exarg_T *eap) int todo; hashitem_T *hi; int sourcing_lnum_off; + bool show_block = false; /* * ":function" without argument: list functions. @@ -19737,6 +19728,11 @@ void ex_function(exarg_T *eap) goto errret_2; } + if (KeyTyped && ui_is_external(kUICmdline)) { + show_block = true; + ui_ext_cmdline_block_append(0, (const char *)eap->cmd); + } + // find extra arguments "range", "dict", "abort" and "closure" for (;; ) { p = skipwhite(p); @@ -19789,7 +19785,9 @@ void ex_function(exarg_T *eap) if (!eap->skip && did_emsg) goto erret; - msg_putchar('\n'); /* don't overwrite the function name */ + if (!ui_is_external(kUICmdline)) { + msg_putchar('\n'); // don't overwrite the function name + } cmdline_row = msg_row; } @@ -19823,6 +19821,9 @@ void ex_function(exarg_T *eap) EMSG(_("E126: Missing :endfunction")); goto erret; } + if (show_block) { + ui_ext_cmdline_block_append(indent, (const char *)theline); + } /* Detect line continuation: sourcing_lnum increased more than one. */ if (sourcing_lnum > sourcing_lnum_off + 1) @@ -20115,6 +20116,9 @@ ret_free: xfree(name); did_emsg |= saved_did_emsg; need_wait_return |= saved_wait_return; + if (show_block) { + ui_ext_cmdline_block_leave(); + } } /// Get a function name, translating "<SID>" and "<SNR>". @@ -20875,7 +20879,9 @@ void ex_delfunction(exarg_T *eap) if (!eap->skip) { if (fp == NULL) { - EMSG2(_(e_nofunc), eap->arg); + if (!eap->forceit) { + EMSG2(_(e_nofunc), eap->arg); + } return; } if (fp->uf_calls > 0) { @@ -20989,11 +20995,11 @@ void func_unref(char_u *name) if (fp == NULL && isdigit(*name)) { #ifdef EXITFREE if (!entered_free_all_mem) { - EMSG2(_(e_intern2), "func_unref()"); + internal_error("func_unref()"); abort(); } #else - EMSG2(_(e_intern2), "func_unref()"); + internal_error("func_unref()"); abort(); #endif } @@ -21032,7 +21038,7 @@ void func_ref(char_u *name) } else if (isdigit(*name)) { // Only give an error for a numbered function. // Fail silently, when named or lambda function isn't found. - EMSG2(_(e_intern2), "func_ref()"); + internal_error("func_ref()"); } } @@ -21044,6 +21050,22 @@ void func_ptr_ref(ufunc_T *fp) } } +/// Check whether funccall is still referenced outside +/// +/// It is supposed to be referenced if either it is referenced itself or if l:, +/// a: or a:000 are referenced as all these are statically allocated within +/// funccall structure. +static inline bool fc_referenced(const funccall_T *const fc) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT + FUNC_ATTR_NONNULL_ALL +{ + return ((fc->l_varlist.lv_refcount // NOLINT(runtime/deprecated) + != DO_NOT_FREE_CNT) + || fc->l_vars.dv_refcount != DO_NOT_FREE_CNT + || fc->l_avars.dv_refcount != DO_NOT_FREE_CNT + || fc->fc_refcount > 0); +} + /// Call a user function /// /// @param fp Function to call. @@ -21157,9 +21179,8 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, v->di_tv.v_type = VAR_LIST; v->di_tv.v_lock = VAR_FIXED; v->di_tv.vval.v_list = &fc->l_varlist; - memset(&fc->l_varlist, 0, sizeof(list_T)); - fc->l_varlist.lv_refcount = DO_NOT_FREE_CNT; - fc->l_varlist.lv_lock = VAR_FIXED; + tv_list_init_static(&fc->l_varlist); + tv_list_set_lock(&fc->l_varlist, VAR_FIXED); // Set a:firstline to "firstline" and a:lastline to "lastline". // Set a:name to named arguments. @@ -21208,8 +21229,8 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, if (ai >= 0 && ai < MAX_FUNC_ARGS) { tv_list_append(&fc->l_varlist, &fc->l_listitems[ai]); - fc->l_listitems[ai].li_tv = argvars[i]; - fc->l_listitems[ai].li_tv.v_lock = VAR_FIXED; + *TV_LIST_ITEM_TV(&fc->l_listitems[ai]) = argvars[i]; + TV_LIST_ITEM_TV(&fc->l_listitems[ai])->v_lock = VAR_FIXED; } } @@ -21279,15 +21300,17 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, } } + const bool do_profiling_yes = do_profiling == PROF_YES; + bool func_not_yet_profiling_but_should = - do_profiling == PROF_YES - && !fp->uf_profiling && has_profiling(FALSE, fp->uf_name, NULL); + do_profiling_yes + && !fp->uf_profiling && has_profiling(false, fp->uf_name, NULL); if (func_not_yet_profiling_but_should) func_do_profile(fp); bool func_or_func_caller_profiling = - do_profiling == PROF_YES + do_profiling_yes && (fp->uf_profiling || (fc->caller != NULL && fc->caller->func->uf_profiling)); @@ -21297,7 +21320,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, fp->uf_tm_children = profile_zero(); } - if (do_profiling == PROF_YES) { + if (do_profiling_yes) { script_prof_save(&wait_start); } @@ -21373,8 +21396,9 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, sourcing_name = save_sourcing_name; sourcing_lnum = save_sourcing_lnum; current_SID = save_current_SID; - if (do_profiling == PROF_YES) + if (do_profiling_yes) { script_prof_restore(&wait_start); + } if (p_verbose >= 12 && sourcing_name != NULL) { ++no_wait_return; @@ -21393,10 +21417,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, // If the a:000 list and the l: and a: dicts are not referenced and there // is no closure using it, we can free the funccall_T and what's in it. - if (fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT - && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT - && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT - && fc->fc_refcount <= 0) { + if (!fc_referenced(fc)) { free_funccal(fc, false); } else { // "fc" is still in use. This can happen when returning "a:000", @@ -21411,10 +21432,9 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, }); // Make a copy of the a:000 items, since we didn't do that above. - for (listitem_T *li = fc->l_varlist.lv_first; li != NULL; - li = li->li_next) { - tv_copy(&li->li_tv, &li->li_tv); - } + TV_LIST_ITER(&fc->l_varlist, li, { + tv_copy(TV_LIST_ITEM_TV(li), TV_LIST_ITEM_TV(li)); + }); } if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0) { @@ -21441,10 +21461,8 @@ static void funccal_unref(funccall_T *fc, ufunc_T *fp, bool force) return; } - if (--fc->fc_refcount <= 0 && (force || ( - fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT - && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT - && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT))) { + fc->fc_refcount--; + if (force ? fc->fc_refcount <= 0 : !fc_referenced(fc)) { for (pfc = &previous_funccal; *pfc != NULL; pfc = &(*pfc)->caller) { if (fc == *pfc) { *pfc = fc->caller; @@ -21479,8 +21497,6 @@ free_funccal ( int free_val /* a: vars were allocated */ ) { - listitem_T *li; - for (int i = 0; i < fc->fc_funcs.ga_len; i++) { ufunc_T *fp = ((ufunc_T **)(fc->fc_funcs.ga_data))[i]; @@ -21498,14 +21514,14 @@ free_funccal ( // allocated variables. vars_clear_ext(&fc->l_avars.dv_hashtab, free_val); - /* free all l: variables */ + // Free all l: variables. vars_clear(&fc->l_vars.dv_hashtab); // Free the a:000 variables if they were allocated. if (free_val) { - for (li = fc->l_varlist.lv_first; li != NULL; li = li->li_next) { - tv_clear(&li->li_tv); - } + TV_LIST_ITER(&fc->l_varlist, li, { + tv_clear(TV_LIST_ITEM_TV(li)); + }); } func_ptr_unref(fc->func); @@ -22352,317 +22368,54 @@ char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, return ret; } -static inline TerminalJobData *common_job_init(char **argv, - Callback on_stdout, - Callback on_stderr, - Callback on_exit, - bool pty, - bool rpc, - bool detach, - const char *cwd) -{ - TerminalJobData *data = xcalloc(1, sizeof(TerminalJobData)); - data->stopped = false; - data->on_stdout = on_stdout; - data->on_stderr = on_stderr; - data->on_exit = on_exit; - data->events = multiqueue_new_child(main_loop.events); - data->rpc = rpc; - if (pty) { - data->proc.pty = pty_process_init(&main_loop, data); - } else { - data->proc.uv = libuv_process_init(&main_loop, data); - } - Process *proc = (Process *)&data->proc; - proc->argv = argv; - proc->in = &data->in; - proc->out = &data->out; - if (!pty) { - proc->err = &data->err; - } - proc->cb = eval_job_process_exit_cb; - proc->events = data->events; - proc->detach = detach; - proc->cwd = cwd; - return data; -} - /// common code for getting job callbacks for jobstart, termopen and rpcstart /// /// @return true/false on success/failure. -static inline bool common_job_callbacks(dict_T *vopts, Callback *on_stdout, - Callback *on_stderr, Callback *on_exit) +static inline bool common_job_callbacks(dict_T *vopts, + CallbackReader *on_stdout, + CallbackReader *on_stderr, + Callback *on_exit) { - if (tv_dict_get_callback(vopts, S_LEN("on_stdout"), on_stdout) - &&tv_dict_get_callback(vopts, S_LEN("on_stderr"), on_stderr) + if (tv_dict_get_callback(vopts, S_LEN("on_stdout"), &on_stdout->cb) + &&tv_dict_get_callback(vopts, S_LEN("on_stderr"), &on_stderr->cb) && tv_dict_get_callback(vopts, S_LEN("on_exit"), on_exit)) { + on_stdout->buffered = tv_dict_get_number(vopts, "stdout_buffered"); + on_stderr->buffered = tv_dict_get_number(vopts, "stderr_buffered"); + if (on_stdout->buffered && on_stdout->cb.type == kCallbackNone) { + on_stdout->self = vopts; + } + if (on_stderr->buffered && on_stderr->cb.type == kCallbackNone) { + on_stderr->self = vopts; + } vopts->dv_refcount++; return true; } - callback_free(on_stdout); - callback_free(on_stderr); + callback_reader_free(on_stdout); + callback_reader_free(on_stderr); callback_free(on_exit); return false; } -static inline bool common_job_start(TerminalJobData *data, typval_T *rettv) -{ - Process *proc = (Process *)&data->proc; - if (proc->type == kProcessTypePty && proc->detach) { - EMSG2(_(e_invarg2), "terminal/pty job cannot be detached"); - xfree(data->proc.pty.term_name); - shell_free_argv(proc->argv); - free_term_job_data_event((void **)&data); - return false; - } - - data->id = next_chan_id++; - pmap_put(uint64_t)(jobs, data->id, data); - - data->refcount++; - char *cmd = xstrdup(proc->argv[0]); - int status = process_spawn(proc); - if (status) { - EMSG3(_(e_jobspawn), os_strerror(status), cmd); - xfree(cmd); - if (proc->type == kProcessTypePty) { - xfree(data->proc.pty.term_name); - } - rettv->vval.v_number = proc->status; - term_job_data_decref(data); - return false; - } - xfree(cmd); - - - if (data->rpc) { - // the rpc channel takes over the in and out streams - channel_from_process(proc, data->id); - } else { - wstream_init(proc->in, 0); - if (proc->out) { - rstream_init(proc->out, 0); - rstream_start(proc->out, on_job_stdout, data); - } - } - - if (proc->err) { - rstream_init(proc->err, 0); - rstream_start(proc->err, on_job_stderr, data); - } - rettv->vval.v_number = data->id; - return true; -} - -static inline void free_term_job_data_event(void **argv) -{ - TerminalJobData *data = argv[0]; - callback_free(&data->on_stdout); - callback_free(&data->on_stderr); - callback_free(&data->on_exit); - multiqueue_free(data->events); - pmap_del(uint64_t)(jobs, data->id); - xfree(data); -} - -static inline void free_term_job_data(TerminalJobData *data) -{ - // data->queue may still be used after this function returns(process_wait), so - // only free in the next event loop iteration - multiqueue_put(main_loop.fast_events, free_term_job_data_event, 1, data); -} - -// vimscript job callbacks must be executed on Nvim main loop -static inline void process_job_event(TerminalJobData *data, Callback *callback, - const char *type, char *buf, size_t count, - int status) +static Channel *find_job(uint64_t id, bool show_error) { - JobEvent event_data; - event_data.received = NULL; - if (buf) { - event_data.received = tv_list_alloc(); - char *ptr = buf; - size_t remaining = count; - size_t off = 0; - - while (off < remaining) { - // append the line - if (ptr[off] == NL) { - tv_list_append_string(event_data.received, ptr, off); - size_t skip = off + 1; - ptr += skip; - remaining -= skip; - off = 0; - continue; - } - if (ptr[off] == NUL) { - // Translate NUL to NL - ptr[off] = NL; + Channel *data = find_channel(id); + if (!data || data->streamtype != kChannelStreamProc + || process_is_stopped(&data->stream.proc)) { + if (show_error) { + if (data && data->streamtype != kChannelStreamProc) { + EMSG(_(e_invchanjob)); + } else { + EMSG(_(e_invchan)); } - off++; } - tv_list_append_string(event_data.received, ptr, off); - } else { - event_data.status = status; - } - event_data.data = data; - event_data.callback = callback; - event_data.type = type; - on_job_event(&event_data); -} - -static void on_job_stdout(Stream *stream, RBuffer *buf, size_t count, - void *job, bool eof) -{ - TerminalJobData *data = job; - on_job_output(stream, job, buf, count, eof, &data->on_stdout, "stdout"); -} - -static void on_job_stderr(Stream *stream, RBuffer *buf, size_t count, - void *job, bool eof) -{ - TerminalJobData *data = job; - on_job_output(stream, job, buf, count, eof, &data->on_stderr, "stderr"); -} - -static void on_job_output(Stream *stream, TerminalJobData *data, RBuffer *buf, - size_t count, bool eof, Callback *callback, - const char *type) -{ - if (eof) { - return; - } - - // stub variable, to keep reading consistent with the order of events, only - // consider the count parameter. - size_t r; - char *ptr = rbuffer_read_ptr(buf, &r); - - // The order here matters, the terminal must receive the data first because - // process_job_event will modify the read buffer(convert NULs into NLs) - if (data->term) { - terminal_receive(data->term, ptr, count); - } - - rbuffer_consumed(buf, count); - if (callback->type != kCallbackNone) { - process_job_event(data, callback, type, ptr, count, 0); - } -} - -static void eval_job_process_exit_cb(Process *proc, int status, void *d) -{ - TerminalJobData *data = d; - if (data->term && !data->exited) { - data->exited = true; - char msg[sizeof("\r\n[Process exited ]") + NUMBUFLEN]; - snprintf(msg, sizeof msg, "\r\n[Process exited %d]", proc->status); - terminal_close(data->term, msg); - } - if (data->rpc) { - channel_process_exit(data->id, status); - } - - if (data->status_ptr) { - *data->status_ptr = status; - } - - process_job_event(data, &data->on_exit, "exit", NULL, 0, status); - - term_job_data_decref(data); -} - -static void term_write(char *buf, size_t size, void *d) -{ - TerminalJobData *job = d; - if (job->in.closed) { - // If the backing stream was closed abruptly, there may be write events - // ahead of the terminal close event. Just ignore the writes. - ILOG("write failed: stream is closed"); - return; - } - WBuffer *wbuf = wstream_new_buffer(xmemdup(buf, size), size, 1, xfree); - wstream_write(&job->in, wbuf); -} - -static void term_resize(uint16_t width, uint16_t height, void *d) -{ - TerminalJobData *data = d; - pty_process_resize(&data->proc.pty, width, height); -} - -static inline void term_delayed_free(void **argv) -{ - TerminalJobData *j = argv[0]; - if (j->in.pending_reqs || j->out.pending_reqs || j->err.pending_reqs) { - multiqueue_put(j->events, term_delayed_free, 1, j); - return; - } - - terminal_destroy(j->term); - term_job_data_decref(j); -} - -static void term_close(void *d) -{ - TerminalJobData *data = d; - if (!data->exited) { - data->exited = true; - process_stop((Process *)&data->proc); - } - multiqueue_put(data->events, term_delayed_free, 1, data); -} - -static void term_job_data_decref(TerminalJobData *data) -{ - if (!(--data->refcount)) { - free_term_job_data(data); - } -} - -static void on_job_event(JobEvent *ev) -{ - if (!ev->callback) { - return; - } - - typval_T argv[4]; - - argv[0].v_type = VAR_NUMBER; - argv[0].v_lock = 0; - argv[0].vval.v_number = ev->data->id; - - if (ev->received) { - argv[1].v_type = VAR_LIST; - argv[1].v_lock = 0; - argv[1].vval.v_list = ev->received; - argv[1].vval.v_list->lv_refcount++; - } else { - argv[1].v_type = VAR_NUMBER; - argv[1].v_lock = 0; - argv[1].vval.v_number = ev->status; - } - - argv[2].v_type = VAR_STRING; - argv[2].v_lock = 0; - argv[2].vval.v_string = (uint8_t *)ev->type; - - typval_T rettv = TV_INITIAL_VALUE; - callback_call(ev->callback, 3, argv, &rettv); - tv_clear(&rettv); -} - -static TerminalJobData *find_job(uint64_t id) -{ - TerminalJobData *data = pmap_get(uint64_t)(jobs, id); - if (!data || data->stopped) { return NULL; } return data; } + static void script_host_eval(char *name, typval_T *argvars, typval_T *rettv) { if (check_restricted() || check_secure()) { @@ -22674,7 +22427,7 @@ static void script_host_eval(char *name, typval_T *argvars, typval_T *rettv) return; } - list_T *args = tv_list_alloc(); + list_T *args = tv_list_alloc(1); tv_list_append_string(args, (const char *)argvars[0].vval.v_string, -1); *rettv = eval_call_provider(name, "eval", args); } @@ -22692,7 +22445,6 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) .sourcing_lnum = sourcing_lnum, .autocmd_fname = autocmd_fname, .autocmd_match = autocmd_match, - .autocmd_fname_full = autocmd_fname_full, .autocmd_bufnr = autocmd_bufnr, .funccalp = save_funccal() }; @@ -22703,8 +22455,8 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) {.v_type = VAR_LIST, .vval.v_list = arguments, .v_lock = 0}, {.v_type = VAR_UNKNOWN} }; - typval_T rettv = {.v_type = VAR_UNKNOWN, .v_lock = 0}; - arguments->lv_refcount++; + typval_T rettv = { .v_type = VAR_UNKNOWN, .v_lock = VAR_UNLOCKED }; + tv_list_ref(arguments); int dummy; (void)call_func((const char_u *)func, @@ -22725,13 +22477,14 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments) restore_funccal(provider_caller_scope.funccalp); provider_caller_scope = saved_provider_caller_scope; provider_call_nesting--; - + assert(provider_call_nesting >= 0); + return rettv; } bool eval_has_provider(const char *name) { -#define check_provider(name) \ +#define CHECK_PROVIDER(name) \ if (has_##name == -1) { \ has_##name = !!find_func((char_u *)"provider#" #name "#Call"); \ if (!has_##name) { \ @@ -22747,19 +22500,61 @@ bool eval_has_provider(const char *name) static int has_python3 = -1; static int has_ruby = -1; - if (!strcmp(name, "clipboard")) { - check_provider(clipboard); + if (strequal(name, "clipboard")) { + CHECK_PROVIDER(clipboard); return has_clipboard; - } else if (!strcmp(name, "python3")) { - check_provider(python3); + } else if (strequal(name, "python3")) { + CHECK_PROVIDER(python3); return has_python3; - } else if (!strcmp(name, "python")) { - check_provider(python); + } else if (strequal(name, "python")) { + CHECK_PROVIDER(python); return has_python; - } else if (!strcmp(name, "ruby")) { - check_provider(ruby); + } else if (strequal(name, "ruby")) { + CHECK_PROVIDER(ruby); return has_ruby; } return false; } + +/// Writes "<sourcing_name>:<sourcing_lnum>" to `buf[bufsize]`. +void eval_fmt_source_name_line(char *buf, size_t bufsize) +{ + if (sourcing_name) { + snprintf(buf, bufsize, "%s:%" PRIdLINENR, sourcing_name, sourcing_lnum); + } else { + snprintf(buf, bufsize, "?"); + } +} + +/// ":checkhealth [plugins]" +void ex_checkhealth(exarg_T *eap) +{ + bool found = !!find_func((char_u *)"health#check"); + if (!found + && script_autoload("health#check", sizeof("health#check") - 1, false)) { + found = !!find_func((char_u *)"health#check"); + } + if (!found) { + const char *vimruntime_env = os_getenv("VIMRUNTIME"); + if (vimruntime_env == NULL) { + EMSG(_("E5009: $VIMRUNTIME is empty or unset")); + } else { + bool rtp_ok = NULL != strstr((char *)p_rtp, vimruntime_env); + if (rtp_ok) { + EMSG2(_("E5009: Invalid $VIMRUNTIME: %s"), vimruntime_env); + } else { + EMSG(_("E5009: Invalid 'runtimepath'")); + } + } + return; + } + + size_t bufsize = STRLEN(eap->arg) + sizeof("call health#check('')"); + char *buf = xmalloc(bufsize); + snprintf(buf, bufsize, "call health#check('%s')", eap->arg); + + do_cmdline_cmd(buf); + + xfree(buf); +} diff --git a/src/nvim/eval.h b/src/nvim/eval.h index 070bc35bd5..b798eae187 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -7,6 +7,9 @@ #include "nvim/eval/typval.h" #include "nvim/profile.h" #include "nvim/garray.h" +#include "nvim/event/rstream.h" +#include "nvim/event/wstream.h" +#include "nvim/channel.h" #define COPYID_INC 2 #define COPYID_MASK (~0x1) @@ -53,6 +56,7 @@ typedef enum { VV_DYING, VV_EXCEPTION, VV_THROWPOINT, + VV_STDERR, VV_REG, VV_CMDBANG, VV_INSERTMODE, @@ -82,7 +86,6 @@ typedef enum { VV_OLDFILES, VV_WINDOWID, VV_PROGPATH, - VV_COMMAND_OUTPUT, VV_COMPLETED_ITEM, VV_OPTION_NEW, VV_OPTION_OLD, diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 30766a0734..daa3b637a3 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -2,10 +2,10 @@ -- -- Keys: -- --- args Number of arguments, list with maximum and minimum number of arguments --- or list with a minimum number of arguments only. Defaults to zero +-- args Number of arguments, list with maximum and minimum number of arguments +-- or list with a minimum number of arguments only. Defaults to zero -- arguments. --- func Name of the C function which implements the VimL function. Defaults to +-- func Name of the C function which implements the VimL function. Defaults to -- `f_{funcname}`. local varargs = function(nr) @@ -29,7 +29,7 @@ return { assert_exception={args={1, 2}}, assert_fails={args={1, 2}}, assert_false={args={1, 2}}, - assert_inrange={args={2, 3}}, + assert_inrange={args={3, 4}}, assert_match={args={2, 3}}, assert_notequal={args={2, 3}}, assert_notmatch={args={2, 3}}, @@ -55,6 +55,8 @@ return { call={args={2, 3}}, ceil={args=1, func="float_op_wrapper", data="&ceil"}, changenr={}, + chanclose={args={1, 2}}, + chansend={args=2}, char2nr={args={1, 2}}, cindent={args=1}, clearmatches={}, @@ -173,10 +175,10 @@ return { islocked={args=1}, id={args=1}, items={args=1}, - jobclose={args={1, 2}}, + jobclose={args={1, 2}, func="f_chanclose"}, jobpid={args=1}, jobresize={args=3}, - jobsend={args=2}, + jobsend={args=2, func="f_chansend"}, jobstart={args={1, 2}}, jobstop={args=1}, jobwait={args={1, 2}}, @@ -208,6 +210,7 @@ return { matchstr={args={2, 4}}, matchstrpos={args={2,4}}, max={args=1}, + menu_get={args={1, 2}}, min={args=1}, mkdir={args={1, 3}}, mode={args={0, 1}}, @@ -272,6 +275,7 @@ return { sockconnect={args={2,3}}, sort={args={1, 3}}, soundfold={args=1}, + stdioopen={args=1}, spellbadword={args={0, 1}}, spellsuggest={args={1, 3}}, split={args={1, 3}}, @@ -309,6 +313,7 @@ return { tempname={}, termopen={args={1, 2}}, test_garbagecollect_now={}, + test_write_list_log={args=1}, timer_info={args={0,1}}, timer_pause={args=2}, timer_start={args={2,3}}, diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index 9c9c2c2dc8..17799b500c 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -60,8 +60,8 @@ static inline void create_special_dict(typval_T *const rettv, dictitem_T *const type_di = tv_dict_item_alloc_len(S_LEN("_TYPE")); type_di->di_tv.v_type = VAR_LIST; type_di->di_tv.v_lock = VAR_UNLOCKED; - type_di->di_tv.vval.v_list = (list_T *) eval_msgpack_type_lists[type]; - type_di->di_tv.vval.v_list->lv_refcount++; + type_di->di_tv.vval.v_list = (list_T *)eval_msgpack_type_lists[type]; + tv_list_ref(type_di->di_tv.vval.v_list); tv_dict_add(dict, type_di); dictitem_T *const val_di = tv_dict_item_alloc_len(S_LEN("_VAL")); val_di->di_tv = val; @@ -120,16 +120,14 @@ static inline int json_decoder_pop(ValuesStackItem obj, last_container = kv_last(*container_stack); } if (last_container.container.v_type == VAR_LIST) { - if (last_container.container.vval.v_list->lv_len != 0 + if (tv_list_len(last_container.container.vval.v_list) != 0 && !obj.didcomma) { EMSG2(_("E474: Expected comma before list item: %s"), val_location); tv_clear(&obj.val); return FAIL; } assert(last_container.special_val == NULL); - listitem_T *obj_li = tv_list_item_alloc(); - obj_li->li_tv = obj.val; - tv_list_append(last_container.container.vval.v_list, obj_li); + tv_list_append_owned_tv(last_container.container.vval.v_list, obj.val); } else if (last_container.stack_index == kv_size(*stack) - 2) { if (!obj.didcolon) { EMSG2(_("E474: Expected colon before dictionary value: %s"), @@ -152,14 +150,10 @@ static inline int json_decoder_pop(ValuesStackItem obj, } obj_di->di_tv = obj.val; } else { - list_T *const kv_pair = tv_list_alloc(); + list_T *const kv_pair = tv_list_alloc(2); tv_list_append_list(last_container.special_val, kv_pair); - listitem_T *const key_li = tv_list_item_alloc(); - key_li->li_tv = key.val; - tv_list_append(kv_pair, key_li); - listitem_T *const val_li = tv_list_item_alloc(); - val_li->li_tv = obj.val; - tv_list_append(kv_pair, val_li); + tv_list_append_owned_tv(kv_pair, key.val); + tv_list_append_owned_tv(kv_pair, obj.val); } } else { // Object with key only @@ -227,14 +221,19 @@ static inline int json_decoder_pop(ValuesStackItem obj, /// Create a new special dictionary that ought to represent a MAP /// /// @param[out] ret_tv Address where new special dictionary is saved. +/// @param[in] len Expected number of items to be populated before list +/// becomes accessible from VimL. It is still valid to +/// underpopulate a list, value only controls how many elements +/// will be allocated in advance. @see ListLenSpecials. /// /// @return [allocated] list which should contain key-value pairs. Return value /// may be safely ignored. -list_T *decode_create_map_special_dict(typval_T *const ret_tv) +list_T *decode_create_map_special_dict(typval_T *const ret_tv, + const ptrdiff_t len) FUNC_ATTR_NONNULL_ALL { - list_T *const list = tv_list_alloc(); - list->lv_refcount++; + list_T *const list = tv_list_alloc(len); + tv_list_ref(list); create_special_dict(ret_tv, kMPMap, ((typval_T) { .v_type = VAR_LIST, .v_lock = VAR_UNLOCKED, @@ -266,11 +265,11 @@ typval_T decode_string(const char *const s, const size_t len, { assert(s != NULL || len == 0); const bool really_hasnul = (hasnul == kNone - ? memchr(s, NUL, len) != NULL + ? ((s != NULL) && (memchr(s, NUL, len) != NULL)) : (bool)hasnul); if (really_hasnul) { - list_T *const list = tv_list_alloc(); - list->lv_refcount++; + list_T *const list = tv_list_alloc(kListLenMayKnow); + tv_list_ref(list); typval_T tv; create_special_dict(&tv, binary ? kMPBinary : kMPString, ((typval_T) { .v_type = VAR_LIST, @@ -291,7 +290,7 @@ typval_T decode_string(const char *const s, const size_t len, .v_type = VAR_STRING, .v_lock = VAR_UNLOCKED, .vval = { .v_string = (char_u *)( - s_allocated ? (char *)s : xmemdupz(s, len)) }, + (s == NULL || s_allocated) ? (char *)s : xmemdupz(s, len)) }, }; } } @@ -738,8 +737,9 @@ json_decode_string_cycle_start: } else if (last_container.special_val == NULL ? (last_container.container.v_type == VAR_DICT ? (DICT_LEN(last_container.container.vval.v_dict) == 0) - : (last_container.container.vval.v_list->lv_len == 0)) - : (last_container.special_val->lv_len == 0)) { + : (tv_list_len(last_container.container.vval.v_list) + == 0)) + : (tv_list_len(last_container.special_val) == 0)) { emsgf(_("E474: Leading comma: %.*s"), LENP(p, e)); goto json_decode_string_fail; } @@ -848,8 +848,8 @@ json_decode_string_cycle_start: break; } case '[': { - list_T *list = tv_list_alloc(); - list->lv_refcount++; + list_T *list = tv_list_alloc(kListLenMayKnow); + tv_list_ref(list); typval_T tv = { .v_type = VAR_LIST, .v_lock = VAR_UNLOCKED, @@ -869,7 +869,7 @@ json_decode_string_cycle_start: list_T *val_list = NULL; if (next_map_special) { next_map_special = false; - val_list = decode_create_map_special_dict(&tv); + val_list = decode_create_map_special_dict(&tv, kListLenMayKnow); } else { dict_T *dict = tv_dict_alloc(); dict->dv_refcount++; @@ -969,8 +969,8 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) .vval = { .v_number = (varnumber_T) mobj.via.u64 }, }; } else { - list_T *const list = tv_list_alloc(); - list->lv_refcount++; + list_T *const list = tv_list_alloc(4); + tv_list_ref(list); create_special_dict(rettv, kMPInteger, ((typval_T) { .v_type = VAR_LIST, .v_lock = VAR_UNLOCKED, @@ -992,8 +992,8 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) .vval = { .v_number = (varnumber_T) mobj.via.i64 }, }; } else { - list_T *const list = tv_list_alloc(); - list->lv_refcount++; + list_T *const list = tv_list_alloc(4); + tv_list_ref(list); create_special_dict(rettv, kMPInteger, ((typval_T) { .v_type = VAR_LIST, .v_lock = VAR_UNLOCKED, @@ -1038,18 +1038,19 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) break; } case MSGPACK_OBJECT_ARRAY: { - list_T *const list = tv_list_alloc(); - list->lv_refcount++; + list_T *const list = tv_list_alloc((ptrdiff_t)mobj.via.array.size); + tv_list_ref(list); *rettv = (typval_T) { .v_type = VAR_LIST, .v_lock = VAR_UNLOCKED, .vval = { .v_list = list }, }; for (size_t i = 0; i < mobj.via.array.size; i++) { - listitem_T *const li = tv_list_item_alloc(); - li->li_tv.v_type = VAR_UNKNOWN; - tv_list_append(list, li); - if (msgpack_to_vim(mobj.via.array.ptr[i], &li->li_tv) == FAIL) { + // Not populated yet, need to create list item to push. + tv_list_append_owned_tv(list, (typval_T) { .v_type = VAR_UNKNOWN }); + if (msgpack_to_vim(mobj.via.array.ptr[i], + TV_LIST_ITEM_TV(tv_list_last(list))) + == FAIL) { return FAIL; } } @@ -1089,30 +1090,33 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) } break; msgpack_to_vim_generic_map: {} - list_T *const list = decode_create_map_special_dict(rettv); + list_T *const list = decode_create_map_special_dict( + rettv, (ptrdiff_t)mobj.via.map.size); for (size_t i = 0; i < mobj.via.map.size; i++) { - list_T *const kv_pair = tv_list_alloc(); + list_T *const kv_pair = tv_list_alloc(2); tv_list_append_list(list, kv_pair); - listitem_T *const key_li = tv_list_item_alloc(); - key_li->li_tv.v_type = VAR_UNKNOWN; - tv_list_append(kv_pair, key_li); - listitem_T *const val_li = tv_list_item_alloc(); - val_li->li_tv.v_type = VAR_UNKNOWN; - tv_list_append(kv_pair, val_li); - if (msgpack_to_vim(mobj.via.map.ptr[i].key, &key_li->li_tv) == FAIL) { + + typval_T key_tv = { .v_type = VAR_UNKNOWN }; + if (msgpack_to_vim(mobj.via.map.ptr[i].key, &key_tv) == FAIL) { + tv_clear(&key_tv); return FAIL; } - if (msgpack_to_vim(mobj.via.map.ptr[i].val, &val_li->li_tv) == FAIL) { + tv_list_append_owned_tv(kv_pair, key_tv); + + typval_T val_tv = { .v_type = VAR_UNKNOWN }; + if (msgpack_to_vim(mobj.via.map.ptr[i].val, &val_tv) == FAIL) { + tv_clear(&val_tv); return FAIL; } + tv_list_append_owned_tv(kv_pair, val_tv); } break; } case MSGPACK_OBJECT_EXT: { - list_T *const list = tv_list_alloc(); - list->lv_refcount++; + list_T *const list = tv_list_alloc(2); + tv_list_ref(list); tv_list_append_number(list, mobj.via.ext.type); - list_T *const ext_val_list = tv_list_alloc(); + list_T *const ext_val_list = tv_list_alloc(kListLenMayKnow); tv_list_append_list(list, ext_val_list); create_special_dict(rettv, kMPExt, ((typval_T) { .v_type = VAR_LIST, diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index ef647b3ee4..9bae436e3d 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -28,6 +28,11 @@ #include "nvim/lib/kvec.h" #include "nvim/eval/typval_encode.h" +#ifdef __MINGW32__ +# undef fpclassify +# define fpclassify __fpclassify +#endif + #define ga_concat(a, b) ga_concat(a, (char_u *)b) #define utf_ptr2char(b) utf_ptr2char((char_u *)b) #define utf_ptr2len(b) ((size_t)utf_ptr2len((char_u *)b)) @@ -53,17 +58,18 @@ int encode_list_write(void *const data, const char *const buf, const size_t len) list_T *const list = (list_T *) data; const char *const end = buf + len; const char *line_end = buf; - listitem_T *li = list->lv_last; + listitem_T *li = tv_list_last(list); // Continue the last list element if (li != NULL) { line_end = xmemscan(buf, NL, len); if (line_end != buf) { const size_t line_length = (size_t)(line_end - buf); - char *str = (char *)li->li_tv.vval.v_string; + char *str = (char *)TV_LIST_ITEM_TV(li)->vval.v_string; const size_t li_len = (str == NULL ? 0 : strlen(str)); - li->li_tv.vval.v_string = xrealloc(str, li_len + line_length + 1); - str = (char *)li->li_tv.vval.v_string + li_len; + TV_LIST_ITEM_TV(li)->vval.v_string = xrealloc( + str, li_len + line_length + 1); + str = (char *)TV_LIST_ITEM_TV(li)->vval.v_string + li_len; memcpy(str, buf, line_length); str[line_length] = 0; memchrsub(str, NUL, NL, line_length); @@ -135,21 +141,27 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack, } case kMPConvPairs: case kMPConvList: { - int idx = 0; - const listitem_T *li; - for (li = v.data.l.list->lv_first; - li != NULL && li->li_next != v.data.l.li; - li = li->li_next) { - idx++; - } + const int idx = (v.data.l.li == tv_list_first(v.data.l.list) + ? 0 + : (v.data.l.li == NULL + ? tv_list_len(v.data.l.list) - 1 + : (int)tv_list_idx_of_item( + v.data.l.list, + TV_LIST_ITEM_PREV(v.data.l.list, + v.data.l.li)))); + const listitem_T *const li = (v.data.l.li == NULL + ? tv_list_last(v.data.l.list) + : TV_LIST_ITEM_PREV(v.data.l.list, + v.data.l.li)); if (v.type == kMPConvList || li == NULL - || (li->li_tv.v_type != VAR_LIST - && li->li_tv.vval.v_list->lv_len <= 0)) { - vim_snprintf((char *) IObuff, IOSIZE, idx_msg, idx); + || (TV_LIST_ITEM_TV(li)->v_type != VAR_LIST + && tv_list_len(TV_LIST_ITEM_TV(li)->vval.v_list) <= 0)) { + vim_snprintf((char *)IObuff, IOSIZE, idx_msg, idx); ga_concat(&msg_ga, IObuff); } else { - typval_T key_tv = li->li_tv.vval.v_list->lv_first->li_tv; + typval_T key_tv = *TV_LIST_ITEM_TV( + tv_list_first(TV_LIST_ITEM_TV(li)->vval.v_list)); char *const key = encode_tv2echo(&key_tv, NULL); vim_snprintf((char *) IObuff, IOSIZE, key_pair_msg, key, idx); xfree(key); @@ -202,21 +214,17 @@ bool encode_vim_list_to_buf(const list_T *const list, size_t *const ret_len, FUNC_ATTR_NONNULL_ARG(2, 3) FUNC_ATTR_WARN_UNUSED_RESULT { size_t len = 0; - if (list != NULL) { - for (const listitem_T *li = list->lv_first; - li != NULL; - li = li->li_next) { - if (li->li_tv.v_type != VAR_STRING) { - return false; - } - len++; - if (li->li_tv.vval.v_string != 0) { - len += STRLEN(li->li_tv.vval.v_string); - } + TV_LIST_ITER_CONST(list, li, { + if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING) { + return false; } - if (len) { - len--; + len++; + if (TV_LIST_ITEM_TV(li)->vval.v_string != NULL) { + len += STRLEN(TV_LIST_ITEM_TV(li)->vval.v_string); } + }); + if (len) { + len--; } *ret_len = len; if (len == 0) { @@ -253,31 +261,34 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, char *const buf_end = buf + nbuf; char *p = buf; while (p < buf_end) { - assert(state->li_length == 0 || state->li->li_tv.vval.v_string != NULL); + assert(state->li_length == 0 + || TV_LIST_ITEM_TV(state->li)->vval.v_string != NULL); for (size_t i = state->offset; i < state->li_length && p < buf_end; i++) { - assert(state->li->li_tv.vval.v_string != NULL); - const char ch = (char)state->li->li_tv.vval.v_string[state->offset++]; + assert(TV_LIST_ITEM_TV(state->li)->vval.v_string != NULL); + const char ch = (char)( + TV_LIST_ITEM_TV(state->li)->vval.v_string[state->offset++]); *p++ = (char)((char)ch == (char)NL ? (char)NUL : (char)ch); } if (p < buf_end) { - state->li = state->li->li_next; + state->li = TV_LIST_ITEM_NEXT(state->list, state->li); if (state->li == NULL) { *read_bytes = (size_t) (p - buf); return OK; } *p++ = NL; - if (state->li->li_tv.v_type != VAR_STRING) { - *read_bytes = (size_t) (p - buf); + if (TV_LIST_ITEM_TV(state->li)->v_type != VAR_STRING) { + *read_bytes = (size_t)(p - buf); return FAIL; } state->offset = 0; - state->li_length = (state->li->li_tv.vval.v_string == NULL + state->li_length = (TV_LIST_ITEM_TV(state->li)->vval.v_string == NULL ? 0 - : STRLEN(state->li->li_tv.vval.v_string)); + : STRLEN(TV_LIST_ITEM_TV(state->li)->vval.v_string)); } } *read_bytes = nbuf; - return (state->offset < state->li_length || state->li->li_next != NULL + return ((state->offset < state->li_length + || TV_LIST_ITEM_NEXT(state->list, state->li) != NULL) ? NOTDONE : OK); } @@ -340,7 +351,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, do { \ const char *const fun_ = (const char *)(fun); \ if (fun_ == NULL) { \ - EMSG2(_(e_intern2), "string(): NULL function name"); \ + internal_error("string(): NULL function name"); \ ga_concat(gap, "function(NULL"); \ } else { \ ga_concat(gap, "function("); \ @@ -727,12 +738,11 @@ bool encode_check_json_key(const typval_T *const tv) if (val_di->di_tv.vval.v_list == NULL) { return true; } - for (const listitem_T *li = val_di->di_tv.vval.v_list->lv_first; - li != NULL; li = li->li_next) { - if (li->li_tv.v_type != VAR_STRING) { + TV_LIST_ITER_CONST(val_di->di_tv.vval.v_list, li, { + if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING) { return false; } - } + }); return true; } diff --git a/src/nvim/eval/encode.h b/src/nvim/eval/encode.h index 9bc665253b..ccea245ab3 100644 --- a/src/nvim/eval/encode.h +++ b/src/nvim/eval/encode.h @@ -33,9 +33,10 @@ int encode_vim_to_echo(garray_T *const packer, /// Structure defining state for read_from_list() typedef struct { + const list_T *const list; ///< List being currently read. const listitem_T *li; ///< Item currently read. - size_t offset; ///< Byte offset inside the read item. - size_t li_length; ///< Length of the string inside the read item. + size_t offset; ///< Byte offset inside the read item. + size_t li_length; ///< Length of the string inside the read item. } ListReaderState; /// Initialize ListReaderState structure @@ -43,11 +44,13 @@ static inline ListReaderState encode_init_lrstate(const list_T *const list) FUNC_ATTR_NONNULL_ALL { return (ListReaderState) { - .li = list->lv_first, + .list = list, + .li = tv_list_first(list), .offset = 0, - .li_length = (list->lv_first->li_tv.vval.v_string == NULL + .li_length = (TV_LIST_ITEM_TV(tv_list_first(list))->vval.v_string == NULL ? 0 - : STRLEN(list->lv_first->li_tv.vval.v_string)), + : STRLEN(TV_LIST_ITEM_TV( + tv_list_first(list))->vval.v_string)), }; } diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 4521085519..c8b550f902 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -3,6 +3,7 @@ #include <stdio.h> #include <stddef.h> +#include <stdlib.h> #include <string.h> #include <assert.h> #include <stdbool.h> @@ -30,6 +31,7 @@ #include "nvim/message.h" // TODO(ZyX-I): Move line_breakcheck out of misc1 #include "nvim/misc1.h" // For line_breakcheck +#include "nvim/os/fileio.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/typval.c.generated.h" @@ -44,6 +46,70 @@ bool tv_in_free_unref_items = false; const char *const tv_empty_string = ""; //{{{1 Lists +//{{{2 List log +#ifdef LOG_LIST_ACTIONS +ListLog *list_log_first = NULL; +ListLog *list_log_last = NULL; + +/// Write list log to the given file +/// +/// @param[in] fname File to write log to. Will be appended to if already +/// present. +void list_write_log(const char *const fname) + FUNC_ATTR_NONNULL_ALL +{ + FileDescriptor fp; + const int fo_ret = file_open(&fp, fname, kFileCreate|kFileAppend, 0600); + if (fo_ret != 0) { + emsgf(_("E5142: Failed to open file %s: %s"), fname, os_strerror(fo_ret)); + return; + } + for (ListLog *chunk = list_log_first; chunk != NULL;) { + for (size_t i = 0; i < chunk->size; i++) { + char buf[10 + 1 + ((16 + 3) * 3) + (8 + 2) + 2]; + // act : hex " c:" len "[]" "\n\0" + const ListLogEntry entry = chunk->entries[i]; + const size_t snp_len = (size_t)snprintf( + buf, sizeof(buf), + "%-10.10s: l:%016" PRIxPTR "[%08d] 1:%016" PRIxPTR " 2:%016" PRIxPTR + "\n", + entry.action, entry.l, entry.len, entry.li1, entry.li2); + assert(snp_len + 1 == sizeof(buf)); + const ptrdiff_t fw_ret = file_write(&fp, buf, snp_len); + if (fw_ret != (ptrdiff_t)snp_len) { + assert(fw_ret < 0); + if (i) { + memmove(chunk->entries, chunk->entries + i, + sizeof(chunk->entries[0]) * (chunk->size - i)); + chunk->size -= i; + } + emsgf(_("E5143: Failed to write to file %s: %s"), + fname, os_strerror((int)fw_ret)); + return; + } + } + list_log_first = chunk->next; + xfree(chunk); + chunk = list_log_first; + } + const int fc_ret = file_close(&fp, true); + if (fc_ret != 0) { + emsgf(_("E5144: Failed to close file %s: %s"), fname, os_strerror(fc_ret)); + } +} + +#ifdef EXITFREE +/// Free list log +void list_free_log(void) +{ + for (ListLog *chunk = list_log_first; chunk != NULL;) { + list_log_first = chunk->next; + xfree(chunk); + chunk = list_log_first; + } +} +#endif +#endif //{{{2 List item /// Allocate a list item @@ -52,35 +118,29 @@ const char *const tv_empty_string = ""; /// and specifically set lv_lock. /// /// @return [allocated] new list item. -listitem_T *tv_list_item_alloc(void) +static listitem_T *tv_list_item_alloc(void) FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC { return xmalloc(sizeof(listitem_T)); } -/// Free a list item -/// -/// Also clears the value. Does not touch watchers. -/// -/// @param[out] item Item to free. -void tv_list_item_free(listitem_T *const item) - FUNC_ATTR_NONNULL_ALL -{ - tv_clear(&item->li_tv); - xfree(item); -} - /// Remove a list item from a List and free it /// /// Also clears the value. /// /// @param[out] l List to remove item from. /// @param[in,out] item Item to remove. -void tv_list_item_remove(list_T *const l, listitem_T *const item) +/// +/// @return Pointer to the list item just after removed one, NULL if removed +/// item was the last one. +listitem_T *tv_list_item_remove(list_T *const l, listitem_T *const item) FUNC_ATTR_NONNULL_ALL { - tv_list_remove_items(l, item, item); - tv_list_item_free(item); + listitem_T *const next_item = TV_LIST_ITEM_NEXT(l, item); + tv_list_drop_items(l, item, item); + tv_clear(TV_LIST_ITEM_TV(item)); + xfree(item); + return next_item; } //{{{2 List watchers @@ -137,8 +197,14 @@ void tv_list_watch_fix(list_T *const l, const listitem_T *const item) /// /// Caller should take care of the reference count. /// +/// @param[in] len Expected number of items to be populated before list +/// becomes accessible from VimL. It is still valid to +/// underpopulate a list, value only controls how many elements +/// will be allocated in advance. Currently does nothing. +/// @see ListLenSpecials. +/// /// @return [allocated] new list. -list_T *tv_list_alloc(void) +list_T *tv_list_alloc(const ptrdiff_t len) FUNC_ATTR_NONNULL_RET { list_T *const list = xcalloc(1, sizeof(list_T)); @@ -150,15 +216,59 @@ list_T *tv_list_alloc(void) list->lv_used_prev = NULL; list->lv_used_next = gc_first_list; gc_first_list = list; + list_log(list, NULL, (void *)(uintptr_t)len, "alloc"); return list; } +/// Initialize a static list with 10 items +/// +/// @param[out] sl Static list to initialize. +void tv_list_init_static10(staticList10_T *const sl) + FUNC_ATTR_NONNULL_ALL +{ +#define SL_SIZE ARRAY_SIZE(sl->sl_items) + list_T *const l = &sl->sl_list; + + memset(sl, 0, sizeof(staticList10_T)); + l->lv_first = &sl->sl_items[0]; + l->lv_last = &sl->sl_items[SL_SIZE - 1]; + l->lv_refcount = DO_NOT_FREE_CNT; + tv_list_set_lock(l, VAR_FIXED); + sl->sl_list.lv_len = 10; + + sl->sl_items[0].li_prev = NULL; + sl->sl_items[0].li_next = &sl->sl_items[1]; + sl->sl_items[SL_SIZE - 1].li_prev = &sl->sl_items[SL_SIZE - 2]; + sl->sl_items[SL_SIZE - 1].li_next = NULL; + + for (size_t i = 1; i < SL_SIZE - 1; i++) { + listitem_T *const li = &sl->sl_items[i]; + li->li_prev = li - 1; + li->li_next = li + 1; + } + list_log((const list_T *)sl, &sl->sl_items[0], &sl->sl_items[SL_SIZE - 1], + "s10init"); +#undef SL_SIZE +} + +/// Initialize static list with undefined number of elements +/// +/// @param[out] l List to initialize. +void tv_list_init_static(list_T *const l) + FUNC_ATTR_NONNULL_ALL +{ + memset(l, 0, sizeof(*l)); + l->lv_refcount = DO_NOT_FREE_CNT; + list_log(l, NULL, NULL, "sinit"); +} + /// Free items contained in a list /// /// @param[in,out] l List to clear. void tv_list_free_contents(list_T *const l) FUNC_ATTR_NONNULL_ALL { + list_log(l, NULL, NULL, "freecont"); for (listitem_T *item = l->lv_first; item != NULL; item = l->lv_first) { // Remove the item before deleting it. l->lv_first = item->li_next; @@ -188,6 +298,7 @@ void tv_list_free_list(list_T *const l) if (l->lv_used_next != NULL) { l->lv_used_next->lv_used_prev = l->lv_used_prev; } + list_log(l, NULL, NULL, "freelist"); xfree(l); } @@ -221,17 +332,18 @@ void tv_list_unref(list_T *const l) //{{{2 Add/remove -/// Remove items "item" to "item2" from list "l". +/// Remove items "item" to "item2" from list "l" /// /// @warning Does not free the listitem or the value! /// /// @param[out] l List to remove from. /// @param[in] item First item to remove. /// @param[in] item2 Last item to remove. -void tv_list_remove_items(list_T *const l, listitem_T *const item, - listitem_T *const item2) +void tv_list_drop_items(list_T *const l, listitem_T *const item, + listitem_T *const item2) FUNC_ATTR_NONNULL_ALL { + list_log(l, item, item2, "drop"); // Notify watchers. for (listitem_T *ip = item; ip != item2->li_next; ip = ip->li_next) { l->lv_len--; @@ -249,6 +361,51 @@ void tv_list_remove_items(list_T *const l, listitem_T *const item, item->li_prev->li_next = item2->li_next; } l->lv_idx_item = NULL; + list_log(l, l->lv_first, l->lv_last, "afterdrop"); +} + +/// Like tv_list_drop_items, but also frees all removed items +void tv_list_remove_items(list_T *const l, listitem_T *const item, + listitem_T *const item2) + FUNC_ATTR_NONNULL_ALL +{ + list_log(l, item, item2, "remove"); + tv_list_drop_items(l, item, item2); + for (listitem_T *li = item;;) { + tv_clear(TV_LIST_ITEM_TV(li)); + listitem_T *const nli = li->li_next; + xfree(li); + if (li == item2) { + break; + } + li = nli; + } +} + +/// Move items "item" to "item2" from list "l" to the end of the list "tgt_l" +/// +/// @param[out] l List to move from. +/// @param[in] item First item to move. +/// @param[in] item2 Last item to move. +/// @param[out] tgt_l List to move to. +/// @param[in] cnt Number of items moved. +void tv_list_move_items(list_T *const l, listitem_T *const item, + listitem_T *const item2, list_T *const tgt_l, + const int cnt) + FUNC_ATTR_NONNULL_ALL +{ + list_log(l, item, item2, "move"); + tv_list_drop_items(l, item, item2); + item->li_prev = tgt_l->lv_last; + item2->li_next = NULL; + if (tgt_l->lv_last == NULL) { + tgt_l->lv_first = item; + } else { + tgt_l->lv_last->li_next = item; + } + tgt_l->lv_last = item2; + tgt_l->lv_len += cnt; + list_log(tgt_l, tgt_l->lv_first, tgt_l->lv_last, "movetgt"); } /// Insert list item @@ -277,6 +434,7 @@ void tv_list_insert(list_T *const l, listitem_T *const ni, } item->li_prev = ni; l->lv_len++; + list_log(l, ni, item, "insert"); } } @@ -303,6 +461,7 @@ void tv_list_insert_tv(list_T *const l, typval_T *const tv, void tv_list_append(list_T *const l, listitem_T *const item) FUNC_ATTR_NONNULL_ALL { + list_log(l, item, NULL, "append"); if (l->lv_last == NULL) { // empty list l->lv_first = item; @@ -326,7 +485,19 @@ void tv_list_append_tv(list_T *const l, typval_T *const tv) FUNC_ATTR_NONNULL_ALL { listitem_T *const li = tv_list_item_alloc(); - tv_copy(tv, &li->li_tv); + tv_copy(tv, TV_LIST_ITEM_TV(li)); + tv_list_append(l, li); +} + +/// Like tv_list_append_tv(), but tv is moved to a list +/// +/// This means that it is no longer valid to use contents of the typval_T after +/// function exits. +void tv_list_append_owned_tv(list_T *const l, typval_T tv) + FUNC_ATTR_NONNULL_ALL +{ + listitem_T *const li = tv_list_item_alloc(); + *TV_LIST_ITEM_TV(li) = tv; tv_list_append(l, li); } @@ -334,33 +505,29 @@ void tv_list_append_tv(list_T *const l, typval_T *const tv) /// /// @param[out] l List to append to. /// @param[in,out] itemlist List to append. Reference count is increased. -void tv_list_append_list(list_T *const list, list_T *const itemlist) +void tv_list_append_list(list_T *const l, list_T *const itemlist) FUNC_ATTR_NONNULL_ARG(1) { - listitem_T *const li = tv_list_item_alloc(); - - li->li_tv.v_type = VAR_LIST; - li->li_tv.v_lock = VAR_UNLOCKED; - li->li_tv.vval.v_list = itemlist; - tv_list_append(list, li); - if (itemlist != NULL) { - itemlist->lv_refcount++; - } + tv_list_append_owned_tv(l, (typval_T) { + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval.v_list = itemlist, + }); + tv_list_ref(itemlist); } /// Append a dictionary to a list /// /// @param[out] l List to append to. /// @param[in,out] dict Dictionary to append. Reference count is increased. -void tv_list_append_dict(list_T *const list, dict_T *const dict) +void tv_list_append_dict(list_T *const l, dict_T *const dict) FUNC_ATTR_NONNULL_ARG(1) { - listitem_T *const li = tv_list_item_alloc(); - - li->li_tv.v_type = VAR_DICT; - li->li_tv.v_lock = VAR_UNLOCKED; - li->li_tv.vval.v_dict = dict; - tv_list_append(list, li); + tv_list_append_owned_tv(l, (typval_T) { + .v_type = VAR_DICT, + .v_lock = VAR_UNLOCKED, + .vval.v_dict = dict, + }); if (dict != NULL) { dict->dv_refcount++; } @@ -374,17 +541,18 @@ void tv_list_append_dict(list_T *const list, dict_T *const dict) /// case string is considered to be usual zero-terminated /// string or NULL “empty” string. void tv_list_append_string(list_T *const l, const char *const str, - const ptrdiff_t len) + const ssize_t len) FUNC_ATTR_NONNULL_ARG(1) { - if (str == NULL) { - assert(len == 0 || len == -1); - tv_list_append_allocated_string(l, NULL); - } else { - tv_list_append_allocated_string(l, (len >= 0 - ? xmemdupz(str, (size_t)len) - : xstrdup(str))); - } + tv_list_append_owned_tv(l, (typval_T) { + .v_type = VAR_STRING, + .v_lock = VAR_UNLOCKED, + .vval.v_string = (str == NULL + ? NULL + : (len >= 0 + ? xmemdupz(str, (size_t)len) + : xstrdup(str))), + }); } /// Append given string to the list @@ -396,12 +564,11 @@ void tv_list_append_string(list_T *const l, const char *const str, void tv_list_append_allocated_string(list_T *const l, char *const str) FUNC_ATTR_NONNULL_ARG(1) { - listitem_T *const li = tv_list_item_alloc(); - - tv_list_append(l, li); - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = VAR_UNLOCKED; - li->li_tv.vval.v_string = (char_u *)str; + tv_list_append_owned_tv(l, (typval_T) { + .v_type = VAR_STRING, + .v_lock = VAR_UNLOCKED, + .vval.v_string = (char_u *)str, + }); } /// Append number to the list @@ -411,11 +578,11 @@ void tv_list_append_allocated_string(list_T *const l, char *const str) /// listitem_T. void tv_list_append_number(list_T *const l, const varnumber_T n) { - listitem_T *const li = tv_list_item_alloc(); - li->li_tv.v_type = VAR_NUMBER; - li->li_tv.v_lock = VAR_UNLOCKED; - li->li_tv.vval.v_number = n; - tv_list_append(l, li); + tv_list_append_owned_tv(l, (typval_T) { + .v_type = VAR_NUMBER, + .v_lock = VAR_UNLOCKED, + .vval.v_number = n, + }); } //{{{2 Operations on the whole list @@ -438,34 +605,36 @@ list_T *tv_list_copy(const vimconv_T *const conv, list_T *const orig, return NULL; } - list_T *copy = tv_list_alloc(); + list_T *copy = tv_list_alloc(tv_list_len(orig)); + tv_list_ref(copy); if (copyID != 0) { // Do this before adding the items, because one of the items may // refer back to this list. orig->lv_copyID = copyID; orig->lv_copylist = copy; } - listitem_T *item; - for (item = orig->lv_first; item != NULL && !got_int; - item = item->li_next) { + TV_LIST_ITER(orig, item, { + if (got_int) { + break; + } listitem_T *const ni = tv_list_item_alloc(); if (deep) { - if (var_item_copy(conv, &item->li_tv, &ni->li_tv, deep, copyID) == FAIL) { + if (var_item_copy(conv, TV_LIST_ITEM_TV(item), TV_LIST_ITEM_TV(ni), + deep, copyID) == FAIL) { xfree(ni); - break; + goto tv_list_copy_error; } } else { - tv_copy(&item->li_tv, &ni->li_tv); + tv_copy(TV_LIST_ITEM_TV(item), TV_LIST_ITEM_TV(ni)); } tv_list_append(copy, ni); - } - copy->lv_refcount++; - if (item != NULL) { - tv_list_unref(copy); - copy = NULL; - } + }); return copy; + +tv_list_copy_error: + tv_list_unref(copy); + return NULL; } /// Extend first list with the second @@ -475,17 +644,17 @@ list_T *tv_list_copy(const vimconv_T *const conv, list_T *const orig, /// @param[in] bef If not NULL, extends before this item. void tv_list_extend(list_T *const l1, list_T *const l2, listitem_T *const bef) - FUNC_ATTR_NONNULL_ARG(1, 2) + FUNC_ATTR_NONNULL_ARG(1) { - int todo = l2->lv_len; + int todo = tv_list_len(l2); listitem_T *const befbef = (bef == NULL ? NULL : bef->li_prev); listitem_T *const saved_next = (befbef == NULL ? NULL : befbef->li_next); // We also quit the loop when we have inserted the original item count of // the list, avoid a hang when we extend a list with itself. - for (listitem_T *item = l2->lv_first - ; item != NULL && --todo >= 0 + for (listitem_T *item = tv_list_first(l2) + ; item != NULL && todo-- ; item = (item == befbef ? saved_next : item->li_next)) { - tv_list_insert_tv(l1, &item->li_tv, bef); + tv_list_insert_tv(l1, TV_LIST_ITEM_TV(item), bef); } } @@ -540,13 +709,15 @@ static int list_join_inner(garray_T *const gap, list_T *const l, { size_t sumlen = 0; bool first = true; - listitem_T *item; // Stringify each item in the list. - for (item = l->lv_first; item != NULL && !got_int; item = item->li_next) { + TV_LIST_ITER(l, item, { + if (got_int) { + break; + } char *s; size_t len; - s = encode_tv2echo(&item->li_tv, &len); + s = encode_tv2echo(TV_LIST_ITEM_TV(item), &len); if (s == NULL) { return FAIL; } @@ -557,7 +728,7 @@ static int list_join_inner(garray_T *const gap, list_T *const l, p->tofree = p->s = (char_u *)s; line_breakcheck(); - } + }); // Allocate result buffer with its total size, avoid re-allocation and // multiple copy operations. Add 2 for a tailing ']' and NUL. @@ -591,16 +762,16 @@ static int list_join_inner(garray_T *const gap, list_T *const l, /// /// @return OK in case of success, FAIL otherwise. int tv_list_join(garray_T *const gap, list_T *const l, const char *const sep) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ARG(1) { - if (l->lv_len < 1) { + if (!tv_list_len(l)) { return OK; } garray_T join_ga; int retval; - ga_init(&join_ga, (int)sizeof(Join), l->lv_len); + ga_init(&join_ga, (int)sizeof(Join), tv_list_len(l)); retval = list_join_inner(gap, l, sep, &join_ga); #define FREE_JOIN_TOFREE(join) xfree((join)->tofree) @@ -632,11 +803,13 @@ bool tv_list_equal(list_T *const l1, list_T *const l2, const bool ic, return false; } - listitem_T *item1 = l1->lv_first; - listitem_T *item2 = l2->lv_first; + listitem_T *item1 = tv_list_first(l1); + listitem_T *item2 = tv_list_first(l2); for (; item1 != NULL && item2 != NULL - ; item1 = item1->li_next, item2 = item2->li_next) { - if (!tv_equal(&item1->li_tv, &item2->li_tv, ic, recursive)) { + ; (item1 = TV_LIST_ITEM_NEXT(l1, item1), + item2 = TV_LIST_ITEM_NEXT(l2, item2))) { + if (!tv_equal(TV_LIST_ITEM_TV(item1), TV_LIST_ITEM_TV(item2), ic, + recursive)) { return false; } } @@ -644,6 +817,74 @@ bool tv_list_equal(list_T *const l1, list_T *const l2, const bool ic, return true; } +/// Reverse list in-place +/// +/// @param[in,out] l List to reverse. +void tv_list_reverse(list_T *const l) +{ + if (tv_list_len(l) <= 1) { + return; + } + list_log(l, NULL, NULL, "reverse"); +#define SWAP(a, b) \ + do { \ + tmp = a; \ + a = b; \ + b = tmp; \ + } while (0) + listitem_T *tmp; + + SWAP(l->lv_first, l->lv_last); + for (listitem_T *li = l->lv_first; li != NULL; li = li->li_next) { + SWAP(li->li_next, li->li_prev); + } +#undef SWAP + + l->lv_idx = l->lv_len - l->lv_idx - 1; +} + +// FIXME Add unit tests for tv_list_item_sort(). + +/// Sort list using libc qsort +/// +/// @param[in,out] l List to sort, will be sorted in-place. +/// @param ptrs Preallocated array of items to sort, must have at least +/// tv_list_len(l) entries. Should not be initialized. +/// @param[in] item_compare_func Function used to compare list items. +/// @param errp Location where information about whether error occurred is +/// saved by item_compare_func. If boolean there appears to be +/// true list will not be modified. Must be initialized to false +/// by the caller. +void tv_list_item_sort(list_T *const l, ListSortItem *const ptrs, + const ListSorter item_compare_func, + bool *errp) + FUNC_ATTR_NONNULL_ARG(3, 4) +{ + const int len = tv_list_len(l); + if (len <= 1) { + return; + } + list_log(l, NULL, NULL, "sort"); + int i = 0; + TV_LIST_ITER(l, li, { + ptrs[i].item = li; + ptrs[i].idx = i; + i++; + }); + // Sort the array with item pointers. + qsort(ptrs, (size_t)len, sizeof(ListSortItem), item_compare_func); + if (!(*errp)) { + // Clear the list and append the items in the sorted order. + l->lv_first = NULL; + l->lv_last = NULL; + l->lv_idx_item = NULL; + l->lv_len = 0; + for (i = 0; i < len; i++) { + tv_list_append(l, ptrs[i].item); + } + } +} + //{{{2 Indexing/searching /// Locate item with a given index in a list and return it @@ -662,13 +903,8 @@ listitem_T *tv_list_find(list_T *const l, int n) return NULL; } - // Negative index is relative to the end. - if (n < 0) { - n = l->lv_len + n; - } - - // Check for index out of range. - if (n < 0 || n >= l->lv_len) { + n = tv_list_uidx(l, n); + if (n == -1) { return NULL; } @@ -717,6 +953,7 @@ listitem_T *tv_list_find(list_T *const l, int n) // Cache the used index. l->lv_idx = idx; l->lv_idx_item = item; + list_log(l, l->lv_idx_item, (void *)(uintptr_t)l->lv_idx, "find"); return item; } @@ -740,7 +977,7 @@ varnumber_T tv_list_find_nr(list_T *const l, const int n, bool *const ret_error) } return -1; } - return tv_get_number_chk(&li->li_tv, ret_error); + return tv_get_number_chk(TV_LIST_ITEM_TV(li), ret_error); } /// Get list item l[n] as a string @@ -757,7 +994,7 @@ const char *tv_list_find_str(list_T *const l, const int n) emsgf(_(e_listidx), (int64_t)n); return NULL; } - return tv_get_string(&li->li_tv); + return tv_get_string(TV_LIST_ITEM_TV(li)); } /// Locate item in a list and return its index @@ -772,15 +1009,14 @@ long tv_list_idx_of_item(const list_T *const l, const listitem_T *const item) if (l == NULL) { return -1; } - long idx = 0; - const listitem_T *li; - for (li = l->lv_first; li != NULL && li != item; li = li->li_next) { + int idx = 0; + TV_LIST_ITER_CONST(l, li, { + if (li == item) { + return idx; + } idx++; - } - if (li == NULL) { - return -1; - } - return idx; + }); + return -1; } //{{{1 Dictionaries @@ -824,7 +1060,7 @@ void tv_dict_watcher_add(dict_T *const dict, const char *const key_pattern, /// @param[in] cb2 Second callback to check. /// /// @return True if they are equal, false otherwise. -bool tv_callback_equal(const Callback *const cb1, const Callback *const cb2) +bool tv_callback_equal(const Callback *cb1, const Callback *cb2) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { if (cb1->type != cb2->type) { @@ -843,10 +1079,31 @@ bool tv_callback_equal(const Callback *const cb1, const Callback *const cb2) return true; } } - assert(false); + abort(); return false; } +/// Unref/free callback +void callback_free(Callback *callback) + FUNC_ATTR_NONNULL_ALL +{ + switch (callback->type) { + case kCallbackFuncref: { + func_unref(callback->data.funcref); + xfree(callback->data.funcref); + break; + } + case kCallbackPartial: { + partial_unref(callback->data.partial); + break; + } + case kCallbackNone: { + break; + } + } + callback->type = kCallbackNone; +} + /// Remove watcher from a dictionary /// /// @param dict Dictionary to remove watcher from. @@ -1318,7 +1575,7 @@ int tv_dict_add_list(dict_T *const d, const char *const key, item->di_tv.v_lock = VAR_UNLOCKED; item->di_tv.v_type = VAR_LIST; item->di_tv.vval.v_list = list; - list->lv_refcount++; + tv_list_ref(list); if (tv_dict_add(d, item) == FAIL) { tv_dict_item_free(item); return FAIL; @@ -1374,6 +1631,29 @@ int tv_dict_add_nr(dict_T *const d, const char *const key, return OK; } +/// Add a special entry to dictionary +/// +/// @param[out] d Dictionary to add entry to. +/// @param[in] key Key to add. +/// @param[in] key_len Key length. +/// @param[in] val SpecialVarValue to add. +/// +/// @return OK in case of success, FAIL when key already exists. +int tv_dict_add_special(dict_T *const d, const char *const key, + const size_t key_len, SpecialVarValue val) +{ + dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); + + item->di_tv.v_lock = VAR_UNLOCKED; + item->di_tv.v_type = VAR_SPECIAL; + item->di_tv.vval.v_special = val; + if (tv_dict_add(d, item) == FAIL) { + tv_dict_item_free(item); + return FAIL; + } + return OK; +} + /// Add a string entry to dictionary /// /// @param[out] d Dictionary to add entry to. @@ -1387,11 +1667,32 @@ int tv_dict_add_str(dict_T *const d, const char *const val) FUNC_ATTR_NONNULL_ALL { + return tv_dict_add_allocated_str(d, key, key_len, xstrdup(val)); +} + +/// Add a string entry to dictionary +/// +/// Unlike tv_dict_add_str() saves val to the new dictionary item in place of +/// creating a new copy. +/// +/// @warning String will be freed even in case addition fails. +/// +/// @param[out] d Dictionary to add entry to. +/// @param[in] key Key to add. +/// @param[in] key_len Key length. +/// @param[in] val String to add. +/// +/// @return OK in case of success, FAIL when key already exists. +int tv_dict_add_allocated_str(dict_T *const d, + const char *const key, const size_t key_len, + char *const val) + FUNC_ATTR_NONNULL_ALL +{ dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); item->di_tv.v_lock = VAR_UNLOCKED; item->di_tv.v_type = VAR_STRING; - item->di_tv.vval.v_string = (char_u *)xstrdup(val); + item->di_tv.vval.v_string = (char_u *)val; if (tv_dict_add(d, item) == FAIL) { tv_dict_item_free(item); return FAIL; @@ -1603,16 +1904,20 @@ void tv_dict_set_keys_readonly(dict_T *const dict) /// Also sets reference count. /// /// @param[out] ret_tv Structure where list is saved. +/// @param[in] len Expected number of items to be populated before list +/// becomes accessible from VimL. It is still valid to +/// underpopulate a list, value only controls how many elements +/// will be allocated in advance. @see ListLenSpecials. /// /// @return [allocated] pointer to the created list. -list_T *tv_list_alloc_ret(typval_T *const ret_tv) +list_T *tv_list_alloc_ret(typval_T *const ret_tv, const ptrdiff_t len) FUNC_ATTR_NONNULL_ALL { - list_T *const l = tv_list_alloc(); + list_T *const l = tv_list_alloc(len); ret_tv->vval.v_list = l; ret_tv->v_type = VAR_LIST; ret_tv->v_lock = VAR_UNLOCKED; - l->lv_refcount++; + tv_list_ref(l); return l; } @@ -1729,14 +2034,20 @@ static inline void _nothing_conv_func_end(typval_T *const tv, const int copyID) tv->v_lock = VAR_UNLOCKED; \ } while (0) +static inline void _nothing_conv_empty_dict(typval_T *const tv, + dict_T **const dictp) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ARG(2) +{ + tv_dict_unref(*dictp); + *dictp = NULL; + if (tv != NULL) { + tv->v_lock = VAR_UNLOCKED; + } +} #define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ do { \ assert((void *)&dict != (void *)&TYPVAL_ENCODE_NODICT_VAR); \ - tv_dict_unref((dict_T *)dict); \ - *((dict_T **)&dict) = NULL; \ - if (tv != NULL) { \ - ((typval_T *)tv)->v_lock = VAR_UNLOCKED; \ - } \ + _nothing_conv_empty_dict(tv, ((dict_T **)&dict)); \ } while (0) static inline int _nothing_conv_real_list_after_start( @@ -1933,7 +2244,7 @@ void tv_free(typval_T *tv) /// /// @param[in] from Location to copy from. /// @param[out] to Location to copy to. -void tv_copy(typval_T *const from, typval_T *const to) +void tv_copy(const typval_T *const from, typval_T *const to) { to->v_type = from->v_type; to->v_lock = VAR_UNLOCKED; @@ -1961,9 +2272,7 @@ void tv_copy(typval_T *const from, typval_T *const to) break; } case VAR_LIST: { - if (from->vval.v_list != NULL) { - to->vval.v_list->lv_refcount++; - } + tv_list_ref(to->vval.v_list); break; } case VAR_DICT: { @@ -2019,9 +2328,9 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock) CHANGE_LOCK(lock, l->lv_lock); if (deep < 0 || deep > 1) { // Recursive: lock/unlock the items the List contains. - for (listitem_T *li = l->lv_first; li != NULL; li = li->li_next) { - tv_item_lock(&li->li_tv, deep - 1, lock); - } + TV_LIST_ITER(l, li, { + tv_item_lock(TV_LIST_ITEM_TV(li), deep - 1, lock); + }); } } break; @@ -2057,6 +2366,8 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock) /// Check whether VimL value is locked itself or refers to a locked container /// +/// @warning Fixed container is not the same as locked. +/// /// @param[in] tv Value to check. /// /// @return True if value is locked, false otherwise. @@ -2065,8 +2376,7 @@ bool tv_islocked(const typval_T *const tv) { return ((tv->v_lock == VAR_LOCKED) || (tv->v_type == VAR_LIST - && tv->vval.v_list != NULL - && (tv->vval.v_list->lv_lock == VAR_LOCKED)) + && (tv_list_locked(tv->vval.v_list) == VAR_LOCKED)) || (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL && (tv->vval.v_dict->dv_lock == VAR_LOCKED))); diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 3f8ed3b3f9..2272a580d6 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -6,6 +6,8 @@ #include <stdint.h> #include <string.h> #include <stdbool.h> +#include <assert.h> +#include <limits.h> #include "nvim/types.h" #include "nvim/hashtab.h" @@ -18,6 +20,9 @@ #include "nvim/gettext.h" #include "nvim/message.h" #include "nvim/macros.h" +#ifdef LOG_LIST_ACTIONS +# include "nvim/memory.h" +#endif /// Type used for VimL VAR_NUMBER values typedef int64_t varnumber_T; @@ -26,6 +31,28 @@ typedef uint64_t uvarnumber_T; /// Type used for VimL VAR_FLOAT values typedef double float_T; +/// Refcount for dict or list that should not be freed +enum { DO_NOT_FREE_CNT = (INT_MAX / 2) }; + +/// Additional values for tv_list_alloc() len argument +enum { + /// List length is not known in advance + /// + /// To be used when there is neither a way to know how many elements will be + /// needed nor are any educated guesses. + kListLenUnknown = -1, + /// List length *should* be known, but is actually not + /// + /// All occurrences of this value should be eventually removed. This is for + /// the case when the only reason why list length is not known is that it + /// would be hard to code without refactoring, but refactoring is needed. + kListLenShouldKnow = -2, + /// List length may be known in advance, but it requires too much effort + /// + /// To be used when it looks impractical to determine list length. + kListLenMayKnow = -3, +} ListLenSpecials; + /// Maximal possible value of varnumber_T variable #define VARNUMBER_MAX INT64_MAX #define UVARNUMBER_MAX UINT64_MAX @@ -43,7 +70,7 @@ typedef struct partial_S partial_T; typedef struct ufunc ufunc_T; typedef enum { - kCallbackNone, + kCallbackNone = 0, kCallbackFuncref, kCallbackPartial, } CallbackType; @@ -150,12 +177,26 @@ struct listvar_S { list_T *lv_used_prev; ///< Previous list in used lists list. }; -// Static list with 10 items. Use init_static_list() to initialize. +// Static list with 10 items. Use tv_list_init_static10() to initialize. typedef struct { list_T sl_list; // must be first listitem_T sl_items[10]; } staticList10_T; +#define TV_LIST_STATIC10_INIT { \ + .sl_list = { \ + .lv_first = NULL, \ + .lv_last = NULL, \ + .lv_refcount = 0, \ + .lv_len = 0, \ + .lv_watch = NULL, \ + .lv_idx_item = NULL, \ + .lv_lock = VAR_FIXED, \ + .lv_used_next = NULL, \ + .lv_used_prev = NULL, \ + }, \ + } + // Structure to hold an item of a Dictionary. // Also used for a variable. // The key is copied into "di_key" to avoid an extra alloc/free for it. @@ -165,11 +206,11 @@ struct dictitem_S { char_u di_key[1]; ///< key (actually longer!) }; -#define TV_DICTITEM_STRUCT(KEY_LEN) \ +#define TV_DICTITEM_STRUCT(...) \ struct { \ typval_T di_tv; /* Structure that holds scope dictionary itself. */ \ uint8_t di_flags; /* Flags. */ \ - char_u di_key[KEY_LEN]; /* Key value. */ \ + char_u di_key[__VA_ARGS__]; /* Key value. */ \ } /// Structure to hold a scope dictionary @@ -277,6 +318,104 @@ typedef struct list_stack_S { struct list_stack_S *prev; } list_stack_T; +/// Structure representing one list item, used for sort array. +typedef struct { + listitem_T *item; ///< Sorted list item. + int idx; ///< Sorted list item index. +} ListSortItem; + +typedef int (*ListSorter)(const void *, const void *); + +#ifdef LOG_LIST_ACTIONS + +/// List actions log entry +typedef struct { + uintptr_t l; ///< List log entry belongs to. + uintptr_t li1; ///< First list item log entry belongs to, if applicable. + uintptr_t li2; ///< Second list item log entry belongs to, if applicable. + int len; ///< List length when log entry was created. + const char *action; ///< Logged action. +} ListLogEntry; + +typedef struct list_log ListLog; + +/// List actions log +struct list_log { + ListLog *next; ///< Next chunk or NULL. + size_t capacity; ///< Number of entries in current chunk. + size_t size; ///< Current chunk size. + ListLogEntry entries[]; ///< Actual log entries. +}; + +extern ListLog *list_log_first; ///< First list log chunk, NULL if missing +extern ListLog *list_log_last; ///< Last list log chunk + +static inline ListLog *list_log_alloc(const size_t size) + REAL_FATTR_ALWAYS_INLINE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Allocate a new log chunk and update globals +/// +/// @param[in] size Number of entries in a new chunk. +/// +/// @return [allocated] Newly allocated chunk. +static inline ListLog *list_log_new(const size_t size) +{ + ListLog *ret = xmalloc(offsetof(ListLog, entries) + + size * sizeof(ret->entries[0])); + ret->size = 0; + ret->capacity = size; + ret->next = NULL; + if (list_log_first == NULL) { + list_log_first = ret; + } else { + list_log_last->next = ret; + } + list_log_last = ret; + return ret; +} + +static inline void list_log(const list_T *const l, + const listitem_T *const li1, + const listitem_T *const li2, + const char *const action) + REAL_FATTR_ALWAYS_INLINE; + +/// Add new entry to log +/// +/// If last chunk was filled it uses twice as much memory to allocate the next +/// chunk. +/// +/// @param[in] l List to which entry belongs. +/// @param[in] li1 List item 1. +/// @param[in] li2 List item 2, often used for integers and not list items. +/// @param[in] action Logged action. +static inline void list_log(const list_T *const l, + const listitem_T *const li1, + const listitem_T *const li2, + const char *const action) +{ + ListLog *tgt; + if (list_log_first == NULL) { + tgt = list_log_new(128); + } else if (list_log_last->size == list_log_last->capacity) { + tgt = list_log_new(list_log_last->capacity * 2); + } else { + tgt = list_log_last; + } + tgt->entries[tgt->size++] = (ListLogEntry) { + .l = (uintptr_t)l, + .li1 = (uintptr_t)li1, + .li2 = (uintptr_t)li2, + .len = (l == NULL ? 0 : l->lv_len), + .action = action, + }; +} +#else +# define list_log(...) +# define list_write_log(...) +# define list_free_log() +#endif + // In a hashtab item "hi_key" points to "di_key" in a dictitem. // This avoids adding a pointer to the hashtab item. @@ -284,20 +423,181 @@ typedef struct list_stack_S { #define TV_DICT_HI2DI(hi) \ ((dictitem_T *)((hi)->hi_key - offsetof(dictitem_T, di_key))) -static inline long tv_list_len(const list_T *const l) +/// Increase reference count for a given list +/// +/// Does nothing for NULL lists. +/// +/// @param[in] l List to modify. +static inline void tv_list_ref(list_T *const l) +{ + if (l == NULL) { + return; + } + l->lv_refcount++; +} + +static inline VarLockStatus tv_list_locked(const list_T *const l) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Get list lock status +/// +/// Returns VAR_FIXED for NULL lists. +/// +/// @param[in] l List to check. +static inline VarLockStatus tv_list_locked(const list_T *const l) +{ + if (l == NULL) { + return VAR_FIXED; + } + return l->lv_lock; +} + +/// Set list lock status +/// +/// May only “set” VAR_FIXED for NULL lists. +/// +/// @param[out] l List to modify. +/// @param[in] lock New lock status. +static inline void tv_list_set_lock(list_T *const l, + const VarLockStatus lock) +{ + if (l == NULL) { + assert(lock == VAR_FIXED); + return; + } + l->lv_lock = lock; +} + +/// Set list copyID +/// +/// Does not expect NULL list, be careful. +/// +/// @param[out] l List to modify. +/// @param[in] copyid New copyID. +static inline void tv_list_set_copyid(list_T *const l, + const int copyid) + FUNC_ATTR_NONNULL_ALL +{ + l->lv_copyID = copyid; +} + +static inline int tv_list_len(const list_T *const l) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; /// Get the number of items in a list /// /// @param[in] l List to check. -static inline long tv_list_len(const list_T *const l) +static inline int tv_list_len(const list_T *const l) { + list_log(l, NULL, NULL, "len"); if (l == NULL) { return 0; } return l->lv_len; } +static inline int tv_list_copyid(const list_T *const l) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL; + +/// Get list copyID +/// +/// Does not expect NULL list, be careful. +/// +/// @param[in] l List to check. +static inline int tv_list_copyid(const list_T *const l) +{ + return l->lv_copyID; +} + +static inline list_T *tv_list_latest_copy(const list_T *const l) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL; + +/// Get latest list copy +/// +/// Gets lv_copylist field assigned by tv_list_copy() earlier. +/// +/// Does not expect NULL list, be careful. +/// +/// @param[in] l List to check. +static inline list_T *tv_list_latest_copy(const list_T *const l) +{ + return l->lv_copylist; +} + +static inline int tv_list_uidx(const list_T *const l, int n) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Normalize index: that is, return either -1 or non-negative index +/// +/// @param[in] l List to index. Used to get length. +/// @param[in] n List index, possibly negative. +/// +/// @return -1 or list index in range [0, tv_list_len(l)). +static inline int tv_list_uidx(const list_T *const l, int n) +{ + // Negative index is relative to the end. + if (n < 0) { + n += tv_list_len(l); + } + + // Check for index out of range. + if (n < 0 || n >= tv_list_len(l)) { + return -1; + } + return n; +} + +static inline bool tv_list_has_watchers(const list_T *const l) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Check whether list has watchers +/// +/// E.g. is referenced by a :for loop. +/// +/// @param[in] l List to check. +/// +/// @return true if there are watchers, false otherwise. +static inline bool tv_list_has_watchers(const list_T *const l) +{ + return l && l->lv_watch; +} + +static inline listitem_T *tv_list_first(const list_T *const l) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Get first list item +/// +/// @param[in] l List to get item from. +/// +/// @return List item or NULL in case of an empty list. +static inline listitem_T *tv_list_first(const list_T *const l) +{ + if (l == NULL) { + list_log(l, NULL, NULL, "first"); + return NULL; + } + list_log(l, l->lv_first, NULL, "first"); + return l->lv_first; +} + +static inline listitem_T *tv_list_last(const list_T *const l) + REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; + +/// Get last list item +/// +/// @param[in] l List to get item from. +/// +/// @return List item or NULL in case of an empty list. +static inline listitem_T *tv_list_last(const list_T *const l) +{ + if (l == NULL) { + list_log(l, NULL, NULL, "last"); + return NULL; + } + list_log(l, l->lv_last, NULL, "last"); + return l->lv_last; +} + static inline long tv_dict_len(const dict_T *const d) REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; @@ -352,6 +652,76 @@ extern const char *const tv_empty_string; /// Specifies that free_unref_items() function has (not) been entered extern bool tv_in_free_unref_items; +/// Iterate over a list +/// +/// @param modifier Modifier: expected to be const or nothing, volatile should +/// also work if you have any uses for the volatile list. +/// @param[in] l List to iterate over. +/// @param li Name of the variable with current listitem_T entry. +/// @param code Cycle body. +#define _TV_LIST_ITER_MOD(modifier, l, li, code) \ + do { \ + modifier list_T *const l_ = (l); \ + list_log(l_, NULL, NULL, "iter" #modifier); \ + if (l_ != NULL) { \ + for (modifier listitem_T *li = l_->lv_first; \ + li != NULL; li = li->li_next) { \ + code \ + } \ + } \ + } while (0) + +/// Iterate over a list +/// +/// To be used when you need to modify list or values you iterate over, use +/// #TV_LIST_ITER_CONST if you don’t. +/// +/// @param[in] l List to iterate over. +/// @param li Name of the variable with current listitem_T entry. +/// @param code Cycle body. +#define TV_LIST_ITER(l, li, code) \ + _TV_LIST_ITER_MOD(, l, li, code) + +/// Iterate over a list +/// +/// To be used when you don’t need to modify list or values you iterate over, +/// use #TV_LIST_ITER if you do. +/// +/// @param[in] l List to iterate over. +/// @param li Name of the variable with current listitem_T entry. +/// @param code Cycle body. +#define TV_LIST_ITER_CONST(l, li, code) \ + _TV_LIST_ITER_MOD(const, l, li, code) + +// Below macros are macros to avoid duplicating code for functionally identical +// const and non-const function variants. + +/// Get typval_T out of list item +/// +/// @param[in] li List item to get typval_T from, must not be NULL. +/// +/// @return Pointer to typval_T. +#define TV_LIST_ITEM_TV(li) (&(li)->li_tv) + +/// Get next list item given the current one +/// +/// @param[in] l List to get item from. +/// @param[in] li List item to get typval_T from. +/// +/// @return Pointer to the next item or NULL. +#define TV_LIST_ITEM_NEXT(l, li) ((li)->li_next) + +/// Get previous list item given the current one +/// +/// @param[in] l List to get item from. +/// @param[in] li List item to get typval_T from. +/// +/// @return Pointer to the previous item or NULL. +#define TV_LIST_ITEM_PREV(l, li) ((li)->li_prev) +// List argument is not used currently, but it is a must for lists implemented +// as a pair (size(in list), array) without terminator - basically for lists on +// top of kvec. + /// Iterate over a dictionary /// /// @param[in] d Dictionary to iterate over. diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h index a93ad2dbba..f2d0d7265f 100644 --- a/src/nvim/eval/typval_encode.c.h +++ b/src/nvim/eval/typval_encode.c.h @@ -355,14 +355,14 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( break; } case VAR_LIST: { - if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) { + if (tv->vval.v_list == NULL || tv_list_len(tv->vval.v_list) == 0) { TYPVAL_ENCODE_CONV_EMPTY_LIST(tv); break; } - const int saved_copyID = tv->vval.v_list->lv_copyID; + const int saved_copyID = tv_list_copyid(tv->vval.v_list); _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, copyID, kMPConvList); - TYPVAL_ENCODE_CONV_LIST_START(tv, tv->vval.v_list->lv_len); + TYPVAL_ENCODE_CONV_LIST_START(tv, tv_list_len(tv->vval.v_list)); assert(saved_copyID != copyID && saved_copyID != copyID - 1); _mp_push(*mpstack, ((MPConvStackVal) { .type = kMPConvList, @@ -371,7 +371,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( .data = { .l = { .list = tv->vval.v_list, - .li = tv->vval.v_list->lv_first, + .li = tv_list_first(tv->vval.v_list), }, }, })); @@ -440,23 +440,43 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( // bits is not checked), other unsigned and have at most 31 // non-zero bits (number of bits is not checked). if (val_di->di_tv.v_type != VAR_LIST - || (val_list = val_di->di_tv.vval.v_list) == NULL - || val_list->lv_len != 4 - || val_list->lv_first->li_tv.v_type != VAR_NUMBER - || (sign = val_list->lv_first->li_tv.vval.v_number) == 0 - || val_list->lv_first->li_next->li_tv.v_type != VAR_NUMBER - || (highest_bits = - val_list->lv_first->li_next->li_tv.vval.v_number) < 0 - || val_list->lv_last->li_prev->li_tv.v_type != VAR_NUMBER - || (high_bits = - val_list->lv_last->li_prev->li_tv.vval.v_number) < 0 - || val_list->lv_last->li_tv.v_type != VAR_NUMBER - || (low_bits = val_list->lv_last->li_tv.vval.v_number) < 0) { + || tv_list_len(val_list = val_di->di_tv.vval.v_list) != 4) { goto _convert_one_value_regular_dict; } - uint64_t number = ((uint64_t)(((uint64_t)highest_bits) << 62) - | (uint64_t)(((uint64_t)high_bits) << 31) - | (uint64_t)low_bits); + + const listitem_T *const sign_li = tv_list_first(val_list); + if (TV_LIST_ITEM_TV(sign_li)->v_type != VAR_NUMBER + || (sign = TV_LIST_ITEM_TV(sign_li)->vval.v_number) == 0) { + goto _convert_one_value_regular_dict; + } + + const listitem_T *const highest_bits_li = ( + TV_LIST_ITEM_NEXT(val_list, sign_li)); + if (TV_LIST_ITEM_TV(highest_bits_li)->v_type != VAR_NUMBER + || ((highest_bits + = TV_LIST_ITEM_TV(highest_bits_li)->vval.v_number) + < 0)) { + goto _convert_one_value_regular_dict; + } + + const listitem_T *const high_bits_li = ( + TV_LIST_ITEM_NEXT(val_list, highest_bits_li)); + if (TV_LIST_ITEM_TV(high_bits_li)->v_type != VAR_NUMBER + || ((high_bits = TV_LIST_ITEM_TV(high_bits_li)->vval.v_number) + < 0)) { + goto _convert_one_value_regular_dict; + } + + const listitem_T *const low_bits_li = tv_list_last(val_list); + if (TV_LIST_ITEM_TV(low_bits_li)->v_type != VAR_NUMBER + || ((low_bits = TV_LIST_ITEM_TV(low_bits_li)->vval.v_number) + < 0)) { + goto _convert_one_value_regular_dict; + } + + const uint64_t number = ((uint64_t)(((uint64_t)highest_bits) << 62) + | (uint64_t)(((uint64_t)high_bits) << 31) + | (uint64_t)low_bits); if (sign > 0) { TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, number); } else { @@ -495,12 +515,12 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( if (val_di->di_tv.v_type != VAR_LIST) { goto _convert_one_value_regular_dict; } - const int saved_copyID = val_di->di_tv.vval.v_list->lv_copyID; + const int saved_copyID = tv_list_copyid(val_di->di_tv.vval.v_list); _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_di->di_tv.vval.v_list, lv_copyID, copyID, kMPConvList); - TYPVAL_ENCODE_CONV_LIST_START(tv, - val_di->di_tv.vval.v_list->lv_len); + TYPVAL_ENCODE_CONV_LIST_START( + tv, tv_list_len(val_di->di_tv.vval.v_list)); assert(saved_copyID != copyID && saved_copyID != copyID - 1); _mp_push(*mpstack, ((MPConvStackVal) { .tv = tv, @@ -509,7 +529,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( .data = { .l = { .list = val_di->di_tv.vval.v_list, - .li = val_di->di_tv.vval.v_list->lv_first, + .li = tv_list_first(val_di->di_tv.vval.v_list), }, }, })); @@ -520,22 +540,21 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( goto _convert_one_value_regular_dict; } list_T *const val_list = val_di->di_tv.vval.v_list; - if (val_list == NULL || val_list->lv_len == 0) { + if (val_list == NULL || tv_list_len(val_list) == 0) { TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, TYPVAL_ENCODE_NODICT_VAR); break; } - for (const listitem_T *li = val_list->lv_first; li != NULL; - li = li->li_next) { - if (li->li_tv.v_type != VAR_LIST - || li->li_tv.vval.v_list->lv_len != 2) { + TV_LIST_ITER_CONST(val_list, li, { + if (TV_LIST_ITEM_TV(li)->v_type != VAR_LIST + || tv_list_len(TV_LIST_ITEM_TV(li)->vval.v_list) != 2) { goto _convert_one_value_regular_dict; } - } - const int saved_copyID = val_di->di_tv.vval.v_list->lv_copyID; + }); + const int saved_copyID = tv_list_copyid(val_di->di_tv.vval.v_list); _TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(val_list, lv_copyID, copyID, kMPConvPairs); TYPVAL_ENCODE_CONV_DICT_START(tv, TYPVAL_ENCODE_NODICT_VAR, - val_list->lv_len); + tv_list_len(val_list)); assert(saved_copyID != copyID && saved_copyID != copyID - 1); _mp_push(*mpstack, ((MPConvStackVal) { .tv = tv, @@ -544,7 +563,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( .data = { .l = { .list = val_list, - .li = val_list->lv_first, + .li = tv_list_first(val_list), }, }, })); @@ -554,18 +573,23 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( const list_T *val_list; varnumber_T type; if (val_di->di_tv.v_type != VAR_LIST - || (val_list = val_di->di_tv.vval.v_list) == NULL - || val_list->lv_len != 2 - || (val_list->lv_first->li_tv.v_type != VAR_NUMBER) - || (type = val_list->lv_first->li_tv.vval.v_number) > INT8_MAX + || tv_list_len((val_list = val_di->di_tv.vval.v_list)) != 2 + || (TV_LIST_ITEM_TV(tv_list_first(val_list))->v_type + != VAR_NUMBER) + || ((type + = TV_LIST_ITEM_TV(tv_list_first(val_list))->vval.v_number) + > INT8_MAX) || type < INT8_MIN - || (val_list->lv_last->li_tv.v_type != VAR_LIST)) { + || (TV_LIST_ITEM_TV(tv_list_last(val_list))->v_type + != VAR_LIST)) { goto _convert_one_value_regular_dict; } size_t len; char *buf; - if (!encode_vim_list_to_buf(val_list->lv_last->li_tv.vval.v_list, - &len, &buf)) { + if (!( + encode_vim_list_to_buf( + TV_LIST_ITEM_TV(tv_list_last(val_list))->vval.v_list, &len, + &buf))) { goto _convert_one_value_regular_dict; } TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type); @@ -600,7 +624,7 @@ _convert_one_value_regular_dict: {} break; } case VAR_UNKNOWN: { - EMSG2(_(e_intern2), STR(_TYPVAL_ENCODE_CONVERT_ONE_VALUE) "()"); + internal_error(STR(_TYPVAL_ENCODE_CONVERT_ONE_VALUE) "()"); return FAIL; } } @@ -671,40 +695,45 @@ typval_encode_stop_converting_one_item: case kMPConvList: { if (cur_mpsv->data.l.li == NULL) { (void)_mp_pop(mpstack); - cur_mpsv->data.l.list->lv_copyID = cur_mpsv->saved_copyID; + tv_list_set_copyid(cur_mpsv->data.l.list, cur_mpsv->saved_copyID); TYPVAL_ENCODE_CONV_LIST_END(cur_mpsv->tv); continue; - } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { + } else if (cur_mpsv->data.l.li + != tv_list_first(cur_mpsv->data.l.list)) { TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(cur_mpsv->tv); } - tv = &cur_mpsv->data.l.li->li_tv; - cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; + tv = TV_LIST_ITEM_TV(cur_mpsv->data.l.li); + cur_mpsv->data.l.li = TV_LIST_ITEM_NEXT(cur_mpsv->data.l.list, + cur_mpsv->data.l.li); break; } case kMPConvPairs: { if (cur_mpsv->data.l.li == NULL) { (void)_mp_pop(mpstack); - cur_mpsv->data.l.list->lv_copyID = cur_mpsv->saved_copyID; + tv_list_set_copyid(cur_mpsv->data.l.list, cur_mpsv->saved_copyID); TYPVAL_ENCODE_CONV_DICT_END(cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR); continue; - } else if (cur_mpsv->data.l.li != cur_mpsv->data.l.list->lv_first) { + } else if (cur_mpsv->data.l.li + != tv_list_first(cur_mpsv->data.l.list)) { TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS( cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR); } - const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list; + const list_T *const kv_pair = ( + TV_LIST_ITEM_TV(cur_mpsv->data.l.li)->vval.v_list); TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK( - encode_vim_to__error_ret, kv_pair->lv_first->li_tv); - if (_TYPVAL_ENCODE_CONVERT_ONE_VALUE(TYPVAL_ENCODE_FIRST_ARG_NAME, - &mpstack, cur_mpsv, - &kv_pair->lv_first->li_tv, - copyID, - objname) == FAIL) { + encode_vim_to__error_ret, *TV_LIST_ITEM_TV(tv_list_first(kv_pair))); + if ( + _TYPVAL_ENCODE_CONVERT_ONE_VALUE( + TYPVAL_ENCODE_FIRST_ARG_NAME, &mpstack, cur_mpsv, + TV_LIST_ITEM_TV(tv_list_first(kv_pair)), copyID, objname) + == FAIL) { goto encode_vim_to__error_ret; } TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(cur_mpsv->tv, TYPVAL_ENCODE_NODICT_VAR); - tv = &kv_pair->lv_last->li_tv; - cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; + tv = TV_LIST_ITEM_TV(tv_list_last(kv_pair)); + cur_mpsv->data.l.li = TV_LIST_ITEM_NEXT(cur_mpsv->data.l.list, + cur_mpsv->data.l.li); break; } case kMPConvPartial: { diff --git a/src/nvim/event/libuv_process.c b/src/nvim/event/libuv_process.c index f6a567a520..0c2bf397e5 100644 --- a/src/nvim/event/libuv_process.c +++ b/src/nvim/event/libuv_process.c @@ -26,19 +26,22 @@ int libuv_process_spawn(LibuvProcess *uvproc) uvproc->uvopts.file = proc->argv[0]; uvproc->uvopts.args = proc->argv; uvproc->uvopts.flags = UV_PROCESS_WINDOWS_HIDE; - if (proc->detach) { - uvproc->uvopts.flags |= UV_PROCESS_DETACHED; - } #ifdef WIN32 // libuv collapses the argv to a CommandLineToArgvW()-style string. cmd.exe // expects a different syntax (must be prepared by the caller before now). if (os_shell_is_cmdexe(proc->argv[0])) { uvproc->uvopts.flags |= UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS; } + if (proc->detach) { + uvproc->uvopts.flags |= UV_PROCESS_DETACHED; + } +#else + // Always setsid() on unix-likes. #8107 + uvproc->uvopts.flags |= UV_PROCESS_DETACHED; #endif uvproc->uvopts.exit_cb = exit_cb; uvproc->uvopts.cwd = proc->cwd; - uvproc->uvopts.env = NULL; + uvproc->uvopts.env = NULL; // Inherits the parent (nvim) env. uvproc->uvopts.stdio = uvproc->uvstdio; uvproc->uvopts.stdio_count = 3; uvproc->uvstdio[0].flags = UV_IGNORE; @@ -46,22 +49,22 @@ int libuv_process_spawn(LibuvProcess *uvproc) uvproc->uvstdio[2].flags = UV_IGNORE; uvproc->uv.data = proc; - if (proc->in) { + if (!proc->in.closed) { uvproc->uvstdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; uvproc->uvstdio[0].data.stream = STRUCT_CAST(uv_stream_t, - &proc->in->uv.pipe); + &proc->in.uv.pipe); } - if (proc->out) { + if (!proc->out.closed) { uvproc->uvstdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; uvproc->uvstdio[1].data.stream = STRUCT_CAST(uv_stream_t, - &proc->out->uv.pipe); + &proc->out.uv.pipe); } - if (proc->err) { + if (!proc->err.closed) { uvproc->uvstdio[2].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; uvproc->uvstdio[2].data.stream = STRUCT_CAST(uv_stream_t, - &proc->err->uv.pipe); + &proc->err.uv.pipe); } int status; diff --git a/src/nvim/event/loop.c b/src/nvim/event/loop.c index 25701a1621..d92464f17b 100644 --- a/src/nvim/event/loop.c +++ b/src/nvim/event/loop.c @@ -30,19 +30,25 @@ void loop_init(Loop *loop, void *data) uv_signal_init(&loop->uv, &loop->children_watcher); uv_timer_init(&loop->uv, &loop->children_kill_timer); uv_timer_init(&loop->uv, &loop->poll_timer); + loop->poll_timer.data = xmalloc(sizeof(bool)); // "timeout expired" flag } -void loop_poll_events(Loop *loop, int ms) +/// Processes one `Loop.uv` event (at most). +/// Processes all `Loop.fast_events` events. +/// +/// @returns true if `ms` timeout was reached +bool loop_poll_events(Loop *loop, int ms) { if (loop->recursive++) { abort(); // Should not re-enter uv_run } uv_run_mode mode = UV_RUN_ONCE; + bool timeout_expired = false; if (ms > 0) { - // Use a repeating timeout of ms milliseconds to make sure - // we do not block indefinitely for I/O. + *((bool *)loop->poll_timer.data) = false; // reset "timeout expired" flag + // Dummy timer to ensure UV_RUN_ONCE does not block indefinitely for I/O. uv_timer_start(&loop->poll_timer, timer_cb, (uint64_t)ms, (uint64_t)ms); } else if (ms == 0) { // For ms == 0, do a non-blocking event poll. @@ -52,14 +58,23 @@ void loop_poll_events(Loop *loop, int ms) uv_run(&loop->uv, mode); if (ms > 0) { + timeout_expired = *((bool *)loop->poll_timer.data); uv_timer_stop(&loop->poll_timer); } loop->recursive--; // Can re-enter uv_run now multiqueue_process_events(loop->fast_events); + return timeout_expired; } -// Schedule an event from another thread +/// Schedules an event from another thread. +/// +/// @note Event is queued into `fast_events`, which is processed outside of the +/// primary `events` queue by loop_poll_events(). For `main_loop`, that +/// means `fast_events` is NOT processed in an "editor mode" +/// (VimState.execute), so redraw and other side-effects are likely to be +/// skipped. +/// @see loop_schedule_deferred void loop_schedule(Loop *loop, Event event) { uv_mutex_lock(&loop->mutex); @@ -68,6 +83,24 @@ void loop_schedule(Loop *loop, Event event) uv_mutex_unlock(&loop->mutex); } +/// Schedules an event from another thread. Unlike loop_schedule(), the event +/// is forwarded to `Loop.events`, instead of being processed immediately. +/// +/// @see loop_schedule +void loop_schedule_deferred(Loop *loop, Event event) +{ + Event *eventp = xmalloc(sizeof(*eventp)); + *eventp = event; + loop_schedule(loop, event_create(loop_deferred_event, 2, loop, eventp)); +} +static void loop_deferred_event(void **argv) +{ + Loop *loop = argv[0]; + Event *eventp = argv[1]; + multiqueue_put_event(loop->events, *eventp); + xfree(eventp); +} + void loop_on_put(MultiQueue *queue, void *data) { Loop *loop = data; @@ -86,7 +119,7 @@ bool loop_close(Loop *loop, bool wait) uv_mutex_destroy(&loop->mutex); uv_close((uv_handle_t *)&loop->children_watcher, NULL); uv_close((uv_handle_t *)&loop->children_kill_timer, NULL); - uv_close((uv_handle_t *)&loop->poll_timer, NULL); + uv_close((uv_handle_t *)&loop->poll_timer, timer_close_cb); uv_close((uv_handle_t *)&loop->async, NULL); uint64_t start = wait ? os_hrtime() : 0; while (true) { @@ -138,5 +171,11 @@ static void async_cb(uv_async_t *handle) static void timer_cb(uv_timer_t *handle) { + bool *timeout_expired = handle->data; + *timeout_expired = true; } +static void timer_close_cb(uv_handle_t *handle) +{ + xfree(handle->data); +} diff --git a/src/nvim/event/loop.h b/src/nvim/event/loop.h index e7d7bdd483..d1a40d5cc9 100644 --- a/src/nvim/event/loop.h +++ b/src/nvim/event/loop.h @@ -16,10 +16,27 @@ KLIST_INIT(WatcherPtr, WatcherPtr, _noop) typedef struct loop { uv_loop_t uv; - MultiQueue *events, *fast_events, *thread_events; + MultiQueue *events; + MultiQueue *thread_events; + // Immediate events: + // "Processed after exiting uv_run() (to avoid recursion), but before + // returning from loop_poll_events()." 502aee690c98 + // Practical consequence (for main_loop): these events are processed by + // state_enter()..os_inchar() + // whereas "regular" events (main_loop.events) are processed by + // state_enter()..VimState.execute() + // But state_enter()..os_inchar() can be "too early" if you want the event + // to trigger UI updates and other user-activity-related side-effects. + MultiQueue *fast_events; + + // used by process/job-control subsystem klist_t(WatcherPtr) *children; uv_signal_t children_watcher; - uv_timer_t children_kill_timer, poll_timer; + uv_timer_t children_kill_timer; + + // generic timer, used by loop_poll_events() + uv_timer_t poll_timer; + size_t children_stop_requests; uv_async_t async; uv_mutex_t mutex; diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c index cad49e2007..60650344ce 100644 --- a/src/nvim/event/process.c +++ b/src/nvim/event/process.c @@ -12,6 +12,7 @@ #include "nvim/event/wstream.h" #include "nvim/event/process.h" #include "nvim/event/libuv_process.h" +#include "nvim/os/process.h" #include "nvim/os/pty_process.h" #include "nvim/globals.h" #include "nvim/macros.h" @@ -21,32 +22,32 @@ # include "event/process.c.generated.h" #endif -// Time (ns) for a process to exit cleanly before we send TERM/KILL. -#define TERM_TIMEOUT 1000000000 -#define KILL_TIMEOUT (TERM_TIMEOUT * 2) - -#define CLOSE_PROC_STREAM(proc, stream) \ - do { \ - if (proc->stream && !proc->stream->closed) { \ - stream_close(proc->stream, NULL, NULL); \ - } \ - } while (0) +// Time for a process to exit cleanly before we send KILL. +// For pty processes SIGTERM is sent first (in case SIGHUP was not enough). +#define KILL_TIMEOUT_MS 2000 static bool process_is_tearing_down = false; /// @returns zero on success, or negative error code -int process_spawn(Process *proc) FUNC_ATTR_NONNULL_ALL +int process_spawn(Process *proc, bool in, bool out, bool err) + FUNC_ATTR_NONNULL_ALL { - if (proc->in) { - uv_pipe_init(&proc->loop->uv, &proc->in->uv.pipe, 0); + if (in) { + uv_pipe_init(&proc->loop->uv, &proc->in.uv.pipe, 0); + } else { + proc->in.closed = true; } - if (proc->out) { - uv_pipe_init(&proc->loop->uv, &proc->out->uv.pipe, 0); + if (out) { + uv_pipe_init(&proc->loop->uv, &proc->out.uv.pipe, 0); + } else { + proc->out.closed = true; } - if (proc->err) { - uv_pipe_init(&proc->loop->uv, &proc->err->uv.pipe, 0); + if (err) { + uv_pipe_init(&proc->loop->uv, &proc->err.uv.pipe, 0); + } else { + proc->err.closed = true; } int status; @@ -62,14 +63,14 @@ int process_spawn(Process *proc) FUNC_ATTR_NONNULL_ALL } if (status) { - if (proc->in) { - uv_close((uv_handle_t *)&proc->in->uv.pipe, NULL); + if (in) { + uv_close((uv_handle_t *)&proc->in.uv.pipe, NULL); } - if (proc->out) { - uv_close((uv_handle_t *)&proc->out->uv.pipe, NULL); + if (out) { + uv_close((uv_handle_t *)&proc->out.uv.pipe, NULL); } - if (proc->err) { - uv_close((uv_handle_t *)&proc->err->uv.pipe, NULL); + if (err) { + uv_close((uv_handle_t *)&proc->err.uv.pipe, NULL); } if (proc->type == kProcessTypeUv) { @@ -82,30 +83,27 @@ int process_spawn(Process *proc) FUNC_ATTR_NONNULL_ALL return status; } - if (proc->in) { - stream_init(NULL, proc->in, -1, - STRUCT_CAST(uv_stream_t, &proc->in->uv.pipe)); - proc->in->events = proc->events; - proc->in->internal_data = proc; - proc->in->internal_close_cb = on_process_stream_close; + if (in) { + stream_init(NULL, &proc->in, -1, + STRUCT_CAST(uv_stream_t, &proc->in.uv.pipe)); + proc->in.internal_data = proc; + proc->in.internal_close_cb = on_process_stream_close; proc->refcount++; } - if (proc->out) { - stream_init(NULL, proc->out, -1, - STRUCT_CAST(uv_stream_t, &proc->out->uv.pipe)); - proc->out->events = proc->events; - proc->out->internal_data = proc; - proc->out->internal_close_cb = on_process_stream_close; + if (out) { + stream_init(NULL, &proc->out, -1, + STRUCT_CAST(uv_stream_t, &proc->out.uv.pipe)); + proc->out.internal_data = proc; + proc->out.internal_close_cb = on_process_stream_close; proc->refcount++; } - if (proc->err) { - stream_init(NULL, proc->err, -1, - STRUCT_CAST(uv_stream_t, &proc->err->uv.pipe)); - proc->err->events = proc->events; - proc->err->internal_data = proc; - proc->err->internal_close_cb = on_process_stream_close; + if (err) { + stream_init(NULL, &proc->err, -1, + STRUCT_CAST(uv_stream_t, &proc->err.uv.pipe)); + proc->err.internal_data = proc; + proc->err.internal_close_cb = on_process_stream_close; proc->refcount++; } @@ -125,8 +123,6 @@ void process_teardown(Loop *loop) FUNC_ATTR_NONNULL_ALL // Close handles to process without killing it. CREATE_EVENT(loop->events, process_close_handles, 1, proc); } else { - uv_kill(proc->pid, SIGTERM); - proc->term_sent = true; process_stop(proc); } } @@ -138,27 +134,11 @@ void process_teardown(Loop *loop) FUNC_ATTR_NONNULL_ALL pty_process_teardown(loop); } -// Wrappers around `stream_close` that protect against double-closing. void process_close_streams(Process *proc) FUNC_ATTR_NONNULL_ALL { - process_close_in(proc); - process_close_out(proc); - process_close_err(proc); -} - -void process_close_in(Process *proc) FUNC_ATTR_NONNULL_ALL -{ - CLOSE_PROC_STREAM(proc, in); -} - -void process_close_out(Process *proc) FUNC_ATTR_NONNULL_ALL -{ - CLOSE_PROC_STREAM(proc, out); -} - -void process_close_err(Process *proc) FUNC_ATTR_NONNULL_ALL -{ - CLOSE_PROC_STREAM(proc, err); + stream_may_close(&proc->in); + stream_may_close(&proc->out); + stream_may_close(&proc->err); } /// Synchronously wait for a process to finish @@ -166,16 +146,14 @@ void process_close_err(Process *proc) FUNC_ATTR_NONNULL_ALL /// @param process Process instance /// @param ms Time in milliseconds to wait for the process. /// 0 for no wait. -1 to wait until the process quits. -/// @return Exit code of the process. +/// @return Exit code of the process. proc->status will have the same value. /// -1 if the timeout expired while the process is still running. /// -2 if the user interruped the wait. int process_wait(Process *proc, int ms, MultiQueue *events) FUNC_ATTR_NONNULL_ARG(1) { - int status = -1; // default - bool interrupted = false; if (!proc->refcount) { - status = proc->status; + int status = proc->status; LOOP_PROCESS_EVENTS(proc->loop, proc->events, 0); return status; } @@ -195,7 +173,6 @@ int process_wait(Process *proc, int ms, MultiQueue *events) // we'll assume that a user frantically hitting interrupt doesn't like // the current job. Signal that it has to be killed. if (got_int) { - interrupted = true; got_int = false; process_stop(proc); if (ms == -1) { @@ -206,12 +183,13 @@ int process_wait(Process *proc, int ms, MultiQueue *events) } else { LOOP_PROCESS_EVENTS(proc->loop, events, 0); } + + proc->status = -2; } if (proc->refcount == 1) { // Job exited, collect status and manually invoke close_cb to free the job // resources - status = interrupted ? -2 : proc->status; decref(proc); if (events) { // the decref call created an exit event, process it now @@ -221,7 +199,7 @@ int process_wait(Process *proc, int ms, MultiQueue *events) proc->refcount--; } - return status; + return proc->status; } /// Ask a process to terminate and eventually kill if it doesn't respond @@ -237,7 +215,8 @@ void process_stop(Process *proc) FUNC_ATTR_NONNULL_ALL // Close the process's stdin. If the process doesn't close its own // stdout/stderr, they will be closed when it exits(possibly due to being // terminated after a timeout) - process_close_in(proc); + stream_may_close(&proc->in); + os_proc_tree_kill(proc->pid, SIGTERM); break; case kProcessTypePty: // close all streams for pty processes to send SIGHUP to the process @@ -251,9 +230,10 @@ void process_stop(Process *proc) FUNC_ATTR_NONNULL_ALL Loop *loop = proc->loop; if (!loop->children_stop_requests++) { // When there's at least one stop request pending, start a timer that - // will periodically check if a signal should be send to a to the job - DLOG("Starting job kill timer"); - uv_timer_start(&loop->children_kill_timer, children_kill_cb, 100, 100); + // will periodically check if a signal should be send to the job. + ILOG("starting job kill timer"); + uv_timer_start(&loop->children_kill_timer, children_kill_cb, + KILL_TIMEOUT_MS, KILL_TIMEOUT_MS); } } @@ -269,15 +249,13 @@ static void children_kill_cb(uv_timer_t *handle) if (!proc->stopped_time) { continue; } - uint64_t elapsed = now - proc->stopped_time; - - if (!proc->term_sent && elapsed >= TERM_TIMEOUT) { - ILOG("Sending SIGTERM to pid %d", proc->pid); - uv_kill(proc->pid, SIGTERM); - proc->term_sent = true; - } else if (elapsed >= KILL_TIMEOUT) { - ILOG("Sending SIGKILL to pid %d", proc->pid); - uv_kill(proc->pid, SIGKILL); + uint64_t elapsed = (now - proc->stopped_time) / 1000000 + 1; + + if (elapsed >= KILL_TIMEOUT_MS) { + int sig = proc->type == kProcessTypePty && elapsed < KILL_TIMEOUT_MS * 2 + ? SIGTERM + : SIGKILL; + os_proc_tree_kill(proc->pid, sig); } } } @@ -324,6 +302,13 @@ static void process_close(Process *proc) } assert(!proc->closed); proc->closed = true; + + if (proc->detach) { + if (proc->type == kProcessTypeUv) { + uv_unref((uv_handle_t *)&(((LibuvProcess *)proc)->uv)); + } + } + switch (proc->type) { case kProcessTypeUv: libuv_process_close((LibuvProcess *)proc); @@ -368,15 +353,15 @@ static void flush_stream(Process *proc, Stream *stream) // Poll for data and process the generated events. loop_poll_events(proc->loop, 0); - if (proc->events) { - multiqueue_process_events(proc->events); + if (stream->events) { + multiqueue_process_events(stream->events); } // Stream can be closed if it is empty. if (num_bytes == stream->num_bytes) { - if (stream->read_cb) { + if (stream->read_cb && !stream->did_eof) { // Stream callback could miss EOF handling if a child keeps the stream - // open. + // open. But only send EOF if we haven't already. stream->read_cb(stream, stream->buffer, 0, stream->cb_data, true); } break; @@ -388,8 +373,8 @@ static void process_close_handles(void **argv) { Process *proc = argv[0]; - flush_stream(proc, proc->out); - flush_stream(proc, proc->err); + flush_stream(proc, &proc->out); + flush_stream(proc, &proc->err); process_close_streams(proc); process_close(proc); diff --git a/src/nvim/event/process.h b/src/nvim/event/process.h index 26d70a5e6d..033ce3604b 100644 --- a/src/nvim/event/process.h +++ b/src/nvim/event/process.h @@ -23,13 +23,14 @@ struct process { uint64_t stopped_time; const char *cwd; char **argv; - Stream *in, *out, *err; + Stream in, out, err; process_exit_cb cb; internal_process_cb internal_exit_cb, internal_close_cb; - bool closed, term_sent, detach; + bool closed, detach; MultiQueue *events; }; + static inline Process process_init(Loop *loop, ProcessType type, void *data) { return (Process) { @@ -38,23 +39,27 @@ static inline Process process_init(Loop *loop, ProcessType type, void *data) .loop = loop, .events = NULL, .pid = 0, - .status = 0, + .status = -1, .refcount = 0, .stopped_time = 0, .cwd = NULL, .argv = NULL, - .in = NULL, - .out = NULL, - .err = NULL, + .in = { .closed = false }, + .out = { .closed = false }, + .err = { .closed = false }, .cb = NULL, .closed = false, - .term_sent = false, .internal_close_cb = NULL, .internal_exit_cb = NULL, .detach = false }; } +static inline bool process_is_stopped(Process *proc) +{ + return proc->stopped_time != 0; +} + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "event/process.h.generated.h" #endif diff --git a/src/nvim/event/rstream.c b/src/nvim/event/rstream.c index 854af474b2..2fbe7f6773 100644 --- a/src/nvim/event/rstream.c +++ b/src/nvim/event/rstream.c @@ -95,7 +95,7 @@ static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf) // `uv_buf_t.len` happens to have different size on Windows. size_t write_count; buf->base = rbuffer_write_ptr(stream->buffer, &write_count); - buf->len = write_count; + buf->len = UV_BUF_LEN(write_count); } // Callback invoked by libuv after it copies the data into the buffer provided @@ -105,19 +105,19 @@ static void read_cb(uv_stream_t *uvstream, ssize_t cnt, const uv_buf_t *buf) { Stream *stream = uvstream->data; - if (cnt > 0) { - stream->num_bytes += (size_t)cnt; - } - if (cnt <= 0) { - if (cnt != UV_ENOBUFS - // cnt == 0 means libuv asked for a buffer and decided it wasn't needed: - // http://docs.libuv.org/en/latest/stream.html#c.uv_read_start. - // - // We don't need to do anything with the RBuffer because the next call - // to `alloc_cb` will return the same unused pointer(`rbuffer_produced` - // won't be called) - && cnt != 0) { + // cnt == 0 means libuv asked for a buffer and decided it wasn't needed: + // http://docs.libuv.org/en/latest/stream.html#c.uv_read_start. + // + // We don't need to do anything with the RBuffer because the next call + // to `alloc_cb` will return the same unused pointer(`rbuffer_produced` + // won't be called) + if (cnt == UV_ENOBUFS || cnt == 0) { + return; + } else if (cnt == UV_EOF && uvstream->type == UV_TTY) { + // The TTY driver might signal TTY without closing the stream + invoke_read_cb(stream, 0, true); + } else { DLOG("Closing Stream (%p): %s (%s)", stream, uv_err_name((int)cnt), os_strerror((int)cnt)); // Read error or EOF, either way stop the stream and invoke the callback @@ -130,6 +130,7 @@ static void read_cb(uv_stream_t *uvstream, ssize_t cnt, const uv_buf_t *buf) // at this point we're sure that cnt is positive, no error occurred size_t nread = (size_t)cnt; + stream->num_bytes += nread; // Data was already written, so all we need is to update 'wpos' to reflect // the space actually used in the buffer. rbuffer_produced(stream->buffer, nread); @@ -145,7 +146,7 @@ static void fread_idle_cb(uv_idle_t *handle) // `uv_buf_t.len` happens to have different size on Windows. size_t write_count; stream->uvbuf.base = rbuffer_write_ptr(stream->buffer, &write_count); - stream->uvbuf.len = write_count; + stream->uvbuf.len = UV_BUF_LEN(write_count); // the offset argument to uv_fs_read is int64_t, could someone really try // to read more than 9 quintillion (9e18) bytes? @@ -187,6 +188,7 @@ static void read_event(void **argv) if (stream->read_cb) { size_t count = (uintptr_t)argv[1]; bool eof = (uintptr_t)argv[2]; + stream->did_eof = eof; stream->read_cb(stream, stream->buffer, count, stream->cb_data, eof); } stream->pending_reqs--; diff --git a/src/nvim/event/socket.c b/src/nvim/event/socket.c index a796f303ab..6f45b09fce 100644 --- a/src/nvim/event/socket.c +++ b/src/nvim/event/socket.c @@ -105,9 +105,10 @@ int socket_watcher_start(SocketWatcher *watcher, int backlog, socket_cb cb) // contain 0 in this case, unless uv_tcp_getsockname() is used first. uv_tcp_getsockname(&watcher->uv.tcp.handle, (struct sockaddr *)&sas, &(int){ sizeof(sas) }); - uint16_t port = (uint16_t)((sas.ss_family == AF_INET) - ? ((struct sockaddr_in *)&sas)->sin_port - : ((struct sockaddr_in6 *)&sas)->sin6_port); + uint16_t port = (uint16_t)( + (sas.ss_family == AF_INET) + ? (STRUCT_CAST(struct sockaddr_in, &sas))->sin_port + : (STRUCT_CAST(struct sockaddr_in6, &sas))->sin6_port); // v:servername uses the string from watcher->addr size_t len = strlen(watcher->addr); snprintf(watcher->addr+len, sizeof(watcher->addr)-len, ":%" PRIu16, @@ -247,7 +248,7 @@ tcp_retry: uv_pipe_t *pipe = &stream->uv.pipe; uv_pipe_init(&loop->uv, pipe, 0); uv_pipe_connect(&req, pipe, address, connect_cb); - uv_stream = (uv_stream_t *)pipe; + uv_stream = STRUCT_CAST(uv_stream_t, pipe); } status = 1; LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, timeout, status != 1); diff --git a/src/nvim/event/stream.c b/src/nvim/event/stream.c index 60ceff9b24..ba25b76ec7 100644 --- a/src/nvim/event/stream.c +++ b/src/nvim/event/stream.c @@ -7,6 +7,7 @@ #include <uv.h> +#include "nvim/log.h" #include "nvim/rbuffer.h" #include "nvim/macros.h" #include "nvim/event/stream.h" @@ -81,6 +82,7 @@ void stream_close(Stream *stream, stream_close_cb on_stream_close, void *data) FUNC_ATTR_NONNULL_ARG(1) { assert(!stream->closed); + DLOG("closing Stream: %p", stream); stream->closed = true; stream->close_cb = on_stream_close; stream->close_cb_data = data; @@ -90,6 +92,13 @@ void stream_close(Stream *stream, stream_close_cb on_stream_close, void *data) } } +void stream_may_close(Stream *stream) +{ + if (!stream->closed) { + stream_close(stream, NULL, NULL); + } +} + void stream_close_handle(Stream *stream) FUNC_ATTR_NONNULL_ALL { diff --git a/src/nvim/event/stream.h b/src/nvim/event/stream.h index d27497e4a4..e713323f5c 100644 --- a/src/nvim/event/stream.h +++ b/src/nvim/event/stream.h @@ -14,10 +14,7 @@ typedef struct stream Stream; /// /// @param stream The Stream instance /// @param rbuffer The associated RBuffer instance -/// @param count Number of bytes to read. This must be respected if keeping -/// the order of events is a requirement. This is because events -/// may be queued and only processed later when more data is copied -/// into to the buffer, so one read may starve another. +/// @param count Number of bytes that was read. /// @param data User-defined data /// @param eof If the stream reached EOF. typedef void (*stream_read_cb)(Stream *stream, RBuffer *buf, size_t count, @@ -33,6 +30,8 @@ typedef void (*stream_write_cb)(Stream *stream, void *data, int status); typedef void (*stream_close_cb)(Stream *stream, void *data); struct stream { + bool closed; + bool did_eof; union { uv_pipe_t pipe; uv_tcp_t tcp; @@ -52,7 +51,6 @@ struct stream { size_t maxmem; size_t pending_reqs; size_t num_bytes; - bool closed; MultiQueue *events; }; diff --git a/src/nvim/event/wstream.c b/src/nvim/event/wstream.c index f453e5898d..d2fb52243c 100644 --- a/src/nvim/event/wstream.c +++ b/src/nvim/event/wstream.c @@ -8,6 +8,7 @@ #include <uv.h> +#include "nvim/log.h" #include "nvim/event/loop.h" #include "nvim/event/wstream.h" #include "nvim/vim.h" @@ -89,7 +90,7 @@ bool wstream_write(Stream *stream, WBuffer *buffer) uv_buf_t uvbuf; uvbuf.base = buffer->data; - uvbuf.len = buffer->size; + uvbuf.len = UV_BUF_LEN(buffer->size); if (uv_write(&data->uv_req, stream->uvstream, &uvbuf, 1, write_cb)) { xfree(data); diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 8dcb3ac449..c396b5891a 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -90,14 +90,20 @@ typedef struct { SubIgnoreType do_ic; ///< ignore case flag } subflags_T; -/// Lines matched during :substitute. +/// Partial result of a substitution during :substitute. +/// Numbers refer to the buffer _after_ substitution typedef struct { - linenr_T lnum; - long nmatch; - char_u *line; - kvec_t(colnr_T) cols; ///< columns of in-line matches -} MatchedLine; -typedef kvec_t(MatchedLine) MatchedLineVec; + lpos_T start; // start of the match + lpos_T end; // end of the match + linenr_T pre_match; // where to begin showing lines before the match +} SubResult; + +// Collected results of a substitution for showing them in +// the preview window +typedef struct { + kvec_t(SubResult) subresults; + linenr_T lines_needed; // lines neede in the preview window +} PreviewLines; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_cmds.c.generated.h" @@ -1169,7 +1175,7 @@ static void do_filter( // to read the error messages. Otherwise errors are ignored, so you can see // the error messages from the command that appear on stdout; use 'u' to fix // the text. - // Pass on the kShellDoOut flag when the output is being redirected. + // Pass on the kShellOptDoOut flag when the output is being redirected. if (call_shell( cmd_buf, kShellOptFilter | shell_flags, @@ -1411,7 +1417,7 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp) #else // For shells that don't understand braces around commands, at least allow // the use of commands in a pipe. - xstrlcpy(buf, cmd, len); + xstrlcpy(buf, (char *)cmd, len); if (itmp != NULL) { // If there is a pipe, we have to put the '<' in front of it. // Don't do this when 'shellquote' is not empty, otherwise the @@ -2007,9 +2013,10 @@ int getfile(int fnum, char_u *ffname, char_u *sfname, int setpm, linenr_T lnum, } else other = (fnum != curbuf->b_fnum); - if (other) - ++no_wait_return; /* don't wait for autowrite message */ - if (other && !forceit && curbuf->b_nwindows == 1 && !P_HID(curbuf) + if (other) { + no_wait_return++; // don't wait for autowrite message + } + if (other && !forceit && curbuf->b_nwindows == 1 && !buf_hide(curbuf) && curbufIsChanged() && autowrite(curbuf, forceit) == FAIL) { if (p_confirm && p_write) dialog_changed(curbuf, FALSE); @@ -2026,17 +2033,19 @@ int getfile(int fnum, char_u *ffname, char_u *sfname, int setpm, linenr_T lnum, if (setpm) setpcmark(); if (!other) { - if (lnum != 0) + if (lnum != 0) { curwin->w_cursor.lnum = lnum; + } check_cursor_lnum(); beginline(BL_SOL | BL_FIX); - retval = 0; /* it's in the same file */ + retval = 0; // it's in the same file } else if (do_ecmd(fnum, ffname, sfname, NULL, lnum, - (P_HID(curbuf) ? ECMD_HIDE : 0) + (forceit ? ECMD_FORCEIT : 0), - curwin) == OK) - retval = -1; /* opened another file */ - else - retval = 1; /* error encountered */ + (buf_hide(curbuf) ? ECMD_HIDE : 0) + + (forceit ? ECMD_FORCEIT : 0), curwin) == OK) { + retval = -1; // opened another file + } else { + retval = 1; // error encountered + } theend: xfree(free_me); @@ -2802,16 +2811,18 @@ void ex_z(exarg_T *eap) int j; linenr_T lnum = eap->line2; - /* Vi compatible: ":z!" uses display height, without a count uses - * 'scroll' */ - if (eap->forceit) + // Vi compatible: ":z!" uses display height, without a count uses + // 'scroll' + if (eap->forceit) { bigness = curwin->w_height; - else if (firstwin == lastwin) + } else if (ONE_WINDOW) { bigness = curwin->w_p_scr * 2; - else + } else { bigness = curwin->w_height - 3; - if (bigness < 1) + } + if (bigness < 1) { bigness = 1; + } x = eap->arg; kind = x; @@ -3166,7 +3177,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) linenr_T old_line_count = curbuf->b_ml.ml_line_count; char_u *sub_firstline; // allocated copy of first sub line bool endcolumn = false; // cursor in last column when done - MatchedLineVec matched_lines = KV_INITIAL_VALUE; + PreviewLines preview_lines = { KV_INITIAL_VALUE, 0 }; + static int pre_src_id = 0; // Source id for the preview highlight + static int pre_hl_id = 0; + buf_T *orig_buf = curbuf; // save to reset highlighting pos_T old_cursor = curwin->w_cursor; int start_nsubs; int save_ma = 0; @@ -3330,9 +3344,12 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) sub = regtilde(sub, p_magic); // Check for a match on each line. + // If preview: limit to max('cmdwinheight', viewport). linenr_T line2 = eap->line2; for (linenr_T lnum = eap->line1; - lnum <= line2 && !(got_quit || aborting()); + lnum <= line2 && !got_quit && !aborting() + && (!preview || preview_lines.lines_needed <= (linenr_T)p_cwh + || lnum <= curwin->w_botline); lnum++) { long nmatch = vim_regexec_multi(®match, curwin, curbuf, lnum, (colnr_T)0, NULL); @@ -3396,8 +3413,6 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) sub_firstlnum = lnum; copycol = 0; matchcol = 0; - // the current match - MatchedLine matched_line = { 0, 0, NULL, KV_INITIAL_VALUE }; /* At first match, remember current cursor position. */ if (!got_match) { @@ -3414,10 +3429,19 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) * 5. break if there isn't another match in this line */ for (;; ) { - /* Advance "lnum" to the line where the match starts. The - * match does not start in the first line when there is a line - * break before \zs. */ + SubResult current_match = { + .start = { 0, 0 }, + .end = { 0, 0 }, + .pre_match = 0, + }; + // lnum is where the match start, but maybe not the pattern match, + // since we can have \n before \zs in the pattern + + // Advance "lnum" to the line where the match starts. The + // match does not start in the first line when there is a line + // break before \zs. if (regmatch.startpos[0].lnum > 0) { + current_match.pre_match = lnum; lnum += regmatch.startpos[0].lnum; sub_firstlnum += regmatch.startpos[0].lnum; nmatch -= regmatch.startpos[0].lnum; @@ -3425,6 +3449,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) sub_firstline = NULL; } + // Now we're at the line where the pattern match starts + // Note: If not first match on a line, column can't be known here + current_match.start.lnum = sub_firstlnum; + if (sub_firstline == NULL) { sub_firstline = vim_strsave(ml_get(sub_firstlnum)); } @@ -3434,12 +3462,6 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) curwin->w_cursor.lnum = lnum; do_again = FALSE; - if (preview) { - // Increment the in-line match count and store the column. - matched_line.nmatch++; - kv_push(matched_line.cols, regmatch.startpos[0].col); - } - /* * 1. Match empty string does not count, except for first * match. This reproduces the strange vi behaviour. @@ -3459,6 +3481,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) else ++matchcol; } + // match will be pushed to preview_lines, bring it into a proper state + current_match.start.col = matchcol; + current_match.end.lnum = sub_firstlnum; + current_match.end.col = matchcol; goto skip; } @@ -3497,6 +3523,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) setmouse(); /* disable mouse in xterm */ curwin->w_cursor.col = regmatch.startpos[0].col; + if (curwin->w_p_crb) { + do_check_cursorbind(); + } + /* When 'cpoptions' contains "u" don't sync undo when * asking for confirmation. */ if (vim_strchr(p_cpo, CPO_UNDO) != NULL) @@ -3514,6 +3544,9 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) getvcol(curwin, &curwin->w_cursor, &sc, NULL, NULL); curwin->w_cursor.col = regmatch.endpos[0].col - 1; + if (curwin->w_cursor.col < 0) { + curwin->w_cursor.col = 0; + } getvcol(curwin, &curwin->w_cursor, NULL, NULL, &ec); if (subflags.do_number || curwin->w_p_nu) { int numw = number_width(curwin) + 1; @@ -3656,6 +3689,54 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) * use "\=col("."). */ curwin->w_cursor.col = regmatch.startpos[0].col; + // When the match included the "$" of the last line it may + // go beyond the last line of the buffer. + if (nmatch > curbuf->b_ml.ml_line_count - sub_firstlnum + 1) { + nmatch = curbuf->b_ml.ml_line_count - sub_firstlnum + 1; + current_match.end.lnum = sub_firstlnum + nmatch; + skip_match = true; + } + +#define ADJUST_SUB_FIRSTLNUM() \ + do { \ + /* For a multi-line match, make a copy of the last matched */ \ + /* line and continue in that one. */ \ + if (nmatch > 1) { \ + sub_firstlnum += nmatch - 1; \ + xfree(sub_firstline); \ + sub_firstline = vim_strsave(ml_get(sub_firstlnum)); \ + /* When going beyond the last line, stop substituting. */ \ + if (sub_firstlnum <= line2) { \ + do_again = true; \ + } else { \ + subflags.do_all = false; \ + } \ + } \ + if (skip_match) { \ + /* Already hit end of the buffer, sub_firstlnum is one */ \ + /* less than what it ought to be. */ \ + xfree(sub_firstline); \ + sub_firstline = vim_strsave((char_u *)""); \ + copycol = 0; \ + } \ + } while (0) + + // Save the line numbers for the preview buffer + // NOTE: If the pattern matches a final newline, the next line will + // be shown also, but should not be highlighted. Intentional for now. + if (preview && !has_second_delim) { + current_match.start.col = regmatch.startpos[0].col; + if (current_match.end.lnum == 0) { + current_match.end.lnum = sub_firstlnum + nmatch - 1; + } + current_match.end.col = regmatch.endpos[0].col; + + ADJUST_SUB_FIRSTLNUM(); + lnum += nmatch - 1; + + goto skip; + } + // 3. Substitute the string. During 'inccommand' preview only do this if // there is a replace pattern. if (!preview || has_second_delim) { @@ -3682,13 +3763,6 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) goto skip; } - // When the match included the "$" of the last line it may - // go beyond the last line of the buffer. - if (nmatch > curbuf->b_ml.ml_line_count - sub_firstlnum + 1) { - nmatch = curbuf->b_ml.ml_line_count - sub_firstlnum + 1; - skip_match = true; - } - // Need room for: // - result so far in new_start (not for first sub in line) // - original text up to match @@ -3709,6 +3783,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) memmove(new_end, sub_firstline + copycol, (size_t)copy_len); new_end += copy_len; + // Finally, at this point we can know where the match actually will + // start in the new text + current_match.start.col = new_end - new_start; + (void)vim_regsub_multi(®match, sub_firstlnum - regmatch.startpos[0].lnum, sub, new_end, true, p_magic, true); @@ -3719,30 +3797,10 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) // is beyond the end of the line after the substitution. curwin->w_cursor.col = 0; - // For a multi-line match, make a copy of the last matched - // line and continue in that one. - if (nmatch > 1) { - sub_firstlnum += nmatch - 1; - xfree(sub_firstline); - sub_firstline = vim_strsave(ml_get(sub_firstlnum)); - // When going beyond the last line, stop substituting. - if (sub_firstlnum <= line2) { - do_again = true; - } else { - subflags.do_all = false; - } - } - // Remember next character to be copied. copycol = regmatch.endpos[0].col; - if (skip_match) { - // Already hit end of the buffer, sub_firstlnum is one - // less than what it ought to be. - xfree(sub_firstline); - sub_firstline = vim_strsave((char_u *)""); - copycol = 0; - } + ADJUST_SUB_FIRSTLNUM(); // Now the trick is to replace CTRL-M chars with a real line // break. This would make it impossible to insert a CTRL-M in @@ -3781,6 +3839,8 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout) p1 += (*mb_ptr2len)(p1) - 1; } } + current_match.end.col = STRLEN(new_start); + current_match.end.lnum = lnum; } // 4. If subflags.do_all is set, find next match. @@ -3889,9 +3949,30 @@ skip: * found the match. */ if (nmatch == -1) lnum -= regmatch.startpos[0].lnum; + +#define PUSH_PREVIEW_LINES() \ + do { \ + linenr_T match_lines = current_match.end.lnum \ + - current_match.start.lnum +1; \ + if (preview_lines.subresults.size > 0) { \ + linenr_T last = kv_last(preview_lines.subresults).end.lnum; \ + if (last == current_match.start.lnum) { \ + preview_lines.lines_needed += match_lines - 1; \ + } \ + } else { \ + preview_lines.lines_needed += match_lines; \ + } \ + kv_push(preview_lines.subresults, current_match); \ + } while (0) + + // Push the match to preview_lines. + PUSH_PREVIEW_LINES(); + break; } } + // Push the match to preview_lines. + PUSH_PREVIEW_LINES(); line_breakcheck(); } @@ -3901,12 +3982,6 @@ skip: xfree(new_start); /* for when substitute was cancelled */ xfree(sub_firstline); /* free the copy of the original line */ sub_firstline = NULL; - - if (preview) { - matched_line.lnum = lnum; - matched_line.line = vim_strsave(ml_get(lnum)); - kv_push(matched_lines, matched_line); - } } line_breakcheck(); @@ -3981,24 +4056,34 @@ skip: // Show 'inccommand' preview if there are matched lines. buf_T *preview_buf = NULL; + size_t subsize = preview_lines.subresults.size; if (preview && !aborting()) { if (got_quit) { // Substitution is too slow, disable 'inccommand'. set_string_option_direct((char_u *)"icm", -1, (char_u *)"", OPT_FREE, SID_NONE); - } else if (*p_icm != NUL && matched_lines.size != 0 && pat != NULL) { + } else if (*p_icm != NUL && pat != NULL) { + if (pre_src_id == 0) { + // Get a unique new src_id, saved in a static + pre_src_id = bufhl_add_hl(NULL, 0, -1, 0, 0, 0); + } + if (pre_hl_id == 0) { + pre_hl_id = syn_check_group((char_u *)S_LEN("Substitute")); + } curbuf->b_changed = save_b_changed; // preserve 'modified' during preview - preview_buf = show_sub(eap, old_cursor, pat, sub, &matched_lines); + preview_buf = show_sub(eap, old_cursor, &preview_lines, + pre_hl_id, pre_src_id); + if (subsize > 0) { + bufhl_clear_line_range(orig_buf, pre_src_id, eap->line1, + kv_last(preview_lines.subresults).end.lnum); + } } } - for (MatchedLine m; kv_size(matched_lines);) { - m = kv_pop(matched_lines); - xfree(m.line); - kv_destroy(m.cols); - } - kv_destroy(matched_lines); + kv_destroy(preview_lines.subresults); return preview_buf; +#undef ADJUST_SUB_FIRSTLNUM +#undef PUSH_PREVIEW_LINES } // NOLINT(readability/fn_size) /* @@ -4538,18 +4623,20 @@ int find_help_tags(char_u *arg, int *num_matches, char_u ***matches, int keep_la static char *(mtable[]) = {"*", "g*", "[*", "]*", "/*", "/\\*", "\"*", "**", "/\\(\\)", "/\\%(\\)", - "?", ":?", "?<CR>", "g?", "g?g?", "g??", "z?", + "?", ":?", "?<CR>", "g?", "g?g?", "g??", "/\\?", "/\\z(\\)", "\\=", ":s\\=", - "[count]", "[quotex]", "[range]", + "[count]", "[quotex]", + "[range]", ":[range]", "[pattern]", "\\|", "\\%$", "s/\\~", "s/\\U", "s/\\L", "s/\\1", "s/\\2", "s/\\3", "s/\\9"}; static char *(rtable[]) = {"star", "gstar", "[star", "]star", "/star", "/\\\\star", "quotestar", "starstar", "/\\\\(\\\\)", "/\\\\%(\\\\)", - "?", ":?", "?<CR>", "g?", "g?g?", "g??", "z?", + "?", ":?", "?<CR>", "g?", "g?g?", "g??", "/\\\\?", "/\\\\z(\\\\)", "\\\\=", ":s\\\\=", - "\\[count]", "\\[quotex]", "\\[range]", + "\\[count]", "\\[quotex]", + "\\[range]", ":\\[range]", "\\[pattern]", "\\\\bar", "/\\\\%\\$", "s/\\\\\\~", "s/\\\\U", "s/\\\\L", "s/\\\\1", "s/\\\\2", "s/\\\\3", "s/\\\\9"}; @@ -4777,7 +4864,9 @@ void fix_help_buffer(void) char_u *rt; // Set filetype to "help". - set_option_value("ft", 0L, "help", OPT_LOCAL); + if (STRCMP(curbuf->b_p_ft, "help") != 0) { + set_option_value("ft", 0L, "help", OPT_LOCAL); + } if (!syntax_present(curwin)) { for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) { @@ -5022,8 +5111,9 @@ static void helptags_one(char_u *dir, char_u *ext, char_u *tagfname, if (gen_expand_wildcards(1, buff_list, &filecount, &files, EW_FILE|EW_SILENT) == FAIL || filecount == 0) { - if (!got_int) - EMSG2("E151: No match: %s", NameBuff); + if (!got_int) { + EMSG2(_("E151: No match: %s"), NameBuff); + } return; } @@ -5212,7 +5302,6 @@ static void do_helptags(char_u *dirname, bool add_help_tags) if (!add_pathsep((char *)NameBuff) || STRLCAT(NameBuff, "**", sizeof(NameBuff)) >= MAXPATHL) { EMSG(_(e_fnametoolong)); - xfree(dirname); return; } @@ -5222,8 +5311,7 @@ static void do_helptags(char_u *dirname, bool add_help_tags) if (gen_expand_wildcards(1, buff_list, &filecount, &files, EW_FILE|EW_SILENT) == FAIL || filecount == 0) { - EMSG2("E151: No match: %s", NameBuff); - xfree(dirname); + EMSG2(_("E151: No match: %s"), NameBuff); return; } @@ -5740,7 +5828,8 @@ static void sign_list_defined(sign_T *sp) } if (sp->sn_line_hl > 0) { msg_puts(" linehl="); - const char *const p = get_highlight_name(NULL, sp->sn_line_hl - 1); + const char *const p = get_highlight_name_ext(NULL, + sp->sn_line_hl - 1, false); if (p == NULL) { msg_puts("NONE"); } else { @@ -5749,7 +5838,8 @@ static void sign_list_defined(sign_T *sp) } if (sp->sn_text_hl > 0) { msg_puts(" texthl="); - const char *const p = get_highlight_name(NULL, sp->sn_text_hl - 1); + const char *const p = get_highlight_name_ext(NULL, + sp->sn_text_hl - 1, false); if (p == NULL) { msg_puts("NONE"); } else { @@ -5997,8 +6087,8 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg) /// Shows the effects of the :substitute command being typed ('inccommand'). /// If inccommand=split, shows a preview window and later restores the layout. -static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, char_u *pat, char_u *sub, - MatchedLineVec *matched_lines) +static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, + PreviewLines *preview_lines, int hl_id, int src_id) FUNC_ATTR_NONNULL_ALL { static handle_T bufnr = 0; // special buffer, re-used on each visit @@ -6006,8 +6096,8 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, char_u *pat, char_u *sub, win_T *save_curwin = curwin; cmdmod_T save_cmdmod = cmdmod; char_u *save_shm_p = vim_strsave(p_shm); - size_t sub_size = mb_string2cells(sub); - size_t pat_size = mb_string2cells(pat); + PreviewLines lines = *preview_lines; + buf_T *orig_buf = curbuf; // We keep a special-purpose buffer around, but don't assume it exists. buf_T *preview_buf = bufnr ? buflist_findnr(bufnr) : 0; @@ -6019,22 +6109,26 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, char_u *pat, char_u *sub, bool outside_curline = (eap->line1 != old_cusr.lnum || eap->line2 != old_cusr.lnum); - bool split = outside_curline && (*p_icm != 'n') && (sub_size || pat_size); + bool split = outside_curline && (*p_icm != 'n'); if (preview_buf == curbuf) { // Preview buffer cannot preview itself! split = false; preview_buf = NULL; } // Place cursor on nearest matching line, to undo do_sub() cursor placement. - for (size_t i = 0; i < matched_lines->size; i++) { - MatchedLine curmatch = matched_lines->items[i]; - if (curmatch.lnum >= old_cusr.lnum) { - curwin->w_cursor.lnum = curmatch.lnum; - curwin->w_cursor.col = curmatch.cols.items[0]; + for (size_t i = 0; i < lines.subresults.size; i++) { + SubResult curres = lines.subresults.items[i]; + if (curres.start.lnum >= old_cusr.lnum) { + curwin->w_cursor.lnum = curres.start.lnum; + curwin->w_cursor.col = curres.start.col; break; } // Else: All matches are above, do_sub() already placed cursor. } + // Width of the "| lnum|..." column which displays the line numbers. + linenr_T highest_num_line = 0; + int col_width = 0; + if (split && win_split((int)p_cwh, WSP_BOT) != FAIL) { buf_open_scratch(preview_buf ? bufnr : 0, "[Preview]"); buf_clear(); @@ -6049,44 +6143,77 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, char_u *pat, char_u *sub, curwin->w_p_spell = false; curwin->w_p_fen = false; - // Width of the "| lnum|..." column which displays the line numbers. - linenr_T highest_num_line = kv_last(*matched_lines).lnum; - int col_width = log10(highest_num_line) + 1 + 3; - - char *str = NULL; - size_t old_line_size = 0; - size_t line_size; - int src_id_highlight = 0; - int hl_id = syn_check_group((char_u *)"Substitute", 13); - - // Dump the lines into the preview buffer. - for (size_t line = 0; line < matched_lines->size; line++) { - MatchedLine mat = matched_lines->items[line]; - line_size = mb_string2cells(mat.line) + col_width + 1; - - // Reallocate if str not long enough - if (line_size > old_line_size) { - str = xrealloc(str, line_size * sizeof(char)); - old_line_size = line_size; + if (lines.subresults.size > 0) { + highest_num_line = kv_last(lines.subresults).end.lnum; + col_width = log10(highest_num_line) + 1 + 3; + } + } + + char *str = NULL; // construct the line to show in here + size_t old_line_size = 0; + size_t line_size = 0; + linenr_T linenr_preview = 0; // last line added to preview buffer + linenr_T linenr_origbuf = 0; // last line added to original buffer + linenr_T next_linenr = 0; // next line to show for the match + + for (size_t matchidx = 0; matchidx < lines.subresults.size; matchidx++) { + SubResult match = lines.subresults.items[matchidx]; + + if (split && preview_buf) { + lpos_T p_start = { 0, match.start.col }; // match starts here in preview + lpos_T p_end = { 0, match.end.col }; // ... and ends here + + if (match.pre_match == 0) { + next_linenr = match.start.lnum; + } else { + next_linenr = match.pre_match; } + // Don't add a line twice + if (next_linenr == linenr_origbuf) { + next_linenr++; + p_start.lnum = linenr_preview; // might be redefined below + p_end.lnum = linenr_preview; // might be redefined below + } + + for (; next_linenr <= match.end.lnum; next_linenr++) { + if (next_linenr == match.start.lnum) { + p_start.lnum = linenr_preview + 1; + } + if (next_linenr == match.end.lnum) { + p_end.lnum = linenr_preview + 1; + } + char *line; + if (next_linenr == orig_buf->b_ml.ml_line_count + 1) { + line = ""; + } else { + line = (char *)ml_get_buf(orig_buf, next_linenr, false); + line_size = strlen(line) + col_width + 1; - // Put "|lnum| line" into `str` and append it to the preview buffer. - snprintf(str, line_size, "|%*ld| %s", col_width - 3, mat.lnum, mat.line); - ml_append(line, (char_u *)str, (colnr_T)line_size, false); - - // highlight the replaced part - if (sub_size > 0) { - for (size_t i = 0; i < mat.cols.size; i++) { - colnr_T col_start = mat.cols.items[i] + col_width - + i * (sub_size - pat_size) + 1; - colnr_T col_end = col_start - 1 + sub_size; - src_id_highlight = bufhl_add_hl(curbuf, src_id_highlight, hl_id, - line + 1, col_start, col_end); + // Reallocate if line not long enough + if (line_size > old_line_size) { + str = xrealloc(str, line_size * sizeof(char)); + old_line_size = line_size; + } } + // Put "|lnum| line" into `str` and append it to the preview buffer. + snprintf(str, line_size, "|%*ld| %s", col_width - 3, + next_linenr, line); + if (linenr_preview == 0) { + ml_replace(1, (char_u *)str, true); + } else { + ml_append(linenr_preview, (char_u *)str, (colnr_T)line_size, false); + } + linenr_preview += 1; } + linenr_origbuf = match.end.lnum; + + bufhl_add_hl_pos_offset(preview_buf, src_id, hl_id, p_start, + p_end, col_width); } - xfree(str); + bufhl_add_hl_pos_offset(orig_buf, src_id, hl_id, match.start, + match.end, 0); } + xfree(str); redraw_later(SOME_VALID); win_enter(save_curwin, false); // Return to original window @@ -6136,7 +6263,11 @@ void ex_substitute(exarg_T *eap) curwin->w_p_cul = false; // Disable 'cursorline' curwin->w_p_cuc = false; // Disable 'cursorcolumn' + // Don't show search highlighting during live substitution + bool save_hls = p_hls; + p_hls = false; buf_T *preview_buf = do_sub(eap, profile_setlimit(p_rdt)); + p_hls = save_hls; if (save_changedtick != curbuf->b_changedtick) { // Undo invisibly. This also moves the cursor! @@ -6215,7 +6346,6 @@ char_u *skip_vimgrep_pat(char_u *p, char_u **s, int *flags) void ex_oldfiles(exarg_T *eap) { list_T *l = get_vim_var_list(VV_OLDFILES); - listitem_T *li; long nr = 0; if (l == NULL) { @@ -6223,19 +6353,22 @@ void ex_oldfiles(exarg_T *eap) } else { msg_start(); msg_scroll = true; - for (li = l->lv_first; li != NULL && !got_int; li = li->li_next) { + TV_LIST_ITER(l, li, { + if (got_int) { + break; + } nr++; - const char *fname = tv_get_string(&li->li_tv); + const char *fname = tv_get_string(TV_LIST_ITEM_TV(li)); if (!message_filtered((char_u *)fname)) { msg_outnum(nr); MSG_PUTS(": "); - msg_outtrans((char_u *)tv_get_string(&li->li_tv)); + msg_outtrans((char_u *)tv_get_string(TV_LIST_ITEM_TV(li))); msg_clr_eos(); msg_putchar('\n'); ui_flush(); // output one line at a time os_breakcheck(); } - } + }); // Assume "got_int" was set to truncate the listing. got_int = false; @@ -6245,7 +6378,7 @@ void ex_oldfiles(exarg_T *eap) quit_more = false; nr = prompt_for_number(false); msg_starthere(); - if (nr > 0 && nr <= l->lv_len) { + if (nr > 0 && nr <= tv_list_len(l)) { const char *const p = tv_list_find_str(l, nr - 1); if (p == NULL) { return; diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index 7203fbd97d..ce02808ad3 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -1,4 +1,4 @@ -bit = require 'bit' +local bit = require 'bit' -- Description of the values below is contained in ex_cmds_defs.h file. local RANGE = 0x001 @@ -108,7 +108,7 @@ return { }, { command='argedit', - flags=bit.bor(BANG, NEEDARG, RANGE, NOTADR, ZEROR, FILE1, EDITCMD, ARGOPT, TRLBAR), + flags=bit.bor(BANG, NEEDARG, RANGE, NOTADR, ZEROR, FILES, EDITCMD, ARGOPT, TRLBAR), addr_type=ADDR_ARGUMENTS, func='ex_argedit', }, @@ -451,6 +451,12 @@ return { func='ex_changes', }, { + command='checkhealth', + flags=bit.bor(EXTRA, TRLBAR), + addr_type=ADDR_LINES, + func='ex_checkhealth', + }, + { command='checkpath', flags=bit.bor(TRLBAR, BANG, CMDWIN), addr_type=ADDR_LINES, @@ -614,7 +620,7 @@ return { }, { command='cquit', - flags=bit.bor(TRLBAR, BANG), + flags=bit.bor(RANGE, NOTADR, COUNT, ZEROR, TRLBAR, BANG), addr_type=ADDR_LINES, func='ex_cquit', }, @@ -628,13 +634,13 @@ return { command='cscope', flags=bit.bor(EXTRA, NOTRLCOM, XFILE), addr_type=ADDR_LINES, - func='do_cscope', + func='ex_cscope', }, { command='cstag', flags=bit.bor(BANG, TRLBAR, WORD1), addr_type=ADDR_LINES, - func='do_cstag', + func='ex_cstag', }, { command='cunmap', @@ -686,7 +692,7 @@ return { }, { command='delcommand', - flags=bit.bor(NEEDARG, WORD1, TRLBAR, CMDWIN), + flags=bit.bor(BANG, NEEDARG, WORD1, TRLBAR, CMDWIN), addr_type=ADDR_LINES, func='ex_delcommand', }, @@ -1076,7 +1082,7 @@ return { }, { command='hide', - flags=bit.bor(BANG, RANGE, NOTADR, COUNT, EXTRA, NOTRLCOM), + flags=bit.bor(BANG, RANGE, NOTADR, COUNT, EXTRA, TRLBAR), addr_type=ADDR_WINDOWS, func='ex_hide', }, @@ -1324,7 +1330,7 @@ return { command='lcscope', flags=bit.bor(EXTRA, NOTRLCOM, XFILE), addr_type=ADDR_LINES, - func='do_cscope', + func='ex_cscope', }, { command='ldo', @@ -2334,7 +2340,7 @@ return { command='scscope', flags=bit.bor(EXTRA, NOTRLCOM), addr_type=ADDR_LINES, - func='do_scscope', + func='ex_scscope', }, { command='set', diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 1a728647ca..821c050c50 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -189,7 +189,8 @@ void do_debug(char_u *cmd) } xfree(cmdline); - cmdline = getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL); + cmdline = (char_u *)getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL, + CALLBACK_NONE); if (typeahead_saved) { restore_typeahead(&typeaheadbuf); @@ -1038,9 +1039,14 @@ static void profile_reset(void) uf->uf_tm_total = profile_zero(); uf->uf_tm_self = profile_zero(); uf->uf_tm_children = profile_zero(); + + xfree(uf->uf_tml_count); + xfree(uf->uf_tml_total); + xfree(uf->uf_tml_self); uf->uf_tml_count = NULL; uf->uf_tml_total = NULL; uf->uf_tml_self = NULL; + uf->uf_tml_start = profile_zero(); uf->uf_tml_children = profile_zero(); uf->uf_tml_wait = profile_zero(); @@ -1067,7 +1073,7 @@ static void profile_init(scriptitem_T *si) si->sn_pr_nest = 0; } -/// save time when starting to invoke another script or function. +/// Save time when starting to invoke another script or function. void script_prof_save( proftime_T *tm // place to store wait time ) @@ -1140,12 +1146,14 @@ static void script_dump_profile(FILE *fd) if (sfd == NULL) { fprintf(fd, "Cannot open file!\n"); } else { - for (int i = 0; i < si->sn_prl_ga.ga_len; i++) { + // Keep going till the end of file, so that trailing + // continuation lines are listed. + for (int i = 0; ; i++) { if (vim_fgets(IObuff, IOSIZE, sfd)) { break; } - pp = &PRL_ITEM(si, i); - if (pp->snp_count > 0) { + if (i < si->sn_prl_ga.ga_len + && (pp = &PRL_ITEM(si, i))->snp_count > 0) { fprintf(fd, "%5d ", pp->snp_count); if (profile_equal(pp->sn_prl_total, pp->sn_prl_self)) { fprintf(fd, " "); @@ -1335,7 +1343,7 @@ void dialog_changed(buf_T *buf, int checkall) /// hidden, autowriting it or unloading it. bool can_abandon(buf_T *buf, int forceit) { - return P_HID(buf) + return buf_hide(buf) || !bufIsChanged(buf) || buf->b_nwindows > 1 || autowrite(buf, forceit) == OK @@ -1550,12 +1558,17 @@ static char_u *do_one_arg(char_u *str) /// Separate the arguments in "str" and return a list of pointers in the /// growarray "gap". -void get_arglist(garray_T *gap, char_u *str) +static void get_arglist(garray_T *gap, char_u *str, int escaped) { ga_init(gap, (int)sizeof(char_u *), 20); while (*str != NUL) { GA_APPEND(char_u *, gap, str); + // If str is escaped, don't handle backslashes or spaces + if (!escaped) { + return; + } + // Isolate one argument, change it in-place, put a NUL after it. str = do_one_arg(str); } @@ -1570,7 +1583,7 @@ int get_arglist_exp(char_u *str, int *fcountp, char_u ***fnamesp, bool wig) garray_T ga; int i; - get_arglist(&ga, str); + get_arglist(&ga, str, true); if (wig) { i = expand_wildcards(ga.ga_len, (char_u **)ga.ga_data, @@ -1601,6 +1614,7 @@ static int do_arglist(char_u *str, int what, int after) char_u **exp_files; char_u *p; int match; + int arg_escaped = true; // Set default argument for ":argadd" command. if (what == AL_ADD && *str == NUL) { @@ -1608,10 +1622,11 @@ static int do_arglist(char_u *str, int what, int after) return FAIL; } str = curbuf->b_fname; + arg_escaped = false; } // Collect all file name arguments in "new_ga". - get_arglist(&new_ga, str); + get_arglist(&new_ga, str, arg_escaped); if (what == AL_DEL) { regmatch_T regmatch; @@ -1845,12 +1860,12 @@ void do_argfile(exarg_T *eap, int argn) // if 'hidden' set, only check for changed file when re-editing // the same buffer other = true; - if (P_HID(curbuf)) { + if (buf_hide(curbuf)) { p = (char_u *)fix_fname((char *)alist_name(&ARGLIST[argn])); other = otherfile(p); xfree(p); } - if ((!P_HID(curbuf) || !other) + if ((!buf_hide(curbuf) || !other) && check_changed(curbuf, CCGD_AW | (other ? 0 : CCGD_MULTWIN) | (eap->forceit ? CCGD_FORCEIT : 0) @@ -1870,7 +1885,7 @@ void do_argfile(exarg_T *eap, int argn) // argument index. if (do_ecmd(0, alist_name(&ARGLIST[curwin->w_arg_idx]), NULL, eap, ECMD_LAST, - (P_HID(curwin->w_buffer) ? ECMD_HIDE : 0) + (buf_hide(curwin->w_buffer) ? ECMD_HIDE : 0) + (eap->forceit ? ECMD_FORCEIT : 0), curwin) == FAIL) { curwin->w_arg_idx = old_arg_idx; } else if (eap->cmdidx != CMD_argdo) { @@ -1887,7 +1902,7 @@ void ex_next(exarg_T *eap) // check for changed buffer now, if this fails the argument list is not // redefined. - if (P_HID(curbuf) + if (buf_hide(curbuf) || eap->cmdidx == CMD_snext || !check_changed(curbuf, CCGD_AW | (eap->forceit ? CCGD_FORCEIT : 0) @@ -1907,31 +1922,21 @@ void ex_next(exarg_T *eap) /// ":argedit" void ex_argedit(exarg_T *eap) { - int fnum; - int i; - char_u *s; + int i = eap->addr_count ? (int)eap->line2 : curwin->w_arg_idx + 1; - // Add the argument to the buffer list and get the buffer number. - fnum = buflist_add(eap->arg, BLN_LISTED); - - // Check if this argument is already in the argument list. - for (i = 0; i < ARGCOUNT; i++) { - if (ARGLIST[i].ae_fnum == fnum) { - break; - } - } - if (i == ARGCOUNT) { - // Can't find it, add it to the argument list. - s = vim_strsave(eap->arg); - int after = eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1; - i = alist_add_list(1, &s, after); - curwin->w_arg_idx = i; + if (do_arglist(eap->arg, AL_ADD, i) == FAIL) { + return; } + maketitle(); - alist_check_arg_idx(); - + if (curwin->w_arg_idx == 0 && (curbuf->b_ml.ml_flags & ML_EMPTY) + && curbuf->b_ffname == NULL) { + i = 0; + } // Edit the argument. - do_argfile(eap, i); + if (i < ARGCOUNT) { + do_argfile(eap, i); + } } /// ":argadd" @@ -1951,8 +1956,14 @@ void ex_argdelete(exarg_T *eap) eap->line2 = ARGCOUNT; } linenr_T n = eap->line2 - eap->line1 + 1; - if (*eap->arg != NUL || n <= 0) { + if (*eap->arg != NUL) { + // Can't have both a range and an argument. EMSG(_(e_invarg)); + } else if (n <= 0) { + // Don't give an error for ":%argdel" if the list is empty. + if (eap->line1 != 1 || eap->line2 != 0) { + EMSG(_(e_invrange)); + } } else { for (linenr_T i = eap->line1; i <= eap->line2; i++) { xfree(ARGLIST[i - 1].ae_fname); @@ -1997,7 +2008,7 @@ void ex_listdo(exarg_T *eap) if (eap->cmdidx == CMD_windo || eap->cmdidx == CMD_tabdo - || P_HID(curbuf) + || buf_hide(curbuf) || !check_changed(curbuf, CCGD_AW | (eap->forceit ? CCGD_FORCEIT : 0) | CCGD_EXCMD)) { @@ -2318,16 +2329,6 @@ static void source_callback(char_u *fname, void *cookie) (void)do_source(fname, false, DOSO_NONE); } -/// Source the file "name" from all directories in 'runtimepath'. -/// "name" can contain wildcards. -/// When "flags" has DIP_ALL: source all files, otherwise only the first one. -/// -/// return FAIL when no file could be sourced, OK otherwise. -int source_runtime(char_u *name, int flags) -{ - return do_in_runtimepath(name, flags, source_callback, NULL); -} - /// Find the file "name" in all directories in "path" and invoke /// "callback(fname, cookie)". /// "name" can contain wildcards. @@ -2433,21 +2434,21 @@ int do_in_path(char_u *path, char_u *name, int flags, return did_one ? OK : FAIL; } -/// Find "name" in 'runtimepath'. When found, invoke the callback function for +/// Find "name" in "path". When found, invoke the callback function for /// it: callback(fname, "cookie") /// When "flags" has DIP_ALL repeat for all matches, otherwise only the first /// one is used. /// Returns OK when at least one match found, FAIL otherwise. -/// If "name" is NULL calls callback for each entry in runtimepath. Cookie is +/// If "name" is NULL calls callback for each entry in "path". Cookie is /// passed by reference in this case, setting it to NULL indicates that callback /// has done its job. -int do_in_runtimepath(char_u *name, int flags, DoInRuntimepathCB callback, - void *cookie) +int do_in_path_and_pp(char_u *path, char_u *name, int flags, + DoInRuntimepathCB callback, void *cookie) { int done = FAIL; if ((flags & DIP_NORTP) == 0) { - done = do_in_path(p_rtp, name, flags, callback, cookie); + done = do_in_path(path, name, flags, callback, cookie); } if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_START)) { @@ -2475,6 +2476,29 @@ int do_in_runtimepath(char_u *name, int flags, DoInRuntimepathCB callback, return done; } +/// Just like do_in_path_and_pp(), using 'runtimepath' for "path". +int do_in_runtimepath(char_u *name, int flags, DoInRuntimepathCB callback, + void *cookie) +{ + return do_in_path_and_pp(p_rtp, name, flags, callback, cookie); +} + +/// Source the file "name" from all directories in 'runtimepath'. +/// "name" can contain wildcards. +/// When "flags" has DIP_ALL: source all files, otherwise only the first one. +/// +/// return FAIL when no file could be sourced, OK otherwise. +int source_runtime(char_u *name, int flags) +{ + return source_in_path(p_rtp, name, flags); +} + +/// Just like source_runtime(), but use "path" instead of 'runtimepath'. +int source_in_path(char_u *path, char_u *name, int flags) +{ + return do_in_path_and_pp(path, name, flags, source_callback, NULL); +} + // Expand wildcards in "pat" and invoke do_source() for each match. static void source_all_matches(char_u *pat) { @@ -2497,6 +2521,7 @@ static int APP_BOTH; static void add_pack_plugin(char_u *fname, void *cookie) { char_u *p4, *p3, *p2, *p1, *p; + char_u *buf = NULL; char *const ffname = fix_fname((char *)fname); @@ -2524,26 +2549,30 @@ static void add_pack_plugin(char_u *fname, void *cookie) // Find "ffname" in "p_rtp", ignoring '/' vs '\' differences size_t fname_len = strlen(ffname); const char *insp = (const char *)p_rtp; - for (;;) { - if (path_fnamencmp(insp, ffname, fname_len) == 0) { - break; + buf = try_malloc(MAXPATHL); + if (buf == NULL) { + goto theend; + } + while (*insp != NUL) { + copy_option_part((char_u **)&insp, buf, MAXPATHL, ","); + add_pathsep((char *)buf); + char *const rtp_ffname = fix_fname((char *)buf); + if (rtp_ffname == NULL) { + goto theend; } - insp = strchr(insp, ','); - if (insp == NULL) { + bool match = path_fnamencmp(rtp_ffname, ffname, fname_len) == 0; + xfree(rtp_ffname); + if (match) { break; } - insp++; } - if (insp == NULL) { + if (*insp == NUL) { // not found, append at the end insp = (const char *)p_rtp + STRLEN(p_rtp); } else { // append after the matching directory. - insp += strlen(ffname); - while (*insp != NUL && *insp != ',') { - insp++; - } + insp--; } *p4 = c; @@ -2613,26 +2642,35 @@ static void add_pack_plugin(char_u *fname, void *cookie) } theend: + xfree(buf); xfree(ffname); } -static bool did_source_packages = false; +/// Add all packages in the "start" directory to 'runtimepath'. +void add_pack_start_dirs(void) +{ + do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR, // NOLINT + add_pack_plugin, &APP_ADD_DIR); +} + +/// Load plugins from all packages in the "start" directory. +void load_start_packages(void) +{ + did_source_packages = true; + do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR, // NOLINT + add_pack_plugin, &APP_LOAD); +} // ":packloadall" // Find plugins in the package directories and source them. -// "eap" is NULL when invoked during startup. void ex_packloadall(exarg_T *eap) { - if (!did_source_packages || (eap != NULL && eap->forceit)) { - did_source_packages = true; - + if (!did_source_packages || eap->forceit) { // First do a round to add all directories to 'runtimepath', then load // the plugins. This allows for plugins to use an autoload directory // of another plugin. - do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR, // NOLINT - add_pack_plugin, &APP_ADD_DIR); - do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR, // NOLINT - add_pack_plugin, &APP_LOAD); + add_pack_start_dirs(); + load_start_packages(); } } @@ -2843,22 +2881,6 @@ int do_source(char_u *fname, int check_other, int is_vimrc) save_sourcing_lnum = sourcing_lnum; sourcing_lnum = 0; - cookie.conv.vc_type = CONV_NONE; // no conversion - - // Read the first line so we can check for a UTF-8 BOM. - firstline = getsourceline(0, (void *)&cookie, 0); - if (firstline != NULL && STRLEN(firstline) >= 3 && firstline[0] == 0xef - && firstline[1] == 0xbb && firstline[2] == 0xbf) { - // Found BOM; setup conversion, skip over BOM and recode the line. - convert_setup(&cookie.conv, (char_u *)"utf-8", p_enc); - p = string_convert(&cookie.conv, firstline + 3, NULL); - if (p == NULL) { - p = vim_strsave(firstline + 3); - } - xfree(firstline); - firstline = p; - } - // start measuring script load time if --startuptime was passed and // time_fd was successfully opened afterwards. proftime_T rel_time; @@ -2931,6 +2953,22 @@ int do_source(char_u *fname, int check_other, int is_vimrc) } } + cookie.conv.vc_type = CONV_NONE; // no conversion + + // Read the first line so we can check for a UTF-8 BOM. + firstline = getsourceline(0, (void *)&cookie, 0); + if (firstline != NULL && STRLEN(firstline) >= 3 && firstline[0] == 0xef + && firstline[1] == 0xbb && firstline[2] == 0xbf) { + // Found BOM; setup conversion, skip over BOM and recode the line. + convert_setup(&cookie.conv, (char_u *)"utf-8", p_enc); + p = string_convert(&cookie.conv, firstline + 3, NULL); + if (p == NULL) { + p = vim_strsave(firstline + 3); + } + xfree(firstline); + firstline = p; + } + // Call do_cmdline, which will call getsourceline() to get the lines. do_cmdline(firstline, getsourceline, (void *)&cookie, DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_REPEAT); @@ -3040,6 +3078,8 @@ char_u *get_scriptname(scid_T id) # if defined(EXITFREE) void free_scriptnames(void) { + profile_reset(); + # define FREE_SCRIPTNAME(item) xfree((item)->sn_name) GA_DEEP_CLEAR(&script_items, scriptitem_T, FREE_SCRIPTNAME); } @@ -3162,8 +3202,14 @@ static char_u *get_one_sourceline(struct source_cookie *sp) ga_grow(&ga, 120); buf = (char_u *)ga.ga_data; +retry: + errno = 0; if (fgets((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len, sp->fp) == NULL) { + if (errno == EINTR) { + goto retry; + } + break; } len = ga.ga_len + (int)STRLEN(buf + ga.ga_len); @@ -3253,7 +3299,8 @@ void script_line_start(void) if (si->sn_prof_on && sourcing_lnum >= 1) { // Grow the array before starting the timer, so that the time spent // here isn't counted. - ga_grow(&si->sn_prl_ga, (int)(sourcing_lnum - si->sn_prl_ga.ga_len)); + (void)ga_grow(&si->sn_prl_ga, + (int)(sourcing_lnum - si->sn_prl_ga.ga_len)); si->sn_prl_idx = sourcing_lnum - 1; while (si->sn_prl_ga.ga_len <= si->sn_prl_idx && si->sn_prl_ga.ga_len < si->sn_prl_ga.ga_maxlen) { @@ -3610,18 +3657,11 @@ void ex_language(exarg_T *eap) static char_u **locales = NULL; // Array of all available locales -static bool did_init_locales = false; -/// Lazy initialization of all available locales. -static void init_locales(void) -{ - if (!did_init_locales) { - did_init_locales = true; - locales = find_locales(); - } -} +#ifndef WIN32 +static bool did_init_locales = false; -// Return an array of strings for all available locales + NULL for the +/// Return an array of strings for all available locales + NULL for the /// last element. Return NULL in case of error. static char_u **find_locales(void) { @@ -3653,6 +3693,18 @@ static char_u **find_locales(void) ((char_u **)locales_ga.ga_data)[locales_ga.ga_len] = NULL; return (char_u **)locales_ga.ga_data; } +#endif + +/// Lazy initialization of all available locales. +static void init_locales(void) +{ +#ifndef WIN32 + if (!did_init_locales) { + did_init_locales = true; + locales = find_locales(); + } +#endif +} # if defined(EXITFREE) void free_locales(void) @@ -3709,7 +3761,7 @@ static void script_host_execute(char *name, exarg_T *eap) char *const script = script_get(eap, &len); if (script != NULL) { - list_T *const args = tv_list_alloc(); + list_T *const args = tv_list_alloc(3); // script tv_list_append_allocated_string(args, script); // current range @@ -3724,7 +3776,7 @@ static void script_host_execute_file(char *name, exarg_T *eap) uint8_t buffer[MAXPATHL]; vim_FullName((char *)eap->arg, (char *)buffer, sizeof(buffer), false); - list_T *args = tv_list_alloc(); + list_T *args = tv_list_alloc(3); // filename tv_list_append_string(args, (const char *)buffer, -1); // current range @@ -3735,7 +3787,7 @@ static void script_host_execute_file(char *name, exarg_T *eap) static void script_host_do_range(char *name, exarg_T *eap) { - list_T *args = tv_list_alloc(); + list_T *args = tv_list_alloc(3); tv_list_append_number(args, (int)eap->line1); tv_list_append_number(args, (int)eap->line2); tv_list_append_string(args, (const char *)eap->arg, -1); @@ -3788,7 +3840,7 @@ void ex_drop(exarg_T *eap) // to split the current window or data could be lost. // Skip the check if the 'hidden' option is set, as in this case the // buffer won't be lost. - if (!P_HID(curbuf)) { + if (!buf_hide(curbuf)) { emsg_off++; split = check_changed(curbuf, CCGD_AW | CCGD_EXCMD); emsg_off--; diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index af8845de87..4b37abab9e 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -9,6 +9,7 @@ #include <string.h> #include <stdbool.h> #include <stdint.h> +#include <stdlib.h> #include <inttypes.h> #include "nvim/vim.h" @@ -132,7 +133,6 @@ struct dbg_stuff { char_u *vv_throwpoint; int did_emsg; int got_int; - int did_throw; int need_rethrow; int check_cstack; except_T *current_exception; @@ -164,12 +164,11 @@ static void save_dbg_stuff(struct dbg_stuff *dsp) dsp->vv_exception = v_exception(NULL); dsp->vv_throwpoint = v_throwpoint(NULL); - /* Necessary for debugging an inactive ":catch", ":finally", ":endtry" */ - dsp->did_emsg = did_emsg; did_emsg = FALSE; - dsp->got_int = got_int; got_int = FALSE; - dsp->did_throw = did_throw; did_throw = FALSE; - dsp->need_rethrow = need_rethrow; need_rethrow = FALSE; - dsp->check_cstack = check_cstack; check_cstack = FALSE; + // Necessary for debugging an inactive ":catch", ":finally", ":endtry". + dsp->did_emsg = did_emsg; did_emsg = false; + dsp->got_int = got_int; got_int = false; + dsp->need_rethrow = need_rethrow; need_rethrow = false; + dsp->check_cstack = check_cstack; check_cstack = false; dsp->current_exception = current_exception; current_exception = NULL; } @@ -183,7 +182,6 @@ static void restore_dbg_stuff(struct dbg_stuff *dsp) (void)v_throwpoint(dsp->vv_throwpoint); did_emsg = dsp->did_emsg; got_int = dsp->got_int; - did_throw = dsp->did_throw; need_rethrow = dsp->need_rethrow; check_cstack = dsp->check_cstack; current_exception = dsp->current_exception; @@ -340,12 +338,13 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, msg_list = &private_msg_list; private_msg_list = NULL; - /* It's possible to create an endless loop with ":execute", catch that - * here. The value of 200 allows nested function calls, ":source", etc. */ - if (call_depth == 200) { + // It's possible to create an endless loop with ":execute", catch that + // here. The value of 200 allows nested function calls, ":source", etc. + // Allow 200 or 'maxfuncdepth', whatever is larger. + if (call_depth >= 200 && call_depth >= p_mfd) { EMSG(_("E169: Command too recursive")); - /* When converting to an exception, we do not include the command name - * since this is not an error of the specific command. */ + // When converting to an exception, we do not include the command name + // since this is not an error of the specific command. do_errthrow((struct condstack *)NULL, (char_u *)NULL); msg_list = saved_msg_list; return FAIL; @@ -398,16 +397,11 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, initial_trylevel = trylevel; - /* - * "did_throw" will be set to TRUE when an exception is being thrown. - */ - did_throw = FALSE; - /* - * "did_emsg" will be set to TRUE when emsg() is used, in which case we - * cancel the whole command line, and any if/endif or loop. - * If force_abort is set, we cancel everything. - */ - did_emsg = FALSE; + current_exception = NULL; + // "did_emsg" will be set to TRUE when emsg() is used, in which case we + // cancel the whole command line, and any if/endif or loop. + // If force_abort is set, we cancel everything. + did_emsg = false; /* * KeyTyped is only set when calling vgetc(). Reset it here when not @@ -657,7 +651,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, * not to use a cs_line[] from an entry that isn't a ":while" * or ":for": It would make "current_line" invalid and can * cause a crash. */ - if (!did_emsg && !got_int && !did_throw + if (!did_emsg && !got_int && !current_exception && cstack.cs_idx >= 0 && (cstack.cs_flags[cstack.cs_idx] & (CSF_WHILE | CSF_FOR)) @@ -705,7 +699,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, } /* - * A ":finally" makes did_emsg, got_int, and did_throw pending for + * A ":finally" makes did_emsg, got_int and current_exception pending for * being restored at the ":endtry". Reset them here and set the * ACTIVE and FINALLY flags, so that the finally clause gets executed. * This includes the case where a missing ":endif", ":endwhile" or @@ -713,10 +707,11 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, */ if (cstack.cs_lflags & CSL_HAD_FINA) { cstack.cs_lflags &= ~CSL_HAD_FINA; - report_make_pending(cstack.cs_pending[cstack.cs_idx] - & (CSTP_ERROR | CSTP_INTERRUPT | CSTP_THROW), - did_throw ? (void *)current_exception : NULL); - did_emsg = got_int = did_throw = FALSE; + report_make_pending((cstack.cs_pending[cstack.cs_idx] + & (CSTP_ERROR | CSTP_INTERRUPT | CSTP_THROW)), + current_exception); + did_emsg = got_int = false; + current_exception = NULL; cstack.cs_flags[cstack.cs_idx] |= CSF_ACTIVE | CSF_FINALLY; } @@ -724,15 +719,14 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, * within this loop. */ trylevel = initial_trylevel + cstack.cs_trylevel; - /* - * If the outermost try conditional (across function calls and sourced - * files) is aborted because of an error, an interrupt, or an uncaught - * exception, cancel everything. If it is left normally, reset - * force_abort to get the non-EH compatible abortion behavior for - * the rest of the script. - */ - if (trylevel == 0 && !did_emsg && !got_int && !did_throw) - force_abort = FALSE; + // If the outermost try conditional (across function calls and sourced + // files) is aborted because of an error, an interrupt, or an uncaught + // exception, cancel everything. If it is left normally, reset + // force_abort to get the non-EH compatible abortion behavior for + // the rest of the script. + if (trylevel == 0 && !did_emsg && !got_int && !current_exception) { + force_abort = false; + } /* Convert an interrupt to an exception if appropriate. */ (void)do_intthrow(&cstack); @@ -747,11 +741,8 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, * - there is a command after '|', inside a :if, :while, :for or :try, or * looping for ":source" command or function call. */ - while (!((got_int - || (did_emsg && force_abort) || did_throw - ) - && cstack.cs_trylevel == 0 - ) + while (!((got_int || (did_emsg && force_abort) || current_exception) + && cstack.cs_trylevel == 0) && !(did_emsg /* Keep going when inside try/catch, so that the error can be * deal with, except when it is a syntax error, it may cause @@ -773,7 +764,7 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, * If a sourced file or executed function ran to its end, report the * unclosed conditional. */ - if (!got_int && !did_throw + if (!got_int && !current_exception && ((getline_equal(fgetline, cookie, getsourceline) && !source_finished(fgetline, cookie)) || (getline_equal(fgetline, cookie, get_func_line) @@ -813,17 +804,16 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, ? (char_u *)"endfunction" : (char_u *)NULL); if (trylevel == 0) { - /* - * When an exception is being thrown out of the outermost try - * conditional, discard the uncaught exception, disable the conversion - * of interrupts or errors to exceptions, and ensure that no more - * commands are executed. - */ - if (did_throw) { - void *p = NULL; - char_u *saved_sourcing_name; + // When an exception is being thrown out of the outermost try + // conditional, discard the uncaught exception, disable the conversion + // of interrupts or errors to exceptions, and ensure that no more + // commands are executed. + if (current_exception) { + void *p = NULL; + char_u *saved_sourcing_name; int saved_sourcing_lnum; - struct msglist *messages = NULL, *next; + struct msglist *messages = NULL; + struct msglist *next; /* * If the uncaught exception is a user exception, report it as an @@ -844,8 +834,6 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, break; case ET_INTERRUPT: break; - default: - p = vim_strsave((char_u *)_(e_internal)); } saved_sourcing_name = sourcing_name; @@ -885,22 +873,22 @@ int do_cmdline(char_u *cmdline, LineGetter fgetline, suppress_errthrow = TRUE; } - /* - * The current cstack will be freed when do_cmdline() returns. An uncaught - * exception will have to be rethrown in the previous cstack. If a function - * has just returned or a script file was just finished and the previous - * cstack belongs to the same function or, respectively, script file, it - * will have to be checked for finally clauses to be executed due to the - * ":return" or ":finish". This is done in do_one_cmd(). - */ - if (did_throw) - need_rethrow = TRUE; + // The current cstack will be freed when do_cmdline() returns. An uncaught + // exception will have to be rethrown in the previous cstack. If a function + // has just returned or a script file was just finished and the previous + // cstack belongs to the same function or, respectively, script file, it + // will have to be checked for finally clauses to be executed due to the + // ":return" or ":finish". This is done in do_one_cmd(). + if (current_exception) { + need_rethrow = true; + } if ((getline_equal(fgetline, cookie, getsourceline) && ex_nesting_level > source_level(real_cookie)) || (getline_equal(fgetline, cookie, get_func_line) && ex_nesting_level > func_level(real_cookie) + 1)) { - if (!did_throw) - check_cstack = TRUE; + if (!current_exception) { + check_cstack = true; + } } else { /* When leaving a function, reduce nesting level. */ if (getline_equal(fgetline, cookie, get_func_line)) @@ -1480,10 +1468,11 @@ static char_u * do_one_cmd(char_u **cmdlinep, } char_u *after_modifier = ea.cmd; - ea.skip = did_emsg || got_int || did_throw || (cstack->cs_idx >= 0 - && !(cstack->cs_flags[cstack-> - cs_idx] - & CSF_ACTIVE)); + ea.skip = (did_emsg + || got_int + || current_exception + || (cstack->cs_idx >= 0 + && !(cstack->cs_flags[cstack->cs_idx] & CSF_ACTIVE))); /* Count this line for profiling if ea.skip is FALSE. */ if (do_profiling == PROF_YES && !ea.skip) { @@ -1667,11 +1656,15 @@ static char_u * do_one_cmd(char_u **cmdlinep, ea.addr_count++; if (*ea.cmd == ';') { - if (!ea.skip) + if (!ea.skip) { curwin->w_cursor.lnum = ea.line2; - } else if (*ea.cmd != ',') + // don't leave the cursor on an illegal line or column + check_cursor(); + } + } else if (*ea.cmd != ',') { break; - ++ea.cmd; + } + ea.cmd++; } /* One address given: set start and end lines */ @@ -1682,9 +1675,6 @@ static char_u * do_one_cmd(char_u **cmdlinep, ea.addr_count = 0; } - /* Don't leave the cursor on an illegal line (caused by ';') */ - check_cursor_lnum(); - /* * 5. Parse the command. */ @@ -1781,13 +1771,14 @@ static char_u * do_one_cmd(char_u **cmdlinep, )); - /* forced commands */ + // Forced commands. if (*p == '!' && ea.cmdidx != CMD_substitute && ea.cmdidx != CMD_smagic && ea.cmdidx != CMD_snomagic) { - ++p; - ea.forceit = TRUE; - } else - ea.forceit = FALSE; + p++; + ea.forceit = true; + } else { + ea.forceit = false; + } /* * 6. Parse arguments. @@ -1813,7 +1804,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, if (text_locked() && !(ea.argt & CMDWIN) && !IS_USER_CMDIDX(ea.cmdidx)) { // Command not allowed when editing the command line. - errormsg = get_text_locked_msg(); + errormsg = (char_u *)_(get_text_locked_msg()); goto doend; } /* Disallow editing another buffer when "curbuf_lock" is set. @@ -3271,6 +3262,12 @@ const char * set_one_cmd_context( case CMD_echoerr: case CMD_call: case CMD_return: + case CMD_cexpr: + case CMD_caddexpr: + case CMD_cgetexpr: + case CMD_lexpr: + case CMD_laddexpr: + case CMD_lgetexpr: set_context_for_expression(xp, (char_u *)arg, ea.cmdidx); break; @@ -3435,11 +3432,20 @@ const char * set_one_cmd_context( case CMD_profile: set_context_in_profile_cmd(xp, arg); break; + case CMD_checkhealth: + xp->xp_context = EXPAND_CHECKHEALTH; + xp->xp_pattern = (char_u *)arg; + break; case CMD_behave: xp->xp_context = EXPAND_BEHAVE; xp->xp_pattern = (char_u *)arg; break; + case CMD_messages: + xp->xp_context = EXPAND_MESSAGES; + xp->xp_pattern = (char_u *)arg; + break; + case CMD_history: xp->xp_context = EXPAND_HISTORY; xp->xp_pattern = (char_u *)arg; @@ -3472,10 +3478,17 @@ char_u *skip_range( { unsigned delim; - while (vim_strchr((char_u *)" \t0123456789.$%'/?-+,;", *cmd) != NULL) { - if (*cmd == '\'') { - if (*++cmd == NUL && ctx != NULL) + while (vim_strchr((char_u *)" \t0123456789.$%'/?-+,;\\", *cmd) != NULL) { + if (*cmd == '\\') { + if (cmd[1] == '?' || cmd[1] == '/' || cmd[1] == '&') { + cmd++; + } else { + break; + } + } else if (*cmd == '\'') { + if (*++cmd == NUL && ctx != NULL) { *ctx = EXPAND_NOTHING; + } } else if (*cmd == '/' || *cmd == '?') { delim = *cmd++; while (*cmd != NUL && *cmd != delim) @@ -4670,17 +4683,17 @@ char_u *find_nextcmd(const char_u *p) return (char_u *)p + 1; } -/* - * Check if *p is a separator between Ex commands. - * Return NULL if it isn't, (p + 1) if it is. - */ +/// Check if *p is a separator between Ex commands, skipping over white space. +/// Return NULL if it isn't, the following character if it is. char_u *check_nextcmd(char_u *p) { - p = skipwhite(p); - if (*p == '|' || *p == '\n') - return p + 1; - else - return NULL; + char_u *s = skipwhite(p); + + if (*s == '|' || *s == '\n') { + return (s + 1); + } else { + return NULL; + } } /* @@ -4747,7 +4760,7 @@ static int uc_add_command(char_u *name, size_t name_len, char_u *rep, char_u *rep_buf = NULL; garray_T *gap; - replace_termcodes(rep, STRLEN(rep), &rep_buf, false, false, false, + replace_termcodes(rep, STRLEN(rep), &rep_buf, false, false, true, CPO_TO_CPO_FLAGS); if (rep_buf == NULL) { /* Can't replace termcodes - try using the string as is */ @@ -4852,6 +4865,7 @@ static struct { { EXPAND_AUGROUP, "augroup" }, { EXPAND_BEHAVE, "behave" }, { EXPAND_BUFFERS, "buffer" }, + { EXPAND_CHECKHEALTH, "checkhealth" }, { EXPAND_COLORS, "color" }, { EXPAND_COMMANDS, "command" }, { EXPAND_COMPILER, "compiler" }, @@ -4874,6 +4888,7 @@ static struct { #endif { EXPAND_MAPPINGS, "mapping" }, { EXPAND_MENUS, "menu" }, + { EXPAND_MESSAGES, "messages" }, { EXPAND_OWNSYNTAX, "syntax" }, { EXPAND_SYNTIME, "syntime" }, { EXPAND_SETTINGS, "option" }, @@ -5905,9 +5920,10 @@ static void ex_colorscheme(exarg_T *eap) static void ex_highlight(exarg_T *eap) { - if (*eap->arg == NUL && eap->cmd[2] == '!') + if (*eap->arg == NUL && eap->cmd[2] == '!') { MSG(_("Greetings, Vim user!")); - do_highlight(eap->arg, eap->forceit, FALSE); + } + do_highlight((const char *)eap->arg, eap->forceit, false); } @@ -5948,22 +5964,24 @@ static void ex_quit(exarg_T *eap) wp = curwin; } - apply_autocmds(EVENT_QUITPRE, NULL, NULL, false, curbuf); + // Refuse to quit when locked. + if (curbuf_locked()) { + return; + } + apply_autocmds(EVENT_QUITPRE, NULL, NULL, false, wp->w_buffer); // Refuse to quit when locked or when the buffer in the last window is // being closed (can only happen in autocommands). - if (curbuf_locked() + if (!win_valid(wp) || (wp->w_buffer->b_nwindows == 1 && wp->w_buffer->b_locked > 0)) { return; } - - /* - * If there are more files or windows we won't exit. - */ - if (check_more(FALSE, eap->forceit) == OK && only_one_window()) - exiting = TRUE; - if ((!P_HID(curbuf) - && check_changed(curbuf, (p_awa ? CCGD_AW : 0) + // If there are more files or windows we won't exit. + if (check_more(false, eap->forceit) == OK && only_one_window()) { + exiting = true; + } + if ((!buf_hide(wp->w_buffer) + && check_changed(wp->w_buffer, (p_awa ? CCGD_AW : 0) | (eap->forceit ? CCGD_FORCEIT : 0) | CCGD_EXCMD)) || check_more(true, eap->forceit) == FAIL @@ -5976,11 +5994,11 @@ static void ex_quit(exarg_T *eap) // specified. Example: // :h|wincmd w|1q - don't quit // :h|wincmd w|q - quit - if (only_one_window() && (firstwin == lastwin || eap->addr_count == 0)) { + if (only_one_window() && (ONE_WINDOW || eap->addr_count == 0)) { getout(0); } - /* close window; may free buffer */ - win_close(wp, !P_HID(wp->w_buffer) || eap->forceit); + // close window; may free buffer + win_close(wp, !buf_hide(wp->w_buffer) || eap->forceit); } } @@ -5989,7 +6007,7 @@ static void ex_quit(exarg_T *eap) */ static void ex_cquit(exarg_T *eap) { - getout(1); + getout(eap->addr_count > 0 ? (int)eap->line2 : EXIT_FAILURE); } /* @@ -6079,7 +6097,7 @@ ex_win_close ( buf_T *buf = win->w_buffer; need_hide = (bufIsChanged(buf) && buf->b_nwindows <= 1); - if (need_hide && !P_HID(buf) && !forceit) { + if (need_hide && !buf_hide(buf) && !forceit) { if ((p_confirm || cmdmod.confirm) && p_write) { bufref_T bufref; set_bufref(&bufref, buf); @@ -6095,11 +6113,12 @@ ex_win_close ( } - /* free buffer when not hiding it or when it's a scratch buffer */ - if (tp == NULL) - win_close(win, !need_hide && !P_HID(buf)); - else - win_close_othertab(win, !need_hide && !P_HID(buf), tp); + // free buffer when not hiding it or when it's a scratch buffer + if (tp == NULL) { + win_close(win, !need_hide && !buf_hide(buf)); + } else { + win_close_othertab(win, !need_hide && !buf_hide(buf), tp); + } } /* @@ -6174,12 +6193,14 @@ static void ex_tabonly(exarg_T *eap) */ void tabpage_close(int forceit) { - /* First close all the windows but the current one. If that worked then - * close the last window in this tab, that will close it. */ - if (lastwin != firstwin) - close_others(TRUE, forceit); - if (lastwin == firstwin) + // First close all the windows but the current one. If that worked then + // close the last window in this tab, that will close it. + if (!ONE_WINDOW) { + close_others(true, forceit); + } + if (ONE_WINDOW) { ex_win_close(forceit, curwin, NULL); + } } /* @@ -6207,7 +6228,6 @@ void tabpage_close_other(tabpage_T *tp, int forceit) if (!valid_tabpage(tp) || tp->tp_firstwin == wp) break; } - apply_autocmds(EVENT_TABCLOSED, prev_idx, prev_idx, FALSE, curbuf); redraw_tabline = TRUE; if (h != tabline_height()) @@ -6247,31 +6267,27 @@ void ex_all(exarg_T *eap) static void ex_hide(exarg_T *eap) { - if (*eap->arg != NUL && check_nextcmd(eap->arg) == NULL) - eap->errmsg = e_invarg; - else { - /* ":hide" or ":hide | cmd": hide current window */ - eap->nextcmd = check_nextcmd(eap->arg); + // ":hide" or ":hide | cmd": hide current window if (!eap->skip) { - if (eap->addr_count == 0) - win_close(curwin, FALSE); /* don't free buffer */ - else { - int winnr = 0; - win_T *win = NULL; - - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - winnr++; - if (winnr == eap->line2) { - win = wp; - break; - } + if (eap->addr_count == 0) { + win_close(curwin, false); // don't free buffer + } else { + int winnr = 0; + win_T *win = NULL; + + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + winnr++; + if (winnr == eap->line2) { + win = wp; + break; + } + } + if (win == NULL) { + win = lastwin; + } + win_close(win, false); } - if (win == NULL) - win = lastwin; - win_close(win, FALSE); - } } - } } /* @@ -6333,7 +6349,7 @@ static void ex_exit(exarg_T *eap) getout(0); } // Quit current window, may free the buffer. - win_close(curwin, !P_HID(curwin->w_buffer)); + win_close(curwin, !buf_hide(curwin->w_buffer)); } } @@ -6903,24 +6919,23 @@ do_exedit ( empty buffer */ setpcmark(); if (do_ecmd(0, (eap->cmdidx == CMD_enew ? NULL : eap->arg), - NULL, eap, - eap->do_ecmd_lnum, - (P_HID(curbuf) ? ECMD_HIDE : 0) - + (eap->forceit ? ECMD_FORCEIT : 0) - // After a split we can use an existing buffer. - + (old_curwin != NULL ? ECMD_OLDBUF : 0) - + (eap->cmdidx == CMD_badd ? ECMD_ADDBUF : 0 ) - , old_curwin == NULL ? curwin : NULL) == FAIL) { - /* Editing the file failed. If the window was split, close it. */ + NULL, eap, eap->do_ecmd_lnum, + (buf_hide(curbuf) ? ECMD_HIDE : 0) + + (eap->forceit ? ECMD_FORCEIT : 0) + // After a split we can use an existing buffer. + + (old_curwin != NULL ? ECMD_OLDBUF : 0) + + (eap->cmdidx == CMD_badd ? ECMD_ADDBUF : 0) + , old_curwin == NULL ? curwin : NULL) == FAIL) { + // Editing the file failed. If the window was split, close it. if (old_curwin != NULL) { need_hide = (curbufIsChanged() && curbuf->b_nwindows <= 1); - if (!need_hide || P_HID(curbuf)) { + if (!need_hide || buf_hide(curbuf)) { cleanup_T cs; /* Reset the error/interrupt/exception state here so that * aborting() returns FALSE when closing a window. */ enter_cleanup(&cs); - win_close(curwin, !need_hide && !P_HID(curbuf)); + win_close(curwin, !need_hide && !buf_hide(curbuf)); /* Restore the error/interrupt/exception state if not * discarded by a new aborting error, interrupt, or @@ -7686,6 +7701,7 @@ static void ex_redraw(exarg_T *eap) RedrawingDisabled = 0; p_lz = FALSE; + validate_cursor(); update_topline(); update_screen(eap->forceit ? CLEAR : VIsual_active ? INVERTED : @@ -8130,6 +8146,10 @@ static void ex_startinsert(exarg_T *eap) restart_edit = 'i'; curwin->w_curswant = 0; /* avoid MAXCOL */ } + + if (VIsual_active) { + showmode(); + } } /* @@ -8315,7 +8335,7 @@ static void ex_tag_cmd(exarg_T *eap, char_u *name) break; default: /* ":tag" */ if (p_cst && *eap->arg != NUL) { - do_cstag(eap); + ex_cstag(eap); return; } cmd = DT_TAG; @@ -8520,22 +8540,22 @@ eval_vars ( resultbuf = result; /* remember allocated string */ break; - case SPEC_AFILE: /* file name for autocommand */ - result = autocmd_fname; - if (result != NULL && !autocmd_fname_full) { - /* Still need to turn the fname into a full path. It is - * postponed to avoid a delay when <afile> is not used. */ - autocmd_fname_full = TRUE; - result = (char_u *)FullName_save((char *)autocmd_fname, FALSE); - xfree(autocmd_fname); - autocmd_fname = result; + case SPEC_AFILE: // file name for autocommand + if (autocmd_fname != NULL && !path_is_absolute(autocmd_fname)) { + // Still need to turn the fname into a full path. It was + // postponed to avoid a delay when <afile> is not used. + result = (char_u *)FullName_save((char *)autocmd_fname, false); + // Copy into `autocmd_fname`, don't reassign it. #8165 + xstrlcpy((char *)autocmd_fname, (char *)result, MAXPATHL); + xfree(result); } + result = autocmd_fname; if (result == NULL) { *errormsg = (char_u *)_( "E495: no autocommand file name to substitute for \"<afile>\""); return NULL; } - result = path_shorten_fname_if_possible(result); + result = path_try_shorten_fname(result); break; case SPEC_ABUF: /* buffer number for autocommand */ @@ -8805,11 +8825,12 @@ makeopens ( && buf->b_fname != NULL && buf->b_p_bl) { if (fprintf(fd, "badd +%" PRId64 " ", - buf->b_wininfo == NULL ? - (int64_t)1L : - (int64_t)buf->b_wininfo->wi_fpos.lnum) < 0 - || ses_fname(fd, buf, &ssop_flags) == FAIL) + buf->b_wininfo == NULL + ? (int64_t)1L + : (int64_t)buf->b_wininfo->wi_fpos.lnum) < 0 + || ses_fname(fd, buf, &ssop_flags, true) == FAIL) { return FAIL; + } } } @@ -8880,11 +8901,13 @@ makeopens ( && !bt_nofile(wp->w_buffer) ) { if (fputs(need_tabnew ? "tabedit " : "edit ", fd) < 0 - || ses_fname(fd, wp->w_buffer, &ssop_flags) == FAIL) + || ses_fname(fd, wp->w_buffer, &ssop_flags, true) == FAIL) { return FAIL; - need_tabnew = FALSE; - if (!wp->w_arg_idx_invalid) + } + need_tabnew = false; + if (!wp->w_arg_idx_invalid) { edited_win = wp; + } break; } } @@ -8928,6 +8951,8 @@ makeopens ( // resized when moving between windows. // Do this before restoring the view, so that the topline and the // cursor can be set. This is done again below. + // winminheight and winminwidth need to be set to avoid an error if the + // user has set winheight or winwidth. if (put_line(fd, "set winminheight=1 winminwidth=1 winheight=1 winwidth=1") == FAIL) { return FAIL; @@ -9216,24 +9241,35 @@ put_view ( if (wp->w_buffer->b_ffname != NULL && (!bt_nofile(wp->w_buffer) || wp->w_buffer->terminal) ) { - /* - * Editing a file in this buffer: use ":edit file". - * This may have side effects! (e.g., compressed or network file). - */ - if (fputs("edit ", fd) < 0 - || ses_fname(fd, wp->w_buffer, flagp) == FAIL) + // Editing a file in this buffer: use ":edit file". + // This may have side effects! (e.g., compressed or network file). + // + // Note, if a buffer for that file already exists, use :badd to + // edit that buffer, to not lose folding information (:edit resets + // folds in other buffers) + if (fputs("if bufexists('", fd) < 0 + || ses_fname(fd, wp->w_buffer, flagp, false) == FAIL + || fputs("') | buffer ", fd) < 0 + || ses_fname(fd, wp->w_buffer, flagp, false) == FAIL + || fputs(" | else | edit ", fd) < 0 + || ses_fname(fd, wp->w_buffer, flagp, false) == FAIL + || fputs(" | endif", fd) < 0 + || put_eol(fd) == FAIL) { return FAIL; + } } else { - /* No file in this buffer, just make it empty. */ - if (put_line(fd, "enew") == FAIL) + // No file in this buffer, just make it empty. + if (put_line(fd, "enew") == FAIL) { return FAIL; + } if (wp->w_buffer->b_ffname != NULL) { - /* The buffer does have a name, but it's not a file name. */ + // The buffer does have a name, but it's not a file name. if (fputs("file ", fd) < 0 - || ses_fname(fd, wp->w_buffer, flagp) == FAIL) + || ses_fname(fd, wp->w_buffer, flagp, true) == FAIL) { return FAIL; + } } - do_cursor = FALSE; + do_cursor = false; } } @@ -9328,15 +9364,18 @@ put_view ( } } - /* - * Local directory. - */ - if (wp->w_localdir != NULL) { + // + // Local directory, if the current flag is not view options or the "curdir" + // option is included. + // + if (wp->w_localdir != NULL + && (flagp != &vop_flags || (*flagp & SSOP_CURDIR))) { if (fputs("lcd ", fd) < 0 || ses_put_fname(fd, wp->w_localdir, flagp) == FAIL - || put_eol(fd) == FAIL) + || put_eol(fd) == FAIL) { return FAIL; - did_lcd = TRUE; + } + did_lcd = true; } return OK; @@ -9373,7 +9412,7 @@ ses_arglist ( (void)vim_FullName((char *)s, (char *)buf, MAXPATHL, FALSE); s = buf; } - if (fputs("argadd ", fd) < 0 || ses_put_fname(fd, s, flagp) == FAIL + if (fputs("$argadd ", fd) < 0 || ses_put_fname(fd, s, flagp) == FAIL || put_eol(fd) == FAIL) { xfree(buf); return FAIL; @@ -9384,12 +9423,10 @@ ses_arglist ( return OK; } -/* - * Write a buffer name to the session file. - * Also ends the line. - * Returns FAIL if writing fails. - */ -static int ses_fname(FILE *fd, buf_T *buf, unsigned *flagp) +/// Write a buffer name to the session file. +/// Also ends the line, if "add_eol" is true. +/// Returns FAIL if writing fails. +static int ses_fname(FILE *fd, buf_T *buf, unsigned *flagp, bool add_eol) { char_u *name; @@ -9406,8 +9443,10 @@ static int ses_fname(FILE *fd, buf_T *buf, unsigned *flagp) name = buf->b_sfname; else name = buf->b_ffname; - if (ses_put_fname(fd, name, flagp) == FAIL || put_eol(fd) == FAIL) + if (ses_put_fname(fd, name, flagp) == FAIL + || (add_eol && put_eol(fd) == FAIL)) { return FAIL; + } return OK; } @@ -9593,6 +9632,16 @@ char_u *get_behave_arg(expand_T *xp, int idx) return NULL; } +// Function given to ExpandGeneric() to obtain the possible arguments of the +// ":messages {clear}" command. +char_u *get_messages_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx) +{ + if (idx == 0) { + return (char_u *)"clear"; + } + return NULL; +} + static TriState filetype_detect = kNone; static TriState filetype_plugin = kNone; static TriState filetype_indent = kNone; @@ -9670,29 +9719,37 @@ static void ex_filetype(exarg_T *eap) EMSG2(_(e_invarg2), arg); } -/// Do ":filetype plugin indent on" if user did not already do some -/// permutation thereof. +/// Set all :filetype options ON if user did not explicitly set any to OFF. void filetype_maybe_enable(void) { - if (filetype_detect == kNone - && filetype_plugin == kNone - && filetype_indent == kNone) { + if (filetype_detect == kNone) { source_runtime((char_u *)FILETYPE_FILE, true); filetype_detect = kTrue; + } + if (filetype_plugin == kNone) { source_runtime((char_u *)FTPLUGIN_FILE, true); filetype_plugin = kTrue; + } + if (filetype_indent == kNone) { source_runtime((char_u *)INDENT_FILE, true); filetype_indent = kTrue; } } -/* - * ":setfiletype {name}" - */ +/// ":setfiletype [FALLBACK] {name}" static void ex_setfiletype(exarg_T *eap) { if (!did_filetype) { - set_option_value("filetype", 0L, (char *)eap->arg, OPT_LOCAL); + char_u *arg = eap->arg; + + if (STRNCMP(arg, "FALLBACK ", 9) == 0) { + arg += 9; + } + + set_option_value("filetype", 0L, (char *)arg, OPT_LOCAL); + if (arg != eap->arg) { + did_filetype = false; + } } } @@ -9820,7 +9877,7 @@ static void ex_terminal(exarg_T *eap) if (*eap->arg != NUL) { // Run {cmd} in 'shell'. char *name = (char *)vim_strsave_escaped(eap->arg, (char_u *)"\"\\"); snprintf(ex_cmd, sizeof(ex_cmd), - ":enew%s | call termopen(\"%s\") | startinsert", + ":enew%s | call termopen(\"%s\")", eap->forceit ? "!" : "", name); xfree(name); } else { // No {cmd}: run the job with tokenized 'shell'. @@ -9842,7 +9899,7 @@ static void ex_terminal(exarg_T *eap) shell_free_argv(argv); snprintf(ex_cmd, sizeof(ex_cmd), - ":enew%s | call termopen([%s]) | startinsert", + ":enew%s | call termopen([%s])", eap->forceit ? "!" : "", shell_argv + 1); } @@ -9851,7 +9908,7 @@ static void ex_terminal(exarg_T *eap) /// Checks if `cmd` is "previewable" (i.e. supported by 'inccommand'). /// -/// @param[in] cmd Commandline to check. May start with a range. +/// @param[in] cmd Commandline to check. May start with a range or modifier. /// /// @return true if `cmd` is previewable bool cmd_can_preview(char_u *cmd) @@ -9860,6 +9917,12 @@ bool cmd_can_preview(char_u *cmd) return false; } + // Ignore any leading modifiers (:keeppatterns, :verbose, etc.) + for (int len = modifier_len(cmd); len != 0; len = modifier_len(cmd)) { + cmd += len; + cmd = skipwhite(cmd); + } + exarg_T ea; // parse the command line ea.cmd = skip_range(cmd, NULL); diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index 5d664b94a8..e23945c842 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -45,22 +45,21 @@ * there or even outside the try conditional. Try conditionals may be nested. */ -/* - * Configuration whether an exception is thrown on error or interrupt. When - * the preprocessor macros below evaluate to FALSE, an error (did_emsg) or - * interrupt (got_int) under an active try conditional terminates the script - * after the non-active finally clauses of all active try conditionals have been - * executed. Otherwise, errors and/or interrupts are converted into catchable - * exceptions (did_throw additionally set), which terminate the script only if - * not caught. For user exceptions, only did_throw is set. (Note: got_int can - * be set asynchronously afterwards by a SIGINT, so did_throw && got_int is not - * a reliant test that the exception currently being thrown is an interrupt - * exception. Similarly, did_emsg can be set afterwards on an error in an - * (unskipped) conditional command inside an inactive conditional, so did_throw - * && did_emsg is not a reliant test that the exception currently being thrown - * is an error exception.) - The macros can be defined as expressions checking - * for a variable that is allowed to be changed during execution of a script. - */ +// Configuration whether an exception is thrown on error or interrupt. When +// the preprocessor macros below evaluate to FALSE, an error (did_emsg) or +// interrupt (got_int) under an active try conditional terminates the script +// after the non-active finally clauses of all active try conditionals have been +// executed. Otherwise, errors and/or interrupts are converted into catchable +// exceptions, which terminate the script only if not caught. For user +// exceptions, only current_exception is set. (Note: got_int can be set +// asynchronously afterwards by a SIGINT, so current_exception && got_int is not +// a reliant test that the exception currently being thrown is an interrupt +// exception. Similarly, did_emsg can be set afterwards on an error in an +// (unskipped) conditional command inside an inactive conditional, so +// current_exception && did_emsg is not a reliant test that the exception +// currently being thrown is an error exception.) - The macros can be defined +// as expressions checking for a variable that is allowed to be changed during +// execution of a script. // Values used for the Vim release. #define THROW_ON_ERROR true @@ -68,6 +67,15 @@ #define THROW_ON_INTERRUPT true #define THROW_ON_INTERRUPT_TRUE +// Don't do something after an error, interrupt, or throw, or when +// there is a surrounding conditional and it was not active. +#define CHECK_SKIP \ + (did_emsg \ + || got_int \ + || current_exception \ + || (cstack->cs_idx > 0 \ + && !(cstack->cs_flags[cstack->cs_idx - 1] & CSF_ACTIVE))) + #define discard_pending_return(p) tv_free((typval_T *)(p)) /* @@ -94,7 +102,7 @@ static int cause_abort = FALSE; */ int aborting(void) { - return (did_emsg && force_abort) || got_int || did_throw; + return (did_emsg && force_abort) || got_int || current_exception; } /* @@ -178,8 +186,9 @@ int cause_errthrow(char_u *mesg, int severe, int *ignore) * currently throwing an exception, do nothing. The message text will * then be stored to v:errmsg by emsg() without displaying it. */ - if (((trylevel == 0 && !cause_abort) || emsg_silent) && !did_throw) - return FALSE; + if (((trylevel == 0 && !cause_abort) || emsg_silent) && !current_exception) { + return false; + } /* * Ignore an interrupt message when inside a try conditional or when an @@ -208,12 +217,13 @@ int cause_errthrow(char_u *mesg, int severe, int *ignore) * exception currently being thrown to prevent it from being caught. Just * execute finally clauses and terminate. */ - if (did_throw) { - /* When discarding an interrupt exception, reset got_int to prevent the - * same interrupt being converted to an exception again and discarding - * the error exception we are about to throw here. */ - if (current_exception->type == ET_INTERRUPT) - got_int = FALSE; + if (current_exception) { + // When discarding an interrupt exception, reset got_int to prevent the + // same interrupt being converted to an exception again and discarding + // the error exception we are about to throw here. + if (current_exception->type == ET_INTERRUPT) { + got_int = false; + } discard_current_exception(); } @@ -333,51 +343,49 @@ void do_errthrow(struct condstack *cstack, char_u *cmdname) */ int do_intthrow(struct condstack *cstack) { - /* - * If no interrupt occurred or no try conditional is active and no exception - * is being thrown, do nothing (for compatibility of non-EH scripts). - */ - if (!got_int || (trylevel == 0 && !did_throw)) - return FALSE; + // If no interrupt occurred or no try conditional is active and no exception + // is being thrown, do nothing (for compatibility of non-EH scripts). + if (!got_int || (trylevel == 0 && !current_exception)) { + return false; + } -#ifdef THROW_TEST /* avoid warning for condition always true */ +#ifdef THROW_TEST // avoid warning for condition always true if (!THROW_ON_INTERRUPT) { - /* - * The interrupt aborts everything except for executing finally clauses. - * Discard any user or error or interrupt exception currently being - * thrown. - */ - if (did_throw) + // The interrupt aborts everything except for executing finally clauses. + // Discard any user or error or interrupt exception currently being + // thrown. + if (current_exception) { discard_current_exception(); - } else + } + } else { #endif - { - /* - * Throw an interrupt exception, so that everything will be aborted - * (except for executing finally clauses), until the interrupt exception - * is caught; if still uncaught at the top level, the script processing - * will be terminated then. - If an interrupt exception is already - * being thrown, do nothing. - * - */ - if (did_throw) { - if (current_exception->type == ET_INTERRUPT) - return FALSE; + // Throw an interrupt exception, so that everything will be aborted + // (except for executing finally clauses), until the interrupt exception + // is caught; if still uncaught at the top level, the script processing + // will be terminated then. - If an interrupt exception is already + // being thrown, do nothing. + + if (current_exception) { + if (current_exception->type == ET_INTERRUPT) { + return false; + } - /* An interrupt exception replaces any user or error exception. */ + // An interrupt exception replaces any user or error exception. discard_current_exception(); } - if (throw_exception("Vim:Interrupt", ET_INTERRUPT, NULL) != FAIL) + if (throw_exception("Vim:Interrupt", ET_INTERRUPT, NULL) != FAIL) { do_throw(cstack); + } +#ifdef THROW_TEST } +#endif - return TRUE; + return true; } -/* - * Get an exception message that is to be stored in current_exception->value. - */ -char_u *get_exception_string(void *value, int type, char_u *cmdname, int *should_free) +// Get an exception message that is to be stored in current_exception->value. +char_u *get_exception_string(void *value, except_type_T type, char_u *cmdname, + int *should_free) { char_u *ret, *mesg; char_u *p, *val; @@ -435,13 +443,11 @@ char_u *get_exception_string(void *value, int type, char_u *cmdname, int *should } -/* - * Throw a new exception. Return FAIL when out of memory or it was tried to - * throw an illegal user exception. "value" is the exception string for a - * user or interrupt exception, or points to a message list in case of an - * error exception. - */ -static int throw_exception(void *value, int type, char_u *cmdname) +// Throw a new exception. Return FAIL when out of memory or it was tried to +// throw an illegal user exception. "value" is the exception string for a +// user or interrupt exception, or points to a message list in case of an +// error exception. +static int throw_exception(void *value, except_type_T type, char_u *cmdname) { except_T *excp; int should_free; @@ -520,7 +526,7 @@ static void discard_exception(except_T *excp, int was_finished) char_u *saved_IObuff; if (excp == NULL) { - EMSG(_(e_internal)); + internal_error("discard_exception()"); return; } @@ -564,10 +570,11 @@ static void discard_exception(except_T *excp, int was_finished) */ void discard_current_exception(void) { - discard_exception(current_exception, FALSE); + discard_exception(current_exception, false); + // Note: all globals manipulated here should be saved/restored in + // try_enter/try_leave. current_exception = NULL; - did_throw = FALSE; - need_rethrow = FALSE; + need_rethrow = false; } /* @@ -620,8 +627,9 @@ static void catch_exception(except_T *excp) */ static void finish_exception(except_T *excp) { - if (excp != caught_stack) - EMSG(_(e_internal)); + if (excp != caught_stack) { + internal_error("finish_exception()"); + } caught_stack = caught_stack->caught; if (caught_stack != NULL) { set_vim_var_string(VV_EXCEPTION, (char *) caught_stack->value, -1); @@ -795,15 +803,7 @@ void ex_if(exarg_T *eap) ++cstack->cs_idx; cstack->cs_flags[cstack->cs_idx] = 0; - /* - * Don't do something after an error, interrupt, or throw, or when there - * is a surrounding conditional and it was not active. - */ - skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0 - && !(cstack->cs_flags[cstack-> - cs_idx - - 1] & - CSF_ACTIVE)); + skip = CHECK_SKIP; bool error; result = eval_to_bool(eap->arg, &error, &eap->nextcmd, skip); @@ -854,15 +854,7 @@ void ex_else(exarg_T *eap) int result; struct condstack *cstack = eap->cstack; - /* - * Don't do something after an error, interrupt, or throw, or when there is - * a surrounding conditional and it was not active. - */ - skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0 - && !(cstack->cs_flags[cstack-> - cs_idx - - 1] & - CSF_ACTIVE)); + skip = CHECK_SKIP; if (cstack->cs_idx < 0 || (cstack->cs_flags[cstack->cs_idx] @@ -952,15 +944,7 @@ void ex_while(exarg_T *eap) cstack->cs_flags[cstack->cs_idx] = eap->cmdidx == CMD_while ? CSF_WHILE : CSF_FOR; - /* - * Don't do something after an error, interrupt, or throw, or when - * there is a surrounding conditional and it was not active. - */ - skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0 - && !(cstack->cs_flags[cstack-> - cs_idx - - 1] & - CSF_ACTIVE)); + skip = CHECK_SKIP; if (eap->cmdidx == CMD_while) { /* * ":while bool-expr" @@ -1233,8 +1217,6 @@ void do_throw(struct condstack *cstack) cstack->cs_flags[idx] &= ~CSF_ACTIVE; cstack->cs_exception[idx] = current_exception; } - - did_throw = TRUE; } /* @@ -1253,15 +1235,7 @@ void ex_try(exarg_T *eap) cstack->cs_flags[cstack->cs_idx] = CSF_TRY; cstack->cs_pending[cstack->cs_idx] = CSTP_NONE; - /* - * Don't do something after an error, interrupt, or throw, or when there - * is a surrounding conditional and it was not active. - */ - skip = did_emsg || got_int || did_throw || (cstack->cs_idx > 0 - && !(cstack->cs_flags[cstack-> - cs_idx - - 1] & - CSF_ACTIVE)); + skip = CHECK_SKIP; if (!skip) { /* Set ACTIVE and TRUE. TRUE means that the corresponding ":catch" @@ -1353,8 +1327,9 @@ void ex_catch(exarg_T *eap) * corresponding try block never got active (because of an inactive * surrounding conditional or after an error or interrupt or throw). */ - if (!did_throw || !(cstack->cs_flags[idx] & CSF_TRUE)) - skip = TRUE; + if (!current_exception || !(cstack->cs_flags[idx] & CSF_TRUE)) { + skip = true; + } /* * Check for a match only if an exception is thrown but not caught by @@ -1413,18 +1388,23 @@ void ex_catch(exarg_T *eap) } if (caught) { - /* Make this ":catch" clause active and reset did_emsg, got_int, - * and did_throw. Put the exception on the caught stack. */ + /* Make this ":catch" clause active and reset did_emsg and got_int. + * Put the exception on the caught stack. */ cstack->cs_flags[idx] |= CSF_ACTIVE | CSF_CAUGHT; - did_emsg = got_int = did_throw = FALSE; + did_emsg = got_int = false; catch_exception((except_T *)cstack->cs_exception[idx]); /* It's mandatory that the current exception is stored in the cstack * so that it can be discarded at the next ":catch", ":finally", or * ":endtry" or when the catch clause is left by a ":continue", * ":break", ":return", ":finish", error, interrupt, or another * exception. */ - if (cstack->cs_exception[cstack->cs_idx] != current_exception) - EMSG(_(e_internal)); + if (cstack->cs_exception[cstack->cs_idx] != current_exception) { + internal_error("ex_catch()"); + } + // Discarding current_exceptions happens based on what is stored in + // cstack->cs_exception, *all* calls to discard_current_exception() are + // (and must be) guarded by current_exception check. + current_exception = NULL; } else { /* * If there is a preceding catch clause and it caught the exception, @@ -1483,7 +1463,7 @@ void ex_finally(exarg_T *eap) * interrupt or throw) or for a ":finally" without ":try" or a multiple * ":finally". After every other error (did_emsg or the conditional * errors detected above) or after an interrupt (got_int) or an - * exception (did_throw), the finally clause must be executed. + * exception (current_exception), the finally clause must be executed. */ skip = !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE); @@ -1510,30 +1490,31 @@ void ex_finally(exarg_T *eap) cleanup_conditionals(cstack, CSF_TRY, FALSE); /* - * Make did_emsg, got_int, did_throw pending. If set, they overrule - * a pending ":continue", ":break", ":return", or ":finish". Then - * we have particularly to discard a pending return value (as done + * Make did_emsg, got_int, current_exception pending. If set, they + * overrule a pending ":continue", ":break", ":return", or ":finish". + * Then we have particularly to discard a pending return value (as done * by the call to cleanup_conditionals() above when did_emsg or * got_int is set). The pending values are restored by the * ":endtry", except if there is a new error, interrupt, exception, * ":continue", ":break", ":return", or ":finish" in the following * finally clause. A missing ":endwhile", ":endfor" or ":endif" - * detected here is treated as if did_emsg and did_throw had + * detected here is treated as if did_emsg and current_exception had * already been set, respectively in case that the error is not - * converted to an exception, did_throw had already been unset. + * converted to an exception, current_exception had already been unset. * We must not set did_emsg here since that would suppress the * error message. */ - if (pending == CSTP_ERROR || did_emsg || got_int || did_throw) { + if (pending == CSTP_ERROR || did_emsg || got_int || current_exception) { if (cstack->cs_pending[cstack->cs_idx] == CSTP_RETURN) { report_discard_pending(CSTP_RETURN, cstack->cs_rettv[cstack->cs_idx]); discard_pending_return(cstack->cs_rettv[cstack->cs_idx]); } - if (pending == CSTP_ERROR && !did_emsg) - pending |= (THROW_ON_ERROR) ? CSTP_THROW : 0; - else - pending |= did_throw ? CSTP_THROW : 0; + if (pending == CSTP_ERROR && !did_emsg) { + pending |= (THROW_ON_ERROR ? CSTP_THROW : 0); + } else { + pending |= (current_exception ? CSTP_THROW : 0); + } pending |= did_emsg ? CSTP_ERROR : 0; pending |= got_int ? CSTP_INTERRUPT : 0; assert(pending >= CHAR_MIN && pending <= CHAR_MAX); @@ -1546,14 +1527,15 @@ void ex_finally(exarg_T *eap) * exception. When emsg() is called for a missing ":endif" or * a missing ":endwhile"/":endfor" detected here, the * exception will be discarded. */ - if (did_throw && cstack->cs_exception[cstack->cs_idx] - != current_exception) - EMSG(_(e_internal)); + if (current_exception + && cstack->cs_exception[cstack->cs_idx] != current_exception) { + internal_error("ex_finally()"); + } } /* * Set CSL_HAD_FINA, so do_cmdline() will reset did_emsg, - * got_int, and did_throw and make the finally clause active. + * got_int, and current_exception and make the finally clause active. * This will happen after emsg() has been called for a missing * ":endif" or a missing ":endwhile"/":endfor" detected here, so * that the following finally clause will be executed even then. @@ -1588,7 +1570,7 @@ void ex_endtry(exarg_T *eap) // made inactive by a ":continue", ":break", ":return", or ":finish" in // the finally clause. The latter case need not be tested since then // anything pending has already been discarded. - skip = (did_emsg || got_int || did_throw + skip = (did_emsg || got_int || current_exception || !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE)); if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) { @@ -1605,12 +1587,13 @@ void ex_endtry(exarg_T *eap) /* * If an exception is being thrown, discard it to prevent it from * being rethrown at the end of this function. It would be - * discarded by the error message, anyway. Resets did_throw. + * discarded by the error message, anyway. Resets current_exception. * This does not affect the script termination due to the error * since "trylevel" is decremented after emsg() has been called. */ - if (did_throw) + if (current_exception) { discard_current_exception(); + } } else { idx = cstack->cs_idx; @@ -1620,9 +1603,11 @@ void ex_endtry(exarg_T *eap) * a finally clause, we need to rethrow it after closing the try * conditional. */ - if (did_throw && (cstack->cs_flags[idx] & CSF_TRUE) - && !(cstack->cs_flags[idx] & CSF_FINALLY)) - rethrow = TRUE; + if (current_exception + && (cstack->cs_flags[idx] & CSF_TRUE) + && !(cstack->cs_flags[idx] & CSF_FINALLY)) { + rethrow = true; + } } /* If there was no finally clause, show the user when debugging or @@ -1643,11 +1628,12 @@ void ex_endtry(exarg_T *eap) if (got_int) { skip = TRUE; (void)do_intthrow(cstack); - /* The do_intthrow() call may have reset did_throw or - * cstack->cs_pending[idx].*/ - rethrow = FALSE; - if (did_throw && !(cstack->cs_flags[idx] & CSF_FINALLY)) - rethrow = TRUE; + // The do_intthrow() call may have reset current_exception or + // cstack->cs_pending[idx]. + rethrow = false; + if (current_exception && !(cstack->cs_flags[idx] & CSF_FINALLY)) { + rethrow = true; + } } } @@ -1709,26 +1695,30 @@ void ex_endtry(exarg_T *eap) do_finish(eap, FALSE); break; - /* When the finally clause was entered due to an error, - * interrupt or throw (as opposed to a ":continue", ":break", - * ":return", or ":finish"), restore the pending values of - * did_emsg, got_int, and did_throw. This is skipped, if there - * was a new error, interrupt, throw, ":continue", ":break", - * ":return", or ":finish". in the finally clause. */ + // When the finally clause was entered due to an error, + // interrupt or throw (as opposed to a ":continue", ":break", + // ":return", or ":finish"), restore the pending values of + // did_emsg, got_int, and current_exception. This is skipped, if there + // was a new error, interrupt, throw, ":continue", ":break", + // ":return", or ":finish". in the finally clause. default: - if (pending & CSTP_ERROR) - did_emsg = TRUE; - if (pending & CSTP_INTERRUPT) - got_int = TRUE; - if (pending & CSTP_THROW) - rethrow = TRUE; + if (pending & CSTP_ERROR) { + did_emsg = true; + } + if (pending & CSTP_INTERRUPT) { + got_int = true; + } + if (pending & CSTP_THROW) { + rethrow = true; + } break; } } - if (rethrow) - /* Rethrow the current exception (within this cstack). */ + if (rethrow) { + // Rethrow the current exception (within this cstack). do_throw(cstack); + } } } @@ -1758,33 +1748,34 @@ void enter_cleanup(cleanup_T *csp) int pending = CSTP_NONE; /* - * Postpone did_emsg, got_int, did_throw. The pending values will be + * Postpone did_emsg, got_int, current_exception. The pending values will be * restored by leave_cleanup() except if there was an aborting error, * interrupt, or uncaught exception after this function ends. */ - if (did_emsg || got_int || did_throw || need_rethrow) { - csp->pending = (did_emsg ? CSTP_ERROR : 0) - | (got_int ? CSTP_INTERRUPT : 0) - | (did_throw ? CSTP_THROW : 0) - | (need_rethrow ? CSTP_THROW : 0); - - /* If we are currently throwing an exception (did_throw), save it as - * well. On an error not yet converted to an exception, update - * "force_abort" and reset "cause_abort" (as do_errthrow() would do). - * This is needed for the do_cmdline() call that is going to be made - * for autocommand execution. We need not save *msg_list because - * there is an extra instance for every call of do_cmdline(), anyway. + if (did_emsg || got_int || current_exception || need_rethrow) { + csp->pending = (did_emsg ? CSTP_ERROR : 0) + | (got_int ? CSTP_INTERRUPT : 0) + | (current_exception ? CSTP_THROW : 0) + | (need_rethrow ? CSTP_THROW : 0); + + /* If we are currently throwing an exception, save it as well. On an error + * not yet converted to an exception, update "force_abort" and reset + * "cause_abort" (as do_errthrow() would do). This is needed for the + * do_cmdline() call that is going to be made for autocommand execution. We + * need not save *msg_list because there is an extra instance for every call + * of do_cmdline(), anyway. */ - if (did_throw || need_rethrow) + if (current_exception || need_rethrow) { csp->exception = current_exception; - else { + } else { csp->exception = NULL; if (did_emsg) { force_abort |= cause_abort; cause_abort = FALSE; } } - did_emsg = got_int = did_throw = need_rethrow = FALSE; + did_emsg = got_int = need_rethrow = false; + current_exception = NULL; /* Report if required by the 'verbose' option or when debugging. */ report_make_pending(pending, csp->exception); @@ -1856,19 +1847,20 @@ void leave_cleanup(cleanup_T *csp) force_abort = FALSE; } - /* - * Restore the pending values of did_emsg, got_int, and did_throw. - */ - if (pending & CSTP_ERROR) - did_emsg = TRUE; - if (pending & CSTP_INTERRUPT) - got_int = TRUE; - if (pending & CSTP_THROW) - need_rethrow = TRUE; /* did_throw will be set by do_one_cmd() */ + // Restore the pending values of did_emsg, got_int, and current_exception. + if (pending & CSTP_ERROR) { + did_emsg = true; + } + if (pending & CSTP_INTERRUPT) { + got_int = true; + } + if (pending & CSTP_THROW) { + need_rethrow = true; // current_exception will be set by do_one_cmd() + } - /* Report if required by the 'verbose' option or when debugging. */ - report_resume_pending(pending, - (pending & CSTP_THROW) ? (void *)current_exception : NULL); + // Report if required by the 'verbose' option or when debugging. + report_resume_pending( + pending, ((pending & CSTP_THROW) ? (void *)current_exception : NULL)); } } diff --git a/src/nvim/ex_eval.h b/src/nvim/ex_eval.h index f61e01d25b..d5f8737bf3 100644 --- a/src/nvim/ex_eval.h +++ b/src/nvim/ex_eval.h @@ -89,28 +89,29 @@ struct msglist { struct msglist *next; /* next of several messages in a row */ }; +// The exception types. +typedef enum +{ + ET_USER, // exception caused by ":throw" command + ET_ERROR, // error exception + ET_INTERRUPT // interrupt exception triggered by Ctrl-C +} except_type_T; + /* * Structure describing an exception. * (don't use "struct exception", it's used by the math library). */ typedef struct vim_exception except_T; struct vim_exception { - int type; /* exception type */ - char_u *value; /* exception value */ - struct msglist *messages; /* message(s) causing error exception */ - char_u *throw_name; /* name of the throw point */ - linenr_T throw_lnum; /* line number of the throw point */ - except_T *caught; /* next exception on the caught stack */ + except_type_T type; // exception type + char_u *value; // exception value + struct msglist *messages; // message(s) causing error exception + char_u *throw_name; // name of the throw point + linenr_T throw_lnum; // line number of the throw point + except_T *caught; // next exception on the caught stack }; /* - * The exception types. - */ -#define ET_USER 0 /* exception caused by ":throw" command */ -#define ET_ERROR 1 /* error exception */ -#define ET_INTERRUPT 2 /* interrupt exception triggered by Ctrl-C */ - -/* * Structure to save the error/interrupt/exception state between calls to * enter_cleanup() and leave_cleanup(). Must be allocated as an automatic * variable by the (common) caller of these functions. diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 7793081957..698419405a 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -12,6 +12,7 @@ #include <inttypes.h> #include "nvim/assert.h" +#include "nvim/log.h" #include "nvim/vim.h" #include "nvim/ascii.h" #include "nvim/arabic.h" @@ -62,6 +63,42 @@ #include "nvim/os/os.h" #include "nvim/event/loop.h" #include "nvim/os/time.h" +#include "nvim/lib/kvec.h" +#include "nvim/api/private/helpers.h" +#include "nvim/highlight_defs.h" +#include "nvim/viml/parser/parser.h" +#include "nvim/viml/parser/expressions.h" + +/// Command-line colors: one chunk +/// +/// Defines a region which has the same highlighting. +typedef struct { + int start; ///< Colored chunk start. + int end; ///< Colored chunk end (exclusive, > start). + int attr; ///< Highlight attr. +} CmdlineColorChunk; + +/// Command-line colors +/// +/// Holds data about all colors. +typedef kvec_t(CmdlineColorChunk) CmdlineColors; + +/// Command-line coloring +/// +/// Holds both what are the colors and what have been colored. Latter is used to +/// suppress unnecessary calls to coloring callbacks. +typedef struct { + unsigned prompt_id; ///< ID of the prompt which was colored last. + char *cmdbuff; ///< What exactly was colored last time or NULL. + CmdlineColors colors; ///< Last colors. +} ColoredCmdline; + +/// Keeps track how much state must be sent to external ui. +typedef enum { + kCmdRedrawNone, + kCmdRedrawPos, + kCmdRedrawAll, +} CmdRedraw; /* * Variables shared between getcmdline(), redrawcmdline() and others. @@ -69,23 +106,33 @@ * structure. */ struct cmdline_info { - char_u *cmdbuff; /* pointer to command line buffer */ - int cmdbufflen; /* length of cmdbuff */ - int cmdlen; /* number of chars in command line */ - int cmdpos; /* current cursor position */ - int cmdspos; /* cursor column on screen */ - int cmdfirstc; /* ':', '/', '?', '=', '>' or NUL */ - int cmdindent; /* number of spaces before cmdline */ - char_u *cmdprompt; /* message in front of cmdline */ - int cmdattr; /* attributes for prompt */ - int overstrike; /* Typing mode on the command line. Shared by - getcmdline() and put_on_cmdline(). */ - expand_T *xpc; /* struct being used for expansion, xp_pattern - may point into cmdbuff */ - int xp_context; /* type of expansion */ - char_u *xp_arg; /* user-defined expansion arg */ - int input_fn; /* when TRUE Invoked for input() function */ + char_u *cmdbuff; // pointer to command line buffer + int cmdbufflen; // length of cmdbuff + int cmdlen; // number of chars in command line + int cmdpos; // current cursor position + int cmdspos; // cursor column on screen + int cmdfirstc; // ':', '/', '?', '=', '>' or NUL + int cmdindent; // number of spaces before cmdline + char_u *cmdprompt; // message in front of cmdline + int cmdattr; // attributes for prompt + int overstrike; // Typing mode on the command line. Shared by + // getcmdline() and put_on_cmdline(). + expand_T *xpc; // struct being used for expansion, xp_pattern + // may point into cmdbuff + int xp_context; // type of expansion + char_u *xp_arg; // user-defined expansion arg + int input_fn; // when TRUE Invoked for input() function + unsigned prompt_id; ///< Prompt number, used to disable coloring on errors. + Callback highlight_callback; ///< Callback used for coloring user input. + ColoredCmdline last_colors; ///< Last cmdline colors + int level; // current cmdline level + struct cmdline_info *prev_ccline; ///< pointer to saved cmdline state + char special_char; ///< last putcmdline char (used for redraws) + bool special_shift; ///< shift of last putcmdline char + CmdRedraw redraw_state; ///< needed redraw for external cmdline }; +/// Last value of prompt_id, incremented when doing new prompt +static unsigned last_prompt_id = 0; typedef struct command_line_state { VimState state; @@ -135,6 +182,8 @@ typedef struct command_line_state { struct cmdline_info save_ccline; } CommandLineState; +typedef struct cmdline_info CmdlineInfo; + /* The current cmdline_info. It is initialized in getcmdline() and after that * used by other functions. When invoking getcmdline() recursively it needs * to be saved with save_cmdline() and restored with restore_cmdline(). @@ -145,6 +194,9 @@ static int cmd_showtail; /* Only show path tail in lists ? */ static int new_cmdpos; /* position set by set_cmdline_pos() */ +/// currently displayed block of context +static Array cmdline_block = ARRAY_DICT_INIT; + /* * Type used by call_user_expand_func */ @@ -156,6 +208,12 @@ static int hisnum[HIST_COUNT] = {0, 0, 0, 0, 0}; /* identifying (unique) number of newest history entry */ static int hislen = 0; /* actual length of history tables */ +/// Flag for command_line_handle_key to ignore <C-c> +/// +/// Used if it was received while processing highlight function in order for +/// user interrupting highlight function to not interrupt command-line. +static bool getln_interrupted_highlight = false; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_getln.c.generated.h" @@ -192,6 +250,8 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) cmd_hkmap = 0; } + ccline.prompt_id = last_prompt_id++; + ccline.level++; ccline.overstrike = false; // always start in insert mode clearpos(&s->match_end); s->save_cursor = curwin->w_cursor; // may be restored later @@ -211,6 +271,9 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) ccline.cmdlen = ccline.cmdpos = 0; ccline.cmdbuff[0] = NUL; + ccline.last_colors = (ColoredCmdline){ .cmdbuff = NULL, + .colors = KV_INITIAL_VALUE }; + // autoindent for :insert and :append if (s->firstc <= 0) { memset(ccline.cmdbuff, ' ', s->indent); @@ -288,8 +351,57 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) got_int = false; s->state.check = command_line_check; s->state.execute = command_line_execute; + + TryState tstate; + Error err = ERROR_INIT; + bool tl_ret = true; + dict_T *dict = get_vim_var_dict(VV_EVENT); + char firstcbuf[2]; + firstcbuf[0] = firstc > 0 ? firstc : '-'; + firstcbuf[1] = 0; + + if (has_event(EVENT_CMDLINEENTER)) { + // set v:event to a dictionary with information about the commandline + tv_dict_add_str(dict, S_LEN("cmdtype"), firstcbuf); + tv_dict_add_nr(dict, S_LEN("cmdlevel"), ccline.level); + tv_dict_set_keys_readonly(dict); + try_enter(&tstate); + + apply_autocmds(EVENT_CMDLINEENTER, (char_u *)firstcbuf, (char_u *)firstcbuf, + false, curbuf); + tv_dict_clear(dict); + + + tl_ret = try_leave(&tstate, &err); + if (!tl_ret && ERROR_SET(&err)) { + msg_putchar('\n'); + msg_printf_attr(hl_attr(HLF_E)|MSG_HIST, (char *)e_autocmd_err, err.msg); + api_clear_error(&err); + redrawcmd(); + } + tl_ret = true; + } + state_enter(&s->state); + if (has_event(EVENT_CMDLINELEAVE)) { + tv_dict_add_str(dict, S_LEN("cmdtype"), firstcbuf); + tv_dict_add_nr(dict, S_LEN("cmdlevel"), ccline.level); + tv_dict_set_keys_readonly(dict); + // not readonly: + tv_dict_add_special(dict, S_LEN("abort"), + s->gotesc ? kSpecialVarTrue : kSpecialVarFalse); + try_enter(&tstate); + apply_autocmds(EVENT_CMDLINELEAVE, (char_u *)firstcbuf, (char_u *)firstcbuf, + false, curbuf); + // error printed below, to avoid redraw issues + tl_ret = try_leave(&tstate, &err); + if (tv_dict_get_number(dict, "abort") != 0) { + s->gotesc = 1; + } + tv_dict_clear(dict); + } + cmdmsg_rl = false; cmd_fkmap = 0; @@ -306,7 +418,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) curwin->w_cursor = s->save_cursor; setpcmark(); } - curwin->w_cursor = s->search_start; + curwin->w_cursor = s->search_start; // -V519 } curwin->w_curswant = s->old_curswant; curwin->w_leftcol = s->old_leftcol; @@ -315,7 +427,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) curwin->w_botline = s->old_botline; highlight_match = false; validate_cursor(); // needed for TAB - redraw_later(SOME_VALID); + redraw_all_later(SOME_VALID); } if (ccline.cmdbuff != NULL) { @@ -332,14 +444,8 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) } } - if (s->gotesc) { // abandon command line - xfree(ccline.cmdbuff); - ccline.cmdbuff = NULL; - if (msg_scrolled == 0) { - compute_cmdrow(); - } - MSG(""); - redraw_cmdline = true; + if (s->gotesc) { + abandon_cmdline(); } } @@ -350,8 +456,14 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) msg_scroll = s->save_msg_scroll; redir_off = false; + if (!tl_ret && ERROR_SET(&err)) { + msg_putchar('\n'); + msg_printf_attr(hl_attr(HLF_E)|MSG_HIST, (char *)e_autocmd_err, err.msg); + api_clear_error(&err); + } + // When the command line was typed, no need for a wait-return prompt. - if (s->some_key_typed) { + if (s->some_key_typed && tl_ret) { need_wait_return = false; } @@ -361,12 +473,20 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) setmouse(); ui_cursor_shape(); // may show different cursor shape xfree(s->save_p_icm); + xfree(ccline.last_colors.cmdbuff); + kv_destroy(ccline.last_colors.colors); { char_u *p = ccline.cmdbuff; // Make ccline empty, getcmdline() may try to use it. ccline.cmdbuff = NULL; + + if (ui_is_external(kUICmdline)) { + ccline.redraw_state = kCmdRedrawNone; + ui_call_cmdline_hide(ccline.level); + } + ccline.level--; return p; } } @@ -392,8 +512,12 @@ static int command_line_execute(VimState *state, int key) CommandLineState *s = (CommandLineState *)state; s->c = key; - if (s->c == K_EVENT) { - multiqueue_process_events(main_loop.events); + if (s->c == K_EVENT || s->c == K_COMMAND) { + if (s->c == K_EVENT) { + multiqueue_process_events(main_loop.events); + } else { + do_cmdline(NULL, getcmdkeycmd, NULL, DOCMD_NOWAIT); + } redrawcmdline(); return 1; } @@ -472,11 +596,15 @@ static int command_line_execute(VimState *state, int key) } // free expanded names when finished walking through matches - if (s->xpc.xp_numfiles != -1 - && !(s->c == p_wc && KeyTyped) && s->c != p_wcm + if (!(s->c == p_wc && KeyTyped) && s->c != p_wcm && s->c != Ctrl_N && s->c != Ctrl_P && s->c != Ctrl_A && s->c != Ctrl_L) { - (void)ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE); + if (ui_is_external(kUIWildmenu)) { + ui_call_wildmenu_hide(); + } + if (s->xpc.xp_numfiles != -1) { + (void)ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE); + } s->did_wild_list = false; if (!p_wmnu || (s->c != K_UP && s->c != K_DOWN)) { s->xpc.xp_context = EXPAND_NOTHING; @@ -609,7 +737,7 @@ static int command_line_execute(VimState *state, int key) } if (vim_ispathsep(ccline.cmdbuff[s->j]) #ifdef BACKSLASH_IN_FILENAME - && vim_strchr(" *?[{`$%#", ccline.cmdbuff[s->j + 1]) + && vim_strchr((const char_u *)" *?[{`$%#", ccline.cmdbuff[s->j + 1]) == NULL #endif ) { @@ -756,7 +884,9 @@ static int command_line_execute(VimState *state, int key) } if (!cmd_silent) { - ui_cursor_goto(msg_row, 0); + if (!ui_is_external(kUICmdline)) { + ui_cursor_goto(msg_row, 0); + } ui_flush(); } return 0; @@ -888,17 +1018,36 @@ static void command_line_next_incsearch(CommandLineState *s, bool next_match) ui_flush(); pos_T t; - int search_flags = SEARCH_KEEP + SEARCH_NOOF + SEARCH_PEEK; + char_u *pat; + int search_flags = SEARCH_NOOF; + + + if (s->firstc == ccline.cmdbuff[0]) { + pat = last_search_pattern(); + } else { + pat = ccline.cmdbuff; + } + + save_last_search_pattern(); + if (next_match) { t = s->match_end; + if (lt(s->match_start, s->match_end)) { + // start searching at the end of the match + // not at the beginning of the next column + (void)decl(&t); + } search_flags += SEARCH_COL; } else { t = s->match_start; } + if (!p_hls) { + search_flags += SEARCH_KEEP; + } emsg_off++; s->i = searchit(curwin, curbuf, &t, next_match ? FORWARD : BACKWARD, - ccline.cmdbuff, s->count, search_flags, + pat, s->count, search_flags, RE_SEARCH, 0, NULL); emsg_off--; ui_busy_stop(); @@ -912,6 +1061,12 @@ static void command_line_next_incsearch(CommandLineState *s, bool next_match) // put back on the match s->search_start = t; (void)decl(&s->search_start); + } else if (next_match && s->firstc == '?') { + // move just after the current match, so that + // when nv_search finishes the cursor will be + // put back on the match + s->search_start = t; + (void)incl(&s->search_start); } if (lt(t, s->search_start) && next_match) { // wrap around @@ -939,6 +1094,7 @@ static void command_line_next_incsearch(CommandLineState *s, bool next_match) } else { vim_beep(BO_ERROR); } + restore_last_search_pattern(); return; } @@ -1086,7 +1242,7 @@ static int command_line_handle_key(CommandLineState *s) xfree(ccline.cmdbuff); // no commandline to return ccline.cmdbuff = NULL; - if (!cmd_silent) { + if (!cmd_silent && !ui_is_external(kUICmdline)) { if (cmdmsg_rl) { msg_col = Columns; } else { @@ -1158,8 +1314,11 @@ static int command_line_handle_key(CommandLineState *s) case ESC: // get here if p_wc != ESC or when ESC typed twice case Ctrl_C: // In exmode it doesn't make sense to return. Except when - // ":normal" runs out of characters. - if (exmode_active && (ex_normal_busy == 0 || typebuf.tb_len > 0)) { + // ":normal" runs out of characters. Also when highlight callback is active + // <C-c> should interrupt only it. + if ((exmode_active && (ex_normal_busy == 0 || typebuf.tb_len > 0)) + || (getln_interrupted_highlight && s->c == Ctrl_C)) { + getln_interrupted_highlight = false; return command_line_not_changed(s); } @@ -1222,6 +1381,7 @@ static int command_line_handle_key(CommandLineState *s) break; // Use ^D as normal char instead } + wild_menu_showing = WM_LIST; redrawcmd(); return 1; // don't do incremental search now @@ -1394,7 +1554,7 @@ static int command_line_handle_key(CommandLineState *s) } if (s->c != NUL) { if (s->c == s->firstc - || vim_strchr((char_u *)(p_magic ? "\\^$.*[" : "\\^$"), s->c) + || vim_strchr((char_u *)(p_magic ? "\\~^$.*[" : "\\^$"), s->c) != NULL) { // put a backslash before special characters stuffcharReadbuff(s->c); @@ -1452,7 +1612,7 @@ static int command_line_handle_key(CommandLineState *s) if (s->hiscnt != s->i) { // jumped to other entry char_u *p; - int len; + int len = 0; int old_firstc; xfree(ccline.cmdbuff); @@ -1518,10 +1678,9 @@ static int command_line_handle_key(CommandLineState *s) case Ctrl_G: // next match case Ctrl_T: // previous match if (p_is && !cmd_silent && (s->firstc == '/' || s->firstc == '?')) { - if (char_avail()) { - return 1; + if (ccline.cmdlen != 0) { + command_line_next_incsearch(s, s->c == Ctrl_G); } - command_line_next_incsearch(s, s->c == Ctrl_G); return command_line_not_changed(s); } break; @@ -1534,9 +1693,14 @@ static int command_line_handle_key(CommandLineState *s) s->do_abbr = false; // don't do abbreviation now // may need to remove ^ when composing char was typed if (enc_utf8 && utf_iscomposing(s->c) && !cmd_silent) { - draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos); - msg_putchar(' '); - cursorcmd(); + if (ui_is_external(kUICmdline)) { + // TODO(bfredl): why not make unputcmdline also work with true? + unputcmdline(); + } else { + draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos); + msg_putchar(' '); + cursorcmd(); + } } break; @@ -1568,14 +1732,6 @@ static int command_line_handle_key(CommandLineState *s) } return command_line_not_changed(s); - case K_FOCUSGAINED: // Neovim has been given focus - apply_autocmds(EVENT_FOCUSGAINED, NULL, NULL, false, curbuf); - return command_line_not_changed(s); - - case K_FOCUSLOST: // Neovim has lost focus - apply_autocmds(EVENT_FOCUSLOST, NULL, NULL, false, curbuf); - return command_line_not_changed(s); - default: // Normal character with no special meaning. Just set mod_mask // to 0x0 so that typing Shift-Space in the GUI doesn't enter @@ -1627,6 +1783,20 @@ static int command_line_not_changed(CommandLineState *s) return command_line_changed(s); } +/// Guess that the pattern matches everything. Only finds specific cases, such +/// as a trailing \|, which can happen while typing a pattern. +static int empty_pattern(char_u *p) +{ + int n = STRLEN(p); + + // remove trailing \v and the like + while (n >= 2 && p[n - 2] == '\\' + && vim_strchr((char_u *)"mMvVcCZ", p[n - 1]) != NULL) { + n -= 2; + } + return n == 0 || (n >= 2 && p[n - 2] == '\\' && p[n - 1] == '|'); +} + static int command_line_changed(CommandLineState *s) { // 'incsearch' highlighting. @@ -1641,20 +1811,27 @@ static int command_line_changed(CommandLineState *s) } s->incsearch_postponed = false; curwin->w_cursor = s->search_start; // start at old position + save_last_search_pattern(); // If there is no command line, don't do anything if (ccline.cmdlen == 0) { s->i = 0; + SET_NO_HLSEARCH(true); // turn off previous highlight + redraw_all_later(SOME_VALID); } else { + int search_flags = SEARCH_OPT + SEARCH_NOOF + SEARCH_PEEK; ui_busy_start(); ui_flush(); ++emsg_off; // So it doesn't beep if bad expr // Set the time limit to half a second. tm = profile_setlimit(500L); + if (!p_hls) { + search_flags += SEARCH_KEEP; + } s->i = do_search(NULL, s->firstc, ccline.cmdbuff, s->count, - SEARCH_KEEP + SEARCH_OPT + SEARCH_NOOF + SEARCH_PEEK, - &tm); - --emsg_off; + search_flags, + &tm); + emsg_off--; // if interrupted while searching, behave like it failed if (got_int) { (void)vpeekc(); // remove <C-C> from input stream @@ -1695,6 +1872,12 @@ static int command_line_changed(CommandLineState *s) end_pos = curwin->w_cursor; // shutup gcc 4 } + // Disable 'hlsearch' highlighting if the pattern matches + // everything. Avoids a flash when typing "foo\|". + if (empty_pattern(ccline.cmdbuff)) { + SET_NO_HLSEARCH(true); + } + validate_cursor(); // May redraw the status line to show the cursor position. if (p_ru && curwin->w_status_height > 0) { @@ -1704,6 +1887,7 @@ static int command_line_changed(CommandLineState *s) save_cmdline(&s->save_ccline); update_screen(SOME_VALID); restore_cmdline(&s->save_ccline); + restore_last_search_pattern(); // Leave it at the end to make CTRL-R CTRL-W work. if (s->i != 0) { @@ -1750,7 +1934,8 @@ static int command_line_changed(CommandLineState *s) // right-left typing. Not efficient, but it works. // Do it only when there are no characters left to read // to avoid useless intermediate redraws. - if (vpeekc() == NUL) { + // if cmdline is external the ui handles shaping, no redraw needed. + if (!ui_is_external(kUICmdline) && vpeekc() == NUL) { redrawcmd(); } } @@ -1758,6 +1943,18 @@ static int command_line_changed(CommandLineState *s) return 1; } +/// Abandon the command line. +static void abandon_cmdline(void) +{ + xfree(ccline.cmdbuff); + ccline.cmdbuff = NULL; + if (msg_scrolled == 0) { + compute_cmdrow(); + } + MSG(""); + redraw_cmdline = true; +} + /* * getcmdline() - accept a command line starting with firstc. * @@ -1787,41 +1984,54 @@ getcmdline ( return command_line_enter(firstc, count, indent); } -/* - * Get a command line with a prompt. - * This is prepared to be called recursively from getcmdline() (e.g. by - * f_input() when evaluating an expression from CTRL-R =). - * Returns the command line in allocated memory, or NULL. - */ -char_u * -getcmdline_prompt ( - int firstc, - char_u *prompt, /* command line prompt */ - int attr, /* attributes for prompt */ - int xp_context, /* type of expansion */ - char_u *xp_arg /* user-defined expansion argument */ -) +/// Get a command line with a prompt +/// +/// This is prepared to be called recursively from getcmdline() (e.g. by +/// f_input() when evaluating an expression from `<C-r>=`). +/// +/// @param[in] firstc Prompt type: e.g. '@' for input(), '>' for debug. +/// @param[in] prompt Prompt string: what is displayed before the user text. +/// @param[in] attr Prompt highlighting. +/// @param[in] xp_context Type of expansion. +/// @param[in] xp_arg User-defined expansion argument. +/// @param[in] highlight_callback Callback used for highlighting user input. +/// +/// @return [allocated] Command line or NULL. +char *getcmdline_prompt(const char firstc, const char *const prompt, + const int attr, const int xp_context, + const char *const xp_arg, + const Callback highlight_callback) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC { - char_u *s; - struct cmdline_info save_ccline; - int msg_col_save = msg_col; + const int msg_col_save = msg_col; + struct cmdline_info save_ccline; save_cmdline(&save_ccline); - ccline.cmdprompt = prompt; + + ccline.prompt_id = last_prompt_id++; + ccline.cmdprompt = (char_u *)prompt; ccline.cmdattr = attr; ccline.xp_context = xp_context; - ccline.xp_arg = xp_arg; + ccline.xp_arg = (char_u *)xp_arg; ccline.input_fn = (firstc == '@'); - s = getcmdline(firstc, 1L, 0); + ccline.highlight_callback = highlight_callback; + + int msg_silent_saved = msg_silent; + msg_silent = 0; + + char *const ret = (char *)getcmdline(firstc, 1L, 0); + restore_cmdline(&save_ccline); - /* Restore msg_col, the prompt from input() may have changed it. - * But only if called recursively and the commandline is therefore being - * restored to an old one; if not, the input() prompt stays on the screen, - * so we need its modified msg_col left intact. */ - if (ccline.cmdbuff != NULL) + msg_silent = msg_silent_saved; + // Restore msg_col, the prompt from input() may have changed it. + // But only if called recursively and the commandline is therefore being + // restored to an old one; if not, the input() prompt stays on the screen, + // so we need its modified msg_col left intact. + if (ccline.cmdbuff != NULL) { msg_col = msg_col_save; + } - return s; + return ret; } /* @@ -2018,7 +2228,13 @@ getexmodeline ( /* Get one character at a time. Don't use inchar(), it can't handle * special characters. */ prev_char = c1; - c1 = vgetc(); + + // Check for a ":normal" command and no more characters left. + if (ex_normal_busy > 0 && typebuf.tb_len == 0) { + c1 = '\n'; + } else { + c1 = vgetc(); + } /* * Handle line editing. @@ -2282,75 +2498,389 @@ void free_cmdline_buf(void) # endif +enum { MAX_CB_ERRORS = 1 }; + +/// Color expression cmdline using built-in expressions parser +/// +/// @param[in] colored_ccline Command-line to color. +/// @param[out] ret_ccline_colors What should be colored. +/// +/// Always colors the whole cmdline. +static void color_expr_cmdline(const CmdlineInfo *const colored_ccline, + ColoredCmdline *const ret_ccline_colors) + FUNC_ATTR_NONNULL_ALL +{ + ParserLine plines[] = { + { + .data = (const char *)colored_ccline->cmdbuff, + .size = STRLEN(colored_ccline->cmdbuff), + .allocated = false, + }, + { NULL, 0, false }, + }; + ParserLine *plines_p = plines; + ParserHighlight colors; + kvi_init(colors); + ParserState pstate; + viml_parser_init( + &pstate, parser_simple_get_line, &plines_p, &colors); + ExprAST east = viml_pexpr_parse(&pstate, kExprFlagsDisallowEOC); + viml_pexpr_free_ast(east); + viml_parser_destroy(&pstate); + kv_resize(ret_ccline_colors->colors, kv_size(colors)); + size_t prev_end = 0; + for (size_t i = 0 ; i < kv_size(colors) ; i++) { + const ParserHighlightChunk chunk = kv_A(colors, i); + if (chunk.start.col != prev_end) { + kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) { + .start = prev_end, + .end = chunk.start.col, + .attr = 0, + })); + } + const int id = syn_name2id((const char_u *)chunk.group); + const int attr = (id == 0 ? 0 : syn_id2attr(id)); + kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) { + .start = chunk.start.col, + .end = chunk.end_col, + .attr = attr, + })); + prev_end = chunk.end_col; + } + if (prev_end < (size_t)colored_ccline->cmdlen) { + kv_push(ret_ccline_colors->colors, ((CmdlineColorChunk) { + .start = prev_end, + .end = (size_t)colored_ccline->cmdlen, + .attr = 0, + })); + } + kvi_destroy(colors); +} + +/// Color command-line +/// +/// Should use built-in command parser or user-specified one. Currently only the +/// latter is supported. +/// +/// @param[in,out] colored_ccline Command-line to color. Also holds a cache: +/// if ->prompt_id and ->cmdbuff values happen +/// to be equal to those from colored_cmdline it +/// will just do nothing, assuming that ->colors +/// already contains needed data. +/// +/// Always colors the whole cmdline. +/// +/// @return true if draw_cmdline may proceed, false if it does not need anything +/// to do. +static bool color_cmdline(CmdlineInfo *colored_ccline) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + bool printed_errmsg = false; + +#define PRINT_ERRMSG(...) \ + do { \ + msg_putchar('\n'); \ + msg_printf_attr(hl_attr(HLF_E)|MSG_HIST, __VA_ARGS__); \ + printed_errmsg = true; \ + } while (0) + bool ret = true; + + ColoredCmdline *ccline_colors = &colored_ccline->last_colors; + + // Check whether result of the previous call is still valid. + if (ccline_colors->prompt_id == colored_ccline->prompt_id + && ccline_colors->cmdbuff != NULL + && STRCMP(ccline_colors->cmdbuff, colored_ccline->cmdbuff) == 0) { + return ret; + } + + kv_size(ccline_colors->colors) = 0; + + if (colored_ccline->cmdbuff == NULL || *colored_ccline->cmdbuff == NUL) { + // Nothing to do, exiting. + xfree(ccline_colors->cmdbuff); + ccline_colors->cmdbuff = NULL; + return ret; + } + + bool arg_allocated = false; + typval_T arg = { + .v_type = VAR_STRING, + .vval.v_string = colored_ccline->cmdbuff, + }; + typval_T tv = { .v_type = VAR_UNKNOWN }; + + static unsigned prev_prompt_id = UINT_MAX; + static int prev_prompt_errors = 0; + Callback color_cb = CALLBACK_NONE; + bool can_free_cb = false; + TryState tstate; + Error err = ERROR_INIT; + const char *err_errmsg = (const char *)e_intern2; + bool dgc_ret = true; + bool tl_ret = true; + + if (colored_ccline->prompt_id != prev_prompt_id) { + prev_prompt_errors = 0; + prev_prompt_id = colored_ccline->prompt_id; + } else if (prev_prompt_errors >= MAX_CB_ERRORS) { + goto color_cmdline_end; + } + if (colored_ccline->highlight_callback.type != kCallbackNone) { + // Currently this should only happen while processing input() prompts. + assert(colored_ccline->input_fn); + color_cb = colored_ccline->highlight_callback; + } else if (colored_ccline->cmdfirstc == ':') { + try_enter(&tstate); + err_errmsg = N_( + "E5408: Unable to get g:Nvim_color_cmdline callback: %s"); + dgc_ret = tv_dict_get_callback(&globvardict, S_LEN("Nvim_color_cmdline"), + &color_cb); + tl_ret = try_leave(&tstate, &err); + can_free_cb = true; + } else if (colored_ccline->cmdfirstc == '=') { + color_expr_cmdline(colored_ccline, ccline_colors); + } + if (!tl_ret || !dgc_ret) { + goto color_cmdline_error; + } + + if (color_cb.type == kCallbackNone) { + goto color_cmdline_end; + } + if (colored_ccline->cmdbuff[colored_ccline->cmdlen] != NUL) { + arg_allocated = true; + arg.vval.v_string = xmemdupz((const char *)colored_ccline->cmdbuff, + (size_t)colored_ccline->cmdlen); + } + // msg_start() called by e.g. :echo may shift command-line to the first column + // even though msg_silent is here. Two ways to workaround this problem without + // altering message.c: use full_screen or save and restore msg_col. + // + // Saving and restoring full_screen does not work well with :redraw!. Saving + // and restoring msg_col is neither ideal, but while with full_screen it + // appears shifted one character to the right and cursor position is no longer + // correct, with msg_col it just misses leading `:`. Since `redraw!` in + // callback lags this is least of the user problems. + // + // Also using try_enter() because error messages may overwrite typed + // command-line which is not expected. + getln_interrupted_highlight = false; + try_enter(&tstate); + err_errmsg = N_("E5407: Callback has thrown an exception: %s"); + const int saved_msg_col = msg_col; + msg_silent++; + const bool cbcall_ret = callback_call(&color_cb, 1, &arg, &tv); + msg_silent--; + msg_col = saved_msg_col; + if (got_int) { + getln_interrupted_highlight = true; + } + if (!try_leave(&tstate, &err) || !cbcall_ret) { + goto color_cmdline_error; + } + if (tv.v_type != VAR_LIST) { + PRINT_ERRMSG(_("E5400: Callback should return list")); + goto color_cmdline_error; + } + if (tv.vval.v_list == NULL) { + goto color_cmdline_end; + } + varnumber_T prev_end = 0; + int i = 0; + TV_LIST_ITER_CONST(tv.vval.v_list, li, { + if (TV_LIST_ITEM_TV(li)->v_type != VAR_LIST) { + PRINT_ERRMSG(_("E5401: List item %i is not a List"), i); + goto color_cmdline_error; + } + const list_T *const l = TV_LIST_ITEM_TV(li)->vval.v_list; + if (tv_list_len(l) != 3) { + PRINT_ERRMSG(_("E5402: List item %i has incorrect length: %li /= 3"), + i, tv_list_len(l)); + goto color_cmdline_error; + } + bool error = false; + const varnumber_T start = ( + tv_get_number_chk(TV_LIST_ITEM_TV(tv_list_first(l)), &error)); + if (error) { + goto color_cmdline_error; + } else if (!(prev_end <= start && start < colored_ccline->cmdlen)) { + PRINT_ERRMSG(_("E5403: Chunk %i start %" PRIdVARNUMBER " not in range " + "[%" PRIdVARNUMBER ", %i)"), + i, start, prev_end, colored_ccline->cmdlen); + goto color_cmdline_error; + } else if (utf8len_tab_zero[(uint8_t)colored_ccline->cmdbuff[start]] == 0) { + PRINT_ERRMSG(_("E5405: Chunk %i start %" PRIdVARNUMBER " splits " + "multibyte character"), i, start); + goto color_cmdline_error; + } + if (start != prev_end) { + kv_push(ccline_colors->colors, ((CmdlineColorChunk) { + .start = prev_end, + .end = start, + .attr = 0, + })); + } + const varnumber_T end = tv_get_number_chk( + TV_LIST_ITEM_TV(TV_LIST_ITEM_NEXT(l, tv_list_first(l))), &error); + if (error) { + goto color_cmdline_error; + } else if (!(start < end && end <= colored_ccline->cmdlen)) { + PRINT_ERRMSG(_("E5404: Chunk %i end %" PRIdVARNUMBER " not in range " + "(%" PRIdVARNUMBER ", %i]"), + i, end, start, colored_ccline->cmdlen); + goto color_cmdline_error; + } else if (end < colored_ccline->cmdlen + && (utf8len_tab_zero[(uint8_t)colored_ccline->cmdbuff[end]] + == 0)) { + PRINT_ERRMSG(_("E5406: Chunk %i end %" PRIdVARNUMBER " splits multibyte " + "character"), i, end); + goto color_cmdline_error; + } + prev_end = end; + const char *const group = tv_get_string_chk( + TV_LIST_ITEM_TV(tv_list_last(l))); + if (group == NULL) { + goto color_cmdline_error; + } + const int id = syn_name2id((char_u *)group); + const int attr = (id == 0 ? 0 : syn_id2attr(id)); + kv_push(ccline_colors->colors, ((CmdlineColorChunk) { + .start = start, + .end = end, + .attr = attr, + })); + i++; + }); + if (prev_end < colored_ccline->cmdlen) { + kv_push(ccline_colors->colors, ((CmdlineColorChunk) { + .start = prev_end, + .end = colored_ccline->cmdlen, + .attr = 0, + })); + } + prev_prompt_errors = 0; +color_cmdline_end: + assert(!ERROR_SET(&err)); + if (can_free_cb) { + callback_free(&color_cb); + } + xfree(ccline_colors->cmdbuff); + // Note: errors “output” is cached just as well as regular results. + ccline_colors->prompt_id = colored_ccline->prompt_id; + if (arg_allocated) { + ccline_colors->cmdbuff = (char *)arg.vval.v_string; + } else { + ccline_colors->cmdbuff = xmemdupz((const char *)colored_ccline->cmdbuff, + (size_t)colored_ccline->cmdlen); + } + tv_clear(&tv); + return ret; +color_cmdline_error: + if (ERROR_SET(&err)) { + PRINT_ERRMSG(_(err_errmsg), err.msg); + api_clear_error(&err); + } + assert(printed_errmsg); + (void)printed_errmsg; + + prev_prompt_errors++; + kv_size(ccline_colors->colors) = 0; + redrawcmdline(); + ret = false; + goto color_cmdline_end; +#undef PRINT_ERRMSG +} + /* * Draw part of the cmdline at the current cursor position. But draw stars * when cmdline_star is TRUE. */ static void draw_cmdline(int start, int len) { - int i; + if (!color_cmdline(&ccline)) { + return; + } - if (cmdline_star > 0) - for (i = 0; i < len; ++i) { + if (ui_is_external(kUICmdline)) { + ccline.special_char = NUL; + ccline.redraw_state = kCmdRedrawAll; + return; + } + + if (cmdline_star > 0) { + for (int i = 0; i < len; i++) { msg_putchar('*'); - if (has_mbyte) + if (has_mbyte) { i += (*mb_ptr2len)(ccline.cmdbuff + start + i) - 1; + } } - else if (p_arshape && !p_tbidi && enc_utf8 && len > 0) { - static int buflen = 0; - char_u *p; - int j; - int newlen = 0; + } else if (p_arshape && !p_tbidi && enc_utf8 && len > 0) { + bool do_arabicshape = false; int mb_l; - int pc, pc1 = 0; - int prev_c = 0; - int prev_c1 = 0; - int u8c; - int u8cc[MAX_MCO]; - int nc = 0; + for (int i = start; i < start + len; i += mb_l) { + char_u *p = ccline.cmdbuff + i; + int u8cc[MAX_MCO]; + int u8c = utfc_ptr2char_len(p, u8cc, start + len - i); + mb_l = utfc_ptr2len_len(p, start + len - i); + if (arabic_char(u8c)) { + do_arabicshape = true; + break; + } + } + if (!do_arabicshape) { + goto draw_cmdline_no_arabicshape; + } - /* - * Do arabic shaping into a temporary buffer. This is very - * inefficient! - */ + static int buflen = 0; + + // Do arabic shaping into a temporary buffer. This is very + // inefficient! if (len * 2 + 2 > buflen) { - /* Re-allocate the buffer. We keep it around to avoid a lot of - * alloc()/free() calls. */ + // Re-allocate the buffer. We keep it around to avoid a lot of + // alloc()/free() calls. xfree(arshape_buf); buflen = len * 2 + 2; arshape_buf = xmalloc(buflen); } + int newlen = 0; if (utf_iscomposing(utf_ptr2char(ccline.cmdbuff + start))) { - /* Prepend a space to draw the leading composing char on. */ + // Prepend a space to draw the leading composing char on. arshape_buf[0] = ' '; newlen = 1; } - for (j = start; j < start + len; j += mb_l) { - p = ccline.cmdbuff + j; - u8c = utfc_ptr2char_len(p, u8cc, start + len - j); - mb_l = utfc_ptr2len_len(p, start + len - j); + int prev_c = 0; + int prev_c1 = 0; + for (int i = start; i < start + len; i += mb_l) { + char_u *p = ccline.cmdbuff + i; + int u8cc[MAX_MCO]; + int u8c = utfc_ptr2char_len(p, u8cc, start + len - i); + mb_l = utfc_ptr2len_len(p, start + len - i); if (arabic_char(u8c)) { - /* Do Arabic shaping. */ + int pc; + int pc1 = 0; + int nc = 0; + // Do Arabic shaping. if (cmdmsg_rl) { - /* displaying from right to left */ + // Displaying from right to left. pc = prev_c; pc1 = prev_c1; prev_c1 = u8cc[0]; - if (j + mb_l >= start + len) + if (i + mb_l >= start + len) { nc = NUL; - else + } else { nc = utf_ptr2char(p + mb_l); + } } else { - /* displaying from left to right */ - if (j + mb_l >= start + len) + // Displaying from left to right. + if (i + mb_l >= start + len) { pc = NUL; - else { + } else { int pcc[MAX_MCO]; - pc = utfc_ptr2char_len(p + mb_l, pcc, - start + len - j - mb_l); + pc = utfc_ptr2char_len(p + mb_l, pcc, start + len - i - mb_l); pc1 = pcc[0]; } nc = prev_c; @@ -2374,8 +2904,147 @@ static void draw_cmdline(int start, int len) } msg_outtrans_len(arshape_buf, newlen); - } else - msg_outtrans_len(ccline.cmdbuff + start, len); + } else { +draw_cmdline_no_arabicshape: + if (kv_size(ccline.last_colors.colors)) { + for (size_t i = 0; i < kv_size(ccline.last_colors.colors); i++) { + CmdlineColorChunk chunk = kv_A(ccline.last_colors.colors, i); + if (chunk.end <= start) { + continue; + } + const int chunk_start = MAX(chunk.start, start); + msg_outtrans_len_attr(ccline.cmdbuff + chunk_start, + chunk.end - chunk_start, + chunk.attr); + } + } else { + msg_outtrans_len(ccline.cmdbuff + start, len); + } + } +} + +static void ui_ext_cmdline_show(CmdlineInfo *line) +{ + Array content = ARRAY_DICT_INIT; + if (cmdline_star) { + size_t len = 0; + for (char_u *p = ccline.cmdbuff; *p; mb_ptr_adv(p)) { + len++; + } + char *buf = xmallocz(len); + memset(buf, '*', len); + Array item = ARRAY_DICT_INIT; + ADD(item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT)); + ADD(item, STRING_OBJ(((String) { .data = buf, .size = len }))); + ADD(content, ARRAY_OBJ(item)); + } else if (kv_size(line->last_colors.colors)) { + for (size_t i = 0; i < kv_size(line->last_colors.colors); i++) { + CmdlineColorChunk chunk = kv_A(line->last_colors.colors, i); + Array item = ARRAY_DICT_INIT; + + if (chunk.attr) { + HlAttrs *aep = syn_cterm_attr2entry(chunk.attr); + // TODO(bfredl): this desicion could be delayed by making attr_code a + // recognized type + Dictionary rgb_attrs = hlattrs2dict(aep, true); + ADD(item, DICTIONARY_OBJ(rgb_attrs)); + } else { + ADD(item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT)); + } + ADD(item, STRING_OBJ(cbuf_to_string((char *)line->cmdbuff + chunk.start, + chunk.end-chunk.start))); + ADD(content, ARRAY_OBJ(item)); + } + } else { + Array item = ARRAY_DICT_INIT; + ADD(item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT)); + ADD(item, STRING_OBJ(cstr_to_string((char *)(line->cmdbuff)))); + ADD(content, ARRAY_OBJ(item)); + } + ui_call_cmdline_show(content, line->cmdpos, + cchar_to_string((char)line->cmdfirstc), + cstr_to_string((char *)(line->cmdprompt)), + line->cmdindent, + line->level); + if (line->special_char) { + ui_call_cmdline_special_char(cchar_to_string((char)(line->special_char)), + line->special_shift, + line->level); + } +} + +void ui_ext_cmdline_block_append(int indent, const char *line) +{ + char *buf = xmallocz(indent + strlen(line)); + memset(buf, ' ', indent); + memcpy(buf + indent, line, strlen(line)); // -V575 + + Array item = ARRAY_DICT_INIT; + ADD(item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT)); + ADD(item, STRING_OBJ(cstr_as_string(buf))); + Array content = ARRAY_DICT_INIT; + ADD(content, ARRAY_OBJ(item)); + ADD(cmdline_block, ARRAY_OBJ(content)); + if (cmdline_block.size > 1) { + ui_call_cmdline_block_append(copy_array(content)); + } else { + ui_call_cmdline_block_show(copy_array(cmdline_block)); + } +} + +void ui_ext_cmdline_block_leave(void) +{ + api_free_array(cmdline_block); + cmdline_block = (Array)ARRAY_DICT_INIT; + ui_call_cmdline_block_hide(); +} + +/// Extra redrawing needed for redraw! and on ui_attach +/// assumes "redrawcmdline()" will already be invoked +void cmdline_screen_cleared(void) +{ + if (!ui_is_external(kUICmdline)) { + return; + } + + if (cmdline_block.size) { + ui_call_cmdline_block_show(copy_array(cmdline_block)); + } + + int prev_level = ccline.level-1; + CmdlineInfo *prev_ccline = ccline.prev_ccline; + while (prev_level > 0 && prev_ccline) { + if (prev_ccline->level == prev_level) { + // don't redraw a cmdline already shown in the cmdline window + if (prev_level != cmdwin_level) { + prev_ccline->redraw_state = kCmdRedrawAll; + } + prev_level--; + } + prev_ccline = prev_ccline->prev_ccline; + } +} + +/// called by ui_flush, do what redraws neccessary to keep cmdline updated. +void cmdline_ui_flush(void) +{ + if (!ui_is_external(kUICmdline)) { + return; + } + int level = ccline.level; + CmdlineInfo *line = &ccline; + while (level > 0 && line) { + if (line->level == level) { + if (line->redraw_state == kCmdRedrawAll) { + ui_ext_cmdline_show(line); + } else if (line->redraw_state == kCmdRedrawPos) { + ui_call_cmdline_pos(line->cmdpos, line->level); + } + line->redraw_state = kCmdRedrawNone; + level--; + } + line = line->prev_ccline; + } } /* @@ -2385,33 +3054,41 @@ static void draw_cmdline(int start, int len) */ void putcmdline(int c, int shift) { - if (cmd_silent) + if (cmd_silent) { return; - msg_no_more = TRUE; - msg_putchar(c); - if (shift) - draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos); - msg_no_more = FALSE; + } + if (!ui_is_external(kUICmdline)) { + msg_no_more = true; + msg_putchar(c); + if (shift) { + draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos); + } + msg_no_more = false; + } else { + ccline.special_char = c; + ccline.special_shift = shift; + if (ccline.redraw_state != kCmdRedrawAll) { + ui_call_cmdline_special_char(cchar_to_string((char)(c)), shift, + ccline.level); + } + } cursorcmd(); ui_cursor_shape(); } -/* - * Undo a putcmdline(c, FALSE). - */ +/// Undo a putcmdline(c, FALSE). void unputcmdline(void) { - if (cmd_silent) + if (cmd_silent) { return; - msg_no_more = TRUE; - if (ccline.cmdlen == ccline.cmdpos) + } + msg_no_more = true; + if (ccline.cmdlen == ccline.cmdpos && !ui_is_external(kUICmdline)) { msg_putchar(' '); - else if (has_mbyte) - draw_cmdline(ccline.cmdpos, - (*mb_ptr2len)(ccline.cmdbuff + ccline.cmdpos)); - else - draw_cmdline(ccline.cmdpos, 1); - msg_no_more = FALSE; + } else { + draw_cmdline(ccline.cmdpos, mb_ptr2len(ccline.cmdbuff + ccline.cmdpos)); + } + msg_no_more = false; cursorcmd(); ui_cursor_shape(); } @@ -2545,9 +3222,6 @@ void put_on_cmdline(char_u *str, int len, int redraw) msg_check(); } -static struct cmdline_info prev_ccline; -static int prev_ccline_used = FALSE; - /* * Save ccline, because obtaining the "=" register may execute "normal :cmd" * and overwrite it. But get_cmdline_str() may need it, thus make it @@ -2555,15 +3229,12 @@ static int prev_ccline_used = FALSE; */ static void save_cmdline(struct cmdline_info *ccp) { - if (!prev_ccline_used) { - memset(&prev_ccline, 0, sizeof(struct cmdline_info)); - prev_ccline_used = TRUE; - } - *ccp = prev_ccline; - prev_ccline = ccline; + *ccp = ccline; + ccline.prev_ccline = ccp; ccline.cmdbuff = NULL; ccline.cmdprompt = NULL; ccline.xpc = NULL; + ccline.special_char = NUL; } /* @@ -2571,8 +3242,7 @@ static void save_cmdline(struct cmdline_info *ccp) */ static void restore_cmdline(struct cmdline_info *ccp) { - ccline = prev_ccline; - prev_ccline = *ccp; + ccline = *ccp; } /* @@ -2740,17 +3410,25 @@ static void redrawcmdprompt(void) if (cmd_silent) return; - if (ccline.cmdfirstc != NUL) + if (ui_is_external(kUICmdline)) { + ccline.redraw_state = kCmdRedrawAll; + return; + } + if (ccline.cmdfirstc != NUL) { msg_putchar(ccline.cmdfirstc); + } if (ccline.cmdprompt != NULL) { msg_puts_attr((const char *)ccline.cmdprompt, ccline.cmdattr); ccline.cmdindent = msg_col + (msg_row - cmdline_row) * Columns; - /* do the reverse of set_cmdspos() */ - if (ccline.cmdfirstc != NUL) - --ccline.cmdindent; - } else - for (i = ccline.cmdindent; i > 0; --i) + // do the reverse of set_cmdspos() + if (ccline.cmdfirstc != NUL) { + ccline.cmdindent--; + } + } else { + for (i = ccline.cmdindent; i > 0; i--) { msg_putchar(' '); + } + } } /* @@ -2761,6 +3439,11 @@ void redrawcmd(void) if (cmd_silent) return; + if (ui_is_external(kUICmdline)) { + draw_cmdline(0, ccline.cmdlen); + return; + } + /* when 'incsearch' is set there may be no command line while redrawing */ if (ccline.cmdbuff == NULL) { ui_cursor_goto(cmdline_row, 0); @@ -2804,6 +3487,13 @@ static void cursorcmd(void) if (cmd_silent) return; + if (ui_is_external(kUICmdline)) { + if (ccline.redraw_state < kCmdRedrawPos) { + ccline.redraw_state = kCmdRedrawPos; + } + return; + } + if (cmdmsg_rl) { msg_row = cmdline_row + (ccline.cmdspos / (int)(Columns - 1)); msg_col = (int)Columns - (ccline.cmdspos % (int)(Columns - 1)) - 1; @@ -2821,6 +3511,9 @@ static void cursorcmd(void) void gotocmdline(int clr) { + if (ui_is_external(kUICmdline)) { + return; + } msg_start(); if (cmdmsg_rl) msg_col = Columns - 1; @@ -2888,8 +3581,10 @@ nextwild ( return FAIL; } - MSG_PUTS("..."); /* show that we are busy */ - ui_flush(); + if (!ui_is_external(kUIWildmenu)) { + MSG_PUTS("..."); // show that we are busy + ui_flush(); + } i = (int)(xp->xp_pattern - ccline.cmdbuff); xp->xp_pattern_len = ccline.cmdpos - i; @@ -3042,11 +3737,17 @@ ExpandOne ( else findex = -1; } - if (p_wmnu) - win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files, - findex, cmd_showtail); - if (findex == -1) + if (p_wmnu) { + if (ui_is_external(kUIWildmenu)) { + ui_call_wildmenu_select(findex); + } else { + win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files, + findex, cmd_showtail); + } + } + if (findex == -1) { return vim_strsave(orig_save); + } return vim_strsave(xp->xp_files[findex]); } else return NULL; @@ -3403,6 +4104,15 @@ static int showmatches(expand_T *xp, int wildmenu) showtail = cmd_showtail; } + if (ui_is_external(kUIWildmenu)) { + Array args = ARRAY_DICT_INIT; + for (i = 0; i < num_files; i++) { + ADD(args, STRING_OBJ(cstr_to_string((char *)files_found[i]))); + } + ui_call_wildmenu_show(args); + return EXPAND_OK; + } + if (!wildmenu) { msg_didany = FALSE; /* lines_left will be set */ msg_start(); /* prepare for paging */ @@ -3413,12 +4123,12 @@ static int showmatches(expand_T *xp, int wildmenu) msg_start(); /* prepare for paging */ } - if (got_int) - got_int = FALSE; /* only int. the completion, not the cmd line */ - else if (wildmenu) - win_redr_status_matches(xp, num_files, files_found, 0, showtail); - else { - /* find the length of the longest file name */ + if (got_int) { + got_int = false; // only int. the completion, not the cmd line + } else if (wildmenu) { + win_redr_status_matches(xp, num_files, files_found, -1, showtail); + } else { + // find the length of the longest file name maxlen = 0; for (i = 0; i < num_files; ++i) { if (!showtail && (xp->xp_context == EXPAND_FILES @@ -3612,18 +4322,20 @@ addstar ( * use with vim_regcomp(). First work out how long it will be: */ - /* For help tags the translation is done in find_help_tags(). - * For a tag pattern starting with "/" no translation is needed. */ + // For help tags the translation is done in find_help_tags(). + // For a tag pattern starting with "/" no translation is needed. if (context == EXPAND_HELP + || context == EXPAND_CHECKHEALTH || context == EXPAND_COLORS || context == EXPAND_COMPILER || context == EXPAND_OWNSYNTAX || context == EXPAND_FILETYPE || context == EXPAND_PACKADD - || (context == EXPAND_TAGS && fname[0] == '/')) + || ((context == EXPAND_TAGS_LISTFILES || context == EXPAND_TAGS) + && fname[0] == '/')) { retval = vim_strnsave(fname, len); - else { - new_len = len + 2; /* +2 for '^' at start, NUL at end */ + } else { + new_len = len + 2; // +2 for '^' at start, NUL at end for (i = 0; i < len; i++) { if (fname[i] == '*' || fname[i] == '~') new_len++; /* '*' needs to be replaced by ".*" @@ -4020,6 +4732,10 @@ ExpandFromContext ( char *directories[] = { "syntax", "indent", "ftplugin", NULL }; return ExpandRTDir(pat, 0, num_file, file, directories); } + if (xp->xp_context == EXPAND_CHECKHEALTH) { + char *directories[] = { "autoload/health", NULL }; + return ExpandRTDir(pat, 0, num_file, file, directories); + } if (xp->xp_context == EXPAND_USER_LIST) { return ExpandUserList(xp, num_file, file); } @@ -4050,6 +4766,7 @@ ExpandFromContext ( } tab[] = { { EXPAND_COMMANDS, get_command_name, false, true }, { EXPAND_BEHAVE, get_behave_arg, true, true }, + { EXPAND_MESSAGES, get_messages_arg, true, true }, { EXPAND_HISTORY, get_history_arg, true, true }, { EXPAND_USER_COMMANDS, get_user_commands, false, true }, { EXPAND_USER_ADDR_TYPE, get_user_cmd_addr_type, false, true }, @@ -4210,13 +4927,14 @@ static void expand_shellcmd(char_u *filepat, int *num_file, char_u ***file, flags |= EW_FILE | EW_EXEC | EW_SHELLCMD; bool mustfree = false; // Track memory allocation for *path. - /* For an absolute name we don't use $PATH. */ - if (path_is_absolute_path(pat)) + // For an absolute name we don't use $PATH. + if (path_is_absolute(pat)) { path = (char_u *)" "; - else if ((pat[0] == '.' && (vim_ispathsep(pat[1]) - || (pat[1] == '.' && vim_ispathsep(pat[2]))))) + } else if (pat[0] == '.' && (vim_ispathsep(pat[1]) + || (pat[1] == '.' + && vim_ispathsep(pat[2])))) { path = (char_u *)"."; - else { + } else { path = (char_u *)vim_getenv("PATH"); if (path == NULL) { path = (char_u *)""; @@ -4381,24 +5099,24 @@ static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, */ static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file) { - list_T *retlist; - listitem_T *li; - garray_T ga; - - retlist = call_user_expand_func((user_expand_func_T)call_func_retlist, xp, - num_file, file); + list_T *const retlist = call_user_expand_func( + (user_expand_func_T)call_func_retlist, xp, num_file, file); if (retlist == NULL) { return FAIL; } + garray_T ga; ga_init(&ga, (int)sizeof(char *), 3); - /* Loop over the items in the list. */ - for (li = retlist->lv_first; li != NULL; li = li->li_next) { - if (li->li_tv.v_type != VAR_STRING || li->li_tv.vval.v_string == NULL) - continue; /* Skip non-string items and empty strings */ + // Loop over the items in the list. + TV_LIST_ITER_CONST(retlist, li, { + if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING + || TV_LIST_ITEM_TV(li)->vval.v_string == NULL) { + continue; // Skip non-string items and empty strings. + } - GA_APPEND(char_u *, &ga, vim_strsave(li->li_tv.vval.v_string)); - } + GA_APPEND(char *, &ga, xstrdup( + (const char *)TV_LIST_ITEM_TV(li)->vval.v_string)); + }); tv_list_unref(retlist); *file = ga.ga_data; @@ -4877,13 +5595,15 @@ int get_history_idx(int histype) */ static struct cmdline_info *get_ccline_ptr(void) { - if ((State & CMDLINE) == 0) + if ((State & CMDLINE) == 0) { return NULL; - if (ccline.cmdbuff != NULL) + } else if (ccline.cmdbuff != NULL) { return &ccline; - if (prev_ccline_used && prev_ccline.cmdbuff != NULL) - return &prev_ccline; - return NULL; + } else if (ccline.prev_ccline && ccline.prev_ccline->cmdbuff != NULL) { + return ccline.prev_ccline; + } else { + return NULL; + } } /* @@ -5319,6 +6039,7 @@ static int ex_window(void) return K_IGNORE; } cmdwin_type = get_cmdline_type(); + cmdwin_level = ccline.level; // Create empty command-line buffer. buf_open_scratch(0, "[Command Line]"); @@ -5371,6 +6092,10 @@ static int ex_window(void) curwin->w_cursor.col = ccline.cmdpos; changed_line_abv_curs(); invalidate_botline(); + if (ui_is_external(kUICmdline)) { + ccline.redraw_state = kCmdRedrawNone; + ui_call_cmdline_hide(ccline.level); + } redraw_later(SOME_VALID); // Save the command line info, can be used recursively. @@ -5391,6 +6116,7 @@ static int ex_window(void) i = RedrawingDisabled; RedrawingDisabled = 0; + int save_count = save_batch_count(); /* * Call the main loop until <CR> or CTRL-C is typed. @@ -5399,6 +6125,7 @@ static int ex_window(void) normal_enter(true, false); RedrawingDisabled = i; + restore_batch_count(save_count); int save_KeyTyped = KeyTyped; @@ -5411,6 +6138,7 @@ static int ex_window(void) // Restore the command line info. restore_cmdline(&save_ccline); cmdwin_type = 0; + cmdwin_level = 0; exmode_active = save_exmode; diff --git a/src/nvim/farsi.c b/src/nvim/farsi.c index 1053cb3ac2..5801a2d8fb 100644 --- a/src/nvim/farsi.c +++ b/src/nvim/farsi.c @@ -596,78 +596,83 @@ static void chg_r_to_Xor_X_(void) int fkmap(int c) { int tempc; - static int revins; + int insert_mode = (State & INSERT); + static int revins = 0; if (IS_SPECIAL(c)) { return c; } - if (ascii_isdigit(c) - || ((c == '.' - || c == '+' - || c == '-' - || c == '^' - || c == '%' - || c == '#' - || c == '=') - && revins)) { - if (!revins) { - if (curwin->w_cursor.col) { - if (!p_ri) { - dec_cursor(); - } + if (insert_mode) { + if (ascii_isdigit(c) + || ((c == '.' + || c == '+' + || c == '-' + || c == '^' + || c == '%' + || c == '#' + || c == '=') + && revins)) { + // Numbers are entered left-to-right. + if (!revins) { + if (curwin->w_cursor.col) { + if (!p_ri) { + dec_cursor(); + } - chg_c_toX_orX(); - chg_l_toXor_X(); - if (!p_ri) { - inc_cursor(); + chg_c_toX_orX(); + chg_l_toXor_X(); + if (!p_ri) { + inc_cursor(); + } } } - } - arrow_used = TRUE; - (void)stop_arrow(); + arrow_used = true; + (void)stop_arrow(); - if (!curwin->w_p_rl && revins) { - inc_cursor(); - } + if (!curwin->w_p_rl && revins) { + inc_cursor(); + } - revins++; - p_ri = 1; - } else { - if (revins) { - arrow_used = TRUE; - (void)stop_arrow(); + revins++; + p_ri = 1; + } else { + if (revins) { + // Stop entering number. + arrow_used = true; + (void)stop_arrow(); - revins = 0; - if (curwin->w_p_rl) { - while ((F_isdigit(gchar_cursor()) - || (gchar_cursor() == F_PERIOD - || gchar_cursor() == F_PLUS - || gchar_cursor() == F_MINUS - || gchar_cursor() == F_MUL - || gchar_cursor() == F_DIVIDE - || gchar_cursor() == F_PERCENT - || gchar_cursor() == F_EQUALS)) - && gchar_cursor() != NUL) { - curwin->w_cursor.col++; - } - } else { - if (curwin->w_cursor.col) { + revins = 0; + if (curwin->w_p_rl) { while ((F_isdigit(gchar_cursor()) - || (gchar_cursor() == F_PERIOD - || gchar_cursor() == F_PLUS - || gchar_cursor() == F_MINUS - || gchar_cursor() == F_MUL - || gchar_cursor() == F_DIVIDE - || gchar_cursor() == F_PERCENT - || gchar_cursor() == F_EQUALS)) - && --curwin->w_cursor.col) { + || (gchar_cursor() == F_PERIOD + || gchar_cursor() == F_PLUS + || gchar_cursor() == F_MINUS + || gchar_cursor() == F_MUL + || gchar_cursor() == F_DIVIDE + || gchar_cursor() == F_PERCENT + || gchar_cursor() == F_EQUALS)) + && gchar_cursor() != NUL) { + curwin->w_cursor.col++; + } + } else { + if (curwin->w_cursor.col) { + while ((F_isdigit(gchar_cursor()) + || (gchar_cursor() == F_PERIOD + || gchar_cursor() == F_PLUS + || gchar_cursor() == F_MINUS + || gchar_cursor() == F_MUL + || gchar_cursor() == F_DIVIDE + || gchar_cursor() == F_PERCENT + || gchar_cursor() == F_EQUALS)) + && --curwin->w_cursor.col) { + } } - } - if (!F_isdigit(gchar_cursor())) { - ++curwin->w_cursor.col; + if (!F_isdigit(gchar_cursor())) { + curwin->w_cursor.col++; + } } } } @@ -755,7 +760,7 @@ int fkmap(int c) case 'Y': case NL: case TAB: - if (p_ri && (c == NL) && curwin->w_cursor.col) { + if (p_ri && (c == NL) && curwin->w_cursor.col && insert_mode) { // If the char before the cursor is _X_ or X_ do not change // the one under the cursor with X type. @@ -826,135 +831,137 @@ int fkmap(int c) } } - if (!p_ri) { - dec_cursor(); - } + if (insert_mode) { + if (!p_ri) { + dec_cursor(); + } - switch ((tempc = gchar_cursor())) { - case _BE: - case _PE: - case _TE: - case _SE: - case _JIM: - case _CHE: - case _HE_J: - case _XE: - case _SIN: - case _SHIN: - case _SAD: - case _ZAD: - case _FE: - case _GHAF: - case _KAF: - case _KAF_H: - case _GAF: - case _LAM: - case _MIM: - case _NOON: - case _HE: - case _HE_: - case _TA: - case _ZA: - put_curr_and_l_to_X(toF_TyA((char_u)tempc)); - break; + switch ((tempc = gchar_cursor())) { + case _BE: + case _PE: + case _TE: + case _SE: + case _JIM: + case _CHE: + case _HE_J: + case _XE: + case _SIN: + case _SHIN: + case _SAD: + case _ZAD: + case _FE: + case _GHAF: + case _KAF: + case _KAF_H: + case _GAF: + case _LAM: + case _MIM: + case _NOON: + case _HE: + case _HE_: + case _TA: + case _ZA: + put_curr_and_l_to_X(toF_TyA((char_u)tempc)); + break; - case _AYN: - case _AYN_: - if (!p_ri) { - if (!curwin->w_cursor.col) { - put_curr_and_l_to_X(AYN); - break; + case _AYN: + case _AYN_: + if (!p_ri) { + if (!curwin->w_cursor.col) { + put_curr_and_l_to_X(AYN); + break; + } } - } - if (p_ri) { - inc_cursor(); - } else { - dec_cursor(); - } + if (p_ri) { + inc_cursor(); + } else { + dec_cursor(); + } - if (F_is_TyB_TyC_TyD(SRC_EDT, AT_CURSOR)) { - tempc = AYN_; - } else { - tempc = AYN; - } + if (F_is_TyB_TyC_TyD(SRC_EDT, AT_CURSOR)) { + tempc = AYN_; + } else { + tempc = AYN; + } - if (p_ri) { - dec_cursor(); - } else { - inc_cursor(); - } + if (p_ri) { + dec_cursor(); + } else { + inc_cursor(); + } - put_curr_and_l_to_X((char_u)tempc); - break; + put_curr_and_l_to_X((char_u)tempc); + break; - case _GHAYN: - case _GHAYN_: + case _GHAYN: + case _GHAYN_: - if (!p_ri) { - if (!curwin->w_cursor.col) { - put_curr_and_l_to_X(GHAYN); - break; + if (!p_ri) { + if (!curwin->w_cursor.col) { + put_curr_and_l_to_X(GHAYN); + break; + } } - } - if (p_ri) { - inc_cursor(); - } else { - dec_cursor(); - } + if (p_ri) { + inc_cursor(); + } else { + dec_cursor(); + } - if (F_is_TyB_TyC_TyD(SRC_EDT, AT_CURSOR)) { - tempc = GHAYN_; - } else { - tempc = GHAYN; - } + if (F_is_TyB_TyC_TyD(SRC_EDT, AT_CURSOR)) { + tempc = GHAYN_; + } else { + tempc = GHAYN; + } - if (p_ri) { - dec_cursor(); - } else { - inc_cursor(); - } + if (p_ri) { + dec_cursor(); + } else { + inc_cursor(); + } - put_curr_and_l_to_X((char_u)tempc); - break; + put_curr_and_l_to_X((char_u)tempc); + break; - case _YE: - case _IE: - case _YEE: + case _YE: + case _IE: + case _YEE: - if (!p_ri) { - if (!curwin->w_cursor.col) { - put_curr_and_l_to_X( - (tempc == _YE ? YE : tempc == _IE ? IE : YEE)); - break; + if (!p_ri) { + if (!curwin->w_cursor.col) { + put_curr_and_l_to_X( + (tempc == _YE ? YE : tempc == _IE ? IE : YEE)); + break; + } } - } - if (p_ri) { - inc_cursor(); - } else { - dec_cursor(); - } + if (p_ri) { + inc_cursor(); + } else { + dec_cursor(); + } - if (F_is_TyB_TyC_TyD(SRC_EDT, AT_CURSOR)) { - tempc = (tempc == _YE ? YE_ : tempc == _IE ? IE_ : YEE_); - } else { - tempc = (tempc == _YE ? YE : tempc == _IE ? IE : YEE); - } + if (F_is_TyB_TyC_TyD(SRC_EDT, AT_CURSOR)) { + tempc = (tempc == _YE ? YE_ : tempc == _IE ? IE_ : YEE_); + } else { + tempc = (tempc == _YE ? YE : tempc == _IE ? IE : YEE); + } - if (p_ri) { - dec_cursor(); - } else { - inc_cursor(); - } + if (p_ri) { + dec_cursor(); + } else { + inc_cursor(); + } - put_curr_and_l_to_X((char_u)tempc); - break; - } + put_curr_and_l_to_X((char_u)tempc); + break; + } - if (!p_ri) { - inc_cursor(); + if (!p_ri) { + inc_cursor(); + } } tempc = 0; diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index 8094a1b266..3272ce81bc 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -1560,7 +1560,7 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope) tv_dict_set_keys_readonly(dict); apply_autocmds(EVENT_DIRCHANGED, (char_u *)buf, (char_u *)new_dir, false, - NULL); + curbuf); tv_dict_clear(dict); @@ -1586,7 +1586,7 @@ int vim_chdirfile(char_u *fname) } #ifdef BACKSLASH_IN_FILENAME - slash_adjust(dir); + slash_adjust((char_u *)dir); #endif if (!strequal(dir, (char *)NameBuff)) { do_autocmd_dirchanged(dir, kCdScopeWindow); diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index be4188c4df..52686f6651 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -247,6 +247,7 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr) * stdin) * READ_DUMMY read into a dummy buffer (to check if file contents changed) * READ_KEEP_UNDO don't clear undo info or read it from a file + * READ_FIFO read from fifo/socket instead of a file * * return FAIL for failure, NOTDONE for directory (failure), or OK */ @@ -267,6 +268,7 @@ readfile ( int filtering = (flags & READ_FILTER); int read_stdin = (flags & READ_STDIN); int read_buffer = (flags & READ_BUFFER); + int read_fifo = (flags & READ_FIFO); int set_options = newfile || read_buffer || (eap != NULL && eap->read_edit); linenr_T read_buf_lnum = 1; /* next line to read from curbuf */ @@ -300,12 +302,9 @@ readfile ( linenr_T skip_count = 0; linenr_T read_count = 0; int msg_save = msg_scroll; - linenr_T read_no_eol_lnum = 0; /* non-zero lnum when last line of - * last read was missing the eol */ - int try_mac = (vim_strchr(p_ffs, 'm') != NULL); - int try_dos = (vim_strchr(p_ffs, 'd') != NULL); - int try_unix = (vim_strchr(p_ffs, 'x') != NULL); - int file_rewind = FALSE; + linenr_T read_no_eol_lnum = 0; // non-zero lnum when last line of + // last read was missing the eol + int file_rewind = false; int can_retry; linenr_T conv_error = 0; /* line nr with conversion error */ linenr_T illegal_byte = 0; /* line nr with illegal byte */ @@ -426,7 +425,7 @@ readfile ( } } - if (!read_buffer && !read_stdin) { + if (!read_buffer && !read_stdin && !read_fifo) { perm = os_getperm((const char *)fname); #ifdef UNIX // On Unix it is possible to read a directory, so we have to @@ -468,8 +467,8 @@ readfile ( if (check_readonly && !readonlymode) curbuf->b_p_ro = FALSE; - if (newfile && !read_stdin && !read_buffer) { - /* Remember time of file. */ + if (newfile && !read_stdin && !read_buffer && !read_fifo) { + // Remember time of file. FileInfo file_info; if (os_fileinfo((char *)fname, &file_info)) { buf_store_file_info(curbuf, &file_info); @@ -637,37 +636,46 @@ readfile ( curbuf->b_op_start.lnum = ((from == 0) ? 1 : from); curbuf->b_op_start.col = 0; + int try_mac = (vim_strchr(p_ffs, 'm') != NULL); + int try_dos = (vim_strchr(p_ffs, 'd') != NULL); + int try_unix = (vim_strchr(p_ffs, 'x') != NULL); + if (!read_buffer) { int m = msg_scroll; int n = msg_scrolled; - /* - * The file must be closed again, the autocommands may want to change - * the file before reading it. - */ - if (!read_stdin) - close(fd); /* ignore errors */ + // The file must be closed again, the autocommands may want to change + // the file before reading it. + if (!read_stdin) { + close(fd); // ignore errors + } - /* - * The output from the autocommands should not overwrite anything and - * should not be overwritten: Set msg_scroll, restore its value if no - * output was done. - */ - msg_scroll = TRUE; - if (filtering) + // The output from the autocommands should not overwrite anything and + // should not be overwritten: Set msg_scroll, restore its value if no + // output was done. + msg_scroll = true; + if (filtering) { apply_autocmds_exarg(EVENT_FILTERREADPRE, NULL, sfname, - FALSE, curbuf, eap); - else if (read_stdin) + false, curbuf, eap); + } else if (read_stdin) { apply_autocmds_exarg(EVENT_STDINREADPRE, NULL, sfname, - FALSE, curbuf, eap); - else if (newfile) + false, curbuf, eap); + } else if (newfile) { apply_autocmds_exarg(EVENT_BUFREADPRE, NULL, sfname, - FALSE, curbuf, eap); - else + false, curbuf, eap); + } else { apply_autocmds_exarg(EVENT_FILEREADPRE, sfname, sfname, - FALSE, NULL, eap); - if (msg_scrolled == n) + false, NULL, eap); + } + + // autocommands may have changed it + try_mac = (vim_strchr(p_ffs, 'm') != NULL); + try_dos = (vim_strchr(p_ffs, 'd') != NULL); + try_unix = (vim_strchr(p_ffs, 'x') != NULL); + + if (msg_scrolled == n) { msg_scroll = m; + } if (aborting()) { /* autocmds may abort script processing */ --no_wait_return; @@ -895,6 +903,7 @@ retry: * and we can't do it internally or with iconv(). */ if (fio_flags == 0 && !read_stdin && !read_buffer && *p_ccv != NUL + && !read_fifo # ifdef USE_ICONV && iconv_fd == (iconv_t)-1 # endif @@ -935,7 +944,7 @@ retry: /* Set "can_retry" when it's possible to rewind the file and try with * another "fenc" value. It's FALSE when no other "fenc" to try, reading * stdin or fixed at a specific encoding. */ - can_retry = (*fenc != NUL && !read_stdin && !keep_dest_enc); + can_retry = (*fenc != NUL && !read_stdin && !keep_dest_enc && !read_fifo); if (!skip_read) { linerest = 0; @@ -947,6 +956,7 @@ retry: && curbuf->b_ffname != NULL && curbuf->b_p_udf && !filtering + && !read_fifo && !read_stdin && !read_buffer); if (read_undo_file) @@ -1612,7 +1622,8 @@ rewind_retry: *ptr = NUL; /* end of line */ len = (colnr_T)(ptr - line_start + 1); if (fileformat == EOL_DOS) { - if (ptr[-1] == CAR) { /* remove CR */ + if (ptr > line_start && ptr[-1] == CAR) { + // remove CR before NL ptr[-1] = NUL; len--; } else if (ff_error != EOL_DOS) { @@ -1919,7 +1930,7 @@ failed: u_read_undo(NULL, hash, fname); } - if (!read_stdin && !read_buffer) { + if (!read_stdin && !read_fifo && (!read_buffer || sfname != NULL)) { int m = msg_scroll; int n = msg_scrolled; @@ -1937,7 +1948,7 @@ failed: if (filtering) { apply_autocmds_exarg(EVENT_FILTERREADPOST, NULL, sfname, false, curbuf, eap); - } else if (newfile) { + } else if (newfile || (read_buffer && sfname != NULL)) { apply_autocmds_exarg(EVENT_BUFREADPOST, NULL, sfname, false, curbuf, eap); if (!au_did_filetype && *curbuf->b_p_ft != NUL) { @@ -1970,7 +1981,7 @@ failed: /// Do not accept "/dev/fd/[012]", opening these may hang Vim. /// /// @param fname file name to check -static bool is_dev_fd_file(char_u *fname) +bool is_dev_fd_file(char_u *fname) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { return STRNCMP(fname, "/dev/fd/", 8) == 0 @@ -2566,11 +2577,9 @@ buf_write ( perm = -1; } } -#else /* win32 */ - /* - * Check for a writable device name. - */ - c = os_nodetype((char *)fname); +#else // win32 + // Check for a writable device name. + c = fname == NULL ? NODE_OTHER : os_nodetype((char *)fname); if (c == NODE_OTHER) { SET_ERRMSG_NUM("E503", _("is not a file or writable device")); goto fail; @@ -2590,9 +2599,8 @@ buf_write ( if (overwriting) { os_fileinfo((char *)fname, &file_info_old); } - } -#endif /* !UNIX */ +#endif // !UNIX if (!device && !newfile) { /* @@ -3158,8 +3166,8 @@ nobackup: #ifdef UNIX FileInfo file_info; - /* Don't delete the file when it's a hard or symbolic link. */ - if ((!newfile && os_fileinfo_hardlinks(&file_info) > 1) + // Don't delete the file when it's a hard or symbolic link. + if ((!newfile && os_fileinfo_hardlinks(&file_info_old) > 1) || (os_fileinfo_link((char *)fname, &file_info) && !os_fileinfo_id_equal(&file_info, &file_info_old))) { SET_ERRMSG(_("E166: Can't open linked file for writing")); @@ -4310,7 +4318,7 @@ void shorten_fnames(int force) && !path_with_url((char *)buf->b_fname) && (force || buf->b_sfname == NULL - || path_is_absolute_path(buf->b_sfname))) { + || path_is_absolute(buf->b_sfname))) { xfree(buf->b_sfname); buf->b_sfname = NULL; p = path_shorten_fname(buf->b_ffname, dirname); @@ -4435,22 +4443,32 @@ char *modname(const char *fname, const char *ext, bool prepend_dot) /// @return true for end-of-file. bool vim_fgets(char_u *buf, int size, FILE *fp) FUNC_ATTR_NONNULL_ALL { - char *eof; -#define FGETS_SIZE 200 - char tbuf[FGETS_SIZE]; + char *retval; + assert(size > 0); buf[size - 2] = NUL; - eof = fgets((char *)buf, size, fp); + + do { + errno = 0; + retval = fgets((char *)buf, size, fp); + } while (retval == NULL && errno == EINTR); + if (buf[size - 2] != NUL && buf[size - 2] != '\n') { - buf[size - 1] = NUL; /* Truncate the line */ + char tbuf[200]; + + buf[size - 1] = NUL; // Truncate the line. - /* Now throw away the rest of the line: */ + // Now throw away the rest of the line: do { - tbuf[FGETS_SIZE - 2] = NUL; - ignoredp = fgets((char *)tbuf, FGETS_SIZE, fp); - } while (tbuf[FGETS_SIZE - 2] != NUL && tbuf[FGETS_SIZE - 2] != '\n'); + tbuf[sizeof(tbuf) - 2] = NUL; + errno = 0; + retval = fgets((char *)tbuf, sizeof(tbuf), fp); + if (retval == NULL && errno != EINTR) { + break; + } + } while (tbuf[sizeof(tbuf) - 2] != NUL && tbuf[sizeof(tbuf) - 2] != '\n'); } - return eof == NULL; + return retval ? false : feof(fp); } /// Read 2 bytes from "fd" and turn them into an int, MSB first. @@ -4543,6 +4561,7 @@ int put_time(FILE *fd, time_t time_) /// /// @return -1 for failure, 0 for success int vim_rename(const char_u *from, const char_u *to) + FUNC_ATTR_NONNULL_ALL { int fd_in; int fd_out; @@ -4818,6 +4837,7 @@ buf_check_timestamp ( buf_T *buf, int focus /* called for GUI focus event */ ) + FUNC_ATTR_NONNULL_ALL { int retval = 0; char_u *path; @@ -5068,14 +5088,12 @@ void buf_reload(buf_T *buf, int orig_mode) flags |= READ_KEEP_UNDO; } - /* - * To behave like when a new file is edited (matters for - * BufReadPost autocommands) we first need to delete the current - * buffer contents. But if reading the file fails we should keep - * the old contents. Can't use memory only, the file might be - * too big. Use a hidden buffer to move the buffer contents to. - */ - if (bufempty() || saved == FAIL) { + // To behave like when a new file is edited (matters for + // BufReadPost autocommands) we first need to delete the current + // buffer contents. But if reading the file fails we should keep + // the old contents. Can't use memory only, the file might be + // too big. Use a hidden buffer to move the buffer contents to. + if (BUFEMPTY() || saved == FAIL) { savebuf = NULL; } else { // Allocate a buffer without putting it in the buffer list. @@ -5108,7 +5126,7 @@ void buf_reload(buf_T *buf, int orig_mode) if (savebuf != NULL && bufref_valid(&bufref) && buf == curbuf) { // Put the text back from the save buffer. First // delete any lines that readfile() added. - while (!bufempty()) { + while (!BUFEMPTY()) { if (ml_delete(buf->b_ml.ml_line_count, false) == FAIL) { break; } @@ -6256,13 +6274,13 @@ do_doautocmd ( fname = skipwhite(fname); - /* - * Loop over the events. - */ - while (*arg && !ascii_iswhite(*arg)) - if (apply_autocmds_group(event_name2nr(arg, &arg), - fname, NULL, TRUE, group, curbuf, NULL)) - nothing_done = FALSE; + // Loop over the events. + while (*arg && !ends_excmd(*arg) && !ascii_iswhite(*arg)) { + if (apply_autocmds_group(event_name2nr(arg, &arg), fname, NULL, true, + group, curbuf, NULL)) { + nothing_done = false; + } + } if (nothing_done && do_msg) { MSG(_("No matching autocommands")); @@ -6637,7 +6655,6 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, char_u *save_sourcing_name; linenr_T save_sourcing_lnum; char_u *save_autocmd_fname; - int save_autocmd_fname_full; int save_autocmd_bufnr; char_u *save_autocmd_match; int save_autocmd_busy; @@ -6653,12 +6670,12 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, proftime_T wait_time; bool did_save_redobuff = false; - /* - * Quickly return if there are no autocommands for this event or - * autocommands are blocked. - */ - if (first_autopat[(int)event] == NULL || autocmd_blocked > 0) + // Quickly return if there are no autocommands for this event or + // autocommands are blocked. + if (event == NUM_EVENTS || first_autopat[(int)event] == NULL + || autocmd_blocked > 0) { goto BYPASS_AU; + } /* * When autocommands are busy, new autocommands are only executed when @@ -6710,7 +6727,6 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, * Save the autocmd_* variables and info about the current buffer. */ save_autocmd_fname = autocmd_fname; - save_autocmd_fname_full = autocmd_fname_full; save_autocmd_bufnr = autocmd_bufnr; save_autocmd_match = autocmd_match; save_autocmd_busy = autocmd_busy; @@ -6724,19 +6740,22 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, * invalid. */ if (fname_io == NULL) { - if (event == EVENT_COLORSCHEME || event == EVENT_OPTIONSET) + if (event == EVENT_COLORSCHEME || event == EVENT_OPTIONSET) { autocmd_fname = NULL; - else if (fname != NULL && *fname != NUL) + } else if (fname != NULL && !ends_excmd(*fname)) { autocmd_fname = fname; - else if (buf != NULL) + } else if (buf != NULL) { autocmd_fname = buf->b_ffname; - else + } else { autocmd_fname = NULL; - } else + } + } else { autocmd_fname = fname_io; - if (autocmd_fname != NULL) - autocmd_fname = vim_strsave(autocmd_fname); - autocmd_fname_full = FALSE; /* call FullName_save() later */ + } + if (autocmd_fname != NULL) { + // Allocate MAXPATHL for when eval_vars() resolves the fullpath. + autocmd_fname = vim_strnsave(autocmd_fname, MAXPATHL); + } /* * Set the buffer number to be used for <abuf>. @@ -6903,7 +6922,6 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, sourcing_lnum = save_sourcing_lnum; xfree(autocmd_fname); autocmd_fname = save_autocmd_fname; - autocmd_fname_full = save_autocmd_fname_full; autocmd_bufnr = save_autocmd_bufnr; autocmd_match = save_autocmd_match; current_SID = save_current_SID; diff --git a/src/nvim/fileio.h b/src/nvim/fileio.h index 426dc0fcb3..8db4b89806 100644 --- a/src/nvim/fileio.h +++ b/src/nvim/fileio.h @@ -4,13 +4,14 @@ #include "nvim/buffer_defs.h" #include "nvim/os/os.h" -/* Values for readfile() flags */ -#define READ_NEW 0x01 /* read a file into a new buffer */ -#define READ_FILTER 0x02 /* read filter output */ -#define READ_STDIN 0x04 /* read from stdin */ -#define READ_BUFFER 0x08 /* read from curbuf (converting stdin) */ -#define READ_DUMMY 0x10 /* reading into a dummy buffer */ -#define READ_KEEP_UNDO 0x20 /* keep undo info*/ +// Values for readfile() flags +#define READ_NEW 0x01 // read a file into a new buffer +#define READ_FILTER 0x02 // read filter output +#define READ_STDIN 0x04 // read from stdin +#define READ_BUFFER 0x08 // read from curbuf (converting stdin) +#define READ_DUMMY 0x10 // reading into a dummy buffer +#define READ_KEEP_UNDO 0x20 // keep undo info +#define READ_FIFO 0x40 // read from fifo or socket #define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua index acdb25ca67..2666ca6e6f 100644 --- a/src/nvim/generators/gen_api_ui_events.lua +++ b/src/nvim/generators/gen_api_ui_events.lua @@ -37,7 +37,7 @@ function write_arglist(output, ev, need_copy) for j = 1, #ev.parameters do local param = ev.parameters[j] local kind = string.upper(param[1]) - local do_copy = need_copy and (kind == "ARRAY" or kind == "DICTIONARY" or kind == "STRING") + local do_copy = need_copy and (kind == "ARRAY" or kind == "DICTIONARY" or kind == "STRING" or kind == "OBJECT") output:write(' ADD(args, ') if do_copy then output:write('copy_object(') @@ -91,7 +91,7 @@ for i = 1, #events do recv_cleanup = recv_cleanup..' api_free_string('..param[2]..');\n' argc = argc+2 elseif param[1] == 'Array' then - send = send..' Array copy_'..param[2]..' = copy_array('..param[2]..');\n' + send = send..' Array '..copy..' = copy_array('..param[2]..');\n' argv = argv..', '..copy..'.items, INT2PTR('..copy..'.size)' recv = (recv..' Array '..param[2].. ' = (Array){.items = argv['..argc..'],'.. @@ -99,6 +99,15 @@ for i = 1, #events do recv_argv = recv_argv..', '..param[2] recv_cleanup = recv_cleanup..' api_free_array('..param[2]..');\n' argc = argc+2 + elseif param[1] == 'Object' then + send = send..' Object *'..copy..' = xmalloc(sizeof(Object));\n' + send = send..' *'..copy..' = copy_object('..param[2]..');\n' + argv = argv..', '..copy + recv = recv..' Object '..param[2]..' = *(Object *)argv['..argc..'];\n' + recv_argv = recv_argv..', '..param[2] + recv_cleanup = (recv_cleanup..' api_free_object('..param[2]..');\n'.. + ' xfree(argv['..argc..']);\n') + argc = argc+1 elseif param[1] == 'Integer' or param[1] == 'Boolean' then argv = argv..', INT2PTR('..param[2]..')' recv_argv = recv_argv..', PTR2INT(argv['..argc..'])' @@ -119,7 +128,7 @@ for i = 1, #events do write_signature(bridge_output, ev, 'UI *ui') bridge_output:write('\n{\n') bridge_output:write(send) - bridge_output:write(' UI_CALL(ui, '..ev.name..', '..argc..', ui'..argv..');\n}\n') + bridge_output:write(' UI_BRIDGE_CALL(ui, '..ev.name..', '..argc..', ui'..argv..');\n}\n\n') end end @@ -128,6 +137,7 @@ for i = 1, #events do call_output:write('\n{\n') if ev.remote_only then write_arglist(call_output, ev, false) + call_output:write(' UI_LOG('..ev.name..', 0);\n') call_output:write(' ui_event("'..ev.name..'", args);\n') else call_output:write(' UI_CALL') diff --git a/src/nvim/generators/gen_declarations.lua b/src/nvim/generators/gen_declarations.lua index e999e53e4a..c40c37bb3e 100755 --- a/src/nvim/generators/gen_declarations.lua +++ b/src/nvim/generators/gen_declarations.lua @@ -164,9 +164,40 @@ local pattern = concat( ) if fname == '--help' then - print'Usage:' - print() - print' gendeclarations.lua definitions.c static.h non-static.h preprocessor.i' + print([[ +Usage: + + gendeclarations.lua definitions.c static.h non-static.h definitions.i + +Generates declarations for a C file definitions.c, putting declarations for +static functions into static.h and declarations for non-static functions into +non-static.h. File `definitions.i' should contain an already preprocessed +version of definitions.c and it is the only one which is actually parsed, +definitions.c is needed only to determine functions from which file out of all +functions found in definitions.i are needed. + +Additionally uses the following environment variables: + + NVIM_GEN_DECLARATIONS_LINE_NUMBERS: + If set to 1 then all generated declarations receive a comment with file + name and line number after the declaration. This may be useful for + debugging gen_declarations script, but not much beyond that with + configured development environment (i.e. with ctags/cscope/finding + definitions with clang/etc). + + WARNING: setting this to 1 will cause extensive rebuilds: declarations + generator script will not regenerate non-static.h file if its + contents did not change, but including line numbers will make + contents actually change. + + With contents changed timestamp of the file is regenerated even + when no real changes were made (e.g. a few lines were added to + a function which is not at the bottom of the file). + + With changed timestamp build system will assume that header + changed, triggering rebuilds of all C files which depend on the + "changed" header. +]]) os.exit() end @@ -193,23 +224,15 @@ local static = header local filepattern = '^#%a* (%d+) "([^"]-)/?([^"/]+)"' local curfile -local init = 0 +local init = 1 local curfile = nil local neededfile = fname:match('[^/]+$') local declline = 0 local declendpos = 0 local curdir = nil local is_needed_file = false +local init_is_nl = true while init ~= nil do - init = text:find('[\n;}]', init) - if init == nil then - break - end - local init_is_nl = text:sub(init, init) == '\n' - init = init + 1 - if init_is_nl and is_needed_file then - declline = declline + 1 - end if init_is_nl and text:sub(init, init) == '#' then local line, dir, file = text:match(filepattern, init) if file ~= nil then @@ -249,8 +272,10 @@ while init ~= nil do declaration = declaration:gsub(' $', '') declaration = declaration:gsub('^ ', '') declaration = declaration .. ';' - declaration = declaration .. (' // %s/%s:%u'):format( - curdir, curfile, declline) + if os.getenv('NVIM_GEN_DECLARATIONS_LINE_NUMBERS') == '1' then + declaration = declaration .. (' // %s/%s:%u'):format( + curdir, curfile, declline) + end declaration = declaration .. '\n' if declaration:sub(1, 6) == 'static' then static = static .. declaration @@ -260,6 +285,15 @@ while init ~= nil do declendpos = e end end + init = text:find('[\n;}]', init) + if init == nil then + break + end + init_is_nl = text:sub(init, init) == '\n' + init = init + 1 + if init_is_nl and is_needed_file then + declline = declline + 1 + end end non_static = non_static .. footer diff --git a/src/nvim/generators/gen_options.lua b/src/nvim/generators/gen_options.lua index ca0134043c..fdc00d5dc0 100644 --- a/src/nvim/generators/gen_options.lua +++ b/src/nvim/generators/gen_options.lua @@ -36,6 +36,7 @@ local redraw_flags={ all_windows='P_RALL', everything='P_RCLR', curswant='P_CURSWANT', + ui_option='P_UI_OPTION', } local list_flags={ @@ -74,6 +75,7 @@ local get_flags = function(o) {'gettext'}, {'noglob'}, {'normal_fname_chars', 'P_NFNAME'}, + {'normal_dname_chars', 'P_NDNAME'}, {'pri_mkrc'}, {'deny_in_modelines', 'P_NO_ML'}, {'deny_duplicates', 'P_NODUP'}, diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 4e42042959..5d4e61d56a 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -92,17 +92,15 @@ static int typeahead_char = 0; /* typeahead char that's not flushed */ */ static int block_redo = FALSE; -/* - * Make a hash value for a mapping. - * "mode" is the lower 4 bits of the State for the mapping. - * "c1" is the first character of the "lhs". - * Returns a value between 0 and 255, index in maphash. - * Put Normal/Visual mode mappings mostly separately from Insert/Cmdline mode. - */ +// Make a hash value for a mapping. +// "mode" is the lower 4 bits of the State for the mapping. +// "c1" is the first character of the "lhs". +// Returns a value between 0 and 255, index in maphash. +// Put Normal/Visual mode mappings mostly separately from Insert/Cmdline mode. #define MAP_HASH(mode, \ c1) (((mode) & \ (NORMAL + VISUAL + SELECTMODE + \ - OP_PENDING)) ? (c1) : ((c1) ^ 0x80)) + OP_PENDING + TERM_FOCUS)) ? (c1) : ((c1) ^ 0x80)) // Each mapping is put in one of the MAX_MAPHASH hash lists, // to speed up finding it. @@ -256,16 +254,17 @@ static void add_buff(buffheader_T *const buf, const char *const s, return; } - if (buf->bh_first.b_next == NULL) { /* first add to list */ + if (buf->bh_first.b_next == NULL) { // first add to list buf->bh_space = 0; buf->bh_curr = &(buf->bh_first); - } else if (buf->bh_curr == NULL) { /* buffer has already been read */ - EMSG(_("E222: Add to read buffer")); + } else if (buf->bh_curr == NULL) { // buffer has already been read + IEMSG(_("E222: Add to read buffer")); return; - } else if (buf->bh_index != 0) + } else if (buf->bh_index != 0) { memmove(buf->bh_first.b_next->b_str, - buf->bh_first.b_next->b_str + buf->bh_index, - STRLEN(buf->bh_first.b_next->b_str + buf->bh_index) + 1); + buf->bh_first.b_next->b_str + buf->bh_index, + STRLEN(buf->bh_first.b_next->b_str + buf->bh_index) + 1); + } buf->bh_index = 0; size_t len; @@ -870,20 +869,15 @@ int ins_typebuf(char_u *str, int noremap, int offset, int nottyped, bool silent) addlen = (int)STRLEN(str); - /* - * Easy case: there is room in front of typebuf.tb_buf[typebuf.tb_off] - */ if (offset == 0 && addlen <= typebuf.tb_off) { + // Easy case: there is room in front of typebuf.tb_buf[typebuf.tb_off] typebuf.tb_off -= addlen; memmove(typebuf.tb_buf + typebuf.tb_off, str, (size_t)addlen); - } - /* - * Need to allocate a new buffer. - * In typebuf.tb_buf there must always be room for 3 * MAXMAPLEN + 4 - * characters. We add some extra room to avoid having to allocate too - * often. - */ - else { + } else { + // Need to allocate a new buffer. + // In typebuf.tb_buf there must always be room for 3 * MAXMAPLEN + 4 + // characters. We add some extra room to avoid having to allocate too + // often. newoff = MAXMAPLEN + 4; newlen = typebuf.tb_len + addlen + newoff + 4 * (MAXMAPLEN + 4); if (newlen < 0) { /* string is getting too long */ @@ -1159,14 +1153,16 @@ void alloc_typebuf(void) */ void free_typebuf(void) { - if (typebuf.tb_buf == typebuf_init) - EMSG2(_(e_intern2), "Free typebuf 1"); - else + if (typebuf.tb_buf == typebuf_init) { + internal_error("Free typebuf 1"); + } else { xfree(typebuf.tb_buf); - if (typebuf.tb_noremap == noremapbuf_init) - EMSG2(_(e_intern2), "Free typebuf 2"); - else + } + if (typebuf.tb_noremap == noremapbuf_init) { + internal_error("Free typebuf 2"); + } else { xfree(typebuf.tb_noremap); + } } /* @@ -1665,10 +1661,10 @@ static int vgetorpeek(int advance) } if (c != NUL && !got_int) { if (advance) { - /* KeyTyped = FALSE; When the command that stuffed something - * was typed, behave like the stuffed command was typed. - * needed for CTRL-W CTRl-] to open a fold, for example. */ - KeyStuffed = TRUE; + // KeyTyped = FALSE; When the command that stuffed something + // was typed, behave like the stuffed command was typed. + // needed for CTRL-W CTRL-] to open a fold, for example. + KeyStuffed = true; } if (typebuf.tb_no_abbr_cnt == 0) typebuf.tb_no_abbr_cnt = 1; /* no abbreviations now */ @@ -1806,7 +1802,7 @@ static int vgetorpeek(int advance) * <M-a> and then changing 'encoding'. Beware * that 0x80 is escaped. */ char_u *p1 = mp->m_keys; - char_u *p2 = mb_unescape(&p1); + char_u *p2 = (char_u *)mb_unescape((const char **)&p1); if (has_mbyte && p2 != NULL && MB_BYTE2LEN(c1) > MB_PTR2LEN(p2)) mlen = 0; @@ -2537,7 +2533,6 @@ do_map ( bool unique = false; bool nowait = false; bool silent = false; - bool special = false; bool expr = false; int noremap; char_u *orig_rhs; @@ -2583,12 +2578,9 @@ do_map ( continue; } - /* - * Check for "<special>": accept special keys in <> - */ + // Ignore obsolete "<special>" modifier. if (STRNCMP(keys, "<special>", 9) == 0) { keys = skipwhite(keys + 9); - special = true; continue; } @@ -2657,7 +2649,7 @@ do_map ( // needs to be freed later (*keys_buf and *arg_buf). // replace_termcodes() also removes CTRL-Vs and sometimes backslashes. if (haskey) { - keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, special, + keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, true, CPO_TO_CPO_FLAGS); } orig_rhs = rhs; @@ -2665,7 +2657,7 @@ do_map ( if (STRICMP(rhs, "<nop>") == 0) { // "<Nop>" means nothing rhs = (char_u *)""; } else { - rhs = replace_termcodes(rhs, STRLEN(rhs), &arg_buf, false, true, special, + rhs = replace_termcodes(rhs, STRLEN(rhs), &arg_buf, false, true, true, CPO_TO_CPO_FLAGS); } } @@ -3245,7 +3237,7 @@ bool map_to_exists(const char *const str, const char *const modechars, char_u *buf; char_u *const rhs = replace_termcodes((const char_u *)str, strlen(str), &buf, - false, true, false, + false, true, true, CPO_TO_CPO_FLAGS); #define MAPMODE(mode, modechars, chr, modeflags) \ @@ -3374,6 +3366,10 @@ set_context_in_map_cmd ( arg = skipwhite(arg + 8); continue; } + if (STRNCMP(arg, "<special>", 9) == 0) { + arg = skipwhite(arg + 9); + continue; + } if (STRNCMP(arg, "<script>", 8) == 0) { arg = skipwhite(arg + 8); continue; @@ -3416,21 +3412,24 @@ int ExpandMappings(regmatch_T *regmatch, int *num_file, char_u ***file) for (round = 1; round <= 2; ++round) { count = 0; - for (i = 0; i < 6; ++i) { - if (i == 0) + for (i = 0; i < 7; i++) { + if (i == 0) { p = (char_u *)"<silent>"; - else if (i == 1) + } else if (i == 1) { p = (char_u *)"<unique>"; - else if (i == 2) + } else if (i == 2) { p = (char_u *)"<script>"; - else if (i == 3) + } else if (i == 3) { p = (char_u *)"<expr>"; - else if (i == 4 && !expand_buffer) + } else if (i == 4 && !expand_buffer) { p = (char_u *)"<buffer>"; - else if (i == 5) + } else if (i == 5) { p = (char_u *)"<nowait>"; - else + } else if (i == 6) { + p = (char_u *)"<special>"; + } else { continue; + } if (vim_regexec(regmatch, p, (colnr_T)0)) { if (round == 1) @@ -3916,7 +3915,7 @@ makemap ( c1 = 't'; break; default: - EMSG(_("E228: makemap: Illegal mode")); + IEMSG(_("E228: makemap: Illegal mode")); return FAIL; } do { /* do this twice if c2 is set, 3 times with c3 */ @@ -3999,12 +3998,10 @@ int put_escstr(FILE *fd, char_u *strstart, int what) return OK; } - for (; *str != NUL; ++str) { - char_u *p; - - /* Check for a multi-byte character, which may contain escaped - * K_SPECIAL and CSI bytes */ - p = mb_unescape(&str); + for (; *str != NUL; str++) { + // Check for a multi-byte character, which may contain escaped + // K_SPECIAL and CSI bytes. + const char *p = mb_unescape((const char **)&str); if (p != NULL) { while (*p != NUL) if (fputc(*p++, fd) < 0) @@ -4160,8 +4157,7 @@ void add_map(char_u *map, int mode) } // Translate an internal mapping/abbreviation representation into the -// corresponding external one recognized by :map/:abbrev commands; -// respects the current B/k/< settings of 'cpoption'. +// corresponding external one recognized by :map/:abbrev commands. // // This function is called when expanding mappings/abbreviations on the // command-line, and for building the "Ambiguous mapping..." error message. @@ -4181,7 +4177,6 @@ static char_u * translate_mapping ( ga_init(&ga, 1, 40); bool cpo_bslash = !(cpo_flags&FLAG_CPO_BSLASH); - bool cpo_special = !(cpo_flags&FLAG_CPO_SPECI); for (; *str; ++str) { int c = *str; @@ -4194,7 +4189,7 @@ static char_u * translate_mapping ( } if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL) { - if (expmap && cpo_special) { + if (expmap) { ga_clear(&ga); return NULL; } @@ -4205,8 +4200,8 @@ static char_u * translate_mapping ( } str += 2; } - if (IS_SPECIAL(c) || modifiers) { /* special key */ - if (expmap && cpo_special) { + if (IS_SPECIAL(c) || modifiers) { // special key + if (expmap) { ga_clear(&ga); return NULL; } @@ -4216,7 +4211,7 @@ static char_u * translate_mapping ( } if (c == ' ' || c == '\t' || c == Ctrl_J || c == Ctrl_V - || (c == '<' && !cpo_special) || (c == '\\' && !cpo_bslash)) { + || (c == '\\' && !cpo_bslash)) { ga_append(&ga, cpo_bslash ? Ctrl_V : '\\'); } @@ -4252,3 +4247,70 @@ mapblock_T *get_maphash(int index, buf_T *buf) return (buf == NULL) ? maphash[index] : buf->b_maphash[index]; } + +/// Get command argument for <Cmd> key +char_u * getcmdkeycmd(int promptc, void *cookie, int indent) +{ + garray_T line_ga; + int c1 = -1, c2; + int cmod = 0; + bool aborted = false; + + ga_init(&line_ga, 1, 32); + + no_mapping++; + + got_int = false; + while (c1 != NUL && !aborted) { + ga_grow(&line_ga, 32); + + if (vgetorpeek(false) == NUL) { + // incomplete <Cmd> is an error, because there is not much the user + // could do in this state. + EMSG(e_cmdmap_err); + aborted = true; + break; + } + + // Get one character at a time. + c1 = vgetorpeek(true); + // Get two extra bytes for special keys + if (c1 == K_SPECIAL) { + c1 = vgetorpeek(true); // no mapping for these chars + c2 = vgetorpeek(true); + if (c1 == KS_MODIFIER) { + cmod = c2; + continue; + } + c1 = TO_SPECIAL(c1, c2); + } + + + if (got_int) { + aborted = true; + } else if (c1 == '\r' || c1 == '\n') { + c1 = NUL; // end the line + } else if (c1 == ESC) { + aborted = true; + } else if (c1 == K_COMMAND) { + // special case to give nicer error message + EMSG(e_cmdmap_repeated); + aborted = true; + } else if (IS_SPECIAL(c1)) { + EMSG2(e_cmdmap_key, get_special_key_name(c1, cmod)); + aborted = true; + } else { + ga_append(&line_ga, (char)c1); + } + + cmod = 0; + } + + no_mapping--; + + if (aborted) { + ga_clear(&line_ga); + } + + return (char_u *)line_ga.ga_data; +} diff --git a/src/nvim/getchar.h b/src/nvim/getchar.h index 28584e0534..e634273e0d 100644 --- a/src/nvim/getchar.h +++ b/src/nvim/getchar.h @@ -5,12 +5,15 @@ #include "nvim/buffer_defs.h" #include "nvim/ex_cmds_defs.h" -/* Values for "noremap" argument of ins_typebuf(). Also used for - * map->m_noremap and menu->noremap[]. */ -#define REMAP_YES 0 /* allow remapping */ -#define REMAP_NONE -1 /* no remapping */ -#define REMAP_SCRIPT -2 /* remap script-local mappings only */ -#define REMAP_SKIP -3 /* no remapping for first char */ +/// Values for "noremap" argument of ins_typebuf(). Also used for +/// map->m_noremap and menu->noremap[]. +/// @addtogroup REMAP_VALUES +/// @{ +#define REMAP_YES 0 ///< allow remapping +#define REMAP_NONE -1 ///< no remapping +#define REMAP_SCRIPT -2 ///< remap script-local mappings only +#define REMAP_SKIP -3 ///< no remapping for first char +/// @} #define KEYLEN_PART_KEY -1 /* keylen value for incomplete key-code */ #define KEYLEN_PART_MAP -2 /* keylen value for incomplete mapping */ diff --git a/src/nvim/gettext.h b/src/nvim/gettext.h index aa0e97233e..60317b8484 100644 --- a/src/nvim/gettext.h +++ b/src/nvim/gettext.h @@ -13,6 +13,7 @@ #else # define _(x) ((char *)(x)) # define N_(x) x +# define ngettext(x, xs, n) ((n) == 1 ? (x) : (xs)) # define bindtextdomain(x, y) // empty # define bind_textdomain_codeset(x, y) // empty # define textdomain(x) // empty diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 6d1bd1de12..31bde4aa1e 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -317,17 +317,11 @@ EXTERN int do_profiling INIT(= PROF_NONE); /* PROF_ values */ /* * The exception currently being thrown. Used to pass an exception to * a different cstack. Also used for discarding an exception before it is - * caught or made pending. Only valid when did_throw is TRUE. + * caught or made pending. */ EXTERN except_T *current_exception; /* - * did_throw: An exception is being thrown. Reset when the exception is caught - * or as long as it is pending in a finally clause. - */ -EXTERN int did_throw INIT(= FALSE); - -/* * need_rethrow: set to TRUE when a throw that cannot be handled in do_cmdline() * must be propagated to the cstack of the previously called do_cmdline(). */ @@ -407,13 +401,16 @@ EXTERN int garbage_collect_at_exit INIT(= FALSE); /* ID of script being sourced or was sourced to define the current function. */ EXTERN scid_T current_SID INIT(= 0); + +EXTERN bool did_source_packages INIT(= false); + // Scope information for the code that indirectly triggered the current // provider function call EXTERN struct caller_scope { scid_T SID; uint8_t *sourcing_name, *autocmd_fname, *autocmd_match; linenr_T sourcing_lnum; - int autocmd_fname_full, autocmd_bufnr; + int autocmd_bufnr; void *funccalp; } provider_caller_scope; EXTERN int provider_call_nesting INIT(= 0); @@ -494,6 +491,7 @@ EXTERN int updating_screen INIT(= FALSE); EXTERN win_T *firstwin; /* first window */ EXTERN win_T *lastwin; /* last window */ EXTERN win_T *prevwin INIT(= NULL); /* previous window */ +# define ONE_WINDOW (firstwin == lastwin) /* * When using this macro "break" only breaks out of the inner loop. Use "goto" * to break out of the tabpage loop. @@ -560,21 +558,22 @@ EXTERN int ru_col; /* column for ruler */ EXTERN int ru_wid; /* 'rulerfmt' width of ruler when non-zero */ EXTERN int sc_col; /* column for shown command */ -/* - * When starting or exiting some things are done differently (e.g. screen - * updating). - */ +// +// When starting or exiting some things are done differently (e.g. screen +// updating). +// + +// First NO_SCREEN, then NO_BUFFERS, then 0 when startup finished. EXTERN int starting INIT(= NO_SCREEN); -/* first NO_SCREEN, then NO_BUFFERS and then - * set to 0 when starting up finished */ -EXTERN int exiting INIT(= FALSE); -/* TRUE when planning to exit Vim. Might - * still keep on running if there is a changed - * buffer. */ -// volatile because it is used in signal handler deathtrap(). +// true when planning to exit. Might keep running if there is a changed buffer. +EXTERN int exiting INIT(= false); +// is stdin a terminal? +EXTERN int stdin_isatty INIT(= true); +// is stdout a terminal? +EXTERN int stdout_isatty INIT(= true); +// true when doing full-screen output, otherwise only writing some messages. +// volatile because it is used in a signal handler. EXTERN volatile int full_screen INIT(= false); -// TRUE when doing full-screen output -// otherwise only writing some messages EXTERN int restricted INIT(= FALSE); // TRUE when started in restricted mode (-Z) @@ -724,29 +723,6 @@ EXTERN int vr_lines_changed INIT(= 0); /* #Lines changed by "gR" so far */ /// Encoding used when 'fencs' is set to "default" EXTERN char_u *fenc_default INIT(= NULL); -// To speed up BYTELEN(); keep a lookup table to quickly get the length in -// bytes of a UTF-8 character from the first byte of a UTF-8 string. Bytes -// which are illegal when used as the first byte have a 1. The NUL byte has -// length 1. -EXTERN char utf8len_tab[256] INIT(= { - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1, -}); - # if defined(USE_ICONV) && defined(DYNAMIC_ICONV) /* Pointers to functions and variables to be loaded at runtime */ EXTERN size_t (*iconv)(iconv_t cd, const char **inbuf, size_t *inbytesleft, @@ -890,7 +866,6 @@ EXTERN char_u *last_cmdline INIT(= NULL); // last command line (for ":) EXTERN char_u *repeat_cmdline INIT(= NULL); // command line for "." EXTERN char_u *new_last_cmdline INIT(= NULL); // new value for last_cmdline EXTERN char_u *autocmd_fname INIT(= NULL); // fname for <afile> on cmdline -EXTERN int autocmd_fname_full; // autocmd_fname is full path EXTERN int autocmd_bufnr INIT(= 0); // fnum for <abuf> on cmdline EXTERN char_u *autocmd_match INIT(= NULL); // name for <amatch> on cmdline EXTERN int did_cursorhold INIT(= false); // set when CursorHold t'gerd @@ -930,8 +905,11 @@ EXTERN char_u langmap_mapchar[256]; /* mapping for language keys */ EXTERN int save_p_ls INIT(= -1); /* Save 'laststatus' setting */ EXTERN int save_p_wmh INIT(= -1); /* Save 'winminheight' setting */ EXTERN int wild_menu_showing INIT(= 0); -# define WM_SHOWN 1 /* wildmenu showing */ -# define WM_SCROLLED 2 /* wildmenu showing with scroll */ +enum { + WM_SHOWN = 1, ///< wildmenu showing + WM_SCROLLED = 2, ///< wildmenu showing with scroll + WM_LIST = 3, ///< cmdline CTRL-D +}; EXTERN char breakat_flags[256]; /* which characters are in 'breakat' */ @@ -952,7 +930,7 @@ extern char_u *compiled_sys; * directory is not a local directory, globaldir is NULL. */ EXTERN char_u *globaldir INIT(= NULL); -/* Characters from 'listchars' option */ +// 'listchars' characters. Defaults are overridden in set_chars_option(). EXTERN int lcs_eol INIT(= '$'); EXTERN int lcs_ext INIT(= NUL); EXTERN int lcs_prec INIT(= NUL); @@ -963,20 +941,21 @@ EXTERN int lcs_tab2 INIT(= NUL); EXTERN int lcs_trail INIT(= NUL); EXTERN int lcs_conceal INIT(= ' '); -/* Characters from 'fillchars' option */ +// 'fillchars' characters. Defaults are overridden in set_chars_option(). EXTERN int fill_stl INIT(= ' '); EXTERN int fill_stlnc INIT(= ' '); -EXTERN int fill_vert INIT(= ' '); -EXTERN int fill_fold INIT(= '-'); +EXTERN int fill_vert INIT(= 9474); // │ +EXTERN int fill_fold INIT(= 183); // · EXTERN int fill_diff INIT(= '-'); /* Whether 'keymodel' contains "stopsel" and "startsel". */ EXTERN int km_stopsel INIT(= FALSE); EXTERN int km_startsel INIT(= FALSE); -EXTERN int cedit_key INIT(= -1); /* key value of 'cedit' option */ -EXTERN int cmdwin_type INIT(= 0); /* type of cmdline window or 0 */ -EXTERN int cmdwin_result INIT(= 0); /* result of cmdline window or 0 */ +EXTERN int cedit_key INIT(= -1); ///< key value of 'cedit' option +EXTERN int cmdwin_type INIT(= 0); ///< type of cmdline window or 0 +EXTERN int cmdwin_result INIT(= 0); ///< result of cmdline window or 0 +EXTERN int cmdwin_level INIT(= 0); ///< cmdline recursion level EXTERN char_u no_lines_msg[] INIT(= N_("--No lines in buffer--")); @@ -1057,6 +1036,7 @@ EXTERN char_u e_for[] INIT(= N_("E588: :endfor without :for")); EXTERN char_u e_exists[] INIT(= N_("E13: File exists (add ! to override)")); EXTERN char_u e_failed[] INIT(= N_("E472: Command failed")); EXTERN char_u e_internal[] INIT(= N_("E473: Internal error")); +EXTERN char_u e_intern2[] INIT(= N_("E685: Internal error: %s")); EXTERN char_u e_interr[] INIT(= N_("Interrupted")); EXTERN char_u e_invaddr[] INIT(= N_("E14: Invalid address")); EXTERN char_u e_invarg[] INIT(= N_("E474: Invalid argument")); @@ -1065,11 +1045,20 @@ EXTERN char_u e_invexpr2[] INIT(= N_("E15: Invalid expression: %s")); EXTERN char_u e_invrange[] INIT(= N_("E16: Invalid range")); EXTERN char_u e_invcmd[] INIT(= N_("E476: Invalid command")); EXTERN char_u e_isadir2[] INIT(= N_("E17: \"%s\" is a directory")); -EXTERN char_u e_invjob[] INIT(= N_("E900: Invalid job id")); +EXTERN char_u e_invchan[] INIT(= N_("E900: Invalid channel id")); +EXTERN char_u e_invchanjob[] INIT(= N_("E900: Invalid channel id: not a job")); EXTERN char_u e_jobtblfull[] INIT(= N_("E901: Job table is full")); EXTERN char_u e_jobspawn[] INIT(= N_( - "E903: Process failed to start: %s: \"%s\"")); -EXTERN char_u e_jobnotpty[] INIT(= N_("E904: Job is not connected to a pty")); + "E903: Process failed to start: %s: \"%s\"")); +EXTERN char_u e_channotpty[] INIT(= N_("E904: channel is not a pty")); +EXTERN char_u e_stdiochan2[] INIT(= N_( + "E905: Couldn't open stdio channel: %s")); +EXTERN char_u e_invstream[] INIT(= N_("E906: invalid stream for channel")); +EXTERN char_u e_invstreamrpc[] INIT(= N_( + "E906: invalid stream for rpc channel, use 'rpc'")); +EXTERN char_u e_streamkey[] INIT(= N_( + "E5210: dict key '%s' already set for buffered stream in channel %" + PRIu64)); EXTERN char_u e_libcall[] INIT(= N_("E364: Library call failed for \"%s()\"")); EXTERN char_u e_mkdir[] INIT(= N_("E739: Cannot create directory %s: %s")); EXTERN char_u e_markinval[] INIT(= N_("E19: Mark has invalid line number")); @@ -1139,9 +1128,9 @@ EXTERN char_u e_winheight[] INIT(= N_( EXTERN char_u e_winwidth[] INIT(= N_( "E592: 'winwidth' cannot be smaller than 'winminwidth'")); EXTERN char_u e_write[] INIT(= N_("E80: Error while writing")); -EXTERN char_u e_zerocount[] INIT(= N_("Zero count")); -EXTERN char_u e_usingsid[] INIT(= N_("E81: Using <SID> not in a script context")); -EXTERN char_u e_intern2[] INIT(= N_("E685: Internal error: %s")); +EXTERN char_u e_zerocount[] INIT(= N_("E939: Positive count required")); +EXTERN char_u e_usingsid[] INIT(= N_( + "E81: Using <SID> not in a script context")); EXTERN char_u e_maxmempat[] INIT(= N_( "E363: pattern uses more memory than 'maxmempattern'")); EXTERN char_u e_emptybuf[] INIT(= N_("E749: empty buffer")); @@ -1157,6 +1146,14 @@ EXTERN char_u e_dirnotf[] INIT(= N_( EXTERN char_u e_unsupportedoption[] INIT(= N_("E519: Option not supported")); EXTERN char_u e_fnametoolong[] INIT(= N_("E856: Filename too long")); EXTERN char_u e_float_as_string[] INIT(= N_("E806: using Float as a String")); +EXTERN char_u e_autocmd_err[] INIT(=N_( + "E5500: autocmd has thrown an exception: %s")); +EXTERN char_u e_cmdmap_err[] INIT(=N_( + "E5520: <Cmd> mapping must end with <CR>")); +EXTERN char_u e_cmdmap_repeated[] INIT(=N_( + "E5521: <Cmd> mapping must end with <CR> before second <Cmd>")); +EXTERN char_u e_cmdmap_key[] INIT(=N_( + "E5522: <Cmd> mapping must not include %s key")); EXTERN char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM")); @@ -1177,9 +1174,9 @@ EXTERN char *ignoredp; // If a msgpack-rpc channel should be started over stdin/stdout EXTERN bool embedded_mode INIT(= false); - -/// next free id for a job or rpc channel -EXTERN uint64_t next_chan_id INIT(= 1); +// Dont try to start an user interface +// or read/write to stdio (unless embedding) +EXTERN bool headless_mode INIT(= false); /// Used to track the status of external functions. /// Currently only used for iconv(). diff --git a/src/nvim/hashtab.c b/src/nvim/hashtab.c index 3397788b00..526bc284a4 100644 --- a/src/nvim/hashtab.c +++ b/src/nvim/hashtab.c @@ -208,7 +208,7 @@ int hash_add(hashtab_T *ht, char_u *key) hash_T hash = hash_hash(key); hashitem_T *hi = hash_lookup(ht, (const char *)key, STRLEN(key), hash); if (!HASHITEM_EMPTY(hi)) { - EMSG2(_(e_intern2), "hash_add()"); + internal_error("hash_add()"); return FAIL; } hash_add_item(ht, hi, key, hash); diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h index 927fc94bbe..08157935f5 100644 --- a/src/nvim/highlight_defs.h +++ b/src/nvim/highlight_defs.h @@ -7,6 +7,34 @@ typedef int32_t RgbValue; +/// Highlighting attribute bits. +typedef enum { + HL_INVERSE = 0x01, + HL_BOLD = 0x02, + HL_ITALIC = 0x04, + HL_UNDERLINE = 0x08, + HL_UNDERCURL = 0x10, + HL_STANDOUT = 0x20, +} HlAttrFlags; + +/// Stores a complete highlighting entry, including colors and attributes +/// for both TUI and GUI. +typedef struct attr_entry { + int16_t rgb_ae_attr, cterm_ae_attr; // HL_BOLD, etc. + RgbValue rgb_fg_color, rgb_bg_color, rgb_sp_color; + int cterm_fg_color, cterm_bg_color; +} HlAttrs; + +#define HLATTRS_INIT (HlAttrs) { \ + .rgb_ae_attr = 0, \ + .cterm_ae_attr = 0, \ + .rgb_fg_color = -1, \ + .rgb_bg_color = -1, \ + .rgb_sp_color = -1, \ + .cterm_fg_color = 0, \ + .cterm_bg_color = 0, \ +} + /// Values for index in highlight_attr[]. /// When making changes, also update hlf_names below! typedef enum { @@ -117,7 +145,6 @@ EXTERN int highlight_attr[HLF_COUNT]; // Highl. attr for each context. EXTERN int highlight_user[9]; // User[1-9] attributes EXTERN int highlight_stlnc[9]; // On top of user EXTERN int cterm_normal_fg_color INIT(= 0); -EXTERN int cterm_normal_fg_bold INIT(= 0); EXTERN int cterm_normal_bg_color INIT(= 0); EXTERN RgbValue normal_fg INIT(= -1); EXTERN RgbValue normal_bg INIT(= -1); diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c index f7706f5a0d..b78b56562c 100644 --- a/src/nvim/if_cscope.c +++ b/src/nvim/if_cscope.c @@ -172,14 +172,10 @@ void set_context_in_cscope_cmd(expand_T *xp, const char *arg, cmdidx_T cmdidx) } -/* - * PRIVATE: do_cscope_general - * - * Find the command, print help if invalid, and then call the corresponding - * command function. - */ -static void -do_cscope_general ( +/// Find the command, print help if invalid, and then call the corresponding +/// command function. +static void +do_cscope_general( exarg_T *eap, int make_split /* whether to split window */ ) @@ -208,29 +204,20 @@ do_cscope_general ( postponed_split_tab = 0; } -/* - * PUBLIC: do_cscope - */ -void do_cscope(exarg_T *eap) +/// Implementation of ":cscope" and ":lcscope" +void ex_cscope(exarg_T *eap) { do_cscope_general(eap, FALSE); } -/* - * PUBLIC: do_scscope - * - * same as do_cscope, but splits window, too. - */ -void do_scscope(exarg_T *eap) +/// Implementation of ":scscope". Same as ex_cscope(), but splits window, too. +void ex_scscope(exarg_T *eap) { do_cscope_general(eap, TRUE); } -/* - * PUBLIC: do_cstag - * - */ -void do_cstag(exarg_T *eap) +/// Implementation of ":cstag" +void ex_cstag(exarg_T *eap) { int ret = FALSE; @@ -285,18 +272,13 @@ void do_cstag(exarg_T *eap) (void)EMSG(_("E257: cstag: tag not found")); g_do_tagpreview = 0; } - -} /* do_cscope */ +} -/* - * PUBLIC: cs_find - * - * this simulates a vim_fgets(), but for cscope, returns the next line - * from the cscope output. should only be called from find_tags() - * - * returns TRUE if eof, FALSE otherwise - */ +/// This simulates a vim_fgets(), but for cscope, returns the next line +/// from the cscope output. should only be called from find_tags() +/// +/// @return TRUE if eof, FALSE otherwise int cs_fgets(char_u *buf, int size) { char *p; @@ -309,21 +291,13 @@ int cs_fgets(char_u *buf, int size) } /* cs_fgets */ -/* - * PUBLIC: cs_free_tags - * - * called only from do_tag(), when popping the tag stack - */ +/// Called only from do_tag(), when popping the tag stack. void cs_free_tags(void) { cs_manage_matches(NULL, NULL, 0, Free); } -/* - * PUBLIC: cs_print_tags - * - * called from do_tag() - */ +/// Called from do_tag(). void cs_print_tags(void) { cs_manage_matches(NULL, NULL, 0, Print); @@ -404,14 +378,8 @@ int cs_connection(int num, char_u *dbpath, char_u *ppath) * PRIVATE functions ****************************************************************************/ -/* - * PRIVATE: cs_add - * - * add cscope database or a directory name (to look for cscope.out) - * to the cscope connection list - * - * MAXPATHL 256 - */ +/// Add cscope database or a directory name (to look for cscope.out) +/// to the cscope connection list. static int cs_add(exarg_T *eap) { char *fname, *ppath, *flags = NULL; @@ -437,17 +405,13 @@ static void cs_stat_emsg(char *fname) } -/* - * PRIVATE: cs_add_common - * - * the common routine to add a new cscope connection. called by - * cs_add() and cs_reset(). i really don't like to do this, but this - * routine uses a number of goto statements. - */ -static int -cs_add_common ( - char *arg1, /* filename - may contain environment variables */ - char *arg2, /* prepend path - may contain environment variables */ +/// The common routine to add a new cscope connection. Called by +/// cs_add() and cs_reset(). I really don't like to do this, but this +/// routine uses a number of goto statements. +static int +cs_add_common( + char *arg1, // filename - may contain environment variables + char *arg2, // prepend path - may contain environment variables char *flags ) { @@ -561,11 +525,7 @@ static int cs_check_for_tags(void) return p_tags[0] != NUL && curbuf->b_p_tags != NULL; } /* cs_check_for_tags */ -/* - * PRIVATE: cs_cnt_connections - * - * count the number of cscope connections - */ +/// Count the number of cscope connections. static size_t cs_cnt_connections(void) { size_t cnt = 0; @@ -585,21 +545,23 @@ static void cs_reading_emsg( } #define CSREAD_BUFSIZE 2048 -/* - * PRIVATE: cs_cnt_matches - * - * count the number of matches for a given cscope connection. - */ +/// Count the number of matches for a given cscope connection. static int cs_cnt_matches(size_t idx) { char *stok; - int nlines; + int nlines = 0; char *buf = xmalloc(CSREAD_BUFSIZE); for (;; ) { + errno = 0; if (!fgets(buf, CSREAD_BUFSIZE, csinfo[idx].fr_fp)) { - if (feof(csinfo[idx].fr_fp)) + if (errno == EINTR) { + continue; + } + + if (feof(csinfo[idx].fr_fp)) { errno = EIO; + } cs_reading_emsg(idx); @@ -607,16 +569,20 @@ static int cs_cnt_matches(size_t idx) return CSCOPE_FAILURE; } - /* - * If the database is out of date, or there's some other problem, - * cscope will output error messages before the number-of-lines output. - * Display/discard any output that doesn't match what we want. - * Accept "\S*cscope: X lines", also matches "mlcscope". - */ - if ((stok = strtok(buf, (const char *)" ")) == NULL) + // If the database is out of date, or there's some other problem, + // cscope will output error messages before the number-of-lines output. + // Display/discard any output that doesn't match what we want. + // Accept "\S*cscope: X lines", also matches "mlcscope". + // Bail out for the "Unable to search" error. + if (strstr((const char *)buf, "Unable to search database") != NULL) { + break; + } + if ((stok = strtok(buf, (const char *)" ")) == NULL) { continue; - if (strstr((const char *)stok, "cscope:") == NULL) + } + if (strstr((const char *)stok, "cscope:") == NULL) { continue; + } if ((stok = strtok(NULL, (const char *)" ")) == NULL) continue; @@ -639,11 +605,7 @@ static int cs_cnt_matches(size_t idx) } /* cs_cnt_matches */ -/* - * PRIVATE: cs_create_cmd - * - * Creates the actual cscope command query from what the user entered. - */ +/// Creates the actual cscope command query from what the user entered. static char *cs_create_cmd(char *csoption, char *pattern) { char *cmd; @@ -699,12 +661,8 @@ static char *cs_create_cmd(char *csoption, char *pattern) } /* cs_create_cmd */ -/* - * PRIVATE: cs_create_connection - * - * This piece of code was taken/adapted from nvi. do we need to add - * the BSD license notice? - */ +/// This piece of code was taken/adapted from nvi. do we need to add +/// the BSD license notice? static int cs_create_connection(size_t i) { #ifdef UNIX @@ -830,7 +788,6 @@ err_closing: if (execl("/bin/sh", "sh", "-c", cmd, (char *)NULL) == -1) PERROR(_("cs_create_connection exec failed")); - stream_set_blocking(input_global_fd(), true); // normalize stream (#2598) exit(127); /* NOTREACHED */ default: /* parent. */ @@ -893,14 +850,10 @@ err_closing: } /* cs_create_connection */ -/* - * PRIVATE: cs_find - * - * query cscope using command line interface. parse the output and use tselect - * to allow choices. like Nvi, creates a pipe to send to/from query/cscope. - * - * returns TRUE if we jump to a tag or abort, FALSE if not. - */ +/// Query cscope using command line interface. Parse the output and use tselect +/// to allow choices. Like Nvi, creates a pipe to send to/from query/cscope. +/// +/// @return TRUE if we jump to a tag or abort, FALSE if not. static int cs_find(exarg_T *eap) { char *opt, *pat; @@ -934,11 +887,7 @@ static int cs_find(exarg_T *eap) } /* cs_find */ -/* - * PRIVATE: cs_find_common - * - * common code for cscope find, shared by cs_find() and do_cstag() - */ +/// Common code for cscope find, shared by cs_find() and ex_cstag(). static int cs_find_common(char *opt, char *pat, int forceit, int verbose, int use_ll, char_u *cmdline) { @@ -1067,9 +1016,9 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, fclose(f); if (use_ll) /* Use location list */ wp = curwin; - /* '-' starts a new error list */ + // '-' starts a new error list if (qf_init(wp, tmp, (char_u *)"%f%*\\t%l%*\\t%m", - *qfpos == '-', cmdline) > 0) { + *qfpos == '-', cmdline, NULL) > 0) { if (postponed_split != 0) { (void)win_split(postponed_split > 0 ? postponed_split : 0, postponed_split_flags); @@ -1111,11 +1060,7 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, } /* cs_find_common */ -/* - * PRIVATE: cs_help - * - * print help - */ +/// Print help. static int cs_help(exarg_T *eap) { cscmd_T *cmdp = cs_cmds; @@ -1163,11 +1108,7 @@ static void clear_csinfo(size_t i) csinfo[i].to_fp = NULL; } -/* - * PRIVATE: cs_insert_filelist - * - * insert a new cscope database filename into the filelist - */ +/// Insert a new cscope database filename into the filelist. static int cs_insert_filelist(char *fname, char *ppath, char *flags, FileInfo *file_info) { @@ -1227,11 +1168,7 @@ static int cs_insert_filelist(char *fname, char *ppath, char *flags, } /* cs_insert_filelist */ -/* - * PRIVATE: cs_lookup_cmd - * - * find cscope command in command table - */ +/// Find cscope command in command table. static cscmd_T * cs_lookup_cmd(exarg_T *eap) { cscmd_T *cmdp; @@ -1256,11 +1193,7 @@ static cscmd_T * cs_lookup_cmd(exarg_T *eap) } /* cs_lookup_cmd */ -/* - * PRIVATE: cs_kill - * - * nuke em - */ +/// Nuke em. static int cs_kill(exarg_T *eap) { char *stok; @@ -1317,11 +1250,7 @@ static int cs_kill(exarg_T *eap) } /* cs_kill */ -/* - * PRIVATE: cs_kill_execute - * - * Actually kills a specific cscope connection. - */ +/// Actually kills a specific cscope connection. static void cs_kill_execute( size_t i, /* cscope table index */ char *cname /* cscope database name */ @@ -1336,26 +1265,22 @@ static void cs_kill_execute( } -/* - * PRIVATE: cs_make_vim_style_matches - * - * convert the cscope output into a ctags style entry (as might be found - * in a ctags tags file). there's one catch though: cscope doesn't tell you - * the type of the tag you are looking for. for example, in Darren Hiebert's - * ctags (the one that comes with vim), #define's use a line number to find the - * tag in a file while function definitions use a regexp search pattern. - * - * i'm going to always use the line number because cscope does something - * quirky (and probably other things i don't know about): - * - * if you have "# define" in your source file, which is - * perfectly legal, cscope thinks you have "#define". this - * will result in a failed regexp search. :( - * - * besides, even if this particular case didn't happen, the search pattern - * would still have to be modified to escape all the special regular expression - * characters to comply with ctags formatting. - */ +/// Convert the cscope output into a ctags style entry (as might be found +/// in a ctags tags file). there's one catch though: cscope doesn't tell you +/// the type of the tag you are looking for. for example, in Darren Hiebert's +/// ctags (the one that comes with vim), #define's use a line number to find the +/// tag in a file while function definitions use a regexp search pattern. +/// +/// I'm going to always use the line number because cscope does something +/// quirky (and probably other things i don't know about): +/// +/// if you have "# define" in your source file, which is +/// perfectly legal, cscope thinks you have "#define". this +/// will result in a failed regexp search. :( +/// +/// Besides, even if this particular case didn't happen, the search pattern +/// would still have to be modified to escape all the special regular expression +/// characters to comply with ctags formatting. static char *cs_make_vim_style_matches(char *fname, char *slno, char *search, char *tagstr) { @@ -1389,24 +1314,20 @@ static char *cs_make_vim_style_matches(char *fname, char *slno, char *search, } /* cs_make_vim_style_matches */ -/* - * PRIVATE: cs_manage_matches - * - * this is kind of hokey, but i don't see an easy way round this.. - * - * Store: keep a ptr to the (malloc'd) memory of matches originally - * generated from cs_find(). the matches are originally lines directly - * from cscope output, but transformed to look like something out of a - * ctags. see cs_make_vim_style_matches for more details. - * - * Get: used only from cs_fgets(), this simulates a vim_fgets() to return - * the next line from the cscope output. it basically keeps track of which - * lines have been "used" and returns the next one. - * - * Free: frees up everything and resets - * - * Print: prints the tags - */ +/// This is kind of hokey, but i don't see an easy way round this. +/// +/// Store: keep a ptr to the (malloc'd) memory of matches originally +/// generated from cs_find(). the matches are originally lines directly +/// from cscope output, but transformed to look like something out of a +/// ctags. see cs_make_vim_style_matches for more details. +/// +/// Get: used only from cs_fgets(), this simulates a vim_fgets() to return +/// the next line from the cscope output. it basically keeps track of which +/// lines have been "used" and returns the next one. +/// +/// Free: frees up everything and resets +/// +/// Print: prints the tags static char *cs_manage_matches(char **matches, char **contexts, size_t totmatches, mcmd_e cmd) { @@ -1452,8 +1373,8 @@ static char *cs_manage_matches(char **matches, char **contexts, case Print: cs_print_tags_priv(mp, cp, cnt); break; - default: /* should not reach here */ - (void)EMSG(_("E570: fatal error in cs_manage_matches")); + default: // should not reach here + IEMSG(_("E570: fatal error in cs_manage_matches")); return NULL; } @@ -1461,11 +1382,7 @@ static char *cs_manage_matches(char **matches, char **contexts, } /* cs_manage_matches */ -/* - * PRIVATE: cs_parse_results - * - * parse cscope output - */ +/// Parse cscope output. static char *cs_parse_results(size_t cnumber, char *buf, int bufsize, char **context, char **linenumber, char **search) { @@ -1473,9 +1390,16 @@ static char *cs_parse_results(size_t cnumber, char *buf, int bufsize, char *p; char *name; +retry: + errno = 0; if (fgets(buf, bufsize, csinfo[cnumber].fr_fp) == NULL) { - if (feof(csinfo[cnumber].fr_fp)) + if (errno == EINTR) { + goto retry; + } + + if (feof(csinfo[cnumber].fr_fp)) { errno = EIO; + } cs_reading_emsg(cnumber); @@ -1515,11 +1439,7 @@ static char *cs_parse_results(size_t cnumber, char *buf, int bufsize, return name; } -/* - * PRIVATE: cs_file_results - * - * write cscope find results to file - */ +/// Write cscope find results to file. static void cs_file_results(FILE *f, int *nummatches_a) { char *search, *slno; @@ -1560,13 +1480,9 @@ static void cs_file_results(FILE *f, int *nummatches_a) xfree(buf); } -/* - * PRIVATE: cs_fill_results - * - * get parsed cscope output and calls cs_make_vim_style_matches to convert - * into ctags format - * When there are no matches sets "*matches_p" to NULL. - */ +/// Get parsed cscope output and calls cs_make_vim_style_matches to convert +/// into ctags format. +/// When there are no matches sets "*matches_p" to NULL. static void cs_fill_results(char *tagstr, size_t totmatches, int *nummatches_a, char ***matches_p, char ***cntxts_p, size_t *matched) @@ -1758,11 +1674,7 @@ static void cs_print_tags_priv(char **matches, char **cntxts, xfree(buf); } -/* - * PRIVATE: cs_read_prompt - * - * read a cscope prompt (basically, skip over the ">> ") - */ +/// Read a cscope prompt (basically, skip over the ">> "). static int cs_read_prompt(size_t i) { int ch; @@ -1777,8 +1689,15 @@ static int cs_read_prompt(size_t i) assert(IOSIZE >= cs_emsg_len); size_t maxlen = IOSIZE - cs_emsg_len; - for (;; ) { - while ((ch = getc(csinfo[i].fr_fp)) != EOF && ch != CSCOPE_PROMPT[0]) { + while (1) { + while (1) { + do { + errno = 0; + ch = fgetc(csinfo[i].fr_fp); + } while (ch == EOF && errno == EINTR && ferror(csinfo[i].fr_fp)); + if (ch == EOF || ch == CSCOPE_PROMPT[0]) { + break; + } // if there is room and char is printable if (bufpos < maxlen - 1 && vim_isprintc(ch)) { // lazy buffer allocation @@ -1807,9 +1726,13 @@ static int cs_read_prompt(size_t i) } } - for (size_t n = 0; n < strlen(CSCOPE_PROMPT); ++n) { - if (n > 0) - ch = (char)getc(csinfo[i].fr_fp); + for (size_t n = 0; n < strlen(CSCOPE_PROMPT); n++) { + if (n > 0) { + do { + errno = 0; + ch = fgetc(csinfo[i].fr_fp); + } while (ch == EOF && errno == EINTR && ferror(csinfo[i].fr_fp)); + } if (ch == EOF) { PERROR("cs_read_prompt EOF"); if (buf != NULL && buf[0] != NUL) @@ -1847,12 +1770,8 @@ static void sig_handler(int s) { #endif -/* - * PRIVATE: cs_release_csp - * - * Does the actual free'ing for the cs ptr with an optional flag of whether - * or not to free the filename. Called by cs_kill and cs_reset. - */ +/// Does the actual free'ing for the cs ptr with an optional flag of whether +/// or not to free the filename. Called by cs_kill and cs_reset. static void cs_release_csp(size_t i, int freefnpp) { // Trying to exit normally (not sure whether it is fit to Unix cscope) @@ -1964,11 +1883,7 @@ static void cs_release_csp(size_t i, int freefnpp) } /* cs_release_csp */ -/* - * PRIVATE: cs_reset - * - * calls cs_kill on all cscope connections then reinits - */ +/// Calls cs_kill on all cscope connections then reinits. static int cs_reset(exarg_T *eap) { char **dblist = NULL, **pplist = NULL, **fllist = NULL; @@ -2018,17 +1933,13 @@ static int cs_reset(exarg_T *eap) } /* cs_reset */ -/* - * PRIVATE: cs_resolve_file - * - * Construct the full pathname to a file found in the cscope database. - * (Prepends ppath, if there is one and if it's not already prepended, - * otherwise just uses the name found.) - * - * We need to prepend the prefix because on some cscope's (e.g., the one that - * ships with Solaris 2.6), the output never has the prefix prepended. - * Contrast this with my development system (Digital Unix), which does. - */ +/// Construct the full pathname to a file found in the cscope database. +/// (Prepends ppath, if there is one and if it's not already prepended, +/// otherwise just uses the name found.) +/// +/// We need to prepend the prefix because on some cscope's (e.g., the one that +/// ships with Solaris 2.6), the output never has the prefix prepended. +/// Contrast this with my development system (Digital Unix), which does. static char *cs_resolve_file(size_t i, char *name) { char *fullname; @@ -2074,11 +1985,7 @@ static char *cs_resolve_file(size_t i, char *name) } -/* - * PRIVATE: cs_show - * - * show all cscope connections - */ +/// Show all cscope connections. static int cs_show(exarg_T *eap) { if (cs_cnt_connections() == 0) @@ -2106,11 +2013,7 @@ static int cs_show(exarg_T *eap) } /* cs_show */ -/* - * PUBLIC: cs_end - * - * Only called when VIM exits to quit any cscope sessions. - */ +/// Only called when VIM exits to quit any cscope sessions. void cs_end(void) { for (size_t i = 0; i < csinfo_size; i++) diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c index fd194a4080..2a215f854f 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -515,34 +515,41 @@ int cin_isscopedecl(char_u *s) /* Maximum number of lines to search back for a "namespace" line. */ #define FIND_NAMESPACE_LIM 20 -/* - * Recognize a "namespace" scope declaration. - */ -static int cin_is_cpp_namespace(char_u *s) +// Recognize a "namespace" scope declaration. +static bool cin_is_cpp_namespace(char_u *s) { - char_u *p; - int has_name = FALSE; + char_u *p; + bool has_name = false; + bool has_name_start = false; s = cin_skipcomment(s); if (STRNCMP(s, "namespace", 9) == 0 && (s[9] == NUL || !vim_iswordc(s[9]))) { p = cin_skipcomment(skipwhite(s + 9)); while (*p != NUL) { if (ascii_iswhite(*p)) { - has_name = TRUE; /* found end of a name */ + has_name = true; // found end of a name p = cin_skipcomment(skipwhite(p)); } else if (*p == '{') { break; } else if (vim_iswordc(*p)) { - if (has_name) - return FALSE; /* word character after skipping past name */ - ++p; + has_name_start = true; + if (has_name) { + return false; // word character after skipping past name + } + p++; + } else if (p[0] == ':' && p[1] == ':' && vim_iswordc(p[2])) { + if (!has_name_start || has_name) { + return false; + } + // C++ 17 nested namespace + p += 3; } else { - return FALSE; + return false; } } - return TRUE; + return true; } - return FALSE; + return false; } /* @@ -727,16 +734,20 @@ static int cin_ispreproc(char_u *s) return FALSE; } -/* - * Return TRUE if line "*pp" at "*lnump" is a preprocessor statement or a - * continuation line of a preprocessor statement. Decrease "*lnump" to the - * start and return the line in "*pp". - */ -static int cin_ispreproc_cont(char_u **pp, linenr_T *lnump) +/// Return TRUE if line "*pp" at "*lnump" is a preprocessor statement or a +/// continuation line of a preprocessor statement. Decrease "*lnump" to the +/// start and return the line in "*pp". +/// Put the amount of indent in "*amount". +static int cin_ispreproc_cont(char_u **pp, linenr_T *lnump, int *amount) { char_u *line = *pp; linenr_T lnum = *lnump; - int retval = FALSE; + int retval = false; + int candidate_amount = *amount; + + if (*line != NUL && line[STRLEN(line) - 1] == '\\') { + candidate_amount = get_indent_lnum(lnum); + } for (;; ) { if (cin_ispreproc(line)) { @@ -751,8 +762,12 @@ static int cin_ispreproc_cont(char_u **pp, linenr_T *lnump) break; } - if (lnum != *lnump) + if (lnum != *lnump) { *pp = ml_get(*lnump); + } + if (retval) { + *amount = candidate_amount; + } return retval; } @@ -1299,6 +1314,43 @@ static int cin_starts_with(char_u *s, char *word) return STRNCMP(s, word, l) == 0 && !vim_isIDc(s[l]); } +/// Recognize a `extern "C"` or `extern "C++"` linkage specifications. +static int cin_is_cpp_extern_c(char_u *s) +{ + char_u *p; + int has_string_literal = false; + + s = cin_skipcomment(s); + if (STRNCMP(s, "extern", 6) == 0 && (s[6] == NUL || !vim_iswordc(s[6]))) { + p = cin_skipcomment(skipwhite(s + 6)); + while (*p != NUL) { + if (ascii_iswhite(*p)) { + p = cin_skipcomment(skipwhite(p)); + } else if (*p == '{') { + break; + } else if (p[0] == '"' && p[1] == 'C' && p[2] == '"') { + if (has_string_literal) { + return false; + } + has_string_literal = true; + p += 3; + } else if (p[0] == '"' && p[1] == 'C' && p[2] == '+' && p[3] == '+' + && p[4] == '"') { + if (has_string_literal) { + return false; + } + has_string_literal = true; + p += 5; + } else { + return false; + } + } + return has_string_literal ? true : false; + } + return false; +} + + /* * Skip strings, chars and comments until at or past "trypos". * Return the column found. @@ -1307,14 +1359,19 @@ static int cin_skip2pos(pos_T *trypos) { char_u *line; char_u *p; + char_u *new_p; p = line = ml_get(trypos->lnum); while (*p && (colnr_T)(p - line) < trypos->col) { - if (cin_iscomment(p)) + if (cin_iscomment(p)) { p = cin_skipcomment(p); - else { - p = skip_string(p); - ++p; + } else { + new_p = skip_string(p); + if (new_p == p) { + p++; + } else { + p = new_p; + } } } return (int)(p - line); @@ -1604,6 +1661,12 @@ void parse_cino(buf_T *buf) * while(). */ buf->b_ind_if_for_while = 0; + // indentation for # comments + buf->b_ind_hash_comment = 0; + + // Handle C++ extern "C" or "C++" + buf->b_ind_cpp_extern_c = 0; + for (p = buf->b_p_cino; *p; ) { l = p++; if (*p == '-') @@ -1672,6 +1735,7 @@ void parse_cino(buf_T *buf) case '#': buf->b_ind_hash_comment = n; break; case 'N': buf->b_ind_cpp_namespace = n; break; case 'k': buf->b_ind_if_for_while = n; break; + case 'E': buf->b_ind_cpp_extern_c = n; break; } if (*p == ',') ++p; @@ -1987,10 +2051,12 @@ int get_c_indent(void) amount = -1; for (lnum = cur_curpos.lnum - 1; lnum > our_paren_pos.lnum; --lnum) { l = skipwhite(ml_get(lnum)); - if (cin_nocode(l)) /* skip comment lines */ + if (cin_nocode(l)) { // skip comment lines continue; - if (cin_ispreproc_cont(&l, &lnum)) - continue; /* ignore #define, #if, etc. */ + } + if (cin_ispreproc_cont(&l, &lnum, &amount)) { + continue; // ignore #define, #if, etc. + } curwin->w_cursor.lnum = lnum; /* Skip a comment or raw string. XXX */ @@ -2300,8 +2366,11 @@ int get_c_indent(void) amount += curbuf->b_ind_open_imag; l = skipwhite(get_cursor_line_ptr()); - if (cin_is_cpp_namespace(l)) + if (cin_is_cpp_namespace(l)) { amount += curbuf->b_ind_cpp_namespace; + } else if (cin_is_cpp_extern_c(l)) { + amount += curbuf->b_ind_cpp_extern_c; + } } else { /* Compensate for adding b_ind_open_extra later. */ amount -= curbuf->b_ind_open_extra; @@ -2346,15 +2415,14 @@ int get_c_indent(void) * up with it. */ if (curwin->w_cursor.lnum <= ourscope) { - /* we reached end of scope: - * if looking for an enum or structure initialization - * go further back: - * if it is an initializer (enum xxx or xxx =), then - * don't add ind_continuation, otherwise it is a variable - * declaration: - * int x, - * here; <-- add ind_continuation - */ + // We reached end of scope: + // If looking for a enum or structure initialization + // go further back: + // If it is an initializer (enum xxx or xxx =), then + // don't add ind_continuation, otherwise it is a variable + // declaration: + // int x, + // here; <-- add ind_continuation if (lookfor == LOOKFOR_ENUM_OR_INIT) { if (curwin->w_cursor.lnum == 0 || curwin->w_cursor.lnum @@ -2382,11 +2450,12 @@ int get_c_indent(void) continue; } - /* - * Skip preprocessor directives and blank lines. - */ - if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum)) + // + // Skip preprocessor directives and blank lines. + // + if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum, &amount)) { continue; + } if (cin_nocode(l)) continue; @@ -2490,15 +2559,19 @@ int get_c_indent(void) continue; } - /* Skip preprocessor directives and blank lines. */ - if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum)) + // Skip preprocessor directives and blank lines. + if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum, &amount)) { continue; + } /* Finally the actual check for "namespace". */ if (cin_is_cpp_namespace(l)) { amount += curbuf->b_ind_cpp_namespace - added_to_amount; break; + } else if (cin_is_cpp_extern_c(l)) { + amount += curbuf->b_ind_cpp_extern_c - added_to_amount; + break; } if (cin_nocode(l)) @@ -2655,9 +2728,10 @@ int get_c_indent(void) * unlocked it) */ l = get_cursor_line_ptr(); - if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum) - || cin_nocode(l)) + if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum, &amount) + || cin_nocode(l)) { continue; + } /* * Are we at the start of a cpp base class declaration or @@ -3302,11 +3376,12 @@ term_again: break; } - /* - * Skip preprocessor directives and blank lines. - */ - if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum)) + // + // Skip preprocessor directives and blank lines. + // + if (cin_ispreproc_cont(&l, &curwin->w_cursor.lnum, &amount)) { continue; + } if (cin_nocode(l)) continue; @@ -3398,9 +3473,10 @@ term_again: while (curwin->w_cursor.lnum > 1) { look = ml_get(--curwin->w_cursor.lnum); - if (!(cin_nocode(look) || cin_ispreproc_cont( - &look, &curwin->w_cursor.lnum))) + if (!(cin_nocode(look) + || cin_ispreproc_cont(&look, &curwin->w_cursor.lnum, &amount))) { break; + } } if (curwin->w_cursor.lnum > 0 && cin_ends_in(look, (char_u *)"}", NULL)) diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c index ee67b4c19d..628bfef221 100644 --- a/src/nvim/keymap.c +++ b/src/nvim/keymap.c @@ -24,23 +24,23 @@ * Some useful tables. */ -static struct modmasktable { - short mod_mask; /* Bit-mask for particular key modifier */ - short mod_flag; /* Bit(s) for particular key modifier */ - char_u name; /* Single letter name of modifier */ -} mod_mask_table[] = -{ - {MOD_MASK_ALT, MOD_MASK_ALT, (char_u)'M'}, - {MOD_MASK_META, MOD_MASK_META, (char_u)'T'}, - {MOD_MASK_CTRL, MOD_MASK_CTRL, (char_u)'C'}, - {MOD_MASK_SHIFT, MOD_MASK_SHIFT, (char_u)'S'}, - {MOD_MASK_MULTI_CLICK, MOD_MASK_2CLICK, (char_u)'2'}, - {MOD_MASK_MULTI_CLICK, MOD_MASK_3CLICK, (char_u)'3'}, - {MOD_MASK_MULTI_CLICK, MOD_MASK_4CLICK, (char_u)'4'}, - {MOD_MASK_CMD, MOD_MASK_CMD, (char_u)'D'}, +static const struct modmasktable { + uint16_t mod_mask; ///< Bit-mask for particular key modifier. + uint16_t mod_flag; ///< Bit(s) for particular key modifier. + char_u name; ///< Single letter name of modifier. +} mod_mask_table[] = { + { MOD_MASK_ALT, MOD_MASK_ALT, (char_u)'M' }, + { MOD_MASK_META, MOD_MASK_META, (char_u)'T' }, + { MOD_MASK_CTRL, MOD_MASK_CTRL, (char_u)'C' }, + { MOD_MASK_SHIFT, MOD_MASK_SHIFT, (char_u)'S' }, + { MOD_MASK_MULTI_CLICK, MOD_MASK_2CLICK, (char_u)'2' }, + { MOD_MASK_MULTI_CLICK, MOD_MASK_3CLICK, (char_u)'3' }, + { MOD_MASK_MULTI_CLICK, MOD_MASK_4CLICK, (char_u)'4' }, + { MOD_MASK_CMD, MOD_MASK_CMD, (char_u)'D' }, // 'A' must be the last one - {MOD_MASK_ALT, MOD_MASK_ALT, (char_u)'A'}, - {0, 0, NUL} + { MOD_MASK_ALT, MOD_MASK_ALT, (char_u)'A' }, + { 0, 0, NUL } + // NOTE: when adding an entry, update MAX_KEY_NAME_LEN! }; /* @@ -139,156 +139,155 @@ static char_u modifier_keys_table[] = NUL }; -static struct key_name_entry { - int key; /* Special key code or ascii value */ - char_u *name; /* Name of key */ -} key_names_table[] = -{ - {' ', (char_u *)"Space"}, - {TAB, (char_u *)"Tab"}, - {K_TAB, (char_u *)"Tab"}, - {NL, (char_u *)"NL"}, - {NL, (char_u *)"NewLine"}, /* Alternative name */ - {NL, (char_u *)"LineFeed"}, /* Alternative name */ - {NL, (char_u *)"LF"}, /* Alternative name */ - {CAR, (char_u *)"CR"}, - {CAR, (char_u *)"Return"}, /* Alternative name */ - {CAR, (char_u *)"Enter"}, /* Alternative name */ - {K_BS, (char_u *)"BS"}, - {K_BS, (char_u *)"BackSpace"}, /* Alternative name */ - {ESC, (char_u *)"Esc"}, - {CSI, (char_u *)"CSI"}, - {K_CSI, (char_u *)"xCSI"}, - {'|', (char_u *)"Bar"}, - {'\\', (char_u *)"Bslash"}, - {K_DEL, (char_u *)"Del"}, - {K_DEL, (char_u *)"Delete"}, /* Alternative name */ - {K_KDEL, (char_u *)"kDel"}, - {K_UP, (char_u *)"Up"}, - {K_DOWN, (char_u *)"Down"}, - {K_LEFT, (char_u *)"Left"}, - {K_RIGHT, (char_u *)"Right"}, - {K_XUP, (char_u *)"xUp"}, - {K_XDOWN, (char_u *)"xDown"}, - {K_XLEFT, (char_u *)"xLeft"}, - {K_XRIGHT, (char_u *)"xRight"}, - - {K_F1, (char_u *)"F1"}, - {K_F2, (char_u *)"F2"}, - {K_F3, (char_u *)"F3"}, - {K_F4, (char_u *)"F4"}, - {K_F5, (char_u *)"F5"}, - {K_F6, (char_u *)"F6"}, - {K_F7, (char_u *)"F7"}, - {K_F8, (char_u *)"F8"}, - {K_F9, (char_u *)"F9"}, - {K_F10, (char_u *)"F10"}, - - {K_F11, (char_u *)"F11"}, - {K_F12, (char_u *)"F12"}, - {K_F13, (char_u *)"F13"}, - {K_F14, (char_u *)"F14"}, - {K_F15, (char_u *)"F15"}, - {K_F16, (char_u *)"F16"}, - {K_F17, (char_u *)"F17"}, - {K_F18, (char_u *)"F18"}, - {K_F19, (char_u *)"F19"}, - {K_F20, (char_u *)"F20"}, - - {K_F21, (char_u *)"F21"}, - {K_F22, (char_u *)"F22"}, - {K_F23, (char_u *)"F23"}, - {K_F24, (char_u *)"F24"}, - {K_F25, (char_u *)"F25"}, - {K_F26, (char_u *)"F26"}, - {K_F27, (char_u *)"F27"}, - {K_F28, (char_u *)"F28"}, - {K_F29, (char_u *)"F29"}, - {K_F30, (char_u *)"F30"}, - - {K_F31, (char_u *)"F31"}, - {K_F32, (char_u *)"F32"}, - {K_F33, (char_u *)"F33"}, - {K_F34, (char_u *)"F34"}, - {K_F35, (char_u *)"F35"}, - {K_F36, (char_u *)"F36"}, - {K_F37, (char_u *)"F37"}, - - {K_XF1, (char_u *)"xF1"}, - {K_XF2, (char_u *)"xF2"}, - {K_XF3, (char_u *)"xF3"}, - {K_XF4, (char_u *)"xF4"}, - - {K_HELP, (char_u *)"Help"}, - {K_UNDO, (char_u *)"Undo"}, - {K_INS, (char_u *)"Insert"}, - {K_INS, (char_u *)"Ins"}, /* Alternative name */ - {K_KINS, (char_u *)"kInsert"}, - {K_HOME, (char_u *)"Home"}, - {K_KHOME, (char_u *)"kHome"}, - {K_XHOME, (char_u *)"xHome"}, - {K_ZHOME, (char_u *)"zHome"}, - {K_END, (char_u *)"End"}, - {K_KEND, (char_u *)"kEnd"}, - {K_XEND, (char_u *)"xEnd"}, - {K_ZEND, (char_u *)"zEnd"}, - {K_PAGEUP, (char_u *)"PageUp"}, - {K_PAGEDOWN, (char_u *)"PageDown"}, - {K_KPAGEUP, (char_u *)"kPageUp"}, - {K_KPAGEDOWN, (char_u *)"kPageDown"}, - - {K_KPLUS, (char_u *)"kPlus"}, - {K_KMINUS, (char_u *)"kMinus"}, - {K_KDIVIDE, (char_u *)"kDivide"}, - {K_KMULTIPLY, (char_u *)"kMultiply"}, - {K_KENTER, (char_u *)"kEnter"}, - {K_KPOINT, (char_u *)"kPoint"}, - - {K_K0, (char_u *)"k0"}, - {K_K1, (char_u *)"k1"}, - {K_K2, (char_u *)"k2"}, - {K_K3, (char_u *)"k3"}, - {K_K4, (char_u *)"k4"}, - {K_K5, (char_u *)"k5"}, - {K_K6, (char_u *)"k6"}, - {K_K7, (char_u *)"k7"}, - {K_K8, (char_u *)"k8"}, - {K_K9, (char_u *)"k9"}, - - {'<', (char_u *)"lt"}, - - {K_MOUSE, (char_u *)"Mouse"}, - {K_LEFTMOUSE, (char_u *)"LeftMouse"}, - {K_LEFTMOUSE_NM, (char_u *)"LeftMouseNM"}, - {K_LEFTDRAG, (char_u *)"LeftDrag"}, - {K_LEFTRELEASE, (char_u *)"LeftRelease"}, - {K_LEFTRELEASE_NM, (char_u *)"LeftReleaseNM"}, - {K_MIDDLEMOUSE, (char_u *)"MiddleMouse"}, - {K_MIDDLEDRAG, (char_u *)"MiddleDrag"}, - {K_MIDDLERELEASE, (char_u *)"MiddleRelease"}, - {K_RIGHTMOUSE, (char_u *)"RightMouse"}, - {K_RIGHTDRAG, (char_u *)"RightDrag"}, - {K_RIGHTRELEASE, (char_u *)"RightRelease"}, - {K_MOUSEDOWN, (char_u *)"ScrollWheelUp"}, - {K_MOUSEUP, (char_u *)"ScrollWheelDown"}, - {K_MOUSELEFT, (char_u *)"ScrollWheelRight"}, - {K_MOUSERIGHT, (char_u *)"ScrollWheelLeft"}, - {K_MOUSEDOWN, (char_u *)"MouseDown"}, /* OBSOLETE: Use */ - {K_MOUSEUP, (char_u *)"MouseUp"}, /* ScrollWheelXXX instead */ - {K_X1MOUSE, (char_u *)"X1Mouse"}, - {K_X1DRAG, (char_u *)"X1Drag"}, - {K_X1RELEASE, (char_u *)"X1Release"}, - {K_X2MOUSE, (char_u *)"X2Mouse"}, - {K_X2DRAG, (char_u *)"X2Drag"}, - {K_X2RELEASE, (char_u *)"X2Release"}, - {K_DROP, (char_u *)"Drop"}, - {K_ZERO, (char_u *)"Nul"}, - {K_SNR, (char_u *)"SNR"}, - {K_PLUG, (char_u *)"Plug"}, - {K_PASTE, (char_u *)"Paste"}, - {K_FOCUSGAINED, (char_u *)"FocusGained"}, - {K_FOCUSLOST, (char_u *)"FocusLost"}, - {0, NULL} +static const struct key_name_entry { + int key; // Special key code or ascii value + const char *name; // Name of key +} key_names_table[] = { + { ' ', "Space" }, + { TAB, "Tab" }, + { K_TAB, "Tab" }, + { NL, "NL" }, + { NL, "NewLine" }, // Alternative name + { NL, "LineFeed" }, // Alternative name + { NL, "LF" }, // Alternative name + { CAR, "CR" }, + { CAR, "Return" }, // Alternative name + { CAR, "Enter" }, // Alternative name + { K_BS, "BS" }, + { K_BS, "BackSpace" }, // Alternative name + { ESC, "Esc" }, + { CSI, "CSI" }, + { K_CSI, "xCSI" }, + { '|', "Bar" }, + { '\\', "Bslash" }, + { K_DEL, "Del" }, + { K_DEL, "Delete" }, // Alternative name + { K_KDEL, "kDel" }, + { K_UP, "Up" }, + { K_DOWN, "Down" }, + { K_LEFT, "Left" }, + { K_RIGHT, "Right" }, + { K_XUP, "xUp" }, + { K_XDOWN, "xDown" }, + { K_XLEFT, "xLeft" }, + { K_XRIGHT, "xRight" }, + + { K_F1, "F1" }, + { K_F2, "F2" }, + { K_F3, "F3" }, + { K_F4, "F4" }, + { K_F5, "F5" }, + { K_F6, "F6" }, + { K_F7, "F7" }, + { K_F8, "F8" }, + { K_F9, "F9" }, + { K_F10, "F10" }, + + { K_F11, "F11" }, + { K_F12, "F12" }, + { K_F13, "F13" }, + { K_F14, "F14" }, + { K_F15, "F15" }, + { K_F16, "F16" }, + { K_F17, "F17" }, + { K_F18, "F18" }, + { K_F19, "F19" }, + { K_F20, "F20" }, + + { K_F21, "F21" }, + { K_F22, "F22" }, + { K_F23, "F23" }, + { K_F24, "F24" }, + { K_F25, "F25" }, + { K_F26, "F26" }, + { K_F27, "F27" }, + { K_F28, "F28" }, + { K_F29, "F29" }, + { K_F30, "F30" }, + + { K_F31, "F31" }, + { K_F32, "F32" }, + { K_F33, "F33" }, + { K_F34, "F34" }, + { K_F35, "F35" }, + { K_F36, "F36" }, + { K_F37, "F37" }, + + { K_XF1, "xF1" }, + { K_XF2, "xF2" }, + { K_XF3, "xF3" }, + { K_XF4, "xF4" }, + + { K_HELP, "Help" }, + { K_UNDO, "Undo" }, + { K_INS, "Insert" }, + { K_INS, "Ins" }, // Alternative name + { K_KINS, "kInsert" }, + { K_HOME, "Home" }, + { K_KHOME, "kHome" }, + { K_XHOME, "xHome" }, + { K_ZHOME, "zHome" }, + { K_END, "End" }, + { K_KEND, "kEnd" }, + { K_XEND, "xEnd" }, + { K_ZEND, "zEnd" }, + { K_PAGEUP, "PageUp" }, + { K_PAGEDOWN, "PageDown" }, + { K_KPAGEUP, "kPageUp" }, + { K_KPAGEDOWN, "kPageDown" }, + + { K_KPLUS, "kPlus" }, + { K_KMINUS, "kMinus" }, + { K_KDIVIDE, "kDivide" }, + { K_KMULTIPLY, "kMultiply" }, + { K_KENTER, "kEnter" }, + { K_KPOINT, "kPoint" }, + + { K_K0, "k0" }, + { K_K1, "k1" }, + { K_K2, "k2" }, + { K_K3, "k3" }, + { K_K4, "k4" }, + { K_K5, "k5" }, + { K_K6, "k6" }, + { K_K7, "k7" }, + { K_K8, "k8" }, + { K_K9, "k9" }, + + { '<', "lt" }, + + { K_MOUSE, "Mouse" }, + { K_LEFTMOUSE, "LeftMouse" }, + { K_LEFTMOUSE_NM, "LeftMouseNM" }, + { K_LEFTDRAG, "LeftDrag" }, + { K_LEFTRELEASE, "LeftRelease" }, + { K_LEFTRELEASE_NM, "LeftReleaseNM" }, + { K_MIDDLEMOUSE, "MiddleMouse" }, + { K_MIDDLEDRAG, "MiddleDrag" }, + { K_MIDDLERELEASE, "MiddleRelease" }, + { K_RIGHTMOUSE, "RightMouse" }, + { K_RIGHTDRAG, "RightDrag" }, + { K_RIGHTRELEASE, "RightRelease" }, + { K_MOUSEDOWN, "ScrollWheelUp" }, + { K_MOUSEUP, "ScrollWheelDown" }, + { K_MOUSELEFT, "ScrollWheelRight" }, + { K_MOUSERIGHT, "ScrollWheelLeft" }, + { K_MOUSEDOWN, "MouseDown" }, // OBSOLETE: Use + { K_MOUSEUP, "MouseUp" }, // ScrollWheelXXX instead + { K_X1MOUSE, "X1Mouse" }, + { K_X1DRAG, "X1Drag" }, + { K_X1RELEASE, "X1Release" }, + { K_X2MOUSE, "X2Mouse" }, + { K_X2DRAG, "X2Drag" }, + { K_X2RELEASE, "X2Release" }, + { K_DROP, "Drop" }, + { K_ZERO, "Nul" }, + { K_SNR, "SNR" }, + { K_PLUG, "Plug" }, + { K_PASTE, "Paste" }, + { K_COMMAND, "Cmd" }, + { 0, NULL } + // NOTE: When adding a long name update MAX_KEY_NAME_LEN. }; static struct mousetable { @@ -320,73 +319,73 @@ static struct mousetable { {0, 0, 0, 0}, }; -/* - * Return the modifier mask bit (MOD_MASK_*) which corresponds to the given - * modifier name ('S' for Shift, 'C' for Ctrl etc). - */ +/// Return the modifier mask bit (#MOD_MASK_*) corresponding to mod name +/// +/// E.g. 'S' for shift, 'C' for ctrl. int name_to_mod_mask(int c) + FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT { - int i; - c = TOUPPER_ASC(c); - for (i = 0; mod_mask_table[i].mod_mask != 0; i++) - if (c == mod_mask_table[i].name) + for (size_t i = 0; mod_mask_table[i].mod_mask != 0; i++) { + if (c == mod_mask_table[i].name) { return mod_mask_table[i].mod_flag; + } + } return 0; } -/* - * Check if if there is a special key code for "key" that includes the - * modifiers specified. - */ -int simplify_key(int key, int *modifiers) +/// Check if there is a special key code for "key" with specified modifiers +/// +/// @param[in] key Initial key code. +/// @param[in,out] modifiers Initial modifiers, is adjusted to have simplified +/// modifiers. +/// +/// @return Simplified key code. +int simplify_key(const int key, int *modifiers) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - int i; - int key0; - int key1; - if (*modifiers & (MOD_MASK_SHIFT | MOD_MASK_CTRL | MOD_MASK_ALT)) { - /* TAB is a special case */ + // TAB is a special case. if (key == TAB && (*modifiers & MOD_MASK_SHIFT)) { *modifiers &= ~MOD_MASK_SHIFT; return K_S_TAB; } - key0 = KEY2TERMCAP0(key); - key1 = KEY2TERMCAP1(key); - for (i = 0; modifier_keys_table[i] != NUL; i += MOD_KEYS_ENTRY_SIZE) + const int key0 = KEY2TERMCAP0(key); + const int key1 = KEY2TERMCAP1(key); + for (int i = 0; modifier_keys_table[i] != NUL; i += MOD_KEYS_ENTRY_SIZE) { if (key0 == modifier_keys_table[i + 3] && key1 == modifier_keys_table[i + 4] && (*modifiers & modifier_keys_table[i])) { *modifiers &= ~modifier_keys_table[i]; return TERMCAP2KEY(modifier_keys_table[i + 1], - modifier_keys_table[i + 2]); + modifier_keys_table[i + 2]); } + } } return key; } -/* - * Change <xHome> to <Home>, <xUp> to <Up>, etc. - */ -int handle_x_keys(int key) +/// Change <xKey> to <Key> +int handle_x_keys(const int key) + FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT { switch (key) { - case K_XUP: return K_UP; - case K_XDOWN: return K_DOWN; - case K_XLEFT: return K_LEFT; - case K_XRIGHT: return K_RIGHT; - case K_XHOME: return K_HOME; - case K_ZHOME: return K_HOME; - case K_XEND: return K_END; - case K_ZEND: return K_END; - case K_XF1: return K_F1; - case K_XF2: return K_F2; - case K_XF3: return K_F3; - case K_XF4: return K_F4; - case K_S_XF1: return K_S_F1; - case K_S_XF2: return K_S_F2; - case K_S_XF3: return K_S_F3; - case K_S_XF4: return K_S_F4; + case K_XUP: return K_UP; + case K_XDOWN: return K_DOWN; + case K_XLEFT: return K_LEFT; + case K_XRIGHT: return K_RIGHT; + case K_XHOME: return K_HOME; + case K_ZHOME: return K_HOME; + case K_XEND: return K_END; + case K_ZEND: return K_END; + case K_XF1: return K_F1; + case K_XF2: return K_F2; + case K_XF3: return K_F3; + case K_XF4: return K_F4; + case K_S_XF1: return K_S_F1; + case K_S_XF2: return K_S_F2; + case K_S_XF3: return K_S_F3; + case K_S_XF4: return K_S_F4; } return key; } @@ -476,16 +475,20 @@ char_u *get_special_key_name(int c, int modifiers) string[idx++] = *s++; } } - } else { /* use name of special key */ - STRCPY(string + idx, key_names_table[table_idx].name); - idx = (int)STRLEN(string); + } else { // use name of special key + size_t len = STRLEN(key_names_table[table_idx].name); + + if ((int)len + idx + 2 <= MAX_KEY_NAME_LEN) { + STRCPY(string + idx, key_names_table[table_idx].name); + idx += (int)len; + } } string[idx++] = '>'; string[idx] = NUL; return string; } -/// Try translating a <> name +/// Try translating a <> name ("keycode"). /// /// @param[in,out] srcp Source from which <> are translated. Is advanced to /// after the <> name if there is a match. @@ -510,7 +513,7 @@ unsigned int trans_special(const char_u **srcp, const size_t src_len, return 0; } - /* Put the appropriate modifier in a string */ + // Put the appropriate modifier in a string. if (modifiers != 0) { dst[dlen++] = K_SPECIAL; dst[dlen++] = KS_MODIFIER; @@ -571,15 +574,11 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp, // Find end of modifier list last_dash = src; - for (bp = src + 1; bp <= end && (*bp == '-' || vim_isIDc(*bp)); bp++) { + for (bp = src + 1; bp <= end && (*bp == '-' || ascii_isident(*bp)); bp++) { if (*bp == '-') { last_dash = bp; if (bp + 1 <= end) { - if (has_mbyte) { - l = mb_ptr2len_len(bp + 1, (int) (end - bp) + 1); - } else { - l = 1; - } + l = utfc_ptr2len_len(bp + 1, (int)(end - bp) + 1); // Anything accepted, like <C-?>. // <C-"> or <M-"> are not special in strings as " is // the string delimiter. With a backslash it works: <M-\"> @@ -627,9 +626,11 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp, // Modifier with single letter, or special key name. if (in_string && last_dash[1] == '\\' && last_dash[2] == '"') { - off = 2; + // Special case for a double-quoted string + off = l = 2; + } else { + l = mb_ptr2len(last_dash + 1); } - l = mb_ptr2len(last_dash + 1); if (modifiers != 0 && last_dash[l + 1] == '>') { key = PTR2CHAR(last_dash + off); } else { @@ -704,33 +705,39 @@ int find_special_key_in_table(int c) { int i; - for (i = 0; key_names_table[i].name != NULL; i++) - if (c == key_names_table[i].key) + for (i = 0; key_names_table[i].name != NULL; i++) { + if (c == key_names_table[i].key) { break; - if (key_names_table[i].name == NULL) + } + } + if (key_names_table[i].name == NULL) { i = -1; + } return i; } -/* - * Find the special key with the given name (the given string does not have to - * end with NUL, the name is assumed to end before the first non-idchar). - * If the name starts with "t_" the next two characters are interpreted as a - * termcap name. - * Return the key code, or 0 if not found. - */ +/// Find the special key with the given name +/// +/// @param[in] name Name of the special. Does not have to end with NUL, it is +/// assumed to end before the first non-idchar. If name starts +/// with "t_" the next two characters are interpreted as +/// a termcap name. +/// +/// @return Key code or 0 if not found. int get_special_key_code(const char_u *name) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - char_u *table_name; - int i, j; - - for (i = 0; key_names_table[i].name != NULL; i++) { - table_name = key_names_table[i].name; - for (j = 0; vim_isIDc(name[j]) && table_name[j] != NUL; j++) - if (TOLOWER_ASC(table_name[j]) != TOLOWER_ASC(name[j])) + for (int i = 0; key_names_table[i].name != NULL; i++) { + const char *const table_name = key_names_table[i].name; + int j; + for (j = 0; ascii_isident(name[j]) && table_name[j] != NUL; j++) { + if (TOLOWER_ASC(table_name[j]) != TOLOWER_ASC(name[j])) { break; - if (!vim_isIDc(name[j]) && table_name[j] == NUL) + } + } + if (!ascii_isident(name[j]) && table_name[j] == NUL) { return key_names_table[i].key; + } } return 0; @@ -756,9 +763,9 @@ int get_mouse_button(int code, bool *is_click, bool *is_drag) /// Replace any terminal code strings with the equivalent internal /// representation /// -/// This is used for the "from" and "to" part of a mapping, and the "to" part of +/// Used for the "from" and "to" part of a mapping, and the "to" part of /// a menu command. Any strings like "<C-UP>" are also replaced, unless -/// 'cpoptions' contains '<'. K_SPECIAL by itself is replaced by K_SPECIAL +/// `special` is false. K_SPECIAL by itself is replaced by K_SPECIAL /// KS_SPECIAL KE_FILLER. /// /// @param[in] from What characters to replace. @@ -771,7 +778,7 @@ int get_mouse_button(int code, bool *is_click, bool *is_drag) /// When cpo_flags contains #FLAG_CPO_BSLASH, a backslash /// can be used in place of <C-v>. All other <C-v> /// characters are removed. -/// @param[in] special If true, always accept <key> notation. +/// @param[in] special Replace keycodes, e.g. <CR> becomes a "\n" char. /// @param[in] cpo_flags Relevant flags derived from p_cpo, see /// #CPO_TO_CPO_FLAGS. /// @@ -790,11 +797,9 @@ char_u *replace_termcodes(const char_u *from, const size_t from_len, const char_u *src; const char_u *const end = from + from_len - 1; int do_backslash; // backslash is a special character - int do_special; // recognize <> key codes char_u *result; // buffer for resulting string do_backslash = !(cpo_flags&FLAG_CPO_BSLASH); - do_special = !(cpo_flags&FLAG_CPO_SPECI) || special; // Allocate space for the translation. Worst case a single character is // replaced by 6 bytes (shifted special key), plus a NUL at the end. @@ -817,10 +822,9 @@ char_u *replace_termcodes(const char_u *from, const size_t from_len, // Copy each byte from *from to result[dlen] while (src <= end) { - // If 'cpoptions' does not contain '<', check for special key codes, - // like "<C-S-LeftMouse>" - if (do_special && (do_lt || ((end - src) >= 3 - && STRNCMP(src, "<lt>", 4) != 0))) { + // Check for special <> keycodes, like "<C-S-LeftMouse>" + if (special && (do_lt || ((end - src) >= 3 + && STRNCMP(src, "<lt>", 4) != 0))) { // Replace <SID> by K_SNR <script-nr> _. // (room: 5 * 6 = 30 bytes; needed: 3 + <nr> + 1 <= 14) if (end - src >= 4 && STRNICMP(src, "<SID>", 5) == 0) { @@ -839,14 +843,14 @@ char_u *replace_termcodes(const char_u *from, const size_t from_len, } slen = trans_special(&src, (size_t)(end - src) + 1, result + dlen, true, - true); + false); if (slen) { dlen += slen; continue; } } - if (do_special) { + if (special) { char_u *p, *s, len; // Replace <Leader> by the value of "mapleader". diff --git a/src/nvim/keymap.h b/src/nvim/keymap.h index bb8ba84a6a..00e9cf6ed3 100644 --- a/src/nvim/keymap.h +++ b/src/nvim/keymap.h @@ -243,6 +243,7 @@ enum key_extra { , KE_EVENT // event , KE_PASTE // special key to toggle the 'paste' option. // sent only by UIs + , KE_COMMAND // special key to execute command in any mode }; /* @@ -428,11 +429,10 @@ enum key_extra { #define K_CMDWIN TERMCAP2KEY(KS_EXTRA, KE_CMDWIN) #define K_DROP TERMCAP2KEY(KS_EXTRA, KE_DROP) -#define K_FOCUSGAINED TERMCAP2KEY(KS_EXTRA, KE_FOCUSGAINED) -#define K_FOCUSLOST TERMCAP2KEY(KS_EXTRA, KE_FOCUSLOST) #define K_EVENT TERMCAP2KEY(KS_EXTRA, KE_EVENT) #define K_PASTE TERMCAP2KEY(KS_EXTRA, KE_PASTE) +#define K_COMMAND TERMCAP2KEY(KS_EXTRA, KE_COMMAND) /* Bits for modifier mask */ /* 0x01 cannot be used, because the modifier must be 0x02 or higher */ @@ -450,9 +450,10 @@ enum key_extra { /* * The length of the longest special key name, including modifiers. - * Current longest is <M-C-S-T-4-MiddleRelease> (length includes '<' and '>'). + * Current longest is <M-C-S-T-D-A-4-ScrollWheelRight> (length includes '<' and + * '>'). */ -#define MAX_KEY_NAME_LEN 25 +#define MAX_KEY_NAME_LEN 32 // Maximum length of a special key event as tokens. This includes modifiers. // The longest event is something like <M-C-S-T-4-LeftDrag> which would be the @@ -464,13 +465,9 @@ enum key_extra { #define MAX_KEY_CODE_LEN 6 #define FLAG_CPO_BSLASH 0x01 -#define FLAG_CPO_SPECI 0x02 -#define CPO_TO_CPO_FLAGS (((vim_strchr(p_cpo, CPO_BSLASH) == NULL) \ - ? 0 \ - : FLAG_CPO_BSLASH)| \ - (vim_strchr(p_cpo, CPO_SPECI) == NULL \ - ? 0 \ - : FLAG_CPO_SPECI)) +#define CPO_TO_CPO_FLAGS ((vim_strchr(p_cpo, CPO_BSLASH) == NULL) \ + ? 0 \ + : FLAG_CPO_BSLASH) #ifdef INCLUDE_GENERATED_DECLARATIONS # include "keymap.h.generated.h" diff --git a/src/nvim/lib/kbtree.h b/src/nvim/lib/kbtree.h index a3943054e6..e2688064a8 100644 --- a/src/nvim/lib/kbtree.h +++ b/src/nvim/lib/kbtree.h @@ -390,34 +390,14 @@ #define KBTREE_INIT(name, key_t, __cmp, T) \ KBTREE_INIT_IMPL(name, key_t, kbnode_##name##_t, __cmp, T, (sizeof(kbnode_##name##_t)+(2*T)*sizeof(void *))) -#if (!defined(__clang__) && !defined(__INTEL_COMPILER)) && (__GNUC__ > 4 ) - -// The index trickery shouldn't be UB anymore, -// still it is to much for gcc:s -Werror=array-bounds -# define __KB_PRAGMA_START \ - _Pragma("GCC diagnostic push") \ - _Pragma("GCC diagnostic ignored \"-Warray-bounds\"") - -# define __KB_PRAGMA_END \ - _Pragma("GCC diagnostic pop") \ - -#else - -# define __KB_PRAGMA_START -# define __KB_PRAGMA_END - -#endif - #define KBTREE_INIT_IMPL(name, key_t, kbnode_t, __cmp, T, ILEN) \ - __KB_PRAGMA_START \ __KB_TREE_T(name, key_t, T) \ __KB_GET_AUX1(name, key_t, kbnode_t, __cmp) \ __KB_GET(name, key_t, kbnode_t) \ __KB_INTERVAL(name, key_t, kbnode_t) \ __KB_PUT(name, key_t, kbnode_t, __cmp, T, ILEN) \ __KB_DEL(name, key_t, kbnode_t, T) \ - __KB_ITR(name, key_t, kbnode_t) \ - __KB_PRAGMA_END + __KB_ITR(name, key_t, kbnode_t) #define KB_DEFAULT_SIZE 512 diff --git a/src/nvim/lib/kvec.h b/src/nvim/lib/kvec.h index 584282d773..ad56c9237b 100644 --- a/src/nvim/lib/kvec.h +++ b/src/nvim/lib/kvec.h @@ -41,6 +41,7 @@ #include <string.h> #include "nvim/memory.h" +#include "nvim/os/os_defs.h" #define kv_roundup32(x) \ ((--(x)), \ @@ -62,7 +63,16 @@ #define kv_pop(v) ((v).items[--(v).size]) #define kv_size(v) ((v).size) #define kv_max(v) ((v).capacity) -#define kv_last(v) kv_A(v, kv_size(v) - 1) +#define kv_Z(v, i) kv_A(v, kv_size(v) - (i) - 1) +#define kv_last(v) kv_Z(v, 0) + +/// Drop last n items from kvec without resizing +/// +/// Previously spelled as `(void)kv_pop(v)`, repeated n times. +/// +/// @param[out] v Kvec to drop items from. +/// @param[in] n Number of elements to drop. +#define kv_drop(v, n) ((v).size -= (n)) #define kv_resize(v, s) \ ((v).capacity = (s), \ diff --git a/src/nvim/lib/ringbuf.h b/src/nvim/lib/ringbuf.h index 12b75ec65a..e63eae70b0 100644 --- a/src/nvim/lib/ringbuf.h +++ b/src/nvim/lib/ringbuf.h @@ -15,6 +15,7 @@ #ifndef NVIM_LIB_RINGBUF_H #define NVIM_LIB_RINGBUF_H +#include <stddef.h> #include <string.h> #include <assert.h> #include <stdint.h> @@ -73,6 +74,32 @@ typedef struct { \ RBType *buf_end; \ } TypeName##RingBuffer; +/// Dummy item free macros, for use in RINGBUF_INIT +/// +/// This macros actually does nothing. +/// +/// @param[in] item Item to be freed. +#define RINGBUF_DUMMY_FREE(item) + +/// Static ring buffer +/// +/// @warning Ring buffers created with this macros must neither be freed nor +/// deallocated. +/// +/// @param scope Ring buffer scope. +/// @param TypeName Ring buffer type name. +/// @param RBType Type of the single ring buffer element. +/// @param varname Variable name. +/// @param rbsize Ring buffer size. +#define RINGBUF_STATIC(scope, TypeName, RBType, varname, rbsize) \ +static RBType _##varname##_buf[rbsize]; \ +scope TypeName##RingBuffer varname = { \ + .buf = _##varname##_buf, \ + .next = _##varname##_buf, \ + .first = NULL, \ + .buf_end = _##varname##_buf + rbsize - 1, \ +}; + /// Initialize a new ring buffer /// /// @param TypeName Ring buffer type name. Actual type name will be diff --git a/src/nvim/log.c b/src/nvim/log.c index f1dbe61dda..7bfe5c4089 100644 --- a/src/nvim/log.c +++ b/src/nvim/log.c @@ -25,6 +25,10 @@ static uv_mutex_t mutex; # include "log.c.generated.h" #endif +#ifdef HAVE_EXECINFO_BACKTRACE +# include <execinfo.h> +#endif + static bool log_try_create(char *fname) { if (fname == NULL || fname[0] == '\0') { @@ -91,8 +95,12 @@ void log_unlock(void) uv_mutex_unlock(&mutex); } -bool do_log(int log_level, const char *func_name, int line_num, bool eol, - const char* fmt, ...) FUNC_ATTR_UNUSED +/// @param context description of a shared context or subsystem +/// @param func_name function name, or NULL +/// @param line_num source line number, or -1 +bool do_log(int log_level, const char *context, const char *func_name, + int line_num, bool eol, const char *fmt, ...) + FUNC_ATTR_UNUSED { if (log_level < MIN_LOG_LEVEL) { return false; @@ -108,8 +116,8 @@ bool do_log(int log_level, const char *func_name, int line_num, bool eol, va_list args; va_start(args, fmt); - ret = v_do_log_to_file(log_file, log_level, func_name, line_num, eol, - fmt, args); + ret = v_do_log_to_file(log_file, log_level, context, func_name, line_num, + eol, fmt, args); va_end(args); if (log_file != stderr && log_file != stdout) { @@ -147,7 +155,7 @@ FILE *open_log_file(void) static bool opening_log_file = false; // check if it's a recursive call if (opening_log_file) { - do_log_to_file(stderr, ERROR_LOG_LEVEL, __func__, __LINE__, true, + do_log_to_file(stderr, ERROR_LOG_LEVEL, NULL, __func__, __LINE__, true, "Cannot LOG() recursively."); return stderr; } @@ -167,33 +175,87 @@ FILE *open_log_file(void) // - LOG() is called before early_init() // - Directory does not exist // - File is not writable - do_log_to_file(stderr, ERROR_LOG_LEVEL, __func__, __LINE__, true, + do_log_to_file(stderr, ERROR_LOG_LEVEL, NULL, __func__, __LINE__, true, "Logging to stderr, failed to open $" LOG_FILE_ENV ": %s", log_file_path); return stderr; } -static bool do_log_to_file(FILE *log_file, int log_level, +#ifdef HAVE_EXECINFO_BACKTRACE +void log_callstack_to_file(FILE *log_file, const char *const func_name, + const int line_num) +{ + void *trace[100]; + int trace_size = backtrace(trace, ARRAY_SIZE(trace)); + + char exepath[MAXPATHL] = { 0 }; + size_t exepathlen = MAXPATHL; + if (os_exepath(exepath, &exepathlen) != 0) { + abort(); + } + assert(24 + exepathlen < IOSIZE); // Must fit in `cmdbuf` below. + + char cmdbuf[IOSIZE + (20 * ARRAY_SIZE(trace))]; + snprintf(cmdbuf, sizeof(cmdbuf), "addr2line -e %s -f -p", exepath); + for (int i = 1; i < trace_size; i++) { + char buf[20]; // 64-bit pointer 0xNNNNNNNNNNNNNNNN with leading space. + snprintf(buf, sizeof(buf), " %p", trace[i]); + xstrlcat(cmdbuf, buf, sizeof(cmdbuf)); + } + // Now we have a command string like: + // addr2line -e /path/to/exe -f -p 0x123 0x456 ... + + do_log_to_file(log_file, DEBUG_LOG_LEVEL, NULL, func_name, line_num, true, + "trace:"); + FILE *fp = popen(cmdbuf, "r"); + char linebuf[IOSIZE]; + while (fgets(linebuf, sizeof(linebuf) - 1, fp) != NULL) { + fprintf(log_file, " %s", linebuf); + } + pclose(fp); + + if (log_file != stderr && log_file != stdout) { + fclose(log_file); + } +} + +void log_callstack(const char *const func_name, const int line_num) +{ + log_lock(); + FILE *log_file = open_log_file(); + if (log_file == NULL) { + goto end; + } + + log_callstack_to_file(log_file, func_name, line_num); + +end: + log_unlock(); +} +#endif + +static bool do_log_to_file(FILE *log_file, int log_level, const char *context, const char *func_name, int line_num, bool eol, const char* fmt, ...) { va_list args; va_start(args, fmt); - bool ret = v_do_log_to_file(log_file, log_level, func_name, line_num, eol, - fmt, args); + bool ret = v_do_log_to_file(log_file, log_level, context, func_name, + line_num, eol, fmt, args); va_end(args); return ret; } static bool v_do_log_to_file(FILE *log_file, int log_level, - const char *func_name, int line_num, bool eol, - const char* fmt, va_list args) + const char *context, const char *func_name, + int line_num, bool eol, const char *fmt, + va_list args) { static const char *log_levels[] = { [DEBUG_LOG_LEVEL] = "DEBUG", [INFO_LOG_LEVEL] = "INFO ", - [WARNING_LOG_LEVEL] = "WARN ", + [WARN_LOG_LEVEL] = "WARN ", [ERROR_LOG_LEVEL] = "ERROR", }; assert(log_level >= DEBUG_LOG_LEVEL && log_level <= ERROR_LOG_LEVEL); @@ -211,8 +273,15 @@ static bool v_do_log_to_file(FILE *log_file, int log_level, // print the log message prefixed by the current timestamp and pid int64_t pid = os_get_pid(); - if (fprintf(log_file, "%s %s %" PRId64 "/%s:%d: ", date_time, - log_levels[log_level], pid, func_name, line_num) < 0) { + int rv = (line_num == -1 || func_name == NULL) + ? fprintf(log_file, "%s %s %" PRId64 " %s", date_time, + log_levels[log_level], pid, + (context == NULL ? "?:" : context)) + : fprintf(log_file, "%s %s %" PRId64 " %s%s:%d: ", date_time, + log_levels[log_level], pid, + (context == NULL ? "" : context), + func_name, line_num); + if (rv < 0) { return false; } if (vfprintf(log_file, fmt, args) < 0) { diff --git a/src/nvim/log.h b/src/nvim/log.h index 221f0bbaf6..f378b92039 100644 --- a/src/nvim/log.h +++ b/src/nvim/log.h @@ -6,7 +6,7 @@ #define DEBUG_LOG_LEVEL 0 #define INFO_LOG_LEVEL 1 -#define WARNING_LOG_LEVEL 2 +#define WARN_LOG_LEVEL 2 #define ERROR_LOG_LEVEL 3 #define DLOG(...) @@ -22,45 +22,50 @@ # define MIN_LOG_LEVEL INFO_LOG_LEVEL #endif -#define LOG(level, ...) do_log((level), __func__, __LINE__, true, \ +#define LOG(level, ...) do_log((level), NULL, __func__, __LINE__, true, \ __VA_ARGS__) #if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL # undef DLOG # undef DLOGN -# define DLOG(...) do_log(DEBUG_LOG_LEVEL, __func__, __LINE__, true, \ +# define DLOG(...) do_log(DEBUG_LOG_LEVEL, NULL, __func__, __LINE__, true, \ __VA_ARGS__) -# define DLOGN(...) do_log(DEBUG_LOG_LEVEL, __func__, __LINE__, false, \ +# define DLOGN(...) do_log(DEBUG_LOG_LEVEL, NULL, __func__, __LINE__, false, \ __VA_ARGS__) #endif #if MIN_LOG_LEVEL <= INFO_LOG_LEVEL # undef ILOG # undef ILOGN -# define ILOG(...) do_log(INFO_LOG_LEVEL, __func__, __LINE__, true, \ +# define ILOG(...) do_log(INFO_LOG_LEVEL, NULL, __func__, __LINE__, true, \ __VA_ARGS__) -# define ILOGN(...) do_log(INFO_LOG_LEVEL, __func__, __LINE__, false, \ +# define ILOGN(...) do_log(INFO_LOG_LEVEL, NULL, __func__, __LINE__, false, \ __VA_ARGS__) #endif -#if MIN_LOG_LEVEL <= WARNING_LOG_LEVEL +#if MIN_LOG_LEVEL <= WARN_LOG_LEVEL # undef WLOG # undef WLOGN -# define WLOG(...) do_log(WARNING_LOG_LEVEL, __func__, __LINE__, true, \ +# define WLOG(...) do_log(WARN_LOG_LEVEL, NULL, __func__, __LINE__, true, \ __VA_ARGS__) -# define WLOGN(...) do_log(WARNING_LOG_LEVEL, __func__, __LINE__, false, \ +# define WLOGN(...) do_log(WARN_LOG_LEVEL, NULL, __func__, __LINE__, false, \ __VA_ARGS__) #endif #if MIN_LOG_LEVEL <= ERROR_LOG_LEVEL # undef ELOG # undef ELOGN -# define ELOG(...) do_log(ERROR_LOG_LEVEL, __func__, __LINE__, true, \ +# define ELOG(...) do_log(ERROR_LOG_LEVEL, NULL, __func__, __LINE__, true, \ __VA_ARGS__) -# define ELOGN(...) do_log(ERROR_LOG_LEVEL, __func__, __LINE__, false, \ +# define ELOGN(...) do_log(ERROR_LOG_LEVEL, NULL, __func__, __LINE__, false, \ __VA_ARGS__) #endif +#ifdef HAVE_EXECINFO_BACKTRACE +# define LOG_CALLSTACK() log_callstack(__func__, __LINE__) +# define LOG_CALLSTACK_TO_FILE(fp) log_callstack_to_file(fp, __func__, __LINE__) +#endif + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "log.h.generated.h" #endif diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index cacba3ce87..5da6d2c0a0 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -211,20 +211,28 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) size_t len; const char *s = lua_tolstring(lstate, -2, &len); if (cur.special) { - list_T *const kv_pair = tv_list_alloc(); - tv_list_append_list(cur.tv->vval.v_list, kv_pair); - listitem_T *const key = tv_list_item_alloc(); - key->li_tv = decode_string(s, len, kTrue, false, false); - tv_list_append(kv_pair, key); - if (key->li_tv.v_type == VAR_UNKNOWN) { + list_T *const kv_pair = tv_list_alloc(2); + + typval_T s_tv = decode_string(s, len, kTrue, false, false); + if (s_tv.v_type == VAR_UNKNOWN) { ret = false; tv_list_unref(kv_pair); continue; } - listitem_T *const val = tv_list_item_alloc(); - tv_list_append(kv_pair, val); + tv_list_append_owned_tv(kv_pair, s_tv); + + // Value: not populated yet, need to create list item to push. + tv_list_append_owned_tv(kv_pair, (typval_T) { + .v_type = VAR_UNKNOWN, + }); kv_push(stack, cur); - cur = (TVPopStackItem) { &val->li_tv, false, false, 0 }; + tv_list_append_list(cur.tv->vval.v_list, kv_pair); + cur = (TVPopStackItem) { + .tv = TV_LIST_ITEM_TV(tv_list_last(kv_pair)), + .container = false, + .special = false, + .idx = 0, + }; } else { dictitem_T *const di = tv_dict_item_alloc_len(s, len); if (tv_dict_add(cur.tv->vval.v_dict, di) == FAIL) { @@ -239,15 +247,23 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) } } else { assert(cur.tv->v_type == VAR_LIST); - lua_rawgeti(lstate, -1, cur.tv->vval.v_list->lv_len + 1); + lua_rawgeti(lstate, -1, tv_list_len(cur.tv->vval.v_list) + 1); if (lua_isnil(lstate, -1)) { lua_pop(lstate, 2); continue; } - listitem_T *const li = tv_list_item_alloc(); - tv_list_append(cur.tv->vval.v_list, li); + // Not populated yet, need to create list item to push. + tv_list_append_owned_tv(cur.tv->vval.v_list, (typval_T) { + .v_type = VAR_UNKNOWN, + }); kv_push(stack, cur); - cur = (TVPopStackItem) { &li->li_tv, false, false, 0 }; + // TODO(ZyX-I): Use indexes, here list item *will* be reallocated. + cur = (TVPopStackItem) { + .tv = TV_LIST_ITEM_TV(tv_list_last(cur.tv->vval.v_list)), + .container = false, + .special = false, + .idx = 0, + }; } } assert(!cur.container); @@ -305,8 +321,8 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) switch (table_props.type) { case kObjectTypeArray: { cur.tv->v_type = VAR_LIST; - cur.tv->vval.v_list = tv_list_alloc(); - cur.tv->vval.v_list->lv_refcount++; + cur.tv->vval.v_list = tv_list_alloc((ptrdiff_t)table_props.maxidx); + tv_list_ref(cur.tv->vval.v_list); if (table_props.maxidx != 0) { cur.container = true; cur.idx = lua_gettop(lstate); @@ -322,7 +338,8 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) } else { cur.special = table_props.has_string_with_nul; if (table_props.has_string_with_nul) { - decode_create_map_special_dict(cur.tv); + decode_create_map_special_dict( + cur.tv, (ptrdiff_t)table_props.string_keys_num); assert(cur.tv->v_type == VAR_DICT); dictitem_T *const val_di = tv_dict_find(cur.tv->vval.v_dict, S_LEN("_VAL")); @@ -406,7 +423,7 @@ nlua_pop_typval_table_processing_end: #define TYPVAL_ENCODE_CONV_STR_STRING TYPVAL_ENCODE_CONV_STRING #define TYPVAL_ENCODE_CONV_EXT_STRING(tv, str, len, type) \ - TYPVAL_ENCODE_CONV_NIL() + TYPVAL_ENCODE_CONV_NIL(tv) #define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \ do { \ diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 9ec5bfb8ad..02eabb9c89 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -40,66 +40,6 @@ typedef struct { /// Name of the run code for use in messages #define NLUA_EVAL_NAME "<VimL compiled string>" -/// Call C function which does not expect any arguments -/// -/// @param function Called function -/// @param numret Number of returned arguments -#define NLUA_CALL_C_FUNCTION_0(lstate, function, numret) \ - do { \ - lua_pushcfunction(lstate, &function); \ - lua_call(lstate, 0, numret); \ - } while (0) -/// Call C function which expects one argument -/// -/// @param function Called function -/// @param numret Number of returned arguments -/// @param a… Supplied argument (should be a void* pointer) -#define NLUA_CALL_C_FUNCTION_1(lstate, function, numret, a1) \ - do { \ - lua_pushcfunction(lstate, &function); \ - lua_pushlightuserdata(lstate, a1); \ - lua_call(lstate, 1, numret); \ - } while (0) -/// Call C function which expects two arguments -/// -/// @param function Called function -/// @param numret Number of returned arguments -/// @param a… Supplied argument (should be a void* pointer) -#define NLUA_CALL_C_FUNCTION_2(lstate, function, numret, a1, a2) \ - do { \ - lua_pushcfunction(lstate, &function); \ - lua_pushlightuserdata(lstate, a1); \ - lua_pushlightuserdata(lstate, a2); \ - lua_call(lstate, 2, numret); \ - } while (0) -/// Call C function which expects three arguments -/// -/// @param function Called function -/// @param numret Number of returned arguments -/// @param a… Supplied argument (should be a void* pointer) -#define NLUA_CALL_C_FUNCTION_3(lstate, function, numret, a1, a2, a3) \ - do { \ - lua_pushcfunction(lstate, &function); \ - lua_pushlightuserdata(lstate, a1); \ - lua_pushlightuserdata(lstate, a2); \ - lua_pushlightuserdata(lstate, a3); \ - lua_call(lstate, 3, numret); \ - } while (0) -/// Call C function which expects five arguments -/// -/// @param function Called function -/// @param numret Number of returned arguments -/// @param a… Supplied argument (should be a void* pointer) -#define NLUA_CALL_C_FUNCTION_4(lstate, function, numret, a1, a2, a3, a4) \ - do { \ - lua_pushcfunction(lstate, &function); \ - lua_pushlightuserdata(lstate, a1); \ - lua_pushlightuserdata(lstate, a2); \ - lua_pushlightuserdata(lstate, a3); \ - lua_pushlightuserdata(lstate, a4); \ - lua_call(lstate, 4, numret); \ - } while (0) - /// Convert lua error into a Vim error message /// /// @param lstate Lua interpreter state. @@ -124,140 +64,50 @@ static void nlua_error(lua_State *const lstate, const char *const msg) /// omitted. static int nlua_stricmp(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL { - const char *s1 = luaL_checklstring(lstate, 1, NULL); - const char *s2 = luaL_checklstring(lstate, 2, NULL); - const int ret = STRICMP(s1, s2); + size_t s1_len; + size_t s2_len; + const char *s1 = luaL_checklstring(lstate, 1, &s1_len); + const char *s2 = luaL_checklstring(lstate, 2, &s2_len); + char *nul1; + char *nul2; + int ret = 0; + assert(s1[s1_len] == NUL); + assert(s2[s2_len] == NUL); + do { + nul1 = memchr(s1, NUL, s1_len); + nul2 = memchr(s2, NUL, s2_len); + ret = STRICMP(s1, s2); + if (ret == 0) { + // Compare "a\0" greater then "a". + if ((nul1 == NULL) != (nul2 == NULL)) { + ret = ((nul1 != NULL) - (nul2 != NULL)); + break; + } + if (nul1 != NULL) { + assert(nul2 != NULL); + // Can't shift both strings by the same amount of bytes: lowercase + // letter may have different byte-length than uppercase. + s1_len -= (size_t)(nul1 - s1) + 1; + s2_len -= (size_t)(nul2 - s2) + 1; + s1 = nul1 + 1; + s2 = nul2 + 1; + } else { + break; + } + } else { + break; + } + } while (true); lua_pop(lstate, 2); lua_pushnumber(lstate, (lua_Number)((ret > 0) - (ret < 0))); return 1; } -/// Evaluate lua string -/// -/// Expects two values on the stack: string to evaluate, pointer to the -/// location where result is saved. Always returns nothing (from the lua point -/// of view). -static int nlua_exec_lua_string(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL -{ - const String *const str = (const String *)lua_touserdata(lstate, 1); - typval_T *const ret_tv = (typval_T *)lua_touserdata(lstate, 2); - lua_pop(lstate, 2); - - if (luaL_loadbuffer(lstate, str->data, str->size, NLUA_EVAL_NAME)) { - nlua_error(lstate, _("E5104: Error while creating lua chunk: %.*s")); - return 0; - } - if (lua_pcall(lstate, 0, 1, 0)) { - nlua_error(lstate, _("E5105: Error while calling lua chunk: %.*s")); - return 0; - } - if (!nlua_pop_typval(lstate, ret_tv)) { - return 0; - } - return 0; -} - -/// Evaluate lua string for each line in range -/// -/// Expects two values on the stack: string to evaluate and pointer to integer -/// array with line range. Always returns nothing (from the lua point of view). -static int nlua_exec_luado_string(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL -{ - const String *const str = (const String *)lua_touserdata(lstate, 1); - const linenr_T *const range = (const linenr_T *)lua_touserdata(lstate, 2); - lua_pop(lstate, 2); - -#define DOSTART "return function(line, linenr) " -#define DOEND " end" - const size_t lcmd_len = (str->size - + (sizeof(DOSTART) - 1) - + (sizeof(DOEND) - 1)); - char *lcmd; - if (lcmd_len < IOSIZE) { - lcmd = (char *)IObuff; - } else { - lcmd = xmalloc(lcmd_len + 1); - } - memcpy(lcmd, DOSTART, sizeof(DOSTART) - 1); - memcpy(lcmd + sizeof(DOSTART) - 1, str->data, str->size); - memcpy(lcmd + sizeof(DOSTART) - 1 + str->size, DOEND, sizeof(DOEND) - 1); -#undef DOSTART -#undef DOEND - - if (luaL_loadbuffer(lstate, lcmd, lcmd_len, NLUA_EVAL_NAME)) { - nlua_error(lstate, _("E5109: Error while creating lua chunk: %.*s")); - if (lcmd_len >= IOSIZE) { - xfree(lcmd); - } - return 0; - } - if (lcmd_len >= IOSIZE) { - xfree(lcmd); - } - if (lua_pcall(lstate, 0, 1, 0)) { - nlua_error(lstate, _("E5110: Error while creating lua function: %.*s")); - return 0; - } - for (linenr_T l = range[0]; l <= range[1]; l++) { - if (l > curbuf->b_ml.ml_line_count) { - break; - } - lua_pushvalue(lstate, -1); - lua_pushstring(lstate, (const char *)ml_get_buf(curbuf, l, false)); - lua_pushnumber(lstate, (lua_Number)l); - if (lua_pcall(lstate, 2, 1, 0)) { - nlua_error(lstate, _("E5111: Error while calling lua function: %.*s")); - break; - } - if (lua_isstring(lstate, -1)) { - size_t new_line_len; - const char *const new_line = lua_tolstring(lstate, -1, &new_line_len); - char *const new_line_transformed = xmemdupz(new_line, new_line_len); - for (size_t i = 0; i < new_line_len; i++) { - if (new_line_transformed[i] == NUL) { - new_line_transformed[i] = '\n'; - } - } - ml_replace(l, (char_u *)new_line_transformed, false); - changed_bytes(l, 0); - } - lua_pop(lstate, 1); - } - lua_pop(lstate, 1); - check_cursor(); - update_screen(NOT_VALID); - return 0; -} - -/// Evaluate lua file -/// -/// Expects one value on the stack: file to evaluate. Always returns nothing -/// (from the lua point of view). -static int nlua_exec_lua_file(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL -{ - const char *const filename = (const char *)lua_touserdata(lstate, 1); - lua_pop(lstate, 1); - - if (luaL_loadfile(lstate, filename)) { - nlua_error(lstate, _("E5112: Error while creating lua chunk: %.*s")); - return 0; - } - if (lua_pcall(lstate, 0, 0, 0)) { - nlua_error(lstate, _("E5113: Error while calling lua chunk: %.*s")); - return 0; - } - return 0; -} - /// Initialize lua interpreter state /// /// Called by lua interpreter itself to initialize state. static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL { - // stricmp - lua_pushcfunction(lstate, &nlua_stricmp); - lua_setglobal(lstate, "stricmp"); - // print lua_pushcfunction(lstate, &nlua_print); lua_setglobal(lstate, "print"); @@ -277,13 +127,17 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL nlua_add_api_functions(lstate); // vim.types, vim.type_idx, vim.val_idx nlua_init_types(lstate); + // stricmp + lua_pushcfunction(lstate, &nlua_stricmp); + lua_setfield(lstate, -2, "stricmp"); + lua_setglobal(lstate, "vim"); return 0; } /// Initialize lua interpreter /// -/// Crashes NeoVim if initialization fails. Should be called once per lua +/// Crashes Nvim if initialization fails. Should be called once per lua /// interpreter instance. /// /// @return New lua interpreter instance. @@ -296,7 +150,7 @@ static lua_State *nlua_init(void) preserve_exit(); } luaL_openlibs(lstate); - NLUA_CALL_C_FUNCTION_0(lstate, nlua_state_init, 0); + nlua_state_init(lstate); return lstate; } @@ -305,8 +159,8 @@ static lua_State *nlua_init(void) /// Calls nlua_init() if needed. Is responsible for pre-lua call initalization /// like updating `package.[c]path` with directories derived from &runtimepath. /// -/// @return Interprter instance to use. Will either be initialized now or taken -/// from previous initalization. +/// @return Interpreter instance to use. Will either be initialized now or +/// taken from previous initialization. static lua_State *nlua_enter(void) FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT { @@ -347,108 +201,18 @@ static lua_State *nlua_enter(void) void executor_exec_lua(const String str, typval_T *const ret_tv) FUNC_ATTR_NONNULL_ALL { - NLUA_CALL_C_FUNCTION_2(nlua_enter(), nlua_exec_lua_string, 0, - (void *)&str, ret_tv); -} - -/// Evaluate lua string -/// -/// Used for luaeval(). Expects three values on the stack: -/// -/// 1. String to evaluate. -/// 2. _A value. -/// 3. Pointer to location where result is saved. -/// -/// @param[in,out] lstate Lua interpreter state. -static int nlua_eval_lua_string(lua_State *const lstate) - FUNC_ATTR_NONNULL_ALL -{ - const String *const str = (const String *)lua_touserdata(lstate, 1); - typval_T *const arg = (typval_T *)lua_touserdata(lstate, 2); - typval_T *const ret_tv = (typval_T *)lua_touserdata(lstate, 3); - lua_pop(lstate, 3); + lua_State *const lstate = nlua_enter(); - garray_T str_ga; - ga_init(&str_ga, 1, 80); -#define EVALHEADER "local _A=select(1,...) return (" - const size_t lcmd_len = sizeof(EVALHEADER) - 1 + str->size + 1; - char *lcmd; - if (lcmd_len < IOSIZE) { - lcmd = (char *)IObuff; - } else { - lcmd = xmalloc(lcmd_len); - } - memcpy(lcmd, EVALHEADER, sizeof(EVALHEADER) - 1); - memcpy(lcmd + sizeof(EVALHEADER) - 1, str->data, str->size); - lcmd[lcmd_len - 1] = ')'; -#undef EVALHEADER - if (luaL_loadbuffer(lstate, lcmd, lcmd_len, NLUA_EVAL_NAME)) { - nlua_error(lstate, - _("E5107: Error while creating lua chunk for luaeval(): %.*s")); - if (lcmd != (char *)IObuff) { - xfree(lcmd); - } - return 0; - } - if (lcmd != (char *)IObuff) { - xfree(lcmd); - } - - if (arg == NULL || arg->v_type == VAR_UNKNOWN) { - lua_pushnil(lstate); - } else { - nlua_push_typval(lstate, arg); - } - if (lua_pcall(lstate, 1, 1, 0)) { - nlua_error(lstate, - _("E5108: Error while calling lua chunk for luaeval(): %.*s")); - return 0; - } - if (!nlua_pop_typval(lstate, ret_tv)) { - return 0; - } - - return 0; -} - -/// Evaluate lua string -/// -/// Expects four values on the stack: string to evaluate, pointer to args array, -/// and locations where result and error are saved, respectively. Always -/// returns nothing (from the lua point of view). -static int nlua_exec_lua_string_api(lua_State *const lstate) - FUNC_ATTR_NONNULL_ALL -{ - const String *str = (const String *)lua_touserdata(lstate, 1); - const Array *args = (const Array *)lua_touserdata(lstate, 2); - Object *retval = (Object *)lua_touserdata(lstate, 3); - Error *err = (Error *)lua_touserdata(lstate, 4); - - lua_pop(lstate, 4); - - if (luaL_loadbuffer(lstate, str->data, str->size, "<nvim>")) { - size_t len; - const char *str = lua_tolstring(lstate, -1, &len); - api_set_error(err, kErrorTypeValidation, - "Error loading lua: %.*s", (int)len, str); - return 0; - } - - for (size_t i = 0; i < args->size; i++) { - nlua_push_Object(lstate, args->items[i]); + if (luaL_loadbuffer(lstate, str.data, str.size, NLUA_EVAL_NAME)) { + nlua_error(lstate, _("E5104: Error while creating lua chunk: %.*s")); + return; } - - if (lua_pcall(lstate, (int)args->size, 1, 0)) { - size_t len; - const char *str = lua_tolstring(lstate, -1, &len); - api_set_error(err, kErrorTypeException, - "Error executing lua: %.*s", (int)len, str); - return 0; + if (lua_pcall(lstate, 0, 1, 0)) { + nlua_error(lstate, _("E5105: Error while calling lua chunk: %.*s")); + return; } - *retval = nlua_pop_Object(lstate, err); - - return 0; + nlua_pop_typval(lstate, ret_tv); } /// Print as a Vim message @@ -519,7 +283,7 @@ static int nlua_print(lua_State *const lstate) } msg((char_u *)str + start); } - if (str[len - 1] == NUL) { // Last was newline + if (len && str[len - 1] == NUL) { // Last was newline msg((char_u *)""); } } @@ -586,8 +350,46 @@ void executor_eval_lua(const String str, typval_T *const arg, typval_T *const ret_tv) FUNC_ATTR_NONNULL_ALL { - NLUA_CALL_C_FUNCTION_3(nlua_enter(), nlua_eval_lua_string, 0, - (void *)&str, arg, ret_tv); + lua_State *const lstate = nlua_enter(); + + garray_T str_ga; + ga_init(&str_ga, 1, 80); +#define EVALHEADER "local _A=select(1,...) return (" + const size_t lcmd_len = sizeof(EVALHEADER) - 1 + str.size + 1; + char *lcmd; + if (lcmd_len < IOSIZE) { + lcmd = (char *)IObuff; + } else { + lcmd = xmalloc(lcmd_len); + } + memcpy(lcmd, EVALHEADER, sizeof(EVALHEADER) - 1); + memcpy(lcmd + sizeof(EVALHEADER) - 1, str.data, str.size); + lcmd[lcmd_len - 1] = ')'; +#undef EVALHEADER + if (luaL_loadbuffer(lstate, lcmd, lcmd_len, NLUA_EVAL_NAME)) { + nlua_error(lstate, + _("E5107: Error while creating lua chunk for luaeval(): %.*s")); + if (lcmd != (char *)IObuff) { + xfree(lcmd); + } + return; + } + if (lcmd != (char *)IObuff) { + xfree(lcmd); + } + + if (arg->v_type == VAR_UNKNOWN) { + lua_pushnil(lstate); + } else { + nlua_push_typval(lstate, arg); + } + if (lua_pcall(lstate, 1, 1, 0)) { + nlua_error(lstate, + _("E5108: Error while calling lua chunk for luaeval(): %.*s")); + return; + } + + nlua_pop_typval(lstate, ret_tv); } /// Execute lua string @@ -601,10 +403,29 @@ void executor_eval_lua(const String str, typval_T *const arg, /// @return Return value of the execution. Object executor_exec_lua_api(const String str, const Array args, Error *err) { - Object retval = NIL; - NLUA_CALL_C_FUNCTION_4(nlua_enter(), nlua_exec_lua_string_api, 0, - (void *)&str, (void *)&args, &retval, err); - return retval; + lua_State *const lstate = nlua_enter(); + + if (luaL_loadbuffer(lstate, str.data, str.size, "<nvim>")) { + size_t len; + const char *errstr = lua_tolstring(lstate, -1, &len); + api_set_error(err, kErrorTypeValidation, + "Error loading lua: %.*s", (int)len, errstr); + return NIL; + } + + for (size_t i = 0; i < args.size; i++) { + nlua_push_Object(lstate, args.items[i]); + } + + if (lua_pcall(lstate, (int)args.size, 1, 0)) { + size_t len; + const char *errstr = lua_tolstring(lstate, -1, &len); + api_set_error(err, kErrorTypeException, + "Error executing lua: %.*s", (int)len, errstr); + return NIL; + } + + return nlua_pop_Object(lstate, err); } @@ -640,13 +461,70 @@ void ex_luado(exarg_T *const eap) EMSG(_("cannot save undo information")); return; } - const String cmd = { - .size = STRLEN(eap->arg), - .data = (char *)eap->arg, - }; - const linenr_T range[] = { eap->line1, eap->line2 }; - NLUA_CALL_C_FUNCTION_2(nlua_enter(), nlua_exec_luado_string, 0, - (void *)&cmd, (void *)range); + const char *const cmd = (const char *)eap->arg; + const size_t cmd_len = strlen(cmd); + + lua_State *const lstate = nlua_enter(); + +#define DOSTART "return function(line, linenr) " +#define DOEND " end" + const size_t lcmd_len = (cmd_len + + (sizeof(DOSTART) - 1) + + (sizeof(DOEND) - 1)); + char *lcmd; + if (lcmd_len < IOSIZE) { + lcmd = (char *)IObuff; + } else { + lcmd = xmalloc(lcmd_len + 1); + } + memcpy(lcmd, DOSTART, sizeof(DOSTART) - 1); + memcpy(lcmd + sizeof(DOSTART) - 1, cmd, cmd_len); + memcpy(lcmd + sizeof(DOSTART) - 1 + cmd_len, DOEND, sizeof(DOEND) - 1); +#undef DOSTART +#undef DOEND + + if (luaL_loadbuffer(lstate, lcmd, lcmd_len, NLUA_EVAL_NAME)) { + nlua_error(lstate, _("E5109: Error while creating lua chunk: %.*s")); + if (lcmd_len >= IOSIZE) { + xfree(lcmd); + } + return; + } + if (lcmd_len >= IOSIZE) { + xfree(lcmd); + } + if (lua_pcall(lstate, 0, 1, 0)) { + nlua_error(lstate, _("E5110: Error while creating lua function: %.*s")); + return; + } + for (linenr_T l = eap->line1; l <= eap->line2; l++) { + if (l > curbuf->b_ml.ml_line_count) { + break; + } + lua_pushvalue(lstate, -1); + lua_pushstring(lstate, (const char *)ml_get_buf(curbuf, l, false)); + lua_pushnumber(lstate, (lua_Number)l); + if (lua_pcall(lstate, 2, 1, 0)) { + nlua_error(lstate, _("E5111: Error while calling lua function: %.*s")); + break; + } + if (lua_isstring(lstate, -1)) { + size_t new_line_len; + const char *const new_line = lua_tolstring(lstate, -1, &new_line_len); + char *const new_line_transformed = xmemdupz(new_line, new_line_len); + for (size_t i = 0; i < new_line_len; i++) { + if (new_line_transformed[i] == NUL) { + new_line_transformed[i] = '\n'; + } + } + ml_replace(l, (char_u *)new_line_transformed, false); + changed_bytes(l, 0); + } + lua_pop(lstate, 1); + } + lua_pop(lstate, 1); + check_cursor(); + update_screen(NOT_VALID); } /// Run lua file @@ -657,6 +535,15 @@ void ex_luado(exarg_T *const eap) void ex_luafile(exarg_T *const eap) FUNC_ATTR_NONNULL_ALL { - NLUA_CALL_C_FUNCTION_1(nlua_enter(), nlua_exec_lua_file, 0, - (void *)eap->arg); + lua_State *const lstate = nlua_enter(); + + if (luaL_loadfile(lstate, (const char *)eap->arg)) { + nlua_error(lstate, _("E5112: Error while creating lua chunk: %.*s")); + return; + } + + if (lua_pcall(lstate, 0, 0, 0)) { + nlua_error(lstate, _("E5113: Error while calling lua chunk: %.*s")); + return; + } } diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index c7952520b0..e1bbb03d3f 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -1,3 +1,62 @@ +-- Internal-only until comments in #8107 are addressed. +-- Returns: +-- {errcode}, {output} +local function _system(cmd) + local out = vim.api.nvim_call_function('system', { cmd }) + local err = vim.api.nvim_get_vvar('shell_error') + return err, out +end + +-- Gets process info from the `ps` command. +-- Used by nvim_get_proc() as a fallback. +local function _os_proc_info(pid) + if pid == nil or pid <= 0 or type(pid) ~= 'number' then + error('invalid pid') + end + local cmd = { 'ps', '-p', pid, '-o', 'ucomm=', } + local err, name = _system(cmd) + if 1 == err and string.gsub(name, '%s*', '') == '' then + return {} -- Process not found. + elseif 0 ~= err then + local args_str = vim.api.nvim_call_function('string', { cmd }) + error('command failed: '..args_str) + end + local _, ppid = _system({ 'ps', '-p', pid, '-o', 'ppid=', }) + -- Remove trailing whitespace. + name = string.gsub(name, '%s+$', '') + ppid = string.gsub(ppid, '%s+$', '') + ppid = tonumber(ppid) == nil and -1 or tonumber(ppid) + return { + name = name, + pid = pid, + ppid = ppid, + } +end + +-- Gets process children from the `pgrep` command. +-- Used by nvim_get_proc_children() as a fallback. +local function _os_proc_children(ppid) + if ppid == nil or ppid <= 0 or type(ppid) ~= 'number' then + error('invalid ppid') + end + local cmd = { 'pgrep', '-P', ppid, } + local err, rv = _system(cmd) + if 1 == err and string.gsub(rv, '%s*', '') == '' then + return {} -- Process not found. + elseif 0 ~= err then + local args_str = vim.api.nvim_call_function('string', { cmd }) + error('command failed: '..args_str) + end + local children = {} + for s in string.gmatch(rv, '%S+') do + local i = tonumber(s) + if i ~= nil then + table.insert(children, i) + end + end + return children +end + -- TODO(ZyX-I): Create compatibility layer. --{{{1 package.path updater function -- Last inserted paths. Used to clear out items from package.[c]path when they @@ -58,7 +117,12 @@ local function _update_package_paths() end last_nvim_paths = cur_nvim_paths end ---{{{1 Module definition -return { + +local module = { _update_package_paths = _update_package_paths, + _os_proc_children = _os_proc_children, + _os_proc_info = _os_proc_info, + _system = _system, } + +return module diff --git a/src/nvim/macros.h b/src/nvim/macros.h index 26d4f74b6a..4e01265498 100644 --- a/src/nvim/macros.h +++ b/src/nvim/macros.h @@ -28,15 +28,11 @@ /// @return `s, sizeof(s) - 1` #define S_LEN(s) (s), (sizeof(s) - 1) -/* - * lineempty() - return TRUE if the line is empty - */ -#define lineempty(p) (*ml_get(p) == NUL) +/// LINEEMPTY() - return TRUE if the line is empty +#define LINEEMPTY(p) (*ml_get(p) == NUL) -/* - * bufempty() - return TRUE if the current buffer is empty - */ -#define bufempty() (curbuf->b_ml.ml_line_count == 1 && *ml_get((linenr_T)1) == \ +/// BUFEMPTY() - return TRUE if the current buffer is empty +#define BUFEMPTY() (curbuf->b_ml.ml_line_count == 1 && *ml_get((linenr_T)1) == \ NUL) /* @@ -75,7 +71,7 @@ do { \ if (*p_langmap \ && (condition) \ - && (p_lrm || KeyTyped) \ + && (p_lrm || (vgetc_busy ? typebuf_maplen() == 0 : KeyTyped)) \ && !KeyStuffed \ && (c) >= 0) \ { \ @@ -148,7 +144,8 @@ /// zero in those cases (-Wdiv-by-zero in GCC). #define ARRAY_SIZE(arr) ((sizeof(arr)/sizeof((arr)[0])) / ((size_t)(!(sizeof(arr) % sizeof((arr)[0]))))) -#define RGB(r, g, b) ((r << 16) | (g << 8) | b) +// Duplicated in os/win_defs.h to avoid include-order sensitivity. +#define RGB_(r, g, b) ((r << 16) | (g << 8) | b) #define STR_(x) #x #define STR(x) STR_(x) @@ -183,4 +180,19 @@ /// @return ((Type *)obj). #define STRUCT_CAST(Type, obj) ((Type *)(obj)) +// Type of uv_buf_t.len is platform-dependent. +// Related: https://github.com/libuv/libuv/pull/1236 +#if defined(WIN32) +# define UV_BUF_LEN(x) (ULONG)(x) +#else +# define UV_BUF_LEN(x) (x) +#endif + +// Type of read()/write() `count` param is platform-dependent. +#if defined(WIN32) +# define IO_COUNT(x) (unsigned)(x) +#else +# define IO_COUNT(x) (x) +#endif + #endif // NVIM_MACROS_H diff --git a/src/nvim/main.c b/src/nvim/main.c index 19a661d7db..4288d7f9d7 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -57,6 +57,7 @@ #include "nvim/os/input.h" #include "nvim/os/os.h" #include "nvim/os/time.h" +#include "nvim/os/fileio.h" #include "nvim/event/loop.h" #include "nvim/os/signal.h" #include "nvim/event/process.h" @@ -67,6 +68,9 @@ #include "nvim/api/private/helpers.h" #include "nvim/api/private/handle.h" #include "nvim/api/private/dispatch.h" +#ifndef WIN32 +# include "nvim/os/pty_process_unix.h" +#endif /* Maximum number of commands from + or -c arguments. */ #define MAX_ARG_CMDS 10 @@ -97,10 +101,7 @@ typedef struct { bool input_isatty; // stdin is a terminal bool output_isatty; // stdout is a terminal bool err_isatty; // stderr is a terminal - bool headless; // Dont try to start an user interface - // or read/write to stdio(unless - // embedding) - int no_swap_file; /* "-n" argument used */ + int no_swap_file; // "-n" argument used int use_debug_break_level; int window_count; /* number of windows to use */ int window_layout; /* 0, WIN_HOR, WIN_VER or WIN_TABS */ @@ -216,10 +217,22 @@ void early_init(void) #ifdef MAKE_LIB int nvim_main(int argc, char **argv) +#elif defined(WIN32) +int wmain(int argc, wchar_t **argv_w) // multibyte args on Windows. #7060 #else int main(int argc, char **argv) #endif { +#if defined(WIN32) && !defined(MAKE_LIB) + char **argv = xmalloc((size_t)argc * sizeof(char *)); + for (int i = 0; i < argc; i++) { + char *buf = NULL; + utf16_to_utf8(argv_w[i], &buf); + assert(buf); + argv[i] = buf; + } +#endif + argv0 = argv[0]; char_u *fname = NULL; // file name from command line @@ -282,8 +295,8 @@ int main(int argc, char **argv) assert(p_ch >= 0 && Rows >= p_ch && Rows - p_ch <= INT_MAX); cmdline_row = (int)(Rows - p_ch); msg_row = cmdline_row; - screenalloc(false); /* allocate screen buffers */ - set_init_2(params.headless); + screenalloc(false); // allocate screen buffers + set_init_2(headless_mode); TIME_MSG("inits 2"); msg_scroll = TRUE; @@ -295,8 +308,9 @@ int main(int argc, char **argv) /* Set the break level after the terminal is initialized. */ debug_break_level = params.use_debug_break_level; - bool reading_input = !params.headless && (params.input_isatty - || params.output_isatty || params.err_isatty); + bool reading_input = !headless_mode + && (params.input_isatty || params.output_isatty + || params.err_isatty); if (reading_input) { // One of the startup commands (arguments, sourced scripts or plugins) may @@ -393,7 +407,7 @@ int main(int argc, char **argv) } // It's better to make v:oldfiles an empty list than NULL. if (get_vim_var_list(VV_OLDFILES) == NULL) { - set_vim_var_list(VV_OLDFILES, tv_list_alloc()); + set_vim_var_list(VV_OLDFILES, tv_list_alloc(0)); } /* @@ -432,7 +446,7 @@ int main(int argc, char **argv) wait_return(TRUE); } - if (!params.headless) { + if (!headless_mode) { // Stop reading from input stream, the UI layer will take over now. input_stop(); ui_builtin_start(); @@ -552,11 +566,15 @@ int main(int argc, char **argv) */ normal_enter(false, false); +#if defined(WIN32) && !defined(MAKE_LIB) + xfree(argv); +#endif return 0; } -/* Exit properly */ +/// Exit properly void getout(int exitval) + FUNC_ATTR_NORETURN { tabpage_T *tp, *next_tp; @@ -633,6 +651,11 @@ void getout(int exitval) /* Position the cursor again, the autocommands may have moved it */ ui_cursor_goto((int)Rows - 1, 0); + // Apply 'titleold'. + if (p_title && *p_titleold != NUL) { + ui_call_set_title(cstr_as_string((char *)p_titleold)); + } + #if defined(USE_ICONV) && defined(DYNAMIC_ICONV) iconv_end(); #endif @@ -766,23 +789,36 @@ static void command_line_scan(mparm_T *parmp) version(); mch_exit(0); } else if (STRICMP(argv[0] + argv_idx, "api-info") == 0) { - msgpack_sbuffer* b = msgpack_sbuffer_new(); - msgpack_packer* p = msgpack_packer_new(b, msgpack_sbuffer_write); - Object md = DICTIONARY_OBJ(api_metadata()); - msgpack_rpc_from_object(md, p); + FileDescriptor fp; + const int fof_ret = file_open_fd(&fp, STDOUT_FILENO, true); + msgpack_packer *p = msgpack_packer_new(&fp, msgpack_file_write); + + if (fof_ret != 0) { + emsgf(_("E5421: Failed to open stdin: %s"), os_strerror(fof_ret)); + } - for (size_t i = 0; i < b->size; i++) { - putchar(b->data[i]); + if (p == NULL) { + emsgf(_(e_outofmem)); } + Object md = DICTIONARY_OBJ(api_metadata()); + msgpack_rpc_from_object(md, p); + msgpack_packer_free(p); + const int ff_ret = file_flush(&fp); + if (ff_ret < 0) { + msgpack_file_write_error(ff_ret); + } mch_exit(0); } else if (STRICMP(argv[0] + argv_idx, "headless") == 0) { - parmp->headless = true; + headless_mode = true; } else if (STRICMP(argv[0] + argv_idx, "embed") == 0) { embedded_mode = true; - parmp->headless = true; - channel_from_stdio(); + headless_mode = true; + const char *err; + if (!channel_from_stdio(true, CALLBACK_READER_INIT, &err)) { + abort(); + } } else if (STRNICMP(argv[0] + argv_idx, "literal", 7) == 0) { #if !defined(UNIX) parmp->literal = TRUE; @@ -921,10 +957,11 @@ static void command_line_scan(mparm_T *parmp) break; case 's': - if (exmode_active) /* "-s" silent (batch) mode */ - silent_mode = TRUE; - else /* "-s {scriptin}" read from script file */ - want_argument = TRUE; + if (exmode_active) { // "-es" silent (batch) mode + silent_mode = true; + } else { // "-s {scriptin}" read from script file + want_argument = true; + } break; case 't': /* "-t {tag}" or "-t{tag}" jump to tag */ @@ -1184,7 +1221,6 @@ static void init_params(mparm_T *paramp, int argc, char **argv) memset(paramp, 0, sizeof(*paramp)); paramp->argc = argc; paramp->argv = argv; - paramp->headless = false; paramp->want_full_screen = true; paramp->use_debug_break_level = -1; paramp->window_count = -1; @@ -1208,9 +1244,19 @@ static void init_startuptime(mparm_T *paramp) static void check_and_set_isatty(mparm_T *paramp) { - paramp->input_isatty = os_isatty(fileno(stdin)); - paramp->output_isatty = os_isatty(fileno(stdout)); + stdin_isatty + = paramp->input_isatty = os_isatty(fileno(stdin)); + stdout_isatty + = paramp->output_isatty = os_isatty(fileno(stdout)); paramp->err_isatty = os_isatty(fileno(stderr)); +#ifndef WIN32 + int tty_fd = paramp->input_isatty + ? STDIN_FILENO + : (paramp->output_isatty + ? STDOUT_FILENO + : (paramp->err_isatty ? STDERR_FILENO : -1)); + pty_process_save_termios(tty_fd); +#endif TIME_MSG("window checked"); } @@ -1281,10 +1327,29 @@ static void set_window_layout(mparm_T *paramp) static void load_plugins(void) { if (p_lpl) { - source_runtime((char_u *)"plugin/**/*.vim", DIP_ALL | DIP_NOAFTER); // NOLINT + char_u *rtp_copy = NULL; + + // First add all package directories to 'runtimepath', so that their + // autoload directories can be found. Only if not done already with a + // :packloadall command. + // Make a copy of 'runtimepath', so that source_runtime does not use the + // pack directories. + if (!did_source_packages) { + rtp_copy = vim_strsave(p_rtp); + add_pack_start_dirs(); + } + + source_in_path(rtp_copy == NULL ? p_rtp : rtp_copy, + (char_u *)"plugin/**/*.vim", // NOLINT + DIP_ALL | DIP_NOAFTER); TIME_MSG("loading plugins"); + xfree(rtp_copy); - ex_packloadall(NULL); + // Only source "start" packages if not done already with a :packloadall + // command. + if (!did_source_packages) { + load_start_packages(); + } TIME_MSG("loading packages"); source_runtime((char_u *)"plugin/**/*.vim", DIP_ALL | DIP_AFTER); @@ -1303,7 +1368,7 @@ static void handle_quickfix(mparm_T *paramp) set_string_option_direct((char_u *)"ef", -1, paramp->use_ef, OPT_FREE, SID_CARG); vim_snprintf((char *)IObuff, IOSIZE, "cfile %s", p_ef); - if (qf_init(NULL, p_ef, p_efm, true, IObuff) < 0) { + if (qf_init(NULL, p_ef, p_efm, true, IObuff, p_menc) < 0) { ui_linefeed(); mch_exit(3); } @@ -1334,7 +1399,7 @@ static void handle_tag(char_u *tagname) // When starting in Ex mode and commands come from a file, set Silent mode. static void check_tty(mparm_T *parmp) { - if (parmp->headless) { + if (headless_mode) { return; } @@ -1815,6 +1880,7 @@ static int process_env(char *env, bool is_viminit) /// os_fileinfo_link() respectively for extra security. static bool file_owned(const char *fname) { + assert(fname != NULL); uid_t uid = getuid(); FileInfo file_info; bool file_owned = os_fileinfo(fname, &file_info) @@ -1867,54 +1933,47 @@ static void usage(void) signal_stop(); // kill us with CTRL-C here, if you like mch_msg(_("Usage:\n")); - mch_msg(_(" nvim [arguments] [file ...] Edit specified file(s)\n")); - mch_msg(_(" nvim [arguments] - Read text from stdin\n")); - mch_msg(_(" nvim [arguments] -t <tag> Edit file where tag is defined\n")); - mch_msg(_(" nvim [arguments] -q [errorfile] Edit file with first error\n")); - mch_msg(_("\nArguments:\n")); + mch_msg(_(" nvim [options] [file ...] Edit file(s)\n")); + mch_msg(_(" nvim [options] - Read text from stdin\n")); + mch_msg(_(" nvim [options] -t <tag> Edit file where tag is defined\n")); + mch_msg(_(" nvim [options] -q [errorfile] Edit file with first error\n")); + mch_msg(_("\nOptions:\n")); mch_msg(_(" -- Only file names after this\n")); -#if !defined(UNIX) - mch_msg(_(" --literal Don't expand wildcards\n")); -#endif - mch_msg(_(" -e Ex mode\n")); - mch_msg(_(" -E Improved Ex mode\n")); - mch_msg(_(" -s Silent (batch) mode (only for ex mode)\n")); + mch_msg(_(" + Start at end of file\n")); + mch_msg(_(" --cmd <cmd> Execute <cmd> before any config\n")); + mch_msg(_(" +<cmd>, -c <cmd> Execute <cmd> after config and first file\n")); + mch_msg("\n"); + mch_msg(_(" -b Binary mode\n")); mch_msg(_(" -d Diff mode\n")); - mch_msg(_(" -R Read-only mode\n")); - mch_msg(_(" -Z Restricted mode\n")); + mch_msg(_(" -e, -E Ex mode, Improved Ex mode\n")); + mch_msg(_(" -es Silent (batch) mode\n")); + mch_msg(_(" -h, --help Print this help message\n")); + mch_msg(_(" -i <shada> Use this shada file\n")); mch_msg(_(" -m Modifications (writing files) not allowed\n")); mch_msg(_(" -M Modifications in text not allowed\n")); - mch_msg(_(" -b Binary mode\n")); - mch_msg(_(" -l Lisp mode\n")); - mch_msg(_(" -A Arabic mode\n")); - mch_msg(_(" -F Farsi mode\n")); - mch_msg(_(" -H Hebrew mode\n")); - mch_msg(_(" -V[N][file] Be verbose [level N][log messages to file]\n")); - mch_msg(_(" -D Debugging mode\n")); mch_msg(_(" -n No swap file, use memory only\n")); - mch_msg(_(" -r, -L List swap files and exit\n")); - mch_msg(_(" -r <file> Recover crashed session\n")); - mch_msg(_(" -u <vimrc> Use <vimrc> instead of the default\n")); - mch_msg(_(" -i <shada> Use <shada> instead of the default\n")); - mch_msg(_(" --noplugin Don't load plugin scripts\n")); - mch_msg(_(" -o[N] Open N windows (default: one for each file)\n")); - mch_msg(_(" -O[N] Like -o but split vertically\n")); - mch_msg(_(" -p[N] Open N tab pages (default: one for each file)\n")); - mch_msg(_(" + Start at end of file\n")); - mch_msg(_(" +<linenum> Start at line <linenum>\n")); - mch_msg(_(" +/<pattern> Start at first occurrence of <pattern>\n")); - mch_msg(_(" --cmd <command> Execute <command> before loading any vimrc\n")); - mch_msg(_(" -c <command> Execute <command> after loading the first file\n")); + mch_msg(_(" -o[N] Open N windows (default: one per file)\n")); + mch_msg(_(" -O[N] Open N vertical windows (default: one per file)\n")); + mch_msg(_(" -p[N] Open N tab pages (default: one per file)\n")); + mch_msg(_(" -r, -L List swap files\n")); + mch_msg(_(" -r <file> Recover edit state for this file\n")); + mch_msg(_(" -R Read-only mode\n")); mch_msg(_(" -S <session> Source <session> after loading the first file\n")); mch_msg(_(" -s <scriptin> Read Normal mode commands from <scriptin>\n")); - mch_msg(_(" -w <scriptout> Append all typed characters to <scriptout>\n")); - mch_msg(_(" -W <scriptout> Write all typed characters to <scriptout>\n")); - mch_msg(_(" --startuptime <file> Write startup timing messages to <file>\n")); - mch_msg(_(" --api-info Dump API metadata serialized to msgpack and exit\n")); + mch_msg(_(" -u <config> Use this config file\n")); + mch_msg(_(" -v, --version Print version information\n")); + mch_msg(_(" -V[N][file] Verbose [level][file]\n")); + mch_msg(_(" -Z Restricted mode\n")); + mch_msg("\n"); + mch_msg(_(" --api-info Write msgpack-encoded API metadata to stdout\n")); mch_msg(_(" --embed Use stdin/stdout as a msgpack-rpc channel\n")); mch_msg(_(" --headless Don't start a user interface\n")); - mch_msg(_(" -v, --version Print version information and exit\n")); - mch_msg(_(" -h, --help Print this help message and exit\n")); +#if !defined(UNIX) + mch_msg(_(" --literal Don't expand wildcards\n")); +#endif + mch_msg(_(" --noplugin Don't load plugins\n")); + mch_msg(_(" --startuptime <file> Write startup timing messages to <file>\n")); + mch_msg(_("\nSee \":help startup-options\" for all options.\n")); } diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 7889fabd45..3cd26a5bf7 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -171,6 +171,10 @@ void setpcmark(void) curwin->w_prev_pcmark = curwin->w_pcmark; curwin->w_pcmark = curwin->w_cursor; + if (curwin->w_pcmark.lnum == 0) { + curwin->w_pcmark.lnum = 1; + } + /* If jumplist is full: remove oldest entry */ if (++curwin->w_jumplistlen > JUMPLISTSIZE) { curwin->w_jumplistlen = JUMPLISTSIZE; @@ -951,11 +955,17 @@ static void mark_adjust_internal(linenr_T line1, linenr_T line2, one_adjust_nodel(&(curbuf->b_visual.vi_start.lnum)); one_adjust_nodel(&(curbuf->b_visual.vi_end.lnum)); - /* quickfix marks */ - qf_mark_adjust(NULL, line1, line2, amount, amount_after); - /* location lists */ + // quickfix marks + if (!qf_mark_adjust(NULL, line1, line2, amount, amount_after)) { + curbuf->b_has_qf_entry &= ~BUF_HAS_QF_ENTRY; + } + // location lists + bool found_one = false; FOR_ALL_TAB_WINDOWS(tab, win) { - qf_mark_adjust(win, line1, line2, amount, amount_after); + found_one |= qf_mark_adjust(win, line1, line2, amount, amount_after); + } + if (!found_one) { + curbuf->b_has_qf_entry &= ~BUF_HAS_LL_ENTRY; } sign_mark_adjust(line1, line2, amount, amount_after); diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index d5907da2ed..008bce6df6 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -72,19 +72,49 @@ struct interval { # include "unicode_tables.generated.h" #endif -/* - * Like utf8len_tab above, but using a zero for illegal lead bytes. - */ -static uint8_t utf8len_tab_zero[256] = -{ - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,0,0, +// To speed up BYTELEN(); keep a lookup table to quickly get the length in +// bytes of a UTF-8 character from the first byte of a UTF-8 string. Bytes +// which are illegal when used as the first byte have a 1. The NUL byte has +// length 1. +const uint8_t utf8len_tab[] = { + // ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 ?A ?B ?C ?D ?E ?F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B? + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C? + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // D? + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // E? + 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1, // F? +}; + +// Like utf8len_tab above, but using a zero for illegal lead bytes. +const uint8_t utf8len_tab_zero[] = { + // ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9 ?A ?B ?C ?D ?E ?F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6? + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7? + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8? + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9? + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A? + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B? + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C? + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // D? + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // E? + 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 0, 0, // F? }; /* @@ -528,45 +558,52 @@ int utf_off2cells(unsigned off, unsigned max_off) return (off + 1 < max_off && ScreenLines[off + 1] == 0) ? 2 : 1; } -/* - * Convert a UTF-8 byte sequence to a wide character. - * If the sequence is illegal or truncated by a NUL the first byte is - * returned. - * Does not include composing characters, of course. - */ -int utf_ptr2char(const char_u *p) +/// Convert a UTF-8 byte sequence to a wide character +/// +/// If the sequence is illegal or truncated by a NUL then the first byte is +/// returned. Does not include composing characters for obvious reasons. +/// +/// @param[in] p String to convert. +/// +/// @return Unicode codepoint or byte value. +int utf_ptr2char(const char_u *const p) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - uint8_t len; - - if (p[0] < 0x80) /* be quick for ASCII */ + if (p[0] < 0x80) { // Be quick for ASCII. return p[0]; + } - len = utf8len_tab_zero[p[0]]; + const uint8_t len = utf8len_tab_zero[p[0]]; if (len > 1 && (p[1] & 0xc0) == 0x80) { - if (len == 2) + if (len == 2) { return ((p[0] & 0x1f) << 6) + (p[1] & 0x3f); + } if ((p[2] & 0xc0) == 0x80) { - if (len == 3) - return ((p[0] & 0x0f) << 12) + ((p[1] & 0x3f) << 6) - + (p[2] & 0x3f); + if (len == 3) { + return (((p[0] & 0x0f) << 12) + ((p[1] & 0x3f) << 6) + + (p[2] & 0x3f)); + } if ((p[3] & 0xc0) == 0x80) { - if (len == 4) - return ((p[0] & 0x07) << 18) + ((p[1] & 0x3f) << 12) - + ((p[2] & 0x3f) << 6) + (p[3] & 0x3f); + if (len == 4) { + return (((p[0] & 0x07) << 18) + ((p[1] & 0x3f) << 12) + + ((p[2] & 0x3f) << 6) + (p[3] & 0x3f)); + } if ((p[4] & 0xc0) == 0x80) { - if (len == 5) - return ((p[0] & 0x03) << 24) + ((p[1] & 0x3f) << 18) - + ((p[2] & 0x3f) << 12) + ((p[3] & 0x3f) << 6) - + (p[4] & 0x3f); - if ((p[5] & 0xc0) == 0x80 && len == 6) - return ((p[0] & 0x01) << 30) + ((p[1] & 0x3f) << 24) - + ((p[2] & 0x3f) << 18) + ((p[3] & 0x3f) << 12) - + ((p[4] & 0x3f) << 6) + (p[5] & 0x3f); + if (len == 5) { + return (((p[0] & 0x03) << 24) + ((p[1] & 0x3f) << 18) + + ((p[2] & 0x3f) << 12) + ((p[3] & 0x3f) << 6) + + (p[4] & 0x3f)); + } + if ((p[5] & 0xc0) == 0x80 && len == 6) { + return (((p[0] & 0x01) << 30) + ((p[1] & 0x3f) << 24) + + ((p[2] & 0x3f) << 18) + ((p[3] & 0x3f) << 12) + + ((p[4] & 0x3f) << 6) + (p[5] & 0x3f)); + } } } } } - /* Illegal value, just return the first byte */ + // Illegal value: just return the first byte. return p[0]; } @@ -767,23 +804,24 @@ int utfc_char2bytes(int off, char_u *buf) return len; } -/* - * Get the length of a UTF-8 byte sequence, not including any following - * composing characters. - * Returns 0 for "". - * Returns 1 for an illegal byte sequence. - */ -int utf_ptr2len(const char_u *p) +/// Get the length of a UTF-8 byte sequence representing a single codepoint +/// +/// @param[in] p UTF-8 string. +/// +/// @return Sequence length, 0 for empty string and 1 for non-UTF-8 byte +/// sequence. +int utf_ptr2len(const char_u *const p) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - int len; - int i; - - if (*p == NUL) + if (*p == NUL) { return 0; - len = utf8len_tab[*p]; - for (i = 1; i < len; ++i) - if ((p[i] & 0xc0) != 0x80) + } + const int len = utf8len_tab[*p]; + for (int i = 1; i < len; i++) { + if ((p[i] & 0xc0) != 0x80) { return 1; + } + } return len; } @@ -824,38 +862,38 @@ int utf_ptr2len_len(const char_u *p, int size) return len; } -/* - * Return the number of bytes the UTF-8 encoding of the character at "p" takes. - * This includes following composing characters. - */ -int utfc_ptr2len(const char_u *p) +/// Return the number of bytes occupied by a UTF-8 character in a string +/// +/// This includes following composing characters. +int utfc_ptr2len(const char_u *const p) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - int len; - int b0 = *p; - int prevlen; + uint8_t b0 = (uint8_t)(*p); - if (b0 == NUL) + if (b0 == NUL) { return 0; - if (b0 < 0x80 && p[1] < 0x80) /* be quick for ASCII */ + } + if (b0 < 0x80 && p[1] < 0x80) { // be quick for ASCII return 1; + } - /* Skip over first UTF-8 char, stopping at a NUL byte. */ - len = utf_ptr2len(p); + // Skip over first UTF-8 char, stopping at a NUL byte. + int len = utf_ptr2len(p); - /* Check for illegal byte. */ - if (len == 1 && b0 >= 0x80) + // Check for illegal byte. + if (len == 1 && b0 >= 0x80) { return 1; + } - /* - * Check for composing characters. We can handle only the first six, but - * skip all of them (otherwise the cursor would get stuck). - */ - prevlen = 0; - for (;; ) { - if (p[len] < 0x80 || !UTF_COMPOSINGLIKE(p + prevlen, p + len)) + // Check for composing characters. We can handle only the first six, but + // skip all of them (otherwise the cursor would get stuck). + int prevlen = 0; + for (;;) { + if (p[len] < 0x80 || !UTF_COMPOSINGLIKE(p + prevlen, p + len)) { return len; + } - /* Skip over composing char */ + // Skip over composing char. prevlen = len; len += utf_ptr2len(p + len); } @@ -913,70 +951,65 @@ int utfc_ptr2len_len(const char_u *p, int size) return len; } -/* - * Return the number of bytes the UTF-8 encoding of character "c" takes. - * This does not include composing characters. - */ -int utf_char2len(int c) +/// Determine how many bytes certain unicode codepoint will occupy +int utf_char2len(const int c) { - if (c < 0x80) + if (c < 0x80) { return 1; - if (c < 0x800) + } else if (c < 0x800) { return 2; - if (c < 0x10000) + } else if (c < 0x10000) { return 3; - if (c < 0x200000) + } else if (c < 0x200000) { return 4; - if (c < 0x4000000) + } else if (c < 0x4000000) { return 5; - return 6; + } else { + return 6; + } } -/* - * Convert Unicode character "c" to UTF-8 string in "buf[]". - * Returns the number of bytes. - * This does not include composing characters. - */ -int utf_char2bytes(int c, char_u *buf) +/// Convert Unicode character to UTF-8 string +/// +/// @param c character to convert to \p buf +/// @param[out] buf UTF-8 string generated from \p c, does not add \0 +/// @return Number of bytes (1-6). Does not include composing characters. +int utf_char2bytes(const int c, char_u *const buf) { - if (c < 0x80) { /* 7 bits */ + if (c < 0x80) { // 7 bits buf[0] = c; return 1; - } - if (c < 0x800) { /* 11 bits */ + } else if (c < 0x800) { // 11 bits buf[0] = 0xc0 + ((unsigned)c >> 6); buf[1] = 0x80 + (c & 0x3f); return 2; - } - if (c < 0x10000) { /* 16 bits */ + } else if (c < 0x10000) { // 16 bits buf[0] = 0xe0 + ((unsigned)c >> 12); buf[1] = 0x80 + (((unsigned)c >> 6) & 0x3f); buf[2] = 0x80 + (c & 0x3f); return 3; - } - if (c < 0x200000) { /* 21 bits */ + } else if (c < 0x200000) { // 21 bits buf[0] = 0xf0 + ((unsigned)c >> 18); buf[1] = 0x80 + (((unsigned)c >> 12) & 0x3f); buf[2] = 0x80 + (((unsigned)c >> 6) & 0x3f); buf[3] = 0x80 + (c & 0x3f); return 4; - } - if (c < 0x4000000) { /* 26 bits */ + } else if (c < 0x4000000) { // 26 bits buf[0] = 0xf8 + ((unsigned)c >> 24); buf[1] = 0x80 + (((unsigned)c >> 18) & 0x3f); buf[2] = 0x80 + (((unsigned)c >> 12) & 0x3f); buf[3] = 0x80 + (((unsigned)c >> 6) & 0x3f); buf[4] = 0x80 + (c & 0x3f); return 5; + } else { // 31 bits + buf[0] = 0xfc + ((unsigned)c >> 30); + buf[1] = 0x80 + (((unsigned)c >> 24) & 0x3f); + buf[2] = 0x80 + (((unsigned)c >> 18) & 0x3f); + buf[3] = 0x80 + (((unsigned)c >> 12) & 0x3f); + buf[4] = 0x80 + (((unsigned)c >> 6) & 0x3f); + buf[5] = 0x80 + (c & 0x3f); + return 6; } - /* 31 bits */ - buf[0] = 0xfc + ((unsigned)c >> 30); - buf[1] = 0x80 + (((unsigned)c >> 24) & 0x3f); - buf[2] = 0x80 + (((unsigned)c >> 18) & 0x3f); - buf[3] = 0x80 + (((unsigned)c >> 12) & 0x3f); - buf[4] = 0x80 + (((unsigned)c >> 6) & 0x3f); - buf[5] = 0x80 + (c & 0x3f); - return 6; } /* @@ -1513,14 +1546,15 @@ int utf_head_off(const char_u *base, const char_u *p) return (int)(p - q); } -/* - * Copy a character from "*fp" to "*tp" and advance the pointers. - */ -void mb_copy_char(const char_u **fp, char_u **tp) +/// Copy a character, advancing the pointers +/// +/// @param[in,out] fp Source of the character to copy. +/// @param[in,out] tp Destination to copy to. +void mb_copy_char(const char_u **const fp, char_u **const tp) { - int l = (*mb_ptr2len)(*fp); + const size_t l = (size_t)utfc_ptr2len(*fp); - memmove(*tp, *fp, (size_t)l); + memmove(*tp, *fp, l); *tp += l; *fp += l; } @@ -1739,52 +1773,55 @@ int mb_charlen_len(char_u *str, int len) return count; } -/* - * Try to un-escape a multi-byte character. - * Used for the "to" and "from" part of a mapping. - * Return the un-escaped string if it is a multi-byte character, and advance - * "pp" to just after the bytes that formed it. - * Return NULL if no multi-byte char was found. - */ -char_u * mb_unescape(char_u **pp) -{ - static char_u buf[6]; - int n; - int m = 0; - char_u *str = *pp; - - /* Must translate K_SPECIAL KS_SPECIAL KE_FILLER to K_SPECIAL and CSI - * KS_EXTRA KE_CSI to CSI. - * Maximum length of a utf-8 character is 4 bytes. */ - for (n = 0; str[n] != NUL && m < 4; ++n) { - if (str[n] == K_SPECIAL - && str[n + 1] == KS_SPECIAL - && str[n + 2] == KE_FILLER) { - buf[m++] = K_SPECIAL; - n += 2; - } else if ((str[n] == K_SPECIAL - ) - && str[n + 1] == KS_EXTRA - && str[n + 2] == (int)KE_CSI) { - buf[m++] = CSI; - n += 2; - } else if (str[n] == K_SPECIAL - ) - break; /* a special key can't be a multibyte char */ - else - buf[m++] = str[n]; - buf[m] = NUL; +/// Try to unescape a multibyte character +/// +/// Used for the rhs and lhs of the mappings. +/// +/// @param[in,out] pp String to unescape. Is advanced to just after the bytes +/// that form a multibyte character. +/// +/// @return Unescaped string if it is a multibyte character, NULL if no +/// multibyte character was found. Returns a static buffer, always one +/// and the same. +const char *mb_unescape(const char **const pp) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + static char buf[6]; + size_t buf_idx = 0; + uint8_t *str = (uint8_t *)(*pp); + + // Must translate K_SPECIAL KS_SPECIAL KE_FILLER to K_SPECIAL and CSI + // KS_EXTRA KE_CSI to CSI. + // Maximum length of a utf-8 character is 4 bytes. + for (size_t str_idx = 0; str[str_idx] != NUL && buf_idx < 4; str_idx++) { + if (str[str_idx] == K_SPECIAL + && str[str_idx + 1] == KS_SPECIAL + && str[str_idx + 2] == KE_FILLER) { + buf[buf_idx++] = (char)K_SPECIAL; + str_idx += 2; + } else if ((str[str_idx] == K_SPECIAL) + && str[str_idx + 1] == KS_EXTRA + && str[str_idx + 2] == KE_CSI) { + buf[buf_idx++] = (char)CSI; + str_idx += 2; + } else if (str[str_idx] == K_SPECIAL) { + break; // A special key can't be a multibyte char. + } else { + buf[buf_idx++] = (char)str[str_idx]; + } + buf[buf_idx] = NUL; - /* Return a multi-byte character if it's found. An illegal sequence - * will result in a 1 here. */ - if ((*mb_ptr2len)(buf) > 1) { - *pp = str + n + 1; + // Return a multi-byte character if it's found. An illegal sequence + // will result in a 1 here. + if (utf_ptr2len((const char_u *)buf) > 1) { + *pp = (const char *)str + str_idx + 1; return buf; } - /* Bail out quickly for ASCII. */ - if (buf[0] < 128) + // Bail out quickly for ASCII. + if ((uint8_t)buf[0] < 128) { break; + } } return NULL; } @@ -2259,9 +2296,7 @@ int convert_setup_ext(vimconv_T *vcp, char_u *from, bool from_unicode_is_utf8, if (vcp->vc_type == CONV_ICONV && vcp->vc_fd != (iconv_t)-1) iconv_close(vcp->vc_fd); # endif - vcp->vc_type = CONV_NONE; - vcp->vc_factor = 1; - vcp->vc_fail = false; + *vcp = (vimconv_T)MBYTE_NONE_CONV; /* No conversion when one of the names is empty or they are equal. */ if (from == NULL || *from == NUL || to == NULL || *to == NUL diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h index ad9e38004c..a5ce1b0a15 100644 --- a/src/nvim/mbyte.h +++ b/src/nvim/mbyte.h @@ -1,6 +1,7 @@ #ifndef NVIM_MBYTE_H #define NVIM_MBYTE_H +#include <stdint.h> #include <stdbool.h> #include <string.h> @@ -18,6 +19,9 @@ #define MB_BYTE2LEN(b) utf8len_tab[b] #define MB_BYTE2LEN_CHECK(b) (((b) < 0 || (b) > 255) ? 1 : utf8len_tab[b]) +// max length of an unicode char +#define MB_MAXCHAR 6 + /* properties used in enc_canon_table[] (first three mutually exclusive) */ #define ENC_8BIT 0x01 #define ENC_DBCS 0x02 @@ -56,6 +60,12 @@ typedef enum { CONV_ICONV = 5, } ConvFlags; +#define MBYTE_NONE_CONV { \ + .vc_type = CONV_NONE, \ + .vc_factor = 1, \ + .vc_fail = false, \ +} + /// Structure used for string conversions typedef struct { int vc_type; ///< Zero or more ConvFlags. @@ -67,6 +77,10 @@ typedef struct { ///< otherwise use '?'. } vimconv_T; +extern const uint8_t utf8len_tab_zero[256]; + +extern const uint8_t utf8len_tab[256]; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "mbyte.h.generated.h" #endif diff --git a/src/nvim/memfile.c b/src/nvim/memfile.c index 9429703620..4eeba12b87 100644 --- a/src/nvim/memfile.c +++ b/src/nvim/memfile.c @@ -376,8 +376,9 @@ void mf_put(memfile_T *mfp, bhdr_T *hp, bool dirty, bool infile) { unsigned flags = hp->bh_flags; - if ((flags & BH_LOCKED) == 0) - EMSG(_("E293: block was not locked")); + if ((flags & BH_LOCKED) == 0) { + IEMSG(_("E293: block was not locked")); + } flags &= ~BH_LOCKED; if (dirty) { flags |= BH_DIRTY; @@ -895,6 +896,7 @@ static bool mf_do_open(memfile_T *mfp, char_u *fname, int flags) { // fname cannot be NameBuff, because it must have been allocated. mf_set_fnames(mfp, fname); + assert(mfp->mf_fname != NULL); /// 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. diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 55e7e01825..c11ca74f5c 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -293,7 +293,7 @@ int ml_open(buf_T *buf) */ hp = mf_new(mfp, false, 1); if (hp->bh_bnum != 0) { - EMSG(_("E298: Didn't get block nr 0?")); + IEMSG(_("E298: Didn't get block nr 0?")); goto error; } b0p = hp->bh_data; @@ -335,7 +335,7 @@ int ml_open(buf_T *buf) if ((hp = ml_new_ptr(mfp)) == NULL) goto error; if (hp->bh_bnum != 1) { - EMSG(_("E298: Didn't get block nr 1?")); + IEMSG(_("E298: Didn't get block nr 1?")); goto error; } pp = hp->bh_data; @@ -351,7 +351,7 @@ int ml_open(buf_T *buf) */ hp = ml_new_data(mfp, FALSE, 1); if (hp->bh_bnum != 2) { - EMSG(_("E298: Didn't get block nr 2?")); + IEMSG(_("E298: Didn't get block nr 2?")); goto error; } @@ -635,13 +635,14 @@ static void ml_upd_block0(buf_T *buf, upd_block0_T what) if (mfp == NULL || (hp = mf_get(mfp, 0, 1)) == NULL) return; b0p = hp->bh_data; - if (ml_check_b0_id(b0p) == FAIL) - EMSG(_("E304: ml_upd_block0(): Didn't get block 0??")); - else { - if (what == UB_FNAME) + if (ml_check_b0_id(b0p) == FAIL) { + IEMSG(_("E304: ml_upd_block0(): Didn't get block 0??")); + } else { + if (what == UB_FNAME) { set_b0_fname(b0p, buf); - else /* what == UB_SAME_DIR */ + } else { // what == UB_SAME_DIR set_b0_dir_flag(b0p, buf); + } } mf_put(mfp, hp, true, false); } @@ -1329,10 +1330,14 @@ recover_names ( names[2] = (char_u *)concat_fnames((char *)dir_name, ".sw?", TRUE); num_names = 3; } else { - p = dir_name + STRLEN(dir_name); - if (after_pathsep((char *)dir_name, (char *)p) && p[-1] == p[-2]) { - /* Ends with '//', Use Full path for swap name */ - tail = (char_u *)make_percent_swname((char *)dir_name, (char *)fname_res); + int len = (int)STRLEN(dir_name); + p = dir_name + len; + if (after_pathsep((char *)dir_name, (char *)p) + && len > 1 + && p[-1] == p[-2]) { + // Ends with '//', Use Full path for swap name + tail = (char_u *)make_percent_swname((char *)dir_name, + (char *)fname_res); } else { tail = path_tail(fname_res); tail = (char_u *)concat_fnames((char *)dir_name, (char *)tail, TRUE); @@ -1460,6 +1465,7 @@ static int process_still_running; */ static time_t swapfile_info(char_u *fname) { + assert(fname != NULL); int fd; struct block0 b0; time_t x = (time_t)0; @@ -1741,11 +1747,11 @@ ml_get_buf ( if (lnum > buf->b_ml.ml_line_count) { /* invalid line number */ if (recursive == 0) { - /* Avoid giving this message for a recursive call, may happen when - * the GUI redraws part of the text. */ - ++recursive; - EMSGN(_("E315: ml_get: invalid lnum: %" PRId64), lnum); - --recursive; + // Avoid giving this message for a recursive call, may happen when + // the GUI redraws part of the text. + recursive++; + IEMSGN(_("E315: ml_get: invalid lnum: %" PRId64), lnum); + recursive--; } errorret: STRCPY(IObuff, "???"); @@ -1773,11 +1779,11 @@ errorret: */ if ((hp = ml_find_line(buf, lnum, ML_FIND)) == NULL) { if (recursive == 0) { - /* Avoid giving this message for a recursive call, may happen - * when the GUI redraws part of the text. */ - ++recursive; - EMSGN(_("E316: ml_get: cannot find line %" PRId64), lnum); - --recursive; + // Avoid giving this message for a recursive call, may happen + // when the GUI redraws part of the text. + recursive++; + IEMSGN(_("E316: ml_get: cannot find line %" PRId64), lnum); + recursive--; } goto errorret; } @@ -2161,7 +2167,7 @@ ml_append_int ( return FAIL; pp = hp->bh_data; /* must be pointer block */ if (pp->pb_id != PTR_ID) { - EMSG(_("E317: pointer block id wrong 3")); + IEMSG(_("E317: pointer block id wrong 3")); mf_put(mfp, hp, false, false); return FAIL; } @@ -2294,8 +2300,8 @@ ml_append_int ( * Safety check: fallen out of for loop? */ if (stack_idx < 0) { - EMSG(_("E318: Updated too many blocks?")); - buf->b_ml.ml_stack_top = 0; /* invalidate stack */ + IEMSG(_("E318: Updated too many blocks?")); + buf->b_ml.ml_stack_top = 0; // invalidate stack } } @@ -2434,7 +2440,7 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, int message) return FAIL; pp = hp->bh_data; /* must be pointer block */ if (pp->pb_id != PTR_ID) { - EMSG(_("E317: pointer block id wrong 4")); + IEMSG(_("E317: pointer block id wrong 4")); mf_put(mfp, hp, false, false); return FAIL; } @@ -2629,9 +2635,9 @@ static void ml_flush_line(buf_T *buf) new_line = buf->b_ml.ml_line_ptr; hp = ml_find_line(buf, lnum, ML_FIND); - if (hp == NULL) - EMSGN(_("E320: Cannot find line %" PRId64), lnum); - else { + if (hp == NULL) { + IEMSGN(_("E320: Cannot find line %" PRId64), lnum); + } else { dp = hp->bh_data; idx = lnum - buf->b_ml.ml_locked_low; start = ((dp->db_index[idx]) & DB_INDEX_MASK); @@ -2840,7 +2846,7 @@ static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action) pp = (PTR_BL *)(dp); /* must be pointer block */ if (pp->pb_id != PTR_ID) { - EMSG(_("E317: pointer block id wrong")); + IEMSG(_("E317: pointer block id wrong")); goto error_block; } @@ -2877,13 +2883,14 @@ static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action) break; } } - if (idx >= (int)pp->pb_count) { /* past the end: something wrong! */ - if (lnum > buf->b_ml.ml_line_count) - EMSGN(_("E322: line number out of range: %" PRId64 " past the end"), - lnum - buf->b_ml.ml_line_count); + if (idx >= (int)pp->pb_count) { // past the end: something wrong! + if (lnum > buf->b_ml.ml_line_count) { + IEMSGN(_("E322: line number out of range: %" PRId64 " past the end"), + lnum - buf->b_ml.ml_line_count); - else - EMSGN(_("E323: line count wrong in block %" PRId64), bnum); + } else { + IEMSGN(_("E323: line count wrong in block %" PRId64), bnum); + } goto error_block; } if (action == ML_DELETE) { @@ -2959,7 +2966,7 @@ static void ml_lineadd(buf_T *buf, int count) pp = hp->bh_data; /* must be pointer block */ if (pp->pb_id != PTR_ID) { mf_put(mfp, hp, false, false); - EMSG(_("E317: pointer block id wrong 2")); + IEMSG(_("E317: pointer block id wrong 2")); break; } pp->pb_pointer[ip->ip_index].pe_line_count += count; @@ -3013,20 +3020,17 @@ int resolve_symlink(const char_u *fname, char_u *buf) } buf[ret] = NUL; - /* - * Check whether the symlink is relative or absolute. - * If it's relative, build a new path based on the directory - * portion of the filename (if any) and the path the symlink - * points to. - */ - if (path_is_absolute_path(buf)) + // Check whether the symlink is relative or absolute. + // If it's relative, build a new path based on the directory + // portion of the filename (if any) and the path the symlink + // points to. + if (path_is_absolute(buf)) { STRCPY(tmp, buf); - else { - char_u *tail; - - tail = path_tail(tmp); - if (STRLEN(tail) + STRLEN(buf) >= MAXPATHL) + } else { + char_u *tail = path_tail(tmp); + if (STRLEN(tail) + STRLEN(buf) >= MAXPATHL) { return FAIL; + } STRCPY(tail, buf); } } @@ -3051,9 +3055,12 @@ char_u *makeswapname(char_u *fname, char_u *ffname, buf_T *buf, char_u *dir_name #ifdef HAVE_READLINK char_u fname_buf[MAXPATHL]; #endif + int len = (int)STRLEN(dir_name); - s = dir_name + STRLEN(dir_name); - if (after_pathsep((char *)dir_name, (char *)s) && s[-1] == s[-2]) { /* Ends with '//', Use Full path */ + s = dir_name + len; + if (after_pathsep((char *)dir_name, (char *)s) + && len > 1 + && s[-1] == s[-2]) { // Ends with '//', Use Full path r = NULL; if ((s = (char_u *)make_percent_swname((char *)dir_name, (char *)fname)) != NULL) { r = (char_u *)modname((char *)s, ".swp", FALSE); @@ -3135,6 +3142,7 @@ attention_message ( char_u *fname /* swap file name */ ) { + assert(buf->b_fname != NULL); time_t x, sx; char *p; diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 74c58fb203..a66ab6a3cc 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -559,6 +559,7 @@ void time_to_bytes(time_t time_, uint8_t buf[8]) #include "nvim/tag.h" #include "nvim/window.h" #include "nvim/os/os.h" +#include "nvim/eval/typval.h" /* * Free everything that we allocated. @@ -585,7 +586,7 @@ void free_all_mem(void) p_ea = false; if (first_tabpage->tp_next != NULL) do_cmdline_cmd("tabonly!"); - if (firstwin != lastwin) + if (!ONE_WINDOW) do_cmdline_cmd("only!"); /* Free all spell info. */ @@ -692,6 +693,7 @@ void free_all_mem(void) free_screenlines(); clear_hl_tables(); + list_free_log(); } #endif diff --git a/src/nvim/menu.c b/src/nvim/menu.c index 7e9e9e9e5c..42417f75d5 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -26,7 +26,7 @@ #include "nvim/state.h" #include "nvim/strings.h" #include "nvim/ui.h" - +#include "nvim/eval/typval.h" #define MENUDEPTH 10 /* maximum depth of menus */ @@ -38,8 +38,8 @@ -/* The character for each menu mode */ -static char_u menu_mode_chars[] = {'n', 'v', 's', 'o', 'i', 'c', 't'}; +/// The character for each menu mode +static char_u menu_mode_chars[] = { 'n', 'v', 's', 'o', 'i', 'c', 't' }; static char_u e_notsubmenu[] = N_( "E327: Part of menu-item path is not sub-menu"); @@ -47,20 +47,16 @@ static char_u e_othermode[] = N_("E328: Menu only exists in another mode"); static char_u e_nomenu[] = N_("E329: No menu \"%s\""); -/* - * Do the :menu command and relatives. - */ -void -ex_menu ( - exarg_T *eap /* Ex command arguments */ -) +/// Do the :menu command and relatives. +/// @param eap Ex command arguments +void +ex_menu(exarg_T *eap) { char_u *menu_path; int modes; - char_u *map_to; + char_u *map_to; // command mapped to the menu entry int noremap; bool silent = false; - bool special = false; int unmenu; char_u *map_buf; char_u *arg; @@ -86,7 +82,7 @@ ex_menu ( continue; } if (STRNCMP(arg, "<special>", 9) == 0) { - special = true; + // Ignore obsolete "<special>" modifier. arg = skipwhite(arg + 9); continue; } @@ -94,7 +90,8 @@ ex_menu ( } - /* Locate an optional "icon=filename" argument. */ + // Locate an optional "icon=filename" argument + // TODO(nvim): Currently this is only parsed. Should expose it to UIs. if (STRNCMP(arg, "icon=", 5) == 0) { arg += 5; while (*arg != NUL && *arg != ' ') { @@ -108,12 +105,12 @@ ex_menu ( } } - /* - * Fill in the priority table. - */ - for (p = arg; *p; ++p) - if (!ascii_isdigit(*p) && *p != '.') + // Fill in the priority table. + for (p = arg; *p; p++) { + if (!ascii_isdigit(*p) && *p != '.') { break; + } + } if (ascii_iswhite(*p)) { for (i = 0; i < MENUDEPTH && !ascii_iswhite(*arg); ++i) { pri_tab[i] = getdigits_long(&arg); @@ -222,13 +219,12 @@ ex_menu ( map_buf = NULL; // Menu tips are plain text. } else { map_to = replace_termcodes(map_to, STRLEN(map_to), &map_buf, false, true, - special, CPO_TO_CPO_FLAGS); + true, CPO_TO_CPO_FLAGS); } menuarg.modes = modes; menuarg.noremap[0] = noremap; menuarg.silent[0] = silent; - add_menu_path(menu_path, &menuarg, pri_tab, map_to - ); + add_menu_path(menu_path, &menuarg, pri_tab, map_to); /* * For the PopUp menu, add a menu for each mode separately. @@ -253,16 +249,18 @@ theend: ; } -/* - * Add the menu with the given name to the menu hierarchy - */ -static int -add_menu_path ( - char_u *menu_path, - vimmenu_T *menuarg, /* passes modes, iconfile, iconidx, - icon_builtin, silent[0], noremap[0] */ - long *pri_tab, - char_u *call_data + +/// Add the menu with the given name to the menu hierarchy +/// +/// @param[out] menuarg menu entry +/// @param[] pri_tab priority table +/// @param[in] call_data Right hand side command +static int +add_menu_path( + const char_u *const menu_path, + vimmenu_T *menuarg, + const long *const pri_tab, + const char_u *const call_data ) { char_u *path_name; @@ -297,8 +295,9 @@ add_menu_path ( if (map_to != NULL) { en_name = name; name = map_to; - } else + } else { en_name = NULL; + } dname = menu_text(name, NULL, NULL); if (*dname == NUL) { /* Only a mnemonic or accelerator is not valid. */ @@ -312,14 +311,15 @@ add_menu_path ( while (menu != NULL) { if (menu_name_equal(name, menu) || menu_name_equal(dname, menu)) { if (*next_name == NUL && menu->children != NULL) { - if (!sys_menu) + if (!sys_menu) { EMSG(_("E330: Menu path must not lead to a sub-menu")); + } goto erret; } - if (*next_name != NUL && menu->children == NULL - ) { - if (!sys_menu) + if (*next_name != NUL && menu->children == NULL) { + if (!sys_menu) { EMSG(_(e_notsubmenu)); + } goto erret; } break; @@ -353,7 +353,7 @@ add_menu_path ( menu->modes = modes; menu->enabled = MENU_ALL_MODES; menu->name = vim_strsave(name); - /* separate mnemonic and accelerator text from actual menu name */ + // separate mnemonic and accelerator text from actual menu name menu->dname = menu_text(name, &menu->mnemonic, &menu->actext); if (en_name != NULL) { menu->en_name = vim_strsave(en_name); @@ -365,9 +365,7 @@ add_menu_path ( menu->priority = pri_tab[pri_idx]; menu->parent = parent; - /* - * Add after menu that has lower priority. - */ + // Add after menu that has lower priority. menu->next = *lower_pri; *lower_pri = menu; @@ -393,8 +391,9 @@ add_menu_path ( name = next_name; xfree(dname); dname = NULL; - if (pri_tab[pri_idx + 1] != -1) - ++pri_idx; + if (pri_tab[pri_idx + 1] != -1) { + pri_idx++; + } } xfree(path_name); @@ -420,8 +419,7 @@ add_menu_path ( // Don't do this for "<Nop>". c = 0; d = 0; - if (amenu && call_data != NULL && *call_data != NUL - ) { + if (amenu && call_data != NULL && *call_data != NUL) { switch (1 << i) { case MENU_VISUAL_MODE: case MENU_SELECT_MODE: @@ -439,9 +437,9 @@ add_menu_path ( if (c != 0) { menu->strings[i] = xmalloc(STRLEN(call_data) + 5 ); menu->strings[i][0] = c; - if (d == 0) + if (d == 0) { STRCPY(menu->strings[i] + 1, call_data); - else { + } else { menu->strings[i][1] = d; STRCPY(menu->strings[i] + 2, call_data); } @@ -453,8 +451,9 @@ add_menu_path ( menu->strings[i][len + 1] = Ctrl_G; menu->strings[i][len + 2] = NUL; } - } else + } else { menu->strings[i] = p; + } menu->noremap[i] = menuarg->noremap[0]; menu->silent[i] = menuarg->silent[0]; } @@ -658,20 +657,109 @@ static void free_menu_string(vimmenu_T *menu, int idx) menu->strings[idx] = NULL; } -/* - * Show the mapping associated with a menu item or hierarchy in a sub-menu. - */ -static int show_menus(char_u *path_name, int modes) +/// Export menus +/// +/// @param[in] menu if null, starts from root_menu +/// @param modes, a choice of \ref MENU_MODES +/// @return a dict with name/commands +/// @see menu_get +static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes) +{ + dict_T *dict; + + if (!menu || (menu->modes & modes) == 0x0) { + return NULL; + } + + dict = tv_dict_alloc(); + tv_dict_add_str(dict, S_LEN("name"), (char *)menu->dname); + tv_dict_add_nr(dict, S_LEN("priority"), (int)menu->priority); + tv_dict_add_nr(dict, S_LEN("hidden"), menu_is_hidden(menu->dname)); + + if (menu->mnemonic) { + char buf[MB_MAXCHAR + 1] = { 0 }; // > max value of utf8_char2bytes + utf_char2bytes(menu->mnemonic, (char_u *)buf); + tv_dict_add_str(dict, S_LEN("shortcut"), buf); + } + + if (menu->actext) { + tv_dict_add_str(dict, S_LEN("actext"), (char *)menu->actext); + } + + if (menu->modes & MENU_TIP_MODE && menu->strings[MENU_INDEX_TIP]) { + tv_dict_add_str(dict, S_LEN("tooltip"), + (char *)menu->strings[MENU_INDEX_TIP]); + } + + if (!menu->children) { + // leaf menu + dict_T *commands = tv_dict_alloc(); + tv_dict_add_dict(dict, S_LEN("mappings"), commands); + + for (int bit = 0; bit < MENU_MODES; bit++) { + if ((menu->modes & modes & (1 << bit)) != 0) { + dict_T *impl = tv_dict_alloc(); + tv_dict_add_allocated_str(impl, S_LEN("rhs"), + str2special_save((char *)menu->strings[bit], + false, false)); + tv_dict_add_nr(impl, S_LEN("silent"), menu->silent[bit]); + tv_dict_add_nr(impl, S_LEN("enabled"), + (menu->enabled & (1 << bit)) ? 1 : 0); + tv_dict_add_nr(impl, S_LEN("noremap"), + (menu->noremap[bit] & REMAP_NONE) ? 1 : 0); + tv_dict_add_nr(impl, S_LEN("sid"), + (menu->noremap[bit] & REMAP_SCRIPT) ? 1 : 0); + tv_dict_add_dict(commands, (char *)&menu_mode_chars[bit], 1, impl); + } + } + } else { + // visit recursively all children + list_T *const children_list = tv_list_alloc(kListLenMayKnow); + for (menu = menu->children; menu != NULL; menu = menu->next) { + dict_T *dic = menu_get_recursive(menu, modes); + if (tv_dict_len(dict) > 0) { + tv_list_append_dict(children_list, dic); + } + } + tv_dict_add_list(dict, S_LEN("submenus"), children_list); + } + return dict; +} + + +/// Export menus matching path \p path_name +/// +/// @param path_name +/// @param modes supported modes, see \ref MENU_MODES +/// @param[in,out] list must be allocated +/// @return false if could not find path_name +bool menu_get(char_u *const path_name, int modes, list_T *list) { - char_u *p; - char_u *name; vimmenu_T *menu; - vimmenu_T *parent = NULL; + menu = find_menu(root_menu, path_name, modes); + if (!menu) { + return false; + } + for (; menu != NULL; menu = menu->next) { + dict_T *dict = menu_get_recursive(menu, modes); + if (dict && tv_dict_len(dict) > 0) { + tv_list_append_dict(list, dict); + } + } + return true; +} - menu = root_menu; - name = path_name = vim_strsave(path_name); - /* First, find the (sub)menu with the given name */ +/// Find menu matching required name and modes +/// +/// @param menu top menu to start looking from +/// @param name path towards the menu +/// @return menu if \p name is null, found menu or NULL +vimmenu_T * +find_menu(vimmenu_T *menu, char_u * name, int modes) +{ + char_u *p; + while (*name) { p = menu_name_skip(name); while (menu != NULL) { @@ -679,39 +767,46 @@ static int show_menus(char_u *path_name, int modes) /* Found menu */ if (*p != NUL && menu->children == NULL) { EMSG(_(e_notsubmenu)); - xfree(path_name); - return FAIL; + return NULL; } else if ((menu->modes & modes) == 0x0) { EMSG(_(e_othermode)); - xfree(path_name); - return FAIL; + return NULL; } break; } menu = menu->next; } + if (menu == NULL) { EMSG2(_(e_nomenu), name); - xfree(path_name); - return FAIL; + return NULL; } name = p; - parent = menu; menu = menu->children; } - xfree(path_name); + return menu; +} + +/// Show the mapping associated with a menu item or hierarchy in a sub-menu. +static int show_menus(char_u *const path_name, int modes) +{ + vimmenu_T *menu; + + // First, find the (sub)menu with the given name + menu = find_menu(root_menu, path_name, modes); + if (!menu) { + return FAIL; + } /* Now we have found the matching menu, and we list the mappings */ /* Highlight title */ MSG_PUTS_TITLE(_("\n--- Menus ---")); - show_menus_recursive(parent, modes, 0); + show_menus_recursive(menu->parent, modes, 0); return OK; } -/* - * Recursively show the mappings associated with the menus under the given one - */ +/// Recursively show the mappings associated with the menus under the given one static void show_menus_recursive(vimmenu_T *menu, int modes, int depth) { int i; @@ -994,12 +1089,13 @@ char_u *get_menu_names(expand_T *xp, int idx) return str; } -/* - * Skip over this element of the menu path and return the start of the next - * element. Any \ and ^Vs are removed from the current element. - * "name" may be modified. - */ -char_u *menu_name_skip(char_u *name) + +/// Skip over this element of the menu path and return the start of the next +/// element. Any \ and ^Vs are removed from the current element. +/// +/// @param name may be modified. +/// @return start of the next element +char_u *menu_name_skip(char_u *const name) { char_u *p; @@ -1019,16 +1115,16 @@ char_u *menu_name_skip(char_u *name) * Return TRUE when "name" matches with menu "menu". The name is compared in * two ways: raw menu name and menu name without '&'. ignore part after a TAB. */ -static int menu_name_equal(char_u *name, vimmenu_T *menu) +static bool menu_name_equal(const char_u *const name, vimmenu_T *const menu) { if (menu->en_name != NULL && (menu_namecmp(name, menu->en_name) || menu_namecmp(name, menu->en_dname))) - return TRUE; + return true; return menu_namecmp(name, menu->name) || menu_namecmp(name, menu->dname); } -static int menu_namecmp(char_u *name, char_u *mname) +static bool menu_namecmp(const char_u *const name, const char_u *const mname) { int i; @@ -1039,18 +1135,20 @@ static int menu_namecmp(char_u *name, char_u *mname) && (mname[i] == NUL || mname[i] == TAB); } -/* - * Return the modes specified by the given menu command (eg :menu! returns - * MENU_CMDLINE_MODE | MENU_INSERT_MODE). - * If "noremap" is not NULL, then the flag it points to is set according to - * whether the command is a "nore" command. - * If "unmenu" is not NULL, then the flag it points to is set according to - * whether the command is an "unmenu" command. - */ -static int -get_menu_cmd_modes ( - char_u *cmd, - int forceit, /* Was there a "!" after the command? */ + +/// Returns the \ref MENU_MODES specified by menu command `cmd`. +/// (eg :menu! returns MENU_CMDLINE_MODE | MENU_INSERT_MODE) +/// +/// @param[in] cmd string like "nmenu", "vmenu", etc. +/// @param[in] forceit bang (!) was given after the command +/// @param[out] noremap If not NULL, the flag it points to is set according +/// to whether the command is a "nore" command. +/// @param[out] unmenu If not NULL, the flag it points to is set according +/// to whether the command is an "unmenu" command. +int +get_menu_cmd_modes( + const char_u * cmd, + bool forceit, int *noremap, int *unmenu ) @@ -1091,12 +1189,15 @@ get_menu_cmd_modes ( } /* FALLTHROUGH */ default: - --cmd; - if (forceit) /* menu!! */ + cmd--; + if (forceit) { + // menu!! modes = MENU_INSERT_MODE | MENU_CMDLINE_MODE; - else /* menu */ + } else { + // menu modes = MENU_NORMAL_MODE | MENU_VISUAL_MODE | MENU_SELECT_MODE | MENU_OP_PENDING_MODE; + } } if (noremap != NULL) @@ -1202,12 +1303,14 @@ int menu_is_separator(char_u *name) return name[0] == '-' && name[STRLEN(name) - 1] == '-'; } -/* - * Return TRUE if the menu is hidden: Starts with ']' - */ + +/// True if a popup menu or starts with \ref MNU_HIDDEN_CHAR +/// +/// @return true if the menu is hidden static int menu_is_hidden(char_u *name) { - return (name[0] == ']') || (menu_is_popup(name) && name[5] != NUL); + return (name[0] == MNU_HIDDEN_CHAR) + || (menu_is_popup(name) && name[5] != NUL); } /* @@ -1314,17 +1417,20 @@ void ex_emenu(exarg_T *eap) idx = MENU_INDEX_NORMAL; } - if (idx != MENU_INDEX_INVALID && menu->strings[idx] != NULL) { - /* When executing a script or function execute the commands right now. - * Otherwise put them in the typeahead buffer. */ - if (current_SID != 0) + assert(idx != MENU_INDEX_INVALID); + if (menu->strings[idx] != NULL) { + // When executing a script or function execute the commands right now. + // Otherwise put them in the typeahead buffer. + if (current_SID != 0) { exec_normal_cmd(menu->strings[idx], menu->noremap[idx], - menu->silent[idx]); - else - ins_typebuf(menu->strings[idx], menu->noremap[idx], 0, - TRUE, menu->silent[idx]); - } else + menu->silent[idx]); + } else { + ins_typebuf(menu->strings[idx], menu->noremap[idx], 0, true, + menu->silent[idx]); + } + } else { EMSG2(_("E335: Menu not defined for %s mode"), mode); + } } /* diff --git a/src/nvim/menu.h b/src/nvim/menu.h index a84b7d812e..5ff979f2bf 100644 --- a/src/nvim/menu.h +++ b/src/nvim/menu.h @@ -6,7 +6,9 @@ #include "nvim/types.h" // for char_u and expand_T #include "nvim/ex_cmds_defs.h" // for exarg_T -/* Indices into vimmenu_T->strings[] and vimmenu_T->noremap[] for each mode */ +/// Indices into vimmenu_T->strings[] and vimmenu_T->noremap[] for each mode +/// \addtogroup MENU_INDEX +/// @{ #define MENU_INDEX_INVALID -1 #define MENU_INDEX_NORMAL 0 #define MENU_INDEX_VISUAL 1 @@ -16,8 +18,12 @@ #define MENU_INDEX_CMDLINE 5 #define MENU_INDEX_TIP 6 #define MENU_MODES 7 +/// @} +/// note MENU_INDEX_TIP is not a 'real' mode -/* Menu modes */ +/// Menu modes +/// \addtogroup MENU_MODES +/// @{ #define MENU_NORMAL_MODE (1 << MENU_INDEX_NORMAL) #define MENU_VISUAL_MODE (1 << MENU_INDEX_VISUAL) #define MENU_SELECT_MODE (1 << MENU_INDEX_SELECT) @@ -26,31 +32,30 @@ #define MENU_CMDLINE_MODE (1 << MENU_INDEX_CMDLINE) #define MENU_TIP_MODE (1 << MENU_INDEX_TIP) #define MENU_ALL_MODES ((1 << MENU_INDEX_TIP) - 1) -/*note MENU_INDEX_TIP is not a 'real' mode*/ +/// @} -/* Start a menu name with this to not include it on the main menu bar */ +/// Start a menu name with this to not include it on the main menu bar #define MNU_HIDDEN_CHAR ']' typedef struct VimMenu vimmenu_T; struct VimMenu { - int modes; /* Which modes is this menu visible for? */ - int enabled; /* for which modes the menu is enabled */ - char_u *name; /* Name of menu, possibly translated */ - char_u *dname; /* Displayed Name ("name" without '&') */ - char_u *en_name; /* "name" untranslated, NULL when "name" - * was not translated */ - char_u *en_dname; /* "dname" untranslated, NULL when "dname" - * was not translated */ - int mnemonic; /* mnemonic key (after '&') */ - char_u *actext; /* accelerator text (after TAB) */ - long priority; /* Menu order priority */ - char_u *strings[MENU_MODES]; /* Mapped string for each mode */ - int noremap[MENU_MODES]; /* A REMAP_ flag for each mode */ - bool silent[MENU_MODES]; /* A silent flag for each mode */ - vimmenu_T *children; /* Children of sub-menu */ - vimmenu_T *parent; /* Parent of menu */ - vimmenu_T *next; /* Next item in menu */ + int modes; ///< Which modes is this menu visible for + int enabled; ///< for which modes the menu is enabled + char_u *name; ///< Name of menu, possibly translated + char_u *dname; ///< Displayed Name ("name" without '&') + char_u *en_name; ///< "name" untranslated, NULL when + ///< was not translated + char_u *en_dname; ///< NULL when "dname" untranslated + int mnemonic; ///< mnemonic key (after '&') + char_u *actext; ///< accelerator text (after TAB) + long priority; ///< Menu order priority + char_u *strings[MENU_MODES]; ///< Mapped string for each mode + int noremap[MENU_MODES]; ///< A \ref REMAP_VALUES flag for each mode + bool silent[MENU_MODES]; ///< A silent flag for each mode + vimmenu_T *children; ///< Children of sub-menu + vimmenu_T *parent; ///< Parent of menu + vimmenu_T *next; ///< Next item in menu }; diff --git a/src/nvim/message.c b/src/nvim/message.c index 057ce75f79..12e5b844be 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -487,9 +487,6 @@ int emsg(const char_u *s_) } called_emsg = true; - if (emsg_silent == 0) { - ex_exitval = 1; - } // If "emsg_severe" is TRUE: When an error exception is to be thrown, // prefer this message over previous messages for the same command. @@ -540,6 +537,8 @@ int emsg(const char_u *s_) return true; } + ex_exitval = 1; + // Reset msg_silent, an error causes messages to be switched back on. msg_silent = 0; cmd_silent = FALSE; @@ -583,19 +582,60 @@ void emsg_invreg(int name) /// Print an error message with unknown number of arguments bool emsgf(const char *const fmt, ...) { + bool ret; + + va_list ap; + va_start(ap, fmt); + ret = emsgfv(fmt, ap); + va_end(ap); + + return ret; +} + +/// Print an error message with unknown number of arguments +static bool emsgfv(const char *fmt, va_list ap) +{ static char errbuf[IOSIZE]; if (emsg_not_now()) { return true; } - va_list ap; - va_start(ap, fmt); vim_vsnprintf(errbuf, sizeof(errbuf), fmt, ap, NULL); - va_end(ap); return emsg((const char_u *)errbuf); } +/// Same as emsg(...), but abort on error when ABORT_ON_INTERNAL_ERROR is +/// defined. It is used for internal errors only, so that they can be +/// detected when fuzzing vim. +void iemsg(const char *s) +{ + msg((char_u *)s); +#ifdef ABORT_ON_INTERNAL_ERROR + abort(); +#endif +} + +/// Same as emsgf(...) but abort on error when ABORT_ON_INTERNAL_ERROR is +/// defined. It is used for internal errors only, so that they can be +/// detected when fuzzing vim. +void iemsgf(const char *s, ...) +{ + va_list ap; + va_start(ap, s); + (void)emsgfv(s, ap); + va_end(ap); +#ifdef ABORT_ON_INTERNAL_ERROR + abort(); +#endif +} + +/// Give an "Internal error" message. +void internal_error(char *where) +{ + IEMSG2(_(e_intern2), where); +} + static void msg_emsgf_event(void **argv) { char *s = argv[0]; @@ -1196,7 +1236,7 @@ int msg_outtrans_len_attr(char_u *msgstr, int len, int attr) len -= mb_l - 1; str += mb_l; } else { - s = transchar_byte(*str); + s = transchar_byte((uint8_t)(*str)); if (s[1] != NUL) { // Unprintable char: print the printable chars so far and the // translation of the unprintable char. @@ -1237,31 +1277,30 @@ void msg_make(char_u *arg) } } -/* - * Output the string 'str' upto a NUL character. - * Return the number of characters it takes on the screen. - * - * If K_SPECIAL is encountered, then it is taken in conjunction with the - * following character and shown as <F1>, <S-Up> etc. Any other character - * which is not printable shown in <> form. - * If 'from' is TRUE (lhs of a mapping), a space is shown as <Space>. - * If a character is displayed in one of these special ways, is also - * highlighted (its highlight name is '8' in the p_hl variable). - * Otherwise characters are not highlighted. - * This function is used to show mappings, where we want to see how to type - * the character/string -- webb - */ -int -msg_outtrans_special ( - char_u *strstart, - int from /* TRUE for lhs of a mapping */ +/// Output the string 'str' upto a NUL character. +/// Return the number of characters it takes on the screen. +/// +/// If K_SPECIAL is encountered, then it is taken in conjunction with the +/// following character and shown as <F1>, <S-Up> etc. Any other character +/// which is not printable shown in <> form. +/// If 'from' is TRUE (lhs of a mapping), a space is shown as <Space>. +/// If a character is displayed in one of these special ways, is also +/// highlighted (its highlight name is '8' in the p_hl variable). +/// Otherwise characters are not highlighted. +/// This function is used to show mappings, where we want to see how to type +/// the character/string -- webb +int msg_outtrans_special( + const char_u *strstart, + int from ///< true for LHS of a mapping ) { - char_u *str = strstart; + if (strstart == NULL) { + return 0; // Do nothing. + } + const char_u *str = strstart; int retval = 0; - int attr; + int attr = hl_attr(HLF_8); - attr = hl_attr(HLF_8); while (*str != NUL) { const char *string; // Leading and trailing spaces need to be displayed in <> form. @@ -1269,7 +1308,7 @@ msg_outtrans_special ( string = "<Space>"; str++; } else { - string = (const char *)str2special((char_u **)&str, from); + string = str2special((const char **)&str, from, false); } const int len = vim_strsize((char_u *)string); // Highlight special keys @@ -1281,108 +1320,125 @@ msg_outtrans_special ( return retval; } -/* - * Return the lhs or rhs of a mapping, with the key codes turned into printable - * strings, in an allocated string. - */ -char_u * -str2special_save ( - char_u *str, - int is_lhs /* TRUE for lhs, FALSE for rhs */ -) +/// Convert string, replacing key codes with printables +/// +/// Used for lhs or rhs of mappings. +/// +/// @param[in] str String to convert. +/// @param[in] replace_spaces Convert spaces into `<Space>`, normally used fo +/// lhs, but not rhs. +/// @param[in] replace_lt Convert `<` into `<lt>`. +/// +/// @return [allocated] Converted string. +char *str2special_save(const char *const str, const bool replace_spaces, + const bool replace_lt) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC + FUNC_ATTR_NONNULL_RET { garray_T ga; - char_u *p = str; - ga_init(&ga, 1, 40); - while (*p != NUL) - ga_concat(&ga, str2special(&p, is_lhs)); + + const char *p = str; + while (*p != NUL) { + ga_concat(&ga, (const char_u *)str2special(&p, replace_spaces, replace_lt)); + } ga_append(&ga, NUL); - return (char_u *)ga.ga_data; + return (char *)ga.ga_data; } -/* - * Return the printable string for the key codes at "*sp". - * Used for translating the lhs or rhs of a mapping to printable chars. - * Advances "sp" to the next code. - */ -char_u * -str2special ( - char_u **sp, - int from /* TRUE for lhs of mapping */ -) -{ - int c; - static char_u buf[7]; - char_u *str = *sp; - int modifiers = 0; - int special = FALSE; - - if (has_mbyte) { - char_u *p; - - /* Try to un-escape a multi-byte character. Return the un-escaped - * string if it is a multi-byte character. */ - p = mb_unescape(sp); - if (p != NULL) - return p; +/// Convert character, replacing key with printable representation. +/// +/// @param[in,out] sp String to convert. Is advanced to the next key code. +/// @param[in] replace_spaces Convert spaces into <Space>, normally used for +/// lhs, but not rhs. +/// @param[in] replace_lt Convert `<` into `<lt>`. +/// +/// @return Converted key code, in a static buffer. Buffer is always one and the +/// same, so save converted string somewhere before running str2special +/// for the second time. +const char *str2special(const char **const sp, const bool replace_spaces, + const bool replace_lt) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET +{ + static char buf[7]; + + // Try to un-escape a multi-byte character. Return the un-escaped + // string if it is a multi-byte character. + const char *const p = mb_unescape(sp); + if (p != NULL) { + return p; } - c = *str; + const char *str = *sp; + int c = (uint8_t)(*str); + int modifiers = 0; + bool special = false; if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL) { - if (str[1] == KS_MODIFIER) { - modifiers = str[2]; + if ((uint8_t)str[1] == KS_MODIFIER) { + modifiers = (uint8_t)str[2]; str += 3; - c = *str; + c = (uint8_t)(*str); } if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL) { - c = TO_SPECIAL(str[1], str[2]); + c = TO_SPECIAL((uint8_t)str[1], (uint8_t)str[2]); str += 2; - if (c == KS_ZERO) /* display <Nul> as ^@ or <Nul> */ + if (c == KS_ZERO) { // display <Nul> as ^@ or <Nul> c = NUL; + } + } + if (IS_SPECIAL(c) || modifiers) { // Special key. + special = true; } - if (IS_SPECIAL(c) || modifiers) /* special key */ - special = TRUE; } - if (has_mbyte && !IS_SPECIAL(c)) { - int len = (*mb_ptr2len)(str); + if (!IS_SPECIAL(c)) { + const int len = utf_ptr2len((const char_u *)str); - /* For multi-byte characters check for an illegal byte. */ - if (has_mbyte && MB_BYTE2LEN(*str) > len) { - transchar_nonprint(buf, c); + // Check for an illegal byte. + if (MB_BYTE2LEN((uint8_t)(*str)) > len) { + transchar_nonprint((char_u *)buf, c); *sp = str + 1; return buf; } - /* Since 'special' is TRUE the multi-byte character 'c' will be - * processed by get_special_key_name() */ - c = (*mb_ptr2char)(str); + // Since 'special' is TRUE the multi-byte character 'c' will be + // processed by get_special_key_name(). + c = utf_ptr2char((const char_u *)str); *sp = str + len; - } else + } else { *sp = str + 1; + } - /* Make unprintable characters in <> form, also <M-Space> and <Tab>. - * Use <Space> only for lhs of a mapping. */ - if (special || char2cells(c) > 1 || (from && c == ' ')) - return get_special_key_name(c, modifiers); + // Make unprintable characters in <> form, also <M-Space> and <Tab>. + if (special + || char2cells(c) > 1 + || (replace_spaces && c == ' ') + || (replace_lt && c == '<')) { + return (const char *)get_special_key_name(c, modifiers); + } buf[0] = c; buf[1] = NUL; return buf; } -/* - * Translate a key sequence into special key names. - */ -void str2specialbuf(char_u *sp, char_u *buf, int len) +/// Convert string, replacing key codes with printables +/// +/// @param[in] str String to convert. +/// @param[out] buf Buffer to save results to. +/// @param[in] len Buffer length. +void str2specialbuf(const char *sp, char *buf, size_t len) + FUNC_ATTR_NONNULL_ALL { - char_u *s; - - *buf = NUL; while (*sp) { - s = str2special(&sp, FALSE); - if ((int)(STRLEN(s) + STRLEN(buf)) < len) - STRCAT(buf, s); + const char *s = str2special(&sp, false, false); + const size_t s_len = strlen(s); + if (len <= s_len) { + break; + } + memcpy(buf, s, s_len); + buf += s_len; + len -= s_len; } + *buf = NUL; } /* @@ -1611,6 +1667,27 @@ void msg_puts_attr_len(const char *const str, const ptrdiff_t len, int attr) } } +/// Print a formatted message +/// +/// Message printed is limited by #IOSIZE. Must not be used from inside +/// msg_puts_attr(). +/// +/// @param[in] attr Highlight attributes. +/// @param[in] fmt Format string. +void msg_printf_attr(const int attr, const char *const fmt, ...) + FUNC_ATTR_NONNULL_ARG(2) +{ + static char msgbuf[IOSIZE]; + + va_list ap; + va_start(ap, fmt); + const size_t len = vim_vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap, NULL); + va_end(ap); + + msg_scroll = true; + msg_puts_attr_len(msgbuf, (ptrdiff_t)len, attr); +} + /* * The display part of msg_puts_attr_len(). * May be called recursively to display scroll-back text. @@ -1747,17 +1824,13 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, } while (msg_col & 7); } else if (*s == BELL) { // beep (from ":sh") vim_beep(BO_SH); - } else { - if (has_mbyte) { - cw = (*mb_ptr2cells)(s); - if (enc_utf8 && maxlen >= 0) - /* avoid including composing chars after the end */ - l = utfc_ptr2len_len(s, (int)((str + maxlen) - s)); - else - l = (*mb_ptr2len)(s); + } else if (*s >= 0x20) { // printable char + cw = mb_ptr2cells(s); + if (maxlen >= 0) { + // avoid including composing chars after the end + l = utfc_ptr2len_len(s, (int)((str + maxlen) - s)); } else { - cw = 1; - l = 1; + l = utfc_ptr2len(s); } // When drawing from right to left or when a double-wide character // doesn't fit, draw a single character here. Otherwise collect @@ -2705,9 +2778,11 @@ do_dialog ( int c; int i; - /* Don't output anything in silent mode ("ex -s") */ - if (silent_mode) - return dfltbutton; /* return default option */ + if (silent_mode // No dialogs in silent mode ("ex -s") + || !ui_active() // Without a UI Nvim waits for input forever. + ) { + return dfltbutton; // return default option + } oldState = State; diff --git a/src/nvim/message.h b/src/nvim/message.h index 904a9d3ca1..82935a36a9 100644 --- a/src/nvim/message.h +++ b/src/nvim/message.h @@ -49,6 +49,15 @@ /// Like #EMSG, but for messages with one "%" PRIu64 inside #define EMSGU(s, n) emsgf((const char *) (s), (uint64_t)(n)) +/// Like #EMSG, but for internal messages +#define IEMSG(s) iemsg((const char *)(s)) + +/// Like #EMSG2, but for internal messages +#define IEMSG2(s, p) iemsgf((const char *)(s), (p)) + +/// Like #EMSGN, but for internal messages +#define IEMSGN(s, n) iemsgf((const char *)(s), (int64_t)(n)) + /// Display message at the recorded position #define MSG_PUTS(s) msg_puts((const char *)(s)) diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index 835b9c7b20..b0232b6516 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -749,8 +749,9 @@ open_line ( // Postpone calling changed_lines(), because it would mess up folding // with markers. // Skip mark_adjust when adding a line after the last one, there can't - // be marks there. - if (curwin->w_cursor.lnum + 1 < curbuf->b_ml.ml_line_count) { + // be marks there. But still needed in diff mode. + if (curwin->w_cursor.lnum + 1 < curbuf->b_ml.ml_line_count + || curwin->w_p_diff) { mark_adjust(curwin->w_cursor.lnum + 1, (linenr_T)MAXLNUM, 1L, 0L, false); } did_append = true; @@ -1273,8 +1274,8 @@ int plines_win_nofold(win_T *wp, linenr_T lnum) * Add column offset for 'number', 'relativenumber' and 'foldcolumn'. */ width = wp->w_width - win_col_off(wp); - if (width <= 0) { - return 32000; // bigger than the number of lines of the screen + if (width <= 0 || col > 32000) { + return 32000; // bigger than the number of screen columns } if (col <= (unsigned int)width) { return 1; @@ -1468,7 +1469,7 @@ void ins_char_bytes(char_u *buf, size_t charlen) } } - char_u *newp = (char_u *) xmalloc((size_t)(linelen + newlen - oldlen)); + char_u *newp = xmalloc((size_t)(linelen + newlen - oldlen)); // Copy bytes before the cursor. if (col > 0) { @@ -1477,7 +1478,10 @@ void ins_char_bytes(char_u *buf, size_t charlen) // Copy bytes after the changed character(s). char_u *p = newp + col; - memmove(p + newlen, oldp + col + oldlen, (size_t)(linelen - col - oldlen)); + if (linelen > col + oldlen) { + memmove(p + newlen, oldp + col + oldlen, + (size_t)(linelen - col - oldlen)); + } // Insert or overwrite the new character. memmove(p, buf, charlen); @@ -1864,8 +1868,8 @@ void appended_lines(linenr_T lnum, long count) void appended_lines_mark(linenr_T lnum, long count) { // Skip mark_adjust when adding a line after the last one, there can't - // be marks there. - if (lnum + count < curbuf->b_ml.ml_line_count) { + // be marks there. But it's still needed in diff mode. + if (lnum + count < curbuf->b_ml.ml_line_count || curwin->w_p_diff) { mark_adjust(lnum + 1, (linenr_T)MAXLNUM, count, 0L, false); } changed_lines(lnum + 1, 0, lnum + 1, count); @@ -2203,7 +2207,7 @@ change_warning ( set_vim_var_string(VV_WARNINGMSG, _(w_readonly), -1); msg_clr_eos(); (void)msg_end(); - if (msg_silent == 0 && !silent_mode) { + if (msg_silent == 0 && !silent_mode && ui_active()) { ui_flush(); os_delay(1000L, true); /* give the user time to think about it */ } @@ -2609,20 +2613,22 @@ int match_user(char_u *name) return result; } -/* - * Preserve files and exit. - * When called IObuff must contain a message. - * NOTE: This may be called from deathtrap() in a signal handler, avoid unsafe - * functions, such as allocating memory. - */ +/// Preserve files and exit. +/// @note IObuff must contain a message. +/// @note This may be called from deadly_signal() in a signal handler, avoid +/// unsafe functions, such as allocating memory. void preserve_exit(void) + FUNC_ATTR_NORETURN { // 'true' when we are sure to exit, e.g., after a deadly signal static bool really_exiting = false; // Prevent repeated calls into this method. if (really_exiting) { - stream_set_blocking(input_global_fd(), true); //normalize stream (#2598) + if (input_global_fd() >= 0) { + // normalize stream (#2598) + stream_set_blocking(input_global_fd(), true); + } exit(2); } diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index d908a022f1..6f636f643a 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -608,8 +608,7 @@ bool mouse_scroll_horiz(int dir) return leftcol_changed(); } -// Adjust the clicked column position if there are concealed characters -// before the current column. But only when it's absolutely necessary. +/// Adjusts the clicked column position when 'conceallevel' > 0 static int mouse_adjust_click(win_T *wp, int row, int col) { if (!(wp->w_p_cole > 0 && curbuf->b_p_smc > 0 @@ -617,64 +616,102 @@ static int mouse_adjust_click(win_T *wp, int row, int col) return col; } - int end = (colnr_T)STRLEN(ml_get(wp->w_cursor.lnum)); - int vend = getviscol2(end, 0); + // `col` is the position within the current line that is highlighted by the + // cursor without consideration for concealed characters. The current line is + // scanned *up to* `col`, nudging it left or right when concealed characters + // are encountered. + // + // chartabsize() is used to keep track of the virtual column position relative + // to the line's bytes. For example: if col == 9 and the line starts with a + // tab that's 8 columns wide, we would want the cursor to be highlighting the + // second byte, not the ninth. + + linenr_T lnum = wp->w_cursor.lnum; + char_u *line = ml_get(lnum); + char_u *ptr = line; + char_u *ptr_end = line; + char_u *ptr_row_offset = line; // Where we begin adjusting `ptr_end` - if (col >= vend) { - return col; + // Find the offset where scanning should begin. + int offset = wp->w_leftcol; + if (row > 0) { + offset += row * (wp->w_width - win_col_off(wp) - win_col_off2(wp) - + wp->w_leftcol + wp->w_skipcol); } - int i = wp->w_leftcol; + int vcol; + + if (offset) { + // Skip everything up to an offset since nvim takes care of displaying the + // correct portion of the line when horizontally scrolling. + // When 'wrap' is enabled, only the row (of the wrapped line) needs to be + // checked for concealed characters. + vcol = 0; + while (vcol < offset && *ptr != NUL) { + vcol += chartabsize(ptr, vcol); + ptr += utfc_ptr2len(ptr); + } - if (row > 0) { - i += row * (wp->w_width - win_col_off(wp) - win_col_off2(wp) - - wp->w_leftcol) + wp->w_skipcol; + ptr_row_offset = ptr; } - int start_col = i; - int matchid; - int last_matchid; - int bcol = end - (vend - col); + // Align `ptr_end` with `col` + vcol = offset; + ptr_end = ptr_row_offset; + while (vcol < col && *ptr_end != NUL) { + vcol += chartabsize(ptr_end, vcol); + ptr_end += utfc_ptr2len(ptr_end); + } - while (i < bcol) { - matchid = syn_get_concealed_id(wp, wp->w_cursor.lnum, i); + int matchid; + int prev_matchid; + int nudge = 0; + int cwidth = 0; + + vcol = offset; + +#define incr() nudge++; ptr_end += utfc_ptr2len(ptr_end) +#define decr() nudge--; ptr_end -= utfc_ptr2len(ptr_end) + + while (ptr < ptr_end && *ptr != NUL) { + cwidth = chartabsize(ptr, vcol); + vcol += cwidth; + if (cwidth > 1 && *ptr == '\t' && nudge > 0) { + // A tab will "absorb" any previous adjustments. + cwidth = MIN(cwidth, nudge); + while (cwidth > 0) { + decr(); + cwidth--; + } + } + matchid = syn_get_concealed_id(wp, lnum, (colnr_T)(ptr - line)); if (matchid != 0) { if (wp->w_p_cole == 3) { - bcol++; + incr(); } else { - if (row > 0 && i == start_col) { - // Check if the current concealed character is actually part of - // the previous wrapped row's conceal group. - last_matchid = syn_get_concealed_id(wp, wp->w_cursor.lnum, - i - 1); - if (last_matchid == matchid) { - bcol++; - } - } else if (wp->w_p_cole == 1 - || (wp->w_p_cole == 2 - && (lcs_conceal != NUL - || syn_get_sub_char() != NUL))) { + if (!(row > 0 && ptr == ptr_row_offset) + && (wp->w_p_cole == 1 || (wp->w_p_cole == 2 + && (lcs_conceal != NUL + || syn_get_sub_char() != NUL)))) { // At least one placeholder character will be displayed. - bcol--; + decr(); } - last_matchid = matchid; + prev_matchid = matchid; - // Adjust for concealed text that spans more than one character. - do { - i++; - bcol++; - matchid = syn_get_concealed_id(wp, wp->w_cursor.lnum, i); - } while (last_matchid == matchid); + while (prev_matchid == matchid && *ptr != NUL) { + incr(); + ptr += utfc_ptr2len(ptr); + matchid = syn_get_concealed_id(wp, lnum, (colnr_T)(ptr - line)); + } continue; } } - i++; + ptr += utfc_ptr2len(ptr); } - return getviscol2(bcol, 0); + return col + nudge; } - diff --git a/src/nvim/move.c b/src/nvim/move.c index d5be4cb8c3..cb13a9e207 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -155,12 +155,11 @@ void update_topline(void) old_topline = curwin->w_topline; old_topfill = curwin->w_topfill; - /* - * If the buffer is empty, always set topline to 1. - */ - if (bufempty()) { /* special case - file is empty */ - if (curwin->w_topline != 1) + // If the buffer is empty, always set topline to 1. + if (BUFEMPTY()) { // special case - file is empty + if (curwin->w_topline != 1) { redraw_later(NOT_VALID); + } curwin->w_topline = 1; curwin->w_botline = 2; curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP; @@ -673,7 +672,7 @@ int win_col_off(win_T *wp) return ((wp->w_p_nu || wp->w_p_rnu) ? number_width(wp) + 1 : 0) + (cmdwin_type == 0 || wp != curwin ? 0 : 1) + (int)wp->w_p_fdc - + (signcolumn_on(wp) ? 2 : 0); + + (signcolumn_on(wp) ? win_signcol_width(wp) : 0); } int curwin_col_off(void) @@ -1763,7 +1762,7 @@ int onepage(int dir, long count) loff.fill = 0; if (dir == FORWARD) { - if (firstwin == lastwin && p_window > 0 && p_window < Rows - 1) { + if (ONE_WINDOW && p_window > 0 && p_window < Rows - 1) { /* Vi compatible scrolling */ if (p_window <= 2) ++curwin->w_topline; @@ -1797,7 +1796,7 @@ int onepage(int dir, long count) max_topfill(); continue; } - if (firstwin == lastwin && p_window > 0 && p_window < Rows - 1) { + if (ONE_WINDOW && p_window > 0 && p_window < Rows - 1) { /* Vi compatible scrolling (sort of) */ if (p_window <= 2) --curwin->w_topline; @@ -1989,9 +1988,8 @@ void halfpage(bool flag, linenr_T Prenum) while (n > 0 && curwin->w_botline <= curbuf->b_ml.ml_line_count) { if (curwin->w_topfill > 0) { i = 1; - if (--n < 0 && scrolled > 0) - break; - --curwin->w_topfill; + n--; + curwin->w_topfill--; } else { i = plines_nofill(curwin->w_topline); n -= i; @@ -2067,9 +2065,8 @@ void halfpage(bool flag, linenr_T Prenum) while (n > 0 && curwin->w_topline > 1) { if (curwin->w_topfill < diff_check_fill(curwin, curwin->w_topline)) { i = 1; - if (--n < 0 && scrolled > 0) - break; - ++curwin->w_topfill; + n--; + curwin->w_topfill++; } else { i = plines_nofill(curwin->w_topline - 1); n -= i; @@ -2147,14 +2144,12 @@ void do_check_cursorbind(void) curbuf = curwin->w_buffer; /* skip original window and windows with 'noscrollbind' */ if (curwin != old_curwin && curwin->w_p_crb) { - if (curwin->w_p_diff) - curwin->w_cursor.lnum - = diff_get_corresponding_line(old_curbuf, - line, - curbuf, - curwin->w_cursor.lnum); - else + if (curwin->w_p_diff) { + curwin->w_cursor.lnum = + diff_get_corresponding_line(old_curbuf, line); + } else { curwin->w_cursor.lnum = line; + } curwin->w_cursor.col = col; curwin->w_cursor.coladd = coladd; curwin->w_curswant = curswant; @@ -2166,16 +2161,21 @@ void do_check_cursorbind(void) int restart_edit_save = restart_edit; restart_edit = true; check_cursor(); + if (curwin->w_p_cul || curwin->w_p_cuc) { + validate_cursor(); + } restart_edit = restart_edit_save; } - /* Correct cursor for multi-byte character. */ - if (has_mbyte) + // Correct cursor for multi-byte character. + if (has_mbyte) { mb_adjust_cursor(); + } redraw_later(VALID); - /* Only scroll when 'scrollbind' hasn't done this. */ - if (!curwin->w_p_scb) + // Only scroll when 'scrollbind' hasn't done this. + if (!curwin->w_p_scb) { update_topline(); + } curwin->w_redr_status = true; } } diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 68ac35bc4e..32781cf4d9 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -11,8 +11,8 @@ #include "nvim/api/private/helpers.h" #include "nvim/api/vim.h" #include "nvim/api/ui.h" +#include "nvim/channel.h" #include "nvim/msgpack_rpc/channel.h" -#include "nvim/msgpack_rpc/server.h" #include "nvim/event/loop.h" #include "nvim/event/libuv_process.h" #include "nvim/event/rstream.h" @@ -29,60 +29,14 @@ #include "nvim/map.h" #include "nvim/log.h" #include "nvim/misc1.h" -#include "nvim/path.h" #include "nvim/lib/kvec.h" #include "nvim/os/input.h" -#define CHANNEL_BUFFER_SIZE 0xffff - #if MIN_LOG_LEVEL > DEBUG_LOG_LEVEL #define log_client_msg(...) #define log_server_msg(...) #endif -typedef enum { - kChannelTypeSocket, - kChannelTypeProc, - kChannelTypeStdio, - kChannelTypeInternal -} ChannelType; - -typedef struct { - uint64_t request_id; - bool returned, errored; - Object result; -} ChannelCallFrame; - -typedef struct { - uint64_t id; - size_t refcount; - size_t pending_requests; - PMap(cstr_t) *subscribed_events; - bool closed; - ChannelType type; - msgpack_unpacker *unpacker; - union { - Stream stream; - Process *proc; - struct { - Stream in; - Stream out; - } std; - } data; - uint64_t next_request_id; - kvec_t(ChannelCallFrame *) call_stack; - kvec_t(WBuffer *) delayed_notifications; - MultiQueue *events; -} Channel; - -typedef struct { - Channel *channel; - MsgpackRpcRequestHandler handler; - Array args; - uint64_t request_id; -} RequestEvent; - -static PMap(uint64_t) *channels = NULL; static PMap(cstr_t) *event_strings = NULL; static msgpack_sbuffer out_buffer; @@ -90,121 +44,64 @@ static msgpack_sbuffer out_buffer; # include "msgpack_rpc/channel.c.generated.h" #endif -/// Initializes the module -void channel_init(void) +void rpc_init(void) { ch_before_blocking_events = multiqueue_new_child(main_loop.events); - channels = pmap_new(uint64_t)(); event_strings = pmap_new(cstr_t)(); msgpack_sbuffer_init(&out_buffer); - remote_ui_init(); } -/// Teardown the module -void channel_teardown(void) -{ - if (!channels) { - return; - } - - Channel *channel; - map_foreach_value(channels, channel, { - close_channel(channel); - }); -} - -/// Creates an API channel by starting a process and connecting to its -/// stdin/stdout. stderr is handled by the job infrastructure. -/// -/// @param argv The argument vector for the process. [consumed] -/// @return The channel id (> 0), on success. -/// 0, on error. -uint64_t channel_from_process(Process *proc, uint64_t id) +void rpc_start(Channel *channel) { - Channel *channel = register_channel(kChannelTypeProc, id, proc->events); - incref(channel); // process channels are only closed by the exit_cb - channel->data.proc = proc; + channel_incref(channel); + channel->is_rpc = true; + RpcState *rpc = &channel->rpc; + rpc->closed = false; + rpc->unpacker = msgpack_unpacker_new(MSGPACK_UNPACKER_INIT_BUFFER_SIZE); + rpc->subscribed_events = pmap_new(cstr_t)(); + rpc->next_request_id = 1; + kv_init(rpc->call_stack); - wstream_init(proc->in, 0); - rstream_init(proc->out, 0); - rstream_start(proc->out, receive_msgpack, channel); + if (channel->streamtype != kChannelStreamInternal) { + Stream *out = channel_outstream(channel); +#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL + Stream *in = channel_instream(channel); + DLOG("rpc ch %" PRIu64 " in-stream=%p out-stream=%p", channel->id, in, out); +#endif - return channel->id; + rstream_start(out, receive_msgpack, channel); + } } -/// Creates an API channel from a tcp/pipe socket connection -/// -/// @param watcher The SocketWatcher ready to accept the connection -void channel_from_connection(SocketWatcher *watcher) -{ - Channel *channel = register_channel(kChannelTypeSocket, 0, NULL); - socket_watcher_accept(watcher, &channel->data.stream); - incref(channel); // close channel only after the stream is closed - channel->data.stream.internal_close_cb = close_cb; - channel->data.stream.internal_data = channel; - wstream_init(&channel->data.stream, 0); - rstream_init(&channel->data.stream, CHANNEL_BUFFER_SIZE); - rstream_start(&channel->data.stream, receive_msgpack, channel); -} -uint64_t channel_connect(bool tcp, const char *address, - int timeout, const char **error) +static Channel *find_rpc_channel(uint64_t id) { - if (!tcp) { - char *path = fix_fname(address); - if (server_owns_pipe_address(path)) { - // avoid deadlock - xfree(path); - return channel_create_internal(); - } - xfree(path); - } - - Channel *channel = register_channel(kChannelTypeSocket, 0, NULL); - if (!socket_connect(&main_loop, &channel->data.stream, - tcp, address, timeout, error)) { - decref(channel); - return 0; + Channel *chan = find_channel(id); + if (!chan || !chan->is_rpc || chan->rpc.closed) { + return NULL; } - - incref(channel); // close channel only after the stream is closed - channel->data.stream.internal_close_cb = close_cb; - channel->data.stream.internal_data = channel; - wstream_init(&channel->data.stream, 0); - rstream_init(&channel->data.stream, CHANNEL_BUFFER_SIZE); - rstream_start(&channel->data.stream, receive_msgpack, channel); - return channel->id; + return chan; } -/// Sends event/arguments to channel +/// Publishes an event to a channel. /// -/// @param id The channel id. If 0, the event will be sent to all -/// channels that have subscribed to the event type -/// @param name The event name, an arbitrary string -/// @param args Array with event arguments +/// @param id Channel id. 0 means "broadcast to all subscribed channels" +/// @param name Event name (application-defined) +/// @param args Array of event arguments /// @return True if the event was sent successfully, false otherwise. -bool channel_send_event(uint64_t id, const char *name, Array args) +bool rpc_send_event(uint64_t id, const char *name, Array args) { Channel *channel = NULL; - if (id && (!(channel = pmap_get(uint64_t)(channels, id)) - || channel->closed)) { + if (id && (!(channel = find_rpc_channel(id)))) { api_free_array(args); return false; } if (channel) { - if (channel->pending_requests) { - // Pending request, queue the notification for later sending. - const String method = cstr_as_string((char *)name); - WBuffer *buffer = serialize_request(id, 0, method, args, &out_buffer, 1); - kv_push(channel->delayed_notifications, buffer); - } else { - send_event(channel, name, args); - } + send_event(channel, name, args); } else { - // TODO(tarruda): Implement event broadcasting in vimscript broadcast_event(name, args); } @@ -218,31 +115,30 @@ bool channel_send_event(uint64_t id, const char *name, Array args) /// @param args Array with method arguments /// @param[out] error True if the return value is an error /// @return Whatever the remote method returned -Object channel_send_call(uint64_t id, - const char *method_name, - Array args, - Error *err) +Object rpc_send_call(uint64_t id, + const char *method_name, + Array args, + Error *err) { Channel *channel = NULL; - if (!(channel = pmap_get(uint64_t)(channels, id)) || channel->closed) { + if (!(channel = find_rpc_channel(id))) { api_set_error(err, kErrorTypeException, "Invalid channel: %" PRIu64, id); api_free_array(args); return NIL; } - incref(channel); - uint64_t request_id = channel->next_request_id++; + channel_incref(channel); + RpcState *rpc = &channel->rpc; + uint64_t request_id = rpc->next_request_id++; // Send the msgpack-rpc request send_request(channel, request_id, method_name, args); // Push the frame ChannelCallFrame frame = { request_id, false, false, NIL }; - kv_push(channel->call_stack, &frame); - channel->pending_requests++; + kv_push(rpc->call_stack, &frame); LOOP_PROCESS_EVENTS_UNTIL(&main_loop, channel->events, -1, frame.returned); - (void)kv_pop(channel->call_stack); - channel->pending_requests--; + (void)kv_pop(rpc->call_stack); if (frame.errored) { if (frame.result.type == kObjectTypeString) { @@ -267,11 +163,7 @@ Object channel_send_call(uint64_t id, api_free_object(frame.result); } - if (!channel->pending_requests) { - send_delayed_notifications(channel); - } - - decref(channel); + channel_decref(channel); return frame.errored ? NIL : frame.result; } @@ -280,11 +172,11 @@ Object channel_send_call(uint64_t id, /// /// @param id The channel id /// @param event The event type string -void channel_subscribe(uint64_t id, char *event) +void rpc_subscribe(uint64_t id, char *event) { Channel *channel; - if (!(channel = pmap_get(uint64_t)(channels, id)) || channel->closed) { + if (!(channel = find_rpc_channel(id))) { abort(); } @@ -295,97 +187,52 @@ void channel_subscribe(uint64_t id, char *event) pmap_put(cstr_t)(event_strings, event_string, event_string); } - pmap_put(cstr_t)(channel->subscribed_events, event_string, event_string); + pmap_put(cstr_t)(channel->rpc.subscribed_events, event_string, event_string); } /// Unsubscribes to event broadcasts /// /// @param id The channel id /// @param event The event type string -void channel_unsubscribe(uint64_t id, char *event) +void rpc_unsubscribe(uint64_t id, char *event) { Channel *channel; - if (!(channel = pmap_get(uint64_t)(channels, id)) || channel->closed) { + if (!(channel = find_rpc_channel(id))) { abort(); } unsubscribe(channel, event); } -/// Closes a channel -/// -/// @param id The channel id -/// @return true if successful, false otherwise -bool channel_close(uint64_t id) -{ - Channel *channel; - - if (!(channel = pmap_get(uint64_t)(channels, id)) || channel->closed) { - return false; - } - - close_channel(channel); - return true; -} - -/// Creates an API channel from stdin/stdout. This is used when embedding -/// Neovim -void channel_from_stdio(void) -{ - Channel *channel = register_channel(kChannelTypeStdio, 0, NULL); - incref(channel); // stdio channels are only closed on exit - // read stream - rstream_init_fd(&main_loop, &channel->data.std.in, 0, CHANNEL_BUFFER_SIZE); - rstream_start(&channel->data.std.in, receive_msgpack, channel); - // write stream - wstream_init_fd(&main_loop, &channel->data.std.out, 1, 0); -} - -/// Creates a loopback channel. This is used to avoid deadlock -/// when an instance connects to its own named pipe. -uint64_t channel_create_internal(void) -{ - Channel *channel = register_channel(kChannelTypeInternal, 0, NULL); - incref(channel); // internal channel lives until process exit - return channel->id; -} - -void channel_process_exit(uint64_t id, int status) -{ - Channel *channel = pmap_get(uint64_t)(channels, id); - - channel->closed = true; - decref(channel); -} - static void receive_msgpack(Stream *stream, RBuffer *rbuf, size_t c, void *data, bool eof) { Channel *channel = data; - incref(channel); + channel_incref(channel); if (eof) { - close_channel(channel); + channel_close(channel->id, kChannelPartRpc, NULL); char buf[256]; snprintf(buf, sizeof(buf), "ch %" PRIu64 " was closed by the client", channel->id); - call_set_error(channel, buf, WARNING_LOG_LEVEL); + call_set_error(channel, buf, WARN_LOG_LEVEL); goto end; } size_t count = rbuffer_size(rbuf); - DLOG("parsing %u bytes of msgpack data from Stream(%p)", count, stream); + DLOG("ch %" PRIu64 ": parsing %zu bytes from msgpack Stream: %p", + channel->id, count, stream); // Feed the unpacker with data - msgpack_unpacker_reserve_buffer(channel->unpacker, count); - rbuffer_read(rbuf, msgpack_unpacker_buffer(channel->unpacker), count); - msgpack_unpacker_buffer_consumed(channel->unpacker, count); + msgpack_unpacker_reserve_buffer(channel->rpc.unpacker, count); + rbuffer_read(rbuf, msgpack_unpacker_buffer(channel->rpc.unpacker), count); + msgpack_unpacker_buffer_consumed(channel->rpc.unpacker, count); parse_msgpack(channel); end: - decref(channel); + channel_decref(channel); } static void parse_msgpack(Channel *channel) @@ -395,8 +242,8 @@ static void parse_msgpack(Channel *channel) msgpack_unpack_return result; // Deserialize everything we can. - while ((result = msgpack_unpacker_next(channel->unpacker, &unpacked)) == - MSGPACK_UNPACK_SUCCESS) { + while ((result = msgpack_unpacker_next(channel->rpc.unpacker, &unpacked)) == + MSGPACK_UNPACK_SUCCESS) { bool is_response = is_rpc_response(&unpacked.data); log_client_msg(channel->id, !is_response, unpacked.data); @@ -422,7 +269,7 @@ static void parse_msgpack(Channel *channel) if (result == MSGPACK_UNPACK_NOMEM_ERROR) { mch_errmsg(e_outofmem); mch_errmsg("\n"); - decref(channel); + channel_decref(channel); preserve_exit(); } @@ -431,8 +278,8 @@ static void parse_msgpack(Channel *channel) // causes for this error(search for 'goto _failed') // // A not so uncommon cause for this might be deserializing objects with - // a high nesting level: msgpack will break when it's internal parse stack - // size exceeds MSGPACK_EMBED_STACK_SIZE(defined as 32 by default) + // a high nesting level: msgpack will break when its internal parse stack + // size exceeds MSGPACK_EMBED_STACK_SIZE (defined as 32 by default) send_error(channel, 0, "Invalid msgpack payload. " "This error can also happen when deserializing " "an object with high level of nesting"); @@ -487,7 +334,7 @@ static void handle_request(Channel *channel, msgpack_object *request) evdata->handler = handler; evdata->args = args; evdata->request_id = request_id; - incref(channel); + channel_incref(channel); if (handler.async) { bool is_get_mode = handler.fn == handle_nvim_get_mode; @@ -525,44 +372,37 @@ static void on_request_event(void **argv) api_free_object(result); } api_free_array(args); - decref(channel); + channel_decref(channel); xfree(e); api_clear_error(&error); } static bool channel_write(Channel *channel, WBuffer *buffer) { - bool success = false; + bool success; - if (channel->closed) { + if (channel->rpc.closed) { wstream_release_wbuffer(buffer); return false; } - switch (channel->type) { - case kChannelTypeSocket: - success = wstream_write(&channel->data.stream, buffer); - break; - case kChannelTypeProc: - success = wstream_write(channel->data.proc->in, buffer); - break; - case kChannelTypeStdio: - success = wstream_write(&channel->data.std.out, buffer); - break; - case kChannelTypeInternal: - incref(channel); - CREATE_EVENT(channel->events, internal_read_event, 2, channel, buffer); - success = true; - break; + if (channel->streamtype == kChannelStreamInternal) { + channel_incref(channel); + CREATE_EVENT(channel->events, internal_read_event, 2, channel, buffer); + success = true; + } else { + Stream *in = channel_instream(channel); + success = wstream_write(in, buffer); } + if (!success) { // If the write failed for any reason, close the channel char buf[256]; snprintf(buf, sizeof(buf), - "Before returning from a RPC call, ch %" PRIu64 " was " - "closed due to a failed write", + "ch %" PRIu64 ": stream write failed. " + "RPC canceled; closing channel", channel->id); call_set_error(channel, buf, ERROR_LOG_LEVEL); } @@ -575,14 +415,14 @@ static void internal_read_event(void **argv) Channel *channel = argv[0]; WBuffer *buffer = argv[1]; - msgpack_unpacker_reserve_buffer(channel->unpacker, buffer->size); - memcpy(msgpack_unpacker_buffer(channel->unpacker), + msgpack_unpacker_reserve_buffer(channel->rpc.unpacker, buffer->size); + memcpy(msgpack_unpacker_buffer(channel->rpc.unpacker), buffer->data, buffer->size); - msgpack_unpacker_buffer_consumed(channel->unpacker, buffer->size); + msgpack_unpacker_buffer_consumed(channel->rpc.unpacker, buffer->size); parse_msgpack(channel); - decref(channel); + channel_decref(channel); wstream_release_wbuffer(buffer); } @@ -631,7 +471,8 @@ static void broadcast_event(const char *name, Array args) Channel *channel; map_foreach_value(channels, channel, { - if (pmap_has(cstr_t)(channel->subscribed_events, name)) { + if (channel->is_rpc + && pmap_has(cstr_t)(channel->rpc.subscribed_events, name)) { kv_push(subscribed, channel); } }); @@ -651,11 +492,7 @@ static void broadcast_event(const char *name, Array args) for (size_t i = 0; i < kv_size(subscribed); i++) { Channel *channel = kv_A(subscribed, i); - if (channel->pending_requests) { - kv_push(channel->delayed_notifications, buffer); - } else { - channel_write(channel, buffer); - } + channel_write(channel, buffer); } end: @@ -665,10 +502,11 @@ end: static void unsubscribe(Channel *channel, char *event) { char *event_string = pmap_get(cstr_t)(event_strings, event); - pmap_del(cstr_t)(channel->subscribed_events, event_string); + pmap_del(cstr_t)(channel->rpc.subscribed_events, event_string); map_foreach_value(channels, channel, { - if (pmap_has(cstr_t)(channel->subscribed_events, event_string)) { + if (channel->is_rpc + && pmap_has(cstr_t)(channel->rpc.subscribed_events, event_string)) { return; } }); @@ -678,90 +516,43 @@ static void unsubscribe(Channel *channel, char *event) xfree(event_string); } -/// Close the channel streams/process and free the channel resources. -static void close_channel(Channel *channel) + +/// Mark rpc state as closed, and release its reference to the channel. +/// Don't call this directly, call channel_close(id, kChannelPartRpc, &error) +void rpc_close(Channel *channel) { - if (channel->closed) { + if (channel->rpc.closed) { return; } - channel->closed = true; + channel->rpc.closed = true; + channel_decref(channel); - switch (channel->type) { - case kChannelTypeSocket: - stream_close(&channel->data.stream, NULL, NULL); - break; - case kChannelTypeProc: - // Only close the rpc channel part, - // there could be an error message on the stderr stream - process_close_in(channel->data.proc); - process_close_out(channel->data.proc); - break; - case kChannelTypeStdio: - stream_close(&channel->data.std.in, NULL, NULL); - stream_close(&channel->data.std.out, NULL, NULL); - multiqueue_put(main_loop.fast_events, exit_event, 1, channel); - return; - case kChannelTypeInternal: - // nothing to free. - break; + if (channel->streamtype == kChannelStreamStdio) { + multiqueue_put(main_loop.fast_events, exit_event, 0); } - - decref(channel); } static void exit_event(void **argv) { - decref(argv[0]); - if (!exiting) { mch_exit(0); } } -static void free_channel(Channel *channel) +void rpc_free(Channel *channel) { remote_ui_disconnect(channel->id); - pmap_del(uint64_t)(channels, channel->id); - msgpack_unpacker_free(channel->unpacker); + msgpack_unpacker_free(channel->rpc.unpacker); // Unsubscribe from all events char *event_string; - map_foreach_value(channel->subscribed_events, event_string, { + map_foreach_value(channel->rpc.subscribed_events, event_string, { unsubscribe(channel, event_string); }); - pmap_free(cstr_t)(channel->subscribed_events); - kv_destroy(channel->call_stack); - kv_destroy(channel->delayed_notifications); - if (channel->type != kChannelTypeProc) { - multiqueue_free(channel->events); - } - xfree(channel); -} - -static void close_cb(Stream *stream, void *data) -{ - decref(data); -} - -static Channel *register_channel(ChannelType type, uint64_t id, - MultiQueue *events) -{ - Channel *rv = xmalloc(sizeof(Channel)); - rv->events = events ? events : multiqueue_new_child(main_loop.events); - rv->type = type; - rv->refcount = 1; - rv->closed = false; - rv->unpacker = msgpack_unpacker_new(MSGPACK_UNPACKER_INIT_BUFFER_SIZE); - rv->id = id > 0 ? id : next_chan_id++; - rv->pending_requests = 0; - rv->subscribed_events = pmap_new(cstr_t)(); - rv->next_request_id = 1; - kv_init(rv->call_stack); - kv_init(rv->delayed_notifications); - pmap_put(uint64_t)(channels, rv->id, rv); - return rv; + pmap_free(cstr_t)(channel->rpc.subscribed_events); + kv_destroy(channel->rpc.call_stack); } static bool is_rpc_response(msgpack_object *obj) @@ -776,15 +567,18 @@ static bool is_rpc_response(msgpack_object *obj) static bool is_valid_rpc_response(msgpack_object *obj, Channel *channel) { uint64_t response_id = obj->via.array.ptr[1].via.u64; + if (kv_size(channel->rpc.call_stack) == 0) { + return false; + } + // Must be equal to the frame at the stack's bottom - return kv_size(channel->call_stack) && response_id - == kv_A(channel->call_stack, kv_size(channel->call_stack) - 1)->request_id; + ChannelCallFrame *frame = kv_last(channel->rpc.call_stack); + return response_id == frame->request_id; } static void complete_call(msgpack_object *obj, Channel *channel) { - ChannelCallFrame *frame = kv_A(channel->call_stack, - kv_size(channel->call_stack) - 1); + ChannelCallFrame *frame = kv_last(channel->rpc.call_stack); frame->returned = true; frame->errored = obj->via.array.ptr[2].type != MSGPACK_OBJECT_NIL; @@ -798,14 +592,15 @@ static void complete_call(msgpack_object *obj, Channel *channel) static void call_set_error(Channel *channel, char *msg, int loglevel) { LOG(loglevel, "RPC: %s", msg); - for (size_t i = 0; i < kv_size(channel->call_stack); i++) { - ChannelCallFrame *frame = kv_A(channel->call_stack, i); + for (size_t i = 0; i < kv_size(channel->rpc.call_stack); i++) { + ChannelCallFrame *frame = kv_A(channel->rpc.call_stack, i); frame->returned = true; frame->errored = true; + api_free_object(frame->result); frame->result = STRING_OBJ(cstr_to_string(msg)); } - close_channel(channel); + channel_close(channel->id, kChannelPartRpc, NULL); } static WBuffer *serialize_request(uint64_t channel_id, @@ -847,28 +642,6 @@ static WBuffer *serialize_response(uint64_t channel_id, return rv; } -static void send_delayed_notifications(Channel* channel) -{ - for (size_t i = 0; i < kv_size(channel->delayed_notifications); i++) { - WBuffer *buffer = kv_A(channel->delayed_notifications, i); - channel_write(channel, buffer); - } - - kv_size(channel->delayed_notifications) = 0; -} - -static void incref(Channel *channel) -{ - channel->refcount++; -} - -static void decref(Channel *channel) -{ - if (!(--channel->refcount)) { - free_channel(channel); - } -} - #if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL #define REQ "[request] " #define RES "[response] " diff --git a/src/nvim/msgpack_rpc/channel.h b/src/nvim/msgpack_rpc/channel.h index f8fe6f129b..9ff5abdc5f 100644 --- a/src/nvim/msgpack_rpc/channel.h +++ b/src/nvim/msgpack_rpc/channel.h @@ -8,6 +8,7 @@ #include "nvim/event/socket.h" #include "nvim/event/process.h" #include "nvim/vim.h" +#include "nvim/channel.h" #define METHOD_MAXLEN 512 @@ -16,6 +17,7 @@ /// of os_inchar(), so they are processed "just-in-time". MultiQueue *ch_before_blocking_events; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "msgpack_rpc/channel.h.generated.h" #endif diff --git a/src/nvim/msgpack_rpc/channel_defs.h b/src/nvim/msgpack_rpc/channel_defs.h new file mode 100644 index 0000000000..6d8362e8b7 --- /dev/null +++ b/src/nvim/msgpack_rpc/channel_defs.h @@ -0,0 +1,36 @@ +#ifndef NVIM_MSGPACK_RPC_CHANNEL_DEFS_H +#define NVIM_MSGPACK_RPC_CHANNEL_DEFS_H + +#include <stdbool.h> +#include <uv.h> +#include <msgpack.h> + +#include "nvim/api/private/defs.h" +#include "nvim/event/socket.h" +#include "nvim/event/process.h" +#include "nvim/vim.h" + +typedef struct Channel Channel; + +typedef struct { + uint64_t request_id; + bool returned, errored; + Object result; +} ChannelCallFrame; + +typedef struct { + Channel *channel; + MsgpackRpcRequestHandler handler; + Array args; + uint64_t request_id; +} RequestEvent; + +typedef struct { + PMap(cstr_t) *subscribed_events; + bool closed; + msgpack_unpacker *unpacker; + uint64_t next_request_id; + kvec_t(ChannelCallFrame *) call_stack; +} RpcState; + +#endif // NVIM_MSGPACK_RPC_CHANNEL_DEFS_H diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index 444c6cc256..fecae11d45 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -88,7 +88,12 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg) { bool ret = true; kvec_t(MPToAPIObjectStackItem) stack = KV_INITIAL_VALUE; - kv_push(stack, ((MPToAPIObjectStackItem) { obj, arg, false, 0 })); + kv_push(stack, ((MPToAPIObjectStackItem) { + .mobj = obj, + .aobj = arg, + .container = false, + .idx = 0, + })); while (ret && kv_size(stack)) { MPToAPIObjectStackItem cur = kv_last(stack); if (!cur.container) { @@ -361,7 +366,7 @@ typedef struct { size_t idx; } APIToMPObjectStackItem; -/// Convert type used by Neovim API to msgpack +/// Convert type used by Nvim API to msgpack type. /// /// @param[in] result Object to convert. /// @param[out] res Structure that defines where conversion results are saved. diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c index 1e0cc27886..9bf122f4db 100644 --- a/src/nvim/msgpack_rpc/server.c +++ b/src/nvim/msgpack_rpc/server.c @@ -180,6 +180,7 @@ int server_start(const char *endpoint) void server_stop(char *endpoint) { SocketWatcher *watcher; + bool watcher_found = false; char addr[ADDRESS_MAX_SIZE]; // Trim to `ADDRESS_MAX_SIZE` @@ -189,11 +190,12 @@ void server_stop(char *endpoint) for (; i < watchers.ga_len; i++) { watcher = ((SocketWatcher **)watchers.ga_data)[i]; if (strcmp(addr, watcher->addr) == 0) { + watcher_found = true; break; } } - if (i >= watchers.ga_len) { + if (!watcher_found) { ELOG("Not listening on %s", addr); return; } diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 39cd2c6631..e4310de5d8 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -13,6 +13,7 @@ #include <stdbool.h> #include <stdlib.h> +#include "nvim/log.h" #include "nvim/vim.h" #include "nvim/ascii.h" #include "nvim/normal.h" @@ -344,8 +345,7 @@ static const struct nv_cmd { { K_F8, farsi_f8, 0, 0 }, { K_F9, farsi_f9, 0, 0 }, { K_EVENT, nv_event, NV_KEEPREG, 0 }, - { K_FOCUSGAINED, nv_focusgained, NV_KEEPREG, 0 }, - { K_FOCUSLOST, nv_focuslost, NV_KEEPREG, 0 }, + { K_COMMAND, nv_colon, 0, 0 }, }; /* Number of commands in nv_cmds[]. */ @@ -1451,9 +1451,8 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) /* Never redo "zf" (define fold). */ if ((vim_strchr(p_cpo, CPO_YANK) != NULL || oap->op_type != OP_YANK) && ((!VIsual_active || oap->motion_force) - /* Also redo Operator-pending Visual mode mappings */ - || (VIsual_active && cap->cmdchar == ':' - && oap->op_type != OP_COLON)) + // Also redo Operator-pending Visual mode mappings. + || (cap->cmdchar == ':' && oap->op_type != OP_COLON)) && cap->cmdchar != 'D' && oap->op_type != OP_FOLD && oap->op_type != OP_FOLDOPEN @@ -1475,13 +1474,13 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) AppendToRedobuffLit(cap->searchbuf, -1); } AppendToRedobuff(NL_STR); - } else if (cap->cmdchar == ':') { - /* do_cmdline() has stored the first typed line in - * "repeat_cmdline". When several lines are typed repeating - * won't be possible. */ - if (repeat_cmdline == NULL) + } else if (cap->cmdchar == ':' || cap->cmdchar == K_COMMAND) { + // do_cmdline() has stored the first typed line in + // "repeat_cmdline". When several lines are typed repeating + // won't be possible. + if (repeat_cmdline == NULL) { ResetRedobuff(); - else { + } else { AppendToRedobuffLit(repeat_cmdline, -1); AppendToRedobuff(NL_STR); xfree(repeat_cmdline); @@ -1550,8 +1549,10 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) } oap->start = VIsual; - if (VIsual_mode == 'V') + if (VIsual_mode == 'V') { oap->start.col = 0; + oap->start.coladd = 0; + } } /* @@ -1637,16 +1638,22 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) /* Prepare for redoing. Only use the nchar field for "r", * otherwise it might be the second char of the operator. */ if (cap->cmdchar == 'g' && (cap->nchar == 'n' - || cap->nchar == 'N')) + || cap->nchar == 'N')) { prep_redo(oap->regname, cap->count0, - get_op_char(oap->op_type), get_extra_op_char(oap->op_type), - oap->motion_force, cap->cmdchar, cap->nchar); - else if (cap->cmdchar != ':') - prep_redo(oap->regname, 0L, NUL, 'v', - get_op_char(oap->op_type), - get_extra_op_char(oap->op_type), - oap->op_type == OP_REPLACE - ? cap->nchar : NUL); + get_op_char(oap->op_type), get_extra_op_char(oap->op_type), + oap->motion_force, cap->cmdchar, cap->nchar); + } else if (cap->cmdchar != ':') { + int nchar = oap->op_type == OP_REPLACE ? cap->nchar : NUL; + + // reverse what nv_replace() did + if (nchar == REPLACE_CR_NCHAR) { + nchar = CAR; + } else if (nchar == REPLACE_NL_NCHAR) { + nchar = NL; + } + prep_redo(oap->regname, 0L, NUL, 'v', get_op_char(oap->op_type), + get_extra_op_char(oap->op_type), nchar); + } if (!redo_VIsual_busy) { redo_VIsual_mode = resel_VIsual_mode; redo_VIsual_vcol = resel_VIsual_vcol; @@ -1944,8 +1951,11 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank) * the lines. */ auto_format(false, true); - if (restart_edit == 0) + if (restart_edit == 0) { restart_edit = restart_edit_save; + } else { + cap->retval |= CA_COMMAND_BUSY; + } } break; @@ -3658,6 +3668,39 @@ nv_gd ( } } +// Return true if line[offset] is not inside a C-style comment or string, false +// otherwise. +static bool is_ident(char_u *line, int offset) +{ + bool incomment = false; + int instring = 0; + int prev = 0; + + for (int i = 0; i < offset && line[i] != NUL; i++) { + if (instring != 0) { + if (prev != '\\' && line[i] == instring) { + instring = 0; + } + } else if ((line[i] == '"' || line[i] == '\'') && !incomment) { + instring = line[i]; + } else { + if (incomment) { + if (prev == '*' && line[i] == '/') { + incomment = false; + } + } else if (prev == '/' && line[i] == '*') { + incomment = true; + } else if (prev == '/' && line[i] == '/') { + return false; + } + } + + prev = line[i]; + } + + return incomment == false && instring == 0; +} + /* * Search for variable declaration of "ptr[len]". * When "locally" is true in the current function ("gd"), otherwise in the @@ -3684,6 +3727,7 @@ find_decl ( bool retval = true; bool incll; int searchflags = flags_arg; + bool valid; pat = xmalloc(len + 7); @@ -3718,6 +3762,7 @@ find_decl ( /* Search forward for the identifier, ignore comment lines. */ clearpos(&found_pos); for (;; ) { + valid = false; t = searchit(curwin, curbuf, &curwin->w_cursor, FORWARD, pat, 1L, searchflags, RE_LAST, (linenr_T)0, NULL); if (curwin->w_cursor.lnum >= old_pos.lnum) @@ -3748,20 +3793,35 @@ find_decl ( curwin->w_cursor.col = 0; continue; } - if (!locally) /* global search: use first match found */ + valid = is_ident(get_cursor_line_ptr(), curwin->w_cursor.col); + + // If the current position is not a valid identifier and a previous match is + // present, favor that one instead. + if (!valid && found_pos.lnum != 0) { + curwin->w_cursor = found_pos; break; - if (curwin->w_cursor.lnum >= par_pos.lnum) { - /* If we previously found a valid position, use it. */ - if (found_pos.lnum != 0) + } + // global search: use first match found + if (valid && !locally) { + break; + } + if (valid && curwin->w_cursor.lnum >= par_pos.lnum) { + // If we previously found a valid position, use it. + if (found_pos.lnum != 0) { curwin->w_cursor = found_pos; + } break; } - // For finding a local variable and the match is before the "{" search - // to find a later match. For K&R style function declarations this - // skips the function header without types. Remove SEARCH_START from - // flags to avoid getting stuck at one position. - found_pos = curwin->w_cursor; + // For finding a local variable and the match is before the "{" or + // inside a comment, continue searching. For K&R style function + // declarations this skips the function header without types. + if (!valid) { + clearpos(&found_pos); + } else { + found_pos = curwin->w_cursor; + } + // Remove SEARCH_START from flags to avoid getting stuck at one position. searchflags &= ~SEARCH_START; } @@ -4465,23 +4525,22 @@ static void nv_exmode(cmdarg_T *cap) } } -/* - * Handle a ":" command. - */ +/// Handle a ":" command and <Cmd>. static void nv_colon(cmdarg_T *cap) { int old_p_im; bool cmd_result; + bool is_cmdkey = cap->cmdchar == K_COMMAND; - if (VIsual_active) + if (VIsual_active && !is_cmdkey) { nv_operator(cap); - else { + } else { if (cap->oap->op_type != OP_NOP) { // Using ":" as a movement is characterwise exclusive. cap->oap->motion_type = kMTCharWise; cap->oap->inclusive = false; - } else if (cap->count0) { - /* translate "count:" into ":.,.+(count - 1)" */ + } else if (cap->count0 && !is_cmdkey) { + // translate "count:" into ":.,.+(count - 1)" stuffcharReadbuff('.'); if (cap->count0 > 1) { stuffReadbuff(",.+"); @@ -4495,9 +4554,9 @@ static void nv_colon(cmdarg_T *cap) old_p_im = p_im; - /* get a command line and execute it */ - cmd_result = do_cmdline(NULL, getexline, NULL, - cap->oap->op_type != OP_NOP ? DOCMD_KEEPLINE : 0); + // get a command line and execute it + cmd_result = do_cmdline(NULL, is_cmdkey ? getcmdkeycmd : getexline, NULL, + cap->oap->op_type != OP_NOP ? DOCMD_KEEPLINE : 0); /* If 'insertmode' changed, enter or exit Insert mode */ if (p_im != old_p_im) { @@ -4978,26 +5037,21 @@ static void nv_right(cmdarg_T *cap) if ((!PAST_LINE && oneright() == false) || (PAST_LINE && *get_cursor_pos_ptr() == NUL) ) { - /* - * <Space> wraps to next line if 'whichwrap' has 's'. - * 'l' wraps to next line if 'whichwrap' has 'l'. - * CURS_RIGHT wraps to next line if 'whichwrap' has '>'. - */ - if ( ((cap->cmdchar == ' ' - && vim_strchr(p_ww, 's') != NULL) - || (cap->cmdchar == 'l' - && vim_strchr(p_ww, 'l') != NULL) - || (cap->cmdchar == K_RIGHT - && vim_strchr(p_ww, '>') != NULL)) - && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { - /* When deleting we also count the NL as a character. - * Set cap->oap->inclusive when last char in the line is - * included, move to next line after that */ - if ( cap->oap->op_type != OP_NOP - && !cap->oap->inclusive - && !lineempty(curwin->w_cursor.lnum)) + // <Space> wraps to next line if 'whichwrap' has 's'. + // 'l' wraps to next line if 'whichwrap' has 'l'. + // CURS_RIGHT wraps to next line if 'whichwrap' has '>'. + if (((cap->cmdchar == ' ' && vim_strchr(p_ww, 's') != NULL) + || (cap->cmdchar == 'l' && vim_strchr(p_ww, 'l') != NULL) + || (cap->cmdchar == K_RIGHT && vim_strchr(p_ww, '>') != NULL)) + && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { + // When deleting we also count the NL as a character. + // Set cap->oap->inclusive when last char in the line is + // included, move to next line after that + if (cap->oap->op_type != OP_NOP + && !cap->oap->inclusive + && !LINEEMPTY(curwin->w_cursor.lnum)) { cap->oap->inclusive = true; - else { + } else { ++curwin->w_cursor.lnum; curwin->w_cursor.col = 0; curwin->w_cursor.coladd = 0; @@ -5007,12 +5061,14 @@ static void nv_right(cmdarg_T *cap) continue; } if (cap->oap->op_type == OP_NOP) { - /* Only beep and flush if not moved at all */ - if (n == cap->count1) + // Only beep and flush if not moved at all + if (n == cap->count1) { beep_flush(); + } } else { - if (!lineempty(curwin->w_cursor.lnum)) + if (!LINEEMPTY(curwin->w_cursor.lnum)) { cap->oap->inclusive = true; + } } break; } else if (PAST_LINE) { @@ -5070,13 +5126,12 @@ static void nv_left(cmdarg_T *cap) coladvance((colnr_T)MAXCOL); curwin->w_set_curswant = true; - /* When the NL before the first char has to be deleted we - * put the cursor on the NUL after the previous line. - * This is a very special case, be careful! - * Don't adjust op_end now, otherwise it won't work. */ - if ( (cap->oap->op_type == OP_DELETE - || cap->oap->op_type == OP_CHANGE) - && !lineempty(curwin->w_cursor.lnum)) { + // When the NL before the first char has to be deleted we + // put the cursor on the NUL after the previous line. + // This is a very special case, be careful! + // Don't adjust op_end now, otherwise it won't work. + if ((cap->oap->op_type == OP_DELETE || cap->oap->op_type == OP_CHANGE) + && !LINEEMPTY(curwin->w_cursor.lnum)) { char_u *cp = get_cursor_pos_ptr(); if (*cp != NUL) { @@ -5175,12 +5230,12 @@ static void nv_gotofile(cmdarg_T *cap) if (ptr != NULL) { // do autowrite if necessary - if (curbufIsChanged() && curbuf->b_nwindows <= 1 && !P_HID(curbuf)) { + if (curbufIsChanged() && curbuf->b_nwindows <= 1 && !buf_hide(curbuf)) { (void)autowrite(curbuf, false); } setpcmark(); (void)do_ecmd(0, ptr, NULL, NULL, ECMD_LAST, - P_HID(curbuf) ? ECMD_HIDE : 0, curwin); + buf_hide(curbuf) ? ECMD_HIDE : 0, curwin); if (cap->nchar == 'F' && lnum >= 0) { curwin->w_cursor.lnum = lnum; check_cursor_lnum(); @@ -5634,6 +5689,8 @@ static void nv_brackets(cmdarg_T *cap) cap->nchar == 's', false, NULL) == 0) { clearopbeep(cap->oap); break; + } else { + curwin->w_set_curswant = true; } if (cap->oap->op_type == OP_NOP && (fdo_flags & FDO_SEARCH) && KeyTyped) foldOpenCursor(); @@ -5803,10 +5860,13 @@ static void nv_replace(cmdarg_T *cap) if (got_int) reset_VIsual(); if (had_ctrl_v) { - if (cap->nchar == '\r') - cap->nchar = -1; - else if (cap->nchar == '\n') - cap->nchar = -2; + // Use a special (negative) number to make a difference between a + // literal CR or NL and a line break. + if (cap->nchar == CAR) { + cap->nchar = REPLACE_CR_NCHAR; + } else if (cap->nchar == NL) { + cap->nchar = REPLACE_NL_NCHAR; + } } nv_operator(cap); return; @@ -6043,10 +6103,11 @@ static void n_swapchar(cmdarg_T *cap) pos_T startpos; int did_change = 0; - if (checkclearopq(cap->oap)) + if (checkclearopq(cap->oap)) { return; + } - if (lineempty(curwin->w_cursor.lnum) && vim_strchr(p_ww, '~') == NULL) { + if (LINEEMPTY(curwin->w_cursor.lnum) && vim_strchr(p_ww, '~') == NULL) { clearopbeep(cap->oap); return; } @@ -6209,15 +6270,18 @@ static void nv_gomark(cmdarg_T *cap) } else nv_cursormark(cap, cap->arg, pos); - /* May need to clear the coladd that a mark includes. */ - if (!virtual_active()) + // May need to clear the coladd that a mark includes. + if (!virtual_active()) { curwin->w_cursor.coladd = 0; + } + check_cursor_col(); if (cap->oap->op_type == OP_NOP && pos != NULL && (pos == (pos_T *)-1 || !equalpos(old_cursor, *pos)) && (fdo_flags & FDO_MARK) - && old_KeyTyped) + && old_KeyTyped) { foldOpenCursor(); + } } /* @@ -7908,18 +7972,7 @@ static void nv_event(cmdarg_T *cap) may_garbage_collect = false; multiqueue_process_events(main_loop.events); cap->retval |= CA_COMMAND_BUSY; // don't call edit() now -} - -/// Trigger FocusGained event. -static void nv_focusgained(cmdarg_T *cap) -{ - apply_autocmds(EVENT_FOCUSGAINED, NULL, NULL, false, curbuf); -} - -/// Trigger FocusLost event. -static void nv_focuslost(cmdarg_T *cap) -{ - apply_autocmds(EVENT_FOCUSLOST, NULL, NULL, false, curbuf); + finish_op = false; } /* diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 5c6f4d0d07..b421d81b7e 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -55,12 +55,11 @@ static yankreg_T y_regs[NUM_REGISTERS]; static yankreg_T *y_previous = NULL; /* ptr to last written yankreg */ -static bool clipboard_didwarn_unnamed = false; - // for behavior between start_batch_changes() and end_batch_changes()) -static bool clipboard_delay_update = false; // delay clipboard update static int batch_change_count = 0; // inside a script +static bool clipboard_delay_update = false; // delay clipboard update static bool clipboard_needs_update = false; // clipboard was updated +static bool clipboard_didwarn = false; /* * structure used by block_prep, op_delete and op_yank for blockwise operators @@ -1630,13 +1629,18 @@ int op_replace(oparg_T *oap, int c) colnr_T oldlen; struct block_def bd; char_u *after_p = NULL; - int had_ctrl_v_cr = (c == -1 || c == -2); + int had_ctrl_v_cr = false; if ((curbuf->b_ml.ml_flags & ML_EMPTY ) || oap->empty) return OK; /* nothing to do */ - if (had_ctrl_v_cr) - c = (c == -1 ? '\r' : '\n'); + if (c == REPLACE_CR_NCHAR) { + had_ctrl_v_cr = true; + c = CAR; + } else if (c == REPLACE_NL_NCHAR) { + had_ctrl_v_cr = true; + c = NL; + } if (has_mbyte) mb_adjust_opend(oap); @@ -1714,7 +1718,7 @@ int op_replace(oparg_T *oap, int c) // insert pre-spaces memset(newp + bd.textcol, ' ', (size_t)bd.startspaces); // insert replacement chars CHECK FOR ALLOCATED SPACE - // -1/-2 is used for entering CR literally. + // REPLACE_CR_NCHAR/REPLACE_NL_NCHAR is used for entering CR literally. size_t after_p_len = 0; if (had_ctrl_v_cr || (c != '\r' && c != '\n')) { // strlen(newp) at this point @@ -2053,15 +2057,16 @@ void op_insert(oparg_T *oap, long count1) curwin->w_cursor = oap->end; check_cursor_col(); - /* Works just like an 'i'nsert on the next character. */ - if (!lineempty(curwin->w_cursor.lnum) - && oap->start_vcol != oap->end_vcol) + // Works just like an 'i'nsert on the next character. + if (!LINEEMPTY(curwin->w_cursor.lnum) + && oap->start_vcol != oap->end_vcol) { inc_cursor(); + } } } t1 = oap->start; - edit(NUL, false, (linenr_T)count1); + (void)edit(NUL, false, (linenr_T)count1); // When a tab was inserted, and the characters in front of the tab // have been converted to a tab as well, the column of the cursor @@ -2181,9 +2186,10 @@ int op_change(oparg_T *oap) } else if (op_delete(oap) == FAIL) return FALSE; - if ((l > curwin->w_cursor.col) && !lineempty(curwin->w_cursor.lnum) - && !virtual_op) + if ((l > curwin->w_cursor.col) && !LINEEMPTY(curwin->w_cursor.lnum) + && !virtual_op) { inc_cursor(); + } // check for still on same line (<CR> in inserted text meaningless) // skip blank lines too @@ -2565,11 +2571,11 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg) dict_T *dict = get_vim_var_dict(VV_EVENT); // the yanked text - list_T *list = tv_list_alloc(); + list_T *const list = tv_list_alloc((ptrdiff_t)reg->y_size); for (size_t i = 0; i < reg->y_size; i++) { tv_list_append_string(list, (const char *)reg->y_array[i], -1); } - list->lv_lock = VAR_FIXED; + tv_list_set_lock(list, VAR_FIXED); tv_dict_add_list(dict, S_LEN("regcontents"), list); // the register type @@ -2736,7 +2742,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) // Autocommands may be executed when saving lines for undo, which may make // y_array invalid. Start undo now to avoid that. if (u_save(curwin->w_cursor.lnum, curwin->w_cursor.lnum + 1) == FAIL) { - ELOG(_("Failed to save undo information")); + ELOG("Failed to save undo information"); return; } } @@ -2856,25 +2862,30 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } } else if (y_type == kMTLineWise) { lnum = curwin->w_cursor.lnum; - /* Correct line number for closed fold. Don't move the cursor yet, - * u_save() uses it. */ - if (dir == BACKWARD) + // Correct line number for closed fold. Don't move the cursor yet, + // u_save() uses it. + if (dir == BACKWARD) { (void)hasFolding(lnum, &lnum, NULL); - else + } else { (void)hasFolding(lnum, NULL, &lnum); - if (dir == FORWARD) - ++lnum; - /* In an empty buffer the empty line is going to be replaced, include - * it in the saved lines. */ - if ((bufempty() ? u_save(0, 2) : u_save(lnum - 1, lnum)) == FAIL) + } + if (dir == FORWARD) { + lnum++; + } + // In an empty buffer the empty line is going to be replaced, include + // it in the saved lines. + if ((BUFEMPTY() ? u_save(0, 2) : u_save(lnum - 1, lnum)) == FAIL) { goto end; - if (dir == FORWARD) + } + if (dir == FORWARD) { curwin->w_cursor.lnum = lnum - 1; - else + } else { curwin->w_cursor.lnum = lnum; - curbuf->b_op_start = curwin->w_cursor; /* for mark_adjust() */ - } else if (u_save_cursor() == FAIL) + } + curbuf->b_op_start = curwin->w_cursor; // for mark_adjust() + } else if (u_save_cursor() == FAIL) { goto end; + } yanklen = (int)STRLEN(y_array[0]); @@ -3067,15 +3078,26 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) --lnum; new_cursor = curwin->w_cursor; - /* - * simple case: insert into current line - */ + // simple case: insert into current line if (y_type == kMTCharWise && y_size == 1) { + linenr_T end_lnum = 0; // init for gcc + + if (VIsual_active) { + end_lnum = curbuf->b_visual.vi_end.lnum; + if (end_lnum < curbuf->b_visual.vi_start.lnum) { + end_lnum = curbuf->b_visual.vi_start.lnum; + } + } + do { totlen = (size_t)(count * yanklen); if (totlen > 0) { oldp = ml_get(lnum); - newp = (char_u *) xmalloc((size_t)(STRLEN(oldp) + totlen + 1)); + if (VIsual_active && col > (int)STRLEN(oldp)) { + lnum++; + continue; + } + newp = (char_u *)xmalloc((size_t)(STRLEN(oldp) + totlen + 1)); memmove(newp, oldp, (size_t)col); ptr = newp + col; for (i = 0; i < (size_t)count; i++) { @@ -3091,11 +3113,10 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) curwin->w_cursor.col += (colnr_T)(totlen - 1); } } - if (VIsual_active) + if (VIsual_active) { lnum++; - } while (VIsual_active - && (lnum <= curbuf->b_visual.vi_end.lnum - || lnum <= curbuf->b_visual.vi_start.lnum)); + } + } while (VIsual_active && lnum <= end_lnum); if (VIsual_active) { /* reset lnum to the last visual line */ lnum--; @@ -3178,9 +3199,9 @@ error: curbuf->b_op_start.lnum++; } // Skip mark_adjust when adding lines after the last one, there - // can't be marks there. + // can't be marks there. But still needed in diff mode. if (curbuf->b_op_start.lnum + (y_type == kMTCharWise) - 1 + nr_lines - < curbuf->b_ml.ml_line_count) { + < curbuf->b_ml.ml_line_count || curwin->w_p_diff) { mark_adjust(curbuf->b_op_start.lnum + (y_type == kMTCharWise), (linenr_T)MAXLNUM, nr_lines, 0L, false); } @@ -3998,7 +4019,7 @@ format_lines ( && (do_second_indent || do_number_indent) && prev_is_end_par && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { - if (do_second_indent && !lineempty(curwin->w_cursor.lnum + 1)) { + if (do_second_indent && !LINEEMPTY(curwin->w_cursor.lnum + 1)) { if (leader_len == 0 && next_leader_len == 0) { /* no comment found */ second_indent = @@ -4855,9 +4876,8 @@ static void *get_reg_wrap_one_line(char_u *s, int flags) if (!(flags & kGRegList)) { return s; } - list_T *list = tv_list_alloc(); - tv_list_append_string(list, NULL, 0); - list->lv_first->li_tv.vval.v_string = s; + list_T *const list = tv_list_alloc(1); + tv_list_append_allocated_string(list, (char *)s); return list; } @@ -4906,7 +4926,7 @@ void *get_reg_contents(int regname, int flags) return NULL; if (flags & kGRegList) { - list_T *list = tv_list_alloc(); + list_T *const list = tv_list_alloc((ptrdiff_t)reg->y_size); for (size_t i = 0; i < reg->y_size; i++) { tv_list_append_string(list, (const char *)reg->y_array[i], -1); } @@ -5524,7 +5544,7 @@ int get_default_register_name(void) } /// Determine if register `*name` should be used as a clipboard. -/// In an unnammed operation, `*name` is `NUL` and will be adjusted to `'*'/'+'` if +/// In an unnamed operation, `*name` is `NUL` and will be adjusted to */+ if /// `clipboard=unnamed[plus]` is set. /// /// @param name The name of register, or `NUL` if unnamed. @@ -5535,33 +5555,41 @@ int get_default_register_name(void) /// if the register isn't a clipboard or provider isn't available. static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing) { - if (*name == '*' || *name == '+') { - if(!eval_has_provider("clipboard")) { - if (!quiet) { - EMSG("clipboard: No provider. Try \":CheckHealth\" or " - "\":h clipboard\"."); - } - return NULL; - } - return &y_regs[*name == '*' ? STAR_REGISTER : PLUS_REGISTER]; - } else if ((*name == NUL) && (cb_flags & CB_UNNAMEDMASK)) { - if(!eval_has_provider("clipboard")) { - if (!quiet && !clipboard_didwarn_unnamed) { - msg((char_u *)"clipboard: No provider. Try \":CheckHealth\" or " - "\":h clipboard\"."); - clipboard_didwarn_unnamed = true; - } - return NULL; +#define MSG_NO_CLIP "clipboard: No provider. " \ + "Try \":checkhealth\" or \":h clipboard\"." + + yankreg_T *target = NULL; + bool explicit_cb_reg = (*name == '*' || *name == '+'); + bool implicit_cb_reg = (*name == NUL) && (cb_flags & CB_UNNAMEDMASK); + if (!explicit_cb_reg && !implicit_cb_reg) { + goto end; + } + + if (!eval_has_provider("clipboard")) { + if (batch_change_count == 1 && !quiet + && (!clipboard_didwarn || (explicit_cb_reg && !redirecting()))) { + clipboard_didwarn = true; + // Do NOT error (emsg()) here--if it interrupts :redir we get into + // a weird state, stuck in "redirect mode". + msg((char_u *)MSG_NO_CLIP); } + // ... else, be silent (don't flood during :while, :redir, etc.). + goto end; + } + + if (explicit_cb_reg) { + target = &y_regs[*name == '*' ? STAR_REGISTER : PLUS_REGISTER]; + goto end; + } else { // unnamed register: "implicit" clipboard if (writing && clipboard_delay_update) { + // For "set" (copy), defer the clipboard call. clipboard_needs_update = true; - return NULL; + goto end; } else if (!writing && clipboard_needs_update) { - // use the internal value - return NULL; + // For "get" (paste), use the internal value. + goto end; } - yankreg_T *target; if (cb_flags & CB_UNNAMEDPLUS) { *name = (cb_flags & CB_UNNAMED && writing) ? '"': '+'; target = &y_regs[PLUS_REGISTER]; @@ -5569,10 +5597,11 @@ static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing) *name = '*'; target = &y_regs[STAR_REGISTER]; } - return target; // unnamed register + goto end; } - // don't do anything for other register names - return NULL; + +end: + return target; } static bool get_clipboard(int name, yankreg_T **target, bool quiet) @@ -5586,7 +5615,7 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) } free_register(reg); - list_T *const args = tv_list_alloc(); + list_T *const args = tv_list_alloc(1); const char regname = (char)name; tv_list_append_string(args, ®name, 1); @@ -5602,13 +5631,14 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) list_T *res = result.vval.v_list; list_T *lines = NULL; - if (res->lv_len == 2 && res->lv_first->li_tv.v_type == VAR_LIST) { - lines = res->lv_first->li_tv.vval.v_list; - if (res->lv_last->li_tv.v_type != VAR_STRING) { + if (tv_list_len(res) == 2 + && TV_LIST_ITEM_TV(tv_list_first(res))->v_type == VAR_LIST) { + lines = TV_LIST_ITEM_TV(tv_list_first(res))->vval.v_list; + if (TV_LIST_ITEM_TV(tv_list_last(res))->v_type != VAR_STRING) { goto err; } - char_u *regtype = res->lv_last->li_tv.vval.v_string; - if (regtype == NULL || strlen((char*)regtype) > 1) { + char_u *regtype = TV_LIST_ITEM_TV(tv_list_last(res))->vval.v_string; + if (regtype == NULL || strlen((char *)regtype) > 1) { goto err; } switch (regtype[0]) { @@ -5633,20 +5663,21 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet) reg->y_type = kMTUnknown; } - reg->y_array = xcalloc((size_t)lines->lv_len, sizeof(uint8_t *)); - reg->y_size = (size_t)lines->lv_len; + reg->y_array = xcalloc((size_t)tv_list_len(lines), sizeof(char_u *)); + reg->y_size = (size_t)tv_list_len(lines); reg->additional_data = NULL; reg->timestamp = 0; // Timestamp is not saved for clipboard registers because clipboard registers // are not saved in the ShaDa file. int i = 0; - for (listitem_T *li = lines->lv_first; li != NULL; li = li->li_next) { - if (li->li_tv.v_type != VAR_STRING) { + TV_LIST_ITER_CONST(lines, li, { + if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING) { goto err; } - reg->y_array[i++] = (char_u *)xstrdupnul((char *)li->li_tv.vval.v_string); - } + reg->y_array[i++] = (char_u *)xstrdupnul( + (const char *)TV_LIST_ITEM_TV(li)->vval.v_string); + }); if (reg->y_size > 0 && strlen((char*)reg->y_array[reg->y_size-1]) == 0) { // a known-to-be charwise yank might have a final linebreak @@ -5703,15 +5734,13 @@ static void set_clipboard(int name, yankreg_T *reg) return; } - list_T *lines = tv_list_alloc(); + list_T *const lines = tv_list_alloc( + (ptrdiff_t)reg->y_size + (reg->y_type != kMTCharWise)); for (size_t i = 0; i < reg->y_size; i++) { tv_list_append_string(lines, (const char *)reg->y_array[i], -1); } - list_T *args = tv_list_alloc(); - tv_list_append_list(args, lines); - char regtype; switch (reg->y_type) { case kMTLineWise: { @@ -5732,25 +5761,25 @@ static void set_clipboard(int name, yankreg_T *reg) assert(false); } } - tv_list_append_string(args, ®type, 1); - const char regname = (char)name; - tv_list_append_string(args, ®name, 1); + list_T *args = tv_list_alloc(3); + tv_list_append_list(args, lines); + tv_list_append_string(args, ®type, 1); + tv_list_append_string(args, ((char[]) { (char)name }), 1); (void)eval_call_provider("clipboard", "set", args); } -/// Avoid clipboard (slow) during batch operations (i.e., a script). +/// Avoid slow things (clipboard) during batch operations (while/for-loops). void start_batch_changes(void) { if (++batch_change_count > 1) { return; } clipboard_delay_update = true; - clipboard_needs_update = false; } -/// Update the clipboard after batch changes finished. +/// Counterpart to start_batch_changes(). void end_batch_changes(void) { if (--batch_change_count > 0) { @@ -5759,11 +5788,37 @@ void end_batch_changes(void) } clipboard_delay_update = false; if (clipboard_needs_update) { + // must be before, as set_clipboard will invoke + // start/end_batch_changes recursively + clipboard_needs_update = false; + // unnamed ("implicit" clipboard) set_clipboard(NUL, y_previous); + } +} + +int save_batch_count(void) +{ + int save_count = batch_change_count; + batch_change_count = 0; + clipboard_delay_update = false; + if (clipboard_needs_update) { clipboard_needs_update = false; + // unnamed ("implicit" clipboard) + set_clipboard(NUL, y_previous); + } + return save_count; +} + +void restore_batch_count(int save_count) +{ + assert(batch_change_count == 0); + batch_change_count = save_count; + if (batch_change_count > 0) { + clipboard_delay_update = true; } } + /// Check whether register is empty static inline bool reg_empty(const yankreg_T *const reg) FUNC_ATTR_PURE diff --git a/src/nvim/option.c b/src/nvim/option.c index a4be2abf9a..41cfdf9856 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -75,7 +75,9 @@ #include "nvim/undo.h" #include "nvim/window.h" #include "nvim/os/os.h" +#include "nvim/api/private/helpers.h" #include "nvim/os/input.h" +#include "nvim/os/lang.h" /* * The options that are local to a window or buffer have "indir" set to one of @@ -106,6 +108,9 @@ typedef enum { */ #define VAR_WIN ((char_u *)-1) +static char *p_term = NULL; +static char *p_ttytype = NULL; + /* * These are the global values for options which are also local to a buffer. * Only to be used in option.c! @@ -116,6 +121,7 @@ static int p_bomb; static char_u *p_bh; static char_u *p_bt; static int p_bl; +static long p_channel; static int p_ci; static int p_cin; static char_u *p_cink; @@ -243,6 +249,8 @@ typedef struct vimoption { #define P_NO_DEF_EXP 0x8000000U ///< Do not expand default value. #define P_RWINONLY 0x10000000U ///< only redraw current window +#define P_NDNAME 0x20000000U ///< only normal dir name chars allowed +#define P_UI_OPTION 0x40000000U ///< send option to remote ui #define HIGHLIGHT_INIT \ "8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText," \ @@ -780,6 +788,8 @@ void set_init_1(void) didset_options2(); + lang_init(); + // enc_locale() will try to find the encoding of the current locale. // This will be used when 'default' is used as encoding specifier // in 'fileencodings' @@ -822,24 +832,26 @@ set_option_default ( if (flags & P_STRING) { /* Use set_string_option_direct() for local options to handle * freeing and allocating the value. */ - if (options[opt_idx].indir != PV_NONE) + if (options[opt_idx].indir != PV_NONE) { set_string_option_direct(NULL, opt_idx, - options[opt_idx].def_val[dvi], opt_flags, 0); - else { - if ((opt_flags & OPT_FREE) && (flags & P_ALLOCED)) + options[opt_idx].def_val[dvi], opt_flags, 0); + } else { + if ((opt_flags & OPT_FREE) && (flags & P_ALLOCED)) { free_string_option(*(char_u **)(varp)); + } *(char_u **)varp = options[opt_idx].def_val[dvi]; options[opt_idx].flags &= ~P_ALLOCED; } } else if (flags & P_NUM) { - if (options[opt_idx].indir == PV_SCROLL) + if (options[opt_idx].indir == PV_SCROLL) { win_comp_scroll(curwin); - else { - *(long *)varp = (long)options[opt_idx].def_val[dvi]; - /* May also set global value for local option. */ - if (both) + } else { + *(long *)varp = (long)(intptr_t)options[opt_idx].def_val[dvi]; + // May also set global value for local option. + if (both) { *(long *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) = *(long *)varp; + } } } else { /* P_BOOL */ *(int *)varp = (int)(intptr_t)options[opt_idx].def_val[dvi]; @@ -917,7 +929,7 @@ void set_number_default(char *name, long val) opt_idx = findoption(name); if (opt_idx >= 0) { - options[opt_idx].def_val[VI_DEFAULT] = (char_u *)val; + options[opt_idx].def_val[VI_DEFAULT] = (char_u *)(intptr_t)val; } } @@ -970,10 +982,13 @@ void set_init_2(bool headless) p_window = Rows - 1; } set_number_default("window", Rows - 1); +#if 0 + // This bodges around problems that should be fixed in the TUI layer. if (!headless && !os_term_is_nice()) { set_string_option_direct((char_u *)"guicursor", -1, (char_u *)"", OPT_GLOBAL, SID_NONE); } +#endif parse_shape_opt(SHAPE_CURSOR); // set cursor shapes from 'guicursor' (void)parse_printoptions(); // parse 'printoptions' default value } @@ -1045,7 +1060,7 @@ void set_init_3(void) xfree(p); } - if (bufempty()) { + if (BUFEMPTY()) { int idx_ffs = findoption_len(S_LEN("ffs")); // Apply the first entry of 'fileformats' to the initial buffer. @@ -1178,15 +1193,12 @@ do_set ( set_options_default(OPT_FREE | opt_flags); didset_options(); didset_options2(); + ui_refresh_options(); redraw_all_later(CLEAR); } else { showoptions(1, opt_flags); did_show = TRUE; } - } else if (STRNCMP(arg, "termcap", - 7) == 0 && !(opt_flags & OPT_MODELINE)) { - did_show = TRUE; - arg += 7; } else { prefix = 1; if (STRNCMP(arg, "no", 2) == 0) { @@ -1431,20 +1443,19 @@ do_set ( * [-]0-9 set number * other error */ - ++arg; - if (nextchar == '&') - value = (long)options[opt_idx].def_val[ - ((flags & P_VI_DEF) || cp_val) - ? VI_DEFAULT : VIM_DEFAULT]; - else if (nextchar == '<') { - /* For 'undolevels' NO_LOCAL_UNDOLEVEL means to - * use the global value. */ - if ((long *)varp == &curbuf->b_p_ul - && opt_flags == OPT_LOCAL) + arg++; + if (nextchar == '&') { + value = (long)(intptr_t)options[opt_idx].def_val[ + ((flags & P_VI_DEF) || cp_val) ? VI_DEFAULT : VIM_DEFAULT]; + } else if (nextchar == '<') { + // For 'undolevels' NO_LOCAL_UNDOLEVEL means to + // use the global value. + if ((long *)varp == &curbuf->b_p_ul && opt_flags == OPT_LOCAL) { value = NO_LOCAL_UNDOLEVEL; - else + } else { value = *(long *)get_varp_scope( &(options[opt_idx]), OPT_GLOBAL); + } } else if (((long *)varp == &p_wc || (long *)varp == &p_wcm) && (*arg == '<' @@ -1487,6 +1498,7 @@ do_set ( char_u *newval; char_u *origval = NULL; char *saved_origval = NULL; + char *saved_newval = NULL; unsigned newlen; int comma; int bs; @@ -1503,7 +1515,17 @@ do_set ( /* The old value is kept until we are sure that the * new value is valid. */ oldval = *(char_u **)varp; - if (nextchar == '&') { /* set to default val */ + + // When setting the local value of a global + // option, the old value may be the global value. + if (((int)options[opt_idx].indir & PV_BOTH) && (opt_flags + & OPT_LOCAL)) { + origval = *(char_u **)get_varp(&options[opt_idx]); + } else { + origval = oldval; + } + + if (nextchar == '&') { // set to default val newval = options[opt_idx].def_val[ ((flags & P_VI_DEF) || cp_val) ? VI_DEFAULT : VIM_DEFAULT]; @@ -1562,6 +1584,9 @@ do_set ( break; } xfree(oldval); + if (origval == oldval) { + origval = *(char_u **)varp; + } oldval = *(char_u **)varp; } /* @@ -1598,15 +1623,6 @@ do_set ( ++arg; } - /* When setting the local value of a global - * option, the old value may be the global value. */ - if (((int)options[opt_idx].indir & PV_BOTH) - && (opt_flags & OPT_LOCAL)) - origval = *(char_u **)get_varp( - &options[opt_idx]); - else - origval = oldval; - /* * Copy the new string into allocated memory. * Can't use set_string_option_direct(), because @@ -1750,7 +1766,7 @@ do_set ( if (flags & P_FLAGLIST) { // Remove flags that appear twice. - for (s = newval; *s; s++) { + for (s = newval; *s;) { // if options have P_FLAGLIST and P_ONECOMMA such as // 'whichwrap' if (flags & P_ONECOMMA) { @@ -1758,15 +1774,16 @@ do_set ( && vim_strchr(s + 2, *s) != NULL) { // Remove the duplicated value and the next comma. STRMOVE(s, s + 2); - s -= 2; + continue; } } else { if ((!(flags & P_COMMA) || *s != ',') && vim_strchr(s + 1, *s) != NULL) { STRMOVE(s, s + 1); - s--; + continue; } } + s++; } } @@ -1775,39 +1792,37 @@ do_set ( new_value_alloced = TRUE; } - /* Set the new value. */ + // Set the new value. *(char_u **)(varp) = newval; - if (!starting && origval != NULL) { + if (!starting && origval != NULL && newval != NULL) { // origval may be freed by // did_set_string_option(), make a copy. - saved_origval = xstrdup((char *) origval); + saved_origval = xstrdup((char *)origval); + // newval (and varp) may become invalid if the + // buffer is closed by autocommands. + saved_newval = xstrdup((char *)newval); } - /* Handle side effects, and set the global value for - * ":set" on local options. */ + // Handle side effects, and set the global value for + // ":set" on local options. Note: when setting 'syntax' + // or 'filetype' autocommands may be triggered that can + // cause havoc. errmsg = did_set_string_option(opt_idx, (char_u **)varp, new_value_alloced, oldval, errbuf, opt_flags); + if (errmsg == NULL) { + trigger_optionsset_string(opt_idx, opt_flags, saved_origval, + saved_newval); + } + xfree(saved_origval); + xfree(saved_newval); + // If error detected, print the error message. if (errmsg != NULL) { - xfree(saved_origval); goto skip; } - if (saved_origval != NULL) { - char buf_type[7]; - vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s", - (opt_flags & OPT_LOCAL) ? "local" : "global"); - set_vim_var_string(VV_OPTION_NEW, *(char **) varp, -1); - set_vim_var_string(VV_OPTION_OLD, saved_origval, -1); - set_vim_var_string(VV_OPTION_TYPE, buf_type, -1); - apply_autocmds(EVENT_OPTIONSET, - (char_u *)options[opt_idx].fullname, - NULL, false, NULL); - reset_v_option_vars(); - xfree(saved_origval); - } } else { // key code option(FIXME(tarruda): Show a warning or something // similar) @@ -2202,6 +2217,7 @@ void check_buf_options(buf_T *buf) check_string_option(&buf->b_p_tsr); check_string_option(&buf->b_p_lw); check_string_option(&buf->b_p_bkc); + check_string_option(&buf->b_p_menc); } /* @@ -2243,7 +2259,7 @@ int was_set_insecurely(char_u *opt, int opt_flags) uint32_t *flagp = insecure_flag(idx, opt_flags); return (*flagp & P_INSECURE) != 0; } - EMSG2(_(e_intern2), "was_set_insecurely()"); + internal_error("was_set_insecurely()"); return -1; } @@ -2302,8 +2318,8 @@ set_string_option_direct ( if (idx == -1) { // Use name. idx = findoption((const char *)name); if (idx < 0) { // Not found (should not happen). - EMSG2(_(e_intern2), "set_string_option_direct()"); - EMSG2(_("For option %s"), name); + internal_error("set_string_option_direct()"); + IEMSG2(_("For option %s"), name); return; } } @@ -2389,6 +2405,7 @@ static char *set_string_option(const int opt_idx, const char *const value, *varp = s; char *const saved_oldval = (starting ? NULL : xstrdup(oldval)); + char *const saved_newval = (starting ? NULL : xstrdup(s)); char *const r = (char *)did_set_string_option( opt_idx, (char_u **)varp, (int)true, (char_u *)oldval, NULL, opt_flags); @@ -2397,19 +2414,12 @@ static char *set_string_option(const int opt_idx, const char *const value, } // call autocommand after handling side effects - if (saved_oldval != NULL) { - char buf_type[7]; - vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s", - (opt_flags & OPT_LOCAL) ? "local" : "global"); - set_vim_var_string(VV_OPTION_NEW, (char *)(*varp), -1); - set_vim_var_string(VV_OPTION_OLD, saved_oldval, -1); - set_vim_var_string(VV_OPTION_TYPE, buf_type, -1); - apply_autocmds(EVENT_OPTIONSET, - (char_u *)options[opt_idx].fullname, - NULL, false, NULL); - reset_v_option_vars(); - xfree(saved_oldval); + if (r == NULL) { + trigger_optionsset_string(opt_idx, opt_flags, + saved_oldval, saved_newval); } + xfree(saved_oldval); + xfree(saved_newval); return r; } @@ -2426,6 +2436,11 @@ static bool valid_filetype(char_u *val) return true; } +#ifdef _MSC_VER +// MSVC optimizations are disabled for this function because it +// incorrectly generates an empty string for SHM_ALL. +#pragma optimize("", off) +#endif /* * Handle string options that need some action to perform when changed. * Returns NULL for success, or an error message for an error. @@ -2445,6 +2460,7 @@ did_set_string_option ( int did_chartab = FALSE; char_u **gvarp; bool free_oldval = (options[opt_idx].flags & P_ALLOCED); + int ft_changed = false; /* Get the global option to compare with, otherwise we would have to check * two values for all local options. */ @@ -2454,12 +2470,14 @@ did_set_string_option ( if ((secure || sandbox != 0) && (options[opt_idx].flags & P_SECURE)) { errmsg = e_secure; - } - /* Check for a "normal" file name in some options. Disallow a path - * separator (slash and/or backslash), wildcards and characters that are - * often illegal in a file name. */ - else if ((options[opt_idx].flags & P_NFNAME) - && vim_strpbrk(*varp, (char_u *)"/\\*?[|<>") != NULL) { + } else if (((options[opt_idx].flags & P_NFNAME) + && vim_strpbrk(*varp, (char_u *)(secure ? "/\\*?[|;&<>\r\n" + : "/\\*?[<>\r\n")) != NULL) + || ((options[opt_idx].flags & P_NDNAME) + && vim_strpbrk(*varp, (char_u *)"*?[|;&<>\r\n") != NULL)) { + // Check for a "normal" directory or file name in some options. Disallow a + // path separator (slash and/or backslash), wildcards and characters that + // are often illegal in a file name. Be more permissive if "secure" is off. errmsg = e_invarg; } /* 'backupcopy' */ @@ -2614,8 +2632,8 @@ did_set_string_option ( else if (varp == &p_ei) { if (check_ei() == FAIL) errmsg = e_invarg; - /* 'encoding' and 'fileencoding' */ - } else if (varp == &p_enc || gvarp == &p_fenc) { + // 'encoding', 'fileencoding' and 'makeencoding' + } else if (varp == &p_enc || gvarp == &p_fenc || gvarp == &p_menc) { if (gvarp == &p_fenc) { if (!MODIFIABLE(curbuf) && opt_flags != OPT_GLOBAL) { errmsg = e_modifiable; @@ -2998,9 +3016,10 @@ did_set_string_option ( if (s[-1] == 'k' || s[-1] == 's') { /* skip optional filename after 'k' and 's' */ while (*s && *s != ',' && *s != ' ') { - if (*s == '\\') - ++s; - ++s; + if (*s == '\\' && s[1] != NUL) { + s++; + } + s++; } } else { if (errbuf != NULL) { @@ -3031,7 +3050,7 @@ did_set_string_option ( /* 'pastetoggle': translate key codes like in a mapping */ else if (varp == &p_pt) { if (*p_pt) { - (void)replace_termcodes(p_pt, STRLEN(p_pt), &p, true, true, false, + (void)replace_termcodes(p_pt, STRLEN(p_pt), &p, true, true, true, CPO_TO_CPO_FLAGS); if (p != NULL) { if (new_value_alloced) @@ -3161,6 +3180,8 @@ did_set_string_option ( } else if (gvarp == &p_ft) { if (!valid_filetype(*varp)) { errmsg = e_invarg; + } else { + ft_changed = STRCMP(oldval, *varp) != 0; } } else if (gvarp == &p_syn) { if (!valid_filetype(*varp)) { @@ -3173,17 +3194,18 @@ did_set_string_option ( } else { // Options that are a list of flags. p = NULL; - if (varp == &p_ww) + if (varp == &p_ww) { // 'whichwrap' p = (char_u *)WW_ALL; - if (varp == &p_shm) + } + if (varp == &p_shm) { // 'shortmess' p = (char_u *)SHM_ALL; - else if (varp == &(p_cpo)) + } else if (varp == &(p_cpo)) { // 'cpoptions' p = (char_u *)CPO_VI; - else if (varp == &(curbuf->b_p_fo)) + } else if (varp == &(curbuf->b_p_fo)) { // 'formatoptions' p = (char_u *)FO_ALL; - else if (varp == &curwin->w_p_cocu) + } else if (varp == &curwin->w_p_cocu) { // 'concealcursor' p = (char_u *)COCU_ALL; - else if (varp == &p_mouse) { + } else if (varp == &p_mouse) { // 'mouse' p = (char_u *)MOUSE_ALL; } if (p != NULL) { @@ -3242,10 +3264,12 @@ did_set_string_option ( apply_autocmds(EVENT_SYNTAX, curbuf->b_p_syn, curbuf->b_fname, TRUE, curbuf); } else if (varp == &(curbuf->b_p_ft)) { - /* 'filetype' is set, trigger the FileType autocommand */ - did_filetype = TRUE; - apply_autocmds(EVENT_FILETYPE, curbuf->b_p_ft, - curbuf->b_fname, TRUE, curbuf); + // 'filetype' is set, trigger the FileType autocommand + if (!(opt_flags & OPT_MODELINE) || ft_changed) { + did_filetype = true; + apply_autocmds(EVENT_FILETYPE, curbuf->b_p_ft, + curbuf->b_fname, true, curbuf); + } } if (varp == &(curwin->w_s->b_p_spl)) { char_u fname[200]; @@ -3286,6 +3310,9 @@ did_set_string_option ( return errmsg; } +#ifdef _MSC_VER +#pragma optimize("", on) +#endif /* * Simple int comparison function for use with qsort() @@ -3362,37 +3389,38 @@ skip: return NULL; /* no error */ } -/* - * Handle setting 'listchars' or 'fillchars'. - * Returns error message, NULL if it's OK. - */ + +/// Handle setting 'listchars' or 'fillchars'. +/// Assume monocell characters +/// +/// @param varp either &p_lcs ('listchars') or &p_fcs ('fillchar') +/// @return error message, NULL if it's OK. static char_u *set_chars_option(char_u **varp) { int round, i, len, entries; char_u *p, *s; int c1, c2 = 0; struct charstab { - int *cp; - char *name; + int *cp; ///< char value + char *name; ///< char id + int def; ///< default value }; - static struct charstab filltab[] = - { - {&fill_stl, "stl"}, - {&fill_stlnc, "stlnc"}, - {&fill_vert, "vert"}, - {&fill_fold, "fold"}, - {&fill_diff, "diff"}, + static struct charstab filltab[] = { + { &fill_stl, "stl" , ' ' }, + { &fill_stlnc, "stlnc", ' ' }, + { &fill_vert, "vert" , 9474 }, // │ + { &fill_fold, "fold" , 183 }, // · + { &fill_diff, "diff" , '-' }, }; - static struct charstab lcstab[] = - { - {&lcs_eol, "eol"}, - {&lcs_ext, "extends"}, - {&lcs_nbsp, "nbsp"}, - {&lcs_prec, "precedes"}, - {&lcs_space, "space"}, - {&lcs_tab2, "tab"}, - {&lcs_trail, "trail"}, - {&lcs_conceal, "conceal"}, + static struct charstab lcstab[] = { + { &lcs_eol, "eol", NUL }, + { &lcs_ext, "extends", NUL }, + { &lcs_nbsp, "nbsp", NUL }, + { &lcs_prec, "precedes", NUL }, + { &lcs_space, "space", NUL }, + { &lcs_tab2, "tab", NUL }, + { &lcs_trail, "trail", NUL }, + { &lcs_conceal, "conceal", NUL }, }; struct charstab *tab; @@ -3402,20 +3430,29 @@ static char_u *set_chars_option(char_u **varp) } else { tab = filltab; entries = ARRAY_SIZE(filltab); + if (*p_ambw == 'd') { + // XXX: If ambiwidth=double then "|" and "·" take 2 columns, which is + // forbidden (TUI limitation?). Set old defaults. + filltab[2].def = '|'; + filltab[3].def = '-'; + } else { + filltab[2].def = 9474; // │ + filltab[3].def = 183; // · + } } - /* first round: check for valid value, second round: assign values */ - for (round = 0; round <= 1; ++round) { + // first round: check for valid value, second round: assign values + for (round = 0; round <= 1; round++) { if (round > 0) { - /* After checking that the value is valid: set defaults: space for - * 'fillchars', NUL for 'listchars' */ - for (i = 0; i < entries; ++i) - if (tab[i].cp != NULL) - *(tab[i].cp) = (varp == &p_lcs ? NUL : ' '); - if (varp == &p_lcs) + // After checking that the value is valid: set defaults + for (i = 0; i < entries; i++) { + if (tab[i].cp != NULL) { + *(tab[i].cp) = tab[i].def; + } + } + if (varp == &p_lcs) { lcs_tab1 = NUL; - else - fill_diff = '-'; + } } p = *varp; while (*p) { @@ -4012,6 +4049,10 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, (char_u *) options[opt_idx].fullname, NULL, false, NULL); reset_v_option_vars(); + if (options[opt_idx].flags & P_UI_OPTION) { + ui_call_option_set(cstr_as_string(options[opt_idx].fullname), + BOOLEAN_OBJ(value)); + } } comp_col(); /* in case 'ruler' or 'showcmd' changed */ @@ -4118,6 +4159,10 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, if (value < 0) { errmsg = e_positive; } + } else if (pp == &p_titlelen) { + if (value < 0) { + errmsg = e_positive; + } } else if (pp == &p_so) { if (value < 0 && full_screen) { errmsg = e_scroll; @@ -4172,6 +4217,8 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, if (value < -1 || value > B_IMODE_LAST) { errmsg = e_invarg; } + } else if (pp == &curbuf->b_p_channel || pp == &p_channel) { + errmsg = e_invarg; } else if (pp == &curbuf->b_p_scbk || pp == &p_scbk) { if (value < -1 || value > SB_MAX || (value != -1 && opt_flags == OPT_LOCAL && !curbuf->terminal)) { @@ -4215,17 +4262,17 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, // Number options that need some action when changed if (pp == &p_wh) { - if (lastwin != firstwin && curwin->w_height < p_wh) { + if (!ONE_WINDOW && curwin->w_height < p_wh) { win_setheight((int)p_wh); } } else if (pp == &p_hh) { - if (lastwin != firstwin && curbuf->b_help && curwin->w_height < p_hh) { + if (!ONE_WINDOW && curbuf->b_help && curwin->w_height < p_hh) { win_setheight((int)p_hh); } } else if (pp == &p_wmh) { win_setminheight(); } else if (pp == &p_wiw) { - if (lastwin != firstwin && curwin->w_width < p_wiw) { + if (!ONE_WINDOW && curwin->w_width < p_wiw) { win_setwidth((int)p_wiw); } } else if (pp == &p_ls) { @@ -4388,6 +4435,10 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, (char_u *) options[opt_idx].fullname, NULL, false, NULL); reset_v_option_vars(); + if (options[opt_idx].flags & P_UI_OPTION) { + ui_call_option_set(cstr_as_string(options[opt_idx].fullname), + INTEGER_OBJ(value)); + } } comp_col(); /* in case 'columns' or 'ls' changed */ @@ -4399,6 +4450,27 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, return (char *)errmsg; } +static void trigger_optionsset_string(int opt_idx, int opt_flags, + char *oldval, char *newval) +{ + if (oldval != NULL && newval != NULL) { + char buf_type[7]; + + vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s", + (opt_flags & OPT_LOCAL) ? "local" : "global"); + set_vim_var_string(VV_OPTION_OLD, oldval, -1); + set_vim_var_string(VV_OPTION_NEW, newval, -1); + set_vim_var_string(VV_OPTION_TYPE, buf_type, -1); + apply_autocmds(EVENT_OPTIONSET, + (char_u *)options[opt_idx].fullname, NULL, false, NULL); + reset_v_option_vars(); + if (options[opt_idx].flags & P_UI_OPTION) { + ui_call_option_set(cstr_as_string(options[opt_idx].fullname), + STRING_OBJ(cstr_as_string(newval))); + } + } +} + /* * Called after an option changed: check if something needs to be redrawn. */ @@ -4495,13 +4567,17 @@ int findoption_len(const char *const arg, const size_t len) bool is_tty_option(const char *name) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - return (name[0] == 't' && name[1] == '_') || strcmp(name, "term") == 0; + return (name[0] == 't' && name[1] == '_') + || strequal(name, "term") + || strequal(name, "ttytype"); } #define TCO_BUFFER_SIZE 8 +/// @param name TUI-related option +/// @param[out,allocated] value option string value bool get_tty_option(char *name, char **value) { - if (!strcmp(name, "t_Co")) { + if (strequal(name, "t_Co")) { if (value) { if (t_colors <= 1) { *value = xstrdup(""); @@ -4513,9 +4589,16 @@ bool get_tty_option(char *name, char **value) return true; } - if (!strcmp(name, "term") || !strcmp(name, "ttytype")) { + if (strequal(name, "term")) { + if (value) { + *value = p_term ? xstrdup(p_term) : xstrdup("nvim"); + } + return true; + } + + if (strequal(name, "ttytype")) { if (value) { - *value = xstrdup("nvim"); + *value = p_ttytype ? xstrdup(p_ttytype) : xstrdup("nvim"); } return true; } @@ -4531,25 +4614,25 @@ bool get_tty_option(char *name, char **value) return false; } -bool set_tty_option(const char *name, const char *value) +bool set_tty_option(const char *name, char *value) { - if (!strcmp(name, "t_Co")) { - int colors = atoi(value); - - // Only reinitialize colors if t_Co value has really changed to - // avoid expensive reload of colorscheme if t_Co is set to the - // same value multiple times - if (colors != t_colors) { - t_colors = colors; - // We now have a different color setup, initialize it again. - init_highlight(true, false); + if (strequal(name, "term")) { + if (p_term) { + xfree(p_term); } + p_term = value; + return true; + } + if (strequal(name, "ttytype")) { + if (p_ttytype) { + xfree(p_ttytype); + } + p_ttytype = value; return true; } - return (is_tty_option(name) || !strcmp(name, "term") - || !strcmp(name, "ttytype")); + return false; } /// Find index for an option @@ -4562,21 +4645,18 @@ static int findoption(const char *const arg) return findoption_len(arg, strlen(arg)); } -/* - * Get the value for an option. - * - * Returns: - * Number or Toggle option: 1, *numval gets value. - * String option: 0, *stringval gets allocated string. - * Hidden Number or Toggle option: -1. - * hidden String option: -2. - * unknown option: -3. - */ -int -get_option_value ( +/// Gets the value for an option. +/// +/// @returns: +/// Number or Toggle option: 1, *numval gets value. +/// String option: 0, *stringval gets allocated string. +/// Hidden Number or Toggle option: -1. +/// hidden String option: -2. +/// unknown option: -3. +int get_option_value( char_u *name, long *numval, - char_u **stringval, /* NULL when only checking existence */ + char_u **stringval, ///< NULL when only checking existence int opt_flags ) { @@ -4584,32 +4664,31 @@ get_option_value ( return 0; } - int opt_idx; - char_u *varp; - - opt_idx = findoption((const char *)name); + int opt_idx = findoption((const char *)name); if (opt_idx < 0) { // Unknown option. return -3; } - varp = get_varp_scope(&(options[opt_idx]), opt_flags); + char_u *varp = get_varp_scope(&(options[opt_idx]), opt_flags); if (options[opt_idx].flags & P_STRING) { - if (varp == NULL) /* hidden option */ + if (varp == NULL) { // hidden option return -2; + } if (stringval != NULL) { *stringval = vim_strsave(*(char_u **)(varp)); } return 0; } - if (varp == NULL) /* hidden option */ + if (varp == NULL) { // hidden option return -1; - if (options[opt_idx].flags & P_NUM) + } + if (options[opt_idx].flags & P_NUM) { *numval = *(long *)varp; - else { - /* Special case: 'modified' is b_changed, but we also want to consider - * it set when 'ff' or 'fenc' changed. */ + } else { + // Special case: 'modified' is b_changed, but we also want to consider + // it set when 'ff' or 'fenc' changed. if ((int *)varp == &curbuf->b_changed) { *numval = curbufIsChanged(); } else { @@ -4756,8 +4835,8 @@ char *set_option_value(const char *const name, const long number, const char *const string, const int opt_flags) FUNC_ATTR_NONNULL_ARG(1) { - if (set_tty_option(name, string)) { - return NULL; + if (is_tty_option(name)) { + return NULL; // Fail silently; many old vimrcs set t_xx options. } int opt_idx; @@ -4861,15 +4940,14 @@ showoptions ( vimoption_T **items = xmalloc(sizeof(vimoption_T *) * PARAM_COUNT); - /* Highlight title */ - if (all == 2) - MSG_PUTS_TITLE(_("\n--- Terminal codes ---")); - else if (opt_flags & OPT_GLOBAL) + // Highlight title + if (opt_flags & OPT_GLOBAL) { MSG_PUTS_TITLE(_("\n--- Global option values ---")); - else if (opt_flags & OPT_LOCAL) + } else if (opt_flags & OPT_LOCAL) { MSG_PUTS_TITLE(_("\n--- Local option values ---")); - else + } else { MSG_PUTS_TITLE(_("\n--- Options ---")); + } /* * do the loop two times: @@ -4944,14 +5022,39 @@ static int optval_default(vimoption_T *p, char_u *varp) if (varp == NULL) return TRUE; /* hidden option is always at default */ dvi = ((p->flags & P_VI_DEF) || p_cp) ? VI_DEFAULT : VIM_DEFAULT; - if (p->flags & P_NUM) - return *(long *)varp == (long)p->def_val[dvi]; - if (p->flags & P_BOOL) + if (p->flags & P_NUM) { + return *(long *)varp == (long)(intptr_t)p->def_val[dvi]; + } + if (p->flags & P_BOOL) { return *(int *)varp == (int)(intptr_t)p->def_val[dvi]; - /* P_STRING */ + } + // P_STRING return STRCMP(*(char_u **)varp, p->def_val[dvi]) == 0; } +/// Send update to UIs with values of UI relevant options +void ui_refresh_options(void) +{ + for (int opt_idx = 0; options[opt_idx].fullname; opt_idx++) { + uint32_t flags = options[opt_idx].flags; + if (!(flags & P_UI_OPTION)) { + continue; + } + String name = cstr_as_string(options[opt_idx].fullname); + void *varp = options[opt_idx].var; + Object value = OBJECT_INIT; + if (flags & P_BOOL) { + value = BOOLEAN_OBJ(*(int *)varp); + } else if (flags & P_NUM) { + value = INTEGER_OBJ(*(long *)varp); + } else if (flags & P_STRING) { + // cstr_as_string handles NULL string + value = STRING_OBJ(cstr_as_string(*(char **)varp)); + } + ui_call_option_set(name, value); + } +} + /* * showoneopt: show the value of one option * must not be called with a hidden option! @@ -5149,9 +5252,13 @@ static int put_setstring(FILE *fd, char *cmd, char *name, char_u **valuep, int e * CTRL-V or backslash */ if (valuep == &p_pt) { s = *valuep; - while (*s != NUL) - if (put_escstr(fd, str2special(&s, FALSE), 2) == FAIL) + while (*s != NUL) { + if (put_escstr(fd, (char_u *)str2special((const char **)&s, false, + false), 2) + == FAIL) { return FAIL; + } + } } else if (expand) { buf = xmalloc(MAXPATHL); home_replace(NULL, *valuep, buf, MAXPATHL, FALSE); @@ -5206,7 +5313,7 @@ static int put_setbool(FILE *fd, char *cmd, char *name, int value) void comp_col(void) { - int last_has_status = (p_ls == 2 || (p_ls == 1 && firstwin != lastwin)); + int last_has_status = (p_ls == 2 || (p_ls == 1 && !ONE_WINDOW)); sc_col = 0; ru_col = 0; @@ -5307,6 +5414,9 @@ void unset_global_local_option(char *name, void *from) case PV_LW: clear_string_option(&buf->b_p_lw); break; + case PV_MENC: + clear_string_option(&buf->b_p_menc); + break; } } @@ -5340,6 +5450,7 @@ static char_u *get_varp_scope(vimoption_T *p, int opt_flags) case PV_UL: return (char_u *)&(curbuf->b_p_ul); case PV_LW: return (char_u *)&(curbuf->b_p_lw); case PV_BKC: return (char_u *)&(curbuf->b_p_bkc); + case PV_MENC: return (char_u *)&(curbuf->b_p_menc); } return NULL; /* "cannot happen" */ } @@ -5395,6 +5506,8 @@ static char_u *get_varp(vimoption_T *p) ? (char_u *)&(curbuf->b_p_ul) : p->var; case PV_LW: return *curbuf->b_p_lw != NUL ? (char_u *)&(curbuf->b_p_lw) : p->var; + case PV_MENC: return *curbuf->b_p_menc != NUL + ? (char_u *)&(curbuf->b_p_menc) : p->var; case PV_ARAB: return (char_u *)&(curwin->w_p_arab); case PV_LIST: return (char_u *)&(curwin->w_p_list); @@ -5437,6 +5550,7 @@ static char_u *get_varp(vimoption_T *p) case PV_BH: return (char_u *)&(curbuf->b_p_bh); case PV_BT: return (char_u *)&(curbuf->b_p_bt); case PV_BL: return (char_u *)&(curbuf->b_p_bl); + case PV_CHANNEL:return (char_u *)&(curbuf->b_p_channel); case PV_CI: return (char_u *)&(curbuf->b_p_ci); case PV_CIN: return (char_u *)&(curbuf->b_p_cin); case PV_CINK: return (char_u *)&(curbuf->b_p_cink); @@ -5490,7 +5604,7 @@ static char_u *get_varp(vimoption_T *p) case PV_KMAP: return (char_u *)&(curbuf->b_p_keymap); case PV_SCL: return (char_u *)&(curwin->w_p_scl); case PV_WINHL: return (char_u *)&(curwin->w_p_winhl); - default: EMSG(_("E356: get_varp ERROR")); + default: IEMSG(_("E356: get_varp ERROR")); } /* always return a valid pointer to avoid a crash! */ return (char_u *)&(curbuf->b_p_wm); @@ -5685,7 +5799,22 @@ void buf_copy_options(buf_T *buf, int flags) free_buf_options(buf, TRUE); buf->b_p_ro = FALSE; /* don't copy readonly */ buf->b_p_fenc = vim_strsave(p_fenc); - buf->b_p_ff = vim_strsave(p_ff); + switch (*p_ffs) { + case 'm': + buf->b_p_ff = vim_strsave((char_u *)FF_MAC); + break; + case 'd': + buf->b_p_ff = vim_strsave((char_u *)FF_DOS); + break; + case 'u': + buf->b_p_ff = vim_strsave((char_u *)FF_UNIX); + break; + default: + buf->b_p_ff = vim_strsave(p_ff); + } + if (buf->b_p_ff != NULL) { + buf->b_start_ffc = *buf->b_p_ff; + } buf->b_p_bh = empty_option; buf->b_p_bt = empty_option; } else @@ -5723,6 +5852,7 @@ void buf_copy_options(buf_T *buf, int flags) buf->b_p_nf = vim_strsave(p_nf); buf->b_p_mps = vim_strsave(p_mps); buf->b_p_si = p_si; + buf->b_p_channel = 0; buf->b_p_ci = p_ci; buf->b_p_cin = p_cin; buf->b_p_cink = vim_strsave(p_cink); @@ -5775,6 +5905,7 @@ void buf_copy_options(buf_T *buf, int flags) buf->b_p_qe = vim_strsave(p_qe); buf->b_p_udf = p_udf; buf->b_p_lw = empty_option; + buf->b_p_menc = empty_option; /* * Don't copy the options set by ex_help(), use the saved values, @@ -6023,8 +6154,8 @@ int ExpandSettings(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u *** int count = 0; char_u *str; int loop; - static char *(names[]) = {"all", "termcap"}; - int ic = regmatch->rm_ic; /* remember the ignore-case flag */ + static char *(names[]) = { "all" }; + int ic = regmatch->rm_ic; // remember the ignore-case flag /* do this loop twice: * loop == 0: count the number of matching options @@ -6147,15 +6278,16 @@ option_value2string ( } } else { // P_STRING varp = *(char_u **)(varp); - if (varp == NULL) /* just in case */ + if (varp == NULL) { // Just in case. NameBuff[0] = NUL; - else if (opp->flags & P_EXPAND) - home_replace(NULL, varp, NameBuff, MAXPATHL, FALSE); - /* Translate 'pastetoggle' into special key names */ - else if ((char_u **)opp->var == &p_pt) - str2specialbuf(p_pt, NameBuff, MAXPATHL); - else + } else if (opp->flags & P_EXPAND) { + home_replace(NULL, varp, NameBuff, MAXPATHL, false); + // Translate 'pastetoggle' into special key names. + } else if ((char_u **)opp->var == &p_pt) { + str2specialbuf((const char *)p_pt, (char *)NameBuff, MAXPATHL); + } else { STRLCPY(NameBuff, varp, MAXPATHL); + } } } @@ -7005,8 +7137,11 @@ dict_T *get_winbuf_options(const int bufopt) if (opt->flags & P_STRING) { tv_dict_add_str(d, opt->fullname, strlen(opt->fullname), *(const char **)varp); + } else if (opt->flags & P_NUM) { + tv_dict_add_nr(d, opt->fullname, strlen(opt->fullname), + *(long *)varp); } else { - tv_dict_add_nr(d, opt->fullname, strlen(opt->fullname), *varp); + tv_dict_add_nr(d, opt->fullname, strlen(opt->fullname), *(int *)varp); } } } diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index e68dba734e..e2e98f251e 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -81,58 +81,56 @@ #define DFLT_FO_VIM "tcqj" #define FO_ALL "tcroq2vlb1mMBn,awj" /* for do_set() */ -/* characters for the p_cpo option: */ -#define CPO_ALTREAD 'a' /* ":read" sets alternate file name */ -#define CPO_ALTWRITE 'A' /* ":write" sets alternate file name */ -#define CPO_BAR 'b' /* "\|" ends a mapping */ -#define CPO_BSLASH 'B' /* backslash in mapping is not special */ +// characters for the p_cpo option: +#define CPO_ALTREAD 'a' // ":read" sets alternate file name +#define CPO_ALTWRITE 'A' // ":write" sets alternate file name +#define CPO_BAR 'b' // "\|" ends a mapping +#define CPO_BSLASH 'B' // backslash in mapping is not special #define CPO_SEARCH 'c' -#define CPO_CONCAT 'C' /* Don't concatenate sourced lines */ -#define CPO_DOTTAG 'd' /* "./tags" in 'tags' is in current dir */ -#define CPO_DIGRAPH 'D' /* No digraph after "r", "f", etc. */ +#define CPO_CONCAT 'C' // Don't concatenate sourced lines +#define CPO_DOTTAG 'd' // "./tags" in 'tags' is in current dir +#define CPO_DIGRAPH 'D' // No digraph after "r", "f", etc. #define CPO_EXECBUF 'e' -#define CPO_EMPTYREGION 'E' /* operating on empty region is an error */ -#define CPO_FNAMER 'f' /* set file name for ":r file" */ -#define CPO_FNAMEW 'F' /* set file name for ":w file" */ -#define CPO_INTMOD 'i' /* interrupt a read makes buffer modified */ -#define CPO_INDENT 'I' /* remove auto-indent more often */ -#define CPO_ENDOFSENT 'J' /* need two spaces to detect end of sentence */ -#define CPO_KEYCODE 'k' /* don't recognize raw key code in mappings */ -#define CPO_KOFFSET 'K' /* don't wait for key code in mappings */ -#define CPO_LITERAL 'l' /* take char after backslash in [] literal */ -#define CPO_LISTWM 'L' /* 'list' changes wrapmargin */ +#define CPO_EMPTYREGION 'E' // operating on empty region is an error +#define CPO_FNAMER 'f' // set file name for ":r file" +#define CPO_FNAMEW 'F' // set file name for ":w file" +#define CPO_INTMOD 'i' // interrupt a read makes buffer modified +#define CPO_INDENT 'I' // remove auto-indent more often +#define CPO_ENDOFSENT 'J' // need two spaces to detect end of sentence +#define CPO_KOFFSET 'K' // don't wait for key code in mappings +#define CPO_LITERAL 'l' // take char after backslash in [] literal +#define CPO_LISTWM 'L' // 'list' changes wrapmargin #define CPO_SHOWMATCH 'm' -#define CPO_MATCHBSL 'M' /* "%" ignores use of backslashes */ -#define CPO_NUMCOL 'n' /* 'number' column also used for text */ +#define CPO_MATCHBSL 'M' // "%" ignores use of backslashes +#define CPO_NUMCOL 'n' // 'number' column also used for text #define CPO_LINEOFF 'o' -#define CPO_OVERNEW 'O' /* silently overwrite new file */ -#define CPO_LISP 'p' /* 'lisp' indenting */ -#define CPO_FNAMEAPP 'P' /* set file name for ":w >>file" */ -#define CPO_JOINCOL 'q' /* with "3J" use column after first join */ +#define CPO_OVERNEW 'O' // silently overwrite new file +#define CPO_LISP 'p' // 'lisp' indenting +#define CPO_FNAMEAPP 'P' // set file name for ":w >>file" +#define CPO_JOINCOL 'q' // with "3J" use column after first join #define CPO_REDO 'r' -#define CPO_REMMARK 'R' /* remove marks when filtering */ +#define CPO_REMMARK 'R' // remove marks when filtering #define CPO_BUFOPT 's' #define CPO_BUFOPTGLOB 'S' #define CPO_TAGPAT 't' -#define CPO_UNDO 'u' /* "u" undoes itself */ -#define CPO_BACKSPACE 'v' /* "v" keep deleted text */ -#define CPO_FWRITE 'W' /* "w!" doesn't overwrite readonly files */ +#define CPO_UNDO 'u' // "u" undoes itself +#define CPO_BACKSPACE 'v' // "v" keep deleted text +#define CPO_FWRITE 'W' // "w!" doesn't overwrite readonly files #define CPO_ESC 'x' -#define CPO_REPLCNT 'X' /* "R" with a count only deletes chars once */ +#define CPO_REPLCNT 'X' // "R" with a count only deletes chars once #define CPO_YANK 'y' -#define CPO_KEEPRO 'Z' /* don't reset 'readonly' on ":w!" */ +#define CPO_KEEPRO 'Z' // don't reset 'readonly' on ":w!" #define CPO_DOLLAR '$' #define CPO_FILTER '!' #define CPO_MATCH '%' -#define CPO_PLUS '+' /* ":write file" resets 'modified' */ -#define CPO_SPECI '<' /* don't recognize <> in mappings */ -#define CPO_REGAPPEND '>' /* insert NL when appending to a register */ -#define CPO_SCOLON ';' /* using "," and ";" will skip over char if - * cursor would not move */ +#define CPO_PLUS '+' // ":write file" resets 'modified' +#define CPO_REGAPPEND '>' // insert NL when appending to a register +#define CPO_SCOLON ';' // using "," and ";" will skip over char if + // cursor would not move #define CPO_CHANGEW '_' // "cw" special-case // default values for Vim and Vi #define CPO_VIM "aABceFs_" -#define CPO_VI "aAbBcCdDeEfFiIJkKlLmMnoOpPqrRsStuvWxXyZ$!%+<>;_" +#define CPO_VI "aAbBcCdDeEfFiIJKlLmMnoOpPqrRsStuvWxXyZ$!%+>;_" /* characters for p_ww option: */ #define WW_ALL "bshl<>[],~" @@ -449,13 +447,13 @@ EXTERN char_u *p_popt; // 'printoptions' EXTERN char_u *p_header; // 'printheader' EXTERN int p_prompt; // 'prompt' EXTERN char_u *p_guicursor; // 'guicursor' +EXTERN char_u *p_guifont; // 'guifont' +EXTERN char_u *p_guifontset; // 'guifontset' +EXTERN char_u *p_guifontwide; // 'guifontwide' EXTERN char_u *p_hf; // 'helpfile' EXTERN long p_hh; // 'helpheight' EXTERN char_u *p_hlg; // 'helplang' EXTERN int p_hid; // 'hidden' -// Use P_HID to check if a buffer is to be hidden when it is no longer -// visible in a window. -# define P_HID(buf) (buf_hide(buf)) EXTERN char_u *p_hl; // 'highlight' EXTERN int p_hls; // 'hlsearch' EXTERN long p_hi; // 'history' @@ -480,6 +478,7 @@ EXTERN char_u *p_langmap; // 'langmap' EXTERN int p_lnr; // 'langnoremap' EXTERN int p_lrm; // 'langremap' EXTERN char_u *p_lm; // 'langmenu' +EXTERN long *p_linespace; // 'linespace' EXTERN char_u *p_lispwords; // 'lispwords' EXTERN long p_ls; // 'laststatus' EXTERN long p_stal; // 'showtabline' @@ -488,6 +487,7 @@ EXTERN char_u *p_lcs; // 'listchars' EXTERN int p_lz; // 'lazyredraw' EXTERN int p_lpl; // 'loadplugins' EXTERN int p_magic; // 'magic' +EXTERN char_u *p_menc; // 'makeencoding' EXTERN char_u *p_mef; // 'makeef' EXTERN char_u *p_mp; // 'makeprg' EXTERN char_u *p_cc; // 'colorcolumn' @@ -541,7 +541,7 @@ static char *(p_ssop_values[]) = {"buffers", "winpos", "resize", "winsize", "localoptions", "options", "help", "blank", "globals", "slash", "unix", "sesdir", "curdir", "folds", "cursor", - "tabpages", NULL}; + "tabpages", NULL }; # endif # define SSOP_BUFFERS 0x001 # define SSOP_WINPOS 0x002 @@ -559,16 +559,17 @@ static char *(p_ssop_values[]) = {"buffers", "winpos", "resize", "winsize", # define SSOP_FOLDS 0x2000 # define SSOP_CURSOR 0x4000 # define SSOP_TABPAGES 0x8000 -EXTERN char_u *p_sh; /* 'shell' */ -EXTERN char_u *p_shcf; /* 'shellcmdflag' */ -EXTERN char_u *p_sp; /* 'shellpipe' */ -EXTERN char_u *p_shq; /* 'shellquote' */ -EXTERN char_u *p_sxq; /* 'shellxquote' */ -EXTERN char_u *p_sxe; /* 'shellxescape' */ -EXTERN char_u *p_srr; /* 'shellredir' */ -EXTERN int p_stmp; /* 'shelltemp' */ + +EXTERN char_u *p_sh; // 'shell' +EXTERN char_u *p_shcf; // 'shellcmdflag' +EXTERN char_u *p_sp; // 'shellpipe' +EXTERN char_u *p_shq; // 'shellquote' +EXTERN char_u *p_sxq; // 'shellxquote' +EXTERN char_u *p_sxe; // 'shellxescape' +EXTERN char_u *p_srr; // 'shellredir' +EXTERN int p_stmp; // 'shelltemp' #ifdef BACKSLASH_IN_FILENAME -EXTERN int p_ssl; /* 'shellslash' */ +EXTERN int p_ssl; // 'shellslash' #endif EXTERN char_u *p_stl; // 'statusline' EXTERN int p_sr; // 'shiftround' @@ -696,6 +697,7 @@ enum { , BV_BIN , BV_BL , BV_BOMB + , BV_CHANNEL , BV_CI , BV_CIN , BV_CINK @@ -733,6 +735,7 @@ enum { , BV_KP , BV_LISP , BV_LW + , BV_MENC , BV_MA , BV_ML , BV_MOD diff --git a/src/nvim/options.lua b/src/nvim/options.lua index c2778a6329..d653147943 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -7,7 +7,7 @@ -- enable_if=nil, -- defaults={condition=nil, if_true={vi=224, vim=0}, if_false=nil}, -- secure=nil, gettext=nil, noglob=nil, normal_fname_chars=nil, --- pri_mkrc=nil, deny_in_modelines=nil, +-- pri_mkrc=nil, deny_in_modelines=nil, normal_dname_chars=nil, -- expand=nil, nodefault=nil, no_mkrc=nil, vi_def=true, vim=true, -- alloced=nil, -- save_pv_indir=nil, @@ -68,7 +68,8 @@ return { type='bool', scope={'global'}, vi_def=true, vim=true, - redraw={'everything'}, + redraw={'everything', 'ui_option'}, + varname='p_arshape', defaults={if_true={vi=true}} }, @@ -91,7 +92,7 @@ return { full_name='ambiwidth', abbreviation='ambw', type='string', scope={'global'}, vi_def=true, - redraw={'everything'}, + redraw={'everything', 'ui_option'}, varname='p_ambw', defaults={if_true={vi="single"}} }, @@ -295,6 +296,14 @@ return { defaults={if_true={vi="", vim=macros('CTRL_F_STR')}} }, { + full_name='channel', + type='number', scope={'buffer'}, + no_mkrc=true, + nodefault=true, + varname='p_channel', + defaults={if_true={vi=0}} + }, + { full_name='charconvert', abbreviation='ccv', type='string', scope={'global'}, secure=true, @@ -524,7 +533,7 @@ return { vi_def=true, vim=true, varname='p_csverbose', - defaults={if_true={vi=0}} + defaults={if_true={vi=1}} }, { full_name='cursorbind', abbreviation='crb', @@ -575,6 +584,7 @@ return { full_name='dictionary', abbreviation='dict', type='string', list='onecomma', scope={'global', 'buffer'}, deny_duplicates=true, + normal_dname_chars=true, vi_def=true, expand=true, varname='p_dict', @@ -652,7 +662,7 @@ return { full_name='emoji', abbreviation='emo', type='bool', scope={'global'}, vi_def=true, - redraw={'everything'}, + redraw={'everything', 'ui_option'}, varname='p_emoji', defaults={if_true={vi=true}} }, @@ -802,7 +812,7 @@ return { vi_def=true, redraw={'all_windows'}, varname='p_fcs', - defaults={if_true={vi="vert:|,fold:-"}} + defaults={if_true={vi=''}} }, { full_name='fixendofline', abbreviation='fixeol', @@ -992,11 +1002,11 @@ return { expand=true, varname='p_gp', defaults={ - condition='UNIX', + condition='WIN32', -- Add an extra file name so that grep will always -- insert a file name in the match line. */ - if_true={vi="grep -n $* /dev/null"}, - if_false={vi="grep -n "}, + if_true={vi="findstr /n $* nul"}, + if_false={vi="grep -n $* /dev/null"} } }, { @@ -1012,23 +1022,26 @@ return { type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vi_def=true, - redraw={'everything'}, - enable_if=false, + varname='p_guifont', + redraw={'everything', 'ui_option'}, + defaults={if_true={vi=""}} }, { full_name='guifontset', abbreviation='gfs', type='string', list='onecomma', scope={'global'}, vi_def=true, - redraw={'everything'}, - enable_if=false, + varname='p_guifontset', + redraw={'everything', 'ui_option'}, + defaults={if_true={vi=""}} }, { full_name='guifontwide', abbreviation='gfw', type='string', list='onecomma', scope={'global'}, deny_duplicates=true, vi_def=true, - redraw={'everything'}, - enable_if=false, + redraw={'everything', 'ui_option'}, + varname='p_guifontwide', + defaults={if_true={vi=""}} }, { full_name='guioptions', abbreviation='go', @@ -1386,8 +1399,9 @@ return { full_name='linespace', abbreviation='lsp', type='number', scope={'global'}, vi_def=true, - redraw={'everything'}, - enable_if=false, + redraw={'everything', 'ui_option'}, + varname='p_linespace', + defaults={if_true={vi=0}} }, { full_name='lisp', @@ -1444,6 +1458,13 @@ return { defaults={if_true={vi=""}} }, { + full_name='makeencoding', abbreviation='menc', + type='string', scope={'global', 'buffer'}, + vi_def=true, + varname='p_menc', + defaults={if_true={vi=""}} + }, + { full_name='makeprg', abbreviation='mp', type='string', scope={'global', 'buffer'}, secure=true, @@ -1750,6 +1771,7 @@ return { { full_name='printexpr', abbreviation='pexpr', type='string', scope={'global'}, + secure=true, vi_def=true, varname='p_pexpr', defaults={if_true={vi=""}} @@ -1922,7 +1944,7 @@ return { vi_def=true, varname='p_scbk', redraw={'current_buffer'}, - defaults={if_true={vi=1000}} + defaults={if_true={vi=10000}} }, { full_name='scrollbind', abbreviation='scb', @@ -2026,7 +2048,7 @@ return { varname='p_shcf', defaults={ condition='WIN32', - if_true={vi="/c"}, + if_true={vi="/s /c"}, if_false={vi="-c"} } }, @@ -2082,7 +2104,11 @@ return { secure=true, vi_def=true, varname='p_sxq', - defaults={if_true={vi=""}} + defaults={ + condition='WIN32', + if_true={vi="\""}, + if_false={vi=""}, + } }, { full_name='shellxescape', abbreviation='sxe', @@ -2154,7 +2180,7 @@ return { full_name='showtabline', abbreviation='stal', type='number', scope={'global'}, vi_def=true, - redraw={'all_windows'}, + redraw={'all_windows', 'ui_option'}, varname='p_stal', defaults={if_true={vi=1}} }, @@ -2163,7 +2189,7 @@ return { type='number', scope={'global'}, vi_def=true, varname='p_ss', - defaults={if_true={vi=0}} + defaults={if_true={vi=1}} }, { full_name='sidescrolloff', abbreviation='siso', @@ -2425,7 +2451,7 @@ return { full_name='termguicolors', abbreviation='tgc', type='bool', scope={'global'}, vi_def=false, - redraw={'everything'}, + redraw={'ui_option'}, varname='p_tgc', defaults={if_true={vi=false}} }, @@ -2449,6 +2475,7 @@ return { full_name='thesaurus', abbreviation='tsr', type='string', list='onecomma', scope={'global', 'buffer'}, deny_duplicates=true, + normal_dname_chars=true, vi_def=true, expand=true, varname='p_tsr', @@ -2494,11 +2521,10 @@ return { full_name='titleold', type='string', scope={'global'}, secure=true, - gettext=true, no_mkrc=true, vi_def=true, varname='p_titleold', - defaults={if_true={vi=N_("Thanks for flying Vim")}} + defaults={if_true={vi=""}} }, { full_name='titlestring', @@ -2513,14 +2539,14 @@ return { vi_def=true, vim=true, varname='p_ttimeout', - defaults={if_true={vi=false}} + defaults={if_true={vi=true}} }, { full_name='ttimeoutlen', abbreviation='ttm', type='number', scope={'global'}, vi_def=true, varname='p_ttm', - defaults={if_true={vi=-1}} + defaults={if_true={vi=50}} }, { full_name='ttyfast', abbreviation='tf', @@ -2607,7 +2633,7 @@ return { deny_duplicates=true, vi_def=true, varname='p_vop', - defaults={if_true={vi="folds,options,cursor"}} + defaults={if_true={vi="folds,options,cursor,curdir"}} }, { full_name='viminfo', abbreviation='vi', diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index de0cd10d9c..3fcb9415c7 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -886,8 +886,8 @@ bool os_setenv_append_path(const char *fname) // No prescribed maximum on unix. # define MAX_ENVPATHLEN INT_MAX #endif - if (!path_is_absolute_path((char_u *)fname)) { - EMSG2(_(e_intern2), "os_setenv_append_path()"); + if (!path_is_absolute((char_u *)fname)) { + internal_error("os_setenv_append_path()"); return false; } const char *tail = (char *)path_tail_with_sep((char_u *)fname); diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c index 4309ac723c..a95adc86b6 100644 --- a/src/nvim/os/fileio.c +++ b/src/nvim/os/fileio.c @@ -4,7 +4,7 @@ /// @file fileio.c /// /// Buffered reading/writing to a file. Unlike fileio.c this is not dealing with -/// Neovim stuctures for buffer, with autocommands, etc: just fopen/fread/fwrite +/// Nvim stuctures for buffer, with autocommands, etc: just fopen/fread/fwrite /// replacement. #include <assert.h> @@ -26,6 +26,7 @@ #include "nvim/globals.h" #include "nvim/rbuffer.h" #include "nvim/macros.h" +#include "nvim/message.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/fileio.c.generated.h" @@ -38,17 +39,16 @@ /// @param[in] fname File name to open. /// @param[in] flags Flags, @see FileOpenFlags. Currently reading from and /// writing to the file at once is not supported, so either -/// FILE_WRITE_ONLY or FILE_READ_ONLY is required. +/// kFileWriteOnly or kFileReadOnly is required. /// @param[in] mode Permissions for the newly created file (ignored if flags -/// does not have FILE_CREATE\*). +/// does not have kFileCreate\*). /// -/// @return Error code (@see os_strerror()) or 0. +/// @return Error code, or 0 on success. @see os_strerror() int file_open(FileDescriptor *const ret_fp, const char *const fname, const int flags, const int mode) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { int os_open_flags = 0; - int fd; TriState wr = kNone; // -V:FLAG:501 #define FLAG(flags, flag, fcntl_flags, wrval, cond) \ @@ -73,14 +73,35 @@ int file_open(FileDescriptor *const ret_fp, const char *const fname, FLAG(flags, kFileNoSymlink, O_NOFOLLOW, kNone, true); #endif #undef FLAG + // wr is used for kFileReadOnly flag, but on + // QB:neovim-qb-slave-ubuntu-12-04-64bit it still errors out with + // `error: variable ‘wr’ set but not used [-Werror=unused-but-set-variable]` + (void)wr; - fd = os_open(fname, os_open_flags, mode); + const int fd = os_open(fname, os_open_flags, mode); if (fd < 0) { return fd; } + return file_open_fd(ret_fp, fd, (wr == kTrue)); +} - ret_fp->wr = (wr == kTrue); +/// Wrap file descriptor with FileDescriptor structure +/// +/// @warning File descriptor wrapped like this must not be accessed by other +/// means. +/// +/// @param[out] ret_fp Address where information needed for reading from or +/// writing to a file is saved +/// @param[in] fd File descriptor to wrap. +/// @param[in] wr True if fd is opened for writing only, false if it is read +/// only. +/// +/// @return Error code (@see os_strerror()) or 0. Currently always returns 0. +int file_open_fd(FileDescriptor *const ret_fp, const int fd, const bool wr) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + ret_fp->wr = wr; ret_fp->fd = fd; ret_fp->eof = false; ret_fp->rv = rbuffer_new(kRWBufferSize); @@ -94,12 +115,11 @@ int file_open(FileDescriptor *const ret_fp, const char *const fname, /// Like file_open(), but allocate and return ret_fp /// -/// @param[out] error Error code, @see os_strerror(). Is set to zero on -/// success. +/// @param[out] error Error code, or 0 on success. @see os_strerror() /// @param[in] fname File name to open. /// @param[in] flags Flags, @see FileOpenFlags. /// @param[in] mode Permissions for the newly created file (ignored if flags -/// does not have FILE_CREATE\*). +/// does not have kFileCreate\*). /// /// @return [allocated] Opened file or NULL in case of error. FileDescriptor *file_open_new(int *const error, const char *const fname, @@ -114,6 +134,25 @@ FileDescriptor *file_open_new(int *const error, const char *const fname, return fp; } +/// Like file_open_fd(), but allocate and return ret_fp +/// +/// @param[out] error Error code, or 0 on success. @see os_strerror() +/// @param[in] fd File descriptor to wrap. +/// @param[in] wr True if fd is opened for writing only, false if it is read +/// only. +/// +/// @return [allocated] Opened file or NULL in case of error. +FileDescriptor *file_open_fd_new(int *const error, const int fd, const bool wr) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT +{ + FileDescriptor *const fp = xmalloc(sizeof(*fp)); + if ((*error = file_open_fd(fp, fd, wr)) != 0) { + xfree(fp); + return NULL; + } + return fp; +} + /// Close file and free its buffer /// /// @param[in,out] fp File to close. @@ -345,3 +384,32 @@ ptrdiff_t file_skip(FileDescriptor *const fp, const size_t size) return (ptrdiff_t)read_bytes; } + +/// Msgpack callback for writing to a file +/// +/// @param data File to write to. +/// @param[in] buf Data to write. +/// @param[in] len Length of the data to write. +/// +/// @return 0 in case of success, -1 in case of error. +int msgpack_file_write(void *data, const char *buf, size_t len) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + assert(len < PTRDIFF_MAX); + const ptrdiff_t written_bytes = file_write((FileDescriptor *)data, buf, len); + if (written_bytes < 0) { + return msgpack_file_write_error((int)written_bytes); + } + return 0; +} + +/// Print error which occurs when failing to write msgpack data +/// +/// @param[in] error Error code of the error to print. +/// +/// @return -1 (error return for msgpack_packer callbacks). +int msgpack_file_write_error(const int error) +{ + emsgf(_("E5420: Failed to write to file: %s"), os_strerror(error)); + return -1; +} diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 14dacd97c4..b7c2714296 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -131,23 +131,14 @@ bool os_isdir(const char_u *name) /// NODE_WRITABLE: writable device, socket, fifo, etc. /// NODE_OTHER: non-writable things int os_nodetype(const char *name) + FUNC_ATTR_NONNULL_ALL { -#ifdef WIN32 - // Edge case from Vim os_win32.c: - // We can't open a file with a name "\\.\con" or "\\.\prn", trying to read - // from it later will cause Vim to hang. Thus return NODE_WRITABLE here. - if (STRNCMP(name, "\\\\.\\", 4) == 0) { - return NODE_WRITABLE; - } -#endif - +#ifndef WIN32 // Unix uv_stat_t statbuf; if (0 != os_stat(name, &statbuf)) { return NODE_NORMAL; // File doesn't exist. } - -#ifndef WIN32 - // libuv does not handle BLK and DIR in uv_handle_type. + // uv_handle_type does not distinguish BLK and DIR. // Related: https://github.com/joyent/libuv/pull/1421 if (S_ISREG(statbuf.st_mode) || S_ISDIR(statbuf.st_mode)) { return NODE_NORMAL; @@ -155,48 +146,51 @@ int os_nodetype(const char *name) if (S_ISBLK(statbuf.st_mode)) { // block device isn't writable return NODE_OTHER; } -#endif + // Everything else is writable? + // buf_write() expects NODE_WRITABLE for char device /dev/stderr. + return NODE_WRITABLE; +#else // Windows + // Edge case from Vim os_win32.c: + // We can't open a file with a name "\\.\con" or "\\.\prn", trying to read + // from it later will cause Vim to hang. Thus return NODE_WRITABLE here. + if (STRNCMP(name, "\\\\.\\", 4) == 0) { + return NODE_WRITABLE; + } - // Vim os_win32.c:mch_nodetype does this (since patch 7.4.015): - // if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) { - // wn = enc_to_utf16(name, NULL); - // hFile = CreatFile(wn, ...) - // to get a HANDLE. But libuv just calls win32's _get_osfhandle() on the fd we - // give it. uv_fs_open calls fs__capture_path which does a similar dance and - // saves us the hassle. + // Vim os_win32.c:mch_nodetype does (since 7.4.015): + // wn = enc_to_utf16(name, NULL); + // hFile = CreatFile(wn, ...) + // to get a HANDLE. Whereas libuv just calls _get_osfhandle() on the fd we + // give it. But uv_fs_open later calls fs__capture_path which does a similar + // utf8-to-utf16 dance and saves us the hassle. - int nodetype = NODE_WRITABLE; + // macOS: os_open(/dev/stderr) would return UV_EACCES. int fd = os_open(name, O_RDONLY -#ifdef O_NONBLOCK +# ifdef O_NONBLOCK | O_NONBLOCK -#endif +# endif , 0); - if (fd == -1) { - return NODE_OTHER; // open() failed. + if (fd < 0) { // open() failed. + return NODE_NORMAL; + } + int guess = uv_guess_handle(fd); + if (close(fd) == -1) { + ELOG("close(%d) failed. name='%s'", fd, name); } - switch (uv_guess_handle(fd)) { - case UV_TTY: // FILE_TYPE_CHAR - nodetype = NODE_WRITABLE; - break; - case UV_FILE: // FILE_TYPE_DISK - nodetype = NODE_NORMAL; - break; - case UV_NAMED_PIPE: // not handled explicitly in Vim os_win32.c - case UV_UDP: // unix only - case UV_TCP: // unix only + switch (guess) { + case UV_TTY: // FILE_TYPE_CHAR + return NODE_WRITABLE; + case UV_FILE: // FILE_TYPE_DISK + return NODE_NORMAL; + case UV_NAMED_PIPE: // not handled explicitly in Vim os_win32.c + case UV_UDP: // unix only + case UV_TCP: // unix only case UV_UNKNOWN_HANDLE: default: -#ifdef WIN32 - nodetype = NODE_NORMAL; -#else - nodetype = NODE_WRITABLE; // Everything else is writable? -#endif - break; + return NODE_OTHER; // Vim os_win32.c default } - - close(fd); - return nodetype; +#endif } /// Gets the absolute path of the currently running executable. @@ -228,7 +222,7 @@ int os_exepath(char *buffer, size_t *size) bool os_can_exe(const char_u *name, char_u **abspath, bool use_path) FUNC_ATTR_NONNULL_ARG(1) { - bool no_path = !use_path || path_is_absolute_path(name); + bool no_path = !use_path || path_is_absolute(name); #ifndef WIN32 // If the filename is "qualified" (relative or absolute) do not check $PATH. no_path |= (name[0] == '.' @@ -250,7 +244,7 @@ bool os_can_exe(const char_u *name, char_u **abspath, bool use_path) #endif if (ok) { if (abspath != NULL) { - *abspath = save_absolute_path(name); + *abspath = save_abs_path(name); } return true; } @@ -363,7 +357,7 @@ static bool is_executable_in_path(const char_u *name, char_u **abspath) #endif if (ok) { if (abspath != NULL) { // Caller asked for a copy of the path. - *abspath = save_absolute_path((char_u *)buf); + *abspath = save_abs_path((char_u *)buf); } rv = true; @@ -393,9 +387,11 @@ end: /// @param mode Permissions for the newly-created file (IGNORED if 'flags' is /// not `O_CREAT` or `O_TMPFILE`), subject to the current umask /// @return file descriptor, or libuv error code on failure -int os_open(const char* path, int flags, int mode) - FUNC_ATTR_NONNULL_ALL +int os_open(const char *path, int flags, int mode) { + if (path == NULL) { // uv_fs_open asserts on NULL. #7561 + return UV_EINVAL; + } int r; RUN_UV_FS_FUNC(r, uv_fs_open, path, flags, mode, NULL); return r; @@ -465,7 +461,7 @@ ptrdiff_t os_read(const int fd, bool *ret_eof, char *const ret_buf, while (read_bytes != size) { assert(size >= read_bytes); const ptrdiff_t cur_read_bytes = read(fd, ret_buf + read_bytes, - size - read_bytes); + IO_COUNT(size - read_bytes)); if (cur_read_bytes > 0) { read_bytes += (size_t)cur_read_bytes; } @@ -568,7 +564,7 @@ ptrdiff_t os_write(const int fd, const char *const buf, const size_t size) while (written_bytes != size) { assert(size >= written_bytes); const ptrdiff_t cur_written_bytes = write(fd, buf + written_bytes, - size - written_bytes); + IO_COUNT(size - written_bytes)); if (cur_written_bytes > 0) { written_bytes += (size_t)cur_written_bytes; } @@ -602,10 +598,13 @@ int os_fsync(int fd) /// Get stat information for a file. /// -/// @return libuv return code. +/// @return libuv return code, or -errno static int os_stat(const char *name, uv_stat_t *statbuf) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ARG(2) { + if (!name) { + return UV_EINVAL; + } uv_fs_t request; int result = uv_fs_stat(&fs_loop, &request, name, NULL); *statbuf = request.statbuf; @@ -617,7 +616,6 @@ static int os_stat(const char *name, uv_stat_t *statbuf) /// /// @return libuv error code on error. int32_t os_getperm(const char *name) - FUNC_ATTR_NONNULL_ALL { uv_stat_t statbuf; int stat_result = os_stat(name, &statbuf); @@ -656,7 +654,6 @@ int os_fchown(int fd, uv_uid_t owner, uv_gid_t group) /// /// @return `true` if `path` exists bool os_path_exists(const char_u *path) - FUNC_ATTR_NONNULL_ALL { uv_stat_t statbuf; return os_stat((char *)path, &statbuf) == kLibuvSuccess; @@ -846,7 +843,7 @@ int os_remove(const char *path) /// @param[out] file_info Pointer to a FileInfo to put the information in. /// @return `true` on success, `false` for failure. bool os_fileinfo(const char *path, FileInfo *file_info) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ARG(2) { return os_stat(path, &(file_info->stat)) == kLibuvSuccess; } @@ -857,8 +854,11 @@ bool os_fileinfo(const char *path, FileInfo *file_info) /// @param[out] file_info Pointer to a FileInfo to put the information in. /// @return `true` on success, `false` for failure. bool os_fileinfo_link(const char *path, FileInfo *file_info) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ARG(2) { + if (path == NULL) { + return false; + } uv_fs_t request; int result = uv_fs_lstat(&fs_loop, &request, path, NULL); file_info->stat = request.statbuf; @@ -990,7 +990,7 @@ bool os_fileid_equal_fileinfo(const FileID *file_id, /// to and return that name in allocated memory. /// Otherwise NULL is returned. char *os_resolve_shortcut(const char *fname) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC { HRESULT hr; IPersistFile *ppf = NULL; @@ -1073,7 +1073,8 @@ shortcut_end: #endif -int os_translate_sys_error(int sys_errno) { +int os_translate_sys_error(int sys_errno) +{ #ifdef HAVE_UV_TRANSLATE_SYS_ERROR return uv_translate_sys_error(sys_errno); #elif defined(WIN32) diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index 31e06ce404..405500767d 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -37,7 +37,7 @@ typedef enum { static Stream read_stream = {.closed = true}; static RBuffer *input_buffer = NULL; static bool input_eof = false; -static int global_fd = 0; +static int global_fd = -1; static int events_enabled = 0; static bool blocking = false; @@ -188,7 +188,7 @@ size_t input_enqueue(String keys) uint8_t buf[6] = { 0 }; unsigned int new_size = trans_special((const uint8_t **)&ptr, (size_t)(end - ptr), buf, true, - true); + false); if (new_size) { new_size = handle_mouse_event(&ptr, buf, new_size); diff --git a/src/nvim/os/lang.c b/src/nvim/os/lang.c new file mode 100644 index 0000000000..47c278ee97 --- /dev/null +++ b/src/nvim/os/lang.c @@ -0,0 +1,43 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +#ifdef __APPLE__ +# define Boolean CFBoolean // Avoid conflict with API's Boolean +# include <CoreFoundation/CFLocale.h> +# include <CoreFoundation/CFString.h> +# undef Boolean +#endif + +#ifdef HAVE_LOCALE_H +# include <locale.h> +#endif +#include "nvim/os/os.h" + +void lang_init(void) +{ +#ifdef __APPLE__ + if (os_getenv("LANG") == NULL) { + CFLocaleRef cf_locale = CFLocaleCopyCurrent(); + CFTypeRef cf_lang_region = CFLocaleGetValue(cf_locale, + kCFLocaleIdentifier); + CFRetain(cf_lang_region); + CFRelease(cf_locale); + + const char *lang_region = CFStringGetCStringPtr(cf_lang_region, + kCFStringEncodingUTF8); + if (lang_region) { + os_setenv("LANG", lang_region, true); + } else { + char buf[20] = { 0 }; + if (CFStringGetCString(cf_lang_region, buf, 20, + kCFStringEncodingUTF8)) { + os_setenv("LANG", lang_region, true); + } + } + CFRelease(cf_lang_region); +# ifdef HAVE_LOCALE_H + setlocale(LC_ALL, ""); +# endif + } +#endif +} diff --git a/src/nvim/os/lang.h b/src/nvim/os/lang.h new file mode 100644 index 0000000000..f60e064f57 --- /dev/null +++ b/src/nvim/os/lang.h @@ -0,0 +1,7 @@ +#ifndef NVIM_OS_LANG_H +#define NVIM_OS_LANG_H + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/lang.h.generated.h" +#endif +#endif // NVIM_OS_LANG_H diff --git a/src/nvim/os/process.c b/src/nvim/os/process.c new file mode 100644 index 0000000000..e23ba8a4ee --- /dev/null +++ b/src/nvim/os/process.c @@ -0,0 +1,253 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +/// OS process functions +/// +/// psutil is a good reference for cross-platform syscall voodoo: +/// https://github.com/giampaolo/psutil/tree/master/psutil/arch + +#include <uv.h> // for HANDLE (win32) + +#ifdef WIN32 +# include <tlhelp32.h> // for CreateToolhelp32Snapshot +#endif + +#if defined(__FreeBSD__) // XXX: OpenBSD, NetBSD ? +# include <string.h> +# include <sys/types.h> +# include <sys/user.h> +#endif + +#if defined(__APPLE__) || defined(BSD) +# include <sys/sysctl.h> +# include <pwd.h> +#endif + +#include "nvim/globals.h" +#include "nvim/log.h" +#include "nvim/os/process.h" +#include "nvim/os/os.h" +#include "nvim/os/os_defs.h" +#include "nvim/api/private/helpers.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/process.c.generated.h" +#endif + +#ifdef WIN32 +static bool os_proc_tree_kill_rec(HANDLE process, int sig) +{ + if (process == NULL) { + return false; + } + PROCESSENTRY32 pe; + DWORD pid = GetProcessId(process); + + if (pid != 0) { + HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (h != INVALID_HANDLE_VALUE) { + pe.dwSize = sizeof(PROCESSENTRY32); + if (!Process32First(h, &pe)) { + goto theend; + } + do { + if (pe.th32ParentProcessID == pid) { + HANDLE ph = OpenProcess(PROCESS_ALL_ACCESS, false, pe.th32ProcessID); + if (ph != NULL) { + os_proc_tree_kill_rec(ph, sig); + CloseHandle(ph); + } + } + } while (Process32Next(h, &pe)); + CloseHandle(h); + } + } + +theend: + return (bool)TerminateProcess(process, (unsigned int)sig); +} +/// Kills process `pid` and its descendants recursively. +bool os_proc_tree_kill(int pid, int sig) +{ + assert(sig >= 0); + assert(sig == SIGTERM || sig == SIGKILL); + if (pid > 0) { + ILOG("terminating process tree: %d", pid); + HANDLE h = OpenProcess(PROCESS_ALL_ACCESS, false, (DWORD)pid); + return os_proc_tree_kill_rec(h, sig); + } else { + ELOG("invalid pid: %d", pid); + } + return false; +} +#else +/// Kills process group where `pid` is the process group leader. +bool os_proc_tree_kill(int pid, int sig) +{ + assert(sig == SIGTERM || sig == SIGKILL); + int pgid = getpgid(pid); + if (pgid > 0) { // Ignore error. Never kill self (pid=0). + if (pgid == pid) { + ILOG("sending %s to process group: -%d", + sig == SIGTERM ? "SIGTERM" : "SIGKILL", pgid); + int rv = uv_kill(-pgid, sig); + return rv == 0; + } else { + // Should never happen, because process_spawn() did setsid() in the child. + ELOG("pgid %d != pid %d", pgid, pid); + } + } else { + ELOG("getpgid(%d) returned %d", pid, pgid); + } + return false; +} +#endif + +/// Gets the process ids of the immediate children of process `ppid`. +/// +/// @param ppid Process to inspect. +/// @param[out,allocated] proc_list Child process ids. +/// @param[out] proc_count Number of child processes. +/// @return 0 on success, 1 if process not found, 2 on other error. +int os_proc_children(int ppid, int **proc_list, size_t *proc_count) +{ + if (ppid < 0) { + return 2; + } + + int *temp = NULL; + *proc_list = NULL; + *proc_count = 0; + +#ifdef WIN32 + PROCESSENTRY32 pe; + + // Snapshot of all processes. + HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (h == INVALID_HANDLE_VALUE) { + return 2; + } + + pe.dwSize = sizeof(PROCESSENTRY32); + // Get root process. + if (!Process32First(h, &pe)) { + CloseHandle(h); + return 2; + } + // Collect processes whose parent matches `ppid`. + do { + if (pe.th32ParentProcessID == (DWORD)ppid) { + temp = xrealloc(temp, (*proc_count + 1) * sizeof(*temp)); + temp[*proc_count] = (int)pe.th32ProcessID; + (*proc_count)++; + } + } while (Process32Next(h, &pe)); + CloseHandle(h); + +#elif defined(__APPLE__) || defined(BSD) +# if defined(__APPLE__) +# define KP_PID(o) o.kp_proc.p_pid +# define KP_PPID(o) o.kp_eproc.e_ppid +# elif defined(__FreeBSD__) +# define KP_PID(o) o.ki_pid +# define KP_PPID(o) o.ki_ppid +# else +# define KP_PID(o) o.p_pid +# define KP_PPID(o) o.p_ppid +# endif + static int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 }; + + // Get total process count. + size_t len = 0; + int rv = sysctl(name, ARRAY_SIZE(name) - 1, NULL, &len, NULL, 0); + if (rv) { + return 2; + } + + // Get ALL processes. + struct kinfo_proc *p_list = xmalloc(len); + rv = sysctl(name, ARRAY_SIZE(name) - 1, p_list, &len, NULL, 0); + if (rv) { + xfree(p_list); + return 2; + } + + // Collect processes whose parent matches `ppid`. + bool exists = false; + size_t p_count = len / sizeof(*p_list); + for (size_t i = 0; i < p_count; i++) { + exists = exists || KP_PID(p_list[i]) == ppid; + if (KP_PPID(p_list[i]) == ppid) { + temp = xrealloc(temp, (*proc_count + 1) * sizeof(*temp)); + temp[*proc_count] = KP_PID(p_list[i]); + (*proc_count)++; + } + } + xfree(p_list); + if (!exists) { + return 1; // Process not found. + } + +#elif defined(__linux__) + char proc_p[256] = { 0 }; + // Collect processes whose parent matches `ppid`. + // Rationale: children are defined in thread with same ID of process. + snprintf(proc_p, sizeof(proc_p), "/proc/%d/task/%d/children", ppid, ppid); + FILE *fp = fopen(proc_p, "r"); + if (fp == NULL) { + return 2; // Process not found, or /proc/…/children not supported. + } + int match_pid; + while (fscanf(fp, "%d", &match_pid) > 0) { + temp = xrealloc(temp, (*proc_count + 1) * sizeof(*temp)); + temp[*proc_count] = match_pid; + (*proc_count)++; + } + fclose(fp); +#endif + + *proc_list = temp; + return 0; +} + +#ifdef WIN32 +/// Gets various properties of the process identified by `pid`. +/// +/// @param pid Process to inspect. +/// @return Map of process properties, empty on error. +Dictionary os_proc_info(int pid) +{ + Dictionary pinfo = ARRAY_DICT_INIT; + PROCESSENTRY32 pe; + + // Snapshot of all processes. This is used instead of: + // OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, …) + // to avoid ERROR_PARTIAL_COPY. https://stackoverflow.com/a/29942376 + HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (h == INVALID_HANDLE_VALUE) { + return pinfo; // Return empty. + } + + pe.dwSize = sizeof(PROCESSENTRY32); + // Get root process. + if (!Process32First(h, &pe)) { + CloseHandle(h); + return pinfo; // Return empty. + } + // Find the process. + do { + if (pe.th32ProcessID == (DWORD)pid) { + break; + } + } while (Process32Next(h, &pe)); + CloseHandle(h); + + if (pe.th32ProcessID == (DWORD)pid) { + PUT(pinfo, "pid", INTEGER_OBJ(pid)); + PUT(pinfo, "ppid", INTEGER_OBJ((int)pe.th32ParentProcessID)); + PUT(pinfo, "name", STRING_OBJ(cstr_to_string(pe.szExeFile))); + } + + return pinfo; +} +#endif diff --git a/src/nvim/os/process.h b/src/nvim/os/process.h new file mode 100644 index 0000000000..1722d56bd3 --- /dev/null +++ b/src/nvim/os/process.h @@ -0,0 +1,11 @@ +#ifndef NVIM_OS_PROCESS_H +#define NVIM_OS_PROCESS_H + +#include <stddef.h> +#include "nvim/api/private/defs.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/process.h.generated.h" +#endif + +#endif // NVIM_OS_PROCESS_H diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c index ee3ab96a83..dfe2cfbb8d 100644 --- a/src/nvim/os/pty_process_unix.c +++ b/src/nvim/os/pty_process_unix.c @@ -36,31 +36,43 @@ # include "os/pty_process_unix.c.generated.h" #endif +/// termios saved at startup (for TUI) or initialized by pty_process_spawn(). +static struct termios termios_default; + +/// Saves the termios properties associated with `tty_fd`. +/// +/// @param tty_fd TTY file descriptor, or -1 if not in a terminal. +void pty_process_save_termios(int tty_fd) +{ + if (tty_fd == -1 || tcgetattr(tty_fd, &termios_default) != 0) { + return; + } +} + /// @returns zero on success, or negative error code int pty_process_spawn(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL { - static struct termios termios; - if (!termios.c_cflag) { - init_termios(&termios); + if (!termios_default.c_cflag) { + // TODO(jkeyes): We could pass NULL to forkpty() instead ... + init_termios(&termios_default); } int status = 0; // zero or negative error code (libuv convention) Process *proc = (Process *)ptyproc; - assert(!proc->err); + assert(proc->err.closed); uv_signal_start(&proc->loop->children_watcher, chld_handler, SIGCHLD); ptyproc->winsize = (struct winsize){ ptyproc->height, ptyproc->width, 0, 0 }; uv_disable_stdio_inheritance(); int master; - int pid = forkpty(&master, NULL, &termios, &ptyproc->winsize); + int pid = forkpty(&master, NULL, &termios_default, &ptyproc->winsize); if (pid < 0) { status = -errno; ELOG("forkpty failed: %s", strerror(errno)); return status; } else if (pid == 0) { - init_child(ptyproc); - abort(); + init_child(ptyproc); // never returns } // make sure the master file descriptor is non blocking @@ -83,12 +95,12 @@ int pty_process_spawn(PtyProcess *ptyproc) goto error; } - if (proc->in - && (status = set_duplicating_descriptor(master, &proc->in->uv.pipe))) { + if (!proc->in.closed + && (status = set_duplicating_descriptor(master, &proc->in.uv.pipe))) { goto error; } - if (proc->out - && (status = set_duplicating_descriptor(master, &proc->out->uv.pipe))) { + if (!proc->out.closed + && (status = set_duplicating_descriptor(master, &proc->out.uv.pipe))) { goto error; } @@ -133,8 +145,12 @@ void pty_process_teardown(Loop *loop) uv_signal_stop(&loop->children_watcher); } -static void init_child(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL +static void init_child(PtyProcess *ptyproc) + FUNC_ATTR_NONNULL_ALL { + // New session/process-group. #6530 + setsid(); + unsetenv("COLUMNS"); unsetenv("LINES"); unsetenv("TERMCAP"); @@ -150,14 +166,15 @@ static void init_child(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL Process *proc = (Process *)ptyproc; if (proc->cwd && os_chdir(proc->cwd) != 0) { - fprintf(stderr, "chdir failed: %s\n", strerror(errno)); + ELOG("chdir failed: %s", strerror(errno)); return; } char *prog = ptyproc->process.argv[0]; setenv("TERM", ptyproc->term_name ? ptyproc->term_name : "ansi", 1); execvp(prog, ptyproc->process.argv); - fprintf(stderr, "execvp failed: %s: %s\n", strerror(errno), prog); + ELOG("execvp failed: %s: %s", strerror(errno), prog); + _exit(122); // 122 is EXEC_FAILED in the Vim source. } static void init_termios(struct termios *termios) FUNC_ATTR_NONNULL_ALL diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c new file mode 100644 index 0000000000..a6774a6275 --- /dev/null +++ b/src/nvim/os/pty_process_win.c @@ -0,0 +1,413 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +#include <assert.h> +#include <stdbool.h> +#include <stdlib.h> + +#include <winpty_constants.h> + +#include "nvim/os/os.h" +#include "nvim/ascii.h" +#include "nvim/memory.h" +#include "nvim/mbyte.h" // for utf8_to_utf16, utf16_to_utf8 +#include "nvim/os/pty_process_win.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/pty_process_win.c.generated.h" +#endif + +static void CALLBACK pty_process_finish1(void *context, BOOLEAN unused) + FUNC_ATTR_NONNULL_ALL +{ + PtyProcess *ptyproc = (PtyProcess *)context; + Process *proc = (Process *)ptyproc; + + uv_timer_init(&proc->loop->uv, &ptyproc->wait_eof_timer); + ptyproc->wait_eof_timer.data = (void *)ptyproc; + uv_timer_start(&ptyproc->wait_eof_timer, wait_eof_timer_cb, 200, 200); +} + +/// @returns zero on success, or negative error code. +int pty_process_spawn(PtyProcess *ptyproc) + FUNC_ATTR_NONNULL_ALL +{ + Process *proc = (Process *)ptyproc; + int status = 0; + winpty_error_ptr_t err = NULL; + winpty_config_t *cfg = NULL; + winpty_spawn_config_t *spawncfg = NULL; + winpty_t *winpty_object = NULL; + char *in_name = NULL; + char *out_name = NULL; + HANDLE process_handle = NULL; + uv_connect_t *in_req = NULL; + uv_connect_t *out_req = NULL; + wchar_t *cmd_line = NULL; + wchar_t *cwd = NULL; + const char *emsg = NULL; + + assert(proc->err.closed); + + cfg = winpty_config_new(WINPTY_FLAG_ALLOW_CURPROC_DESKTOP_CREATION, &err); + if (cfg == NULL) { + emsg = "Failed, winpty_config_new."; + goto cleanup; + } + + winpty_config_set_initial_size(cfg, ptyproc->width, ptyproc->height); + winpty_object = winpty_open(cfg, &err); + if (winpty_object == NULL) { + emsg = "Failed, winpty_open."; + goto cleanup; + } + + status = utf16_to_utf8(winpty_conin_name(winpty_object), &in_name); + if (status != 0) { + emsg = "Failed to convert in_name from utf16 to utf8."; + goto cleanup; + } + + status = utf16_to_utf8(winpty_conout_name(winpty_object), &out_name); + if (status != 0) { + emsg = "Failed to convert out_name from utf16 to utf8."; + goto cleanup; + } + + if (!proc->in.closed) { + in_req = xmalloc(sizeof(uv_connect_t)); + uv_pipe_connect( + in_req, + &proc->in.uv.pipe, + in_name, + pty_process_connect_cb); + } + + if (!proc->out.closed) { + out_req = xmalloc(sizeof(uv_connect_t)); + uv_pipe_connect( + out_req, + &proc->out.uv.pipe, + out_name, + pty_process_connect_cb); + } + + if (proc->cwd != NULL) { + status = utf8_to_utf16(proc->cwd, &cwd); + if (status != 0) { + emsg = "Failed to convert pwd form utf8 to utf16."; + goto cleanup; + } + } + + status = build_cmd_line(proc->argv, &cmd_line, + os_shell_is_cmdexe(proc->argv[0])); + if (status != 0) { + emsg = "Failed to convert cmd line form utf8 to utf16."; + goto cleanup; + } + + spawncfg = winpty_spawn_config_new( + WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, + NULL, // Optional application name + cmd_line, + cwd, + NULL, // Optional environment variables + &err); + if (spawncfg == NULL) { + emsg = "Failed winpty_spawn_config_new."; + goto cleanup; + } + + DWORD win_err = 0; + if (!winpty_spawn(winpty_object, + spawncfg, + &process_handle, + NULL, // Optional thread handle + &win_err, + &err)) { + if (win_err) { + status = (int)win_err; + emsg = "Failed spawn process."; + } else { + emsg = "Failed winpty_spawn."; + } + goto cleanup; + } + proc->pid = (int)GetProcessId(process_handle); + + if (!RegisterWaitForSingleObject( + &ptyproc->finish_wait, + process_handle, + pty_process_finish1, + ptyproc, + INFINITE, + WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE)) { + abort(); + } + + // Wait until pty_process_connect_cb is called. + while ((in_req != NULL && in_req->handle != NULL) + || (out_req != NULL && out_req->handle != NULL)) { + uv_run(&proc->loop->uv, UV_RUN_ONCE); + } + + ptyproc->winpty_object = winpty_object; + ptyproc->process_handle = process_handle; + winpty_object = NULL; + process_handle = NULL; + +cleanup: + if (status) { + // In the case of an error of MultiByteToWideChar or CreateProcessW. + ELOG("%s error code: %d", emsg, status); + status = os_translate_sys_error(status); + } else if (err != NULL) { + status = (int)winpty_error_code(err); + ELOG("%s error code: %d", emsg, status); + status = translate_winpty_error(status); + } + winpty_error_free(err); + winpty_config_free(cfg); + winpty_spawn_config_free(spawncfg); + winpty_free(winpty_object); + xfree(in_name); + xfree(out_name); + if (process_handle != NULL) { + CloseHandle(process_handle); + } + xfree(in_req); + xfree(out_req); + xfree(cmd_line); + xfree(cwd); + return status; +} + +void pty_process_resize(PtyProcess *ptyproc, uint16_t width, + uint16_t height) + FUNC_ATTR_NONNULL_ALL +{ + if (ptyproc->winpty_object != NULL) { + winpty_set_size(ptyproc->winpty_object, width, height, NULL); + } +} + +void pty_process_close(PtyProcess *ptyproc) + FUNC_ATTR_NONNULL_ALL +{ + Process *proc = (Process *)ptyproc; + + pty_process_close_master(ptyproc); + + if (proc->internal_close_cb) { + proc->internal_close_cb(proc); + } +} + +void pty_process_close_master(PtyProcess *ptyproc) + FUNC_ATTR_NONNULL_ALL +{ + if (ptyproc->winpty_object != NULL) { + winpty_free(ptyproc->winpty_object); + ptyproc->winpty_object = NULL; + } +} + +void pty_process_teardown(Loop *loop) + FUNC_ATTR_NONNULL_ALL +{ +} + +static void pty_process_connect_cb(uv_connect_t *req, int status) + FUNC_ATTR_NONNULL_ALL +{ + assert(status == 0); + req->handle = NULL; +} + +static void wait_eof_timer_cb(uv_timer_t *wait_eof_timer) + FUNC_ATTR_NONNULL_ALL +{ + PtyProcess *ptyproc = wait_eof_timer->data; + Process *proc = (Process *)ptyproc; + + if (proc->out.closed || !uv_is_readable(proc->out.uvstream)) { + uv_timer_stop(&ptyproc->wait_eof_timer); + pty_process_finish2(ptyproc); + } +} + +static void pty_process_finish2(PtyProcess *ptyproc) + FUNC_ATTR_NONNULL_ALL +{ + Process *proc = (Process *)ptyproc; + + UnregisterWaitEx(ptyproc->finish_wait, NULL); + uv_close((uv_handle_t *)&ptyproc->wait_eof_timer, NULL); + + DWORD exit_code = 0; + GetExitCodeProcess(ptyproc->process_handle, &exit_code); + proc->status = (int)exit_code; + + CloseHandle(ptyproc->process_handle); + ptyproc->process_handle = NULL; + + proc->internal_exit_cb(proc); +} + +/// Build the command line to pass to CreateProcessW. +/// +/// @param[in] argv Array with string arguments. +/// @param[out] cmd_line Location where saved builded cmd line. +/// +/// @returns zero on success, or error code of MultiByteToWideChar function. +/// +static int build_cmd_line(char **argv, wchar_t **cmd_line, bool is_cmdexe) + FUNC_ATTR_NONNULL_ALL +{ + size_t utf8_cmd_line_len = 0; + size_t argc = 0; + QUEUE args_q; + + QUEUE_INIT(&args_q); + while (*argv) { + size_t buf_len = is_cmdexe ? (strlen(*argv) + 1) : (strlen(*argv) * 2 + 3); + ArgNode *arg_node = xmalloc(sizeof(*arg_node)); + arg_node->arg = xmalloc(buf_len); + if (is_cmdexe) { + xstrlcpy(arg_node->arg, *argv, buf_len); + } else { + quote_cmd_arg(arg_node->arg, buf_len, *argv); + } + utf8_cmd_line_len += strlen(arg_node->arg); + QUEUE_INIT(&arg_node->node); + QUEUE_INSERT_TAIL(&args_q, &arg_node->node); + argc++; + argv++; + } + + utf8_cmd_line_len += argc; + char *utf8_cmd_line = xmalloc(utf8_cmd_line_len); + *utf8_cmd_line = NUL; + while (1) { + QUEUE *head = QUEUE_HEAD(&args_q); + QUEUE_REMOVE(head); + ArgNode *arg_node = QUEUE_DATA(head, ArgNode, node); + xstrlcat(utf8_cmd_line, arg_node->arg, utf8_cmd_line_len); + xfree(arg_node->arg); + xfree(arg_node); + if (QUEUE_EMPTY(&args_q)) { + break; + } else { + xstrlcat(utf8_cmd_line, " ", utf8_cmd_line_len); + } + } + + int result = utf8_to_utf16(utf8_cmd_line, cmd_line); + xfree(utf8_cmd_line); + return result; +} + +/// Emulate quote_cmd_arg of libuv and quotes command line argument. +/// Most of the code came from libuv. +/// +/// @param[out] dest Location where saved quotes argument. +/// @param dest_remaining Destination buffer size. +/// @param[in] src Pointer to argument. +/// +static void quote_cmd_arg(char *dest, size_t dest_remaining, const char *src) + FUNC_ATTR_NONNULL_ALL +{ + size_t src_len = strlen(src); + bool quote_hit = true; + char *start = dest; + + if (src_len == 0) { + // Need double quotation for empty argument. + snprintf(dest, dest_remaining, "\"\""); + return; + } + + if (NULL == strpbrk(src, " \t\"")) { + // No quotation needed. + xstrlcpy(dest, src, dest_remaining); + return; + } + + if (NULL == strpbrk(src, "\"\\")) { + // No embedded double quotes or backlashes, so I can just wrap quote marks. + // around the whole thing. + snprintf(dest, dest_remaining, "\"%s\"", src); + return; + } + + // Expected input/output: + // input : 'hello"world' + // output: '"hello\"world"' + // input : 'hello""world' + // output: '"hello\"\"world"' + // input : 'hello\world' + // output: 'hello\world' + // input : 'hello\\world' + // output: 'hello\\world' + // input : 'hello\"world' + // output: '"hello\\\"world"' + // input : 'hello\\"world' + // output: '"hello\\\\\"world"' + // input : 'hello world\' + // output: '"hello world\\"' + + assert(dest_remaining--); + *(dest++) = NUL; + assert(dest_remaining--); + *(dest++) = '"'; + for (size_t i = src_len; i > 0; i--) { + assert(dest_remaining--); + *(dest++) = src[i - 1]; + if (quote_hit && src[i - 1] == '\\') { + assert(dest_remaining--); + *(dest++) = '\\'; + } else if (src[i - 1] == '"') { + quote_hit = true; + assert(dest_remaining--); + *(dest++) = '\\'; + } else { + quote_hit = false; + } + } + assert(dest_remaining); + *dest = '"'; + + while (start < dest) { + char tmp = *start; + *start = *dest; + *dest = tmp; + start++; + dest--; + } +} + +/// Translate winpty error code to libuv error. +/// +/// @param[in] winpty_errno Winpty error code returned by winpty_error_code +/// function. +/// +/// @returns Error code of libuv error. +int translate_winpty_error(int winpty_errno) +{ + if (winpty_errno <= 0) { + return winpty_errno; // If < 0 then it's already a libuv error. + } + + switch (winpty_errno) { + case WINPTY_ERROR_OUT_OF_MEMORY: return UV_ENOMEM; + case WINPTY_ERROR_SPAWN_CREATE_PROCESS_FAILED: return UV_EAI_FAIL; + case WINPTY_ERROR_LOST_CONNECTION: return UV_ENOTCONN; + case WINPTY_ERROR_AGENT_EXE_MISSING: return UV_ENOENT; + case WINPTY_ERROR_UNSPECIFIED: return UV_UNKNOWN; + case WINPTY_ERROR_AGENT_DIED: return UV_ESRCH; + case WINPTY_ERROR_AGENT_TIMEOUT: return UV_ETIMEDOUT; + case WINPTY_ERROR_AGENT_CREATION_FAILED: return UV_EAI_FAIL; + default: return UV_UNKNOWN; + } +} diff --git a/src/nvim/os/pty_process_win.h b/src/nvim/os/pty_process_win.h index 8e2b37a1c1..1a4019e654 100644 --- a/src/nvim/os/pty_process_win.h +++ b/src/nvim/os/pty_process_win.h @@ -1,20 +1,27 @@ #ifndef NVIM_OS_PTY_PROCESS_WIN_H #define NVIM_OS_PTY_PROCESS_WIN_H -#include "nvim/event/libuv_process.h" +#include <uv.h> +#include <winpty.h> + +#include "nvim/event/process.h" +#include "nvim/lib/queue.h" typedef struct pty_process { Process process; char *term_name; uint16_t width, height; + winpty_t *winpty_object; + HANDLE finish_wait; + HANDLE process_handle; + uv_timer_t wait_eof_timer; } PtyProcess; -#define pty_process_spawn(job) libuv_process_spawn((LibuvProcess *)job) -#define pty_process_close(job) libuv_process_close((LibuvProcess *)job) -#define pty_process_close_master(job) libuv_process_close((LibuvProcess *)job) -#define pty_process_resize(job, width, height) ( \ - (void)job, (void)width, (void)height, 0) -#define pty_process_teardown(loop) ((void)loop, 0) +// Structure used by build_cmd_line() +typedef struct arg_node { + char *arg; // pointer to argument. + QUEUE node; // QUEUE structure. +} ArgNode; static inline PtyProcess pty_process_init(Loop *loop, void *data) { @@ -23,7 +30,14 @@ static inline PtyProcess pty_process_init(Loop *loop, void *data) rv.term_name = NULL; rv.width = 80; rv.height = 24; + rv.winpty_object = NULL; + rv.finish_wait = NULL; + rv.process_handle = NULL; return rv; } +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/pty_process_win.h.generated.h" +#endif + #endif // NVIM_OS_PTY_PROCESS_WIN_H diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index 9d80a43718..f650a51fe7 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -123,6 +123,9 @@ int os_call_shell(char_u *cmd, ShellOpts opts, char_u *extra_args) if (opts & kShellOptRead) { output_ptr = &output; forward_output = false; + } else if (opts & kShellOptDoOut) { + // Caller has already redirected output + forward_output = false; } } @@ -189,6 +192,7 @@ static int do_os_system(char **argv, { out_data_decide_throttle(0); // Initialize throttle decider. out_data_ring(NULL, 0); // Initialize output ring-buffer. + bool has_input = (input != NULL && input[0] != '\0'); // the output buffer DynamicBuffer buf = DYNAMIC_BUFFER_INIT; @@ -207,16 +211,12 @@ static int do_os_system(char **argv, char prog[MAXPATHL]; xstrlcpy(prog, argv[0], MAXPATHL); - Stream in, out, err; LibuvProcess uvproc = libuv_process_init(&main_loop, &buf); Process *proc = &uvproc.process; MultiQueue *events = multiqueue_new_child(main_loop.events); proc->events = events; proc->argv = argv; - proc->in = input != NULL ? &in : NULL; - proc->out = &out; - proc->err = &err; - int status = process_spawn(proc); + int status = process_spawn(proc, has_input, true, true); if (status) { loop_poll_events(&main_loop, 0); // Failed, probably 'shell' is not executable. @@ -231,43 +231,54 @@ static int do_os_system(char **argv, return -1; } - // We want to deal with stream events as fast a possible while queueing - // process events, so reset everything to NULL. It prevents closing the + // Note: unlike process events, stream events are not queued, as we want to + // deal with stream events as fast a possible. It prevents closing the // streams while there's still data in the OS buffer (due to the process // exiting before all data is read). - if (input != NULL) { - proc->in->events = NULL; - wstream_init(proc->in, 0); + if (has_input) { + wstream_init(&proc->in, 0); } - proc->out->events = NULL; - rstream_init(proc->out, 0); - rstream_start(proc->out, data_cb, &buf); - proc->err->events = NULL; - rstream_init(proc->err, 0); - rstream_start(proc->err, data_cb, &buf); + rstream_init(&proc->out, 0); + rstream_start(&proc->out, data_cb, &buf); + rstream_init(&proc->err, 0); + rstream_start(&proc->err, data_cb, &buf); // write the input, if any - if (input) { - WBuffer *input_buffer = wstream_new_buffer((char *) input, len, 1, NULL); + if (has_input) { + WBuffer *input_buffer = wstream_new_buffer((char *)input, len, 1, NULL); - if (!wstream_write(&in, input_buffer)) { + if (!wstream_write(&proc->in, input_buffer)) { // couldn't write, stop the process and tell the user about it process_stop(proc); return -1; } // close the input stream after everything is written - wstream_set_write_cb(&in, shell_write_cb, NULL); + wstream_set_write_cb(&proc->in, shell_write_cb, NULL); } // Invoke busy_start here so LOOP_PROCESS_EVENTS_UNTIL will not change the // busy state. ui_busy_start(); ui_flush(); + if (forward_output) { + msg_sb_eol(); + msg_start(); + msg_no_more = true; + lines_left = -1; + } int exitcode = process_wait(proc, -1, NULL); if (!got_int && out_data_decide_throttle(0)) { // Last chunk of output was skipped; display it now. out_data_ring(NULL, SIZE_MAX); } + if (forward_output) { + // caller should decide if wait_return is invoked + no_wait_return++; + msg_end(); + no_wait_return--; + msg_no_more = false; + } + ui_busy_stop(); // prepare the out parameters if requested @@ -411,7 +422,7 @@ static void out_data_ring(char *output, size_t size) } if (output == NULL && size == SIZE_MAX) { // Print mode - out_data_append_to_screen(last_skipped, last_skipped_len, true); + out_data_append_to_screen(last_skipped, &last_skipped_len, true); return; } @@ -439,78 +450,58 @@ static void out_data_ring(char *output, size_t size) /// @param output Data to append to screen lines. /// @param remaining Size of data. /// @param new_line If true, next data output will be on a new line. -static void out_data_append_to_screen(char *output, size_t remaining, - bool new_line) +static void out_data_append_to_screen(char *output, size_t *count, + bool eof) { - static colnr_T last_col = 0; // Column of last row to append to. - - size_t off = 0; - int last_row = (int)Rows - 1; - - while (off < remaining) { - // Found end of line? - if (output[off] == NL) { - // Can we start a new line or do we need to continue the last one? - if (last_col == 0) { - screen_del_lines(0, 0, 1, (int)Rows, NULL); + char *p = output, *end = output + *count; + while (p < end) { + if (*p == '\n' || *p == '\r' || *p == TAB || *p == BELL) { + msg_putchar_attr((uint8_t)(*p), 0); + p++; + } else { + // Note: this is not 100% precise: + // 1. we don't check if received continuation bytes are already invalid + // and we thus do some buffering that could be avoided + // 2. we don't compose chars over buffer boundaries, even if we see an + // incomplete UTF-8 sequence that could be composing with the last + // complete sequence. + // This will be corrected when we switch to vterm based implementation + int i = *p ? mb_ptr2len_len((char_u *)p, (int)(end-p)) : 1; + if (!eof && i == 1 && utf8len_tab_zero[*(uint8_t *)p] > (end-p)) { + *count = (size_t)(p - output); + goto end; } - screen_puts_len((char_u *)output, (int)off, last_row, last_col, 0); - last_col = 0; - - size_t skip = off + 1; - output += skip; - remaining -= skip; - off = 0; - continue; - } - // TODO(bfredl): using msg_puts would be better until - // terminal emulation is implemented. - if (output[off] < 0x20) { - output[off] = ' '; + (void)msg_outtrans_len_attr((char_u *)p, i, 0); + p += i; } - - off++; - } - - if (remaining) { - if (last_col == 0) { - screen_del_lines(0, 0, 1, (int)Rows, NULL); - } - screen_puts_len((char_u *)output, (int)remaining, last_row, last_col, 0); - last_col += (colnr_T)remaining; - } - - if (new_line) { - last_col = 0; } +end: ui_flush(); } static void out_data_cb(Stream *stream, RBuffer *buf, size_t count, void *data, bool eof) { - // We always output the whole buffer, so the buffer can never - // wrap around. size_t cnt; char *ptr = rbuffer_read_ptr(buf, &cnt); - if (ptr == NULL || cnt == 0) { - // Nothing to read; - return; - } - - if (out_data_decide_throttle(cnt)) { // Skip output above a threshold. + if (ptr != NULL && cnt > 0 + && out_data_decide_throttle(cnt)) { // Skip output above a threshold. // Save the skipped output. If it is the final chunk, we display it later. out_data_ring(ptr, cnt); } else { - out_data_append_to_screen(ptr, cnt, eof); + out_data_append_to_screen(ptr, &cnt, eof); } if (cnt) { rbuffer_consumed(buf, cnt); } + + // Move remaining data to start of buffer, so the buffer can never + // wrap around. + rbuffer_reset(buf); } /// Parses a command string into a sequence of words, taking quotes into @@ -599,14 +590,10 @@ static void read_input(DynamicBuffer *buf) if (len == l) { // Finished a line, add a NL, unless this line should not have one. - // FIXME need to make this more readable if (lnum != curbuf->b_op_end.lnum - || (!curbuf->b_p_bin - && curbuf->b_p_fixeol) + || (!curbuf->b_p_bin && curbuf->b_p_fixeol) || (lnum != curbuf->b_no_eol_lnum - && (lnum != - curbuf->b_ml.ml_line_count - || curbuf->b_p_eol))) { + && (lnum != curbuf->b_ml.ml_line_count || curbuf->b_p_eol))) { dynamic_buffer_ensure(buf, buf->len + 1); buf->data[buf->len++] = NL; } @@ -688,10 +675,6 @@ static void shell_write_cb(Stream *stream, void *data, int status) msg_schedule_emsgf(_("E5677: Error writing input to shell-command: %s"), uv_err_name(status)); } - if (stream->closed) { // Process may have exited before this write. - WLOG("stream was already closed"); - return; - } stream_close(stream, NULL, NULL); } diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c index fd6d3b32e4..732be072e1 100644 --- a/src/nvim/os/signal.c +++ b/src/nvim/os/signal.c @@ -10,6 +10,7 @@ #endif #include "nvim/ascii.h" +#include "nvim/log.h" #include "nvim/vim.h" #include "nvim/globals.h" #include "nvim/memline.h" @@ -162,7 +163,7 @@ static void on_signal(SignalWatcher *handle, int signum, void *data) } break; default: - fprintf(stderr, "Invalid signal %d", signum); + ELOG("invalid signal: %d", signum); break; } } diff --git a/src/nvim/os/time.c b/src/nvim/os/time.c index c471352c02..290d421acc 100644 --- a/src/nvim/os/time.c +++ b/src/nvim/os/time.c @@ -38,33 +38,33 @@ uint64_t os_hrtime(void) return uv_hrtime(); } -/// Sleeps for a certain amount of milliseconds. +/// Sleeps for `ms` milliseconds. /// -/// @param milliseconds Number of milliseconds to sleep +/// @param ms Number of milliseconds to sleep /// @param ignoreinput If true, only SIGINT (CTRL-C) can interrupt. -void os_delay(uint64_t milliseconds, bool ignoreinput) +void os_delay(uint64_t ms, bool ignoreinput) { if (ignoreinput) { - if (milliseconds > INT_MAX) { - milliseconds = INT_MAX; + if (ms > INT_MAX) { + ms = INT_MAX; } - LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, (int)milliseconds, got_int); + LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, (int)ms, got_int); } else { - os_microdelay(milliseconds * 1000u, ignoreinput); + os_microdelay(ms * 1000u, ignoreinput); } } -/// Sleeps for a certain amount of microseconds. +/// Sleeps for `us` microseconds. /// -/// @param ms Number of microseconds to sleep. +/// @param us Number of microseconds to sleep. /// @param ignoreinput If true, ignore all input (including SIGINT/CTRL-C). /// If false, waiting is aborted on any input. -void os_microdelay(uint64_t ms, bool ignoreinput) +void os_microdelay(uint64_t us, bool ignoreinput) { uint64_t elapsed = 0u; uint64_t base = uv_hrtime(); // Convert microseconds to nanoseconds, or UINT64_MAX on overflow. - const uint64_t ns = (ms < UINT64_MAX / 1000u) ? ms * 1000u : UINT64_MAX; + const uint64_t ns = (us < UINT64_MAX / 1000u) ? us * 1000u : UINT64_MAX; uv_mutex_lock(&delay_mutex); diff --git a/src/nvim/os/unix_defs.h b/src/nvim/os/unix_defs.h index 5c9daca476..60a2dfa882 100644 --- a/src/nvim/os/unix_defs.h +++ b/src/nvim/os/unix_defs.h @@ -1,9 +1,8 @@ #ifndef NVIM_OS_UNIX_DEFS_H #define NVIM_OS_UNIX_DEFS_H -// Windows doesn't have unistd.h, so we include it here to avoid numerous -// instances of `#ifdef WIN32'. #include <unistd.h> +#include <sys/param.h> // POSIX.1-2008 says that NAME_MAX should be in here #include <limits.h> diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h index 7c980c3768..db93f016bf 100644 --- a/src/nvim/os/win_defs.h +++ b/src/nvim/os/win_defs.h @@ -30,8 +30,10 @@ #define USE_CRNL -// We have our own RGB macro in macros.h. -#undef RGB +// Windows defines a RGB macro that produces 0x00bbggrr color values for use +// with GDI. Our macro is different, and we don't use GDI. +// Duplicated from macros.h to avoid include-order sensitivity. +#define RGB_(r, g, b) ((r << 16) | (g << 8) | b) #ifdef _MSC_VER # ifndef inline @@ -40,6 +42,9 @@ # ifndef restrict # define restrict __restrict # endif +# ifndef STDIN_FILENO +# define STDIN_FILENO _fileno(stdin) +# endif # ifndef STDOUT_FILENO # define STDOUT_FILENO _fileno(stdout) # endif @@ -55,6 +60,7 @@ #ifdef _MSC_VER typedef SSIZE_T ssize_t; +typedef int mode_t; #endif #ifndef SSIZE_MAX @@ -91,4 +97,14 @@ typedef SSIZE_T ssize_t; # define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) #endif +#ifndef STDIN_FILENO +# define STDIN_FILENO 0 +#endif +#ifndef STDOUT_FILENO +# define STDOUT_FILENO 1 +#endif +#ifndef STDERR_FILENO +# define STDERR_FILENO 2 +#endif + #endif // NVIM_OS_WIN_DEFS_H diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c index 692bcc97f4..a27fee4e90 100644 --- a/src/nvim/os_unix.c +++ b/src/nvim/os_unix.c @@ -133,7 +133,8 @@ void mch_free_acl(vim_acl_T aclent) } #endif -void mch_exit(int r) FUNC_ATTR_NORETURN +void mch_exit(int r) + FUNC_ATTR_NORETURN { exiting = true; @@ -144,7 +145,9 @@ void mch_exit(int r) FUNC_ATTR_NORETURN if (!event_teardown() && r == 0) { r = 1; // Exit with error if main_loop did not teardown gracefully. } - stream_set_blocking(input_global_fd(), true); // normalize stream (#2598) + if (input_global_fd() >= 0) { + stream_set_blocking(input_global_fd(), true); // normalize stream (#2598) + } #ifdef EXITFREE free_all_mem(); diff --git a/src/nvim/path.c b/src/nvim/path.c index f2339c8046..168d835a66 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -332,7 +332,7 @@ int path_fnamencmp(const char *const fname1, const char *const fname2, && (p_fic ? (c1 != c2 && CH_FOLD(c1) != CH_FOLD(c2)) : c1 != c2)) { break; } - len -= MB_PTR2LEN((const char_u *)p1); + len -= (size_t)MB_PTR2LEN((const char_u *)p1); p1 += MB_PTR2LEN((const char_u *)p1); p2 += MB_PTR2LEN((const char_u *)p2); } @@ -452,10 +452,10 @@ char *FullName_save(const char *fname, bool force) /// Saves the absolute path. /// @param name An absolute or relative path. /// @return The absolute path of `name`. -char_u *save_absolute_path(const char_u *name) +char_u *save_abs_path(const char_u *name) FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { - if (!path_is_absolute_path(name)) { + if (!path_is_absolute(name)) { return (char_u *)FullName_save((char *)name, true); } return vim_strsave((char_u *) name); @@ -814,7 +814,7 @@ static void expand_path_option(char_u *curdir, garray_T *gap) 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)) { + } else if (!path_is_absolute(buf)) { // Expand relative path to their full path equivalent size_t len = STRLEN(curdir); if (len + STRLEN(buf) + 3 > MAXPATHL) { @@ -949,19 +949,17 @@ static void uniquefy_paths(garray_T *gap, char_u *pattern) } } - if (path_is_absolute_path(path)) { - /* - * Last resort: shorten relative to curdir if possible. - * 'possible' means: - * 1. It is under the current directory. - * 2. The result is actually shorter than the original. - * - * Before curdir After - * /foo/bar/file.txt /foo/bar ./file.txt - * c:\foo\bar\file.txt c:\foo\bar .\file.txt - * /file.txt / /file.txt - * c:\file.txt c:\ .\file.txt - */ + if (path_is_absolute(path)) { + // Last resort: shorten relative to curdir if possible. + // 'possible' means: + // 1. It is under the current directory. + // 2. The result is actually shorter than the original. + // + // Before curdir After + // /foo/bar/file.txt /foo/bar ./file.txt + // c:\foo\bar\file.txt c:\foo\bar .\file.txt + // /file.txt / /file.txt + // c:\file.txt c:\ .\file.txt short_name = path_shorten_fname(path, curdir); if (short_name != NULL && short_name > path + 1 ) { @@ -1221,7 +1219,7 @@ int gen_expand_wildcards(int num_pat, char_u **pat, int *num_file, */ if (path_has_exp_wildcard(p)) { if ((flags & EW_PATH) - && !path_is_absolute_path(p) + && !path_is_absolute(p) && !(p[0] == '.' && (vim_ispathsep(p[1]) || (p[1] == '.' && vim_ispathsep(p[2])))) @@ -1667,7 +1665,7 @@ int path_with_url(const char *fname) */ bool vim_isAbsName(char_u *name) { - return path_with_url((char *)name) != 0 || path_is_absolute_path(name); + return path_with_url((char *)name) != 0 || path_is_absolute(name); } /// Save absolute file name to "buf[len]". @@ -1690,6 +1688,9 @@ int vim_FullName(const char *fname, char *buf, size_t len, bool force) if (strlen(fname) > (len - 1)) { xstrlcpy(buf, fname, len); // truncate +#ifdef WIN32 + slash_adjust((char_u *)buf); +#endif return FAIL; } @@ -1698,10 +1699,13 @@ int vim_FullName(const char *fname, char *buf, size_t len, bool force) return OK; } - int rv = path_get_absolute_path((char_u *)fname, (char_u *)buf, len, force); + int rv = path_to_absolute((char_u *)fname, (char_u *)buf, len, force); if (rv == FAIL) { xstrlcpy(buf, fname, len); // something failed; use the filename } +#ifdef WIN32 + slash_adjust((char_u *)buf); +#endif return rv; } @@ -1735,7 +1739,7 @@ char *fix_fname(const char *fname) path_fix_case((char_u *)fname); // set correct case for file name # endif - return fname; + return (char *)fname; #endif } @@ -1904,7 +1908,7 @@ int pathcmp(const char *p, const char *q, int maxlen) /// - Pointer into `full_path` if shortened. /// - `full_path` unchanged if no shorter name is possible. /// - NULL if `full_path` is NULL. -char_u *path_shorten_fname_if_possible(char_u *full_path) +char_u *path_try_shorten_fname(char_u *full_path) { char_u *dirname = xmalloc(MAXPATHL); char_u *p = full_path; @@ -2185,8 +2189,8 @@ int append_path(char *path, const char *to_append, size_t max_len) /// @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) +static int path_to_absolute(const char_u *fname, char_u *buf, size_t len, + int force) { char_u *p; *buf = NUL; @@ -2195,12 +2199,18 @@ static int path_get_absolute_path(const char_u *fname, char_u *buf, char *end_of_path = (char *) fname; // expand it if forced or not an absolute path - if (force || !path_is_absolute_path(fname)) { - if ((p = vim_strrchr(fname, '/')) != NULL) { + if (force || !path_is_absolute(fname)) { + p = vim_strrchr(fname, '/'); +#ifdef WIN32 + if (p == NULL) { + p = vim_strrchr(fname, '\\'); + } +#endif + if (p != NULL) { // relative to root if (p == fname) { // only one path component - relative_directory[0] = '/'; + relative_directory[0] = PATHSEP; relative_directory[1] = NUL; } else { assert(p >= fname); @@ -2222,10 +2232,10 @@ static int path_get_absolute_path(const char_u *fname, char_u *buf, return append_path((char *)buf, end_of_path, len); } -/// Check if the given file is absolute. +/// Check if file `fname` is a full (absolute) path. /// /// @return `TRUE` if "fname" is absolute. -int path_is_absolute_path(const char_u *fname) +int path_is_absolute(const char_u *fname) { #ifdef WIN32 // A name like "d:/foo" and "//server/share" is absolute @@ -2250,7 +2260,7 @@ void path_guess_exepath(const char *argv0, char *buf, size_t bufsize) { char *path = getenv("PATH"); - if (path == NULL || path_is_absolute_path((char_u *)argv0)) { + if (path == NULL || path_is_absolute((char_u *)argv0)) { xstrlcpy(buf, argv0, bufsize); } else if (argv0[0] == '.' || strchr(argv0, PATHSEP)) { // Relative to CWD. diff --git a/src/nvim/po/af.po b/src/nvim/po/af.po index eb6be42688..f42ad04409 100644 --- a/src/nvim/po/af.po +++ b/src/nvim/po/af.po @@ -26,7 +26,7 @@ msgid "" msgstr "" "Project-Id-Version: Vim 6.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-05-26 14:21+0200\n" +"POT-Creation-Date: 2017-11-07 20:04+0100\n" "PO-Revision-Date: Wed Oct 31 13:41 SAST 2001\n" "Last-Translator: Danie Roux <droux@tuks.co.za>\n" "Language-Team: Danie Roux <droux@tuks.co.za>\n" @@ -35,101 +35,74 @@ msgstr "" "Content-Type: text/plain; charset=ISO_8859-1\n" "Content-Transfer-Encoding: 8-bit\n" -#: ../api/private/helpers.c:201 -#, fuzzy -msgid "Unable to get option value" -msgstr "E258: Kan nie na klint stuur nie" - -#: ../api/private/helpers.c:204 -msgid "internal error: unknown option type" -msgstr "" - -#: ../buffer.c:92 -msgid "[Location List]" -msgstr "" +#~ msgid "[Location List]" +#~ msgstr "" -#: ../buffer.c:93 -msgid "[Quickfix List]" -msgstr "" +#~ msgid "[Quickfix List]" +#~ msgstr "" -#: ../buffer.c:94 -msgid "E855: Autocommands caused command to abort" -msgstr "" +#~ msgid "E855: Autocommands caused command to abort" +#~ msgstr "" -#: ../buffer.c:135 msgid "E82: Cannot allocate any buffer, exiting..." msgstr "E82: Kan nie buffer toeken nie, program sluit..." -#: ../buffer.c:138 msgid "E83: Cannot allocate buffer, using other one..." msgstr "E83: Kan nie buffer toeken nie, gaan ander een gebruik..." -#: ../buffer.c:763 +#~ msgid "E937: Attempt to delete a buffer that is in use" +#~ msgstr "" + msgid "E515: No buffers were unloaded" msgstr "E515: Geen buffers is uitgelaai nie" -#: ../buffer.c:765 msgid "E516: No buffers were deleted" msgstr "E516: Geen buffers is geskrap nie" -#: ../buffer.c:767 msgid "E517: No buffers were wiped out" msgstr "E517: Geen buffers is geskrap nie" -#: ../buffer.c:772 msgid "1 buffer unloaded" msgstr "1 buffer uitgelaai" -#: ../buffer.c:774 #, c-format msgid "%d buffers unloaded" msgstr "%d buffers uitgelaai" -#: ../buffer.c:777 msgid "1 buffer deleted" msgstr "1 buffer geskrap" -#: ../buffer.c:779 #, c-format msgid "%d buffers deleted" msgstr "%d buffers geskrap" -#: ../buffer.c:782 msgid "1 buffer wiped out" msgstr "1 buffer geskrap" -#: ../buffer.c:784 #, c-format msgid "%d buffers wiped out" msgstr "%d buffers geskrap" -#: ../buffer.c:806 msgid "E90: Cannot unload last buffer" msgstr "E90: Kan nie laaste buffer uitlaai nie" -#: ../buffer.c:874 msgid "E84: No modified buffer found" msgstr "E84: Geen veranderde buffer gevind nie" #. back where we started, didn't find anything. -#: ../buffer.c:903 msgid "E85: There is no listed buffer" msgstr "E85: Daar is geen gelyste buffer nie" -#: ../buffer.c:913 -#, c-format -msgid "E86: Buffer %<PRId64> does not exist" -msgstr "E86: Buffer %<PRId64> bestaan nie" - -#: ../buffer.c:915 msgid "E87: Cannot go beyond last buffer" msgstr "E87: Kan nie verby laaste buffer gaan nie" -#: ../buffer.c:917 msgid "E88: Cannot go before first buffer" msgstr "E88: Kan nie vr eerste buffer gaan nie" -#: ../buffer.c:945 +#, fuzzy, c-format +#~ msgid "E89: %s will be killed(add ! to override)" +#~ msgstr "E189: \"%s\" bestaan (gebruik ! om te dwing)" + #, c-format msgid "" "E89: No write since last change for buffer %<PRId64> (add ! to override)" @@ -138,117 +111,85 @@ msgstr "" "dwing)" #. wrap around (may cause duplicates) -#: ../buffer.c:1423 msgid "W14: Warning: List of file names overflow" msgstr "W14: Waarskuwing: Lerlys loop oor" -#: ../buffer.c:1555 ../quickfix.c:3361 #, c-format msgid "E92: Buffer %<PRId64> not found" msgstr "E92: buffer %<PRId64> kon nie gevind word nie" -#: ../buffer.c:1798 #, c-format msgid "E93: More than one match for %s" msgstr "E93: Meer as een treffer vir %s" -#: ../buffer.c:1800 #, c-format msgid "E94: No matching buffer for %s" msgstr "E94: Geen buffer wat by %s pas nie" -#: ../buffer.c:2161 #, c-format msgid "line %<PRId64>" msgstr "rel %<PRId64>" -#: ../buffer.c:2233 msgid "E95: Buffer with this name already exists" msgstr "E95: Buffer met hierdie naam bestaan alreeds" -#: ../buffer.c:2498 msgid " [Modified]" msgstr " [Gewysig]" -#: ../buffer.c:2501 msgid "[Not edited]" msgstr "[Ongewysig]" -#: ../buffer.c:2504 msgid "[New file]" msgstr "[Nuwe ler]" -#: ../buffer.c:2505 msgid "[Read errors]" msgstr "[Leesfoute]" -#: ../buffer.c:2506 ../buffer.c:3217 ../fileio.c:1807 ../screen.c:4895 msgid "[RO]" msgstr "[RO]" -#: ../buffer.c:2507 ../fileio.c:1807 msgid "[readonly]" msgstr "[lees alleen]" -#: ../buffer.c:2524 #, c-format msgid "1 line --%d%%--" msgstr "1 rel --%d%%--" -#: ../buffer.c:2526 #, c-format msgid "%<PRId64> lines --%d%%--" msgstr "%<PRId64> rels --%d%%--" -#: ../buffer.c:2530 #, c-format msgid "line %<PRId64> of %<PRId64> --%d%%-- col " msgstr "rel %<PRId64> van %<PRId64> --%d%%-- kolom " -#: ../buffer.c:2632 ../buffer.c:4292 ../memline.c:1554 #, fuzzy -msgid "[No Name]" -msgstr "[Geen ler]" +#~ msgid "[No Name]" +#~ msgstr "[Geen ler]" #. must be a help buffer -#: ../buffer.c:2667 msgid "help" msgstr "help" -#: ../buffer.c:3225 ../screen.c:4883 #, fuzzy -msgid "[Help]" -msgstr "[help]" +#~ msgid "[Help]" +#~ msgstr "[help]" -#: ../buffer.c:3254 ../screen.c:4887 msgid "[Preview]" msgstr "[Voorskou]" -#: ../buffer.c:3528 msgid "All" msgstr "Alles" -#: ../buffer.c:3528 msgid "Bot" msgstr "Ond" -#: ../buffer.c:3531 msgid "Top" msgstr "Bo" -#: ../buffer.c:4244 -msgid "" -"\n" -"# Buffer list:\n" -msgstr "" -"\n" -"# Buffer lys:\n" - -#: ../buffer.c:4289 -msgid "[Scratch]" -msgstr "" +#~ msgid "[Scratch]" +#~ msgstr "" -#: ../buffer.c:4529 msgid "" "\n" "--- Signs ---" @@ -256,208 +197,161 @@ msgstr "" "\n" "--- Tekens ---" -#: ../buffer.c:4538 #, c-format msgid "Signs for %s:" msgstr "Tekens vir %s:" -#: ../buffer.c:4543 #, c-format msgid " line=%<PRId64> id=%d name=%s" msgstr " rel=%<PRId64> id=%d naam=%s" -#: ../cursor_shape.c:68 msgid "E545: Missing colon" msgstr "E545: Ontbrekende dubbelpunt" -#: ../cursor_shape.c:70 ../cursor_shape.c:94 msgid "E546: Illegal mode" msgstr "E546: Ongeldige modus" -#: ../cursor_shape.c:134 msgid "E548: digit expected" msgstr "E548: syfer verwag" -#: ../cursor_shape.c:138 msgid "E549: Illegal percentage" msgstr "E549: Ongeldige persentasie" -#: ../diff.c:146 -#, c-format -msgid "E96: Can not diff more than %<PRId64> buffers" -msgstr "E96: Kan nie meer as %<PRId64> buffers 'diff' nie" +#, fuzzy, c-format +#~ msgid "E96: Cannot diff more than %<PRId64> buffers" +#~ msgstr "E96: Kan nie meer as %<PRId64> buffers 'diff' nie" -#: ../diff.c:753 #, fuzzy -msgid "E810: Cannot read or write temp files" -msgstr "E557: Kan nie 'termcap'-ler oopmaak nie" +#~ msgid "E810: Cannot read or write temp files" +#~ msgstr "E557: Kan nie 'termcap'-ler oopmaak nie" -#: ../diff.c:755 msgid "E97: Cannot create diffs" msgstr "E97: Kan nie 'diffs' skep nie " -#: ../diff.c:966 #, fuzzy -msgid "E816: Cannot read patch output" -msgstr "E98: Kan nie 'diff' afvoer lees nie" +#~ msgid "E816: Cannot read patch output" +#~ msgstr "E98: Kan nie 'diff' afvoer lees nie" -#: ../diff.c:1220 msgid "E98: Cannot read diff output" msgstr "E98: Kan nie 'diff' afvoer lees nie" -#: ../diff.c:2081 msgid "E99: Current buffer is not in diff mode" msgstr "E99: Huidige buffer is nie in 'diff' modus nie" -#: ../diff.c:2100 #, fuzzy -msgid "E793: No other buffer in diff mode is modifiable" -msgstr "E100: Geen ander buffer in 'diff' modus nie" +#~ msgid "E793: No other buffer in diff mode is modifiable" +#~ msgstr "E100: Geen ander buffer in 'diff' modus nie" -#: ../diff.c:2102 msgid "E100: No other buffer in diff mode" msgstr "E100: Geen ander buffer in 'diff' modus nie" -#: ../diff.c:2112 msgid "E101: More than two buffers in diff mode, don't know which one to use" msgstr "" "E101: Meer as twee buffers in 'diff' modus, weet nie watter een om te " "gebruik nie" -#: ../diff.c:2141 #, c-format msgid "E102: Can't find buffer \"%s\"" msgstr "E102: Kan buffer %s nie vind nie" -#: ../diff.c:2152 #, c-format msgid "E103: Buffer \"%s\" is not in diff mode" msgstr "E103: Buffer \"%s\" is nie in 'diff' modus nie" -#: ../diff.c:2193 -msgid "E787: Buffer changed unexpectedly" -msgstr "" +#~ msgid "E787: Buffer changed unexpectedly" +#~ msgstr "" -#: ../digraph.c:1598 msgid "E104: Escape not allowed in digraph" msgstr "E104: 'Escape' nie toegelaat in digraaf nie" -#: ../digraph.c:1760 msgid "E544: Keymap file not found" msgstr "E544: Sleutelbindingler nie gevind nie" -#: ../digraph.c:1785 msgid "E105: Using :loadkeymap not in a sourced file" msgstr "E105: :loadkeymap word buite 'n uitvoerler gebruik" -#: ../digraph.c:1821 -msgid "E791: Empty keymap entry" -msgstr "" +#~ msgid "E791: Empty keymap entry" +#~ msgstr "" -#: ../edit.c:82 msgid " Keyword completion (^N^P)" msgstr " Sleutelwoord voltooiing (^N^P)" #. ctrl_x_mode == 0, ^P/^N compl. -#: ../edit.c:83 #, fuzzy -msgid " ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)" -msgstr " ^X modus (^E^Y^L^]^F^I^K^D^V^N^P)" +#~ msgid " ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)" +#~ msgstr " ^X modus (^E^Y^L^]^F^I^K^D^V^N^P)" -#: ../edit.c:85 msgid " Whole line completion (^L^N^P)" msgstr " Hele-rel voltooiing (^L^N^P)" -#: ../edit.c:86 msgid " File name completion (^F^N^P)" msgstr " Lernaam voltooiing (^F^N^P)" -#: ../edit.c:87 msgid " Tag completion (^]^N^P)" msgstr " Etiketvoltooiing (^]^N^P)" -#: ../edit.c:88 msgid " Path pattern completion (^N^P)" msgstr " Gidspatroon voltooiing (^N^P)" -#: ../edit.c:89 msgid " Definition completion (^D^N^P)" msgstr " Definisievoltooiing (^D^N^P)" -#: ../edit.c:91 msgid " Dictionary completion (^K^N^P)" msgstr " Woordeboekvoltooiing (^K^N^P)" -#: ../edit.c:92 msgid " Thesaurus completion (^T^N^P)" msgstr " Tesourusvoltooiing (^T^N^P)" -#: ../edit.c:93 msgid " Command-line completion (^V^N^P)" msgstr " Bevelrelvoltooiing (^V^N^P)" -#: ../edit.c:94 #, fuzzy -msgid " User defined completion (^U^N^P)" -msgstr " Hele-rel voltooiing (^L^N^P)" +#~ msgid " User defined completion (^U^N^P)" +#~ msgstr " Hele-rel voltooiing (^L^N^P)" -#: ../edit.c:95 #, fuzzy -msgid " Omni completion (^O^N^P)" -msgstr " Etiketvoltooiing (^]^N^P)" +#~ msgid " Omni completion (^O^N^P)" +#~ msgstr " Etiketvoltooiing (^]^N^P)" -#: ../edit.c:96 #, fuzzy -msgid " Spelling suggestion (s^N^P)" -msgstr " Hele-rel voltooiing (^L^N^P)" +#~ msgid " Spelling suggestion (s^N^P)" +#~ msgstr " Hele-rel voltooiing (^L^N^P)" -#: ../edit.c:97 msgid " Keyword Local completion (^N^P)" msgstr " Sleutelwoord Lokale voltooiing (^N^P)" -#: ../edit.c:100 msgid "Hit end of paragraph" msgstr "Het einde van paragraaf getref" -#: ../edit.c:101 -msgid "E839: Completion function changed window" -msgstr "" +#~ msgid "E839: Completion function changed window" +#~ msgstr "" -#: ../edit.c:102 -msgid "E840: Completion function deleted text" -msgstr "" +#~ msgid "E840: Completion function deleted text" +#~ msgstr "" -#: ../edit.c:1847 msgid "'dictionary' option is empty" msgstr "'dictionary' opsie is leeg" -#: ../edit.c:1848 msgid "'thesaurus' option is empty" msgstr "'thesaurus' opsie is leeg" -#: ../edit.c:2655 #, c-format msgid "Scanning dictionary: %s" msgstr "Deursoek woordeboek: %s" -#: ../edit.c:3079 msgid " (insert) Scroll (^E/^Y)" msgstr " (invoeg) Rol (^E/^Y)" -#: ../edit.c:3081 msgid " (replace) Scroll (^E/^Y)" msgstr " (vervang) Rol (^E/^Y)" -#: ../edit.c:3587 #, c-format msgid "Scanning: %s" msgstr "Soek vir: %s" -#: ../edit.c:3614 msgid "Scanning tags." msgstr "Deursoek etikette." -#: ../edit.c:4519 msgid " Adding" msgstr " Word bygevoeg" @@ -465,605 +359,550 @@ msgstr " Word bygevoeg" #. * be called before line = ml_get(), or when this address is no #. * longer needed. -- Acevedo. #. -#: ../edit.c:4562 msgid "-- Searching..." msgstr "-- Soekend..." -#: ../edit.c:4618 msgid "Back at original" msgstr "Terug by oorspronklike" -#: ../edit.c:4621 msgid "Word from other line" msgstr "Woord van ander rel" -#: ../edit.c:4624 msgid "The only match" msgstr "Die enigste treffer" -#: ../edit.c:4680 #, c-format msgid "match %d of %d" msgstr "treffer %d van %d" -#: ../edit.c:4684 #, c-format msgid "match %d" msgstr "treffer %d" -#: ../eval.c:137 #, fuzzy -msgid "E18: Unexpected characters in :let" -msgstr "E18: Onverwagte karakters voor '='" +#~ msgid "E18: Unexpected characters in :let" +#~ msgstr "E18: Onverwagte karakters voor '='" -#: ../eval.c:138 -#, fuzzy, c-format -msgid "E684: list index out of range: %<PRId64>" -msgstr "E322: relnommer buite perke: %<PRId64> verby die einde" - -#: ../eval.c:139 -#, c-format -msgid "E121: Undefined variable: %s" -msgstr "E121: Ongedefinieerde veranderlike: %s" - -#: ../eval.c:140 msgid "E111: Missing ']'" msgstr "E111: Ontbrekende ']'" -#: ../eval.c:141 #, fuzzy, c-format -msgid "E686: Argument of %s must be a List" -msgstr "E487: Parameter moet positief wees" +#~ msgid "E686: Argument of %s must be a List" +#~ msgstr "E487: Parameter moet positief wees" -#: ../eval.c:143 #, fuzzy, c-format -msgid "E712: Argument of %s must be a List or Dictionary" -msgstr "E487: Parameter moet positief wees" +#~ msgid "E712: Argument of %s must be a List or Dictionary" +#~ msgstr "E487: Parameter moet positief wees" -#: ../eval.c:144 #, fuzzy -msgid "E713: Cannot use empty key for Dictionary" -msgstr "E214: Kan nie tydelike ler vind vir skryf nie" +#~ msgid "E714: List required" +#~ msgstr "E471: Parameter benodig" -#: ../eval.c:145 #, fuzzy -msgid "E714: List required" -msgstr "E471: Parameter benodig" +#~ msgid "E715: Dictionary required" +#~ msgstr "E129: Funksienaam vereis" -#: ../eval.c:146 #, fuzzy -msgid "E715: Dictionary required" -msgstr "E129: Funksienaam vereis" +#~ msgid "E928: String required" +#~ msgstr "E397: Lernaam benodig" -#: ../eval.c:147 #, c-format msgid "E118: Too many arguments for function: %s" msgstr "E118: Te veel parameters vir funksie: %s" -#: ../eval.c:148 #, c-format -msgid "E716: Key not present in Dictionary: %s" -msgstr "" +#~ msgid "E716: Key not present in Dictionary: %s" +#~ msgstr "" -#: ../eval.c:150 #, c-format msgid "E122: Function %s already exists, add ! to replace it" msgstr "E122: Funksie %s bestaan alreeds, gebruik ! om te vervang" -#: ../eval.c:151 #, fuzzy -msgid "E717: Dictionary entry already exists" -msgstr "E95: Buffer met hierdie naam bestaan alreeds" +#~ msgid "E717: Dictionary entry already exists" +#~ msgstr "E95: Buffer met hierdie naam bestaan alreeds" -#: ../eval.c:152 #, fuzzy -msgid "E718: Funcref required" -msgstr "E129: Funksienaam vereis" +#~ msgid "E718: Funcref required" +#~ msgstr "E129: Funksienaam vereis" -#: ../eval.c:153 #, fuzzy -msgid "E719: Cannot use [:] with a Dictionary" -msgstr "E360: Kan nie dop met -f opsie uitvoer nie" +#~ msgid "E719: Cannot use [:] with a Dictionary" +#~ msgstr "E360: Kan nie dop met -f opsie uitvoer nie" -#: ../eval.c:154 -#, c-format -msgid "E734: Wrong variable type for %s=" -msgstr "" - -#: ../eval.c:155 #, fuzzy, c-format -msgid "E130: Unknown function: %s" -msgstr "E117: Onbekende funksie: %s" +#~ msgid "E130: Unknown function: %s" +#~ msgstr "E117: Onbekende funksie: %s" -#: ../eval.c:156 #, c-format msgid "E461: Illegal variable name: %s" msgstr "E461: Ongeldige veranderlikenaam: %s" -#: ../eval.c:157 -msgid "E806: using Float as a String" -msgstr "" +#, fuzzy, c-format +#~ msgid "E46: Cannot change read-only variable \"%.*s\"" +#~ msgstr "E46: Kan nie lees-alleen veranderlike stel nie \"%s\"" -#: ../eval.c:1830 -msgid "E687: Less targets than List items" -msgstr "" +#. TODO(ZyX-I): move to eval/executor +#, c-format +#~ msgid "E734: Wrong variable type for %s=" +#~ msgstr "" -#: ../eval.c:1834 -msgid "E688: More targets than List items" -msgstr "" +#~ msgid "E687: Less targets than List items" +#~ msgstr "" -#: ../eval.c:1906 -msgid "Double ; in list of variables" -msgstr "" +#~ msgid "E688: More targets than List items" +#~ msgstr "" + +#~ msgid "Double ; in list of variables" +#~ msgstr "" -#: ../eval.c:2078 #, fuzzy, c-format -msgid "E738: Can't list variables for %s" -msgstr "E138: Kan nie viminfo ler %s stoor nie!" +#~ msgid "E738: Can't list variables for %s" +#~ msgstr "E138: Kan nie viminfo ler %s stoor nie!" -#: ../eval.c:2391 -msgid "E689: Can only index a List or Dictionary" -msgstr "" +#, fuzzy, c-format +#~ msgid "E121: Undefined variable: %.*s" +#~ msgstr "E121: Ongedefinieerde veranderlike: %s" -#: ../eval.c:2396 -msgid "E708: [:] must come last" -msgstr "" +#~ msgid "E689: Can only index a List or Dictionary" +#~ msgstr "" -#: ../eval.c:2439 -msgid "E709: [:] requires a List value" -msgstr "" +#~ msgid "E708: [:] must come last" +#~ msgstr "" -#: ../eval.c:2674 -msgid "E710: List value has more items than target" -msgstr "" +#, fuzzy +#~ msgid "E713: Cannot use empty key after ." +#~ msgstr "E214: Kan nie tydelike ler vind vir skryf nie" -#: ../eval.c:2678 -msgid "E711: List value has not enough items" -msgstr "" +#~ msgid "E709: [:] requires a List value" +#~ msgstr "" + +#~ msgid "E710: List value has more items than target" +#~ msgstr "" + +#~ msgid "E711: List value has not enough items" +#~ msgstr "" -#: ../eval.c:2867 #, fuzzy -msgid "E690: Missing \"in\" after :for" -msgstr "E69: Ontbrekende ] na %s%%[" +#~ msgid "E690: Missing \"in\" after :for" +#~ msgstr "E69: Ontbrekende ] na %s%%[" -#: ../eval.c:3063 #, c-format msgid "E107: Missing parentheses: %s" msgstr "E107: Ontbrekende hakies: %s" -#: ../eval.c:3263 #, c-format msgid "E108: No such variable: \"%s\"" msgstr "E108: Geen veranderlike: \"%s\"" -#: ../eval.c:3333 -msgid "E743: variable nested too deep for (un)lock" -msgstr "" +#. For historical reasons this error is not given for Lists and +#. Dictionaries. E.g. b: dictionary may be locked/unlocked. +#, fuzzy, c-format +#~ msgid "E940: Cannot lock or unlock variable %s" +#~ msgstr "E46: Kan nie lees-alleen veranderlike stel nie \"%s\"" -#: ../eval.c:3630 msgid "E109: Missing ':' after '?'" msgstr "E109: Ontbrekende ':' na '?'" -#: ../eval.c:3893 -msgid "E691: Can only compare List with List" -msgstr "" +#~ msgid "E691: Can only compare List with List" +#~ msgstr "" -#: ../eval.c:3895 #, fuzzy -msgid "E692: Invalid operation for Lists" -msgstr "E449: Ongeldige uitdrukking ontvang" +#~ msgid "E692: Invalid operation for List" +#~ msgstr "E449: Ongeldige uitdrukking ontvang" -#: ../eval.c:3915 -msgid "E735: Can only compare Dictionary with Dictionary" -msgstr "" +#~ msgid "E735: Can only compare Dictionary with Dictionary" +#~ msgstr "" -#: ../eval.c:3917 #, fuzzy -msgid "E736: Invalid operation for Dictionary" -msgstr "E116: Ongeldige parameters vir funksie %s" - -#: ../eval.c:3932 -msgid "E693: Can only compare Funcref with Funcref" -msgstr "" +#~ msgid "E736: Invalid operation for Dictionary" +#~ msgstr "E116: Ongeldige parameters vir funksie %s" -#: ../eval.c:3934 #, fuzzy -msgid "E694: Invalid operation for Funcrefs" -msgstr "E116: Ongeldige parameters vir funksie %s" +#~ msgid "E694: Invalid operation for Funcrefs" +#~ msgstr "E116: Ongeldige parameters vir funksie %s" -#: ../eval.c:4277 #, fuzzy -msgid "E804: Cannot use '%' with Float" -msgstr "E360: Kan nie dop met -f opsie uitvoer nie" +#~ msgid "E804: Cannot use '%' with Float" +#~ msgstr "E360: Kan nie dop met -f opsie uitvoer nie" -#: ../eval.c:4478 msgid "E110: Missing ')'" msgstr "E110: Ontbrekende ')'" -#: ../eval.c:4609 #, fuzzy -msgid "E695: Cannot index a Funcref" -msgstr "E90: Kan nie laaste buffer uitlaai nie" +#~ msgid "E695: Cannot index a Funcref" +#~ msgstr "E90: Kan nie laaste buffer uitlaai nie" + +#, fuzzy +#~ msgid "E909: Cannot index a special variable" +#~ msgstr "E90: Kan nie laaste buffer uitlaai nie" -#: ../eval.c:4839 #, c-format msgid "E112: Option name missing: %s" msgstr "E112: Opsienaam ontbreek: %s" -#: ../eval.c:4855 #, c-format msgid "E113: Unknown option: %s" msgstr "E113: Onbekende opsie: %s" -#: ../eval.c:4904 #, c-format msgid "E114: Missing quote: %s" msgstr "E114: Ontbrekende aanhalingsteken: %s" -#: ../eval.c:5020 #, c-format msgid "E115: Missing quote: %s" msgstr "E115: Ontbrekende aanhalingsteken: %s" -#: ../eval.c:5084 #, fuzzy, c-format -msgid "E696: Missing comma in List: %s" -msgstr "E405: Ontbrekende gelykaanteken: %s" +#~ msgid "E696: Missing comma in List: %s" +#~ msgstr "E405: Ontbrekende gelykaanteken: %s" -#: ../eval.c:5091 #, fuzzy, c-format -msgid "E697: Missing end of List ']': %s" -msgstr "E398: Ontbrekende '=': %s" +#~ msgid "E697: Missing end of List ']': %s" +#~ msgstr "E398: Ontbrekende '=': %s" + +#~ msgid "Not enough memory to set references, garbage collection aborted!" +#~ msgstr "" -#: ../eval.c:6475 #, fuzzy, c-format -msgid "E720: Missing colon in Dictionary: %s" -msgstr "E242: Ontbrekende kleur: %s" +#~ msgid "E720: Missing colon in Dictionary: %s" +#~ msgstr "E242: Ontbrekende kleur: %s" -#: ../eval.c:6499 #, c-format -msgid "E721: Duplicate key in Dictionary: \"%s\"" -msgstr "" +#~ msgid "E721: Duplicate key in Dictionary: \"%s\"" +#~ msgstr "" -#: ../eval.c:6517 #, fuzzy, c-format -msgid "E722: Missing comma in Dictionary: %s" -msgstr "E242: Ontbrekende kleur: %s" +#~ msgid "E722: Missing comma in Dictionary: %s" +#~ msgstr "E242: Ontbrekende kleur: %s" -#: ../eval.c:6524 #, fuzzy, c-format -msgid "E723: Missing end of Dictionary '}': %s" -msgstr "E126: Ontbrekende ':endfunction'" +#~ msgid "E723: Missing end of Dictionary '}': %s" +#~ msgstr "E126: Ontbrekende ':endfunction'" -#: ../eval.c:6555 -#, fuzzy -msgid "E724: variable nested too deep for displaying" -msgstr "E22: Skripte te diep ge-nes" +#, c-format +msgid "E125: Illegal argument: %s" +msgstr "E125: Ongeldige parameter: %s" -#: ../eval.c:7188 #, fuzzy, c-format -msgid "E740: Too many arguments for function %s" -msgstr "E118: Te veel parameters vir funksie: %s" +#~ msgid "E853: Duplicate argument name: %s" +#~ msgstr "E125: Ongeldige parameter: %s" + +#, fuzzy, c-format +#~ msgid "E740: Too many arguments for function %s" +#~ msgstr "E118: Te veel parameters vir funksie: %s" -#: ../eval.c:7190 #, c-format msgid "E116: Invalid arguments for function %s" msgstr "E116: Ongeldige parameters vir funksie %s" -#: ../eval.c:7377 #, c-format msgid "E117: Unknown function: %s" msgstr "E117: Onbekende funksie: %s" -#: ../eval.c:7383 +#, fuzzy, c-format +#~ msgid "E933: Function was deleted: %s" +#~ msgstr "E129: Funksienaam vereis" + #, c-format msgid "E119: Not enough arguments for function: %s" msgstr "E119: Te min parameters vir funksie: %s" -#: ../eval.c:7387 #, c-format msgid "E120: Using <SID> not in a script context: %s" msgstr "E120: <SID> word buite skripkonteks gebruik: %s" -#: ../eval.c:7391 #, c-format -msgid "E725: Calling dict function without Dictionary: %s" -msgstr "" +#~ msgid "E725: Calling dict function without Dictionary: %s" +#~ msgstr "" + +#, c-format +#~ msgid "Error converting the call result: %s" +#~ msgstr "" -#: ../eval.c:7453 #, fuzzy -msgid "E808: Number or Float required" -msgstr "E521: Nommer vereis na =" +#~ msgid "E699: Too many arguments" +#~ msgstr "Te veel redigeer-parameters" -#: ../eval.c:7503 #, fuzzy -msgid "add() argument" -msgstr "Ongeldige parameter vir" +#~ msgid "E785: complete() can only be used in Insert mode" +#~ msgstr "E328: Kieslys bestaan slegs in 'n ander modus" + +msgid "&Ok" +msgstr "&Ok" -#: ../eval.c:7907 #, fuzzy -msgid "E699: Too many arguments" -msgstr "Te veel redigeer-parameters" +#~ msgid "dictwatcheradd() argument" +#~ msgstr "Ongeldige parameter vir" + +#~ msgid "extend() argument" +#~ msgstr "" -#: ../eval.c:8073 #, fuzzy -msgid "E785: complete() can only be used in Insert mode" -msgstr "E328: Kieslys bestaan slegs in 'n ander modus" +#~ msgid "map() argument" +#~ msgstr " vim [parameters] " -#: ../eval.c:8156 -msgid "&Ok" -msgstr "&Ok" +#~ msgid "filter() argument" +#~ msgstr "" -#: ../eval.c:8676 #, fuzzy, c-format -msgid "E737: Key already exists: %s" -msgstr "E227: binding bestaan alreeds vir %s" +#~ msgid "+-%s%3ld line: " +#~ msgid_plural "+-%s%3ld lines: " +#~ msgstr[0] "+-%s%3ld rels: " +#~ msgstr[1] "+-%s%3ld rels: " -#: ../eval.c:8692 -msgid "extend() argument" -msgstr "" +#, fuzzy, c-format +#~ msgid "E700: Unknown function: %s" +#~ msgstr "E117: Onbekende funksie: %s" + +#~ msgid "E922: expected a dict" +#~ msgstr "" -#: ../eval.c:8915 #, fuzzy -msgid "map() argument" -msgstr " vim [parameters] " +#~ msgid "E923: Second argument of function() must be a list or a dict" +#~ msgstr "E487: Parameter moet positief wees" -#: ../eval.c:8916 -msgid "filter() argument" -msgstr "" +#, fuzzy +#~ msgid "E5000: Cannot find tab number." +#~ msgstr "E90: Kan nie laaste buffer uitlaai nie" -#: ../eval.c:9229 -#, c-format -msgid "+-%s%3ld lines: " -msgstr "+-%s%3ld rels: " +#~ msgid "E5001: Higher scope cannot be -1 if lower scope is >= 0." +#~ msgstr "" -#: ../eval.c:9291 -#, fuzzy, c-format -msgid "E700: Unknown function: %s" -msgstr "E117: Onbekende funksie: %s" +#, fuzzy +#~ msgid "E5002: Cannot find window number." +#~ msgstr "E671: Kan nie venster titel vind nie \"%s\"" + +#~ msgid "E5050: {opts} must be the only argument" +#~ msgstr "" -#: ../eval.c:10729 msgid "called inputrestore() more often than inputsave()" msgstr "inputrestore() is meer gereeld as inputsave() geroep" -#: ../eval.c:10771 #, fuzzy -msgid "insert() argument" -msgstr "Te veel redigeer-parameters" +#~ msgid "insert() argument" +#~ msgstr "Te veel redigeer-parameters" -#: ../eval.c:10841 #, fuzzy -msgid "E786: Range not allowed" -msgstr "E481: Geen omvang toegelaat nie" +#~ msgid "E786: Range not allowed" +#~ msgstr "E481: Geen omvang toegelaat nie" + +#~ msgid "Invalid stream on rpc job, use jobclose(id, 'rpc')" +#~ msgstr "" + +#~ msgid "Invalid job stream: Not an rpc job" +#~ msgstr "" + +#, c-format +#~ msgid "Invalid job stream \"%s\"" +#~ msgstr "" + +#~ msgid "Can't send data to the job: stdin is closed" +#~ msgstr "" + +#~ msgid "Can't send raw data to rpc channel" +#~ msgstr "" + +#~ msgid "E474: Failed to convert list to string" +#~ msgstr "" + +#, fuzzy, c-format +#~ msgid "E474: Failed to parse %.*s" +#~ msgstr "E241: Kan nie na %s stuur nie" -#: ../eval.c:11140 #, fuzzy -msgid "E701: Invalid type for len()" -msgstr "E596: Ongeldige font(e)" +#~ msgid "E701: Invalid type for len()" +#~ msgstr "E596: Ongeldige font(e)" -#: ../eval.c:11980 -msgid "E726: Stride is zero" -msgstr "" +#, c-format +#~ msgid "E798: ID is reserved for \":match\": %<PRId64>" +#~ msgstr "" -#: ../eval.c:11982 -msgid "E727: Start past end" -msgstr "" +#, c-format +#~ msgid "E798: ID is reserved for \"match\": %<PRId64>" +#~ msgstr "" -#: ../eval.c:12024 ../eval.c:15297 -msgid "<empty>" -msgstr "" +#, fuzzy, c-format +#~ msgid "msgpackdump() argument, index %i" +#~ msgstr " vim [parameters] " -#: ../eval.c:12282 -msgid "remove() argument" -msgstr "" +#~ msgid "E5070: Character number must not be less than zero" +#~ msgstr "" + +#, c-format +#~ msgid "E5071: Character number must not be greater than INT_MAX (%i)" +#~ msgstr "" + +#~ msgid "E726: Stride is zero" +#~ msgstr "" + +#~ msgid "E727: Start past end" +#~ msgstr "" + +#~ msgid "<empty>" +#~ msgstr "" + +#~ msgid "remove() argument" +#~ msgstr "" -#: ../eval.c:12466 msgid "E655: Too many symbolic links (cycle?)" msgstr "E655: Te veel simboliese skakels (siklus?)" -#: ../eval.c:12593 -msgid "reverse() argument" -msgstr "" +#~ msgid "reverse() argument" +#~ msgstr "" -#: ../eval.c:13721 -msgid "sort() argument" -msgstr "" +#, fuzzy, c-format +#~ msgid "E927: Invalid action: '%s'" +#~ msgstr "E383: Ongeldige soekstring: %s" + +#, fuzzy, c-format +#~ msgid "connection failed: %s" +#~ msgstr "XSMP 'SmcOpenConnection' het gefaal: %s" + +#~ msgid "sort() argument" +#~ msgstr "" -#: ../eval.c:13721 #, fuzzy -msgid "uniq() argument" -msgstr "Ongeldige parameter vir" +#~ msgid "uniq() argument" +#~ msgstr "Ongeldige parameter vir" -#: ../eval.c:13776 #, fuzzy -msgid "E702: Sort compare function failed" -msgstr "E237: Drukker-seleksie het gefaal" +#~ msgid "E702: Sort compare function failed" +#~ msgstr "E237: Drukker-seleksie het gefaal" -#: ../eval.c:13806 -msgid "E882: Uniq compare function failed" -msgstr "" +#~ msgid "E882: Uniq compare function failed" +#~ msgstr "" -#: ../eval.c:14085 msgid "(Invalid)" msgstr "(Ongeldig)" -#: ../eval.c:14590 -#, fuzzy -msgid "E677: Error writing temp file" -msgstr "E208: Kan nie skryf na \"%s\"" - -#: ../eval.c:16159 -msgid "E805: Using a Float as a Number" -msgstr "" +#, fuzzy, c-format +#~ msgid "E935: invalid submatch number: %d" +#~ msgstr "E354: Ongeldige registernaam: '%s'" -#: ../eval.c:16162 -msgid "E703: Using a Funcref as a Number" -msgstr "" +#~ msgid "Can only call this function in an unmodified buffer" +#~ msgstr "" -#: ../eval.c:16170 -msgid "E745: Using a List as a Number" -msgstr "" +#, fuzzy +#~ msgid "E921: Invalid callback argument" +#~ msgstr "E474: Ongeldige parameter" -#: ../eval.c:16173 -msgid "E728: Using a Dictionary as a Number" -msgstr "" +#, fuzzy, c-format +#~ msgid "E80: Error while writing: %s" +#~ msgstr "E80: Fout tydens skryfoperasie" -#: ../eval.c:16259 -msgid "E729: using Funcref as a String" -msgstr "" +#. Using %s, p and not %c, *p to preserve multibyte characters +#, fuzzy, c-format +#~ msgid "E5060: Unknown flag: %s" +#~ msgstr "E235: Onbekende font: %s" -#: ../eval.c:16262 #, fuzzy -msgid "E730: using List as a String" -msgstr "E374: Ontbrekende ] in formaatstring" +#~ msgid "E482: Can't open file with an empty name" +#~ msgstr "E212: Kan ler nie oopmaak vir skryf nie" -#: ../eval.c:16265 -msgid "E731: using Dictionary as a String" -msgstr "" +#, fuzzy, c-format +#~ msgid "E482: Can't open file %s for writing: %s" +#~ msgstr "E212: Kan ler nie oopmaak vir skryf nie" -#: ../eval.c:16619 #, fuzzy, c-format -msgid "E706: Variable type mismatch for: %s" -msgstr "E93: Meer as een treffer vir %s" +#~ msgid "E80: Error when closing file %s: %s" +#~ msgstr "E209: Kan \"%s\" nie sluit nie" -#: ../eval.c:16705 #, fuzzy, c-format -msgid "E795: Cannot delete variable %s" -msgstr "E46: Kan nie lees-alleen veranderlike stel nie \"%s\"" +#~ msgid "E794: Cannot set variable in the sandbox: \"%.*s\"" +#~ msgstr "E46: Kan nie lees-alleen veranderlike stel nie \"%s\"" -#: ../eval.c:16724 #, fuzzy, c-format -msgid "E704: Funcref variable name must start with a capital: %s" -msgstr "E128: Funksienaam moet met 'n hoofletter begin: %s" +#~ msgid "E795: Cannot delete variable %.*s" +#~ msgstr "E46: Kan nie lees-alleen veranderlike stel nie \"%s\"" -#: ../eval.c:16732 -#, c-format -msgid "E705: Variable name conflicts with existing function: %s" -msgstr "" +#, fuzzy, c-format +#~ msgid "E704: Funcref variable name must start with a capital: %s" +#~ msgstr "E128: Funksienaam moet met 'n hoofletter begin: %s" -#: ../eval.c:16763 #, c-format -msgid "E741: Value is locked: %s" -msgstr "" - -#: ../eval.c:16764 ../eval.c:16769 ../message.c:1839 -msgid "Unknown" -msgstr "Onbekend" - -#: ../eval.c:16768 -#, fuzzy, c-format -msgid "E742: Cannot change value of %s" -msgstr "E284: Kan nie IC waardes stel nie" +#~ msgid "E705: Variable name conflicts with existing function: %s" +#~ msgstr "" -#: ../eval.c:16838 -msgid "E698: variable nested too deep for making a copy" -msgstr "" +#~ msgid "E698: variable nested too deep for making a copy" +#~ msgstr "" -#: ../eval.c:17249 #, c-format msgid "E123: Undefined function: %s" msgstr "E123: Ongedefinieerde funksie: %s" -#: ../eval.c:17260 #, c-format msgid "E124: Missing '(': %s" msgstr "E124: Ontbrekende '(': %s" -#: ../eval.c:17293 #, fuzzy -msgid "E862: Cannot use g: here" -msgstr "E284: Kan nie IC waardes stel nie" +#~ msgid "E862: Cannot use g: here" +#~ msgstr "E284: Kan nie IC waardes stel nie" -#: ../eval.c:17312 #, c-format -msgid "E125: Illegal argument: %s" -msgstr "E125: Ongeldige parameter: %s" - -#: ../eval.c:17323 -#, fuzzy, c-format -msgid "E853: Duplicate argument name: %s" -msgstr "E125: Ongeldige parameter: %s" +#~ msgid "E932: Closure function should not be at top level: %s" +#~ msgstr "" -#: ../eval.c:17416 msgid "E126: Missing :endfunction" msgstr "E126: Ontbrekende ':endfunction'" -#: ../eval.c:17537 #, fuzzy, c-format -msgid "E707: Function name conflicts with variable: %s" -msgstr "E128: Funksienaam moet met 'n hoofletter begin: %s" +#~ msgid "E707: Function name conflicts with variable: %s" +#~ msgstr "E128: Funksienaam moet met 'n hoofletter begin: %s" -#: ../eval.c:17549 #, c-format msgid "E127: Cannot redefine function %s: It is in use" msgstr "E127: Kan funksie %s nie herdefinieer nie: Dit is in gebruik" -#: ../eval.c:17604 #, fuzzy, c-format -msgid "E746: Function name does not match script file name: %s" -msgstr "E128: Funksienaam moet met 'n hoofletter begin: %s" +#~ msgid "E746: Function name does not match script file name: %s" +#~ msgstr "E128: Funksienaam moet met 'n hoofletter begin: %s" -#: ../eval.c:17716 msgid "E129: Function name required" msgstr "E129: Funksienaam vereis" -#: ../eval.c:17824 #, fuzzy, c-format -msgid "E128: Function name must start with a capital or \"s:\": %s" -msgstr "E128: Funksienaam moet met 'n hoofletter begin: %s" +#~ msgid "E128: Function name must start with a capital or \"s:\": %s" +#~ msgstr "E128: Funksienaam moet met 'n hoofletter begin: %s" -#: ../eval.c:17833 #, fuzzy, c-format -msgid "E884: Function name cannot contain a colon: %s" -msgstr "E128: Funksienaam moet met 'n hoofletter begin: %s" +#~ msgid "E884: Function name cannot contain a colon: %s" +#~ msgstr "E128: Funksienaam moet met 'n hoofletter begin: %s" -#: ../eval.c:18336 #, c-format msgid "E131: Cannot delete function %s: It is in use" msgstr "E131: Kan funksie %s nie verwyder nie: Dit is in gebruik" -#: ../eval.c:18441 +#, fuzzy, c-format +#~ msgid "Cannot delete function %s: It is being used internally" +#~ msgstr "E131: Kan funksie %s nie verwyder nie: Dit is in gebruik" + msgid "E132: Function call depth is higher than 'maxfuncdepth'" msgstr "E132: Funksieroepdiepte is groter as 'maxfuncdepth'" -#: ../eval.c:18568 #, c-format msgid "calling %s" msgstr "roep %s" -#: ../eval.c:18651 #, c-format msgid "%s aborted" msgstr "%s gekanselleer" -#: ../eval.c:18653 #, c-format msgid "%s returning #%<PRId64>" msgstr "%s lewer #%<PRId64> op" -#: ../eval.c:18670 #, fuzzy, c-format -msgid "%s returning %s" -msgstr "%s lewer \"%s\" op" +#~ msgid "%s returning %s" +#~ msgstr "%s lewer \"%s\" op" -#: ../eval.c:18691 ../ex_cmds2.c:2695 #, c-format msgid "continuing in %s" msgstr "vervolg in %s" -#: ../eval.c:18795 msgid "E133: :return not inside a function" msgstr "E133: ':return' buite funksie" -#: ../eval.c:19159 -msgid "" -"\n" -"# global variables:\n" -msgstr "" -"\n" -"# globale veranderlikes:\n" - -#: ../eval.c:19254 msgid "" "\n" "\tLast set from " @@ -1071,154 +910,418 @@ msgstr "" "\n" "\tLaas gestel vanaf " -#: ../eval.c:19272 -#, fuzzy -msgid "No old files" -msgstr "Geen ingeslote lers nie" +#~ msgid "E5009: $VIMRUNTIME is empty or unset" +#~ msgstr "" -#: ../ex_cmds.c:122 #, c-format -msgid "<%s>%s%s %d, Hex %02x, Octal %03o" -msgstr "<%s>%s%s %d, Hex %02x, Oktaal %03o" +#~ msgid "E5009: Invalid $VIMRUNTIME: %s" +#~ msgstr "" -#: ../ex_cmds.c:145 #, c-format -msgid "> %d, Hex %04x, Octal %o" -msgstr "> %d, Hex %04x, Oktaal %o" +#~ msgid "E474: Expected comma before list item: %s" +#~ msgstr "" -#: ../ex_cmds.c:146 #, c-format -msgid "> %d, Hex %08x, Octal %o" -msgstr "> %d, Hex %08x, Oktaal %o" +#~ msgid "E474: Expected colon before dictionary value: %s" +#~ msgstr "" -#: ../ex_cmds.c:684 -msgid "E134: Move lines into themselves" -msgstr "E134: Skuif rels in hulself in" +#, fuzzy, c-format +#~ msgid "E474: Expected string key: %s" +#~ msgstr "E415: onverwagte gelykaanteken: %s" -#: ../ex_cmds.c:747 -msgid "1 line moved" -msgstr "1 rel geskuif" +#, fuzzy, c-format +#~ msgid "E474: Expected comma before dictionary key: %s" +#~ msgstr "E242: Ontbrekende kleur: %s" + +#, fuzzy, c-format +#~ msgid "E474: Unfinished escape sequence: %.*s" +#~ msgstr "E540: Onvoltooide uitdrukkingreeks" -#: ../ex_cmds.c:749 #, c-format -msgid "%<PRId64> lines moved" -msgstr "%<PRId64> rels geskuif" +#~ msgid "E474: Unfinished unicode escape sequence: %.*s" +#~ msgstr "" -#: ../ex_cmds.c:1175 #, c-format -msgid "%<PRId64> lines filtered" -msgstr "%<PRId64> rels filtreer" +#~ msgid "E474: Expected four hex digits after \\u: %.*s" +#~ msgstr "" -#: ../ex_cmds.c:1194 -msgid "E135: *Filter* Autocommands must not change current buffer" -msgstr "E135: *Filter* Outobevele mag nie die huidige buffer verander nie" +#, fuzzy, c-format +#~ msgid "E474: Unknown escape sequence: %.*s" +#~ msgstr "E409: Onbekende groepnaam: %s" -#: ../ex_cmds.c:1244 -msgid "[No write since last change]\n" -msgstr "[Ongestoor sedert vorige verandering]\n" +#, c-format +#~ msgid "E474: ASCII control characters cannot be present inside string: %.*s" +#~ msgstr "" -#: ../ex_cmds.c:1424 #, c-format -msgid "%sviminfo: %s in line: " -msgstr "%sviminfo: %s in rel: " +#~ msgid "E474: Only UTF-8 strings allowed: %.*s" +#~ msgstr "" -#: ../ex_cmds.c:1431 -msgid "E136: viminfo: Too many errors, skipping rest of file" -msgstr "E136: viminfo: Te veel foute, slaan die res van die ler oor" +#, c-format +#~ msgid "" +#~ "E474: Only UTF-8 code points up to U+10FFFF are allowed to appear unescaped: " +#~ "%.*s" +#~ msgstr "" + +#, fuzzy, c-format +#~ msgid "E474: Expected string end: %.*s" +#~ msgstr "E415: onverwagte gelykaanteken: %s" + +#, fuzzy, c-format +#~ msgid "E474: Leading zeroes are not allowed: %.*s" +#~ msgstr "E481: Geen omvang toegelaat nie" + +#, fuzzy, c-format +#~ msgid "E474: Missing number after minus sign: %.*s" +#~ msgstr "E526: Ontbrekende nommer na <%s>" + +#, fuzzy, c-format +#~ msgid "E474: Missing number after decimal dot: %.*s" +#~ msgstr "E526: Ontbrekende nommer na <%s>" + +#, fuzzy, c-format +#~ msgid "E474: Missing exponent: %.*s" +#~ msgstr "E114: Ontbrekende aanhalingsteken: %s" -#: ../ex_cmds.c:1458 #, c-format -msgid "Reading viminfo file \"%s\"%s%s%s" -msgstr "Besig om viminfo ler \"%s\"%s%s%s te lees" +#~ msgid "" +#~ "E685: internal error: while converting number \"%.*s\" to float string2float " +#~ "consumed %zu bytes in place of %zu" +#~ msgstr "" -#: ../ex_cmds.c:1460 -msgid " info" -msgstr " inligting" +#, c-format +#~ msgid "" +#~ "E685: internal error: while converting number \"%.*s\" to integer vim_str2nr " +#~ "consumed %i bytes in place of %zu" +#~ msgstr "" -#: ../ex_cmds.c:1461 -msgid " marks" -msgstr " merkers" +#~ msgid "E474: Attempt to decode a blank string" +#~ msgstr "" + +#, c-format +#~ msgid "E474: No container to close: %.*s" +#~ msgstr "" + +#, c-format +#~ msgid "E474: Closing list with curly bracket: %.*s" +#~ msgstr "" + +#, c-format +#~ msgid "E474: Closing dictionary with square bracket: %.*s" +#~ msgstr "" + +#, fuzzy, c-format +#~ msgid "E474: Trailing comma: %.*s" +#~ msgstr "E488: Oorbodige karakters" + +#, c-format +#~ msgid "E474: Expected value after colon: %.*s" +#~ msgstr "" + +#, fuzzy, c-format +#~ msgid "E474: Expected value: %.*s" +#~ msgstr "E415: onverwagte gelykaanteken: %s" + +#, fuzzy, c-format +#~ msgid "E474: Comma not inside container: %.*s" +#~ msgstr "E242: Kleurnaam is onbekend: %s" + +#, fuzzy, c-format +#~ msgid "E474: Duplicate comma: %.*s" +#~ msgstr "E125: Ongeldige parameter: %s" + +#, fuzzy, c-format +#~ msgid "E474: Comma after colon: %.*s" +#~ msgstr "E254: Kan nie kleur %s toeken nie" + +#, fuzzy, c-format +#~ msgid "E474: Using comma in place of colon: %.*s" +#~ msgstr "E242: Ontbrekende kleur: %s" + +#, c-format +#~ msgid "E474: Leading comma: %.*s" +#~ msgstr "" + +#, fuzzy, c-format +#~ msgid "E474: Colon not inside container: %.*s" +#~ msgstr "E242: Kleurnaam is onbekend: %s" + +#, fuzzy, c-format +#~ msgid "E474: Using colon not in dictionary: %.*s" +#~ msgstr "E242: Ontbrekende kleur: %s" + +#, fuzzy, c-format +#~ msgid "E474: Unexpected colon: %.*s" +#~ msgstr "E415: onverwagte gelykaanteken: %s" + +#, c-format +#~ msgid "E474: Colon after comma: %.*s" +#~ msgstr "" + +#, fuzzy, c-format +#~ msgid "E474: Duplicate colon: %.*s" +#~ msgstr "gelaaide fontnaam: %s" + +#, fuzzy, c-format +#~ msgid "E474: Expected null: %.*s" +#~ msgstr "E415: onverwagte gelykaanteken: %s" + +#, fuzzy, c-format +#~ msgid "E474: Expected true: %.*s" +#~ msgstr "E415: onverwagte gelykaanteken: %s" + +#, fuzzy, c-format +#~ msgid "E474: Expected false: %.*s" +#~ msgstr "E415: onverwagte gelykaanteken: %s" + +#, fuzzy, c-format +#~ msgid "E474: Unidentified byte: %.*s" +#~ msgstr "E121: Ongedefinieerde veranderlike: %s" + +#, fuzzy, c-format +#~ msgid "E474: Trailing characters: %.*s" +#~ msgstr "E488: Oorbodige karakters" + +#, fuzzy, c-format +#~ msgid "E474: Unexpected end of input: %.*s" +#~ msgstr "E415: onverwagte gelykaanteken: %s" + +#, c-format +#~ msgid "key %s" +#~ msgstr "" + +#, c-format +#~ msgid "key %s at index %i from special map" +#~ msgstr "" + +#, c-format +#~ msgid "index %i" +#~ msgstr "" + +#~ msgid "partial" +#~ msgstr "" + +#, fuzzy, c-format +#~ msgid "argument %i" +#~ msgstr " vim [parameters] " + +#~ msgid "partial self dictionary" +#~ msgstr "" + +#~ msgid "itself" +#~ msgstr "" + +#. Only give this message once for a recursive call to avoid +#. flooding the user with errors. +#~ msgid "E724: unable to correctly dump variable with self-referencing container" +#~ msgstr "" + +#~ msgid "E474: Unable to represent NaN value in JSON" +#~ msgstr "" + +#~ msgid "E474: Unable to represent infinity in JSON" +#~ msgstr "" + +#, c-format +#~ msgid "" +#~ "E474: String \"%.*s\" contains byte that does not start any UTF-8 character" +#~ msgstr "" + +#, c-format +#~ msgid "" +#~ "E474: UTF-8 string contains code point which belongs to a surrogate pair: " +#~ "%.*s" +#~ msgstr "" -#: ../ex_cmds.c:1462 #, fuzzy -msgid " oldfiles" -msgstr "Geen ingeslote lers nie" +#~ msgid "E474: Unable to convert EXT string to JSON" +#~ msgstr "E620: Kon nie van wye-greep na \"%s\" enkodering verander nie" -#: ../ex_cmds.c:1463 -msgid " FAILED" -msgstr " GEFAAL" +#, c-format +#~ msgid "E474: Error while dumping %s, %s: attempt to dump function reference" +#~ msgstr "" + +#, fuzzy +#~ msgid "E474: Invalid key in special dictionary" +#~ msgstr "E116: Ongeldige parameters vir funksie %s" + +#, fuzzy +#~ msgid "encode_tv2string() argument" +#~ msgstr "Te veel redigeer-parameters" + +#, fuzzy +#~ msgid ":echo argument" +#~ msgstr " vim [parameters] " + +#, fuzzy +#~ msgid "encode_tv2json() argument" +#~ msgstr "Te veel redigeer-parameters" -#. avoid a wait_return for this message, it's annoying -#: ../ex_cmds.c:1541 #, c-format -msgid "E137: Viminfo file is not writable: %s" -msgstr "E137: Viminfo ler is nie skryfbaar nie: %s" +#~ msgid "E5004: Error while dumping %s, %s: attempt to dump function reference" +#~ msgstr "" -#: ../ex_cmds.c:1626 #, c-format -msgid "E138: Can't write viminfo file %s!" -msgstr "E138: Kan nie viminfo ler %s stoor nie!" +#~ msgid "E5005: Unable to dump %s: container references itself in %s" +#~ msgstr "" + +#, fuzzy, c-format +#~ msgid "E684: list index out of range: %<PRId64>" +#~ msgstr "E322: relnommer buite perke: %<PRId64> verby die einde" + +#~ msgid "E6000: Argument is not a function or function name" +#~ msgstr "" + +#, fuzzy, c-format +#~ msgid "E737: Key already exists: %s" +#~ msgstr "E227: binding bestaan alreeds vir %s" + +#~ msgid "E743: variable nested too deep for (un)lock" +#~ msgstr "" -#: ../ex_cmds.c:1635 #, c-format -msgid "Writing viminfo file \"%s\"" -msgstr "Besig om viminfo ler \"%s\" te stoor" +#~ msgid "E741: Value is locked: %.*s" +#~ msgstr "" + +#, fuzzy, c-format +#~ msgid "E742: Cannot change value of %.*s" +#~ msgstr "E284: Kan nie IC waardes stel nie" + +msgid "Unknown" +msgstr "Onbekend" + +#~ msgid "E805: Expected a Number or a String, Float found" +#~ msgstr "" + +#~ msgid "E703: Expected a Number or a String, Funcref found" +#~ msgstr "" + +#~ msgid "E745: Expected a Number or a String, List found" +#~ msgstr "" + +#~ msgid "E728: Expected a Number or a String, Dictionary found" +#~ msgstr "" + +#, fuzzy +#~ msgid "E5300: Expected a Number or a String" +#~ msgstr "E373: Onverwagte %%%c in formaatstring" + +#~ msgid "E745: Using a List as a Number" +#~ msgstr "" + +#~ msgid "E728: Using a Dictionary as a Number" +#~ msgstr "" + +#~ msgid "E805: Using a Float as a Number" +#~ msgstr "" + +#, fuzzy +#~ msgid "E685: using an invalid value as a Number" +#~ msgstr "E19: Merker het ongeldige relnommer" + +#, fuzzy +#~ msgid "E730: using List as a String" +#~ msgstr "E374: Ontbrekende ] in formaatstring" + +#~ msgid "E731: using Dictionary as a String" +#~ msgstr "" + +#, fuzzy +#~ msgid "E908: using an invalid value as a String" +#~ msgstr "E374: Ontbrekende ] in formaatstring" + +#~ msgid "E891: Using a Funcref as a Float" +#~ msgstr "" + +#~ msgid "E892: Using a String as a Float" +#~ msgstr "" + +#, fuzzy +#~ msgid "E893: Using a List as a Float" +#~ msgstr "E374: Ontbrekende ] in formaatstring" + +#, fuzzy +#~ msgid "E894: Using a Dictionary as a Float" +#~ msgstr "E242: Ontbrekende kleur: %s" + +#~ msgid "E907: Using a special value as a Float" +#~ msgstr "" + +#, fuzzy +#~ msgid "E808: Number or Float required" +#~ msgstr "E521: Nommer vereis na =" + +#~ msgid "tcp address must be host:port" +#~ msgstr "" + +#~ msgid "failed to lookup host or port" +#~ msgstr "" + +#, fuzzy +#~ msgid "connection refused" +#~ msgstr "'cscope' verbinding gesluit" -#. Write the info: -#: ../ex_cmds.c:1720 #, c-format -msgid "# This viminfo file was generated by Vim %s.\n" -msgstr "# Hierdie viminfo ler is gegenereer deur Vim %s.\n" +msgid "<%s>%s%s %d, Hex %02x, Octal %03o" +msgstr "<%s>%s%s %d, Hex %02x, Oktaal %03o" -#: ../ex_cmds.c:1722 -msgid "" -"# You may edit it if you're careful!\n" -"\n" -msgstr "" -"# Jy mag dit wysig as jy versigtig is!\n" -"\n" +#, c-format +msgid "> %d, Hex %04x, Octal %o" +msgstr "> %d, Hex %04x, Oktaal %o" + +#, c-format +msgid "> %d, Hex %08x, Octal %o" +msgstr "> %d, Hex %08x, Oktaal %o" -#: ../ex_cmds.c:1723 -msgid "# Value of 'encoding' when this file was written\n" -msgstr "# Waarde van 'encoding' toe hierdie ler gestoor is\n" +msgid "E134: Move lines into themselves" +msgstr "E134: Skuif rels in hulself in" -#: ../ex_cmds.c:1800 -msgid "Illegal starting char" -msgstr "Ongeldige beginkarakter" +msgid "1 line moved" +msgstr "1 rel geskuif" + +#, c-format +msgid "%<PRId64> lines moved" +msgstr "%<PRId64> rels geskuif" + +#, c-format +msgid "E482: Can't create file %s" +msgstr "E482: Kan nie ler %s skep nie" + +#, c-format +msgid "%<PRId64> lines filtered" +msgstr "%<PRId64> rels filtreer" + +msgid "E135: *Filter* Autocommands must not change current buffer" +msgstr "E135: *Filter* Outobevele mag nie die huidige buffer verander nie" + +msgid "[No write since last change]\n" +msgstr "[Ongestoor sedert vorige verandering]\n" -#: ../ex_cmds.c:2162 msgid "Write partial file?" msgstr "Skryf gedeeltelike ler?" -#: ../ex_cmds.c:2166 msgid "E140: Use ! to write partial buffer" msgstr "E140: Gebruik ! om gedeeltelike buffer te skryf" -#: ../ex_cmds.c:2281 #, fuzzy, c-format -msgid "Overwrite existing file \"%s\"?" -msgstr "Oorskryf bestaande ler \"%.*s\"?" +#~ msgid "Overwrite existing file \"%s\"?" +#~ msgstr "Oorskryf bestaande ler \"%.*s\"?" -#: ../ex_cmds.c:2317 #, c-format -msgid "Swap file \"%s\" exists, overwrite anyway?" -msgstr "" +#~ msgid "Swap file \"%s\" exists, overwrite anyway?" +#~ msgstr "" -#: ../ex_cmds.c:2326 #, fuzzy, c-format -msgid "E768: Swap file exists: %s (:silent! overrides)" -msgstr "E13: Ler bestaan (gebruik ! om te dwing)" +#~ msgid "E768: Swap file exists: %s (:silent! overrides)" +#~ msgstr "E13: Ler bestaan (gebruik ! om te dwing)" -#: ../ex_cmds.c:2381 #, c-format msgid "E141: No file name for buffer %<PRId64>" msgstr "E141: Geen lernaam vir buffer %<PRId64> nie" -#: ../ex_cmds.c:2412 msgid "E142: File not written: Writing is disabled by 'write' option" msgstr "E142: Ler nie gestoor nie: Stoor is afgeskakel deur die 'write' opsie" -#: ../ex_cmds.c:2434 #, fuzzy, c-format msgid "" "'readonly' option is set for \"%s\".\n" @@ -1227,823 +1330,667 @@ msgstr "" "'readonly' opsie is aan vir \"%.*s\".\n" "Wil jy dit forseer?" -#: ../ex_cmds.c:2439 #, c-format -msgid "" -"File permissions of \"%s\" are read-only.\n" -"It may still be possible to write it.\n" -"Do you wish to try?" -msgstr "" +#~ msgid "" +#~ "File permissions of \"%s\" are read-only.\n" +#~ "It may still be possible to write it.\n" +#~ "Do you wish to try?" +#~ msgstr "" -#: ../ex_cmds.c:2451 #, fuzzy, c-format -msgid "E505: \"%s\" is read-only (add ! to override)" -msgstr "is lees-alleen (gebruik ! om te dwing)" +#~ msgid "E505: \"%s\" is read-only (add ! to override)" +#~ msgstr "is lees-alleen (gebruik ! om te dwing)" -#: ../ex_cmds.c:3120 #, c-format msgid "E143: Autocommands unexpectedly deleted new buffer %s" msgstr "E143: Outobevele het nuwe buffer %s onverwags geskrap" -#: ../ex_cmds.c:3313 msgid "E144: non-numeric argument to :z" msgstr "E144: nie-numeriese parameter vir :z" -#: ../ex_cmds.c:3404 -msgid "E145: Shell commands not allowed in rvim" -msgstr "E145: Dop bevele nie toegelaat in rvim" +#, fuzzy +#~ msgid "E145: Shell commands not allowed in restricted mode" +#~ msgstr "E145: Dop bevele nie toegelaat in rvim" -#: ../ex_cmds.c:3498 msgid "E146: Regular expressions can't be delimited by letters" msgstr "E146: Patrone kan nie deur letters afgebaken word nie" -#: ../ex_cmds.c:3964 #, c-format msgid "replace with %s (y/n/a/q/l/^E/^Y)?" msgstr "vervang met %s (y/n/a/q/l/^E/^Y)?" -#: ../ex_cmds.c:4379 msgid "(Interrupted) " msgstr "(Onderbreek) " -#: ../ex_cmds.c:4384 #, fuzzy -msgid "1 match" -msgstr "; treffer " +#~ msgid "1 match" +#~ msgstr "; treffer " -#: ../ex_cmds.c:4384 msgid "1 substitution" msgstr "1 vervanging" -#: ../ex_cmds.c:4387 #, fuzzy, c-format -msgid "%<PRId64> matches" -msgstr "%<PRId64> veranderinge" +#~ msgid "%<PRId64> matches" +#~ msgstr "%<PRId64> veranderinge" -#: ../ex_cmds.c:4388 #, c-format msgid "%<PRId64> substitutions" msgstr "%<PRId64> vervangings" -#: ../ex_cmds.c:4392 msgid " on 1 line" msgstr " op 1 rel" -#: ../ex_cmds.c:4395 #, c-format msgid " on %<PRId64> lines" msgstr " op %<PRId64> rels" -#: ../ex_cmds.c:4438 msgid "E147: Cannot do :global recursive" msgstr "E147: Kan nie :global rekursief doen nie " -#: ../ex_cmds.c:4467 msgid "E148: Regular expression missing from global" msgstr "E148: Patroon ontbreek uit globaal" -#: ../ex_cmds.c:4508 #, c-format msgid "Pattern found in every line: %s" msgstr "Patroon gevind in elke rel: %s" -#: ../ex_cmds.c:4510 #, fuzzy, c-format -msgid "Pattern not found: %s" -msgstr "Patroon nie gevind nie" +#~ msgid "Pattern not found: %s" +#~ msgstr "Patroon nie gevind nie" -#: ../ex_cmds.c:4587 -msgid "" -"\n" -"# Last Substitute String:\n" -"$" -msgstr "" -"\n" -"# Vorige Vervangstring:\n" -"$" - -#: ../ex_cmds.c:4679 msgid "E478: Don't panic!" msgstr "E478: Bly kalm!" -#: ../ex_cmds.c:4717 #, c-format msgid "E661: Sorry, no '%s' help for %s" msgstr "E661: Jammer, geen '%s' hulp vir %s nie" -#: ../ex_cmds.c:4719 #, c-format msgid "E149: Sorry, no help for %s" msgstr "E149: Jammer, geen hulp vir %s nie" -#: ../ex_cmds.c:4751 #, c-format msgid "Sorry, help file \"%s\" not found" msgstr "Jammer, hulpler \"%s\" kan nie gevind word nie" -#: ../ex_cmds.c:5323 -#, c-format -msgid "E150: Not a directory: %s" -msgstr "E150: Nie 'n gids nie: %s" +#, fuzzy, c-format +#~ msgid "E151: No match: %s" +#~ msgstr "E480: Geen treffer: %s" -#: ../ex_cmds.c:5446 #, c-format msgid "E152: Cannot open %s for writing" msgstr "E152: Kan nie %s oopmaak om te skryf nie" -#: ../ex_cmds.c:5471 #, c-format msgid "E153: Unable to open %s for reading" msgstr "E153: Kan nie %s oop maak om te lees nie" -#: ../ex_cmds.c:5500 #, c-format msgid "E670: Mix of help file encodings within a language: %s" msgstr "E670: 'n Mengsel van hulpler enkoderings in 'n taal: %s" -#: ../ex_cmds.c:5565 #, c-format msgid "E154: Duplicate tag \"%s\" in file %s/%s" msgstr "E154: Duplikaat etiket \"%s\" in ler %s/%s" -#: ../ex_cmds.c:5687 +#, c-format +msgid "E150: Not a directory: %s" +msgstr "E150: Nie 'n gids nie: %s" + #, c-format msgid "E160: Unknown sign command: %s" msgstr "E160: Onbekende funksie: %s" -#: ../ex_cmds.c:5704 msgid "E156: Missing sign name" msgstr "E156: Ontbrekende tekennaam" -#: ../ex_cmds.c:5746 msgid "E612: Too many signs defined" msgstr "E612: Te veel tekens gedefinieer" -#: ../ex_cmds.c:5813 #, c-format msgid "E239: Invalid sign text: %s" msgstr "E239: Ongeldige tekenteks: %s" -#: ../ex_cmds.c:5844 ../ex_cmds.c:6035 #, c-format msgid "E155: Unknown sign: %s" msgstr "E155: Onbekende opsie: %s" -#: ../ex_cmds.c:5877 msgid "E159: Missing sign number" msgstr "E159: Ontbrekende tekennommer" -#: ../ex_cmds.c:5971 #, c-format msgid "E158: Invalid buffer name: %s" msgstr "E158: Ongeldige buffernaam: %s" -#: ../ex_cmds.c:6008 +#~ msgid "E934: Cannot jump to a buffer that does not have a name" +#~ msgstr "" + #, c-format msgid "E157: Invalid sign ID: %<PRId64>" msgstr "E157: Ongeldige teken ID: %<PRId64>" -#: ../ex_cmds.c:6066 +#, c-format +#~ msgid "E885: Not possible to change sign %s" +#~ msgstr "" + msgid " (not supported)" msgstr " (word nie ondersteun nie)" -#: ../ex_cmds.c:6169 msgid "[Deleted]" msgstr "[Geskrap]" -#: ../ex_cmds2.c:139 +#, fuzzy +#~ msgid "No old files" +#~ msgstr "Geen ingeslote lers nie" + msgid "Entering Debug mode. Type \"cont\" to continue." msgstr "Ontfoutmodus begin nou. Tik \"cont\" om te verlaat." -#: ../ex_cmds2.c:143 ../ex_docmd.c:759 #, c-format msgid "line %<PRId64>: %s" msgstr "rel %<PRId64>: %s" -#: ../ex_cmds2.c:145 #, c-format msgid "cmd: %s" msgstr "cmd: %s" -#: ../ex_cmds2.c:322 +#~ msgid "frame is zero" +#~ msgstr "" + +#, c-format +#~ msgid "frame at highest level: %d" +#~ msgstr "" + #, c-format msgid "Breakpoint in \"%s%s\" line %<PRId64>" msgstr "Inspeksiepunt in \"%s%s\" rel %<PRId64>" -#: ../ex_cmds2.c:581 #, c-format msgid "E161: Breakpoint not found: %s" msgstr "E161: Inspeksiepunt kon nie gevind word nie: %s" -#: ../ex_cmds2.c:611 msgid "No breakpoints defined" msgstr "Geen inspeksiepunte gedefinieer nie" -#: ../ex_cmds2.c:617 #, c-format msgid "%3d %s %s line %<PRId64>" msgstr "%3d %s %s rel %<PRId64>" -#: ../ex_cmds2.c:942 -msgid "E750: First use \":profile start {fname}\"" -msgstr "" +#~ msgid "E750: First use \":profile start {fname}\"" +#~ msgstr "" -#: ../ex_cmds2.c:1269 #, fuzzy, c-format -msgid "Save changes to \"%s\"?" -msgstr "Stoor veranderinge na \"%.*s\"?" +#~ msgid "Save changes to \"%s\"?" +#~ msgstr "Stoor veranderinge na \"%.*s\"?" -#: ../ex_cmds2.c:1271 ../ex_docmd.c:8851 msgid "Untitled" msgstr "Ongetiteld" -#: ../ex_cmds2.c:1421 #, c-format msgid "E162: No write since last change for buffer \"%s\"" msgstr "E162: Buffer \"%s\" is nie geskryf sedert vorige wysiging nie" -#: ../ex_cmds2.c:1480 msgid "Warning: Entered other buffer unexpectedly (check autocommands)" msgstr "Waarskuwing: Ander buffer onverwags betree (kyk na outobevele)" -#: ../ex_cmds2.c:1826 msgid "E163: There is only one file to edit" msgstr "E163: Daar is net een ler om te bewerk" -#: ../ex_cmds2.c:1828 msgid "E164: Cannot go before first file" msgstr "E164: Kan nie vr die eerste ler gaan nie" -#: ../ex_cmds2.c:1830 msgid "E165: Cannot go beyond last file" msgstr "E165: Kan nie verby die laaste ler gaan nie" -#: ../ex_cmds2.c:2175 #, c-format msgid "E666: compiler not supported: %s" msgstr "E666: vertaler word nie ondersteun nie: %s" -#: ../ex_cmds2.c:2257 #, c-format msgid "Searching for \"%s\" in \"%s\"" msgstr "Besig om te soek vir \"%s\" in \"%s\"" -#: ../ex_cmds2.c:2284 #, c-format msgid "Searching for \"%s\"" msgstr "Besig om te soek vir \"%s\"" -#: ../ex_cmds2.c:2307 -#, c-format -msgid "not found in 'runtimepath': \"%s\"" -msgstr "kon nie in 'runtimepath' gevind word nie: \"%s\"" +#, fuzzy, c-format +#~ msgid "not found in '%s': \"%s\"" +#~ msgstr "kon nie in 'runtimepath' gevind word nie: \"%s\"" -#: ../ex_cmds2.c:2472 #, c-format msgid "Cannot source a directory: \"%s\"" msgstr "Kan nie gids uitvoer nie: \"%s\"" -#: ../ex_cmds2.c:2518 #, c-format msgid "could not source \"%s\"" msgstr "kon nie \"%s\" uitvoer nie" -#: ../ex_cmds2.c:2520 #, c-format msgid "line %<PRId64>: could not source \"%s\"" msgstr "rel %<PRId64>: kon nie \"%s\" uitvoer nie" -#: ../ex_cmds2.c:2535 #, c-format msgid "sourcing \"%s\"" msgstr "besig om \"%s\" uit te voer" -#: ../ex_cmds2.c:2537 #, c-format msgid "line %<PRId64>: sourcing \"%s\"" msgstr "rel %<PRId64>: voer nou \"%s\" uit" -#: ../ex_cmds2.c:2693 #, c-format msgid "finished sourcing %s" msgstr "%s klaar uitgevoer" -#: ../ex_cmds2.c:2765 #, fuzzy -msgid "modeline" -msgstr "1 rel meer" +#~ msgid "modeline" +#~ msgstr "1 rel meer" -#: ../ex_cmds2.c:2767 #, fuzzy -msgid "--cmd argument" -msgstr " vim [parameters] " +#~ msgid "--cmd argument" +#~ msgstr " vim [parameters] " -#: ../ex_cmds2.c:2769 #, fuzzy -msgid "-c argument" -msgstr " vim [parameters] " +#~ msgid "-c argument" +#~ msgstr " vim [parameters] " -#: ../ex_cmds2.c:2771 #, fuzzy -msgid "environment variable" -msgstr "Stel die 'LANG' omgewingsveranderlike na jou lokaal toe" +#~ msgid "environment variable" +#~ msgstr "Stel die 'LANG' omgewingsveranderlike na jou lokaal toe" -#: ../ex_cmds2.c:2773 #, fuzzy -msgid "error handler" -msgstr "Fout en onderbreking" +#~ msgid "error handler" +#~ msgstr "Fout en onderbreking" -#: ../ex_cmds2.c:3020 msgid "W15: Warning: Wrong line separator, ^M may be missing" msgstr "W15: Waarskuwing: Verkeerde relskeiding, ^M ontbreek dalk" -#: ../ex_cmds2.c:3139 msgid "E167: :scriptencoding used outside of a sourced file" msgstr "E167: ':scriptencoding' buite 'n uitvoerler gebruik" -#: ../ex_cmds2.c:3166 msgid "E168: :finish used outside of a sourced file" msgstr "E168: ':finish' buite 'n uitvoerler gebruik" -#: ../ex_cmds2.c:3389 #, c-format msgid "Current %slanguage: \"%s\"" msgstr "Huidige %staal: \"%s\"" -#: ../ex_cmds2.c:3404 #, c-format msgid "E197: Cannot set language to \"%s\"" msgstr "E197: Kan nie taal na \"%s\" verander nie" #. don't redisplay the window #. don't wait for return -#: ../ex_docmd.c:387 msgid "Entering Ex mode. Type \"visual\" to go to Normal mode." msgstr "Betree Ex modus. Tik \"visual\" om na Normale modus terug te keer." -#: ../ex_docmd.c:428 msgid "E501: At end-of-file" msgstr "E501: By lereinde" -#: ../ex_docmd.c:513 msgid "E169: Command too recursive" msgstr "E169: Bevel te rekursief" -#: ../ex_docmd.c:1006 +#, fuzzy +#~ msgid "line %" +#~ msgstr "rel %4ld:" + #, c-format msgid "E605: Exception not caught: %s" msgstr "E605: Uitsondering nie gevang nie: %s" -#: ../ex_docmd.c:1085 msgid "End of sourced file" msgstr "Einde van uitvoerler" -#: ../ex_docmd.c:1086 msgid "End of function" msgstr "Einde van funksie " -#: ../ex_docmd.c:1628 msgid "E464: Ambiguous use of user-defined command" msgstr "E464: Dubbelsinnige gebruik van gebruiker-gedefinieerde bevel" -#: ../ex_docmd.c:1638 msgid "E492: Not an editor command" msgstr "E492: Nie 'n verwerkerbevel nie" -#: ../ex_docmd.c:1729 msgid "E493: Backwards range given" msgstr "E493: Terugwaardse omvang gegee" -#: ../ex_docmd.c:1733 msgid "Backwards range given, OK to swap" msgstr "Terugwaardse omvang gegee, OK om te ruil" #. append #. typed wrong -#: ../ex_docmd.c:1787 msgid "E494: Use w or w>>" msgstr "E494: Gebruik w of w>>" -#: ../ex_docmd.c:3454 msgid "E319: The command is not available in this version" msgstr "E319: Jammer, die bevel is nie gemplementeer nie" -#: ../ex_docmd.c:3752 -msgid "E172: Only one file name allowed" -msgstr "E172: Slegs een lernaam toegelaat" - -#: ../ex_docmd.c:4238 msgid "1 more file to edit. Quit anyway?" msgstr "Nog 1 ler om te bewerk. Stop in elk geval?" -#: ../ex_docmd.c:4242 #, c-format msgid "%d more files to edit. Quit anyway?" msgstr "Nog %d lers om te bewerk. Stop in elk geval?" -#: ../ex_docmd.c:4248 msgid "E173: 1 more file to edit" msgstr "E173: Nog 1 ler om te bewerk" -#: ../ex_docmd.c:4250 #, c-format msgid "E173: %<PRId64> more files to edit" msgstr "E173: Nog %<PRId64> lers om te bewerk" -#: ../ex_docmd.c:4320 msgid "E174: Command already exists: add ! to replace it" msgstr "E174: Bevel bestaan alreeds: gebruik ! om te herdefinieer" -#: ../ex_docmd.c:4432 +#, fuzzy msgid "" "\n" -" Name Args Range Complete Definition" +" Name Args Address Complete Definition" msgstr "" "\n" " Naam Args Reeks Klaar Definisie" -#: ../ex_docmd.c:4516 msgid "No user-defined commands found" msgstr "Geen gebruiker-gedefinieerde bevele gevind nie" -#: ../ex_docmd.c:4538 msgid "E175: No attribute specified" msgstr "E175: Geen eienskappe gespesifiseer nie" -#: ../ex_docmd.c:4583 msgid "E176: Invalid number of arguments" msgstr "E176: Ongeldige aantal parameters" -#: ../ex_docmd.c:4594 msgid "E177: Count cannot be specified twice" msgstr "E177: Telling kan nie twee keer gespesifiseer word nie" -#: ../ex_docmd.c:4603 msgid "E178: Invalid default value for count" msgstr "E178: Ongeldige verstekwaarde vir telling" -#: ../ex_docmd.c:4625 #, fuzzy -msgid "E179: argument required for -complete" -msgstr "E179: parameter nodig vir voltooiing" +#~ msgid "E179: argument required for -complete" +#~ msgstr "E179: parameter nodig vir voltooiing" + +#, fuzzy +#~ msgid "E179: argument required for -addr" +#~ msgstr "E179: parameter nodig vir voltooiing" -#: ../ex_docmd.c:4635 #, c-format msgid "E181: Invalid attribute: %s" msgstr "E181: Ongeldige eienskap: %s" -#: ../ex_docmd.c:4678 msgid "E182: Invalid command name" msgstr "E182: Ongeldige bevelnaam" -#: ../ex_docmd.c:4691 msgid "E183: User defined commands must start with an uppercase letter" msgstr "E183: Gebruiker-gedefinieerde bevele moet met 'n hoofletter begin" -#: ../ex_docmd.c:4696 #, fuzzy -msgid "E841: Reserved name, cannot be used for user defined command" -msgstr "E464: Dubbelsinnige gebruik van gebruiker-gedefinieerde bevel" +#~ msgid "E841: Reserved name, cannot be used for user defined command" +#~ msgstr "E464: Dubbelsinnige gebruik van gebruiker-gedefinieerde bevel" -#: ../ex_docmd.c:4751 #, c-format msgid "E184: No such user-defined command: %s" msgstr "E184: Geen gebruiker-gedefinieerde bevel nie: %s" -#: ../ex_docmd.c:5219 +#, fuzzy, c-format +#~ msgid "E180: Invalid address type value: %s" +#~ msgstr "E180: Ongeldige voltooiingswaarde: %s" + #, c-format msgid "E180: Invalid complete value: %s" msgstr "E180: Ongeldige voltooiingswaarde: %s" -#: ../ex_docmd.c:5225 msgid "E468: Completion argument only allowed for custom completion" msgstr "E468: Voltooiingsargument words slegs toegelaat vir eie voltooiing" -#: ../ex_docmd.c:5231 msgid "E467: Custom completion requires a function argument" msgstr "E467: Eie voltooiing benodig 'n funksie parameter" -#: ../ex_docmd.c:5257 #, fuzzy, c-format -msgid "E185: Cannot find color scheme '%s'" -msgstr "E185: Kan nie kleurskema %s vind nie" +#~ msgid "E185: Cannot find color scheme '%s'" +#~ msgstr "E185: Kan nie kleurskema %s vind nie" -#: ../ex_docmd.c:5263 msgid "Greetings, Vim user!" msgstr "Goeiedag, Vim gebruiker!" -#: ../ex_docmd.c:5431 #, fuzzy -msgid "E784: Cannot close last tab page" -msgstr "E444: Kan nie laaste venster toemaak nie" +#~ msgid "E784: Cannot close last tab page" +#~ msgstr "E444: Kan nie laaste venster toemaak nie" -#: ../ex_docmd.c:5462 #, fuzzy -msgid "Already only one tab page" -msgstr "Daar is alreeds slegs een venster" +#~ msgid "Already only one tab page" +#~ msgstr "Daar is alreeds slegs een venster" -#: ../ex_docmd.c:6004 #, fuzzy, c-format -msgid "Tab page %d" -msgstr "Bladsy %d" +#~ msgid "Tab page %d" +#~ msgstr "Bladsy %d" -#: ../ex_docmd.c:6295 msgid "No swap file" msgstr "Geen ruiller" -#: ../ex_docmd.c:6478 -#, fuzzy -msgid "E747: Cannot change directory, buffer is modified (add ! to override)" -msgstr "E509: Kan rugsteunler nie skep nie (gebruik ! om te dwing)" - -#: ../ex_docmd.c:6485 msgid "E186: No previous directory" msgstr "E186: Geen vorige gids nie" -#: ../ex_docmd.c:6530 msgid "E187: Unknown" msgstr "E187: Onbekend" -#: ../ex_docmd.c:6610 msgid "E465: :winsize requires two number arguments" msgstr "E465: ':winsize' benodig twee nommer parameters" -#: ../ex_docmd.c:6655 -msgid "E188: Obtaining window position not implemented for this platform" -msgstr "" -"E188: Verkryging van vensterposisie is nie vir hierdie platform " -"gemplementeer nie" - -#: ../ex_docmd.c:6662 -msgid "E466: :winpos requires two number arguments" -msgstr "E466: :winpos benodig twee parameters" - -#: ../ex_docmd.c:7241 -#, fuzzy, c-format -msgid "E739: Cannot create directory: %s" -msgstr "Kan nie gids uitvoer nie: \"%s\"" - -#: ../ex_docmd.c:7268 #, c-format msgid "E189: \"%s\" exists (add ! to override)" msgstr "E189: \"%s\" bestaan (gebruik ! om te dwing)" -#: ../ex_docmd.c:7273 #, c-format msgid "E190: Cannot open \"%s\" for writing" msgstr "E190: Kan \"%s\" nie oopmaak vir skryf nie" #. set mark -#: ../ex_docmd.c:7294 msgid "E191: Argument must be a letter or forward/backward quote" msgstr "" "E191: Parameter moet 'n letter of 'n terug/vorentoe aanhalingsteken wees" -#: ../ex_docmd.c:7333 msgid "E192: Recursive use of :normal too deep" msgstr "E192: Rekursiewe gebruik van ':normal' te diep" -#: ../ex_docmd.c:7807 msgid "E194: No alternate file name to substitute for '#'" msgstr "E194: Geen alternatiewe lernaam vir '#' nie" -#: ../ex_docmd.c:7841 msgid "E495: no autocommand file name to substitute for \"<afile>\"" msgstr "E495: geen outobevel-lernaam om \"<afile>\" mee te vervang nie" -#: ../ex_docmd.c:7850 msgid "E496: no autocommand buffer number to substitute for \"<abuf>\"" msgstr "E496: geen outobevel buffernommer om \"<abuf>\" mee te vervang nie" -#: ../ex_docmd.c:7861 msgid "E497: no autocommand match name to substitute for \"<amatch>\"" msgstr "E497: geen outobevel treffernaam om \"<amatch>\" mee te vervang nie" -#: ../ex_docmd.c:7870 msgid "E498: no :source file name to substitute for \"<sfile>\"" msgstr "E498: geen ':source' lernaam om \"<sfile>\" mee te vervang nie" -#: ../ex_docmd.c:7876 #, fuzzy -msgid "E842: no line number to use for \"<slnum>\"" -msgstr "E498: geen ':source' lernaam om \"<sfile>\" mee te vervang nie" +#~ msgid "E842: no line number to use for \"<slnum>\"" +#~ msgstr "E498: geen ':source' lernaam om \"<sfile>\" mee te vervang nie" -#: ../ex_docmd.c:7903 #, c-format msgid "E499: Empty file name for '%' or '#', only works with \":p:h\"" msgstr "E499: Le lernaam vir '%' of '#', werk slegs met \":p:h\"" -#: ../ex_docmd.c:7905 msgid "E500: Evaluates to an empty string" msgstr "E500: Evalueer na 'n le string" -#: ../ex_docmd.c:8838 -msgid "E195: Cannot open viminfo file for reading" -msgstr "E195: Kan 'viminfo' ler nie oopmaak om te lees nie" - -#: ../ex_eval.c:464 msgid "E608: Cannot :throw exceptions with 'Vim' prefix" msgstr "E608: Kan nie uitsonderings ':throw' met 'Vim' voorvoegsel nie" #. always scroll up, don't overwrite -#: ../ex_eval.c:496 #, c-format msgid "Exception thrown: %s" msgstr "Uitsondering gegooi: %s" -#: ../ex_eval.c:545 +#. always scroll up, don't overwrite #, c-format msgid "Exception finished: %s" msgstr "Uitsondering het klaar gemaak: %s" -#: ../ex_eval.c:546 #, c-format msgid "Exception discarded: %s" msgstr "Uitsondering weg gegooi: %s" -#: ../ex_eval.c:588 ../ex_eval.c:634 #, c-format msgid "%s, line %<PRId64>" msgstr "%s, rel %<PRId64>" #. always scroll up, don't overwrite -#: ../ex_eval.c:608 #, c-format msgid "Exception caught: %s" msgstr "Uitsondering gevang: %s" -#: ../ex_eval.c:676 #, c-format msgid "%s made pending" msgstr "%s is afwagtend gemaak" -#: ../ex_eval.c:679 #, c-format msgid "%s resumed" msgstr "%s teruggekeer" -#: ../ex_eval.c:683 #, c-format msgid "%s discarded" msgstr "%s weg gegooi" -#: ../ex_eval.c:708 msgid "Exception" msgstr "Uitsondering" -#: ../ex_eval.c:713 msgid "Error and interrupt" msgstr "Fout en onderbreking" -#: ../ex_eval.c:715 msgid "Error" msgstr "Fout" #. if (pending & CSTP_INTERRUPT) -#: ../ex_eval.c:717 msgid "Interrupt" msgstr "Onderbreek" -#: ../ex_eval.c:795 msgid "E579: :if nesting too deep" msgstr "E579: geneste ':if' te diep" -#: ../ex_eval.c:830 msgid "E580: :endif without :if" msgstr "E580: ':endif' sonder ':if'" -#: ../ex_eval.c:873 msgid "E581: :else without :if" msgstr "E581: ':else' sonder ':if'" -#: ../ex_eval.c:876 msgid "E582: :elseif without :if" msgstr "E582: ':elseif' sonder ':if'" -#: ../ex_eval.c:880 msgid "E583: multiple :else" msgstr "E583: meer as een ':else'" -#: ../ex_eval.c:883 msgid "E584: :elseif after :else" msgstr "E584: ':elseif' na ':else'" -#: ../ex_eval.c:941 #, fuzzy -msgid "E585: :while/:for nesting too deep" -msgstr "E585: ':while' te diep genes" +#~ msgid "E585: :while/:for nesting too deep" +#~ msgstr "E585: ':while' te diep genes" -#: ../ex_eval.c:1028 #, fuzzy -msgid "E586: :continue without :while or :for" -msgstr "E586: ':continue' sonder ':while'" +#~ msgid "E586: :continue without :while or :for" +#~ msgstr "E586: ':continue' sonder ':while'" -#: ../ex_eval.c:1061 #, fuzzy -msgid "E587: :break without :while or :for" -msgstr "E587: ':break' sonder ':while'" +#~ msgid "E587: :break without :while or :for" +#~ msgstr "E587: ':break' sonder ':while'" -#: ../ex_eval.c:1102 #, fuzzy -msgid "E732: Using :endfor with :while" -msgstr "E170: Ontbrekende ':endwhile'" +#~ msgid "E732: Using :endfor with :while" +#~ msgstr "E170: Ontbrekende ':endwhile'" -#: ../ex_eval.c:1104 #, fuzzy -msgid "E733: Using :endwhile with :for" -msgstr "E170: Ontbrekende ':endwhile'" +#~ msgid "E733: Using :endwhile with :for" +#~ msgstr "E170: Ontbrekende ':endwhile'" -#: ../ex_eval.c:1247 msgid "E601: :try nesting too deep" msgstr "E601: geneste ':try' te diep" -#: ../ex_eval.c:1317 msgid "E603: :catch without :try" msgstr "E603: ':catch' sonder ':try'" #. Give up for a ":catch" after ":finally" and ignore it. #. * Just parse. -#: ../ex_eval.c:1332 msgid "E604: :catch after :finally" msgstr "E604: ':catch' na ':finally'" -#: ../ex_eval.c:1451 msgid "E606: :finally without :try" msgstr "E606: ':finally' sonder ':try'" #. Give up for a multiple ":finally" and ignore it. -#: ../ex_eval.c:1467 msgid "E607: multiple :finally" msgstr "E607: meer as een ':finally'" -#: ../ex_eval.c:1571 msgid "E602: :endtry without :try" msgstr "E602: ':endtry' sonder ':try'" -#: ../ex_eval.c:2026 msgid "E193: :endfunction not inside a function" msgstr "E193: ':endfunction' nie in 'n funksie nie" -#: ../ex_getln.c:1643 #, fuzzy -msgid "E788: Not allowed to edit another buffer now" -msgstr "E48: Nie toegelaat in sandput nie" +#~ msgid "E788: Not allowed to edit another buffer now" +#~ msgstr "E48: Nie toegelaat in sandput nie" -#: ../ex_getln.c:1656 #, fuzzy -msgid "E811: Not allowed to change buffer information now" -msgstr "E94: Geen buffer wat by %s pas nie" +#~ msgid "E811: Not allowed to change buffer information now" +#~ msgstr "E94: Geen buffer wat by %s pas nie" -#: ../ex_getln.c:3178 -msgid "tagname" -msgstr "etiketnaam" +#, c-format +#~ msgid "E5408: Unable to get g:Nvim_color_cmdline callback: %s" +#~ msgstr "" -#: ../ex_getln.c:3181 -msgid " kind file\n" -msgstr " tipe ler\n" +#, c-format +#~ msgid "E5409: Unable to get g:Nvim_color_expr callback: %s" +#~ msgstr "" -#: ../ex_getln.c:4799 -msgid "'history' option is zero" -msgstr "'history' opsie is nul" +#, c-format +#~ msgid "E5407: Callback has thrown an exception: %s" +#~ msgstr "" + +#~ msgid "E5400: Callback should return list" +#~ msgstr "" -#: ../ex_getln.c:5046 #, c-format -msgid "" -"\n" -"# %s History (newest to oldest):\n" -msgstr "" -"\n" -"# %s Geskiedenis (van nuutste na oudste):\n" +#~ msgid "E5401: List item %i is not a List" +#~ msgstr "" + +#, c-format +#~ msgid "E5402: List item %i has incorrect length: %li /= 3" +#~ msgstr "" + +#~ msgid "E5403: Chunk %i start %" +#~ msgstr "" -#: ../ex_getln.c:5047 -msgid "Command Line" -msgstr "Bevelrel" +#~ msgid "E5405: Chunk %i start %" +#~ msgstr "" -#: ../ex_getln.c:5048 -msgid "Search String" -msgstr "Soekstring" +#~ msgid "E5404: Chunk %i end %" +#~ msgstr "" -#: ../ex_getln.c:5049 -msgid "Expression" -msgstr "Uitdrukking" +#~ msgid "E5406: Chunk %i end %" +#~ msgstr "" -#: ../ex_getln.c:5050 -msgid "Input Line" -msgstr "Invoer Lyn" +msgid "tagname" +msgstr "etiketnaam" + +msgid " kind file\n" +msgstr " tipe ler\n" + +msgid "'history' option is zero" +msgstr "'history' opsie is nul" -#: ../ex_getln.c:5117 msgid "E198: cmd_pchar beyond the command length" msgstr "E198: 'cmd_pchar' verby die einde van opdraglengte" -#: ../ex_getln.c:5279 msgid "E199: Active window or buffer deleted" msgstr "E199: Aktiewe venster of buffer geskrap" -#: ../file_search.c:203 -msgid "E854: path too long for completion" -msgstr "" +#~ msgid "E854: path too long for completion" +#~ msgstr "" -#: ../file_search.c:446 #, c-format msgid "" "E343: Invalid path: '**[number]' must be at the end of the path or be " @@ -2052,267 +1999,208 @@ msgstr "" "E343: Ongeldige pad: '**[nommer]' moet aan die einde van 'n pad wees of " "gevolg wees deur %s'." -#: ../file_search.c:1505 #, c-format msgid "E344: Can't find directory \"%s\" in cdpath" msgstr "E344: Kan nie gids \"%s\" in 'cdpath' vind nie" -#: ../file_search.c:1508 #, c-format msgid "E345: Can't find file \"%s\" in path" msgstr "E345: Kan ler \"%s\" nie vind in pad nie" -#: ../file_search.c:1512 #, c-format msgid "E346: No more directory \"%s\" found in cdpath" msgstr "E346: Geen gids \"%s\" meer gevind in 'cdpath' nie" -#: ../file_search.c:1515 #, c-format msgid "E347: No more file \"%s\" found in path" msgstr "E347: Geen ler \"%s\" meer gevind in pad nie" -#: ../fileio.c:137 #, fuzzy -msgid "E812: Autocommands changed buffer or buffer name" -msgstr "E135: *Filter* Outobevele mag nie die huidige buffer verander nie" +#~ msgid "E812: Autocommands changed buffer or buffer name" +#~ msgstr "E135: *Filter* Outobevele mag nie die huidige buffer verander nie" -#: ../fileio.c:368 msgid "Illegal file name" msgstr "Ongeldige lernaam" -#: ../fileio.c:395 ../fileio.c:476 ../fileio.c:2543 ../fileio.c:2578 msgid "is a directory" msgstr "is 'n gids" -#: ../fileio.c:397 msgid "is not a file" msgstr "is nie 'n ler nie" -#: ../fileio.c:508 ../fileio.c:3522 msgid "[New File]" msgstr "[Nuwe ler]" -#: ../fileio.c:511 -msgid "[New DIRECTORY]" -msgstr "" +#~ msgid "[New DIRECTORY]" +#~ msgstr "" -#: ../fileio.c:529 ../fileio.c:532 -msgid "[File too big]" -msgstr "" +#. libuv only returns -errno in Unix and in Windows open() does not +#. set EOVERFLOW +#~ msgid "[File too big]" +#~ msgstr "" -#: ../fileio.c:534 msgid "[Permission Denied]" msgstr "[Toestemming Geweier]" -#: ../fileio.c:653 msgid "E200: *ReadPre autocommands made the file unreadable" msgstr "E200: '*ReadPre' outobevele het die ler onleesbaar gemaak" -#: ../fileio.c:655 msgid "E201: *ReadPre autocommands must not change current buffer" msgstr "E201: '*ReadPre' outobevele mag nie die huidige buffer verander nie" -#: ../fileio.c:672 -msgid "Nvim: Reading from stdin...\n" -msgstr "Vim: Lees nou vanaf 'stdin'...\n" - #. Re-opening the original file failed! -#: ../fileio.c:909 msgid "E202: Conversion made file unreadable!" msgstr "E202: Omsetting het ler onleesbaar gemaak!" #. fifo or socket -#: ../fileio.c:1782 msgid "[fifo/socket]" msgstr "[fifo/socket]" #. fifo -#: ../fileio.c:1788 msgid "[fifo]" msgstr "[fifo]" #. or socket -#: ../fileio.c:1794 msgid "[socket]" msgstr "[socket]" #. or character special -#: ../fileio.c:1801 #, fuzzy -msgid "[character special]" -msgstr "1 karakter" +#~ msgid "[character special]" +#~ msgstr "1 karakter" -#: ../fileio.c:1815 msgid "[CR missing]" msgstr "[CR ontbreek]" -#: ../fileio.c:1819 msgid "[long lines split]" msgstr "[lang rels verdeel]" -#: ../fileio.c:1823 ../fileio.c:3512 msgid "[NOT converted]" msgstr "[NIE omgesit nie]" -#: ../fileio.c:1826 ../fileio.c:3515 msgid "[converted]" msgstr "[omgesit]" -#: ../fileio.c:1831 #, fuzzy, c-format -msgid "[CONVERSION ERROR in line %<PRId64>]" -msgstr "[ONWETTIGE GREEP in rel %<PRId64>]" +#~ msgid "[CONVERSION ERROR in line %<PRId64>]" +#~ msgstr "[ONWETTIGE GREEP in rel %<PRId64>]" -#: ../fileio.c:1835 #, c-format msgid "[ILLEGAL BYTE in line %<PRId64>]" msgstr "[ONWETTIGE GREEP in rel %<PRId64>]" -#: ../fileio.c:1838 msgid "[READ ERRORS]" msgstr "[LEESFOUTE]" -#: ../fileio.c:2104 msgid "Can't find temp file for conversion" msgstr "Kan nie tydelike ler vir omsetting vind nie" -#: ../fileio.c:2110 msgid "Conversion with 'charconvert' failed" msgstr "Omsetting met 'charconvert' het gefaal" -#: ../fileio.c:2113 msgid "can't read output of 'charconvert'" msgstr "kan afvoer van 'charconvert' nie lees nie" -#: ../fileio.c:2437 #, fuzzy -msgid "E676: No matching autocommands for acwrite buffer" -msgstr "Geen passende outobevele nie" +#~ msgid "E676: No matching autocommands for acwrite buffer" +#~ msgstr "Geen passende outobevele nie" -#: ../fileio.c:2466 msgid "E203: Autocommands deleted or unloaded buffer to be written" msgstr "E203: Outobevele het die skryfbuffer geskrap of uitgelaai" -#: ../fileio.c:2486 msgid "E204: Autocommand changed number of lines in unexpected way" msgstr "E204: Outobevel het etlike rels op onverwagse wyse verander " -#: ../fileio.c:2548 ../fileio.c:2565 msgid "is not a file or writable device" msgstr "is nie 'n ler of 'n skryfbare toestel nie" -#: ../fileio.c:2601 msgid "is read-only (add ! to override)" msgstr "is lees-alleen (gebruik ! om te dwing)" -#: ../fileio.c:2886 msgid "E506: Can't write to backup file (add ! to override)" msgstr "E506: Kan nie na rugsteunler skryf nie (gebruik ! om te dwing)" -#: ../fileio.c:2898 -msgid "E507: Close error for backup file (add ! to override)" -msgstr "E507: Sluitfout vir rugsteunler (gebruik ! om te dwing)" +#, fuzzy, c-format +#~ msgid "E507: Close error for backup file (add ! to override): %s" +#~ msgstr "E507: Sluitfout vir rugsteunler (gebruik ! om te dwing)" -#: ../fileio.c:2901 msgid "E508: Can't read file for backup (add ! to override)" msgstr "E508: Kan rugsteunler nie lees nie (gebruik ! om te dwing)" -#: ../fileio.c:2923 msgid "E509: Cannot create backup file (add ! to override)" msgstr "E509: Kan rugsteunler nie skep nie (gebruik ! om te dwing)" -#: ../fileio.c:3008 msgid "E510: Can't make backup file (add ! to override)" msgstr "E510: Kan rugsteunler nie skep nie (gebruik ! om te dwing)" #. Can't write without a tempfile! -#: ../fileio.c:3121 msgid "E214: Can't find temp file for writing" msgstr "E214: Kan nie tydelike ler vind vir skryf nie" -#: ../fileio.c:3134 msgid "E213: Cannot convert (add ! to write without conversion)" msgstr "E213: Kan nie omsit nie (gebruik ! om te skryf sonder omsetting)" -#: ../fileio.c:3169 msgid "E166: Can't open linked file for writing" msgstr "E166: Kan ler nie oopmaak vir skryf nie" -#: ../fileio.c:3173 -msgid "E212: Can't open file for writing" -msgstr "E212: Kan ler nie oopmaak vir skryf nie" +#, fuzzy, c-format +#~ msgid "E212: Can't open file for writing: %s" +#~ msgstr "E212: Kan ler nie oopmaak vir skryf nie" -#: ../fileio.c:3363 -msgid "E667: Fsync failed" -msgstr "E667: 'Fsync' het gefaal" +#, fuzzy, c-format +#~ msgid "E667: Fsync failed: %s" +#~ msgstr "E667: 'Fsync' het gefaal" -#: ../fileio.c:3398 -msgid "E512: Close failed" -msgstr "E512: Sluiting gefaal" +#, fuzzy, c-format +#~ msgid "E512: Close failed: %s" +#~ msgstr "E512: Sluiting gefaal" -#: ../fileio.c:3436 #, fuzzy -msgid "E513: write error, conversion failed (make 'fenc' empty to override)" -msgstr "E513: skryffout, omsetting gefaal" +#~ msgid "E513: write error, conversion failed (make 'fenc' empty to override)" +#~ msgstr "E513: skryffout, omsetting gefaal" -#: ../fileio.c:3441 -#, c-format -msgid "" -"E513: write error, conversion failed in line %<PRId64> (make 'fenc' empty to " -"override)" -msgstr "" +#, fuzzy +#~ msgid "E513: write error, conversion failed in line %" +#~ msgstr "E513: skryffout, omsetting gefaal" -#: ../fileio.c:3448 msgid "E514: write error (file system full?)" msgstr "E514: skryffout (lerstelsel vol?)" -#: ../fileio.c:3506 msgid " CONVERSION ERROR" msgstr " OMSETTINGSFOUT" -#: ../fileio.c:3509 #, fuzzy, c-format -msgid " in line %<PRId64>;" -msgstr "rel %<PRId64>" +#~ msgid " in line %<PRId64>;" +#~ msgstr "rel %<PRId64>" -#: ../fileio.c:3519 msgid "[Device]" msgstr "[Toestel]" -#: ../fileio.c:3522 msgid "[New]" msgstr "[Nuut]" -#: ../fileio.c:3535 msgid " [a]" msgstr " [a]" -#: ../fileio.c:3535 msgid " appended" msgstr " bygevoeg" -#: ../fileio.c:3537 msgid " [w]" msgstr " [w]" -#: ../fileio.c:3537 msgid " written" msgstr " geskryf" -#: ../fileio.c:3579 msgid "E205: Patchmode: can't save original file" msgstr "E205: patchmode: kan oorspronklike ler nie stoor nie" -#: ../fileio.c:3602 msgid "E206: patchmode: can't touch empty original file" msgstr "E206: patchmode: kan le oorsprongler nie 'touch' nie" -#: ../fileio.c:3616 msgid "E207: Can't delete backup file" msgstr "E207: Kan rugsteunler nie verwyder nie" -#: ../fileio.c:3672 +#. Set highlight for error messages. msgid "" "\n" "WARNING: Original file may be lost or damaged\n" @@ -2320,96 +2208,75 @@ msgstr "" "\n" "WAARSKUWING: Oorspronklike ler mag verlore of beskadig wees\n" -#: ../fileio.c:3675 msgid "don't quit the editor until the file is successfully written!" msgstr "moenie die verwerker verlaat voor die ler suksesvol geskryf is nie!" -#: ../fileio.c:3795 -msgid "[dos]" -msgstr "[dos]" - -#: ../fileio.c:3795 msgid "[dos format]" msgstr "[dos formaat]" -#: ../fileio.c:3801 -msgid "[mac]" -msgstr "[mac]" +msgid "[dos]" +msgstr "[dos]" -#: ../fileio.c:3801 msgid "[mac format]" msgstr "[mac formaat]" -#: ../fileio.c:3807 -msgid "[unix]" -msgstr "[unix]" +msgid "[mac]" +msgstr "[mac]" -#: ../fileio.c:3807 msgid "[unix format]" msgstr "[unix formaat]" -#: ../fileio.c:3831 +msgid "[unix]" +msgstr "[unix]" + msgid "1 line, " msgstr "1 rel, " -#: ../fileio.c:3833 #, c-format msgid "%<PRId64> lines, " msgstr "%<PRId64> rels, " -#: ../fileio.c:3836 msgid "1 character" msgstr "1 karakter" -#: ../fileio.c:3838 #, c-format msgid "%<PRId64> characters" msgstr "%<PRId64> karakters" -#: ../fileio.c:3849 -msgid "[noeol]" -msgstr "[noeol]" - -#: ../fileio.c:3849 msgid "[Incomplete last line]" msgstr "[Onvoltooide laaste rel]" -#. don't overwrite messages here -#. must give this prompt -#. don't use emsg() here, don't want to flush the buffers -#: ../fileio.c:3865 +msgid "[noeol]" +msgstr "[noeol]" + +#. Don't overwrite messages here. +#. Must give this prompt. +#. Don't use emsg() here, don't want to flush the buffers. msgid "WARNING: The file has been changed since reading it!!!" msgstr "WAARSKUWING: Die ler het verander sedert dit gelees is!!!" -#: ../fileio.c:3867 msgid "Do you really want to write to it" msgstr "Wil jy regtig soontoe skryf?" -#: ../fileio.c:4648 #, c-format msgid "E208: Error writing to \"%s\"" msgstr "E208: Kan nie skryf na \"%s\"" -#: ../fileio.c:4655 #, c-format msgid "E209: Error closing \"%s\"" msgstr "E209: Kan \"%s\" nie sluit nie" -#: ../fileio.c:4657 #, c-format msgid "E210: Error reading \"%s\"" msgstr "E210: Kan \"%s\" nie lees nie" -#: ../fileio.c:4883 msgid "E246: FileChangedShell autocommand deleted buffer" msgstr "E246: 'FileChangedShell' outobevel het buffer verwyder" -#: ../fileio.c:4894 #, fuzzy, c-format -msgid "E211: File \"%s\" no longer available" -msgstr "E211: Waarskuwing: Ler \"%s\" is nie meer beskikbaar nie" +#~ msgid "E211: File \"%s\" no longer available" +#~ msgstr "E211: Waarskuwing: Ler \"%s\" is nie meer beskikbaar nie" -#: ../fileio.c:4906 #, c-format msgid "" "W12: Warning: File \"%s\" has changed and the buffer was changed in Vim as " @@ -2418,42 +2285,34 @@ msgstr "" "W12: Waarskuwing: Ler \"%s\" het verander sedert bewerking begin het en die " "buffer in Vim het ook verander" -#: ../fileio.c:4907 #, fuzzy -msgid "See \":help W12\" for more info." -msgstr "Sien \":help W11\" vir meer inligting." +#~ msgid "See \":help W12\" for more info." +#~ msgstr "Sien \":help W11\" vir meer inligting." -#: ../fileio.c:4910 #, c-format msgid "W11: Warning: File \"%s\" has changed since editing started" msgstr "W11: Waarskuwing: Ler \"%s\" het verander sedert bewerking begin het" -#: ../fileio.c:4911 msgid "See \":help W11\" for more info." msgstr "Sien \":help W11\" vir meer inligting." -#: ../fileio.c:4914 #, c-format msgid "W16: Warning: Mode of file \"%s\" has changed since editing started" msgstr "" "W16: Waarskuwing: Modus van ler \"%s\" het verander sedert bewerking begin " "het" -#: ../fileio.c:4915 #, fuzzy -msgid "See \":help W16\" for more info." -msgstr "Sien \":help W11\" vir meer inligting." +#~ msgid "See \":help W16\" for more info." +#~ msgstr "Sien \":help W11\" vir meer inligting." -#: ../fileio.c:4927 #, c-format msgid "W13: Warning: File \"%s\" has been created after editing started" msgstr "W13: Waarskuwing: Ler \"%s\" is geskep sedert bewerking begin het" -#: ../fileio.c:4947 msgid "Warning" msgstr "Waarskuwing" -#: ../fileio.c:4948 msgid "" "&OK\n" "&Load File" @@ -2461,48 +2320,46 @@ msgstr "" "&OK\n" "&Laai Ler" -#: ../fileio.c:5065 #, c-format msgid "E462: Could not prepare for reloading \"%s\"" msgstr "E462: Kon nie voorberei vir herlaai nie \"%s\"" -#: ../fileio.c:5078 #, c-format msgid "E321: Could not reload \"%s\"" msgstr "E321: Kon nie \"%s\" herlaai nie" -#: ../fileio.c:5601 msgid "--Deleted--" msgstr "--Geskrap--" -#: ../fileio.c:5732 #, c-format -msgid "auto-removing autocommand: %s <buffer=%d>" -msgstr "" +#~ msgid "auto-removing autocommand: %s <buffer=%d>" +#~ msgstr "" #. the group doesn't exist -#: ../fileio.c:5772 #, c-format msgid "E367: No such group: \"%s\"" msgstr "E367: Geen sodanige groep nie: \"%s\"" -#: ../fileio.c:5897 +#, fuzzy +#~ msgid "E936: Cannot delete the current group" +#~ msgstr "E351: Kan nie vou skrap met huidige 'foldmethod' nie" + +#~ msgid "W19: Deleting augroup that is still in use" +#~ msgstr "" + #, c-format msgid "E215: Illegal character after *: %s" msgstr "E215: Ongeldige karakter na *: %s" -#: ../fileio.c:5905 #, c-format msgid "E216: No such event: %s" msgstr "E216: Geen sodanige gebeurtenis nie: %s" -#: ../fileio.c:5907 #, c-format msgid "E216: No such group or event: %s" msgstr "E216: Geen sodanige groep of gebeurtenis nie: %s" #. Highlight title -#: ../fileio.c:6090 msgid "" "\n" "--- Auto-Commands ---" @@ -2510,108 +2367,88 @@ msgstr "" "\n" "--- Outobevele ---" -#: ../fileio.c:6293 #, fuzzy, c-format -msgid "E680: <buffer=%d>: invalid buffer number " -msgstr "ongeldige buffernommer" +#~ msgid "E680: <buffer=%d>: invalid buffer number " +#~ msgstr "ongeldige buffernommer" -#: ../fileio.c:6370 msgid "E217: Can't execute autocommands for ALL events" msgstr "E217: Kan nie outobevele uitvoer vir 'ALL' gebeurtenisse nie" -#: ../fileio.c:6393 msgid "No matching autocommands" msgstr "Geen passende outobevele nie" -#: ../fileio.c:6831 msgid "E218: autocommand nesting too deep" msgstr "E218: outobevele te diep genes" -#: ../fileio.c:7143 #, c-format msgid "%s Auto commands for \"%s\"" msgstr "%s outobevele vir \"%s\"" -#: ../fileio.c:7149 #, c-format msgid "Executing %s" msgstr "Voer %s uit" -#: ../fileio.c:7211 #, c-format msgid "autocommand %s" msgstr "outobevel %s" -#: ../fileio.c:7795 msgid "E219: Missing {." msgstr "E219: Ontbrekende {." -#: ../fileio.c:7797 msgid "E220: Missing }." msgstr "E220: Ontbrekende }." -#: ../fold.c:93 msgid "E490: No fold found" msgstr "E490: Geen vou gevind nie" -#: ../fold.c:544 msgid "E350: Cannot create fold with current 'foldmethod'" msgstr "E350: Kan nie vou skep met huidige 'foldmethod' nie" -#: ../fold.c:546 msgid "E351: Cannot delete fold with current 'foldmethod'" msgstr "E351: Kan nie vou skrap met huidige 'foldmethod' nie" -#: ../fold.c:1784 -#, c-format -msgid "+--%3ld lines folded " -msgstr "+--%3ld rels gevou " +#, fuzzy, c-format +#~ msgid "+--%3ld line folded" +#~ msgid_plural "+--%3ld lines folded " +#~ msgstr[0] "+--%3ld rels gevou " +#~ msgstr[1] "+--%3ld rels gevou " #. buffer has already been read -#: ../getchar.c:273 msgid "E222: Add to read buffer" msgstr "E222: Voeg by leesbuffer" -#: ../getchar.c:2040 msgid "E223: recursive mapping" msgstr "E223: rekursiewe binding" -#: ../getchar.c:2849 #, c-format msgid "E224: global abbreviation already exists for %s" msgstr "E224: globale afkorting bestaan alreeds vir %s" -#: ../getchar.c:2852 #, c-format msgid "E225: global mapping already exists for %s" msgstr "E225: globale binding bestaan alreeds vir %s" -#: ../getchar.c:2952 #, c-format msgid "E226: abbreviation already exists for %s" msgstr "E226: afkorting bestaan already vir %s" -#: ../getchar.c:2955 #, c-format msgid "E227: mapping already exists for %s" msgstr "E227: binding bestaan alreeds vir %s" -#: ../getchar.c:3008 msgid "No abbreviation found" msgstr "Geen afkorting gevind nie" -#: ../getchar.c:3010 msgid "No mapping found" msgstr "Geen binding gevind nie" -#: ../getchar.c:3974 msgid "E228: makemap: Illegal mode" msgstr "E228: makemap: Ongeldige modus" -#. key value of 'cedit' option -#. type of cmdline window or 0 -#. result of cmdline window or 0 -#: ../globals.h:924 +#. /< key value of 'cedit' option +#. /< type of cmdline window or 0 +#. /< result of cmdline window or 0 +#. /< cmdline recursion level msgid "--No lines in buffer--" msgstr "--Geen rels in buffer--" @@ -2619,691 +2456,566 @@ msgstr "--Geen rels in buffer--" #. * The error messages that can be shared are included here. #. * Excluded are errors that are only used once and debugging messages. #. -#: ../globals.h:996 msgid "E470: Command aborted" msgstr "E470: Bevel gekanselleer" -#: ../globals.h:997 +#, fuzzy +#~ msgid "E905: Cannot set this option after startup" +#~ msgstr "E529: Kan nie 'term' stel na le string nie" + +#, fuzzy +#~ msgid "E903: Could not spawn API job" +#~ msgstr "E623: Kon nie 'cscope' proses skep nie" + msgid "E471: Argument required" msgstr "E471: Parameter benodig" -#: ../globals.h:998 msgid "E10: \\ should be followed by /, ? or &" msgstr "E10: \\ moet gevolg word deur /, ? of &" -#: ../globals.h:1000 msgid "E11: Invalid in command-line window; <CR> executes, CTRL-C quits" msgstr "E11: Ongeldig in bevelrel venster: <CR> voer uit, CTRL-C stop" -#: ../globals.h:1002 msgid "E12: Command not allowed from exrc/vimrc in current dir or tag search" msgstr "" "E12: Bevel uit exrc/vimrc nie toegelaat in huidige gids- of etiketsoektog nie" -#: ../globals.h:1003 msgid "E171: Missing :endif" msgstr "E171: Ontbrekende ':endif'" -#: ../globals.h:1004 msgid "E600: Missing :endtry" msgstr "E600: Ontbrekende ':endtry'" -#: ../globals.h:1005 msgid "E170: Missing :endwhile" msgstr "E170: Ontbrekende ':endwhile'" -#: ../globals.h:1006 #, fuzzy -msgid "E170: Missing :endfor" -msgstr "E171: Ontbrekende ':endif'" +#~ msgid "E170: Missing :endfor" +#~ msgstr "E171: Ontbrekende ':endif'" -#: ../globals.h:1007 msgid "E588: :endwhile without :while" msgstr "E588: ':endwhile' sonder ':while'" -#: ../globals.h:1008 #, fuzzy -msgid "E588: :endfor without :for" -msgstr "E580: ':endif' sonder ':if'" +#~ msgid "E588: :endfor without :for" +#~ msgstr "E580: ':endif' sonder ':if'" -#: ../globals.h:1009 msgid "E13: File exists (add ! to override)" msgstr "E13: Ler bestaan (gebruik ! om te dwing)" -#: ../globals.h:1010 msgid "E472: Command failed" msgstr "E472: Bevel het gefaal" -#: ../globals.h:1011 msgid "E473: Internal error" msgstr "E473: Interne fout" -#: ../globals.h:1012 msgid "Interrupted" msgstr "Onderbreek" -#: ../globals.h:1013 msgid "E14: Invalid address" msgstr "E14: Ongeldige adres" -#: ../globals.h:1014 msgid "E474: Invalid argument" msgstr "E474: Ongeldige parameter" -#: ../globals.h:1015 #, c-format msgid "E475: Invalid argument: %s" msgstr "E475: Ongeldige parameter: %s" -#: ../globals.h:1016 #, c-format msgid "E15: Invalid expression: %s" msgstr "E15: Ongeldige uitdrukking: %s" -#: ../globals.h:1017 msgid "E16: Invalid range" msgstr "E16: Ongeldige omvang" -#: ../globals.h:1018 msgid "E476: Invalid command" msgstr "E476: Ongeldige bevel" -#: ../globals.h:1019 #, c-format msgid "E17: \"%s\" is a directory" msgstr "E17: \"%s\" is 'n gids" -#: ../globals.h:1020 #, fuzzy -msgid "E900: Invalid job id" -msgstr "E49: Ongeldige rolgrootte" +#~ msgid "E900: Invalid job id" +#~ msgstr "E49: Ongeldige rolgrootte" -#: ../globals.h:1021 -msgid "E901: Job table is full" -msgstr "" +#~ msgid "E901: Job table is full" +#~ msgstr "" + +#, c-format +#~ msgid "E903: Process failed to start: %s: \"%s\"" +#~ msgstr "" + +#~ msgid "E904: Job is not connected to a pty" +#~ msgstr "" -#: ../globals.h:1024 #, c-format msgid "E364: Library call failed for \"%s()\"" msgstr "E364: Biblioteekroep het gefaal vir \"%s\"()" -#: ../globals.h:1026 +#, fuzzy, c-format +#~ msgid "E739: Cannot create directory %s: %s" +#~ msgstr "Kan nie gids uitvoer nie: \"%s\"" + msgid "E19: Mark has invalid line number" msgstr "E19: Merker het ongeldige relnommer" -#: ../globals.h:1027 msgid "E20: Mark not set" msgstr "E20: Merker nie gestel nie" -#: ../globals.h:1029 msgid "E21: Cannot make changes, 'modifiable' is off" msgstr "E21: Kan nie wysig nie, 'modifiable' is af" -#: ../globals.h:1030 msgid "E22: Scripts nested too deep" msgstr "E22: Skripte te diep ge-nes" -#: ../globals.h:1031 msgid "E23: No alternate file" msgstr "E23: Geen alternatiewe ler nie" -#: ../globals.h:1032 msgid "E24: No such abbreviation" msgstr "E24: Afkorting bestaan nie" -#: ../globals.h:1033 msgid "E477: No ! allowed" msgstr "E477: Geen ! toegelaat nie" -#: ../globals.h:1035 msgid "E25: Nvim does not have a built-in GUI" msgstr "E25: GUI kan nie gebruik word nie: Nie tydens kompilering gekies nie" -#: ../globals.h:1036 #, c-format msgid "E28: No such highlight group name: %s" msgstr "E28: Geen sodanige uitliggroepnaam nie: %s" -#: ../globals.h:1037 msgid "E29: No inserted text yet" msgstr "E29: Nog geen ingevoegde teks nie" -#: ../globals.h:1038 msgid "E30: No previous command line" msgstr "E30: Geen vorige bevelrel nie" -#: ../globals.h:1039 msgid "E31: No such mapping" msgstr "E31: Geen so 'n binding nie" -#: ../globals.h:1040 msgid "E479: No match" msgstr "E479: Geen treffer nie" -#: ../globals.h:1041 #, c-format msgid "E480: No match: %s" msgstr "E480: Geen treffer: %s" -#: ../globals.h:1042 msgid "E32: No file name" msgstr "E32: Geen lernaam" -#: ../globals.h:1044 msgid "E33: No previous substitute regular expression" msgstr "E33: Geen vorige vervangingspatroon nie" -#: ../globals.h:1045 msgid "E34: No previous command" msgstr "E34: Geen vorige bevel nie" -#: ../globals.h:1046 msgid "E35: No previous regular expression" msgstr "E35: Geen vorige patroon nie" -#: ../globals.h:1047 msgid "E481: No range allowed" msgstr "E481: Geen omvang toegelaat nie" -#: ../globals.h:1048 msgid "E36: Not enough room" msgstr "E36: Te min plek" -#: ../globals.h:1049 -#, c-format -msgid "E482: Can't create file %s" -msgstr "E482: Kan nie ler %s skep nie" - -#: ../globals.h:1050 msgid "E483: Can't get temp file name" msgstr "E483: Kan nie tydelike lernaam kry nie" -#: ../globals.h:1051 #, c-format msgid "E484: Can't open file %s" msgstr "E484: Kan nie ler %s oopmaak nie" -#: ../globals.h:1052 #, c-format msgid "E485: Can't read file %s" msgstr "E485: Kan nie ler %s lees nie" -#: ../globals.h:1054 msgid "E37: No write since last change (add ! to override)" msgstr "E37: Ongeskryf sedert vorige verandering (gebruik ! om te dwing)" -#: ../globals.h:1055 #, fuzzy -msgid "E37: No write since last change" -msgstr "[Ongestoor sedert vorige verandering]\n" +#~ msgid "E37: No write since last change" +#~ msgstr "[Ongestoor sedert vorige verandering]\n" -#: ../globals.h:1056 msgid "E38: Null argument" msgstr "E38: Nul parameter" -#: ../globals.h:1057 msgid "E39: Number expected" msgstr "E39: Nommer verwag" -#: ../globals.h:1058 #, c-format msgid "E40: Can't open errorfile %s" msgstr "E40: Kan nie foutler %s oopmaak nie" -#: ../globals.h:1059 msgid "E41: Out of memory!" msgstr "E41: Geheue op!" -#: ../globals.h:1060 msgid "Pattern not found" msgstr "Patroon nie gevind nie" -#: ../globals.h:1061 #, c-format msgid "E486: Pattern not found: %s" msgstr "E486: Patroon nie gevind nie: %s" -#: ../globals.h:1062 msgid "E487: Argument must be positive" msgstr "E487: Parameter moet positief wees" -#: ../globals.h:1064 msgid "E459: Cannot go back to previous directory" msgstr "E459: Kan nie terug gaan na die vorige gids nie" -#: ../globals.h:1066 msgid "E42: No Errors" msgstr "E42: Geen Foute" -#: ../globals.h:1067 -msgid "E776: No location list" -msgstr "" +#~ msgid "E776: No location list" +#~ msgstr "" -#: ../globals.h:1068 msgid "E43: Damaged match string" msgstr "E43: Beskadige trefferstring" -#: ../globals.h:1069 msgid "E44: Corrupted regexp program" msgstr "E44: Korrupte patroonprogram" -#: ../globals.h:1071 msgid "E45: 'readonly' option is set (add ! to override)" msgstr "E45: 'readonly' opsie is aan (gebruik ! om te dwing)" -#: ../globals.h:1073 -#, fuzzy, c-format -msgid "E46: Cannot change read-only variable \"%s\"" -msgstr "E46: Kan nie lees-alleen veranderlike stel nie \"%s\"" - -#: ../globals.h:1075 -#, fuzzy, c-format -msgid "E794: Cannot set variable in the sandbox: \"%s\"" -msgstr "E46: Kan nie lees-alleen veranderlike stel nie \"%s\"" - -#: ../globals.h:1076 msgid "E47: Error while reading errorfile" msgstr "E47: Fout tydens lees van 'errorfile'" -#: ../globals.h:1078 msgid "E48: Not allowed in sandbox" msgstr "E48: Nie toegelaat in sandput nie" -#: ../globals.h:1080 msgid "E523: Not allowed here" msgstr "E523: Nie hier toegelaat nie" -#: ../globals.h:1082 msgid "E359: Screen mode setting not supported" msgstr "E359: Skermmodus instelling nie ondersteun nie" -#: ../globals.h:1083 msgid "E49: Invalid scroll size" msgstr "E49: Ongeldige rolgrootte" -#: ../globals.h:1084 msgid "E91: 'shell' option is empty" msgstr "E91: 'shell' (dop) opsie is leeg" -#: ../globals.h:1085 msgid "E255: Couldn't read in sign data!" msgstr "E255: Fout -- kon nie tekendata lees nie!" -#: ../globals.h:1086 msgid "E72: Close error on swap file" msgstr "E72: Sluitfout met ruiller" -#: ../globals.h:1087 msgid "E73: tag stack empty" msgstr "E73: etiketstapel leeg" -#: ../globals.h:1088 msgid "E74: Command too complex" msgstr "E74: Bevel te kompleks" -#: ../globals.h:1089 msgid "E75: Name too long" msgstr "E75: Naam te lank" -#: ../globals.h:1090 msgid "E76: Too many [" msgstr "E76: Te veel [" -#: ../globals.h:1091 msgid "E77: Too many file names" msgstr "E77: Te veel lername" -#: ../globals.h:1092 msgid "E488: Trailing characters" msgstr "E488: Oorbodige karakters" -#: ../globals.h:1093 +#, fuzzy, c-format +#~ msgid "E488: Trailing characters: %s" +#~ msgstr "E488: Oorbodige karakters" + msgid "E78: Unknown mark" msgstr "E78: Onbekende merker" -#: ../globals.h:1094 msgid "E79: Cannot expand wildcards" msgstr "E79: Kan nie plekhouers uitbrei nie" -#: ../globals.h:1096 msgid "E591: 'winheight' cannot be smaller than 'winminheight'" msgstr "E591: 'winheight' kan nie kleiner as 'winminheight' wees nie" -#: ../globals.h:1098 msgid "E592: 'winwidth' cannot be smaller than 'winminwidth'" msgstr "E592: 'winwidth' kan nie kleiner as 'winminwidth' wees nie" -#: ../globals.h:1099 msgid "E80: Error while writing" msgstr "E80: Fout tydens skryfoperasie" -#: ../globals.h:1100 -msgid "Zero count" -msgstr "Nul telling" +#, fuzzy +#~ msgid "E939: Positive count required" +#~ msgstr "E397: Lernaam benodig" -#: ../globals.h:1101 msgid "E81: Using <SID> not in a script context" msgstr "E81: Gebruik van '<SID>' buite skripkonteks" -#: ../globals.h:1102 #, fuzzy, c-format -msgid "E685: Internal error: %s" -msgstr "E473: Interne fout" +#~ msgid "E685: Internal error: %s" +#~ msgstr "E473: Interne fout" -#: ../globals.h:1104 -msgid "E363: pattern uses more memory than 'maxmempattern'" -msgstr "" +#~ msgid "E363: pattern uses more memory than 'maxmempattern'" +#~ msgstr "" -#: ../globals.h:1105 #, fuzzy -msgid "E749: empty buffer" -msgstr "E279: Nie 'n SNiFF+ buffer nie" +#~ msgid "E749: empty buffer" +#~ msgstr "E279: Nie 'n SNiFF+ buffer nie" + +#, c-format +msgid "E86: Buffer %<PRId64> does not exist" +msgstr "E86: Buffer %<PRId64> bestaan nie" -#: ../globals.h:1108 #, fuzzy -msgid "E682: Invalid search pattern or delimiter" -msgstr "E383: Ongeldige soekstring: %s" +#~ msgid "E682: Invalid search pattern or delimiter" +#~ msgstr "E383: Ongeldige soekstring: %s" -#: ../globals.h:1109 msgid "E139: File is loaded in another buffer" msgstr "E139: Ler is gelaai in ander buffer" -#: ../globals.h:1110 #, fuzzy, c-format -msgid "E764: Option '%s' is not set" -msgstr "E236: Font \"%s\" is nie 'n vaste-wydte font nie" +#~ msgid "E764: Option '%s' is not set" +#~ msgstr "E236: Font \"%s\" is nie 'n vaste-wydte font nie" -#: ../globals.h:1111 #, fuzzy -msgid "E850: Invalid register name" -msgstr "E354: Ongeldige registernaam: '%s'" +#~ msgid "E850: Invalid register name" +#~ msgstr "E354: Ongeldige registernaam: '%s'" + +#, fuzzy, c-format +#~ msgid "E919: Directory not found in '%s': \"%s\"" +#~ msgstr "E161: Inspeksiepunt kon nie gevind word nie: %s" + +msgid "E519: Option not supported" +msgstr "E519: Opsie is nie ondersteun nie" + +#, fuzzy +#~ msgid "E856: Filename too long" +#~ msgstr "E75: Naam te lank" + +#~ msgid "E806: using Float as a String" +#~ msgstr "" -#: ../globals.h:1114 msgid "search hit TOP, continuing at BOTTOM" msgstr "soektog het BO getref, gaan voort van ONDER af" -#: ../globals.h:1115 msgid "search hit BOTTOM, continuing at TOP" msgstr "soektog het ONDER getref, gaan voort van BO af" -#: ../hardcopy.c:240 msgid "E550: Missing colon" msgstr "E550: Ontbrekende dubbelpunt" -#: ../hardcopy.c:252 msgid "E551: Illegal component" msgstr "E551: Ongeldige komponent" -#: ../hardcopy.c:259 msgid "E552: digit expected" msgstr "E552: syfer verwag" -#: ../hardcopy.c:473 #, c-format msgid "Page %d" msgstr "Bladsy %d" -#: ../hardcopy.c:597 msgid "No text to be printed" msgstr "Geen teks om te druk nie" -#: ../hardcopy.c:668 -#, c-format -msgid "Printing page %d (%d%%)" -msgstr "Druk nou bladsy %d (%d%%)" +#, fuzzy, c-format +#~ msgid "Printing page %d (%zu%%)" +#~ msgstr "Druk nou bladsy %d (%d%%)" -#: ../hardcopy.c:680 #, c-format msgid " Copy %d of %d" msgstr " Kopie %d van %d" -#: ../hardcopy.c:733 #, c-format msgid "Printed: %s" msgstr "Gedruk: %s" -#: ../hardcopy.c:740 msgid "Printing aborted" msgstr "Drukkery gestaak" -#: ../hardcopy.c:1365 msgid "E455: Error writing to PostScript output file" msgstr "E455: Kan nie na 'PostScript' afvoerler skryf nie" -#: ../hardcopy.c:1747 #, c-format msgid "E624: Can't open file \"%s\"" msgstr "E624: Kan nie ler \"%s\" oopmaak nie" -#: ../hardcopy.c:1756 ../hardcopy.c:2470 #, c-format msgid "E457: Can't read PostScript resource file \"%s\"" msgstr "E457: Kan nie 'PostScript' hulpbron-ler \"%s\" lees nie" -#: ../hardcopy.c:1772 #, c-format msgid "E618: file \"%s\" is not a PostScript resource file" msgstr "E618: Ler \"%s\" is nie 'n 'PostScript' hulpbron-ler nie" -#: ../hardcopy.c:1788 ../hardcopy.c:1805 ../hardcopy.c:1844 #, c-format msgid "E619: file \"%s\" is not a supported PostScript resource file" msgstr "" "E619: Ler \"%s\" is nie 'n ondersteunde 'PostScript' hulpbron-ler nie" -#: ../hardcopy.c:1856 #, c-format msgid "E621: \"%s\" resource file has wrong version" msgstr "E621: \"%s\" die hulpbron ler het die verkeerde weergawe" -#: ../hardcopy.c:2225 -msgid "E673: Incompatible multi-byte encoding and character set." -msgstr "" +#~ msgid "E673: Incompatible multi-byte encoding and character set." +#~ msgstr "" -#: ../hardcopy.c:2238 -msgid "E674: printmbcharset cannot be empty with multi-byte encoding." -msgstr "" +#~ msgid "E674: printmbcharset cannot be empty with multi-byte encoding." +#~ msgstr "" -#: ../hardcopy.c:2254 -msgid "E675: No default font specified for multi-byte printing." -msgstr "" +#~ msgid "E675: No default font specified for multi-byte printing." +#~ msgstr "" -#: ../hardcopy.c:2426 msgid "E324: Can't open PostScript output file" msgstr "E324: Kan nie 'PostScript' afvoerler oopmaak nie" -#: ../hardcopy.c:2458 #, c-format msgid "E456: Can't open file \"%s\"" msgstr "E456: Kan nie ler %s oopmaak nie" -#: ../hardcopy.c:2583 msgid "E456: Can't find PostScript resource file \"prolog.ps\"" msgstr "E456: Kan nie 'PostScript' hulpbron-ler \"prolog.ps\" lees nie" -#: ../hardcopy.c:2593 #, fuzzy -msgid "E456: Can't find PostScript resource file \"cidfont.ps\"" -msgstr "E456: Kan nie 'PostScript' hulpbron-ler \"%s\" vind nie" +#~ msgid "E456: Can't find PostScript resource file \"cidfont.ps\"" +#~ msgstr "E456: Kan nie 'PostScript' hulpbron-ler \"%s\" vind nie" -#: ../hardcopy.c:2622 ../hardcopy.c:2639 ../hardcopy.c:2665 #, c-format msgid "E456: Can't find PostScript resource file \"%s.ps\"" msgstr "E456: Kan nie 'PostScript' hulpbron-ler \"%s\" vind nie" -#: ../hardcopy.c:2654 #, fuzzy, c-format -msgid "E620: Unable to convert to print encoding \"%s\"" -msgstr "E620: Kon nie van wye-greep na \"%s\" enkodering verander nie" +#~ msgid "E620: Unable to convert to print encoding \"%s\"" +#~ msgstr "E620: Kon nie van wye-greep na \"%s\" enkodering verander nie" -#: ../hardcopy.c:2877 msgid "Sending to printer..." msgstr "Besig om te stuur na drukker..." -#: ../hardcopy.c:2881 msgid "E365: Failed to print PostScript file" msgstr "E365: Kon nie 'PostScript' ler druk nie" -#: ../hardcopy.c:2883 msgid "Print job sent." msgstr "Druktaak gestuur." -#: ../if_cscope.c:85 msgid "Add a new database" msgstr "Voeg 'n nuwe databasis by" -#: ../if_cscope.c:87 msgid "Query for a pattern" msgstr "Soek vir 'n patroon" -#: ../if_cscope.c:89 msgid "Show this message" msgstr "Wys hierdie boodskap" -#: ../if_cscope.c:91 msgid "Kill a connection" msgstr "Sluit 'n verbinding" -#: ../if_cscope.c:93 msgid "Reinit all connections" msgstr "Herstel alle verbindings" -#: ../if_cscope.c:95 msgid "Show connections" msgstr "Wys verbindings" -#: ../if_cscope.c:101 #, c-format msgid "E560: Usage: cs[cope] %s" msgstr "E560: Gebruik: cs[cope] %s" -#: ../if_cscope.c:225 msgid "This cscope command does not support splitting the window.\n" msgstr "" "Hierdie 'cscope' bevel ondersteun nie die splitsing van die venster nie.\n" -#: ../if_cscope.c:266 msgid "E562: Usage: cstag <ident>" msgstr "E562: Gebruik: 'cstag <ident>'" -#: ../if_cscope.c:313 msgid "E257: cstag: tag not found" msgstr "E257: 'cstag': etiket nie gevind nie" -#: ../if_cscope.c:461 #, c-format msgid "E563: stat(%s) error: %d" msgstr "E563: 'stat(%s)' fout: %d" -#: ../if_cscope.c:551 #, c-format msgid "E564: %s is not a directory or a valid cscope database" msgstr "E564: %s is nie 'n gids of 'n geldige 'cscope' databasis nie" -#: ../if_cscope.c:566 #, c-format msgid "Added cscope database %s" msgstr "'cscope' databasis %s bygevoeg" -#: ../if_cscope.c:616 -#, c-format -msgid "E262: error reading cscope connection %<PRId64>" -msgstr "E262: 'cscope' verbinding %<PRId64> kon nie gelees word nie" +#, fuzzy, c-format +#~ msgid "E262: error reading cscope connection %<PRIu64>" +#~ msgstr "E262: 'cscope' verbinding %<PRId64> kon nie gelees word nie" -#: ../if_cscope.c:711 msgid "E561: unknown cscope search type" msgstr "E561: onbekende 'cscope' soektipe" -#: ../if_cscope.c:752 ../if_cscope.c:789 msgid "E566: Could not create cscope pipes" msgstr "E566: Kon nie 'cscope' pype skep nie" -#: ../if_cscope.c:767 msgid "E622: Could not fork for cscope" msgstr "E622: Kon nie vurk vir 'cscope' nie" -#: ../if_cscope.c:849 #, fuzzy -msgid "cs_create_connection setpgid failed" -msgstr "'cs_create_connection' uitvoering het misluk" +#~ msgid "cs_create_connection setpgid failed" +#~ msgstr "'cs_create_connection' uitvoering het misluk" -#: ../if_cscope.c:853 ../if_cscope.c:889 msgid "cs_create_connection exec failed" msgstr "'cs_create_connection' uitvoering het misluk" -#: ../if_cscope.c:863 ../if_cscope.c:902 msgid "cs_create_connection: fdopen for to_fp failed" msgstr "'cs_create_connection': 'fdopen' vir 'to_fp' het misluk" -#: ../if_cscope.c:865 ../if_cscope.c:906 msgid "cs_create_connection: fdopen for fr_fp failed" msgstr "'cs_create_connection': 'fdopen' vir 'fr_fp' het misluk" -#: ../if_cscope.c:890 msgid "E623: Could not spawn cscope process" msgstr "E623: Kon nie 'cscope' proses skep nie" -#: ../if_cscope.c:932 msgid "E567: no cscope connections" msgstr "E567: geen 'cscope' verbindings nie" -#: ../if_cscope.c:1009 #, c-format msgid "E469: invalid cscopequickfix flag %c for %c" msgstr "E469: ongeldige 'cscopequickfix' vlag %c vir %c" -#: ../if_cscope.c:1058 #, c-format msgid "E259: no matches found for cscope query %s of %s" msgstr "E259: geen treffers gevind vir 'cscope' versoek %s van %s nie" -#: ../if_cscope.c:1142 msgid "cscope commands:\n" msgstr "'cscope' bevele:\n" -#: ../if_cscope.c:1150 #, fuzzy, c-format -msgid "%-5s: %s%*s (Usage: %s)" -msgstr "%-5s: %-30s: (Gebruik: %s)" +#~ msgid "%-5s: %s%*s (Usage: %s)" +#~ msgstr "%-5s: %-30s: (Gebruik: %s)" -#: ../if_cscope.c:1155 -msgid "" -"\n" -" c: Find functions calling this function\n" -" d: Find functions called by this function\n" -" e: Find this egrep pattern\n" -" f: Find this file\n" -" g: Find this definition\n" -" i: Find files #including this file\n" -" s: Find this C symbol\n" -" t: Find this text string\n" -msgstr "" +#~ msgid "" +#~ "\n" +#~ " a: Find assignments to this symbol\n" +#~ " c: Find functions calling this function\n" +#~ " d: Find functions called by this function\n" +#~ " e: Find this egrep pattern\n" +#~ " f: Find this file\n" +#~ " g: Find this definition\n" +#~ " i: Find files #including this file\n" +#~ " s: Find this C symbol\n" +#~ " t: Find this text string\n" +#~ msgstr "" -#: ../if_cscope.c:1226 msgid "E568: duplicate cscope database not added" msgstr "E568: duplikaat 'cscope' databasis nie bygevoeg nie" -#: ../if_cscope.c:1335 #, c-format msgid "E261: cscope connection %s not found" msgstr "E261: 'cscope' verbinding %s nie gevind nie" -#: ../if_cscope.c:1364 #, c-format msgid "cscope connection %s closed" msgstr "'cscope' verbinding %s gesluit" #. should not reach here -#: ../if_cscope.c:1486 msgid "E570: fatal error in cs_manage_matches" msgstr "E570: fatale fout in 'cs_manage_matches'" -#: ../if_cscope.c:1693 #, c-format msgid "Cscope tag: %s" msgstr "Cscope etiket: %s" -#: ../if_cscope.c:1711 +#. Column headers for match number, line number and filename. msgid "" "\n" " # line" @@ -3311,329 +3023,314 @@ msgstr "" "\n" " # rel" -#: ../if_cscope.c:1713 msgid "filename / context / line\n" msgstr "lernaam / konteks / rel\n" -#: ../if_cscope.c:1809 #, c-format msgid "E609: Cscope error: %s" msgstr "E609: Cscope fout: %s" -#: ../if_cscope.c:2053 msgid "All cscope databases reset" msgstr "Alle 'cscope' databasisse herstel" -#: ../if_cscope.c:2123 msgid "no cscope connections\n" msgstr "geen 'cscope' verbindings nie\n" -#: ../if_cscope.c:2126 msgid " # pid database name prepend path\n" msgstr " # pid databasis naam gidsvoorvoegsel\n" -#: ../main.c:144 -#, fuzzy -msgid "Unknown option argument" -msgstr "Onbekende opsie" +#, c-format +#~ msgid "E1502: Lua failed to grow stack to %i" +#~ msgstr "" -#: ../main.c:146 -msgid "Too many edit arguments" -msgstr "Te veel redigeer-parameters" +#~ msgid "" +#~ "E5100: Cannot convert given lua table: table should either have a sequence " +#~ "of positive integer keys or contain only string keys" +#~ msgstr "" + +#~ msgid "E5101: Cannot convert given lua type" +#~ msgstr "" + +#, c-format +#~ msgid "E5102: Lua failed to grow stack to %i" +#~ msgstr "" -#: ../main.c:148 +#, fuzzy, c-format +#~ msgid "E5104: Error while creating lua chunk: %.*s" +#~ msgstr "E47: Fout tydens lees van 'errorfile'" + +#, fuzzy, c-format +#~ msgid "E5105: Error while calling lua chunk: %.*s" +#~ msgstr "E47: Fout tydens lees van 'errorfile'" + +#, fuzzy, c-format +#~ msgid "E5109: Error while creating lua chunk: %.*s" +#~ msgstr "E47: Fout tydens lees van 'errorfile'" + +#, fuzzy, c-format +#~ msgid "E5110: Error while creating lua function: %.*s" +#~ msgstr "E47: Fout tydens lees van 'errorfile'" + +#, fuzzy, c-format +#~ msgid "E5111: Error while calling lua function: %.*s" +#~ msgstr "E47: Fout tydens lees van 'errorfile'" + +#, fuzzy, c-format +#~ msgid "E5112: Error while creating lua chunk: %.*s" +#~ msgstr "E47: Fout tydens lees van 'errorfile'" + +#, fuzzy, c-format +#~ msgid "E5113: Error while calling lua chunk: %.*s" +#~ msgstr "E47: Fout tydens lees van 'errorfile'" + +#, fuzzy, c-format +#~ msgid "E5106: Error while creating vim module: %.*s" +#~ msgstr "E47: Fout tydens lees van 'errorfile'" + +#~ msgid "E970: Failed to initialize lua interpreter" +#~ msgstr "" + +#. stack: vim, error +#, fuzzy, c-format +#~ msgid "E5117: Error while updating package paths: %.*s" +#~ msgstr "E47: Fout tydens lees van 'errorfile'" + +#, fuzzy, c-format +#~ msgid "E5107: Error while creating lua chunk for luaeval(): %.*s" +#~ msgstr "E47: Fout tydens lees van 'errorfile'" + +#, fuzzy, c-format +#~ msgid "E5108: Error while calling lua chunk for luaeval(): %.*s" +#~ msgstr "E47: Fout tydens lees van 'errorfile'" + +#, c-format +#~ msgid "E5114: Error while converting print argument #%i: %.*s" +#~ msgstr "" + +#, fuzzy, c-format +#~ msgid "E5115: Error while loading debug string: %.*s" +#~ msgstr "E47: Fout tydens lees van 'errorfile'" + +#, fuzzy, c-format +#~ msgid "E5116: Error while calling debug string: %.*s" +#~ msgstr "E47: Fout tydens lees van 'errorfile'" + +msgid "cannot save undo information" +msgstr "kan nie herwin-inligting stoor nie" + +#. Error messages msgid "Argument missing after" msgstr "Parameter ontbreek na" -#: ../main.c:150 #, fuzzy -msgid "Garbage after option argument" -msgstr "Gemors na opsie" +#~ msgid "Garbage after option argument" +#~ msgstr "Gemors na opsie" + +#, fuzzy +#~ msgid "Unknown option argument" +#~ msgstr "Onbekende opsie" + +msgid "Too many edit arguments" +msgstr "Te veel redigeer-parameters" -#: ../main.c:152 msgid "Too many \"+command\", \"-c command\" or \"--cmd command\" arguments" msgstr "Te veel \"+command\", \"-c command\" of \"--cmd command\" parameters" -#: ../main.c:154 -msgid "Invalid argument for" -msgstr "Ongeldige parameter vir" - -#: ../main.c:294 -#, c-format -msgid "%d files to edit\n" -msgstr "%d lers om te bewerk\n" +#, fuzzy, c-format +#~ msgid "E5421: Failed to open stdin: %s" +#~ msgstr "E286: Gefaal om invoermetode oop te maak" -#: ../main.c:1342 msgid "Attempt to open script file again: \"" msgstr "Probeer weer om skripler oop te maak: \"" -#: ../main.c:1350 msgid "Cannot open for reading: \"" msgstr "Kan nie oopmaak om te lees nie: \"" -#: ../main.c:1393 msgid "Cannot open for script output: \"" msgstr "Kan nie oopmaak vir skrip-afvoer nie: \"" -#: ../main.c:1622 msgid "Vim: Warning: Output is not to a terminal\n" msgstr "Vim: Waarskuwing: Afvoer gaan nie na 'n terminaal nie\n" -#: ../main.c:1624 msgid "Vim: Warning: Input is not from a terminal\n" msgstr "Vim: Waarskuwing: Invoer kom nie vanaf 'n terminaal nie\n" #. just in case.. -#: ../main.c:1891 msgid "pre-vimrc command line" msgstr "vr-'vimrc' bevelrel" -#: ../main.c:1964 #, c-format msgid "E282: Cannot read from \"%s\"" msgstr "E282: Kan nie lees uit \"%s\" nie" -#: ../main.c:2149 +#, fuzzy msgid "" "\n" -"More info with: \"vim -h\"\n" +"More info with \"" msgstr "" "\n" "Meer inligting met: \"vim -h\"\n" -#: ../main.c:2178 -msgid "[file ..] edit specified file(s)" -msgstr "[ler ..] bewerk ler(s)" - -#: ../main.c:2179 -msgid "- read text from stdin" -msgstr "- lees teks uit 'stdin'" - -#: ../main.c:2180 -msgid "-t tag edit file where tag is defined" -msgstr "-t tag bewerk ler waar etiket gedefinieer is" +#. kill us with CTRL-C here, if you like +#, fuzzy +#~ msgid "Usage:\n" +#~ msgstr "" +#~ "\n" +#~ "\n" +#~ "gebruik:" -#: ../main.c:2181 -msgid "-q [errorfile] edit file with first error" -msgstr "-q [foutler] bewerk ler met eerste fout" +#, fuzzy +#~ msgid " nvim [options] [file ...] Edit file(s)\n" +#~ msgstr "[ler ..] bewerk ler(s)" -#: ../main.c:2187 -msgid "" -"\n" -"\n" -"usage:" -msgstr "" -"\n" -"\n" -"gebruik:" +#, fuzzy +#~ msgid " nvim [options] - Read text from stdin\n" +#~ msgstr "- lees teks uit 'stdin'" -#: ../main.c:2189 -msgid " vim [arguments] " -msgstr " vim [parameters] " +#, fuzzy +#~ msgid " nvim [options] -t <tag> Edit file where tag is defined\n" +#~ msgstr "-t tag bewerk ler waar etiket gedefinieer is" -#: ../main.c:2193 -msgid "" -"\n" -" or:" -msgstr "" -"\n" -" of:" +#, fuzzy +#~ msgid " nvim [options] -q [errorfile] Edit file with first error\n" +#~ msgstr "-q [foutler] bewerk ler met eerste fout" -#: ../main.c:2196 +#, fuzzy msgid "" "\n" -"\n" -"Arguments:\n" +"Options:\n" msgstr "" "\n" -"\n" -"Parameters:\n" - -#: ../main.c:2197 -msgid "--\t\t\tOnly file names after this" -msgstr "--\t\t\tSlegs lername hierna" - -#: ../main.c:2199 -msgid "--literal\t\tDon't expand wildcards" -msgstr "--literal\t\tMoet nie plekhouers uitbrei nie" - -#: ../main.c:2201 -msgid "-v\t\t\tVi mode (like \"vi\")" -msgstr "-v\t\t\tVi modus (soos \"vi\")" - -#: ../main.c:2202 -msgid "-e\t\t\tEx mode (like \"ex\")" -msgstr "-e\t\t\tEx modus (soos \"ex\")" - -#: ../main.c:2203 -msgid "-E\t\t\tImproved Ex mode" -msgstr "" - -#: ../main.c:2204 -msgid "-s\t\t\tSilent (batch) mode (only for \"ex\")" -msgstr "-s\t\t\tStil (bondel) modus (slegs vir \"ex\")" - -#: ../main.c:2205 -msgid "-d\t\t\tDiff mode (like \"vimdiff\")" -msgstr "-d\t\t\tDiff modus (soos \"vimdiff\")" - -#: ../main.c:2206 -msgid "-y\t\t\tEasy mode (like \"evim\", modeless)" -msgstr "-y\t\t\tEasy modus (soos \"evim\", modusloos)" - -#: ../main.c:2207 -msgid "-R\t\t\tReadonly mode (like \"view\")" -msgstr "-R\t\t\tLeesalleen modus (soos \"view\")" - -#: ../main.c:2208 -msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\t\tBeperkte modus (soos \"rvim\")" +"--- Opsies ---" -#: ../main.c:2209 -msgid "-m\t\t\tModifications (writing files) not allowed" -msgstr "-m\t\t\tVeranderings (skryf van lers) nie toegelaat nie" +#, fuzzy +#~ msgid " -- Only file names after this\n" +#~ msgstr "--\t\t\tSlegs lername hierna" -#: ../main.c:2210 -msgid "-M\t\t\tModifications in text not allowed" -msgstr "-M\t\t\tVeranderings aan teks nie toegelaat nie" +#, fuzzy +#~ msgid " + Start at end of file\n" +#~ msgstr " vir twee modusse " -#: ../main.c:2211 -msgid "-b\t\t\tBinary mode" -msgstr "-b\t\t\tBinre modus" +#, fuzzy +#~ msgid " --cmd <cmd> Execute <cmd> before any config\n" +#~ msgstr "--cmd <bevel>\tVoer <bevel> uit voor enige .vimrc-ler gelaai word" -#: ../main.c:2212 -msgid "-l\t\t\tLisp mode" -msgstr "-l\t\t\tLisp modus" +#, fuzzy +#~ msgid " +<cmd>, -c <cmd> Execute <cmd> after config and first file\n" +#~ msgstr "-c <bevel>\t\tVoer <bevel> uit na eerste ler gelaai is" -#: ../main.c:2213 -msgid "-C\t\t\tCompatible with Vi: 'compatible'" -msgstr "-C\t\t\tVersoenbaar met Vi: 'compatible'" +#, fuzzy +#~ msgid " -b Binary mode\n" +#~ msgstr " vir twee modusse " -#: ../main.c:2214 -msgid "-N\t\t\tNot fully Vi compatible: 'nocompatible'" -msgstr "-N\t\t\tNie ten volle Vi-versoenbaar nie: 'nocompatible'" +#, fuzzy +#~ msgid " -d Diff mode\n" +#~ msgstr " vir twee modusse " -#: ../main.c:2215 -msgid "-V[N][fname]\t\tBe verbose [level N] [log messages to fname]" -msgstr "" +#~ msgid " -e, -E Ex mode, Improved Ex mode\n" +#~ msgstr "" -#: ../main.c:2216 -msgid "-D\t\t\tDebugging mode" -msgstr "-D\t\t\tOntfoutmodus" +#, fuzzy +#~ msgid " -es Silent (batch) mode\n" +#~ msgstr " vir twee modusse " -#: ../main.c:2217 -msgid "-n\t\t\tNo swap file, use memory only" -msgstr "-n\t\t\tGeen ruiller, gebruik slegs geheue" +#~ msgid " -h, --help Print this help message\n" +#~ msgstr "" -#: ../main.c:2218 -msgid "-r\t\t\tList swap files and exit" -msgstr "-r\t\t\tLys ruillers en verlaat vim" +#~ msgid " -i <shada> Use this shada file\n" +#~ msgstr "" -#: ../main.c:2219 -msgid "-r (with file name)\tRecover crashed session" -msgstr "-r (met ler naam)\tHerwin ineengestorte sessie" +#, fuzzy +#~ msgid " -m Modifications (writing files) not allowed\n" +#~ msgstr "-m\t\t\tVeranderings (skryf van lers) nie toegelaat nie" -#: ../main.c:2220 -msgid "-L\t\t\tSame as -r" -msgstr "-L\t\t\tSelfde as -r" +#, fuzzy +#~ msgid " -M Modifications in text not allowed\n" +#~ msgstr "-M\t\t\tVeranderings aan teks nie toegelaat nie" -#: ../main.c:2221 -msgid "-A\t\t\tstart in Arabic mode" -msgstr "-A\t\t\tbegin in Arabiese modus" +#, fuzzy +#~ msgid " -n No swap file, use memory only\n" +#~ msgstr "-n\t\t\tGeen ruiller, gebruik slegs geheue" -#: ../main.c:2222 -msgid "-H\t\t\tStart in Hebrew mode" -msgstr "-H\t\t\tBegin in Hebreeuse modus" +#, fuzzy +#~ msgid " -o[N] Open N windows (default: one per file)\n" +#~ msgstr "-o[N]\t\tMaak N vensters oop (verstek: een vir elke ler)" -#: ../main.c:2223 -msgid "-F\t\t\tStart in Farsi mode" -msgstr "-F\t\t\tBegin in Farsi modus" +#, fuzzy +msgid "" +" -O[N] Open N vertical windows (default: one per file)\n" +msgstr "-o[N]\t\tMaak N vensters oop (verstek: een vir elke ler)" -#: ../main.c:2224 -msgid "-T <terminal>\tSet terminal type to <terminal>" -msgstr "-T <terminaal>\tStel terminaaltipe na <terminaal>" +#, fuzzy +#~ msgid " -p[N] Open N tab pages (default: one per file)\n" +#~ msgstr "-o[N]\t\tMaak N vensters oop (verstek: een vir elke ler)" -#: ../main.c:2225 -msgid "-u <vimrc>\t\tUse <vimrc> instead of any .vimrc" -msgstr "-u <vimrc>\t\tGebruik <vimrc> in plaas van enige ander .vimrc" +#~ msgid " -r, -L List swap files\n" +#~ msgstr "" -#: ../main.c:2226 -msgid "--noplugin\t\tDon't load plugin scripts" -msgstr "--noplugin\t\tMoet nie inpropskripte laai nie" +#~ msgid " -r <file> Recover edit state for this file\n" +#~ msgstr "" -#: ../main.c:2227 #, fuzzy -msgid "-p[N]\t\tOpen N tab pages (default: one for each file)" -msgstr "-o[N]\t\tMaak N vensters oop (verstek: een vir elke ler)" - -#: ../main.c:2228 -msgid "-o[N]\t\tOpen N windows (default: one for each file)" -msgstr "-o[N]\t\tMaak N vensters oop (verstek: een vir elke ler)" +#~ msgid " -R Read-only mode\n" +#~ msgstr " vir twee modusse " -#: ../main.c:2229 -msgid "-O[N]\t\tLike -o but split vertically" -msgstr "-O[N]\t\tSoos -o maar verdeel vertikaal" +#, fuzzy +#~ msgid " -S <session> Source <session> after loading the first file\n" +#~ msgstr "" +#~ "-S <sessie>\t\tVoer bevele in ler <sessie> uit na eerste ler gelaai is" -#: ../main.c:2230 -msgid "+\t\t\tStart at end of file" -msgstr "+\t\t\tBegin by einde van ler" +#, fuzzy +#~ msgid " -s <scriptin> Read Normal mode commands from <scriptin>\n" +#~ msgstr "-s <skripin>\t\tLees Normale-modus bevele van ler <skripin>" -#: ../main.c:2231 -msgid "+<lnum>\t\tStart at line <lnum>" -msgstr "+<lnum>\t\tBegin by rel <lnum>" +#~ msgid " -u <config> Use this config file\n" +#~ msgstr "" -#: ../main.c:2232 -msgid "--cmd <command>\tExecute <command> before loading any vimrc file" -msgstr "--cmd <bevel>\tVoer <bevel> uit voor enige .vimrc-ler gelaai word" +#, fuzzy +#~ msgid " -v, --version Print version information\n" +#~ msgstr "--version\t\tSkryf weergawe-inligting en sluit" -#: ../main.c:2233 -msgid "-c <command>\t\tExecute <command> after loading the first file" -msgstr "-c <bevel>\t\tVoer <bevel> uit na eerste ler gelaai is" +#~ msgid " -V[N][file] Verbose [level][file]\n" +#~ msgstr "" -#: ../main.c:2235 -msgid "-S <session>\t\tSource file <session> after loading the first file" -msgstr "" -"-S <sessie>\t\tVoer bevele in ler <sessie> uit na eerste ler gelaai is" +#, fuzzy +#~ msgid " -Z Restricted mode\n" +#~ msgstr " vir twee modusse " -#: ../main.c:2236 -msgid "-s <scriptin>\tRead Normal mode commands from file <scriptin>" -msgstr "-s <skripin>\t\tLees Normale-modus bevele van ler <skripin>" +#~ msgid " --api-info Write msgpack-encoded API metadata to stdout\n" +#~ msgstr "" -#: ../main.c:2237 -msgid "-w <scriptout>\tAppend all typed commands to file <scriptout>" -msgstr "-w <skripuit>\tLas alle getikte bevele aan by ler <skripuit>" +#~ msgid " --embed Use stdin/stdout as a msgpack-rpc channel\n" +#~ msgstr "" -#: ../main.c:2238 -msgid "-W <scriptout>\tWrite all typed commands to file <scriptout>" -msgstr "-W <skripuit>\tSkryf alle getikte bevele na ler <skripuit>" +#~ msgid " --headless Don't start a user interface\n" +#~ msgstr "" -#: ../main.c:2240 -msgid "--startuptime <file>\tWrite startup timing messages to <file>" -msgstr "" +#, fuzzy +#~ msgid " --literal Don't expand wildcards\n" +#~ msgstr "--literal\t\tMoet nie plekhouers uitbrei nie" -#: ../main.c:2242 -msgid "-i <viminfo>\t\tUse <viminfo> instead of .viminfo" -msgstr "-i <viminfo>\t\tGebruik <viminfo> in plaas van .viminfo" +#, fuzzy +#~ msgid " --noplugin Don't load plugins\n" +#~ msgstr "--noplugin\t\tMoet nie inpropskripte laai nie" -#: ../main.c:2243 -msgid "-h or --help\tPrint Help (this message) and exit" -msgstr "-h of --help\tSkryf Hulp (hierdie boodskap) en sluit" +#~ msgid " --startuptime <file> Write startup timing messages to <file>\n" +#~ msgstr "" -#: ../main.c:2244 -msgid "--version\t\tPrint version information and exit" -msgstr "--version\t\tSkryf weergawe-inligting en sluit" +#~ msgid "" +#~ "\n" +#~ "See \":help startup-options\" for all options.\n" +#~ msgstr "" -#: ../mark.c:676 msgid "No marks set" msgstr "Geen merkers gestel nie" -#: ../mark.c:678 #, c-format msgid "E283: No marks matching \"%s\"" msgstr "E283: Geen merkers pas op \"%s\" nie" #. Highlight title -#: ../mark.c:687 msgid "" "\n" "mark line col file/text" @@ -3642,7 +3339,6 @@ msgstr "" "merk rel kol ler/teks" #. Highlight title -#: ../mark.c:789 msgid "" "\n" " jump line col file/text" @@ -3651,7 +3347,6 @@ msgstr "" " spring rel kol ler/teks" #. Highlight title -#: ../mark.c:831 msgid "" "\n" "change line col text" @@ -3659,110 +3354,63 @@ msgstr "" "\n" "verander rel kol teks" -#: ../mark.c:1238 -msgid "" -"\n" -"# File marks:\n" -msgstr "" -"\n" -"# Lermerkers:\n" - -#. Write the jumplist with -' -#: ../mark.c:1271 -msgid "" -"\n" -"# Jumplist (newest first):\n" -msgstr "" -"\n" -"# Springlys (nuutste eerste):\n" - -#: ../mark.c:1352 -msgid "" -"\n" -"# History of marks within files (newest to oldest):\n" -msgstr "" -"\n" -"# Geskiedenis van merkers in lers (nuutste tot oudste):\n" - -#: ../mark.c:1431 -msgid "Missing '>'" -msgstr "Ontbrekende '>'" - -#: ../memfile.c:426 msgid "E293: block was not locked" msgstr "E293: blok was nie gesluit nie" -#: ../memfile.c:799 msgid "E294: Seek error in swap file read" msgstr "E294: Soekfout in lees van ruiller" -#: ../memfile.c:803 msgid "E295: Read error in swap file" msgstr "E295: Leesfout in ruiller" -#: ../memfile.c:849 msgid "E296: Seek error in swap file write" msgstr "E296: Soekfout in skryf van ruiller" -#: ../memfile.c:865 msgid "E297: Write error in swap file" msgstr "E297: Skryffout in ruiller" -#: ../memfile.c:1036 msgid "E300: Swap file already exists (symlink attack?)" msgstr "E300: Ruiller bestaan alreeds! ('symlink' probleem?)" -#: ../memline.c:318 msgid "E298: Didn't get block nr 0?" msgstr "E298: Het nie blok no 0 gekry nie?" -#: ../memline.c:361 msgid "E298: Didn't get block nr 1?" msgstr "E298: Het nie blok no 1 gekry nie?" -#: ../memline.c:377 msgid "E298: Didn't get block nr 2?" msgstr "E298: Het nie blok no 2 gekry nie?" #. could not (re)open the swap file, what can we do???? -#: ../memline.c:465 msgid "E301: Oops, lost the swap file!!!" msgstr "E301: Hiert, die ruiller is weg!!!" -#: ../memline.c:477 msgid "E302: Could not rename swap file" msgstr "E302: Kon nie ruiller vernoem nie" -#: ../memline.c:554 #, c-format msgid "E303: Unable to open swap file for \"%s\", recovery impossible" msgstr "E303: Kon nie ruiller oopmaak vir \"%s\" nie, herwinning onmoontlik" -#: ../memline.c:666 #, fuzzy -msgid "E304: ml_upd_block0(): Didn't get block 0??" -msgstr "E304: 'ml_timestamp': Het nie blok 0 gekry nie??" +#~ msgid "E304: ml_upd_block0(): Didn't get block 0??" +#~ msgstr "E304: 'ml_timestamp': Het nie blok 0 gekry nie??" #. no swap files found -#: ../memline.c:830 #, c-format msgid "E305: No swap file found for %s" msgstr "E305: Geen ruiller gevind vir %s nie" -#: ../memline.c:839 msgid "Enter number of swap file to use (0 to quit): " msgstr "Tik die nommer van die ruiller om te gebruik (0 om te stop)" -#: ../memline.c:879 #, c-format msgid "E306: Cannot open %s" msgstr "E306: Kan %s nie oopmaak nie" -#: ../memline.c:897 msgid "Unable to read block 0 from " msgstr "Kan nie blok 0 lees vanaf " -#: ../memline.c:900 msgid "" "\n" "Maybe no changes were made or Vim did not update the swap file." @@ -3770,28 +3418,22 @@ msgstr "" "\n" "Vim het die ruiller nie opgedateer nie. Dalk was niks verander nie." -#: ../memline.c:909 msgid " cannot be used with this version of Vim.\n" msgstr " kan nie gebruik word met hierdie weergawe van Vim nie.\n" -#: ../memline.c:911 msgid "Use Vim version 3.0.\n" msgstr "Gebruik Vim weergawe 3.0.\n" -#: ../memline.c:916 #, c-format msgid "E307: %s does not look like a Vim swap file" msgstr "E307: %s lyk nie soos 'n Vim ruiller nie" -#: ../memline.c:922 msgid " cannot be used on this computer.\n" msgstr " kan nie gebruik word op hierdie rekenaar nie.\n" -#: ../memline.c:924 msgid "The file was created on " msgstr "Die ler is geskep op " -#: ../memline.c:928 msgid "" ",\n" "or the file has been damaged." @@ -3799,85 +3441,66 @@ msgstr "" ",\n" "of die ler is beskadig." -#: ../memline.c:945 -msgid " has been damaged (page size is smaller than minimum value).\n" -msgstr "" +#~ msgid " has been damaged (page size is smaller than minimum value).\n" +#~ msgstr "" -#: ../memline.c:974 #, c-format msgid "Using swap file \"%s\"" msgstr "Gebruik ruiller \"%s\"" -#: ../memline.c:980 #, c-format msgid "Original file \"%s\"" msgstr "Oorspronklike ler \"%s\"" -#: ../memline.c:995 msgid "E308: Warning: Original file may have been changed" msgstr "E308: Waarskuwing: Oorspronklike ler is dalk gewysig" -#: ../memline.c:1061 #, c-format msgid "E309: Unable to read block 1 from %s" msgstr "E309: Kan nie block 1 lees van %s" -#: ../memline.c:1065 msgid "???MANY LINES MISSING" msgstr "???BAIE RELS WEG" -#: ../memline.c:1076 msgid "???LINE COUNT WRONG" msgstr "???RELTELLING FOUTIEF" -#: ../memline.c:1082 msgid "???EMPTY BLOCK" msgstr "???LE BLOK" -#: ../memline.c:1103 msgid "???LINES MISSING" msgstr "???RELS WEG" -#: ../memline.c:1128 #, c-format msgid "E310: Block 1 ID wrong (%s not a .swp file?)" msgstr "E310: Blok 1 se ID is foutief (%s nie 'n .swp ler nie?)" -#: ../memline.c:1133 msgid "???BLOCK MISSING" msgstr "???BLOK WEG" -#: ../memline.c:1147 msgid "??? from here until ???END lines may be messed up" msgstr "??? van hier tot ???END mag rels deurmekaar wees" -#: ../memline.c:1164 msgid "??? from here until ???END lines may have been inserted/deleted" msgstr "??? van hier tot ???END mag daar rels ingevoeg/geskrap wees" -#: ../memline.c:1181 msgid "???END" msgstr "???END" -#: ../memline.c:1238 msgid "E311: Recovery Interrupted" msgstr "E311: Herwinning onderbreek" -#: ../memline.c:1243 msgid "" "E312: Errors detected while recovering; look for lines starting with ???" msgstr "" "E312: Foute raakgesien gedurende herwinning; soek vir rels wat begin met ???" -#: ../memline.c:1245 msgid "See \":help E312\" for more information." msgstr "Sien \":help E312\" vir meer inligting." -#: ../memline.c:1249 msgid "Recovery completed. You should check if everything is OK." msgstr "Herwinning is klaar. Kyk of alles reg is." -#: ../memline.c:1251 msgid "" "\n" "(You might want to write out this file under another name\n" @@ -3885,16 +3508,13 @@ msgstr "" "\n" "(Jy wil dalk die ler stoor onder 'n ander naam\n" -#: ../memline.c:1252 #, fuzzy -msgid "and run diff with the original file to check for changes)" -msgstr "en dit \"diff\" teen die oorspronklike ler om wysigings te soek)\n" +#~ msgid "and run diff with the original file to check for changes)" +#~ msgstr "en dit \"diff\" teen die oorspronklike ler om wysigings te soek)\n" -#: ../memline.c:1254 -msgid "Recovery completed. Buffer contents equals file contents." -msgstr "" +#~ msgid "Recovery completed. Buffer contents equals file contents." +#~ msgstr "" -#: ../memline.c:1255 #, fuzzy msgid "" "\n" @@ -3905,51 +3525,42 @@ msgstr "" "\n" #. use msg() to start the scrolling properly -#: ../memline.c:1327 msgid "Swap files found:" msgstr "Ruillers gevind:" -#: ../memline.c:1446 msgid " In current directory:\n" msgstr " In huidige gids:\n" -#: ../memline.c:1448 msgid " Using specified name:\n" msgstr " Wat gespesifiseerde naam gebruik:\n" -#: ../memline.c:1450 msgid " In directory " msgstr " In gids " -#: ../memline.c:1465 msgid " -- none --\n" msgstr " -- geen --\n" -#: ../memline.c:1527 msgid " owned by: " msgstr " eienaar: " -#: ../memline.c:1529 msgid " dated: " msgstr " gedateer: " -#: ../memline.c:1532 ../memline.c:3231 msgid " dated: " msgstr " gedateer: " -#: ../memline.c:1548 msgid " [from Vim version 3.0]" msgstr " [van Vim weergawe 3.0]" -#: ../memline.c:1550 msgid " [does not look like a Vim swap file]" msgstr " [lyk nie soos 'n Vim ruiller nie]" -#: ../memline.c:1552 +#~ msgid " [garbled strings (not nul terminated)]" +#~ msgstr "" + msgid " file name: " msgstr " lernaam: " -#: ../memline.c:1558 msgid "" "\n" " modified: " @@ -3957,15 +3568,12 @@ msgstr "" "\n" " gewysig: " -#: ../memline.c:1559 msgid "YES" msgstr "JA" -#: ../memline.c:1559 msgid "no" msgstr "nee" -#: ../memline.c:1562 msgid "" "\n" " user name: " @@ -3973,11 +3581,9 @@ msgstr "" "\n" " gebruikersnaam: " -#: ../memline.c:1568 msgid " host name: " msgstr " gasheernaam: " -#: ../memline.c:1570 msgid "" "\n" " host name: " @@ -3985,7 +3591,6 @@ msgstr "" "\n" " gasheernaam: " -#: ../memline.c:1575 msgid "" "\n" " process ID: " @@ -3993,11 +3598,9 @@ msgstr "" "\n" " proses ID: " -#: ../memline.c:1579 msgid " (still running)" msgstr " (nog steeds aan die uitvoer)" -#: ../memline.c:1586 msgid "" "\n" " [not usable on this computer]" @@ -4005,97 +3608,75 @@ msgstr "" "\n" " [nie bruikbaar op hierdie rekenaar nie]" -#: ../memline.c:1590 msgid " [cannot be read]" msgstr " [kan nie gelees word nie]" -#: ../memline.c:1593 msgid " [cannot be opened]" msgstr " [kan nie oopgemaak word nie]" -#: ../memline.c:1698 msgid "E313: Cannot preserve, there is no swap file" msgstr "E313: Kan nie bewaar nie, daar is geen ruiller nie" -#: ../memline.c:1747 msgid "File preserved" msgstr "Ler bewaar" -#: ../memline.c:1749 msgid "E314: Preserve failed" msgstr "E314: Kon nie bewaar nie" -#: ../memline.c:1819 #, c-format msgid "E315: ml_get: invalid lnum: %<PRId64>" msgstr "E315: 'ml_get': ongeldige 'lnum': %<PRId64>" -#: ../memline.c:1851 #, c-format msgid "E316: ml_get: cannot find line %<PRId64>" msgstr "E316: 'ml_get': kan rel %<PRId64> nie vind nie" -#: ../memline.c:2236 msgid "E317: pointer block id wrong 3" msgstr "E317: wyser blok id verkeerd 3" -#: ../memline.c:2311 msgid "stack_idx should be 0" msgstr "'stack_idx' moet 0 wees" -#: ../memline.c:2369 msgid "E318: Updated too many blocks?" msgstr "E318: Te veel blokke opgedateer?" -#: ../memline.c:2511 msgid "E317: pointer block id wrong 4" msgstr "E317: wyser blok id verkeerd 4" -#: ../memline.c:2536 msgid "deleted block 1?" msgstr "verwyder blok 1?" -#: ../memline.c:2707 #, c-format msgid "E320: Cannot find line %<PRId64>" msgstr "E320: Kan nie rel %<PRId64> vind nie" -#: ../memline.c:2916 msgid "E317: pointer block id wrong" msgstr "E317: wyser blok id verkeerd" -#: ../memline.c:2930 msgid "pe_line_count is zero" msgstr "'pe_line_count' is nul" -#: ../memline.c:2955 #, c-format msgid "E322: line number out of range: %<PRId64> past the end" msgstr "E322: relnommer buite perke: %<PRId64> verby die einde" -#: ../memline.c:2959 #, c-format msgid "E323: line count wrong in block %<PRId64>" msgstr "E323: reltelling mag verkeerd wees in blok %<PRId64>" -#: ../memline.c:2999 msgid "Stack size increases" msgstr "Stapel grootte verhoog" -#: ../memline.c:3038 msgid "E317: pointer block id wrong 2" msgstr "E317: wyser blok id verkeerd 2" -#: ../memline.c:3070 #, c-format -msgid "E773: Symlink loop for \"%s\"" -msgstr "" +#~ msgid "E773: Symlink loop for \"%s\"" +#~ msgstr "" -#: ../memline.c:3221 msgid "E325: ATTENTION" msgstr "E325: LET OP" -#: ../memline.c:3222 msgid "" "\n" "Found a swap file by the name \"" @@ -4103,44 +3684,35 @@ msgstr "" "\n" "Het 'n ruiller gevind met die naam \"" -#: ../memline.c:3226 msgid "While opening file \"" msgstr "Tydens oopmaak van ler \"" -#: ../memline.c:3239 msgid " NEWER than swap file!\n" msgstr " NUWER as die ruiller!\n" -#: ../memline.c:3244 +#. Some of these messages are long to allow translation to +#. * other languages. #, fuzzy msgid "" "\n" "(1) Another program may be editing the same file. If this is the case,\n" " be careful not to end up with two different instances of the same\n" -" file when making changes." +" file when making changes. Quit, or continue with caution.\n" msgstr "" "\n" "(1) 'n Ander program mag besig wees met hierdie ler.\n" " Indien wel, pas op om nie met twee verskillende weergawes\n" " van dieselfde ler te sit wanneer veranderinge gemaak word nie.\n" -#: ../memline.c:3245 -#, fuzzy -msgid " Quit, or continue with caution.\n" -msgstr " Stop, of gaan versigtig voort.\n" - -#: ../memline.c:3246 #, fuzzy -msgid "(2) An edit session for this file crashed.\n" -msgstr "" -"\n" -"(2) 'n Bewerkingsessie van hierdie ler het ineengestort.\n" +#~ msgid "(2) An edit session for this file crashed.\n" +#~ msgstr "" +#~ "\n" +#~ "(2) 'n Bewerkingsessie van hierdie ler het ineengestort.\n" -#: ../memline.c:3247 msgid " If this is the case, use \":recover\" or \"vim -r " msgstr " Indien wel, gebruik \":recover\" of \"vim -r" -#: ../memline.c:3249 msgid "" "\"\n" " to recover the changes (see \":help recovery\").\n" @@ -4148,11 +3720,9 @@ msgstr "" "\"\n" " om die veranderinge te herwin (sien \":help recovery\").\n" -#: ../memline.c:3250 msgid " If you did this already, delete the swap file \"" msgstr " Indien jy dit alreeds gedoen het, verwyder die ruiller \"" -#: ../memline.c:3252 msgid "" "\"\n" " to avoid this message.\n" @@ -4160,23 +3730,15 @@ msgstr "" "\"\n" " om hierdie boodskap te vermy.\n" -#: ../memline.c:3450 ../memline.c:3452 msgid "Swap file \"" msgstr "Ruiller \"" -#: ../memline.c:3451 ../memline.c:3455 msgid "\" already exists!" msgstr "\" bestaan alreeds!" -#: ../memline.c:3457 msgid "VIM - ATTENTION" msgstr "VIM - LET OP" -#: ../memline.c:3459 -msgid "Swap file already exists!" -msgstr "Ruiller bestaan alreeds!" - -#: ../memline.c:3464 msgid "" "&Open Read-Only\n" "&Edit anyway\n" @@ -4190,7 +3752,6 @@ msgstr "" "&Verlaat\n" "&Stop" -#: ../memline.c:3467 #, fuzzy msgid "" "&Open Read-Only\n" @@ -4214,48 +3775,47 @@ msgstr "" #. #. ".s?a" #. ".saa": tried enough, give up -#: ../memline.c:3528 msgid "E326: Too many swap files found" msgstr "E326: Te veel ruillers gevind" -#: ../memory.c:227 +#, fuzzy, c-format +msgid "" +"E303: Unable to create directory \"%s\" for swap file, recovery impossible: " +"%s" +msgstr "E303: Kon nie ruiller oopmaak vir \"%s\" nie, herwinning onmoontlik" + +#~ msgid "Vim: Data too large to fit into virtual memory space\n" +#~ msgstr "" + #, c-format msgid "E342: Out of memory! (allocating %<PRIu64> bytes)" msgstr "E342: Geheue is op! (ken %<PRIu64> grepe toe)" -#: ../menu.c:62 msgid "E327: Part of menu-item path is not sub-menu" msgstr "E327: Deel van kieslys-item pad is nie 'n sub-kieslys nie" -#: ../menu.c:63 msgid "E328: Menu only exists in another mode" msgstr "E328: Kieslys bestaan slegs in 'n ander modus" -#: ../menu.c:64 #, fuzzy, c-format -msgid "E329: No menu \"%s\"" -msgstr "E329: Geen kieslys met daardie naam nie" +#~ msgid "E329: No menu \"%s\"" +#~ msgstr "E329: Geen kieslys met daardie naam nie" #. Only a mnemonic or accelerator is not valid. -#: ../menu.c:329 -msgid "E792: Empty menu name" -msgstr "" +#~ msgid "E792: Empty menu name" +#~ msgstr "" -#: ../menu.c:340 msgid "E330: Menu path must not lead to a sub-menu" msgstr "E330: Kieslyspad moenie lei na 'n sub-kieslys nie" -#: ../menu.c:365 msgid "E331: Must not add menu items directly to menu bar" msgstr "E331: Moenie kieslysitems direk by kieslysstaaf voeg nie" -#: ../menu.c:370 msgid "E332: Separator cannot be part of a menu path" msgstr "E332: Verdeler kan nie deel wees van kieslyspad nie" #. Now we have found the matching menu, and we list the mappings #. Highlight title -#: ../menu.c:762 msgid "" "\n" "--- Menus ---" @@ -4263,70 +3823,49 @@ msgstr "" "\n" "--- Kieslyste ---" -#: ../menu.c:1313 msgid "E333: Menu path must lead to a menu item" msgstr "E333: Kieslyspad moet lei na 'n kieslysitem" -#: ../menu.c:1330 #, c-format msgid "E334: Menu not found: %s" msgstr "E334: Kieslys nie gevind nie: %s" -#: ../menu.c:1396 #, c-format msgid "E335: Menu not defined for %s mode" msgstr "E335: Kieslys nie gedefinieer vir %s modus nie" -#: ../menu.c:1426 -msgid "E336: Menu path must lead to a sub-menu" -msgstr "E336: Kieslyspad moet lei na 'n sub-kieslys" - -#: ../menu.c:1447 -msgid "E337: Menu not found - check menu names" -msgstr "E337: Kieslys nie gevind nie - maak seker oor die kieslys name" - -#: ../message.c:423 #, c-format msgid "Error detected while processing %s:" msgstr "Fout ontdek tydens verwerking van %s: " -#: ../message.c:445 #, c-format msgid "line %4ld:" msgstr "rel %4ld:" -#: ../message.c:617 #, c-format msgid "E354: Invalid register name: '%s'" msgstr "E354: Ongeldige registernaam: '%s'" -#: ../message.c:986 msgid "Interrupt: " msgstr "Onderbreek: " -#: ../message.c:988 #, fuzzy -msgid "Press ENTER or type command to continue" -msgstr "Druk ENTER of tik 'n bevel om voort te gaan" +#~ msgid "Press ENTER or type command to continue" +#~ msgstr "Druk ENTER of tik 'n bevel om voort te gaan" -#: ../message.c:1843 #, fuzzy, c-format -msgid "%s line %<PRId64>" -msgstr "%s, rel %<PRId64>" +#~ msgid "%s line %<PRId64>" +#~ msgstr "%s, rel %<PRId64>" -#: ../message.c:2392 msgid "-- More --" msgstr "-- Meer --" -#: ../message.c:2398 -msgid " SPACE/d/j: screen/page/line down, b/u/k: up, q: quit " -msgstr "" +#~ msgid " SPACE/d/j: screen/page/line down, b/u/k: up, q: quit " +#~ msgstr "" -#: ../message.c:3021 ../message.c:3031 msgid "Question" msgstr "Vraag" -#: ../message.c:3023 msgid "" "&Yes\n" "&No" @@ -4334,7 +3873,6 @@ msgstr "" "&Ja\n" "&Nee" -#: ../message.c:3033 msgid "" "&Yes\n" "&No\n" @@ -4344,7 +3882,6 @@ msgstr "" "&Nee\n" "&Kanselleer" -#: ../message.c:3045 msgid "" "&Yes\n" "&No\n" @@ -4358,180 +3895,131 @@ msgstr "" "&Gooi alles weg\n" "&Kanselleer" -#: ../message.c:3058 -#, fuzzy -msgid "E766: Insufficient arguments for printf()" -msgstr "E116: Ongeldige parameters vir funksie %s" - -#: ../message.c:3119 -msgid "E807: Expected Float argument for printf()" -msgstr "" - -#: ../message.c:3873 -#, fuzzy -msgid "E767: Too many arguments to printf()" -msgstr "E118: Te veel parameters vir funksie: %s" - -#: ../misc1.c:2256 msgid "W10: Warning: Changing a readonly file" msgstr "W10: Waarskuwing: Jy wysig aan 'n leesalleen ler" -#: ../misc1.c:2537 -msgid "Type number and <Enter> or click with mouse (empty cancels): " -msgstr "" +#~ msgid "Type number and <Enter> or click with mouse (empty cancels): " +#~ msgstr "" -#: ../misc1.c:2539 -msgid "Type number and <Enter> (empty cancels): " -msgstr "" +#~ msgid "Type number and <Enter> (empty cancels): " +#~ msgstr "" -#: ../misc1.c:2585 msgid "1 more line" msgstr "1 rel meer" -#: ../misc1.c:2588 msgid "1 line less" msgstr "1 rel minder" -#: ../misc1.c:2593 #, c-format msgid "%<PRId64> more lines" msgstr "%<PRId64> meer rels" -#: ../misc1.c:2596 #, c-format msgid "%<PRId64> fewer lines" msgstr "%<PRId64> minder rels" -#: ../misc1.c:2599 msgid " (Interrupted)" msgstr " (Onderbreek)" -#: ../misc1.c:2635 -msgid "Beep!" -msgstr "" +#~ msgid "Beep!" +#~ msgstr "" -#: ../misc2.c:738 #, c-format msgid "Calling shell to execute: \"%s\"" msgstr "Roep dop om uit te voer: \"%s\"" -#: ../normal.c:183 +#. +#. * nv_*(): functions called to handle Normal and Visual mode commands. +#. * n_*(): functions called to handle Normal mode commands. +#. * v_*(): functions called to handle Visual mode commands. +#. msgid "E349: No identifier under cursor" msgstr "E349: Geen identifiseerder onder loper nie" -#: ../normal.c:1866 #, fuzzy -msgid "E774: 'operatorfunc' is empty" -msgstr "E221: 'commentstring' opsie is leeg" +#~ msgid "E774: 'operatorfunc' is empty" +#~ msgstr "E221: 'commentstring' opsie is leeg" -#: ../normal.c:2637 msgid "Warning: terminal cannot highlight" msgstr "Waarskuwing: terminaal kan nie teks uitlig nie" -#: ../normal.c:2807 msgid "E348: No string under cursor" msgstr "E348: Geen string onder loper nie" -#: ../normal.c:3937 msgid "E352: Cannot erase folds with current 'foldmethod'" msgstr "E352: Kan nie voue verwyder met huidige 'foldmethod' nie" -#: ../normal.c:5897 msgid "E664: changelist is empty" msgstr "E664: 'changelist' is leeg" -#: ../normal.c:5899 msgid "E662: At start of changelist" msgstr "E662: By die begin van die veranderingslys" -#: ../normal.c:5901 msgid "E663: At end of changelist" msgstr "E663: By die einde van die veranderingslys" -#: ../normal.c:7053 msgid "Type :quit<Enter> to exit Nvim" msgstr "Tik :quit<Enter> om Vim te verlaat" # Het te doen met < en > -#: ../ops.c:248 #, c-format msgid "1 line %sed 1 time" msgstr "1 rel 1 keer ge-%s" -#: ../ops.c:250 #, c-format msgid "1 line %sed %d times" msgstr "1 rel ge-%s %d keer" -#: ../ops.c:253 #, c-format msgid "%<PRId64> lines %sed 1 time" msgstr "%<PRId64> rels 1 keer ge-%s" -#: ../ops.c:256 #, c-format msgid "%<PRId64> lines %sed %d times" msgstr "%<PRId64> rels ge-%s %d keer" -#: ../ops.c:592 #, c-format msgid "%<PRId64> lines to indent... " msgstr "%<PRId64> rels om in te keep..." -#: ../ops.c:634 msgid "1 line indented " msgstr "1 rel ingekeep " -#: ../ops.c:636 #, c-format msgid "%<PRId64> lines indented " msgstr "%<PRId64> rels ingekeep " -#: ../ops.c:938 #, fuzzy -msgid "E748: No previously used register" -msgstr "E186: Geen vorige gids nie" - -#. must display the prompt -#: ../ops.c:1433 -msgid "cannot yank; delete anyway" -msgstr "kan nie pluk nie: verwyder in elk geval" +#~ msgid "E748: No previously used register" +#~ msgstr "E186: Geen vorige gids nie" -#: ../ops.c:1929 msgid "1 line changed" msgstr "1 rel verander" -#: ../ops.c:1931 #, c-format msgid "%<PRId64> lines changed" msgstr "%<PRId64> rels verander" -#: ../ops.c:2521 #, fuzzy -msgid "block of 1 line yanked" -msgstr "1 rel gepluk" +#~ msgid "block of 1 line yanked" +#~ msgstr "1 rel gepluk" -#: ../ops.c:2523 msgid "1 line yanked" msgstr "1 rel gepluk" -#: ../ops.c:2525 #, fuzzy, c-format -msgid "block of %<PRId64> lines yanked" -msgstr "%<PRId64> rels gepluk" +#~ msgid "block of %<PRId64> lines yanked" +#~ msgstr "%<PRId64> rels gepluk" -#: ../ops.c:2528 #, c-format msgid "%<PRId64> lines yanked" msgstr "%<PRId64> rels gepluk" -#: ../ops.c:2710 #, c-format msgid "E353: Nothing in register %s" msgstr "E353: Niks in register %s nie" #. Highlight title -#: ../ops.c:3185 msgid "" "\n" "--- Registers ---" @@ -4539,29 +4027,15 @@ msgstr "" "\n" "--- Registers ---" -#: ../ops.c:4455 -msgid "Illegal register name" -msgstr "Ongeldige registernaam" - -#: ../ops.c:4533 -msgid "" -"\n" -"# Registers:\n" -msgstr "" -"\n" -"# Registers:\n" - -#: ../ops.c:4575 -#, c-format -msgid "E574: Unknown register type %d" -msgstr "E574: Onbekende registertipe %d" +#~ msgid "" +#~ "E883: search pattern and expression register may not contain two or more " +#~ "lines" +#~ msgstr "" -#: ../ops.c:5089 #, c-format msgid "%<PRId64> Cols; " msgstr "%<PRId64> Kolomme; " -#: ../ops.c:5097 #, c-format msgid "" "Selected %s%<PRId64> of %<PRId64> Lines; %<PRId64> of %<PRId64> Words; " @@ -4570,7 +4044,6 @@ msgstr "" "%s%<PRId64> van %<PRId64> rels gekies; %<PRId64> van %<PRId64> Woorde; " "%<PRId64> van %<PRId64> Grepe" -#: ../ops.c:5105 #, fuzzy, c-format msgid "" "Selected %s%<PRId64> of %<PRId64> Lines; %<PRId64> of %<PRId64> Words; " @@ -4580,7 +4053,6 @@ msgstr "" "%<PRId64> van %<PRId64> Grepe" # njj: Karakters kan meerdere grepe wees, sien ':h multibyte' -#: ../ops.c:5123 #, c-format msgid "" "Col %s of %s; Line %<PRId64> of %<PRId64>; Word %<PRId64> of %<PRId64>; Byte " @@ -4590,7 +4062,6 @@ msgstr "" "Greep %<PRId64> van %<PRId64>" # njj: Karakters kan meerdere grepe wees, sien ':h multibyte' -#: ../ops.c:5133 #, fuzzy, c-format msgid "" "Col %s of %s; Line %<PRId64> of %<PRId64>; Word %<PRId64> of %<PRId64>; Char " @@ -4599,148 +4070,104 @@ msgstr "" "Kol %s van %s; Rel %<PRId64> van %<PRId64>; Woord %<PRId64> van %<PRId64>; " "Greep %<PRId64> van %<PRId64>" -#: ../ops.c:5146 #, c-format msgid "(+%<PRId64> for BOM)" msgstr "(+%<PRId64> vir 'BOM')" -#: ../option.c:1238 -msgid "%<%f%h%m%=Page %N" -msgstr "%<%f%h%m%=Bladsy %N" - -#: ../option.c:1574 -msgid "Thanks for flying Vim" -msgstr "Dankie dat jy vlieg met Vim" - #. found a mismatch: skip -#: ../option.c:2698 msgid "E518: Unknown option" msgstr "E518: Onbekende opsie" -#: ../option.c:2709 -msgid "E519: Option not supported" -msgstr "E519: Opsie is nie ondersteun nie" - -#: ../option.c:2740 msgid "E520: Not allowed in a modeline" msgstr "E520: Nie toegelaat in 'n moduslyn nie" -#: ../option.c:2815 -msgid "E846: Key code not set" -msgstr "" +#~ msgid "E846: Key code not set" +#~ msgstr "" -#: ../option.c:2924 msgid "E521: Number required after =" msgstr "E521: Nommer vereis na =" -#: ../option.c:3226 ../option.c:3864 -msgid "E522: Not found in termcap" -msgstr "E522: Nie gevind in 'termcap' nie" - -#: ../option.c:3335 #, c-format msgid "E539: Illegal character <%s>" msgstr "E539: Ongeldige karakter <%s>" -#: ../option.c:3862 -msgid "E529: Cannot set 'term' to empty string" -msgstr "E529: Kan nie 'term' stel na le string nie" +#, c-format +#~ msgid "For option %s" +#~ msgstr "" -#: ../option.c:3885 msgid "E589: 'backupext' and 'patchmode' are equal" msgstr "E589: 'backupext' en 'patchmode' is dieselfde" -#: ../option.c:3964 -msgid "E834: Conflicts with value of 'listchars'" -msgstr "" +#~ msgid "E834: Conflicts with value of 'listchars'" +#~ msgstr "" -#: ../option.c:3966 -msgid "E835: Conflicts with value of 'fillchars'" -msgstr "" +#~ msgid "E835: Conflicts with value of 'fillchars'" +#~ msgstr "" -#: ../option.c:4163 msgid "E524: Missing colon" msgstr "E524: Ontbrekende dubbelpunt" -#: ../option.c:4165 msgid "E525: Zero length string" msgstr "E525: Nul-lengte string" -#: ../option.c:4220 #, c-format msgid "E526: Missing number after <%s>" msgstr "E526: Ontbrekende nommer na <%s>" -#: ../option.c:4232 msgid "E527: Missing comma" msgstr "E527: Ontbrekende komma" -#: ../option.c:4239 msgid "E528: Must specify a ' value" msgstr "E528: Moet 'n ' waarde spesifiseer" -#: ../option.c:4271 msgid "E595: contains unprintable or wide character" msgstr "E595: bevat 'n ondrukbare of wye karakter" -#: ../option.c:4469 #, c-format msgid "E535: Illegal character after <%c>" msgstr "E535: Ongeldige karakter na <%c>" -#: ../option.c:4534 msgid "E536: comma required" msgstr "E536: komma benodig" -#: ../option.c:4543 #, c-format msgid "E537: 'commentstring' must be empty or contain %s" msgstr "E537: 'commentstring' moet leeg wees of %s bevat" -#: ../option.c:4928 msgid "E540: Unclosed expression sequence" msgstr "E540: Onvoltooide uitdrukkingreeks" -#: ../option.c:4932 msgid "E541: too many items" msgstr "E541: te veel items" -#: ../option.c:4934 msgid "E542: unbalanced groups" msgstr "E542: ongebalanseerde groepe" -#: ../option.c:5148 msgid "E590: A preview window already exists" msgstr "E590: Daar bestaan reeds 'n voorskouvenster" -#: ../option.c:5311 msgid "W17: Arabic requires UTF-8, do ':set encoding=utf-8'" msgstr "W17: Arabies benodig UTF-8, doen ':set encoding=utf-8'" -#: ../option.c:5623 #, c-format msgid "E593: Need at least %d lines" msgstr "E593: Benodig ten minste %d rels" -#: ../option.c:5631 #, c-format msgid "E594: Need at least %d columns" msgstr "E594: Benodig ten minste %d kolomme" -#: ../option.c:6011 #, c-format msgid "E355: Unknown option: %s" msgstr "E355: Onbekende opsie: %s" #. There's another character after zeros or the string -#. * is empty. In both cases, we are trying to set a -#. * num option using a string. -#: ../option.c:6037 +#. is empty. In both cases, we are trying to set a +#. num option using a string. #, fuzzy, c-format -msgid "E521: Number required: &%s = '%s'" -msgstr "E521: Nommer vereis na =" +#~ msgid "E521: Number required: &%s = '%s'" +#~ msgstr "E521: Nommer vereis na =" -#: ../option.c:6149 msgid "" "\n" "--- Terminal codes ---" @@ -4748,7 +4175,6 @@ msgstr "" "\n" "--- Terminaal kodes ---" -#: ../option.c:6151 msgid "" "\n" "--- Global option values ---" @@ -4756,7 +4182,6 @@ msgstr "" "\n" "--- Globale opsie waardes ---" -#: ../option.c:6153 msgid "" "\n" "--- Local option values ---" @@ -4764,7 +4189,6 @@ msgstr "" "\n" "--- Lokale opsie waardes ---" -#: ../option.c:6155 msgid "" "\n" "--- Options ---" @@ -4772,29 +4196,28 @@ msgstr "" "\n" "--- Opsies ---" -#: ../option.c:6816 msgid "E356: get_varp ERROR" msgstr "E356: 'get_varp' FOUT" -#: ../option.c:7696 #, c-format msgid "E357: 'langmap': Matching character missing for %s" msgstr "E357: 'langmap': Passende karakter ontbreek vir %s" -#: ../option.c:7715 #, c-format msgid "E358: 'langmap': Extra characters after semicolon: %s" msgstr "E358: 'langmap: Ekstra karakters na kommapunt: %s" -#: ../os/shell.c:194 -msgid "" -"\n" -"Cannot execute shell " -msgstr "" -"\n" -"Kan nie dop uitvoer nie " +#, c-format +#~ msgid "dlerror = \"%s\"" +#~ msgstr "" + +#, fuzzy, c-format +#~ msgid "E5420: Failed to write to file: %s" +#~ msgstr "E365: Kon nie 'PostScript' ler druk nie" + +msgid "Vim: Error reading input, exiting...\n" +msgstr "Vim: Fout met lees van invoer, verlaat...\n" -#: ../os/shell.c:439 msgid "" "\n" "shell returned " @@ -4802,959 +4225,930 @@ msgstr "" "\n" "dop lewer " -#: ../os_unix.c:465 ../os_unix.c:471 -msgid "" -"\n" -"Could not get security context for " -msgstr "" +#~ msgid "" +#~ "\n" +#~ "shell failed to start: " +#~ msgstr "" -#: ../os_unix.c:479 -msgid "" -"\n" -"Could not set security context for " -msgstr "" +#. Can happen if system() tries to send input to a shell command that was +#. backgrounded (:call system("cat - &", "foo")). #3529 #5241 +#, fuzzy, c-format +#~ msgid "E5677: Error writing input to shell-command: %s" +#~ msgstr "E208: Kan nie skryf na \"%s\"" -#: ../os_unix.c:1558 ../os_unix.c:1647 -#, c-format -msgid "dlerror = \"%s\"" -msgstr "" +#~ msgid "" +#~ "\n" +#~ "Could not get security context for " +#~ msgstr "" + +#~ msgid "" +#~ "\n" +#~ "Could not set security context for " +#~ msgstr "" -#: ../path.c:1449 #, c-format msgid "E447: Can't find file \"%s\" in path" msgstr "E447: Kan ler \"%s\" nie vind in pad nie" -#: ../quickfix.c:359 #, c-format msgid "E372: Too many %%%c in format string" msgstr "E372: Te veel %%%c in formaatstring" -#: ../quickfix.c:371 #, c-format msgid "E373: Unexpected %%%c in format string" msgstr "E373: Onverwagte %%%c in formaatstring" -#: ../quickfix.c:420 msgid "E374: Missing ] in format string" msgstr "E374: Ontbrekende ] in formaatstring" -#: ../quickfix.c:431 #, c-format msgid "E375: Unsupported %%%c in format string" msgstr "E375: Ongesteunde %%%c in formaatstring" -#: ../quickfix.c:448 #, c-format msgid "E376: Invalid %%%c in format string prefix" msgstr "E376: Ongeldige %%%c in formaatstringvoorvoegsel" -#: ../quickfix.c:454 #, c-format msgid "E377: Invalid %%%c in format string" msgstr "E377: Ongeldige %%%c in formaatstring" #. nothing found -#: ../quickfix.c:477 msgid "E378: 'errorformat' contains no pattern" msgstr "E378: 'errorformat' bevat geen patroon nie" -#: ../quickfix.c:695 msgid "E379: Missing or empty directory name" msgstr "E379: Ontbrekende of le gidsnaam" -#: ../quickfix.c:1305 msgid "E553: No more items" msgstr "E553: Geen items meer nie" -#: ../quickfix.c:1674 +#~ msgid "E924: Current window was closed" +#~ msgstr "" + +#~ msgid "E925: Current quickfix was changed" +#~ msgstr "" + +#~ msgid "E926: Current location list was changed" +#~ msgstr "" + #, c-format msgid "(%d of %d)%s%s: " msgstr "(%d van %d)%s%s: " -#: ../quickfix.c:1676 msgid " (line deleted)" msgstr " (rel verwyder)" -#: ../quickfix.c:1863 +#, fuzzy, c-format +#~ msgid "%serror list %d of %d; %d errors " +#~ msgstr "foutelys %d van %d; %d foute" + msgid "E380: At bottom of quickfix stack" msgstr "E380: Onder aan 'quickfix' stapel" -#: ../quickfix.c:1869 msgid "E381: At top of quickfix stack" msgstr "E381: Bo aan 'quickfix' stapel" -#: ../quickfix.c:1880 -#, c-format -msgid "error list %d of %d; %d errors" -msgstr "foutelys %d van %d; %d foute" +#~ msgid "No entries" +#~ msgstr "" -#: ../quickfix.c:2427 msgid "E382: Cannot write, 'buftype' option is set" msgstr "E382: Kan nie skryf nie, 'buftype' opsie is aan" -#: ../quickfix.c:2812 -msgid "E683: File name missing or invalid pattern" -msgstr "" +#~ msgid "E683: File name missing or invalid pattern" +#~ msgstr "" -#: ../quickfix.c:2911 #, fuzzy, c-format -msgid "Cannot open file \"%s\"" -msgstr "Kan nie ler %s oopmaak nie" +#~ msgid "Cannot open file \"%s\"" +#~ msgstr "Kan nie ler %s oopmaak nie" -#: ../quickfix.c:3429 #, fuzzy -msgid "E681: Buffer is not loaded" -msgstr "1 buffer uitgelaai" +#~ msgid "E681: Buffer is not loaded" +#~ msgstr "1 buffer uitgelaai" -#: ../quickfix.c:3487 #, fuzzy -msgid "E777: String or List expected" -msgstr "E548: syfer verwag" +#~ msgid "E777: String or List expected" +#~ msgstr "E548: syfer verwag" -#: ../regexp.c:359 #, c-format msgid "E369: invalid item in %s%%[]" msgstr "E369: ongeldige item in %s%%[]" -#: ../regexp.c:374 #, fuzzy, c-format -msgid "E769: Missing ] after %s[" -msgstr "E69: Ontbrekende ] na %s%%[" +#~ msgid "E769: Missing ] after %s[" +#~ msgstr "E69: Ontbrekende ] na %s%%[" -#: ../regexp.c:375 #, c-format msgid "E53: Unmatched %s%%(" msgstr "E53: Onpaar %s%%(" -#: ../regexp.c:376 #, c-format msgid "E54: Unmatched %s(" msgstr "E54: Onpaar %s(" -#: ../regexp.c:377 #, c-format msgid "E55: Unmatched %s)" msgstr "E55: Onpaar %s)" -#: ../regexp.c:378 msgid "E66: \\z( not allowed here" msgstr "E66: \\z( nie hier toegelaat nie" -#: ../regexp.c:379 msgid "E67: \\z1 et al. not allowed here" msgstr "E67: \\z1 e.a. nie hier toegelaat nie" -#: ../regexp.c:380 #, c-format msgid "E69: Missing ] after %s%%[" msgstr "E69: Ontbrekende ] na %s%%[" -#: ../regexp.c:381 #, c-format msgid "E70: Empty %s%%[]" msgstr "E70: Le %s%%[]" -#: ../regexp.c:1209 ../regexp.c:1224 msgid "E339: Pattern too long" msgstr "E339: Patroon te lank" -#: ../regexp.c:1371 msgid "E50: Too many \\z(" msgstr "E50: Te veel \\z(" -#: ../regexp.c:1378 #, c-format msgid "E51: Too many %s(" msgstr "E51: Te veel %s(" -#: ../regexp.c:1427 msgid "E52: Unmatched \\z(" msgstr "E52: Onpaar \\z(" -#: ../regexp.c:1637 #, c-format msgid "E59: invalid character after %s@" msgstr "E59: ongeldige karakter na %s@" -#: ../regexp.c:1672 #, c-format msgid "E60: Too many complex %s{...}s" msgstr "E60: Te veel komplekse %s{...}ies" -#: ../regexp.c:1687 #, c-format msgid "E61: Nested %s*" msgstr "E61: Geneste %s*" -#: ../regexp.c:1690 #, c-format msgid "E62: Nested %s%c" msgstr "E62: Geneste %s%c" -#: ../regexp.c:1800 msgid "E63: invalid use of \\_" msgstr "E63: ongeldige gebruik van \\_" -#: ../regexp.c:1850 #, c-format msgid "E64: %s%c follows nothing" msgstr "E64: %s%c volg niks" -#: ../regexp.c:1902 msgid "E65: Illegal back reference" msgstr "E65: Ongeldige tru-verwysing" -#: ../regexp.c:1943 msgid "E68: Invalid character after \\z" msgstr "E68: ongeldige karakter na \\z" -#: ../regexp.c:2049 ../regexp_nfa.c:1296 #, fuzzy, c-format -msgid "E678: Invalid character after %s%%[dxouU]" -msgstr "E71: Ongeldige karakter na %s%%" +#~ msgid "E678: Invalid character after %s%%[dxouU]" +#~ msgstr "E71: Ongeldige karakter na %s%%" -#: ../regexp.c:2107 #, c-format msgid "E71: Invalid character after %s%%" msgstr "E71: Ongeldige karakter na %s%%" -#: ../regexp.c:3017 +#, fuzzy, c-format +#~ msgid "E888: (NFA regexp) cannot repeat %s" +#~ msgstr "E50: Te veel \\z(" + #, c-format msgid "E554: Syntax error in %s{...}" msgstr "E554: Sintaksfout in %s{...}" -#: ../regexp.c:3805 msgid "External submatches:\n" msgstr "Eksterne subtreffers:\n" -#: ../regexp.c:7022 -msgid "" -"E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be " -"used " -msgstr "" - -#: ../regexp_nfa.c:239 -msgid "E865: (NFA) Regexp end encountered prematurely" -msgstr "" - -#: ../regexp_nfa.c:240 -#, c-format -msgid "E866: (NFA regexp) Misplaced %c" -msgstr "" - -#: ../regexp_nfa.c:242 -#, c-format -msgid "E877: (NFA regexp) Invalid character class: %<PRId64>" -msgstr "" - -#: ../regexp_nfa.c:1261 -#, c-format -msgid "E867: (NFA) Unknown operator '\\z%c'" -msgstr "" - -#: ../regexp_nfa.c:1387 -#, c-format -msgid "E867: (NFA) Unknown operator '\\%%%c'" -msgstr "" - -#: ../regexp_nfa.c:1802 -#, c-format -msgid "E869: (NFA) Unknown operator '\\@%c'" -msgstr "" - -#: ../regexp_nfa.c:1831 -msgid "E870: (NFA regexp) Error reading repetition limits" -msgstr "" - -#. Can't have a multi follow a multi. -#: ../regexp_nfa.c:1895 -msgid "E871: (NFA regexp) Can't have a multi follow a multi !" -msgstr "" - -#. Too many `(' -#: ../regexp_nfa.c:2037 -msgid "E872: (NFA regexp) Too many '('" -msgstr "" - -#: ../regexp_nfa.c:2042 -#, fuzzy -msgid "E879: (NFA regexp) Too many \\z(" -msgstr "E50: Te veel \\z(" - -#: ../regexp_nfa.c:2066 -msgid "E873: (NFA regexp) proper termination error" -msgstr "" - -#: ../regexp_nfa.c:2599 -msgid "E874: (NFA) Could not pop the stack !" -msgstr "" - -#: ../regexp_nfa.c:3298 -msgid "" -"E875: (NFA regexp) (While converting from postfix to NFA), too many states " -"left on stack" -msgstr "" - -#: ../regexp_nfa.c:3302 -msgid "E876: (NFA regexp) Not enough space to store the whole NFA " -msgstr "" - -#: ../regexp_nfa.c:4571 ../regexp_nfa.c:4869 -msgid "" -"Could not open temporary log file for writing, displaying on stderr ... " -msgstr "" +#~ msgid "" +#~ "E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be " +#~ "used " +#~ msgstr "" -#: ../regexp_nfa.c:4840 -#, c-format -msgid "(NFA) COULD NOT OPEN %s !" -msgstr "" +#~ msgid "Switching to backtracking RE engine for pattern: " +#~ msgstr "" -#: ../regexp_nfa.c:6049 -#, fuzzy -msgid "Could not open temporary log file for writing " -msgstr "E214: Kan nie tydelike ler vind vir skryf nie" +#~ msgid " TERMINAL" +#~ msgstr "" -#: ../screen.c:7435 msgid " VREPLACE" msgstr " VVERVANG" -#: ../screen.c:7437 msgid " REPLACE" msgstr " VERVANG" -#: ../screen.c:7440 msgid " REVERSE" msgstr " OMKEER" -#: ../screen.c:7441 msgid " INSERT" msgstr " INVOEG" -#: ../screen.c:7443 msgid " (insert)" msgstr " (invoeg)" -#: ../screen.c:7445 msgid " (replace)" msgstr " (vervang)" -#: ../screen.c:7447 msgid " (vreplace)" msgstr " (vvervang)" -#: ../screen.c:7449 msgid " Hebrew" msgstr " Hebreeus" -#: ../screen.c:7454 msgid " Arabic" msgstr " Arabies" -#: ../screen.c:7456 -msgid " (lang)" -msgstr " (taal)" - -#: ../screen.c:7459 msgid " (paste)" msgstr " (plak)" -#: ../screen.c:7469 msgid " VISUAL" msgstr " VISUELE" -#: ../screen.c:7470 msgid " VISUAL LINE" msgstr " VISUELE REL" -#: ../screen.c:7471 msgid " VISUAL BLOCK" msgstr " VISUELE BLOK" -#: ../screen.c:7472 msgid " SELECT" msgstr " KIES" -#: ../screen.c:7473 msgid " SELECT LINE" msgstr " KIES REL" -#: ../screen.c:7474 msgid " SELECT BLOCK" msgstr " KIES BLOK" -#: ../screen.c:7486 ../screen.c:7541 msgid "recording" msgstr "besig om op te neem" -#: ../search.c:487 #, c-format msgid "E383: Invalid search string: %s" msgstr "E383: Ongeldige soekstring: %s" -#: ../search.c:832 #, c-format msgid "E384: search hit TOP without match for: %s" msgstr "E384: soektog het BO getref sonder treffer vir: %s" -#: ../search.c:835 #, c-format msgid "E385: search hit BOTTOM without match for: %s" msgstr "E385: soektog het ONDER getref sonder treffer vir: %s" -#: ../search.c:1200 msgid "E386: Expected '?' or '/' after ';'" msgstr "E386: Verwag '?' of '/' na ';'" -#: ../search.c:4085 msgid " (includes previously listed match)" msgstr " (sluit in vorige gelyste treffer)" #. cursor at status line -#: ../search.c:4104 msgid "--- Included files " msgstr "--- Ingeslote lers" -#: ../search.c:4106 msgid "not found " msgstr "nie gevind nie " -#: ../search.c:4107 msgid "in path ---\n" msgstr "in pad ---\n" -#: ../search.c:4168 msgid " (Already listed)" msgstr " (Alreeds gelys)" -#: ../search.c:4170 msgid " NOT FOUND" msgstr " NIE GEVIND NIE" -#: ../search.c:4211 #, c-format msgid "Scanning included file: %s" msgstr "Deursoek ingeslote ler: %s" -#: ../search.c:4216 #, fuzzy, c-format -msgid "Searching included file %s" -msgstr "Deursoek ingeslote ler: %s" +#~ msgid "Searching included file %s" +#~ msgstr "Deursoek ingeslote ler: %s" -#: ../search.c:4405 msgid "E387: Match is on current line" msgstr "E387: Treffer is op huidige rel" -#: ../search.c:4517 msgid "All included files were found" msgstr "Alle ingeslote lers is gevind" -#: ../search.c:4519 msgid "No included files" msgstr "Geen ingeslote lers nie" -#: ../search.c:4527 msgid "E388: Couldn't find definition" msgstr "E388: Kon definisie nie vind nie" -#: ../search.c:4529 msgid "E389: Couldn't find pattern" msgstr "E389: Kon patroon nie vind nie" -#: ../search.c:4668 -#, fuzzy -msgid "Substitute " -msgstr "1 vervanging" +#~ msgid "too few bytes read" +#~ msgstr "" + +#, fuzzy, c-format +#~ msgid "System error while skipping in ShaDa file: %s" +#~ msgstr "E47: Fout tydens lees van 'errorfile'" -#: ../search.c:4681 #, c-format -msgid "" -"\n" -"# Last %sSearch Pattern:\n" -"~" -msgstr "" +#~ msgid "" +#~ "Error while reading ShaDa file: last entry specified that it occupies " +#~ "%<PRIu64> bytes, but file ended earlier" +#~ msgstr "" + +#, fuzzy, c-format +#~ msgid "System error while closing ShaDa file: %s" +#~ msgstr "E47: Fout tydens lees van 'errorfile'" + +#, fuzzy, c-format +#~ msgid "System error while writing ShaDa file: %s" +#~ msgstr "E47: Fout tydens lees van 'errorfile'" + +#, fuzzy, c-format +#~ msgid "Reading ShaDa file \"%s\"%s%s%s" +#~ msgstr "Besig om viminfo ler \"%s\"%s%s%s te lees" + +msgid " info" +msgstr " inligting" + +msgid " marks" +msgstr " merkers" -#: ../spell.c:951 #, fuzzy -msgid "E759: Format error in spell file" -msgstr "E297: Skryffout in ruiller" +#~ msgid " oldfiles" +#~ msgstr "Geen ingeslote lers nie" -#: ../spell.c:952 -msgid "E758: Truncated spell file" -msgstr "" +msgid " FAILED" +msgstr " GEFAAL" -#: ../spell.c:953 #, c-format -msgid "Trailing text in %s line %d: %s" -msgstr "" +#~ msgid "System error while opening ShaDa file %s for reading: %s" +#~ msgstr "" + +#~ msgid "additional elements of ShaDa " +#~ msgstr "" + +#~ msgid "additional data of ShaDa " +#~ msgstr "" -#: ../spell.c:954 #, c-format -msgid "Affix name too long in %s line %d: %s" -msgstr "" +#~ msgid "Failed to write variable %s" +#~ msgstr "" -#: ../spell.c:955 -#, fuzzy -msgid "E761: Format error in affix file FOL, LOW or UPP" -msgstr "E431: Formaatfout in etiketler \"%s\"" +#, c-format +#~ msgid "" +#~ "Failed to parse ShaDa file due to a msgpack parser error at position " +#~ "%<PRIu64>" +#~ msgstr "" -#: ../spell.c:957 -msgid "E762: Character in FOL, LOW or UPP is out of range" -msgstr "" +#, c-format +#~ msgid "" +#~ "Failed to parse ShaDa file: incomplete msgpack string at position %<PRIu64>" +#~ msgstr "" -#: ../spell.c:958 -msgid "Compressing word tree..." -msgstr "" +#, c-format +#~ msgid "" +#~ "Failed to parse ShaDa file: extra bytes in msgpack string at position " +#~ "%<PRIu64>" +#~ msgstr "" -#: ../spell.c:1951 -msgid "E756: Spell checking is not enabled" -msgstr "" +#, c-format +#~ msgid "" +#~ "System error while opening ShaDa file %s for reading to merge before writing " +#~ "it: %s" +#~ msgstr "" -#: ../spell.c:2249 +#. Tried names from .tmp.a to .tmp.z, all failed. Something must be +#. wrong then. #, c-format -msgid "Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\"" -msgstr "" +#~ msgid "E138: All %s.tmp.X files exist, cannot write ShaDa file!" +#~ msgstr "" -#: ../spell.c:2473 #, fuzzy, c-format -msgid "Reading spell file \"%s\"" -msgstr "Gebruik ruiller \"%s\"" +#~ msgid "System error while opening temporary ShaDa file %s for writing: %s" +#~ msgstr "E214: Kan nie tydelike ler vind vir skryf nie" -#: ../spell.c:2496 -#, fuzzy -msgid "E757: This does not look like a spell file" -msgstr "E307: %s lyk nie soos 'n Vim ruiller nie" +#, c-format +#~ msgid "Failed to create directory %s for writing ShaDa file: %s" +#~ msgstr "" -#: ../spell.c:2501 -msgid "E771: Old spell file, needs to be updated" -msgstr "" +#, c-format +#~ msgid "System error while opening ShaDa file %s for writing: %s" +#~ msgstr "" -#: ../spell.c:2504 -msgid "E772: Spell file is for newer version of Vim" -msgstr "" +#, fuzzy, c-format +#~ msgid "Writing ShaDa file \"%s\"" +#~ msgstr "Besig om viminfo ler \"%s\" te stoor" -#: ../spell.c:2602 -#, fuzzy -msgid "E770: Unsupported section in spell file" -msgstr "E297: Skryffout in ruiller" +#, fuzzy, c-format +#~ msgid "Failed setting uid and gid for file %s: %s" +#~ msgstr "%s klaar uitgevoer" -#: ../spell.c:3762 #, fuzzy, c-format -msgid "Warning: region %s not supported" -msgstr "E519: Opsie is nie ondersteun nie" +#~ msgid "E137: ShaDa file is not writable: %s" +#~ msgstr "E137: Viminfo ler is nie skryfbaar nie: %s" + +#, c-format +#~ msgid "Can't rename ShaDa file from %s to %s!" +#~ msgstr "" -#: ../spell.c:4550 #, fuzzy, c-format -msgid "Reading affix file %s ..." -msgstr "Deursoek etiketler %s" +#~ msgid "Did not rename %s because %s does not look like a ShaDa file" +#~ msgstr " [lyk nie soos 'n Vim ruiller nie]" -#: ../spell.c:4589 ../spell.c:5635 ../spell.c:6140 #, c-format -msgid "Conversion failure for word in %s line %d: %s" -msgstr "" +#~ msgid "Did not rename %s to %s because there were errors during writing it" +#~ msgstr "" -#: ../spell.c:4630 ../spell.c:6170 #, c-format -msgid "Conversion in %s not supported: from %s to %s" -msgstr "" +#~ msgid "Do not forget to remove %s or rename it manually to %s." +#~ msgstr "" + +#, fuzzy, c-format +#~ msgid "System error while reading ShaDa file: %s" +#~ msgstr "E47: Fout tydens lees van 'errorfile'" + +#, fuzzy, c-format +#~ msgid "System error while reading integer from ShaDa file: %s" +#~ msgstr "E47: Fout tydens lees van 'errorfile'" -#: ../spell.c:4642 #, c-format -msgid "Invalid value for FLAG in %s line %d: %s" -msgstr "" +#~ msgid "" +#~ "Error while reading ShaDa file: expected positive integer at position " +#~ "%<PRIu64>, but got nothing" +#~ msgstr "" -#: ../spell.c:4655 #, c-format -msgid "FLAG after using flags in %s line %d: %s" -msgstr "" +#~ msgid "" +#~ "Error while reading ShaDa file: expected positive integer at position " +#~ "%<PRIu64>" +#~ msgstr "" -#: ../spell.c:4723 #, c-format -msgid "" -"Defining COMPOUNDFORBIDFLAG after PFX item may give wrong results in %s line " -"%d" -msgstr "" +#~ msgid "" +#~ "Error while reading ShaDa file: there is an item at position %<PRIu64> that " +#~ "is stated to be too long" +#~ msgstr "" -#: ../spell.c:4731 +#. kSDItemUnknown cannot possibly pass that far because it is -1 and that +#. will fail in msgpack_read_uint64. But kSDItemMissing may and it will +#. otherwise be skipped because (1 << 0) will never appear in flags. #, c-format -msgid "" -"Defining COMPOUNDPERMITFLAG after PFX item may give wrong results in %s line " -"%d" -msgstr "" +#~ msgid "" +#~ "Error while reading ShaDa file: there is an item at position %<PRIu64> that " +#~ "must not be there: Missing items are for internal uses only" +#~ msgstr "" -#: ../spell.c:4747 #, c-format -msgid "Wrong COMPOUNDRULES value in %s line %d: %s" -msgstr "" +#~ msgid "" +#~ "Error while reading ShaDa file: buffer list at position %<PRIu64> contains " +#~ "entry that is not a dictionary" +#~ msgstr "" -#: ../spell.c:4771 #, c-format -msgid "Wrong COMPOUNDWORDMAX value in %s line %d: %s" -msgstr "" +#~ msgid "" +#~ "Error while reading ShaDa file: buffer list at position %<PRIu64> contains " +#~ "entry with invalid line number" +#~ msgstr "" -#: ../spell.c:4777 #, c-format -msgid "Wrong COMPOUNDMIN value in %s line %d: %s" -msgstr "" +#~ msgid "" +#~ "Error while reading ShaDa file: buffer list at position %<PRIu64> contains " +#~ "entry with invalid column number" +#~ msgstr "" -#: ../spell.c:4783 #, c-format -msgid "Wrong COMPOUNDSYLMAX value in %s line %d: %s" -msgstr "" +#~ msgid "" +#~ "Error while reading ShaDa file: buffer list at position %<PRIu64> contains " +#~ "entry that does not have a file name" +#~ msgstr "" + +#. values for ts_isdiff +#. no different byte (yet) +#. different byte found +#. inserting character +#. values for ts_flags +#. already checked that prefix is OK +#. tried split at this point +#. did a delete, "ts_delidx" has index +#. special values ts_prefixdepth +#. not using prefixes +#. walking through the prefix tree +#. highest value that's not special +#. mode values for find_word +#. find word case-folded +#. find keep-case word +#. find word after prefix +#. find case-folded compound word +#. find keep-case compound word +#, fuzzy +#~ msgid "E759: Format error in spell file" +#~ msgstr "E297: Skryffout in ruiller" + +#~ msgid "E756: Spell checking is not enabled" +#~ msgstr "" -#: ../spell.c:4795 #, c-format -msgid "Wrong CHECKCOMPOUNDPATTERN value in %s line %d: %s" -msgstr "" +#~ msgid "Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\"" +#~ msgstr "" + +#, fuzzy +#~ msgid "E797: SpellFileMissing autocommand deleted buffer" +#~ msgstr "E246: 'FileChangedShell' outobevel het buffer verwyder" + +#. This is probably an error. Give a warning and +#. accept the words anyway. +#, fuzzy, c-format +#~ msgid "Warning: region %s not supported" +#~ msgstr "E519: Opsie is nie ondersteun nie" + +#~ msgid "Sorry, no suggestions" +#~ msgstr "" + +#, fuzzy, c-format +#~ msgid "Sorry, only %<PRId64> suggestions" +#~ msgstr " op %<PRId64> rels" + +#. for when 'cmdheight' > 1 +#. avoid more prompt +#, fuzzy, c-format +#~ msgid "Change \"%.*s\" to:" +#~ msgstr "Stoor veranderinge na \"%.*s\"?" -#: ../spell.c:4847 #, c-format -msgid "Different combining flag in continued affix block in %s line %d: %s" -msgstr "" +#~ msgid " < \"%.*s\"" +#~ msgstr "" + +#, fuzzy +#~ msgid "E752: No previous spell replacement" +#~ msgstr "E35: Geen vorige patroon nie" -#: ../spell.c:4850 #, fuzzy, c-format -msgid "Duplicate affix in %s line %d: %s" -msgstr "E154: Duplikaat etiket \"%s\" in ler %s/%s" +#~ msgid "E753: Not found: %s" +#~ msgstr "E334: Kieslys nie gevind nie: %s" + +#~ msgid "E758: Truncated spell file" +#~ msgstr "" -#: ../spell.c:4871 #, c-format -msgid "" -"Affix also used for BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST in %s " -"line %d: %s" -msgstr "" +#~ msgid "Trailing text in %s line %d: %s" +#~ msgstr "" -#: ../spell.c:4893 #, c-format -msgid "Expected Y or N in %s line %d: %s" -msgstr "" +#~ msgid "Affix name too long in %s line %d: %s" +#~ msgstr "" + +#, fuzzy +#~ msgid "E761: Format error in affix file FOL, LOW or UPP" +#~ msgstr "E431: Formaatfout in etiketler \"%s\"" + +#~ msgid "E762: Character in FOL, LOW or UPP is out of range" +#~ msgstr "" + +#~ msgid "Compressing word tree..." +#~ msgstr "" + +#, fuzzy, c-format +#~ msgid "Reading spell file \"%s\"" +#~ msgstr "Gebruik ruiller \"%s\"" + +#, fuzzy +#~ msgid "E757: This does not look like a spell file" +#~ msgstr "E307: %s lyk nie soos 'n Vim ruiller nie" + +#, fuzzy, c-format +#~ msgid "E5042: Failed to read spell file %s: %s" +#~ msgstr "E482: Kan nie ler %s skep nie" + +#~ msgid "E771: Old spell file, needs to be updated" +#~ msgstr "" + +#~ msgid "E772: Spell file is for newer version of Vim" +#~ msgstr "" + +#, fuzzy +#~ msgid "E770: Unsupported section in spell file" +#~ msgstr "E297: Skryffout in ruiller" + +#, fuzzy, c-format +#~ msgid "E778: This does not look like a .sug file: %s" +#~ msgstr "E307: %s lyk nie soos 'n Vim ruiller nie" -#: ../spell.c:4968 #, c-format -msgid "Broken condition in %s line %d: %s" -msgstr "" +#~ msgid "E779: Old .sug file, needs to be updated: %s" +#~ msgstr "" -#: ../spell.c:5091 #, c-format -msgid "Expected REP(SAL) count in %s line %d" -msgstr "" +#~ msgid "E780: .sug file is for newer version of Vim: %s" +#~ msgstr "" -#: ../spell.c:5120 #, c-format -msgid "Expected MAP count in %s line %d" -msgstr "" +#~ msgid "E781: .sug file doesn't match .spl file: %s" +#~ msgstr "" + +#, fuzzy, c-format +#~ msgid "E782: error while reading .sug file: %s" +#~ msgstr "E47: Fout tydens lees van 'errorfile'" + +#, fuzzy, c-format +#~ msgid "Reading affix file %s ..." +#~ msgstr "Deursoek etiketler %s" -#: ../spell.c:5132 #, c-format -msgid "Duplicate character in MAP in %s line %d" -msgstr "" +#~ msgid "Conversion failure for word in %s line %d: %s" +#~ msgstr "" -#: ../spell.c:5176 #, c-format -msgid "Unrecognized or duplicate item in %s line %d: %s" -msgstr "" +#~ msgid "Conversion in %s not supported: from %s to %s" +#~ msgstr "" -#: ../spell.c:5197 #, c-format -msgid "Missing FOL/LOW/UPP line in %s" -msgstr "" +#~ msgid "Invalid value for FLAG in %s line %d: %s" +#~ msgstr "" -#: ../spell.c:5220 -msgid "COMPOUNDSYLMAX used without SYLLABLE" -msgstr "" +#, c-format +#~ msgid "FLAG after using flags in %s line %d: %s" +#~ msgstr "" -#: ../spell.c:5236 -#, fuzzy -msgid "Too many postponed prefixes" -msgstr "Te veel redigeer-parameters" +#, c-format +#~ msgid "" +#~ "Defining COMPOUNDFORBIDFLAG after PFX item may give wrong results in %s line " +#~ "%d" +#~ msgstr "" -#: ../spell.c:5238 -#, fuzzy -msgid "Too many compound flags" -msgstr "Te veel redigeer-parameters" +#, c-format +#~ msgid "" +#~ "Defining COMPOUNDPERMITFLAG after PFX item may give wrong results in %s line " +#~ "%d" +#~ msgstr "" -#: ../spell.c:5240 -msgid "Too many postponed prefixes and/or compound flags" -msgstr "" +#, c-format +#~ msgid "Wrong COMPOUNDRULES value in %s line %d: %s" +#~ msgstr "" -#: ../spell.c:5250 #, c-format -msgid "Missing SOFO%s line in %s" -msgstr "" +#~ msgid "Wrong COMPOUNDWORDMAX value in %s line %d: %s" +#~ msgstr "" -#: ../spell.c:5253 #, c-format -msgid "Both SAL and SOFO lines in %s" -msgstr "" +#~ msgid "Wrong COMPOUNDMIN value in %s line %d: %s" +#~ msgstr "" -#: ../spell.c:5331 #, c-format -msgid "Flag is not a number in %s line %d: %s" -msgstr "" +#~ msgid "Wrong COMPOUNDSYLMAX value in %s line %d: %s" +#~ msgstr "" -#: ../spell.c:5334 #, c-format -msgid "Illegal flag in %s line %d: %s" -msgstr "" +#~ msgid "Wrong CHECKCOMPOUNDPATTERN value in %s line %d: %s" +#~ msgstr "" -#: ../spell.c:5493 ../spell.c:5501 #, c-format -msgid "%s value differs from what is used in another .aff file" -msgstr "" +#~ msgid "Different combining flag in continued affix block in %s line %d: %s" +#~ msgstr "" -#: ../spell.c:5602 #, fuzzy, c-format -msgid "Reading dictionary file %s ..." -msgstr "Deursoek woordeboek: %s" +#~ msgid "Duplicate affix in %s line %d: %s" +#~ msgstr "E154: Duplikaat etiket \"%s\" in ler %s/%s" -#: ../spell.c:5611 #, c-format -msgid "E760: No word count in %s" -msgstr "" +#~ msgid "" +#~ "Affix also used for BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGESTin %s " +#~ "line %d: %s" +#~ msgstr "" -#: ../spell.c:5669 #, c-format -msgid "line %6d, word %6d - %s" -msgstr "" - -#: ../spell.c:5691 -#, fuzzy, c-format -msgid "Duplicate word in %s line %d: %s" -msgstr "Patroon gevind in elke rel: %s" +#~ msgid "Expected Y or N in %s line %d: %s" +#~ msgstr "" -#: ../spell.c:5694 #, c-format -msgid "First duplicate word in %s line %d: %s" -msgstr "" +#~ msgid "Broken condition in %s line %d: %s" +#~ msgstr "" -#: ../spell.c:5746 #, c-format -msgid "%d duplicate word(s) in %s" -msgstr "" +#~ msgid "Expected REP(SAL) count in %s line %d" +#~ msgstr "" -#: ../spell.c:5748 #, c-format -msgid "Ignored %d word(s) with non-ASCII characters in %s" -msgstr "" +#~ msgid "Expected MAP count in %s line %d" +#~ msgstr "" -#: ../spell.c:6115 -#, fuzzy, c-format -msgid "Reading word file %s ..." -msgstr "Lees nou vanaf stdin... " +#, c-format +#~ msgid "Duplicate character in MAP in %s line %d" +#~ msgstr "" -#: ../spell.c:6155 #, c-format -msgid "Duplicate /encoding= line ignored in %s line %d: %s" -msgstr "" +#~ msgid "Unrecognized or duplicate item in %s line %d: %s" +#~ msgstr "" -#: ../spell.c:6159 #, c-format -msgid "/encoding= line after word ignored in %s line %d: %s" -msgstr "" +#~ msgid "Missing FOL/LOW/UPP line in %s" +#~ msgstr "" + +#~ msgid "COMPOUNDSYLMAX used without SYLLABLE" +#~ msgstr "" + +#, fuzzy +#~ msgid "Too many postponed prefixes" +#~ msgstr "Te veel redigeer-parameters" + +#, fuzzy +#~ msgid "Too many compound flags" +#~ msgstr "Te veel redigeer-parameters" + +#~ msgid "Too many postponed prefixes and/or compound flags" +#~ msgstr "" -#: ../spell.c:6180 #, c-format -msgid "Duplicate /regions= line ignored in %s line %d: %s" -msgstr "" +#~ msgid "Missing SOFO%s line in %s" +#~ msgstr "" -#: ../spell.c:6185 #, c-format -msgid "Too many regions in %s line %d: %s" -msgstr "" +#~ msgid "Both SAL and SOFO lines in %s" +#~ msgstr "" -#: ../spell.c:6198 #, c-format -msgid "/ line ignored in %s line %d: %s" -msgstr "" +#~ msgid "Flag is not a number in %s line %d: %s" +#~ msgstr "" -#: ../spell.c:6224 -#, fuzzy, c-format -msgid "Invalid region nr in %s line %d: %s" -msgstr "E573: Ongeldige bediener-id gebruik: %s" +#, c-format +#~ msgid "Illegal flag in %s line %d: %s" +#~ msgstr "" -#: ../spell.c:6230 #, c-format -msgid "Unrecognized flags in %s line %d: %s" -msgstr "" +#~ msgid "%s value differs from what is used in another .aff file" +#~ msgstr "" + +#, fuzzy, c-format +#~ msgid "Reading dictionary file %s ..." +#~ msgstr "Deursoek woordeboek: %s" -#: ../spell.c:6257 #, c-format -msgid "Ignored %d words with non-ASCII characters" -msgstr "" +#~ msgid "E760: No word count in %s" +#~ msgstr "" -#: ../spell.c:6656 #, c-format -msgid "Compressed %d of %d nodes; %d (%d%%) remaining" -msgstr "" +#~ msgid "line %6d, word %6d - %s" +#~ msgstr "" -#: ../spell.c:7340 -msgid "Reading back spell file..." -msgstr "" +#, fuzzy, c-format +#~ msgid "Duplicate word in %s line %d: %s" +#~ msgstr "Patroon gevind in elke rel: %s" -#. Go through the trie of good words, soundfold each word and add it to -#. the soundfold trie. -#: ../spell.c:7357 -msgid "Performing soundfolding..." -msgstr "" +#, c-format +#~ msgid "First duplicate word in %s line %d: %s" +#~ msgstr "" -#: ../spell.c:7368 #, c-format -msgid "Number of words after soundfolding: %<PRId64>" -msgstr "" +#~ msgid "%d duplicate word(s) in %s" +#~ msgstr "" -#: ../spell.c:7476 #, c-format -msgid "Total number of words: %d" -msgstr "" +#~ msgid "Ignored %d word(s) with non-ASCII characters in %s" +#~ msgstr "" -#: ../spell.c:7655 #, fuzzy, c-format -msgid "Writing suggestion file %s ..." -msgstr "Besig om viminfo ler \"%s\" te stoor" +#~ msgid "Reading word file %s ..." +#~ msgstr "Lees nou vanaf stdin... " -#: ../spell.c:7707 ../spell.c:7927 #, c-format -msgid "Estimated runtime memory use: %d bytes" -msgstr "" +#~ msgid "Duplicate /encoding= line ignored in %s line %d: %s" +#~ msgstr "" -#: ../spell.c:7820 -msgid "E751: Output file name must not have region name" -msgstr "" +#, c-format +#~ msgid "/encoding= line after word ignored in %s line %d: %s" +#~ msgstr "" -#: ../spell.c:7822 -#, fuzzy -msgid "E754: Only up to 8 regions supported" -msgstr "E519: Opsie is nie ondersteun nie" +#, c-format +#~ msgid "Duplicate /regions= line ignored in %s line %d: %s" +#~ msgstr "" -#: ../spell.c:7846 -#, fuzzy, c-format -msgid "E755: Invalid region in %s" -msgstr "E15: Ongeldige uitdrukking: %s" +#, c-format +#~ msgid "Too many regions in %s line %d: %s" +#~ msgstr "" -#: ../spell.c:7907 -msgid "Warning: both compounding and NOBREAK specified" -msgstr "" +#, c-format +#~ msgid "/ line ignored in %s line %d: %s" +#~ msgstr "" -#: ../spell.c:7920 #, fuzzy, c-format -msgid "Writing spell file %s ..." -msgstr "Besig om viminfo ler \"%s\" te stoor" +#~ msgid "Invalid region nr in %s line %d: %s" +#~ msgstr "E573: Ongeldige bediener-id gebruik: %s" -#: ../spell.c:7925 -msgid "Done!" -msgstr "" - -#: ../spell.c:8034 #, c-format -msgid "E765: 'spellfile' does not have %<PRId64> entries" -msgstr "" +#~ msgid "Unrecognized flags in %s line %d: %s" +#~ msgstr "" -#: ../spell.c:8074 #, c-format -msgid "Word '%.*s' removed from %s" -msgstr "" +#~ msgid "Ignored %d words with non-ASCII characters" +#~ msgstr "" -#: ../spell.c:8117 #, c-format -msgid "Word '%.*s' added to %s" -msgstr "" +#~ msgid "Compressed %d of %d nodes; %d (%d%%) remaining" +#~ msgstr "" -#: ../spell.c:8381 -msgid "E763: Word characters differ between spell files" -msgstr "" +#~ msgid "Reading back spell file..." +#~ msgstr "" -#: ../spell.c:8684 -msgid "Sorry, no suggestions" -msgstr "" +#. Go through the trie of good words, soundfold each word and add it to +#. the soundfold trie. +#~ msgid "Performing soundfolding..." +#~ msgstr "" -#: ../spell.c:8687 -#, fuzzy, c-format -msgid "Sorry, only %<PRId64> suggestions" -msgstr " op %<PRId64> rels" +#, c-format +#~ msgid "Number of words after soundfolding: %<PRId64>" +#~ msgstr "" + +#, c-format +#~ msgid "Total number of words: %d" +#~ msgstr "" -#. for when 'cmdheight' > 1 -#. avoid more prompt -#: ../spell.c:8704 #, fuzzy, c-format -msgid "Change \"%.*s\" to:" -msgstr "Stoor veranderinge na \"%.*s\"?" +#~ msgid "Writing suggestion file %s ..." +#~ msgstr "Besig om viminfo ler \"%s\" te stoor" -#: ../spell.c:8737 #, c-format -msgid " < \"%.*s\"" -msgstr "" +#~ msgid "Estimated runtime memory use: %d bytes" +#~ msgstr "" + +#~ msgid "E751: Output file name must not have region name" +#~ msgstr "" -#: ../spell.c:8882 #, fuzzy -msgid "E752: No previous spell replacement" -msgstr "E35: Geen vorige patroon nie" +#~ msgid "E754: Only up to 8 regions supported" +#~ msgstr "E519: Opsie is nie ondersteun nie" -#: ../spell.c:8925 #, fuzzy, c-format -msgid "E753: Not found: %s" -msgstr "E334: Kieslys nie gevind nie: %s" +#~ msgid "E755: Invalid region in %s" +#~ msgstr "E15: Ongeldige uitdrukking: %s" + +#~ msgid "Warning: both compounding and NOBREAK specified" +#~ msgstr "" -#: ../spell.c:9276 #, fuzzy, c-format -msgid "E778: This does not look like a .sug file: %s" -msgstr "E307: %s lyk nie soos 'n Vim ruiller nie" +#~ msgid "Writing spell file %s ..." +#~ msgstr "Besig om viminfo ler \"%s\" te stoor" + +#~ msgid "Done!" +#~ msgstr "" -#: ../spell.c:9282 #, c-format -msgid "E779: Old .sug file, needs to be updated: %s" -msgstr "" +#~ msgid "E765: 'spellfile' does not have %<PRId64> entries" +#~ msgstr "" -#: ../spell.c:9286 #, c-format -msgid "E780: .sug file is for newer version of Vim: %s" -msgstr "" +#~ msgid "Word '%.*s' removed from %s" +#~ msgstr "" -#: ../spell.c:9295 #, c-format -msgid "E781: .sug file doesn't match .spl file: %s" -msgstr "" +#~ msgid "Word '%.*s' added to %s" +#~ msgstr "" -#: ../spell.c:9305 -#, fuzzy, c-format -msgid "E782: error while reading .sug file: %s" -msgstr "E47: Fout tydens lees van 'errorfile'" +#~ msgid "E763: Word characters differ between spell files" +#~ msgstr "" #. This should have been checked when generating the .spl #. file. -#: ../spell.c:11575 -msgid "E783: duplicate char in MAP entry" -msgstr "" +#~ msgid "E783: duplicate char in MAP entry" +#~ msgstr "" + +#, fuzzy +#~ msgid "E766: Insufficient arguments for printf()" +#~ msgstr "E116: Ongeldige parameters vir funksie %s" + +#~ msgid "E807: Expected Float argument for printf()" +#~ msgstr "" + +#, fuzzy +#~ msgid "E767: Too many arguments to printf()" +#~ msgstr "E118: Te veel parameters vir funksie: %s" -#: ../syntax.c:266 msgid "No Syntax items defined for this buffer" msgstr "Geen Sintaks-items gedefinieer vir hierdie buffer nie" -#: ../syntax.c:3083 ../syntax.c:3104 ../syntax.c:3127 #, c-format msgid "E390: Illegal argument: %s" msgstr "E390: Ongeldige parameter: %s" -#: ../syntax.c:3299 +#~ msgid "syntax iskeyword " +#~ msgstr "" + #, c-format msgid "E391: No such syntax cluster: %s" msgstr "E391: Geen sodanige sintakskluster nie: %s" -#: ../syntax.c:3433 msgid "syncing on C-style comments" msgstr "sinchroniseer met C-styl kommentaar" -#: ../syntax.c:3439 msgid "no syncing" msgstr "geen sinchronisering" -#: ../syntax.c:3441 msgid "syncing starts " msgstr "sinchronisasie begin " -#: ../syntax.c:3443 ../syntax.c:3506 msgid " lines before top line" msgstr " rels voor boonste lyn" -#: ../syntax.c:3448 msgid "" "\n" "--- Syntax sync items ---" @@ -5762,7 +5156,6 @@ msgstr "" "\n" "--- Sintaks sync items ---" -#: ../syntax.c:3452 msgid "" "\n" "syncing on items" @@ -5770,7 +5163,6 @@ msgstr "" "\n" "sinchronisering met items" -#: ../syntax.c:3457 msgid "" "\n" "--- Syntax items ---" @@ -5778,275 +5170,216 @@ msgstr "" "\n" "--- Sintaks items ---" -#: ../syntax.c:3475 #, c-format msgid "E392: No such syntax cluster: %s" msgstr "E392: Geen sodanige sintakskluster nie: %s" -#: ../syntax.c:3497 msgid "minimal " msgstr "minimaal " -#: ../syntax.c:3503 msgid "maximal " msgstr "maksimaal " -#: ../syntax.c:3513 msgid "; match " msgstr "; treffer " -#: ../syntax.c:3515 msgid " line breaks" msgstr " rel breuke" -#: ../syntax.c:4076 msgid "E395: contains argument not accepted here" msgstr "E395: bevat parameters nie hier aanvaar nie" -#: ../syntax.c:4096 #, fuzzy -msgid "E844: invalid cchar value" -msgstr "E474: Ongeldige parameter" +#~ msgid "E844: invalid cchar value" +#~ msgstr "E474: Ongeldige parameter" -#: ../syntax.c:4107 msgid "E393: group[t]here not accepted here" msgstr "E393: 'group[t]here' nie hier aanvaar nie" -#: ../syntax.c:4126 #, c-format msgid "E394: Didn't find region item for %s" msgstr "E394: Kon nie omgewingsitem vind vir %s nie" -#: ../syntax.c:4188 msgid "E397: Filename required" msgstr "E397: Lernaam benodig" -#: ../syntax.c:4221 #, fuzzy -msgid "E847: Too many syntax includes" -msgstr "E77: Te veel lername" +#~ msgid "E847: Too many syntax includes" +#~ msgstr "E77: Te veel lername" -#: ../syntax.c:4303 #, fuzzy, c-format -msgid "E789: Missing ']': %s" -msgstr "E398: Ontbrekende '=': %s" +#~ msgid "E789: Missing ']': %s" +#~ msgstr "E398: Ontbrekende '=': %s" + +#, fuzzy, c-format +#~ msgid "E890: trailing char after ']': %s]%s" +#~ msgstr "E488: Oorbodige karakters" -#: ../syntax.c:4531 #, c-format msgid "E398: Missing '=': %s" msgstr "E398: Ontbrekende '=': %s" -#: ../syntax.c:4666 #, c-format msgid "E399: Not enough arguments: syntax region %s" msgstr "E399: Nie genoeg parameters nie: sintaksomgewing %s" -#: ../syntax.c:4870 #, fuzzy -msgid "E848: Too many syntax clusters" -msgstr "E391: Geen sodanige sintakskluster nie: %s" +#~ msgid "E848: Too many syntax clusters" +#~ msgstr "E391: Geen sodanige sintakskluster nie: %s" -#: ../syntax.c:4954 msgid "E400: No cluster specified" msgstr "E400: Geen kluster gespesifiseer nie" #. end delimiter not found -#: ../syntax.c:4986 #, c-format msgid "E401: Pattern delimiter not found: %s" msgstr "E401: Patroonbegrenser nie gevind nie: %s" -#: ../syntax.c:5049 #, c-format msgid "E402: Garbage after pattern: %s" msgstr "E402: Gemors na patroon: %s" -#: ../syntax.c:5120 msgid "E403: syntax sync: line continuations pattern specified twice" msgstr "E403: sintaks sync: relvoortgaanpatroon twee keer gespesifiseer" -#: ../syntax.c:5169 #, c-format msgid "E404: Illegal arguments: %s" msgstr "E404: Ongeldige parameters: %s" -#: ../syntax.c:5217 #, c-format msgid "E405: Missing equal sign: %s" msgstr "E405: Ontbrekende gelykaanteken: %s" -#: ../syntax.c:5222 #, c-format msgid "E406: Empty argument: %s" msgstr "E406: Le parameter: %s" -#: ../syntax.c:5240 #, c-format msgid "E407: %s not allowed here" msgstr "E407: %s nie toegelaat hier nie" -#: ../syntax.c:5246 #, c-format msgid "E408: %s must be first in contains list" msgstr "E408: %s moet vr in 'contains' lys wees" -#: ../syntax.c:5304 #, c-format msgid "E409: Unknown group name: %s" msgstr "E409: Onbekende groepnaam: %s" -#: ../syntax.c:5512 #, c-format msgid "E410: Invalid :syntax subcommand: %s" msgstr "E410: Ongeldige :syntax subbevel %s" -#: ../syntax.c:5854 -msgid "" -" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN" -msgstr "" +#~ msgid "" +#~ " TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN" +#~ msgstr "" -#: ../syntax.c:6146 msgid "E679: recursive loop loading syncolor.vim" msgstr "E679: rekursiewe lus gedurende laai van syncolor.vim" -#: ../syntax.c:6256 #, c-format msgid "E411: highlight group not found: %s" msgstr "E411: uitliggroep nie gevind nie: %s" -#: ../syntax.c:6278 #, c-format msgid "E412: Not enough arguments: \":highlight link %s\"" msgstr "E412: Te min parameters: \":highlight link %s\"" -#: ../syntax.c:6284 #, c-format msgid "E413: Too many arguments: \":highlight link %s\"" msgstr "E413: Te veel parameters: \":highlight link %s\"" -#: ../syntax.c:6302 msgid "E414: group has settings, highlight link ignored" msgstr "" "E414: groep het instellings, uitligskakel ('highlight link') gegnoreer" -#: ../syntax.c:6367 #, c-format msgid "E415: unexpected equal sign: %s" msgstr "E415: onverwagte gelykaanteken: %s" -#: ../syntax.c:6395 #, c-format msgid "E416: missing equal sign: %s" msgstr "E416: ontbrekende gelykaanteken: %s" -#: ../syntax.c:6418 #, c-format msgid "E417: missing argument: %s" msgstr "E417: ontbrekende parameter: %s" -#: ../syntax.c:6446 #, c-format msgid "E418: Illegal value: %s" msgstr "E418: Ongeldige waarde: %s" -#: ../syntax.c:6496 msgid "E419: FG color unknown" msgstr "E419: FG kleur onbekend" -#: ../syntax.c:6504 msgid "E420: BG color unknown" msgstr "E420: BG kleur onbekend" -#: ../syntax.c:6564 #, c-format msgid "E421: Color name or number not recognized: %s" msgstr "E421: Kleurnaam of -nommer nie herken nie: %s" -#: ../syntax.c:6714 -#, c-format -msgid "E422: terminal code too long: %s" -msgstr "E422: terminaalkode te lank: %s" - -#: ../syntax.c:6753 #, c-format msgid "E423: Illegal argument: %s" msgstr "E423: Ongeldige parameter: %s" -#: ../syntax.c:6925 msgid "E424: Too many different highlighting attributes in use" msgstr "E424: Te veel verskillende uitlig-eienskappe in gebruik" -#: ../syntax.c:7427 msgid "E669: Unprintable character in group name" msgstr "E669: Onvertoonbare karakter in groepnaam" -#: ../syntax.c:7434 msgid "W18: Invalid character in group name" msgstr "W18: Ongeldige karakter groepnaam" -#: ../syntax.c:7448 -msgid "E849: Too many highlight and syntax groups" -msgstr "" +#~ msgid "E849: Too many highlight and syntax groups" +#~ msgstr "" -#: ../tag.c:104 msgid "E555: at bottom of tag stack" msgstr "E555: onderaan etiketstapel" -#: ../tag.c:105 msgid "E556: at top of tag stack" msgstr "E556: bo-aan etiketstapel" -#: ../tag.c:380 msgid "E425: Cannot go before first matching tag" msgstr "E425: Kan nie vr eerste etiket-treffer gaan nie" -#: ../tag.c:504 #, c-format msgid "E426: tag not found: %s" msgstr "E426: etiket nie gevind nie: %s" -#: ../tag.c:528 msgid " # pri kind tag" msgstr " # pri tipe etiket" -#: ../tag.c:531 msgid "file\n" msgstr "ler\n" -#: ../tag.c:829 msgid "E427: There is only one matching tag" msgstr "E427: Daar is slegs een etiket-treffer" -#: ../tag.c:831 msgid "E428: Cannot go beyond last matching tag" msgstr "E428: Kan nie verby laaste etiket-treffer gaan nie" -#: ../tag.c:850 #, c-format msgid "File \"%s\" does not exist" msgstr "Ler \"%s\" bestaan nie" #. Give an indication of the number of matching tags -#: ../tag.c:859 #, c-format msgid "tag %d of %d%s" msgstr "etiket %d van %d%s" -#: ../tag.c:862 msgid " or more" msgstr " of meer" -#: ../tag.c:864 msgid " Using tag with different case!" msgstr " Gaan etiket met ander kas gebruik!" -#: ../tag.c:909 #, c-format msgid "E429: File \"%s\" does not exist" msgstr "E429: Ler \"%s\" bestaan nie" #. Highlight title -#: ../tag.c:960 msgid "" "\n" " # TO tag FROM line in file/text" @@ -6054,275 +5387,173 @@ msgstr "" "\n" " # NA etiket VAN rel in ler/teks" -#: ../tag.c:1303 #, c-format msgid "Searching tags file %s" msgstr "Deursoek etiketler %s" -#: ../tag.c:1545 -msgid "Ignoring long line in tags file" -msgstr "" +#~ msgid "Ignoring long line in tags file" +#~ msgstr "" -#: ../tag.c:1915 #, c-format msgid "E431: Format error in tags file \"%s\"" msgstr "E431: Formaatfout in etiketler \"%s\"" -#: ../tag.c:1917 #, c-format msgid "Before byte %<PRId64>" msgstr "Voor greep %<PRId64>" -#: ../tag.c:1929 #, c-format msgid "E432: Tags file not sorted: %s" msgstr "E432: Etiketler ongesorteer: %s" #. never opened any tags file -#: ../tag.c:1960 msgid "E433: No tags file" msgstr "E433: Geen etiketler nie" -#: ../tag.c:2536 msgid "E434: Can't find tag pattern" msgstr "E434: Kan nie etiketpatroon vind nie" -#: ../tag.c:2544 msgid "E435: Couldn't find tag, just guessing!" msgstr "E435: Kon nie etiket vind nie, ek raai maar!" -#: ../tag.c:2797 #, fuzzy, c-format -msgid "Duplicate field name: %s" -msgstr "gelaaide fontnaam: %s" - -#: ../term.c:1442 -msgid "' not known. Available builtin terminals are:" -msgstr "' onbekend. Beskikbare ingeboude terminale is:" - -#: ../term.c:1463 -msgid "defaulting to '" -msgstr "gebruik verstek '" - -#: ../term.c:1731 -msgid "E557: Cannot open termcap file" -msgstr "E557: Kan nie 'termcap'-ler oopmaak nie" - -#: ../term.c:1735 -msgid "E558: Terminal entry not found in terminfo" -msgstr "E558: Terminaalinskrywing nie in 'terminfo' gevind nie" - -#: ../term.c:1737 -msgid "E559: Terminal entry not found in termcap" -msgstr "E559: Terminaalinskrywing nie in 'termcap' gevind nie" - -#: ../term.c:1878 -#, c-format -msgid "E436: No \"%s\" entry in termcap" -msgstr "E436: Geen \"%s\" inskrywing in termcap nie" - -#: ../term.c:2249 -msgid "E437: terminal capability \"cm\" required" -msgstr "E437: terminaalvermo \"cm\" vereis" - -#. Highlight title -#: ../term.c:4376 -msgid "" -"\n" -"--- Terminal keys ---" -msgstr "" -"\n" -"--- Terminaal sleutels ---" - -#: ../ui.c:481 -msgid "Vim: Error reading input, exiting...\n" -msgstr "Vim: Fout met lees van invoer, verlaat...\n" +#~ msgid "Duplicate field name: %s" +#~ msgstr "gelaaide fontnaam: %s" #. This happens when the FileChangedRO autocommand changes the #. * file in a way it becomes shorter. -#: ../undo.c:379 -msgid "E881: Line count changed unexpectedly" -msgstr "" +#~ msgid "E881: Line count changed unexpectedly" +#~ msgstr "" -#: ../undo.c:627 #, fuzzy, c-format -msgid "E828: Cannot open undo file for writing: %s" -msgstr "E212: Kan ler nie oopmaak vir skryf nie" +#~ msgid "E828: Cannot open undo file for writing: %s" +#~ msgstr "E212: Kan ler nie oopmaak vir skryf nie" + +#, fuzzy, c-format +#~ msgid "E5003: Unable to create directory \"%s\" for undo file: %s" +#~ msgstr "E346: Geen gids \"%s\" meer gevind in 'cdpath' nie" -#: ../undo.c:717 #, c-format -msgid "E825: Corrupted undo file (%s): %s" -msgstr "" +#~ msgid "E825: Corrupted undo file (%s): %s" +#~ msgstr "" -#: ../undo.c:1039 -msgid "Cannot write undo file in any directory in 'undodir'" -msgstr "" +#~ msgid "Cannot write undo file in any directory in 'undodir'" +#~ msgstr "" -#: ../undo.c:1074 #, c-format -msgid "Will not overwrite with undo file, cannot read: %s" -msgstr "" +#~ msgid "Will not overwrite with undo file, cannot read: %s" +#~ msgstr "" -#: ../undo.c:1092 #, c-format -msgid "Will not overwrite, this is not an undo file: %s" -msgstr "" +#~ msgid "Will not overwrite, this is not an undo file: %s" +#~ msgstr "" -#: ../undo.c:1108 -msgid "Skipping undo file write, nothing to undo" -msgstr "" +#~ msgid "Skipping undo file write, nothing to undo" +#~ msgstr "" -#: ../undo.c:1121 #, fuzzy, c-format -msgid "Writing undo file: %s" -msgstr "Besig om viminfo ler \"%s\" te stoor" +#~ msgid "Writing undo file: %s" +#~ msgstr "Besig om viminfo ler \"%s\" te stoor" -#: ../undo.c:1213 #, fuzzy, c-format -msgid "E829: write error in undo file: %s" -msgstr "E297: Skryffout in ruiller" +#~ msgid "E829: write error in undo file: %s" +#~ msgstr "E297: Skryffout in ruiller" -#: ../undo.c:1280 #, c-format -msgid "Not reading undo file, owner differs: %s" -msgstr "" +#~ msgid "Not reading undo file, owner differs: %s" +#~ msgstr "" -#: ../undo.c:1292 #, fuzzy, c-format -msgid "Reading undo file: %s" -msgstr "Besig om viminfo ler \"%s\"%s%s%s te lees" +#~ msgid "Reading undo file: %s" +#~ msgstr "Besig om viminfo ler \"%s\"%s%s%s te lees" -#: ../undo.c:1299 #, fuzzy, c-format -msgid "E822: Cannot open undo file for reading: %s" -msgstr "E195: Kan 'viminfo' ler nie oopmaak om te lees nie" +#~ msgid "E822: Cannot open undo file for reading: %s" +#~ msgstr "E195: Kan 'viminfo' ler nie oopmaak om te lees nie" -#: ../undo.c:1308 #, fuzzy, c-format -msgid "E823: Not an undo file: %s" -msgstr "E484: Kan nie ler %s oopmaak nie" +#~ msgid "E823: Not an undo file: %s" +#~ msgstr "E484: Kan nie ler %s oopmaak nie" -#: ../undo.c:1313 #, fuzzy, c-format -msgid "E824: Incompatible undo file: %s" -msgstr "E484: Kan nie ler %s oopmaak nie" +#~ msgid "E824: Incompatible undo file: %s" +#~ msgstr "E484: Kan nie ler %s oopmaak nie" -#: ../undo.c:1328 -msgid "File contents changed, cannot use undo info" -msgstr "" +#~ msgid "File contents changed, cannot use undo info" +#~ msgstr "" -#: ../undo.c:1497 #, fuzzy, c-format -msgid "Finished reading undo file %s" -msgstr "%s klaar uitgevoer" +#~ msgid "Finished reading undo file %s" +#~ msgstr "%s klaar uitgevoer" -#: ../undo.c:1586 ../undo.c:1812 -msgid "Already at oldest change" -msgstr "" +#~ msgid "Already at oldest change" +#~ msgstr "" -#: ../undo.c:1597 ../undo.c:1814 -msgid "Already at newest change" -msgstr "" +#~ msgid "Already at newest change" +#~ msgstr "" -#: ../undo.c:1806 #, fuzzy, c-format -msgid "E830: Undo number %<PRId64> not found" -msgstr "E92: buffer %<PRId64> kon nie gevind word nie" +#~ msgid "E830: Undo number %<PRId64> not found" +#~ msgstr "E92: buffer %<PRId64> kon nie gevind word nie" -#: ../undo.c:1979 msgid "E438: u_undo: line numbers wrong" msgstr "E438: u_undo: relnommers foutief" -#: ../undo.c:2183 #, fuzzy -msgid "more line" -msgstr "1 rel meer" +#~ msgid "more line" +#~ msgstr "1 rel meer" -#: ../undo.c:2185 #, fuzzy -msgid "more lines" -msgstr "1 rel meer" +#~ msgid "more lines" +#~ msgstr "1 rel meer" -#: ../undo.c:2187 #, fuzzy -msgid "line less" -msgstr "1 rel minder" +#~ msgid "line less" +#~ msgstr "1 rel minder" -#: ../undo.c:2189 #, fuzzy -msgid "fewer lines" -msgstr "%<PRId64> minder rels" +#~ msgid "fewer lines" +#~ msgstr "%<PRId64> minder rels" -#: ../undo.c:2193 #, fuzzy -msgid "change" -msgstr "1 verandering" +#~ msgid "change" +#~ msgstr "1 verandering" -#: ../undo.c:2195 #, fuzzy -msgid "changes" -msgstr "1 verandering" +#~ msgid "changes" +#~ msgstr "1 verandering" -#: ../undo.c:2225 #, fuzzy, c-format -msgid "%<PRId64> %s; %s #%<PRId64> %s" -msgstr "%<PRId64>R, %<PRId64>K" +#~ msgid "%<PRId64> %s; %s #%<PRId64> %s" +#~ msgstr "%<PRId64>R, %<PRId64>K" -#: ../undo.c:2228 -msgid "before" -msgstr "" +#~ msgid "after" +#~ msgstr "" -#: ../undo.c:2228 -msgid "after" -msgstr "" +#~ msgid "before" +#~ msgstr "" -#: ../undo.c:2325 #, fuzzy -msgid "Nothing to undo" -msgstr "Geen binding gevind nie" +#~ msgid "Nothing to undo" +#~ msgstr "Geen binding gevind nie" -#: ../undo.c:2330 -msgid "number changes when saved" -msgstr "" +#~ msgid "number changes when saved" +#~ msgstr "" -#: ../undo.c:2360 #, fuzzy, c-format -msgid "%<PRId64> seconds ago" -msgstr "%<PRId64> Kolomme; " +#~ msgid "%<PRId64> seconds ago" +#~ msgstr "%<PRId64> Kolomme; " -#: ../undo.c:2372 #, fuzzy -msgid "E790: undojoin is not allowed after undo" -msgstr "E407: %s nie toegelaat hier nie" +#~ msgid "E790: undojoin is not allowed after undo" +#~ msgstr "E407: %s nie toegelaat hier nie" -#: ../undo.c:2466 msgid "E439: undo list corrupt" msgstr "E439: herstellys korrup" -#: ../undo.c:2495 msgid "E440: undo line missing" msgstr "E440: herstelrel ontbreek" -#: ../version.c:600 -msgid "" -"\n" -"Included patches: " -msgstr "" -"\n" -"Ingeslote laslappies:" - -#: ../version.c:627 -#, fuzzy -msgid "" -"\n" -"Extra patches: " -msgstr "Eksterne subtreffers:\n" - -#: ../version.c:639 ../version.c:864 -msgid "Modified by " -msgstr "Gewysig deur " - -#: ../version.c:646 msgid "" "\n" "Compiled " @@ -6330,933 +5561,919 @@ msgstr "" "\n" "Gekompileer op " -#: ../version.c:649 msgid "by " msgstr "deur " -#: ../version.c:660 -msgid "" -"\n" -"Huge version " -msgstr "" -"\n" -"Enorme weergawe " - -#: ../version.c:661 -msgid "without GUI." -msgstr "sonder GUI." - -#: ../version.c:662 -msgid " Features included (+) or not (-):\n" -msgstr " Kenmerke in- (+) of uitgesluit (-):\n" +#~ msgid "" +#~ "\n" +#~ "\n" +#~ "Features: " +#~ msgstr "" -#: ../version.c:667 msgid " system vimrc file: \"" msgstr " stelsel vimrc-ler: \"" -#: ../version.c:672 -msgid " user vimrc file: \"" -msgstr " gebruiker vimrc-ler: \"" - -#: ../version.c:677 -msgid " 2nd user vimrc file: \"" -msgstr " 2de gebruiker vimrc-ler \"" - -#: ../version.c:682 -msgid " 3rd user vimrc file: \"" -msgstr " 3de gebruiker vimrc-ler \"" - -#: ../version.c:687 -msgid " user exrc file: \"" -msgstr " gebruiker exrc-ler: \"" - -#: ../version.c:692 -msgid " 2nd user exrc file: \"" -msgstr " 2de gebruiker exrc-ler: \"" - -#: ../version.c:699 msgid " fall-back for $VIM: \"" msgstr " bystand vir $VIM: \"" -#: ../version.c:705 msgid " f-b for $VIMRUNTIME: \"" msgstr " bystand vir $VIMRUNTIME: \"" -#: ../version.c:709 -msgid "Compilation: " -msgstr "Kompilering: " - -#: ../version.c:712 -msgid "Linking: " -msgstr "Koppeling: " +#, fuzzy +#~ msgid "Nvim is open source and freely distributable" +#~ msgstr "Vim is vryekode, en vrylik verspreibaar" -#: ../version.c:717 -msgid " DEBUG BUILD" -msgstr " ONTFOUTINGS-KOMPILERING" +#~ msgid "https://neovim.io/community" +#~ msgstr "" -#: ../version.c:767 -msgid "VIM - Vi IMproved" -msgstr "VIM - Vi Met skop" +#, fuzzy +#~ msgid "type :help nvim<Enter> if you are new! " +#~ msgstr "tik :help uganda<Enter> as jy hou van Vim " -# njj: :)) -#: ../version.c:769 -msgid "version " -msgstr "Weergawe " +#, fuzzy +#~ msgid "type :checkhealth<Enter> to optimize Nvim" +#~ msgstr "Tik :quit<Enter> om Vim te verlaat" -#: ../version.c:770 -msgid "by Bram Moolenaar et al." -msgstr "deur Bram Moolenaar et al." +msgid "type :q<Enter> to exit " +msgstr "tik :q<Enter> om program verlaat " -#: ../version.c:774 -msgid "Vim is open source and freely distributable" -msgstr "Vim is vryekode, en vrylik verspreibaar" +#, fuzzy +#~ msgid "type :help<Enter> for help " +#~ msgstr "tik :q<Enter> om program verlaat " -#: ../version.c:776 msgid "Help poor children in Uganda!" msgstr "Help arm kinders in Uganda!" -#: ../version.c:777 msgid "type :help iccf<Enter> for information " msgstr "tik :help iccf<Enter> vir meer inligting hieroor " -#: ../version.c:779 -msgid "type :q<Enter> to exit " -msgstr "tik :q<Enter> om program verlaat " - -#: ../version.c:780 -msgid "type :help<Enter> or <F1> for on-line help" -msgstr "tik :help<Enter> of <F1> vir aanlyn hulp " - -#: ../version.c:781 -msgid "type :help version7<Enter> for version info" -msgstr "tik :help version7<Enter> vir weergawe-inligting" - -#: ../version.c:784 -msgid "Running in Vi compatible mode" -msgstr "Voer tans uit in Vi-versoenbare modus" - -#: ../version.c:785 -msgid "type :set nocp<Enter> for Vim defaults" -msgstr "tik :set nocp<Enter> vir Vim verstekwaardes " - -#: ../version.c:786 -msgid "type :help cp-default<Enter> for info on this" -msgstr "tik :help cp-default<Enter> vir meer inligting hieroor" - -#: ../version.c:827 msgid "Sponsor Vim development!" msgstr "Borg Vim ontwikkeling!" -#: ../version.c:828 msgid "Become a registered Vim user!" msgstr "Word 'n geregistreerde Vim gebruiker!" -#: ../version.c:831 msgid "type :help sponsor<Enter> for information " msgstr "tik :help sponsor<Enter> vir meer inligting hieroor " -#: ../version.c:832 msgid "type :help register<Enter> for information " msgstr "tik :help register<Enter> vir meer inligting hieroor " -#: ../version.c:834 msgid "menu Help->Sponsor/Register for information " msgstr "menu Hulp->Borg/Registreer vir meer inligting" -#: ../window.c:119 msgid "Already only one window" msgstr "Daar is alreeds slegs een venster" -#: ../window.c:224 msgid "E441: There is no preview window" msgstr "E441: Daar is nie 'n voorskou-venster nie" -#: ../window.c:559 msgid "E442: Can't split topleft and botright at the same time" msgstr "E442: Kan nie bo-links en onder-regs terselfdertyd verdeel nie" -#: ../window.c:1228 msgid "E443: Cannot rotate when another window is split" msgstr "E443: Kan nie roteer terwyl 'n ander venster verdeel is nie" -#: ../window.c:1803 msgid "E444: Cannot close last window" msgstr "E444: Kan nie laaste venster toemaak nie" -#: ../window.c:1810 #, fuzzy -msgid "E813: Cannot close autocmd window" -msgstr "E444: Kan nie laaste venster toemaak nie" +#~ msgid "E813: Cannot close autocmd window" +#~ msgstr "E444: Kan nie laaste venster toemaak nie" -#: ../window.c:1814 #, fuzzy -msgid "E814: Cannot close window, only autocmd window would remain" -msgstr "E444: Kan nie laaste venster toemaak nie" +#~ msgid "E814: Cannot close window, only autocmd window would remain" +#~ msgstr "E444: Kan nie laaste venster toemaak nie" -#: ../window.c:2717 msgid "E445: Other window contains changes" msgstr "E445: Die ander venster bevat veranderinge" -#: ../window.c:4805 msgid "E446: No file name under cursor" msgstr "E446: Geen lernaam onder loper" -#~ msgid "[Error List]" -#~ msgstr "[Foutlys]" - -#~ msgid "[No File]" -#~ msgstr "[Geen ler]" +#, c-format +#~ msgid "E799: Invalid ID: %<PRId64> (must be greater than or equal to 1)" +#~ msgstr "" -#~ msgid "Patch file" -#~ msgstr "Laslap ler" +#, fuzzy, c-format +#~ msgid "E801: ID already taken: %<PRId64>" +#~ msgstr "E157: Ongeldige teken ID: %<PRId64>" -#~ msgid "E106: Unknown variable: \"%s\"" -#~ msgstr "E106: Onbekende veranderlike: \"%s\"" +#, fuzzy +#~ msgid "List or number required" +#~ msgstr "E471: Parameter benodig" -#~ msgid "" -#~ "&OK\n" -#~ "&Cancel" +#, c-format +#~ msgid "E802: Invalid ID: %<PRId64> (must be greater than or equal to 1)" #~ msgstr "" -#~ "&OK\n" -#~ "&Kanselleer" -#~ msgid "E240: No connection to Vim server" -#~ msgstr "E240: Geen verbinding met Vim bediener" - -#~ msgid "E277: Unable to read a server reply" -#~ msgstr "E277: Kon bediener-terugvoer nie lees nie" +#, fuzzy, c-format +#~ msgid "E803: ID not found: %<PRId64>" +#~ msgstr "E320: Kan nie rel %<PRId64> vind nie" -#~ msgid "E241: Unable to send to %s" -#~ msgstr "E241: Kan nie na %s stuur nie" +#~ msgid "WARNING: tag command changed a buffer!!!" +#~ msgstr "WAARSKUWING: etiketbevel het buffer verander!!!" -#~ msgid "E130: Undefined function: %s" -#~ msgstr "E130: Ongedefinieerde funksie: %s" +#~ msgid "Leave: %s" +#~ msgstr "Verlaat: %s" -#~ msgid "Save As" -#~ msgstr "Stoor As" +#~ msgid "File name '%s' is valid" +#~ msgstr "lernaam '%s is ongeldig" -#~ msgid "Source Vim script" -#~ msgstr "Voer Vim skrip uit" +#~ msgid "Not a proper file name: '%s'" +#~ msgstr "Nie 'n geldige lernaam nie: '%s'" -#~ msgid "Edit File" -#~ msgstr "Verander ler" +#~ msgid "Change dir debugging enabled." +#~ msgstr "Verandergids ontfouting in staat gestel" -#~ msgid " (NOT FOUND)" -#~ msgstr " (NIE GEVIND NIE)" +#~ msgid "Warning: %s option changed from modeline" +#~ msgstr "Waarskuwing: %s opsie verander vanaf moduslyn" -#~ msgid "Edit File in new window" -#~ msgstr "Bewerk ler in nuwe venster" +#~ msgid "Security error: shell command output is a symbolic link" +#~ msgstr "Sekuriteitsfout: Dop-bevel afvoer is 'n simboliese skakel" -#~ msgid "Append File" -#~ msgstr "Las aan by ler" +#~ msgid "No fold at this line" +#~ msgstr "Geen vou by hierdie rel nie" -#~ msgid "Window position: X %d, Y %d" -#~ msgstr "Vensterposisie: X %d, Y %d" +#~ msgid "Fold must be at least two lines" +#~ msgstr "'n Vou moet ten minste 2 rels wees" -#~ msgid "Save Redirection" -#~ msgstr "Stoor Herversturing" +#~ msgid "Security error: filter input is a symbolic link: %s" +#~ msgstr "Sekuriteitsfout: filter invoer is 'n simboliese skakel" -#~ msgid "Save View" -#~ msgstr "Stoor Oorsig" +#~ msgid "Security error: 'charconvert' output is a symbolic link" +#~ msgstr "Sekuriteitsfout: 'charconvert' afvoer is 'n simboliese skakel" -#~ msgid "Save Session" -#~ msgstr "Stoor Sessie" +#~ msgid "Security error: filter output is a symbolic link: %s" +#~ msgstr "Sekuriteitsfout: filter afvoer is 'n simboliese skakel" -#~ msgid "Save Setup" -#~ msgstr "Stoor konfigurasie" +#~ msgid "makeef option not set" +#~ msgstr "'makeef' opsie nie aan nie" -#~ msgid "E196: No digraphs in this version" -#~ msgstr "E196: Geen digrawe in hierdie weergawe nie" +#~ msgid "line ~%<PRId64>: %s" +#~ msgstr "rel ~%<PRId64>: %s" -#~ msgid "[NL found]" -#~ msgstr "[NL gevind]" +#~ msgid "Security error: new viminfo file is a symbolic link" +#~ msgstr "Sekuriteitsfout: nuwe viminfo ler is a simboliese skakel" -#~ msgid "[crypted]" -#~ msgstr "[gekodeer]" +#~ msgid " PPC has a much better architecture. " +#~ msgstr " PPC het 'n veel beter argitektuur. " -#~ msgid "[CONVERSION ERROR]" -#~ msgstr "[OMSETTINGSFOUT]" +#~ msgid " WARNING: Intel CPU detected. " +#~ msgstr " WAARSKUWING: Intel SVE bespeur. " -#~ msgid "NetBeans disallows writes of unmodified buffers" -#~ msgstr "NetBeans laat nie skryf toe van onveranderde buffers nie" +#~ msgid "Unexpected magic character; check META." +#~ msgstr "Onverwagte toorkarakter; kyk na META." -#~ msgid "Partial writes disallowed for NetBeans buffers" -#~ msgstr "Gedeeltelike skryf word nie toegelaat vir NetBeans buffers nie" +#~ msgid "\\* follows nothing" +#~ msgstr "\\* volg niks" -#~ msgid "E460: The resource fork would be lost (add ! to override)" -#~ msgstr "E460: Die hulpbronvurk sal verlore gaan (gebruik ! om te dwing)" +#~ msgid "\\{ follows nothing" +#~ msgstr "\\{ volg niks" -#~ msgid "<cannot open> " -#~ msgstr "<kan nie oopmaak nie> " +#~ msgid "\\@ follows nothing" +#~ msgstr "\\@ volg niks" -#~ msgid "E616: vim_SelFile: can't get font %s" -#~ msgstr "E616: 'vim_SelFile': kan font %s nie kry nie" +#~ msgid "\\+ follows nothing" +#~ msgstr "\\+ volg niks" -#~ msgid "E614: vim_SelFile: can't return to current directory" -#~ msgstr "E614: 'vim_SelFile': Kan nie terugkeer na huidige gids nie" +#~ msgid "\\= follows nothing" +#~ msgstr "\\= volg niks" -#~ msgid "Pathname:" -#~ msgstr "Gidsnaam:" +#~ msgid "Nested *, \\=, \\+, \\! or \\{" +#~ msgstr "Geneste *, \\=, \\+, \\! of \\{" -#~ msgid "E615: vim_SelFile: can't get current directory" -#~ msgstr "E615: vim_SelFile: Kan nie huidige gids verkry nie" +#~ msgid "Unmatched \\(" +#~ msgstr "Onpaar \\(" -#~ msgid "OK" -#~ msgstr "OK" +#~ msgid "Too many \\(" +#~ msgstr "Te veel \\(" -#~ msgid "Cancel" -#~ msgstr "Kanselleer" +#~ msgid "Ambiguous mapping, conflicts with \"%s\"" +#~ msgstr "Dubbelsinnige binding, bots met \"%s\"" -#~ msgid "Vim dialog" -#~ msgstr "Vim dialooghokkie" +#~ msgid "Ambiguous mapping" +#~ msgstr "Dubbelsinnige binding" -#~ msgid "Scrollbar Widget: Could not get geometry of thumb pixmap." -#~ msgstr "" -#~ "Rolstaafelement: Kon nie pikselmatriks-duimnael se geometrie kry nie" +#~ msgid "Command too long" +#~ msgstr "Bevel te lank" -#~ msgid "E232: Cannot create BalloonEval with both message and callback" -#~ msgstr "E232: Kan nie BalloonEval skep met beide boodskap en terugroep nie" +#~ msgid "GUI is not running" +#~ msgstr "GUI voer nie uit nie" -#~ msgid "E229: Cannot start the GUI" -#~ msgstr "E229: Kan nie die GUI begin nie" +#~ msgid "Cannot clear all highlight groups" +#~ msgstr "Kan nie alle uitliggroepe leegmaak nie" -#~ msgid "E230: Cannot read from \"%s\"" -#~ msgstr "E230: Kan nie lees uit \"%s\" nie" +#~ msgid "Library call failed" +#~ msgstr "Biblioteekfunksieroep het gefaal" -#~ msgid "E665: Cannot start GUI, no valid font found" -#~ msgstr "E665: Kan nie GUI begin nie, geen geldige font gevind nie" +#~ msgid "some" +#~ msgstr "sommige" -#~ msgid "E231: 'guifontwide' invalid" -#~ msgstr "E231: 'guifontwide' ongeldig" +#~ msgid "deadly signal" +#~ msgstr "dodelike sein" -#~ msgid "E599: Value of 'imactivatekey' is invalid" -#~ msgstr "E599: Waarde van 'imactivatekey' is ongeldig" +#~ msgid "Unsupported screen mode" +#~ msgstr "Ongesteunde skermmodus" -#~ msgid "E254: Cannot allocate color %s" -#~ msgstr "E254: Kan nie kleur %s toeken nie" +#~ msgid "PC (16 bits Vim)" +#~ msgstr "PC (16 bisse Vim)" -#~ msgid "Vim dialog..." -#~ msgstr "Vim dialooghokkie..." +#~ msgid "PC (32 bits Vim)" +#~ msgstr "PC (32 bisse Vim)" -#~ msgid "Input _Methods" -#~ msgstr "Invoer _Metodes" +#~ msgid "Out of memory" +#~ msgstr "Geheue op" -#~ msgid "VIM - Search and Replace..." -#~ msgstr "VIM - Soek en Vervang..." +#~ msgid "Sorry, deleting a menu is not possible in the Athena version" +#~ msgstr "" +#~ "Jammer, in die Athena weergawe is dit onmoontlik om 'n kieslys te skrap" -#~ msgid "VIM - Search..." -#~ msgstr "VIM - Soek..." +#~ msgid "Can't create input context." +#~ msgstr "Kan nie invoerkonteks skep nie." -#~ msgid "Find what:" -#~ msgstr "Soek na:" +#~ msgid "Unrecognized sniff request [%s]" +#~ msgstr "Onbekende sniff versoek [%s]" -#~ msgid "Replace with:" -#~ msgstr "Vervang met:" +#~ msgid "-- SNiFF+ commands --" +#~ msgstr "-- SNiFF+ bevele --" -#~ msgid "Match whole word only" -#~ msgstr "Tref slegs presiese woord" +#~ msgid "Retrieve next symbol" +#~ msgstr "Kry volgende simbool" -#~ msgid "Match case" -#~ msgstr "Tref kas" +#~ msgid "cs_add_common: alloc fail #4" +#~ msgstr "'cs_add_common': toeken onsuksesvol #4" -#~ msgid "Direction" -#~ msgstr "Rigting" +#~ msgid "cs_add_common: alloc fail #3" +#~ msgstr "'cs_add_common': toeken onsuksesvol #3" -#~ msgid "Up" -#~ msgstr "Op" +#~ msgid "cs_add_common: alloc fail #2" +#~ msgstr "'cs_add_common': toeken onsuksesvol #2" -#~ msgid "Down" -#~ msgstr "Af" +#~ msgid "cs_add_common: alloc fail #1" +#~ msgstr "'cs_add_common': toeken onsuksesvol #1" -#~ msgid "Find Next" -#~ msgstr "Vind volgende" +#~ msgid "automata ERROR: internal" +#~ msgstr "automata FOUT: intern" -#~ msgid "Replace" -#~ msgstr "Vervang" +#~ msgid "Your language Font missing" +#~ msgstr "Jou taal Font ontbreek" -#~ msgid "Replace All" -#~ msgstr "Vervang alles" +#~ msgid "fontset name: %s" +#~ msgstr "fontstel naam: %s" -#~ msgid "Vim: Received \"die\" request from session manager\n" -#~ msgstr "Vim: Het die \"die\" opdrag ontvang van sessiebestuurder\n" +#~ msgid " sh : export LANG=ko" +#~ msgstr " sh: export LANG=af" -#~ msgid "Vim: Main window unexpectedly destroyed\n" -#~ msgstr "Vim: Hoofvenster onverwags verwoes\n" +#~ msgid " csh: setenv LANG ko" +#~ msgstr " csh: setenv LANG af" -#~ msgid "Font Selection" -#~ msgstr "Fontkeuse" +#~ msgid "For korean:" +#~ msgstr "Vir Afrikaans:" -#~ msgid "Used CUT_BUFFER0 instead of empty selection" -#~ msgstr "'CUT_BUFFER0' is gebruik in plaas van le seleksie" +#~ msgid "locale is not set correctly" +#~ msgstr "lokaal is nie korrek gestel nie" -#~ msgid "Filter" -#~ msgstr "Filter" +#~ msgid "Error: During loading fontset %s" +#~ msgstr "Fout: Gedurende die laai van fontstel %s" -#~ msgid "Directories" -#~ msgstr "Gidse" +#~ msgid "Topic:" +#~ msgstr "Onderwerp:" -#~ msgid "Help" -#~ msgstr "Hulp" +#~ msgid "VIM - Help on..." +#~ msgstr "VIM - Hulp met.." -#~ msgid "Files" -#~ msgstr "Lers" +#~ msgid "Cannot use :normal from event handler" +#~ msgstr "Kan ':normal' nie vanuit gebeurtenishanteerder gebruik nie" -#~ msgid "Selection" -#~ msgstr "Seleksie" +#~ msgid "Invalid line number: %<PRId64>" +#~ msgstr "Ongeldige relnommer: %<PRId64>" -#~ msgid "Undo" -#~ msgstr "Herroep" +#~ msgid "Missing filename" +#~ msgstr "Ontbrekende lernaam" -#~ msgid "E671: Cannot find window title \"%s\"" -#~ msgstr "E671: Kan nie venster titel vind nie \"%s\"" +#~ msgid "No servers found for this display" +#~ msgstr "Geen bedieners gevind vir die 'display' nie" -#~ msgid "E243: Argument not supported: \"-%s\"; Use the OLE version." -#~ msgstr "E243: Parameter nie bekend: \"-%s\"; Gebruik die OLE weergawe." +#~ msgid "E258: no matches found in cscope connections" +#~ msgstr "E258: geen treffers gevind in 'cscope' verbindings nie" -#~ msgid "E672: Unable to open window inside MDI application" -#~ msgstr "E672: Kon nie venster oopmaak binne 'n MDI toepassing nie" +#~ msgid "Binary tag search" +#~ msgstr "Binre etiketsoek" -#~ msgid "Find string (use '\\\\' to find a '\\')" -#~ msgstr "Vind string (gebruik '\\\\' om 'n '\\' te vind" +#~ msgid "Linear tag search" +#~ msgstr "Linire etiketsoek" -#~ msgid "Find & Replace (use '\\\\' to find a '\\')" -#~ msgstr "Vind & vervang string (gebruik '\\\\' om 'n '\\' te vind" +#~ msgid " LINE" +#~ msgstr " REL" -#~ msgid "" -#~ "Vim E458: Cannot allocate colormap entry, some colors may be incorrect" -#~ msgstr "" -#~ "Vim E458: Kan nie kleurkaart-inskrywing toeken nie, sommige kleure mag " -#~ "verkeerd wees" +#~ msgid " BLOCK" +#~ msgstr " BLOK" -#~ msgid "E250: Fonts for the following charsets are missing in fontset %s:" -#~ msgstr "" -#~ "E250: Fonte vir die volgende karakterstelle ontbreek in fontversameling " -#~ "%s:" +#~ msgid "%<PRId64> lines ~ed" +#~ msgstr "%<PRId64> rels ge-~" -#~ msgid "E252: Fontset name: %s" -#~ msgstr "E252: Fontstel naam: %s" +#~ msgid "1 line ~ed" +#~ msgstr "1 rel ge-~" -#~ msgid "Font '%s' is not fixed-width" -#~ msgstr "Font '%s' is nie 'n vaste-wydte font nie" +#~ msgid "--help\t\tShow Gnome arguments" +#~ msgstr "--help\t\tWys Gnome parameters" -#~ msgid "E253: Fontset name: %s\n" -#~ msgstr "E253: Fonstel naam: %s\n" +#~ msgid "\"\n" +#~ msgstr "\"\n" -#~ msgid "Font0: %s\n" -#~ msgstr "Font0: %s\n" +#~ msgid "E249: couldn't read VIM instance registry property" +#~ msgstr "E249: kon nie VIM instansie register-kenmerk lees nie" -#~ msgid "Font1: %s\n" -#~ msgstr "Font1: %s\n" +#~ msgid "%2d %-5ld %-34s <none>\n" +#~ msgstr "%2d %-5ld %-34s <geen>\n" -#~ msgid "Font%<PRId64> width is not twice that of font0\n" -#~ msgstr "Font%<PRId64> wydte is nie twee keer de van font0 nie\n" +# njj: dalk 'verbinding' ipv 'verbinding' orals? +#~ msgid "couldn't malloc\n" +#~ msgstr "kon nie 'malloc' nie\n" -#~ msgid "Font0 width: %<PRId64>\n" -#~ msgstr "Font0 wydte: %<PRId64>\n" +#~ msgid "E260: cscope connection not found" +#~ msgstr "E260: 'cscope' verbinding nie gevind nie" -#~ msgid "" -#~ "Font1 width: %<PRId64>\n" -#~ "\n" -#~ msgstr "" -#~ "Font1 wydte: %<PRId64>\n" -#~ "\n" +#~ msgid "error reading cscope connection %d" +#~ msgstr "'cscope' verbinding %d kon nie gelees word nie" -#~ msgid "E256: Hangul automata ERROR" -#~ msgstr "E256: Hangul outomatiserings FOUT" +#~ msgid "Run Macro" +#~ msgstr "Voer Makro uit" -#~ msgid "E563: stat error" -#~ msgstr "E563: 'stat' fout" +#~ msgid "function " +#~ msgstr "funksie " -#~ msgid "E625: cannot open cscope database: %s" -#~ msgstr "E625: Kon nie 'cscope' databasis oopmaak nie: %s" +#~ msgid "E463: Region is guarded, cannot modify" +#~ msgstr "E463: Omgewing is onder bewaking, kan nie verander nie" -#~ msgid "E626: cannot get cscope database information" -#~ msgstr "E626: kan nie 'cscope' databasisinligting kry nie" +#~ msgid "E233: cannot open display" +#~ msgstr "E233: kan nie vertoonskerm oopmaak nie" -#~ msgid "E569: maximum number of cscope connections reached" -#~ msgstr "E569: maksimum aantal 'cscope' verbindings bereik" +#~ msgid "E247: no registered server named \"%s\"" +#~ msgstr "E247: geen geregistreerde bediener genaamd \"%s\"" -#~ msgid "" -#~ "E263: Sorry, this command is disabled, the Python library could not be " -#~ "loaded." +#~ msgid "E800: Arabic cannot be used: Not enabled at compile time\n" #~ msgstr "" -#~ "E263: Jammer, hierdie bevel is afgeskakel, die Python biblioteek ler kon " -#~ "nie gelaai word nie." +#~ "E800: Arabies kan nie gebruik word nie: Nie tydens kompilering gekies " +#~ "nie\n" -#~ msgid "E659: Cannot invoke Python recursively" -#~ msgstr "E659: Kan nie Python rekursief roep nie" +#~ msgid "E27: Farsi cannot be used: Not enabled at compile time\n" +#~ msgstr "" +#~ "E27: Farsi kan nie gebruik word nie: Nie tydens kompilering gekies nie\n" -#~ msgid "can't delete OutputObject attributes" -#~ msgstr "kan nie 'OutputObject' eienskappe skrap nie" +#~ msgid "E26: Hebrew cannot be used: Not enabled at compile time\n" +#~ msgstr "" +#~ "E26: Hebreeus kan nie gebruik word nie: Nie tydens kompilering gekies " +#~ "nie\n" -#~ msgid "softspace must be an integer" -#~ msgstr "'softspace' moet 'n heelgetal wees" +#~ msgid "E448: Could not load library function %s" +#~ msgstr "E448: Kon nie biblioteek funksie laai nie %s" -#~ msgid "invalid attribute" -#~ msgstr "ongeldige eienskap" +#~ msgid "E234: Unknown fontset: %s" +#~ msgstr "E234: Onbekende fontstel: %s" -#~ msgid "writelines() requires list of strings" -#~ msgstr "'writelines()' benodig 'n lys van stringe" +#~ msgid "Path length too long!" +#~ msgstr "Pad-lengte te lank" -#~ msgid "E264: Python: Error initialising I/O objects" -#~ msgstr "E264: Python: Kon nie I/O objekte inwy nie" +#~ msgid "gvimext.dll error" +#~ msgstr "'gvimext.dll' fout" -# njj: net 'n voorstel .. -#~ msgid "invalid expression" -#~ msgstr "ongeldige uitdrukking" +#~ msgid "Error creating process: Check if gvim is in your path!" +#~ msgstr "FOut met die skep van proses: Kyk of gvim in jou pad is!" -#~ msgid "expressions disabled at compile time" -#~ msgstr "uitdrukkings afgeskakel tydens kompilering" +#~ msgid "Edits the selected file(s) with Vim" +#~ msgstr "Wysig die gekose ler(s) met Vim" -#~ msgid "attempt to refer to deleted buffer" -#~ msgstr "poging om na 'n geskrapte buffer te verwys" +#~ msgid "Edit with existing Vim - &" +#~ msgstr "Wysig met bestaande Vim - &" -#~ msgid "line number out of range" -#~ msgstr "relnommer buite omvang" +#~ msgid "Edit with &Vim" +#~ msgstr "Wysig met &Vim" -#~ msgid "<buffer object (deleted) at %8lX>" -#~ msgstr "<buffervoorwerp (geskrap) by %8lX>" +#~ msgid "&Diff with Vim" +#~ msgstr "Wys verskille ('&diff') met Vim" -#~ msgid "invalid mark name" -#~ msgstr "onbekende merknaam" +#~ msgid "Edit with single &Vim" +#~ msgstr "Wysig met 'n enkel &Vim" -#~ msgid "no such buffer" -#~ msgstr "buffer bestaan nie" +#~ msgid "Edit with &multiple Vims" +#~ msgstr "Wysig met &meer as een Vim" -#~ msgid "attempt to refer to deleted window" -#~ msgstr "poging om na geskrapte venster te verwys" +#~ msgid "E299: Perl evaluation forbidden in sandbox without the Safe module" +#~ msgstr "" +#~ "E299: Perl evaluasie verbied in die sandput sonder die 'Safe' module" -#~ msgid "readonly attribute" -#~ msgstr "leesalleen eienskap" +#~ msgid "" +#~ "Sorry, this command is disabled: the Perl library could not be loaded." +#~ msgstr "" +#~ "Jammer, hierdie bevel is afgeskakel: die Perl biblioteek kon nie gelaai " +#~ "word nie." -#~ msgid "cursor position outside buffer" -#~ msgstr "loperposisie buite buffer" +#~ msgid "E370: Could not load library %s" +#~ msgstr "E370: Kon nie biblioteek laai nie %s" -#~ msgid "<window object (deleted) at %.8lX>" -#~ msgstr "<venster voorwerp (geskrap) by %.8lX>" +#~ msgid "type :help windows95<Enter> for info on this" +#~ msgstr "tik :help windows95<Enter> vir meer inligting hieroor" -#~ msgid "<window object (unknown) at %.8lX>" -#~ msgstr "<verwyder voorwerp (onbekend) by %.8lX>" +#~ msgid "WARNING: Windows 95/98/ME detected" +#~ msgstr "WAARSKUWING: Windows 95/98/ME bespeur" -#~ msgid "<window %d>" -#~ msgstr "<venster %d>" +#~ msgid " for Vim defaults " +#~ msgstr " vir Vim verstekwaardes" -#~ msgid "no such window" -#~ msgstr "geen sodanige venster nie" +#~ msgid "menu Edit->Global Settings->Toggle Vi Compatible" +#~ msgstr "menu Redigeer->Global verstellings->Stel en herstel Vi Versoenbaar" -#~ msgid "cannot save undo information" -#~ msgstr "kan nie herwin-inligting stoor nie" +#~ msgid "menu Edit->Global Settings->Toggle Insert Mode " +#~ msgstr "menu Redigeer->Globale verstellings->Stel en herstel Invoeg Modus" -#~ msgid "cannot delete line" -#~ msgstr "kan rel nie verwyder nie" +#~ msgid "Running modeless, typed text is inserted" +#~ msgstr "Voer modus-loos uit, getikte teks word ingevoeg" -#~ msgid "cannot replace line" -#~ msgstr "kan rel nie vervang nie" +#~ msgid "menu Help->Orphans for information " +#~ msgstr "menu Hulp->Weeskinders vir meer inligting hieroor " -#~ msgid "cannot insert line" -#~ msgstr "kan rel nie byvoeg nie" +#~ msgid "Compiler: " +#~ msgstr "Kompileerder: " -#~ msgid "string cannot contain newlines" -#~ msgstr "string kan nie 'newlines' bevat nie" +#~ msgid " system menu file: \"" +#~ msgstr " stelsel kieslys-ler: \"" -#~ msgid "" -#~ "E266: Sorry, this command is disabled, the Ruby library could not be " -#~ "loaded." -#~ msgstr "" -#~ "E266: Jammer, hierdie bevel is afgeskakel, die Ruby biblioteekler kon " -#~ "nie gelaai word nie." +#~ msgid "3rd user gvimrc file: \"" +#~ msgstr "3de gebruiker gvimrc-ler: \"" -#~ msgid "E273: unknown longjmp status %d" -#~ msgstr "E273: Onbekende 'longjmp' status %d" +#~ msgid "2nd user gvimrc file: \"" +#~ msgstr "2de gebruiker gvimrc-ler: \"" -#~ msgid "Toggle implementation/definition" -#~ msgstr "Stel en herstel implimentasie/definisie" +#~ msgid " user gvimrc file: \"" +#~ msgstr " gebruiker gvimrc-ler: \"" -#~ msgid "Show base class of" -#~ msgstr "Wys basisklas van" +#~ msgid " system gvimrc file: \"" +#~ msgstr " stelsel gvimrc-ler: \"" -#~ msgid "Show overridden member function" -#~ msgstr "Wys vervangde lidfunksie" +#~ msgid "with (classic) GUI." +#~ msgstr "met (klassieke) GUI." -#~ msgid "Retrieve from file" -#~ msgstr "Gaan haal uit ler" +#~ msgid "with Cocoa GUI." +#~ msgstr "met Cocoa GUI." -#~ msgid "Retrieve from project" -#~ msgstr "Gaan haal uit projek" +#~ msgid "with Carbon GUI." +#~ msgstr "met Carbon GUI." -#~ msgid "Retrieve from all projects" -#~ msgstr "Gaan haal uit alle projekte" +#~ msgid "with GUI." +#~ msgstr "met GUI." -#~ msgid "Retrieve" -#~ msgstr "Gaan haal" +#~ msgid "with Photon GUI." +#~ msgstr "met Photon GUI." -#~ msgid "Show source of" -#~ msgstr "Wys kode van" +#~ msgid "with BeOS GUI." +#~ msgstr "met BeOS GUI" -#~ msgid "Find symbol" -#~ msgstr "Vind simbool" +#~ msgid "with X11-Athena GUI." +#~ msgstr "met X11-Athena GUI" -#~ msgid "Browse class" -#~ msgstr "Kyk klas deur" +#~ msgid "with X11-neXtaw GUI." +#~ msgstr "met X11-neXtaw GUI" -#~ msgid "Show class in hierarchy" -#~ msgstr "Wys klas in hirargie" +#~ msgid "with X11-Motif GUI." +#~ msgstr "met X11-Motif GUI." -#~ msgid "Show class in restricted hierarchy" -#~ msgstr "Wys klas in beperkte hirargie" +#~ msgid "with GTK GUI." +#~ msgstr "met GTK GUI" -#~ msgid "Xref refers to" -#~ msgstr "Xref verwys na" +#~ msgid "with GTK2 GUI." +#~ msgstr "met GTK2 GUI" -#~ msgid "Xref referred by" -#~ msgstr "Xref verwys deur" +#~ msgid "with GTK-GNOME GUI." +#~ msgstr "met GTK-GNOME GUI." -#~ msgid "Xref has a" -#~ msgstr "Xref het 'n" +#~ msgid "with GTK2-GNOME GUI." +#~ msgstr "met GTK2-GNOME GUI." -#~ msgid "Xref used by" -#~ msgstr "Xref gebruik deur" +#~ msgid "" +#~ "\n" +#~ "Tiny version " +#~ msgstr "" +#~ "\n" +#~ "Piepklein weergawe " -#~ msgid "Show docu of" -#~ msgstr "Wys 'docu' van" +#~ msgid "" +#~ "\n" +#~ "Small version " +#~ msgstr "" +#~ "\n" +#~ "Klein weergawe " -#~ msgid "Generate docu for" -#~ msgstr "Genereer 'docu' vir" +#~ msgid "" +#~ "\n" +#~ "Normal version " +#~ msgstr "" +#~ "\n" +#~ "Normale weergawe " #~ msgid "" -#~ "Cannot connect to SNiFF+. Check environment (sniffemacs must be found in " -#~ "$PATH).\n" +#~ "\n" +#~ "Big version " #~ msgstr "" -#~ "Kan nie 'n verbinding met 'SNiFF+' maak nie. Kyk of die omgewing reg is " -#~ "('sniffemacs' moet in '$PATH' gevind word).\n" +#~ "\n" +#~ "Groot weergawe " -#~ msgid "E274: Sniff: Error during read. Disconnected" -#~ msgstr "E274: Sniff: Fout gedurende lees. Verbinding gebreek" +#~ msgid "" +#~ "\n" +#~ "RISC OS version" +#~ msgstr "" +#~ "\n" +#~ "RISC OS weergawe" -#~ msgid "SNiFF+ is currently " -#~ msgstr "SNiFF+ is tans" +#~ msgid "" +#~ "\n" +#~ "MacOS version" +#~ msgstr "" +#~ "\n" +#~ "MacOS weergawe" -#~ msgid "not " -#~ msgstr "nie " +#~ msgid "" +#~ "\n" +#~ "MacOS X version" +#~ msgstr "" +#~ "\n" +#~ "MacOS X weergawe" -#~ msgid "connected" -#~ msgstr "gekonnekteer" +#~ msgid "" +#~ "\n" +#~ "MacOS X (unix) version" +#~ msgstr "" +#~ "\n" +#~ "MacOS X (unix) weergawe" -#~ msgid "E275: Unknown SNiFF+ request: %s" -#~ msgstr "E275: Onbekende SNiFF+ versoek: %s" +#~ msgid "" +#~ "\n" +#~ "16-bit MS-DOS version" +#~ msgstr "" +#~ "\n" +#~ "16-bis MS-DOS weergawe" -#~ msgid "E276: Error connecting to SNiFF+" -#~ msgstr "E276: Fout in konnekteer met SNiFF+" +#~ msgid "" +#~ "\n" +#~ "32-bit MS-DOS version" +#~ msgstr "" +#~ "\n" +#~ "32-bis MS-DOS weergawe" -#~ msgid "E278: SNiFF+ not connected" -#~ msgstr "E278: SNiFF+ is nie gekonnekteer nie" +#~ msgid "" +#~ "\n" +#~ "MS-Windows 16-bit version" +#~ msgstr "" +#~ "\n" +#~ "MS-Windows 16-bis weergawe" -#~ msgid "Sniff: Error during write. Disconnected" -#~ msgstr "Sniff: Fout gedurende stoor. Verbinding gebreek" +#~ msgid "" +#~ "\n" +#~ "MS-Windows 32-bit console version" +#~ msgstr "" +#~ "\n" +#~ "MS-Windows 32-bis konsole weergawe" -#~ msgid "not implemented yet" -#~ msgstr "nog nie gemplementeer nie" +#~ msgid " with OLE support" +#~ msgstr " met OLE ondersteuning" -#~ msgid "unknown option" -#~ msgstr "onbekende opsie" +#~ msgid " in Win32s mode" +#~ msgstr " in Win32s modus" -#~ msgid "cannot set line(s)" -#~ msgstr "kan nie rel(s) stel nie" +#~ msgid "" +#~ "\n" +#~ "MS-Windows 32-bit GUI version" +#~ msgstr "" +#~ "\n" +#~ "MS-Windows 32-bis GUI version" -#~ msgid "mark not set" -#~ msgstr "merker nie gestel nie" +#~ msgid "" +#~ "\n" +#~ "MS-Windows 16/32-bit GUI version" +#~ msgstr "" +#~ "\n" +#~ "MS-Windows 16/32-bis GUI weergawe" -#~ msgid "row %d column %d" -#~ msgstr "ry %d kolom %d" +#~ msgid "No undo possible; continue anyway" +#~ msgstr "Geen herstel moontlik; gaan in elk geval voort" -#~ msgid "cannot insert/append line" -#~ msgstr "kan nie rel invoeg/aanlas nie" +#~ msgid "new shell started\n" +#~ msgstr "nuwe dop begin\n" -#~ msgid "unknown flag: " -#~ msgstr "onbekende vlag: " +#~ msgid "E430: Tag file path truncated for %s\n" +#~ msgstr "E430: Etiketlergids afgekap vir %s\n" -#~ msgid "unknown vimOption" -#~ msgstr "onbekende 'vimOption'" +#~ msgid "Enter nr of choice (<CR> to abort): " +#~ msgstr "Sleutel nommer van keuse in (<CR> om te stop): " -#~ msgid "keyboard interrupt" -#~ msgstr "sleutelbordonderbreking" +#~ msgid "E396: containedin argument not accepted here" +#~ msgstr "E396: 'containedin' parameter nie hier aanvaar nie" -#~ msgid "vim error" -#~ msgstr "vim fout" +#~ msgid "E363: pattern caused out-of-stack error" +#~ msgstr "E363: patroon het le-stapel fout veroorsaak" -#~ msgid "cannot create buffer/window command: object is being deleted" -#~ msgstr "kan nie buffer/venster bevel skep nie: voorwerp word geskrap" +#~ msgid "E361: Crash intercepted; regexp too complex?" +#~ msgstr "E361: Ineenstorting onderskep. Patroon te kompleks?" -#~ msgid "" -#~ "cannot register callback command: buffer/window is already being deleted" -#~ msgstr "" -#~ "kan nie terugroepbevel registreer nie: buffer/venster word alreeds geskrap" +#~ msgid "E58: %s{ operand could be empty" +#~ msgstr "E58: %s{ operand mag leeg wees" -#~ msgid "" -#~ "E280: TCL FATAL ERROR: reflist corrupt!? Please report this to vim-" -#~ "dev@vim.org" -#~ msgstr "" -#~ "E280: TCL FATALE FOUT: verwlys korrup!? Rapporteer dit asb. aan <vim-" -#~ "dev@vim.org>" +#~ msgid "E57: %s+ operand could be empty" +#~ msgstr "E57: %s+ operand mag leeg wees" -#~ msgid "cannot register callback command: buffer/window reference not found" -#~ msgstr "" -#~ "kan terugroepbevel nie registreer nie: buffer/vensterverwysing nie gevind " -#~ "nie" +#~ msgid "E56: %s* operand could be empty" +#~ msgstr "E56: %s* operand mag leeg wees" -#~ msgid "" -#~ "E571: Sorry, this command is disabled: the Tcl library could not be " -#~ "loaded." -#~ msgstr "" -#~ "E571: Jammer, hierdie bevel is afgeskakel, die Tcl biblioteek kon nie " -#~ "gelaai word nie." +#~ msgid "Vim Warning" +#~ msgstr "Vim Waarskuwing" #~ msgid "" -#~ "E281: TCL ERROR: exit code is not int!? Please report this to vim-dev@vim." -#~ "org" +#~ "VIMRUN.EXE not found in your $PATH.\n" +#~ "External commands will not pause after completion.\n" +#~ "See :help win32-vimrun for more information." #~ msgstr "" -#~ "E281: TCL FOUT: verlaatkode is nie 'n 'int'!? Rapporteer dit asb. aan " -#~ "<vim-dev@vim.org>" - -#~ msgid "cannot get line" -#~ msgstr "kan nie rel kry nie" - -#~ msgid "Unable to register a command server name" -#~ msgstr "Kon nie bevelbediener naam registreer nie" - -#~ msgid "E248: Failed to send command to the destination program" -#~ msgstr "E248: Het gefaal om bevel na doel program te stuur" +#~ "'VIMRUN.EXE' nie gevind in '$PATH' nie.\n" +#~ "Eksterne opdragte sal nie wag na voltooiing nie\n" +#~ "Sien ':help win32-vimrun' vir meer inligting." -#~ msgid "E251: VIM instance registry property is badly formed. Deleted!" -#~ msgstr "E251: VIM instansie register-kenmerk is swak gevorm. Geskrap!" +#~ msgid "E371: Command not found" +#~ msgstr "E371: Bevel nie gevind nie" -#~ msgid "This Vim was not compiled with the diff feature." -#~ msgstr "Hierdie Vim is nie gekompileer met 'diff' funksionaliteit nie." +#~ msgid "shutdown" +#~ msgstr "sit af" -#~ msgid "-register\t\tRegister this gvim for OLE" -#~ msgstr "-register\t\tRegistreer hierdie gvim vir OLE" +#~ msgid "logoff" +#~ msgstr "teken uit" -#~ msgid "-unregister\t\tUnregister gvim for OLE" -#~ msgstr "-unregister\t\tOnregistreer gvim vir OLE" +#~ msgid "close" +#~ msgstr "maak toe" -#~ msgid "-g\t\t\tRun using GUI (like \"gvim\")" -#~ msgstr "-g\t\t\tVoer uit met die GUI (soos \"gvim\")" +#~ msgid "Vim: Caught %s event\n" +#~ msgstr "Vim: Het %s gebeurtenis gevang\n" -#~ msgid "-f or --nofork\tForeground: Don't fork when starting GUI" -#~ msgstr "-f of --nofork\tVoorgrond: Moenie vurk wanneer GUI begin nie" +#~ msgid "shell returned %d" +#~ msgstr "dop het %d gelewer" -#~ msgid "-V[N]\t\tVerbose level" -#~ msgstr "-V[N]\t\tOmslagtigheidsgraad" +#~ msgid "Could not fix up function pointers to the DLL!" +#~ msgstr "Kon nie funksiewysers na die DLL opstel nie!" -#~ msgid "-f\t\t\tDon't use newcli to open window" -#~ msgstr "-f\t\t\tMoet nie 'newcli' gebruik om venster oop te maak nie" +#~ msgid "Could not load vim32.dll!" +#~ msgstr "Kon nie 'vim32.dll' laai nie!" -#~ msgid "-dev <device>\t\tUse <device> for I/O" -#~ msgstr "-dev <toestel>\t\tGebruik <toestel> vir I/O" +#~ msgid "VIM Error" +#~ msgstr "VIM Fout" -#~ msgid "-U <gvimrc>\t\tUse <gvimrc> instead of any .gvimrc" -#~ msgstr "-U <gvimrc>\t\tGebruik <gvimrc> in plaas van enige .gvimrc" +#~ msgid "Could not allocate memory for command line." +#~ msgstr "Kan nie geheue toeken vir bevelrel nie" -#~ msgid "-x\t\t\tEdit encrypted files" -#~ msgstr "-x\t\t\tBewerk genkripteerde lers" +#~ msgid "At line" +#~ msgstr "By rel" -#~ msgid "-display <display>\tConnect vim to this particular X-server" -#~ msgstr "-display <display>\tKoppel vim aan hierdie X-bediener" +#~ msgid "XSMP ICE connection watch failed" +#~ msgstr "XSMP ICE konneksie beloer het gefaal" -#~ msgid "-X\t\t\tDo not connect to X server" -#~ msgstr "-X\t\t\tMoet nie verbinding met X-bediener maak nie" +#~ msgid "XSMP opening connection" +#~ msgstr "XSMP maak nou konneksie oop" -#~ msgid "--remote <files>\tEdit <files> in a Vim server if possible" -#~ msgstr "" -#~ "--remote <lers>\tWysig die <lers> in a Vim bediener indien moontlik" +#~ msgid "XSMP handling save-yourself request" +#~ msgstr "XSMP hanteer 'save-yourself' versoek" -#~ msgid "--remote-silent <files> Same, don't complain if there is no server" -#~ msgstr "" -#~ "--remote-silent <lers> Dieselfde, moet nie kla as daar nie so 'n " -#~ "bediener is nie" +#~ msgid "Opening the X display failed" +#~ msgstr "Oopmaak van die X vertoonskerm het gefaal" -#~ msgid "" -#~ "--remote-wait <files> As --remote but wait for files to have been edited" -#~ msgstr "" -#~ "--remote-wait <lers> Soos '--remote', maar wag vir lers om gewysig te " -#~ "word" +#~ msgid "XSMP lost ICE connection" +#~ msgstr "XSMP het ICE konneksie verloor" #~ msgid "" -#~ "--remote-wait-silent <files> Same, don't complain if there is no server" -#~ msgstr "" -#~ "--remote-wait-silent <lers> Dieselfde, moet nie kla as daar nie so 'n " -#~ "bediener is nie" - -#~ msgid "--remote-send <keys>\tSend <keys> to a Vim server and exit" +#~ "\n" +#~ "Command terminated\n" #~ msgstr "" -#~ "--remote-send <sleutels>\tStuur <sleutels> na 'n Vim-bediener en verlaat" +#~ "\n" +#~ "Bevel beindig\n" #~ msgid "" -#~ "--remote-expr <expr>\tEvaluate <expr> in a Vim server and print result" +#~ "\n" +#~ "Cannot fork\n" #~ msgstr "" -#~ "--remote-expr <expr>\tEvalueer <expr> in 'n Vim-bediener en druk resultaat" - -#~ msgid "--serverlist\t\tList available Vim server names and exit" -#~ msgstr "--serverlist\t\tLys beskikbare Vim-bediener name en verlaat" - -#~ msgid "--servername <name>\tSend to/become the Vim server <name>" -#~ msgstr "--servername <naam>\tStuur na/word die Vim-bediener <naam>" +#~ "\n" +#~ "Kan nie vurk nie\n" #~ msgid "" #~ "\n" -#~ "Arguments recognised by gvim (Motif version):\n" +#~ "Cannot create pipes\n" #~ msgstr "" #~ "\n" -#~ "Parameters deur gvim herken (Motif weergawe):\n" +#~ "Kan nie pype skep nie\n" #~ msgid "" #~ "\n" -#~ "Arguments recognised by gvim (neXtaw version):\n" +#~ "Cannot execute shell sh\n" #~ msgstr "" #~ "\n" -#~ "Parameters deur gvim herken (neXtaw weergawe):\n" +#~ "Kan nie dop 'sh' uitvoer nie\n" + +#~ msgid "Opening the X display timed out" +#~ msgstr "Oopmaak van die X-vertoonskerm het uitgetel" + +#~ msgid "Testing the X display failed" +#~ msgstr "Toetsing van die X-vertoonskerm het gefaal" #~ msgid "" #~ "\n" -#~ "Arguments recognised by gvim (Athena version):\n" +#~ "Vim: Got X error\n" #~ msgstr "" #~ "\n" -#~ "Parameters deur gvim herken (Athena weergawe):\n" +#~ "Vim: Het X fout ontvang\n" -#~ msgid "-display <display>\tRun vim on <display>" -#~ msgstr "-display <display>\tVoer vim op <display> uit" +#~ msgid "Opening the X display took %<PRId64> msec" +#~ msgstr "Om die X-vertoonskerm oop te maak het %<PRId64> msek gevat" -#~ msgid "-iconic\t\tStart vim iconified" -#~ msgstr "-iconic\t\tBegin vim as ikoon" +#~ msgid "Vim: Caught deadly signal\n" +#~ msgstr "Vim: Het dodelike sein gevang\n" -#~ msgid "-name <name>\t\tUse resource as if vim was <name>" -#~ msgstr "-name <name>\t\tGebruik hulpbron asof vim <name> was" +#~ msgid "Vim: Caught deadly signal %s\n" +#~ msgstr "Vim: Het dodelike sein %s gevang\n" -#~ msgid "\t\t\t (Unimplemented)\n" -#~ msgstr "\t\t\t (Nog nie gemplementeer nie)\n" +#~ msgid "Vim: Double signal, exiting\n" +#~ msgstr "Vim: Dubbel sein, staak\n" -#~ msgid "-background <color>\tUse <color> for the background (also: -bg)" -#~ msgstr "-background <kleur>\tGebruik <kleur> vir die agtergrond (ook: -bg)" +#~ msgid "E245: Illegal char '%c' in font name \"%s\"" +#~ msgstr "E245: Ongeldige karakter '%c' in fontnaam \"%s\"" -#~ msgid "-foreground <color>\tUse <color> for normal text (also: -fg)" -#~ msgstr "-voorgrond <kleur>\tGebruik <kleur> vir normale teks (ook: -fg)" +#~ msgid "E244: Illegal charset name \"%s\" in font name \"%s\"" +#~ msgstr "E244: Ongeldige karakterstelnaam \"%s\" in fontnaam \"%s\"" -#~ msgid "-font <font>\t\tUse <font> for normal text (also: -fn)" -#~ msgstr "-font <font>\t\tGebruik <font> vir normale teks (ook -fn)" +#~ msgid "Printing '%s'" +#~ msgstr "Druk nou '%s'" -#~ msgid "-boldfont <font>\tUse <font> for bold text" -#~ msgstr "boldfont <font>\t Gebruik <font> vir vetletter teks" +#~ msgid "E238: Print error: %s" +#~ msgstr "E238: Drukfout: %s" -#~ msgid "-italicfont <font>\tUse <font> for italic text" -#~ msgstr "-italicfont <font>\tGebruik <font> vir kursiewe teks" +#~ msgid "E613: Unknown printer font: %s" +#~ msgstr "E613: Onbekende drukker font: %s" -#~ msgid "-geometry <geom>\tUse <geom> for initial geometry (also: -geom)" -#~ msgstr "-geometry <geom>\tGebruik <geom> vir aanvanklike geometrie" +#~ msgid "to %s on %s" +#~ msgstr "na %s op %s" -#~ msgid "-borderwidth <width>\tUse a border width of <width> (also: -bw)" -#~ msgstr "-borderwidth <wydte>\tGebruik 'n grenswydte van <wydte> (ook: -bw)" +#~ msgid "'columns' is not 80, cannot execute external commands" +#~ msgstr "'columns' is nie 80 nie, kan nie eksterne bevele uitvoer nie" -#~ msgid "" -#~ "-scrollbarwidth <width> Use a scrollbar width of <width> (also: -sw)" -#~ msgstr "" -#~ "-scrollbarwidth <wydte>\tGebruik 'n rolstaafwydte van <wydte> (ook: -sw>" +#~ msgid "...(truncated)" +#~ msgstr "...(afgekap)" -#~ msgid "-menuheight <height>\tUse a menu bar height of <height> (also: -mh)" -#~ msgstr "" -#~ "-menuheight <hoogte>\tGebruik a kieslysstaafhoogte van <hoogte> (ook: -mh)" +#~ msgid "I/O ERROR" +#~ msgstr "I/O FOUT" -#~ msgid "-reverse\t\tUse reverse video (also: -rv)" -#~ msgstr "-reverse\t\tGebruik tru-video (ook: -rv)" +#~ msgid "ANCHOR_BUF_SIZE too small." +#~ msgstr "'ANCHOR_BUF_SIZE' is te klein" -#~ msgid "+reverse\t\tDon't use reverse video (also: +rv)" -#~ msgstr "+reverse\t\tMoet nie tru-video gebruik nie (ook: +rv)" +#~ msgid " returned\n" +#~ msgstr " teruggekeer\n" -#~ msgid "-xrm <resource>\tSet the specified resource" -#~ msgstr "-xrm <hulpbron>\tStel die gespesifiseerde hulpbron" +#~ msgid "shell " +#~ msgstr "dop " -#~ msgid "" -#~ "\n" -#~ "Arguments recognised by gvim (RISC OS version):\n" -#~ msgstr "" -#~ "\n" -#~ "Parameters wat gvim verstaan (RISC OS weergawe):\n" +#~ msgid "Cannot execute " +#~ msgstr "Kan nie uitvoer nie " -#~ msgid "--columns <number>\tInitial width of window in columns" -#~ msgstr "--columns <aantal>\tAanvanklike wydte van venster in kolomme" +#~ msgid "mch_get_shellsize: not a console??\n" +#~ msgstr "'mch_get_shellsize': nie 'n konsole nie??\n" -#~ msgid "--rows <number>\tInitial height of window in rows" -#~ msgstr "--rows <aantal>\tAanvanklike hoogte van venster in rye" +#~ msgid "cannot change console mode ?!\n" +#~ msgstr "kan konsole-modus nie verander nie ?!\n" -#~ msgid "" -#~ "\n" -#~ "Arguments recognised by gvim (GTK+ version):\n" -#~ msgstr "" -#~ "\n" -#~ "Parameters wat gvim verstaan (GTK+ weergawe):\n" +#~ msgid "Vim exiting with %d\n" +#~ msgstr "Vim stop met %d\n" -#~ msgid "-display <display>\tRun vim on <display> (also: --display)" -#~ msgstr "-display <skerm>\tVoer vim op <skerm> uit: (ook --display)" +#~ msgid "Cannot create " +#~ msgstr "Kan nie skep nie: " -#~ msgid "--role <role>\tSet a unique role to identify the main window" -#~ msgstr "--role <rol>\tStel 'n unieke rol om die hoofvenster te identifiseer" +#~ msgid "Cannot open NIL:\n" +#~ msgstr "Kan nie NIL: oopmaak nie\n" -#~ msgid "--socketid <xid>\tOpen Vim inside another GTK widget" -#~ msgstr "--socketid <xid>\tMaak Vim in 'n ander GTK element oop" +#~ msgid "Need %s version %<PRId64>\n" +#~ msgstr "Benodig %s weergawe %<PRId64>\n" -#~ msgid "-P <parent title>\tOpen Vim inside parent application" -#~ msgstr "-P <ouer title>\tMaak Vim oop binne 'n ouer toepassing" +#~ msgid "Need Amigados version 2.04 or later\n" +#~ msgstr "Benodig Amigados weergawe 2.04 of later\n" -#~ msgid "No display" -#~ msgstr "Geen vertoonskerm" +#~ msgid "VIM: Can't open window!\n" +#~ msgstr "VIM: Kan nie venster oopmaak nie!\n" -#~ msgid ": Send failed.\n" -#~ msgstr ": Stuur het gefaal.\n" +#~ msgid "cannot open " +#~ msgstr "kan nie oopmaak nie " -#~ msgid ": Send failed. Trying to execute locally\n" -#~ msgstr ": Stuur het gefaal. Probeer om lokaal uit te voer\n" +#~ msgid "E538: No mouse support" +#~ msgstr "E538: Geen muisondersteuning nie" -#~ msgid "%d of %d edited" -#~ msgstr "%d van %d lers bewerk" +#~ msgid "E534: Invalid wide font" +#~ msgstr "E534: Ongeldige wye font" -#~ msgid "No display: Send expression failed.\n" -#~ msgstr "Geen vertoonskerm: Stuur van uitdrukking het gefaal.\n" +#~ msgid "E533: can't select wide font" +#~ msgstr "E533: kan nie wye font kies nie" -#~ msgid ": Send expression failed.\n" -#~ msgstr ": Stuur van uitdrukking het gefaal.\n" +#~ msgid "E598: Invalid fontset" +#~ msgstr "E598: Ongeldige fontstel" -#~ msgid "E543: Not a valid codepage" -#~ msgstr "E543: Nie 'n geldige kodeblad nie" +#~ msgid "E597: can't select fontset" +#~ msgstr "E597: kan nie fontstel kies nie" -#~ msgid "E285: Failed to create input context" -#~ msgstr "E285: Gefaal met die skep van invoerkonteks" +#~ msgid "E617: Cannot be changed in the GTK+ 2 GUI" +#~ msgstr "E617: Kan nie 'term' verander in die GTK+ 2 GUI nie" -#~ msgid "E286: Failed to open input method" -#~ msgstr "E286: Gefaal om invoermetode oop te maak" +#~ msgid "E531: Use \":gui\" to start the GUI" +#~ msgstr "E531: Gebruik \":gui\" om die GUI te begin" -#~ msgid "E287: Warning: Could not set destroy callback to IM" -#~ msgstr "E287: Waarskuwing: Kon nie uitwis-terugroep na IM stel nie" +#~ msgid "E530: Cannot change term in GUI" +#~ msgstr "E530: Kan nie 'term' verander in GUI nie" -#~ msgid "E288: input method doesn't support any style" -#~ msgstr "E288: invoermetode ondersteun geen styl nie" +#~ msgid "freeing %<PRId64> lines" +#~ msgstr "laat %<PRId64> rels gaan" -#~ msgid "E289: input method doesn't support my preedit type" -#~ msgstr "E289: invoermetode ondersteun nie my voor-bewerking tipe nie" +#~ msgid "E658: NetBeans connection lost for buffer %<PRId64>" +#~ msgstr "E658: NetBeans konneksie vir buffer %<PRId64> verloor" -#~ msgid "E290: over-the-spot style requires fontset" -#~ msgstr "E290: oor-die-plek styl vereis fontstel" +#~ msgid "read from Netbeans socket" +#~ msgstr "lees vanaf Netbeans 'socket'" -#~ msgid "E291: Your GTK+ is older than 1.2.3. Status area disabled" -#~ msgstr "E291: Jou GTK+ is ouer as 1.2.3. Statusarea afgeskakel" +#~ msgid "E668: Wrong access mode for NetBeans connection info file: \"%s\"" +#~ msgstr "" +#~ "E668: Verkeerde toegangsmodue vir NetBeans konneksie inligtingsler: \"%s" +#~ "\"" -#~ msgid "E292: Input Method Server is not running" -#~ msgstr "E292: Invoermetodebediener voer nie uit nie" +#~ msgid "Cannot connect to Netbeans" +#~ msgstr "Kan nie aan Netbeans koppel nie" + +#~ msgid "Cannot connect to Netbeans #2" +#~ msgstr "Kan nie aan Netbeans #2 koppel nie" + +#~ msgid "Keys don't match!" +#~ msgstr "Sleutels verskil!" + +#~ msgid "Enter same key again: " +#~ msgstr "Voer die sleutel weer in: " + +#~ msgid "Enter encryption key: " +#~ msgstr "Voer enkripsie-sleutel in: " + +#~ msgid "E547: Illegal mouseshape" +#~ msgstr "E547: Ongeldige muisvorm" + +#~ msgid "E341: Internal error: lalloc(%<PRId64>, )" +#~ msgstr "E341: Interne fout: 'lalloc(%<PRId64>, )'" + +#~ msgid "E340: Line is becoming too long" +#~ msgstr "E340: Rel word te lank" #~ msgid "" +#~ "[calls] total re/malloc()'s %<PRIu64>, total free()'s %<PRIu64>\n" #~ "\n" -#~ " [not usable with this version of Vim]" #~ msgstr "" +#~ "[roepe] totaal re/malloc()'s %<PRIu64>, totale free()'s %<PRIu64>\n" #~ "\n" -#~ " [nie bruikbaar met hierdie weergawe van Vim nie]" + +#~ msgid "" +#~ "\n" +#~ "[bytes] total alloc-freed %<PRIu64>-%<PRIu64>, in use %<PRIu64>, peak use " +#~ "%<PRIu64>\n" +#~ msgstr "" +#~ "\n" +#~ "[grepe] totaal 'alloc'-vrygelaat %<PRIu64>-%<PRIu64>, in gebruik " +#~ "%<PRIu64>, piekgebruik %<PRIu64>\n" + +#~ msgid "ERROR: " +#~ msgstr "FOUT: " + +#~ msgid "Vim: Finished.\n" +#~ msgstr "Vim: Klaar.\n" + +#~ msgid "Vim: preserving files...\n" +#~ msgstr "Vim: bewaar lers...\n" + +#~ msgid "E338: Sorry, no file browser in console mode" +#~ msgstr "E338: Jammer, lerblaaier nie beskikbaar in konsole-modus nie" + +#~ msgid "Open File dialog" +#~ msgstr "Maak ler oop dialooghokkie" + +#~ msgid "Save File dialog" +#~ msgstr "Stoor Ler dialooghokkie" + +#~ msgid " (RET: line, SPACE: page, d: half page, q: quit)" +#~ msgstr " (RET: rel, SPACE: bladsy, d: halwe bladsy, q: los dit" + +#~ msgid " (RET/BS: line, SPACE/b: page, d/u: half page, q: quit)" +#~ msgstr " (RET/BS: rel, SPACE/b: bladsy, d/u: halwe bladsy, q: los dit" + +#~ msgid "Hit ENTER to continue" +#~ msgstr "Druk ENTER om voort te gaan" + +#~ msgid "[string too long]" +#~ msgstr "[string te lank]" + +#~ msgid "Tear off this menu" +#~ msgstr "Skeur die kieslys af" #~ msgid "" #~ "&Open Read-Only\n" @@ -7273,824 +6490,1139 @@ msgstr "E446: Geen lernaam onder loper" #~ "&Stop\n" #~ "S&krap dit" -#~ msgid "Tear off this menu" -#~ msgstr "Skeur die kieslys af" +#~ msgid "" +#~ "\n" +#~ " [not usable with this version of Vim]" +#~ msgstr "" +#~ "\n" +#~ " [nie bruikbaar met hierdie weergawe van Vim nie]" -#~ msgid "[string too long]" -#~ msgstr "[string te lank]" +#~ msgid "E292: Input Method Server is not running" +#~ msgstr "E292: Invoermetodebediener voer nie uit nie" -#~ msgid "Hit ENTER to continue" -#~ msgstr "Druk ENTER om voort te gaan" +#~ msgid "E291: Your GTK+ is older than 1.2.3. Status area disabled" +#~ msgstr "E291: Jou GTK+ is ouer as 1.2.3. Statusarea afgeskakel" -#~ msgid " (RET/BS: line, SPACE/b: page, d/u: half page, q: quit)" -#~ msgstr " (RET/BS: rel, SPACE/b: bladsy, d/u: halwe bladsy, q: los dit" +#~ msgid "E290: over-the-spot style requires fontset" +#~ msgstr "E290: oor-die-plek styl vereis fontstel" -#~ msgid " (RET: line, SPACE: page, d: half page, q: quit)" -#~ msgstr " (RET: rel, SPACE: bladsy, d: halwe bladsy, q: los dit" +#~ msgid "E289: input method doesn't support my preedit type" +#~ msgstr "E289: invoermetode ondersteun nie my voor-bewerking tipe nie" -#~ msgid "Save File dialog" -#~ msgstr "Stoor Ler dialooghokkie" +#~ msgid "E288: input method doesn't support any style" +#~ msgstr "E288: invoermetode ondersteun geen styl nie" -#~ msgid "Open File dialog" -#~ msgstr "Maak ler oop dialooghokkie" +#~ msgid "E287: Warning: Could not set destroy callback to IM" +#~ msgstr "E287: Waarskuwing: Kon nie uitwis-terugroep na IM stel nie" -#~ msgid "E338: Sorry, no file browser in console mode" -#~ msgstr "E338: Jammer, lerblaaier nie beskikbaar in konsole-modus nie" +#~ msgid "E285: Failed to create input context" +#~ msgstr "E285: Gefaal met die skep van invoerkonteks" -#~ msgid "Vim: preserving files...\n" -#~ msgstr "Vim: bewaar lers...\n" +#~ msgid "E543: Not a valid codepage" +#~ msgstr "E543: Nie 'n geldige kodeblad nie" -#~ msgid "Vim: Finished.\n" -#~ msgstr "Vim: Klaar.\n" +#~ msgid ": Send expression failed.\n" +#~ msgstr ": Stuur van uitdrukking het gefaal.\n" -#~ msgid "ERROR: " -#~ msgstr "FOUT: " +#~ msgid "No display: Send expression failed.\n" +#~ msgstr "Geen vertoonskerm: Stuur van uitdrukking het gefaal.\n" + +#~ msgid "%d of %d edited" +#~ msgstr "%d van %d lers bewerk" + +#~ msgid ": Send failed. Trying to execute locally\n" +#~ msgstr ": Stuur het gefaal. Probeer om lokaal uit te voer\n" + +#~ msgid ": Send failed.\n" +#~ msgstr ": Stuur het gefaal.\n" + +#~ msgid "No display" +#~ msgstr "Geen vertoonskerm" + +#~ msgid "-P <parent title>\tOpen Vim inside parent application" +#~ msgstr "-P <ouer title>\tMaak Vim oop binne 'n ouer toepassing" + +#~ msgid "--socketid <xid>\tOpen Vim inside another GTK widget" +#~ msgstr "--socketid <xid>\tMaak Vim in 'n ander GTK element oop" + +#~ msgid "--role <role>\tSet a unique role to identify the main window" +#~ msgstr "--role <rol>\tStel 'n unieke rol om die hoofvenster te identifiseer" + +#~ msgid "-display <display>\tRun vim on <display> (also: --display)" +#~ msgstr "-display <skerm>\tVoer vim op <skerm> uit: (ook --display)" #~ msgid "" #~ "\n" -#~ "[bytes] total alloc-freed %<PRIu64>-%<PRIu64>, in use %<PRIu64>, peak use " -#~ "%<PRIu64>\n" +#~ "Arguments recognised by gvim (GTK+ version):\n" #~ msgstr "" #~ "\n" -#~ "[grepe] totaal 'alloc'-vrygelaat %<PRIu64>-%<PRIu64>, in gebruik " -#~ "%<PRIu64>, piekgebruik %<PRIu64>\n" +#~ "Parameters wat gvim verstaan (GTK+ weergawe):\n" + +#~ msgid "--rows <number>\tInitial height of window in rows" +#~ msgstr "--rows <aantal>\tAanvanklike hoogte van venster in rye" + +#~ msgid "--columns <number>\tInitial width of window in columns" +#~ msgstr "--columns <aantal>\tAanvanklike wydte van venster in kolomme" #~ msgid "" -#~ "[calls] total re/malloc()'s %<PRIu64>, total free()'s %<PRIu64>\n" #~ "\n" +#~ "Arguments recognised by gvim (RISC OS version):\n" #~ msgstr "" -#~ "[roepe] totaal re/malloc()'s %<PRIu64>, totale free()'s %<PRIu64>\n" #~ "\n" +#~ "Parameters wat gvim verstaan (RISC OS weergawe):\n" -#~ msgid "E340: Line is becoming too long" -#~ msgstr "E340: Rel word te lank" - -#~ msgid "E341: Internal error: lalloc(%<PRId64>, )" -#~ msgstr "E341: Interne fout: 'lalloc(%<PRId64>, )'" - -#~ msgid "E547: Illegal mouseshape" -#~ msgstr "E547: Ongeldige muisvorm" - -#~ msgid "Enter encryption key: " -#~ msgstr "Voer enkripsie-sleutel in: " - -#~ msgid "Enter same key again: " -#~ msgstr "Voer die sleutel weer in: " +#~ msgid "-xrm <resource>\tSet the specified resource" +#~ msgstr "-xrm <hulpbron>\tStel die gespesifiseerde hulpbron" -#~ msgid "Keys don't match!" -#~ msgstr "Sleutels verskil!" +#~ msgid "+reverse\t\tDon't use reverse video (also: +rv)" +#~ msgstr "+reverse\t\tMoet nie tru-video gebruik nie (ook: +rv)" -#~ msgid "Cannot connect to Netbeans #2" -#~ msgstr "Kan nie aan Netbeans #2 koppel nie" +#~ msgid "-reverse\t\tUse reverse video (also: -rv)" +#~ msgstr "-reverse\t\tGebruik tru-video (ook: -rv)" -#~ msgid "Cannot connect to Netbeans" -#~ msgstr "Kan nie aan Netbeans koppel nie" +#~ msgid "-menuheight <height>\tUse a menu bar height of <height> (also: -mh)" +#~ msgstr "" +#~ "-menuheight <hoogte>\tGebruik a kieslysstaafhoogte van <hoogte> (ook: -mh)" -#~ msgid "E668: Wrong access mode for NetBeans connection info file: \"%s\"" +#~ msgid "" +#~ "-scrollbarwidth <width> Use a scrollbar width of <width> (also: -sw)" #~ msgstr "" -#~ "E668: Verkeerde toegangsmodue vir NetBeans konneksie inligtingsler: \"%s" -#~ "\"" +#~ "-scrollbarwidth <wydte>\tGebruik 'n rolstaafwydte van <wydte> (ook: -sw>" -#~ msgid "read from Netbeans socket" -#~ msgstr "lees vanaf Netbeans 'socket'" +#~ msgid "-borderwidth <width>\tUse a border width of <width> (also: -bw)" +#~ msgstr "-borderwidth <wydte>\tGebruik 'n grenswydte van <wydte> (ook: -bw)" -#~ msgid "E658: NetBeans connection lost for buffer %<PRId64>" -#~ msgstr "E658: NetBeans konneksie vir buffer %<PRId64> verloor" +#~ msgid "-geometry <geom>\tUse <geom> for initial geometry (also: -geom)" +#~ msgstr "-geometry <geom>\tGebruik <geom> vir aanvanklike geometrie" -#~ msgid "freeing %<PRId64> lines" -#~ msgstr "laat %<PRId64> rels gaan" +#~ msgid "-italicfont <font>\tUse <font> for italic text" +#~ msgstr "-italicfont <font>\tGebruik <font> vir kursiewe teks" -#~ msgid "E530: Cannot change term in GUI" -#~ msgstr "E530: Kan nie 'term' verander in GUI nie" +#~ msgid "-boldfont <font>\tUse <font> for bold text" +#~ msgstr "boldfont <font>\t Gebruik <font> vir vetletter teks" -#~ msgid "E531: Use \":gui\" to start the GUI" -#~ msgstr "E531: Gebruik \":gui\" om die GUI te begin" +#~ msgid "-font <font>\t\tUse <font> for normal text (also: -fn)" +#~ msgstr "-font <font>\t\tGebruik <font> vir normale teks (ook -fn)" -#~ msgid "E617: Cannot be changed in the GTK+ 2 GUI" -#~ msgstr "E617: Kan nie 'term' verander in die GTK+ 2 GUI nie" +#~ msgid "-foreground <color>\tUse <color> for normal text (also: -fg)" +#~ msgstr "-voorgrond <kleur>\tGebruik <kleur> vir normale teks (ook: -fg)" -#~ msgid "E597: can't select fontset" -#~ msgstr "E597: kan nie fontstel kies nie" +#~ msgid "-background <color>\tUse <color> for the background (also: -bg)" +#~ msgstr "-background <kleur>\tGebruik <kleur> vir die agtergrond (ook: -bg)" -#~ msgid "E598: Invalid fontset" -#~ msgstr "E598: Ongeldige fontstel" +#~ msgid "\t\t\t (Unimplemented)\n" +#~ msgstr "\t\t\t (Nog nie gemplementeer nie)\n" -#~ msgid "E533: can't select wide font" -#~ msgstr "E533: kan nie wye font kies nie" +#~ msgid "-name <name>\t\tUse resource as if vim was <name>" +#~ msgstr "-name <name>\t\tGebruik hulpbron asof vim <name> was" -#~ msgid "E534: Invalid wide font" -#~ msgstr "E534: Ongeldige wye font" +#~ msgid "-iconic\t\tStart vim iconified" +#~ msgstr "-iconic\t\tBegin vim as ikoon" -#~ msgid "E538: No mouse support" -#~ msgstr "E538: Geen muisondersteuning nie" +#~ msgid "-display <display>\tRun vim on <display>" +#~ msgstr "-display <display>\tVoer vim op <display> uit" -#~ msgid "cannot open " -#~ msgstr "kan nie oopmaak nie " +#~ msgid "" +#~ "\n" +#~ "Arguments recognised by gvim (Athena version):\n" +#~ msgstr "" +#~ "\n" +#~ "Parameters deur gvim herken (Athena weergawe):\n" -#~ msgid "VIM: Can't open window!\n" -#~ msgstr "VIM: Kan nie venster oopmaak nie!\n" +#~ msgid "" +#~ "\n" +#~ "Arguments recognised by gvim (neXtaw version):\n" +#~ msgstr "" +#~ "\n" +#~ "Parameters deur gvim herken (neXtaw weergawe):\n" -#~ msgid "Need Amigados version 2.04 or later\n" -#~ msgstr "Benodig Amigados weergawe 2.04 of later\n" +#~ msgid "" +#~ "\n" +#~ "Arguments recognised by gvim (Motif version):\n" +#~ msgstr "" +#~ "\n" +#~ "Parameters deur gvim herken (Motif weergawe):\n" -#~ msgid "Need %s version %<PRId64>\n" -#~ msgstr "Benodig %s weergawe %<PRId64>\n" +#~ msgid "--servername <name>\tSend to/become the Vim server <name>" +#~ msgstr "--servername <naam>\tStuur na/word die Vim-bediener <naam>" -#~ msgid "Cannot open NIL:\n" -#~ msgstr "Kan nie NIL: oopmaak nie\n" +#~ msgid "--serverlist\t\tList available Vim server names and exit" +#~ msgstr "--serverlist\t\tLys beskikbare Vim-bediener name en verlaat" -#~ msgid "Cannot create " -#~ msgstr "Kan nie skep nie: " +#~ msgid "" +#~ "--remote-expr <expr>\tEvaluate <expr> in a Vim server and print result" +#~ msgstr "" +#~ "--remote-expr <expr>\tEvalueer <expr> in 'n Vim-bediener en druk resultaat" -#~ msgid "Vim exiting with %d\n" -#~ msgstr "Vim stop met %d\n" +#~ msgid "--remote-send <keys>\tSend <keys> to a Vim server and exit" +#~ msgstr "" +#~ "--remote-send <sleutels>\tStuur <sleutels> na 'n Vim-bediener en verlaat" -#~ msgid "cannot change console mode ?!\n" -#~ msgstr "kan konsole-modus nie verander nie ?!\n" +#~ msgid "" +#~ "--remote-wait-silent <files> Same, don't complain if there is no server" +#~ msgstr "" +#~ "--remote-wait-silent <lers> Dieselfde, moet nie kla as daar nie so 'n " +#~ "bediener is nie" -#~ msgid "mch_get_shellsize: not a console??\n" -#~ msgstr "'mch_get_shellsize': nie 'n konsole nie??\n" +#~ msgid "" +#~ "--remote-wait <files> As --remote but wait for files to have been edited" +#~ msgstr "" +#~ "--remote-wait <lers> Soos '--remote', maar wag vir lers om gewysig te " +#~ "word" -#~ msgid "Cannot execute " -#~ msgstr "Kan nie uitvoer nie " +#~ msgid "--remote-silent <files> Same, don't complain if there is no server" +#~ msgstr "" +#~ "--remote-silent <lers> Dieselfde, moet nie kla as daar nie so 'n " +#~ "bediener is nie" -#~ msgid "shell " -#~ msgstr "dop " +#~ msgid "--remote <files>\tEdit <files> in a Vim server if possible" +#~ msgstr "" +#~ "--remote <lers>\tWysig die <lers> in a Vim bediener indien moontlik" -#~ msgid " returned\n" -#~ msgstr " teruggekeer\n" +#~ msgid "-X\t\t\tDo not connect to X server" +#~ msgstr "-X\t\t\tMoet nie verbinding met X-bediener maak nie" -#~ msgid "ANCHOR_BUF_SIZE too small." -#~ msgstr "'ANCHOR_BUF_SIZE' is te klein" +#~ msgid "-display <display>\tConnect vim to this particular X-server" +#~ msgstr "-display <display>\tKoppel vim aan hierdie X-bediener" -#~ msgid "I/O ERROR" -#~ msgstr "I/O FOUT" +#~ msgid "-x\t\t\tEdit encrypted files" +#~ msgstr "-x\t\t\tBewerk genkripteerde lers" -#~ msgid "...(truncated)" -#~ msgstr "...(afgekap)" +#~ msgid "-U <gvimrc>\t\tUse <gvimrc> instead of any .gvimrc" +#~ msgstr "-U <gvimrc>\t\tGebruik <gvimrc> in plaas van enige .gvimrc" -#~ msgid "'columns' is not 80, cannot execute external commands" -#~ msgstr "'columns' is nie 80 nie, kan nie eksterne bevele uitvoer nie" +#~ msgid "-dev <device>\t\tUse <device> for I/O" +#~ msgstr "-dev <toestel>\t\tGebruik <toestel> vir I/O" -#~ msgid "to %s on %s" -#~ msgstr "na %s op %s" +#~ msgid "-f\t\t\tDon't use newcli to open window" +#~ msgstr "-f\t\t\tMoet nie 'newcli' gebruik om venster oop te maak nie" -#~ msgid "E613: Unknown printer font: %s" -#~ msgstr "E613: Onbekende drukker font: %s" +#~ msgid "-V[N]\t\tVerbose level" +#~ msgstr "-V[N]\t\tOmslagtigheidsgraad" -#~ msgid "E238: Print error: %s" -#~ msgstr "E238: Drukfout: %s" +#~ msgid "-f or --nofork\tForeground: Don't fork when starting GUI" +#~ msgstr "-f of --nofork\tVoorgrond: Moenie vurk wanneer GUI begin nie" -#~ msgid "Printing '%s'" -#~ msgstr "Druk nou '%s'" +#~ msgid "-g\t\t\tRun using GUI (like \"gvim\")" +#~ msgstr "-g\t\t\tVoer uit met die GUI (soos \"gvim\")" -#~ msgid "E244: Illegal charset name \"%s\" in font name \"%s\"" -#~ msgstr "E244: Ongeldige karakterstelnaam \"%s\" in fontnaam \"%s\"" +#~ msgid "-unregister\t\tUnregister gvim for OLE" +#~ msgstr "-unregister\t\tOnregistreer gvim vir OLE" -#~ msgid "E245: Illegal char '%c' in font name \"%s\"" -#~ msgstr "E245: Ongeldige karakter '%c' in fontnaam \"%s\"" +#~ msgid "-register\t\tRegister this gvim for OLE" +#~ msgstr "-register\t\tRegistreer hierdie gvim vir OLE" -#~ msgid "Vim: Double signal, exiting\n" -#~ msgstr "Vim: Dubbel sein, staak\n" +#~ msgid "This Vim was not compiled with the diff feature." +#~ msgstr "Hierdie Vim is nie gekompileer met 'diff' funksionaliteit nie." -#~ msgid "Vim: Caught deadly signal %s\n" -#~ msgstr "Vim: Het dodelike sein %s gevang\n" +#~ msgid "E251: VIM instance registry property is badly formed. Deleted!" +#~ msgstr "E251: VIM instansie register-kenmerk is swak gevorm. Geskrap!" -#~ msgid "Vim: Caught deadly signal\n" -#~ msgstr "Vim: Het dodelike sein gevang\n" +#~ msgid "E248: Failed to send command to the destination program" +#~ msgstr "E248: Het gefaal om bevel na doel program te stuur" -#~ msgid "Opening the X display took %<PRId64> msec" -#~ msgstr "Om die X-vertoonskerm oop te maak het %<PRId64> msek gevat" +#~ msgid "Unable to register a command server name" +#~ msgstr "Kon nie bevelbediener naam registreer nie" + +#~ msgid "cannot get line" +#~ msgstr "kan nie rel kry nie" #~ msgid "" -#~ "\n" -#~ "Vim: Got X error\n" +#~ "E281: TCL ERROR: exit code is not int!? Please report this to vim-dev@vim." +#~ "org" #~ msgstr "" -#~ "\n" -#~ "Vim: Het X fout ontvang\n" - -#~ msgid "Testing the X display failed" -#~ msgstr "Toetsing van die X-vertoonskerm het gefaal" - -#~ msgid "Opening the X display timed out" -#~ msgstr "Oopmaak van die X-vertoonskerm het uitgetel" +#~ "E281: TCL FOUT: verlaatkode is nie 'n 'int'!? Rapporteer dit asb. aan " +#~ "<vim-dev@vim.org>" #~ msgid "" -#~ "\n" -#~ "Cannot execute shell sh\n" +#~ "E571: Sorry, this command is disabled: the Tcl library could not be " +#~ "loaded." #~ msgstr "" -#~ "\n" -#~ "Kan nie dop 'sh' uitvoer nie\n" +#~ "E571: Jammer, hierdie bevel is afgeskakel, die Tcl biblioteek kon nie " +#~ "gelaai word nie." -#~ msgid "" -#~ "\n" -#~ "Cannot create pipes\n" +#~ msgid "cannot register callback command: buffer/window reference not found" #~ msgstr "" -#~ "\n" -#~ "Kan nie pype skep nie\n" +#~ "kan terugroepbevel nie registreer nie: buffer/vensterverwysing nie gevind " +#~ "nie" #~ msgid "" -#~ "\n" -#~ "Cannot fork\n" +#~ "E280: TCL FATAL ERROR: reflist corrupt!? Please report this to vim-" +#~ "dev@vim.org" #~ msgstr "" -#~ "\n" -#~ "Kan nie vurk nie\n" +#~ "E280: TCL FATALE FOUT: verwlys korrup!? Rapporteer dit asb. aan <vim-" +#~ "dev@vim.org>" #~ msgid "" -#~ "\n" -#~ "Command terminated\n" +#~ "cannot register callback command: buffer/window is already being deleted" #~ msgstr "" -#~ "\n" -#~ "Bevel beindig\n" +#~ "kan nie terugroepbevel registreer nie: buffer/venster word alreeds geskrap" -#~ msgid "XSMP lost ICE connection" -#~ msgstr "XSMP het ICE konneksie verloor" +#~ msgid "cannot create buffer/window command: object is being deleted" +#~ msgstr "kan nie buffer/venster bevel skep nie: voorwerp word geskrap" -#~ msgid "Opening the X display failed" -#~ msgstr "Oopmaak van die X vertoonskerm het gefaal" +#~ msgid "vim error" +#~ msgstr "vim fout" -#~ msgid "XSMP handling save-yourself request" -#~ msgstr "XSMP hanteer 'save-yourself' versoek" +#~ msgid "keyboard interrupt" +#~ msgstr "sleutelbordonderbreking" -#~ msgid "XSMP opening connection" -#~ msgstr "XSMP maak nou konneksie oop" +#~ msgid "unknown vimOption" +#~ msgstr "onbekende 'vimOption'" -#~ msgid "XSMP ICE connection watch failed" -#~ msgstr "XSMP ICE konneksie beloer het gefaal" +#~ msgid "unknown flag: " +#~ msgstr "onbekende vlag: " -#~ msgid "XSMP SmcOpenConnection failed: %s" -#~ msgstr "XSMP 'SmcOpenConnection' het gefaal: %s" +#~ msgid "cannot insert/append line" +#~ msgstr "kan nie rel invoeg/aanlas nie" -#~ msgid "At line" -#~ msgstr "By rel" +#~ msgid "row %d column %d" +#~ msgstr "ry %d kolom %d" -#~ msgid "Could not allocate memory for command line." -#~ msgstr "Kan nie geheue toeken vir bevelrel nie" +#~ msgid "mark not set" +#~ msgstr "merker nie gestel nie" -#~ msgid "VIM Error" -#~ msgstr "VIM Fout" +#~ msgid "cannot set line(s)" +#~ msgstr "kan nie rel(s) stel nie" -#~ msgid "Could not load vim32.dll!" -#~ msgstr "Kon nie 'vim32.dll' laai nie!" +#~ msgid "unknown option" +#~ msgstr "onbekende opsie" -#~ msgid "Could not fix up function pointers to the DLL!" -#~ msgstr "Kon nie funksiewysers na die DLL opstel nie!" +#~ msgid "not implemented yet" +#~ msgstr "nog nie gemplementeer nie" -#~ msgid "shell returned %d" -#~ msgstr "dop het %d gelewer" +#~ msgid "Sniff: Error during write. Disconnected" +#~ msgstr "Sniff: Fout gedurende stoor. Verbinding gebreek" -#~ msgid "Vim: Caught %s event\n" -#~ msgstr "Vim: Het %s gebeurtenis gevang\n" +#~ msgid "E278: SNiFF+ not connected" +#~ msgstr "E278: SNiFF+ is nie gekonnekteer nie" -#~ msgid "close" -#~ msgstr "maak toe" +#~ msgid "E276: Error connecting to SNiFF+" +#~ msgstr "E276: Fout in konnekteer met SNiFF+" -#~ msgid "logoff" -#~ msgstr "teken uit" +#~ msgid "E275: Unknown SNiFF+ request: %s" +#~ msgstr "E275: Onbekende SNiFF+ versoek: %s" -#~ msgid "shutdown" -#~ msgstr "sit af" +#~ msgid "connected" +#~ msgstr "gekonnekteer" -#~ msgid "E371: Command not found" -#~ msgstr "E371: Bevel nie gevind nie" +#~ msgid "not " +#~ msgstr "nie " + +#~ msgid "SNiFF+ is currently " +#~ msgstr "SNiFF+ is tans" + +#~ msgid "E274: Sniff: Error during read. Disconnected" +#~ msgstr "E274: Sniff: Fout gedurende lees. Verbinding gebreek" #~ msgid "" -#~ "VIMRUN.EXE not found in your $PATH.\n" -#~ "External commands will not pause after completion.\n" -#~ "See :help win32-vimrun for more information." +#~ "Cannot connect to SNiFF+. Check environment (sniffemacs must be found in " +#~ "$PATH).\n" #~ msgstr "" -#~ "'VIMRUN.EXE' nie gevind in '$PATH' nie.\n" -#~ "Eksterne opdragte sal nie wag na voltooiing nie\n" -#~ "Sien ':help win32-vimrun' vir meer inligting." +#~ "Kan nie 'n verbinding met 'SNiFF+' maak nie. Kyk of die omgewing reg is " +#~ "('sniffemacs' moet in '$PATH' gevind word).\n" -#~ msgid "Vim Warning" -#~ msgstr "Vim Waarskuwing" +#~ msgid "Generate docu for" +#~ msgstr "Genereer 'docu' vir" -#~ msgid "E56: %s* operand could be empty" -#~ msgstr "E56: %s* operand mag leeg wees" +#~ msgid "Show docu of" +#~ msgstr "Wys 'docu' van" -#~ msgid "E57: %s+ operand could be empty" -#~ msgstr "E57: %s+ operand mag leeg wees" +#~ msgid "Xref used by" +#~ msgstr "Xref gebruik deur" -#~ msgid "E58: %s{ operand could be empty" -#~ msgstr "E58: %s{ operand mag leeg wees" +#~ msgid "Xref has a" +#~ msgstr "Xref het 'n" -#~ msgid "E361: Crash intercepted; regexp too complex?" -#~ msgstr "E361: Ineenstorting onderskep. Patroon te kompleks?" +#~ msgid "Xref referred by" +#~ msgstr "Xref verwys deur" -#~ msgid "E363: pattern caused out-of-stack error" -#~ msgstr "E363: patroon het le-stapel fout veroorsaak" +#~ msgid "Xref refers to" +#~ msgstr "Xref verwys na" -#~ msgid "E396: containedin argument not accepted here" -#~ msgstr "E396: 'containedin' parameter nie hier aanvaar nie" +#~ msgid "Show class in restricted hierarchy" +#~ msgstr "Wys klas in beperkte hirargie" -#~ msgid "Enter nr of choice (<CR> to abort): " -#~ msgstr "Sleutel nommer van keuse in (<CR> om te stop): " +#~ msgid "Show class in hierarchy" +#~ msgstr "Wys klas in hirargie" -#~ msgid "E430: Tag file path truncated for %s\n" -#~ msgstr "E430: Etiketlergids afgekap vir %s\n" +#~ msgid "Browse class" +#~ msgstr "Kyk klas deur" -#~ msgid "new shell started\n" -#~ msgstr "nuwe dop begin\n" +#~ msgid "Find symbol" +#~ msgstr "Vind simbool" -#~ msgid "No undo possible; continue anyway" -#~ msgstr "Geen herstel moontlik; gaan in elk geval voort" +#~ msgid "Show source of" +#~ msgstr "Wys kode van" -#~ msgid "" -#~ "\n" -#~ "MS-Windows 16/32-bit GUI version" -#~ msgstr "" -#~ "\n" -#~ "MS-Windows 16/32-bis GUI weergawe" +#~ msgid "Retrieve" +#~ msgstr "Gaan haal" -#~ msgid "" -#~ "\n" -#~ "MS-Windows 32-bit GUI version" -#~ msgstr "" -#~ "\n" -#~ "MS-Windows 32-bis GUI version" +#~ msgid "Retrieve from all projects" +#~ msgstr "Gaan haal uit alle projekte" -#~ msgid " in Win32s mode" -#~ msgstr " in Win32s modus" +#~ msgid "Retrieve from project" +#~ msgstr "Gaan haal uit projek" -#~ msgid " with OLE support" -#~ msgstr " met OLE ondersteuning" +#~ msgid "Retrieve from file" +#~ msgstr "Gaan haal uit ler" -#~ msgid "" -#~ "\n" -#~ "MS-Windows 32-bit console version" -#~ msgstr "" -#~ "\n" -#~ "MS-Windows 32-bis konsole weergawe" +#~ msgid "Show overridden member function" +#~ msgstr "Wys vervangde lidfunksie" -#~ msgid "" -#~ "\n" -#~ "MS-Windows 16-bit version" -#~ msgstr "" -#~ "\n" -#~ "MS-Windows 16-bis weergawe" +#~ msgid "Show base class of" +#~ msgstr "Wys basisklas van" -#~ msgid "" -#~ "\n" -#~ "32-bit MS-DOS version" -#~ msgstr "" -#~ "\n" -#~ "32-bis MS-DOS weergawe" +#~ msgid "Toggle implementation/definition" +#~ msgstr "Stel en herstel implimentasie/definisie" -#~ msgid "" -#~ "\n" -#~ "16-bit MS-DOS version" -#~ msgstr "" -#~ "\n" -#~ "16-bis MS-DOS weergawe" +#~ msgid "E273: unknown longjmp status %d" +#~ msgstr "E273: Onbekende 'longjmp' status %d" #~ msgid "" -#~ "\n" -#~ "MacOS X (unix) version" +#~ "E266: Sorry, this command is disabled, the Ruby library could not be " +#~ "loaded." #~ msgstr "" -#~ "\n" -#~ "MacOS X (unix) weergawe" +#~ "E266: Jammer, hierdie bevel is afgeskakel, die Ruby biblioteekler kon " +#~ "nie gelaai word nie." -#~ msgid "" -#~ "\n" -#~ "MacOS X version" -#~ msgstr "" -#~ "\n" -#~ "MacOS X weergawe" +#~ msgid "string cannot contain newlines" +#~ msgstr "string kan nie 'newlines' bevat nie" -#~ msgid "" -#~ "\n" -#~ "MacOS version" -#~ msgstr "" -#~ "\n" -#~ "MacOS weergawe" +#~ msgid "cannot insert line" +#~ msgstr "kan rel nie byvoeg nie" + +#~ msgid "cannot replace line" +#~ msgstr "kan rel nie vervang nie" + +#~ msgid "cannot delete line" +#~ msgstr "kan rel nie verwyder nie" + +#~ msgid "no such window" +#~ msgstr "geen sodanige venster nie" + +#~ msgid "<window %d>" +#~ msgstr "<venster %d>" + +#~ msgid "<window object (unknown) at %.8lX>" +#~ msgstr "<verwyder voorwerp (onbekend) by %.8lX>" + +#~ msgid "<window object (deleted) at %.8lX>" +#~ msgstr "<venster voorwerp (geskrap) by %.8lX>" + +#~ msgid "cursor position outside buffer" +#~ msgstr "loperposisie buite buffer" + +#~ msgid "readonly attribute" +#~ msgstr "leesalleen eienskap" + +#~ msgid "attempt to refer to deleted window" +#~ msgstr "poging om na geskrapte venster te verwys" + +#~ msgid "no such buffer" +#~ msgstr "buffer bestaan nie" + +#~ msgid "invalid mark name" +#~ msgstr "onbekende merknaam" + +#~ msgid "<buffer object (deleted) at %8lX>" +#~ msgstr "<buffervoorwerp (geskrap) by %8lX>" + +#~ msgid "line number out of range" +#~ msgstr "relnommer buite omvang" + +#~ msgid "attempt to refer to deleted buffer" +#~ msgstr "poging om na 'n geskrapte buffer te verwys" + +#~ msgid "expressions disabled at compile time" +#~ msgstr "uitdrukkings afgeskakel tydens kompilering" + +# njj: net 'n voorstel .. +#~ msgid "invalid expression" +#~ msgstr "ongeldige uitdrukking" + +#~ msgid "E264: Python: Error initialising I/O objects" +#~ msgstr "E264: Python: Kon nie I/O objekte inwy nie" + +#~ msgid "writelines() requires list of strings" +#~ msgstr "'writelines()' benodig 'n lys van stringe" + +#~ msgid "invalid attribute" +#~ msgstr "ongeldige eienskap" + +#~ msgid "softspace must be an integer" +#~ msgstr "'softspace' moet 'n heelgetal wees" + +#~ msgid "can't delete OutputObject attributes" +#~ msgstr "kan nie 'OutputObject' eienskappe skrap nie" + +#~ msgid "E659: Cannot invoke Python recursively" +#~ msgstr "E659: Kan nie Python rekursief roep nie" #~ msgid "" -#~ "\n" -#~ "RISC OS version" +#~ "E263: Sorry, this command is disabled, the Python library could not be " +#~ "loaded." #~ msgstr "" -#~ "\n" -#~ "RISC OS weergawe" +#~ "E263: Jammer, hierdie bevel is afgeskakel, die Python biblioteek ler kon " +#~ "nie gelaai word nie." + +#~ msgid "E569: maximum number of cscope connections reached" +#~ msgstr "E569: maksimum aantal 'cscope' verbindings bereik" + +#~ msgid "E626: cannot get cscope database information" +#~ msgstr "E626: kan nie 'cscope' databasisinligting kry nie" + +#~ msgid "E625: cannot open cscope database: %s" +#~ msgstr "E625: Kon nie 'cscope' databasis oopmaak nie: %s" + +#~ msgid "E563: stat error" +#~ msgstr "E563: 'stat' fout" + +#~ msgid "E256: Hangul automata ERROR" +#~ msgstr "E256: Hangul outomatiserings FOUT" #~ msgid "" +#~ "Font1 width: %<PRId64>\n" #~ "\n" -#~ "Big version " #~ msgstr "" +#~ "Font1 wydte: %<PRId64>\n" #~ "\n" -#~ "Groot weergawe " -#~ msgid "" -#~ "\n" -#~ "Normal version " +#~ msgid "Font0 width: %<PRId64>\n" +#~ msgstr "Font0 wydte: %<PRId64>\n" + +#~ msgid "Font%<PRId64> width is not twice that of font0\n" +#~ msgstr "Font%<PRId64> wydte is nie twee keer de van font0 nie\n" + +#~ msgid "Font1: %s\n" +#~ msgstr "Font1: %s\n" + +#~ msgid "Font0: %s\n" +#~ msgstr "Font0: %s\n" + +#~ msgid "E253: Fontset name: %s\n" +#~ msgstr "E253: Fonstel naam: %s\n" + +#~ msgid "Font '%s' is not fixed-width" +#~ msgstr "Font '%s' is nie 'n vaste-wydte font nie" + +#~ msgid "E252: Fontset name: %s" +#~ msgstr "E252: Fontstel naam: %s" + +#~ msgid "E250: Fonts for the following charsets are missing in fontset %s:" #~ msgstr "" -#~ "\n" -#~ "Normale weergawe " +#~ "E250: Fonte vir die volgende karakterstelle ontbreek in fontversameling " +#~ "%s:" #~ msgid "" -#~ "\n" -#~ "Small version " +#~ "Vim E458: Cannot allocate colormap entry, some colors may be incorrect" #~ msgstr "" -#~ "\n" -#~ "Klein weergawe " +#~ "Vim E458: Kan nie kleurkaart-inskrywing toeken nie, sommige kleure mag " +#~ "verkeerd wees" -#~ msgid "" -#~ "\n" -#~ "Tiny version " +#~ msgid "Find & Replace (use '\\\\' to find a '\\')" +#~ msgstr "Vind & vervang string (gebruik '\\\\' om 'n '\\' te vind" + +#~ msgid "Find string (use '\\\\' to find a '\\')" +#~ msgstr "Vind string (gebruik '\\\\' om 'n '\\' te vind" + +#~ msgid "E672: Unable to open window inside MDI application" +#~ msgstr "E672: Kon nie venster oopmaak binne 'n MDI toepassing nie" + +#~ msgid "E243: Argument not supported: \"-%s\"; Use the OLE version." +#~ msgstr "E243: Parameter nie bekend: \"-%s\"; Gebruik die OLE weergawe." + +#~ msgid "Undo" +#~ msgstr "Herroep" + +#~ msgid "Selection" +#~ msgstr "Seleksie" + +#~ msgid "Files" +#~ msgstr "Lers" + +#~ msgid "Help" +#~ msgstr "Hulp" + +#~ msgid "Directories" +#~ msgstr "Gidse" + +#~ msgid "Filter" +#~ msgstr "Filter" + +#~ msgid "Used CUT_BUFFER0 instead of empty selection" +#~ msgstr "'CUT_BUFFER0' is gebruik in plaas van le seleksie" + +#~ msgid "Font Selection" +#~ msgstr "Fontkeuse" + +#~ msgid "Vim: Main window unexpectedly destroyed\n" +#~ msgstr "Vim: Hoofvenster onverwags verwoes\n" + +#~ msgid "Vim: Received \"die\" request from session manager\n" +#~ msgstr "Vim: Het die \"die\" opdrag ontvang van sessiebestuurder\n" + +#~ msgid "Replace All" +#~ msgstr "Vervang alles" + +#~ msgid "Replace" +#~ msgstr "Vervang" + +#~ msgid "Find Next" +#~ msgstr "Vind volgende" + +#~ msgid "Down" +#~ msgstr "Af" + +#~ msgid "Up" +#~ msgstr "Op" + +#~ msgid "Direction" +#~ msgstr "Rigting" + +#~ msgid "Match case" +#~ msgstr "Tref kas" + +#~ msgid "Match whole word only" +#~ msgstr "Tref slegs presiese woord" + +#~ msgid "Replace with:" +#~ msgstr "Vervang met:" + +#~ msgid "Find what:" +#~ msgstr "Soek na:" + +#~ msgid "VIM - Search..." +#~ msgstr "VIM - Soek..." + +#~ msgid "VIM - Search and Replace..." +#~ msgstr "VIM - Soek en Vervang..." + +#~ msgid "Input _Methods" +#~ msgstr "Invoer _Metodes" + +#~ msgid "Vim dialog..." +#~ msgstr "Vim dialooghokkie..." + +#~ msgid "E599: Value of 'imactivatekey' is invalid" +#~ msgstr "E599: Waarde van 'imactivatekey' is ongeldig" + +#~ msgid "E231: 'guifontwide' invalid" +#~ msgstr "E231: 'guifontwide' ongeldig" + +#~ msgid "E665: Cannot start GUI, no valid font found" +#~ msgstr "E665: Kan nie GUI begin nie, geen geldige font gevind nie" + +#~ msgid "E230: Cannot read from \"%s\"" +#~ msgstr "E230: Kan nie lees uit \"%s\" nie" + +#~ msgid "E229: Cannot start the GUI" +#~ msgstr "E229: Kan nie die GUI begin nie" + +#~ msgid "E232: Cannot create BalloonEval with both message and callback" +#~ msgstr "E232: Kan nie BalloonEval skep met beide boodskap en terugroep nie" + +#~ msgid "Scrollbar Widget: Could not get geometry of thumb pixmap." #~ msgstr "" -#~ "\n" -#~ "Piepklein weergawe " +#~ "Rolstaafelement: Kon nie pikselmatriks-duimnael se geometrie kry nie" -#~ msgid "with GTK2-GNOME GUI." -#~ msgstr "met GTK2-GNOME GUI." +#~ msgid "Vim dialog" +#~ msgstr "Vim dialooghokkie" -#~ msgid "with GTK-GNOME GUI." -#~ msgstr "met GTK-GNOME GUI." +#~ msgid "Cancel" +#~ msgstr "Kanselleer" -#~ msgid "with GTK2 GUI." -#~ msgstr "met GTK2 GUI" +#~ msgid "OK" +#~ msgstr "OK" -#~ msgid "with GTK GUI." -#~ msgstr "met GTK GUI" +#~ msgid "E615: vim_SelFile: can't get current directory" +#~ msgstr "E615: vim_SelFile: Kan nie huidige gids verkry nie" -#~ msgid "with X11-Motif GUI." -#~ msgstr "met X11-Motif GUI." +#~ msgid "Pathname:" +#~ msgstr "Gidsnaam:" -#~ msgid "with X11-neXtaw GUI." -#~ msgstr "met X11-neXtaw GUI" +#~ msgid "E614: vim_SelFile: can't return to current directory" +#~ msgstr "E614: 'vim_SelFile': Kan nie terugkeer na huidige gids nie" -#~ msgid "with X11-Athena GUI." -#~ msgstr "met X11-Athena GUI" +#~ msgid "E616: vim_SelFile: can't get font %s" +#~ msgstr "E616: 'vim_SelFile': kan font %s nie kry nie" -#~ msgid "with BeOS GUI." -#~ msgstr "met BeOS GUI" +#~ msgid "<cannot open> " +#~ msgstr "<kan nie oopmaak nie> " -#~ msgid "with Photon GUI." -#~ msgstr "met Photon GUI." +#~ msgid "E460: The resource fork would be lost (add ! to override)" +#~ msgstr "E460: Die hulpbronvurk sal verlore gaan (gebruik ! om te dwing)" -#~ msgid "with GUI." -#~ msgstr "met GUI." +#~ msgid "Partial writes disallowed for NetBeans buffers" +#~ msgstr "Gedeeltelike skryf word nie toegelaat vir NetBeans buffers nie" -#~ msgid "with Carbon GUI." -#~ msgstr "met Carbon GUI." +#~ msgid "NetBeans disallows writes of unmodified buffers" +#~ msgstr "NetBeans laat nie skryf toe van onveranderde buffers nie" -#~ msgid "with Cocoa GUI." -#~ msgstr "met Cocoa GUI." +#~ msgid "[CONVERSION ERROR]" +#~ msgstr "[OMSETTINGSFOUT]" -#~ msgid "with (classic) GUI." -#~ msgstr "met (klassieke) GUI." +#~ msgid "[crypted]" +#~ msgstr "[gekodeer]" -#~ msgid " system gvimrc file: \"" -#~ msgstr " stelsel gvimrc-ler: \"" +#~ msgid "[NL found]" +#~ msgstr "[NL gevind]" -#~ msgid " user gvimrc file: \"" -#~ msgstr " gebruiker gvimrc-ler: \"" +#~ msgid "E196: No digraphs in this version" +#~ msgstr "E196: Geen digrawe in hierdie weergawe nie" -#~ msgid "2nd user gvimrc file: \"" -#~ msgstr "2de gebruiker gvimrc-ler: \"" +#~ msgid "Save Setup" +#~ msgstr "Stoor konfigurasie" -#~ msgid "3rd user gvimrc file: \"" -#~ msgstr "3de gebruiker gvimrc-ler: \"" +#~ msgid "Save Session" +#~ msgstr "Stoor Sessie" -#~ msgid " system menu file: \"" -#~ msgstr " stelsel kieslys-ler: \"" +#~ msgid "Save View" +#~ msgstr "Stoor Oorsig" -#~ msgid "Compiler: " -#~ msgstr "Kompileerder: " +#~ msgid "Save Redirection" +#~ msgstr "Stoor Herversturing" -#~ msgid "menu Help->Orphans for information " -#~ msgstr "menu Hulp->Weeskinders vir meer inligting hieroor " +#~ msgid "Window position: X %d, Y %d" +#~ msgstr "Vensterposisie: X %d, Y %d" -#~ msgid "Running modeless, typed text is inserted" -#~ msgstr "Voer modus-loos uit, getikte teks word ingevoeg" +#~ msgid "Append File" +#~ msgstr "Las aan by ler" -#~ msgid "menu Edit->Global Settings->Toggle Insert Mode " -#~ msgstr "menu Redigeer->Globale verstellings->Stel en herstel Invoeg Modus" +#~ msgid "Edit File in new window" +#~ msgstr "Bewerk ler in nuwe venster" -#~ msgid " for two modes " -#~ msgstr " vir twee modusse " +#~ msgid " (NOT FOUND)" +#~ msgstr " (NIE GEVIND NIE)" -#~ msgid "menu Edit->Global Settings->Toggle Vi Compatible" -#~ msgstr "menu Redigeer->Global verstellings->Stel en herstel Vi Versoenbaar" +#~ msgid "Edit File" +#~ msgstr "Verander ler" -#~ msgid " for Vim defaults " -#~ msgstr " vir Vim verstekwaardes" +#~ msgid "Source Vim script" +#~ msgstr "Voer Vim skrip uit" -#~ msgid "WARNING: Windows 95/98/ME detected" -#~ msgstr "WAARSKUWING: Windows 95/98/ME bespeur" +#~ msgid "Save As" +#~ msgstr "Stoor As" -#~ msgid "type :help windows95<Enter> for info on this" -#~ msgstr "tik :help windows95<Enter> vir meer inligting hieroor" +#~ msgid "E130: Undefined function: %s" +#~ msgstr "E130: Ongedefinieerde funksie: %s" -#~ msgid "E370: Could not load library %s" -#~ msgstr "E370: Kon nie biblioteek laai nie %s" +#~ msgid "E277: Unable to read a server reply" +#~ msgstr "E277: Kon bediener-terugvoer nie lees nie" + +#~ msgid "E240: No connection to Vim server" +#~ msgstr "E240: Geen verbinding met Vim bediener" #~ msgid "" -#~ "Sorry, this command is disabled: the Perl library could not be loaded." +#~ "&OK\n" +#~ "&Cancel" #~ msgstr "" -#~ "Jammer, hierdie bevel is afgeskakel: die Perl biblioteek kon nie gelaai " -#~ "word nie." +#~ "&OK\n" +#~ "&Kanselleer" -#~ msgid "E299: Perl evaluation forbidden in sandbox without the Safe module" -#~ msgstr "" -#~ "E299: Perl evaluasie verbied in die sandput sonder die 'Safe' module" +#~ msgid "E106: Unknown variable: \"%s\"" +#~ msgstr "E106: Onbekende veranderlike: \"%s\"" -#~ msgid "Edit with &multiple Vims" -#~ msgstr "Wysig met &meer as een Vim" +#~ msgid "Patch file" +#~ msgstr "Laslap ler" -#~ msgid "Edit with single &Vim" -#~ msgstr "Wysig met 'n enkel &Vim" +#~ msgid "[No File]" +#~ msgstr "[Geen ler]" -#~ msgid "&Diff with Vim" -#~ msgstr "Wys verskille ('&diff') met Vim" +#~ msgid "[Error List]" +#~ msgstr "[Foutlys]" -#~ msgid "Edit with &Vim" -#~ msgstr "Wysig met &Vim" +#~ msgid "type :help cp-default<Enter> for info on this" +#~ msgstr "tik :help cp-default<Enter> vir meer inligting hieroor" -#~ msgid "Edit with existing Vim - &" -#~ msgstr "Wysig met bestaande Vim - &" +#~ msgid "type :set nocp<Enter> for Vim defaults" +#~ msgstr "tik :set nocp<Enter> vir Vim verstekwaardes " -#~ msgid "Edits the selected file(s) with Vim" -#~ msgstr "Wysig die gekose ler(s) met Vim" +#~ msgid "Running in Vi compatible mode" +#~ msgstr "Voer tans uit in Vi-versoenbare modus" -#~ msgid "Error creating process: Check if gvim is in your path!" -#~ msgstr "FOut met die skep van proses: Kyk of gvim in jou pad is!" +#~ msgid "type :help version7<Enter> for version info" +#~ msgstr "tik :help version7<Enter> vir weergawe-inligting" -#~ msgid "gvimext.dll error" -#~ msgstr "'gvimext.dll' fout" +#~ msgid "type :help<Enter> or <F1> for on-line help" +#~ msgstr "tik :help<Enter> of <F1> vir aanlyn hulp " -#~ msgid "Path length too long!" -#~ msgstr "Pad-lengte te lank" +#~ msgid "by Bram Moolenaar et al." +#~ msgstr "deur Bram Moolenaar et al." -#~ msgid "E234: Unknown fontset: %s" -#~ msgstr "E234: Onbekende fontstel: %s" +# njj: :)) +#~ msgid "version " +#~ msgstr "Weergawe " -#~ msgid "E235: Unknown font: %s" -#~ msgstr "E235: Onbekende font: %s" +#~ msgid "VIM - Vi IMproved" +#~ msgstr "VIM - Vi Met skop" -#~ msgid "E448: Could not load library function %s" -#~ msgstr "E448: Kon nie biblioteek funksie laai nie %s" +#~ msgid " DEBUG BUILD" +#~ msgstr " ONTFOUTINGS-KOMPILERING" -#~ msgid "E26: Hebrew cannot be used: Not enabled at compile time\n" +#~ msgid "Linking: " +#~ msgstr "Koppeling: " + +#~ msgid "Compilation: " +#~ msgstr "Kompilering: " + +#~ msgid " 2nd user exrc file: \"" +#~ msgstr " 2de gebruiker exrc-ler: \"" + +#~ msgid " user exrc file: \"" +#~ msgstr " gebruiker exrc-ler: \"" + +#~ msgid " 3rd user vimrc file: \"" +#~ msgstr " 3de gebruiker vimrc-ler \"" + +#~ msgid " 2nd user vimrc file: \"" +#~ msgstr " 2de gebruiker vimrc-ler \"" + +#~ msgid " user vimrc file: \"" +#~ msgstr " gebruiker vimrc-ler: \"" + +#~ msgid " Features included (+) or not (-):\n" +#~ msgstr " Kenmerke in- (+) of uitgesluit (-):\n" + +#~ msgid "without GUI." +#~ msgstr "sonder GUI." + +#~ msgid "" +#~ "\n" +#~ "Huge version " #~ msgstr "" -#~ "E26: Hebreeus kan nie gebruik word nie: Nie tydens kompilering gekies " -#~ "nie\n" +#~ "\n" +#~ "Enorme weergawe " -#~ msgid "E27: Farsi cannot be used: Not enabled at compile time\n" +#~ msgid "Modified by " +#~ msgstr "Gewysig deur " + +#, fuzzy +#~ msgid "" +#~ "\n" +#~ "Extra patches: " +#~ msgstr "Eksterne subtreffers:\n" + +#~ msgid "" +#~ "\n" +#~ "Included patches: " #~ msgstr "" -#~ "E27: Farsi kan nie gebruik word nie: Nie tydens kompilering gekies nie\n" +#~ "\n" +#~ "Ingeslote laslappies:" -#~ msgid "E800: Arabic cannot be used: Not enabled at compile time\n" +#~ msgid "" +#~ "\n" +#~ "--- Terminal keys ---" #~ msgstr "" -#~ "E800: Arabies kan nie gebruik word nie: Nie tydens kompilering gekies " -#~ "nie\n" +#~ "\n" +#~ "--- Terminaal sleutels ---" -#~ msgid "E247: no registered server named \"%s\"" -#~ msgstr "E247: geen geregistreerde bediener genaamd \"%s\"" +#~ msgid "E437: terminal capability \"cm\" required" +#~ msgstr "E437: terminaalvermo \"cm\" vereis" -#~ msgid "E233: cannot open display" -#~ msgstr "E233: kan nie vertoonskerm oopmaak nie" +#~ msgid "E436: No \"%s\" entry in termcap" +#~ msgstr "E436: Geen \"%s\" inskrywing in termcap nie" -#~ msgid "E463: Region is guarded, cannot modify" -#~ msgstr "E463: Omgewing is onder bewaking, kan nie verander nie" +#~ msgid "E559: Terminal entry not found in termcap" +#~ msgstr "E559: Terminaalinskrywing nie in 'termcap' gevind nie" -#~ msgid "function " -#~ msgstr "funksie " +#~ msgid "E558: Terminal entry not found in terminfo" +#~ msgstr "E558: Terminaalinskrywing nie in 'terminfo' gevind nie" -#~ msgid "Run Macro" -#~ msgstr "Voer Makro uit" +#~ msgid "E557: Cannot open termcap file" +#~ msgstr "E557: Kan nie 'termcap'-ler oopmaak nie" -#~ msgid "E242: Color name not recognized: %s" -#~ msgstr "E242: Kleurnaam is onbekend: %s" +#~ msgid "defaulting to '" +#~ msgstr "gebruik verstek '" -#~ msgid "error reading cscope connection %d" -#~ msgstr "'cscope' verbinding %d kon nie gelees word nie" +#~ msgid "' not known. Available builtin terminals are:" +#~ msgstr "' onbekend. Beskikbare ingeboude terminale is:" -#~ msgid "E260: cscope connection not found" -#~ msgstr "E260: 'cscope' verbinding nie gevind nie" +#~ msgid "E422: terminal code too long: %s" +#~ msgstr "E422: terminaalkode te lank: %s" -#~ msgid "cscope connection closed" -#~ msgstr "'cscope' verbinding gesluit" +#, fuzzy +#~ msgid "Substitute " +#~ msgstr "1 vervanging" -# njj: dalk 'verbinding' ipv 'verbinding' orals? -#~ msgid "couldn't malloc\n" -#~ msgstr "kon nie 'malloc' nie\n" +#~ msgid " (lang)" +#~ msgstr " (taal)" -#~ msgid "%2d %-5ld %-34s <none>\n" -#~ msgstr "%2d %-5ld %-34s <geen>\n" +#~ msgid "" +#~ "\n" +#~ "Cannot execute shell " +#~ msgstr "" +#~ "\n" +#~ "Kan nie dop uitvoer nie " -#~ msgid "E249: couldn't read VIM instance registry property" -#~ msgstr "E249: kon nie VIM instansie register-kenmerk lees nie" +#~ msgid "E522: Not found in termcap" +#~ msgstr "E522: Nie gevind in 'termcap' nie" -#~ msgid "\"\n" -#~ msgstr "\"\n" +#~ msgid "Thanks for flying Vim" +#~ msgstr "Dankie dat jy vlieg met Vim" -#~ msgid "--help\t\tShow Gnome arguments" -#~ msgstr "--help\t\tWys Gnome parameters" +#~ msgid "%<%f%h%m%=Page %N" +#~ msgstr "%<%f%h%m%=Bladsy %N" -#~ msgid "1 line ~ed" -#~ msgstr "1 rel ge-~" +#~ msgid "E574: Unknown register type %d" +#~ msgstr "E574: Onbekende registertipe %d" -#~ msgid "%<PRId64> lines ~ed" -#~ msgstr "%<PRId64> rels ge-~" +#~ msgid "" +#~ "\n" +#~ "# Registers:\n" +#~ msgstr "" +#~ "\n" +#~ "# Registers:\n" -#~ msgid " BLOCK" -#~ msgstr " BLOK" +#~ msgid "Illegal register name" +#~ msgstr "Ongeldige registernaam" -#~ msgid " LINE" -#~ msgstr " REL" +#~ msgid "cannot yank; delete anyway" +#~ msgstr "kan nie pluk nie: verwyder in elk geval" -#~ msgid "Linear tag search" -#~ msgstr "Linire etiketsoek" +#~ msgid "E337: Menu not found - check menu names" +#~ msgstr "E337: Kieslys nie gevind nie - maak seker oor die kieslys name" -#~ msgid "Binary tag search" -#~ msgstr "Binre etiketsoek" +#~ msgid "E336: Menu path must lead to a sub-menu" +#~ msgstr "E336: Kieslyspad moet lei na 'n sub-kieslys" -#~ msgid "E258: no matches found in cscope connections" -#~ msgstr "E258: geen treffers gevind in 'cscope' verbindings nie" +#~ msgid "Swap file already exists!" +#~ msgstr "Ruiller bestaan alreeds!" -#~ msgid "No servers found for this display" -#~ msgstr "Geen bedieners gevind vir die 'display' nie" +#, fuzzy +#~ msgid " Quit, or continue with caution.\n" +#~ msgstr " Stop, of gaan versigtig voort.\n" -#~ msgid "Missing filename" -#~ msgstr "Ontbrekende lernaam" +#~ msgid "Missing '>'" +#~ msgstr "Ontbrekende '>'" -#~ msgid "Invalid line number: %<PRId64>" -#~ msgstr "Ongeldige relnommer: %<PRId64>" +#~ msgid "" +#~ "\n" +#~ "# History of marks within files (newest to oldest):\n" +#~ msgstr "" +#~ "\n" +#~ "# Geskiedenis van merkers in lers (nuutste tot oudste):\n" -#~ msgid "Cannot use :normal from event handler" -#~ msgstr "Kan ':normal' nie vanuit gebeurtenishanteerder gebruik nie" +#~ msgid "" +#~ "\n" +#~ "# Jumplist (newest first):\n" +#~ msgstr "" +#~ "\n" +#~ "# Springlys (nuutste eerste):\n" -#~ msgid "VIM - Help on..." -#~ msgstr "VIM - Hulp met.." +#~ msgid "" +#~ "\n" +#~ "# File marks:\n" +#~ msgstr "" +#~ "\n" +#~ "# Lermerkers:\n" -#~ msgid "Topic:" -#~ msgstr "Onderwerp:" +#~ msgid "-h or --help\tPrint Help (this message) and exit" +#~ msgstr "-h of --help\tSkryf Hulp (hierdie boodskap) en sluit" -#~ msgid "Error: During loading fontset %s" -#~ msgstr "Fout: Gedurende die laai van fontstel %s" +#~ msgid "-i <viminfo>\t\tUse <viminfo> instead of .viminfo" +#~ msgstr "-i <viminfo>\t\tGebruik <viminfo> in plaas van .viminfo" -#~ msgid "locale is not set correctly" -#~ msgstr "lokaal is nie korrek gestel nie" +#~ msgid "-W <scriptout>\tWrite all typed commands to file <scriptout>" +#~ msgstr "-W <skripuit>\tSkryf alle getikte bevele na ler <skripuit>" -#~ msgid "For korean:" -#~ msgstr "Vir Afrikaans:" +#~ msgid "-w <scriptout>\tAppend all typed commands to file <scriptout>" +#~ msgstr "-w <skripuit>\tLas alle getikte bevele aan by ler <skripuit>" -#~ msgid " csh: setenv LANG ko" -#~ msgstr " csh: setenv LANG af" +#~ msgid "+<lnum>\t\tStart at line <lnum>" +#~ msgstr "+<lnum>\t\tBegin by rel <lnum>" -#~ msgid " sh : export LANG=ko" -#~ msgstr " sh: export LANG=af" +#~ msgid "+\t\t\tStart at end of file" +#~ msgstr "+\t\t\tBegin by einde van ler" -#~ msgid "fontset name: %s" -#~ msgstr "fontstel naam: %s" +#~ msgid "-O[N]\t\tLike -o but split vertically" +#~ msgstr "-O[N]\t\tSoos -o maar verdeel vertikaal" -#~ msgid "Your language Font missing" -#~ msgstr "Jou taal Font ontbreek" +#~ msgid "-u <vimrc>\t\tUse <vimrc> instead of any .vimrc" +#~ msgstr "-u <vimrc>\t\tGebruik <vimrc> in plaas van enige ander .vimrc" -#~ msgid "automata ERROR: internal" -#~ msgstr "automata FOUT: intern" +#~ msgid "-T <terminal>\tSet terminal type to <terminal>" +#~ msgstr "-T <terminaal>\tStel terminaaltipe na <terminaal>" -#~ msgid "cs_add_common: alloc fail #1" -#~ msgstr "'cs_add_common': toeken onsuksesvol #1" +#~ msgid "-F\t\t\tStart in Farsi mode" +#~ msgstr "-F\t\t\tBegin in Farsi modus" -#~ msgid "cs_add_common: alloc fail #2" -#~ msgstr "'cs_add_common': toeken onsuksesvol #2" +#~ msgid "-H\t\t\tStart in Hebrew mode" +#~ msgstr "-H\t\t\tBegin in Hebreeuse modus" -#~ msgid "cs_add_common: alloc fail #3" -#~ msgstr "'cs_add_common': toeken onsuksesvol #3" +#~ msgid "-A\t\t\tstart in Arabic mode" +#~ msgstr "-A\t\t\tbegin in Arabiese modus" -#~ msgid "cs_add_common: alloc fail #4" -#~ msgstr "'cs_add_common': toeken onsuksesvol #4" +#~ msgid "-L\t\t\tSame as -r" +#~ msgstr "-L\t\t\tSelfde as -r" -#~ msgid "Retrieve next symbol" -#~ msgstr "Kry volgende simbool" +#~ msgid "-r (with file name)\tRecover crashed session" +#~ msgstr "-r (met ler naam)\tHerwin ineengestorte sessie" -#~ msgid "-- SNiFF+ commands --" -#~ msgstr "-- SNiFF+ bevele --" +#~ msgid "-r\t\t\tList swap files and exit" +#~ msgstr "-r\t\t\tLys ruillers en verlaat vim" -#~ msgid "Unrecognized sniff request [%s]" -#~ msgstr "Onbekende sniff versoek [%s]" +#~ msgid "-D\t\t\tDebugging mode" +#~ msgstr "-D\t\t\tOntfoutmodus" -#~ msgid "Can't create input context." -#~ msgstr "Kan nie invoerkonteks skep nie." +#~ msgid "-N\t\t\tNot fully Vi compatible: 'nocompatible'" +#~ msgstr "-N\t\t\tNie ten volle Vi-versoenbaar nie: 'nocompatible'" -#~ msgid "Sorry, deleting a menu is not possible in the Athena version" -#~ msgstr "" -#~ "Jammer, in die Athena weergawe is dit onmoontlik om 'n kieslys te skrap" +#~ msgid "-C\t\t\tCompatible with Vi: 'compatible'" +#~ msgstr "-C\t\t\tVersoenbaar met Vi: 'compatible'" -#~ msgid "Out of memory" -#~ msgstr "Geheue op" +#~ msgid "-l\t\t\tLisp mode" +#~ msgstr "-l\t\t\tLisp modus" -#~ msgid "PC (32 bits Vim)" -#~ msgstr "PC (32 bisse Vim)" +#~ msgid "-b\t\t\tBinary mode" +#~ msgstr "-b\t\t\tBinre modus" -#~ msgid "PC (16 bits Vim)" -#~ msgstr "PC (16 bisse Vim)" +#~ msgid "-Z\t\t\tRestricted mode (like \"rvim\")" +#~ msgstr "-Z\t\t\tBeperkte modus (soos \"rvim\")" -#~ msgid "Unsupported screen mode" -#~ msgstr "Ongesteunde skermmodus" +#~ msgid "-R\t\t\tReadonly mode (like \"view\")" +#~ msgstr "-R\t\t\tLeesalleen modus (soos \"view\")" -#~ msgid "deadly signal" -#~ msgstr "dodelike sein" +#~ msgid "-y\t\t\tEasy mode (like \"evim\", modeless)" +#~ msgstr "-y\t\t\tEasy modus (soos \"evim\", modusloos)" -#~ msgid "some" -#~ msgstr "sommige" +#~ msgid "-d\t\t\tDiff mode (like \"vimdiff\")" +#~ msgstr "-d\t\t\tDiff modus (soos \"vimdiff\")" -#~ msgid "Library call failed" -#~ msgstr "Biblioteekfunksieroep het gefaal" +#~ msgid "-s\t\t\tSilent (batch) mode (only for \"ex\")" +#~ msgstr "-s\t\t\tStil (bondel) modus (slegs vir \"ex\")" -#~ msgid "Cannot clear all highlight groups" -#~ msgstr "Kan nie alle uitliggroepe leegmaak nie" +#~ msgid "-e\t\t\tEx mode (like \"ex\")" +#~ msgstr "-e\t\t\tEx modus (soos \"ex\")" -#~ msgid "GUI is not running" -#~ msgstr "GUI voer nie uit nie" +#~ msgid "-v\t\t\tVi mode (like \"vi\")" +#~ msgstr "-v\t\t\tVi modus (soos \"vi\")" -#~ msgid "Command too long" -#~ msgstr "Bevel te lank" +#~ msgid "" +#~ "\n" +#~ "\n" +#~ "Arguments:\n" +#~ msgstr "" +#~ "\n" +#~ "\n" +#~ "Parameters:\n" -#~ msgid "Ambiguous mapping" -#~ msgstr "Dubbelsinnige binding" +#~ msgid "" +#~ "\n" +#~ " or:" +#~ msgstr "" +#~ "\n" +#~ " of:" -#~ msgid "Ambiguous mapping, conflicts with \"%s\"" -#~ msgstr "Dubbelsinnige binding, bots met \"%s\"" +#~ msgid " vim [arguments] " +#~ msgstr " vim [parameters] " -#~ msgid "Too many \\(" -#~ msgstr "Te veel \\(" +#~ msgid "%d files to edit\n" +#~ msgstr "%d lers om te bewerk\n" -#~ msgid "Unmatched \\(" -#~ msgstr "Onpaar \\(" +#~ msgid "Invalid argument for" +#~ msgstr "Ongeldige parameter vir" -#~ msgid "Nested *, \\=, \\+, \\! or \\{" -#~ msgstr "Geneste *, \\=, \\+, \\! of \\{" +#~ msgid "Zero count" +#~ msgstr "Nul telling" -#~ msgid "\\= follows nothing" -#~ msgstr "\\= volg niks" +#~ msgid "Nvim: Reading from stdin...\n" +#~ msgstr "Vim: Lees nou vanaf 'stdin'...\n" -#~ msgid "\\+ follows nothing" -#~ msgstr "\\+ volg niks" +#~ msgid "Input Line" +#~ msgstr "Invoer Lyn" -#~ msgid "\\@ follows nothing" -#~ msgstr "\\@ volg niks" +#~ msgid "Expression" +#~ msgstr "Uitdrukking" -#~ msgid "\\{ follows nothing" -#~ msgstr "\\{ volg niks" +#~ msgid "Search String" +#~ msgstr "Soekstring" -#~ msgid "\\* follows nothing" -#~ msgstr "\\* volg niks" +#~ msgid "Command Line" +#~ msgstr "Bevelrel" -#~ msgid "Unexpected magic character; check META." -#~ msgstr "Onverwagte toorkarakter; kyk na META." +#~ msgid "" +#~ "\n" +#~ "# %s History (newest to oldest):\n" +#~ msgstr "" +#~ "\n" +#~ "# %s Geskiedenis (van nuutste na oudste):\n" -#~ msgid "type :help uganda<Enter> if you like Vim " -#~ msgstr "tik :help uganda<Enter> as jy hou van Vim " +#~ msgid "E195: Cannot open viminfo file for reading" +#~ msgstr "E195: Kan 'viminfo' ler nie oopmaak om te lees nie" -#~ msgid " WARNING: Intel CPU detected. " -#~ msgstr " WAARSKUWING: Intel SVE bespeur. " +#~ msgid "E466: :winpos requires two number arguments" +#~ msgstr "E466: :winpos benodig twee parameters" -#~ msgid " PPC has a much better architecture. " -#~ msgstr " PPC het 'n veel beter argitektuur. " +#~ msgid "E188: Obtaining window position not implemented for this platform" +#~ msgstr "" +#~ "E188: Verkryging van vensterposisie is nie vir hierdie platform " +#~ "gemplementeer nie" -#~ msgid "Security error: new viminfo file is a symbolic link" -#~ msgstr "Sekuriteitsfout: nuwe viminfo ler is a simboliese skakel" +#, fuzzy +#~ msgid "" +#~ "E747: Cannot change directory, buffer is modified (add ! to override)" +#~ msgstr "E509: Kan rugsteunler nie skep nie (gebruik ! om te dwing)" -#~ msgid "line ~%<PRId64>: %s" -#~ msgstr "rel ~%<PRId64>: %s" +#~ msgid "E172: Only one file name allowed" +#~ msgstr "E172: Slegs een lernaam toegelaat" -#~ msgid "makeef option not set" -#~ msgstr "'makeef' opsie nie aan nie" +#~ msgid "" +#~ "\n" +#~ "# Last Substitute String:\n" +#~ "$" +#~ msgstr "" +#~ "\n" +#~ "# Vorige Vervangstring:\n" +#~ "$" -#~ msgid "Security error: filter output is a symbolic link: %s" -#~ msgstr "Sekuriteitsfout: filter afvoer is 'n simboliese skakel" +#~ msgid "Illegal starting char" +#~ msgstr "Ongeldige beginkarakter" -#~ msgid "Security error: 'charconvert' output is a symbolic link" -#~ msgstr "Sekuriteitsfout: 'charconvert' afvoer is 'n simboliese skakel" +#~ msgid "# Value of 'encoding' when this file was written\n" +#~ msgstr "# Waarde van 'encoding' toe hierdie ler gestoor is\n" -#~ msgid "Security error: filter input is a symbolic link: %s" -#~ msgstr "Sekuriteitsfout: filter invoer is 'n simboliese skakel" +#~ msgid "" +#~ "# You may edit it if you're careful!\n" +#~ "\n" +#~ msgstr "" +#~ "# Jy mag dit wysig as jy versigtig is!\n" +#~ "\n" -#~ msgid "Fold must be at least two lines" -#~ msgstr "'n Vou moet ten minste 2 rels wees" +#~ msgid "# This viminfo file was generated by Vim %s.\n" +#~ msgstr "# Hierdie viminfo ler is gegenereer deur Vim %s.\n" -#~ msgid "No fold at this line" -#~ msgstr "Geen vou by hierdie rel nie" +#~ msgid "E138: Can't write viminfo file %s!" +#~ msgstr "E138: Kan nie viminfo ler %s stoor nie!" -#~ msgid "Security error: shell command output is a symbolic link" -#~ msgstr "Sekuriteitsfout: Dop-bevel afvoer is 'n simboliese skakel" +#~ msgid "E136: viminfo: Too many errors, skipping rest of file" +#~ msgstr "E136: viminfo: Te veel foute, slaan die res van die ler oor" -#~ msgid "Warning: %s option changed from modeline" -#~ msgstr "Waarskuwing: %s opsie verander vanaf moduslyn" +#~ msgid "%sviminfo: %s in line: " +#~ msgstr "%sviminfo: %s in rel: " -#~ msgid "Change dir debugging enabled." -#~ msgstr "Verandergids ontfouting in staat gestel" +#~ msgid "" +#~ "\n" +#~ "# global variables:\n" +#~ msgstr "" +#~ "\n" +#~ "# globale veranderlikes:\n" -#~ msgid "Not a proper file name: '%s'" -#~ msgstr "Nie 'n geldige lernaam nie: '%s'" +#, fuzzy +#~ msgid "E706: Variable type mismatch for: %s" +#~ msgstr "E93: Meer as een treffer vir %s" -#~ msgid "File name '%s' is valid" -#~ msgstr "lernaam '%s is ongeldig" +#, fuzzy +#~ msgid "E724: variable nested too deep for displaying" +#~ msgstr "E22: Skripte te diep ge-nes" -#~ msgid "Leave: %s" -#~ msgstr "Verlaat: %s" +#~ msgid "" +#~ "\n" +#~ "# Buffer list:\n" +#~ msgstr "" +#~ "\n" +#~ "# Buffer lys:\n" -#~ msgid "WARNING: tag command changed a buffer!!!" -#~ msgstr "WAARSKUWING: etiketbevel het buffer verander!!!" +#, fuzzy +#~ msgid "Unable to get option value" +#~ msgstr "E258: Kan nie na klint stuur nie" diff --git a/src/nvim/po/ca.po b/src/nvim/po/ca.po index 7d32db9f97..b485b61491 100644 --- a/src/nvim/po/ca.po +++ b/src/nvim/po/ca.po @@ -299,7 +299,7 @@ msgstr "E100: No hi ha cap altre buffer en mode diff" #: ../diff.c:2112 msgid "E101: More than two buffers in diff mode, don't know which one to use" -msgstr "E101: Hi ha ms de 2 buffers en mode diff, no se sap quin usar" +msgstr "E101: Hi ha ms de 2 buffers en mode diff" #: ../diff.c:2141 #, c-format @@ -1095,7 +1095,7 @@ msgstr "%<PRId64> lnies filtrades" #: ../ex_cmds.c:1194 msgid "E135: *Filter* Autocommands must not change current buffer" -msgstr "E135: Les auto-ordres *Filter* no poden canviar el buffer actual" +msgstr "E135: Les ordres automtiques *Filter* han de no modificar el buffer" #: ../ex_cmds.c:1244 msgid "[No write since last change]\n" @@ -1582,7 +1582,7 @@ msgstr "E605: No s'ha interceptat l'excepci: %s" #: ../ex_docmd.c:1085 msgid "End of sourced file" -msgstr "Final del fitxer interpretat" +msgstr "Final de l'script" #: ../ex_docmd.c:1086 msgid "End of function" @@ -1881,7 +1881,7 @@ msgstr "%s s'ha descartat" #: ../ex_eval.c:708 msgid "Exception" -msgstr "Exepci" +msgstr "Excepci" #: ../ex_eval.c:713 msgid "Error and interrupt" @@ -2162,7 +2162,7 @@ msgstr "[ERRORS DE LECTURA]" #: ../fileio.c:2104 msgid "Can't find temp file for conversion" -msgstr "No s'ha trobat el fitxer temporal per la conversi" +msgstr "No s'ha trobat el fitxer temporal per a fer la conversi" #: ../fileio.c:2110 msgid "Conversion with 'charconvert' failed" @@ -2298,7 +2298,7 @@ msgstr "E205: patchmode: no s'ha pogut desar el fitxer original" #: ../fileio.c:3602 msgid "E206: patchmode: can't touch empty original file" -msgstr "E206: patchmode: no s'ha pogut tocar el fitxer original buit" +msgstr "E206: patchmode: no s'ha pogut fer un toc al fitxer original buit" #: ../fileio.c:3616 msgid "E207: Can't delete backup file" @@ -2308,9 +2308,7 @@ msgstr "E207: No s'ha pogut eliminar la cpia de seguretat" msgid "" "\n" "WARNING: Original file may be lost or damaged\n" -msgstr "" -"\n" -"ATENCI: El fitxer original es pot haver fet malb\n" +msgstr "\nATENCI: El fitxer original es pot haver perdut o fet malb\n" #: ../fileio.c:3675 msgid "don't quit the editor until the file is successfully written!" @@ -2460,7 +2458,7 @@ msgstr "" #: ../fileio.c:5065 #, c-format msgid "E462: Could not prepare for reloading \"%s\"" -msgstr "E462: No s'han pogut fer les preparacions per rellegir \"%s\"" +msgstr "E462: No s'han pogut fer les preparacions per a rellegir \"%s\"" #: ../fileio.c:5078 #, c-format @@ -2536,7 +2534,7 @@ msgstr "Executant %s" #: ../fileio.c:7211 #, c-format msgid "autocommand %s" -msgstr "auto-ordre %s" +msgstr "ordre automtica %s" #: ../fileio.c:7795 msgid "E219: Missing {." @@ -3043,7 +3041,7 @@ msgstr "No hi ha text per imprimir" #: ../hardcopy.c:668 #, c-format msgid "Printing page %d (%d%%)" -msgstr "S'est imprimint la pgina %d (%d%%)" +msgstr "Imprimint la pgina %d (%d%%)" #: ../hardcopy.c:680 #, c-format @@ -3057,7 +3055,7 @@ msgstr "S'ha imprs: %s" #: ../hardcopy.c:740 msgid "Printing aborted" -msgstr "S'ha avortat l'impressi" +msgstr "S'ha avortat la impressi" #: ../hardcopy.c:1365 msgid "E455: Error writing to PostScript output file" @@ -3314,7 +3312,7 @@ msgstr "E609: Error de cscope: %s" #: ../if_cscope.c:2053 msgid "All cscope databases reset" -msgstr "S'han reiniciat totes les bases de dades cscope" +msgstr "S'han restablert totes les bases de dades cscope" #: ../if_cscope.c:2123 msgid "no cscope connections\n" @@ -3517,7 +3515,7 @@ msgstr "-n\t\t\tNo usa fitxers d'intercanvi, noms memria" #: ../main.c:2218 msgid "-r\t\t\tList swap files and exit" -msgstr "-r\t\t\tLlista els fitxers d'intercanvi i surt" +msgstr "-r\t\t\tLlistat dels fitxers d'intercanvi" #: ../main.c:2219 msgid "-r (with file name)\tRecover crashed session" @@ -3549,7 +3547,7 @@ msgstr "-u <vimrc>\t\tUsa <vimrc> en lloc de qualsevol altre .vimrc" #: ../main.c:2226 msgid "--noplugin\t\tDon't load plugin scripts" -msgstr "--noplugin\t\tNo carrega cap plugin" +msgstr "--noplugin\t\tNo carrega plugins" #: ../main.c:2227 msgid "-p[N]\t\tOpen N tab pages (default: one for each file)" @@ -3561,7 +3559,7 @@ msgstr "-o[N]\t\tObre N finestres (per omissi: una per fitxer)" #: ../main.c:2229 msgid "-O[N]\t\tLike -o but split vertically" -msgstr "-O[N]\t\tCom -o per amb divisions verticals" +msgstr "-O[N]\t\tCom -o per amb divisi vertical" #: ../main.c:2230 msgid "+\t\t\tStart at end of file" @@ -3739,7 +3737,7 @@ msgstr "E305: No s'ha trobat el fitxer d'intercanvi de %s" #: ../memline.c:839 msgid "Enter number of swap file to use (0 to quit): " -msgstr "Entreu el nmero del fitxer d'intercanvi a utilitzar (0 per sortir): " +msgstr "Entreu el nmero del fitxer .swp a utilitzar (0 per a sortir): " #: ../memline.c:879 #, c-format @@ -3807,6 +3805,30 @@ msgstr "E308: Atenci: El fitxer original pot haver canviat" #: ../memline.c:1061 #, c-format +msgid "Swap file is encrypted: \"%s\"" +msgstr "El fitxer d'intercanvi est xifrat: \"%s\"" + +msgid "" +"\n" +"If you entered a new crypt key but did not write the text file," +msgstr "\nSi vau entrar una nova clau de xifrat per no vau desar el fitxer," + +msgid "" +"\n" +"enter the new crypt key." +msgstr "\nentreu la nova clau." + +msgid "" +"\n" +"If you wrote the text file after changing the crypt key press enter" +msgstr "\nSi vau desar el fitxer desprs de canviar la clau, premeu Entrar per a" + +msgid "" +"\n" +"to use the same key for text file and swap file" +msgstr "\nusar la mateixa clau per al fitxer de text i per al fitxer d'intercanvi." + +#, c-format msgid "E309: Unable to read block 1 from %s" msgstr "E309: No s'ha pogut llegir el bloc 1 de %s" @@ -3931,7 +3953,7 @@ msgstr " [del Vim versi 3.0]" #: ../memline.c:1550 msgid " [does not look like a Vim swap file]" -msgstr " [no sembla un fitxer d'intercanvi de Vim]" +msgstr " [no sembla un fitxer .swp de Vim]" #: ../memline.c:1552 msgid " file name: " @@ -4003,7 +4025,7 @@ msgstr " [no es pot obrir]" #: ../memline.c:1698 msgid "E313: Cannot preserve, there is no swap file" -msgstr "E313: No s'ha pogut preservar, no hi ha fitxer d'intercanvi" +msgstr "E313: No s'ha pogut preservar, no existeix cap fitxer d'intercanvi" #: ../memline.c:1747 msgid "File preserved" @@ -4025,7 +4047,7 @@ msgstr "E316: ml_get: no s'ha trobat la lnia %<PRId64>" #: ../memline.c:2236 msgid "E317: pointer block id wrong 3" -msgstr "E317: Punter a la id d'un bloc incorrecte 3" +msgstr "E317: punter a id de bloc incorrecte 3" #: ../memline.c:2311 msgid "stack_idx should be 0" @@ -4037,7 +4059,7 @@ msgstr "E318: S'han actualitzat massa blocs?" #: ../memline.c:2511 msgid "E317: pointer block id wrong 4" -msgstr "E317: Punter a la id d'un bloc incorrecte 4" +msgstr "E317: Punter a id de bloc incorrecte 4" #: ../memline.c:2536 msgid "deleted block 1?" @@ -4087,9 +4109,7 @@ msgstr "E325: ATENCI" msgid "" "\n" "Found a swap file by the name \"" -msgstr "" -"\n" -"S'ha trobat un fitxer d'intercanvi de nom \"" +msgstr "\nS'ha trobat un fitxer d'intercanvi amb nom \"" #: ../memline.c:3226 msgid "While opening file \"" @@ -4134,7 +4154,7 @@ msgid "" " to recover the changes (see \":help recovery\").\n" msgstr "" "\"\n" -" per recuperar els canvis (vegeu \":help recovery\").\n" +" per a recuperar els canvis (vegeu \":help recovery\").\n" #: ../memline.c:3250 msgid " If you did this already, delete the swap file \"" @@ -4146,7 +4166,7 @@ msgid "" " to avoid this message.\n" msgstr "" "\"\n" -" per evitar aquest missatge.\n" +" per a evitar aquest missatge.\n" #: ../memline.c:3450 ../memline.c:3452 msgid "Swap file \"" @@ -4172,7 +4192,7 @@ msgid "" "&Quit\n" "&Abort" msgstr "" -"&Obrir noms-lectura\n" +"&Obrir amb noms lectura\n" "&Editar igualment\n" "&Recuperar\n" "&Sortir\n" @@ -4840,7 +4860,7 @@ msgstr "E377: %%%c no vlid a la cadena de format" #. nothing found #: ../quickfix.c:477 msgid "E378: 'errorformat' contains no pattern" -msgstr "E378: L'opci 'errorformat' no cont cap patr" +msgstr "E378: 'errorformat' no cont cap patr" #: ../quickfix.c:695 msgid "E379: Missing or empty directory name" @@ -5234,7 +5254,7 @@ msgstr "S'han trobat tots els fitxers inclosos" #: ../search.c:4519 msgid "No included files" -msgstr "No hi han fitxers inclosos" +msgstr "No hi ha fitxers inclosos" #: ../search.c:4527 msgid "E388: Couldn't find definition" @@ -5605,7 +5625,7 @@ msgstr "Escrivint el fitxer de suggeriments %s ..." #: ../spell.c:7707 ../spell.c:7927 #, c-format msgid "Estimated runtime memory use: %d bytes" -msgstr "s estimat de memria en funcionament: %d octets" +msgstr "s estimat de memria durant l'execuci: %d octets" #: ../spell.c:7820 msgid "E751: Output file name must not have region name" @@ -5622,7 +5642,7 @@ msgstr "E755: Regi no vlida a %s" #: ../spell.c:7907 msgid "Warning: both compounding and NOBREAK specified" -msgstr "Atenci: heu especificat composici i NOBREAK alhora" +msgstr "Atenci: s'ha especificat composici i NOBREAK alhora" #: ../spell.c:7920 #, c-format @@ -6373,7 +6393,7 @@ msgstr " alternativa per a $VIM: \"" # 29 carcters fins el ":" (incls) #: ../version.c:705 msgid " f-b for $VIMRUNTIME: \"" -msgstr " alt per a $VIMRUNTIME: \"" +msgstr " altern. per a $VIMRUNTIME: \"" #: ../version.c:709 msgid "Compilation: " @@ -6401,7 +6421,7 @@ msgstr "per Bram Moolenaar et al." #: ../version.c:774 msgid "Vim is open source and freely distributable" -msgstr "Vim s un programa obert i lliure distribuci" +msgstr "Vim s un programa obert i de lliure distribuci" #: ../version.c:776 msgid "Help poor children in Uganda!" diff --git a/src/nvim/po/de.po b/src/nvim/po/de.po index f04d3be4b4..3986a796d8 100644 --- a/src/nvim/po/de.po +++ b/src/nvim/po/de.po @@ -12,7 +12,7 @@ msgstr "" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-06-26 15:13+0200\n" "PO-Revision-Date: 2008-05-24 17:26+0200\n" -"Last-Translator: Georg Dahn <georg.dahn@gmail.com>\n" +"Last-Translator: was: Georg Dahn\n" "Language-Team: German <de@li.org>\n" "Language: de\n" "MIME-Version: 1.0\n" @@ -377,7 +377,7 @@ msgstr " Definitions-Ergnzung (^D^N^P)" #: ../edit.c:92 msgid " Dictionary completion (^K^N^P)" -msgstr " Wrterbuch-Ergnzung (^K^N^P) " +msgstr " Dictionary-Ergnzung (^K^N^P) " #: ../edit.c:93 msgid " Thesaurus completion (^T^N^P)" @@ -401,7 +401,7 @@ msgstr " Vorschlag der Rechtschreibprfung (s^N^P)" #: ../edit.c:98 msgid " Keyword Local completion (^N^P)" -msgstr " Lokale Schlsselwort-Ergnzung(^N^P)" +msgstr " Lokale Stichwort-Ergnzung(^N^P)" #: ../edit.c:101 msgid "Hit end of paragraph" @@ -443,7 +443,7 @@ msgstr "Durchsuche: %s" #: ../edit.c:3513 msgid "Scanning tags." -msgstr "Durchsuchen von Tags." +msgstr "Durchsuche Tags" #: ../edit.c:4418 msgid " Adding" @@ -633,6 +633,9 @@ msgstr "" #: ../ex_cmds.c:2433 #, c-format +msgid "Pattern not found: %s" +msgstr "Muster nicht gefunden: %s" + msgid "" "File permissions of \"%s\" are read-only.\n" "It may still be possible to write it.\n" @@ -713,11 +716,6 @@ msgstr "E148: Regulrer Ausdruck fehlt in global" msgid "Pattern found in every line: %s" msgstr "Muster in jeder Zeile gefunden: %s" -#: ../ex_cmds.c:4504 -#, c-format -msgid "Pattern not found: %s" -msgstr "Muster nicht gefunden: %s" - #: ../ex_cmds.c:4581 msgid "" "\n" @@ -6488,7 +6486,7 @@ msgid "" "\tLast set from " msgstr "" "\n" -"\tZuletzt gesetzt von " +"\tZuletzt gesetzt in " #: ../eval.c:18682 msgid "No old files" diff --git a/src/nvim/po/eo.po b/src/nvim/po/eo.po index 3fb300c63f..cc0893de3a 100644 --- a/src/nvim/po/eo.po +++ b/src/nvim/po/eo.po @@ -13,18 +13,12 @@ # Komputeko: http://komputeko.net/index_eo.php # Komputada leksikono: http://bertilow.com/div/komputada_leksikono/ # -# Lasta versio: -# http://dominique.pelle.free.fr/vim-eo.php -# -# Ĉiu komento estas bonvenata... -# Every remark is welcome... -# msgid "" msgstr "" "Project-Id-Version: Vim(Esperanto)\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-01-16 00:30+0100\n" -"PO-Revision-Date: 2017-01-16 01:14+0100\n" +"POT-Creation-Date: 2017-10-02 22:42+0200\n" +"PO-Revision-Date: 2017-10-02 22:57+0200\n" "Last-Translator: Dominique PELLÉ <dominique.pelle@gmail.com>\n" "Language-Team: \n" "Language: eo\n" @@ -105,7 +99,6 @@ msgstr "E90: Ne eblas malŝargi la lastan bufron" msgid "E84: No modified buffer found" msgstr "E84: Neniu modifita bufro trovita" -#. back where we started, didn't find anything. msgid "E85: There is no listed buffer" msgstr "E85: Estas neniu listigita bufro" @@ -121,6 +114,18 @@ msgstr "" "E89: Neniu skribo de post la lasta ŝanĝo de la bufro %ld (aldonu ! por " "transpasi)" +msgid "E948: Job still running (add ! to end the job)" +msgstr "E948: Tasko akoraŭ aktiva (aldonu ! por fini la taskon)" + +msgid "E37: No write since last change (add ! to override)" +msgstr "E37: Neniu skribo de post lasta ŝanĝo (aldonu ! por transpasi)" + +msgid "E948: Job still running" +msgstr "E948: Tasko ankoraŭ aktiva" + +msgid "E37: No write since last change" +msgstr "E37: Neniu skribo de post lasta ŝanĝo" + msgid "W14: Warning: List of file names overflow" msgstr "W14: Averto: Listo de dosiernomoj troas" @@ -176,7 +181,6 @@ msgstr "linio %ld de %ld --%d%%-- kol " msgid "[No Name]" msgstr "[Neniu nomo]" -#. must be a help buffer msgid "help" msgstr "helpo" @@ -202,6 +206,9 @@ msgstr "" "\n" "# Listo de bufroj:\n" +msgid "E382: Cannot write, 'buftype' option is set" +msgstr "E382: Ne eblas skribi, opcio 'buftype' estas ŝaltita" + msgid "[Scratch]" msgstr "[Malneto]" @@ -369,7 +376,6 @@ msgstr "E791: Malplena rikordo en klavmapo" msgid " Keyword completion (^N^P)" msgstr " Kompletigo de ŝlosilvorto (^N^P)" -#. ctrl_x_mode == 0, ^P/^N compl. msgid " ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)" msgstr " Reĝimo ^X (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)" @@ -442,13 +448,12 @@ msgstr "Analizas: %s" msgid "Scanning tags." msgstr "Analizas etikedojn." +msgid "match in file" +msgstr "kongruo en dosiero" + msgid " Adding" msgstr " Aldonanta" -#. showmode might reset the internal line pointers, so it must -#. * be called before line = ml_get(), or when this address is no -#. * longer needed. -- Acevedo. -#. msgid "-- Searching..." msgstr "-- Serĉanta..." @@ -469,7 +474,6 @@ msgstr "kongruo %d de %d" msgid "match %d" msgstr "kongruo %d" -#. maximum nesting of lists and dicts msgid "E18: Unexpected characters in :let" msgstr "E18: Neatenditaj signoj en \":let\"" @@ -523,17 +527,21 @@ msgid "E711: List value has not enough items" msgstr "E711: Lista valoro ne havas sufiĉe da eroj" msgid "E690: Missing \"in\" after :for" -msgstr "E690: \"in\" mankas post \":for\"" +msgstr "E690: \"in\" mankas malantaŭ \":for\"" #, c-format msgid "E108: No such variable: \"%s\"" msgstr "E108: Ne estas tia variablo: \"%s\"" +#, c-format +msgid "E940: Cannot lock or unlock variable %s" +msgstr "E940: Ne eblas ŝlosi aŭ malŝlosi variablon %s" + msgid "E743: variable nested too deep for (un)lock" -msgstr "E743: variablo ingita tro profunde por malŝlosi" +msgstr "E743: variablo ingita tro profunde por (mal)ŝlosi" msgid "E109: Missing ':' after '?'" -msgstr "E109: Mankas ':' post '?'" +msgstr "E109: Mankas ':' malantaŭ '?'" msgid "E691: Can only compare List with List" msgstr "E691: Eblas nur kompari Liston kun Listo" @@ -697,21 +705,10 @@ msgstr "argumento de add()" msgid "E785: complete() can only be used in Insert mode" msgstr "E785: complete() uzeblas nur en Enmeta reĝimo" -#. -#. * Yes this is ugly, I don't particularly like it either. But doing it -#. * this way has the compelling advantage that translations need not to -#. * be touched at all. See below what 'ok' and 'ync' are used for. -#. msgid "&Ok" msgstr "&Bone" #, c-format -msgid "+-%s%3ld line: " -msgid_plural "+-%s%3ld lines: " -msgstr[0] "+-%s%3ld linio: " -msgstr[1] "+-%s%3ld linioj: " - -#, c-format msgid "E700: Unknown function: %s" msgstr "E700: Nekonata funkcio: %s" @@ -756,8 +753,8 @@ msgstr "E727: Komenco preter fino" msgid "<empty>" msgstr "<malplena>" -msgid "E240: No connection to Vim server" -msgstr "E240: Neniu konekto al Vim-servilo" +msgid "E240: No connection to the X server" +msgstr "E240: Neniu konekto al X-servilo" #, c-format msgid "E241: Unable to send to %s" @@ -766,6 +763,12 @@ msgstr "E241: Ne eblas sendi al %s" msgid "E277: Unable to read a server reply" msgstr "E277: Ne eblas legi respondon de servilo" +msgid "E941: already started a server" +msgstr "E941: servilo jam lanĉita" + +msgid "E942: +clientserver feature not available" +msgstr "E942: la eblo +clientserver ne disponeblas" + msgid "remove() argument" msgstr "argumento de remove()" @@ -862,7 +865,6 @@ msgstr " malnovaj dosieroj" msgid " FAILED" msgstr " MALSUKCESIS" -#. avoid a wait_return for this message, it's annoying #, c-format msgid "E137: Viminfo file is not writable: %s" msgstr "E137: Dosiero viminfo ne skribeblas: %s" @@ -883,7 +885,6 @@ msgstr "Skribas dosieron viminfo \"%s\"" msgid "E886: Can't rename viminfo file to %s!" msgstr "E886: Ne eblas renomi dosieron viminfo al %s!" -#. Write the info: #, c-format msgid "# This viminfo file was generated by Vim %s.\n" msgstr "# Tiu dosiero viminfo estis kreita de Vim %s.\n" @@ -1002,8 +1003,8 @@ msgstr " en 1 linio" msgid " on %ld lines" msgstr " en %ld linioj" -msgid "E147: Cannot do :global recursive" -msgstr "E147: Ne eblas fari \":global\" rekursie" +msgid "E147: Cannot do :global recursive with a range" +msgstr "E147: Ne eblas fari \":global\" rekursie kun amplekso" # DP: global estas por ":global" do mi ne tradukis ĝin msgid "E148: Regular expression missing from global" @@ -1154,8 +1155,9 @@ msgstr "E750: Uzu unue \":profile start {dosiernomo}\"" msgid "Save changes to \"%s\"?" msgstr "Ĉu konservi ŝanĝojn al \"%s\"?" -msgid "Untitled" -msgstr "Sen titolo" +#, c-format +msgid "E947: Job still running in buffer \"%s\"" +msgstr "E947: Tasko ankoraŭ aktiva en la bufro \"%s\"" #, c-format msgid "E162: No write since last change for buffer \"%s\"" @@ -1189,6 +1191,14 @@ msgstr "Serĉado de \"%s\"" msgid "not found in '%s': \"%s\"" msgstr "ne trovita en '%s: \"%s\"" +#, c-format +msgid "W20: Required python version 2.x not supported, ignoring file: %s" +msgstr "W20: Pitono versio 2.x bezonata sed nesubtenata, ignoro de dosiero: %s" + +#, c-format +msgid "W21: Required python version 3.x not supported, ignoring file: %s" +msgstr "W21: pitono versio 3.x bezonata sed nesubtenata, ignoro de dosiero: %s" + msgid "Source Vim script" msgstr "Ruli Vim-skripton" @@ -1286,6 +1296,9 @@ msgstr "Inversa amplekso donita, permuteblas" msgid "E494: Use w or w>>" msgstr "E494: Uzu w aŭ w>>" +msgid "E943: Command table needs to be updated, run 'make cmdidxs'" +msgstr "E943: Tabulo de komandoj estas ĝisdatigenda, lanĉu 'make cmdidx'" + msgid "E319: Sorry, the command is not available in this version" msgstr "E319: Bedaŭrinde, tiu komando ne haveblas en tiu versio" @@ -1451,7 +1464,6 @@ msgstr "E189: \"%s\" ekzistas (aldonu ! por transpasi)" msgid "E190: Cannot open \"%s\" for writing" msgstr "E190: Ne eblas malfermi \"%s\" por skribi" -#. set mark msgid "E191: Argument must be a letter or forward/backward quote" msgstr "E191: Argumento devas esti litero, citilo aŭ retrocitilo" @@ -1493,13 +1505,15 @@ msgstr "E500: Liveras malplenan ĉenon" msgid "E195: Cannot open viminfo file for reading" msgstr "E195: Ne eblas malfermi dosieron viminfo en lega reĝimo" +msgid "Untitled" +msgstr "Sen titolo" + msgid "E196: No digraphs in this version" msgstr "E196: Neniu duliteraĵo en tiu versio" msgid "E608: Cannot :throw exceptions with 'Vim' prefix" msgstr "E608: Ne eblas lanĉi (:throw) escepton kun prefikso 'Vim'" -#. always scroll up, don't overwrite #, c-format msgid "Exception thrown: %s" msgstr "Escepto lanĉita: %s" @@ -1516,7 +1530,6 @@ msgstr "Escepto ne konservita: %s" msgid "%s, line %ld" msgstr "%s, linio %ld" -#. always scroll up, don't overwrite #, c-format msgid "Exception caught: %s" msgstr "Kaptis escepton: %s" @@ -1542,7 +1555,6 @@ msgstr "Eraro kaj interrompo" msgid "Error" msgstr "Eraro" -#. if (pending & CSTP_INTERRUPT) msgid "Interrupt" msgstr "Interrompo" @@ -1562,7 +1574,7 @@ msgid "E583: multiple :else" msgstr "E583: pluraj \":else\"" msgid "E584: :elseif after :else" -msgstr "E584: \":elseif\" post \":else\"" +msgstr "E584: \":elseif\" malantaŭ \":else\"" msgid "E585: :while/:for nesting too deep" msgstr "E585: \":while/:for\" ingita tro profunde" @@ -1585,15 +1597,12 @@ msgstr "E601: \":try\" ingita tro profunde" msgid "E603: :catch without :try" msgstr "E603: \":catch\" sen \":try\"" -#. Give up for a ":catch" after ":finally" and ignore it. -#. * Just parse. msgid "E604: :catch after :finally" -msgstr "E604: \":catch\" post \":finally\"" +msgstr "E604: \":catch\" malantaŭ \":finally\"" msgid "E606: :finally without :try" msgstr "E606: \":finally\" sen \":try\"" -#. Give up for a multiple ":finally" and ignore it. msgid "E607: multiple :finally" msgstr "E607: pluraj \":finally\"" @@ -1686,7 +1695,6 @@ msgstr "Vim: Legado el stdin...\n" msgid "Reading from stdin..." msgstr "Legado el stdin..." -#. Re-opening the original file failed! msgid "E202: Conversion made file unreadable!" msgstr "E202: Konverto igis la dosieron nelegebla!" @@ -1892,9 +1900,6 @@ msgstr "[sen EOL]" msgid "[Incomplete last line]" msgstr "[Nekompleta lasta linio]" -#. don't overwrite messages here -#. must give this prompt -#. don't use emsg() here, don't want to flush the buffers msgid "WARNING: The file has been changed since reading it!!!" msgstr "AVERTO: La dosiero estas ŝanĝita de post kiam ĝi estis legita!!!" @@ -1973,7 +1978,6 @@ msgstr "--Forviŝita--" msgid "auto-removing autocommand: %s <buffer=%d>" msgstr "aŭto-forviŝas aŭtokomandon: %s <bufro=%d>" -#. the group doesn't exist #, c-format msgid "E367: No such group: \"%s\"" msgstr "E367: Ne ekzistas tia grupo: \"%s\"" @@ -1986,7 +1990,7 @@ msgstr "W19: Forviŝo de augroup kiu estas ankoraŭ uzata" #, c-format msgid "E215: Illegal character after *: %s" -msgstr "E215: Nevalida signo post *: %s" +msgstr "E215: Nevalida signo malantaŭ *: %s" #, c-format msgid "E216: No such event: %s" @@ -1996,7 +2000,6 @@ msgstr "E216: Ne estas tia evento: %s" msgid "E216: No such group or event: %s" msgstr "E216: Ne ekzistas tia grupo aŭ evento: %s" -#. Highlight title msgid "" "\n" "--- Auto-Commands ---" @@ -2044,12 +2047,6 @@ msgstr "E350: Ne eblas krei faldon per la aktuala 'foldmethod'" msgid "E351: Cannot delete fold with current 'foldmethod'" msgstr "E351: Ne eblas forviŝi faldon per la aktuala 'foldmethod'" -#, c-format -msgid "+--%3ld line folded " -msgid_plural "+--%3ld lines folded " -msgstr[0] "+--%3ld linio faldita " -msgstr[1] "+--%3ld linioj falditaj " - msgid "E222: Add to read buffer" msgstr "E222: Aldoni al lega bufro" @@ -2186,18 +2183,15 @@ msgstr "Serĉi kion:" msgid "Replace with:" msgstr "Anstataŭigi per:" -#. whole word only button msgid "Match whole word only" msgstr "Kongrui kun nur plena vorto" -#. match case button msgid "Match case" msgstr "Uskleca kongruo" msgid "Direction" msgstr "Direkto" -#. 'Up' and 'Down' buttons msgid "Up" msgstr "Supren" @@ -2276,8 +2270,6 @@ msgstr "Trovi ĉenon (uzu '\\\\' por trovi '\\')" msgid "Find & Replace (use '\\\\' to find a '\\')" msgstr "Trovi kaj anstataŭigi (uzu '\\\\' por trovi '\\')" -#. We fake this: Use a filter that doesn't select anything and a default -#. * file name that won't be used. msgid "Not Used" msgstr "Ne uzata" @@ -2351,7 +2343,6 @@ msgstr "Vim - Elektilo de tiparo" msgid "Name:" msgstr "Nomo:" -#. create toggle button msgid "Show size in Points" msgstr "Montri grandon en punktoj" @@ -2597,7 +2588,6 @@ msgstr "E261: konekto cscope %s netrovita" msgid "cscope connection %s closed" msgstr "konekto cscope %s fermita" -#. should not reach here msgid "E570: fatal error in cs_manage_matches" msgstr "E570: neriparebla eraro en cs_manage_matches" @@ -2759,7 +2749,6 @@ msgstr "nevalida numero de bufro" msgid "not implemented yet" msgstr "ankoraŭ ne realigita" -#. ??? msgid "cannot set line(s)" msgstr "ne eblas meti la linio(j)n" @@ -2800,7 +2789,6 @@ msgid "" msgstr "" "ne eblas registri postalvokan komandon: bufro/fenestro estas jam forviŝiĝanta" -#. This should never happen. Famous last word? msgid "" "E280: TCL FATAL ERROR: reflist corrupt!? Please report this to vim-dev@vim." "org" @@ -2859,10 +2847,10 @@ msgid "Too many edit arguments" msgstr "Tro da argumentoj de redakto" msgid "Argument missing after" -msgstr "Argumento mankas post" +msgstr "Argumento mankas malantaŭ" msgid "Garbage after option argument" -msgstr "Forĵetindaĵo post argumento de opcio" +msgstr "Forĵetindaĵo malantaŭ argumento de opcio" msgid "Too many \"+command\", \"-c command\" or \"--cmd command\" arguments" msgstr "Tro da argumentoj \"+komando\", \"-c komando\" aŭ \"--cmd komando\"" @@ -2904,7 +2892,6 @@ msgstr "Vim: Averto: Eligo ne estas al terminalo\n" msgid "Vim: Warning: Input is not from a terminal\n" msgstr "Vim: Averto: Enigo ne estas el terminalo\n" -#. just in case.. msgid "pre-vimrc command line" msgstr "komanda linio pre-vimrc" @@ -2968,7 +2955,7 @@ msgstr "" "Argumentoj:\n" msgid "--\t\t\tOnly file names after this" -msgstr "--\t\t\tNur dosiernomoj post tio" +msgstr "--\t\t\tNur dosiernomoj malantaŭ tio" msgid "--literal\t\tDon't expand wildcards" msgstr "--literal\t\tNe malvolvi ĵokerojn" @@ -3069,8 +3056,7 @@ msgstr "" "--not-a-term\t\tPreterpasi averton por enigo/eligo, kiu ne estas terminalo" msgid "--ttyfail\t\tExit if input or output is not a terminal" -msgstr "" -"--ttyfail\t\tEliri se le eniro aŭ eliro ne estas terminalo" +msgstr "--ttyfail\t\tEliri se la eniro aŭ eliro ne estas terminalo" msgid "-u <vimrc>\t\tUse <vimrc> instead of any .vimrc" msgstr "-u <vimrc>\t\tUzi <vimrc> anstataŭ iun ajn .vimrc" @@ -3171,6 +3157,9 @@ msgstr "" msgid "-i <viminfo>\t\tUse <viminfo> instead of .viminfo" msgstr "-i <viminfo>\t\tUzi <viminfo> anstataŭ .viminfo" +msgid "--clean\t\t'nocompatible', Vim defaults, no plugins, no viminfo" +msgstr "--clean\t\t'nocompatible', defaŭltaj agordoj de Vim, neniu viminfo" + msgid "-h or --help\tPrint Help (this message) and exit" msgstr "-h aŭ --help\tAfiŝi Helpon (tiun mesaĝon) kaj eliri" @@ -3271,11 +3260,9 @@ msgstr "--windowid <HWND>\tMalfermi Vim en alia win32 fenestraĵo" msgid "No display" msgstr "Neniu ekrano" -#. Failed to send, abort. msgid ": Send failed.\n" msgstr ": Sendo malsukcesis.\n" -#. Let vim start normally. msgid ": Send failed. Trying to execute locally\n" msgstr ": Sendo malsukcesis. Provo de loka plenumo\n" @@ -3296,7 +3283,6 @@ msgstr "Neniu marko" msgid "E283: No marks matching \"%s\"" msgstr "E283: Neniu marko kongruas kun \"%s\"" -#. Highlight title msgid "" "\n" "mark line col file/text" @@ -3304,7 +3290,6 @@ msgstr "" "\n" "mark linio kol dosiero/teksto" -#. Highlight title msgid "" "\n" " jump line col file/text" @@ -3312,7 +3297,6 @@ msgstr "" "\n" " salt linio kol dosiero/teksto" -#. Highlight title msgid "" "\n" "change line col text" @@ -3327,7 +3311,6 @@ msgstr "" "\n" "# Markoj de dosiero:\n" -#. Write the jumplist with -' msgid "" "\n" "# Jumplist (newest first):\n" @@ -3397,7 +3380,6 @@ msgstr "E298: Ĉu ne akiris blokon n-ro 2?" msgid "E843: Error while updating swap file crypt" msgstr "E843: Eraro dum ĝisdatigo de ĉifrada permutodosiero .swp" -#. could not (re)open the swap file, what can we do???? msgid "E301: Oops, lost the swap file!!!" msgstr "E301: Ve, perdis la permutodosieron .swp!!!" @@ -3581,7 +3563,6 @@ msgid "Using crypt key from swap file for the text file.\n" msgstr "" "Uzas ŝlosilon de ĉifrado el permuto dosiero .swp por la teksta dosiero.\n" -#. use msg() to start the scrolling properly msgid "Swap files found:" msgstr "Permutodosiero .swp trovita:" @@ -3751,8 +3732,6 @@ msgstr "Dum malfermo de dosiero \"" msgid " NEWER than swap file!\n" msgstr " PLI NOVA ol permutodosiero .swp!\n" -#. Some of these messages are long to allow translation to -#. * other languages. msgid "" "\n" "(1) Another program may be editing the same file. If this is the case,\n" @@ -3842,7 +3821,6 @@ msgstr "E328: Menuo nur ekzistas en alia reĝimo" msgid "E329: No menu \"%s\"" msgstr "E329: Neniu menuo \"%s\"" -#. Only a mnemonic or accelerator is not valid. msgid "E792: Empty menu name" msgstr "E792: Malplena nomo de menuo" @@ -3855,8 +3833,6 @@ msgstr "E331: Aldono de menueroj direkte al menuzono estas malpermesita" msgid "E332: Separator cannot be part of a menu path" msgstr "E332: Disigilo ne rajtas esti ero de vojo de menuo" -#. Now we have found the matching menu, and we list the mappings -#. Highlight title msgid "" "\n" "--- Menus ---" @@ -3867,6 +3843,10 @@ msgstr "" msgid "Tear off this menu" msgstr "Disigi tiun menuon" +#, c-format +msgid "E335: Menu not defined for %s mode" +msgstr "E335: Menuo ne estas difinita por reĝimo %s" + msgid "E333: Menu path must lead to a menu item" msgstr "E333: Vojo de menuo devas konduki al menuero" @@ -3874,10 +3854,6 @@ msgstr "E333: Vojo de menuo devas konduki al menuero" msgid "E334: Menu not found: %s" msgstr "E334: Menuo netrovita: %s" -#, c-format -msgid "E335: Menu not defined for %s mode" -msgstr "E335: Menuo ne estas difinita por reĝimo %s" - msgid "E336: Menu path must lead to a sub-menu" msgstr "E336: Vojo de menuo devas konduki al sub-menuo" @@ -3949,7 +3925,6 @@ msgstr "Dialogujo de dosiera konservo" msgid "Open File dialog" msgstr "Dialogujo de dosiera malfermo" -#. TODO: non-GUI file selector here msgid "E338: Sorry, no file browser in console mode" msgstr "E338: Bedaŭrinde ne estas dosierfoliumilo en konzola reĝimo" @@ -4116,8 +4091,10 @@ msgstr "E662: Ĉe komenco de ŝanĝlisto" msgid "E663: At end of changelist" msgstr "E663: Ĉe fino de ŝanĝlisto" -msgid "Type :quit<Enter> to exit Vim" -msgstr "Tajpu \":quit<Enenklavo>\" por eliri el Vim" +msgid "Type :qa! and press <Enter> to abandon all changes and exit Vim" +msgstr "" +"Tajpu :qa! kaj premu <Enenklavon> por forlasi ĉiujn ŝanĝojn kaj eliri el " +"Vim" #, c-format msgid "1 line %sed 1 time" @@ -4149,7 +4126,6 @@ msgstr "%ld linioj krommarĝenitaj " msgid "E748: No previously used register" msgstr "E748: Neniu reĝistro antaŭe uzata" -#. must display the prompt msgid "cannot yank; delete anyway" msgstr "ne eblas kopii; tamen forviŝi" @@ -4164,25 +4140,30 @@ msgstr "%ld linioj ŝanĝitaj" msgid "freeing %ld lines" msgstr "malokupas %ld liniojn" -msgid "block of 1 line yanked" -msgstr "bloko de 1 linio kopiita" +#, c-format +msgid " into \"%c" +msgstr " en \"%c" -msgid "1 line yanked" -msgstr "1 linio kopiita" +#, c-format +msgid "block of 1 line yanked%s" +msgstr "bloko de 1 linio kopiita%s" #, c-format -msgid "block of %ld lines yanked" -msgstr "bloko de %ld linioj kopiita" +msgid "1 line yanked%s" +msgstr "1 linio kopiita%s" #, c-format -msgid "%ld lines yanked" -msgstr "%ld linioj kopiitaj" +msgid "block of %ld lines yanked%s" +msgstr "bloko de %ld linioj kopiita%s" + +#, c-format +msgid "%ld lines yanked%s" +msgstr "%ld linioj kopiitaj%s" #, c-format msgid "E353: Nothing in register %s" msgstr "E353: Nenio en reĝistro %s" -#. Highlight title msgid "" "\n" "--- Registers ---" @@ -4243,9 +4224,6 @@ msgstr "" msgid "(+%ld for BOM)" msgstr "(+%ld por BOM)" -msgid "%<%f%h%m%=Page %N" -msgstr "%<%f%h%m%=Folio %N" - msgid "Thanks for flying Vim" msgstr "Dankon pro flugi per Vim" @@ -4262,7 +4240,7 @@ msgid "E846: Key code not set" msgstr "E846: Klavkodo ne agordita" msgid "E521: Number required after =" -msgstr "E521: Nombro bezonata post =" +msgstr "E521: Nombro bezonata malantaŭ =" msgid "E522: Not found in termcap" msgstr "E522: Netrovita en termcap" @@ -4304,7 +4282,7 @@ msgstr "E525: Ĉeno de nula longo" #, c-format msgid "E526: Missing number after <%s>" -msgstr "E526: Mankas nombro post <%s>" +msgstr "E526: Mankas nombro malantaŭ <%s>" msgid "E527: Missing comma" msgstr "E527: Mankas komo" @@ -4332,7 +4310,7 @@ msgstr "E534: Nevalida larĝa tiparo" #, c-format msgid "E535: Illegal character after <%c>" -msgstr "E535: Nevalida signo post <%c>" +msgstr "E535: Nevalida signo malantaŭ <%c>" msgid "E536: comma required" msgstr "E536: komo bezonata" @@ -4353,6 +4331,9 @@ msgstr "E541: tro da elementoj" msgid "E542: unbalanced groups" msgstr "E542: misekvilibraj grupoj" +msgid "E946: Cannot make a terminal with running job modifiable" +msgstr "E946: Ne eblas igi modifebla terminalon kun aktiva tasko" + msgid "E590: A preview window already exists" msgstr "E590: Antaŭvida fenestro jam ekzistas" @@ -4371,9 +4352,6 @@ msgstr "E594: Bezonas almenaŭ %d kolumnojn" msgid "E355: Unknown option: %s" msgstr "E355: Nekonata opcio: %s" -#. There's another character after zeros or the string -#. * is empty. In both cases, we are trying to set a -#. * num option using a string. #, c-format msgid "E521: Number required: &%s = '%s'" msgstr "E521: Nombro bezonata: &%s = '%s'" @@ -4415,7 +4393,7 @@ msgstr "E357: 'langmap': Kongrua signo mankas por %s" #, c-format msgid "E358: 'langmap': Extra characters after semicolon: %s" -msgstr "E358: 'langmap': Ekstraj signoj post punktokomo: %s" +msgstr "E358: 'langmap': Ekstraj signoj malantaŭ punktokomo: %s" msgid "cannot open " msgstr "ne eblas malfermi " @@ -4446,7 +4424,6 @@ msgstr "ne eblas ŝanĝi reĝimon de konzolo?!\n" msgid "mch_get_shellsize: not a console??\n" msgstr "mch_get_shellsize: ne estas konzolo??\n" -#. if Vim opened a window: Executing a shell may cause crashes msgid "E360: Cannot execute shell with -f option" msgstr "E360: Ne eblas plenumi ŝelon kun opcio -f" @@ -4672,7 +4649,6 @@ msgstr "E376: Nevalida %%%c en prefikso de formata ĉeno" msgid "E377: Invalid %%%c in format string" msgstr "E377: Nevalida %%%c en formata ĉeno" -#. nothing found msgid "E378: 'errorformat' contains no pattern" msgstr "E378: 'errorformat' enhavas neniun ŝablonon" @@ -4711,9 +4687,6 @@ msgstr "E381: Ĉe la supro de stako de rapidriparo" msgid "No entries" msgstr "Neniu ano" -msgid "E382: Cannot write, 'buftype' option is set" -msgstr "E382: Ne eblas skribi, opcio 'buftype' estas ŝaltita" - msgid "Error file" msgstr "Erara Dosiero" @@ -4736,7 +4709,13 @@ msgstr "E369: nevalida ano en %s%%[]" #, c-format msgid "E769: Missing ] after %s[" -msgstr "E769: Mankas ] post %s[" +msgstr "E769: Mankas ] malantaŭ %s[" + +msgid "E944: Reverse range in character class" +msgstr "E944: Inversa amplekso en klaso de signoj" + +msgid "E945: Range too large in character class" +msgstr "E945: tro larga amplekso de klaso de signoj" #, c-format msgid "E53: Unmatched %s%%(" @@ -4759,12 +4738,15 @@ msgstr "E67: \\z1 kaj aliaj estas nepermeseblaj tie" #, c-format msgid "E69: Missing ] after %s%%[" -msgstr "E69: Mankas ] post %s%%[" +msgstr "E69: Mankas ] malantaŭ %s%%[" #, c-format msgid "E70: Empty %s%%[]" msgstr "E70: Malplena %s%%[]" +msgid "E65: Illegal back reference" +msgstr "E65: Nevalida retro-referenco" + msgid "E339: Pattern too long" msgstr "E339: Ŝablono tro longa" @@ -4780,7 +4762,7 @@ msgstr "E52: Neekvilibra \\z(" #, c-format msgid "E59: invalid character after %s@" -msgstr "E59: nevalida signo post %s@" +msgstr "E59: nevalida signo malantaŭ %s@" #, c-format msgid "E60: Too many complex %s{...}s" @@ -4801,19 +4783,16 @@ msgstr "E63: nevalida uzo de \\_" msgid "E64: %s%c follows nothing" msgstr "E64: %s%c sekvas nenion" -msgid "E65: Illegal back reference" -msgstr "E65: Nevalida retro-referenco" - msgid "E68: Invalid character after \\z" -msgstr "E68: Nevalida signo post \\z" +msgstr "E68: Nevalida signo malantaŭ \\z" #, c-format msgid "E678: Invalid character after %s%%[dxouU]" -msgstr "E678: Nevalida signo post %s%%[dxouU]" +msgstr "E678: Nevalida signo malantaŭ %s%%[dxouU]" #, c-format msgid "E71: Invalid character after %s%%" -msgstr "E71: Nevalida signo post %s%%" +msgstr "E71: Nevalida signo malantaŭ %s%%" #, c-format msgid "E554: Syntax error in %s{...}" @@ -4845,7 +4824,7 @@ msgstr "E866: (NFA-regulesprimo) Mispoziciigita %c" #, c-format msgid "E877: (NFA regexp) Invalid character class: %ld" -msgstr "E877: (NFA-regulesprimo) Nevalida klaso de signo: %ld" +msgstr "E877: (NFA-regulesprimo) Nevalida klaso de signoj: %ld" #, c-format msgid "E867: (NFA) Unknown operator '\\z%c'" @@ -4855,7 +4834,6 @@ msgstr "E867: (NFA) Nekonata operatoro '\\z%c'" msgid "E867: (NFA) Unknown operator '\\%%%c'" msgstr "E867: (NFA) Nekonata operatoro '\\%%%c'" -#. should never happen msgid "E868: Error building NFA with equivalence class!" msgstr "E868: Eraro dum prekomputado de NFA kun ekvivalentoklaso!" @@ -4866,13 +4844,11 @@ msgstr "E869: (NFA) Nekonata operatoro '\\@%c'" msgid "E870: (NFA regexp) Error reading repetition limits" msgstr "E870: (NFS-regulesprimo) Eraro dum legado de limoj de ripeto" -#. Can't have a multi follow a multi. msgid "E871: (NFA regexp) Can't have a multi follow a multi !" msgstr "" "E871: (NFA-regulesprimo) Ne povas havi mult-selekton tuj post alia mult-" "selekto!" -#. Too many `(' msgid "E872: (NFA regexp) Too many '('" msgstr "E872: (NFA-regulesprimo) tro da '('" @@ -4893,7 +4869,7 @@ msgstr "" "statoj en la staplo" msgid "E876: (NFA regexp) Not enough space to store the whole NFA " -msgstr "E876: (NFA-regulesprimo) ne sufiĉa spaco por enmomorigi la tutan NFA " +msgstr "E876: (NFA-regulesprimo) ne sufiĉa spaco por enmemorigi la tutan NFA " msgid "E878: (NFA) Could not allocate memory for branch traversal!" msgstr "E878: (NFA) Ne povis asigni memoron por traigi branĉojn!" @@ -4975,12 +4951,11 @@ msgid "E385: search hit BOTTOM without match for: %s" msgstr "E385: serĉo atingis SUBON sen trovi: %s" msgid "E386: Expected '?' or '/' after ';'" -msgstr "E386: Atendis '?' aŭ '/' post ';'" +msgstr "E386: Atendis '?' aŭ '/' malantaŭ ';'" msgid " (includes previously listed match)" msgstr " (enhavas antaŭe listigitajn kongruojn)" -#. cursor at status line msgid "--- Included files " msgstr "--- Inkluzivitaj dosieroj " @@ -5057,8 +5032,6 @@ msgstr "Bedaŭrinde ne estas sugestoj" msgid "Sorry, only %ld suggestions" msgstr "Bedaŭrinde estas nur %ld sugestoj" -#. for when 'cmdheight' > 1 -#. avoid more prompt #, c-format msgid "Change \"%.*s\" to:" msgstr "Anstataŭigi \"%.*s\" per:" @@ -5340,10 +5313,6 @@ msgstr "Densigis %d de %d nodoj; %d (%d%%) restantaj" msgid "Reading back spell file..." msgstr "Relegas la dosieron de literumo..." -#. -#. * Go through the trie of good words, soundfold each word and add it to -#. * the soundfold trie. -#. msgid "Performing soundfolding..." msgstr "Fonetika analizado..." @@ -5398,18 +5367,38 @@ msgstr "Vorto '%.*s' aldonita al %s" msgid "E763: Word characters differ between spell files" msgstr "E763: Signoj de vorto malsamas tra literumaj dosieroj" -#. This should have been checked when generating the .spl -#. * file. msgid "E783: duplicate char in MAP entry" msgstr "E783: ripetita signo en rikordo MAP" msgid "No Syntax items defined for this buffer" msgstr "Neniu sintaksa elemento difinita por tiu bufro" +msgid "syntax conceal on" +msgstr "sintakso de conceal ŝaltata" + +msgid "syntax conceal off" +msgstr "sintakso de conceal malŝaltita" + #, c-format msgid "E390: Illegal argument: %s" msgstr "E390: Nevalida argumento: %s" +msgid "syntax case ignore" +msgstr "sintakso ignoras usklecon" + +msgid "syntax case match" +msgstr "sintakso konsideras usklecon" + +msgid "syntax spell toplevel" +msgstr "literumado en teksto sen sintaksa grupo" + +msgid "syntax spell notoplevel" +msgstr "sen literumado en teksto sen sintaksa grupo" + +msgid "syntax spell default" +msgstr "" +"literumado en teksto sen sintaksa grupo, nur se ne estas @Spell aŭ @NoSpell" + msgid "syntax iskeyword " msgstr "sintakso iskeyword " @@ -5491,7 +5480,7 @@ msgstr "E789: Mankas ']': %s" #, c-format msgid "E890: trailing char after ']': %s]%s" -msgstr "E890: vosta signo post ']': %s]%s" +msgstr "E890: vosta signo malantaŭ ']': %s]%s" #, c-format msgid "E398: Missing '=': %s" @@ -5513,7 +5502,7 @@ msgstr "E401: Disigilo de ŝablono netrovita: %s" #, c-format msgid "E402: Garbage after pattern: %s" -msgstr "E402: Forĵetindaĵo post ŝablono: %s" +msgstr "E402: Forĵetindaĵo malantaŭ ŝablono: %s" msgid "E403: syntax sync: line continuations pattern specified twice" msgstr "E403: sintaksa sinkronigo: ŝablono de linia daŭrigo specifita dufoje" @@ -5645,7 +5634,6 @@ msgstr "E428: Ne eblas iri preter lastan kongruan etikedon" msgid "File \"%s\" does not exist" msgstr "La dosiero \"%s\" ne ekzistas" -#. Give an indication of the number of matching tags #, c-format msgid "tag %d of %d%s" msgstr "etikedo %d de %d%s" @@ -5660,7 +5648,6 @@ msgstr " Uzo de etikedo kun malsama uskleco!" msgid "E429: File \"%s\" does not exist" msgstr "E429: Dosiero \"%s\" ne ekzistas" -#. Highlight title msgid "" "\n" " # TO tag FROM line in file/text" @@ -5691,7 +5678,6 @@ msgstr "Antaŭ bajto %ld" msgid "E432: Tags file not sorted: %s" msgstr "E432: Etikeda dosiero ne estas ordigita: %s" -#. never opened any tags file msgid "E433: No tags file" msgstr "E433: Neniu etikeda dosiero" @@ -5727,7 +5713,6 @@ msgstr "E436: Neniu rikordo \"%s\" en termcap" msgid "E437: terminal capability \"cm\" required" msgstr "E437: kapablo de terminalo \"cm\" bezonata" -#. Highlight title msgid "" "\n" "--- Terminal keys ---" @@ -5738,6 +5723,21 @@ msgstr "" msgid "Cannot open $VIMRUNTIME/rgb.txt" msgstr "Ne povas malfermi $VIMRUNTIME/rgb.txt" +msgid "Terminal" +msgstr "Terminalo" + +msgid "Terminal-finished" +msgstr "Terminalo-finiĝis" + +msgid "active" +msgstr "aktiva" + +msgid "running" +msgstr "ruliĝas" + +msgid "finished" +msgstr "finiĝis" + msgid "new shell started\n" msgstr "nova ŝelo lanĉita\n" @@ -5747,12 +5747,9 @@ msgstr "Vim: Eraro dum legado de eniro, elironta...\n" msgid "Used CUT_BUFFER0 instead of empty selection" msgstr "Uzis CUT_BUFFER0 anstataŭ malplenan apartigon" -#. This happens when the FileChangedRO autocommand changes the -#. * file in a way it becomes shorter. msgid "E881: Line count changed unexpectedly" msgstr "E881: Nombro de linioj ŝanĝiĝis neatendite" -#. must display the prompt msgid "No undo possible; continue anyway" msgstr "Malfaro neebla; tamen daŭrigi" @@ -5987,6 +5984,10 @@ msgid "E126: Missing :endfunction" msgstr "E126: Mankas \":endfunction\"" #, c-format +msgid "W22: Text found after :endfunction: %s" +msgstr "W22: Teksto trovita malantaŭ :endfunction: %s" + +#, c-format msgid "E707: Function name conflicts with variable: %s" msgstr "E707: Nomo de funkcio konfliktas kun variablo: %s" @@ -6382,7 +6383,6 @@ msgstr "Kompari per Vim" msgid "Edit with &Vim" msgstr "Redakti per &Vim" -#. Now concatenate msgid "Edit with existing Vim - " msgstr "Redakti per ekzistanta Vim - " @@ -6401,10 +6401,6 @@ msgstr "Serĉvojo estas tro longa!" msgid "--No lines in buffer--" msgstr "--Neniu linio en bufro--" -#. -#. * The error messages that can be shared are included here. -#. * Excluded are errors that are only used once and debugging messages. -#. msgid "E470: Command aborted" msgstr "E470: komando ĉesigita" @@ -6590,12 +6586,6 @@ msgstr "E484: Ne eblas malfermi dosieron %s" msgid "E485: Can't read file %s" msgstr "E485: Ne eblas legi dosieron %s" -msgid "E37: No write since last change (add ! to override)" -msgstr "E37: Neniu skribo de post lasta ŝanĝo (aldonu ! por transpasi)" - -msgid "E37: No write since last change" -msgstr "E37: Neniu skribo de post lasta ŝanĝo" - msgid "E38: Null argument" msgstr "E38: Nula argumento" @@ -6730,8 +6720,8 @@ msgstr "E592: 'winwidth' ne rajtas esti malpli ol 'winminwidth'" msgid "E80: Error while writing" msgstr "E80: Eraro dum skribado" -msgid "Zero count" -msgstr "Nul kvantoro" +msgid "E939: Positive count required" +msgstr "E939: Pozitiva kvantoro bezonata" msgid "E81: Using <SID> not in a script context" msgstr "E81: Uzo de <SID> ekster kunteksto de skripto" @@ -6875,7 +6865,6 @@ msgstr "konstruilo de listo ne akceptas ŝlosilvortajn argumentojn" msgid "list index out of range" msgstr "indekso de listo ekster limoj" -#. No more suitable format specifications in python-2.3 #, c-format msgid "internal error: failed to get vim list item %d" msgstr "interna eraro: obteno de vim-a listero %d malsukcesis" diff --git a/src/nvim/po/es.po b/src/nvim/po/es.po index ed96e4de94..10b661a5d2 100644 --- a/src/nvim/po/es.po +++ b/src/nvim/po/es.po @@ -1197,7 +1197,7 @@ msgstr "E141: No existe un nombre de archivo para el búfer %<PRId64>" #: ../ex_cmds.c:2412 msgid "E142: File not written: Writing is disabled by 'write' option" msgstr "" -"E142: No se ha escrito el archivo: escritura desactivada por \n" +"E142: No se ha escrito el archivo: escritura desactivada por " "la opción 'write'" #: ../ex_cmds.c:2434 @@ -2209,7 +2209,7 @@ msgstr "es de solo lectura (añada ! para sobreescribir)" #: ../fileio.c:2886 msgid "E506: Can't write to backup file (add ! to override)" msgstr "" -"E506: No se pudo escribir en el archivo de recuperación\n" +"E506: No se pudo escribir en el archivo de recuperación " "(añada ! para forzar la orden)" #: ../fileio.c:2898 @@ -3136,7 +3136,7 @@ msgstr "" #: ../hardcopy.c:2254 msgid "E675: No default font specified for multi-byte printing." msgstr "" -"E675: No se ha definido un tipo de letra predeterminado para impresión\n" +"E675: No se ha definido un tipo de letra predeterminado para impresión " "multi-byte" #: ../hardcopy.c:2426 @@ -3393,7 +3393,7 @@ msgstr "Basura después de la opción" #: ../main.c:152 msgid "Too many \"+command\", \"-c command\" or \"--cmd command\" arguments" msgstr "" -"Demasiados argumentos tales como: \"+orden\", \"-c orden\" \n" +"Demasiados argumentos tales como: \"+orden\", \"-c orden\" " "o \"--cmd orden\"" #: ../main.c:154 @@ -3797,7 +3797,7 @@ msgstr "E302: No pude cambiar el nombre del archivo de intercambio" #, c-format msgid "E303: Unable to open swap file for \"%s\", recovery impossible" msgstr "" -"E303: Incapaz de abrir el archivo de intercambio para %s,\n" +"E303: Incapaz de abrir el archivo de intercambio para %s, " "recuperación imposible" #: ../memline.c:666 @@ -3916,7 +3916,7 @@ msgstr "??? desde aquí hasta ???FIN las líneas pueden estar desordenadas" #: ../memline.c:1164 msgid "??? from here until ???END lines may have been inserted/deleted" msgstr "" -"??? desde aquí hasta ???FIN las líneas pueden haber sido\n" +"??? desde aquí hasta ???FIN las líneas pueden haber sido " "insertadas/borradas" #: ../memline.c:1181 @@ -3931,7 +3931,7 @@ msgstr "E311: Recuperación interrumpida" msgid "" "E312: Errors detected while recovering; look for lines starting with ???" msgstr "" -"E312: Se han detectado errores al recuperar; busque líneas que\n" +"E312: Se han detectado errores al recuperar; busque líneas que " "empiecen con ???" #: ../memline.c:1245 @@ -5382,7 +5382,7 @@ msgstr "E756: La corrección ortográfica está desactivada" #, c-format msgid "Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\"" msgstr "" -"Advertencia: No se pudo hallar la lista de palabras \"%s.%s.spl\" \n" +"Advertencia: No se pudo hallar la lista de palabras \"%s.%s.spl\" " "or \"%s.ascii.spl\"" #: ../spell.c:2473 @@ -5443,8 +5443,7 @@ msgid "" "%d" msgstr "" "Definir COMPOUNDFORBIDFLAG después de un elemento PFX puede dar resultados " -"erróneos\n" -"en %s línea %d" +"erróneos en %s línea %d" #: ../spell.c:4731 #, c-format @@ -5453,8 +5452,7 @@ msgid "" "%d" msgstr "" "Definir COMPOUNDPERMITFLAG después de un ítem PFX puede dar resultados " -"erróneos\n" -"en %s línea %d" +"erróneos en %s línea %d" #: ../spell.c:4747 #, c-format @@ -5485,7 +5483,7 @@ msgstr "Valor equivocado de CHECKCOMPOUNDPATTERN en %s línea %d: %s" #, c-format msgid "Different combining flag in continued affix block in %s line %d: %s" msgstr "" -"Marca de combinación diferente en el bloque de afijos continuo\n" +"Marca de combinación diferente en el bloque de afijos continuo " "en %s línea %d: %s" #: ../spell.c:4850 @@ -5500,8 +5498,7 @@ msgid "" "line %d: %s" msgstr "" "Afijo usado también para BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST " -"en\n" -"%s línea %d: %s" +"en %s línea %d: %s" #: ../spell.c:4893 #, c-format @@ -5942,7 +5939,7 @@ msgstr "E402: Basura después del patrón: %s" #: ../syntax.c:5120 msgid "E403: syntax sync: line continuations pattern specified twice" msgstr "" -"E403: Sincronización de sintaxis: Se especificó dos veces un\n" +"E403: Sincronización de sintaxis: Se especificó dos veces un " "patrón de continuación de línea" #: ../syntax.c:5169 @@ -6569,7 +6566,7 @@ msgstr "E813: No se puede cerrar la ventana de autocmd" #: ../window.c:1814 msgid "E814: Cannot close window, only autocmd window would remain" msgstr "" -"E814: No se pudo cerrar la última ventana, solo quedará\n" +"E814: No se pudo cerrar la última ventana, solo quedará " "la ventana de autocmd" #: ../window.c:2717 diff --git a/src/nvim/po/fi.po b/src/nvim/po/fi.po index 3551c07ff2..8fa0cc48d4 100644 --- a/src/nvim/po/fi.po +++ b/src/nvim/po/fi.po @@ -1,6 +1,6 @@ # Finnish translation for Vim. # Copyright (C) 2003-2006 Free Software Foundation, Inc. -# 2007-2016, Flammie Pirinen <flammie@iki.fi> +# 2007-2018, Flammie Pirinen <flammie@iki.fi> # # Jargonia ei ole yritetty suotta kotoperäistää missä teknisempi lainasanasto # tulee paremmin kyseeseen. @@ -313,7 +313,6 @@ msgstr "E90: Ei voi vapauttaa viimeistä puskuria" msgid "E84: No modified buffer found" msgstr "E84: Ei muokattuja puskureita" -#. back where we started, didn't find anything. msgid "E85: There is no listed buffer" msgstr "E85: Luetteloitua puskuria ei ole" @@ -390,7 +389,6 @@ msgstr "1 rivi --%d %%--" msgid "[No Name]" msgstr "[Nimetön]" -#. must be a help buffer msgid "help" msgstr "ohje" @@ -495,7 +493,6 @@ msgstr "E791: Tyhjä keymap-kenttä" msgid " Keyword completion (^N^P)" msgstr " Avainsanatäydennys (^N^P)" -#. ctrl_x_mode == 0, ^P/^N compl. msgid " ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)" msgstr " ^X-tila (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)" @@ -570,10 +567,6 @@ msgstr "Luetaan tägejä." msgid " Adding" msgstr " Lisätään" -#. showmode might reset the internal line pointers, so it must -#. * be called before line = ml_get(), or when this address is no -#. * longer needed. -- Acevedo. -#. msgid "-- Searching..." msgstr "-- Haetaan..." @@ -1418,6 +1411,18 @@ msgstr "E907: Käytettiin erikoisarvoa Floattina" msgid "E808: Number or Float required" msgstr "E808: Number tai Float vaaditaan" +#, c-format +msgid "line %ld: %s" +msgstr "rivi %ld: %s" + +#, c-format +msgid "Breakpoint in \"%s%s\" line %ld" +msgstr "Katkaisukohta %s%s rivillä %ld" + +#, c-format +msgid "%3d %s %s line %ld" +msgstr "%3d %s %s rivi %ld" + # puhutaan merkin ulkoasusta snprintf(..., c, c, c, c) #, c-format msgid "<%s>%s%s %d, Hex %02x, Octal %03o" @@ -1545,7 +1550,7 @@ msgstr " 1 rivillä" #~ msgid " on %<PRId64> lines" #~ msgstr " %ld rivillä" -msgid "E147: Cannot do :global recursive" +msgid "E147: Cannot do :global recursive with a range" msgstr "E147: :globalia ei voi suorittaa rekursiivisesti" msgid "E148: Regular expression missing from global" @@ -1575,6 +1580,10 @@ msgid "Sorry, help file \"%s\" not found" msgstr "ohjetiedostoa %s ei löydy" #, c-format +msgid "E151: No match: %s" +msgstr "E151: Ei täsmää: %s" + +#, c-format msgid "E152: Cannot open %s for writing" msgstr "E152: Ei voi avata tiedostoa %s kirjoittamista varten" @@ -1926,7 +1935,6 @@ msgstr "E189: %s on jo olemassa (lisää komentoon ! ohittaaksesi)" msgid "E190: Cannot open \"%s\" for writing" msgstr "E190: Tiedostoa %s ei voitu avata kirjoittamista varten" -#. set mark msgid "E191: Argument must be a letter or forward/backward quote" msgstr "E191: Argumentin eteen- tai taaksepäin lainaukseen pitää olla kirjain" @@ -1979,7 +1987,6 @@ msgstr "Poikkeus poistettu: %s" #~ msgid "%s, line %<PRId64>" #~ msgstr "%s, rivi %ld" -#. always scroll up, don't overwrite #, c-format msgid "Exception caught: %s" msgstr "Poikkeus otettu kiinni: %s" @@ -2005,7 +2012,6 @@ msgstr "Virhe ja keskeytys" msgid "Error" msgstr "Virhe" -#. if (pending & CSTP_INTERRUPT) msgid "Interrupt" msgstr "Keskeytys" @@ -2048,15 +2054,12 @@ msgstr "E601: liian monta tasoa :try-komennossa" msgid "E603: :catch without :try" msgstr "E603: :catch ilman komentoa :try" -#. Give up for a ":catch" after ":finally" and ignore it. -#. * Just parse. msgid "E604: :catch after :finally" msgstr "E604: :catch ilman komentoa :finally" msgid "E606: :finally without :try" msgstr "E606: :finally ilman komentoa :try" -#. Give up for a multiple ":finally" and ignore it. msgid "E607: multiple :finally" msgstr "E607: :finally monta kertaa" @@ -2147,7 +2150,6 @@ msgstr "" msgid "E201: *ReadPre autocommands must not change current buffer" msgstr "E201: *ReadPre-autocommand-komennot eivät saa muuttaa puskuria" -#. Re-opening the original file failed! msgid "E202: Conversion made file unreadable!" msgstr "E202: Muunnos teki tiedostosta lukukelvottoman." @@ -2469,7 +2471,6 @@ msgstr "E216: Eventtiä ei ole: %s" msgid "E216: No such group or event: %s" msgstr "E216: Ryhmää tai eventtiä ei ole: %s" -#. Highlight title msgid "" "\n" "--- Auto-Commands ---" @@ -3113,7 +3114,6 @@ msgstr "E261: cscope-yhteys %s puuttuu" msgid "cscope connection %s closed" msgstr "cscope-yhteys %s on katkaistu" -#. should not reach here msgid "E570: fatal error in cs_manage_matches" msgstr "E570: kriittinen virhe cs_manage_matches-funktiossa" @@ -3176,7 +3176,6 @@ msgstr "Vim: Varoitus: Tuloste ei mene terminaalille\n" msgid "Vim: Warning: Input is not from a terminal\n" msgstr "Vim: Varoitus: Syöte ei tule terminaalilta\n" -#. just in case.. msgid "pre-vimrc command line" msgstr "esi-vimrc-komentorivi" @@ -3394,7 +3393,6 @@ msgstr "Ei asetettuja merkkejä" msgid "E283: No marks matching \"%s\"" msgstr "E283: Mikään merkki ei täsmää ilmaukseen \"%s\"" -#. Highlight title msgid "" "\n" "mark line col file/text" @@ -3402,7 +3400,6 @@ msgstr "" "\n" "merkki rivi sarake tiedosto/teksti" -#. Highlight title msgid "" "\n" " jump line col file/text" @@ -3410,7 +3407,6 @@ msgstr "" "\n" "hyppy rivi sarake tiedosto/teksti" -#. Highlight title msgid "" "\n" "change line col text" @@ -3445,7 +3441,6 @@ msgstr "E298: Lohko 1:tä ei saatu?" msgid "E298: Didn't get block nr 2?" msgstr "E298: Lohko 2:ta ei saatu?" -#. could not (re)open the swap file, what can we do???? msgid "E301: Oops, lost the swap file!!!" msgstr "E301: Hups, swap-tiedosto hävisi!" @@ -3585,7 +3580,6 @@ msgstr "" "Voit poistaa .swp-tiedosto nyt.\n" "\n" -#. use msg() to start the scrolling properly msgid "Swap files found:" msgstr "Swap-tiedostoja löytyi:" @@ -3751,8 +3745,6 @@ msgstr "Avattaessa tiedostoa " msgid " NEWER than swap file!\n" msgstr " joka on UUDEMPI kuin swap-tiedosto!\n" -#. Some of these messages are long to allow translation to -#. * other languages. msgid "" "\n" "(1) Another program may be editing the same file. If this is the case,\n" @@ -3859,7 +3851,6 @@ msgstr "E328: Valikko on olemassa vain toisessa tilassa" msgid "E329: No menu \"%s\"" msgstr "E329: Ei valikkoa %s" -#. Only a mnemonic or accelerator is not valid. msgid "E792: Empty menu name" msgstr "E792: tyhjä valikkonimi" @@ -3872,8 +3863,6 @@ msgstr "E331: Valikkokohtia ei saa lisätä suoraan valikkopalkkiin" msgid "E332: Separator cannot be part of a menu path" msgstr "E332: Erotin ei voi olla valikkopolun osa" -#. Now we have found the matching menu, and we list the mappings -#. Highlight title msgid "" "\n" "--- Menus ---" @@ -4354,7 +4343,6 @@ msgstr "E376: Virheellinen %%%c muotoilumerkkijonon alussa" msgid "E377: Invalid %%%c in format string" msgstr "E377: Virheellinen %%%c muotoilumerkkijonossa" -#. nothing found msgid "E378: 'errorformat' contains no pattern" msgstr "E378: errorformatissa ei ole kuvioita" @@ -4479,9 +4467,6 @@ msgstr "E63: väärinkäytetty \\_" msgid "E64: %s%c follows nothing" msgstr "E64: %s%c jälkeen ei minkään" -msgid "E65: Illegal back reference" -msgstr "E65: Virheellinen täsmäysviittaus" - msgid "E68: Invalid character after \\z" msgstr "E68: Virheellinen merkki ilmauksen \\z jälkeen" @@ -4587,7 +4572,6 @@ msgstr "E386: ;:n jälkeen pitää olla ? tai /" msgid " (includes previously listed match)" msgstr " (sisältää viimeksi luetellun täsmäyksen)" -#. cursor at status line msgid "--- Included files " msgstr "--- Sisällytetyt tiedostot " @@ -4735,7 +4719,7 @@ msgstr " EPÄONNISTUI" #~ msgstr "E886: Viminfo-tiedostoa ei voit uudelleennimetä nimelle %s" #, fuzzy, c-format -#~ msgid "Did not rename %s because %s does not looks like a ShaDa file" +#~ msgid "Did not rename %s because %s does not look like a ShaDa file" #~ msgstr " [ei näytä Vimin swap-tiedostolta]" #, c-format @@ -4841,8 +4825,6 @@ msgstr "ei ehdotuksia" #~ msgid "Sorry, only %<PRId64> suggestions" #~ msgstr "vain %ld ehdotusta" -#. for when 'cmdheight' > 1 -#. avoid more prompt #, c-format msgid "Change \"%.*s\" to:" msgstr "Muuta %.*s:" @@ -5430,7 +5412,6 @@ msgstr "E428: Ei voida edetä viimeisen täsmäävän tägin ohi" msgid "File \"%s\" does not exist" msgstr "Tiedostoa %s ei ole" -#. Give an indication of the number of matching tags #, c-format msgid "tag %d of %d%s" msgstr "tägi %d/%d%s" @@ -5445,7 +5426,6 @@ msgstr " Tägissä eri kirjaintaso" msgid "E429: File \"%s\" does not exist" msgstr "E429: Tiedostoa %s ei ole" -#. Highlight title msgid "" "\n" " # TO tag FROM line in file/text" @@ -5472,7 +5452,6 @@ msgstr "E431: Muotovirh tägitiedostossa %s" msgid "E432: Tags file not sorted: %s" msgstr "E432: Tägitiedosto ei ole järjestetty: %s" -#. never opened any tags file msgid "E433: No tags file" msgstr "E433: Ei tägitiedostoja" diff --git a/src/nvim/po/fr.po b/src/nvim/po/fr.po index c0df5f2170..4fa4ef8f8f 100644 --- a/src/nvim/po/fr.po +++ b/src/nvim/po/fr.po @@ -4,20 +4,17 @@ # Do ":help uganda" in Vim to read copying and usage conditions. # Do ":help credits" in Vim to see a list of people who contributed. # -# FIRST AUTHOR DindinX <David.Odin@bigfoot.com> 2000. -# SECOND AUTHOR Adrien Beau <version.francaise@free.fr> 2002, 2003. -# THIRD AUTHOR David Blanchet <david.blanchet@free.fr> 2006, 2008. -# FOURTH AUTHOR Dominique Pell <dominique.pelle@gmail.com> 2008, 2017. -# -# Latest translation available at: -# http://dominique.pelle.free.fr/vim-fr.php +# FIRST AUTHOR DindinX <David.Odin@bigfoot.com> 2000. +# SECOND AUTHOR Adrien Beau <version.francaise@free.fr> 2002, 2003. +# THIRD AUTHOR David Blanchet <david.blanchet@free.fr> 2006, 2008. +# FOURTH AUTHOR Dominique Pell <dominique.pelle@gmail.com> 2008, 2017. # msgid "" msgstr "" "Project-Id-Version: Vim(Franais)\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-01-16 00:30+0100\n" -"PO-Revision-Date: 2017-01-16 00:51+0100\n" +"POT-Creation-Date: 2017-10-04 23:32+0200\n" +"PO-Revision-Date: 2017-10-04 23:44+0200\n" "Last-Translator: Dominique Pell <dominique.pelle@gmail.com>\n" "Language-Team: \n" "Language: fr\n" @@ -104,7 +101,6 @@ msgstr "E90: Impossible de dcharger le dernier tampon" msgid "E84: No modified buffer found" msgstr "E84: Aucun tampon n'est modifi" -#. back where we started, didn't find anything. msgid "E85: There is no listed buffer" msgstr "E85: Aucun tampon n'est list" @@ -121,6 +117,18 @@ msgid "E89: No write since last change for buffer %ld (add ! to override)" msgstr "" "E89: Le tampon %ld n'a pas t enregistr (ajoutez ! pour passer outre)" +msgid "E948: Job still running (add ! to end the job)" +msgstr "E948: Tche en cours d'excution (ajouter ! pour terminer la tche)" + +msgid "E37: No write since last change (add ! to override)" +msgstr "E37: Modifications non enregistres (ajoutez ! pour passer outre)" + +msgid "E948: Job still running" +msgstr "E948: Tche en cours d'excution" + +msgid "E37: No write since last change" +msgstr "E37: Modifications non enregistres" + msgid "W14: Warning: List of file names overflow" msgstr "W14: Alerte : La liste des noms de fichier dborde" @@ -187,7 +195,6 @@ msgstr "ligne %ld sur %ld --%d%%-- col " msgid "[No Name]" msgstr "[Aucun nom]" -#. must be a help buffer msgid "help" msgstr "aide" @@ -218,6 +225,9 @@ msgstr "" "\n" "# Liste des tampons :\n" +msgid "E382: Cannot write, 'buftype' option is set" +msgstr "E382: criture impossible, l'option 'buftype' est active" + msgid "[Scratch]" msgstr "[Brouillon]" @@ -396,7 +406,6 @@ msgid " Keyword completion (^N^P)" msgstr " Compltement de mot-cl (^N^P)" # DB - todo : Faut-il une majuscule "mode" ? -#. ctrl_x_mode == 0, ^P/^N compl. msgid " ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)" msgstr " mode ^X (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)" @@ -481,6 +490,9 @@ msgstr "Examen : %s" msgid "Scanning tags." msgstr "Examen des marqueurs." +msgid "match in file" +msgstr "correspondance dans le fichier" + # AB - Cette chane de caractres est ajoute en dbut de ligne lorsqu'une # opration de compltion est rpte (typiquement avec CTRL-X CTRL-N). # Que ce soit en anglais ou en franais, il y a un problme de majuscules. @@ -488,10 +500,6 @@ msgstr "Examen des marqueurs." msgid " Adding" msgstr " Ajout" -#. showmode might reset the internal line pointers, so it must -#. * be called before line = ml_get(), or when this address is no -#. * longer needed. -- Acevedo. -#. msgid "-- Searching..." msgstr "-- Recherche en cours..." @@ -522,7 +530,6 @@ msgstr "Correspondance %d sur %d" msgid "match %d" msgstr "Correspondance %d" -#. maximum nesting of lists and dicts msgid "E18: Unexpected characters in :let" msgstr "E18: Caractres inattendus avant '='" @@ -584,6 +591,10 @@ msgstr "E690: \"in\" manquant aprs :for" msgid "E108: No such variable: \"%s\"" msgstr "E108: Variable inexistante : %s" +#, c-format +msgid "E940: Cannot lock or unlock variable %s" +msgstr "E940: Impossible de (d)verrouiler la variable %s" + msgid "E743: variable nested too deep for (un)lock" msgstr "E743: variable trop imbrique pour la (d)verrouiller" @@ -765,21 +776,10 @@ msgstr "E785: complete() n'est utilisable que dans le mode Insertion" # AB - Texte par dfaut du bouton de la bote de dialogue affiche par la # fonction confirm(). -#. -#. * Yes this is ugly, I don't particularly like it either. But doing it -#. * this way has the compelling advantage that translations need not to -#. * be touched at all. See below what 'ok' and 'ync' are used for. -#. msgid "&Ok" msgstr "&Ok" #, c-format -msgid "+-%s%3ld line: " -msgid_plural "+-%s%3ld lines: " -msgstr[0] "+-%s%3ld ligne : " -msgstr[1] "+-%s%3ld lignes : " - -#, c-format msgid "E700: Unknown function: %s" msgstr "E700: Fonction inconnue : %s" @@ -827,9 +827,7 @@ msgstr "E727: Dbut au-del de la fin" msgid "<empty>" msgstr "<vide>" -# AB - mon avis, la version anglaise est errone. -# DB : Vrifier -msgid "E240: No connection to Vim server" +msgid "E240: No connection to the X server" msgstr "E240: Pas de connexion au serveur X" # AB - La version franaise est meilleure que la version anglaise. @@ -840,6 +838,12 @@ msgstr "E241: L'envoi au serveur %s a chou" msgid "E277: Unable to read a server reply" msgstr "E277: Impossible de lire la rponse du serveur" +msgid "E941: already started a server" +msgstr "E941: serveur dj dmarr" + +msgid "E942: +clientserver feature not available" +msgstr "E942: La fonctionnalit +clientserver n'est pas disponible" + msgid "remove() argument" msgstr "argument de remove()" @@ -965,7 +969,6 @@ msgstr " CHEC" # ses droits d'accs. # AB - Le mot "viminfo" a t retir pour que le message ne dpasse pas 80 # caractres dans le cas courant o %s = /home/12345678/.viminfo -#. avoid a wait_return for this message, it's annoying #, c-format msgid "E137: Viminfo file is not writable: %s" msgstr "E137: L'criture dans le fichier %s est interdite" @@ -990,7 +993,6 @@ msgstr "criture du fichier viminfo \"%s\"" msgid "E886: Can't rename viminfo file to %s!" msgstr "E886: Impossible de renommer viminfo en %s" -#. Write the info: #, c-format msgid "# This viminfo file was generated by Vim %s.\n" msgstr "# Ce fichier viminfo a t gnr par Vim %s.\n" @@ -1137,8 +1139,8 @@ msgstr " sur %ld lignes" # AB - Il faut respecter l'esprit plus que la lettre. # AB - Ce message devrait contenir une rfrence :vglobal. -msgid "E147: Cannot do :global recursive" -msgstr "E147: :global ne peut pas excuter :global" +msgid "E147: Cannot do :global recursive with a range" +msgstr "E147: :global ne peut pas excuter :global avec une plage" # AB - Ce message devrait contenir une rfrence :vglobal. msgid "E148: Regular expression missing from global" @@ -1317,10 +1319,9 @@ msgstr "E750: Utilisez d'abord \":profile start {nomfichier}\"" msgid "Save changes to \"%s\"?" msgstr "Enregistrer \"%s\" ?" -# AB - Si les parenthses posent problme, il faudra remettre les guillemets -# ci-dessus. -msgid "Untitled" -msgstr "(sans titre)" +#, c-format +msgid "E947: Job still running in buffer \"%s\"" +msgstr "E947: Tche en cours d'excution dans le buffer \"%s\"" # AB - Il faut respecter l'esprit plus que la lettre. # AB - Ce message est similaire au message E89. @@ -1357,6 +1358,14 @@ msgstr "Recherche de \"%s\"" msgid "not found in '%s': \"%s\"" msgstr "introuvable dans '%s' : \"%s\"" +#, c-format +msgid "W20: Required python version 2.x not supported, ignoring file: %s" +msgstr "W20: Python version 2.x non support, fichier %s ignor" + +#, c-format +msgid "W21: Required python version 3.x not supported, ignoring file: %s" +msgstr "W21: Python 3.x non support, fichier %s ignor" + msgid "Source Vim script" msgstr "Sourcer un script - Vim" @@ -1457,6 +1466,10 @@ msgstr "La plage spcifie est inverse, OK pour l'inverser" msgid "E494: Use w or w>>" msgstr "E494: Utilisez w ou w>>" +msgid "E943: Command table needs to be updated, run 'make cmdidxs'" +msgstr "" +"E943: La table des commandes doit tre mise jour, lancez 'make cmdidxs'" + msgid "E319: Sorry, the command is not available in this version" msgstr "E319: Dsol, cette commande n'est pas disponible dans cette version" @@ -1622,7 +1635,6 @@ msgstr "E189: \"%s\" existe (ajoutez ! pour passer outre)" msgid "E190: Cannot open \"%s\" for writing" msgstr "E190: Impossible d'ouvrir \"%s\" pour y crire" -#. set mark msgid "E191: Argument must be a letter or forward/backward quote" msgstr "E191: L'argument doit tre une lettre ou une (contre-)apostrophe" @@ -1660,13 +1672,17 @@ msgstr "E500: valu en une chane vide" msgid "E195: Cannot open viminfo file for reading" msgstr "E195: Impossible d'ouvrir le viminfo en lecture" +# AB - Si les parenthses posent problme, il faudra remettre les guillemets +# ci-dessus. +msgid "Untitled" +msgstr "(sans titre)" + msgid "E196: No digraphs in this version" msgstr "E196: Pas de digraphes dans cette version" msgid "E608: Cannot :throw exceptions with 'Vim' prefix" msgstr "E608: Impossible d'mettre des exceptions avec 'Vim' comme prfixe" -#. always scroll up, don't overwrite #, c-format msgid "Exception thrown: %s" msgstr "Exception mise : %s" @@ -1683,7 +1699,6 @@ msgstr "Exception limine : %s" msgid "%s, line %ld" msgstr "%s, ligne %ld" -#. always scroll up, don't overwrite #, c-format msgid "Exception caught: %s" msgstr "Exception intercepte : %s" @@ -1710,7 +1725,6 @@ msgstr "Erreur et interruption" msgid "Error" msgstr "Erreur" -#. if (pending & CSTP_INTERRUPT) msgid "Interrupt" msgstr "Interruption" @@ -1753,15 +1767,12 @@ msgstr "E601: Imbrication de :try trop importante" msgid "E603: :catch without :try" msgstr "E603: :catch sans :try" -#. Give up for a ":catch" after ":finally" and ignore it. -#. * Just parse. msgid "E604: :catch after :finally" msgstr "E604: :catch aprs :finally" msgid "E606: :finally without :try" msgstr "E606: :finally sans :try" -#. Give up for a multiple ":finally" and ignore it. msgid "E607: multiple :finally" msgstr "E607: Il ne peut y avoir qu'un seul :finally" @@ -1862,7 +1873,6 @@ msgstr "Vim : Lecture de stdin...\n" msgid "Reading from stdin..." msgstr "Lecture de stdin..." -#. Re-opening the original file failed! msgid "E202: Conversion made file unreadable!" msgstr "E202: La conversion a rendu le fichier illisible !" @@ -2077,9 +2087,6 @@ msgstr "[noeol]" msgid "[Incomplete last line]" msgstr "[Dernire ligne incomplte]" -#. don't overwrite messages here -#. must give this prompt -#. don't use emsg() here, don't want to flush the buffers msgid "WARNING: The file has been changed since reading it!!!" msgstr "ALERTE : Le fichier a t modifi depuis que Vim l'a lu !" @@ -2161,7 +2168,6 @@ msgstr "--Effac--" msgid "auto-removing autocommand: %s <buffer=%d>" msgstr "Autocommandes marques pour auto-suppression : %s <tampon=%d>" -#. the group doesn't exist #, c-format msgid "E367: No such group: \"%s\"" msgstr "E367: Aucun groupe \"%s\"" @@ -2184,7 +2190,6 @@ msgstr "E216: Aucun vnement %s" msgid "E216: No such group or event: %s" msgstr "E216: Aucun vnement ou groupe %s" -#. Highlight title msgid "" "\n" "--- Auto-Commands ---" @@ -2233,12 +2238,6 @@ msgstr "E350: Impossible de crer un repli avec la 'foldmethod'e actuelle" msgid "E351: Cannot delete fold with current 'foldmethod'" msgstr "E351: Impossible de supprimer un repli avec la 'foldmethod'e actuelle" -#, c-format -msgid "+--%3ld line folded " -msgid_plural "+--%3ld lines folded " -msgstr[0] "+--%3ld ligne replie " -msgstr[1] "+--%3ld lignes replies " - msgid "E222: Add to read buffer" msgstr "E222: Ajout au tampon de lecture" @@ -2377,18 +2376,15 @@ msgstr "Rechercher :" msgid "Replace with:" msgstr "Remplacer par :" -#. whole word only button msgid "Match whole word only" msgstr "Mots entiers seulement" -#. match case button msgid "Match case" msgstr "Respecter la casse" msgid "Direction" msgstr "Direction" -#. 'Up' and 'Down' buttons msgid "Up" msgstr "Haut" @@ -2472,8 +2468,6 @@ msgstr "Chercher et remplacer (utilisez '\\\\' pour trouver un '\\')" # DB - Traduction non indispensable puisque le code indique qu'il s'agit d'un # paramtrage bidon afin de slectionner un rpertoire plutt qu'un # fichier. -#. We fake this: Use a filter that doesn't select anything and a default -#. * file name that won't be used. msgid "Not Used" msgstr "Non utilis" @@ -2554,7 +2548,6 @@ msgstr "Choisir une police - Vim" msgid "Name:" msgstr "Nom :" -#. create toggle button msgid "Show size in Points" msgstr "Afficher la taille en Points" @@ -2805,7 +2798,6 @@ msgstr "E261: Connexion cscope %s introuvable" msgid "cscope connection %s closed" msgstr "connexion cscope %s ferme" -#. should not reach here msgid "E570: fatal error in cs_manage_matches" msgstr "E570: erreur fatale dans cs_manage_matches" @@ -2970,7 +2962,6 @@ msgid "not implemented yet" msgstr "pas encore implment" # DB - TODO : le contexte est celui d'une annulation. -#. ??? msgid "cannot set line(s)" msgstr "Impossible de remettre la/les ligne(s)" @@ -3011,7 +3002,6 @@ msgid "" msgstr "" "Impossible d'inscrire la commande de rappel : tampon/fentre en effacement" -#. This should never happen. Famous last word? msgid "" "E280: TCL FATAL ERROR: reflist corrupt!? Please report this to vim-dev@vim." "org" @@ -3114,7 +3104,6 @@ msgstr "Vim : Alerte : La sortie ne s'effectue pas sur un terminal\n" msgid "Vim: Warning: Input is not from a terminal\n" msgstr "Vim : Alerte : L'entre ne se fait pas sur un terminal\n" -#. just in case.. msgid "pre-vimrc command line" msgstr "ligne de commande pre-vimrc" @@ -3281,8 +3270,7 @@ msgstr "" "--no-a-term\t\tAucun avertissement si l'entre/sortie n'est pas un terminal" msgid "--ttyfail\t\tExit if input or output is not a terminal" -msgstr "" -"--ttyfail\t\tQuitte si l'entre ou la sortie ne sont pas un terminal" +msgstr "--ttyfail\t\tQuitte si l'entre ou la sortie ne sont pas un terminal" msgid "-u <vimrc>\t\tUse <vimrc> instead of any .vimrc" msgstr "-u <vimrc>\tUtiliser <vimrc> au lieu du vimrc habituel" @@ -3382,6 +3370,9 @@ msgstr "" msgid "-i <viminfo>\t\tUse <viminfo> instead of .viminfo" msgstr "-i <viminfo>\t\tUtiliser <viminfo> au lieu du viminfo habituel" +msgid "--clean\t\t'nocompatible', Vim defaults, no plugins, no viminfo" +msgstr "--clean\t\t'nocompatible', rglages par dfaut, aucun greffon ni viminfo" + msgid "-h or --help\tPrint Help (this message) and exit" msgstr "-h ou --help\t\tAfficher l'aide (ce message) puis quitter" @@ -3484,11 +3475,9 @@ msgstr "--windowid <HWND>\tOuvrir Vim dans un autre widget win32" msgid "No display" msgstr "Aucun display" -#. Failed to send, abort. msgid ": Send failed.\n" msgstr " : L'envoi a chou.\n" -#. Let vim start normally. msgid ": Send failed. Trying to execute locally\n" msgstr " : L'envoi a chou. Tentative d'excution locale\n" @@ -3509,7 +3498,6 @@ msgstr "Aucune marque positionne" msgid "E283: No marks matching \"%s\"" msgstr "E283: Aucune marque ne correspond \"%s\"" -#. Highlight title msgid "" "\n" "mark line col file/text" @@ -3517,7 +3505,6 @@ msgstr "" "\n" "marq ligne col fichier/texte" -#. Highlight title msgid "" "\n" " jump line col file/text" @@ -3525,7 +3512,6 @@ msgstr "" "\n" " saut ligne col fichier/texte" -#. Highlight title msgid "" "\n" "change line col text" @@ -3540,7 +3526,6 @@ msgstr "" "\n" "# Marques dans le fichier :\n" -#. Write the jumplist with -' msgid "" "\n" "# Jumplist (newest first):\n" @@ -3612,7 +3597,6 @@ msgstr "E298: Bloc n2 non rcupr ?" msgid "E843: Error while updating swap file crypt" msgstr "E843: Erreur lors de la mise jour du fichier d'change crypt" -#. could not (re)open the swap file, what can we do???? msgid "E301: Oops, lost the swap file!!!" msgstr "E301: Oups, le fichier d'change a disparu !" @@ -3801,7 +3785,6 @@ msgstr "" "Utilisation de la cl de chiffrement du fichier d'change pour le fichier " "texte.\n" -#. use msg() to start the scrolling properly msgid "Swap files found:" msgstr "Fichiers d'change trouvs :" @@ -3971,8 +3954,6 @@ msgstr "Lors de l'ouverture du fichier \"" msgid " NEWER than swap file!\n" msgstr " PLUS RCENT que le fichier d'change !\n" -#. Some of these messages are long to allow translation to -#. * other languages. msgid "" "\n" "(1) Another program may be editing the same file. If this is the case,\n" @@ -4063,7 +4044,6 @@ msgstr "E328: Le menu n'existe que dans un autre mode" msgid "E329: No menu \"%s\"" msgstr "E329: Aucun menu \"%s\"" -#. Only a mnemonic or accelerator is not valid. msgid "E792: Empty menu name" msgstr "E792: Nom de menu vide" @@ -4076,8 +4056,6 @@ msgstr "E331: Ajout d'lments de menu directement dans barre de menu interdit" msgid "E332: Separator cannot be part of a menu path" msgstr "E332: Un sparateur ne peut faire partie d'un chemin de menu" -#. Now we have found the matching menu, and we list the mappings -#. Highlight title msgid "" "\n" "--- Menus ---" @@ -4088,6 +4066,10 @@ msgstr "" msgid "Tear off this menu" msgstr "Dtacher ce menu" +#, c-format +msgid "E335: Menu not defined for %s mode" +msgstr "E335: Le menu n'est pas dfini pour le mode %s" + msgid "E333: Menu path must lead to a menu item" msgstr "E333: Le chemin du menu doit conduire un lment de menu" @@ -4095,10 +4077,6 @@ msgstr "E333: Le chemin du menu doit conduire un lment de menu" msgid "E334: Menu not found: %s" msgstr "E334: Menu introuvable : %s" -#, c-format -msgid "E335: Menu not defined for %s mode" -msgstr "E335: Le menu n'est pas dfini pour le mode %s" - msgid "E336: Menu path must lead to a sub-menu" msgstr "E336: Le chemin du menu doit conduire un sous-menu" @@ -4172,7 +4150,6 @@ msgstr "Enregistrer un fichier" msgid "Open File dialog" msgstr "Ouvrir un fichier" -#. TODO: non-GUI file selector here msgid "E338: Sorry, no file browser in console mode" msgstr "E338: Dsol, pas de slecteur de fichiers en mode console" @@ -4338,8 +4315,10 @@ msgstr "E662: Au dbut de la liste des modifications" msgid "E663: At end of changelist" msgstr "E663: la fin de la liste des modifications" -msgid "Type :quit<Enter> to exit Vim" -msgstr "tapez :q<Entre> pour quitter Vim" +msgid "Type :qa! and press <Enter> to abandon all changes and exit Vim" +msgstr "" +"Tapez :qa! puis <Entre> pour abandonner tous les changements et quitter " +"Vim" #, c-format msgid "1 line %sed 1 time" @@ -4372,7 +4351,6 @@ msgid "E748: No previously used register" msgstr "E748: Aucun registre n'a t prcdemment utilis" # DB - Question O/N. -#. must display the prompt msgid "cannot yank; delete anyway" msgstr "impossible de raliser une copie ; effacer tout de mme" @@ -4387,25 +4365,30 @@ msgstr "%ld lignes modifies" msgid "freeing %ld lines" msgstr "libration de %ld lignes" -msgid "block of 1 line yanked" -msgstr "bloc de 1 ligne copi" +#, c-format +msgid " into \"%c" +msgstr " dans \"%c" -msgid "1 line yanked" -msgstr "1 ligne copie" +#, c-format +msgid "block of 1 line yanked%s" +msgstr "bloc de 1 ligne copi%s" + +#, c-format +msgid "1 line yanked%s" +msgstr "1 ligne copie%s" #, c-format -msgid "block of %ld lines yanked" -msgstr "bloc de %ld lignes copi" +msgid "block of %ld lines yanked%s" +msgstr "bloc de %ld lignes copi%s" #, c-format -msgid "%ld lines yanked" -msgstr "%ld lignes copies" +msgid "%ld lines yanked%s" +msgstr "%ld lignes copies%s" #, c-format msgid "E353: Nothing in register %s" msgstr "E353: Le registre %s est vide" -#. Highlight title msgid "" "\n" "--- Registers ---" @@ -4469,9 +4452,6 @@ msgstr "" msgid "(+%ld for BOM)" msgstr "(+%ld pour le BOM)" -msgid "%<%f%h%m%=Page %N" -msgstr "%<%f%h%m%=Page %N" - msgid "Thanks for flying Vim" msgstr "Merci d'avoir choisi Vim" @@ -4582,6 +4562,9 @@ msgstr "E541: trop d'lments" msgid "E542: unbalanced groups" msgstr "E542: parenthses non quilibres" +msgid "E946: Cannot make a terminal with running job modifiable" +msgstr "E946: terminal avec tche en cours d'excution ne peut pas tre modifiable" + msgid "E590: A preview window already exists" msgstr "E590: Il existe dj une fentre de prvisualisation" @@ -4600,9 +4583,6 @@ msgstr "E594: Au moins %d colonnes sont ncessaires" msgid "E355: Unknown option: %s" msgstr "E355: Option inconnue : %s" -#. There's another character after zeros or the string -#. * is empty. In both cases, we are trying to set a -#. * num option using a string. #, c-format msgid "E521: Number required: &%s = '%s'" msgstr "E521: Nombre requis : &%s = '%s'" @@ -4675,7 +4655,6 @@ msgstr "Impossible de modifier le mode de la console ?!\n" msgid "mch_get_shellsize: not a console??\n" msgstr "mch_get_shellsize : pas une console ?!\n" -#. if Vim opened a window: Executing a shell may cause crashes msgid "E360: Cannot execute shell with -f option" msgstr "E360: Impossible d'excuter un shell avec l'option -f" @@ -4902,7 +4881,6 @@ msgstr "E376: %%%c invalide dans le prfixe de la chane de format" msgid "E377: Invalid %%%c in format string" msgstr "E377: %%%c invalide dans la chane de format" -#. nothing found msgid "E378: 'errorformat' contains no pattern" msgstr "E378: 'errorformat' ne contient aucun motif" @@ -4941,9 +4919,6 @@ msgstr "E381: Au sommet de la pile quickfix" msgid "No entries" msgstr "Aucune entre" -msgid "E382: Cannot write, 'buftype' option is set" -msgstr "E382: criture impossible, l'option 'buftype' est active" - msgid "Error file" msgstr "Fichier d'erreurs" @@ -4968,6 +4943,12 @@ msgstr "E369: lment invalide dans %s%%[]" msgid "E769: Missing ] after %s[" msgstr "E769: ']' manquant aprs %s[" +msgid "E944: Reverse range in character class" +msgstr "E944: Classe de caractres inverse" + +msgid "E945: Range too large in character class" +msgstr "E945: Plage de classe de caractres trop large" + #, c-format msgid "E53: Unmatched %s%%(" msgstr "E53: Pas de correspondance pour %s%%(" @@ -4994,6 +4975,9 @@ msgstr "E69: ']' manquant aprs %s%%[" msgid "E70: Empty %s%%[]" msgstr "E70: %s%%[] vide" +msgid "E65: Illegal back reference" +msgstr "E65: post-rfrence invalide" + msgid "E339: Pattern too long" msgstr "E339: Motif trop long" @@ -5030,9 +5014,6 @@ msgstr "E63: utilisation invalide de \\_" msgid "E64: %s%c follows nothing" msgstr "E64: %s%c ne suit aucun atome" -msgid "E65: Illegal back reference" -msgstr "E65: post-rfrence invalide" - msgid "E68: Invalid character after \\z" msgstr "E68: Caractre invalide aprs \\z" @@ -5084,7 +5065,6 @@ msgstr "E867: (NFA) Oprateur inconnu '\\z%c'" msgid "E867: (NFA) Unknown operator '\\%%%c'" msgstr "E867: (NFA) Oprateur inconnu '\\%%%c'" -#. should never happen msgid "E868: Error building NFA with equivalence class!" msgstr "E868: Erreur lors de la construction du NFA avec classe d'quivalence" @@ -5095,11 +5075,9 @@ msgstr "E869: (NFA) Oprateur inconnu '\\@%c'" msgid "E870: (NFA regexp) Error reading repetition limits" msgstr "E870: (regexp NFA) Erreur la lecture des limites de rptition" -#. Can't have a multi follow a multi. msgid "E871: (NFA regexp) Can't have a multi follow a multi !" msgstr "E871: (regexp NFA) Un multi ne peut pas suivre un multi !" -#. Too many `(' msgid "E872: (NFA regexp) Too many '('" msgstr "E872: (regexp NFA) Trop de '('" @@ -5209,7 +5187,6 @@ msgstr "E386: '?' ou '/' attendu aprs ';'" msgid " (includes previously listed match)" msgstr " (inclut des correspondances listes prcdemment)" -#. cursor at status line msgid "--- Included files " msgstr "--- Fichiers inclus " @@ -5286,8 +5263,6 @@ msgstr "Dsol, aucune suggestion" msgid "Sorry, only %ld suggestions" msgstr "Dsol, seulement %ld suggestions" -#. for when 'cmdheight' > 1 -#. avoid more prompt #, c-format msgid "Change \"%.*s\" to:" msgstr "Remplacer \"%.*s\" par :" @@ -5574,10 +5549,6 @@ msgstr "%d noeuds compresss sur %d ; %d (%d%%) restants " msgid "Reading back spell file..." msgstr "Relecture du fichier orthographique" -#. -#. * Go through the trie of good words, soundfold each word and add it to -#. * the soundfold trie. -#. msgid "Performing soundfolding..." msgstr "Analyse phontique en cours..." @@ -5634,18 +5605,39 @@ msgid "E763: Word characters differ between spell files" msgstr "" "E763: Les caractres de mots diffrent entre les fichiers orthographiques" -#. This should have been checked when generating the .spl -#. * file. msgid "E783: duplicate char in MAP entry" msgstr "E783: caractre dupliqu dans l'entre MAP" msgid "No Syntax items defined for this buffer" msgstr "Aucun lment de syntaxe dfini pour ce tampon" +msgid "syntax conceal on" +msgstr "\"syntax conceal\" active" + +msgid "syntax conceal off" +msgstr "\"syntax conceal\" dsactive" + #, c-format msgid "E390: Illegal argument: %s" msgstr "E390: Argument invalide : %s" +msgid "syntax case ignore" +msgstr "syntaxe ignore la casse" + +msgid "syntax case match" +msgstr "syntaxe respecte la casse" + +msgid "syntax spell toplevel" +msgstr "contrle orthographique dans le texte sans groupe syntaxique" + +msgid "syntax spell notoplevel" +msgstr "pas de contrle orthographique dans le texte sans groupe syntaxique" + +msgid "syntax spell default" +msgstr "" +"contrle orthographique dans le texte sans groupe syntaxique, sauf si @Spell/" +"@NoSpell" + msgid "syntax iskeyword " msgstr "syntaxe iskeyword " @@ -5885,7 +5877,6 @@ msgstr "E428: Impossible d'aller au-del du dernier marqueur correspondant" msgid "File \"%s\" does not exist" msgstr "Le fichier \"%s\" n'existe pas" -#. Give an indication of the number of matching tags #, c-format msgid "tag %d of %d%s" msgstr "marqueur %d sur %d%s" @@ -5900,7 +5891,6 @@ msgstr " Utilisation d'un marqueur avec une casse diffrente !" msgid "E429: File \"%s\" does not exist" msgstr "E429: Le fichier \"%s\" n'existe pas" -#. Highlight title msgid "" "\n" " # TO tag FROM line in file/text" @@ -5931,7 +5921,6 @@ msgstr "Avant l'octet %ld" msgid "E432: Tags file not sorted: %s" msgstr "E432: Le fichier de marqueurs %s n'est pas ordonn" -#. never opened any tags file msgid "E433: No tags file" msgstr "E433: Aucun fichier de marqueurs" @@ -5968,7 +5957,6 @@ msgstr "E436: Aucune entre \"%s\" dans termcap" msgid "E437: terminal capability \"cm\" required" msgstr "E437: capacit de terminal \"cm\" requise" -#. Highlight title msgid "" "\n" "--- Terminal keys ---" @@ -5979,6 +5967,21 @@ msgstr "" msgid "Cannot open $VIMRUNTIME/rgb.txt" msgstr "Impossible d'ouvrir $VIMRUNTIME/rgb.txt" +msgid "Terminal" +msgstr "Terminal" + +msgid "Terminal-finished" +msgstr "Terminal-fini" + +msgid "active" +msgstr "actif" + +msgid "running" +msgstr "en cours" + +msgid "finished" +msgstr "fini" + msgid "new shell started\n" msgstr "nouveau shell dmarr\n" @@ -5989,13 +5992,10 @@ msgstr "Vim : Erreur lors de la lecture de l'entre, sortie...\n" msgid "Used CUT_BUFFER0 instead of empty selection" msgstr "CUT_BUFFER0 utilis plutt qu'une slection vide" -#. This happens when the FileChangedRO autocommand changes the -#. * file in a way it becomes shorter. msgid "E881: Line count changed unexpectedly" msgstr "E881: Le nombre de lignes a t chang inopinment" # DB - Question O/N. -#. must display the prompt msgid "No undo possible; continue anyway" msgstr "Annulation impossible ; continuer" @@ -6249,6 +6249,10 @@ msgid "E126: Missing :endfunction" msgstr "E126: Il manque :endfunction" #, c-format +msgid "W22: Text found after :endfunction: %s" +msgstr "W22: Texte trouv aprs :endfunction: %s" + +#, c-format msgid "E707: Function name conflicts with variable: %s" msgstr "E707: Le nom de fonction entre en conflit avec la variable : %s" @@ -6640,7 +6644,6 @@ msgstr "&Comparer avec Vim" msgid "Edit with &Vim" msgstr "diter dans &Vim" -#. Now concatenate msgid "Edit with existing Vim - " msgstr "diter dans le Vim existant - " @@ -6664,10 +6667,6 @@ msgstr "Le chemin est trop long !" msgid "--No lines in buffer--" msgstr "--Le tampon est vide--" -#. -#. * The error messages that can be shared are included here. -#. * Excluded are errors that are only used once and debugging messages. -#. msgid "E470: Command aborted" msgstr "E470: Commande annule" @@ -6854,12 +6853,6 @@ msgstr "E484: Impossible d'ouvrir le fichier \"%s\"" msgid "E485: Can't read file %s" msgstr "E485: Impossible de lire le fichier %s" -msgid "E37: No write since last change (add ! to override)" -msgstr "E37: Modifications non enregistres (ajoutez ! pour passer outre)" - -msgid "E37: No write since last change" -msgstr "E37: Modifications non enregistres" - msgid "E38: Null argument" msgstr "E38: Argument null" @@ -6997,8 +6990,8 @@ msgstr "E592: 'winwidth' ne peut pas tre plus petit que 'winminwidth'" msgid "E80: Error while writing" msgstr "E80: Erreur lors de l'criture" -msgid "Zero count" -msgstr "Le quantificateur est nul" +msgid "E939: Positive count required" +msgstr "E939: Quantificateur positif requis" msgid "E81: Using <SID> not in a script context" msgstr "E81: <SID> utilis en dehors d'un script" @@ -7151,7 +7144,6 @@ msgstr "le constructeur de liste n'accepte pas les arguments nomms" msgid "list index out of range" msgstr "index de liste hors limites" -#. No more suitable format specifications in python-2.3 #, c-format msgid "internal error: failed to get vim list item %d" msgstr "erreur interne : accs un lment %d de liste a chou" diff --git a/src/nvim/po/ga.po b/src/nvim/po/ga.po index abb3565077..d409560f6b 100644 --- a/src/nvim/po/ga.po +++ b/src/nvim/po/ga.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: vim 7.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-10-25 09:31-0500\n" +"POT-Creation-Date: 2017-07-11 15:45-0500\n" "PO-Revision-Date: 2010-04-14 10:01-0500\n" "Last-Translator: Kevin Patrick Scannell <kscanne@gmail.com>\n" "Language-Team: Irish <gaeilge-gnulinux@lists.sourceforge.net>\n" @@ -239,7 +239,8 @@ msgid "E917: Cannot use a callback with %s()" msgstr "E917: N fidir aisghlaoch a sid le %s()" msgid "E912: cannot use ch_evalexpr()/ch_sendexpr() with a raw or nl channel" -msgstr "E912: n fidir ch_evalexpr()/ch_sendexpr() a sid le cainal raw n nl" +msgstr "" +"E912: n fidir ch_evalexpr()/ch_sendexpr() a sid le cainal raw n nl" msgid "E906: not an open channel" msgstr "E906: n cainal oscailte " @@ -426,6 +427,9 @@ msgstr "%s scanadh" msgid "Scanning tags." msgstr "Clibeanna scanadh." +msgid "match in file" +msgstr "meaitseil sa chomhad" + msgid " Adding" msgstr " Mad" @@ -513,6 +517,12 @@ msgstr "E690: \"in\" ar iarraidh i ndiaidh :for" msgid "E108: No such variable: \"%s\"" msgstr "E108: Nl a leithid d'athrg: \"%s\"" +#. For historic reasons this error is not given for a list or dict. +#. * E.g., the b: dict could be locked/unlocked. +#, c-format +msgid "E940: Cannot lock or unlock variable %s" +msgstr "E940: N fidir athrg %s a ghlasil n a dhghlasil" + msgid "E743: variable nested too deep for (un)lock" msgstr "E743: athrg neadaithe rdhomhain chun a (d)ghlasil" @@ -691,15 +701,6 @@ msgid "&Ok" msgstr "&Ok" #, c-format -msgid "+-%s%3ld line: " -msgid_plural "+-%s%3ld lines: " -msgstr[0] "+-%s%3ld lne: " -msgstr[1] "+-%s%3ld lne: " -msgstr[2] "+-%s%3ld lne: " -msgstr[3] "+-%s%3ld lne: " -msgstr[4] "+-%s%3ld lne: " - -#, c-format msgid "E700: Unknown function: %s" msgstr "E700: Feidhm anaithnid: %s" @@ -707,7 +708,9 @@ msgid "E922: expected a dict" msgstr "E922: bhothas ag sil le foclir" msgid "E923: Second argument of function() must be a list or a dict" -msgstr "E923: Caithfidh an dara hargint de function() a bheith ina liosta n ina foclir" +msgstr "" +"E923: Caithfidh an dara hargint de function() a bheith ina liosta n ina " +"foclir" msgid "" "&OK\n" @@ -744,8 +747,8 @@ msgstr "E727: Tosach thar dheireadh" msgid "<empty>" msgstr "<folamh>" -msgid "E240: No connection to Vim server" -msgstr "E240: Nl aon nasc le freastala Vim" +msgid "E240: No connection to the X server" +msgstr "E240: Nl aon cheangal leis an bhfreastala X" #, c-format msgid "E241: Unable to send to %s" @@ -754,6 +757,12 @@ msgstr "E241: N fidir aon rud a sheoladh chuig %s" msgid "E277: Unable to read a server reply" msgstr "E277: N fidir freagra n fhreastala a lamh" +msgid "E941: already started a server" +msgstr "E941: tosaodh freastala cheana" + +msgid "E942: +clientserver feature not available" +msgstr "E942: nl an ghn +clientserver ar fil" + msgid "remove() argument" msgstr "argint remove()" @@ -993,8 +1002,9 @@ msgstr " ar lne amhin" msgid " on %ld lines" msgstr " ar %ld lne" -msgid "E147: Cannot do :global recursive" -msgstr "E147: N cheadatear :global go hathchrsach" +#. will increment global_busy to break out of the loop +msgid "E147: Cannot do :global recursive with a range" +msgstr "E147: N cheadatear :global athchrsach le raon" # should have ":" msgid "E148: Regular expression missing from global" @@ -1179,6 +1189,14 @@ msgstr "Ag danamh cuardach ar \"%s\"" msgid "not found in '%s': \"%s\"" msgstr "gan aimsi in '%s': \"%s\"" +#, c-format +msgid "W20: Required python version 2.x not supported, ignoring file: %s" +msgstr "W20: Nl leagan 2.x de Python ar fil; ag danamh neamhaird de %s" + +#, c-format +msgid "W21: Required python version 3.x not supported, ignoring file: %s" +msgstr "W21: Nl leagan 3.x de Python ar fil; ag danamh neamhaird de %s" + msgid "Source Vim script" msgstr "Foinsigh script Vim" @@ -1278,6 +1296,9 @@ msgstr "Raon droim ar ais, babhtil" msgid "E494: Use w or w>>" msgstr "E494: Bain sid as w n w>>" +msgid "E943: Command table needs to be updated, run 'make cmdidxs'" +msgstr "E943: Caithfear tbla na n-orduithe a nuashonr; rith 'make cmdidxs'" + msgid "E319: Sorry, the command is not available in this version" msgstr "E319: T brn orm, nl an t-ord ar fil sa leagan seo" @@ -1391,7 +1412,7 @@ msgid "No swap file" msgstr "Nl aon chomhad babhtla ann" msgid "Append File" -msgstr "Cuir Comhad i nDeireadh" +msgstr "Ceangail Comhad ag an Deireadh" msgid "E747: Cannot change directory, buffer is modified (add ! to override)" msgstr "" @@ -1412,10 +1433,10 @@ msgid "Window position: X %d, Y %d" msgstr "Ionad na fuinneoige: X %d, Y %d" msgid "E188: Obtaining window position not implemented for this platform" -msgstr "E188: N fidir ionad na fuinneoige a fhil amach ar an chras seo" +msgstr "E188: N fidir ionad na fuinneoige a fhil amach ar an gcras seo" msgid "E466: :winpos requires two number arguments" -msgstr "E466: n folir dh argint uimhrila le :winpos" +msgstr "E466: dh argint uimhrila de dhth le :winpos" msgid "E930: Cannot use :redir inside execute()" msgstr "E930: N fidir :redir a sid laistigh de execute()" @@ -2045,15 +2066,6 @@ msgstr "E350: N fidir filleadh a chruth leis an 'foldmethod' reatha" msgid "E351: Cannot delete fold with current 'foldmethod'" msgstr "E351: N fidir filleadh a scriosadh leis an 'foldmethod' reatha" -#, c-format -msgid "+--%3ld line folded " -msgid_plural "+--%3ld lines folded " -msgstr[0] "+--%3ld lne fillte " -msgstr[1] "+--%3ld lne fillte " -msgstr[2] "+--%3ld lne fillte " -msgstr[3] "+--%3ld lne fillte " -msgstr[4] "+--%3ld lne fillte " - msgid "E222: Add to read buffer" msgstr "E222: Cuir leis an maoln lite" @@ -2652,8 +2664,8 @@ msgid "" "E895: Sorry, this command is disabled, the MzScheme's racket/base module " "could not be loaded." msgstr "" -"E895: r leithscal, t an t-ord seo dchumasaithe; norbh fhidir " -"modl racket/base MzScheme a lucht." +"E895: r leithscal, t an t-ord seo dchumasaithe; norbh fhidir modl " +"racket/base MzScheme a lucht." msgid "invalid expression" msgstr "slonn neamhbhail" @@ -2846,6 +2858,10 @@ msgid "E251: VIM instance registry property is badly formed. Deleted!" msgstr "E251: Air mchumtha sa chlrlann isc VIM. Scriosta!" #, c-format +msgid "E938: Duplicate key in JSON: \"%s\"" +msgstr "E938: Eochair dhblach in JSON: \"%s\"" + +#, c-format msgid "E696: Missing comma in List: %s" msgstr "E696: Camg ar iarraidh i Liosta: %s" @@ -2898,7 +2914,8 @@ msgid "Vim: Error: Failure to start gvim from NetBeans\n" msgstr "Vim: Earrid: Theip ar thos gvim NetBeans\n" msgid "Vim: Error: This version of Vim does not run in a Cygwin terminal\n" -msgstr "Vim: Earrid: N fidir an leagan seo de Vim a rith i dteirminal Cygwin\n" +msgstr "" +"Vim: Earrid: N fidir an leagan seo de Vim a rith i dteirminal Cygwin\n" msgid "Vim: Warning: Output is not to a terminal\n" msgstr "Vim: Rabhadh: Nl an t-aschur ag dul chuig teirminal\n" @@ -3067,7 +3084,12 @@ msgid "-T <terminal>\tSet terminal type to <terminal>" msgstr "-T <teirminal>\tSocraigh cinel teirminal" msgid "--not-a-term\t\tSkip warning for input/output not being a terminal" -msgstr "--not-a-term\t\tN bac le rabhadh faoi ionchur/aschur gan a bheith n teirminal" +msgstr "" +"--not-a-term\t\tN bac le rabhadh faoi ionchur/aschur gan a bheith n " +"teirminal" + +msgid "--ttyfail\t\tExit if input or output is not a terminal" +msgstr "--ttyfail\t\tScoir mura bhfuil ionchur agus aschur ina dteirminil" msgid "-u <vimrc>\t\tUse <vimrc> instead of any .vimrc" msgstr "-u <vimrc>\t\tsid <vimrc> in ionad aon .vimrc" @@ -3435,7 +3457,7 @@ msgid "" "Maybe no changes were made or Vim did not update the swap file." msgstr "" "\n" -"B'fhidir nach raibh aon athr dhanamh, n t an comhad\n" +"B'fhidir nach raibh aon athr dhanamh, n t an comhad " "babhtla as dta." msgid " cannot be used with this version of Vim.\n" @@ -4122,8 +4144,8 @@ msgstr "E662: Ag tosach liosta na n-athruithe" msgid "E663: At end of changelist" msgstr "E663: Ag deireadh liosta na n-athruithe" -msgid "Type :quit<Enter> to exit Vim" -msgstr "Clscrobh :quit<Enter> chun Vim a scor" +msgid "Type :qa! and press <Enter> to abandon all changes and exit Vim" +msgstr "Clscrobh :qa! agus brigh <Enter> le fgil Vim gan athruithe a shbhil" # ouch - English -ed ? #, c-format @@ -4234,7 +4256,8 @@ msgid "" "Selected %s%ld of %ld Lines; %lld of %lld Words; %lld of %lld Chars; %lld of " "%lld Bytes" msgstr "" -"Roghnaodh %s%ld as %ld Lne; %lld as %lld Focal; %lld as %lld Carachtar; %lld as %lld Beart" +"Roghnaodh %s%ld as %ld Lne; %lld as %lld Focal; %lld as %lld Carachtar; " +"%lld as %lld Beart" #, c-format msgid "Col %s of %s; Line %ld of %ld; Word %lld of %lld; Byte %lld of %lld" @@ -4245,7 +4268,8 @@ msgid "" "Col %s of %s; Line %ld of %ld; Word %lld of %lld; Char %lld of %lld; Byte " "%lld of %lld" msgstr "" -"Col %s as %s; Lne %ld as %ld; Focal %lld as %lld; Carachtar %lld as %lld; Beart %lld as %lld" +"Col %s as %s; Lne %ld as %ld; Focal %lld as %lld; Carachtar %lld as %lld; " +"Beart %lld as %lld" #, c-format msgid "(+%ld for BOM)" @@ -4507,8 +4531,7 @@ msgstr "" #, c-format msgid "E244: Illegal quality name \"%s\" in font name \"%s\"" -msgstr "" -"E244: Ainm neamhcheadaithe ar chilocht \"%s\" in ainm cl \"%s\"" +msgstr "E244: Ainm neamhcheadaithe ar chilocht \"%s\" in ainm cl \"%s\"" #, c-format msgid "E245: Illegal char '%c' in font name \"%s\"" @@ -4551,7 +4574,8 @@ msgstr "Norbh fhidir comhthacs slndla %s a shocr le haghaidh %s" #, c-format msgid "Could not get security context %s for %s. Removing it!" -msgstr "Norbh fhidir comhthacs slndla %s a fhil le haghaidh %s. bhaint!" +msgstr "" +"Norbh fhidir comhthacs slndla %s a fhil le haghaidh %s. bhaint!" msgid "" "\n" @@ -4752,6 +4776,12 @@ msgstr "E369: mr neamhbhail i %s%%[]" msgid "E769: Missing ] after %s[" msgstr "E769: ] ar iarraidh i ndiaidh %s[" +msgid "E944: Reverse range in character class" +msgstr "E944: Raon aisiompaithe in aicme carachtar" + +msgid "E945: Range too large in character class" +msgstr "E945: Raon rmhr in aicme carachtar" + #, c-format msgid "E53: Unmatched %s%%(" msgstr "E53: %s%%( corr" @@ -4778,6 +4808,9 @@ msgstr "E69: ] ar iarraidh i ndiaidh %s%%[" msgid "E70: Empty %s%%[]" msgstr "E70: %s%%[] folamh" +msgid "E65: Illegal back reference" +msgstr "E65: Cltagairt neamhbhail" + msgid "E339: Pattern too long" msgstr "E339: Slonn rfhada" @@ -4814,9 +4847,6 @@ msgstr "E63: sid neamhbhail de \\_" msgid "E64: %s%c follows nothing" msgstr "E64: nl aon rud roimh %s%c" -msgid "E65: Illegal back reference" -msgstr "E65: Cltagairt neamhbhail" - msgid "E68: Invalid character after \\z" msgstr "E68: Carachtar neamhbhail i ndiaidh \\z" @@ -5424,12 +5454,33 @@ msgstr "E783: carachtar dblach in iontril MAP" msgid "No Syntax items defined for this buffer" msgstr "Nl aon mhr chomhrire sainmhnithe le haghaidh an mhaolin seo" +msgid "syntax conceal on" +msgstr "syntax conceal on" + +msgid "syntax conceal off" +msgstr "syntax conceal off" + #, c-format msgid "E390: Illegal argument: %s" msgstr "E390: Argint neamhcheadaithe: %s" +msgid "syntax case ignore" +msgstr "syntax case ignore" + +msgid "syntax case match" +msgstr "syntax case match" + +msgid "syntax spell toplevel" +msgstr "syntax spell toplevel" + +msgid "syntax spell notoplevel" +msgstr "syntax spell notoplevel" + +msgid "syntax spell default" +msgstr "syntax spell default" + msgid "syntax iskeyword " -msgstr "comhrir iskeyword " +msgstr "syntax iskeyword " #, c-format msgid "E391: No such syntax cluster: %s" @@ -6006,6 +6057,10 @@ msgid "E126: Missing :endfunction" msgstr "E126: :endfunction ar iarraidh" #, c-format +msgid "W22: Text found after :endfunction: %s" +msgstr "W22: Aimsodh tacs tar is :endfunction: %s" + +#, c-format msgid "E707: Function name conflicts with variable: %s" msgstr "E707: Tagann ainm na feidhme salach ar athrg: %s" @@ -6475,6 +6530,10 @@ msgstr "E236: N cl aonleithid \"%s\"" msgid "E473: Internal error" msgstr "E473: Earrid inmhenach" +#, c-format +msgid "E685: Internal error: %s" +msgstr "E685: Earrid inmhenach: %s" + msgid "Interrupted" msgstr "Idirbhriste" @@ -6635,7 +6694,7 @@ msgid "E486: Pattern not found: %s" msgstr "E486: Patrn gan aimsi: %s" msgid "E487: Argument must be positive" -msgstr "E487: N folir argint dheimhneach" +msgstr "E487: Argint dheimhneach de dhth" msgid "E459: Cannot go back to previous directory" msgstr "E459: N fidir a fhilleadh ar an chomhadlann roimhe seo" @@ -6745,8 +6804,8 @@ msgstr "E592: n cheadatear 'winwidth' a bheith nos l n 'winminwidth'" msgid "E80: Error while writing" msgstr "E80: Earrid agus scrobh" -msgid "Zero count" -msgstr "Nialas" +msgid "E939: Positive count required" +msgstr "E939: Uimhir dheimhneach de dhth" msgid "E81: Using <SID> not in a script context" msgstr "E81: <SID> sid nach i gcomhthacs scripte" @@ -6760,10 +6819,6 @@ msgstr "E463: Rigin cosanta, n fidir a athr" msgid "E744: NetBeans does not allow changes in read-only files" msgstr "E744: N cheadaonn NetBeans aon athr i gcomhaid inlite amhin" -#, c-format -msgid "E685: Internal error: %s" -msgstr "E685: Earrid inmhenach: %s" - msgid "E363: pattern uses more memory than 'maxmempattern'" msgstr "E363: sideann an patrn nos m cuimhne n 'maxmempattern'" @@ -7062,6 +7117,28 @@ msgstr "" "Norbh fhidir an chonair a shocr: n liosta sys.path\n" "Ba chir duit vim.VIM_SPECIAL_PATH a cheangal le deireadh sys.path" +#~ msgid "+-%s%3ld line: " +#~ msgid_plural "+-%s%3ld lines: " +#~ msgstr[0] "+-%s%3ld lne: " +#~ msgstr[1] "+-%s%3ld lne: " +#~ msgstr[2] "+-%s%3ld lne: " +#~ msgstr[3] "+-%s%3ld lne: " +#~ msgstr[4] "+-%s%3ld lne: " + +#~ msgid "+--%3ld line folded " +#~ msgid_plural "+--%3ld lines folded " +#~ msgstr[0] "+--%3ld lne fillte " +#~ msgstr[1] "+--%3ld lne fillte " +#~ msgstr[2] "+--%3ld lne fillte " +#~ msgstr[3] "+--%3ld lne fillte " +#~ msgstr[4] "+--%3ld lne fillte " + +#~ msgid "Type :quit<Enter> to exit Vim" +#~ msgstr "Clscrobh :quit<Enter> chun Vim a scor" + +#~ msgid "Zero count" +#~ msgstr "Nialas" + #~ msgid "E693: Can only compare Funcref with Funcref" #~ msgstr "E693: Is fidir Funcref a chur i gcomparid le Funcref eile amhin" diff --git a/src/nvim/po/it.po b/src/nvim/po/it.po index b8b119ade6..c95faf1dce 100644 --- a/src/nvim/po/it.po +++ b/src/nvim/po/it.po @@ -11,7 +11,7 @@ # msgid "" msgstr "" -"Project-Id-Version: vim 7.4\n" +"Project-Id-Version: vim 8.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2016-02-11 12:10+0100\n" "PO-Revision-Date: 2016-02-11 14:42+0200\n" @@ -1402,6 +1402,33 @@ msgstr "com: %s" msgid "frame is zero" msgstr "al livello zero" +msgid "E901: gethostbyname() in channel_open()" +msgstr "E901: gethostbyname() in channel_open()" + +msgid "E898: socket() in channel_open()" +msgstr "E898: socket() in channel_open()" + +msgid "E903: received command with non-string argument" +msgstr "E903: il comando ricevuto non aveva come argomento una stringa" + +msgid "E904: last argument for expr/call must be a number" +msgstr "E904: l'ultimo argomento per espressione/chiamata dev'essere numerico" + +msgid "E904: third argument for call must be a list" +msgstr "E904: il terzo argomento della chiamata dev'essere una Lista" + +#, c-format +msgid "E905: received unknown command: %s" +msgstr "E905: recevuto comando non conosciuto: %s" + +#, c-format +msgid "E630: %s(): write while not connected" +msgstr "E630: %s(): scrittura in mancanza di connessione" + +#, c-format +msgid "E631: %s(): write failed" +msgstr "E631: %s(): scrittura non riuscita" + #, c-format msgid "frame at highest level: %d" msgstr "al livello pi alto: %d" @@ -4813,9 +4840,16 @@ msgstr "" "Non posso impostare il contesto di sicurezza per " #, c-format +msgid "E151: No match: %s" +msgstr "E151: Nessuna corrispondenza: %s" + +#, c-format msgid "Could not set security context %s for %s" msgstr "Non posso impostare il contesto di sicurezza %s per %s" +msgid "E934: Cannot jump to a buffer that does not have a name" +msgstr "E934: Impossibile passare a un buffer che non ha un nome" + #, c-format msgid "Could not get security context %s for %s. Removing it!" msgstr "Non posso ottenere il contesto di sicurezza %s per %s. Lo rimuovo!" @@ -5351,10 +5385,15 @@ msgstr "E772: Il file ortografico per versioni di Vim pi recenti" msgid "E770: Unsupported section in spell file" msgstr "E770: Sezione non supportata nel file ortografico" -#: ../spell.c:3762 +msgid "E944: Reverse range in character class" +msgstr "E944: Intervallo invertito nella classe di caratteri" + +msgid "E945: Range too large in character class" +msgstr "E945: Intervallo troppo ampio nella classe di caratteri" + #, c-format -msgid "Warning: region %s not supported" -msgstr "Avviso: regione %s non supportata" +msgid "E779: Old .sug file, needs to be updated: %s" +msgstr "E779: File .sug obsoleto, necessario aggiornarlo: %s" #: ../spell.c:4550 #, c-format @@ -5714,11 +5753,6 @@ msgstr "E753: Non trovato: %s" msgid "E778: This does not look like a .sug file: %s" msgstr "E778: Questo non sembra un file .sug: %s" -#: ../spell.c:9282 -#, c-format -msgid "E779: Old .sug file, needs to be updated: %s" -msgstr "E779: File .sug obsoleto, necessario aggiornarlo: %s" - #: ../spell.c:9286 #, c-format msgid "E780: .sug file is for newer version of Vim: %s" diff --git a/src/nvim/po/ja.euc-jp.po b/src/nvim/po/ja.euc-jp.po index 4b32096f1a..10d7342430 100644 --- a/src/nvim/po/ja.euc-jp.po +++ b/src/nvim/po/ja.euc-jp.po @@ -13,10 +13,10 @@ # msgid "" msgstr "" -"Project-Id-Version: Vim 7.4\n" +"Project-Id-Version: Vim 8.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-09-10 21:10+0900\n" -"PO-Revision-Date: 2016-09-10 21:20+0900\n" +"POT-Creation-Date: 2017-07-03 23:05+0900\n" +"PO-Revision-Date: 2017-07-12 20:45+0900\n" "Last-Translator: MURAOKA Taro <koron.kaoriya@gmail.com>\n" "Language-Team: vim-jp (https://github.com/vim-jp/lang-ja)\n" "Language: Japanese\n" @@ -245,7 +245,8 @@ msgid "E917: Cannot use a callback with %s()" msgstr "E917: %s() ˥ХåϻȤޤ" msgid "E912: cannot use ch_evalexpr()/ch_sendexpr() with a raw or nl channel" -msgstr "E912: nl ͥ ch_evalexpr()/ch_sendexpr ϻȤޤ" +msgstr "" +"E912: raw nl ⡼ɤΥͥ ch_evalexpr()/ch_sendexpr() ϻȤޤ" msgid "E906: not an open channel" msgstr "E906: ƤʤͥǤ" @@ -431,6 +432,9 @@ msgstr ": %s" msgid "Scanning tags." msgstr "." +msgid "match in file" +msgstr "եΥޥå" + msgid " Adding" msgstr " ɲ" @@ -519,6 +523,12 @@ msgstr "E690: :for θ \"in\" ޤ" msgid "E108: No such variable: \"%s\"" msgstr "E108: ѿϤޤ: \"%s\"" +#. For historic reasons this error is not given for a list or dict. +#. * E.g., the b: dict could be locked/unlocked. +#, c-format +msgid "E940: Cannot lock or unlock variable %s" +msgstr "E940: ѿ %s ϥåޤϥåǤޤ" + msgid "E743: variable nested too deep for (un)lock" msgstr "E743: ()åˤѿҤޤ" @@ -697,11 +707,6 @@ msgid "&Ok" msgstr "&Ok" #, c-format -msgid "+-%s%3ld line: " -msgid_plural "+-%s%3ld lines: " -msgstr[0] "+-%s%3ld : " - -#, c-format msgid "E700: Unknown function: %s" msgstr "E700: ̤ΤδؿǤ: %s" @@ -746,8 +751,8 @@ msgstr "E727: ϰ֤λ֤ۤޤ" msgid "<empty>" msgstr "<>" -msgid "E240: No connection to Vim server" -msgstr "E240: Vim Сؤ³ޤ" +msgid "E240: No connection to the X server" +msgstr "E240: X Сؤ³ޤ" #, c-format msgid "E241: Unable to send to %s" @@ -756,6 +761,12 @@ msgstr "E241: %s 뤳ȤǤޤ" msgid "E277: Unable to read a server reply" msgstr "E277: Сαޤ" +msgid "E941: already started a server" +msgstr "E941: СϤǤ˳ϤƤޤ" + +msgid "E942: +clientserver feature not available" +msgstr "E942: +clientserver ǽ̵ˤʤäƤޤ" + msgid "remove() argument" msgstr "remove() ΰ" @@ -993,8 +1004,9 @@ msgstr " ( 1 )" msgid " on %ld lines" msgstr " ( %ld )" -msgid "E147: Cannot do :global recursive" -msgstr "E147: :global ƵŪˤϻȤޤ" +#. will increment global_busy to break out of the loop +msgid "E147: Cannot do :global recursive with a range" +msgstr "E147: :global ϰդǺƵŪˤϻȤޤ" msgid "E148: Regular expression missing from global" msgstr "E148: globalޥɤɽꤵƤޤ" @@ -1180,6 +1192,14 @@ msgstr "\"%s\" " msgid "not found in '%s': \"%s\"" msgstr "'%s' ˤϤޤ: \"%s\"" +#, c-format +msgid "W20: Required python version 2.x not supported, ignoring file: %s" +msgstr "W20: ᤵ줿python 2.xбƤޤե̵뤷ޤ: %s" + +#, c-format +msgid "W21: Required python version 3.x not supported, ignoring file: %s" +msgstr "W21: ᤵ줿python 3.xбƤޤե̵뤷ޤ: %s" + msgid "Source Vim script" msgstr "VimץȤμ" @@ -1278,6 +1298,11 @@ msgstr "դޤϰϤꤵޤ, ؤޤ?" msgid "E494: Use w or w>>" msgstr "E494: w ⤷ w>> ѤƤ" +msgid "E943: Command table needs to be updated, run 'make cmdidxs'" +msgstr "" +"E943: ޥɥơ֥ɬפޤ'make cmdidxs' ¹ԤƤ" +"" + msgid "E319: Sorry, the command is not available in this version" msgstr "E319: ΥСǤϤΥޥɤѤǤޤ, ʤ" @@ -2030,11 +2055,6 @@ msgstr "E350: ߤ 'foldmethod' ǤߤǤޤ" msgid "E351: Cannot delete fold with current 'foldmethod'" msgstr "E351: ߤ 'foldmethod' ǤߤǤޤ" -#, c-format -msgid "+--%3ld line folded " -msgid_plural "+--%3ld lines folded " -msgstr[0] "+--%3ld Ԥޤޤ " - msgid "E222: Add to read buffer" msgstr "E222: ɹХåեɲ" @@ -2819,6 +2839,10 @@ msgid "E251: VIM instance registry property is badly formed. Deleted!" msgstr "E251: VIM ΤϿץѥƥǤ. õޤ!" #, c-format +msgid "E938: Duplicate key in JSON: \"%s\"" +msgstr "E938: JSON˽ʣޤ: \"%s\"" + +#, c-format msgid "E696: Missing comma in List: %s" msgstr "E696: ꥹȷ˥ޤޤ: %s" @@ -2858,7 +2882,7 @@ msgid "This Vim was not compiled with the diff feature." msgstr "Vimˤdiffǽޤ(ѥ)." msgid "Attempt to open script file again: \"" -msgstr "ץȥեƤӳƤߤޤ: \"" +msgstr "ץȥեƤӳȤޤ: \"" msgid "Cannot open for reading: \"" msgstr "ɹѤȤƳޤ" @@ -3039,6 +3063,9 @@ msgstr "-T <terminal>\tü <terminal> ꤹ" msgid "--not-a-term\t\tSkip warning for input/output not being a terminal" msgstr "--not-a-term\t\tϤüǤʤȤηٹåפ" +msgid "--ttyfail\t\tExit if input or output is not a terminal" +msgstr "--ttyfail\t\tϤüǤʤнλ" + msgid "-u <vimrc>\t\tUse <vimrc> instead of any .vimrc" msgstr "-u <vimrc>\t\t.vimrc <vimrc> Ȥ" @@ -3906,7 +3933,7 @@ msgid "E766: Insufficient arguments for printf()" msgstr "E766: printf() ΰԽʬǤ" msgid "E807: Expected Float argument for printf()" -msgstr "E807: printf() ΰˤưԤƤޤ" +msgstr "E807: printf() ΰˤưԤƤޤ" msgid "E767: Too many arguments to printf()" msgstr "E767: printf() ΰ¿ޤ" @@ -4063,8 +4090,10 @@ msgstr "E662: ѹꥹȤƬ" msgid "E663: At end of changelist" msgstr "E663: ѹꥹȤ" -msgid "Type :quit<Enter> to exit Vim" -msgstr "Vimλˤ :quit<Enter> ϤƤ" +msgid "Type :qa! and press <Enter> to abandon all changes and exit Vim" +msgstr "" +"٤Ƥѹ˴Vimλˤ :qa! Ϥ <Enter> Ƥ" +"" #, c-format msgid "1 line %sed 1 time" @@ -4413,9 +4442,6 @@ msgstr "ϥ顼" msgid "Message" msgstr "å" -msgid "'columns' is not 80, cannot execute external commands" -msgstr "'columns' 80 ǤϤʤᡢޥɤ¹ԤǤޤ" - msgid "E237: Printer selection failed" msgstr "E237: ץ˼Ԥޤ" @@ -4685,6 +4711,12 @@ msgstr "E369: ̵ʹܤǤ: %s%%[]" msgid "E769: Missing ] after %s[" msgstr "E769: %s[ θ ] ޤ" +msgid "E944: Reverse range in character class" +msgstr "E944: ʸ饹ϰϤդǤ" + +msgid "E945: Range too large in character class" +msgstr "E945: ʸ饹ϰϤ礭ޤ" + #, c-format msgid "E53: Unmatched %s%%(" msgstr "E53: %s%%( äƤޤ" @@ -4714,6 +4746,10 @@ msgstr "E69: %s%%[ θ ] ޤ" msgid "E70: Empty %s%%[]" msgstr "E70: %s%%[] Ǥ" +# +msgid "E65: Illegal back reference" +msgstr "E65: ʸȤǤ" + msgid "E339: Pattern too long" msgstr "E339: ѥĹޤ" @@ -4752,10 +4788,6 @@ msgid "E64: %s%c follows nothing" msgstr "E64:%s%c θˤʤˤ⤢ޤ" # -msgid "E65: Illegal back reference" -msgstr "E65: ʸȤǤ" - -# msgid "E68: Invalid character after \\z" msgstr "E68: \\z θʸޤ" @@ -5363,12 +5395,33 @@ msgstr "E783: MAP ȥ˽ʣʸ¸ߤޤ" msgid "No Syntax items defined for this buffer" msgstr "ΥХåե줿ʸǤϤޤ" +msgid "syntax conceal on" +msgstr "ʸ conceal ϸ on Ǥ" + +msgid "syntax conceal off" +msgstr "ʸ conceal ϸ off Ǥ" + #, c-format msgid "E390: Illegal argument: %s" msgstr "E390: ʰǤ: %s" +msgid "syntax case ignore" +msgstr "ʸʸʸϸ ignore Ǥ" + +msgid "syntax case match" +msgstr "ʸʸʸϸ match Ǥ" + +msgid "syntax spell toplevel" +msgstr "ʸ spell ϸ toplevel Ǥ" + +msgid "syntax spell notoplevel" +msgstr "ʸ spell ϸ notoplevel Ǥ" + +msgid "syntax spell default" +msgstr "ʸ spell ϸ default Ǥ" + msgid "syntax iskeyword " -msgstr "å iskeyword " +msgstr "ʸ iskeyword " #, c-format msgid "E391: No such syntax cluster: %s" @@ -5579,7 +5632,7 @@ msgid "E556: at top of tag stack" msgstr "E556: åƬǤ" msgid "E425: Cannot go before first matching tag" -msgstr "E425: ǽγĶ뤳ȤϤǤޤ" +msgstr "E425: ǽγۤ뤳ȤϤǤޤ" #, c-format msgid "E426: tag not found: %s" @@ -5595,7 +5648,7 @@ msgid "E427: There is only one matching tag" msgstr "E427: 1Ĥޤ" msgid "E428: Cannot go beyond last matching tag" -msgstr "E428: Ǹ˳륿ĶƿʤळȤϤǤޤ" +msgstr "E428: ǸγۤƿʤळȤϤǤޤ" #, c-format msgid "File \"%s\" does not exist" @@ -5943,6 +5996,10 @@ msgid "E126: Missing :endfunction" msgstr "E126: :endfunction ޤ" #, c-format +msgid "W22: Text found after :endfunction: %s" +msgstr "W22: :endfunction θʸޤ: %s" + +#, c-format msgid "E707: Function name conflicts with variable: %s" msgstr "E707: ؿ̾ѿ̾Ⱦͤޤ: %s" @@ -5965,14 +6022,6 @@ msgstr "E133: ؿ :return ޤ" msgid "E107: Missing parentheses: %s" msgstr "E107: å '(' ޤ: %s" -#. Only MS VC 4.1 and earlier can do Win32s -msgid "" -"\n" -"MS-Windows 16/32-bit GUI version" -msgstr "" -"\n" -"MS-Windows 16/32 ӥå GUI " - msgid "" "\n" "MS-Windows 64-bit GUI version" @@ -6256,12 +6305,6 @@ msgstr "ܺ٤ʾ :help register<Enter> " msgid "menu Help->Sponsor/Register for information " msgstr "ܺ٤ϥ˥塼 إ->ݥ/Ͽ ȤƲ" -msgid "WARNING: Windows 95/98/ME detected" -msgstr "ٹ: Windows 95/98/ME Фޤ" - -msgid "type :help windows95<Enter> for info on this" -msgstr "ܺ٤ʾ :help windows95<Enter>" - msgid "Already only one window" msgstr "˥ɥ1Ĥޤ" @@ -6415,6 +6458,10 @@ msgstr "E236: ե \"%s\" ϸǤϤޤ" msgid "E473: Internal error" msgstr "E473: 顼Ǥ" +#, c-format +msgid "E685: Internal error: %s" +msgstr "E685: 顼Ǥ: %s" + msgid "Interrupted" msgstr "ޤޤ" @@ -6680,8 +6727,8 @@ msgstr "E592: 'winwidth' 'winminwidth' 꾮Ǥޤ" msgid "E80: Error while writing" msgstr "E80: Υ顼" -msgid "Zero count" -msgstr "" +msgid "E939: Positive count required" +msgstr "E939: ΥȤɬפǤ" msgid "E81: Using <SID> not in a script context" msgstr "E81: ץȰʳ<SID>Ȥޤ" @@ -6695,10 +6742,6 @@ msgstr "E463: ΰ褬ݸƤΤ, ѹǤޤ" msgid "E744: NetBeans does not allow changes in read-only files" msgstr "E744: NetBeans ɹѥեѹ뤳Ȥޤ" -#, c-format -msgid "E685: Internal error: %s" -msgstr "E685: 顼Ǥ: %s" - msgid "E363: pattern uses more memory than 'maxmempattern'" msgstr "E363: ѥ 'maxmempattern' ʾΥѤޤ" diff --git a/src/nvim/po/ja.po b/src/nvim/po/ja.po index 5cb789c93e..39b4a89517 100644 --- a/src/nvim/po/ja.po +++ b/src/nvim/po/ja.po @@ -13,10 +13,10 @@ # msgid "" msgstr "" -"Project-Id-Version: Vim 7.4\n" +"Project-Id-Version: Vim 8.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-09-10 21:10+0900\n" -"PO-Revision-Date: 2016-09-10 21:20+0900\n" +"POT-Creation-Date: 2017-07-03 23:05+0900\n" +"PO-Revision-Date: 2017-07-12 20:45+0900\n" "Last-Translator: MURAOKA Taro <koron.kaoriya@gmail.com>\n" "Language-Team: vim-jp (https://github.com/vim-jp/lang-ja)\n" "Language: Japanese\n" @@ -245,7 +245,8 @@ msgid "E917: Cannot use a callback with %s()" msgstr "E917: %s() にコールバックは使えません" msgid "E912: cannot use ch_evalexpr()/ch_sendexpr() with a raw or nl channel" -msgstr "E912: 生や nl チャンネルに ch_evalexpr()/ch_sendexpr は使えません" +msgstr "" +"E912: raw や nl モードのチャンネルに ch_evalexpr()/ch_sendexpr() は使えません" msgid "E906: not an open channel" msgstr "E906: 開いていないチャンネルです" @@ -431,6 +432,9 @@ msgstr "スキャン中: %s" msgid "Scanning tags." msgstr "タグをスキャン中." +msgid "match in file" +msgstr "ファイル内のマッチ" + msgid " Adding" msgstr " 追加中" @@ -519,6 +523,12 @@ msgstr "E690: :for の後に \"in\" がありません" msgid "E108: No such variable: \"%s\"" msgstr "E108: その変数はありません: \"%s\"" +#. For historic reasons this error is not given for a list or dict. +#. * E.g., the b: dict could be locked/unlocked. +#, c-format +msgid "E940: Cannot lock or unlock variable %s" +msgstr "E940: 変数 %s はロックまたはアンロックできません" + msgid "E743: variable nested too deep for (un)lock" msgstr "E743: (アン)ロックするには変数の入れ子が深過ぎます" @@ -697,11 +707,6 @@ msgid "&Ok" msgstr "&Ok" #, c-format -msgid "+-%s%3ld line: " -msgid_plural "+-%s%3ld lines: " -msgstr[0] "+-%s%3ld 行: " - -#, c-format msgid "E700: Unknown function: %s" msgstr "E700: 未知の関数です: %s" @@ -746,8 +751,8 @@ msgstr "E727: 開始位置が終了位置を越えました" msgid "<empty>" msgstr "<空>" -msgid "E240: No connection to Vim server" -msgstr "E240: Vim サーバーへの接続がありません" +msgid "E240: No connection to the X server" +msgstr "E240: X サーバーへの接続がありません" #, c-format msgid "E241: Unable to send to %s" @@ -756,6 +761,12 @@ msgstr "E241: %s へ送ることができません" msgid "E277: Unable to read a server reply" msgstr "E277: サーバーの応答がありません" +msgid "E941: already started a server" +msgstr "E941: サーバーはすでに開始しています" + +msgid "E942: +clientserver feature not available" +msgstr "E942: +clientserver 機能が無効になっています" + msgid "remove() argument" msgstr "remove() の引数" @@ -993,8 +1004,9 @@ msgstr " (計 1 行内)" msgid " on %ld lines" msgstr " (計 %ld 行内)" -msgid "E147: Cannot do :global recursive" -msgstr "E147: :global を再帰的には使えません" +#. will increment global_busy to break out of the loop +msgid "E147: Cannot do :global recursive with a range" +msgstr "E147: :global を範囲付きで再帰的には使えません" msgid "E148: Regular expression missing from global" msgstr "E148: globalコマンドに正規表現が指定されていません" @@ -1180,6 +1192,14 @@ msgstr "\"%s\" を検索中" msgid "not found in '%s': \"%s\"" msgstr "'%s' の中にはありません: \"%s\"" +#, c-format +msgid "W20: Required python version 2.x not supported, ignoring file: %s" +msgstr "W20: 要求されたpython 2.xは対応していません、ファイルを無視します: %s" + +#, c-format +msgid "W21: Required python version 3.x not supported, ignoring file: %s" +msgstr "W21: 要求されたpython 3.xは対応していません、ファイルを無視します: %s" + msgid "Source Vim script" msgstr "Vimスクリプトの取込み" @@ -1278,6 +1298,11 @@ msgstr "逆さまの範囲が指定されました, 入替えますか?" msgid "E494: Use w or w>>" msgstr "E494: w もしくは w>> を使用してください" +msgid "E943: Command table needs to be updated, run 'make cmdidxs'" +msgstr "" +"E943: コマンドテーブルを更新する必要があります、'make cmdidxs' を実行してくだ" +"さい" + msgid "E319: Sorry, the command is not available in this version" msgstr "E319: このバージョンではこのコマンドは利用できません, ごめんなさい" @@ -2030,11 +2055,6 @@ msgstr "E350: 現在の 'foldmethod' では折畳みを作成できません" msgid "E351: Cannot delete fold with current 'foldmethod'" msgstr "E351: 現在の 'foldmethod' では折畳みを削除できません" -#, c-format -msgid "+--%3ld line folded " -msgid_plural "+--%3ld lines folded " -msgstr[0] "+--%3ld 行が折畳まれました " - msgid "E222: Add to read buffer" msgstr "E222: 読込バッファへ追加" @@ -2819,6 +2839,10 @@ msgid "E251: VIM instance registry property is badly formed. Deleted!" msgstr "E251: VIM 実体の登録プロパティが不正です. 消去しました!" #, c-format +msgid "E938: Duplicate key in JSON: \"%s\"" +msgstr "E938: JSONに重複キーがあります: \"%s\"" + +#, c-format msgid "E696: Missing comma in List: %s" msgstr "E696: リスト型にカンマがありません: %s" @@ -2858,7 +2882,7 @@ msgid "This Vim was not compiled with the diff feature." msgstr "このVimにはdiff機能がありません(コンパイル時設定)." msgid "Attempt to open script file again: \"" -msgstr "スクリプトファイルを再び開いてみます: \"" +msgstr "スクリプトファイルを再び開こうとしました: \"" msgid "Cannot open for reading: \"" msgstr "読込用として開けません" @@ -3039,6 +3063,9 @@ msgstr "-T <terminal>\t端末を <terminal> に設定する" msgid "--not-a-term\t\tSkip warning for input/output not being a terminal" msgstr "--not-a-term\t\t入出力が端末でないとの警告をスキップする" +msgid "--ttyfail\t\tExit if input or output is not a terminal" +msgstr "--ttyfail\t\t入出力が端末でなければ終了する" + msgid "-u <vimrc>\t\tUse <vimrc> instead of any .vimrc" msgstr "-u <vimrc>\t\t.vimrcの代わりに <vimrc> を使う" @@ -3906,7 +3933,7 @@ msgid "E766: Insufficient arguments for printf()" msgstr "E766: printf() の引数が不十分です" msgid "E807: Expected Float argument for printf()" -msgstr "E807: printf() の引数には浮動少数点数が期待されています" +msgstr "E807: printf() の引数には浮動小数点数が期待されています" msgid "E767: Too many arguments to printf()" msgstr "E767: printf() の引数が多過ぎます" @@ -4063,8 +4090,10 @@ msgstr "E662: 変更リストの先頭" msgid "E663: At end of changelist" msgstr "E663: 変更リストの末尾" -msgid "Type :quit<Enter> to exit Vim" -msgstr "Vimを終了するには :quit<Enter> と入力してください" +msgid "Type :qa! and press <Enter> to abandon all changes and exit Vim" +msgstr "" +"すべての変更を破棄し、Vimを終了するには :qa! と入力し <Enter> を押してくだ" +"さい" #, c-format msgid "1 line %sed 1 time" @@ -4413,9 +4442,6 @@ msgstr "入出力エラー" msgid "Message" msgstr "メッセージ" -msgid "'columns' is not 80, cannot execute external commands" -msgstr "'columns' が 80 ではないため、外部コマンドを実行できません" - msgid "E237: Printer selection failed" msgstr "E237: プリンタの選択に失敗しました" @@ -4685,6 +4711,12 @@ msgstr "E369: 無効な項目です: %s%%[]" msgid "E769: Missing ] after %s[" msgstr "E769: %s[ の後に ] がありません" +msgid "E944: Reverse range in character class" +msgstr "E944: 文字クラスの範囲が逆です" + +msgid "E945: Range too large in character class" +msgstr "E945: 文字クラスの範囲が大きすぎます" + #, c-format msgid "E53: Unmatched %s%%(" msgstr "E53: %s%%( が釣り合っていません" @@ -4714,6 +4746,10 @@ msgstr "E69: %s%%[ の後に ] がありません" msgid "E70: Empty %s%%[]" msgstr "E70: %s%%[] が空です" +# +msgid "E65: Illegal back reference" +msgstr "E65: 不正な後方参照です" + msgid "E339: Pattern too long" msgstr "E339: パターンが長過ぎます" @@ -4752,10 +4788,6 @@ msgid "E64: %s%c follows nothing" msgstr "E64:%s%c の後になにもありません" # -msgid "E65: Illegal back reference" -msgstr "E65: 不正な後方参照です" - -# msgid "E68: Invalid character after \\z" msgstr "E68: \\z の後に不正な文字がありました" @@ -5363,12 +5395,33 @@ msgstr "E783: MAP エントリに重複文字が存在します" msgid "No Syntax items defined for this buffer" msgstr "このバッファに定義された構文要素はありません" +msgid "syntax conceal on" +msgstr "構文の conceal は現在 on です" + +msgid "syntax conceal off" +msgstr "構文の conceal は現在 off です" + #, c-format msgid "E390: Illegal argument: %s" msgstr "E390: 不正な引数です: %s" +msgid "syntax case ignore" +msgstr "構文の大文字小文字は現在 ignore です" + +msgid "syntax case match" +msgstr "構文の大文字小文字は現在 match です" + +msgid "syntax spell toplevel" +msgstr "構文の spell は現在 toplevel です" + +msgid "syntax spell notoplevel" +msgstr "構文の spell は現在 notoplevel です" + +msgid "syntax spell default" +msgstr "構文の spell は現在 default です" + msgid "syntax iskeyword " -msgstr "シンタックス用 iskeyword " +msgstr "構文用 iskeyword " #, c-format msgid "E391: No such syntax cluster: %s" @@ -5579,7 +5632,7 @@ msgid "E556: at top of tag stack" msgstr "E556: タグスタックの先頭です" msgid "E425: Cannot go before first matching tag" -msgstr "E425: 最初の該当タグを超えて戻ることはできません" +msgstr "E425: 最初の該当タグを越えて戻ることはできません" #, c-format msgid "E426: tag not found: %s" @@ -5595,7 +5648,7 @@ msgid "E427: There is only one matching tag" msgstr "E427: 該当タグが1つだけしかありません" msgid "E428: Cannot go beyond last matching tag" -msgstr "E428: 最後に該当するタグを超えて進むことはできません" +msgstr "E428: 最後の該当タグを越えて進むことはできません" #, c-format msgid "File \"%s\" does not exist" @@ -5943,6 +5996,10 @@ msgid "E126: Missing :endfunction" msgstr "E126: :endfunction がありません" #, c-format +msgid "W22: Text found after :endfunction: %s" +msgstr "W22: :endfunction の後に文字があります: %s" + +#, c-format msgid "E707: Function name conflicts with variable: %s" msgstr "E707: 関数名が変数名と衝突します: %s" @@ -5965,14 +6022,6 @@ msgstr "E133: 関数外に :return がありました" msgid "E107: Missing parentheses: %s" msgstr "E107: カッコ '(' がありません: %s" -#. Only MS VC 4.1 and earlier can do Win32s -msgid "" -"\n" -"MS-Windows 16/32-bit GUI version" -msgstr "" -"\n" -"MS-Windows 16/32 ビット GUI 版" - msgid "" "\n" "MS-Windows 64-bit GUI version" @@ -6256,12 +6305,6 @@ msgstr "詳細な情報は :help register<Enter> " msgid "menu Help->Sponsor/Register for information " msgstr "詳細はメニューの ヘルプ->スポンサー/登録 を参照して下さい" -msgid "WARNING: Windows 95/98/ME detected" -msgstr "警告: Windows 95/98/ME を検出しました" - -msgid "type :help windows95<Enter> for info on this" -msgstr "詳細な情報は :help windows95<Enter>" - msgid "Already only one window" msgstr "既にウィンドウは1つしかありません" @@ -6415,6 +6458,10 @@ msgstr "E236: フォント \"%s\" は固定幅ではありません" msgid "E473: Internal error" msgstr "E473: 内部エラーです" +#, c-format +msgid "E685: Internal error: %s" +msgstr "E685: 内部エラーです: %s" + msgid "Interrupted" msgstr "割込まれました" @@ -6680,8 +6727,8 @@ msgstr "E592: 'winwidth' は 'winminwidth' より小さくできません" msgid "E80: Error while writing" msgstr "E80: 書込み中のエラー" -msgid "Zero count" -msgstr "ゼロカウント" +msgid "E939: Positive count required" +msgstr "E939: 正のカウントが必要です" msgid "E81: Using <SID> not in a script context" msgstr "E81: スクリプト以外で<SID>が使われました" @@ -6695,10 +6742,6 @@ msgstr "E463: 領域が保護されているので, 変更できません" msgid "E744: NetBeans does not allow changes in read-only files" msgstr "E744: NetBeans は読込専用ファイルを変更することを許しません" -#, c-format -msgid "E685: Internal error: %s" -msgstr "E685: 内部エラーです: %s" - msgid "E363: pattern uses more memory than 'maxmempattern'" msgstr "E363: パターンが 'maxmempattern' 以上のメモリを使用します" diff --git a/src/nvim/po/ko.UTF-8.po b/src/nvim/po/ko.UTF-8.po index 7afa507edb..6173283135 100644 --- a/src/nvim/po/ko.UTF-8.po +++ b/src/nvim/po/ko.UTF-8.po @@ -1111,6 +1111,10 @@ msgstr "E137: Viminfo 파일의 쓰기 권한이 없습니다: %s" #: ../ex_cmds.c:1626 #, c-format +msgid "E929: Too many viminfo temp files, like %s!" +msgstr "E929: 너무 많은 viminfo 임시 파일들, 가령 %s!" + +#, c-format msgid "E138: Can't write viminfo file %s!" msgstr "E138: Viminfo 파일 %s을(를) 쓸 수 없습니다!" @@ -1119,6 +1123,10 @@ msgstr "E138: Viminfo 파일 %s을(를) 쓸 수 없습니다!" msgid "Writing viminfo file \"%s\"" msgstr "Viminfo 파일 \"%s\"을(를) 쓰는 중" +#, c-format +msgid "E886: Can't rename viminfo file to %s!" +msgstr "E886: viminfo 파일명을 %s(으)로 변경할 수 없습니다!" + #. Write the info: #: ../ex_cmds.c:1720 #, c-format @@ -1300,8 +1308,8 @@ msgstr "미안합니다, 도움말 파일 \"%s\"을(를) 찾을 수 없습니다 #: ../ex_cmds.c:5323 #, c-format -msgid "E150: Not a directory: %s" -msgstr "E150: 디렉토리가 아님: %s" +msgid "E151: No match: %s" +msgstr "E151: 맞지 않음: %s" #: ../ex_cmds.c:5446 #, c-format @@ -1325,6 +1333,10 @@ msgstr "E154: \"%s\" 태그가 %s/%s 파일에서 중복되었습니다" #: ../ex_cmds.c:5687 #, c-format +msgid "E150: Not a directory: %s" +msgstr "E150: 디렉토리가 아님: %s" + +#, c-format msgid "E160: Unknown sign command: %s" msgstr "E160: 모르는 sign 명령: %s" @@ -1452,8 +1464,16 @@ msgstr "\"%s\"을(를) 찾는 중" #: ../ex_cmds2.c:2307 #, c-format -msgid "not found in 'runtimepath': \"%s\"" -msgstr "'runtimepath'에서 찾을 수 없음: \"%s\"" +msgid "not found in '%s': \"%s\"" +msgstr "'%s'에서 찾을 수 없음: \"%s\"" + +#, c-format +msgid "W20: Required python version 2.x not supported, ignoring file: %s" +msgstr "W20: 요구되는 파이선 버젼 2.x는 지원되지 않음, 파일을 무시: %s" + +#, c-format +msgid "W21: Required python version 3.x not supported, ignoring file: %s" +msgstr "W21: 요구되는 파이선 버젼 3.x는 지원되지 않음, 파일을 무시: %s" #: ../ex_cmds2.c:2472 #, c-format @@ -1609,10 +1629,10 @@ msgstr "E174: 명령이 이미 존재합니다: 바꾸려면 !을 더하세요" #: ../ex_docmd.c:4432 msgid "" "\n" -" Name Args Range Complete Definition" +" Name Args Address Complete Definition" msgstr "" "\n" -" 이름 인자 범위 완성 정의" +" 이름 인자 주소 완성 정의" #: ../ex_docmd.c:4516 msgid "No user-defined commands found" @@ -1662,6 +1682,10 @@ msgstr "E184: 그런 사용자 정의 명령 없음: %s" #: ../ex_docmd.c:5219 #, c-format +msgid "E180: Invalid address type value: %s" +msgstr "E180: 잘못된 주소 형식 값: %s" + +#, c-format msgid "E180: Invalid complete value: %s" msgstr "E180: 잘못된 끝내기 값: %s" @@ -1953,7 +1977,7 @@ msgstr "" #: ../ex_getln.c:5047 msgid "Command Line" -msgstr "명령 줄" +msgstr "명령 행" #: ../ex_getln.c:5048 msgid "Search String" @@ -1965,7 +1989,10 @@ msgstr "표현" #: ../ex_getln.c:5050 msgid "Input Line" -msgstr "입력 줄" +msgstr "입력 행" + +msgid "Debug Line" +msgstr "디버그 행" #: ../ex_getln.c:5117 msgid "E198: cmd_pchar beyond the command length" @@ -3190,6 +3217,7 @@ msgstr "%-5s: %s%*s (사용법: %s)" #: ../if_cscope.c:1155 msgid "" "\n" +" a: Find assignments to this symbol\n" " c: Find functions calling this function\n" " d: Find functions called by this function\n" " e: Find this egrep pattern\n" @@ -3200,13 +3228,14 @@ msgid "" " t: Find this text string\n" msgstr "" "\n" +" a: 이 기호에 대한 할당 찾기\n" " c: 이 함수를 부르는 함수들 찾기\n" " d: 이 함수에 의해 불려지는 함수들 찾기\n" " e: 이 egrep 패턴 찾기\n" " f: 이 파일 찾기\n" " g: 이 정의 찾기\n" -" i: 이 파일을 포함하는 파일들 찾기\n" -" s: 이 C 심볼 찾기\n" +" i: 이 파일을 #include하는 파일들 찾기\n" +" s: 이 C 기호 찾기\n" " t: 이 문자열 찾기\n" #: ../if_cscope.c:1226 @@ -4740,6 +4769,10 @@ msgstr "E447: path에서 \"%s\" 파일을 찾을 수 없습니다" #: ../quickfix.c:359 #, c-format +msgid "shell returned %d" +msgstr "쉘이 %d을(를) 돌려주었습니다" + +#, c-format msgid "E372: Too many %%%c in format string" msgstr "E372: 형식 문자열에 %%%c이(가) 너무 많습니다" @@ -5247,11 +5280,6 @@ msgstr "E772: Spell 파일이 새 버젼의 Vim용입니다" msgid "E770: Unsupported section in spell file" msgstr "E770: spell 파일에 지원되지 않는 섹션" -#: ../spell.c:3762 -#, c-format -msgid "Warning: region %s not supported" -msgstr "경고: %s 영역은 지원되지 않습니다" - #: ../spell.c:4550 #, c-format msgid "Reading affix file %s ..." @@ -6312,7 +6340,7 @@ msgstr "by Bram Moolenaar et al." #: ../version.c:774 msgid "Vim is open source and freely distributable" -msgstr "빔은 소스가 열려 있고 공짜로 배포됩니다" +msgstr "빔은 누구나 소스를 볼 수 있고 공짜로 배포됩니다" #: ../version.c:776 msgid "Help poor children in Uganda!" @@ -7478,9 +7506,6 @@ msgstr "E446: 커서 밑에 파일 이름이 없습니다" #~ msgid "Could not fix up function pointers to the DLL!" #~ msgstr "함수 포인터를 DLL로 바꿀 수 없습니다!" -#~ msgid "shell returned %d" -#~ msgstr "쉘이 %d을(를) 돌려주었습니다" - #~ msgid "Vim: Caught %s event\n" #~ msgstr "빔: %s 이벤트를 잡았습니다\n" diff --git a/src/nvim/po/pl.UTF-8.po b/src/nvim/po/pl.UTF-8.po index 0757afd3ae..c65602344e 100644 --- a/src/nvim/po/pl.UTF-8.po +++ b/src/nvim/po/pl.UTF-8.po @@ -3550,7 +3550,7 @@ msgstr "" #: ../main.c:2240 msgid "--startuptime <file>\tWrite startup timing messages to <file>" msgstr "" -"--startuptime <plik>\n" +"--startuptime <plik> " "Zapisz wiadomości o długości startu do <plik>" #: ../main.c:2242 diff --git a/src/nvim/po/pt_BR.po b/src/nvim/po/pt_BR.po index 05986cf097..f8d6ac3011 100644 --- a/src/nvim/po/pt_BR.po +++ b/src/nvim/po/pt_BR.po @@ -24,7 +24,7 @@ msgstr "[Ajuda]" #: ../screen.c:4815 ../buffer.c:3244 msgid "[Preview]" -msgstr "[Visualizao]" +msgstr "[Visualização]" #: ../screen.c:4823 ../fileio.c:1855 ../buffer.c:2496 ../buffer.c:3207 msgid "[RO]" @@ -4859,7 +4859,7 @@ msgstr " tipo arquivo\n" #: ../ex_getln.c:4762 msgid "'history' option is zero" -msgstr "opo 'history' vale zero" +msgstr "opção 'history' vale zero" #: ../ex_getln.c:5008 #, c-format @@ -6203,7 +6203,7 @@ msgstr "--\t\t\tApenas nomes de arquivo depois daqui" #: ../main.c:2177 msgid "--literal\t\tDon't expand wildcards" -msgstr "--literal\t\tNo expandir caracteres-curinga" +msgstr "--literal\t\tNão expandir caracteres-curinga" #: ../main.c:2179 msgid "-v\t\t\tVi mode (like \"vi\")" @@ -6227,7 +6227,7 @@ msgstr "-d\t\t\tModo diff (como \"vimdiff\")" #: ../main.c:2184 msgid "-y\t\t\tEasy mode (like \"evim\", modeless)" -msgstr "-y\t\t\tModo fcil (como \"evim\", o Vim no modal)" +msgstr "-y\t\t\tModo fácil (como \"evim\", o Vim não modal)" #: ../main.c:2185 msgid "-R\t\t\tReadonly mode (like \"view\")" @@ -6341,8 +6341,8 @@ msgstr "" #: ../main.c:2213 msgid "-S <session>\t\tSource file <session> after loading the first file" msgstr "" -"-S <sesso>\t\tExecutar o arquivo <sesso> depois de carregar o\n" -"\t\t\tprimeiro arquivo" +"-S <sesso>\t\tExecutar o arquivo <sesso> depois de carregar o " +"primeiro arquivo" #: ../main.c:2214 msgid "-s <scriptin>\tRead Normal mode commands from file <scriptin>" diff --git a/src/nvim/po/ru.po b/src/nvim/po/ru.po index a4668743ba..12022d02c8 100644 --- a/src/nvim/po/ru.po +++ b/src/nvim/po/ru.po @@ -3376,8 +3376,7 @@ msgstr "-t метка редактирование файла с указан #: ../main.c:2181 msgid "-q [errorfile] edit file with first error" msgstr "" -"-q [файл-ошибок]\n" -"\t\t\t\t редактирование файла с первой ошибкой" +"-q [файл-ошибок] редактирование файла с первой ошибкой" #: ../main.c:2187 msgid "" @@ -3479,8 +3478,8 @@ msgstr "-N\t\t\tРежим неполной совместимости с Vi: 'n #: ../main.c:2215 msgid "-V[N][fname]\t\tBe verbose [level N] [log messages to fname]" msgstr "" -"-V[N][файл]\t\tВыводить дополнительные сообщения\n" -"\t\t\t\t[уровень N] [записывать в файл]" +"-V[N][файл]\t\tВыводить дополнительные сообщения " +"[уровень N] [записывать в файл]" #: ../main.c:2216 msgid "-D\t\t\tDebugging mode" @@ -4105,7 +4104,6 @@ msgstr "" #: ../memline.c:3245 msgid " Quit, or continue with caution.\n" msgstr "" -" \n" " Завершите работу или продолжайте с осторожностью.\n" #: ../memline.c:3246 diff --git a/src/nvim/po/sk.cp1250.po b/src/nvim/po/sk.cp1250.po index 4b1e64bd02..bf205938fa 100644 --- a/src/nvim/po/sk.cp1250.po +++ b/src/nvim/po/sk.cp1250.po @@ -3870,6 +3870,7 @@ msgid "" "You may want to delete the .swp file now.\n" "\n" msgstr "Potom vymate odkladac sbor s prponou .swp.\n" +"\n" #. use msg() to start the scrolling properly #: ../memline.c:1327 diff --git a/src/nvim/po/sk.po b/src/nvim/po/sk.po index e48a5de927..3c92ec3c34 100644 --- a/src/nvim/po/sk.po +++ b/src/nvim/po/sk.po @@ -3870,6 +3870,7 @@ msgid "" "You may want to delete the .swp file now.\n" "\n" msgstr "Potom vymate odkladac sbor s prponou .swp.\n" +"\n" #. use msg() to start the scrolling properly #: ../memline.c:1327 diff --git a/src/nvim/po/uk.po b/src/nvim/po/uk.po index 2c203f808f..1ac2d2247a 100644 --- a/src/nvim/po/uk.po +++ b/src/nvim/po/uk.po @@ -1,6 +1,8 @@ # # Ukrainian Vim translation [uk] # +# Original translations +# # Copyright (C) 2001 Bohdan Vlasyuk <bohdan@vstu.edu.ua> # Bohdan donated this work to be distributed with Vim under the Vim license. # @@ -510,6 +512,9 @@ msgstr "Пошук у: %s" msgid "Scanning tags." msgstr "Пошук серед теґів." +msgid "match in file" +msgstr "збіг у файлі" + msgid " Adding" msgstr " Додається" @@ -642,6 +647,12 @@ msgstr "E107: Пропущено дужки: %s" msgid "E108: No such variable: \"%s\"" msgstr "E108: Змінної немає: «%s»" +#. For historic reasons this error is not given for a list or dict. +#. * E.g., the b: dict could be locked/unlocked. +#, c-format +msgid "E940: Cannot lock or unlock variable %s" +msgstr "E940: Неможливо заблокувати чи розблокувати змінну %s" + msgid "E743: variable nested too deep for (un)lock" msgstr "E743: Змінна має забагато вкладень щоб бути за-/відкритою." @@ -1392,8 +1403,9 @@ msgstr " в одному рядку" msgid " on %<PRId64> lines" msgstr " в %<PRId64> рядках" -msgid "E147: Cannot do :global recursive" -msgstr "E147: :global не можна вживати рекурсивно" +#. will increment global_busy to break out of the loop +msgid "E147: Cannot do :global recursive with a range" +msgstr "E147: :global не можна вживати рекурсивно з діапазоном" msgid "E148: Regular expression missing from global" msgstr "E148: У global бракує зразка" @@ -3516,7 +3528,6 @@ msgid "" msgstr "" "»,\n" " щоб позбутися цього повідомлення.\n" -"\n" msgid "Swap file \"" msgstr "Файл обміну «" @@ -4130,6 +4141,12 @@ msgstr "E369: Некоректний елемент у %s%%[]" msgid "E769: Missing ] after %s[" msgstr "E769: Бракує ] після %s[" +msgid "E944: Reverse range in character class" +msgstr "E944: Зворотній діапазон у класі символів" + +msgid "E945: Range too large in character class" +msgstr "E945: Завеликий діапазон у класі символів" + #, c-format msgid "E53: Unmatched %s%%(" msgstr "E53: Немає пари %s%%(" @@ -4456,7 +4473,7 @@ msgid "Can't rename ShaDa file from %s to %s!" msgstr "Не вдалося перейменувати файл ShaDa з %s у %s!" #, c-format -msgid "Did not rename %s because %s does not looks like a ShaDa file" +msgid "Did not rename %s because %s does not look like a ShaDa file" msgstr "Не перейменував %s, тому що %s не схожий на файл ShaDa" #, c-format @@ -4888,10 +4905,31 @@ msgstr "E783: Повторено символ у елементі MAP" msgid "No Syntax items defined for this buffer" msgstr "Для буфера не визначено елементів синтаксису" +msgid "syntax conceal on" +msgstr "маскування синтаксису увімк" + +msgid "syntax conceal off" +msgstr "маскування синтаксису вимк" + #, c-format msgid "E390: Illegal argument: %s" msgstr "E390: Неправильний аргумент: %s" +msgid "syntax case ignore" +msgstr "синтаксис ігнорувати регістр" + +msgid "syntax case match" +msgstr "синтаксис дотримуватися регістру" + +msgid "syntax spell toplevel" +msgstr "синтаксис перевіряти всюди" + +msgid "syntax spell notoplevel" +msgstr "синтаксис не перевіряти" + +msgid "syntax spell default" +msgstr "синтаксис початково" + msgid "syntax iskeyword " msgstr "синтаксис iskeyword " diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index 2462975c9b..348daf028a 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -567,6 +567,7 @@ static int pum_set_selected(int n, int repeat) && (repeat <= 1) && (vim_strchr(p_cot, 'p') != NULL)) { win_T *curwin_save = curwin; + tabpage_T *curtab_save = curtab; int res = OK; // Open a preview window. 3 lines by default. Prefer @@ -593,7 +594,7 @@ static int pum_set_selected(int n, int repeat) && (curbuf->b_p_bt[2] == 'f') && (curbuf->b_p_bh[0] == 'w')) { // Already a "wipeout" buffer, make it empty. - while (!bufempty()) { + while (!BUFEMPTY()) { ml_delete((linenr_T)1, FALSE); } } else { @@ -647,7 +648,12 @@ static int pum_set_selected(int n, int repeat) curwin->w_cursor.lnum = 1; curwin->w_cursor.col = 0; - if ((curwin != curwin_save) && win_valid(curwin_save)) { + if ((curwin != curwin_save && win_valid(curwin_save)) + || (curtab != curtab_save && valid_tabpage(curtab_save))) { + if (curtab != curtab_save && valid_tabpage(curtab_save)) { + goto_tabpage_tp(curtab_save, false, false); + } + // When the first completion is done and the preview // window is not resized, skip the preview window's // status line redrawing. diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index bd5dfa92cc..2d8c353f92 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -76,17 +76,25 @@ struct qfline_S { */ #define LISTCOUNT 10 +/// Quickfix/Location list definition +/// +/// Usually the list contains one or more entries. But an empty list can be +/// created using setqflist()/setloclist() with a title and/or user context +/// information and entries can be added later using setqflist()/setloclist(). typedef struct qf_list_S { - qfline_T *qf_start; // pointer to the first error - qfline_T *qf_last; // pointer to the last error - qfline_T *qf_ptr; // pointer to the current error - int qf_count; // number of errors (0 means no error list) - int qf_index; // current index in the error list - int qf_nonevalid; // TRUE if not a single valid entry found - char_u *qf_title; // title derived from the command that created - // the error list + qfline_T *qf_start; ///< pointer to the first error + qfline_T *qf_last; ///< pointer to the last error + qfline_T *qf_ptr; ///< pointer to the current error + int qf_count; ///< number of errors (0 means empty list) + int qf_index; ///< current index in the error list + int qf_nonevalid; ///< TRUE if not a single valid entry found + char_u *qf_title; ///< title derived from the command that created + ///< the error list or set by setqflist + typval_T *qf_ctx; ///< context set by setqflist/setloclist } qf_list_T; +/// Quickfix/Location list stack definition +/// Contains a list of quickfix/location lists (qf_list_T) struct qf_info_S { /* * Count of references to this list. Used only for location lists. @@ -156,10 +164,12 @@ typedef struct { FILE *fd; typval_T *tv; char_u *p_str; + list_T *p_list; listitem_T *p_li; buf_T *buf; linenr_T buflnum; linenr_T lnumlast; + vimconv_T vc; } qfstate_T; typedef struct { @@ -193,19 +203,19 @@ typedef struct { static char_u *qf_last_bufname = NULL; static bufref_T qf_last_bufref = { NULL, 0, 0 }; -/* - * Read the errorfile "efile" into memory, line by line, building the error - * list. Set the error list's title to qf_title. - * Return -1 for error, number of errors for success. - */ -int -qf_init ( - win_T *wp, - char_u *efile, - char_u *errorformat, - int newlist, /* TRUE: start a new error list */ - char_u *qf_title -) +/// Read the errorfile "efile" into memory, line by line, building the error +/// list. Set the error list's title to qf_title. +/// +/// @params wp If non-NULL, make a location list +/// @params efile If non-NULL, errorfile to parse +/// @params errorformat 'errorformat' string used to parse the error lines +/// @params newlist If true, create a new error list +/// @params qf_title If non-NULL, title of the error list +/// @params enc If non-NULL, encoding used to parse errors +/// +/// @returns -1 for error, number of errors for success. +int qf_init(win_T *wp, char_u *efile, char_u *errorformat, int newlist, + char_u *qf_title, char_u *enc) { qf_info_T *qi = &ql_info; @@ -214,8 +224,8 @@ qf_init ( } return qf_init_ext(qi, efile, curbuf, NULL, errorformat, newlist, - (linenr_T)0, (linenr_T)0, - qf_title); + (linenr_T)0, (linenr_T)0, + qf_title, enc); } // Maximum number of bytes allowed per line while reading an errorfile. @@ -383,6 +393,8 @@ static int efm_to_regpat(char_u *efm, int len, efm_T *fmt_ptr, return 0; } +static efm_T *fmt_start = NULL; // cached across qf_parse_line() calls + static void free_efm_list(efm_T **efm_first) { for (efm_T *efm_ptr = *efm_first; efm_ptr != NULL; efm_ptr = *efm_first) { @@ -390,6 +402,8 @@ static void free_efm_list(efm_T **efm_first) vim_regfree(efm_ptr->prog); xfree(efm_ptr); } + + fmt_start = NULL; } // Parse 'errorformat' option @@ -512,17 +526,17 @@ static int qf_get_next_list_line(qfstate_T *state) // Get the next line from the supplied list while (p_li != NULL - && (p_li->li_tv.v_type != VAR_STRING - || p_li->li_tv.vval.v_string == NULL)) { - p_li = p_li->li_next; // Skip non-string items + && (TV_LIST_ITEM_TV(p_li)->v_type != VAR_STRING + || TV_LIST_ITEM_TV(p_li)->vval.v_string == NULL)) { + p_li = TV_LIST_ITEM_NEXT(state->p_list, p_li); // Skip non-string items. } - if (p_li == NULL) { // End of the list + if (p_li == NULL) { // End of the list. state->p_li = NULL; return QF_END_OF_INPUT; } - len = STRLEN(p_li->li_tv.vval.v_string); + len = STRLEN(TV_LIST_ITEM_TV(p_li)->vval.v_string); if (len > IOSIZE - 2) { state->linebuf = qf_grow_linebuf(state, len); } else { @@ -530,9 +544,10 @@ static int qf_get_next_list_line(qfstate_T *state) state->linelen = len; } - STRLCPY(state->linebuf, p_li->li_tv.vval.v_string, state->linelen + 1); + STRLCPY(state->linebuf, TV_LIST_ITEM_TV(p_li)->vval.v_string, + state->linelen + 1); - state->p_li = p_li->li_next; // next item + state->p_li = TV_LIST_ITEM_NEXT(state->p_list, p_li); return QF_OK; } @@ -566,7 +581,12 @@ static int qf_get_next_file_line(qfstate_T *state) { size_t growbuflen; +retry: + errno = 0; if (fgets((char *)IObuff, IOSIZE, state->fd) == NULL) { + if (errno == EINTR) { + goto retry; + } return QF_END_OF_INPUT; } @@ -586,8 +606,12 @@ static int qf_get_next_file_line(qfstate_T *state) growbuflen = state->linelen; for (;;) { + errno = 0; if (fgets((char *)state->growbuf + growbuflen, (int)(state->growbufsiz - growbuflen), state->fd) == NULL) { + if (errno == EINTR) { + continue; + } break; } state->linelen = STRLEN(state->growbuf + growbuflen); @@ -608,9 +632,14 @@ static int qf_get_next_file_line(qfstate_T *state) while (discard) { // The current line is longer than LINE_MAXLEN, continue reading but // discard everything until EOL or EOF is reached. - if (fgets((char *)IObuff, IOSIZE, state->fd) == NULL - || STRLEN(IObuff) < IOSIZE - 1 - || IObuff[IOSIZE - 1] == '\n') { + errno = 0; + if (fgets((char *)IObuff, IOSIZE, state->fd) == NULL) { + if (errno == EINTR) { + continue; + } + break; + } + if (STRLEN(IObuff) < IOSIZE - 1 || IObuff[IOSIZE - 1] == '\n') { break; } } @@ -620,6 +649,22 @@ static int qf_get_next_file_line(qfstate_T *state) } else { state->linebuf = IObuff; } + + // Convert a line if it contains a non-ASCII character + if (state->vc.vc_type != CONV_NONE && has_non_ascii(state->linebuf)) { + char_u *line = string_convert(&state->vc, state->linebuf, &state->linelen); + if (line != NULL) { + if (state->linelen < IOSIZE) { + STRLCPY(state->linebuf, line, state->linelen + 1); + xfree(line); + } else { + xfree(state->growbuf); + state->linebuf = state->growbuf = line; + state->growbufsiz = state->linelen < LINE_MAXLEN + ? state->linelen : LINE_MAXLEN; + } + } + } return QF_OK; } @@ -671,7 +716,6 @@ static int qf_parse_line(qf_info_T *qi, char_u *linebuf, size_t linelen, efm_T *fmt_first, qffields_T *fields) { efm_T *fmt_ptr; - static efm_T *fmt_start = NULL; // cached across calls size_t len; int i; int idx = 0; @@ -766,7 +810,7 @@ restofline: fields->type = *regmatch.startp[i]; } if (fmt_ptr->flags == '+' && !qi->qf_multiscan) { // %+ - if (linelen > fields->errmsglen) { + if (linelen >= fields->errmsglen) { // linelen + null terminator fields->errmsg = xrealloc(fields->errmsg, linelen + 1); fields->errmsglen = linelen + 1; @@ -777,7 +821,7 @@ restofline: continue; } len = (size_t)(regmatch.endp[i] - regmatch.startp[i]); - if (len > fields->errmsglen) { + if (len >= fields->errmsglen) { // len + null terminator fields->errmsg = xrealloc(fields->errmsg, len + 1); fields->errmsglen = len + 1; @@ -854,7 +898,7 @@ restofline: fields->namebuf[0] = NUL; // no match found, remove file name fields->lnum = 0; // don't jump to this line fields->valid = false; - if (linelen > fields->errmsglen) { + if (linelen >= fields->errmsglen) { // linelen + null terminator fields->errmsg = xrealloc(fields->errmsg, linelen + 1); fields->errmsglen = linelen + 1; @@ -875,36 +919,38 @@ restofline: qi->qf_multiignore = false; // reset continuation } else if (vim_strchr((char_u *)"CZ", idx) != NULL) { // continuation of multi-line msg - qfline_T *qfprev = qi->qf_lists[qi->qf_curlist].qf_last; - if (qfprev == NULL) { - return QF_FAIL; - } - if (*fields->errmsg && !qi->qf_multiignore) { - size_t len = STRLEN(qfprev->qf_text); - qfprev->qf_text = xrealloc(qfprev->qf_text, - len + STRLEN(fields->errmsg) + 2); - qfprev->qf_text[len] = '\n'; - STRCPY(qfprev->qf_text + len + 1, fields->errmsg); - } - if (qfprev->qf_nr == -1) { - qfprev->qf_nr = fields->enr; - } - if (vim_isprintc(fields->type) && !qfprev->qf_type) { - qfprev->qf_type = fields->type; // only printable chars allowed - } - if (!qfprev->qf_lnum) { - qfprev->qf_lnum = fields->lnum; - } - if (!qfprev->qf_col) { - qfprev->qf_col = fields->col; - } - qfprev->qf_viscol = fields->use_viscol; - if (!qfprev->qf_fnum) { - qfprev->qf_fnum = qf_get_fnum(qi, qi->qf_directory, - *fields->namebuf || qi->qf_directory - ? fields->namebuf - : qi->qf_currfile && fields->valid - ? qi->qf_currfile : 0); + if (!qi->qf_multiignore) { + qfline_T *qfprev = qi->qf_lists[qi->qf_curlist].qf_last; + if (qfprev == NULL) { + return QF_FAIL; + } + if (*fields->errmsg && !qi->qf_multiignore) { + size_t len = STRLEN(qfprev->qf_text); + qfprev->qf_text = xrealloc(qfprev->qf_text, + len + STRLEN(fields->errmsg) + 2); + qfprev->qf_text[len] = '\n'; + STRCPY(qfprev->qf_text + len + 1, fields->errmsg); + } + if (qfprev->qf_nr == -1) { + qfprev->qf_nr = fields->enr; + } + if (vim_isprintc(fields->type) && !qfprev->qf_type) { + qfprev->qf_type = fields->type; // only printable chars allowed + } + if (!qfprev->qf_lnum) { + qfprev->qf_lnum = fields->lnum; + } + if (!qfprev->qf_col) { + qfprev->qf_col = fields->col; + } + qfprev->qf_viscol = fields->use_viscol; + if (!qfprev->qf_fnum) { + qfprev->qf_fnum = qf_get_fnum(qi, qi->qf_directory, + *fields->namebuf || qi->qf_directory + ? fields->namebuf + : qi->qf_currfile && fields->valid + ? qi->qf_currfile : 0); + } } if (idx == 'Z') { qi->qf_multiline = qi->qf_multiignore = false; @@ -957,16 +1003,17 @@ qf_init_ext( buf_T *buf, typval_T *tv, char_u *errorformat, - int newlist, /* TRUE: start a new error list */ - linenr_T lnumfirst, /* first line number to use */ - linenr_T lnumlast, /* last line number to use */ - char_u *qf_title + int newlist, // TRUE: start a new error list + linenr_T lnumfirst, // first line number to use + linenr_T lnumlast, // last line number to use + char_u *qf_title, + char_u *enc ) { - qfstate_T state = { NULL, 0, NULL, 0, NULL, NULL, NULL, NULL, - NULL, 0, 0 }; - qffields_T fields = { NULL, NULL, 0, 0L, 0, false, NULL, 0, 0, 0 }; + qfstate_T state; + qffields_T fields; qfline_T *old_last = NULL; + bool adding = false; static efm_T *fmt_first = NULL; char_u *efm; static char_u *last_efm = NULL; @@ -977,6 +1024,13 @@ qf_init_ext( xfree(qf_last_bufname); qf_last_bufname = NULL; + memset(&state, 0, sizeof(state)); + memset(&fields, 0, sizeof(fields)); + state.vc.vc_type = CONV_NONE; + if (enc != NULL && *enc != NUL) { + convert_setup(&state.vc, enc, p_enc); + } + fields.namebuf = xmalloc(CMDBUFFSIZE + 1); fields.errmsglen = CMDBUFFSIZE + 1; fields.errmsg = xmalloc(fields.errmsglen); @@ -992,6 +1046,7 @@ qf_init_ext( qf_new_list(qi, qf_title); } else if (qi->qf_lists[qi->qf_curlist].qf_count > 0) { // Adding to existing list, use last entry. + adding = true; old_last = qi->qf_lists[qi->qf_curlist].qf_last; } @@ -1043,7 +1098,8 @@ qf_init_ext( if (tv->v_type == VAR_STRING) { state.p_str = tv->vval.v_string; } else if (tv->v_type == VAR_LIST) { - state.p_li = tv->vval.v_list->lv_first; + state.p_list = tv->vval.v_list; + state.p_li = tv_list_first(tv->vval.v_list); } state.tv = tv; } @@ -1072,6 +1128,7 @@ qf_init_ext( } if (qf_add_entry(qi, + qi->qf_curlist, qi->qf_directory, (*fields.namebuf || qi->qf_directory) ? fields.namebuf : ((qi->qf_currfile && fields.valid) @@ -1108,10 +1165,12 @@ qf_init_ext( } EMSG(_(e_readerrf)); error2: - qf_free(qi, qi->qf_curlist); - qi->qf_listcount--; - if (qi->qf_curlist > 0) { - qi->qf_curlist--; + if (!adding) { + qf_free(qi, qi->qf_curlist); + qi->qf_listcount--; + if (qi->qf_curlist > 0) { + qi->qf_curlist--; + } } qf_init_end: if (state.fd != NULL) { @@ -1124,16 +1183,24 @@ qf_init_end: qf_update_buffer(qi, old_last); + if (state.vc.vc_type != CONV_NONE) { + convert_setup(&state.vc, NULL, NULL); + } + return retval; } -static void qf_store_title(qf_info_T *qi, char_u *title) +static void qf_store_title(qf_info_T *qi, int qf_idx, char_u *title) { + xfree(qi->qf_lists[qf_idx].qf_title); + qi->qf_lists[qf_idx].qf_title = NULL; + if (title != NULL) { - char_u *p = xmalloc(STRLEN(title) + 2); + size_t len = STRLEN(title) + 1; + char_u *p = xmallocz(len); - qi->qf_lists[qi->qf_curlist].qf_title = p; - sprintf((char *)p, ":%s", (char *)title); + qi->qf_lists[qf_idx].qf_title = p; + snprintf((char *)p, len + 1, ":%s", (char *)title); } } @@ -1162,7 +1229,7 @@ static void qf_new_list(qf_info_T *qi, char_u *qf_title) } else qi->qf_curlist = qi->qf_listcount++; memset(&qi->qf_lists[qi->qf_curlist], 0, (size_t)(sizeof(qf_list_T))); - qf_store_title(qi, qf_title); + qf_store_title(qi, qi->qf_curlist, qf_title); } /* @@ -1205,6 +1272,7 @@ void qf_free_all(win_T *wp) /// Add an entry to the end of the list of errors. /// /// @param qi quickfix list +/// @param qf_idx list index /// @param dir optional directory name /// @param fname file name or NULL /// @param bufnum buffer number or zero @@ -1218,9 +1286,10 @@ void qf_free_all(win_T *wp) /// @param valid valid entry /// /// @returns OK or FAIL. -static int qf_add_entry(qf_info_T *qi, 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) +static int qf_add_entry(qf_info_T *qi, int qf_idx, 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)); qfline_T **lastp; // pointer to qf_last or NULL @@ -1251,12 +1320,12 @@ static int qf_add_entry(qf_info_T *qi, char_u *dir, char_u *fname, int bufnum, qfp->qf_type = (char_u)type; qfp->qf_valid = valid; - lastp = &qi->qf_lists[qi->qf_curlist].qf_last; - if (qi->qf_lists[qi->qf_curlist].qf_count == 0) { - /* first element in the list */ - qi->qf_lists[qi->qf_curlist].qf_start = qfp; - qi->qf_lists[qi->qf_curlist].qf_ptr = qfp; - qi->qf_lists[qi->qf_curlist].qf_index = 0; + lastp = &qi->qf_lists[qf_idx].qf_last; + if (qi->qf_lists[qf_idx].qf_count == 0) { + // first element in the list + qi->qf_lists[qf_idx].qf_start = qfp; + qi->qf_lists[qf_idx].qf_ptr = qfp; + qi->qf_lists[qf_idx].qf_index = 0; qfp->qf_prev = NULL; } else { assert(*lastp); @@ -1266,12 +1335,11 @@ static int qf_add_entry(qf_info_T *qi, char_u *dir, char_u *fname, int bufnum, qfp->qf_next = NULL; qfp->qf_cleared = false; *lastp = qfp; - qi->qf_lists[qi->qf_curlist].qf_count++; - if (qi->qf_lists[qi->qf_curlist].qf_index == 0 && qfp->qf_valid) { - /* first valid entry */ - qi->qf_lists[qi->qf_curlist].qf_index = - qi->qf_lists[qi->qf_curlist].qf_count; - qi->qf_lists[qi->qf_curlist].qf_ptr = qfp; + qi->qf_lists[qf_idx].qf_count++; + if (qi->qf_lists[qf_idx].qf_index == 0 && qfp->qf_valid) { + // first valid entry + qi->qf_lists[qf_idx].qf_index = qi->qf_lists[qf_idx].qf_count; + qi->qf_lists[qf_idx].qf_ptr = qfp; } return OK; @@ -1358,6 +1426,13 @@ void copy_loclist(win_T *from, win_T *to) else to_qfl->qf_title = NULL; + if (from_qfl->qf_ctx != NULL) { + to_qfl->qf_ctx = xcalloc(1, sizeof(typval_T)); + tv_copy(from_qfl->qf_ctx, to_qfl->qf_ctx); + } else { + to_qfl->qf_ctx = NULL; + } + if (from_qfl->qf_count) { qfline_T *from_qfp; qfline_T *prevp; @@ -1367,6 +1442,7 @@ void copy_loclist(win_T *from, win_T *to) i < from_qfl->qf_count && from_qfp != NULL; i++, from_qfp = from_qfp->qf_next) { if (qf_add_entry(to->w_llist, + to->w_llist->qf_curlist, NULL, NULL, 0, @@ -1408,7 +1484,7 @@ void copy_loclist(win_T *from, win_T *to) to->w_llist->qf_curlist = qi->qf_curlist; /* current list */ } -// Get buffer number for file "directory.fname". +// Get buffer number for file "directory/fname". // Also sets the b_has_qf_entry flag. static int qf_get_fnum(qf_info_T *qi, char_u *directory, char_u *fname) { @@ -1869,7 +1945,7 @@ win_found: * If there is only one window and it is the quickfix window, create a * new one above the quickfix window. */ - if (((firstwin == lastwin) && bt_quickfix(curbuf)) || !usable_win) { + if ((ONE_WINDOW && bt_quickfix(curbuf)) || !usable_win) { flags = WSP_ABOVE; if (ll_ref != NULL) flags |= WSP_NEWLOC; @@ -2330,8 +2406,9 @@ void qf_history(exarg_T *eap) } } -/// Free all the entries in the error list "idx". -static void qf_free(qf_info_T *qi, int idx) +/// Free all the entries in the error list "idx". Note that other information +/// associated with the list like context and title are not freed. +static void qf_free_items(qf_info_T *qi, int idx) { qfline_T *qfp; qfline_T *qfpnext; @@ -2355,20 +2432,41 @@ static void qf_free(qf_info_T *qi, int idx) qi->qf_lists[idx].qf_start = qfpnext; 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; + qi->qf_lists[idx].qf_start = NULL; + qi->qf_lists[idx].qf_last = NULL; + qi->qf_lists[idx].qf_ptr = NULL; + qi->qf_lists[idx].qf_nonevalid = true; qf_clean_dir_stack(&qi->qf_dir_stack); + qi->qf_directory = NULL; qf_clean_dir_stack(&qi->qf_file_stack); + qi->qf_currfile = NULL; + qi->qf_multiline = false; + qi->qf_multiignore = false; + qi->qf_multiscan = false; +} + +/// Free error list "idx". Frees all the entries in the quickfix list, +/// associated context information and the title. +static void qf_free(qf_info_T *qi, int idx) +{ + qf_free_items(qi, idx); + + xfree(qi->qf_lists[idx].qf_title); + qi->qf_lists[idx].qf_title = NULL; + tv_free(qi->qf_lists[idx].qf_ctx); + qi->qf_lists[idx].qf_ctx = NULL; } /* * qf_mark_adjust: adjust marks */ -void qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, long amount, long amount_after) +bool qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, long amount, + long amount_after) { int i; qfline_T *qfp; @@ -2378,11 +2476,12 @@ void qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, long amount, long int buf_has_flag = wp == NULL ? BUF_HAS_QF_ENTRY : BUF_HAS_LL_ENTRY; if (!(curbuf->b_has_qf_entry & buf_has_flag)) { - return; + return false; } if (wp != NULL) { - if (wp->w_llist == NULL) - return; + if (wp->w_llist == NULL) { + return false; + } qi = wp->w_llist; } @@ -2403,9 +2502,7 @@ void qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, long amount, long } } - if (!found_one) { - curbuf->b_has_qf_entry &= ~buf_has_flag; - } + return found_one; } /* @@ -2828,7 +2925,7 @@ static void qf_fill_buffer(qf_info_T *qi, buf_T *buf, qfline_T *old_last) if (old_last == NULL) { if (buf != curbuf) { - EMSG2(_(e_intern2), "qf_fill_buffer()"); + internal_error("qf_fill_buffer()"); return; } @@ -2999,6 +3096,7 @@ void ex_make(exarg_T *eap) qf_info_T *qi = &ql_info; int res; char_u *au_name = NULL; + char_u *enc = (*curbuf->b_p_menc != NUL) ? curbuf->b_p_menc : p_menc; /* Redirect ":grep" to ":vimgrep" if 'grepprg' is "internal". */ if (grep_internal(eap->cmdidx)) { @@ -3050,19 +3148,18 @@ void ex_make(exarg_T *eap) } msg_start(); MSG_PUTS(":!"); - msg_outtrans((char_u *) cmd); // show what we are doing + msg_outtrans((char_u *)cmd); // show what we are doing - // let the shell know if we are redirecting output or not - do_shell((char_u *) cmd, *p_sp != NUL ? kShellOptDoOut : 0); + do_shell((char_u *)cmd, 0); res = qf_init(wp, fname, (eap->cmdidx != CMD_make && eap->cmdidx != CMD_lmake) ? p_gefm : p_efm, - (eap->cmdidx != CMD_grepadd - && eap->cmdidx != CMD_lgrepadd), - *eap->cmdlinep); - if (wp != NULL) + (eap->cmdidx != CMD_grepadd && eap->cmdidx != CMD_lgrepadd), + *eap->cmdlinep, enc); + if (wp != NULL) { qi = GET_LOC_LIST(wp); + } if (au_name != NULL) { apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, curbuf->b_fname, TRUE, curbuf); @@ -3116,7 +3213,7 @@ static char_u *get_mef_name(void) STRCPY(name, p_mef); sprintf((char *)name + (p - p_mef), "%d%d", start, off); STRCAT(name, p + 2); - // Don't accept a symbolic link, its a security risk. + // Don't accept a symbolic link, it's a security risk. FileInfo file_info; bool file_or_link_found = os_fileinfo_link((char *)name, &file_info); if (!file_or_link_found) { @@ -3404,19 +3501,18 @@ void ex_cfile(exarg_T *eap) if (*eap->arg != NUL) set_string_option_direct((char_u *)"ef", -1, eap->arg, OPT_FREE, 0); - /* - * This function is used by the :cfile, :cgetfile and :caddfile - * commands. - * :cfile always creates a new quickfix list and jumps to the - * first error. - * :cgetfile creates a new quickfix list but doesn't jump to the - * first error. - * :caddfile adds to an existing quickfix list. If there is no - * quickfix list then a new list is created. - */ + char_u *enc = (*curbuf->b_p_menc != NUL) ? curbuf->b_p_menc : p_menc; + // This function is used by the :cfile, :cgetfile and :caddfile + // commands. + // :cfile always creates a new quickfix list and jumps to the + // first error. + // :cgetfile creates a new quickfix list but doesn't jump to the + // first error. + // :caddfile adds to an existing quickfix list. If there is no + // quickfix list then a new list is created. if (qf_init(wp, p_ef, p_efm, (eap->cmdidx != CMD_caddfile && eap->cmdidx != CMD_laddfile), - *eap->cmdlinep) > 0 + *eap->cmdlinep, enc) > 0 && (eap->cmdidx == CMD_cfile || eap->cmdidx == CMD_lfile)) { if (au_name != NULL) @@ -3553,8 +3649,8 @@ void ex_vimgrep(exarg_T *eap) cur_qf_start = qi->qf_lists[qi->qf_curlist].qf_start; seconds = (time_t)0; - for (fi = 0; fi < fcount && !got_int && tomatch > 0; ++fi) { - fname = path_shorten_fname_if_possible(fnames[fi]); + for (fi = 0; fi < fcount && !got_int && tomatch > 0; fi++) { + fname = path_try_shorten_fname(fnames[fi]); if (time(NULL) > seconds) { /* Display the file name every second or so, show the user we are * working on it. */ @@ -3631,6 +3727,7 @@ void ex_vimgrep(exarg_T *eap) // dummy buffer, unless duplicate_name is set, then the // buffer will be wiped out below. if (qf_add_entry(qi, + qi->qf_curlist, NULL, // dir fname, duplicate_name ? 0 : buf->b_fnum, @@ -3812,6 +3909,7 @@ load_dummy_buffer ( bufref_T newbuf_to_wipe; int failed = true; aco_save_T aco; + int readfile_result; // Allocate a buffer without putting it in the buffer list. newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY); @@ -3825,7 +3923,9 @@ load_dummy_buffer ( /* need to open the memfile before putting the buffer in a window */ if (ml_open(newbuf) == OK) { - /* set curwin/curbuf to buf and save a few things */ + // Make sure this buffer isn't wiped out by auto commands. + newbuf->b_locked++; + // set curwin/curbuf to buf and save a few things aucmd_prepbuf(&aco, newbuf); /* Need to set the filename for autocommands. */ @@ -3839,9 +3939,11 @@ load_dummy_buffer ( curbuf->b_flags &= ~BF_DUMMY; newbuf_to_wipe.br_buf = NULL; - if (readfile(fname, NULL, - (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, - NULL, READ_NEW | READ_DUMMY) == OK + readfile_result = readfile(fname, NULL, (linenr_T)0, (linenr_T)0, + (linenr_T)MAXLNUM, NULL, + READ_NEW | READ_DUMMY); + newbuf->b_locked--; + if (readfile_result == OK && !got_int && !(curbuf->b_flags & BF_NEW)) { failed = FALSE; @@ -4000,6 +4102,7 @@ enum { QF_GETLIST_ITEMS = 0x2, QF_GETLIST_NR = 0x4, QF_GETLIST_WINID = 0x8, + QF_GETLIST_CONTEXT = 0x10, QF_GETLIST_ALL = 0xFF }; @@ -4009,42 +4112,72 @@ enum { int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict) { qf_info_T *qi = &ql_info; + dictitem_T *di; if (wp != NULL) { qi = GET_LOC_LIST(wp); if (qi == NULL) { + // If querying for the size of the location list, return 0 + if (((di = tv_dict_find(what, S_LEN("nr"))) != NULL) + && (di->di_tv.v_type == VAR_STRING) + && strequal((const char *)di->di_tv.vval.v_string, "$")) { + return tv_dict_add_nr(retdict, S_LEN("nr"), 0); + } return FAIL; } } int status = OK; - dictitem_T *di; int flags = QF_GETLIST_NONE; int qf_idx = qi->qf_curlist; // default is the current list if ((di = tv_dict_find(what, S_LEN("nr"))) != NULL) { // Use the specified quickfix/location list if (di->di_tv.v_type == VAR_NUMBER) { - qf_idx = (int)di->di_tv.vval.v_number - 1; - if (qf_idx < 0 || qf_idx >= qi->qf_listcount) { + // for zero use the current list + if (di->di_tv.vval.v_number != 0) { + qf_idx = (int)di->di_tv.vval.v_number - 1; + if (qf_idx < 0 || qf_idx >= qi->qf_listcount) { + return FAIL; + } + } else if (qi->qf_listcount == 0) { // stack is empty return FAIL; } flags |= QF_GETLIST_NR; + } else if (di->di_tv.v_type == VAR_STRING + && strequal((const char *)di->di_tv.vval.v_string, "$")) { + // Get the last quickfix list number + if (qi->qf_listcount > 0) { + qf_idx = qi->qf_listcount - 1; + } else { + qf_idx = -1; // Quickfix stack is empty + } + flags |= QF_GETLIST_NR; } else { return FAIL; } } - if (tv_dict_find(what, S_LEN("all")) != NULL) { - flags |= QF_GETLIST_ALL; - } + if (qf_idx != -1) { + if (tv_dict_find(what, S_LEN("all")) != NULL) { + flags |= QF_GETLIST_ALL; + } - if (tv_dict_find(what, S_LEN("title")) != NULL) { - flags |= QF_GETLIST_TITLE; - } + if (tv_dict_find(what, S_LEN("title")) != NULL) { + flags |= QF_GETLIST_TITLE; + } + + if (tv_dict_find(what, S_LEN("winid")) != NULL) { + flags |= QF_GETLIST_WINID; + } + + if (tv_dict_find(what, S_LEN("context")) != NULL) { + flags |= QF_GETLIST_CONTEXT; + } - if (tv_dict_find(what, S_LEN("winid")) != NULL) { - flags |= QF_GETLIST_WINID; + if (tv_dict_find(what, S_LEN("items")) != NULL) { + flags |= QF_GETLIST_ITEMS; + } } if (flags & QF_GETLIST_TITLE) { @@ -4063,39 +4196,60 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict) status = tv_dict_add_nr(retdict, S_LEN("winid"), win->handle); } } + if ((status == OK) && (flags & QF_GETLIST_ITEMS)) { + list_T *l = tv_list_alloc(kListLenMayKnow); + (void)get_errorlist(wp, qf_idx, l); + tv_dict_add_list(retdict, S_LEN("items"), l); + } + + if ((status == OK) && (flags & QF_GETLIST_CONTEXT)) { + if (qi->qf_lists[qf_idx].qf_ctx != NULL) { + di = tv_dict_item_alloc_len(S_LEN("context")); + if (di != NULL) { + tv_copy(qi->qf_lists[qf_idx].qf_ctx, &di->di_tv); + if (tv_dict_add(retdict, di) == FAIL) { + tv_dict_item_free(di); + } + } + } else { + status = tv_dict_add_str(retdict, S_LEN("context"), ""); + } + } return status; } /// Add list of entries to quickfix/location list. Each list entry is /// a dictionary with item information. -static int qf_add_entries(qf_info_T *qi, list_T *list, char_u *title, - int action) +static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list, + char_u *title, int action) { - listitem_T *li; dict_T *d; qfline_T *old_last = NULL; int retval = OK; bool did_bufnr_emsg = false; - if (action == ' ' || qi->qf_curlist == qi->qf_listcount) { + if (action == ' ' || qf_idx == qi->qf_listcount) { // make place for a new list qf_new_list(qi, title); - } else if (action == 'a' && qi->qf_lists[qi->qf_curlist].qf_count > 0) { + qf_idx = qi->qf_curlist; + } else if (action == 'a' && qi->qf_lists[qf_idx].qf_count > 0) { // Adding to existing list, use last entry. - old_last = qi->qf_lists[qi->qf_curlist].qf_last; + old_last = qi->qf_lists[qf_idx].qf_last; } else if (action == 'r') { - qf_free(qi, qi->qf_curlist); - qf_store_title(qi, title); + qf_free_items(qi, qf_idx); + qf_store_title(qi, qf_idx, title); } - for (li = list->lv_first; li != NULL; li = li->li_next) { - if (li->li_tv.v_type != VAR_DICT) - continue; /* Skip non-dict items */ + TV_LIST_ITER_CONST(list, li, { + if (TV_LIST_ITEM_TV(li)->v_type != VAR_DICT) { + continue; // Skip non-dict items. + } - d = li->li_tv.vval.v_dict; - if (d == NULL) + d = TV_LIST_ITEM_TV(li)->vval.v_dict; + if (d == NULL) { continue; + } char *const filename = tv_dict_get_string(d, "filename", true); int bufnum = (int)tv_dict_get_number(d, "bufnr"); @@ -4126,7 +4280,13 @@ static int qf_add_entries(qf_info_T *qi, list_T *list, char_u *title, bufnum = 0; } + // If the 'valid' field is present it overrules the detected value. + if (tv_dict_find(d, "valid", -1) != NULL) { + valid = (int)tv_dict_get_number(d, "valid"); + } + int status = qf_add_entry(qi, + qf_idx, NULL, // dir (char_u *)filename, bufnum, @@ -4147,18 +4307,18 @@ static int qf_add_entries(qf_info_T *qi, list_T *list, char_u *title, retval = FAIL; break; } - } + }); - if (qi->qf_lists[qi->qf_curlist].qf_index == 0) { + if (qi->qf_lists[qf_idx].qf_index == 0) { // no valid entry - qi->qf_lists[qi->qf_curlist].qf_nonevalid = true; + qi->qf_lists[qf_idx].qf_nonevalid = true; } else { - qi->qf_lists[qi->qf_curlist].qf_nonevalid = false; + qi->qf_lists[qf_idx].qf_nonevalid = false; } if (action != 'a') { - qi->qf_lists[qi->qf_curlist].qf_ptr = qi->qf_lists[qi->qf_curlist].qf_start; - if (qi->qf_lists[qi->qf_curlist].qf_count > 0) { - qi->qf_lists[qi->qf_curlist].qf_index = 1; + qi->qf_lists[qf_idx].qf_ptr = qi->qf_lists[qf_idx].qf_start; + if (qi->qf_lists[qf_idx].qf_count > 0) { + qi->qf_lists[qf_idx].qf_index = 1; } } @@ -4181,14 +4341,28 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action) if ((di = tv_dict_find(what, S_LEN("nr"))) != NULL) { // Use the specified quickfix/location list if (di->di_tv.v_type == VAR_NUMBER) { - qf_idx = (int)di->di_tv.vval.v_number - 1; - if (qf_idx < 0 || qf_idx >= qi->qf_listcount) { + // for zero use the current list + if (di->di_tv.vval.v_number != 0) { + qf_idx = (int)di->di_tv.vval.v_number - 1; + } + + if ((action == ' ' || action == 'a') && qf_idx == qi->qf_listcount) { + // When creating a new list, accept qf_idx pointing to the next + // non-available list + newlist = true; + } else if (qf_idx < 0 || qf_idx >= qi->qf_listcount) { return FAIL; + } else { + newlist = false; // use the specified list } + } else if (di->di_tv.v_type == VAR_STRING + && strequal((const char *)di->di_tv.vval.v_string, "$") + && qi->qf_listcount > 0) { + qf_idx = qi->qf_listcount - 1; + newlist = false; } else { return FAIL; } - newlist = false; // use the specified list } if (newlist) { @@ -4207,10 +4381,85 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action) retval = OK; } } + if ((di = tv_dict_find(what, S_LEN("items"))) != NULL) { + if (di->di_tv.v_type == VAR_LIST) { + char_u *title_save = vim_strsave(qi->qf_lists[qf_idx].qf_title); + + retval = qf_add_entries(qi, qf_idx, di->di_tv.vval.v_list, + title_save, action == ' ' ? 'a' : action); + xfree(title_save); + } + } + + if ((di = tv_dict_find(what, S_LEN("context"))) != NULL) { + tv_free(qi->qf_lists[qf_idx].qf_ctx); + + typval_T *ctx = xcalloc(1, sizeof(typval_T)); + tv_copy(&di->di_tv, ctx); + qi->qf_lists[qf_idx].qf_ctx = ctx; + } return retval; } +// Find the non-location list window with the specified location list. +static win_T * find_win_with_ll(qf_info_T *qi) +{ + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if ((wp->w_llist == qi) && !bt_quickfix(wp->w_buffer)) { + return wp; + } + } + + return NULL; +} + +// Free the entire quickfix/location list stack. +// If the quickfix/location list window is open, then clear it. +static void qf_free_stack(win_T *wp, qf_info_T *qi) +{ + win_T *qfwin = qf_find_win(qi); + + if (qfwin != NULL) { + // If the quickfix/location list window is open, then clear it + if (qi->qf_curlist < qi->qf_listcount) { + qf_free(qi, qi->qf_curlist); + } + qf_update_buffer(qi, NULL); + } + + win_T *llwin = NULL; + win_T *orig_wp = wp; + if (wp != NULL && IS_LL_WINDOW(wp)) { + // If in the location list window, then use the non-location list + // window with this location list (if present) + llwin = find_win_with_ll(qi); + if (llwin != NULL) { + wp = llwin; + } + } + + qf_free_all(wp); + if (wp == NULL) { + // quickfix list + qi->qf_curlist = 0; + qi->qf_listcount = 0; + } else if (IS_LL_WINDOW(orig_wp)) { + // If the location list window is open, then create a new empty location + // list + qf_info_T *new_ll = ll_new_list(); + + // first free the list reference in the location list window + ll_free_all(&orig_wp->w_llist_ref); + + orig_wp->w_llist_ref = new_ll; + if (llwin != NULL) { + llwin->w_llist = new_ll; + new_ll->qf_refcount++; + } + } +} + // Populate the quickfix list with the items supplied in the list // of dictionaries. "title" will be copied to w:quickfix_title // "action" is 'a' for add, 'r' for replace. Otherwise create a new list. @@ -4224,15 +4473,54 @@ int set_errorlist(win_T *wp, list_T *list, int action, char_u *title, qi = ll_get_or_alloc_list(wp); } - if (what != NULL) { + if (action == 'f') { + // Free the entire quickfix or location list stack + qf_free_stack(wp, qi); + } else if (what != NULL) { retval = qf_set_properties(qi, what, action); } else { - retval = qf_add_entries(qi, list, title, action); + retval = qf_add_entries(qi, qi->qf_curlist, list, title, action); } return retval; } +static bool mark_quickfix_ctx(qf_info_T *qi, int copyID) +{ + bool abort = false; + + for (int i = 0; i < LISTCOUNT && !abort; i++) { + typval_T *ctx = qi->qf_lists[i].qf_ctx; + if (ctx != NULL && ctx->v_type != VAR_NUMBER + && ctx->v_type != VAR_STRING && ctx->v_type != VAR_FLOAT) { + abort = set_ref_in_item(ctx, copyID, NULL, NULL); + } + } + + return abort; +} + +/// Mark the context of the quickfix list and the location lists (if present) as +/// "in use". So that garabage collection doesn't free the context. +bool set_ref_in_quickfix(int copyID) +{ + bool abort = mark_quickfix_ctx(&ql_info, copyID); + if (abort) { + return abort; + } + + FOR_ALL_TAB_WINDOWS(tp, win) { + if (win->w_llist != NULL) { + abort = mark_quickfix_ctx(win->w_llist, copyID); + if (abort) { + return abort; + } + } + } + + return abort; +} + /* * ":[range]cbuffer [bufnr]" command. * ":[range]caddbuffer [bufnr]" command. @@ -4310,7 +4598,7 @@ void ex_cbuffer(exarg_T *eap) if (qf_init_ext(qi, NULL, buf, NULL, p_efm, (eap->cmdidx != CMD_caddbuffer && eap->cmdidx != CMD_laddbuffer), - eap->line1, eap->line2, qf_title) > 0) { + eap->line1, eap->line2, qf_title, NULL) > 0) { if (au_name != NULL) { apply_autocmds(EVENT_QUICKFIXCMDPOST, (char_u *)au_name, curbuf->b_fname, true, curbuf); @@ -4371,11 +4659,11 @@ void ex_cexpr(exarg_T *eap) typval_T tv; if (eval0(eap->arg, &tv, NULL, true) != FAIL) { if ((tv.v_type == VAR_STRING && tv.vval.v_string != NULL) - || (tv.v_type == VAR_LIST && tv.vval.v_list != NULL)) { + || tv.v_type == VAR_LIST) { if (qf_init_ext(qi, NULL, NULL, &tv, p_efm, (eap->cmdidx != CMD_caddexpr && eap->cmdidx != CMD_laddexpr), - (linenr_T)0, (linenr_T)0, *eap->cmdlinep) > 0) { + (linenr_T)0, (linenr_T)0, *eap->cmdlinep, NULL) > 0) { if (au_name != NULL) { apply_autocmds(EVENT_QUICKFIXCMDPOST, (char_u *)au_name, curbuf->b_fname, true, curbuf); @@ -4446,6 +4734,9 @@ void ex_helpgrep(exarg_T *eap) } } + // Autocommands may change the list. Save it for later comparison + qf_info_T *save_qi = qi; + regmatch.regprog = vim_regcomp(eap->arg, RE_MAGIC + RE_STRING); regmatch.rm_ic = FALSE; if (regmatch.regprog != NULL) { @@ -4506,6 +4797,7 @@ void ex_helpgrep(exarg_T *eap) line[--l] = NUL; if (qf_add_entry(qi, + qi->qf_curlist, NULL, // dir fnames[fi], 0, @@ -4558,10 +4850,11 @@ void ex_helpgrep(exarg_T *eap) if (au_name != NULL) { apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, - curbuf->b_fname, TRUE, curbuf); - if (!new_qi && qi != &ql_info && qf_find_buf(qi) == NULL) - /* autocommands made "qi" invalid */ + curbuf->b_fname, true, curbuf); + if (!new_qi && qi != save_qi && qf_find_buf(qi) == NULL) { + // autocommands made "qi" invalid return; + } } /* Jump to first match. */ diff --git a/src/nvim/rbuffer.c b/src/nvim/rbuffer.c index 18d453cbe9..df9394fbb2 100644 --- a/src/nvim/rbuffer.c +++ b/src/nvim/rbuffer.c @@ -121,7 +121,7 @@ char *rbuffer_read_ptr(RBuffer *buf, size_t *read_count) FUNC_ATTR_NONNULL_ALL { if (!buf->size) { *read_count = 0; - return NULL; + return buf->read_ptr; } if (buf->read_ptr < buf->write_ptr) { diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 5448cc7131..e4de43b49e 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -232,17 +232,17 @@ #define LAST_NL NUPPER + ADD_NL #define WITH_NL(op) ((op) >= FIRST_NL && (op) <= LAST_NL) -#define MOPEN 80 /* -89 Mark this point in input as start of - * \( subexpr. MOPEN + 0 marks start of - * match. */ -#define MCLOSE 90 /* -99 Analogous to MOPEN. MCLOSE + 0 marks - * end of match. */ -#define BACKREF 100 /* -109 node Match same string again \1-\9 */ - -# define ZOPEN 110 /* -119 Mark this point in input as start of - * \z( subexpr. */ -# define ZCLOSE 120 /* -129 Analogous to ZOPEN. */ -# define ZREF 130 /* -139 node Match external submatch \z1-\z9 */ +#define MOPEN 80 // -89 Mark this point in input as start of + // \( … \) subexpr. MOPEN + 0 marks start of + // match. +#define MCLOSE 90 // -99 Analogous to MOPEN. MCLOSE + 0 marks + // end of match. +#define BACKREF 100 // -109 node Match same string again \1-\9. + +# define ZOPEN 110 // -119 Mark this point in input as start of + // \z( … \) subexpr. +# define ZCLOSE 120 // -129 Analogous to ZOPEN. +# define ZREF 130 // -139 node Match external submatch \z1-\z9 #define BRACE_COMPLEX 140 /* -149 node Match nodes between m & n times */ @@ -458,18 +458,15 @@ static int toggle_Magic(int x) /* Used for an error (down from) vim_regcomp(): give the error message, set * rc_did_emsg and return NULL */ -#define EMSG_RET_NULL(m) return (EMSG(m), rc_did_emsg = TRUE, (void *)NULL) -#define EMSG_RET_FAIL(m) return (EMSG(m), rc_did_emsg = TRUE, FAIL) -#define EMSG2_RET_NULL(m, \ - c) return (EMSG2((m), \ - (c) ? "" : "\\"), rc_did_emsg = TRUE, \ - (void *)NULL) -#define EMSG2_RET_FAIL(m, \ - c) return (EMSG2((m), \ - (c) ? "" : "\\"), rc_did_emsg = TRUE, \ - FAIL) +#define EMSG_RET_NULL(m) return (EMSG(m), rc_did_emsg = true, (void *)NULL) +#define IEMSG_RET_NULL(m) return (IEMSG(m), rc_did_emsg = true, (void *)NULL) +#define EMSG_RET_FAIL(m) return (EMSG(m), rc_did_emsg = true, FAIL) +#define EMSG2_RET_NULL(m, c) \ + return (EMSG2((m), (c) ? "" : "\\"), rc_did_emsg = true, (void *)NULL) +#define EMSG2_RET_FAIL(m, c) \ + return (EMSG2((m), (c) ? "" : "\\"), rc_did_emsg = true, FAIL) #define EMSG_ONE_RET_NULL EMSG2_RET_NULL(_( \ - "E369: invalid item in %s%%[]"), reg_magic == MAGIC_ALL) + "E369: invalid item in %s%%[]"), reg_magic == MAGIC_ALL) #define MAX_LIMIT (32767L << 16L) @@ -1891,8 +1888,8 @@ static char_u *regatom(int *flagp) case Magic(')'): if (one_exactly) EMSG_ONE_RET_NULL; - EMSG_RET_NULL(_(e_internal)); /* Supposed to be caught earlier. */ - /* NOTREACHED */ + IEMSG_RET_NULL(_(e_internal)); // Supposed to be caught earlier. + // NOTREACHED case Magic('='): case Magic('?'): @@ -3172,61 +3169,56 @@ static int need_clear_zsubexpr = FALSE; /* extmatch subexpressions int regnarrate = 0; #endif -/* - * Internal copy of 'ignorecase'. It is set at each call to vim_regexec(). - * Normally it gets the value of "rm_ic" or "rmm_ic", but when the pattern - * contains '\c' or '\C' the value is overruled. - */ -static int ireg_ic; - -/* - * Similar to ireg_ic, but only for 'combining' characters. Set with \Z flag - * in the regexp. Defaults to false, always. - */ -static int ireg_icombine; - -/* - * Copy of "rmm_maxcol": maximum column to search for a match. Zero when - * there is no maximum. - */ -static colnr_T ireg_maxcol; - -/* - * Sometimes need to save a copy of a line. Since alloc()/free() is very - * slow, we keep one allocated piece of memory and only re-allocate it when - * it's too small. It's freed in bt_regexec_both() when finished. - */ +// Sometimes need to save a copy of a line. Since alloc()/free() is very +// slow, we keep one allocated piece of memory and only re-allocate it when +// it's too small. It's freed in bt_regexec_both() when finished. static char_u *reg_tofree = NULL; static unsigned reg_tofreelen; -/* - * These variables are set when executing a regexp to speed up the execution. - * Which ones are set depends on whether a single-line or multi-line match is - * done: - * single-line multi-line - * reg_match ®match_T NULL - * reg_mmatch NULL ®mmatch_T - * reg_startp reg_match->startp <invalid> - * reg_endp reg_match->endp <invalid> - * reg_startpos <invalid> reg_mmatch->startpos - * reg_endpos <invalid> reg_mmatch->endpos - * reg_win NULL window in which to search - * reg_buf curbuf buffer in which to search - * reg_firstlnum <invalid> first line in which to search - * reg_maxline 0 last line nr - * reg_line_lbr FALSE or TRUE FALSE - */ -static regmatch_T *reg_match; -static regmmatch_T *reg_mmatch; -static char_u **reg_startp = NULL; -static char_u **reg_endp = NULL; -static lpos_T *reg_startpos = NULL; -static lpos_T *reg_endpos = NULL; -static win_T *reg_win; -static buf_T *reg_buf; -static linenr_T reg_firstlnum; -static linenr_T reg_maxline; -static int reg_line_lbr; /* "\n" in string is line break */ +// Structure used to store the execution state of the regex engine. +// Which ones are set depends on whether a single-line or multi-line match is +// done: +// single-line multi-line +// reg_match ®match_T NULL +// reg_mmatch NULL ®mmatch_T +// reg_startp reg_match->startp <invalid> +// reg_endp reg_match->endp <invalid> +// reg_startpos <invalid> reg_mmatch->startpos +// reg_endpos <invalid> reg_mmatch->endpos +// reg_win NULL window in which to search +// reg_buf curbuf buffer in which to search +// reg_firstlnum <invalid> first line in which to search +// reg_maxline 0 last line nr +// reg_line_lbr false or true false +typedef struct { + regmatch_T *reg_match; + regmmatch_T *reg_mmatch; + char_u **reg_startp; + char_u **reg_endp; + lpos_T *reg_startpos; + lpos_T *reg_endpos; + win_T *reg_win; + buf_T *reg_buf; + linenr_T reg_firstlnum; + linenr_T reg_maxline; + bool reg_line_lbr; // "\n" in string is line break + + // Internal copy of 'ignorecase'. It is set at each call to vim_regexec(). + // Normally it gets the value of "rm_ic" or "rmm_ic", but when the pattern + // contains '\c' or '\C' the value is overruled. + bool reg_ic; + + // Similar to rex.reg_ic, but only for 'combining' characters. Set with \Z + // flag in the regexp. Defaults to false, always. + bool reg_icombine; + + // Copy of "rmm_maxcol": maximum column to search for a match. Zero when + // there is no maximum. + colnr_T reg_maxcol; +} regexec_T; + +static regexec_T rex; +static bool rex_in_use = false; /* * "regstack" and "backpos" are used by regmatch(). They are kept over calls @@ -3268,14 +3260,16 @@ void free_regexp_stuff(void) */ static char_u *reg_getline(linenr_T lnum) { - /* when looking behind for a match/no-match lnum is negative. But we - * can't go before line 1 */ - if (reg_firstlnum + lnum < 1) + // when looking behind for a match/no-match lnum is negative. But we + // can't go before line 1 + if (rex.reg_firstlnum + lnum < 1) { return NULL; - if (lnum > reg_maxline) - /* Must have matched the "\n" in the last line. */ + } + if (lnum > rex.reg_maxline) { + // Must have matched the "\n" in the last line. return (char_u *)""; - return ml_get_buf(reg_buf, reg_firstlnum + lnum, FALSE); + } + return ml_get_buf(rex.reg_buf, rex.reg_firstlnum + lnum, false); } static regsave_T behind_pos; @@ -3285,9 +3279,8 @@ static char_u *reg_endzp[NSUBEXP]; /* and end of \z(...\) matches */ static lpos_T reg_startzpos[NSUBEXP]; /* idem, beginning pos */ static lpos_T reg_endzpos[NSUBEXP]; /* idem, end pos */ -/* TRUE if using multi-line regexp. */ -#define REG_MULTI (reg_match == NULL) - +// TRUE if using multi-line regexp. +#define REG_MULTI (rex.reg_match == NULL) /* * Match a regexp against a string. @@ -3305,21 +3298,62 @@ bt_regexec_nl ( bool line_lbr ) { - reg_match = rmp; - reg_mmatch = NULL; - reg_maxline = 0; - reg_line_lbr = line_lbr; - reg_buf = curbuf; - reg_win = NULL; - ireg_ic = rmp->rm_ic; - ireg_icombine = FALSE; - ireg_maxcol = 0; + rex.reg_match = rmp; + rex.reg_mmatch = NULL; + rex.reg_maxline = 0; + rex.reg_line_lbr = line_lbr; + rex.reg_buf = curbuf; + rex.reg_win = NULL; + rex.reg_ic = rmp->rm_ic; + rex.reg_icombine = false; + rex.reg_maxcol = 0; long r = bt_regexec_both(line, col, NULL); assert(r <= INT_MAX); return (int)r; } +/// Wrapper around strchr which accounts for case-insensitive searches and +/// non-ASCII characters. +/// +/// This function is used a lot for simple searches, keep it fast! +/// +/// @param s string to search +/// @param c character to find in @a s +/// +/// @return NULL if no match, otherwise pointer to the position in @a s +static inline char_u *cstrchr(const char_u *const s, const int c) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_ALWAYS_INLINE +{ + if (!rex.reg_ic) { + return vim_strchr(s, c); + } + + // Use folded case for UTF-8, slow! For ASCII use libc strpbrk which is + // expected to be highly optimized. + if (c > 0x80) { + const int folded_c = utf_fold(c); + for (const char_u *p = s; *p != NUL; p += utfc_ptr2len(p)) { + if (utf_fold(utf_ptr2char(p)) == folded_c) { + return (char_u *)p; + } + } + return NULL; + } + + int cc; + if (ASCII_ISUPPER(c)) { + cc = TOLOWER_ASC(c); + } else if (ASCII_ISLOWER(c)) { + cc = TOUPPER_ASC(c); + } else { + return vim_strchr(s, c); + } + + char tofind[] = { (char)c, (char)cc, NUL }; + return (char_u *)strpbrk((const char *)s, tofind); +} /// Matches a regexp against multiple lines. /// "rmp->regprog" is a compiled regexp as returned by vim_regcomp(). @@ -3336,16 +3370,16 @@ bt_regexec_nl ( static long bt_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf, linenr_T lnum, colnr_T col, proftime_T *tm) { - reg_match = NULL; - reg_mmatch = rmp; - reg_buf = buf; - reg_win = win; - reg_firstlnum = lnum; - reg_maxline = reg_buf->b_ml.ml_line_count - lnum; - reg_line_lbr = FALSE; - ireg_ic = rmp->rmm_ic; - ireg_icombine = FALSE; - ireg_maxcol = rmp->rmm_maxcol; + rex.reg_match = NULL; + rex.reg_mmatch = rmp; + rex.reg_buf = buf; + rex.reg_win = win; + rex.reg_firstlnum = lnum; + rex.reg_maxline = rex.reg_buf->b_ml.ml_line_count - lnum; + rex.reg_line_lbr = false; + rex.reg_ic = rmp->rmm_ic; + rex.reg_icombine = false; + rex.reg_maxcol = rmp->rmm_maxcol; return bt_regexec_both(NULL, col, tm); } @@ -3383,14 +3417,14 @@ static long bt_regexec_both(char_u *line, } if (REG_MULTI) { - prog = (bt_regprog_T *)reg_mmatch->regprog; + prog = (bt_regprog_T *)rex.reg_mmatch->regprog; line = reg_getline((linenr_T)0); - reg_startpos = reg_mmatch->startpos; - reg_endpos = reg_mmatch->endpos; + rex.reg_startpos = rex.reg_mmatch->startpos; + rex.reg_endpos = rex.reg_mmatch->endpos; } else { - prog = (bt_regprog_T *)reg_match->regprog; - reg_startp = reg_match->startp; - reg_endp = reg_match->endp; + prog = (bt_regprog_T *)rex.reg_match->regprog; + rex.reg_startp = rex.reg_match->startp; + rex.reg_endp = rex.reg_match->endp; } /* Be paranoid... */ @@ -3403,19 +3437,22 @@ static long bt_regexec_both(char_u *line, if (prog_magic_wrong()) goto theend; - /* If the start column is past the maximum column: no need to try. */ - if (ireg_maxcol > 0 && col >= ireg_maxcol) + // If the start column is past the maximum column: no need to try. + if (rex.reg_maxcol > 0 && col >= rex.reg_maxcol) { goto theend; + } - /* If pattern contains "\c" or "\C": overrule value of ireg_ic */ - if (prog->regflags & RF_ICASE) - ireg_ic = TRUE; - else if (prog->regflags & RF_NOICASE) - ireg_ic = FALSE; + // If pattern contains "\c" or "\C": overrule value of rex.reg_ic + if (prog->regflags & RF_ICASE) { + rex.reg_ic = true; + } else if (prog->regflags & RF_NOICASE) { + rex.reg_ic = false; + } - /* If pattern contains "\Z" overrule value of ireg_icombine */ - if (prog->regflags & RF_ICOMBINE) - ireg_icombine = TRUE; + // If pattern contains "\Z" overrule value of rex.reg_icombine + if (prog->regflags & RF_ICOMBINE) { + rex.reg_icombine = true; + } /* If there is a "must appear" string, look for it. */ if (prog->regmust != NULL) { @@ -3429,7 +3466,7 @@ static long bt_regexec_both(char_u *line, // This is used very often, esp. for ":global". Use two versions of // the loop to avoid overhead of conditions. - if (!ireg_ic) { + if (!rex.reg_ic) { while ((s = vim_strchr(s, c)) != NULL) { if (cstrncmp(s, prog->regmust, &prog->regmlen) == 0) { break; // Found it. @@ -3463,7 +3500,7 @@ static long bt_regexec_both(char_u *line, c = regline[col]; if (prog->regstart == NUL || prog->regstart == c - || (ireg_ic + || (rex.reg_ic && (((enc_utf8 && utf_fold(prog->regstart) == utf_fold(c))) || (c < 255 && prog->regstart < 255 && mb_tolower(prog->regstart) == mb_tolower(c))))) { @@ -3485,8 +3522,8 @@ static long bt_regexec_both(char_u *line, col = (int)(s - regline); } - /* Check for maximum column to try. */ - if (ireg_maxcol > 0 && col >= ireg_maxcol) { + // Check for maximum column to try. + if (rex.reg_maxcol > 0 && col >= rex.reg_maxcol) { retval = 0; break; } @@ -3583,21 +3620,24 @@ static long regtry(bt_regprog_T *prog, colnr_T col) cleanup_subexpr(); if (REG_MULTI) { - if (reg_startpos[0].lnum < 0) { - reg_startpos[0].lnum = 0; - reg_startpos[0].col = col; + if (rex.reg_startpos[0].lnum < 0) { + rex.reg_startpos[0].lnum = 0; + rex.reg_startpos[0].col = col; + } + if (rex.reg_endpos[0].lnum < 0) { + rex.reg_endpos[0].lnum = reglnum; + rex.reg_endpos[0].col = (int)(reginput - regline); + } else { + // Use line number of "\ze". + reglnum = rex.reg_endpos[0].lnum; } - if (reg_endpos[0].lnum < 0) { - reg_endpos[0].lnum = reglnum; - reg_endpos[0].col = (int)(reginput - regline); - } else - /* Use line number of "\ze". */ - reglnum = reg_endpos[0].lnum; } else { - if (reg_startp[0] == NULL) - reg_startp[0] = regline + col; - if (reg_endp[0] == NULL) - reg_endp[0] = reginput; + if (rex.reg_startp[0] == NULL) { + rex.reg_startp[0] = regline + col; + } + if (rex.reg_endp[0] == NULL) { + rex.reg_endp[0] = reginput; + } } /* Package any found \z(...\) matches for export. Default is none. */ unref_extmatch(re_extmatch_out); @@ -3632,36 +3672,33 @@ static long regtry(bt_regprog_T *prog, colnr_T col) } -/* - * Get class of previous character. - */ +// Get class of previous character. static int reg_prev_class(void) { if (reginput > regline) { return mb_get_class_tab(reginput - 1 - (*mb_head_off)(regline, reginput - 1), - reg_buf->b_chartab); + rex.reg_buf->b_chartab); } return -1; } -/* - * Return TRUE if the current reginput position matches the Visual area. - */ +// Return TRUE if the current reginput position matches the Visual area. static int reg_match_visual(void) { pos_T top, bot; linenr_T lnum; colnr_T col; - win_T *wp = reg_win == NULL ? curwin : reg_win; + win_T *wp = rex.reg_win == NULL ? curwin : rex.reg_win; int mode; colnr_T start, end; colnr_T start2, end2; - /* Check if the buffer is the current buffer. */ - if (reg_buf != curbuf || VIsual.lnum == 0) - return FALSE; + // Check if the buffer is the current buffer. + if (rex.reg_buf != curbuf || VIsual.lnum == 0) { + return false; + } if (VIsual_active) { if (lt(VIsual, wp->w_cursor)) { @@ -3682,9 +3719,10 @@ static int reg_match_visual(void) } mode = curbuf->b_visual.vi_mode; } - lnum = reglnum + reg_firstlnum; - if (lnum < top.lnum || lnum > bot.lnum) - return FALSE; + lnum = reglnum + rex.reg_firstlnum; + if (lnum < top.lnum || lnum > bot.lnum) { + return false; + } if (mode == 'v') { col = (colnr_T)(reginput - regline); @@ -3803,11 +3841,11 @@ regmatch ( next = regnext(scan); op = OP(scan); - /* Check for character class with NL added. */ - if (!reg_line_lbr && WITH_NL(op) && REG_MULTI - && *reginput == NUL && reglnum <= reg_maxline) { + // Check for character class with NL added. + if (!rex.reg_line_lbr && WITH_NL(op) && REG_MULTI + && *reginput == NUL && reglnum <= rex.reg_maxline) { reg_nextline(); - } else if (reg_line_lbr && WITH_NL(op) && *reginput == '\n') { + } else if (rex.reg_line_lbr && WITH_NL(op) && *reginput == '\n') { ADVANCE_REGINPUT(); } else { if (WITH_NL(op)) @@ -3828,26 +3866,29 @@ regmatch ( break; case RE_BOF: - /* We're not at the beginning of the file when below the first - * line where we started, not at the start of the line or we - * didn't start at the first line of the buffer. */ + // We're not at the beginning of the file when below the first + // line where we started, not at the start of the line or we + // didn't start at the first line of the buffer. if (reglnum != 0 || reginput != regline - || (REG_MULTI && reg_firstlnum > 1)) + || (REG_MULTI && rex.reg_firstlnum > 1)) { status = RA_NOMATCH; + } break; case RE_EOF: - if (reglnum != reg_maxline || c != NUL) + if (reglnum != rex.reg_maxline || c != NUL) { status = RA_NOMATCH; + } break; case CURSOR: - /* Check if the buffer is in a window and compare the - * reg_win->w_cursor position to the match position. */ - if (reg_win == NULL - || (reglnum + reg_firstlnum != reg_win->w_cursor.lnum) - || ((colnr_T)(reginput - regline) != reg_win->w_cursor.col)) + // Check if the buffer is in a window and compare the + // rex.reg_win->w_cursor position to the match position. + if (rex.reg_win == NULL + || (reglnum + rex.reg_firstlnum != rex.reg_win->w_cursor.lnum) + || ((colnr_T)(reginput - regline) != rex.reg_win->w_cursor.col)) { status = RA_NOMATCH; + } break; case RE_MARK: @@ -3857,19 +3898,20 @@ regmatch ( int cmp = OPERAND(scan)[1]; pos_T *pos; - pos = getmark_buf(reg_buf, mark, FALSE); - if (pos == NULL /* mark doesn't exist */ - || pos->lnum <= 0 /* mark isn't set in reg_buf */ - || (pos->lnum == reglnum + reg_firstlnum + pos = getmark_buf(rex.reg_buf, mark, false); + if (pos == NULL // mark doesn't exist + || pos->lnum <= 0 // mark isn't set in reg_buf + || (pos->lnum == reglnum + rex.reg_firstlnum ? (pos->col == (colnr_T)(reginput - regline) ? (cmp == '<' || cmp == '>') : (pos->col < (colnr_T)(reginput - regline) ? cmp != '>' : cmp != '<')) - : (pos->lnum < reglnum + reg_firstlnum + : (pos->lnum < reglnum + rex.reg_firstlnum ? cmp != '>' - : cmp != '<'))) + : cmp != '<'))) { status = RA_NOMATCH; + } } break; @@ -3879,11 +3921,12 @@ regmatch ( break; case RE_LNUM: - assert(reglnum + reg_firstlnum >= 0 - && (uintmax_t)(reglnum + reg_firstlnum) <= UINT32_MAX); - if (!REG_MULTI || !re_num_cmp((uint32_t)(reglnum + reg_firstlnum), - scan)) + assert(reglnum + rex.reg_firstlnum >= 0 + && (uintmax_t)(reglnum + rex.reg_firstlnum) <= UINT32_MAX); + if (!REG_MULTI + || !re_num_cmp((uint32_t)(reglnum + rex.reg_firstlnum), scan)) { status = RA_NOMATCH; + } break; case RE_COL: @@ -3894,11 +3937,13 @@ regmatch ( break; case RE_VCOL: - if (!re_num_cmp(win_linetabsize(reg_win == NULL ? curwin : reg_win, + if (!re_num_cmp(win_linetabsize(rex.reg_win == NULL + ? curwin : rex.reg_win, regline, (colnr_T)(reginput - regline)) + 1, - scan)) + scan)) { status = RA_NOMATCH; + } break; case BOW: /* \<word; reginput points to w */ @@ -3908,17 +3953,18 @@ regmatch ( int this_class; // Get class of current and previous char (if it exists). - this_class = mb_get_class_tab(reginput, reg_buf->b_chartab); + this_class = mb_get_class_tab(reginput, rex.reg_buf->b_chartab); if (this_class <= 1) { status = RA_NOMATCH; // Not on a word at all. } else if (reg_prev_class() == this_class) { status = RA_NOMATCH; // Previous char is in same word. } } else { - if (!vim_iswordc_buf(c, reg_buf) || (reginput > regline - && vim_iswordc_buf(reginput[-1 - ], reg_buf))) + if (!vim_iswordc_buf(c, rex.reg_buf) + || (reginput > regline + && vim_iswordc_buf(reginput[-1], rex.reg_buf))) { status = RA_NOMATCH; + } } break; @@ -3929,15 +3975,16 @@ regmatch ( int this_class, prev_class; // Get class of current and previous char (if it exists). - this_class = mb_get_class_tab(reginput, reg_buf->b_chartab); + this_class = mb_get_class_tab(reginput, rex.reg_buf->b_chartab); prev_class = reg_prev_class(); if (this_class == prev_class || prev_class == 0 || prev_class == 1) status = RA_NOMATCH; } else { - if (!vim_iswordc_buf(reginput[-1], reg_buf) - || (reginput[0] != NUL && vim_iswordc_buf(c, reg_buf))) + if (!vim_iswordc_buf(reginput[-1], rex.reg_buf) + || (reginput[0] != NUL && vim_iswordc_buf(c, rex.reg_buf))) { status = RA_NOMATCH; + } } break; /* Matched with EOW */ @@ -3964,17 +4011,20 @@ regmatch ( break; case KWORD: - if (!vim_iswordp_buf(reginput, reg_buf)) + if (!vim_iswordp_buf(reginput, rex.reg_buf)) { status = RA_NOMATCH; - else + } else { ADVANCE_REGINPUT(); + } break; case SKWORD: - if (ascii_isdigit(*reginput) || !vim_iswordp_buf(reginput, reg_buf)) + if (ascii_isdigit(*reginput) + || !vim_iswordp_buf(reginput, rex.reg_buf)) { status = RA_NOMATCH; - else + } else { ADVANCE_REGINPUT(); + } break; case FNAME: @@ -4139,7 +4189,7 @@ regmatch ( opnd = OPERAND(scan); // Inline the first byte, for speed. if (*opnd != *reginput - && (!ireg_ic + && (!rex.reg_ic || (!enc_utf8 && mb_tolower(*opnd) != mb_tolower(*reginput)))) { status = RA_NOMATCH; @@ -4147,8 +4197,8 @@ regmatch ( // match empty string always works; happens when "~" is // empty. } else { - if (opnd[1] == NUL && !(enc_utf8 && ireg_ic)) { - len = 1; /* matched a single byte above */ + if (opnd[1] == NUL && !(enc_utf8 && rex.reg_ic)) { + len = 1; // matched a single byte above } else { // Need to match first byte again for multi-byte. len = (int)STRLEN(opnd); @@ -4160,7 +4210,7 @@ regmatch ( // follows (skips over all composing chars). if (status != RA_NOMATCH && enc_utf8 && UTF_COMPOSINGLIKE(reginput, reginput + len) - && !ireg_icombine + && !rex.reg_icombine && OP(next) != RE_COMPOSING) { // raaron: This code makes a composing character get // ignored, which is the correct behavior (sometimes) @@ -4284,9 +4334,9 @@ regmatch ( status = RA_FAIL; else { rp->rs_no = no; - save_se(&rp->rs_un.sesave, ®_startpos[no], - ®_startp[no]); - /* We simply continue and handle the result when done. */ + save_se(&rp->rs_un.sesave, &rex.reg_startpos[no], + &rex.reg_startp[no]); + // We simply continue and handle the result when done. } } break; @@ -4336,12 +4386,12 @@ regmatch ( no = op - MCLOSE; cleanup_subexpr(); rp = regstack_push(RS_MCLOSE, scan); - if (rp == NULL) + if (rp == NULL) { status = RA_FAIL; - else { + } else { rp->rs_no = no; - save_se(&rp->rs_un.sesave, ®_endpos[no], ®_endp[no]); - /* We simply continue and handle the result when done. */ + save_se(&rp->rs_un.sesave, &rex.reg_endpos[no], &rex.reg_endp[no]); + // We simply continue and handle the result when done. } } break; @@ -4384,41 +4434,40 @@ regmatch ( no = op - BACKREF; cleanup_subexpr(); - if (!REG_MULTI) { /* Single-line regexp */ - if (reg_startp[no] == NULL || reg_endp[no] == NULL) { - /* Backref was not set: Match an empty string. */ + if (!REG_MULTI) { // Single-line regexp + if (rex.reg_startp[no] == NULL || rex.reg_endp[no] == NULL) { + // Backref was not set: Match an empty string. len = 0; } else { - /* Compare current input with back-ref in the same - * line. */ - len = (int)(reg_endp[no] - reg_startp[no]); - if (cstrncmp(reg_startp[no], reginput, &len) != 0) + // Compare current input with back-ref in the same line. + len = (int)(rex.reg_endp[no] - rex.reg_startp[no]); + if (cstrncmp(rex.reg_startp[no], reginput, &len) != 0) { status = RA_NOMATCH; + } } - } else { /* Multi-line regexp */ - if (reg_startpos[no].lnum < 0 || reg_endpos[no].lnum < 0) { - /* Backref was not set: Match an empty string. */ + } else { // Multi-line regexp + if (rex.reg_startpos[no].lnum < 0 || rex.reg_endpos[no].lnum < 0) { + // Backref was not set: Match an empty string. len = 0; } else { - if (reg_startpos[no].lnum == reglnum - && reg_endpos[no].lnum == reglnum) { - /* Compare back-ref within the current line. */ - len = reg_endpos[no].col - reg_startpos[no].col; - if (cstrncmp(regline + reg_startpos[no].col, - reginput, &len) != 0) + if (rex.reg_startpos[no].lnum == reglnum + && rex.reg_endpos[no].lnum == reglnum) { + // Compare back-ref within the current line. + len = rex.reg_endpos[no].col - rex.reg_startpos[no].col; + if (cstrncmp(regline + rex.reg_startpos[no].col, + reginput, &len) != 0) { status = RA_NOMATCH; + } } else { - /* Messy situation: Need to compare between two - * lines. */ - int r = match_with_backref( - reg_startpos[no].lnum, - reg_startpos[no].col, - reg_endpos[no].lnum, - reg_endpos[no].col, - &len); - - if (r != RA_MATCH) + // Messy situation: Need to compare between two lines. + int r = match_with_backref(rex.reg_startpos[no].lnum, + rex.reg_startpos[no].col, + rex.reg_endpos[no].lnum, + rex.reg_endpos[no].col, + &len); + if (r != RA_MATCH) { status = r; + } } } } @@ -4482,7 +4531,7 @@ regmatch ( brace_max[no] = OPERAND_MAX(scan); brace_count[no] = 0; } else { - EMSG(_(e_internal)); /* Shouldn't happen */ + internal_error("BRACE_LIMITS"); status = RA_FAIL; } } @@ -4558,7 +4607,7 @@ regmatch ( */ if (OP(next) == EXACTLY) { rst.nextb = *OPERAND(next); - if (ireg_ic) { + if (rex.reg_ic) { if (mb_isupper(rst.nextb)) { rst.nextb_ic = mb_tolower(rst.nextb); } else { @@ -4665,13 +4714,14 @@ regmatch ( break; case NEWL: - if ((c != NUL || !REG_MULTI || reglnum > reg_maxline - || reg_line_lbr) && (c != '\n' || !reg_line_lbr)) + if ((c != NUL || !REG_MULTI || reglnum > rex.reg_maxline + || rex.reg_line_lbr) && (c != '\n' || !rex.reg_line_lbr)) { status = RA_NOMATCH; - else if (reg_line_lbr) + } else if (rex.reg_line_lbr) { ADVANCE_REGINPUT(); - else + } else { reg_nextline(); + } break; case END: @@ -4710,10 +4760,11 @@ regmatch ( break; case RS_MOPEN: - /* Pop the state. Restore pointers when there is no match. */ - if (status == RA_NOMATCH) - restore_se(&rp->rs_un.sesave, ®_startpos[rp->rs_no], - ®_startp[rp->rs_no]); + // Pop the state. Restore pointers when there is no match. + if (status == RA_NOMATCH) { + restore_se(&rp->rs_un.sesave, &rex.reg_startpos[rp->rs_no], + &rex.reg_startp[rp->rs_no]); + } regstack_pop(&scan); break; @@ -4726,10 +4777,11 @@ regmatch ( break; case RS_MCLOSE: - /* Pop the state. Restore pointers when there is no match. */ - if (status == RA_NOMATCH) - restore_se(&rp->rs_un.sesave, ®_endpos[rp->rs_no], - ®_endp[rp->rs_no]); + // Pop the state. Restore pointers when there is no match. + if (status == RA_NOMATCH) { + restore_se(&rp->rs_un.sesave, &rex.reg_endpos[rp->rs_no], + &rex.reg_endp[rp->rs_no]); + } regstack_pop(&scan); break; @@ -5109,10 +5161,11 @@ regrepeat ( ++count; mb_ptr_adv(scan); } - if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > reg_maxline - || reg_line_lbr || count == maxcount) + if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > rex.reg_maxline + || rex.reg_line_lbr || count == maxcount) { break; - ++count; /* count the line-break */ + } + count++; // count the line-break reg_nextline(); scan = reginput; if (got_int) @@ -5130,17 +5183,19 @@ regrepeat ( if (vim_isIDc(PTR2CHAR(scan)) && (testval || !ascii_isdigit(*scan))) { mb_ptr_adv(scan); } else if (*scan == NUL) { - if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > reg_maxline - || reg_line_lbr) + if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > rex.reg_maxline + || rex.reg_line_lbr) { break; + } reg_nextline(); scan = reginput; if (got_int) break; - } else if (reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) - ++scan; - else + } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) { + scan++; + } else { break; + } ++count; } break; @@ -5152,22 +5207,25 @@ regrepeat ( case SKWORD: case SKWORD + ADD_NL: while (count < maxcount) { - if (vim_iswordp_buf(scan, reg_buf) + if (vim_iswordp_buf(scan, rex.reg_buf) && (testval || !ascii_isdigit(*scan))) { mb_ptr_adv(scan); } else if (*scan == NUL) { - if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > reg_maxline - || reg_line_lbr) + if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > rex.reg_maxline + || rex.reg_line_lbr) { break; + } reg_nextline(); scan = reginput; - if (got_int) + if (got_int) { break; - } else if (reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) - ++scan; - else + } + } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) { + scan++; + } else { break; - ++count; + } + count++; } break; @@ -5181,18 +5239,21 @@ regrepeat ( if (vim_isfilec(PTR2CHAR(scan)) && (testval || !ascii_isdigit(*scan))) { mb_ptr_adv(scan); } else if (*scan == NUL) { - if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > reg_maxline - || reg_line_lbr) + if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > rex.reg_maxline + || rex.reg_line_lbr) { break; + } reg_nextline(); scan = reginput; - if (got_int) + if (got_int) { break; - } else if (reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) - ++scan; - else + } + } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) { + scan++; + } else { break; - ++count; + } + count++; } break; @@ -5204,21 +5265,24 @@ regrepeat ( case SPRINT + ADD_NL: while (count < maxcount) { if (*scan == NUL) { - if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > reg_maxline - || reg_line_lbr) + if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > rex.reg_maxline + || rex.reg_line_lbr) { break; + } reg_nextline(); scan = reginput; - if (got_int) + if (got_int) { break; + } } else if (vim_isprintc(PTR2CHAR(scan)) == 1 && (testval || !ascii_isdigit(*scan))) { mb_ptr_adv(scan); - } else if (reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) - ++scan; - else + } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) { + scan++; + } else { break; - ++count; + } + count++; } break; @@ -5229,9 +5293,10 @@ do_class: while (count < maxcount) { int l; if (*scan == NUL) { - if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > reg_maxline - || reg_line_lbr) + if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > rex.reg_maxline + || rex.reg_line_lbr) { break; + } reg_nextline(); scan = reginput; if (got_int) @@ -5240,12 +5305,13 @@ do_class: if (testval != 0) break; scan += l; - } else if ((class_tab[*scan] & mask) == testval) - ++scan; - else if (reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) - ++scan; - else + } else if ((class_tab[*scan] & mask) == testval) { + scan++; + } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) { + scan++; + } else { break; + } ++count; } break; @@ -5323,10 +5389,10 @@ do_class: { int cu, cl; - /* This doesn't do a multi-byte character, because a MULTIBYTECODE - * would have been used for it. It does handle single-byte - * characters, such as latin1. */ - if (ireg_ic) { + // This doesn't do a multi-byte character, because a MULTIBYTECODE + // would have been used for it. It does handle single-byte + // characters, such as latin1. + if (rex.reg_ic) { cu = mb_toupper(*opnd); cl = mb_tolower(*opnd); while (count < maxcount && (*scan == cu || *scan == cl)) { @@ -5350,17 +5416,19 @@ do_class: /* Safety check (just in case 'encoding' was changed since * compiling the program). */ if ((len = (*mb_ptr2len)(opnd)) > 1) { - if (ireg_ic && enc_utf8) + if (rex.reg_ic && enc_utf8) { cf = utf_fold(utf_ptr2char(opnd)); + } while (count < maxcount && (*mb_ptr2len)(scan) >= len) { for (i = 0; i < len; ++i) { if (opnd[i] != scan[i]) { break; } } - if (i < len && (!ireg_ic || !enc_utf8 - || utf_fold(utf_ptr2char(scan)) != cf)) + if (i < len && (!rex.reg_ic || !enc_utf8 + || utf_fold(utf_ptr2char(scan)) != cf)) { break; + } scan += len; ++count; } @@ -5378,18 +5446,21 @@ do_class: while (count < maxcount) { int len; if (*scan == NUL) { - if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > reg_maxline - || reg_line_lbr) + if (!REG_MULTI || !WITH_NL(OP(p)) || reglnum > rex.reg_maxline + || rex.reg_line_lbr) { break; + } reg_nextline(); scan = reginput; - if (got_int) + if (got_int) { break; - } else if (reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) - ++scan; - else if (has_mbyte && (len = (*mb_ptr2len)(scan)) > 1) { - if ((cstrchr(opnd, (*mb_ptr2char)(scan)) == NULL) == testval) + } + } else if (rex.reg_line_lbr && *scan == '\n' && WITH_NL(OP(p))) { + scan++; + } else if (has_mbyte && (len = (*mb_ptr2len)(scan)) > 1) { + if ((cstrchr(opnd, (*mb_ptr2char)(scan)) == NULL) == testval) { break; + } scan += len; } else { if ((cstrchr(opnd, *scan) == NULL) == testval) @@ -5402,13 +5473,14 @@ do_class: case NEWL: while (count < maxcount - && ((*scan == NUL && reglnum <= reg_maxline && !reg_line_lbr - && REG_MULTI) || (*scan == '\n' && reg_line_lbr))) { + && ((*scan == NUL && reglnum <= rex.reg_maxline && !rex.reg_line_lbr + && REG_MULTI) || (*scan == '\n' && rex.reg_line_lbr))) { count++; - if (reg_line_lbr) + if (rex.reg_line_lbr) { ADVANCE_REGINPUT(); - else + } else { reg_nextline(); + } scan = reginput; if (got_int) break; @@ -5458,10 +5530,11 @@ static int prog_magic_wrong(void) { regprog_T *prog; - prog = REG_MULTI ? reg_mmatch->regprog : reg_match->regprog; - if (prog->engine == &nfa_regengine) - /* For NFA matcher we don't check the magic */ - return FALSE; + prog = REG_MULTI ? rex.reg_mmatch->regprog : rex.reg_match->regprog; + if (prog->engine == &nfa_regengine) { + // For NFA matcher we don't check the magic + return false; + } if (UCHARAT(((bt_regprog_T *)prog)->program) != REGMAGIC) { EMSG(_(e_re_corr)); @@ -5479,12 +5552,12 @@ static void cleanup_subexpr(void) { if (need_clear_subexpr) { if (REG_MULTI) { - /* Use 0xff to set lnum to -1 */ - memset(reg_startpos, 0xff, sizeof(lpos_T) * NSUBEXP); - memset(reg_endpos, 0xff, sizeof(lpos_T) * NSUBEXP); + // Use 0xff to set lnum to -1 + memset(rex.reg_startpos, 0xff, sizeof(lpos_T) * NSUBEXP); + memset(rex.reg_endpos, 0xff, sizeof(lpos_T) * NSUBEXP); } else { - memset(reg_startp, 0, sizeof(char_u *) * NSUBEXP); - memset(reg_endp, 0, sizeof(char_u *) * NSUBEXP); + memset(rex.reg_startp, 0, sizeof(char_u *) * NSUBEXP); + memset(rex.reg_endp, 0, sizeof(char_u *) * NSUBEXP); } need_clear_subexpr = FALSE; } @@ -5513,17 +5586,17 @@ static void save_subexpr(regbehind_T *bp) { int i; - /* When "need_clear_subexpr" is set we don't need to save the values, only - * remember that this flag needs to be set again when restoring. */ + // When "need_clear_subexpr" is set we don't need to save the values, only + // remember that this flag needs to be set again when restoring. bp->save_need_clear_subexpr = need_clear_subexpr; if (!need_clear_subexpr) { for (i = 0; i < NSUBEXP; ++i) { if (REG_MULTI) { - bp->save_start[i].se_u.pos = reg_startpos[i]; - bp->save_end[i].se_u.pos = reg_endpos[i]; + bp->save_start[i].se_u.pos = rex.reg_startpos[i]; + bp->save_end[i].se_u.pos = rex.reg_endpos[i]; } else { - bp->save_start[i].se_u.ptr = reg_startp[i]; - bp->save_end[i].se_u.ptr = reg_endp[i]; + bp->save_start[i].se_u.ptr = rex.reg_startp[i]; + bp->save_end[i].se_u.ptr = rex.reg_endp[i]; } } } @@ -5541,11 +5614,11 @@ static void restore_subexpr(regbehind_T *bp) if (!need_clear_subexpr) { for (i = 0; i < NSUBEXP; ++i) { if (REG_MULTI) { - reg_startpos[i] = bp->save_start[i].se_u.pos; - reg_endpos[i] = bp->save_end[i].se_u.pos; + rex.reg_startpos[i] = bp->save_start[i].se_u.pos; + rex.reg_endpos[i] = bp->save_end[i].se_u.pos; } else { - reg_startp[i] = bp->save_start[i].se_u.ptr; - reg_endp[i] = bp->save_end[i].se_u.ptr; + rex.reg_startp[i] = bp->save_start[i].se_u.ptr; + rex.reg_endp[i] = bp->save_end[i].se_u.ptr; } } } @@ -5681,10 +5754,12 @@ static int match_with_backref(linenr_T start_lnum, colnr_T start_col, linenr_T e return RA_NOMATCH; /* doesn't match */ if (bytelen != NULL) *bytelen += len; - if (clnum == end_lnum) - break; /* match and at end! */ - if (reglnum >= reg_maxline) - return RA_NOMATCH; /* text too short */ + if (clnum == end_lnum) { + break; // match and at end! + } + if (reglnum >= rex.reg_maxline) { + return RA_NOMATCH; // text too short + } /* Advance to next line. */ reg_nextline(); @@ -6232,24 +6307,22 @@ static void mb_decompose(int c, int *c1, int *c2, int *c3) } } -/* - * Compare two strings, ignore case if ireg_ic set. - * Return 0 if strings match, non-zero otherwise. - * Correct the length "*n" when composing characters are ignored. - */ +// Compare two strings, ignore case if rex.reg_ic set. +// Return 0 if strings match, non-zero otherwise. +// Correct the length "*n" when composing characters are ignored. static int cstrncmp(char_u *s1, char_u *s2, int *n) { int result; - if (!ireg_ic) + if (!rex.reg_ic) { result = STRNCMP(s1, s2, *n); - else { + } else { assert(*n >= 0); result = mb_strnicmp(s1, s2, (size_t)*n); } - /* if it failed and it's utf8 and we want to combineignore: */ - if (result != 0 && enc_utf8 && ireg_icombine) { + // if it failed and it's utf8 and we want to combineignore: + if (result != 0 && enc_utf8 && rex.reg_icombine) { char_u *str1, *str2; int c1, c2, c11, c12; int junk; @@ -6266,14 +6339,15 @@ static int cstrncmp(char_u *s1, char_u *s2, int *n) /* decompose the character if necessary, into 'base' characters * because I don't care about Arabic, I will hard-code the Hebrew * which I *do* care about! So sue me... */ - if (c1 != c2 && (!ireg_ic || utf_fold(c1) != utf_fold(c2))) { - /* decomposition necessary? */ + if (c1 != c2 && (!rex.reg_ic || utf_fold(c1) != utf_fold(c2))) { + // decomposition necessary? mb_decompose(c1, &c11, &junk, &junk); mb_decompose(c2, &c12, &junk, &junk); c1 = c11; c2 = c12; - if (c11 != c12 && (!ireg_ic || utf_fold(c11) != utf_fold(c12))) + if (c11 != c12 && (!rex.reg_ic || utf_fold(c11) != utf_fold(c12))) { break; + } } } result = c2 - c1; @@ -6284,42 +6358,6 @@ static int cstrncmp(char_u *s1, char_u *s2, int *n) return result; } -/* - * cstrchr: This function is used a lot for simple searches, keep it fast! - */ -static inline char_u *cstrchr(const char_u *const s, const int c) - FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL - FUNC_ATTR_ALWAYS_INLINE -{ - if (!ireg_ic) { - return vim_strchr(s, c); - } - - // Use folded case for UTF-8, slow! For ASCII use libc strpbrk which is - // expected to be highly optimized. - if (c > 0x80) { - const int folded_c = utf_fold(c); - for (const char_u *p = s; *p != NUL; p += utfc_ptr2len(p)) { - if (utf_fold(utf_ptr2char(p)) == folded_c) { - return (char_u *)p; - } - } - return NULL; - } - - int cc; - if (ASCII_ISUPPER(c)) { - cc = TOLOWER_ASC(c); - } else if (ASCII_ISLOWER(c)) { - cc = TOUPPER_ASC(c); - } else { - return vim_strchr(s, c); - } - - char tofind[] = { (char)c, (char)cc, NUL }; - return (char_u *)strpbrk((const char *)s, tofind); -} - /*************************************************************** * regsub stuff * ***************************************************************/ @@ -6419,54 +6457,52 @@ char_u *regtilde(char_u *source, int magic) static int can_f_submatch = FALSE; /* TRUE when submatch() can be used */ -/* These pointers are used instead of reg_match and reg_mmatch for - * reg_submatch(). Needed for when the substitution string is an expression - * that contains a call to substitute() and submatch(). */ -static regmatch_T *submatch_match; -static regmmatch_T *submatch_mmatch; -static linenr_T submatch_firstlnum; -static linenr_T submatch_maxline; -static int submatch_line_lbr; +// These pointers are used for reg_submatch(). Needed for when the +// substitution string is an expression that contains a call to substitute() +// and submatch(). +typedef struct { + regmatch_T *sm_match; + regmmatch_T *sm_mmatch; + linenr_T sm_firstlnum; + linenr_T sm_maxline; + int sm_line_lbr; +} regsubmatch_T; + +static regsubmatch_T rsm; // can only be used when can_f_submatch is true /// Put the submatches in "argv[0]" which is a list passed into call_func() by /// vim_regsub_both(). static int fill_submatch_list(int argc, typval_T *argv, int argcount) { - listitem_T *li; - int i; - char_u *s; - if (argcount == 0) { // called function doesn't take an argument return 0; } // Relies on sl_list to be the first item in staticList10_T. - init_static_list((staticList10_T *)(argv->vval.v_list)); + tv_list_init_static10((staticList10_T *)argv->vval.v_list); // There are always 10 list items in staticList10_T. - li = argv->vval.v_list->lv_first; - for (i = 0; i < 10; i++) { - s = submatch_match->startp[i]; - if (s == NULL || submatch_match->endp[i] == NULL) { + listitem_T *li = tv_list_first(argv->vval.v_list); + for (int i = 0; i < 10; i++) { + char_u *s = rsm.sm_match->startp[i]; + if (s == NULL || rsm.sm_match->endp[i] == NULL) { s = NULL; } else { - s = vim_strnsave(s, (int)(submatch_match->endp[i] - s)); + s = vim_strnsave(s, (int)(rsm.sm_match->endp[i] - s)); } - li->li_tv.v_type = VAR_STRING; - li->li_tv.vval.v_string = s; - li = li->li_next; + TV_LIST_ITEM_TV(li)->v_type = VAR_STRING; + TV_LIST_ITEM_TV(li)->vval.v_string = s; + li = TV_LIST_ITEM_NEXT(argv->vval.v_list, li); } return 1; } static void clear_submatch_list(staticList10_T *sl) { - int i; - - for (i = 0; i < 10; i++) { - xfree(sl->sl_items[i].li_tv.vval.v_string); - } + TV_LIST_ITER(&sl->sl_list, li, { + xfree(TV_LIST_ITEM_TV(li)->vval.v_string); + }); } /// vim_regsub() - perform substitutions after a vim_regexec() or @@ -6488,23 +6524,55 @@ static void clear_submatch_list(staticList10_T *sl) int vim_regsub(regmatch_T *rmp, char_u *source, typval_T *expr, char_u *dest, int copy, int magic, int backslash) { - reg_match = rmp; - reg_mmatch = NULL; - reg_maxline = 0; - reg_buf = curbuf; - reg_line_lbr = true; - return vim_regsub_both(source, expr, dest, copy, magic, backslash); + regexec_T rex_save; + bool rex_in_use_save = rex_in_use; + + if (rex_in_use) { + // Being called recursively, save the state. + rex_save = rex; + } + rex_in_use = true; + + rex.reg_match = rmp; + rex.reg_mmatch = NULL; + rex.reg_maxline = 0; + rex.reg_buf = curbuf; + rex.reg_line_lbr = true; + int result = vim_regsub_both(source, expr, dest, copy, magic, backslash); + + rex_in_use = rex_in_use_save; + if (rex_in_use) { + rex = rex_save; + } + + return result; } int vim_regsub_multi(regmmatch_T *rmp, linenr_T lnum, char_u *source, char_u *dest, int copy, int magic, int backslash) { - reg_match = NULL; - reg_mmatch = rmp; - reg_buf = curbuf; /* always works on the current buffer! */ - reg_firstlnum = lnum; - reg_maxline = curbuf->b_ml.ml_line_count - lnum; - reg_line_lbr = false; - return vim_regsub_both(source, NULL, dest, copy, magic, backslash); + regexec_T rex_save; + bool rex_in_use_save = rex_in_use; + + if (rex_in_use) { + // Being called recursively, save the state. + rex_save = rex; + } + rex_in_use = true; + + rex.reg_match = NULL; + rex.reg_mmatch = rmp; + rex.reg_buf = curbuf; // always works on the current buffer! + rex.reg_firstlnum = lnum; + rex.reg_maxline = curbuf->b_ml.ml_line_count - lnum; + rex.reg_line_lbr = false; + int result = vim_regsub_both(source, NULL, dest, copy, magic, backslash); + + rex_in_use = rex_in_use_save; + if (rex_in_use) { + rex = rex_save; + } + + return result; } static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, @@ -6533,8 +6601,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, dst = dest; // When the substitute part starts with "\=" evaluate it as an expression. - if (expr != NULL || (source[0] == '\\' && source[1] == '=' - && !can_f_submatch)) { // can't do this recursively + if (expr != NULL || (source[0] == '\\' && source[1] == '=')) { // To make sure that the length doesn't change between checking the // length and copying the string, and to speed up things, the // resulting string is saved from the call with "copy" == FALSE to the @@ -6547,56 +6614,50 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, eval_result = NULL; } } else { - win_T *save_reg_win; - int save_ireg_ic; - bool prev_can_f_submatch = can_f_submatch; + int prev_can_f_submatch = can_f_submatch; + regsubmatch_T rsm_save; xfree(eval_result); - /* The expression may contain substitute(), which calls us - * recursively. Make sure submatch() gets the text from the first - * level. Don't need to save "reg_buf", because - * vim_regexec_multi() can't be called recursively. */ - submatch_match = reg_match; - submatch_mmatch = reg_mmatch; - submatch_firstlnum = reg_firstlnum; - submatch_maxline = reg_maxline; - submatch_line_lbr = reg_line_lbr; - save_reg_win = reg_win; - save_ireg_ic = ireg_ic; + // The expression may contain substitute(), which calls us + // recursively. Make sure submatch() gets the text from the first + // level. + if (can_f_submatch) { + rsm_save = rsm; + } can_f_submatch = true; + rsm.sm_match = rex.reg_match; + rsm.sm_mmatch = rex.reg_mmatch; + rsm.sm_firstlnum = rex.reg_firstlnum; + rsm.sm_maxline = rex.reg_maxline; + rsm.sm_line_lbr = rex.reg_line_lbr; if (expr != NULL) { typval_T argv[2]; int dummy; typval_T rettv; - staticList10_T matchList; + staticList10_T matchList = TV_LIST_STATIC10_INIT; rettv.v_type = VAR_STRING; rettv.vval.v_string = NULL; - if (prev_can_f_submatch) { - // can't do this recursively - } else { - argv[0].v_type = VAR_LIST; - argv[0].vval.v_list = &matchList.sl_list; - matchList.sl_list.lv_len = 0; - if (expr->v_type == VAR_FUNC) { - s = expr->vval.v_string; - call_func(s, (int)STRLEN(s), &rettv, 1, argv, - fill_submatch_list, 0L, 0L, &dummy, - true, NULL, NULL); - } else if (expr->v_type == VAR_PARTIAL) { - partial_T *partial = expr->vval.v_partial; - - s = partial_name(partial); - call_func(s, (int)STRLEN(s), &rettv, 1, argv, - fill_submatch_list, 0L, 0L, &dummy, - true, partial, NULL); - } - if (matchList.sl_list.lv_len > 0) { - // fill_submatch_list() was called. - clear_submatch_list(&matchList); - } + argv[0].v_type = VAR_LIST; + argv[0].vval.v_list = &matchList.sl_list; + if (expr->v_type == VAR_FUNC) { + s = expr->vval.v_string; + call_func(s, (int)STRLEN(s), &rettv, 1, argv, + fill_submatch_list, 0L, 0L, &dummy, + true, NULL, NULL); + } else if (expr->v_type == VAR_PARTIAL) { + partial_T *partial = expr->vval.v_partial; + + s = partial_name(partial); + call_func(s, (int)STRLEN(s), &rettv, 1, argv, + fill_submatch_list, 0L, 0L, &dummy, + true, partial, NULL); + } + if (tv_list_len(&matchList.sl_list) > 0) { + // fill_submatch_list() was called. + clear_submatch_list(&matchList); } char buf[NUMBUFLEN]; eval_result = (char_u *)tv_get_string_buf_chk(&rettv, buf); @@ -6612,22 +6673,23 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int had_backslash = FALSE; for (s = eval_result; *s != NUL; mb_ptr_adv(s)) { - /* Change NL to CR, so that it becomes a line break, - * unless called from vim_regexec_nl(). - * Skip over a backslashed character. */ - if (*s == NL && !submatch_line_lbr) + // Change NL to CR, so that it becomes a line break, + // unless called from vim_regexec_nl(). + // Skip over a backslashed character. + if (*s == NL && !rsm.sm_line_lbr) { *s = CAR; - else if (*s == '\\' && s[1] != NUL) { - ++s; + } else if (*s == '\\' && s[1] != NUL) { + s++; /* Change NL to CR here too, so that this works: * :s/abc\\\ndef/\="aaa\\\nbbb"/ on text: * abc\ * def * Not when called from vim_regexec_nl(). */ - if (*s == NL && !submatch_line_lbr) + if (*s == NL && !rsm.sm_line_lbr) { *s = CAR; - had_backslash = TRUE; + } + had_backslash = true; } } if (had_backslash && backslash) { @@ -6640,14 +6702,10 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, dst += STRLEN(eval_result); } - reg_match = submatch_match; - reg_mmatch = submatch_mmatch; - reg_firstlnum = submatch_firstlnum; - reg_maxline = submatch_maxline; - reg_line_lbr = submatch_line_lbr; - reg_win = save_reg_win; - ireg_ic = save_ireg_ic; - can_f_submatch = FALSE; + can_f_submatch = prev_can_f_submatch; + if (can_f_submatch) { + rsm = rsm_save; + } } } else while ((c = *src++) != NUL) { @@ -6745,43 +6803,50 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, dst++; } else { if (REG_MULTI) { - clnum = reg_mmatch->startpos[no].lnum; - if (clnum < 0 || reg_mmatch->endpos[no].lnum < 0) + clnum = rex.reg_mmatch->startpos[no].lnum; + if (clnum < 0 || rex.reg_mmatch->endpos[no].lnum < 0) { s = NULL; - else { - s = reg_getline(clnum) + reg_mmatch->startpos[no].col; - if (reg_mmatch->endpos[no].lnum == clnum) - len = reg_mmatch->endpos[no].col - - reg_mmatch->startpos[no].col; - else + } else { + s = reg_getline(clnum) + rex.reg_mmatch->startpos[no].col; + if (rex.reg_mmatch->endpos[no].lnum == clnum) { + len = rex.reg_mmatch->endpos[no].col + - rex.reg_mmatch->startpos[no].col; + } else { len = (int)STRLEN(s); + } } } else { - s = reg_match->startp[no]; - if (reg_match->endp[no] == NULL) + s = rex.reg_match->startp[no]; + if (rex.reg_match->endp[no] == NULL) { s = NULL; - else - len = (int)(reg_match->endp[no] - s); + } else { + len = (int)(rex.reg_match->endp[no] - s); + } } if (s != NULL) { for (;; ) { if (len == 0) { if (REG_MULTI) { - if (reg_mmatch->endpos[no].lnum == clnum) + if (rex.reg_mmatch->endpos[no].lnum == clnum) { break; - if (copy) + } + if (copy) { *dst = CAR; - ++dst; + } + dst++; s = reg_getline(++clnum); - if (reg_mmatch->endpos[no].lnum == clnum) - len = reg_mmatch->endpos[no].col; - else + if (rex.reg_mmatch->endpos[no].lnum == clnum) { + len = rex.reg_mmatch->endpos[no].col; + } else { len = (int)STRLEN(s); - } else + } + } else { break; - } else if (*s == NUL) { /* we hit NUL. */ - if (copy) + } + } else if (*s == NUL) { // we hit NUL. + if (copy) { EMSG(_(e_re_damg)); + } goto exit; } else { if (backslash && (*s == CAR || *s == '\\')) { @@ -6855,16 +6920,16 @@ exit: static char_u *reg_getline_submatch(linenr_T lnum) { char_u *s; - linenr_T save_first = reg_firstlnum; - linenr_T save_max = reg_maxline; + linenr_T save_first = rex.reg_firstlnum; + linenr_T save_max = rex.reg_maxline; - reg_firstlnum = submatch_firstlnum; - reg_maxline = submatch_maxline; + rex.reg_firstlnum = rsm.sm_firstlnum; + rex.reg_maxline = rsm.sm_maxline; s = reg_getline(lnum); - reg_firstlnum = save_first; - reg_maxline = save_max; + rex.reg_firstlnum = save_first; + rex.reg_maxline = save_max; return s; } @@ -6883,39 +6948,41 @@ char_u *reg_submatch(int no) if (!can_f_submatch || no < 0) return NULL; - if (submatch_match == NULL) { + if (rsm.sm_match == NULL) { ssize_t len; /* * First round: compute the length and allocate memory. * Second round: copy the text. */ - for (round = 1; round <= 2; ++round) { - lnum = submatch_mmatch->startpos[no].lnum; - if (lnum < 0 || submatch_mmatch->endpos[no].lnum < 0) + for (round = 1; round <= 2; round++) { + lnum = rsm.sm_mmatch->startpos[no].lnum; + if (lnum < 0 || rsm.sm_mmatch->endpos[no].lnum < 0) { return NULL; + } - s = reg_getline_submatch(lnum) + submatch_mmatch->startpos[no].col; - if (s == NULL) /* anti-crash check, cannot happen? */ + s = reg_getline_submatch(lnum) + rsm.sm_mmatch->startpos[no].col; + if (s == NULL) { // anti-crash check, cannot happen? break; - if (submatch_mmatch->endpos[no].lnum == lnum) { - /* Within one line: take form start to end col. */ - len = submatch_mmatch->endpos[no].col - - submatch_mmatch->startpos[no].col; - if (round == 2) + } + if (rsm.sm_mmatch->endpos[no].lnum == lnum) { + // Within one line: take form start to end col. + len = rsm.sm_mmatch->endpos[no].col - rsm.sm_mmatch->startpos[no].col; + if (round == 2) { STRLCPY(retval, s, len + 1); - ++len; + } + len++; } else { - /* Multiple lines: take start line from start col, middle - * lines completely and end line up to end col. */ - len = STRLEN(s); + // Multiple lines: take start line from start col, middle + // lines completely and end line up to end col. + len = (ssize_t)STRLEN(s); if (round == 2) { STRCPY(retval, s); retval[len] = '\n'; } - ++len; - ++lnum; - while (lnum < submatch_mmatch->endpos[no].lnum) { + len++; + lnum++; + while (lnum < rsm.sm_mmatch->endpos[no].lnum) { s = reg_getline_submatch(lnum++); if (round == 2) STRCPY(retval + len, s); @@ -6924,13 +6991,15 @@ char_u *reg_submatch(int no) retval[len] = '\n'; ++len; } - if (round == 2) + if (round == 2) { STRNCPY(retval + len, reg_getline_submatch(lnum), - submatch_mmatch->endpos[no].col); - len += submatch_mmatch->endpos[no].col; - if (round == 2) - retval[len] = NUL; - ++len; + rsm.sm_mmatch->endpos[no].col); + } + len += rsm.sm_mmatch->endpos[no].col; + if (round == 2) { + retval[len] = NUL; // -V595 + } + len++; } if (retval == NULL) { @@ -6938,11 +7007,12 @@ char_u *reg_submatch(int no) } } } else { - s = submatch_match->startp[no]; - if (s == NULL || submatch_match->endp[no] == NULL) + s = rsm.sm_match->startp[no]; + if (s == NULL || rsm.sm_match->endp[no] == NULL) { retval = NULL; - else - retval = vim_strnsave(s, (int)(submatch_match->endp[no] - s)); + } else { + retval = vim_strnsave(s, (int)(rsm.sm_match->endp[no] - s)); + } } return retval; @@ -6964,17 +7034,17 @@ list_T *reg_submatch_list(int no) list_T *list; const char *s; - if (submatch_match == NULL) { - slnum = submatch_mmatch->startpos[no].lnum; - elnum = submatch_mmatch->endpos[no].lnum; + if (rsm.sm_match == NULL) { + slnum = rsm.sm_mmatch->startpos[no].lnum; + elnum = rsm.sm_mmatch->endpos[no].lnum; if (slnum < 0 || elnum < 0) { return NULL; } - colnr_T scol = submatch_mmatch->startpos[no].col; - colnr_T ecol = submatch_mmatch->endpos[no].col; + colnr_T scol = rsm.sm_mmatch->startpos[no].col; + colnr_T ecol = rsm.sm_mmatch->endpos[no].col; - list = tv_list_alloc(); + list = tv_list_alloc(elnum - slnum + 1); s = (const char *)reg_getline_submatch(slnum) + scol; if (slnum == elnum) { @@ -6989,12 +7059,12 @@ list_T *reg_submatch_list(int no) tv_list_append_string(list, s, ecol); } } else { - s = (const char *)submatch_match->startp[no]; - if (s == NULL || submatch_match->endp[no] == NULL) { + s = (const char *)rsm.sm_match->startp[no]; + if (s == NULL || rsm.sm_match->endp[no] == NULL) { return NULL; } - list = tv_list_alloc(); - tv_list_append_string(list, s, (const char *)submatch_match->endp[no] - s); + list = tv_list_alloc(1); + tv_list_append_string(list, s, (const char *)rsm.sm_match->endp[no] - s); } return list; @@ -7148,6 +7218,19 @@ static void report_re_switch(char_u *pat) /// @return TRUE if there is a match, FALSE if not. static int vim_regexec_both(regmatch_T *rmp, char_u *line, colnr_T col, bool nl) { + regexec_T rex_save; + bool rex_in_use_save = rex_in_use; + + if (rex_in_use) { + // Being called recursively, save the state. + rex_save = rex; + } + rex_in_use = true; + rex.reg_startp = NULL; + rex.reg_endp = NULL; + rex.reg_startpos = NULL; + rex.reg_endpos = NULL; + int result = rmp->regprog->engine->regexec_nl(rmp, line, col, nl); // NFA engine aborted because it's very slow, use backtracking engine instead. @@ -7169,6 +7252,11 @@ static int vim_regexec_both(regmatch_T *rmp, char_u *line, colnr_T col, bool nl) p_re = save_p_re; } + rex_in_use = rex_in_use_save; + if (rex_in_use) { + rex = rex_save; + } + return result > 0; } @@ -7216,6 +7304,15 @@ long vim_regexec_multi( proftime_T *tm /* timeout limit or NULL */ ) { + regexec_T rex_save; + bool rex_in_use_save = rex_in_use; + + if (rex_in_use) { + // Being called recursively, save the state. + rex_save = rex; + } + rex_in_use = true; + int result = rmp->regprog->engine->regexec_multi(rmp, win, buf, lnum, col, tm); @@ -7239,5 +7336,10 @@ long vim_regexec_multi( p_re = save_p_re; } + rex_in_use = rex_in_use_save; + if (rex_in_use) { + rex = rex_save; + } + return result <= 0 ? 0 : result; } diff --git a/src/nvim/regexp_defs.h b/src/nvim/regexp_defs.h index 6426ee441b..b5d56e07fc 100644 --- a/src/nvim/regexp_defs.h +++ b/src/nvim/regexp_defs.h @@ -15,6 +15,8 @@ #include <stdbool.h> #include "nvim/pos.h" +#include "nvim/types.h" +#include "nvim/profile.h" /* * The number of sub-matches is limited to 10. @@ -41,18 +43,36 @@ #define NFA_ENGINE 2 typedef struct regengine regengine_T; +typedef struct regprog regprog_T; +typedef struct reg_extmatch reg_extmatch_T; + +/// Structure to be used for multi-line matching. +/// Sub-match "no" starts in line "startpos[no].lnum" column "startpos[no].col" +/// and ends in line "endpos[no].lnum" just before column "endpos[no].col". +/// The line numbers are relative to the first line, thus startpos[0].lnum is +/// always 0. +/// When there is no match, the line number is -1. +typedef struct { + regprog_T *regprog; + lpos_T startpos[NSUBEXP]; + lpos_T endpos[NSUBEXP]; + int rmm_ic; + colnr_T rmm_maxcol; /// when not zero: maximum column +} regmmatch_T; + +#include "nvim/buffer_defs.h" /* * Structure returned by vim_regcomp() to pass on to vim_regexec(). * This is the general structure. For the actual matcher, two specific * structures are used. See code below. */ -typedef struct regprog { +struct regprog { regengine_T *engine; unsigned regflags; unsigned re_engine; ///< Automatic, backtracking or NFA engine. unsigned re_flags; ///< Second argument for vim_regcomp(). -} regprog_T; +}; /* * Structure used by the back track matcher. @@ -126,30 +146,14 @@ typedef struct { } regmatch_T; /* - * Structure to be used for multi-line matching. - * Sub-match "no" starts in line "startpos[no].lnum" column "startpos[no].col" - * and ends in line "endpos[no].lnum" just before column "endpos[no].col". - * The line numbers are relative to the first line, thus startpos[0].lnum is - * always 0. - * When there is no match, the line number is -1. - */ -typedef struct { - regprog_T *regprog; - lpos_T startpos[NSUBEXP]; - lpos_T endpos[NSUBEXP]; - int rmm_ic; - colnr_T rmm_maxcol; /* when not zero: maximum column */ -} regmmatch_T; - -/* * Structure used to store external references: "\z\(\)" to "\z\1". * Use a reference count to avoid the need to copy this around. When it goes * from 1 to zero the matches need to be freed. */ -typedef struct { - short refcnt; +struct reg_extmatch { + int16_t refcnt; char_u *matches[NSUBEXP]; -} reg_extmatch_T; +}; struct regengine { regprog_T *(*regcomp)(char_u*, int); diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index 24c156d2ba..c520ef5fb9 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -56,13 +56,13 @@ enum { NFA_RANGE_MIN, /* low end of a range */ NFA_RANGE_MAX, /* high end of a range */ - NFA_CONCAT, /* concatenate two previous items (postfix - * only) */ - NFA_OR, /* \| (postfix only) */ - NFA_STAR, /* greedy * (posfix only) */ - NFA_STAR_NONGREEDY, /* non-greedy * (postfix only) */ - NFA_QUEST, /* greedy \? (postfix only) */ - NFA_QUEST_NONGREEDY, /* non-greedy \? (postfix only) */ + NFA_CONCAT, // concatenate two previous items (postfix + // only) + NFA_OR, // \| (postfix only) + NFA_STAR, // greedy * (postfix only) + NFA_STAR_NONGREEDY, // non-greedy * (postfix only) + NFA_QUEST, // greedy \? (postfix only) + NFA_QUEST_NONGREEDY, // non-greedy \? (postfix only) NFA_BOL, /* ^ Begin line */ NFA_EOL, /* $ End line */ @@ -1263,7 +1263,7 @@ static int nfa_regatom(void) rc_did_emsg = TRUE; return FAIL; } - EMSGN("INTERNAL: Unknown character class char: %" PRId64, c); + IEMSGN("INTERNAL: Unknown character class char: %" PRId64, c); return FAIL; } /* When '.' is followed by a composing char ignore the dot, so that @@ -1988,7 +1988,7 @@ static int nfa_regpiece(void) // The engine is very inefficient (uses too many states) when the maximum // is much larger than the minimum and when the maximum is large. Bail out // if we can use the other engine. - if ((nfa_re_flags & RE_AUTO) && (maxval > minval + 200 || maxval > 500)) { + if ((nfa_re_flags & RE_AUTO) && (maxval > 500 || maxval > minval + 200)) { return FAIL; } @@ -3913,7 +3913,7 @@ addstate ( int k; int found = FALSE; nfa_thread_T *thread; - lpos_T save_lpos; + struct multipos save_multipos; int save_in_use; char_u *save_ptr; int i; @@ -4127,15 +4127,13 @@ skip_add: /* avoid compiler warnings */ save_ptr = NULL; - save_lpos.lnum = 0; - save_lpos.col = 0; + memset(&save_multipos, 0, sizeof(save_multipos)); /* Set the position (with "off" added) in the subexpression. Save * and restore it when it was in use. Otherwise fill any gap. */ if (REG_MULTI) { if (subidx < sub->in_use) { - save_lpos.lnum = sub->list.multi[subidx].start_lnum; - save_lpos.col = sub->list.multi[subidx].start_col; + save_multipos = sub->list.multi[subidx]; save_in_use = -1; } else { save_in_use = sub->in_use; @@ -4178,9 +4176,8 @@ skip_add: sub = &subs->norm; if (save_in_use == -1) { - if (REG_MULTI){ - sub->list.multi[subidx].start_lnum = save_lpos.lnum; - sub->list.multi[subidx].start_col = save_lpos.col; + if (REG_MULTI) { + sub->list.multi[subidx] = save_multipos; } else sub->list.line[subidx].start = save_ptr; @@ -4234,8 +4231,7 @@ skip_add: if (sub->in_use <= subidx) sub->in_use = subidx + 1; if (REG_MULTI) { - save_lpos.lnum = sub->list.multi[subidx].end_lnum; - save_lpos.col = sub->list.multi[subidx].end_col; + save_multipos = sub->list.multi[subidx]; if (off == -1) { sub->list.multi[subidx].end_lnum = reglnum + 1; sub->list.multi[subidx].end_col = 0; @@ -4249,9 +4245,8 @@ skip_add: } else { save_ptr = sub->list.line[subidx].end; sub->list.line[subidx].end = reginput + off; - /* avoid compiler warnings */ - save_lpos.lnum = 0; - save_lpos.col = 0; + // avoid compiler warnings + memset(&save_multipos, 0, sizeof(save_multipos)); } subs = addstate(l, state->out, subs, pim, off_arg); @@ -4261,9 +4256,8 @@ skip_add: else sub = &subs->norm; - if (REG_MULTI){ - sub->list.multi[subidx].end_lnum = save_lpos.lnum; - sub->list.multi[subidx].end_col = save_lpos.col; + if (REG_MULTI) { + sub->list.multi[subidx] = save_multipos; } else sub->list.line[subidx].end = save_ptr; @@ -4419,8 +4413,8 @@ static int check_char_class(int class, int c) break; default: - /* should not be here :P */ - EMSGN(_(e_ill_char_class), class); + // should not be here :P + IEMSGN(_(e_ill_char_class), class); return FAIL; } return FAIL; @@ -4888,7 +4882,7 @@ static long find_match_text(colnr_T startcol, int regstart, char_u *match_text) int c2_len = PTR2LEN(s2); int c2 = PTR2CHAR(s2); - if ((c1 != c2 && (!ireg_ic || mb_tolower(c1) != mb_tolower(c2))) + if ((c1 != c2 && (!rex.reg_ic || mb_tolower(c1) != mb_tolower(c2))) || c1_len != c2_len) { match = false; break; @@ -4901,13 +4895,13 @@ static long find_match_text(colnr_T startcol, int regstart, char_u *match_text) && !(enc_utf8 && utf_iscomposing(PTR2CHAR(s2)))) { cleanup_subexpr(); if (REG_MULTI) { - reg_startpos[0].lnum = reglnum; - reg_startpos[0].col = col; - reg_endpos[0].lnum = reglnum; - reg_endpos[0].col = s2 - regline; + rex.reg_startpos[0].lnum = reglnum; + rex.reg_startpos[0].col = col; + rex.reg_endpos[0].lnum = reglnum; + rex.reg_endpos[0].col = s2 - regline; } else { - reg_startp[0] = regline + col; - reg_endp[0] = s2; + rex.reg_startp[0] = regline + col; + rex.reg_endp[0] = s2; } return 1L; } @@ -5122,8 +5116,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, case NFA_MATCH: { // If the match ends before a composing characters and - // ireg_icombine is not set, that is not really a match. - if (enc_utf8 && !ireg_icombine && utf_iscomposing(curc)) { + // rex.reg_icombine is not set, that is not really a match. + if (enc_utf8 && !rex.reg_icombine && utf_iscomposing(curc)) { break; } nfa_match = true; @@ -5406,15 +5400,15 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, int this_class; // Get class of current and previous char (if it exists). - this_class = mb_get_class_tab(reginput, reg_buf->b_chartab); + this_class = mb_get_class_tab(reginput, rex.reg_buf->b_chartab); if (this_class <= 1) { result = false; } else if (reg_prev_class() == this_class) { result = false; } - } else if (!vim_iswordc_buf(curc, reg_buf) + } else if (!vim_iswordc_buf(curc, rex.reg_buf) || (reginput > regline - && vim_iswordc_buf(reginput[-1], reg_buf))) { + && vim_iswordc_buf(reginput[-1], rex.reg_buf))) { result = false; } if (result) { @@ -5431,15 +5425,15 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, int this_class, prev_class; // Get class of current and previous char (if it exists). - this_class = mb_get_class_tab(reginput, reg_buf->b_chartab); + this_class = mb_get_class_tab(reginput, rex.reg_buf->b_chartab); prev_class = reg_prev_class(); if (this_class == prev_class || prev_class == 0 || prev_class == 1) { result = false; } - } else if (!vim_iswordc_buf(reginput[-1], reg_buf) + } else if (!vim_iswordc_buf(reginput[-1], rex.reg_buf) || (reginput[0] != NUL - && vim_iswordc_buf(curc, reg_buf))) { + && vim_iswordc_buf(curc, rex.reg_buf))) { result = false; } if (result) { @@ -5450,14 +5444,14 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, case NFA_BOF: if (reglnum == 0 && reginput == regline - && (!REG_MULTI || reg_firstlnum == 1)) { + && (!REG_MULTI || rex.reg_firstlnum == 1)) { add_here = true; add_state = t->state->out; } break; case NFA_EOF: - if (reglnum == reg_maxline && curc == NUL) { + if (reglnum == rex.reg_maxline && curc == NUL) { add_here = true; add_state = t->state->out; } @@ -5481,7 +5475,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, // (no preceding character). len += mb_char2len(mc); } - if (ireg_icombine && len == 0) { + if (rex.reg_icombine && len == 0) { // If \Z was present, then ignore composing characters. // When ignoring the base character this always matches. if (sta->c != curc) { @@ -5532,14 +5526,14 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, } case NFA_NEWL: - if (curc == NUL && !reg_line_lbr && REG_MULTI - && reglnum <= reg_maxline) { + if (curc == NUL && !rex.reg_line_lbr && REG_MULTI + && reglnum <= rex.reg_maxline) { go_to_nextline = true; // Pass -1 for the offset, which means taking the position // at the start of the next line. add_state = t->state->out; add_off = -1; - } else if (curc == '\n' && reg_line_lbr) { + } else if (curc == '\n' && rex.reg_line_lbr) { // match \n as if it is an ordinary character add_state = t->state->out; add_off = 1; @@ -5580,7 +5574,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, result = result_if_matched; break; } - if (ireg_ic) { + if (rex.reg_ic) { int curc_low = mb_tolower(curc); int done = false; @@ -5597,7 +5591,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, } } else if (state->c < 0 ? check_char_class(state->c, curc) : (curc == state->c - || (ireg_ic && mb_tolower(curc) + || (rex.reg_ic && mb_tolower(curc) == mb_tolower(state->c)))) { result = result_if_matched; break; @@ -5645,13 +5639,13 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, break; case NFA_KWORD: // \k - result = vim_iswordp_buf(reginput, reg_buf); + result = vim_iswordp_buf(reginput, rex.reg_buf); ADD_STATE_IF_MATCH(t->state); break; case NFA_SKWORD: // \K result = !ascii_isdigit(curc) - && vim_iswordp_buf(reginput, reg_buf); + && vim_iswordp_buf(reginput, rex.reg_buf); ADD_STATE_IF_MATCH(t->state); break; @@ -5766,24 +5760,24 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, break; case NFA_LOWER_IC: // [a-z] - result = ri_lower(curc) || (ireg_ic && ri_upper(curc)); + result = ri_lower(curc) || (rex.reg_ic && ri_upper(curc)); ADD_STATE_IF_MATCH(t->state); break; case NFA_NLOWER_IC: // [^a-z] result = curc != NUL - && !(ri_lower(curc) || (ireg_ic && ri_upper(curc))); + && !(ri_lower(curc) || (rex.reg_ic && ri_upper(curc))); ADD_STATE_IF_MATCH(t->state); break; case NFA_UPPER_IC: // [A-Z] - result = ri_upper(curc) || (ireg_ic && ri_lower(curc)); + result = ri_upper(curc) || (rex.reg_ic && ri_lower(curc)); ADD_STATE_IF_MATCH(t->state); break; case NFA_NUPPER_IC: // [^A-Z] result = curc != NUL - && !(ri_upper(curc) || (ireg_ic && ri_lower(curc))); + && !(ri_upper(curc) || (rex.reg_ic && ri_lower(curc))); ADD_STATE_IF_MATCH(t->state); break; @@ -5857,13 +5851,15 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, case NFA_LNUM_GT: case NFA_LNUM_LT: assert(t->state->val >= 0 - && !((reg_firstlnum > 0 && reglnum > LONG_MAX - reg_firstlnum) - || (reg_firstlnum <0 && reglnum < LONG_MIN + reg_firstlnum)) - && reglnum + reg_firstlnum >= 0); + && !((rex.reg_firstlnum > 0 + && reglnum > LONG_MAX - rex.reg_firstlnum) + || (rex.reg_firstlnum < 0 + && reglnum < LONG_MIN + rex.reg_firstlnum)) + && reglnum + rex.reg_firstlnum >= 0); result = (REG_MULTI && nfa_re_num_cmp((uintmax_t)t->state->val, t->state->c - NFA_LNUM, - (uintmax_t)(reglnum + reg_firstlnum))); + (uintmax_t)(reglnum + rex.reg_firstlnum))); if (result) { add_here = true; add_state = t->state->out; @@ -5899,7 +5895,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, } result = false; - win_T *wp = reg_win == NULL ? curwin : reg_win; + win_T *wp = rex.reg_win == NULL ? curwin : rex.reg_win; if (op == 1 && col - 1 > t->state->val && col > 100) { long ts = wp->w_buffer->b_p_ts; @@ -5926,18 +5922,18 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, case NFA_MARK_GT: case NFA_MARK_LT: { - pos_T *pos = getmark_buf(reg_buf, t->state->val, FALSE); + pos_T *pos = getmark_buf(rex.reg_buf, t->state->val, false); // Compare the mark position to the match position. result = (pos != NULL // mark doesn't exist && pos->lnum > 0 // mark isn't set in reg_buf - && (pos->lnum == reglnum + reg_firstlnum + && (pos->lnum == reglnum + rex.reg_firstlnum ? (pos->col == (colnr_T)(reginput - regline) ? t->state->c == NFA_MARK : (pos->col < (colnr_T)(reginput - regline) ? t->state->c == NFA_MARK_GT : t->state->c == NFA_MARK_LT)) - : (pos->lnum < reglnum + reg_firstlnum + : (pos->lnum < reglnum + rex.reg_firstlnum ? t->state->c == NFA_MARK_GT : t->state->c == NFA_MARK_LT))); if (result) { @@ -5948,10 +5944,10 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, } case NFA_CURSOR: - result = (reg_win != NULL - && (reglnum + reg_firstlnum == reg_win->w_cursor.lnum) + result = (rex.reg_win != NULL + && (reglnum + rex.reg_firstlnum == rex.reg_win->w_cursor.lnum) && ((colnr_T)(reginput - regline) - == reg_win->w_cursor.col)); + == rex.reg_win->w_cursor.col)); if (result) { add_here = true; add_state = t->state->out; @@ -5996,18 +5992,19 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, int c = t->state->c; #ifdef REGEXP_DEBUG - if (c < 0) - EMSGN("INTERNAL: Negative state char: %" PRId64, c); + if (c < 0) { + IEMSGN("INTERNAL: Negative state char: %" PRId64, c); + } #endif result = (c == curc); - if (!result && ireg_ic) { + if (!result && rex.reg_ic) { result = mb_tolower(c) == mb_tolower(curc); } - // If ireg_icombine is not set only skip over the character + // If rex.reg_icombine is not set only skip over the character // itself. When it is set skip over composing characters. - if (result && enc_utf8 && !ireg_icombine) { + if (result && enc_utf8 && !rex.reg_icombine) { clen = utf_ptr2len(reginput); } @@ -6115,8 +6112,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, && ((toplevel && reglnum == 0 && clen != 0 - && (ireg_maxcol == 0 - || (colnr_T)(reginput - regline) < ireg_maxcol)) + && (rex.reg_maxcol == 0 + || (colnr_T)(reginput - regline) < rex.reg_maxcol)) || (nfa_endp != NULL && (REG_MULTI ? (reglnum < nfa_endp->se_u.pos.lnum @@ -6151,7 +6148,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, // Checking if the required start character matches is // cheaper than adding a state that won't match. c = PTR2CHAR(reginput + clen); - if (c != prog->regstart && (!ireg_ic || mb_tolower(c) + if (c != prog->regstart && (!rex.reg_ic || mb_tolower(c) != mb_tolower(prog->regstart))) { #ifdef REGEXP_DEBUG fprintf(log_fd, @@ -6277,34 +6274,37 @@ static long nfa_regtry(nfa_regprog_T *prog, colnr_T col, proftime_T *tm) cleanup_subexpr(); if (REG_MULTI) { for (i = 0; i < subs.norm.in_use; i++) { - reg_startpos[i].lnum = subs.norm.list.multi[i].start_lnum; - reg_startpos[i].col = subs.norm.list.multi[i].start_col; + rex.reg_startpos[i].lnum = subs.norm.list.multi[i].start_lnum; + rex.reg_startpos[i].col = subs.norm.list.multi[i].start_col; - reg_endpos[i].lnum = subs.norm.list.multi[i].end_lnum; - reg_endpos[i].col = subs.norm.list.multi[i].end_col; + rex.reg_endpos[i].lnum = subs.norm.list.multi[i].end_lnum; + rex.reg_endpos[i].col = subs.norm.list.multi[i].end_col; } - if (reg_startpos[0].lnum < 0) { - reg_startpos[0].lnum = 0; - reg_startpos[0].col = col; + if (rex.reg_startpos[0].lnum < 0) { + rex.reg_startpos[0].lnum = 0; + rex.reg_startpos[0].col = col; + } + if (rex.reg_endpos[0].lnum < 0) { + // pattern has a \ze but it didn't match, use current end + rex.reg_endpos[0].lnum = reglnum; + rex.reg_endpos[0].col = (int)(reginput - regline); + } else { + // Use line number of "\ze". + reglnum = rex.reg_endpos[0].lnum; } - if (reg_endpos[0].lnum < 0) { - /* pattern has a \ze but it didn't match, use current end */ - reg_endpos[0].lnum = reglnum; - reg_endpos[0].col = (int)(reginput - regline); - } else - /* Use line number of "\ze". */ - reglnum = reg_endpos[0].lnum; } else { for (i = 0; i < subs.norm.in_use; i++) { - reg_startp[i] = subs.norm.list.line[i].start; - reg_endp[i] = subs.norm.list.line[i].end; + rex.reg_startp[i] = subs.norm.list.line[i].start; + rex.reg_endp[i] = subs.norm.list.line[i].end; } - if (reg_startp[0] == NULL) - reg_startp[0] = regline + col; - if (reg_endp[0] == NULL) - reg_endp[0] = reginput; + if (rex.reg_startp[0] == NULL) { + rex.reg_startp[0] = regline + col; + } + if (rex.reg_endp[0] == NULL) { + rex.reg_endp[0] = reginput; + } } /* Package any found \z(...\) matches for export. Default is none. */ @@ -6358,14 +6358,14 @@ static long nfa_regexec_both(char_u *line, colnr_T startcol, proftime_T *tm) colnr_T col = startcol; if (REG_MULTI) { - prog = (nfa_regprog_T *)reg_mmatch->regprog; - line = reg_getline((linenr_T)0); /* relative to the cursor */ - reg_startpos = reg_mmatch->startpos; - reg_endpos = reg_mmatch->endpos; + prog = (nfa_regprog_T *)rex.reg_mmatch->regprog; + line = reg_getline((linenr_T)0); // relative to the cursor + rex.reg_startpos = rex.reg_mmatch->startpos; + rex.reg_endpos = rex.reg_mmatch->endpos; } else { - prog = (nfa_regprog_T *)reg_match->regprog; - reg_startp = reg_match->startp; - reg_endp = reg_match->endp; + prog = (nfa_regprog_T *)rex.reg_match->regprog; + rex.reg_startp = rex.reg_match->startp; + rex.reg_endp = rex.reg_match->endp; } /* Be paranoid... */ @@ -6374,15 +6374,17 @@ static long nfa_regexec_both(char_u *line, colnr_T startcol, proftime_T *tm) goto theend; } - /* If pattern contains "\c" or "\C": overrule value of ireg_ic */ - if (prog->regflags & RF_ICASE) - ireg_ic = TRUE; - else if (prog->regflags & RF_NOICASE) - ireg_ic = FALSE; + // If pattern contains "\c" or "\C": overrule value of rex.reg_ic + if (prog->regflags & RF_ICASE) { + rex.reg_ic = true; + } else if (prog->regflags & RF_NOICASE) { + rex.reg_ic = false; + } - /* If pattern contains "\Z" overrule value of ireg_icombine */ - if (prog->regflags & RF_ICOMBINE) - ireg_icombine = TRUE; + // If pattern contains "\Z" overrule value of rex.reg_icombine + if (prog->regflags & RF_ICOMBINE) { + rex.reg_icombine = true; + } regline = line; reglnum = 0; /* relative to line */ @@ -6411,17 +6413,17 @@ static long nfa_regexec_both(char_u *line, colnr_T startcol, proftime_T *tm) if (skip_to_start(prog->regstart, &col) == FAIL) return 0L; - /* If match_text is set it contains the full text that must match. - * Nothing else to try. Doesn't handle combining chars well. */ - if (prog->match_text != NULL - && !ireg_icombine - ) + // If match_text is set it contains the full text that must match. + // Nothing else to try. Doesn't handle combining chars well. + if (prog->match_text != NULL && !rex.reg_icombine) { return find_match_text(col, prog->regstart, prog->match_text); + } } - /* If the start column is past the maximum column: no need to try. */ - if (ireg_maxcol > 0 && col >= ireg_maxcol) + // If the start column is past the maximum column: no need to try. + if (rex.reg_maxcol > 0 && col >= rex.reg_maxcol) { goto theend; + } nstate = prog->nstate; for (i = 0; i < nstate; ++i) { @@ -6461,12 +6463,13 @@ static regprog_T *nfa_regcomp(char_u *expr, int re_flags) * (and count its size). */ postfix = re2post(); if (postfix == NULL) { - /* TODO: only give this error for debugging? */ - if (post_ptr >= post_end) - EMSGN("Internal error: estimated max number " - "of states insufficient: %" PRId64, - post_end - post_start); - goto fail; /* Cascaded (syntax?) error */ + // TODO(vim): only give this error for debugging? + if (post_ptr >= post_end) { + IEMSGN("Internal error: estimated max number " + "of states insufficient: %" PRId64, + post_end - post_start); + } + goto fail; // Cascaded (syntax?) error } /* @@ -6573,15 +6576,15 @@ nfa_regexec_nl ( bool line_lbr ) { - reg_match = rmp; - reg_mmatch = NULL; - reg_maxline = 0; - reg_line_lbr = line_lbr; - reg_buf = curbuf; - reg_win = NULL; - ireg_ic = rmp->rm_ic; - ireg_icombine = FALSE; - ireg_maxcol = 0; + rex.reg_match = rmp; + rex.reg_mmatch = NULL; + rex.reg_maxline = 0; + rex.reg_line_lbr = line_lbr; + rex.reg_buf = curbuf; + rex.reg_win = NULL; + rex.reg_ic = rmp->rm_ic; + rex.reg_icombine = false; + rex.reg_maxcol = 0; return nfa_regexec_both(line, col, NULL); } @@ -6622,16 +6625,16 @@ nfa_regexec_nl ( static long nfa_regexec_multi(regmmatch_T *rmp, win_T *win, buf_T *buf, linenr_T lnum, colnr_T col, proftime_T *tm) { - reg_match = NULL; - reg_mmatch = rmp; - reg_buf = buf; - reg_win = win; - reg_firstlnum = lnum; - reg_maxline = reg_buf->b_ml.ml_line_count - lnum; - reg_line_lbr = FALSE; - ireg_ic = rmp->rmm_ic; - ireg_icombine = FALSE; - ireg_maxcol = rmp->rmm_maxcol; + rex.reg_match = NULL; + rex.reg_mmatch = rmp; + rex.reg_buf = buf; + rex.reg_win = win; + rex.reg_firstlnum = lnum; + rex.reg_maxline = rex.reg_buf->b_ml.ml_line_count - lnum; + rex.reg_line_lbr = false; + rex.reg_ic = rmp->rmm_ic; + rex.reg_icombine = false; + rex.reg_maxcol = rmp->rmm_maxcol; return nfa_regexec_both(NULL, col, tm); } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 2f7fa8724f..22de08041a 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -86,6 +86,7 @@ #include <stdbool.h> #include <string.h> +#include "nvim/log.h" #include "nvim/vim.h" #include "nvim/ascii.h" #include "nvim/arabic.h" @@ -138,11 +139,6 @@ * doesn't fit. */ #define W_ENDCOL(wp) (wp->w_wincol + wp->w_width) -/* - * The attributes that are actually active for writing to the screen. - */ -static int screen_attr = 0; - static match_T search_hl; /* used for 'hlsearch' highlight matching */ static foldinfo_T win_foldinfo; /* info for 'foldcolumn' */ @@ -188,8 +184,6 @@ void redraw_win_later(win_T *wp, int type) void redraw_later_clear(void) { redraw_all_later(CLEAR); - /* Use attributes that is very unlikely to appear in text. */ - screen_attr = HL_BOLD | HL_UNDERLINE | HL_INVERSE; } /* @@ -344,8 +338,9 @@ void update_screen(int type) if (need_highlight_changed) highlight_changed(); - if (type == CLEAR) { /* first clear screen */ - screenclear(); /* will reset clear_cmdline */ + if (type == CLEAR) { // first clear screen + screenclear(); // will reset clear_cmdline + cmdline_screen_cleared(); // clear external cmdline state type = NOT_VALID; } @@ -691,12 +686,18 @@ static void win_update(win_T *wp) if (wp->w_nrwidth != i) { type = NOT_VALID; wp->w_nrwidth = i; - } else if (buf->b_mod_set && buf->b_mod_xlines != 0 && wp->w_redraw_top != 0) { - /* - * When there are both inserted/deleted lines and specific lines to be - * redrawn, w_redraw_top and w_redraw_bot may be invalid, just redraw - * everything (only happens when redrawing is off for while). - */ + + if (buf->terminal) { + terminal_resize(buf->terminal, + (uint16_t)(MAX(0, wp->w_width - win_col_off(wp))), + (uint16_t)wp->w_height); + } + } else if (buf->b_mod_set + && buf->b_mod_xlines != 0 + && wp->w_redraw_top != 0) { + // When there are both inserted/deleted lines and specific lines to be + // redrawn, w_redraw_top and w_redraw_bot may be invalid, just redraw + // everything (only happens when redrawing is off for while). type = NOT_VALID; } else { /* @@ -991,7 +992,7 @@ static void win_update(win_T *wp) * first. */ if (mid_start == 0) { mid_end = wp->w_height; - if (lastwin == firstwin) { + if (ONE_WINDOW) { /* Clear the screen when it was not done by win_del_lines() or * win_ins_lines() above, "screen_cleared" is FALSE or MAYBE * then. */ @@ -1581,6 +1582,20 @@ static void win_update(win_T *wp) got_int = save_got_int; } +/// Returns width of the signcolumn that should be used for the whole window +/// +/// @param wp window we want signcolumn width from +/// @return max width of signcolumn (cell unit) +/// +/// @note Returns a constant for now but hopefully we can improve neovim so that +/// the returned value width adapts to the maximum number of marks to draw +/// for the window +/// TODO(teto) +int win_signcol_width(win_T *wp) +{ + // 2 is vim default value + return 2; +} /* * Clear the rest of the window and mark the unused lines with "c1". use "c2" @@ -1608,7 +1623,7 @@ static void win_draw_end(win_T *wp, int c1, int c2, int row, int endrow, hlf_T h } if (signcolumn_on(wp)) { - int nn = n + 2; + int nn = n + win_signcol_width(wp); /* draw the sign column left of the fold column */ if (nn > wp->w_width) { @@ -1649,7 +1664,7 @@ static void win_draw_end(win_T *wp, int c1, int c2, int row, int endrow, hlf_T h } if (signcolumn_on(wp)) { - int nn = n + 2; + int nn = n + win_signcol_width(wp); /* draw the sign column after the fold column */ if (nn > wp->w_width) { @@ -1761,12 +1776,13 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T * text */ RL_MEMSET(col, win_hl_attr(wp, HLF_FL), wp->w_width - col); - // If signs are being displayed, add two spaces. + // If signs are being displayed, add spaces. if (signcolumn_on(wp)) { len = wp->w_width - col; if (len > 0) { - if (len > 2) { - len = 2; + int len_max = win_signcol_width(wp); + if (len > len_max) { + len = len_max; } copy_text_attr(off + col, (char_u *)" ", len, win_hl_attr(wp, HLF_FL)); @@ -1923,10 +1939,15 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T if (fill_fold >= 0x80) { ScreenLinesUC[off + col] = fill_fold; ScreenLinesC[0][off + col] = 0; - } else + ScreenLines[off + col] = 0x80; // avoid storing zero + } else { ScreenLinesUC[off + col] = 0; + ScreenLines[off + col] = fill_fold; + } + col++; + } else { + ScreenLines[off + col++] = fill_fold; } - ScreenLines[off + col++] = fill_fold; } if (text != buf) @@ -2111,16 +2132,16 @@ win_line ( bool nochange /* not updating for changed text */ ) { - int col; /* visual column on screen */ - unsigned off; /* offset in ScreenLines/ScreenAttrs */ - int c = 0; /* init for GCC */ - long vcol = 0; /* virtual column (for tabs) */ + int col = 0; // visual column on screen + unsigned off; // offset in ScreenLines/ScreenAttrs + int c = 0; // init for GCC + long vcol = 0; // virtual column (for tabs) long vcol_sbr = -1; // virtual column after showbreak - long vcol_prev = -1; /* "vcol" of previous character */ - char_u *line; /* current line */ - char_u *ptr; /* current position in "line" */ - int row; /* row in the window, excl w_winrow */ - int screen_row; /* row on the screen, incl w_winrow */ + long vcol_prev = -1; // "vcol" of previous character + char_u *line; // current line + char_u *ptr; // current position in "line" + int row; // row in the window, excl w_winrow + int screen_row; // row on the screen, incl w_winrow char_u extra[18]; /* line number and 'fdc' must fit in here */ int n_extra = 0; /* number of extra chars */ @@ -2194,17 +2215,17 @@ win_line ( int change_start = MAXCOL; /* first col of changed area */ int change_end = -1; /* last col of changed area */ colnr_T trailcol = MAXCOL; /* start of trailing spaces */ - int need_showbreak = FALSE; - int line_attr = 0; /* attribute for the whole line */ - matchitem_T *cur; /* points to the match list */ - match_T *shl; /* points to search_hl or a match */ - int shl_flag; /* flag to indicate whether search_hl - has been processed or not */ - int prevcol_hl_flag; /* flag to indicate whether prevcol - equals startcol of search_hl or one - of the matches */ - int prev_c = 0; /* previous Arabic character */ - int prev_c1 = 0; /* first composing char for prev_c */ + int need_showbreak = false; // overlong line, skip first x chars + int line_attr = 0; // attribute for the whole line + matchitem_T *cur; // points to the match list + match_T *shl; // points to search_hl or a match + int shl_flag; // flag to indicate whether search_hl + // has been processed or not + int prevcol_hl_flag; // flag to indicate whether prevcol + // equals startcol of search_hl or one + // of the matches + int prev_c = 0; // previous Arabic character + int prev_c1 = 0; // first composing char for prev_c int did_line_attr = 0; bool search_attr_from_match = false; // if search_attr is from :match @@ -2421,10 +2442,11 @@ win_line ( filler_lines = wp->w_topfill; filler_todo = filler_lines; - /* If this line has a sign with line highlighting set line_attr. */ + // If this line has a sign with line highlighting set line_attr. v = buf_getsigntype(wp->w_buffer, lnum, SIGN_LINEHL); - if (v != 0) - line_attr = sign_get_attr((int)v, TRUE); + if (v != 0) { + line_attr = sign_get_attr((int)v, true); + } // Highlight the current line in the quickfix window. if (bt_quickfix(wp->w_buffer) && qf_current_entry(wp) == lnum) { @@ -2521,7 +2543,11 @@ win_line ( if (vcol > v) { vcol -= c; ptr = prev_ptr; - n_skip = v - vcol; + // If the character fits on the screen, don't need to skip it. + // Except for a TAB. + if (((*mb_ptr2cells)(ptr) >= c || *ptr == TAB) && col == 0) { + n_skip = v - vcol; + } } /* @@ -2653,9 +2679,9 @@ win_line ( cur = cur->next; } - /* Cursor line highlighting for 'cursorline' in the current window. Not - * when Visual mode is active, because it's not clear what is selected - * then. */ + // Cursor line highlighting for 'cursorline' in the current window. Not + // when Visual mode is active, because it's not clear what is selected + // then. if (wp->w_p_cul && lnum == wp->w_cursor.lnum && !(wp == curwin && VIsual_active)) { if (line_attr != 0 && !(State & INSERT) && bt_quickfix(wp->w_buffer) @@ -2704,11 +2730,14 @@ win_line ( draw_state = WL_FOLD; if (fdc > 0) { - // Draw the 'foldcolumn'. - fill_foldcolumn(extra, wp, false, lnum); + // Draw the 'foldcolumn'. Allocate a buffer, "extra" may + // already be in use. + xfree(p_extra_free); + p_extra_free = xmalloc(12 + 1); + fill_foldcolumn(p_extra_free, wp, false, lnum); n_extra = fdc; - p_extra = extra; - p_extra[n_extra] = NUL; + p_extra_free[n_extra] = NUL; + p_extra = p_extra_free; c_extra = NUL; char_attr = win_hl_attr(wp, HLF_FC); } @@ -2721,18 +2750,26 @@ win_line ( * buffer or when using Netbeans. */ if (signcolumn_on(wp)) { int text_sign; - /* Draw two cells with the sign value or blank. */ + // Draw cells with the sign value or blank. c_extra = ' '; char_attr = win_hl_attr(wp, HLF_SC); n_extra = 2; + n_extra = win_signcol_width(wp); if (row == startrow + filler_lines && filler_todo <= 0) { text_sign = buf_getsigntype(wp->w_buffer, lnum, SIGN_TEXT); if (text_sign != 0) { p_extra = sign_get_text(text_sign); + int symbol_blen = (int)STRLEN(p_extra); if (p_extra != NULL) { c_extra = NUL; - n_extra = (int)STRLEN(p_extra); + // symbol(s) bytes + (filling spaces) (one byte each) + n_extra = symbol_blen + + (win_signcol_width(wp) - mb_string2cells(p_extra)); + memset(extra, ' ', sizeof(extra)); + STRNCPY(extra, p_extra, STRLEN(p_extra)); + p_extra = extra; + p_extra[n_extra] = NUL; } char_attr = sign_get_attr(text_sign, FALSE); } @@ -2804,8 +2841,10 @@ win_line ( // draw 'breakindent': indent wrapped text accodringly if (draw_state == WL_BRI - 1 && n_extra == 0) { draw_state = WL_BRI; - if (wp->w_p_bri && row != startrow && filler_lines == 0) { - char_attr = wp->w_hl_attr_normal; // was: hl_attr(HLF_AT); + // if need_showbreak is set, breakindent also applies + if (wp->w_p_bri && (row != startrow || need_showbreak) + && filler_lines == 0) { + char_attr = wp->w_hl_attr_normal; if (diff_hlf != (hlf_T)0) { char_attr = win_hl_attr(wp, diff_hlf); @@ -3120,14 +3159,15 @@ win_line ( } --n_extra; } else { + int c0; + if (p_extra_free != NULL) { xfree(p_extra_free); p_extra_free = NULL; } - /* - * Get a character from the line itself. - */ - c = *ptr; + + // Get a character from the line itself. + c0 = c = *ptr; if (has_mbyte) { mb_c = c; if (enc_utf8) { @@ -3137,11 +3177,12 @@ win_line ( mb_utf8 = FALSE; if (mb_l > 1) { mb_c = utfc_ptr2char(ptr, u8cc); - /* Overlong encoded ASCII or ASCII with composing char - * is displayed normally, except a NUL. */ - if (mb_c < 0x80) - c = mb_c; - mb_utf8 = TRUE; + // Overlong encoded ASCII or ASCII with composing char + // is displayed normally, except a NUL. + if (mb_c < 0x80) { + c0 = c = mb_c; + } + mb_utf8 = true; /* At start of the line we can have a composing char. * Draw it as a space with a composing char. */ @@ -3335,10 +3376,11 @@ win_line ( /* Use nextline[] if possible, it has the start of the * next line concatenated. */ - if ((prev_ptr - line) - nextlinecol >= 0) - p = nextline + (prev_ptr - line) - nextlinecol; - else + if ((prev_ptr - line) - nextlinecol >= 0) { + p = nextline + ((prev_ptr - line) - nextlinecol); + } else { p = prev_ptr; + } cap_col -= (int)(prev_ptr - line); size_t tmplen = spell_check(wp, p, &spell_hlf, &cap_col, nochange); assert(tmplen <= INT_MAX); @@ -3405,10 +3447,8 @@ win_line ( char_attr = hl_combine_attr(char_attr, term_attrs[vcol]); } - /* - * Found last space before word: check for line break. - */ - if (wp->w_p_lbr && vim_isbreak(c) && !vim_isbreak(*ptr)) { + // Found last space before word: check for line break. + if (wp->w_p_lbr && c0 == c && vim_isbreak(c) && !vim_isbreak(*ptr)) { int mb_off = has_mbyte ? (*mb_head_off)(line, ptr - 1) : 0; char_u *p = ptr - (mb_off + 1); // TODO: is passing p for start of the line OK? @@ -3509,6 +3549,7 @@ win_line ( p = xmalloc(len + 1); memset(p, ' ', len); p[len] = NUL; + xfree(p_extra_free); p_extra_free = p; for (i = 0; i < tab_len; i++) { mb_char2bytes(lcs_tab2, p); @@ -3578,15 +3619,13 @@ win_line ( && lcs_eol_one > 0) { // Display a '$' after the line or highlight an extra // character if the line break is included. - // For a diff line the highlighting continues after the - // "$". + // For a diff line the highlighting continues after the "$". if (diff_hlf == (hlf_T)0 && line_attr == 0) { - /* In virtualedit, visual selections may extend - * beyond end of line. */ + // In virtualedit, visual selections may extend beyond end of line. if (area_highlighting && virtual_active() - && tocol != MAXCOL && vcol < tocol) + && tocol != MAXCOL && vcol < tocol) { n_extra = 0; - else { + } else { p_extra = at_end_str; n_extra = 1; c_extra = NUL; @@ -3624,6 +3663,7 @@ win_line ( memset(p, ' ', n_extra); STRNCPY(p, p_extra + 1, STRLEN(p_extra) - 1); p[n_extra] = NUL; + xfree(p_extra_free); p_extra_free = p_extra = p; } else { n_extra = byte2cells(c) - 1; @@ -4018,10 +4058,10 @@ win_line ( if (wp->w_p_cuc && VCOL_HLC == (long)wp->w_virtcol && lnum != wp->w_cursor.lnum) { vcol_save_attr = char_attr; - char_attr = hl_combine_attr(char_attr, win_hl_attr(wp, HLF_CUC)); + char_attr = hl_combine_attr(win_hl_attr(wp, HLF_CUC), char_attr); } else if (draw_color_col && VCOL_HLC == *color_cols) { vcol_save_attr = char_attr; - char_attr = hl_combine_attr(char_attr, win_hl_attr(wp, HLF_MC)); + char_attr = hl_combine_attr(win_hl_attr(wp, HLF_MC), char_attr); } } @@ -4230,7 +4270,6 @@ win_line ( * (regardless of the xn,am settings). * Only do this if the cursor is on the current line * (something has been written in it). - * Don't do this for the GUI. * Don't do this for double-width characters. * Don't do this for a window not at the right screen border. */ @@ -4298,6 +4337,7 @@ win_line ( cap_col = 0; } + xfree(p_extra_free); return row; } @@ -4344,7 +4384,8 @@ static int char_needs_redraw(int off_from, int off_to, int cols) && comp_char_differs(off_from, off_to)) || ((*mb_off2cells)(off_from, off_from + cols) > 1 && ScreenLines[off_from + 1] - != ScreenLines[off_to + 1]))))); + != ScreenLines[off_to + 1]))) + || p_wd < 0)); } /* @@ -4875,11 +4916,14 @@ void win_redr_status(win_T *wp) int this_ru_col; static int busy = FALSE; - /* It's possible to get here recursively when 'statusline' (indirectly) - * invokes ":redrawstatus". Simply ignore the call then. */ - if (busy) + // May get here recursively when 'statusline' (indirectly) + // invokes ":redrawstatus". Simply ignore the call then. + if (busy + // Also ignore if wildmenu is showing. + || (wild_menu_showing != 0 && !ui_is_external(kUIWildmenu))) { return; - busy = TRUE; + } + busy = true; wp->w_redr_status = FALSE; if (wp->w_status_height == 0) { @@ -5821,52 +5865,29 @@ next_search_hl_pos( return 0; } -static void screen_start_highlight(int attr) -{ - screen_attr = attr; - ui_start_highlight(attr); -} - -void screen_stop_highlight(void) -{ - ui_stop_highlight(); - screen_attr = 0; -} - /* * Put character ScreenLines["off"] on the screen at position "row" and "col", * using the attributes from ScreenAttrs["off"]. */ static void screen_char(unsigned off, int row, int col) { - int attr; - - /* Check for illegal values, just in case (could happen just after - * resizing). */ - if (row >= screen_Rows || col >= screen_Columns) + // Check for illegal values, just in case (could happen just after resizing). + if (row >= screen_Rows || col >= screen_Columns) { return; + } - /* Outputting the last character on the screen may scrollup the screen. - * Don't to it! Mark the character invalid (update it when scrolled up) */ + // Outputting the last character on the screen may scrollup the screen. + // Don't to it! Mark the character invalid (update it when scrolled up) + // FIXME: The premise here is not actually true (cf. deferred wrap). if (row == screen_Rows - 1 && col == screen_Columns - 1 - /* account for first command-line character in rightleft mode */ - && !cmdmsg_rl - ) { + // account for first command-line character in rightleft mode + && !cmdmsg_rl) { ScreenAttrs[off] = (sattr_T)-1; return; } - /* - * Stop highlighting first, so it's easier to move the cursor. - */ - attr = ScreenAttrs[off]; - if (screen_attr != attr) - screen_stop_highlight(); - ui_cursor_goto(row, col); - - if (screen_attr != attr) - screen_start_highlight(attr); + ui_set_highlight(ScreenAttrs[off]); if (enc_utf8 && ScreenLinesUC[off] != 0) { char_u buf[MB_MAXBYTES + 1]; @@ -5975,7 +5996,7 @@ void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1, ++off; if (off < end_off) { /* something to be cleared */ col = off - LineOffset[row]; - screen_stop_highlight(); + ui_clear_highlight(); ui_cursor_goto(row, col); // clear rest of this screen line ui_call_eol_clear(); col = end_col - col; @@ -6357,8 +6378,7 @@ static void screenclear2(void) return; } - screen_stop_highlight(); /* don't want highlighting here */ - + ui_clear_highlight(); // don't want highlighting here /* blank out ScreenLines */ for (i = 0; i < Rows; ++i) { @@ -6442,125 +6462,39 @@ void setcursor(void) } } -/* - * insert 'line_count' lines at 'row' in window 'wp' - * if 'invalid' is TRUE the wp->w_lines[].wl_lnum is invalidated. - * if 'mayclear' is TRUE the screen will be cleared if it is faster than - * scrolling. - * Returns FAIL if the lines are not inserted, OK for success. - */ +/// Insert 'line_count' lines at 'row' in window 'wp'. +/// If 'invalid' is TRUE the wp->w_lines[].wl_lnum is invalidated. +/// If 'mayclear' is TRUE the screen will be cleared if it is faster than +/// scrolling. +/// Returns FAIL if the lines are not inserted, OK for success. int win_ins_lines(win_T *wp, int row, int line_count, int invalid, int mayclear) { - int did_delete; - int nextrow; - int lastrow; - int retval; - - if (invalid) - wp->w_lines_valid = 0; - - if (wp->w_height < 5) - return FAIL; - - if (line_count > wp->w_height - row) - line_count = wp->w_height - row; - - retval = win_do_lines(wp, row, line_count, mayclear, FALSE); - if (retval != MAYBE) - return retval; - - /* - * If there is a next window or a status line, we first try to delete the - * lines at the bottom to avoid messing what is after the window. - * If this fails and there are following windows, don't do anything to avoid - * messing up those windows, better just redraw. - */ - did_delete = FALSE; - if (wp->w_next != NULL || wp->w_status_height) { - if (screen_del_lines(0, wp->w_winrow + wp->w_height - line_count, - line_count, (int)Rows, NULL) == OK) - did_delete = TRUE; - else if (wp->w_next) - return FAIL; - } - /* - * if no lines deleted, blank the lines that will end up below the window - */ - if (!did_delete) { - wp->w_redr_status = TRUE; - redraw_cmdline = TRUE; - nextrow = wp->w_winrow + wp->w_height + wp->w_status_height; - lastrow = nextrow + line_count; - if (lastrow > Rows) - lastrow = Rows; - screen_fill(nextrow - line_count, lastrow - line_count, - wp->w_wincol, W_ENDCOL(wp), - ' ', ' ', 0); - } - - if (screen_ins_lines(0, wp->w_winrow + row, line_count, (int)Rows, NULL) - == FAIL) { - /* deletion will have messed up other windows */ - if (did_delete) { - wp->w_redr_status = TRUE; - win_rest_invalid(wp->w_next); - } + if (wp->w_height < 5) { return FAIL; } - return OK; + return win_do_lines(wp, row, line_count, invalid, mayclear, false); } -/* - * delete "line_count" window lines at "row" in window "wp" - * If "invalid" is TRUE curwin->w_lines[] is invalidated. - * If "mayclear" is TRUE the screen will be cleared if it is faster than - * scrolling - * Return OK for success, FAIL if the lines are not deleted. - */ +/// Delete "line_count" window lines at "row" in window "wp". +/// If "invalid" is TRUE curwin->w_lines[] is invalidated. +/// If "mayclear" is TRUE the screen will be cleared if it is faster than +/// scrolling +/// Return OK for success, FAIL if the lines are not deleted. int win_del_lines(win_T *wp, int row, int line_count, int invalid, int mayclear) { - int retval; - - if (invalid) - wp->w_lines_valid = 0; - - if (line_count > wp->w_height - row) - line_count = wp->w_height - row; - - retval = win_do_lines(wp, row, line_count, mayclear, TRUE); - if (retval != MAYBE) - return retval; - - if (screen_del_lines(0, wp->w_winrow + row, line_count, - (int)Rows, NULL) == FAIL) { - return FAIL; - } - - /* - * If there are windows or status lines below, try to put them at the - * correct place. If we can't do that, they have to be redrawn. - */ - if (wp->w_next || wp->w_status_height || cmdline_row < Rows - 1) { - if (screen_ins_lines(0, wp->w_winrow + wp->w_height - line_count, - line_count, (int)Rows, NULL) == FAIL) { - wp->w_redr_status = TRUE; - win_rest_invalid(wp->w_next); - } - } - /* - * If this is the last window and there is no status line, redraw the - * command line later. - */ - else - redraw_cmdline = TRUE; - return OK; + return win_do_lines(wp, row, line_count, invalid, mayclear, true); } // Common code for win_ins_lines() and win_del_lines(). // Returns OK or FAIL when the work has been done. -static int win_do_lines(win_T *wp, int row, int line_count, int mayclear, int del) +static int win_do_lines(win_T *wp, int row, int line_count, + int invalid, int mayclear, int del) { + if (invalid) { + wp->w_lines_valid = 0; + } + if (!redrawing() || line_count <= 0) { return FAIL; } @@ -6814,16 +6748,20 @@ int showmode(void) if (p_ri) MSG_PUTS_ATTR(_(" REVERSE"), attr); MSG_PUTS_ATTR(_(" INSERT"), attr); - } else if (restart_edit == 'I') + } else if (restart_edit == 'I' || restart_edit == 'i' + || restart_edit == 'a') { MSG_PUTS_ATTR(_(" (insert)"), attr); - else if (restart_edit == 'R') + } else if (restart_edit == 'R') { MSG_PUTS_ATTR(_(" (replace)"), attr); - else if (restart_edit == 'V') + } else if (restart_edit == 'V') { MSG_PUTS_ATTR(_(" (vreplace)"), attr); - if (p_hkmap) + } + if (p_hkmap) { MSG_PUTS_ATTR(_(" Hebrew"), attr); - if (p_fkmap) + } + if (p_fkmap) { MSG_PUTS_ATTR(farsi_text_5, attr); + } if (State & LANGMAP) { if (curwin->w_p_arab) { MSG_PUTS_ATTR(_(" Arabic"), attr); @@ -7161,7 +7099,7 @@ static int fillchar_status(int *attr, win_T *wp) * window differs, or the fillchars differ, or this is not the * current window */ if (*attr != 0 && ((win_hl_attr(wp, HLF_S) != win_hl_attr(wp, HLF_SNC) - || !is_curwin || firstwin == lastwin) + || !is_curwin || ONE_WINDOW) || (fill_stl != fill_stlnc))) { return fill; } @@ -7178,11 +7116,7 @@ static int fillchar_status(int *attr, win_T *wp) static int fillchar_vsep(win_T *wp, int *attr) { *attr = win_hl_attr(wp, HLF_C); - if (*attr == 0 && fill_vert == ' ') { - return '|'; - } else { - return fill_vert; - } + return fill_vert; } /* @@ -7424,13 +7358,7 @@ int number_width(win_T *wp) return n; } -/* - * Set size of the Vim shell. - * If 'mustset' is TRUE, we must set Rows and Columns, do not get the real - * window size (this is used for the :win command). - * If 'mustset' is FALSE, we may try to get the real window size and if - * it fails use 'width' and 'height'. - */ +/// Set dimensions of the Nvim application "shell". void screen_resize(int width, int height) { static int busy = FALSE; @@ -7515,8 +7443,8 @@ void screen_resize(int width, int height) --busy; } -// Check if the new shell size is valid, correct it if it's too small or way -// too big. +/// Check if the new Nvim application "shell" dimensions are valid. +/// Correct it if it's too small or way too big. void check_shellsize(void) { if (Rows < min_rows()) { diff --git a/src/nvim/search.c b/src/nvim/search.c index 1bf2317d2a..1943e2ca43 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -96,6 +96,9 @@ static int lastc_bytelen = 1; /* >1 for multi-byte char */ /* copy of spats[], for keeping the search patterns while executing autocmds */ static struct spat saved_spats[2]; +// copy of spats[RE_SEARCH], for keeping the search patterns while incremental +// searching +static struct spat saved_last_search_spat; static int saved_last_idx = 0; static int saved_no_hlsearch = 0; @@ -305,6 +308,36 @@ void free_search_patterns(void) #endif +/// Save and restore the search pattern for incremental highlight search +/// feature. +/// +/// It's similar but different from save_search_patterns() and +/// restore_search_patterns(), because the search pattern must be restored when +/// cancelling incremental searching even if it's called inside user functions. +void save_last_search_pattern(void) +{ + saved_last_search_spat = spats[RE_SEARCH]; + if (spats[RE_SEARCH].pat != NULL) { + saved_last_search_spat.pat = vim_strsave(spats[RE_SEARCH].pat); + } + saved_last_idx = last_idx; + saved_no_hlsearch = no_hlsearch; +} + +void restore_last_search_pattern(void) +{ + xfree(spats[RE_SEARCH].pat); + spats[RE_SEARCH] = saved_last_search_spat; + set_vv_searchforward(); + last_idx = saved_last_idx; + SET_NO_HLSEARCH(saved_no_hlsearch); +} + +char_u *last_search_pattern(void) +{ + return spats[RE_SEARCH].pat; +} + /* * Return TRUE when case should be ignored for search pattern "pat". * Uses the 'ignorecase' and 'smartcase' options. @@ -742,12 +775,17 @@ int searchit( } } if (ptr[matchcol] == NUL - || (nmatched = vim_regexec_multi(®match, - win, buf, lnum + matchpos.lnum, - matchcol, - tm - )) == 0) - break; + || (nmatched = vim_regexec_multi( + ®match, win, buf, lnum + matchpos.lnum, matchcol, + tm)) == 0) { + // If the search timed out, we did find a match + // but it might be the wrong one, so that's not + // OK. + if (tm != NULL && profile_passed_limit(*tm)) { + match_ok = false; + } + break; + } /* Need to get the line pointer again, a * multi-line search may have made it invalid. */ @@ -1342,13 +1380,15 @@ int searchc(cmdarg_T *cap, int t_cmd) lastc_bytelen += (*mb_char2bytes)(cap->ncharC2, lastc_bytes + lastc_bytelen); } } - } else { /* repeat previous search */ - if (*lastc == NUL) + } else { // repeat previous search + if (*lastc == NUL && lastc_bytelen == 1) { return FAIL; - if (dir) /* repeat in opposite direction */ + } + if (dir) { // repeat in opposite direction dir = -lastcdir; - else + } else { dir = lastcdir; + } t_cmd = last_t_cmd; c = *lastc; /* For multi-byte re-use last lastc_bytes[] and lastc_bytelen. */ @@ -1382,13 +1422,13 @@ int searchc(cmdarg_T *cap, int t_cmd) col -= (*mb_head_off)(p, p + col - 1) + 1; } if (lastc_bytelen == 1) { - if (p[col] == c && stop) + if (p[col] == c && stop) { break; - } else { - if (memcmp(p + col, lastc_bytes, lastc_bytelen) == 0 && stop) + } + } else if (STRNCMP(p + col, lastc_bytes, lastc_bytelen) == 0 && stop) { break; } - stop = TRUE; + stop = true; } } else { for (;; ) { @@ -2227,10 +2267,11 @@ int findsent(int dir, long count) break; found_dot = TRUE; } - if (decl(&pos) == -1) + if (decl(&pos) == -1) { break; - /* when going forward: Stop in front of empty line */ - if (lineempty(pos.lnum) && dir == FORWARD) { + } + // when going forward: Stop in front of empty line + if (LINEEMPTY(pos.lnum) && dir == FORWARD) { incl(&pos); goto found; } @@ -2534,10 +2575,12 @@ int bck_word(long count, int bigword, int stop) */ while (cls() == 0) { if (curwin->w_cursor.col == 0 - && lineempty(curwin->w_cursor.lnum)) + && LINEEMPTY(curwin->w_cursor.lnum)) { goto finished; - if (dec_cursor() == -1) /* hit start of file, stop here */ + } + if (dec_cursor() == -1) { // hit start of file, stop here return OK; + } } /* @@ -2601,10 +2644,12 @@ int end_word(long count, int bigword, int stop, int empty) */ while (cls() == 0) { if (empty && curwin->w_cursor.col == 0 - && lineempty(curwin->w_cursor.lnum)) + && LINEEMPTY(curwin->w_cursor.lnum)) { goto finished; - if (inc_cursor() == -1) /* hit end of file, stop here */ + } + if (inc_cursor() == -1) { // hit end of file, stop here return FAIL; + } } /* @@ -2657,10 +2702,12 @@ bckend_word ( * Move backward to end of the previous word */ while (cls() == 0) { - if (curwin->w_cursor.col == 0 && lineempty(curwin->w_cursor.lnum)) + if (curwin->w_cursor.col == 0 && LINEEMPTY(curwin->w_cursor.lnum)) { break; - if ((i = dec_cursor()) == -1 || (eol && i == 1)) + } + if ((i = dec_cursor()) == -1 || (eol && i == 1)) { return OK; + } } } return OK; @@ -3557,11 +3604,15 @@ extend: --start_lnum; if (VIsual_active) { - /* Problem: when doing "Vipipip" nothing happens in a single white - * line, we get stuck there. Trap this here. */ - if (VIsual_mode == 'V' && start_lnum == curwin->w_cursor.lnum) + // Problem: when doing "Vipipip" nothing happens in a single white + // line, we get stuck there. Trap this here. + if (VIsual_mode == 'V' && start_lnum == curwin->w_cursor.lnum) { goto extend; - VIsual.lnum = start_lnum; + } + if (VIsual.lnum != start_lnum) { + VIsual.lnum = start_lnum; + VIsual.col = 0; + } VIsual_mode = 'V'; redraw_curbuf_later(INVERTED); /* update the inversion */ showmode(); @@ -3663,11 +3714,25 @@ current_quote ( int selected_quote = FALSE; /* Has quote inside selection */ int i; - /* Correct cursor when 'selection' is exclusive */ + // Correct cursor when 'selection' is "exclusive". if (VIsual_active) { + // this only works within one line + if (VIsual.lnum != curwin->w_cursor.lnum) { + return false; + } + vis_bef_curs = lt(VIsual, curwin->w_cursor); - if (*p_sel == 'e' && vis_bef_curs) + if (*p_sel == 'e') { + if (!vis_bef_curs) { + // VIsual needs to be start of Visual selection. + pos_T t = curwin->w_cursor; + + curwin->w_cursor = VIsual; + VIsual = t; + vis_bef_curs = true; + } dec_cursor(); + } vis_empty = equalpos(VIsual, curwin->w_cursor); } diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 4788b1e7d0..605d9c30a6 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -76,8 +76,8 @@ KHASH_SET_INIT_STR(strset) (vim_rename((char_u *)a, (char_u *)b)) #define mb_strnicmp(a, b, c) \ (mb_strnicmp((char_u *)a, (char_u *)b, c)) -#define path_shorten_fname_if_possible(b) \ - ((char *)path_shorten_fname_if_possible((char_u *)b)) +#define path_try_shorten_fname(b) \ + ((char *)path_try_shorten_fname((char_u *)b)) #define buflist_new(ffname, sfname, ...) \ (buflist_new((char_u *)ffname, (char_u *)sfname, __VA_ARGS__)) #define os_isdir(f) (os_isdir((char_u *) f)) @@ -1180,8 +1180,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) list_T *oldfiles_list = get_vim_var_list(VV_OLDFILES); const bool force = flags & kShaDaForceit; const bool get_old_files = (flags & (kShaDaGetOldfiles | kShaDaForceit) - && (force || oldfiles_list == NULL - || oldfiles_list->lv_len == 0)); + && (force || tv_list_len(oldfiles_list) == 0)); const bool want_marks = flags & kShaDaWantMarks; const unsigned srni_flags = (unsigned) ( (flags & kShaDaWantInfo @@ -1218,7 +1217,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) khash_t(fnamebufs) fname_bufs = KHASH_EMPTY_TABLE(fnamebufs); khash_t(strset) oldfiles_set = KHASH_EMPTY_TABLE(strset); if (get_old_files && (oldfiles_list == NULL || force)) { - oldfiles_list = tv_list_alloc(); + oldfiles_list = tv_list_alloc(kListLenUnknown); set_vim_var_list(VV_OLDFILES, oldfiles_list); } ShaDaReadResult srni_ret; @@ -1398,7 +1397,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) } case kSDItemBufferList: { for (size_t i = 0; i < cur_entry.data.buffer_list.size; i++) { - char *const sfname = path_shorten_fname_if_possible( + char *const sfname = path_try_shorten_fname( cur_entry.data.buffer_list.buffers[i].fname); buf_T *const buf = buflist_new( cur_entry.data.buffer_list.buffers[i].fname, sfname, 0, @@ -1599,13 +1598,13 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, #define DUMP_ADDITIONAL_ELEMENTS(src, what) \ do { \ if ((src) != NULL) { \ - for (listitem_T *li = (src)->lv_first; li != NULL; li = li->li_next) { \ - if (encode_vim_to_msgpack(spacker, &li->li_tv, \ + TV_LIST_ITER((src), li, { \ + if (encode_vim_to_msgpack(spacker, TV_LIST_ITEM_TV(li), \ _("additional elements of ShaDa " what)) \ == FAIL) { \ goto shada_pack_entry_error; \ } \ - } \ + }); \ } \ } while (0) #define DUMP_ADDITIONAL_DATA(src, what) \ @@ -1647,25 +1646,21 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, case kSDItemHistoryEntry: { const bool is_hist_search = entry.data.history_item.histtype == HIST_SEARCH; - const size_t arr_size = 2 + (size_t) is_hist_search + (size_t) ( - entry.data.history_item.additional_elements == NULL - ? 0 - : entry.data.history_item.additional_elements->lv_len); + const size_t arr_size = 2 + (size_t)is_hist_search + (size_t)( + tv_list_len(entry.data.history_item.additional_elements)); msgpack_pack_array(spacker, arr_size); msgpack_pack_uint8(spacker, entry.data.history_item.histtype); PACK_BIN(cstr_as_string(entry.data.history_item.string)); if (is_hist_search) { - msgpack_pack_uint8(spacker, (uint8_t) entry.data.history_item.sep); + msgpack_pack_uint8(spacker, (uint8_t)entry.data.history_item.sep); } DUMP_ADDITIONAL_ELEMENTS(entry.data.history_item.additional_elements, "history entry item"); break; } case kSDItemVariable: { - const size_t arr_size = 2 + (size_t) ( - entry.data.global_var.additional_elements == NULL - ? 0 - : entry.data.global_var.additional_elements->lv_len); + const size_t arr_size = 2 + (size_t)( + tv_list_len(entry.data.global_var.additional_elements)); msgpack_pack_array(spacker, arr_size); const String varname = cstr_as_string(entry.data.global_var.name); PACK_BIN(varname); @@ -1684,10 +1679,8 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, break; } case kSDItemSubString: { - const size_t arr_size = 1 + (size_t) ( - entry.data.sub_string.additional_elements == NULL - ? 0 - : entry.data.sub_string.additional_elements->lv_len); + const size_t arr_size = 1 + (size_t)( + tv_list_len(entry.data.sub_string.additional_elements)); msgpack_pack_array(spacker, arr_size); PACK_BIN(cstr_as_string(entry.data.sub_string.sub)); DUMP_ADDITIONAL_ELEMENTS(entry.data.sub_string.additional_elements, @@ -2564,6 +2557,12 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, xfmark_T fm; jump_iter = mark_jumplist_iter(jump_iter, curwin, &fm); + if (fm.fmark.mark.lnum == 0) { + iemsgf("ShaDa: mark lnum zero (ji:%p, js:%p, len:%i)", + (void *)jump_iter, (void *)&curwin->w_jumplist[0], + curwin->w_jumplistlen); + continue; + } const buf_T *const buf = (fm.fmark.fnum == 0 ? NULL : buflist_findnr(fm.fmark.fnum)); @@ -2993,7 +2992,7 @@ shada_write_file_nomerge: {} } else { if (sw_ret == kSDWriteReadNotShada) { EMSG3(_(RNERR "Did not rename %s because %s " - "does not looks like a ShaDa file"), tempname, fname); + "does not look like a ShaDa file"), tempname, fname); } else { EMSG3(_(RNERR "Did not rename %s to %s because there were errors " "during writing it"), tempname, fname); @@ -3413,8 +3412,16 @@ shada_read_next_item_start: return mru_ret; } - const size_t length = (size_t) length_u64; - entry->timestamp = (Timestamp) timestamp_u64; + if (length_u64 > PTRDIFF_MAX) { + emsgf(_(RCERR "Error while reading ShaDa file: " + "there is an item at position %" PRIu64 " " + "that is stated to be too long"), + initial_fpos); + return kSDReadStatusNotShaDa; + } + + const size_t length = (size_t)length_u64; + entry->timestamp = (Timestamp)timestamp_u64; if (type_u64 == 0) { // kSDItemUnknown cannot possibly pass that far because it is -1 and that diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 25ae562e65..34eb2fdf1b 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -1433,12 +1433,10 @@ spell_move_to ( // the cursor. if (dir == BACKWARD || lnum != wp->w_cursor.lnum - || (lnum == wp->w_cursor.lnum - && (wrapped - || ((colnr_T)(curline - ? p - buf + (ptrdiff_t)len - : p - buf) - > wp->w_cursor.col)))) { + || wrapped + || ((colnr_T)(curline + ? p - buf + (ptrdiff_t)len + : p - buf) > wp->w_cursor.col)) { if (has_syntax) { col = (int)(p - buf); (void)syn_get_id(wp, lnum, (colnr_T)col, @@ -1486,21 +1484,23 @@ spell_move_to ( return found_len; } - if (curline) + if (curline) { break; // only check cursor line + } + + // If we are back at the starting line and searched it again there + // is no match, give up. + if (lnum == wp->w_cursor.lnum && wrapped) { + break; + } // Advance to next line. if (dir == BACKWARD) { - // If we are back at the starting line and searched it again there - // is no match, give up. - if (lnum == wp->w_cursor.lnum && wrapped) - break; - - if (lnum > 1) - --lnum; - else if (!p_ws) + if (lnum > 1) { + lnum--; + } else if (!p_ws) { break; // at first line and 'nowrapscan' - else { + } else { // Wrap around to the end of the buffer. May search the // starting line again and accept the last match. lnum = wp->w_buffer->b_ml.ml_line_count; @@ -1525,8 +1525,9 @@ spell_move_to ( // If we are back at the starting line and there is no match then // give up. - if (lnum == wp->w_cursor.lnum && (!found_one || wrapped)) + if (lnum == wp->w_cursor.lnum && !found_one) { break; + } // Skip the characters at the start of the next line that were // included in a match crossing line boundaries. @@ -2070,7 +2071,7 @@ char_u *did_set_spelllang(win_T *wp) // destroying the buffer we are using... if (!bufref_valid(&bufref)) { ret_msg = - (char_u *)"E797: SpellFileMissing autocommand deleted buffer"; + (char_u *)N_("E797: SpellFileMissing autocommand deleted buffer"); goto theend; } } @@ -2567,7 +2568,7 @@ static bool spell_iswordp(char_u *p, win_T *wp) int c; if (has_mbyte) { - l = MB_BYTE2LEN(*p); + l = MB_PTR2LEN(p); s = p; if (l == 1) { // be quick for ASCII @@ -3138,8 +3139,13 @@ spell_find_suggest ( if (su->su_badlen >= MAXWLEN) su->su_badlen = MAXWLEN - 1; // just in case STRLCPY(su->su_badword, su->su_badptr, su->su_badlen + 1); - (void)spell_casefold(su->su_badptr, su->su_badlen, - su->su_fbadword, MAXWLEN); + (void)spell_casefold(su->su_badptr, su->su_badlen, su->su_fbadword, MAXWLEN); + + // TODO(vim): make this work if the case-folded text is longer than the + // original text. Currently an illegal byte causes wrong pointer + // computations. + su->su_fbadword[su->su_badlen] = NUL; + // get caps flags for bad word su->su_badflags = badword_captype(su->su_badptr, su->su_badptr + su->su_badlen); @@ -3215,26 +3221,25 @@ spell_find_suggest ( // Find suggestions by evaluating expression "expr". static void spell_suggest_expr(suginfo_T *su, char_u *expr) { - list_T *list; - listitem_T *li; int score; const char *p; // The work is split up in a few parts to avoid having to export // suginfo_T. // First evaluate the expression and get the resulting list. - list = eval_spell_expr(su->su_badword, expr); + list_T *const list = eval_spell_expr(su->su_badword, expr); if (list != NULL) { // Loop over the items in the list. - for (li = list->lv_first; li != NULL; li = li->li_next) - if (li->li_tv.v_type == VAR_LIST) { + TV_LIST_ITER(list, li, { + if (TV_LIST_ITEM_TV(li)->v_type == VAR_LIST) { // Get the word and the score from the items. - score = get_spellword(li->li_tv.vval.v_list, &p); + score = get_spellword(TV_LIST_ITEM_TV(li)->vval.v_list, &p); if (score >= 0 && score <= su->su_maxscore) { add_suggestion(su, &su->su_ga, (const char_u *)p, su->su_badlen, score, 0, true, su->su_sallang, false); } } + }); tv_list_unref(list); } @@ -3635,7 +3640,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // word). depth = 0; sp = &stack[0]; - memset(sp, 0, sizeof(trystate_T)); + memset(sp, 0, sizeof(trystate_T)); // -V512 sp->ts_curi = 1; if (soundfold) { @@ -4110,10 +4115,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so && goodword_ends) { int l; - if (has_mbyte) - l = MB_BYTE2LEN(fword[sp->ts_fidx]); - else - l = 1; + l = MB_PTR2LEN(fword + sp->ts_fidx); if (fword_ends) { // Copy the skipped character to preword. memmove(preword + sp->ts_prewordlen, @@ -4259,8 +4261,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // Correct ts_fidx for the byte length of the // character (we didn't check that before). sp->ts_fidx = sp->ts_fcharstart - + MB_BYTE2LEN( - fword[sp->ts_fcharstart]); + + MB_PTR2LEN(fword + sp->ts_fcharstart); // For changing a composing character adjust // the score from SCORE_SUBST to @@ -4366,11 +4367,12 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // results. if (has_mbyte) { c = mb_ptr2char(fword + sp->ts_fidx); - stack[depth].ts_fidx += MB_BYTE2LEN(fword[sp->ts_fidx]); - if (enc_utf8 && utf_iscomposing(c)) + stack[depth].ts_fidx += MB_PTR2LEN(fword + sp->ts_fidx); + if (enc_utf8 && utf_iscomposing(c)) { stack[depth].ts_score -= SCORE_DEL - SCORE_DELCOMP; - else if (c == mb_ptr2char(fword + stack[depth].ts_fidx)) + } else if (c == mb_ptr2char(fword + stack[depth].ts_fidx)) { stack[depth].ts_score -= SCORE_DEL - SCORE_DELDUP; + } } else { ++stack[depth].ts_fidx; if (fword[sp->ts_fidx] == fword[sp->ts_fidx + 1]) @@ -4552,9 +4554,9 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // Undo the STATE_SWAP swap: "21" -> "12". p = fword + sp->ts_fidx; if (has_mbyte) { - n = MB_BYTE2LEN(*p); + n = MB_PTR2LEN(p); c = mb_ptr2char(p + n); - memmove(p + MB_BYTE2LEN(p[n]), p, n); + memmove(p + MB_PTR2LEN(p + n), p, n); mb_char2bytes(c, p); } else { c = *p; @@ -4627,11 +4629,11 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // Undo STATE_SWAP3: "321" -> "123" p = fword + sp->ts_fidx; if (has_mbyte) { - n = MB_BYTE2LEN(*p); + n = MB_PTR2LEN(p); c2 = mb_ptr2char(p + n); - fl = MB_BYTE2LEN(p[n]); + fl = MB_PTR2LEN(p + n); c = mb_ptr2char(p + n + fl); - tl = MB_BYTE2LEN(p[n + fl]); + tl = MB_PTR2LEN(p + n + fl); memmove(p + fl + tl, p, n); mb_char2bytes(c, p); mb_char2bytes(c2, p + tl); @@ -4690,10 +4692,10 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // Undo ROT3L: "231" -> "123" p = fword + sp->ts_fidx; if (has_mbyte) { - n = MB_BYTE2LEN(*p); - n += MB_BYTE2LEN(p[n]); + n = MB_PTR2LEN(p); + n += MB_PTR2LEN(p + n); c = mb_ptr2char(p + n); - tl = MB_BYTE2LEN(p[n]); + tl = MB_PTR2LEN(p + n); memmove(p + tl, p, n); mb_char2bytes(c, p); } else { @@ -4743,9 +4745,9 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so p = fword + sp->ts_fidx; if (has_mbyte) { c = mb_ptr2char(p); - tl = MB_BYTE2LEN(*p); - n = MB_BYTE2LEN(p[tl]); - n += MB_BYTE2LEN(p[tl + n]); + tl = MB_PTR2LEN(p); + n = MB_PTR2LEN(p + tl); + n += MB_PTR2LEN(p + tl + n); memmove(p, p + tl, n); mb_char2bytes(c, p + n); } else { @@ -5357,7 +5359,7 @@ add_sound_suggest ( // Find the word nr in the soundfold tree. sfwordnr = soundfold_find(slang, goodword); if (sfwordnr < 0) { - EMSG2(_(e_intern2), "add_sound_suggest()"); + internal_error("add_sound_suggest()"); return; } @@ -7145,7 +7147,7 @@ void ex_spelldump(exarg_T *eap) set_option_value("spl", dummy, (char *)spl, OPT_LOCAL); xfree(spl); - if (!bufempty()) { + if (!BUFEMPTY()) { return; } diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index 1f7f616782..f5d5d408a1 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -228,7 +228,6 @@ #include <stdio.h> #include <stdint.h> #include <wctype.h> -#include <strings.h> #include "nvim/vim.h" #include "nvim/spell_defs.h" @@ -3656,7 +3655,7 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *fname) flags |= WF_REGION; l = *p - '0'; - if (l > spin->si_region_count) { + if (l == 0 || l > spin->si_region_count) { smsg(_("Invalid region nr in %s line %d: %s"), fname, lnum, p); break; diff --git a/src/nvim/state.c b/src/nvim/state.c index be6aa21664..a4b394c9e4 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -6,6 +6,7 @@ #include "nvim/lib/kvec.h" #include "nvim/ascii.h" +#include "nvim/log.h" #include "nvim/state.h" #include "nvim/vim.h" #include "nvim/main.h" @@ -13,6 +14,8 @@ #include "nvim/option_defs.h" #include "nvim/ui.h" #include "nvim/os/input.h" +#include "nvim/ex_docmd.h" +#include "nvim/edit.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "state.c.generated.h" @@ -25,10 +28,11 @@ void state_enter(VimState *s) int check_result = s->check ? s->check(s) : 1; if (!check_result) { - break; + break; // Terminate this state. } else if (check_result == -1) { - continue; + continue; // check() again. } + // Execute this state. int key; @@ -47,11 +51,13 @@ getkey: ui_flush(); // Call `os_inchar` directly to block for events or user input without // consuming anything from `input_buffer`(os/input.c) or calling the - // mapping engine. If an event was put into the queue, we send K_EVENT - // directly. + // mapping engine. (void)os_inchar(NULL, 0, -1, 0); input_disable_events(); - key = !multiqueue_empty(main_loop.events) ? K_EVENT : safe_vgetc(); + // If an event was put into the queue, we send K_EVENT directly. + key = !multiqueue_empty(main_loop.events) + ? K_EVENT + : safe_vgetc(); } if (key == K_EVENT) { @@ -123,19 +129,25 @@ char *get_mode(void) if (State & VREPLACE_FLAG) { buf[0] = 'R'; buf[1] = 'v'; - } else if (State & REPLACE_FLAG) { - buf[0] = 'R'; } else { - buf[0] = 'i'; + if (State & REPLACE_FLAG) { + buf[0] = 'R'; + } else { + buf[0] = 'i'; + } + if (ins_compl_active()) { + buf[1] = 'c'; + } else if (ctrl_x_mode == 1) { + buf[1] = 'x'; + } } - } else if (State & CMDLINE) { + } else if ((State & CMDLINE) || exmode_active) { buf[0] = 'c'; - if (exmode_active) { + if (exmode_active == EXMODE_VIM) { buf[1] = 'v'; + } else if (exmode_active == EXMODE_NORMAL) { + buf[1] = 'e'; } - } else if (exmode_active) { - buf[0] = 'c'; - buf[1] = 'e'; } else if (State & TERM_FOCUS) { buf[0] = 't'; } else { diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 687f734742..e3f6a8cbf6 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -50,6 +50,13 @@ #include "nvim/os/shell.h" #include "nvim/eval/encode.h" +#ifdef __MINGW32__ +# undef fpclassify +# define fpclassify __fpclassify +# undef isnan +# define isnan _isnan +#endif + /* * Copy "string" into newly allocated memory. */ diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index a4bb260183..8ec393e568 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -42,6 +42,7 @@ #include "nvim/ui.h" #include "nvim/os/os.h" #include "nvim/os/time.h" +#include "nvim/api/private/helpers.h" static bool did_syntax_onoff = false; @@ -51,6 +52,7 @@ static bool did_syntax_onoff = false; struct hl_group { char_u *sg_name; ///< highlight group name char_u *sg_name_u; ///< uppercase of sg_name + int sg_cleared; ///< "hi clear" was used int sg_attr; ///< Screen attr @see ATTR_ENTRY int sg_link; ///< link to this highlight group ID int sg_set; ///< combination of flags in \ref SG_SET @@ -62,7 +64,7 @@ struct hl_group { int sg_cterm_bold; ///< bold attr was set for light color // for RGB UIs int sg_gui; ///< "gui=" highlighting attributes - ///< (combination of \ref HL_ATTRIBUTES) + ///< (combination of \ref HlAttrFlags) RgbValue sg_rgb_fg; ///< RGB foreground color RgbValue sg_rgb_bg; ///< RGB background color RgbValue sg_rgb_sp; ///< RGB special color @@ -78,10 +80,13 @@ struct hl_group { #define SG_LINK 8 // link has been set /// @} -// highlight groups for 'highlight' option +// builtin |highlight-groups| static garray_T highlight_ga = GA_EMPTY_INIT_VALUE; -#define HL_TABLE() ((struct hl_group *)((highlight_ga.ga_data))) +static inline struct hl_group * HL_TABLE(void) +{ + return ((struct hl_group *)((highlight_ga.ga_data))); +} #define MAX_HL_ID 20000 /* maximum value for a highlight ID. */ @@ -100,10 +105,8 @@ static int include_none = 0; /* when 1 include "nvim/None" */ static int include_default = 0; /* when 1 include "nvim/default" */ static int include_link = 0; /* when 2 include "nvim/link" and "clear" */ -/* - * The "term", "cterm" and "gui" arguments can be any combination of the - * following names, separated by commas (but no spaces!). - */ +/// The "term", "cterm" and "gui" arguments can be any combination of the +/// following names, separated by commas (but no spaces!). static char *(hl_name_table[]) = {"bold", "standout", "underline", "undercurl", "italic", "reverse", "inverse", "NONE"}; @@ -878,7 +881,8 @@ static void syn_start_line(void) } next_match_idx = -1; - ++current_line_id; + current_line_id++; + next_seqnr = 1; } /* @@ -1596,6 +1600,7 @@ get_syntax_attr ( current_id = 0; current_trans_id = 0; current_flags = 0; + current_seqnr = 0; return 0; } @@ -1666,8 +1671,9 @@ syn_current_attr ( * If we found a match after the last column, use it. */ if (next_match_idx >= 0 && next_match_col >= (int)current_col - && next_match_col != MAXCOL) - (void)push_next_match(NULL); + && next_match_col != MAXCOL) { + (void)push_next_match(); + } current_finished = TRUE; current_state_stored = FALSE; @@ -1774,8 +1780,9 @@ syn_current_attr ( cur_si->si_trans_id = CUR_STATE( current_state.ga_len - 2).si_trans_id; } - } else + } else { cur_si->si_attr = syn_id2attr(syn_id); + } cur_si->si_cont_list = NULL; cur_si->si_next_list = next_list; check_keepend(); @@ -1985,9 +1992,10 @@ syn_current_attr ( * endless loop). */ GA_APPEND(int, &zero_width_next_ga, next_match_idx); next_match_idx = -1; - } else - cur_si = push_next_match(cur_si); - found_match = TRUE; + } else { + cur_si = push_next_match(); + } + found_match = true; } } } @@ -2036,6 +2044,7 @@ syn_current_attr ( current_id = 0; current_trans_id = 0; current_flags = 0; + current_seqnr = 0; if (cur_si != NULL) { for (int idx = current_state.ga_len - 1; idx >= 0; --idx) { sip = &CUR_STATE(idx); @@ -2124,9 +2133,11 @@ syn_current_attr ( /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */ if (current_next_list != NULL - && syn_getcurline()[current_col + 1] == NUL - && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))) + && (line = syn_getcurline())[current_col] != NUL + && line[current_col + 1] == NUL + && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))) { current_next_list = NULL; + } if (!GA_EMPTY(&zero_width_next_ga)) ga_clear(&zero_width_next_ga); @@ -2167,9 +2178,10 @@ static int did_match_already(int idx, garray_T *gap) /* * Push the next match onto the stack. */ -static stateitem_T *push_next_match(stateitem_T *cur_si) +static stateitem_T *push_next_match(void) { - synpat_T *spp; + stateitem_T *cur_si; + synpat_T *spp; int save_flags; spp = &(SYN_ITEMS(syn_block)[next_match_idx]); @@ -3013,12 +3025,19 @@ static void syn_cmd_conceal(exarg_T *eap, int syncing) return; next = skiptowhite(arg); - if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2) - curwin->w_s->b_syn_conceal = TRUE; - else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3) - curwin->w_s->b_syn_conceal = FALSE; - else + if (*arg == NUL) { + if (curwin->w_s->b_syn_conceal) { + MSG(_("syn conceal on")); + } else { + MSG(_("syn conceal off")); + } + } else if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2) { + curwin->w_s->b_syn_conceal = true; + } else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3) { + curwin->w_s->b_syn_conceal = false; + } else { EMSG2(_("E390: Illegal argument: %s"), arg); + } } /* @@ -3034,12 +3053,19 @@ static void syn_cmd_case(exarg_T *eap, int syncing) return; next = skiptowhite(arg); - if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5) - curwin->w_s->b_syn_ic = FALSE; - else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6) - curwin->w_s->b_syn_ic = TRUE; - else + if (*arg == NUL) { + if (curwin->w_s->b_syn_ic) { + MSG(_("syntax case ignore")); + } else { + MSG(_("syntax case match")); + } + } else if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5) { + curwin->w_s->b_syn_ic = false; + } else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6) { + curwin->w_s->b_syn_ic = true; + } else { EMSG2(_("E390: Illegal argument: %s"), arg); + } } /* @@ -3055,7 +3081,15 @@ static void syn_cmd_spell(exarg_T *eap, int syncing) return; next = skiptowhite(arg); - if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8) { + if (*arg == NUL) { + if (curwin->w_s->b_syn_spell == SYNSPL_TOP) { + MSG(_("syntax spell toplevel")); + } else if (curwin->w_s->b_syn_spell == SYNSPL_NOTOP) { + MSG(_("syntax spell notoplevel")); + } else { + MSG(_("syntax spell default")); + } + } else if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8) { curwin->w_s->b_syn_spell = SYNSPL_TOP; } else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10) { curwin->w_s->b_syn_spell = SYNSPL_NOTOP; @@ -3115,10 +3149,11 @@ static void syn_cmd_iskeyword(exarg_T *eap, int syncing) */ void syntax_clear(synblock_T *block) { - block->b_syn_error = FALSE; /* clear previous error */ - block->b_syn_ic = FALSE; /* Use case, by default */ - block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */ - block->b_syn_containedin = FALSE; + block->b_syn_error = false; // clear previous error + block->b_syn_ic = false; // Use case, by default + block->b_syn_spell = SYNSPL_DEFAULT; // default spell checking + block->b_syn_containedin = false; + block->b_syn_conceal = false; /* free the keywords */ clear_keywtab(&block->b_keywtab); @@ -3995,18 +4030,19 @@ get_group_name ( * Return NULL for any error; */ static char_u * -get_syn_options ( - char_u *arg, /* next argument to be checked */ - syn_opt_arg_T *opt, /* various things */ - int *conceal_char +get_syn_options( + char_u *arg, // next argument to be checked + syn_opt_arg_T *opt, // various things + int *conceal_char, + int skip // TRUE if skipping over command ) { char_u *gname_start, *gname; int syn_id; - int len; + int len = 0; char *p; int fidx; - static struct flag { + static const struct flag { char *name; int argtype; int flags; @@ -4029,7 +4065,7 @@ get_syn_options ( {"cCoOnNtTaAiInNsS", 1, 0}, {"cCoOnNtTaAiInNeEdDiInN", 2, 0}, {"nNeExXtTgGrRoOuUpP", 3, 0},}; - static char *first_letters = "cCoOkKeEtTsSgGdDfFnN"; + static const char *const first_letters = "cCoOkKeEtTsSgGdDfFnN"; if (arg == NULL) /* already detected error */ return NULL; @@ -4049,9 +4085,10 @@ get_syn_options ( for (fidx = ARRAY_SIZE(flagtab); --fidx >= 0; ) { p = flagtab[fidx].name; int i; - for (i = 0, len = 0; p[i] != NUL; i += 2, ++len) + for (i = 0, len = 0; p[i] != NUL; i += 2, ++len) { if (arg[len] != p[i] && arg[len] != p[i + 1]) break; + } if (p[i] == NUL && (ascii_iswhite(arg[len]) || (flagtab[fidx].argtype > 0 ? arg[len] == '=' @@ -4073,14 +4110,17 @@ get_syn_options ( EMSG(_("E395: contains argument not accepted here")); return NULL; } - if (get_id_list(&arg, 8, &opt->cont_list) == FAIL) + if (get_id_list(&arg, 8, &opt->cont_list, skip) == FAIL) { return NULL; + } } else if (flagtab[fidx].argtype == 2) { - if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL) + if (get_id_list(&arg, 11, &opt->cont_in_list, skip) == FAIL) { return NULL; + } } else if (flagtab[fidx].argtype == 3) { - if (get_id_list(&arg, 9, &opt->next_list) == FAIL) + if (get_id_list(&arg, 9, &opt->next_list, skip) == FAIL) { return NULL; + } } else if (flagtab[fidx].argtype == 11 && arg[5] == '=') { /* cchar=? */ if (has_mbyte) { @@ -4199,11 +4239,11 @@ static void syn_cmd_include(exarg_T *eap, int syncing) */ eap->argt |= (XFILE | NOSPC); separate_nextcmd(eap); - if (*eap->arg == '<' || *eap->arg == '$' || path_is_absolute_path(eap->arg)) { - /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the - * file. Need to expand the file name first. In other cases - * ":runtime!" is used. */ - source = TRUE; + if (*eap->arg == '<' || *eap->arg == '$' || path_is_absolute(eap->arg)) { + // For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the + // file. Need to expand the file name first. In other cases + // ":runtime!" is used. + source = true; if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL) { if (errormsg != NULL) EMSG(errormsg); @@ -4250,7 +4290,11 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing) rest = get_group_name(arg, &group_name_end); if (rest != NULL) { - syn_id = syn_check_group(arg, (int)(group_name_end - arg)); + if (eap->skip) { + syn_id = -1; + } else { + syn_id = syn_check_group(arg, (int)(group_name_end - arg)); + } if (syn_id != 0) { // Allocate a buffer, for removing backslashes in the keyword. keyword_copy = xmalloc(STRLEN(rest) + 1); @@ -4269,7 +4313,7 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing) cnt = 0; p = keyword_copy; for (; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest)) { - rest = get_syn_options(rest, &syn_opt_arg, &conceal_char); + rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip); if (rest == NULL || ends_excmd(*rest)) { break; } @@ -4368,17 +4412,18 @@ syn_cmd_match ( syn_opt_arg.cont_list = NULL; syn_opt_arg.cont_in_list = NULL; syn_opt_arg.next_list = NULL; - rest = get_syn_options(rest, &syn_opt_arg, &conceal_char); + rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip); /* get the pattern. */ init_syn_patterns(); memset(&item, 0, sizeof(item)); rest = get_syn_pattern(rest, &item); - if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL)) + if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL)) { syn_opt_arg.flags |= HL_HAS_EOL; + } - /* Get options after the pattern */ - rest = get_syn_options(rest, &syn_opt_arg, &conceal_char); + // Get options after the pattern + rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip); if (rest != NULL) { /* all arguments are valid */ /* @@ -4490,14 +4535,13 @@ syn_cmd_region ( syn_opt_arg.cont_in_list = NULL; syn_opt_arg.next_list = NULL; - /* - * get the options, patterns and matchgroup. - */ + // get the options, patterns and matchgroup. while (rest != NULL && !ends_excmd(*rest)) { - /* Check for option arguments */ - rest = get_syn_options(rest, &syn_opt_arg, &conceal_char); - if (rest == NULL || ends_excmd(*rest)) + // Check for option arguments + rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip); + if (rest == NULL || ends_excmd(*rest)) { break; + } /* must be a pattern or matchgroup then */ key_end = rest; @@ -4919,13 +4963,17 @@ static void syn_cmd_cluster(exarg_T *eap, int syncing) break; clstr_list = NULL; - if (get_id_list(&rest, opt_len, &clstr_list) == FAIL) { + if (get_id_list(&rest, opt_len, &clstr_list, eap->skip) == FAIL) { EMSG2(_(e_invarg2), rest); break; } - syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list, - &clstr_list, list_op); - got_clstr = TRUE; + if (scl_id >= 0) { + syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list, + &clstr_list, list_op); + } else { + xfree(clstr_list); + } + got_clstr = true; } if (got_clstr) { @@ -5173,9 +5221,10 @@ static void syn_cmd_sync(exarg_T *eap, int syncing) static int get_id_list ( char_u **arg, - int keylen, /* length of keyword */ - short **list /* where to store the resulting list, if not - NULL, the list is silently skipped! */ + int keylen, // length of keyword + int16_t **list, // where to store the resulting list, if not + // NULL, the list is silently skipped! + int skip ) { char_u *p = NULL; @@ -5231,8 +5280,9 @@ get_id_list ( break; } if (count != 0) { - EMSG2(_("E408: %s must be first in contains list"), name + 1); - failed = TRUE; + EMSG2(_("E408: %s must be first in contains list"), + name + 1); + failed = true; xfree(name); break; } @@ -5244,17 +5294,19 @@ get_id_list ( id = SYNID_CONTAINED; id += current_syn_inc_tag; } else if (name[1] == '@') { - id = syn_check_cluster(name + 2, (int)(end - p - 1)); + if (skip) { + id = -1; + } else { + id = syn_check_cluster(name + 2, (int)(end - p - 1)); + } } else { /* * Handle full group name. */ - if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL) + if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL) { id = syn_check_group(name + 1, (int)(end - p)); - else { - /* - * Handle match of regexp with group names. - */ + } else { + // Handle match of regexp with group names. *name = '^'; STRCAT(name, "$"); regmatch.regprog = vim_regcomp(name, RE_MAGIC); @@ -5564,8 +5616,10 @@ bool syntax_present(win_T *win) static enum { - EXP_SUBCMD, /* expand ":syn" sub-commands */ - EXP_CASE /* expand ":syn case" arguments */ + EXP_SUBCMD, // expand ":syn" sub-commands + EXP_CASE, // expand ":syn case" arguments + EXP_SPELL, // expand ":syn spell" arguments + EXP_SYNC // expand ":syn sync" arguments } expand_what; /* @@ -5609,6 +5663,10 @@ void set_context_in_syntax_cmd(expand_T *xp, const char *arg) xp->xp_context = EXPAND_NOTHING; } else if (STRNICMP(arg, "case", p - arg) == 0) { expand_what = EXP_CASE; + } else if (STRNICMP(arg, "spell", p - arg) == 0) { + expand_what = EXP_SPELL; + } else if (STRNICMP(arg, "sync", p - arg) == 0) { + expand_what = EXP_SYNC; } else if (STRNICMP(arg, "keyword", p - arg) == 0 || STRNICMP(arg, "region", p - arg) == 0 || STRNICMP(arg, "match", p - arg) == 0 @@ -5621,17 +5679,33 @@ void set_context_in_syntax_cmd(expand_T *xp, const char *arg) } } -static char *(case_args[]) = {"match", "ignore", NULL}; - /* * Function given to ExpandGeneric() to obtain the list syntax names for * expansion. */ char_u *get_syntax_name(expand_T *xp, int idx) { - if (expand_what == EXP_SUBCMD) - return (char_u *)subcommands[idx].name; - return (char_u *)case_args[idx]; + switch (expand_what) { + case EXP_SUBCMD: + return (char_u *)subcommands[idx].name; + case EXP_CASE: { + static char *case_args[] = { "match", "ignore", NULL }; + return (char_u *)case_args[idx]; + } + case EXP_SPELL: { + static char *spell_args[] = + { "toplevel", "notoplevel", "default", NULL }; + return (char_u *)spell_args[idx]; + } + case EXP_SYNC: { + static char *sync_args[] = + { "ccomment", "clear", "fromstart", + "linebreaks=", "linecont", "lines=", "match", + "maxlines=", "minlines=", "region", NULL }; + return (char_u *)sync_args[idx]; + } + } + return NULL; } @@ -5842,9 +5916,12 @@ static void syntime_report(void) } } - /* sort on total time */ - qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T), - syn_compare_syntime); + // Sort on total time. Skip if there are no items to avoid passing NULL + // pointer to qsort(). + if (ga.ga_len > 1) { + qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T), + syn_compare_syntime); + } MSG_PUTS_TITLE(_( " TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN")); @@ -5900,9 +5977,9 @@ static void syntime_report(void) // // When making changes here, also change runtime/colors/default.vim! -static char *highlight_init_both[] = -{ - "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey", +static const char *highlight_init_both[] = { + "Conceal " + "ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey", "Cursor guibg=fg guifg=bg", "lCursor guibg=fg guifg=bg", "DiffText cterm=bold ctermbg=Red gui=bold guibg=Red", @@ -5925,8 +6002,7 @@ static char *highlight_init_both[] = NULL }; -static char *highlight_init_light[] = -{ +static const char *highlight_init_light[] = { "ColorColumn ctermbg=LightRed guibg=LightRed", "CursorColumn ctermbg=LightGrey guibg=Grey90", "CursorLine cterm=underline guibg=Grey90", @@ -5955,11 +6031,11 @@ static char *highlight_init_light[] = "Title ctermfg=DarkMagenta gui=bold guifg=Magenta", "Visual guibg=LightGrey", "WarningMsg ctermfg=DarkRed guifg=Red", + "Normal gui=NONE", NULL }; -static char *highlight_init_dark[] = -{ +static const char *highlight_init_dark[] = { "ColorColumn ctermbg=DarkRed guibg=DarkRed", "CursorColumn ctermbg=DarkGrey guibg=Grey40", "CursorLine cterm=underline guibg=Grey40", @@ -5988,23 +6064,230 @@ static char *highlight_init_dark[] = "Title ctermfg=LightMagenta gui=bold guifg=Magenta", "Visual guibg=DarkGrey", "WarningMsg ctermfg=LightRed guifg=Red", + "Normal gui=NONE", NULL }; -void -init_highlight ( - int both, /* include groups where 'bg' doesn't matter */ - int reset /* clear group first */ -) +const char *const highlight_init_cmdline[] = { + // XXX When modifying a list modify it in both valid and invalid halfs. + // TODO(ZyX-I): merge valid and invalid groups via a macros. + + // NvimInternalError should appear only when highlighter has a bug. + "NvimInternalError ctermfg=Red ctermbg=Red guifg=Red guibg=Red", + + // Highlight groups (links) used by parser: + + "default link NvimAssignment Operator", + "default link NvimPlainAssignment NvimAssignment", + "default link NvimAugmentedAssignment NvimAssignment", + "default link NvimAssignmentWithAddition NvimAugmentedAssignment", + "default link NvimAssignmentWithSubtraction NvimAugmentedAssignment", + "default link NvimAssignmentWithConcatenation NvimAugmentedAssignment", + + "default link NvimOperator Operator", + + "default link NvimUnaryOperator NvimOperator", + "default link NvimUnaryPlus NvimUnaryOperator", + "default link NvimUnaryMinus NvimUnaryOperator", + "default link NvimNot NvimUnaryOperator", + + "default link NvimBinaryOperator NvimOperator", + "default link NvimComparison NvimBinaryOperator", + "default link NvimComparisonModifier NvimComparison", + "default link NvimBinaryPlus NvimBinaryOperator", + "default link NvimBinaryMinus NvimBinaryOperator", + "default link NvimConcat NvimBinaryOperator", + "default link NvimConcatOrSubscript NvimConcat", + "default link NvimOr NvimBinaryOperator", + "default link NvimAnd NvimBinaryOperator", + "default link NvimMultiplication NvimBinaryOperator", + "default link NvimDivision NvimBinaryOperator", + "default link NvimMod NvimBinaryOperator", + + "default link NvimTernary NvimOperator", + "default link NvimTernaryColon NvimTernary", + + "default link NvimParenthesis Delimiter", + "default link NvimLambda NvimParenthesis", + "default link NvimNestingParenthesis NvimParenthesis", + "default link NvimCallingParenthesis NvimParenthesis", + + "default link NvimSubscript NvimParenthesis", + "default link NvimSubscriptBracket NvimSubscript", + "default link NvimSubscriptColon NvimSubscript", + "default link NvimCurly NvimSubscript", + + "default link NvimContainer NvimParenthesis", + "default link NvimDict NvimContainer", + "default link NvimList NvimContainer", + + "default link NvimIdentifier Identifier", + "default link NvimIdentifierScope NvimIdentifier", + "default link NvimIdentifierScopeDelimiter NvimIdentifier", + "default link NvimIdentifierName NvimIdentifier", + "default link NvimIdentifierKey NvimIdentifier", + + "default link NvimColon Delimiter", + "default link NvimComma Delimiter", + "default link NvimArrow Delimiter", + + "default link NvimRegister SpecialChar", + "default link NvimNumber Number", + "default link NvimFloat NvimNumber", + "default link NvimNumberPrefix Type", + + "default link NvimOptionSigil Type", + "default link NvimOptionName NvimIdentifier", + "default link NvimOptionScope NvimIdentifierScope", + "default link NvimOptionScopeDelimiter NvimIdentifierScopeDelimiter", + + "default link NvimEnvironmentSigil NvimOptionSigil", + "default link NvimEnvironmentName NvimIdentifier", + + "default link NvimString String", + "default link NvimStringBody NvimString", + "default link NvimStringQuote NvimString", + "default link NvimStringSpecial SpecialChar", + + "default link NvimSingleQuote NvimStringQuote", + "default link NvimSingleQuotedBody NvimStringBody", + "default link NvimSingleQuotedQuote NvimStringSpecial", + + "default link NvimDoubleQuote NvimStringQuote", + "default link NvimDoubleQuotedBody NvimStringBody", + "default link NvimDoubleQuotedEscape NvimStringSpecial", + + "default link NvimFigureBrace NvimInternalError", + "default link NvimSingleQuotedUnknownEscape NvimInternalError", + + "default link NvimSpacing Normal", + + // NvimInvalid groups: + + "default link NvimInvalidSingleQuotedUnknownEscape NvimInternalError", + + "default link NvimInvalid Error", + + "default link NvimInvalidAssignment NvimInvalid", + "default link NvimInvalidPlainAssignment NvimInvalidAssignment", + "default link NvimInvalidAugmentedAssignment NvimInvalidAssignment", + "default link NvimInvalidAssignmentWithAddition " + "NvimInvalidAugmentedAssignment", + "default link NvimInvalidAssignmentWithSubtraction " + "NvimInvalidAugmentedAssignment", + "default link NvimInvalidAssignmentWithConcatenation " + "NvimInvalidAugmentedAssignment", + + "default link NvimInvalidOperator NvimInvalid", + + "default link NvimInvalidUnaryOperator NvimInvalidOperator", + "default link NvimInvalidUnaryPlus NvimInvalidUnaryOperator", + "default link NvimInvalidUnaryMinus NvimInvalidUnaryOperator", + "default link NvimInvalidNot NvimInvalidUnaryOperator", + + "default link NvimInvalidBinaryOperator NvimInvalidOperator", + "default link NvimInvalidComparison NvimInvalidBinaryOperator", + "default link NvimInvalidComparisonModifier NvimInvalidComparison", + "default link NvimInvalidBinaryPlus NvimInvalidBinaryOperator", + "default link NvimInvalidBinaryMinus NvimInvalidBinaryOperator", + "default link NvimInvalidConcat NvimInvalidBinaryOperator", + "default link NvimInvalidConcatOrSubscript NvimInvalidConcat", + "default link NvimInvalidOr NvimInvalidBinaryOperator", + "default link NvimInvalidAnd NvimInvalidBinaryOperator", + "default link NvimInvalidMultiplication NvimInvalidBinaryOperator", + "default link NvimInvalidDivision NvimInvalidBinaryOperator", + "default link NvimInvalidMod NvimInvalidBinaryOperator", + + "default link NvimInvalidTernary NvimInvalidOperator", + "default link NvimInvalidTernaryColon NvimInvalidTernary", + + "default link NvimInvalidDelimiter NvimInvalid", + + "default link NvimInvalidParenthesis NvimInvalidDelimiter", + "default link NvimInvalidLambda NvimInvalidParenthesis", + "default link NvimInvalidNestingParenthesis NvimInvalidParenthesis", + "default link NvimInvalidCallingParenthesis NvimInvalidParenthesis", + + "default link NvimInvalidSubscript NvimInvalidParenthesis", + "default link NvimInvalidSubscriptBracket NvimInvalidSubscript", + "default link NvimInvalidSubscriptColon NvimInvalidSubscript", + "default link NvimInvalidCurly NvimInvalidSubscript", + + "default link NvimInvalidContainer NvimInvalidParenthesis", + "default link NvimInvalidDict NvimInvalidContainer", + "default link NvimInvalidList NvimInvalidContainer", + + "default link NvimInvalidValue NvimInvalid", + + "default link NvimInvalidIdentifier NvimInvalidValue", + "default link NvimInvalidIdentifierScope NvimInvalidIdentifier", + "default link NvimInvalidIdentifierScopeDelimiter NvimInvalidIdentifier", + "default link NvimInvalidIdentifierName NvimInvalidIdentifier", + "default link NvimInvalidIdentifierKey NvimInvalidIdentifier", + + "default link NvimInvalidColon NvimInvalidDelimiter", + "default link NvimInvalidComma NvimInvalidDelimiter", + "default link NvimInvalidArrow NvimInvalidDelimiter", + + "default link NvimInvalidRegister NvimInvalidValue", + "default link NvimInvalidNumber NvimInvalidValue", + "default link NvimInvalidFloat NvimInvalidNumber", + "default link NvimInvalidNumberPrefix NvimInvalidNumber", + + "default link NvimInvalidOptionSigil NvimInvalidIdentifier", + "default link NvimInvalidOptionName NvimInvalidIdentifier", + "default link NvimInvalidOptionScope NvimInvalidIdentifierScope", + "default link NvimInvalidOptionScopeDelimiter " + "NvimInvalidIdentifierScopeDelimiter", + + "default link NvimInvalidEnvironmentSigil NvimInvalidOptionSigil", + "default link NvimInvalidEnvironmentName NvimInvalidIdentifier", + + // Invalid string bodies and specials are still highlighted as valid ones to + // minimize the red area. + "default link NvimInvalidString NvimInvalidValue", + "default link NvimInvalidStringBody NvimStringBody", + "default link NvimInvalidStringQuote NvimInvalidString", + "default link NvimInvalidStringSpecial NvimStringSpecial", + + "default link NvimInvalidSingleQuote NvimInvalidStringQuote", + "default link NvimInvalidSingleQuotedBody NvimInvalidStringBody", + "default link NvimInvalidSingleQuotedQuote NvimInvalidStringSpecial", + + "default link NvimInvalidDoubleQuote NvimInvalidStringQuote", + "default link NvimInvalidDoubleQuotedBody NvimInvalidStringBody", + "default link NvimInvalidDoubleQuotedEscape NvimInvalidStringSpecial", + "default link NvimInvalidDoubleQuotedUnknownEscape NvimInvalidValue", + + "default link NvimInvalidFigureBrace NvimInvalidDelimiter", + + "default link NvimInvalidSpacing ErrorMsg", + + // Not actually invalid, but we highlight user that he is doing something + // wrong. + "default link NvimDoubleQuotedUnknownEscape NvimInvalidValue", + NULL, +}; + +/// Create default links for Nvim* highlight groups used for cmdline coloring +void syn_init_cmdline_highlight(bool reset, bool init) { - int i; - char **pp; - static int had_both = FALSE; + for (size_t i = 0 ; highlight_init_cmdline[i] != NULL ; i++) { + do_highlight(highlight_init_cmdline[i], reset, init); + } +} - /* - * Try finding the color scheme file. Used when a color file was loaded - * and 'background' or 't_Co' is changed. - */ +/// Load colors from a file if "g:colors_name" is set, otherwise load builtin +/// colors +/// +/// @param both include groups where 'bg' doesn't matter +/// @param reset clear groups first +void init_highlight(bool both, bool reset) +{ + static int had_both = false; + + // Try finding the color scheme file. Used when a color file was loaded + // and 'background' or 't_Co' is changed. char_u *p = get_var_value("g:colors_name"); if (p != NULL) { // Value of g:colors_name could be freed in load_colors() and make @@ -6021,39 +6304,40 @@ init_highlight ( * Didn't use a color file, use the compiled-in colors. */ if (both) { - had_both = TRUE; - pp = highlight_init_both; - for (i = 0; pp[i] != NULL; ++i) - do_highlight((char_u *)pp[i], reset, TRUE); - } else if (!had_both) - /* Don't do anything before the call with both == TRUE from main(). - * Not everything has been setup then, and that call will overrule - * everything anyway. */ + had_both = true; + const char *const *const pp = highlight_init_both; + for (size_t i = 0; pp[i] != NULL; i++) { + do_highlight(pp[i], reset, true); + } + } else if (!had_both) { + // Don't do anything before the call with both == TRUE from main(). + // Not everything has been setup then, and that call will overrule + // everything anyway. return; + } - if (*p_bg == 'l') - pp = highlight_init_light; - else - pp = highlight_init_dark; - for (i = 0; pp[i] != NULL; ++i) - do_highlight((char_u *)pp[i], reset, TRUE); + const char *const *const pp = ((*p_bg == 'l') + ? highlight_init_light + : highlight_init_dark); + for (size_t i = 0; pp[i] != NULL; i++) { + do_highlight(pp[i], reset, true); + } /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it * depend on the number of colors available. * With 8 colors brown is equal to yellow, need to use black for Search fg * to avoid Statement highlighted text disappears. * Clear the attributes, needed when changing the t_Co value. */ - if (t_colors > 8) + if (t_colors > 8) { do_highlight( - (char_u *)(*p_bg == 'l' - ? "Visual cterm=NONE ctermbg=LightGrey" - : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, - TRUE); - else { - do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE", - FALSE, TRUE); - if (*p_bg == 'l') - do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE); + (*p_bg == 'l' + ? "Visual cterm=NONE ctermbg=LightGrey" + : "Visual cterm=NONE ctermbg=DarkGrey"), false, true); + } else { + do_highlight("Visual cterm=reverse ctermbg=NONE", false, true); + if (*p_bg == 'l') { + do_highlight("Search ctermfg=black", false, true); + } } /* @@ -6070,6 +6354,7 @@ init_highlight ( recursive--; } } + syn_init_cmdline_highlight(false, false); } /* @@ -6104,22 +6389,22 @@ int load_colors(char_u *name) } -/// Handle the ":highlight .." command. -/// When using ":hi clear" this is called recursively for each group with -/// "forceit" and "init" both TRUE. -/// @param init TRUE when called for initializing -void -do_highlight( - char_u *line, - int forceit, - int init -) +/// Handle ":highlight" command +/// +/// When using ":highlight clear" this is called recursively for each group with +/// forceit and init being both true. +/// +/// @param[in] line Command arguments. +/// @param[in] forceit True when bang is given, allows to link group even if +/// it has its own settings. +/// @param[in] init True when initializing. +void do_highlight(const char *line, const bool forceit, const bool init) + FUNC_ATTR_NONNULL_ALL { - char_u *name_end; - char_u *linep; - char_u *key_start; - char_u *arg_start; - char_u *key = NULL, *arg = NULL; + const char *name_end; + const char *linep; + const char *key_start; + const char *arg_start; long i; int off; int len; @@ -6131,162 +6416,154 @@ do_highlight( int dolink = FALSE; int error = FALSE; int color; - int is_normal_group = FALSE; /* "Normal" group */ + bool is_normal_group = false; // "Normal" group - /* - * If no argument, list current highlighting. - */ - if (ends_excmd(*line)) { - for (int i = 1; i <= highlight_ga.ga_len && !got_int; ++i) - /* TODO: only call when the group has attributes set */ + // If no argument, list current highlighting. + if (ends_excmd((uint8_t)(*line))) { + for (int i = 1; i <= highlight_ga.ga_len && !got_int; i++) { + // TODO(brammool): only call when the group has attributes set highlight_list_one(i); + } return; } - /* - * Isolate the name. - */ - name_end = skiptowhite(line); - linep = skipwhite(name_end); + // Isolate the name. + name_end = (const char *)skiptowhite((const char_u *)line); + linep = (const char *)skipwhite((const char_u *)name_end); - /* - * Check for "default" argument. - */ - if (STRNCMP(line, "default", name_end - line) == 0) { - dodefault = TRUE; + // Check for "default" argument. + if (strncmp(line, "default", name_end - line) == 0) { + dodefault = true; line = linep; - name_end = skiptowhite(line); - linep = skipwhite(name_end); + name_end = (const char *)skiptowhite((const char_u *)line); + linep = (const char *)skipwhite((const char_u *)name_end); } - /* - * Check for "clear" or "link" argument. - */ - if (STRNCMP(line, "clear", name_end - line) == 0) - doclear = TRUE; - if (STRNCMP(line, "link", name_end - line) == 0) - dolink = TRUE; + // Check for "clear" or "link" argument. + if (strncmp(line, "clear", name_end - line) == 0) { + doclear = true; + } else if (strncmp(line, "link", name_end - line) == 0) { + dolink = true; + } - /* - * ":highlight {group-name}": list highlighting for one group. - */ - if (!doclear && !dolink && ends_excmd(*linep)) { - id = syn_namen2id(line, (int)(name_end - line)); - if (id == 0) - EMSG2(_("E411: highlight group not found: %s"), line); - else + // ":highlight {group-name}": list highlighting for one group. + if (!doclear && !dolink && ends_excmd((uint8_t)(*linep))) { + id = syn_namen2id((const char_u *)line, (int)(name_end - line)); + if (id == 0) { + emsgf(_("E411: highlight group not found: %s"), line); + } else { highlight_list_one(id); + } return; } - /* - * Handle ":highlight link {from} {to}" command. - */ + // Handle ":highlight link {from} {to}" command. if (dolink) { - char_u *from_start = linep; - char_u *from_end; - char_u *to_start; - char_u *to_end; + const char *from_start = linep; + const char *from_end; + const char *to_start; + const char *to_end; int from_id; int to_id; - from_end = skiptowhite(from_start); - to_start = skipwhite(from_end); - to_end = skiptowhite(to_start); + from_end = (const char *)skiptowhite((const char_u *)from_start); + to_start = (const char *)skipwhite((const char_u *)from_end); + to_end = (const char *)skiptowhite((const char_u *)to_start); - if (ends_excmd(*from_start) || ends_excmd(*to_start)) { - EMSG2(_("E412: Not enough arguments: \":highlight link %s\""), - from_start); + if (ends_excmd((uint8_t)(*from_start)) + || ends_excmd((uint8_t)(*to_start))) { + emsgf(_("E412: Not enough arguments: \":highlight link %s\""), + from_start); return; } - if (!ends_excmd(*skipwhite(to_end))) { - EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start); + if (!ends_excmd(*skipwhite((const char_u *)to_end))) { + emsgf(_("E413: Too many arguments: \":highlight link %s\""), from_start); return; } - from_id = syn_check_group(from_start, (int)(from_end - from_start)); - if (STRNCMP(to_start, "NONE", 4) == 0) + from_id = syn_check_group((const char_u *)from_start, + (int)(from_end - from_start)); + if (strncmp(to_start, "NONE", 4) == 0) { to_id = 0; - else - to_id = syn_check_group(to_start, (int)(to_end - to_start)); + } else { + to_id = syn_check_group((const char_u *)to_start, + (int)(to_end - to_start)); + } if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0)) { - /* - * Don't allow a link when there already is some highlighting - * for the group, unless '!' is used - */ + // Don't allow a link when there already is some highlighting + // for the group, unless '!' is used if (to_id > 0 && !forceit && !init && hl_has_settings(from_id - 1, dodefault)) { - if (sourcing_name == NULL && !dodefault) + if (sourcing_name == NULL && !dodefault) { EMSG(_("E414: group has settings, highlight link ignored")); + } } else { if (!init) HL_TABLE()[from_id - 1].sg_set |= SG_LINK; HL_TABLE()[from_id - 1].sg_link = to_id; HL_TABLE()[from_id - 1].sg_scriptID = current_SID; + HL_TABLE()[from_id - 1].sg_cleared = false; redraw_all_later(SOME_VALID); } } - /* Only call highlight_changed() once, after sourcing a syntax file */ - need_highlight_changed = TRUE; + // Only call highlight_changed() once, after sourcing a syntax file. + need_highlight_changed = true; return; } if (doclear) { - /* - * ":highlight clear [group]" command. - */ + // ":highlight clear [group]" command. line = linep; - if (ends_excmd(*line)) { + if (ends_excmd((uint8_t)(*line))) { do_unlet(S_LEN("colors_name"), true); restore_cterm_colors(); - /* - * Clear all default highlight groups and load the defaults. - */ - for (int idx = 0; idx < highlight_ga.ga_len; ++idx) { + // Clear all default highlight groups and load the defaults. + for (int idx = 0; idx < highlight_ga.ga_len; idx++) { highlight_clear(idx); } - init_highlight(TRUE, TRUE); + init_highlight(true, true); highlight_changed(); redraw_later_clear(); return; } - name_end = skiptowhite(line); - linep = skipwhite(name_end); + name_end = (const char *)skiptowhite((const char_u *)line); + linep = (const char *)skipwhite((const char_u *)name_end); } - /* - * Find the group name in the table. If it does not exist yet, add it. - */ - id = syn_check_group(line, (int)(name_end - line)); - if (id == 0) /* failed (out of memory) */ + // Find the group name in the table. If it does not exist yet, add it. + id = syn_check_group((const char_u *)line, (int)(name_end - line)); + if (id == 0) { // Failed (out of memory). return; - idx = id - 1; /* index is ID minus one */ + } + idx = id - 1; // Index is ID minus one. - /* Return if "default" was used and the group already has settings. */ - if (dodefault && hl_has_settings(idx, TRUE)) + // Return if "default" was used and the group already has settings + if (dodefault && hl_has_settings(idx, true)) { return; + } - if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0) - is_normal_group = TRUE; + is_normal_group = (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0); - /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */ + // Clear the highlighting for ":hi clear {group}" and ":hi clear". if (doclear || (forceit && init)) { highlight_clear(idx); if (!doclear) HL_TABLE()[idx].sg_set = 0; } - if (!doclear) - while (!ends_excmd(*linep)) { + char *key = NULL; + char *arg = NULL; + if (!doclear) { + while (!ends_excmd((uint8_t)(*linep))) { key_start = linep; if (*linep == '=') { - EMSG2(_("E415: unexpected equal sign: %s"), key_start); - error = TRUE; + emsgf(_("E415: unexpected equal sign: %s"), key_start); + error = true; break; } @@ -6296,61 +6573,58 @@ do_highlight( linep++; } xfree(key); - key = vim_strnsave_up(key_start, (int)(linep - key_start)); - linep = skipwhite(linep); + key = (char *)vim_strnsave_up((const char_u *)key_start, + (int)(linep - key_start)); + linep = (const char *)skipwhite((const char_u *)linep); - if (STRCMP(key, "NONE") == 0) { + if (strcmp(key, "NONE") == 0) { if (!init || HL_TABLE()[idx].sg_set == 0) { - if (!init) + if (!init) { HL_TABLE()[idx].sg_set |= SG_CTERM+SG_GUI; + } highlight_clear(idx); } continue; } - /* - * Check for the equal sign. - */ + // Check for the equal sign. if (*linep != '=') { - EMSG2(_("E416: missing equal sign: %s"), key_start); - error = TRUE; + emsgf(_("E416: missing equal sign: %s"), key_start); + error = true; break; } - ++linep; + linep++; - /* - * Isolate the argument. - */ - linep = skipwhite(linep); - if (*linep == '\'') { /* guifg='color name' */ + // Isolate the argument. + linep = (const char *)skipwhite((const char_u *)linep); + if (*linep == '\'') { // guifg='color name' arg_start = ++linep; - linep = vim_strchr(linep, '\''); + linep = strchr(linep, '\''); if (linep == NULL) { - EMSG2(_(e_invarg2), key_start); - error = TRUE; + emsgf(_(e_invarg2), key_start); + error = true; break; } } else { arg_start = linep; - linep = skiptowhite(linep); + linep = (const char *)skiptowhite((const char_u *)linep); } if (linep == arg_start) { - EMSG2(_("E417: missing argument: %s"), key_start); - error = TRUE; + emsgf(_("E417: missing argument: %s"), key_start); + error = true; break; } xfree(arg); - arg = vim_strnsave(arg_start, (int)(linep - arg_start)); + arg = xstrndup(arg_start, (size_t)(linep - arg_start)); - if (*linep == '\'') - ++linep; + if (*linep == '\'') { + linep++; + } - /* - * Store the argument. - */ - if ( STRCMP(key, "TERM") == 0 - || STRCMP(key, "CTERM") == 0 - || STRCMP(key, "GUI") == 0) { + // Store the argument. + if (strcmp(key, "TERM") == 0 + || strcmp(key, "CTERM") == 0 + || strcmp(key, "GUI") == 0) { attr = 0; off = 0; while (arg[off] != NUL) { @@ -6363,36 +6637,40 @@ do_highlight( } } if (i < 0) { - EMSG2(_("E418: Illegal value: %s"), arg); - error = TRUE; + emsgf(_("E418: Illegal value: %s"), arg); + error = true; break; } - if (arg[off] == ',') /* another one follows */ - ++off; + if (arg[off] == ',') { // Another one follows. + off++; + } } - if (error) + if (error) { break; + } if (*key == 'C') { if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) { - if (!init) + if (!init) { HL_TABLE()[idx].sg_set |= SG_CTERM; + } HL_TABLE()[idx].sg_cterm = attr; HL_TABLE()[idx].sg_cterm_bold = FALSE; } } else if (*key == 'G') { if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { - if (!init) + if (!init) { HL_TABLE()[idx].sg_set |= SG_GUI; + } HL_TABLE()[idx].sg_gui = attr; } } } else if (STRCMP(key, "FONT") == 0) { - /* in non-GUI fonts are simply ignored */ - } else if (STRCMP(key, - "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0) { + // in non-GUI fonts are simply ignored + } else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0) { if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) { - if (!init) + if (!init) { HL_TABLE()[idx].sg_set |= SG_CTERM; + } /* When setting the foreground color, and previously the "bold" * flag was set for a light color, reset it now */ @@ -6401,14 +6679,14 @@ do_highlight( HL_TABLE()[idx].sg_cterm_bold = FALSE; } - if (ascii_isdigit(*arg)) + if (ascii_isdigit(*arg)) { color = atoi((char *)arg); - else if (STRICMP(arg, "fg") == 0) { - if (cterm_normal_fg_color) + } else if (STRICMP(arg, "fg") == 0) { + if (cterm_normal_fg_color) { color = cterm_normal_fg_color - 1; - else { + } else { EMSG(_("E419: FG color unknown")); - error = TRUE; + error = true; break; } } else if (STRICMP(arg, "bg") == 0) { @@ -6416,79 +6694,94 @@ do_highlight( color = cterm_normal_bg_color - 1; else { EMSG(_("E420: BG color unknown")); - error = TRUE; + error = true; break; } } else { - static char *(color_names[28]) = { + static const char *color_names[] = { "Black", "DarkBlue", "DarkGreen", "DarkCyan", "DarkRed", "DarkMagenta", "Brown", "DarkYellow", "Gray", "Grey", "LightGray", "LightGrey", "DarkGray", "DarkGrey", "Blue", "LightBlue", "Green", "LightGreen", "Cyan", "LightCyan", "Red", "LightRed", "Magenta", - "LightMagenta", "Yellow", "LightYellow", "White", "NONE" + "LightMagenta", "Yellow", "LightYellow", "White", + "NONE" }; - static int color_numbers_16[28] = {0, 1, 2, 3, - 4, 5, 6, 6, - 7, 7, - 7, 7, 8, 8, - 9, 9, 10, 10, - 11, 11, 12, 12, 13, - 13, 14, 14, 15, -1}; - /* for xterm with 88 colors... */ - static int color_numbers_88[28] = {0, 4, 2, 6, - 1, 5, 32, 72, - 84, 84, - 7, 7, 82, 82, - 12, 43, 10, 61, - 14, 63, 9, 74, 13, - 75, 11, 78, 15, -1}; - /* for xterm with 256 colors... */ - static int color_numbers_256[28] = {0, 4, 2, 6, - 1, 5, 130, 130, - 248, 248, - 7, 7, 242, 242, - 12, 81, 10, 121, - 14, 159, 9, 224, 13, - 225, 11, 229, 15, -1}; - /* for terminals with less than 16 colors... */ - static int color_numbers_8[28] = {0, 4, 2, 6, - 1, 5, 3, 3, - 7, 7, - 7, 7, 0+8, 0+8, - 4+8, 4+8, 2+8, 2+8, - 6+8, 6+8, 1+8, 1+8, 5+8, - 5+8, 3+8, 3+8, 7+8, -1}; - - /* reduce calls to STRICMP a bit, it can be slow */ + static const int color_numbers_16[] = { + 0, 1, 2, 3, + 4, 5, 6, 6, + 7, 7, + 7, 7, 8, 8, + 9, 9, 10, 10, + 11, 11, 12, 12, 13, + 13, 14, 14, 15, + -1 + }; + // For xterm with 88 colors: + static int color_numbers_88[] = { + 0, 4, 2, 6, + 1, 5, 32, 72, + 84, 84, + 7, 7, 82, 82, + 12, 43, 10, 61, + 14, 63, 9, 74, 13, + 75, 11, 78, 15, + -1 + }; + // For xterm with 256 colors: + static int color_numbers_256[] = { + 0, 4, 2, 6, + 1, 5, 130, 130, + 248, 248, + 7, 7, 242, 242, + 12, 81, 10, 121, + 14, 159, 9, 224, 13, + 225, 11, 229, 15, + -1 + }; + // For terminals with less than 16 colors: + static int color_numbers_8[28] = { + 0, 4, 2, 6, + 1, 5, 3, 3, + 7, 7, + 7, 7, 0+8, 0+8, + 4+8, 4+8, 2+8, 2+8, + 6+8, 6+8, 1+8, 1+8, 5+8, + 5+8, 3+8, 3+8, 7+8, + -1 + }; + + // Reduce calls to STRICMP a bit, it can be slow. off = TOUPPER_ASC(*arg); - for (i = ARRAY_SIZE(color_names); --i >= 0; ) + for (i = ARRAY_SIZE(color_names); --i >= 0; ) { if (off == color_names[i][0] - && STRICMP(arg + 1, color_names[i] + 1) == 0) + && STRICMP(arg + 1, color_names[i] + 1) == 0) { break; + } + } if (i < 0) { - EMSG2(_( - "E421: Color name or number not recognized: %s"), - key_start); - error = TRUE; + emsgf(_("E421: Color name or number not recognized: %s"), + key_start); + error = true; break; } - /* Use the _16 table to check if its a valid color name. */ + // Use the _16 table to check if its a valid color name. color = color_numbers_16[i]; if (color >= 0) { if (t_colors == 8) { - /* t_Co is 8: use the 8 colors table */ + // t_Co is 8: use the 8 colors table. color = color_numbers_8[i]; if (key[5] == 'F') { /* set/reset bold attribute to get light foreground * colors (on some terminals, e.g. "linux") */ if (color & 8) { HL_TABLE()[idx].sg_cterm |= HL_BOLD; - HL_TABLE()[idx].sg_cterm_bold = TRUE; - } else + HL_TABLE()[idx].sg_cterm_bold = true; + } else { HL_TABLE()[idx].sg_cterm &= ~HL_BOLD; + } } color &= 7; // truncate to 8 colors } else if (t_colors == 16 || t_colors == 88 || t_colors >= 256) { @@ -6502,16 +6795,13 @@ do_highlight( } } } - /* Add one to the argument, to avoid zero. Zero is used for - * "NONE", then "color" is -1. */ + // Add one to the argument, to avoid zero. Zero is used for + // "NONE", then "color" is -1. if (key[5] == 'F') { HL_TABLE()[idx].sg_cterm_fg = color + 1; if (is_normal_group) { cterm_normal_fg_color = color + 1; - cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD); - { - must_redraw = CLEAR; - } + must_redraw = CLEAR; } } else { HL_TABLE()[idx].sg_cterm_bg = color + 1; @@ -6535,15 +6825,15 @@ do_highlight( } } } - } else if (STRCMP(key, "GUIFG") == 0) { + } else if (strcmp(key, "GUIFG") == 0) { if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { if (!init) HL_TABLE()[idx].sg_set |= SG_GUI; xfree(HL_TABLE()[idx].sg_rgb_fg_name); - if (STRCMP(arg, "NONE")) { - HL_TABLE()[idx].sg_rgb_fg_name = (uint8_t *)xstrdup((char *)arg); - HL_TABLE()[idx].sg_rgb_fg = name_to_color(arg); + if (strcmp(arg, "NONE")) { + HL_TABLE()[idx].sg_rgb_fg_name = (char_u *)xstrdup((char *)arg); + HL_TABLE()[idx].sg_rgb_fg = name_to_color((const char_u *)arg); } else { HL_TABLE()[idx].sg_rgb_fg_name = NULL; HL_TABLE()[idx].sg_rgb_fg = -1; @@ -6560,8 +6850,8 @@ do_highlight( xfree(HL_TABLE()[idx].sg_rgb_bg_name); if (STRCMP(arg, "NONE") != 0) { - HL_TABLE()[idx].sg_rgb_bg_name = (uint8_t *)xstrdup((char *)arg); - HL_TABLE()[idx].sg_rgb_bg = name_to_color(arg); + HL_TABLE()[idx].sg_rgb_bg_name = (char_u *)xstrdup((char *)arg); + HL_TABLE()[idx].sg_rgb_bg = name_to_color((const char_u *)arg); } else { HL_TABLE()[idx].sg_rgb_bg_name = NULL; HL_TABLE()[idx].sg_rgb_bg = -1; @@ -6571,15 +6861,15 @@ do_highlight( if (is_normal_group) { normal_bg = HL_TABLE()[idx].sg_rgb_bg; } - } else if (STRCMP(key, "GUISP") == 0) { + } else if (strcmp(key, "GUISP") == 0) { if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { if (!init) HL_TABLE()[idx].sg_set |= SG_GUI; xfree(HL_TABLE()[idx].sg_rgb_sp_name); - if (STRCMP(arg, "NONE") != 0) { - HL_TABLE()[idx].sg_rgb_sp_name = (uint8_t *)xstrdup((char *)arg); - HL_TABLE()[idx].sg_rgb_sp = name_to_color(arg); + if (strcmp(arg, "NONE") != 0) { + HL_TABLE()[idx].sg_rgb_sp_name = (char_u *)xstrdup((char *)arg); + HL_TABLE()[idx].sg_rgb_sp = name_to_color((const char_u *)arg); } else { HL_TABLE()[idx].sg_rgb_sp_name = NULL; HL_TABLE()[idx].sg_rgb_sp = -1; @@ -6589,49 +6879,46 @@ do_highlight( if (is_normal_group) { normal_sp = HL_TABLE()[idx].sg_rgb_sp; } - } else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0) { + } else if (strcmp(key, "START") == 0 || strcmp(key, "STOP") == 0) { // Ignored for now } else { - EMSG2(_("E423: Illegal argument: %s"), key_start); - error = TRUE; + emsgf(_("E423: Illegal argument: %s"), key_start); + error = true; break; } + HL_TABLE()[idx].sg_cleared = false; - /* - * When highlighting has been given for a group, don't link it. - */ - if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK)) + // When highlighting has been given for a group, don't link it. + if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK)) { HL_TABLE()[idx].sg_link = 0; + } - /* - * Continue with next argument. - */ - linep = skipwhite(linep); + // Continue with next argument. + linep = (const char *)skipwhite((const char_u *)linep); } + } - /* - * If there is an error, and it's a new entry, remove it from the table. - */ - if (error && idx == highlight_ga.ga_len) + // If there is an error, and it's a new entry, remove it from the table. + if (error && idx == highlight_ga.ga_len) { syn_unadd_group(); - else { - if (is_normal_group) { - HL_TABLE()[idx].sg_attr = 0; + } else { + if (!error && is_normal_group) { // Need to update all groups, because they might be using "bg" and/or // "fg", which have been changed now. highlight_attr_set_all(); // If the normal group has changed, it is simpler to refresh every UI ui_refresh(); - } else + } else { set_hl_attr(idx); + } HL_TABLE()[idx].sg_scriptID = current_SID; redraw_all_later(NOT_VALID); } xfree(key); xfree(arg); - /* Only call highlight_changed() once, after sourcing a syntax file */ - need_highlight_changed = TRUE; + // Only call highlight_changed() once, after sourcing a syntax file + need_highlight_changed = true; } #if defined(EXITFREE) @@ -6657,7 +6944,6 @@ void restore_cterm_colors(void) normal_bg = -1; normal_sp = -1; cterm_normal_fg_color = 0; - cterm_normal_fg_bold = 0; cterm_normal_bg_color = 0; } @@ -6681,6 +6967,8 @@ static int hl_has_settings(int idx, int check_link) */ static void highlight_clear(int idx) { + HL_TABLE()[idx].sg_cleared = true; + HL_TABLE()[idx].sg_attr = 0; HL_TABLE()[idx].sg_cterm = 0; HL_TABLE()[idx].sg_cterm_bold = FALSE; @@ -6704,37 +6992,36 @@ static void highlight_clear(int idx) } -/* - * Table with the specifications for an attribute number. - * Note that this table is used by ALL buffers. This is required because the - * GUI can redraw at any time for any buffer. - */ +/// Table with the specifications for an attribute number. +/// Note that this table is used by ALL buffers. This is required because the +/// GUI can redraw at any time for any buffer. static garray_T attr_table = GA_EMPTY_INIT_VALUE; -#define ATTR_ENTRY(idx) ((attrentry_T *)attr_table.ga_data)[idx] +static inline HlAttrs * ATTR_ENTRY(int idx) +{ + return &((HlAttrs *)attr_table.ga_data)[idx]; +} /// Return the attr number for a set of colors and font. /// Add a new entry to the term_attr_table, attr_table or gui_attr_table /// if the combination is new. /// @return 0 for error. -int get_attr_entry(attrentry_T *aep) +int get_attr_entry(HlAttrs *aep) { garray_T *table = &attr_table; - attrentry_T *taep; - static int recursive = FALSE; + HlAttrs *taep; + static int recursive = false; /* * Init the table, in case it wasn't done yet. */ - table->ga_itemsize = sizeof(attrentry_T); + table->ga_itemsize = sizeof(HlAttrs); ga_set_growsize(table, 7); - /* - * Try to find an entry with the same specifications. - */ - for (int i = 0; i < table->ga_len; ++i) { - taep = &(((attrentry_T *)table->ga_data)[i]); + // Try to find an entry with the same specifications. + for (int i = 0; i < table->ga_len; i++) { + taep = &(((HlAttrs *)table->ga_data)[i]); if (aep->cterm_ae_attr == taep->cterm_ae_attr && aep->cterm_fg_color == taep->cterm_fg_color && aep->cterm_bg_color == taep->cterm_bg_color @@ -6771,7 +7058,7 @@ int get_attr_entry(attrentry_T *aep) // This is a new combination of colors and font, add an entry. - taep = GA_APPEND_VIA_PTR(attrentry_T, table); + taep = GA_APPEND_VIA_PTR(HlAttrs, table); memset(taep, 0, sizeof(*taep)); taep->cterm_ae_attr = aep->cterm_ae_attr; taep->cterm_fg_color = aep->cterm_fg_color; @@ -6799,9 +7086,9 @@ void clear_hl_tables(void) // Return the resulting attributes. int hl_combine_attr(int char_attr, int prim_attr) { - attrentry_T *char_aep = NULL; - attrentry_T *spell_aep; - attrentry_T new_en; + HlAttrs *char_aep = NULL; + HlAttrs *spell_aep; + HlAttrs new_en = HLATTRS_INIT; if (char_attr == 0) { return prim_attr; @@ -6817,8 +7104,6 @@ int hl_combine_attr(int char_attr, int prim_attr) if (char_aep != NULL) { // Copy all attributes from char_aep to the new entry new_en = *char_aep; - } else { - memset(&new_en, 0, sizeof(new_en)); } spell_aep = syn_cterm_attr2entry(prim_attr); @@ -6849,17 +7134,25 @@ int hl_combine_attr(int char_attr, int prim_attr) return get_attr_entry(&new_en); } -attrentry_T *syn_cterm_attr2entry(int attr) +/// \note this function does not apply exclusively to cterm attr contrary +/// to what its name implies +/// \warn don't call it with attr 0 (i.e., the null attribute) +HlAttrs *syn_cterm_attr2entry(int attr) { attr -= ATTR_OFF; - if (attr >= attr_table.ga_len) /* did ":syntax clear" */ + if (attr >= attr_table.ga_len) { + // did ":syntax clear" return NULL; - return &(ATTR_ENTRY(attr)); + } + return ATTR_ENTRY(attr); } +/// \addtogroup LIST_XXX +/// @{ #define LIST_ATTR 1 #define LIST_STRING 2 #define LIST_INT 3 +/// @} static void highlight_list_one(int id) { @@ -6898,7 +7191,13 @@ static void highlight_list_one(int id) last_set_msg(sgp->sg_scriptID); } -static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name) +/// Outputs a highlight when doing ":hi MyHighlight" +/// +/// @param type one of \ref LIST_XXX +/// @param iarg integer argument used if \p type == LIST_INT +/// @param sarg string used if \p type == LIST_STRING +static int highlight_list_arg(int id, int didh, int type, int iarg, + char_u *sarg, const char *name) { char_u buf[100]; char_u *ts; @@ -7038,24 +7337,23 @@ const char *highlight_color(const int id, const char *const what, return NULL; } -/* - * Output the syntax list header. - * Return TRUE when started a new line. - */ -static int -syn_list_header ( - int did_header, /* did header already */ - int outlen, /* length of string that comes */ - int id /* highlight group id */ -) +/// Output the syntax list header. +/// +/// @param did_header did header already +/// @param outlen length of string that comes +/// @param id highlight group id +/// @return true when started a new line. +static int +syn_list_header(int did_header, int outlen, int id) { int endcol = 19; int newline = TRUE; if (!did_header) { msg_putchar('\n'); - if (got_int) - return TRUE; + if (got_int) { + return true; + } msg_outtrans(HL_TABLE()[id - 1].sg_name); endcol = 15; } else if (msg_col + outlen + 1 >= Columns) { @@ -7083,21 +7381,14 @@ syn_list_header ( return newline; } -/* - * Set the attribute numbers for a highlight group. - * Called after one of the attributes has changed. - */ -static void -set_hl_attr ( - int idx /* index in array */ -) +/// Set the attribute numbers for a highlight group. +/// Called after one of the attributes has changed. +/// @param idx corrected highlight index +static void set_hl_attr(int idx) { - attrentry_T at_en; + HlAttrs at_en = HLATTRS_INIT; struct hl_group *sgp = HL_TABLE() + idx; - /* The "Normal" group doesn't need an attribute number */ - if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0) - return; at_en.cterm_ae_attr = sgp->sg_cterm; at_en.cterm_fg_color = sgp->sg_cterm_fg; @@ -7121,10 +7412,10 @@ set_hl_attr ( } } -/* - * Lookup a highlight group name and return it's ID. - * If it is not found, 0 is returned. - */ +/// Lookup a highlight group name and return its ID. +/// +/// @param highlight name e.g. 'Cursor', 'Normal' +/// @return the highlight id, else 0 if \p name does not exist int syn_name2id(const char_u *name) { int i; @@ -7164,7 +7455,7 @@ char_u *syn_id2name(int id) /* * Like syn_name2id(), but take a pointer + length argument. */ -int syn_namen2id(char_u *linep, int len) +int syn_namen2id(const char_u *linep, int len) { char_u *name = vim_strnsave(linep, len); int id = syn_name2id(name); @@ -7173,14 +7464,14 @@ int syn_namen2id(char_u *linep, int len) return id; } -/// Find highlight group name in the table and return it's ID. +/// Find highlight group name in the table and return its ID. /// If it doesn't exist yet, a new entry is created. /// /// @param pp Highlight group name /// @param len length of \p pp /// /// @return 0 for failure else the id of the group -int syn_check_group(char_u *pp, int len) +int syn_check_group(const char_u *pp, int len) { char_u *name = vim_strnsave(pp, len); int id = syn_name2id(name); @@ -7192,11 +7483,11 @@ int syn_check_group(char_u *pp, int len) return id; } -/* - * Add new highlight group and return it's ID. - * "name" must be an allocated string, it will be consumed. - * Return 0 for failure. - */ +/// Add new highlight group and return it's ID. +/// +/// @param name must be an allocated string, it will be consumed. +/// @return 0 for failure, else the allocated group id +/// @see syn_check_group syn_unadd_group static int syn_add_group(char_u *name) { char_u *p; @@ -7234,25 +7525,26 @@ static int syn_add_group(char_u *name) struct hl_group* hlgp = GA_APPEND_VIA_PTR(struct hl_group, &highlight_ga); memset(hlgp, 0, sizeof(*hlgp)); hlgp->sg_name = name; + hlgp->sg_rgb_bg = -1; + hlgp->sg_rgb_fg = -1; + hlgp->sg_rgb_sp = -1; hlgp->sg_name_u = vim_strsave_up(name); return highlight_ga.ga_len; /* ID is index plus one */ } -/* - * When, just after calling syn_add_group(), an error is discovered, this - * function deletes the new name. - */ +/// When, just after calling syn_add_group(), an error is discovered, this +/// function deletes the new name. static void syn_unadd_group(void) { - --highlight_ga.ga_len; + highlight_ga.ga_len--; xfree(HL_TABLE()[highlight_ga.ga_len].sg_name); xfree(HL_TABLE()[highlight_ga.ga_len].sg_name_u); } -/* - * Translate a group ID to highlight attributes. - */ + +/// Translate a group ID to highlight attributes. +/// @see syn_cterm_attr2entry int syn_id2attr(int hl_id) { struct hl_group *sgp; @@ -7466,14 +7758,28 @@ static void highlight_list_two(int cnt, int attr) } -/* - * Function given to ExpandGeneric() to obtain the list of group names. - * Also used for synIDattr() function. - */ -const char *get_highlight_name(expand_T *const xp, const int idx) +/// Function given to ExpandGeneric() to obtain the list of group names. +const char *get_highlight_name(expand_T *const xp, int idx) FUNC_ATTR_WARN_UNUSED_RESULT { - // TODO(justinmk): 'xp' is unused + return get_highlight_name_ext(xp, idx, true); +} + + +/// Obtain a highlight group name. +/// When "skip_cleared" is TRUE don't return a cleared entry. +const char *get_highlight_name_ext(expand_T *xp, int idx, int skip_cleared) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (idx < 0) { + return NULL; + } + + // Items are never removed from the table, skip the ones that were cleared. + if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared) { + return ""; + } + if (idx == highlight_ga.ga_len && include_none != 0) { return "none"; } else if (idx == highlight_ga.ga_len + include_none @@ -7485,7 +7791,7 @@ const char *get_highlight_name(expand_T *const xp, const int idx) } else if (idx == highlight_ga.ga_len + include_none + include_default + 1 && include_link != 0) { return "clear"; - } else if (idx < 0 || idx >= highlight_ga.ga_len) { + } else if (idx >= highlight_ga.ga_len) { return NULL; } return (const char *)HL_TABLE()[idx].sg_name; @@ -7493,685 +7799,685 @@ const char *get_highlight_name(expand_T *const xp, const int idx) color_name_table_T color_name_table[] = { // Colors from rgb.txt - { "AliceBlue", RGB(0xf0, 0xf8, 0xff) }, - { "AntiqueWhite", RGB(0xfa, 0xeb, 0xd7) }, - { "AntiqueWhite1", RGB(0xff, 0xef, 0xdb) }, - { "AntiqueWhite2", RGB(0xee, 0xdf, 0xcc) }, - { "AntiqueWhite3", RGB(0xcd, 0xc0, 0xb0) }, - { "AntiqueWhite4", RGB(0x8b, 0x83, 0x78) }, - { "Aqua", RGB(0x00, 0xff, 0xff) }, - { "Aquamarine", RGB(0x7f, 0xff, 0xd4) }, - { "Aquamarine1", RGB(0x7f, 0xff, 0xd4) }, - { "Aquamarine2", RGB(0x76, 0xee, 0xc6) }, - { "Aquamarine3", RGB(0x66, 0xcd, 0xaa) }, - { "Aquamarine4", RGB(0x45, 0x8b, 0x74) }, - { "Azure", RGB(0xf0, 0xff, 0xff) }, - { "Azure1", RGB(0xf0, 0xff, 0xff) }, - { "Azure2", RGB(0xe0, 0xee, 0xee) }, - { "Azure3", RGB(0xc1, 0xcd, 0xcd) }, - { "Azure4", RGB(0x83, 0x8b, 0x8b) }, - { "Beige", RGB(0xf5, 0xf5, 0xdc) }, - { "Bisque", RGB(0xff, 0xe4, 0xc4) }, - { "Bisque1", RGB(0xff, 0xe4, 0xc4) }, - { "Bisque2", RGB(0xee, 0xd5, 0xb7) }, - { "Bisque3", RGB(0xcd, 0xb7, 0x9e) }, - { "Bisque4", RGB(0x8b, 0x7d, 0x6b) }, - { "Black", RGB(0x00, 0x00, 0x00) }, - { "BlanchedAlmond", RGB(0xff, 0xeb, 0xcd) }, - { "Blue", RGB(0x00, 0x00, 0xff) }, - { "Blue1", RGB(0x0, 0x0, 0xff) }, - { "Blue2", RGB(0x0, 0x0, 0xee) }, - { "Blue3", RGB(0x0, 0x0, 0xcd) }, - { "Blue4", RGB(0x0, 0x0, 0x8b) }, - { "BlueViolet", RGB(0x8a, 0x2b, 0xe2) }, - { "Brown", RGB(0xa5, 0x2a, 0x2a) }, - { "Brown1", RGB(0xff, 0x40, 0x40) }, - { "Brown2", RGB(0xee, 0x3b, 0x3b) }, - { "Brown3", RGB(0xcd, 0x33, 0x33) }, - { "Brown4", RGB(0x8b, 0x23, 0x23) }, - { "BurlyWood", RGB(0xde, 0xb8, 0x87) }, - { "Burlywood1", RGB(0xff, 0xd3, 0x9b) }, - { "Burlywood2", RGB(0xee, 0xc5, 0x91) }, - { "Burlywood3", RGB(0xcd, 0xaa, 0x7d) }, - { "Burlywood4", RGB(0x8b, 0x73, 0x55) }, - { "CadetBlue", RGB(0x5f, 0x9e, 0xa0) }, - { "CadetBlue1", RGB(0x98, 0xf5, 0xff) }, - { "CadetBlue2", RGB(0x8e, 0xe5, 0xee) }, - { "CadetBlue3", RGB(0x7a, 0xc5, 0xcd) }, - { "CadetBlue4", RGB(0x53, 0x86, 0x8b) }, - { "ChartReuse", RGB(0x7f, 0xff, 0x00) }, - { "Chartreuse1", RGB(0x7f, 0xff, 0x0) }, - { "Chartreuse2", RGB(0x76, 0xee, 0x0) }, - { "Chartreuse3", RGB(0x66, 0xcd, 0x0) }, - { "Chartreuse4", RGB(0x45, 0x8b, 0x0) }, - { "Chocolate", RGB(0xd2, 0x69, 0x1e) }, - { "Chocolate1", RGB(0xff, 0x7f, 0x24) }, - { "Chocolate2", RGB(0xee, 0x76, 0x21) }, - { "Chocolate3", RGB(0xcd, 0x66, 0x1d) }, - { "Chocolate4", RGB(0x8b, 0x45, 0x13) }, - { "Coral", RGB(0xff, 0x7f, 0x50) }, - { "Coral1", RGB(0xff, 0x72, 0x56) }, - { "Coral2", RGB(0xee, 0x6a, 0x50) }, - { "Coral3", RGB(0xcd, 0x5b, 0x45) }, - { "Coral4", RGB(0x8b, 0x3e, 0x2f) }, - { "CornFlowerBlue", RGB(0x64, 0x95, 0xed) }, - { "Cornsilk", RGB(0xff, 0xf8, 0xdc) }, - { "Cornsilk1", RGB(0xff, 0xf8, 0xdc) }, - { "Cornsilk2", RGB(0xee, 0xe8, 0xcd) }, - { "Cornsilk3", RGB(0xcd, 0xc8, 0xb1) }, - { "Cornsilk4", RGB(0x8b, 0x88, 0x78) }, - { "Crimson", RGB(0xdc, 0x14, 0x3c) }, - { "Cyan", RGB(0x00, 0xff, 0xff) }, - { "Cyan1", RGB(0x0, 0xff, 0xff) }, - { "Cyan2", RGB(0x0, 0xee, 0xee) }, - { "Cyan3", RGB(0x0, 0xcd, 0xcd) }, - { "Cyan4", RGB(0x0, 0x8b, 0x8b) }, - { "DarkBlue", RGB(0x00, 0x00, 0x8b) }, - { "DarkCyan", RGB(0x00, 0x8b, 0x8b) }, - { "DarkGoldenRod", RGB(0xb8, 0x86, 0x0b) }, - { "DarkGoldenrod1", RGB(0xff, 0xb9, 0xf) }, - { "DarkGoldenrod2", RGB(0xee, 0xad, 0xe) }, - { "DarkGoldenrod3", RGB(0xcd, 0x95, 0xc) }, - { "DarkGoldenrod4", RGB(0x8b, 0x65, 0x8) }, - { "DarkGray", RGB(0xa9, 0xa9, 0xa9) }, - { "DarkGreen", RGB(0x00, 0x64, 0x00) }, - { "DarkGrey", RGB(0xa9, 0xa9, 0xa9) }, - { "DarkKhaki", RGB(0xbd, 0xb7, 0x6b) }, - { "DarkMagenta", RGB(0x8b, 0x00, 0x8b) }, - { "DarkOliveGreen", RGB(0x55, 0x6b, 0x2f) }, - { "DarkOliveGreen1", RGB(0xca, 0xff, 0x70) }, - { "DarkOliveGreen2", RGB(0xbc, 0xee, 0x68) }, - { "DarkOliveGreen3", RGB(0xa2, 0xcd, 0x5a) }, - { "DarkOliveGreen4", RGB(0x6e, 0x8b, 0x3d) }, - { "DarkOrange", RGB(0xff, 0x8c, 0x00) }, - { "DarkOrange1", RGB(0xff, 0x7f, 0x0) }, - { "DarkOrange2", RGB(0xee, 0x76, 0x0) }, - { "DarkOrange3", RGB(0xcd, 0x66, 0x0) }, - { "DarkOrange4", RGB(0x8b, 0x45, 0x0) }, - { "DarkOrchid", RGB(0x99, 0x32, 0xcc) }, - { "DarkOrchid1", RGB(0xbf, 0x3e, 0xff) }, - { "DarkOrchid2", RGB(0xb2, 0x3a, 0xee) }, - { "DarkOrchid3", RGB(0x9a, 0x32, 0xcd) }, - { "DarkOrchid4", RGB(0x68, 0x22, 0x8b) }, - { "DarkRed", RGB(0x8b, 0x00, 0x00) }, - { "DarkSalmon", RGB(0xe9, 0x96, 0x7a) }, - { "DarkSeaGreen", RGB(0x8f, 0xbc, 0x8f) }, - { "DarkSeaGreen1", RGB(0xc1, 0xff, 0xc1) }, - { "DarkSeaGreen2", RGB(0xb4, 0xee, 0xb4) }, - { "DarkSeaGreen3", RGB(0x9b, 0xcd, 0x9b) }, - { "DarkSeaGreen4", RGB(0x69, 0x8b, 0x69) }, - { "DarkSlateBlue", RGB(0x48, 0x3d, 0x8b) }, - { "DarkSlateGray", RGB(0x2f, 0x4f, 0x4f) }, - { "DarkSlateGray1", RGB(0x97, 0xff, 0xff) }, - { "DarkSlateGray2", RGB(0x8d, 0xee, 0xee) }, - { "DarkSlateGray3", RGB(0x79, 0xcd, 0xcd) }, - { "DarkSlateGray4", RGB(0x52, 0x8b, 0x8b) }, - { "DarkSlateGrey", RGB(0x2f, 0x4f, 0x4f) }, - { "DarkTurquoise", RGB(0x00, 0xce, 0xd1) }, - { "DarkViolet", RGB(0x94, 0x00, 0xd3) }, - { "DarkYellow", RGB(0xbb, 0xbb, 0x00) }, - { "DeepPink", RGB(0xff, 0x14, 0x93) }, - { "DeepPink1", RGB(0xff, 0x14, 0x93) }, - { "DeepPink2", RGB(0xee, 0x12, 0x89) }, - { "DeepPink3", RGB(0xcd, 0x10, 0x76) }, - { "DeepPink4", RGB(0x8b, 0xa, 0x50) }, - { "DeepSkyBlue", RGB(0x00, 0xbf, 0xff) }, - { "DeepSkyBlue1", RGB(0x0, 0xbf, 0xff) }, - { "DeepSkyBlue2", RGB(0x0, 0xb2, 0xee) }, - { "DeepSkyBlue3", RGB(0x0, 0x9a, 0xcd) }, - { "DeepSkyBlue4", RGB(0x0, 0x68, 0x8b) }, - { "DimGray", RGB(0x69, 0x69, 0x69) }, - { "DimGrey", RGB(0x69, 0x69, 0x69) }, - { "DodgerBlue", RGB(0x1e, 0x90, 0xff) }, - { "DodgerBlue1", RGB(0x1e, 0x90, 0xff) }, - { "DodgerBlue2", RGB(0x1c, 0x86, 0xee) }, - { "DodgerBlue3", RGB(0x18, 0x74, 0xcd) }, - { "DodgerBlue4", RGB(0x10, 0x4e, 0x8b) }, - { "Firebrick", RGB(0xb2, 0x22, 0x22) }, - { "Firebrick1", RGB(0xff, 0x30, 0x30) }, - { "Firebrick2", RGB(0xee, 0x2c, 0x2c) }, - { "Firebrick3", RGB(0xcd, 0x26, 0x26) }, - { "Firebrick4", RGB(0x8b, 0x1a, 0x1a) }, - { "FloralWhite", RGB(0xff, 0xfa, 0xf0) }, - { "ForestGreen", RGB(0x22, 0x8b, 0x22) }, - { "Fuchsia", RGB(0xff, 0x00, 0xff) }, - { "Gainsboro", RGB(0xdc, 0xdc, 0xdc) }, - { "GhostWhite", RGB(0xf8, 0xf8, 0xff) }, - { "Gold", RGB(0xff, 0xd7, 0x00) }, - { "Gold1", RGB(0xff, 0xd7, 0x0) }, - { "Gold2", RGB(0xee, 0xc9, 0x0) }, - { "Gold3", RGB(0xcd, 0xad, 0x0) }, - { "Gold4", RGB(0x8b, 0x75, 0x0) }, - { "GoldenRod", RGB(0xda, 0xa5, 0x20) }, - { "Goldenrod1", RGB(0xff, 0xc1, 0x25) }, - { "Goldenrod2", RGB(0xee, 0xb4, 0x22) }, - { "Goldenrod3", RGB(0xcd, 0x9b, 0x1d) }, - { "Goldenrod4", RGB(0x8b, 0x69, 0x14) }, - { "Gray", RGB(0x80, 0x80, 0x80) }, - { "Gray0", RGB(0x0, 0x0, 0x0) }, - { "Gray1", RGB(0x3, 0x3, 0x3) }, - { "Gray10", RGB(0x1a, 0x1a, 0x1a) }, - { "Gray100", RGB(0xff, 0xff, 0xff) }, - { "Gray11", RGB(0x1c, 0x1c, 0x1c) }, - { "Gray12", RGB(0x1f, 0x1f, 0x1f) }, - { "Gray13", RGB(0x21, 0x21, 0x21) }, - { "Gray14", RGB(0x24, 0x24, 0x24) }, - { "Gray15", RGB(0x26, 0x26, 0x26) }, - { "Gray16", RGB(0x29, 0x29, 0x29) }, - { "Gray17", RGB(0x2b, 0x2b, 0x2b) }, - { "Gray18", RGB(0x2e, 0x2e, 0x2e) }, - { "Gray19", RGB(0x30, 0x30, 0x30) }, - { "Gray2", RGB(0x5, 0x5, 0x5) }, - { "Gray20", RGB(0x33, 0x33, 0x33) }, - { "Gray21", RGB(0x36, 0x36, 0x36) }, - { "Gray22", RGB(0x38, 0x38, 0x38) }, - { "Gray23", RGB(0x3b, 0x3b, 0x3b) }, - { "Gray24", RGB(0x3d, 0x3d, 0x3d) }, - { "Gray25", RGB(0x40, 0x40, 0x40) }, - { "Gray26", RGB(0x42, 0x42, 0x42) }, - { "Gray27", RGB(0x45, 0x45, 0x45) }, - { "Gray28", RGB(0x47, 0x47, 0x47) }, - { "Gray29", RGB(0x4a, 0x4a, 0x4a) }, - { "Gray3", RGB(0x8, 0x8, 0x8) }, - { "Gray30", RGB(0x4d, 0x4d, 0x4d) }, - { "Gray31", RGB(0x4f, 0x4f, 0x4f) }, - { "Gray32", RGB(0x52, 0x52, 0x52) }, - { "Gray33", RGB(0x54, 0x54, 0x54) }, - { "Gray34", RGB(0x57, 0x57, 0x57) }, - { "Gray35", RGB(0x59, 0x59, 0x59) }, - { "Gray36", RGB(0x5c, 0x5c, 0x5c) }, - { "Gray37", RGB(0x5e, 0x5e, 0x5e) }, - { "Gray38", RGB(0x61, 0x61, 0x61) }, - { "Gray39", RGB(0x63, 0x63, 0x63) }, - { "Gray4", RGB(0xa, 0xa, 0xa) }, - { "Gray40", RGB(0x66, 0x66, 0x66) }, - { "Gray41", RGB(0x69, 0x69, 0x69) }, - { "Gray42", RGB(0x6b, 0x6b, 0x6b) }, - { "Gray43", RGB(0x6e, 0x6e, 0x6e) }, - { "Gray44", RGB(0x70, 0x70, 0x70) }, - { "Gray45", RGB(0x73, 0x73, 0x73) }, - { "Gray46", RGB(0x75, 0x75, 0x75) }, - { "Gray47", RGB(0x78, 0x78, 0x78) }, - { "Gray48", RGB(0x7a, 0x7a, 0x7a) }, - { "Gray49", RGB(0x7d, 0x7d, 0x7d) }, - { "Gray5", RGB(0xd, 0xd, 0xd) }, - { "Gray50", RGB(0x7f, 0x7f, 0x7f) }, - { "Gray51", RGB(0x82, 0x82, 0x82) }, - { "Gray52", RGB(0x85, 0x85, 0x85) }, - { "Gray53", RGB(0x87, 0x87, 0x87) }, - { "Gray54", RGB(0x8a, 0x8a, 0x8a) }, - { "Gray55", RGB(0x8c, 0x8c, 0x8c) }, - { "Gray56", RGB(0x8f, 0x8f, 0x8f) }, - { "Gray57", RGB(0x91, 0x91, 0x91) }, - { "Gray58", RGB(0x94, 0x94, 0x94) }, - { "Gray59", RGB(0x96, 0x96, 0x96) }, - { "Gray6", RGB(0xf, 0xf, 0xf) }, - { "Gray60", RGB(0x99, 0x99, 0x99) }, - { "Gray61", RGB(0x9c, 0x9c, 0x9c) }, - { "Gray62", RGB(0x9e, 0x9e, 0x9e) }, - { "Gray63", RGB(0xa1, 0xa1, 0xa1) }, - { "Gray64", RGB(0xa3, 0xa3, 0xa3) }, - { "Gray65", RGB(0xa6, 0xa6, 0xa6) }, - { "Gray66", RGB(0xa8, 0xa8, 0xa8) }, - { "Gray67", RGB(0xab, 0xab, 0xab) }, - { "Gray68", RGB(0xad, 0xad, 0xad) }, - { "Gray69", RGB(0xb0, 0xb0, 0xb0) }, - { "Gray7", RGB(0x12, 0x12, 0x12) }, - { "Gray70", RGB(0xb3, 0xb3, 0xb3) }, - { "Gray71", RGB(0xb5, 0xb5, 0xb5) }, - { "Gray72", RGB(0xb8, 0xb8, 0xb8) }, - { "Gray73", RGB(0xba, 0xba, 0xba) }, - { "Gray74", RGB(0xbd, 0xbd, 0xbd) }, - { "Gray75", RGB(0xbf, 0xbf, 0xbf) }, - { "Gray76", RGB(0xc2, 0xc2, 0xc2) }, - { "Gray77", RGB(0xc4, 0xc4, 0xc4) }, - { "Gray78", RGB(0xc7, 0xc7, 0xc7) }, - { "Gray79", RGB(0xc9, 0xc9, 0xc9) }, - { "Gray8", RGB(0x14, 0x14, 0x14) }, - { "Gray80", RGB(0xcc, 0xcc, 0xcc) }, - { "Gray81", RGB(0xcf, 0xcf, 0xcf) }, - { "Gray82", RGB(0xd1, 0xd1, 0xd1) }, - { "Gray83", RGB(0xd4, 0xd4, 0xd4) }, - { "Gray84", RGB(0xd6, 0xd6, 0xd6) }, - { "Gray85", RGB(0xd9, 0xd9, 0xd9) }, - { "Gray86", RGB(0xdb, 0xdb, 0xdb) }, - { "Gray87", RGB(0xde, 0xde, 0xde) }, - { "Gray88", RGB(0xe0, 0xe0, 0xe0) }, - { "Gray89", RGB(0xe3, 0xe3, 0xe3) }, - { "Gray9", RGB(0x17, 0x17, 0x17) }, - { "Gray90", RGB(0xe5, 0xe5, 0xe5) }, - { "Gray91", RGB(0xe8, 0xe8, 0xe8) }, - { "Gray92", RGB(0xeb, 0xeb, 0xeb) }, - { "Gray93", RGB(0xed, 0xed, 0xed) }, - { "Gray94", RGB(0xf0, 0xf0, 0xf0) }, - { "Gray95", RGB(0xf2, 0xf2, 0xf2) }, - { "Gray96", RGB(0xf5, 0xf5, 0xf5) }, - { "Gray97", RGB(0xf7, 0xf7, 0xf7) }, - { "Gray98", RGB(0xfa, 0xfa, 0xfa) }, - { "Gray99", RGB(0xfc, 0xfc, 0xfc) }, - { "Green", RGB(0x00, 0x80, 0x00) }, - { "Green1", RGB(0x0, 0xff, 0x0) }, - { "Green2", RGB(0x0, 0xee, 0x0) }, - { "Green3", RGB(0x0, 0xcd, 0x0) }, - { "Green4", RGB(0x0, 0x8b, 0x0) }, - { "GreenYellow", RGB(0xad, 0xff, 0x2f) }, - { "Grey", RGB(0x80, 0x80, 0x80) }, - { "Grey0", RGB(0x0, 0x0, 0x0) }, - { "Grey1", RGB(0x3, 0x3, 0x3) }, - { "Grey10", RGB(0x1a, 0x1a, 0x1a) }, - { "Grey100", RGB(0xff, 0xff, 0xff) }, - { "Grey11", RGB(0x1c, 0x1c, 0x1c) }, - { "Grey12", RGB(0x1f, 0x1f, 0x1f) }, - { "Grey13", RGB(0x21, 0x21, 0x21) }, - { "Grey14", RGB(0x24, 0x24, 0x24) }, - { "Grey15", RGB(0x26, 0x26, 0x26) }, - { "Grey16", RGB(0x29, 0x29, 0x29) }, - { "Grey17", RGB(0x2b, 0x2b, 0x2b) }, - { "Grey18", RGB(0x2e, 0x2e, 0x2e) }, - { "Grey19", RGB(0x30, 0x30, 0x30) }, - { "Grey2", RGB(0x5, 0x5, 0x5) }, - { "Grey20", RGB(0x33, 0x33, 0x33) }, - { "Grey21", RGB(0x36, 0x36, 0x36) }, - { "Grey22", RGB(0x38, 0x38, 0x38) }, - { "Grey23", RGB(0x3b, 0x3b, 0x3b) }, - { "Grey24", RGB(0x3d, 0x3d, 0x3d) }, - { "Grey25", RGB(0x40, 0x40, 0x40) }, - { "Grey26", RGB(0x42, 0x42, 0x42) }, - { "Grey27", RGB(0x45, 0x45, 0x45) }, - { "Grey28", RGB(0x47, 0x47, 0x47) }, - { "Grey29", RGB(0x4a, 0x4a, 0x4a) }, - { "Grey3", RGB(0x8, 0x8, 0x8) }, - { "Grey30", RGB(0x4d, 0x4d, 0x4d) }, - { "Grey31", RGB(0x4f, 0x4f, 0x4f) }, - { "Grey32", RGB(0x52, 0x52, 0x52) }, - { "Grey33", RGB(0x54, 0x54, 0x54) }, - { "Grey34", RGB(0x57, 0x57, 0x57) }, - { "Grey35", RGB(0x59, 0x59, 0x59) }, - { "Grey36", RGB(0x5c, 0x5c, 0x5c) }, - { "Grey37", RGB(0x5e, 0x5e, 0x5e) }, - { "Grey38", RGB(0x61, 0x61, 0x61) }, - { "Grey39", RGB(0x63, 0x63, 0x63) }, - { "Grey4", RGB(0xa, 0xa, 0xa) }, - { "Grey40", RGB(0x66, 0x66, 0x66) }, - { "Grey41", RGB(0x69, 0x69, 0x69) }, - { "Grey42", RGB(0x6b, 0x6b, 0x6b) }, - { "Grey43", RGB(0x6e, 0x6e, 0x6e) }, - { "Grey44", RGB(0x70, 0x70, 0x70) }, - { "Grey45", RGB(0x73, 0x73, 0x73) }, - { "Grey46", RGB(0x75, 0x75, 0x75) }, - { "Grey47", RGB(0x78, 0x78, 0x78) }, - { "Grey48", RGB(0x7a, 0x7a, 0x7a) }, - { "Grey49", RGB(0x7d, 0x7d, 0x7d) }, - { "Grey5", RGB(0xd, 0xd, 0xd) }, - { "Grey50", RGB(0x7f, 0x7f, 0x7f) }, - { "Grey51", RGB(0x82, 0x82, 0x82) }, - { "Grey52", RGB(0x85, 0x85, 0x85) }, - { "Grey53", RGB(0x87, 0x87, 0x87) }, - { "Grey54", RGB(0x8a, 0x8a, 0x8a) }, - { "Grey55", RGB(0x8c, 0x8c, 0x8c) }, - { "Grey56", RGB(0x8f, 0x8f, 0x8f) }, - { "Grey57", RGB(0x91, 0x91, 0x91) }, - { "Grey58", RGB(0x94, 0x94, 0x94) }, - { "Grey59", RGB(0x96, 0x96, 0x96) }, - { "Grey6", RGB(0xf, 0xf, 0xf) }, - { "Grey60", RGB(0x99, 0x99, 0x99) }, - { "Grey61", RGB(0x9c, 0x9c, 0x9c) }, - { "Grey62", RGB(0x9e, 0x9e, 0x9e) }, - { "Grey63", RGB(0xa1, 0xa1, 0xa1) }, - { "Grey64", RGB(0xa3, 0xa3, 0xa3) }, - { "Grey65", RGB(0xa6, 0xa6, 0xa6) }, - { "Grey66", RGB(0xa8, 0xa8, 0xa8) }, - { "Grey67", RGB(0xab, 0xab, 0xab) }, - { "Grey68", RGB(0xad, 0xad, 0xad) }, - { "Grey69", RGB(0xb0, 0xb0, 0xb0) }, - { "Grey7", RGB(0x12, 0x12, 0x12) }, - { "Grey70", RGB(0xb3, 0xb3, 0xb3) }, - { "Grey71", RGB(0xb5, 0xb5, 0xb5) }, - { "Grey72", RGB(0xb8, 0xb8, 0xb8) }, - { "Grey73", RGB(0xba, 0xba, 0xba) }, - { "Grey74", RGB(0xbd, 0xbd, 0xbd) }, - { "Grey75", RGB(0xbf, 0xbf, 0xbf) }, - { "Grey76", RGB(0xc2, 0xc2, 0xc2) }, - { "Grey77", RGB(0xc4, 0xc4, 0xc4) }, - { "Grey78", RGB(0xc7, 0xc7, 0xc7) }, - { "Grey79", RGB(0xc9, 0xc9, 0xc9) }, - { "Grey8", RGB(0x14, 0x14, 0x14) }, - { "Grey80", RGB(0xcc, 0xcc, 0xcc) }, - { "Grey81", RGB(0xcf, 0xcf, 0xcf) }, - { "Grey82", RGB(0xd1, 0xd1, 0xd1) }, - { "Grey83", RGB(0xd4, 0xd4, 0xd4) }, - { "Grey84", RGB(0xd6, 0xd6, 0xd6) }, - { "Grey85", RGB(0xd9, 0xd9, 0xd9) }, - { "Grey86", RGB(0xdb, 0xdb, 0xdb) }, - { "Grey87", RGB(0xde, 0xde, 0xde) }, - { "Grey88", RGB(0xe0, 0xe0, 0xe0) }, - { "Grey89", RGB(0xe3, 0xe3, 0xe3) }, - { "Grey9", RGB(0x17, 0x17, 0x17) }, - { "Grey90", RGB(0xe5, 0xe5, 0xe5) }, - { "Grey91", RGB(0xe8, 0xe8, 0xe8) }, - { "Grey92", RGB(0xeb, 0xeb, 0xeb) }, - { "Grey93", RGB(0xed, 0xed, 0xed) }, - { "Grey94", RGB(0xf0, 0xf0, 0xf0) }, - { "Grey95", RGB(0xf2, 0xf2, 0xf2) }, - { "Grey96", RGB(0xf5, 0xf5, 0xf5) }, - { "Grey97", RGB(0xf7, 0xf7, 0xf7) }, - { "Grey98", RGB(0xfa, 0xfa, 0xfa) }, - { "Grey99", RGB(0xfc, 0xfc, 0xfc) }, - { "Honeydew", RGB(0xf0, 0xff, 0xf0) }, - { "Honeydew1", RGB(0xf0, 0xff, 0xf0) }, - { "Honeydew2", RGB(0xe0, 0xee, 0xe0) }, - { "Honeydew3", RGB(0xc1, 0xcd, 0xc1) }, - { "Honeydew4", RGB(0x83, 0x8b, 0x83) }, - { "HotPink", RGB(0xff, 0x69, 0xb4) }, - { "HotPink1", RGB(0xff, 0x6e, 0xb4) }, - { "HotPink2", RGB(0xee, 0x6a, 0xa7) }, - { "HotPink3", RGB(0xcd, 0x60, 0x90) }, - { "HotPink4", RGB(0x8b, 0x3a, 0x62) }, - { "IndianRed", RGB(0xcd, 0x5c, 0x5c) }, - { "IndianRed1", RGB(0xff, 0x6a, 0x6a) }, - { "IndianRed2", RGB(0xee, 0x63, 0x63) }, - { "IndianRed3", RGB(0xcd, 0x55, 0x55) }, - { "IndianRed4", RGB(0x8b, 0x3a, 0x3a) }, - { "Indigo", RGB(0x4b, 0x00, 0x82) }, - { "Ivory", RGB(0xff, 0xff, 0xf0) }, - { "Ivory1", RGB(0xff, 0xff, 0xf0) }, - { "Ivory2", RGB(0xee, 0xee, 0xe0) }, - { "Ivory3", RGB(0xcd, 0xcd, 0xc1) }, - { "Ivory4", RGB(0x8b, 0x8b, 0x83) }, - { "Khaki", RGB(0xf0, 0xe6, 0x8c) }, - { "Khaki1", RGB(0xff, 0xf6, 0x8f) }, - { "Khaki2", RGB(0xee, 0xe6, 0x85) }, - { "Khaki3", RGB(0xcd, 0xc6, 0x73) }, - { "Khaki4", RGB(0x8b, 0x86, 0x4e) }, - { "Lavender", RGB(0xe6, 0xe6, 0xfa) }, - { "LavenderBlush", RGB(0xff, 0xf0, 0xf5) }, - { "LavenderBlush1", RGB(0xff, 0xf0, 0xf5) }, - { "LavenderBlush2", RGB(0xee, 0xe0, 0xe5) }, - { "LavenderBlush3", RGB(0xcd, 0xc1, 0xc5) }, - { "LavenderBlush4", RGB(0x8b, 0x83, 0x86) }, - { "LawnGreen", RGB(0x7c, 0xfc, 0x00) }, - { "LemonChiffon", RGB(0xff, 0xfa, 0xcd) }, - { "LemonChiffon1", RGB(0xff, 0xfa, 0xcd) }, - { "LemonChiffon2", RGB(0xee, 0xe9, 0xbf) }, - { "LemonChiffon3", RGB(0xcd, 0xc9, 0xa5) }, - { "LemonChiffon4", RGB(0x8b, 0x89, 0x70) }, - { "LightBlue", RGB(0xad, 0xd8, 0xe6) }, - { "LightBlue1", RGB(0xbf, 0xef, 0xff) }, - { "LightBlue2", RGB(0xb2, 0xdf, 0xee) }, - { "LightBlue3", RGB(0x9a, 0xc0, 0xcd) }, - { "LightBlue4", RGB(0x68, 0x83, 0x8b) }, - { "LightCoral", RGB(0xf0, 0x80, 0x80) }, - { "LightCyan", RGB(0xe0, 0xff, 0xff) }, - { "LightCyan1", RGB(0xe0, 0xff, 0xff) }, - { "LightCyan2", RGB(0xd1, 0xee, 0xee) }, - { "LightCyan3", RGB(0xb4, 0xcd, 0xcd) }, - { "LightCyan4", RGB(0x7a, 0x8b, 0x8b) }, - { "LightGoldenrod", RGB(0xee, 0xdd, 0x82) }, - { "LightGoldenrod1", RGB(0xff, 0xec, 0x8b) }, - { "LightGoldenrod2", RGB(0xee, 0xdc, 0x82) }, - { "LightGoldenrod3", RGB(0xcd, 0xbe, 0x70) }, - { "LightGoldenrod4", RGB(0x8b, 0x81, 0x4c) }, - { "LightGoldenRodYellow", RGB(0xfa, 0xfa, 0xd2) }, - { "LightGray", RGB(0xd3, 0xd3, 0xd3) }, - { "LightGreen", RGB(0x90, 0xee, 0x90) }, - { "LightGrey", RGB(0xd3, 0xd3, 0xd3) }, - { "LightMagenta", RGB(0xff, 0xbb, 0xff) }, - { "LightPink", RGB(0xff, 0xb6, 0xc1) }, - { "LightPink1", RGB(0xff, 0xae, 0xb9) }, - { "LightPink2", RGB(0xee, 0xa2, 0xad) }, - { "LightPink3", RGB(0xcd, 0x8c, 0x95) }, - { "LightPink4", RGB(0x8b, 0x5f, 0x65) }, - { "LightRed", RGB(0xff, 0xbb, 0xbb) }, - { "LightSalmon", RGB(0xff, 0xa0, 0x7a) }, - { "LightSalmon1", RGB(0xff, 0xa0, 0x7a) }, - { "LightSalmon2", RGB(0xee, 0x95, 0x72) }, - { "LightSalmon3", RGB(0xcd, 0x81, 0x62) }, - { "LightSalmon4", RGB(0x8b, 0x57, 0x42) }, - { "LightSeaGreen", RGB(0x20, 0xb2, 0xaa) }, - { "LightSkyBlue", RGB(0x87, 0xce, 0xfa) }, - { "LightSkyBlue1", RGB(0xb0, 0xe2, 0xff) }, - { "LightSkyBlue2", RGB(0xa4, 0xd3, 0xee) }, - { "LightSkyBlue3", RGB(0x8d, 0xb6, 0xcd) }, - { "LightSkyBlue4", RGB(0x60, 0x7b, 0x8b) }, - { "LightSlateBlue", RGB(0x84, 0x70, 0xff) }, - { "LightSlateGray", RGB(0x77, 0x88, 0x99) }, - { "LightSlateGrey", RGB(0x77, 0x88, 0x99) }, - { "LightSteelBlue", RGB(0xb0, 0xc4, 0xde) }, - { "LightSteelBlue1", RGB(0xca, 0xe1, 0xff) }, - { "LightSteelBlue2", RGB(0xbc, 0xd2, 0xee) }, - { "LightSteelBlue3", RGB(0xa2, 0xb5, 0xcd) }, - { "LightSteelBlue4", RGB(0x6e, 0x7b, 0x8b) }, - { "LightYellow", RGB(0xff, 0xff, 0xe0) }, - { "LightYellow1", RGB(0xff, 0xff, 0xe0) }, - { "LightYellow2", RGB(0xee, 0xee, 0xd1) }, - { "LightYellow3", RGB(0xcd, 0xcd, 0xb4) }, - { "LightYellow4", RGB(0x8b, 0x8b, 0x7a) }, - { "Lime", RGB(0x00, 0xff, 0x00) }, - { "LimeGreen", RGB(0x32, 0xcd, 0x32) }, - { "Linen", RGB(0xfa, 0xf0, 0xe6) }, - { "Magenta", RGB(0xff, 0x00, 0xff) }, - { "Magenta1", RGB(0xff, 0x0, 0xff) }, - { "Magenta2", RGB(0xee, 0x0, 0xee) }, - { "Magenta3", RGB(0xcd, 0x0, 0xcd) }, - { "Magenta4", RGB(0x8b, 0x0, 0x8b) }, - { "Maroon", RGB(0x80, 0x00, 0x00) }, - { "Maroon1", RGB(0xff, 0x34, 0xb3) }, - { "Maroon2", RGB(0xee, 0x30, 0xa7) }, - { "Maroon3", RGB(0xcd, 0x29, 0x90) }, - { "Maroon4", RGB(0x8b, 0x1c, 0x62) }, - { "MediumAquamarine", RGB(0x66, 0xcd, 0xaa) }, - { "MediumBlue", RGB(0x00, 0x00, 0xcd) }, - { "MediumOrchid", RGB(0xba, 0x55, 0xd3) }, - { "MediumOrchid1", RGB(0xe0, 0x66, 0xff) }, - { "MediumOrchid2", RGB(0xd1, 0x5f, 0xee) }, - { "MediumOrchid3", RGB(0xb4, 0x52, 0xcd) }, - { "MediumOrchid4", RGB(0x7a, 0x37, 0x8b) }, - { "MediumPurple", RGB(0x93, 0x70, 0xdb) }, - { "MediumPurple1", RGB(0xab, 0x82, 0xff) }, - { "MediumPurple2", RGB(0x9f, 0x79, 0xee) }, - { "MediumPurple3", RGB(0x89, 0x68, 0xcd) }, - { "MediumPurple4", RGB(0x5d, 0x47, 0x8b) }, - { "MediumSeaGreen", RGB(0x3c, 0xb3, 0x71) }, - { "MediumSlateBlue", RGB(0x7b, 0x68, 0xee) }, - { "MediumSpringGreen", RGB(0x00, 0xfa, 0x9a) }, - { "MediumTurquoise", RGB(0x48, 0xd1, 0xcc) }, - { "MediumVioletRed", RGB(0xc7, 0x15, 0x85) }, - { "MidnightBlue", RGB(0x19, 0x19, 0x70) }, - { "MintCream", RGB(0xf5, 0xff, 0xfa) }, - { "MistyRose", RGB(0xff, 0xe4, 0xe1) }, - { "MistyRose1", RGB(0xff, 0xe4, 0xe1) }, - { "MistyRose2", RGB(0xee, 0xd5, 0xd2) }, - { "MistyRose3", RGB(0xcd, 0xb7, 0xb5) }, - { "MistyRose4", RGB(0x8b, 0x7d, 0x7b) }, - { "Moccasin", RGB(0xff, 0xe4, 0xb5) }, - { "NavajoWhite", RGB(0xff, 0xde, 0xad) }, - { "NavajoWhite1", RGB(0xff, 0xde, 0xad) }, - { "NavajoWhite2", RGB(0xee, 0xcf, 0xa1) }, - { "NavajoWhite3", RGB(0xcd, 0xb3, 0x8b) }, - { "NavajoWhite4", RGB(0x8b, 0x79, 0x5e) }, - { "Navy", RGB(0x00, 0x00, 0x80) }, - { "NavyBlue", RGB(0x0, 0x0, 0x80) }, - { "OldLace", RGB(0xfd, 0xf5, 0xe6) }, - { "Olive", RGB(0x80, 0x80, 0x00) }, - { "OliveDrab", RGB(0x6b, 0x8e, 0x23) }, - { "OliveDrab1", RGB(0xc0, 0xff, 0x3e) }, - { "OliveDrab2", RGB(0xb3, 0xee, 0x3a) }, - { "OliveDrab3", RGB(0x9a, 0xcd, 0x32) }, - { "OliveDrab4", RGB(0x69, 0x8b, 0x22) }, - { "Orange", RGB(0xff, 0xa5, 0x00) }, - { "Orange1", RGB(0xff, 0xa5, 0x0) }, - { "Orange2", RGB(0xee, 0x9a, 0x0) }, - { "Orange3", RGB(0xcd, 0x85, 0x0) }, - { "Orange4", RGB(0x8b, 0x5a, 0x0) }, - { "OrangeRed", RGB(0xff, 0x45, 0x00) }, - { "OrangeRed1", RGB(0xff, 0x45, 0x0) }, - { "OrangeRed2", RGB(0xee, 0x40, 0x0) }, - { "OrangeRed3", RGB(0xcd, 0x37, 0x0) }, - { "OrangeRed4", RGB(0x8b, 0x25, 0x0) }, - { "Orchid", RGB(0xda, 0x70, 0xd6) }, - { "Orchid1", RGB(0xff, 0x83, 0xfa) }, - { "Orchid2", RGB(0xee, 0x7a, 0xe9) }, - { "Orchid3", RGB(0xcd, 0x69, 0xc9) }, - { "Orchid4", RGB(0x8b, 0x47, 0x89) }, - { "PaleGoldenRod", RGB(0xee, 0xe8, 0xaa) }, - { "PaleGreen", RGB(0x98, 0xfb, 0x98) }, - { "PaleGreen1", RGB(0x9a, 0xff, 0x9a) }, - { "PaleGreen2", RGB(0x90, 0xee, 0x90) }, - { "PaleGreen3", RGB(0x7c, 0xcd, 0x7c) }, - { "PaleGreen4", RGB(0x54, 0x8b, 0x54) }, - { "PaleTurquoise", RGB(0xaf, 0xee, 0xee) }, - { "PaleTurquoise1", RGB(0xbb, 0xff, 0xff) }, - { "PaleTurquoise2", RGB(0xae, 0xee, 0xee) }, - { "PaleTurquoise3", RGB(0x96, 0xcd, 0xcd) }, - { "PaleTurquoise4", RGB(0x66, 0x8b, 0x8b) }, - { "PaleVioletRed", RGB(0xdb, 0x70, 0x93) }, - { "PaleVioletRed1", RGB(0xff, 0x82, 0xab) }, - { "PaleVioletRed2", RGB(0xee, 0x79, 0x9f) }, - { "PaleVioletRed3", RGB(0xcd, 0x68, 0x89) }, - { "PaleVioletRed4", RGB(0x8b, 0x47, 0x5d) }, - { "PapayaWhip", RGB(0xff, 0xef, 0xd5) }, - { "PeachPuff", RGB(0xff, 0xda, 0xb9) }, - { "PeachPuff1", RGB(0xff, 0xda, 0xb9) }, - { "PeachPuff2", RGB(0xee, 0xcb, 0xad) }, - { "PeachPuff3", RGB(0xcd, 0xaf, 0x95) }, - { "PeachPuff4", RGB(0x8b, 0x77, 0x65) }, - { "Peru", RGB(0xcd, 0x85, 0x3f) }, - { "Pink", RGB(0xff, 0xc0, 0xcb) }, - { "Pink1", RGB(0xff, 0xb5, 0xc5) }, - { "Pink2", RGB(0xee, 0xa9, 0xb8) }, - { "Pink3", RGB(0xcd, 0x91, 0x9e) }, - { "Pink4", RGB(0x8b, 0x63, 0x6c) }, - { "Plum", RGB(0xdd, 0xa0, 0xdd) }, - { "Plum1", RGB(0xff, 0xbb, 0xff) }, - { "Plum2", RGB(0xee, 0xae, 0xee) }, - { "Plum3", RGB(0xcd, 0x96, 0xcd) }, - { "Plum4", RGB(0x8b, 0x66, 0x8b) }, - { "PowderBlue", RGB(0xb0, 0xe0, 0xe6) }, - { "Purple", RGB(0x80, 0x00, 0x80) }, - { "Purple1", RGB(0x9b, 0x30, 0xff) }, - { "Purple2", RGB(0x91, 0x2c, 0xee) }, - { "Purple3", RGB(0x7d, 0x26, 0xcd) }, - { "Purple4", RGB(0x55, 0x1a, 0x8b) }, - { "RebeccaPurple", RGB(0x66, 0x33, 0x99) }, - { "Red", RGB(0xff, 0x00, 0x00) }, - { "Red1", RGB(0xff, 0x0, 0x0) }, - { "Red2", RGB(0xee, 0x0, 0x0) }, - { "Red3", RGB(0xcd, 0x0, 0x0) }, - { "Red4", RGB(0x8b, 0x0, 0x0) }, - { "RosyBrown", RGB(0xbc, 0x8f, 0x8f) }, - { "RosyBrown1", RGB(0xff, 0xc1, 0xc1) }, - { "RosyBrown2", RGB(0xee, 0xb4, 0xb4) }, - { "RosyBrown3", RGB(0xcd, 0x9b, 0x9b) }, - { "RosyBrown4", RGB(0x8b, 0x69, 0x69) }, - { "RoyalBlue", RGB(0x41, 0x69, 0xe1) }, - { "RoyalBlue1", RGB(0x48, 0x76, 0xff) }, - { "RoyalBlue2", RGB(0x43, 0x6e, 0xee) }, - { "RoyalBlue3", RGB(0x3a, 0x5f, 0xcd) }, - { "RoyalBlue4", RGB(0x27, 0x40, 0x8b) }, - { "SaddleBrown", RGB(0x8b, 0x45, 0x13) }, - { "Salmon", RGB(0xfa, 0x80, 0x72) }, - { "Salmon1", RGB(0xff, 0x8c, 0x69) }, - { "Salmon2", RGB(0xee, 0x82, 0x62) }, - { "Salmon3", RGB(0xcd, 0x70, 0x54) }, - { "Salmon4", RGB(0x8b, 0x4c, 0x39) }, - { "SandyBrown", RGB(0xf4, 0xa4, 0x60) }, - { "SeaGreen", RGB(0x2e, 0x8b, 0x57) }, - { "SeaGreen1", RGB(0x54, 0xff, 0x9f) }, - { "SeaGreen2", RGB(0x4e, 0xee, 0x94) }, - { "SeaGreen3", RGB(0x43, 0xcd, 0x80) }, - { "SeaGreen4", RGB(0x2e, 0x8b, 0x57) }, - { "SeaShell", RGB(0xff, 0xf5, 0xee) }, - { "Seashell1", RGB(0xff, 0xf5, 0xee) }, - { "Seashell2", RGB(0xee, 0xe5, 0xde) }, - { "Seashell3", RGB(0xcd, 0xc5, 0xbf) }, - { "Seashell4", RGB(0x8b, 0x86, 0x82) }, - { "Sienna", RGB(0xa0, 0x52, 0x2d) }, - { "Sienna1", RGB(0xff, 0x82, 0x47) }, - { "Sienna2", RGB(0xee, 0x79, 0x42) }, - { "Sienna3", RGB(0xcd, 0x68, 0x39) }, - { "Sienna4", RGB(0x8b, 0x47, 0x26) }, - { "Silver", RGB(0xc0, 0xc0, 0xc0) }, - { "SkyBlue", RGB(0x87, 0xce, 0xeb) }, - { "SkyBlue1", RGB(0x87, 0xce, 0xff) }, - { "SkyBlue2", RGB(0x7e, 0xc0, 0xee) }, - { "SkyBlue3", RGB(0x6c, 0xa6, 0xcd) }, - { "SkyBlue4", RGB(0x4a, 0x70, 0x8b) }, - { "SlateBlue", RGB(0x6a, 0x5a, 0xcd) }, - { "SlateBlue1", RGB(0x83, 0x6f, 0xff) }, - { "SlateBlue2", RGB(0x7a, 0x67, 0xee) }, - { "SlateBlue3", RGB(0x69, 0x59, 0xcd) }, - { "SlateBlue4", RGB(0x47, 0x3c, 0x8b) }, - { "SlateGray", RGB(0x70, 0x80, 0x90) }, - { "SlateGray1", RGB(0xc6, 0xe2, 0xff) }, - { "SlateGray2", RGB(0xb9, 0xd3, 0xee) }, - { "SlateGray3", RGB(0x9f, 0xb6, 0xcd) }, - { "SlateGray4", RGB(0x6c, 0x7b, 0x8b) }, - { "SlateGrey", RGB(0x70, 0x80, 0x90) }, - { "Snow", RGB(0xff, 0xfa, 0xfa) }, - { "Snow1", RGB(0xff, 0xfa, 0xfa) }, - { "Snow2", RGB(0xee, 0xe9, 0xe9) }, - { "Snow3", RGB(0xcd, 0xc9, 0xc9) }, - { "Snow4", RGB(0x8b, 0x89, 0x89) }, - { "SpringGreen", RGB(0x00, 0xff, 0x7f) }, - { "SpringGreen1", RGB(0x0, 0xff, 0x7f) }, - { "SpringGreen2", RGB(0x0, 0xee, 0x76) }, - { "SpringGreen3", RGB(0x0, 0xcd, 0x66) }, - { "SpringGreen4", RGB(0x0, 0x8b, 0x45) }, - { "SteelBlue", RGB(0x46, 0x82, 0xb4) }, - { "SteelBlue1", RGB(0x63, 0xb8, 0xff) }, - { "SteelBlue2", RGB(0x5c, 0xac, 0xee) }, - { "SteelBlue3", RGB(0x4f, 0x94, 0xcd) }, - { "SteelBlue4", RGB(0x36, 0x64, 0x8b) }, - { "Tan", RGB(0xd2, 0xb4, 0x8c) }, - { "Tan1", RGB(0xff, 0xa5, 0x4f) }, - { "Tan2", RGB(0xee, 0x9a, 0x49) }, - { "Tan3", RGB(0xcd, 0x85, 0x3f) }, - { "Tan4", RGB(0x8b, 0x5a, 0x2b) }, - { "Teal", RGB(0x00, 0x80, 0x80) }, - { "Thistle", RGB(0xd8, 0xbf, 0xd8) }, - { "Thistle1", RGB(0xff, 0xe1, 0xff) }, - { "Thistle2", RGB(0xee, 0xd2, 0xee) }, - { "Thistle3", RGB(0xcd, 0xb5, 0xcd) }, - { "Thistle4", RGB(0x8b, 0x7b, 0x8b) }, - { "Tomato", RGB(0xff, 0x63, 0x47) }, - { "Tomato1", RGB(0xff, 0x63, 0x47) }, - { "Tomato2", RGB(0xee, 0x5c, 0x42) }, - { "Tomato3", RGB(0xcd, 0x4f, 0x39) }, - { "Tomato4", RGB(0x8b, 0x36, 0x26) }, - { "Turquoise", RGB(0x40, 0xe0, 0xd0) }, - { "Turquoise1", RGB(0x0, 0xf5, 0xff) }, - { "Turquoise2", RGB(0x0, 0xe5, 0xee) }, - { "Turquoise3", RGB(0x0, 0xc5, 0xcd) }, - { "Turquoise4", RGB(0x0, 0x86, 0x8b) }, - { "Violet", RGB(0xee, 0x82, 0xee) }, - { "VioletRed", RGB(0xd0, 0x20, 0x90) }, - { "VioletRed1", RGB(0xff, 0x3e, 0x96) }, - { "VioletRed2", RGB(0xee, 0x3a, 0x8c) }, - { "VioletRed3", RGB(0xcd, 0x32, 0x78) }, - { "VioletRed4", RGB(0x8b, 0x22, 0x52) }, - { "WebGray", RGB(0x80, 0x80, 0x80) }, - { "WebGreen", RGB(0x0, 0x80, 0x0) }, - { "WebGrey", RGB(0x80, 0x80, 0x80) }, - { "WebMaroon", RGB(0x80, 0x0, 0x0) }, - { "WebPurple", RGB(0x80, 0x0, 0x80) }, - { "Wheat", RGB(0xf5, 0xde, 0xb3) }, - { "Wheat1", RGB(0xff, 0xe7, 0xba) }, - { "Wheat2", RGB(0xee, 0xd8, 0xae) }, - { "Wheat3", RGB(0xcd, 0xba, 0x96) }, - { "Wheat4", RGB(0x8b, 0x7e, 0x66) }, - { "White", RGB(0xff, 0xff, 0xff) }, - { "WhiteSmoke", RGB(0xf5, 0xf5, 0xf5) }, - { "X11Gray", RGB(0xbe, 0xbe, 0xbe) }, - { "X11Green", RGB(0x0, 0xff, 0x0) }, - { "X11Grey", RGB(0xbe, 0xbe, 0xbe) }, - { "X11Maroon", RGB(0xb0, 0x30, 0x60) }, - { "X11Purple", RGB(0xa0, 0x20, 0xf0) }, - { "Yellow", RGB(0xff, 0xff, 0x00) }, - { "Yellow1", RGB(0xff, 0xff, 0x0) }, - { "Yellow2", RGB(0xee, 0xee, 0x0) }, - { "Yellow3", RGB(0xcd, 0xcd, 0x0) }, - { "Yellow4", RGB(0x8b, 0x8b, 0x0) }, - { "YellowGreen", RGB(0x9a, 0xcd, 0x32) }, + { "AliceBlue", RGB_(0xf0, 0xf8, 0xff) }, + { "AntiqueWhite", RGB_(0xfa, 0xeb, 0xd7) }, + { "AntiqueWhite1", RGB_(0xff, 0xef, 0xdb) }, + { "AntiqueWhite2", RGB_(0xee, 0xdf, 0xcc) }, + { "AntiqueWhite3", RGB_(0xcd, 0xc0, 0xb0) }, + { "AntiqueWhite4", RGB_(0x8b, 0x83, 0x78) }, + { "Aqua", RGB_(0x00, 0xff, 0xff) }, + { "Aquamarine", RGB_(0x7f, 0xff, 0xd4) }, + { "Aquamarine1", RGB_(0x7f, 0xff, 0xd4) }, + { "Aquamarine2", RGB_(0x76, 0xee, 0xc6) }, + { "Aquamarine3", RGB_(0x66, 0xcd, 0xaa) }, + { "Aquamarine4", RGB_(0x45, 0x8b, 0x74) }, + { "Azure", RGB_(0xf0, 0xff, 0xff) }, + { "Azure1", RGB_(0xf0, 0xff, 0xff) }, + { "Azure2", RGB_(0xe0, 0xee, 0xee) }, + { "Azure3", RGB_(0xc1, 0xcd, 0xcd) }, + { "Azure4", RGB_(0x83, 0x8b, 0x8b) }, + { "Beige", RGB_(0xf5, 0xf5, 0xdc) }, + { "Bisque", RGB_(0xff, 0xe4, 0xc4) }, + { "Bisque1", RGB_(0xff, 0xe4, 0xc4) }, + { "Bisque2", RGB_(0xee, 0xd5, 0xb7) }, + { "Bisque3", RGB_(0xcd, 0xb7, 0x9e) }, + { "Bisque4", RGB_(0x8b, 0x7d, 0x6b) }, + { "Black", RGB_(0x00, 0x00, 0x00) }, + { "BlanchedAlmond", RGB_(0xff, 0xeb, 0xcd) }, + { "Blue", RGB_(0x00, 0x00, 0xff) }, + { "Blue1", RGB_(0x0, 0x0, 0xff) }, + { "Blue2", RGB_(0x0, 0x0, 0xee) }, + { "Blue3", RGB_(0x0, 0x0, 0xcd) }, + { "Blue4", RGB_(0x0, 0x0, 0x8b) }, + { "BlueViolet", RGB_(0x8a, 0x2b, 0xe2) }, + { "Brown", RGB_(0xa5, 0x2a, 0x2a) }, + { "Brown1", RGB_(0xff, 0x40, 0x40) }, + { "Brown2", RGB_(0xee, 0x3b, 0x3b) }, + { "Brown3", RGB_(0xcd, 0x33, 0x33) }, + { "Brown4", RGB_(0x8b, 0x23, 0x23) }, + { "BurlyWood", RGB_(0xde, 0xb8, 0x87) }, + { "Burlywood1", RGB_(0xff, 0xd3, 0x9b) }, + { "Burlywood2", RGB_(0xee, 0xc5, 0x91) }, + { "Burlywood3", RGB_(0xcd, 0xaa, 0x7d) }, + { "Burlywood4", RGB_(0x8b, 0x73, 0x55) }, + { "CadetBlue", RGB_(0x5f, 0x9e, 0xa0) }, + { "CadetBlue1", RGB_(0x98, 0xf5, 0xff) }, + { "CadetBlue2", RGB_(0x8e, 0xe5, 0xee) }, + { "CadetBlue3", RGB_(0x7a, 0xc5, 0xcd) }, + { "CadetBlue4", RGB_(0x53, 0x86, 0x8b) }, + { "ChartReuse", RGB_(0x7f, 0xff, 0x00) }, + { "Chartreuse1", RGB_(0x7f, 0xff, 0x0) }, + { "Chartreuse2", RGB_(0x76, 0xee, 0x0) }, + { "Chartreuse3", RGB_(0x66, 0xcd, 0x0) }, + { "Chartreuse4", RGB_(0x45, 0x8b, 0x0) }, + { "Chocolate", RGB_(0xd2, 0x69, 0x1e) }, + { "Chocolate1", RGB_(0xff, 0x7f, 0x24) }, + { "Chocolate2", RGB_(0xee, 0x76, 0x21) }, + { "Chocolate3", RGB_(0xcd, 0x66, 0x1d) }, + { "Chocolate4", RGB_(0x8b, 0x45, 0x13) }, + { "Coral", RGB_(0xff, 0x7f, 0x50) }, + { "Coral1", RGB_(0xff, 0x72, 0x56) }, + { "Coral2", RGB_(0xee, 0x6a, 0x50) }, + { "Coral3", RGB_(0xcd, 0x5b, 0x45) }, + { "Coral4", RGB_(0x8b, 0x3e, 0x2f) }, + { "CornFlowerBlue", RGB_(0x64, 0x95, 0xed) }, + { "Cornsilk", RGB_(0xff, 0xf8, 0xdc) }, + { "Cornsilk1", RGB_(0xff, 0xf8, 0xdc) }, + { "Cornsilk2", RGB_(0xee, 0xe8, 0xcd) }, + { "Cornsilk3", RGB_(0xcd, 0xc8, 0xb1) }, + { "Cornsilk4", RGB_(0x8b, 0x88, 0x78) }, + { "Crimson", RGB_(0xdc, 0x14, 0x3c) }, + { "Cyan", RGB_(0x00, 0xff, 0xff) }, + { "Cyan1", RGB_(0x0, 0xff, 0xff) }, + { "Cyan2", RGB_(0x0, 0xee, 0xee) }, + { "Cyan3", RGB_(0x0, 0xcd, 0xcd) }, + { "Cyan4", RGB_(0x0, 0x8b, 0x8b) }, + { "DarkBlue", RGB_(0x00, 0x00, 0x8b) }, + { "DarkCyan", RGB_(0x00, 0x8b, 0x8b) }, + { "DarkGoldenRod", RGB_(0xb8, 0x86, 0x0b) }, + { "DarkGoldenrod1", RGB_(0xff, 0xb9, 0xf) }, + { "DarkGoldenrod2", RGB_(0xee, 0xad, 0xe) }, + { "DarkGoldenrod3", RGB_(0xcd, 0x95, 0xc) }, + { "DarkGoldenrod4", RGB_(0x8b, 0x65, 0x8) }, + { "DarkGray", RGB_(0xa9, 0xa9, 0xa9) }, + { "DarkGreen", RGB_(0x00, 0x64, 0x00) }, + { "DarkGrey", RGB_(0xa9, 0xa9, 0xa9) }, + { "DarkKhaki", RGB_(0xbd, 0xb7, 0x6b) }, + { "DarkMagenta", RGB_(0x8b, 0x00, 0x8b) }, + { "DarkOliveGreen", RGB_(0x55, 0x6b, 0x2f) }, + { "DarkOliveGreen1", RGB_(0xca, 0xff, 0x70) }, + { "DarkOliveGreen2", RGB_(0xbc, 0xee, 0x68) }, + { "DarkOliveGreen3", RGB_(0xa2, 0xcd, 0x5a) }, + { "DarkOliveGreen4", RGB_(0x6e, 0x8b, 0x3d) }, + { "DarkOrange", RGB_(0xff, 0x8c, 0x00) }, + { "DarkOrange1", RGB_(0xff, 0x7f, 0x0) }, + { "DarkOrange2", RGB_(0xee, 0x76, 0x0) }, + { "DarkOrange3", RGB_(0xcd, 0x66, 0x0) }, + { "DarkOrange4", RGB_(0x8b, 0x45, 0x0) }, + { "DarkOrchid", RGB_(0x99, 0x32, 0xcc) }, + { "DarkOrchid1", RGB_(0xbf, 0x3e, 0xff) }, + { "DarkOrchid2", RGB_(0xb2, 0x3a, 0xee) }, + { "DarkOrchid3", RGB_(0x9a, 0x32, 0xcd) }, + { "DarkOrchid4", RGB_(0x68, 0x22, 0x8b) }, + { "DarkRed", RGB_(0x8b, 0x00, 0x00) }, + { "DarkSalmon", RGB_(0xe9, 0x96, 0x7a) }, + { "DarkSeaGreen", RGB_(0x8f, 0xbc, 0x8f) }, + { "DarkSeaGreen1", RGB_(0xc1, 0xff, 0xc1) }, + { "DarkSeaGreen2", RGB_(0xb4, 0xee, 0xb4) }, + { "DarkSeaGreen3", RGB_(0x9b, 0xcd, 0x9b) }, + { "DarkSeaGreen4", RGB_(0x69, 0x8b, 0x69) }, + { "DarkSlateBlue", RGB_(0x48, 0x3d, 0x8b) }, + { "DarkSlateGray", RGB_(0x2f, 0x4f, 0x4f) }, + { "DarkSlateGray1", RGB_(0x97, 0xff, 0xff) }, + { "DarkSlateGray2", RGB_(0x8d, 0xee, 0xee) }, + { "DarkSlateGray3", RGB_(0x79, 0xcd, 0xcd) }, + { "DarkSlateGray4", RGB_(0x52, 0x8b, 0x8b) }, + { "DarkSlateGrey", RGB_(0x2f, 0x4f, 0x4f) }, + { "DarkTurquoise", RGB_(0x00, 0xce, 0xd1) }, + { "DarkViolet", RGB_(0x94, 0x00, 0xd3) }, + { "DarkYellow", RGB_(0xbb, 0xbb, 0x00) }, + { "DeepPink", RGB_(0xff, 0x14, 0x93) }, + { "DeepPink1", RGB_(0xff, 0x14, 0x93) }, + { "DeepPink2", RGB_(0xee, 0x12, 0x89) }, + { "DeepPink3", RGB_(0xcd, 0x10, 0x76) }, + { "DeepPink4", RGB_(0x8b, 0xa, 0x50) }, + { "DeepSkyBlue", RGB_(0x00, 0xbf, 0xff) }, + { "DeepSkyBlue1", RGB_(0x0, 0xbf, 0xff) }, + { "DeepSkyBlue2", RGB_(0x0, 0xb2, 0xee) }, + { "DeepSkyBlue3", RGB_(0x0, 0x9a, 0xcd) }, + { "DeepSkyBlue4", RGB_(0x0, 0x68, 0x8b) }, + { "DimGray", RGB_(0x69, 0x69, 0x69) }, + { "DimGrey", RGB_(0x69, 0x69, 0x69) }, + { "DodgerBlue", RGB_(0x1e, 0x90, 0xff) }, + { "DodgerBlue1", RGB_(0x1e, 0x90, 0xff) }, + { "DodgerBlue2", RGB_(0x1c, 0x86, 0xee) }, + { "DodgerBlue3", RGB_(0x18, 0x74, 0xcd) }, + { "DodgerBlue4", RGB_(0x10, 0x4e, 0x8b) }, + { "Firebrick", RGB_(0xb2, 0x22, 0x22) }, + { "Firebrick1", RGB_(0xff, 0x30, 0x30) }, + { "Firebrick2", RGB_(0xee, 0x2c, 0x2c) }, + { "Firebrick3", RGB_(0xcd, 0x26, 0x26) }, + { "Firebrick4", RGB_(0x8b, 0x1a, 0x1a) }, + { "FloralWhite", RGB_(0xff, 0xfa, 0xf0) }, + { "ForestGreen", RGB_(0x22, 0x8b, 0x22) }, + { "Fuchsia", RGB_(0xff, 0x00, 0xff) }, + { "Gainsboro", RGB_(0xdc, 0xdc, 0xdc) }, + { "GhostWhite", RGB_(0xf8, 0xf8, 0xff) }, + { "Gold", RGB_(0xff, 0xd7, 0x00) }, + { "Gold1", RGB_(0xff, 0xd7, 0x0) }, + { "Gold2", RGB_(0xee, 0xc9, 0x0) }, + { "Gold3", RGB_(0xcd, 0xad, 0x0) }, + { "Gold4", RGB_(0x8b, 0x75, 0x0) }, + { "GoldenRod", RGB_(0xda, 0xa5, 0x20) }, + { "Goldenrod1", RGB_(0xff, 0xc1, 0x25) }, + { "Goldenrod2", RGB_(0xee, 0xb4, 0x22) }, + { "Goldenrod3", RGB_(0xcd, 0x9b, 0x1d) }, + { "Goldenrod4", RGB_(0x8b, 0x69, 0x14) }, + { "Gray", RGB_(0x80, 0x80, 0x80) }, + { "Gray0", RGB_(0x0, 0x0, 0x0) }, + { "Gray1", RGB_(0x3, 0x3, 0x3) }, + { "Gray10", RGB_(0x1a, 0x1a, 0x1a) }, + { "Gray100", RGB_(0xff, 0xff, 0xff) }, + { "Gray11", RGB_(0x1c, 0x1c, 0x1c) }, + { "Gray12", RGB_(0x1f, 0x1f, 0x1f) }, + { "Gray13", RGB_(0x21, 0x21, 0x21) }, + { "Gray14", RGB_(0x24, 0x24, 0x24) }, + { "Gray15", RGB_(0x26, 0x26, 0x26) }, + { "Gray16", RGB_(0x29, 0x29, 0x29) }, + { "Gray17", RGB_(0x2b, 0x2b, 0x2b) }, + { "Gray18", RGB_(0x2e, 0x2e, 0x2e) }, + { "Gray19", RGB_(0x30, 0x30, 0x30) }, + { "Gray2", RGB_(0x5, 0x5, 0x5) }, + { "Gray20", RGB_(0x33, 0x33, 0x33) }, + { "Gray21", RGB_(0x36, 0x36, 0x36) }, + { "Gray22", RGB_(0x38, 0x38, 0x38) }, + { "Gray23", RGB_(0x3b, 0x3b, 0x3b) }, + { "Gray24", RGB_(0x3d, 0x3d, 0x3d) }, + { "Gray25", RGB_(0x40, 0x40, 0x40) }, + { "Gray26", RGB_(0x42, 0x42, 0x42) }, + { "Gray27", RGB_(0x45, 0x45, 0x45) }, + { "Gray28", RGB_(0x47, 0x47, 0x47) }, + { "Gray29", RGB_(0x4a, 0x4a, 0x4a) }, + { "Gray3", RGB_(0x8, 0x8, 0x8) }, + { "Gray30", RGB_(0x4d, 0x4d, 0x4d) }, + { "Gray31", RGB_(0x4f, 0x4f, 0x4f) }, + { "Gray32", RGB_(0x52, 0x52, 0x52) }, + { "Gray33", RGB_(0x54, 0x54, 0x54) }, + { "Gray34", RGB_(0x57, 0x57, 0x57) }, + { "Gray35", RGB_(0x59, 0x59, 0x59) }, + { "Gray36", RGB_(0x5c, 0x5c, 0x5c) }, + { "Gray37", RGB_(0x5e, 0x5e, 0x5e) }, + { "Gray38", RGB_(0x61, 0x61, 0x61) }, + { "Gray39", RGB_(0x63, 0x63, 0x63) }, + { "Gray4", RGB_(0xa, 0xa, 0xa) }, + { "Gray40", RGB_(0x66, 0x66, 0x66) }, + { "Gray41", RGB_(0x69, 0x69, 0x69) }, + { "Gray42", RGB_(0x6b, 0x6b, 0x6b) }, + { "Gray43", RGB_(0x6e, 0x6e, 0x6e) }, + { "Gray44", RGB_(0x70, 0x70, 0x70) }, + { "Gray45", RGB_(0x73, 0x73, 0x73) }, + { "Gray46", RGB_(0x75, 0x75, 0x75) }, + { "Gray47", RGB_(0x78, 0x78, 0x78) }, + { "Gray48", RGB_(0x7a, 0x7a, 0x7a) }, + { "Gray49", RGB_(0x7d, 0x7d, 0x7d) }, + { "Gray5", RGB_(0xd, 0xd, 0xd) }, + { "Gray50", RGB_(0x7f, 0x7f, 0x7f) }, + { "Gray51", RGB_(0x82, 0x82, 0x82) }, + { "Gray52", RGB_(0x85, 0x85, 0x85) }, + { "Gray53", RGB_(0x87, 0x87, 0x87) }, + { "Gray54", RGB_(0x8a, 0x8a, 0x8a) }, + { "Gray55", RGB_(0x8c, 0x8c, 0x8c) }, + { "Gray56", RGB_(0x8f, 0x8f, 0x8f) }, + { "Gray57", RGB_(0x91, 0x91, 0x91) }, + { "Gray58", RGB_(0x94, 0x94, 0x94) }, + { "Gray59", RGB_(0x96, 0x96, 0x96) }, + { "Gray6", RGB_(0xf, 0xf, 0xf) }, + { "Gray60", RGB_(0x99, 0x99, 0x99) }, + { "Gray61", RGB_(0x9c, 0x9c, 0x9c) }, + { "Gray62", RGB_(0x9e, 0x9e, 0x9e) }, + { "Gray63", RGB_(0xa1, 0xa1, 0xa1) }, + { "Gray64", RGB_(0xa3, 0xa3, 0xa3) }, + { "Gray65", RGB_(0xa6, 0xa6, 0xa6) }, + { "Gray66", RGB_(0xa8, 0xa8, 0xa8) }, + { "Gray67", RGB_(0xab, 0xab, 0xab) }, + { "Gray68", RGB_(0xad, 0xad, 0xad) }, + { "Gray69", RGB_(0xb0, 0xb0, 0xb0) }, + { "Gray7", RGB_(0x12, 0x12, 0x12) }, + { "Gray70", RGB_(0xb3, 0xb3, 0xb3) }, + { "Gray71", RGB_(0xb5, 0xb5, 0xb5) }, + { "Gray72", RGB_(0xb8, 0xb8, 0xb8) }, + { "Gray73", RGB_(0xba, 0xba, 0xba) }, + { "Gray74", RGB_(0xbd, 0xbd, 0xbd) }, + { "Gray75", RGB_(0xbf, 0xbf, 0xbf) }, + { "Gray76", RGB_(0xc2, 0xc2, 0xc2) }, + { "Gray77", RGB_(0xc4, 0xc4, 0xc4) }, + { "Gray78", RGB_(0xc7, 0xc7, 0xc7) }, + { "Gray79", RGB_(0xc9, 0xc9, 0xc9) }, + { "Gray8", RGB_(0x14, 0x14, 0x14) }, + { "Gray80", RGB_(0xcc, 0xcc, 0xcc) }, + { "Gray81", RGB_(0xcf, 0xcf, 0xcf) }, + { "Gray82", RGB_(0xd1, 0xd1, 0xd1) }, + { "Gray83", RGB_(0xd4, 0xd4, 0xd4) }, + { "Gray84", RGB_(0xd6, 0xd6, 0xd6) }, + { "Gray85", RGB_(0xd9, 0xd9, 0xd9) }, + { "Gray86", RGB_(0xdb, 0xdb, 0xdb) }, + { "Gray87", RGB_(0xde, 0xde, 0xde) }, + { "Gray88", RGB_(0xe0, 0xe0, 0xe0) }, + { "Gray89", RGB_(0xe3, 0xe3, 0xe3) }, + { "Gray9", RGB_(0x17, 0x17, 0x17) }, + { "Gray90", RGB_(0xe5, 0xe5, 0xe5) }, + { "Gray91", RGB_(0xe8, 0xe8, 0xe8) }, + { "Gray92", RGB_(0xeb, 0xeb, 0xeb) }, + { "Gray93", RGB_(0xed, 0xed, 0xed) }, + { "Gray94", RGB_(0xf0, 0xf0, 0xf0) }, + { "Gray95", RGB_(0xf2, 0xf2, 0xf2) }, + { "Gray96", RGB_(0xf5, 0xf5, 0xf5) }, + { "Gray97", RGB_(0xf7, 0xf7, 0xf7) }, + { "Gray98", RGB_(0xfa, 0xfa, 0xfa) }, + { "Gray99", RGB_(0xfc, 0xfc, 0xfc) }, + { "Green", RGB_(0x00, 0x80, 0x00) }, + { "Green1", RGB_(0x0, 0xff, 0x0) }, + { "Green2", RGB_(0x0, 0xee, 0x0) }, + { "Green3", RGB_(0x0, 0xcd, 0x0) }, + { "Green4", RGB_(0x0, 0x8b, 0x0) }, + { "GreenYellow", RGB_(0xad, 0xff, 0x2f) }, + { "Grey", RGB_(0x80, 0x80, 0x80) }, + { "Grey0", RGB_(0x0, 0x0, 0x0) }, + { "Grey1", RGB_(0x3, 0x3, 0x3) }, + { "Grey10", RGB_(0x1a, 0x1a, 0x1a) }, + { "Grey100", RGB_(0xff, 0xff, 0xff) }, + { "Grey11", RGB_(0x1c, 0x1c, 0x1c) }, + { "Grey12", RGB_(0x1f, 0x1f, 0x1f) }, + { "Grey13", RGB_(0x21, 0x21, 0x21) }, + { "Grey14", RGB_(0x24, 0x24, 0x24) }, + { "Grey15", RGB_(0x26, 0x26, 0x26) }, + { "Grey16", RGB_(0x29, 0x29, 0x29) }, + { "Grey17", RGB_(0x2b, 0x2b, 0x2b) }, + { "Grey18", RGB_(0x2e, 0x2e, 0x2e) }, + { "Grey19", RGB_(0x30, 0x30, 0x30) }, + { "Grey2", RGB_(0x5, 0x5, 0x5) }, + { "Grey20", RGB_(0x33, 0x33, 0x33) }, + { "Grey21", RGB_(0x36, 0x36, 0x36) }, + { "Grey22", RGB_(0x38, 0x38, 0x38) }, + { "Grey23", RGB_(0x3b, 0x3b, 0x3b) }, + { "Grey24", RGB_(0x3d, 0x3d, 0x3d) }, + { "Grey25", RGB_(0x40, 0x40, 0x40) }, + { "Grey26", RGB_(0x42, 0x42, 0x42) }, + { "Grey27", RGB_(0x45, 0x45, 0x45) }, + { "Grey28", RGB_(0x47, 0x47, 0x47) }, + { "Grey29", RGB_(0x4a, 0x4a, 0x4a) }, + { "Grey3", RGB_(0x8, 0x8, 0x8) }, + { "Grey30", RGB_(0x4d, 0x4d, 0x4d) }, + { "Grey31", RGB_(0x4f, 0x4f, 0x4f) }, + { "Grey32", RGB_(0x52, 0x52, 0x52) }, + { "Grey33", RGB_(0x54, 0x54, 0x54) }, + { "Grey34", RGB_(0x57, 0x57, 0x57) }, + { "Grey35", RGB_(0x59, 0x59, 0x59) }, + { "Grey36", RGB_(0x5c, 0x5c, 0x5c) }, + { "Grey37", RGB_(0x5e, 0x5e, 0x5e) }, + { "Grey38", RGB_(0x61, 0x61, 0x61) }, + { "Grey39", RGB_(0x63, 0x63, 0x63) }, + { "Grey4", RGB_(0xa, 0xa, 0xa) }, + { "Grey40", RGB_(0x66, 0x66, 0x66) }, + { "Grey41", RGB_(0x69, 0x69, 0x69) }, + { "Grey42", RGB_(0x6b, 0x6b, 0x6b) }, + { "Grey43", RGB_(0x6e, 0x6e, 0x6e) }, + { "Grey44", RGB_(0x70, 0x70, 0x70) }, + { "Grey45", RGB_(0x73, 0x73, 0x73) }, + { "Grey46", RGB_(0x75, 0x75, 0x75) }, + { "Grey47", RGB_(0x78, 0x78, 0x78) }, + { "Grey48", RGB_(0x7a, 0x7a, 0x7a) }, + { "Grey49", RGB_(0x7d, 0x7d, 0x7d) }, + { "Grey5", RGB_(0xd, 0xd, 0xd) }, + { "Grey50", RGB_(0x7f, 0x7f, 0x7f) }, + { "Grey51", RGB_(0x82, 0x82, 0x82) }, + { "Grey52", RGB_(0x85, 0x85, 0x85) }, + { "Grey53", RGB_(0x87, 0x87, 0x87) }, + { "Grey54", RGB_(0x8a, 0x8a, 0x8a) }, + { "Grey55", RGB_(0x8c, 0x8c, 0x8c) }, + { "Grey56", RGB_(0x8f, 0x8f, 0x8f) }, + { "Grey57", RGB_(0x91, 0x91, 0x91) }, + { "Grey58", RGB_(0x94, 0x94, 0x94) }, + { "Grey59", RGB_(0x96, 0x96, 0x96) }, + { "Grey6", RGB_(0xf, 0xf, 0xf) }, + { "Grey60", RGB_(0x99, 0x99, 0x99) }, + { "Grey61", RGB_(0x9c, 0x9c, 0x9c) }, + { "Grey62", RGB_(0x9e, 0x9e, 0x9e) }, + { "Grey63", RGB_(0xa1, 0xa1, 0xa1) }, + { "Grey64", RGB_(0xa3, 0xa3, 0xa3) }, + { "Grey65", RGB_(0xa6, 0xa6, 0xa6) }, + { "Grey66", RGB_(0xa8, 0xa8, 0xa8) }, + { "Grey67", RGB_(0xab, 0xab, 0xab) }, + { "Grey68", RGB_(0xad, 0xad, 0xad) }, + { "Grey69", RGB_(0xb0, 0xb0, 0xb0) }, + { "Grey7", RGB_(0x12, 0x12, 0x12) }, + { "Grey70", RGB_(0xb3, 0xb3, 0xb3) }, + { "Grey71", RGB_(0xb5, 0xb5, 0xb5) }, + { "Grey72", RGB_(0xb8, 0xb8, 0xb8) }, + { "Grey73", RGB_(0xba, 0xba, 0xba) }, + { "Grey74", RGB_(0xbd, 0xbd, 0xbd) }, + { "Grey75", RGB_(0xbf, 0xbf, 0xbf) }, + { "Grey76", RGB_(0xc2, 0xc2, 0xc2) }, + { "Grey77", RGB_(0xc4, 0xc4, 0xc4) }, + { "Grey78", RGB_(0xc7, 0xc7, 0xc7) }, + { "Grey79", RGB_(0xc9, 0xc9, 0xc9) }, + { "Grey8", RGB_(0x14, 0x14, 0x14) }, + { "Grey80", RGB_(0xcc, 0xcc, 0xcc) }, + { "Grey81", RGB_(0xcf, 0xcf, 0xcf) }, + { "Grey82", RGB_(0xd1, 0xd1, 0xd1) }, + { "Grey83", RGB_(0xd4, 0xd4, 0xd4) }, + { "Grey84", RGB_(0xd6, 0xd6, 0xd6) }, + { "Grey85", RGB_(0xd9, 0xd9, 0xd9) }, + { "Grey86", RGB_(0xdb, 0xdb, 0xdb) }, + { "Grey87", RGB_(0xde, 0xde, 0xde) }, + { "Grey88", RGB_(0xe0, 0xe0, 0xe0) }, + { "Grey89", RGB_(0xe3, 0xe3, 0xe3) }, + { "Grey9", RGB_(0x17, 0x17, 0x17) }, + { "Grey90", RGB_(0xe5, 0xe5, 0xe5) }, + { "Grey91", RGB_(0xe8, 0xe8, 0xe8) }, + { "Grey92", RGB_(0xeb, 0xeb, 0xeb) }, + { "Grey93", RGB_(0xed, 0xed, 0xed) }, + { "Grey94", RGB_(0xf0, 0xf0, 0xf0) }, + { "Grey95", RGB_(0xf2, 0xf2, 0xf2) }, + { "Grey96", RGB_(0xf5, 0xf5, 0xf5) }, + { "Grey97", RGB_(0xf7, 0xf7, 0xf7) }, + { "Grey98", RGB_(0xfa, 0xfa, 0xfa) }, + { "Grey99", RGB_(0xfc, 0xfc, 0xfc) }, + { "Honeydew", RGB_(0xf0, 0xff, 0xf0) }, + { "Honeydew1", RGB_(0xf0, 0xff, 0xf0) }, + { "Honeydew2", RGB_(0xe0, 0xee, 0xe0) }, + { "Honeydew3", RGB_(0xc1, 0xcd, 0xc1) }, + { "Honeydew4", RGB_(0x83, 0x8b, 0x83) }, + { "HotPink", RGB_(0xff, 0x69, 0xb4) }, + { "HotPink1", RGB_(0xff, 0x6e, 0xb4) }, + { "HotPink2", RGB_(0xee, 0x6a, 0xa7) }, + { "HotPink3", RGB_(0xcd, 0x60, 0x90) }, + { "HotPink4", RGB_(0x8b, 0x3a, 0x62) }, + { "IndianRed", RGB_(0xcd, 0x5c, 0x5c) }, + { "IndianRed1", RGB_(0xff, 0x6a, 0x6a) }, + { "IndianRed2", RGB_(0xee, 0x63, 0x63) }, + { "IndianRed3", RGB_(0xcd, 0x55, 0x55) }, + { "IndianRed4", RGB_(0x8b, 0x3a, 0x3a) }, + { "Indigo", RGB_(0x4b, 0x00, 0x82) }, + { "Ivory", RGB_(0xff, 0xff, 0xf0) }, + { "Ivory1", RGB_(0xff, 0xff, 0xf0) }, + { "Ivory2", RGB_(0xee, 0xee, 0xe0) }, + { "Ivory3", RGB_(0xcd, 0xcd, 0xc1) }, + { "Ivory4", RGB_(0x8b, 0x8b, 0x83) }, + { "Khaki", RGB_(0xf0, 0xe6, 0x8c) }, + { "Khaki1", RGB_(0xff, 0xf6, 0x8f) }, + { "Khaki2", RGB_(0xee, 0xe6, 0x85) }, + { "Khaki3", RGB_(0xcd, 0xc6, 0x73) }, + { "Khaki4", RGB_(0x8b, 0x86, 0x4e) }, + { "Lavender", RGB_(0xe6, 0xe6, 0xfa) }, + { "LavenderBlush", RGB_(0xff, 0xf0, 0xf5) }, + { "LavenderBlush1", RGB_(0xff, 0xf0, 0xf5) }, + { "LavenderBlush2", RGB_(0xee, 0xe0, 0xe5) }, + { "LavenderBlush3", RGB_(0xcd, 0xc1, 0xc5) }, + { "LavenderBlush4", RGB_(0x8b, 0x83, 0x86) }, + { "LawnGreen", RGB_(0x7c, 0xfc, 0x00) }, + { "LemonChiffon", RGB_(0xff, 0xfa, 0xcd) }, + { "LemonChiffon1", RGB_(0xff, 0xfa, 0xcd) }, + { "LemonChiffon2", RGB_(0xee, 0xe9, 0xbf) }, + { "LemonChiffon3", RGB_(0xcd, 0xc9, 0xa5) }, + { "LemonChiffon4", RGB_(0x8b, 0x89, 0x70) }, + { "LightBlue", RGB_(0xad, 0xd8, 0xe6) }, + { "LightBlue1", RGB_(0xbf, 0xef, 0xff) }, + { "LightBlue2", RGB_(0xb2, 0xdf, 0xee) }, + { "LightBlue3", RGB_(0x9a, 0xc0, 0xcd) }, + { "LightBlue4", RGB_(0x68, 0x83, 0x8b) }, + { "LightCoral", RGB_(0xf0, 0x80, 0x80) }, + { "LightCyan", RGB_(0xe0, 0xff, 0xff) }, + { "LightCyan1", RGB_(0xe0, 0xff, 0xff) }, + { "LightCyan2", RGB_(0xd1, 0xee, 0xee) }, + { "LightCyan3", RGB_(0xb4, 0xcd, 0xcd) }, + { "LightCyan4", RGB_(0x7a, 0x8b, 0x8b) }, + { "LightGoldenrod", RGB_(0xee, 0xdd, 0x82) }, + { "LightGoldenrod1", RGB_(0xff, 0xec, 0x8b) }, + { "LightGoldenrod2", RGB_(0xee, 0xdc, 0x82) }, + { "LightGoldenrod3", RGB_(0xcd, 0xbe, 0x70) }, + { "LightGoldenrod4", RGB_(0x8b, 0x81, 0x4c) }, + { "LightGoldenRodYellow", RGB_(0xfa, 0xfa, 0xd2) }, + { "LightGray", RGB_(0xd3, 0xd3, 0xd3) }, + { "LightGreen", RGB_(0x90, 0xee, 0x90) }, + { "LightGrey", RGB_(0xd3, 0xd3, 0xd3) }, + { "LightMagenta", RGB_(0xff, 0xbb, 0xff) }, + { "LightPink", RGB_(0xff, 0xb6, 0xc1) }, + { "LightPink1", RGB_(0xff, 0xae, 0xb9) }, + { "LightPink2", RGB_(0xee, 0xa2, 0xad) }, + { "LightPink3", RGB_(0xcd, 0x8c, 0x95) }, + { "LightPink4", RGB_(0x8b, 0x5f, 0x65) }, + { "LightRed", RGB_(0xff, 0xbb, 0xbb) }, + { "LightSalmon", RGB_(0xff, 0xa0, 0x7a) }, + { "LightSalmon1", RGB_(0xff, 0xa0, 0x7a) }, + { "LightSalmon2", RGB_(0xee, 0x95, 0x72) }, + { "LightSalmon3", RGB_(0xcd, 0x81, 0x62) }, + { "LightSalmon4", RGB_(0x8b, 0x57, 0x42) }, + { "LightSeaGreen", RGB_(0x20, 0xb2, 0xaa) }, + { "LightSkyBlue", RGB_(0x87, 0xce, 0xfa) }, + { "LightSkyBlue1", RGB_(0xb0, 0xe2, 0xff) }, + { "LightSkyBlue2", RGB_(0xa4, 0xd3, 0xee) }, + { "LightSkyBlue3", RGB_(0x8d, 0xb6, 0xcd) }, + { "LightSkyBlue4", RGB_(0x60, 0x7b, 0x8b) }, + { "LightSlateBlue", RGB_(0x84, 0x70, 0xff) }, + { "LightSlateGray", RGB_(0x77, 0x88, 0x99) }, + { "LightSlateGrey", RGB_(0x77, 0x88, 0x99) }, + { "LightSteelBlue", RGB_(0xb0, 0xc4, 0xde) }, + { "LightSteelBlue1", RGB_(0xca, 0xe1, 0xff) }, + { "LightSteelBlue2", RGB_(0xbc, 0xd2, 0xee) }, + { "LightSteelBlue3", RGB_(0xa2, 0xb5, 0xcd) }, + { "LightSteelBlue4", RGB_(0x6e, 0x7b, 0x8b) }, + { "LightYellow", RGB_(0xff, 0xff, 0xe0) }, + { "LightYellow1", RGB_(0xff, 0xff, 0xe0) }, + { "LightYellow2", RGB_(0xee, 0xee, 0xd1) }, + { "LightYellow3", RGB_(0xcd, 0xcd, 0xb4) }, + { "LightYellow4", RGB_(0x8b, 0x8b, 0x7a) }, + { "Lime", RGB_(0x00, 0xff, 0x00) }, + { "LimeGreen", RGB_(0x32, 0xcd, 0x32) }, + { "Linen", RGB_(0xfa, 0xf0, 0xe6) }, + { "Magenta", RGB_(0xff, 0x00, 0xff) }, + { "Magenta1", RGB_(0xff, 0x0, 0xff) }, + { "Magenta2", RGB_(0xee, 0x0, 0xee) }, + { "Magenta3", RGB_(0xcd, 0x0, 0xcd) }, + { "Magenta4", RGB_(0x8b, 0x0, 0x8b) }, + { "Maroon", RGB_(0x80, 0x00, 0x00) }, + { "Maroon1", RGB_(0xff, 0x34, 0xb3) }, + { "Maroon2", RGB_(0xee, 0x30, 0xa7) }, + { "Maroon3", RGB_(0xcd, 0x29, 0x90) }, + { "Maroon4", RGB_(0x8b, 0x1c, 0x62) }, + { "MediumAquamarine", RGB_(0x66, 0xcd, 0xaa) }, + { "MediumBlue", RGB_(0x00, 0x00, 0xcd) }, + { "MediumOrchid", RGB_(0xba, 0x55, 0xd3) }, + { "MediumOrchid1", RGB_(0xe0, 0x66, 0xff) }, + { "MediumOrchid2", RGB_(0xd1, 0x5f, 0xee) }, + { "MediumOrchid3", RGB_(0xb4, 0x52, 0xcd) }, + { "MediumOrchid4", RGB_(0x7a, 0x37, 0x8b) }, + { "MediumPurple", RGB_(0x93, 0x70, 0xdb) }, + { "MediumPurple1", RGB_(0xab, 0x82, 0xff) }, + { "MediumPurple2", RGB_(0x9f, 0x79, 0xee) }, + { "MediumPurple3", RGB_(0x89, 0x68, 0xcd) }, + { "MediumPurple4", RGB_(0x5d, 0x47, 0x8b) }, + { "MediumSeaGreen", RGB_(0x3c, 0xb3, 0x71) }, + { "MediumSlateBlue", RGB_(0x7b, 0x68, 0xee) }, + { "MediumSpringGreen", RGB_(0x00, 0xfa, 0x9a) }, + { "MediumTurquoise", RGB_(0x48, 0xd1, 0xcc) }, + { "MediumVioletRed", RGB_(0xc7, 0x15, 0x85) }, + { "MidnightBlue", RGB_(0x19, 0x19, 0x70) }, + { "MintCream", RGB_(0xf5, 0xff, 0xfa) }, + { "MistyRose", RGB_(0xff, 0xe4, 0xe1) }, + { "MistyRose1", RGB_(0xff, 0xe4, 0xe1) }, + { "MistyRose2", RGB_(0xee, 0xd5, 0xd2) }, + { "MistyRose3", RGB_(0xcd, 0xb7, 0xb5) }, + { "MistyRose4", RGB_(0x8b, 0x7d, 0x7b) }, + { "Moccasin", RGB_(0xff, 0xe4, 0xb5) }, + { "NavajoWhite", RGB_(0xff, 0xde, 0xad) }, + { "NavajoWhite1", RGB_(0xff, 0xde, 0xad) }, + { "NavajoWhite2", RGB_(0xee, 0xcf, 0xa1) }, + { "NavajoWhite3", RGB_(0xcd, 0xb3, 0x8b) }, + { "NavajoWhite4", RGB_(0x8b, 0x79, 0x5e) }, + { "Navy", RGB_(0x00, 0x00, 0x80) }, + { "NavyBlue", RGB_(0x0, 0x0, 0x80) }, + { "OldLace", RGB_(0xfd, 0xf5, 0xe6) }, + { "Olive", RGB_(0x80, 0x80, 0x00) }, + { "OliveDrab", RGB_(0x6b, 0x8e, 0x23) }, + { "OliveDrab1", RGB_(0xc0, 0xff, 0x3e) }, + { "OliveDrab2", RGB_(0xb3, 0xee, 0x3a) }, + { "OliveDrab3", RGB_(0x9a, 0xcd, 0x32) }, + { "OliveDrab4", RGB_(0x69, 0x8b, 0x22) }, + { "Orange", RGB_(0xff, 0xa5, 0x00) }, + { "Orange1", RGB_(0xff, 0xa5, 0x0) }, + { "Orange2", RGB_(0xee, 0x9a, 0x0) }, + { "Orange3", RGB_(0xcd, 0x85, 0x0) }, + { "Orange4", RGB_(0x8b, 0x5a, 0x0) }, + { "OrangeRed", RGB_(0xff, 0x45, 0x00) }, + { "OrangeRed1", RGB_(0xff, 0x45, 0x0) }, + { "OrangeRed2", RGB_(0xee, 0x40, 0x0) }, + { "OrangeRed3", RGB_(0xcd, 0x37, 0x0) }, + { "OrangeRed4", RGB_(0x8b, 0x25, 0x0) }, + { "Orchid", RGB_(0xda, 0x70, 0xd6) }, + { "Orchid1", RGB_(0xff, 0x83, 0xfa) }, + { "Orchid2", RGB_(0xee, 0x7a, 0xe9) }, + { "Orchid3", RGB_(0xcd, 0x69, 0xc9) }, + { "Orchid4", RGB_(0x8b, 0x47, 0x89) }, + { "PaleGoldenRod", RGB_(0xee, 0xe8, 0xaa) }, + { "PaleGreen", RGB_(0x98, 0xfb, 0x98) }, + { "PaleGreen1", RGB_(0x9a, 0xff, 0x9a) }, + { "PaleGreen2", RGB_(0x90, 0xee, 0x90) }, + { "PaleGreen3", RGB_(0x7c, 0xcd, 0x7c) }, + { "PaleGreen4", RGB_(0x54, 0x8b, 0x54) }, + { "PaleTurquoise", RGB_(0xaf, 0xee, 0xee) }, + { "PaleTurquoise1", RGB_(0xbb, 0xff, 0xff) }, + { "PaleTurquoise2", RGB_(0xae, 0xee, 0xee) }, + { "PaleTurquoise3", RGB_(0x96, 0xcd, 0xcd) }, + { "PaleTurquoise4", RGB_(0x66, 0x8b, 0x8b) }, + { "PaleVioletRed", RGB_(0xdb, 0x70, 0x93) }, + { "PaleVioletRed1", RGB_(0xff, 0x82, 0xab) }, + { "PaleVioletRed2", RGB_(0xee, 0x79, 0x9f) }, + { "PaleVioletRed3", RGB_(0xcd, 0x68, 0x89) }, + { "PaleVioletRed4", RGB_(0x8b, 0x47, 0x5d) }, + { "PapayaWhip", RGB_(0xff, 0xef, 0xd5) }, + { "PeachPuff", RGB_(0xff, 0xda, 0xb9) }, + { "PeachPuff1", RGB_(0xff, 0xda, 0xb9) }, + { "PeachPuff2", RGB_(0xee, 0xcb, 0xad) }, + { "PeachPuff3", RGB_(0xcd, 0xaf, 0x95) }, + { "PeachPuff4", RGB_(0x8b, 0x77, 0x65) }, + { "Peru", RGB_(0xcd, 0x85, 0x3f) }, + { "Pink", RGB_(0xff, 0xc0, 0xcb) }, + { "Pink1", RGB_(0xff, 0xb5, 0xc5) }, + { "Pink2", RGB_(0xee, 0xa9, 0xb8) }, + { "Pink3", RGB_(0xcd, 0x91, 0x9e) }, + { "Pink4", RGB_(0x8b, 0x63, 0x6c) }, + { "Plum", RGB_(0xdd, 0xa0, 0xdd) }, + { "Plum1", RGB_(0xff, 0xbb, 0xff) }, + { "Plum2", RGB_(0xee, 0xae, 0xee) }, + { "Plum3", RGB_(0xcd, 0x96, 0xcd) }, + { "Plum4", RGB_(0x8b, 0x66, 0x8b) }, + { "PowderBlue", RGB_(0xb0, 0xe0, 0xe6) }, + { "Purple", RGB_(0x80, 0x00, 0x80) }, + { "Purple1", RGB_(0x9b, 0x30, 0xff) }, + { "Purple2", RGB_(0x91, 0x2c, 0xee) }, + { "Purple3", RGB_(0x7d, 0x26, 0xcd) }, + { "Purple4", RGB_(0x55, 0x1a, 0x8b) }, + { "RebeccaPurple", RGB_(0x66, 0x33, 0x99) }, + { "Red", RGB_(0xff, 0x00, 0x00) }, + { "Red1", RGB_(0xff, 0x0, 0x0) }, + { "Red2", RGB_(0xee, 0x0, 0x0) }, + { "Red3", RGB_(0xcd, 0x0, 0x0) }, + { "Red4", RGB_(0x8b, 0x0, 0x0) }, + { "RosyBrown", RGB_(0xbc, 0x8f, 0x8f) }, + { "RosyBrown1", RGB_(0xff, 0xc1, 0xc1) }, + { "RosyBrown2", RGB_(0xee, 0xb4, 0xb4) }, + { "RosyBrown3", RGB_(0xcd, 0x9b, 0x9b) }, + { "RosyBrown4", RGB_(0x8b, 0x69, 0x69) }, + { "RoyalBlue", RGB_(0x41, 0x69, 0xe1) }, + { "RoyalBlue1", RGB_(0x48, 0x76, 0xff) }, + { "RoyalBlue2", RGB_(0x43, 0x6e, 0xee) }, + { "RoyalBlue3", RGB_(0x3a, 0x5f, 0xcd) }, + { "RoyalBlue4", RGB_(0x27, 0x40, 0x8b) }, + { "SaddleBrown", RGB_(0x8b, 0x45, 0x13) }, + { "Salmon", RGB_(0xfa, 0x80, 0x72) }, + { "Salmon1", RGB_(0xff, 0x8c, 0x69) }, + { "Salmon2", RGB_(0xee, 0x82, 0x62) }, + { "Salmon3", RGB_(0xcd, 0x70, 0x54) }, + { "Salmon4", RGB_(0x8b, 0x4c, 0x39) }, + { "SandyBrown", RGB_(0xf4, 0xa4, 0x60) }, + { "SeaGreen", RGB_(0x2e, 0x8b, 0x57) }, + { "SeaGreen1", RGB_(0x54, 0xff, 0x9f) }, + { "SeaGreen2", RGB_(0x4e, 0xee, 0x94) }, + { "SeaGreen3", RGB_(0x43, 0xcd, 0x80) }, + { "SeaGreen4", RGB_(0x2e, 0x8b, 0x57) }, + { "SeaShell", RGB_(0xff, 0xf5, 0xee) }, + { "Seashell1", RGB_(0xff, 0xf5, 0xee) }, + { "Seashell2", RGB_(0xee, 0xe5, 0xde) }, + { "Seashell3", RGB_(0xcd, 0xc5, 0xbf) }, + { "Seashell4", RGB_(0x8b, 0x86, 0x82) }, + { "Sienna", RGB_(0xa0, 0x52, 0x2d) }, + { "Sienna1", RGB_(0xff, 0x82, 0x47) }, + { "Sienna2", RGB_(0xee, 0x79, 0x42) }, + { "Sienna3", RGB_(0xcd, 0x68, 0x39) }, + { "Sienna4", RGB_(0x8b, 0x47, 0x26) }, + { "Silver", RGB_(0xc0, 0xc0, 0xc0) }, + { "SkyBlue", RGB_(0x87, 0xce, 0xeb) }, + { "SkyBlue1", RGB_(0x87, 0xce, 0xff) }, + { "SkyBlue2", RGB_(0x7e, 0xc0, 0xee) }, + { "SkyBlue3", RGB_(0x6c, 0xa6, 0xcd) }, + { "SkyBlue4", RGB_(0x4a, 0x70, 0x8b) }, + { "SlateBlue", RGB_(0x6a, 0x5a, 0xcd) }, + { "SlateBlue1", RGB_(0x83, 0x6f, 0xff) }, + { "SlateBlue2", RGB_(0x7a, 0x67, 0xee) }, + { "SlateBlue3", RGB_(0x69, 0x59, 0xcd) }, + { "SlateBlue4", RGB_(0x47, 0x3c, 0x8b) }, + { "SlateGray", RGB_(0x70, 0x80, 0x90) }, + { "SlateGray1", RGB_(0xc6, 0xe2, 0xff) }, + { "SlateGray2", RGB_(0xb9, 0xd3, 0xee) }, + { "SlateGray3", RGB_(0x9f, 0xb6, 0xcd) }, + { "SlateGray4", RGB_(0x6c, 0x7b, 0x8b) }, + { "SlateGrey", RGB_(0x70, 0x80, 0x90) }, + { "Snow", RGB_(0xff, 0xfa, 0xfa) }, + { "Snow1", RGB_(0xff, 0xfa, 0xfa) }, + { "Snow2", RGB_(0xee, 0xe9, 0xe9) }, + { "Snow3", RGB_(0xcd, 0xc9, 0xc9) }, + { "Snow4", RGB_(0x8b, 0x89, 0x89) }, + { "SpringGreen", RGB_(0x00, 0xff, 0x7f) }, + { "SpringGreen1", RGB_(0x0, 0xff, 0x7f) }, + { "SpringGreen2", RGB_(0x0, 0xee, 0x76) }, + { "SpringGreen3", RGB_(0x0, 0xcd, 0x66) }, + { "SpringGreen4", RGB_(0x0, 0x8b, 0x45) }, + { "SteelBlue", RGB_(0x46, 0x82, 0xb4) }, + { "SteelBlue1", RGB_(0x63, 0xb8, 0xff) }, + { "SteelBlue2", RGB_(0x5c, 0xac, 0xee) }, + { "SteelBlue3", RGB_(0x4f, 0x94, 0xcd) }, + { "SteelBlue4", RGB_(0x36, 0x64, 0x8b) }, + { "Tan", RGB_(0xd2, 0xb4, 0x8c) }, + { "Tan1", RGB_(0xff, 0xa5, 0x4f) }, + { "Tan2", RGB_(0xee, 0x9a, 0x49) }, + { "Tan3", RGB_(0xcd, 0x85, 0x3f) }, + { "Tan4", RGB_(0x8b, 0x5a, 0x2b) }, + { "Teal", RGB_(0x00, 0x80, 0x80) }, + { "Thistle", RGB_(0xd8, 0xbf, 0xd8) }, + { "Thistle1", RGB_(0xff, 0xe1, 0xff) }, + { "Thistle2", RGB_(0xee, 0xd2, 0xee) }, + { "Thistle3", RGB_(0xcd, 0xb5, 0xcd) }, + { "Thistle4", RGB_(0x8b, 0x7b, 0x8b) }, + { "Tomato", RGB_(0xff, 0x63, 0x47) }, + { "Tomato1", RGB_(0xff, 0x63, 0x47) }, + { "Tomato2", RGB_(0xee, 0x5c, 0x42) }, + { "Tomato3", RGB_(0xcd, 0x4f, 0x39) }, + { "Tomato4", RGB_(0x8b, 0x36, 0x26) }, + { "Turquoise", RGB_(0x40, 0xe0, 0xd0) }, + { "Turquoise1", RGB_(0x0, 0xf5, 0xff) }, + { "Turquoise2", RGB_(0x0, 0xe5, 0xee) }, + { "Turquoise3", RGB_(0x0, 0xc5, 0xcd) }, + { "Turquoise4", RGB_(0x0, 0x86, 0x8b) }, + { "Violet", RGB_(0xee, 0x82, 0xee) }, + { "VioletRed", RGB_(0xd0, 0x20, 0x90) }, + { "VioletRed1", RGB_(0xff, 0x3e, 0x96) }, + { "VioletRed2", RGB_(0xee, 0x3a, 0x8c) }, + { "VioletRed3", RGB_(0xcd, 0x32, 0x78) }, + { "VioletRed4", RGB_(0x8b, 0x22, 0x52) }, + { "WebGray", RGB_(0x80, 0x80, 0x80) }, + { "WebGreen", RGB_(0x0, 0x80, 0x0) }, + { "WebGrey", RGB_(0x80, 0x80, 0x80) }, + { "WebMaroon", RGB_(0x80, 0x0, 0x0) }, + { "WebPurple", RGB_(0x80, 0x0, 0x80) }, + { "Wheat", RGB_(0xf5, 0xde, 0xb3) }, + { "Wheat1", RGB_(0xff, 0xe7, 0xba) }, + { "Wheat2", RGB_(0xee, 0xd8, 0xae) }, + { "Wheat3", RGB_(0xcd, 0xba, 0x96) }, + { "Wheat4", RGB_(0x8b, 0x7e, 0x66) }, + { "White", RGB_(0xff, 0xff, 0xff) }, + { "WhiteSmoke", RGB_(0xf5, 0xf5, 0xf5) }, + { "X11Gray", RGB_(0xbe, 0xbe, 0xbe) }, + { "X11Green", RGB_(0x0, 0xff, 0x0) }, + { "X11Grey", RGB_(0xbe, 0xbe, 0xbe) }, + { "X11Maroon", RGB_(0xb0, 0x30, 0x60) }, + { "X11Purple", RGB_(0xa0, 0x20, 0xf0) }, + { "Yellow", RGB_(0xff, 0xff, 0x00) }, + { "Yellow1", RGB_(0xff, 0xff, 0x0) }, + { "Yellow2", RGB_(0xee, 0xee, 0x0) }, + { "Yellow3", RGB_(0xcd, 0xcd, 0x0) }, + { "Yellow4", RGB_(0x8b, 0x8b, 0x0) }, + { "YellowGreen", RGB_(0x9a, 0xcd, 0x32) }, { NULL, 0 }, }; @@ -8182,7 +8488,7 @@ color_name_table_T color_name_table[] = { /// /// @param[in] name string value to convert to RGB /// return the hex value or -1 if could not find a correct value -RgbValue name_to_color(const uint8_t *name) +RgbValue name_to_color(const char_u *name) { if (name[0] == '#' && isxdigit(name[1]) && isxdigit(name[2]) @@ -8205,6 +8511,27 @@ RgbValue name_to_color(const uint8_t *name) return -1; } +/// Gets highlight description for id `attr_id` as a map. +Dictionary hl_get_attr_by_id(Integer attr_id, Boolean rgb, Error *err) +{ + HlAttrs *aep = NULL; + Dictionary dic = ARRAY_DICT_INIT; + + if (attr_id == 0) { + return dic; + } + + aep = syn_cterm_attr2entry((int)attr_id); + if (!aep) { + api_set_error(err, kErrorTypeException, + "Invalid attribute id: %" PRId64, attr_id); + return dic; + } + + return hlattrs2dict(aep, rgb); +} + + /************************************** * End of Highlighting stuff * **************************************/ diff --git a/src/nvim/syntax.h b/src/nvim/syntax.h index bb733ead30..f8282955a6 100644 --- a/src/nvim/syntax.h +++ b/src/nvim/syntax.h @@ -6,19 +6,6 @@ #include "nvim/buffer_defs.h" #include "nvim/ex_cmds_defs.h" -/// Terminal highlighting attribute bits. -/// Attributes above HL_ALL are used for syntax highlighting. -/// \addtogroup HL_ATTRIBUTES -/// @{ -#define HL_NORMAL 0x00 -#define HL_INVERSE 0x01 -#define HL_BOLD 0x02 -#define HL_ITALIC 0x04 -#define HL_UNDERLINE 0x08 -#define HL_UNDERCURL 0x10 -#define HL_STANDOUT 0x20 -/// @} - #define HL_CONTAINED 0x01 /* not used on toplevel */ #define HL_TRANSP 0x02 /* has no highlighting */ #define HL_ONELINE 0x04 /* match within one line only */ @@ -45,6 +32,9 @@ typedef struct { } color_name_table_T; extern color_name_table_T color_name_table[]; +/// Array of highlight definitions, used for unit testing +extern const char *const highlight_init_cmdline[]; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "syntax.h.generated.h" #endif diff --git a/src/nvim/syntax_defs.h b/src/nvim/syntax_defs.h index 9f309451b0..63089a62af 100644 --- a/src/nvim/syntax_defs.h +++ b/src/nvim/syntax_defs.h @@ -2,7 +2,6 @@ #define NVIM_SYNTAX_DEFS_H #include "nvim/highlight_defs.h" -#include "nvim/regexp_defs.h" # define SST_MIN_ENTRIES 150 /* minimal size for state stack array */ # define SST_MAX_ENTRIES 1000 /* maximal size for state stack array */ @@ -10,6 +9,11 @@ # define SST_DIST 16 /* normal distance between entries */ # define SST_INVALID (synstate_T *)-1 /* invalid syn_state pointer */ +typedef struct syn_state synstate_T; + +#include "nvim/buffer_defs.h" +#include "nvim/regexp_defs.h" + typedef unsigned short disptick_T; /* display tick type */ /* struct passed to in_id_list() */ @@ -48,8 +52,6 @@ typedef struct buf_state { * syn_state contains the syntax state stack for the start of one line. * Used by b_sst_array[]. */ -typedef struct syn_state synstate_T; - struct syn_state { synstate_T *sst_next; /* next entry in used or free list */ linenr_T sst_lnum; /* line number for this state */ @@ -66,11 +68,4 @@ struct syn_state { * may have made the state invalid */ }; -// Structure shared between syntax.c, screen.c -typedef struct attr_entry { - int16_t rgb_ae_attr, cterm_ae_attr; // HL_BOLD, etc. - RgbValue rgb_fg_color, rgb_bg_color, rgb_sp_color; - int cterm_fg_color, cterm_bg_color; -} attrentry_T; - #endif // NVIM_SYNTAX_DEFS_H diff --git a/src/nvim/tag.c b/src/nvim/tag.c index be9d621c7d..f23465e501 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -667,7 +667,7 @@ do_tag ( fname = xmalloc(MAXPATHL + 1); cmd = xmalloc(CMDBUFFSIZE + 1); - list = tv_list_alloc(); + list = tv_list_alloc(num_matches); for (i = 0; i < num_matches; ++i) { int len, cmd_len; diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 1882f263db..276b47536f 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -43,6 +43,7 @@ #include <vterm.h> +#include "nvim/log.h" #include "nvim/vim.h" #include "nvim/terminal.h" #include "nvim/message.h" @@ -60,7 +61,6 @@ #include "nvim/edit.h" #include "nvim/mouse.h" #include "nvim/memline.h" -#include "nvim/mark.h" #include "nvim/map.h" #include "nvim/misc1.h" #include "nvim/move.h" @@ -180,13 +180,14 @@ void terminal_init(void) vterm_state_get_palette_color(state, color_index, &color); } map_put(int, int)(color_indexes, - RGB(color.red, color.green, color.blue), color_index + 1); + RGB_(color.red, color.green, color.blue), + color_index + 1); } VTermColor fg, bg; vterm_state_get_default_colors(state, &fg, &bg); - default_vt_fg = RGB(fg.red, fg.green, fg.blue); - default_vt_bg = RGB(bg.red, bg.green, bg.blue); + default_vt_fg = RGB_(fg.red, fg.green, fg.blue); + default_vt_bg = RGB_(bg.red, bg.green, bg.blue); default_vt_bg_rgb = bg; vterm_free(vt); } @@ -229,7 +230,7 @@ Terminal *terminal_open(TerminalOptions opts) rv->invalid_start = 0; rv->invalid_end = opts.height; refresh_screen(rv, curbuf); - set_option_value("buftype", 0, "terminal", OPT_LOCAL); + set_option_value("buftype", 0, "terminal", OPT_LOCAL); // -V666 // Default settings for terminal buffers curbuf->b_p_ma = false; // 'nomodifiable' @@ -375,8 +376,6 @@ void terminal_enter(void) // Ensure the terminal is properly sized. terminal_resize(s->term, 0, 0); - checkpcmark(); - setpcmark(); int save_state = State; s->save_rd = RedrawingDisabled; State = TERM_FOCUS; @@ -431,14 +430,6 @@ static int terminal_execute(VimState *state, int key) TerminalState *s = (TerminalState *)state; switch (key) { - case K_FOCUSGAINED: // nvim has been given focus - apply_autocmds(EVENT_FOCUSGAINED, NULL, NULL, false, curbuf); - break; - - case K_FOCUSLOST: // nvim has lost focus - apply_autocmds(EVENT_FOCUSLOST, NULL, NULL, false, curbuf); - break; - // Temporary fix until paste events gets implemented case K_PASTE: break; @@ -470,6 +461,10 @@ static int terminal_execute(VimState *state, int key) } break; + case K_COMMAND: + do_cmdline(NULL, getcmdkeycmd, NULL, 0); + break; + case Ctrl_N: if (s->got_bsl) { return 0; @@ -529,6 +524,12 @@ void terminal_send(Terminal *term, char *data, size_t size) void terminal_send_key(Terminal *term, int c) { VTermModifier mod = VTERM_MOD_NONE; + + // Convert K_ZERO back to ASCII + if (c == K_ZERO) { + c = Ctrl_AT; + } + VTermKey key = convert_key(c, &mod); if (key) { @@ -553,7 +554,7 @@ void terminal_receive(Terminal *term, char *data, size_t len) } void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, - int *term_attrs) + int *term_attrs) { int height, width; vterm_get_size(term->vt, &height, &width); @@ -569,8 +570,8 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, VTermScreenCell cell; fetch_cell(term, row, col, &cell); // Get the rgb value set by libvterm. - int vt_fg = RGB(cell.fg.red, cell.fg.green, cell.fg.blue); - int vt_bg = RGB(cell.bg.red, cell.bg.green, cell.bg.blue); + int vt_fg = RGB_(cell.fg.red, cell.fg.green, cell.fg.blue); + int vt_bg = RGB_(cell.bg.red, cell.bg.green, cell.bg.blue); vt_fg = vt_fg != default_vt_fg ? vt_fg : - 1; vt_bg = vt_bg != default_vt_bg ? vt_bg : - 1; // Since libvterm does not expose the color index used by the program, we @@ -589,7 +590,7 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, int attr_id = 0; if (hl_attrs || vt_fg != -1 || vt_bg != -1) { - attr_id = get_attr_entry(&(attrentry_T) { + attr_id = get_attr_entry(&(HlAttrs) { .cterm_ae_attr = (int16_t)hl_attrs, .cterm_fg_color = vt_fg_idx, .cterm_bg_color = vt_bg_idx, @@ -782,26 +783,60 @@ static int term_sb_pop(int cols, VTermScreenCell *cells, void *data) // }}} // input handling {{{ -static void convert_modifiers(VTermModifier *statep) +static void convert_modifiers(int key, VTermModifier *statep) { if (mod_mask & MOD_MASK_SHIFT) { *statep |= VTERM_MOD_SHIFT; } if (mod_mask & MOD_MASK_CTRL) { *statep |= VTERM_MOD_CTRL; } if (mod_mask & MOD_MASK_ALT) { *statep |= VTERM_MOD_ALT; } + + switch (key) { + case K_S_TAB: + case K_S_UP: + case K_S_DOWN: + case K_S_LEFT: + case K_S_RIGHT: + case K_S_F1: + case K_S_F2: + case K_S_F3: + case K_S_F4: + case K_S_F5: + case K_S_F6: + case K_S_F7: + case K_S_F8: + case K_S_F9: + case K_S_F10: + case K_S_F11: + case K_S_F12: + *statep |= VTERM_MOD_SHIFT; + break; + + case K_C_LEFT: + case K_C_RIGHT: + *statep |= VTERM_MOD_CTRL; + break; + } } static VTermKey convert_key(int key, VTermModifier *statep) { - convert_modifiers(statep); + convert_modifiers(key, statep); switch (key) { case K_BS: return VTERM_KEY_BACKSPACE; + case K_S_TAB: // FALLTHROUGH case TAB: return VTERM_KEY_TAB; case Ctrl_M: return VTERM_KEY_ENTER; case ESC: return VTERM_KEY_ESCAPE; + case K_S_UP: // FALLTHROUGH case K_UP: return VTERM_KEY_UP; + case K_S_DOWN: // FALLTHROUGH case K_DOWN: return VTERM_KEY_DOWN; + case K_S_LEFT: // FALLTHROUGH + case K_C_LEFT: // FALLTHROUGH case K_LEFT: return VTERM_KEY_LEFT; + case K_S_RIGHT: // FALLTHROUGH + case K_C_RIGHT: // FALLTHROUGH case K_RIGHT: return VTERM_KEY_RIGHT; case K_INS: return VTERM_KEY_INS; @@ -811,22 +846,22 @@ static VTermKey convert_key(int key, VTermModifier *statep) case K_PAGEUP: return VTERM_KEY_PAGEUP; case K_PAGEDOWN: return VTERM_KEY_PAGEDOWN; - case K_K0: + case K_K0: // FALLTHROUGH case K_KINS: return VTERM_KEY_KP_0; - case K_K1: + case K_K1: // FALLTHROUGH case K_KEND: return VTERM_KEY_KP_1; case K_K2: return VTERM_KEY_KP_2; - case K_K3: + case K_K3: // FALLTHROUGH case K_KPAGEDOWN: return VTERM_KEY_KP_3; case K_K4: return VTERM_KEY_KP_4; case K_K5: return VTERM_KEY_KP_5; case K_K6: return VTERM_KEY_KP_6; - case K_K7: + case K_K7: // FALLTHROUGH case K_KHOME: return VTERM_KEY_KP_7; case K_K8: return VTERM_KEY_KP_8; - case K_K9: + case K_K9: // FALLTHROUGH case K_KPAGEUP: return VTERM_KEY_KP_9; - case K_KDEL: + case K_KDEL: // FALLTHROUGH case K_KPOINT: return VTERM_KEY_KP_PERIOD; case K_KENTER: return VTERM_KEY_KP_ENTER; case K_KPLUS: return VTERM_KEY_KP_PLUS; @@ -834,6 +869,57 @@ static VTermKey convert_key(int key, VTermModifier *statep) case K_KMULTIPLY: return VTERM_KEY_KP_MULT; case K_KDIVIDE: return VTERM_KEY_KP_DIVIDE; + case K_S_F1: // FALLTHROUGH + case K_F1: return VTERM_KEY_FUNCTION(1); + case K_S_F2: // FALLTHROUGH + case K_F2: return VTERM_KEY_FUNCTION(2); + case K_S_F3: // FALLTHROUGH + case K_F3: return VTERM_KEY_FUNCTION(3); + case K_S_F4: // FALLTHROUGH + case K_F4: return VTERM_KEY_FUNCTION(4); + case K_S_F5: // FALLTHROUGH + case K_F5: return VTERM_KEY_FUNCTION(5); + case K_S_F6: // FALLTHROUGH + case K_F6: return VTERM_KEY_FUNCTION(6); + case K_S_F7: // FALLTHROUGH + case K_F7: return VTERM_KEY_FUNCTION(7); + case K_S_F8: // FALLTHROUGH + case K_F8: return VTERM_KEY_FUNCTION(8); + case K_S_F9: // FALLTHROUGH + case K_F9: return VTERM_KEY_FUNCTION(9); + case K_S_F10: // FALLTHROUGH + case K_F10: return VTERM_KEY_FUNCTION(10); + case K_S_F11: // FALLTHROUGH + case K_F11: return VTERM_KEY_FUNCTION(11); + case K_S_F12: // FALLTHROUGH + case K_F12: return VTERM_KEY_FUNCTION(12); + + case K_F13: return VTERM_KEY_FUNCTION(13); + case K_F14: return VTERM_KEY_FUNCTION(14); + case K_F15: return VTERM_KEY_FUNCTION(15); + case K_F16: return VTERM_KEY_FUNCTION(16); + case K_F17: return VTERM_KEY_FUNCTION(17); + case K_F18: return VTERM_KEY_FUNCTION(18); + case K_F19: return VTERM_KEY_FUNCTION(19); + case K_F20: return VTERM_KEY_FUNCTION(20); + case K_F21: return VTERM_KEY_FUNCTION(21); + case K_F22: return VTERM_KEY_FUNCTION(22); + case K_F23: return VTERM_KEY_FUNCTION(23); + case K_F24: return VTERM_KEY_FUNCTION(24); + case K_F25: return VTERM_KEY_FUNCTION(25); + case K_F26: return VTERM_KEY_FUNCTION(26); + case K_F27: return VTERM_KEY_FUNCTION(27); + case K_F28: return VTERM_KEY_FUNCTION(28); + case K_F29: return VTERM_KEY_FUNCTION(29); + case K_F30: return VTERM_KEY_FUNCTION(30); + case K_F31: return VTERM_KEY_FUNCTION(31); + case K_F32: return VTERM_KEY_FUNCTION(32); + case K_F33: return VTERM_KEY_FUNCTION(33); + case K_F34: return VTERM_KEY_FUNCTION(34); + case K_F35: return VTERM_KEY_FUNCTION(35); + case K_F36: return VTERM_KEY_FUNCTION(36); + case K_F37: return VTERM_KEY_FUNCTION(37); + default: return VTERM_KEY_NONE; } } @@ -1010,8 +1096,12 @@ static void refresh_terminal(Terminal *term) // Calls refresh_terminal() on all invalidated_terminals. static void refresh_timer_cb(TimeWatcher *watcher, void *data) { - if (exiting) { // Cannot redraw (requires event loop) during teardown/exit. - goto end; + refresh_pending = false; + if (exiting // Cannot redraw (requires event loop) during teardown/exit. + // WM_LIST (^D) is not redrawn, unlike the normal wildmenu. So we must + // skip redraws to keep it visible. + || wild_menu_showing == WM_LIST) { + return; } Terminal *term; void *stub; (void)(stub); @@ -1026,8 +1116,6 @@ static void refresh_timer_cb(TimeWatcher *watcher, void *data) if (any_visible) { redraw(true); } -end: - refresh_pending = false; } static void refresh_size(Terminal *term, buf_T *buf) @@ -1172,6 +1260,10 @@ static void redraw(bool restore_cursor) update_screen(0); } + if (need_maketitle) { // Update title in terminal-mode. #7248 + maketitle(); + } + if (restore_cursor) { ui_cursor_goto(save_row, save_col); } else if (term) { diff --git a/src/nvim/terminal.h b/src/nvim/terminal.h index 25e609fb68..f2b0e232c3 100644 --- a/src/nvim/terminal.h +++ b/src/nvim/terminal.h @@ -10,6 +10,8 @@ typedef void (*terminal_write_cb)(char *buffer, size_t size, void *data); typedef void (*terminal_resize_cb)(uint16_t width, uint16_t height, void *data); typedef void (*terminal_close_cb)(void *data); +#include "nvim/buffer_defs.h" + typedef struct { void *data; uint16_t width, height; diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index 7e55fffa06..a31e1843fc 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -2,68 +2,106 @@ # Makefile to run all tests for Vim # -NVIM_PRG ?= ../../../build/bin/nvim -SCRIPTSOURCE := ../../../runtime +ifeq ($(OS),Windows_NT) + NVIM_PRG ?= ../../../build/bin/nvim.exe +else + NVIM_PRG ?= ../../../build/bin/nvim +endif +ROOT := ../../.. export SHELL := sh export NVIM_PRG := $(NVIM_PRG) +export TMPDIR := $(abspath ../../../Xtest-tmpdir) -SCRIPTS ?= \ - test13.out \ +SCRIPTS_DEFAULT = \ test14.out \ - test17.out \ test24.out \ - test32.out \ test37.out \ test40.out \ test42.out \ test48.out \ test49.out \ test52.out \ - test53.out \ test64.out \ - test73.out \ - test79.out \ + +ifneq ($(OS),Windows_NT) + SCRIPTS_DEFAULTS := $(SCRIPTS_DEFAULT) \ + test17.out \ + +endif + +SCRIPTS ?= $(SCRIPTS_DEFAULT) # Tests using runtest.vim. # Keep test_alot*.res as the last one, sort the others. NEW_TESTS ?= \ + test_arabic.res \ test_autocmd.res \ test_bufwintabinfo.res \ + test_changedtick.res \ test_charsearch.res \ + test_cindent.res \ + test_close_count.res \ test_cmdline.res \ test_command_count.res \ test_cscope.res \ + test_curswant.res \ test_digraph.res \ + test_edit.res \ + test_erasebackword.res \ + test_exists.res \ test_diffmode.res \ test_farsi.res \ + test_file_size.res \ test_filter_map.res \ + test_find_complete.res \ + test_fixeol.res \ + test_findfile.res \ test_fnameescape.res \ test_fold.res \ + test_ga.res \ test_glob2regpat.res \ test_gf.res \ test_gn.res \ test_hardcopy.res \ test_help_tagjump.res \ + test_hide.res \ test_history.res \ test_hlsearch.res \ test_increment.res \ test_increment_dbcs.res \ + test_ins_complete.res \ test_lambda.res \ test_langmap.res \ + test_let.res \ + test_lineending.res \ + test_listdict.res \ + test_listchars.res \ + test_makeencoding.res \ test_marks.res \ test_match.res \ test_matchadd_conceal.res \ - test_matchadd_conceal_utf8.res \ + test_mksession.res \ test_nested_function.res \ test_normal.res \ + test_number.res \ + test_options.res \ + test_profile.res \ + test_put.res \ test_quickfix.res \ + test_recover.res \ + test_retab.res \ + test_scrollbind.res \ test_search.res \ test_signs.res \ test_smartindent.res \ + test_spell.res \ test_stat.res \ + test_startup.res \ test_substitute.res \ test_syntax.res \ + test_system.res \ + test_tab.res \ test_tabpage.res \ test_textobjects.res \ test_timers.res \ @@ -71,8 +109,11 @@ NEW_TESTS ?= \ test_usercommands.res \ test_vimscript.res \ test_visual.res \ + test_winbuf_close.res \ test_window_id.res \ test_writefile.res \ + test_alot_latin.res \ + test_alot_utf8.res \ test_alot.res SCRIPTS_GUI := test16.out @@ -93,7 +134,7 @@ ifdef USE_VALGRIND $(VALGRIND_TOOL) \ --suppressions=../../.valgrind.supp \ --error-exitcode=123 \ - --log-file=valgrind.\%p.$* \ + --log-file=valgrind-\%p.$* \ $(VGDB) \ --trace-children=yes else @@ -111,7 +152,8 @@ nongui: nolog $(SCRIPTS) newtests report gui: nolog $(SCRIPTS) $(SCRIPTS_GUI) newtests report .gdbinit: - echo 'set $$_exitcode = -1\nrun\nif $$_exitcode != -1\n quit\nend' > .gdbinit + @echo "[OLDTEST-PREP] Setting up .gdbinit" + @echo 'set $$_exitcode = -1\nrun\nif $$_exitcode != -1\n quit\nend' > .gdbinit report: @echo @@ -130,7 +172,7 @@ $(SCRIPTS) $(SCRIPTS_GUI): $(NVIM_PRG) test1.out RM_ON_RUN := test.out X* viminfo RM_ON_START := test.ok -RUN_VIM := VIMRUNTIME=$(SCRIPTSOURCE); export VIMRUNTIME; $(TOOL) $(NVIM_PRG) -u unix.vim -U NONE -i viminfo --noplugin -s dotest.in +RUN_VIM := $(TOOL) $(NVIM_PRG) -u unix.vim -U NONE -i viminfo --headless --noplugin -s dotest.in clean: -rm -rf *.out \ @@ -138,6 +180,7 @@ clean: *.res \ *.rej \ *.orig \ + *.tlog \ test.log \ messages \ $(RM_ON_RUN) \ @@ -146,60 +189,36 @@ clean: .*.swp \ .*.swo \ .gdbinit \ + $(TMPDIR) \ del test1.out: .gdbinit test1.in - -rm -rf $*.failed $(RM_ON_RUN) $(RM_ON_START) wrongtermsize - $(RUN_VIM) $*.in - @/bin/sh -c "if test -e wrongtermsize; then \ - echo; \ - echo test1 FAILED - terminal size must be 80x24 or larger; \ - echo; exit 1; \ - elif diff test.out $*.ok; then \ - mv -f test.out $*.out; \ - else \ - echo; \ - echo test1 FAILED - Something basic is wrong; \ - echo; \ - exit 1; \ - fi" - -rm -rf X* viminfo + @echo "[OLDTEST-PREP] Running test1" + @rm -rf $*.failed $(RM_ON_RUN) $(RM_ON_START) wrongtermsize + @mkdir -p $(TMPDIR) + @/bin/sh runnvim.sh $(ROOT) $(NVIM_PRG) $* $(RUN_VIM) $*.in + @rm -f wrongtermsize + @rm -rf X* viminfo %.out: %.in .gdbinit - -rm -rf $*.failed test.ok $(RM_ON_RUN) - cp $*.ok test.ok - # Sleep a moment to avoid that the xterm title is messed up. - # 200 msec is sufficient, but only modern sleep supports a fraction of - # a second, fall back to a second if it fails. - @-/bin/sh -c "sleep .2 > /dev/null 2>&1 || sleep 1" - $(RUN_VIM) $*.in - - # Check if the test.out file matches test.ok. - @/bin/sh -c "if test -f test.out; then \ - if diff -u test.out $*.ok; then \ - mv -f test.out $*.out; \ - else \ - echo $* FAILED >> test.log; \ - mv -f test.out $*.failed; \ - fi; \ - else \ - echo $* NO OUTPUT >>test.log; \ - fi" - @/bin/sh -c "if test -f valgrind; then \ - mv -f valgrind valgrind.$*; \ - fi" - -rm -rf X* test.ok viminfo + @echo "[OLDESTTEST] Running" $* + @rm -rf $*.failed test.ok $(RM_ON_RUN) + @mkdir -p $(TMPDIR) + @cp $*.ok test.ok + @/bin/sh runnvim.sh --oldesttest $(ROOT) $(NVIM_PRG) $* $(RUN_VIM) $*.in + @rm -rf X* test.ok viminfo test49.out: test49.vim nolog: - -rm -f test.log messages + @echo "[OLDTEST-PREP] Removing test.log and messages" + @rm -f test.log messages # New style of tests uses Vim script with assert calls. These are easier # to write and a lot easier to read and debug. # Limitation: Only works with the +eval feature. -RUN_VIMTEST = VIMRUNTIME=$(SCRIPTSOURCE); export VIMRUNTIME; $(VALGRIND) $(NVIM_PRG) -u unix.vim -U NONE --noplugin +RUN_VIMTEST = $(TOOL) $(NVIM_PRG) -u unix.vim -U NONE --headless --noplugin newtests: newtestssilent @/bin/sh -c "if test -f messages && grep -q 'FAILED' messages; then \ @@ -209,4 +228,6 @@ newtests: newtestssilent newtestssilent: $(NEW_TESTS) %.res: %.vim .gdbinit - $(RUN_VIMTEST) -u NONE -S runtest.vim $*.vim + @echo "[OLDTEST] Running" $* + @mkdir -p $(TMPDIR) + @/bin/sh runnvim.sh $(ROOT) $(NVIM_PRG) $* $(RUN_VIMTEST) -u NONE -S runtest.vim $*.vim diff --git a/src/nvim/testdir/runnvim.sh b/src/nvim/testdir/runnvim.sh new file mode 100755 index 0000000000..43556f3ad3 --- /dev/null +++ b/src/nvim/testdir/runnvim.sh @@ -0,0 +1,82 @@ +#!/bin/sh + +main() {( + local separator="================================================================================" + local oldesttest= + if test "$1" = "--oldesttest" ; then + shift + oldesttest=1 + fi + local root="$1" ; shift + local nvim_prg="$1" ; shift + local test_name="$1" ; shift + + local tlog="$test_name.tlog" + + export NVIM_TEST_ARGC=$# + local arg + local i=0 + for arg ; do + eval "export NVIM_TEST_ARG$i=\"\$arg\"" + i=$(( i+1 )) + done + + export CI_DIR="$root/ci" + export BUILD_DIR="$(dirname "$nvim_prg")/.." + export FAILED=0 + + . "$CI_DIR/common/suite.sh" + . "$CI_DIR/common/test.sh" + + export VIMRUNTIME="$root/runtime" + if ! "$nvim_prg" \ + -u NONE -i NONE \ + --headless \ + --cmd 'set shortmess+=I noswapfile noundofile nomore' \ + -S runnvim.vim \ + "$tlog" > "out-$tlog" 2> "err-$tlog" + then + fail "$test_name" F "Nvim exited with non-zero code" + fi + echo "Stdout of :terminal runner" >> "$tlog" + echo "$separator" >> "$tlog" + cat "out-$tlog" >> "$tlog" + echo "$separator" >> "$tlog" + echo "Stderr of :terminal runner" >> "$tlog" + echo "$separator" >> "$tlog" + cat "err-$tlog" >> "$tlog" + echo "$separator" >> "$tlog" + if test "$oldesttest" = 1 ; then + if ! diff -q test.out "$test_name.ok" > /dev/null 2>&1 ; then + if test -f test.out ; then + fail "$test_name" F "Oldest test .out file differs from .ok file" + echo "Diff between test.out and $test_name.ok" >> "$tlog" + echo "$separator" >> "$tlog" + diff -a test.out "$test_name.ok" >> "$tlog" + echo "$separator" >> "$tlog" + else + echo "No output in test.out" >> "$tlog" + fi + fi + fi + if test "$FAILED" = 1 ; then + travis_fold start "$NVIM_TEST_CURRENT_SUITE/$test_name" + fi + valgrind_check . + if test -n "$LOG_DIR" ; then + asan_check "$LOG_DIR" + fi + check_core_dumps + if test "$FAILED" = 1 ; then + cat "$tlog" + fi + rm -f "$tlog" + if test "$FAILED" = 1 ; then + travis_fold end "$NVIM_TEST_CURRENT_SUITE/$test_name" + fi + if test "$FAILED" = 1 ; then + echo "Test $test_name failed, see output above and summary for more details" >> test.log + fi +)} + +main "$@" diff --git a/src/nvim/testdir/runnvim.vim b/src/nvim/testdir/runnvim.vim new file mode 100644 index 0000000000..396a3a6477 --- /dev/null +++ b/src/nvim/testdir/runnvim.vim @@ -0,0 +1,55 @@ +let s:logger = {'d_events': []} +function s:logger.on_stdout(id, data, event) + call add(self.d_events, [a:event, a:data]) +endfunction +let s:logger.on_stderr = s:logger.on_stdout +function s:logger.on_exit(id, data, event) + call add(self.d_events, [a:event, ['']]) +endfunction + +function Main() + let argc = +$NVIM_TEST_ARGC + let args = [] + for i in range(argc) + call add(args, eval("$NVIM_TEST_ARG" . i)) + endfor + set lines=25 + set columns=80 + enew + let job = termopen(args, s:logger) + let results = jobwait([job], 5 * 60 * 1000) + " TODO(ZyX-I): Get colors + let screen = getline(1, '$') + bwipeout! + let stringified_events = map(s:logger.d_events, + \'v:val[0] . ": " . ' . + \'join(map(v:val[1], '. + \ '''substitute(v:val, '. + \ '"\\v\\C(\\p@!.|\\<)", '. + \ '"\\=printf(\"<%x>\", '. + \ 'char2nr(submatch(0)))", '. + \ '"")''), '. + \ '''\n'')') + call setline(1, [ + \ 'Job exited with code ' . results[0], + \ printf('Screen (%u lines)', len(screen)), + \ repeat('=', 80), + \] + screen + [ + \ repeat('=', 80), + \ printf('Events (%u lines):', len(stringified_events)), + \ repeat('=', 80), + \] + stringified_events + [ + \ repeat('=', 80), + \]) + write + if results[0] != 0 + if results[0] != -1 + call jobstop(job) + endif + cquit + else + qall + endif +endfunction + +call Main() diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim index 117ba52eb6..7090be7726 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -4,7 +4,7 @@ " " To execute only specific test functions, add a second argument. It will be " matched against the names of the Test_ function. E.g.: -" ../vim -u NONE -S runtest.vim test_channel.vim open_delay +" ../vim -u NONE -S runtest.vim test_channel.vim open_delay " The output can be found in the "messages" file. " " The test script may contain anything, only functions that start with @@ -42,10 +42,11 @@ endif " Common with all tests on all systems. source setup.vim +" For consistency run all tests with 'nocompatible' set. " This also enables use of line continuation. -set viminfo+=nviminfo +set nocp viminfo+=nviminfo -" Use utf-8 or latin1 be default, instead of whatever the system default +" Use utf-8 or latin1 by default, instead of whatever the system default " happens to be. Individual tests can overrule this at the top of the file. if has('multi_byte') set encoding=utf-8 @@ -62,42 +63,84 @@ lang mess C " Always use forward slashes. set shellslash -" Make sure $HOME does not get read or written. -let $HOME = '/does/not/exist' - -" Prepare for calling garbagecollect_for_testing(). +" Prepare for calling test_garbagecollect_now(). let v:testing = 1 -" Align Nvim defaults to Vim. -set directory^=. -set backspace= -set nohidden smarttab noautoindent noautoread complete-=i noruler noshowcmd -set listchars=eol:$ -" Prevent Nvim log from writing to stderr. -let $NVIM_LOG_FILE = exists($NVIM_LOG_FILE) ? $NVIM_LOG_FILE : 'Xnvim.log' +" Support function: get the alloc ID by name. +function GetAllocId(name) + exe 'split ' . s:srcdir . '/alloc.h' + let top = search('typedef enum') + if top == 0 + call add(v:errors, 'typedef not found in alloc.h') + endif + let lnum = search('aid_' . a:name . ',') + if lnum == 0 + call add(v:errors, 'Alloc ID ' . a:name . ' not defined') + endif + close + return lnum - top - 1 +endfunc func RunTheTest(test) echo 'Executing ' . a:test + + " Avoid stopping at the "hit enter" prompt + set nomore + + " Avoid a three second wait when a message is about to be overwritten by the + " mode message. + set noshowmode + + " Some tests wipe out buffers. To be consistent, always wipe out all + " buffers. + %bwipe! + + " The test may change the current directory. Save and restore the + " directory after executing the test. + let save_cwd = getcwd() + if exists("*SetUp") - call SetUp() + try + call SetUp() + catch + call add(v:errors, 'Caught exception in SetUp() before ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint) + endtry endif call add(s:messages, 'Executing ' . a:test) let s:done += 1 - try + + if a:test =~ 'Test_nocatch_' + " Function handles errors itself. This avoids skipping commands after the + " error. exe 'call ' . a:test - catch /^\cskipped/ - call add(s:messages, ' Skipped') - call add(s:skipped, 'SKIPPED ' . a:test . ': ' . substitute(v:exception, '^\S*\s\+', '', '')) - catch - call add(v:errors, 'Caught exception in ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint) - endtry + else + try + exe 'call ' . a:test + catch /^\cskipped/ + call add(s:messages, ' Skipped') + call add(s:skipped, 'SKIPPED ' . a:test . ': ' . substitute(v:exception, '^\S*\s\+', '', '')) + catch + call add(v:errors, 'Caught exception in ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint) + endtry + endif if exists("*TearDown") - call TearDown() + try + call TearDown() + catch + call add(v:errors, 'Caught exception in TearDown() after ' . a:test . ': ' . v:exception . ' @ ' . v:throwpoint) + endtry endif - " Close any extra windows and make the current one not modified. + " Clear any autocommands + au! + + " Close any extra tab pages and windows and make the current one not modified. + while tabpagenr('$') > 1 + quit! + endwhile + while 1 let wincount = winnr('$') if wincount == 1 @@ -110,7 +153,8 @@ func RunTheTest(test) break endif endwhile - set nomodified + + exe 'cd ' . save_cwd endfunc func AfterTheTest() @@ -189,13 +233,20 @@ endif " Names of flaky tests. let s:flaky = [ - \ 'Test_with_partial_callback()', + \ 'Test_exit_callback_interval()', \ 'Test_oneshot()', + \ 'Test_out_cb()', + \ 'Test_paused()', + \ 'Test_quoteplus()', + \ 'Test_reltime()', + \ 'Test_terminal_composing_unicode()', + \ 'Test_terminal_redir_file()', + \ 'Test_terminal_tmap()', + \ 'Test_with_partial_callback()', \ 'Test_lambda_with_timer()', \ ] " Locate Test_ functions and execute them. -set nomore redir @q silent function /^Test_ redir END @@ -208,12 +259,30 @@ endif " Execute the tests in alphabetical order. for s:test in sort(s:tests) + " Silence, please! + set belloff=all + call RunTheTest(s:test) if len(v:errors) > 0 && index(s:flaky, s:test) >= 0 + call add(s:messages, 'Found errors in ' . s:test . ':') + call extend(s:messages, v:errors) call add(s:messages, 'Flaky test failed, running it again') + let first_run = v:errors + + " Flakiness is often caused by the system being very busy. Sleep a couple + " of seconds to have a higher chance of succeeding the second time. + sleep 2 + let v:errors = [] call RunTheTest(s:test) + if len(v:errors) > 0 + let second_run = v:errors + let v:errors = ['First run:'] + call extend(v:errors, first_run) + call add(v:errors, 'Second run:') + call extend(v:errors, second_run) + endif endif call AfterTheTest() diff --git a/src/nvim/testdir/sautest/autoload/footest.vim b/src/nvim/testdir/sautest/autoload/footest.vim index f467bc376d..1e78963a10 100644 --- a/src/nvim/testdir/sautest/autoload/footest.vim +++ b/src/nvim/testdir/sautest/autoload/footest.vim @@ -1,4 +1,4 @@ -" Autoload script used by test55 and test60 +" Autoload script used by test_listdict.vim, test_exists.vim and test_let.vim let footest#x = 1 func footest#F() return 0 diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim index 06f2199214..7d6dd0c7ce 100644 --- a/src/nvim/testdir/setup.vim +++ b/src/nvim/testdir/setup.vim @@ -1,8 +1,15 @@ " Common preparations for running tests. -set noruler -set noshowcmd -set belloff= +" Align Nvim defaults to Vim. +set sidescroll=0 +set directory^=. +set backspace= +set nohidden smarttab noautoindent noautoread complete-=i noruler noshowcmd +set listchars=eol:$ +set fillchars=vert:\|,fold:- +" Prevent Nvim log from writing to stderr. +let $NVIM_LOG_FILE = exists($NVIM_LOG_FILE) ? $NVIM_LOG_FILE : 'Xnvim.log' + " Make sure 'runtimepath' and 'packpath' does not include $HOME. set rtp=$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after diff --git a/src/nvim/testdir/shared.vim b/src/nvim/testdir/shared.vim index 72cfea96c6..4925b04a82 100644 --- a/src/nvim/testdir/shared.vim +++ b/src/nvim/testdir/shared.vim @@ -1,5 +1,25 @@ " Functions shared by several tests. +" {Nvim} +" Filepath captured from output may be truncated, like this: +" /home/va...estdir/Xtest-tmpdir/nvimxbXN4i/10 +" Get last 2 segments, then combine with $TMPDIR. +func! Fix_truncated_tmpfile(fname) + " sanity check + if $TMPDIR ==# '' + throw '$TMPDIR is empty' + endif + let tmpdir_tail = fnamemodify(substitute($TMPDIR, '[\/]\+$', '', 'g'), ':t') + if tmpdir_tail ==# '' + throw 'empty tmpdir_tail' + endif + if a:fname !~# tmpdir_tail + throw printf('$TMPDIR (%s) not in fname: %s', tmpdir_tail, a:fname) + endif + let last2segments = matchstr(a:fname, '[\/][^\/]\+[\/][^\/]\+$') + return $TMPDIR.last2segments +endfunc + " Get the name of the Python executable. " Also keeps it in s:python. func PythonProg() diff --git a/src/nvim/testdir/test32.in b/src/nvim/testdir/test32.in deleted file mode 100644 index 76bd9be889..0000000000 --- a/src/nvim/testdir/test32.in +++ /dev/null @@ -1,60 +0,0 @@ -Test for insert expansion - -:se cpt=.,w -* add-expands (word from next line) from other window -* add-expands (current buffer first) -* Local expansion, ends in an empty line (unless it becomes a global expansion) -* starts Local and switches to global add-expansion -:se cpt=.,w,i -* i-add-expands and switches to local -* add-expands lines (it would end in an empty line if it didn't ignored it self) -:se cpt=kXtestfile -* checks k-expansion, and file expansion (use Xtest11 instead of test11, -* because TEST11.OUT may match first on DOS) -:se cpt=w -* checks make_cyclic in other window -:se cpt=u nohid -* checks unloaded buffer expansion -* checks adding mode abortion -:se cpt=t,d -* tag expansion, define add-expansion interrupted -* t-expansion - -STARTTEST -:se backspace="" -:se cpt=.,w ff=unix | $-2,$w!Xtestfile | set ff& -:se cot= -nO#include "Xtestfile" -ru -O - - -:se cpt=.,w,i -kOM - -:se cpt=kXtestfile -:w Xtest11.one -:w Xtest11.two -OIXA -:" use CTRL-X CTRL-F to complete Xtest11.one, remove it and then use -:" CTRL-X CTRL-F again to verify this doesn't cause trouble. -OXddk -:se cpt=w -OST -:se cpt=u nohid -oOEN -unl -:se cpt=t,d def=^\\k* tags=Xtestfile notagbsearch -O -a -:wq! test.out -ENDTEST - -start of testfile -run1 -run2 -end of testfile - -test11 36Gepeto /Tag/ -asd test11file 36G -Makefile to run diff --git a/src/nvim/testdir/test32.ok b/src/nvim/testdir/test32.ok deleted file mode 100644 index afc4463fac..0000000000 --- a/src/nvim/testdir/test32.ok +++ /dev/null @@ -1,15 +0,0 @@ -#include "Xtestfile" -run1 run3 -run3 run3 - -Makefile to run3 -Makefile to run3 -Makefile to run3 -Xtest11.two -STARTTEST -ENDTEST -unless -test11file 36Gepeto /Tag/ asd -asd -run1 run2 - diff --git a/src/nvim/testdir/test53.in b/src/nvim/testdir/test53.in deleted file mode 100644 index 20b5d019af..0000000000 --- a/src/nvim/testdir/test53.in +++ /dev/null @@ -1,74 +0,0 @@ -Tests for string and html text objects. vim: set ft=vim : - -Note that the end-of-line moves the cursor to the next test line. - -Also test match() and matchstr() - -STARTTEST -/^start:/ -da" -0va'a'rx -02f`da` -0fXdi" -03f'vi'ry -:set quoteescape=+*- -di` -$F"va"oha"i"rz -:" -/^<begin -jfXdit -0fXdit -fXdat -0fXdat -dit -:" -:put =matchstr(\"abcd\", \".\", 0, 2) " b -:put =matchstr(\"abcd\", \"..\", 0, 2) " bc -:put =matchstr(\"abcd\", \".\", 2, 0) " c (zero and negative -> first match) -:put =matchstr(\"abcd\", \".\", 0, -1) " a -:put =match(\"abcd\", \".\", 0, 5) " -1 -:put =match(\"abcd\", \".\", 0, -1) " 0 -:put =match('abc', '.', 0, 1) " 0 -:put =match('abc', '.', 0, 2) " 1 -:put =match('abc', '.', 0, 3) " 2 -:put =match('abc', '.', 0, 4) " -1 -:put =match('abc', '.', 1, 1) " 1 -:put =match('abc', '.', 2, 1) " 2 -:put =match('abc', '.', 3, 1) " -1 -:put =match('abc', '$', 0, 1) " 3 -:put =match('abc', '$', 0, 2) " -1 -:put =match('abc', '$', 1, 1) " 3 -:put =match('abc', '$', 2, 1) " 3 -:put =match('abc', '$', 3, 1) " 3 -:put =match('abc', '$', 4, 1) " -1 -:put =match('abc', '\zs', 0, 1) " 0 -:put =match('abc', '\zs', 0, 2) " 1 -:put =match('abc', '\zs', 0, 3) " 2 -:put =match('abc', '\zs', 0, 4) " 3 -:put =match('abc', '\zs', 0, 5) " -1 -:put =match('abc', '\zs', 1, 1) " 1 -:put =match('abc', '\zs', 2, 1) " 2 -:put =match('abc', '\zs', 3, 1) " 3 -:put =match('abc', '\zs', 4, 1) " -1 -:/^start:/,/^end:/wq! test.out -ENDTEST - -start: "wo\"rd\\" foo -'foo' 'bar' 'piep' -bla bla `quote` blah -out " in "noXno" -"'" 'blah' rep 'buh' -bla `s*`d-`+++`l**` b`la -voo "nah" sdf " asdf" sdf " sdf" sd - -<begin> --<b>asdf<i>Xasdf</i>asdf</b>- --<b>asdX<i>a<i />sdf</i>asdf</b>- --<b>asdf<i>Xasdf</i>asdf</b>- --<b>asdX<i>as<b />df</i>asdf</b>- --<b> -innertext object -</b> -</begin> -SEARCH: -end: diff --git a/src/nvim/testdir/test53.ok b/src/nvim/testdir/test53.ok deleted file mode 100644 index d57d86bbb0..0000000000 --- a/src/nvim/testdir/test53.ok +++ /dev/null @@ -1,45 +0,0 @@ -start: foo -xxxxxxxxxxxx'piep' -bla bla blah -out " in "" -"'" 'blah'yyyyy'buh' -bla `` b`la -voo "zzzzzzzzzzzzzzzzzzzzzzzzzzzzsd - -<begin> --<b>asdf<i></i>asdf</b>- --<b></b>- --<b>asdfasdf</b>- --- --<b></b> -</begin> -b -bc -c -a --1 -0 -0 -1 -2 --1 -1 -2 --1 -3 --1 -3 -3 -3 --1 -0 -1 -2 -3 --1 -1 -2 -3 --1 -SEARCH: -end: diff --git a/src/nvim/testdir/test73.in b/src/nvim/testdir/test73.in deleted file mode 100644 index 9d50f7a789..0000000000 --- a/src/nvim/testdir/test73.in +++ /dev/null @@ -1,168 +0,0 @@ -Tests for find completion. - -STARTTEST -:set wildmode=full -:" Do all test in a separate window to avoid E211 when we recursively -:" delete the Xfind directory during cleanup -:" -:" This will cause a few errors, do it silently. -:set visualbell -:" -:" On windows a stale "Xfind" directory may exist, remove it so that -:" we start from a clean state. -:call delete("Xfind", "rf") -:new -:let cwd=getcwd() -:let test_out = cwd . '/test.out' -:call mkdir('Xfind') -:cd Xfind -:set path= -:find -:exec "w! " . test_out -:close -:new -:set path=. -:find -:exec "w >>" . test_out -:close -:new -:set path=.,, -:find -:exec "w >>" . test_out -:close -:new -:set path=./** -:find -:exec "w >>" . test_out -:close -:new -:" We shouldn't find any file at this point, test.out must be empty. -:call mkdir('in') -:cd in -:call mkdir('path') -:exec "cd " . cwd -:e Xfind/file.txt -SHoly Grail:w -:e Xfind/in/file.txt -SJimmy Hoffa:w -:e Xfind/in/stuff.txt -SAnother Holy Grail:w -:e Xfind/in/path/file.txt -SE.T.:w -:set path=Xfind/** -:find file -:exec "w >>" . test_out -:find file -:exec "w >>" . test_out -:find file -:exec "w >>" . test_out -:" Rerun the previous three find completions, using fullpath in 'path' -:exec "set path=" . cwd . "/Xfind/**" -:find file -:exec "w >>" . test_out -:find file -:exec "w >>" . test_out -:find file -:exec "w >>" . test_out -:" Same steps again, using relative and fullpath items that point to the same -:" recursive location. -:" This is to test that there are no duplicates in the completion list. -:exec "set path+=Xfind/**" -:find file -:exec "w >>" . test_out -:find file -:exec "w >>" . test_out -:find file -:exec "w >>" . test_out -:find file -:" Test find completion for directory of current buffer, which at this point -:" is Xfind/in/file.txt. -:set path=. -:find st -:exec "w >>" . test_out -:" Test find completion for empty path item ",," which is the current directory -:cd Xfind -:set path=,, -:find f -:exec "w >>" . test_out -:" Test shortening of -:" -:" foo/x/bar/voyager.txt -:" foo/y/bar/voyager.txt -:" -:" When current directory is above foo/ they should be shortened to (in order -:" of appearance): -:" -:" x/bar/voyager.txt -:" y/bar/voyager.txt -:call mkdir('foo') -:cd foo -:call mkdir('x') -:call mkdir('y') -:cd x -:call mkdir('bar') -:cd .. -:cd y -:call mkdir('bar') -:cd .. -:cd .. -:" We should now be in the Xfind directory -:e foo/x/bar/voyager.txt -SVoyager 1:w -:e foo/y/bar/voyager.txt -SVoyager 2:w -:exec "set path=" . cwd . "/Xfind/**" -:find voyager -:exec "w >>" . test_out -:find voyager -:exec "w >>" . test_out -:" -:" When current directory is .../foo/y/bar they should be shortened to (in -:" order of appearance): -:" -:" ./voyager.txt -:" x/bar/voyager.txt -:cd foo -:cd y -:cd bar -:find voyager -:exec "w >> " . test_out -:find voyager -:exec "w >> " . test_out -:" Check the opposite too: -:cd .. -:cd .. -:cd x -:cd bar -:find voyager -:exec "w >> " . test_out -:find voyager -:exec "w >> " . test_out -:" Check for correct handling of shorten_fname()'s behavior on windows -:exec "cd " . cwd . "/Xfind/in" -:find file -:exec "w >>" . test_out -:" Test for relative to current buffer 'path' item -:exec "cd " . cwd . "/Xfind/" -:set path=./path -:" Open the file where Jimmy Hoffa is found -:e in/file.txt -:" Find the file containing 'E.T.' in the Xfind/in/path directory -:find file -:exec "w >>" . test_out -:" -:" Test that completion works when path=.,, -:" -:set path=.,, -:" Open Jimmy Hoffa file -:e in/file.txt -:exec "w >>" . test_out -:" Search for the file containing Holy Grail in same directory as in/path.txt -:find stu -:exec "w >>" . test_out -:q -:exec "cd " . cwd -:call delete("Xfind", "rf") -:qa! -ENDTEST - diff --git a/src/nvim/testdir/test73.ok b/src/nvim/testdir/test73.ok deleted file mode 100644 index 90efab756f..0000000000 --- a/src/nvim/testdir/test73.ok +++ /dev/null @@ -1,21 +0,0 @@ -Holy Grail -Jimmy Hoffa -E.T. -Holy Grail -Jimmy Hoffa -E.T. -Holy Grail -Jimmy Hoffa -E.T. -Another Holy Grail -Holy Grail -Voyager 1 -Voyager 2 -Voyager 2 -Voyager 1 -Voyager 1 -Voyager 2 -Jimmy Hoffa -E.T. -Jimmy Hoffa -Another Holy Grail diff --git a/src/nvim/testdir/test79.in b/src/nvim/testdir/test79.in Binary files differdeleted file mode 100644 index afbf2083d2..0000000000 --- a/src/nvim/testdir/test79.in +++ /dev/null diff --git a/src/nvim/testdir/test79.ok b/src/nvim/testdir/test79.ok Binary files differdeleted file mode 100644 index d4e0ae8819..0000000000 --- a/src/nvim/testdir/test79.ok +++ /dev/null diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index 1103778107..b4baf7a8d7 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -2,27 +2,33 @@ " This makes testing go faster, since Vim doesn't need to restart. source test_assign.vim +source test_changedtick.vim source test_cursor_func.vim -source test_execute_func.vim source test_ex_undo.vim +source test_execute_func.vim source test_expr.vim -source test_expr_utf8.vim source test_feedkeys.vim source test_filter_cmd.vim source test_filter_map.vim +source test_findfile.vim source test_float_func.vim source test_functions.vim +source test_ga.vim source test_goto.vim source test_jumps.vim +source test_fileformat.vim +source test_filetype.vim source test_lambda.vim source test_menu.vim source test_mapping.vim source test_messages.vim -source test_options.vim source test_partial.vim source test_popup.vim +source test_put.vim +source test_recover.vim source test_regexp_utf8.vim source test_source_utf8.vim +source test_sha256.vim source test_statusline.vim source test_syn_attr.vim source test_tabline.vim @@ -33,4 +39,5 @@ source test_taglist.vim source test_true_false.vim source test_unlet.vim source test_utf8.vim +source test_virtualedit.vim source test_window_cmd.vim diff --git a/src/nvim/testdir/test_alot_latin.vim b/src/nvim/testdir/test_alot_latin.vim new file mode 100644 index 0000000000..ebb3bde4ce --- /dev/null +++ b/src/nvim/testdir/test_alot_latin.vim @@ -0,0 +1,10 @@ +" A series of tests that can run in one Vim invocation. +" This makes testing go faster, since Vim doesn't need to restart. + +" These tests use latin1 'encoding'. Setting 'encoding' is in the individual +" files, so that they can be run by themselves. + +" Nvim does not allow setting 'encoding', so skip this test group. +finish + +source test_regexp_latin.vim diff --git a/src/nvim/testdir/test_alot_utf8.vim b/src/nvim/testdir/test_alot_utf8.vim new file mode 100644 index 0000000000..648d806a94 --- /dev/null +++ b/src/nvim/testdir/test_alot_utf8.vim @@ -0,0 +1,17 @@ +" A series of tests that can run in one Vim invocation. +" This makes testing go faster, since Vim doesn't need to restart. + +" These tests use utf8 'encoding'. Setting 'encoding' is already done in +" runtest.vim. Checking for the multi_byte feature is in the individual +" files, so that they can be run by themselves. + +source test_charsearch_utf8.vim +source test_expr_utf8.vim +source test_listlbr_utf8.vim +source test_matchadd_conceal_utf8.vim +source test_mksession_utf8.vim +source test_regexp_utf8.vim +source test_source_utf8.vim +source test_startup_utf8.vim +source test_utf8.vim +source test_utf8_comparisons.vim diff --git a/src/nvim/testdir/test_arabic.vim b/src/nvim/testdir/test_arabic.vim new file mode 100644 index 0000000000..9a16833a4c --- /dev/null +++ b/src/nvim/testdir/test_arabic.vim @@ -0,0 +1,472 @@ +" Simplistic testing of Arabic mode. + +if !has('arabic') || !has('multi_byte') + finish +endif + +source view_util.vim + +" Return list of Unicode characters at line lnum. +" Combining characters are treated as a single item. +func s:get_chars(lnum) + call cursor(a:lnum, 1) + let chars = [] + let numchars = strchars(getline('.'), 1) + for i in range(1, numchars) + exe 'norm ' i . '|' + let c=execute('ascii') + let c=substitute(c, '\n\?<.\{-}Hex\s*', 'U+', 'g') + let c=substitute(c, ',\s*Octal\s*\d*', '', 'g') + call add(chars, c) + endfor + return chars +endfunc + +func Test_arabic_toggle() + set arabic + call assert_equal(1, &rightleft) + call assert_equal(1, &arabicshape) + call assert_equal('arabic', &keymap) + call assert_equal(1, &delcombine) + + set iminsert=1 imsearch=1 + set arabic& + call assert_equal(0, &rightleft) + call assert_equal(1, &arabicshape) + call assert_equal('arabic', &keymap) + call assert_equal(1, &delcombine) + call assert_equal(0, &iminsert) + call assert_equal(-1, &imsearch) + + set arabicshape& keymap= delcombine& +endfunc + +func Test_arabic_input() + new + set arabic + " Typing sghl in Arabic insert mode should show the + " Arabic word 'Salaam' i.e. 'peace', spelled: + " SEEN, LAM, ALEF, MEEM. + " See: https://www.mediawiki.org/wiki/VisualEditor/Typing/Right-to-left + call feedkeys('isghl!', 'tx') + call assert_match("^ *!\uFEE1\uFEFC\uFEB3$", ScreenLines(1, &columns)[0]) + call assert_equal([ + \ 'U+0633', + \ 'U+0644 U+0627', + \ 'U+0645', + \ 'U+21'], s:get_chars(1)) + + " Without shaping, it should give individual Arabic letters. + set noarabicshape + call assert_match("^ *!\u0645\u0627\u0644\u0633$", ScreenLines(1, &columns)[0]) + call assert_equal([ + \ 'U+0633', + \ 'U+0644', + \ 'U+0627', + \ 'U+0645', + \ 'U+21'], s:get_chars(1)) + + set arabic& arabicshape& + bwipe! +endfunc + +func Test_arabic_toggle_keymap() + new + set arabic + call feedkeys("i12\<C-^>12\<C-^>12", 'tx') + call assert_match("^ *٢١21٢١$", ScreenLines(1, &columns)[0]) + call assert_equal('١٢12١٢', getline('.')) + set arabic& + bwipe! +endfunc + +func Test_delcombine() + new + set arabic + call feedkeys("isghl\<BS>\<BS>", 'tx') + call assert_match("^ *\uFEDE\uFEB3$", ScreenLines(1, &columns)[0]) + call assert_equal(['U+0633', 'U+0644'], s:get_chars(1)) + + " Now the same with 'nodelcombine' + set nodelcombine + %d + call feedkeys("isghl\<BS>\<BS>", 'tx') + call assert_match("^ *\uFEB1$", ScreenLines(1, &columns)[0]) + call assert_equal(['U+0633'], s:get_chars(1)) + set arabic& + bwipe! +endfunc + +" Values from src/arabic.h (not all used yet) +let s:a_COMMA = "\u060C" +let s:a_SEMICOLON = "\u061B" +let s:a_QUESTION = "\u061F" +let s:a_HAMZA = "\u0621" +let s:a_ALEF_MADDA = "\u0622" +let s:a_ALEF_HAMZA_ABOVE = "\u0623" +let s:a_WAW_HAMZA = "\u0624" +let s:a_ALEF_HAMZA_BELOW = "\u0625" +let s:a_YEH_HAMZA = "\u0626" +let s:a_ALEF = "\u0627" +let s:a_BEH = "\u0628" +let s:a_TEH_MARBUTA = "\u0629" +let s:a_TEH = "\u062a" +let s:a_THEH = "\u062b" +let s:a_JEEM = "\u062c" +let s:a_HAH = "\u062d" +let s:a_KHAH = "\u062e" +let s:a_DAL = "\u062f" +let s:a_THAL = "\u0630" +let s:a_REH = "\u0631" +let s:a_ZAIN = "\u0632" +let s:a_SEEN = "\u0633" +let s:a_SHEEN = "\u0634" +let s:a_SAD = "\u0635" +let s:a_DAD = "\u0636" +let s:a_TAH = "\u0637" +let s:a_ZAH = "\u0638" +let s:a_AIN = "\u0639" +let s:a_GHAIN = "\u063a" +let s:a_TATWEEL = "\u0640" +let s:a_FEH = "\u0641" +let s:a_QAF = "\u0642" +let s:a_KAF = "\u0643" +let s:a_LAM = "\u0644" +let s:a_MEEM = "\u0645" +let s:a_NOON = "\u0646" +let s:a_HEH = "\u0647" +let s:a_WAW = "\u0648" +let s:a_ALEF_MAKSURA = "\u0649" +let s:a_YEH = "\u064a" + +let s:a_FATHATAN = "\u064b" +let s:a_DAMMATAN = "\u064c" +let s:a_KASRATAN = "\u064d" +let s:a_FATHA = "\u064e" +let s:a_DAMMA = "\u064f" +let s:a_KASRA = "\u0650" +let s:a_SHADDA = "\u0651" +let s:a_SUKUN = "\u0652" + +let s:a_MADDA_ABOVE = "\u0653" +let s:a_HAMZA_ABOVE = "\u0654" +let s:a_HAMZA_BELOW = "\u0655" + +let s:a_ZERO = "\u0660" +let s:a_ONE = "\u0661" +let s:a_TWO = "\u0662" +let s:a_THREE = "\u0663" +let s:a_FOUR = "\u0664" +let s:a_FIVE = "\u0665" +let s:a_SIX = "\u0666" +let s:a_SEVEN = "\u0667" +let s:a_EIGHT = "\u0668" +let s:a_NINE = "\u0669" +let s:a_PERCENT = "\u066a" +let s:a_DECIMAL = "\u066b" +let s:a_THOUSANDS = "\u066c" +let s:a_STAR = "\u066d" +let s:a_MINI_ALEF = "\u0670" + +let s:a_s_FATHATAN = "\ufe70" +let s:a_m_TATWEEL_FATHATAN = "\ufe71" +let s:a_s_DAMMATAN = "\ufe72" + +let s:a_s_KASRATAN = "\ufe74" + +let s:a_s_FATHA = "\ufe76" +let s:a_m_FATHA = "\ufe77" +let s:a_s_DAMMA = "\ufe78" +let s:a_m_DAMMA = "\ufe79" +let s:a_s_KASRA = "\ufe7a" +let s:a_m_KASRA = "\ufe7b" +let s:a_s_SHADDA = "\ufe7c" +let s:a_m_SHADDA = "\ufe7d" +let s:a_s_SUKUN = "\ufe7e" +let s:a_m_SUKUN = "\ufe7f" + +let s:a_s_HAMZA = "\ufe80" +let s:a_s_ALEF_MADDA = "\ufe81" +let s:a_f_ALEF_MADDA = "\ufe82" +let s:a_s_ALEF_HAMZA_ABOVE = "\ufe83" +let s:a_f_ALEF_HAMZA_ABOVE = "\ufe84" +let s:a_s_WAW_HAMZA = "\ufe85" +let s:a_f_WAW_HAMZA = "\ufe86" +let s:a_s_ALEF_HAMZA_BELOW = "\ufe87" +let s:a_f_ALEF_HAMZA_BELOW = "\ufe88" +let s:a_s_YEH_HAMZA = "\ufe89" +let s:a_f_YEH_HAMZA = "\ufe8a" +let s:a_i_YEH_HAMZA = "\ufe8b" +let s:a_m_YEH_HAMZA = "\ufe8c" +let s:a_s_ALEF = "\ufe8d" +let s:a_f_ALEF = "\ufe8e" +let s:a_s_BEH = "\ufe8f" +let s:a_f_BEH = "\ufe90" +let s:a_i_BEH = "\ufe91" +let s:a_m_BEH = "\ufe92" +let s:a_s_TEH_MARBUTA = "\ufe93" +let s:a_f_TEH_MARBUTA = "\ufe94" +let s:a_s_TEH = "\ufe95" +let s:a_f_TEH = "\ufe96" +let s:a_i_TEH = "\ufe97" +let s:a_m_TEH = "\ufe98" +let s:a_s_THEH = "\ufe99" +let s:a_f_THEH = "\ufe9a" +let s:a_i_THEH = "\ufe9b" +let s:a_m_THEH = "\ufe9c" +let s:a_s_JEEM = "\ufe9d" +let s:a_f_JEEM = "\ufe9e" +let s:a_i_JEEM = "\ufe9f" +let s:a_m_JEEM = "\ufea0" +let s:a_s_HAH = "\ufea1" +let s:a_f_HAH = "\ufea2" +let s:a_i_HAH = "\ufea3" +let s:a_m_HAH = "\ufea4" +let s:a_s_KHAH = "\ufea5" +let s:a_f_KHAH = "\ufea6" +let s:a_i_KHAH = "\ufea7" +let s:a_m_KHAH = "\ufea8" +let s:a_s_DAL = "\ufea9" +let s:a_f_DAL = "\ufeaa" +let s:a_s_THAL = "\ufeab" +let s:a_f_THAL = "\ufeac" +let s:a_s_REH = "\ufead" +let s:a_f_REH = "\ufeae" +let s:a_s_ZAIN = "\ufeaf" +let s:a_f_ZAIN = "\ufeb0" +let s:a_s_SEEN = "\ufeb1" +let s:a_f_SEEN = "\ufeb2" +let s:a_i_SEEN = "\ufeb3" +let s:a_m_SEEN = "\ufeb4" +let s:a_s_SHEEN = "\ufeb5" +let s:a_f_SHEEN = "\ufeb6" +let s:a_i_SHEEN = "\ufeb7" +let s:a_m_SHEEN = "\ufeb8" +let s:a_s_SAD = "\ufeb9" +let s:a_f_SAD = "\ufeba" +let s:a_i_SAD = "\ufebb" +let s:a_m_SAD = "\ufebc" +let s:a_s_DAD = "\ufebd" +let s:a_f_DAD = "\ufebe" +let s:a_i_DAD = "\ufebf" +let s:a_m_DAD = "\ufec0" +let s:a_s_TAH = "\ufec1" +let s:a_f_TAH = "\ufec2" +let s:a_i_TAH = "\ufec3" +let s:a_m_TAH = "\ufec4" +let s:a_s_ZAH = "\ufec5" +let s:a_f_ZAH = "\ufec6" +let s:a_i_ZAH = "\ufec7" +let s:a_m_ZAH = "\ufec8" +let s:a_s_AIN = "\ufec9" +let s:a_f_AIN = "\ufeca" +let s:a_i_AIN = "\ufecb" +let s:a_m_AIN = "\ufecc" +let s:a_s_GHAIN = "\ufecd" +let s:a_f_GHAIN = "\ufece" +let s:a_i_GHAIN = "\ufecf" +let s:a_m_GHAIN = "\ufed0" +let s:a_s_FEH = "\ufed1" +let s:a_f_FEH = "\ufed2" +let s:a_i_FEH = "\ufed3" +let s:a_m_FEH = "\ufed4" +let s:a_s_QAF = "\ufed5" +let s:a_f_QAF = "\ufed6" +let s:a_i_QAF = "\ufed7" +let s:a_m_QAF = "\ufed8" +let s:a_s_KAF = "\ufed9" +let s:a_f_KAF = "\ufeda" +let s:a_i_KAF = "\ufedb" +let s:a_m_KAF = "\ufedc" +let s:a_s_LAM = "\ufedd" +let s:a_f_LAM = "\ufede" +let s:a_i_LAM = "\ufedf" +let s:a_m_LAM = "\ufee0" +let s:a_s_MEEM = "\ufee1" +let s:a_f_MEEM = "\ufee2" +let s:a_i_MEEM = "\ufee3" +let s:a_m_MEEM = "\ufee4" +let s:a_s_NOON = "\ufee5" +let s:a_f_NOON = "\ufee6" +let s:a_i_NOON = "\ufee7" +let s:a_m_NOON = "\ufee8" +let s:a_s_HEH = "\ufee9" +let s:a_f_HEH = "\ufeea" +let s:a_i_HEH = "\ufeeb" +let s:a_m_HEH = "\ufeec" +let s:a_s_WAW = "\ufeed" +let s:a_f_WAW = "\ufeee" +let s:a_s_ALEF_MAKSURA = "\ufeef" +let s:a_f_ALEF_MAKSURA = "\ufef0" +let s:a_s_YEH = "\ufef1" +let s:a_f_YEH = "\ufef2" +let s:a_i_YEH = "\ufef3" +let s:a_m_YEH = "\ufef4" +let s:a_s_LAM_ALEF_MADDA_ABOVE = "\ufef5" +let s:a_f_LAM_ALEF_MADDA_ABOVE = "\ufef6" +let s:a_s_LAM_ALEF_HAMZA_ABOVE = "\ufef7" +let s:a_f_LAM_ALEF_HAMZA_ABOVE = "\ufef8" +let s:a_s_LAM_ALEF_HAMZA_BELOW = "\ufef9" +let s:a_f_LAM_ALEF_HAMZA_BELOW = "\ufefa" +let s:a_s_LAM_ALEF = "\ufefb" +let s:a_f_LAM_ALEF = "\ufefc" + +let s:a_BYTE_ORDER_MARK = "\ufeff" + +func Test_shape_initial() + new + set arabicshape + + " Shaping arabic {testchar} non-arabic Tests chg_c_a2i(). + " pair[0] = testchar, pair[1] = next-result, pair[2] = current-result + for pair in [[s:a_YEH_HAMZA, s:a_f_GHAIN, s:a_i_YEH_HAMZA], + \ [s:a_HAMZA, s:a_s_GHAIN, s:a_s_HAMZA], + \ [s:a_ALEF_MADDA, s:a_s_GHAIN, s:a_s_ALEF_MADDA], + \ [s:a_ALEF_HAMZA_ABOVE, s:a_s_GHAIN, s:a_s_ALEF_HAMZA_ABOVE], + \ [s:a_WAW_HAMZA, s:a_s_GHAIN, s:a_s_WAW_HAMZA], + \ [s:a_ALEF_HAMZA_BELOW, s:a_s_GHAIN, s:a_s_ALEF_HAMZA_BELOW], + \ [s:a_ALEF, s:a_s_GHAIN, s:a_s_ALEF], + \ [s:a_TEH_MARBUTA, s:a_s_GHAIN, s:a_s_TEH_MARBUTA], + \ [s:a_DAL, s:a_s_GHAIN, s:a_s_DAL], + \ [s:a_THAL, s:a_s_GHAIN, s:a_s_THAL], + \ [s:a_REH, s:a_s_GHAIN, s:a_s_REH], + \ [s:a_ZAIN, s:a_s_GHAIN, s:a_s_ZAIN], + \ [s:a_TATWEEL, s:a_f_GHAIN, s:a_TATWEEL], + \ [s:a_WAW, s:a_s_GHAIN, s:a_s_WAW], + \ [s:a_ALEF_MAKSURA, s:a_s_GHAIN, s:a_s_ALEF_MAKSURA], + \ [s:a_BEH, s:a_f_GHAIN, s:a_i_BEH], + \ [s:a_TEH, s:a_f_GHAIN, s:a_i_TEH], + \ [s:a_THEH, s:a_f_GHAIN, s:a_i_THEH], + \ [s:a_JEEM, s:a_f_GHAIN, s:a_i_JEEM], + \ [s:a_HAH, s:a_f_GHAIN, s:a_i_HAH], + \ [s:a_KHAH, s:a_f_GHAIN, s:a_i_KHAH], + \ [s:a_SEEN, s:a_f_GHAIN, s:a_i_SEEN], + \ [s:a_SHEEN, s:a_f_GHAIN, s:a_i_SHEEN], + \ [s:a_SAD, s:a_f_GHAIN, s:a_i_SAD], + \ [s:a_DAD, s:a_f_GHAIN, s:a_i_DAD], + \ [s:a_TAH, s:a_f_GHAIN, s:a_i_TAH], + \ [s:a_ZAH, s:a_f_GHAIN, s:a_i_ZAH], + \ [s:a_AIN, s:a_f_GHAIN, s:a_i_AIN], + \ [s:a_GHAIN, s:a_f_GHAIN, s:a_i_GHAIN], + \ [s:a_FEH, s:a_f_GHAIN, s:a_i_FEH], + \ [s:a_QAF, s:a_f_GHAIN, s:a_i_QAF], + \ [s:a_KAF, s:a_f_GHAIN, s:a_i_KAF], + \ [s:a_LAM, s:a_f_GHAIN, s:a_i_LAM], + \ [s:a_MEEM, s:a_f_GHAIN, s:a_i_MEEM], + \ [s:a_NOON, s:a_f_GHAIN, s:a_i_NOON], + \ [s:a_HEH, s:a_f_GHAIN, s:a_i_HEH], + \ [s:a_YEH, s:a_f_GHAIN, s:a_i_YEH], + \ ] + call setline(1, s:a_GHAIN . pair[0] . ' ') + call assert_equal([pair[1] . pair[2] . ' '], ScreenLines(1, 3)) + endfor + + set arabicshape& + bwipe! +endfunc + +func Test_shape_isolated() + new + set arabicshape + + " Shaping non-arabic {testchar} non-arabic Tests chg_c_a2s(). + " pair[0] = testchar, pair[1] = current-result + for pair in [[s:a_HAMZA, s:a_s_HAMZA], + \ [s:a_ALEF_MADDA, s:a_s_ALEF_MADDA], + \ [s:a_ALEF_HAMZA_ABOVE, s:a_s_ALEF_HAMZA_ABOVE], + \ [s:a_WAW_HAMZA, s:a_s_WAW_HAMZA], + \ [s:a_ALEF_HAMZA_BELOW, s:a_s_ALEF_HAMZA_BELOW], + \ [s:a_YEH_HAMZA, s:a_s_YEH_HAMZA], + \ [s:a_ALEF, s:a_s_ALEF], + \ [s:a_TEH_MARBUTA, s:a_s_TEH_MARBUTA], + \ [s:a_DAL, s:a_s_DAL], + \ [s:a_THAL, s:a_s_THAL], + \ [s:a_REH, s:a_s_REH], + \ [s:a_ZAIN, s:a_s_ZAIN], + \ [s:a_TATWEEL, s:a_TATWEEL], + \ [s:a_WAW, s:a_s_WAW], + \ [s:a_ALEF_MAKSURA, s:a_s_ALEF_MAKSURA], + \ [s:a_BEH, s:a_s_BEH], + \ [s:a_TEH, s:a_s_TEH], + \ [s:a_THEH, s:a_s_THEH], + \ [s:a_JEEM, s:a_s_JEEM], + \ [s:a_HAH, s:a_s_HAH], + \ [s:a_KHAH, s:a_s_KHAH], + \ [s:a_SEEN, s:a_s_SEEN], + \ [s:a_SHEEN, s:a_s_SHEEN], + \ [s:a_SAD, s:a_s_SAD], + \ [s:a_DAD, s:a_s_DAD], + \ [s:a_TAH, s:a_s_TAH], + \ [s:a_ZAH, s:a_s_ZAH], + \ [s:a_AIN, s:a_s_AIN], + \ [s:a_GHAIN, s:a_s_GHAIN], + \ [s:a_FEH, s:a_s_FEH], + \ [s:a_QAF, s:a_s_QAF], + \ [s:a_KAF, s:a_s_KAF], + \ [s:a_LAM, s:a_s_LAM], + \ [s:a_MEEM, s:a_s_MEEM], + \ [s:a_NOON, s:a_s_NOON], + \ [s:a_HEH, s:a_s_HEH], + \ [s:a_YEH, s:a_s_YEH], + \ ] + call setline(1, ' ' . pair[0] . ' ') + call assert_equal([' ' . pair[1] . ' '], ScreenLines(1, 3)) + endfor + + set arabicshape& + bwipe! +endfunc + +func Test_shape_medial() + new + set arabicshape + + " Shaping arabic {testchar} arabic Tests chg_c_a2m(). + " pair[0] = testchar, pair[1] = next-result, pair[2] = current-result, + " pair[3] = previous-result + for pair in [[s:a_HAMZA, s:a_s_GHAIN, s:a_s_HAMZA, s:a_s_BEH], + \[s:a_ALEF_MADDA, s:a_s_GHAIN, s:a_f_ALEF_MADDA, s:a_i_BEH], + \[s:a_ALEF_HAMZA_ABOVE, s:a_s_GHAIN, s:a_f_ALEF_HAMZA_ABOVE, s:a_i_BEH], + \[s:a_WAW_HAMZA, s:a_s_GHAIN, s:a_f_WAW_HAMZA, s:a_i_BEH], + \[s:a_ALEF_HAMZA_BELOW, s:a_s_GHAIN, s:a_f_ALEF_HAMZA_BELOW, s:a_i_BEH], + \[s:a_YEH_HAMZA, s:a_f_GHAIN, s:a_m_YEH_HAMZA, s:a_i_BEH], + \[s:a_ALEF, s:a_s_GHAIN, s:a_f_ALEF, s:a_i_BEH], + \[s:a_BEH, s:a_f_GHAIN, s:a_m_BEH, s:a_i_BEH], + \[s:a_TEH_MARBUTA, s:a_s_GHAIN, s:a_f_TEH_MARBUTA, s:a_i_BEH], + \[s:a_TEH, s:a_f_GHAIN, s:a_m_TEH, s:a_i_BEH], + \[s:a_THEH, s:a_f_GHAIN, s:a_m_THEH, s:a_i_BEH], + \[s:a_JEEM, s:a_f_GHAIN, s:a_m_JEEM, s:a_i_BEH], + \[s:a_HAH, s:a_f_GHAIN, s:a_m_HAH, s:a_i_BEH], + \[s:a_KHAH, s:a_f_GHAIN, s:a_m_KHAH, s:a_i_BEH], + \[s:a_DAL, s:a_s_GHAIN, s:a_f_DAL, s:a_i_BEH], + \[s:a_THAL, s:a_s_GHAIN, s:a_f_THAL, s:a_i_BEH], + \[s:a_REH, s:a_s_GHAIN, s:a_f_REH, s:a_i_BEH], + \[s:a_ZAIN, s:a_s_GHAIN, s:a_f_ZAIN, s:a_i_BEH], + \[s:a_SEEN, s:a_f_GHAIN, s:a_m_SEEN, s:a_i_BEH], + \[s:a_SHEEN, s:a_f_GHAIN, s:a_m_SHEEN, s:a_i_BEH], + \[s:a_SAD, s:a_f_GHAIN, s:a_m_SAD, s:a_i_BEH], + \[s:a_DAD, s:a_f_GHAIN, s:a_m_DAD, s:a_i_BEH], + \[s:a_TAH, s:a_f_GHAIN, s:a_m_TAH, s:a_i_BEH], + \[s:a_ZAH, s:a_f_GHAIN, s:a_m_ZAH, s:a_i_BEH], + \[s:a_AIN, s:a_f_GHAIN, s:a_m_AIN, s:a_i_BEH], + \[s:a_GHAIN, s:a_f_GHAIN, s:a_m_GHAIN, s:a_i_BEH], + \[s:a_TATWEEL, s:a_f_GHAIN, s:a_TATWEEL, s:a_i_BEH], + \[s:a_FEH, s:a_f_GHAIN, s:a_m_FEH, s:a_i_BEH], + \[s:a_QAF, s:a_f_GHAIN, s:a_m_QAF, s:a_i_BEH], + \[s:a_KAF, s:a_f_GHAIN, s:a_m_KAF, s:a_i_BEH], + \[s:a_LAM, s:a_f_GHAIN, s:a_m_LAM, s:a_i_BEH], + \[s:a_MEEM, s:a_f_GHAIN, s:a_m_MEEM, s:a_i_BEH], + \[s:a_NOON, s:a_f_GHAIN, s:a_m_NOON, s:a_i_BEH], + \[s:a_HEH, s:a_f_GHAIN, s:a_m_HEH, s:a_i_BEH], + \[s:a_WAW, s:a_s_GHAIN, s:a_f_WAW, s:a_i_BEH], + \[s:a_ALEF_MAKSURA, s:a_s_GHAIN, s:a_f_ALEF_MAKSURA, s:a_i_BEH], + \[s:a_YEH, s:a_f_GHAIN, s:a_m_YEH, s:a_i_BEH], + \ ] + call setline(1, s:a_GHAIN . pair[0] . s:a_BEH) + call assert_equal([pair[1] . pair[2] . pair[3]], ScreenLines(1, 3)) + endfor + + set arabicshape& + bwipe! +endfunc + diff --git a/src/nvim/testdir/test_arglist.vim b/src/nvim/testdir/test_arglist.vim new file mode 100644 index 0000000000..19d0cee47a --- /dev/null +++ b/src/nvim/testdir/test_arglist.vim @@ -0,0 +1,355 @@ +" Test argument list commands + +func Test_argidx() + args a b c + last + call assert_equal(2, argidx()) + %argdelete + call assert_equal(0, argidx()) + " doing it again doesn't result in an error + %argdelete + call assert_equal(0, argidx()) + call assert_fails('2argdelete', 'E16:') + + args a b c + call assert_equal(0, argidx()) + next + call assert_equal(1, argidx()) + next + call assert_equal(2, argidx()) + 1argdelete + call assert_equal(1, argidx()) + 1argdelete + call assert_equal(0, argidx()) + 1argdelete + call assert_equal(0, argidx()) +endfunc + +func Test_argadd() + %argdelete + argadd a b c + call assert_equal(0, argidx()) + + %argdelete + argadd a + call assert_equal(0, argidx()) + argadd b c d + call assert_equal(0, argidx()) + + call Init_abc() + argadd x + call Assert_argc(['a', 'b', 'x', 'c']) + call assert_equal(1, argidx()) + + call Init_abc() + 0argadd x + call Assert_argc(['x', 'a', 'b', 'c']) + call assert_equal(2, argidx()) + + call Init_abc() + 1argadd x + call Assert_argc(['a', 'x', 'b', 'c']) + call assert_equal(2, argidx()) + + call Init_abc() + $argadd x + call Assert_argc(['a', 'b', 'c', 'x']) + call assert_equal(1, argidx()) + + call Init_abc() + $argadd x + +2argadd y + call Assert_argc(['a', 'b', 'c', 'x', 'y']) + call assert_equal(1, argidx()) + + %argd + edit d + arga + call assert_equal(1, len(argv())) + call assert_equal('d', get(argv(), 0, '')) + + %argd + edit some\ file + arga + call assert_equal(1, len(argv())) + call assert_equal('some file', get(argv(), 0, '')) + + %argd + new + arga + call assert_equal(0, len(argv())) +endfunc + +func Init_abc() + args a b c + next +endfunc + +func Assert_argc(l) + call assert_equal(len(a:l), argc()) + let i = 0 + while i < len(a:l) && i < argc() + call assert_equal(a:l[i], argv(i)) + let i += 1 + endwhile +endfunc + +" Test for [count]argument and [count]argdelete commands +" Ported from the test_argument_count.in test script +func Test_argument() + " Clean the argument list + arga a | %argd + + let save_hidden = &hidden + set hidden + + let g:buffers = [] + augroup TEST + au BufEnter * call add(buffers, expand('%:t')) + augroup END + + argadd a b c d + $argu + $-argu + -argu + 1argu + +2argu + + augroup TEST + au! + augroup END + + call assert_equal(['d', 'c', 'b', 'a', 'c'], g:buffers) + + redir => result + ar + redir END + call assert_true(result =~# 'a b \[c] d') + + .argd + call assert_equal(['a', 'b', 'd'], argv()) + + -argd + call assert_equal(['a', 'd'], argv()) + + $argd + call assert_equal(['a'], argv()) + + 1arga c + 1arga b + $argu + $arga x + call assert_equal(['a', 'b', 'c', 'x'], argv()) + + 0arga y + call assert_equal(['y', 'a', 'b', 'c', 'x'], argv()) + + %argd + call assert_equal([], argv()) + + arga a b c d e f + 2,$-argd + call assert_equal(['a', 'f'], argv()) + + let &hidden = save_hidden + + " Setting argument list should fail when the current buffer has unsaved + " changes + %argd + enew! + set modified + call assert_fails('args x y z', 'E37:') + args! x y z + call assert_equal(['x', 'y', 'z'], argv()) + call assert_equal('x', expand('%:t')) + + last | enew | argu + call assert_equal('z', expand('%:t')) + + %argdelete + call assert_fails('argument', 'E163:') +endfunc + +" Test for 0argadd and 0argedit +" Ported from the test_argument_0count.in test script +func Test_zero_argadd() + " Clean the argument list + arga a | %argd + + arga a b c d + 2argu + 0arga added + call assert_equal(['added', 'a', 'b', 'c', 'd'], argv()) + + 2argu + arga third + call assert_equal(['added', 'a', 'third', 'b', 'c', 'd'], argv()) + + %argd + arga a b c d + 2argu + 0arge edited + call assert_equal(['edited', 'a', 'b', 'c', 'd'], argv()) + + 2argu + arga third + call assert_equal(['edited', 'a', 'third', 'b', 'c', 'd'], argv()) + + 2argu + argedit file\ with\ spaces another file + call assert_equal(['edited', 'a', 'file with spaces', 'another', 'file', 'third', 'b', 'c', 'd'], argv()) + call assert_equal('file with spaces', expand('%')) +endfunc + +func Reset_arglist() + args a | %argd +endfunc + +" Test for argc() +func Test_argc() + call Reset_arglist() + call assert_equal(0, argc()) + argadd a b + call assert_equal(2, argc()) +endfunc + +" Test for arglistid() +func Test_arglistid() + call Reset_arglist() + arga a b + call assert_equal(0, arglistid()) + split + arglocal + call assert_equal(1, arglistid()) + tabnew | tabfirst + call assert_equal(0, arglistid(2)) + call assert_equal(1, arglistid(1, 1)) + call assert_equal(0, arglistid(2, 1)) + call assert_equal(1, arglistid(1, 2)) + tabonly | only | enew! + argglobal + call assert_equal(0, arglistid()) +endfunc + +" Test for argv() +func Test_argv() + call Reset_arglist() + call assert_equal([], argv()) + call assert_equal("", argv(2)) + argadd a b c d + call assert_equal('c', argv(2)) +endfunc + +" Test for the :argedit command +func Test_argedit() + call Reset_arglist() + argedit a + call assert_equal(['a'], argv()) + call assert_equal('a', expand('%:t')) + argedit b + call assert_equal(['a', 'b'], argv()) + call assert_equal('b', expand('%:t')) + argedit a + call assert_equal(['a', 'b', 'a'], argv()) + call assert_equal('a', expand('%:t')) + " When file name case is ignored, an existing buffer with only case + " difference is re-used. + argedit C D + call assert_equal('C', expand('%:t')) + call assert_equal(['a', 'b', 'a', 'C', 'D'], argv()) + argedit c + if has('fname_case') + call assert_equal(['a', 'b', 'a', 'C', 'c', 'D'], argv()) + else + call assert_equal(['a', 'b', 'a', 'C', 'C', 'D'], argv()) + endif + 0argedit x + if has('fname_case') + call assert_equal(['x', 'a', 'b', 'a', 'C', 'c', 'D'], argv()) + else + call assert_equal(['x', 'a', 'b', 'a', 'C', 'C', 'D'], argv()) + endif + enew! | set modified + call assert_fails('argedit y', 'E37:') + argedit! y + if has('fname_case') + call assert_equal(['x', 'y', 'y', 'a', 'b', 'a', 'C', 'c', 'D'], argv()) + else + call assert_equal(['x', 'y', 'y', 'a', 'b', 'a', 'C', 'C', 'D'], argv()) + endif + %argd + bwipe! C + bwipe! D +endfunc + +" Test for the :argdelete command +func Test_argdelete() + call Reset_arglist() + args aa a aaa b bb + argdelete a* + call assert_equal(['b', 'bb'], argv()) + call assert_equal('aa', expand('%:t')) + last + argdelete % + call assert_equal(['b'], argv()) + call assert_fails('argdelete', 'E471:') + call assert_fails('1,100argdelete', 'E16:') + %argd +endfunc + +" Tests for the :next, :prev, :first, :last, :rewind commands +func Test_argpos() + call Reset_arglist() + args a b c d + last + call assert_equal(3, argidx()) + call assert_fails('next', 'E165:') + prev + call assert_equal(2, argidx()) + Next + call assert_equal(1, argidx()) + first + call assert_equal(0, argidx()) + call assert_fails('prev', 'E164:') + 3next + call assert_equal(3, argidx()) + rewind + call assert_equal(0, argidx()) + %argd +endfunc + +" Test for autocommand that redefines the argument list, when doing ":all". +func Test_arglist_autocmd() + autocmd BufReadPost Xxx2 next Xxx2 Xxx1 + call writefile(['test file Xxx1'], 'Xxx1') + call writefile(['test file Xxx2'], 'Xxx2') + call writefile(['test file Xxx3'], 'Xxx3') + + new + " redefine arglist; go to Xxx1 + next! Xxx1 Xxx2 Xxx3 + " open window for all args + all + call assert_equal('test file Xxx1', getline(1)) + wincmd w + wincmd w + call assert_equal('test file Xxx1', getline(1)) + " should now be in Xxx2 + rewind + call assert_equal('test file Xxx2', getline(1)) + + autocmd! BufReadPost Xxx2 + enew! | only + call delete('Xxx1') + call delete('Xxx2') + call delete('Xxx3') + argdelete Xxx* + bwipe! Xxx1 Xxx2 Xxx3 +endfunc + +func Test_arg_all_expand() + call writefile(['test file Xxx1'], 'Xx x') + next notexist Xx\ x runtest.vim + call assert_equal('notexist Xx\ x runtest.vim', expand('##')) + call delete('Xx x') +endfunc diff --git a/src/nvim/testdir/test_autochdir.vim b/src/nvim/testdir/test_autochdir.vim new file mode 100644 index 0000000000..05d69631c4 --- /dev/null +++ b/src/nvim/testdir/test_autochdir.vim @@ -0,0 +1,19 @@ +" Test 'autochdir' behavior + +if !exists("+autochdir") + finish +endif + +func Test_set_filename() + let cwd = getcwd() + call test_autochdir() + set acd + new + w samples/Xtest + call assert_equal("Xtest", expand('%')) + call assert_equal("samples", substitute(getcwd(), '.*/\(\k*\)', '\1', '')) + bwipe! + set noacd + exe 'cd ' . cwd + call delete('samples/Xtest') +endfunc diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index c4110eba94..24651b75e1 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -1,14 +1,13 @@ " Tests for autocommands -set belloff=all -function! s:cleanup_buffers() abort +func! s:cleanup_buffers() abort for bnr in range(1, bufnr('$')) if bufloaded(bnr) && bufnr('%') != bnr execute 'bd! ' . bnr endif endfor -endfunction +endfunc func Test_vim_did_enter() call assert_false(v:vim_did_enter) @@ -49,7 +48,7 @@ if has('timers') endfunc endif -function Test_bufunload() +func Test_bufunload() augroup test_bufunload_group autocmd! autocmd BufUnload * call add(s:li, "bufunload") @@ -80,7 +79,7 @@ function Test_bufunload() endfunc " SEGV occurs in older versions. (At least 7.4.2005 or older) -function Test_autocmd_bufunload_with_tabnext() +func Test_autocmd_bufunload_with_tabnext() tabedit tabfirst @@ -98,7 +97,7 @@ function Test_autocmd_bufunload_with_tabnext() quit endfunc -function Test_autocmd_bufwinleave_with_tabfirst() +func Test_autocmd_bufwinleave_with_tabfirst() tabedit augroup sample autocmd! @@ -110,7 +109,7 @@ function Test_autocmd_bufwinleave_with_tabfirst() endfunc " SEGV occurs in older versions. (At least 7.4.2321 or older) -function Test_autocmd_bufunload_avoiding_SEGV_01() +func Test_autocmd_bufunload_avoiding_SEGV_01() split aa.txt let lastbuf = bufnr('$') @@ -128,7 +127,7 @@ function Test_autocmd_bufunload_avoiding_SEGV_01() endfunc " SEGV occurs in older versions. (At least 7.4.2321 or older) -function Test_autocmd_bufunload_avoiding_SEGV_02() +func Test_autocmd_bufunload_avoiding_SEGV_02() setlocal buftype=nowrite let lastbuf = bufnr('$') @@ -342,7 +341,10 @@ func Test_BufEnter() call mkdir('Xdir') split Xdir call assert_equal('+++', g:val) - bwipe! + + " On MS-Windows we can't edit the directory, make sure we wipe the right + " buffer. + bwipe! Xdir call delete('Xdir', 'd') au! BufEnter @@ -350,41 +352,40 @@ endfunc " Closing a window might cause an endless loop " E814 for older Vims -function Test_autocmd_bufwipe_in_SessLoadPost() - if has('win32') - throw 'Skipped: test hangs on MS-Windows' - endif +func Test_autocmd_bufwipe_in_SessLoadPost() + edit Xtest tabnew + file Xsomething set noswapfile - let g:bufnr=bufnr('%') mksession! - let content=['set nocp noswapfile', + let content = ['set nocp noswapfile', \ 'let v:swapchoice="e"', \ 'augroup test_autocmd_sessionload', \ 'autocmd!', - \ 'autocmd SessionLoadPost * 4bw!|qall!', + \ 'autocmd SessionLoadPost * exe bufnr("Xsomething") . "bw!"', \ 'augroup END', + \ '', + \ 'func WriteErrors()', + \ ' call writefile([execute("messages")], "Xerrors")', + \ 'endfunc', + \ 'au VimLeave * call WriteErrors()', \ ] call writefile(content, 'Xvimrc') - let a=system(v:progpath. ' --headless -i NONE -u Xvimrc --noplugins -S Session.vim') - call assert_match('E814', a) + call system(v:progpath. ' --headless -i NONE -u Xvimrc --noplugins -S Session.vim -c cq') + let errors = join(readfile('Xerrors')) + call assert_match('E814', errors) - unlet! g:bufnr set swapfile - for file in ['Session.vim', 'Xvimrc'] + for file in ['Session.vim', 'Xvimrc', 'Xerrors'] call delete(file) endfor endfunc " SEGV occurs in older versions. -function Test_autocmd_bufwipe_in_SessLoadPost2() - if has('win32') - throw 'Skipped: test hangs on MS-Windows' - endif +func Test_autocmd_bufwipe_in_SessLoadPost2() tabnew set noswapfile - let g:bufnr=bufnr('%') mksession! let content = ['set nocp noswapfile', @@ -399,22 +400,768 @@ function Test_autocmd_bufwipe_in_SessLoadPost2() \ ' exec ''bwipeout '' . b', \ ' endif', \ ' endfor', - \ 'redraw!', - \ 'echon "SessionLoadPost DONE"', - \ 'qall!', + \ ' echomsg "SessionLoadPost DONE"', \ 'endfunction', - \ 'au SessionLoadPost * call DeleteInactiveBufs()'] + \ 'au SessionLoadPost * call DeleteInactiveBufs()', + \ '', + \ 'func WriteErrors()', + \ ' call writefile([execute("messages")], "Xerrors")', + \ 'endfunc', + \ 'au VimLeave * call WriteErrors()', + \ ] call writefile(content, 'Xvimrc') - let a=system(v:progpath. ' --headless -i NONE -u Xvimrc --noplugins -S Session.vim') - " this probably only matches on unix - if has("unix") - call assert_notmatch('Caught deadly signal SEGV', a) - endif - call assert_match('SessionLoadPost DONE', a) + call system(v:progpath. ' --headless -i NONE -u Xvimrc --noplugins -S Session.vim -c cq') + let errors = join(readfile('Xerrors')) + " This probably only ever matches on unix. + call assert_notmatch('Caught deadly signal SEGV', errors) + call assert_match('SessionLoadPost DONE', errors) - unlet! g:bufnr set swapfile - for file in ['Session.vim', 'Xvimrc'] + for file in ['Session.vim', 'Xvimrc', 'Xerrors'] call delete(file) endfor endfunc + +func Test_empty_doau() + doau \| +endfunc + +func s:AutoCommandOptionSet(match) + let item = remove(g:options, 0) + let expected = printf("Option: <%s>, Oldval: <%s>, NewVal: <%s>, Scope: <%s>\n", item[0], item[1], item[2], item[3]) + let actual = printf("Option: <%s>, Oldval: <%s>, NewVal: <%s>, Scope: <%s>\n", a:match, v:option_old, v:option_new, v:option_type) + let g:opt = [expected, actual] + "call assert_equal(expected, actual) +endfunc + +func Test_OptionSet() + throw 'skipped: Nvim does not support test_override()' + if !has("eval") || !has("autocmd") || !exists("+autochdir") + return + endif + + call test_override('starting', 1) + set nocp + au OptionSet * :call s:AutoCommandOptionSet(expand("<amatch>")) + + " 1: Setting number option" + let g:options=[['number', 0, 1, 'global']] + set nu + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 2: Setting local number option" + let g:options=[['number', 1, 0, 'local']] + setlocal nonu + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 3: Setting global number option" + let g:options=[['number', 1, 0, 'global']] + setglobal nonu + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 4: Setting local autoindent option" + let g:options=[['autoindent', 0, 1, 'local']] + setlocal ai + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 5: Setting global autoindent option" + let g:options=[['autoindent', 0, 1, 'global']] + setglobal ai + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 6: Setting global autoindent option" + let g:options=[['autoindent', 1, 0, 'global']] + set ai! + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " Should not print anything, use :noa + " 7: don't trigger OptionSet" + let g:options=[['invalid', 1, 1, 'invalid']] + noa set nonu + call assert_equal([['invalid', 1, 1, 'invalid']], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 8: Setting several global list and number option" + let g:options=[['list', 0, 1, 'global'], ['number', 0, 1, 'global']] + set list nu + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 9: don't trigger OptionSet" + let g:options=[['invalid', 1, 1, 'invalid'], ['invalid', 1, 1, 'invalid']] + noa set nolist nonu + call assert_equal([['invalid', 1, 1, 'invalid'], ['invalid', 1, 1, 'invalid']], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 10: Setting global acd" + let g:options=[['autochdir', 0, 1, 'local']] + setlocal acd + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 11: Setting global autoread (also sets local value)" + let g:options=[['autoread', 0, 1, 'global']] + set ar + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 12: Setting local autoread" + let g:options=[['autoread', 1, 1, 'local']] + setlocal ar + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 13: Setting global autoread" + let g:options=[['autoread', 1, 0, 'global']] + setglobal invar + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 14: Setting option backspace through :let" + let g:options=[['backspace', '', 'eol,indent,start', 'global']] + let &bs="eol,indent,start" + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 15: Setting option backspace through setbufvar()" + let g:options=[['backup', 0, 1, 'local']] + " try twice, first time, shouldn't trigger because option name is invalid, + " second time, it should trigger + call assert_fails("call setbufvar(1, '&l:bk', 1)", "E355") + " should trigger, use correct option name + call setbufvar(1, '&backup', 1) + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 16: Setting number option using setwinvar" + let g:options=[['number', 0, 1, 'local']] + call setwinvar(0, '&number', 1) + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 17: Setting key option, shouldn't trigger" + let g:options=[['key', 'invalid', 'invalid1', 'invalid']] + setlocal key=blah + setlocal key= + call assert_equal([['key', 'invalid', 'invalid1', 'invalid']], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 18: Setting string option" + let oldval = &tags + let g:options=[['tags', oldval, 'tagpath', 'global']] + set tags=tagpath + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " 1l: Resetting string option" + let g:options=[['tags', 'tagpath', oldval, 'global']] + set tags& + call assert_equal([], g:options) + call assert_equal(g:opt[0], g:opt[1]) + + " Cleanup + au! OptionSet + for opt in ['nu', 'ai', 'acd', 'ar', 'bs', 'backup', 'cul', 'cp'] + exe printf(":set %s&vi", opt) + endfor + call test_override('starting', 0) + delfunc! AutoCommandOptionSet +endfunc + +func Test_OptionSet_diffmode() + throw 'skipped: Nvim does not support test_override()' + call test_override('starting', 1) + " 18: Changing an option when enetering diff mode + new + au OptionSet diff :let &l:cul=v:option_new + + call setline(1, ['buffer 1', 'line2', 'line3', 'line4']) + call assert_equal(0, &l:cul) + diffthis + call assert_equal(1, &l:cul) + + vnew + call setline(1, ['buffer 2', 'line 2', 'line 3', 'line4']) + call assert_equal(0, &l:cul) + diffthis + call assert_equal(1, &l:cul) + + diffoff + call assert_equal(0, &l:cul) + call assert_equal(1, getwinvar(2, '&l:cul')) + bw! + + call assert_equal(1, &l:cul) + diffoff! + call assert_equal(0, &l:cul) + call assert_equal(0, getwinvar(1, '&l:cul')) + bw! + + " Cleanup + au! OptionSet + call test_override('starting', 0) +endfunc + +func Test_OptionSet_diffmode_close() + throw 'skipped: Nvim does not support test_override()' + call test_override('starting', 1) + " 19: Try to close the current window when entering diff mode + " should not segfault + new + au OptionSet diff close + + call setline(1, ['buffer 1', 'line2', 'line3', 'line4']) + call assert_fails(':diffthis', 'E788') + call assert_equal(1, &diff) + vnew + call setline(1, ['buffer 2', 'line 2', 'line 3', 'line4']) + call assert_fails(':diffthis', 'E788') + call assert_equal(1, &diff) + bw! + call assert_fails(':diffoff!', 'E788') + bw! + + " Cleanup + au! OptionSet + call test_override('starting', 0) + "delfunc! AutoCommandOptionSet +endfunc + +" Test for Bufleave autocommand that deletes the buffer we are about to edit. +func Test_BufleaveWithDelete() + new | edit Xfile1 + + augroup test_bufleavewithdelete + autocmd! + autocmd BufLeave Xfile1 bwipe Xfile2 + augroup END + + call assert_fails('edit Xfile2', 'E143:') + call assert_equal('Xfile1', bufname('%')) + + autocmd! test_bufleavewithdelete BufLeave Xfile1 + augroup! test_bufleavewithdelete + + new + bwipe! Xfile1 +endfunc + +" Test for autocommand that changes the buffer list, when doing ":ball". +func Test_Acmd_BufAll() + enew! + %bwipe! + call writefile(['Test file Xxx1'], 'Xxx1') + call writefile(['Test file Xxx2'], 'Xxx2') + call writefile(['Test file Xxx3'], 'Xxx3') + + " Add three files to the buffer list + split Xxx1 + close + split Xxx2 + close + split Xxx3 + close + + " Wipe the buffer when the buffer is opened + au BufReadPost Xxx2 bwipe + + call append(0, 'Test file Xxx4') + ball + + call assert_equal(2, winnr('$')) + call assert_equal('Xxx1', bufname(winbufnr(winnr('$')))) + wincmd t + + au! BufReadPost + %bwipe! + call delete('Xxx1') + call delete('Xxx2') + call delete('Xxx3') + enew! | only +endfunc + +" Test for autocommand that changes current buffer on BufEnter event. +" Check if modelines are interpreted for the correct buffer. +func Test_Acmd_BufEnter() + %bwipe! + call writefile(['start of test file Xxx1', + \ "\<Tab>this is a test", + \ 'end of test file Xxx1'], 'Xxx1') + call writefile(['start of test file Xxx2', + \ 'vim: set noai :', + \ "\<Tab>this is a test", + \ 'end of test file Xxx2'], 'Xxx2') + + au BufEnter Xxx2 brew + set ai modeline modelines=3 + edit Xxx1 + " edit Xxx2, autocmd will do :brew + edit Xxx2 + exe "normal G?this is a\<CR>" + " Append text with autoindent to this file + normal othis should be auto-indented + call assert_equal("\<Tab>this should be auto-indented", getline('.')) + call assert_equal(3, line('.')) + " Remove autocmd and edit Xxx2 again + au! BufEnter Xxx2 + buf! Xxx2 + exe "normal G?this is a\<CR>" + " append text without autoindent to Xxx + normal othis should be in column 1 + call assert_equal("this should be in column 1", getline('.')) + call assert_equal(4, line('.')) + + %bwipe! + call delete('Xxx1') + call delete('Xxx2') + set ai&vim modeline&vim modelines&vim +endfunc + +" Test for issue #57 +" do not move cursor on <c-o> when autoindent is set +func Test_ai_CTRL_O() + enew! + set ai + let save_fo = &fo + set fo+=r + exe "normal o# abcdef\<Esc>2hi\<CR>\<C-O>d0\<Esc>" + exe "normal o# abcdef\<Esc>2hi\<C-O>d0\<Esc>" + call assert_equal(['# abc', 'def', 'def'], getline(2, 4)) + + set ai&vim + let &fo = save_fo + enew! +endfunc + +" Test for autocommand that deletes the current buffer on BufLeave event. +" Also test deleting the last buffer, should give a new, empty buffer. +func Test_BufLeave_Wipe() + throw 'skipped: TODO: ' + %bwipe! + let content = ['start of test file Xxx', + \ 'this is a test', + \ 'end of test file Xxx'] + call writefile(content, 'Xxx1') + call writefile(content, 'Xxx2') + + au BufLeave Xxx2 bwipe + edit Xxx1 + split Xxx2 + " delete buffer Xxx2, we should be back to Xxx1 + bwipe + call assert_equal('Xxx1', bufname('%')) + call assert_equal(1, winnr('$')) + + " Create an alternate buffer + %write! test.out + call assert_equal('test.out', bufname('#')) + " delete alternate buffer + bwipe test.out + call assert_equal('Xxx1', bufname('%')) + call assert_equal('', bufname('#')) + + au BufLeave Xxx1 bwipe + " delete current buffer, get an empty one + bwipe! + call assert_equal(1, line('$')) + call assert_equal('', bufname('%')) + let g:bufinfo = getbufinfo() + call assert_equal(1, len(g:bufinfo)) + + call delete('Xxx1') + call delete('Xxx2') + call delete('test.out') + %bwipe + au! BufLeave + + " check that bufinfo doesn't contain a pointer to freed memory + call test_garbagecollect_now() +endfunc + +func Test_QuitPre() + edit Xfoo + let winid = win_getid(winnr()) + split Xbar + au! QuitPre * let g:afile = expand('<afile>') + " Close the other window, <afile> should be correct. + exe win_id2win(winid) . 'q' + call assert_equal('Xfoo', g:afile) + + unlet g:afile + bwipe Xfoo + bwipe Xbar +endfunc + +func Test_Cmdline() + au! CmdlineEnter : let g:entered = expand('<afile>') + au! CmdlineLeave : let g:left = expand('<afile>') + let g:entered = 0 + let g:left = 0 + call feedkeys(":echo 'hello'\<CR>", 'xt') + call assert_equal(':', g:entered) + call assert_equal(':', g:left) + au! CmdlineEnter + au! CmdlineLeave + + au! CmdlineEnter / let g:entered = expand('<afile>') + au! CmdlineLeave / let g:left = expand('<afile>') + let g:entered = 0 + let g:left = 0 + new + call setline(1, 'hello') + call feedkeys("/hello\<CR>", 'xt') + call assert_equal('/', g:entered) + call assert_equal('/', g:left) + bwipe! + au! CmdlineEnter + au! CmdlineLeave +endfunc + +" Test for BufWritePre autocommand that deletes or unloads the buffer. +func Test_BufWritePre() + %bwipe + au BufWritePre Xxx1 bunload + au BufWritePre Xxx2 bwipe + + call writefile(['start of Xxx1', 'test', 'end of Xxx1'], 'Xxx1') + call writefile(['start of Xxx2', 'test', 'end of Xxx2'], 'Xxx2') + + edit Xtest + e! Xxx2 + bdel Xtest + e Xxx1 + " write it, will unload it and give an error msg + call assert_fails('w', 'E203') + call assert_equal('Xxx2', bufname('%')) + edit Xtest + e! Xxx2 + bwipe Xtest + " write it, will delete the buffer and give an error msg + call assert_fails('w', 'E203') + call assert_equal('Xxx1', bufname('%')) + au! BufWritePre + call delete('Xxx1') + call delete('Xxx2') +endfunc + +" Test for BufUnload autocommand that unloads all the other buffers +func Test_bufunload_all() + call writefile(['Test file Xxx1'], 'Xxx1')" + call writefile(['Test file Xxx2'], 'Xxx2')" + + let content = [ + \ "func UnloadAllBufs()", + \ " let i = 1", + \ " while i <= bufnr('$')", + \ " if i != bufnr('%') && bufloaded(i)", + \ " exe i . 'bunload'", + \ " endif", + \ " let i += 1", + \ " endwhile", + \ "endfunc", + \ "au BufUnload * call UnloadAllBufs()", + \ "au VimLeave * call writefile(['Test Finished'], 'Xout')", + \ "edit Xxx1", + \ "split Xxx2", + \ "q"] + call writefile(content, 'Xtest') + + call delete('Xout') + call system(v:progpath. ' -u NORC -i NONE -N -S Xtest') + call assert_true(filereadable('Xout')) + + call delete('Xxx1') + call delete('Xxx2') + call delete('Xtest') + call delete('Xout') +endfunc + +" Some tests for buffer-local autocommands +func Test_buflocal_autocmd() + let g:bname = '' + edit xx + au BufLeave <buffer> let g:bname = expand("%") + " here, autocommand for xx should trigger. + " but autocommand shall not apply to buffer named <buffer>. + edit somefile + call assert_equal('xx', g:bname) + let g:bname = '' + " here, autocommand shall be auto-deleted + bwipe xx + " autocmd should not trigger + edit xx + call assert_equal('', g:bname) + " autocmd should not trigger + edit somefile + call assert_equal('', g:bname) + enew + unlet g:bname +endfunc + +" Test for "*Cmd" autocommands +func Test_Cmd_Autocmds() + call writefile(['start of Xxx', "\tabc2", 'end of Xxx'], 'Xxx') + + enew! + au BufReadCmd XtestA 0r Xxx|$del + edit XtestA " will read text of Xxd instead + call assert_equal('start of Xxx', getline(1)) + + au BufWriteCmd XtestA call append(line("$"), "write") + write " will append a line to the file + call assert_equal('write', getline('$')) + call assert_fails('read XtestA', 'E484') " should not read anything + call assert_equal('write', getline(4)) + + " now we have: + " 1 start of Xxx + " 2 abc2 + " 3 end of Xxx + " 4 write + + au FileReadCmd XtestB '[r Xxx + 2r XtestB " will read Xxx below line 2 instead + call assert_equal('start of Xxx', getline(3)) + + " now we have: + " 1 start of Xxx + " 2 abc2 + " 3 start of Xxx + " 4 abc2 + " 5 end of Xxx + " 6 end of Xxx + " 7 write + + au FileWriteCmd XtestC '[,']copy $ + normal 4GA1 + 4,5w XtestC " will copy lines 4 and 5 to the end + call assert_equal("\tabc21", getline(8)) + call assert_fails('r XtestC', 'E484') " should not read anything + call assert_equal("end of Xxx", getline(9)) + + " now we have: + " 1 start of Xxx + " 2 abc2 + " 3 start of Xxx + " 4 abc21 + " 5 end of Xxx + " 6 end of Xxx + " 7 write + " 8 abc21 + " 9 end of Xxx + + let g:lines = [] + au FileAppendCmd XtestD call extend(g:lines, getline(line("'["), line("']"))) + w >>XtestD " will add lines to 'lines' + call assert_equal(9, len(g:lines)) + call assert_fails('$r XtestD', 'E484') " should not read anything + call assert_equal(9, line('$')) + call assert_equal('end of Xxx', getline('$')) + + au BufReadCmd XtestE 0r Xxx|$del + sp XtestE " split window with test.out + call assert_equal('end of Xxx', getline(3)) + + let g:lines = [] + exe "normal 2Goasdf\<Esc>\<C-W>\<C-W>" + au BufWriteCmd XtestE call extend(g:lines, getline(0, '$')) + wall " will write other window to 'lines' + call assert_equal(4, len(g:lines), g:lines) + call assert_equal("\tasdf", g:lines[2]) + + au! BufReadCmd + au! BufWriteCmd + au! FileReadCmd + au! FileWriteCmd + au! FileAppendCmd + %bwipe! + call delete('Xxx') + enew! +endfunc + +func SetChangeMarks(start, end) + exe a:start. 'mark [' + exe a:end. 'mark ]' +endfunc + +" Verify the effects of autocmds on '[ and '] +func Test_change_mark_in_autocmds() + edit! Xtest + call feedkeys("ia\<CR>b\<CR>c\<CR>d\<C-g>u", 'xtn') + + call SetChangeMarks(2, 3) + write + call assert_equal([1, 4], [line("'["), line("']")]) + + call SetChangeMarks(2, 3) + au BufWritePre * call assert_equal([1, 4], [line("'["), line("']")]) + write + au! BufWritePre + + if executable('cat') + write XtestFilter + write >> XtestFilter + + call SetChangeMarks(2, 3) + " Marks are set to the entire range of the write + au FilterWritePre * call assert_equal([1, 4], [line("'["), line("']")]) + " '[ is adjusted to just before the line that will receive the filtered + " data + au FilterReadPre * call assert_equal([4, 4], [line("'["), line("']")]) + " The filtered data is read into the buffer, and the source lines are + " still present, so the range is after the source lines + au FilterReadPost * call assert_equal([5, 12], [line("'["), line("']")]) + %!cat XtestFilter + " After the filtered data is read, the original lines are deleted + call assert_equal([1, 8], [line("'["), line("']")]) + au! FilterWritePre,FilterReadPre,FilterReadPost + undo + + call SetChangeMarks(1, 4) + au FilterWritePre * call assert_equal([2, 3], [line("'["), line("']")]) + au FilterReadPre * call assert_equal([3, 3], [line("'["), line("']")]) + au FilterReadPost * call assert_equal([4, 11], [line("'["), line("']")]) + 2,3!cat XtestFilter + call assert_equal([2, 9], [line("'["), line("']")]) + au! FilterWritePre,FilterReadPre,FilterReadPost + undo + + call delete('XtestFilter') + endif + + call SetChangeMarks(1, 4) + au FileWritePre * call assert_equal([2, 3], [line("'["), line("']")]) + 2,3write Xtest2 + au! FileWritePre + + call SetChangeMarks(2, 3) + au FileAppendPre * call assert_equal([1, 4], [line("'["), line("']")]) + write >> Xtest2 + au! FileAppendPre + + call SetChangeMarks(1, 4) + au FileAppendPre * call assert_equal([2, 3], [line("'["), line("']")]) + 2,3write >> Xtest2 + au! FileAppendPre + + call SetChangeMarks(1, 1) + au FileReadPre * call assert_equal([3, 1], [line("'["), line("']")]) + au FileReadPost * call assert_equal([4, 11], [line("'["), line("']")]) + 3read Xtest2 + au! FileReadPre,FileReadPost + undo + + call SetChangeMarks(4, 4) + " When the line is 0, it's adjusted to 1 + au FileReadPre * call assert_equal([1, 4], [line("'["), line("']")]) + au FileReadPost * call assert_equal([1, 8], [line("'["), line("']")]) + 0read Xtest2 + au! FileReadPre,FileReadPost + undo + + call SetChangeMarks(4, 4) + " When the line is 0, it's adjusted to 1 + au FileReadPre * call assert_equal([1, 4], [line("'["), line("']")]) + au FileReadPost * call assert_equal([2, 9], [line("'["), line("']")]) + 1read Xtest2 + au! FileReadPre,FileReadPost + undo + + bwipe! + call delete('Xtest') + call delete('Xtest2') +endfunc + +func Test_Filter_noshelltemp() + if !executable('cat') + return + endif + + enew! + call setline(1, ['a', 'b', 'c', 'd']) + + let shelltemp = &shelltemp + set shelltemp + + let g:filter_au = 0 + au FilterWritePre * let g:filter_au += 1 + au FilterReadPre * let g:filter_au += 1 + au FilterReadPost * let g:filter_au += 1 + %!cat + call assert_equal(3, g:filter_au) + + if has('filterpipe') + set noshelltemp + + let g:filter_au = 0 + au FilterWritePre * let g:filter_au += 1 + au FilterReadPre * let g:filter_au += 1 + au FilterReadPost * let g:filter_au += 1 + %!cat + call assert_equal(0, g:filter_au) + endif + + au! FilterWritePre,FilterReadPre,FilterReadPost + let &shelltemp = shelltemp + bwipe! +endfunc + +func Test_TextYankPost() + enew! + call setline(1, ['foo']) + + let g:event = [] + au TextYankPost * let g:event = copy(v:event) + + call assert_equal({}, v:event) + call assert_fails('let v:event = {}', 'E46:') + call assert_fails('let v:event.mykey = 0', 'E742:') + + norm "ayiw + call assert_equal( + \{'regcontents': ['foo'], 'regname': 'a', 'operator': 'y', 'regtype': 'v'}, + \g:event) + norm y_ + call assert_equal( + \{'regcontents': ['foo'], 'regname': '', 'operator': 'y', 'regtype': 'V'}, + \g:event) + call feedkeys("\<C-V>y", 'x') + call assert_equal( + \{'regcontents': ['f'], 'regname': '', 'operator': 'y', 'regtype': "\x161"}, + \g:event) + norm "xciwbar + call assert_equal( + \{'regcontents': ['foo'], 'regname': 'x', 'operator': 'c', 'regtype': 'v'}, + \g:event) + norm "bdiw + call assert_equal( + \{'regcontents': ['bar'], 'regname': 'b', 'operator': 'd', 'regtype': 'v'}, + \g:event) + + call assert_equal({}, v:event) + + au! TextYankPost + unlet g:event + bwipe! +endfunc + +func Test_nocatch_wipe_all_buffers() + " Real nasty autocommand: wipe all buffers on any event. + au * * bwipe * + call assert_fails('next x', 'E93') + bwipe + au! +endfunc + +func Test_nocatch_wipe_dummy_buffer() + " Nasty autocommand: wipe buffer on any event. + au * x bwipe + call assert_fails('lv½ /x', 'E480') + au! +endfunc diff --git a/src/nvim/testdir/test_breakindent.vim b/src/nvim/testdir/test_breakindent.vim new file mode 100644 index 0000000000..7deffbe452 --- /dev/null +++ b/src/nvim/testdir/test_breakindent.vim @@ -0,0 +1,298 @@ +" Test for breakindent +" +" Note: if you get strange failures when adding new tests, it might be that +" while the test is run, the breakindent cacheing gets in its way. +" It helps to change the tabstop setting and force a redraw (e.g. see +" Test_breakindent08()) +if !exists('+breakindent') + finish +endif + +source view_util.vim + +let s:input ="\tabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP" + +function s:screen_lines(lnum, width) abort + return ScreenLines([a:lnum, a:lnum + 2], a:width) +endfunction + +function! s:compare_lines(expect, actual) + call assert_equal(join(a:expect, "\n"), join(a:actual, "\n")) +endfunction + +function s:test_windows(...) + call NewWindow(10, 20) + setl ts=4 sw=4 sts=4 breakindent + put =s:input + exe get(a:000, 0, '') +endfunction + +function s:close_windows(...) + call CloseWindow() + exe get(a:000, 0, '') +endfunction + +function Test_breakindent01() + " simple breakindent test + call s:test_windows('setl briopt=min:0') + let lines=s:screen_lines(line('.'),8) + let expect=[ +\ " abcd", +\ " qrst", +\ " GHIJ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunction + +function Test_breakindent02() + " simple breakindent test with showbreak set + call s:test_windows('setl briopt=min:0 sbr=>>') + let lines=s:screen_lines(line('.'),8) + let expect=[ +\ " abcd", +\ " >>qr", +\ " >>EF", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows('set sbr=') +endfunction + +function Test_breakindent03() + " simple breakindent test with showbreak set and briopt including sbr + call s:test_windows('setl briopt=sbr,min:0 sbr=++') + let lines=s:screen_lines(line('.'),8) + let expect=[ +\ " abcd", +\ "++ qrst", +\ "++ GHIJ", +\ ] + call s:compare_lines(expect, lines) + " clean up + call s:close_windows('set sbr=') +endfunction + +function Test_breakindent04() + " breakindent set with min width 18 + call s:test_windows('setl sbr= briopt=min:18') + let lines=s:screen_lines(line('.'),8) + let expect=[ +\ " abcd", +\ " qrstuv", +\ " IJKLMN", +\ ] + call s:compare_lines(expect, lines) + " clean up + call s:close_windows('set sbr=') +endfunction + +function Test_breakindent05() + " breakindent set and shift by 2 + call s:test_windows('setl briopt=shift:2,min:0') + let lines=s:screen_lines(line('.'),8) + let expect=[ +\ " abcd", +\ " qr", +\ " EF", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunction + +function Test_breakindent06() + " breakindent set and shift by -1 + call s:test_windows('setl briopt=shift:-1,min:0') + let lines=s:screen_lines(line('.'),8) + let expect=[ +\ " abcd", +\ " qrstu", +\ " HIJKL", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunction + +function Test_breakindent07() + " breakindent set and shift by 1, Number set sbr=? and briopt:sbr + call s:test_windows('setl briopt=shift:1,sbr,min:0 nu sbr=? nuw=4 cpo+=n') + let lines=s:screen_lines(line('.'),10) + let expect=[ +\ " 2 ab", +\ "? m", +\ "? x", +\ ] + call s:compare_lines(expect, lines) + " clean up + call s:close_windows('set sbr= cpo-=n') +endfunction + +function Test_breakindent07a() + " breakindent set and shift by 1, Number set sbr=? and briopt:sbr + call s:test_windows('setl briopt=shift:1,sbr,min:0 nu sbr=? nuw=4') + let lines=s:screen_lines(line('.'),10) + let expect=[ +\ " 2 ab", +\ " ? m", +\ " ? x", +\ ] + call s:compare_lines(expect, lines) + " clean up + call s:close_windows('set sbr=') +endfunction + +function Test_breakindent08() + " breakindent set and shift by 1, Number and list set sbr=# and briopt:sbr + call s:test_windows('setl briopt=shift:1,sbr,min:0 nu nuw=4 sbr=# list cpo+=n ts=4') + " make sure, cache is invalidated! + set ts=8 + redraw! + set ts=4 + redraw! + let lines=s:screen_lines(line('.'),10) + let expect=[ +\ " 2 ^Iabcd", +\ "# opq", +\ "# BCD", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows('set sbr= cpo-=n') +endfunction + +function Test_breakindent08a() + " breakindent set and shift by 1, Number and list set sbr=# and briopt:sbr + call s:test_windows('setl briopt=shift:1,sbr,min:0 nu nuw=4 sbr=# list') + let lines=s:screen_lines(line('.'),10) + let expect=[ +\ " 2 ^Iabcd", +\ " # opq", +\ " # BCD", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows('set sbr=') +endfunction + +function Test_breakindent09() + " breakindent set and shift by 1, Number and list set sbr=# + call s:test_windows('setl briopt=shift:1,min:0 nu nuw=4 sbr=# list') + let lines=s:screen_lines(line('.'),10) + let expect=[ +\ " 2 ^Iabcd", +\ " #op", +\ " #AB", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows('set sbr=') +endfunction + +function Test_breakindent10() + " breakindent set, Number set sbr=~ + call s:test_windows('setl cpo+=n sbr=~ nu nuw=4 nolist briopt=sbr,min:0') + " make sure, cache is invalidated! + set ts=8 + redraw! + set ts=4 + redraw! + let lines=s:screen_lines(line('.'),10) + let expect=[ +\ " 2 ab", +\ "~ mn", +\ "~ yz", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows('set sbr= cpo-=n') +endfunction + +function Test_breakindent11() + " test strdisplaywidth() + call s:test_windows('setl cpo-=n sbr=>> nu nuw=4 nolist briopt= ts=4') + let text=getline(2) + let width = strlen(text[1:])+indent(2)+strlen(&sbr)*3 " text wraps 3 times + call assert_equal(width, strdisplaywidth(text)) + call s:close_windows('set sbr=') +endfunction + +function Test_breakindent12() + " test breakindent with long indent + let s:input="\t\t\t\t\t{" + call s:test_windows('setl breakindent linebreak briopt=min:10 nu numberwidth=3 ts=4 list listchars=tab:>-') + let lines=s:screen_lines(2,16) + let expect=[ +\ " 2 >--->--->--->", +\ " ---{ ", +\ "~ ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows('set nuw=4 listchars=') +endfunction + +function Test_breakindent13() + let s:input="" + call s:test_windows('setl breakindent briopt=min:10 ts=8') + vert resize 20 + call setline(1, [" a\tb\tc\td\te", " z y x w v"]) + 1 + norm! fbgj"ayl + 2 + norm! fygj"byl + call assert_equal('d', @a) + call assert_equal('w', @b) + call s:close_windows() +endfunction + +function Test_breakindent14() + let s:input="" + call s:test_windows('setl breakindent briopt= ts=8') + vert resize 30 + norm! 3a1234567890 + norm! a abcde + exec "norm! 0\<C-V>tex" + let lines=s:screen_lines(line('.'),8) + let expect=[ +\ "e ", +\ "~ ", +\ "~ ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunction + +function Test_breakindent15() + let s:input="" + call s:test_windows('setl breakindent briopt= ts=8 sw=8') + vert resize 30 + norm! 4a1234567890 + exe "normal! >>\<C-V>3f0x" + let lines=s:screen_lines(line('.'),20) + let expect=[ +\ " 1234567890 ", +\ "~ ", +\ "~ ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunction + +function Test_breakindent16() + " Check that overlong lines are indented correctly. + let s:input="" + call s:test_windows('setl breakindent briopt=min:0 ts=4') + call setline(1, "\t".repeat("1234567890", 10)) + resize 6 + norm! 1gg$ + redraw! + let lines=s:screen_lines(1,10) + let expect=[ +\ " 789012", +\ " 345678", +\ " 901234", +\ ] + call s:compare_lines(expect, lines) + let lines=s:screen_lines(4,10) + let expect=[ +\ " 567890", +\ " 123456", +\ " 7890 ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunction diff --git a/src/nvim/testdir/test_bufwintabinfo.vim b/src/nvim/testdir/test_bufwintabinfo.vim index 5c916e2dd7..a592cd7b11 100644 --- a/src/nvim/testdir/test_bufwintabinfo.vim +++ b/src/nvim/testdir/test_bufwintabinfo.vim @@ -1,7 +1,6 @@ " Tests for the getbufinfo(), getwininfo() and gettabinfo() functions function Test_getbufwintabinfo() - 1,$bwipeout edit Xtestfile1 edit Xtestfile2 let buflist = getbufinfo() @@ -87,9 +86,17 @@ function Test_get_buf_options() endfunc function Test_get_win_options() + if has('folding') + set foldlevel=999 + endif + set list let opts = getwinvar(1, '&') call assert_equal(v:t_dict, type(opts)) call assert_equal(0, opts.linebreak) + call assert_equal(1, opts.list) + if has('folding') + call assert_equal(999, opts.foldlevel) + endif if has('signs') call assert_equal('auto', opts.signcolumn) endif @@ -97,7 +104,12 @@ function Test_get_win_options() let opts = gettabwinvar(1, 1, '&') call assert_equal(v:t_dict, type(opts)) call assert_equal(0, opts.linebreak) + call assert_equal(1, opts.list) if has('signs') call assert_equal('auto', opts.signcolumn) endif + set list& + if has('folding') + set foldlevel=0 + endif endfunc diff --git a/src/nvim/testdir/test_changedtick.vim b/src/nvim/testdir/test_changedtick.vim new file mode 100644 index 0000000000..3a91bb54aa --- /dev/null +++ b/src/nvim/testdir/test_changedtick.vim @@ -0,0 +1,57 @@ +" Tests for b:changedtick + +func Test_changedtick_increments() + new + " New buffer has an empty line, tick starts at 2. + let expected = 2 + call assert_equal(expected, b:changedtick) + call assert_equal(expected, b:['changedtick']) + call setline(1, 'hello') + let expected += 1 + call assert_equal(expected, b:changedtick) + call assert_equal(expected, b:['changedtick']) + undo + " Somehow undo counts as two changes. + let expected += 2 + call assert_equal(expected, b:changedtick) + call assert_equal(expected, b:['changedtick']) + bwipe! +endfunc + +func Test_changedtick_dict_entry() + let d = b: + call assert_equal(b:changedtick, d['changedtick']) +endfunc + +func Test_changedtick_bdel() + new + let bnr = bufnr('%') + let v = b:changedtick + bdel + " Delete counts as a change too. + call assert_equal(v + 1, getbufvar(bnr, 'changedtick')) +endfunc + +func Test_changedtick_islocked() + call assert_equal(0, islocked('b:changedtick')) + let d = b: + call assert_equal(0, islocked('d.changedtick')) +endfunc + +func Test_changedtick_fixed() + call assert_fails('let b:changedtick = 4', 'E46:') + call assert_fails('let b:["changedtick"] = 4', 'E46:') + + call assert_fails('lockvar b:changedtick', 'E940:') + call assert_fails('lockvar b:["changedtick"]', 'E46:') + call assert_fails('unlockvar b:changedtick', 'E940:') + call assert_fails('unlockvar b:["changedtick"]', 'E46:') + call assert_fails('unlet b:changedtick', 'E795:') + call assert_fails('unlet b:["changedtick"]', 'E46:') + + let d = b: + call assert_fails('lockvar d["changedtick"]', 'E46:') + call assert_fails('unlockvar d["changedtick"]', 'E46:') + call assert_fails('unlet d["changedtick"]', 'E46:') + +endfunc diff --git a/src/nvim/testdir/test_charsearch_utf8.vim b/src/nvim/testdir/test_charsearch_utf8.vim new file mode 100644 index 0000000000..ade7dd408c --- /dev/null +++ b/src/nvim/testdir/test_charsearch_utf8.vim @@ -0,0 +1,22 @@ +" Tests for related f{char} and t{char} using utf-8. +if !has('multi_byte') + finish +endif + +" Test for t,f,F,T movement commands +function! Test_search_cmds() + new! + call setline(1, "・最初から最後まで最強のVimは最高") + 1 + normal! f最 + call assert_equal([0, 1, 4, 0], getpos('.')) + normal! ; + call assert_equal([0, 1, 16, 0], getpos('.')) + normal! 2; + call assert_equal([0, 1, 43, 0], getpos('.')) + normal! , + call assert_equal([0, 1, 28, 0], getpos('.')) + bw! +endfunction + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_cindent.vim b/src/nvim/testdir/test_cindent.vim new file mode 100644 index 0000000000..444c4c4109 --- /dev/null +++ b/src/nvim/testdir/test_cindent.vim @@ -0,0 +1,76 @@ +" Test for cinoptions and cindent +" +" TODO: rewrite test3.in into this new style test + +func Test_cino_hash() + " Test that curbuf->b_ind_hash_comment is correctly reset + new + setlocal cindent cinoptions=#1 + setlocal cinoptions= + call setline(1, ["#include <iostream>"]) + call cursor(1, 1) + norm! o#include + "call feedkeys("o#include\<esc>", 't') + call assert_equal(["#include <iostream>", "#include"], getline(1,2)) + bwipe! +endfunc + +func Test_cino_extern_c() + " Test for cino-E + + let without_ind = [ + \ '#ifdef __cplusplus', + \ 'extern "C" {', + \ '#endif', + \ 'int func_a(void);', + \ '#ifdef __cplusplus', + \ '}', + \ '#endif' + \ ] + + let with_ind = [ + \ '#ifdef __cplusplus', + \ 'extern "C" {', + \ '#endif', + \ "\tint func_a(void);", + \ '#ifdef __cplusplus', + \ '}', + \ '#endif' + \ ] + new + setlocal cindent cinoptions=E0 + call setline(1, without_ind) + call feedkeys("gg=G", 'tx') + call assert_equal(with_ind, getline(1, '$')) + + setlocal cinoptions=E-s + call setline(1, with_ind) + call feedkeys("gg=G", 'tx') + call assert_equal(without_ind, getline(1, '$')) + + setlocal cinoptions=Es + let tests = [ + \ ['recognized', ['extern "C" {'], "\t\t;"], + \ ['recognized', ['extern "C++" {'], "\t\t;"], + \ ['recognized', ['extern /* com */ "C"{'], "\t\t;"], + \ ['recognized', ['extern"C"{'], "\t\t;"], + \ ['recognized', ['extern "C"', '{'], "\t\t;"], + \ ['not recognized', ['extern {'], "\t;"], + \ ['not recognized', ['extern /*"C"*/{'], "\t;"], + \ ['not recognized', ['extern "C" //{'], ";"], + \ ['not recognized', ['extern "C" /*{*/'], ";"], + \ ] + + for pair in tests + let lines = pair[1] + call setline(1, lines) + call feedkeys(len(lines) . "Go;", 'tx') + call assert_equal(pair[2], getline(len(lines) + 1), 'Failed for "' . string(lines) . '"') + endfor + + + + bwipe! +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_close_count.vim b/src/nvim/testdir/test_close_count.vim new file mode 100644 index 0000000000..1f9adba32d --- /dev/null +++ b/src/nvim/testdir/test_close_count.vim @@ -0,0 +1,174 @@ + +" Tests for :[count]close! command +func Test_close_count() + enew! | only + + let wids = [win_getid()] + for i in range(5) + new + call add(wids, win_getid()) + endfor + + 4wincmd w + close! + let ids = [] + windo call add(ids, win_getid()) + call assert_equal([wids[5], wids[4], wids[3], wids[1], wids[0]], ids) + + 1close! + let ids = [] + windo call add(ids, win_getid()) + call assert_equal([wids[4], wids[3], wids[1], wids[0]], ids) + + $close! + let ids = [] + windo call add(ids, win_getid()) + call assert_equal([wids[4], wids[3], wids[1]], ids) + + 1wincmd w + 2close! + let ids = [] + windo call add(ids, win_getid()) + call assert_equal([wids[4], wids[1]], ids) + + 1wincmd w + new + call add(wids, win_getid()) + new + call add(wids, win_getid()) + 2wincmd w + -1close! + let ids = [] + windo call add(ids, win_getid()) + call assert_equal([wids[6], wids[4], wids[1]], ids) + + 2wincmd w + +1close! + let ids = [] + windo call add(ids, win_getid()) + call assert_equal([wids[6], wids[4]], ids) + + only! +endfunc + +" Tests for :[count]hide command +func Test_hide_count() + enew! | only + + let wids = [win_getid()] + for i in range(5) + new + call add(wids, win_getid()) + endfor + + 4wincmd w + .hide + let ids = [] + windo call add(ids, win_getid()) + call assert_equal([wids[5], wids[4], wids[3], wids[1], wids[0]], ids) + + 1hide + let ids = [] + windo call add(ids, win_getid()) + call assert_equal([wids[4], wids[3], wids[1], wids[0]], ids) + + $hide + let ids = [] + windo call add(ids, win_getid()) + call assert_equal([wids[4], wids[3], wids[1]], ids) + + 1wincmd w + 2hide + let ids = [] + windo call add(ids, win_getid()) + call assert_equal([wids[4], wids[1]], ids) + + 1wincmd w + new + call add(wids, win_getid()) + new + call add(wids, win_getid()) + 3wincmd w + -hide + let ids = [] + windo call add(ids, win_getid()) + call assert_equal([wids[7], wids[4], wids[1]], ids) + + 2wincmd w + +hide + let ids = [] + windo call add(ids, win_getid()) + call assert_equal([wids[7], wids[4]], ids) + + only! +endfunc + +" Tests for :[count]close! command with 'hidden' +func Test_hidden_close_count() + enew! | only + + let wids = [win_getid()] + for i in range(5) + new + call add(wids, win_getid()) + endfor + + set hidden + + $ hide + let ids = [] + windo call add(ids, win_getid()) + call assert_equal([wids[5], wids[4], wids[3], wids[2], wids[1]], ids) + + $-1 close! + let ids = [] + windo call add(ids, win_getid()) + call assert_equal([wids[5], wids[4], wids[3], wids[1]], ids) + + 1wincmd w + .+close! + let ids = [] + windo call add(ids, win_getid()) + call assert_equal([wids[5], wids[3], wids[1]], ids) + + set nohidden + only! +endfunc + +" Tests for 'CTRL-W c' command to close windows. +func Test_winclose_command() + enew! | only + + let wids = [win_getid()] + for i in range(5) + new + call add(wids, win_getid()) + endfor + + set hidden + + 4wincmd w + exe "normal \<C-W>c" + let ids = [] + windo call add(ids, win_getid()) + call assert_equal([wids[5], wids[4], wids[3], wids[1], wids[0]], ids) + + exe "normal 1\<C-W>c" + let ids = [] + windo call add(ids, win_getid()) + call assert_equal([wids[4], wids[3], wids[1], wids[0]], ids) + + exe "normal 9\<C-W>c" + let ids = [] + windo call add(ids, win_getid()) + call assert_equal([wids[4], wids[3], wids[1]], ids) + + 1wincmd w + exe "normal 2\<C-W>c" + let ids = [] + windo call add(ids, win_getid()) + call assert_equal([wids[4], wids[1]], ids) + + set nohidden + only! +endfunc diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index 0c9d1297d6..673246e1fb 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -1,8 +1,9 @@ " Tests for editing the command line. + func Test_complete_tab() call writefile(['testfile'], 'Xtestfile') - call feedkeys(":e Xtest\t\r", "tx") + call feedkeys(":e Xtestf\t\r", "tx") call assert_equal('testfile', getline(1)) call delete('Xtestfile') endfunc @@ -17,7 +18,7 @@ func Test_complete_wildmenu() call writefile(['testfile1'], 'Xtestfile1') call writefile(['testfile2'], 'Xtestfile2') set wildmenu - call feedkeys(":e Xtest\t\t\r", "tx") + call feedkeys(":e Xtestf\t\t\r", "tx") call assert_equal('testfile2', getline(1)) call delete('Xtestfile1') @@ -25,6 +26,88 @@ func Test_complete_wildmenu() set nowildmenu endfunc +func Test_map_completion() + if !has('cmdline_compl') + return + endif + call feedkeys(":map <unique> <si\<Tab>\<Home>\"\<CR>", 'xt') + call assert_equal('"map <unique> <silent>', getreg(':')) + call feedkeys(":map <script> <un\<Tab>\<Home>\"\<CR>", 'xt') + call assert_equal('"map <script> <unique>', getreg(':')) + call feedkeys(":map <expr> <sc\<Tab>\<Home>\"\<CR>", 'xt') + call assert_equal('"map <expr> <script>', getreg(':')) + call feedkeys(":map <buffer> <e\<Tab>\<Home>\"\<CR>", 'xt') + call assert_equal('"map <buffer> <expr>', getreg(':')) + call feedkeys(":map <nowait> <b\<Tab>\<Home>\"\<CR>", 'xt') + call assert_equal('"map <nowait> <buffer>', getreg(':')) + call feedkeys(":map <special> <no\<Tab>\<Home>\"\<CR>", 'xt') + call assert_equal('"map <special> <nowait>', getreg(':')) + call feedkeys(":map <silent> <sp\<Tab>\<Home>\"\<CR>", 'xt') + call assert_equal('"map <silent> <special>', getreg(':')) +endfunc + +func Test_match_completion() + if !has('cmdline_compl') + return + endif + hi Aardig ctermfg=green + call feedkeys(":match \<Tab>\<Home>\"\<CR>", 'xt') + call assert_equal('"match Aardig', getreg(':')) + call feedkeys(":match \<S-Tab>\<Home>\"\<CR>", 'xt') + call assert_equal('"match none', getreg(':')) +endfunc + +func Test_highlight_completion() + if !has('cmdline_compl') + return + endif + hi Aardig ctermfg=green + call feedkeys(":hi \<Tab>\<Home>\"\<CR>", 'xt') + call assert_equal('"hi Aardig', getreg(':')) + call feedkeys(":hi li\<S-Tab>\<Home>\"\<CR>", 'xt') + call assert_equal('"hi link', getreg(':')) + call feedkeys(":hi d\<S-Tab>\<Home>\"\<CR>", 'xt') + call assert_equal('"hi default', getreg(':')) + call feedkeys(":hi c\<S-Tab>\<Home>\"\<CR>", 'xt') + call assert_equal('"hi clear', getreg(':')) + + " A cleared group does not show up in completions. + hi Anders ctermfg=green + call assert_equal(['Aardig', 'Anders'], getcompletion('A', 'highlight')) + hi clear Aardig + call assert_equal(['Anders'], getcompletion('A', 'highlight')) + hi clear Anders + call assert_equal([], getcompletion('A', 'highlight')) +endfunc + +func Test_expr_completion() + if !has('cmdline_compl') + return + endif + for cmd in [ + \ 'let a = ', + \ 'if', + \ 'elseif', + \ 'while', + \ 'for', + \ 'echo', + \ 'echon', + \ 'execute', + \ 'echomsg', + \ 'echoerr', + \ 'call', + \ 'return', + \ 'cexpr', + \ 'caddexpr', + \ 'cgetexpr', + \ 'lexpr', + \ 'laddexpr', + \ 'lgetexpr'] + call feedkeys(":" . cmd . " getl\<Tab>\<Home>\"\<CR>", 'xt') + call assert_equal('"' . cmd . ' getline(', getreg(':')) + endfor +endfunc + func Test_getcompletion() if !has('cmdline_compl') return @@ -130,6 +213,11 @@ func Test_getcompletion() let l = getcompletion('dark', 'highlight') call assert_equal([], l) + let l = getcompletion('', 'messages') + call assert_true(index(l, 'clear') >= 0) + let l = getcompletion('not', 'messages') + call assert_equal([], l) + if has('cscope') let l = getcompletion('', 'cscope') let cmds = ['add', 'find', 'help', 'kill', 'reset', 'show'] @@ -204,3 +292,129 @@ func Test_expand_star_star() bwipe! call delete('a', 'rf') endfunc + +func Test_paste_in_cmdline() + let @a = "def" + call feedkeys(":abc \<C-R>a ghi\<C-B>\"\<CR>", 'tx') + call assert_equal('"abc def ghi', @:) + + new + call setline(1, 'asdf.x /tmp/some verylongword a;b-c*d ') + + call feedkeys(":aaa \<C-R>\<C-W> bbb\<C-B>\"\<CR>", 'tx') + call assert_equal('"aaa asdf bbb', @:) + + call feedkeys("ft:aaa \<C-R>\<C-F> bbb\<C-B>\"\<CR>", 'tx') + call assert_equal('"aaa /tmp/some bbb', @:) + + set incsearch + call feedkeys("fy:aaa veryl\<C-R>\<C-W> bbb\<C-B>\"\<CR>", 'tx') + call assert_equal('"aaa verylongword bbb', @:) + + call feedkeys("f;:aaa \<C-R>\<C-A> bbb\<C-B>\"\<CR>", 'tx') + call assert_equal('"aaa a;b-c*d bbb', @:) + + call feedkeys(":\<C-\>etoupper(getline(1))\<CR>\<C-B>\"\<CR>", 'tx') + call assert_equal('"ASDF.X /TMP/SOME VERYLONGWORD A;B-C*D ', @:) + bwipe! +endfunc + +func Test_remove_char_in_cmdline() + call feedkeys(":abc def\<S-Left>\<Del>\<C-B>\"\<CR>", 'tx') + call assert_equal('"abc ef', @:) + + call feedkeys(":abc def\<S-Left>\<BS>\<C-B>\"\<CR>", 'tx') + call assert_equal('"abcdef', @:) + + call feedkeys(":abc def ghi\<S-Left>\<C-W>\<C-B>\"\<CR>", 'tx') + call assert_equal('"abc ghi', @:) + + call feedkeys(":abc def\<S-Left>\<C-U>\<C-B>\"\<CR>", 'tx') + call assert_equal('"def', @:) +endfunc + +func Test_illegal_address1() + new + 2;'( + 2;') + quit +endfunc + +func Test_illegal_address2() + call writefile(['c', 'x', ' x', '.', '1;y'], 'Xtest.vim') + new + source Xtest.vim + " Trigger calling validate_cursor() + diffsp Xtest.vim + quit! + bwipe! + call delete('Xtest.vim') +endfunc + +func Test_cmdline_complete_wildoptions() + help + call feedkeys(":tag /\<c-a>\<c-b>\"\<cr>", 'tx') + let a = join(sort(split(@:)),' ') + set wildoptions=tagfile + call feedkeys(":tag /\<c-a>\<c-b>\"\<cr>", 'tx') + let b = join(sort(split(@:)),' ') + call assert_equal(a, b) + bw! +endfunc + +" using a leading backslash here +set cpo+=C + +func Test_cmdline_search_range() + new + call setline(1, ['a', 'b', 'c', 'd']) + /d + 1,\/s/b/B/ + call assert_equal('B', getline(2)) + + /a + $ + \?,4s/c/C/ + call assert_equal('C', getline(3)) + + call setline(1, ['a', 'b', 'c', 'd']) + %s/c/c/ + 1,\&s/b/B/ + call assert_equal('B', getline(2)) + + bwipe! +endfunc + +" Tests for getcmdline(), getcmdpos() and getcmdtype() +func Check_cmdline(cmdtype) + call assert_equal('MyCmd a', getcmdline()) + call assert_equal(8, getcmdpos()) + call assert_equal(a:cmdtype, getcmdtype()) + return '' +endfunc + +func Test_getcmdtype() + call feedkeys(":MyCmd a\<C-R>=Check_cmdline(':')\<CR>\<Esc>", "xt") + + let cmdtype = '' + debuggreedy + call feedkeys(":debug echo 'test'\<CR>", "t") + call feedkeys("let cmdtype = \<C-R>=string(getcmdtype())\<CR>\<CR>", "t") + call feedkeys("cont\<CR>", "xt") + 0debuggreedy + call assert_equal('>', cmdtype) + + call feedkeys("/MyCmd a\<C-R>=Check_cmdline('/')\<CR>\<Esc>", "xt") + call feedkeys("?MyCmd a\<C-R>=Check_cmdline('?')\<CR>\<Esc>", "xt") + + call feedkeys(":call input('Answer?')\<CR>", "t") + call feedkeys("MyCmd a\<C-R>=Check_cmdline('@')\<CR>\<C-C>", "xt") + + call feedkeys(":insert\<CR>MyCmd a\<C-R>=Check_cmdline('-')\<CR>\<Esc>", "xt") + + cnoremap <expr> <F6> Check_cmdline('=') + call feedkeys("a\<C-R>=MyCmd a\<F6>\<Esc>\<Esc>", "xt") + cunmap <F6> +endfunc + +set cpo& diff --git a/src/nvim/testdir/test_command_count.vim b/src/nvim/testdir/test_command_count.vim index e438a8b077..2d793ed88f 100644 --- a/src/nvim/testdir/test_command_count.vim +++ b/src/nvim/testdir/test_command_count.vim @@ -1,6 +1,7 @@ " Test for user command counts. func Test_command_count_0() + let bufnr = bufnr('%') set hidden set noswapfile @@ -15,17 +16,17 @@ func Test_command_count_0() command! -range=% -addr=buffers RangeBuffersAll :let lines = [<line1>, <line2>] .,$RangeLoadedBuffers - call assert_equal([1, 1], lines) + call assert_equal([bufnr, bufnr], lines) %RangeLoadedBuffers - call assert_equal([1, 1], lines) + call assert_equal([bufnr, bufnr], lines) RangeLoadedBuffersAll - call assert_equal([1, 1], lines) + call assert_equal([bufnr, bufnr], lines) .,$RangeBuffers - call assert_equal([1, lastbuf], lines) + call assert_equal([bufnr, lastbuf], lines) %RangeBuffers - call assert_equal([1, lastbuf], lines) + call assert_equal([bufnr, lastbuf], lines) RangeBuffersAll - call assert_equal([1, lastbuf], lines) + call assert_equal([bufnr, lastbuf], lines) delcommand RangeLoadedBuffers delcommand RangeLoadedBuffersAll @@ -138,6 +139,7 @@ func Test_command_count_2() endfunc func Test_command_count_3() + let bufnr = bufnr('%') se nohidden e aaa let buf_aaa = bufnr('%') @@ -145,7 +147,7 @@ func Test_command_count_3() let buf_bbb = bufnr('%') e ccc let buf_ccc = bufnr('%') - buf 1 + exe bufnr . 'buf' call assert_equal([1, 1, 1], [buflisted(buf_aaa), buflisted(buf_bbb), buflisted(buf_ccc)]) exe buf_bbb . "," . buf_ccc . "bdelete" call assert_equal([1, 0, 0], [buflisted(buf_aaa), buflisted(buf_bbb), buflisted(buf_ccc)]) @@ -155,7 +157,7 @@ endfunc func Test_command_count_4() %argd - let bufnr = bufnr('$') + 1 + let bufnr = bufnr('$') arga aa bb cc dd ee ff 3argu let args = [] @@ -171,6 +173,8 @@ func Test_command_count_4() only! exe bufnr . 'buf' + bnext + let bufnr = bufnr('%') let buffers = [] .,$-bufdo call add(buffers, bufnr('%')) call assert_equal([bufnr, bufnr + 1, bufnr + 2, bufnr + 3, bufnr + 4], buffers) diff --git a/src/nvim/testdir/test_curswant.vim b/src/nvim/testdir/test_curswant.vim new file mode 100644 index 0000000000..e54cd4b280 --- /dev/null +++ b/src/nvim/testdir/test_curswant.vim @@ -0,0 +1,23 @@ +" Tests for curswant not changing when setting an option + +func Test_curswant() + new + call append(0, ['1234567890', '12345']) + + normal! ggf8j + call assert_equal(7, winsaveview().curswant) + let &tabstop=&tabstop + call assert_equal(4, winsaveview().curswant) + + normal! ggf8j + call assert_equal(7, winsaveview().curswant) + let &timeoutlen=&timeoutlen + call assert_equal(7, winsaveview().curswant) + + normal! ggf8j + call assert_equal(7, winsaveview().curswant) + let &ttimeoutlen=&ttimeoutlen + call assert_equal(7, winsaveview().curswant) + + enew! +endfunc diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim index 5de394de8e..d95b29759e 100644 --- a/src/nvim/testdir/test_diffmode.vim +++ b/src/nvim/testdir/test_diffmode.vim @@ -198,23 +198,366 @@ func Test_diffget_diffput() call assert_fails('diffget', 'E101:') windo diffoff - bwipe! - bwipe! - enew! + %bwipe! +endfunc + +func Test_dp_do_buffer() + e! one + let bn1=bufnr('%') + let l = range(60) + call setline(1, l) + diffthis + + new two + let l[10] = 'one' + let l[20] = 'two' + let l[30] = 'three' + let l[40] = 'four' + let l[50] = 'five' + call setline(1, l) + diffthis + + " dp and do with invalid buffer number. + 11 + call assert_fails('norm 99999dp', 'E102:') + call assert_fails('norm 99999do', 'E102:') + call assert_fails('diffput non_existing_buffer', 'E94:') + call assert_fails('diffget non_existing_buffer', 'E94:') + + " dp and do with valid buffer number. + call assert_equal('one', getline('.')) + exe 'norm ' . bn1 . 'do' + call assert_equal('10', getline('.')) + 21 + call assert_equal('two', getline('.')) + diffget one + call assert_equal('20', getline('.')) + + 31 + exe 'norm ' . bn1 . 'dp' + 41 + diffput one + wincmd w + 31 + call assert_equal('three', getline('.')) + 41 + call assert_equal('four', getline('.')) + + " dp and do with buffer number which is not in diff mode. + new not_in_diff_mode + let bn3=bufnr('%') + wincmd w + 51 + call assert_fails('exe "norm" . bn3 . "dp"', 'E103:') + call assert_fails('exe "norm" . bn3 . "do"', 'E103:') + call assert_fails('diffput not_in_diff_mode', 'E94:') + call assert_fails('diffget not_in_diff_mode', 'E94:') + + windo diffoff + %bwipe! endfunc func Test_diffoff() enew! call setline(1, ['Two', 'Three']) + redraw let normattr = screenattr(1, 1) diffthis botright vert new call setline(1, ['One', '', 'Two', 'Three']) diffthis redraw + call assert_notequal(normattr, screenattr(1, 1)) + diffoff! + redraw + call assert_equal(normattr, screenattr(1, 1)) + bwipe! + bwipe! +endfunc + +func Test_diffopt_icase() + set diffopt=icase,foldcolumn:0 + + e one + call setline(1, ['One', 'Two', 'Three', 'Four']) + redraw + let normattr = screenattr(1, 1) + diffthis + + botright vert new two + call setline(1, ['one', 'TWO', 'Three ', 'Four']) + diffthis + + redraw + call assert_equal(normattr, screenattr(1, 1)) + call assert_equal(normattr, screenattr(2, 1)) + call assert_notequal(normattr, screenattr(3, 1)) + call assert_equal(normattr, screenattr(4, 1)) + diffoff! + %bwipe! + set diffopt& +endfunc + +func Test_diffopt_iwhite() + set diffopt=iwhite,foldcolumn:0 + + e one + " Difference in trailing spaces should be ignored, + " but not other space differences. + call setline(1, ["One \t", 'Two', 'Three', 'Four']) + redraw + let normattr = screenattr(1, 1) + diffthis + + botright vert new two + call setline(1, ["One\t ", "Two\t ", 'Three', ' Four']) + diffthis + redraw call assert_equal(normattr, screenattr(1, 1)) + call assert_equal(normattr, screenattr(2, 1)) + call assert_equal(normattr, screenattr(3, 1)) + call assert_notequal(normattr, screenattr(4, 1)) + + diffoff! + %bwipe! + set diffopt& +endfunc + +func Test_diffopt_context() + enew! + call setline(1, ['1', '2', '3', '4', '5', '6', '7']) + diffthis + new + call setline(1, ['1', '2', '3', '4', '5x', '6', '7']) + diffthis + + set diffopt=context:2 + call assert_equal('+-- 2 lines: 1', foldtextresult(1)) + set diffopt=context:1 + call assert_equal('+-- 3 lines: 1', foldtextresult(1)) + + diffoff! + %bwipe! + set diffopt& +endfunc + +func Test_diffopt_horizontal() + set diffopt=horizontal + diffsplit + + call assert_equal(&columns, winwidth(1)) + call assert_equal(&columns, winwidth(2)) + call assert_equal(&lines, winheight(1) + winheight(2) + 3) + call assert_inrange(0, 1, winheight(1) - winheight(2)) + + set diffopt& + diffoff! + %bwipe +endfunc + +func Test_diffopt_vertical() + set diffopt=vertical + diffsplit + + call assert_equal(&lines - 2, winheight(1)) + call assert_equal(&lines - 2, winheight(2)) + call assert_equal(&columns, winwidth(1) + winwidth(2) + 1) + call assert_inrange(0, 1, winwidth(1) - winwidth(2)) + + set diffopt& + diffoff! + %bwipe +endfunc + +func Test_diffoff_hidden() + set diffopt=filler,foldcolumn:0 + e! one + call setline(1, ['Two', 'Three']) + redraw + let normattr = screenattr(1, 1) + diffthis + botright vert new two + call setline(1, ['One', 'Four']) + diffthis + redraw + call assert_notequal(normattr, screenattr(1, 1)) + set hidden + close + redraw + " diffing with hidden buffer two + call assert_notequal(normattr, screenattr(1, 1)) + diffoff + redraw + call assert_equal(normattr, screenattr(1, 1)) + diffthis + redraw + " still diffing with hidden buffer two + call assert_notequal(normattr, screenattr(1, 1)) + diffoff! + redraw + call assert_equal(normattr, screenattr(1, 1)) + diffthis + redraw + " no longer diffing with hidden buffer two + call assert_equal(normattr, screenattr(1, 1)) + + bwipe! + bwipe! + set hidden& diffopt& +endfunc + +func Test_setting_cursor() + new Xtest1 + put =range(1,90) + wq + new Xtest2 + put =range(1,100) + wq + + tabe Xtest2 + $ + diffsp Xtest1 + tabclose + + call delete('Xtest1') + call delete('Xtest2') +endfunc + +func Test_diff_move_to() + new + call setline(1, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) + diffthis + vnew + call setline(1, [1, '2x', 3, 4, 4, 5, '6x', 7, '8x', 9, '10x']) + diffthis + norm ]c + call assert_equal(2, line('.')) + norm 3]c + call assert_equal(9, line('.')) + norm 10]c + call assert_equal(11, line('.')) + norm [c + call assert_equal(9, line('.')) + norm 2[c + call assert_equal(5, line('.')) + norm 10[c + call assert_equal(2, line('.')) + %bwipe! +endfunc + +func Test_diffexpr() + if !executable('diff') + return + endif + + func DiffExpr() + silent exe '!diff ' . v:fname_in . ' ' . v:fname_new . '>' . v:fname_out + endfunc + set diffexpr=DiffExpr() + set diffopt=foldcolumn:0 + + enew! + call setline(1, ['one', 'two', 'three']) + redraw + let normattr = screenattr(1, 1) + diffthis + + botright vert new + call setline(1, ['one', 'two', 'three.']) + diffthis + + redraw + call assert_equal(normattr, screenattr(1, 1)) + call assert_equal(normattr, screenattr(2, 1)) + call assert_notequal(normattr, screenattr(3, 1)) + + diffoff! + %bwipe! + set diffexpr& diffopt& +endfunc + +func Test_diffpatch() + " The patch program on MS-Windows may fail or hang. + if !executable('patch') || !has('unix') + return + endif + new + insert +*************** +*** 1,3 **** + 1 +! 2 + 3 +--- 1,4 ---- + 1 +! 2x + 3 ++ 4 +. + saveas Xpatch + bwipe! + new + call assert_fails('diffpatch Xpatch', 'E816:') + + for name in ['Xpatch', 'Xpatch$HOME', 'Xpa''tch'] + call setline(1, ['1', '2', '3']) + if name != 'Xpatch' + call rename('Xpatch', name) + endif + exe 'diffpatch ' . escape(name, '$') + call assert_equal(['1', '2x', '3', '4'], getline(1, '$')) + if name != 'Xpatch' + call rename(name, 'Xpatch') + endif + bwipe! + endfor + + call delete('Xpatch') + bwipe! +endfunc + +func Test_diff_too_many_buffers() + for i in range(1, 8) + exe "new Xtest" . i + diffthis + endfor + new Xtest9 + call assert_fails('diffthis', 'E96:') + %bwipe! +endfunc + +func Test_diff_nomodifiable() + new + call setline(1, [1, 2, 3, 4]) + setl nomodifiable + diffthis + vnew + call setline(1, ['1x', 2, 3, 3, 4]) + diffthis + call assert_fails('norm dp', 'E793:') + setl nomodifiable + call assert_fails('norm do', 'E21:') + %bwipe! +endfunc + +func Test_diff_lastline() + enew! + only! + call setline(1, ['This is a ', 'line with five ', 'rows']) + diffthis + botright vert new + call setline(1, ['This is', 'a line with ', 'four rows']) + diffthis + 1 + call feedkeys("Je a\<CR>", 'tx') + call feedkeys("Je a\<CR>", 'tx') + let w1lines = winline() + wincmd w + $ + let w2lines = winline() + call assert_equal(w2lines, w1lines) bwipe! bwipe! endfunc diff --git a/src/nvim/testdir/test_display.vim b/src/nvim/testdir/test_display.vim new file mode 100644 index 0000000000..0ed672d577 --- /dev/null +++ b/src/nvim/testdir/test_display.vim @@ -0,0 +1,71 @@ +" Test for displaying stuff + +" Nvim: `:set term` is not supported. +" if !has('gui_running') && has('unix') +" set term=ansi +" endif + +source view_util.vim + +func! Test_display_foldcolumn() + if !has("folding") + return + endif + new + vnew + vert resize 25 + call assert_equal(25, winwidth(winnr())) + set isprint=@ + + 1put='e more noise blah blah more stuff here' + + let expect = [ + \ "e more noise blah blah<82", + \ "> more stuff here " + \ ] + + call cursor(2, 1) + norm! zt + let lines=ScreenLines([1,2], winwidth(0)) + call assert_equal(expect, lines) + set fdc=2 + let lines=ScreenLines([1,2], winwidth(0)) + let expect = [ + \ " e more noise blah blah<", + \ " 82> more stuff here " + \ ] + call assert_equal(expect, lines) + + quit! + quit! +endfunc + +func! Test_display_foldtext_mbyte() + if !has("folding") || !has("multi_byte") + return + endif + call NewWindow(10, 40) + call append(0, range(1,20)) + exe "set foldmethod=manual foldtext=foldtext() fillchars=fold:\u2500,vert:\u2502 fdc=2" + call cursor(2, 1) + norm! zf13G + let lines=ScreenLines([1,3], winwidth(0)+1) + let expect=[ + \ " 1 \u2502", + \ "+ +-- 12 lines: 2". repeat("\u2500", 23). "\u2502", + \ " 14 \u2502", + \ ] + call assert_equal(expect, lines) + + set fillchars=fold:-,vert:\| + let lines=ScreenLines([1,3], winwidth(0)+1) + let expect=[ + \ " 1 |", + \ "+ +-- 12 lines: 2". repeat("-", 23). "|", + \ " 14 |", + \ ] + call assert_equal(expect, lines) + + set foldtext& fillchars& foldmethod& fdc& + bw! +endfunc diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim new file mode 100644 index 0000000000..8f815478c2 --- /dev/null +++ b/src/nvim/testdir/test_edit.vim @@ -0,0 +1,1325 @@ +" Test for edit functions +" +if exists("+t_kD") + let &t_kD="[3;*~" +endif + +" Needed for testing basic rightleft: Test_edit_rightleft +source view_util.vim + +" Needs to come first until the bug in getchar() is +" fixed: https://groups.google.com/d/msg/vim_dev/fXL9yme4H4c/bOR-U6_bAQAJ +func! Test_edit_00b() + new + call setline(1, ['abc ']) + inoreabbr <buffer> h here some more + call cursor(1, 4) + " <c-l> expands the abbreviation and ends insertmode + call feedkeys(":set im\<cr> h\<c-l>:set noim\<cr>", 'tix') + call assert_equal(['abc here some more '], getline(1,'$')) + iunabbr <buffer> h + bw! +endfunc + +func! Test_edit_01() + " set for Travis CI? + " set nocp noesckeys + new + " 1) empty buffer + call assert_equal([''], getline(1,'$')) + " 2) delete in an empty line + call feedkeys("i\<del>\<esc>", 'tnix') + call assert_equal([''], getline(1,'$')) + %d + " 3) delete one character + call setline(1, 'a') + call feedkeys("i\<del>\<esc>", 'tnix') + call assert_equal([''], getline(1,'$')) + %d + " 4) delete a multibyte character + if has("multi_byte") + call setline(1, "\u0401") + call feedkeys("i\<del>\<esc>", 'tnix') + call assert_equal([''], getline(1,'$')) + %d + endif + " 5.1) delete linebreak with 'bs' option containing eol + let _bs=&bs + set bs=eol + call setline(1, ["abc def", "ghi jkl"]) + call cursor(1, 1) + call feedkeys("A\<del>\<esc>", 'tnix') + call assert_equal(['abc defghi jkl'], getline(1, 2)) + %d + " 5.2) delete linebreak with backspace option w/out eol + set bs= + call setline(1, ["abc def", "ghi jkl"]) + call cursor(1, 1) + call feedkeys("A\<del>\<esc>", 'tnix') + call assert_equal(["abc def", "ghi jkl"], getline(1, 2)) + let &bs=_bs + bw! +endfunc + +func! Test_edit_02() + " Change cursor position in InsertCharPre command + new + call setline(1, 'abc') + call cursor(1, 1) + fu! DoIt(...) + call cursor(1, 4) + if len(a:000) + let v:char=a:1 + endif + endfu + au InsertCharPre <buffer> :call DoIt('y') + call feedkeys("ix\<esc>", 'tnix') + call assert_equal(['abcy'], getline(1, '$')) + " Setting <Enter> in InsertCharPre + au! InsertCharPre <buffer> :call DoIt("\n") + call setline(1, 'abc') + call cursor(1, 1) + call feedkeys("ix\<esc>", 'tnix') + call assert_equal(['abc', ''], getline(1, '$')) + %d + au! InsertCharPre + " Change cursor position in InsertEnter command + " 1) when setting v:char, keeps changed cursor position + au! InsertEnter <buffer> :call DoIt('y') + call setline(1, 'abc') + call cursor(1, 1) + call feedkeys("ix\<esc>", 'tnix') + call assert_equal(['abxc'], getline(1, '$')) + " 2) when not setting v:char, restores changed cursor position + au! InsertEnter <buffer> :call DoIt() + call setline(1, 'abc') + call cursor(1, 1) + call feedkeys("ix\<esc>", 'tnix') + call assert_equal(['xabc'], getline(1, '$')) + au! InsertEnter + delfu DoIt + bw! +endfunc + +func! Test_edit_03() + " Change cursor after <c-o> command to end of line + new + call setline(1, 'abc') + call cursor(1, 1) + call feedkeys("i\<c-o>$y\<esc>", 'tnix') + call assert_equal(['abcy'], getline(1, '$')) + %d + call setline(1, 'abc') + call cursor(1, 1) + call feedkeys("i\<c-o>80|y\<esc>", 'tnix') + call assert_equal(['abcy'], getline(1, '$')) + %d + call setline(1, 'abc') + call feedkeys("Ad\<c-o>:s/$/efg/\<cr>hij", 'tnix') + call assert_equal(['hijabcdefg'], getline(1, '$')) + bw! +endfunc + +func! Test_edit_04() + " test for :stopinsert + new + call setline(1, 'abc') + call cursor(1, 1) + call feedkeys("i\<c-o>:stopinsert\<cr>$", 'tnix') + call feedkeys("aX\<esc>", 'tnix') + call assert_equal(['abcX'], getline(1, '$')) + %d + bw! +endfunc + +func! Test_edit_05() + " test for folds being opened + new + call setline(1, ['abcX', 'abcX', 'zzzZ']) + call cursor(1, 1) + set foldmethod=manual foldopen+=insert + " create fold for those two lines + norm! Vjzf + call feedkeys("$ay\<esc>", 'tnix') + call assert_equal(['abcXy', 'abcX', 'zzzZ'], getline(1, '$')) + %d + call setline(1, ['abcX', 'abcX', 'zzzZ']) + call cursor(1, 1) + set foldmethod=manual foldopen-=insert + " create fold for those two lines + norm! Vjzf + call feedkeys("$ay\<esc>", 'tnix') + call assert_equal(['abcXy', 'abcX', 'zzzZ'], getline(1, '$')) + %d + bw! +endfunc + +func! Test_edit_06() + " Test in diff mode + if !has("diff") || !executable("diff") + return + endif + new + call setline(1, ['abc', 'xxx', 'yyy']) + vnew + call setline(1, ['abc', 'zzz', 'xxx', 'yyy']) + wincmd p + diffthis + wincmd p + diffthis + wincmd p + call cursor(2, 1) + norm! zt + call feedkeys("Ozzz\<esc>", 'tnix') + call assert_equal(['abc', 'zzz', 'xxx', 'yyy'], getline(1,'$')) + bw! + bw! +endfunc + +func! Test_edit_07() + " 1) Test with completion <c-l> when popupmenu is visible + new + call setline(1, 'J') + + func! ListMonths() + call complete(col('.')-1, ['January', 'February', 'March', + \ 'April', 'May', 'June', 'July', 'August', 'September', + \ 'October', 'November', 'December']) + return '' + endfunc + inoremap <buffer> <F5> <C-R>=ListMonths()<CR> + + call feedkeys("A\<f5>\<c-p>". repeat("\<down>", 6)."\<c-l>\<down>\<c-l>\<cr>", 'tx') + call assert_equal(['July'], getline(1,'$')) + " 1) Test completion when InsertCharPre kicks in + %d + call setline(1, 'J') + fu! DoIt() + if v:char=='u' + let v:char='an' + endif + endfu + au InsertCharPre <buffer> :call DoIt() + call feedkeys("A\<f5>\<c-p>u\<cr>\<c-l>\<cr>", 'tx') + call assert_equal(["Jan\<c-l>",''], getline(1,'$')) + %d + call setline(1, 'J') + call feedkeys("A\<f5>\<c-p>u\<down>\<c-l>\<cr>", 'tx') + call assert_equal(["January"], getline(1,'$')) + + delfu ListMonths + delfu DoIt + iunmap <buffer> <f5> + bw! +endfunc + +func! Test_edit_08() + throw 'skipped: moved to test/functional/legacy/edit_spec.lua' + " reset insertmode from i_ctrl-r_= + let g:bufnr = bufnr('%') + new + call setline(1, ['abc']) + call cursor(1, 4) + call feedkeys(":set im\<cr>ZZZ\<c-r>=setbufvar(g:bufnr,'&im', 0)\<cr>",'tnix') + call assert_equal(['abZZZc'], getline(1,'$')) + call assert_equal([0, 1, 1, 0], getpos('.')) + call assert_false(0, '&im') + bw! + unlet g:bufnr +endfunc + +func! Test_edit_09() + " test i_CTRL-\ combinations + new + call setline(1, ['abc', 'def', 'ghi']) + call cursor(1, 1) + " 1) CTRL-\ CTLR-N + call feedkeys(":set im\<cr>\<c-\>\<c-n>ccABC\<c-l>", 'txin') + call assert_equal(['ABC', 'def', 'ghi'], getline(1,'$')) + call setline(1, ['ABC', 'def', 'ghi']) + " 2) CTRL-\ CTLR-G + call feedkeys("j0\<c-\>\<c-g>ZZZ\<cr>\<c-l>", 'txin') + call assert_equal(['ABC', 'ZZZ', 'def', 'ghi'], getline(1,'$')) + call feedkeys("I\<c-\>\<c-g>YYY\<c-l>", 'txin') + call assert_equal(['ABC', 'ZZZ', 'YYYdef', 'ghi'], getline(1,'$')) + set noinsertmode + " 3) CTRL-\ CTRL-O + call setline(1, ['ABC', 'ZZZ', 'def', 'ghi']) + call cursor(1, 1) + call feedkeys("A\<c-o>ix", 'txin') + call assert_equal(['ABxC', 'ZZZ', 'def', 'ghi'], getline(1,'$')) + call feedkeys("A\<c-\>\<c-o>ix", 'txin') + call assert_equal(['ABxCx', 'ZZZ', 'def', 'ghi'], getline(1,'$')) + " 4) CTRL-\ a (should be inserted literally, not special after <c-\> + call setline(1, ['ABC', 'ZZZ', 'def', 'ghi']) + call cursor(1, 1) + call feedkeys("A\<c-\>a", 'txin') + call assert_equal(["ABC\<c-\>a", 'ZZZ', 'def', 'ghi'], getline(1, '$')) + bw! +endfunc + +func! Test_edit_10() + " Test for starting selectmode + new + set selectmode=key keymodel=startsel + call setline(1, ['abc', 'def', 'ghi']) + call cursor(1, 4) + call feedkeys("A\<s-home>start\<esc>", 'txin') + call assert_equal(['startdef', 'ghi'], getline(1, '$')) + set selectmode= keymodel= + bw! +endfunc + +func! Test_edit_11() + " Test that indenting kicks in + new + set cindent + call setline(1, ['{', '', '']) + call cursor(2, 1) + call feedkeys("i\<c-f>int c;\<esc>", 'tnix') + call cursor(3, 1) + call feedkeys("i/* comment */", 'tnix') + call assert_equal(['{', "\<tab>int c;", "/* comment */"], getline(1, '$')) + " added changed cindentkeys slightly + set cindent cinkeys+=*/ + call setline(1, ['{', '', '']) + call cursor(2, 1) + call feedkeys("i\<c-f>int c;\<esc>", 'tnix') + call cursor(3, 1) + call feedkeys("i/* comment */", 'tnix') + call assert_equal(['{', "\<tab>int c;", "\<tab>/* comment */"], getline(1, '$')) + set cindent cinkeys+==end + call feedkeys("oend\<cr>\<esc>", 'tnix') + call assert_equal(['{', "\<tab>int c;", "\<tab>/* comment */", "\tend", ''], getline(1, '$')) + set cinkeys-==end + %d + " Use indentexpr instead of cindenting + func! Do_Indent() + if v:lnum == 3 + return 3*shiftwidth() + else + return 2*shiftwidth() + endif + endfunc + setl indentexpr=Do_Indent() indentkeys+=*/ + call setline(1, ['{', '', '']) + call cursor(2, 1) + call feedkeys("i\<c-f>int c;\<esc>", 'tnix') + call cursor(3, 1) + call feedkeys("i/* comment */", 'tnix') + call assert_equal(['{', "\<tab>\<tab>int c;", "\<tab>\<tab>\<tab>/* comment */"], getline(1, '$')) + set cinkeys&vim indentkeys&vim + set nocindent indentexpr= + delfu Do_Indent + bw! +endfunc + +func! Test_edit_12() + " Test changing indent in replace mode + new + call setline(1, ["\tabc", "\tdef"]) + call cursor(2, 4) + call feedkeys("R^\<c-d>", 'tnix') + call assert_equal(["\tabc", "def"], getline(1, '$')) + call assert_equal([0, 2, 2, 0], getpos('.')) + %d + call setline(1, ["\tabc", "\t\tdef"]) + call cursor(2, 2) + call feedkeys("R^\<c-d>", 'tnix') + call assert_equal(["\tabc", "def"], getline(1, '$')) + call assert_equal([0, 2, 1, 0], getpos('.')) + %d + call setline(1, ["\tabc", "\t\tdef"]) + call cursor(2, 2) + call feedkeys("R\<c-t>", 'tnix') + call assert_equal(["\tabc", "\t\t\tdef"], getline(1, '$')) + call assert_equal([0, 2, 2, 0], getpos('.')) + bw! + 10vnew + call setline(1, ["\tabc", "\t\tdef"]) + call cursor(2, 2) + call feedkeys("R\<c-t>", 'tnix') + call assert_equal(["\tabc", "\t\t\tdef"], getline(1, '$')) + call assert_equal([0, 2, 2, 0], getpos('.')) + %d + set sw=4 + call setline(1, ["\tabc", "\t\tdef"]) + call cursor(2, 2) + call feedkeys("R\<c-t>\<c-t>", 'tnix') + call assert_equal(["\tabc", "\t\t\tdef"], getline(1, '$')) + call assert_equal([0, 2, 2, 0], getpos('.')) + %d + call setline(1, ["\tabc", "\t\tdef"]) + call cursor(2, 2) + call feedkeys("R\<c-t>\<c-t>", 'tnix') + call assert_equal(["\tabc", "\t\t\tdef"], getline(1, '$')) + call assert_equal([0, 2, 2, 0], getpos('.')) + set et + set sw& et& + %d + call setline(1, ["\t/*"]) + set formatoptions=croql + call cursor(1, 3) + call feedkeys("A\<cr>\<cr>/", 'tnix') + call assert_equal(["\t/*", " *", " */"], getline(1, '$')) + set formatoptions& + bw! +endfunc + +func! Test_edit_13() + " Test smartindenting + if exists("+smartindent") + new + set smartindent autoindent + call setline(1, ["\tabc"]) + call feedkeys("A {\<cr>more\<cr>}\<esc>", 'tnix') + call assert_equal(["\tabc {", "\t\tmore", "\t}"], getline(1, '$')) + set smartindent& autoindent& + bw! + endif +endfunc + +func! Test_edit_CR() + " Test for <CR> in insert mode + " basically only in quickfix mode ist tested, the rest + " has been taken care of by other tests + if !has("quickfix") + return + endif + botright new + call writefile(range(1, 10), 'Xqflist.txt') + call setqflist([{'filename': 'Xqflist.txt', 'lnum': 2}]) + copen + set modifiable + call feedkeys("A\<cr>", 'tnix') + call assert_equal('Xqflist.txt', bufname('')) + call assert_equal(2, line('.')) + cclose + botright new + call setloclist(0, [{'filename': 'Xqflist.txt', 'lnum': 10}]) + lopen + set modifiable + call feedkeys("A\<cr>", 'tnix') + call assert_equal('Xqflist.txt', bufname('')) + call assert_equal(10, line('.')) + call feedkeys("A\<Enter>", 'tnix') + call feedkeys("A\<kEnter>", 'tnix') + call feedkeys("A\n", 'tnix') + call feedkeys("A\r", 'tnix') + call assert_equal(map(range(1, 10), 'string(v:val)') + ['', '', '', ''], getline(1, '$')) + bw! + lclose + call delete('Xqflist.txt') +endfunc + +func! Test_edit_CTRL_() + " disabled for Windows builds, why? + if !has("multi_byte") || !has("rightleft") || has("win32") + return + endif + let _encoding=&encoding + set encoding=utf-8 + " Test for CTRL-_ + new + call setline(1, ['abc']) + call cursor(1, 1) + call feedkeys("i\<c-_>xyz\<esc>", 'tnix') + call assert_equal(["\<C-_>xyzabc"], getline(1, '$')) + call assert_false(&revins) + set ari + call setline(1, ['abc']) + call cursor(1, 1) + call feedkeys("i\<c-_>xyz\<esc>", 'tnix') + call assert_equal(["æèñabc"], getline(1, '$')) + call assert_true(&revins) + call setline(1, ['abc']) + call cursor(1, 1) + call feedkeys("i\<c-_>xyz\<esc>", 'tnix') + call assert_equal(["xyzabc"], getline(1, '$')) + call assert_false(&revins) + set noari + let &encoding=_encoding + bw! +endfunc + +" needs to come first, to have the @. register empty +func! Test_edit_00a_CTRL_A() + " Test pressing CTRL-A + new + call setline(1, repeat([''], 5)) + call cursor(1, 1) + try + call feedkeys("A\<NUL>", 'tnix') + catch /^Vim\%((\a\+)\)\=:E29/ + call assert_true(1, 'E29 error caught') + endtry + call cursor(1, 1) + call feedkeys("Afoobar \<esc>", 'tnix') + call cursor(2, 1) + call feedkeys("A\<c-a>more\<esc>", 'tnix') + call cursor(3, 1) + call feedkeys("A\<NUL>and more\<esc>", 'tnix') + call assert_equal(['foobar ', 'foobar more', 'foobar morend more', '', ''], getline(1, '$')) + bw! +endfunc + +func! Test_edit_CTRL_EY() + " Ctrl-E/ Ctrl-Y in insert mode completion to scroll + 10new + call setline(1, range(1, 100)) + call cursor(30, 1) + norm! z. + call feedkeys("A\<c-x>\<c-e>\<c-e>\<c-e>\<c-e>\<c-e>", 'tnix') + call assert_equal(30, winsaveview()['topline']) + call assert_equal([0, 30, 2, 0], getpos('.')) + call feedkeys("A\<c-x>\<c-e>\<c-e>\<c-e>\<c-e>\<c-e>", 'tnix') + call feedkeys("A\<c-x>".repeat("\<c-y>", 10), 'tnix') + call assert_equal(21, winsaveview()['topline']) + call assert_equal([0, 30, 2, 0], getpos('.')) + bw! +endfunc + +func! Test_edit_CTRL_G() + new + call setline(1, ['foobar', 'foobar', 'foobar']) + call cursor(2, 4) + call feedkeys("ioooooooo\<c-g>k\<c-r>.\<esc>", 'tnix') + call assert_equal(['foooooooooobar', 'foooooooooobar', 'foobar'], getline(1, '$')) + call assert_equal([0, 1, 11, 0], getpos('.')) + call feedkeys("i\<c-g>k\<esc>", 'tnix') + call assert_equal([0, 1, 10, 0], getpos('.')) + call cursor(2, 4) + call feedkeys("i\<c-g>jzzzz\<esc>", 'tnix') + call assert_equal(['foooooooooobar', 'foooooooooobar', 'foozzzzbar'], getline(1, '$')) + call assert_equal([0, 3, 7, 0], getpos('.')) + call feedkeys("i\<c-g>j\<esc>", 'tnix') + call assert_equal([0, 3, 6, 0], getpos('.')) + bw! +endfunc + +func! Test_edit_CTRL_I() + " Tab in completion mode + let path=expand("%:p:h") + new + call setline(1, [path."/", '']) + call feedkeys("Arunt\<c-x>\<c-f>\<tab>\<cr>\<esc>", 'tnix') + call assert_match('runtest\.vim', getline(1)) + %d + call writefile(['one', 'two', 'three'], 'Xinclude.txt') + let include='#include Xinclude.txt' + call setline(1, [include, '']) + call cursor(2, 1) + call feedkeys("A\<c-x>\<tab>\<cr>\<esc>", 'tnix') + call assert_equal([include, 'one', ''], getline(1, '$')) + call feedkeys("2ggC\<c-x>\<tab>\<down>\<cr>\<esc>", 'tnix') + call assert_equal([include, 'two', ''], getline(1, '$')) + call feedkeys("2ggC\<c-x>\<tab>\<down>\<down>\<cr>\<esc>", 'tnix') + call assert_equal([include, 'three', ''], getline(1, '$')) + call feedkeys("2ggC\<c-x>\<tab>\<down>\<down>\<down>\<cr>\<esc>", 'tnix') + call assert_equal([include, '', ''], getline(1, '$')) + call delete("Xinclude.txt") + bw! +endfunc + +func! Test_edit_CTRL_K() + " Test pressing CTRL-K (basically only dictionary completion and digraphs + " the rest is already covered + call writefile(['A', 'AA', 'AAA', 'AAAA'], 'Xdictionary.txt') + set dictionary=Xdictionary.txt + new + call setline(1, 'A') + call cursor(1, 1) + call feedkeys("A\<c-x>\<c-k>\<cr>\<esc>", 'tnix') + call assert_equal(['AA', ''], getline(1, '$')) + %d + call setline(1, 'A') + call cursor(1, 1) + call feedkeys("A\<c-x>\<c-k>\<down>\<cr>\<esc>", 'tnix') + call assert_equal(['AAA'], getline(1, '$')) + %d + call setline(1, 'A') + call cursor(1, 1) + call feedkeys("A\<c-x>\<c-k>\<down>\<down>\<cr>\<esc>", 'tnix') + call assert_equal(['AAAA'], getline(1, '$')) + %d + call setline(1, 'A') + call cursor(1, 1) + call feedkeys("A\<c-x>\<c-k>\<down>\<down>\<down>\<cr>\<esc>", 'tnix') + call assert_equal(['A'], getline(1, '$')) + %d + call setline(1, 'A') + call cursor(1, 1) + call feedkeys("A\<c-x>\<c-k>\<down>\<down>\<down>\<down>\<cr>\<esc>", 'tnix') + call assert_equal(['AA'], getline(1, '$')) + + " press an unexecpted key after dictionary completion + %d + call setline(1, 'A') + call cursor(1, 1) + call feedkeys("A\<c-x>\<c-k>\<c-]>\<cr>\<esc>", 'tnix') + call assert_equal(['AA', ''], getline(1, '$')) + %d + call setline(1, 'A') + call cursor(1, 1) + call feedkeys("A\<c-x>\<c-k>\<c-s>\<cr>\<esc>", 'tnix') + call assert_equal(["AA\<c-s>", ''], getline(1, '$')) + %d + call setline(1, 'A') + call cursor(1, 1) + call feedkeys("A\<c-x>\<c-k>\<c-f>\<cr>\<esc>", 'tnix') + call assert_equal(["AA\<c-f>", ''], getline(1, '$')) + + set dictionary= + %d + call setline(1, 'A') + call cursor(1, 1) + let v:testing = 1 + try + call feedkeys("A\<c-x>\<c-k>\<esc>", 'tnix') + catch + " error sleeps 2 seconds, when v:testing is not set + let v:testing = 0 + endtry + call delete('Xdictionary.txt') + + if has("multi_byte") && !has("nvim") + call test_override("char_avail", 1) + set showcmd + %d + call feedkeys("A\<c-k>a:\<esc>", 'tnix') + call assert_equal(['ä'], getline(1, '$')) + call test_override("char_avail", 0) + set noshowcmd + endif + bw! +endfunc + +func! Test_edit_CTRL_L() + " Test Ctrl-X Ctrl-L (line completion) + new + set complete=. + call setline(1, ['one', 'two', 'three', '', '', '', '']) + call cursor(4, 1) + call feedkeys("A\<c-x>\<c-l>\<esc>", 'tnix') + call assert_equal(['one', 'two', 'three', 'three', '', '', ''], getline(1, '$')) + call feedkeys("cct\<c-x>\<c-l>\<c-n>\<esc>", 'tnix') + call assert_equal(['one', 'two', 'three', 't', '', '', ''], getline(1, '$')) + call feedkeys("cct\<c-x>\<c-l>\<c-n>\<c-n>\<esc>", 'tnix') + call assert_equal(['one', 'two', 'three', 't', '', '', ''], getline(1, '$')) + call feedkeys("cct\<c-x>\<c-l>\<c-n>\<c-n>\<c-n>\<esc>", 'tnix') + call assert_equal(['one', 'two', 'three', 'two', '', '', ''], getline(1, '$')) + call feedkeys("cct\<c-x>\<c-l>\<c-n>\<c-n>\<c-n>\<c-n>\<esc>", 'tnix') + call assert_equal(['one', 'two', 'three', 'three', '', '', ''], getline(1, '$')) + call feedkeys("cct\<c-x>\<c-l>\<c-p>\<esc>", 'tnix') + call assert_equal(['one', 'two', 'three', 'two', '', '', ''], getline(1, '$')) + call feedkeys("cct\<c-x>\<c-l>\<c-p>\<c-p>\<esc>", 'tnix') + call assert_equal(['one', 'two', 'three', 't', '', '', ''], getline(1, '$')) + call feedkeys("cct\<c-x>\<c-l>\<c-p>\<c-p>\<c-p>\<esc>", 'tnix') + call assert_equal(['one', 'two', 'three', 'three', '', '', ''], getline(1, '$')) + set complete= + call cursor(5, 1) + call feedkeys("A\<c-x>\<c-l>\<c-p>\<c-n>\<esc>", 'tnix') + call assert_equal(['one', 'two', 'three', 'three', "\<c-l>\<c-p>\<c-n>", '', ''], getline(1, '$')) + set complete& + %d + if has("conceal") && has("syntax") && !has("nvim") + call setline(1, ['foo', 'bar', 'foobar']) + call test_override("char_avail", 1) + set conceallevel=2 concealcursor=n + syn on + syn match ErrorMsg "^bar" + call matchadd("Conceal", 'oo', 10, -1, {'conceal': 'X'}) + func! DoIt() + let g:change=1 + endfunc + au! TextChangedI <buffer> :call DoIt() + + call cursor(2, 1) + call assert_false(exists("g:change")) + call feedkeys("A \<esc>", 'tnix') + call assert_equal(['foo', 'bar ', 'foobar'], getline(1, '$')) + call assert_equal(1, g:change) + + call test_override("char_avail", 0) + call clearmatches() + syn off + au! TextChangedI + delfu DoIt + unlet! g:change + endif + bw! +endfunc + +func! Test_edit_CTRL_N() + " Check keyword completion + new + set complete=. + call setline(1, ['INFER', 'loWER', '', '', ]) + call cursor(3, 1) + call feedkeys("Ai\<c-n>\<cr>\<esc>", "tnix") + call feedkeys("ILO\<c-n>\<cr>\<esc>", 'tnix') + call assert_equal(['INFER', 'loWER', 'i', 'LO', '', ''], getline(1, '$')) + %d + call setline(1, ['INFER', 'loWER', '', '', ]) + call cursor(3, 1) + set ignorecase infercase + call feedkeys("Ii\<c-n>\<cr>\<esc>", "tnix") + call feedkeys("ILO\<c-n>\<cr>\<esc>", 'tnix') + call assert_equal(['INFER', 'loWER', 'infer', 'LOWER', '', ''], getline(1, '$')) + + set noignorecase noinfercase complete& + bw! +endfunc + +func! Test_edit_CTRL_O() + " Check for CTRL-O in insert mode + new + inoreabbr <buffer> h here some more + call setline(1, ['abc', 'def']) + call cursor(1, 1) + " Ctrl-O after an abbreviation + exe "norm A h\<c-o>:set nu\<cr> text" + call assert_equal(['abc here some more text', 'def'], getline(1, '$')) + call assert_true(&nu) + set nonu + iunabbr <buffer> h + " Ctrl-O at end of line with 've'=onemore + call cursor(1, 1) + call feedkeys("A\<c-o>:let g:a=getpos('.')\<cr>\<esc>", 'tnix') + call assert_equal([0, 1, 23, 0], g:a) + call cursor(1, 1) + set ve=onemore + call feedkeys("A\<c-o>:let g:a=getpos('.')\<cr>\<esc>", 'tnix') + call assert_equal([0, 1, 24, 0], g:a) + set ve= + unlet! g:a + bw! +endfunc + +func! Test_edit_CTRL_R() + throw 'skipped: Nvim does not support test_override()' + " Insert Register + new + call test_override("ALL", 1) + set showcmd + call feedkeys("AFOOBAR eins zwei\<esc>", 'tnix') + call feedkeys("O\<c-r>.", 'tnix') + call feedkeys("O\<c-r>=10*500\<cr>\<esc>", 'tnix') + call feedkeys("O\<c-r>=getreg('=', 1)\<cr>\<esc>", 'tnix') + call assert_equal(["getreg('=', 1)", '5000', "FOOBAR eins zwei", "FOOBAR eins zwei"], getline(1, '$')) + call test_override("ALL", 0) + set noshowcmd + bw! +endfunc + +func! Test_edit_CTRL_S() + " Test pressing CTRL-S (basically only spellfile completion) + " the rest is already covered + new + if !has("spell") + call setline(1, 'vim') + call feedkeys("A\<c-x>ss\<cr>\<esc>", 'tnix') + call assert_equal(['vims', ''], getline(1, '$')) + bw! + return + endif + call setline(1, 'vim') + " spell option not yet set + try + call feedkeys("A\<c-x>\<c-s>\<cr>\<esc>", 'tnix') + catch /^Vim\%((\a\+)\)\=:E756/ + call assert_true(1, 'error caught') + endtry + call assert_equal(['vim', ''], getline(1, '$')) + %d + setl spell spelllang=en + call setline(1, 'vim') + call cursor(1, 1) + call feedkeys("A\<c-x>\<c-s>\<cr>\<esc>", 'tnix') + call assert_equal(['Vim', ''], getline(1, '$')) + %d + call setline(1, 'vim') + call cursor(1, 1) + call feedkeys("A\<c-x>\<c-s>\<down>\<cr>\<esc>", 'tnix') + call assert_equal(['Aim'], getline(1, '$')) + %d + call setline(1, 'vim') + call cursor(1, 1) + call feedkeys("A\<c-x>\<c-s>\<c-p>\<cr>\<esc>", 'tnix') + call assert_equal(['vim', ''], getline(1, '$')) + %d + " empty buffer + call cursor(1, 1) + call feedkeys("A\<c-x>\<c-s>\<c-p>\<cr>\<esc>", 'tnix') + call assert_equal(['', ''], getline(1, '$')) + setl nospell + bw! +endfunc + +func! Test_edit_CTRL_T() + " Check for CTRL-T and CTRL-X CTRL-T in insert mode + " 1) increase indent + new + call setline(1, "abc") + call cursor(1, 1) + call feedkeys("A\<c-t>xyz", 'tnix') + call assert_equal(["\<tab>abcxyz"], getline(1, '$')) + " 2) also when paste option is set + set paste + call setline(1, "abc") + call cursor(1, 1) + call feedkeys("A\<c-t>xyz", 'tnix') + call assert_equal(["\<tab>abcxyz"], getline(1, '$')) + set nopaste + " CTRL-X CTRL-T (thesaurus complete) + call writefile(['angry furious mad enraged'], 'Xthesaurus') + set thesaurus=Xthesaurus + call setline(1, 'mad') + call cursor(1, 1) + call feedkeys("A\<c-x>\<c-t>\<cr>\<esc>", 'tnix') + call assert_equal(['mad', ''], getline(1, '$')) + %d + call setline(1, 'mad') + call cursor(1, 1) + call feedkeys("A\<c-x>\<c-t>\<c-n>\<cr>\<esc>", 'tnix') + call assert_equal(['angry', ''], getline(1, '$')) + %d + call setline(1, 'mad') + call cursor(1, 1) + call feedkeys("A\<c-x>\<c-t>\<c-n>\<c-n>\<cr>\<esc>", 'tnix') + call assert_equal(['furious', ''], getline(1, '$')) + %d + call setline(1, 'mad') + call cursor(1, 1) + call feedkeys("A\<c-x>\<c-t>\<c-n>\<c-n>\<c-n>\<cr>\<esc>", 'tnix') + call assert_equal(['enraged', ''], getline(1, '$')) + %d + call setline(1, 'mad') + call cursor(1, 1) + call feedkeys("A\<c-x>\<c-t>\<c-n>\<c-n>\<c-n>\<c-n>\<cr>\<esc>", 'tnix') + call assert_equal(['mad', ''], getline(1, '$')) + %d + call setline(1, 'mad') + call cursor(1, 1) + call feedkeys("A\<c-x>\<c-t>\<c-n>\<c-n>\<c-n>\<c-n>\<c-n>\<cr>\<esc>", 'tnix') + call assert_equal(['mad', ''], getline(1, '$')) + " Using <c-p> <c-n> when 'complete' is empty + set complete= + %d + call setline(1, 'mad') + call cursor(1, 1) + call feedkeys("A\<c-x>\<c-t>\<c-n>\<cr>\<esc>", 'tnix') + call assert_equal(['angry', ''], getline(1, '$')) + %d + call setline(1, 'mad') + call cursor(1, 1) + call feedkeys("A\<c-x>\<c-t>\<c-p>\<cr>\<esc>", 'tnix') + call assert_equal(['mad', ''], getline(1, '$')) + set complete& + + set thesaurus= + %d + call setline(1, 'mad') + call cursor(1, 1) + let v:testing = 1 + try + call feedkeys("A\<c-x>\<c-t>\<esc>", 'tnix') + catch + " error sleeps 2 seconds, when v:testing is not set + let v:testing = 0 + endtry + call assert_equal(['mad'], getline(1, '$')) + call delete('Xthesaurus') + bw! +endfunc + +func! Test_edit_CTRL_U() + " Test 'completefunc' + new + " -1, -2 and -3 are special return values + let g:special=0 + fun! CompleteMonths(findstart, base) + if a:findstart + " locate the start of the word + return g:special + else + " find months matching with "a:base" + let res = [] + for m in split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec") + if m =~ '^\c'.a:base + call add(res, {'word': m, 'abbr': m.' Month', 'icase': 0}) + endif + endfor + return {'words': res, 'refresh': 'always'} + endif + endfun + set completefunc=CompleteMonths + call setline(1, ['', '']) + call cursor(1, 1) + call feedkeys("AX\<c-x>\<c-u>\<cr>\<esc>", 'tnix') + call assert_equal(['X', '', ''], getline(1, '$')) + %d + let g:special=-1 + call feedkeys("AX\<c-x>\<c-u>\<cr>\<esc>", 'tnix') + call assert_equal(['XJan', ''], getline(1, '$')) + %d + let g:special=-2 + call feedkeys("AX\<c-x>\<c-u>\<cr>\<esc>", 'tnix') + call assert_equal(['X', ''], getline(1, '$')) + %d + let g:special=-3 + call feedkeys("AX\<c-x>\<c-u>\<cr>\<esc>", 'tnix') + call assert_equal(['X', ''], getline(1, '$')) + %d + let g:special=0 + call feedkeys("AM\<c-x>\<c-u>\<cr>\<esc>", 'tnix') + call assert_equal(['Mar', ''], getline(1, '$')) + %d + call feedkeys("AM\<c-x>\<c-u>\<c-n>\<cr>\<esc>", 'tnix') + call assert_equal(['May', ''], getline(1, '$')) + %d + call feedkeys("AM\<c-x>\<c-u>\<c-n>\<c-n>\<cr>\<esc>", 'tnix') + call assert_equal(['M', ''], getline(1, '$')) + delfu CompleteMonths + %d + try + call feedkeys("A\<c-x>\<c-u>", 'tnix') + call assert_fails(1, 'unknown completion function') + catch /^Vim\%((\a\+)\)\=:E117/ + call assert_true(1, 'E117 error caught') + endtry + set completefunc= + bw! +endfunc + +func! Test_edit_CTRL_Z() + " Ctrl-Z when insertmode is not set inserts it literally + new + call setline(1, 'abc') + call feedkeys("A\<c-z>\<esc>", 'tnix') + call assert_equal(["abc\<c-z>"], getline(1,'$')) + bw! + " TODO: How to Test Ctrl-Z in insert mode, e.g. suspend? +endfunc + +func! Test_edit_DROP() + if !has("dnd") + return + endif + new + call setline(1, ['abc def ghi']) + call cursor(1, 1) + try + call feedkeys("i\<Drop>\<Esc>", 'tnix') + call assert_fails(1, 'Invalid register name') + catch /^Vim\%((\a\+)\)\=:E353/ + call assert_true(1, 'error caught') + endtry + bw! +endfunc + +func! Test_edit_CTRL_V() + throw 'skipped: Nvim does not support test_override()' + if has("ebcdic") + return + endif + new + call setline(1, ['abc']) + call cursor(2, 1) + " force some redraws + set showmode showcmd + "call test_override_char_avail(1) + call test_override('ALL', 1) + call feedkeys("A\<c-v>\<c-n>\<c-v>\<c-l>\<c-v>\<c-b>\<esc>", 'tnix') + call assert_equal(["abc\x0e\x0c\x02"], getline(1, '$')) + + if has("rightleft") && exists("+rl") + set rl + call setline(1, ['abc']) + call cursor(2, 1) + call feedkeys("A\<c-v>\<c-n>\<c-v>\<c-l>\<c-v>\<c-b>\<esc>", 'tnix') + call assert_equal(["abc\x0e\x0c\x02"], getline(1, '$')) + set norl + endif + + call test_override('ALL', 0) + set noshowmode showcmd + bw! +endfunc + +func! Test_edit_F1() + " Pressing <f1> + new + call feedkeys(":set im\<cr>\<f1>\<c-l>", 'tnix') + set noinsertmode + call assert_equal('help', &buftype) + bw + bw +endfunc + +func! Test_edit_F21() + " Pressing <f21> + " sends a netbeans command + if has("netbeans_intg") + new + " I have no idea what this is supposed to do :) + call feedkeys("A\<F21>\<F1>\<esc>", 'tnix') + bw + endif +endfunc + +func! Test_edit_HOME_END() + " Test Home/End Keys + new + set foldopen+=hor + call setline(1, ['abc', 'def']) + call cursor(1, 1) + call feedkeys("AX\<Home>Y\<esc>", 'tnix') + call cursor(2, 1) + call feedkeys("iZ\<End>Y\<esc>", 'tnix') + call assert_equal(['YabcX', 'ZdefY'], getline(1, '$')) + + set foldopen-=hor + bw! +endfunc + +func! Test_edit_INS() + " Test for Pressing <Insert> + new + call setline(1, ['abc', 'def']) + call cursor(1, 1) + call feedkeys("i\<Insert>ZYX>", 'tnix') + call assert_equal(['ZYX>', 'def'], getline(1, '$')) + call setline(1, ['abc', 'def']) + call cursor(1, 1) + call feedkeys("i\<Insert>Z\<Insert>YX>", 'tnix') + call assert_equal(['ZYX>bc', 'def'], getline(1, '$')) + bw! +endfunc + +func! Test_edit_LEFT_RIGHT() + " Left, Shift-Left, Right, Shift-Right + new + call setline(1, ['abc def ghi', 'ABC DEF GHI', 'ZZZ YYY XXX']) + let _ww=&ww + set ww= + call cursor(2, 1) + call feedkeys("i\<left>\<esc>", 'tnix') + call assert_equal([0, 2, 1, 0], getpos('.')) + " Is this a bug, <s-left> does not respect whichwrap option + call feedkeys("i\<s-left>\<esc>", 'tnix') + call assert_equal([0, 1, 8, 0], getpos('.')) + call feedkeys("i". repeat("\<s-left>", 3). "\<esc>", 'tnix') + call assert_equal([0, 1, 1, 0], getpos('.')) + call feedkeys("i\<right>\<esc>", 'tnix') + call assert_equal([0, 1, 1, 0], getpos('.')) + call feedkeys("i\<right>\<right>\<esc>", 'tnix') + call assert_equal([0, 1, 2, 0], getpos('.')) + call feedkeys("A\<right>\<esc>", 'tnix') + call assert_equal([0, 1, 11, 0], getpos('.')) + call feedkeys("A\<s-right>\<esc>", 'tnix') + call assert_equal([0, 2, 1, 0], getpos('.')) + call feedkeys("i\<s-right>\<esc>", 'tnix') + call assert_equal([0, 2, 4, 0], getpos('.')) + call cursor(3, 11) + call feedkeys("A\<right>\<esc>", 'tnix') + call feedkeys("A\<s-right>\<esc>", 'tnix') + call assert_equal([0, 3, 11, 0], getpos('.')) + call cursor(2, 11) + " <S-Right> does not respect 'whichwrap' option + call feedkeys("A\<s-right>\<esc>", 'tnix') + call assert_equal([0, 3, 1, 0], getpos('.')) + " Check motion when 'whichwrap' contains cursor keys for insert mode + set ww+=[,] + call cursor(2, 1) + call feedkeys("i\<left>\<esc>", 'tnix') + call assert_equal([0, 1, 11, 0], getpos('.')) + call cursor(2, 11) + call feedkeys("A\<right>\<esc>", 'tnix') + call assert_equal([0, 3, 1, 0], getpos('.')) + call cursor(2, 11) + call feedkeys("A\<s-right>\<esc>", 'tnix') + call assert_equal([0, 3, 1, 0], getpos('.')) + let &ww = _ww + bw! +endfunc + +func! Test_edit_MOUSE() + " This is a simple test, since we not really using the mouse here + if !has("mouse") + return + endif + 10new + call setline(1, range(1, 100)) + call cursor(1, 1) + set mouse=a + call feedkeys("A\<ScrollWheelDown>\<esc>", 'tnix') + call assert_equal([0, 4, 1, 0], getpos('.')) + " This should move by one pageDown, but only moves + " by one line when the test is run... + call feedkeys("A\<S-ScrollWheelDown>\<esc>", 'tnix') + call assert_equal([0, 5, 1, 0], getpos('.')) + set nostartofline + call feedkeys("A\<C-ScrollWheelDown>\<esc>", 'tnix') + call assert_equal([0, 6, 1, 0], getpos('.')) + call feedkeys("A\<LeftMouse>\<esc>", 'tnix') + call assert_equal([0, 6, 1, 0], getpos('.')) + call feedkeys("A\<RightMouse>\<esc>", 'tnix') + call assert_equal([0, 6, 1, 0], getpos('.')) + call cursor(1, 100) + norm! zt + " this should move by a screen up, but when the test + " is run, it moves up to the top of the buffer... + call feedkeys("A\<ScrollWheelUp>\<esc>", 'tnix') + call assert_equal([0, 1, 1, 0], getpos('.')) + call cursor(1, 30) + norm! zt + call feedkeys("A\<S-ScrollWheelUp>\<esc>", 'tnix') + call assert_equal([0, 1, 1, 0], getpos('.')) + call cursor(1, 30) + norm! zt + call feedkeys("A\<C-ScrollWheelUp>\<esc>", 'tnix') + call assert_equal([0, 1, 1, 0], getpos('.')) + %d + call setline(1, repeat(["12345678901234567890"], 100)) + call cursor(2, 1) + call feedkeys("A\<ScrollWheelRight>\<esc>", 'tnix') + call assert_equal([0, 2, 20, 0], getpos('.')) + call feedkeys("A\<ScrollWheelLeft>\<esc>", 'tnix') + call assert_equal([0, 2, 20, 0], getpos('.')) + call feedkeys("A\<S-ScrollWheelRight>\<esc>", 'tnix') + call assert_equal([0, 2, 20, 0], getpos('.')) + call feedkeys("A\<S-ScrollWheelLeft>\<esc>", 'tnix') + call assert_equal([0, 2, 20, 0], getpos('.')) + call feedkeys("A\<C-ScrollWheelRight>\<esc>", 'tnix') + call assert_equal([0, 2, 20, 0], getpos('.')) + call feedkeys("A\<C-ScrollWheelLeft>\<esc>", 'tnix') + call assert_equal([0, 2, 20, 0], getpos('.')) + set mouse& startofline + bw! +endfunc + +func! Test_edit_PAGEUP_PAGEDOWN() + 10new + call setline(1, repeat(['abc def ghi'], 30)) + call cursor(1, 1) + call feedkeys("i\<PageDown>\<esc>", 'tnix') + call assert_equal([0, 9, 1, 0], getpos('.')) + call feedkeys("i\<PageDown>\<esc>", 'tnix') + call assert_equal([0, 17, 1, 0], getpos('.')) + call feedkeys("i\<PageDown>\<esc>", 'tnix') + call assert_equal([0, 25, 1, 0], getpos('.')) + call feedkeys("i\<PageDown>\<esc>", 'tnix') + call assert_equal([0, 30, 1, 0], getpos('.')) + call feedkeys("i\<PageDown>\<esc>", 'tnix') + call assert_equal([0, 30, 1, 0], getpos('.')) + call feedkeys("A\<PageUp>\<esc>", 'tnix') + call assert_equal([0, 29, 1, 0], getpos('.')) + call feedkeys("A\<PageUp>\<esc>", 'tnix') + call assert_equal([0, 21, 1, 0], getpos('.')) + call feedkeys("A\<PageUp>\<esc>", 'tnix') + call assert_equal([0, 13, 1, 0], getpos('.')) + call feedkeys("A\<PageUp>\<esc>", 'tnix') + call assert_equal([0, 5, 1, 0], getpos('.')) + call feedkeys("A\<PageUp>\<esc>", 'tnix') + call assert_equal([0, 5, 11, 0], getpos('.')) + " <S-Up> is the same as <PageUp> + " <S-Down> is the same as <PageDown> + call cursor(1, 1) + call feedkeys("i\<S-Down>\<esc>", 'tnix') + call assert_equal([0, 9, 1, 0], getpos('.')) + call feedkeys("i\<S-Down>\<esc>", 'tnix') + call assert_equal([0, 17, 1, 0], getpos('.')) + call feedkeys("i\<S-Down>\<esc>", 'tnix') + call assert_equal([0, 25, 1, 0], getpos('.')) + call feedkeys("i\<S-Down>\<esc>", 'tnix') + call assert_equal([0, 30, 1, 0], getpos('.')) + call feedkeys("i\<S-Down>\<esc>", 'tnix') + call assert_equal([0, 30, 1, 0], getpos('.')) + call feedkeys("A\<S-Up>\<esc>", 'tnix') + call assert_equal([0, 29, 1, 0], getpos('.')) + call feedkeys("A\<S-Up>\<esc>", 'tnix') + call assert_equal([0, 21, 1, 0], getpos('.')) + call feedkeys("A\<S-Up>\<esc>", 'tnix') + call assert_equal([0, 13, 1, 0], getpos('.')) + call feedkeys("A\<S-Up>\<esc>", 'tnix') + call assert_equal([0, 5, 1, 0], getpos('.')) + call feedkeys("A\<S-Up>\<esc>", 'tnix') + call assert_equal([0, 5, 11, 0], getpos('.')) + set nostartofline + call cursor(30, 11) + norm! zt + call feedkeys("A\<PageUp>\<esc>", 'tnix') + call assert_equal([0, 29, 11, 0], getpos('.')) + call feedkeys("A\<PageUp>\<esc>", 'tnix') + call assert_equal([0, 21, 11, 0], getpos('.')) + call feedkeys("A\<PageUp>\<esc>", 'tnix') + call assert_equal([0, 13, 11, 0], getpos('.')) + call feedkeys("A\<PageUp>\<esc>", 'tnix') + call assert_equal([0, 5, 11, 0], getpos('.')) + call feedkeys("A\<PageUp>\<esc>", 'tnix') + call assert_equal([0, 5, 11, 0], getpos('.')) + call cursor(1, 1) + call feedkeys("A\<PageDown>\<esc>", 'tnix') + call assert_equal([0, 9, 11, 0], getpos('.')) + call feedkeys("A\<PageDown>\<esc>", 'tnix') + call assert_equal([0, 17, 11, 0], getpos('.')) + call feedkeys("A\<PageDown>\<esc>", 'tnix') + call assert_equal([0, 25, 11, 0], getpos('.')) + call feedkeys("A\<PageDown>\<esc>", 'tnix') + call assert_equal([0, 30, 11, 0], getpos('.')) + call feedkeys("A\<PageDown>\<esc>", 'tnix') + call assert_equal([0, 30, 11, 0], getpos('.')) + " <S-Up> is the same as <PageUp> + " <S-Down> is the same as <PageDown> + call cursor(30, 11) + norm! zt + call feedkeys("A\<S-Up>\<esc>", 'tnix') + call assert_equal([0, 29, 11, 0], getpos('.')) + call feedkeys("A\<S-Up>\<esc>", 'tnix') + call assert_equal([0, 21, 11, 0], getpos('.')) + call feedkeys("A\<S-Up>\<esc>", 'tnix') + call assert_equal([0, 13, 11, 0], getpos('.')) + call feedkeys("A\<S-Up>\<esc>", 'tnix') + call assert_equal([0, 5, 11, 0], getpos('.')) + call feedkeys("A\<S-Up>\<esc>", 'tnix') + call assert_equal([0, 5, 11, 0], getpos('.')) + call cursor(1, 1) + call feedkeys("A\<S-Down>\<esc>", 'tnix') + call assert_equal([0, 9, 11, 0], getpos('.')) + call feedkeys("A\<S-Down>\<esc>", 'tnix') + call assert_equal([0, 17, 11, 0], getpos('.')) + call feedkeys("A\<S-Down>\<esc>", 'tnix') + call assert_equal([0, 25, 11, 0], getpos('.')) + call feedkeys("A\<S-Down>\<esc>", 'tnix') + call assert_equal([0, 30, 11, 0], getpos('.')) + call feedkeys("A\<S-Down>\<esc>", 'tnix') + call assert_equal([0, 30, 11, 0], getpos('.')) + bw! +endfunc + +func! Test_edit_forbidden() + new + " 1) edit in the sandbox is not allowed + call setline(1, 'a') + com! Sandbox :sandbox call feedkeys("i\<del>\<esc>", 'tnix') + call assert_fails(':Sandbox', 'E48:') + com! Sandbox :sandbox exe "norm! i\<del>" + call assert_fails(':Sandbox', 'E48:') + delcom Sandbox + call assert_equal(['a'], getline(1,'$')) + " 2) edit with textlock set + fu! DoIt() + call feedkeys("i\<del>\<esc>", 'tnix') + endfu + au InsertCharPre <buffer> :call DoIt() + try + call feedkeys("ix\<esc>", 'tnix') + call assert_fails(1, 'textlock') + catch /^Vim\%((\a\+)\)\=:E523/ " catch E523: not allowed here + endtry + " TODO: Might be a bug: should x really be inserted here + call assert_equal(['xa'], getline(1, '$')) + delfu DoIt + try + call feedkeys("ix\<esc>", 'tnix') + call assert_fails(1, 'unknown function') + catch /^Vim\%((\a\+)\)\=:E117/ " catch E117: unknown function + endtry + au! InsertCharPre + " 3) edit when completion is shown + fun! Complete(findstart, base) + if a:findstart + return col('.') + else + call feedkeys("i\<del>\<esc>", 'tnix') + return [] + endif + endfun + set completefunc=Complete + try + call feedkeys("i\<c-x>\<c-u>\<esc>", 'tnix') + call assert_fails(1, 'change in complete function') + catch /^Vim\%((\a\+)\)\=:E523/ " catch E523 + endtry + delfu Complete + set completefunc= + if has("rightleft") && exists("+fkmap") + " 4) 'R' when 'fkmap' and 'revins' is set. + set revins fkmap + try + normal Ri + call assert_fails(1, "R with 'fkmap' and 'ri' set") + catch + finally + set norevins nofkmap + endtry + endif + bw! +endfunc + +func! Test_edit_rightleft() + " Cursor in rightleft mode moves differently + if !exists("+rightleft") + return + endif + call NewWindow(10, 20) + call setline(1, ['abc', 'def', 'ghi']) + call cursor(1, 2) + set rightleft + " Screen looks as expected + let lines = ScreenLines([1, 4], winwidth(0)) + let expect = [ + \" cba", + \" fed", + \" ihg", + \" ~"] + call assert_equal(join(expect, "\n"), join(lines, "\n")) + " 2) right moves to the left + call feedkeys("i\<right>\<esc>x", 'txin') + call assert_equal(['bc', 'def', 'ghi'], getline(1,'$')) + call cursor(1, 2) + call feedkeys("i\<s-right>\<esc>", 'txin') + call cursor(1, 2) + call feedkeys("i\<c-right>\<esc>", 'txin') + " Screen looks as expected + let lines = ScreenLines([1, 4], winwidth(0)) + let expect = [ + \" cb", + \" fed", + \" ihg", + \" ~"] + call assert_equal(join(expect, "\n"), join(lines, "\n")) + " 2) left moves to the right + call setline(1, ['abc', 'def', 'ghi']) + call cursor(1, 2) + call feedkeys("i\<left>\<esc>x", 'txin') + call assert_equal(['ac', 'def', 'ghi'], getline(1,'$')) + call cursor(1, 2) + call feedkeys("i\<s-left>\<esc>", 'txin') + call cursor(1, 2) + call feedkeys("i\<c-left>\<esc>", 'txin') + " Screen looks as expected + let lines = ScreenLines([1, 4], winwidth(0)) + let expect = [ + \" ca", + \" fed", + \" ihg", + \" ~"] + call assert_equal(join(expect, "\n"), join(lines, "\n")) + set norightleft + bw! +endfunc + +func Test_edit_quit() + edit foo.txt + split + new + call setline(1, 'hello') + 3wincmd w + redraw! + call assert_fails('1q', 'E37:') + bwipe! foo.txt + only +endfunc + diff --git a/src/nvim/testdir/test_erasebackword.vim b/src/nvim/testdir/test_erasebackword.vim new file mode 100644 index 0000000000..098d6edfcb --- /dev/null +++ b/src/nvim/testdir/test_erasebackword.vim @@ -0,0 +1,25 @@ + +func Test_erasebackword() + if !has('multi_byte') + return + endif + + set encoding=utf-8 + enew + + exe "normal o wwwこんにちわ世界ワールドvim \<C-W>" + call assert_equal(' wwwこんにちわ世界ワールド', getline('.')) + exe "normal o wwwこんにちわ世界ワールドvim \<C-W>\<C-W>" + call assert_equal(' wwwこんにちわ世界', getline('.')) + exe "normal o wwwこんにちわ世界ワールドvim \<C-W>\<C-W>\<C-W>" + call assert_equal(' wwwこんにちわ', getline('.')) + exe "normal o wwwこんにちわ世界ワールドvim \<C-W>\<C-W>\<C-W>\<C-W>" + call assert_equal(' www', getline('.')) + exe "normal o wwwこんにちわ世界ワールドvim \<C-W>\<C-W>\<C-W>\<C-W>\<C-W>" + call assert_equal(' ', getline('.')) + exe "normal o wwwこんにちわ世界ワールドvim \<C-W>\<C-W>\<C-W>\<C-W>\<C-W>\<C-W>" + call assert_equal('', getline('.')) + + enew! + set encoding& +endfunc diff --git a/src/nvim/testdir/test_exists.vim b/src/nvim/testdir/test_exists.vim new file mode 100644 index 0000000000..fd34be83b0 --- /dev/null +++ b/src/nvim/testdir/test_exists.vim @@ -0,0 +1,321 @@ +" Tests for the exists() function +func Test_exists() + augroup myagroup + autocmd! BufEnter *.my echo "myfile edited" + autocmd! FuncUndefined UndefFun exec "fu UndefFun()\nendfu" + augroup END + set rtp+=./sautest + + " valid autocmd group + call assert_equal(1, exists('#myagroup')) + " valid autocmd group with garbage + call assert_equal(0, exists('#myagroup+b')) + " Valid autocmd group and event + call assert_equal(1, exists('#myagroup#BufEnter')) + " Valid autocmd group, event and pattern + call assert_equal(1, exists('#myagroup#BufEnter#*.my')) + " Valid autocmd event + call assert_equal(1, exists('#BufEnter')) + " Valid autocmd event and pattern + call assert_equal(1, exists('#BufEnter#*.my')) + " Non-existing autocmd group or event + call assert_equal(0, exists('#xyzagroup')) + " Non-existing autocmd group and valid autocmd event + call assert_equal(0, exists('#xyzagroup#BufEnter')) + " Valid autocmd group and event with no matching pattern + call assert_equal(0, exists('#myagroup#CmdwinEnter')) + " Valid autocmd group and non-existing autocmd event + call assert_equal(0, exists('#myagroup#xyzacmd')) + " Valid autocmd group and event and non-matching pattern + call assert_equal(0, exists('#myagroup#BufEnter#xyzpat')) + " Valid autocmd event and non-matching pattern + call assert_equal(0, exists('#BufEnter#xyzpat')) + " Empty autocmd group, event and pattern + call assert_equal(0, exists('###')) + " Empty autocmd group and event or empty event and pattern + call assert_equal(0, exists('##')) + " Valid autocmd event + call assert_equal(1, exists('##FileReadCmd')) + " Non-existing autocmd event + call assert_equal(0, exists('##MySpecialCmd')) + + " Existing and working option (long form) + call assert_equal(1, exists('&textwidth')) + " Existing and working option (short form) + call assert_equal(1, exists('&tw')) + " Existing and working option with garbage + call assert_equal(0, exists('&tw-')) + " Global option + call assert_equal(1, exists('&g:errorformat')) + " Local option + call assert_equal(1, exists('&l:errorformat')) + " Negative form of existing and working option (long form) + call assert_equal(0, exists('&nojoinspaces')) + " Negative form of existing and working option (short form) + call assert_equal(0, exists('&nojs')) + " Non-existing option + call assert_equal(0, exists('&myxyzoption')) + + " Existing and working option (long form) + call assert_equal(1, exists('+incsearch')) + " Existing and working option with garbage + call assert_equal(0, exists('+incsearch!1')) + " Existing and working option (short form) + call assert_equal(1, exists('+is')) + " Existing option that is hidden. + call assert_equal(0, exists('+autoprint')) + + " Existing environment variable + let $EDITOR_NAME = 'Vim Editor' + call assert_equal(1, exists('$EDITOR_NAME')) + " Non-existing environment variable + call assert_equal(0, exists('$NON_ENV_VAR')) + + " Valid internal function + call assert_equal(1, exists('*bufnr')) + " Valid internal function with () + call assert_equal(1, exists('*bufnr()')) + " Non-existing internal function + call assert_equal(0, exists('*myxyzfunc')) + " Valid internal function with garbage + call assert_equal(0, exists('*bufnr&6')) + " Valid user defined function + call assert_equal(1, exists('*Test_exists')) + " Non-existing user defined function + call assert_equal(0, exists('*MyxyzFunc')) + " Function that may be created by FuncUndefined event + call assert_equal(0, exists('*UndefFun')) + " Function that may be created by script autoloading + call assert_equal(0, exists('*footest#F')) + + " Valid internal command (full match) + call assert_equal(2, exists(':edit')) + " Valid internal command (full match) with garbage + call assert_equal(0, exists(':edit/a')) + " Valid internal command (partial match) + call assert_equal(1, exists(':q')) + " Non-existing internal command + call assert_equal(0, exists(':invalidcmd')) + + " User defined command (full match) + command! MyCmd :echo 'My command' + call assert_equal(2, exists(':MyCmd')) + " User defined command (partial match) + command! MyOtherCmd :echo 'Another command' + call assert_equal(3, exists(':My')) + + " Command modifier + call assert_equal(2, exists(':rightbelow')) + + " Non-existing user defined command (full match) + delcommand MyCmd + call assert_equal(0, exists(':MyCmd')) + + " Non-existing user defined command (partial match) + delcommand MyOtherCmd + call assert_equal(0, exists(':My')) + + " Valid local variable + let local_var = 1 + call assert_equal(1, exists('local_var')) + " Valid local variable with garbage + call assert_equal(0, exists('local_var%n')) + " Non-existing local variable + unlet local_var + call assert_equal(0, exists('local_var')) + + " Non-existing autoload variable that may be autoloaded + call assert_equal(0, exists('footest#x')) + + " Valid local list + let local_list = ["blue", "orange"] + call assert_equal(1, exists('local_list')) + " Valid local list item + call assert_equal(1, exists('local_list[1]')) + " Valid local list item with garbage + call assert_equal(0, exists('local_list[1]+5')) + " Invalid local list item + call assert_equal(0, exists('local_list[2]')) + " Non-existing local list + unlet local_list + call assert_equal(0, exists('local_list')) + " Valid local dictionary + let local_dict = {"xcord":100, "ycord":2} + call assert_equal(1, exists('local_dict')) + " Non-existing local dictionary + unlet local_dict + call assert_equal(0, exists('local_dict')) + " Existing local curly-brace variable + let str = "local" + let curly_{str}_var = 1 + call assert_equal(1, exists('curly_{str}_var')) + " Non-existing local curly-brace variable + unlet curly_{str}_var + call assert_equal(0, exists('curly_{str}_var')) + + " Existing global variable + let g:global_var = 1 + call assert_equal(1, exists('g:global_var')) + " Existing global variable with garbage + call assert_equal(0, exists('g:global_var-n')) + " Non-existing global variable + unlet g:global_var + call assert_equal(0, exists('g:global_var')) + " Existing global list + let g:global_list = ["blue", "orange"] + call assert_equal(1, exists('g:global_list')) + " Non-existing global list + unlet g:global_list + call assert_equal(0, exists('g:global_list')) + " Existing global dictionary + let g:global_dict = {"xcord":100, "ycord":2} + call assert_equal(1, exists('g:global_dict')) + " Non-existing global dictionary + unlet g:global_dict + call assert_equal(0, exists('g:global_dict')) + " Existing global curly-brace variable + let str = "global" + let g:curly_{str}_var = 1 + call assert_equal(1, exists('g:curly_{str}_var')) + " Non-existing global curly-brace variable + unlet g:curly_{str}_var + call assert_equal(0, exists('g:curly_{str}_var')) + + " Existing window variable + let w:window_var = 1 + call assert_equal(1, exists('w:window_var')) + " Non-existing window variable + unlet w:window_var + call assert_equal(0, exists('w:window_var')) + " Existing window list + let w:window_list = ["blue", "orange"] + call assert_equal(1, exists('w:window_list')) + " Non-existing window list + unlet w:window_list + call assert_equal(0, exists('w:window_list')) + " Existing window dictionary + let w:window_dict = {"xcord":100, "ycord":2} + call assert_equal(1, exists('w:window_dict')) + " Non-existing window dictionary + unlet w:window_dict + call assert_equal(0, exists('w:window_dict')) + " Existing window curly-brace variable + let str = "window" + let w:curly_{str}_var = 1 + call assert_equal(1, exists('w:curly_{str}_var')) + " Non-existing window curly-brace variable + unlet w:curly_{str}_var + call assert_equal(0, exists('w:curly_{str}_var')) + + " Existing tab variable + let t:tab_var = 1 + call assert_equal(1, exists('t:tab_var')) + " Non-existing tab variable + unlet t:tab_var + call assert_equal(0, exists('t:tab_var')) + " Existing tab list + let t:tab_list = ["blue", "orange"] + call assert_equal(1, exists('t:tab_list')) + " Non-existing tab list + unlet t:tab_list + call assert_equal(0, exists('t:tab_list')) + " Existing tab dictionary + let t:tab_dict = {"xcord":100, "ycord":2} + call assert_equal(1, exists('t:tab_dict')) + " Non-existing tab dictionary + unlet t:tab_dict + call assert_equal(0, exists('t:tab_dict')) + " Existing tab curly-brace variable + let str = "tab" + let t:curly_{str}_var = 1 + call assert_equal(1, exists('t:curly_{str}_var')) + " Non-existing tab curly-brace variable + unlet t:curly_{str}_var + call assert_equal(0, exists('t:curly_{str}_var')) + + " Existing buffer variable + let b:buffer_var = 1 + call assert_equal(1, exists('b:buffer_var')) + " Non-existing buffer variable + unlet b:buffer_var + call assert_equal(0, exists('b:buffer_var')) + " Existing buffer list + let b:buffer_list = ["blue", "orange"] + call assert_equal(1, exists('b:buffer_list')) + " Non-existing buffer list + unlet b:buffer_list + call assert_equal(0, exists('b:buffer_list')) + " Existing buffer dictionary + let b:buffer_dict = {"xcord":100, "ycord":2} + call assert_equal(1, exists('b:buffer_dict')) + " Non-existing buffer dictionary + unlet b:buffer_dict + call assert_equal(0, exists('b:buffer_dict')) + " Existing buffer curly-brace variable + let str = "buffer" + let b:curly_{str}_var = 1 + call assert_equal(1, exists('b:curly_{str}_var')) + " Non-existing buffer curly-brace variable + unlet b:curly_{str}_var + call assert_equal(0, exists('b:curly_{str}_var')) + + " Existing Vim internal variable + call assert_equal(1, exists('v:version')) + " Non-existing Vim internal variable + call assert_equal(0, exists('v:non_exists_var')) + + " Existing script-local variable + let s:script_var = 1 + call assert_equal(1, exists('s:script_var')) + " Non-existing script-local variable + unlet s:script_var + call assert_equal(0, exists('s:script_var')) + " Existing script-local list + let s:script_list = ["blue", "orange"] + call assert_equal(1, exists('s:script_list')) + " Non-existing script-local list + unlet s:script_list + call assert_equal(0, exists('s:script_list')) + " Existing script-local dictionary + let s:script_dict = {"xcord":100, "ycord":2} + call assert_equal(1, exists('s:script_dict')) + " Non-existing script-local dictionary + unlet s:script_dict + call assert_equal(0, exists('s:script_dict')) + " Existing script curly-brace variable + let str = "script" + let s:curly_{str}_var = 1 + call assert_equal(1, exists('s:curly_{str}_var')) + " Non-existing script-local curly-brace variable + unlet s:curly_{str}_var + call assert_equal(0, exists('s:curly_{str}_var')) + + " Existing script-local function + function! s:my_script_func() + endfunction + + echo '*s:my_script_func: 1' + call assert_equal(1, exists('*s:my_script_func')) + + " Non-existing script-local function + delfunction s:my_script_func + + call assert_equal(0, exists('*s:my_script_func')) + unlet str + + call assert_equal(1, g:footest#x) + call assert_equal(0, footest#F()) + call assert_equal(0, UndefFun()) +endfunc + +" exists() test for Function arguments +func FuncArg_Tests(func_arg, ...) + call assert_equal(1, exists('a:func_arg')) + call assert_equal(0, exists('a:non_exists_arg')) + call assert_equal(1, exists('a:1')) + call assert_equal(0, exists('a:2')) +endfunc + +func Test_exists_funcarg() + call FuncArg_Tests("arg1", "arg2") +endfunc diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index 82c5d21bd0..ad967c528c 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -384,9 +384,10 @@ func Test_substitute_expr() \ {-> submatch(2) . submatch(3) . submatch(1)}, '')) func Recurse() - return substitute('yyy', 'y*', {-> g:val}, '') + return substitute('yyy', 'y\(.\)y', {-> submatch(1)}, '') endfunc - call assert_equal('--', substitute('xxx', 'x*', {-> '-' . Recurse() . '-'}, '')) + " recursive call works + call assert_equal('-y-x-', substitute('xxx', 'x\(.\)x', {-> '-' . Recurse() . '-' . submatch(1) . '-'}, '')) endfunc func Test_invalid_submatch() @@ -418,6 +419,9 @@ func Test_function_with_funcref() let s:fref = function(s:f) call assert_equal(v:t_string, s:fref('x')) call assert_fails("call function('s:f')", 'E700:') + + call assert_fails("call function('foo()')", 'E475:') + call assert_fails("call function('foo()')", 'foo()') endfunc func Test_funcref() @@ -435,3 +439,8 @@ func Test_funcref() call assert_equal(2, OneByRef()) call assert_fails('echo funcref("{")', 'E475:') endfunc + +func Test_empty_concatenate() + call assert_equal('b', 'a'[4:0] . 'b') + call assert_equal('b', 'b' . 'a'[4:0]) +endfunc diff --git a/src/nvim/testdir/test_expr_utf8.vim b/src/nvim/testdir/test_expr_utf8.vim index 9ea6d8872b..1737a9f745 100644 --- a/src/nvim/testdir/test_expr_utf8.vim +++ b/src/nvim/testdir/test_expr_utf8.vim @@ -3,7 +3,7 @@ if !has('multi_byte') finish endif -func Test_strgetchar_utf8() +func Test_strgetchar() call assert_equal(char2nr('á'), strgetchar('áxb', 0)) call assert_equal(char2nr('x'), strgetchar('áxb', 1)) @@ -16,7 +16,7 @@ func Test_strgetchar_utf8() call assert_equal(char2nr('い'), strgetchar('あaい', 2)) endfunc -func Test_strcharpart_utf8() +func Test_strcharpart() call assert_equal('áxb', strcharpart('áxb', 0)) call assert_equal('á', strcharpart('áxb', 0, 1)) call assert_equal('x', strcharpart('áxb', 1, 1)) diff --git a/src/nvim/testdir/test_farsi.vim b/src/nvim/testdir/test_farsi.vim index e094191599..9ff2653af4 100644 --- a/src/nvim/testdir/test_farsi.vim +++ b/src/nvim/testdir/test_farsi.vim @@ -1,4 +1,5 @@ " Simplistic testing of Farsi mode. +" Note: must be edited with latin1 encoding. if !has('farsi') || has('nvim') " Not supported in Nvim. #6192 finish @@ -82,3 +83,51 @@ func Test_farsi_map() set noaltkeymap bwipe! endfunc + +func Test_input_farsi() + new + setlocal rightleft fkmap + " numbers switch input direction + call feedkeys("aabc0123456789.+-^%#=xyz\<Esc>", 'tx') + call assert_equal("\x8cν\x93", getline('.')) + + " all non-number special chars with spaces + call feedkeys("oB E F H I K L M O P Q R T U W Y ` ! @ # $ % ^ & * () - _ = + \\ | : \" . / < > ? \<Esc>", 'tx') + call assert_equal(" []蠨頽", getline('.')) + + " all non-number special chars without spaces + call feedkeys("oBEFHIKLMOPQRTUWY`!@#$%^&*()-_=+\\|:\"./<>?\<Esc>",'tx') + call assert_equal("[]訩齫꺻", getline('.')) + + " all letter chars with spaces + call feedkeys("oa A b c C d D e f g G h i j J k l m n N o p q r s S t u v V w x X y z Z ; \ , [ ] \<Esc>", 'tx') + call assert_equal("Ѡ̠ΠϠƠàܠŠޠݠĠˠˠʠɠӠ٠Рؠ֠͠͠ҠԠԠנՠڠߠǠȠ", getline('.')) + + " all letter chars without spaces + call feedkeys("oaAbcCdDefgGhijJklmnNopqrsStuvVwxXyzZ;\,[]\<Esc>", 'tx') + call assert_equal("\x8c\x9f\x86\x83\x9d\x85\x80\x9c\x9b\x84\x8a\x89\x8e\x96\x8b\x95\x90\x8d\x93\x97\x87\x88", getline('.')) + + bwipe! +endfunc + +func Test_command_line_farsi() + set allowrevins altkeymap + + " letter characters with spaces + call feedkeys(":\"\<C-_>a A b c C d D e f g G h i j J k l m n N o p q r s S t u v V w x X y z Z ; \\ , [ ]\<CR>", 'tx') + call assert_equal("\"\x88ǠߠڠՠՠנԠԠҠ֠͠͠ؠР٠ӠɠʠˠˠĠݠޠŠܠàƠϠΠ̠", getreg(':')) + + " letter characters without spaces + call feedkeys(":\"\<C-_>aAbcCdDefgGhijJklmnNopqrsStuvVwxXyzZ;\\,[]\<CR>", 'tx') + call assert_equal("\"\x88\x87\x93\x8d\x90\x95\x8b\x96\x8e\x89\x8a\x84\x9b\x9c\x80\x85\x9d\x83\x86\x9f\x8c", getreg(':')) + + " other characters with spaces + call feedkeys(":\"\<C-_>0 1 2 3 4 5 6 7 8 9 ` . ! \" $ % ^ & / () = \\ ? + - _ * : # ~ @ < > { } | B E F H I K L M O P Q R T U W Y\<CR>", 'tx') + call assert_equal("\"][ }{~頭렽", getreg(':')) + + " other characters without spaces + call feedkeys(":\"\<C-_>0123456789`.!\"$%^&/()=\\?+-_*:#~@<>{}|BEFHIKLMOPQRTUWY\<CR>", 'tx') + call assert_equal("\"][}{~魫뽩", getreg(':')) + + set noallowrevins noaltkeymap +endfunc diff --git a/src/nvim/testdir/test_file_size.vim b/src/nvim/testdir/test_file_size.vim new file mode 100644 index 0000000000..3e78a7b23c --- /dev/null +++ b/src/nvim/testdir/test_file_size.vim @@ -0,0 +1,58 @@ +" Inserts 2 million lines with consecutive integers starting from 1 +" (essentially, the output of GNU's seq 1 2000000), writes them to Xtest +" and writes its cksum to test.out. +" +" We need 2 million lines to trigger a call to mf_hash_grow(). If it would mess +" up the lines the checksum would differ. +" +" cksum is part of POSIX and so should be available on most Unixes. +" If it isn't available then the test will be skipped. +func Test_File_Size() + if !executable('cksum') + return + endif + + new + set fileformat=unix undolevels=-1 + for i in range(1, 2000000, 100) + call append(i, range(i, i + 99)) + endfor + + 1delete + w! Xtest + let res = systemlist('cksum Xtest')[0] + let res = substitute(res, "\r", "", "") + call assert_equal('3678979763 14888896 Xtest', res) + + enew! + call delete('Xtest') + set fileformat& undolevels& +endfunc + +" Test for writing and reading a file of over 100 Kbyte +func Test_File_Read_Write() + enew! + + " Create a file with the following contents + " 1 line: "This is the start" + " 3001 lines: "This is the leader" + " 1 line: "This is the middle" + " 3001 lines: "This is the trailer" + " 1 line: "This is the end" + call append(0, "This is the start") + call append(1, repeat(["This is the leader"], 3001)) + call append(3002, "This is the middle") + call append(3003, repeat(["This is the trailer"], 3001)) + call append(6004, "This is the end") + + write! Xtest + enew! + edit! Xtest + + call assert_equal("This is the start", getline(1)) + call assert_equal("This is the middle", getline(3003)) + call assert_equal("This is the end", getline(6005)) + + enew! + call delete("Xtest") +endfunc diff --git a/src/nvim/testdir/test_fileformat.vim b/src/nvim/testdir/test_fileformat.vim new file mode 100644 index 0000000000..8dc25f62b1 --- /dev/null +++ b/src/nvim/testdir/test_fileformat.vim @@ -0,0 +1,33 @@ +" Test behavior of fileformat after bwipeout of last buffer + +func Test_fileformat_after_bw() + bwipeout + set fileformat& + if &fileformat == 'dos' + let test_fileformats = 'unix' + elseif &fileformat == 'unix' + let test_fileformats = 'mac' + else " must be mac + let test_fileformats = 'dos' + endif + exec 'set fileformats='.test_fileformats + bwipeout! + call assert_equal(test_fileformats, &fileformat) + set fileformats& +endfunc + +func Test_fileformat_autocommand() + let filecnt = ["", "foobar\<CR>", "eins\<CR>", "\<CR>", "zwei\<CR>", "drei", "vier", "fünf", ""] + let ffs = &ffs + call writefile(filecnt, 'Xfile', 'b') + au BufReadPre Xfile set ffs=dos ff=dos + new Xfile + call assert_equal('dos', &l:ff) + call assert_equal('dos', &ffs) + + " cleanup + call delete('Xfile') + let &ffs = ffs + au! BufReadPre Xfile + bw! +endfunc diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim new file mode 100644 index 0000000000..43dc383f3d --- /dev/null +++ b/src/nvim/testdir/test_filetype.vim @@ -0,0 +1,557 @@ +" Test :setfiletype + +func Test_detection() + filetype on + augroup filetypedetect + au BufNewFile,BufRead * call assert_equal(1, did_filetype()) + augroup END + new something.vim + call assert_equal('vim', &filetype) + + bwipe! + filetype off +endfunc + +func Test_conf_type() + filetype on + call writefile(['# some comment', 'must be conf'], 'Xfile') + augroup filetypedetect + au BufNewFile,BufRead * call assert_equal(0, did_filetype()) + augroup END + split Xfile + call assert_equal('conf', &filetype) + + bwipe! + call delete('Xfile') + filetype off +endfunc + +func Test_other_type() + filetype on + augroup filetypedetect + au BufNewFile,BufRead * call assert_equal(0, did_filetype()) + au BufNewFile,BufRead Xfile setf testfile + au BufNewFile,BufRead * call assert_equal(1, did_filetype()) + augroup END + call writefile(['# some comment', 'must be conf'], 'Xfile') + split Xfile + call assert_equal('testfile', &filetype) + + bwipe! + call delete('Xfile') + filetype off +endfunc + +" Filetypes detected just from matching the file name. +let s:filename_checks = { + \ 'a2ps': ['/etc/a2ps.cfg', '/etc/a2ps/file.cfg', 'a2psrc', '.a2psrc'], + \ 'a65': ['file.a65'], + \ 'abap': ['file.abap'], + \ 'abc': ['file.abc'], + \ 'abel': ['file.abl'], + \ 'acedb': ['file.wrm'], + \ 'ada': ['file.adb', 'file.ads', 'file.ada', 'file.gpr'], + \ 'ahdl': ['file.tdf'], + \ 'alsaconf': ['.asoundrc', '/usr/share/alsa/alsa.conf', '/etc/asound.conf'], + \ 'aml': ['file.aml'], + \ 'ampl': ['file.run'], + \ 'ant': ['build.xml'], + \ 'apache': ['.htaccess', '/etc/httpd/file.conf'], + \ 'applescript': ['file.scpt'], + \ 'aptconf': ['apt.conf', '/.aptitude/config'], + \ 'arch': ['.arch-inventory'], + \ 'arduino': ['file.ino', 'file.pde'], + \ 'art': ['file.art'], + \ 'asciidoc': ['file.asciidoc', 'file.adoc'], + \ 'asn': ['file.asn', 'file.asn1'], + \ 'atlas': ['file.atl', 'file.as'], + \ 'autohotkey': ['file.ahk'], + \ 'autoit': ['file.au3'], + \ 'automake': ['GNUmakefile.am'], + \ 'ave': ['file.ave'], + \ 'awk': ['file.awk'], + \ 'b': ['file.mch', 'file.ref', 'file.imp'], + \ 'bc': ['file.bc'], + \ 'bdf': ['file.bdf'], + \ 'bib': ['file.bib'], + \ 'bindzone': ['named.root'], + \ 'blank': ['file.bl'], + \ 'bst': ['file.bst'], + \ 'bzr': ['bzr_log.any'], + \ 'c': ['enlightenment/file.cfg', 'file.qc', 'file.c'], + \ 'cabal': ['file.cabal'], + \ 'calendar': ['calendar'], + \ 'catalog': ['catalog'], + \ 'cdl': ['file.cdl'], + \ 'cdrdaoconf': ['/etc/cdrdao.conf', '/etc/defaults/cdrdao', '/etc/default/cdrdao', '.cdrdao'], + \ 'cdrtoc': ['file.toc'], + \ 'cf': ['file.cfm', 'file.cfi', 'file.cfc'], + \ 'cfengine': ['cfengine.conf'], + \ 'cfg': ['file.cfg', 'file.hgrc', 'filehgrc'], + \ 'ch': ['file.chf'], + \ 'chaiscript': ['file.chai'], + \ 'chaskell': ['file.chs'], + \ 'chill': ['file..ch'], + \ 'chordpro': ['file.chopro', 'file.crd', 'file.cho', 'file.crdpro', 'file.chordpro'], + \ 'cl': ['file.eni'], + \ 'clean': ['file.dcl', 'file.icl'], + \ 'clojure': ['file.clj', 'file.cljs', 'file.cljx', 'file.cljc'], + \ 'cmake': ['CMakeLists.txt', 'file.cmake', 'file.cmake.in'], + \ 'cmusrc': ['any/.cmus/autosave', 'any/.cmus/rc', 'any/.cmus/command-history', 'any/.cmus/file.theme', 'any/cmus/rc', 'any/cmus/file.theme'], + \ 'cobol': ['file.cbl', 'file.cob', 'file.lib'], + \ 'coco': ['file.atg'], + \ 'conaryrecipe': ['file.recipe'], + \ 'conf': ['auto.master'], + \ 'config': ['configure.in', 'configure.ac'], + \ 'context': ['tex/context/any/file.tex', 'file.mkii', 'file.mkiv', 'file.mkvi'], + \ 'cpp': ['file.cxx', 'file.c++', 'file.hh', 'file.hxx', 'file.hpp', 'file.ipp', 'file.moc', 'file.tcc', 'file.inl', 'file.tlh'], + \ 'crm': ['file.crm'], + \ 'cs': ['file.cs'], + \ 'csc': ['file.csc'], + \ 'csdl': ['file.csdl'], + \ 'csp': ['file.csp', 'file.fdr'], + \ 'css': ['file.css'], + \ 'cterm': ['file.con'], + \ 'cucumber': ['file.feature'], + \ 'cuda': ['file.cu'], + \ 'cupl': ['file.pld'], + \ 'cuplsim': ['file.si'], + \ 'cvs': ['cvs123'], + \ 'cvsrc': ['.cvsrc'], + \ 'cynpp': ['file.cyn'], + \ 'datascript': ['file.ds'], + \ 'dcd': ['file.dcd'], + \ 'debcontrol': ['/debian/control'], + \ 'debsources': ['/etc/apt/sources.list', '/etc/apt/sources.list.d/file.list'], + \ 'def': ['file.def'], + \ 'denyhosts': ['denyhosts.conf'], + \ 'desc': ['file.desc'], + \ 'desktop': ['file.desktop', '.directory'], + \ 'dictconf': ['dict.conf', '.dictrc'], + \ 'dictdconf': ['dictd.conf'], + \ 'diff': ['file.diff', 'file.rej'], + \ 'dircolors': ['.dir_colors', '.dircolors', '/etc/DIR_COLORS'], + \ 'dnsmasq': ['/etc/dnsmasq.conf'], + \ 'dockerfile': ['Dockerfile', 'file.Dockerfile'], + \ 'dosbatch': ['file.bat', 'file.sys'], + \ 'dosini': ['.editorconfig', '/etc/yum.conf', 'file.ini'], + \ 'dot': ['file.dot'], + \ 'dracula': ['file.drac', 'file.drc', 'filelvs', 'filelpe'], + \ 'dsl': ['file.dsl'], + \ 'dtd': ['file.dtd'], + \ 'dts': ['file.dts', 'file.dtsi'], + \ 'dylan': ['file.dylan'], + \ 'dylanintr': ['file.intr'], + \ 'dylanlid': ['file.lid'], + \ 'ecd': ['file.ecd'], + \ 'edif': ['file.edf', 'file.edif', 'file.edo'], + \ 'elinks': ['/etc/elinks.conf', '/.elinks/elinks.conf'], + \ 'elmfilt': ['filter-rules'], + \ 'erlang': ['file.erl', 'file.hrl', 'file.yaws'], + \ 'eruby': ['file.erb', 'file.rhtml'], + \ 'esmtprc': ['anyesmtprc'], + \ 'esqlc': ['file.ec', 'file.EC'], + \ 'esterel': ['file.strl'], + \ 'eterm': ['anyEterm/file.cfg'], + \ 'exim': ['exim.conf'], + \ 'expect': ['file.exp'], + \ 'exports': ['exports'], + \ 'factor': ['file.factor'], + \ 'falcon': ['file.fal'], + \ 'fan': ['file.fan', 'file.fwt'], + \ 'fetchmail': ['.fetchmailrc'], + \ 'fgl': ['file.4gl', 'file.4gh', 'file.m4gl'], + \ 'focexec': ['file.fex', 'file.focexec'], + \ 'forth': ['file.fs', 'file.ft'], + \ 'fortran': ['file.f', 'file.for', 'file.fortran', 'file.fpp', 'file.ftn', 'file.f77', 'file.f90', 'file.f95', 'file.f03', 'file.f08'], + \ 'framescript': ['file.fsl'], + \ 'freebasic': ['file.fb', 'file.bi'], + \ 'fstab': ['fstab', 'mtab'], + \ 'gdb': ['.gdbinit'], + \ 'gdmo': ['file.mo', 'file.gdmo'], + \ 'gedcom': ['file.ged', 'lltxxxxx.txt'], + \ 'gitcommit': ['COMMIT_EDITMSG', 'MERGE_MSG', 'TAG_EDITMSG'], + \ 'gitconfig': ['file.git/config', '.gitconfig', '.gitmodules', 'file.git/modules//config', '/.config/git/config'], + \ 'gitolite': ['gitolite.conf'], + \ 'gitrebase': ['git-rebase-todo'], + \ 'gitsendemail': ['.gitsendemail.msg.xxxxxx'], + \ 'gkrellmrc': ['gkrellmrc', 'gkrellmrc_x'], + \ 'gnash': ['gnashrc', '.gnashrc', 'gnashpluginrc', '.gnashpluginrc'], + \ 'gnuplot': ['file.gpi'], + \ 'go': ['file.go'], + \ 'gp': ['file.gp', '.gprc'], + \ 'gpg': ['/.gnupg/options', '/.gnupg/gpg.conf', '/usr/any/gnupg/options.skel'], + \ 'grads': ['file.gs'], + \ 'gretl': ['file.gretl'], + \ 'groovy': ['file.gradle', 'file.groovy'], + \ 'group': ['any/etc/group', 'any/etc/group-', 'any/etc/group.edit', 'any/etc/gshadow', 'any/etc/gshadow-', 'any/etc/gshadow.edit', 'any/var/backups/group.bak', 'any/var/backups/gshadow.bak'], + \ 'grub': ['/boot/grub/menu.lst', '/boot/grub/grub.conf', '/etc/grub.conf'], + \ 'gsp': ['file.gsp'], + \ 'gtkrc': ['.gtkrc', 'gtkrc'], + \ 'haml': ['file.haml'], + \ 'hamster': ['file.hsc', 'file.hsm'], + \ 'haskell': ['file.hs', 'file.hs-boot'], + \ 'haste': ['file.ht'], + \ 'hastepreproc': ['file.htpp'], + \ 'hb': ['file.hb'], + \ 'hercules': ['file.vc', 'file.ev', 'file.sum', 'file.errsum'], + \ 'hex': ['file.hex', 'file.h32'], + \ 'hgcommit': ['hg-editor-file.txt'], + \ 'hog': ['file.hog', 'snort.conf', 'vision.conf'], + \ 'hostconf': ['/etc/host.conf'], + \ 'hostsaccess': ['/etc/hosts.allow', '/etc/hosts.deny'], + \ 'htmlcheetah': ['file.tmpl'], + \ 'htmlm4': ['file.html.m4'], + \ 'httest': ['file.htt', 'file.htb'], + \ 'ibasic': ['file.iba', 'file.ibi'], + \ 'icemenu': ['/.icewm/menu'], + \ 'icon': ['file.icn'], + \ 'indent': ['.indent.pro', 'indentrc'], + \ 'inform': ['file.inf', 'file.INF'], + \ 'initng': ['/etc/initng/any/file.i', 'file.ii'], + \ 'inittab': ['inittab'], + \ 'ipfilter': ['ipf.conf', 'ipf6.conf', 'ipf.rules'], + \ 'iss': ['file.iss'], + \ 'ist': ['file.ist', 'file.mst'], + \ 'j': ['file.ijs'], + \ 'jal': ['file.jal', 'file.JAL'], + \ 'jam': ['file.jpl', 'file.jpr'], + \ 'java': ['file.java', 'file.jav'], + \ 'javacc': ['file.jj', 'file.jjt'], + \ 'javascript': ['file.js', 'file.javascript', 'file.es', 'file.jsx', 'file.mjs'], + \ 'jess': ['file.clp'], + \ 'jgraph': ['file.jgr'], + \ 'jovial': ['file.jov', 'file.j73', 'file.jovial'], + \ 'jproperties': ['file.properties', 'file.properties_xx', 'file.properties_xx_xx'], + \ 'json': ['file.json', 'file.jsonp', 'file.webmanifest'], + \ 'jsp': ['file.jsp'], + \ 'kconfig': ['Kconfig', 'Kconfig.debug'], + \ 'kivy': ['file.kv'], + \ 'kix': ['file.kix'], + \ 'kscript': ['file.ks'], + \ 'kwt': ['file.k'], + \ 'lace': ['file.ace', 'file.ACE'], + \ 'latte': ['file.latte', 'file.lte'], + \ 'ld': ['file.ld'], + \ 'ldif': ['file.ldif'], + \ 'less': ['file.less'], + \ 'lex': ['file.lex', 'file.l', 'file.lxx', 'file.l++'], + \ 'lftp': ['lftp.conf', '.lftprc', 'anylftp/rc'], + \ 'lhaskell': ['file.lhs'], + \ 'libao': ['/etc/libao.conf', '/.libao'], + \ 'lifelines': ['file.ll'], + \ 'lilo': ['lilo.conf'], + \ 'limits': ['/etc/limits', '/etc/anylimits.conf', '/etc/anylimits.d/file.conf'], + \ 'liquid': ['file.liquid'], + \ 'lisp': ['sbclrc', '.sbclrc'], + \ 'lite': ['file.lite', 'file.lt'], + \ 'litestep': ['/LiteStep/any/file.rc'], + \ 'loginaccess': ['/etc/login.access'], + \ 'logindefs': ['/etc/login.defs'], + \ 'logtalk': ['file.lgt'], + \ 'lotos': ['file.lot', 'file.lotos'], + \ 'lout': ['file.lou', 'file.lout'], + \ 'lprolog': ['file.sig'], + \ 'lsl': ['file.lsl'], + \ 'lss': ['file.lss'], + \ 'lua': ['file.lua', 'file.rockspec', 'file.nse'], + \ 'lynx': ['lynx.cfg'], + \ 'm4': ['file.at'], + \ 'mail': ['snd.123', '.letter', '.letter.123', '.followup', '.article', '.article.123', 'pico.123', 'mutt-xx-xxx', 'muttng-xx-xxx', 'ae123.txt', 'file.eml'], + \ 'mailaliases': ['/etc/mail/aliases', '/etc/aliases'], + \ 'mailcap': ['.mailcap', 'mailcap'], + \ 'make': ['file.mk', 'file.mak', 'file.dsp'], + \ 'mallard': ['file.page'], + \ 'manconf': ['/etc/man.conf', 'man.config'], + \ 'map': ['file.map'], + \ 'maple': ['file.mv', 'file.mpl', 'file.mws'], + \ 'markdown': ['file.markdown', 'file.mdown', 'file.mkd', 'file.mkdn', 'file.mdwn', 'file.md'], + \ 'mason': ['file.mason', 'file.mhtml', 'file.comp'], + \ 'master': ['file.mas', 'file.master'], + \ 'mel': ['file.mel'], + \ 'messages': ['/log/auth', '/log/cron', '/log/daemon', '/log/debug', '/log/kern', '/log/lpr', '/log/mail', '/log/messages', '/log/news/news', '/log/syslog', '/log/user', + \ '/log/auth.log', '/log/cron.log', '/log/daemon.log', '/log/debug.log', '/log/kern.log', '/log/lpr.log', '/log/mail.log', '/log/messages.log', '/log/news/news.log', '/log/syslog.log', '/log/user.log', + \ '/log/auth.err', '/log/cron.err', '/log/daemon.err', '/log/debug.err', '/log/kern.err', '/log/lpr.err', '/log/mail.err', '/log/messages.err', '/log/news/news.err', '/log/syslog.err', '/log/user.err', + \ '/log/auth.info', '/log/cron.info', '/log/daemon.info', '/log/debug.info', '/log/kern.info', '/log/lpr.info', '/log/mail.info', '/log/messages.info', '/log/news/news.info', '/log/syslog.info', '/log/user.info', + \ '/log/auth.warn', '/log/cron.warn', '/log/daemon.warn', '/log/debug.warn', '/log/kern.warn', '/log/lpr.warn', '/log/mail.warn', '/log/messages.warn', '/log/news/news.warn', '/log/syslog.warn', '/log/user.warn', + \ '/log/auth.crit', '/log/cron.crit', '/log/daemon.crit', '/log/debug.crit', '/log/kern.crit', '/log/lpr.crit', '/log/mail.crit', '/log/messages.crit', '/log/news/news.crit', '/log/syslog.crit', '/log/user.crit', + \ '/log/auth.notice', '/log/cron.notice', '/log/daemon.notice', '/log/debug.notice', '/log/kern.notice', '/log/lpr.notice', '/log/mail.notice', '/log/messages.notice', '/log/news/news.notice', '/log/syslog.notice', '/log/user.notice'], + \ 'mf': ['file.mf'], + \ 'mgl': ['file.mgl'], + \ 'mgp': ['file.mgp'], + \ 'mib': ['file.mib', 'file.my'], + \ 'mix': ['file.mix', 'file.mixal'], + \ 'mma': ['file.nb'], + \ 'mmp': ['file.mmp'], + \ 'modconf': ['/etc/modules.conf', '/etc/modules', '/etc/conf.modules'], + \ 'modula2': ['file.m2', 'file.mi'], + \ 'monk': ['file.isc', 'file.monk', 'file.ssc', 'file.tsc'], + \ 'moo': ['file.moo'], + \ 'mp': ['file.mp'], + \ 'mplayerconf': ['mplayer.conf', '/.mplayer/config'], + \ 'mrxvtrc': ['mrxvtrc', '.mrxvtrc'], + \ 'msidl': ['file.odl', 'file.mof'], + \ 'msql': ['file.msql'], + \ 'mupad': ['file.mu'], + \ 'mush': ['file.mush'], + \ 'muttrc': ['Muttngrc', 'Muttrc'], + \ 'mysql': ['file.mysql'], + \ 'n1ql': ['file.n1ql', 'file.nql'], + \ 'named': ['namedfile.conf', 'rndcfile.conf'], + \ 'nanorc': ['/etc/nanorc', 'file.nanorc'], + \ 'ncf': ['file.ncf'], + \ 'netrc': ['.netrc'], + \ 'ninja': ['file.ninja'], + \ 'nqc': ['file.nqc'], + \ 'nroff': ['file.tr', 'file.nr', 'file.roff', 'file.tmac', 'file.mom'], + \ 'nsis': ['file.nsi', 'file.nsh'], + \ 'obj': ['file.obj'], + \ 'ocaml': ['file.ml', 'file.mli', 'file.mll', 'file.mly', '.ocamlinit'], + \ 'occam': ['file.occ'], + \ 'omnimark': ['file.xom', 'file.xin'], + \ 'openroad': ['file.or'], + \ 'ora': ['file.ora'], + \ 'pamconf': ['/etc/pam.conf'], + \ 'papp': ['file.papp', 'file.pxml', 'file.pxsl'], + \ 'pascal': ['file.pas', 'file.dpr'], + \ 'passwd': ['any/etc/passwd', 'any/etc/passwd-', 'any/etc/passwd.edit', 'any/etc/shadow', 'any/etc/shadow-', 'any/etc/shadow.edit', 'any/var/backups/passwd.bak', 'any/var/backups/shadow.bak'], + \ 'pccts': ['file.g'], + \ 'pdf': ['file.pdf'], + \ 'perl': ['file.plx', 'file.al', 'file.psgi', 'gitolite.rc', '.gitolite.rc', 'example.gitolite.rc'], + \ 'perl6': ['file.p6', 'file.pm6', 'file.pl6'], + \ 'pf': ['pf.conf'], + \ 'pfmain': ['main.cf'], + \ 'php': ['file.php', 'file.php9', 'file.phtml', 'file.ctp'], + \ 'pike': ['file.pike', 'file.lpc', 'file.ulpc', 'file.pmod'], + \ 'pilrc': ['file.rcp'], + \ 'pine': ['.pinerc', 'pinerc', '.pinercex', 'pinercex'], + \ 'pinfo': ['/etc/pinforc', '/.pinforc'], + \ 'pli': ['file.pli', 'file.pl1'], + \ 'plm': ['file.plm', 'file.p36', 'file.pac'], + \ 'plp': ['file.plp'], + \ 'plsql': ['file.pls', 'file.plsql'], + \ 'po': ['file.po', 'file.pot'], + \ 'pod': ['file.pod'], + \ 'pod6': ['file.pod6'], + \ 'postscr': ['file.ps', 'file.pfa', 'file.afm', 'file.eps', 'file.epsf', 'file.epsi', 'file.ai'], + \ 'pov': ['file.pov'], + \ 'povini': ['.povrayrc'], + \ 'ppd': ['file.ppd'], + \ 'ppwiz': ['file.it', 'file.ih'], + \ 'privoxy': ['file.action'], + \ 'proc': ['file.pc'], + \ 'procmail': ['.procmail', '.procmailrc'], + \ 'prolog': ['file.pdb'], + \ 'promela': ['file.pml'], + \ 'proto': ['file.proto'], + \ 'protocols': ['/etc/protocols'], + \ 'psf': ['file.psf'], + \ 'pyrex': ['file.pyx', 'file.pxd'], + \ 'python': ['file.py', 'file.pyw', '.pythonstartup', '.pythonrc', 'file.ptl'], + \ 'quake': ['anybaseq2/file.cfg', 'anyid1/file.cfg', 'quake3/file.cfg'], + \ 'radiance': ['file.rad', 'file.mat'], + \ 'ratpoison': ['.ratpoisonrc', 'ratpoisonrc'], + \ 'rc': ['file.rc', 'file.rch'], + \ 'rcs': ['file,v'], + \ 'readline': ['.inputrc', 'inputrc'], + \ 'remind': ['.reminders', 'file.remind', 'file.rem'], + \ 'resolv': ['resolv.conf'], + \ 'reva': ['file.frt'], + \ 'rexx': ['file.rex', 'file.orx', 'file.rxo', 'file.rxj', 'file.jrexx', 'file.rexxj', 'file.rexx', 'file.testGroup', 'file.testUnit'], + \ 'rib': ['file.rib'], + \ 'rnc': ['file.rnc'], + \ 'rng': ['file.rng'], + \ 'robots': ['robots.txt'], + \ 'rpcgen': ['file.x'], + \ 'rpl': ['file.rpl'], + \ 'rst': ['file.rst'], + \ 'rtf': ['file.rtf'], + \ 'ruby': ['.irbrc', 'irbrc', 'file.rb', 'file.rbw', 'file.gemspec', 'file.ru', 'Gemfile', 'file.builder', 'file.rxml', 'file.rjs', 'file.rant', 'file.rake'], + \ 'rust': ['file.rs'], + \ 'samba': ['smb.conf'], + \ 'sas': ['file.sas'], + \ 'sass': ['file.sass'], + \ 'sather': ['file.sa'], + \ 'sbt': ['file.sbt'], + \ 'scala': ['file.scala'], + \ 'scheme': ['file.scm', 'file.ss', 'file.rkt'], + \ 'scilab': ['file.sci', 'file.sce'], + \ 'screen': ['.screenrc', 'screenrc'], + \ 'scss': ['file.scss'], + \ 'sd': ['file.sd'], + \ 'sdc': ['file.sdc'], + \ 'sdl': ['file.sdl', 'file.pr'], + \ 'sed': ['file.sed'], + \ 'sensors': ['/etc/sensors.conf', '/etc/sensors3.conf'], + \ 'services': ['/etc/services'], + \ 'setserial': ['/etc/serial.conf'], + \ 'sh': ['/etc/udev/cdsymlinks.conf'], + \ 'sieve': ['file.siv'], + \ 'simula': ['file.sim'], + \ 'sinda': ['file.sin', 'file.s85'], + \ 'sisu': ['file.sst', 'file.ssm', 'file.ssi', 'file.-sst', 'file._sst', 'file.sst.meta', 'file.-sst.meta', 'file._sst.meta'], + \ 'skill': ['file.il', 'file.ils', 'file.cdf'], + \ 'slang': ['file.sl'], + \ 'slice': ['file.ice'], + \ 'slpconf': ['/etc/slp.conf'], + \ 'slpreg': ['/etc/slp.reg'], + \ 'slpspi': ['/etc/slp.spi'], + \ 'slrnrc': ['.slrnrc'], + \ 'slrnsc': ['file.score'], + \ 'sm': ['sendmail.cf'], + \ 'smarty': ['file.tpl'], + \ 'smcl': ['file.hlp', 'file.ihlp', 'file.smcl'], + \ 'smith': ['file.smt', 'file.smith'], + \ 'sml': ['file.sml'], + \ 'snobol4': ['file.sno', 'file.spt'], + \ 'spec': ['file.spec'], + \ 'spice': ['file.sp', 'file.spice'], + \ 'spup': ['file.speedup', 'file.spdata', 'file.spd'], + \ 'spyce': ['file.spy', 'file.spi'], + \ 'sql': ['file.tyb', 'file.typ', 'file.tyc', 'file.pkb', 'file.pks'], + \ 'sqlj': ['file.sqlj'], + \ 'sqr': ['file.sqr', 'file.sqi'], + \ 'squid': ['squid.conf'], + \ 'srec': ['file.s19', 'file.s28', 'file.s37', 'file.mot', 'file.srec'], + \ 'sshconfig': ['ssh_config', '/.ssh/config'], + \ 'sshdconfig': ['sshd_config'], + \ 'st': ['file.st'], + \ 'stata': ['file.ado', 'file.do', 'file.imata', 'file.mata'], + \ 'stp': ['file.stp'], + \ 'sudoers': ['any/etc/sudoers', 'sudoers.tmp'], + \ 'svg': ['file.svg'], + \ 'svn': ['svn-commitfile.tmp'], + \ 'sysctl': ['/etc/sysctl.conf', '/etc/sysctl.d/file.conf'], + \ 'systemd': ['any/systemd/file.automount', 'any/systemd/file.mount', 'any/systemd/file.path', 'any/systemd/file.service', 'any/systemd/file.socket', 'any/systemd/file.swap', 'any/systemd/file.target', 'any/systemd/file.timer'], + \ 'systemverilog': ['file.sv', 'file.svh'], + \ 'tags': ['tags'], + \ 'tak': ['file.tak'], + \ 'taskdata': ['pending.data', 'completed.data', 'undo.data'], + \ 'taskedit': ['file.task'], + \ 'tcl': ['file.tcl', 'file.tk', 'file.itcl', 'file.itk', 'file.jacl'], + \ 'teraterm': ['file.ttl'], + \ 'terminfo': ['file.ti'], + \ 'tex': ['file.latex', 'file.sty', 'file.dtx', 'file.ltx', 'file.bbl'], + \ 'texinfo': ['file.texinfo', 'file.texi', 'file.txi'], + \ 'texmf': ['texmf.cnf'], + \ 'text': ['file.text', 'README'], + \ 'tf': ['file.tf', '.tfrc', 'tfrc'], + \ 'tidy': ['.tidyrc', 'tidyrc'], + \ 'tilde': ['file.t.html'], + \ 'tli': ['file.tli'], + \ 'tmux': ['tmuxfile.conf', '.tmuxfile.conf'], + \ 'tpp': ['file.tpp'], + \ 'treetop': ['file.treetop'], + \ 'trustees': ['trustees.conf'], + \ 'tsalt': ['file.slt'], + \ 'tsscl': ['file.tsscl'], + \ 'tssgm': ['file.tssgm'], + \ 'tssop': ['file.tssop'], + \ 'twig': ['file.twig'], + \ 'uc': ['file.uc'], + \ 'udevconf': ['/etc/udev/udev.conf'], + \ 'udevperm': ['/etc/udev/permissions.d/file.permissions'], + \ 'uil': ['file.uit', 'file.uil'], + \ 'updatedb': ['/etc/updatedb.conf'], + \ 'upstart': ['/usr/share/upstart/file.conf', '/usr/share/upstart/file.override', '/etc/init/file.conf', '/etc/init/file.override', '/.init/file.conf', '/.init/file.override', '/.config/upstart/file.conf', '/.config/upstart/file.override'], + \ 'upstreamdat': ['upstream.dat', 'UPSTREAM.DAT', 'upstream.file.dat', 'UPSTREAM.FILE.DAT', 'file.upstream.dat', 'FILE.UPSTREAM.DAT'], + \ 'upstreaminstalllog': ['upstreaminstall.log', 'UPSTREAMINSTALL.LOG', 'upstreaminstall.file.log', 'UPSTREAMINSTALL.FILE.LOG', 'file.upstreaminstall.log', 'FILE.UPSTREAMINSTALL.LOG'], + \ 'upstreamlog': ['fdrupstream.log', 'upstream.log', 'UPSTREAM.LOG', 'upstream.file.log', 'UPSTREAM.FILE.LOG', 'file.upstream.log', 'FILE.UPSTREAM.LOG', 'UPSTREAM-file.log', 'UPSTREAM-FILE.LOG'], + \ 'usserverlog': ['usserver.log', 'USSERVER.LOG', 'usserver.file.log', 'USSERVER.FILE.LOG', 'file.usserver.log', 'FILE.USSERVER.LOG'], + \ 'usw2kagtlog': ['usw2kagt.log', 'USW2KAGT.LOG', 'usw2kagt.file.log', 'USW2KAGT.FILE.LOG', 'file.usw2kagt.log', 'FILE.USW2KAGT.LOG'], + \ 'vb': ['file.sba', 'file.vb', 'file.vbs', 'file.dsm', 'file.ctl'], + \ 'vera': ['file.vr', 'file.vri', 'file.vrh'], + \ 'verilog': ['file.v'], + \ 'verilogams': ['file.va', 'file.vams'], + \ 'vgrindefs': ['vgrindefs'], + \ 'vhdl': ['file.hdl', 'file.vhd', 'file.vhdl', 'file.vbe', 'file.vst'], + \ 'vim': ['file.vim', 'file.vba', '.exrc', '_exrc'], + \ 'viminfo': ['.viminfo', '_viminfo'], + \ 'vmasm': ['file.mar'], + \ 'voscm': ['file.cm'], + \ 'vrml': ['file.wrl'], + \ 'vroom': ['file.vroom'], + \ 'webmacro': ['file.wm'], + \ 'wget': ['.wgetrc', 'wgetrc'], + \ 'winbatch': ['file.wbt'], + \ 'wml': ['file.wml'], + \ 'wsml': ['file.wsml'], + \ 'wvdial': ['wvdial.conf', '.wvdialrc'], + \ 'xdefaults': ['.Xdefaults', '.Xpdefaults', '.Xresources', 'xdm-config', 'file.ad'], + \ 'xhtml': ['file.xhtml', 'file.xht'], + \ 'xinetd': ['/etc/xinetd.conf'], + \ 'xmath': ['file.msc', 'file.msf'], + \ 'xml': ['/etc/blkid.tab', '/etc/blkid.tab.old', 'file.xmi', 'file.csproj', 'file.csproj.user', 'file.ts', 'file.ui', 'file.tpm', '/etc/xdg/menus/file.menu', 'fglrxrc', 'file.xlf', 'file.xliff', 'file.xul'], + \ 'xmodmap': ['anyXmodmap'], + \ 'xf86conf': ['xorg.conf', 'xorg.conf-4'], + \ 'xpm2': ['file.xpm2'], + \ 'xquery': ['file.xq', 'file.xql', 'file.xqm', 'file.xquery', 'file.xqy'], + \ 'xs': ['file.xs'], + \ 'xsd': ['file.xsd'], + \ 'xslt': ['file.xsl', 'file.xslt'], + \ 'yacc': ['file.yy', 'file.yxx', 'file.y++'], + \ 'yaml': ['file.yaml', 'file.yml'], + \ 'z8a': ['file.z8a'], + \ 'zimbu': ['file.zu'], + \ 'zimbutempl': ['file.zut'], + \ 'zsh': ['.zprofile', '/etc/zprofile', '.zfbfmarks', 'file.zsh'], + \ + \ 'aap': ['file.aap'], + \ 'help': [$VIMRUNTIME . '/doc/help.txt'], + \ 'xpm': ['file.xpm'], + \ } + +let s:filename_case_checks = { + \ 'modula2': ['file.DEF', 'file.MOD'], + \ } + +func CheckItems(checks) + for [ft, names] in items(a:checks) + for i in range(0, len(names) - 1) + new + try + exe 'edit ' . names[i] + catch + call assert_report('cannot edit "' . names[i] . '": ' . v:errmsg) + endtry + call assert_equal(ft, &filetype, 'with file name: ' . names[i]) + bwipe! + endfor + endfor +endfunc + +func Test_filetype_detection() + filetype on + call CheckItems(s:filename_checks) + if has('fname_case') + call CheckItems(s:filename_case_checks) + endif + filetype off +endfunc + +" Filetypes detected from the file contents by scripts.vim +let s:script_checks = { + \ 'virata': [['% Virata'], + \ ['', '% Virata'], + \ ['', '', '% Virata'], + \ ['', '', '', '% Virata'], + \ ['', '', '', '', '% Virata']], + \ 'strace': [['execve("/usr/bin/pstree", ["pstree"], 0x7ff0 /* 63 vars */) = 0'], + \ ['15:17:47 execve("/usr/bin/pstree", ["pstree"], ... "_=/usr/bin/strace"]) = 0'], + \ ['__libc_start_main and something']], + \ } + +func Test_script_detection() + filetype on + for [ft, files] in items(s:script_checks) + for file in files + call writefile(file, 'Xtest') + split Xtest + call assert_equal(ft, &filetype, 'for text: ' . string(file)) + bwipe! + endfor + endfor + call delete('Xtest') + filetype off +endfunc + diff --git a/src/nvim/testdir/test_find_complete.vim b/src/nvim/testdir/test_find_complete.vim new file mode 100644 index 0000000000..4732109ed0 --- /dev/null +++ b/src/nvim/testdir/test_find_complete.vim @@ -0,0 +1,157 @@ +" Tests for the 'find' command completion. + +" Do all the tests in a separate window to avoid E211 when we recursively +" delete the Xfind directory during cleanup +func Test_find_complete() + set belloff=all + + " On windows a stale "Xfind" directory may exist, remove it so that + " we start from a clean state. + call delete("Xfind", "rf") + let cwd = getcwd() + let test_out = cwd . '/test.out' + call mkdir('Xfind') + cd Xfind + + new + set path= + call assert_fails('call feedkeys(":find\t\n", "xt")', 'E345:') + close + + new + set path=. + call assert_fails('call feedkeys(":find\t\n", "xt")', 'E32:') + close + + new + set path=.,, + call assert_fails('call feedkeys(":find\t\n", "xt")', 'E32:') + close + + new + set path=./** + call assert_fails('call feedkeys(":find\t\n", "xt")', 'E32:') + close + + " We shouldn't find any file till this point + + call mkdir('in/path', 'p') + exe 'cd ' . cwd + call writefile(['Holy Grail'], 'Xfind/file.txt') + call writefile(['Jimmy Hoffa'], 'Xfind/in/file.txt') + call writefile(['Another Holy Grail'], 'Xfind/in/stuff.txt') + call writefile(['E.T.'], 'Xfind/in/path/file.txt') + + new + set path=Xfind/** + call feedkeys(":find file\t\n", "xt") + call assert_equal('Holy Grail', getline(1)) + call feedkeys(":find file\t\t\n", "xt") + call assert_equal('Jimmy Hoffa', getline(1)) + call feedkeys(":find file\t\t\t\n", "xt") + call assert_equal('E.T.', getline(1)) + + " Rerun the previous three find completions, using fullpath in 'path' + exec "set path=" . cwd . "/Xfind/**" + + call feedkeys(":find file\t\n", "xt") + call assert_equal('Holy Grail', getline(1)) + call feedkeys(":find file\t\t\n", "xt") + call assert_equal('Jimmy Hoffa', getline(1)) + call feedkeys(":find file\t\t\t\n", "xt") + call assert_equal('E.T.', getline(1)) + + " Same steps again, using relative and fullpath items that point to the same + " recursive location. + " This is to test that there are no duplicates in the completion list. + set path+=Xfind/** + call feedkeys(":find file\t\n", "xt") + call assert_equal('Holy Grail', getline(1)) + call feedkeys(":find file\t\t\n", "xt") + call assert_equal('Jimmy Hoffa', getline(1)) + call feedkeys(":find file\t\t\t\n", "xt") + call assert_equal('E.T.', getline(1)) + call feedkeys(":find file\t\t\n", "xt") + + " Test find completion for directory of current buffer, which at this point + " is Xfind/in/file.txt. + set path=. + call feedkeys(":find st\t\n", "xt") + call assert_equal('Another Holy Grail', getline(1)) + + " Test find completion for empty path item ",," which is the current + " directory + cd Xfind + set path=,, + call feedkeys(":find f\t\n", "xt") + call assert_equal('Holy Grail', getline(1)) + + " Test shortening of + " + " foo/x/bar/voyager.txt + " foo/y/bar/voyager.txt + " + " When current directory is above foo/ they should be shortened to (in order + " of appearance): + " + " x/bar/voyager.txt + " y/bar/voyager.txt + call mkdir('foo/x/bar', 'p') + call mkdir('foo/y/bar', 'p') + call writefile(['Voyager 1'], 'foo/x/bar/voyager.txt') + call writefile(['Voyager 2'], 'foo/y/bar/voyager.txt') + + exec "set path=" . cwd . "/Xfind/**" + call feedkeys(":find voyager\t\n", "xt") + call assert_equal('Voyager 1', getline(1)) + call feedkeys(":find voyager\t\t\n", "xt") + call assert_equal('Voyager 2', getline(1)) + + " + " When current directory is .../foo/y/bar they should be shortened to (in + " order of appearance): + " + " ./voyager.txt + " x/bar/voyager.txt + cd foo/y/bar + call feedkeys(":find voyager\t\n", "xt") + call assert_equal('Voyager 2', getline(1)) + call feedkeys(":find voyager\t\t\n", "xt") + call assert_equal('Voyager 1', getline(1)) + + " Check the opposite too: + cd ../../x/bar + call feedkeys(":find voyager\t\n", "xt") + call assert_equal('Voyager 1', getline(1)) + call feedkeys(":find voyager\t\t\n", "xt") + call assert_equal('Voyager 2', getline(1)) + + " Check for correct handling of shorten_fname()'s behavior on windows + exec "cd " . cwd . "/Xfind/in" + call feedkeys(":find file\t\n", "xt") + call assert_equal('Jimmy Hoffa', getline(1)) + + " Test for relative to current buffer 'path' item + exec "cd " . cwd . "/Xfind/" + set path=./path + " Open the file where Jimmy Hoffa is found + e in/file.txt + " Find the file containing 'E.T.' in the Xfind/in/path directory + call feedkeys(":find file\t\n", "xt") + call assert_equal('E.T.', getline(1)) + + " Test that completion works when path=.,, + set path=.,, + " Open Jimmy Hoffa file + e in/file.txt + call assert_equal('Jimmy Hoffa', getline(1)) + + " Search for the file containing Holy Grail in same directory as in/path.txt + call feedkeys(":find stu\t\n", "xt") + call assert_equal('Another Holy Grail', getline(1)) + + enew | only + exe 'cd ' . cwd + call delete('Xfind', 'rf') + set path& +endfunc diff --git a/src/nvim/testdir/test_findfile.vim b/src/nvim/testdir/test_findfile.vim new file mode 100644 index 0000000000..d9a89801ea --- /dev/null +++ b/src/nvim/testdir/test_findfile.vim @@ -0,0 +1,25 @@ +" Test for findfile() +" +func Test_findfile() + new + let cwd=getcwd() + cd .. + + " Tests may be run from a shadow directory, so an extra cd needs to be done to + " get above src/ + if fnamemodify(getcwd(), ':t') != 'src' + cd ../.. + else + cd .. + endif + set ssl + + call assert_equal('src/nvim/testdir/test_findfile.vim', findfile('test_findfile.vim','src/nvim/test*')) + exe "cd" cwd + cd .. + call assert_equal('testdir/test_findfile.vim', findfile('test_findfile.vim','test*')) + call assert_equal('testdir/test_findfile.vim', findfile('test_findfile.vim','testdir')) + + exe "cd" cwd + q! +endfunc diff --git a/src/nvim/testdir/test_fixeol.vim b/src/nvim/testdir/test_fixeol.vim new file mode 100644 index 0000000000..32cb059e26 --- /dev/null +++ b/src/nvim/testdir/test_fixeol.vim @@ -0,0 +1,48 @@ +" Tests for 'fixeol' and 'eol' +func Test_fixeol() + " first write two test files – with and without trailing EOL + " use Unix fileformat for consistency + set ff=unix + enew! + call setline('.', 'with eol') + w! XXEol + enew! + set noeol nofixeol + call setline('.', 'without eol') + w! XXNoEol + set eol fixeol + bwipe XXEol XXNoEol + + " try editing files with 'fixeol' disabled + e! XXEol + normal ostays eol + set nofixeol + w! XXTestEol + e! XXNoEol + normal ostays without + set nofixeol + w! XXTestNoEol + bwipe! XXEol XXNoEol XXTestEol XXTestNoEol + set fixeol + + " Append "END" to each file so that we can see what the last written char + " was. + normal ggdGaEND + w >>XXEol + w >>XXNoEol + w >>XXTestEol + w >>XXTestNoEol + + call assert_equal(['with eol', 'END'], readfile('XXEol')) + call assert_equal(['without eolEND'], readfile('XXNoEol')) + call assert_equal(['with eol', 'stays eol', 'END'], readfile('XXTestEol')) + call assert_equal(['without eol', 'stays withoutEND'], + \ readfile('XXTestNoEol')) + + call delete('XXEol') + call delete('XXNoEol') + call delete('XXTestEol') + call delete('XXTestNoEol') + set ff& fixeol& eol& + enew! +endfunc diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim index 46c54e8614..7c6d38d7ec 100644 --- a/src/nvim/testdir/test_fold.vim +++ b/src/nvim/testdir/test_fold.vim @@ -360,3 +360,24 @@ func! Test_move_folds_around_indent() call assert_equal([0, 1, 1, 1, 1, 0, 0, 0, 1, 1], map(range(1, line('$')), 'foldlevel(v:val)')) bw! endfunc + +" test for patch 7.3.637 +" Cannot catch the error caused by a foldopen when there is no fold. +func Test_foldopen_exception() + enew! + let a = 'No error caught' + try + foldopen + catch + let a = matchstr(v:exception,'^[^ ]*') + endtry + call assert_equal('Vim(foldopen):E490:', a) + + let a = 'No error caught' + try + foobar + catch + let a = matchstr(v:exception,'^[^ ]*') + endtry + call assert_match('E492:', a) +endfunc diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 237a2dc820..398e9ab331 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -191,4 +191,165 @@ func Test_toupper() call assert_equal("ⱥ ⱦ", tolower("Ⱥ Ⱦ")) endfunc +" Tests for the mode() function +let current_modes = '' +func! Save_mode() + let g:current_modes = mode(0) . '-' . mode(1) + return '' +endfunc + +func! Test_mode() + new + call append(0, ["Blue Ball Black", "Brown Band Bowl", ""]) + + inoremap <F2> <C-R>=Save_mode()<CR> + + normal! 3G + exe "normal i\<F2>\<Esc>" + call assert_equal('i-i', g:current_modes) + " i_CTRL-P: Multiple matches + exe "normal i\<C-G>uBa\<C-P>\<F2>\<Esc>u" + call assert_equal('i-ic', g:current_modes) + " i_CTRL-P: Single match + exe "normal iBro\<C-P>\<F2>\<Esc>u" + call assert_equal('i-ic', g:current_modes) + " i_CTRL-X + exe "normal iBa\<C-X>\<F2>\<Esc>u" + call assert_equal('i-ix', g:current_modes) + " i_CTRL-X CTRL-P: Multiple matches + exe "normal iBa\<C-X>\<C-P>\<F2>\<Esc>u" + call assert_equal('i-ic', g:current_modes) + " i_CTRL-X CTRL-P: Single match + exe "normal iBro\<C-X>\<C-P>\<F2>\<Esc>u" + call assert_equal('i-ic', g:current_modes) + " i_CTRL-X CTRL-P + CTRL-P: Single match + exe "normal iBro\<C-X>\<C-P>\<C-P>\<F2>\<Esc>u" + call assert_equal('i-ic', g:current_modes) + " i_CTRL-X CTRL-L: Multiple matches + exe "normal i\<C-X>\<C-L>\<F2>\<Esc>u" + call assert_equal('i-ic', g:current_modes) + " i_CTRL-X CTRL-L: Single match + exe "normal iBlu\<C-X>\<C-L>\<F2>\<Esc>u" + call assert_equal('i-ic', g:current_modes) + " i_CTRL-P: No match + exe "normal iCom\<C-P>\<F2>\<Esc>u" + call assert_equal('i-ic', g:current_modes) + " i_CTRL-X CTRL-P: No match + exe "normal iCom\<C-X>\<C-P>\<F2>\<Esc>u" + call assert_equal('i-ic', g:current_modes) + " i_CTRL-X CTRL-L: No match + exe "normal iabc\<C-X>\<C-L>\<F2>\<Esc>u" + call assert_equal('i-ic', g:current_modes) + + " R_CTRL-P: Multiple matches + exe "normal RBa\<C-P>\<F2>\<Esc>u" + call assert_equal('R-Rc', g:current_modes) + " R_CTRL-P: Single match + exe "normal RBro\<C-P>\<F2>\<Esc>u" + call assert_equal('R-Rc', g:current_modes) + " R_CTRL-X + exe "normal RBa\<C-X>\<F2>\<Esc>u" + call assert_equal('R-Rx', g:current_modes) + " R_CTRL-X CTRL-P: Multiple matches + exe "normal RBa\<C-X>\<C-P>\<F2>\<Esc>u" + call assert_equal('R-Rc', g:current_modes) + " R_CTRL-X CTRL-P: Single match + exe "normal RBro\<C-X>\<C-P>\<F2>\<Esc>u" + call assert_equal('R-Rc', g:current_modes) + " R_CTRL-X CTRL-P + CTRL-P: Single match + exe "normal RBro\<C-X>\<C-P>\<C-P>\<F2>\<Esc>u" + call assert_equal('R-Rc', g:current_modes) + " R_CTRL-X CTRL-L: Multiple matches + exe "normal R\<C-X>\<C-L>\<F2>\<Esc>u" + call assert_equal('R-Rc', g:current_modes) + " R_CTRL-X CTRL-L: Single match + exe "normal RBlu\<C-X>\<C-L>\<F2>\<Esc>u" + call assert_equal('R-Rc', g:current_modes) + " R_CTRL-P: No match + exe "normal RCom\<C-P>\<F2>\<Esc>u" + call assert_equal('R-Rc', g:current_modes) + " R_CTRL-X CTRL-P: No match + exe "normal RCom\<C-X>\<C-P>\<F2>\<Esc>u" + call assert_equal('R-Rc', g:current_modes) + " R_CTRL-X CTRL-L: No match + exe "normal Rabc\<C-X>\<C-L>\<F2>\<Esc>u" + call assert_equal('R-Rc', g:current_modes) + + call assert_equal('n', mode(0)) + call assert_equal('n', mode(1)) + + " How to test operator-pending mode? + + call feedkeys("v", 'xt') + call assert_equal('v', mode()) + call assert_equal('v', mode(1)) + call feedkeys("\<Esc>V", 'xt') + call assert_equal('V', mode()) + call assert_equal('V', mode(1)) + call feedkeys("\<Esc>\<C-V>", 'xt') + call assert_equal("\<C-V>", mode()) + call assert_equal("\<C-V>", mode(1)) + call feedkeys("\<Esc>", 'xt') + + call feedkeys("gh", 'xt') + call assert_equal('s', mode()) + call assert_equal('s', mode(1)) + call feedkeys("\<Esc>gH", 'xt') + call assert_equal('S', mode()) + call assert_equal('S', mode(1)) + call feedkeys("\<Esc>g\<C-H>", 'xt') + call assert_equal("\<C-S>", mode()) + call assert_equal("\<C-S>", mode(1)) + call feedkeys("\<Esc>", 'xt') + + call feedkeys(":echo \<C-R>=Save_mode()\<C-U>\<CR>", 'xt') + call assert_equal('c-c', g:current_modes) + call feedkeys("gQecho \<C-R>=Save_mode()\<CR>\<CR>vi\<CR>", 'xt') + call assert_equal('c-cv', g:current_modes) + " How to test Ex mode? + + bwipe! + iunmap <F2> +endfunc + +func Test_getbufvar() + let bnr = bufnr('%') + let b:var_num = '1234' + let def_num = '5678' + call assert_equal('1234', getbufvar(bnr, 'var_num')) + call assert_equal('1234', getbufvar(bnr, 'var_num', def_num)) + + let bd = getbufvar(bnr, '') + call assert_equal('1234', bd['var_num']) + call assert_true(exists("bd['changedtick']")) + call assert_equal(2, len(bd)) + + let bd2 = getbufvar(bnr, '', def_num) + call assert_equal(bd, bd2) + + unlet b:var_num + call assert_equal(def_num, getbufvar(bnr, 'var_num', def_num)) + call assert_equal('', getbufvar(bnr, 'var_num')) + let bd = getbufvar(bnr, '') + call assert_equal(1, len(bd)) + let bd = getbufvar(bnr, '',def_num) + call assert_equal(1, len(bd)) + + call assert_equal('', getbufvar(9999, '')) + call assert_equal(def_num, getbufvar(9999, '', def_num)) + unlet def_num + + call assert_equal(0, getbufvar(bnr, '&autoindent')) + call assert_equal(0, getbufvar(bnr, '&autoindent', 1)) + + " Open new window with forced option values + set fileformats=unix,dos + new ++ff=dos ++bin ++enc=iso-8859-2 + call assert_equal('dos', getbufvar(bufnr('%'), '&fileformat')) + call assert_equal(1, getbufvar(bufnr('%'), '&bin')) + call assert_equal('iso-8859-2', getbufvar(bufnr('%'), '&fenc')) + close + + set fileformats& +endfunc diff --git a/src/nvim/testdir/test_ga.vim b/src/nvim/testdir/test_ga.vim new file mode 100644 index 0000000000..f9357ddc87 --- /dev/null +++ b/src/nvim/testdir/test_ga.vim @@ -0,0 +1,37 @@ +" Test ga normal command, and :ascii Ex command. +func Do_ga(c) + call setline(1, a:c) + let l:a = execute("norm 1goga") + let l:b = execute("ascii") + call assert_equal(l:a, l:b) + return l:a +endfunc + +func Test_ga_command() + new + set display=uhex + call assert_equal("\nNUL", Do_ga('')) + call assert_equal("\n<<01>> 1, Hex 01, Octal 001", Do_ga("\x01")) + call assert_equal("\n<<09>> 9, Hex 09, Octal 011", Do_ga("\t")) + + set display= + call assert_equal("\nNUL", Do_ga('')) + call assert_equal("\n<^A> 1, Hex 01, Octal 001", Do_ga("\x01")) + call assert_equal("\n<^I> 9, Hex 09, Octal 011", Do_ga("\t")) + + call assert_equal("\n<e> 101, Hex 65, Octal 145", Do_ga('e')) + + if !has('multi_byte') + return + endif + + " Test a few multi-bytes characters. + call assert_equal("\n<é> 233, Hex 00e9, Octal 351", Do_ga('é')) + call assert_equal("\n<ẻ> 7867, Hex 1ebb, Octal 17273", Do_ga('ẻ')) + + " Test with combining characters. + call assert_equal("\n<e> 101, Hex 65, Octal 145 < ́> 769, Hex 0301, Octal 1401", Do_ga("e\u0301")) + call assert_equal("\n<e> 101, Hex 65, Octal 145 < ́> 769, Hex 0301, Octal 1401 < ̱> 817, Hex 0331, Octal 1461", Do_ga("e\u0301\u0331")) + call assert_equal("\n<e> 101, Hex 65, Octal 145 < ́> 769, Hex 0301, Octal 1401 < ̱> 817, Hex 0331, Octal 1461 < ̸> 824, Hex 0338, Octal 1470", Do_ga("e\u0301\u0331\u0338")) + bwipe! +endfunc diff --git a/src/nvim/testdir/test_goto.vim b/src/nvim/testdir/test_goto.vim index b6ac5720c3..2573401707 100644 --- a/src/nvim/testdir/test_goto.vim +++ b/src/nvim/testdir/test_goto.vim @@ -1,22 +1,277 @@ " Test commands that jump somewhere. -func Test_geeDEE() +" Create a new buffer using "lines" and place the cursor on the word after the +" first occurrence of return and invoke "cmd". The cursor should now be +" positioned at the given line and col. +func XTest_goto_decl(cmd, lines, line, col) new - call setline(1, ["Filename x;", "", "int Filename", "int func() {", "Filename y;"]) - /y;/ - normal gD - call assert_equal(1, line('.')) + call setline(1, a:lines) + /return/ + normal! W + execute 'norm! ' . a:cmd + call assert_equal(a:line, line('.')) + call assert_equal(a:col, col('.')) quit! endfunc -func Test_gee_dee() - new - call setline(1, ["int x;", "", "int func(int x)", "{", " return x;", "}"]) - /return/ - normal $hgd - call assert_equal(3, line('.')) - call assert_equal(14, col('.')) - quit! +func Test_gD() + let lines = [ + \ 'int x;', + \ '', + \ 'int func(void)', + \ '{', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gD', lines, 1, 5) +endfunc + +func Test_gD_too() + let lines = [ + \ 'Filename x;', + \ '', + \ 'int Filename', + \ 'int func() {', + \ ' Filename x;', + \ ' return x;', + \ ] + call XTest_goto_decl('gD', lines, 1, 10) +endfunc + +func Test_gD_comment() + let lines = [ + \ '/* int x; */', + \ 'int x;', + \ '', + \ 'int func(void)', + \ '{', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gD', lines, 2, 5) +endfunc + +func Test_gD_inline_comment() + let lines = [ + \ 'int y /* , x */;', + \ 'int x;', + \ '', + \ 'int func(void)', + \ '{', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gD', lines, 2, 5) +endfunc + +func Test_gD_string() + let lines = [ + \ 'char *s[] = "x";', + \ 'int x = 1;', + \ '', + \ 'int func(void)', + \ '{', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gD', lines, 2, 5) +endfunc + +func Test_gD_string_same_line() + let lines = [ + \ 'char *s[] = "x", int x = 1;', + \ '', + \ 'int func(void)', + \ '{', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gD', lines, 1, 22) +endfunc + +func Test_gD_char() + let lines = [ + \ "char c = 'x';", + \ 'int x = 1;', + \ '', + \ 'int func(void)', + \ '{', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gD', lines, 2, 5) +endfunc + +func Test_gd() + let lines = [ + \ 'int x;', + \ '', + \ 'int func(int x)', + \ '{', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gd', lines, 3, 14) +endfunc + +func Test_gd_not_local() + let lines = [ + \ 'int func1(void)', + \ '{', + \ ' return x;', + \ '}', + \ '', + \ 'int func2(int x)', + \ '{', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gd', lines, 3, 10) +endfunc + +func Test_gd_kr_style() + let lines = [ + \ 'int func(x)', + \ ' int x;', + \ '{', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gd', lines, 2, 7) +endfunc + +func Test_gd_missing_braces() + let lines = [ + \ 'def func1(a)', + \ ' a + 1', + \ 'end', + \ '', + \ 'a = 1', + \ '', + \ 'def func2()', + \ ' return a', + \ 'end', + \ ] + call XTest_goto_decl('gd', lines, 1, 11) +endfunc + +func Test_gd_comment() + let lines = [ + \ 'int func(void)', + \ '{', + \ ' /* int x; */', + \ ' int x;', + \ ' return x;', + \ '}', + \] + call XTest_goto_decl('gd', lines, 4, 7) +endfunc + +func Test_gd_comment_in_string() + let lines = [ + \ 'int func(void)', + \ '{', + \ ' char *s ="//"; int x;', + \ ' int x;', + \ ' return x;', + \ '}', + \] + call XTest_goto_decl('gd', lines, 3, 22) +endfunc + +func Test_gd_string_in_comment() + set comments= + let lines = [ + \ 'int func(void)', + \ '{', + \ ' /* " */ int x;', + \ ' int x;', + \ ' return x;', + \ '}', + \] + call XTest_goto_decl('gd', lines, 3, 15) + set comments& +endfunc + +func Test_gd_inline_comment() + let lines = [ + \ 'int func(/* x is an int */ int x)', + \ '{', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gd', lines, 1, 32) +endfunc + +func Test_gd_inline_comment_only() + let lines = [ + \ 'int func(void) /* one lonely x */', + \ '{', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gd', lines, 3, 10) +endfunc + +func Test_gd_inline_comment_body() + let lines = [ + \ 'int func(void)', + \ '{', + \ ' int y /* , x */;', + \ '', + \ ' for (/* int x = 0 */; y < 2; y++);', + \ '', + \ ' int x = 0;', + \ '', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gd', lines, 7, 7) +endfunc + +func Test_gd_trailing_multiline_comment() + let lines = [ + \ 'int func(int x) /* x is an int */', + \ '{', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gd', lines, 1, 14) +endfunc + +func Test_gd_trailing_comment() + let lines = [ + \ 'int func(int x) // x is an int', + \ '{', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gd', lines, 1, 14) +endfunc + +func Test_gd_string() + let lines = [ + \ 'int func(void)', + \ '{', + \ ' char *s = "x";', + \ ' int x = 1;', + \ '', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gd', lines, 4, 7) +endfunc + +func Test_gd_string_only() + let lines = [ + \ 'int func(void)', + \ '{', + \ ' char *s = "x";', + \ '', + \ ' return x;', + \ '}', + \ ] + call XTest_goto_decl('gd', lines, 5, 10) endfunc " Check that setting 'cursorline' does not change curswant diff --git a/src/nvim/testdir/test_hardcopy.vim b/src/nvim/testdir/test_hardcopy.vim index ea9790d134..7aea704e86 100644 --- a/src/nvim/testdir/test_hardcopy.vim +++ b/src/nvim/testdir/test_hardcopy.vim @@ -50,6 +50,7 @@ endfunc " We don't check much of the contents. func Test_with_syntax() if has('postscript') + edit test_hardcopy.vim set printoptions=syntax:y syn on hardcopy > Xhardcopy diff --git a/src/nvim/testdir/test_help_tagjump.vim b/src/nvim/testdir/test_help_tagjump.vim index 1ca0f722cf..06c48d8e76 100644 --- a/src/nvim/testdir/test_help_tagjump.vim +++ b/src/nvim/testdir/test_help_tagjump.vim @@ -18,6 +18,52 @@ func Test_help_tagjump() call assert_true(getline('.') =~ '\*help.txt\*') helpclose + help | + call assert_equal("help", &filetype) + call assert_true(getline('.') =~ '\*bar\*') + helpclose + + help "* + call assert_equal("help", &filetype) + call assert_true(getline('.') =~ '\*quotestar\*') + helpclose + + help sp?it + call assert_equal("help", &filetype) + call assert_true(getline('.') =~ '\*:split\*') + helpclose + + help :? + call assert_equal("help", &filetype) + call assert_true(getline('.') =~ '\*:?\*') + helpclose + + help FileW*Post + call assert_equal("help", &filetype) + call assert_true(getline('.') =~ '\*FileWritePost\*') + helpclose + + help `ls` + call assert_equal("help", &filetype) + call assert_true(getline('.') =~ '\*:ls\*') + helpclose + + help ^X + call assert_equal("help", &filetype) + call assert_true(getline('.') =~ '\*CTRL-X\*') + helpclose + + help i_^_CTRL-D + call assert_equal("help", &filetype) + call assert_true(getline('.') =~ '\*i_^_CTRL-D\*') + helpclose + + exec "help \<C-V>" + call assert_equal("help", &filetype) + call assert_true(getline('.') =~ '\*CTRL-V\*') + helpclose + + exec "help! ('textwidth'" call assert_equal("help", &filetype) call assert_true(getline('.') =~ "\\*'textwidth'\\*") @@ -47,6 +93,16 @@ func Test_help_tagjump() call assert_equal("help", &filetype) call assert_true(getline('.') =~ '\*{address}\*') helpclose + + exusage + call assert_equal("help", &filetype) + call assert_true(getline('.') =~ '\*:index\*') + helpclose + + viusage + call assert_equal("help", &filetype) + call assert_true(getline('.') =~ '\*normal-index\*') + helpclose endfunc let s:langs = ['en', 'ab', 'ja'] @@ -89,17 +145,8 @@ func s:doc_config_teardown() endif endfunc -func s:get_cmd_compl_list(cmd) - let list = [] - let str = '' - for cnt in range(1, 999) - call feedkeys(a:cmd . repeat("\<Tab>", cnt) . "'\<C-B>let str='\<CR>", 'tx') - if str ==# a:cmd[1:] - break - endif - call add(list, str) - endfor - return list +func s:get_help_compl_list(cmd) + return getcompletion(a:cmd, 'help') endfunc func Test_help_complete() @@ -111,49 +158,49 @@ func Test_help_complete() if has('multi_lang') set helplang= endif - let list = s:get_cmd_compl_list(":h test") - call assert_equal(['h test-col', 'h test-char'], list) + let list = s:get_help_compl_list("test") + call assert_equal(['test-col', 'test-char'], list) if has('multi_lang') " 'helplang=ab' and help file lang is 'en' set helplang=ab - let list = s:get_cmd_compl_list(":h test") - call assert_equal(['h test-col', 'h test-char'], list) + let list = s:get_help_compl_list("test") + call assert_equal(['test-col', 'test-char'], list) " 'helplang=' and help file lang is 'en' and 'ab' set rtp+=Xdir1/doc-ab set helplang= - let list = s:get_cmd_compl_list(":h test") - call assert_equal(sort(['h test-col@en', 'h test-col@ab', - \ 'h test-char@en', 'h test-char@ab']), sort(list)) + let list = s:get_help_compl_list("test") + call assert_equal(sort(['test-col@en', 'test-col@ab', + \ 'test-char@en', 'test-char@ab']), sort(list)) " 'helplang=ab' and help file lang is 'en' and 'ab' set helplang=ab - let list = s:get_cmd_compl_list(":h test") - call assert_equal(sort(['h test-col', 'h test-col@en', - \ 'h test-char', 'h test-char@en']), sort(list)) + let list = s:get_help_compl_list("test") + call assert_equal(sort(['test-col', 'test-col@en', + \ 'test-char', 'test-char@en']), sort(list)) " 'helplang=' and help file lang is 'en', 'ab' and 'ja' set rtp+=Xdir1/doc-ja set helplang= - let list = s:get_cmd_compl_list(":h test") - call assert_equal(sort(['h test-col@en', 'h test-col@ab', - \ 'h test-col@ja', 'h test-char@en', - \ 'h test-char@ab', 'h test-char@ja']), sort(list)) + let list = s:get_help_compl_list("test") + call assert_equal(sort(['test-col@en', 'test-col@ab', + \ 'test-col@ja', 'test-char@en', + \ 'test-char@ab', 'test-char@ja']), sort(list)) " 'helplang=ab' and help file lang is 'en', 'ab' and 'ja' set helplang=ab - let list = s:get_cmd_compl_list(":h test") - call assert_equal(sort(['h test-col', 'h test-col@en', - \ 'h test-col@ja', 'h test-char', - \ 'h test-char@en', 'h test-char@ja']), sort(list)) + let list = s:get_help_compl_list("test") + call assert_equal(sort(['test-col', 'test-col@en', + \ 'test-col@ja', 'test-char', + \ 'test-char@en', 'test-char@ja']), sort(list)) " 'helplang=ab,ja' and help file lang is 'en', 'ab' and 'ja' set helplang=ab,ja - let list = s:get_cmd_compl_list(":h test") - call assert_equal(sort(['h test-col', 'h test-col@ja', - \ 'h test-col@en', 'h test-char', - \ 'h test-char@ja', 'h test-char@en']), sort(list)) + let list = s:get_help_compl_list("test") + call assert_equal(sort(['test-col', 'test-col@ja', + \ 'test-col@en', 'test-char', + \ 'test-char@ja', 'test-char@en']), sort(list)) endif catch call assert_exception('X') diff --git a/src/nvim/testdir/test_hide.vim b/src/nvim/testdir/test_hide.vim new file mode 100644 index 0000000000..128b8ff945 --- /dev/null +++ b/src/nvim/testdir/test_hide.vim @@ -0,0 +1,97 @@ +" Tests for :hide command/modifier and 'hidden' option + +function SetUp() + let s:save_hidden = &hidden + let s:save_bufhidden = &bufhidden + let s:save_autowrite = &autowrite + set nohidden + set bufhidden= + set noautowrite +endfunc + +function TearDown() + let &hidden = s:save_hidden + let &bufhidden = s:save_bufhidden + let &autowrite = s:save_autowrite +endfunc + +function Test_hide() + let orig_bname = bufname('') + let orig_winnr = winnr('$') + + new Xf1 + set modified + call assert_fails('edit Xf2') + bwipeout! Xf1 + + new Xf1 + set modified + edit! Xf2 + call assert_equal(['Xf2', 2], [bufname(''), winnr('$')]) + call assert_equal([1, 0], [buflisted('Xf1'), bufloaded('Xf1')]) + bwipeout! Xf1 + bwipeout! Xf2 + + new Xf1 + set modified + " :hide as a command + hide + call assert_equal([orig_bname, orig_winnr], [bufname(''), winnr('$')]) + call assert_equal([1, 1], [buflisted('Xf1'), bufloaded('Xf1')]) + bwipeout! Xf1 + + new Xf1 + set modified + " :hide as a command with trailing comment + hide " comment + call assert_equal([orig_bname, orig_winnr], [bufname(''), winnr('$')]) + call assert_equal([1, 1], [buflisted('Xf1'), bufloaded('Xf1')]) + bwipeout! Xf1 + + new Xf1 + set modified + " :hide as a command with bar + hide | new Xf2 " comment + call assert_equal(['Xf2', 2], [bufname(''), winnr('$')]) + call assert_equal([1, 1], [buflisted('Xf1'), bufloaded('Xf1')]) + bwipeout! Xf1 + bwipeout! Xf2 + + new Xf1 + set modified + " :hide as a modifier with trailing comment + hide edit Xf2 " comment + call assert_equal(['Xf2', 2], [bufname(''), winnr('$')]) + call assert_equal([1, 1], [buflisted('Xf1'), bufloaded('Xf1')]) + bwipeout! Xf1 + bwipeout! Xf2 + + new Xf1 + set modified + " To check that the bar is not recognized to separate commands + hide echo "one|two" + call assert_equal(['Xf1', 2], [bufname(''), winnr('$')]) + call assert_equal([1, 1], [buflisted('Xf1'), bufloaded('Xf1')]) + bwipeout! Xf1 + + " set hidden + new Xf1 + set hidden + set modified + edit Xf2 " comment + call assert_equal(['Xf2', 2], [bufname(''), winnr('$')]) + call assert_equal([1, 1], [buflisted('Xf1'), bufloaded('Xf1')]) + bwipeout! Xf1 + bwipeout! Xf2 + + " set hidden bufhidden=wipe + new Xf1 + set bufhidden=wipe + set modified + hide edit! Xf2 " comment + call assert_equal(['Xf2', 2], [bufname(''), winnr('$')]) + call assert_equal([0, 0], [buflisted('Xf1'), bufloaded('Xf1')]) + bwipeout! Xf2 +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_history.vim b/src/nvim/testdir/test_history.vim index 3163b344d3..ca31e3f06c 100644 --- a/src/nvim/testdir/test_history.vim +++ b/src/nvim/testdir/test_history.vim @@ -31,6 +31,30 @@ function History_Tests(hist) call assert_equal('ls', histget(a:hist, -1)) call assert_equal(4, histnr(a:hist)) + let a=execute('history ' . a:hist) + call assert_match("^\n # \\S* history\n 3 buffers\n> 4 ls$", a) + let a=execute('history all') + call assert_match("^\n # .* history\n 3 buffers\n> 4 ls", a) + + if len(a:hist) > 0 + let a=execute('history ' . a:hist . ' 2') + call assert_match("^\n # \\S* history$", a) + let a=execute('history ' . a:hist . ' 3') + call assert_match("^\n # \\S* history\n 3 buffers$", a) + let a=execute('history ' . a:hist . ' 4') + call assert_match("^\n # \\S* history\n> 4 ls$", a) + let a=execute('history ' . a:hist . ' 3,4') + call assert_match("^\n # \\S* history\n 3 buffers\n> 4 ls$", a) + let a=execute('history ' . a:hist . ' -1') + call assert_match("^\n # \\S* history\n> 4 ls$", a) + let a=execute('history ' . a:hist . ' -2') + call assert_match("^\n # \\S* history\n 3 buffers$", a) + let a=execute('history ' . a:hist . ' -2,') + call assert_match("^\n # \\S* history\n 3 buffers\n> 4 ls$", a) + let a=execute('history ' . a:hist . ' -3') + call assert_match("^\n # \\S* history$", a) + endif + " Test for removing entries matching a pattern for i in range(1, 3) call histadd(a:hist, 'text_' . i) diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim new file mode 100644 index 0000000000..c307e33cbf --- /dev/null +++ b/src/nvim/testdir/test_ins_complete.vim @@ -0,0 +1,219 @@ + +" Test for insert expansion +func Test_ins_complete() + edit test_ins_complete.vim + " The files in the current directory interferes with the files + " used by this test. So use a separate directory for the test. + call mkdir('Xdir') + cd Xdir + + set ff=unix + call writefile(["test11\t36Gepeto\t/Tag/", + \ "asd\ttest11file\t36G", + \ "Makefile\tto\trun"], 'Xtestfile') + call writefile(['', 'start of testfile', + \ 'ru', + \ 'run1', + \ 'run2', + \ 'STARTTEST', + \ 'ENDTEST', + \ 'end of testfile'], 'Xtestdata') + set ff& + + enew! + edit Xtestdata + new + call append(0, ['#include "Xtestfile"', '']) + call cursor(2, 1) + + set cot= + set cpt=.,w + " add-expands (word from next line) from other window + exe "normal iru\<C-N>\<C-N>\<C-X>\<C-N>\<Esc>\<C-A>" + call assert_equal('run1 run3', getline('.')) + " add-expands (current buffer first) + exe "normal o\<C-P>\<C-X>\<C-N>" + call assert_equal('run3 run3', getline('.')) + " Local expansion, ends in an empty line (unless it becomes a global + " expansion) + exe "normal o\<C-X>\<C-P>\<C-P>\<C-P>\<C-P>\<C-P>" + call assert_equal('', getline('.')) + " starts Local and switches to global add-expansion + exe "normal o\<C-X>\<C-P>\<C-P>\<C-X>\<C-X>\<C-N>\<C-X>\<C-N>\<C-N>" + call assert_equal('run1 run2', getline('.')) + + set cpt=.,w,i + " i-add-expands and switches to local + exe "normal OM\<C-N>\<C-X>\<C-N>\<C-X>\<C-N>\<C-X>\<C-X>\<C-X>\<C-P>" + call assert_equal("Makefile\tto\trun3", getline('.')) + " add-expands lines (it would end in an empty line if it didn't ignored + " itself) + exe "normal o\<C-X>\<C-L>\<C-X>\<C-L>\<C-P>\<C-P>" + call assert_equal("Makefile\tto\trun3", getline('.')) + call assert_equal("Makefile\tto\trun3", getline(line('.') - 1)) + + set cpt=kXtestfile + " checks k-expansion, and file expansion (use Xtest11 instead of test11, + " because TEST11.OUT may match first on DOS) + write Xtest11.one + write Xtest11.two + exe "normal o\<C-N>\<Esc>IX\<Esc>A\<C-X>\<C-F>\<C-N>" + call assert_equal('Xtest11.two', getline('.')) + + " use CTRL-X CTRL-F to complete Xtest11.one, remove it and then use CTRL-X + " CTRL-F again to verify this doesn't cause trouble. + exe "normal oXt\<C-X>\<C-F>\<BS>\<BS>\<BS>\<BS>\<BS>\<BS>\<BS>\<BS>\<C-X>\<C-F>" + call assert_equal('Xtest11.one', getline('.')) + normal ddk + + set cpt=w + " checks make_cyclic in other window + exe "normal oST\<C-N>\<C-P>\<C-P>\<C-P>\<C-P>" + call assert_equal('STARTTEST', getline('.')) + + set cpt=u nohid + " checks unloaded buffer expansion + only + exe "normal oEN\<C-N>" + call assert_equal('ENDTEST', getline('.')) + " checks adding mode abortion + exe "normal ounl\<C-N>\<C-X>\<C-X>\<C-P>" + call assert_equal('unless', getline('.')) + + set cpt=t,d def=^\\k* tags=Xtestfile notagbsearch + " tag expansion, define add-expansion interrupted + exe "normal o\<C-X>\<C-]>\<C-X>\<C-D>\<C-X>\<C-D>\<C-X>\<C-X>\<C-D>\<C-X>\<C-D>\<C-X>\<C-D>\<C-X>\<C-D>" + call assert_equal('test11file 36Gepeto /Tag/ asd', getline('.')) + " t-expansion + exe "normal oa\<C-N>\<Esc>" + call assert_equal('asd', getline('.')) + + %bw! + call delete('Xtestfile') + call delete('Xtest11.one') + call delete('Xtest11.two') + call delete('Xtestdata') + set cpt& cot& def& tags& tagbsearch& hidden& + cd .. + call delete('Xdir', 'rf') +endfunc + +function! s:CompleteDone_CompleteFuncDict( findstart, base ) + if a:findstart + return 0 + endif + + return { + \ 'words': [ + \ { + \ 'word': 'aword', + \ 'abbr': 'wrd', + \ 'menu': 'extra text', + \ 'info': 'words are cool', + \ 'kind': 'W', + \ 'user_data': 'test' + \ } + \ ] + \ } +endfunction + +function! s:CompleteDone_CheckCompletedItemDict() + call assert_equal( 'aword', v:completed_item[ 'word' ] ) + call assert_equal( 'wrd', v:completed_item[ 'abbr' ] ) + call assert_equal( 'extra text', v:completed_item[ 'menu' ] ) + call assert_equal( 'words are cool', v:completed_item[ 'info' ] ) + call assert_equal( 'W', v:completed_item[ 'kind' ] ) + call assert_equal( 'test', v:completed_item[ 'user_data' ] ) + + let s:called_completedone = 1 +endfunction + +function Test_CompleteDoneDict() + au CompleteDone * :call <SID>CompleteDone_CheckCompletedItemDict() + + set completefunc=<SID>CompleteDone_CompleteFuncDict + execute "normal a\<C-X>\<C-U>\<C-Y>" + set completefunc& + + call assert_equal( 'test', v:completed_item[ 'user_data' ] ) + call assert_true( s:called_completedone ) + + let s:called_completedone = 0 + au! CompleteDone +endfunc + +function! s:CompleteDone_CompleteFuncDictNoUserData( findstart, base ) + if a:findstart + return 0 + endif + + return { + \ 'words': [ + \ { + \ 'word': 'aword', + \ 'abbr': 'wrd', + \ 'menu': 'extra text', + \ 'info': 'words are cool', + \ 'kind': 'W' + \ } + \ ] + \ } +endfunction + +function! s:CompleteDone_CheckCompletedItemDictNoUserData() + call assert_equal( 'aword', v:completed_item[ 'word' ] ) + call assert_equal( 'wrd', v:completed_item[ 'abbr' ] ) + call assert_equal( 'extra text', v:completed_item[ 'menu' ] ) + call assert_equal( 'words are cool', v:completed_item[ 'info' ] ) + call assert_equal( 'W', v:completed_item[ 'kind' ] ) + call assert_equal( '', v:completed_item[ 'user_data' ] ) + + let s:called_completedone = 1 +endfunction + +function Test_CompleteDoneDictNoUserData() + au CompleteDone * :call <SID>CompleteDone_CheckCompletedItemDictNoUserData() + + set completefunc=<SID>CompleteDone_CompleteFuncDictNoUserData + execute "normal a\<C-X>\<C-U>\<C-Y>" + set completefunc& + + call assert_equal( '', v:completed_item[ 'user_data' ] ) + call assert_true( s:called_completedone ) + + let s:called_completedone = 0 + au! CompleteDone +endfunc + +function! s:CompleteDone_CompleteFuncList( findstart, base ) + if a:findstart + return 0 + endif + + return [ 'aword' ] +endfunction + +function! s:CompleteDone_CheckCompletedItemList() + call assert_equal( 'aword', v:completed_item[ 'word' ] ) + call assert_equal( '', v:completed_item[ 'abbr' ] ) + call assert_equal( '', v:completed_item[ 'menu' ] ) + call assert_equal( '', v:completed_item[ 'info' ] ) + call assert_equal( '', v:completed_item[ 'kind' ] ) + call assert_equal( '', v:completed_item[ 'user_data' ] ) + + let s:called_completedone = 1 +endfunction + +function Test_CompleteDoneList() + au CompleteDone * :call <SID>CompleteDone_CheckCompletedItemList() + + set completefunc=<SID>CompleteDone_CompleteFuncList + execute "normal a\<C-X>\<C-U>\<C-Y>" + set completefunc& + + call assert_equal( '', v:completed_item[ 'user_data' ] ) + call assert_true( s:called_completedone ) + + let s:called_completedone = 0 + au! CompleteDone +endfunc diff --git a/src/nvim/testdir/test_let.vim b/src/nvim/testdir/test_let.vim new file mode 100644 index 0000000000..24c6ef5e01 --- /dev/null +++ b/src/nvim/testdir/test_let.vim @@ -0,0 +1,27 @@ +" Tests for the :let command. + +func Test_let() + " Test to not autoload when assigning. It causes internal error. + set runtimepath+=./sautest + let Test104#numvar = function('tr') + call assert_equal("function('tr')", string(Test104#numvar)) + + let a = 1 + let b = 2 + + let out = execute('let a b') + let s = "\na #1\nb #2" + call assert_equal(s, out) + + let out = execute('let {0 == 1 ? "a" : "b"}') + let s = "\nb #2" + call assert_equal(s, out) + + let out = execute('let {0 == 1 ? "a" : "b"} a') + let s = "\nb #2\na #1" + call assert_equal(s, out) + + let out = execute('let a {0 == 1 ? "a" : "b"}') + let s = "\na #1\nb #2" + call assert_equal(s, out) +endfunc diff --git a/src/nvim/testdir/test_lineending.vim b/src/nvim/testdir/test_lineending.vim new file mode 100644 index 0000000000..5be3be8db3 --- /dev/null +++ b/src/nvim/testdir/test_lineending.vim @@ -0,0 +1,19 @@ +" Tests for saving/loading a file with some lines ending in +" CTRL-M, some not +func Test_lineending() + let l = ["this line ends in a\<CR>", + \ "this one doesn't", + \ "this one does\<CR>", + \ "and the last one doesn't"] + set fileformat=dos + enew! + call append(0, l) + $delete + write Xfile1 + bwipe Xfile1 + edit Xfile1 + let t = getline(1, '$') + call assert_equal(l, t) + new | only + call delete('Xfile1') +endfunc diff --git a/src/nvim/testdir/test_lispwords.vim b/src/nvim/testdir/test_lispwords.vim new file mode 100644 index 0000000000..4c05504cf1 --- /dev/null +++ b/src/nvim/testdir/test_lispwords.vim @@ -0,0 +1,82 @@ +" Tests for 'lispwords' settings being global-local + +set nocompatible viminfo+=nviminfo + +func Test_global_local_lispwords() + setglobal lispwords=foo,bar,baz + setlocal lispwords-=foo | setlocal lispwords+=quux + call assert_equal('foo,bar,baz', &g:lispwords) + call assert_equal('bar,baz,quux', &l:lispwords) + call assert_equal('bar,baz,quux', &lispwords) + + setlocal lispwords< + call assert_equal('foo,bar,baz', &g:lispwords) + call assert_equal('foo,bar,baz', &l:lispwords) + call assert_equal('foo,bar,baz', &lispwords) +endfunc + +func Test_lisp_indent() + enew! + + call append(0, [ + \ '(defun html-file (base)', + \ '(format nil "~(~A~).html" base))', + \ '', + \ '(defmacro page (name title &rest body)', + \ '(let ((ti (gensym)))', + \ '`(with-open-file (*standard-output*', + \ '(html-file ,name)', + \ ':direction :output', + \ ':if-exists :supersede)', + \ '(let ((,ti ,title))', + \ '(as title ,ti)', + \ '(with center ', + \ '(as h2 (string-upcase ,ti)))', + \ '(brs 3)', + \ ',@body))))', + \ '', + \ ';;; Utilities for generating links', + \ '', + \ '(defmacro with-link (dest &rest body)', + \ '`(progn', + \ '(format t "<a href=\"~A\">" (html-file ,dest))', + \ ',@body', + \ '(princ "</a>")))' + \ ]) + set lisp + set lispwords& + let save_copt = &cpoptions + set cpoptions+=p + normal 1G=G + + call assert_equal([ + \ '(defun html-file (base)', + \ ' (format nil "~(~A~).html" base))', + \ '', + \ '(defmacro page (name title &rest body)', + \ ' (let ((ti (gensym)))', + \ ' `(with-open-file (*standard-output*', + \ ' (html-file ,name)', + \ ' :direction :output', + \ ' :if-exists :supersede)', + \ ' (let ((,ti ,title))', + \ ' (as title ,ti)', + \ ' (with center ', + \ ' (as h2 (string-upcase ,ti)))', + \ ' (brs 3)', + \ ' ,@body))))', + \ '', + \ ';;; Utilities for generating links', + \ '', + \ '(defmacro with-link (dest &rest body)', + \ ' `(progn', + \ ' (format t "<a href=\"~A\">" (html-file ,dest))', + \ ' ,@body', + \ ' (princ "</a>")))', + \ '' + \ ], getline(1, "$")) + + enew! + let &cpoptions=save_copt + set nolisp +endfunc diff --git a/src/nvim/testdir/test_listchars.vim b/src/nvim/testdir/test_listchars.vim new file mode 100644 index 0000000000..57ea7ca5a9 --- /dev/null +++ b/src/nvim/testdir/test_listchars.vim @@ -0,0 +1,63 @@ +" Tests for 'listchars' display with 'list' and :list + +source view_util.vim + +func Test_listchars() + enew! + set ff=unix + set list + + set listchars+=tab:>-,space:.,trail:< + call append(0, [ + \ ' aa ', + \ ' bb ', + \ ' cccc ', + \ 'dd ee ', + \ ' ' + \ ]) + let expected = [ + \ '>-------aa>-----$', + \ '..bb>---<<$', + \ '...cccc><$', + \ 'dd........ee<<>-$', + \ '<$' + \ ] + redraw! + for i in range(1, 5) + call cursor(i, 1) + call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$'))) + endfor + + set listchars-=trail:< + let expected = [ + \ '>-------aa>-----$', + \ '..bb>---..$', + \ '...cccc>.$', + \ 'dd........ee..>-$', + \ '.$' + \ ] + redraw! + for i in range(1, 5) + call cursor(i, 1) + call assert_equal([expected[i - 1]], ScreenLines(i, virtcol('$'))) + endfor + + set listchars+=trail:< + set nolist + normal ggdG + call append(0, [ + \ ' fff ', + \ ' gg ', + \ ' h ', + \ 'iii ', + \ ]) + let l = split(execute("%list"), "\n") + call assert_equal([ + \ '..fff>--<<$', + \ '>-------gg>-----$', + \ '.....h>-$', + \ 'iii<<<<><<$', '$'], l) + + enew! + set listchars& ff& +endfunc diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim new file mode 100644 index 0000000000..023332c90a --- /dev/null +++ b/src/nvim/testdir/test_listdict.vim @@ -0,0 +1,603 @@ +" Tests for the List and Dict types + +func TearDown() + " Run garbage collection after every test + call test_garbagecollect_now() +endfunc + +" Tests for List type + +" List creation +func Test_list_create() + " Creating List directly with different types + let l = [1, 'as''d', [1, 2, function("strlen")], {'a': 1},] + call assert_equal("[1, 'as''d', [1, 2, function('strlen')], {'a': 1}]", string(l)) + call assert_equal({'a' : 1}, l[-1]) + call assert_equal(1, l[-4]) + let x = 10 + try + let x = l[-5] + catch + call assert_match('E684:', v:exception) + endtry + call assert_equal(10, x) +endfunc + +" List slices +func Test_list_slice() + let l = [1, 'as''d', [1, 2, function("strlen")], {'a': 1},] + call assert_equal([1, 'as''d', [1, 2, function('strlen')], {'a': 1}], l[:]) + call assert_equal(['as''d', [1, 2, function('strlen')], {'a': 1}], l[1:]) + call assert_equal([1, 'as''d', [1, 2, function('strlen')]], l[:-2]) + call assert_equal([1, 'as''d', [1, 2, function('strlen')], {'a': 1}], l[0:8]) + call assert_equal([], l[8:-1]) +endfunc + +" List identity +func Test_list_identity() + let l = [1, 'as''d', [1, 2, function("strlen")], {'a': 1},] + let ll = l + let lx = copy(l) + call assert_true(l == ll) + call assert_false(l isnot ll) + call assert_true(l is ll) + call assert_true(l == lx) + call assert_false(l is lx) + call assert_true(l isnot lx) +endfunc + +" removing items with :unlet +func Test_list_unlet() + let l = [1, 'as''d', [1, 2, function("strlen")], {'a': 1},] + unlet l[2] + call assert_equal([1, 'as''d', {'a': 1}], l) + let l = range(8) + unlet l[:3] + unlet l[1:] + call assert_equal([4], l) + + " removing items out of range: silently skip items that don't exist + let l = [0, 1, 2, 3] + call assert_fails('unlet l[2:1]', 'E684') + let l = [0, 1, 2, 3] + unlet l[2:2] + call assert_equal([0, 1, 3], l) + let l = [0, 1, 2, 3] + unlet l[2:3] + call assert_equal([0, 1], l) + let l = [0, 1, 2, 3] + unlet l[2:4] + call assert_equal([0, 1], l) + let l = [0, 1, 2, 3] + unlet l[2:5] + call assert_equal([0, 1], l) + let l = [0, 1, 2, 3] + call assert_fails('unlet l[-1:2]', 'E684') + let l = [0, 1, 2, 3] + unlet l[-2:2] + call assert_equal([0, 1, 3], l) + let l = [0, 1, 2, 3] + unlet l[-3:2] + call assert_equal([0, 3], l) + let l = [0, 1, 2, 3] + unlet l[-4:2] + call assert_equal([3], l) + let l = [0, 1, 2, 3] + unlet l[-5:2] + call assert_equal([3], l) + let l = [0, 1, 2, 3] + unlet l[-6:2] + call assert_equal([3], l) +endfunc + +" assignment to a list +func Test_list_assign() + let l = [0, 1, 2, 3] + let [va, vb] = l[2:3] + call assert_equal([2, 3], [va, vb]) + call assert_fails('let [va, vb] = l', 'E687') + call assert_fails('let [va, vb] = l[1:1]', 'E688') +endfunc + +" test for range assign +func Test_list_range_assign() + let l = [0] + let l[:] = [1, 2] + call assert_equal([1, 2], l) +endfunc + +" Tests for Dictionary type + +func Test_dict() + " Creating Dictionary directly with different types + let d = {001: 'asd', 'b': [1, 2, function('strlen')], -1: {'a': 1},} + call assert_equal("{'1': 'asd', 'b': [1, 2, function('strlen')], '-1': {'a': 1}}", string(d)) + call assert_equal('asd', d.1) + call assert_equal(['-1', '1', 'b'], sort(keys(d))) + call assert_equal(['asd', [1, 2, function('strlen')], {'a': 1}], values(d)) + let v = [] + for [key, val] in items(d) + call extend(v, [key, val]) + unlet key val + endfor + call assert_equal(['1','asd','b',[1, 2, function('strlen')],'-1',{'a': 1}], v) + + call extend(d, {3:33, 1:99}) + call extend(d, {'b':'bbb', 'c':'ccc'}, "keep") + call assert_fails("call extend(d, {3:333,4:444}, 'error')", 'E737') + call assert_equal({'c': 'ccc', '1': 99, 'b': [1, 2, function('strlen')], '3': 33, '-1': {'a': 1}}, d) + call filter(d, 'v:key =~ ''[ac391]''') + call assert_equal({'c': 'ccc', '1': 99, '3': 33, '-1': {'a': 1}}, d) +endfunc + +" Dictionary identity +func Test_dict_identity() + let d = {001: 'asd', 'b': [1, 2, function('strlen')], -1: {'a': 1},} + let dd = d + let dx = copy(d) + call assert_true(d == dd) + call assert_false(d isnot dd) + call assert_true(d is dd) + call assert_true(d == dx) + call assert_false(d is dx) + call assert_true(d isnot dx) +endfunc + +" removing items with :unlet +func Test_dict_unlet() + let d = {'b':'bbb', '1': 99, '3': 33, '-1': {'a': 1}} + unlet d.b + unlet d[-1] + call assert_equal({'1': 99, '3': 33}, d) +endfunc + +" manipulating a big Dictionary (hashtable.c has a border of 1000 entries) +func Test_dict_big() + let d = {} + for i in range(1500) + let d[i] = 3000 - i + endfor + call assert_equal([3000, 2900, 2001, 1600, 1501], [d[0], d[100], d[999], d[1400], d[1499]]) + let str = '' + try + let n = d[1500] + catch + let str=substitute(v:exception, '\v(.{14}).*( \d{4}).*', '\1\2', '') + endtry + call assert_equal('Vim(let):E716: 1500', str) + + " lookup each items + for i in range(1500) + call assert_equal(3000 - i, d[i]) + endfor + let i += 1 + + " delete even items + while i >= 2 + let i -= 2 + unlet d[i] + endwhile + call assert_equal('NONE', get(d, 1500 - 100, 'NONE')) + call assert_equal(2999, d[1]) + + " delete odd items, checking value, one intentionally wrong + let d[33] = 999 + let i = 1 + while i < 1500 + if i != 33 + call assert_equal(3000 - i, d[i]) + else + call assert_equal(999, d[i]) + endif + unlet d[i] + let i += 2 + endwhile + call assert_equal({}, d) + unlet d +endfunc + +" Dictionary function +func Test_dict_func() + let d = {} + func d.func(a) dict + return a:a . len(self.data) + endfunc + let d.data = [1,2,3] + call assert_equal('len: 3', d.func('len: ')) + let x = d.func('again: ') + call assert_equal('again: 3', x) + let Fn = d.func + call assert_equal('xxx3', Fn('xxx')) +endfunc + +" Function in script-local List or Dict +func Test_script_local_dict_func() + let g:dict = {} + function g:dict.func() dict + return 'g:dict.func' . self.foo[1] . self.foo[0]('asdf') + endfunc + let g:dict.foo = ['-', 2, 3] + call insert(g:dict.foo, function('strlen')) + call assert_equal('g:dict.func-4', g:dict.func()) + unlet g:dict +endfunc + +" Nasty: remove func from Dict that's being called (works) +func Test_dict_func_remove_in_use() + let d = {1:1} + func d.func(a) + return "a:" . a:a + endfunc + let expected = 'a:' . string(get(d, 'func')) + call assert_equal(expected, d.func(string(remove(d, 'func')))) +endfunc + +" Nasty: deepcopy() dict that refers to itself (fails when noref used) +func Test_dict_deepcopy() + let d = {1:1, 2:2} + let l = [4, d, 6] + let d[3] = l + let dc = deepcopy(d) + call assert_fails('call deepcopy(d, 1)', 'E698') + let l2 = [0, l, l, 3] + let l[1] = l2 + let l3 = deepcopy(l2) + call assert_true(l3[1] is l3[2]) +endfunc + +" Locked variables +func Test_list_locked_var() + let expected = [ + \ [['0000-000', 'ppppppp'], + \ ['0000-000', 'ppppppp'], + \ ['0000-000', 'ppppppp']], + \ [['1000-000', 'ppppppF'], + \ ['0000-000', 'ppppppp'], + \ ['0000-000', 'ppppppp']], + \ [['1100-100', 'ppFppFF'], + \ ['0000-000', 'ppppppp'], + \ ['0000-000', 'ppppppp']], + \ [['1110-110', 'pFFpFFF'], + \ ['0010-010', 'pFppFpp'], + \ ['0000-000', 'ppppppp']], + \ [['1111-111', 'FFFFFFF'], + \ ['0011-011', 'FFpFFpp'], + \ ['0000-000', 'ppppppp']] + \ ] + for depth in range(5) + for u in range(3) + unlet! l + let l = [0, [1, [2, 3]], {4: 5, 6: {7: 8}}] + exe "lockvar " . depth . " l" + if u == 1 + exe "unlockvar l" + elseif u == 2 + exe "unlockvar " . depth . " l" + endif + let ps = islocked("l").islocked("l[1]").islocked("l[1][1]").islocked("l[1][1][0]").'-'.islocked("l[2]").islocked("l[2]['6']").islocked("l[2]['6'][7]") + call assert_equal(expected[depth][u][0], ps) + let ps = '' + try + let l[1][1][0] = 99 + let ps .= 'p' + catch + let ps .= 'F' + endtry + try + let l[1][1] = [99] + let ps .= 'p' + catch + let ps .= 'F' + endtry + try + let l[1] = [99] + let ps .= 'p' + catch + let ps .= 'F' + endtry + try + let l[2]['6'][7] = 99 + let ps .= 'p' + catch + let ps .= 'F' + endtry + try + let l[2][6] = {99: 99} + let ps .= 'p' + catch + let ps .= 'F' + endtry + try + let l[2] = {99: 99} + let ps .= 'p' + catch + let ps .= 'F' + endtry + try + let l = [99] + let ps .= 'p' + catch + let ps .= 'F' + endtry + call assert_equal(expected[depth][u][1], ps) + endfor + endfor +endfunc + +" Unletting locked variables +func Test_list_locked_var_unlet() + let expected = [ + \ [['0000-000', 'ppppppp'], + \ ['0000-000', 'ppppppp'], + \ ['0000-000', 'ppppppp']], + \ [['1000-000', 'ppFppFp'], + \ ['0000-000', 'ppppppp'], + \ ['0000-000', 'ppppppp']], + \ [['1100-100', 'pFFpFFp'], + \ ['0000-000', 'ppppppp'], + \ ['0000-000', 'ppppppp']], + \ [['1110-110', 'FFFFFFp'], + \ ['0010-010', 'FppFppp'], + \ ['0000-000', 'ppppppp']], + \ [['1111-111', 'FFFFFFp'], + \ ['0011-011', 'FppFppp'], + \ ['0000-000', 'ppppppp']] + \ ] + + for depth in range(5) + for u in range(3) + unlet! l + let l = [0, [1, [2, 3]], {4: 5, 6: {7: 8}}] + exe "lockvar " . depth . " l" + if u == 1 + exe "unlockvar l" + elseif u == 2 + exe "unlockvar " . depth . " l" + endif + let ps = islocked("l").islocked("l[1]").islocked("l[1][1]").islocked("l[1][1][0]").'-'.islocked("l[2]").islocked("l[2]['6']").islocked("l[2]['6'][7]") + call assert_equal(expected[depth][u][0], ps) + let ps = '' + try + unlet l[2]['6'][7] + let ps .= 'p' + catch + let ps .= 'F' + endtry + try + unlet l[2][6] + let ps .= 'p' + catch + let ps .= 'F' + endtry + try + unlet l[2] + let ps .= 'p' + catch + let ps .= 'F' + endtry + try + unlet l[1][1][0] + let ps .= 'p' + catch + let ps .= 'F' + endtry + try + unlet l[1][1] + let ps .= 'p' + catch + let ps .= 'F' + endtry + try + unlet l[1] + let ps .= 'p' + catch + let ps .= 'F' + endtry + try + unlet l + let ps .= 'p' + catch + let ps .= 'F' + endtry + call assert_equal(expected[depth][u][1], ps) + endfor + endfor +endfunc + +" Locked variables and :unlet or list / dict functions + +" No :unlet after lock on dict: +func Test_dict_lock_unlet() + unlet! d + let d = {'a': 99, 'b': 100} + lockvar 1 d + call assert_fails('unlet d.a', 'E741') +endfunc + +" unlet after lock on dict item +func Test_dict_item_lock_unlet() + unlet! d + let d = {'a': 99, 'b': 100} + lockvar d.a + unlet d.a + call assert_equal({'b' : 100}, d) +endfunc + +" filter() after lock on dict item +func Test_dict_lock_filter() + unlet! d + let d = {'a': 99, 'b': 100} + lockvar d.a + call filter(d, 'v:key != "a"') + call assert_equal({'b' : 100}, d) +endfunc + +" map() after lock on dict +func Test_dict_lock_map() + unlet! d + let d = {'a': 99, 'b': 100} + lockvar 1 d + call map(d, 'v:val + 200') + call assert_equal({'a' : 299, 'b' : 300}, d) +endfunc + +" No extend() after lock on dict item +func Test_dict_lock_extend() + unlet! d + let d = {'a': 99, 'b': 100} + lockvar d.a + call assert_fails("call extend(d, {'a' : 123})", 'E741') + call assert_equal({'a': 99, 'b': 100}, d) +endfunc + +" No remove() of write-protected scope-level variable +func! Tfunc(this_is_a_long_parameter_name) + call assert_fails("call remove(a:, 'this_is_a_long_parameter_name')", 'E795') +endfun +func Test_dict_scope_var_remove() + call Tfunc('testval') +endfunc + +" No extend() of write-protected scope-level variable +func! Tfunc(this_is_a_long_parameter_name) + call assert_fails("call extend(a:, {'this_is_a_long_parameter_name': 1234})", 'E742') +endfunc +func Test_dict_scope_var_extend() + call Tfunc('testval') +endfunc + +" No :unlet of variable in locked scope +func Test_lock_var_unlet() + let b:testvar = 123 + lockvar 1 b: + call assert_fails('unlet b:testvar', 'E741:') + unlockvar 1 b: + unlet! b:testvar +endfunc + +" No :let += of locked list variable +func Test_let_lock_list() + let l = ['a', 'b', 3] + lockvar 1 l + call assert_fails("let l += ['x']", 'E741:') + call assert_equal(['a', 'b', 3], l) + + unlet l + let l = [1, 2, 3, 4] + lockvar! l + call assert_equal([1, 2, 3, 4], l) + unlockvar l[1] + call assert_fails('unlet l[0:1]', 'E741:') + call assert_equal([1, 2, 3, 4], l) + call assert_fails('unlet l[1:2]', 'E741:') + call assert_equal([1, 2, 3, 4], l) + unlockvar l[1] + call assert_fails('let l[0:1] = [0, 1]', 'E741:') + call assert_equal([1, 2, 3, 4], l) + call assert_fails('let l[1:2] = [0, 1]', 'E741:') + call assert_equal([1, 2, 3, 4], l) + unlet l +endfunc + +" lockvar/islocked() triggering script autoloading +func Test_lockvar_script_autoload() + let old_rtp = &rtp + set rtp+=./sautest + lockvar g:footest#x + unlockvar g:footest#x + call assert_equal(-1, islocked('g:footest#x')) + call assert_equal(0, exists('g:footest#x')) + call assert_equal(1, g:footest#x) + let &rtp = old_rtp +endfunc + +" a:000 function argument test +func s:arg_list_test(...) + call assert_fails('let a:000 = [1, 2]', 'E46:') + call assert_fails('let a:000[0] = 9', 'E742:') + call assert_fails('let a:000[2] = [9, 10]', 'E742:') + call assert_fails('let a:000[3] = {9 : 10}', 'E742:') + + " now the tests that should pass + let a:000[2][1] = 9 + call extend(a:000[2], [5, 6]) + let a:000[3][5] = 8 + let a:000[3]['a'] = 12 + call assert_equal([1, 2, [3, 9, 5, 6], {'a': 12, '5': 8}], a:000) +endfunc + +func Test_func_arg_list() + call s:arg_list_test(1, 2, [3, 4], {5: 6}) +endfunc + +" Tests for reverse(), sort(), uniq() +func Test_reverse_sort_uniq() + let l = ['-0', 'A11', 2, 2, 'xaaa', 4, 'foo', 'foo6', 'foo', [0, 1, 2], 'x8', [0, 1, 2], 1.5] + call assert_equal(['-0', 'A11', 2, 'xaaa', 4, 'foo', 'foo6', 'foo', [0, 1, 2], 'x8', [0, 1, 2], 1.5], uniq(copy(l))) + call assert_equal([1.5, [0, 1, 2], 'x8', [0, 1, 2], 'foo', 'foo6', 'foo', 4, 'xaaa', 2, 2, 'A11', '-0'], reverse(l)) + call assert_equal([1.5, [0, 1, 2], 'x8', [0, 1, 2], 'foo', 'foo6', 'foo', 4, 'xaaa', 2, 2, 'A11', '-0'], reverse(reverse(l))) + call assert_equal(['-0', 'A11', 'foo', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 2, 4, [0, 1, 2], [0, 1, 2]], sort(l)) + call assert_equal([[0, 1, 2], [0, 1, 2], 4, 2, 2, 1.5, 'xaaa', 'x8', 'foo6', 'foo', 'foo', 'A11', '-0'], reverse(sort(l))) + call assert_equal(['-0', 'A11', 'foo', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 2, 4, [0, 1, 2], [0, 1, 2]], sort(reverse(sort(l)))) + call assert_equal(['-0', 'A11', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 4, [0, 1, 2]], uniq(sort(l))) + + let l=[7, 9, 'one', 18, 12, 22, 'two', 10.0e-16, -1, 'three', 0xff, 0.22, 'four'] + call assert_equal([-1, 'one', 'two', 'three', 'four', 1.0e-15, 0.22, 7, 9, 12, 18, 22, 255], sort(copy(l), 'n')) + + let l=[7, 9, 18, 12, 22, 10.0e-16, -1, 0xff, 0, -0, 0.22, 'bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', {}, []] + call assert_equal(['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l), 1)) + call assert_equal(['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l), 'i')) + call assert_equal(['BAR', 'Bar', 'FOO', 'FOOBAR', 'Foo', 'bar', 'foo', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l))) +endfunc + +" splitting a string to a List +func Test_str_split() + call assert_equal(['aa', 'bb'], split(' aa bb ')) + call assert_equal(['aa', 'bb'], split(' aa bb ', '\W\+', 0)) + call assert_equal(['', 'aa', 'bb', ''], split(' aa bb ', '\W\+', 1)) + call assert_equal(['', '', 'aa', '', 'bb', '', ''], split(' aa bb ', '\W', 1)) + call assert_equal(['aa', '', 'bb'], split(':aa::bb:', ':', 0)) + call assert_equal(['', 'aa', '', 'bb', ''], split(':aa::bb:', ':', 1)) + call assert_equal(['aa', '', 'bb', 'cc', ''], split('aa,,bb, cc,', ',\s*', 1)) + call assert_equal(['a', 'b', 'c'], split('abc', '\zs')) + call assert_equal(['', 'a', '', 'b', '', 'c', ''], split('abc', '\zs', 1)) +endfunc + +" compare recursively linked list and dict +func Test_listdict_compare() + let l = [1, 2, 3, 4] + let d = {'1': 1, '2': l, '3': 3} + let l[1] = d + call assert_true(l == l) + call assert_true(d == d) + call assert_false(l != deepcopy(l)) + call assert_false(d != deepcopy(d)) +endfunc + + " compare complex recursively linked list and dict +func Test_listdict_compare_complex() + let l = [] + call add(l, l) + let dict4 = {"l": l} + call add(dict4.l, dict4) + let lcopy = deepcopy(l) + let dict4copy = deepcopy(dict4) + call assert_true(l == lcopy) + call assert_true(dict4 == dict4copy) +endfunc + +func Test_listdict_extend() + " Pass the same List to extend() + let l = [1, 2, 3, 4, 5] + call extend(l, l) + call assert_equal([1, 2, 3, 4, 5, 1, 2, 3, 4, 5], l) + + " Pass the same Dict to extend() + let d = { 'a': {'b': 'B'}} + call extend(d, d) + call assert_equal({'a': {'b': 'B'}}, d) + + " Pass the same Dict to extend() with "error" + call assert_fails("call extend(d, d, 'error')", 'E737:') + call assert_equal({'a': {'b': 'B'}}, d) +endfunc diff --git a/src/nvim/testdir/test_listlbr.vim b/src/nvim/testdir/test_listlbr.vim new file mode 100644 index 0000000000..d28dbc444c --- /dev/null +++ b/src/nvim/testdir/test_listlbr.vim @@ -0,0 +1,238 @@ +" Test for linebreak and list option (non-utf8) + +" Nvim does not allow setting 'encoding', so skip this test. +finish + +set encoding=latin1 +scriptencoding latin1 + +if !exists("+linebreak") || !has("conceal") + finish +endif + +source view_util.vim + +function s:screen_lines(lnum, width) abort + return ScreenLines(a:lnum, a:width) +endfunction + +function! s:compare_lines(expect, actual) + call assert_equal(join(a:expect, "\n"), join(a:actual, "\n")) +endfunction + +function s:test_windows(...) + call NewWindow(10, 20) + setl ts=8 sw=4 sts=4 linebreak sbr= wrap + exe get(a:000, 0, '') +endfunction + +function s:close_windows(...) + call CloseWindow() + exe get(a:000, 0, '') +endfunction + +func Test_set_linebreak() + call s:test_windows('setl ts=4 sbr=+') + call setline(1, "\tabcdef hijklmn\tpqrstuvwxyz_1060ABCDEFGHIJKLMNOP ") + let lines = s:screen_lines([1, 4], winwidth(0)) + let expect = [ +\ " abcdef ", +\ "+hijklmn ", +\ "+pqrstuvwxyz_1060ABC", +\ "+DEFGHIJKLMNOP ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +func Test_linebreak_with_list() + call s:test_windows('setl ts=4 sbr=+ list listchars=') + call setline(1, "\tabcdef hijklmn\tpqrstuvwxyz_1060ABCDEFGHIJKLMNOP ") + let lines = s:screen_lines([1, 4], winwidth(0)) + let expect = [ +\ "^Iabcdef hijklmn^I ", +\ "+pqrstuvwxyz_1060ABC", +\ "+DEFGHIJKLMNOP ", +\ "~ ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +func Test_linebreak_with_nolist() + call s:test_windows('setl ts=4 sbr=+ nolist') + call setline(1, "\tabcdef hijklmn\tpqrstuvwxyz_1060ABCDEFGHIJKLMNOP ") + let lines = s:screen_lines([1, 4], winwidth(0)) + let expect = [ +\ " abcdef ", +\ "+hijklmn ", +\ "+pqrstuvwxyz_1060ABC", +\ "+DEFGHIJKLMNOP ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +func Test_should_break() + call s:test_windows('setl sbr=+ nolist') + call setline(1, "1\t" . repeat('a', winwidth(0)-2)) + let lines = s:screen_lines([1, 4], winwidth(0)) + let expect = [ +\ "1 ", +\ "+aaaaaaaaaaaaaaaaaa ", +\ "~ ", +\ "~ ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +func Test_linebreak_with_conceal() + call s:test_windows('setl cpo&vim sbr=+ list conceallevel=2 concealcursor=nv listchars=tab:ab') + call setline(1, "_S_\t bla") + syn match ConcealVar contained /_/ conceal + syn match All /.*/ contains=ConcealVar + let lines = s:screen_lines([1, 4], winwidth(0)) + let expect = [ +\ "Sabbbbbb bla ", +\ "~ ", +\ "~ ", +\ "~ ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +func Test_virtual_block() + call s:test_windows('setl sbr=+') + call setline(1, [ +\ "REMOVE: this not", +\ "REMOVE: aaaaaaaaaaaaa", +\ ]) + exe "norm! 1/^REMOVE:" + exe "norm! 0\<C-V>jf x" + $put + let lines = s:screen_lines([1, 4], winwidth(0)) + let expect = [ +\ "this not ", +\ "aaaaaaaaaaaaa ", +\ "REMOVE: ", +\ "REMOVE: ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +func Test_virtual_block_and_vbA() + call s:test_windows() + call setline(1, "long line: " . repeat("foobar ", 40) . "TARGET at end") + exe "norm! $3B\<C-v>eAx\<Esc>" + let lines = s:screen_lines([1, 10], winwidth(0)) + let expect = [ +\ "foobar foobar ", +\ "foobar foobar ", +\ "foobar foobar ", +\ "foobar foobar ", +\ "foobar foobar ", +\ "foobar foobar ", +\ "foobar foobar ", +\ "foobar foobar ", +\ "foobar foobar ", +\ "foobar TARGETx at ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +func Test_virtual_char_and_block() + call s:test_windows() + call setline(1, "1111-1111-1111-11-1111-1111-1111") + exe "norm! 0f-lv3lc2222\<Esc>bgj." + let lines = s:screen_lines([1, 2], winwidth(0)) + let expect = [ +\ "1111-2222-1111-11- ", +\ "1111-2222-1111 ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +func Test_undo_after_block_visual() + call s:test_windows() + call setline(1, ["aaa", "aaa", "a"]) + exe "norm! gg\<C-V>2j~e." + let lines = s:screen_lines([1, 3], winwidth(0)) + let expect = [ +\ "AaA ", +\ "AaA ", +\ "A ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +func Test_norm_after_block_visual() + call s:test_windows() + call setline(1, ["abcd{ef", "ghijklm", "no}pgrs"]) + exe "norm! ggf{\<C-V>\<C-V>c%" + let lines = s:screen_lines([1, 3], winwidth(0)) + let expect = [ +\ "abcdpgrs ", +\ "~ ", +\ "~ ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +func Test_block_replace_after_wrapping() + call s:test_windows() + call setline(1, repeat("a", 150)) + exe "norm! 0yypk147|\<C-V>jr0" + call assert_equal(repeat("a", 146) . "0aaa", getline(1)) + call assert_equal(repeat("a", 146) . "0aaa", getline(2)) + let lines = s:screen_lines([1, 10], winwidth(0)) + let expect = [ +\ "aaaaaaaaaaaaaaaaaaaa", +\ "aaaaaaaaaaaaaaaaaaaa", +\ "aaaaaaaaaaaaaaaaaaaa", +\ "aaaaaaaaaaaaaaaaaaaa", +\ "aaaaaaaaaaaaaaaaaaaa", +\ "aaaaaaaaaaaaaaaaaaaa", +\ "aaaaaaaaaaaaaaaaaaaa", +\ "aaaaaa0aaa ", +\ "@ ", +\ "@ ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +func Test_list_with_listchars() + call s:test_windows('setl list listchars=space:_,trail:-,tab:>-,eol:$') + call setline(1, "a aaaaaaaaaaaaaaaaaaaaaa\ta ") + let lines = s:screen_lines([1, 3], winwidth(0)) + let expect = [ +\ "a_ ", +\ "aaaaaaaaaaaaaaaaaaaa", +\ "aa>-----a-$ ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +func Test_list_with_tab_and_skipping_first_chars() + call s:test_windows('setl list listchars=tab:>- ts=70 nowrap') + call setline(1, ["iiiiiiiiiiiiiiii\taaaaaaaaaaaaaaaaaa", "iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii\taaaaaaaaaaaaaaaaaa", "iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii\taaaaaaaaaaaaaaaaaa", "iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii\taaaaaaaaaaaaaaaaaa"]) + call cursor(4,64) + norm! 2zl + let lines = s:screen_lines([1, 4], winwidth(0)) + let expect = [ +\ "---------------aaaaa", +\ "---------------aaaaa", +\ "---------------aaaaa", +\ "iiiiiiiii>-----aaaaa", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfu diff --git a/src/nvim/testdir/test_listlbr_utf8.vim b/src/nvim/testdir/test_listlbr_utf8.vim new file mode 100644 index 0000000000..56a4cc9b31 --- /dev/null +++ b/src/nvim/testdir/test_listlbr_utf8.vim @@ -0,0 +1,256 @@ +" Test for linebreak and list option in utf-8 mode + +set encoding=utf-8 +scriptencoding utf-8 + +if !exists("+linebreak") || !has("conceal") || !has("signs") + finish +endif + +source view_util.vim + +function s:screen_lines(lnum, width) abort + return ScreenLines(a:lnum, a:width) +endfunction + +function! s:compare_lines(expect, actual) + call assert_equal(a:expect, a:actual) +endfunction + +function s:screen_attr(lnum, chars, ...) abort + let line = getline(a:lnum) + let attr = [] + let prefix = get(a:000, 0, 0) + for i in range(a:chars[0], a:chars[1]) + let scol = strdisplaywidth(strcharpart(line, 0, i-1)) + 1 + let attr += [screenattr(a:lnum, scol + prefix)] + endfor + return attr +endfunction + +function s:test_windows(...) + call NewWindow(10, 20) + setl ts=4 sw=4 sts=4 linebreak sbr=+ wrap + exe get(a:000, 0, '') +endfunction + +function s:close_windows(...) + call CloseWindow() + exe get(a:000, 0, '') +endfunction + +func Test_linebreak_with_fancy_listchars() + call s:test_windows("setl list listchars=nbsp:\u2423,tab:\u2595\u2014,trail:\u02d1,eol:\ub6") + call setline(1, "\tabcdef hijklmn\tpqrstuvwxyz\u00a01060ABCDEFGHIJKLMNOP ") + redraw! + let lines = s:screen_lines([1, 4], winwidth(0)) + let expect = [ +\ "▕———abcdef ", +\ "+hijklmn▕——— ", +\ "+pqrstuvwxyz␣1060ABC", +\ "+DEFGHIJKLMNOPˑ¶ ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +func Test_nolinebreak_with_list() + call s:test_windows("setl nolinebreak list listchars=nbsp:\u2423,tab:\u2595\u2014,trail:\u02d1,eol:\ub6") + call setline(1, "\tabcdef hijklmn\tpqrstuvwxyz\u00a01060ABCDEFGHIJKLMNOP ") + redraw! + let lines = s:screen_lines([1, 4], winwidth(0)) + let expect = [ +\ "▕———abcdef hijklmn▕—", +\ "+pqrstuvwxyz␣1060ABC", +\ "+DEFGHIJKLMNOPˑ¶ ", +\ "~ ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +func Test_linebreak_with_nolist() + call s:test_windows('setl nolist') + call setline(1, "\t*mask = nil;") + redraw! + let lines = s:screen_lines([1, 4], winwidth(0)) + let expect = [ +\ " *mask = nil; ", +\ "~ ", +\ "~ ", +\ "~ ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +func Test_list_and_concealing1() + call s:test_windows('setl list listchars=tab:>- cole=1') + call setline(1, [ +\ "#define ABCDE\t\t1", +\ "#define ABCDEF\t\t1", +\ "#define ABCDEFG\t\t1", +\ "#define ABCDEFGH\t1", +\ "#define MSG_MODE_FILE\t\t\t1", +\ "#define MSG_MODE_CONSOLE\t\t2", +\ "#define MSG_MODE_FILE_AND_CONSOLE\t3", +\ "#define MSG_MODE_FILE_THEN_CONSOLE\t4", +\ ]) + vert resize 40 + syn match Conceal conceal cchar=>'AB\|MSG_MODE' + redraw! + let lines = s:screen_lines([1, 7], winwidth(0)) + let expect = [ +\ "#define ABCDE>-->---1 ", +\ "#define >CDEF>-->---1 ", +\ "#define >CDEFG>->---1 ", +\ "#define >CDEFGH>----1 ", +\ "#define >_FILE>--------->--->---1 ", +\ "#define >_CONSOLE>---------->---2 ", +\ "#define >_FILE_AND_CONSOLE>---------3 ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +func Test_list_and_concealing2() + call s:test_windows('setl nowrap ts=2 list listchars=tab:>- cole=2 concealcursor=n') + call setline(1, "bbeeeeee\t\t;\tsome text") + vert resize 40 + syn clear + syn match meaning /;\s*\zs.*/ + syn match hasword /^\x\{8}/ contains=word + syn match word /\<\x\{8}\>/ contains=beginword,endword contained + syn match beginword /\<\x\x/ contained conceal + syn match endword /\x\{6}\>/ contained + hi meaning guibg=blue + hi beginword guibg=green + hi endword guibg=red + redraw! + let lines = s:screen_lines([1, 1], winwidth(0)) + let expect = [ +\ "eeeeee>--->-;>some text ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +func Test_screenattr_for_comment() + call s:test_windows("setl ft=c ts=7 list listchars=nbsp:\u2423,tab:\u2595\u2014,trail:\u02d1,eol:\ub6") + call setline(1, " /*\t\t and some more */") + norm! gg0 + syntax on + hi SpecialKey term=underline ctermfg=red guifg=red + redraw! + let line = getline(1) + let attr = s:screen_attr(1, [1, 6]) + call assert_notequal(attr[0], attr[1]) + call assert_notequal(attr[1], attr[3]) + call assert_notequal(attr[3], attr[5]) + call s:close_windows() +endfunc + +func Test_visual_block_and_selection_exclusive() + call s:test_windows('setl selection=exclusive') + call setline(1, "long line: " . repeat("foobar ", 40) . "TARGETÃ' at end") + exe "norm! $3B\<C-v>eAx\<Esc>" + let lines = s:screen_lines([1, 10], winwidth(0)) + let expect = [ +\ "+foobar foobar ", +\ "+foobar foobar ", +\ "+foobar foobar ", +\ "+foobar foobar ", +\ "+foobar foobar ", +\ "+foobar foobar ", +\ "+foobar foobar ", +\ "+foobar foobar ", +\ "+foobar foobar ", +\ "+foobar TARGETÃx' ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +func Test_multibyte_sign_and_colorcolumn() + call s:test_windows("setl nolinebreak cc=3 list listchars=nbsp:\u2423,tab:\u2595\u2014,trail:\u02d1,eol:\ub6") + call setline(1, ["", "a b c", "a b c"]) + exe "sign define foo text=\uff0b" + exe "sign place 1 name=foo line=2 buffer=" . bufnr('%') + redraw! + norm! ggj0 + let signwidth = strdisplaywidth("\uff0b") + let attr1 = s:screen_attr(2, [1, 3], signwidth) + let attr2 = s:screen_attr(3, [1, 3], signwidth) + call assert_equal(attr1[0], attr2[0]) + call assert_equal(attr1[1], attr2[1]) + call assert_equal(attr1[2], attr2[2]) + let lines = s:screen_lines([1, 3], winwidth(0)) + let expect = [ +\ " ¶ ", +\ "+a b c¶ ", +\ " a b c¶ ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +func Test_illegal_byte_and_breakat() + call s:test_windows("setl sbr= brk+=<") + vert resize 18 + call setline(1, repeat("\x80", 6)) + redraw! + let lines = s:screen_lines([1, 2], winwidth(0)) + let expect = [ +\ "<80><80><80><80><8", +\ "0><80> ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows('setl brk&vim') +endfunc + +func Test_multibyte_wrap_and_breakat() + call s:test_windows("setl sbr= brk+=>") + call setline(1, repeat('a', 17) . repeat('あ', 2)) + redraw! + let lines = s:screen_lines([1, 2], winwidth(0)) + let expect = [ +\ "aaaaaaaaaaaaaaaaaあ>", +\ "あ ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows('setl brk&vim') +endfunc + +func Test_chinese_char_on_wrap_column() + call s:test_windows("setl nolbr wrap sbr=") + syntax off + call setline(1, [ +\ 'aaaaaaaaaaaaaaaaaaa中'. +\ 'aaaaaaaaaaaaaaaaa中'. +\ 'aaaaaaaaaaaaaaaaa中'. +\ 'aaaaaaaaaaaaaaaaa中'. +\ 'aaaaaaaaaaaaaaaaa中'. +\ 'aaaaaaaaaaaaaaaaa中'. +\ 'aaaaaaaaaaaaaaaaa中'. +\ 'aaaaaaaaaaaaaaaaa中'. +\ 'aaaaaaaaaaaaaaaaa中'. +\ 'aaaaaaaaaaaaaaaaa中'. +\ 'hello']) + call cursor(1,1) + norm! $ + redraw! + let expect=[ +\ '中aaaaaaaaaaaaaaaaa>', +\ '中aaaaaaaaaaaaaaaaa>', +\ '中aaaaaaaaaaaaaaaaa>', +\ '中aaaaaaaaaaaaaaaaa>', +\ '中aaaaaaaaaaaaaaaaa>', +\ '中aaaaaaaaaaaaaaaaa>', +\ '中aaaaaaaaaaaaaaaaa>', +\ '中aaaaaaaaaaaaaaaaa>', +\ '中aaaaaaaaaaaaaaaaa>', +\ '中hello '] + let lines = s:screen_lines([1, 10], winwidth(0)) + call s:compare_lines(expect, lines) + call s:close_windows() +endfu diff --git a/src/nvim/testdir/test_makeencoding.py b/src/nvim/testdir/test_makeencoding.py new file mode 100644 index 0000000000..041edadc0a --- /dev/null +++ b/src/nvim/testdir/test_makeencoding.py @@ -0,0 +1,67 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Test program for :make, :grep and :cgetfile. + +from __future__ import print_function, unicode_literals +import locale +import io +import sys + +def set_output_encoding(enc=None): + """Set the encoding of stdout and stderr + + arguments: + enc -- Encoding name. + If omitted, locale.getpreferredencoding() is used. + """ + if enc is None: + enc = locale.getpreferredencoding() + + def get_text_writer(fo, **kwargs): + kw = dict(kwargs) + kw.setdefault('errors', 'backslashreplace') # use \uXXXX style + kw.setdefault('closefd', False) + + if sys.version_info[0] < 3: + # Work around for Python 2.x + # New line conversion isn't needed here. Done in somewhere else. + writer = io.open(fo.fileno(), mode='w', newline='', **kw) + write = writer.write # save the original write() function + enc = locale.getpreferredencoding() + def convwrite(s): + if isinstance(s, bytes): + write(s.decode(enc)) # convert to unistr + else: + write(s) + try: + writer.flush() # needed on Windows + except IOError: + pass + writer.write = convwrite + else: + writer = io.open(fo.fileno(), mode='w', **kw) + return writer + + sys.stdout = get_text_writer(sys.stdout, encoding=enc) + sys.stderr = get_text_writer(sys.stderr, encoding=enc) + + +def main(): + enc = 'utf-8' + if len(sys.argv) > 1: + enc = sys.argv[1] + set_output_encoding(enc) + + message_tbl = { + 'utf-8': 'ÀÈÌÒÙ こんにちは 你好', + 'latin1': 'ÀÈÌÒÙ', + 'cp932': 'こんにちは', + 'cp936': '你好', + } + + print('Xfoobar.c(10) : %s (%s)' % (message_tbl[enc], enc)) + + +if __name__ == "__main__": + main() diff --git a/src/nvim/testdir/test_makeencoding.vim b/src/nvim/testdir/test_makeencoding.vim new file mode 100644 index 0000000000..a3d5538a47 --- /dev/null +++ b/src/nvim/testdir/test_makeencoding.vim @@ -0,0 +1,106 @@ +" Tests for 'makeencoding'. +if !has('multi_byte') + finish +endif + +source shared.vim + +let s:python = PythonProg() +if s:python == '' + " Can't run this test. + finish +endif + +let s:script = 'test_makeencoding.py' + +let s:message_tbl = { + \ 'utf-8': 'ÀÈÌÒÙ こんにちは 你好', + \ 'latin1': 'ÀÈÌÒÙ', + \ 'cp932': 'こんにちは', + \ 'cp936': '你好', + \} + + +" Tests for :cgetfile and :lgetfile. +func Test_getfile() + set errorfile=Xerror.txt + set errorformat=%f(%l)\ :\ %m + + " :cgetfile + for enc in keys(s:message_tbl) + let &makeencoding = enc + exec "silent !" . s:python . " " . s:script . " " . enc . " > " . &errorfile + cgetfile + copen + call assert_equal("Xfoobar.c|10| " . s:message_tbl[enc] . " (" . enc . ")", + \ getline('.')) + cclose + endfor + + " :lgetfile + for enc in keys(s:message_tbl) + let &makeencoding = enc + exec "silent !" . s:python . " " . s:script . " " . enc . " > " . &errorfile + lgetfile + lopen + call assert_equal("Xfoobar.c|10| " . s:message_tbl[enc] . " (" . enc . ")", + \ getline('.')) + lclose + endfor + + call delete(&errorfile) +endfunc + + +" Tests for :grep and :lgrep. +func Test_grep() + let &grepprg = s:python + set grepformat=%f(%l)\ :\ %m + + " :grep + for enc in keys(s:message_tbl) + let &makeencoding = enc + exec "silent grep! " . s:script . " " . enc + copen + call assert_equal("Xfoobar.c|10| " . s:message_tbl[enc] . " (" . enc . ")", + \ getline('.')) + cclose + endfor + + " :lgrep + for enc in keys(s:message_tbl) + let &makeencoding = enc + exec "silent lgrep! " . s:script . " " . enc + lopen + call assert_equal("Xfoobar.c|10| " . s:message_tbl[enc] . " (" . enc . ")", + \ getline('.')) + lclose + endfor +endfunc + + +" Tests for :make and :lmake. +func Test_make() + let &makeprg = s:python + set errorformat=%f(%l)\ :\ %m + + " :make + for enc in keys(s:message_tbl) + let &makeencoding = enc + exec "silent make! " . s:script . " " . enc + copen + call assert_equal("Xfoobar.c|10| " . s:message_tbl[enc] . " (" . enc . ")", + \ getline('.')) + cclose + endfor + + " :lmake + for enc in keys(s:message_tbl) + let &makeencoding = enc + exec "silent lmake! " . s:script . " " . enc + lopen + call assert_equal("Xfoobar.c|10| " . s:message_tbl[enc] . " (" . enc . ")", + \ getline('.')) + lclose + endfor +endfunc diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim index 7f93ddd56e..f5e4c4b90c 100644 --- a/src/nvim/testdir/test_mapping.vim +++ b/src/nvim/testdir/test_mapping.vim @@ -110,6 +110,8 @@ func Test_map_langmap() call feedkeys(":call append(line('$'), '+')\<CR>", "xt") call assert_equal('+', getline('$')) + iunmap a + iunmap c set nomodified endfunc @@ -120,7 +122,7 @@ func Test_map_feedkeys() $-1 call feedkeys("0qqdw.ifoo\<Esc>qj0@q\<Esc>", "xt") call assert_equal(['fooc d', 'fooc d'], getline(line('$') - 1, line('$'))) - unmap . + nunmap . set nomodified endfunc diff --git a/src/nvim/testdir/test_matchadd_conceal.vim b/src/nvim/testdir/test_matchadd_conceal.vim index c788689e33..c11f1a84a9 100644 --- a/src/nvim/testdir/test_matchadd_conceal.vim +++ b/src/nvim/testdir/test_matchadd_conceal.vim @@ -277,6 +277,7 @@ function! Test_matchadd_and_syn_conceal() call assert_notequal(screenattr(1, 11) , screenattr(1, 12)) call assert_equal(screenattr(1, 11) , screenattr(1, 32)) call matchadd('CheckedByCoq', '\%<2l\%>9c\%<16c') + redraw! call assert_equal(expect, s:screenline(1)) call assert_notequal(screenattr(1, 10) , screenattr(1, 11)) call assert_notequal(screenattr(1, 11) , screenattr(1, 12)) diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim new file mode 100644 index 0000000000..4774cf4af5 --- /dev/null +++ b/src/nvim/testdir/test_mksession.vim @@ -0,0 +1,155 @@ +" Test for :mksession, :mkview and :loadview in latin1 encoding + +scriptencoding latin1 + +if !has('multi_byte') || !has('mksession') + finish +endif + +func Test_mksession() + tabnew + let wrap_save = &wrap + set sessionoptions=buffers splitbelow fileencoding=latin1 + call setline(1, [ + \ 'start:', + \ 'no multibyte chAracter', + \ ' one leaDing tab', + \ ' four leadinG spaces', + \ 'two consecutive tabs', + \ 'two tabs in one line', + \ 'one multibyteCharacter', + \ 'a two multiByte characters', + \ 'A three mulTibyte characters' + \ ]) + let tmpfile = 'Xtemp' + exec 'w! ' . tmpfile + /^start: + set wrap + vsplit + norm! j16| + split + norm! j16| + split + norm! j16| + split + norm! j8| + split + norm! j8| + split + norm! j16| + split + norm! j16| + split + norm! j16| + wincmd l + + set nowrap + /^start: + norm! j16|3zl + split + norm! j016|3zl + split + norm! j016|3zl + split + norm! j08|3zl + split + norm! j08|3zl + split + norm! j016|3zl + split + norm! j016|3zl + split + norm! j016|3zl + split + call wincol() + mksession! Xtest_mks.out + let li = filter(readfile('Xtest_mks.out'), 'v:val =~# "\\(^ *normal! 0\\|^ *exe ''normal!\\)"') + let expected = [ + \ 'normal! 016|', + \ 'normal! 016|', + \ 'normal! 016|', + \ 'normal! 08|', + \ 'normal! 08|', + \ 'normal! 016|', + \ 'normal! 016|', + \ 'normal! 016|', + \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'", + \ " normal! 016|", + \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'", + \ " normal! 016|", + \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'", + \ " normal! 016|", + \ " exe 'normal! ' . s:c . '|zs' . 8 . '|'", + \ " normal! 08|", + \ " exe 'normal! ' . s:c . '|zs' . 8 . '|'", + \ " normal! 08|", + \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'", + \ " normal! 016|", + \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'", + \ " normal! 016|", + \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'", + \ " normal! 016|", + \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'", + \ " normal! 016|" + \ ] + call assert_equal(expected, li) + tabclose! + + call delete('Xtest_mks.out') + call delete(tmpfile) + let &wrap = wrap_save +endfunc + +func Test_mksession_winheight() + new + set winheight=10 winminheight=2 + mksession! Xtest_mks.out + source Xtest_mks.out + + call delete('Xtest_mks.out') +endfunc + +" Verify that arglist is stored correctly to the session file. +func Test_mksession_arglist() + argdel * + next file1 file2 file3 file4 + mksession! Xtest_mks.out + source Xtest_mks.out + call assert_equal(['file1', 'file2', 'file3', 'file4'], argv()) + + call delete('Xtest_mks.out') + argdel * +endfunc + + +func Test_mksession_one_buffer_two_windows() + edit Xtest1 + new Xtest2 + split + mksession! Xtest_mks.out + let lines = readfile('Xtest_mks.out') + let count1 = 0 + let count2 = 0 + let count2buf = 0 + for line in lines + if line =~ 'edit \f*Xtest1$' + let count1 += 1 + endif + if line =~ 'edit \f\{-}Xtest2' + let count2 += 1 + endif + if line =~ 'buffer \f\{-}Xtest2' + let count2buf += 1 + endif + endfor + call assert_equal(1, count1, 'Xtest1 count') + call assert_equal(2, count2, 'Xtest2 count') + call assert_equal(2, count2buf, 'Xtest2 buffer count') + + close + bwipe! + call delete('Xtest_mks.out') +endfunc + + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_mksession_utf8.vim b/src/nvim/testdir/test_mksession_utf8.vim new file mode 100644 index 0000000000..8ffbba2a1c --- /dev/null +++ b/src/nvim/testdir/test_mksession_utf8.vim @@ -0,0 +1,105 @@ +" Test for :mksession, :mkview and :loadview in utf-8 encoding + +set encoding=utf-8 +scriptencoding utf-8 + +if !has('multi_byte') || !has('mksession') + finish +endif + +func Test_mksession_utf8() + tabnew + let wrap_save = &wrap + set sessionoptions=buffers splitbelow fileencoding=utf-8 + call setline(1, [ + \ 'start:', + \ 'no multibyte chAracter', + \ ' one leaDing tab', + \ ' four leadinG spaces', + \ 'two consecutive tabs', + \ 'two tabs in one line', + \ 'one … multibyteCharacter', + \ 'a “b” two multiByte characters', + \ '“c”1€ three mulTibyte characters' + \ ]) + let tmpfile = tempname() + exec 'w! ' . tmpfile + /^start: + set wrap + vsplit + norm! j16| + split + norm! j16| + split + norm! j16| + split + norm! j8| + split + norm! j8| + split + norm! j16| + split + norm! j16| + split + norm! j16| + wincmd l + + set nowrap + /^start: + norm! j16|3zl + split + norm! j016|3zl + split + norm! j016|3zl + split + norm! j08|3zl + split + norm! j08|3zl + split + norm! j016|3zl + split + norm! j016|3zl + split + norm! j016|3zl + split + call wincol() + mksession! test_mks.out + let li = filter(readfile('test_mks.out'), 'v:val =~# "\\(^ *normal! 0\\|^ *exe ''normal!\\)"') + let expected = [ + \ 'normal! 016|', + \ 'normal! 016|', + \ 'normal! 016|', + \ 'normal! 08|', + \ 'normal! 08|', + \ 'normal! 016|', + \ 'normal! 016|', + \ 'normal! 016|', + \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'", + \ " normal! 016|", + \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'", + \ " normal! 016|", + \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'", + \ " normal! 016|", + \ " exe 'normal! ' . s:c . '|zs' . 8 . '|'", + \ " normal! 08|", + \ " exe 'normal! ' . s:c . '|zs' . 8 . '|'", + \ " normal! 08|", + \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'", + \ " normal! 016|", + \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'", + \ " normal! 016|", + \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'", + \ " normal! 016|", + \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'", + \ " normal! 016|" + \ ] + call assert_equal(expected, li) + tabclose! + + call delete('test_mks.out') + call delete(tmpfile) + let &wrap = wrap_save + set sessionoptions& splitbelow& fileencoding& +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_nested_function.vim b/src/nvim/testdir/test_nested_function.vim index f881730529..afaaea6ceb 100644 --- a/src/nvim/testdir/test_nested_function.vim +++ b/src/nvim/testdir/test_nested_function.vim @@ -1,32 +1,63 @@ "Tests for nested functions " -function! NestedFunc() - fu! Func1() +func NestedFunc() + func! Func1() let g:text .= 'Func1 ' - endfunction + endfunc call Func1() - fu! s:func2() + func! s:func2() let g:text .= 's:func2 ' - endfunction + endfunc call s:func2() - fu! s:_func3() + func! s:_func3() let g:text .= 's:_func3 ' - endfunction + endfunc call s:_func3() let fn = 'Func4' - fu! {fn}() + func! {fn}() let g:text .= 'Func4 ' - endfunction + endfunc call {fn}() let fn = 'func5' - fu! s:{fn}() + func! s:{fn}() let g:text .= 's:func5' - endfunction + endfunc call s:{fn}() -endfunction +endfunc -function! Test_nested_functions() +func Test_nested_functions() let g:text = '' call NestedFunc() call assert_equal('Func1 s:func2 s:_func3 Func4 s:func5', g:text) endfunction + +func Test_nested_argument() + func g:X() + let g:Y = function('sort') + endfunc + let g:Y = function('sort') + echo g:Y([], g:X()) + delfunc g:X + unlet g:Y +endfunc + +func Recurse(count) + if a:count > 0 + call Recurse(a:count - 1) + endif +endfunc + +func Test_max_nesting() + let call_depth_here = 2 + let ex_depth_here = 5 + set mfd& + + call Recurse(99 - call_depth_here) + call assert_fails('call Recurse(' . (100 - call_depth_here) . ')', 'E132:') + + set mfd=210 + call Recurse(209 - ex_depth_here) + call assert_fails('call Recurse(' . (210 - ex_depth_here) . ')', 'E169:') + + set mfd& +endfunc diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index 6261625801..1d15c7f83d 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -1,5 +1,7 @@ " Test for various Normal mode commands +source shared.vim + func! Setup_NewWindow() 10new call setline(1, range(1,100)) @@ -1069,7 +1071,6 @@ func! Test_normal18_z_fold() endfunc func! Test_normal19_z_spell() - throw "skipped: Nvim 'spell' requires download" if !has("spell") || !has('syntax') return endif @@ -1120,6 +1121,7 @@ func! Test_normal19_z_spell() let a=execute('unsilent norm! V$zG') call assert_match("Word '2 goood' added to .*", a) let fname=matchstr(a, 'to\s\+\zs\f\+$') + let fname=Fix_truncated_tmpfile(fname) let cnt=readfile(fname) call assert_equal('2 goood', cnt[0]) @@ -1255,21 +1257,27 @@ func! Test_normal22_zet() " Test for ZZ " let shell = &shell " let &shell = 'sh' - call writefile(['1', '2'], 'Xfile') + + " Remove any stale test files from previous run. + for file in ['Xfile_Test_normal22_zet'] + call delete(file) + endfor + + call writefile(['1', '2'], 'Xfile_Test_normal22_zet') let args = ' --headless -u NONE -N -U NONE -i NONE --noplugins' - call system(v:progpath . args . ' -c "%d" -c ":norm! ZZ" Xfile') - let a = readfile('Xfile') + call system(v:progpath . args . ' -c "%d" -c ":norm! ZZ" Xfile_Test_normal22_zet') + let a = readfile('Xfile_Test_normal22_zet') call assert_equal([], a) " Test for ZQ - call writefile(['1', '2'], 'Xfile') - call system(v:progpath . args . ' -c "%d" -c ":norm! ZQ" Xfile') - let a = readfile('Xfile') + call writefile(['1', '2'], 'Xfile_Test_normal22_zet') + call system(v:progpath . args . ' -c "%d" -c ":norm! ZQ" Xfile_Test_normal22_zet') + let a = readfile('Xfile_Test_normal22_zet') call assert_equal(['1', '2'], a) - " clean up - for file in ['Xfile'] - call delete(file) - endfor + " Nvim: This sometimes hangs the TSAN build. + " for file in ['Xfile_Test_normal22_zet'] + " call delete(file) + " endfor " let &shell = shell endfunc @@ -2329,3 +2337,45 @@ func! Test_normal54_Ctrl_bsl() " clean up bw! endfunc + +" Test for the gr (virtual replace) command +" Test for the bug fixed by 7.4.387 +func Test_gr_command() + enew! + let save_cpo = &cpo + call append(0, ['First line', 'Second line', 'Third line']) + exe "normal i\<C-G>u" + call cursor(2, 1) + set cpo-=X + normal 4gro + call assert_equal('oooond line', getline(2)) + undo + set cpo+=X + normal 4gro + call assert_equal('ooooecond line', getline(2)) + let &cpo = save_cpo + enew! +endfunc + +" When splitting a window the changelist position is wrong. +" Test the changelist position after splitting a window. +" Test for the bug fixed by 7.4.386 +func Test_changelist() + let save_ul = &ul + enew! + call append('$', ['1', '2']) + exe "normal i\<C-G>u" + exe "normal Gkylpa\<C-G>u" + set ul=100 + exe "normal Gylpa\<C-G>u" + set ul=100 + normal gg + vsplit + normal g; + call assert_equal([3, 2], [line('.'), col('.')]) + normal g; + call assert_equal([2, 2], [line('.'), col('.')]) + call assert_fails('normal g;', 'E662:') + %bwipe! + let &ul = save_ul +endfunc diff --git a/src/nvim/testdir/test_number.vim b/src/nvim/testdir/test_number.vim new file mode 100644 index 0000000000..59debcea0d --- /dev/null +++ b/src/nvim/testdir/test_number.vim @@ -0,0 +1,254 @@ +" Test for 'number' and 'relativenumber' + +source view_util.vim + +func! s:screen_lines(start, end) abort + return ScreenLines([a:start, a:end], 8) +endfunc + +func! s:compare_lines(expect, actual) + call assert_equal(a:expect, a:actual) +endfunc + +func! s:test_windows(h, w) abort + call NewWindow(a:h, a:w) +endfunc + +func! s:close_windows() abort + call CloseWindow() +endfunc + +func! s:validate_cursor() abort + " update skipcol. + " wincol(): + " f_wincol + " -> validate_cursor + " -> curs_columns + call wincol() +endfunc + +func Test_set_options() + set nu rnu + call assert_equal(1, &nu) + call assert_equal(1, &rnu) + + call s:test_windows(10, 20) + call assert_equal(1, &nu) + call assert_equal(1, &rnu) + call s:close_windows() + + set nu& rnu& +endfunc + +func Test_set_global_and_local() + " setlocal must NOT reset the other global value + set nonu nornu + setglobal nu + setlocal rnu + call assert_equal(1, &g:nu) + + set nonu nornu + setglobal rnu + setlocal nu + call assert_equal(1, &g:rnu) + + " setglobal MUST reset the other global value + set nonu nornu + setglobal nu + setglobal rnu + call assert_equal(1, &g:nu) + + set nonu nornu + setglobal rnu + setglobal nu + call assert_equal(1, &g:rnu) + + " set MUST reset the other global value + set nonu nornu + set nu + set rnu + call assert_equal(1, &g:nu) + + set nonu nornu + set rnu + set nu + call assert_equal(1, &g:rnu) + + set nu& rnu& +endfunc + +func Test_number() + call s:test_windows(10, 20) + call setline(1, ["abcdefghij", "klmnopqrst", "uvwxyzABCD", "EFGHIJKLMN", "OPQRSTUVWX", "YZ"]) + setl number + let lines = s:screen_lines(1, 4) + let expect = [ +\ " 1 abcd", +\ " 2 klmn", +\ " 3 uvwx", +\ " 4 EFGH", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +func Test_relativenumber() + call s:test_windows(10, 20) + call setline(1, ["abcdefghij", "klmnopqrst", "uvwxyzABCD", "EFGHIJKLMN", "OPQRSTUVWX", "YZ"]) + 3 + setl relativenumber + let lines = s:screen_lines(1, 6) + let expect = [ +\ " 2 abcd", +\ " 1 klmn", +\ " 0 uvwx", +\ " 1 EFGH", +\ " 2 OPQR", +\ " 3 YZ ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +func Test_number_with_relativenumber() + call s:test_windows(10, 20) + call setline(1, ["abcdefghij", "klmnopqrst", "uvwxyzABCD", "EFGHIJKLMN", "OPQRSTUVWX", "YZ"]) + 4 + setl number relativenumber + let lines = s:screen_lines(1, 6) + let expect = [ +\ " 3 abcd", +\ " 2 klmn", +\ " 1 uvwx", +\ "4 EFGH", +\ " 1 OPQR", +\ " 2 YZ ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +func Test_number_with_linewrap1() + call s:test_windows(3, 20) + normal! 61ia + setl number wrap + call s:validate_cursor() + let lines = s:screen_lines(1, 3) + let expect = [ +\ "--1 aaaa", +\ " aaaa", +\ " aaaa", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +" Pending: https://groups.google.com/forum/#!topic/vim_dev/tzNKP7EDWYI +func XTest_number_with_linewrap2() + call s:test_windows(3, 20) + normal! 61ia + setl number wrap + call s:validate_cursor() + 0 + call s:validate_cursor() + let lines = s:screen_lines(1, 3) + let expect = [ +\ " 1 aaaa", +\ " aaaa", +\ " aaaa", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +" Pending: https://groups.google.com/forum/#!topic/vim_dev/tzNKP7EDWYI +func XTest_number_with_linewrap3() + call s:test_windows(4, 20) + normal! 81ia + setl number wrap + call s:validate_cursor() + setl nonumber + call s:validate_cursor() + let lines = s:screen_lines(1, 4) + let expect = [ +\ "aaaaaaaa", +\ "aaaaaaaa", +\ "aaaaaaaa", +\ "a ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +func Test_numberwidth() + call s:test_windows(10, 20) + call setline(1, repeat(['aaaa'], 10)) + setl number numberwidth=6 + let lines = s:screen_lines(1, 3) + let expect = [ +\ " 1 aa", +\ " 2 aa", +\ " 3 aa", +\ ] + call s:compare_lines(expect, lines) + + set relativenumber + let lines = s:screen_lines(1, 3) + let expect = [ +\ "1 aa", +\ " 1 aa", +\ " 2 aa", +\ ] + call s:compare_lines(expect, lines) + + set nonumber + let lines = s:screen_lines(1, 3) + let expect = [ +\ " 0 aa", +\ " 1 aa", +\ " 2 aa", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +func Test_numberwidth_adjusted() + call s:test_windows(10, 20) + call setline(1, repeat(['aaaa'], 10000)) + setl number numberwidth=4 + let lines = s:screen_lines(1, 3) + let expect = [ +\ " 1 aa", +\ " 2 aa", +\ " 3 aa", +\ ] + call s:compare_lines(expect, lines) + + $ + let lines = s:screen_lines(8, 10) + let expect = [ +\ " 9998 aa", +\ " 9999 aa", +\ "10000 aa", +\ ] + call s:compare_lines(expect, lines) + + setl relativenumber + let lines = s:screen_lines(8, 10) + let expect = [ +\ " 2 aa", +\ " 1 aa", +\ "10000 aa", +\ ] + call s:compare_lines(expect, lines) + + setl nonumber + let lines = s:screen_lines(8, 10) + let expect = [ +\ " 2 aaaa", +\ " 1 aaaa", +\ " 0 aaaa", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index 5ee0919e18..a15d15213a 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -13,6 +13,12 @@ function! Test_whichwrap() set whichwrap+=h,l call assert_equal('b,s,h,l', &whichwrap) + set whichwrap=h,h + call assert_equal('h', &whichwrap) + + set whichwrap=h,h,h + call assert_equal('h', &whichwrap) + set whichwrap& endfunction @@ -97,3 +103,133 @@ func Test_keymap_valid() call assert_fails(":set kmp=trunc\x00name", "E544:") call assert_fails(":set kmp=trunc\x00name", "trunc") endfunc + +func Check_dir_option(name) + " Check that it's possible to set the option. + exe 'set ' . a:name . '=/usr/share/dict/words' + call assert_equal('/usr/share/dict/words', eval('&' . a:name)) + exe 'set ' . a:name . '=/usr/share/dict/words,/and/there' + call assert_equal('/usr/share/dict/words,/and/there', eval('&' . a:name)) + exe 'set ' . a:name . '=/usr/share/dict\ words' + call assert_equal('/usr/share/dict words', eval('&' . a:name)) + + " Check rejecting weird characters. + call assert_fails("set " . a:name . "=/not&there", "E474:") + call assert_fails("set " . a:name . "=/not>there", "E474:") + call assert_fails("set " . a:name . "=/not.*there", "E474:") +endfunc + +func Test_cinkeys() + " This used to cause invalid memory access + set cindent cinkeys=0 + norm a + set cindent& cinkeys& +endfunc + +func Test_dictionary() + call Check_dir_option('dictionary') +endfunc + +func Test_thesaurus() + call Check_dir_option('thesaurus') +endfun + +func Test_set_completion() + call feedkeys(":set di\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"set dictionary diff diffexpr diffopt digraph directory display', @:) + + " Expand boolan options. When doing :set no<Tab> + " vim displays the options names without "no" but completion uses "no...". + call feedkeys(":set nodi\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"set nodiff digraph', @:) + + call feedkeys(":set invdi\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"set invdiff digraph', @:) + + " Expand abbreviation of options. + call feedkeys(":set ts\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"set tabstop thesaurus', @:) + + " Expand current value + call feedkeys(":set fileencodings=\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"set fileencodings=ucs-bom,utf-8,default,latin1', @:) + + call feedkeys(":set fileencodings:\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"set fileencodings:ucs-bom,utf-8,default,latin1', @:) + + " Expand directories. + call feedkeys(":set cdpath=./\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_match('./samples/ ', @:) + call assert_notmatch('./small.vim ', @:) + + " Expand files and directories. + call feedkeys(":set tags=./\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_match('./samples/ ./sautest/ ./setup.vim ./shared.vim', @:) + + call feedkeys(":set tags=./\\\\ dif\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"set tags=./\\ diff diffexpr diffopt', @:) +endfunc + +func Test_set_errors() + call assert_fails('set scroll=-1', 'E49:') + call assert_fails('set backupcopy=', 'E474:') + call assert_fails('set regexpengine=3', 'E474:') + call assert_fails('set history=10001', 'E474:') + call assert_fails('set numberwidth=11', 'E474:') + call assert_fails('set colorcolumn=-a') + call assert_fails('set colorcolumn=a') + call assert_fails('set colorcolumn=1,') + call assert_fails('set cmdheight=-1', 'E487:') + call assert_fails('set cmdwinheight=-1', 'E487:') + if has('conceal') + call assert_fails('set conceallevel=-1', 'E487:') + call assert_fails('set conceallevel=4', 'E474:') + endif + call assert_fails('set helpheight=-1', 'E487:') + call assert_fails('set history=-1', 'E487:') + call assert_fails('set report=-1', 'E487:') + call assert_fails('set shiftwidth=-1', 'E487:') + call assert_fails('set sidescroll=-1', 'E487:') + call assert_fails('set tabstop=-1', 'E487:') + call assert_fails('set textwidth=-1', 'E487:') + call assert_fails('set timeoutlen=-1', 'E487:') + call assert_fails('set updatecount=-1', 'E487:') + call assert_fails('set updatetime=-1', 'E487:') + call assert_fails('set winheight=-1', 'E487:') + call assert_fails('set tabstop!', 'E488:') + call assert_fails('set xxx', 'E518:') + call assert_fails('set beautify?', 'E518:') + call assert_fails('set undolevels=x', 'E521:') + call assert_fails('set tabstop=', 'E521:') + call assert_fails('set comments=-', 'E524:') + call assert_fails('set comments=a', 'E525:') + call assert_fails('set foldmarker=x', 'E536:') + call assert_fails('set commentstring=x', 'E537:') + call assert_fails('set complete=x', 'E539:') + call assert_fails('set statusline=%{', 'E540:') + call assert_fails('set statusline=' . repeat("%p", 81), 'E541:') + call assert_fails('set statusline=%(', 'E542:') + if has('cursorshape') + " This invalid value for 'guicursor' used to cause Vim to crash. + call assert_fails('set guicursor=i-ci,r-cr:h', 'E545:') + call assert_fails('set guicursor=i-ci', 'E545:') + call assert_fails('set guicursor=x', 'E545:') + call assert_fails('set guicursor=r-cr:horx', 'E548:') + call assert_fails('set guicursor=r-cr:hor0', 'E549:') + endif + call assert_fails('set backupext=~ patchmode=~', 'E589:') + call assert_fails('set winminheight=10 winheight=9', 'E591:') + call assert_fails('set winminwidth=10 winwidth=9', 'E592:') + call assert_fails("set showbreak=\x01", 'E595:') + call assert_fails('set t_foo=', 'E846:') +endfunc + +func Test_complete() + " Trailing single backslash used to cause invalid memory access. + set complete=s\ + new + call feedkeys("i\<C-N>\<Esc>", 'xt') + bwipe! + set complete& +endfun + diff --git a/src/nvim/testdir/test_popup.vim b/src/nvim/testdir/test_popup.vim index 519d855cd8..f5bdd717dd 100644 --- a/src/nvim/testdir/test_popup.vim +++ b/src/nvim/testdir/test_popup.vim @@ -7,10 +7,10 @@ func! ListMonths() if g:setting != '' exe ":set" g:setting endif - let mth=copy(g:months) + let mth = copy(g:months) let entered = strcharpart(getline('.'),0,col('.')) if !empty(entered) - let mth=filter(mth, 'v:val=~"^".entered') + let mth = filter(mth, 'v:val=~"^".entered') endif call complete(1, mth) return '' @@ -468,7 +468,7 @@ endfunc " auto-wrap text. func Test_completion_ctrl_e_without_autowrap() new - let tw_save=&tw + let tw_save = &tw set tw=78 let li = [ \ '" zzz', @@ -478,7 +478,7 @@ func Test_completion_ctrl_e_without_autowrap() call feedkeys("A\<C-X>\<C-N>\<C-E>\<Esc>", "tx") call assert_equal(li, getline(1, '$')) - let &tw=tw_save + let &tw = tw_save q! endfunc @@ -541,4 +541,71 @@ func Test_completion_comment_formatting() bwipe! endfunc +function! DummyCompleteSix() + call complete(1, ['Hello', 'World']) + return '' +endfunction + +" complete() correctly clears the list of autocomplete candidates +func Test_completion_clear_candidate_list() + new + %d + " select first entry from the completion popup + call feedkeys("a xxx\<C-N>\<C-R>=DummyCompleteSix()\<CR>", "tx") + call assert_equal('Hello', getline(1)) + %d + " select second entry from the completion popup + call feedkeys("a xxx\<C-N>\<C-R>=DummyCompleteSix()\<CR>\<C-N>", "tx") + call assert_equal('World', getline(1)) + %d + " select original text + call feedkeys("a xxx\<C-N>\<C-R>=DummyCompleteSix()\<CR>\<C-N>\<C-N>", "tx") + call assert_equal(' xxx', getline(1)) + %d + " back at first entry from completion list + call feedkeys("a xxx\<C-N>\<C-R>=DummyCompleteSix()\<CR>\<C-N>\<C-N>\<C-N>", "tx") + call assert_equal('Hello', getline(1)) + + bw! +endfunc + + +func Test_popup_and_preview_autocommand() + " This used to crash Vim + if !has('python') + return + endif + let h = winheight(0) + if h < 15 + return + endif + new + augroup MyBufAdd + au! + au BufAdd * nested tab sball + augroup END + set omnifunc=pythoncomplete#Complete + call setline(1, 'import os') + " make the line long + call setline(2, ' os.') + $ + call feedkeys("A\<C-X>\<C-O>\<C-N>\<C-N>\<C-N>\<enter>\<esc>", 'tx') + call assert_equal("import os", getline(1)) + call assert_match(' os.\(EX_IOERR\|O_CREAT\)$', getline(2)) + call assert_equal(1, winnr('$')) + " previewwindow option is not set + call assert_equal(0, &previewwindow) + norm! gt + call assert_equal(0, &previewwindow) + norm! gT + call assert_equal(12, tabpagenr('$')) + tabonly + pclose + augroup MyBufAdd + au! + augroup END + augroup! MyBufAdd + bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_profile.vim b/src/nvim/testdir/test_profile.vim new file mode 100644 index 0000000000..4cbd800da5 --- /dev/null +++ b/src/nvim/testdir/test_profile.vim @@ -0,0 +1,183 @@ +" Test Vim profiler +if !has('profile') + finish +endif + +func Test_profile_func() + let lines = [ + \ "func! Foo1()", + \ "endfunc", + \ "func! Foo2()", + \ " let l:count = 100", + \ " while l:count > 0", + \ " let l:count = l:count - 1", + \ " endwhile", + \ "endfunc", + \ "func! Foo3()", + \ "endfunc", + \ "func! Bar()", + \ "endfunc", + \ "call Foo1()", + \ "call Foo1()", + \ "profile pause", + \ "call Foo1()", + \ "profile continue", + \ "call Foo2()", + \ "call Foo3()", + \ "call Bar()", + \ "if !v:profiling", + \ " delfunc Foo2", + \ "endif", + \ "delfunc Foo3", + \ ] + + call writefile(lines, 'Xprofile_func.vim') + call system(v:progpath + \ . ' -es -u NONE -U NONE -i NONE --noplugin' + \ . ' -c "profile start Xprofile_func.log"' + \ . ' -c "profile func Foo*"' + \ . ' -c "so Xprofile_func.vim"' + \ . ' -c "qall!"') + call assert_equal(0, v:shell_error) + + let lines = readfile('Xprofile_func.log') + + " - Foo1() is called 3 times but should be reported as called twice + " since one call is in between "profile pause" .. "profile continue". + " - Foo2() should come before Foo1() since Foo1() does much more work. + " - Foo3() is not reported because function is deleted. + " - Unlike Foo3(), Foo2() should not be deleted since there is a check + " for v:profiling. + " - Bar() is not reported since it does not match "profile func Foo*". + call assert_equal(28, len(lines)) + + call assert_equal('FUNCTION Foo1()', lines[0]) + call assert_equal('Called 2 times', lines[1]) + call assert_match('^Total time:\s\+\d\+\.\d\+$', lines[2]) + call assert_match('^ Self time:\s\+\d\+\.\d\+$', lines[3]) + call assert_equal('', lines[4]) + call assert_equal('count total (s) self (s)', lines[5]) + call assert_equal('', lines[6]) + call assert_equal('FUNCTION Foo2()', lines[7]) + call assert_equal('Called 1 time', lines[8]) + call assert_match('^Total time:\s\+\d\+\.\d\+$', lines[9]) + call assert_match('^ Self time:\s\+\d\+\.\d\+$', lines[10]) + call assert_equal('', lines[11]) + call assert_equal('count total (s) self (s)', lines[12]) + call assert_match('^\s*1\s\+.*\slet l:count = 100$', lines[13]) + call assert_match('^\s*101\s\+.*\swhile l:count > 0$', lines[14]) + call assert_match('^\s*100\s\+.*\s let l:count = l:count - 1$', lines[15]) + call assert_match('^\s*100\s\+.*\sendwhile$', lines[16]) + call assert_equal('', lines[17]) + call assert_equal('FUNCTIONS SORTED ON TOTAL TIME', lines[18]) + call assert_equal('count total (s) self (s) function', lines[19]) + call assert_match('^\s*1\s\+\d\+\.\d\+\s\+Foo2()$', lines[20]) + call assert_match('^\s*2\s\+\d\+\.\d\+\s\+Foo1()$', lines[21]) + call assert_equal('', lines[22]) + call assert_equal('FUNCTIONS SORTED ON SELF TIME', lines[23]) + call assert_equal('count total (s) self (s) function', lines[24]) + call assert_match('^\s*1\s\+\d\+\.\d\+\s\+Foo2()$', lines[25]) + call assert_match('^\s*2\s\+\d\+\.\d\+\s\+Foo1()$', lines[26]) + call assert_equal('', lines[27]) + + call delete('Xprofile_func.vim') + call delete('Xprofile_func.log') +endfunc + +func Test_profile_file() + let lines = [ + \ 'func! Foo()', + \ 'endfunc', + \ 'for i in range(10)', + \ ' " a comment', + \ ' call Foo()', + \ 'endfor', + \ 'call Foo()', + \ ] + + call writefile(lines, 'Xprofile_file.vim') + call system(v:progpath + \ . ' -es -u NONE -U NONE -i NONE --noplugin' + \ . ' -c "profile start Xprofile_file.log"' + \ . ' -c "profile file Xprofile_file.vim"' + \ . ' -c "so Xprofile_file.vim"' + \ . ' -c "so Xprofile_file.vim"' + \ . ' -c "qall!"') + call assert_equal(0, v:shell_error) + + let lines = readfile('Xprofile_file.log') + + call assert_equal(14, len(lines)) + + call assert_match('^SCRIPT .*Xprofile_file.vim$', lines[0]) + call assert_equal('Sourced 2 times', lines[1]) + call assert_match('^Total time:\s\+\d\+\.\d\+$', lines[2]) + call assert_match('^ Self time:\s\+\d\+\.\d\+$', lines[3]) + call assert_equal('', lines[4]) + call assert_equal('count total (s) self (s)', lines[5]) + call assert_match(' 2 0.\d\+ func! Foo()', lines[6]) + call assert_equal(' endfunc', lines[7]) + " Loop iterates 10 times. Since script runs twice, body executes 20 times. + " First line of loop executes one more time than body to detect end of loop. + call assert_match('^\s*22\s\+\d\+\.\d\+\s\+for i in range(10)$', lines[8]) + call assert_equal(' " a comment', lines[9]) + " if self and total are equal we only get one number + call assert_match('^\s*20\s\+\(\d\+\.\d\+\s\+\)\=\d\+\.\d\+\s\+call Foo()$', lines[10]) + call assert_match('^\s*20\s\+\d\+\.\d\+\s\+endfor$', lines[11]) + " if self and total are equal we only get one number + call assert_match('^\s*2\s\+\(\d\+\.\d\+\s\+\)\=\d\+\.\d\+\s\+call Foo()$', lines[12]) + call assert_equal('', lines[13]) + + call delete('Xprofile_file.vim') + call delete('Xprofile_file.log') +endfunc + +func Test_profile_file_with_cont() + let lines = [ + \ 'echo "hello', + \ ' \ world"', + \ 'echo "foo ', + \ ' \bar"', + \ ] + + call writefile(lines, 'Xprofile_file.vim') + call system(v:progpath + \ . ' -es -u NONE -U NONE -i NONE --noplugin' + \ . ' -c "profile start Xprofile_file.log"' + \ . ' -c "profile file Xprofile_file.vim"' + \ . ' -c "so Xprofile_file.vim"' + \ . ' -c "qall!"') + call assert_equal(0, v:shell_error) + + let lines = readfile('Xprofile_file.log') + call assert_equal(11, len(lines)) + + call assert_match('^SCRIPT .*Xprofile_file.vim$', lines[0]) + call assert_equal('Sourced 1 time', lines[1]) + call assert_match('^Total time:\s\+\d\+\.\d\+$', lines[2]) + call assert_match('^ Self time:\s\+\d\+\.\d\+$', lines[3]) + call assert_equal('', lines[4]) + call assert_equal('count total (s) self (s)', lines[5]) + call assert_match(' 1 0.\d\+ echo "hello', lines[6]) + call assert_equal(' \ world"', lines[7]) + call assert_match(' 1 0.\d\+ echo "foo ', lines[8]) + call assert_equal(' \bar"', lines[9]) + call assert_equal('', lines[10]) + + call delete('Xprofile_file.vim') + call delete('Xprofile_file.log') +endfunc + +func Test_profile_completion() + call feedkeys(":profile \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"profile continue dump file func pause start stop', @:) + + call feedkeys(":profile start test_prof\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_match('^"profile start.* test_profile\.vim', @:) +endfunc + +func Test_profile_errors() + call assert_fails("profile func Foo", 'E750:') + call assert_fails("profile pause", 'E750:') + call assert_fails("profile continue", 'E750:') +endfunc diff --git a/src/nvim/testdir/test_put.vim b/src/nvim/testdir/test_put.vim new file mode 100644 index 0000000000..38c812bc9c --- /dev/null +++ b/src/nvim/testdir/test_put.vim @@ -0,0 +1,36 @@ + +func Test_put_block() + if !has('multi_byte') + return + endif + new + call feedkeys("i\<C-V>u2500\<CR>x\<ESC>", 'x') + call feedkeys("\<C-V>y", 'x') + call feedkeys("gg0p", 'x') + call assert_equal("\u2500x", getline(1)) + bwipe! +endfunc + +func Test_put_char_block() + new + call setline(1, ['Line 1', 'Line 2']) + f Xfile_put + " visually select both lines and put the cursor at the top of the visual + " selection and then put the buffer name over it + exe "norm! G0\<c-v>ke\"%p" + call assert_equal(['Xfile_put 1', 'Xfile_put 2'], getline(1,2)) + bw! +endfunc + +func Test_put_char_block2() + new + let a = [ getreg('a'), getregtype('a') ] + call setreg('a', ' one ', 'v') + call setline(1, ['Line 1', '', 'Line 3', '']) + " visually select the first 3 lines and put register a over it + exe "norm! ggl\<c-v>2j2l\"ap" + call assert_equal(['L one 1', '', 'L one 3', ''], getline(1,4)) + " clean up + bw! + call setreg('a', a[0], a[1]) +endfunc diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 75ab01f013..dd177fd633 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -6,7 +6,7 @@ endif set encoding=utf-8 -function! s:setup_commands(cchar) +func s:setup_commands(cchar) if a:cchar == 'c' command! -nargs=* -bang Xlist <mods>clist<bang> <args> command! -nargs=* Xgetexpr <mods>cgetexpr <args> @@ -24,19 +24,21 @@ function! s:setup_commands(cchar) command! -nargs=* Xgetbuffer <mods>cgetbuffer <args> command! -nargs=* Xaddbuffer <mods>caddbuffer <args> command! -nargs=* Xrewind <mods>crewind <args> - command! -nargs=* -bang Xnext <mods>cnext<bang> <args> - command! -nargs=* -bang Xprev <mods>cprev<bang> <args> + command! -count -nargs=* -bang Xnext <mods><count>cnext<bang> <args> + command! -count -nargs=* -bang Xprev <mods><count>cprev<bang> <args> command! -nargs=* -bang Xfirst <mods>cfirst<bang> <args> command! -nargs=* -bang Xlast <mods>clast<bang> <args> command! -nargs=* -bang Xnfile <mods>cnfile<bang> <args> command! -nargs=* -bang Xpfile <mods>cpfile<bang> <args> command! -nargs=* Xexpr <mods>cexpr <args> - command! -nargs=* Xvimgrep <mods>vimgrep <args> + command! -range -nargs=* Xvimgrep <mods><count>vimgrep <args> + command! -nargs=* Xvimgrepadd <mods>vimgrepadd <args> command! -nargs=* Xgrep <mods> grep <args> command! -nargs=* Xgrepadd <mods> grepadd <args> command! -nargs=* Xhelpgrep helpgrep <args> let g:Xgetlist = function('getqflist') let g:Xsetlist = function('setqflist') + call setqflist([], 'f') else command! -nargs=* -bang Xlist <mods>llist<bang> <args> command! -nargs=* Xgetexpr <mods>lgetexpr <args> @@ -54,26 +56,31 @@ function! s:setup_commands(cchar) command! -nargs=* Xgetbuffer <mods>lgetbuffer <args> command! -nargs=* Xaddbuffer <mods>laddbuffer <args> command! -nargs=* Xrewind <mods>lrewind <args> - command! -nargs=* -bang Xnext <mods>lnext<bang> <args> - command! -nargs=* -bang Xprev <mods>lprev<bang> <args> + command! -count -nargs=* -bang Xnext <mods><count>lnext<bang> <args> + command! -count -nargs=* -bang Xprev <mods><count>lprev<bang> <args> command! -nargs=* -bang Xfirst <mods>lfirst<bang> <args> command! -nargs=* -bang Xlast <mods>llast<bang> <args> command! -nargs=* -bang Xnfile <mods>lnfile<bang> <args> command! -nargs=* -bang Xpfile <mods>lpfile<bang> <args> command! -nargs=* Xexpr <mods>lexpr <args> - command! -nargs=* Xvimgrep <mods>lvimgrep <args> + command! -range -nargs=* Xvimgrep <mods><count>lvimgrep <args> + command! -nargs=* Xvimgrepadd <mods>lvimgrepadd <args> command! -nargs=* Xgrep <mods> lgrep <args> command! -nargs=* Xgrepadd <mods> lgrepadd <args> command! -nargs=* Xhelpgrep lhelpgrep <args> let g:Xgetlist = function('getloclist', [0]) let g:Xsetlist = function('setloclist', [0]) + call setloclist(0, [], 'f') endif -endfunction +endfunc " Tests for the :clist and :llist commands -function XlistTests(cchar) +func XlistTests(cchar) call s:setup_commands(a:cchar) + if a:cchar == 'l' + call assert_fails('llist', 'E776:') + endif " With an empty list, command should return error Xgetexpr [] silent! Xlist @@ -85,62 +92,68 @@ function XlistTests(cchar) \ 'non-error 3', 'Xtestfile3:3:1:Line3'] " List only valid entries - redir => result - Xlist - redir END - let l = split(result, "\n") + let l = split(execute('Xlist', ''), "\n") call assert_equal([' 2 Xtestfile1:1 col 3: Line1', \ ' 4 Xtestfile2:2 col 2: Line2', \ ' 6 Xtestfile3:3 col 1: Line3'], l) " List all the entries - redir => result - Xlist! - redir END - let l = split(result, "\n") + let l = split(execute('Xlist!', ''), "\n") call assert_equal([' 1: non-error 1', ' 2 Xtestfile1:1 col 3: Line1', \ ' 3: non-error 2', ' 4 Xtestfile2:2 col 2: Line2', \ ' 5: non-error 3', ' 6 Xtestfile3:3 col 1: Line3'], l) " List a range of errors - redir => result - Xlist 3,6 - redir END - let l = split(result, "\n") + let l = split(execute('Xlist 3,6', ''), "\n") call assert_equal([' 4 Xtestfile2:2 col 2: Line2', \ ' 6 Xtestfile3:3 col 1: Line3'], l) - redir => result - Xlist! 3,4 - redir END - let l = split(result, "\n") + let l = split(execute('Xlist! 3,4', ''), "\n") call assert_equal([' 3: non-error 2', ' 4 Xtestfile2:2 col 2: Line2'], l) - redir => result - Xlist -6,-4 - redir END - let l = split(result, "\n") + let l = split(execute('Xlist -6,-4', ''), "\n") call assert_equal([' 2 Xtestfile1:1 col 3: Line1'], l) - redir => result - Xlist! -5,-3 - redir END - let l = split(result, "\n") + let l = split(execute('Xlist! -5,-3', ''), "\n") + call assert_equal([' 2 Xtestfile1:1 col 3: Line1', + \ ' 3: non-error 2', ' 4 Xtestfile2:2 col 2: Line2'], l) + + " Test for '+' + let l = split(execute('Xlist! +2', ''), "\n") call assert_equal([' 2 Xtestfile1:1 col 3: Line1', \ ' 3: non-error 2', ' 4 Xtestfile2:2 col 2: Line2'], l) -endfunction -function Test_clist() + " Different types of errors + call g:Xsetlist([{'lnum':10,'col':5,'type':'W', 'text':'Warning','nr':11}, + \ {'lnum':20,'col':10,'type':'e','text':'Error','nr':22}, + \ {'lnum':30,'col':15,'type':'i','text':'Info','nr':33}, + \ {'lnum':40,'col':20,'type':'x', 'text':'Other','nr':44}, + \ {'lnum':50,'col':25,'type':"\<C-A>",'text':'one','nr':55}]) + let l = split(execute('Xlist', ""), "\n") + call assert_equal([' 1:10 col 5 warning 11: Warning', + \ ' 2:20 col 10 error 22: Error', + \ ' 3:30 col 15 info 33: Info', + \ ' 4:40 col 20 x 44: Other', + \ ' 5:50 col 25 55: one'], l) + + " Error cases + call assert_fails('Xlist abc', 'E488:') +endfunc + +func Test_clist() call XlistTests('c') call XlistTests('l') -endfunction +endfunc " Tests for the :colder, :cnewer, :lolder and :lnewer commands " Note that this test assumes that a quickfix/location list is " already set by the caller. -function XageTests(cchar) +func XageTests(cchar) call s:setup_commands(a:cchar) + let list = [{'bufnr': bufnr('%'), 'lnum': 1}] + call g:Xsetlist(list) + " Jumping to a non existent list should return error silent! Xolder 99 call assert_true(v:errmsg ==# 'E380: At bottom of quickfix stack') @@ -171,22 +184,23 @@ function XageTests(cchar) Xnewer 2 let l = g:Xgetlist() call assert_equal('Line3', l[0].text) -endfunction +endfunc -function Test_cage() - let list = [{'bufnr': 1, 'lnum': 1}] - call setqflist(list) +func Test_cage() call XageTests('c') - - call setloclist(0, list) call XageTests('l') -endfunction +endfunc " Tests for the :cwindow, :lwindow :cclose, :lclose, :copen and :lopen " commands -function XwindowTests(cchar) +func XwindowTests(cchar) call s:setup_commands(a:cchar) + " Opening the location list window without any errors should fail + if a:cchar == 'l' + call assert_fails('lopen', 'E776:') + endif + " Create a list with no valid entries Xgetexpr ['non-error 1', 'non-error 2', 'non-error 3'] @@ -227,16 +241,29 @@ function XwindowTests(cchar) " Calling cwindow should close the quickfix window with no valid errors Xwindow call assert_true(winnr('$') == 1) -endfunction -function Test_cwindow() + if a:cchar == 'c' + " Opening the quickfix window in multiple tab pages should reuse the + " quickfix buffer + Xgetexpr ['Xtestfile1:1:3:Line1', 'Xtestfile2:2:2:Line2', + \ 'Xtestfile3:3:1:Line3'] + Xopen + let qfbufnum = bufnr('%') + tabnew + Xopen + call assert_equal(qfbufnum, bufnr('%')) + new | only | tabonly + endif +endfunc + +func Test_cwindow() call XwindowTests('c') call XwindowTests('l') -endfunction +endfunc " Tests for the :cfile, :lfile, :caddfile, :laddfile, :cgetfile and :lgetfile " commands. -function XfileTests(cchar) +func XfileTests(cchar) call s:setup_commands(a:cchar) call writefile(['Xtestfile1:700:10:Line 700', @@ -275,16 +302,16 @@ function XfileTests(cchar) \ l[1].lnum == 333 && l[1].col == 88 && l[1].text ==# 'Line 333') call delete('Xqftestfile1') -endfunction +endfunc -function Test_cfile() +func Test_cfile() call XfileTests('c') call XfileTests('l') -endfunction +endfunc " Tests for the :cbuffer, :lbuffer, :caddbuffer, :laddbuffer, :cgetbuffer and " :lgetbuffer commands. -function XbufferTests(cchar) +func XbufferTests(cchar) call s:setup_commands(a:cchar) enew! @@ -316,35 +343,61 @@ function XbufferTests(cchar) \ l[3].lnum == 750 && l[3].col == 25 && l[3].text ==# 'Line 750') enew! -endfunction + " Check for invalid buffer + call assert_fails('Xbuffer 199', 'E474:') -function Test_cbuffer() + " Check for unloaded buffer + edit Xtestfile1 + let bnr = bufnr('%') + enew! + call assert_fails('Xbuffer ' . bnr, 'E681:') + + " Check for invalid range + " Using Xbuffer will not run the range check in the cbuffer/lbuffer + " commands. So directly call the commands. + if (a:cchar == 'c') + call assert_fails('900,999cbuffer', 'E16:') + else + call assert_fails('900,999lbuffer', 'E16:') + endif +endfunc + +func Test_cbuffer() call XbufferTests('c') call XbufferTests('l') -endfunction +endfunc -function XexprTests(cchar) +func XexprTests(cchar) call s:setup_commands(a:cchar) call assert_fails('Xexpr 10', 'E777:') -endfunction +endfunc -function Test_cexpr() +func Test_cexpr() call XexprTests('c') call XexprTests('l') -endfunction +endfunc " Tests for :cnext, :cprev, :cfirst, :clast commands -function Xtest_browse(cchar) +func Xtest_browse(cchar) call s:setup_commands(a:cchar) + " Jumping to first or next location list entry without any error should + " result in failure + if a:cchar == 'l' + call assert_fails('lfirst', 'E776:') + call assert_fails('lnext', 'E776:') + endif + call s:create_test_file('Xqftestfile1') call s:create_test_file('Xqftestfile2') Xgetexpr ['Xqftestfile1:5:Line5', \ 'Xqftestfile1:6:Line6', \ 'Xqftestfile2:10:Line10', - \ 'Xqftestfile2:11:Line11'] + \ 'Xqftestfile2:11:Line11', + \ 'RegularLine1', + \ 'RegularLine2'] Xfirst call assert_fails('Xprev', 'E553') @@ -356,6 +409,7 @@ function Xtest_browse(cchar) call assert_equal('Xqftestfile1', bufname('%')) call assert_equal(6, line('.')) Xlast + Xprev call assert_equal('Xqftestfile2', bufname('%')) call assert_equal(11, line('.')) call assert_fails('Xnext', 'E553') @@ -364,16 +418,26 @@ function Xtest_browse(cchar) call assert_equal('Xqftestfile1', bufname('%')) call assert_equal(5, line('.')) + 10Xnext + call assert_equal('Xqftestfile2', bufname('%')) + call assert_equal(11, line('.')) + 10Xprev + call assert_equal('Xqftestfile1', bufname('%')) + call assert_equal(5, line('.')) + + Xexpr "" + call assert_fails('Xnext', 'E42:') + call delete('Xqftestfile1') call delete('Xqftestfile2') -endfunction +endfunc -function Test_browse() +func Test_browse() call Xtest_browse('c') call Xtest_browse('l') -endfunction +endfunc -function! s:test_xhelpgrep(cchar) +func s:test_xhelpgrep(cchar) call s:setup_commands(a:cchar) Xhelpgrep quickfix Xopen @@ -383,11 +447,35 @@ function! s:test_xhelpgrep(cchar) let title_text = ':lhelpgrep quickfix' endif call assert_true(w:quickfix_title =~ title_text, w:quickfix_title) + + " Jumping to a help topic should open the help window + only + Xnext + call assert_true(&buftype == 'help') + call assert_true(winnr('$') == 2) + " Jumping to the next match should reuse the help window + Xnext + call assert_true(&buftype == 'help') + call assert_true(winnr() == 1) + call assert_true(winnr('$') == 2) + " Jumping to the next match from the quickfix window should reuse the help + " window + Xopen + Xnext + call assert_true(&buftype == 'help') + call assert_true(winnr() == 1) + call assert_true(winnr('$') == 2) + " This wipes out the buffer, make sure that doesn't cause trouble. Xclose -endfunction -function Test_helpgrep() + new | only + + " Search for non existing help string + call assert_fails('Xhelpgrep a1b2c3', 'E480:') +endfunc + +func Test_helpgrep() call s:test_xhelpgrep('c') helpclose call s:test_xhelpgrep('l') @@ -425,7 +513,7 @@ func Test_vimgreptitle() augroup! QfBufWinEnter endfunc -function XqfTitleTests(cchar) +func XqfTitleTests(cchar) call s:setup_commands(a:cchar) Xgetexpr ['file:1:1:message'] @@ -444,16 +532,16 @@ function XqfTitleTests(cchar) endif call assert_equal(title, w:quickfix_title) Xclose -endfunction +endfunc " Tests for quickfix window's title -function Test_qf_title() +func Test_qf_title() call XqfTitleTests('c') call XqfTitleTests('l') -endfunction +endfunc " Tests for 'errorformat' -function Test_efm() +func Test_efm() let save_efm = &efm set efm=%EEEE%m,%WWWW%m,%+CCCC%.%#,%-GGGG%.%# cgetexpr ['WWWW', 'EEEE', 'CCCC'] @@ -466,7 +554,7 @@ function Test_efm() let l = strtrans(string(map(getqflist(), '[v:val.text, v:val.valid]'))) call assert_equal("[['W', 1], ['ZZZZ', 0], ['E^@CCCC', 1], ['YYYY', 0]]", l) let &efm = save_efm -endfunction +endfunc " This will test for problems in quickfix: " A. incorrectly copying location lists which caused the location list to show @@ -477,7 +565,7 @@ endfunction " window it belongs to. " " Set up the test environment: -function! ReadTestProtocol(name) +func ReadTestProtocol(name) let base = substitute(a:name, '\v^test://(.*)%(\.[^.]+)?', '\1', '') let word = substitute(base, '\v(.*)\..*', '\1', '') @@ -496,9 +584,9 @@ function! ReadTestProtocol(name) setl nomodifiable setl readonly exe 'doautocmd BufRead ' . substitute(a:name, '\v^test://(.*)', '\1', '') -endfunction +endfunc -function Test_locationlist() +func Test_locationlist() enew augroup testgroup @@ -521,10 +609,7 @@ function Test_locationlist() lrewind enew lopen - lnext - lnext - lnext - lnext + 4lnext vert split wincmd L lopen @@ -578,15 +663,15 @@ function Test_locationlist() wincmd n | only augroup! testgroup -endfunction +endfunc -function Test_locationlist_curwin_was_closed() +func Test_locationlist_curwin_was_closed() augroup testgroup au! autocmd BufReadCmd test_curwin.txt call R(expand("<amatch>")) augroup END - function! R(n) + func! R(n) quit endfunc @@ -597,9 +682,9 @@ function Test_locationlist_curwin_was_closed() call assert_fails('lrewind', 'E924:') augroup! testgroup -endfunction +endfunc -function Test_locationlist_cross_tab_jump() +func Test_locationlist_cross_tab_jump() call writefile(['loclistfoo'], 'loclistfoo') call writefile(['loclistbar'], 'loclistbar') set switchbuf=usetab @@ -613,10 +698,10 @@ function Test_locationlist_cross_tab_jump() set switchbuf&vim call delete('loclistfoo') call delete('loclistbar') -endfunction +endfunc " More tests for 'errorformat' -function! Test_efm1() +func Test_efm1() if !has('unix') " The 'errorformat' setting is different on non-Unix systems. " This test works only on Unix-like systems. @@ -734,10 +819,10 @@ function! Test_efm1() call delete('Xerrorfile1') call delete('Xerrorfile2') call delete('Xtestfile') -endfunction +endfunc " Test for quickfix directory stack support -function! s:dir_stack_tests(cchar) +func s:dir_stack_tests(cchar) call s:setup_commands(a:cchar) let save_efm=&efm @@ -779,10 +864,10 @@ function! s:dir_stack_tests(cchar) call assert_equal(5, qf[11].lnum) let &efm=save_efm -endfunction +endfunc " Tests for %D and %X errorformat options -function! Test_efm_dirstack() +func Test_efm_dirstack() " Create the directory stack and files call mkdir('dir1') call mkdir('dir1/a') @@ -814,10 +899,33 @@ function! Test_efm_dirstack() call delete('dir1', 'rf') call delete('dir2', 'rf') call delete('habits1.txt') -endfunction +endfunc + +" Test for resync after continuing an ignored message +func Xefm_ignore_continuations(cchar) + call s:setup_commands(a:cchar) + + let save_efm = &efm + + let &efm = + \ '%Eerror %m %l,' . + \ '%-Wignored %m %l,' . + \ '%+Cmore ignored %m %l,' . + \ '%Zignored end' + Xgetexpr ['ignored warning 1', 'more ignored continuation 2', 'ignored end', 'error resync 4'] + let l = map(g:Xgetlist(), '[v:val.text, v:val.valid, v:val.lnum, v:val.type]') + call assert_equal([['resync', 1, 4, 'E']], l) + + let &efm = save_efm +endfunc + +func Test_efm_ignore_continuations() + call Xefm_ignore_continuations('c') + call Xefm_ignore_continuations('l') +endfunc " Tests for invalid error format specifies -function Xinvalid_efm_Tests(cchar) +func Xinvalid_efm_Tests(cchar) call s:setup_commands(a:cchar) let save_efm = &efm @@ -850,17 +958,17 @@ function Xinvalid_efm_Tests(cchar) call assert_fails('Xexpr ["Entering dir abc", "abc.txt:1:Hello world"]', 'E379:') let &efm = save_efm -endfunction +endfunc -function Test_invalid_efm() +func Test_invalid_efm() call Xinvalid_efm_Tests('c') call Xinvalid_efm_Tests('l') -endfunction +endfunc " TODO: " Add tests for the following formats in 'errorformat' " %r %O -function! Test_efm2() +func Test_efm2() let save_efm = &efm " Test for %s format in efm @@ -870,30 +978,44 @@ function! Test_efm2() call assert_equal(l[0].pattern, '^\VLine search text\$') call assert_equal(l[0].lnum, 0) + let l = split(execute('clist', ''), "\n") + call assert_equal([' 1 Xtestfile:^\VLine search text\$: '], l) + " Test for %P, %Q and %t format specifiers let lines=["[Xtestfile1]", \ "(1,17) error: ';' missing", \ "(21,2) warning: variable 'z' not defined", \ "(67,3) error: end of file found before string ended", + \ "--", \ "", \ "[Xtestfile2]", + \ "--", \ "", \ "[Xtestfile3]", \ "NEW compiler v1.1", \ "(2,2) warning: variable 'x' not defined", - \ "(67,3) warning: 's' already defined" + \ "(67,3) warning: 's' already defined", + \ "--" \] - set efm=%+P[%f],(%l\\,%c)%*[\ ]%t%*[^:]:\ %m,%-Q + set efm=%+P[%f]%r,(%l\\,%c)%*[\ ]%t%*[^:]:\ %m,%+Q--%r + " To exercise the push/pop file functionality in quickfix, the test files + " need to be created. + call writefile(['Line1'], 'Xtestfile1') + call writefile(['Line2'], 'Xtestfile2') + call writefile(['Line3'], 'Xtestfile3') cexpr "" for l in lines caddexpr l endfor let l = getqflist() - call assert_equal(9, len(l)) + call assert_equal(12, len(l)) call assert_equal(21, l[2].lnum) call assert_equal(2, l[2].col) call assert_equal('w', l[2].type) call assert_equal('e', l[3].type) + call delete('Xtestfile1') + call delete('Xtestfile2') + call delete('Xtestfile3') " Tests for %E, %C and %Z format specifiers let lines = ["Error 275", @@ -945,20 +1067,39 @@ function! Test_efm2() call assert_equal(1, l[4].valid) call assert_equal('unittests/dbfacadeTest.py', bufname(l[4].bufnr)) + " The following sequence of commands used to crash Vim + set efm=%W%m + cgetexpr ['msg1'] + let l = getqflist() + call assert_equal(1, len(l), string(l)) + call assert_equal('msg1', l[0].text) + set efm=%C%m + lexpr 'msg2' + let l = getloclist(0) + call assert_equal(1, len(l), string(l)) + call assert_equal('msg2', l[0].text) + lopen + call setqflist([], 'r') + caddbuf + let l = getqflist() + call assert_equal(1, len(l), string(l)) + call assert_equal('|| msg2', l[0].text) + + new | only let &efm = save_efm -endfunction +endfunc -function XquickfixChangedByAutocmd(cchar) +func XquickfixChangedByAutocmd(cchar) call s:setup_commands(a:cchar) if a:cchar == 'c' let ErrorNr = 'E925' - function! ReadFunc() + func! ReadFunc() colder cgetexpr [] endfunc else let ErrorNr = 'E926' - function! ReadFunc() + func! ReadFunc() lolder lgetexpr [] endfunc @@ -981,10 +1122,10 @@ function XquickfixChangedByAutocmd(cchar) augroup! testgroup endfunc -function Test_quickfix_was_changed_by_autocmd() +func Test_quickfix_was_changed_by_autocmd() call XquickfixChangedByAutocmd('c') call XquickfixChangedByAutocmd('l') -endfunction +endfunc func Test_caddbuffer_to_empty() helpgr quickfix @@ -1006,7 +1147,7 @@ func Test_cgetexpr_works() endfunc " Tests for the setqflist() and setloclist() functions -function SetXlistTests(cchar, bnum) +func SetXlistTests(cchar, bnum) call s:setup_commands(a:cchar) call g:Xsetlist([{'bufnr': a:bnum, 'lnum': 1}, @@ -1041,9 +1182,35 @@ function SetXlistTests(cchar, bnum) call g:Xsetlist([]) let l = g:Xgetlist() call assert_equal(0, len(l)) -endfunction -function Test_setqflist() + " Tests for setting the 'valid' flag + call g:Xsetlist([{'bufnr':a:bnum, 'lnum':4, 'valid':0}]) + Xwindow + call assert_equal(1, winnr('$')) + let l = g:Xgetlist() + call g:Xsetlist(l) + call assert_equal(0, g:Xgetlist()[0].valid) + call g:Xsetlist([{'text':'Text1', 'valid':1}]) + Xwindow + call assert_equal(2, winnr('$')) + Xclose + let save_efm = &efm + set efm=%m + Xgetexpr 'TestMessage' + let l = g:Xgetlist() + call g:Xsetlist(l) + call assert_equal(1, g:Xgetlist()[0].valid) + let &efm = save_efm + + " Error cases: + " Refer to a non-existing buffer and pass a non-dictionary type + call assert_fails("call g:Xsetlist([{'bufnr':998, 'lnum':4}," . + \ " {'bufnr':999, 'lnum':5}])", 'E92:') + call g:Xsetlist([[1, 2,3]]) + call assert_equal(0, len(g:Xgetlist())) +endfunc + +func Test_setqflist() new Xtestfile | only let bnum = bufnr('%') call setline(1, range(1,5)) @@ -1053,13 +1220,14 @@ function Test_setqflist() enew! call delete('Xtestfile') -endfunction +endfunc -function Xlist_empty_middle(cchar) +func Xlist_empty_middle(cchar) call s:setup_commands(a:cchar) " create three quickfix lists - Xvimgrep Test_ test_quickfix.vim + let @/ = 'Test_' + Xvimgrep // test_quickfix.vim let testlen = len(g:Xgetlist()) call assert_true(testlen > 0) Xvimgrep empty test_quickfix.vim @@ -1078,12 +1246,12 @@ function Xlist_empty_middle(cchar) call assert_equal(matchlen, len(g:Xgetlist())) endfunc -function Test_setqflist_empty_middle() +func Test_setqflist_empty_middle() call Xlist_empty_middle('c') call Xlist_empty_middle('l') -endfunction +endfunc -function Xlist_empty_older(cchar) +func Xlist_empty_older(cchar) call s:setup_commands(a:cchar) " create three quickfix lists @@ -1104,14 +1272,14 @@ function Xlist_empty_older(cchar) call assert_equal(twolen, len(g:Xgetlist())) Xnewer call assert_equal(threelen, len(g:Xgetlist())) -endfunction +endfunc -function Test_setqflist_empty_older() +func Test_setqflist_empty_older() call Xlist_empty_older('c') call Xlist_empty_older('l') -endfunction +endfunc -function! XquickfixSetListWithAct(cchar) +func XquickfixSetListWithAct(cchar) call s:setup_commands(a:cchar) let list1 = [{'filename': 'fnameA', 'text': 'A'}, @@ -1185,12 +1353,12 @@ function! XquickfixSetListWithAct(cchar) call assert_fails("call g:Xsetlist(list1, 0)", 'E928:') endfunc -function Test_quickfix_set_list_with_act() +func Test_quickfix_set_list_with_act() call XquickfixSetListWithAct('c') call XquickfixSetListWithAct('l') -endfunction +endfunc -function XLongLinesTests(cchar) +func XLongLinesTests(cchar) let l = g:Xgetlist() call assert_equal(4, len(l)) @@ -1208,9 +1376,9 @@ function XLongLinesTests(cchar) call assert_equal(10, len(l[3].text)) call g:Xsetlist([], 'r') -endfunction +endfunc -function s:long_lines_tests(cchar) +func s:long_lines_tests(cchar) call s:setup_commands(a:cchar) let testfile = 'samples/quickfix.txt' @@ -1231,22 +1399,22 @@ function s:long_lines_tests(cchar) exe 'edit' testfile exe 'Xbuffer' bufnr('%') call XLongLinesTests(a:cchar) -endfunction +endfunc -function Test_long_lines() +func Test_long_lines() call s:long_lines_tests('c') call s:long_lines_tests('l') -endfunction +endfunc -function! s:create_test_file(filename) +func s:create_test_file(filename) let l = [] for i in range(1, 20) call add(l, 'Line' . i) endfor call writefile(l, a:filename) -endfunction +endfunc -function! Test_switchbuf() +func Test_switchbuf() call s:create_test_file('Xqftestfile1') call s:create_test_file('Xqftestfile2') call s:create_test_file('Xqftestfile3') @@ -1267,18 +1435,18 @@ function! Test_switchbuf() let winid = win_getid() cfirst | cnext call assert_equal(winid, win_getid()) - cnext | cnext + 2cnext call assert_equal(winid, win_getid()) - cnext | cnext + 2cnext call assert_equal(winid, win_getid()) enew set switchbuf=useopen cfirst | cnext call assert_equal(file1_winid, win_getid()) - cnext | cnext + 2cnext call assert_equal(file2_winid, win_getid()) - cnext | cnext + 2cnext call assert_equal(file2_winid, win_getid()) enew | only @@ -1288,9 +1456,9 @@ function! Test_switchbuf() tabfirst cfirst | cnext call assert_equal(2, tabpagenr()) - cnext | cnext + 2cnext call assert_equal(3, tabpagenr()) - cnext | cnext + 2cnext call assert_equal(3, tabpagenr()) tabfirst | tabonly | enew @@ -1328,14 +1496,28 @@ function! Test_switchbuf() call assert_equal(2, winnr('$')) call assert_equal(1, bufwinnr('Xqftestfile3')) + " If only quickfix window is open in the current tabpage, jumping to an + " entry with 'switchubf' set to 'usetab' should search in other tabpages. enew | only + set switchbuf=usetab + tabedit Xqftestfile1 + tabedit Xqftestfile2 + tabedit Xqftestfile3 + tabfirst + copen | only + clast + call assert_equal(4, tabpagenr()) + tabfirst | tabonly | enew | only call delete('Xqftestfile1') call delete('Xqftestfile2') call delete('Xqftestfile3') -endfunction + set switchbuf&vim -function! Xadjust_qflnum(cchar) + enew | only +endfunc + +func Xadjust_qflnum(cchar) call s:setup_commands(a:cchar) enew | only @@ -1360,17 +1542,17 @@ function! Xadjust_qflnum(cchar) enew! call delete(fname) -endfunction +endfunc -function! Test_adjust_lnum() +func Test_adjust_lnum() call setloclist(0, []) call Xadjust_qflnum('c') call setqflist([]) call Xadjust_qflnum('l') -endfunction +endfunc " Tests for the :grep/:lgrep and :grepadd/:lgrepadd commands -function! s:test_xgrep(cchar) +func s:test_xgrep(cchar) call s:setup_commands(a:cchar) " The following lines are used for the grep test. Don't remove. @@ -1389,9 +1571,9 @@ function! s:test_xgrep(cchar) set makeef=Temp_File_## silent Xgrepadd GrepAdd_Test_Text: test_quickfix.vim call assert_true(len(g:Xgetlist()) == 6) -endfunction +endfunc -function! Test_grep() +func Test_grep() if !has('unix') " The grepprg may not be set on non-Unix systems return @@ -1399,9 +1581,9 @@ function! Test_grep() call s:test_xgrep('c') call s:test_xgrep('l') -endfunction +endfunc -function! Test_two_windows() +func Test_two_windows() " Use one 'errorformat' for two windows. Add an expression to each of them, " make sure they each keep their own state. set efm=%DEntering\ dir\ '%f',%f:%l:%m,%XLeaving\ dir\ '%f' @@ -1427,12 +1609,10 @@ function! Test_two_windows() laddexpr 'one.txt:3:one one one' let loc_one = getloclist(one_id) -echo string(loc_one) call assert_equal('Xone/a/one.txt', bufname(loc_one[1].bufnr)) call assert_equal(3, loc_one[1].lnum) let loc_two = getloclist(two_id) -echo string(loc_two) call assert_equal('Xtwo/a/two.txt', bufname(loc_two[1].bufnr)) call assert_equal(5, loc_two[1].lnum) @@ -1444,15 +1624,20 @@ echo string(loc_two) call delete('Xtwo', 'rf') endfunc -function XbottomTests(cchar) +func XbottomTests(cchar) call s:setup_commands(a:cchar) - call g:Xsetlist([{'filename': 'foo', 'lnum': 42}]) + " Calling lbottom without any errors should fail + if a:cchar == 'l' + call assert_fails('lbottom', 'E776:') + endif + + call g:Xsetlist([{'filename': 'foo', 'lnum': 42}]) Xopen let wid = win_getid() call assert_equal(1, line('.')) wincmd w - call g:Xsetlist([{'filename': 'var', 'lnum': 24}], 'a') + call g:Xsetlist([{'filename': 'var', 'lnum': 24}], 'a') Xbottom call win_gotoid(wid) call assert_equal(2, line('.')) @@ -1460,18 +1645,17 @@ function XbottomTests(cchar) endfunc " Tests for the :cbottom and :lbottom commands -function Test_cbottom() +func Test_cbottom() call XbottomTests('c') call XbottomTests('l') -endfunction +endfunc -function HistoryTest(cchar) +func HistoryTest(cchar) call s:setup_commands(a:cchar) - call assert_fails(a:cchar . 'older 99', 'E380:') " clear all lists after the first one, then replace the first one. call g:Xsetlist([]) - Xolder + call assert_fails('Xolder 99', 'E380:') let entry = {'filename': 'foo', 'lnum': 42} call g:Xsetlist([entry], 'r') call g:Xsetlist([entry, entry]) @@ -1505,7 +1689,7 @@ func Test_duplicate_buf() endfunc " Quickfix/Location list set/get properties tests -function Xproperty_tests(cchar) +func Xproperty_tests(cchar) call s:setup_commands(a:cchar) " Error cases @@ -1514,6 +1698,7 @@ function Xproperty_tests(cchar) call assert_fails('call g:Xsetlist([], "a", [])', 'E715:') " Set and get the title + call g:Xsetlist([]) Xopen wincmd p call g:Xsetlist([{'filename':'foo', 'lnum':27}]) @@ -1532,9 +1717,30 @@ function Xproperty_tests(cchar) call assert_equal('N1', g:Xgetlist({'all':1}).title) call g:Xsetlist([], ' ', {'title' : 'N2'}) call assert_equal(qfnr + 1, g:Xgetlist({'all':1}).nr) + + let res = g:Xgetlist({'nr': 0}) + call assert_equal(qfnr + 1, res.nr) + call assert_equal(['nr'], keys(res)) + call g:Xsetlist([], ' ', {'title' : 'N3'}) call assert_equal('N2', g:Xgetlist({'nr':2, 'title':1}).title) + " Changing the title of an earlier quickfix list + call g:Xsetlist([], ' ', {'title' : 'NewTitle', 'nr' : 2}) + call assert_equal('NewTitle', g:Xgetlist({'nr':2, 'title':1}).title) + + " Changing the title of an invalid quickfix list + call assert_equal(-1, g:Xsetlist([], ' ', + \ {'title' : 'SomeTitle', 'nr' : 99})) + call assert_equal(-1, g:Xsetlist([], ' ', + \ {'title' : 'SomeTitle', 'nr' : 'abc'})) + + if a:cchar == 'c' + copen + call assert_equal({'winid':win_getid()}, getqflist({'winid':1})) + cclose + endif + " Invalid arguments call assert_fails('call g:Xgetlist([])', 'E715') call assert_fails('call g:Xsetlist([], "a", [])', 'E715') @@ -1542,23 +1748,155 @@ function Xproperty_tests(cchar) call assert_equal(-1, s) call assert_equal({}, g:Xgetlist({'abc':1})) + call assert_equal({}, g:Xgetlist({'nr':99, 'title':1})) + call assert_equal({}, g:Xgetlist({'nr':[], 'title':1})) if a:cchar == 'l' - call assert_equal({}, getloclist(99, ['title'])) + call assert_equal({}, getloclist(99, {'title': 1})) endif -endfunction -function Test_qf_property() + " Context related tests + call g:Xsetlist([], 'a', {'context':[1,2,3]}) + call test_garbagecollect_now() + let d = g:Xgetlist({'context':1}) + call assert_equal([1,2,3], d.context) + call g:Xsetlist([], 'a', {'context':{'color':'green'}}) + let d = g:Xgetlist({'context':1}) + call assert_equal({'color':'green'}, d.context) + call g:Xsetlist([], 'a', {'context':"Context info"}) + let d = g:Xgetlist({'context':1}) + call assert_equal("Context info", d.context) + call g:Xsetlist([], 'a', {'context':246}) + let d = g:Xgetlist({'context':1}) + call assert_equal(246, d.context) + if a:cchar == 'l' + " Test for copying context across two different location lists + new | only + let w1_id = win_getid() + let l = [1] + call setloclist(0, [], 'a', {'context':l}) + new + let w2_id = win_getid() + call add(l, 2) + call assert_equal([1, 2], getloclist(w1_id, {'context':1}).context) + call assert_equal([1, 2], getloclist(w2_id, {'context':1}).context) + unlet! l + call assert_equal([1, 2], getloclist(w2_id, {'context':1}).context) + only + call setloclist(0, [], 'f') + call assert_equal({}, getloclist(0, {'context':1})) + endif + + " Test for changing the context of previous quickfix lists + call g:Xsetlist([], 'f') + Xexpr "One" + Xexpr "Two" + Xexpr "Three" + call g:Xsetlist([], ' ', {'context' : [1], 'nr' : 1}) + call g:Xsetlist([], ' ', {'context' : [2], 'nr' : 2}) + " Also, check for setting the context using quickfix list number zero. + call g:Xsetlist([], ' ', {'context' : [3], 'nr' : 0}) + call test_garbagecollect_now() + let l = g:Xgetlist({'nr' : 1, 'context' : 1}) + call assert_equal([1], l.context) + let l = g:Xgetlist({'nr' : 2, 'context' : 1}) + call assert_equal([2], l.context) + let l = g:Xgetlist({'nr' : 3, 'context' : 1}) + call assert_equal([3], l.context) + + " Test for changing the context through reference and for garbage + " collection of quickfix context + let l = ["red"] + call g:Xsetlist([], ' ', {'context' : l}) + call add(l, "blue") + let x = g:Xgetlist({'context' : 1}) + call add(x.context, "green") + call assert_equal(["red", "blue", "green"], l) + call assert_equal(["red", "blue", "green"], x.context) + unlet l + call test_garbagecollect_now() + let m = g:Xgetlist({'context' : 1}) + call assert_equal(["red", "blue", "green"], m.context) + + " Test for setting/getting items + Xexpr "" + let qfprev = g:Xgetlist({'nr':0}) + call g:Xsetlist([], ' ', {'title':'Green', + \ 'items' : [{'filename':'F1', 'lnum':10}]}) + let qfcur = g:Xgetlist({'nr':0}) + call assert_true(qfcur.nr == qfprev.nr + 1) + let l = g:Xgetlist({'items':1}) + call assert_equal('F1', bufname(l.items[0].bufnr)) + call assert_equal(10, l.items[0].lnum) + call g:Xsetlist([], 'a', {'items' : [{'filename':'F2', 'lnum':20}, + \ {'filename':'F2', 'lnum':30}]}) + let l = g:Xgetlist({'items':1}) + call assert_equal('F2', bufname(l.items[2].bufnr)) + call assert_equal(30, l.items[2].lnum) + call g:Xsetlist([], 'r', {'items' : [{'filename':'F3', 'lnum':40}]}) + let l = g:Xgetlist({'items':1}) + call assert_equal('F3', bufname(l.items[0].bufnr)) + call assert_equal(40, l.items[0].lnum) + call g:Xsetlist([], 'r', {'items' : []}) + let l = g:Xgetlist({'items':1}) + call assert_equal(0, len(l.items)) + + " Save and restore the quickfix stack + call g:Xsetlist([], 'f') + call assert_equal(0, g:Xgetlist({'nr':'$'}).nr) + Xexpr "File1:10:Line1" + Xexpr "File2:20:Line2" + Xexpr "File3:30:Line3" + let last_qf = g:Xgetlist({'nr':'$'}).nr + call assert_equal(3, last_qf) + let qstack = [] + for i in range(1, last_qf) + let qstack = add(qstack, g:Xgetlist({'nr':i, 'all':1})) + endfor + call g:Xsetlist([], 'f') + for i in range(len(qstack)) + call g:Xsetlist([], ' ', qstack[i]) + endfor + call assert_equal(3, g:Xgetlist({'nr':'$'}).nr) + call assert_equal(10, g:Xgetlist({'nr':1, 'items':1}).items[0].lnum) + call assert_equal(20, g:Xgetlist({'nr':2, 'items':1}).items[0].lnum) + call assert_equal(30, g:Xgetlist({'nr':3, 'items':1}).items[0].lnum) + call g:Xsetlist([], 'f') + + " Swap two quickfix lists + Xexpr "File1:10:Line10" + Xexpr "File2:20:Line20" + Xexpr "File3:30:Line30" + call g:Xsetlist([], 'r', {'nr':1,'title':'Colors','context':['Colors']}) + call g:Xsetlist([], 'r', {'nr':2,'title':'Fruits','context':['Fruits']}) + let l1=g:Xgetlist({'nr':1,'all':1}) + let l2=g:Xgetlist({'nr':2,'all':1}) + let l1.nr=2 + let l2.nr=1 + call g:Xsetlist([], 'r', l1) + call g:Xsetlist([], 'r', l2) + let newl1=g:Xgetlist({'nr':1,'all':1}) + let newl2=g:Xgetlist({'nr':2,'all':1}) + call assert_equal(':Fruits', newl1.title) + call assert_equal(['Fruits'], newl1.context) + call assert_equal('Line20', newl1.items[0].text) + call assert_equal(':Colors', newl2.title) + call assert_equal(['Colors'], newl2.context) + call assert_equal('Line10', newl2.items[0].text) + call g:Xsetlist([], 'f') +endfunc + +func Test_qf_property() call Xproperty_tests('c') call Xproperty_tests('l') -endfunction +endfunc " Tests for the QuickFixCmdPre/QuickFixCmdPost autocommands -function QfAutoCmdHandler(loc, cmd) +func QfAutoCmdHandler(loc, cmd) call add(g:acmds, a:loc . a:cmd) -endfunction +endfunc -function Test_Autocmd() +func Test_Autocmd() autocmd QuickFixCmdPre * call QfAutoCmdHandler('pre', expand('<amatch>')) autocmd QuickFixCmdPost * call QfAutoCmdHandler('post', expand('<amatch>')) @@ -1586,9 +1924,9 @@ function Test_Autocmd() \ 'precaddbuffer', \ 'postcaddbuffer'] call assert_equal(l, g:acmds) -endfunction +endfunc -function! Test_Autocmd_Exception() +func Test_Autocmd_Exception() set efm=%m lgetexpr '?' @@ -1603,4 +1941,271 @@ function! Test_Autocmd_Exception() call assert_equal('1', getloclist(0)[0].text) set efm&vim -endfunction +endfunc + +func Test_caddbuffer_wrong() + " This used to cause a memory access in freed memory. + let save_efm = &efm + set efm=%EEEE%m,%WWWW,%+CCCC%>%#,%GGGG%.# + cgetexpr ['WWWW', 'EEEE', 'CCCC'] + let &efm = save_efm + caddbuffer + bwipe! +endfunc + +func Test_caddexpr_wrong() + " This used to cause a memory access in freed memory. + cbuffer + cbuffer + copen + let save_efm = &efm + set efm=% + call assert_fails('caddexpr ""', 'E376:') + let &efm = save_efm +endfunc + +func Test_dirstack_cleanup() + " This used to cause a memory access in freed memory. + let save_efm = &efm + lexpr '0' + lopen + fun X(c) + let save_efm=&efm + set efm=%D%f + if a:c == 'c' + caddexpr '::' + else + laddexpr ':0:0' + endif + let &efm=save_efm + endfun + call X('c') + call X('l') + call setqflist([], 'r') + caddbuffer + let &efm = save_efm +endfunc + +" Tests for jumping to entries from the location list window and quickfix +" window +func Test_cwindow_jump() + set efm=%f%%%l%%%m + lgetexpr ["F1%10%Line 10", "F2%20%Line 20", "F3%30%Line 30"] + lopen | only + lfirst + call assert_true(winnr('$') == 2) + call assert_true(winnr() == 1) + " Location list for the new window should be set + call assert_true(getloclist(0)[2].text == 'Line 30') + + " Open a scratch buffer + " Open a new window and create a location list + " Open the location list window and close the other window + " Jump to an entry. + " Should create a new window and jump to the entry. The scrtach buffer + " should not be used. + enew | only + set buftype=nofile + below new + lgetexpr ["F1%10%Line 10", "F2%20%Line 20", "F3%30%Line 30"] + lopen + 2wincmd c + lnext + call assert_true(winnr('$') == 3) + call assert_true(winnr() == 2) + + " Open two windows with two different location lists + " Open the location list window and close the previous window + " Jump to an entry in the location list window + " Should open the file in the first window and not set the location list. + enew | only + lgetexpr ["F1%5%Line 5"] + below new + lgetexpr ["F1%10%Line 10", "F2%20%Line 20", "F3%30%Line 30"] + lopen + 2wincmd c + lnext + call assert_true(winnr() == 1) + call assert_true(getloclist(0)[0].text == 'Line 5') + + enew | only + cgetexpr ["F1%10%Line 10", "F2%20%Line 20", "F3%30%Line 30"] + copen + cnext + call assert_true(winnr('$') == 2) + call assert_true(winnr() == 1) + + enew | only + set efm&vim +endfunc + +func XvimgrepTests(cchar) + call s:setup_commands(a:cchar) + + call writefile(['Editor:VIM vim', + \ 'Editor:Emacs EmAcS', + \ 'Editor:Notepad NOTEPAD'], 'Xtestfile1') + call writefile(['Linux', 'MacOS', 'MS-Windows'], 'Xtestfile2') + + " Error cases + call assert_fails('Xvimgrep /abc *', 'E682:') + + let @/='' + call assert_fails('Xvimgrep // *', 'E35:') + + call assert_fails('Xvimgrep abc', 'E683:') + call assert_fails('Xvimgrep a1b2c3 Xtestfile1', 'E480:') + call assert_fails('Xvimgrep pat Xa1b2c3', 'E480:') + + Xexpr "" + Xvimgrepadd Notepad Xtestfile1 + Xvimgrepadd MacOS Xtestfile2 + let l = g:Xgetlist() + call assert_equal(2, len(l)) + call assert_equal('Editor:Notepad NOTEPAD', l[0].text) + + Xvimgrep #\cvim#g Xtestfile? + let l = g:Xgetlist() + call assert_equal(2, len(l)) + call assert_equal(8, l[0].col) + call assert_equal(12, l[1].col) + + 1Xvimgrep ?Editor? Xtestfile* + let l = g:Xgetlist() + call assert_equal(1, len(l)) + call assert_equal('Editor:VIM vim', l[0].text) + + edit +3 Xtestfile2 + Xvimgrep +\cemacs+j Xtestfile1 + let l = g:Xgetlist() + call assert_equal('Xtestfile2', bufname('')) + call assert_equal('Editor:Emacs EmAcS', l[0].text) + + call delete('Xtestfile1') + call delete('Xtestfile2') +endfunc + +" Tests for the :vimgrep command +func Test_vimgrep() + call XvimgrepTests('c') + call XvimgrepTests('l') +endfunc + +func XfreeTests(cchar) + call s:setup_commands(a:cchar) + + enew | only + + " Deleting the quickfix stack should work even When the current list is + " somewhere in the middle of the stack + Xexpr ['Xfile1:10:10:Line 10', 'Xfile1:15:15:Line 15'] + Xexpr ['Xfile2:20:20:Line 20', 'Xfile2:25:25:Line 25'] + Xexpr ['Xfile3:30:30:Line 30', 'Xfile3:35:35:Line 35'] + Xolder + call g:Xsetlist([], 'f') + call assert_equal(0, len(g:Xgetlist())) + + " After deleting the stack, adding a new list should create a stack with a + " single list. + Xexpr ['Xfile1:10:10:Line 10', 'Xfile1:15:15:Line 15'] + call assert_equal(1, g:Xgetlist({'all':1}).nr) + + " Deleting the stack from a quickfix window should update/clear the + " quickfix/location list window. + Xexpr ['Xfile1:10:10:Line 10', 'Xfile1:15:15:Line 15'] + Xexpr ['Xfile2:20:20:Line 20', 'Xfile2:25:25:Line 25'] + Xexpr ['Xfile3:30:30:Line 30', 'Xfile3:35:35:Line 35'] + Xolder + Xwindow + call g:Xsetlist([], 'f') + call assert_equal(2, winnr('$')) + call assert_equal(1, line('$')) + Xclose + + " Deleting the stack from a non-quickfix window should update/clear the + " quickfix/location list window. + Xexpr ['Xfile1:10:10:Line 10', 'Xfile1:15:15:Line 15'] + Xexpr ['Xfile2:20:20:Line 20', 'Xfile2:25:25:Line 25'] + Xexpr ['Xfile3:30:30:Line 30', 'Xfile3:35:35:Line 35'] + Xolder + Xwindow + wincmd p + call g:Xsetlist([], 'f') + call assert_equal(0, len(g:Xgetlist())) + wincmd p + call assert_equal(2, winnr('$')) + call assert_equal(1, line('$')) + + " After deleting the location list stack, if the location list window is + " opened, then a new location list should be created. So opening the + " location list window again should not create a new window. + if a:cchar == 'l' + lexpr ['Xfile1:10:10:Line 10', 'Xfile1:15:15:Line 15'] + wincmd p + lopen + call assert_equal(2, winnr('$')) + endif + Xclose +endfunc + +" Tests for the quickifx free functionality +func Test_qf_free() + call XfreeTests('c') + call XfreeTests('l') +endfunc + +" Test for buffer overflow when parsing lines and adding new entries to +" the quickfix list. +func Test_bufoverflow() + set efm=%f:%l:%m + cgetexpr ['File1:100:' . repeat('x', 1025)] + + set efm=%+GCompiler:\ %.%#,%f:%l:%m + cgetexpr ['Compiler: ' . repeat('a', 1015), 'File1:10:Hello World'] + + set efm=%DEntering\ directory\ %f,%f:%l:%m + cgetexpr ['Entering directory ' . repeat('a', 1006), + \ 'File1:10:Hello World'] + set efm&vim +endfunc + +func Test_cclose_from_copen() + augroup QF_Test + au! + au FileType qf :cclose + augroup END + copen + augroup QF_Test + au! + augroup END + augroup! QF_Test +endfunc + +" Tests for getting the quickfix stack size +func XsizeTests(cchar) + call s:setup_commands(a:cchar) + + call g:Xsetlist([], 'f') + call assert_equal(0, g:Xgetlist({'nr':'$'}).nr) + call assert_equal(1, len(g:Xgetlist({'nr':'$', 'all':1}))) + call assert_equal(0, len(g:Xgetlist({'nr':0}))) + + Xexpr "File1:10:Line1" + Xexpr "File2:20:Line2" + Xexpr "File3:30:Line3" + Xolder | Xolder + call assert_equal(3, g:Xgetlist({'nr':'$'}).nr) + call g:Xsetlist([], 'f') + + Xexpr "File1:10:Line1" + Xexpr "File2:20:Line2" + Xexpr "File3:30:Line3" + Xolder | Xolder + call g:Xsetlist([], 'a', {'nr':'$', 'title':'Compiler'}) + call assert_equal('Compiler', g:Xgetlist({'nr':3, 'all':1}).title) +endfunc + +func Test_Qf_Size() + call XsizeTests('c') + call XsizeTests('l') +endfunc diff --git a/src/nvim/testdir/test_recover.vim b/src/nvim/testdir/test_recover.vim new file mode 100644 index 0000000000..46d884a97c --- /dev/null +++ b/src/nvim/testdir/test_recover.vim @@ -0,0 +1,63 @@ +" Test :recover + +func Test_recover_root_dir() + " This used to access invalid memory. + split Xtest + set dir=/ + call assert_fails('recover', 'E305:') + close! + + if has('win32') || filewritable('/') == 2 + " can write in / directory on MS-Windows + set dir=/notexist/ + endif + call assert_fails('split Xtest', 'E303:') + set dir& +endfunc + +" Inserts 10000 lines with text to fill the swap file with two levels of pointer +" blocks. Then recovers from the swap file and checks all text is restored. +" +" We need about 10000 lines of 100 characters to get two levels of pointer +" blocks. +func Test_swap_file() + set directory=. + set fileformat=unix undolevels=-1 + edit! Xtest + let text = "\tabcdefghijklmnoparstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnoparstuvwxyz0123456789" + let i = 1 + let linecount = 10000 + while i <= linecount + call append(i - 1, i . text) + let i += 1 + endwhile + $delete + preserve + " get the name of the swap file + let swname = split(execute("swapname"))[0] + let swname = substitute(swname, '[[:blank:][:cntrl:]]*\(.\{-}\)[[:blank:][:cntrl:]]*$', '\1', '') + " make a copy of the swap file in Xswap + set binary + exe 'sp ' . swname + w! Xswap + set nobinary + new + only! + bwipe! Xtest + call rename('Xswap', swname) + recover Xtest + call delete(swname) + let linedollar = line('$') + call assert_equal(linecount, linedollar) + if linedollar < linecount + let linecount = linedollar + endif + let i = 1 + while i <= linecount + call assert_equal(i . text, getline(i)) + let i += 1 + endwhile + + set undolevels& + enew! | only +endfunc diff --git a/src/nvim/testdir/test_regexp_latin.vim b/src/nvim/testdir/test_regexp_latin.vim new file mode 100644 index 0000000000..8528412806 --- /dev/null +++ b/src/nvim/testdir/test_regexp_latin.vim @@ -0,0 +1,32 @@ +" Tests for regexp in latin1 encoding +set encoding=latin1 +scriptencoding latin1 + +func s:equivalence_test() + let str = "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z" + let groups = split(str) + for group1 in groups + for c in split(group1, '\zs') + " next statement confirms that equivalence class matches every + " character in group + call assert_match('^[[=' . c . '=]]*$', group1) + for group2 in groups + if group2 != group1 + " next statement converts that equivalence class doesn't match + " a character in any other group + call assert_equal(-1, match(group2, '[[=' . c . '=]]')) + endif + endfor + endfor + endfor +endfunc + +func Test_equivalence_re1() + set re=1 + call s:equivalence_test() +endfunc + +func Test_equivalence_re2() + set re=2 + call s:equivalence_test() +endfunc diff --git a/src/nvim/testdir/test_regexp_utf8.vim b/src/nvim/testdir/test_regexp_utf8.vim index 7f3b31575d..a2f4286d4f 100644 --- a/src/nvim/testdir/test_regexp_utf8.vim +++ b/src/nvim/testdir/test_regexp_utf8.vim @@ -98,6 +98,21 @@ func Test_recursive_substitute() bwipe! endfunc +func Test_nested_backrefs() + " Check example in change.txt. + new + for re in range(0, 2) + exe 'set re=' . re + call setline(1, 'aa ab x') + 1s/\(\(a[a-d] \)*\)\(x\)/-\1- -\2- -\3-/ + call assert_equal('-aa ab - -ab - -x-', getline(1)) + + call assert_equal('-aa ab - -ab - -x-', substitute('aa ab x', '\(\(a[a-d] \)*\)\(x\)', '-\1- -\2- -\3-', '')) + endfor + bwipe! + set re=0 +endfunc + func Test_eow_with_optional() let expected = ['abc def', 'abc', 'def', '', '', '', '', '', '', ''] for re in range(0, 2) diff --git a/src/nvim/testdir/test_retab.vim b/src/nvim/testdir/test_retab.vim new file mode 100644 index 0000000000..f11a32bade --- /dev/null +++ b/src/nvim/testdir/test_retab.vim @@ -0,0 +1,77 @@ +" Test :retab +func SetUp() + new + call setline(1, "\ta \t b c ") +endfunc + +func TearDown() + bwipe! +endfunc + +func Retab(bang, n) + let l:old_tabstop = &tabstop + let l:old_line = getline(1) + exe "retab" . a:bang . a:n + let l:line = getline(1) + call setline(1, l:old_line) + if a:n > 0 + " :retab changes 'tabstop' to n with argument n > 0. + call assert_equal(a:n, &tabstop) + exe 'set tabstop=' . l:old_tabstop + else + " :retab does not change 'tabstop' with empty or n <= 0. + call assert_equal(l:old_tabstop, &tabstop) + endif + return l:line +endfunc + +func Test_retab() + set tabstop=8 noexpandtab + call assert_equal("\ta\t b c ", Retab('', '')) + call assert_equal("\ta\t b c ", Retab('', 0)) + call assert_equal("\ta\t b c ", Retab('', 8)) + call assert_equal("\ta\t b\t c\t ", Retab('!', '')) + call assert_equal("\ta\t b\t c\t ", Retab('!', 0)) + call assert_equal("\ta\t b\t c\t ", Retab('!', 8)) + + call assert_equal("\t\ta\t\t\tb c ", Retab('', 4)) + call assert_equal("\t\ta\t\t\tb\t\t c\t ", Retab('!', 4)) + + call assert_equal(" a\t\tb c ", Retab('', 10)) + call assert_equal(" a\t\tb c ", Retab('!', 10)) + + set tabstop=8 expandtab + call assert_equal(" a b c ", Retab('', '')) + call assert_equal(" a b c ", Retab('', 0)) + call assert_equal(" a b c ", Retab('', 8)) + call assert_equal(" a b c ", Retab('!', '')) + call assert_equal(" a b c ", Retab('!', 0)) + call assert_equal(" a b c ", Retab('!', 8)) + + call assert_equal(" a b c ", Retab(' ', 4)) + call assert_equal(" a b c ", Retab('!', 4)) + + call assert_equal(" a b c ", Retab(' ', 10)) + call assert_equal(" a b c ", Retab('!', 10)) + + set tabstop=4 noexpandtab + call assert_equal("\ta\t\tb c ", Retab('', '')) + call assert_equal("\ta\t\tb\t\t c\t ", Retab('!', '')) + call assert_equal("\t a\t\t\tb c ", Retab('', 3)) + call assert_equal("\t a\t\t\tb\t\t\tc\t ", Retab('!', 3)) + call assert_equal(" a\t b c ", Retab('', 5)) + call assert_equal(" a\t b\t\t c\t ", Retab('!', 5)) + + set tabstop=4 expandtab + call assert_equal(" a b c ", Retab('', '')) + call assert_equal(" a b c ", Retab('!', '')) + call assert_equal(" a b c ", Retab('', 3)) + call assert_equal(" a b c ", Retab('!', 3)) + call assert_equal(" a b c ", Retab('', 5)) + call assert_equal(" a b c ", Retab('!', 5)) +endfunc + +func Test_retab_error() + call assert_fails('retab -1', 'E487:') + call assert_fails('retab! -1', 'E487:') +endfunc diff --git a/src/nvim/testdir/test_scrollbind.vim b/src/nvim/testdir/test_scrollbind.vim new file mode 100644 index 0000000000..baa24f1979 --- /dev/null +++ b/src/nvim/testdir/test_scrollbind.vim @@ -0,0 +1,32 @@ +" Test for 'scrollbind' causing an unexpected scroll of one of the windows. +func Test_scrollbind() + " We don't want the status line to cause problems: + set laststatus=0 + let totalLines = &lines * 20 + let middle = totalLines / 2 + new | only + for i in range(1, totalLines) + call setline(i, 'LINE ' . i) + endfor + exe string(middle) + normal zt + normal M + aboveleft vert new + for i in range(1, totalLines) + call setline(i, 'line ' . i) + endfor + exe string(middle) + normal zt + normal M + " Execute the following two commands at once to reproduce the problem. + setl scb | wincmd p + setl scb + wincmd w + let topLineLeft = line('w0') + wincmd p + let topLineRight = line('w0') + setl noscrollbind + wincmd p + setl noscrollbind + call assert_equal(0, topLineLeft - topLineRight) +endfunc diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim index 2106fc2dec..5da9397be5 100644 --- a/src/nvim/testdir/test_search.vim +++ b/src/nvim/testdir/test_search.vim @@ -2,13 +2,13 @@ func Test_search_cmdline() " See test/functional/legacy/search_spec.lua - throw 'skipped: Nvim does not support test_disable_char_avail()' + throw 'skipped: Nvim does not support test_override()' if !exists('+incsearch') return endif " need to disable char_avail, " so that expansion of commandline works - call test_disable_char_avail(1) + call test_override("char_avail", 1) new call setline(1, [' 1', ' 2 these', ' 3 the', ' 4 their', ' 5 there', ' 6 their', ' 7 the', ' 8 them', ' 9 these', ' 10 foobar']) " Test 1 @@ -195,19 +195,19 @@ func Test_search_cmdline() call assert_equal(' 3 the', getline('.')) " clean up - call test_disable_char_avail(0) + call test_override("char_avail", 0) bw! endfunc func Test_search_cmdline2() " See test/functional/legacy/search_spec.lua - throw 'skipped: Nvim does not support test_disable_char_avail()' + throw 'skipped: Nvim does not support test_override()' if !exists('+incsearch') return endif " need to disable char_avail, " so that expansion of commandline works - call test_disable_char_avail(1) + call test_override("char_avail", 1) new call setline(1, [' 1', ' 2 these', ' 3 the theother']) " Test 1 @@ -269,7 +269,7 @@ func Test_search_cmdline2() " clean up set noincsearch - call test_disable_char_avail(0) + call test_override("char_avail", 0) bw! endfunc @@ -283,3 +283,173 @@ func Test_use_sub_pat() call X() bwipe! endfunc + +func Test_searchpair() + new + call setline(1, ['other code here', '', '[', '" cursor here', ']']) + 4 + let a=searchpair('\[','',']','bW') + call assert_equal(3, a) + set nomagic + 4 + let a=searchpair('\[','',']','bW') + call assert_equal(3, a) + set magic + q! +endfunc + +func Test_searchc() + " These commands used to cause memory overflow in searchc(). + new + norm ixx + exe "norm 0t\u93cf" + bw! +endfunc + +func Test_search_cmdline3() + throw 'skipped: Nvim does not support test_override()' + if !exists('+incsearch') + return + endif + " need to disable char_avail, + " so that expansion of commandline works + call test_override("char_avail", 1) + new + call setline(1, [' 1', ' 2 the~e', ' 3 the theother']) + set incsearch + 1 + " first match + call feedkeys("/the\<c-l>\<cr>", 'tx') + call assert_equal(' 2 the~e', getline('.')) + " clean up + set noincsearch + call test_override("char_avail", 0) + bw! +endfunc + +func Test_search_cmdline4() + " See test/functional/legacy/search_spec.lua + throw 'skipped: Nvim does not support test_override()' + if !exists('+incsearch') + return + endif + " need to disable char_avail, + " so that expansion of commandline works + call test_override("char_avail", 1) + new + call setline(1, [' 1 the first', ' 2 the second', ' 3 the third']) + set incsearch + $ + call feedkeys("?the\<c-g>\<cr>", 'tx') + call assert_equal(' 3 the third', getline('.')) + $ + call feedkeys("?the\<c-g>\<c-g>\<cr>", 'tx') + call assert_equal(' 1 the first', getline('.')) + $ + call feedkeys("?the\<c-g>\<c-g>\<c-g>\<cr>", 'tx') + call assert_equal(' 2 the second', getline('.')) + $ + call feedkeys("?the\<c-t>\<cr>", 'tx') + call assert_equal(' 1 the first', getline('.')) + $ + call feedkeys("?the\<c-t>\<c-t>\<cr>", 'tx') + call assert_equal(' 3 the third', getline('.')) + $ + call feedkeys("?the\<c-t>\<c-t>\<c-t>\<cr>", 'tx') + call assert_equal(' 2 the second', getline('.')) + " clean up + set noincsearch + call test_override("char_avail", 0) + bw! +endfunc + +func Test_search_cmdline5() + if !exists('+incsearch') + return + endif + " Do not call test_override("char_avail", 1) so that <C-g> and <C-t> work + " regardless char_avail. + new + call setline(1, [' 1 the first', ' 2 the second', ' 3 the third']) + set incsearch + 1 + call feedkeys("/the\<c-g>\<c-g>\<cr>", 'tx') + call assert_equal(' 3 the third', getline('.')) + $ + call feedkeys("?the\<c-t>\<c-t>\<c-t>\<cr>", 'tx') + call assert_equal(' 2 the second', getline('.')) + " clean up + set noincsearch + bw! +endfunc + +" Tests for regexp with various magic settings +func Test_search_regexp() + enew! + + put ='1 a aa abb abbccc' + exe 'normal! /a*b\{2}c\+/e' . "\<CR>" + call assert_equal([0, 2, 17, 0], getpos('.')) + + put ='2 d dd dee deefff' + exe 'normal! /\Md\*e\{2}f\+/e' . "\<CR>" + call assert_equal([0, 3, 17, 0], getpos('.')) + + set nomagic + put ='3 g gg ghh ghhiii' + exe 'normal! /g\*h\{2}i\+/e' . "\<CR>" + call assert_equal([0, 4, 17, 0], getpos('.')) + + put ='4 j jj jkk jkklll' + exe 'normal! /\mj*k\{2}l\+/e' . "\<CR>" + call assert_equal([0, 5, 17, 0], getpos('.')) + + put ='5 m mm mnn mnnooo' + exe 'normal! /\vm*n{2}o+/e' . "\<CR>" + call assert_equal([0, 6, 17, 0], getpos('.')) + + put ='6 x ^aa$ x' + exe 'normal! /\V^aa$' . "\<CR>" + call assert_equal([0, 7, 5, 0], getpos('.')) + + set magic + put ='7 (a)(b) abbaa' + exe 'normal! /\v(a)(b)\2\1\1/e' . "\<CR>" + call assert_equal([0, 8, 14, 0], getpos('.')) + + put ='8 axx [ab]xx' + exe 'normal! /\V[ab]\(\[xy]\)\1' . "\<CR>" + call assert_equal([0, 9, 7, 0], getpos('.')) + + set undolevels=100 + put ='9 foobar' + put ='' + exe "normal! a\<C-G>u\<Esc>" + normal G + exe 'normal! dv?bar?' . "\<CR>" + call assert_equal('9 foo', getline('.')) + call assert_equal([0, 10, 5, 0], getpos('.')) + call assert_equal(10, line('$')) + normal u + call assert_equal('9 foobar', getline('.')) + call assert_equal([0, 10, 6, 0], getpos('.')) + call assert_equal(11, line('$')) + + set undolevels& + enew! +endfunc + +" Test for search('multi-byte char', 'bce') +func Test_search_multibyte() + if !has('multi_byte') + return + endif + let save_enc = &encoding + set encoding=utf8 + enew! + call append('$', 'A') + call cursor(2, 1) + call assert_equal(2, search('A', 'bce', line('.'))) + enew! + let &encoding = save_enc +endfunc diff --git a/src/nvim/testdir/test_sha256.vim b/src/nvim/testdir/test_sha256.vim new file mode 100644 index 0000000000..dd4707977e --- /dev/null +++ b/src/nvim/testdir/test_sha256.vim @@ -0,0 +1,22 @@ +" Tests for the sha256() function. + +if !has('cryptv') || !exists('*sha256') + finish +endif + +function Test_sha256() + " test for empty string: + call assert_equal(sha256(""), 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855') + + "'test for 1 char: + call assert_equal(sha256("a"), 'ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb') + " + "test for 3 chars: + call assert_equal(sha256("abc"), 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad') + + " test for contains meta char: + call assert_equal(sha256("foo\nbar"), '807eff6267f3f926a21d234f7b0cf867a86f47e07a532f15e8cc39ed110ca776') + + " test for contains non-ascii char: + call assert_equal(sha256("\xde\xad\xbe\xef"), '5f78c33274e43fa9de5659265c1d917e25c03722dcb0b8d27db8d5feaa813953') +endfunction diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim index 75dbd74b34..a967435346 100644 --- a/src/nvim/testdir/test_signs.vim +++ b/src/nvim/testdir/test_signs.vim @@ -182,7 +182,7 @@ func Test_sign_invalid_commands() call assert_fails('sign define Sign1 xxx', 'E475:') call assert_fails('sign undefine', 'E156:') call assert_fails('sign list xxx', 'E155:') - call assert_fails('sign place 1 buffer=', 'E158:') + call assert_fails('sign place 1 buffer=999', 'E158:') call assert_fails('sign define Sign2 text=', 'E239:') endfunc diff --git a/src/nvim/testdir/test_source_utf8.vim b/src/nvim/testdir/test_source_utf8.vim index edb76fc43d..c29c2ec1f3 100644 --- a/src/nvim/testdir/test_source_utf8.vim +++ b/src/nvim/testdir/test_source_utf8.vim @@ -31,3 +31,33 @@ func Test_source_latin() bwipe! call delete('Xscript') endfunc + +" Test for sourcing a file with CTRL-V's at the end of the line +func Test_source_ctrl_v() + call writefile(['map __1 afirst', + \ 'map __2 asecond', + \ 'map __3 athird', + \ 'map __4 afourth', + \ 'map __5 afifth', + \ "map __1 asd\<C-V>", + \ "map __2 asd\<C-V>\<C-V>", + \ "map __3 asd\<C-V>\<C-V>", + \ "map __4 asd\<C-V>\<C-V>\<C-V>", + \ "map __5 asd\<C-V>\<C-V>\<C-V>", + \ ], 'Xtestfile') + source Xtestfile + enew! + exe "normal __1\<Esc>\<Esc>__2\<Esc>__3\<Esc>\<Esc>__4\<Esc>__5\<Esc>" + exe "%s/\<C-J>/0/g" + call assert_equal(['sd', + \ "map __2 asd\<Esc>secondsd\<Esc>sd0map __5 asd0fifth"], + \ getline(1, 2)) + + enew! + call delete('Xtestfile') + unmap __1 + unmap __2 + unmap __3 + unmap __4 + unmap __5 +endfunc diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim new file mode 100644 index 0000000000..21f2363731 --- /dev/null +++ b/src/nvim/testdir/test_spell.vim @@ -0,0 +1,821 @@ +" Test spell checking + +if !has('spell') + finish +endif + +func TearDown() + set nospell + call delete('Xtest.aff') + call delete('Xtest.dic') + call delete('Xtest.latin1.add') + call delete('Xtest.latin1.add.spl') + call delete('Xtest.latin1.spl') + call delete('Xtest.latin1.sug') +endfunc + +func Test_wrap_search() + new + call setline(1, ['The', '', 'A plong line with two zpelling mistakes', '', 'End']) + set spell wrapscan + normal ]s + call assert_equal('plong', expand('<cword>')) + normal ]s + call assert_equal('zpelling', expand('<cword>')) + normal ]s + call assert_equal('plong', expand('<cword>')) + bwipe! + set nospell +endfunc + +func Test_curswant() + new + call setline(1, ['Another plong line', 'abcdefghijklmnopq']) + set spell wrapscan + normal 0]s + call assert_equal('plong', expand('<cword>')) + normal j + call assert_equal(9, getcurpos()[2]) + normal 0[s + call assert_equal('plong', expand('<cword>')) + normal j + call assert_equal(9, getcurpos()[2]) + + normal 0]S + call assert_equal('plong', expand('<cword>')) + normal j + call assert_equal(9, getcurpos()[2]) + normal 0[S + call assert_equal('plong', expand('<cword>')) + normal j + call assert_equal(9, getcurpos()[2]) + + normal 1G0 + call assert_equal('plong', spellbadword()[0]) + normal j + call assert_equal(9, getcurpos()[2]) + + bwipe! + set nospell +endfunc + +func Test_z_equal_on_invalid_utf8_word() + split + set spell + call setline(1, "\xff") + norm z= + set nospell + bwipe! +endfunc + +func Test_spellreall() + new + set spell + call assert_fails('spellrepall', 'E752:') + call setline(1, ['A speling mistake. The same speling mistake.', + \ 'Another speling mistake.']) + call feedkeys(']s1z=', 'tx') + call assert_equal('A spelling mistake. The same speling mistake.', getline(1)) + call assert_equal('Another speling mistake.', getline(2)) + spellrepall + call assert_equal('A spelling mistake. The same spelling mistake.', getline(1)) + call assert_equal('Another spelling mistake.', getline(2)) + call assert_fails('spellrepall', 'E753:') + set spell& + bwipe! +endfunc + +func Test_zz_basic() + call LoadAffAndDic(g:test_data_aff1, g:test_data_dic1) + call RunGoodBad("wrong OK puts. Test the end", + \ "bad: inputs comment ok Ok. test d\xE9\xF4l end the", + \["Comment", "deol", "d\xE9\xF4r", "input", "OK", "output", "outputs", "outtest", "put", "puts", + \ "test", "testen", "testn", "the end", "uk", "wrong"], + \[ + \ ["bad", ["put", "uk", "OK"]], + \ ["inputs", ["input", "puts", "outputs"]], + \ ["comment", ["Comment", "outtest", "the end"]], + \ ["ok", ["OK", "uk", "put"]], + \ ["Ok", ["OK", "Uk", "Put"]], + \ ["test", ["Test", "testn", "testen"]], + \ ["d\xE9\xF4l", ["deol", "d\xE9\xF4r", "test"]], + \ ["end", ["put", "uk", "test"]], + \ ["the", ["put", "uk", "test"]], + \ ] + \ ) + + call assert_equal("gebletegek", soundfold('goobledygoook')) + call assert_equal("kepereneven", soundfold('koprnven')) + call assert_equal("everles gesvets etele", soundfold('oeverloos gezwets edale')) +endfunc + +" Postponed prefixes +func Test_zz_prefixes() + call LoadAffAndDic(g:test_data_aff2, g:test_data_dic1) + call RunGoodBad("puts", + \ "bad: inputs comment ok Ok end the. test d\xE9\xF4l", + \ ["Comment", "deol", "d\xE9\xF4r", "OK", "put", "input", "output", "puts", "outputs", "test", "outtest", "testen", "testn", "the end", "uk", "wrong"], + \ [ + \ ["bad", ["put", "uk", "OK"]], + \ ["inputs", ["input", "puts", "outputs"]], + \ ["comment", ["Comment"]], + \ ["ok", ["OK", "uk", "put"]], + \ ["Ok", ["OK", "Uk", "Put"]], + \ ["end", ["put", "uk", "deol"]], + \ ["the", ["put", "uk", "test"]], + \ ["test", ["Test", "testn", "testen"]], + \ ["d\xE9\xF4l", ["deol", "d\xE9\xF4r", "test"]], + \ ]) +endfunc + +"Compound words +func Test_zz_compound() + call LoadAffAndDic(g:test_data_aff3, g:test_data_dic3) + call RunGoodBad("foo m\xEF foobar foofoobar barfoo barbarfoo", + \ "bad: bar la foom\xEF barm\xEF m\xEFfoo m\xEFbar m\xEFm\xEF lala m\xEFla lam\xEF foola labar", + \ ["foo", "m\xEF"], + \ [ + \ ["bad", ["foo", "m\xEF"]], + \ ["bar", ["barfoo", "foobar", "foo"]], + \ ["la", ["m\xEF", "foo"]], + \ ["foom\xEF", ["foo m\xEF", "foo", "foofoo"]], + \ ["barm\xEF", ["barfoo", "m\xEF", "barbar"]], + \ ["m\xEFfoo", ["m\xEF foo", "foo", "foofoo"]], + \ ["m\xEFbar", ["foobar", "barbar", "m\xEF"]], + \ ["m\xEFm\xEF", ["m\xEF m\xEF", "m\xEF"]], + \ ["lala", []], + \ ["m\xEFla", ["m\xEF", "m\xEF m\xEF"]], + \ ["lam\xEF", ["m\xEF", "m\xEF m\xEF"]], + \ ["foola", ["foo", "foobar", "foofoo"]], + \ ["labar", ["barbar", "foobar"]], + \ ]) + + call LoadAffAndDic(g:test_data_aff4, g:test_data_dic4) + call RunGoodBad("word util bork prebork start end wordutil wordutils pro-ok bork borkbork borkborkbork borkborkborkbork borkborkborkborkbork tomato tomatotomato startend startword startwordword startwordend startwordwordend startwordwordwordend prebork preborkbork preborkborkbork nouword", + \ "bad: wordutilize pro borkborkborkborkborkbork tomatotomatotomato endstart endend startstart wordend wordstart preborkprebork preborkpreborkbork startwordwordwordwordend borkpreborkpreborkbork utilsbork startnouword", + \ ["bork", "prebork", "end", "pro-ok", "start", "tomato", "util", "utilize", "utils", "word", "nouword"], + \ [ + \ ["bad", ["end", "bork", "word"]], + \ ["wordutilize", ["word utilize", "wordutils", "wordutil"]], + \ ["pro", ["bork", "word", "end"]], + \ ["borkborkborkborkborkbork", ["bork borkborkborkborkbork", "borkbork borkborkborkbork", "borkborkbork borkborkbork"]], + \ ["tomatotomatotomato", ["tomato tomatotomato", "tomatotomato tomato", "tomato tomato tomato"]], + \ ["endstart", ["end start", "start"]], + \ ["endend", ["end end", "end"]], + \ ["startstart", ["start start"]], + \ ["wordend", ["word end", "word", "wordword"]], + \ ["wordstart", ["word start", "bork start"]], + \ ["preborkprebork", ["prebork prebork", "preborkbork", "preborkborkbork"]], + \ ["preborkpreborkbork", ["prebork preborkbork", "preborkborkbork", "preborkborkborkbork"]], + \ ["startwordwordwordwordend", ["startwordwordwordword end", "startwordwordwordword", "start wordwordwordword end"]], + \ ["borkpreborkpreborkbork", ["bork preborkpreborkbork", "bork prebork preborkbork", "bork preborkprebork bork"]], + \ ["utilsbork", ["utilbork", "utils bork", "util bork"]], + \ ["startnouword", ["start nouword", "startword", "startborkword"]], + \ ]) + +endfunc + +"Test affix flags with two characters +func Test_zz_affix() + call LoadAffAndDic(g:test_data_aff5, g:test_data_dic5) + call RunGoodBad("fooa1 fooa\xE9 bar prebar barbork prebarbork startprebar start end startend startmiddleend nouend", + \ "bad: foo fooa2 prabar probarbirk middle startmiddle middleend endstart startprobar startnouend", + \ ["bar", "barbork", "end", "fooa1", "fooa\xE9", "nouend", "prebar", "prebarbork", "start"], + \ [ + \ ["bad", ["bar", "end", "fooa1"]], + \ ["foo", ["fooa1", "fooa\xE9", "bar"]], + \ ["fooa2", ["fooa1", "fooa\xE9", "bar"]], + \ ["prabar", ["prebar", "bar", "bar bar"]], + \ ["probarbirk", ["prebarbork"]], + \ ["middle", []], + \ ["startmiddle", ["startmiddleend", "startmiddlebar"]], + \ ["middleend", []], + \ ["endstart", ["end start", "start"]], + \ ["startprobar", ["startprebar", "start prebar", "startbar"]], + \ ["startnouend", ["start nouend", "startend"]], + \ ]) + + call LoadAffAndDic(g:test_data_aff6, g:test_data_dic6) + call RunGoodBad("meea1 meea\xE9 bar prebar barbork prebarbork leadprebar lead end leadend leadmiddleend", + \ "bad: mee meea2 prabar probarbirk middle leadmiddle middleend endlead leadprobar", + \ ["bar", "barbork", "end", "lead", "meea1", "meea\xE9", "prebar", "prebarbork"], + \ [ + \ ["bad", ["bar", "end", "lead"]], + \ ["mee", ["meea1", "meea\xE9", "bar"]], + \ ["meea2", ["meea1", "meea\xE9", "lead"]], + \ ["prabar", ["prebar", "bar", "leadbar"]], + \ ["probarbirk", ["prebarbork"]], + \ ["middle", []], + \ ["leadmiddle", ["leadmiddleend", "leadmiddlebar"]], + \ ["middleend", []], + \ ["endlead", ["end lead", "lead", "end end"]], + \ ["leadprobar", ["leadprebar", "lead prebar", "leadbar"]], + \ ]) + + call LoadAffAndDic(g:test_data_aff7, g:test_data_dic7) + call RunGoodBad("meea1 meea\xE9 bar prebar barmeat prebarmeat leadprebar lead tail leadtail leadmiddletail", + \ "bad: mee meea2 prabar probarmaat middle leadmiddle middletail taillead leadprobar", + \ ["bar", "barmeat", "lead", "meea1", "meea\xE9", "prebar", "prebarmeat", "tail"], + \ [ + \ ["bad", ["bar", "lead", "tail"]], + \ ["mee", ["meea1", "meea\xE9", "bar"]], + \ ["meea2", ["meea1", "meea\xE9", "lead"]], + \ ["prabar", ["prebar", "bar", "leadbar"]], + \ ["probarmaat", ["prebarmeat"]], + \ ["middle", []], + \ ["leadmiddle", ["leadmiddlebar"]], + \ ["middletail", []], + \ ["taillead", ["tail lead", "tail"]], + \ ["leadprobar", ["leadprebar", "lead prebar", "leadbar"]], + \ ]) +endfunc + +func Test_zz_NOSLITSUGS() + call LoadAffAndDic(g:test_data_aff8, g:test_data_dic8) + call RunGoodBad("foo bar faabar", "bad: foobar barfoo", + \ ["bar", "faabar", "foo"], + \ [ + \ ["bad", ["bar", "foo"]], + \ ["foobar", ["faabar", "foo bar", "bar"]], + \ ["barfoo", ["bar foo", "bar", "foo"]], + \ ]) +endfunc + +" Numbers +func Test_zz_Numbers() + call LoadAffAndDic(g:test_data_aff9, g:test_data_dic9) + call RunGoodBad("0b1011 0777 1234 0x01ff", "", + \ ["bar", "foo"], + \ [ + \ ]) +endfunc + +function FirstSpellWord() + call feedkeys("/^start:\n", 'tx') + normal ]smm + let [str, a] = spellbadword() + return str +endfunc + +function SecondSpellWord() + normal `m]s + let [str, a] = spellbadword() + return str +endfunc + +"Test with SAL instead of SOFO items; test automatic reloading +func Test_zz_sal_and_addition() + throw 'skipped: Nvim does not support enc=latin1' + set enc=latin1 + set spellfile= + call writefile(g:test_data_dic1, "Xtest.dic") + call writefile(g:test_data_aff_sal, "Xtest.aff") + mkspell! Xtest Xtest + set spl=Xtest.latin1.spl spell + call assert_equal('kbltykk', soundfold('goobledygoook')) + call assert_equal('kprnfn', soundfold('koprnven')) + call assert_equal('*fls kswts tl', soundfold('oeverloos gezwets edale')) + + "also use an addition file + call writefile(["/regions=usgbnz", "elequint/2", "elekwint/3"], "Xtest.latin1.add") + mkspell! Xtest.latin1.add.spl Xtest.latin1.add + + bwipe! + call setline(1, ["start: elequint test elekwint test elekwent asdf"]) + + set spellfile=Xtest.latin1.add + call assert_equal("elekwent", FirstSpellWord()) + + set spl=Xtest_us.latin1.spl + call assert_equal("elequint", FirstSpellWord()) + call assert_equal("elekwint", SecondSpellWord()) + + set spl=Xtest_gb.latin1.spl + call assert_equal("elekwint", FirstSpellWord()) + call assert_equal("elekwent", SecondSpellWord()) + + set spl=Xtest_nz.latin1.spl + call assert_equal("elequint", FirstSpellWord()) + call assert_equal("elekwent", SecondSpellWord()) + + set spl=Xtest_ca.latin1.spl + call assert_equal("elequint", FirstSpellWord()) + call assert_equal("elekwint", SecondSpellWord()) +endfunc + +func Test_region_error() + messages clear + call writefile(["/regions=usgbnz", "elequint/0"], "Xtest.latin1.add") + mkspell! Xtest.latin1.add.spl Xtest.latin1.add + call assert_match('Invalid region nr in Xtest.latin1.add line 2: 0', execute('messages')) + call delete('Xtest.latin1.add') + call delete('Xtest.latin1.add.spl') +endfunc + +" Check using z= in new buffer (crash fixed by patch 7.4a.028). +func Test_zeq_crash() + new + set maxmem=512 spell + call feedkeys('iasdz=:\"', 'tx') + + bwipe! +endfunc + +func LoadAffAndDic(aff_contents, dic_contents) + throw 'skipped: Nvim does not support enc=latin1' + set enc=latin1 + set spellfile= + call writefile(a:aff_contents, "Xtest.aff") + call writefile(a:dic_contents, "Xtest.dic") + " Generate a .spl file from a .dic and .aff file. + mkspell! Xtest Xtest + " use that spell file + set spl=Xtest.latin1.spl spell +endfunc + +func ListWords() + spelldump + %yank + quit + return split(@", "\n") +endfunc + +func TestGoodBadBase() + exe '1;/^good:' + normal 0f:]s + let prevbad = '' + let result = [] + while 1 + let [bad, a] = spellbadword() + if bad == '' || bad == prevbad || bad == 'badend' + break + endif + let prevbad = bad + let lst = spellsuggest(bad, 3) + normal mm + + call add(result, [bad, lst]) + normal `m]s + endwhile + return result +endfunc + +func RunGoodBad(good, bad, expected_words, expected_bad_words) + bwipe! + call setline(1, ["good: ", a:good, a:bad, " badend "]) + let words = ListWords() + call assert_equal(a:expected_words, words[1:-1]) + let bad_words = TestGoodBadBase() + call assert_equal(a:expected_bad_words, bad_words) + bwipe! +endfunc + +let g:test_data_aff1 = [ + \"SET ISO8859-1", + \"TRY esianrtolcdugmphbyfvkwjkqxz-\xEB\xE9\xE8\xEA\xEF\xEE\xE4\xE0\xE2\xF6\xFC\xFB'ESIANRTOLCDUGMPHBYFVKWJKQXZ", + \"", + \"FOL \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF", + \"LOW \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF", + \"UPP \xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xFF", + \"", + \"SOFOFROM abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xBF", + \"SOFOTO ebctefghejklnnepkrstevvkesebctefghejklnnepkrstevvkeseeeeeeeceeeeeeeedneeeeeeeeeeepseeeeeeeeceeeeeeeedneeeeeeeeeeep?", + \"", + \"MIDWORD\t'-", + \"", + \"KEP =", + \"RAR ?", + \"BAD !", + \"", + \"PFX I N 1", + \"PFX I 0 in .", + \"", + \"PFX O Y 1", + \"PFX O 0 out .", + \"", + \"SFX S Y 2", + \"SFX S 0 s [^s]", + \"SFX S 0 es s", + \"", + \"SFX N N 3", + \"SFX N 0 en [^n]", + \"SFX N 0 nen n", + \"SFX N 0 n .", + \"", + \"REP 3", + \"REP g ch", + \"REP ch g", + \"REP svp s.v.p.", + \"", + \"MAP 9", + \"MAP a\xE0\xE1\xE2\xE3\xE4\xE5", + \"MAP e\xE8\xE9\xEA\xEB", + \"MAP i\xEC\xED\xEE\xEF", + \"MAP o\xF2\xF3\xF4\xF5\xF6", + \"MAP u\xF9\xFA\xFB\xFC", + \"MAP n\xF1", + \"MAP c\xE7", + \"MAP y\xFF\xFD", + \"MAP s\xDF", + \ ] +let g:test_data_dic1 = [ + \"123456", + \"test/NO", + \"# comment", + \"wrong", + \"Comment", + \"OK", + \"uk", + \"put/ISO", + \"the end", + \"deol", + \"d\xE9\xF4r", + \ ] +let g:test_data_aff2 = [ + \"SET ISO8859-1", + \"", + \"FOL \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF", + \"LOW \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF", + \"UPP \xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xFF", + \"", + \"PFXPOSTPONE", + \"", + \"MIDWORD\t'-", + \"", + \"KEP =", + \"RAR ?", + \"BAD !", + \"", + \"PFX I N 1", + \"PFX I 0 in .", + \"", + \"PFX O Y 1", + \"PFX O 0 out [a-z]", + \"", + \"SFX S Y 2", + \"SFX S 0 s [^s]", + \"SFX S 0 es s", + \"", + \"SFX N N 3", + \"SFX N 0 en [^n]", + \"SFX N 0 nen n", + \"SFX N 0 n .", + \"", + \"REP 3", + \"REP g ch", + \"REP ch g", + \"REP svp s.v.p.", + \"", + \"MAP 9", + \"MAP a\xE0\xE1\xE2\xE3\xE4\xE5", + \"MAP e\xE8\xE9\xEA\xEB", + \"MAP i\xEC\xED\xEE\xEF", + \"MAP o\xF2\xF3\xF4\xF5\xF6", + \"MAP u\xF9\xFA\xFB\xFC", + \"MAP n\xF1", + \"MAP c\xE7", + \"MAP y\xFF\xFD", + \"MAP s\xDF", + \ ] +let g:test_data_aff3 = [ + \"SET ISO8859-1", + \"", + \"COMPOUNDMIN 3", + \"COMPOUNDRULE m*", + \"NEEDCOMPOUND x", + \ ] +let g:test_data_dic3 = [ + \"1234", + \"foo/m", + \"bar/mx", + \"m\xEF/m", + \"la/mx", + \ ] +let g:test_data_aff4 = [ + \"SET ISO8859-1", + \"", + \"FOL \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF", + \"LOW \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF", + \"UPP \xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xFF", + \"", + \"COMPOUNDRULE m+", + \"COMPOUNDRULE sm*e", + \"COMPOUNDRULE sm+", + \"COMPOUNDMIN 3", + \"COMPOUNDWORDMAX 3", + \"COMPOUNDFORBIDFLAG t", + \"", + \"COMPOUNDSYLMAX 5", + \"SYLLABLE a\xE1e\xE9i\xEDo\xF3\xF6\xF5u\xFA\xFC\xFBy/aa/au/ea/ee/ei/ie/oa/oe/oo/ou/uu/ui", + \"", + \"MAP 9", + \"MAP a\xE0\xE1\xE2\xE3\xE4\xE5", + \"MAP e\xE8\xE9\xEA\xEB", + \"MAP i\xEC\xED\xEE\xEF", + \"MAP o\xF2\xF3\xF4\xF5\xF6", + \"MAP u\xF9\xFA\xFB\xFC", + \"MAP n\xF1", + \"MAP c\xE7", + \"MAP y\xFF\xFD", + \"MAP s\xDF", + \"", + \"NEEDAFFIX x", + \"", + \"PFXPOSTPONE", + \"", + \"MIDWORD '-", + \"", + \"SFX q N 1", + \"SFX q 0 -ok .", + \"", + \"SFX a Y 2", + \"SFX a 0 s .", + \"SFX a 0 ize/t .", + \"", + \"PFX p N 1", + \"PFX p 0 pre .", + \"", + \"PFX P N 1", + \"PFX P 0 nou .", + \ ] +let g:test_data_dic4 = [ + \"1234", + \"word/mP", + \"util/am", + \"pro/xq", + \"tomato/m", + \"bork/mp", + \"start/s", + \"end/e", + \ ] +let g:test_data_aff5 = [ + \"SET ISO8859-1", + \"", + \"FLAG long", + \"", + \"NEEDAFFIX !!", + \"", + \"COMPOUNDRULE ssmm*ee", + \"", + \"NEEDCOMPOUND xx", + \"COMPOUNDPERMITFLAG pp", + \"", + \"SFX 13 Y 1", + \"SFX 13 0 bork .", + \"", + \"SFX a1 Y 1", + \"SFX a1 0 a1 .", + \"", + \"SFX a\xE9 Y 1", + \"SFX a\xE9 0 a\xE9 .", + \"", + \"PFX zz Y 1", + \"PFX zz 0 pre/pp .", + \"", + \"PFX yy Y 1", + \"PFX yy 0 nou .", + \ ] +let g:test_data_dic5 = [ + \"1234", + \"foo/a1a\xE9!!", + \"bar/zz13ee", + \"start/ss", + \"end/eeyy", + \"middle/mmxx", + \ ] +let g:test_data_aff6 = [ + \"SET ISO8859-1", + \"", + \"FLAG caplong", + \"", + \"NEEDAFFIX A!", + \"", + \"COMPOUNDRULE sMm*Ee", + \"", + \"NEEDCOMPOUND Xx", + \"", + \"COMPOUNDPERMITFLAG p", + \"", + \"SFX N3 Y 1", + \"SFX N3 0 bork .", + \"", + \"SFX A1 Y 1", + \"SFX A1 0 a1 .", + \"", + \"SFX A\xE9 Y 1", + \"SFX A\xE9 0 a\xE9 .", + \"", + \"PFX Zz Y 1", + \"PFX Zz 0 pre/p .", + \ ] +let g:test_data_dic6 = [ + \"1234", + \"mee/A1A\xE9A!", + \"bar/ZzN3Ee", + \"lead/s", + \"end/Ee", + \"middle/MmXx", + \ ] +let g:test_data_aff7 = [ + \"SET ISO8859-1", + \"", + \"FLAG num", + \"", + \"NEEDAFFIX 9999", + \"", + \"COMPOUNDRULE 2,77*123", + \"", + \"NEEDCOMPOUND 1", + \"COMPOUNDPERMITFLAG 432", + \"", + \"SFX 61003 Y 1", + \"SFX 61003 0 meat .", + \"", + \"SFX 391 Y 1", + \"SFX 391 0 a1 .", + \"", + \"SFX 111 Y 1", + \"SFX 111 0 a\xE9 .", + \"", + \"PFX 17 Y 1", + \"PFX 17 0 pre/432 .", + \ ] +let g:test_data_dic7 = [ + \"1234", + \"mee/391,111,9999", + \"bar/17,61003,123", + \"lead/2", + \"tail/123", + \"middle/77,1", + \ ] +let g:test_data_aff8 = [ + \"SET ISO8859-1", + \"", + \"NOSPLITSUGS", + \ ] +let g:test_data_dic8 = [ + \"1234", + \"foo", + \"bar", + \"faabar", + \ ] +let g:test_data_aff9 = [ + \ ] +let g:test_data_dic9 = [ + \"1234", + \"foo", + \"bar", + \ ] +let g:test_data_aff_sal = [ + \"SET ISO8859-1", + \"TRY esianrtolcdugmphbyfvkwjkqxz-\xEB\xE9\xE8\xEA\xEF\xEE\xE4\xE0\xE2\xF6\xFC\xFB'ESIANRTOLCDUGMPHBYFVKWJKQXZ", + \"", + \"FOL \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF", + \"LOW \xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xDF\xFF", + \"UPP \xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xFF", + \"", + \"MIDWORD\t'-", + \"", + \"KEP =", + \"RAR ?", + \"BAD !", + \"", + \"PFX I N 1", + \"PFX I 0 in .", + \"", + \"PFX O Y 1", + \"PFX O 0 out .", + \"", + \"SFX S Y 2", + \"SFX S 0 s [^s]", + \"SFX S 0 es s", + \"", + \"SFX N N 3", + \"SFX N 0 en [^n]", + \"SFX N 0 nen n", + \"SFX N 0 n .", + \"", + \"REP 3", + \"REP g ch", + \"REP ch g", + \"REP svp s.v.p.", + \"", + \"MAP 9", + \"MAP a\xE0\xE1\xE2\xE3\xE4\xE5", + \"MAP e\xE8\xE9\xEA\xEB", + \"MAP i\xEC\xED\xEE\xEF", + \"MAP o\xF2\xF3\xF4\xF5\xF6", + \"MAP u\xF9\xFA\xFB\xFC", + \"MAP n\xF1", + \"MAP c\xE7", + \"MAP y\xFF\xFD", + \"MAP s\xDF", + \"", + \"SAL AH(AEIOUY)-^ *H", + \"SAL AR(AEIOUY)-^ *R", + \"SAL A(HR)^ *", + \"SAL A^ *", + \"SAL AH(AEIOUY)- H", + \"SAL AR(AEIOUY)- R", + \"SAL A(HR) _", + \"SAL \xC0^ *", + \"SAL \xC5^ *", + \"SAL BB- _", + \"SAL B B", + \"SAL CQ- _", + \"SAL CIA X", + \"SAL CH X", + \"SAL C(EIY)- S", + \"SAL CK K", + \"SAL COUGH^ KF", + \"SAL CC< C", + \"SAL C K", + \"SAL DG(EIY) K", + \"SAL DD- _", + \"SAL D T", + \"SAL \xC9< E", + \"SAL EH(AEIOUY)-^ *H", + \"SAL ER(AEIOUY)-^ *R", + \"SAL E(HR)^ *", + \"SAL ENOUGH^$ *NF", + \"SAL E^ *", + \"SAL EH(AEIOUY)- H", + \"SAL ER(AEIOUY)- R", + \"SAL E(HR) _", + \"SAL FF- _", + \"SAL F F", + \"SAL GN^ N", + \"SAL GN$ N", + \"SAL GNS$ NS", + \"SAL GNED$ N", + \"SAL GH(AEIOUY)- K", + \"SAL GH _", + \"SAL GG9 K", + \"SAL G K", + \"SAL H H", + \"SAL IH(AEIOUY)-^ *H", + \"SAL IR(AEIOUY)-^ *R", + \"SAL I(HR)^ *", + \"SAL I^ *", + \"SAL ING6 N", + \"SAL IH(AEIOUY)- H", + \"SAL IR(AEIOUY)- R", + \"SAL I(HR) _", + \"SAL J K", + \"SAL KN^ N", + \"SAL KK- _", + \"SAL K K", + \"SAL LAUGH^ LF", + \"SAL LL- _", + \"SAL L L", + \"SAL MB$ M", + \"SAL MM M", + \"SAL M M", + \"SAL NN- _", + \"SAL N N", + \"SAL OH(AEIOUY)-^ *H", + \"SAL OR(AEIOUY)-^ *R", + \"SAL O(HR)^ *", + \"SAL O^ *", + \"SAL OH(AEIOUY)- H", + \"SAL OR(AEIOUY)- R", + \"SAL O(HR) _", + \"SAL PH F", + \"SAL PN^ N", + \"SAL PP- _", + \"SAL P P", + \"SAL Q K", + \"SAL RH^ R", + \"SAL ROUGH^ RF", + \"SAL RR- _", + \"SAL R R", + \"SAL SCH(EOU)- SK", + \"SAL SC(IEY)- S", + \"SAL SH X", + \"SAL SI(AO)- X", + \"SAL SS- _", + \"SAL S S", + \"SAL TI(AO)- X", + \"SAL TH @", + \"SAL TCH-- _", + \"SAL TOUGH^ TF", + \"SAL TT- _", + \"SAL T T", + \"SAL UH(AEIOUY)-^ *H", + \"SAL UR(AEIOUY)-^ *R", + \"SAL U(HR)^ *", + \"SAL U^ *", + \"SAL UH(AEIOUY)- H", + \"SAL UR(AEIOUY)- R", + \"SAL U(HR) _", + \"SAL V^ W", + \"SAL V F", + \"SAL WR^ R", + \"SAL WH^ W", + \"SAL W(AEIOU)- W", + \"SAL X^ S", + \"SAL X KS", + \"SAL Y(AEIOU)- Y", + \"SAL ZZ- _", + \"SAL Z S", + \ ] diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim index 5996b2cd4a..11e26d03aa 100644 --- a/src/nvim/testdir/test_startup.vim +++ b/src/nvim/testdir/test_startup.vim @@ -24,28 +24,34 @@ func Test_after_comes_later() \ 'set guioptions+=M', \ 'let $HOME = "/does/not/exist"', \ 'set loadplugins', - \ 'set rtp=Xhere,Xafter', + \ 'set rtp=Xhere,Xafter,Xanother', \ 'set packpath=Xhere,Xafter', \ 'set nomore', + \ 'let g:sequence = ""', \ ] let after = [ \ 'redir! > Xtestout', \ 'scriptnames', \ 'redir END', + \ 'redir! > Xsequence', + \ 'echo g:sequence', + \ 'redir END', \ 'quit', \ ] call mkdir('Xhere/plugin', 'p') - call writefile(['let done = 1'], 'Xhere/plugin/here.vim') + call writefile(['let g:sequence .= "here "'], 'Xhere/plugin/here.vim') + call mkdir('Xanother/plugin', 'p') + call writefile(['let g:sequence .= "another "'], 'Xanother/plugin/another.vim') call mkdir('Xhere/pack/foo/start/foobar/plugin', 'p') - call writefile(['let done = 1'], 'Xhere/pack/foo/start/foobar/plugin/foo.vim') + call writefile(['let g:sequence .= "pack "'], 'Xhere/pack/foo/start/foobar/plugin/foo.vim') call mkdir('Xafter/plugin', 'p') - call writefile(['let done = 1'], 'Xafter/plugin/later.vim') + call writefile(['let g:sequence .= "after "'], 'Xafter/plugin/later.vim') if RunVim(before, after, '') let lines = readfile('Xtestout') - let expected = ['Xbefore.vim', 'here.vim', 'foo.vim', 'later.vim', 'Xafter.vim'] + let expected = ['Xbefore.vim', 'here.vim', 'another.vim', 'foo.vim', 'later.vim', 'Xafter.vim'] let found = [] for line in lines for one in expected @@ -57,11 +63,47 @@ func Test_after_comes_later() call assert_equal(expected, found) endif + call assert_equal('here another pack after', substitute(join(readfile('Xsequence', 1), ''), '\s\+$', '', '')) + call delete('Xtestout') + call delete('Xsequence') call delete('Xhere', 'rf') + call delete('Xanother', 'rf') call delete('Xafter', 'rf') endfunc +func Test_pack_in_rtp_when_plugins_run() + if !has('packages') + return + endif + let before = [ + \ 'set nocp viminfo+=nviminfo', + \ 'set guioptions+=M', + \ 'let $HOME = "/does/not/exist"', + \ 'set loadplugins', + \ 'set rtp=Xhere', + \ 'set packpath=Xhere', + \ 'set nomore', + \ ] + let after = [ + \ 'quit', + \ ] + call mkdir('Xhere/plugin', 'p') + call writefile(['redir! > Xtestout', 'silent set runtimepath?', 'silent! call foo#Trigger()', 'redir END'], 'Xhere/plugin/here.vim') + call mkdir('Xhere/pack/foo/start/foobar/autoload', 'p') + call writefile(['function! foo#Trigger()', 'echo "autoloaded foo"', 'endfunction'], 'Xhere/pack/foo/start/foobar/autoload/foo.vim') + + if RunVim(before, after, '') + + let lines = filter(readfile('Xtestout'), '!empty(v:val)') + call assert_match('Xhere[/\\]pack[/\\]foo[/\\]start[/\\]foobar', get(lines, 0)) + call assert_match('autoloaded foo', get(lines, 1)) + endif + + call delete('Xtestout') + call delete('Xhere', 'rf') +endfunc + func Test_help_arg() if !has('unix') && has('gui') " this doesn't work with gvim on MS-Windows @@ -75,12 +117,12 @@ func Test_help_arg() " check if couple of lines are there let found = [] for line in lines - if line =~ '-R.*Readonly mode' - call add(found, 'Readonly mode') + if line =~ '-R.*Read-only mode' + call add(found, 'Readonly mode') endif " Watch out for a second --version line in the Gnome version. - if line =~ '--version.*Print version information and exit' - call add(found, "--version") + if line =~ '--version.*Print version information' + call add(found, "--version") endif endfor call assert_equal(['Readonly mode', '--version'], found) diff --git a/src/nvim/testdir/test_startup_utf8.vim b/src/nvim/testdir/test_startup_utf8.vim new file mode 100644 index 0000000000..d179a4cc79 --- /dev/null +++ b/src/nvim/testdir/test_startup_utf8.vim @@ -0,0 +1,64 @@ +" Tests for startup using utf-8. +if !has('multi_byte') + finish +endif + +source shared.vim + +func Test_read_stdin_utf8() + let linesin = ['テスト', '€ÀÈÌÒÙ'] + call writefile(linesin, 'Xtestin') + let before = [ + \ 'set enc=utf-8', + \ 'set fencs=cp932,utf-8', + \ ] + let after = [ + \ 'write ++enc=utf-8 Xtestout', + \ 'quit!', + \ ] + if has('win32') + let pipecmd = 'type Xtestin | ' + else + let pipecmd = 'cat Xtestin | ' + endif + if RunVimPiped(before, after, '-', pipecmd) + let lines = readfile('Xtestout') + call assert_equal(linesin, lines) + else + call assert_equal('', 'RunVimPiped failed.') + endif + call delete('Xtestout') + call delete('Xtestin') +endfunc + +func Test_read_fifo_utf8() + if !has('unix') + return + endif + " Using bash/zsh's process substitution. + if executable('bash') + set shell=bash + elseif executable('zsh') + set shell=zsh + else + return + endif + let linesin = ['テスト', '€ÀÈÌÒÙ'] + call writefile(linesin, 'Xtestin') + let before = [ + \ 'set enc=utf-8', + \ 'set fencs=cp932,utf-8', + \ ] + let after = [ + \ 'write ++enc=utf-8 Xtestout', + \ 'quit!', + \ ] + if RunVim(before, after, '<(cat Xtestin)') + let lines = readfile('Xtestout') + call assert_equal(linesin, lines) + else + call assert_equal('', 'RunVim failed.') + endif + call delete('Xtestout') + call delete('Xtestin') +endfunc diff --git a/src/nvim/testdir/test_stat.vim b/src/nvim/testdir/test_stat.vim index 89ca9ef379..1239fe9427 100644 --- a/src/nvim/testdir/test_stat.vim +++ b/src/nvim/testdir/test_stat.vim @@ -1,24 +1,45 @@ " Tests for stat functions and checktime -func Test_existent_file() - let fname='Xtest.tmp' +func CheckFileTime(doSleep) + let fname = 'Xtest.tmp' + let result = 0 - let ts=localtime() - sleep 1 - let fl=['Hello World!'] + let ts = localtime() + if a:doSleep + sleep 1 + endif + let fl = ['Hello World!'] call writefile(fl, fname) - let tf=getftime(fname) - sleep 1 - let te=localtime() + let tf = getftime(fname) + if a:doSleep + sleep 1 + endif + let te = localtime() + + let time_correct = (ts <= tf && tf <= te) + if a:doSleep || time_correct + call assert_true(time_correct) + call assert_equal(strlen(fl[0] . "\n"), getfsize(fname)) + call assert_equal('file', getftype(fname)) + call assert_equal('rw-', getfperm(fname)[0:2]) + let result = 1 + endif + + call delete(fname) + return result +endfunc - call assert_true(ts <= tf && tf <= te) - call assert_equal(strlen(fl[0] . "\n"), getfsize(fname)) - call assert_equal('file', getftype(fname)) - call assert_equal('rw-', getfperm(fname)[0:2]) +func Test_existent_file() + " On some systems the file timestamp is rounded to a multiple of 2 seconds. + " We need to sleep to handle that, but that makes the test slow. First try + " without the sleep, and if it fails try again with the sleep. + if CheckFileTime(0) == 0 + call CheckFileTime(1) + endif endfunc func Test_existent_directory() - let dname='.' + let dname = '.' call assert_equal(0, getfsize(dname)) call assert_equal('dir', getftype(dname)) @@ -26,22 +47,29 @@ func Test_existent_directory() endfunc func Test_checktime() - let fname='Xtest.tmp' + let fname = 'Xtest.tmp' - let fl=['Hello World!'] + let fl = ['Hello World!'] call writefile(fl, fname) set autoread exec 'e' fname - sleep 2 - let fl=readfile(fname) + " FAT has a granularity of 2 seconds, otherwise it's usually 1 second + if has('win32') + sleep 2 + else + sleep 2 + endif + let fl = readfile(fname) let fl[0] .= ' - checktime' call writefile(fl, fname) checktime call assert_equal(fl[0], getline(1)) + + call delete(fname) endfunc func Test_nonexistent_file() - let fname='Xtest.tmp' + let fname = 'Xtest.tmp' call delete(fname) call assert_equal(-1, getftime(fname)) @@ -55,7 +83,7 @@ func Test_win32_symlink_dir() " So we use an existing symlink for this test. if has('win32') " Check if 'C:\Users\All Users' is a symlink to a directory. - let res=system('dir C:\Users /a') + let res = system('dir C:\Users /a') if match(res, '\C<SYMLINKD> *All Users') >= 0 " Get the filetype of the symlink. call assert_equal('dir', getftype('C:\Users\All Users')) diff --git a/src/nvim/testdir/test_statusline.vim b/src/nvim/testdir/test_statusline.vim index 82898df92d..cf85bd58ac 100644 --- a/src/nvim/testdir/test_statusline.vim +++ b/src/nvim/testdir/test_statusline.vim @@ -1,19 +1,39 @@ -function! StatuslineWithCaughtError() +" Test 'statusline' +" +" Not tested yet: +" %a +" %N +" %T +" %X +" %* + +source view_util.vim + +func s:get_statusline() + return ScreenLines(&lines - 1, &columns)[0] +endfunc + +func StatuslineWithCaughtError() let s:func_in_statusline_called = 1 try call eval('unknown expression') catch endtry return '' -endfunction +endfunc -function! StatuslineWithError() +func StatuslineWithError() let s:func_in_statusline_called = 1 call eval('unknown expression') return '' -endfunction +endfunc -function! Test_caught_error_in_statusline() +" Function used to display syntax group. +func SyntaxItem() + return synIDattr(synID(line("."),col("."),1),"name") +endfunc + +func Test_caught_error_in_statusline() let s:func_in_statusline_called = 0 set laststatus=2 let statusline = '%{StatuslineWithCaughtError()}' @@ -22,9 +42,9 @@ function! Test_caught_error_in_statusline() call assert_true(s:func_in_statusline_called) call assert_equal(statusline, &statusline) set statusline= -endfunction +endfunc -function! Test_statusline_will_be_disabled_with_error() +func Test_statusline_will_be_disabled_with_error() let s:func_in_statusline_called = 0 set laststatus=2 let statusline = '%{StatuslineWithError()}' @@ -36,4 +56,219 @@ function! Test_statusline_will_be_disabled_with_error() call assert_true(s:func_in_statusline_called) call assert_equal('', &statusline) set statusline= -endfunction +endfunc + +func Test_statusline() + new Xstatusline + only + set laststatus=2 + set splitbelow + call setline(1, range(1, 200)) + + " %b: Value of character under cursor. + " %B: As above, in hexadecimal. + call cursor(180, 2) + set statusline=%b,%B + call assert_match('^56,38\s*$', s:get_statusline()) + + " %o: Byte number in file of byte under cursor, first byte is 1. + " %O: As above, in hexadecimal. + set statusline=%o,%O + set fileformat=dos + call assert_match('^789,315\s*$', s:get_statusline()) + set fileformat=mac + call assert_match('^610,262\s*$', s:get_statusline()) + set fileformat=unix + call assert_match('^610,262\s*$', s:get_statusline()) + set fileformat& + + " %f: Path to the file in the buffer, as typed or relative to current dir. + set statusline=%f + call assert_match('^Xstatusline\s*$', s:get_statusline()) + + " %F: Full path to the file in the buffer. + set statusline=%F + call assert_match('/testdir/Xstatusline\s*$', s:get_statusline()) + + " %h: Help buffer flag, text is "[help]". + " %H: Help buffer flag, text is ",HLP". + set statusline=%h,%H + call assert_match('^,\s*$', s:get_statusline()) + help + call assert_match('^\[Help\],HLP\s*$', s:get_statusline()) + helpclose + + " %k: Value of "b:keymap_name" or 'keymap' + " when :lmap mappings are being used: <keymap>" + set statusline=%k + if has('keymap') + set keymap=esperanto + call assert_match('^<Eo>\s*$', s:get_statusline()) + set keymap& + else + call assert_match('^\s*$', s:get_statusline()) + endif + + " %l: Line number. + " %L: Number of line in buffer. + " %c: Column number. + set statusline=%l/%L,%c + call assert_match('^180/200,2\s*$', s:get_statusline()) + + " %m: Modified flag, text is "[+]", "[-]" if 'modifiable' is off. + " %M: Modified flag, text is ",+" or ",-". + set statusline=%m%M + call assert_match('^\[+\],+\s*$', s:get_statusline()) + set nomodifiable + call assert_match('^\[+-\],+-\s*$', s:get_statusline()) + write + call assert_match('^\[-\],-\s*$', s:get_statusline()) + set modifiable& + call assert_match('^\s*$', s:get_statusline()) + + " %n: Buffer number. + set statusline=%n + call assert_match('^'.bufnr('%').'\s*$', s:get_statusline()) + + " %p: Percentage through file in lines as in CTRL-G. + " %P: Percentage through file of displayed window. + set statusline=%p,%P + 0 + call assert_match('^0,Top\s*$', s:get_statusline()) + norm G + call assert_match('^100,Bot\s*$', s:get_statusline()) + 180 + " Don't check the exact percentage as it depends on the window size + call assert_match('^90,\(Top\|Bot\|\d\+%\)\s*$', s:get_statusline()) + + " %q: "[Quickfix List]", "[Location List]" or empty. + set statusline=%q + call assert_match('^\s*$', s:get_statusline()) + copen + call assert_match('^\[Quickfix List\]\s*$', s:get_statusline()) + cclose + lexpr getline(1, 2) + lopen + call assert_match('^\[Location List\]\s*$', s:get_statusline()) + lclose + + " %r: Readonly flag, text is "[RO]". + " %R: Readonly flag, text is ",RO". + set statusline=%r,%R + call assert_match('^,\s*$', s:get_statusline()) + help + call assert_match('^\[RO\],RO\s*$', s:get_statusline()) + helpclose + + " %t: File name (tail) of file in the buffer. + set statusline=%t + call assert_match('^Xstatusline\s*$', s:get_statusline()) + + " %v: Virtual column number. + " %V: Virtual column number as -{num}. Not displayed if equal to 'c'. + call cursor(180, 2) + set statusline=%v,%V + call assert_match('^2,\s*$', s:get_statusline()) + set virtualedit=all + norm 10| + call assert_match('^10,-10\s*$', s:get_statusline()) + set virtualedit& + + " %w: Preview window flag, text is "[Preview]". + " %W: Preview window flag, text is ",PRV". + set statusline=%w%W + call assert_match('^\s*$', s:get_statusline()) + pedit + wincmd j + call assert_match('^\[Preview\],PRV\s*$', s:get_statusline()) + pclose + + " %y: Type of file in the buffer, e.g., "[vim]". See 'filetype'. + " %Y: Type of file in the buffer, e.g., ",VIM". See 'filetype'. + set statusline=%y\ %Y + call assert_match('^\s*$', s:get_statusline()) + setfiletype vim + call assert_match('^\[vim\] VIM\s*$', s:get_statusline()) + + " %=: Separation point between left and right aligned items. + set statusline=foo%=bar + call assert_match('^foo\s\+bar\s*$', s:get_statusline()) + + " Test min/max width, leading zeroes, left/right justify. + set statusline=%04B + call cursor(180, 2) + call assert_match('^0038\s*$', s:get_statusline()) + set statusline=#%4B# + call assert_match('^# 38#\s*$', s:get_statusline()) + set statusline=#%-4B# + call assert_match('^#38 #\s*$', s:get_statusline()) + set statusline=%.6f + call assert_match('^<sline\s*$', s:get_statusline()) + + " %<: Where to truncate. + exe 'set statusline=a%<b' . repeat('c', 1000) . 'd' + call assert_match('^a<c*d$', s:get_statusline()) + exe 'set statusline=a' . repeat('b', 1000) . '%<c' + call assert_match('^ab*>$', s:get_statusline()) + + "%{: Evaluate expression between '%{' and '}' and substitute result. + syntax on + set statusline=%{SyntaxItem()} + call assert_match('^vimNumber\s*$', s:get_statusline()) + s/^/"/ + call assert_match('^vimLineComment\s*$', s:get_statusline()) + syntax off + + "%(: Start of item group. + set statusline=ab%(cd%q%)de + call assert_match('^abde\s*$', s:get_statusline()) + copen + call assert_match('^abcd\[Quickfix List\1]de\s*$', s:get_statusline()) + cclose + + " %#: Set highlight group. The name must follow and then a # again. + set statusline=ab%#Todo#cd%#Error#ef + call assert_match('^abcdef\s*$', s:get_statusline()) + let sa1=screenattr(&lines - 1, 1) + let sa2=screenattr(&lines - 1, 3) + let sa3=screenattr(&lines - 1, 5) + call assert_notequal(sa1, sa2) + call assert_notequal(sa1, sa3) + call assert_notequal(sa2, sa3) + call assert_equal(sa1, screenattr(&lines - 1, 2)) + call assert_equal(sa2, screenattr(&lines - 1, 4)) + call assert_equal(sa3, screenattr(&lines - 1, 6)) + call assert_equal(sa3, screenattr(&lines - 1, 7)) + + " %*: Set highlight group to User{N} + set statusline=a%1*b%0*c + call assert_match('^abc\s*$', s:get_statusline()) + let sa1=screenattr(&lines - 1, 1) + let sa2=screenattr(&lines - 1, 2) + let sa3=screenattr(&lines - 1, 3) + call assert_equal(sa1, sa3) + call assert_notequal(sa1, sa2) + + " %%: a percent sign. + set statusline=10%% + call assert_match('^10%\s*$', s:get_statusline()) + + " %!: evaluated expression is used as the option value + set statusline=%!2*3+1 + call assert_match('7\s*$', s:get_statusline()) + + " Check statusline in current and non-current window + " with the 'fillchars' option. + set fillchars=stl:^,stlnc:=,vert:\|,fold:-,diff:- + vsplit + set statusline=x%=y + call assert_match('^x^\+y^x=\+y$', s:get_statusline()) + set fillchars& + close + + %bw! + call delete('Xstatusline') + set statusline& + set laststatus& + set splitbelow& +endfunc diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim index e2b6de03c3..75ce24de46 100644 --- a/src/nvim/testdir/test_substitute.vim +++ b/src/nvim/testdir/test_substitute.vim @@ -39,3 +39,285 @@ function! Test_multiline_subst() call assert_equal('xxxxx', getline(13)) enew! endfunction + +function! Test_substitute_variants() + " Validate that all the 2-/3-letter variants which embed the flags into the + " command name actually work. + enew! + let ln = 'Testing string' + let variants = [ + \ { 'cmd': ':s/Test/test/c', 'exp': 'testing string', 'prompt': 'y' }, + \ { 'cmd': ':s/foo/bar/ce', 'exp': ln }, + \ { 'cmd': ':s/t/r/cg', 'exp': 'Tesring srring', 'prompt': 'a' }, + \ { 'cmd': ':s/t/r/ci', 'exp': 'resting string', 'prompt': 'y' }, + \ { 'cmd': ':s/t/r/cI', 'exp': 'Tesring string', 'prompt': 'y' }, + \ { 'cmd': ':s/t/r/cn', 'exp': ln }, + \ { 'cmd': ':s/t/r/cp', 'exp': 'Tesring string', 'prompt': 'y' }, + \ { 'cmd': ':s/t/r/cl', 'exp': 'Tesring string', 'prompt': 'y' }, + \ { 'cmd': ':s/t/r/gc', 'exp': 'Tesring srring', 'prompt': 'a' }, + \ { 'cmd': ':s/foo/bar/ge', 'exp': ln }, + \ { 'cmd': ':s/t/r/g', 'exp': 'Tesring srring' }, + \ { 'cmd': ':s/t/r/gi', 'exp': 'resring srring' }, + \ { 'cmd': ':s/t/r/gI', 'exp': 'Tesring srring' }, + \ { 'cmd': ':s/t/r/gn', 'exp': ln }, + \ { 'cmd': ':s/t/r/gp', 'exp': 'Tesring srring' }, + \ { 'cmd': ':s/t/r/gl', 'exp': 'Tesring srring' }, + \ { 'cmd': ':s//r/gr', 'exp': 'Testr strr' }, + \ { 'cmd': ':s/t/r/ic', 'exp': 'resting string', 'prompt': 'y' }, + \ { 'cmd': ':s/foo/bar/ie', 'exp': ln }, + \ { 'cmd': ':s/t/r/i', 'exp': 'resting string' }, + \ { 'cmd': ':s/t/r/iI', 'exp': 'Tesring string' }, + \ { 'cmd': ':s/t/r/in', 'exp': ln }, + \ { 'cmd': ':s/t/r/ip', 'exp': 'resting string' }, + \ { 'cmd': ':s//r/ir', 'exp': 'Testr string' }, + \ { 'cmd': ':s/t/r/Ic', 'exp': 'Tesring string', 'prompt': 'y' }, + \ { 'cmd': ':s/foo/bar/Ie', 'exp': ln }, + \ { 'cmd': ':s/t/r/Ig', 'exp': 'Tesring srring' }, + \ { 'cmd': ':s/t/r/Ii', 'exp': 'resting string' }, + \ { 'cmd': ':s/t/r/I', 'exp': 'Tesring string' }, + \ { 'cmd': ':s/t/r/Ip', 'exp': 'Tesring string' }, + \ { 'cmd': ':s/t/r/Il', 'exp': 'Tesring string' }, + \ { 'cmd': ':s//r/Ir', 'exp': 'Testr string' }, + \ { 'cmd': ':s//r/rc', 'exp': 'Testr string', 'prompt': 'y' }, + \ { 'cmd': ':s//r/rg', 'exp': 'Testr strr' }, + \ { 'cmd': ':s//r/ri', 'exp': 'Testr string' }, + \ { 'cmd': ':s//r/rI', 'exp': 'Testr string' }, + \ { 'cmd': ':s//r/rn', 'exp': 'Testing string' }, + \ { 'cmd': ':s//r/rp', 'exp': 'Testr string' }, + \ { 'cmd': ':s//r/rl', 'exp': 'Testr string' }, + \ { 'cmd': ':s//r/r', 'exp': 'Testr string' }, + \] + + for var in variants + for run in [1, 2] + let cmd = var.cmd + if run == 2 && cmd =~ "/.*/.*/." + " Change :s/from/to/{flags} to :s{flags} + let cmd = substitute(cmd, '/.*/', '', '') + endif + call setline(1, [ln]) + let msg = printf('using "%s"', cmd) + let @/='ing' + let v:errmsg = '' + call feedkeys(cmd . "\<CR>" . get(var, 'prompt', ''), 'ntx') + " No error should exist (matters for testing e flag) + call assert_equal('', v:errmsg, msg) + call assert_equal(var.exp, getline('.'), msg) + endfor + endfor +endfunction + +func Test_substitute_repeat() + " This caused an invalid memory access. + split Xfile + s/^/x + call feedkeys("Qsc\<CR>y", 'tx') + bwipe! +endfunc + +" Tests for *sub-replace-special* and *sub-replace-expression* on :substitute. + +" Execute a list of :substitute command tests +func Run_SubCmd_Tests(tests) + enew! + for t in a:tests + let start = line('.') + 1 + let end = start + len(t[2]) - 1 + exe "normal o" . t[0] + call cursor(start, 1) + exe t[1] + call assert_equal(t[2], getline(start, end), t[1]) + endfor + enew! +endfunc + +func Test_sub_cmd_1() + set magic + set cpo& + + " List entry format: [input, cmd, output] + let tests = [['A', 's/A/&&/', ['AA']], + \ ['B', 's/B/\&/', ['&']], + \ ['C123456789', 's/C\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)/\0\9\8\7\6\5\4\3\2\1/', ['C123456789987654321']], + \ ['D', 's/D/d/', ['d']], + \ ['E', 's/E/~/', ['d']], + \ ['F', 's/F/\~/', ['~']], + \ ['G', 's/G/\ugg/', ['Gg']], + \ ['H', 's/H/\Uh\Eh/', ['Hh']], + \ ['I', 's/I/\lII/', ['iI']], + \ ['J', 's/J/\LJ\EJ/', ['jJ']], + \ ['K', 's/K/\Uk\ek/', ['Kk']], + \ ['lLl', "s/L/\<C-V>\<C-M>/", ["l\<C-V>", 'l']], + \ ['mMm', 's/M/\r/', ['m', 'm']], + \ ['nNn', "s/N/\\\<C-V>\<C-M>/", ["n\<C-V>", 'n']], + \ ['oOo', 's/O/\n/', ["o\no"]], + \ ['pPp', 's/P/\b/', ["p\<C-H>p"]], + \ ['qQq', 's/Q/\t/', ["q\tq"]], + \ ['rRr', 's/R/\\/', ['r\r']], + \ ['sSs', 's/S/\c/', ['scs']], + \ ['tTt', "s/T/\<C-V>\<C-J>/", ["t\<C-V>\<C-J>t"]], + \ ['U', 's/U/\L\uuUu\l\EU/', ['UuuU']], + \ ['V', 's/V/\U\lVvV\u\Ev/', ['vVVv']] + \ ] + call Run_SubCmd_Tests(tests) +endfunc + +func Test_sub_cmd_2() + set nomagic + set cpo& + + " List entry format: [input, cmd, output] + let tests = [['A', 's/A/&&/', ['&&']], + \ ['B', 's/B/\&/', ['B']], + \ ['C123456789', 's/\mC\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)/\0\9\8\7\6\5\4\3\2\1/', ['C123456789987654321']], + \ ['D', 's/D/d/', ['d']], + \ ['E', 's/E/~/', ['~']], + \ ['F', 's/F/\~/', ['~']], + \ ['G', 's/G/\ugg/', ['Gg']], + \ ['H', 's/H/\Uh\Eh/', ['Hh']], + \ ['I', 's/I/\lII/', ['iI']], + \ ['J', 's/J/\LJ\EJ/', ['jJ']], + \ ['K', 's/K/\Uk\ek/', ['Kk']], + \ ['lLl', "s/L/\<C-V>\<C-M>/", ["l\<C-V>", 'l']], + \ ['mMm', 's/M/\r/', ['m', 'm']], + \ ['nNn', "s/N/\\\<C-V>\<C-M>/", ["n\<C-V>", 'n']], + \ ['oOo', 's/O/\n/', ["o\no"]], + \ ['pPp', 's/P/\b/', ["p\<C-H>p"]], + \ ['qQq', 's/Q/\t/', ["q\tq"]], + \ ['rRr', 's/R/\\/', ['r\r']], + \ ['sSs', 's/S/\c/', ['scs']], + \ ['tTt', "s/T/\<C-V>\<C-J>/", ["t\<C-V>\<C-J>t"]], + \ ['U', 's/U/\L\uuUu\l\EU/', ['UuuU']], + \ ['V', 's/V/\U\lVvV\u\Ev/', ['vVVv']] + \ ] + call Run_SubCmd_Tests(tests) +endfunc + +func Test_sub_cmd_3() + set nomagic + set cpo& + + " List entry format: [input, cmd, output] + let tests = [['aAa', "s/A/\\='\\'/", ['a\a']], + \ ['bBb', "s/B/\\='\\\\'/", ['b\\b']], + \ ['cCc', "s/C/\\='\<C-V>\<C-M>'/", ["c\<C-V>", 'c']], + \ ['dDd', "s/D/\\='\\\<C-V>\<C-M>'/", ["d\\\<C-V>", 'd']], + \ ['eEe', "s/E/\\='\\\\\<C-V>\<C-M>'/", ["e\\\\\<C-V>", 'e']], + \ ['fFf', "s/F/\\='\r'/", ['f', 'f']], + \ ['gGg', "s/G/\\='\<C-V>\<C-J>'/", ["g\<C-V>", 'g']], + \ ['hHh', "s/H/\\='\\\<C-V>\<C-J>'/", ["h\\\<C-V>", 'h']], + \ ['iIi', "s/I/\\='\\\\\<C-V>\<C-J>'/", ["i\\\\\<C-V>", 'i']], + \ ['jJj', "s/J/\\='\n'/", ['j', 'j']], + \ ['kKk', 's/K/\="\r"/', ['k', 'k']], + \ ['lLl', 's/L/\="\n"/', ['l', 'l']] + \ ] + call Run_SubCmd_Tests(tests) +endfunc + +" Test for submatch() on :substitue. +func Test_sub_cmd_4() + set magic& + set cpo& + + " List entry format: [input, cmd, output] + let tests = [ ['aAa', "s/A/\\=substitute(submatch(0), '.', '\\', '')/", + \ ['a\a']], + \ ['bBb', "s/B/\\=substitute(submatch(0), '.', '\\', '')/", + \ ['b\b']], + \ ['cCc', "s/C/\\=substitute(submatch(0), '.', '\<C-V>\<C-M>', '')/", + \ ["c\<C-V>", 'c']], + \ ['dDd', "s/D/\\=substitute(submatch(0), '.', '\\\<C-V>\<C-M>', '')/", + \ ["d\<C-V>", 'd']], + \ ['eEe', "s/E/\\=substitute(submatch(0), '.', '\\\\\<C-V>\<C-M>', '')/", + \ ["e\\\<C-V>", 'e']], + \ ['fFf', "s/F/\\=substitute(submatch(0), '.', '\\r', '')/", + \ ['f', 'f']], + \ ['gGg', 's/G/\=substitute(submatch(0), ".", "\<C-V>\<C-J>", "")/', + \ ["g\<C-V>", 'g']], + \ ['hHh', 's/H/\=substitute(submatch(0), ".", "\\\<C-V>\<C-J>", "")/', + \ ["h\<C-V>", 'h']], + \ ['iIi', 's/I/\=substitute(submatch(0), ".", "\\\\\<C-V>\<C-J>", "")/', + \ ["i\\\<C-V>", 'i']], + \ ['jJj', "s/J/\\=substitute(submatch(0), '.', '\\n', '')/", + \ ['j', 'j']], + \ ['kKk', "s/K/\\=substitute(submatch(0), '.', '\\r', '')/", + \ ['k', 'k']], + \ ['lLl', "s/L/\\=substitute(submatch(0), '.', '\\n', '')/", + \ ['l', 'l']], + \ ] + call Run_SubCmd_Tests(tests) +endfunc + +func Test_sub_cmd_5() + set magic& + set cpo& + + " List entry format: [input, cmd, output] + let tests = [ ['A123456789', 's/A\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)/\=submatch(0) . submatch(9) . submatch(8) . submatch(7) . submatch(6) . submatch(5) . submatch(4) . submatch(3) . submatch(2) . submatch(1)/', ['A123456789987654321']], + \ ['B123456789', 's/B\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)/\=string([submatch(0, 1), submatch(9, 1), submatch(8, 1), submatch(7, 1), submatch(6, 1), submatch(5, 1), submatch(4, 1), submatch(3, 1), submatch(2, 1), submatch(1, 1)])/', ["[['B123456789'], ['9'], ['8'], ['7'], ['6'], ['5'], ['4'], ['3'], ['2'], ['1']]"]], + \ ] + call Run_SubCmd_Tests(tests) +endfunc + +" Test for *:s%* on :substitute. +func Test_sub_cmd_6() + throw "skipped: Nvim removed POSIX-related 'cpoptions' flags" + set magic& + set cpo+=/ + + " List entry format: [input, cmd, output] + let tests = [ ['A', 's/A/a/', ['a']], + \ ['B', 's/B/%/', ['a']], + \ ] + call Run_SubCmd_Tests(tests) + + set cpo-=/ + let tests = [ ['C', 's/C/c/', ['c']], + \ ['D', 's/D/%/', ['%']], + \ ] + call Run_SubCmd_Tests(tests) + + set cpo& +endfunc + +" Test for :s replacing \n with line break. +func Test_sub_cmd_7() + set magic& + set cpo& + + " List entry format: [input, cmd, output] + let tests = [ ["A\<C-V>\<C-M>A", 's/A./\=submatch(0)/', ['A', 'A']], + \ ["B\<C-V>\<C-J>B", 's/B./\=submatch(0)/', ['B', 'B']], + \ ["C\<C-V>\<C-J>C", 's/C./\=strtrans(string(submatch(0, 1)))/', [strtrans("['C\<C-J>']C")]], + \ ["D\<C-V>\<C-J>\nD", 's/D.\nD/\=strtrans(string(submatch(0, 1)))/', [strtrans("['D\<C-J>', 'D']")]], + \ ["E\<C-V>\<C-J>\n\<C-V>\<C-J>\n\<C-V>\<C-J>\n\<C-V>\<C-J>\n\<C-V>\<C-J>E", 's/E\_.\{-}E/\=strtrans(string(submatch(0, 1)))/', [strtrans("['E\<C-J>', '\<C-J>', '\<C-J>', '\<C-J>', '\<C-J>E']")]], + \ ] + call Run_SubCmd_Tests(tests) + + exe "normal oQ\nQ\<Esc>k" + call assert_fails('s/Q[^\n]Q/\=submatch(0)."foobar"/', 'E486') + enew! +endfunc + +func TitleString() + let check = 'foo' =~ 'bar' + return "" +endfunc + +func Test_sub_cmd_8() + set titlestring=%{TitleString()} + + enew! + call append(0, ['', 'test_one', 'test_two']) + call cursor(1,1) + /^test_one/s/.*/\="foo\nbar"/ + call assert_equal('foo', getline(2)) + call assert_equal('bar', getline(3)) + call feedkeys(':/^test_two/s/.*/\="foo\nbar"/c', "t") + call feedkeys("\<CR>y", "xt") + call assert_equal('foo', getline(4)) + call assert_equal('bar', getline(5)) + + enew! + set titlestring& +endfunc diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim index af2cbbfe8e..8465fe7d45 100644 --- a/src/nvim/testdir/test_syntax.vim +++ b/src/nvim/testdir/test_syntax.vim @@ -1,5 +1,7 @@ " Test for syntax and syntax iskeyword option +source view_util.vim + func GetSyntaxItem(pat) let c = '' let a = ['a', getreg('a'), getregtype('a')] @@ -50,7 +52,7 @@ func Test_syn_iskeyword() setlocal isk-=_ call assert_equal('DLTD_BY', GetSyntaxItem('DLTD')) /\<D\k\+\>/:norm! ygn - let b2=@0 + let b2 = @0 call assert_equal('DLTD', @0) syn iskeyword clear @@ -76,3 +78,307 @@ func Test_syntax_after_reload() call assert_true(exists('g:gotit')) call delete('Xsomefile') endfunc + +func Test_syntime() + if !has('profile') + return + endif + + syntax on + syntime on + let a = execute('syntime report') + call assert_equal("\nNo Syntax items defined for this buffer", a) + + view ../memfile_test.c + setfiletype cpp + redraw + let a = execute('syntime report') + call assert_match('^ TOTAL *COUNT *MATCH *SLOWEST *AVERAGE *NAME *PATTERN', a) + call assert_match(' \d*\.\d* \+[^0]\d* .* cppRawString ', a) + call assert_match(' \d*\.\d* \+[^0]\d* .* cppNumber ', a) + + syntime off + syntime clear + let a = execute('syntime report') + call assert_match('^ TOTAL *COUNT *MATCH *SLOWEST *AVERAGE *NAME *PATTERN', a) + call assert_notmatch('.* cppRawString *', a) + call assert_notmatch('.* cppNumber*', a) + call assert_notmatch('[1-9]', a) + + call assert_fails('syntime abc', 'E475') + + syntax clear + let a = execute('syntime report') + call assert_equal("\nNo Syntax items defined for this buffer", a) + + bd +endfunc + +func Test_syntax_list() + syntax on + let a = execute('syntax list') + call assert_equal("\nNo Syntax items defined for this buffer", a) + + view ../memfile_test.c + setfiletype c + + let a = execute('syntax list') + call assert_match('cInclude*', a) + call assert_match('cDefine', a) + + let a = execute('syntax list cDefine') + call assert_notmatch('cInclude*', a) + call assert_match('cDefine', a) + call assert_match(' links to Macro$', a) + + call assert_fails('syntax list ABCD', 'E28:') + call assert_fails('syntax list @ABCD', 'E392:') + + syntax clear + let a = execute('syntax list') + call assert_equal("\nNo Syntax items defined for this buffer", a) + + bd +endfunc + +func Test_syntax_completion() + call feedkeys(":syn \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"syn case clear cluster conceal enable include iskeyword keyword list manual match off on region reset spell sync', @:) + + call feedkeys(":syn case \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"syn case ignore match', @:) + + call feedkeys(":syn spell \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"syn spell default notoplevel toplevel', @:) + + call feedkeys(":syn sync \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"syn sync ccomment clear fromstart linebreaks= linecont lines= match maxlines= minlines= region', @:) + + " Check that clearing "Aap" avoids it showing up before Boolean. + hi Aap ctermfg=blue + call feedkeys(":syn list \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_match('^"syn list Aap Boolean Character ', @:) + hi clear Aap + + call feedkeys(":syn list \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_match('^"syn list Boolean Character ', @:) + + call feedkeys(":syn match \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_match('^"syn match Boolean Character ', @:) +endfunc + +func Test_syntax_arg_skipped() + syn clear + syntax case ignore + if 0 + syntax case match + endif + call assert_match('case ignore', execute('syntax case')) + + syn keyword Foo foo + call assert_match('Foo', execute('syntax')) + syn clear + call assert_match('case match', execute('syntax case')) + call assert_notmatch('Foo', execute('syntax')) + + if has('conceal') + syn clear + syntax conceal on + if 0 + syntax conceal off + endif + call assert_match('conceal on', execute('syntax conceal')) + syn clear + call assert_match('conceal off', execute('syntax conceal')) + endif + + syntax conceal on + syntax conceal off + call assert_match('conceal off', execute('syntax conceal')) + + syntax region Bar start=/</ end=/>/ + if 0 + syntax region NotTest start=/</ end=/>/ contains=@Spell + endif + call assert_match('Bar', execute('syntax')) + call assert_notmatch('NotTest', execute('syntax')) + call assert_notmatch('Spell', execute('syntax')) + + hi Foo ctermfg=blue + let a = execute('hi Foo') + if 0 + syntax rest + endif + call assert_equal(a, execute('hi Foo')) + hi clear Bar + hi clear Foo + + set ft=tags + syn off + if 0 + syntax enable + endif + call assert_match('No Syntax items defined', execute('syntax')) + syntax enable + call assert_match('tagComment', execute('syntax')) + set ft= + + syn clear + if 0 + syntax include @Spell nothing + endif + call assert_notmatch('Spell', execute('syntax')) + + syn clear + syn iskeyword 48-57,$,_ + call assert_match('48-57,$,_', execute('syntax iskeyword')) + if 0 + syn clear + syn iskeyword clear + endif + call assert_match('48-57,$,_', execute('syntax iskeyword')) + syn iskeyword clear + call assert_match('not set', execute('syntax iskeyword')) + syn iskeyword 48-57,$,_ + syn clear + call assert_match('not set', execute('syntax iskeyword')) + + syn clear + syn keyword Foo foo + if 0 + syn keyword NotAdded bar + endif + call assert_match('Foo', execute('syntax')) + call assert_notmatch('NotAdded', execute('highlight')) + + syn clear + syn keyword Foo foo + call assert_match('Foo', execute('syntax')) + call assert_match('Foo', execute('syntax list')) + call assert_notmatch('Foo', execute('if 0 | syntax | endif')) + call assert_notmatch('Foo', execute('if 0 | syntax list | endif')) + + syn clear + syn match Fopi /asdf/ + if 0 + syn match Fopx /asdf/ + endif + call assert_match('Fopi', execute('syntax')) + call assert_notmatch('Fopx', execute('syntax')) + + syn clear + syn spell toplevel + call assert_match('spell toplevel', execute('syntax spell')) + if 0 + syn spell notoplevel + endif + call assert_match('spell toplevel', execute('syntax spell')) + syn spell notoplevel + call assert_match('spell notoplevel', execute('syntax spell')) + syn spell default + call assert_match('spell default', execute('syntax spell')) + + syn clear + if 0 + syntax cluster Spell + endif + call assert_notmatch('Spell', execute('syntax')) + + syn clear + syn keyword Foo foo + syn sync ccomment + syn sync maxlines=5 + if 0 + syn sync maxlines=11 + endif + call assert_match('on C-style comments', execute('syntax sync')) + call assert_match('maximal 5 lines', execute('syntax sync')) + syn sync clear + if 0 + syn sync ccomment + endif + call assert_notmatch('on C-style comments', execute('syntax sync')) + + syn clear +endfunc + +func Test_invalid_arg() + call assert_fails('syntax case asdf', 'E390:') + call assert_fails('syntax conceal asdf', 'E390:') + call assert_fails('syntax spell asdf', 'E390:') +endfunc + +func Test_syn_sync() + syntax region HereGroup start=/this/ end=/that/ + syntax sync match SyncHere grouphere HereGroup "pattern" + call assert_match('SyncHere', execute('syntax sync')) + syn sync clear + call assert_notmatch('SyncHere', execute('syntax sync')) + syn clear +endfunc + +func Test_syn_clear() + syntax keyword Foo foo + syntax keyword Bar tar + call assert_match('Foo', execute('syntax')) + call assert_match('Bar', execute('syntax')) + call assert_equal('Foo', synIDattr(hlID("Foo"), "name")) + syn clear Foo + call assert_notmatch('Foo', execute('syntax')) + call assert_match('Bar', execute('syntax')) + call assert_equal('Foo', synIDattr(hlID("Foo"), "name")) + syn clear Foo Bar + call assert_notmatch('Foo', execute('syntax')) + call assert_notmatch('Bar', execute('syntax')) + hi clear Foo + call assert_equal('Foo', synIDattr(hlID("Foo"), "name")) + hi clear Bar +endfunc + +func Test_invalid_name() + syn clear + syn keyword Nop yes + call assert_fails("syntax keyword Wr\x17ong bar", 'E669:') + syntax keyword @Wrong bar + call assert_match('W18:', execute('1messages')) + syn clear + hi clear Nop + hi clear @Wrong +endfunc + + +func Test_conceal() + if !has('conceal') + return + endif + + new + call setline(1, ['', '123456']) + syn match test23 "23" conceal cchar=X + syn match test45 "45" conceal + + set conceallevel=0 + call assert_equal('123456 ', ScreenLines(2, 7)[0]) + call assert_equal([[0, '', 0], [0, '', 0], [0, '', 0], [0, '', 0], [0, '', 0], [0, '', 0]], map(range(1, 6), 'synconcealed(2, v:val)')) + + set conceallevel=1 + call assert_equal('1X 6 ', ScreenLines(2, 7)[0]) + call assert_equal([[0, '', 0], [1, 'X', 1], [1, 'X', 1], [1, ' ', 2], [1, ' ', 2], [0, '', 0]], map(range(1, 6), 'synconcealed(2, v:val)')) + + set conceallevel=1 + set listchars=conceal:Y + call assert_equal([[0, '', 0], [1, 'X', 1], [1, 'X', 1], [1, 'Y', 2], [1, 'Y', 2], [0, '', 0]], map(range(1, 6), 'synconcealed(2, v:val)')) + call assert_equal('1XY6 ', ScreenLines(2, 7)[0]) + + set conceallevel=2 + call assert_match('1X6 ', ScreenLines(2, 7)[0]) + call assert_equal([[0, '', 0], [1, 'X', 1], [1, 'X', 1], [1, '', 2], [1, '', 2], [0, '', 0]], map(range(1, 6), 'synconcealed(2, v:val)')) + + set conceallevel=3 + call assert_match('16 ', ScreenLines(2, 7)[0]) + call assert_equal([[0, '', 0], [1, '', 1], [1, '', 1], [1, '', 2], [1, '', 2], [0, '', 0]], map(range(1, 6), 'synconcealed(2, v:val)')) + + syn clear + set conceallevel& + bw! +endfunc diff --git a/src/nvim/testdir/test_system.vim b/src/nvim/testdir/test_system.vim new file mode 100644 index 0000000000..ce9d110d82 --- /dev/null +++ b/src/nvim/testdir/test_system.vim @@ -0,0 +1,92 @@ +" Tests for system() and systemlist() + +function! Test_System() + if !executable('echo') || !executable('cat') || !executable('wc') + return + endif + let out = system('echo 123') + " On Windows we may get a trailing space. + if out != "123 \n" + call assert_equal("123\n", out) + endif + + let out = systemlist('echo 123') + " On Windows we may get a trailing space and CR. + if out != ["123 \r"] + call assert_equal(['123'], out) + endif + + call assert_equal('123', system('cat', '123')) + call assert_equal(['123'], systemlist('cat', '123')) + call assert_equal(["as\<NL>df"], systemlist('cat', ["as\<NL>df"])) + + new Xdummy + call setline(1, ['asdf', "pw\<NL>er", 'xxxx']) + let out = system('wc -l', bufnr('%')) + " On OS/X we get leading spaces + let out = substitute(out, '^ *', '', '') + call assert_equal("3\n", out) + + let out = systemlist('wc -l', bufnr('%')) + " On Windows we may get a trailing CR. + if out != ["3\r"] + " On OS/X we get leading spaces + if type(out) == v:t_list + let out[0] = substitute(out[0], '^ *', '', '') + endif + call assert_equal(['3'], out) + endif + + let out = systemlist('cat', bufnr('%')) + " On Windows we may get a trailing CR. + if out != ["asdf\r", "pw\<NL>er\r", "xxxx\r"] + call assert_equal(['asdf', "pw\<NL>er", 'xxxx'], out) + endif + bwipe! + + call assert_fails('call system("wc -l", 99999)', 'E86:') +endfunction + +function! Test_system_exmode() + if has('unix') " echo $? only works on Unix + let cmd = ' -es --headless -u NONE -c "source Xscript" +q; echo "result=$?"' + " Need to put this in a script, "catch" isn't found after an unknown + " function. + call writefile(['try', 'call doesnotexist()', 'catch', 'endtry'], 'Xscript') + let a = system(v:progpath . cmd) + call assert_match('result=0', a) + call assert_equal(0, v:shell_error) + endif + + " Error before try does set error flag. + call writefile(['call nosuchfunction()', 'try', 'call doesnotexist()', 'catch', 'endtry'], 'Xscript') + if has('unix') " echo $? only works on Unix + let a = system(v:progpath . cmd) + call assert_notequal('0', a[0]) + endif + + let cmd = ' -es --headless -u NONE -c "source Xscript" +q' + let a = system(v:progpath . cmd) + call assert_notequal(0, v:shell_error) + call delete('Xscript') + + if has('unix') " echo $? only works on Unix + let cmd = ' -es --headless -u NONE -c "call doesnotexist()" +q; echo $?' + let a = system(v:progpath. cmd) + call assert_notequal(0, a[0]) + endif + + let cmd = ' -es --headless -u NONE -c "call doesnotexist()" +q' + let a = system(v:progpath. cmd) + call assert_notequal(0, v:shell_error) + + if has('unix') " echo $? only works on Unix + let cmd = ' -es --headless -u NONE -c "call doesnotexist()|let a=1" +q; echo $?' + let a = system(v:progpath. cmd) + call assert_notequal(0, a[0]) + endif + + let cmd = ' -es --headless -u NONE -c "call doesnotexist()|let a=1" +q' + let a = system(v:progpath. cmd) + call assert_notequal(0, v:shell_error) +endfunc diff --git a/src/nvim/testdir/test_tab.vim b/src/nvim/testdir/test_tab.vim new file mode 100644 index 0000000000..b847dbd962 --- /dev/null +++ b/src/nvim/testdir/test_tab.vim @@ -0,0 +1,45 @@ + +" Tests for "r<Tab>" with 'smarttab' and 'expandtab' set/not set. +" Also test that dv_ works correctly +func Test_smarttab() + enew! + set smarttab expandtab ts=8 sw=4 + " make sure that backspace works, no matter what termcap is used + exe "set t_kD=\<C-V>x7f t_kb=\<C-V>x08" + call append(0, ['start text', + \ "\t\tsome test text", + \ 'test text', + \ "\t\tother test text", + \ ' a cde', + \ ' f ghi', + \ 'test text', + \ ' Second line beginning with whitespace' + \ ]) + call cursor(1, 1) + exe "normal /some\<CR>" + exe "normal r\t" + call assert_equal("\t\t ome test text", getline('.')) + set noexpandtab + exe "normal /other\<CR>" + exe "normal r\t" + call assert_equal("\t\t ther test text", getline('.')) + + " Test replacing with Tabs and then backspacing to undo it + exe "normal j0wR\t\t\t\<BS>\<BS>\<BS>" + call assert_equal(" a cde", getline('.')) + " Test replacing with Tabs + exe "normal j0wR\t\t\t" + call assert_equal(" \t\thi", getline('.')) + + " Test that copyindent works with expandtab set + set expandtab smartindent copyindent ts=8 sw=8 sts=8 + exe "normal jo{\<CR>x" + call assert_equal('{', getline(line('.') - 1)) + call assert_equal(' x', getline('.')) + set nosol + exe "normal /Second line/\<CR>" + exe "normal fwdv_" + call assert_equal(' with whitespace', getline('.')) + enew! + set expandtab& smartindent& copyindent& ts& sw& sts& +endfunc diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim index 33139fcda0..180563ebbd 100644 --- a/src/nvim/testdir/test_tabpage.vim +++ b/src/nvim/testdir/test_tabpage.vim @@ -473,5 +473,24 @@ func Test_tabnext_on_buf_unload2() endwhile endfunc +func Test_close_on_quitpre() + " This once caused a crash + edit Xtest + new + only + set bufhidden=delete + au QuitPre <buffer> close + tabnew tab1 + tabnew tab2 + 1tabn + q! + call assert_equal(1, tabpagenr()) + call assert_equal(2, tabpagenr('$')) + " clean up + while tabpagenr('$') > 1 + bwipe! + endwhile + buf Xtest +endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim index 0d697b3f3e..d5ce193bc6 100644 --- a/src/nvim/testdir/test_tagjump.vim +++ b/src/nvim/testdir/test_tagjump.vim @@ -95,4 +95,41 @@ function Test_keyword_jump() call delete('Xinclude') endfunction +" Test for jumping to a tag with 'hidden' set, with symbolic link in path of +" tag. This only works for Unix, because of the symbolic link. +func Test_tag_symbolic() + if !has('unix') + return + endif + set hidden + call delete("Xtest.dir", "rf") + call system("ln -s . Xtest.dir") + " Create a tags file with the current directory name inserted. + call writefile([ + \ "SECTION_OFF " . getcwd() . "/Xtest.dir/Xtest.c /^#define SECTION_OFF 3$/", + \ '', + \ ], 'Xtags') + call writefile(['#define SECTION_OFF 3', + \ '#define NUM_SECTIONS 3'], 'Xtest.c') + + " Try jumping to a tag, but with a path that contains a symbolic link. When + " wrong, this will give the ATTENTION message. The next space will then be + " eaten by hit-return, instead of moving the cursor to 'd'. + set tags=Xtags + enew! + call append(0, 'SECTION_OFF') + call cursor(1,1) + exe "normal \<C-]> " + call assert_equal('Xtest.c', expand('%:t')) + call assert_equal(2, col('.')) + + set hidden& + set tags& + enew! + call delete('Xtags') + call delete('Xtest.c') + call delete("Xtest.dir", "rf") + %bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_textformat.vim b/src/nvim/testdir/test_textformat.vim new file mode 100644 index 0000000000..999566c6ac --- /dev/null +++ b/src/nvim/testdir/test_textformat.vim @@ -0,0 +1,168 @@ +" Tests for the various 'formatoptions' settings +func Test_text_format() + enew! + + setl noai tw=2 fo=t + call append('$', [ + \ '{', + \ ' ', + \ '', + \ '}']) + exe "normal /^{/+1\n0" + normal gRa b + let lnum = line('.') + call assert_equal([ + \ 'a', + \ 'b'], getline(lnum - 1, lnum)) + + normal ggdG + setl ai tw=2 fo=tw + call append('$', [ + \ '{', + \ 'a b ', + \ '', + \ 'a ', + \ '}']) + exe "normal /^{/+1\n0" + normal gqgqjjllab + let lnum = line('.') + call assert_equal([ + \ 'a ', + \ 'b ', + \ '', + \ 'a ', + \ 'b'], getline(lnum - 4, lnum)) + + normal ggdG + setl tw=3 fo=t + call append('$', [ + \ '{', + \ "a \<C-A>", + \ '}']) + exe "normal /^{/+1\n0" + exe "normal gqgqo\na \<C-V>\<C-A>" + let lnum = line('.') + call assert_equal([ + \ 'a', + \ "\<C-A>", + \ '', + \ 'a', + \ "\<C-A>"], getline(lnum - 4, lnum)) + + normal ggdG + setl tw=2 fo=tcq1 comments=:# + call append('$', [ + \ '{', + \ 'a b', + \ '#a b', + \ '}']) + exe "normal /^{/+1\n0" + exe "normal gqgqjgqgqo\na b\n#a b" + let lnum = line('.') + call assert_equal([ + \ 'a b', + \ '#a b', + \ '', + \ 'a b', + \ '#a b'], getline(lnum - 4, lnum)) + + normal ggdG + setl tw=5 fo=tcn comments=:# + call append('$', [ + \ '{', + \ ' 1 a', + \ '# 1 a', + \ '}']) + exe "normal /^{/+1\n0" + exe "normal A b\<Esc>jA b" + let lnum = line('.') + call assert_equal([ + \ ' 1 a', + \ ' b', + \ '# 1 a', + \ '# b'], getline(lnum - 3, lnum)) + + normal ggdG + setl tw=5 fo=t2a si + call append('$', [ + \ '{', + \ '', + \ ' x a', + \ ' b', + \ ' c', + \ '', + \ '}']) + exe "normal /^{/+3\n0" + exe "normal i \<Esc>A_" + let lnum = line('.') + call assert_equal([ + \ '', + \ ' x a', + \ ' b_', + \ ' c', + \ ''], getline(lnum - 2, lnum + 2)) + + normal ggdG + setl tw=5 fo=qn comments=:# + call append('$', [ + \ '{', + \ '# 1 a b', + \ '}']) + exe "normal /^{/+1\n5|" + normal gwap + call assert_equal(5, col('.')) + let lnum = line('.') + call assert_equal([ + \ '# 1 a', + \ '# b'], getline(lnum, lnum + 1)) + + normal ggdG + setl tw=5 fo=q2 comments=:# + call append('$', [ + \ '{', + \ '# x', + \ '# a b', + \ '}']) + exe "normal /^{/+1\n0" + normal gwap + let lnum = line('.') + call assert_equal([ + \ '# x a', + \ '# b'], getline(lnum, lnum + 1)) + + normal ggdG + setl tw& fo=a + call append('$', [ + \ '{', + \ ' 1aa', + \ ' 2bb', + \ '}']) + exe "normal /^{/+2\n0" + normal I^^ + call assert_equal('{ 1aa ^^2bb }', getline('.')) + + normal ggdG + setl tw=20 fo=an12wcq comments=s1:/*,mb:*,ex:*/ + call append('$', [ + \ '/* abc def ghi jkl ', + \ ' * mno pqr stu', + \ ' */']) + exe "normal /mno pqr/\n" + normal A vwx yz + let lnum = line('.') + call assert_equal([ + \ ' * mno pqr stu ', + \ ' * vwx yz', + \ ' */'], getline(lnum - 1, lnum + 1)) + + normal ggdG + setl tw=12 fo=tqnc comments=:# + call setline('.', '# 1 xxxxx') + normal A foobar + call assert_equal([ + \ '# 1 xxxxx', + \ '# foobar'], getline(1, 2)) + + setl ai& tw& fo& si& comments& + enew! +endfunc diff --git a/src/nvim/testdir/test_textobjects.vim b/src/nvim/testdir/test_textobjects.vim index 630ae5d3a4..684f197f5f 100644 --- a/src/nvim/testdir/test_textobjects.vim +++ b/src/nvim/testdir/test_textobjects.vim @@ -4,7 +4,7 @@ if !has('textobjects') finish endif -function! CpoM(line, useM, expected) +func CpoM(line, useM, expected) new if a:useM @@ -28,16 +28,127 @@ function! CpoM(line, useM, expected) call assert_equal(getreg('"'), a:expected[2]) q! -endfunction +endfunc -function! Test_inner_block_without_cpo_M() +func Test_inner_block_without_cpo_M() call CpoM('(red \(blue) green)', 0, ['red \(blue', 'red \(blue', '']) -endfunction +endfunc -function! Test_inner_block_with_cpo_M_left_backslash() +func Test_inner_block_with_cpo_M_left_backslash() call CpoM('(red \(blue) green)', 1, ['red \(blue) green', 'blue', 'red \(blue) green']) -endfunction +endfunc -function! Test_inner_block_with_cpo_M_right_backslash() +func Test_inner_block_with_cpo_M_right_backslash() call CpoM('(red (blue\) green)', 1, ['red (blue\) green', 'blue\', 'red (blue\) green']) -endfunction +endfunc + +func Test_quote_selection_selection_exclusive() + new + call setline(1, "a 'bcde' f") + set selection=exclusive + exe "norm! fdvhi'y" + call assert_equal('bcde', @") + set selection&vim + bw! +endfunc + +" Tests for string and html text objects +func Test_string_html_objects() + enew! + + let t = '"wo\"rd\\" foo' + put =t + normal! da" + call assert_equal('foo', getline('.')) + + let t = "'foo' 'bar' 'piep'" + put =t + normal! 0va'a'rx + call assert_equal("xxxxxxxxxxxx'piep'", getline('.')) + + let t = "bla bla `quote` blah" + put =t + normal! 02f`da` + call assert_equal("bla bla blah", getline('.')) + + let t = 'out " in "noXno"' + put =t + normal! 0fXdi" + call assert_equal('out " in ""', getline('.')) + + let t = "\"'\" 'blah' rep 'buh'" + put =t + normal! 03f'vi'ry + call assert_equal("\"'\" 'blah'yyyyy'buh'", getline('.')) + + set quoteescape=+*- + let t = "bla `s*`d-`+++`l**` b`la" + put =t + normal! di` + call assert_equal("bla `` b`la", getline('.')) + + let t = 'voo "nah" sdf " asdf" sdf " sdf" sd' + put =t + normal! $F"va"oha"i"rz + call assert_equal('voo "zzzzzzzzzzzzzzzzzzzzzzzzzzzzsd', getline('.')) + + let t = "-<b>asdf<i>Xasdf</i>asdf</b>-" + put =t + normal! fXdit + call assert_equal('-<b>asdf<i></i>asdf</b>-', getline('.')) + + let t = "-<b>asdX<i>a<i />sdf</i>asdf</b>-" + put =t + normal! 0fXdit + call assert_equal('-<b></b>-', getline('.')) + + let t = "-<b>asdf<i>Xasdf</i>asdf</b>-" + put =t + normal! fXdat + call assert_equal('-<b>asdfasdf</b>-', getline('.')) + + let t = "-<b>asdX<i>as<b />df</i>asdf</b>-" + put =t + normal! 0fXdat + call assert_equal('--', getline('.')) + + let t = "-<b>\ninnertext object\n</b>" + put =t + normal! dit + call assert_equal('-<b></b>', getline('.')) + + set quoteescape& + enew! +endfunc + +" Tests for match() and matchstr() +func Test_match() + call assert_equal("b", matchstr("abcd", ".", 0, 2)) + call assert_equal("bc", matchstr("abcd", "..", 0, 2)) + call assert_equal("c", matchstr("abcd", ".", 2, 0)) + call assert_equal("a", matchstr("abcd", ".", 0, -1)) + call assert_equal(-1, match("abcd", ".", 0, 5)) + call assert_equal(0 , match("abcd", ".", 0, -1)) + call assert_equal(0 , match('abc', '.', 0, 1)) + call assert_equal(1 , match('abc', '.', 0, 2)) + call assert_equal(2 , match('abc', '.', 0, 3)) + call assert_equal(-1, match('abc', '.', 0, 4)) + call assert_equal(1 , match('abc', '.', 1, 1)) + call assert_equal(2 , match('abc', '.', 2, 1)) + call assert_equal(-1, match('abc', '.', 3, 1)) + call assert_equal(3 , match('abc', '$', 0, 1)) + call assert_equal(-1, match('abc', '$', 0, 2)) + call assert_equal(3 , match('abc', '$', 1, 1)) + call assert_equal(3 , match('abc', '$', 2, 1)) + call assert_equal(3 , match('abc', '$', 3, 1)) + call assert_equal(-1, match('abc', '$', 4, 1)) + call assert_equal(0 , match('abc', '\zs', 0, 1)) + call assert_equal(1 , match('abc', '\zs', 0, 2)) + call assert_equal(2 , match('abc', '\zs', 0, 3)) + call assert_equal(3 , match('abc', '\zs', 0, 4)) + call assert_equal(-1, match('abc', '\zs', 0, 5)) + call assert_equal(1 , match('abc', '\zs', 1, 1)) + call assert_equal(2 , match('abc', '\zs', 2, 1)) + call assert_equal(3 , match('abc', '\zs', 3, 1)) + call assert_equal(-1, match('abc', '\zs', 4, 1)) +endfunc diff --git a/src/nvim/testdir/test_undo.vim b/src/nvim/testdir/test_undo.vim index 9ff73fd870..2bc6073d52 100644 --- a/src/nvim/testdir/test_undo.vim +++ b/src/nvim/testdir/test_undo.vim @@ -237,3 +237,54 @@ func Test_insert_expr() close! endfunc + +func Test_undofile_earlier() + throw 'skipped: Nvim does not support test_settime()' + + let t0 = localtime() - 43200 + call test_settime(t0) + new Xfile + call feedkeys("ione\<Esc>", 'xt') + set ul=100 + call test_settime(t0 + 1) + call feedkeys("otwo\<Esc>", 'xt') + set ul=100 + call test_settime(t0 + 2) + call feedkeys("othree\<Esc>", 'xt') + set ul=100 + w + wundo Xundofile + bwipe! + " restore normal timestamps. + call test_settime(0) + new Xfile + rundo Xundofile + earlier 1d + call assert_equal('', getline(1)) + bwipe! + call delete('Xfile') + call delete('Xundofile') +endfunc + +" Test for undo working properly when executing commands from a register. +" Also test this in an empty buffer. +func Test_cmd_in_reg_undo() + enew! + let @a="Ox\<Esc>jAy\<Esc>kdd" + edit +/^$ test_undo.vim + normal @au + call assert_equal(0, &modified) + return + new + normal @au + call assert_equal(0, &modified) + only! + let @a='' +endfunc + +func Test_redo_empty_line() + new + exe "norm\x16r\x160" + exe "norm." + bwipe! +endfunc diff --git a/src/nvim/testdir/test_unlet.vim b/src/nvim/testdir/test_unlet.vim index f6705997a9..3f06058d03 100644 --- a/src/nvim/testdir/test_unlet.vim +++ b/src/nvim/testdir/test_unlet.vim @@ -3,7 +3,7 @@ func Test_read_only() try " this caused a crash - unlet count + unlet v:count catch call assert_true(v:exception =~ ':E795:') endtry @@ -24,3 +24,7 @@ func Test_not_existing() call assert_true(v:exception =~ ':E108:') endtry endfunc + +func Test_unlet_fails() + call assert_fails('unlet v:["count"]', 'E46:') +endfunc diff --git a/src/nvim/testdir/test_usercommands.vim b/src/nvim/testdir/test_usercommands.vim index d0864ec64c..db603610da 100644 --- a/src/nvim/testdir/test_usercommands.vim +++ b/src/nvim/testdir/test_usercommands.vim @@ -102,3 +102,107 @@ func Test_CmdUndefined() call assert_fails('Dothat', 'E492:') call assert_equal('yes', g:didnot) endfunc + +func Test_CmdErrors() + call assert_fails('com! docmd :', 'E183:') + call assert_fails('com! \<Tab> :', 'E182:') + call assert_fails('com! _ :', 'E182:') + call assert_fails('com! X :', 'E841:') + call assert_fails('com! - DoCmd :', 'E175:') + call assert_fails('com! -xxx DoCmd :', 'E181:') + call assert_fails('com! -addr DoCmd :', 'E179:') + call assert_fails('com! -complete DoCmd :', 'E179:') + call assert_fails('com! -complete=xxx DoCmd :', 'E180:') + call assert_fails('com! -complete=custom DoCmd :', 'E467:') + call assert_fails('com! -complete=customlist DoCmd :', 'E467:') + call assert_fails('com! -complete=behave,CustomComplete DoCmd :', 'E468:') + call assert_fails('com! -nargs=x DoCmd :', 'E176:') + call assert_fails('com! -count=1 -count=2 DoCmd :', 'E177:') + call assert_fails('com! -count=x DoCmd :', 'E178:') + call assert_fails('com! -range=x DoCmd :', 'E178:') + + com! -nargs=0 DoCmd : + call assert_fails('DoCmd x', 'E488:') + + com! -nargs=1 DoCmd : + call assert_fails('DoCmd', 'E471:') + + com! -nargs=+ DoCmd : + call assert_fails('DoCmd', 'E471:') + + call assert_fails('com DoCmd :', 'E174:') + comclear + call assert_fails('delcom DoCmd', 'E184:') +endfunc + +func CustomComplete(A, L, P) + return "January\nFebruary\nMars\n" +endfunc + +func CustomCompleteList(A, L, P) + return [ "Monday", "Tuesday", "Wednesday" ] +endfunc + +func Test_CmdCompletion() + call feedkeys(":com -\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"com -addr bang bar buffer complete count nargs range register', @:) + + call feedkeys(":com -nargs=0 -\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"com -nargs=0 -addr bang bar buffer complete count nargs range register', @:) + + call feedkeys(":com -nargs=\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"com -nargs=* + 0 1 ?', @:) + + call feedkeys(":com -addr=\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"com -addr=arguments buffers lines loaded_buffers quickfix tabs windows', @:) + + call feedkeys(":com -complete=co\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"com -complete=color command compiler', @:) + + command! DoCmd1 : + command! DoCmd2 : + call feedkeys(":com \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"com DoCmd1 DoCmd2', @:) + + call feedkeys(":DoC\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"DoCmd1 DoCmd2', @:) + + call feedkeys(":delcom DoC\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"delcom DoCmd1 DoCmd2', @:) + + delcom DoCmd1 + call feedkeys(":delcom DoC\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"delcom DoCmd2', @:) + + call feedkeys(":com DoC\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"com DoCmd2', @:) + + delcom DoCmd2 + call feedkeys(":delcom DoC\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"delcom DoC', @:) + + call feedkeys(":com DoC\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"com DoC', @:) + + com! -complete=behave DoCmd : + call feedkeys(":DoCmd \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"DoCmd mswin xterm', @:) + + " This does not work. Why? + "call feedkeys(":DoCmd x\<C-A>\<C-B>\"\<CR>", 'tx') + "call assert_equal('"DoCmd xterm', @:) + + com! -complete=custom,CustomComplete DoCmd : + call feedkeys(":DoCmd \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"DoCmd January February Mars', @:) + + com! -complete=customlist,CustomCompleteList DoCmd : + call feedkeys(":DoCmd \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"DoCmd Monday Tuesday Wednesday', @:) + + com! -complete=custom,CustomCompleteList DoCmd : + call assert_fails("call feedkeys(':DoCmd \<C-D>', 'tx')", 'E730:') + + com! -complete=customlist,CustomComp DoCmd : + call assert_fails("call feedkeys(':DoCmd \<C-D>', 'tx')", 'E117:') +endfunc diff --git a/src/nvim/testdir/test_utf8_comparisons.vim b/src/nvim/testdir/test_utf8_comparisons.vim new file mode 100644 index 0000000000..576e86142f --- /dev/null +++ b/src/nvim/testdir/test_utf8_comparisons.vim @@ -0,0 +1,95 @@ +" Tests for case-insensitive UTF-8 comparisons (utf_strnicmp() in mbyte.c) +" Also test "g~ap". + +if !has("multi_byte") + finish +endif + +function! Ch(a, op, b, expected) + call assert_equal(eval(printf('"%s" %s "%s"', a:a, a:op, a:b)), a:expected, + \ printf('"%s" %s "%s" should return %d', a:a, a:op, a:b, a:expected)) +endfunction + +function! Chk(a, b, result) + if a:result == 0 + call Ch(a:a, '==?', a:b, 1) + call Ch(a:a, '!=?', a:b, 0) + call Ch(a:a, '<=?', a:b, 1) + call Ch(a:a, '>=?', a:b, 1) + call Ch(a:a, '<?', a:b, 0) + call Ch(a:a, '>?', a:b, 0) + elseif a:result > 0 + call Ch(a:a, '==?', a:b, 0) + call Ch(a:a, '!=?', a:b, 1) + call Ch(a:a, '<=?', a:b, 0) + call Ch(a:a, '>=?', a:b, 1) + call Ch(a:a, '<?', a:b, 0) + call Ch(a:a, '>?', a:b, 1) + else + call Ch(a:a, '==?', a:b, 0) + call Ch(a:a, '!=?', a:b, 1) + call Ch(a:a, '<=?', a:b, 1) + call Ch(a:a, '>=?', a:b, 0) + call Ch(a:a, '<?', a:b, 1) + call Ch(a:a, '>?', a:b, 0) + endif +endfunction + +function! Check(a, b, result) + call Chk(a:a, a:b, a:result) + call Chk(a:b, a:a, -a:result) +endfunction + +function! LT(a, b) + call Check(a:a, a:b, -1) +endfunction + +function! GT(a, b) + call Check(a:a, a:b, 1) +endfunction + +function! EQ(a, b) + call Check(a:a, a:b, 0) +endfunction + +function Test_comparisons() + call EQ('', '') + call LT('', 'a') + call EQ('abc', 'abc') + call EQ('Abc', 'abC') + call LT('ab', 'abc') + call LT('AB', 'abc') + call LT('ab', 'aBc') + call EQ('\xd0\xb9\xd1\x86\xd1\x83\xd0\xba\xd0\xb5\xd0\xbd', '\xd0\xb9\xd0\xa6\xd0\xa3\xd0\xba\xd0\x95\xd0\xbd') + call LT('\xd0\xb9\xd1\x86\xd1\x83\xd0\xba\xd0\xb5\xd0\xbd', '\xd0\xaf\xd1\x86\xd1\x83\xd0\xba\xd0\xb5\xd0\xbd') + call EQ('\xe2\x84\xaa', 'k') + call LT('\xe2\x84\xaa', 'kkkkkk') + call EQ('\xe2\x84\xaa\xe2\x84\xaa\xe2\x84\xaa', 'kkk') + call LT('kk', '\xe2\x84\xaa\xe2\x84\xaa\xe2\x84\xaa') + call EQ('\xe2\x84\xaa\xe2\x84\xa6k\xe2\x84\xaak\xcf\x89', 'k\xcf\x89\xe2\x84\xaakk\xe2\x84\xa6') + call EQ('Abc\x80', 'AbC\x80') + call LT('Abc\x80', 'AbC\x81') + call LT('Abc', 'AbC\x80') + call LT('abc\x80DEF', 'abc\x80def') " case folding stops at the first bad character + call LT('\xc3XYZ', '\xc3xyz') + call EQ('\xef\xbc\xba', '\xef\xbd\x9a') " FF3A (upper), FF5A (lower) + call GT('\xef\xbc\xba', '\xef\xbc\xff') " first string is ok and equals \xef\xbd\x9a after folding, second string is illegal and was left unchanged, then the strings were bytewise compared + call LT('\xc3', '\xc3\x83') + call EQ('\xc3\xa3xYz', '\xc3\x83XyZ') + for n in range(0x60, 0xFF) + call LT(printf('xYz\x%.2X', n-1), printf('XyZ\x%.2X', n)) + endfor + for n in range(0x80, 0xBF) + call EQ(printf('xYz\xc2\x%.2XUvW', n), printf('XyZ\xc2\x%.2XuVw', n)) + endfor + for n in range(0xC0, 0xFF) + call LT(printf('xYz\xc2\x%.2XUvW', n), printf('XyZ\xc2\x%.2XuVw', n)) + endfor +endfunction + +" test that g~ap changes one paragraph only. +function Test_gap() + new + call feedkeys("iabcd\n\ndefggg0g~ap", "tx") + call assert_equal(["ABCD", "", "defg"], getline(1,3)) +endfunction diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index 4e0f1bbd2f..c449fc91b0 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -1215,6 +1215,62 @@ func Test_bitwise_functions() call assert_fails("call invert({})", 'E728:') endfunc +" Test trailing text after :endfunction {{{1 +func Test_endfunction_trailing() + call assert_false(exists('*Xtest')) + + exe "func Xtest()\necho 'hello'\nendfunc\nlet done = 'yes'" + call assert_true(exists('*Xtest')) + call assert_equal('yes', done) + delfunc Xtest + unlet done + + exe "func Xtest()\necho 'hello'\nendfunc|let done = 'yes'" + call assert_true(exists('*Xtest')) + call assert_equal('yes', done) + delfunc Xtest + unlet done + + " trailing line break + exe "func Xtest()\necho 'hello'\nendfunc\n" + call assert_true(exists('*Xtest')) + delfunc Xtest + + set verbose=1 + exe "func Xtest()\necho 'hello'\nendfunc \" garbage" + call assert_true(exists('*Xtest')) + delfunc Xtest + + call assert_fails("func Xtest()\necho 'hello'\nendfunc garbage", 'E946') + call assert_true(exists('*Xtest')) + delfunc Xtest + set verbose=0 + + function Foo() + echo 'hello' + endfunction | echo 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' + delfunc Foo +endfunc + +func Test_delfunction_force() + delfunc! Xtest + delfunc! Xtest + func Xtest() + echo 'nothing' + endfunc + delfunc! Xtest + delfunc! Xtest +endfunc + +" Test using bang after user command {{{1 +func Test_user_command_with_bang() + command -bang Nieuw let nieuw = 1 + Ni! + call assert_equal(1, nieuw) + unlet nieuw + delcommand Nieuw +endfunc + "------------------------------------------------------------------------------- " Modelines {{{1 " vim: ts=8 sw=4 tw=80 fdm=marker diff --git a/src/nvim/testdir/test_virtualedit.vim b/src/nvim/testdir/test_virtualedit.vim new file mode 100644 index 0000000000..2b8849f488 --- /dev/null +++ b/src/nvim/testdir/test_virtualedit.vim @@ -0,0 +1,43 @@ +" Tests for 'virtualedit'. + +func Test_yank_move_change() + new + call setline(1, [ + \ "func foo() error {", + \ "\tif n, err := bar();", + \ "\terr != nil {", + \ "\t\treturn err", + \ "\t}", + \ "\tn = n * n", + \ ]) + set virtualedit=all + set ts=4 + function! MoveSelectionDown(count) abort + normal! m` + silent! exe "'<,'>move'>+".a:count + norm! `` + endfunction + + xmap ]e :<C-U>call MoveSelectionDown(v:count1)<CR> + 2 + normal 2gg + normal J + normal jVj + normal ]e + normal ce + bwipe! + set virtualedit= + set ts=8 +endfunc + +func Test_paste_end_of_line() + new + set virtualedit=all + call setline(1, ['456', '123']) + normal! gg0"ay$ + exe "normal! 2G$lllA\<C-O>:normal! \"agP\r" + call assert_equal('123456', getline(2)) + + bwipe! + set virtualedit= +endfunc diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim index cf0e535937..0f2e7e493e 100644 --- a/src/nvim/testdir/test_visual.vim +++ b/src/nvim/testdir/test_visual.vim @@ -1,13 +1,14 @@ -" Tests for Visual mode -if !has('multi_byte') - finish -endif - +" Tests for various Visual mode. if !has('visual') finish endif + func Test_block_shift_multibyte() + " Uses double-wide character. + if !has('multi_byte') + return + endif split call setline(1, ['xヹxxx', 'ヹxxx']) exe "normal 1G0l\<C-V>jl>" @@ -15,3 +16,139 @@ func Test_block_shift_multibyte() call assert_equal(' ヹxxx', getline(2)) q! endfunc + +func Test_Visual_ctrl_o() + new + call setline(1, ['one', 'two', 'three']) + call cursor(1,2) + set noshowmode + set tw=0 + call feedkeys("\<c-v>jjlIa\<c-\>\<c-o>:set tw=88\<cr>\<esc>", 'tx') + call assert_equal(['oane', 'tawo', 'tahree'], getline(1, 3)) + call assert_equal(88, &tw) + set tw& + bw! +endfu + +func Test_Visual_vapo() + new + normal oxx + normal vapo + bwipe! +endfunc + +func Test_dotregister_paste() + new + exe "norm! ihello world\<esc>" + norm! 0ve".p + call assert_equal('hello world world', getline(1)) + q! +endfunc + +func Test_Visual_inner_quote() + new + normal oxX + normal vki' + bwipe! +endfunc + +" Test for visual block shift and tab characters. +func Test_block_shift_tab() + enew! + call append(0, repeat(['one two three'], 5)) + call cursor(1,1) + exe "normal i\<C-G>u" + exe "normal fe\<C-V>4jR\<Esc>ugvr1" + call assert_equal('on1 two three', getline(1)) + call assert_equal('on1 two three', getline(2)) + call assert_equal('on1 two three', getline(5)) + + enew! + call append(0, repeat(['abcdefghijklmnopqrstuvwxyz'], 5)) + call cursor(1,1) + exe "normal \<C-V>4jI \<Esc>j<<11|D" + exe "normal j7|a\<Tab>\<Tab>" + exe "normal j7|a\<Tab>\<Tab> " + exe "normal j7|a\<Tab> \<Tab>\<Esc>4k13|\<C-V>4j<" + call assert_equal(' abcdefghijklmnopqrstuvwxyz', getline(1)) + call assert_equal('abcdefghij', getline(2)) + call assert_equal(" abc\<Tab> defghijklmnopqrstuvwxyz", getline(3)) + call assert_equal(" abc\<Tab> defghijklmnopqrstuvwxyz", getline(4)) + call assert_equal(" abc\<Tab> defghijklmnopqrstuvwxyz", getline(5)) + + %s/\s\+//g + call cursor(1,1) + exe "normal \<C-V>4jI \<Esc>j<<" + exe "normal j7|a\<Tab>\<Tab>" + exe "normal j7|a\<Tab>\<Tab>\<Tab>\<Tab>\<Tab>" + exe "normal j7|a\<Tab> \<Tab>\<Tab>\<Esc>4k13|\<C-V>4j3<" + call assert_equal(' abcdefghijklmnopqrstuvwxyz', getline(1)) + call assert_equal('abcdefghij', getline(2)) + call assert_equal(" abc\<Tab> defghijklmnopqrstuvwxyz", getline(3)) + call assert_equal(" abc\<Tab>\<Tab>defghijklmnopqrstuvwxyz", getline(4)) + call assert_equal(" abc\<Tab> defghijklmnopqrstuvwxyz", getline(5)) + + enew! +endfunc + +" Tests Blockwise Visual when there are TABs before the text. +func Test_blockwise_visual() + enew! + call append(0, ['123456', + \ '234567', + \ '345678', + \ '', + \ 'test text test tex start here', + \ "\t\tsome text", + \ "\t\ttest text", + \ 'test text']) + call cursor(1,1) + exe "normal /start here$\<CR>" + exe 'normal "by$' . "\<C-V>jjlld" + exe "normal /456$\<CR>" + exe "normal \<C-V>jj" . '"bP' + call assert_equal(['123start here56', + \ '234start here67', + \ '345start here78', + \ '', + \ 'test text test tex rt here', + \ "\t\tsomext", + \ "\t\ttesext"], getline(1, 7)) + + enew! +endfunc + +" Test Virtual replace mode. +func Test_virtual_replace() + throw 'skipped: TODO: ' + exe "set t_kD=\<C-V>x7f t_kb=\<C-V>x08" + enew! + exe "normal a\nabcdefghi\njk\tlmn\n opq rst\n\<C-D>uvwxyz" + call cursor(1,1) + set ai bs=2 + exe "normal gR0\<C-D> 1\nA\nBCDEFGHIJ\n\tKL\nMNO\nPQR" + call assert_equal([' 1', + \ ' A', + \ ' BCDEFGHIJ', + \ ' KL', + \ ' MNO', + \ ' PQR', + \ ], getline(1, 6)) + normal G + mark a + inoremap <C-D> <Del> + exe "normal o0\<C-D>\nabcdefghi\njk\tlmn\n opq\trst\n\<C-D>uvwxyz\n" + exe "normal 'ajgR0\<C-D> 1\nA\nBCDEFGHIJ\n\tKL\nMNO\nPQR" . repeat("\<BS>", 29) + call assert_equal([' 1', + \ 'abcdefghi', + \ 'jk lmn', + \ ' opq rst', + \ 'uvwxyz'], getline(7, 11)) + normal G + exe "normal iab\tcdefghi\tjkl" + exe "normal 0gRAB......CDEFGHI.J\<Esc>o" + exe "normal iabcdefghijklmnopqrst\<Esc>0gRAB\tIJKLMNO\tQR" + call assert_equal(['AB......CDEFGHI.Jkl', + \ 'AB IJKLMNO QRst'], getline(12, 13)) + enew! +endfunc diff --git a/src/nvim/testdir/test_winbuf_close.vim b/src/nvim/testdir/test_winbuf_close.vim new file mode 100644 index 0000000000..ed64dd79b7 --- /dev/null +++ b/src/nvim/testdir/test_winbuf_close.vim @@ -0,0 +1,124 @@ +" Test for commands that close windows and/or buffers: +" :quit +" :close +" :hide +" :only +" :sall +" :all +" :ball +" :buf +" :edit +" +func Test_winbuf_close() + enew | only + + call writefile(['testtext 1'], 'Xtest1') + call writefile(['testtext 2'], 'Xtest2') + call writefile(['testtext 3'], 'Xtest3') + + next! Xtest1 Xtest2 + call setline(1, 'testtext 1 1') + + " test for working :n when hidden set + set hidden + next + call assert_equal('Xtest2', bufname('%')) + + " test for failing :rew when hidden not set + set nohidden + call setline(1, 'testtext 2 2') + call assert_fails('rewind', 'E37') + call assert_equal('Xtest2', bufname('%')) + call assert_equal('testtext 2 2', getline(1)) + + " test for working :rew when hidden set + set hidden + rewind + call assert_equal('Xtest1', bufname('%')) + call assert_equal('testtext 1 1', getline(1)) + + " test for :all keeping a buffer when it's modified + set nohidden + call setline(1, 'testtext 1 1 1') + split + next Xtest2 Xtest3 + all + 1wincmd w + call assert_equal('Xtest1', bufname('%')) + call assert_equal('testtext 1 1 1', getline(1)) + + " test abandoning changed buffer, should be unloaded even when 'hidden' set + set hidden + call setline(1, 'testtext 1 1 1 1') + quit! + call assert_equal('Xtest2', bufname('%')) + call assert_equal('testtext 2 2', getline(1)) + unhide + call assert_equal('Xtest2', bufname('%')) + call assert_equal('testtext 2 2', getline(1)) + + " test ":hide" hides anyway when 'hidden' not set + set nohidden + call setline(1, 'testtext 2 2 2') + hide + call assert_equal('Xtest3', bufname('%')) + call assert_equal('testtext 3', getline(1)) + + " test ":edit" failing in modified buffer when 'hidden' not set + call setline(1, 'testtext 3 3') + call assert_fails('edit Xtest1', 'E37') + call assert_equal('Xtest3', bufname('%')) + call assert_equal('testtext 3 3', getline(1)) + + " test ":edit" working in modified buffer when 'hidden' set + set hidden + edit Xtest1 + call assert_equal('Xtest1', bufname('%')) + call assert_equal('testtext 1', getline(1)) + + " test ":close" not hiding when 'hidden' not set in modified buffer + split Xtest3 + set nohidden + call setline(1, 'testtext 3 3 3') + call assert_fails('close', 'E37') + call assert_equal('Xtest3', bufname('%')) + call assert_equal('testtext 3 3 3', getline(1)) + + " test ":close!" does hide when 'hidden' not set in modified buffer; + call setline(1, 'testtext 3 3 3 3') + close! + call assert_equal('Xtest1', bufname('%')) + call assert_equal('testtext 1', getline(1)) + + set nohidden + + " test ":all!" hides changed buffer + split Xtest4 + call setline(1, 'testtext 4') + all! + 1wincmd w + call assert_equal('Xtest2', bufname('%')) + call assert_equal('testtext 2 2 2', getline(1)) + + " test ":q!" and hidden buffer. + bwipe! Xtest1 Xtest2 Xtest3 Xtest4 + split Xtest1 + wincmd w + bwipe! + set modified + bot split Xtest2 + set modified + bot split Xtest3 + set modified + wincmd t + hide + call assert_equal('Xtest2', bufname('%')) + quit! + call assert_equal('Xtest3', bufname('%')) + call assert_fails('silent! quit!', 'E162') + call assert_equal('Xtest1', bufname('%')) + + call delete('Xtest1') + call delete('Xtest2') + call delete('Xtest3') +endfunc diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index 188a7ed0f3..139d29a48b 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -306,20 +306,14 @@ func Test_window_width() set winfixwidth vsplit Xc let [ww1, ww2, ww3] = [winwidth(1), winwidth(2), winwidth(3)] - " FIXME: commented out: I would expect the width of 2nd window to - " remain 2 but it's actually 1?! - "call assert_equal(2, winwidth(2)) + call assert_equal(2, winwidth(2)) call assert_inrange(ww3, ww3 + 1, ww1) 3wincmd > - " FIXME: commented out: I would expect the width of 2nd window to - " remain 2 but it's actually 1?! - "call assert_equal(2, winwidth(2)) + call assert_equal(2, winwidth(2)) call assert_equal(ww1 + 3, winwidth(1)) call assert_equal(ww3 - 3, winwidth(3)) wincmd = - " FIXME: commented out: I would expect the width of 2nd window to - " remain 2 but it's actually 1?! - "call assert_equal(2, winwidth(2)) + call assert_equal(2, winwidth(2)) call assert_equal(ww1, winwidth(1)) call assert_equal(ww3, winwidth(3)) @@ -336,6 +330,50 @@ func Test_window_width() bw Xa Xb Xc endfunc +func Test_equalalways_on_close() + set equalalways + vsplit + windo split + split + wincmd J + " now we have a frame top-left with two windows, a frame top-right with two + " windows and a frame at the bottom, full-width. + let height_1 = winheight(1) + let height_2 = winheight(2) + let height_3 = winheight(3) + let height_4 = winheight(4) + " closing the bottom window causes all windows to be resized. + close + call assert_notequal(height_1, winheight(1)) + call assert_notequal(height_2, winheight(2)) + call assert_notequal(height_3, winheight(3)) + call assert_notequal(height_4, winheight(4)) + call assert_equal(winheight(1), winheight(3)) + call assert_equal(winheight(2), winheight(4)) + + 1wincmd w + split + 4wincmd w + resize + 5 + " left column has three windows, equalized heights. + " right column has two windows, top one a bit higher + let height_1 = winheight(1) + let height_2 = winheight(2) + let height_4 = winheight(4) + let height_5 = winheight(5) + 3wincmd w + " closing window in left column equalizes heights in left column but not in + " the right column + close + call assert_notequal(height_1, winheight(1)) + call assert_notequal(height_2, winheight(2)) + call assert_equal(height_4, winheight(3)) + call assert_equal(height_5, winheight(4)) + + only + set equalalways& +endfunc + func Test_window_jump_tag() help /iccf diff --git a/src/nvim/testdir/unix.vim b/src/nvim/testdir/unix.vim index a7daacf8cf..ce2beff7fe 100644 --- a/src/nvim/testdir/unix.vim +++ b/src/nvim/testdir/unix.vim @@ -2,6 +2,12 @@ " Always use "sh", don't use the value of "$SHELL". set shell=sh +if has('win32') + set shellcmdflag=-c shellxquote= shellxescape= shellquote= + let &shellredir = '>%s 2>&1' + set shellslash +endif + " Don't depend on system locale, always use utf-8 set encoding=utf-8 diff --git a/src/nvim/testdir/view_util.vim b/src/nvim/testdir/view_util.vim new file mode 100644 index 0000000000..eb92630761 --- /dev/null +++ b/src/nvim/testdir/view_util.vim @@ -0,0 +1,30 @@ +" Functions about view shared by several tests + +" ScreenLines(lnum, width) or +" ScreenLines([start, end], width) +function! ScreenLines(lnum, width) abort + redraw! + if type(a:lnum) == v:t_list + let start = a:lnum[0] + let end = a:lnum[1] + else + let start = a:lnum + let end = a:lnum + endif + let lines = [] + for l in range(start, end) + let lines += [join(map(range(1, a:width), 'nr2char(screenchar(l, v:val))'), '')] + endfor + return lines +endfunction + +function! NewWindow(height, width) abort + exe a:height . 'new' + exe a:width . 'vsp' + redraw! +endfunction + +function! CloseWindow() abort + bw! + redraw! +endfunction diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 03587d68f0..b04a6ce4f9 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -8,13 +8,12 @@ #include "nvim/api/private/helpers.h" #include "nvim/ascii.h" #include "nvim/main.h" +#include "nvim/aucmd.h" #include "nvim/os/os.h" #include "nvim/os/input.h" #include "nvim/event/rstream.h" #define PASTETOGGLE_KEY "<Paste>" -#define FOCUSGAINED_KEY "<FocusGained>" -#define FOCUSLOST_KEY "<FocusLost>" #define KEY_BUFFER_SIZE 0xfff #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -48,9 +47,11 @@ void term_input_init(TermInput *input, Loop *loop) termkey_set_canonflags(input->tk, curflags | TERMKEY_CANON_DELBS); // setup input handle #ifdef WIN32 - uv_tty_init(loop, &input->tty_in, 0, 1); + uv_tty_init(&loop->uv, &input->tty_in, 0, 1); uv_tty_set_mode(&input->tty_in, UV_TTY_MODE_RAW); - rstream_init_stream(&input->read_stream, &input->tty_in, 0xfff); + rstream_init_stream(&input->read_stream, + (uv_stream_t *)&input->tty_in, + 0xfff); #else rstream_init_fd(loop, &input->read_stream, input->in_fd, 0xfff); #endif @@ -200,18 +201,25 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key) len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Right"); } - if (ev == TERMKEY_MOUSE_PRESS) { - if (button == 4) { - len += (size_t)snprintf(buf + len, sizeof(buf) - len, "ScrollWheelUp"); - } else if (button == 5) { - len += (size_t)snprintf(buf + len, sizeof(buf) - len, "ScrollWheelDown"); - } else { - len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Mouse"); - } - } else if (ev == TERMKEY_MOUSE_DRAG) { - len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Drag"); - } else if (ev == TERMKEY_MOUSE_RELEASE) { - len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Release"); + switch (ev) { + case TERMKEY_MOUSE_PRESS: + if (button == 4) { + len += (size_t)snprintf(buf + len, sizeof(buf) - len, "ScrollWheelUp"); + } else if (button == 5) { + len += (size_t)snprintf(buf + len, sizeof(buf) - len, + "ScrollWheelDown"); + } else { + len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Mouse"); + } + break; + case TERMKEY_MOUSE_DRAG: + len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Drag"); + break; + case TERMKEY_MOUSE_RELEASE: + len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Release"); + break; + case TERMKEY_MOUSE_UNKNOWN: + assert(false); } len += (size_t)snprintf(buf + len, sizeof(buf) - len, "><%d,%d>", col, row); @@ -280,9 +288,9 @@ static void timer_cb(TimeWatcher *watcher, void *data) /// Handle focus events. /// -/// If the upcoming sequence of bytes in the input stream matches either the -/// escape code for focus gained `<ESC>[I` or focus lost `<ESC>[O` then consume -/// that sequence and push the appropriate event into the input queue +/// If the upcoming sequence of bytes in the input stream matches the termcode +/// for "focus gained" or "focus lost", consume that sequence and schedule an +/// event on the main loop. /// /// @param input the input stream /// @return true iff handle_focus_event consumed some input @@ -294,11 +302,7 @@ static bool handle_focus_event(TermInput *input) // Advance past the sequence bool focus_gained = *rbuffer_get(input->read_stream.buffer, 2) == 'I'; rbuffer_consumed(input->read_stream.buffer, 3); - if (focus_gained) { - enqueue_input(input, FOCUSGAINED_KEY, sizeof(FOCUSGAINED_KEY) - 1); - } else { - enqueue_input(input, FOCUSLOST_KEY, sizeof(FOCUSLOST_KEY) - 1); - } + aucmd_schedule_focusgained(focus_gained); return true; } return false; diff --git a/src/nvim/tui/terminfo.c b/src/nvim/tui/terminfo.c new file mode 100644 index 0000000000..2f07e83158 --- /dev/null +++ b/src/nvim/tui/terminfo.c @@ -0,0 +1,255 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +// Built-in fallback terminfo entries. + +#include <stdbool.h> +#include <string.h> + +#include <unibilium.h> + +#include "nvim/log.h" +#include "nvim/globals.h" +#include "nvim/memory.h" +#include "nvim/message.h" +#include "nvim/option.h" +#include "nvim/tui/terminfo.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "tui/terminfo.c.generated.h" +#endif + +// One creates the dumps from terminfo.src by using +// od -t d1 -w | cut -c9- | sed -e 's/\>/,/g' +// on the compiled files. + +// Taken from Dickey ncurses terminfo.src dated 2017-04-22. +// This is a 256-colour terminfo description that lacks true-colour and +// DECSTBM/DECSLRM/DECLRMM capabilities that xterm actually has. +static const signed char xterm_256colour_terminfo[] = { + 26, 1, 37, 0, 29, 0, 15, 0, 105, 1, -42, 5, 120, 116, 101, 114, 109, 45, 50, 53, 54, 99, 111, 108, 111, 114, 124, 120, 116, 101, 114, 109, 32, 119, 105, 116, 104, 32, 50, 53, 54, 32, 99, 111, 108, 111, 114, 115, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 80, 0, 8, 0, 24, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, -1, 127, 0, 0, 4, 0, 6, 0, 8, 0, 25, 0, 30, 0, 38, 0, 42, 0, 46, 0, -1, -1, 57, 0, 74, 0, 76, 0, 80, 0, 87, 0, -1, -1, 89, 0, 102, 0, -1, -1, 106, 0, 110, 0, 120, 0, 124, 0, -1, -1, -1, -1,-128, 0,-124, 0,-119, 0,-114, 0, -1, -1,-105, 0,-100, 0, -95, 0, -1, -1, -90, 0, -85, 0, -80, 0, -75, 0, -66, 0, -62, 0, -55, 0, -1, -1, -46, 0, -41, 0, -35, 0, -29, 0, -1, -1, -1, -1, -1, -1, -11, 0, -1, -1, -1, -1, -1, -1, 7, 1, -1, -1, 11, 1, -1, -1, -1, -1, -1, -1, 13, 1, -1, -1, 18, 1, -1, -1, -1, -1, -1, -1, -1, -1, 22, 1, 26, 1, 32, 1, 36, 1, 40, 1, 44, 1, 50, 1, 56, 1, 62, 1, 68, 1, 74, 1, 78, 1, -1, -1, 83, 1, -1, -1, 87, 1, 92, 1, 97, 1, 101, 1, 108, 1, -1, -1, 115, 1, 119, 1, 127, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-121, 1,-112, 1, -1, -1, -1, -1,-103, 1, -94, 1, -85, 1, -76, 1, -67, 1, -58, 1, -49, 1, -40, 1, -31, 1, -22, 1, -1, -1, -1, -1, -1, -1, -13, 1, -9, 1, -4, 1, -1, -1, 1, 2, 10, 2, -1, -1, -1, -1, 28, 2, 31, 2, 42, 2, 45, 2, 47, 2, 50, 2,-113, 2, -1, -1,-110, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-108, 2, -1, -1, -1, -1, -1, -1, -1, -1,-104, 2, -1, -1, -51, 2, -1, -1, -1, -1, -47, 2, -41, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -35, 2, -31, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -27, 2, -1, -1, -1, -1, -20, 2, -1, -1, -1, -1, -1, -1, -1, -1, -13, 2, -6, 2, 1, 3, -1, -1, -1, -1, 8, 3, -1, -1, 15, 3, -1, -1, -1, -1, -1, -1, 22, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 29, 3, 35, 3, 41, 3, 48, 3, 55, 3, 62, 3, 69, 3, 77, 3, 85, 3, 93, 3, 101, 3, 109, 3, 117, 3, 125, 3,-123, 3,-116, 3,-109, 3,-102, 3, -95, 3, -87, 3, -79, 3, -71, 3, -63, 3, -55, 3, -47, 3, -39, 3, -31, 3, -24, 3, -17, 3, -10, 3, -3, 3, 5, 4, 13, 4, 21, 4, 29, 4, 37, 4, 45, 4, 53, 4, 61, 4, 68, 4, 75, 4, 82, 4, 89, 4, 97, 4, 105, 4, 113, 4, 121, 4,-127, 4,-119, 4,-111, 4,-103, 4, -96, 4, -89, 4, -82, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -77, 4, -66, 4, -61, 4, -42, 4, -38, 4, -29, 4, -22, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 72, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 77, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 83, 5, -1, -1, -1, -1, -1, -1, 87, 5,-106, 5, 27, 91, 90, 0, 7, 0, 13, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 114, 0, 27, 91, 51, 103, 0, 27, 91, 72, 27, 91, 50, 74, 0, 27, 91, 75, 0, 27, 91, 74, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 71, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 72, 0, 10, 0, 27, 91, 72, 0, 27, 91, 63, 50, 53, 108, 0, 8, 0, 27, 91, 63, 49, 50, 108, 27, 91, 63, 50, 53, 104, 0, 27, 91, 67, 0, 27, 91, 65, 0, 27, 91, 63, 49, 50, 59, 50, 53, 104, 0, 27, 91, 80, 0, 27, 91, 77, 0, 27, 40, 48, 0, 27, 91, 53, 109, 0, 27, 91, 49, 109, 0, 27, 91, 63, 49, 48, 52, 57, 104, 0, 27, 91, 50, 109, 0, 27, 91, 52, 104, 0, 27, 91, 56, 109, 0, 27, 91, 55, 109, 0, 27, 91, 55, 109, 0, 27, 91, 52, 109, 0, 27, 91, 37, 112, 49, 37, 100, 88, 0, 27, 40, 66, 0, 27, 40, 66, 27, 91, 109, 0, 27, 91, 63, 49, 48, 52, 57, 108, 0, 27, 91, 52, 108, 0, 27, 91, 50, 55, 109, 0, 27, 91, 50, 52, 109, 0, 27, 91, 63, 53, 104, 36, 60, 49, 48, 48, 47, 62, 27, 91, 63, 53, 108, 0, 27, 91, 33, 112, 27, 91, 63, 51, 59, 52, 108, 27, 91, 52, 108, 27, 62, 0, 27, 91, 76, 0, 8, 0, 27, 91, 51, 126, 0, 27, 79, 66, 0, 27, 79, 80, 0, 27, 91, 50, 49, 126, 0, 27, 79, 81, 0, 27, 79, 82, 0, 27, 79, 83, 0, 27, 91, 49, 53, 126, 0, 27, 91, 49, 55, 126, 0, 27, 91, 49, 56, 126, 0, 27, 91, 49, 57, 126, 0, 27, 91, 50, 48, 126, 0, 27, 79, 72, 0, 27, 91, 50, 126, 0, 27, 79, 68, 0, 27, 91, 54, 126, 0, 27, 91, 53, 126, 0, 27, 79, 67, 0, 27, 91, 49, 59, 50, 66, 0, 27, 91, 49, 59, 50, 65, 0, 27, 79, 65, 0, 27, 91, 63, 49, 108, 27, 62, 0, 27, 91, 63, 49, 104, 27, 61, 0, 27, 91, 63, 49, 48, 51, 52, 108, 0, 27, 91, 63, 49, 48, 51, 52, 104, 0, 27, 91, 37, 112, 49, 37, 100, 80, 0, 27, 91, 37, 112, 49, 37, 100, 77, 0, 27, 91, 37, 112, 49, 37, 100, 66, 0, 27, 91, 37, 112, 49, 37, 100, 64, 0, 27, 91, 37, 112, 49, 37, 100, 83, 0, 27, 91, 37, 112, 49, 37, 100, 76, 0, 27, 91, 37, 112, 49, 37, 100, 68, 0, 27, 91, 37, 112, 49, 37, 100, 67, 0, 27, 91, 37, 112, 49, 37, 100, 84, 0, 27, 91, 37, 112, 49, 37, 100, 65, 0, 27, 91, 105, 0, 27, 91, 52, 105, 0, 27, 91, 53, 105, 0, 27, 99, 27, 93, 49, 48, 52, 7, 0, 27, 91, 33, 112, 27, 91, 63, 51, 59, 52, 108, 27, 91, 52, 108, 27, 62, 0, 27, 56, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 100, 0, 27, 55, 0, 10, 0, 27, 77, 0, 37, 63, 37, 112, 57, 37, 116, 27, 40, 48, 37, 101, 27, 40, 66, 37, 59, 27, 91, 48, 37, 63, 37, 112, 54, 37, 116, 59, 49, 37, 59, 37, 63, 37, 112, 53, 37, 116, 59, 50, 37, 59, 37, 63, 37, 112, 50, 37, 116, 59, 52, 37, 59, 37, 63, 37, 112, 49, 37, 112, 51, 37, 124, 37, 116, 59, 55, 37, 59, 37, 63, 37, 112, 52, 37, 116, 59, 53, 37, 59, 37, 63, 37, 112, 55, 37, 116, 59, 56, 37, 59, 109, 0, 27, 72, 0, 9, 0, 27, 79, 69, 0, 96, 96, 97, 97, 102, 102, 103, 103, 105, 105, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 125, 126, 126, 0, 27, 91, 90, 0, 27, 91, 63, 55, 104, 0, 27, 91, 63, 55, 108, 0, 27, 79, 70, 0, 27, 79, 77, 0, 27, 91, 51, 59, 50, 126, 0, 27, 91, 49, 59, 50, 70, 0, 27, 91, 49, 59, 50, 72, 0, 27, 91, 50, 59, 50, 126, 0, 27, 91, 49, 59, 50, 68, 0, 27, 91, 54, 59, 50, 126, 0, 27, 91, 53, 59, 50, 126, 0, 27, 91, 49, 59, 50, 67, 0, 27, 91, 50, 51, 126, 0, 27, 91, 50, 52, 126, 0, 27, 91, 49, 59, 50, 80, 0, 27, 91, 49, 59, 50, 81, 0, 27, 91, 49, 59, 50, 82, 0, 27, 91, 49, 59, 50, 83, 0, 27, 91, 49, 53, 59, 50, 126, 0, 27, 91, 49, 55, 59, 50, 126, 0, 27, 91, 49, 56, 59, 50, 126, 0, 27, 91, 49, 57, 59, 50, 126, 0, 27, 91, 50, 48, 59, 50, 126, 0, 27, 91, 50, 49, 59, 50, 126, 0, 27, 91, 50, 51, 59, 50, 126, 0, 27, 91, 50, 52, 59, 50, 126, 0, 27, 91, 49, 59, 53, 80, 0, 27, 91, 49, 59, 53, 81, 0, 27, 91, 49, 59, 53, 82, 0, 27, 91, 49, 59, 53, 83, 0, 27, 91, 49, 53, 59, 53, 126, 0, 27, 91, 49, 55, 59, 53, 126, 0, 27, 91, 49, 56, 59, 53, 126, 0, 27, 91, 49, 57, 59, 53, 126, 0, 27, 91, 50, 48, 59, 53, 126, 0, 27, 91, 50, 49, 59, 53, 126, 0, 27, 91, 50, 51, 59, 53, 126, 0, 27, 91, 50, 52, 59, 53, 126, 0, 27, 91, 49, 59, 54, 80, 0, 27, 91, 49, 59, 54, 81, 0, 27, 91, 49, 59, 54, 82, 0, 27, 91, 49, 59, 54, 83, 0, 27, 91, 49, 53, 59, 54, 126, 0, 27, 91, 49, 55, 59, 54, 126, 0, 27, 91, 49, 56, 59, 54, 126, 0, 27, 91, 49, 57, 59, 54, 126, 0, 27, 91, 50, 48, 59, 54, 126, 0, 27, 91, 50, 49, 59, 54, 126, 0, 27, 91, 50, 51, 59, 54, 126, 0, 27, 91, 50, 52, 59, 54, 126, 0, 27, 91, 49, 59, 51, 80, 0, 27, 91, 49, 59, 51, 81, 0, 27, 91, 49, 59, 51, 82, 0, 27, 91, 49, 59, 51, 83, 0, 27, 91, 49, 53, 59, 51, 126, 0, 27, 91, 49, 55, 59, 51, 126, 0, 27, 91, 49, 56, 59, 51, 126, 0, 27, 91, 49, 57, 59, 51, 126, 0, 27, 91, 50, 48, 59, 51, 126, 0, 27, 91, 50, 49, 59, 51, 126, 0, 27, 91, 50, 51, 59, 51, 126, 0, 27, 91, 50, 52, 59, 51, 126, 0, 27, 91, 49, 59, 52, 80, 0, 27, 91, 49, 59, 52, 81, 0, 27, 91, 49, 59, 52, 82, 0, 27, 91, 49, 75, 0, 27, 91, 37, 105, 37, 100, 59, 37, 100, 82, 0, 27, 91, 54, 110, 0, 27, 91, 63, 37, 91, 59, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 93, 99, 0, 27, 91, 99, 0, 27, 91, 51, 57, 59, 52, 57, 109, 0, 27, 93, 49, 48, 52, 7, 0, 27, 93, 52, 59, 37, 112, 49, 37, 100, 59, 114, 103, 98, 58, 37, 112, 50, 37, 123, 50, 53, 53, 125, 37, 42, 37, 123, 49, 48, 48, 48, 125, 37, 47, 37, 50, 46, 50, 88, 47, 37, 112, 51, 37, 123, 50, 53, 53, 125, 37, 42, 37, 123, 49, 48, 48, 48, 125, 37, 47, 37, 50, 46, 50, 88, 47, 37, 112, 52, 37, 123, 50, 53, 53, 125, 37, 42, 37, 123, 49, 48, 48, 48, 125, 37, 47, 37, 50, 46, 50, 88, 27, 92, 0, 27, 91, 51, 109, 0, 27, 91, 50, 51, 109, 0, 27, 91, 77, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 51, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 57, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 51, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 52, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 49, 48, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 52, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0 +}; +// Taken from unibilium/t/static_tmux.c as of 2015-08-14. +// This is an 256-colour terminfo description that lacks +// status line capabilities that tmux actually has. +static const signed char tmux_256colour_terminfo[] = { + 26, 1, 56, 0, 15, 0, 15, 0, 105, 1, -48, 2, 116, 109, 117, 120, 124, 86, 84, 32, 49, 48, 48, 47, 65, 78, 83, 73, 32, 88, 51, 46, 54, 52, 32, 118, 105, 114, 116, 117, 97, 108, 32, 116, 101, 114, 109, 105, 110, 97, 108, 32, 119, 105, 116, 104, 32, 50, 53, 54, 32, 99, 111, 108, 111, 114, 115, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 80, 0, 8, 0, 24, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, -1, 127, 0, 0, 4, 0, 6, 0, 8, 0, 25, 0, 30, 0, 37, 0, 41, 0, -1, -1, -1, -1, 45, 0, 62, 0, 64, 0, 68, 0, 75, 0, -1, -1, 77, 0, 89, 0, -1, -1, 93, 0, 96, 0, 102, 0, 106, 0, -1, -1, -1, -1, 110, 0, 112, 0, 117, 0, 122, 0, -1, -1, -1, -1, 123, 0, -1, -1, -1, -1, -128, 0, -123, 0, -118, 0, -1, -1, -113, 0, -111, 0, -106, 0, -1, -1, -105, 0, -100, 0, -94, 0, -88, 0, -1, -1, -1, -1, -1, -1, -85, 0, -1, -1, -1, -1, -1, -1, -81, 0, -1, -1, -77, 0, -1, -1, -1, -1, -1, -1, -75, 0, -1, -1, -70, 0, -1, -1, -1, -1, -1, -1, -1, -1, -66, 0, -62, 0, -56, 0, -52, 0, -48, 0, -44, 0, -38, 0, -32, 0, -26, 0, -20, 0, -14, 0, -9, 0, -1, -1, -4, 0, -1, -1, 0, 1, 5, 1, 10, 1, -1, -1, -1, -1, -1, -1, 14, 1, 18, 1, 26, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 34, 1, -1, -1, 37, 1, 46, 1, 55, 1, 64, 1, -1, -1, 73, 1, 82, 1, 91, 1, -1, -1, 100, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 109, 1, -1, -1, -1, -1, 126, 1, -1, -1, -127, 1, -124, 1, -122, 1, -119, 1, -46, 1, -1, -1, -43, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -41, 1, -1, -1, 24, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 28, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 35, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 40, 2, 46, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 52, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 57, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 66, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 71, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 77, 2, -1, -1, -1, -1, -1, -1, 81, 2, -112, 2, 27, 91, 90, 0, 7, 0, 13, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 114, 0, 27, 91, 51, 103, 0, 27, 91, 72, 27, 91, 74, 0, 27, 91, 75, 0, 27, 91, 74, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 72, 0, 10, 0, 27, 91, 72, 0, 27, 91, 63, 50, 53, 108, 0, 8, 0, 27, 91, 51, 52, 104, 27, 91, 63, 50, 53, 104, 0, 27, 91, 67, 0, 27, 77, 0, 27, 91, 51, 52, 108, 0, 27, 91, 80, 0, 27, 91, 77, 0, 14, 0, 27, 91, 53, 109, 0, 27, 91, 49, 109, 0, 0, 27, 91, 52, 104, 0, 27, 91, 55, 109, 0, 27, 91, 55, 109, 0, 27, 91, 52, 109, 0, 15, 0, 27, 91, 109, 15, 0, 0, 27, 91, 52, 108, 0, 27, 91, 50, 55, 109, 0, 27, 91, 50, 52, 109, 0, 27, 103, 0, 27, 41, 48, 0, 27, 91, 76, 0, 8, 0, 27, 91, 51, 126, 0, 27, 79, 66, 0, 27, 79, 80, 0, 27, 91, 50, 49, 126, 0, 27, 79, 81, 0, 27, 79, 82, 0, 27, 79, 83, 0, 27, 91, 49, 53, 126, 0, 27, 91, 49, 55, 126, 0, 27, 91, 49, 56, 126, 0, 27, 91, 49, 57, 126, 0, 27, 91, 50, 48, 126, 0, 27, 91, 49, 126, 0, 27, 91, 50, 126, 0, 27, 79, 68, 0, 27, 91, 54, 126, 0, 27, 91, 53, 126, 0, 27, 79, 67, 0, 27, 79, 65, 0, 27, 91, 63, 49, 108, 27, 62, 0, 27, 91, 63, 49, 104, 27, 61, 0, 27, 69, 0, 27, 91, 37, 112, 49, 37, 100, 80, 0, 27, 91, 37, 112, 49, 37, 100, 77, 0, 27, 91, 37, 112, 49, 37, 100, 66, 0, 27, 91, 37, 112, 49, 37, 100, 64, 0, 27, 91, 37, 112, 49, 37, 100, 76, 0, 27, 91, 37, 112, 49, 37, 100, 68, 0, 27, 91, 37, 112, 49, 37, 100, 67, 0, 27, 91, 37, 112, 49, 37, 100, 65, 0, 27, 99, 27, 91, 63, 49, 48, 48, 48, 108, 27, 91, 63, 50, 53, 104, 0, 27, 56, 0, 27, 55, 0, 10, 0, 27, 77, 0, 27, 91, 48, 37, 63, 37, 112, 54, 37, 116, 59, 49, 37, 59, 37, 63, 37, 112, 49, 37, 116, 59, 55, 37, 59, 37, 63, 37, 112, 50, 37, 116, 59, 52, 37, 59, 37, 63, 37, 112, 51, 37, 116, 59, 55, 37, 59, 37, 63, 37, 112, 52, 37, 116, 59, 53, 37, 59, 109, 37, 63, 37, 112, 57, 37, 116, 14, 37, 101, 15, 37, 59, 0, 27, 72, 0, 9, 0, 43, 43, 44, 44, 45, 45, 46, 46, 48, 48, 96, 96, 97, 97, 102, 102, 103, 103, 104, 104, 105, 105, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 125, 126, 126, 0, 27, 91, 90, 0, 27, 40, 66, 27, 41, 48, 0, 27, 91, 52, 126, 0, 27, 91, 50, 51, 126, 0, 27, 91, 50, 52, 126, 0, 27, 91, 49, 75, 0, 27, 91, 51, 57, 59, 52, 57, 109, 0, 27, 91, 51, 109, 0, 27, 91, 50, 51, 109, 0, 27, 91, 77, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 51, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 57, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 51, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 52, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 49, 48, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 52, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0 +}; +// Taken from unibilium/t/static_screen-256color.c as of 2015-08-14. +// This is an 256-colour terminfo description that lacks +// status line capabilities that screen actually has. +static const signed char screen_256colour_terminfo[] = { + 26, 1, 43, 0, 43, 0, 15, 0, 105, 1, -43, 2, 115, 99, 114, 101, 101, 110, 45, 50, 53, 54, 99, 111, 108, 111, 114, 124, 71, 78, 85, 32, 83, 99, 114, 101, 101, 110, 32, 119, 105, 116, 104, 32, 50, 53, 54, 32, 99, 111, 108, 111, 114, 115, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 80, 0, 8, 0, 24, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, -1, 127, 0, 0, 4, 0, 6, 0, 8, 0, 25, 0, 30, 0, 37, 0, 41, 0, -1, -1, -1, -1, 45, 0, 62, 0, 64, 0, 68, 0, 75, 0, -1, -1, 77, 0, 89, 0, -1, -1, 93, 0, 96, 0, 102, 0, 106, 0, -1, -1, -1, -1, 110, 0, 112, 0, 117, 0, 122, 0, -1, -1, -1, -1, -125, 0, -1, -1, -1, -1, -120, 0, -115, 0, -110, 0, -1, -1, -105, 0, -103, 0, -98, 0, -1, -1, -89, 0, -84, 0, -78, 0, -72, 0, -1, -1, -1, -1, -1, -1, -69, 0, -1, -1, -1, -1, -1, -1, -65, 0, -1, -1, -61, 0, -1, -1, -1, -1, -1, -1, -59, 0, -1, -1, -54, 0, -1, -1, -1, -1, -1, -1, -1, -1, -50, 0, -46, 0, -40, 0, -36, 0, -32, 0, -28, 0, -22, 0, -16, 0, -10, 0, -4, 0, 2, 1, 7, 1, -1, -1, 12, 1, -1, -1, 16, 1, 21, 1, 26, 1, -1, -1, -1, -1, -1, -1, 30, 1, 34, 1, 42, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 50, 1, -1, -1, 53, 1, 62, 1, 71, 1, 80, 1, -1, -1, 89, 1, 98, 1, 107, 1, -1, -1, 116, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 125, 1, -1, -1, -1, -1, -114, 1, -1, -1, -111, 1, -108, 1, -106, 1, -103, 1, -30, 1, -1, -1, -27, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -25, 1, -1, -1, 40, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 44, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 51, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 56, 2, 62, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 68, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 73, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 82, 2, -1, -1, -1, -1, -1, -1, 86, 2, -107, 2, 27, 91, 90, 0, 7, 0, 13, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 114, 0, 27, 91, 51, 103, 0, 27, 91, 72, 27, 91, 74, 0, 27, 91, 75, 0, 27, 91, 74, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 72, 0, 10, 0, 27, 91, 72, 0, 27, 91, 63, 50, 53, 108, 0, 8, 0, 27, 91, 51, 52, 104, 27, 91, 63, 50, 53, 104, 0, 27, 91, 67, 0, 27, 77, 0, 27, 91, 51, 52, 108, 0, 27, 91, 80, 0, 27, 91, 77, 0, 14, 0, 27, 91, 53, 109, 0, 27, 91, 49, 109, 0, 27, 91, 63, 49, 48, 52, 57, 104, 0, 27, 91, 52, 104, 0, 27, 91, 55, 109, 0, 27, 91, 51, 109, 0, 27, 91, 52, 109, 0, 15, 0, 27, 91, 109, 15, 0, 27, 91, 63, 49, 48, 52, 57, 108, 0, 27, 91, 52, 108, 0, 27, 91, 50, 51, 109, 0, 27, 91, 50, 52, 109, 0, 27, 103, 0, 27, 41, 48, 0, 27, 91, 76, 0, 8, 0, 27, 91, 51, 126, 0, 27, 79, 66, 0, 27, 79, 80, 0, 27, 91, 50, 49, 126, 0, 27, 79, 81, 0, 27, 79, 82, 0, 27, 79, 83, 0, 27, 91, 49, 53, 126, 0, 27, 91, 49, 55, 126, 0, 27, 91, 49, 56, 126, 0, 27, 91, 49, 57, 126, 0, 27, 91, 50, 48, 126, 0, 27, 91, 49, 126, 0, 27, 91, 50, 126, 0, 27, 79, 68, 0, 27, 91, 54, 126, 0, 27, 91, 53, 126, 0, 27, 79, 67, 0, 27, 79, 65, 0, 27, 91, 63, 49, 108, 27, 62, 0, 27, 91, 63, 49, 104, 27, 61, 0, 27, 69, 0, 27, 91, 37, 112, 49, 37, 100, 80, 0, 27, 91, 37, 112, 49, 37, 100, 77, 0, 27, 91, 37, 112, 49, 37, 100, 66, 0, 27, 91, 37, 112, 49, 37, 100, 64, 0, 27, 91, 37, 112, 49, 37, 100, 76, 0, 27, 91, 37, 112, 49, 37, 100, 68, 0, 27, 91, 37, 112, 49, 37, 100, 67, 0, 27, 91, 37, 112, 49, 37, 100, 65, 0, 27, 99, 27, 91, 63, 49, 48, 48, 48, 108, 27, 91, 63, 50, 53, 104, 0, 27, 56, 0, 27, 55, 0, 10, 0, 27, 77, 0, 27, 91, 48, 37, 63, 37, 112, 54, 37, 116, 59, 49, 37, 59, 37, 63, 37, 112, 49, 37, 116, 59, 51, 37, 59, 37, 63, 37, 112, 50, 37, 116, 59, 52, 37, 59, 37, 63, 37, 112, 51, 37, 116, 59, 55, 37, 59, 37, 63, 37, 112, 52, 37, 116, 59, 53, 37, 59, 109, 37, 63, 37, 112, 57, 37, 116, 14, 37, 101, 15, 37, 59, 0, 27, 72, 0, 9, 0, 43, 43, 44, 44, 45, 45, 46, 46, 48, 48, 96, 96, 97, 97, 102, 102, 103, 103, 104, 104, 105, 105, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 125, 126, 126, 0, 27, 91, 90, 0, 27, 40, 66, 27, 41, 48, 0, 27, 91, 52, 126, 0, 27, 91, 50, 51, 126, 0, 27, 91, 50, 52, 126, 0, 27, 91, 49, 75, 0, 27, 91, 51, 57, 59, 52, 57, 109, 0, 27, 91, 77, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 51, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 57, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 51, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 52, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 49, 48, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 52, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0, 0, 3, 0, 1, 0, 24, 0, 52, 0, -112, 0, 1, 1, 0, 0, 1, 0, 0, 0, 4, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 3, 0, 6, 0, 9, 0, 12, 0, 15, 0, 18, 0, 23, 0, 28, 0, 32, 0, 37, 0, 43, 0, 49, 0, 55, 0, 61, 0, 66, 0, 71, 0, 77, 0, 83, 0, 89, 0, 95, 0, 101, 0, 107, 0, 111, 0, 116, 0, 120, 0, 124, 0, -128, 0, 27, 40, 66, 0, 27, 40, 37, 112, 49, 37, 99, 0, 65, 88, 0, 71, 48, 0, 88, 84, 0, 85, 56, 0, 69, 48, 0, 83, 48, 0, 107, 68, 67, 53, 0, 107, 68, 67, 54, 0, 107, 68, 78, 0, 107, 68, 78, 53, 0, 107, 69, 78, 68, 53, 0, 107, 69, 78, 68, 54, 0, 107, 72, 79, 77, 53, 0, 107, 72, 79, 77, 54, 0, 107, 73, 67, 53, 0, 107, 73, 67, 54, 0, 107, 76, 70, 84, 53, 0, 107, 78, 88, 84, 53, 0, 107, 78, 88, 84, 54, 0, 107, 80, 82, 86, 53, 0, 107, 80, 82, 86, 54, 0, 107, 82, 73, 84, 53, 0, 107, 85, 80, 0, 107, 85, 80, 53, 0, 107, 97, 50, 0, 107, 98, 49, 0, 107, 98, 51, 0, 107, 99, 50, 0 +}; +// Taken from Dickey ncurses terminfo.src dated 2017-04-22. +static const signed char iterm_256colour_terminfo[] = { + 26, 1, 57, 0, 29, 0, 15, 0, 105, 1, 73, 3, 105, 84, 101, 114, 109, 46, 97, 112, 112, 124, 105, 116, 101, 114, 109, 124, 105, 84, 101, 114, 109, 46, 97, 112, 112, 32, 116, 101, 114, 109, 105, 110, 97, 108, 32, 101, 109, 117, 108, 97, 116, 111, 114, 32, 102, 111, 114, 32, 77, 97, 99, 32, 79, 83, 32, 88, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 80, 0, 8, 0, 24, 0, -1, -1, -1, -1, -1, -1, -1, -1, 50, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, -1, 127, -1, -1, 0, 0, 2, 0, -2, -1, 4, 0, 9, 0, 16, 0, 20, 0, 24, 0, -1, -1, 35, 0, 52, 0, 54, 0, 58, 0, 65, 0, -1, -1, 67, 0, 74, 0, -1, -1, 78, 0, -1, -1, 82, 0, 86, 0, 90, 0, -1, -1, 96, 0, 98, 0, 103, 0, 108, 0, -1, -1, -2, -1, 117, 0, 122, 0, -1, -1, 127, 0,-124, 0,-119, 0, -1, -1,-114, 0,-112, 0,-107, 0, -1, -1, -94, 0, -89, 0, -85, 0, -81, 0, -1, -1, -63, 0, -1, -1, -1, -1, -1, -1, -1, -1, -61, 0, -57, 0, -1, -1, -53, 0, -1, -1, -1, -1, -1, -1, -51, 0, -1, -1, -46, 0, -1, -1, -1, -1, -1, -1, -1, -1, -42, 0, -38, 0, -32, 0, -28, 0, -24, 0, -20, 0, -14, 0, -8, 0, -2, 0, 4, 1, 10, 1, -1, -1, -1, -1, 14, 1, -1, -1, 18, 1, 23, 1, 28, 1, -1, -1, -1, -1, -1, -1, 32, 1, 36, 1, 44, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 52, 1, 61, 1, 70, 1, 79, 1, -1, -1, 88, 1, 97, 1, 106, 1, -1, -1, 115, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 1, -1, -1, -1, -1,-104, 1,-101, 1, -90, 1, -87, 1, -85, 1, -82, 1, -4, 1, -1, -1, -1, 1, 1, 2, -1, -1, -1, -1, -1, -1, 6, 2, 10, 2, 14, 2, 18, 2, 22, 2, -1, -1, -1, -1, 26, 2, -1, -1, -1, -1, -1, -1, -1, -1, 77, 2, 83, 2, -1, -1, -1, -1, 89, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 96, 2, 100, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 104, 2, 110, 2, 116, 2, 122, 2,-128, 2,-122, 2,-116, 2,-110, 2, 104, 2, -98, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -92, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -87, 2, -76, 2, -71, 2, -63, 2, -59, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -54, 2, 9, 3, 7, 0, 13, 0, 27, 91, 51, 103, 0, 27, 91, 72, 27, 91, 74, 0, 27, 91, 75, 0, 27, 91, 74, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 71, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 72, 0, 10, 0, 27, 91, 72, 0, 27, 91, 63, 50, 53, 108, 0, 8, 0, 27, 91, 63, 50, 53, 104, 0, 27, 91, 67, 0, 27, 91, 65, 0, 27, 91, 80, 0, 27, 91, 77, 0, 27, 93, 50, 59, 7, 0, 14, 0, 27, 91, 53, 109, 0, 27, 91, 49, 109, 0, 27, 55, 27, 91, 63, 52, 55, 104, 0, 27, 91, 52, 104, 0, 27, 91, 56, 109, 0, 27, 91, 55, 109, 0, 27, 91, 55, 109, 0, 27, 91, 52, 109, 0, 15, 0, 27, 91, 109, 15, 0, 27, 91, 50, 74, 27, 91, 63, 52, 55, 108, 27, 56, 0, 27, 91, 52, 108, 0, 27, 91, 109, 0, 27, 91, 109, 0, 27, 91, 63, 53, 104, 36, 60, 50, 48, 48, 47, 62, 27, 91, 63, 53, 108, 0, 7, 0, 27, 91, 64, 0, 27, 91, 76, 0, 127, 0, 27, 91, 51, 126, 0, 27, 79, 66, 0, 27, 79, 80, 0, 27, 91, 50, 49, 126, 0, 27, 79, 81, 0, 27, 79, 82, 0, 27, 79, 83, 0, 27, 91, 49, 53, 126, 0, 27, 91, 49, 55, 126, 0, 27, 91, 49, 56, 126, 0, 27, 91, 49, 57, 126, 0, 27, 91, 50, 48, 126, 0, 27, 79, 72, 0, 27, 79, 68, 0, 27, 91, 54, 126, 0, 27, 91, 53, 126, 0, 27, 79, 67, 0, 27, 79, 65, 0, 27, 91, 63, 49, 108, 27, 62, 0, 27, 91, 63, 49, 104, 27, 61, 0, 27, 91, 37, 112, 49, 37, 100, 80, 0, 27, 91, 37, 112, 49, 37, 100, 77, 0, 27, 91, 37, 112, 49, 37, 100, 66, 0, 27, 91, 37, 112, 49, 37, 100, 64, 0, 27, 91, 37, 112, 49, 37, 100, 76, 0, 27, 91, 37, 112, 49, 37, 100, 68, 0, 27, 91, 37, 112, 49, 37, 100, 67, 0, 27, 91, 37, 112, 49, 37, 100, 65, 0, 27, 62, 27, 91, 63, 51, 108, 27, 91, 63, 52, 108, 27, 91, 63, 53, 108, 27, 91, 63, 55, 104, 27, 91, 63, 56, 104, 0, 27, 56, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 100, 0, 27, 55, 0, 10, 0, 27, 77, 0, 27, 91, 48, 37, 63, 37, 112, 54, 37, 116, 59, 49, 37, 59, 37, 63, 37, 112, 50, 37, 116, 59, 52, 37, 59, 37, 63, 37, 112, 49, 37, 112, 51, 37, 124, 37, 116, 59, 55, 37, 59, 37, 63, 37, 112, 52, 37, 116, 59, 53, 37, 59, 37, 63, 37, 112, 55, 37, 116, 59, 56, 37, 59, 109, 37, 63, 37, 112, 57, 37, 116, 14, 37, 101, 15, 37, 59, 0, 27, 72, 0, 9, 0, 27, 93, 50, 59, 0, 27, 79, 113, 0, 27, 79, 115, 0, 27, 79, 114, 0, 27, 79, 112, 0, 27, 79, 110, 0, 96, 96, 97, 97, 102, 102, 103, 103, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 125, 126, 126, 0, 27, 91, 63, 55, 104, 0, 27, 91, 63, 55, 108, 0, 27, 40, 66, 27, 41, 48, 0, 27, 79, 70, 0, 27, 79, 77, 0, 27, 91, 50, 51, 126, 0, 27, 91, 50, 52, 126, 0, 27, 91, 50, 53, 126, 0, 27, 91, 50, 54, 126, 0, 27, 91, 50, 56, 126, 0, 27, 91, 50, 57, 126, 0, 27, 91, 51, 49, 126, 0, 27, 91, 50, 50, 126, 0, 27, 91, 51, 51, 126, 0, 27, 91, 51, 52, 126, 0, 27, 91, 49, 75, 0, 27, 91, 37, 105, 37, 100, 59, 37, 100, 82, 0, 27, 91, 54, 110, 0, 27, 91, 63, 49, 59, 50, 99, 0, 27, 91, 99, 0, 27, 91, 48, 109, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 51, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 57, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 51, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 52, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 49, 48, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 52, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0 +}; +// Taken from Dickey ncurses terminfo.src dated 2017-04-22. +// This is a 256-colour terminfo description that lacks true-colour +// capabilities that rxvt actually has. +static const signed char rxvt_256colour_terminfo[] = { + 26, 1, 47, 0, 29, 0, 15, 0, 110, 1, -31, 4, 114, 120, 118, 116, 45, 50, 53, 54, 99, 111, 108, 111, 114, 124, 114, 120, 118, 116, 32, 50, 46, 55, 46, 57, 32, 119, 105, 116, 104, 32, 120, 116, 101, 114, 109, 32, 50, 53, 54, 45, 99, 111, 108, 111, 114, 115, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 80, 0, 8, 0, 24, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, -1, 127, -1, -1, 0, 0, 2, 0, 4, 0, 21, 0, 26, 0, 34, 0, 38, 0, 42, 0, -1, -1, 53, 0, 70, 0, 72, 0, 76, 0, 83, 0, -1, -1, 85, 0, 92, 0, -1, -1, 96, 0, -1, -1, -1, -1, 100, 0, -1, -1, -1, -1, 104, 0, 106, 0, 111, 0, 116, 0, -1, -1, -1, -1, 125, 0, -1, -1, -1, -1,-126, 0,-121, 0,-116, 0, -1, -1,-111, 0,-109, 0,-104, 0, -1, -1, -91, 0, -86, 0, -80, 0, -74, 0, -1, -1, -1, -1, -56, 0, -42, 0, -1, -1, -1, -1, -8, 0, -4, 0, -1, -1, 0, 1, -1, -1, -1, -1, -1, -1, 2, 1, -1, -1, 7, 1, -1, -1, 11, 1, -1, -1, 16, 1, 22, 1, 28, 1, 34, 1, 40, 1, 46, 1, 52, 1, 58, 1, 64, 1, 70, 1, 76, 1, 82, 1, 87, 1, -1, -1, 92, 1, -1, -1, 96, 1, 101, 1, 106, 1, 110, 1, 114, 1, -1, -1, 118, 1, 122, 1, 125, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-128, 1,-119, 1,-110, 1, -1, -1,-101, 1, -92, 1, -83, 1, -1, -1, -74, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -65, 1, -32, 1, -1, -1, -1, -1, 18, 2, 21, 2, 32, 2, 35, 2, 37, 2, 40, 2, 107, 2, -1, -1, 110, 2, -1, -1, -1, -1, -1, -1, -1, -1, 112, 2, 116, 2, 120, 2, 124, 2,-128, 2, -1, -1, -1, -1,-124, 2, -1, -1, -73, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -69, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -62, 2, -57, 2, -1, -1, -53, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -48, 2, -1, -1, -43, 2, -38, 2, -1, -1, -1, -1, -1, -1, -1, -1, -33, 2, -28, 2, -23, 2, -1, -1, -1, -1, -19, 2, -1, -1, -14, 2, -1, -1, -1, -1, -1, -1, -9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -5, 2, 1, 3, 7, 3, 13, 3, 19, 3, 25, 3, 31, 3, 37, 3, 43, 3, 49, 3, 55, 3, 61, 3, 67, 3, 73, 3, 79, 3, 85, 3, 91, 3, 97, 3, 103, 3, 109, 3, 115, 3, 121, 3, 127, 3,-123, 3,-117, 3,-111, 3,-105, 3, -99, 3, -93, 3, -87, 3, -81, 3, -75, 3, -69, 3, -63, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -57, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -52, 3, -41, 3, -36, 3, -28, 3, -24, 3, -15, 3, -8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 86, 4, -1, -1, -1, -1, -1, -1, 90, 4,-103, 4, -1, -1, -1, -1, -1, -1, -39, 4, -35, 4, 7, 0, 13, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 114, 0, 27, 91, 51, 103, 0, 27, 91, 72, 27, 91, 50, 74, 0, 27, 91, 75, 0, 27, 91, 74, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 71, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 72, 0, 10, 0, 27, 91, 72, 0, 27, 91, 63, 50, 53, 108, 0, 8, 0, 27, 91, 63, 50, 53, 104, 0, 27, 91, 67, 0, 27, 91, 65, 0, 27, 91, 77, 0, 14, 0, 27, 91, 53, 109, 0, 27, 91, 49, 109, 0, 27, 55, 27, 91, 63, 52, 55, 104, 0, 27, 91, 52, 104, 0, 27, 91, 55, 109, 0, 27, 91, 55, 109, 0, 27, 91, 52, 109, 0, 15, 0, 27, 91, 109, 15, 0, 27, 91, 50, 74, 27, 91, 63, 52, 55, 108, 27, 56, 0, 27, 91, 52, 108, 0, 27, 91, 50, 55, 109, 0, 27, 91, 50, 52, 109, 0, 27, 91, 63, 53, 104, 36, 60, 49, 48, 48, 47, 62, 27, 91, 63, 53, 108, 0, 27, 91, 63, 52, 55, 108, 27, 61, 27, 91, 63, 49, 108, 0, 27, 91, 114, 27, 91, 109, 27, 91, 50, 74, 27, 91, 72, 27, 91, 63, 55, 104, 27, 91, 63, 49, 59, 51, 59, 52, 59, 54, 108, 27, 91, 52, 108, 0, 27, 91, 64, 0, 27, 91, 76, 0, 8, 0, 27, 91, 51, 126, 0, 27, 91, 66, 0, 27, 91, 56, 94, 0, 27, 91, 50, 49, 126, 0, 27, 91, 49, 49, 126, 0, 27, 91, 50, 49, 126, 0, 27, 91, 49, 50, 126, 0, 27, 91, 49, 51, 126, 0, 27, 91, 49, 52, 126, 0, 27, 91, 49, 53, 126, 0, 27, 91, 49, 55, 126, 0, 27, 91, 49, 56, 126, 0, 27, 91, 49, 57, 126, 0, 27, 91, 50, 48, 126, 0, 27, 91, 55, 126, 0, 27, 91, 50, 126, 0, 27, 91, 68, 0, 27, 91, 54, 126, 0, 27, 91, 53, 126, 0, 27, 91, 67, 0, 27, 91, 97, 0, 27, 91, 98, 0, 27, 91, 65, 0, 27, 62, 0, 27, 61, 0, 27, 91, 37, 112, 49, 37, 100, 77, 0, 27, 91, 37, 112, 49, 37, 100, 66, 0, 27, 91, 37, 112, 49, 37, 100, 64, 0, 27, 91, 37, 112, 49, 37, 100, 76, 0, 27, 91, 37, 112, 49, 37, 100, 68, 0, 27, 91, 37, 112, 49, 37, 100, 67, 0, 27, 91, 37, 112, 49, 37, 100, 65, 0, 27, 62, 27, 91, 49, 59, 51, 59, 52, 59, 53, 59, 54, 108, 27, 91, 63, 55, 104, 27, 91, 109, 27, 91, 114, 27, 91, 50, 74, 27, 91, 72, 0, 27, 91, 114, 27, 91, 109, 27, 91, 50, 74, 27, 91, 72, 27, 91, 63, 55, 104, 27, 91, 63, 49, 59, 51, 59, 52, 59, 54, 108, 27, 91, 52, 108, 27, 62, 27, 91, 63, 49, 48, 48, 48, 108, 27, 91, 63, 50, 53, 104, 0, 27, 56, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 100, 0, 27, 55, 0, 10, 0, 27, 77, 0, 27, 91, 48, 37, 63, 37, 112, 54, 37, 116, 59, 49, 37, 59, 37, 63, 37, 112, 50, 37, 116, 59, 52, 37, 59, 37, 63, 37, 112, 49, 37, 112, 51, 37, 124, 37, 116, 59, 55, 37, 59, 37, 63, 37, 112, 52, 37, 116, 59, 53, 37, 59, 109, 37, 63, 37, 112, 57, 37, 116, 14, 37, 101, 15, 37, 59, 0, 27, 72, 0, 9, 0, 27, 79, 119, 0, 27, 79, 121, 0, 27, 79, 117, 0, 27, 79, 113, 0, 27, 79, 115, 0, 96, 96, 97, 97, 102, 102, 103, 103, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 125, 126, 126, 0, 27, 91, 90, 0, 27, 40, 66, 27, 41, 48, 0, 27, 91, 56, 126, 0, 27, 79, 77, 0, 27, 91, 49, 126, 0, 27, 91, 51, 36, 0, 27, 91, 52, 126, 0, 27, 91, 56, 36, 0, 27, 91, 55, 36, 0, 27, 91, 50, 36, 0, 27, 91, 100, 0, 27, 91, 54, 36, 0, 27, 91, 53, 36, 0, 27, 91, 99, 0, 27, 91, 50, 51, 126, 0, 27, 91, 50, 52, 126, 0, 27, 91, 50, 53, 126, 0, 27, 91, 50, 54, 126, 0, 27, 91, 50, 56, 126, 0, 27, 91, 50, 57, 126, 0, 27, 91, 51, 49, 126, 0, 27, 91, 51, 50, 126, 0, 27, 91, 51, 51, 126, 0, 27, 91, 51, 52, 126, 0, 27, 91, 50, 51, 36, 0, 27, 91, 50, 52, 36, 0, 27, 91, 49, 49, 94, 0, 27, 91, 49, 50, 94, 0, 27, 91, 49, 51, 94, 0, 27, 91, 49, 52, 94, 0, 27, 91, 49, 53, 94, 0, 27, 91, 49, 55, 94, 0, 27, 91, 49, 56, 94, 0, 27, 91, 49, 57, 94, 0, 27, 91, 50, 48, 94, 0, 27, 91, 50, 49, 94, 0, 27, 91, 50, 51, 94, 0, 27, 91, 50, 52, 94, 0, 27, 91, 50, 53, 94, 0, 27, 91, 50, 54, 94, 0, 27, 91, 50, 56, 94, 0, 27, 91, 50, 57, 94, 0, 27, 91, 51, 49, 94, 0, 27, 91, 51, 50, 94, 0, 27, 91, 51, 51, 94, 0, 27, 91, 51, 52, 94, 0, 27, 91, 50, 51, 64, 0, 27, 91, 50, 52, 64, 0, 27, 91, 49, 75, 0, 27, 91, 37, 105, 37, 100, 59, 37, 100, 82, 0, 27, 91, 54, 110, 0, 27, 91, 63, 49, 59, 50, 99, 0, 27, 91, 99, 0, 27, 91, 51, 57, 59, 52, 57, 109, 0, 27, 93, 49, 48, 52, 7, 0, 27, 93, 52, 59, 37, 112, 49, 37, 100, 59, 114, 103, 98, 58, 37, 112, 50, 37, 123, 50, 53, 53, 125, 37, 42, 37, 123, 49, 48, 48, 48, 125, 37, 47, 37, 50, 46, 50, 88, 47, 37, 112, 51, 37, 123, 50, 53, 53, 125, 37, 42, 37, 123, 49, 48, 48, 48, 125, 37, 47, 37, 50, 46, 50, 88, 47, 37, 112, 52, 37, 123, 50, 53, 53, 125, 37, 42, 37, 123, 49, 48, 48, 48, 125, 37, 47, 37, 50, 46, 50, 88, 27, 92, 0, 27, 91, 77, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 51, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 57, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 51, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 52, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 49, 48, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 52, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0, 27, 40, 66, 0, 27, 40, 48, 0 +}; +// Taken from Dickey ncurses terminfo.src dated 2017-04-22. +// This is a 16-colour terminfo description that lacks true-colour +// and 256-colour capabilities that linux (4.8+) actually has. +static const signed char linux_16colour_terminfo[] = { + 26, 1, 43, 0, 29, 0, 16, 0, 125, 1, 125, 3, 108, 105, 110, 117, 120, 45, 49, 54, 99, 111, 108, 111, 114, 124, 108, 105, 110, 117, 120, 32, 99, 111, 110, 115, 111, 108, 101, 32, 119, 105, 116, 104, 32, 49, 54, 32, 99, 111, 108, 111, 114, 115, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, -1, -1, 8, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 16, 0, 0, 1, 42, 0, -1, -1, 0, 0, 2, 0, 4, 0, 21, 0, 26, 0, 33, 0, 37, 0, 41, 0, -1, -1, 52, 0, 69, 0, 71, 0, 75, 0, 87, 0, -1, -1, 89, 0, 101, 0, -1, -1, 105, 0, 109, 0, 121, 0, 125, 0, -1, -1, -1, -1,-127, 0,-125, 0,-120, 0, -1, -1, -1, -1,-115, 0,-110, 0, -1, -1, -1, -1,-105, 0,-100, 0, -95, 0, -90, 0, -81, 0, -79, 0, -1, -1, -1, -1, -74, 0, -69, 0, -63, 0, -57, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -39, 0, -35, 0, -1, -1, -31, 0, -1, -1, -1, -1, -1, -1, -29, 0, -1, -1, -24, 0, -1, -1, -1, -1, -1, -1, -1, -1, -20, 0, -15, 0, -9, 0, -4, 0, 1, 1, 6, 1, 11, 1, 17, 1, 23, 1, 29, 1, 35, 1, 40, 1, -1, -1, 45, 1, -1, -1, 49, 1, 54, 1, 59, 1, -1, -1, -1, -1, -1, -1, 63, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 67, 1, -1, -1, 70, 1, 79, 1, 88, 1, 97, 1, -1, -1, 106, 1, 115, 1, 124, 1, -1, -1,-123, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-114, 1, -1, -1, -1, -1, -1, -1,-108, 1,-105, 1, -94, 1, -91, 1, -89, 1, -86, 1, 1, 2, -1, -1, 4, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, 10, 2, -1, -1, 77, 2, -1, -1, -1, -1, 81, 2, 87, 2, -1, -1, -1, -1, 93, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 97, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 102, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 104, 2, 110, 2, 116, 2, 122, 2,-128, 2,-122, 2,-116, 2,-110, 2,-104, 2, -98, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -92, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -87, 2, -76, 2, -71, 2, -65, 2, -61, 2, -52, 2, -48, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 33, 3, -1, -1, -1, -1, -1, -1, 37, 3, 75, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 113, 3, 119, 3, 7, 0, 13, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 114, 0, 27, 91, 51, 103, 0, 27, 91, 72, 27, 91, 74, 0, 27, 91, 75, 0, 27, 91, 74, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 71, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 72, 0, 10, 0, 27, 91, 72, 0, 27, 91, 63, 50, 53, 108, 27, 91, 63, 49, 99, 0, 8, 0, 27, 91, 63, 50, 53, 104, 27, 91, 63, 48, 99, 0, 27, 91, 67, 0, 27, 91, 65, 0, 27, 91, 63, 50, 53, 104, 27, 91, 63, 56, 99, 0, 27, 91, 80, 0, 27, 91, 77, 0, 14, 0, 27, 91, 53, 109, 0, 27, 91, 49, 109, 0, 27, 91, 50, 109, 0, 27, 91, 52, 104, 0, 27, 91, 55, 109, 0, 27, 91, 55, 109, 0, 27, 91, 52, 109, 0, 27, 91, 37, 112, 49, 37, 100, 88, 0, 15, 0, 27, 91, 109, 15, 0, 27, 91, 52, 108, 0, 27, 91, 50, 55, 109, 0, 27, 91, 50, 52, 109, 0, 27, 91, 63, 53, 104, 36, 60, 50, 48, 48, 47, 62, 27, 91, 63, 53, 108, 0, 27, 91, 64, 0, 27, 91, 76, 0, 127, 0, 27, 91, 51, 126, 0, 27, 91, 66, 0, 27, 91, 91, 65, 0, 27, 91, 50, 49, 126, 0, 27, 91, 91, 66, 0, 27, 91, 91, 67, 0, 27, 91, 91, 68, 0, 27, 91, 91, 69, 0, 27, 91, 49, 55, 126, 0, 27, 91, 49, 56, 126, 0, 27, 91, 49, 57, 126, 0, 27, 91, 50, 48, 126, 0, 27, 91, 49, 126, 0, 27, 91, 50, 126, 0, 27, 91, 68, 0, 27, 91, 54, 126, 0, 27, 91, 53, 126, 0, 27, 91, 67, 0, 27, 91, 65, 0, 13, 10, 0, 27, 91, 37, 112, 49, 37, 100, 80, 0, 27, 91, 37, 112, 49, 37, 100, 77, 0, 27, 91, 37, 112, 49, 37, 100, 66, 0, 27, 91, 37, 112, 49, 37, 100, 64, 0, 27, 91, 37, 112, 49, 37, 100, 76, 0, 27, 91, 37, 112, 49, 37, 100, 68, 0, 27, 91, 37, 112, 49, 37, 100, 67, 0, 27, 91, 37, 112, 49, 37, 100, 65, 0, 27, 99, 27, 93, 82, 0, 27, 56, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 100, 0, 27, 55, 0, 10, 0, 27, 77, 0, 27, 91, 48, 59, 49, 48, 37, 63, 37, 112, 49, 37, 116, 59, 55, 37, 59, 37, 63, 37, 112, 50, 37, 116, 59, 52, 37, 59, 37, 63, 37, 112, 51, 37, 116, 59, 55, 37, 59, 37, 63, 37, 112, 52, 37, 116, 59, 53, 37, 59, 37, 63, 37, 112, 53, 37, 116, 59, 50, 37, 59, 37, 63, 37, 112, 54, 37, 116, 59, 49, 37, 59, 109, 37, 63, 37, 112, 57, 37, 116, 14, 37, 101, 15, 37, 59, 0, 27, 72, 0, 9, 0, 27, 91, 71, 0, 43, 43, 44, 44, 45, 45, 46, 46, 48, 48, 95, 95, 96, 96, 97, 97, 102, 102, 103, 103, 104, 104, 105, 105, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 99, 126, 126, 0, 27, 91, 90, 0, 27, 91, 63, 55, 104, 0, 27, 91, 63, 55, 108, 0, 27, 41, 48, 0, 27, 91, 52, 126, 0, 26, 0, 27, 91, 50, 51, 126, 0, 27, 91, 50, 52, 126, 0, 27, 91, 50, 53, 126, 0, 27, 91, 50, 54, 126, 0, 27, 91, 50, 56, 126, 0, 27, 91, 50, 57, 126, 0, 27, 91, 51, 49, 126, 0, 27, 91, 51, 50, 126, 0, 27, 91, 51, 51, 126, 0, 27, 91, 51, 52, 126, 0, 27, 91, 49, 75, 0, 27, 91, 37, 105, 37, 100, 59, 37, 100, 82, 0, 27, 91, 54, 110, 0, 27, 91, 63, 54, 99, 0, 27, 91, 99, 0, 27, 91, 51, 57, 59, 52, 57, 109, 0, 27, 93, 82, 0, 27, 93, 80, 37, 112, 49, 37, 120, 37, 112, 50, 37, 123, 50, 53, 53, 125, 37, 42, 37, 123, 49, 48, 48, 48, 125, 37, 47, 37, 48, 50, 120, 37, 112, 51, 37, 123, 50, 53, 53, 125, 37, 42, 37, 123, 49, 48, 48, 48, 125, 37, 47, 37, 48, 50, 120, 37, 112, 52, 37, 123, 50, 53, 53, 125, 37, 42, 37, 123, 49, 48, 48, 48, 125, 37, 47, 37, 48, 50, 120, 0, 27, 91, 77, 0, 27, 91, 51, 37, 112, 49, 37, 123, 56, 125, 37, 109, 37, 100, 37, 63, 37, 112, 49, 37, 123, 55, 125, 37, 62, 37, 116, 59, 49, 37, 101, 59, 50, 49, 37, 59, 109, 0, 27, 91, 52, 37, 112, 49, 37, 123, 56, 125, 37, 109, 37, 100, 37, 63, 37, 112, 49, 37, 123, 55, 125, 37, 62, 37, 116, 59, 53, 37, 101, 59, 50, 53, 37, 59, 109, 0, 27, 91, 49, 49, 109, 0, 27, 91, 49, 48, 109, 0 +}; +// Taken from Dickey ncurses terminfo.src dated 2017-04-22. +static const signed char putty_256colour_terminfo[] = { + 26, 1, 48, 0, 29, 0, 16, 0, 125, 1,-106, 4, 112, 117, 116, 116, 121, 45, 50, 53, 54, 99, 111, 108, 111, 114, 124, 80, 117, 84, 84, 89, 32, 48, 46, 53, 56, 32, 119, 105, 116, 104, 32, 120, 116, 101, 114, 109, 32, 50, 53, 54, 45, 99, 111, 108, 111, 114, 115, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, -1, -1, 8, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, -1, 127, 22, 0, 0, 0, 4, 0, 6, 0, 8, 0, 25, 0, 30, 0, 37, 0, 41, 0, 45, 0, -1, -1, 56, 0, 73, 0, 76, 0, 80, 0, 87, 0, -1, -1, 89, 0, 96, 0, -1, -1, 100, 0, -1, -1, 103, 0, 107, 0, 111, 0, -1, -1, 117, 0, 119, 0, 124, 0,-127, 0, -1, -1, -1, -1,-120, 0, -1, -1, -1, -1,-115, 0,-110, 0,-105, 0,-100, 0, -91, 0, -89, 0, -84, 0, -1, -1, -73, 0, -68, 0, -62, 0, -56, 0, -1, -1, -38, 0, -1, -1, -36, 0, -1, -1, -1, -1, -1, -1, -2, 0, -1, -1, 2, 1, -1, -1, -1, -1, -1, -1, 4, 1, -1, -1, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, 13, 1, 19, 1, 25, 1, 31, 1, 37, 1, 43, 1, 49, 1, 55, 1, 61, 1, 67, 1, 73, 1, 78, 1, -1, -1, 83, 1, -1, -1, 87, 1, 92, 1, 97, 1, 101, 1, 105, 1, -1, -1, 109, 1, 113, 1, 121, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-127, 1, -1, -1,-124, 1,-115, 1,-106, 1, -1, -1, -97, 1, -88, 1, -79, 1, -70, 1, -61, 1, -52, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -43, 1, -1, -1, -1, -1, -10, 1, -7, 1, 4, 2, 7, 2, 9, 2, 12, 2, 84, 2, -1, -1, 87, 2, 89, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 94, 2, -1, -1, -1, -1, -1, -1, -1, -1, 98, 2, -1, -1,-107, 2, -1, -1, -1, -1,-103, 2, -97, 2, -1, -1, -1, -1, -91, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -84, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -79, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -77, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -73, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -69, 2, -63, 2, -57, 2, -51, 2, -45, 2, -39, 2, -33, 2, -27, 2, -21, 2, -15, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -4, 2, 7, 3, 12, 3, 18, 3, 22, 3, 31, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 35, 3, -1, -1, -1, -1, -1, -1, 39, 3, 102, 3, -1, -1, -1, -1, -1, -1, -90, 3, -84, 3, -78, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -72, 3,-118, 4,-112, 4, 27, 91, 90, 0, 7, 0, 13, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 114, 0, 27, 91, 51, 103, 0, 27, 91, 72, 27, 91, 74, 0, 27, 91, 75, 0, 27, 91, 74, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 71, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 72, 0, 27, 68, 0, 27, 91, 72, 0, 27, 91, 63, 50, 53, 108, 0, 8, 0, 27, 91, 63, 50, 53, 104, 0, 27, 91, 67, 0, 27, 77, 0, 27, 91, 80, 0, 27, 91, 77, 0, 27, 93, 48, 59, 7, 0, 14, 0, 27, 91, 53, 109, 0, 27, 91, 49, 109, 0, 27, 91, 63, 52, 55, 104, 0, 27, 91, 52, 104, 0, 27, 91, 55, 109, 0, 27, 91, 55, 109, 0, 27, 91, 52, 109, 0, 27, 91, 37, 112, 49, 37, 100, 88, 0, 15, 0, 27, 91, 109, 15, 0, 27, 91, 50, 74, 27, 91, 63, 52, 55, 108, 0, 27, 91, 52, 108, 0, 27, 91, 50, 55, 109, 0, 27, 91, 50, 52, 109, 0, 27, 91, 63, 53, 104, 36, 60, 49, 48, 48, 47, 62, 27, 91, 63, 53, 108, 0, 7, 0, 27, 55, 27, 91, 114, 27, 91, 109, 27, 91, 63, 55, 104, 27, 91, 63, 49, 59, 52, 59, 54, 108, 27, 91, 52, 108, 27, 56, 27, 62, 27, 93, 82, 0, 27, 91, 76, 0, 127, 0, 27, 91, 51, 126, 0, 27, 79, 66, 0, 27, 91, 49, 49, 126, 0, 27, 91, 50, 49, 126, 0, 27, 91, 49, 50, 126, 0, 27, 91, 49, 51, 126, 0, 27, 91, 49, 52, 126, 0, 27, 91, 49, 53, 126, 0, 27, 91, 49, 55, 126, 0, 27, 91, 49, 56, 126, 0, 27, 91, 49, 57, 126, 0, 27, 91, 50, 48, 126, 0, 27, 91, 49, 126, 0, 27, 91, 50, 126, 0, 27, 79, 68, 0, 27, 91, 54, 126, 0, 27, 91, 53, 126, 0, 27, 79, 67, 0, 27, 91, 66, 0, 27, 91, 65, 0, 27, 79, 65, 0, 27, 91, 63, 49, 108, 27, 62, 0, 27, 91, 63, 49, 104, 27, 61, 0, 13, 10, 0, 27, 91, 37, 112, 49, 37, 100, 80, 0, 27, 91, 37, 112, 49, 37, 100, 77, 0, 27, 91, 37, 112, 49, 37, 100, 66, 0, 27, 91, 37, 112, 49, 37, 100, 83, 0, 27, 91, 37, 112, 49, 37, 100, 76, 0, 27, 91, 37, 112, 49, 37, 100, 68, 0, 27, 91, 37, 112, 49, 37, 100, 67, 0, 27, 91, 37, 112, 49, 37, 100, 84, 0, 27, 91, 37, 112, 49, 37, 100, 65, 0, 27, 60, 27, 91, 34, 112, 27, 91, 53, 48, 59, 54, 34, 112, 27, 99, 27, 91, 63, 51, 108, 27, 93, 82, 27, 91, 63, 49, 48, 48, 48, 108, 0, 27, 56, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 100, 0, 27, 55, 0, 10, 0, 27, 77, 0, 27, 91, 48, 37, 63, 37, 112, 49, 37, 112, 54, 37, 124, 37, 116, 59, 49, 37, 59, 37, 63, 37, 112, 50, 37, 116, 59, 52, 37, 59, 37, 63, 37, 112, 49, 37, 112, 51, 37, 124, 37, 116, 59, 55, 37, 59, 37, 63, 37, 112, 52, 37, 116, 59, 53, 37, 59, 109, 37, 63, 37, 112, 57, 37, 116, 14, 37, 101, 15, 37, 59, 0, 27, 72, 0, 9, 0, 27, 93, 48, 59, 0, 27, 91, 71, 0, 96, 96, 97, 97, 102, 102, 103, 103, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 125, 126, 126, 0, 27, 91, 90, 0, 27, 91, 63, 55, 104, 0, 27, 91, 63, 55, 108, 0, 27, 40, 66, 27, 41, 48, 0, 27, 91, 52, 126, 0, 26, 0, 27, 91, 68, 0, 27, 91, 67, 0, 27, 91, 50, 51, 126, 0, 27, 91, 50, 52, 126, 0, 27, 91, 50, 53, 126, 0, 27, 91, 50, 54, 126, 0, 27, 91, 50, 56, 126, 0, 27, 91, 50, 57, 126, 0, 27, 91, 51, 49, 126, 0, 27, 91, 51, 50, 126, 0, 27, 91, 51, 51, 126, 0, 27, 91, 51, 52, 126, 0, 27, 91, 49, 75, 0, 27, 91, 37, 105, 37, 100, 59, 37, 100, 82, 0, 27, 91, 54, 110, 0, 27, 91, 63, 54, 99, 0, 27, 91, 99, 0, 27, 91, 51, 57, 59, 52, 57, 109, 0, 27, 93, 82, 0, 27, 91, 77, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 51, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 57, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 51, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 52, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 49, 48, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 52, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0, 27, 91, 49, 48, 109, 0, 27, 91, 49, 49, 109, 0, 27, 91, 49, 50, 109, 0, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 61, 37, 116, 27, 37, 37, 71, -30,-105,-104, 27, 37, 37, 64, 37, 101, 37, 112, 49, 37, 123, 49, 48, 125, 37, 61, 37, 116, 27, 37, 37, 71, -30,-105,-103, 27, 37, 37, 64, 37, 101, 37, 112, 49, 37, 123, 49, 50, 125, 37, 61, 37, 116, 27, 37, 37, 71, -30,-103,-128, 27, 37, 37, 64, 37, 101, 37, 112, 49, 37, 123, 49, 51, 125, 37, 61, 37, 116, 27, 37, 37, 71, -30,-103, -86, 27, 37, 37, 64, 37, 101, 37, 112, 49, 37, 123, 49, 52, 125, 37, 61, 37, 116, 27, 37, 37, 71, -30,-103, -85, 27, 37, 37, 64, 37, 101, 37, 112, 49, 37, 123, 49, 53, 125, 37, 61, 37, 116, 27, 37, 37, 71, -30,-104, -68, 27, 37, 37, 64, 37, 101, 37, 112, 49, 37, 123, 50, 55, 125, 37, 61, 37, 116, 27, 37, 37, 71, -30,-122,-112, 27, 37, 37, 64, 37, 101, 37, 112, 49, 37, 123, 49, 53, 53, 125, 37, 61, 37, 116, 27, 37, 37, 71, -32,-126, -94, 27, 37, 37, 64, 37, 101, 37, 112, 49, 37, 99, 37, 59, 0, 27, 91, 49, 49, 109, 0, 27, 91, 49, 48, 109, 0 +}; +// Taken from Dickey ncurses terminfo.src dated 2017-04-22. +static const signed char interix_8colour_terminfo[] = { + 26, 1, 82, 0, 15, 0, 16, 0, 105, 1, 123, 2, 105, 110, 116, 101, 114, 105, 120, 124, 111, 112, 101, 110, 110, 116, 124, 111, 112, 101, 110, 110, 116, 45, 50, 53, 124, 110, 116, 99, 111, 110, 115, 111, 108, 101, 124, 110, 116, 99, 111, 110, 115, 111, 108, 101, 45, 50, 53, 124, 79, 112, 101, 110, 78, 84, 45, 116, 101, 114, 109, 32, 99, 111, 109, 112, 97, 116, 105, 98, 108, 101, 32, 119, 105, 116, 104, 32, 99, 111, 108, 111, 114, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 80, 0, -1, -1, 25, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 8, 0, 64, 0, 3, 0, 0, 0, 4, 0, -1, -1, -1, -1, -1, -1, 6, 0, 11, 0, 15, 0, -1, -1, -1, -1, 19, 0, 36, 0, 38, 0, -1, -1, 42, 0, -1, -1, -1, -1, 46, 0, 50, 0, 54, 0, -1, -1, -1, -1, 58, 0, -1, -1, -1, -1, -1, -1, -1, -1, 62, 0, 67, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 75, 0, 80, 0, 85, 0, -1, -1, -1, -1, 90, 0, 95, 0, -1, -1, -1, -1, 107, 0, 111, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 115, 0, -1, -1, 119, 0, -1, -1, -1, -1, -1, -1, 121, 0, -1, -1, 125, 0, -1, -1, -1, -1, -1, -1,-127, 0,-123, 0,-119, 0,-115, 0,-111, 0,-107, 0,-103, 0, -99, 0, -95, 0, -91, 0, -87, 0, -1, -1, -83, 0, -1, -1, -79, 0, -75, 0, -71, 0, -67, 0, -63, 0, -1, -1, -1, -1, -1, -1, -59, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -55, 0, -1, -1, -1, -1, -52, 0, -43, 0, -1, -1, -34, 0, -25, 0, -16, 0, -7, 0, 2, 1, 11, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 20, 1, -1, -1, -1, -1, -1, -1, 23, 1, -1, -1, 27, 1, 31, 1, 35, 1, -1, -1, -1, -1, -1, -1, 39, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 41, 1, -1, -1, 104, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 108, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 112, 1, 116, 1, 120, 1, 124, 1,-128, 1,-124, 1,-120, 1,-116, 1,-112, 1,-108, 1,-104, 1,-100, 1, -96, 1, -92, 1, -88, 1, -84, 1, -80, 1, -76, 1, -72, 1, -68, 1, -64, 1, -60, 1, -56, 1, -52, 1, -48, 1, -44, 1, -40, 1, -36, 1, -32, 1, -28, 1, -24, 1, -20, 1, -16, 1, -12, 1, -8, 1, -4, 1, 0, 2, 4, 2, 8, 2, 12, 2, 16, 2, 20, 2, 24, 2, 28, 2, 32, 2, 36, 2, 40, 2, 44, 2, 48, 2, 52, 2, 56, 2, 60, 2, 64, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 68, 2, -1, -1, -1, -1, -1, -1, -1, -1, 72, 2, 88, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 103, 2, 113, 2, 27, 91, 90, 0, 7, 0, 27, 91, 50, 74, 0, 27, 91, 75, 0, 27, 91, 74, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 72, 0, 10, 0, 27, 91, 72, 0, 27, 91, 68, 0, 27, 91, 67, 0, 27, 91, 85, 0, 27, 91, 65, 0, 27, 91, 77, 0, 27, 91, 49, 109, 0, 27, 91, 115, 27, 91, 49, 98, 0, 27, 91, 55, 109, 0, 27, 91, 55, 109, 0, 27, 91, 52, 109, 0, 27, 91, 48, 109, 0, 27, 91, 50, 98, 27, 91, 117, 13, 27, 91, 75, 0, 27, 91, 109, 0, 27, 91, 109, 0, 27, 91, 76, 0, 8, 0, 27, 91, 77, 0, 27, 91, 66, 0, 27, 70, 65, 0, 27, 70, 49, 0, 27, 70, 65, 0, 27, 70, 50, 0, 27, 70, 51, 0, 27, 70, 52, 0, 27, 70, 53, 0, 27, 70, 54, 0, 27, 70, 55, 0, 27, 70, 56, 0, 27, 70, 57, 0, 27, 91, 76, 0, 27, 91, 68, 0, 27, 91, 85, 0, 27, 91, 84, 0, 27, 91, 83, 0, 27, 91, 67, 0, 27, 91, 65, 0, 13, 10, 0, 27, 91, 37, 112, 49, 37, 100, 77, 0, 27, 91, 37, 112, 49, 37, 100, 66, 0, 27, 91, 37, 112, 49, 37, 100, 83, 0, 27, 91, 37, 112, 49, 37, 100, 76, 0, 27, 91, 37, 112, 49, 37, 100, 68, 0, 27, 91, 37, 112, 49, 37, 100, 67, 0, 27, 91, 37, 112, 49, 37, 100, 84, 0, 27, 91, 37, 112, 49, 37, 100, 65, 0, 27, 99, 0, 27, 91, 117, 0, 27, 91, 115, 0, 27, 91, 83, 0, 27, 91, 84, 0, 9, 0, 43, 16, 44, 17, 45, 24, 46, 25, 48, -37, 96, 4, 97, -79, 102, -8, 103, -15, 104, -80, 106, -39, 107, -65, 108, -38, 109, -64, 110, -59, 111, 126, 112, -60, 113, -60, 114, -60, 115, 95, 116, -61, 117, -76, 118, -63, 119, -62, 120, -77, 121, -13, 122, -14, 123, -29, 124, -40, 125,-100, 126, -2, 0, 27, 91, 90, 0, 27, 91, 85, 0, 27, 70, 66, 0, 27, 70, 67, 0, 27, 70, 68, 0, 27, 70, 69, 0, 27, 70, 70, 0, 27, 70, 71, 0, 27, 70, 72, 0, 27, 70, 73, 0, 27, 70, 74, 0, 27, 70, 75, 0, 27, 70, 76, 0, 27, 70, 77, 0, 27, 70, 78, 0, 27, 70, 79, 0, 27, 70, 80, 0, 27, 70, 81, 0, 27, 70, 82, 0, 27, 70, 83, 0, 27, 70, 84, 0, 27, 70, 85, 0, 27, 70, 86, 0, 27, 70, 87, 0, 27, 70, 88, 0, 27, 70, 89, 0, 27, 70, 90, 0, 27, 70, 97, 0, 27, 70, 98, 0, 27, 70, 99, 0, 27, 70, 100, 0, 27, 70, 101, 0, 27, 70, 102, 0, 27, 70, 103, 0, 27, 70, 104, 0, 27, 70, 105, 0, 27, 70, 106, 0, 27, 70, 107, 0, 27, 70, 109, 0, 27, 70, 110, 0, 27, 70, 111, 0, 27, 70, 112, 0, 27, 70, 113, 0, 27, 70, 114, 0, 27, 70, 115, 0, 27, 70, 116, 0, 27, 70, 117, 0, 27, 70, 118, 0, 27, 70, 119, 0, 27, 70, 120, 0, 27, 70, 121, 0, 27, 70, 122, 0, 27, 70, 43, 0, 27, 70, 45, 0, 27, 70, 12, 0, 27, 91, 109, 0, 27, 91, 37, 112, 49, 37, 123, 51, 48, 125, 37, 43, 37, 100, 109, 0, 27, 91, 37, 112, 49, 37, 39, 40, 39, 37, 43, 37, 100, 109, 0, 27, 91, 51, 37, 112, 49, 37, 100, 109, 0, 27, 91, 52, 37, 112, 49, 37, 100, 109, 0 +}; +// Taken from Dickey ncurses terminfo.src dated 2017-04-22. +// This is a 256-colour terminfo description that lacks true-colour +// capabilities that stterm actually has. +static const signed char st_256colour_terminfo[] = { + 26, 1, 55, 0, 29, 0, 15, 0, 105, 1, 117, 5, 115, 116, 45, 50, 53, 54, 99, 111, 108, 111, 114, 124, 115, 116, 116, 101, 114, 109, 45, 50, 53, 54, 99, 111, 108, 111, 114, 124, 115, 105, 109, 112, 108, 101, 116, 101, 114, 109, 32, 119, 105, 116, 104, 32, 50, 53, 54, 32, 99, 111, 108, 111, 114, 115, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 80, 0, 8, 0, 24, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, -1, 127, 0, 0, 4, 0, 6, 0, 8, 0, 25, 0, 30, 0, 38, 0, 42, 0, 46, 0, -1, -1, 57, 0, 74, 0, 76, 0, 80, 0, 87, 0, -1, -1, 89, 0, 102, 0, -1, -1, 106, 0, 110, 0, 117, 0, 121, 0, -1, -1, -1, -1, 125, 0,-127, 0,-122, 0,-117, 0, -1, -1, -1, -1,-108, 0,-103, 0, -1, -1, -98, 0, -93, 0, -88, 0, -83, 0, -74, 0, -70, 0, -65, 0, -1, -1, -56, 0, -51, 0, -45, 0, -39, 0, -1, -1, -21, 0, -1, -1, -19, 0, -1, -1, -1, -1, -1, -1, -4, 0, -1, -1, 0, 1, -1, -1, 2, 1, -1, -1, 9, 1, 14, 1, 21, 1, 25, 1, 32, 1, 39, 1, -1, -1, 46, 1, 50, 1, 56, 1, 60, 1, 64, 1, 68, 1, 74, 1, 80, 1, 86, 1, 92, 1, 98, 1, 103, 1, 108, 1, 115, 1, -1, -1, 119, 1, 124, 1,-127, 1,-123, 1,-116, 1, -1, -1,-109, 1,-105, 1, -97, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -89, 1, -80, 1, -71, 1, -62, 1, -53, 1, -44, 1, -35, 1, -26, 1, -1, -1, -17, 1, -1, -1, -1, -1, -1, -1, -8, 1, -4, 1, 1, 2, -1, -1, 6, 2, 9, 2, -1, -1, -1, -1, 24, 2, 27, 2, 38, 2, 41, 2, 43, 2, 46, 2,-128, 2, -1, -1,-125, 2,-123, 2, -1, -1, -1, -1, -1, -1,-118, 2,-113, 2,-108, 2,-104, 2, -99, 2, -1, -1, -1, -1, -94, 2, -1, -1, -29, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -25, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -21, 2, -16, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -12, 2, -1, -1, -1, -1, -5, 2, -1, -1, -1, -1, -1, -1, -1, -1, 2, 3, 9, 3, 16, 3, -1, -1, -1, -1, 23, 3, -1, -1, 30, 3, -1, -1, -1, -1, -1, -1, 37, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 44, 3, 50, 3, 56, 3, 63, 3, 70, 3, 77, 3, 84, 3, 92, 3, 100, 3, 108, 3, 116, 3, 124, 3,-124, 3,-116, 3,-108, 3,-101, 3, -94, 3, -87, 3, -80, 3, -72, 3, -64, 3, -56, 3, -48, 3, -40, 3, -32, 3, -24, 3, -16, 3, -9, 3, -2, 3, 5, 4, 12, 4, 20, 4, 28, 4, 36, 4, 44, 4, 52, 4, 60, 4, 68, 4, 76, 4, 83, 4, 90, 4, 97, 4, 104, 4, 112, 4, 120, 4,-128, 4,-120, 4,-112, 4,-104, 4, -96, 4, -88, 4, -81, 4, -74, 4, -67, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -62, 4, -51, 4, -46, 4, -38, 4, -34, 4, -2, -1, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -25, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -20, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -14, 4, -1, -1, -1, -1, -1, -1, -10, 4, 53, 5, 27, 91, 90, 0, 7, 0, 13, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 114, 0, 27, 91, 51, 103, 0, 27, 91, 72, 27, 91, 50, 74, 0, 27, 91, 75, 0, 27, 91, 74, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 71, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 72, 0, 10, 0, 27, 91, 72, 0, 27, 91, 63, 50, 53, 108, 0, 8, 0, 27, 91, 63, 49, 50, 108, 27, 91, 63, 50, 53, 104, 0, 27, 91, 67, 0, 27, 91, 65, 0, 27, 91, 63, 50, 53, 104, 0, 27, 91, 80, 0, 27, 91, 77, 0, 27, 40, 48, 0, 27, 91, 53, 109, 0, 27, 91, 49, 109, 0, 27, 91, 63, 49, 48, 52, 57, 104, 0, 27, 91, 52, 104, 0, 27, 91, 56, 109, 0, 27, 91, 55, 109, 0, 27, 91, 55, 109, 0, 27, 91, 52, 109, 0, 27, 91, 37, 112, 49, 37, 100, 88, 0, 27, 40, 66, 0, 27, 91, 48, 109, 0, 27, 91, 63, 49, 48, 52, 57, 108, 0, 27, 91, 52, 108, 0, 27, 91, 50, 55, 109, 0, 27, 91, 50, 52, 109, 0, 27, 91, 63, 53, 104, 36, 60, 49, 48, 48, 47, 62, 27, 91, 63, 53, 108, 0, 7, 0, 27, 91, 52, 108, 27, 62, 27, 91, 63, 49, 48, 51, 52, 108, 0, 27, 91, 76, 0, 127, 0, 27, 91, 51, 59, 53, 126, 0, 27, 91, 51, 126, 0, 27, 91, 51, 59, 50, 126, 0, 27, 79, 66, 0, 27, 91, 50, 59, 50, 126, 0, 27, 91, 49, 59, 50, 70, 0, 27, 91, 49, 59, 53, 70, 0, 27, 79, 80, 0, 27, 91, 50, 49, 126, 0, 27, 79, 81, 0, 27, 79, 82, 0, 27, 79, 83, 0, 27, 91, 49, 53, 126, 0, 27, 91, 49, 55, 126, 0, 27, 91, 49, 56, 126, 0, 27, 91, 49, 57, 126, 0, 27, 91, 50, 48, 126, 0, 27, 91, 49, 126, 0, 27, 91, 50, 126, 0, 27, 91, 50, 59, 53, 126, 0, 27, 79, 68, 0, 27, 91, 54, 126, 0, 27, 91, 53, 126, 0, 27, 79, 67, 0, 27, 91, 49, 59, 50, 66, 0, 27, 91, 49, 59, 50, 65, 0, 27, 79, 65, 0, 27, 91, 63, 49, 108, 27, 62, 0, 27, 91, 63, 49, 104, 27, 61, 0, 27, 91, 37, 112, 49, 37, 100, 80, 0, 27, 91, 37, 112, 49, 37, 100, 77, 0, 27, 91, 37, 112, 49, 37, 100, 66, 0, 27, 91, 37, 112, 49, 37, 100, 64, 0, 27, 91, 37, 112, 49, 37, 100, 83, 0, 27, 91, 37, 112, 49, 37, 100, 76, 0, 27, 91, 37, 112, 49, 37, 100, 68, 0, 27, 91, 37, 112, 49, 37, 100, 67, 0, 27, 91, 37, 112, 49, 37, 100, 65, 0, 27, 91, 105, 0, 27, 91, 52, 105, 0, 27, 91, 53, 105, 0, 27, 99, 0, 27, 91, 52, 108, 27, 62, 27, 91, 63, 49, 48, 51, 52, 108, 0, 27, 56, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 100, 0, 27, 55, 0, 10, 0, 27, 77, 0, 37, 63, 37, 112, 57, 37, 116, 27, 40, 48, 37, 101, 27, 40, 66, 37, 59, 27, 91, 48, 37, 63, 37, 112, 54, 37, 116, 59, 49, 37, 59, 37, 63, 37, 112, 50, 37, 116, 59, 52, 37, 59, 37, 63, 37, 112, 49, 37, 112, 51, 37, 124, 37, 116, 59, 55, 37, 59, 37, 63, 37, 112, 52, 37, 116, 59, 53, 37, 59, 37, 63, 37, 112, 55, 37, 116, 59, 56, 37, 59, 109, 0, 27, 72, 0, 9, 0, 27, 93, 48, 59, 0, 27, 91, 49, 126, 0, 27, 91, 53, 126, 0, 27, 79, 117, 0, 27, 91, 52, 126, 0, 27, 91, 54, 126, 0, 43, 67, 44, 68, 45, 65, 46, 66, 48, 69, 96, 96, 97, 97, 102, 102, 103, 103, 104, 70, 105, 71, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 125, 126, 126, 0, 27, 91, 90, 0, 27, 41, 48, 0, 27, 91, 52, 126, 0, 27, 79, 77, 0, 27, 91, 51, 59, 50, 126, 0, 27, 91, 49, 59, 50, 70, 0, 27, 91, 49, 59, 50, 72, 0, 27, 91, 50, 59, 50, 126, 0, 27, 91, 49, 59, 50, 68, 0, 27, 91, 54, 59, 50, 126, 0, 27, 91, 53, 59, 50, 126, 0, 27, 91, 49, 59, 50, 67, 0, 27, 91, 50, 51, 126, 0, 27, 91, 50, 52, 126, 0, 27, 91, 49, 59, 50, 80, 0, 27, 91, 49, 59, 50, 81, 0, 27, 91, 49, 59, 50, 82, 0, 27, 91, 49, 59, 50, 83, 0, 27, 91, 49, 53, 59, 50, 126, 0, 27, 91, 49, 55, 59, 50, 126, 0, 27, 91, 49, 56, 59, 50, 126, 0, 27, 91, 49, 57, 59, 50, 126, 0, 27, 91, 50, 48, 59, 50, 126, 0, 27, 91, 50, 49, 59, 50, 126, 0, 27, 91, 50, 51, 59, 50, 126, 0, 27, 91, 50, 52, 59, 50, 126, 0, 27, 91, 49, 59, 53, 80, 0, 27, 91, 49, 59, 53, 81, 0, 27, 91, 49, 59, 53, 82, 0, 27, 91, 49, 59, 53, 83, 0, 27, 91, 49, 53, 59, 53, 126, 0, 27, 91, 49, 55, 59, 53, 126, 0, 27, 91, 49, 56, 59, 53, 126, 0, 27, 91, 49, 57, 59, 53, 126, 0, 27, 91, 50, 48, 59, 53, 126, 0, 27, 91, 50, 49, 59, 53, 126, 0, 27, 91, 50, 51, 59, 53, 126, 0, 27, 91, 50, 52, 59, 53, 126, 0, 27, 91, 49, 59, 54, 80, 0, 27, 91, 49, 59, 54, 81, 0, 27, 91, 49, 59, 54, 82, 0, 27, 91, 49, 59, 54, 83, 0, 27, 91, 49, 53, 59, 54, 126, 0, 27, 91, 49, 55, 59, 54, 126, 0, 27, 91, 49, 56, 59, 54, 126, 0, 27, 91, 49, 57, 59, 54, 126, 0, 27, 91, 50, 48, 59, 54, 126, 0, 27, 91, 50, 49, 59, 54, 126, 0, 27, 91, 50, 51, 59, 54, 126, 0, 27, 91, 50, 52, 59, 54, 126, 0, 27, 91, 49, 59, 51, 80, 0, 27, 91, 49, 59, 51, 81, 0, 27, 91, 49, 59, 51, 82, 0, 27, 91, 49, 59, 51, 83, 0, 27, 91, 49, 53, 59, 51, 126, 0, 27, 91, 49, 55, 59, 51, 126, 0, 27, 91, 49, 56, 59, 51, 126, 0, 27, 91, 49, 57, 59, 51, 126, 0, 27, 91, 50, 48, 59, 51, 126, 0, 27, 91, 50, 49, 59, 51, 126, 0, 27, 91, 50, 51, 59, 51, 126, 0, 27, 91, 50, 52, 59, 51, 126, 0, 27, 91, 49, 59, 52, 80, 0, 27, 91, 49, 59, 52, 81, 0, 27, 91, 49, 59, 52, 82, 0, 27, 91, 49, 75, 0, 27, 91, 37, 105, 37, 100, 59, 37, 100, 82, 0, 27, 91, 54, 110, 0, 27, 91, 63, 49, 59, 50, 99, 0, 27, 91, 99, 0, 27, 91, 51, 57, 59, 52, 57, 109, 0, 27, 91, 51, 109, 0, 27, 91, 50, 51, 109, 0, 27, 91, 77, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 51, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 57, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 51, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 52, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 49, 48, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 52, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0 +}; +// Taken from Dickey ncurses terminfo.src dated 2017-04-22. +// This is a 256-colour terminfo description that lacks true-colour +// capabilities that gnome actually has. +static const signed char vte_256colour_terminfo[] = { + 26, 1, 52, 0, 29, 0, 15, 0, 105, 1, -55, 5, 103, 110, 111, 109, 101, 45, 50, 53, 54, 99, 111, 108, 111, 114, 124, 71, 78, 79, 77, 69, 32, 84, 101, 114, 109, 105, 110, 97, 108, 32, 119, 105, 116, 104, 32, 120, 116, 101, 114, 109, 32, 50, 53, 54, 45, 99, 111, 108, 111, 114, 115, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 80, 0, 8, 0, 24, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, -1, 127, 0, 0, 4, 0, 6, 0, 8, 0, 25, 0, 30, 0, 38, 0, 42, 0, 46, 0, -1, -1, 57, 0, 74, 0, 76, 0, 80, 0, 87, 0, -1, -1, 89, 0, 96, 0, -1, -1, 100, 0, -1, -1, 104, 0, 108, 0, -1, -1, -1, -1, 112, 0, -1, -1, 114, 0, 119, 0, -1, -1, -128, 0, -123, 0, -118, 0, -1, -1,-113, 0, -108, 0, -103, 0, -98, 0, -89, 0, -87, 0, -81, 0, -1, -1, -68, 0, -63, 0, -57, 0, -51, 0, -1, -1, -1, -1, -1, -1, -33, 0, -1, -1, -1, -1, -1, -1, 0, 1, -1, -1, 4, 1, -1, -1, -1, -1, -1, -1, 6, 1, -1, -1, 11, 1, -1, -1, -1, -1, -1, -1, -1, -1, 15, 1, 19, 1, 25, 1, 29, 1, 33, 1, 37, 1, 43, 1, 49, 1, 55, 1, 61, 1, 67, 1, 71, 1, -1, -1, 76, 1, -1, -1, 80, 1, 85, 1, 90, 1, 94, 1, 101, 1, -1, -1, 108, 1, 112, 1, 120, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -128, 1,-119, 1, -110, 1, -101, 1, -92, 1, -83, 1, -74, 1, -65, 1, -56, 1, -47, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -38, 1, -35, 1, -1, -1, -1, -1, 16, 2, 19, 2, 30, 2, 33, 2, 35, 2, 38, 2, 116, 2, -1, -1, 119, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 121, 2, -1, -1, -1, -1, -1, -1, -1, -1, 125, 2, -1, -1, -78, 2, -1, -1, -1, -1, -74, 2, -68, 2, -1, -1, -1, -1, -62, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -58, 2, -54, 2, -1, -1, -50, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -45, 2, -1, -1, -38, 2, -33, 2, -1, -1, -1, -1, -1, -1, -1, -1, -26, 2, -19, 2, -12, 2, -1, -1, -1, -1, -5, 2, -1, -1, 2, 3, -1, -1, -1, -1, -1, -1, 9, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 16, 3, 22, 3, 28, 3, 35, 3, 42, 3, 49, 3, 56, 3, 64, 3, 72, 3, 80, 3, 88, 3, 96, 3, 104, 3, 112, 3, 120, 3, 127, 3, -122, 3, -115, 3,-108, 3, -100, 3, -92, 3, -84, 3, -76, 3, -68, 3, -60, 3, -52, 3, -44, 3, -37, 3, -30, 3, -23, 3, -16, 3, -8, 3, 0, 4, 8, 4, 16, 4, 24, 4, 32, 4, 40, 4, 48, 4, 55, 4, 62, 4, 69, 4, 76, 4, 84, 4, 92, 4, 100, 4, 108, 4, 116, 4, 124, 4, -124, 4,-116, 4, -109, 4, -102, 4, -95, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -90, 4, -79, 4, -74, 4, -55, 4, -51, 4, -42, 4, -35, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 59, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 64, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 70, 5, -1, -1, -1, -1, -1, -1, 74, 5, -119, 5, 27, 91, 90, 0, 7, 0, 13, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 114, 0, 27, 91, 51, 103, 0, 27, 91, 72, 27, 91, 50, 74, 0, 27, 91, 75, 0, 27, 91, 74, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 71, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 72, 0, 10, 0, 27, 91, 72, 0, 27, 91, 63, 50, 53, 108, 0, 8, 0, 27, 91, 63, 50, 53, 104, 0, 27, 91, 67, 0, 27, 91, 65, 0, 27, 91, 80, 0, 27, 91, 77, 0, 14, 0, 27, 91, 49, 109, 0, 27, 55, 27, 91, 63, 52, 55, 104, 0, 27, 91, 50, 109, 0, 27, 91, 52, 104, 0, 27, 91, 56, 109, 0, 27, 91, 55, 109, 0, 27, 91, 55, 109, 0, 27, 91, 52, 109, 0, 27, 91, 37, 112, 49, 37, 100, 88, 0, 15, 0, 27, 91, 48, 109, 15, 0, 27, 91, 50, 74, 27, 91, 63, 52, 55, 108, 27, 56, 0, 27, 91, 52, 108, 0, 27, 91, 50, 55, 109, 0, 27, 91, 50, 52, 109, 0, 27, 91, 63, 53, 104, 36, 60, 49, 48, 48, 47, 62, 27, 91, 63, 53, 108, 0, 27, 91, 109, 27, 91, 63, 55, 104, 27, 91, 52, 108, 27, 62, 27, 55, 27, 91, 114, 27, 91, 63, 49, 59, 51, 59, 52, 59, 54, 108, 27, 56, 0, 27, 91, 76, 0, 127, 0, 27, 91, 51, 126, 0, 27, 79, 66, 0, 27, 79, 80, 0, 27, 91, 50, 49, 126, 0, 27, 79, 81, 0, 27, 79, 82, 0, 27, 79, 83, 0, 27, 91, 49, 53, 126, 0, 27, 91, 49, 55, 126, 0, 27, 91, 49, 56, 126, 0, 27, 91, 49, 57, 126, 0, 27, 91, 50, 48, 126, 0, 27, 79, 72, 0, 27, 91, 50, 126, 0, 27, 79, 68, 0, 27, 91, 54, 126, 0, 27, 91, 53, 126, 0, 27, 79, 67, 0, 27, 91, 49, 59, 50, 66, 0, 27, 91, 49, 59, 50, 65, 0, 27, 79, 65, 0, 27, 91, 63, 49, 108, 27, 62, 0, 27, 91, 63, 49, 104, 27, 61, 0, 27, 91, 37, 112, 49, 37, 100, 80, 0, 27, 91, 37, 112, 49, 37, 100, 77, 0, 27, 91, 37, 112, 49, 37, 100, 66, 0, 27, 91, 37, 112, 49, 37, 100, 64, 0, 27, 91, 37, 112, 49, 37, 100, 83, 0, 27, 91, 37, 112, 49, 37, 100, 76, 0, 27, 91, 37, 112, 49, 37, 100, 68, 0, 27, 91, 37, 112, 49, 37, 100, 67, 0, 27, 91, 37, 112, 49, 37, 100, 84, 0, 27, 91, 37, 112, 49, 37, 100, 65, 0, 27, 99, 0, 27, 55, 27, 91, 114, 27, 56, 27, 91, 109, 27, 91, 63, 55, 104, 27, 91, 33, 112, 27, 91, 63, 49, 59, 51, 59, 52, 59, 54, 108, 27, 91, 52, 108, 27, 62, 27, 91, 63, 49, 48, 48, 48, 108, 27, 91, 63, 50, 53, 104, 0, 27, 56, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 100, 0, 27, 55, 0, 10, 0, 27, 77, 0, 27, 91, 48, 37, 63, 37, 112, 54, 37, 116, 59, 49, 37, 59, 37, 63, 37, 112, 50, 37, 116, 59, 52, 37, 59, 37, 63, 37, 112, 53, 37, 116, 59, 50, 37, 59, 37, 63, 37, 112, 55, 37, 116, 59, 56, 37, 59, 37, 63, 37, 112, 49, 37, 112, 51, 37, 124, 37, 116, 59, 55, 37, 59, 109, 37, 63, 37, 112, 57, 37, 116, 14, 37, 101, 15, 37, 59, 0, 27, 72, 0, 9, 0, 27, 91, 69, 0, 96, 96, 97, 97, 102, 102, 103, 103, 105, 105, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 125, 126, 126, 0, 27, 91, 90, 0, 27, 91, 63, 55, 104, 0, 27, 91, 63, 55, 108, 0, 27, 41, 48, 0, 27, 79, 70, 0, 27, 79, 77, 0, 27, 91, 49, 126, 0, 27, 91, 51, 59, 50, 126, 0, 27, 91, 52, 126, 0, 27, 91, 49, 59, 50, 70, 0, 27, 91, 49, 59, 50, 72, 0, 27, 91, 50, 59, 50, 126, 0, 27, 91, 49, 59, 50, 68, 0, 27, 91, 54, 59, 50, 126, 0, 27, 91, 53, 59, 50, 126, 0, 27, 91, 49, 59, 50, 67, 0, 27, 91, 50, 51, 126, 0, 27, 91, 50, 52, 126, 0, 27, 91, 49, 59, 50, 80, 0, 27, 91, 49, 59, 50, 81, 0, 27, 91, 49, 59, 50, 82, 0, 27, 91, 49, 59, 50, 83, 0, 27, 91, 49, 53, 59, 50, 126, 0, 27, 91, 49, 55, 59, 50, 126, 0, 27, 91, 49, 56, 59, 50, 126, 0, 27, 91, 49, 57, 59, 50, 126, 0, 27, 91, 50, 48, 59, 50, 126, 0, 27, 91, 50, 49, 59, 50, 126, 0, 27, 91, 50, 51, 59, 50, 126, 0, 27, 91, 50, 52, 59, 50, 126, 0, 27, 91, 49, 59, 53, 80, 0, 27, 91, 49, 59, 53, 81, 0, 27, 91, 49, 59, 53, 82, 0, 27, 91, 49, 59, 53, 83, 0, 27, 91, 49, 53, 59, 53, 126, 0, 27, 91, 49, 55, 59, 53, 126, 0, 27, 91, 49, 56, 59, 53, 126, 0, 27, 91, 49, 57, 59, 53, 126, 0, 27, 91, 50, 48, 59, 53, 126, 0, 27, 91, 50, 49, 59, 53, 126, 0, 27, 91, 50, 51, 59, 53, 126, 0, 27, 91, 50, 52, 59, 53, 126, 0, 27, 91, 49, 59, 54, 80, 0, 27, 91, 49, 59, 54, 81, 0, 27, 91, 49, 59, 54, 82, 0, 27, 91, 49, 59, 54, 83, 0, 27, 91, 49, 53, 59, 54, 126, 0, 27, 91, 49, 55, 59, 54, 126, 0, 27, 91, 49, 56, 59, 54, 126, 0, 27, 91, 49, 57, 59, 54, 126, 0, 27, 91, 50, 48, 59, 54, 126, 0, 27, 91, 50, 49, 59, 54, 126, 0, 27, 91, 50, 51, 59, 54, 126, 0, 27, 91, 50, 52, 59, 54, 126, 0, 27, 91, 49, 59, 51, 80, 0, 27, 91, 49, 59, 51, 81, 0, 27, 91, 49, 59, 51, 82, 0, 27, 91, 49, 59, 51, 83, 0, 27, 91, 49, 53, 59, 51, 126, 0, 27, 91, 49, 55, 59, 51, 126, 0, 27, 91, 49, 56, 59, 51, 126, 0, 27, 91, 49, 57, 59, 51, 126, 0, 27, 91, 50, 48, 59, 51, 126, 0, 27, 91, 50, 49, 59, 51, 126, 0, 27, 91, 50, 51, 59, 51, 126, 0, 27, 91, 50, 52, 59, 51, 126, 0, 27, 91, 49, 59, 52, 80, 0, 27, 91, 49, 59, 52, 81, 0, 27, 91, 49, 59, 52, 82, 0, 27, 91, 49, 75, 0, 27, 91, 37, 105, 37, 100, 59, 37, 100, 82, 0, 27, 91, 54, 110, 0, 27, 91, 63, 37, 91, 59, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 93, 99, 0, 27, 91, 99, 0, 27, 91, 51, 57, 59, 52, 57, 109, 0, 27, 93, 49, 48, 52, 7, 0, 27, 93, 52, 59, 37, 112, 49, 37, 100, 59, 114, 103, 98, 58, 37, 112, 50, 37, 123, 50, 53, 53, 125, 37, 42, 37, 123, 49, 48, 48, 48, 125, 37, 47, 37, 50, 46, 50, 88, 47, 37, 112, 51, 37, 123, 50, 53, 53, 125, 37, 42, 37, 123, 49, 48, 48, 48, 125, 37, 47, 37, 50, 46, 50, 88, 47, 37, 112, 52, 37, 123, 50, 53, 53, 125, 37, 42, 37, 123, 49, 48, 48, 48, 125, 37, 47, 37, 50, 46, 50, 88, 27, 92, 0, 27, 91, 51, 109, 0, 27, 91, 50, 51, 109, 0, 27, 91, 77, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 51, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 57, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 51, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0, 27, 91, 37, 63, 37, 112, 49, 37, 123, 56, 125, 37, 60, 37, 116, 52, 37, 112, 49, 37, 100, 37, 101, 37, 112, 49, 37, 123, 49, 54, 125, 37, 60, 37, 116, 49, 48, 37, 112, 49, 37, 123, 56, 125, 37, 45, 37, 100, 37, 101, 52, 56, 59, 53, 59, 37, 112, 49, 37, 100, 37, 59, 109, 0 +}; +// Taken from Dickey ncurses terminfo.src dated 2017-04-22. +static const signed char ansi_terminfo[] = { + 26, 1, 40, 0, 23, 0, 16, 0, 125, 1, 68, 2, 97, 110, 115, 105, 124, 97, 110, 115, 105, 47, 112, 99, 45, 116, 101, 114, 109, 32, 99, 111, 109, 112, 97, 116, 105, 98, 108, 101, 32, 119, 105, 116, 104, 32, 99, 111, 108, 111, 114, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 80, 0, 8, 0, 24, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 8, 0, 64, 0, 3, 0, 0, 0, 4, 0, 6, 0, -1, -1, 8, 0, 13, 0, 20, 0, 24, 0, 28, 0, -1, -1, 39, 0, 56, 0, 60, 0, -1, -1, 64, 0, -1, -1, -1, -1, 68, 0, -1, -1, 72, 0, -1, -1, 76, 0, 80, 0, -1, -1, -1, -1, 84, 0, 90, 0, 95, 0, -1, -1, -1, -1, -1, -1, -1, -1, 100, 0, -1, -1, 105, 0, 110, 0, 115, 0, 120, 0,-127, 0,-121, 0, -1, -1, -1, -1, -1, -1,-113, 0,-109, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-105, 0, -1, -1,-101, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -99, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -95, 0, -91, 0, -1, -1, -87, 0, -1, -1, -1, -1, -1, -1, -83, 0, -1, -1, -1, -1, -1, -1, -79, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -75, 0, -1, -1, -70, 0, -61, 0, -52, 0, -43, 0, -34, 0, -25, 0, -16, 0, -7, 0, 2, 1, 11, 1, -1, -1, -1, -1, -1, -1, -1, -1, 20, 1, 25, 1, 30, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 50, 1, -1, -1, 61, 1, -1, -1, 63, 1,-107, 1, -1, -1,-104, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-100, 1, -1, -1, -37, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -33, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -28, 1, -17, 1, -12, 1, 7, 2, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 20, 2, 30, 2, -1, -1, -1, -1, -1, -1, 40, 2, 44, 2, 48, 2, 52, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 56, 2, 62, 2, 27, 91, 90, 0, 7, 0, 13, 0, 27, 91, 51, 103, 0, 27, 91, 72, 27, 91, 74, 0, 27, 91, 75, 0, 27, 91, 74, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 71, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 59, 37, 112, 50, 37, 100, 72, 0, 27, 91, 66, 0, 27, 91, 72, 0, 27, 91, 68, 0, 27, 91, 67, 0, 27, 91, 65, 0, 27, 91, 80, 0, 27, 91, 77, 0, 27, 91, 49, 49, 109, 0, 27, 91, 53, 109, 0, 27, 91, 49, 109, 0, 27, 91, 56, 109, 0, 27, 91, 55, 109, 0, 27, 91, 55, 109, 0, 27, 91, 52, 109, 0, 27, 91, 37, 112, 49, 37, 100, 88, 0, 27, 91, 49, 48, 109, 0, 27, 91, 48, 59, 49, 48, 109, 0, 27, 91, 109, 0, 27, 91, 109, 0, 27, 91, 76, 0, 8, 0, 27, 91, 66, 0, 27, 91, 72, 0, 27, 91, 76, 0, 27, 91, 68, 0, 27, 91, 67, 0, 27, 91, 65, 0, 13, 27, 91, 83, 0, 27, 91, 37, 112, 49, 37, 100, 80, 0, 27, 91, 37, 112, 49, 37, 100, 77, 0, 27, 91, 37, 112, 49, 37, 100, 66, 0, 27, 91, 37, 112, 49, 37, 100, 64, 0, 27, 91, 37, 112, 49, 37, 100, 83, 0, 27, 91, 37, 112, 49, 37, 100, 76, 0, 27, 91, 37, 112, 49, 37, 100, 68, 0, 27, 91, 37, 112, 49, 37, 100, 67, 0, 27, 91, 37, 112, 49, 37, 100, 84, 0, 27, 91, 37, 112, 49, 37, 100, 65, 0, 27, 91, 52, 105, 0, 27, 91, 53, 105, 0, 37, 112, 49, 37, 99, 27, 91, 37, 112, 50, 37, 123, 49, 125, 37, 45, 37, 100, 98, 0, 27, 91, 37, 105, 37, 112, 49, 37, 100, 100, 0, 10, 0, 27, 91, 48, 59, 49, 48, 37, 63, 37, 112, 49, 37, 116, 59, 55, 37, 59, 37, 63, 37, 112, 50, 37, 116, 59, 52, 37, 59, 37, 63, 37, 112, 51, 37, 116, 59, 55, 37, 59, 37, 63, 37, 112, 52, 37, 116, 59, 53, 37, 59, 37, 63, 37, 112, 54, 37, 116, 59, 49, 37, 59, 37, 63, 37, 112, 55, 37, 116, 59, 56, 37, 59, 37, 63, 37, 112, 57, 37, 116, 59, 49, 49, 37, 59, 109, 0, 27, 72, 0, 27, 91, 73, 0, 43, 16, 44, 17, 45, 24, 46, 25, 48, -37, 96, 4, 97, -79, 102, -8, 103, -15, 104, -80, 106, -39, 107, -65, 108, -38, 109, -64, 110, -59, 111, 126, 112, -60, 113, -60, 114, -60, 115, 95, 116, -61, 117, -76, 118, -63, 119, -62, 120, -77, 121, -13, 122, -14, 123, -29, 124, -40, 125,-100, 126, -2, 0, 27, 91, 90, 0, 27, 91, 49, 75, 0, 27, 91, 37, 105, 37, 100, 59, 37, 100, 82, 0, 27, 91, 54, 110, 0, 27, 91, 63, 37, 91, 59, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 93, 99, 0, 27, 91, 99, 0, 27, 91, 51, 57, 59, 52, 57, 109, 0, 27, 91, 51, 37, 112, 49, 37, 100, 109, 0, 27, 91, 52, 37, 112, 49, 37, 100, 109, 0, 27, 40, 66, 0, 27, 41, 66, 0, 27, 42, 66, 0, 27, 43, 66, 0, 27, 91, 49, 49, 109, 0, 27, 91, 49, 48, 109, 0 +}; + +bool terminfo_is_term_family(const char *term, const char *family) +{ + if (!term) { + return false; + } + size_t tlen = strlen(term); + size_t flen = strlen(family); + return tlen >= flen + && 0 == memcmp(term, family, flen) + // Per commentary in terminfo, minus is the only valid suffix separator. + && ('\0' == term[flen] || '-' == term[flen]); +} + +/// Loads a built-in terminfo db when we (unibilium) failed to load a terminfo +/// record from the environment (termcap systems, unrecognized $TERM, …). +/// We do not attempt to detect xterm pretenders here. +/// +/// @param term $TERM value +/// @param[out,allocated] termname decided builtin 'term' name +/// @return [allocated] terminfo structure +static unibi_term *terminfo_builtin(const char *term, char **termname) +{ + if (terminfo_is_term_family(term, "xterm")) { + *termname = xstrdup("builtin_xterm"); + return unibi_from_mem((const char *)xterm_256colour_terminfo, + sizeof xterm_256colour_terminfo); + } else if (terminfo_is_term_family(term, "screen")) { + *termname = xstrdup("builtin_screen"); + return unibi_from_mem((const char *)screen_256colour_terminfo, + sizeof screen_256colour_terminfo); + } else if (terminfo_is_term_family(term, "tmux")) { + *termname = xstrdup("builtin_tmux"); + return unibi_from_mem((const char *)tmux_256colour_terminfo, + sizeof tmux_256colour_terminfo); + } else if (terminfo_is_term_family(term, "rxvt")) { + *termname = xstrdup("builtin_rxvt"); + return unibi_from_mem((const char *)rxvt_256colour_terminfo, + sizeof rxvt_256colour_terminfo); + } else if (terminfo_is_term_family(term, "putty")) { + *termname = xstrdup("builtin_putty"); + return unibi_from_mem((const char *)putty_256colour_terminfo, + sizeof putty_256colour_terminfo); + } else if (terminfo_is_term_family(term, "linux")) { + *termname = xstrdup("builtin_linux"); + return unibi_from_mem((const char *)linux_16colour_terminfo, + sizeof linux_16colour_terminfo); + } else if (terminfo_is_term_family(term, "interix")) { + *termname = xstrdup("builtin_interix"); + return unibi_from_mem((const char *)interix_8colour_terminfo, + sizeof interix_8colour_terminfo); + } else if (terminfo_is_term_family(term, "iterm") + || terminfo_is_term_family(term, "iterm2") + || terminfo_is_term_family(term, "iTerm.app") + || terminfo_is_term_family(term, "iTerm2.app")) { + *termname = xstrdup("builtin_iterm"); + return unibi_from_mem((const char *)iterm_256colour_terminfo, + sizeof iterm_256colour_terminfo); + } else if (terminfo_is_term_family(term, "st")) { + *termname = xstrdup("builtin_st"); + return unibi_from_mem((const char *)st_256colour_terminfo, + sizeof st_256colour_terminfo); + } else if (terminfo_is_term_family(term, "gnome") + || terminfo_is_term_family(term, "vte")) { + *termname = xstrdup("builtin_vte"); + return unibi_from_mem((const char *)vte_256colour_terminfo, + sizeof vte_256colour_terminfo); + } else { + *termname = xstrdup("builtin_ansi"); + return unibi_from_mem((const char *)ansi_terminfo, + sizeof ansi_terminfo); + } +} + +/// @param term $TERM value +/// @param[out,allocated] termname decided builtin 'term' name +/// @return [allocated] terminfo structure +unibi_term *terminfo_from_builtin(const char *term, char **termname) +{ + unibi_term *ut = terminfo_builtin(term, termname); + if (*termname == NULL) { + *termname = xstrdup("builtin_?"); + } + // Disable BCE by default (for built-in terminfos). #7624 + // https://github.com/kovidgoyal/kitty/issues/160#issuecomment-346470545 + unibi_set_bool(ut, unibi_back_color_erase, false); + return ut; +} + +/// Dumps termcap info to the messages area. +/// Serves a similar purpose as Vim `:set termcap` (removed in Nvim). +/// +/// @note adapted from unibilium unibi-dump.c +void terminfo_info_msg(const unibi_term *const ut) +{ + if (exiting) { + return; + } + msg_puts_title("\n\n--- Terminal info --- {{{\n"); + + char *term; + get_tty_option("term", &term); + msg_printf_attr(0, "&term: %s\n", term); + msg_printf_attr(0, "Description: %s\n", unibi_get_name(ut)); + const char **a = unibi_get_aliases(ut); + if (*a) { + msg_puts("Aliases: "); + do { + msg_printf_attr(0, "%s%s\n", *a, a[1] ? " | " : ""); + a++; + } while (*a); + } + + msg_puts("Boolean capabilities:\n"); + for (enum unibi_boolean i = unibi_boolean_begin_ + 1; + i < unibi_boolean_end_; i++) { + msg_printf_attr(0, " %-25s %-10s = %s\n", unibi_name_bool(i), + unibi_short_name_bool(i), + unibi_get_bool(ut, i) ? "true" : "false"); + } + + msg_puts("Numeric capabilities:\n"); + for (enum unibi_numeric i = unibi_numeric_begin_ + 1; + i < unibi_numeric_end_; i++) { + int n = unibi_get_num(ut, i); // -1 means "empty" + msg_printf_attr(0, " %-25s %-10s = %hd\n", unibi_name_num(i), + unibi_short_name_num(i), n); + } + + msg_puts("String capabilities:\n"); + for (enum unibi_string i = unibi_string_begin_ + 1; + i < unibi_string_end_; i++) { + const char *s = unibi_get_str(ut, i); + if (s) { + msg_printf_attr(0, " %-25s %-10s = ", unibi_name_str(i), + unibi_short_name_str(i)); + // Most of these strings will contain escape sequences. + msg_outtrans_special((char_u *)s, false); + msg_putchar('\n'); + } + } + + if (unibi_count_ext_bool(ut)) { + msg_puts("Extended boolean capabilities:\n"); + for (size_t i = 0; i < unibi_count_ext_bool(ut); i++) { + msg_printf_attr(0, " %-25s = %s\n", + unibi_get_ext_bool_name(ut, i), + unibi_get_ext_bool(ut, i) ? "true" : "false"); + } + } + + if (unibi_count_ext_num(ut)) { + msg_puts("Extended numeric capabilities:\n"); + for (size_t i = 0; i < unibi_count_ext_num(ut); i++) { + msg_printf_attr(0, " %-25s = %hd\n", + unibi_get_ext_num_name(ut, i), + unibi_get_ext_num(ut, i)); + } + } + + if (unibi_count_ext_str(ut)) { + msg_puts("Extended string capabilities:\n"); + for (size_t i = 0; i < unibi_count_ext_str(ut); i++) { + msg_printf_attr(0, " %-25s = ", unibi_get_ext_str_name(ut, i)); + msg_outtrans_special((char_u *)unibi_get_ext_str(ut, i), false); + msg_putchar('\n'); + } + } + + msg_puts("}}}\n"); + xfree(term); +} diff --git a/src/nvim/tui/terminfo.h b/src/nvim/tui/terminfo.h new file mode 100644 index 0000000000..099df8967f --- /dev/null +++ b/src/nvim/tui/terminfo.h @@ -0,0 +1,10 @@ +#ifndef NVIM_TUI_TERMINFO_H +#define NVIM_TUI_TERMINFO_H + +#include <unibilium.h> + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "tui/terminfo.h.generated.h" +#endif + +#endif // NVIM_TUI_TERMINFO_H diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 736d50ee8b..dcb1f850b7 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -23,6 +23,7 @@ #include "nvim/map.h" #include "nvim/main.h" #include "nvim/memory.h" +#include "nvim/option.h" #include "nvim/api/vim.h" #include "nvim/api/private/helpers.h" #include "nvim/event/loop.h" @@ -34,29 +35,32 @@ #include "nvim/ugrid.h" #include "nvim/tui/input.h" #include "nvim/tui/tui.h" +#include "nvim/tui/terminfo.h" #include "nvim/cursor_shape.h" #include "nvim/syntax.h" #include "nvim/macros.h" -// Space reserved in the output buffer to restore the cursor to normal when -// flushing. No existing terminal will require 32 bytes to do that. +// Space reserved in two output buffers to make the cursor normal or invisible +// when flushing. No existing terminal will require 32 bytes to do that. #define CNORM_COMMAND_MAX_SIZE 32 #define OUTBUF_SIZE 0xffff #define TOO_MANY_EVENTS 1000000 -#define STARTS_WITH(str, prefix) (!memcmp(str, prefix, sizeof(prefix) - 1)) -#define TMUX_WRAP(seq) (is_tmux ? "\x1bPtmux;\x1b" seq "\x1b\\" : seq) - -typedef enum TermType { - kTermUnknown, - kTermGnome, - kTermiTerm, - kTermKonsole, - kTermRxvt, - kTermDTTerm, - kTermXTerm, - kTermTeraTerm, -} TermType; +#define STARTS_WITH(str, prefix) (strlen(str) >= (sizeof(prefix) - 1) \ + && 0 == memcmp((str), (prefix), sizeof(prefix) - 1)) +#define TMUX_WRAP(is_tmux, seq) ((is_tmux) \ + ? "\x1bPtmux;\x1b" seq "\x1b\\" : seq) +#define LINUXSET0C "\x1b[?0c" +#define LINUXSET1C "\x1b[?1c" + +#ifdef NVIM_UNIBI_HAS_VAR_FROM +#define UNIBI_SET_NUM_VAR(var, num) \ + do { \ + (var) = unibi_var_from_num((num)); \ + } while (0) +#else +#define UNIBI_SET_NUM_VAR(var, num) (var).i = (num); +#endif typedef struct { int top, bot, left, right; @@ -65,10 +69,12 @@ typedef struct { typedef struct { UIBridgeData *bridge; Loop *loop; - bool stop; unibi_var_t params[9]; char buf[OUTBUF_SIZE]; - size_t bufpos, bufsize; + size_t bufpos; + char norm[CNORM_COMMAND_MAX_SIZE]; + char invis[CNORM_COMMAND_MAX_SIZE]; + size_t normlen, invislen; TermInput input; uv_loop_t write_loop; unibi_term *ut; @@ -86,12 +92,14 @@ typedef struct { bool can_change_scroll_region; bool can_set_lr_margin; bool can_set_left_right_margin; + bool immediate_wrap_after_last_column; bool mouse_enabled; - bool busy; + bool busy, is_invisible; + bool cork, overflow; cursorentry_T cursor_shapes[SHAPE_IDX_COUNT]; HlAttrs print_attrs; + bool default_attr; ModeShape showing_mode; - TermType term; struct { int enable_mouse, disable_mouse; int enable_bracketed_paste, disable_bracketed_paste; @@ -101,12 +109,12 @@ typedef struct { int enable_focus_reporting, disable_focus_reporting; int resize_screen; int reset_scroll_region; + int set_cursor_style, reset_cursor_style; } unibi_ext; } TUIData; static bool volatile got_winch = false; static bool cursor_style_enabled = false; -static bool is_tmux = false; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "tui/tui.c.generated.h" @@ -115,9 +123,8 @@ static bool is_tmux = false; UI *tui_start(void) { - UI *ui = xcalloc(1, sizeof(UI)); + UI *ui = xcalloc(1, sizeof(UI)); // Freed by ui_bridge_stop(). ui->stop = tui_stop; - ui->rgb = p_tgc; ui->resize = tui_resize; ui->clear = tui_clear; ui->eol_clear = tui_eol_clear; @@ -135,26 +142,45 @@ UI *tui_start(void) ui->put = tui_put; ui->bell = tui_bell; ui->visual_bell = tui_visual_bell; - ui->update_fg = tui_update_fg; - ui->update_bg = tui_update_bg; - ui->update_sp = tui_update_sp; + ui->default_colors_set = tui_default_colors_set; ui->flush = tui_flush; ui->suspend = tui_suspend; ui->set_title = tui_set_title; ui->set_icon = tui_set_icon; - ui->event = tui_event; + ui->option_set= tui_option_set; memset(ui->ui_ext, 0, sizeof(ui->ui_ext)); return ui_bridge_attach(ui, tui_main, tui_scheduler); } +static size_t unibi_pre_fmt_str(TUIData *data, unsigned int unibi_index, + char * buf, size_t len) +{ + const char *str = unibi_get_str(data->ut, unibi_index); + if (!str) { + return 0U; + } + return unibi_run(str, data->params, buf, len); +} + +static void termname_set_event(void **argv) +{ + char *termname = argv[0]; + set_tty_option("term", termname); + // Do not free termname, it is freed by set_tty_option. +} + static void terminfo_start(UI *ui) { TUIData *data = ui->data; data->scroll_region_is_full_screen = true; data->bufpos = 0; - data->bufsize = sizeof(data->buf) - CNORM_COMMAND_MAX_SIZE; + data->default_attr = false; + data->is_invisible = true; + data->busy = false; + data->cork = false; + data->overflow = false; data->showing_mode = SHAPE_IDX_N; data->unibi_ext.enable_mouse = -1; data->unibi_ext.disable_mouse = -1; @@ -167,16 +193,36 @@ static void terminfo_start(UI *ui) data->unibi_ext.disable_focus_reporting = -1; data->unibi_ext.resize_screen = -1; data->unibi_ext.reset_scroll_region = -1; + data->unibi_ext.set_cursor_style = -1; + data->unibi_ext.reset_cursor_style = -1; data->out_fd = 1; data->out_isatty = os_isatty(data->out_fd); - // setup unibilium + + // Set up unibilium/terminfo. + const char *term = os_getenv("TERM"); data->ut = unibi_from_env(); + char *termname = NULL; if (!data->ut) { - // For some reason could not read terminfo file, use a dummy entry that - // will be populated with common values by fix_terminfo below - data->ut = unibi_dummy(); + data->ut = terminfo_from_builtin(term, &termname); + } else { + termname = xstrdup(term); } - fix_terminfo(data); + // Update 'term' option. + loop_schedule_deferred(&main_loop, + event_create(termname_set_event, 1, termname)); + + // None of the following work over SSH; see :help TERM . + const char *colorterm = os_getenv("COLORTERM"); + const char *termprg = os_getenv("TERM_PROGRAM"); + const char *vte_version_env = os_getenv("VTE_VERSION"); + long vte_version = vte_version_env ? strtol(vte_version_env, NULL, 10) : 0; + bool iterm_env = termprg && strstr(termprg, "iTerm.app"); + bool konsole = terminfo_is_term_family(term, "konsole") + || os_getenv("KONSOLE_PROFILE_NAME") + || os_getenv("KONSOLE_DBUS_SESSION"); + + patch_terminfo_bugs(data, term, colorterm, vte_version, konsole, iterm_env); + augment_terminfo(data, term, colorterm, vte_version, konsole, iterm_env); data->can_change_scroll_region = !!unibi_get_str(data->ut, unibi_change_scroll_region); data->can_set_lr_margin = @@ -184,20 +230,31 @@ static void terminfo_start(UI *ui) data->can_set_left_right_margin = !!unibi_get_str(data->ut, unibi_set_left_margin_parm) && !!unibi_get_str(data->ut, unibi_set_right_margin_parm); + data->immediate_wrap_after_last_column = + terminfo_is_term_family(term, "cygwin") + || terminfo_is_term_family(term, "interix"); + data->normlen = unibi_pre_fmt_str(data, unibi_cursor_normal, + data->norm, sizeof data->norm); + data->invislen = unibi_pre_fmt_str(data, unibi_cursor_invisible, + data->invis, sizeof data->invis); // Set 't_Co' from the result of unibilium & fix_terminfo. t_colors = unibi_get_num(data->ut, unibi_max_colors); // Enter alternate screen and clear // NOTE: Do this *before* changing terminal settings. #6433 unibi_out(ui, unibi_enter_ca_mode); + unibi_out(ui, unibi_keypad_xmit); unibi_out(ui, unibi_clear_screen); // Enable bracketed paste - unibi_out(ui, data->unibi_ext.enable_bracketed_paste); - // Enable focus reporting - unibi_out(ui, data->unibi_ext.enable_focus_reporting); + unibi_out_ext(ui, data->unibi_ext.enable_bracketed_paste); + uv_loop_init(&data->write_loop); if (data->out_isatty) { uv_tty_init(&data->write_loop, &data->output_handle.tty, data->out_fd, 0); +#ifdef WIN32 uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_RAW); +#else + uv_tty_set_mode(&data->output_handle.tty, UV_TTY_MODE_IO); +#endif } else { uv_pipe_init(&data->write_loop, &data->output_handle.pipe, 0); uv_pipe_open(&data->output_handle.pipe, data->out_fd); @@ -213,12 +270,13 @@ static void terminfo_stop(UI *ui) unibi_out(ui, unibi_exit_attribute_mode); // cursor should be set to normal before exiting alternate screen unibi_out(ui, unibi_cursor_normal); + unibi_out(ui, unibi_keypad_local); unibi_out(ui, unibi_exit_ca_mode); // Disable bracketed paste - unibi_out(ui, data->unibi_ext.disable_bracketed_paste); + unibi_out_ext(ui, data->unibi_ext.disable_bracketed_paste); // Disable focus reporting - unibi_out(ui, data->unibi_ext.disable_focus_reporting); - flush_buf(ui, true); + unibi_out_ext(ui, data->unibi_ext.disable_focus_reporting); + flush_buf(ui); uv_tty_reset_mode(); uv_close((uv_handle_t *)&data->output_handle, NULL); uv_run(&data->write_loop, UV_RUN_DEFAULT); @@ -231,7 +289,7 @@ static void terminfo_stop(UI *ui) static void tui_terminal_start(UI *ui) { TUIData *data = ui->data; - data->print_attrs = EMPTY_ATTRS; + data->print_attrs = HLATTRS_INIT; ugrid_init(&data->grid); terminfo_start(ui); update_size(ui); @@ -239,6 +297,16 @@ static void tui_terminal_start(UI *ui) term_input_start(&data->input); } +static void tui_terminal_after_startup(UI *ui) + FUNC_ATTR_NONNULL_ALL +{ + TUIData *data = ui->data; + + // Emit this after Nvim startup, not during. This works around a tmux + // 2.3 bug(?) which caused slow drawing during startup. #7649 + unibi_out_ext(ui, data->unibi_ext.enable_focus_reporting); +} + static void tui_terminal_stop(UI *ui) { TUIData *data = ui->data; @@ -251,11 +319,17 @@ static void tui_terminal_stop(UI *ui) static void tui_stop(UI *ui) { tui_terminal_stop(ui); - TUIData *data = ui->data; - data->stop = true; + // Flag UI as "stopped". + ui->data = NULL; +} + +/// Returns true if UI `ui` is stopped. +static bool tui_is_stopped(UI *ui) +{ + return ui->data == NULL; } -// Main function of the TUI thread +/// Main function of the TUI thread. static void tui_main(UIBridgeData *bridge, UI *ui) { Loop tui_loop; @@ -276,14 +350,26 @@ static void tui_main(UIBridgeData *bridge, UI *ui) #endif term_input_init(&data->input, &tui_loop); tui_terminal_start(ui); - data->stop = false; - // allow the main thread to continue, we are ready to start handling UI - // callbacks + // Allow main thread to continue, we are ready to handle UI callbacks. CONTINUE(bridge); - while (!data->stop) { - loop_poll_events(&tui_loop, -1); + loop_schedule_deferred(&main_loop, + event_create(show_termcap_event, 1, data->ut)); + + // "Active" loop: first ~100 ms of startup. + for (size_t ms = 0; ms < 100 && !tui_is_stopped(ui);) { + ms += (loop_poll_events(&tui_loop, 20) ? 20 : 1); + } + if (!tui_is_stopped(ui)) { + tui_terminal_after_startup(ui); + // Tickle `main_loop` with a dummy event, else the initial "focus-gained" + // terminal response may not get processed until user hits a key. + loop_schedule_deferred(&main_loop, event_create(tui_dummy_event, 0)); + } + // "Passive" (I/O-driven) loop: TUI thread "main loop". + while (!tui_is_stopped(ui)) { + loop_poll_events(&tui_loop, -1); // tui_loop.events is never processed } ui_bridge_stopped(bridge); @@ -294,14 +380,18 @@ static void tui_main(UIBridgeData *bridge, UI *ui) loop_close(&tui_loop, false); kv_destroy(data->invalid_regions); xfree(data); - xfree(ui); } +static void tui_dummy_event(void **argv) +{ +} + +/// Handoff point between the main (ui_bridge) thread and the TUI thread. static void tui_scheduler(Event event, void *d) { UI *ui = d; TUIData *data = ui->data; - loop_schedule(data->loop, event); + loop_schedule(data->loop, event); // `tui_loop` local to tui_main(). } #ifdef UNIX @@ -319,94 +409,293 @@ static void sigwinch_cb(SignalWatcher *watcher, int signum, void *data) ui_schedule_refresh(); } -static bool attrs_differ(HlAttrs a1, HlAttrs a2) +static bool attrs_differ(HlAttrs a1, HlAttrs a2, bool rgb) { - return a1.foreground != a2.foreground || a1.background != a2.background - || a1.bold != a2.bold || a1.italic != a2.italic - || a1.undercurl != a2.undercurl || a1.underline != a2.underline - || a1.reverse != a2.reverse; + if (rgb) { + // TODO(bfredl): when we start to support special color, + // rgb_sp_color must be added here + return a1.rgb_fg_color != a2.rgb_fg_color + || a1.rgb_bg_color != a2.rgb_bg_color + || a1.rgb_ae_attr != a2.rgb_ae_attr; + } else { + return a1.cterm_fg_color != a2.cterm_fg_color + || a1.cterm_bg_color != a2.cterm_bg_color + || a1.cterm_ae_attr != a2.cterm_ae_attr; + } } static void update_attrs(UI *ui, HlAttrs attrs) { TUIData *data = ui->data; - if (!attrs_differ(attrs, data->print_attrs)) { + if (!attrs_differ(attrs, data->print_attrs, ui->rgb)) { return; } data->print_attrs = attrs; - unibi_out(ui, unibi_exit_attribute_mode); UGrid *grid = &data->grid; - int fg = attrs.foreground != -1 ? attrs.foreground : grid->fg; - int bg = attrs.background != -1 ? attrs.background : grid->bg; + int fg = ui->rgb ? attrs.rgb_fg_color : (attrs.cterm_fg_color - 1); + if (fg == -1) { + fg = ui->rgb ? grid->clear_attrs.rgb_fg_color + : (grid->clear_attrs.cterm_fg_color - 1); + } + + int bg = ui->rgb ? attrs.rgb_bg_color : (attrs.cterm_bg_color - 1); + if (bg == -1) { + bg = ui->rgb ? grid->clear_attrs.rgb_bg_color + : (grid->clear_attrs.cterm_bg_color - 1); + } + int attr = ui->rgb ? attrs.rgb_ae_attr : attrs.cterm_ae_attr; + bool bold = attr & HL_BOLD; + bool italic = attr & HL_ITALIC; + bool reverse = attr & HL_INVERSE; + bool standout = attr & HL_STANDOUT; + bool underline = attr & (HL_UNDERLINE), undercurl = attr & (HL_UNDERCURL); + + if (unibi_get_str(data->ut, unibi_set_attributes)) { + if (bold || reverse || underline || undercurl) { + UNIBI_SET_NUM_VAR(data->params[0], 0); // standout + UNIBI_SET_NUM_VAR(data->params[1], underline || undercurl); + UNIBI_SET_NUM_VAR(data->params[2], reverse); + UNIBI_SET_NUM_VAR(data->params[3], 0); // blink + UNIBI_SET_NUM_VAR(data->params[4], 0); // dim + UNIBI_SET_NUM_VAR(data->params[5], bold); + UNIBI_SET_NUM_VAR(data->params[6], 0); // blank + UNIBI_SET_NUM_VAR(data->params[7], 0); // protect + UNIBI_SET_NUM_VAR(data->params[8], 0); // alternate character set + unibi_out(ui, unibi_set_attributes); + } else if (!data->default_attr) { + unibi_out(ui, unibi_exit_attribute_mode); + } + } else { + if (!data->default_attr) { + unibi_out(ui, unibi_exit_attribute_mode); + } + if (bold) { + unibi_out(ui, unibi_enter_bold_mode); + } + if (underline || undercurl) { + unibi_out(ui, unibi_enter_underline_mode); + } + if (standout) { + unibi_out(ui, unibi_enter_standout_mode); + } + if (reverse) { + unibi_out(ui, unibi_enter_reverse_mode); + } + } + if (italic) { + unibi_out(ui, unibi_enter_italics_mode); + } if (ui->rgb) { if (fg != -1) { - data->params[0].i = (fg >> 16) & 0xff; // red - data->params[1].i = (fg >> 8) & 0xff; // green - data->params[2].i = fg & 0xff; // blue - unibi_out(ui, data->unibi_ext.set_rgb_foreground); + UNIBI_SET_NUM_VAR(data->params[0], (fg >> 16) & 0xff); // red + UNIBI_SET_NUM_VAR(data->params[1], (fg >> 8) & 0xff); // green + UNIBI_SET_NUM_VAR(data->params[2], fg & 0xff); // blue + unibi_out_ext(ui, data->unibi_ext.set_rgb_foreground); } if (bg != -1) { - data->params[0].i = (bg >> 16) & 0xff; // red - data->params[1].i = (bg >> 8) & 0xff; // green - data->params[2].i = bg & 0xff; // blue - unibi_out(ui, data->unibi_ext.set_rgb_background); + UNIBI_SET_NUM_VAR(data->params[0], (bg >> 16) & 0xff); // red + UNIBI_SET_NUM_VAR(data->params[1], (bg >> 8) & 0xff); // green + UNIBI_SET_NUM_VAR(data->params[2], bg & 0xff); // blue + unibi_out_ext(ui, data->unibi_ext.set_rgb_background); } } else { if (fg != -1) { - data->params[0].i = fg; + UNIBI_SET_NUM_VAR(data->params[0], fg); unibi_out(ui, unibi_set_a_foreground); } if (bg != -1) { - data->params[0].i = bg; + UNIBI_SET_NUM_VAR(data->params[0], bg); unibi_out(ui, unibi_set_a_background); } } - if (attrs.bold) { - unibi_out(ui, unibi_enter_bold_mode); - } - if (attrs.italic) { - unibi_out(ui, unibi_enter_italics_mode); - } - if (attrs.underline || attrs.undercurl) { - unibi_out(ui, unibi_enter_underline_mode); - } - if (attrs.reverse) { - unibi_out(ui, unibi_enter_reverse_mode); + data->default_attr = fg == -1 && bg == -1 + && !bold && !italic && !underline && !undercurl && !reverse; +} + +static void final_column_wrap(UI *ui) +{ + TUIData *data = ui->data; + UGrid *grid = &data->grid; + if (grid->col == ui->width) { + grid->col = 0; + if (grid->row < MIN(ui->height, grid->height - 1)) { + grid->row++; + } } } +/// It is undocumented, but in the majority of terminals and terminal emulators +/// printing at the right margin does not cause an automatic wrap until the +/// next character is printed, holding the cursor in place until then. static void print_cell(UI *ui, UCell *ptr) { + TUIData *data = ui->data; + UGrid *grid = &data->grid; + if (!data->immediate_wrap_after_last_column) { + // Printing the next character finally advances the cursor. + final_column_wrap(ui); + } update_attrs(ui, ptr->attrs); out(ui, ptr->data, strlen(ptr->data)); + grid->col++; + if (data->immediate_wrap_after_last_column) { + // Printing at the right margin immediately advances the cursor. + final_column_wrap(ui); + } +} + +static bool cheap_to_print(UI *ui, int row, int col, int next) +{ + TUIData *data = ui->data; + UGrid *grid = &data->grid; + UCell *cell = grid->cells[row] + col; + while (next) { + next--; + if (attrs_differ(cell->attrs, data->print_attrs, ui->rgb)) { + if (data->default_attr) { + return false; + } + } + if (strlen(cell->data) > 1) { + return false; + } + cell++; + } + return true; +} + +/// This optimizes several cases where it is cheaper to do something other +/// than send a full cursor positioning control sequence. However, there are +/// some further optimizations that may seem obvious but that will not work. +/// +/// We cannot use VT (ASCII 0/11) for moving the cursor up, because VT means +/// move the cursor down on a DEC terminal. Similarly, on a DEC terminal FF +/// (ASCII 0/12) means the same thing and does not mean home. VT, CVT, and +/// TAB also stop at software-defined tabulation stops, not at a fixed set +/// of row/column positions. +static void cursor_goto(UI *ui, int row, int col) +{ + TUIData *data = ui->data; + UGrid *grid = &data->grid; + if (row == grid->row && col == grid->col) { + return; + } + if (0 == row && 0 == col) { + unibi_out(ui, unibi_cursor_home); + ugrid_goto(grid, row, col); + return; + } + if (0 == col ? col != grid->col : + row != grid->row ? false : + 1 == col ? 2 < grid->col && cheap_to_print(ui, grid->row, 0, col) : + 2 == col ? 5 < grid->col && cheap_to_print(ui, grid->row, 0, col) : + false) { + // Motion to left margin from anywhere else, or CR + printing chars is + // even less expensive than using BSes or CUB. + unibi_out(ui, unibi_carriage_return); + ugrid_goto(grid, grid->row, 0); + } else if (col > grid->col) { + int n = col - grid->col; + if (n <= (row == grid->row ? 4 : 2) + && cheap_to_print(ui, grid->row, grid->col, n)) { + UGRID_FOREACH_CELL(grid, grid->row, grid->row, + grid->col, col - 1, { + print_cell(ui, cell); + }); + } + } + if (row == grid->row) { + if (col < grid->col + // Deferred right margin wrap terminals have inconsistent ideas about + // where the cursor actually is during a deferred wrap. Relative + // motion calculations have OBOEs that cannot be compensated for, + // because two terminals that claim to be the same will implement + // different cursor positioning rules. + && (data->immediate_wrap_after_last_column || grid->col < ui->width)) { + int n = grid->col - col; + if (n <= 4) { // This might be just BS, so it is considered really cheap. + while (n--) { + unibi_out(ui, unibi_cursor_left); + } + } else { + UNIBI_SET_NUM_VAR(data->params[0], n); + unibi_out(ui, unibi_parm_left_cursor); + } + ugrid_goto(grid, row, col); + return; + } else if (col > grid->col) { + int n = col - grid->col; + if (n <= 2) { + while (n--) { + unibi_out(ui, unibi_cursor_right); + } + } else { + UNIBI_SET_NUM_VAR(data->params[0], n); + unibi_out(ui, unibi_parm_right_cursor); + } + ugrid_goto(grid, row, col); + return; + } + } + if (col == grid->col) { + if (row > grid->row) { + int n = row - grid->row; + if (n <= 4) { // This might be just LF, so it is considered really cheap. + while (n--) { + unibi_out(ui, unibi_cursor_down); + } + } else { + UNIBI_SET_NUM_VAR(data->params[0], n); + unibi_out(ui, unibi_parm_down_cursor); + } + ugrid_goto(grid, row, col); + return; + } else if (row < grid->row) { + int n = grid->row - row; + if (n <= 2) { + while (n--) { + unibi_out(ui, unibi_cursor_up); + } + } else { + UNIBI_SET_NUM_VAR(data->params[0], n); + unibi_out(ui, unibi_parm_up_cursor); + } + ugrid_goto(grid, row, col); + return; + } + } + unibi_goto(ui, row, col); + ugrid_goto(grid, row, col); } static void clear_region(UI *ui, int top, int bot, int left, int right) { TUIData *data = ui->data; UGrid *grid = &data->grid; + int saved_row = grid->row; + int saved_col = grid->col; bool cleared = false; - if (grid->bg == -1 && right == ui->width -1) { + bool nobg = ui->rgb ? grid->clear_attrs.rgb_bg_color == -1 + : grid->clear_attrs.cterm_bg_color == 0; + if (nobg && right == ui->width -1) { // Background is set to the default color and the right edge matches the // screen end, try to use terminal codes for clearing the requested area. - HlAttrs clear_attrs = EMPTY_ATTRS; - clear_attrs.foreground = grid->fg; - clear_attrs.background = grid->bg; - update_attrs(ui, clear_attrs); + update_attrs(ui, grid->clear_attrs); if (left == 0) { if (bot == ui->height - 1) { if (top == 0) { unibi_out(ui, unibi_clear_screen); + ugrid_goto(&data->grid, top, left); } else { - unibi_goto(ui, top, 0); + cursor_goto(ui, top, 0); unibi_out(ui, unibi_clr_eos); } cleared = true; @@ -415,8 +704,8 @@ static void clear_region(UI *ui, int top, int bot, int left, int right) if (!cleared) { // iterate through each line and clear with clr_eol - for (int row = top; row <= bot; ++row) { - unibi_goto(ui, row, left); + for (int row = top; row <= bot; row++) { + cursor_goto(ui, row, left); unibi_out(ui, unibi_clr_eol); } cleared = true; @@ -425,18 +714,14 @@ static void clear_region(UI *ui, int top, int bot, int left, int right) if (!cleared) { // could not clear using faster terminal codes, refresh the whole region - int currow = -1; UGRID_FOREACH_CELL(grid, top, bot, left, right, { - if (currow != row) { - unibi_goto(ui, row, col); - currow = row; - } + cursor_goto(ui, row, col); print_cell(ui, cell); }); } // restore cursor - unibi_goto(ui, grid->row, grid->col); + cursor_goto(ui, saved_row, saved_col); } static bool can_use_scroll(UI * ui) @@ -456,19 +741,19 @@ static void set_scroll_region(UI *ui) TUIData *data = ui->data; UGrid *grid = &data->grid; - data->params[0].i = grid->top; - data->params[1].i = grid->bot; + UNIBI_SET_NUM_VAR(data->params[0], grid->top); + UNIBI_SET_NUM_VAR(data->params[1], grid->bot); unibi_out(ui, unibi_change_scroll_region); if (grid->left != 0 || grid->right != ui->width - 1) { - unibi_out(ui, data->unibi_ext.enable_lr_margin); + unibi_out_ext(ui, data->unibi_ext.enable_lr_margin); if (data->can_set_lr_margin) { - data->params[0].i = grid->left; - data->params[1].i = grid->right; + UNIBI_SET_NUM_VAR(data->params[0], grid->left); + UNIBI_SET_NUM_VAR(data->params[1], grid->right); unibi_out(ui, unibi_set_lr_margin); } else { - data->params[0].i = grid->left; + UNIBI_SET_NUM_VAR(data->params[0], grid->left); unibi_out(ui, unibi_set_left_margin_parm); - data->params[0].i = grid->right; + UNIBI_SET_NUM_VAR(data->params[0], grid->right); unibi_out(ui, unibi_set_right_margin_parm); } } @@ -481,24 +766,24 @@ static void reset_scroll_region(UI *ui) UGrid *grid = &data->grid; if (0 <= data->unibi_ext.reset_scroll_region) { - unibi_out(ui, data->unibi_ext.reset_scroll_region); + unibi_out_ext(ui, data->unibi_ext.reset_scroll_region); } else { - data->params[0].i = 0; - data->params[1].i = ui->height - 1; + UNIBI_SET_NUM_VAR(data->params[0], 0); + UNIBI_SET_NUM_VAR(data->params[1], ui->height - 1); unibi_out(ui, unibi_change_scroll_region); } if (grid->left != 0 || grid->right != ui->width - 1) { if (data->can_set_lr_margin) { - data->params[0].i = 0; - data->params[1].i = ui->width - 1; + UNIBI_SET_NUM_VAR(data->params[0], 0); + UNIBI_SET_NUM_VAR(data->params[1], ui->width - 1); unibi_out(ui, unibi_set_lr_margin); } else { - data->params[0].i = 0; + UNIBI_SET_NUM_VAR(data->params[0], 0); unibi_out(ui, unibi_set_left_margin_parm); - data->params[0].i = ui->width - 1; + UNIBI_SET_NUM_VAR(data->params[0], ui->width - 1); unibi_out(ui, unibi_set_right_margin_parm); } - unibi_out(ui, data->unibi_ext.disable_lr_margin); + unibi_out_ext(ui, data->unibi_ext.disable_lr_margin); } unibi_goto(ui, grid->row, grid->col); } @@ -509,9 +794,9 @@ static void tui_resize(UI *ui, Integer width, Integer height) ugrid_resize(&data->grid, (int)width, (int)height); if (!got_winch) { // Try to resize the terminal window. - data->params[0].i = (int)height; - data->params[1].i = (int)width; - unibi_out(ui, data->unibi_ext.resize_screen); + UNIBI_SET_NUM_VAR(data->params[0], (int)height); + UNIBI_SET_NUM_VAR(data->params[1], (int)width); + unibi_out_ext(ui, data->unibi_ext.resize_screen); // DECSLPP does not reset the scroll region. if (data->scroll_region_is_full_screen) { reset_scroll_region(ui); @@ -526,6 +811,7 @@ static void tui_clear(UI *ui) TUIData *data = ui->data; UGrid *grid = &data->grid; ugrid_clear(grid); + kv_size(data->invalid_regions) = 0; clear_region(ui, grid->top, grid->bot, grid->left, grid->right); } @@ -539,9 +825,7 @@ static void tui_eol_clear(UI *ui) static void tui_cursor_goto(UI *ui, Integer row, Integer col) { - TUIData *data = ui->data; - ugrid_goto(&data->grid, (int)row, (int)col); - unibi_goto(ui, (int)row, (int)col); + cursor_goto(ui, (int)row, (int)col); } CursorShape tui_cursor_decode_shape(const char *shape_str) @@ -619,7 +903,7 @@ static void tui_mouse_on(UI *ui) { TUIData *data = ui->data; if (!data->mouse_enabled) { - unibi_out(ui, data->unibi_ext.enable_mouse); + unibi_out_ext(ui, data->unibi_ext.enable_mouse); data->mouse_enabled = true; } } @@ -628,7 +912,7 @@ static void tui_mouse_off(UI *ui) { TUIData *data = ui->data; if (data->mouse_enabled) { - unibi_out(ui, data->unibi_ext.disable_mouse); + unibi_out_ext(ui, data->unibi_ext.disable_mouse); data->mouse_enabled = false; } } @@ -641,51 +925,24 @@ static void tui_set_mode(UI *ui, ModeShape mode) TUIData *data = ui->data; cursorentry_T c = data->cursor_shapes[mode]; int shape = c.shape; - unibi_var_t vars[26 + 26] = { { 0 } }; - - // Support changing cursor shape on some popular terminals. - const char *vte_version = os_getenv("VTE_VERSION"); if (c.id != 0 && ui->rgb) { int attr = syn_id2attr(c.id); if (attr > 0) { - attrentry_T *aep = syn_cterm_attr2entry(attr); - data->params[0].i = aep->rgb_bg_color; - unibi_out(ui, data->unibi_ext.set_cursor_color); + HlAttrs *aep = syn_cterm_attr2entry(attr); + UNIBI_SET_NUM_VAR(data->params[0], aep->rgb_bg_color); + unibi_out_ext(ui, data->unibi_ext.set_cursor_color); } } - if (data->term == kTermKonsole) { - // Konsole uses a proprietary escape code to set the cursor shape - // and does not support DECSCUSR. - switch (shape) { - case SHAPE_BLOCK: shape = 0; break; - case SHAPE_VER: shape = 1; break; - case SHAPE_HOR: shape = 2; break; - default: WLOG("Unknown shape value %d", shape); break; - } - data->params[0].i = shape; - data->params[1].i = (c.blinkon != 0); - - unibi_format(vars, vars + 26, - TMUX_WRAP("\x1b]50;CursorShape=%p1%d;BlinkingCursorEnabled=%p2%d\x07"), - data->params, out, ui, NULL, NULL); - } else if (!vte_version || atoi(vte_version) >= 3900) { - // Assume that the terminal supports DECSCUSR unless it is an - // old VTE based terminal. This should not get wrapped for tmux, - // which will handle it via its Ss/Se terminfo extension - usually - // according to its terminal-overrides. - - switch (shape) { - case SHAPE_BLOCK: shape = 1; break; - case SHAPE_HOR: shape = 3; break; - case SHAPE_VER: shape = 5; break; - default: WLOG("Unknown shape value %d", shape); break; - } - data->params[0].i = shape + (int)(c.blinkon == 0); - unibi_format(vars, vars + 26, "\x1b[%p1%d q", - data->params, out, ui, NULL, NULL); + switch (shape) { + case SHAPE_BLOCK: shape = 1; break; + case SHAPE_HOR: shape = 3; break; + case SHAPE_VER: shape = 5; break; + default: WLOG("Unknown shape value %d", shape); break; } + UNIBI_SET_NUM_VAR(data->params[0], shape + (int)(c.blinkon == 0)); + unibi_out_ext(ui, data->unibi_ext.set_cursor_style); } /// @param mode editor mode @@ -715,6 +972,8 @@ static void tui_scroll(UI *ui, Integer count) ugrid_scroll(grid, (int)count, &clear_top, &clear_bot); if (can_use_scroll(ui)) { + int saved_row = grid->row; + int saved_col = grid->col; bool scroll_clears_to_current_colour = unibi_get_bool(data->ut, unibi_back_color_erase); @@ -722,27 +981,24 @@ static void tui_scroll(UI *ui, Integer count) if (!data->scroll_region_is_full_screen) { set_scroll_region(ui); } - unibi_goto(ui, grid->top, grid->left); + cursor_goto(ui, grid->top, grid->left); // also set default color attributes or some terminals can become funny if (scroll_clears_to_current_colour) { - HlAttrs clear_attrs = EMPTY_ATTRS; - clear_attrs.foreground = grid->fg; - clear_attrs.background = grid->bg; - update_attrs(ui, clear_attrs); + update_attrs(ui, grid->clear_attrs); } if (count > 0) { if (count == 1) { unibi_out(ui, unibi_delete_line); } else { - data->params[0].i = (int)count; + UNIBI_SET_NUM_VAR(data->params[0], (int)count); unibi_out(ui, unibi_parm_delete_line); } } else { if (count == -1) { unibi_out(ui, unibi_insert_line); } else { - data->params[0].i = -(int)count; + UNIBI_SET_NUM_VAR(data->params[0], -(int)count); unibi_out(ui, unibi_parm_insert_line); } } @@ -751,11 +1007,11 @@ static void tui_scroll(UI *ui, Integer count) if (!data->scroll_region_is_full_screen) { reset_scroll_region(ui); } - unibi_goto(ui, grid->row, grid->col); + cursor_goto(ui, saved_row, saved_col); if (!scroll_clears_to_current_colour) { - // This is required because scrolling will leave wrong background in the - // cleared area on non-bge terminals. + // Scrolling will leave wrong background in the cleared area on non-BCE + // terminals. Update the cleared area. clear_region(ui, clear_top, clear_bot, grid->left, grid->right); } } else { @@ -772,7 +1028,15 @@ static void tui_highlight_set(UI *ui, HlAttrs attrs) static void tui_put(UI *ui, String text) { TUIData *data = ui->data; - print_cell(ui, ugrid_put(&data->grid, (uint8_t *)text.data, text.size)); + UGrid *grid = &data->grid; + UCell *cell; + + cell = ugrid_put(&data->grid, (uint8_t *)text.data, text.size); + // ugrid_put does not advance the cursor correctly, as the actual terminal + // will when we print. Its cursor motion model is simplistic and wrong. So + // we have to undo what it has just done before doing it right. + grid->col--; + print_cell(ui, cell); } static void tui_bell(UI *ui) @@ -785,19 +1049,16 @@ static void tui_visual_bell(UI *ui) unibi_out(ui, unibi_flash_screen); } -static void tui_update_fg(UI *ui, Integer fg) -{ - ((TUIData *)ui->data)->grid.fg = (int)fg; -} - -static void tui_update_bg(UI *ui, Integer bg) +static void tui_default_colors_set(UI *ui, Integer rgb_fg, Integer rgb_bg, + Integer rgb_sp, + Integer cterm_fg, Integer cterm_bg) { - ((TUIData *)ui->data)->grid.bg = (int)bg; -} - -static void tui_update_sp(UI *ui, Integer sp) -{ - // Do nothing; 'special' color is for GUI only + UGrid *grid = &((TUIData *)ui->data)->grid; + grid->clear_attrs.rgb_fg_color = (int)rgb_fg; + grid->clear_attrs.rgb_bg_color = (int)rgb_bg; + grid->clear_attrs.rgb_sp_color = (int)rgb_sp; + grid->clear_attrs.cterm_fg_color = (int)cterm_fg; + grid->clear_attrs.cterm_bg_color = (int)cterm_bg; } static void tui_flush(UI *ui) @@ -807,7 +1068,7 @@ static void tui_flush(UI *ui) size_t nrevents = loop_size(data->loop); if (nrevents > TOO_MANY_EVENTS) { - ILOG("TUI event-queue flooded (thread_events=%zu); purging", nrevents); + WLOG("TUI event-queue flooded (thread_events=%zu); purging", nrevents); // Back-pressure: UI events may accumulate much faster than the terminal // device can serve them. Even if SIGINT/CTRL-C is received, user must still // wait for the TUI event-queue to drain, and if there are ~millions of @@ -817,21 +1078,39 @@ static void tui_flush(UI *ui) tui_busy_stop(ui); // avoid hidden cursor } + int saved_row = grid->row; + int saved_col = grid->col; + while (kv_size(data->invalid_regions)) { Rect r = kv_pop(data->invalid_regions); - int currow = -1; + assert(r.bot < grid->height && r.right < grid->width); UGRID_FOREACH_CELL(grid, r.top, r.bot, r.left, r.right, { - if (currow != row) { - unibi_goto(ui, row, col); - currow = row; - } + cursor_goto(ui, row, col); print_cell(ui, cell); }); } - unibi_goto(ui, grid->row, grid->col); + cursor_goto(ui, saved_row, saved_col); + + flush_buf(ui); +} - flush_buf(ui, true); +/// Dumps termcap info to the messages area, if 'verbose' >= 3. +static void show_termcap_event(void **argv) +{ + if (p_verbose < 3) { + return; + } + const unibi_term *const ut = argv[0]; + if (!ut) { + abort(); + } + verbose_enter(); + // XXX: (future) if unibi_term is modified (e.g. after a terminal + // query-response) this is a race condition. + terminfo_info_msg(ut); + verbose_leave(); + verbose_stop(); // flush now } #ifdef UNIX @@ -849,6 +1128,7 @@ static void suspend_event(void **argv) loop_poll_events(data->loop, -1); } tui_terminal_start(ui); + tui_terminal_after_startup(ui); if (enable_mouse) { tui_mouse_on(ui); } @@ -886,10 +1166,13 @@ static void tui_set_icon(UI *ui, String icon) { } -// NB: if we start to use this, the ui_bridge must be updated -// to make a copy for the tui thread -static void tui_event(UI *ui, char *name, Array args, bool *args_consumed) +static void tui_option_set(UI *ui, String name, Object value) { + TUIData *data = ui->data; + if (strequal(name.data, "termguicolors")) { + ui->rgb = value.data.boolean; + invalidate(ui, 0, data->grid.height-1, 0, data->grid.width-1); + } } static void invalidate(UI *ui, int top, int bot, int left, int right) @@ -978,39 +1261,61 @@ end: static void unibi_goto(UI *ui, int row, int col) { TUIData *data = ui->data; - data->params[0].i = row; - data->params[1].i = col; + UNIBI_SET_NUM_VAR(data->params[0], row); + UNIBI_SET_NUM_VAR(data->params[1], col); unibi_out(ui, unibi_cursor_address); } +#define UNIBI_OUT(fn) \ + do { \ + TUIData *data = ui->data; \ + const char *str = NULL; \ + if (unibi_index >= 0) { \ + str = fn(data->ut, (unsigned)unibi_index); \ + } \ + if (str) { \ + unibi_var_t vars[26 + 26]; \ + size_t orig_pos = data->bufpos; \ + \ + memset(&vars, 0, sizeof(vars)); \ + data->cork = true; \ +retry: \ + unibi_format(vars, vars + 26, str, data->params, out, ui, NULL, NULL); \ + if (data->overflow) { \ + data->bufpos = orig_pos; \ + flush_buf(ui); \ + goto retry; \ + } \ + data->cork = false; \ + } \ + } while (0) static void unibi_out(UI *ui, int unibi_index) { - TUIData *data = ui->data; - - const char *str = NULL; - - if (unibi_index >= 0) { - if (unibi_index < unibi_string_begin_) { - str = unibi_get_ext_str(data->ut, (unsigned)unibi_index); - } else { - str = unibi_get_str(data->ut, (unsigned)unibi_index); - } - } - - if (str) { - unibi_var_t vars[26 + 26] = {{0}}; - unibi_format(vars, vars + 26, str, data->params, out, ui, NULL, NULL); - } + UNIBI_OUT(unibi_get_str); +} +static void unibi_out_ext(UI *ui, int unibi_index) +{ + UNIBI_OUT(unibi_get_ext_str); } +#undef UNIBI_OUT static void out(void *ctx, const char *str, size_t len) { UI *ui = ctx; TUIData *data = ui->data; - size_t available = data->bufsize - data->bufpos; + size_t available = sizeof(data->buf) - data->bufpos; + + if (data->cork && data->overflow) { + return; + } if (len > available) { - flush_buf(ui, false); + if (data->cork) { + data->overflow = true; + return; + } else { + flush_buf(ui); + } } memcpy(data->buf + data->bufpos, str, len); @@ -1025,188 +1330,460 @@ static void unibi_set_if_empty(unibi_term *ut, enum unibi_string str, } } -static TermType detect_term(const char *term, const char *colorterm) +static int unibi_find_ext_str(unibi_term *ut, const char *name) { - if (STARTS_WITH(term, "rxvt")) { - return kTermRxvt; - } - if (os_getenv("KONSOLE_PROFILE_NAME") || os_getenv("KONSOLE_DBUS_SESSION")) { - return kTermKonsole; - } - const char *termprg = os_getenv("TERM_PROGRAM"); - if (termprg && strstr(termprg, "iTerm.app")) { - return kTermiTerm; - } - if (colorterm && strstr(colorterm, "gnome-terminal")) { - return kTermGnome; - } - if (STARTS_WITH(term, "xterm")) { - return kTermXTerm; - } - if (STARTS_WITH(term, "dtterm")) { - return kTermDTTerm; - } - if (STARTS_WITH(term, "teraterm")) { - return kTermTeraTerm; + size_t max = unibi_count_ext_str(ut); + for (size_t i = 0; i < max; i++) { + const char * n = unibi_get_ext_str_name(ut, i); + if (n && 0 == strcmp(n, name)) { + return (int)i; + } } - return kTermUnknown; + return -1; } -static void fix_terminfo(TUIData *data) +/// Patches the terminfo records after loading from system or built-in db. +/// Several entries in terminfo are known to be deficient or outright wrong; +/// and several terminal emulators falsely announce incorrect terminal types. +static void patch_terminfo_bugs(TUIData *data, const char *term, + const char *colorterm, long vte_version, + bool konsole, bool iterm_env) { unibi_term *ut = data->ut; - is_tmux = os_getenv("TMUX") != NULL; + const char * xterm_version = os_getenv("XTERM_VERSION"); +#if 0 // We don't need to identify this specifically, for now. + bool roxterm = !!os_getenv("ROXTERM_ID"); +#endif + bool xterm = terminfo_is_term_family(term, "xterm"); + bool linuxvt = terminfo_is_term_family(term, "linux"); + bool rxvt = terminfo_is_term_family(term, "rxvt"); + bool teraterm = terminfo_is_term_family(term, "teraterm"); + bool putty = terminfo_is_term_family(term, "putty"); + bool screen = terminfo_is_term_family(term, "screen"); + bool tmux = terminfo_is_term_family(term, "tmux") || !!os_getenv("TMUX"); + bool st = terminfo_is_term_family(term, "st"); + bool gnome = terminfo_is_term_family(term, "gnome") + || terminfo_is_term_family(term, "vte"); + bool iterm = terminfo_is_term_family(term, "iterm") + || terminfo_is_term_family(term, "iterm2") + || terminfo_is_term_family(term, "iTerm.app") + || terminfo_is_term_family(term, "iTerm2.app"); + // None of the following work over SSH; see :help TERM . + bool iterm_pretending_xterm = xterm && iterm_env; + bool konsole_pretending_xterm = xterm && konsole; + bool gnome_pretending_xterm = xterm && colorterm + && strstr(colorterm, "gnome-terminal"); + bool mate_pretending_xterm = xterm && colorterm + && strstr(colorterm, "mate-terminal"); + bool true_xterm = xterm && !!xterm_version; + + char *fix_normal = (char *)unibi_get_str(ut, unibi_cursor_normal); + if (fix_normal) { + if (STARTS_WITH(fix_normal, "\x1b[?12l")) { + // terminfo typically includes DECRST 12 as part of setting up the + // normal cursor, which interferes with the user's control via + // set_cursor_style. When DECRST 12 is present, skip over it, but honor + // the rest of the cnorm setting. + fix_normal += sizeof "\x1b[?12l" - 1; + unibi_set_str(ut, unibi_cursor_normal, fix_normal); + } + if (linuxvt + && strlen(fix_normal) >= (sizeof LINUXSET0C - 1) + && !memcmp(strchr(fix_normal, 0) - (sizeof LINUXSET0C - 1), + LINUXSET0C, sizeof LINUXSET0C - 1)) { + // The Linux terminfo entry similarly includes a Linux-idiosyncractic + // cursor shape reset in cnorm, which similarly interferes with + // set_cursor_style. + fix_normal[strlen(fix_normal) - (sizeof LINUXSET0C - 1)] = 0; + } + } + char *fix_invisible = (char *)unibi_get_str(ut, unibi_cursor_invisible); + if (fix_invisible) { + if (linuxvt + && strlen(fix_invisible) >= (sizeof LINUXSET1C - 1) + && !memcmp(strchr(fix_invisible, 0) - (sizeof LINUXSET1C - 1), + LINUXSET1C, sizeof LINUXSET1C - 1)) { + // The Linux terminfo entry similarly includes a Linux-idiosyncractic + // cursor shape reset in cinvis, which similarly interferes with + // set_cursor_style. + fix_invisible[strlen(fix_invisible) - (sizeof LINUXSET1C - 1)] = 0; + } + } - const char *term = os_getenv("TERM"); - const char *colorterm = os_getenv("COLORTERM"); - if (!term) { - goto end; + if (!true_xterm) { + // Cannot trust terminfo; safer to disable BCE. #7624 + unibi_set_bool(ut, unibi_back_color_erase, false); } - data->term = detect_term(term, colorterm); - if (data->term == kTermRxvt) { - unibi_set_if_empty(ut, unibi_exit_attribute_mode, "\x1b[m\x1b(B"); - unibi_set_if_empty(ut, unibi_flash_screen, "\x1b[?5h$<20/>\x1b[?5l"); + if (xterm) { + // Termit, LXTerminal, GTKTerm2, GNOME Terminal, MATE Terminal, roxterm, + // and EvilVTE falsely claim to be xterm and do not support important xterm + // control sequences that we use. In an ideal world, these would have + // their own terminal types and terminfo entries, like PuTTY does, and not + // claim to be xterm. Or they would mimic xterm properly enough to be + // treatable as xterm. + + // 2017-04 terminfo.src lacks these. genuine Xterm has them, as have + // the false claimants. + unibi_set_if_empty(ut, unibi_to_status_line, "\x1b]0;"); + unibi_set_if_empty(ut, unibi_from_status_line, "\x07"); + unibi_set_if_empty(ut, unibi_set_tb_margin, "\x1b[%i%p1%d;%p2%dr"); + + if (true_xterm) { + // 2017-04 terminfo.src lacks these. genuine Xterm has them. + unibi_set_if_empty(ut, unibi_set_lr_margin, "\x1b[%i%p1%d;%p2%ds"); + unibi_set_if_empty(ut, unibi_set_left_margin_parm, "\x1b[%i%p1%ds"); + unibi_set_if_empty(ut, unibi_set_right_margin_parm, "\x1b[%i;%p2%ds"); + } + if (true_xterm + || iterm_pretending_xterm + || gnome_pretending_xterm + || konsole_pretending_xterm) { + // Apple's outdated copy of terminfo.src for MacOS lacks these. + // genuine Xterm and three false claimants have them. + unibi_set_if_empty(ut, unibi_enter_italics_mode, "\x1b[3m"); + unibi_set_if_empty(ut, unibi_exit_italics_mode, "\x1b[23m"); + } + } else if (rxvt) { + // 2017-04 terminfo.src lacks these. Unicode rxvt has them. unibi_set_if_empty(ut, unibi_enter_italics_mode, "\x1b[3m"); + unibi_set_if_empty(ut, unibi_exit_italics_mode, "\x1b[23m"); unibi_set_if_empty(ut, unibi_to_status_line, "\x1b]2"); - } else if (data->term == kTermXTerm) { - unibi_set_if_empty(ut, unibi_to_status_line, "\x1b]0;"); - } else if (STARTS_WITH(term, "screen") || STARTS_WITH(term, "tmux")) { + unibi_set_if_empty(ut, unibi_from_status_line, "\x07"); + // 2017-04 terminfo.src has older control sequences. + unibi_set_str(ut, unibi_enter_ca_mode, "\x1b[?1049h"); + unibi_set_str(ut, unibi_exit_ca_mode, "\x1b[?1049l"); + } else if (screen) { + // per the screen manual; 2017-04 terminfo.src lacks these. unibi_set_if_empty(ut, unibi_to_status_line, "\x1b_"); unibi_set_if_empty(ut, unibi_from_status_line, "\x1b\\"); - } - - if (data->term == kTermXTerm || data->term == kTermRxvt) { - const char *normal = unibi_get_str(ut, unibi_cursor_normal); - if (!normal) { - unibi_set_str(ut, unibi_cursor_normal, "\x1b[?25h"); - } else if (STARTS_WITH(normal, "\x1b[?12l")) { - // terminfo typically includes DECRST 12 as part of setting up the normal - // cursor, which interferes with the user's control via - // NVIM_TUI_ENABLE_CURSOR_SHAPE. When DECRST 12 is present, skip over - // it, but honor the rest of the TI setting. - unibi_set_str(ut, unibi_cursor_normal, normal + strlen("\x1b[?12l")); - } - unibi_set_if_empty(ut, unibi_cursor_invisible, "\x1b[?25l"); - unibi_set_if_empty(ut, unibi_flash_screen, "\x1b[?5h$<100/>\x1b[?5l"); - unibi_set_if_empty(ut, unibi_exit_attribute_mode, "\x1b(B\x1b[m"); + } else if (tmux) { + unibi_set_if_empty(ut, unibi_to_status_line, "\x1b_"); + unibi_set_if_empty(ut, unibi_from_status_line, "\x1b\\"); + } else if (terminfo_is_term_family(term, "interix")) { + // 2017-04 terminfo.src lacks this. + unibi_set_if_empty(ut, unibi_carriage_return, "\x0d"); + } else if (linuxvt) { + // Apple's outdated copy of terminfo.src for MacOS lacks these. + unibi_set_if_empty(ut, unibi_parm_up_cursor, "\x1b[%p1%dA"); + unibi_set_if_empty(ut, unibi_parm_down_cursor, "\x1b[%p1%dB"); + unibi_set_if_empty(ut, unibi_parm_right_cursor, "\x1b[%p1%dC"); + unibi_set_if_empty(ut, unibi_parm_left_cursor, "\x1b[%p1%dD"); + } else if (putty) { + // No bugs in the vanilla terminfo for our purposes. + } else if (iterm) { + // 2017-04 terminfo.src has older control sequences. + unibi_set_str(ut, unibi_enter_ca_mode, "\x1b[?1049h"); + unibi_set_str(ut, unibi_exit_ca_mode, "\x1b[?1049l"); + // 2017-04 terminfo.src lacks these. unibi_set_if_empty(ut, unibi_set_tb_margin, "\x1b[%i%p1%d;%p2%dr"); - unibi_set_if_empty(ut, unibi_set_lr_margin, "\x1b[%i%p1%d;%p2%ds"); - unibi_set_if_empty(ut, unibi_set_left_margin_parm, "\x1b[%i%p1%ds"); - unibi_set_if_empty(ut, unibi_set_right_margin_parm, "\x1b[%i;%p2%ds"); - unibi_set_if_empty(ut, unibi_change_scroll_region, "\x1b[%i%p1%d;%p2%dr"); - unibi_set_if_empty(ut, unibi_clear_screen, "\x1b[H\x1b[2J"); - unibi_set_if_empty(ut, unibi_from_status_line, "\x07"); - unibi_set_bool(ut, unibi_back_color_erase, true); + unibi_set_if_empty(ut, unibi_orig_pair, "\x1b[39;49m"); + unibi_set_if_empty(ut, unibi_enter_dim_mode, "\x1b[2m"); + unibi_set_if_empty(ut, unibi_enter_italics_mode, "\x1b[3m"); + unibi_set_if_empty(ut, unibi_exit_italics_mode, "\x1b[23m"); + unibi_set_if_empty(ut, unibi_exit_underline_mode, "\x1b[24m"); + unibi_set_if_empty(ut, unibi_exit_standout_mode, "\x1b[27m"); + } else if (st) { + // No bugs in the vanilla terminfo for our purposes. } - data->unibi_ext.enable_lr_margin = (int)unibi_add_ext_str(ut, NULL, - "\x1b[?69h"); - data->unibi_ext.disable_lr_margin = (int)unibi_add_ext_str(ut, NULL, - "\x1b[?69l"); - - data->unibi_ext.enable_bracketed_paste = (int)unibi_add_ext_str(ut, NULL, - "\x1b[?2004h"); - data->unibi_ext.disable_bracketed_paste = (int)unibi_add_ext_str(ut, NULL, - "\x1b[?2004l"); +// At this time (2017-07-12) it seems like all terminals that support 256 +// color codes can use semicolons in the terminal code and be fine. +// However, this is not correct according to the spec. So to reward those +// terminals that also support colons, we output the code that way on these +// specific ones. - data->unibi_ext.enable_focus_reporting = (int)unibi_add_ext_str(ut, NULL, - "\x1b[?1004h"); - data->unibi_ext.disable_focus_reporting = (int)unibi_add_ext_str(ut, NULL, - "\x1b[?1004l"); +// using colons like ISO 8613-6:1994/ITU T.416:1993 says. +#define XTERM_SETAF_256_COLON \ + "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38:5:%p1%d%;m" +#define XTERM_SETAB_256_COLON \ + "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48:5:%p1%d%;m" -#define XTERM_SETAF \ +#define XTERM_SETAF_256 \ "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m" -#define XTERM_SETAB \ +#define XTERM_SETAB_256 \ "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m" +#define XTERM_SETAF_16 \ + "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e39%;m" +#define XTERM_SETAB_16 \ + "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e39%;m" + + // Terminals with 256-colour SGR support despite what terminfo says. + if (unibi_get_num(ut, unibi_max_colors) < 256) { + // See http://fedoraproject.org/wiki/Features/256_Color_Terminals + if (true_xterm || iterm || iterm_pretending_xterm) { + unibi_set_num(ut, unibi_max_colors, 256); + unibi_set_str(ut, unibi_set_a_foreground, XTERM_SETAF_256_COLON); + unibi_set_str(ut, unibi_set_a_background, XTERM_SETAB_256_COLON); + } else if (konsole || xterm || gnome || rxvt || st || putty + || linuxvt // Linux 4.8+ supports 256-colour SGR. + || mate_pretending_xterm || gnome_pretending_xterm + || tmux + || (colorterm && strstr(colorterm, "256")) + || (term && strstr(term, "256"))) { + unibi_set_num(ut, unibi_max_colors, 256); + unibi_set_str(ut, unibi_set_a_foreground, XTERM_SETAF_256); + unibi_set_str(ut, unibi_set_a_background, XTERM_SETAB_256); + } + } + // Terminals with 16-colour SGR support despite what terminfo says. + if (unibi_get_num(ut, unibi_max_colors) < 16) { + if (colorterm) { + unibi_set_num(ut, unibi_max_colors, 16); + unibi_set_if_empty(ut, unibi_set_a_foreground, XTERM_SETAF_16); + unibi_set_if_empty(ut, unibi_set_a_background, XTERM_SETAB_16); + } + } - if ((colorterm && strstr(colorterm, "256")) - || STARTS_WITH(term, "linux") - || strstr(term, "256") - || strstr(term, "xterm")) { - // Linux 4.8+ supports 256-color SGR, but terminfo has 8-color setaf/setab. - // Assume TERM=~xterm|linux or COLORTERM=~256 supports 256 colors. - unibi_set_num(ut, unibi_max_colors, 256); - unibi_set_str(ut, unibi_set_a_foreground, XTERM_SETAF); - unibi_set_str(ut, unibi_set_a_background, XTERM_SETAB); + // Blacklist of terminals that cannot be trusted to report DECSCUSR support. + if (!(st || (vte_version != 0 && vte_version < 3900) || konsole)) { + data->unibi_ext.reset_cursor_style = unibi_find_ext_str(ut, "Se"); + data->unibi_ext.set_cursor_style = unibi_find_ext_str(ut, "Ss"); } + // Dickey ncurses terminfo includes Ss/Se capabilities since 2011-07-14. So + // adding them to terminal types, that have such control sequences but lack + // the correct terminfo entries, is a fixup, not an augmentation. + if (-1 == data->unibi_ext.set_cursor_style) { + // DECSCUSR (cursor shape) sequence is widely supported by several terminal + // types. https://github.com/gnachman/iTerm2/pull/92 + // xterm extension: vertical bar + if (!konsole + && ((xterm && !vte_version) // anything claiming xterm compat + // per MinTTY 0.4.3-1 release notes from 2009 + || putty + // per https://bugzilla.gnome.org/show_bug.cgi?id=720821 + || (vte_version >= 3900) + || tmux // per tmux manual page + // https://lists.gnu.org/archive/html/screen-devel/2013-03/msg00000.html + || screen + || st // #7641 + || rxvt // per command.C + // per analysis of VT100Terminal.m + || iterm || iterm_pretending_xterm + || teraterm // per TeraTerm "Supported Control Functions" doco + // Some linux-type terminals implement the xterm extension. + // Example: console-terminal-emulator from the nosh toolset. + || (linuxvt + && (xterm_version || (vte_version > 0) || colorterm)))) { + data->unibi_ext.set_cursor_style = + (int)unibi_add_ext_str(ut, "Ss", "\x1b[%p1%d q"); + if (-1 == data->unibi_ext.reset_cursor_style) { + data->unibi_ext.reset_cursor_style = (int)unibi_add_ext_str(ut, "Se", + ""); + } + unibi_set_ext_str(ut, (size_t)data->unibi_ext.reset_cursor_style, + "\x1b[ q"); + } else if (linuxvt) { + // Linux uses an idiosyncratic escape code to set the cursor shape and + // does not support DECSCUSR. + // See http://linuxgazette.net/137/anonymous.html for more info + data->unibi_ext.set_cursor_style = (int)unibi_add_ext_str(ut, "Ss", + "\x1b[?" + "%?" + // The parameter passed to Ss is the DECSCUSR parameter, so the + // terminal capability has to translate into the Linux idiosyncratic + // parameter. + // + // linuxvt only supports block and underline. It is also only + // possible to have a steady block (no steady underline) + "%p1%{2}%<" "%t%{8}" // blink block + "%e%p1%{2}%=" "%t%{112}" // steady block + "%e%p1%{3}%=" "%t%{4}" // blink underline (set to half block) + "%e%p1%{4}%=" "%t%{4}" // steady underline + "%e%p1%{5}%=" "%t%{2}" // blink bar (set to underline) + "%e%p1%{6}%=" "%t%{2}" // steady bar + "%e%{0}" // anything else + "%;" "%dc"); + if (-1 == data->unibi_ext.reset_cursor_style) { + data->unibi_ext.reset_cursor_style = (int)unibi_add_ext_str(ut, "Se", + ""); + } + unibi_set_ext_str(ut, (size_t)data->unibi_ext.reset_cursor_style, + "\x1b[?c"); + } else if (konsole) { + // Konsole uses an idiosyncratic escape code to set the cursor shape and + // does not support DECSCUSR. This makes Konsole set up and apply a + // nonce profile, which has side-effects on temporary font resizing. + // In an ideal world, Konsole would just support DECSCUSR. + data->unibi_ext.set_cursor_style = (int)unibi_add_ext_str(ut, "Ss", + TMUX_WRAP(tmux, "\x1b]50;CursorShape=%?" + "%p1%{3}%<" "%t%{0}" // block + "%e%p1%{5}%<" "%t%{2}" // underline + "%e%{1}" // everything else is bar + "%;%d;BlinkingCursorEnabled=%?" + "%p1%{1}%<" "%t%{1}" // Fortunately if we exclude zero as special, + "%e%p1%{1}%&" // in all other cases we can treat bit #0 as a flag. + "%;%d\x07")); + if (-1 == data->unibi_ext.reset_cursor_style) { + data->unibi_ext.reset_cursor_style = (int)unibi_add_ext_str(ut, "Se", + ""); + } + unibi_set_ext_str(ut, (size_t)data->unibi_ext.reset_cursor_style, + "\x1b]50;\x07"); + } + } +} + +/// This adds stuff that is not in standard terminfo as extended unibilium +/// capabilities. +static void augment_terminfo(TUIData *data, const char *term, + const char *colorterm, long vte_version, bool konsole, bool iterm_env) +{ + unibi_term *ut = data->ut; + bool xterm = terminfo_is_term_family(term, "xterm"); + bool dtterm = terminfo_is_term_family(term, "dtterm"); + bool rxvt = terminfo_is_term_family(term, "rxvt"); + bool teraterm = terminfo_is_term_family(term, "teraterm"); + bool putty = terminfo_is_term_family(term, "putty"); + bool screen = terminfo_is_term_family(term, "screen"); + bool tmux = terminfo_is_term_family(term, "tmux") || !!os_getenv("TMUX"); + bool iterm = terminfo_is_term_family(term, "iterm") + || terminfo_is_term_family(term, "iterm2") + || terminfo_is_term_family(term, "iTerm.app") + || terminfo_is_term_family(term, "iTerm2.app"); + // None of the following work over SSH; see :help TERM . + bool iterm_pretending_xterm = xterm && iterm_env; + + const char * xterm_version = os_getenv("XTERM_VERSION"); + bool true_xterm = xterm && !!xterm_version; + // Only define this capability for terminal types that we know understand it. - if (data->term == kTermDTTerm // originated this extension - || data->term == kTermXTerm // per xterm ctlseqs doc - || data->term == kTermKonsole // per commentary in VT102Emulation.cpp - || data->term == kTermTeraTerm // per "Supported Control Functions" doc - || data->term == kTermRxvt) { // per command.C - data->unibi_ext.resize_screen = (int)unibi_add_ext_str(ut, NULL, + if (dtterm // originated this extension + || xterm // per xterm ctlseqs doco + || konsole // per commentary in VT102Emulation.cpp + || teraterm // per TeraTerm "Supported Control Functions" doco + || rxvt) { // per command.C + data->unibi_ext.resize_screen = (int)unibi_add_ext_str(ut, + "ext.resize_screen", "\x1b[8;%p1%d;%p2%dt"); } - - if (data->term == kTermXTerm || data->term == kTermRxvt) { - data->unibi_ext.reset_scroll_region = (int)unibi_add_ext_str(ut, NULL, + if (putty || xterm || rxvt) { + data->unibi_ext.reset_scroll_region = (int)unibi_add_ext_str(ut, + "ext.reset_scroll_region", "\x1b[r"); } -end: - // Fill some empty slots with common terminal strings - if (data->term == kTermiTerm) { + // Dickey ncurses terminfo does not include the setrgbf and setrgbb + // capabilities, proposed by Rüdiger Sonderfeld on 2013-10-15. Adding + // them here when terminfo lacks them is an augmentation, not a fixup. + // https://gist.github.com/XVilka/8346728 + + // At this time (2017-07-12) it seems like all terminals that support rgb + // color codes can use semicolons in the terminal code and be fine. + // However, this is not correct according to the spec. So to reward those + // terminals that also support colons, we output the code that way on these + // specific ones. + + // can use colons like ISO 8613-6:1994/ITU T.416:1993 says. + bool has_colon_rgb = !tmux && !screen + && !vte_version // VTE colon-support has a big memory leak. #7573 + && (iterm || iterm_pretending_xterm // per VT100Terminal.m + // per http://invisible-island.net/xterm/xterm.log.html#xterm_282 + || true_xterm); + + data->unibi_ext.set_rgb_foreground = unibi_find_ext_str(ut, "setrgbf"); + if (-1 == data->unibi_ext.set_rgb_foreground) { + if (has_colon_rgb) { + data->unibi_ext.set_rgb_foreground = (int)unibi_add_ext_str(ut, "setrgbf", + "\x1b[38:2:%p1%d:%p2%d:%p3%dm"); + } else { + data->unibi_ext.set_rgb_foreground = (int)unibi_add_ext_str(ut, "setrgbf", + "\x1b[38;2;%p1%d;%p2%d;%p3%dm"); + } + } + data->unibi_ext.set_rgb_background = unibi_find_ext_str(ut, "setrgbb"); + if (-1 == data->unibi_ext.set_rgb_background) { + if (has_colon_rgb) { + data->unibi_ext.set_rgb_background = (int)unibi_add_ext_str(ut, "setrgbb", + "\x1b[48:2:%p1%d:%p2%d:%p3%dm"); + } else { + data->unibi_ext.set_rgb_background = (int)unibi_add_ext_str(ut, "setrgbb", + "\x1b[48;2;%p1%d;%p2%d;%p3%dm"); + } + } + + if (iterm || iterm_pretending_xterm) { + // FIXME: Bypassing tmux like this affects the cursor colour globally, in + // all panes, which is not particularly desirable. A better approach + // would use a tmux control sequence and an extra if(screen) test. data->unibi_ext.set_cursor_color = (int)unibi_add_ext_str( - ut, NULL, TMUX_WRAP("\033]Pl%p1%06x\033\\")); - } else { + ut, NULL, TMUX_WRAP(tmux, "\033]Pl%p1%06x\033\\")); + } else if (xterm || (vte_version != 0) || rxvt) { + // This seems to be supported for a long time in VTE + // urxvt also supports this data->unibi_ext.set_cursor_color = (int)unibi_add_ext_str( ut, NULL, "\033]12;#%p1%06x\007"); } - data->unibi_ext.enable_mouse = (int)unibi_add_ext_str(ut, NULL, + + /// Terminals usually ignore unrecognized private modes, and there is no + /// known ambiguity with these. So we just set them unconditionally. + data->unibi_ext.enable_lr_margin = (int)unibi_add_ext_str(ut, + "ext.enable_lr_margin", + "\x1b[?69h"); + data->unibi_ext.disable_lr_margin = (int)unibi_add_ext_str(ut, + "ext.disable_lr_margin", + "\x1b[?69l"); + data->unibi_ext.enable_bracketed_paste = (int)unibi_add_ext_str(ut, + "ext.enable_bpaste", + "\x1b[?2004h"); + data->unibi_ext.disable_bracketed_paste = (int)unibi_add_ext_str(ut, + "ext.disable_bpaste", + "\x1b[?2004l"); + data->unibi_ext.enable_focus_reporting = (int)unibi_add_ext_str(ut, + "ext.enable_focus", + rxvt ? "\x1b]777;focus;on\x7" : "\x1b[?1004h"); + data->unibi_ext.disable_focus_reporting = (int)unibi_add_ext_str(ut, + "ext.disable_focus", + rxvt ? "\x1b]777;focus;off\x7" : "\x1b[?1004l"); + data->unibi_ext.enable_mouse = (int)unibi_add_ext_str(ut, + "ext.enable_mouse", "\x1b[?1002h\x1b[?1006h"); - data->unibi_ext.disable_mouse = (int)unibi_add_ext_str(ut, NULL, + data->unibi_ext.disable_mouse = (int)unibi_add_ext_str(ut, + "ext.disable_mouse", "\x1b[?1002l\x1b[?1006l"); - data->unibi_ext.set_rgb_foreground = (int)unibi_add_ext_str(ut, NULL, - "\x1b[38;2;%p1%d;%p2%d;%p3%dm"); - data->unibi_ext.set_rgb_background = (int)unibi_add_ext_str(ut, NULL, - "\x1b[48;2;%p1%d;%p2%d;%p3%dm"); - unibi_set_if_empty(ut, unibi_cursor_address, "\x1b[%i%p1%d;%p2%dH"); - unibi_set_if_empty(ut, unibi_exit_attribute_mode, "\x1b[0;10m"); - unibi_set_if_empty(ut, unibi_set_a_foreground, XTERM_SETAF); - unibi_set_if_empty(ut, unibi_set_a_background, XTERM_SETAB); - unibi_set_if_empty(ut, unibi_enter_bold_mode, "\x1b[1m"); - unibi_set_if_empty(ut, unibi_enter_underline_mode, "\x1b[4m"); - unibi_set_if_empty(ut, unibi_enter_reverse_mode, "\x1b[7m"); - unibi_set_if_empty(ut, unibi_bell, "\x07"); - unibi_set_if_empty(data->ut, unibi_enter_ca_mode, "\x1b[?1049h"); - unibi_set_if_empty(data->ut, unibi_exit_ca_mode, "\x1b[?1049l"); - unibi_set_if_empty(ut, unibi_delete_line, "\x1b[M"); - unibi_set_if_empty(ut, unibi_parm_delete_line, "\x1b[%p1%dM"); - unibi_set_if_empty(ut, unibi_insert_line, "\x1b[L"); - unibi_set_if_empty(ut, unibi_parm_insert_line, "\x1b[%p1%dL"); - unibi_set_if_empty(ut, unibi_clear_screen, "\x1b[H\x1b[J"); - unibi_set_if_empty(ut, unibi_clr_eol, "\x1b[K"); - unibi_set_if_empty(ut, unibi_clr_eos, "\x1b[J"); -} - -static void flush_buf(UI *ui, bool toggle_cursor) +} + +static void flush_buf(UI *ui) { uv_write_t req; - uv_buf_t buf; + uv_buf_t bufs[3]; + uv_buf_t *bufp = &bufs[0]; TUIData *data = ui->data; - if (toggle_cursor && !data->busy) { - // not busy and the cursor is invisible(see below). Append a "cursor - // normal" command to the end of the buffer. - data->bufsize += CNORM_COMMAND_MAX_SIZE; - unibi_out(ui, unibi_cursor_normal); - data->bufsize -= CNORM_COMMAND_MAX_SIZE; + if (data->bufpos <= 0 && data->busy == data->is_invisible) { + return; } - buf.base = data->buf; - buf.len = data->bufpos; - uv_write(&req, STRUCT_CAST(uv_stream_t, &data->output_handle), &buf, 1, NULL); - uv_run(&data->write_loop, UV_RUN_DEFAULT); - data->bufpos = 0; + if (!data->is_invisible) { + // cursor is visible. Write a "cursor invisible" command before writing the + // buffer. + bufp->base = data->invis; + bufp->len = UV_BUF_LEN(data->invislen); + bufp++; + data->is_invisible = true; + } + + if (data->bufpos > 0) { + bufp->base = data->buf; + bufp->len = UV_BUF_LEN(data->bufpos); + bufp++; + } - if (toggle_cursor && !data->busy) { - // not busy and cursor is visible(see above), append a "cursor invisible" - // command to the beginning of the buffer for the next flush - unibi_out(ui, unibi_cursor_invisible); + if (!data->busy && data->is_invisible) { + // not busy and the cursor is invisible. Write a "cursor normal" command + // after writing the buffer. + bufp->base = data->norm; + bufp->len = UV_BUF_LEN(data->normlen); + bufp++; + data->is_invisible = data->busy; } + + uv_write(&req, STRUCT_CAST(uv_stream_t, &data->output_handle), + bufs, (unsigned)(bufp - bufs), NULL); + uv_run(&data->write_loop, UV_RUN_DEFAULT); + data->bufpos = 0; + data->overflow = false; } #if TERMKEY_VERSION_MAJOR > 0 || TERMKEY_VERSION_MINOR > 18 @@ -1223,7 +1800,7 @@ static const char *tui_get_stty_erase(void) if (tcgetattr(input_global_fd(), &t) != -1) { stty_erase[0] = (char)t.c_cc[VERASE]; stty_erase[1] = '\0'; - ILOG("stty/termios:erase=%s", stty_erase); + DLOG("stty/termios:erase=%s", stty_erase); } #endif return stty_erase; @@ -1240,16 +1817,22 @@ static const char *tui_tk_ti_getstr(const char *name, const char *value, } if (strequal(name, "key_backspace")) { - ILOG("libtermkey:kbs=%s", value); + DLOG("libtermkey:kbs=%s", value); if (stty_erase[0] != 0) { return stty_erase; } } else if (strequal(name, "key_dc")) { - ILOG("libtermkey:kdch1=%s", value); + DLOG("libtermkey:kdch1=%s", value); // Vim: "If <BS> and <DEL> are now the same, redefine <DEL>." if (value != NULL && strequal(stty_erase, value)) { return stty_erase[0] == DEL ? CTRL_H_STR : DEL_STR; } + } else if (strequal(name, "key_mouse")) { + DLOG("libtermkey:kmous=%s", value); + // If key_mouse is found, libtermkey uses its terminfo driver (driver-ti.c) + // for mouse input, which by accident only supports X10 protocol. + // Force libtermkey to fallback to its CSI driver (driver-csi.c). #7948 + return NULL; } return value; diff --git a/src/nvim/ugrid.c b/src/nvim/ugrid.c index 7a0a16687e..6d420ef2f8 100644 --- a/src/nvim/ugrid.c +++ b/src/nvim/ugrid.c @@ -16,8 +16,8 @@ void ugrid_init(UGrid *grid) { - grid->attrs = EMPTY_ATTRS; - grid->fg = grid->bg = -1; + grid->attrs = HLATTRS_INIT; + grid->clear_attrs = HLATTRS_INIT; grid->cells = NULL; } @@ -107,6 +107,7 @@ UCell *ugrid_put(UGrid *grid, uint8_t *text, size_t size) UCell *cell = grid->cells[grid->row] + grid->col; cell->data[size] = 0; cell->attrs = grid->attrs; + assert(size <= CELLBYTES); if (text) { memcpy(cell->data, text, size); @@ -118,9 +119,7 @@ UCell *ugrid_put(UGrid *grid, uint8_t *text, size_t size) static void clear_region(UGrid *grid, int top, int bot, int left, int right) { - HlAttrs clear_attrs = EMPTY_ATTRS; - clear_attrs.foreground = grid->fg; - clear_attrs.background = grid->bg; + HlAttrs clear_attrs = grid->clear_attrs; UGRID_FOREACH_CELL(grid, top, bot, left, right, { cell->data[0] = ' '; cell->data[1] = 0; @@ -135,6 +134,7 @@ static void destroy_cells(UGrid *grid) xfree(grid->cells[i]); } xfree(grid->cells); + grid->cells = NULL; } } diff --git a/src/nvim/ugrid.h b/src/nvim/ugrid.h index 268362bf1b..60c9068eb1 100644 --- a/src/nvim/ugrid.h +++ b/src/nvim/ugrid.h @@ -7,22 +7,22 @@ typedef struct ucell UCell; typedef struct ugrid UGrid; +#define CELLBYTES (4 * (MAX_MCO+1)) + struct ucell { - char data[6 * MAX_MCO + 1]; + char data[CELLBYTES + 1]; HlAttrs attrs; }; struct ugrid { int top, bot, left, right; int row, col; - int bg, fg; + HlAttrs clear_attrs; int width, height; HlAttrs attrs; UCell **cells; }; -#define EMPTY_ATTRS ((HlAttrs){ false, false, false, false, false, -1, -1, -1 }) - #define UGRID_FOREACH_CELL(grid, top, bot, left, right, code) \ do { \ for (int row = top; row <= bot; row++) { \ diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 0a2154438f..42366fdb76 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -8,11 +8,13 @@ #include <limits.h> #include "nvim/vim.h" +#include "nvim/log.h" #include "nvim/ui.h" #include "nvim/charset.h" #include "nvim/cursor.h" #include "nvim/diff.h" #include "nvim/ex_cmds2.h" +#include "nvim/ex_getln.h" #include "nvim/fold.h" #include "nvim/main.h" #include "nvim/ascii.h" @@ -47,18 +49,39 @@ #define MAX_UI_COUNT 16 static UI *uis[MAX_UI_COUNT]; -static bool ui_ext[UI_WIDGETS] = { 0 }; +static bool ui_ext[kUIExtCount] = { 0 }; static size_t ui_count = 0; static int row = 0, col = 0; static struct { int top, bot, left, right; } sr; -static int current_attr_code = 0; +static int current_attr_code = -1; static bool pending_cursor_update = false; static int busy = 0; static int height, width; static int old_mode_idx = -1; +#if MIN_LOG_LEVEL > DEBUG_LOG_LEVEL +# define UI_LOG(funname, ...) +#else +static size_t uilog_seen = 0; +static char uilog_last_event[1024] = { 0 }; +# define UI_LOG(funname, ...) \ + do { \ + if (strequal(uilog_last_event, STR(funname))) { \ + uilog_seen++; \ + } else { \ + if (uilog_seen > 0) { \ + do_log(DEBUG_LOG_LEVEL, "UI: ", NULL, -1, true, \ + "%s (+%zu times...)", uilog_last_event, uilog_seen); \ + } \ + do_log(DEBUG_LOG_LEVEL, "UI: ", NULL, -1, true, STR(funname)); \ + uilog_seen = 0; \ + xstrlcpy(uilog_last_event, STR(funname), sizeof(uilog_last_event)); \ + } \ + } while (0) +#endif + // UI_CALL invokes a function on all registered UI instances. The functions can // have 0-5 arguments (configurable by SELECT_NTH). // @@ -67,6 +90,7 @@ static int old_mode_idx = -1; # define UI_CALL(funname, ...) \ do { \ flush_cursor_update(); \ + UI_LOG(funname, 0); \ for (size_t i = 0; i < ui_count; i++) { \ UI *ui = uis[i]; \ UI_CALL_MORE(funname, __VA_ARGS__); \ @@ -76,15 +100,18 @@ static int old_mode_idx = -1; # define UI_CALL(...) \ do { \ flush_cursor_update(); \ + UI_LOG(__VA_ARGS__, 0); \ for (size_t i = 0; i < ui_count; i++) { \ UI *ui = uis[i]; \ UI_CALL_HELPER(CNT(__VA_ARGS__), __VA_ARGS__); \ } \ } while (0) #endif -#define CNT(...) SELECT_NTH(__VA_ARGS__, MORE, MORE, MORE, MORE, ZERO, ignore) -#define SELECT_NTH(a1, a2, a3, a4, a5, a6, ...) a6 +#define CNT(...) SELECT_NTH(__VA_ARGS__, MORE, MORE, MORE, \ + MORE, MORE, ZERO, ignore) +#define SELECT_NTH(a1, a2, a3, a4, a5, a6, a7, ...) a7 #define UI_CALL_HELPER(c, ...) UI_CALL_HELPER2(c, __VA_ARGS__) +// Resolves to UI_CALL_MORE or UI_CALL_ZERO. #define UI_CALL_HELPER2(c, ...) UI_CALL_##c(__VA_ARGS__) #define UI_CALL_MORE(method, ...) if (ui->method) ui->method(ui, __VA_ARGS__) #define UI_CALL_ZERO(method) if (ui->method) ui->method(ui) @@ -119,6 +146,9 @@ void ui_builtin_stop(void) bool ui_rgb_attached(void) { + if (!headless_mode && p_tgc) { + return true; + } for (size_t i = 0; i < ui_count; i++) { if (uis[i]->rgb) { return true; @@ -141,6 +171,67 @@ void ui_event(char *name, Array args) } } + +/// Converts an HlAttrs into Dictionary +/// +/// @param[in] aep data to convert +/// @param use_rgb use 'gui*' settings if true, else resorts to 'cterm*' +Dictionary hlattrs2dict(const HlAttrs *aep, bool use_rgb) +{ + assert(aep); + Dictionary hl = ARRAY_DICT_INIT; + int mask = use_rgb ? aep->rgb_ae_attr : aep->cterm_ae_attr; + + if (mask & HL_BOLD) { + PUT(hl, "bold", BOOLEAN_OBJ(true)); + } + + if (mask & HL_STANDOUT) { + PUT(hl, "standout", BOOLEAN_OBJ(true)); + } + + if (mask & HL_UNDERLINE) { + PUT(hl, "underline", BOOLEAN_OBJ(true)); + } + + if (mask & HL_UNDERCURL) { + PUT(hl, "undercurl", BOOLEAN_OBJ(true)); + } + + if (mask & HL_ITALIC) { + PUT(hl, "italic", BOOLEAN_OBJ(true)); + } + + if (mask & HL_INVERSE) { + PUT(hl, "reverse", BOOLEAN_OBJ(true)); + } + + + if (use_rgb) { + if (aep->rgb_fg_color != -1) { + PUT(hl, "foreground", INTEGER_OBJ(aep->rgb_fg_color)); + } + + if (aep->rgb_bg_color != -1) { + PUT(hl, "background", INTEGER_OBJ(aep->rgb_bg_color)); + } + + if (aep->rgb_sp_color != -1) { + PUT(hl, "special", INTEGER_OBJ(aep->rgb_sp_color)); + } + } else { + if (cterm_normal_fg_color != aep->cterm_fg_color) { + PUT(hl, "foreground", INTEGER_OBJ(aep->cterm_fg_color - 1)); + } + + if (cterm_normal_bg_color != aep->cterm_bg_color) { + PUT(hl, "background", INTEGER_OBJ(aep->cterm_bg_color - 1)); + } + } + + return hl; +} + void ui_refresh(void) { if (!ui_active()) { @@ -153,8 +244,8 @@ void ui_refresh(void) } int width = INT_MAX, height = INT_MAX; - bool ext_widgets[UI_WIDGETS]; - for (UIWidget i = 0; (int)i < UI_WIDGETS; i++) { + bool ext_widgets[kUIExtCount]; + for (UIExtension i = 0; (int)i < kUIExtCount; i++) { ext_widgets[i] = true; } @@ -162,19 +253,27 @@ void ui_refresh(void) UI *ui = uis[i]; width = MIN(ui->width, width); height = MIN(ui->height, height); - for (UIWidget i = 0; (int)i < UI_WIDGETS; i++) { + for (UIExtension i = 0; (int)i < kUIExtCount; i++) { ext_widgets[i] &= ui->ui_ext[i]; } } row = col = 0; + + int save_p_lz = p_lz; + p_lz = false; // convince redrawing() to return true ... screen_resize(width, height); - for (UIWidget i = 0; (int)i < UI_WIDGETS; i++) { - ui_set_external(i, ext_widgets[i]); + p_lz = save_p_lz; + + for (UIExtension i = 0; (int)i < kUIExtCount; i++) { + ui_ext[i] = ext_widgets[i]; + ui_call_option_set(cstr_as_string((char *)ui_ext_names[i]), + BOOLEAN_OBJ(ext_widgets[i])); } ui_mode_info_set(); old_mode_idx = -1; ui_cursor_shape(); + current_attr_code = -1; } static void ui_refresh_event(void **argv) @@ -192,6 +291,11 @@ void ui_resize(int new_width, int new_height) width = new_width; height = new_height; + // TODO(bfredl): update default colors when they changed, NOT on resize. + ui_call_default_colors_set(normal_fg, normal_bg, normal_sp, + cterm_normal_fg_color, cterm_normal_bg_color); + + // Deprecated: UI_CALL(update_fg, (ui->rgb ? normal_fg : cterm_normal_fg_color - 1)); UI_CALL(update_bg, (ui->rgb ? normal_bg : cterm_normal_bg_color - 1)); UI_CALL(update_sp, (ui->rgb ? normal_sp : -1)); @@ -224,6 +328,7 @@ void ui_attach_impl(UI *ui) } uis[ui_count++] = ui; + ui_refresh_options(); ui_refresh(); } @@ -284,26 +389,28 @@ void ui_reset_scroll_region(void) ui_call_set_scroll_region(sr.top, sr.bot, sr.left, sr.right); } -void ui_start_highlight(int attr_code) +void ui_set_highlight(int attr_code) { + if (current_attr_code == attr_code) { + return; + } current_attr_code = attr_code; - if (!ui_count) { - return; + HlAttrs attrs = HLATTRS_INIT; + + if (attr_code != 0) { + HlAttrs *aep = syn_cterm_attr2entry(attr_code); + if (aep) { + attrs = *aep; + } } - set_highlight_args(current_attr_code); + UI_CALL(highlight_set, attrs); } -void ui_stop_highlight(void) +void ui_clear_highlight(void) { - current_attr_code = HL_NORMAL; - - if (!ui_count) { - return; - } - - set_highlight_args(current_attr_code); + ui_set_highlight(0); } void ui_puts(uint8_t *str) @@ -331,6 +438,12 @@ void ui_puts(uint8_t *str) ui_linefeed(); } p += clen; + + if (p_wd) { // 'writedelay': flush & delay each time. + ui_flush(); + uint64_t wd = (uint64_t)labs(p_wd); + os_delay(wd, false); + } } } @@ -370,63 +483,10 @@ int ui_current_col(void) void ui_flush(void) { + cmdline_ui_flush(); ui_call_flush(); } -static void set_highlight_args(int attr_code) -{ - HlAttrs rgb_attrs = { false, false, false, false, false, -1, -1, -1 }; - HlAttrs cterm_attrs = rgb_attrs; - - if (attr_code == HL_NORMAL) { - goto end; - } - - int rgb_mask = 0; - int cterm_mask = 0; - attrentry_T *aep = syn_cterm_attr2entry(attr_code); - - if (!aep) { - goto end; - } - - rgb_mask = aep->rgb_ae_attr; - cterm_mask = aep->cterm_ae_attr; - - rgb_attrs.bold = rgb_mask & HL_BOLD; - rgb_attrs.underline = rgb_mask & HL_UNDERLINE; - rgb_attrs.undercurl = rgb_mask & HL_UNDERCURL; - rgb_attrs.italic = rgb_mask & HL_ITALIC; - rgb_attrs.reverse = rgb_mask & (HL_INVERSE | HL_STANDOUT); - cterm_attrs.bold = cterm_mask & HL_BOLD; - cterm_attrs.underline = cterm_mask & HL_UNDERLINE; - cterm_attrs.undercurl = cterm_mask & HL_UNDERCURL; - cterm_attrs.italic = cterm_mask & HL_ITALIC; - cterm_attrs.reverse = cterm_mask & (HL_INVERSE | HL_STANDOUT); - - if (aep->rgb_fg_color != normal_fg) { - rgb_attrs.foreground = aep->rgb_fg_color; - } - - if (aep->rgb_bg_color != normal_bg) { - rgb_attrs.background = aep->rgb_bg_color; - } - - if (aep->rgb_sp_color != normal_sp) { - rgb_attrs.special = aep->rgb_sp_color; - } - - if (cterm_normal_fg_color != aep->cterm_fg_color) { - cterm_attrs.foreground = aep->cterm_fg_color - 1; - } - - if (cterm_normal_bg_color != aep->cterm_bg_color) { - cterm_attrs.background = aep->cterm_bg_color - 1; - } - -end: - UI_CALL(highlight_set, (ui->rgb ? rgb_attrs : cterm_attrs)); -} void ui_linefeed(void) { @@ -466,15 +526,23 @@ void ui_cursor_shape(void) } /// Returns true if `widget` is externalized. -bool ui_is_external(UIWidget widget) +bool ui_is_external(UIExtension widget) { return ui_ext[widget]; } -/// Sets `widget` as "external". -/// Such widgets are not drawn by Nvim; external UIs are expected to handle -/// higher-level UI events and present the data. -void ui_set_external(UIWidget widget, bool external) +Array ui_array(void) { - ui_ext[widget] = external; + Array all_uis = ARRAY_DICT_INIT; + for (size_t i = 0; i < ui_count; i++) { + Dictionary dic = ARRAY_DICT_INIT; + PUT(dic, "width", INTEGER_OBJ(uis[i]->width)); + PUT(dic, "height", INTEGER_OBJ(uis[i]->height)); + PUT(dic, "rgb", BOOLEAN_OBJ(uis[i]->rgb)); + for (UIExtension j = 0; j < kUIExtCount; j++) { + PUT(dic, ui_ext_names[j], BOOLEAN_OBJ(uis[i]->ui_ext[j])); + } + ADD(all_uis, DICTIONARY_OBJ(dic)); + } + return all_uis; } diff --git a/src/nvim/ui.h b/src/nvim/ui.h index 064f77fee1..48896a6a3f 100644 --- a/src/nvim/ui.h +++ b/src/nvim/ui.h @@ -13,26 +13,27 @@ typedef enum { kUIPopupmenu, kUITabline, kUIWildmenu, -} UIWidget; -#define UI_WIDGETS (kUIWildmenu + 1) + kUIExtCount, +} UIExtension; + +EXTERN const char *ui_ext_names[] INIT(= { + "ext_cmdline", + "ext_popupmenu", + "ext_tabline", + "ext_wildmenu" +}); -typedef struct { - bool bold, underline, undercurl, italic, reverse; - int foreground, background, special; -} HlAttrs; typedef struct ui_t UI; struct ui_t { bool rgb; - bool ui_ext[UI_WIDGETS]; ///< Externalized widgets + bool ui_ext[kUIExtCount]; ///< Externalized widgets int width, height; void *data; - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ui_events.generated.h" #endif - void (*event)(UI *ui, char *name, Array args, bool *args_consumed); void (*stop)(UI *ui); }; diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c index 0165db7c0c..56e0c0c454 100644 --- a/src/nvim/ui_bridge.c +++ b/src/nvim/ui_bridge.c @@ -24,30 +24,10 @@ #define UI(b) (((UIBridgeData *)b)->ui) -#if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL -static size_t uilog_seen = 0; -static argv_callback uilog_event = NULL; -#define UI_CALL(ui, name, argc, ...) \ - do { \ - if (uilog_event == ui_bridge_##name##_event) { \ - uilog_seen++; \ - } else { \ - if (uilog_seen > 0) { \ - DLOG("UI bridge: ...%zu times", uilog_seen); \ - } \ - DLOG("UI bridge: " STR(name)); \ - uilog_seen = 0; \ - uilog_event = ui_bridge_##name##_event; \ - } \ - ((UIBridgeData *)ui)->scheduler( \ - event_create(ui_bridge_##name##_event, argc, __VA_ARGS__), UI(ui)); \ - } while (0) -#else // Schedule a function call on the UI bridge thread. -#define UI_CALL(ui, name, argc, ...) \ +#define UI_BRIDGE_CALL(ui, name, argc, ...) \ ((UIBridgeData *)ui)->scheduler( \ event_create(ui_bridge_##name##_event, argc, __VA_ARGS__), UI(ui)) -#endif #define INT2PTR(i) ((void *)(intptr_t)i) #define PTR2INT(p) ((Integer)(intptr_t)p) @@ -79,16 +59,15 @@ UI *ui_bridge_attach(UI *ui, ui_main_fn ui_main, event_scheduler scheduler) rv->bridge.put = ui_bridge_put; rv->bridge.bell = ui_bridge_bell; rv->bridge.visual_bell = ui_bridge_visual_bell; - rv->bridge.update_fg = ui_bridge_update_fg; - rv->bridge.update_bg = ui_bridge_update_bg; - rv->bridge.update_sp = ui_bridge_update_sp; + rv->bridge.default_colors_set = ui_bridge_default_colors_set; rv->bridge.flush = ui_bridge_flush; rv->bridge.suspend = ui_bridge_suspend; rv->bridge.set_title = ui_bridge_set_title; rv->bridge.set_icon = ui_bridge_set_icon; + rv->bridge.option_set = ui_bridge_option_set; rv->scheduler = scheduler; - for (UIWidget i = 0; (int)i < UI_WIDGETS; i++) { + for (UIExtension i = 0; (int)i < kUIExtCount; i++) { rv->bridge.ui_ext[i] = ui->ui_ext[i]; } @@ -102,6 +81,7 @@ UI *ui_bridge_attach(UI *ui, ui_main_fn ui_main, event_scheduler scheduler) abort(); } + // Suspend the main thread until CONTINUE is called by the UI thread. while (!rv->ready) { uv_cond_wait(&rv->cond, &rv->mutex); } @@ -126,9 +106,12 @@ static void ui_thread_run(void *data) static void ui_bridge_stop(UI *b) { + // Detach brigde first, so that "stop" is the last event the TUI loop + // receives from the main thread. #8041 + ui_detach_impl(b); UIBridgeData *bridge = (UIBridgeData *)b; bool stopped = bridge->stopped = false; - UI_CALL(b, stop, 1, b); + UI_BRIDGE_CALL(b, stop, 1, b); for (;;) { uv_mutex_lock(&bridge->mutex); stopped = bridge->stopped; @@ -136,12 +119,12 @@ static void ui_bridge_stop(UI *b) if (stopped) { break; } - loop_poll_events(&main_loop, 10); + loop_poll_events(&main_loop, 10); // Process one event. } uv_thread_join(&bridge->ui_thread); uv_mutex_destroy(&bridge->mutex); uv_cond_destroy(&bridge->cond); - ui_detach_impl(b); + xfree(bridge->ui); // Threads joined, now safe to free UI container. #7922 xfree(b); } static void ui_bridge_stop_event(void **argv) @@ -154,7 +137,7 @@ static void ui_bridge_highlight_set(UI *b, HlAttrs attrs) { HlAttrs *a = xmalloc(sizeof(HlAttrs)); *a = attrs; - UI_CALL(b, highlight_set, 2, b, a); + UI_BRIDGE_CALL(b, highlight_set, 2, b, a); } static void ui_bridge_highlight_set_event(void **argv) { @@ -167,9 +150,9 @@ static void ui_bridge_suspend(UI *b) { UIBridgeData *data = (UIBridgeData *)b; uv_mutex_lock(&data->mutex); - UI_CALL(b, suspend, 1, b); + UI_BRIDGE_CALL(b, suspend, 1, b); data->ready = false; - // suspend the main thread until CONTINUE is called by the UI thread + // Suspend the main thread until CONTINUE is called by the UI thread. while (!data->ready) { uv_cond_wait(&data->cond, &data->mutex); } diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 81af3bfda9..35857510fc 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -1838,11 +1838,9 @@ void undo_time(long step, int sec, int file, int absolute) } closest = -1; } else { - /* When doing computations with time_t subtract starttime, because - * time_t converted to a long may result in a wrong number. */ - if (dosec) - target = (long)(curbuf->b_u_time_cur - starttime) + step; - else if (dofile) { + if (dosec) { + target = (long)(curbuf->b_u_time_cur) + step; + } else if (dofile) { if (step < 0) { /* Going back to a previous write. If there were changes after * the last write, count that as moving one file-write, so @@ -1880,14 +1878,16 @@ void undo_time(long step, int sec, int file, int absolute) target = 0; closest = -1; } else { - if (dosec) - closest = (long)(time(NULL) - starttime + 1); - else if (dofile) + if (dosec) { + closest = (long)(os_time() + 1); + } else if (dofile) { closest = curbuf->b_u_save_nr_last + 2; - else + } else { closest = curbuf->b_u_seq_last + 2; - if (target >= closest) + } + if (target >= closest) { target = closest - 1; + } } } closest_start = closest; @@ -1916,12 +1916,13 @@ void undo_time(long step, int sec, int file, int absolute) while (uhp != NULL) { uhp->uh_walk = mark; - if (dosec) - val = (long)(uhp->uh_time - starttime); - else if (dofile) + if (dosec) { + val = (long)(uhp->uh_time); + } else if (dofile) { val = uhp->uh_save_nr; - else + } else { val = uhp->uh_seq; + } if (round == 1 && !(dofile && val == 0)) { /* Remember the header that is closest to the target. @@ -2095,8 +2096,8 @@ void undo_time(long step, int sec, int file, int absolute) uhp = uhp->uh_prev.ptr; if (uhp == NULL || uhp->uh_walk != mark) { - /* Need to redo more but can't find it... */ - EMSG2(_(e_intern2), "undo_time()"); + // Need to redo more but can't find it... + internal_error("undo_time()"); break; } } @@ -2162,8 +2163,8 @@ static void u_undoredo(int undo) if (top > curbuf->b_ml.ml_line_count || top >= bot || bot > curbuf->b_ml.ml_line_count + 1) { unblock_autocmds(); - EMSG(_("E438: u_undo: line numbers wrong")); - changed(); /* don't want UNCHANGED now */ + IEMSG(_("E438: u_undo: line numbers wrong")); + changed(); // don't want UNCHANGED now return; } @@ -2267,12 +2268,14 @@ static void u_undoredo(int undo) curhead->uh_entry = newlist; curhead->uh_flags = new_flags; - if ((old_flags & UH_EMPTYBUF) && bufempty()) + if ((old_flags & UH_EMPTYBUF) && BUFEMPTY()) { curbuf->b_ml.ml_flags |= ML_EMPTY; - if (old_flags & UH_CHANGED) + } + if (old_flags & UH_CHANGED) { changed(); - else + } else { unchanged(curbuf, FALSE); + } /* * restore marks from before undo/redo @@ -2654,7 +2657,7 @@ static void u_unch_branch(u_header_T *uhp) static u_entry_T *u_get_headentry(void) { if (curbuf->b_u_newhead == NULL || curbuf->b_u_newhead->uh_entry == NULL) { - EMSG(_("E439: undo list corrupt")); + IEMSG(_("E439: undo list corrupt")); return NULL; } return curbuf->b_u_newhead->uh_entry; @@ -2683,11 +2686,11 @@ static void u_getbot(void) extra = curbuf->b_ml.ml_line_count - uep->ue_lcount; uep->ue_bot = uep->ue_top + uep->ue_size + 1 + extra; if (uep->ue_bot < 1 || uep->ue_bot > curbuf->b_ml.ml_line_count) { - EMSG(_("E440: undo line missing")); - uep->ue_bot = uep->ue_top + 1; /* assume all lines deleted, will - * get all the old lines back - * without deleting the current - * ones */ + IEMSG(_("E440: undo line missing")); + uep->ue_bot = uep->ue_top + 1; // assume all lines deleted, will + // get all the old lines back + // without deleting the current + // ones } curbuf->b_u_newhead->uh_getbot_entry = NULL; @@ -2940,17 +2943,20 @@ bool curbufIsChanged(void) && (curbuf->b_changed || file_ff_differs(curbuf, true))); } -/* - * For undotree(): Append the list of undo blocks at "first_uhp" to "list". - * Recursive. - */ -void u_eval_tree(u_header_T *first_uhp, list_T *list) +/// Append the list of undo blocks to a newly allocated list +/// +/// For use in undotree(). Recursive. +/// +/// @param[in] first_uhp Undo blocks list to start with. +/// +/// @return [allocated] List with a representation of undo blocks. +list_T *u_eval_tree(const u_header_T *const first_uhp) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET { - u_header_T *uhp = first_uhp; - dict_T *dict; + list_T *const list = tv_list_alloc(kListLenMayKnow); - while (uhp != NULL) { - dict = tv_dict_alloc(); + for (const u_header_T *uhp = first_uhp; uhp != NULL; uhp = uhp->uh_prev.ptr) { + dict_T *const dict = tv_dict_alloc(); tv_dict_add_nr(dict, S_LEN("seq"), (varnumber_T)uhp->uh_seq); tv_dict_add_nr(dict, S_LEN("time"), (varnumber_T)uhp->uh_time); if (uhp == curbuf->b_u_newhead) { @@ -2964,14 +2970,12 @@ void u_eval_tree(u_header_T *first_uhp, list_T *list) } if (uhp->uh_alt_next.ptr != NULL) { - list_T *alt_list = tv_list_alloc(); - // Recursive call to add alternate undo tree. - u_eval_tree(uhp->uh_alt_next.ptr, alt_list); - tv_dict_add_list(dict, S_LEN("alt"), alt_list); + tv_dict_add_list(dict, S_LEN("alt"), u_eval_tree(uhp->uh_alt_next.ptr)); } tv_list_append_dict(list, dict); - uhp = uhp->uh_prev.ptr; } + + return list; } diff --git a/src/nvim/undo.h b/src/nvim/undo.h index ab8584fbb2..802cdc5583 100644 --- a/src/nvim/undo.h +++ b/src/nvim/undo.h @@ -2,6 +2,7 @@ #define NVIM_UNDO_H #include "nvim/undo_defs.h" +#include "nvim/ex_cmds_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "undo.h.generated.h" diff --git a/src/nvim/undo_defs.h b/src/nvim/undo_defs.h index d841210815..6c7e2bba41 100644 --- a/src/nvim/undo_defs.h +++ b/src/nvim/undo_defs.h @@ -4,9 +4,10 @@ #include <time.h> // for time_t #include "nvim/pos.h" -#include "nvim/buffer_defs.h" #include "nvim/mark_defs.h" +typedef struct u_header u_header_T; + /* Structure to store info about the Visual area. */ typedef struct { pos_T vi_start; /* start pos of last VIsual */ @@ -15,8 +16,9 @@ typedef struct { colnr_T vi_curswant; /* MAXCOL from w_curswant */ } visualinfo_T; +#include "nvim/buffer_defs.h" + typedef struct u_entry u_entry_T; -typedef struct u_header u_header_T; struct u_entry { u_entry_T *ue_next; /* pointer to next entry in list */ linenr_T ue_top; /* number of line above undo block */ diff --git a/src/nvim/version.c b/src/nvim/version.c index 094ca29b01..77ae849d2e 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -22,6 +22,7 @@ #include "nvim/message.h" #include "nvim/screen.h" #include "nvim/strings.h" +#include "nvim/lua/executor.h" // version info generated by the build system #include "auto/versiondef.h" @@ -29,8 +30,8 @@ // for ":version", ":intro", and "nvim --version" #ifndef NVIM_VERSION_MEDIUM #define NVIM_VERSION_MEDIUM "v" STR(NVIM_VERSION_MAJOR)\ - "." STR(NVIM_VERSION_MINOR) "." STR(NVIM_VERSION_PATCH)\ - NVIM_VERSION_PRERELEASE +"." STR(NVIM_VERSION_MINOR) "." STR(NVIM_VERSION_PATCH)\ +NVIM_VERSION_PRERELEASE #endif #define NVIM_VERSION_LONG "NVIM " NVIM_VERSION_MEDIUM @@ -46,2110 +47,1303 @@ char *version_cflags = "Compilation: " NVIM_VERSION_CFLAGS; static char *features[] = { #ifdef HAVE_ACL - "+acl", +"+acl", #else - "-acl", +"-acl", #endif #if (defined(HAVE_ICONV_H) && defined(USE_ICONV)) || defined(DYNAMIC_ICONV) # ifdef DYNAMIC_ICONV - "+iconv/dyn", +"+iconv/dyn", # else - "+iconv", +"+iconv", # endif #else - "-iconv", +"-iconv", #endif #ifdef HAVE_JEMALLOC - "+jemalloc", +"+jemalloc", #else - "-jemalloc", +"-jemalloc", #endif #ifdef FEAT_TUI - "+tui", +"+tui", #else - "-tui", +"-tui", #endif - NULL +NULL }; // clang-format off static const int included_patches[] = { - // 2367,NA - // 2366 NA - // 2365 NA - // 2364,NA - // 2363 NA - 2362, - // 2361 NA - 2360, - 2359, - // 2358 NA - 2357, - 2356, - 2355, - // 2354, - 2353, - // 2352 NA - // 2351 NA - // 2350, - 2349, - 2348, - 2347, - 2346, - // 2345 NA - // 2344 NA - 2343, - // 2342 NA - 2341, - // 2340 NA - 2339, - // 2338 NA - 2337, - 2336, - 2335, - 2334, - 2333, - 2332, - 2331, - 2330, - 2329, - 2328, - // 2327 NA - 2326, - // 2325 NA - 2324, - 2323, - 2322, - 2321, - 2320, - // 2319 NA - 2318, - 2317, - // 2316 NA - 2315, - 2314, - 2313, - 2312, - // 2311 NA - // 2310 NA - 2309, - // 2308 NA - 2307, - 2306, - 2305, - 2304, - 2303, - // 2302 NA - // 2301 NA - 2300, - 2299, - // 2298 NA - // 2297 NA - 2296, - 2295, - 2294, - 2293, - 2292, - 2291, - // 2290 NA - // 2289 NA - // 2288 NA - // 2287 NA - // 2286 NA - // 2285 NA - 2284, - 2283, - // 2282 NA - 2281, - 2280, - 2279, - // 2278 NA - 2277, - 2276, - 2275, - 2274, - 2273, - 2272, - // 2271 NA - // 2270 NA - 2269, - 2268, - // 2267 NA - 2266, - 2265, - 2264, - 2263, - // 2262 NA - // 2261 NA - // 2260 NA - 2259, - // 2258 NA - // 2257 NA - 2256, - 2255, - // 2254 NA - // 2253 NA - // 2252 NA - 2251, - // 2250, - 2249, - 2248, - // 2247 NA - 2246, - 2245, - 2244, - // 2243 NA - 2242, - 2241, - 2240, - 2239, - // 2238 NA - 2237, - 2236, - 2235, - // 2234 NA - 2233, - // 2232 NA - 2231, - 2230, - // 2229, - 2228, - 2227, - 2226, - 2225, - 2224, - 2223, - 2222, - 2221, - 2220, - 2219, - // 2218 NA - 2217, - // 2216 NA - 2215, - // 2214 NA - 2213, - 2212, - // 2211 NA - // 2210 NA - 2209, - 2208, - // 2207 NA - // 2206 NA - 2205, - 2204, - // 2203 NA - // 2202 NA - 2201, - 2200, - // 2199 NA - // 2198 NA - 2197, - 2196, - // 2195 NA - 2194, - // 2193 NA - // 2192 NA - // 2191 NA - 2190, - // 2189, - 2188, - 2187, - // 2186 NA - 2185, - 2184, - 2183, - // 2182 NA - // 2181 NA - 2180, - 2179, - 2178, - 2177, - // 2176 NA - 2175, - 2174, - 2173, - 2172, - // 2171 NA - 2170, - 2169, - // 2168 NA - // 2167 NA - // 2166 NA - 2165, - 2164, - 2163, - 2162, - 2161, - 2160, - 2159, - 2158, - // 2157 NA - // 2156 NA - // 2155 NA - // 2154 NA - // 2153 NA - 2152, - 2151, - // 2150 NA - 2149, - 2148, - 2147, - 2146, - // 2145 NA - 2144, - 2143, - 2142, - 2141, - // 2140 NA - 2139, - // 2138 NA - 2137, - 2136, - 2135, - 2134, - // 2133 NA - // 2132 NA - // 2131 NA - // 2130 NA - // 2129 NA - 2128, - 2127, - 2126, - // 2125 NA - 2124, - 2123, - // 2122 NA - 2121, - 2120, - 2119, - // 2118 NA - 2117, - // 2116 NA - // 2115 NA - // 2114 NA - 2113, - 2112, - // 2111 NA - 2110, - 2109, - // 2108 NA - 2107, - 2106, - // 2105 NA - 2104, - 2103, - // 2102 NA - 2101, - 2100, - 2099, - 2098, - 2097, - 2096, - 2095, - // 2094 NA - // 2093 NA - // 2092 NA - // 2091 NA - 2090, - // 2089 NA - 2088, - 2087, - 2086, - 2085, - 2084, - // 2083 NA - 2082, - 2081, - // 2080 NA - // 2079 NA - // 2078 NA - 2077, - 2076, - 2075, - 2074, - // 2073 NA - 2072, - 2071, - // 2070 NA - 2069, - 2068, - 2067, - 2066, - 2065, - 2064, - // 2063 NA - 2062, - 2061, - // 2060 NA - // 2059 NA - // 2058 NA - // 2057 NA - // 2056 NA - // 2055 NA - // 2054 NA - // 2053 NA - // 2052 NA - 2051, - 2050, - 2049, - // 2048 NA - 2047, - 2046, - // 2045 NA - 2044, - 2043, - // 2042 NA - // 2041 NA - // 2040 NA - // 2039 NA - // 2038 NA - // 2037 NA - 2036, - // 2035 NA - // 2034 NA - 2033, - // 2032 NA - 2031, - // 2030 NA - 2029, - 2028, - // 2027 NA - // 2026 NA - // 2025 NA - 2024, - 2023, - 2022, - 2021, - // 2020 NA - 2019, - 2018, - 2017, - // 2016 NA - 2015, - 2014, - 2013, - 2012, - 2011, - 2010, - 2009, - 2008, - 2007, - 2006, - 2005, - // 2004 NA - // 2003 NA - 2002, - // 2001 NA - 2000, - 1999, - // 1998 NA - 1997, - 1996, - // 1995 NA - 1994, - 1993, - 1992, - 1991, - 1990, - 1989, - // 1988 NA - // 1987 NA - 1986, - // 1985 NA - 1984, - // 1983 NA - // 1982 NA - 1981, - 1980, - 1979, - 1978, - 1977, - 1976, - 1975, - // 1974 NA - 1973, - // 1972 NA - 1971, - 1970, - // 1969 NA - 1968, - 1967, - 1966, - // 1965 NA - 1964, - // 1963 NA - 1962, - 1961, - 1960, - // 1959 NA - // 1958 NA - // 1957 NA - 1956, - // 1955 NA - 1954, - 1953, - 1952, - // 1951 NA - 1950, - 1949, - 1948, - // 1947 NA - // 1946 NA - // 1945 NA - // 1944 NA - // 1943 NA - // 1942 NA - 1941, - 1940, - // 1939 NA - // 1938 NA - 1937, - // 1936 NA - // 1935 NA - // 1934 NA - // 1933 NA - // 1932 NA - // 1931 NA - // 1930 NA - // 1929 NA - 1928, - // 1927 NA - // 1926 NA - 1925, - // 1924 NA - 1923, - // 1922 NA - // 1921 NA - // 1920 NA - // 1919 NA - // 1918 NA - // 1917 NA - // 1916 NA - // 1915 NA - // 1914 NA - 1913, - 1912, - // 1911 NA - 1910, - 1909, - // 1908 NA - // 1907 NA - // 1906 NA - // 1905 NA - // 1904 NA - // 1903 NA - // 1902 NA - // 1901 NA - 1900, - // 1899 NA - 1898, - 1897, - 1896, - 1895, - 1894, - 1893, - 1892, - // 1891 NA - // 1890 NA - 1889, - // 1888 NA - // 1887 NA - // 1886 NA - // 1885 NA - 1884, - // 1883 NA - 1882, - 1881, - // 1880 NA - // 1879 NA - // 1878 NA - // 1877 NA - 1876, - 1875, - // 1874 NA - // 1873 NA - // 1872 NA - 1871, - // 1870 NA - // 1869 NA - 1868, - 1867, - 1866, - // 1865 NA - // 1864 NA - // 1863 NA - // 1862 NA - 1861, - 1860, - // 1859 NA - // 1858 NA - // 1857 NA - // 1856 NA - // 1855 NA - // 1854 NA - // 1853 NA - // 1852 NA - 1851, - // 1850 NA - // 1849 NA - // 1848 NA - 1847, - // 1846 NA - // 1845 NA - // 1844 NA - // 1843 NA - 1842, - 1841, - 1840, - 1839, - 1838, - 1837, - 1836, - 1835, - 1834, - 1833, - 1832, - 1831, - // 1830 NA - // 1829 NA - // 1828 NA - // 1827 NA - // 1826 NA - // 1825 NA - // 1824 NA - 1823, - // 1822 NA - 1821, - 1820, - // 1819 NA - 1818, - // 1817 NA - 1816, - 1815, - // 1814 NA - 1813, - // 1812 NA - // 1811 NA - // 1810 NA - 1809, - 1808, - // 1807 NA - 1806, - // 1805 NA - // 1804 NA - // 1803 NA - 1802, - // 1801 NA - // 1800 NA - 1799, - // 1798 NA - // 1797 NA - // 1796 NA - // 1795 NA - // 1794 NA - 1793, - // 1792 NA - // 1791 NA - // 1790 NA - // 1789 NA - // 1789 NA - // 1788 NA - // 1787 NA - // 1786 NA - 1785, - // 1784 NA - 1783, - 1782, - 1781, - 1780, - 1779, - // 1778 NA - // 1777 NA - // 1776 NA - // 1775 NA - // 1774 NA - // 1773 NA - // 1772 NA - // 1771 NA - // 1770 NA - // 1769 NA - 1768, - // 1767 NA - // 1766 NA - 1765, - // 1764 NA - 1763, - // 1762 NA - // 1761 NA - // 1760 NA - 1759, - 1758, - 1757, - // 1756 NA - 1755, - 1754, - 1753, - 1752, - 1751, - // 1750 NA - // 1749 NA - 1748, - // 1747 NA - // 1746 NA - // 1745 NA - // 1744 NA - // 1743 NA - 1742, - 1741, - 1740, - 1739, - 1738, - // 1737 NA - // 1736 NA - 1735, - 1734, - // 1733 NA - 1732, - // 1731 NA - 1730, - // 1729 NA - 1728, - 1727, - // 1726 NA - // 1725 NA - // 1724 NA - 1723, - // 1722 NA - // 1721 NA - // 1720 NA - 1719, - 1718, - // 1717 NA - 1716, - 1715, - 1714, - // 1713 NA - 1712, - 1711, - // 1710 NA - // 1709 NA - 1708, - 1707, - // 1706 NA - // 1705 NA - 1704, - 1703, - 1702, - 1701, - 1700, - 1699, - // 1698 NA - 1697, - 1696, - 1695, - // 1694 NA - // 1693 NA - 1692, - 1691, - // 1690 NA - // 1689 NA - // 1688 NA - // 1687 NA - 1686, - 1685, - // 1684 NA - // 1683 NA - 1682, - 1681, - // 1680 NA - 1679, - // 1678 NA - // 1677 NA - 1676, - 1675, - // 1674 NA - 1673, - // 1672 NA - 1671, - 1670, - // 1669 NA - // 1668 NA - // 1667 NA - // 1666 NA - // 1665 NA - 1664, - 1663, - // 1662 NA - // 1661 NA - 1660, - // 1659 NA - 1658, - // 1657 NA - 1656, - // 1655 NA - 1654, - // 1653 NA - 1652, - // 1651 NA - 1650, - 1649, - 1648, - 1647, - // 1646 NA - 1645, - // 1644 NA - 1643, - 1642, - 1641, - 1640, - 1639, - 1638, - // 1637 NA - // 1636 NA - // 1635 NA - 1634, - // 1633 NA - // 1632 NA - // 1631 NA - 1630, - 1629, - // 1628 NA - // 1627 NA - // 1626 NA - // 1625 NA - // 1624 NA - // 1623 NA - // 1622 NA - // 1621 NA - 1620, - 1619, - // 1618 NA - // 1617 NA - // 1616 NA - // 1615 NA - 1614, - // 1613 NA - // 1612 NA - // 1611 NA - // 1610 NA - // 1609 NA - 1608, - 1607, - 1606, - 1605, - 1604, - 1603, - // 1602 NA - // 1601 NA - // 1600 NA - // 1599 NA - // 1598 NA - // 1597 NA - 1596, - // 1595 NA - // 1594 NA - // 1593 NA - 1592, - 1591, - 1590, - 1589, - 1588, - // 1587 NA - 1586, - 1585, - // 1584 NA - // 1583 NA - 1582, - 1581, - 1580, - // 1579 NA - 1578, - 1577, - 1576, - // 1575 NA - 1574, - // 1573 NA - // 1572 NA - 1571, - 1570, - 1569, - 1568, - 1567, - // 1566 NA - 1565, - 1564, - 1563, - // 1562 NA - // 1561 NA - // 1560 NA - 1559, - 1558, - 1557, - // 1556 NA - // 1555 NA - 1554, - 1553, - 1552, - 1551, - 1550, - 1549, - 1548, - 1547, - 1546, - // 1545 NA - // 1544 NA - // 1543 NA - // 1542 NA - // 1541 NA - // 1540 NA - // 1539 NA - // 1538 NA - // 1537 NA - // 1536 NA - 1535, - // 1534 NA - 1533, - // 1532 NA - // 1531 NA - // 1530 NA - // 1529 NA - 1528, - // 1527 NA - // 1526 NA - // 1525 NA - // 1524 NA - // 1523 NA - // 1522 NA - 1521, - // 1520 NA - // 1519 NA - // 1518 NA - // 1517 NA - 1516, - // 1515 NA - // 1514 NA - 1513, - // 1512 NA - 1511, - // 1510 NA - // 1509 NA - // 1508 NA - // 1507 NA - // 1506 NA - // 1505 NA - // 1504 NA - // 1503 NA - // 1502 NA - // 1501 NA - 1500, - 1499, - // 1498 NA - // 1497 NA - // 1496 NA - // 1495 NA - 1494, - // 1493 NA - 1492, - 1491, - // 1490 NA - // 1489 NA - // 1488 NA - // 1487 NA - 1486, - // 1485 NA - // 1484 NA - // 1483 NA - // 1482 NA - // 1481 NA - 1480, - 1479, - 1478, - 1477, - // 1476 NA - // 1475 NA - // 1474 NA - // 1473 NA - // 1472 NA - // 1471 NA - // 1470 NA - // 1469 NA - 1468, - // 1467 NA - // 1466 NA - // 1465 NA - 1464, - // 1463 NA - // 1462 NA - // 1461 NA - // 1460 NA - // 1459 NA - // 1458 NA - // 1457 NA - // 1456 NA - // 1455 NA - // 1454 NA - // 1453 NA - // 1452 NA - // 1451 NA - // 1450 NA - // 1449 NA - // 1448 NA - // 1447 NA - // 1446 NA - // 1445 NA - // 1444 NA - // 1443 NA - // 1442 NA - // 1441 NA - // 1440 NA - // 1439 NA - // 1438 NA - // 1437 NA - // 1436 NA - // 1435 NA - // 1434 NA - // 1433 NA - // 1432 NA - // 1431 NA - // 1430 NA - // 1429 NA - // 1428 NA - // 1427 NA - // 1426 NA - 1425, - // 1424 NA - // 1423 NA - // 1422 NA - // 1421 NA - // 1420 NA - // 1419 NA - // 1418 NA - // 1417 NA - // 1416 NA - // 1415 NA - // 1414 NA - // 1413 NA - // 1412 NA - // 1411 NA - 1410, - // 1409 NA - // 1408 NA - // 1407 NA - 1406, - 1405, - // 1404 NA - // 1403 NA - // 1402 NA - 1401, - // 1400 NA - // 1399 NA - // 1398 NA - 1397, + 1561, + // 1560, + // 1559, + // 1558, + // 1557, + // 1556, + // 1555, + // 1554, + // 1553, + // 1552, + // 1551, + // 1550, + // 1549, + // 1548, + // 1547, + // 1546, + // 1545, + // 1544, + // 1543, + // 1542, + // 1541, + // 1540, + // 1539, + // 1538, + // 1537, + // 1536, + // 1535, + // 1534, + // 1533, + // 1532, + // 1531, + // 1530, + // 1529, + // 1528, + // 1527, + // 1526, + // 1525, + // 1524, + // 1523, + // 1522, + // 1521, + // 1520, + // 1519, + // 1518, + // 1517, + // 1516, + // 1515, + // 1514, + // 1513, + // 1512, + // 1511, + // 1510, + // 1509, + // 1508, + // 1507, + // 1506, + // 1505, + // 1504, + // 1503, + // 1502, + // 1501, + // 1500, + // 1499, + // 1498, + // 1497, + // 1496, + // 1495, + // 1494, + 1493, + // 1492, + // 1491, + // 1490, + // 1489, + // 1488, + // 1487, + // 1486, + // 1485, + // 1484, + 1483, + // 1482, + // 1481, + // 1480, + // 1479, + // 1478, + // 1477, + // 1476, + 1475, + // 1474, + // 1473, + // 1472, + // 1471, + // 1470, + // 1469, + // 1468, + // 1467, + // 1466, + // 1465, + // 1464, + // 1463, + // 1462, + // 1461, + // 1460, + // 1459, + // 1458, + // 1457, + // 1456, + // 1455, + // 1454, + // 1453, + // 1452, + // 1451, + // 1450, + // 1449, + // 1448, + // 1447, + // 1446, + // 1445, + // 1444, + // 1443, + 1442, + // 1441, + // 1440, + 1439, + // 1438, + // 1437, + // 1436, + 1435, + // 1434, + // 1433, + // 1432, + // 1431, + // 1430, + // 1429, + // 1428, + // 1427, + // 1426, + // 1425, + // 1424, + // 1423, + // 1422, + // 1421, + // 1420, + 1419, + // 1418, + // 1417, + // 1416, + // 1415, + // 1414, + // 1413, + // 1412, + // 1411, + // 1410, + // 1409, + // 1408, + // 1407, + // 1406, + // 1405, + // 1404, + 1403, + 1402, + // 1401, + // 1400, + // 1399, + // 1398, + // 1397, 1396, - // 1395 NA - 1394, - // 1393 NA - // 1392 NA - // 1391 NA - // 1390 NA - // 1389 NA - 1388, - // 1387 NA - // 1386 NA - // 1385 NA - 1384, - // 1383 NA - // 1382 NA - // 1381 NA - // 1380 NA - // 1379 NA - // 1378 NA - // 1377 NA - // 1376 NA - // 1375 NA - // 1374 NA - // 1373 NA - // 1372 NA - // 1371 NA - // 1370 NA - // 1369 NA - // 1368 NA - // 1367 NA - 1366, + // 1395, + // 1394, + 1393, + // 1392, + // 1391, + // 1390, + // 1389, + // 1388, + // 1387, + // 1386, + // 1385, + // 1384, + // 1383, + // 1382, + // 1381, + // 1380, + // 1379, + // 1378, + // 1377, + // 1376, + // 1375, + // 1374, + // 1373, + // 1372, + // 1371, + 1370, + // 1369, + // 1368, + // 1367, + // 1366, 1365, - // 1364 NA - // 1363 NA - // 1362 NA - // 1361 NA - // 1360 NA - // 1359 NA - // 1358 NA - // 1357 NA - // 1356 NA - // 1355 NA - // 1354 NA - // 1353 NA - 1352, - // 1351 NA - // 1350 NA - // 1349 NA - // 1348 NA - 1347, - 1346, - // 1345 NA - // 1344 NA - // 1343 NA - // 1342 NA - // 1341 NA - // 1340 NA - // 1339 NA - // 1338 NA - // 1337 NA - // 1336 NA - // 1335 NA - // 1334 NA - // 1333 NA - // 1332 NA - // 1331 NA - // 1330 NA - // 1329 NA - // 1328 NA - // 1327 NA - // 1326 NA - // 1325 NA - // 1324 NA - // 1323 NA - // 1322 NA - // 1321 NA - // 1320 NA - // 1319 NA - // 1318 NA - // 1317 NA - // 1316 NA - // 1315 NA - // 1314 NA - // 1313 NA - // 1312 NA - // 1311 NA - // 1310 NA - 1309, - // 1308 NA - // 1307 NA - // 1306 NA - 1305, + // 1364, + // 1363, + // 1362, + // 1361, + // 1360, + // 1359, + // 1358, + 1357, + // 1356, + // 1355, + // 1354, + // 1353, + // 1352, + // 1351, + // 1350, + // 1349, + // 1348, + // 1347, + // 1346, + // 1345, + // 1344, + // 1343, + // 1342, + // 1341, + // 1340, + // 1339, + // 1338, + // 1337, + // 1336, + // 1335, + // 1334, + 1333, + // 1332, + // 1331, + // 1330, + 1329, + // 1328, + // 1327, + // 1326, + // 1325, + // 1324, + // 1323, + // 1322, + // 1321, + // 1320, + // 1319, + // 1318, + // 1317, + // 1316, + // 1315, + // 1314, + // 1313, + // 1312, + // 1311, + // 1310, + // 1309, + // 1308, + // 1307, + // 1306, + // 1305, 1304, - // 1303 NA - // 1302 NA - // 1301 NA - // 1300 NA - // 1299 NA - // 1298 NA - // 1297 NA - 1296, - // 1295 NA - // 1294 NA - // 1293 NA - 1292, - // 1291 NA - // 1290 NA - // 1289 NA - // 1288 NA - // 1287 NA - // 1286 NA - 1285, - 1284, - // 1283 NA - 1282, - 1281, - // 1280 NA - // 1279 NA - // 1278 NA - // 1277 NA - 1276, - // 1275 NA - // 1274 NA - // 1273 NA - // 1272 NA + // 1303, + 1302, + // 1301, + // 1300, + // 1299, + // 1298, + // 1297, + // 1296, + // 1295, + // 1294, + // 1293, + // 1292, + // 1291, + // 1290, + 1289, + // 1288, + // 1287, + // 1286, + // 1285, + // 1284, + // 1283, + // 1282, + // 1281, + // 1280, + // 1279, + // 1278, + // 1277, + // 1276, + // 1275, + // 1274, + // 1273, + // 1272, 1271, - // 1270 NA - 1269, - // 1268 NA - 1267, - 1266, - // 1265 NA - // 1264 NA - // 1263 NA - // 1262 NA - // 1261 NA - // 1260 NA - 1259, - // 1258 NA - // 1257 NA - // 1256 NA - // 1255 NA - // 1254 NA - // 1253 NA - // 1252 NA - // 1251 NA - // 1250 NA - // 1249 NA - // 1248 NA - // 1247 NA - // 1246 NA - // 1245 NA - // 1244 NA - // 1243 NA - // 1242 NA - // 1241 NA - // 1240 NA - // 1239 NA - // 1238 NA - 1237, - 1236, - // 1235 NA - // 1234 NA - // 1233 NA - // 1232 NA - // 1231 NA - // 1230 NA - // 1229 NA - 1228, - // 1227 NA - // 1226 NA - // 1225 NA - // 1224 NA + // 1270, + // 1269, + // 1268, + // 1267, + // 1266, + // 1265, + // 1264, + // 1263, + 1262, + // 1261, + // 1260, + // 1259, + // 1258, + // 1257, + // 1256, + // 1255, + // 1254, + // 1253, + // 1252, + // 1251, + 1250, + // 1249, + // 1248, + // 1247, + // 1246, + // 1245, + // 1244, + // 1243, + // 1242, + // 1241, + // 1240, + // 1239, + 1238, + // 1237, + // 1236, + // 1235, + // 1234, + // 1233, + // 1232, + // 1231, + 1230, + 1229, + // 1228, + // 1227, + 1226, + 1225, + 1224, 1223, - // 1222 NA - // 1221 NA - // 1220 NA - // 1219 NA - // 1218 NA - // 1217 NA - // 1216 NA - // 1215 NA - // 1214 NA - // 1213 NA - // 1212 NA - // 1211 NA - // 1210 NA - // 1209 NA - // 1208 NA - // 1207 NA - // 1206 NA - // 1205 NA - // 1204 NA - // 1203 NA - // 1202 NA - // 1201 NA - // 1200 NA - // 1199 NA - // 1198 NA - // 1197 NA - // 1196 NA - // 1195 NA - // 1194 NA - // 1193 NA - // 1192 NA - // 1191 NA - // 1190 NA - // 1189 NA - // 1188 NA - // 1187 NA - // 1186 NA - // 1185 NA - // 1184 NA - // 1183 NA - // 1182 NA - 1181, - 1180, - 1179, - 1178, - // 1177 NA - // 1176 NA - // 1175 NA - // 1174 NA - 1173, - // 1172 NA - // 1171 NA - // 1170 NA - // 1169 NA - 1168, - 1167, - 1166, - // 1165 NA - 1164, - 1163, - // 1162 NA - 1161, - 1160, - // 1159 NA - // 1158 NA - 1157, - // 1156 NA - // 1155 NA - // 1154 NA - 1153, - // 1152 NA - 1151, - 1150, - 1149, - // 1148 NA - 1147, - // 1146 NA - // 1145 NA - 1144, - 1143, - 1142, - 1141, - 1140, - // 1139 NA - // 1138 NA - 1137, - 1136, - // 1135 NA - // 1134 NA - // 1133 NA - 1132, - // 1131 NA - // 1130 NA - // 1129 NA - // 1128 NA - // 1127 NA - 1126, - // 1125 NA - // 1124 NA - 1123, - // 1122 NA - 1121, - 1120, - 1119, - 1118, - 1117, - 1116, - // 1115 NA - 1114, - 1113, - 1112, - 1111, - 1110, - // 1109 NA + 1222, + 1221, + // 1220, + 1219, + // 1218, + // 1217, + // 1216, + // 1215, + // 1214, + // 1213, + // 1212, + // 1211, + 1210, + // 1209, + // 1208, + 1207, + 1206, + 1205, + 1204, + // 1203, + // 1202, + // 1201, + 1200, + // 1199, + // 1198, + // 1197, + // 1196, + // 1195, + // 1194, + // 1193, + // 1192, + // 1191, + // 1190, + 1189, + 1188, + // 1187, + 1186, + // 1185, + // 1184, + // 1183, + // 1182, + // 1181, + // 1180, + // 1179, + // 1178, + // 1177, + // 1176, + // 1175, + // 1174, + // 1173, + // 1172, + // 1171, + // 1170, + // 1169, + // 1168, + // 1167, + // 1166, + // 1165, + // 1164, + // 1163, + // 1162, + // 1161, + // 1160, + // 1159, + 1158, + // 1157, + // 1156, + // 1155, + // 1154, + // 1153, + // 1152, + // 1151, + // 1150, + // 1149, + // 1148, + // 1147, + // 1146, + // 1145, + // 1144, + // 1143, + // 1142, + // 1141, + // 1140, + // 1139, + // 1138, + // 1137, + // 1136, + // 1135, + // 1134, + // 1133, + // 1132, + // 1131, + // 1130, + // 1129, + // 1128, + // 1127, + // 1126, + // 1125, + // 1124, + // 1123, + // 1122, + // 1121, + // 1120, + // 1119, + // 1118, + // 1117, + // 1116, + // 1115, + // 1114, + // 1113, + // 1112, + // 1111, + // 1110, + // 1109, 1108, - 1107, - // 1106 NA - 1105, - // 1104 NA - // 1103 NA - 1102, - 1101, - // 1100 NA - // 1099 NA - // 1098 NA - // 1097 NA - 1096, - // 1095 NA - 1094, - 1093, - 1092, - 1091, - 1090, - 1089, - 1088, - 1087, - 1086, - 1085, - 1084, - // 1083 NA - // 1082 NA - 1081, - // 1080 NA - // 1079 NA - // 1078 NA - // 1077 NA - 1076, - 1075, - // 1074 NA - // 1073 NA - 1072, - 1071, - // 1070 NA - // 1069 NA - 1068, - // 1067 NA - // 1066 NA - 1065, - 1064, - // 1063 NA - // 1062 NA - 1061, - // 1060 NA - 1059, - // 1058 NA - 1057, - 1056, - 1055, - 1054, - 1053, - 1052, - 1051, - 1050, - 1049, - 1048, - 1047, - 1046, - // 1045 NA - // 1044 NA - // 1043 NA - 1042, - 1041, - // 1040 NA - // 1039 NA - // 1038 NA - 1037, - 1036, - 1035, - 1034, - // 1033 NA - 1032, - // 1031 NA - 1030, - 1029, - // 1028 NA - 1027, - // 1026 NA - // 1025 NA - // 1024 NA - // 1023 NA - // 1022 NA - // 1021 NA - // 1020 NA - // 1019 NA - 1018, - 1017, - // 1016 NA - 1015, - // 1014 NA - 1013, - // 1012 NA - // 1011 NA - // 1010 NA - // 1009 NA - // 1008 NA + // 1107, + // 1106, + // 1105, + // 1104, + // 1103, + // 1102, + // 1101, + // 1100, + // 1099, + // 1098, + // 1097, + // 1096, + // 1095, + // 1094, + // 1093, + // 1092, + // 1091, + // 1090, + // 1089, + // 1088, + // 1087, + // 1086, + // 1085, + // 1084, + // 1083, + // 1082, + // 1081, + // 1080, + // 1079, + // 1078, + // 1077, + // 1076, + // 1075, + // 1074, + // 1073, + // 1072, + // 1071, + // 1070, + // 1069, + // 1068, + // 1067, + // 1066, + // 1065, + // 1064, + // 1063, + // 1062, + // 1061, + // 1060, + // 1059, + // 1058, + // 1057, + // 1056, + // 1055, + // 1054, + // 1053, + // 1052, + // 1051, + // 1050, + // 1049, + // 1048, + // 1047, + // 1046, + // 1045, + // 1044, + // 1043, + // 1042, + // 1041, + // 1040, + // 1039, + // 1038, + // 1037, + // 1036, + // 1035, + // 1034, + // 1033, + // 1032, + // 1031, + // 1030, + // 1029, + // 1028, + // 1027, + // 1026, + 1025, + 1024, + // 1023, + // 1022, + // 1021, + // 1020, + 1019, + // 1018, + // 1017, + // 1016, + // 1015, + // 1014, + // 1013, + // 1012, + // 1011, + // 1010, + // 1009, + // 1008, 1007, - 1006, - // 1005 NA - // 1004 NA - // 1003 NA - // 1002 NA - 1001, - 1000, - // 999 NA - 998, - // 997 NA - // 996 NA - // 995 NA - // 994 NA - // 993 NA - // 992 NA - 991, - // 990 NA - 989, - // 988 NA - // 987 NA - // 986 NA - // 985 NA - 984, - // 983 NA - // 982 NA - 981, - 980, - // 979 NA - 978, - 977, - // 976 NA + // 1006, + // 1005, + // 1004, + // 1003, + // 1002, + // 1001, + // 1000, + // 999, + // 998, + // 997, + // 996, + // 995, + // 994, + // 993, + // 992, + // 991, + // 990, + // 989, + // 988, + // 987, + // 986, + // 985, + // 984, + // 983, + // 982, + // 981, + // 980, + // 979, + // 978, + // 977, + // 976, 975, 974, - 973, - 972, - // 971 NA - // 970 NA - // 969 NA - // 968 NA - // 967 NA - // 966 NA - // 965 NA - // 964 NA - 963, - // 962 NA - 961, - // 960 NA - // 959 NA - 958, - 957, - // 956 NA + // 973, + // 972, + // 971, + // 970, + // 969, + // 968, + // 967, + // 966, + // 965, + // 964, + // 963, + 962, + // 961, + // 960, + // 959, + // 958, + // 957, + // 956, 955, - // 954 NA - 953, - 952, - 951, - 950, - 949, - // 948 NA - // 947 NA - 946, - 945, + // 954, + // 953, + // 952, + // 951, + // 950, + // 949, + // 948, + // 947, + // 946, + // 945, 944, - // 943 NA - 942, - 941, - // 940 NA - 939, - // 938 NA - 937, - 936, - // 935 NA - // 934 NA - 933, - 932, - // 931 NA - // 930 NA - 929, - // 928 NA - // 927 NA - 926, - 925, - // 924 NA - // 923 NA - 922, - // 921 NA - // 920 NA - // 919 NA - // 918 NA - // 917 NA - 916, - 915, - // 914 NA - // 913 NA - 912, - // 911 NA - // 910 NA - // 909 NA - // 908 NA - // 907 NA - // 906 NA - // 905 NA - 904, - 903, - // 902 NA - 901, - // 900 NA - // 899 NA - 898, - // 897 NA - 896, + // 943, + // 942, + // 941, + // 940, + // 939, + // 938, + // 937, + // 936, + // 935, + // 934, + // 933, + // 932, + // 931, + // 930, + // 929, + // 928, + // 927, + // 926, + // 925, + // 924, + // 923, + // 922, + // 921, + // 920, + // 919, + // 918, + // 917, + // 916, + // 915, + // 914, + // 913, + // 912, + // 911, + // 910, + // 909, + // 908, + // 907, + 906, + // 905, + // 904, + // 903, + // 902, + // 901, + // 900, + // 899, + // 898, + // 897, + // 896, 895, - // 894 NA - 893, - // 892 NA - 891, - // 890 NA - 889, - 888, - 887, - // 886 NA - 885, - // 884 NA - 883, - 882, - 881, - // 880 NA - 879, - 878, - 877, - // 876 NA - // 875 NA - // 874 NA - // 873 NA - // 872 NA - 871, - 870, - // 869 NA - 868, - // 867 NA - // 866 NA - // 865 NA - // 864 NA - // 863 NA - // 862 NA - // 861 NA - // 860 NA - 859, + 894, + // 893, + // 892, + // 891, + // 890, + // 889, + // 888, + // 887, + // 886, + // 885, + // 884, + // 883, + // 882, + // 881, + // 880, + // 879, + // 878, + // 877, + // 876, + // 875, + // 874, + // 873, + // 872, + // 871, + // 870, + // 869, + // 868, + // 867, + // 866, + // 865, + // 864, + // 863, + 862, + 861, + // 860, + // 859, 858, - 857, - 856, - // 855 NA - // 854 NA - 853, - // 852 NA - // 851 NA - // 850 NA - 849, - 848, + // 857, + // 856, + // 855, + // 854, + // 853, + // 852, + // 851, + // 850, + // 849, + // 848, 847, - // 846 NA - 845, - 844, - 843, - // 842 NA - // 841 NA - // 840 NA - // 839 NA - // 838 NA - // 837 NA - 836, - 835, - 834, - 833, - 832, - 831, - 830, - // 829 NA - 828, - // 827 NA - 826, - 825, - // 824 NA - 823, - 822, - // 821 NA - 820, - 819, - 818, - 817, - 816, - 815, - 814, - 813, - // 812 NA - 811, - 810, - 809, - // 808 NA - 807, - 806, - 805, - // 804 NA - 803, - 802, - 801, - 800, - 799, - 798, - // 797 NA - // 796 NA - 795, - // 794 NA - 793, + // 846, + // 845, + // 844, + // 843, + // 842, + // 841, + // 840, + // 839, + // 838, + // 837, + // 836, + // 835, + // 834, + // 833, + // 832, + // 831, + // 830, + // 829, + // 828, + // 827, + // 826, + // 825, + // 824, + // 823, + // 822, + // 821, + // 820, + // 819, + // 818, + // 817, + // 816, + // 815, + // 814, + // 813, + // 812, + // 811, + // 810, + // 809, + // 808, + // 807, + // 806, + // 805, + // 804, + // 803, + // 802, + // 801, + // 800, + // 799, + // 798, + // 797, + // 796, + // 795, + // 794, + // 793, 792, - 791, - 790, - 789, - // 788 NA - 787, - 786, - 785, - 784, - // 783 NA - 782, - 781, - 780, - 779, - 778, - // 777 NA - 776, - 775, - 774, - 773, - // 772 NA - 771, - // 770 NA - 769, - 768, - // 767 NA - // 766 NA - 765, - 764, - // 763 NA - // 762 NA - // 761 NA - 760, - // 759 NA - 758, - // 757 NA - // 756 NA - 755, - 754, - 753, - // 752 NA - // 751 NA - // 750 NA - 749, - 748, - 747, - 746, - 745, - // 744 NA - 743, - 742, - 741, - 740, - 739, - // 738 NA - 737, + // 791, + // 790, + // 789, + // 788, + // 787, + // 786, + // 785, + // 784, + // 783, + // 782, + // 781, + // 780, + // 779, + // 778, + // 777, + // 776, + // 775, + // 774, + // 773, + // 772, + // 771, + // 770, + // 769, + // 768, + // 767, + // 766, + // 765, + // 764, + // 763, + // 762, + // 761, + // 760, + // 759, + // 758, + // 757, + // 756, + // 755, + // 754, + // 753, + // 752, + // 751, + // 750, + // 749, + // 748, + // 747, + // 746, + // 745, + // 744, + // 743, + // 742, + // 741, + // 740, + // 739, + // 738, + // 737, 736, - // 735 NA - 734, - // 733 NA - 732, - // 731 NA - // 730 NA - 729, - // 728 NA - // 727 NA - // 726 NA - // 725 NA - // 724 NA + // 735, + // 734, + // 733, + // 732, + // 731, + // 730, + // 729, + // 728, + // 727, + // 726, + // 725, + // 724, 723, - 722, + // 722, 721, - // 720 NA - 719, - 718, - 717, - 716, - 715, - 714, - 713, - 712, - 711, + // 720, + // 719, + // 718, + // 717, + // 716, + // 715, + // 714, + // 713, + // 712, + // 711, 710, - 709, - 708, - 707, - 706, - // 705 NA - 704, - // 703 NA - 702, - // 701 NA + // 709, + // 708, + // 707, + // 706, + // 705, + // 704, + 703, + // 702, + // 701, 700, 699, - 698, - 697, - 696, - 695, - 694, - 693, - // 692 NA - // 691 NA - 690, + // 698, + // 697, + // 696, + // 695, + // 694, + // 693, + 692, + // 691, + // 690, 689, - 688, - // 687 NA - 686, - 685, - // 684 NA - // 683 NA - 682, - // 681 NA + // 688, + // 687, + // 686, + // 685, + 684, + // 683, + // 682, + // 681, 680, - // 679 NA - // 678 NA - // 677 NA - // 676 NA - 675, - // 674 NA + 679, + 678, + // 677, + 676, + // 675, + 674, 673, 672, - 671, - 670, - // 669 NA + // 671, + // 670, + // 669, 668, 667, - // 666 NA + 666, 665, - // 664 NA - // 663 NA + 664, + // 663, 662, - // 661 NA + 661, 660, 659, 658, - // 657 NA + 657, 656, 655, 654, - 653, - // 652 NA - 651, - // 650 NA - 649, - // 648 NA - // 647 NA - 646, - 645, - // 644 NA - 643, - 642, - // 641 NA - 640, - 639, - // 638 NA - 637, - 636, - 635, + // 653, + 652, + // 651, + 650, + // 649, + // 648, + // 647, + // 646, + // 645, + // 644, + // 643, + // 642, + 641, + // 640, + // 639, + // 638, + // 637, + // 636, + // 635, 634, - 633, - // 632 NA - 631, - 630, - 629, - // 628 NA - // 627 NA - // 626 NA - // 625 NA - // 624 NA - 623, - // 622 NA - // 621 NA - // 620 NA - // 619 NA - // 618 NA - 617, - 616, - 615, + // 633, + // 632, + // 631, + // 630, + // 629, + // 628, + // 627, + // 626, + // 625, + // 624, + // 623, + 622, + // 621, + // 620, + // 619, + 618, + // 617, + // 616, + // 615, 614, 613, 612, - // 611 NA - // 610 NA - 609, + // 611, + // 610, + // 609, 608, - // 607 NA + 607, 606, 605, - 604, - 603, - 602, + // 604, + // 603, + // 602, 601, 600, 599, - 598, + // 598, 597, - // 596 NA + // 596, 595, - 594, - 593, - // 592 NA - // 591 NA + // 594, + // 593, + // 592, + 591, 590, - // 589 NA - 588, - 587, - // 586 NA - 585, - // 584 NA - // 583 NA + // 589, + // 588, + // 587, + // 586, + // 585, + 584, + // 583, 582, - // 581 NA + // 581, 580, 579, - 578, - 577, - 576, - 575, + // 578, + // 577, + // 576, + // 575, 574, - 573, - 572, - // 571 NA - // 570 NA - 569, - 568, - 567, - 566, + // 573, + // 572, + 571, + // 570, + // 569, + // 568, + // 567, + // 566, 565, - // 564 NA - 563, - 562, + // 564, + // 563, + // 562, 561, - // 560 NA - 559, - // 558 NA - // 557 NA - // 556 NA - // 555 NA + // 560, + // 559, + // 558, + // 557, + // 556, + 555, 554, 553, 552, - 551, + // 551, 550, - 549, - // 548 NA - 547, - 546, - 545, - // 544 NA - 543, - 542, - 541, - // 540 NA - 539, - 538, - 537, + // 549, + // 548, + // 547, + // 546, + // 545, + // 544, + // 543, + // 542, + // 541, + // 540, + // 539, + // 538, + // 537, 536, - 535, - // 534 NA - 533, - 532, - 531, - 530, - 529, + // 535, + // 534, + // 533, + // 532, + // 531, + // 530, + // 529, 528, - 527, - 526, - 525, + // 527, + // 526, + // 525, 524, - // 523 NA - // 522 NA - 521, - 520, - 519, + // 523, + // 522, + // 521, + // 520, + // 519, 518, 517, - 516, - 515, - 514, - 513, - // 512 NA - // 511 NA - // 510 NA - // 509 NA - 508, - // 507 NA - // 506 NA - // 505 NA - // 504 NA - 503, + // 516, + // 515, + // 514, + // 513, + // 512, + // 511, + // 510, + // 509, + // 508, + // 507, + // 506, + // 505, + // 504, + // 503, 502, - // 501 NA - 500, + // 501, + // 500, 499, - // 498 NA - 497, - // 496 NA - // 495 NA - 494, - 493, - 492, - 491, + 498, + // 497, + // 496, + // 495, + // 494, + // 493, + // 492, + // 491, 490, - 489, - 488, + // 489, + // 488, 487, 486, 485, - // 484 NA + 484, 483, - // 482 NA - // 481 NA - // 480 NA - // 479 NA + 482, + // 481, + 480, + 479, 478, 477, - // 476 NA - // 475 NA - 474, + // 476, + // 475, + // 474, 473, 472, - // 471 NA + 471, 470, - // 469 NA - 468, - 467, - 466, - // 465 NA - // 464 NA - 463, - 462, - // 461 NA - // 460 NA - // 459 NA + // 469, + // 468, + // 467, + // 466, + 465, + // 464, + // 463, + // 462, + 461, + // 460, + 459, 458, 457, - 456, - 455, - 454, - // 453 NA - 452, - // 451 NA - 450, - 449, - // 448 NA - 447, + // 456, + // 455, + // 454, + 453, + // 452, + // 451, + // 450, + // 449, + 448, + // 447, 446, - 445, + // 445, 444, - // 443 NA + 443, 442, - 441, + // 441, 440, 439, - 438, + // 438, 437, - 436, - 435, + // 436, + // 435, 434, 433, - // 432 NA - // 431 NA - // 430 NA - // 429 NA - // 428 NA + // 432, + 431, + // 430, + // 429, + // 428, 427, - // 426 NA - 425, - // 424 NA + 426, + // 425, + 424, 423, - 422, + // 422, 421, - // 420 NA - 419, - 418, - 417, - 416, - 415, - 414, - // 413 NA - // 412 NA - 411, - 410, - // 409 NA + 420, + // 419, + // 418, + // 417, + // 416, + // 415, + // 414, + // 413, + // 412, + // 411, + // 410, + // 409, 408, 407, 406, 405, - // 404 NA - // 403 NA - // 402 NA - // 401 NA - // 400 NA - // 399 NA - // 398 NA - 397, - 396, - 395, - // 394 NA - // 393 NA - 392, + 404, + // 403, + 402, + // 401, + 400, + // 399, + 398, + // 397, + // 396, + // 395, + 394, + 393, + // 392, 391, 390, 389, 388, - 387, - 386, - // 385 NA - // 384 NA - 383, - 382, + // 387, + // 386, + 385, + // 384, + // 383, + // 382, 381, - // 380 NA + 380, 379, 378, 377, 376, - 375, + // 375, 374, - 373, - 372, - 371, - 370, - 369, - 368, - 367, - // 366 NA - 365, + // 373, + // 372, + // 371, + // 370, + // 369, + // 368, + // 367, + // 366, + // 365, 364, - // 363 NA - 362, - 361, + // 363, + // 362, + // 361, 360, 359, 358, - 357, - // 356 NA - 355, - // 354 NA + // 357, + // 356, + // 355, + // 354, 353, 352, 351, - 350, - 349, - 348, + // 350, + // 349, + // 348, 347, - 346, + // 346, 345, - 344, + // 344, 343, - // 342 NA + // 342, 341, - // 340 NA + // 340, 339, 338, 337, 336, 335, 334, - // 333 NA - // 332 NA + 333, + // 332, 331, 330, - 329, + // 329, 328, 327, - // 326 NA + 326, 325, 324, 323, - // 322 NA - // 321 NA + 322, + // 321, 320, - // 319 NA - 318, - 317, - // 316 NA + 319, + // 318, + // 317, + // 316, 315, 314, - 313, - 312, + // 313, + // 312, 311, 310, 309, 308, - // 307 NA + 307, 306, 305, - // 304 NA - 303, + // 304, + // 303, 302, 301, - // 300 NA - // 299 NA + 300, + 299, 298, 297, - 296, + // 296, 295, 294, 293, @@ -2158,11 +1352,11 @@ static const int included_patches[] = { 290, 289, 288, - // 287 NA - 286, + 287, + // 286, 285, 284, - // 283 NA + 283, 282, 281, 280, @@ -2172,119 +1366,119 @@ static const int included_patches[] = { 276, 275, 274, - // 273 NA + 273, 272, - // 271 NA - // 270 NA + 271, + 270, 269, 268, 267, 266, - 265, - 264, - // 263 NA - 262, - 261, + // 265, + // 264, + // 263, + // 262, + // 261, 260, - // 259 NA - // 258 NA - // 257 NA - 256, - // 255 NA - // 254 NA + 259, + 258, + 257, + // 256, + // 255, + // 254, 253, - // 252 NA - 251, - // 250 NA + // 252, + // 251, + 250, 249, 248, 247, - // 246 NA + 246, 245, - // 244 NA + 244, 243, 242, 241, 240, 239, - // 238 NA + // 238, 237, 236, 235, 234, - 233, + // 233, 232, - 231, - 230, + // 231, + // 230, 229, - // 228 NA - // 227 NA + // 228, + 227, 226, - // 225 NA - // 224 NA - // 223 NA - // 222 NA + 225, + 224, + 223, + 222, 221, 220, 219, 218, - // 217 NA - // 216 NA + 217, + // 216, 215, - // 214 NA + 214, 213, - // 212 NA + 212, 211, - 210, + // 210, 209, - // 208 NA - 207, - // 206 NA + 208, + // 207, + 206, 205, 204, 203, - // 202 NA + 202, 201, - // 200 NA + 200, 199, - // 198 NA - // 197 NA - // 196 NA - // 195 NA - // 194 NA + 198, + // 197, + 196, + 195, + 194, 193, 192, 191, - // 190 NA - // 189 NA - // 188 NA + 190, + 189, + 188, 187, 186, - // 185 NA - 184, - // 183 NA - // 182 NA + 185, + // 184, + 183, + 182, 181, - // 180 NA - // 179 NA + 180, + 179, 178, - // 177 NA - // 176 NA - // 175 NA - // 174 NA + 177, + 176, + 175, + 174, 173, 172, 171, 170, 169, - // 168 NA + 168, 167, 166, 165, - // 164 NA - // 163 NA - // 162 NA - // 161 NA + 164, + 163, + 162, + 161, 160, 159, 158, @@ -2445,20 +1639,10 @@ static const int included_patches[] = { 3, 2, 1, - 0 + 0, }; // clang-format on -/// Place to put a short description when adding a feature with a patch. -/// Keep it short, e.g.,: "relative numbers", "persistent undo". -/// Also add a comment marker to separate the lines. -/// See the official Vim patches for the diff format: It must use a context of -/// one line only. Create it by hand or use "diff -C2" and edit the patch. -static char *(extra_patches[]) = { - // Add your patch description below this line - NULL -}; - /// Compares a version string to the current Nvim version. /// /// @param version Version string like "1.3.42" @@ -2582,13 +1766,7 @@ static void list_features(void) msg_putchar('\n'); } } else { - while (msg_col % width) { - int old_msg_col = msg_col; - msg_putchar(' '); - if (old_msg_col == msg_col) { - break; // XXX: Avoid infinite loop. - } - } + msg_putchar(' '); } } else { if (msg_col > 0) { @@ -2596,30 +1774,27 @@ static void list_features(void) } } } - MSG_PUTS("For differences from Vim, see :help vim-differences\n\n"); + MSG_PUTS("See \":help feature-compile\"\n\n"); +} + +void list_lua_version(void) +{ + typval_T luaver_tv; + typval_T arg = { .v_type = VAR_UNKNOWN }; // No args. + char *luaver_expr = "((jit and jit.version) and jit.version or _VERSION)"; + executor_eval_lua(cstr_as_string(luaver_expr), &arg, &luaver_tv); + assert(luaver_tv.v_type == VAR_STRING); + MSG(luaver_tv.vval.v_string); + xfree(luaver_tv.vval.v_string); } void list_version(void) { - // When adding features here, don't forget to update the list of - // internal variables in eval.c! MSG(longVersion); MSG(version_buildtype); + list_lua_version(); MSG(version_cflags); - // Print the list of extra patch descriptions if there is at least one. - char *s = ""; - if (extra_patches[0] != NULL) { - MSG_PUTS(_("\nExtra patches: ")); - s = ""; - - for (int i = 0; extra_patches[i] != NULL; ++i) { - MSG_PUTS(s); - s = ", "; - MSG_PUTS(extra_patches[i]); - } - } - #ifdef HAVE_PATHDEF if ((*compiled_user != NUL) || (*compiled_sys != NUL)) { @@ -2637,7 +1812,7 @@ void list_version(void) } #endif // ifdef HAVE_PATHDEF - version_msg(_("\n\nOptional features included (+) or not (-): ")); + version_msg(_("\n\nFeatures: ")); list_features(); @@ -2660,6 +1835,8 @@ void list_version(void) version_msg("\"\n"); } #endif // ifdef HAVE_PATHDEF + + version_msg("\nRun :checkhealth for more info"); } /// Output a string for the version message. If it's going to wrap, output a @@ -2686,7 +1863,7 @@ static void version_msg(char *s) /// Show the intro message when not editing a file. void maybe_intro_message(void) { - if (bufempty() + if (BUFEMPTY() && (curbuf->b_fname == NULL) && (firstwin->w_next == NULL) && (vim_strchr(p_shm, SHM_INTRO) == NULL)) { @@ -2709,12 +1886,11 @@ void intro_message(int colon) static char *(lines[]) = { N_(NVIM_VERSION_LONG), "", - N_("by Bram Moolenaar et al."), N_("Nvim is open source and freely distributable"), N_("https://neovim.io/community"), "", N_("type :help nvim<Enter> if you are new! "), - N_("type :CheckHealth<Enter> to optimize Nvim"), + N_("type :checkhealth<Enter> to optimize Nvim"), N_("type :q<Enter> to exit "), N_("type :help<Enter> for help "), "", diff --git a/src/nvim/version.h b/src/nvim/version.h index a0babfb156..c10f6fa534 100644 --- a/src/nvim/version.h +++ b/src/nvim/version.h @@ -10,14 +10,14 @@ extern char* longVersion; // // Vim version number, name, etc. Patchlevel is defined in version.c. // -#define VIM_VERSION_MAJOR 7 -#define VIM_VERSION_MINOR 4 +#define VIM_VERSION_MAJOR 8 +#define VIM_VERSION_MINOR 0 #define VIM_VERSION_100 (VIM_VERSION_MAJOR * 100 + VIM_VERSION_MINOR) // used for the runtime directory name -#define VIM_VERSION_NODOT "vim74" +#define VIM_VERSION_NODOT "vim80" // swap file compatibility (max. length is 6 chars) -#define VIM_VERSION_SHORT "7.4" +#define VIM_VERSION_SHORT "8.0" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "version.h.generated.h" diff --git a/src/nvim/vim.h b/src/nvim/vim.h index f29ccdd296..0c13d331c8 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -4,37 +4,28 @@ #include "nvim/types.h" #include "nvim/pos.h" // for linenr_T, MAXCOL, etc... -/* Some defines from the old feature.h */ +// Some defines from the old feature.h #define SESSION_FILE "Session.vim" #define MAX_MSG_HIST_LEN 200 #define SYS_OPTWIN_FILE "$VIMRUNTIME/optwin.vim" #define RUNTIME_DIRNAME "runtime" -/* end */ + #include "auto/config.h" #define HAVE_PATHDEF -/* - * Check if configure correctly managed to find sizeof(int). If this failed, - * it becomes zero. This is likely a problem of not being able to run the - * test program. Other items from configure may also be wrong then! - */ +// Check if configure correctly managed to find sizeof(int). If this failed, +// it becomes zero. This is likely a problem of not being able to run the +// test program. Other items from configure may also be wrong then! #if (SIZEOF_INT == 0) # error Configure did not run properly. #endif -#include "nvim/os/os_defs.h" /* bring lots of system header files */ +#include "nvim/os/os_defs.h" // bring lots of system header files /// length of a buffer to store a number in ASCII (64 bits binary + NUL) enum { NUMBUFLEN = 65 }; -// flags for vim_str2nr() -#define STR2NR_BIN 1 -#define STR2NR_OCT 2 -#define STR2NR_HEX 4 -#define STR2NR_ALL (STR2NR_BIN + STR2NR_OCT + STR2NR_HEX) -#define STR2NR_FORCE 8 // only when ONE of the above is used - #define MAX_TYPENR 65535 #define ROOT_UID 0 @@ -44,41 +35,41 @@ enum { NUMBUFLEN = 65 }; #include "nvim/gettext.h" -/* special attribute addition: Put message in history */ +// special attribute addition: Put message in history #define MSG_HIST 0x1000 -/* - * values for State - * - * The lower bits up to 0x20 are used to distinguish normal/visual/op_pending - * and cmdline/insert+replace mode. This is used for mapping. If none of - * these bits are set, no mapping is done. - * The upper bits are used to distinguish between other states. - */ -#define NORMAL 0x01 /* Normal mode, command expected */ -#define VISUAL 0x02 /* Visual mode - use get_real_state() */ -#define OP_PENDING 0x04 /* Normal mode, operator is pending - use - get_real_state() */ -#define CMDLINE 0x08 /* Editing command line */ -#define INSERT 0x10 /* Insert mode */ -#define LANGMAP 0x20 /* Language mapping, can be combined with - INSERT and CMDLINE */ - -#define REPLACE_FLAG 0x40 /* Replace mode flag */ + +// values for State +// +// The lower bits up to 0x20 are used to distinguish normal/visual/op_pending +// and cmdline/insert+replace mode. This is used for mapping. If none of +// these bits are set, no mapping is done. +// The upper bits are used to distinguish between other states. + +#define NORMAL 0x01 // Normal mode, command expected +#define VISUAL 0x02 // Visual mode - use get_real_state() +#define OP_PENDING 0x04 // Normal mode, operator is pending - use + // get_real_state() +#define CMDLINE 0x08 // Editing command line +#define INSERT 0x10 // Insert mode +#define LANGMAP 0x20 // Language mapping, can be combined with + // INSERT and CMDLINE + +#define REPLACE_FLAG 0x40 // Replace mode flag #define REPLACE (REPLACE_FLAG + INSERT) -# define VREPLACE_FLAG 0x80 /* Virtual-replace mode flag */ +# define VREPLACE_FLAG 0x80 // Virtual-replace mode flag # define VREPLACE (REPLACE_FLAG + VREPLACE_FLAG + INSERT) #define LREPLACE (REPLACE_FLAG + LANGMAP) -#define NORMAL_BUSY (0x100 + NORMAL) /* Normal mode, busy with a command */ -#define HITRETURN (0x200 + NORMAL) /* waiting for return or command */ -#define ASKMORE 0x300 /* Asking if you want --more-- */ -#define SETWSIZE 0x400 /* window size has changed */ -#define ABBREV 0x500 /* abbreviation instead of mapping */ -#define EXTERNCMD 0x600 /* executing an external command */ -#define SHOWMATCH (0x700 + INSERT) /* show matching paren */ -#define CONFIRM 0x800 /* ":confirm" prompt */ -#define SELECTMODE 0x1000 /* Select mode, only for mappings */ +#define NORMAL_BUSY (0x100 + NORMAL) // Normal mode, busy with a command +#define HITRETURN (0x200 + NORMAL) // waiting for return or command +#define ASKMORE 0x300 // Asking if you want --more-- +#define SETWSIZE 0x400 // window size has changed +#define ABBREV 0x500 // abbreviation instead of mapping +#define EXTERNCMD 0x600 // executing an external command +#define SHOWMATCH (0x700 + INSERT) // show matching paren +#define CONFIRM 0x800 // ":confirm" prompt +#define SELECTMODE 0x1000 // Select mode, only for mappings #define TERM_FOCUS 0x2000 // Terminal focus mode #define CMDPREVIEW 0x4000 // Showing 'inccommand' command "live" preview. @@ -94,13 +85,13 @@ typedef enum { BACKWARD_FILE = (-3), } Direction; -/* return values for functions */ +// return values for functions #if !(defined(OK) && (OK == 1)) -/* OK already defined to 1 in MacOS X curses, skip this */ +// OK already defined to 1 in MacOS X curses, skip this # define OK 1 #endif #define FAIL 0 -#define NOTDONE 2 /* not OK or FAIL but skipped */ +#define NOTDONE 2 // not OK or FAIL but skipped // Type values for type(). #define VAR_TYPE_NUMBER 0 @@ -111,9 +102,9 @@ typedef enum { #define VAR_TYPE_FLOAT 5 #define VAR_TYPE_BOOL 6 -/* - * values for xp_context when doing command line completion - */ + +// values for xp_context when doing command line completion + enum { EXPAND_UNSUCCESSFUL = -2, EXPAND_OK = -1, @@ -163,62 +154,61 @@ enum { EXPAND_SYNTIME, EXPAND_USER_ADDR_TYPE, EXPAND_PACKADD, + EXPAND_MESSAGES, + EXPAND_CHECKHEALTH, }; +// Minimal size for block 0 of a swap file. +// NOTE: This depends on size of struct block0! It's not done with a sizeof(), +// because struct block0 is defined in memline.c (Sorry). +// The maximal block size is arbitrary. -/* - * Minimal size for block 0 of a swap file. - * NOTE: This depends on size of struct block0! It's not done with a sizeof(), - * because struct block0 is defined in memline.c (Sorry). - * The maximal block size is arbitrary. - */ #define MIN_SWAP_PAGE_SIZE 1048 #define MAX_SWAP_PAGE_SIZE 50000 -/* - * Boolean constants - */ +// Boolean constants + #ifndef TRUE -# define FALSE 0 /* note: this is an int, not a long! */ +# define FALSE 0 // note: this is an int, not a long! # define TRUE 1 #endif -#define MAYBE 2 /* sometimes used for a variant on TRUE */ +#define MAYBE 2 // sometimes used for a variant on TRUE + +#define STATUS_HEIGHT 1 // height of a status line under a window +#define QF_WINHEIGHT 10 // default height for quickfix window -#define STATUS_HEIGHT 1 /* height of a status line under a window */ -#define QF_WINHEIGHT 10 /* default height for quickfix window */ -/* - * Buffer sizes - */ +// Buffer sizes + #ifndef CMDBUFFSIZE -# define CMDBUFFSIZE 256 /* size of the command processing buffer */ +# define CMDBUFFSIZE 256 // size of the command processing buffer #endif -#define LSIZE 512 /* max. size of a line in the tags file */ +#define LSIZE 512 // max. size of a line in the tags file -#define DIALOG_MSG_SIZE 1000 /* buffer size for dialog_msg() */ +#define DIALOG_MSG_SIZE 1000 // buffer size for dialog_msg() enum { FOLD_TEXT_LEN = 51 }; //!< buffer size for get_foldtext() -/* - * Maximum length of key sequence to be mapped. - * Must be able to hold an Amiga resize report. - */ + +// Maximum length of key sequence to be mapped. +// Must be able to hold an Amiga resize report. + #define MAXMAPLEN 50 -/* Size in bytes of the hash used in the undo file. */ +// Size in bytes of the hash used in the undo file. #define UNDO_HASH_SIZE 32 -/* - * defines to avoid typecasts from (char_u *) to (char *) and back - * (vim_strchr() and vim_strrchr() are now in alloc.c) - */ + +// defines to avoid typecasts from (char_u *) to (char *) and back +// (vim_strchr() and vim_strrchr() are now in alloc.c) + #define STRLEN(s) strlen((char *)(s)) #define STRCPY(d, s) strcpy((char *)(d), (char *)(s)) #define STRNCPY(d, s, n) strncpy((char *)(d), (char *)(s), (size_t)(n)) @@ -235,7 +225,7 @@ enum { FOLD_TEXT_LEN = 51 }; //!< buffer size for get_foldtext() # endif #endif -/* Like strcpy() but allows overlapped source and destination. */ +// Like strcpy() but allows overlapped source and destination. #define STRMOVE(d, s) memmove((d), (s), STRLEN(s) + 1) #ifdef HAVE_STRNCASECMP @@ -260,8 +250,8 @@ enum { FOLD_TEXT_LEN = 51 }; //!< buffer size for get_foldtext() // destination and mess up the screen. #define PERROR(msg) (void) emsgf("%s: %s", msg, strerror(errno)) -#define SHOWCMD_COLS 10 /* columns needed by shown command */ -#define STL_MAX_ITEM 80 /* max nr of %<flag> in statusline */ +#define SHOWCMD_COLS 10 // columns needed by shown command +#define STL_MAX_ITEM 80 // max nr of %<flag> in statusline /// Compare file names /// @@ -280,28 +270,27 @@ enum { FOLD_TEXT_LEN = 51 }; //!< buffer size for get_foldtext() (const char *)(y), \ (size_t)(n)) -/* - * Enums need a typecast to be used as array index (for Ultrix). - */ + +// Enums need a typecast to be used as array index (for Ultrix). #define hl_attr(n) highlight_attr[(int)(n)] #define term_str(n) term_strings[(int)(n)] -/* Maximum number of bytes in a multi-byte character. It can be one 32-bit - * character of up to 6 bytes, or one 16-bit character of up to three bytes - * plus six following composing characters of three bytes each. */ +/// Maximum number of bytes in a multi-byte character. It can be one 32-bit +/// character of up to 6 bytes, or one 16-bit character of up to three bytes +/// plus six following composing characters of three bytes each. #define MB_MAXBYTES 21 -/* This has to go after the include of proto.h, as proto/gui.pro declares - * functions of these names. The declarations would break if the defines had - * been seen at that stage. But it must be before globals.h, where error_ga - * is declared. */ +// This has to go after the include of proto.h, as proto/gui.pro declares +// functions of these names. The declarations would break if the defines had +// been seen at that stage. But it must be before globals.h, where error_ga +// is declared. #define mch_errmsg(str) fprintf(stderr, "%s", (str)) #define display_errors() fflush(stderr) #define mch_msg(str) printf("%s", (str)) -#include "nvim/globals.h" /* global variables and messages */ -#include "nvim/buffer_defs.h" /* buffer and windows */ -#include "nvim/ex_cmds_defs.h" /* Ex command defines */ +#include "nvim/globals.h" // global variables and messages +#include "nvim/buffer_defs.h" // buffer and windows +#include "nvim/ex_cmds_defs.h" // Ex command defines # define SET_NO_HLSEARCH(flag) no_hlsearch = (flag); set_vim_var_nr( \ VV_HLSEARCH, !no_hlsearch && p_hls) @@ -319,4 +308,13 @@ enum { FOLD_TEXT_LEN = 51 }; //!< buffer size for get_foldtext() // Lowest number used for window ID. Cannot have this many windows per tab. #define LOWEST_WIN_ID 1000 -#endif /* NVIM_VIM_H */ +// BSD is supposed to cover FreeBSD and similar systems. +#if (defined(BSD) || defined(__FreeBSD_kernel__)) && defined(S_ISCHR) +# define OPEN_CHR_FILES +#endif + +// Replacement for nchar used by nv_replace(). +#define REPLACE_CR_NCHAR -1 +#define REPLACE_NL_NCHAR -2 + +#endif // NVIM_VIM_H diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c new file mode 100644 index 0000000000..4196ecb9d2 --- /dev/null +++ b/src/nvim/viml/parser/expressions.c @@ -0,0 +1,3102 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +/// VimL expression parser + +// Planned incompatibilities (to be included into vim_diff.txt when this parser +// will be an actual part of VimL evaluation process): +// +// 1. Expressions are first fully parsed and only then executed. This means +// that while ":echo [system('touch abc')" will create file "abc" in Vim and +// only then raise syntax error regarding missing comma in list in Neovim +// trying to execute that will immediately raise syntax error regarding +// missing list end without actually executing anything. +// 2. Expressions are first fully parsed, without considering any runtime +// information. This means things like that "d.a" does not change its +// meaning depending on type of "d" (or whether Vim is currently executing or +// skipping). For compatibility reasons the dot thus may either be “concat +// or subscript” operator or just “concat” operator. +// 3. Expressions parser is aware whether it is called for :echo or <C-r>=. +// This means that while "<C-r>=1 | 2<CR>" is equivalent to "<C-r>=1<CR>" +// because "| 2" part is left to be treated as a command separator and then +// ignored in Neovim it is an error. +// 4. Expressions parser has generally better error reporting. But for +// compatibility reasons most errors have error code E15 while error messages +// are significantly different from Vim’s E15. Also some error codes were +// retired because of being harder to emulate or because of them being +// a result of differences in parsing process: e.g. with ":echo {a, b}" Vim +// will attempt to parse expression as lambda, fail, check whether it is +// a curly-braces-name, fail again, and evaluate that as a dictionary, giving +// error regarding undefined variable "a" (or about missing colon). Neovim +// will not try to evaluate anything here: comma right after an argument name +// means that expression may not be anything, but lambda, so the resulting +// error message will never be about missing variable or colon: it will be +// about missing arrow (or a continuation of argument list). +// 5. Failing to parse expression always gives exactly one error message: no +// more stack of error messages like > +// +// :echo [1, +// E697: Missing end of List ']': +// E15: Invalid expression: [1, +// +// < , just exactly one E697 message. +// 6. Some expressions involving calling parenthesis which are treated +// separately by Vim even when not separated by spaces are treated as one +// expression by Neovim: e.g. ":echo (1)(1)" will yield runtime error after +// failing to call "1", while Vim will echo "1 1". Reasoning is the same: +// type of what is in the first expression is generally not known when +// parsing, so to have separate expressions like this separate them with +// spaces. +// 7. 'isident' no longer applies to environment variables, they always include +// ASCII alphanumeric characters and underscore and nothing except this. + +#include <stdbool.h> +#include <stddef.h> +#include <assert.h> +#include <string.h> + +#include "nvim/vim.h" +#include "nvim/memory.h" +#include "nvim/types.h" +#include "nvim/charset.h" +#include "nvim/ascii.h" +#include "nvim/assert.h" +#include "nvim/lib/kvec.h" +#include "nvim/eval/typval.h" + +#include "nvim/viml/parser/expressions.h" +#include "nvim/viml/parser/parser.h" + +#define vim_str2nr(s, ...) vim_str2nr((const char_u *)(s), __VA_ARGS__) + +typedef kvec_withinit_t(ExprASTNode **, 16) ExprASTStack; + +/// Which nodes may be wanted +typedef enum { + /// Operators: function call, subscripts, binary operators, … + /// + /// For unrestricted expressions. + kENodeOperator, + /// Values: literals, variables, nested expressions, unary operators. + /// + /// For unrestricted expressions as well, implies that top item in AST stack + /// points to NULL. + kENodeValue, +} ExprASTWantedNode; + +/// Parse type: what is being parsed currently +typedef enum { + /// Parsing regular VimL expression + kEPTExpr = 0, + /// Parsing lambda arguments + /// + /// Just like parsing function arguments, but it is valid to be ended with an + /// arrow only. + kEPTLambdaArguments, + /// Assignment: parsing for :let + kEPTAssignment, + /// Single assignment: used when lists are not allowed (i.e. when nesting) + kEPTSingleAssignment, +} ExprASTParseType; + +typedef kvec_withinit_t(ExprASTParseType, 4) ExprASTParseTypeStack; + +/// Operator priority level +typedef enum { + kEOpLvlInvalid = 0, + kEOpLvlComplexIdentifier, + kEOpLvlParens, + kEOpLvlAssignment, + kEOpLvlArrow, + kEOpLvlComma, + kEOpLvlColon, + kEOpLvlTernaryValue, + kEOpLvlTernary, + kEOpLvlOr, + kEOpLvlAnd, + kEOpLvlComparison, + kEOpLvlAddition, ///< Addition, subtraction and concatenation. + kEOpLvlMultiplication, ///< Multiplication, division and modulo. + kEOpLvlUnary, ///< Unary operations: not, minus, plus. + kEOpLvlSubscript, ///< Subscripts. + kEOpLvlValue, ///< Values: literals, variables, nested expressions, … +} ExprOpLvl; + +/// Operator associativity +typedef enum { + kEOpAssNo= 'n', ///< Not associative / not applicable. + kEOpAssLeft = 'l', ///< Left associativity. + kEOpAssRight = 'r', ///< Right associativity. +} ExprOpAssociativity; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "viml/parser/expressions.c.generated.h" +#endif + +/// Character used as a separator in autoload function/variable names. +#define AUTOLOAD_CHAR '#' + +/// Scale number by a given factor +/// +/// Used to apply exponent to a number. Idea taken from uClibc. +/// +/// @param[in] num Number to scale. Does not bother doing anything if it is +/// zero. +/// @param[in] base Base, should be 10 since non-decimal floating-point +/// numbers are not supported. +/// @param[in] exponent Exponent to scale by. +/// @param[in] exponent_negative True if exponent is negative. +static inline float_T scale_number(const float_T num, + const uint8_t base, + const uvarnumber_T exponent, + const bool exponent_negative) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_CONST +{ + if (num == 0 || exponent == 0) { + return num; + } + assert(base); + uvarnumber_T exp = exponent; + float_T p_base = (float_T)base; + float_T ret = num; + while (exp) { + if (exp & 1) { + if (exponent_negative) { + ret /= p_base; + } else { + ret *= p_base; + } + } + exp >>= 1; + p_base *= p_base; + } + return ret; +} + +/// Get next token for the VimL expression input +/// +/// @param pstate Parser state. +/// @param[in] flags Flags, @see LexExprFlags. +/// +/// @return Next token. +LexExprToken viml_pexpr_next_token(ParserState *const pstate, const int flags) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + LexExprToken ret = { + .type = kExprLexInvalid, + .start = pstate->pos, + }; + ParserLine pline; + if (!viml_parser_get_remaining_line(pstate, &pline)) { + ret.type = kExprLexEOC; + return ret; + } + if (pline.size <= 0) { + ret.len = 0; + ret.type = kExprLexEOC; + goto viml_pexpr_next_token_adv_return; + } + ret.len = 1; + const uint8_t schar = (uint8_t)pline.data[0]; +#define GET_CCS(ret, pline) \ + do { \ + if (ret.len < pline.size \ + && strchr("?#", pline.data[ret.len]) != NULL) { \ + ret.data.cmp.ccs = \ + (ExprCaseCompareStrategy)pline.data[ret.len]; \ + ret.len++; \ + } else { \ + ret.data.cmp.ccs = kCCStrategyUseOption; \ + } \ + } while (0) + switch (schar) { + // Paired brackets. +#define BRACKET(typ, opning, clsing) \ + case opning: \ + case clsing: { \ + ret.type = typ; \ + ret.data.brc.closing = (schar == clsing); \ + break; \ + } + BRACKET(kExprLexParenthesis, '(', ')') + BRACKET(kExprLexBracket, '[', ']') + BRACKET(kExprLexFigureBrace, '{', '}') +#undef BRACKET + + // Single character tokens without data. +#define CHAR(typ, ch) \ + case ch: { \ + ret.type = typ; \ + break; \ + } + CHAR(kExprLexQuestion, '?') + CHAR(kExprLexColon, ':') + CHAR(kExprLexComma, ',') +#undef CHAR + + // Multiplication/division/modulo. +#define MUL(mul_type, ch) \ + case ch: { \ + ret.type = kExprLexMultiplication; \ + ret.data.mul.type = mul_type; \ + break; \ + } + MUL(kExprLexMulMul, '*') + MUL(kExprLexMulDiv, '/') + MUL(kExprLexMulMod, '%') +#undef MUL + +#define CHARREG(typ, cond) \ + do { \ + ret.type = typ; \ + for (; (ret.len < pline.size \ + && cond(pline.data[ret.len])) \ + ; ret.len++) { \ + } \ + } while (0) + + // Whitespace. + case ' ': + case TAB: { + CHARREG(kExprLexSpacing, ascii_iswhite); + break; + } + + // Control character, except for NUL, NL and TAB. + case Ctrl_A: case Ctrl_B: case Ctrl_C: case Ctrl_D: case Ctrl_E: + case Ctrl_F: case Ctrl_G: case Ctrl_H: + + case Ctrl_K: case Ctrl_L: case Ctrl_M: case Ctrl_N: case Ctrl_O: + case Ctrl_P: case Ctrl_Q: case Ctrl_R: case Ctrl_S: case Ctrl_T: + case Ctrl_U: case Ctrl_V: case Ctrl_W: case Ctrl_X: case Ctrl_Y: + case Ctrl_Z: { +#define ISCTRL(schar) (schar < ' ') + CHARREG(kExprLexInvalid, ISCTRL); + ret.data.err.type = kExprLexSpacing; + ret.data.err.msg = + _("E15: Invalid control character present in input: %.*s"); + break; +#undef ISCTRL + } + + // Number. + case '0': case '1': case '2': case '3': case '4': case '5': case '6': + case '7': case '8': case '9': { + ret.data.num.is_float = false; + ret.data.num.base = 10; + size_t frac_start = 0; + size_t exp_start = 0; + size_t frac_end = 0; + bool exp_negative = false; + CHARREG(kExprLexNumber, ascii_isdigit); + if (flags & kELFlagAllowFloat) { + const LexExprToken non_float_ret = ret; + if (pline.size > ret.len + 1 + && pline.data[ret.len] == '.' + && ascii_isdigit(pline.data[ret.len + 1])) { + ret.len++; + frac_start = ret.len; + frac_end = ret.len; + ret.data.num.is_float = true; + for (; ret.len < pline.size && ascii_isdigit(pline.data[ret.len]) + ; ret.len++) { + // A small optimization: trailing zeroes in fractional part do not + // add anything to significand, so it is useless to include them in + // frac_end. + if (pline.data[ret.len] != '0') { + frac_end = ret.len + 1; + } + } + if (pline.size > ret.len + 1 + && (pline.data[ret.len] == 'e' + || pline.data[ret.len] == 'E') + && ((pline.size > ret.len + 2 + && (pline.data[ret.len + 1] == '+' + || pline.data[ret.len + 1] == '-') + && ascii_isdigit(pline.data[ret.len + 2])) + || ascii_isdigit(pline.data[ret.len + 1]))) { + ret.len++; + if (pline.data[ret.len] == '+' + || (exp_negative = (pline.data[ret.len] == '-'))) { + ret.len++; + } + exp_start = ret.len; + CHARREG(kExprLexNumber, ascii_isdigit); + } + } + if (pline.size > ret.len + && (pline.data[ret.len] == '.' + || ASCII_ISALPHA(pline.data[ret.len]))) { + ret = non_float_ret; + } + } + // TODO(ZyX-I): detect overflows + if (ret.data.num.is_float) { + // Vim used to use string2float here which in turn uses strtod(). There + // are two problems with this approach: + // 1. strtod() is locale-dependent. Not sure how it is worked around so + // that I do not see relevant bugs, but it still does not look like + // a good idea. + // 2. strtod() does not accept length argument. + // + // The below variant of parsing floats was recognized as acceptable + // because it is basically how uClibc does the thing: it generates + // a number ignoring decimal point (but recording its position), then + // uses recorded position to scale number down when processing exponent. + float_T significand_part = 0; + uvarnumber_T exp_part = 0; + const size_t frac_size = (size_t)(frac_end - frac_start); + for (size_t i = 0; i < frac_end; i++) { + if (i == frac_start - 1) { + continue; + } + significand_part = significand_part * 10 + (pline.data[i] - '0'); + } + if (exp_start) { + vim_str2nr(pline.data + exp_start, NULL, NULL, 0, NULL, &exp_part, + (int)(ret.len - exp_start)); + } + if (exp_negative) { + exp_part += frac_size; + } else { + if (exp_part < frac_size) { + exp_negative = true; + exp_part = frac_size - exp_part; + } else { + exp_part -= frac_size; + } + } + ret.data.num.val.floating = scale_number(significand_part, 10, exp_part, + exp_negative); + } else { + int len; + int prep; + vim_str2nr(pline.data, &prep, &len, STR2NR_ALL, NULL, + &ret.data.num.val.integer, (int)pline.size); + ret.len = (size_t)len; + const uint8_t bases[] = { + [0] = 10, + ['0'] = 8, + ['x'] = 16, ['X'] = 16, + ['b'] = 2, ['B'] = 2, + }; + ret.data.num.base = bases[prep]; + } + break; + } + +#define ISWORD_OR_AUTOLOAD(x) \ + (ascii_isident(x) || (x) == AUTOLOAD_CHAR) + + // Environment variable. + case '$': { + CHARREG(kExprLexEnv, ascii_isident); + break; + } + + // Normal variable/function name. + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': + case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': + case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': + case 'v': case 'w': case 'x': case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': + case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': + case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': + case 'V': case 'W': case 'X': case 'Y': case 'Z': + case '_': { + ret.data.var.scope = 0; + ret.data.var.autoload = false; + CHARREG(kExprLexPlainIdentifier, ascii_isident); + // "is" and "isnot" operators. + if (!(flags & kELFlagIsNotCmp) + && ((ret.len == 2 && memcmp(pline.data, "is", 2) == 0) + || (ret.len == 5 && memcmp(pline.data, "isnot", 5) == 0))) { + ret.type = kExprLexComparison; + ret.data.cmp.type = kExprCmpIdentical; + ret.data.cmp.inv = (ret.len == 5); + GET_CCS(ret, pline); + // Scope: `s:`, etc. + } else if (ret.len == 1 + && pline.size > 1 + && memchr(EXPR_VAR_SCOPE_LIST, schar, + sizeof(EXPR_VAR_SCOPE_LIST)) != NULL + && pline.data[ret.len] == ':' + && !(flags & kELFlagForbidScope)) { + ret.len++; + ret.data.var.scope = (ExprVarScope)schar; + CHARREG(kExprLexPlainIdentifier, ISWORD_OR_AUTOLOAD); + ret.data.var.autoload = ( + memchr(pline.data + 2, AUTOLOAD_CHAR, ret.len - 2) + != NULL); + // Previous CHARREG stopped at autoload character in order to make it + // possible to detect `is#`. Continue now with autoload characters + // included. + // + // Warning: there is ambiguity for the lexer: `is#Foo(1)` is a call of + // function `is#Foo()`, `1is#Foo(1)` is a comparison `1 is# Foo(1)`. This + // needs to be resolved on the higher level where context is available. + } else if (pline.size > ret.len + && pline.data[ret.len] == AUTOLOAD_CHAR) { + ret.data.var.autoload = true; + CHARREG(kExprLexPlainIdentifier, ISWORD_OR_AUTOLOAD); + } + break; + } + +#undef ISWORD_OR_AUTOLOAD +#undef CHARREG + + // Option. + case '&': { +#define OPTNAMEMISS(ret) \ + do { \ + ret.type = kExprLexInvalid; \ + ret.data.err.type = kExprLexOption; \ + ret.data.err.msg = _("E112: Option name missing: %.*s"); \ + } while (0) + if (pline.size > 1 && pline.data[1] == '&') { + ret.type = kExprLexAnd; + ret.len++; + break; + } + if (pline.size == 1 || !ASCII_ISALPHA(pline.data[1])) { + OPTNAMEMISS(ret); + break; + } + ret.type = kExprLexOption; + if (pline.size > 2 + && pline.data[2] == ':' + && memchr(EXPR_OPT_SCOPE_LIST, pline.data[1], + sizeof(EXPR_OPT_SCOPE_LIST)) != NULL) { + ret.len += 2; + ret.data.opt.scope = (ExprOptScope)pline.data[1]; + ret.data.opt.name = pline.data + 3; + } else { + ret.data.opt.scope = kExprOptScopeUnspecified; + ret.data.opt.name = pline.data + 1; + } + const char *p = ret.data.opt.name; + const char *const e = pline.data + pline.size; + if (e - p >= 4 && p[0] == 't' && p[1] == '_') { + ret.data.opt.len = 4; + ret.len += 4; + } else { + for (; p < e && ASCII_ISALPHA(*p); p++) { + } + ret.data.opt.len = (size_t)(p - ret.data.opt.name); + if (ret.data.opt.len == 0) { + OPTNAMEMISS(ret); + } else { + ret.len += ret.data.opt.len; + } + } + break; +#undef OPTNAMEMISS + } + + // Register. + case '@': { + ret.type = kExprLexRegister; + if (pline.size > 1) { + ret.len++; + ret.data.reg.name = (uint8_t)pline.data[1]; + } else { + ret.data.reg.name = -1; + } + break; + } + + // Single quoted string. + case '\'': { + ret.type = kExprLexSingleQuotedString; + ret.data.str.closed = false; + for (; ret.len < pline.size && !ret.data.str.closed; ret.len++) { + if (pline.data[ret.len] == '\'') { + if (ret.len + 1 < pline.size && pline.data[ret.len + 1] == '\'') { + ret.len++; + } else { + ret.data.str.closed = true; + } + } + } + break; + } + + // Double quoted string. + case '"': { + ret.type = kExprLexDoubleQuotedString; + ret.data.str.closed = false; + for (; ret.len < pline.size && !ret.data.str.closed; ret.len++) { + if (pline.data[ret.len] == '\\') { + if (ret.len + 1 < pline.size) { + ret.len++; + } + } else if (pline.data[ret.len] == '"') { + ret.data.str.closed = true; + } + } + break; + } + + // Unary not, (un)equality and regex (not) match comparison operators. + case '!': + case '=': { + if (pline.size == 1) { + ret.type = (schar == '!' ? kExprLexNot : kExprLexAssignment); + ret.data.ass.type = kExprAsgnPlain; + break; + } + ret.type = kExprLexComparison; + ret.data.cmp.inv = (schar == '!'); + if (pline.data[1] == '=') { + ret.data.cmp.type = kExprCmpEqual; + ret.len++; + } else if (pline.data[1] == '~') { + ret.data.cmp.type = kExprCmpMatches; + ret.len++; + } else if (schar == '!') { + ret.type = kExprLexNot; + } else { + ret.type = kExprLexAssignment; + ret.data.ass.type = kExprAsgnPlain; + } + GET_CCS(ret, pline); + break; + } + + // Less/greater [or equal to] comparison operators. + case '>': + case '<': { + ret.type = kExprLexComparison; + const bool haseqsign = (pline.size > 1 && pline.data[1] == '='); + if (haseqsign) { + ret.len++; + } + GET_CCS(ret, pline); + ret.data.cmp.inv = (schar == '<'); + ret.data.cmp.type = ((ret.data.cmp.inv ^ haseqsign) + ? kExprCmpGreaterOrEqual + : kExprCmpGreater); + break; + } + + // Minus sign, arrow from lambdas or augmented assignment. + case '-': { + if (pline.size > 1 && pline.data[1] == '>') { + ret.len++; + ret.type = kExprLexArrow; + } else if (pline.size > 1 && pline.data[1] == '=') { + ret.len++; + ret.type = kExprLexAssignment; + ret.data.ass.type = kExprAsgnSubtract; + } else { + ret.type = kExprLexMinus; + } + break; + } + + // Sign or augmented assignment. +#define CHAR_OR_ASSIGN(ch, ch_type, ass_type) \ + case ch: { \ + if (pline.size > 1 && pline.data[1] == '=') { \ + ret.len++; \ + ret.type = kExprLexAssignment; \ + ret.data.ass.type = ass_type; \ + } else { \ + ret.type = ch_type; \ + } \ + break; \ + } + CHAR_OR_ASSIGN('+', kExprLexPlus, kExprAsgnAdd) + CHAR_OR_ASSIGN('.', kExprLexDot, kExprAsgnConcat) +#undef CHAR_OR_ASSIGN + + // Expression end because Ex command ended. + case NUL: + case NL: { + if (flags & kELFlagForbidEOC) { + ret.type = kExprLexInvalid; + ret.data.err.msg = _("E15: Unexpected EOC character: %.*s"); + ret.data.err.type = kExprLexSpacing; + } else { + ret.type = kExprLexEOC; + } + break; + } + + case '|': { + if (pline.size >= 2 && pline.data[ret.len] == '|') { + // "||" is or. + ret.len++; + ret.type = kExprLexOr; + } else if (flags & kELFlagForbidEOC) { + // Note: `<C-r>=1 | 2<CR>` actually yields 1 in Vim without any + // errors. This will be changed here. + ret.type = kExprLexInvalid; + ret.data.err.msg = _("E15: Unexpected EOC character: %.*s"); + ret.data.err.type = kExprLexOr; + } else { + ret.type = kExprLexEOC; + } + break; + } + + // Everything else is not valid. + default: { + ret.len = (size_t)utfc_ptr2len_len((const char_u *)pline.data, + (int)pline.size); + ret.type = kExprLexInvalid; + ret.data.err.type = kExprLexPlainIdentifier; + ret.data.err.msg = _("E15: Unidentified character: %.*s"); + break; + } + } +#undef GET_CCS +viml_pexpr_next_token_adv_return: + if (!(flags & kELFlagPeek)) { + viml_parser_advance(pstate, ret.len); + } + return ret; +} + +static const char *const eltkn_type_tab[] = { + [kExprLexInvalid] = "Invalid", + [kExprLexMissing] = "Missing", + [kExprLexSpacing] = "Spacing", + [kExprLexEOC] = "EOC", + + [kExprLexQuestion] = "Question", + [kExprLexColon] = "Colon", + [kExprLexOr] = "Or", + [kExprLexAnd] = "And", + [kExprLexComparison] = "Comparison", + [kExprLexPlus] = "Plus", + [kExprLexMinus] = "Minus", + [kExprLexDot] = "Dot", + [kExprLexMultiplication] = "Multiplication", + + [kExprLexNot] = "Not", + + [kExprLexNumber] = "Number", + [kExprLexSingleQuotedString] = "SingleQuotedString", + [kExprLexDoubleQuotedString] = "DoubleQuotedString", + [kExprLexOption] = "Option", + [kExprLexRegister] = "Register", + [kExprLexEnv] = "Env", + [kExprLexPlainIdentifier] = "PlainIdentifier", + + [kExprLexBracket] = "Bracket", + [kExprLexFigureBrace] = "FigureBrace", + [kExprLexParenthesis] = "Parenthesis", + [kExprLexComma] = "Comma", + [kExprLexArrow] = "Arrow", + [kExprLexAssignment] = "Assignment", +}; + +const char *const eltkn_cmp_type_tab[] = { + [kExprCmpEqual] = "Equal", + [kExprCmpMatches] = "Matches", + [kExprCmpGreater] = "Greater", + [kExprCmpGreaterOrEqual] = "GreaterOrEqual", + [kExprCmpIdentical] = "Identical", +}; + +const char *const expr_asgn_type_tab[] = { + [kExprAsgnPlain] = "Plain", + [kExprAsgnAdd] = "Add", + [kExprAsgnSubtract] = "Subtract", + [kExprAsgnConcat] = "Concat", +}; + +const char *const ccs_tab[] = { + [kCCStrategyUseOption] = "UseOption", + [kCCStrategyMatchCase] = "MatchCase", + [kCCStrategyIgnoreCase] = "IgnoreCase", +}; + +static const char *const eltkn_mul_type_tab[] = { + [kExprLexMulMul] = "Mul", + [kExprLexMulDiv] = "Div", + [kExprLexMulMod] = "Mod", +}; + +static const char *const eltkn_opt_scope_tab[] = { + [kExprOptScopeUnspecified] = "Unspecified", + [kExprOptScopeGlobal] = "Global", + [kExprOptScopeLocal] = "Local", +}; + +/// Represent token as a string +/// +/// Intended for testing and debugging purposes. +/// +/// @param[in] pstate Parser state, needed to get token string from it. May be +/// NULL, in which case in place of obtaining part of the +/// string represented by token only token length is +/// returned. +/// @param[in] token Token to represent. +/// @param[out] ret_size Return string size, for cases like NULs inside +/// a string. May be NULL. +/// +/// @return Token represented in a string form, in a static buffer (overwritten +/// on each call). +const char *viml_pexpr_repr_token(const ParserState *const pstate, + const LexExprToken token, + size_t *const ret_size) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + static char ret[1024]; + char *p = ret; + const char *const e = &ret[1024] - 1; +#define ADDSTR(...) \ + do { \ + p += snprintf(p, (size_t)(sizeof(ret) - (size_t)(p - ret)), __VA_ARGS__); \ + if (p >= e) { \ + goto viml_pexpr_repr_token_end; \ + } \ + } while (0) + ADDSTR("%zu:%zu:%s", token.start.line, token.start.col, + eltkn_type_tab[token.type]); + switch (token.type) { +#define TKNARGS(tkn_type, ...) \ + case tkn_type: { \ + ADDSTR(__VA_ARGS__); \ + break; \ + } + TKNARGS(kExprLexComparison, "(type=%s,ccs=%s,inv=%i)", + eltkn_cmp_type_tab[token.data.cmp.type], + ccs_tab[token.data.cmp.ccs], + (int)token.data.cmp.inv) + TKNARGS(kExprLexMultiplication, "(type=%s)", + eltkn_mul_type_tab[token.data.mul.type]) + TKNARGS(kExprLexAssignment, "(type=%s)", + expr_asgn_type_tab[token.data.ass.type]) + TKNARGS(kExprLexRegister, "(name=%s)", intchar2str(token.data.reg.name)) + case kExprLexDoubleQuotedString: + TKNARGS(kExprLexSingleQuotedString, "(closed=%i)", + (int)token.data.str.closed) + TKNARGS(kExprLexOption, "(scope=%s,name=%.*s)", + eltkn_opt_scope_tab[token.data.opt.scope], + (int)token.data.opt.len, token.data.opt.name) + TKNARGS(kExprLexPlainIdentifier, "(scope=%s,autoload=%i)", + intchar2str(token.data.var.scope), (int)token.data.var.autoload) + TKNARGS(kExprLexNumber, "(is_float=%i,base=%i,val=%lg)", + (int)token.data.num.is_float, + (int)token.data.num.base, + (double)(token.data.num.is_float + ? (double)token.data.num.val.floating + : (double)token.data.num.val.integer)) + TKNARGS(kExprLexInvalid, "(msg=%s)", token.data.err.msg) + default: { + // No additional arguments. + break; + } +#undef TKNARGS + } + if (pstate == NULL) { + ADDSTR("::%zu", token.len); + } else { + *p++ = ':'; + memmove( + p, &pstate->reader.lines.items[token.start.line].data[token.start.col], + token.len); + p += token.len; + *p = NUL; + } +#undef ADDSTR +viml_pexpr_repr_token_end: + if (ret_size != NULL) { + *ret_size = (size_t)(p - ret); + } + return ret; +} + +const char *const east_node_type_tab[] = { + [kExprNodeMissing] = "Missing", + [kExprNodeOpMissing] = "OpMissing", + [kExprNodeTernary] = "Ternary", + [kExprNodeTernaryValue] = "TernaryValue", + [kExprNodeRegister] = "Register", + [kExprNodeSubscript] = "Subscript", + [kExprNodeListLiteral] = "ListLiteral", + [kExprNodeUnaryPlus] = "UnaryPlus", + [kExprNodeBinaryPlus] = "BinaryPlus", + [kExprNodeNested] = "Nested", + [kExprNodeCall] = "Call", + [kExprNodePlainIdentifier] = "PlainIdentifier", + [kExprNodePlainKey] = "PlainKey", + [kExprNodeComplexIdentifier] = "ComplexIdentifier", + [kExprNodeUnknownFigure] = "UnknownFigure", + [kExprNodeLambda] = "Lambda", + [kExprNodeDictLiteral] = "DictLiteral", + [kExprNodeCurlyBracesIdentifier] = "CurlyBracesIdentifier", + [kExprNodeComma] = "Comma", + [kExprNodeColon] = "Colon", + [kExprNodeArrow] = "Arrow", + [kExprNodeComparison] = "Comparison", + [kExprNodeConcat] = "Concat", + [kExprNodeConcatOrSubscript] = "ConcatOrSubscript", + [kExprNodeInteger] = "Integer", + [kExprNodeFloat] = "Float", + [kExprNodeSingleQuotedString] = "SingleQuotedString", + [kExprNodeDoubleQuotedString] = "DoubleQuotedString", + [kExprNodeOr] = "Or", + [kExprNodeAnd] = "And", + [kExprNodeUnaryMinus] = "UnaryMinus", + [kExprNodeBinaryMinus] = "BinaryMinus", + [kExprNodeNot] = "Not", + [kExprNodeMultiplication] = "Multiplication", + [kExprNodeDivision] = "Division", + [kExprNodeMod] = "Mod", + [kExprNodeOption] = "Option", + [kExprNodeEnvironment] = "Environment", + [kExprNodeAssignment] = "Assignment", +}; + +/// Represent `int` character as a string +/// +/// Converts +/// - ASCII digits into '{digit}' +/// - ASCII printable characters into a single-character strings +/// - everything else to numbers. +/// +/// @param[in] ch Character to convert. +/// +/// @return Converted string, stored in a static buffer (overriden after each +/// call). +static const char *intchar2str(const int ch) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + static char buf[sizeof(int) * 3 + 1]; + if (' ' <= ch && ch < 0x7f) { + if (ascii_isdigit(ch)) { + buf[0] = '\''; + buf[1] = (char)ch; + buf[2] = '\''; + buf[3] = NUL; + } else { + buf[0] = (char)ch; + buf[1] = NUL; + } + } else { + snprintf(buf, sizeof(buf), "%i", ch); + } + return buf; +} + +#ifdef UNIT_TESTING +#include <stdio.h> + +REAL_FATTR_UNUSED +static inline void viml_pexpr_debug_print_ast_node( + const ExprASTNode *const *const eastnode_p, + const char *const prefix) +{ + if (*eastnode_p == NULL) { + fprintf(stderr, "%s %p : NULL\n", prefix, (void *)eastnode_p); + } else { + fprintf(stderr, "%s %p : %p : %s : %zu:%zu:%zu\n", + prefix, (void *)eastnode_p, (void *)(*eastnode_p), + east_node_type_tab[(*eastnode_p)->type], (*eastnode_p)->start.line, + (*eastnode_p)->start.col, (*eastnode_p)->len); + } +} + +REAL_FATTR_UNUSED +static inline void viml_pexpr_debug_print_ast_stack( + const ExprASTStack *const ast_stack, + const char *const msg) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE +{ + fprintf(stderr, "\n%sstack: %zu:\n", msg, kv_size(*ast_stack)); + for (size_t i = 0; i < kv_size(*ast_stack); i++) { + viml_pexpr_debug_print_ast_node( + (const ExprASTNode *const *)kv_A(*ast_stack, i), + "-"); + } +} + +REAL_FATTR_UNUSED +static inline void viml_pexpr_debug_print_token( + const ParserState *const pstate, const LexExprToken token) + FUNC_ATTR_ALWAYS_INLINE +{ + fprintf(stderr, "\ntkn: %s\n", viml_pexpr_repr_token(pstate, token, NULL)); +} +#define PSTACK(msg) \ + viml_pexpr_debug_print_ast_stack(&ast_stack, #msg) +#define PSTACK_P(msg) \ + viml_pexpr_debug_print_ast_stack(ast_stack, #msg) +#define PNODE_P(eastnode_p, msg) \ + viml_pexpr_debug_print_ast_node((const ExprASTNode *const *)eastnode_p, \ + (#msg)) +#define PTOKEN(tkn) \ + viml_pexpr_debug_print_token(pstate, tkn) +#endif + +const uint8_t node_maxchildren[] = { + [kExprNodeMissing] = 0, + [kExprNodeOpMissing] = 2, + [kExprNodeTernary] = 2, + [kExprNodeTernaryValue] = 2, + [kExprNodeRegister] = 0, + [kExprNodeSubscript] = 2, + [kExprNodeListLiteral] = 1, + [kExprNodeUnaryPlus] = 1, + [kExprNodeBinaryPlus] = 2, + [kExprNodeNested] = 1, + [kExprNodeCall] = 2, + [kExprNodePlainIdentifier] = 0, + [kExprNodePlainKey] = 0, + [kExprNodeComplexIdentifier] = 2, + [kExprNodeUnknownFigure] = 1, + [kExprNodeLambda] = 2, + [kExprNodeDictLiteral] = 1, + [kExprNodeCurlyBracesIdentifier] = 1, + [kExprNodeComma] = 2, + [kExprNodeColon] = 2, + [kExprNodeArrow] = 2, + [kExprNodeComparison] = 2, + [kExprNodeConcat] = 2, + [kExprNodeConcatOrSubscript] = 2, + [kExprNodeInteger] = 0, + [kExprNodeFloat] = 0, + [kExprNodeSingleQuotedString] = 0, + [kExprNodeDoubleQuotedString] = 0, + [kExprNodeOr] = 2, + [kExprNodeAnd] = 2, + [kExprNodeUnaryMinus] = 1, + [kExprNodeBinaryMinus] = 2, + [kExprNodeNot] = 1, + [kExprNodeMultiplication] = 2, + [kExprNodeDivision] = 2, + [kExprNodeMod] = 2, + [kExprNodeOption] = 0, + [kExprNodeEnvironment] = 0, + [kExprNodeAssignment] = 2, +}; + +/// Free memory occupied by AST +/// +/// @param ast AST stack to free. +void viml_pexpr_free_ast(ExprAST ast) +{ + ExprASTStack ast_stack; + kvi_init(ast_stack); + kvi_push(ast_stack, &ast.root); + while (kv_size(ast_stack)) { + ExprASTNode **const cur_node = kv_last(ast_stack); +#ifndef NDEBUG + // Explicitly check for AST recursiveness. + for (size_t i = 0 ; i < kv_size(ast_stack) - 1 ; i++) { + assert(*kv_A(ast_stack, i) != *cur_node); + } +#endif + if (*cur_node == NULL) { + assert(kv_size(ast_stack) == 1); + kv_drop(ast_stack, 1); + } else if ((*cur_node)->children != NULL) { +#ifndef NDEBUG + const uint8_t maxchildren = node_maxchildren[(*cur_node)->type]; + assert(maxchildren > 0); + assert(maxchildren <= 2); + assert(maxchildren == 1 + ? (*cur_node)->children->next == NULL + : ((*cur_node)->children->next == NULL + || (*cur_node)->children->next->next == NULL)); +#endif + kvi_push(ast_stack, &(*cur_node)->children); + } else if ((*cur_node)->next != NULL) { + kvi_push(ast_stack, &(*cur_node)->next); + } else if (*cur_node != NULL) { + kv_drop(ast_stack, 1); + switch ((*cur_node)->type) { + case kExprNodeDoubleQuotedString: + case kExprNodeSingleQuotedString: { + xfree((*cur_node)->data.str.value); + break; + } + case kExprNodeMissing: + case kExprNodeOpMissing: + case kExprNodeTernary: + case kExprNodeTernaryValue: + case kExprNodeRegister: + case kExprNodeSubscript: + case kExprNodeListLiteral: + case kExprNodeUnaryPlus: + case kExprNodeBinaryPlus: + case kExprNodeNested: + case kExprNodeCall: + case kExprNodePlainIdentifier: + case kExprNodePlainKey: + case kExprNodeComplexIdentifier: + case kExprNodeUnknownFigure: + case kExprNodeLambda: + case kExprNodeDictLiteral: + case kExprNodeCurlyBracesIdentifier: + case kExprNodeAssignment: + case kExprNodeComma: + case kExprNodeColon: + case kExprNodeArrow: + case kExprNodeComparison: + case kExprNodeConcat: + case kExprNodeConcatOrSubscript: + case kExprNodeInteger: + case kExprNodeFloat: + case kExprNodeOr: + case kExprNodeAnd: + case kExprNodeUnaryMinus: + case kExprNodeBinaryMinus: + case kExprNodeNot: + case kExprNodeMultiplication: + case kExprNodeDivision: + case kExprNodeMod: + case kExprNodeOption: + case kExprNodeEnvironment: { + break; + } + } + xfree(*cur_node); + *cur_node = NULL; + } + } + kvi_destroy(ast_stack); +} + +// Binary operator precedence and associativity: +// +// Operator | Precedence | Associativity +// ---------+------------+----------------- +// || | 2 | left +// && | 3 | left +// cmp* | 4 | not associative +// + - . | 5 | left +// * / % | 6 | left +// +// * comparison operators: +// +// == ==# ==? != !=# !=? +// =~ =~# =~? !~ !~# !~? +// > ># >? <= <=# <=? +// < <# <? >= >=# >=? +// is is# is? isnot isnot# isnot? + +/// Allocate a new node and set some of the values +/// +/// @param[in] type Node type to allocate. +/// @param[in] level Node level to allocate +static inline ExprASTNode *viml_pexpr_new_node(const ExprASTNodeType type) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC +{ + ExprASTNode *ret = xmalloc(sizeof(*ret)); + ret->type = type; + ret->children = NULL; + ret->next = NULL; + return ret; +} + +static struct { + ExprOpLvl lvl; + ExprOpAssociativity ass; +} node_type_to_node_props[] = { + [kExprNodeMissing] = { kEOpLvlInvalid, kEOpAssNo, }, + [kExprNodeOpMissing] = { kEOpLvlMultiplication, kEOpAssNo }, + + [kExprNodeNested] = { kEOpLvlParens, kEOpAssNo }, + // Note: below nodes are kEOpLvlSubscript for “binary operator” itself, but + // kEOpLvlParens when it comes to inside the parenthesis. + [kExprNodeCall] = { kEOpLvlParens, kEOpAssNo }, + [kExprNodeSubscript] = { kEOpLvlParens, kEOpAssNo }, + + [kExprNodeUnknownFigure] = { kEOpLvlParens, kEOpAssLeft }, + [kExprNodeLambda] = { kEOpLvlParens, kEOpAssNo }, + [kExprNodeDictLiteral] = { kEOpLvlParens, kEOpAssNo }, + [kExprNodeListLiteral] = { kEOpLvlParens, kEOpAssNo }, + + [kExprNodeArrow] = { kEOpLvlArrow, kEOpAssNo }, + + // Right associativity for comma because this means easier access to arguments + // list, etc: for "[a, b, c, d]" you can access "a" in one step if it is + // represented as "list(comma(a, comma(b, comma(c, d))))" then if it is + // "list(comma(comma(comma(a, b), c), d))" in which case you will need to + // traverse all three comma() structures. And with comma operator (including + // actual comma operator from C which is not present in VimL) nobody cares + // about associativity, only about order of execution. + [kExprNodeComma] = { kEOpLvlComma, kEOpAssRight }, + + // Colons are not eligible for chaining, so nobody cares about associativity. + [kExprNodeColon] = { kEOpLvlColon, kEOpAssNo }, + + [kExprNodeTernary] = { kEOpLvlTernary, kEOpAssRight }, + + [kExprNodeOr] = { kEOpLvlOr, kEOpAssLeft }, + + [kExprNodeAnd] = { kEOpLvlAnd, kEOpAssLeft }, + + [kExprNodeTernaryValue] = { kEOpLvlTernaryValue, kEOpAssRight }, + + [kExprNodeComparison] = { kEOpLvlComparison, kEOpAssRight }, + + [kExprNodeBinaryPlus] = { kEOpLvlAddition, kEOpAssLeft }, + [kExprNodeBinaryMinus] = { kEOpLvlAddition, kEOpAssLeft }, + [kExprNodeConcat] = { kEOpLvlAddition, kEOpAssLeft }, + + [kExprNodeMultiplication] = { kEOpLvlMultiplication, kEOpAssLeft }, + [kExprNodeDivision] = { kEOpLvlMultiplication, kEOpAssLeft }, + [kExprNodeMod] = { kEOpLvlMultiplication, kEOpAssLeft }, + + [kExprNodeUnaryPlus] = { kEOpLvlUnary, kEOpAssNo }, + [kExprNodeUnaryMinus] = { kEOpLvlUnary, kEOpAssNo }, + [kExprNodeNot] = { kEOpLvlUnary, kEOpAssNo }, + + [kExprNodeConcatOrSubscript] = { kEOpLvlSubscript, kEOpAssLeft }, + + [kExprNodeCurlyBracesIdentifier] = { kEOpLvlComplexIdentifier, kEOpAssLeft }, + + [kExprNodeAssignment] = { kEOpLvlAssignment, kEOpAssLeft }, + + [kExprNodeComplexIdentifier] = { kEOpLvlValue, kEOpAssLeft }, + + [kExprNodePlainIdentifier] = { kEOpLvlValue, kEOpAssNo }, + [kExprNodePlainKey] = { kEOpLvlValue, kEOpAssNo }, + [kExprNodeRegister] = { kEOpLvlValue, kEOpAssNo }, + [kExprNodeInteger] = { kEOpLvlValue, kEOpAssNo }, + [kExprNodeFloat] = { kEOpLvlValue, kEOpAssNo }, + [kExprNodeDoubleQuotedString] = { kEOpLvlValue, kEOpAssNo }, + [kExprNodeSingleQuotedString] = { kEOpLvlValue, kEOpAssNo }, + [kExprNodeOption] = { kEOpLvlValue, kEOpAssNo }, + [kExprNodeEnvironment] = { kEOpLvlValue, kEOpAssNo }, +}; + +/// Get AST node priority level +/// +/// Used primary to reduce line length, so keep the name short. +/// +/// @param[in] node Node to get priority for. +/// +/// @return Node priority level. +static inline ExprOpLvl node_lvl(const ExprASTNode node) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT +{ + return node_type_to_node_props[node.type].lvl; +} + +/// Get AST node associativity, to be used for operator nodes primary +/// +/// Used primary to reduce line length, so keep the name short. +/// +/// @param[in] node Node to get priority for. +/// +/// @return Node associativity. +static inline ExprOpAssociativity node_ass(const ExprASTNode node) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT +{ + return node_type_to_node_props[node.type].ass; +} + +/// Handle binary operator +/// +/// This function is responsible for handling priority levels as well. +/// +/// @param[in] pstate Parser state, used for error reporting. +/// @param ast_stack AST stack. May be popped of some values and will +/// definitely receive new ones. +/// @param bop_node New node to handle. +/// @param[out] want_node_p New value of want_node. +/// @param[out] ast_err Location where error is saved, if any. +/// +/// @return True if no errors occurred, false otherwise. +static bool viml_pexpr_handle_bop(const ParserState *const pstate, + ExprASTStack *const ast_stack, + ExprASTNode *const bop_node, + ExprASTWantedNode *const want_node_p, + ExprASTError *const ast_err) + FUNC_ATTR_NONNULL_ALL +{ + bool ret = true; + ExprASTNode **top_node_p = NULL; + ExprASTNode *top_node; + ExprOpLvl top_node_lvl; + ExprOpAssociativity top_node_ass; + assert(kv_size(*ast_stack)); + const ExprOpLvl bop_node_lvl = ((bop_node->type == kExprNodeCall + || bop_node->type == kExprNodeSubscript) + ? kEOpLvlSubscript + : node_lvl(*bop_node)); +#ifndef NDEBUG + const ExprOpAssociativity bop_node_ass = ( + (bop_node->type == kExprNodeCall + || bop_node->type == kExprNodeSubscript) + ? kEOpAssLeft + : node_ass(*bop_node)); +#endif + do { + ExprASTNode **new_top_node_p = kv_last(*ast_stack); + ExprASTNode *new_top_node = *new_top_node_p; + assert(new_top_node != NULL); + const ExprOpLvl new_top_node_lvl = node_lvl(*new_top_node); + const ExprOpAssociativity new_top_node_ass = node_ass(*new_top_node); + assert(bop_node_lvl != new_top_node_lvl + || bop_node_ass == new_top_node_ass); + if (top_node_p != NULL + && ((bop_node_lvl > new_top_node_lvl + || (bop_node_lvl == new_top_node_lvl + && new_top_node_ass == kEOpAssNo)))) { + break; + } + kv_drop(*ast_stack, 1); + top_node_p = new_top_node_p; + top_node = new_top_node; + top_node_lvl = new_top_node_lvl; + top_node_ass = new_top_node_ass; + if (bop_node_lvl == top_node_lvl && top_node_ass == kEOpAssRight) { + break; + } + } while (kv_size(*ast_stack)); + if (top_node_ass == kEOpAssLeft || top_node_lvl != bop_node_lvl) { + // outer(op(x,y)) -> outer(new_op(op(x,y),*)) + // + // Before: top_node_p = outer(*), points to op(x,y) + // Other stack elements unknown + // + // After: top_node_p = outer(*), points to new_op(op(x,y)) + // &bop_node->children->next = new_op(op(x,y),*), points to NULL + *top_node_p = bop_node; + bop_node->children = top_node; + assert(bop_node->children->next == NULL); + kvi_push(*ast_stack, top_node_p); + kvi_push(*ast_stack, &bop_node->children->next); + } else { + assert(top_node_lvl == bop_node_lvl && top_node_ass == kEOpAssRight); + assert(top_node->children != NULL && top_node->children->next != NULL); + // outer(op(x,y)) -> outer(op(x,new_op(y,*))) + // + // Before: top_node_p = outer(*), points to op(x,y) + // Other stack elements unknown + // + // After: top_node_p = outer(*), points to op(x,new_op(y)) + // &top_node->children->next = op(x,*), points to new_op(y) + // &bop_node->children->next = new_op(y,*), points to NULL + bop_node->children = top_node->children->next; + top_node->children->next = bop_node; + assert(bop_node->children->next == NULL); + kvi_push(*ast_stack, top_node_p); + kvi_push(*ast_stack, &top_node->children->next); + kvi_push(*ast_stack, &bop_node->children->next); + // TODO(ZyX-I): Make this not error, but treat like Python does + if (bop_node->type == kExprNodeComparison) { + east_set_error(pstate, ast_err, + _("E15: Operator is not associative: %.*s"), + bop_node->start); + ret = false; + } + } + *want_node_p = kENodeValue; + return ret; +} + +/// ParserPosition literal based on ParserPosition pos with columns shifted +/// +/// Function does not check whether resulting position is valid. +/// +/// @param[in] pos Position to shift. +/// @param[in] shift Number of bytes to shift. +/// +/// @return Shifted position. +static inline ParserPosition shifted_pos(const ParserPosition pos, + const size_t shift) + FUNC_ATTR_CONST FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT +{ + return (ParserPosition) { .line = pos.line, .col = pos.col + shift }; +} + +/// ParserPosition literal based on ParserPosition pos with specified column +/// +/// Function does not check whether remaining position is valid. +/// +/// @param[in] pos Position to adjust. +/// @param[in] new_col New column. +/// +/// @return Shifted position. +static inline ParserPosition recol_pos(const ParserPosition pos, + const size_t new_col) + FUNC_ATTR_CONST FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT +{ + return (ParserPosition) { .line = pos.line, .col = new_col }; +} + +/// Get highlight group name +#define HL(g) (is_invalid ? "NvimInvalid" #g : "Nvim" #g) + +/// Highlight current token with the given group +#define HL_CUR_TOKEN(g) \ + viml_parser_highlight(pstate, cur_token.start, cur_token.len, \ + HL(g)) + +/// Allocate new node, saving some values +#define NEW_NODE(type) \ + viml_pexpr_new_node(type) + +/// Set position of the given node to position from the given token +/// +/// @param cur_node Node to modify. +/// @param cur_token Token to set position from. +#define POS_FROM_TOKEN(cur_node, cur_token) \ + do { \ + (cur_node)->start = cur_token.start; \ + (cur_node)->len = cur_token.len; \ + } while (0) + +/// Allocate new node and set its position from the current token +/// +/// If previous token happened to contain spacing then it will be included. +/// +/// @param cur_node Variable to save allocated node to. +/// @param typ Node type. +#define NEW_NODE_WITH_CUR_POS(cur_node, typ) \ + do { \ + (cur_node) = NEW_NODE(typ); \ + POS_FROM_TOKEN((cur_node), cur_token); \ + if (prev_token.type == kExprLexSpacing) { \ + (cur_node)->start = prev_token.start; \ + (cur_node)->len += prev_token.len; \ + } \ + } while (0) + +/// Check whether it is possible to have next expression after current +/// +/// For :echo: `:echo @a @a` is a valid expression. `:echo (@a @a)` is not. +#define MAY_HAVE_NEXT_EXPR \ + (kv_size(ast_stack) == 1) + +/// Add operator node +/// +/// @param[in] cur_node Node to add. +#define ADD_OP_NODE(cur_node) \ + is_invalid |= !viml_pexpr_handle_bop(pstate, &ast_stack, cur_node, \ + &want_node, &ast.err) + +/// Record missing operator: for things like +/// +/// :echo @a @a +/// +/// (allowed) or +/// +/// :echo (@a @a) +/// +/// (parsed as OpMissing(@a, @a)). +#define OP_MISSING \ + do { \ + if (flags & kExprFlagsMulti && MAY_HAVE_NEXT_EXPR) { \ + /* Multiple expressions allowed, return without calling */ \ + /* viml_parser_advance(). */ \ + goto viml_pexpr_parse_end; \ + } else { \ + assert(*top_node_p != NULL); \ + ERROR_FROM_TOKEN_AND_MSG(cur_token, _("E15: Missing operator: %.*s")); \ + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeOpMissing); \ + cur_node->len = 0; \ + ADD_OP_NODE(cur_node); \ + goto viml_pexpr_parse_process_token; \ + } \ + } while (0) + +/// Record missing value: for things like "* 5" +/// +/// @param[in] msg Error message. +#define ADD_VALUE_IF_MISSING(msg) \ + do { \ + if (want_node == kENodeValue) { \ + ERROR_FROM_TOKEN_AND_MSG(cur_token, (msg)); \ + NEW_NODE_WITH_CUR_POS((*top_node_p), kExprNodeMissing); \ + (*top_node_p)->len = 0; \ + want_node = kENodeOperator; \ + } \ + } while (0) + +/// Set AST error, unless AST already is not correct +/// +/// @param[out] ret_ast AST to set error in. +/// @param[in] pstate Parser state, used to get error message argument. +/// @param[in] msg Error message, assumed to be already translated and +/// containing a single %token "%.*s". +/// @param[in] start Position at which error occurred. +static inline void east_set_error(const ParserState *const pstate, + ExprASTError *const ret_ast_err, + const char *const msg, + const ParserPosition start) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE +{ + if (ret_ast_err->msg != NULL) { + return; + } + const ParserLine pline = pstate->reader.lines.items[start.line]; + ret_ast_err->msg = msg; + ret_ast_err->arg_len = (int)(pline.size - start.col); + ret_ast_err->arg = pline.data + start.col; +} + +/// Set error from the given token and given message +#define ERROR_FROM_TOKEN_AND_MSG(cur_token, msg) \ + do { \ + is_invalid = true; \ + east_set_error(pstate, &ast.err, msg, cur_token.start); \ + } while (0) + +/// Like #ERROR_FROM_TOKEN_AND_MSG, but gets position from a node +#define ERROR_FROM_NODE_AND_MSG(node, msg) \ + do { \ + is_invalid = true; \ + east_set_error(pstate, &ast.err, msg, node->start); \ + } while (0) + +/// Set error from the given kExprLexInvalid token +#define ERROR_FROM_TOKEN(cur_token) \ + ERROR_FROM_TOKEN_AND_MSG(cur_token, cur_token.data.err.msg) + +/// Select figure brace type, altering highlighting as well if needed +/// +/// @param[out] node Node to modify type. +/// @param[in] new_type New type, one of ExprASTNodeType values without +/// kExprNode prefix. +/// @param[in] hl Corresponding highlighting, passed as an argument to #HL. +#define SELECT_FIGURE_BRACE_TYPE(node, new_type, hl) \ + do { \ + ExprASTNode *const node_ = (node); \ + assert(node_->type == kExprNodeUnknownFigure \ + || node_->type == kExprNode##new_type); \ + node_->type = kExprNode##new_type; \ + if (pstate->colors) { \ + kv_A(*pstate->colors, node_->data.fig.opening_hl_idx).group = \ + HL(hl); \ + } \ + } while (0) + +/// Add identifier which should constitute complex identifier node +/// +/// This one is to be called only in case want_node is kENodeOperator. +/// +/// @param new_ident_node_code Code used to create a new identifier node and +/// update want_node and ast_stack, without +/// a trailing semicolon. +/// @param hl Highlighting name to use, passed as an argument to #HL. +#define ADD_IDENT(new_ident_node_code, hl) \ + do { \ + assert(want_node == kENodeOperator); \ + /* Operator: may only be curly braces name, but only under certain */ \ + /* conditions. */ \ +\ + /* First condition is that there is no space before a part of complex */ \ + /* identifier. */ \ + if (prev_token.type == kExprLexSpacing) { \ + OP_MISSING; \ + } \ + switch ((*top_node_p)->type) { \ + /* Second is that previous node is one of the identifiers: */ \ + /* complex, plain, curly braces. */ \ +\ + /* TODO(ZyX-I): Extend syntax to allow ${expr}. This is needed to */ \ + /* handle environment variables like those bash uses for */ \ + /* `export -f`: their names consist not only of alphanumeric */ \ + /* characetrs. */ \ + case kExprNodeComplexIdentifier: \ + case kExprNodePlainIdentifier: \ + case kExprNodeCurlyBracesIdentifier: { \ + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeComplexIdentifier); \ + cur_node->len = 0; \ + cur_node->children = *top_node_p; \ + *top_node_p = cur_node; \ + kvi_push(ast_stack, &cur_node->children->next); \ + ExprASTNode **const new_top_node_p = kv_last(ast_stack); \ + assert(*new_top_node_p == NULL); \ + new_ident_node_code; \ + *new_top_node_p = cur_node; \ + HL_CUR_TOKEN(hl); \ + break; \ + } \ + default: { \ + OP_MISSING; \ + break; \ + } \ + } \ + } while (0) + +/// Determine whether given parse type is an assignment +/// +/// @param[in] pt Checked parse type. +/// +/// @return true if parsing an assignment, false otherwise. +static inline bool pt_is_assignment(const ExprASTParseType pt) + FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT +{ + return (pt == kEPTAssignment || pt == kEPTSingleAssignment); +} + +/// Structure used to define “string shifts” necessary to map string +/// highlighting to actual strings. +typedef struct { + size_t start; ///< Where special character starts in original string. + size_t orig_len; ///< Length of orininal string (e.g. 4 for "\x80"). + size_t act_len; ///< Length of resulting character(s) (e.g. 1 for "\x80"). + bool escape_not_known; ///< True if escape sequence in original is not known. +} StringShift; + +/// Parse and highlight single- or double-quoted string +/// +/// Function is supposed to detect and highlight regular expressions (but does +/// not do now). +/// +/// @param[out] pstate Parser state which also contains a place where +/// highlighting is saved. +/// @param[out] node Node where string parsing results are saved. +/// @param[in] token Token to highlight. +/// @param[in] ast_stack Parser AST stack, used to detect whether current +/// string is a regex. +/// @param[in] is_invalid Whether currently processed token is not valid. +static void parse_quoted_string(ParserState *const pstate, + ExprASTNode *const node, + const LexExprToken token, + const ExprASTStack ast_stack, + const bool is_invalid) + FUNC_ATTR_NONNULL_ALL +{ + const ParserLine pline = pstate->reader.lines.items[token.start.line]; + const char *const s = pline.data + token.start.col; + const char *const e = s + token.len - token.data.str.closed; + const char *p = s + 1; + const bool is_double = (token.type == kExprLexDoubleQuotedString); + size_t size = token.len - token.data.str.closed - 1; + kvec_withinit_t(StringShift, 16) shifts; + kvi_init(shifts); + if (!is_double) { + viml_parser_highlight(pstate, token.start, 1, HL(SingleQuote)); + while (p < e) { + const char *const chunk_e = memchr(p, '\'', (size_t)(e - p)); + if (chunk_e == NULL) { + break; + } + size--; + p = chunk_e + 2; + if (pstate->colors) { + kvi_push(shifts, ((StringShift) { + .start = token.start.col + (size_t)(chunk_e - s), + .orig_len = 2, + .act_len = 1, + .escape_not_known = false, + })); + } + } + node->data.str.size = size; + if (size == 0) { + node->data.str.value = NULL; + } else { + char *v_p; + v_p = node->data.str.value = xmallocz(size); + p = s + 1; + while (p < e) { + const char *const chunk_e = memchr(p, '\'', (size_t)(e - p)); + if (chunk_e == NULL) { + memcpy(v_p, p, (size_t)(e - p)); + break; + } + memcpy(v_p, p, (size_t)(chunk_e - p)); + v_p += (size_t)(chunk_e - p) + 1; + v_p[-1] = '\''; + p = chunk_e + 2; + } + } + } else { + viml_parser_highlight(pstate, token.start, 1, HL(DoubleQuote)); + for (p = s + 1; p < e; p++) { + if (*p == '\\' && p + 1 < e) { + p++; + if (p + 1 == e) { + size--; + break; + } + switch (*p) { + // A "\<x>" form occupies at least 4 characters, and produces up to + // 6 characters: reserve space for 2 extra, but do not compute actual + // length just now, it would be costy. + case '<': { + size += 2; + break; + } + // Hexadecimal, always single byte, but at least three bytes each. + case 'x': case 'X': { + size--; + if (ascii_isxdigit(p[1])) { + size--; + if (p + 2 < e && ascii_isxdigit(p[2])) { + size--; + } + } + break; + } + // Unicode + // + // \uF takes 1 byte which is 2 bytes less then escape sequence. + // \uFF: 2 bytes, 2 bytes less. + // \uFFF: 3 bytes, 2 bytes less. + // \uFFFF: 3 bytes, 3 bytes less. + // \UFFFFF: 4 bytes, 3 bytes less. + // \UFFFFFF: 5 bytes, 3 bytes less. + // \UFFFFFFF: 6 bytes, 3 bytes less. + // \U7FFFFFFF: 6 bytes, 4 bytes less. + case 'u': case 'U': { + const char *const esc_start = p; + size_t n = (*p == 'u' ? 4 : 8); + int nr = 0; + p++; + while (p + 1 < e && n-- && ascii_isxdigit(p[1])) { + p++; + nr = (nr << 4) + hex2nr(*p); + } + // Escape length: (esc_start - 1) points to "\\", esc_start to "u" + // or "U", p to the byte after last byte. So escape sequence + // occupies p - (esc_start - 1), but it stands for a utf_char2len + // bytes. + size -= (size_t)((p - (esc_start - 1)) - utf_char2len(nr)); + p--; + break; + } + // Octal, always single byte, but at least two bytes each. + case '0': case '1': case '2': case '3': case '4': case '5': case '6': + case '7': { + size--; + p++; + if (*p >= '0' && *p <= '7') { + size--; + p++; + if (p < e && *p >= '0' && *p <= '7') { + size--; + p++; + } + } + break; + } + default: { + size--; + break; + } + } + } + } + if (size == 0) { + node->data.str.value = NULL; + node->data.str.size = 0; + } else { + char *v_p; + v_p = node->data.str.value = xmalloc(size); + p = s + 1; + while (p < e) { + const char *const chunk_e = memchr(p, '\\', (size_t)(e - p)); + if (chunk_e == NULL) { + memcpy(v_p, p, (size_t)(e - p)); + v_p += e - p; + break; + } + memcpy(v_p, p, (size_t)(chunk_e - p)); + v_p += (size_t)(chunk_e - p); + p = chunk_e + 1; + if (p == e) { + *v_p++ = '\\'; + break; + } + bool is_unknown = false; + const char *const v_p_start = v_p; + switch (*p) { +#define SINGLE_CHAR_ESC(ch, real_ch) \ + case ch: { \ + *v_p++ = real_ch; \ + p++; \ + break; \ + } + SINGLE_CHAR_ESC('b', BS) + SINGLE_CHAR_ESC('e', ESC) + SINGLE_CHAR_ESC('f', FF) + SINGLE_CHAR_ESC('n', NL) + SINGLE_CHAR_ESC('r', CAR) + SINGLE_CHAR_ESC('t', TAB) + SINGLE_CHAR_ESC('"', '"') + SINGLE_CHAR_ESC('\\', '\\') +#undef SINGLE_CHAR_ESC + + // Hexadecimal or unicode. + case 'X': case 'x': case 'u': case 'U': { + if (p + 1 < e && ascii_isxdigit(p[1])) { + size_t n; + int nr; + bool is_hex = (*p == 'x' || *p == 'X'); + + if (is_hex) { + n = 2; + } else if (*p == 'u') { + n = 4; + } else { + n = 8; + } + nr = 0; + while (p + 1 < e && n-- && ascii_isxdigit(p[1])) { + p++; + nr = (nr << 4) + hex2nr(*p); + } + p++; + if (is_hex) { + *v_p++ = (char)nr; + } else { + v_p += utf_char2bytes(nr, (char_u *)v_p); + } + } else { + is_unknown = true; + *v_p++ = *p; + p++; + } + break; + } + // Octal: "\1", "\12", "\123". + case '0': case '1': case '2': case '3': case '4': case '5': case '6': + case '7': { + uint8_t ch = (uint8_t)(*p++ - '0'); + if (p < e && *p >= '0' && *p <= '7') { + ch = (uint8_t)((ch << 3) + *p++ - '0'); + if (p < e && *p >= '0' && *p <= '7') { + ch = (uint8_t)((ch << 3) + *p++ - '0'); + } + } + *v_p++ = (char)ch; + break; + } + // Special key, e.g.: "\<C-W>" + case '<': { + const size_t special_len = ( + trans_special((const char_u **)&p, (size_t)(e - p), + (char_u *)v_p, true, true)); + if (special_len != 0) { + v_p += special_len; + } else { + is_unknown = true; + mb_copy_char((const char_u **)&p, (char_u **)&v_p); + } + break; + } + default: { + is_unknown = true; + mb_copy_char((const char_u **)&p, (char_u **)&v_p); + break; + } + } + if (pstate->colors) { + kvi_push(shifts, ((StringShift) { + .start = token.start.col + (size_t)(chunk_e - s), + .orig_len = (size_t)(p - chunk_e), + .act_len = (size_t)(v_p - (char *)v_p_start), + .escape_not_known = is_unknown, + })); + } + } + node->data.str.size = (size_t)(v_p - node->data.str.value); + } + } + if (pstate->colors) { + // TODO(ZyX-I): use ast_stack to determine and highlight regular expressions + // TODO(ZyX-I): use ast_stack to determine and highlight printf format str + // TODO(ZyX-I): use ast_stack to determine and highlight expression strings + size_t next_col = token.start.col + 1; + const char *const body_str = (is_double + ? HL(DoubleQuotedBody) + : HL(SingleQuotedBody)); + const char *const esc_str = (is_double + ? HL(DoubleQuotedEscape) + : HL(SingleQuotedQuote)); + const char *const ukn_esc_str = (is_double + ? HL(DoubleQuotedUnknownEscape) + : HL(SingleQuotedUnknownEscape)); + for (size_t i = 0; i < kv_size(shifts); i++) { + const StringShift cur_shift = kv_A(shifts, i); + if (cur_shift.start > next_col) { + viml_parser_highlight(pstate, recol_pos(token.start, next_col), + cur_shift.start - next_col, + body_str); + } + viml_parser_highlight(pstate, recol_pos(token.start, cur_shift.start), + cur_shift.orig_len, + (cur_shift.escape_not_known + ? ukn_esc_str + : esc_str)); + next_col = cur_shift.start + cur_shift.orig_len; + } + if (next_col - token.start.col < token.len - token.data.str.closed) { + viml_parser_highlight(pstate, recol_pos(token.start, next_col), + (token.start.col + + token.len + - token.data.str.closed + - next_col), + body_str); + } + } + if (token.data.str.closed) { + if (is_double) { + viml_parser_highlight(pstate, shifted_pos(token.start, token.len - 1), + 1, HL(DoubleQuote)); + } else { + viml_parser_highlight(pstate, shifted_pos(token.start, token.len - 1), + 1, HL(SingleQuote)); + } + } + kvi_destroy(shifts); +} + +/// Additional flags to pass to lexer depending on want_node +static const int want_node_to_lexer_flags[] = { + [kENodeValue] = kELFlagIsNotCmp, + [kENodeOperator] = kELFlagForbidScope, +}; + +/// Number of characters to highlight as NumberPrefix depending on the base +static const uint8_t base_to_prefix_length[] = { + [2] = 2, + [8] = 1, + [10] = 0, + [16] = 2, +}; + +/// Parse one VimL expression +/// +/// @param pstate Parser state. +/// @param[in] flags Additional flags, see ExprParserFlags +/// +/// @return Parsed AST. +ExprAST viml_pexpr_parse(ParserState *const pstate, const int flags) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + ExprAST ast = { + .err = { + .msg = NULL, + .arg_len = 0, + .arg = NULL, + }, + .root = NULL, + }; + // Expression stack contains current branch in AST tree: that is + // - Stack item 0 contains root of the tree, i.e. &ast->root. + // - Stack item i points to the previous stack items’ last child. + // + // When parser expects “value” node that is something like identifier or "[" + // (list start) last stack item contains NULL. Otherwise last stack item is + // supposed to contain last “finished” value: e.g. "1" or "+(1, 1)" (node + // representing "1+1"). + ExprASTStack ast_stack; + kvi_init(ast_stack); + kvi_push(ast_stack, &ast.root); + ExprASTWantedNode want_node = kENodeValue; + ExprASTParseTypeStack pt_stack; + kvi_init(pt_stack); + kvi_push(pt_stack, kEPTExpr); + if (flags & kExprFlagsParseLet) { + kvi_push(pt_stack, kEPTAssignment); + } + LexExprToken prev_token = { .type = kExprLexMissing }; + bool highlighted_prev_spacing = false; + // Lambda node, valid when parsing lambda arguments only. + ExprASTNode *lambda_node = NULL; + size_t asgn_level = 0; + do { + const bool is_concat_or_subscript = ( + want_node == kENodeValue + && kv_size(ast_stack) > 1 + && (*kv_Z(ast_stack, 1))->type == kExprNodeConcatOrSubscript); + const int lexer_additional_flags = ( + kELFlagPeek + | ((flags & kExprFlagsDisallowEOC) ? kELFlagForbidEOC : 0) + | ((want_node == kENodeValue + && (kv_size(ast_stack) == 1 + || ((*kv_Z(ast_stack, 1))->type != kExprNodeConcat + && ((*kv_Z(ast_stack, 1))->type + != kExprNodeConcatOrSubscript)))) + ? kELFlagAllowFloat + : 0)); + LexExprToken cur_token = viml_pexpr_next_token( + pstate, want_node_to_lexer_flags[want_node] | lexer_additional_flags); + if (cur_token.type == kExprLexEOC) { + break; + } + LexExprTokenType tok_type = cur_token.type; + const bool token_invalid = (tok_type == kExprLexInvalid); + bool is_invalid = token_invalid; +viml_pexpr_parse_process_token: + // May use different flags this time. + cur_token = viml_pexpr_next_token( + pstate, want_node_to_lexer_flags[want_node] | lexer_additional_flags); + if (tok_type == kExprLexSpacing) { + if (is_invalid) { + HL_CUR_TOKEN(Spacing); + } else { + // Do not do anything: let regular spacing be highlighted as normal. + // This also allows later to highlight spacing as invalid. + } + goto viml_pexpr_parse_cycle_end; + } else if (is_invalid && prev_token.type == kExprLexSpacing + && !highlighted_prev_spacing) { + viml_parser_highlight(pstate, prev_token.start, prev_token.len, + HL(Spacing)); + is_invalid = false; + highlighted_prev_spacing = true; + } + const ParserLine pline = pstate->reader.lines.items[cur_token.start.line]; + ExprASTNode **const top_node_p = kv_last(ast_stack); + assert(kv_size(ast_stack) >= 1); + ExprASTNode *cur_node = NULL; +#ifndef NDEBUG + const bool want_value = (want_node == kENodeValue); + assert(want_value == (*top_node_p == NULL)); + assert(kv_A(ast_stack, 0) == &ast.root); + // Check that stack item i + 1 points to stack items’ i *last* child. + for (size_t i = 0; i + 1 < kv_size(ast_stack); i++) { + const bool item_null = (want_value && i + 2 == kv_size(ast_stack)); + assert((&(*kv_A(ast_stack, i))->children == kv_A(ast_stack, i + 1) + && (item_null + ? (*kv_A(ast_stack, i))->children == NULL + : (*kv_A(ast_stack, i))->children->next == NULL)) + || ((&(*kv_A(ast_stack, i))->children->next + == kv_A(ast_stack, i + 1)) + && (item_null + ? (*kv_A(ast_stack, i))->children->next == NULL + : (*kv_A(ast_stack, i))->children->next->next == NULL))); + } +#endif + // Note: in Vim whether expression "cond?d.a:2" is valid depends both on + // "cond" and whether "d" is a dictionary: expression is valid if condition + // is true and "d" is a dictionary (with "a" key or it will complain about + // missing one, but this is not relevant); if any of the requirements is + // broken then this thing is parsed as "d . a:2" yielding missing colon + // error. This parser does not allow such ambiguity, especially because it + // simply can’t: whether "d" is a dictionary is not known at the parsing + // time. + // + // Here example will always contain a concat with "a:2" sucking colon, + // making expression invalid both because there is no longer a spare colon + // for ternary and because concatenating dictionary with anything is not + // valid. There are more cases when this will make a difference though. + const bool node_is_key = ( + is_concat_or_subscript + && (cur_token.type == kExprLexPlainIdentifier + ? (!cur_token.data.var.autoload + && cur_token.data.var.scope == kExprVarScopeMissing) + : (cur_token.type == kExprLexNumber)) + && prev_token.type != kExprLexSpacing); + if (is_concat_or_subscript && !node_is_key) { + // Note: in Vim "d. a" (this is the reason behind `prev_token.type != + // kExprLexSpacing` part of the condition) as well as any other "d.{expr}" + // where "{expr}" does not look like a key is invalid whenever "d" happens + // to be a dictionary. Since parser has no idea whether preceding + // expression is actually a dictionary it can’t outright reject anything, + // so it turns kExprNodeConcatOrSubscript into kExprNodeConcat instead, + // which will yield different errors then Vim does in a number of + // circumstances, and in any case runtime and not parse time errors. + (*kv_Z(ast_stack, 1))->type = kExprNodeConcat; + } + // Pop some stack pt_stack items in case of misplaced nodes. + const bool is_single_assignment = kv_last(pt_stack) == kEPTSingleAssignment; + switch (kv_last(pt_stack)) { + case kEPTExpr: { + break; + } + case kEPTLambdaArguments: { + if ((want_node == kENodeOperator + && tok_type != kExprLexComma + && tok_type != kExprLexArrow) + || (want_node == kENodeValue + && !(cur_token.type == kExprLexPlainIdentifier + && cur_token.data.var.scope == kExprVarScopeMissing + && !cur_token.data.var.autoload) + && tok_type != kExprLexArrow)) { + lambda_node->data.fig.type_guesses.allow_lambda = false; + if (lambda_node->children != NULL + && lambda_node->children->type == kExprNodeComma) { + // If lambda has comma child this means that parser has already seen + // at least "{arg1,", so node cannot possibly be anything, but + // lambda. + + // Vim may give E121 or E720 in this case, but it does not look + // right to have either because both are results of reevaluation + // possibly-lambda node as a dictionary and here this is not going + // to happen. + ERROR_FROM_TOKEN_AND_MSG( + cur_token, + _("E15: Expected lambda arguments list or arrow: %.*s")); + } else { + // Else it may appear that possibly-lambda node is actually + // a dictionary or curly-braces-name identifier. + lambda_node = NULL; + kv_drop(pt_stack, 1); + } + } + break; + } + case kEPTSingleAssignment: + case kEPTAssignment: { + if (want_node == kENodeValue + && tok_type != kExprLexBracket + && tok_type != kExprLexPlainIdentifier + && (tok_type != kExprLexFigureBrace || cur_token.data.brc.closing) + && !(node_is_key && tok_type == kExprLexNumber) + && tok_type != kExprLexEnv + && tok_type != kExprLexOption + && tok_type != kExprLexRegister) { + ERROR_FROM_TOKEN_AND_MSG( + cur_token, + _("E15: Expected value part of assignment lvalue: %.*s")); + kv_drop(pt_stack, 1); + } else if (want_node == kENodeOperator + && tok_type != kExprLexBracket + && (tok_type != kExprLexFigureBrace + || cur_token.data.brc.closing) + && tok_type != kExprLexDot + && (tok_type != kExprLexComma || !is_single_assignment) + && tok_type != kExprLexAssignment + // Curly brace identifiers: will contain plain identifier or + // another curly brace in position where operator is wanted. + && !((tok_type == kExprLexPlainIdentifier + || (tok_type == kExprLexFigureBrace + && !cur_token.data.brc.closing)) + && prev_token.type != kExprLexSpacing)) { + if (flags & kExprFlagsMulti && MAY_HAVE_NEXT_EXPR) { + goto viml_pexpr_parse_end; + } + ERROR_FROM_TOKEN_AND_MSG( + cur_token, + _("E15: Expected assignment operator or subscript: %.*s")); + kv_drop(pt_stack, 1); + } + assert(kv_size(pt_stack)); + break; + } + } + assert(kv_size(pt_stack)); + const ExprASTParseType cur_pt = kv_last(pt_stack); + assert(lambda_node == NULL || cur_pt == kEPTLambdaArguments); + switch (tok_type) { + case kExprLexMissing: + case kExprLexSpacing: + case kExprLexEOC: { + assert(false); + } + case kExprLexInvalid: { + ERROR_FROM_TOKEN(cur_token); + tok_type = cur_token.data.err.type; + goto viml_pexpr_parse_process_token; + } + case kExprLexRegister: { + if (want_node == kENodeOperator) { + // Register in operator position: e.g. @a @a + OP_MISSING; + } + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeRegister); + cur_node->data.reg.name = cur_token.data.reg.name; + *top_node_p = cur_node; + want_node = kENodeOperator; + HL_CUR_TOKEN(Register); + break; + } +#define SIMPLE_UB_OP(op) \ + case kExprLex##op: { \ + if (want_node == kENodeValue) { \ + /* Value level: assume unary operator. */ \ + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeUnary##op); \ + *top_node_p = cur_node; \ + kvi_push(ast_stack, &cur_node->children); \ + HL_CUR_TOKEN(Unary##op); \ + } else { \ + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeBinary##op); \ + ADD_OP_NODE(cur_node); \ + HL_CUR_TOKEN(Binary##op); \ + } \ + want_node = kENodeValue; \ + break; \ + } + SIMPLE_UB_OP(Plus) + SIMPLE_UB_OP(Minus) +#undef SIMPLE_UB_OP +#define SIMPLE_B_OP(op, msg) \ + case kExprLex##op: { \ + ADD_VALUE_IF_MISSING(_("E15: Unexpected " msg ": %.*s")); \ + NEW_NODE_WITH_CUR_POS(cur_node, kExprNode##op); \ + HL_CUR_TOKEN(op); \ + ADD_OP_NODE(cur_node); \ + break; \ + } + SIMPLE_B_OP(Or, "or operator") + SIMPLE_B_OP(And, "and operator") +#undef SIMPLE_B_OP + case kExprLexMultiplication: { + ADD_VALUE_IF_MISSING( + _("E15: Unexpected multiplication-like operator: %.*s")); + switch (cur_token.data.mul.type) { +#define MUL_OP(lex_op_tail, node_op_tail) \ + case kExprLexMul##lex_op_tail: { \ + NEW_NODE_WITH_CUR_POS(cur_node, kExprNode##node_op_tail); \ + HL_CUR_TOKEN(node_op_tail); \ + break; \ + } + MUL_OP(Mul, Multiplication) + MUL_OP(Div, Division) + MUL_OP(Mod, Mod) +#undef MUL_OP + } + ADD_OP_NODE(cur_node); + break; + } + case kExprLexOption: { + if (want_node == kENodeOperator) { + OP_MISSING; + } + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeOption); + if (cur_token.type == kExprLexInvalid) { + assert(cur_token.len == 1 + || (cur_token.len == 3 + && pline.data[cur_token.start.col + 2] == ':')); + cur_node->data.opt.ident = ( + pline.data + cur_token.start.col + cur_token.len); + cur_node->data.opt.ident_len = 0; + cur_node->data.opt.scope = ( + cur_token.len == 3 + ? (ExprOptScope)pline.data[cur_token.start.col + 1] + : kExprOptScopeUnspecified); + } else { + cur_node->data.opt.ident = cur_token.data.opt.name; + cur_node->data.opt.ident_len = cur_token.data.opt.len; + cur_node->data.opt.scope = cur_token.data.opt.scope; + } + *top_node_p = cur_node; + want_node = kENodeOperator; + viml_parser_highlight(pstate, cur_token.start, 1, HL(OptionSigil)); + const size_t scope_shift = ( + cur_token.data.opt.scope == kExprOptScopeUnspecified ? 0 : 2); + if (scope_shift) { + viml_parser_highlight(pstate, shifted_pos(cur_token.start, 1), 1, + HL(OptionScope)); + viml_parser_highlight(pstate, shifted_pos(cur_token.start, 2), 1, + HL(OptionScopeDelimiter)); + } + viml_parser_highlight( + pstate, shifted_pos(cur_token.start, scope_shift + 1), + cur_token.len - (scope_shift + 1), HL(OptionName)); + break; + } + case kExprLexEnv: { + if (want_node == kENodeOperator) { + OP_MISSING; + } + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeEnvironment); + cur_node->data.env.ident = pline.data + cur_token.start.col + 1; + cur_node->data.env.ident_len = cur_token.len - 1; + if (cur_node->data.env.ident_len == 0) { + ERROR_FROM_TOKEN_AND_MSG(cur_token, + _("E15: Environment variable name missing")); + } + *top_node_p = cur_node; + want_node = kENodeOperator; + viml_parser_highlight(pstate, cur_token.start, 1, HL(EnvironmentSigil)); + viml_parser_highlight(pstate, shifted_pos(cur_token.start, 1), + cur_token.len - 1, HL(EnvironmentName)); + break; + } + case kExprLexNot: { + if (want_node == kENodeOperator) { + OP_MISSING; + } + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeNot); + *top_node_p = cur_node; + kvi_push(ast_stack, &cur_node->children); + HL_CUR_TOKEN(Not); + break; + } + case kExprLexComparison: { + ADD_VALUE_IF_MISSING( + _("E15: Expected value, got comparison operator: %.*s")); + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeComparison); + if (cur_token.type == kExprLexInvalid) { + cur_node->data.cmp.ccs = kCCStrategyUseOption; + cur_node->data.cmp.type = kExprCmpEqual; + cur_node->data.cmp.inv = false; + } else { + cur_node->data.cmp.ccs = cur_token.data.cmp.ccs; + cur_node->data.cmp.type = cur_token.data.cmp.type; + cur_node->data.cmp.inv = cur_token.data.cmp.inv; + } + ADD_OP_NODE(cur_node); + if (cur_token.data.cmp.ccs != kCCStrategyUseOption) { + viml_parser_highlight(pstate, cur_token.start, cur_token.len - 1, + HL(Comparison)); + viml_parser_highlight( + pstate, shifted_pos(cur_token.start, cur_token.len - 1), 1, + HL(ComparisonModifier)); + } else { + HL_CUR_TOKEN(Comparison); + } + want_node = kENodeValue; + break; + } + case kExprLexComma: { + assert(!(want_node == kENodeValue && cur_pt == kEPTLambdaArguments)); + if (want_node == kENodeValue) { + // Value level: comma appearing here is not valid. + // Note: in Vim string(,x) will give E116, this is not the case here. + ERROR_FROM_TOKEN_AND_MSG( + cur_token, _("E15: Expected value, got comma: %.*s")); + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeMissing); + cur_node->len = 0; + *top_node_p = cur_node; + want_node = kENodeOperator; + } + if (cur_pt == kEPTLambdaArguments) { + assert(lambda_node != NULL); + assert(lambda_node->data.fig.type_guesses.allow_lambda); + SELECT_FIGURE_BRACE_TYPE(lambda_node, Lambda, Lambda); + } + if (kv_size(ast_stack) < 2) { + goto viml_pexpr_parse_invalid_comma; + } + for (size_t i = 1; i < kv_size(ast_stack); i++) { + ExprASTNode *const *const eastnode_p = + (ExprASTNode *const *)kv_Z(ast_stack, i); + const ExprASTNodeType eastnode_type = (*eastnode_p)->type; + const ExprOpLvl eastnode_lvl = node_lvl(**eastnode_p); + if (eastnode_type == kExprNodeLambda) { + assert(cur_pt == kEPTLambdaArguments + && want_node == kENodeOperator); + break; + } else if (eastnode_type == kExprNodeDictLiteral + || eastnode_type == kExprNodeListLiteral + || eastnode_type == kExprNodeCall) { + break; + } else if (eastnode_type == kExprNodeComma + || eastnode_type == kExprNodeColon + || eastnode_lvl > kEOpLvlComma) { + // Do nothing + } else { +viml_pexpr_parse_invalid_comma: + ERROR_FROM_TOKEN_AND_MSG( + cur_token, + _("E15: Comma outside of call, lambda or literal: %.*s")); + break; + } + if (i == kv_size(ast_stack) - 1) { + goto viml_pexpr_parse_invalid_comma; + } + } + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeComma); + ADD_OP_NODE(cur_node); + HL_CUR_TOKEN(Comma); + break; + } +#define EXP_VAL_COLON "E15: Expected value, got colon: %.*s" + case kExprLexColon: { + bool is_ternary = false; + if (kv_size(ast_stack) < 2) { + goto viml_pexpr_parse_invalid_colon; + } + bool can_be_ternary = true; + bool is_subscript = false; + for (size_t i = 1; i < kv_size(ast_stack); i++) { + ExprASTNode *const *const eastnode_p = + (ExprASTNode *const *)kv_Z(ast_stack, i); + const ExprASTNodeType eastnode_type = (*eastnode_p)->type; + const ExprOpLvl eastnode_lvl = node_lvl(**eastnode_p); + STATIC_ASSERT(kEOpLvlTernary > kEOpLvlComma, + "Unexpected operator priorities"); + if (can_be_ternary && eastnode_type == kExprNodeTernaryValue + && !(*eastnode_p)->data.ter.got_colon) { + kv_drop(ast_stack, i); + (*eastnode_p)->start = cur_token.start; + (*eastnode_p)->len = cur_token.len; + if (prev_token.type == kExprLexSpacing) { + (*eastnode_p)->start = prev_token.start; + (*eastnode_p)->len += prev_token.len; + } + is_ternary = true; + (*eastnode_p)->data.ter.got_colon = true; + ADD_VALUE_IF_MISSING(_(EXP_VAL_COLON)); + assert((*eastnode_p)->children != NULL); + assert((*eastnode_p)->children->next == NULL); + kvi_push(ast_stack, &(*eastnode_p)->children->next); + break; + } else if (eastnode_type == kExprNodeUnknownFigure) { + SELECT_FIGURE_BRACE_TYPE(*eastnode_p, DictLiteral, Dict); + break; + } else if (eastnode_type == kExprNodeDictLiteral) { + break; + } else if (eastnode_type == kExprNodeSubscript) { + is_subscript = true; + can_be_ternary = false; + assert(!is_ternary); + break; + } else if (eastnode_type == kExprNodeColon) { + goto viml_pexpr_parse_invalid_colon; + } else if (eastnode_lvl >= kEOpLvlTernaryValue) { + // Do nothing + } else if (eastnode_lvl >= kEOpLvlComma) { + can_be_ternary = false; + } else { + goto viml_pexpr_parse_invalid_colon; + } + if (i == kv_size(ast_stack) - 1) { + goto viml_pexpr_parse_invalid_colon; + } + } + if (is_subscript) { + assert(kv_size(ast_stack) > 1); + // Colon immediately following subscript start: it is empty subscript + // part like a[:2]. + if (want_node == kENodeValue + && (*kv_Z(ast_stack, 1))->type == kExprNodeSubscript) { + NEW_NODE_WITH_CUR_POS(*top_node_p, kExprNodeMissing); + (*top_node_p)->len = 0; + want_node = kENodeOperator; + } else { + ADD_VALUE_IF_MISSING(_(EXP_VAL_COLON)); + } + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeColon); + ADD_OP_NODE(cur_node); + HL_CUR_TOKEN(SubscriptColon); + } else { + goto viml_pexpr_parse_valid_colon; +viml_pexpr_parse_invalid_colon: + ERROR_FROM_TOKEN_AND_MSG( + cur_token, + _("E15: Colon outside of dictionary or ternary operator: %.*s")); +viml_pexpr_parse_valid_colon: + ADD_VALUE_IF_MISSING(_(EXP_VAL_COLON)); + if (is_ternary) { + HL_CUR_TOKEN(TernaryColon); + } else { + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeColon); + ADD_OP_NODE(cur_node); + HL_CUR_TOKEN(Colon); + } + } + want_node = kENodeValue; + break; + } +#undef EXP_VAL_COLON + case kExprLexBracket: { + if (cur_token.data.brc.closing) { + ExprASTNode **new_top_node_p = NULL; + // Always drop the topmost value: + // + // 1. When want_node != kENodeValue topmost item on stack is + // a *finished* left operand, which may as well be "{@a}" which + // needs not be finished again. + // 2. Otherwise it is pointing to NULL what nobody wants. + kv_drop(ast_stack, 1); + if (!kv_size(ast_stack)) { + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeListLiteral); + cur_node->len = 0; + if (want_node != kENodeValue) { + cur_node->children = *top_node_p; + } + *top_node_p = cur_node; + goto viml_pexpr_parse_bracket_closing_error; + } + if (want_node == kENodeValue) { + // It is OK to want value if + // + // 1. It is empty list literal, in which case top node will be + // ListLiteral. + // 2. It is list literal with trailing comma, in which case top node + // will be that comma. + // 3. It is subscript with colon, but without one of the values: + // e.g. "a[:]", "a[1:]", top node will be colon in this case. + if ((*kv_last(ast_stack))->type != kExprNodeListLiteral + && (*kv_last(ast_stack))->type != kExprNodeComma + && (*kv_last(ast_stack))->type != kExprNodeColon) { + ERROR_FROM_TOKEN_AND_MSG( + cur_token, + _("E15: Expected value, got closing bracket: %.*s")); + } + } else { + if (!kv_size(ast_stack)) { + new_top_node_p = top_node_p; + goto viml_pexpr_parse_bracket_closing_error; + } + } + do { + new_top_node_p = kv_pop(ast_stack); + } while (kv_size(ast_stack) + && (new_top_node_p == NULL + || ((*new_top_node_p)->type != kExprNodeListLiteral + && (*new_top_node_p)->type != kExprNodeSubscript))); + ExprASTNode *new_top_node = *new_top_node_p; + switch (new_top_node->type) { + case kExprNodeListLiteral: { + if (pt_is_assignment(cur_pt) && new_top_node->children == NULL) { + ERROR_FROM_TOKEN_AND_MSG( + cur_token, _("E475: Unable to assign to empty list: %.*s")); + } + HL_CUR_TOKEN(List); + break; + } + case kExprNodeSubscript: { + HL_CUR_TOKEN(SubscriptBracket); + break; + } + default: { +viml_pexpr_parse_bracket_closing_error: + assert(!kv_size(ast_stack)); + ERROR_FROM_TOKEN_AND_MSG( + cur_token, _("E15: Unexpected closing figure brace: %.*s")); + HL_CUR_TOKEN(List); + break; + } + } + kvi_push(ast_stack, new_top_node_p); + want_node = kENodeOperator; + if (kv_size(ast_stack) <= asgn_level) { + assert(kv_size(ast_stack) == asgn_level); + asgn_level = 0; + if (cur_pt == kEPTAssignment) { + assert(ast.err.msg); + } else if (cur_pt == kEPTExpr + && kv_size(pt_stack) > 1 + && pt_is_assignment(kv_Z(pt_stack, 1))) { + kv_drop(pt_stack, 1); + } + } + if (cur_pt == kEPTSingleAssignment && kv_size(ast_stack) == 1) { + kv_drop(pt_stack, 1); + } + } else { + if (want_node == kENodeValue) { + // Value means list literal or list assignment. + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeListLiteral); + *top_node_p = cur_node; + kvi_push(ast_stack, &cur_node->children); + want_node = kENodeValue; + if (cur_pt == kEPTAssignment) { + // Additional assignment parse type allows to easily forbid nested + // lists. + kvi_push(pt_stack, kEPTSingleAssignment); + } else if (cur_pt == kEPTSingleAssignment) { + ERROR_FROM_TOKEN_AND_MSG( + cur_token, + _("E475: Nested lists not allowed when assigning: %.*s")); + } + HL_CUR_TOKEN(List); + } else { + // Operator means subscript, also in assignment. But in assignment + // subscript may be pretty much any expression, so need to push + // kEPTExpr. + if (prev_token.type == kExprLexSpacing) { + OP_MISSING; + } + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeSubscript); + ADD_OP_NODE(cur_node); + HL_CUR_TOKEN(SubscriptBracket); + if (pt_is_assignment(cur_pt)) { + assert(want_node == kENodeValue); // Subtract 1 for NULL at top. + asgn_level = kv_size(ast_stack) - 1; + kvi_push(pt_stack, kEPTExpr); + } + } + } + break; + } + case kExprLexFigureBrace: { + if (cur_token.data.brc.closing) { + ExprASTNode **new_top_node_p = NULL; + // Always drop the topmost value: + // + // 1. When want_node != kENodeValue topmost item on stack is + // a *finished* left operand, which may as well be "{@a}" which + // needs not be finished again. + // 2. Otherwise it is pointing to NULL what nobody wants. + kv_drop(ast_stack, 1); + if (!kv_size(ast_stack)) { + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeUnknownFigure); + cur_node->data.fig.type_guesses.allow_lambda = false; + cur_node->data.fig.type_guesses.allow_dict = false; + cur_node->data.fig.type_guesses.allow_ident = false; + cur_node->len = 0; + if (want_node != kENodeValue) { + cur_node->children = *top_node_p; + } + *top_node_p = cur_node; + new_top_node_p = top_node_p; + goto viml_pexpr_parse_figure_brace_closing_error; + } + if (want_node == kENodeValue) { + if ((*kv_last(ast_stack))->type != kExprNodeUnknownFigure + && (*kv_last(ast_stack))->type != kExprNodeComma) { + // kv_last being UnknownFigure may occur for empty dictionary + // literal, while Comma is expected in case of non-empty one. + ERROR_FROM_TOKEN_AND_MSG( + cur_token, + _("E15: Expected value, got closing figure brace: %.*s")); + } + } else { + if (!kv_size(ast_stack)) { + new_top_node_p = top_node_p; + goto viml_pexpr_parse_figure_brace_closing_error; + } + } + do { + new_top_node_p = kv_pop(ast_stack); + } while (kv_size(ast_stack) + && (new_top_node_p == NULL + || ((*new_top_node_p)->type != kExprNodeUnknownFigure + && (*new_top_node_p)->type != kExprNodeDictLiteral + && ((*new_top_node_p)->type + != kExprNodeCurlyBracesIdentifier) + && (*new_top_node_p)->type != kExprNodeLambda))); + ExprASTNode *new_top_node = *new_top_node_p; + switch (new_top_node->type) { + case kExprNodeUnknownFigure: { + if (new_top_node->children == NULL) { + // No children of curly braces node indicates empty dictionary. + assert(want_node == kENodeValue); + assert(new_top_node->data.fig.type_guesses.allow_dict); + SELECT_FIGURE_BRACE_TYPE(new_top_node, DictLiteral, Dict); + HL_CUR_TOKEN(Dict); + } else if (new_top_node->data.fig.type_guesses.allow_ident) { + SELECT_FIGURE_BRACE_TYPE(new_top_node, CurlyBracesIdentifier, + Curly); + HL_CUR_TOKEN(Curly); + } else { + // If by this time type of the node has not already been + // guessed, but it definitely is not a curly braces name then + // it is invalid for sure. + ERROR_FROM_NODE_AND_MSG( + new_top_node, + _("E15: Don't know what figure brace means: %.*s")); + if (pstate->colors) { + // Will reset to NvimInvalidFigureBrace. + kv_A(*pstate->colors, + new_top_node->data.fig.opening_hl_idx).group = ( + HL(FigureBrace)); + } + HL_CUR_TOKEN(FigureBrace); + } + break; + } + case kExprNodeDictLiteral: { + HL_CUR_TOKEN(Dict); + break; + } + case kExprNodeCurlyBracesIdentifier: { + HL_CUR_TOKEN(Curly); + break; + } + case kExprNodeLambda: { + HL_CUR_TOKEN(Lambda); + break; + } + default: { +viml_pexpr_parse_figure_brace_closing_error: + assert(!kv_size(ast_stack)); + ERROR_FROM_TOKEN_AND_MSG( + cur_token, _("E15: Unexpected closing figure brace: %.*s")); + HL_CUR_TOKEN(FigureBrace); + break; + } + } + kvi_push(ast_stack, new_top_node_p); + want_node = kENodeOperator; + if (kv_size(ast_stack) <= asgn_level) { + assert(kv_size(ast_stack) == asgn_level); + if (cur_pt == kEPTExpr + && kv_size(pt_stack) > 1 + && pt_is_assignment(kv_Z(pt_stack, 1))) { + kv_drop(pt_stack, 1); + asgn_level = 0; + } + } + } else { + if (want_node == kENodeValue) { + HL_CUR_TOKEN(FigureBrace); + // Value: may be any of lambda, dictionary literal and curly braces + // name. + + // Though if we are in an assignment this may only be a curly braces + // name. + if (pt_is_assignment(cur_pt)) { + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeCurlyBracesIdentifier); + cur_node->data.fig.type_guesses.allow_lambda = false; + cur_node->data.fig.type_guesses.allow_dict = false; + cur_node->data.fig.type_guesses.allow_ident = true; + kvi_push(pt_stack, kEPTExpr); + } else { + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeUnknownFigure); + cur_node->data.fig.type_guesses.allow_lambda = true; + cur_node->data.fig.type_guesses.allow_dict = true; + cur_node->data.fig.type_guesses.allow_ident = true; + } + if (pstate->colors) { + cur_node->data.fig.opening_hl_idx = kv_size(*pstate->colors) - 1; + } + *top_node_p = cur_node; + kvi_push(ast_stack, &cur_node->children); + kvi_push(pt_stack, kEPTLambdaArguments); + lambda_node = cur_node; + } else { + ADD_IDENT( + do { + NEW_NODE_WITH_CUR_POS(cur_node, + kExprNodeCurlyBracesIdentifier); + cur_node->data.fig.opening_hl_idx = kv_size(*pstate->colors); + cur_node->data.fig.type_guesses.allow_lambda = false; + cur_node->data.fig.type_guesses.allow_dict = false; + cur_node->data.fig.type_guesses.allow_ident = true; + kvi_push(ast_stack, &cur_node->children); + if (pt_is_assignment(cur_pt)) { + kvi_push(pt_stack, kEPTExpr); + } + want_node = kENodeValue; + } while (0), + Curly); + } + if (pt_is_assignment(cur_pt) + && !pt_is_assignment(kv_last(pt_stack))) { + assert(want_node == kENodeValue); // Subtract 1 for NULL at top. + asgn_level = kv_size(ast_stack) - 1; + } + } + break; + } + case kExprLexArrow: { + if (cur_pt == kEPTLambdaArguments) { + kv_drop(pt_stack, 1); + assert(kv_size(pt_stack)); + if (want_node == kENodeValue) { + // Wanting value means trailing comma and NULL at the top of the + // stack. + kv_drop(ast_stack, 1); + } + assert(kv_size(ast_stack) >= 1); + while ((*kv_last(ast_stack))->type != kExprNodeLambda + && (*kv_last(ast_stack))->type != kExprNodeUnknownFigure) { + kv_drop(ast_stack, 1); + } + assert((*kv_last(ast_stack)) == lambda_node); + SELECT_FIGURE_BRACE_TYPE(lambda_node, Lambda, Lambda); + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeArrow); + if (lambda_node->children == NULL) { + assert(want_node == kENodeValue); + lambda_node->children = cur_node; + kvi_push(ast_stack, &lambda_node->children); + } else { + assert(lambda_node->children->next == NULL); + lambda_node->children->next = cur_node; + kvi_push(ast_stack, &lambda_node->children->next); + } + kvi_push(ast_stack, &cur_node->children); + lambda_node = NULL; + } else { + // Only first branch is valid. + ADD_VALUE_IF_MISSING(_("E15: Unexpected arrow: %.*s")); + ERROR_FROM_TOKEN_AND_MSG( + cur_token, _("E15: Arrow outside of lambda: %.*s")); + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeArrow); + ADD_OP_NODE(cur_node); + } + want_node = kENodeValue; + HL_CUR_TOKEN(Arrow); + break; + } + case kExprLexPlainIdentifier: { + const ExprVarScope scope = (cur_token.type == kExprLexInvalid + ? kExprVarScopeMissing + : cur_token.data.var.scope); + if (want_node == kENodeValue) { + want_node = kENodeOperator; + NEW_NODE_WITH_CUR_POS(cur_node, + (node_is_key + ? kExprNodePlainKey + : kExprNodePlainIdentifier)); + cur_node->data.var.scope = scope; + const size_t scope_shift = (scope == kExprVarScopeMissing ? 0 : 2); + cur_node->data.var.ident = (pline.data + cur_token.start.col + + scope_shift); + cur_node->data.var.ident_len = cur_token.len - scope_shift; + *top_node_p = cur_node; + if (scope_shift) { + assert(!node_is_key); + viml_parser_highlight(pstate, cur_token.start, 1, + HL(IdentifierScope)); + viml_parser_highlight(pstate, shifted_pos(cur_token.start, 1), 1, + HL(IdentifierScopeDelimiter)); + } + viml_parser_highlight(pstate, shifted_pos(cur_token.start, + scope_shift), + cur_token.len - scope_shift, + (node_is_key + ? HL(IdentifierKey) + : HL(IdentifierName))); + } else { + if (scope == kExprVarScopeMissing) { + ADD_IDENT( + do { + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodePlainIdentifier); + cur_node->data.var.scope = scope; + cur_node->data.var.ident = pline.data + cur_token.start.col; + cur_node->data.var.ident_len = cur_token.len; + want_node = kENodeOperator; + } while (0), + IdentifierName); + } else { + OP_MISSING; + } + } + break; + } + case kExprLexNumber: { + if (want_node != kENodeValue) { + OP_MISSING; + } + if (node_is_key) { + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodePlainKey); + cur_node->data.var.ident = pline.data + cur_token.start.col; + cur_node->data.var.ident_len = cur_token.len; + HL_CUR_TOKEN(IdentifierKey); + } else if (cur_token.data.num.is_float) { + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeFloat); + cur_node->data.flt.value = cur_token.data.num.val.floating; + HL_CUR_TOKEN(Float); + } else { + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeInteger); + cur_node->data.num.value = cur_token.data.num.val.integer; + const uint8_t prefix_length = base_to_prefix_length[ + cur_token.data.num.base]; + viml_parser_highlight(pstate, cur_token.start, prefix_length, + HL(NumberPrefix)); + viml_parser_highlight( + pstate, shifted_pos(cur_token.start, prefix_length), + cur_token.len - prefix_length, HL(Number)); + } + want_node = kENodeOperator; + *top_node_p = cur_node; + break; + } + case kExprLexDot: { + ADD_VALUE_IF_MISSING(_("E15: Unexpected dot: %.*s")); + if (prev_token.type == kExprLexSpacing) { + if (cur_pt == kEPTAssignment) { + ERROR_FROM_TOKEN_AND_MSG( + cur_token, _("E15: Cannot concatenate in assignments: %.*s")); + } + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeConcat); + HL_CUR_TOKEN(Concat); + } else { + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeConcatOrSubscript); + HL_CUR_TOKEN(ConcatOrSubscript); + } + ADD_OP_NODE(cur_node); + break; + } + case kExprLexParenthesis: { + if (cur_token.data.brc.closing) { + if (want_node == kENodeValue) { + if (kv_size(ast_stack) > 1) { + const ExprASTNode *const prev_top_node = *kv_Z(ast_stack, 1); + if (prev_top_node->type == kExprNodeCall) { + // Function call without arguments, this is not an error. + // But further code does not expect NULL nodes. + kv_drop(ast_stack, 1); + goto viml_pexpr_parse_no_paren_closing_error; + } + } + ERROR_FROM_TOKEN_AND_MSG( + cur_token, _("E15: Expected value, got parenthesis: %.*s")); + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeMissing); + cur_node->len = 0; + *top_node_p = cur_node; + } else { + // Always drop the topmost value: when want_node != kENodeValue + // topmost item on stack is a *finished* left operand, which may as + // well be "(@a)" which needs not be finished again. + kv_drop(ast_stack, 1); + } +viml_pexpr_parse_no_paren_closing_error: {} + ExprASTNode **new_top_node_p = NULL; + while (kv_size(ast_stack) + && (new_top_node_p == NULL + || ((*new_top_node_p)->type != kExprNodeNested + && (*new_top_node_p)->type != kExprNodeCall))) { + new_top_node_p = kv_pop(ast_stack); + } + if (new_top_node_p != NULL + && ((*new_top_node_p)->type == kExprNodeNested + || (*new_top_node_p)->type == kExprNodeCall)) { + if ((*new_top_node_p)->type == kExprNodeNested) { + HL_CUR_TOKEN(NestingParenthesis); + } else { + HL_CUR_TOKEN(CallingParenthesis); + } + } else { + // “Always drop the topmost value” branch has got rid of the single + // value stack had, so there is nothing known to enclose. Correct + // this. + if (new_top_node_p == NULL) { + new_top_node_p = top_node_p; + } + ERROR_FROM_TOKEN_AND_MSG( + cur_token, _("E15: Unexpected closing parenthesis: %.*s")); + HL_CUR_TOKEN(NestingParenthesis); + cur_node = NEW_NODE(kExprNodeNested); + cur_node->start = cur_token.start; + cur_node->len = 0; + // Unexpected closing parenthesis, assume that it was wanted to + // enclose everything in (). + cur_node->children = *new_top_node_p; + *new_top_node_p = cur_node; + assert(cur_node->next == NULL); + } + kvi_push(ast_stack, new_top_node_p); + want_node = kENodeOperator; + } else { + if (want_node == kENodeValue) { + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeNested); + *top_node_p = cur_node; + kvi_push(ast_stack, &cur_node->children); + HL_CUR_TOKEN(NestingParenthesis); + } else if (want_node == kENodeOperator) { + if (prev_token.type == kExprLexSpacing) { + // For some reason "function (args)" is a function call, but + // "(funcref) (args)" is not. AFAIR this somehow involves + // compatibility and Bram was commenting that this is + // intentionally inconsistent and he is not very happy with the + // situation himself. + if ((*top_node_p)->type != kExprNodePlainIdentifier + && (*top_node_p)->type != kExprNodeComplexIdentifier + && (*top_node_p)->type != kExprNodeCurlyBracesIdentifier) { + OP_MISSING; + } + } + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeCall); + ADD_OP_NODE(cur_node); + HL_CUR_TOKEN(CallingParenthesis); + } else { + // Currently it is impossible to reach this. + assert(false); + } + want_node = kENodeValue; + } + break; + } + case kExprLexQuestion: { + ADD_VALUE_IF_MISSING(_("E15: Expected value, got question mark: %.*s")); + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeTernary); + ADD_OP_NODE(cur_node); + HL_CUR_TOKEN(Ternary); + ExprASTNode *ter_val_node; + NEW_NODE_WITH_CUR_POS(ter_val_node, kExprNodeTernaryValue); + ter_val_node->data.ter.got_colon = false; + assert(cur_node->children != NULL); + assert(cur_node->children->next == NULL); + assert(kv_last(ast_stack) == &cur_node->children->next); + *kv_last(ast_stack) = ter_val_node; + kvi_push(ast_stack, &ter_val_node->children); + break; + } + case kExprLexDoubleQuotedString: + case kExprLexSingleQuotedString: { + const bool is_double = (tok_type == kExprLexDoubleQuotedString); + if (!cur_token.data.str.closed) { + // It is weird, but Vim has two identical errors messages with + // different error numbers: "E114: Missing quote" and + // "E115: Missing quote". + ERROR_FROM_TOKEN_AND_MSG( + cur_token, (is_double + ? _("E114: Missing double quote: %.*s") + : _("E115: Missing single quote: %.*s"))); + } + if (want_node == kENodeOperator) { + OP_MISSING; + } + NEW_NODE_WITH_CUR_POS( + cur_node, (is_double + ? kExprNodeDoubleQuotedString + : kExprNodeSingleQuotedString)); + *top_node_p = cur_node; + parse_quoted_string(pstate, cur_node, cur_token, ast_stack, is_invalid); + want_node = kENodeOperator; + break; + } + case kExprLexAssignment: { + if (cur_pt == kEPTAssignment) { + kv_drop(pt_stack, 1); + } else if (cur_pt == kEPTSingleAssignment) { + kv_drop(pt_stack, 2); + ERROR_FROM_TOKEN_AND_MSG( + cur_token, + _("E475: Expected closing bracket to end list assignment " + "lvalue: %.*s")); + } else { + ERROR_FROM_TOKEN_AND_MSG( + cur_token, _("E15: Misplaced assignment: %.*s")); + } + assert(kv_size(pt_stack)); + assert(kv_last(pt_stack) == kEPTExpr); + ADD_VALUE_IF_MISSING(_("E15: Unexpected assignment: %.*s")); + NEW_NODE_WITH_CUR_POS(cur_node, kExprNodeAssignment); + cur_node->data.ass.type = cur_token.data.ass.type; + switch (cur_token.data.ass.type) { +#define HL_ASGN(asgn, hl) \ + case kExprAsgn##asgn: { HL_CUR_TOKEN(hl); break; } + HL_ASGN(Plain, PlainAssignment) + HL_ASGN(Add, AssignmentWithAddition) + HL_ASGN(Subtract, AssignmentWithSubtraction) + HL_ASGN(Concat, AssignmentWithConcatenation) +#undef HL_ASGN + } + ADD_OP_NODE(cur_node); + break; + } + } +viml_pexpr_parse_cycle_end: + prev_token = cur_token; + highlighted_prev_spacing = false; + viml_parser_advance(pstate, cur_token.len); + } while (true); +viml_pexpr_parse_end: + assert(kv_size(pt_stack)); + assert(kv_size(ast_stack)); + if (want_node == kENodeValue + // Blacklist some parse type entries as their presence means better error + // message in the other branch. + && kv_last(pt_stack) != kEPTLambdaArguments) { + east_set_error(pstate, &ast.err, _("E15: Expected value, got EOC: %.*s"), + pstate->pos); + } else if (kv_size(ast_stack) != 1) { + // Something may be wrong, check whether it really is. + + // Pointer to ast.root must never be dropped, so “!= 1” is expected to be + // the same as “> 1”. + assert(kv_size(ast_stack)); + // Topmost stack item must be a *finished* value, so it must not be + // analyzed. E.g. it may contain an already finished nested expression. + kv_drop(ast_stack, 1); + while (ast.err.msg == NULL && kv_size(ast_stack)) { + const ExprASTNode *const cur_node = (*kv_pop(ast_stack)); + // This should only happen when want_node == kENodeValue. + assert(cur_node != NULL); + // TODO(ZyX-I): Rehighlight as invalid? + switch (cur_node->type) { + case kExprNodeOpMissing: + case kExprNodeMissing: { + // Error should’ve been already reported. + break; + } + case kExprNodeCall: { + east_set_error( + pstate, &ast.err, + _("E116: Missing closing parenthesis for function call: %.*s"), + cur_node->start); + break; + } + case kExprNodeNested: { + east_set_error( + pstate, &ast.err, + _("E110: Missing closing parenthesis for nested expression" + ": %.*s"), + cur_node->start); + break; + } + case kExprNodeListLiteral: { + // For whatever reason "[1" yields "E696: Missing comma in list" error + // in Vim while "[1," yields E697. + east_set_error( + pstate, &ast.err, + _("E697: Missing end of List ']': %.*s"), + cur_node->start); + break; + } + case kExprNodeDictLiteral: { + // Same problem like with list literal with E722 (missing comma) vs + // E723, but additionally just "{" yields only E15. + east_set_error( + pstate, &ast.err, + _("E723: Missing end of Dictionary '}': %.*s"), + cur_node->start); + break; + } + case kExprNodeUnknownFigure: { + east_set_error( + pstate, &ast.err, + _("E15: Missing closing figure brace: %.*s"), + cur_node->start); + break; + } + case kExprNodeLambda: { + east_set_error( + pstate, &ast.err, + _("E15: Missing closing figure brace for lambda: %.*s"), + cur_node->start); + break; + } + case kExprNodeCurlyBracesIdentifier: { + // Until trailing "}" it is impossible to distinguish curly braces + // identifier and dictionary, so it must not appear in the stack like + // this. + assert(false); + } + case kExprNodeInteger: + case kExprNodeFloat: + case kExprNodeSingleQuotedString: + case kExprNodeDoubleQuotedString: + case kExprNodeOption: + case kExprNodeEnvironment: + case kExprNodeRegister: + case kExprNodePlainIdentifier: + case kExprNodePlainKey: { + // These are plain values and not containers, for them it should only + // be possible to show up in the topmost stack element, but it was + // unconditionally popped at the start. + assert(false); + } + case kExprNodeComma: + case kExprNodeColon: + case kExprNodeArrow: { + // It is actually only valid inside something else, but everything + // where one of the above is valid requires to be closed and thus is + // to be caught later. + break; + } + case kExprNodeSubscript: + case kExprNodeConcatOrSubscript: + case kExprNodeComplexIdentifier: + case kExprNodeAssignment: + case kExprNodeMod: + case kExprNodeDivision: + case kExprNodeMultiplication: + case kExprNodeNot: + case kExprNodeAnd: + case kExprNodeOr: + case kExprNodeConcat: + case kExprNodeComparison: + case kExprNodeUnaryMinus: + case kExprNodeUnaryPlus: + case kExprNodeBinaryMinus: + case kExprNodeTernary: + case kExprNodeBinaryPlus: { + // It is OK to see these in the stack. + break; + } + case kExprNodeTernaryValue: { + if (!cur_node->data.ter.got_colon) { + // Actually Vim throws E109 in more cases. + east_set_error( + pstate, &ast.err, _("E109: Missing ':' after '?': %.*s"), + cur_node->start); + } + break; + } + } + } + } + kvi_destroy(ast_stack); + return ast; +} // NOLINT(readability/fn_size) + +#undef NEW_NODE +#undef HL diff --git a/src/nvim/viml/parser/expressions.h b/src/nvim/viml/parser/expressions.h new file mode 100644 index 0000000000..23e172da75 --- /dev/null +++ b/src/nvim/viml/parser/expressions.h @@ -0,0 +1,389 @@ +#ifndef NVIM_VIML_PARSER_EXPRESSIONS_H +#define NVIM_VIML_PARSER_EXPRESSIONS_H + +#include <stddef.h> +#include <stdint.h> +#include <stdbool.h> + +#include "nvim/types.h" +#include "nvim/viml/parser/parser.h" +#include "nvim/eval/typval.h" + +// Defines whether to ignore case: +// == kCCStrategyUseOption +// ==# kCCStrategyMatchCase +// ==? kCCStrategyIgnoreCase +typedef enum { + kCCStrategyUseOption = 0, // 0 for xcalloc + kCCStrategyMatchCase = '#', + kCCStrategyIgnoreCase = '?', +} ExprCaseCompareStrategy; + +/// Lexer token type +typedef enum { + kExprLexInvalid = 0, ///< Invalid token, indicaten an error. + kExprLexMissing, ///< Missing token, for use in parser. + kExprLexSpacing, ///< Spaces, tabs, newlines, etc. + kExprLexEOC, ///< End of command character: NL, |, just end of stream. + + kExprLexQuestion, ///< Question mark, for use in ternary. + kExprLexColon, ///< Colon, for use in ternary. + kExprLexOr, ///< Logical or operator. + kExprLexAnd, ///< Logical and operator. + kExprLexComparison, ///< One of the comparison operators. + kExprLexPlus, ///< Plus sign. + kExprLexMinus, ///< Minus sign. + kExprLexDot, ///< Dot: either concat or subscript, also part of the float. + kExprLexMultiplication, ///< Multiplication, division or modulo operator. + + kExprLexNot, ///< Not: !. + + kExprLexNumber, ///< Integer number literal, or part of a float. + kExprLexSingleQuotedString, ///< Single quoted string literal. + kExprLexDoubleQuotedString, ///< Double quoted string literal. + kExprLexOption, ///< &optionname option value. + kExprLexRegister, ///< @r register value. + kExprLexEnv, ///< Environment $variable value. + kExprLexPlainIdentifier, ///< Identifier without scope: `abc`, `foo#bar`. + + kExprLexBracket, ///< Bracket, either opening or closing. + kExprLexFigureBrace, ///< Figure brace, either opening or closing. + kExprLexParenthesis, ///< Parenthesis, either opening or closing. + kExprLexComma, ///< Comma. + kExprLexArrow, ///< Arrow, like from lambda expressions. + kExprLexAssignment, ///< Assignment: `=` or `{op}=`. + // XXX When modifying this enum you need to also modify eltkn_type_tab in + // expressions.c and tests and, possibly, viml_pexpr_repr_token. +} LexExprTokenType; + +typedef enum { + kExprCmpEqual, ///< Equality, unequality. + kExprCmpMatches, ///< Matches regex, not matches regex. + kExprCmpGreater, ///< `>` or `<=` + kExprCmpGreaterOrEqual, ///< `>=` or `<`. + kExprCmpIdentical, ///< `is` or `isnot` +} ExprComparisonType; + +/// All possible option scopes +typedef enum { + kExprOptScopeUnspecified = 0, + kExprOptScopeGlobal = 'g', + kExprOptScopeLocal = 'l', +} ExprOptScope; + +/// All possible assignment types: `=` and `{op}=`. +typedef enum { + kExprAsgnPlain = 0, ///< Plain assignment: `=`. + kExprAsgnAdd, ///< Assignment augmented with addition: `+=`. + kExprAsgnSubtract, ///< Assignment augmented with subtraction: `-=`. + kExprAsgnConcat, ///< Assignment augmented with concatenation: `.=`. +} ExprAssignmentType; + +#define EXPR_OPT_SCOPE_LIST \ + ((char[]){ kExprOptScopeGlobal, kExprOptScopeLocal }) + +/// All possible variable scopes +typedef enum { + kExprVarScopeMissing = 0, + kExprVarScopeScript = 's', + kExprVarScopeGlobal = 'g', + kExprVarScopeVim = 'v', + kExprVarScopeBuffer = 'b', + kExprVarScopeWindow = 'w', + kExprVarScopeTabpage = 't', + kExprVarScopeLocal = 'l', + kExprVarScopeArguments = 'a', +} ExprVarScope; + +#define EXPR_VAR_SCOPE_LIST \ + ((char[]) { \ + kExprVarScopeScript, kExprVarScopeGlobal, kExprVarScopeVim, \ + kExprVarScopeBuffer, kExprVarScopeWindow, kExprVarScopeTabpage, \ + kExprVarScopeLocal, kExprVarScopeBuffer, kExprVarScopeArguments, \ + }) + +/// Lexer token +typedef struct { + ParserPosition start; + size_t len; + LexExprTokenType type; + union { + struct { + ExprComparisonType type; ///< Comparison type. + ExprCaseCompareStrategy ccs; ///< Case comparison strategy. + bool inv; ///< True if comparison is to be inverted. + } cmp; ///< For kExprLexComparison. + + struct { + enum { + kExprLexMulMul, ///< Real multiplication. + kExprLexMulDiv, ///< Division. + kExprLexMulMod, ///< Modulo. + } type; ///< Multiplication type. + } mul; ///< For kExprLexMultiplication. + + struct { + bool closing; ///< True if bracket/etc is a closing one. + } brc; ///< For brackets/braces/parenthesis. + + struct { + int name; ///< Register name, may be -1 if name not present. + } reg; ///< For kExprLexRegister. + + struct { + bool closed; ///< True if quote was closed. + } str; ///< For kExprLexSingleQuotedString and kExprLexDoubleQuotedString. + + struct { + const char *name; ///< Option name start. + size_t len; ///< Option name length. + ExprOptScope scope; ///< Option scope: &l:, &g: or not specified. + } opt; ///< Option properties. + + struct { + ExprVarScope scope; ///< Scope character or 0 if not present. + bool autoload; ///< Has autoload characters. + } var; ///< For kExprLexPlainIdentifier + + struct { + LexExprTokenType type; ///< Suggested type for parsing incorrect code. + const char *msg; ///< Error message. + } err; ///< For kExprLexInvalid + + struct { + union { + float_T floating; + uvarnumber_T integer; + } val; ///< Number value. + uint8_t base; ///< Base: 2, 8, 10 or 16. + bool is_float; ///< True if number is a floating-point. + } num; ///< For kExprLexNumber + + struct { + ExprAssignmentType type; + } ass; ///< For kExprLexAssignment + } data; ///< Additional data, if needed. +} LexExprToken; + +typedef enum { + /// If set, “pointer” to the current byte in pstate will not be shifted + kELFlagPeek = (1 << 0), + /// Determines whether scope is allowed to come before the identifier + kELFlagForbidScope = (1 << 1), + /// Determines whether floating-point numbers are allowed + /// + /// I.e. whether dot is a decimal point separator or is not a part of + /// a number at all. + kELFlagAllowFloat = (1 << 2), + /// Determines whether `is` and `isnot` are seen as comparison operators + /// + /// If set they are supposed to be just regular identifiers. + kELFlagIsNotCmp = (1 << 3), + /// Determines whether EOC tokens are allowed + /// + /// If set then it will yield Invalid token with E15 in place of EOC one if + /// “EOC” is something like "|". It is fine with emitting EOC at the end of + /// string still, with or without this flag set. + kELFlagForbidEOC = (1 << 4), + // XXX Whenever you add a new flag, alter klee_assume() statement in + // viml_expressions_lexer.c. +} LexExprFlags; + +/// Expression AST node type +typedef enum { + kExprNodeMissing = 0, + kExprNodeOpMissing, + kExprNodeTernary, ///< Ternary operator. + kExprNodeTernaryValue, ///< Ternary operator, colon. + kExprNodeRegister, ///< Register. + kExprNodeSubscript, ///< Subscript. + kExprNodeListLiteral, ///< List literal. + kExprNodeUnaryPlus, + kExprNodeBinaryPlus, + kExprNodeNested, ///< Nested parenthesised expression. + kExprNodeCall, ///< Function call. + /// Plain identifier: simple variable/function name + /// + /// Looks like "string", "g:Foo", etc: consists from a single + /// kExprLexPlainIdentifier token. + kExprNodePlainIdentifier, + /// Plain dictionary key, for use with kExprNodeConcatOrSubscript + kExprNodePlainKey, + /// Complex identifier: variable/function name with curly braces + kExprNodeComplexIdentifier, + /// Figure brace expression which is not yet known + /// + /// May resolve to any of kExprNodeDictLiteral, kExprNodeLambda or + /// kExprNodeCurlyBracesIdentifier. + kExprNodeUnknownFigure, + kExprNodeLambda, ///< Lambda. + kExprNodeDictLiteral, ///< Dictionary literal. + kExprNodeCurlyBracesIdentifier, ///< Part of the curly braces name. + kExprNodeComma, ///< Comma “operator”. + kExprNodeColon, ///< Colon “operator”. + kExprNodeArrow, ///< Arrow “operator”. + kExprNodeComparison, ///< Various comparison operators. + /// Concat operator + /// + /// To be only used in cases when it is known for sure it is not a subscript. + kExprNodeConcat, + /// Concat or subscript operator + /// + /// For cases when it is not obvious whether expression is a concat or + /// a subscript. May only have either number or plain identifier as the second + /// child. To make it easier to avoid curly braces in place of + /// kExprNodePlainIdentifier node kExprNodePlainKey is used. + kExprNodeConcatOrSubscript, + kExprNodeInteger, ///< Integral number. + kExprNodeFloat, ///< Floating-point number. + kExprNodeSingleQuotedString, + kExprNodeDoubleQuotedString, + kExprNodeOr, + kExprNodeAnd, + kExprNodeUnaryMinus, + kExprNodeBinaryMinus, + kExprNodeNot, + kExprNodeMultiplication, + kExprNodeDivision, + kExprNodeMod, + kExprNodeOption, + kExprNodeEnvironment, + kExprNodeAssignment, + // XXX When modifying this list also modify east_node_type_tab both in parser + // and in tests, and you most likely will also have to alter list of + // highlight groups stored in highlight_init_cmdline variable. +} ExprASTNodeType; + +typedef struct expr_ast_node ExprASTNode; + +/// Structure representing one AST node +struct expr_ast_node { + ExprASTNodeType type; ///< Node type. + /// Node children: e.g. for 1 + 2 nodes 1 and 2 will be children of +. + ExprASTNode *children; + /// Next node: e.g. for 1 + 2 child nodes 1 and 2 are put into a single-linked + /// list: `(+)->children` references only node 1, node 2 is in + /// `(+)->children->next`. + ExprASTNode *next; + ParserPosition start; + size_t len; + union { + struct { + int name; ///< Register name, may be -1 if name not present. + } reg; ///< For kExprNodeRegister. + struct { + /// Which nodes UnknownFigure can’t possibly represent. + struct { + /// True if UnknownFigure may actually represent dictionary literal. + bool allow_dict; + /// True if UnknownFigure may actually represent lambda. + bool allow_lambda; + /// True if UnknownFigure may actually be part of curly braces name. + bool allow_ident; + } type_guesses; + /// Highlight chunk index, used for rehighlighting if needed + size_t opening_hl_idx; + } fig; ///< For kExprNodeUnknownFigure. + struct { + ExprVarScope scope; ///< Scope character or 0 if not present. + /// Actual identifier without scope. + /// + /// Points to inside parser reader state. + const char *ident; + size_t ident_len; ///< Actual identifier length. + } var; ///< For kExprNodePlainIdentifier and kExprNodePlainKey. + struct { + bool got_colon; ///< True if colon was seen. + } ter; ///< For kExprNodeTernaryValue. + struct { + ExprComparisonType type; ///< Comparison type. + ExprCaseCompareStrategy ccs; ///< Case comparison strategy. + bool inv; ///< True if comparison is to be inverted. + } cmp; ///< For kExprNodeComparison. + struct { + uvarnumber_T value; + } num; ///< For kExprNodeInteger. + struct { + float_T value; + } flt; ///< For kExprNodeFloat. + struct { + char *value; + size_t size; + } str; ///< For kExprNodeSingleQuotedString and + ///< kExprNodeDoubleQuotedString. + struct { + const char *ident; ///< Option name start. + size_t ident_len; ///< Option name length. + ExprOptScope scope; ///< Option scope: &l:, &g: or not specified. + } opt; ///< For kExprNodeOption. + struct { + const char *ident; ///< Environment variable name start. + size_t ident_len; ///< Environment variable name length. + } env; ///< For kExprNodeEnvironment. + struct { + ExprAssignmentType type; + } ass; ///< For kExprNodeAssignment + } data; +}; + +enum { + /// Allow multiple expressions in a row: e.g. for :echo + /// + /// Parser will still parse only one of them though. + kExprFlagsMulti = (1 << 0), + /// Allow NL, NUL and bar to be EOC + /// + /// When parsing expressions input by user bar is assumed to be a binary + /// operator and other two are spacings. + kExprFlagsDisallowEOC = (1 << 1), + /// Parse :let argument + /// + /// That mean that top level node must be an assignment and first nodes + /// belong to lvalues. + kExprFlagsParseLet = (1 << 2), + // XXX whenever you add a new flag, alter klee_assume() statement in + // viml_expressions_parser.c, nvim_parse_expression() flags parsing + // alongside with its documentation and flag sets in check_parsing() + // function in expressions parser functional and unit tests. +} ExprParserFlags; + +/// AST error definition +typedef struct { + /// Error message. Must contain a single printf format atom: %.*s. + const char *msg; + /// Error message argument: points to the location of the error. + const char *arg; + /// Message argument length: length till the end of string. + int arg_len; +} ExprASTError; + +/// Structure representing complety AST for one expression +typedef struct { + /// When AST is not correct this message will be printed. + /// + /// Uses `emsgf(msg, arg_len, arg);`, `msg` is assumed to contain only `%.*s`. + ExprASTError err; + /// Root node of the AST. + ExprASTNode *root; +} ExprAST; + +/// Array mapping ExprASTNodeType to maximum amount of children node may have +extern const uint8_t node_maxchildren[]; + +/// Array mapping ExprASTNodeType values to their stringified versions +extern const char *const east_node_type_tab[]; + +/// Array mapping ExprComparisonType values to their stringified versions +extern const char *const eltkn_cmp_type_tab[]; + +/// Array mapping ExprCaseCompareStrategy values to their stringified versions +extern const char *const ccs_tab[]; + +/// Array mapping ExprAssignmentType values to their stringified versions +extern const char *const expr_asgn_type_tab[]; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "viml/parser/expressions.h.generated.h" +#endif + +#endif // NVIM_VIML_PARSER_EXPRESSIONS_H diff --git a/src/nvim/viml/parser/parser.c b/src/nvim/viml/parser/parser.c new file mode 100644 index 0000000000..8d26d08ea7 --- /dev/null +++ b/src/nvim/viml/parser/parser.c @@ -0,0 +1,16 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +#include "nvim/viml/parser/parser.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "viml/parser/parser.c.generated.h" +#endif + + +void parser_simple_get_line(void *cookie, ParserLine *ret_pline) +{ + ParserLine **plines_p = (ParserLine **)cookie; + *ret_pline = **plines_p; + (*plines_p)++; +} diff --git a/src/nvim/viml/parser/parser.h b/src/nvim/viml/parser/parser.h new file mode 100644 index 0000000000..7ac49709d8 --- /dev/null +++ b/src/nvim/viml/parser/parser.h @@ -0,0 +1,244 @@ +#ifndef NVIM_VIML_PARSER_PARSER_H +#define NVIM_VIML_PARSER_PARSER_H + +#include <stdbool.h> +#include <stddef.h> +#include <assert.h> + +#include "nvim/lib/kvec.h" +#include "nvim/func_attr.h" +#include "nvim/mbyte.h" +#include "nvim/memory.h" + +/// One parsed line +typedef struct { + const char *data; ///< Parsed line pointer + size_t size; ///< Parsed line size + bool allocated; ///< True if line may be freed. +} ParserLine; + +/// Line getter type for parser +/// +/// Line getter must return {NULL, 0} for EOF. +typedef void (*ParserLineGetter)(void *cookie, ParserLine *ret_pline); + +/// Parser position in the input +typedef struct { + size_t line; ///< Line index in ParserInputReader.lines. + size_t col; ///< Byte index in the line. +} ParserPosition; + +/// Parser state item. +typedef struct { + enum { + kPTopStateParsingCommand = 0, + kPTopStateParsingExpression, + } type; + union { + struct { + enum { + kExprUnknown = 0, + } type; + } expr; + } data; +} ParserStateItem; + +/// Structure defining input reader +typedef struct { + /// Function used to get next line. + ParserLineGetter get_line; + /// Data for get_line function. + void *cookie; + /// All lines obtained by get_line. + kvec_withinit_t(ParserLine, 4) lines; + /// Conversion, for :scriptencoding. + vimconv_T conv; +} ParserInputReader; + +/// Highlighted region definition +/// +/// Note: one chunk may highlight only one line. +typedef struct { + ParserPosition start; ///< Start of the highlight: line and column. + size_t end_col; ///< End column, points to the start of the next character. + const char *group; ///< Highlight group. +} ParserHighlightChunk; + +/// Highlighting defined by a parser +typedef kvec_withinit_t(ParserHighlightChunk, 16) ParserHighlight; + +/// Structure defining parser state +typedef struct { + /// Line reader. + ParserInputReader reader; + /// Position up to which input was parsed. + ParserPosition pos; + /// Parser state stack. + kvec_withinit_t(ParserStateItem, 16) stack; + /// Highlighting support. + ParserHighlight *colors; + /// True if line continuation can be used. + bool can_continuate; +} ParserState; + +static inline void viml_parser_init( + ParserState *const ret_pstate, + const ParserLineGetter get_line, void *const cookie, + ParserHighlight *const colors) + REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ARG(1, 2); + +/// Initialize a new parser state instance +/// +/// @param[out] ret_pstate Parser state to initialize. +/// @param[in] get_line Line getter function. +/// @param[in] cookie Argument for the get_line function. +/// @param[in] colors Where to save highlighting. May be NULL if it is not +/// needed. +static inline void viml_parser_init( + ParserState *const ret_pstate, + const ParserLineGetter get_line, void *const cookie, + ParserHighlight *const colors) +{ + *ret_pstate = (ParserState) { + .reader = { + .get_line = get_line, + .cookie = cookie, + .conv = MBYTE_NONE_CONV, + }, + .pos = { 0, 0 }, + .colors = colors, + .can_continuate = false, + }; + kvi_init(ret_pstate->reader.lines); + kvi_init(ret_pstate->stack); +} + +static inline void viml_parser_destroy(ParserState *const pstate) + REAL_FATTR_NONNULL_ALL REAL_FATTR_ALWAYS_INLINE; + +/// Free all memory allocated by the parser on heap +/// +/// @param pstate Parser state to free. +static inline void viml_parser_destroy(ParserState *const pstate) +{ + for (size_t i = 0; i < kv_size(pstate->reader.lines); i++) { + ParserLine pline = kv_A(pstate->reader.lines, i); + if (pline.allocated) { + xfree((void *)pline.data); + } + } + kvi_destroy(pstate->reader.lines); + kvi_destroy(pstate->stack); +} + +static inline void viml_preader_get_line(ParserInputReader *const preader, + ParserLine *const ret_pline) + REAL_FATTR_NONNULL_ALL; + +/// Get one line from ParserInputReader +static inline void viml_preader_get_line(ParserInputReader *const preader, + ParserLine *const ret_pline) +{ + ParserLine pline; + preader->get_line(preader->cookie, &pline); + if (preader->conv.vc_type != CONV_NONE && pline.size) { + ParserLine cpline = { + .allocated = true, + .size = pline.size, + }; + cpline.data = (char *)string_convert(&preader->conv, + (char_u *)pline.data, + &cpline.size); + if (pline.allocated) { + xfree((void *)pline.data); + } + pline = cpline; + } + kvi_push(preader->lines, pline); + *ret_pline = pline; +} + +static inline bool viml_parser_get_remaining_line(ParserState *const pstate, + ParserLine *const ret_pline) + REAL_FATTR_ALWAYS_INLINE REAL_FATTR_WARN_UNUSED_RESULT REAL_FATTR_NONNULL_ALL; + +/// Get currently parsed line, shifted to pstate->pos.col +/// +/// @param pstate Parser state to operate on. +/// +/// @return True if there is a line, false in case of EOF. +static inline bool viml_parser_get_remaining_line(ParserState *const pstate, + ParserLine *const ret_pline) +{ + const size_t num_lines = kv_size(pstate->reader.lines); + if (pstate->pos.line == num_lines) { + viml_preader_get_line(&pstate->reader, ret_pline); + } else { + *ret_pline = kv_last(pstate->reader.lines); + } + assert(pstate->pos.line == kv_size(pstate->reader.lines) - 1); + if (ret_pline->data != NULL) { + ret_pline->data += pstate->pos.col; + ret_pline->size -= pstate->pos.col; + } + return ret_pline->data != NULL; +} + +static inline void viml_parser_advance(ParserState *const pstate, + const size_t len) + REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL; + +/// Advance position by a given number of bytes +/// +/// At maximum advances to the next line. +/// +/// @param pstate Parser state to advance. +/// @param[in] len Number of bytes to advance. +static inline void viml_parser_advance(ParserState *const pstate, + const size_t len) +{ + assert(pstate->pos.line == kv_size(pstate->reader.lines) - 1); + const ParserLine pline = kv_last(pstate->reader.lines); + if (pstate->pos.col + len >= pline.size) { + pstate->pos.line++; + pstate->pos.col = 0; + } else { + pstate->pos.col += len; + } +} + +static inline void viml_parser_highlight(ParserState *const pstate, + const ParserPosition start, + const size_t end_col, + const char *const group) + REAL_FATTR_ALWAYS_INLINE REAL_FATTR_NONNULL_ALL; + +/// Record highlighting of some region of text +/// +/// @param pstate Parser state to work with. +/// @param[in] start Start position of the highlight. +/// @param[in] len Highlighting chunk length. +/// @param[in] group Highlight group. +static inline void viml_parser_highlight(ParserState *const pstate, + const ParserPosition start, + const size_t len, + const char *const group) +{ + if (pstate->colors == NULL || len == 0) { + return; + } + assert(kv_size(*pstate->colors) == 0 + || kv_Z(*pstate->colors, 0).start.line < start.line + || kv_Z(*pstate->colors, 0).end_col <= start.col); + kvi_push(*pstate->colors, ((ParserHighlightChunk) { + .start = start, + .end_col = start.col + len, + .group = group, + })); +} + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "viml/parser/parser.h.generated.h" +#endif + +#endif // NVIM_VIML_PARSER_PARSER_H diff --git a/src/nvim/window.c b/src/nvim/window.c index 43af2e5e4f..b4ef901d94 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -193,7 +193,7 @@ newwindow: /* cursor to previous window with wrap around */ case 'W': CHECK_CMDWIN - if (firstwin == lastwin && Prenum != 1) /* just one window */ + if (ONE_WINDOW && Prenum != 1) /* just one window */ beep_flush(); else { if (Prenum) { /* go to specified window */ @@ -574,7 +574,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) oldwin = curwin; /* add a status line when p_ls == 1 and splitting the first window */ - if (lastwin == firstwin && p_ls == 1 && oldwin->w_status_height == 0) { + if (ONE_WINDOW && p_ls == 1 && oldwin->w_status_height == 0) { if (oldwin->w_height <= p_wmh && new_wp == NULL) { EMSG(_(e_noroom)); return FAIL; @@ -641,11 +641,12 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) if (oldwin->w_width - new_size - 1 < p_wmw) do_equal = TRUE; - /* We don't like to take lines for the new window from a - * 'winfixwidth' window. Take them from a window to the left or right - * instead, if possible. */ - if (oldwin->w_p_wfw) - win_setwidth_win(oldwin->w_width + new_size, oldwin); + // We don't like to take lines for the new window from a + // 'winfixwidth' window. Take them from a window to the left or right + // instead, if possible. Add one for the separator. + if (oldwin->w_p_wfw) { + win_setwidth_win(oldwin->w_width + new_size + 1, oldwin); + } /* Only make all windows the same width if one of them (except oldwin) * is wider than one of the split windows. */ @@ -1182,7 +1183,7 @@ static void win_exchange(long Prenum) win_T *wp2; int temp; - if (lastwin == firstwin) { /* just one window */ + if (ONE_WINDOW) { /* just one window */ beep_flush(); return; } @@ -1271,7 +1272,7 @@ static void win_rotate(int upwards, int count) frame_T *frp; int n; - if (firstwin == lastwin) { /* nothing to do */ + if (ONE_WINDOW) { /* nothing to do */ beep_flush(); return; } @@ -1343,7 +1344,7 @@ static void win_totop(int size, int flags) int dir; int height = curwin->w_height; - if (lastwin == firstwin) { + if (ONE_WINDOW) { beep_flush(); return; } @@ -1724,11 +1725,10 @@ void close_windows(buf_T *buf, int keep_curwin) { tabpage_T *tp, *nexttp; int h = tabline_height(); - int count = tabpage_index(NULL); ++RedrawingDisabled; - for (win_T *wp = firstwin; wp != NULL && lastwin != firstwin; ) { + for (win_T *wp = firstwin; wp != NULL && !ONE_WINDOW; ) { if (wp->w_buffer == buf && (!keep_curwin || wp != curwin) && !(wp->w_closing || wp->w_buffer->b_locked > 0)) { if (win_close(wp, false) == FAIL) { @@ -1762,10 +1762,6 @@ void close_windows(buf_T *buf, int keep_curwin) --RedrawingDisabled; - if (count != tabpage_index(NULL)) { - apply_autocmds(EVENT_TABCLOSED, NULL, NULL, false, curbuf); - } - redraw_tabline = true; if (h != tabline_height()) { shell_new_rows(); @@ -1810,7 +1806,7 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, tabpage_T *prev_curtab) FUNC_ATTR_NONNULL_ARG(1) { - if (firstwin != lastwin) { + if (!ONE_WINDOW) { return false; } buf_T *old_curbuf = curbuf; @@ -1846,15 +1842,8 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, shell_new_rows(); } - if (term) { - // When a window containing a terminal buffer is closed, recalculate its - // size - terminal_resize(term, 0, 0); - } - // Since goto_tabpage_tp above did not trigger *Enter autocommands, do // that now. - apply_autocmds(EVENT_TABCLOSED, prev_idx, prev_idx, false, curbuf); apply_autocmds(EVENT_WINENTER, NULL, NULL, false, curbuf); apply_autocmds(EVENT_TABENTER, NULL, NULL, false, curbuf); if (old_curbuf != curbuf) { @@ -1878,6 +1867,7 @@ int win_close(win_T *win, int free_buf) int dir; int help_window = FALSE; tabpage_T *prev_curtab = curtab; + frame_T *win_frame = win->w_frame->fr_parent; if (last_window()) { EMSG(_("E444: Cannot close last window")); @@ -1996,6 +1986,14 @@ int win_close(win_T *win, int free_buf) * the screen space. */ wp = win_free_mem(win, &dir, NULL); + if (help_window) { + // Closing the help window moves the cursor back to the original window. + win_T *tmpwp = get_snapshot_focus(SNAP_HELP_IDX); + if (tmpwp != NULL) { + wp = tmpwp; + } + } + /* Make sure curwin isn't invalid. It can cause severe trouble when * printing an error message. For win_equal() curbuf needs to be valid * too. */ @@ -2027,7 +2025,9 @@ int win_close(win_T *win, int free_buf) check_cursor(); } if (p_ea && (*p_ead == 'b' || *p_ead == dir)) { - win_equal(curwin, true, dir); + // If the frame of the closed window contains the new current window, + // only resize that frame. Otherwise resize all windows. + win_equal(curwin, curwin->w_frame->fr_parent == win_frame, dir); } else { win_comp_pos(); } @@ -2103,19 +2103,29 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp) /* When closing the last window in a tab page remove the tab page. */ if (tp->tp_firstwin == tp->tp_lastwin) { - if (tp == first_tabpage) + char_u prev_idx[NUMBUFLEN]; + if (has_event(EVENT_TABCLOSED)) { + vim_snprintf((char *)prev_idx, NUMBUFLEN, "%i", tabpage_index(tp)); + } + + if (tp == first_tabpage) { first_tabpage = tp->tp_next; - else { + } else { for (ptp = first_tabpage; ptp != NULL && ptp->tp_next != tp; - ptp = ptp->tp_next) - ; + ptp = ptp->tp_next) { + // loop + } if (ptp == NULL) { - EMSG2(_(e_intern2), "win_close_othertab()"); + internal_error("win_close_othertab()"); return; } ptp->tp_next = tp->tp_next; } - free_tp = TRUE; + free_tp = true; + + if (has_event(EVENT_TABCLOSED)) { + apply_autocmds(EVENT_TABCLOSED, prev_idx, prev_idx, false, win->w_buffer); + } } /* Free the memory used for the window. */ @@ -2194,7 +2204,7 @@ winframe_remove ( /* * If there is only one window there is nothing to remove. */ - if (tp == NULL ? firstwin == lastwin : tp->tp_firstwin == tp->tp_lastwin) + if (tp == NULL ? ONE_WINDOW : tp->tp_firstwin == tp->tp_lastwin) return NULL; /* @@ -2331,7 +2341,7 @@ win_altframe ( frame_T *frp; int b; - if (tp == NULL ? firstwin == lastwin : tp->tp_firstwin == tp->tp_lastwin) + if (tp == NULL ? ONE_WINDOW : tp->tp_firstwin == tp->tp_lastwin) /* Last window in this tab page, will go to next tab page. */ return alt_tabpage()->tp_curwin->w_frame; @@ -2848,10 +2858,10 @@ close_others ( if (bufIsChanged(wp->w_buffer)) continue; } - win_close(wp, !P_HID(wp->w_buffer) && !bufIsChanged(wp->w_buffer)); + win_close(wp, !buf_hide(wp->w_buffer) && !bufIsChanged(wp->w_buffer)); } - if (message && lastwin != firstwin) + if (message && !ONE_WINDOW) EMSG(_("E445: Other window contains changes")); } @@ -3742,10 +3752,6 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, /* Change directories when the 'acd' option is set. */ do_autochdir(); - - if (curbuf->terminal) { - terminal_resize(curbuf->terminal, curwin->w_width, curwin->w_height); - } } @@ -4925,9 +4931,7 @@ void scroll_to_fraction(win_T *wp, int prev_height) } } -/* - * Set the width of a window. - */ +/// Set the width of a window. void win_new_width(win_T *wp, int width) { wp->w_width = width; @@ -4943,7 +4947,9 @@ void win_new_width(win_T *wp, int width) if (wp->w_buffer->terminal) { if (wp->w_height != 0) { - terminal_resize(wp->w_buffer->terminal, wp->w_width, 0); + terminal_resize(wp->w_buffer->terminal, + (uint16_t)(MAX(0, wp->w_width - win_col_off(wp))), + 0); } } } @@ -5173,7 +5179,7 @@ last_status ( { /* Don't make a difference between horizontal or vertical split. */ last_status_rec(topframe, (p_ls == 2 - || (p_ls == 1 && (morewin || lastwin != firstwin)))); + || (p_ls == 1 && (morewin || !ONE_WINDOW)))); } static void last_status_rec(frame_T *fr, int statusline) @@ -5428,6 +5434,27 @@ static win_T *restore_snapshot_rec(frame_T *sn, frame_T *fr) return wp; } +/// Gets the focused window (the one holding the cursor) of the snapshot. +static win_T *get_snapshot_focus(int idx) +{ + if (curtab->tp_snapshot[idx] == NULL) { + return NULL; + } + + frame_T *sn = curtab->tp_snapshot[idx]; + // This should be equivalent to the recursive algorithm found in + // restore_snapshot as far as traveling nodes go. + while (sn->fr_child != NULL || sn->fr_next != NULL) { + while (sn->fr_child != NULL) { + sn = sn->fr_child; + } + if (sn->fr_next != NULL) { + sn = sn->fr_next; + } + } + + return sn->fr_win; +} /* * Set "win" to be the curwin and "tp" to be the current tab page. @@ -5512,11 +5539,14 @@ void restore_buffer(bufref_T *save_curbuf) } -// Add match to the match list of window 'wp'. The pattern 'pat' will be -// highlighted with the group 'grp' with priority 'prio'. -// Optionally, a desired ID 'id' can be specified (greater than or equal to 1). -// If no particular ID is desired, -1 must be specified for 'id'. -// Return ID of added match, -1 on failure. +/// Add match to the match list of window 'wp'. The pattern 'pat' will be +/// highlighted with the group 'grp' with priority 'prio'. +/// Optionally, a desired ID 'id' can be specified (greater than or equal to 1). +/// +/// @param[in] id a desired ID 'id' can be specified +/// (greater than or equal to 1). -1 must be specified if no +/// particular ID is desired +/// @return ID of added match, -1 on failure. int match_add(win_T *wp, const char *const grp, const char *const pat, int prio, int id, list_T *pos_list, const char *const conceal_char) @@ -5532,8 +5562,8 @@ int match_add(win_T *wp, const char *const grp, const char *const pat, return -1; } if (id < -1 || id == 0) { - EMSGN("E799: Invalid ID: %" PRId64 - " (must be greater than or equal to 1)", + EMSGN(_("E799: Invalid ID: %" PRId64 + " (must be greater than or equal to 1)"), id); return -1; } @@ -5541,7 +5571,7 @@ int match_add(win_T *wp, const char *const grp, const char *const pat, cur = wp->w_match_head; while (cur != NULL) { if (cur->id == id) { - EMSGN("E801: ID already taken: %" PRId64, id); + EMSGN(_("E801: ID already taken: %" PRId64), id); return -1; } cur = cur->next; @@ -5581,49 +5611,48 @@ int match_add(win_T *wp, const char *const grp, const char *const pat, } // Set up position matches - if (pos_list != NULL) - { - linenr_T toplnum = 0; - linenr_T botlnum = 0; - listitem_T *li; - int i; - - for (i = 0, li = pos_list->lv_first; li != NULL && i < MAXPOSMATCH; - i++, li = li->li_next) { - linenr_T lnum = 0; - colnr_T col = 0; - int len = 1; - list_T *subl; - listitem_T *subli; + if (pos_list != NULL) { + linenr_T toplnum = 0; + linenr_T botlnum = 0; + + int i = 0; + TV_LIST_ITER(pos_list, li, { + linenr_T lnum = 0; + colnr_T col = 0; + int len = 1; bool error = false; - if (li->li_tv.v_type == VAR_LIST) { - subl = li->li_tv.vval.v_list; - if (subl == NULL) { - goto fail; - } - subli = subl->lv_first; + if (TV_LIST_ITEM_TV(li)->v_type == VAR_LIST) { + const list_T *const subl = TV_LIST_ITEM_TV(li)->vval.v_list; + const listitem_T *subli = tv_list_first(subl); if (subli == NULL) { + emsgf(_("E5030: Empty list at position %d"), + (int)tv_list_idx_of_item(pos_list, li)); goto fail; } - lnum = tv_get_number_chk(&subli->li_tv, &error); + lnum = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error); if (error) { goto fail; } - if (lnum == 0) { - --i; + if (lnum <= 0) { continue; } m->pos.pos[i].lnum = lnum; - subli = subli->li_next; + subli = TV_LIST_ITEM_NEXT(subl, subli); if (subli != NULL) { - col = tv_get_number_chk(&subli->li_tv, &error); + col = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error); if (error) { goto fail; } - subli = subli->li_next; + if (col < 0) { + continue; + } + subli = TV_LIST_ITEM_NEXT(subl, subli); if (subli != NULL) { - len = tv_get_number_chk(&subli->li_tv, &error); + len = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error); + if (len < 0) { + continue; + } if (error) { goto fail; } @@ -5631,16 +5660,16 @@ int match_add(win_T *wp, const char *const grp, const char *const pat, } m->pos.pos[i].col = col; m->pos.pos[i].len = len; - } else if (li->li_tv.v_type == VAR_NUMBER) { - if (li->li_tv.vval.v_number == 0) { - --i; + } else if (TV_LIST_ITEM_TV(li)->v_type == VAR_NUMBER) { + if (TV_LIST_ITEM_TV(li)->vval.v_number <= 0) { continue; } - m->pos.pos[i].lnum = li->li_tv.vval.v_number; + m->pos.pos[i].lnum = TV_LIST_ITEM_TV(li)->vval.v_number; m->pos.pos[i].col = 0; m->pos.pos[i].len = 0; } else { - EMSG(_("List or number required")); + emsgf(_("E5031: List or number required at position %d"), + (int)tv_list_idx_of_item(pos_list, li)); goto fail; } if (toplnum == 0 || lnum < toplnum) { @@ -5649,7 +5678,11 @@ int match_add(win_T *wp, const char *const grp, const char *const pat, if (botlnum == 0 || lnum >= botlnum) { botlnum = lnum + 1; } - } + i++; + if (i >= MAXPOSMATCH) { + break; + } + }); // Calculate top and bottom lines for redrawing area if (toplnum != 0){ @@ -5694,10 +5727,9 @@ fail: return -1; } -/* - * Delete match with ID 'id' in the match list of window 'wp'. - * Print error messages if 'perr' is TRUE. - */ + +/// Delete match with ID 'id' in the match list of window 'wp'. +/// Print error messages if 'perr' is TRUE. int match_delete(win_T *wp, int id, int perr) { matchitem_T *cur = wp->w_match_head; @@ -5705,10 +5737,11 @@ int match_delete(win_T *wp, int id, int perr) int rtype = SOME_VALID; if (id < 1) { - if (perr == TRUE) - EMSGN("E802: Invalid ID: %" PRId64 - " (must be greater than or equal to 1)", + if (perr) { + EMSGN(_("E802: Invalid ID: %" PRId64 + " (must be greater than or equal to 1)"), id); + } return -1; } while (cur != NULL && cur->id != id) { @@ -5716,8 +5749,9 @@ int match_delete(win_T *wp, int id, int perr) cur = cur->next; } if (cur == NULL) { - if (perr == TRUE) - EMSGN("E803: ID not found: %" PRId64, id); + if (perr) { + EMSGN(_("E803: ID not found: %" PRId64), id); + } return -1; } if (cur == prev) @@ -5888,13 +5922,15 @@ void win_get_tabwin(handle_T id, int *tabnr, int *winnr) } } -void win_id2tabwin(typval_T *argvars, list_T *list) +void win_id2tabwin(typval_T *const argvars, typval_T *const rettv) { int winnr = 1; int tabnr = 1; handle_T id = (handle_T)tv_get_number(&argvars[0]); win_get_tabwin(id, &tabnr, &winnr); + + list_T *const list = tv_list_alloc_ret(rettv, 2); tv_list_append_number(list, tabnr); tv_list_append_number(list, winnr); } |