diff options
Diffstat (limited to 'src')
130 files changed, 7802 insertions, 12154 deletions
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 2e45b5e9d6..747b63b1ba 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -18,8 +18,15 @@ set(HEADER_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gendeclarations.lua) set(GENERATED_INCLUDES_DIR ${PROJECT_BINARY_DIR}/include) set(GENERATED_EX_CMDS_ENUM ${GENERATED_INCLUDES_DIR}/ex_cmds_enum.generated.h) set(GENERATED_EX_CMDS_DEFS ${GENERATED_DIR}/ex_cmds_defs.generated.h) +set(GENERATED_EVENTS_ENUM ${GENERATED_INCLUDES_DIR}/auevents_enum.generated.h) +set(GENERATED_EVENTS_NAMES_MAP ${GENERATED_DIR}/auevents_name_map.generated.h) +set(GENERATED_OPTIONS ${GENERATED_DIR}/options.generated.h) set(EX_CMDS_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genex_cmds.lua) +set(EVENTS_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gen_events.lua) +set(OPTIONS_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genoptions.lua) +set(EVENTS_LIST_FILE ${PROJECT_SOURCE_DIR}/src/nvim/auevents.lua) set(EX_CMDS_DEFS_FILE ${PROJECT_SOURCE_DIR}/src/nvim/ex_cmds.lua) +set(OPTIONS_LIST_FILE ${PROJECT_SOURCE_DIR}/src/nvim/options.lua) include_directories(${GENERATED_DIR}) include_directories(${GENERATED_INCLUDES_DIR}) @@ -30,16 +37,19 @@ file(MAKE_DIRECTORY ${GENERATED_DIR}/api) file(MAKE_DIRECTORY ${GENERATED_DIR}/api/private) file(MAKE_DIRECTORY ${GENERATED_DIR}/msgpack_rpc) file(MAKE_DIRECTORY ${GENERATED_DIR}/tui) +file(MAKE_DIRECTORY ${GENERATED_DIR}/event) file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}) file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/os) file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/api) file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/api/private) file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/msgpack_rpc) file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/tui) +file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/event) file(GLOB NEOVIM_SOURCES *.c os/*.c api/*.c api/private/*.c msgpack_rpc/*.c - tui/*.c) + tui/*.c event/*.c) file(GLOB_RECURSE NEOVIM_HEADERS *.h) +file(GLOB UNIT_TEST_FIXTURES ${PROJECT_SOURCE_DIR}/test/unit/fixtures/*.c) foreach(sfile ${NEOVIM_SOURCES}) get_filename_component(f ${sfile} NAME) @@ -94,7 +104,7 @@ foreach(gen_cdef ${gen_cdefs} DO_NOT_DEFINE_EMPTY_ATTRIBUTES) list(APPEND gen_cflags "-D${gen_cdef}") endif() endforeach() -if (SANITIZE) +if(CLANG_ASAN_UBSAN OR CLANG_MSAN OR CLANG_TSAN) list(APPEND gen_cflags "-DEXITFREE") endif() @@ -146,6 +156,9 @@ list(APPEND NEOVIM_GENERATED_SOURCES "${MSGPACK_DISPATCH}" "${GENERATED_EX_CMDS_ENUM}" "${GENERATED_EX_CMDS_DEFS}" + "${GENERATED_EVENTS_ENUM}" + "${GENERATED_EVENTS_NAMES_MAP}" + "${GENERATED_OPTIONS}" ) add_custom_command(OUTPUT ${GENERATED_EX_CMDS_ENUM} ${GENERATED_EX_CMDS_DEFS} @@ -154,6 +167,18 @@ add_custom_command(OUTPUT ${GENERATED_EX_CMDS_ENUM} ${GENERATED_EX_CMDS_DEFS} DEPENDS ${EX_CMDS_GENERATOR} ${EX_CMDS_DEFS_FILE} ) +add_custom_command(OUTPUT ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP} + COMMAND ${LUA_PRG} ${EVENTS_GENERATOR} + ${PROJECT_SOURCE_DIR}/src/nvim ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP} + DEPENDS ${EVENTS_GENERATOR} ${EVENTS_LIST_FILE} +) + +add_custom_command(OUTPUT ${GENERATED_OPTIONS} + COMMAND ${LUA_PRG} ${OPTIONS_GENERATOR} + ${PROJECT_SOURCE_DIR}/src/nvim ${GENERATED_OPTIONS} + DEPENDS ${OPTIONS_GENERATOR} ${OPTIONS_LIST_FILE} +) + # Our dependencies come first. if (LibIntl_FOUND) @@ -189,11 +214,21 @@ add_executable(nvim ${NEOVIM_GENERATED_SOURCES} ${NEOVIM_SOURCES} target_link_libraries(nvim ${NVIM_EXEC_LINK_LIBRARIES}) install_helper(TARGETS nvim) -if(SANITIZE) - message(STATUS "Enabling Clang sanitizers for nvim") +if(CLANG_ASAN_UBSAN) + message(STATUS "Enabling Clang address sanitizer and undefined behavior sanitizer for nvim.") set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "-DEXITFREE ") - set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "-fno-sanitize-recover -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=address -fsanitize=undefined ") + set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "-fno-sanitize-recover -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=address -fsanitize=undefined -fsanitize-blacklist=${PROJECT_SOURCE_DIR}/.asan-blacklist") set_property(TARGET nvim APPEND_STRING PROPERTY LINK_FLAGS "-fsanitize=address -fsanitize=undefined ") +elseif(CLANG_MSAN) + message(STATUS "Enabling Clang memory sanitizer for nvim.") + set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "-DEXITFREE ") + set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "-fsanitize=memory -fsanitize-memory-track-origins -fno-omit-frame-pointer -fno-optimize-sibling-calls ") + set_property(TARGET nvim APPEND_STRING PROPERTY LINK_FLAGS "-fsanitize=memory -fsanitize-memory-track-origins ") +elseif(CLANG_TSAN) + message(STATUS "Enabling Clang thread sanitizer for nvim.") + set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "-DEXITFREE ") + set_property(TARGET nvim APPEND_STRING PROPERTY COMPILE_FLAGS "-fsanitize=thread ") + set_property(TARGET nvim APPEND_STRING PROPERTY LINK_FLAGS "-fsanitize=thread ") endif() add_library(libnvim STATIC EXCLUDE_FROM_ALL ${NEOVIM_GENERATED_SOURCES} @@ -205,7 +240,7 @@ set_target_properties(libnvim PROPERTIES set_property(TARGET libnvim APPEND_STRING PROPERTY COMPILE_FLAGS " -DMAKE_LIB ") add_library(nvim-test MODULE EXCLUDE_FROM_ALL ${NEOVIM_GENERATED_SOURCES} - ${NEOVIM_SOURCES} ${NEOVIM_HEADERS}) + ${NEOVIM_SOURCES} ${UNIT_TEST_FIXTURES} ${NEOVIM_HEADERS}) target_link_libraries(nvim-test ${NVIM_LINK_LIBRARIES}) set_property(TARGET nvim-test APPEND_STRING PROPERTY COMPILE_FLAGS -DUNIT_TESTING) diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index c9ada8dfc0..12c97cfffb 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -70,7 +70,6 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err) /// @param line The new line. /// @param[out] err Details of an error that may have occurred void buffer_set_line(Buffer buffer, Integer index, String line, Error *err) - FUNC_ATTR_DEFERRED { Object l = STRING_OBJ(line); Array array = {.items = &l, .size = 1}; @@ -83,7 +82,6 @@ void buffer_set_line(Buffer buffer, Integer index, String line, Error *err) /// @param index The line index /// @param[out] err Details of an error that may have occurred void buffer_del_line(Buffer buffer, Integer index, Error *err) - FUNC_ATTR_DEFERRED { Array array = ARRAY_DICT_INIT; buffer_set_line_slice(buffer, index, index, true, true, array, err); @@ -108,7 +106,7 @@ ArrayOf(String) buffer_get_line_slice(Buffer buffer, Array rv = ARRAY_DICT_INIT; buf_T *buf = find_buffer_by_handle(buffer, err); - if (!buf) { + if (!buf || !inbounds(buf, start)) { return rv; } @@ -171,7 +169,6 @@ void buffer_set_line_slice(Buffer buffer, Boolean include_end, ArrayOf(String) replacement, Error *err) - FUNC_ATTR_DEFERRED { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -179,6 +176,11 @@ void buffer_set_line_slice(Buffer buffer, return; } + if (!inbounds(buf, start)) { + api_set_error(err, Validation, _("Index out of bounds")); + return; + } + start = normalize_index(buf, start) + (include_start ? 0 : 1); include_end = include_end || (end >= buf->b_ml.ml_line_count); end = normalize_index(buf, end) + (include_end ? 1 : 0); @@ -334,7 +336,6 @@ Object buffer_get_var(Buffer buffer, String name, Error *err) /// @param[out] err Details of an error that may have occurred /// @return The old value Object buffer_set_var(Buffer buffer, String name, Object value, Error *err) - FUNC_ATTR_DEFERRED { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -370,7 +371,6 @@ Object buffer_get_option(Buffer buffer, String name, Error *err) /// @param value The option value /// @param[out] err Details of an error that may have occurred void buffer_set_option(Buffer buffer, String name, Object value, Error *err) - FUNC_ATTR_DEFERRED { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -421,7 +421,6 @@ String buffer_get_name(Buffer buffer, Error *err) /// @param name The buffer name /// @param[out] err Details of an error that may have occurred void buffer_set_name(Buffer buffer, String name, Error *err) - FUNC_ATTR_DEFERRED { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -467,7 +466,6 @@ void buffer_insert(Buffer buffer, Integer lnum, ArrayOf(String) lines, Error *err) - FUNC_ATTR_DEFERRED { buffer_set_line_slice(buffer, lnum, lnum, false, true, lines, err); } @@ -550,3 +548,10 @@ static int64_t normalize_index(buf_T *buf, int64_t index) index = index > buf->b_ml.ml_line_count ? buf->b_ml.ml_line_count : index; return index; } + +// Returns true if the 0-indexed `index` is within the 1-indexed buffer bounds. +static bool inbounds(buf_T *buf, int64_t index) +{ + linenr_T nlines = buf->b_ml.ml_line_count; + return index >= -nlines && index < nlines; +} diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 2a01f83688..0485fbacd2 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -426,8 +426,12 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) case kObjectTypeString: tv->v_type = VAR_STRING; - tv->vval.v_string = xmemdupz(obj.data.string.data, - obj.data.string.size); + if (obj.data.string.data == NULL) { + tv->vval.v_string = NULL; + } else { + tv->vval.v_string = xmemdupz(obj.data.string.data, + obj.data.string.size); + } break; case kObjectTypeArray: diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c index 1c958118e1..126ee4072d 100644 --- a/src/nvim/api/tabpage.c +++ b/src/nvim/api/tabpage.c @@ -62,7 +62,6 @@ Object tabpage_get_var(Tabpage tabpage, String name, Error *err) /// @param[out] err Details of an error that may have occurred /// @return The tab page handle Object tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err) - FUNC_ATTR_DEFERRED { tabpage_T *tab = find_tab_by_handle(tabpage, err); diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 22bdbc2ee5..b9900b5d5a 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -37,7 +37,6 @@ /// @param str The command str /// @param[out] err Details of an error that may have occurred void vim_command(String str, Error *err) - FUNC_ATTR_DEFERRED { // Run the command try_start(); @@ -54,7 +53,6 @@ void vim_command(String str, Error *err) /// @see feedkeys() /// @see vim_strsave_escape_csi void vim_feedkeys(String keys, String mode, Boolean escape_csi) - FUNC_ATTR_DEFERRED { bool remap = true; bool insert = false; @@ -100,6 +98,7 @@ void vim_feedkeys(String keys, String mode, Boolean escape_csi) /// @return The number of bytes actually written, which can be lower than /// requested if the buffer becomes full. Integer vim_input(String keys) + FUNC_ATTR_ASYNC { return (Integer)input_enqueue(keys); } @@ -143,7 +142,6 @@ String vim_command_output(String str, Error *err) /// @param[out] err Details of an error that may have occurred /// @return The expanded object Object vim_eval(String str, Error *err) - FUNC_ATTR_DEFERRED { Object rv = OBJECT_INIT; // Evaluate the expression @@ -164,6 +162,55 @@ Object vim_eval(String str, Error *err) return rv; } +/// Call the given function with the given arguments stored in an array. +/// +/// @param fname Function to call +/// @param args Functions arguments packed in an Array +/// @param[out] err Details of an error that may have occurred +/// @return Result of the function call +Object vim_call_function(String fname, Array args, Error *err) +{ + Object rv = OBJECT_INIT; + if (args.size > MAX_FUNC_ARGS) { + api_set_error(err, Validation, + _("Function called with too many arguments.")); + return rv; + } + + // Convert the arguments in args from Object to typval_T values + typval_T vim_args[MAX_FUNC_ARGS + 1]; + size_t i = 0; // also used for freeing the variables + for (; i < args.size; i++) { + if (!object_to_vim(args.items[i], &vim_args[i], err)) { + goto free_vim_args; + } + } + + try_start(); + // Call the function + typval_T rettv; + int dummy; + int r = call_func((char_u *) fname.data, (int) fname.size, + &rettv, (int) args.size, vim_args, + curwin->w_cursor.lnum, curwin->w_cursor.lnum, &dummy, + true, + NULL); + if (r == FAIL) { + api_set_error(err, Exception, _("Error calling function.")); + } + if (!try_end(err)) { + rv = vim_to_object(&rettv); + } + clear_tv(&rettv); + +free_vim_args: + while (i > 0) { + clear_tv(&vim_args[--i]); + } + + return rv; +} + /// Calculates the number of display cells `str` occupies, tab is counted as /// one cell. /// @@ -262,7 +309,6 @@ String vim_get_current_line(Error *err) /// @param line The line contents /// @param[out] err Details of an error that may have occurred void vim_set_current_line(String line, Error *err) - FUNC_ATTR_DEFERRED { buffer_set_line(curbuf->handle, curwin->w_cursor.lnum - 1, line, err); } @@ -271,7 +317,6 @@ void vim_set_current_line(String line, Error *err) /// /// @param[out] err Details of an error that may have occurred void vim_del_current_line(Error *err) - FUNC_ATTR_DEFERRED { buffer_del_line(curbuf->handle, curwin->w_cursor.lnum - 1, err); } @@ -293,7 +338,6 @@ Object vim_get_var(String name, Error *err) /// @param[out] err Details of an error that may have occurred /// @return the old value if any Object vim_set_var(String name, Object value, Error *err) - FUNC_ATTR_DEFERRED { return dict_set_value(&globvardict, name, value, err); } @@ -324,7 +368,6 @@ Object vim_get_option(String name, Error *err) /// @param value The new option value /// @param[out] err Details of an error that may have occurred void vim_set_option(String name, Object value, Error *err) - FUNC_ATTR_DEFERRED { set_option_to(NULL, SREQ_GLOBAL, name, value, err); } @@ -333,7 +376,6 @@ void vim_set_option(String name, Object value, Error *err) /// /// @param str The message void vim_out_write(String str) - FUNC_ATTR_DEFERRED { write_msg(str, false); } @@ -342,7 +384,6 @@ void vim_out_write(String str) /// /// @param str The message void vim_err_write(String str) - FUNC_ATTR_DEFERRED { write_msg(str, true); } @@ -352,7 +393,6 @@ void vim_err_write(String str) /// /// @param str The message void vim_report_error(String str) - FUNC_ATTR_DEFERRED { vim_err_write(str); vim_err_write((String) {.data = "\n", .size = 1}); @@ -392,7 +432,6 @@ Buffer vim_get_current_buffer(void) /// @param id The buffer handle /// @param[out] err Details of an error that may have occurred void vim_set_current_buffer(Buffer buffer, Error *err) - FUNC_ATTR_DEFERRED { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -443,7 +482,6 @@ Window vim_get_current_window(void) /// /// @param handle The window handle void vim_set_current_window(Window window, Error *err) - FUNC_ATTR_DEFERRED { win_T *win = find_window_by_handle(window, err); @@ -495,7 +533,6 @@ Tabpage vim_get_current_tabpage(void) /// @param handle The tab page handle /// @param[out] err Details of an error that may have occurred void vim_set_current_tabpage(Tabpage tabpage, Error *err) - FUNC_ATTR_DEFERRED { tabpage_T *tp = find_tab_by_handle(tabpage, err); @@ -559,6 +596,7 @@ Dictionary vim_get_color_map(void) Array vim_get_api_info(uint64_t channel_id) + FUNC_ATTR_ASYNC { Array rv = ARRAY_DICT_INIT; diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index 5034c26c83..aad616c7bf 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -54,7 +54,6 @@ ArrayOf(Integer, 2) window_get_cursor(Window window, Error *err) /// @param pos the (row, col) tuple representing the new position /// @param[out] err Details of an error that may have occurred void window_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err) - FUNC_ATTR_DEFERRED { win_T *win = find_window_by_handle(window, err); @@ -118,7 +117,6 @@ Integer window_get_height(Window window, Error *err) /// @param height the new height in rows /// @param[out] err Details of an error that may have occurred void window_set_height(Window window, Integer height, Error *err) - FUNC_ATTR_DEFERRED { win_T *win = find_window_by_handle(window, err); @@ -162,7 +160,6 @@ Integer window_get_width(Window window, Error *err) /// @param width the new width in columns /// @param[out] err Details of an error that may have occurred void window_set_width(Window window, Integer width, Error *err) - FUNC_ATTR_DEFERRED { win_T *win = find_window_by_handle(window, err); @@ -208,7 +205,6 @@ Object window_get_var(Window window, String name, Error *err) /// @param[out] err Details of an error that may have occurred /// @return The old value Object window_set_var(Window window, String name, Object value, Error *err) - FUNC_ATTR_DEFERRED { win_T *win = find_window_by_handle(window, err); @@ -244,7 +240,6 @@ Object window_get_option(Window window, String name, Error *err) /// @param value The option value /// @param[out] err Details of an error that may have occurred void window_set_option(Window window, String name, Object value, Error *err) - FUNC_ATTR_DEFERRED { win_T *win = find_window_by_handle(window, err); diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua new file mode 100644 index 0000000000..af801c6f1a --- /dev/null +++ b/src/nvim/auevents.lua @@ -0,0 +1,106 @@ +return { + events = { + 'BufAdd', -- after adding a buffer to the buffer list + 'BufDelete', -- deleting a buffer from the buffer list + 'BufEnter', -- after entering a buffer + 'BufFilePost', -- after renaming a buffer + 'BufFilePre', -- before renaming a buffer + 'BufHidden', -- just after buffer becomes hidden + 'BufLeave', -- before leaving a buffer + 'BufNew', -- after creating any buffer + 'BufNewFile', -- when creating a buffer for a new file + 'BufReadCmd', -- read buffer using command + 'BufReadPost', -- after reading a buffer + 'BufReadPre', -- before reading a buffer + 'BufUnload', -- just before unloading a buffer + 'BufWinEnter', -- after showing a buffer in a window + 'BufWinLeave', -- just after buffer removed from window + 'BufWipeout', -- just before really deleting a buffer + 'BufWriteCmd', -- write buffer using command + 'BufWritePost', -- after writing a buffer + 'BufWritePre', -- before writing a buffer + 'CmdUndefined', -- command undefined + 'CmdWinEnter', -- after entering the cmdline window + 'CmdWinLeave', -- before leaving the cmdline window + 'ColorScheme', -- after loading a colorscheme + 'CompleteDone', -- after finishing insert complete + 'CursorHold', -- cursor in same position for a while + 'CursorHoldI', -- idem, in Insert mode + 'CursorMoved', -- cursor was moved + 'CursorMovedI', -- cursor was moved in Insert mode + 'EncodingChanged', -- after changing the 'encoding' option + 'FileAppendCmd', -- append to a file using command + 'FileAppendPost', -- after appending to a file + 'FileAppendPre', -- before appending to a file + 'FileChangedRO', -- before first change to read-only file + 'FileChangedShell', -- after shell command that changed file + 'FileChangedShellPost', -- after (not) reloading changed file + 'FileReadCmd', -- read from a file using command + 'FileReadPost', -- after reading a file + 'FileReadPre', -- before reading a file + 'FileType', -- new file type detected (user defined) + 'FileWriteCmd', -- write to a file using command + 'FileWritePost', -- after writing a file + 'FileWritePre', -- before writing a file + 'FilterReadPost', -- after reading from a filter + 'FilterReadPre', -- before reading from a filter + 'FilterWritePost', -- after writing to a filter + 'FilterWritePre', -- before writing to a filter + 'FocusGained', -- got the focus + 'FocusLost', -- lost the focus to another app + 'FuncUndefined', -- if calling a function which doesn't exist + 'GUIEnter', -- after starting the GUI + 'GUIFailed', -- after starting the GUI failed + 'InsertChange', -- when changing Insert/Replace mode + 'InsertCharPre', -- before inserting a char + 'InsertEnter', -- when entering Insert mode + 'InsertLeave', -- when leaving Insert mode + 'JobActivity', -- when job sent some data + 'MenuPopup', -- just before popup menu is displayed + 'QuickFixCmdPost', -- after :make, :grep etc. + 'QuickFixCmdPre', -- before :make, :grep etc. + 'QuitPre', -- before :quit + 'RemoteReply', -- upon string reception from a remote vim + 'SessionLoadPost', -- after loading a session file + 'ShellCmdPost', -- after ":!cmd" + 'ShellFilterPost', -- after ":1,2!cmd", ":w !cmd", ":r !cmd". + 'SourceCmd', -- sourcing a Vim script using command + 'SourcePre', -- before sourcing a Vim script + 'SpellFileMissing', -- spell file missing + 'StdinReadPost', -- after reading from stdin + 'StdinReadPre', -- before reading from stdin + 'SwapExists', -- found existing swap file + 'Syntax', -- syntax selected + 'TabClosed', -- a tab has closed + 'TabEnter', -- after entering a tab page + 'TabLeave', -- before leaving a tab page + 'TabNew', -- when creating a new tab + 'TabNewEntered', -- after entering a new tab + 'TermChanged', -- after changing 'term' + 'TermResponse', -- after setting "v:termresponse" + 'TermOpen', -- after opening a terminal buffer + 'TextChanged', -- text was modified + 'TextChangedI', -- text was modified in Insert mode + 'User', -- user defined autocommand + 'VimEnter', -- after starting Vim + 'VimLeave', -- before exiting Vim + 'VimLeavePre', -- before exiting Vim and writing .viminfo + 'VimResized', -- after Vim window was resized + 'WinEnter', -- after entering a window + 'WinLeave', -- before leaving a window + }, + aliases = { + BufCreate = 'BufAdd', + BufRead = 'BufReadPost', + BufWrite = 'BufWritePre', + FileEncoding = 'EncodingChanged', + }, + -- List of neovim-specific events or aliases for the purpose of generating + -- syntax file + neovim_specific = { + TabNew=true, + TabNewEntered=true, + TabClosed=true, + TermEnter=true, + }, +} diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index b4b36f7fc0..2f87c9cbd2 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -2679,7 +2679,7 @@ void maketitle(void) append_arg_number(curwin, buf, SPACE_FOR_ARGNR, FALSE); - STRCAT(buf, " - VIM"); + STRCAT(buf, " - NVIM"); if (maxlen > 0) { /* make it shorter by removing a bit in the middle */ @@ -3576,7 +3576,7 @@ void fname_expand(buf_T *buf, char_u **ffname, char_u **sfname) *sfname = *ffname; *ffname = (char_u *)fix_fname((char *)*ffname); /* expand to full path */ -#ifdef FEAT_SHORTCUT +#ifdef WIN32 if (!buf->b_p_bin) { char_u *rfname; diff --git a/src/nvim/edit.c b/src/nvim/edit.c index dd82b06158..6bcf5e804a 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -59,7 +59,7 @@ #include "nvim/terminal.h" #include "nvim/undo.h" #include "nvim/window.h" -#include "nvim/os/event.h" +#include "nvim/event/loop.h" #include "nvim/os/input.h" #include "nvim/os/time.h" @@ -254,7 +254,7 @@ edit ( ) { if (curbuf->terminal) { - terminal_enter(true); + terminal_enter(); return false; } @@ -601,15 +601,15 @@ edit ( * Get a character for Insert mode. Ignore K_IGNORE. */ lastc = c; /* remember previous char for CTRL-D */ - event_enable_deferred(); + input_enable_events(); do { c = safe_vgetc(); } while (c == K_IGNORE); - event_disable_deferred(); + input_disable_events(); if (c == K_EVENT) { c = lastc; - event_process(); + queue_process_events(loop.events); continue; } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 93590445c9..c7c67cfca4 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -20,6 +20,7 @@ #include <stdbool.h> #include <math.h> #include <limits.h> +#include <msgpack.h> #include "nvim/assert.h" #include "nvim/vim.h" @@ -55,6 +56,7 @@ #include "nvim/misc1.h" #include "nvim/misc2.h" #include "nvim/keymap.h" +#include "nvim/map.h" #include "nvim/file_search.h" #include "nvim/garray.h" #include "nvim/move.h" @@ -82,17 +84,20 @@ #include "nvim/version.h" #include "nvim/window.h" #include "nvim/os/os.h" -#include "nvim/os/job.h" -#include "nvim/os/rstream.h" -#include "nvim/os/rstream_defs.h" +#include "nvim/event/uv_process.h" +#include "nvim/event/pty_process.h" +#include "nvim/event/rstream.h" +#include "nvim/event/wstream.h" #include "nvim/os/time.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/msgpack_rpc/server.h" +#include "nvim/msgpack_rpc/helpers.h" #include "nvim/api/private/helpers.h" #include "nvim/api/vim.h" #include "nvim/os/dl.h" -#include "nvim/os/event.h" #include "nvim/os/input.h" +#include "nvim/event/loop.h" +#include "nvim/lib/kvec.h" #define DICT_MAXNEST 100 /* maximum nesting of lists and dicts */ @@ -158,6 +163,13 @@ typedef struct lval_S { char_u *ll_newkey; /* New key for Dict in alloc. mem or NULL. */ } lval_T; +/// Structure defining state for read_from_list() +typedef struct { + 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. +} ListReaderState; + static char *e_letunexp = N_("E18: Unexpected characters in :let"); static char *e_listidx = N_("E684: list index out of range: %" PRId64); @@ -288,7 +300,6 @@ static ufunc_T dumuf; #define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j] #define FUNCLINE(fp, j) ((char_u **)(fp->uf_lines.ga_data))[j] -#define MAX_FUNC_ARGS 20 /* maximum number of function arguments */ #define VAR_SHORT_LEN 20 /* short variable name length */ #define FIXVAR_CNT 12 /* number of fixed variables */ @@ -428,6 +439,7 @@ static struct vimvar { {VV_NAME("progpath", VAR_STRING), VV_RO}, {VV_NAME("command_output", VAR_STRING), 0}, {VV_NAME("completed_item", VAR_DICT), VV_RO}, + {VV_NAME("msgpack_types", VAR_DICT), VV_RO}, }; /* shorthand */ @@ -443,40 +455,103 @@ static dictitem_T vimvars_var; /* variable used for v: */ #define vimvarht vimvardict.dv_hashtab typedef struct { - Job *job; + union { + UvProcess uv; + PtyProcess pty; + } proc; + Stream in, out, err; Terminal *term; + bool stopped; bool exited; - bool stdin_closed; int refcount; ufunc_T *on_stdout, *on_stderr, *on_exit; dict_T *self; int *status_ptr; + uint64_t id; + Queue *events; } TerminalJobData; +/// Structure representing current VimL to messagepack conversion state +typedef struct { + enum { + kMPConvDict, ///< Convert dict_T *dictionary. + kMPConvList, ///< Convert list_T *list. + kMPConvPairs, ///< Convert mapping represented as a list_T* of pairs. + } type; + union { + struct { + const dict_T *dict; ///< Currently converted dictionary. + const hashitem_T *hi; ///< Currently converted dictionary item. + size_t todo; ///< Amount of items left to process. + } d; ///< State of dictionary conversion. + struct { + const list_T *list; ///< Currently converted list. + const listitem_T *li; ///< Currently converted list item. + } l; ///< State of list or generic mapping conversion. + } data; ///< Data to convert. +} MPConvStackVal; + +/// Stack used to convert VimL values to messagepack. +typedef kvec_t(MPConvStackVal) MPConvStack; -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "eval.c.generated.h" -#endif - -#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 */ -// Memory pool for reusing JobEvent structures typedef struct { - int job_id; TerminalJobData *data; ufunc_T *callback; const char *type; list_T *received; int status; } JobEvent; -static int disable_job_defer = 0; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "eval.c.generated.h" +#endif + +#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 uint64_t current_job_id = 1; +static PMap(uint64_t) *jobs = NULL; + +typedef enum { + kMPNil, + kMPBoolean, + kMPInteger, + kMPFloat, + kMPString, + kMPBinary, + kMPArray, + kMPMap, + kMPExt, +} MessagePackType; +static const char *const msgpack_type_names[] = { + [kMPNil] = "nil", + [kMPBoolean] = "boolean", + [kMPInteger] = "integer", + [kMPFloat] = "float", + [kMPString] = "string", + [kMPBinary] = "binary", + [kMPArray] = "array", + [kMPMap] = "map", + [kMPExt] = "ext", +}; +static const list_T *msgpack_type_lists[] = { + [kMPNil] = NULL, + [kMPBoolean] = NULL, + [kMPInteger] = NULL, + [kMPFloat] = NULL, + [kMPString] = NULL, + [kMPBinary] = NULL, + [kMPArray] = NULL, + [kMPMap] = NULL, + [kMPExt] = NULL, +}; /* * Initialize the global and v: variables. */ void eval_init(void) { + jobs = pmap_new(uint64_t)(); int i; struct vimvar *p; @@ -503,6 +578,27 @@ void eval_init(void) /* add to compat scope dict */ hash_add(&compat_hashtab, p->vv_di.di_key); } + + dict_T *const msgpack_types_dict = dict_alloc(); + for (size_t i = 0; i < ARRAY_SIZE(msgpack_type_names); i++) { + list_T *const type_list = list_alloc(); + type_list->lv_lock = VAR_FIXED; + type_list->lv_refcount = 1; + dictitem_T *const di = dictitem_alloc((char_u *) msgpack_type_names[i]); + di->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; + di->di_tv = (typval_T) { + .v_type = VAR_LIST, + .vval = { .v_list = type_list, }, + }; + msgpack_type_lists[i] = type_list; + if (dict_add(msgpack_types_dict, di) == FAIL) { + // There must not be duplicate items in this dictionary by definition. + assert(false); + } + } + msgpack_types_dict->dv_lock = VAR_FIXED; + + set_vim_var_dict(VV_MSGPACK_TYPES, msgpack_types_dict); set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc()); set_vim_var_nr(VV_SEARCHFORWARD, 1L); set_vim_var_nr(VV_HLSEARCH, 1L); @@ -5148,24 +5244,40 @@ void list_append_dict(list_T *list, dict_T *dict) ++dict->dv_refcount; } -/* - * Make a copy of "str" and append it as an item to list "l". - * When "len" >= 0 use "str[len]". - */ -void list_append_string(list_T *l, char_u *str, int len) +/// Make a copy of "str" and append it as an item to list "l" +/// +/// @param[out] l List to append to. +/// @param[in] str String to append. +/// @param[in] len Length of the appended string. May be negative, in this +/// case string is considered to be usual zero-terminated +/// string. +void list_append_string(list_T *l, const char_u *str, int len) + FUNC_ATTR_NONNULL_ARG(1) +{ + if (str == NULL) { + list_append_allocated_string(l, NULL); + } else { + list_append_allocated_string(l, (len >= 0 + ? xmemdupz((char *) str, len) + : xstrdup((char *) str))); + } +} + +/// Append given string to the list +/// +/// Unlike list_append_string this function does not copy the string. +/// +/// @param[out] l List to append to. +/// @param[in] str String to append. +void list_append_allocated_string(list_T *l, char *const str) + FUNC_ATTR_NONNULL_ARG(1) { listitem_T *li = listitem_alloc(); list_append(l, li); li->li_tv.v_type = VAR_STRING; li->li_tv.v_lock = 0; - - if (str == NULL) { - li->li_tv.vval.v_string = NULL; - } else { - li->li_tv.vval.v_string = (len >= 0) ? vim_strnsave(str, len) - : vim_strsave(str); - } + li->li_tv.vval.v_string = (char_u *) str; } /* @@ -6512,6 +6624,7 @@ static struct fst { {"getwinposy", 0, 0, f_getwinposy}, {"getwinvar", 2, 3, f_getwinvar}, {"glob", 1, 3, f_glob}, + {"glob2regpat", 1, 1, f_glob2regpat}, {"globpath", 2, 4, f_globpath}, {"has", 1, 1, f_has}, {"has_key", 2, 2, f_has_key}, @@ -6573,6 +6686,8 @@ static struct fst { {"min", 1, 1, f_min}, {"mkdir", 1, 3, f_mkdir}, {"mode", 0, 1, f_mode}, + {"msgpackdump", 1, 1, f_msgpackdump}, + {"msgpackparse", 1, 1, f_msgpackparse}, {"nextnonblank", 1, 1, f_nextnonblank}, {"nr2char", 1, 2, f_nr2char}, {"or", 2, 2, f_or}, @@ -6857,7 +6972,7 @@ get_func_tv ( * Return FAIL when the function can't be called, OK otherwise. * Also returns OK when an error was encountered while executing the function. */ -static int +int call_func ( char_u *funcname, /* name of the function */ int len, /* length of "name" */ @@ -7078,7 +7193,7 @@ static int non_zero_arg(typval_T *argvars) * Get the float value of "argvars[0]" into "f". * Returns FAIL when the argument is not a Number or Float. */ -static int get_float_arg(typval_T *argvars, float_T *f) +static inline int get_float_arg(typval_T *argvars, float_T *f) { if (argvars[0].v_type == VAR_FLOAT) { *f = argvars[0].vval.v_float; @@ -7092,14 +7207,34 @@ static int get_float_arg(typval_T *argvars, float_T *f) return FAIL; } +// Apply a floating point C function on a typval with one float_T. +// +// Some versions of glibc on i386 have an optimization that makes it harder to +// call math functions indirectly from inside an inlined function, causing +// compile-time errors. Avoid `inline` in that case. #3072 +#ifndef ARCH_32 +inline +#endif +static void float_op_wrapper(typval_T *argvars, typval_T *rettv, + float_T (*function)(float_T)) +{ + float_T f; + + rettv->v_type = VAR_FLOAT; + if (get_float_arg(argvars, &f) == OK) { + rettv->vval.v_float = function(f); + } else { + rettv->vval.v_float = 0.0; + } +} + /* * "abs(expr)" function */ static void f_abs(typval_T *argvars, typval_T *rettv) { if (argvars[0].v_type == VAR_FLOAT) { - rettv->v_type = VAR_FLOAT; - rettv->vval.v_float = fabs(argvars[0].vval.v_float); + float_op_wrapper(argvars, rettv, &fabs); } else { varnumber_T n; int error = FALSE; @@ -7119,13 +7254,7 @@ static void f_abs(typval_T *argvars, typval_T *rettv) */ static void f_acos(typval_T *argvars, typval_T *rettv) { - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = acos(f); - else - rettv->vval.v_float = 0.0; + float_op_wrapper(argvars, rettv, &acos); } /* @@ -7279,13 +7408,7 @@ static void f_argv(typval_T *argvars, typval_T *rettv) */ static void f_asin(typval_T *argvars, typval_T *rettv) { - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = asin(f); - else - rettv->vval.v_float = 0.0; + float_op_wrapper(argvars, rettv, &asin); } /* @@ -7293,13 +7416,7 @@ static void f_asin(typval_T *argvars, typval_T *rettv) */ static void f_atan(typval_T *argvars, typval_T *rettv) { - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = atan(f); - else - rettv->vval.v_float = 0.0; + float_op_wrapper(argvars, rettv, &atan); } /* @@ -7628,13 +7745,7 @@ static void f_call(typval_T *argvars, typval_T *rettv) */ static void f_ceil(typval_T *argvars, typval_T *rettv) { - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = ceil(f); - else - rettv->vval.v_float = 0.0; + float_op_wrapper(argvars, rettv, &ceil); } /* @@ -7838,13 +7949,7 @@ static void f_copy(typval_T *argvars, typval_T *rettv) */ static void f_cos(typval_T *argvars, typval_T *rettv) { - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = cos(f); - else - rettv->vval.v_float = 0.0; + float_op_wrapper(argvars, rettv, &cos); } /* @@ -7852,13 +7957,7 @@ static void f_cos(typval_T *argvars, typval_T *rettv) */ static void f_cosh(typval_T *argvars, typval_T *rettv) { - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = cosh(f); - else - rettv->vval.v_float = 0.0; + float_op_wrapper(argvars, rettv, &cosh); } /* @@ -8257,13 +8356,7 @@ static void f_exists(typval_T *argvars, typval_T *rettv) */ static void f_exp(typval_T *argvars, typval_T *rettv) { - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = exp(f); - else - rettv->vval.v_float = 0.0; + float_op_wrapper(argvars, rettv, &exp); } /* @@ -8742,13 +8835,7 @@ static void f_float2nr(typval_T *argvars, typval_T *rettv) */ static void f_floor(typval_T *argvars, typval_T *rettv) { - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = floor(f); - else - rettv->vval.v_float = 0.0; + float_op_wrapper(argvars, rettv, &floor); } /* @@ -9894,6 +9981,17 @@ static void f_globpath(typval_T *argvars, typval_T *rettv) } /* + * "glob2regpat()" function + */ +static void f_glob2regpat(typval_T *argvars, typval_T *rettv) +{ + char_u *pat = get_tv_string_chk(&argvars[0]); + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = file_pat_to_reg_pat(pat, NULL, NULL, FALSE); +} + +/* * "has()" function */ static void f_has(typval_T *argvars, typval_T *rettv) @@ -9915,9 +10013,7 @@ static void f_has(typval_T *argvars, typval_T *rettv) #endif "arabic", "autocmd", -#if defined(FEAT_BROWSE) && (defined(USE_FILE_CHOOSER) \ - || defined(FEAT_GUI_W32) \ - || defined(FEAT_GUI_MOTIF)) +#ifdef FEAT_BROWSE "browsefilter", #endif "byte_offset", @@ -10681,29 +10777,27 @@ static void f_jobclose(typval_T *argvars, typval_T *rettv) return; } - Job *job = job_find(argvars[0].vval.v_number); - - if (!is_user_job(job)) { - // Invalid job id + 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")) { - job_close_in(job); - ((TerminalJobData *)job_data(job))->stdin_closed = true; + process_close_in(proc); } else if (!strcmp(stream, "stdout")) { - job_close_out(job); + process_close_out(proc); } else if (!strcmp(stream, "stderr")) { - job_close_err(job); + process_close_err(proc); } else { EMSG2(_("Invalid job stream \"%s\""), stream); } } else { - ((TerminalJobData *)job_data(job))->stdin_closed = true; - job_close_streams(job); + process_close_streams(proc); } } @@ -10724,15 +10818,13 @@ static void f_jobsend(typval_T *argvars, typval_T *rettv) return; } - Job *job = job_find(argvars[0].vval.v_number); - - if (!is_user_job(job)) { - // Invalid job id + TerminalJobData *data = find_job(argvars[0].vval.v_number); + if (!data) { EMSG(_(e_invjob)); return; } - if (((TerminalJobData *)job_data(job))->stdin_closed) { + if (((Process *)&data->proc)->in->closed) { EMSG(_("Can't send data to the job: stdin is closed")); return; } @@ -10746,7 +10838,7 @@ static void f_jobsend(typval_T *argvars, typval_T *rettv) } WBuffer *buf = wstream_new_buffer(input, input_len, 1, xfree); - rettv->vval.v_number = job_write(job, buf); + rettv->vval.v_number = wstream_write(data->proc.uv.process.in, buf); } // "jobresize(job, width, height)" function @@ -10766,19 +10858,20 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv) return; } - Job *job = job_find(argvars[0].vval.v_number); - if (!is_user_job(job)) { - // Probably an invalid job id + TerminalJobData *data = find_job(argvars[0].vval.v_number); + if (!data) { EMSG(_(e_invjob)); return; } - if (!job_resize(job, argvars[1].vval.v_number, argvars[2].vval.v_number)) { + if (data->proc.uv.process.type != kProcessTypePty) { EMSG(_(e_jobnotpty)); return; } + pty_process_resize(&data->proc.pty, argvars[1].vval.v_number, + argvars[2].vval.v_number); rettv->vval.v_number = 1; } @@ -10867,37 +10960,33 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv) } } - JobOptions opts = common_job_options(argv, on_stdout, on_stderr, on_exit, - job_opts); - - if (!job_opts) { - goto start; - } + bool pty = job_opts && get_dict_number(job_opts, (uint8_t *)"pty") != 0; + TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit, + job_opts, pty); + Process *proc = (Process *)&data->proc; - opts.pty = get_dict_number(job_opts, (uint8_t *)"pty"); - if (opts.pty) { + if (pty) { uint16_t width = get_dict_number(job_opts, (uint8_t *)"width"); if (width > 0) { - opts.width = width; + data->proc.pty.width = width; } uint16_t height = get_dict_number(job_opts, (uint8_t *)"height"); if (height > 0) { - opts.height = height; + data->proc.pty.height = height; } char *term = (char *)get_dict_string(job_opts, (uint8_t *)"TERM", true); if (term) { - opts.term_name = term; + data->proc.pty.term_name = term; } } -start: if (!on_stdout) { - opts.stdout_cb = NULL; + proc->out = NULL; } if (!on_stderr) { - opts.stderr_cb = NULL; + proc->err = NULL; } - common_job_start(opts, rettv); + common_job_start(data, rettv); } // "jobstop()" function @@ -10916,14 +11005,15 @@ static void f_jobstop(typval_T *argvars, typval_T *rettv) return; } - Job *job = job_find(argvars[0].vval.v_number); - if (!is_user_job(job)) { + TerminalJobData *data = find_job(argvars[0].vval.v_number); + if (!data) { EMSG(_(e_invjob)); return; } - job_stop(job); + process_stop((Process *)&data->proc); + data->stopped = true; rettv->vval.v_number = 1; } @@ -10947,30 +11037,24 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv) list_T *rv = list_alloc(); ui_busy_start(); - // disable breakchecks, which could result in job callbacks being executed - // at unexpected places - disable_breakcheck++; - // disable job event deferring so the callbacks are processed while waiting. - if (!disable_job_defer++) { - // process any pending job events in the deferred queue, but only do this if - // deferred is not disabled(at the top-level `jobwait()` call) - event_process(); - } + Queue *waiting_jobs = queue_new_parent(loop_on_put, &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) { - Job *job = NULL; + TerminalJobData *data = NULL; if (arg->li_tv.v_type != VAR_NUMBER - || !(job = job_find(arg->li_tv.vval.v_number)) - || !is_user_job(job)) { + || !(data = find_job(arg->li_tv.vval.v_number))) { list_append_number(rv, -3); } else { - TerminalJobData *data = job_data(job); // append the list item and set the status pointer so we'll collect the // status code when the job exits 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 + queue_process_events(data->events); + queue_replace_parent(data->events, waiting_jobs); } } @@ -10982,18 +11066,16 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv) } for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { - Job *job = NULL; + TerminalJobData *data = NULL; if (remaining == 0) { // timed out break; } if (arg->li_tv.v_type != VAR_NUMBER - || !(job = job_find(arg->li_tv.vval.v_number)) - || !is_user_job(job)) { + || !(data = find_job(arg->li_tv.vval.v_number))) { continue; } - TerminalJobData *data = job_data(job); - int status = job_wait(job, remaining); + int status = process_wait((Process *)&data->proc, remaining, waiting_jobs); if (status < 0) { // interrupted or timed out, skip remaining jobs. if (status == -2) { @@ -11013,25 +11095,31 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv) } } - // poll to ensure any pending callbacks from the last job are invoked - event_poll(0); - for (listitem_T *arg = args->lv_first; arg != NULL; arg = arg->li_next) { - Job *job = NULL; + TerminalJobData *data = NULL; if (arg->li_tv.v_type != VAR_NUMBER - || !(job = job_find(arg->li_tv.vval.v_number)) - || !is_user_job(job)) { + || !(data = find_job(arg->li_tv.vval.v_number))) { continue; } - TerminalJobData *data = job_data(job); // remove the status pointer because the list may be freed before the // job exits data->status_ptr = NULL; } - disable_job_defer--; - disable_breakcheck--; - ui_busy_stop(); + // 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))) { + continue; + } + // restore the parent queue for the job + queue_process_events(data->events); + queue_replace_parent(data->events, loop.events); + } + + queue_free(waiting_jobs); + ui_busy_stop(); rv->lv_refcount++; rettv->v_type = VAR_LIST; rettv->vval.v_list = rv; @@ -11306,13 +11394,7 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) */ static void f_log(typval_T *argvars, typval_T *rettv) { - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = log(f); - else - rettv->vval.v_float = 0.0; + float_op_wrapper(argvars, rettv, &log); } /* @@ -11320,13 +11402,7 @@ static void f_log(typval_T *argvars, typval_T *rettv) */ static void f_log10(typval_T *argvars, typval_T *rettv) { - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = log10(f); - else - rettv->vval.v_float = 0.0; + float_op_wrapper(argvars, rettv, &log10); } @@ -11718,33 +11794,6 @@ static void f_min(typval_T *argvars, typval_T *rettv) max_min(argvars, rettv, FALSE); } - -/* - * Create the directory in which "dir" is located, and higher levels when - * needed. - */ -static int mkdir_recurse(char_u *dir, int prot) -{ - char_u *p; - char_u *updir; - int r = FAIL; - - /* Get end of directory name in "dir". - * We're done when it's "/" or "c:/". */ - p = path_tail_with_sep(dir); - if (p <= get_past_head(dir)) - return OK; - - /* If the directory exists we're done. Otherwise: create it.*/ - updir = vim_strnsave(dir, (int)(p - dir)); - if (os_isdir(updir)) - r = OK; - else if (mkdir_recurse(updir, prot) == OK) - r = vim_mkdir_emsg(updir, prot); - xfree(updir); - return r; -} - /* * "mkdir()" function */ @@ -11769,8 +11818,19 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv) if (argvars[1].v_type != VAR_UNKNOWN) { if (argvars[2].v_type != VAR_UNKNOWN) prot = get_tv_number_chk(&argvars[2], NULL); - if (prot != -1 && STRCMP(get_tv_string(&argvars[1]), "p") == 0) - mkdir_recurse(dir, prot); + if (prot != -1 && STRCMP(get_tv_string(&argvars[1]), "p") == 0) { + char *failed_dir; + int ret = os_mkdir_recurse((char *) dir, prot, &failed_dir); + if (ret != 0) { + EMSG3(_(e_mkdir), failed_dir, os_strerror(ret)); + xfree(failed_dir); + rettv->vval.v_number = FAIL; + return; + } else { + rettv->vval.v_number = OK; + return; + } + } } rettv->vval.v_number = prot == -1 ? FAIL : vim_mkdir_emsg(dir, prot); } @@ -11832,6 +11892,827 @@ static void f_mode(typval_T *argvars, typval_T *rettv) rettv->v_type = VAR_STRING; } +/// Msgpack callback for writing to readfile()-style list +static int msgpack_list_write(void *data, const char *buf, size_t len) +{ + if (len == 0) { + return 0; + } + list_T *const list = (list_T *) data; + const char *const end = buf + len; + const char *line_end = buf; + if (list->lv_last == NULL) { + list_append_string(list, NULL, 0); + } + listitem_T *li = list->lv_last; + do { + const char *line_start = line_end; + line_end = xmemscan(line_start, NL, end - line_start); + if (line_end == line_start) { + list_append_allocated_string(list, NULL); + } else { + const size_t line_length = line_end - line_start; + char *str; + if (li == NULL) { + str = xmemdupz(line_start, line_length); + } else { + const size_t li_len = (li->li_tv.vval.v_string == NULL + ? 0 + : STRLEN(li->li_tv.vval.v_string)); + li->li_tv.vval.v_string = xrealloc(li->li_tv.vval.v_string, + li_len + line_length + 1); + str = (char *) li->li_tv.vval.v_string + li_len; + memmove(str, line_start, line_length); + str[line_length] = 0; + } + for (size_t i = 0; i < line_length; i++) { + if (str[i] == NUL) { + str[i] = NL; + } + } + if (li == NULL) { + list_append_allocated_string(list, str); + } else { + li = NULL; + } + if (line_end == end - 1) { + list_append_allocated_string(list, NULL); + } + } + line_end++; + } while (line_end < end); + return 0; +} + +/// Convert readfile()-style list to a char * buffer with length +/// +/// @param[in] list Converted list. +/// @param[out] ret_len Resulting buffer length. +/// @param[out] ret_buf Allocated buffer with the result or NULL if ret_len is +/// zero. +/// +/// @return true in case of success, false in case of failure. +static inline bool vim_list_to_buf(const list_T *const list, + size_t *const ret_len, char **const ret_buf) + 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); + } + } + if (len) { + len--; + } + } + *ret_len = len; + if (len == 0) { + *ret_buf = NULL; + return true; + } + ListReaderState lrstate = init_lrstate(list); + char *const buf = xmalloc(len); + size_t read_bytes; + if (read_from_list(&lrstate, buf, len, &read_bytes) != OK) { + assert(false); + } + assert(len == read_bytes); + *ret_buf = buf; + return true; +} + +/// Convert one VimL value to msgpack +/// +/// @param packer Messagepack packer. +/// @param[out] mpstack Stack with values to convert. Only used for pushing +/// values to it. +/// @param[in] tv Converted value. +/// +/// @return OK in case of success, FAIL otherwise. +static int convert_one_value(msgpack_packer *const packer, + MPConvStack *const mpstack, + const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + switch (tv->v_type) { +#define CHECK_SELF_REFERENCE(conv_type, vval_name, ptr) \ + do { \ + for (size_t i = 0; i < kv_size(*mpstack); i++) { \ + if (kv_A(*mpstack, i).type == conv_type \ + && kv_A(*mpstack, i).data.vval_name == ptr) { \ + EMSG2(_(e_invarg2), "container references itself"); \ + return FAIL; \ + } \ + } \ + } while (0) + case VAR_STRING: { + if (tv->vval.v_string == NULL) { + msgpack_pack_bin(packer, 0); + } else { + const size_t len = STRLEN(tv->vval.v_string); + msgpack_pack_bin(packer, len); + msgpack_pack_bin_body(packer, tv->vval.v_string, len); + } + break; + } + case VAR_NUMBER: { + msgpack_pack_int64(packer, (int64_t) tv->vval.v_number); + break; + } + case VAR_FLOAT: { + msgpack_pack_double(packer, (double) tv->vval.v_float); + break; + } + case VAR_FUNC: { + EMSG2(_(e_invarg2), "attempt to dump function reference"); + return FAIL; + } + case VAR_LIST: { + if (tv->vval.v_list == NULL) { + msgpack_pack_array(packer, 0); + break; + } + CHECK_SELF_REFERENCE(kMPConvList, l.list, tv->vval.v_list); + msgpack_pack_array(packer, tv->vval.v_list->lv_len); + kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { + .type = kMPConvList, + .data = { + .l = { + .list = tv->vval.v_list, + .li = tv->vval.v_list->lv_first, + }, + }, + })); + break; + } + case VAR_DICT: { + if (tv->vval.v_dict == NULL) { + msgpack_pack_map(packer, 0); + break; + } + const dictitem_T *type_di; + const dictitem_T *val_di; + if (tv->vval.v_dict->dv_hashtab.ht_used == 2 + && (type_di = dict_find((dict_T *) tv->vval.v_dict, + (char_u *) "_TYPE", -1)) != NULL + && type_di->di_tv.v_type == VAR_LIST + && (val_di = dict_find((dict_T *) tv->vval.v_dict, + (char_u *) "_VAL", -1)) != NULL) { + size_t i; + for (i = 0; i < ARRAY_SIZE(msgpack_type_lists); i++) { + if (type_di->di_tv.vval.v_list == msgpack_type_lists[i]) { + break; + } + } + if (i == ARRAY_SIZE(msgpack_type_lists)) { + goto vim_to_msgpack_regural_dict; + } + switch ((MessagePackType) i) { + case kMPNil: { + msgpack_pack_nil(packer); + break; + } + case kMPBoolean: { + if (val_di->di_tv.v_type != VAR_NUMBER) { + goto vim_to_msgpack_regural_dict; + } + if (val_di->di_tv.vval.v_number) { + msgpack_pack_true(packer); + } else { + msgpack_pack_false(packer); + } + break; + } + case kMPInteger: { + const list_T *val_list; + varnumber_T sign; + varnumber_T highest_bits; + varnumber_T high_bits; + varnumber_T low_bits; + // List of 4 integers; first is signed (should be 1 or -1, but this + // is not checked), second is unsigned and have at most one (sign is + // -1) or two (sign is 1) non-zero bits (number of 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) { + goto vim_to_msgpack_regural_dict; + } + uint64_t number = ((uint64_t) (((uint64_t) highest_bits) << 62) + | (uint64_t) (((uint64_t) high_bits) << 31) + | (uint64_t) low_bits); + if (sign > 0) { + msgpack_pack_uint64(packer, number); + } else { + msgpack_pack_int64(packer, (int64_t) (-number)); + } + break; + } + case kMPFloat: { + if (val_di->di_tv.v_type != VAR_FLOAT) { + goto vim_to_msgpack_regural_dict; + } + msgpack_pack_double(packer, val_di->di_tv.vval.v_float); + break; + } + case kMPString: + case kMPBinary: { + const bool is_string = ((MessagePackType) i == kMPString); + if (val_di->di_tv.v_type != VAR_LIST) { + goto vim_to_msgpack_regural_dict; + } + size_t len; + char *buf; + if (!vim_list_to_buf(val_di->di_tv.vval.v_list, &len, &buf)) { + goto vim_to_msgpack_regural_dict; + } + if (is_string) { + msgpack_pack_str(packer, len); + } else { + msgpack_pack_bin(packer, len); + } + if (len == 0) { + break; + } + if (is_string) { + msgpack_pack_str_body(packer, buf, len); + } else { + msgpack_pack_bin_body(packer, buf, len); + } + xfree(buf); + break; + } + case kMPArray: { + if (val_di->di_tv.v_type != VAR_LIST) { + goto vim_to_msgpack_regural_dict; + } + CHECK_SELF_REFERENCE(kMPConvList, l.list, + val_di->di_tv.vval.v_list); + msgpack_pack_array(packer, val_di->di_tv.vval.v_list->lv_len); + kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { + .type = kMPConvList, + .data = { + .l = { + .list = val_di->di_tv.vval.v_list, + .li = val_di->di_tv.vval.v_list->lv_first, + }, + }, + })); + break; + } + case kMPMap: { + if (val_di->di_tv.v_type != VAR_LIST) { + goto vim_to_msgpack_regural_dict; + } + if (val_di->di_tv.vval.v_list == NULL) { + msgpack_pack_map(packer, 0); + break; + } + const list_T *const val_list = val_di->di_tv.vval.v_list; + 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) { + goto vim_to_msgpack_regural_dict; + } + } + CHECK_SELF_REFERENCE(kMPConvPairs, l.list, val_list); + msgpack_pack_map(packer, val_list->lv_len); + kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { + .type = kMPConvPairs, + .data = { + .l = { + .list = val_list, + .li = val_list->lv_first, + }, + }, + })); + break; + } + case kMPExt: { + 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 + || type < INT8_MIN + || (val_list->lv_last->li_tv.v_type != VAR_LIST)) { + goto vim_to_msgpack_regural_dict; + } + size_t len; + char *buf; + if (!vim_list_to_buf(val_list->lv_last->li_tv.vval.v_list, + &len, &buf)) { + goto vim_to_msgpack_regural_dict; + } + msgpack_pack_ext(packer, len, (int8_t) type); + msgpack_pack_ext_body(packer, buf, len); + xfree(buf); + break; + } + } + break; + } +vim_to_msgpack_regural_dict: + CHECK_SELF_REFERENCE(kMPConvDict, d.dict, tv->vval.v_dict); + msgpack_pack_map(packer, tv->vval.v_dict->dv_hashtab.ht_used); + kv_push(MPConvStackVal, *mpstack, ((MPConvStackVal) { + .type = kMPConvDict, + .data = { + .d = { + .dict = tv->vval.v_dict, + .hi = tv->vval.v_dict->dv_hashtab.ht_array, + .todo = tv->vval.v_dict->dv_hashtab.ht_used, + }, + }, + })); + break; + } + } +#undef CHECK_SELF_REFERENCE + return OK; +} + +/// Convert typval_T to messagepack +static int vim_to_msgpack(msgpack_packer *const packer, + const typval_T *const tv) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + MPConvStack mpstack; + kv_init(mpstack); + if (convert_one_value(packer, &mpstack, tv) == FAIL) { + goto vim_to_msgpack_error_ret; + } + while (kv_size(mpstack)) { + MPConvStackVal *cur_mpsv = &kv_A(mpstack, kv_size(mpstack) - 1); + const typval_T *cur_tv = NULL; + switch (cur_mpsv->type) { + case kMPConvDict: { + if (!cur_mpsv->data.d.todo) { + (void) kv_pop(mpstack); + continue; + } + while (HASHITEM_EMPTY(cur_mpsv->data.d.hi)) { + cur_mpsv->data.d.hi++; + } + const dictitem_T *const di = HI2DI(cur_mpsv->data.d.hi); + cur_mpsv->data.d.todo--; + cur_mpsv->data.d.hi++; + const size_t key_len = STRLEN(&di->di_key[0]); + msgpack_pack_str(packer, key_len); + msgpack_pack_str_body(packer, &di->di_key[0], key_len); + cur_tv = &di->di_tv; + break; + } + case kMPConvList: { + if (cur_mpsv->data.l.li == NULL) { + (void) kv_pop(mpstack); + continue; + } + cur_tv = &cur_mpsv->data.l.li->li_tv; + cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; + break; + } + case kMPConvPairs: { + if (cur_mpsv->data.l.li == NULL) { + (void) kv_pop(mpstack); + continue; + } + const list_T *const kv_pair = cur_mpsv->data.l.li->li_tv.vval.v_list; + if (convert_one_value(packer, &mpstack, &kv_pair->lv_first->li_tv) + == FAIL) { + goto vim_to_msgpack_error_ret; + } + cur_tv = &kv_pair->lv_last->li_tv; + cur_mpsv->data.l.li = cur_mpsv->data.l.li->li_next; + break; + } + } + if (convert_one_value(packer, &mpstack, cur_tv) == FAIL) { + goto vim_to_msgpack_error_ret; + } + } + kv_destroy(mpstack); + return OK; +vim_to_msgpack_error_ret: + kv_destroy(mpstack); + return FAIL; +} + +/// "msgpackdump()" function +static void f_msgpackdump(typval_T *argvars, typval_T *rettv) + FUNC_ATTR_NONNULL_ALL +{ + if (argvars[0].v_type != VAR_LIST) { + EMSG2(_(e_listarg), "msgpackdump()"); + return; + } + list_T *ret_list = rettv_list_alloc(rettv); + const list_T *list = argvars[0].vval.v_list; + if (list == NULL) { + return; + } + msgpack_packer *lpacker = msgpack_packer_new(ret_list, &msgpack_list_write); + for (const listitem_T *li = list->lv_first; li != NULL; li = li->li_next) { + if (vim_to_msgpack(lpacker, &li->li_tv) == FAIL) { + break; + } + } + msgpack_packer_free(lpacker); +} + +/// Read bytes from list +/// +/// @param[in,out] state Structure describing position in list from which +/// reading should start. Is updated to reflect position +/// at which reading ended. +/// @param[out] buf Buffer to write to. +/// @param[in] nbuf Buffer length. +/// @param[out] read_bytes Is set to amount of bytes read. +/// +/// @return OK when reading was finished, FAIL in case of error (i.e. list item +/// was not a string), NOTDONE if reading was successfull, but there are +/// more bytes to read. +static int read_from_list(ListReaderState *const state, char *const buf, + const size_t nbuf, size_t *const read_bytes) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + char *const buf_end = buf + nbuf; + char *p = buf; + while (p < buf_end) { + for (size_t i = state->offset; i < state->li_length && p < buf_end; i++) { + const char ch = state->li->li_tv.vval.v_string[state->offset++]; + *p++ = (ch == NL ? NUL : ch); + } + if (p < buf_end) { + state->li = state->li->li_next; + 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); + return FAIL; + } + state->offset = 0; + state->li_length = (state->li->li_tv.vval.v_string == NULL + ? 0 + : STRLEN(state->li->li_tv.vval.v_string)); + } + } + *read_bytes = nbuf; + return (state->offset < state->li_length || state->li->li_next != NULL + ? NOTDONE + : OK); +} + +/// Initialize ListReaderState structure +static inline ListReaderState init_lrstate(const list_T *const list) + FUNC_ATTR_NONNULL_ALL +{ + return (ListReaderState) { + .li = list->lv_first, + .offset = 0, + .li_length = (list->lv_first->li_tv.vval.v_string == NULL + ? 0 + : STRLEN(list->lv_first->li_tv.vval.v_string)), + }; +} + +/// Convert msgpack object to a VimL one +static int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ +#define INIT_SPECIAL_DICT(tv, type, val) \ + do { \ + dict_T *const dict = dict_alloc(); \ + dictitem_T *const type_di = dictitem_alloc((char_u *) "_TYPE"); \ + type_di->di_tv.v_type = VAR_LIST; \ + type_di->di_tv.v_lock = 0; \ + type_di->di_tv.vval.v_list = (list_T *) msgpack_type_lists[type]; \ + type_di->di_tv.vval.v_list->lv_refcount++; \ + dict_add(dict, type_di); \ + dictitem_T *const val_di = dictitem_alloc((char_u *) "_VAL"); \ + val_di->di_tv = val; \ + dict_add(dict, val_di); \ + tv->v_type = VAR_DICT; \ + dict->dv_refcount++; \ + tv->vval.v_dict = dict; \ + } while (0) + switch (mobj.type) { + case MSGPACK_OBJECT_NIL: { + INIT_SPECIAL_DICT(rettv, kMPNil, ((typval_T) { + .v_type = VAR_NUMBER, + .v_lock = 0, + .vval = { .v_number = 0 }, + })); + break; + } + case MSGPACK_OBJECT_BOOLEAN: { + INIT_SPECIAL_DICT(rettv, kMPBoolean, + ((typval_T) { + .v_type = VAR_NUMBER, + .v_lock = 0, + .vval = { + .v_number = (varnumber_T) mobj.via.boolean, + }, + })); + break; + } + case MSGPACK_OBJECT_POSITIVE_INTEGER: { + if (mobj.via.u64 <= VARNUMBER_MAX) { + *rettv = (typval_T) { + .v_type = VAR_NUMBER, + .v_lock = 0, + .vval = { .v_number = (varnumber_T) mobj.via.u64 }, + }; + } else { + list_T *const list = list_alloc(); + list->lv_refcount++; + INIT_SPECIAL_DICT(rettv, kMPInteger, + ((typval_T) { + .v_type = VAR_LIST, + .v_lock = 0, + .vval = { .v_list = list }, + })); + uint64_t n = mobj.via.u64; + list_append_number(list, 1); + list_append_number(list, (varnumber_T) ((n >> 62) & 0x3)); + list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF)); + list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF)); + } + break; + } + case MSGPACK_OBJECT_NEGATIVE_INTEGER: { + if (mobj.via.i64 >= VARNUMBER_MIN) { + *rettv = (typval_T) { + .v_type = VAR_NUMBER, + .v_lock = 0, + .vval = { .v_number = (varnumber_T) mobj.via.i64 }, + }; + } else { + list_T *const list = list_alloc(); + list->lv_refcount++; + INIT_SPECIAL_DICT(rettv, kMPInteger, + ((typval_T) { + .v_type = VAR_LIST, + .v_lock = 0, + .vval = { .v_list = list }, + })); + uint64_t n = -((uint64_t) mobj.via.i64); + list_append_number(list, -1); + list_append_number(list, (varnumber_T) ((n >> 62) & 0x3)); + list_append_number(list, (varnumber_T) ((n >> 31) & 0x7FFFFFFF)); + list_append_number(list, (varnumber_T) (n & 0x7FFFFFFF)); + } + break; + } + case MSGPACK_OBJECT_FLOAT: { + *rettv = (typval_T) { + .v_type = VAR_FLOAT, + .v_lock = 0, + .vval = { .v_float = mobj.via.f64 }, + }; + break; + } + case MSGPACK_OBJECT_STR: { + list_T *const list = list_alloc(); + list->lv_refcount++; + INIT_SPECIAL_DICT(rettv, kMPString, + ((typval_T) { + .v_type = VAR_LIST, + .v_lock = 0, + .vval = { .v_list = list }, + })); + if (msgpack_list_write((void *) list, mobj.via.str.ptr, mobj.via.str.size) + == -1) { + return FAIL; + } + break; + } + case MSGPACK_OBJECT_BIN: { + if (memchr(mobj.via.bin.ptr, NUL, mobj.via.bin.size) == NULL) { + *rettv = (typval_T) { + .v_type = VAR_STRING, + .v_lock = 0, + .vval = { .v_string = xmemdupz(mobj.via.bin.ptr, mobj.via.bin.size) }, + }; + break; + } + list_T *const list = list_alloc(); + list->lv_refcount++; + INIT_SPECIAL_DICT(rettv, kMPBinary, + ((typval_T) { + .v_type = VAR_LIST, + .v_lock = 0, + .vval = { .v_list = list }, + })); + if (msgpack_list_write((void *) list, mobj.via.bin.ptr, mobj.via.bin.size) + == -1) { + return FAIL; + } + break; + } + case MSGPACK_OBJECT_ARRAY: { + list_T *const list = list_alloc(); + list->lv_refcount++; + *rettv = (typval_T) { + .v_type = VAR_LIST, + .v_lock = 0, + .vval = { .v_list = list }, + }; + for (size_t i = 0; i < mobj.via.array.size; i++) { + listitem_T *const li = listitem_alloc(); + li->li_tv.v_type = VAR_UNKNOWN; + list_append(list, li); + if (msgpack_to_vim(mobj.via.array.ptr[i], &li->li_tv) == FAIL) { + return FAIL; + } + } + break; + } + case MSGPACK_OBJECT_MAP: { + for (size_t i = 0; i < mobj.via.map.size; i++) { + if (mobj.via.map.ptr[i].key.type != MSGPACK_OBJECT_STR + || mobj.via.map.ptr[i].key.via.str.size == 0 + || memchr(mobj.via.map.ptr[i].key.via.str.ptr, NUL, + mobj.via.map.ptr[i].key.via.str.size) != NULL) { + goto msgpack_to_vim_generic_map; + } + } + dict_T *const dict = dict_alloc(); + dict->dv_refcount++; + *rettv = (typval_T) { + .v_type = VAR_DICT, + .v_lock = 0, + .vval = { .v_dict = dict }, + }; + for (size_t i = 0; i < mobj.via.map.size; i++) { + dictitem_T *const di = xmallocz(offsetof(dictitem_T, di_key) + + mobj.via.map.ptr[i].key.via.str.size); + memcpy(&di->di_key[0], mobj.via.map.ptr[i].key.via.str.ptr, + mobj.via.map.ptr[i].key.via.str.size); + di->di_tv.v_type = VAR_UNKNOWN; + if (dict_add(dict, di) == FAIL) { + // Duplicate key: fallback to generic map + clear_tv(rettv); + xfree(di); + goto msgpack_to_vim_generic_map; + } + if (msgpack_to_vim(mobj.via.map.ptr[i].val, &di->di_tv) == FAIL) { + return FAIL; + } + } + break; +msgpack_to_vim_generic_map: {} + list_T *const list = list_alloc(); + list->lv_refcount++; + INIT_SPECIAL_DICT(rettv, kMPMap, + ((typval_T) { + .v_type = VAR_LIST, + .v_lock = 0, + .vval = { .v_list = list }, + })); + for (size_t i = 0; i < mobj.via.map.size; i++) { + list_T *const kv_pair = list_alloc(); + list_append_list(list, kv_pair); + listitem_T *const key_li = listitem_alloc(); + key_li->li_tv.v_type = VAR_UNKNOWN; + list_append(kv_pair, key_li); + listitem_T *const val_li = listitem_alloc(); + val_li->li_tv.v_type = VAR_UNKNOWN; + list_append(kv_pair, val_li); + if (msgpack_to_vim(mobj.via.map.ptr[i].key, &key_li->li_tv) == FAIL) { + return FAIL; + } + if (msgpack_to_vim(mobj.via.map.ptr[i].val, &val_li->li_tv) == FAIL) { + return FAIL; + } + } + break; + } + case MSGPACK_OBJECT_EXT: { + list_T *const list = list_alloc(); + list->lv_refcount++; + list_append_number(list, mobj.via.ext.type); + list_T *const ext_val_list = list_alloc(); + list_append_list(list, ext_val_list); + INIT_SPECIAL_DICT(rettv, kMPExt, + ((typval_T) { + .v_type = VAR_LIST, + .v_lock = 0, + .vval = { .v_list = list }, + })); + if (msgpack_list_write((void *) ext_val_list, mobj.via.ext.ptr, + mobj.via.ext.size) == -1) { + return FAIL; + } + break; + } + } +#undef INIT_SPECIAL_DICT + return OK; +} + +/// "msgpackparse" function +static void f_msgpackparse(typval_T *argvars, typval_T *rettv) + FUNC_ATTR_NONNULL_ALL +{ + if (argvars[0].v_type != VAR_LIST) { + EMSG2(_(e_listarg), "msgpackparse()"); + } + list_T *ret_list = rettv_list_alloc(rettv); + const list_T *list = argvars[0].vval.v_list; + if (list == NULL || list->lv_first == NULL) { + return; + } + if (list->lv_first->li_tv.v_type != VAR_STRING) { + EMSG2(_(e_invarg2), "List item is not a string"); + return; + } + ListReaderState lrstate = init_lrstate(list); + msgpack_unpacker *const unpacker = msgpack_unpacker_new(IOSIZE); + if (unpacker == NULL) { + EMSG(_(e_outofmem)); + return; + } + msgpack_unpacked unpacked; + msgpack_unpacked_init(&unpacked); + do { + if (!msgpack_unpacker_reserve_buffer(unpacker, IOSIZE)) { + EMSG(_(e_outofmem)); + goto f_msgpackparse_exit; + } + size_t read_bytes; + const int rlret = read_from_list( + &lrstate, msgpack_unpacker_buffer(unpacker), IOSIZE, &read_bytes); + if (rlret == FAIL) { + EMSG2(_(e_invarg2), "List item is not a string"); + goto f_msgpackparse_exit; + } + msgpack_unpacker_buffer_consumed(unpacker, read_bytes); + if (read_bytes == 0) { + break; + } + while (unpacker->off < unpacker->used) { + const msgpack_unpack_return result = msgpack_unpacker_next(unpacker, + &unpacked); + if (result == MSGPACK_UNPACK_PARSE_ERROR) { + EMSG2(_(e_invarg2), "Failed to parse msgpack string"); + goto f_msgpackparse_exit; + } + if (result == MSGPACK_UNPACK_NOMEM_ERROR) { + EMSG(_(e_outofmem)); + goto f_msgpackparse_exit; + } + if (result == MSGPACK_UNPACK_SUCCESS) { + listitem_T *li = listitem_alloc(); + li->li_tv.v_type = VAR_UNKNOWN; + list_append(ret_list, li); + if (msgpack_to_vim(unpacked.data, &li->li_tv) == FAIL) { + EMSG2(_(e_invarg2), "Failed to convert msgpack string"); + goto f_msgpackparse_exit; + } + } + if (result == MSGPACK_UNPACK_CONTINUE) { + if (rlret == OK) { + EMSG2(_(e_invarg2), "Incomplete msgpack string"); + } + break; + } + } + if (rlret == OK) { + break; + } + } while (true); + +f_msgpackparse_exit: + msgpack_unpacked_destroy(&unpacked); + msgpack_unpacker_free(unpacker); + return; +} /* * "nextnonblank()" function @@ -12420,7 +13301,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv) #endif p = get_tv_string(&argvars[0]); -#ifdef FEAT_SHORTCUT +#ifdef WIN32 { char_u *v = NULL; @@ -12756,13 +13637,7 @@ theend: */ static void f_round(typval_T *argvars, typval_T *rettv) { - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = round(f); - else - rettv->vval.v_float = 0.0; + float_op_wrapper(argvars, rettv, &round); } // "rpcnotify()" function @@ -12940,7 +13815,7 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv) // The last item of argv must be NULL argv[i] = NULL; - uint64_t channel_id = channel_from_job(argv); + uint64_t channel_id = channel_from_process(argv); if (!channel_id) { EMSG(_(e_api_spawn_failed)); @@ -13893,13 +14768,7 @@ static void f_simplify(typval_T *argvars, typval_T *rettv) */ static void f_sin(typval_T *argvars, typval_T *rettv) { - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = sin(f); - else - rettv->vval.v_float = 0.0; + float_op_wrapper(argvars, rettv, &sin); } /* @@ -13907,13 +14776,7 @@ static void f_sin(typval_T *argvars, typval_T *rettv) */ static void f_sinh(typval_T *argvars, typval_T *rettv) { - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = sinh(f); - else - rettv->vval.v_float = 0.0; + float_op_wrapper(argvars, rettv, &sinh); } /// struct used in the array that's given to qsort() @@ -14384,13 +15247,7 @@ static void f_split(typval_T *argvars, typval_T *rettv) */ static void f_sqrt(typval_T *argvars, typval_T *rettv) { - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = sqrt(f); - else - rettv->vval.v_float = 0.0; + float_op_wrapper(argvars, rettv, &sqrt); } /* @@ -14763,6 +15620,8 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv) modec = TOLOWER_ASC(mode[0]); if (modec != 'c' && modec != 'g') modec = 0; /* replace invalid with current */ + } else if (ui_rgb_attached()) { + modec = 'g'; } else { modec = 'c'; } @@ -15214,19 +16073,15 @@ static void f_termopen(typval_T *argvars, typval_T *rettv) } } - JobOptions opts = common_job_options(argv, on_stdout, on_stderr, on_exit, - job_opts); - opts.pty = true; - opts.width = curwin->w_width; - opts.height = curwin->w_height; - opts.term_name = xstrdup("xterm-256color"); - Job *job = common_job_start(opts, rettv); - if (!job) { - shell_free_argv(argv); + TerminalJobData *data = common_job_init(argv, on_stdout, on_stderr, on_exit, + job_opts, true); + 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)) { return; } - TerminalJobData *data = opts.data; - TerminalOptions topts = TERMINAL_OPTIONS_INIT; + TerminalOptions topts; topts.data = data; topts.width = curwin->w_width; topts.height = curwin->w_height; @@ -15239,7 +16094,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv) && os_isdir(argvars[1].vval.v_string)) { cwd = (char *)argvars[1].vval.v_string; } - int pid = job_pid(job); + int pid = data->proc.pty.process.pid; // Get the desired name of the buffer. char *name = job_opts ? @@ -15278,13 +16133,7 @@ static void f_test(typval_T *argvars, typval_T *rettv) */ static void f_tan(typval_T *argvars, typval_T *rettv) { - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = tan(f); - else - rettv->vval.v_float = 0.0; + float_op_wrapper(argvars, rettv, &tan); } /* @@ -15292,13 +16141,7 @@ static void f_tan(typval_T *argvars, typval_T *rettv) */ static void f_tanh(typval_T *argvars, typval_T *rettv) { - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - rettv->vval.v_float = tanh(f); - else - rettv->vval.v_float = 0.0; + float_op_wrapper(argvars, rettv, &tanh); } /* @@ -15446,14 +16289,7 @@ error: */ static void f_trunc(typval_T *argvars, typval_T *rettv) { - float_T f; - - rettv->v_type = VAR_FLOAT; - if (get_float_arg(argvars, &f) == OK) - /* trunc() is not in C90, use floor() or ceil() instead. */ - rettv->vval.v_float = f > 0 ? floor(f) : ceil(f); - else - rettv->vval.v_float = 0.0; + float_op_wrapper(argvars, rettv, &trunc); } /* @@ -20211,21 +21047,31 @@ char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, char_u *flags) return ret; } -static inline JobOptions common_job_options(char **argv, ufunc_T *on_stdout, - ufunc_T *on_stderr, ufunc_T *on_exit, dict_T *self) +static inline TerminalJobData *common_job_init(char **argv, ufunc_T *on_stdout, + ufunc_T *on_stderr, ufunc_T *on_exit, dict_T *self, bool pty) { 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->self = self; - JobOptions opts = JOB_OPTIONS_INIT; - opts.argv = argv; - opts.data = data; - opts.stdout_cb = on_job_stdout; - opts.stderr_cb = on_job_stderr; - opts.exit_cb = on_job_exit; - return opts; + data->events = queue_new_child(loop.events); + if (pty) { + data->proc.pty = pty_process_init(&loop, data); + } else { + data->proc.uv = uv_process_init(&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 = on_process_exit; + proc->events = data->events; + return data; } /// Return true/false on success/failure. @@ -20251,27 +21097,37 @@ static inline bool common_job_callbacks(dict_T *vopts, ufunc_T **on_stdout, return false; } -static inline Job *common_job_start(JobOptions opts, typval_T *rettv) +static inline bool common_job_start(TerminalJobData *data, typval_T *rettv) { - TerminalJobData *data = opts.data; data->refcount++; - Job *job = job_start(opts, &rettv->vval.v_number); - - if (rettv->vval.v_number <= 0) { - if (rettv->vval.v_number == 0) { - EMSG(_(e_jobtblfull)); - xfree(opts.term_name); + Process *proc = (Process *)&data->proc; + if (!process_spawn(proc)) { + EMSG(_(e_jobexe)); + if (proc->type == kProcessTypePty) { + xfree(data->proc.pty.term_name); free_term_job_data(data); - } else { - EMSG(_(e_jobexe)); } - return NULL; + return false; } - data->job = job; - return job; + + data->id = current_job_id++; + wstream_init(proc->in, 0); + if (proc->out) { + rstream_init(proc->out, 0); + rstream_start(proc->out, on_job_stdout); + } + if (proc->err) { + rstream_init(proc->err, 0); + rstream_start(proc->err, on_job_stderr); + } + pmap_put(uint64_t)(jobs, data->id, data); + rettv->vval.v_number = data->id; + return true; } -static inline void free_term_job_data(TerminalJobData *data) { +static inline void free_term_job_data_event(void **argv) +{ + TerminalJobData *data = argv[0]; if (data->on_stdout) { user_func_unref(data->on_stdout); } @@ -20286,35 +21142,33 @@ static inline void free_term_job_data(TerminalJobData *data) { data->self->internal_refcount--; dict_unref(data->self); } + queue_free(data->events); xfree(data); } -static inline bool is_user_job(Job *job) +static inline void free_term_job_data(TerminalJobData *data) { - if (!job) { - return false; - } - - JobOptions *opts = job_opts(job); - return opts->exit_cb == on_job_exit; + // data->queue may still be used after this function returns(process_wait), so + // only free in the next event loop iteration + queue_put(loop.fast_events, free_term_job_data_event, 1, data); } // vimscript job callbacks must be executed on Nvim main loop -static inline void push_job_event(Job *job, ufunc_T *callback, - const char *type, char *data, size_t count, int status) -{ - JobEvent *event_data = xmalloc(sizeof(JobEvent)); - event_data->received = NULL; - if (data) { - event_data->received = list_alloc(); - char *ptr = data; +static inline void process_job_event(TerminalJobData *data, ufunc_T *callback, + const char *type, char *buf, size_t count, int status) +{ + JobEvent event_data; + event_data.received = NULL; + if (buf) { + event_data.received = list_alloc(); + char *ptr = buf; size_t remaining = count; size_t off = 0; while (off < remaining) { // append the line if (ptr[off] == NL) { - list_append_string(event_data->received, (uint8_t *)ptr, off); + list_append_string(event_data.received, (uint8_t *)ptr, off); size_t skip = off + 1; ptr += skip; remaining -= skip; @@ -20327,60 +21181,58 @@ static inline void push_job_event(Job *job, ufunc_T *callback, } off++; } - list_append_string(event_data->received, (uint8_t *)ptr, off); + list_append_string(event_data.received, (uint8_t *)ptr, off); } else { - event_data->status = status; + event_data.status = status; } - event_data->job_id = job_id(job); - event_data->data = job_data(job); - event_data->callback = callback; - event_data->type = type; - event_push((Event) { - .handler = on_job_event, - .data = event_data - }, !disable_job_defer); + event_data.data = data; + event_data.callback = callback; + event_data.type = type; + on_job_event(&event_data); } -static void on_job_stdout(RStream *rstream, void *job, bool eof) +static void on_job_stdout(Stream *stream, RBuffer *buf, size_t count, + void *job, bool eof) { - TerminalJobData *data = job_data(job); - on_job_output(rstream, job, eof, data->on_stdout, "stdout"); + TerminalJobData *data = job; + on_job_output(stream, job, buf, count, eof, data->on_stdout, "stdout"); } -static void on_job_stderr(RStream *rstream, void *job, bool eof) +static void on_job_stderr(Stream *stream, RBuffer *buf, size_t count, + void *job, bool eof) { - TerminalJobData *data = job_data(job); - on_job_output(rstream, job, eof, data->on_stderr, "stderr"); + TerminalJobData *data = job; + on_job_output(stream, job, buf, count, eof, data->on_stderr, "stderr"); } -static void on_job_output(RStream *rstream, Job *job, bool eof, - ufunc_T *callback, const char *type) +static void on_job_output(Stream *stream, TerminalJobData *data, RBuffer *buf, + size_t count, bool eof, ufunc_T *callback, const char *type) { if (eof) { return; } - TerminalJobData *data = job_data(job); - char *ptr = rstream_read_ptr(rstream); - size_t len = rstream_pending(rstream); + // 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 - // push_job_event will modify the read buffer(convert NULs into NLs) + // process_job_event will modify the read buffer(convert NULs into NLs) if (data->term) { - terminal_receive(data->term, ptr, len); + terminal_receive(data->term, ptr, count); } if (callback) { - push_job_event(job, callback, type, ptr, len, 0); + process_job_event(data, callback, type, ptr, count, 0); } - rbuffer_consumed(rstream_buffer(rstream), len); + rbuffer_consumed(buf, count); } -static void on_job_exit(Job *job, int status, void *d) +static void on_process_exit(Process *proc, int status, void *d) { TerminalJobData *data = d; - if (data->term && !data->exited) { data->exited = true; terminal_close(data->term, @@ -20391,19 +21243,20 @@ static void on_job_exit(Job *job, int status, void *d) *data->status_ptr = status; } - push_job_event(job, data->on_exit, "exit", NULL, 0, status); + process_job_event(data, data->on_exit, "exit", NULL, 0, status); } -static void term_write(char *buf, size_t size, void *data) +static void term_write(char *buf, size_t size, void *d) { - Job *job = ((TerminalJobData *)data)->job; + TerminalJobData *data = d; WBuffer *wbuf = wstream_new_buffer(xmemdup(buf, size), size, 1, xfree); - job_write(job, wbuf); + wstream_write(&data->in, wbuf); } -static void term_resize(uint16_t width, uint16_t height, void *data) +static void term_resize(uint16_t width, uint16_t height, void *d) { - job_resize(((TerminalJobData *)data)->job, width, height); + TerminalJobData *data = d; + pty_process_resize(&data->proc.pty, width, height); } static void term_close(void *d) @@ -20411,7 +21264,7 @@ static void term_close(void *d) TerminalJobData *data = d; if (!data->exited) { data->exited = true; - job_stop(data->job); + process_stop((Process *)&data->proc); } terminal_destroy(data->term); term_job_data_decref(d); @@ -20424,10 +21277,8 @@ static void term_job_data_decref(TerminalJobData *data) } } -static void on_job_event(Event event) +static void on_job_event(JobEvent *ev) { - JobEvent *ev = event.data; - if (!ev->callback) { goto end; } @@ -20438,7 +21289,7 @@ static void on_job_event(Event event) if (argc > 0) { argv[0].v_type = VAR_NUMBER; argv[0].v_lock = 0; - argv[0].vval.v_number = ev->job_id; + argv[0].vval.v_number = ev->data->id; } if (argc > 1) { @@ -20469,9 +21320,18 @@ static void on_job_event(Event event) end: if (!ev->received) { // exit event, safe to free job data now + pmap_del(uint64_t)(jobs, ev->data->id); term_job_data_decref(ev->data); } - xfree(ev); +} + +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) diff --git a/src/nvim/eval.h b/src/nvim/eval.h index 75e3b247f3..8f065eda33 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -65,9 +65,13 @@ enum { VV_PROGPATH, VV_COMMAND_OUTPUT, VV_COMPLETED_ITEM, + VV_MSGPACK_TYPES, VV_LEN, /* number of v: vars */ }; +/// Maximum number of function arguments +#define MAX_FUNC_ARGS 20 + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval.h.generated.h" #endif diff --git a/src/nvim/eval_defs.h b/src/nvim/eval_defs.h index 34a36004d6..0faf860588 100644 --- a/src/nvim/eval_defs.h +++ b/src/nvim/eval_defs.h @@ -1,11 +1,16 @@ #ifndef NVIM_EVAL_DEFS_H #define NVIM_EVAL_DEFS_H +#include <limits.h> + #include "nvim/hashtab.h" typedef int varnumber_T; typedef double float_T; +#define VARNUMBER_MAX INT_MAX +#define VARNUMBER_MIN INT_MIN + typedef struct listvar_S list_T; typedef struct dictvar_S dict_T; diff --git a/src/nvim/event/defs.h b/src/nvim/event/defs.h new file mode 100644 index 0000000000..5126d52241 --- /dev/null +++ b/src/nvim/event/defs.h @@ -0,0 +1,39 @@ +#ifndef NVIM_EVENT_DEFS_H +#define NVIM_EVENT_DEFS_H + +#include <assert.h> +#include <stdarg.h> + +#define EVENT_HANDLER_MAX_ARGC 4 + +typedef void (*argv_callback)(void **argv); +typedef struct message { + int priority; + argv_callback handler; + void *argv[EVENT_HANDLER_MAX_ARGC]; +} Event; + +#define VA_EVENT_INIT(event, p, h, a) \ + do { \ + assert(a <= EVENT_HANDLER_MAX_ARGC); \ + (event)->priority = p; \ + (event)->handler = h; \ + if (a) { \ + va_list args; \ + va_start(args, a); \ + for (int i = 0; i < a; i++) { \ + (event)->argv[i] = va_arg(args, void *); \ + } \ + va_end(args); \ + } \ + } while (0) + +static inline Event event_create(int priority, argv_callback cb, int argc, ...) +{ + assert(argc <= EVENT_HANDLER_MAX_ARGC); + Event event; + VA_EVENT_INIT(&event, priority, cb, argc); + return event; +} + +#endif // NVIM_EVENT_DEFS_H diff --git a/src/nvim/event/loop.c b/src/nvim/event/loop.c new file mode 100644 index 0000000000..3d3288f858 --- /dev/null +++ b/src/nvim/event/loop.c @@ -0,0 +1,85 @@ +#include <stdarg.h> +#include <stdint.h> + +#include <uv.h> + +#include "nvim/event/loop.h" +#include "nvim/event/process.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/loop.c.generated.h" +#endif + +typedef struct idle_event { + uv_idle_t idle; + Event event; +} IdleEvent; + + +void loop_init(Loop *loop, void *data) +{ + uv_loop_init(&loop->uv); + loop->uv.data = loop; + loop->children = kl_init(WatcherPtr); + loop->children_stop_requests = 0; + loop->events = queue_new_parent(loop_on_put, loop); + loop->fast_events = queue_new_child(loop->events); + 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); +} + +void loop_poll_events(Loop *loop, int ms) +{ + static int recursive = 0; + + if (recursive++) { + abort(); // Should not re-enter uv_run + } + + uv_run_mode mode = UV_RUN_ONCE; + + if (ms > 0) { + // Use a repeating timeout of ms milliseconds to make sure + // we do 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, we need to do a non-blocking event poll by + // setting the run mode to UV_RUN_NOWAIT. + mode = UV_RUN_NOWAIT; + } + + uv_run(&loop->uv, mode); + + if (ms > 0) { + uv_timer_stop(&loop->poll_timer); + } + + recursive--; // Can re-enter uv_run now + queue_process_events(loop->fast_events); +} + +void loop_on_put(Queue *queue, void *data) +{ + Loop *loop = data; + // Sometimes libuv will run pending callbacks(timer for example) before + // blocking for a poll. If this happens and the callback pushes a event to one + // of the queues, the event would only be processed after the poll + // returns(user hits a key for example). To avoid this scenario, we call + // uv_stop when a event is enqueued. + uv_stop(&loop->uv); +} + +void loop_close(Loop *loop) +{ + 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); + do { + uv_run(&loop->uv, UV_RUN_DEFAULT); + } while (uv_loop_close(&loop->uv)); +} + +static void timer_cb(uv_timer_t *handle) +{ +} diff --git a/src/nvim/event/loop.h b/src/nvim/event/loop.h new file mode 100644 index 0000000000..9212a45aa4 --- /dev/null +++ b/src/nvim/event/loop.h @@ -0,0 +1,70 @@ +#ifndef NVIM_EVENT_LOOP_H +#define NVIM_EVENT_LOOP_H + +#include <stdint.h> + +#include <uv.h> + +#include "nvim/lib/klist.h" +#include "nvim/os/time.h" +#include "nvim/event/queue.h" + +typedef void * WatcherPtr; + +#define _noop(x) +KLIST_INIT(WatcherPtr, WatcherPtr, _noop) + +typedef struct loop { + uv_loop_t uv; + Queue *events, *fast_events; + klist_t(WatcherPtr) *children; + uv_signal_t children_watcher; + uv_timer_t children_kill_timer, poll_timer; + size_t children_stop_requests; +} Loop; + +#define CREATE_EVENT(queue, handler, argc, ...) \ + do { \ + if (queue) { \ + queue_put((queue), (handler), argc, __VA_ARGS__); \ + } else { \ + void *argv[argc] = {__VA_ARGS__}; \ + (handler)(argv); \ + } \ + } while (0) + +// Poll for events until a condition or timeout +#define LOOP_PROCESS_EVENTS_UNTIL(loop, queue, timeout, condition) \ + do { \ + int remaining = timeout; \ + uint64_t before = (remaining > 0) ? os_hrtime() : 0; \ + while (!(condition)) { \ + LOOP_PROCESS_EVENTS(loop, queue, remaining); \ + if (remaining == 0) { \ + break; \ + } else if (remaining > 0) { \ + uint64_t now = os_hrtime(); \ + remaining -= (int) ((now - before) / 1000000); \ + before = now; \ + if (remaining <= 0) { \ + break; \ + } \ + } \ + } \ + } while (0) + +#define LOOP_PROCESS_EVENTS(loop, queue, timeout) \ + do { \ + if (queue && !queue_empty(queue)) { \ + queue_process_events(queue); \ + } else { \ + loop_poll_events(loop, timeout); \ + } \ + } while (0) + + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/loop.h.generated.h" +#endif + +#endif // NVIM_EVENT_LOOP_H diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c new file mode 100644 index 0000000000..81d4e690c3 --- /dev/null +++ b/src/nvim/event/process.c @@ -0,0 +1,343 @@ +#include <assert.h> +#include <stdlib.h> + +#include <uv.h> + +#include "nvim/os/shell.h" +#include "nvim/event/loop.h" +#include "nvim/event/rstream.h" +#include "nvim/event/wstream.h" +#include "nvim/event/process.h" +#include "nvim/event/uv_process.h" +#include "nvim/event/pty_process.h" +#include "nvim/globals.h" +#include "nvim/log.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/process.c.generated.h" +#endif + +// {SIGNAL}_TIMEOUT is the time (in nanoseconds) that a process has to cleanly +// exit before we send SIGNAL to it +#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); \ + } \ + } while (0) + + +bool process_spawn(Process *proc) FUNC_ATTR_NONNULL_ALL +{ + if (proc->in) { + uv_pipe_init(&proc->loop->uv, &proc->in->uv.pipe, 0); + } + + if (proc->out) { + uv_pipe_init(&proc->loop->uv, &proc->out->uv.pipe, 0); + } + + if (proc->err) { + uv_pipe_init(&proc->loop->uv, &proc->err->uv.pipe, 0); + } + + bool success; + switch (proc->type) { + case kProcessTypeUv: + success = uv_process_spawn((UvProcess *)proc); + break; + case kProcessTypePty: + success = pty_process_spawn((PtyProcess *)proc); + break; + default: + abort(); + } + + if (!success) { + if (proc->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 (proc->err) { + uv_close((uv_handle_t *)&proc->err->uv.pipe, NULL); + } + process_close(proc); + shell_free_argv(proc->argv); + proc->status = -1; + return false; + } + + void *data = proc->data; + + if (proc->in) { + stream_init(NULL, proc->in, -1, (uv_stream_t *)&proc->in->uv.pipe, data); + proc->in->events = proc->events; + 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, (uv_stream_t *)&proc->out->uv.pipe, data); + proc->out->events = proc->events; + 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, (uv_stream_t *)&proc->err->uv.pipe, data); + proc->err->events = proc->events; + proc->err->internal_data = proc; + proc->err->internal_close_cb = on_process_stream_close; + proc->refcount++; + } + + proc->internal_exit_cb = on_process_exit; + proc->internal_close_cb = decref; + proc->refcount++; + kl_push(WatcherPtr, proc->loop->children, proc); + return true; +} + +void process_teardown(Loop *loop) FUNC_ATTR_NONNULL_ALL +{ + kl_iter(WatcherPtr, loop->children, current) { + Process *proc = (*current)->data; + uv_kill(proc->pid, SIGTERM); + proc->term_sent = true; + process_stop(proc); + } + + // Wait until all children exit + LOOP_PROCESS_EVENTS_UNTIL(loop, loop->events, -1, kl_empty(loop->children)); + 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); +} + +/// Synchronously wait for a process to finish +/// +/// @param process The Process instance +/// @param ms Number of milliseconds to wait, 0 for not waiting, -1 for +/// waiting until the process quits. +/// @return returns the status code of the exited process. -1 if the process is +/// still running and the `timeout` has expired. Note that this is +/// indistinguishable from the process returning -1 by itself. Which +/// is possible on some OS. Returns -2 if an user has interruped the +/// wait. +int process_wait(Process *proc, int ms, Queue *events) FUNC_ATTR_NONNULL_ARG(1) +{ + // The default status is -1, which represents a timeout + int status = -1; + bool interrupted = false; + if (!proc->refcount) { + LOOP_PROCESS_EVENTS(proc->loop, proc->events, 0); + return proc->status; + } + + if (!events) { + events = proc->events; + } + + // Increase refcount to stop the exit callback from being called(and possibly + // being freed) before we have a chance to get the status. + proc->refcount++; + LOOP_PROCESS_EVENTS_UNTIL(proc->loop, events, ms, + // Until... + got_int || // interrupted by the user + proc->refcount == 1); // job exited + + // 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) { + // We can only return if all streams/handles are closed and the job + // exited. + LOOP_PROCESS_EVENTS_UNTIL(proc->loop, events, -1, + proc->refcount == 1); + } else { + LOOP_PROCESS_EVENTS(proc->loop, events, 0); + } + } + + 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 + queue_process_events(events); + } + } else { + proc->refcount--; + } + + return status; +} + +/// Ask a process to terminate and eventually kill if it doesn't respond +void process_stop(Process *proc) FUNC_ATTR_NONNULL_ALL +{ + if (proc->stopped_time) { + return; + } + + proc->stopped_time = os_hrtime(); + switch (proc->type) { + case kProcessTypeUv: + // 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); + break; + case kProcessTypePty: + // close all streams for pty processes to send SIGHUP to the process + process_close_streams(proc); + pty_process_close_master((PtyProcess *)proc); + break; + default: + abort(); + } + + 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); + } +} + +/// Iterates the process list sending SIGTERM to stopped processes and SIGKILL +/// to those that didn't die from SIGTERM after a while(exit_timeout is 0). +static void children_kill_cb(uv_timer_t *handle) +{ + Loop *loop = handle->loop->data; + uint64_t now = os_hrtime(); + + kl_iter(WatcherPtr, loop->children, current) { + Process *proc = (*current)->data; + 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); + } + } +} + +static void process_close_event(void **argv) +{ + Process *proc = argv[0]; + shell_free_argv(proc->argv); + if (proc->type == kProcessTypePty) { + xfree(((PtyProcess *)proc)->term_name); + } + if (proc->cb) { + proc->cb(proc, proc->status, proc->data); + } +} + +static void decref(Process *proc) +{ + if (--proc->refcount != 0) { + return; + } + + Loop *loop = proc->loop; + kliter_t(WatcherPtr) **node = NULL; + kl_iter(WatcherPtr, loop->children, current) { + if ((*current)->data == proc) { + node = current; + break; + } + } + assert(node); + kl_shift_at(WatcherPtr, loop->children, node); + CREATE_EVENT(proc->events, process_close_event, 1, proc); +} + +static void process_close(Process *proc) + FUNC_ATTR_NONNULL_ARG(1) +{ + assert(!proc->closed); + proc->closed = true; + switch (proc->type) { + case kProcessTypeUv: + uv_process_close((UvProcess *)proc); + break; + case kProcessTypePty: + pty_process_close((PtyProcess *)proc); + break; + default: + abort(); + } +} + +static void process_close_handles(void **argv) +{ + Process *proc = argv[0]; + process_close_streams(proc); + process_close(proc); +} + +static void on_process_exit(Process *proc) +{ + Loop *loop = proc->loop; + if (proc->stopped_time && loop->children_stop_requests + && !--loop->children_stop_requests) { + // Stop the timer if no more stop requests are pending + DLOG("Stopping process kill timer"); + uv_timer_stop(&loop->children_kill_timer); + } + // Process handles are closed in the next event loop tick. This is done to + // give libuv more time to read data from the OS after the process exits(If + // process_close_streams is called with data still in the OS buffer, we lose + // it) + CREATE_EVENT(proc->events, process_close_handles, 1, proc); +} + +static void on_process_stream_close(Stream *stream, void *data) +{ + Process *proc = data; + decref(proc); +} + diff --git a/src/nvim/event/process.h b/src/nvim/event/process.h new file mode 100644 index 0000000000..45edc46b95 --- /dev/null +++ b/src/nvim/event/process.h @@ -0,0 +1,58 @@ +#ifndef NVIM_EVENT_PROCESS_H +#define NVIM_EVENT_PROCESS_H + +#include "nvim/event/loop.h" +#include "nvim/event/rstream.h" +#include "nvim/event/wstream.h" + +typedef enum { + kProcessTypeUv, + kProcessTypePty +} ProcessType; + +typedef struct process Process; +typedef void (*process_exit_cb)(Process *proc, int status, void *data); +typedef void (*internal_process_cb)(Process *proc); + +struct process { + ProcessType type; + Loop *loop; + void *data; + int pid, status, refcount; + // set to the hrtime of when process_stop was called for the process. + uint64_t stopped_time; + char **argv; + Stream *in, *out, *err; + process_exit_cb cb; + internal_process_cb internal_exit_cb, internal_close_cb; + bool closed, term_sent; + Queue *events; +}; + +static inline Process process_init(Loop *loop, ProcessType type, void *data) +{ + return (Process) { + .type = type, + .data = data, + .loop = loop, + .events = NULL, + .pid = 0, + .status = 0, + .refcount = 0, + .stopped_time = 0, + .argv = NULL, + .in = NULL, + .out = NULL, + .err = NULL, + .cb = NULL, + .closed = false, + .term_sent = false, + .internal_close_cb = NULL, + .internal_exit_cb = NULL + }; +} + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/process.h.generated.h" +#endif +#endif // NVIM_EVENT_PROCESS_H diff --git a/src/nvim/os/pty_process.c b/src/nvim/event/pty_process.c index ff0bcfb6de..8eef72f12f 100644 --- a/src/nvim/os/pty_process.c +++ b/src/nvim/event/pty_process.c @@ -20,92 +20,40 @@ #include <uv.h> -#include "nvim/func_attr.h" -#include "nvim/os/job.h" -#include "nvim/os/job_defs.h" -#include "nvim/os/job_private.h" -#include "nvim/os/pty_process.h" -#include "nvim/memory.h" +#include "nvim/lib/klist.h" + +#include "nvim/event/loop.h" +#include "nvim/event/rstream.h" +#include "nvim/event/wstream.h" +#include "nvim/event/process.h" +#include "nvim/event/pty_process.h" +#include "nvim/log.h" #ifdef INCLUDE_GENERATED_DECLARATIONS -# include "os/pty_process.c.generated.h" +# include "event/pty_process.c.generated.h" #endif -typedef struct { - struct winsize winsize; - uv_pipe_t proc_stdin, proc_stdout, proc_stderr; - int tty_fd; -} PtyProcess; - -void pty_process_init(Job *job) FUNC_ATTR_NONNULL_ALL -{ - PtyProcess *ptyproc = xmalloc(sizeof(PtyProcess)); - ptyproc->tty_fd = -1; - - if (job->opts.writable) { - uv_pipe_init(uv_default_loop(), &ptyproc->proc_stdin, 0); - ptyproc->proc_stdin.data = NULL; - } - - if (job->opts.stdout_cb) { - uv_pipe_init(uv_default_loop(), &ptyproc->proc_stdout, 0); - ptyproc->proc_stdout.data = NULL; - } - - if (job->opts.stderr_cb) { - uv_pipe_init(uv_default_loop(), &ptyproc->proc_stderr, 0); - ptyproc->proc_stderr.data = NULL; - } - - job->proc_stdin = (uv_stream_t *)&ptyproc->proc_stdin; - job->proc_stdout = (uv_stream_t *)&ptyproc->proc_stdout; - job->proc_stderr = (uv_stream_t *)&ptyproc->proc_stderr; - job->process = ptyproc; -} - -void pty_process_destroy(Job *job) FUNC_ATTR_NONNULL_ALL -{ - xfree(job->opts.term_name); - xfree(job->process); - job->process = NULL; -} - -static bool set_pipe_duplicating_descriptor(int fd, uv_pipe_t *pipe) +bool pty_process_spawn(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL { - int fd_dup = dup(fd); - if (fd_dup < 0) { - ELOG("Failed to dup descriptor %d: %s", fd, strerror(errno)); - return false; + static struct termios termios; + if (!termios.c_cflag) { + init_termios(&termios); } - int uv_result = uv_pipe_open(pipe, fd_dup); - if (uv_result) { - ELOG("Failed to set pipe to descriptor %d: %s", - fd_dup, uv_strerror(uv_result)); - close(fd_dup); - return false; - } - return true; -} -static const unsigned int KILL_RETRIES = 5; -static const unsigned int KILL_TIMEOUT = 2; // seconds - -bool pty_process_spawn(Job *job) FUNC_ATTR_NONNULL_ALL -{ - int master; - PtyProcess *ptyproc = job->process; - ptyproc->winsize = (struct winsize){job->opts.height, job->opts.width, 0, 0}; - struct termios termios; - init_termios(&termios); + Process *proc = (Process *)ptyproc; + assert(!proc->err); + 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); if (pid < 0) { + ELOG("forkpty failed: %s", strerror(errno)); return false; } else if (pid == 0) { - init_child(job); + init_child(ptyproc); abort(); } @@ -120,67 +68,56 @@ bool pty_process_spawn(Job *job) FUNC_ATTR_NONNULL_ALL goto error; } - if (job->opts.writable - && !set_pipe_duplicating_descriptor(master, &ptyproc->proc_stdin)) { + if (proc->in && !set_duplicating_descriptor(master, &proc->in->uv.pipe)) { goto error; } - - if (job->opts.stdout_cb - && !set_pipe_duplicating_descriptor(master, &ptyproc->proc_stdout)) { - goto error; - } - - if (job->opts.stderr_cb - && !set_pipe_duplicating_descriptor(master, &ptyproc->proc_stderr)) { + if (proc->out && !set_duplicating_descriptor(master, &proc->out->uv.pipe)) { goto error; } ptyproc->tty_fd = master; - job->pid = pid; + proc->pid = pid; return true; error: close(master); - - // terminate spawned process - kill(pid, SIGTERM); - int status, child; - unsigned int try = 0; - while (try++ < KILL_RETRIES && !(child = waitpid(pid, &status, WNOHANG))) { - sleep(KILL_TIMEOUT); - } - if (child != pid) { - kill(pid, SIGKILL); - } - + kill(pid, SIGKILL); + waitpid(pid, NULL, 0); return false; } -void pty_process_close(Job *job) FUNC_ATTR_NONNULL_ALL +void pty_process_resize(PtyProcess *ptyproc, uint16_t width, + uint16_t height) + FUNC_ATTR_NONNULL_ALL +{ + ptyproc->winsize = (struct winsize){height, width, 0, 0}; + ioctl(ptyproc->tty_fd, TIOCSWINSZ, &ptyproc->winsize); +} + +void pty_process_close(PtyProcess *ptyproc) + FUNC_ATTR_NONNULL_ALL { - pty_process_close_master(job); - job_close_streams(job); - job_decref(job); + pty_process_close_master(ptyproc); + Process *proc = (Process *)ptyproc; + if (proc->internal_close_cb) { + proc->internal_close_cb(proc); + } } -void pty_process_close_master(Job *job) FUNC_ATTR_NONNULL_ALL +void pty_process_close_master(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL { - PtyProcess *ptyproc = job->process; if (ptyproc->tty_fd >= 0) { close(ptyproc->tty_fd); ptyproc->tty_fd = -1; } } -void pty_process_resize(Job *job, uint16_t width, uint16_t height) - FUNC_ATTR_NONNULL_ALL +void pty_process_teardown(Loop *loop) { - PtyProcess *ptyproc = job->process; - ptyproc->winsize = (struct winsize){height, width, 0, 0}; - ioctl(ptyproc->tty_fd, TIOCSWINSZ, &ptyproc->winsize); + uv_signal_stop(&loop->children_watcher); } -static void init_child(Job *job) FUNC_ATTR_NONNULL_ALL +static void init_child(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL { unsetenv("COLUMNS"); unsetenv("LINES"); @@ -195,14 +132,13 @@ static void init_child(Job *job) FUNC_ATTR_NONNULL_ALL signal(SIGTERM, SIG_DFL); signal(SIGALRM, SIG_DFL); - setenv("TERM", job->opts.term_name ? job->opts.term_name : "ansi", 1); - execvp(job->opts.argv[0], job->opts.argv); + setenv("TERM", ptyproc->term_name ? ptyproc->term_name : "ansi", 1); + execvp(ptyproc->process.argv[0], ptyproc->process.argv); fprintf(stderr, "execvp failed: %s\n", strerror(errno)); } static void init_termios(struct termios *termios) FUNC_ATTR_NONNULL_ALL { - memset(termios, 0, sizeof(struct termios)); // Taken from pangoterm termios->c_iflag = ICRNL|IXON; termios->c_oflag = OPOST|ONLCR; @@ -255,3 +191,50 @@ static void init_termios(struct termios *termios) FUNC_ATTR_NONNULL_ALL termios->c_cc[VMIN] = 1; termios->c_cc[VTIME] = 0; } + +static bool set_duplicating_descriptor(int fd, uv_pipe_t *pipe) + FUNC_ATTR_NONNULL_ALL +{ + int fd_dup = dup(fd); + if (fd_dup < 0) { + ELOG("Failed to dup descriptor %d: %s", fd, strerror(errno)); + return false; + } + int uv_result = uv_pipe_open(pipe, fd_dup); + if (uv_result) { + ELOG("Failed to set pipe to descriptor %d: %s", + fd_dup, uv_strerror(uv_result)); + close(fd_dup); + return false; + } + return true; +} + +static void chld_handler(uv_signal_t *handle, int signum) +{ + int stat = 0; + int pid; + + do { + pid = waitpid(-1, &stat, WNOHANG); + } while (pid < 0 && errno == EINTR); + + if (pid <= 0) { + return; + } + + Loop *loop = handle->loop->data; + + kl_iter(WatcherPtr, loop->children, current) { + Process *proc = (*current)->data; + if (proc->pid == pid) { + if (WIFEXITED(stat)) { + proc->status = WEXITSTATUS(stat); + } else if (WIFSIGNALED(stat)) { + proc->status = WTERMSIG(stat); + } + proc->internal_exit_cb(proc); + break; + } + } +} diff --git a/src/nvim/event/pty_process.h b/src/nvim/event/pty_process.h new file mode 100644 index 0000000000..446d7fd3c8 --- /dev/null +++ b/src/nvim/event/pty_process.h @@ -0,0 +1,30 @@ +#ifndef NVIM_EVENT_PTY_PROCESS_H +#define NVIM_EVENT_PTY_PROCESS_H + +#include <sys/ioctl.h> + +#include "nvim/event/process.h" + +typedef struct pty_process { + Process process; + char *term_name; + uint16_t width, height; + struct winsize winsize; + int tty_fd; +} PtyProcess; + +static inline PtyProcess pty_process_init(Loop *loop, void *data) +{ + PtyProcess rv; + rv.process = process_init(loop, kProcessTypePty, data); + rv.term_name = NULL; + rv.width = 80; + rv.height = 24; + rv.tty_fd = -1; + return rv; +} + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/pty_process.h.generated.h" +#endif +#endif // NVIM_EVENT_PTY_PROCESS_H diff --git a/src/nvim/event/queue.c b/src/nvim/event/queue.c new file mode 100644 index 0000000000..19eca14144 --- /dev/null +++ b/src/nvim/event/queue.c @@ -0,0 +1,208 @@ +// Queue for selective async event processing. Instances of this queue support a +// parent/child relationship with the following properties: +// +// - pushing a node to a child queue will push a corresponding link node to the +// parent queue +// - removing a link node from a parent queue will remove the next node +// in the linked child queue +// - removing a node from a child queue will remove the corresponding link node +// in the parent queue +// +// These properties allow neovim to organize and process events from different +// sources with a certain degree of control. Here's how the queue is used: +// +// +----------------+ +// | Main loop | +// +----------------+ +// ^ +// | +// +----------------+ +// +-------------->| Event loop |<------------+ +// | +--+-------------+ | +// | ^ ^ | +// | | | | +// +-----------+ +-----------+ +---------+ +---------+ +// | Channel 1 | | Channel 2 | | Job 1 | | Job 2 | +// +-----------+ +-----------+ +---------+ +---------+ +// +// +// In the above diagram, the lower boxes represents event emitters, each with +// it's own private queue that have the event loop queue as the parent. +// +// When idle, the main loop spins the event loop which queues events from many +// sources(channels, jobs, user...). Each event emitter pushes events to its own +// private queue which is propagated to the event loop queue. When the main loop +// consumes an event, the corresponding event is removed from the emitter's +// queue. +// +// The main reason for this queue hierarchy is to allow focusing on a single +// event emitter while blocking the main loop. For example, if the `jobwait` +// vimscript function is called on job1, the main loop will temporarily stop +// polling the event loop queue and poll job1 queue instead. Same with channels, +// when calling `rpcrequest`, we want to temporarily stop processing events from +// other sources and focus on a specific channel. + +#include <assert.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdint.h> + + +#include <uv.h> + +#include "nvim/event/queue.h" +#include "nvim/memory.h" +#include "nvim/os/time.h" + +typedef struct queue_item QueueItem; +struct queue_item { + union { + Queue *queue; + struct { + Event event; + QueueItem *parent; + } item; + } data; + bool link; // this is just a link to a node in a child queue + QUEUE node; +}; + +struct queue { + Queue *parent; + QUEUE headtail; + put_callback put_cb; + void *data; +}; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/queue.c.generated.h" +#endif + +static Event NILEVENT = {.handler = NULL, .argv = {NULL}}; + +Queue *queue_new_parent(put_callback put_cb, void *data) +{ + return queue_new(NULL, put_cb, data); +} + +Queue *queue_new_child(Queue *parent) + FUNC_ATTR_NONNULL_ALL +{ + assert(!parent->parent); + return queue_new(parent, NULL, NULL); +} + +static Queue *queue_new(Queue *parent, put_callback put_cb, void *data) +{ + Queue *rv = xmalloc(sizeof(Queue)); + QUEUE_INIT(&rv->headtail); + rv->parent = parent; + rv->put_cb = put_cb; + rv->data = data; + return rv; +} + +void queue_free(Queue *queue) +{ + assert(queue); + if (queue->parent) { + while (!QUEUE_EMPTY(&queue->headtail)) { + QUEUE *q = QUEUE_HEAD(&queue->headtail); + QueueItem *item = queue_node_data(q); + assert(!item->link); + QUEUE_REMOVE(&item->data.item.parent->node); + xfree(item->data.item.parent); + QUEUE_REMOVE(q); + xfree(item); + } + } + + xfree(queue); +} + +Event queue_get(Queue *queue) +{ + return queue_empty(queue) ? NILEVENT : queue_remove(queue); +} + +void queue_put_event(Queue *queue, Event event) +{ + assert(queue); + assert(queue->parent); // don't push directly to the parent queue + queue_push(queue, event); + if (queue->parent->put_cb) { + queue->parent->put_cb(queue->parent, queue->parent->data); + } +} + +void queue_process_events(Queue *queue) +{ + assert(queue); + while (!queue_empty(queue)) { + Event event = queue_get(queue); + if (event.handler) { + event.handler(event.argv); + } + } +} + +bool queue_empty(Queue *queue) +{ + assert(queue); + return QUEUE_EMPTY(&queue->headtail); +} + +void queue_replace_parent(Queue *queue, Queue *new_parent) +{ + assert(queue_empty(queue)); + queue->parent = new_parent; +} + +static Event queue_remove(Queue *queue) +{ + assert(!queue_empty(queue)); + QUEUE *h = QUEUE_HEAD(&queue->headtail); + QUEUE_REMOVE(h); + QueueItem *item = queue_node_data(h); + Event rv; + + if (item->link) { + assert(!queue->parent); + // remove the next node in the linked queue + Queue *linked = item->data.queue; + assert(!queue_empty(linked)); + QueueItem *child = + queue_node_data(QUEUE_HEAD(&linked->headtail)); + QUEUE_REMOVE(&child->node); + rv = child->data.item.event; + xfree(child); + } else { + assert(queue->parent); + assert(!queue_empty(queue->parent)); + // remove the corresponding link node in the parent queue + QUEUE_REMOVE(&item->data.item.parent->node); + xfree(item->data.item.parent); + rv = item->data.item.event; + } + + xfree(item); + return rv; +} + +static void queue_push(Queue *queue, Event event) +{ + QueueItem *item = xmalloc(sizeof(QueueItem)); + item->link = false; + item->data.item.event = event; + QUEUE_INSERT_TAIL(&queue->headtail, &item->node); + // push link node to the parent queue + item->data.item.parent = xmalloc(sizeof(QueueItem)); + item->data.item.parent->link = true; + item->data.item.parent->data.queue = queue; + QUEUE_INSERT_TAIL(&queue->parent->headtail, &item->data.item.parent->node); +} + +static QueueItem *queue_node_data(QUEUE *q) +{ + return QUEUE_DATA(q, QueueItem, node); +} diff --git a/src/nvim/event/queue.h b/src/nvim/event/queue.h new file mode 100644 index 0000000000..85fc59f8b2 --- /dev/null +++ b/src/nvim/event/queue.h @@ -0,0 +1,19 @@ +#ifndef NVIM_EVENT_QUEUE_H +#define NVIM_EVENT_QUEUE_H + +#include <uv.h> + +#include "nvim/event/defs.h" +#include "nvim/lib/queue.h" + +typedef struct queue Queue; +typedef void (*put_callback)(Queue *queue, void *data); + +#define queue_put(q, h, ...) \ + queue_put_event(q, event_create(1, h, __VA_ARGS__)); + + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/queue.h.generated.h" +#endif +#endif // NVIM_EVENT_QUEUE_H diff --git a/src/nvim/event/rstream.c b/src/nvim/event/rstream.c new file mode 100644 index 0000000000..0a720bb852 --- /dev/null +++ b/src/nvim/event/rstream.c @@ -0,0 +1,186 @@ +#include <assert.h> +#include <stdint.h> +#include <stdbool.h> +#include <stdlib.h> + +#include <uv.h> + +#include "nvim/event/rstream.h" +#include "nvim/ascii.h" +#include "nvim/vim.h" +#include "nvim/memory.h" +#include "nvim/log.h" +#include "nvim/misc1.h" +#include "nvim/event/loop.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/rstream.c.generated.h" +#endif + +void rstream_init_fd(Loop *loop, Stream *stream, int fd, size_t bufsize, + void *data) + FUNC_ATTR_NONNULL_ARG(1) + FUNC_ATTR_NONNULL_ARG(2) +{ + stream_init(loop, stream, fd, NULL, data); + rstream_init(stream, bufsize); +} + +void rstream_init_stream(Stream *stream, uv_stream_t *uvstream, size_t bufsize, + void *data) + FUNC_ATTR_NONNULL_ARG(1) + FUNC_ATTR_NONNULL_ARG(2) +{ + stream_init(NULL, stream, -1, uvstream, data); + rstream_init(stream, bufsize); +} + +void rstream_init(Stream *stream, size_t bufsize) + FUNC_ATTR_NONNULL_ARG(1) +{ + stream->buffer = rbuffer_new(bufsize); + stream->buffer->data = stream; + stream->buffer->full_cb = on_rbuffer_full; + stream->buffer->nonfull_cb = on_rbuffer_nonfull; +} + + +/// Starts watching for events from a `Stream` instance. +/// +/// @param stream The `Stream` instance +void rstream_start(Stream *stream, stream_read_cb cb) + FUNC_ATTR_NONNULL_ARG(1) +{ + stream->read_cb = cb; + if (stream->uvstream) { + uv_read_start(stream->uvstream, alloc_cb, read_cb); + } else { + uv_idle_start(&stream->uv.idle, fread_idle_cb); + } +} + +/// Stops watching for events from a `Stream` instance. +/// +/// @param stream The `Stream` instance +void rstream_stop(Stream *stream) + FUNC_ATTR_NONNULL_ALL +{ + if (stream->uvstream) { + uv_read_stop(stream->uvstream); + } else { + uv_idle_stop(&stream->uv.idle); + } +} + +static void on_rbuffer_full(RBuffer *buf, void *data) +{ + rstream_stop(data); +} + +static void on_rbuffer_nonfull(RBuffer *buf, void *data) +{ + Stream *stream = data; + assert(stream->read_cb); + rstream_start(stream, stream->read_cb); +} + +// Callbacks used by libuv + +// Called by libuv to allocate memory for reading. +static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf) +{ + Stream *stream = handle->data; + buf->base = rbuffer_write_ptr(stream->buffer, &buf->len); +} + +// Callback invoked by libuv after it copies the data into the buffer provided +// by `alloc_cb`. This is also called on EOF or when `alloc_cb` returns a +// 0-length buffer. +static void read_cb(uv_stream_t *uvstream, ssize_t cnt, const uv_buf_t *buf) +{ + Stream *stream = uvstream->data; + + 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) { + DLOG("Closing Stream(%p) because of %s(%zd)", stream, + uv_strerror((int)cnt), cnt); + // Read error or EOF, either way stop the stream and invoke the callback + // with eof == true + uv_read_stop(uvstream); + invoke_read_cb(stream, 0, true); + } + return; + } + + // at this point we're sure that cnt is positive, no error occurred + size_t nread = (size_t)cnt; + // 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); + invoke_read_cb(stream, nread, false); +} + +// Called by the by the 'idle' handle to emulate a reading event +static void fread_idle_cb(uv_idle_t *handle) +{ + uv_fs_t req; + Stream *stream = handle->data; + + stream->uvbuf.base = rbuffer_write_ptr(stream->buffer, &stream->uvbuf.len); + + // the offset argument to uv_fs_read is int64_t, could someone really try + // to read more than 9 quintillion (9e18) bytes? + // upcast is meant to avoid tautological condition warning on 32 bits + uintmax_t fpos_intmax = stream->fpos; + if (fpos_intmax > INT64_MAX) { + ELOG("stream offset overflow"); + preserve_exit(); + } + + // Synchronous read + uv_fs_read( + handle->loop, + &req, + stream->fd, + &stream->uvbuf, + 1, + (int64_t) stream->fpos, + NULL); + + uv_fs_req_cleanup(&req); + + if (req.result <= 0) { + uv_idle_stop(&stream->uv.idle); + invoke_read_cb(stream, 0, true); + return; + } + + // no errors (req.result (ssize_t) is positive), it's safe to cast. + size_t nread = (size_t) req.result; + rbuffer_produced(stream->buffer, nread); + stream->fpos += nread; + invoke_read_cb(stream, nread, false); +} + +static void read_event(void **argv) +{ + Stream *stream = argv[0]; + if (stream->read_cb) { + size_t count = (uintptr_t)argv[1]; + bool eof = (uintptr_t)argv[2]; + stream->read_cb(stream, stream->buffer, count, stream->data, eof); + } +} + +static void invoke_read_cb(Stream *stream, size_t count, bool eof) +{ + CREATE_EVENT(stream->events, read_event, 3, stream, + (void *)(uintptr_t *)count, (void *)(uintptr_t)eof); +} diff --git a/src/nvim/event/rstream.h b/src/nvim/event/rstream.h new file mode 100644 index 0000000000..f30ad79ee5 --- /dev/null +++ b/src/nvim/event/rstream.h @@ -0,0 +1,16 @@ +#ifndef NVIM_EVENT_RSTREAM_H +#define NVIM_EVENT_RSTREAM_H + +#include <stdbool.h> +#include <stddef.h> + +#include <uv.h> + +#include "nvim/event/loop.h" +#include "nvim/event/stream.h" + + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/rstream.h.generated.h" +#endif +#endif // NVIM_EVENT_RSTREAM_H diff --git a/src/nvim/event/signal.c b/src/nvim/event/signal.c new file mode 100644 index 0000000000..11ce15a882 --- /dev/null +++ b/src/nvim/event/signal.c @@ -0,0 +1,59 @@ +#include <uv.h> + +#include "nvim/event/loop.h" +#include "nvim/event/signal.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/signal.c.generated.h" +#endif + + +void signal_watcher_init(Loop *loop, SignalWatcher *watcher, void *data) + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_ARG(2) +{ + uv_signal_init(&loop->uv, &watcher->uv); + watcher->uv.data = watcher; + watcher->data = data; + watcher->cb = NULL; + watcher->events = loop->fast_events; +} + +void signal_watcher_start(SignalWatcher *watcher, signal_cb cb, int signum) + FUNC_ATTR_NONNULL_ALL +{ + watcher->cb = cb; + uv_signal_start(&watcher->uv, signal_watcher_cb, signum); +} + +void signal_watcher_stop(SignalWatcher *watcher) + FUNC_ATTR_NONNULL_ALL +{ + uv_signal_stop(&watcher->uv); +} + +void signal_watcher_close(SignalWatcher *watcher, signal_close_cb cb) + FUNC_ATTR_NONNULL_ARG(1) +{ + watcher->close_cb = cb; + uv_close((uv_handle_t *)&watcher->uv, close_cb); +} + +static void signal_event(void **argv) +{ + SignalWatcher *watcher = argv[0]; + watcher->cb(watcher, watcher->uv.signum, watcher->data); +} + +static void signal_watcher_cb(uv_signal_t *handle, int signum) +{ + SignalWatcher *watcher = handle->data; + CREATE_EVENT(watcher->events, signal_event, 1, watcher); +} + +static void close_cb(uv_handle_t *handle) +{ + SignalWatcher *watcher = handle->data; + if (watcher->close_cb) { + watcher->close_cb(watcher, watcher->data); + } +} diff --git a/src/nvim/event/signal.h b/src/nvim/event/signal.h new file mode 100644 index 0000000000..e32608acc0 --- /dev/null +++ b/src/nvim/event/signal.h @@ -0,0 +1,23 @@ +#ifndef NVIM_EVENT_SIGNAL_H +#define NVIM_EVENT_SIGNAL_H + +#include <uv.h> + +#include "nvim/event/loop.h" + +typedef struct signal_watcher SignalWatcher; +typedef void (*signal_cb)(SignalWatcher *watcher, int signum, void *data); +typedef void (*signal_close_cb)(SignalWatcher *watcher, void *data); + +struct signal_watcher { + uv_signal_t uv; + void *data; + signal_cb cb; + signal_close_cb close_cb; + Queue *events; +}; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/signal.h.generated.h" +#endif +#endif // NVIM_EVENT_SIGNAL_H diff --git a/src/nvim/event/socket.c b/src/nvim/event/socket.c new file mode 100644 index 0000000000..347e464d25 --- /dev/null +++ b/src/nvim/event/socket.c @@ -0,0 +1,167 @@ +#include <assert.h> +#include <stdint.h> + +#include <uv.h> + +#include "nvim/event/loop.h" +#include "nvim/event/socket.h" +#include "nvim/event/rstream.h" +#include "nvim/event/wstream.h" +#include "nvim/os/os.h" +#include "nvim/ascii.h" +#include "nvim/vim.h" +#include "nvim/strings.h" +#include "nvim/path.h" +#include "nvim/memory.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/socket.c.generated.h" +#endif + +#define NVIM_DEFAULT_TCP_PORT 7450 + +void socket_watcher_init(Loop *loop, SocketWatcher *watcher, + const char *endpoint, void *data) + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_NONNULL_ARG(3) +{ + // Trim to `ADDRESS_MAX_SIZE` + if (xstrlcpy(watcher->addr, endpoint, sizeof(watcher->addr)) + >= sizeof(watcher->addr)) { + // TODO(aktau): since this is not what the user wanted, perhaps we + // should return an error here + WLOG("Address was too long, truncated to %s", watcher->addr); + } + + bool tcp = true; + char ip[16], *ip_end = xstrchrnul(watcher->addr, ':'); + + // (ip_end - addr) is always > 0, so convert to size_t + size_t addr_len = (size_t)(ip_end - watcher->addr); + + if (addr_len > sizeof(ip) - 1) { + // Maximum length of an IPv4 address buffer is 15 (eg: 255.255.255.255) + addr_len = sizeof(ip) - 1; + } + + // Extract the address part + xstrlcpy(ip, watcher->addr, addr_len + 1); + int port = NVIM_DEFAULT_TCP_PORT; + + if (*ip_end == ':') { + // Extract the port + long lport = strtol(ip_end + 1, NULL, 10); // NOLINT + if (lport <= 0 || lport > 0xffff) { + // Invalid port, treat as named pipe or unix socket + tcp = false; + } else { + port = (int) lport; + } + } + + if (tcp) { + // Try to parse ip address + if (uv_ip4_addr(ip, port, &watcher->uv.tcp.addr)) { + // Invalid address, treat as named pipe or unix socket + tcp = false; + } + } + + if (tcp) { + uv_tcp_init(&loop->uv, &watcher->uv.tcp.handle); + watcher->stream = (uv_stream_t *)&watcher->uv.tcp.handle; + } else { + uv_pipe_init(&loop->uv, &watcher->uv.pipe.handle, 0); + watcher->stream = (uv_stream_t *)&watcher->uv.pipe.handle; + } + + watcher->stream->data = watcher; + watcher->cb = NULL; + watcher->close_cb = NULL; + watcher->events = NULL; +} + +int socket_watcher_start(SocketWatcher *watcher, int backlog, socket_cb cb) + FUNC_ATTR_NONNULL_ALL +{ + watcher->cb = cb; + int result; + + if (watcher->stream->type == UV_TCP) { + result = uv_tcp_bind(&watcher->uv.tcp.handle, + (const struct sockaddr *)&watcher->uv.tcp.addr, 0); + } else { + result = uv_pipe_bind(&watcher->uv.pipe.handle, watcher->addr); + } + + if (result == 0) { + result = uv_listen(watcher->stream, backlog, connection_cb); + } + + assert(result <= 0); // libuv should have returned -errno or zero. + if (result < 0) { + if (result == -EACCES) { + // Libuv converts ENOENT to EACCES for Windows compatibility, but if + // the parent directory does not exist, ENOENT would be more accurate. + *path_tail((char_u *)watcher->addr) = NUL; + if (!os_file_exists((char_u *)watcher->addr)) { + result = -ENOENT; + } + } + return result; + } + + return 0; +} + +int socket_watcher_accept(SocketWatcher *watcher, Stream *stream, void *data) + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_ARG(2) +{ + uv_stream_t *client; + + if (watcher->stream->type == UV_TCP) { + client = (uv_stream_t *)&stream->uv.tcp; + uv_tcp_init(watcher->uv.tcp.handle.loop, (uv_tcp_t *)client); + } else { + client = (uv_stream_t *)&stream->uv.pipe; + uv_pipe_init(watcher->uv.pipe.handle.loop, (uv_pipe_t *)client, 0); + } + + int result = uv_accept(watcher->stream, client); + + if (result) { + uv_close((uv_handle_t *)client, NULL); + return result; + } + + stream_init(NULL, stream, -1, client, data); + return 0; +} + +void socket_watcher_close(SocketWatcher *watcher, socket_close_cb cb) + FUNC_ATTR_NONNULL_ARG(1) +{ + watcher->close_cb = cb; + uv_close((uv_handle_t *)watcher->stream, close_cb); +} + +static void connection_event(void **argv) +{ + SocketWatcher *watcher = argv[0]; + int status = (int)(uintptr_t)(argv[1]); + watcher->cb(watcher, status, watcher->data); +} + +static void connection_cb(uv_stream_t *handle, int status) +{ + SocketWatcher *watcher = handle->data; + CREATE_EVENT(watcher->events, connection_event, 2, watcher, + (void *)(uintptr_t)status); +} + +static void close_cb(uv_handle_t *handle) +{ + SocketWatcher *watcher = handle->data; + if (watcher->close_cb) { + watcher->close_cb(watcher, watcher->data); + } +} diff --git a/src/nvim/event/socket.h b/src/nvim/event/socket.h new file mode 100644 index 0000000000..ad59fdbe3a --- /dev/null +++ b/src/nvim/event/socket.h @@ -0,0 +1,39 @@ +#ifndef NVIM_EVENT_SOCKET_H +#define NVIM_EVENT_SOCKET_H + +#include <uv.h> + +#include "nvim/event/loop.h" +#include "nvim/event/rstream.h" +#include "nvim/event/wstream.h" + +#define ADDRESS_MAX_SIZE 256 + +typedef struct socket_watcher SocketWatcher; +typedef void (*socket_cb)(SocketWatcher *watcher, int result, void *data); +typedef void (*socket_close_cb)(SocketWatcher *watcher, void *data); + +struct socket_watcher { + // Pipe/socket path, or TCP address string + char addr[ADDRESS_MAX_SIZE]; + // TCP server or unix socket (named pipe on Windows) + union { + struct { + uv_tcp_t handle; + struct sockaddr_in addr; + } tcp; + struct { + uv_pipe_t handle; + } pipe; + } uv; + uv_stream_t *stream; + void *data; + socket_cb cb; + socket_close_cb close_cb; + Queue *events; +}; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/socket.h.generated.h" +#endif +#endif // NVIM_EVENT_SOCKET_H diff --git a/src/nvim/event/stream.c b/src/nvim/event/stream.c new file mode 100644 index 0000000000..6caad6fdcc --- /dev/null +++ b/src/nvim/event/stream.c @@ -0,0 +1,111 @@ +#include <assert.h> +#include <stdio.h> +#include <stdbool.h> + +#include <uv.h> + +#include "nvim/rbuffer.h" +#include "nvim/event/stream.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/stream.c.generated.h" +#endif + +/// Sets the stream associated with `fd` to "blocking" mode. +/// +/// @return `0` on success, or `-errno` on failure. +int stream_set_blocking(int fd, bool blocking) +{ + // Private loop to avoid conflict with existing watcher(s): + // uv__io_stop: Assertion `loop->watchers[w->fd] == w' failed. + uv_loop_t loop; + uv_pipe_t stream; + uv_loop_init(&loop); + uv_pipe_init(&loop, &stream, 0); + uv_pipe_open(&stream, fd); + int retval = uv_stream_set_blocking((uv_stream_t *)&stream, blocking); + uv_close((uv_handle_t *)&stream, NULL); + uv_run(&loop, UV_RUN_NOWAIT); // not necessary, but couldn't hurt. + uv_loop_close(&loop); + return retval; +} + +void stream_init(Loop *loop, Stream *stream, int fd, uv_stream_t *uvstream, + void *data) + FUNC_ATTR_NONNULL_ARG(2) +{ + stream->uvstream = uvstream; + + if (fd >= 0) { + uv_handle_type type = uv_guess_handle(fd); + stream->fd = fd; + + if (type == UV_FILE) { + // Non-blocking file reads are simulated with an idle handle that reads in + // chunks of the ring buffer size, giving time for other events to be + // processed between reads. + uv_idle_init(&loop->uv, &stream->uv.idle); + stream->uv.idle.data = stream; + } else { + assert(type == UV_NAMED_PIPE || type == UV_TTY); + uv_pipe_init(&loop->uv, &stream->uv.pipe, 0); + uv_pipe_open(&stream->uv.pipe, fd); + stream->uvstream = (uv_stream_t *)&stream->uv.pipe; + } + } + + if (stream->uvstream) { + stream->uvstream->data = stream; + loop = stream->uvstream->loop->data; + } + + stream->data = data; + stream->internal_data = NULL; + stream->fpos = 0; + stream->curmem = 0; + stream->maxmem = 0; + stream->pending_reqs = 0; + stream->read_cb = NULL; + stream->write_cb = NULL; + stream->close_cb = NULL; + stream->internal_close_cb = NULL; + stream->closed = false; + stream->buffer = NULL; + stream->events = NULL; +} + +void stream_close(Stream *stream, stream_close_cb on_stream_close) + FUNC_ATTR_NONNULL_ARG(1) +{ + assert(!stream->closed); + stream->closed = true; + stream->close_cb = on_stream_close; + + if (!stream->pending_reqs) { + stream_close_handle(stream); + } +} + +void stream_close_handle(Stream *stream) + FUNC_ATTR_NONNULL_ALL +{ + if (stream->uvstream) { + uv_close((uv_handle_t *)stream->uvstream, close_cb); + } else { + uv_close((uv_handle_t *)&stream->uv.idle, close_cb); + } +} + +static void close_cb(uv_handle_t *handle) +{ + Stream *stream = handle->data; + if (stream->buffer) { + rbuffer_free(stream->buffer); + } + if (stream->close_cb) { + stream->close_cb(stream, stream->data); + } + if (stream->internal_close_cb) { + stream->internal_close_cb(stream, stream->internal_data); + } +} diff --git a/src/nvim/event/stream.h b/src/nvim/event/stream.h new file mode 100644 index 0000000000..c6baac0db7 --- /dev/null +++ b/src/nvim/event/stream.h @@ -0,0 +1,60 @@ +#ifndef NVIM_EVENT_STREAM_H +#define NVIM_EVENT_STREAM_H + +#include <stdbool.h> +#include <stddef.h> + +#include <uv.h> + +#include "nvim/event/loop.h" +#include "nvim/rbuffer.h" + +typedef struct stream Stream; +/// Type of function called when the Stream buffer is filled with data +/// +/// @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 data User-defined data +/// @param eof If the stream reached EOF. +typedef void (*stream_read_cb)(Stream *stream, RBuffer *buf, size_t count, + void *data, bool eof); + +/// Type of function called when the Stream has information about a write +/// request. +/// +/// @param wstream The Stream instance +/// @param data User-defined data +/// @param status 0 on success, anything else indicates failure +typedef void (*stream_write_cb)(Stream *stream, void *data, int status); +typedef void (*stream_close_cb)(Stream *stream, void *data); + +struct stream { + union { + uv_pipe_t pipe; + uv_tcp_t tcp; + uv_idle_t idle; + } uv; + uv_stream_t *uvstream; + uv_buf_t uvbuf; + RBuffer *buffer; + uv_file fd; + stream_read_cb read_cb; + stream_write_cb write_cb; + stream_close_cb close_cb, internal_close_cb; + size_t fpos; + size_t curmem; + size_t maxmem; + size_t pending_reqs; + void *data, *internal_data; + bool closed; + Queue *events; +}; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/stream.h.generated.h" +#endif +#endif // NVIM_EVENT_STREAM_H diff --git a/src/nvim/event/time.c b/src/nvim/event/time.c new file mode 100644 index 0000000000..7bf333bcea --- /dev/null +++ b/src/nvim/event/time.c @@ -0,0 +1,62 @@ +#include <stdint.h> + +#include <uv.h> + +#include "nvim/event/loop.h" +#include "nvim/event/time.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/time.c.generated.h" +#endif + + +void time_watcher_init(Loop *loop, TimeWatcher *watcher, void *data) + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_ARG(2) +{ + uv_timer_init(&loop->uv, &watcher->uv); + watcher->uv.data = watcher; + watcher->data = data; + watcher->events = loop->fast_events; +} + +void time_watcher_start(TimeWatcher *watcher, time_cb cb, uint64_t timeout, + uint64_t repeat) + FUNC_ATTR_NONNULL_ALL +{ + watcher->cb = cb; + uv_timer_start(&watcher->uv, time_watcher_cb, timeout, repeat); +} + +void time_watcher_stop(TimeWatcher *watcher) + FUNC_ATTR_NONNULL_ALL +{ + uv_timer_stop(&watcher->uv); +} + +void time_watcher_close(TimeWatcher *watcher, time_cb cb) + FUNC_ATTR_NONNULL_ARG(1) +{ + watcher->close_cb = cb; + uv_close((uv_handle_t *)&watcher->uv, close_cb); +} + +static void time_event(void **argv) +{ + TimeWatcher *watcher = argv[0]; + watcher->cb(watcher, watcher->data); +} + +static void time_watcher_cb(uv_timer_t *handle) + FUNC_ATTR_NONNULL_ALL +{ + TimeWatcher *watcher = handle->data; + CREATE_EVENT(watcher->events, time_event, 1, watcher); +} + +static void close_cb(uv_handle_t *handle) +{ + TimeWatcher *watcher = handle->data; + if (watcher->close_cb) { + watcher->close_cb(watcher, watcher->data); + } +} diff --git a/src/nvim/event/time.h b/src/nvim/event/time.h new file mode 100644 index 0000000000..7882b2b627 --- /dev/null +++ b/src/nvim/event/time.h @@ -0,0 +1,21 @@ +#ifndef NVIM_EVENT_TIME_H +#define NVIM_EVENT_TIME_H + +#include <uv.h> + +#include "nvim/event/loop.h" + +typedef struct time_watcher TimeWatcher; +typedef void (*time_cb)(TimeWatcher *watcher, void *data); + +struct time_watcher { + uv_timer_t uv; + void *data; + time_cb cb, close_cb; + Queue *events; +}; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/time.h.generated.h" +#endif +#endif // NVIM_EVENT_TIME_H diff --git a/src/nvim/event/uv_process.c b/src/nvim/event/uv_process.c new file mode 100644 index 0000000000..21c2fd1790 --- /dev/null +++ b/src/nvim/event/uv_process.c @@ -0,0 +1,77 @@ +#include <assert.h> + +#include <uv.h> + +#include "nvim/event/loop.h" +#include "nvim/event/rstream.h" +#include "nvim/event/wstream.h" +#include "nvim/event/process.h" +#include "nvim/event/uv_process.h" +#include "nvim/log.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/uv_process.c.generated.h" +#endif + +bool uv_process_spawn(UvProcess *uvproc) + FUNC_ATTR_NONNULL_ALL +{ + Process *proc = (Process *)uvproc; + uvproc->uvopts.file = proc->argv[0]; + uvproc->uvopts.args = proc->argv; + uvproc->uvopts.flags = UV_PROCESS_WINDOWS_HIDE; + uvproc->uvopts.exit_cb = exit_cb; + uvproc->uvopts.cwd = NULL; + uvproc->uvopts.env = NULL; + uvproc->uvopts.stdio = uvproc->uvstdio; + uvproc->uvopts.stdio_count = 3; + uvproc->uvstdio[0].flags = UV_IGNORE; + uvproc->uvstdio[1].flags = UV_IGNORE; + uvproc->uvstdio[2].flags = UV_IGNORE; + uvproc->uv.data = proc; + + if (proc->in) { + uvproc->uvstdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; + uvproc->uvstdio[0].data.stream = (uv_stream_t *)&proc->in->uv.pipe; + } + + if (proc->out) { + uvproc->uvstdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; + uvproc->uvstdio[1].data.stream = (uv_stream_t *)&proc->out->uv.pipe; + } + + if (proc->err) { + uvproc->uvstdio[2].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; + uvproc->uvstdio[2].data.stream = (uv_stream_t *)&proc->err->uv.pipe; + } + + int status; + if ((status = uv_spawn(&proc->loop->uv, &uvproc->uv, &uvproc->uvopts))) { + ELOG("uv_spawn failed: %s", uv_strerror(status)); + return false; + } + + proc->pid = uvproc->uv.pid; + return true; +} + +void uv_process_close(UvProcess *uvproc) + FUNC_ATTR_NONNULL_ARG(1) +{ + uv_close((uv_handle_t *)&uvproc->uv, close_cb); +} + +static void close_cb(uv_handle_t *handle) +{ + Process *proc = handle->data; + if (proc->internal_close_cb) { + proc->internal_close_cb(proc); + } +} + +static void exit_cb(uv_process_t *handle, int64_t status, int term_signal) +{ + Process *proc = handle->data; + proc->status = (int)status; + proc->internal_exit_cb(proc); +} diff --git a/src/nvim/event/uv_process.h b/src/nvim/event/uv_process.h new file mode 100644 index 0000000000..5ee73044b5 --- /dev/null +++ b/src/nvim/event/uv_process.h @@ -0,0 +1,25 @@ +#ifndef NVIM_EVENT_UV_PROCESS_H +#define NVIM_EVENT_UV_PROCESS_H + +#include <uv.h> + +#include "nvim/event/process.h" + +typedef struct uv_process { + Process process; + uv_process_t uv; + uv_process_options_t uvopts; + uv_stdio_container_t uvstdio[3]; +} UvProcess; + +static inline UvProcess uv_process_init(Loop *loop, void *data) +{ + UvProcess rv; + rv.process = process_init(loop, kProcessTypeUv, data); + return rv; +} + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/uv_process.h.generated.h" +#endif +#endif // NVIM_EVENT_UV_PROCESS_H diff --git a/src/nvim/event/wstream.c b/src/nvim/event/wstream.c new file mode 100644 index 0000000000..8028e35e6b --- /dev/null +++ b/src/nvim/event/wstream.c @@ -0,0 +1,164 @@ +#include <assert.h> +#include <stdint.h> +#include <stdbool.h> +#include <stdlib.h> + +#include <uv.h> + +#include "nvim/event/loop.h" +#include "nvim/event/wstream.h" +#include "nvim/vim.h" +#include "nvim/memory.h" + +#define DEFAULT_MAXMEM 1024 * 1024 * 10 + +typedef struct { + Stream *stream; + WBuffer *buffer; + uv_write_t uv_req; +} WRequest; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/wstream.c.generated.h" +#endif + +void wstream_init_fd(Loop *loop, Stream *stream, int fd, size_t maxmem, + void *data) + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_ARG(2) +{ + stream_init(loop, stream, fd, NULL, data); + wstream_init(stream, maxmem); +} + +void wstream_init_stream(Stream *stream, uv_stream_t *uvstream, size_t maxmem, + void *data) + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_ARG(2) +{ + stream_init(NULL, stream, -1, uvstream, data); + wstream_init(stream, maxmem); +} + +void wstream_init(Stream *stream, size_t maxmem) +{ + stream->maxmem = maxmem ? maxmem : DEFAULT_MAXMEM; +} + +/// Sets a callback that will be called on completion of a write request, +/// indicating failure/success. +/// +/// This affects all requests currently in-flight as well. Overwrites any +/// possible earlier callback. +/// +/// @note This callback will not fire if the write request couldn't even be +/// queued properly (i.e.: when `wstream_write() returns an error`). +/// +/// @param stream The `Stream` instance +/// @param cb The callback +void wstream_set_write_cb(Stream *stream, stream_write_cb cb) + FUNC_ATTR_NONNULL_ALL +{ + stream->write_cb = cb; +} + +/// Queues data for writing to the backing file descriptor of a `Stream` +/// instance. This will fail if the write would cause the Stream use more +/// memory than specified by `maxmem`. +/// +/// @param stream The `Stream` instance +/// @param buffer The buffer which contains data to be written +/// @return false if the write failed +bool wstream_write(Stream *stream, WBuffer *buffer) + FUNC_ATTR_NONNULL_ALL +{ + assert(stream->maxmem); + // This should not be called after a stream was freed + assert(!stream->closed); + + if (stream->curmem > stream->maxmem) { + goto err; + } + + stream->curmem += buffer->size; + + WRequest *data = xmalloc(sizeof(WRequest)); + data->stream = stream; + data->buffer = buffer; + data->uv_req.data = data; + + uv_buf_t uvbuf; + uvbuf.base = buffer->data; + uvbuf.len = buffer->size; + + if (uv_write(&data->uv_req, stream->uvstream, &uvbuf, 1, write_cb)) { + xfree(data); + goto err; + } + + stream->pending_reqs++; + return true; + +err: + wstream_release_wbuffer(buffer); + return false; +} + +/// Creates a WBuffer object for holding output data. Instances of this +/// object can be reused across Stream instances, and the memory is freed +/// automatically when no longer needed(it tracks the number of references +/// internally) +/// +/// @param data Data stored by the WBuffer +/// @param size The size of the data array +/// @param refcount The number of references for the WBuffer. This will be used +/// by Stream instances to decide when a WBuffer should be freed. +/// @param cb Pointer to function that will be responsible for freeing +/// the buffer data(passing 'free' will work as expected). +/// @return The allocated WBuffer instance +WBuffer *wstream_new_buffer(char *data, + size_t size, + size_t refcount, + wbuffer_data_finalizer cb) + FUNC_ATTR_NONNULL_ARG(1) +{ + WBuffer *rv = xmalloc(sizeof(WBuffer)); + rv->size = size; + rv->refcount = refcount; + rv->cb = cb; + rv->data = data; + + return rv; +} + +static void write_cb(uv_write_t *req, int status) +{ + WRequest *data = req->data; + + data->stream->curmem -= data->buffer->size; + + wstream_release_wbuffer(data->buffer); + + if (data->stream->write_cb) { + data->stream->write_cb(data->stream, data->stream->data, status); + } + + data->stream->pending_reqs--; + + if (data->stream->closed && data->stream->pending_reqs == 0) { + // Last pending write, free the stream; + stream_close_handle(data->stream); + } + + xfree(data); +} + +void wstream_release_wbuffer(WBuffer *buffer) + FUNC_ATTR_NONNULL_ALL +{ + if (!--buffer->refcount) { + if (buffer->cb) { + buffer->cb(buffer->data); + } + + xfree(buffer); + } +} diff --git a/src/nvim/event/wstream.h b/src/nvim/event/wstream.h new file mode 100644 index 0000000000..9008de0d97 --- /dev/null +++ b/src/nvim/event/wstream.h @@ -0,0 +1,24 @@ +#ifndef NVIM_EVENT_WSTREAM_H +#define NVIM_EVENT_WSTREAM_H + +#include <stdint.h> +#include <stdbool.h> + +#include <uv.h> + +#include "nvim/event/loop.h" +#include "nvim/event/stream.h" + +typedef struct wbuffer WBuffer; +typedef void (*wbuffer_data_finalizer)(void *data); + +struct wbuffer { + size_t size, refcount; + char *data; + wbuffer_data_finalizer cb; +}; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "event/wstream.h.generated.h" +#endif +#endif // NVIM_EVENT_WSTREAM_H diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index b4adef9235..cacef01b19 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -3472,18 +3472,8 @@ void do_sub(exarg_T *eap) } if (!eap->skip) { - /* In POSIX vi ":s/pat/%/" uses the previous subst. string. */ - if (STRCMP(sub, "%") == 0 - && vim_strchr(p_cpo, CPO_SUBPERCENT) != NULL) { - if (old_sub == NULL) { /* there is no previous command */ - EMSG(_(e_nopresub)); - return; - } - sub = old_sub; - } else { - xfree(old_sub); - old_sub = vim_strsave(sub); - } + xfree(old_sub); + old_sub = vim_strsave(sub); } } else if (!eap->skip) { /* use previous pattern and substitution */ if (old_sub == NULL) { /* there is no previous command */ @@ -6135,7 +6125,7 @@ char_u * sign_typenr2name(int typenr) /* * Undefine/free all signs. */ -void free_signs() +void free_signs(void) { while (first_sign != NULL) sign_undefine(first_sign, NULL); diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index 011b3ce611..5221554306 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -2678,12 +2678,6 @@ return { func='ex_ni', }, { - command='tearoff', - flags=bit.bor(NEEDARG, EXTRA, TRLBAR, NOTRLCOM, CMDWIN), - addr_type=ADDR_LINES, - func='ex_tearoff', - }, - { command='terminal', flags=bit.bor(BANG, FILES, CMDWIN), addr_type=ADDR_LINES, @@ -3171,12 +3165,6 @@ return { func='ex_previous', }, { - command='Print', - flags=bit.bor(RANGE, WHOLEFOLD, COUNT, EXFLAGS, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, - func='ex_print', - }, - { command='~', enum='CMD_tilde', flags=bit.bor(RANGE, WHOLEFOLD, EXTRA, CMDWIN, MODIFY), diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index e7029b8762..efa18aa681 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -1862,20 +1862,30 @@ void ex_listdo(exarg_T *eap) case CMD_argdo: i = eap->line1 - 1; break; - case CMD_bufdo: - i = eap->line1; - break; default: break; } + buf_T *buf = curbuf; /* set pcmark now */ - if (eap->cmdidx == CMD_bufdo) - goto_buffer(eap, DOBUF_FIRST, FORWARD, i); - else + if (eap->cmdidx == CMD_bufdo) { + /* Advance to the first listed buffer after "eap->line1". */ + for (buf = firstbuf; + buf != NULL && (buf->b_fnum < eap->line1 || !buf->b_p_bl); + buf = buf->b_next) { + if (buf->b_fnum > eap->line2) { + buf = NULL; + break; + } + } + if (buf != NULL) { + goto_buffer(eap, DOBUF_FIRST, FORWARD, buf->b_fnum); + } + } else { setpcmark(); + } listcmd_busy = TRUE; /* avoids setting pcmark below */ - while (!got_int) { + while (!got_int && buf != NULL) { if (eap->cmdidx == CMD_argdo) { /* go to argument "i" */ if (i == ARGCOUNT) @@ -2614,7 +2624,7 @@ char_u *get_scriptname(scid_T id) } # if defined(EXITFREE) -void free_scriptnames() +void free_scriptnames(void) { # define FREE_SCRIPTNAME(item) xfree((item)->sn_name) GA_DEEP_CLEAR(&script_items, scriptitem_T, FREE_SCRIPTNAME); @@ -2992,12 +3002,8 @@ void ex_checktime(exarg_T *eap) static char *get_locale_val(int what) { - char *loc; - - /* Obtain the locale value from the libraries. For DJGPP this is - * redefined and it doesn't use the arguments. */ - loc = setlocale(what, NULL); - + // Obtain the locale value from the libraries. + char *loc = setlocale(what, NULL); return loc; } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 35b62cdd47..3c57537397 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -73,8 +73,8 @@ #include "nvim/os/time.h" #include "nvim/ex_cmds_defs.h" #include "nvim/mouse.h" -#include "nvim/os/rstream.h" -#include "nvim/os/wstream.h" +#include "nvim/event/rstream.h" +#include "nvim/event/wstream.h" static int quitmore = 0; static int ex_pressedreturn = FALSE; @@ -144,7 +144,6 @@ struct dbg_stuff { #endif # define ex_gui ex_nogui -# define ex_tearoff ex_ni # define ex_popup ex_ni # define ex_simalt ex_ni # define gui_mch_find_dialog ex_ni @@ -2402,9 +2401,8 @@ static char_u *find_command(exarg_T *eap, int *full) break; } - /* Look for a user defined command as a last resort. Let ":Print" be - * overruled by a user defined command. */ - if ((eap->cmdidx == CMD_SIZE || eap->cmdidx == CMD_Print) + // Look for a user defined command as a last resort. + if ((eap->cmdidx == CMD_SIZE) && *eap->cmd >= 'A' && *eap->cmd <= 'Z') { /* User defined commands may contain digits. */ while (ASCII_ISALNUM(*p)) @@ -3276,7 +3274,7 @@ set_one_cmd_context ( case CMD_imenu: case CMD_inoremenu: case CMD_iunmenu: case CMD_cmenu: case CMD_cnoremenu: case CMD_cunmenu: case CMD_tmenu: case CMD_tunmenu: - case CMD_popup: case CMD_tearoff: case CMD_emenu: + case CMD_popup: case CMD_emenu: return set_context_in_menu_cmd(xp, cmd, arg, forceit); case CMD_colorscheme: @@ -6835,12 +6833,6 @@ void ex_cd(exarg_T *eap) { if (allbuf_locked()) return; - if (vim_strchr(p_cpo, CPO_CHDIR) != NULL && curbufIsChanged() - && !eap->forceit) { - EMSG(_( - "E747: Cannot change directory, buffer is modified (add ! to override)")); - return; - } /* ":cd -": Change to previous directory */ if (STRCMP(new_dir, "-") == 0) { @@ -7555,8 +7547,9 @@ static void ex_mkrc(exarg_T *eap) int vim_mkdir_emsg(char_u *name, int prot) { - if (os_mkdir((char *)name, prot) != 0) { - EMSG2(_("E739: Cannot create directory: %s"), name); + int ret; + if ((ret = os_mkdir((char *)name, prot)) != 0) { + EMSG3(_(e_mkdir), name, os_strerror(ret)); return FAIL; } return OK; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 3af035a6e3..03116d454f 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -62,8 +62,9 @@ #include "nvim/tag.h" #include "nvim/window.h" #include "nvim/ui.h" +#include "nvim/os/input.h" #include "nvim/os/os.h" -#include "nvim/os/event.h" +#include "nvim/event/loop.h" /* * Variables shared between getcmdline(), redrawcmdline() and others. @@ -298,14 +299,14 @@ getcmdline ( /* Get a character. Ignore K_IGNORE, it should not do anything, such * as stop completion. */ - event_enable_deferred(); + input_enable_events(); do { c = safe_vgetc(); } while (c == K_IGNORE); - event_disable_deferred(); + input_disable_events(); if (c == K_EVENT) { - event_process(); + queue_process_events(loop.events); continue; } diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 4dfa155e61..58e4873e00 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -415,14 +415,11 @@ readfile ( msg_scroll = TRUE; /* don't overwrite previous file message */ /* - * If the name ends in a path separator, we can't open it. Check here, - * because reading the file may actually work, but then creating the swap - * file may destroy it! Reported on MS-DOS and Win 95. * If the name is too long we might crash further on, quit here. */ if (fname != NULL && *fname != NUL) { p = fname + STRLEN(fname); - if (after_pathsep((char *)fname, (char *)p) || STRLEN(fname) >= MAXPATHL) { + if (STRLEN(fname) >= MAXPATHL) { filemess(curbuf, fname, (char_u *)_("Illegal file name"), 0); msg_end(); msg_scroll = msg_save; @@ -5121,116 +5118,9 @@ void forward_slash(char_u *fname) /* * Code for automatic commands. */ - - -static struct event_name { - char *name; /* event name */ - event_T event; /* event number */ -} event_names[] = -{ - {"BufAdd", EVENT_BUFADD}, - {"BufCreate", EVENT_BUFADD}, - {"BufDelete", EVENT_BUFDELETE}, - {"BufEnter", EVENT_BUFENTER}, - {"BufFilePost", EVENT_BUFFILEPOST}, - {"BufFilePre", EVENT_BUFFILEPRE}, - {"BufHidden", EVENT_BUFHIDDEN}, - {"BufLeave", EVENT_BUFLEAVE}, - {"BufNew", EVENT_BUFNEW}, - {"BufNewFile", EVENT_BUFNEWFILE}, - {"BufRead", EVENT_BUFREADPOST}, - {"BufReadCmd", EVENT_BUFREADCMD}, - {"BufReadPost", EVENT_BUFREADPOST}, - {"BufReadPre", EVENT_BUFREADPRE}, - {"BufUnload", EVENT_BUFUNLOAD}, - {"BufWinEnter", EVENT_BUFWINENTER}, - {"BufWinLeave", EVENT_BUFWINLEAVE}, - {"BufWipeout", EVENT_BUFWIPEOUT}, - {"BufWrite", EVENT_BUFWRITEPRE}, - {"BufWritePost", EVENT_BUFWRITEPOST}, - {"BufWritePre", EVENT_BUFWRITEPRE}, - {"BufWriteCmd", EVENT_BUFWRITECMD}, - {"CmdwinEnter", EVENT_CMDWINENTER}, - {"CmdwinLeave", EVENT_CMDWINLEAVE}, - {"CmdUndefined", EVENT_CMDUNDEFINED}, - {"ColorScheme", EVENT_COLORSCHEME}, - {"CompleteDone", EVENT_COMPLETEDONE}, - {"CursorHold", EVENT_CURSORHOLD}, - {"CursorHoldI", EVENT_CURSORHOLDI}, - {"CursorMoved", EVENT_CURSORMOVED}, - {"CursorMovedI", EVENT_CURSORMOVEDI}, - {"EncodingChanged", EVENT_ENCODINGCHANGED}, - {"FileEncoding", EVENT_ENCODINGCHANGED}, - {"FileAppendPost", EVENT_FILEAPPENDPOST}, - {"FileAppendPre", EVENT_FILEAPPENDPRE}, - {"FileAppendCmd", EVENT_FILEAPPENDCMD}, - {"FileChangedShell",EVENT_FILECHANGEDSHELL}, - {"FileChangedShellPost",EVENT_FILECHANGEDSHELLPOST}, - {"FileChangedRO", EVENT_FILECHANGEDRO}, - {"FileReadPost", EVENT_FILEREADPOST}, - {"FileReadPre", EVENT_FILEREADPRE}, - {"FileReadCmd", EVENT_FILEREADCMD}, - {"FileType", EVENT_FILETYPE}, - {"FileWritePost", EVENT_FILEWRITEPOST}, - {"FileWritePre", EVENT_FILEWRITEPRE}, - {"FileWriteCmd", EVENT_FILEWRITECMD}, - {"FilterReadPost", EVENT_FILTERREADPOST}, - {"FilterReadPre", EVENT_FILTERREADPRE}, - {"FilterWritePost", EVENT_FILTERWRITEPOST}, - {"FilterWritePre", EVENT_FILTERWRITEPRE}, - {"FocusGained", EVENT_FOCUSGAINED}, - {"FocusLost", EVENT_FOCUSLOST}, - {"FuncUndefined", EVENT_FUNCUNDEFINED}, - {"GUIEnter", EVENT_GUIENTER}, - {"GUIFailed", EVENT_GUIFAILED}, - {"InsertChange", EVENT_INSERTCHANGE}, - {"InsertEnter", EVENT_INSERTENTER}, - {"InsertLeave", EVENT_INSERTLEAVE}, - {"InsertCharPre", EVENT_INSERTCHARPRE}, - {"MenuPopup", EVENT_MENUPOPUP}, - {"QuickFixCmdPost", EVENT_QUICKFIXCMDPOST}, - {"QuickFixCmdPre", EVENT_QUICKFIXCMDPRE}, - {"QuitPre", EVENT_QUITPRE}, - {"RemoteReply", EVENT_REMOTEREPLY}, - {"SessionLoadPost", EVENT_SESSIONLOADPOST}, - {"ShellCmdPost", EVENT_SHELLCMDPOST}, - {"ShellFilterPost", EVENT_SHELLFILTERPOST}, - {"SourcePre", EVENT_SOURCEPRE}, - {"SourceCmd", EVENT_SOURCECMD}, - {"SpellFileMissing",EVENT_SPELLFILEMISSING}, - {"StdinReadPost", EVENT_STDINREADPOST}, - {"StdinReadPre", EVENT_STDINREADPRE}, - {"SwapExists", EVENT_SWAPEXISTS}, - {"Syntax", EVENT_SYNTAX}, - {"TabClosed", EVENT_TABCLOSED}, - {"TabEnter", EVENT_TABENTER}, - {"TabLeave", EVENT_TABLEAVE}, - {"TabNew", EVENT_TABNEW}, - {"TabNewEntered", EVENT_TABNEWENTERED}, - {"TermChanged", EVENT_TERMCHANGED}, - {"TermOpen", EVENT_TERMOPEN}, - {"TermResponse", EVENT_TERMRESPONSE}, - {"TextChanged", EVENT_TEXTCHANGED}, - {"TextChangedI", EVENT_TEXTCHANGEDI}, - {"User", EVENT_USER}, - {"VimEnter", EVENT_VIMENTER}, - {"VimLeave", EVENT_VIMLEAVE}, - {"VimLeavePre", EVENT_VIMLEAVEPRE}, - {"WinEnter", EVENT_WINENTER}, - {"WinLeave", EVENT_WINLEAVE}, - {"VimResized", EVENT_VIMRESIZED}, - {NULL, (event_T)0} -}; - -static AutoPat *first_autopat[NUM_EVENTS] = -{ - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL -}; +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "auevents_name_map.generated.h" +#endif static AutoPatCmd *active_apc_list = NULL; /* stack of active autocommands */ @@ -5526,7 +5416,7 @@ static event_T event_name2nr(char_u *start, char_u **end) for (p = start; *p && !ascii_iswhite(*p) && *p != ','; ++p) ; for (i = 0; event_names[i].name != NULL; ++i) { - len = (int)STRLEN(event_names[i].name); + len = (int) event_names[i].len; if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0) break; } diff --git a/src/nvim/fileio.h b/src/nvim/fileio.h index 3b37d359d7..d93f3f3eb3 100644 --- a/src/nvim/fileio.h +++ b/src/nvim/fileio.h @@ -13,100 +13,6 @@ #define READ_KEEP_UNDO 0x20 /* keep undo info*/ /* - * Events for autocommands. - */ -typedef enum auto_event { - EVENT_BUFADD = 0, /* after adding a buffer to the buffer list */ - EVENT_BUFNEW, /* after creating any buffer */ - EVENT_BUFDELETE, /* deleting a buffer from the buffer list */ - EVENT_BUFWIPEOUT, /* just before really deleting a buffer */ - EVENT_BUFENTER, /* after entering a buffer */ - EVENT_BUFFILEPOST, /* after renaming a buffer */ - EVENT_BUFFILEPRE, /* before renaming a buffer */ - EVENT_BUFLEAVE, /* before leaving a buffer */ - EVENT_BUFNEWFILE, /* when creating a buffer for a new file */ - EVENT_BUFREADPOST, /* after reading a buffer */ - EVENT_BUFREADPRE, /* before reading a buffer */ - EVENT_BUFREADCMD, /* read buffer using command */ - EVENT_BUFUNLOAD, /* just before unloading a buffer */ - EVENT_BUFHIDDEN, /* just after buffer becomes hidden */ - EVENT_BUFWINENTER, /* after showing a buffer in a window */ - EVENT_BUFWINLEAVE, /* just after buffer removed from window */ - EVENT_BUFWRITEPOST, /* after writing a buffer */ - EVENT_BUFWRITEPRE, /* before writing a buffer */ - EVENT_BUFWRITECMD, /* write buffer using command */ - EVENT_CMDWINENTER, /* after entering the cmdline window */ - EVENT_CMDWINLEAVE, /* before leaving the cmdline window */ - EVENT_COLORSCHEME, /* after loading a colorscheme */ - EVENT_COMPLETEDONE, /* after finishing insert complete */ - EVENT_FILEAPPENDPOST, /* after appending to a file */ - EVENT_FILEAPPENDPRE, /* before appending to a file */ - EVENT_FILEAPPENDCMD, /* append to a file using command */ - EVENT_FILECHANGEDSHELL, /* after shell command that changed file */ - EVENT_FILECHANGEDSHELLPOST, /* after (not) reloading changed file */ - EVENT_FILECHANGEDRO, /* before first change to read-only file */ - EVENT_FILEREADPOST, /* after reading a file */ - EVENT_FILEREADPRE, /* before reading a file */ - EVENT_FILEREADCMD, /* read from a file using command */ - EVENT_FILETYPE, /* new file type detected (user defined) */ - EVENT_FILEWRITEPOST, /* after writing a file */ - EVENT_FILEWRITEPRE, /* before writing a file */ - EVENT_FILEWRITECMD, /* write to a file using command */ - EVENT_FILTERREADPOST, /* after reading from a filter */ - EVENT_FILTERREADPRE, /* before reading from a filter */ - EVENT_FILTERWRITEPOST, /* after writing to a filter */ - EVENT_FILTERWRITEPRE, /* before writing to a filter */ - EVENT_FOCUSGAINED, /* got the focus */ - EVENT_FOCUSLOST, /* lost the focus to another app */ - EVENT_GUIENTER, /* after starting the GUI */ - EVENT_GUIFAILED, /* after starting the GUI failed */ - EVENT_INSERTCHANGE, /* when changing Insert/Replace mode */ - EVENT_INSERTENTER, /* when entering Insert mode */ - EVENT_INSERTLEAVE, /* when leaving Insert mode */ - EVENT_MENUPOPUP, /* just before popup menu is displayed */ - EVENT_QUICKFIXCMDPOST, /* after :make, :grep etc. */ - EVENT_QUICKFIXCMDPRE, /* before :make, :grep etc. */ - EVENT_QUITPRE, /* before :quit */ - EVENT_SESSIONLOADPOST, /* after loading a session file */ - EVENT_STDINREADPOST, /* after reading from stdin */ - EVENT_STDINREADPRE, /* before reading from stdin */ - EVENT_SYNTAX, /* syntax selected */ - EVENT_TERMCHANGED, /* after changing 'term' */ - EVENT_TERMRESPONSE, /* after setting "v:termresponse" */ - EVENT_USER, /* user defined autocommand */ - EVENT_VIMENTER, /* after starting Vim */ - EVENT_VIMLEAVE, /* before exiting Vim */ - EVENT_VIMLEAVEPRE, /* before exiting Vim and writing .viminfo */ - EVENT_VIMRESIZED, /* after Vim window was resized */ - EVENT_WINENTER, /* after entering a window */ - EVENT_WINLEAVE, /* before leaving a window */ - EVENT_ENCODINGCHANGED, /* after changing the 'encoding' option */ - EVENT_INSERTCHARPRE, /* before inserting a char */ - EVENT_CURSORHOLD, /* cursor in same position for a while */ - EVENT_CURSORHOLDI, /* idem, in Insert mode */ - EVENT_FUNCUNDEFINED, /* if calling a function which doesn't exist */ - EVENT_REMOTEREPLY, /* upon string reception from a remote vim */ - EVENT_SWAPEXISTS, /* found existing swap file */ - EVENT_SOURCEPRE, /* before sourcing a Vim script */ - EVENT_SOURCECMD, /* sourcing a Vim script using command */ - EVENT_SPELLFILEMISSING, /* spell file missing */ - EVENT_CURSORMOVED, /* cursor was moved */ - EVENT_CURSORMOVEDI, /* cursor was moved in Insert mode */ - EVENT_TABCLOSED, /* a tab has closed */ - EVENT_TABLEAVE, /* before leaving a tab page */ - EVENT_TABENTER, /* after entering a tab page */ - EVENT_TABNEW, /* when creating a new tab */ - EVENT_TABNEWENTERED, /* after entering a new tab */ - EVENT_SHELLCMDPOST, /* after ":!cmd" */ - EVENT_SHELLFILTERPOST, /* after ":1,2!cmd", ":w !cmd", ":r !cmd". */ - EVENT_TERMOPEN, // after opening a terminal buffer - EVENT_TEXTCHANGED, /* text was modified */ - EVENT_TEXTCHANGEDI, /* text was modified in Insert mode*/ - EVENT_CMDUNDEFINED, ///< command undefined - NUM_EVENTS /* MUST be the last one */ -} event_T; - -/* * Struct to save values in before executing autocommands for a buffer that is * not the current buffer. */ @@ -120,6 +26,8 @@ typedef struct { } aco_save_T; #ifdef INCLUDE_GENERATED_DECLARATIONS +// Events for autocommands +# include "auevents_enum.generated.h" # include "fileio.h.generated.h" #endif #endif // NVIM_FILEIO_H diff --git a/src/nvim/func_attr.h b/src/nvim/func_attr.h index 519f61c763..c31d21ec6d 100644 --- a/src/nvim/func_attr.h +++ b/src/nvim/func_attr.h @@ -179,7 +179,7 @@ #endif #ifdef DEFINE_FUNC_ATTRIBUTES - #define FUNC_ATTR_DEFERRED + #define FUNC_ATTR_ASYNC #define FUNC_ATTR_MALLOC REAL_FATTR_MALLOC #define FUNC_ATTR_ALLOC_SIZE(x) REAL_FATTR_ALLOC_SIZE(x) #define FUNC_ATTR_ALLOC_SIZE_PROD(x,y) REAL_FATTR_ALLOC_SIZE_PROD(x,y) diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 864aa6a622..6a6e4f2214 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -49,7 +49,7 @@ #include "nvim/strings.h" #include "nvim/ui.h" #include "nvim/undo.h" -#include "nvim/os/event.h" +#include "nvim/event/loop.h" #include "nvim/os/input.h" #include "nvim/os/os.h" @@ -1758,7 +1758,7 @@ static int vgetorpeek(int advance) if (c1 == K_SPECIAL) nolmaplen = 2; else { - LANGMAP_ADJUST(c1, (State & INSERT) == 0); + LANGMAP_ADJUST(c1, (State & (CMDLINE | INSERT)) == 0); nolmaplen = 0; } /* First try buffer-local mappings. */ diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 1d93900a94..68cb923e42 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -28,6 +28,7 @@ #include "nvim/menu.h" #include "nvim/syntax_defs.h" #include "nvim/types.h" +#include "nvim/event/loop.h" /* * definition of global variables @@ -58,7 +59,7 @@ /* Values for "starting" */ #define NO_SCREEN 2 /* no screen updating yet */ #define NO_BUFFERS 1 /* not all buffers loaded yet */ -/* 0 not starting anymore */ +/* 0 not starting anymore */ /* * Number of Rows and Columns in the screen. @@ -67,12 +68,16 @@ * They may have different values when the screen wasn't (re)allocated yet * after setting Rows or Columns (e.g., when starting up). */ + +#define DFLT_COLS 80 /* default value for 'columns' */ +#define DFLT_ROWS 24 /* default value for 'lines' */ + EXTERN long Rows /* nr of rows in the screen */ #ifdef DO_INIT - = 24L + = DFLT_ROWS #endif ; -EXTERN long Columns INIT(= 80); /* nr of columns in the screen */ +EXTERN long Columns INIT(= DFLT_COLS); /* nr of columns in the screen */ /* * The characters and attributes cached for the screen. @@ -893,14 +898,6 @@ EXTERN FILE *scriptout INIT(= NULL); /* stream to write script to */ /* volatile because it is used in signal handler catch_sigint(). */ EXTERN volatile int got_int INIT(= FALSE); /* set to TRUE when interrupt signal occurred */ -EXTERN int disable_breakcheck INIT(= 0); // > 0 if breakchecks should be - // ignored. FIXME(tarruda): Hacky - // way to run functions that would - // result in *_breakcheck calls - // while events that would normally - // be deferred are being processed - // immediately. Ref: - // neovim/neovim#2371 EXTERN int bangredo INIT(= FALSE); /* set to TRUE with ! command */ EXTERN int searchcmdlen; /* length of previous search cmd */ EXTERN int reg_do_extmatch INIT(= 0); /* Used when compiling regexp: @@ -1112,6 +1109,7 @@ EXTERN char_u e_jobtblfull[] INIT(= N_("E901: Job table is full")); EXTERN char_u e_jobexe[] INIT(= N_("E902: \"%s\" is not an executable")); EXTERN char_u e_jobnotpty[] INIT(= N_("E904: Job is not connected to a pty")); 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")); EXTERN char_u e_marknotset[] INIT(= N_("E20: Mark not set")); EXTERN char_u e_modifiable[] INIT(= N_( @@ -1216,6 +1214,7 @@ EXTERN char *ignoredp; // If a msgpack-rpc channel should be started over stdin/stdout EXTERN bool embedded_mode INIT(= false); +EXTERN Loop loop; /// Used to track the status of external functions. /// Currently only used for iconv(). diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c index 99926ecf16..d6c5cf4fd5 100644 --- a/src/nvim/if_cscope.c +++ b/src/nvim/if_cscope.c @@ -31,6 +31,7 @@ #include "nvim/window.h" #include "nvim/os/os.h" #include "nvim/os/input.h" +#include "nvim/event/stream.h" #include <sys/types.h> #include <sys/stat.h> diff --git a/src/nvim/keymap.h b/src/nvim/keymap.h index 022d27fda1..38b96b1b8c 100644 --- a/src/nvim/keymap.h +++ b/src/nvim/keymap.h @@ -83,11 +83,6 @@ #define KS_SELECT 245 #define K_SELECT_STRING (char_u *)"\200\365X" -/* - * Used for tearing off a menu. - */ -#define KS_TEAROFF 244 - /* Used a termcap entry that produces a normal character. */ #define KS_KEY 242 @@ -396,7 +391,6 @@ enum key_extra { #define K_HOR_SCROLLBAR TERMCAP2KEY(KS_HOR_SCROLLBAR, KE_FILLER) #define K_SELECT TERMCAP2KEY(KS_SELECT, KE_FILLER) -#define K_TEAROFF TERMCAP2KEY(KS_TEAROFF, KE_FILLER) #define K_TABLINE TERMCAP2KEY(KS_TABLINE, KE_FILLER) #define K_TABMENU TERMCAP2KEY(KS_TABMENU, KE_FILLER) diff --git a/src/nvim/lib/klist.h b/src/nvim/lib/klist.h index 7df809f07b..1280a927e8 100644 --- a/src/nvim/lib/klist.h +++ b/src/nvim/lib/klist.h @@ -27,10 +27,12 @@ #define _AC_KLIST_H #include <stdlib.h> +#include <assert.h> #include "nvim/memory.h" #include "nvim/func_attr.h" + #define KMEMPOOL_INIT(name, kmptype_t, kmpfree_f) \ typedef struct { \ size_t cnt, n, max; \ @@ -95,23 +97,27 @@ kmp_free(name, kl->mp, p); \ kmp_free(name, kl->mp, p); \ kmp_destroy(name, kl->mp); \ - xfree(kl); \ + xfree(kl); \ } \ - static inline kltype_t *kl_pushp_##name(kl_##name##_t *kl) { \ + static inline void kl_push_##name(kl_##name##_t *kl, kltype_t d) { \ kl1_##name *q, *p = kmp_alloc(name, kl->mp); \ q = kl->tail; p->next = 0; kl->tail->next = p; kl->tail = p; \ ++kl->size; \ - return &q->data; \ + q->data = d; \ } \ - static inline int kl_shift_##name(kl_##name##_t *kl, kltype_t *d) { \ + static inline kltype_t kl_shift_at_##name(kl_##name##_t *kl, \ + kl1_##name **n) { \ + assert((*n)->next); \ kl1_##name *p; \ - if (kl->head->next == 0) return -1; \ --kl->size; \ - p = kl->head; kl->head = kl->head->next; \ - if (d) *d = p->data; \ + p = *n; \ + *n = (*n)->next; \ + if (p == kl->head) kl->head = *n; \ + kltype_t d = p->data; \ kmp_free(name, kl->mp, p); \ - return 0; \ - } + return d; \ + } \ + #define kliter_t(name) kl1_##name #define klist_t(name) kl_##name##_t @@ -122,7 +128,14 @@ #define kl_init(name) kl_init_##name() #define kl_destroy(name, kl) kl_destroy_##name(kl) -#define kl_pushp(name, kl) kl_pushp_##name(kl) -#define kl_shift(name, kl, d) kl_shift_##name(kl, d) +#define kl_push(name, kl, d) kl_push_##name(kl, d) +#define kl_shift_at(name, kl, node) kl_shift_at_##name(kl, node) +#define kl_shift(name, kl) kl_shift_at(name, kl, &kl->head) #define kl_empty(kl) ((kl)->size == 0) +// Iteration macros. It's ok to modify the list while iterating as long as a +// `break` statement is executed before the next iteration. +#define kl_iter(name, kl, p) kl_iter_at(name, kl, p, NULL) +#define kl_iter_at(name, kl, p, h) \ + for (kl1_##name **p = h ? h : &kl->head; *p != kl->tail; p = &(*p)->next) + #endif diff --git a/src/nvim/lib/queue.h b/src/nvim/lib/queue.h new file mode 100644 index 0000000000..fe02b454ea --- /dev/null +++ b/src/nvim/lib/queue.h @@ -0,0 +1,92 @@ +/* Copyright (c) 2013, Ben Noordhuis <info@bnoordhuis.nl> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef QUEUE_H_ +#define QUEUE_H_ + +typedef void *QUEUE[2]; + +/* Private macros. */ +#define QUEUE_NEXT(q) (*(QUEUE **) &((*(q))[0])) +#define QUEUE_PREV(q) (*(QUEUE **) &((*(q))[1])) +#define QUEUE_PREV_NEXT(q) (QUEUE_NEXT(QUEUE_PREV(q))) +#define QUEUE_NEXT_PREV(q) (QUEUE_PREV(QUEUE_NEXT(q))) + +/* Public macros. */ +#define QUEUE_DATA(ptr, type, field) \ + ((type *) ((char *) (ptr) - ((char *) &((type *) 0)->field))) + +#define QUEUE_FOREACH(q, h) \ + for ((q) = QUEUE_NEXT(h); (q) != (h); (q) = QUEUE_NEXT(q)) + +#define QUEUE_EMPTY(q) \ + ((const QUEUE *) (q) == (const QUEUE *) QUEUE_NEXT(q)) + +#define QUEUE_HEAD(q) \ + (QUEUE_NEXT(q)) + +#define QUEUE_INIT(q) \ + do { \ + QUEUE_NEXT(q) = (q); \ + QUEUE_PREV(q) = (q); \ + } \ + while (0) + +#define QUEUE_ADD(h, n) \ + do { \ + QUEUE_PREV_NEXT(h) = QUEUE_NEXT(n); \ + QUEUE_NEXT_PREV(n) = QUEUE_PREV(h); \ + QUEUE_PREV(h) = QUEUE_PREV(n); \ + QUEUE_PREV_NEXT(h) = (h); \ + } \ + while (0) + +#define QUEUE_SPLIT(h, q, n) \ + do { \ + QUEUE_PREV(n) = QUEUE_PREV(h); \ + QUEUE_PREV_NEXT(n) = (n); \ + QUEUE_NEXT(n) = (q); \ + QUEUE_PREV(h) = QUEUE_PREV(q); \ + QUEUE_PREV_NEXT(h) = (h); \ + QUEUE_PREV(q) = (n); \ + } \ + while (0) + +#define QUEUE_INSERT_HEAD(h, q) \ + do { \ + QUEUE_NEXT(q) = QUEUE_NEXT(h); \ + QUEUE_PREV(q) = (h); \ + QUEUE_NEXT_PREV(q) = (q); \ + QUEUE_NEXT(h) = (q); \ + } \ + while (0) + +#define QUEUE_INSERT_TAIL(h, q) \ + do { \ + QUEUE_NEXT(q) = (h); \ + QUEUE_PREV(q) = QUEUE_PREV(h); \ + QUEUE_PREV_NEXT(q) = (q); \ + QUEUE_PREV(h) = (q); \ + } \ + while (0) + +#define QUEUE_REMOVE(q) \ + do { \ + QUEUE_PREV_NEXT(q) = QUEUE_NEXT(q); \ + QUEUE_NEXT_PREV(q) = QUEUE_PREV(q); \ + } \ + while (0) + +#endif /* QUEUE_H_ */ diff --git a/src/nvim/main.c b/src/nvim/main.c index e1bb2d0b66..e11db16c61 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -61,9 +61,13 @@ #include "nvim/os/input.h" #include "nvim/os/os.h" #include "nvim/os/time.h" -#include "nvim/os/event.h" +#include "nvim/event/loop.h" #include "nvim/os/signal.h" +#include "nvim/event/process.h" +#include "nvim/msgpack_rpc/defs.h" #include "nvim/msgpack_rpc/helpers.h" +#include "nvim/msgpack_rpc/server.h" +#include "nvim/msgpack_rpc/channel.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/api/private/handle.h" @@ -133,11 +137,47 @@ static const char *err_extra_cmd = N_("Too many \"+command\", \"-c command\" or \"--cmd command\" arguments"); +void event_init(void) +{ + loop_init(&loop, NULL); + // early msgpack-rpc initialization + msgpack_rpc_init_method_table(); + msgpack_rpc_helpers_init(); + // Initialize input events + input_init(); + // Timer to wake the event loop if a timeout argument is passed to + // `event_poll` + // Signals + signal_init(); + // finish mspgack-rpc initialization + channel_init(); + server_init(); + terminal_init(); +} + +void event_teardown(void) +{ + if (!loop.events) { + return; + } + + queue_process_events(loop.events); + input_stop(); + channel_teardown(); + process_teardown(&loop); + server_teardown(); + signal_teardown(); + terminal_teardown(); + + loop_close(&loop); +} + /// Performs early initialization. /// /// Needed for unit tests. Must be called after `time_init()`. void early_init(void) { + fs_init(); handle_init(); (void)mb_init(); // init mb_bytelen_tab[] to ones @@ -481,6 +521,11 @@ int main(int argc, char **argv) if (restart_edit != 0) stuffcharReadbuff(K_NOP); + // WORKAROUND(mhi): #3023 + if (cb_flags & CB_UNNAMEDMASK) { + (void)eval_has_provider("clipboard"); + } + TIME_MSG("before starting main loop"); /* diff --git a/src/nvim/map.c b/src/nvim/map.c index 5d83020619..ed7bda4cce 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -116,5 +116,5 @@ MAP_IMPL(cstr_t, uint64_t, DEFAULT_INITIALIZER) MAP_IMPL(cstr_t, ptr_t, DEFAULT_INITIALIZER) MAP_IMPL(ptr_t, ptr_t, DEFAULT_INITIALIZER) MAP_IMPL(uint64_t, ptr_t, DEFAULT_INITIALIZER) -#define MSGPACK_HANDLER_INITIALIZER {.fn = NULL, .defer = false} +#define MSGPACK_HANDLER_INITIALIZER {.fn = NULL, .async = false} MAP_IMPL(String, MsgpackRpcRequestHandler, MSGPACK_HANDLER_INITIALIZER) diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 0e415b6e8c..d90e91be5d 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -78,7 +78,7 @@ #include "nvim/os/os.h" #include "nvim/os/input.h" -#ifndef UNIX /* it's in os_unix_defs.h for Unix */ +#ifndef UNIX /* it's in os/unix_defs.h for Unix */ # include <time.h> #endif @@ -587,8 +587,7 @@ void ml_close(buf_T *buf, int del_file) void ml_close_all(int del_file) { FOR_ALL_BUFFERS(buf) { - ml_close(buf, del_file && ((buf->b_flags & BF_PRESERVED) == 0 - || vim_strchr(p_cpo, CPO_PRESERVE) == NULL)); + ml_close(buf, del_file && ((buf->b_flags & BF_PRESERVED) == 0)); } spell_delete_wordlist(); /* delete the internal wordlist */ vim_deltempdir(); /* delete created temp directory */ @@ -3935,8 +3934,9 @@ long ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp) size += lnum - 1; /* Don't count the last line break if 'bin' and 'noeol'. */ - if (buf->b_p_bin && !buf->b_p_eol) + if (buf->b_p_bin && !buf->b_p_eol && buf->b_ml.ml_line_count == lnum) { size -= ffdos + 1; + } } return size; diff --git a/src/nvim/memory.h b/src/nvim/memory.h index 4ff31ff732..7b477da2f5 100644 --- a/src/nvim/memory.h +++ b/src/nvim/memory.h @@ -1,6 +1,7 @@ #ifndef NVIM_MEMORY_H #define NVIM_MEMORY_H +#include <stdint.h> // for uint8_t #include <stddef.h> // for size_t #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/menu.c b/src/nvim/menu.c index 0db0ddffb8..d965de6019 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -417,9 +417,8 @@ add_menu_path ( /* free any old menu */ free_menu_string(menu, i); - /* For "amenu", may insert an extra character. - * Don't do this if adding a tearbar (addtearoff == FALSE). - * Don't do this for "<Nop>". */ + // For "amenu", may insert an extra character. + // Don't do this for "<Nop>". c = 0; d = 0; if (amenu && call_data != NULL && *call_data != NUL @@ -461,12 +460,6 @@ add_menu_path ( menu->silent[i] = menuarg->silent[0]; } } -#if defined(FEAT_TOOLBAR) && !defined(FEAT_GUI_W32) \ - && (defined(FEAT_BEVAL) || defined(FEAT_GUI_GTK)) - /* Need to update the menu tip. */ - if (modes & MENU_TIP_MODE) - gui_mch_menu_set_tip(menu); -#endif } return OK; @@ -571,16 +564,6 @@ remove_menu ( return FAIL; } if ((menu->modes & modes) != 0x0) { -#if defined(FEAT_GUI_W32) & defined(FEAT_TEAROFF) - /* - * If we are removing all entries for this menu,MENU_ALL_MODES, - * Then kill any tearoff before we start - */ - if (*p == NUL && modes == MENU_ALL_MODES) { - if (IsWindow(menu->tearoff_handle)) - DestroyWindow(menu->tearoff_handle); - } -#endif if (remove_menu(&menu->children, p, modes, silent) == FAIL) return FAIL; } else if (*name != NUL) { @@ -619,29 +602,14 @@ remove_menu ( /* Recalculate modes for menu based on the new updated children */ menu->modes &= ~modes; -#if defined(FEAT_GUI_W32) & defined(FEAT_TEAROFF) - if ((s_tearoffs) && (menu->children != NULL)) /* there's a tear bar.. */ - child = menu->children->next; /* don't count tearoff bar */ - else -#endif child = menu->children; for (; child != NULL; child = child->next) menu->modes |= child->modes; if (modes & MENU_TIP_MODE) { free_menu_string(menu, MENU_INDEX_TIP); -#if defined(FEAT_TOOLBAR) && !defined(FEAT_GUI_W32) \ - && (defined(FEAT_BEVAL) || defined(FEAT_GUI_GTK)) - /* Need to update the menu tip. */ - if (gui.in_use) - gui_mch_menu_set_tip(menu); -#endif } if ((menu->modes & MENU_ALL_MODES) == 0) { /* The menu item is no longer valid in ANY mode, so delete it */ -#if defined(FEAT_GUI_W32) & defined(FEAT_TEAROFF) - if (s_tearoffs && menu->children != NULL) /* there's a tear bar.. */ - free_menu(&menu->children); -#endif *menup = menu; free_menu(menup); } @@ -862,7 +830,7 @@ char_u *set_context_in_menu_cmd(expand_T *xp, char_u *cmd, char_u *arg, int forc after_dot = p + 1; } - /* ":tearoff" and ":popup" only use menus, not entries */ + // ":popup" only uses menues, not entries expand_menus = !((*cmd == 't' && cmd[1] == 'e') || *cmd == 'p'); expand_emenu = (*cmd == 'e'); if (expand_menus && ascii_iswhite(*p)) @@ -938,7 +906,6 @@ char_u *get_menu_name(expand_T *xp, int idx) /* Skip PopUp[nvoci]. */ while (menu != NULL && (menu_is_hidden(menu->dname) || menu_is_separator(menu->dname) - || menu_is_tearoff(menu->dname) || menu->children == NULL)) menu = menu->next; @@ -986,7 +953,6 @@ char_u *get_menu_names(expand_T *xp, int idx) while (menu != NULL && ( menu_is_hidden(menu->dname) || (expand_emenu && menu_is_separator(menu->dname)) - || menu_is_tearoff(menu->dname) || menu->dname[STRLEN(menu->dname) - 1] == '.' )) menu = menu->next; @@ -1245,16 +1211,6 @@ static int menu_is_hidden(char_u *name) } /* - * Return TRUE if the menu is the tearoff menu. - */ -static int menu_is_tearoff(char_u *name) -{ - return FALSE; -} - - - -/* * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy and * execute it. */ @@ -1301,11 +1257,15 @@ void ex_emenu(exarg_T *eap) /* Found the menu, so execute. * Use the Insert mode entry when returning to Insert mode. */ - if (restart_edit - && !current_SID - ) { + if (((State & INSERT) || restart_edit) && !current_SID) { mode = (char_u *)"Insert"; idx = MENU_INDEX_INSERT; + } else if (get_real_state() & VISUAL) { + /* Detect real visual mode -- if we are really in visual mode we + * don't need to do any guesswork to figure out what the selection + * is. Just execute the visual binding for the menu. */ + mode = (char_u *)"Visual"; + idx = MENU_INDEX_VISUAL; } else if (eap->addr_count) { pos_T tpos; @@ -1364,61 +1324,6 @@ void ex_emenu(exarg_T *eap) EMSG2(_("E335: Menu not defined for %s mode"), mode); } -#if defined(FEAT_GUI_MSWIN) \ - || defined(FEAT_GUI_GTK) \ - || defined(FEAT_BEVAL_TIP) -/* - * Given a menu descriptor, e.g. "File.New", find it in the menu hierarchy. - */ -vimmenu_T *gui_find_menu(char_u *path_name) -{ - vimmenu_T *menu = NULL; - char_u *name; - char_u *saved_name; - char_u *p; - - menu = root_menu; - - saved_name = vim_strsave(path_name); - - name = saved_name; - while (*name) { - /* find the end of one dot-separated name and put a NUL at the dot */ - p = menu_name_skip(name); - - while (menu != NULL) { - if (menu_name_equal(name, menu)) { - if (menu->children == NULL) { - /* found a menu item instead of a sub-menu */ - if (*p == NUL) - EMSG(_("E336: Menu path must lead to a sub-menu")); - else - EMSG(_(e_notsubmenu)); - menu = NULL; - goto theend; - } - if (*p == NUL) /* found a full match */ - goto theend; - break; - } - menu = menu->next; - } - if (menu == NULL) /* didn't find it */ - break; - - /* Found a match, search the sub-menu. */ - menu = menu->children; - name = p; - } - - if (menu == NULL) - EMSG(_("E337: Menu not found - check menu names")); -theend: - xfree(saved_name); - return menu; -} -#endif - /* * Translation of menu names. Just a simple lookup table. */ diff --git a/src/nvim/message.c b/src/nvim/message.c index 5b4c90cc8f..8263ff4896 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -2234,15 +2234,11 @@ void mch_errmsg(char *str) { int len; -#if (defined(UNIX) || defined(FEAT_GUI)) && !defined(ALWAYS_USE_GUI) +#ifdef UNIX /* On Unix use stderr if it's a tty. * When not going to start the GUI also use stderr. * On Mac, when started from Finder, stderr is the console. */ - if ( -# ifdef UNIX - isatty(2) -# endif - ) { + if (os_isatty(2)) { fprintf(stderr, "%s", str); return; } @@ -2284,16 +2280,12 @@ void mch_errmsg(char *str) */ void mch_msg(char *str) { -#if (defined(UNIX) || defined(FEAT_GUI)) && !defined(ALWAYS_USE_GUI) +#ifdef UNIX /* On Unix use stdout if we have a tty. This allows "vim -h | more" and * uses mch_errmsg() when started from the desktop. * When not going to start the GUI also use stdout. * On Mac, when started from Finder, stderr is the console. */ - if ( -# ifdef UNIX - isatty(2) -# endif - ) { + if (os_isatty(2)) { printf("%s", str); return; } diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index 0737caec5d..4f23b3da63 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -61,6 +61,7 @@ #include "nvim/os/shell.h" #include "nvim/os/input.h" #include "nvim/os/time.h" +#include "nvim/event/stream.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "misc1.c.generated.h" diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index df78f822d6..0e3b8200c9 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -9,13 +9,11 @@ #include "nvim/api/vim.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/msgpack_rpc/remote_ui.h" -#include "nvim/os/event.h" -#include "nvim/os/rstream.h" -#include "nvim/os/rstream_defs.h" -#include "nvim/os/wstream.h" -#include "nvim/os/wstream_defs.h" -#include "nvim/os/job.h" -#include "nvim/os/job_defs.h" +#include "nvim/event/loop.h" +#include "nvim/event/uv_process.h" +#include "nvim/event/rstream.h" +#include "nvim/event/wstream.h" +#include "nvim/event/socket.h" #include "nvim/msgpack_rpc/helpers.h" #include "nvim/vim.h" #include "nvim/ascii.h" @@ -34,6 +32,12 @@ #define log_server_msg(...) #endif +typedef enum { + kChannelTypeSocket, + kChannelTypeProc, + kChannelTypeStdio +} ChannelType; + typedef struct { uint64_t request_id; bool returned, errored; @@ -45,19 +49,26 @@ typedef struct { size_t refcount; size_t pending_requests; PMap(cstr_t) *subscribed_events; - bool is_job, closed; + bool closed; + ChannelType type; msgpack_unpacker *unpacker; union { - Job *job; + Stream stream; + struct { + UvProcess uvproc; + Stream in; + Stream out; + Stream err; + } process; struct { - RStream *read; - WStream *write; - uv_stream_t *uv; - } streams; + Stream in; + Stream out; + } std; } data; uint64_t next_request_id; kvec_t(ChannelCallFrame *) call_stack; kvec_t(WBuffer *) delayed_notifications; + Queue *events; } Channel; typedef struct { @@ -104,57 +115,51 @@ void channel_teardown(void) }); } -/// Creates an API channel by starting a job and connecting to its +/// Creates an API channel by starting a process and connecting to its /// stdin/stdout. stderr is forwarded to the editor error stream. /// /// @param argv The argument vector for the process. [consumed] /// @return The channel id (> 0), on success. /// 0, on error. -uint64_t channel_from_job(char **argv) -{ - Channel *channel = register_channel(); - channel->is_job = true; - incref(channel); // job channels are only closed by the exit_cb - - int status; - JobOptions opts = JOB_OPTIONS_INIT; - opts.argv = argv; - opts.data = channel; - opts.stdout_cb = job_out; - opts.stderr_cb = job_err; - opts.exit_cb = job_exit; - channel->data.job = job_start(opts, &status); - - if (status <= 0) { - if (status == 0) { // Two decrefs needed if status == 0. - decref(channel); // Only one needed if status < 0, - } // because exit_cb will do the second one. +uint64_t channel_from_process(char **argv) +{ + Channel *channel = register_channel(kChannelTypeProc); + channel->data.process.uvproc = uv_process_init(&loop, channel); + Process *proc = &channel->data.process.uvproc.process; + proc->argv = argv; + proc->in = &channel->data.process.in; + proc->out = &channel->data.process.out; + proc->err = &channel->data.process.err; + proc->cb = process_exit; + if (!process_spawn(proc)) { + loop_poll_events(&loop, 0); decref(channel); return 0; } + incref(channel); // process channels are only closed by the exit_cb + wstream_init(proc->in, 0); + rstream_init(proc->out, 0); + rstream_start(proc->out, parse_msgpack); + rstream_init(proc->err, 0); + rstream_start(proc->err, forward_stderr); + return channel->id; } -/// Creates an API channel from a libuv stream representing a tcp or -/// pipe/socket client connection +/// Creates an API channel from a tcp/pipe socket connection /// -/// @param stream The established connection -void channel_from_stream(uv_stream_t *stream) +/// @param watcher The SocketWatcher ready to accept the connection +void channel_from_connection(SocketWatcher *watcher) { - Channel *channel = register_channel(); - stream->data = NULL; - channel->is_job = false; - // read stream - channel->data.streams.read = rstream_new(parse_msgpack, - rbuffer_new(CHANNEL_BUFFER_SIZE), - channel); - rstream_set_stream(channel->data.streams.read, stream); - rstream_start(channel->data.streams.read); - // write stream - channel->data.streams.write = wstream_new(0); - wstream_set_stream(channel->data.streams.write, stream); - channel->data.streams.uv = stream; + Channel *channel = register_channel(kChannelTypeSocket); + socket_watcher_accept(watcher, &channel->data.stream, channel); + 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, parse_msgpack); } /// Sends event/arguments to channel @@ -220,7 +225,7 @@ Object channel_send_call(uint64_t id, ChannelCallFrame frame = {request_id, false, false, NIL}; kv_push(ChannelCallFrame *, channel->call_stack, &frame); channel->pending_requests++; - event_poll_until(-1, frame.returned); + LOOP_PROCESS_EVENTS_UNTIL(&loop, channel->events, -1, frame.returned); (void)kv_pop(channel->call_stack); channel->pending_requests--; @@ -313,46 +318,34 @@ bool channel_close(uint64_t id) /// Neovim static void channel_from_stdio(void) { - Channel *channel = register_channel(); + Channel *channel = register_channel(kChannelTypeStdio); incref(channel); // stdio channels are only closed on exit - channel->is_job = false; // read stream - channel->data.streams.read = rstream_new(parse_msgpack, - rbuffer_new(CHANNEL_BUFFER_SIZE), - channel); - rstream_set_file(channel->data.streams.read, 0); - rstream_start(channel->data.streams.read); + rstream_init_fd(&loop, &channel->data.std.in, 0, CHANNEL_BUFFER_SIZE, + channel); + rstream_start(&channel->data.std.in, parse_msgpack); // write stream - channel->data.streams.write = wstream_new(0); - wstream_set_file(channel->data.streams.write, 1); - channel->data.streams.uv = NULL; + wstream_init_fd(&loop, &channel->data.std.out, 1, 0, NULL); } -static void job_out(RStream *rstream, void *data, bool eof) +static void forward_stderr(Stream *stream, RBuffer *rbuf, size_t count, + void *data, bool eof) { - Job *job = data; - parse_msgpack(rstream, job_data(job), eof); -} - -static void job_err(RStream *rstream, void *data, bool eof) -{ - size_t count; - char buf[256]; - - while ((count = rstream_pending(rstream))) { - size_t read = rstream_read(rstream, buf, sizeof(buf) - 1); + while (rbuffer_size(rbuf)) { + char buf[256]; + size_t read = rbuffer_read(rbuf, buf, sizeof(buf) - 1); buf[read] = NUL; - ELOG("Channel %" PRIu64 " stderr: %s", - ((Channel *)job_data(data))->id, buf); + ELOG("Channel %" PRIu64 " stderr: %s", ((Channel *)data)->id, buf); } } -static void job_exit(Job *job, int status, void *data) +static void process_exit(Process *proc, int status, void *data) { decref(data); } -static void parse_msgpack(RStream *rstream, void *data, bool eof) +static void parse_msgpack(Stream *stream, RBuffer *rbuf, size_t c, void *data, + bool eof) { Channel *channel = data; incref(channel); @@ -363,14 +356,14 @@ static void parse_msgpack(RStream *rstream, void *data, bool eof) goto end; } - size_t count = rstream_pending(rstream); - DLOG("Feeding the msgpack parser with %u bytes of data from RStream(%p)", + size_t count = rbuffer_size(rbuf); + DLOG("Feeding the msgpack parser with %u bytes of data from Stream(%p)", count, - rstream); + stream); // Feed the unpacker with data msgpack_unpacker_reserve_buffer(channel->unpacker, count); - rstream_read(rstream, msgpack_unpacker_buffer(channel->unpacker), count); + rbuffer_read(rbuf, msgpack_unpacker_buffer(channel->unpacker), count); msgpack_unpacker_buffer_consumed(channel->unpacker, count); msgpack_unpacked unpacked; @@ -460,31 +453,31 @@ static void handle_request(Channel *channel, msgpack_object *request) method->via.bin.size); } else { handler.fn = msgpack_rpc_handle_missing_method; - handler.defer = false; + handler.async = true; } Array args = ARRAY_DICT_INIT; if (!msgpack_rpc_to_array(msgpack_rpc_args(request), &args)) { handler.fn = msgpack_rpc_handle_invalid_arguments; - handler.defer = false; + handler.async = true; } - bool defer = (!kv_size(channel->call_stack) && handler.defer); RequestEvent *event_data = xmalloc(sizeof(RequestEvent)); event_data->channel = channel; event_data->handler = handler; event_data->args = args; event_data->request_id = request_id; incref(channel); - event_push((Event) { - .handler = on_request_event, - .data = event_data - }, defer); + if (handler.async) { + on_request_event((void **)&event_data); + } else { + queue_put(channel->events, on_request_event, 1, event_data); + } } -static void on_request_event(Event event) +static void on_request_event(void **argv) { - RequestEvent *e = event.data; + RequestEvent *e = argv[0]; Channel *channel = e->channel; MsgpackRpcRequestHandler handler = e->handler; Array args = e->args; @@ -518,10 +511,18 @@ static bool channel_write(Channel *channel, WBuffer *buffer) return false; } - if (channel->is_job) { - success = job_write(channel->data.job, buffer); - } else { - success = wstream_write(channel->data.streams.write, buffer); + switch (channel->type) { + case kChannelTypeSocket: + success = wstream_write(&channel->data.stream, buffer); + break; + case kChannelTypeProc: + success = wstream_write(&channel->data.process.in, buffer); + break; + case kChannelTypeStdio: + success = wstream_write(&channel->data.std.out, buffer); + break; + default: + abort(); } if (!success) { @@ -630,7 +631,7 @@ static void unsubscribe(Channel *channel, char *event) xfree(event_string); } -/// Close the channel streams/job and free the channel resources. +/// Close the channel streams/process and free the channel resources. static void close_channel(Channel *channel) { if (channel->closed) { @@ -639,27 +640,30 @@ static void close_channel(Channel *channel) channel->closed = true; - if (channel->is_job) { - if (channel->data.job) { - job_stop(channel->data.job); - } - } else { - rstream_free(channel->data.streams.read); - wstream_free(channel->data.streams.write); - uv_handle_t *handle = (uv_handle_t *)channel->data.streams.uv; - if (handle) { - uv_close(handle, close_cb); - } else { - event_push((Event) { .handler = on_stdio_close, .data = channel }, false); - } + switch (channel->type) { + case kChannelTypeSocket: + stream_close(&channel->data.stream, NULL); + break; + case kChannelTypeProc: + if (!channel->data.process.uvproc.process.closed) { + process_stop(&channel->data.process.uvproc.process); + } + break; + case kChannelTypeStdio: + stream_close(&channel->data.std.in, NULL); + stream_close(&channel->data.std.out, NULL); + queue_put(loop.fast_events, exit_event, 1, channel); + return; + default: + abort(); } decref(channel); } -static void on_stdio_close(Event e) +static void exit_event(void **argv) { - decref(e.data); + decref(argv[0]); if (!exiting) { mch_exit(0); @@ -681,18 +685,20 @@ static void free_channel(Channel *channel) pmap_free(cstr_t)(channel->subscribed_events); kv_destroy(channel->call_stack); kv_destroy(channel->delayed_notifications); + queue_free(channel->events); xfree(channel); } -static void close_cb(uv_handle_t *handle) +static void close_cb(Stream *stream, void *data) { - xfree(handle->data); - xfree(handle); + decref(data); } -static Channel *register_channel(void) +static Channel *register_channel(ChannelType type) { Channel *rv = xmalloc(sizeof(Channel)); + rv->events = queue_new_child(loop.events); + rv->type = type; rv->refcount = 1; rv->closed = false; rv->unpacker = msgpack_unpacker_new(MSGPACK_UNPACKER_INIT_BUFFER_SIZE); diff --git a/src/nvim/msgpack_rpc/channel.h b/src/nvim/msgpack_rpc/channel.h index df742fe368..104547a7b8 100644 --- a/src/nvim/msgpack_rpc/channel.h +++ b/src/nvim/msgpack_rpc/channel.h @@ -5,6 +5,7 @@ #include <uv.h> #include "nvim/api/private/defs.h" +#include "nvim/event/socket.h" #include "nvim/vim.h" #define METHOD_MAXLEN 512 diff --git a/src/nvim/msgpack_rpc/defs.h b/src/nvim/msgpack_rpc/defs.h index 0492a65290..d97cf28ca1 100644 --- a/src/nvim/msgpack_rpc/defs.h +++ b/src/nvim/msgpack_rpc/defs.h @@ -11,9 +11,8 @@ typedef struct { uint64_t request_id, Array args, Error *error); - bool defer; // Should the call be deferred to the main loop? This should - // be true if the function mutates editor data structures such - // as buffers, windows, tabs, or if it executes vimscript code. + bool async; // function is always safe to run immediately instead of being + // put in a request queue for handling when nvim waits for input. } MsgpackRpcRequestHandler; /// Initializes the msgpack-rpc method table diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index acfd3fe94f..473958c765 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -94,13 +94,14 @@ bool msgpack_rpc_to_string(msgpack_object *obj, String *arg) FUNC_ATTR_NONNULL_ALL { if (obj->type == MSGPACK_OBJECT_BIN || obj->type == MSGPACK_OBJECT_STR) { + if (obj->via.bin.ptr == NULL) { + return false; + } arg->data = xmemdupz(obj->via.bin.ptr, obj->via.bin.size); arg->size = obj->via.bin.size; - } else { - return false; + return true; } - - return true; + return false; } bool msgpack_rpc_to_object(msgpack_object *obj, Object *arg) diff --git a/src/nvim/msgpack_rpc/helpers.h b/src/nvim/msgpack_rpc/helpers.h index bf161d54e0..7d9f114140 100644 --- a/src/nvim/msgpack_rpc/helpers.h +++ b/src/nvim/msgpack_rpc/helpers.h @@ -6,7 +6,7 @@ #include <msgpack.h> -#include "nvim/os/wstream.h" +#include "nvim/event/wstream.h" #include "nvim/api/private/defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/msgpack_rpc/remote_ui.c b/src/nvim/msgpack_rpc/remote_ui.c index 07d78dd9d7..3334b0e6af 100644 --- a/src/nvim/msgpack_rpc/remote_ui.c +++ b/src/nvim/msgpack_rpc/remote_ui.c @@ -28,7 +28,7 @@ void remote_ui_init(void) connected_uis = pmap_new(uint64_t)(); // Add handler for "attach_ui" String method = cstr_as_string("ui_attach"); - MsgpackRpcRequestHandler handler = {.fn = remote_ui_attach, .defer = false}; + MsgpackRpcRequestHandler handler = {.fn = remote_ui_attach, .async = true}; msgpack_rpc_add_method_handler(method, handler); method = cstr_as_string("ui_detach"); handler.fn = remote_ui_detach; @@ -86,8 +86,7 @@ static Object remote_ui_attach(uint64_t channel_id, uint64_t request_id, ui->busy_stop = remote_ui_busy_stop; ui->mouse_on = remote_ui_mouse_on; ui->mouse_off = remote_ui_mouse_off; - ui->insert_mode = remote_ui_insert_mode; - ui->normal_mode = remote_ui_normal_mode; + ui->mode_change = remote_ui_mode_change; ui->set_scroll_region = remote_ui_set_scroll_region; ui->scroll = remote_ui_scroll; ui->highlight_set = remote_ui_highlight_set; @@ -214,16 +213,18 @@ static void remote_ui_mouse_off(UI *ui) push_call(ui, "mouse_off", args); } -static void remote_ui_insert_mode(UI *ui) +static void remote_ui_mode_change(UI *ui, int mode) { Array args = ARRAY_DICT_INIT; - push_call(ui, "insert_mode", args); -} - -static void remote_ui_normal_mode(UI *ui) -{ - Array args = ARRAY_DICT_INIT; - push_call(ui, "normal_mode", args); + if (mode == INSERT) { + ADD(args, STRING_OBJ(cstr_to_string("insert"))); + } else if (mode == REPLACE) { + ADD(args, STRING_OBJ(cstr_to_string("replace"))); + } else { + assert(mode == NORMAL); + ADD(args, STRING_OBJ(cstr_to_string("normal"))); + } + push_call(ui, "mode_change", args); } static void remote_ui_set_scroll_region(UI *ui, int top, int bot, int left, diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c index 388b5a04cf..8dffea35ca 100644 --- a/src/nvim/msgpack_rpc/server.c +++ b/src/nvim/msgpack_rpc/server.c @@ -3,11 +3,10 @@ #include <string.h> #include <stdint.h> -#include <uv.h> - #include "nvim/msgpack_rpc/channel.h" #include "nvim/msgpack_rpc/server.h" #include "nvim/os/os.h" +#include "nvim/event/socket.h" #include "nvim/ascii.h" #include "nvim/eval.h" #include "nvim/garray.h" @@ -19,35 +18,9 @@ #include "nvim/strings.h" #define MAX_CONNECTIONS 32 -#define ADDRESS_MAX_SIZE 256 -#define NVIM_DEFAULT_TCP_PORT 7450 #define LISTEN_ADDRESS_ENV_VAR "NVIM_LISTEN_ADDRESS" -typedef enum { - kServerTypeTcp, - kServerTypePipe -} ServerType; - -typedef struct { - // Pipe/socket path, or TCP address string - char addr[ADDRESS_MAX_SIZE]; - - // Type of the union below - ServerType type; - - // TCP server or unix socket (named pipe on Windows) - union { - struct { - uv_tcp_t handle; - struct sockaddr_in addr; - } tcp; - struct { - uv_pipe_t handle; - } pipe; - } socket; -} Server; - -static garray_T servers = GA_EMPTY_INIT_VALUE; +static garray_T watchers = GA_EMPTY_INIT_VALUE; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "msgpack_rpc/server.c.generated.h" @@ -56,7 +29,7 @@ static garray_T servers = GA_EMPTY_INIT_VALUE; /// Initializes the module bool server_init(void) { - ga_init(&servers, sizeof(Server *), 1); + ga_init(&watchers, sizeof(SocketWatcher *), 1); bool must_free = false; const char *listen_address = os_getenv(LISTEN_ADDRESS_ENV_VAR); @@ -72,18 +45,10 @@ bool server_init(void) return ok; } -/// Retrieve the file handle from a server. -static uv_handle_t *server_handle(Server *server) -{ - return server->type == kServerTypeTcp - ? (uv_handle_t *)&server->socket.tcp.handle - : (uv_handle_t *) &server->socket.pipe.handle; -} - /// Teardown a single server -static void server_close_cb(Server **server) +static void close_socket_watcher(SocketWatcher **watcher) { - uv_close(server_handle(*server), free_server); + socket_watcher_close(*watcher, free_server); } /// Set v:servername to the first server in the server list, or unset it if no @@ -91,7 +56,7 @@ static void server_close_cb(Server **server) static void set_vservername(garray_T *srvs) { char *default_server = (srvs->ga_len > 0) - ? ((Server **)srvs->ga_data)[0]->addr + ? ((SocketWatcher **)srvs->ga_data)[0]->addr : NULL; set_vim_var_string(VV_SEND_SERVER, (char_u *)default_server, -1); } @@ -99,7 +64,7 @@ static void set_vservername(garray_T *srvs) /// Teardown the server module void server_teardown(void) { - GA_DEEP_CLEAR(&servers, Server *, server_close_cb); + GA_DEEP_CLEAR(&watchers, SocketWatcher *, close_socket_watcher); } /// Starts listening for API calls on the TCP address or pipe path `endpoint`. @@ -116,120 +81,38 @@ void server_teardown(void) int server_start(const char *endpoint) FUNC_ATTR_NONNULL_ALL { - char addr[ADDRESS_MAX_SIZE]; - - // Trim to `ADDRESS_MAX_SIZE` - if (xstrlcpy(addr, endpoint, sizeof(addr)) >= sizeof(addr)) { - // TODO(aktau): since this is not what the user wanted, perhaps we - // should return an error here - WLOG("Address was too long, truncated to %s", addr); - } - - // Check if the server already exists - for (int i = 0; i < servers.ga_len; i++) { - if (strcmp(addr, ((Server **)servers.ga_data)[i]->addr) == 0) { - ELOG("Already listening on %s", addr); + SocketWatcher *watcher = xmalloc(sizeof(SocketWatcher)); + socket_watcher_init(&loop, watcher, endpoint, NULL); + + // Check if a watcher for the endpoint already exists + for (int i = 0; i < watchers.ga_len; i++) { + if (!strcmp(watcher->addr, ((SocketWatcher **)watchers.ga_data)[i]->addr)) { + ELOG("Already listening on %s", watcher->addr); + socket_watcher_close(watcher, free_server); return 1; } } - ServerType server_type = kServerTypeTcp; - Server *server = xmalloc(sizeof(Server)); - char ip[16], *ip_end = strrchr(addr, ':'); - - if (!ip_end) { - ip_end = strchr(addr, NUL); - } - - // (ip_end - addr) is always > 0, so convert to size_t - size_t addr_len = (size_t)(ip_end - addr); - - if (addr_len > sizeof(ip) - 1) { - // Maximum length of an IPv4 address buffer is 15 (eg: 255.255.255.255) - addr_len = sizeof(ip) - 1; - } - - // Extract the address part - xstrlcpy(ip, addr, addr_len + 1); - - int port = NVIM_DEFAULT_TCP_PORT; - - if (*ip_end == ':') { - // Extract the port - long lport = strtol(ip_end + 1, NULL, 10); // NOLINT - if (lport <= 0 || lport > 0xffff) { - // Invalid port, treat as named pipe or unix socket - server_type = kServerTypePipe; - } else { - port = (int) lport; - } - } - - if (server_type == kServerTypeTcp) { - // Try to parse ip address - if (uv_ip4_addr(ip, port, &server->socket.tcp.addr)) { - // Invalid address, treat as named pipe or unix socket - server_type = kServerTypePipe; - } - } - - int result; - uv_stream_t *stream = NULL; - - xstrlcpy(server->addr, addr, sizeof(server->addr)); - - if (server_type == kServerTypeTcp) { - // Listen on tcp address/port - uv_tcp_init(uv_default_loop(), &server->socket.tcp.handle); - result = uv_tcp_bind(&server->socket.tcp.handle, - (const struct sockaddr *)&server->socket.tcp.addr, - 0); - stream = (uv_stream_t *)&server->socket.tcp.handle; - } else { - // Listen on named pipe or unix socket - uv_pipe_init(uv_default_loop(), &server->socket.pipe.handle, 0); - result = uv_pipe_bind(&server->socket.pipe.handle, server->addr); - stream = (uv_stream_t *)&server->socket.pipe.handle; - } - - stream->data = server; - - if (result == 0) { - result = uv_listen((uv_stream_t *)&server->socket.tcp.handle, - MAX_CONNECTIONS, - connection_cb); - } - - assert(result <= 0); // libuv should have returned -errno or zero. + int result = socket_watcher_start(watcher, MAX_CONNECTIONS, connection_cb); if (result < 0) { - if (result == -EACCES) { - // Libuv converts ENOENT to EACCES for Windows compatibility, but if - // the parent directory does not exist, ENOENT would be more accurate. - *path_tail((char_u *) addr) = NUL; - if (!os_file_exists((char_u *) addr)) { - result = -ENOENT; - } - } - uv_close((uv_handle_t *)stream, free_server); ELOG("Failed to start server: %s", uv_strerror(result)); + socket_watcher_close(watcher, free_server); return result; } // Update $NVIM_LISTEN_ADDRESS, if not set. const char *listen_address = os_getenv(LISTEN_ADDRESS_ENV_VAR); if (listen_address == NULL) { - os_setenv(LISTEN_ADDRESS_ENV_VAR, addr, 1); + os_setenv(LISTEN_ADDRESS_ENV_VAR, watcher->addr, 1); } - server->type = server_type; - - // Add the server to the list. - ga_grow(&servers, 1); - ((Server **)servers.ga_data)[servers.ga_len++] = server; + // Add the watcher to the list. + ga_grow(&watchers, 1); + ((SocketWatcher **)watchers.ga_data)[watchers.ga_len++] = watcher; // Update v:servername, if not set. if (STRLEN(get_vim_var_str(VV_SEND_SERVER)) == 0) { - set_vservername(&servers); + set_vservername(&watchers); } return 0; @@ -240,21 +123,21 @@ int server_start(const char *endpoint) /// @param endpoint Address of the server. void server_stop(char *endpoint) { - Server *server; + SocketWatcher *watcher; char addr[ADDRESS_MAX_SIZE]; // Trim to `ADDRESS_MAX_SIZE` xstrlcpy(addr, endpoint, sizeof(addr)); int i = 0; // Index of the server whose address equals addr. - for (; i < servers.ga_len; i++) { - server = ((Server **)servers.ga_data)[i]; - if (strcmp(addr, server->addr) == 0) { + for (; i < watchers.ga_len; i++) { + watcher = ((SocketWatcher **)watchers.ga_data)[i]; + if (strcmp(addr, watcher->addr) == 0) { break; } } - if (i >= servers.ga_len) { + if (i >= watchers.ga_len) { ELOG("Not listening on %s", addr); return; } @@ -265,18 +148,18 @@ void server_stop(char *endpoint) os_unsetenv(LISTEN_ADDRESS_ENV_VAR); } - uv_close(server_handle(server), free_server); + socket_watcher_close(watcher, free_server); // Remove this server from the list by swapping it with the last item. - if (i != servers.ga_len - 1) { - ((Server **)servers.ga_data)[i] = - ((Server **)servers.ga_data)[servers.ga_len - 1]; + if (i != watchers.ga_len - 1) { + ((SocketWatcher **)watchers.ga_data)[i] = + ((SocketWatcher **)watchers.ga_data)[watchers.ga_len - 1]; } - servers.ga_len--; + watchers.ga_len--; // If v:servername is the stopped address, re-initialize it. if (STRCMP(addr, get_vim_var_str(VV_SEND_SERVER)) == 0) { - set_vservername(&servers); + set_vservername(&watchers); } } @@ -285,52 +168,28 @@ void server_stop(char *endpoint) char **server_address_list(size_t *size) FUNC_ATTR_NONNULL_ALL { - if ((*size = (size_t) servers.ga_len) == 0) { + if ((*size = (size_t)watchers.ga_len) == 0) { return NULL; } - char **addrs = xcalloc((size_t) servers.ga_len, sizeof(const char *)); - for (int i = 0; i < servers.ga_len; i++) { - addrs[i] = xstrdup(((Server **)servers.ga_data)[i]->addr); + char **addrs = xcalloc((size_t)watchers.ga_len, sizeof(const char *)); + for (int i = 0; i < watchers.ga_len; i++) { + addrs[i] = xstrdup(((SocketWatcher **)watchers.ga_data)[i]->addr); } return addrs; } -static void connection_cb(uv_stream_t *server, int status) +static void connection_cb(SocketWatcher *watcher, int result, void *data) { - int result; - uv_stream_t *client; - Server *srv = server->data; - - if (status < 0) { - abort(); - } - - if (srv->type == kServerTypeTcp) { - client = xmalloc(sizeof(uv_tcp_t)); - uv_tcp_init(uv_default_loop(), (uv_tcp_t *)client); - } else { - client = xmalloc(sizeof(uv_pipe_t)); - uv_pipe_init(uv_default_loop(), (uv_pipe_t *)client, 0); - } - - result = uv_accept(server, client); - if (result) { ELOG("Failed to accept connection: %s", uv_strerror(result)); - uv_close((uv_handle_t *)client, free_client); return; } - channel_from_stream(client); -} - -static void free_client(uv_handle_t *handle) -{ - xfree(handle); + channel_from_connection(watcher); } -static void free_server(uv_handle_t *handle) +static void free_server(SocketWatcher *watcher, void *data) { - xfree(handle->data); + xfree(watcher); } diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 7b42467184..5b35af9209 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -61,8 +61,9 @@ #include "nvim/mouse.h" #include "nvim/undo.h" #include "nvim/window.h" -#include "nvim/os/event.h" +#include "nvim/event/loop.h" #include "nvim/os/time.h" +#include "nvim/os/input.h" /* * The Visual area is remembered for reselection. @@ -487,12 +488,12 @@ normal_cmd ( /* * Get the command character from the user. */ - event_enable_deferred(); + input_enable_events(); c = safe_vgetc(); - event_disable_deferred(); + input_disable_events(); if (c == K_EVENT) { - event_process(); + queue_process_events(loop.events); return; } @@ -2016,6 +2017,9 @@ do_mouse ( if (regname == '.') insert_reg(regname, true); else { + if (regname == 0 && eval_has_provider("clipboard")) { + regname = '*'; + } if ((State & REPLACE_FLAG) && !yank_register_mline(regname)) insert_reg(regname, true); else { @@ -2105,11 +2109,6 @@ do_mouse ( * NOTE: Ignore right button down and drag mouse events. * Windows only shows the popup menu on the button up event. */ -#if defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_GTK) \ - || defined(FEAT_GUI_MAC) - if (!is_click) - return false; -#endif return false; } if (which_button == MOUSE_LEFT @@ -2284,6 +2283,9 @@ do_mouse ( * Middle mouse click: Put text before cursor. */ if (which_button == MOUSE_MIDDLE) { + if (regname == 0 && eval_has_provider("clipboard")) { + regname = '*'; + } if (yank_register_mline(regname)) { if (mouse_past_bottom) dir = FORWARD; @@ -5740,22 +5742,10 @@ static void nv_optrans(cmdarg_T *cap) static char_u *str = (char_u *)"xXDCsSY&"; if (!checkclearopq(cap->oap)) { - /* In Vi "2D" doesn't delete the next line. Can't translate it - * either, because "2." should also not use the count. */ - if (cap->cmdchar == 'D' && vim_strchr(p_cpo, CPO_HASH) != NULL) { - cap->oap->start = curwin->w_cursor; - cap->oap->op_type = OP_DELETE; - set_op_var(OP_DELETE); - cap->count1 = 1; - nv_dollar(cap); - finish_op = true; - ResetRedobuff(); - AppendCharToRedobuff('D'); - } else { - if (cap->count0) - stuffnumReadbuff(cap->count0); - stuffReadbuff(ar[(int)(vim_strchr(str, cap->cmdchar) - str)]); + if (cap->count0) { + stuffnumReadbuff(cap->count0); } + stuffReadbuff(ar[(int)(vim_strchr(str, cap->cmdchar) - str)]); } cap->opcount = 0; } @@ -6553,9 +6543,6 @@ static void n_opencmd(cmdarg_T *cap) 0, 0)) { if (curwin->w_p_cole > 0 && oldline != curwin->w_cursor.lnum) update_single_line(curwin, oldline); - /* When '#' is in 'cpoptions' ignore the count. */ - if (vim_strchr(p_cpo, CPO_HASH) != NULL) - cap->count1 = 1; invoke_edit(cap, false, cap->cmdchar, true); } } @@ -7278,7 +7265,10 @@ static void nv_put(cmdarg_T *cap) */ was_visual = true; regname = cap->oap->regname; - if (regname == 0 || regname == '"' + // '+' and '*' could be the same selection + bool clipoverwrite = (regname == '+' || regname == '*') + && (cb_flags & CB_UNNAMEDMASK); + if (regname == 0 || regname == '"' || clipoverwrite || ascii_isdigit(regname) || regname == '-') { // The delete might overwrite the register we want to put, save it first savereg = copy_register(regname); diff --git a/src/nvim/ops.c b/src/nvim/ops.c index d8df6ae72d..766b5720d9 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -67,8 +67,6 @@ #define PLUS_REGISTER 38 #define NUM_REGISTERS 39 -#define CB_UNNAMEDMASK (CB_UNNAMED | CB_UNNAMEDPLUS) - static yankreg_T y_regs[NUM_REGISTERS]; static yankreg_T *y_previous = NULL; /* ptr to last written yankreg */ @@ -767,7 +765,10 @@ yankreg_T *get_yank_register(int regname, int mode) if (mode == YREG_PASTE && get_clipboard(regname, ®, false)) { // reg is set to clipboard contents. return reg; - } else if (mode != YREG_YANK && (regname == 0 || regname == '"') && y_previous != NULL) { + } else if (mode != YREG_YANK + && (regname == 0 || regname == '"' || regname == '*' || regname == '+') + && y_previous != NULL) { + // in case clipboard not available, paste from previous used register return y_previous; } @@ -5277,11 +5278,11 @@ static yankreg_T *adjust_clipboard_name(int *name, bool quiet) } yankreg_T *target; if (cb_flags & CB_UNNAMEDPLUS) { - *name = '+'; - target = &y_regs[STAR_REGISTER]; + *name = cb_flags & CB_UNNAMED ? '"': '+'; + target = &y_regs[PLUS_REGISTER]; } else { *name = '*'; - target = &y_regs[PLUS_REGISTER]; + target = &y_regs[STAR_REGISTER]; } return target; // unnamed register } diff --git a/src/nvim/option.c b/src/nvim/option.c index 8ec5640b7a..9a375c0675 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -97,121 +97,6 @@ #define OPT_BUF(x) (idopt_T)(PV_BUF + (int)(x)) #define OPT_BOTH(x) (idopt_T)(PV_BOTH + (int)(x)) -/* - * Definition of the PV_ values for buffer-local options. - * The BV_ values are defined in option_defs.h. - */ -#define PV_AI OPT_BUF(BV_AI) -#define PV_AR OPT_BOTH(OPT_BUF(BV_AR)) -#define PV_BKC OPT_BOTH(OPT_BUF(BV_BKC)) -# define PV_BH OPT_BUF(BV_BH) -# define PV_BT OPT_BUF(BV_BT) -# define PV_EFM OPT_BOTH(OPT_BUF(BV_EFM)) -# define PV_GP OPT_BOTH(OPT_BUF(BV_GP)) -# define PV_MP OPT_BOTH(OPT_BUF(BV_MP)) -#define PV_BIN OPT_BUF(BV_BIN) -#define PV_BL OPT_BUF(BV_BL) -# define PV_BOMB OPT_BUF(BV_BOMB) -#define PV_CI OPT_BUF(BV_CI) -# define PV_CIN OPT_BUF(BV_CIN) -# define PV_CINK OPT_BUF(BV_CINK) -# define PV_CINO OPT_BUF(BV_CINO) -# define PV_CINW OPT_BUF(BV_CINW) -#define PV_CM OPT_BOTH(OPT_BUF(BV_CM)) -# define PV_CMS OPT_BUF(BV_CMS) -# define PV_COM OPT_BUF(BV_COM) -# define PV_CPT OPT_BUF(BV_CPT) -# define PV_DICT OPT_BOTH(OPT_BUF(BV_DICT)) -# define PV_TSR OPT_BOTH(OPT_BUF(BV_TSR)) -# define PV_CFU OPT_BUF(BV_CFU) -# define PV_DEF OPT_BOTH(OPT_BUF(BV_DEF)) -# define PV_INC OPT_BOTH(OPT_BUF(BV_INC)) -#define PV_EOL OPT_BUF(BV_EOL) -#define PV_EP OPT_BOTH(OPT_BUF(BV_EP)) -#define PV_ET OPT_BUF(BV_ET) -# define PV_FENC OPT_BUF(BV_FENC) -# define PV_FEX OPT_BUF(BV_FEX) -#define PV_FF OPT_BUF(BV_FF) -#define PV_FLP OPT_BUF(BV_FLP) -#define PV_FO OPT_BUF(BV_FO) -# define PV_FT OPT_BUF(BV_FT) -#define PV_IMI OPT_BUF(BV_IMI) -#define PV_IMS OPT_BUF(BV_IMS) -# define PV_INDE OPT_BUF(BV_INDE) -# define PV_INDK OPT_BUF(BV_INDK) -# define PV_INEX OPT_BUF(BV_INEX) -#define PV_INF OPT_BUF(BV_INF) -#define PV_ISK OPT_BUF(BV_ISK) -# define PV_KMAP OPT_BUF(BV_KMAP) -#define PV_KP OPT_BOTH(OPT_BUF(BV_KP)) -# define PV_LISP OPT_BUF(BV_LISP) -# define PV_LW OPT_BOTH(OPT_BUF(BV_LW)) -#define PV_MA OPT_BUF(BV_MA) -#define PV_ML OPT_BUF(BV_ML) -#define PV_MOD OPT_BUF(BV_MOD) -#define PV_MPS OPT_BUF(BV_MPS) -#define PV_NF OPT_BUF(BV_NF) -# define PV_OFU OPT_BUF(BV_OFU) -#define PV_PATH OPT_BOTH(OPT_BUF(BV_PATH)) -#define PV_PI OPT_BUF(BV_PI) -# define PV_QE OPT_BUF(BV_QE) -#define PV_RO OPT_BUF(BV_RO) -# define PV_SI OPT_BUF(BV_SI) -# define PV_SMC OPT_BUF(BV_SMC) -# define PV_SYN OPT_BUF(BV_SYN) -# define PV_SPC OPT_BUF(BV_SPC) -# define PV_SPF OPT_BUF(BV_SPF) -# define PV_SPL OPT_BUF(BV_SPL) -#define PV_STS OPT_BUF(BV_STS) -# define PV_SUA OPT_BUF(BV_SUA) -#define PV_SW OPT_BUF(BV_SW) -#define PV_SWF OPT_BUF(BV_SWF) -#define PV_TAGS OPT_BOTH(OPT_BUF(BV_TAGS)) -#define PV_TS OPT_BUF(BV_TS) -#define PV_TW OPT_BUF(BV_TW) -# define PV_UDF OPT_BUF(BV_UDF) -#define PV_WM OPT_BUF(BV_WM) - -/* - * Definition of the PV_ values for window-local options. - * The WV_ values are defined in option_defs.h. - */ -#define PV_LIST OPT_WIN(WV_LIST) -# define PV_ARAB OPT_WIN(WV_ARAB) -# define PV_BRI OPT_WIN(WV_BRI) -# define PV_BRIOPT OPT_WIN(WV_BRIOPT) -# define PV_DIFF OPT_WIN(WV_DIFF) -# define PV_FDC OPT_WIN(WV_FDC) -# define PV_FEN OPT_WIN(WV_FEN) -# define PV_FDI OPT_WIN(WV_FDI) -# define PV_FDL OPT_WIN(WV_FDL) -# define PV_FDM OPT_WIN(WV_FDM) -# define PV_FML OPT_WIN(WV_FML) -# define PV_FDN OPT_WIN(WV_FDN) -# define PV_FDE OPT_WIN(WV_FDE) -# define PV_FDT OPT_WIN(WV_FDT) -# define PV_FMR OPT_WIN(WV_FMR) -# define PV_LBR OPT_WIN(WV_LBR) -#define PV_NU OPT_WIN(WV_NU) -#define PV_RNU OPT_WIN(WV_RNU) -# define PV_NUW OPT_WIN(WV_NUW) -# define PV_PVW OPT_WIN(WV_PVW) -# define PV_RL OPT_WIN(WV_RL) -# define PV_RLC OPT_WIN(WV_RLC) -# define PV_SCBIND OPT_WIN(WV_SCBIND) -#define PV_SCROLL OPT_WIN(WV_SCROLL) -# define PV_SPELL OPT_WIN(WV_SPELL) -# define PV_CUC OPT_WIN(WV_CUC) -# define PV_CUL OPT_WIN(WV_CUL) -# define PV_CC OPT_WIN(WV_CC) -# define PV_STL OPT_BOTH(OPT_WIN(WV_STL)) -#define PV_UL OPT_BOTH(OPT_BUF(BV_UL)) -# define PV_WFH OPT_WIN(WV_WFH) -# define PV_WFW OPT_WIN(WV_WFW) -#define PV_WRAP OPT_WIN(WV_WRAP) -# define PV_CRBIND OPT_WIN(WV_CRBIND) -# define PV_COCU OPT_WIN(WV_COCU) -# define PV_COLE OPT_WIN(WV_COLE) /* WV_ and BV_ values get typecasted to this for the "indir" field */ typedef enum { @@ -366,7 +251,7 @@ typedef struct vimoption { "8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText," \ "d:Directory,e:ErrorMsg,i:IncSearch,l:Search,m:MoreMsg,M:ModeMsg,n:LineNr," \ "N:CursorLineNr,r:Question,s:StatusLine,S:StatusLineNC,c:VertSplit,t:Title," \ - "v:Visual,V:VisualNOS,w:WarningMsg,W:WildMenu,f:Folded,F:FoldColumn," \ + "v:Visual,w:WarningMsg,W:WildMenu,f:Folded,F:FoldColumn," \ "A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,>:SignColumn,-:Conceal," \ "B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel," \ "x:PmenuSbar,X:PmenuThumb,*:TabLine,#:TabLineSel,_:TabLineFill," \ @@ -380,1306 +265,10 @@ typedef struct vimoption { * The options with a NULL variable are 'hidden': a set command for them is * ignored and they are not printed. */ -static vimoption_T - options[] = -{ - {"aleph", "al", P_NUM|P_VI_DEF|P_CURSWANT, - (char_u *)&p_aleph, PV_NONE, - { - (char_u *)224L, - (char_u *)0L - } SCRIPTID_INIT}, - {"antialias", "anti", P_BOOL|P_VI_DEF|P_VIM|P_RCLR, - (char_u *)NULL, PV_NONE, - {(char_u *)FALSE, (char_u *)FALSE} - SCRIPTID_INIT}, - {"arabic", "arab", P_BOOL|P_VI_DEF|P_VIM|P_CURSWANT, - VAR_WIN, PV_ARAB, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"arabicshape", "arshape", P_BOOL|P_VI_DEF|P_VIM|P_RCLR, - (char_u *)&p_arshape, PV_NONE, - {(char_u *)TRUE, (char_u *)0L} SCRIPTID_INIT}, - {"allowrevins", "ari", P_BOOL|P_VI_DEF|P_VIM, - (char_u *)&p_ari, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"altkeymap", "akm", P_BOOL|P_VI_DEF, - (char_u *)&p_altkeymap, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"ambiwidth", "ambw", P_STRING|P_VI_DEF|P_RCLR, - (char_u *)&p_ambw, PV_NONE, - {(char_u *)"single", (char_u *)0L} - SCRIPTID_INIT}, - {"autochdir", "acd", P_BOOL|P_VI_DEF, - (char_u *)&p_acd, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"autoindent", "ai", P_BOOL, - (char_u *)&p_ai, PV_AI, - {(char_u *)FALSE, (char_u *)TRUE} SCRIPTID_INIT}, - {"autoread", "ar", P_BOOL|P_VIM, - (char_u *)&p_ar, PV_AR, - {(char_u *)FALSE, (char_u *)TRUE} SCRIPTID_INIT}, - {"autowrite", "aw", P_BOOL|P_VI_DEF, - (char_u *)&p_aw, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"autowriteall","awa", P_BOOL|P_VI_DEF, - (char_u *)&p_awa, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"background", "bg", P_STRING|P_VI_DEF|P_RCLR, - (char_u *)&p_bg, PV_NONE, - { - (char_u *)"light", - (char_u *)0L - } SCRIPTID_INIT}, - {"backspace", "bs", P_STRING|P_VIM|P_COMMA|P_NODUP, - (char_u *)&p_bs, PV_NONE, - {(char_u *)"", (char_u *)"indent,eol,start"} SCRIPTID_INIT}, - {"backup", "bk", P_BOOL|P_VI_DEF|P_VIM, - (char_u *)&p_bk, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"backupcopy", "bkc", P_STRING|P_VIM|P_COMMA|P_NODUP, - (char_u *)&p_bkc, PV_BKC, -#ifdef UNIX - {(char_u *)"yes", (char_u *)"auto"} -#else - {(char_u *)"auto", (char_u *)"auto"} -#endif - SCRIPTID_INIT}, - {"backupdir", "bdir", P_STRING|P_EXPAND|P_VI_DEF|P_COMMA|P_NODUP|P_SECURE, - (char_u *)&p_bdir, PV_NONE, - {(char_u *)DFLT_BDIR, (char_u *)0L} SCRIPTID_INIT}, - {"backupext", "bex", P_STRING|P_VI_DEF|P_NFNAME, - (char_u *)&p_bex, PV_NONE, - { - (char_u *)"~", - (char_u *)0L - } SCRIPTID_INIT}, - {"backupskip", "bsk", P_STRING|P_VI_DEF|P_COMMA, - (char_u *)&p_bsk, PV_NONE, - {(char_u *)"", (char_u *)0L} - SCRIPTID_INIT}, - {"binary", "bin", P_BOOL|P_VI_DEF|P_RSTAT, - (char_u *)&p_bin, PV_BIN, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"bomb", NULL, P_BOOL|P_NO_MKRC|P_VI_DEF|P_RSTAT, - (char_u *)&p_bomb, PV_BOMB, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"breakat", "brk", P_STRING|P_VI_DEF|P_RALL|P_FLAGLIST, - (char_u *)&p_breakat, PV_NONE, - {(char_u *)" \t!@*-+;:,./?", (char_u *)0L} - SCRIPTID_INIT}, - {"breakindent", "bri", P_BOOL|P_VI_DEF|P_VIM|P_RWIN, - VAR_WIN, PV_BRI, - {(char_u *)FALSE, (char_u *)0L} - SCRIPTID_INIT}, - {"breakindentopt", "briopt", P_STRING|P_ALLOCED|P_VI_DEF|P_RBUF|P_COMMA|P_NODUP, - VAR_WIN, PV_BRIOPT, - {(char_u *)"", (char_u *)NULL} - SCRIPTID_INIT}, - {"browsedir", "bsdir",P_STRING|P_VI_DEF, - (char_u *)NULL, PV_NONE, - {(char_u *)0L, (char_u *)0L} - SCRIPTID_INIT}, - {"bufhidden", "bh", P_STRING|P_ALLOCED|P_VI_DEF|P_NOGLOB, - (char_u *)&p_bh, PV_BH, - {(char_u *)"", (char_u *)0L} - SCRIPTID_INIT}, - {"buflisted", "bl", P_BOOL|P_VI_DEF|P_NOGLOB, - (char_u *)&p_bl, PV_BL, - {(char_u *)1L, (char_u *)0L} - SCRIPTID_INIT}, - {"buftype", "bt", P_STRING|P_ALLOCED|P_VI_DEF|P_NOGLOB, - (char_u *)&p_bt, PV_BT, - {(char_u *)"", (char_u *)0L} - SCRIPTID_INIT}, - {"casemap", "cmp", P_STRING|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)&p_cmp, PV_NONE, - {(char_u *)"internal,keepascii", (char_u *)0L} - SCRIPTID_INIT}, - {"cdpath", "cd", P_STRING|P_EXPAND|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)&p_cdpath, PV_NONE, - {(char_u *)",,", (char_u *)0L} - SCRIPTID_INIT}, - {"cedit", NULL, P_STRING, - (char_u *)&p_cedit, PV_NONE, - {(char_u *)"", (char_u *)CTRL_F_STR} - SCRIPTID_INIT}, - {"charconvert", "ccv", P_STRING|P_VI_DEF|P_SECURE, - (char_u *)&p_ccv, PV_NONE, - {(char_u *)"", (char_u *)0L} - SCRIPTID_INIT}, - {"cindent", "cin", P_BOOL|P_VI_DEF|P_VIM, - (char_u *)&p_cin, PV_CIN, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"cinkeys", "cink", P_STRING|P_ALLOCED|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)&p_cink, PV_CINK, - {(char_u *)"0{,0},0),:,0#,!^F,o,O,e", (char_u *)0L} - SCRIPTID_INIT}, - {"cinoptions", "cino", P_STRING|P_ALLOCED|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)&p_cino, PV_CINO, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, - {"cinwords", "cinw", P_STRING|P_ALLOCED|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)&p_cinw, PV_CINW, - {(char_u *)"if,else,while,do,for,switch", - (char_u *)0L} - SCRIPTID_INIT}, - {"clipboard", "cb", P_STRING|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)&p_cb, PV_NONE, - {(char_u *)"", (char_u *)0L} - SCRIPTID_INIT}, - {"cmdheight", "ch", P_NUM|P_VI_DEF|P_RALL, - (char_u *)&p_ch, PV_NONE, - {(char_u *)1L, (char_u *)0L} SCRIPTID_INIT}, - {"cmdwinheight", "cwh", P_NUM|P_VI_DEF, - (char_u *)&p_cwh, PV_NONE, - {(char_u *)7L, (char_u *)0L} SCRIPTID_INIT}, - {"colorcolumn", "cc", P_STRING|P_VI_DEF|P_COMMA|P_NODUP|P_RWIN, - VAR_WIN, PV_CC, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, - {"columns", "co", P_NUM|P_NODEFAULT|P_NO_MKRC|P_VI_DEF|P_RCLR, - (char_u *)&Columns, PV_NONE, - {(char_u *)80L, (char_u *)0L} SCRIPTID_INIT}, - {"comments", "com", P_STRING|P_ALLOCED|P_VI_DEF|P_COMMA|P_NODUP| - P_CURSWANT, - (char_u *)&p_com, PV_COM, - {(char_u *)"s1:/*,mb:*,ex:*/,://,b:#,:%,:XCOMM,n:>,fb:-", - (char_u *)0L} - SCRIPTID_INIT}, - {"commentstring", "cms", P_STRING|P_ALLOCED|P_VI_DEF|P_CURSWANT, - (char_u *)&p_cms, PV_CMS, - {(char_u *)"/*%s*/", (char_u *)0L} - SCRIPTID_INIT}, - /* P_PRI_MKRC isn't needed here, optval_default() - * always returns TRUE for 'compatible' */ - {"compatible", "cp", P_BOOL|P_RALL, - (char_u *)&p_force_off, PV_NONE, - {(char_u *)TRUE, (char_u *)FALSE} SCRIPTID_INIT}, - {"complete", "cpt", P_STRING|P_ALLOCED|P_COMMA|P_NODUP, - (char_u *)&p_cpt, PV_CPT, - {(char_u *)".,w,b,u,t,i", (char_u *)".,w,b,u,t"} - SCRIPTID_INIT}, - {"concealcursor","cocu", P_STRING|P_ALLOCED|P_RWIN|P_VI_DEF, - VAR_WIN, PV_COCU, - {(char_u *)"", (char_u *)NULL} - SCRIPTID_INIT}, - {"conceallevel","cole", P_NUM|P_RWIN|P_VI_DEF, - VAR_WIN, PV_COLE, - {(char_u *)0L, (char_u *)0L} - SCRIPTID_INIT}, - {"completefunc", "cfu", P_STRING|P_ALLOCED|P_VI_DEF|P_SECURE, - (char_u *)&p_cfu, PV_CFU, - {(char_u *)"", (char_u *)0L} - SCRIPTID_INIT}, - {"completeopt", "cot", P_STRING|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)&p_cot, PV_NONE, - {(char_u *)"menu,preview", (char_u *)0L} - SCRIPTID_INIT}, - {"confirm", "cf", P_BOOL|P_VI_DEF, - (char_u *)&p_confirm, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"copyindent", "ci", P_BOOL|P_VI_DEF|P_VIM, - (char_u *)&p_ci, PV_CI, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"cpoptions", "cpo", P_STRING|P_VIM|P_RALL|P_FLAGLIST, - (char_u *)&p_cpo, PV_NONE, - {(char_u *)CPO_VI, (char_u *)CPO_VIM} - SCRIPTID_INIT}, - {"cscopepathcomp", "cspc", P_NUM|P_VI_DEF|P_VIM, - (char_u *)&p_cspc, PV_NONE, - {(char_u *)0L, (char_u *)0L} SCRIPTID_INIT}, - {"cscopeprg", "csprg", P_STRING|P_EXPAND|P_VI_DEF|P_SECURE, - (char_u *)&p_csprg, PV_NONE, - {(char_u *)"cscope", (char_u *)0L} - SCRIPTID_INIT}, - {"cscopequickfix", "csqf", P_STRING|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)&p_csqf, PV_NONE, - {(char_u *)"", (char_u *)0L} - SCRIPTID_INIT}, - {"cscoperelative", "csre", P_BOOL|P_VI_DEF|P_VIM, - (char_u *)&p_csre, PV_NONE, - {(char_u *)0L, (char_u *)0L} SCRIPTID_INIT}, - {"cscopetag", "cst", P_BOOL|P_VI_DEF|P_VIM, - (char_u *)&p_cst, PV_NONE, - {(char_u *)0L, (char_u *)0L} SCRIPTID_INIT}, - {"cscopetagorder", "csto", P_NUM|P_VI_DEF|P_VIM, - (char_u *)&p_csto, PV_NONE, - {(char_u *)0L, (char_u *)0L} SCRIPTID_INIT}, - {"cscopeverbose", "csverb", P_BOOL|P_VI_DEF|P_VIM, - (char_u *)&p_csverbose, PV_NONE, - {(char_u *)0L, (char_u *)0L} SCRIPTID_INIT}, - {"cursorbind", "crb", P_BOOL|P_VI_DEF, - VAR_WIN, PV_CRBIND, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"cursorcolumn", "cuc", P_BOOL|P_VI_DEF|P_RWIN, - VAR_WIN, PV_CUC, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"cursorline", "cul", P_BOOL|P_VI_DEF|P_RWIN, - VAR_WIN, PV_CUL, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"debug", NULL, P_STRING|P_VI_DEF, - (char_u *)&p_debug, PV_NONE, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, - {"define", "def", P_STRING|P_ALLOCED|P_VI_DEF|P_CURSWANT, - (char_u *)&p_def, PV_DEF, - {(char_u *)"^\\s*#\\s*define", (char_u *)0L} - SCRIPTID_INIT}, - {"delcombine", "deco", P_BOOL|P_VI_DEF|P_VIM, - (char_u *)&p_deco, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"dictionary", "dict", P_STRING|P_EXPAND|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)&p_dict, PV_DICT, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, - {"diff", NULL, P_BOOL|P_VI_DEF|P_RWIN|P_NOGLOB, - VAR_WIN, PV_DIFF, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"diffexpr", "dex", P_STRING|P_VI_DEF|P_SECURE|P_CURSWANT, - (char_u *)&p_dex, PV_NONE, - {(char_u *)"", (char_u *)0L} - SCRIPTID_INIT}, - {"diffopt", "dip", P_STRING|P_ALLOCED|P_VI_DEF|P_RWIN|P_COMMA|P_NODUP, - (char_u *)&p_dip, PV_NONE, - {(char_u *)"filler", (char_u *)NULL} - SCRIPTID_INIT}, - {"digraph", "dg", P_BOOL|P_VI_DEF|P_VIM, - (char_u *)&p_dg, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"directory", "dir", P_STRING|P_EXPAND|P_VI_DEF|P_COMMA|P_NODUP|P_SECURE, - (char_u *)&p_dir, PV_NONE, - {(char_u *)DFLT_DIR, (char_u *)0L} SCRIPTID_INIT}, - {"display", "dy", P_STRING|P_VIM|P_COMMA|P_RALL|P_NODUP, - (char_u *)&p_dy, PV_NONE, - {(char_u *)"", (char_u *)"lastline"} SCRIPTID_INIT}, - {"eadirection", "ead", P_STRING|P_VI_DEF, - (char_u *)&p_ead, PV_NONE, - {(char_u *)"both", (char_u *)0L} - SCRIPTID_INIT}, - {"edcompatible","ed", P_BOOL|P_VI_DEF, - (char_u *)&p_force_off, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"encoding", "enc", P_STRING|P_VI_DEF|P_RCLR|P_NO_ML, - (char_u *)&p_enc, PV_NONE, - {(char_u *)ENC_DFLT, (char_u *)0L} - SCRIPTID_INIT}, - {"endofline", "eol", P_BOOL|P_NO_MKRC|P_VI_DEF|P_RSTAT, - (char_u *)&p_eol, PV_EOL, - {(char_u *)TRUE, (char_u *)0L} SCRIPTID_INIT}, - {"equalalways", "ea", P_BOOL|P_VI_DEF|P_RALL, - (char_u *)&p_ea, PV_NONE, - {(char_u *)TRUE, (char_u *)0L} SCRIPTID_INIT}, - {"equalprg", "ep", P_STRING|P_EXPAND|P_VI_DEF|P_SECURE, - (char_u *)&p_ep, PV_EP, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, - {"errorbells", "eb", P_BOOL|P_VI_DEF, - (char_u *)&p_eb, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"errorfile", "ef", P_STRING|P_EXPAND|P_VI_DEF|P_SECURE, - (char_u *)&p_ef, PV_NONE, - {(char_u *)DFLT_ERRORFILE, (char_u *)0L} - SCRIPTID_INIT}, - {"errorformat", "efm", P_STRING|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)&p_efm, PV_EFM, - {(char_u *)DFLT_EFM, (char_u *)0L} - SCRIPTID_INIT}, - {"esckeys", "ek", P_BOOL|P_VIM, - (char_u *)&p_ek, PV_NONE, - {(char_u *)FALSE, (char_u *)TRUE} SCRIPTID_INIT}, - {"eventignore", "ei", P_STRING|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)&p_ei, PV_NONE, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, - {"expandtab", "et", P_BOOL|P_VI_DEF|P_VIM, - (char_u *)&p_et, PV_ET, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"exrc", "ex", P_BOOL|P_VI_DEF|P_SECURE, - (char_u *)&p_exrc, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"fileencoding","fenc", P_STRING|P_ALLOCED|P_VI_DEF|P_RSTAT|P_RBUF|P_NO_MKRC, - (char_u *)&p_fenc, PV_FENC, - {(char_u *)"", (char_u *)0L} - SCRIPTID_INIT}, - {"fileencodings","fencs", P_STRING|P_VI_DEF|P_COMMA, - (char_u *)&p_fencs, PV_NONE, - {(char_u *)"ucs-bom", (char_u *)0L} - SCRIPTID_INIT}, - {"fileformat", "ff", P_STRING|P_ALLOCED|P_VI_DEF|P_RSTAT|P_NO_MKRC| - P_CURSWANT, - (char_u *)&p_ff, PV_FF, - {(char_u *)DFLT_FF, (char_u *)0L} SCRIPTID_INIT}, - {"fileformats", "ffs", P_STRING|P_VIM|P_COMMA|P_NODUP, - (char_u *)&p_ffs, PV_NONE, - {(char_u *)DFLT_FFS_VI, (char_u *)DFLT_FFS_VIM} - SCRIPTID_INIT}, - {"fileignorecase", "fic", P_BOOL|P_VI_DEF, - (char_u *)&p_fic, PV_NONE, - { -#ifdef CASE_INSENSITIVE_FILENAME - (char_u *)TRUE, -#else - (char_u *)FALSE, -#endif - (char_u *)0L - } SCRIPTID_INIT}, - {"filetype", "ft", P_STRING|P_ALLOCED|P_VI_DEF|P_NOGLOB|P_NFNAME, - (char_u *)&p_ft, PV_FT, - {(char_u *)"", (char_u *)0L} - SCRIPTID_INIT}, - {"fillchars", "fcs", P_STRING|P_VI_DEF|P_RALL|P_COMMA|P_NODUP, - (char_u *)&p_fcs, PV_NONE, - {(char_u *)"vert:|,fold:-", (char_u *)0L} - SCRIPTID_INIT}, - {"fkmap", "fk", P_BOOL|P_VI_DEF, - (char_u *)&p_fkmap, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"foldclose", "fcl", P_STRING|P_VI_DEF|P_COMMA|P_NODUP|P_RWIN, - (char_u *)&p_fcl, PV_NONE, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, - {"foldcolumn", "fdc", P_NUM|P_VI_DEF|P_RWIN, - VAR_WIN, PV_FDC, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"foldenable", "fen", P_BOOL|P_VI_DEF|P_RWIN, - VAR_WIN, PV_FEN, - {(char_u *)TRUE, (char_u *)0L} SCRIPTID_INIT}, - {"foldexpr", "fde", P_STRING|P_ALLOCED|P_VIM|P_VI_DEF|P_RWIN, - VAR_WIN, PV_FDE, - {(char_u *)"0", (char_u *)NULL} - SCRIPTID_INIT}, - {"foldignore", "fdi", P_STRING|P_ALLOCED|P_VIM|P_VI_DEF|P_RWIN, - VAR_WIN, PV_FDI, - {(char_u *)"#", (char_u *)NULL} SCRIPTID_INIT}, - {"foldlevel", "fdl", P_NUM|P_VI_DEF|P_RWIN, - VAR_WIN, PV_FDL, - {(char_u *)0L, (char_u *)0L} SCRIPTID_INIT}, - {"foldlevelstart","fdls", P_NUM|P_VI_DEF|P_CURSWANT, - (char_u *)&p_fdls, PV_NONE, - {(char_u *)-1L, (char_u *)0L} SCRIPTID_INIT}, - {"foldmarker", "fmr", P_STRING|P_ALLOCED|P_VIM|P_VI_DEF| - P_RWIN|P_COMMA|P_NODUP, - VAR_WIN, PV_FMR, - {(char_u *)"{{{,}}}", (char_u *)NULL} - SCRIPTID_INIT}, - {"foldmethod", "fdm", P_STRING|P_ALLOCED|P_VIM|P_VI_DEF|P_RWIN, - VAR_WIN, PV_FDM, - {(char_u *)"manual", (char_u *)NULL} SCRIPTID_INIT}, - {"foldminlines","fml", P_NUM|P_VI_DEF|P_RWIN, - VAR_WIN, PV_FML, - {(char_u *)1L, (char_u *)0L} SCRIPTID_INIT}, - {"foldnestmax", "fdn", P_NUM|P_VI_DEF|P_RWIN, - VAR_WIN, PV_FDN, - {(char_u *)20L, (char_u *)0L} SCRIPTID_INIT}, - {"foldopen", "fdo", P_STRING|P_VI_DEF|P_COMMA|P_NODUP|P_CURSWANT, - (char_u *)&p_fdo, PV_NONE, - {(char_u *)"block,hor,mark,percent,quickfix,search,tag,undo", - (char_u *)0L} SCRIPTID_INIT}, - {"foldtext", "fdt", P_STRING|P_ALLOCED|P_VIM|P_VI_DEF|P_RWIN, - VAR_WIN, PV_FDT, - {(char_u *)"foldtext()", (char_u *)NULL} - SCRIPTID_INIT}, - {"formatexpr", "fex", P_STRING|P_ALLOCED|P_VI_DEF|P_VIM, - (char_u *)&p_fex, PV_FEX, - {(char_u *)"", (char_u *)0L} - SCRIPTID_INIT}, - {"formatoptions","fo", P_STRING|P_ALLOCED|P_VIM|P_FLAGLIST, - (char_u *)&p_fo, PV_FO, - {(char_u *)DFLT_FO_VI, (char_u *)DFLT_FO_VIM} - SCRIPTID_INIT}, - {"formatlistpat","flp", P_STRING|P_ALLOCED|P_VI_DEF, - (char_u *)&p_flp, PV_FLP, - {(char_u *)"^\\s*\\d\\+[\\]:.)}\\t ]\\s*", - (char_u *)0L} SCRIPTID_INIT}, - {"formatprg", "fp", P_STRING|P_EXPAND|P_VI_DEF|P_SECURE, - (char_u *)&p_fp, PV_NONE, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, - {"fsync", "fs", P_BOOL|P_SECURE|P_VI_DEF, -#ifdef HAVE_FSYNC - (char_u *)&p_fs, PV_NONE, - {(char_u *)TRUE, (char_u *)0L} -#else - (char_u *)NULL, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} -#endif - SCRIPTID_INIT}, - {"gdefault", "gd", P_BOOL|P_VI_DEF|P_VIM, - (char_u *)&p_gd, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"grepformat", "gfm", P_STRING|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)&p_gefm, PV_NONE, - {(char_u *)DFLT_GREPFORMAT, (char_u *)0L} - SCRIPTID_INIT}, - {"grepprg", "gp", P_STRING|P_EXPAND|P_VI_DEF|P_SECURE, - (char_u *)&p_gp, PV_GP, - { -# ifdef UNIX - /* Add an extra file name so that grep will always - * insert a file name in the match line. */ - (char_u *)"grep -n $* /dev/null", -# else - (char_u *)"grep -n ", -# endif - (char_u *)0L - } - SCRIPTID_INIT}, - {"guicursor", "gcr", P_STRING|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)&p_guicursor, PV_NONE, - { - (char_u *)"n-v-c:block,o:hor50,i-ci:hor15,r-cr:hor30,sm:block", - (char_u *)0L - } - SCRIPTID_INIT}, - {"guifont", "gfn", P_STRING|P_VI_DEF|P_RCLR|P_COMMA|P_NODUP, - (char_u *)NULL, PV_NONE, - {(char_u *)NULL, (char_u *)0L} - SCRIPTID_INIT}, - {"guifontset", "gfs", P_STRING|P_VI_DEF|P_RCLR|P_COMMA, - (char_u *)NULL, PV_NONE, - {(char_u *)NULL, (char_u *)0L} - SCRIPTID_INIT}, - {"guifontwide", "gfw", P_STRING|P_VI_DEF|P_RCLR|P_COMMA|P_NODUP, - (char_u *)NULL, PV_NONE, - {(char_u *)NULL, (char_u *)0L} - SCRIPTID_INIT}, - {"guiheadroom", "ghr", P_NUM|P_VI_DEF, - (char_u *)NULL, PV_NONE, - {(char_u *)50L, (char_u *)0L} SCRIPTID_INIT}, - {"guioptions", "go", P_STRING|P_VI_DEF|P_RALL|P_FLAGLIST, - (char_u *)NULL, PV_NONE, - {(char_u *)NULL, (char_u *)0L} - SCRIPTID_INIT}, - {"guipty", NULL, P_BOOL|P_VI_DEF, - (char_u *)NULL, PV_NONE, - {(char_u *)TRUE, (char_u *)0L} SCRIPTID_INIT}, - {"guitablabel", "gtl", P_STRING|P_VI_DEF|P_RWIN, - (char_u *)NULL, PV_NONE, - {(char_u *)NULL, (char_u *)0L} - SCRIPTID_INIT}, - {"guitabtooltip", "gtt", P_STRING|P_VI_DEF|P_RWIN, - (char_u *)NULL, PV_NONE, - {(char_u *)NULL, (char_u *)0L} - SCRIPTID_INIT}, - {"helpfile", "hf", P_STRING|P_EXPAND|P_VI_DEF|P_SECURE, - (char_u *)&p_hf, PV_NONE, - {(char_u *)DFLT_HELPFILE, (char_u *)0L} - SCRIPTID_INIT}, - {"helpheight", "hh", P_NUM|P_VI_DEF, - (char_u *)&p_hh, PV_NONE, - {(char_u *)20L, (char_u *)0L} SCRIPTID_INIT}, - {"helplang", "hlg", P_STRING|P_VI_DEF|P_COMMA, - (char_u *)&p_hlg, PV_NONE, - {(char_u *)"", (char_u *)0L} - SCRIPTID_INIT}, - {"hidden", "hid", P_BOOL|P_VI_DEF, - (char_u *)&p_hid, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"highlight", "hl", P_STRING|P_VI_DEF|P_RCLR|P_COMMA|P_NODUP, - (char_u *)&p_hl, PV_NONE, - {(char_u *)HIGHLIGHT_INIT, (char_u *)0L} - SCRIPTID_INIT}, - {"history", "hi", P_NUM|P_VIM, - (char_u *)&p_hi, PV_NONE, - {(char_u *)0L, (char_u *)50L} SCRIPTID_INIT}, - {"hkmap", "hk", P_BOOL|P_VI_DEF|P_VIM, - (char_u *)&p_hkmap, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"hkmapp", "hkp", P_BOOL|P_VI_DEF|P_VIM, - (char_u *)&p_hkmapp, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"hlsearch", "hls", P_BOOL|P_VIM|P_RALL, - (char_u *)&p_hls, PV_NONE, - {(char_u *)FALSE, (char_u *)TRUE} SCRIPTID_INIT}, - {"icon", NULL, P_BOOL|P_VI_DEF, - (char_u *)&p_icon, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"iconstring", NULL, P_STRING|P_VI_DEF, - (char_u *)&p_iconstring, PV_NONE, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, - {"ignorecase", "ic", P_BOOL|P_VI_DEF, - (char_u *)&p_ic, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"imactivatefunc","imaf",P_STRING|P_VI_DEF|P_SECURE, - (char_u *)NULL, PV_NONE, - {(char_u *)NULL, (char_u *)0L} - SCRIPTID_INIT}, - {"imactivatekey","imak",P_STRING|P_VI_DEF, - (char_u *)NULL, PV_NONE, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, - {"imcmdline", "imc", P_BOOL|P_VI_DEF, - (char_u *)NULL, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"imdisable", "imd", P_BOOL|P_VI_DEF, - (char_u *)NULL, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} - SCRIPTID_INIT}, - {"iminsert", "imi", P_NUM|P_VI_DEF, - (char_u *)&p_iminsert, PV_IMI, -#ifdef B_IMODE_IM - {(char_u *)B_IMODE_IM, (char_u *)0L} -#else - {(char_u *)B_IMODE_NONE, (char_u *)0L} -#endif - SCRIPTID_INIT}, - {"imsearch", "ims", P_NUM|P_VI_DEF, - (char_u *)&p_imsearch, PV_IMS, -#ifdef B_IMODE_IM - {(char_u *)B_IMODE_IM, (char_u *)0L} -#else - {(char_u *)B_IMODE_NONE, (char_u *)0L} -#endif - SCRIPTID_INIT}, - {"imstatusfunc","imsf",P_STRING|P_VI_DEF|P_SECURE, - (char_u *)NULL, PV_NONE, - {(char_u *)NULL, (char_u *)0L} - SCRIPTID_INIT}, - {"include", "inc", P_STRING|P_ALLOCED|P_VI_DEF, - (char_u *)&p_inc, PV_INC, - {(char_u *)"^\\s*#\\s*include", (char_u *)0L} - SCRIPTID_INIT}, - {"includeexpr", "inex", P_STRING|P_ALLOCED|P_VI_DEF, - (char_u *)&p_inex, PV_INEX, - {(char_u *)"", (char_u *)0L} - SCRIPTID_INIT}, - {"incsearch", "is", P_BOOL|P_VIM, - (char_u *)&p_is, PV_NONE, - {(char_u *)FALSE, (char_u *)TRUE} SCRIPTID_INIT}, - {"indentexpr", "inde", P_STRING|P_ALLOCED|P_VI_DEF|P_VIM, - (char_u *)&p_inde, PV_INDE, - {(char_u *)"", (char_u *)0L} - SCRIPTID_INIT}, - {"indentkeys", "indk", P_STRING|P_ALLOCED|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)&p_indk, PV_INDK, - {(char_u *)"0{,0},:,0#,!^F,o,O,e", (char_u *)0L} - SCRIPTID_INIT}, - {"infercase", "inf", P_BOOL|P_VI_DEF, - (char_u *)&p_inf, PV_INF, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"insertmode", "im", P_BOOL|P_VI_DEF|P_VIM, - (char_u *)&p_im, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"isfname", "isf", P_STRING|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)&p_isf, PV_NONE, - { -#ifdef BACKSLASH_IN_FILENAME - /* Excluded are: & and ^ are special in cmd.exe - * ( and ) are used in text separating fnames */ - (char_u *)"@,48-57,/,\\,.,-,_,+,,,#,$,%,{,},[,],:,@-@,!,~,=", -#else - (char_u *)"@,48-57,/,.,-,_,+,,,#,$,%,~,=", -#endif - (char_u *)0L - } SCRIPTID_INIT}, - {"isident", "isi", P_STRING|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)&p_isi, PV_NONE, - { - (char_u *)"@,48-57,_,192-255", - (char_u *)0L - } SCRIPTID_INIT}, - {"iskeyword", "isk", P_STRING|P_ALLOCED|P_VIM|P_COMMA|P_NODUP, - (char_u *)&p_isk, PV_ISK, - { - (char_u *)"@,48-57,_", - ISK_LATIN1 - } SCRIPTID_INIT}, - {"isprint", "isp", P_STRING|P_VI_DEF|P_RALL|P_COMMA|P_NODUP, - (char_u *)&p_isp, PV_NONE, - { -#if defined(MSWIN) - (char_u *)"@,~-255", -#else - ISP_LATIN1, -#endif - (char_u *)0L - } SCRIPTID_INIT}, - {"joinspaces", "js", P_BOOL|P_VI_DEF|P_VIM, - (char_u *)&p_js, PV_NONE, - {(char_u *)TRUE, (char_u *)0L} SCRIPTID_INIT}, - {"keymap", "kmp", P_STRING|P_ALLOCED|P_VI_DEF|P_RBUF|P_RSTAT|P_NFNAME| - P_PRI_MKRC, - (char_u *)&p_keymap, PV_KMAP, - {(char_u *)"", (char_u *)0L} - SCRIPTID_INIT}, - {"keymodel", "km", P_STRING|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)&p_km, PV_NONE, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, - {"keywordprg", "kp", P_STRING|P_EXPAND|P_VI_DEF|P_SECURE, - (char_u *)&p_kp, PV_KP, - { -# ifdef USEMAN_S - (char_u *)"man -s", -# else - (char_u *)"man", -# endif - (char_u *)0L - } SCRIPTID_INIT}, - {"langmap", "lmap", P_STRING|P_VI_DEF|P_COMMA|P_NODUP|P_SECURE, - (char_u *)&p_langmap, PV_NONE, - {(char_u *)"", /* unmatched } */ - (char_u *)0L} SCRIPTID_INIT}, - {"langmenu", "lm", P_STRING|P_VI_DEF|P_NFNAME, - (char_u *)&p_lm, PV_NONE, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, - {"langnoremap", "lnr", P_BOOL, - (char_u *)&p_lnr, PV_NONE, - {(char_u *)FALSE, (char_u *)TRUE} SCRIPTID_INIT}, - {"laststatus", "ls", P_NUM|P_VI_DEF|P_RALL, - (char_u *)&p_ls, PV_NONE, - {(char_u *)1L, (char_u *)0L} SCRIPTID_INIT}, - {"lazyredraw", "lz", P_BOOL|P_VI_DEF, - (char_u *)&p_lz, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"linebreak", "lbr", P_BOOL|P_VI_DEF|P_RWIN, - VAR_WIN, PV_LBR, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"lines", NULL, P_NUM|P_NODEFAULT|P_NO_MKRC|P_VI_DEF|P_RCLR, - (char_u *)&Rows, PV_NONE, - { - (char_u *)24L, - (char_u *)0L - } SCRIPTID_INIT}, - {"linespace", "lsp", P_NUM|P_VI_DEF|P_RCLR, - (char_u *)NULL, PV_NONE, - {(char_u *)0L, (char_u *)0L} - SCRIPTID_INIT}, - {"lisp", NULL, P_BOOL|P_VI_DEF, - (char_u *)&p_lisp, PV_LISP, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"lispwords", "lw", P_STRING|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)&p_lispwords, PV_LW, - {(char_u *)LISPWORD_VALUE, (char_u *)0L} - SCRIPTID_INIT}, - {"list", NULL, P_BOOL|P_VI_DEF|P_RWIN, - VAR_WIN, PV_LIST, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"listchars", "lcs", P_STRING|P_VI_DEF|P_RALL|P_COMMA|P_NODUP, - (char_u *)&p_lcs, PV_NONE, - {(char_u *)"eol:$", (char_u *)0L} SCRIPTID_INIT}, - {"loadplugins", "lpl", P_BOOL|P_VI_DEF, - (char_u *)&p_lpl, PV_NONE, - {(char_u *)TRUE, (char_u *)0L} SCRIPTID_INIT}, - {"magic", NULL, P_BOOL|P_VI_DEF, - (char_u *)&p_magic, PV_NONE, - {(char_u *)TRUE, (char_u *)0L} SCRIPTID_INIT}, - {"makeef", "mef", P_STRING|P_EXPAND|P_VI_DEF|P_SECURE, - (char_u *)&p_mef, PV_NONE, - {(char_u *)"", (char_u *)0L} - SCRIPTID_INIT}, - {"makeprg", "mp", P_STRING|P_EXPAND|P_VI_DEF|P_SECURE, - (char_u *)&p_mp, PV_MP, - {(char_u *)"make", (char_u *)0L} - SCRIPTID_INIT}, - {"matchpairs", "mps", P_STRING|P_ALLOCED|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)&p_mps, PV_MPS, - {(char_u *)"(:),{:},[:]", (char_u *)0L} - SCRIPTID_INIT}, - {"matchtime", "mat", P_NUM|P_VI_DEF, - (char_u *)&p_mat, PV_NONE, - {(char_u *)5L, (char_u *)0L} SCRIPTID_INIT}, - {"maxcombine", "mco", P_NUM|P_VI_DEF|P_CURSWANT, - (char_u *)&p_mco, PV_NONE, - {(char_u *)2, (char_u *)0L} SCRIPTID_INIT}, - {"maxfuncdepth", "mfd", P_NUM|P_VI_DEF, - (char_u *)&p_mfd, PV_NONE, - {(char_u *)100L, (char_u *)0L} SCRIPTID_INIT}, - {"maxmapdepth", "mmd", P_NUM|P_VI_DEF, - (char_u *)&p_mmd, PV_NONE, - {(char_u *)1000L, (char_u *)0L} SCRIPTID_INIT}, - {"maxmem", "mm", P_NUM|P_VI_DEF, - (char_u *)&p_mm, PV_NONE, - {(char_u *)DFLT_MAXMEM, (char_u *)0L} - SCRIPTID_INIT}, - {"maxmempattern","mmp", P_NUM|P_VI_DEF, - (char_u *)&p_mmp, PV_NONE, - {(char_u *)1000L, (char_u *)0L} SCRIPTID_INIT}, - {"maxmemtot", "mmt", P_NUM|P_VI_DEF, - (char_u *)&p_mmt, PV_NONE, - {(char_u *)DFLT_MAXMEMTOT, (char_u *)0L} - SCRIPTID_INIT}, - {"menuitems", "mis", P_NUM|P_VI_DEF, - (char_u *)&p_mis, PV_NONE, - {(char_u *)25L, (char_u *)0L} SCRIPTID_INIT}, - {"mkspellmem", "msm", P_STRING|P_VI_DEF|P_EXPAND|P_SECURE, - (char_u *)&p_msm, PV_NONE, - {(char_u *)"460000,2000,500", (char_u *)0L} - SCRIPTID_INIT}, - {"modeline", "ml", P_BOOL|P_VIM, - (char_u *)&p_ml, PV_ML, - {(char_u *)FALSE, (char_u *)TRUE} SCRIPTID_INIT}, - {"modelines", "mls", P_NUM|P_VI_DEF, - (char_u *)&p_mls, PV_NONE, - {(char_u *)5L, (char_u *)0L} SCRIPTID_INIT}, - {"modifiable", "ma", P_BOOL|P_VI_DEF|P_NOGLOB, - (char_u *)&p_ma, PV_MA, - {(char_u *)TRUE, (char_u *)0L} SCRIPTID_INIT}, - {"modified", "mod", P_BOOL|P_NO_MKRC|P_VI_DEF|P_RSTAT, - (char_u *)&p_mod, PV_MOD, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"more", NULL, P_BOOL|P_VIM, - (char_u *)&p_more, PV_NONE, - {(char_u *)FALSE, (char_u *)TRUE} SCRIPTID_INIT}, - {"mouse", NULL, P_STRING|P_FLAGLIST, - (char_u *)&p_mouse, PV_NONE, - { - (char_u *)"", - (char_u *)"a" - } SCRIPTID_INIT}, - {"mousefocus", "mousef", P_BOOL|P_VI_DEF, - (char_u *)NULL, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"mousehide", "mh", P_BOOL|P_VI_DEF, - (char_u *)NULL, PV_NONE, - {(char_u *)TRUE, (char_u *)0L} SCRIPTID_INIT}, - {"mousemodel", "mousem", P_STRING|P_VI_DEF, - (char_u *)&p_mousem, PV_NONE, - { - (char_u *)"extend", - (char_u *)0L - } SCRIPTID_INIT}, - {"mouseshape", "mouses", P_STRING|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)NULL, PV_NONE, - {(char_u *)NULL, (char_u *)0L} - SCRIPTID_INIT}, - {"mousetime", "mouset", P_NUM|P_VI_DEF, - (char_u *)&p_mouset, PV_NONE, - {(char_u *)500L, (char_u *)0L} SCRIPTID_INIT}, - {"nrformats", "nf", P_STRING|P_ALLOCED|P_VIM|P_COMMA|P_NODUP, - (char_u *)&p_nf, PV_NF, - {(char_u *)"octal,hex", (char_u *)"hex"} - SCRIPTID_INIT}, - {"number", "nu", P_BOOL|P_VI_DEF|P_RWIN, - VAR_WIN, PV_NU, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"numberwidth", "nuw", P_NUM|P_RWIN|P_VIM, - VAR_WIN, PV_NUW, - {(char_u *)8L, (char_u *)4L} SCRIPTID_INIT}, - {"omnifunc", "ofu", P_STRING|P_ALLOCED|P_VI_DEF|P_SECURE, - (char_u *)&p_ofu, PV_OFU, - {(char_u *)"", (char_u *)0L} - SCRIPTID_INIT}, - {"opendevice", "odev", P_BOOL|P_VI_DEF, - (char_u *)NULL, PV_NONE, - {(char_u *)FALSE, (char_u *)FALSE} - SCRIPTID_INIT}, - {"operatorfunc", "opfunc", P_STRING|P_VI_DEF|P_SECURE, - (char_u *)&p_opfunc, PV_NONE, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, - {"paragraphs", "para", P_STRING|P_VI_DEF, - (char_u *)&p_para, PV_NONE, - {(char_u *)"IPLPPPQPP TPHPLIPpLpItpplpipbp", - (char_u *)0L} SCRIPTID_INIT}, - {"paste", NULL, P_BOOL|P_VI_DEF|P_PRI_MKRC, - (char_u *)&p_paste, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"pastetoggle", "pt", P_STRING|P_VI_DEF, - (char_u *)&p_pt, PV_NONE, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, - {"patchexpr", "pex", P_STRING|P_VI_DEF|P_SECURE, - (char_u *)&p_pex, PV_NONE, - {(char_u *)"", (char_u *)0L} - SCRIPTID_INIT}, - {"patchmode", "pm", P_STRING|P_VI_DEF|P_NFNAME, - (char_u *)&p_pm, PV_NONE, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, - {"path", "pa", P_STRING|P_EXPAND|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)&p_path, PV_PATH, - { - (char_u *)".,/usr/include,,", - (char_u *)0L - } SCRIPTID_INIT}, - {"preserveindent", "pi", P_BOOL|P_VI_DEF|P_VIM, - (char_u *)&p_pi, PV_PI, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"previewheight", "pvh", P_NUM|P_VI_DEF, - (char_u *)&p_pvh, PV_NONE, - {(char_u *)12L, (char_u *)0L} SCRIPTID_INIT}, - {"previewwindow", "pvw", P_BOOL|P_VI_DEF|P_RSTAT|P_NOGLOB, - VAR_WIN, PV_PVW, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"printdevice", "pdev", P_STRING|P_VI_DEF|P_SECURE, - (char_u *)&p_pdev, PV_NONE, - {(char_u *)"", (char_u *)0L} - SCRIPTID_INIT}, - {"printencoding", "penc", P_STRING|P_VI_DEF, - (char_u *)&p_penc, PV_NONE, - {(char_u *)"", (char_u *)0L} - SCRIPTID_INIT}, - {"printexpr", "pexpr", P_STRING|P_VI_DEF, - (char_u *)&p_pexpr, PV_NONE, - {(char_u *)"", (char_u *)0L} - SCRIPTID_INIT}, - {"printfont", "pfn", P_STRING|P_VI_DEF, - (char_u *)&p_pfn, PV_NONE, - { - (char_u *)"courier", - (char_u *)0L - } - SCRIPTID_INIT}, - {"printheader", "pheader", P_STRING|P_VI_DEF|P_GETTEXT, - (char_u *)&p_header, PV_NONE, - {(char_u *)N_("%<%f%h%m%=Page %N"), (char_u *)0L} - SCRIPTID_INIT}, - {"printmbcharset", "pmbcs", P_STRING|P_VI_DEF, - (char_u *)&p_pmcs, PV_NONE, - {(char_u *)"", (char_u *)0L} - SCRIPTID_INIT}, - {"printmbfont", "pmbfn", P_STRING|P_VI_DEF, - (char_u *)&p_pmfn, PV_NONE, - {(char_u *)"", (char_u *)0L} - SCRIPTID_INIT}, - {"printoptions", "popt", P_STRING|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)&p_popt, PV_NONE, - {(char_u *)"", (char_u *)0L} - SCRIPTID_INIT}, - {"prompt", NULL, P_BOOL|P_VI_DEF, - (char_u *)&p_prompt, PV_NONE, - {(char_u *)TRUE, (char_u *)0L} SCRIPTID_INIT}, - {"pumheight", "ph", P_NUM|P_VI_DEF, - (char_u *)&p_ph, PV_NONE, - {(char_u *)0L, (char_u *)0L} SCRIPTID_INIT}, - {"quoteescape", "qe", P_STRING|P_ALLOCED|P_VI_DEF, - (char_u *)&p_qe, PV_QE, - {(char_u *)"\\", (char_u *)0L} - SCRIPTID_INIT}, - {"readonly", "ro", P_BOOL|P_VI_DEF|P_RSTAT|P_NOGLOB, - (char_u *)&p_ro, PV_RO, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"redrawtime", "rdt", P_NUM|P_VI_DEF, - (char_u *)&p_rdt, PV_NONE, - {(char_u *)2000L, (char_u *)0L} SCRIPTID_INIT}, - {"regexpengine", "re", P_NUM|P_VI_DEF, - (char_u *)&p_re, PV_NONE, - {(char_u *)0L, (char_u *)0L} SCRIPTID_INIT}, - {"relativenumber", "rnu", P_BOOL|P_VI_DEF|P_RWIN, - VAR_WIN, PV_RNU, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"remap", NULL, P_BOOL|P_VI_DEF, - (char_u *)&p_remap, PV_NONE, - {(char_u *)TRUE, (char_u *)0L} SCRIPTID_INIT}, - {"report", NULL, P_NUM|P_VI_DEF, - (char_u *)&p_report, PV_NONE, - {(char_u *)2L, (char_u *)0L} SCRIPTID_INIT}, - {"restorescreen", "rs", P_BOOL|P_VI_DEF, - (char_u *)NULL, PV_NONE, - {(char_u *)TRUE, (char_u *)0L} SCRIPTID_INIT}, - {"revins", "ri", P_BOOL|P_VI_DEF|P_VIM, - (char_u *)&p_ri, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"rightleft", "rl", P_BOOL|P_VI_DEF|P_RWIN, - VAR_WIN, PV_RL, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"rightleftcmd", "rlc", P_STRING|P_ALLOCED|P_VI_DEF|P_RWIN, - VAR_WIN, PV_RLC, - {(char_u *)"search", (char_u *)NULL} - SCRIPTID_INIT}, - {"ruler", "ru", P_BOOL|P_VI_DEF|P_VIM|P_RSTAT, - (char_u *)&p_ru, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"rulerformat", "ruf", P_STRING|P_VI_DEF|P_ALLOCED|P_RSTAT, - (char_u *)&p_ruf, PV_NONE, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, - {"runtimepath", "rtp", P_STRING|P_VI_DEF|P_EXPAND|P_COMMA|P_NODUP|P_SECURE, - (char_u *)&p_rtp, PV_NONE, - {(char_u *)DFLT_RUNTIMEPATH, (char_u *)0L} - SCRIPTID_INIT}, - {"scroll", "scr", P_NUM|P_NO_MKRC|P_VI_DEF, - VAR_WIN, PV_SCROLL, - {(char_u *)12L, (char_u *)0L} SCRIPTID_INIT}, - {"scrollbind", "scb", P_BOOL|P_VI_DEF, - VAR_WIN, PV_SCBIND, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"scrolljump", "sj", P_NUM|P_VI_DEF|P_VIM, - (char_u *)&p_sj, PV_NONE, - {(char_u *)1L, (char_u *)0L} SCRIPTID_INIT}, - {"scrolloff", "so", P_NUM|P_VI_DEF|P_VIM|P_RALL, - (char_u *)&p_so, PV_NONE, - {(char_u *)0L, (char_u *)0L} SCRIPTID_INIT}, - {"scrollopt", "sbo", P_STRING|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)&p_sbo, PV_NONE, - {(char_u *)"ver,jump", (char_u *)0L} - SCRIPTID_INIT}, - {"sections", "sect", P_STRING|P_VI_DEF, - (char_u *)&p_sections, PV_NONE, - {(char_u *)"SHNHH HUnhsh", (char_u *)0L} - SCRIPTID_INIT}, - {"secure", NULL, P_BOOL|P_VI_DEF|P_SECURE, - (char_u *)&p_secure, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"selection", "sel", P_STRING|P_VI_DEF, - (char_u *)&p_sel, PV_NONE, - {(char_u *)"inclusive", (char_u *)0L} - SCRIPTID_INIT}, - {"selectmode", "slm", P_STRING|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)&p_slm, PV_NONE, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, - {"sessionoptions", "ssop", P_STRING|P_VIM|P_COMMA|P_NODUP, - (char_u *)&p_ssop, PV_NONE, - {(char_u *)"blank,buffers,curdir,folds,help,options,tabpages,winsize", - (char_u *)"blank,buffers,curdir,folds,help,tabpages,winsize"} - SCRIPTID_INIT}, - {"shell", "sh", P_STRING|P_EXPAND|P_VI_DEF|P_SECURE, - (char_u *)&p_sh, PV_NONE, - { - (char_u *)"sh", - (char_u *)0L - } SCRIPTID_INIT}, - {"shellcmdflag","shcf", P_STRING|P_VI_DEF|P_SECURE, - (char_u *)&p_shcf, PV_NONE, - { - (char_u *)"-c", - (char_u *)0L - } SCRIPTID_INIT}, - {"shellpipe", "sp", P_STRING|P_VI_DEF|P_SECURE, - (char_u *)&p_sp, PV_NONE, - { -#if defined(UNIX) - (char_u *)"| tee", -#else - (char_u *)">", -#endif - (char_u *)0L - } - SCRIPTID_INIT}, - {"shellquote", "shq", P_STRING|P_VI_DEF|P_SECURE, - (char_u *)&p_shq, PV_NONE, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, - {"shellredir", "srr", P_STRING|P_VI_DEF|P_SECURE, - (char_u *)&p_srr, PV_NONE, - {(char_u *)">", (char_u *)0L} SCRIPTID_INIT}, - {"shellslash", "ssl", P_BOOL|P_VI_DEF, -#ifdef BACKSLASH_IN_FILENAME - (char_u *)&p_ssl, PV_NONE, -#else - (char_u *)NULL, PV_NONE, -#endif - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"shelltemp", "stmp", P_BOOL, - (char_u *)&p_stmp, PV_NONE, - {(char_u *)FALSE, (char_u *)TRUE} SCRIPTID_INIT}, - {"shellxquote", "sxq", P_STRING|P_VI_DEF|P_SECURE, - (char_u *)&p_sxq, PV_NONE, - { - (char_u *)"", - (char_u *)0L - } SCRIPTID_INIT}, - {"shellxescape", "sxe", P_STRING|P_VI_DEF|P_SECURE, - (char_u *)&p_sxe, PV_NONE, - { - (char_u *)"", - (char_u *)0L - } SCRIPTID_INIT}, - {"shiftround", "sr", P_BOOL|P_VI_DEF|P_VIM, - (char_u *)&p_sr, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"shiftwidth", "sw", P_NUM|P_VI_DEF, - (char_u *)&p_sw, PV_SW, - {(char_u *)8L, (char_u *)0L} SCRIPTID_INIT}, - {"shortmess", "shm", P_STRING|P_VIM|P_FLAGLIST, - (char_u *)&p_shm, PV_NONE, - {(char_u *)"", (char_u *)"filnxtToO"} - SCRIPTID_INIT}, - {"showbreak", "sbr", P_STRING|P_VI_DEF|P_RALL, - (char_u *)&p_sbr, PV_NONE, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, - {"showcmd", "sc", P_BOOL|P_VIM, - (char_u *)&p_sc, PV_NONE, - {(char_u *)FALSE, -#ifdef UNIX - (char_u *)FALSE -#else - (char_u *) TRUE -#endif - } SCRIPTID_INIT}, - {"showfulltag", "sft", P_BOOL|P_VI_DEF, - (char_u *)&p_sft, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"showmatch", "sm", P_BOOL|P_VI_DEF, - (char_u *)&p_sm, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"showmode", "smd", P_BOOL|P_VIM, - (char_u *)&p_smd, PV_NONE, - {(char_u *)FALSE, (char_u *)TRUE} SCRIPTID_INIT}, - {"showtabline", "stal", P_NUM|P_VI_DEF|P_RALL, - (char_u *)&p_stal, PV_NONE, - {(char_u *)1L, (char_u *)0L} SCRIPTID_INIT}, - {"sidescroll", "ss", P_NUM|P_VI_DEF, - (char_u *)&p_ss, PV_NONE, - {(char_u *)0L, (char_u *)0L} SCRIPTID_INIT}, - {"sidescrolloff", "siso", P_NUM|P_VI_DEF|P_VIM|P_RBUF, - (char_u *)&p_siso, PV_NONE, - {(char_u *)0L, (char_u *)0L} SCRIPTID_INIT}, - {"smartcase", "scs", P_BOOL|P_VI_DEF|P_VIM, - (char_u *)&p_scs, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"smartindent", "si", P_BOOL|P_VI_DEF|P_VIM, - (char_u *)&p_si, PV_SI, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"smarttab", "sta", P_BOOL|P_VIM, - (char_u *)&p_sta, PV_NONE, - {(char_u *)FALSE, (char_u *)TRUE} SCRIPTID_INIT}, - {"softtabstop", "sts", P_NUM|P_VI_DEF|P_VIM, - (char_u *)&p_sts, PV_STS, - {(char_u *)0L, (char_u *)0L} SCRIPTID_INIT}, - {"spell", NULL, P_BOOL|P_VI_DEF|P_RWIN, - VAR_WIN, PV_SPELL, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"spellcapcheck", "spc", P_STRING|P_ALLOCED|P_VI_DEF|P_RBUF, - (char_u *)&p_spc, PV_SPC, - {(char_u *)"[.?!]\\_[\\])'\" ]\\+", (char_u *)0L} - SCRIPTID_INIT}, - {"spellfile", "spf", P_STRING|P_EXPAND|P_ALLOCED|P_VI_DEF|P_SECURE|P_COMMA, - (char_u *)&p_spf, PV_SPF, - {(char_u *)"", (char_u *)0L} - SCRIPTID_INIT}, - {"spelllang", "spl", P_STRING|P_ALLOCED|P_VI_DEF|P_COMMA|P_RBUF|P_EXPAND, - (char_u *)&p_spl, PV_SPL, - {(char_u *)"en", (char_u *)0L} - SCRIPTID_INIT}, - {"spellsuggest", "sps", P_STRING|P_VI_DEF|P_EXPAND|P_SECURE|P_COMMA, - (char_u *)&p_sps, PV_NONE, - {(char_u *)"best", (char_u *)0L} - SCRIPTID_INIT}, - {"splitbelow", "sb", P_BOOL|P_VI_DEF, - (char_u *)&p_sb, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"splitright", "spr", P_BOOL|P_VI_DEF, - (char_u *)&p_spr, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"startofline", "sol", P_BOOL|P_VI_DEF|P_VIM, - (char_u *)&p_sol, PV_NONE, - {(char_u *)TRUE, (char_u *)0L} SCRIPTID_INIT}, - {"statusline","stl", P_STRING|P_VI_DEF|P_ALLOCED|P_RSTAT, - (char_u *)&p_stl, PV_STL, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, - {"suffixes", "su", P_STRING|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)&p_su, PV_NONE, - {(char_u *)".bak,~,.o,.h,.info,.swp,.obj", - (char_u *)0L} SCRIPTID_INIT}, - {"suffixesadd", "sua", P_STRING|P_VI_DEF|P_ALLOCED|P_COMMA|P_NODUP, - (char_u *)&p_sua, PV_SUA, - {(char_u *)"", (char_u *)0L} - SCRIPTID_INIT}, - {"swapfile", "swf", P_BOOL|P_VI_DEF|P_RSTAT, - (char_u *)&p_swf, PV_SWF, - {(char_u *)TRUE, (char_u *)0L} SCRIPTID_INIT}, - {"swapsync", "sws", P_STRING|P_VI_DEF, - (char_u *)&p_sws, PV_NONE, - {(char_u *)"fsync", (char_u *)0L} SCRIPTID_INIT}, - {"switchbuf", "swb", P_STRING|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)&p_swb, PV_NONE, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, - {"synmaxcol", "smc", P_NUM|P_VI_DEF|P_RBUF, - (char_u *)&p_smc, PV_SMC, - {(char_u *)3000L, (char_u *)0L} - SCRIPTID_INIT}, - {"syntax", "syn", P_STRING|P_ALLOCED|P_VI_DEF|P_NOGLOB|P_NFNAME, - (char_u *)&p_syn, PV_SYN, - {(char_u *)"", (char_u *)0L} - SCRIPTID_INIT}, - {"tabline", "tal", P_STRING|P_VI_DEF|P_RALL, - (char_u *)&p_tal, PV_NONE, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, - {"tabpagemax", "tpm", P_NUM|P_VIM, - (char_u *)&p_tpm, PV_NONE, - {(char_u *)10L, (char_u *)50L} SCRIPTID_INIT}, - {"tabstop", "ts", P_NUM|P_VI_DEF|P_RBUF, - (char_u *)&p_ts, PV_TS, - {(char_u *)8L, (char_u *)0L} SCRIPTID_INIT}, - {"tagbsearch", "tbs", P_BOOL|P_VI_DEF, - (char_u *)&p_tbs, PV_NONE, - {(char_u *)TRUE, (char_u *)0L} - SCRIPTID_INIT}, - {"taglength", "tl", P_NUM|P_VI_DEF, - (char_u *)&p_tl, PV_NONE, - {(char_u *)0L, (char_u *)0L} SCRIPTID_INIT}, - {"tagrelative", "tr", P_BOOL|P_VIM, - (char_u *)&p_tr, PV_NONE, - {(char_u *)FALSE, (char_u *)TRUE} SCRIPTID_INIT}, - {"tags", "tag", P_STRING|P_EXPAND|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)&p_tags, PV_TAGS, - { - (char_u *)"./tags;,tags", - (char_u *)0L - } SCRIPTID_INIT}, - {"tagstack", "tgst", P_BOOL|P_VI_DEF, - (char_u *)&p_tgst, PV_NONE, - {(char_u *)TRUE, (char_u *)0L} SCRIPTID_INIT}, - {"termbidi", "tbidi", P_BOOL|P_VI_DEF, - (char_u *)&p_tbidi, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"termencoding", "tenc", P_STRING|P_VI_DEF|P_RCLR, - (char_u *)NULL, PV_NONE, - {(char_u *)"", (char_u *)0L} - SCRIPTID_INIT}, - {"terse", NULL, P_BOOL|P_VI_DEF, - (char_u *)&p_terse, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"textwidth", "tw", P_NUM|P_VI_DEF|P_VIM|P_RBUF, - (char_u *)&p_tw, PV_TW, - {(char_u *)0L, (char_u *)0L} SCRIPTID_INIT}, - {"thesaurus", "tsr", P_STRING|P_EXPAND|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)&p_tsr, PV_TSR, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, - {"tildeop", "top", P_BOOL|P_VI_DEF|P_VIM, - (char_u *)&p_to, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"timeout", "to", P_BOOL|P_VI_DEF, - (char_u *)&p_timeout, PV_NONE, - {(char_u *)TRUE, (char_u *)0L} SCRIPTID_INIT}, - {"timeoutlen", "tm", P_NUM|P_VI_DEF, - (char_u *)&p_tm, PV_NONE, - {(char_u *)1000L, (char_u *)0L} SCRIPTID_INIT}, - {"title", NULL, P_BOOL|P_VI_DEF, - (char_u *)&p_title, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"titlelen", NULL, P_NUM|P_VI_DEF, - (char_u *)&p_titlelen, PV_NONE, - {(char_u *)85L, (char_u *)0L} SCRIPTID_INIT}, - {"titleold", NULL, P_STRING|P_VI_DEF|P_GETTEXT|P_SECURE|P_NO_MKRC, - (char_u *)&p_titleold, PV_NONE, - {(char_u *)N_("Thanks for flying Vim"), - (char_u *)0L} - SCRIPTID_INIT}, - {"titlestring", NULL, P_STRING|P_VI_DEF, - (char_u *)&p_titlestring, PV_NONE, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, - {"ttimeout", NULL, P_BOOL|P_VI_DEF|P_VIM, - (char_u *)&p_ttimeout, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"ttimeoutlen", "ttm", P_NUM|P_VI_DEF, - (char_u *)&p_ttm, PV_NONE, - {(char_u *)-1L, (char_u *)0L} SCRIPTID_INIT}, - {"ttyfast", "tf", P_BOOL|P_NO_MKRC|P_VI_DEF, - (char_u *)&p_force_on, PV_NONE, - {(char_u *)TRUE, (char_u *)0L} SCRIPTID_INIT}, - {"undodir", "udir", P_STRING|P_EXPAND|P_COMMA|P_NODUP|P_SECURE|P_VI_DEF, - (char_u *)&p_udir, PV_NONE, - {(char_u *)".", (char_u *)0L} - SCRIPTID_INIT}, - {"undofile", "udf", P_BOOL|P_VI_DEF|P_VIM, - (char_u *)&p_udf, PV_UDF, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"undolevels", "ul", P_NUM|P_VI_DEF, - (char_u *)&p_ul, PV_UL, - { -#if defined(UNIX) || defined(WIN3264) - (char_u *)1000L, -#else - (char_u *)100L, + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "options.generated.h" #endif - (char_u *)0L - } SCRIPTID_INIT}, - {"undoreload", "ur", P_NUM|P_VI_DEF, - (char_u *)&p_ur, PV_NONE, - { (char_u *)10000L, (char_u *)0L} SCRIPTID_INIT}, - {"updatecount", "uc", P_NUM|P_VI_DEF, - (char_u *)&p_uc, PV_NONE, - {(char_u *)200L, (char_u *)0L} SCRIPTID_INIT}, - {"updatetime", "ut", P_NUM|P_VI_DEF, - (char_u *)&p_ut, PV_NONE, - {(char_u *)4000L, (char_u *)0L} SCRIPTID_INIT}, - {"verbose", "vbs", P_NUM|P_VI_DEF, - (char_u *)&p_verbose, PV_NONE, - {(char_u *)0L, (char_u *)0L} SCRIPTID_INIT}, - {"verbosefile", "vfile", P_STRING|P_EXPAND|P_VI_DEF|P_SECURE, - (char_u *)&p_vfile, PV_NONE, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, - {"viewdir", "vdir", P_STRING|P_EXPAND|P_VI_DEF|P_SECURE, - (char_u *)&p_vdir, PV_NONE, - {(char_u *)DFLT_VDIR, (char_u *)0L} - SCRIPTID_INIT}, - {"viewoptions", "vop", P_STRING|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)&p_vop, PV_NONE, - {(char_u *)"folds,options,cursor", (char_u *)0L} - SCRIPTID_INIT}, - {"viminfo", "vi", P_STRING|P_COMMA|P_NODUP|P_SECURE, - (char_u *)&p_viminfo, PV_NONE, - {(char_u *)"", (char_u *)"!,'100,<50,s10,h"} - SCRIPTID_INIT}, - {"virtualedit", "ve", P_STRING|P_COMMA|P_NODUP|P_VI_DEF|P_VIM|P_CURSWANT, - (char_u *)&p_ve, PV_NONE, - {(char_u *)"", (char_u *)""} - SCRIPTID_INIT}, - {"visualbell", "vb", P_BOOL|P_VI_DEF, - (char_u *)&p_vb, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"warn", NULL, P_BOOL|P_VI_DEF, - (char_u *)&p_warn, PV_NONE, - {(char_u *)TRUE, (char_u *)0L} SCRIPTID_INIT}, - {"whichwrap", "ww", P_STRING|P_VIM|P_COMMA|P_FLAGLIST, - (char_u *)&p_ww, PV_NONE, - {(char_u *)"", (char_u *)"b,s"} SCRIPTID_INIT}, - {"wildchar", "wc", P_NUM|P_VIM, - (char_u *)&p_wc, PV_NONE, - {(char_u *)(long)Ctrl_E, (char_u *)(long)TAB} - SCRIPTID_INIT}, - {"wildcharm", "wcm", P_NUM|P_VI_DEF, - (char_u *)&p_wcm, PV_NONE, - {(char_u *)0L, (char_u *)0L} SCRIPTID_INIT}, - {"wildignore", "wig", P_STRING|P_VI_DEF|P_COMMA|P_NODUP, - (char_u *)&p_wig, PV_NONE, - {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, - {"wildignorecase", "wic", P_BOOL|P_VI_DEF, - (char_u *)&p_wic, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"wildmenu", "wmnu", P_BOOL|P_VIM, - (char_u *)&p_wmnu, PV_NONE, - {(char_u *)FALSE, (char_u *)TRUE} SCRIPTID_INIT}, - {"wildmode", "wim", P_STRING|P_VIM|P_COMMA|P_NODUP, - (char_u *)&p_wim, PV_NONE, - {(char_u *)"", (char_u *)"list:longest,full"} SCRIPTID_INIT}, - {"wildoptions", "wop", P_STRING|P_VI_DEF, - (char_u *)&p_wop, PV_NONE, - {(char_u *)"", (char_u *)0L} - SCRIPTID_INIT}, - {"winaltkeys", "wak", P_STRING|P_VI_DEF, - (char_u *)&p_wak, PV_NONE, - {(char_u *)"menu", (char_u *)0L} - SCRIPTID_INIT}, - {"window", "wi", P_NUM|P_VI_DEF, - (char_u *)&p_window, PV_NONE, - {(char_u *)0L, (char_u *)0L} SCRIPTID_INIT}, - {"winheight", "wh", P_NUM|P_VI_DEF, - (char_u *)&p_wh, PV_NONE, - {(char_u *)1L, (char_u *)0L} SCRIPTID_INIT}, - {"winfixheight", "wfh", P_BOOL|P_VI_DEF|P_RSTAT, - VAR_WIN, PV_WFH, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"winfixwidth", "wfw", P_BOOL|P_VI_DEF|P_RSTAT, - VAR_WIN, PV_WFW, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"winminheight", "wmh", P_NUM|P_VI_DEF, - (char_u *)&p_wmh, PV_NONE, - {(char_u *)1L, (char_u *)0L} SCRIPTID_INIT}, - {"winminwidth", "wmw", P_NUM|P_VI_DEF, - (char_u *)&p_wmw, PV_NONE, - {(char_u *)1L, (char_u *)0L} SCRIPTID_INIT}, - {"winwidth", "wiw", P_NUM|P_VI_DEF, - (char_u *)&p_wiw, PV_NONE, - {(char_u *)20L, (char_u *)0L} SCRIPTID_INIT}, - {"wrap", NULL, P_BOOL|P_VI_DEF|P_RWIN, - VAR_WIN, PV_WRAP, - {(char_u *)TRUE, (char_u *)0L} SCRIPTID_INIT}, - {"wrapmargin", "wm", P_NUM|P_VI_DEF, - (char_u *)&p_wm, PV_WM, - {(char_u *)0L, (char_u *)0L} SCRIPTID_INIT}, - {"wrapscan", "ws", P_BOOL|P_VI_DEF, - (char_u *)&p_ws, PV_NONE, - {(char_u *)TRUE, (char_u *)0L} SCRIPTID_INIT}, - {"write", NULL, P_BOOL|P_VI_DEF, - (char_u *)&p_write, PV_NONE, - {(char_u *)TRUE, (char_u *)0L} SCRIPTID_INIT}, - {"writeany", "wa", P_BOOL|P_VI_DEF, - (char_u *)&p_wa, PV_NONE, - {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, - {"writebackup", "wb", P_BOOL|P_VI_DEF|P_VIM, - (char_u *)&p_wb, PV_NONE, - { - (char_u *)TRUE, - (char_u *)0L - } SCRIPTID_INIT}, - {"writedelay", "wd", P_NUM|P_VI_DEF, - (char_u *)&p_wd, PV_NONE, - {(char_u *)0L, (char_u *)0L} SCRIPTID_INIT}, - - /* end marker */ - { - NULL, NULL, 0, NULL, PV_NONE, {NULL, NULL} SCRIPTID_INIT - } -}; #define PARAM_COUNT ARRAY_SIZE(options) @@ -1727,12 +316,6 @@ void set_init_1(void) /* Be nocompatible */ p_cp = FALSE; - /* Use POSIX compatibility when $VIM_POSIX is set. */ - if (os_env_exists("VIM_POSIX")) { - set_string_default("cpo", (char_u *)CPO_ALL); - set_string_default("shm", (char_u *)"A"); - } - /* * Find default value for 'shell' option. * Don't use it if it is empty. @@ -4229,7 +2812,7 @@ did_set_string_option ( if (varp == &p_shm) p = (char_u *)SHM_ALL; else if (varp == &(p_cpo)) - p = (char_u *)CPO_ALL; + p = (char_u *)CPO_VI; else if (varp == &(curbuf->b_p_fo)) p = (char_u *)FO_ALL; else if (varp == &curwin->w_p_cocu) diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 80f2373a85..e35f8bc55b 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -128,21 +128,11 @@ #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 */ -/* POSIX flags */ -#define CPO_HASH '#' /* "D", "o" and "O" do not use a count */ -#define CPO_PARA '{' /* "{" is also a paragraph boundary */ -#define CPO_TSIZE '|' /* $LINES and $COLUMNS overrule term size */ -#define CPO_PRESERVE '&' /* keep swap file after :preserve */ -#define CPO_SUBPERCENT '/' /* % in :s string uses previous one */ -#define CPO_BACKSL '\\' /* \ is not special in [] */ -#define CPO_CHDIR '.' /* don't chdir if buffer is modified */ #define CPO_SCOLON ';' /* using "," and ";" will skip over char if * cursor would not move */ -/* default values for Vim, Vi and POSIX */ +/* default values for Vim and Vi */ #define CPO_VIM "aABceFs" #define CPO_VI "aAbBcCdDeEfFiIJkKlLmMnoOpPqrRsStuvWxXyZ$!%+<>;" -#define CPO_ALL \ - "aAbBcCdDeEfFiIJkKlLmMnoOpPqrRsStuvWxXyZ$!%+<>#{|&/\\.;" /* characters for p_ww option: */ #define WW_ALL "bshl<>[],~" @@ -200,11 +190,10 @@ #define GO_ASELPLUS 'P' /* autoselectPlus */ #define GO_RIGHT 'r' /* use right scrollbar */ #define GO_VRIGHT 'R' /* right scrollbar with vert split */ -#define GO_TEAROFF 't' /* add tear-off menu items */ #define GO_TOOLBAR 'T' /* add toolbar */ #define GO_FOOTER 'F' /* add footer */ #define GO_VERTICAL 'v' /* arrange dialog buttons vertically */ -#define GO_ALL "aAbcefFghilmMprtTv" /* all possible flags for 'go' */ +#define GO_ALL "aAbcefFghilmMprTv" /* all possible flags for 'go' */ /* flags for 'comments' option */ #define COM_NEST 'n' /* comments strings nest */ @@ -318,6 +307,7 @@ static char *(p_cb_values[]) = {"unnamed", "unnamedplus", NULL}; #endif # define CB_UNNAMED 0x001 # define CB_UNNAMEDPLUS 0x002 +# define CB_UNNAMEDMASK (CB_UNNAMED | CB_UNNAMEDPLUS) EXTERN long p_cwh; /* 'cmdwinheight' */ EXTERN long p_ch; /* 'cmdheight' */ EXTERN int p_confirm; /* 'confirm' */ diff --git a/src/nvim/options.lua b/src/nvim/options.lua new file mode 100644 index 0000000000..b269bfdc98 --- /dev/null +++ b/src/nvim/options.lua @@ -0,0 +1,2777 @@ +-- { +-- { +-- full_name='aleph', abbreviation='al', +-- varname='p_aleph', pv_name=nil, +-- type='number', list=nil, scope={'global'}, +-- deny_duplicates=nil, +-- 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, +-- expand=nil, nodefault=nil, no_mkrc=nil, vi_def=true, vim=true, +-- alloced=nil, +-- save_pv_indir=nil, +-- redraw={'curswant'}, +-- } +-- } +-- types: bool, number, string +-- lists: (nil), comma, flags, flagscomma +-- scopes: global, buffer, window +-- redraw options: statuslines, current_window, current_buffer, all_windows, +-- everything, curswant +-- default: {vi=…[, vim=…]} +-- defaults: {condition=#if condition, if_true=default, if_false=default} +-- #if condition: +-- string: #ifdef string +-- !string: #ifndef string +-- {string, string}: #if defined(string) && defined(string) +-- {!string, !string}: #if !defined(string) && !defined(string) +local cstr = function(s) + return '"' .. s:gsub('["\\]', '\\%0'):gsub('\t', '\\t') .. '"' +end +local macros=function(s) + return function() + return s + end +end +local N_=function(s) + return function() + return 'N_(' .. cstr(s) .. ')' + end +end +return { + cstr=cstr, + options={ + { + full_name='aleph', abbreviation='al', + type='number', scope={'global'}, + vi_def=true, + redraw={'curswant'}, + varname='p_aleph', + defaults={if_true={vi=224}} + }, + { + full_name='antialias', abbreviation='anti', + type='bool', scope={'global'}, + vi_def=true, + vim=true, + redraw={'everything'}, + enable_if=false, + defaults={if_true={vi=false, vim=false}} + }, + { + full_name='arabic', abbreviation='arab', + type='bool', scope={'window'}, + vi_def=true, + vim=true, + redraw={'curswant'}, + defaults={if_true={vi=false}} + }, + { + full_name='arabicshape', abbreviation='arshape', + type='bool', scope={'global'}, + vi_def=true, + vim=true, + redraw={'everything'}, + varname='p_arshape', + defaults={if_true={vi=true}} + }, + { + full_name='allowrevins', abbreviation='ari', + type='bool', scope={'global'}, + vi_def=true, + vim=true, + varname='p_ari', + defaults={if_true={vi=false}} + }, + { + full_name='altkeymap', abbreviation='akm', + type='bool', scope={'global'}, + vi_def=true, + varname='p_altkeymap', + defaults={if_true={vi=false}} + }, + { + full_name='ambiwidth', abbreviation='ambw', + type='string', scope={'global'}, + vi_def=true, + redraw={'everything'}, + varname='p_ambw', + defaults={if_true={vi="single"}} + }, + { + full_name='autochdir', abbreviation='acd', + type='bool', scope={'global'}, + vi_def=true, + varname='p_acd', + defaults={if_true={vi=false}} + }, + { + full_name='autoindent', abbreviation='ai', + type='bool', scope={'buffer'}, + varname='p_ai', + defaults={if_true={vi=false, vim=true}} + }, + { + full_name='autoread', abbreviation='ar', + type='bool', scope={'global', 'buffer'}, + varname='p_ar', + defaults={if_true={vi=false, vim=true}} + }, + { + full_name='autowrite', abbreviation='aw', + type='bool', scope={'global'}, + vi_def=true, + varname='p_aw', + defaults={if_true={vi=false}} + }, + { + full_name='autowriteall', abbreviation='awa', + type='bool', scope={'global'}, + vi_def=true, + varname='p_awa', + defaults={if_true={vi=false}} + }, + { + full_name='background', abbreviation='bg', + type='string', scope={'global'}, + vi_def=true, + redraw={'everything'}, + varname='p_bg', + defaults={if_true={vi="light"}} + }, + { + full_name='backspace', abbreviation='bs', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vim=true, + varname='p_bs', + defaults={if_true={vi="", vim="indent,eol,start"}} + }, + { + full_name='backup', abbreviation='bk', + type='bool', scope={'global'}, + vi_def=true, + vim=true, + varname='p_bk', + defaults={if_true={vi=false}} + }, + { + full_name='backupcopy', abbreviation='bkc', + type='string', list='comma', scope={'global', 'buffer'}, + deny_duplicates=true, + vim=true, + varname='p_bkc', + defaults={ + condition='UNIX', + if_true={vi="yes", vim="auto"}, + if_false={vi="auto", vim="auto"} + }, + }, + { + full_name='backupdir', abbreviation='bdir', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + secure=true, + vi_def=true, + expand=true, + varname='p_bdir', + defaults={if_true={vi=macros('DFLT_BDIR')}} + }, + { + full_name='backupext', abbreviation='bex', + type='string', scope={'global'}, + normal_fname_chars=true, + vi_def=true, + varname='p_bex', + defaults={if_true={vi="~"}} + }, + { + full_name='backupskip', abbreviation='bsk', + type='string', list='comma', scope={'global'}, + vi_def=true, + varname='p_bsk', + defaults={if_true={vi=""}} + }, + { + full_name='binary', abbreviation='bin', + type='bool', scope={'buffer'}, + vi_def=true, + redraw={'statuslines'}, + varname='p_bin', + defaults={if_true={vi=false}} + }, + { + full_name='bomb', + type='bool', scope={'buffer'}, + no_mkrc=true, + vi_def=true, + redraw={'statuslines'}, + varname='p_bomb', + defaults={if_true={vi=false}} + }, + { + full_name='breakat', abbreviation='brk', + type='string', list='flags', scope={'global'}, + vi_def=true, + redraw={'all_windows'}, + varname='p_breakat', + defaults={if_true={vi=" \t!@*-+;:,./?"}} + }, + { + full_name='breakindent', abbreviation='bri', + type='bool', scope={'window'}, + vi_def=true, + vim=true, + redraw={'current_window'}, + defaults={if_true={vi=false}} + }, + { + full_name='breakindentopt', abbreviation='briopt', + type='string', list='comma', scope={'window'}, + deny_duplicates=true, + vi_def=true, + alloced=true, + redraw={'current_buffer'}, + defaults={if_true={vi=""}}, + }, + { + full_name='browsedir', abbreviation='bsdir', + type='string', scope={'global'}, + vi_def=true, + enable_if=false, + }, + { + full_name='bufhidden', abbreviation='bh', + type='string', scope={'buffer'}, + noglob=true, + vi_def=true, + alloced=true, + varname='p_bh', + defaults={if_true={vi=""}} + }, + { + full_name='buflisted', abbreviation='bl', + type='bool', scope={'buffer'}, + noglob=true, + vi_def=true, + varname='p_bl', + defaults={if_true={vi=1}} + }, + { + full_name='buftype', abbreviation='bt', + type='string', scope={'buffer'}, + noglob=true, + vi_def=true, + alloced=true, + varname='p_bt', + defaults={if_true={vi=""}} + }, + { + full_name='casemap', abbreviation='cmp', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vi_def=true, + varname='p_cmp', + defaults={if_true={vi="internal,keepascii"}} + }, + { + full_name='cdpath', abbreviation='cd', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vi_def=true, + expand=true, + varname='p_cdpath', + defaults={if_true={vi=",,"}} + }, + { + full_name='cedit', + type='string', scope={'global'}, + varname='p_cedit', + defaults={if_true={vi="", vim=macros('CTRL_F_STR')}} + }, + { + full_name='charconvert', abbreviation='ccv', + type='string', scope={'global'}, + secure=true, + vi_def=true, + varname='p_ccv', + defaults={if_true={vi=""}} + }, + { + full_name='cindent', abbreviation='cin', + type='bool', scope={'buffer'}, + vi_def=true, + vim=true, + varname='p_cin', + defaults={if_true={vi=false}} + }, + { + full_name='cinkeys', abbreviation='cink', + type='string', list='comma', scope={'buffer'}, + deny_duplicates=true, + vi_def=true, + alloced=true, + varname='p_cink', + defaults={if_true={vi="0{,0},0),:,0#,!^F,o,O,e"}} + }, + { + full_name='cinoptions', abbreviation='cino', + type='string', list='comma', scope={'buffer'}, + deny_duplicates=true, + vi_def=true, + alloced=true, + varname='p_cino', + defaults={if_true={vi=""}} + }, + { + full_name='cinwords', abbreviation='cinw', + type='string', list='comma', scope={'buffer'}, + deny_duplicates=true, + vi_def=true, + alloced=true, + varname='p_cinw', + defaults={if_true={vi="if,else,while,do,for,switch"}} + }, + { + full_name='clipboard', abbreviation='cb', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vi_def=true, + varname='p_cb', + defaults={if_true={vi=""}} + }, + { + full_name='cmdheight', abbreviation='ch', + type='number', scope={'global'}, + vi_def=true, + redraw={'all_windows'}, + varname='p_ch', + defaults={if_true={vi=1}} + }, + { + full_name='cmdwinheight', abbreviation='cwh', + type='number', scope={'global'}, + vi_def=true, + varname='p_cwh', + defaults={if_true={vi=7}} + }, + { + full_name='colorcolumn', abbreviation='cc', + type='string', list='comma', scope={'window'}, + deny_duplicates=true, + vi_def=true, + redraw={'current_window'}, + defaults={if_true={vi=""}} + }, + { + full_name='columns', abbreviation='co', + type='number', scope={'global'}, + no_mkrc=true, + nodefault=true, + vi_def=true, + redraw={'everything'}, + varname='Columns', + defaults={if_true={vi=macros('DFLT_COLS')}} + }, + { + full_name='comments', abbreviation='com', + type='string', list='comma', scope={'buffer'}, + deny_duplicates=true, + vi_def=true, + alloced=true, + redraw={'curswant'}, + varname='p_com', + defaults={if_true={vi="s1:/*,mb:*,ex:*/,://,b:#,:%,:XCOMM,n:>,fb:-"}} + }, + { + full_name='commentstring', abbreviation='cms', + type='string', scope={'buffer'}, + vi_def=true, + alloced=true, + redraw={'curswant'}, + varname='p_cms', + defaults={if_true={vi="/*%s*/"}} + }, + { + full_name='compatible', abbreviation='cp', + type='bool', scope={'global'}, + redraw={'all_windows'}, + varname='p_force_off', + -- pri_mkrc isn't needed here, optval_default() + -- always returns TRUE for 'compatible' + defaults={if_true={vi=true, vim=false}} + }, + { + full_name='complete', abbreviation='cpt', + type='string', list='comma', scope={'buffer'}, + deny_duplicates=true, + alloced=true, + varname='p_cpt', + defaults={if_true={vi=".,w,b,u,t,i", vim=".,w,b,u,t"}} + }, + { + full_name='concealcursor', abbreviation='cocu', + type='string', scope={'window'}, + vi_def=true, + alloced=true, + redraw={'current_window'}, + defaults={if_true={vi=""}} + }, + { + full_name='conceallevel', abbreviation='cole', + type='number', scope={'window'}, + vi_def=true, + redraw={'current_window'}, + defaults={if_true={vi=0}} + }, + { + full_name='completefunc', abbreviation='cfu', + type='string', scope={'buffer'}, + secure=true, + vi_def=true, + alloced=true, + varname='p_cfu', + defaults={if_true={vi=""}} + }, + { + full_name='completeopt', abbreviation='cot', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vi_def=true, + varname='p_cot', + defaults={if_true={vi="menu,preview"}} + }, + { + full_name='confirm', abbreviation='cf', + type='bool', scope={'global'}, + vi_def=true, + varname='p_confirm', + defaults={if_true={vi=false}} + }, + { + full_name='copyindent', abbreviation='ci', + type='bool', scope={'buffer'}, + vi_def=true, + vim=true, + varname='p_ci', + defaults={if_true={vi=false}} + }, + { + full_name='cpoptions', abbreviation='cpo', + type='string', list='flags', scope={'global'}, + vim=true, + redraw={'all_windows'}, + varname='p_cpo', + defaults={if_true={vi=macros('CPO_VI'), vim=macros('CPO_VIM')}} + }, + { + full_name='cscopepathcomp', abbreviation='cspc', + type='number', scope={'global'}, + vi_def=true, + vim=true, + varname='p_cspc', + defaults={if_true={vi=0}} + }, + { + full_name='cscopeprg', abbreviation='csprg', + type='string', scope={'global'}, + secure=true, + vi_def=true, + expand=true, + varname='p_csprg', + defaults={if_true={vi="cscope"}} + }, + { + full_name='cscopequickfix', abbreviation='csqf', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vi_def=true, + varname='p_csqf', + defaults={if_true={vi=""}} + }, + { + full_name='cscoperelative', abbreviation='csre', + type='bool', scope={'global'}, + vi_def=true, + vim=true, + varname='p_csre', + defaults={if_true={vi=0}} + }, + { + full_name='cscopetag', abbreviation='cst', + type='bool', scope={'global'}, + vi_def=true, + vim=true, + varname='p_cst', + defaults={if_true={vi=0}} + }, + { + full_name='cscopetagorder', abbreviation='csto', + type='number', scope={'global'}, + vi_def=true, + vim=true, + varname='p_csto', + defaults={if_true={vi=0}} + }, + { + full_name='cscopeverbose', abbreviation='csverb', + type='bool', scope={'global'}, + vi_def=true, + vim=true, + varname='p_csverbose', + defaults={if_true={vi=0}} + }, + { + full_name='cursorbind', abbreviation='crb', + type='bool', scope={'window'}, + vi_def=true, + pv_name='p_crbind', + defaults={if_true={vi=false}} + }, + { + full_name='cursorcolumn', abbreviation='cuc', + type='bool', scope={'window'}, + vi_def=true, + redraw={'current_window'}, + defaults={if_true={vi=false}} + }, + { + full_name='cursorline', abbreviation='cul', + type='bool', scope={'window'}, + vi_def=true, + redraw={'current_window'}, + defaults={if_true={vi=false}} + }, + { + full_name='debug', + type='string', scope={'global'}, + vi_def=true, + varname='p_debug', + defaults={if_true={vi=""}} + }, + { + full_name='define', abbreviation='def', + type='string', scope={'global', 'buffer'}, + vi_def=true, + alloced=true, + redraw={'curswant'}, + varname='p_def', + defaults={if_true={vi="^\\s*#\\s*define"}} + }, + { + full_name='delcombine', abbreviation='deco', + type='bool', scope={'global'}, + vi_def=true, + vim=true, + varname='p_deco', + defaults={if_true={vi=false}} + }, + { + full_name='dictionary', abbreviation='dict', + type='string', list='comma', scope={'global', 'buffer'}, + deny_duplicates=true, + vi_def=true, + expand=true, + varname='p_dict', + defaults={if_true={vi=""}} + }, + { + full_name='diff', + type='bool', scope={'window'}, + noglob=true, + vi_def=true, + redraw={'current_window'}, + defaults={if_true={vi=false}} + }, + { + full_name='diffexpr', abbreviation='dex', + type='string', scope={'global'}, + secure=true, + vi_def=true, + redraw={'curswant'}, + varname='p_dex', + defaults={if_true={vi=""}} + }, + { + full_name='diffopt', abbreviation='dip', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vi_def=true, + alloced=true, + redraw={'current_window'}, + varname='p_dip', + defaults={if_true={vi="filler"}} + }, + { + full_name='digraph', abbreviation='dg', + type='bool', scope={'global'}, + vi_def=true, + vim=true, + varname='p_dg', + defaults={if_true={vi=false}} + }, + { + full_name='directory', abbreviation='dir', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + secure=true, + vi_def=true, + expand=true, + varname='p_dir', + defaults={if_true={vi=macros('DFLT_DIR')}} + }, + { + full_name='display', abbreviation='dy', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vim=true, + redraw={'all_windows'}, + varname='p_dy', + defaults={if_true={vi="", vim="lastline"}} + }, + { + full_name='eadirection', abbreviation='ead', + type='string', scope={'global'}, + vi_def=true, + varname='p_ead', + defaults={if_true={vi="both"}} + }, + { + full_name='edcompatible', abbreviation='ed', + type='bool', scope={'global'}, + vi_def=true, + varname='p_force_off', + defaults={if_true={vi=false}} + }, + { + full_name='encoding', abbreviation='enc', + type='string', scope={'global'}, + deny_in_modelines=true, + vi_def=true, + redraw={'everything'}, + varname='p_enc', + defaults={if_true={vi=macros('ENC_DFLT')}} + }, + { + full_name='endofline', abbreviation='eol', + type='bool', scope={'buffer'}, + no_mkrc=true, + vi_def=true, + redraw={'statuslines'}, + varname='p_eol', + defaults={if_true={vi=true}} + }, + { + full_name='equalalways', abbreviation='ea', + type='bool', scope={'global'}, + vi_def=true, + redraw={'all_windows'}, + varname='p_ea', + defaults={if_true={vi=true}} + }, + { + full_name='equalprg', abbreviation='ep', + type='string', scope={'global', 'buffer'}, + secure=true, + vi_def=true, + expand=true, + varname='p_ep', + defaults={if_true={vi=""}} + }, + { + full_name='errorbells', abbreviation='eb', + type='bool', scope={'global'}, + vi_def=true, + varname='p_eb', + defaults={if_true={vi=false}} + }, + { + full_name='errorfile', abbreviation='ef', + type='string', scope={'global'}, + secure=true, + vi_def=true, + expand=true, + varname='p_ef', + defaults={if_true={vi=macros('DFLT_ERRORFILE')}} + }, + { + full_name='errorformat', abbreviation='efm', + type='string', list='comma', scope={'global', 'buffer'}, + deny_duplicates=true, + vi_def=true, + varname='p_efm', + defaults={if_true={vi=macros('DFLT_EFM')}} + }, + { + full_name='esckeys', abbreviation='ek', + type='bool', scope={'global'}, + vim=true, + varname='p_ek', + defaults={if_true={vi=false, vim=true}} + }, + { + full_name='eventignore', abbreviation='ei', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vi_def=true, + varname='p_ei', + defaults={if_true={vi=""}} + }, + { + full_name='expandtab', abbreviation='et', + type='bool', scope={'buffer'}, + vi_def=true, + vim=true, + varname='p_et', + defaults={if_true={vi=false}} + }, + { + full_name='exrc', abbreviation='ex', + type='bool', scope={'global'}, + secure=true, + vi_def=true, + varname='p_exrc', + defaults={if_true={vi=false}} + }, + { + full_name='fileencoding', abbreviation='fenc', + type='string', scope={'buffer'}, + no_mkrc=true, + vi_def=true, + alloced=true, + redraw={'statuslines', 'current_buffer'}, + varname='p_fenc', + defaults={if_true={vi=""}} + }, + { + full_name='fileencodings', abbreviation='fencs', + type='string', list='comma', scope={'global'}, + vi_def=true, + varname='p_fencs', + defaults={if_true={vi="ucs-bom"}} + }, + { + full_name='fileformat', abbreviation='ff', + type='string', scope={'buffer'}, + no_mkrc=true, + vi_def=true, + alloced=true, + redraw={'curswant', 'statuslines'}, + varname='p_ff', + defaults={if_true={vi=macros('DFLT_FF')}} + }, + { + full_name='fileformats', abbreviation='ffs', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vim=true, + varname='p_ffs', + defaults={if_true={vi=macros('DFLT_FFS_VI'), vim=macros('DFLT_FFS_VIM')}} + }, + { + full_name='fileignorecase', abbreviation='fic', + type='bool', scope={'global'}, + vi_def=true, + varname='p_fic', + defaults={ + condition='CASE_INSENSITIVE_FILENAME', + if_true={vi=true}, + if_false={vi=false}, + } + }, + { + full_name='filetype', abbreviation='ft', + type='string', scope={'buffer'}, + noglob=true, + normal_fname_chars=true, + vi_def=true, + alloced=true, + varname='p_ft', + defaults={if_true={vi=""}} + }, + { + full_name='fillchars', abbreviation='fcs', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vi_def=true, + redraw={'all_windows'}, + varname='p_fcs', + defaults={if_true={vi="vert:|,fold:-"}} + }, + { + full_name='fkmap', abbreviation='fk', + type='bool', scope={'global'}, + vi_def=true, + varname='p_fkmap', + defaults={if_true={vi=false}} + }, + { + full_name='foldclose', abbreviation='fcl', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vi_def=true, + redraw={'current_window'}, + varname='p_fcl', + defaults={if_true={vi=""}} + }, + { + full_name='foldcolumn', abbreviation='fdc', + type='number', scope={'window'}, + vi_def=true, + redraw={'current_window'}, + defaults={if_true={vi=false}} + }, + { + full_name='foldenable', abbreviation='fen', + type='bool', scope={'window'}, + vi_def=true, + redraw={'current_window'}, + defaults={if_true={vi=true}} + }, + { + full_name='foldexpr', abbreviation='fde', + type='string', scope={'window'}, + vi_def=true, + vim=true, + alloced=true, + redraw={'current_window'}, + defaults={if_true={vi="0"}} + }, + { + full_name='foldignore', abbreviation='fdi', + type='string', scope={'window'}, + vi_def=true, + vim=true, + alloced=true, + redraw={'current_window'}, + defaults={if_true={vi="#"}} + }, + { + full_name='foldlevel', abbreviation='fdl', + type='number', scope={'window'}, + vi_def=true, + redraw={'current_window'}, + defaults={if_true={vi=0}} + }, + { + full_name='foldlevelstart', abbreviation='fdls', + type='number', scope={'global'}, + vi_def=true, + redraw={'curswant'}, + varname='p_fdls', + defaults={if_true={vi=-1}} + }, + { + full_name='foldmarker', abbreviation='fmr', + type='string', list='comma', scope={'window'}, + deny_duplicates=true, + vi_def=true, + vim=true, + alloced=true, + redraw={'current_window'}, + defaults={if_true={vi="{{{,}}}"}} + }, + { + full_name='foldmethod', abbreviation='fdm', + type='string', scope={'window'}, + vi_def=true, + vim=true, + alloced=true, + redraw={'current_window'}, + defaults={if_true={vi="manual"}} + }, + { + full_name='foldminlines', abbreviation='fml', + type='number', scope={'window'}, + vi_def=true, + redraw={'current_window'}, + defaults={if_true={vi=1}} + }, + { + full_name='foldnestmax', abbreviation='fdn', + type='number', scope={'window'}, + vi_def=true, + redraw={'current_window'}, + defaults={if_true={vi=20}} + }, + { + full_name='foldopen', abbreviation='fdo', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vi_def=true, + redraw={'curswant'}, + varname='p_fdo', + defaults={if_true={vi="block,hor,mark,percent,quickfix,search,tag,undo"}} + }, + { + full_name='foldtext', abbreviation='fdt', + type='string', scope={'window'}, + vi_def=true, + vim=true, + alloced=true, + redraw={'current_window'}, + defaults={if_true={vi="foldtext()"}} + }, + { + full_name='formatexpr', abbreviation='fex', + type='string', scope={'buffer'}, + vi_def=true, + vim=true, + alloced=true, + varname='p_fex', + defaults={if_true={vi=""}} + }, + { + full_name='formatoptions', abbreviation='fo', + type='string', list='flags', scope={'buffer'}, + vim=true, + alloced=true, + varname='p_fo', + defaults={if_true={vi=macros('DFLT_FO_VI'), vim=macros('DFLT_FO_VIM')}} + }, + { + full_name='formatlistpat', abbreviation='flp', + type='string', scope={'buffer'}, + vi_def=true, + alloced=true, + varname='p_flp', + defaults={if_true={vi="^\\s*\\d\\+[\\]:.)}\\t ]\\s*"}} + }, + { + full_name='formatprg', abbreviation='fp', + type='string', scope={'global'}, + secure=true, + vi_def=true, + expand=true, + varname='p_fp', + defaults={if_true={vi=""}} + }, + { + full_name='fsync', abbreviation='fs', + type='bool', scope={'global'}, + secure=true, + vi_def=true, + enable_if='HAVE_FSYNC', + varname='p_fs', + defaults={if_true={vi=true}} + }, + { + full_name='gdefault', abbreviation='gd', + type='bool', scope={'global'}, + vi_def=true, + vim=true, + varname='p_gd', + defaults={if_true={vi=false}} + }, + { + full_name='grepformat', abbreviation='gfm', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vi_def=true, + varname='p_gefm', + defaults={if_true={vi=macros('DFLT_GREPFORMAT')}} + }, + { + full_name='grepprg', abbreviation='gp', + type='string', scope={'global', 'buffer'}, + secure=true, + vi_def=true, + expand=true, + varname='p_gp', + defaults={ + condition='UNIX', + -- 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 "}, + } + }, + { + full_name='guicursor', abbreviation='gcr', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vi_def=true, + varname='p_guicursor', + defaults={if_true={vi="n-v-c:block,o:hor50,i-ci:hor15,r-cr:hor30,sm:block"}} + }, + { + full_name='guifont', abbreviation='gfn', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vi_def=true, + redraw={'everything'}, + enable_if=false, + }, + { + full_name='guifontset', abbreviation='gfs', + type='string', list='comma', scope={'global'}, + vi_def=true, + redraw={'everything'}, + enable_if=false, + }, + { + full_name='guifontwide', abbreviation='gfw', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vi_def=true, + redraw={'everything'}, + enable_if=false, + }, + { + full_name='guiheadroom', abbreviation='ghr', + type='number', scope={'global'}, + vi_def=true, + enable_if=false, + defaults={if_true={vi=50}} + }, + { + full_name='guioptions', abbreviation='go', + type='string', list='flags', scope={'global'}, + vi_def=true, + redraw={'all_windows'}, + enable_if=false, + }, + { + full_name='guitablabel', abbreviation='gtl', + type='string', scope={'global'}, + vi_def=true, + redraw={'current_window'}, + enable_if=false, + }, + { + full_name='guitabtooltip', abbreviation='gtt', + type='string', scope={'global'}, + vi_def=true, + redraw={'current_window'}, + enable_if=false, + }, + { + full_name='helpfile', abbreviation='hf', + type='string', scope={'global'}, + secure=true, + vi_def=true, + expand=true, + varname='p_hf', + defaults={if_true={vi=macros('DFLT_HELPFILE')}} + }, + { + full_name='helpheight', abbreviation='hh', + type='number', scope={'global'}, + vi_def=true, + varname='p_hh', + defaults={if_true={vi=20}} + }, + { + full_name='helplang', abbreviation='hlg', + type='string', list='comma', scope={'global'}, + vi_def=true, + varname='p_hlg', + defaults={if_true={vi=""}} + }, + { + full_name='hidden', abbreviation='hid', + type='bool', scope={'global'}, + vi_def=true, + varname='p_hid', + defaults={if_true={vi=false}} + }, + { + full_name='highlight', abbreviation='hl', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vi_def=true, + redraw={'everything'}, + varname='p_hl', + defaults={if_true={vi=macros('HIGHLIGHT_INIT')}} + }, + { + full_name='history', abbreviation='hi', + type='number', scope={'global'}, + vim=true, + varname='p_hi', + defaults={if_true={vi=0, vim=10000}} + }, + { + full_name='hkmap', abbreviation='hk', + type='bool', scope={'global'}, + vi_def=true, + vim=true, + varname='p_hkmap', + defaults={if_true={vi=false}} + }, + { + full_name='hkmapp', abbreviation='hkp', + type='bool', scope={'global'}, + vi_def=true, + vim=true, + varname='p_hkmapp', + defaults={if_true={vi=false}} + }, + { + full_name='hlsearch', abbreviation='hls', + type='bool', scope={'global'}, + vim=true, + redraw={'all_windows'}, + varname='p_hls', + defaults={if_true={vi=false, vim=true}} + }, + { + full_name='icon', + type='bool', scope={'global'}, + vi_def=true, + varname='p_icon', + defaults={if_true={vi=false}} + }, + { + full_name='iconstring', + type='string', scope={'global'}, + vi_def=true, + varname='p_iconstring', + defaults={if_true={vi=""}} + }, + { + full_name='ignorecase', abbreviation='ic', + type='bool', scope={'global'}, + vi_def=true, + varname='p_ic', + defaults={if_true={vi=false}} + }, + { + full_name='imactivatefunc', abbreviation='imaf', + type='string', scope={'global'}, + secure=true, + vi_def=true, + enable_if=false, + }, + { + full_name='imactivatekey', abbreviation='imak', + type='string', scope={'global'}, + vi_def=true, + enable_if=false, + defaults={if_true={vi=""}} + }, + { + full_name='imcmdline', abbreviation='imc', + type='bool', scope={'global'}, + vi_def=true, + enable_if=false, + defaults={if_true={vi=false}} + }, + { + full_name='imdisable', abbreviation='imd', + type='bool', scope={'global'}, + vi_def=true, + enable_if=false, + defaults={if_true={vi=false}} + }, + { + full_name='iminsert', abbreviation='imi', + type='number', scope={'buffer'}, + vi_def=true, + varname='p_iminsert', pv_name='p_imi', + defaults={ + condition='B_IMODE_IM', + if_true={vi=macros('B_IMODE_IM')}, + if_false={vi=macros('B_IMODE_NONE')}, + } + }, + { + full_name='imsearch', abbreviation='ims', + type='number', scope={'buffer'}, + vi_def=true, + varname='p_imsearch', pv_name='p_ims', + defaults={ + condition='B_IMODE_IM', + if_true={vi=macros('B_IMODE_IM')}, + if_false={vi=macros('B_IMODE_NONE')}, + } + }, + { + full_name='imstatusfunc', abbreviation='imsf', + type='string', scope={'global'}, + secure=true, + vi_def=true, + enable_if=false, + }, + { + full_name='include', abbreviation='inc', + type='string', scope={'global', 'buffer'}, + vi_def=true, + alloced=true, + varname='p_inc', + defaults={if_true={vi="^\\s*#\\s*include"}} + }, + { + full_name='includeexpr', abbreviation='inex', + type='string', scope={'buffer'}, + vi_def=true, + alloced=true, + varname='p_inex', + defaults={if_true={vi=""}} + }, + { + full_name='incsearch', abbreviation='is', + type='bool', scope={'global'}, + vim=true, + varname='p_is', + defaults={if_true={vi=false, vim=true}} + }, + { + full_name='indentexpr', abbreviation='inde', + type='string', scope={'buffer'}, + vi_def=true, + vim=true, + alloced=true, + varname='p_inde', + defaults={if_true={vi=""}} + }, + { + full_name='indentkeys', abbreviation='indk', + type='string', list='comma', scope={'buffer'}, + deny_duplicates=true, + vi_def=true, + alloced=true, + varname='p_indk', + defaults={if_true={vi="0{,0},:,0#,!^F,o,O,e"}} + }, + { + full_name='infercase', abbreviation='inf', + type='bool', scope={'buffer'}, + vi_def=true, + varname='p_inf', + defaults={if_true={vi=false}} + }, + { + full_name='insertmode', abbreviation='im', + type='bool', scope={'global'}, + vi_def=true, + vim=true, + varname='p_im', + defaults={if_true={vi=false}} + }, + { + full_name='isfname', abbreviation='isf', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vi_def=true, + varname='p_isf', + defaults={ + condition='BACKSLASH_IN_FILENAME', + -- Excluded are: & and ^ are special in cmd.exe + -- ( and ) are used in text separating fnames */ + if_true={vi="@,48-57,/,\\,.,-,_,+,,,#,$,%,{,},[,],:,@-@,!,~,="}, + if_false={vi="@,48-57,/,.,-,_,+,,,#,$,%,~,="} + } + }, + { + full_name='isident', abbreviation='isi', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vi_def=true, + varname='p_isi', + defaults={if_true={vi="@,48-57,_,192-255"}} + }, + { + full_name='iskeyword', abbreviation='isk', + type='string', list='comma', scope={'buffer'}, + deny_duplicates=true, + vim=true, + alloced=true, + varname='p_isk', + defaults={if_true={vi="@,48-57,_", vim=macros('ISK_LATIN1')}} + }, + { + full_name='isprint', abbreviation='isp', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vi_def=true, + redraw={'all_windows'}, + varname='p_isp', + defaults={ + condition='MSWIN', + if_true={vi="@,~-255"}, + if_false={vi=macros("ISP_LATIN1")} + } + }, + { + full_name='joinspaces', abbreviation='js', + type='bool', scope={'global'}, + vi_def=true, + vim=true, + varname='p_js', + defaults={if_true={vi=true}} + }, + { + full_name='keymap', abbreviation='kmp', + type='string', scope={'buffer'}, + normal_fname_chars=true, + pri_mkrc=true, + vi_def=true, + alloced=true, + redraw={'statuslines', 'current_buffer'}, + varname='p_keymap', pv_name='p_kmap', + defaults={if_true={vi=""}} + }, + { + full_name='keymodel', abbreviation='km', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vi_def=true, + varname='p_km', + defaults={if_true={vi=""}} + }, + { + full_name='keywordprg', abbreviation='kp', + type='string', scope={'global', 'buffer'}, + secure=true, + vi_def=true, + expand=true, + varname='p_kp', + defaults={ + condition='USEMAN_S', + if_true={vi="man -s"}, + if_false={vi="man"}, + } + }, + { + full_name='langmap', abbreviation='lmap', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + secure=true, + vi_def=true, + varname='p_langmap', + defaults={if_true={vi=""}} + }, + { + full_name='langmenu', abbreviation='lm', + type='string', scope={'global'}, + normal_fname_chars=true, + vi_def=true, + varname='p_lm', + defaults={if_true={vi=""}} + }, + { + full_name='langnoremap', abbreviation='lnr', + type='bool', scope={'global'}, + varname='p_lnr', + defaults={if_true={vi=false, vim=true}} + }, + { + full_name='laststatus', abbreviation='ls', + type='number', scope={'global'}, + vi_def=true, + redraw={'all_windows'}, + varname='p_ls', + defaults={if_true={vi=1}} + }, + { + full_name='lazyredraw', abbreviation='lz', + type='bool', scope={'global'}, + vi_def=true, + varname='p_lz', + defaults={if_true={vi=false}} + }, + { + full_name='linebreak', abbreviation='lbr', + type='bool', scope={'window'}, + vi_def=true, + redraw={'current_window'}, + defaults={if_true={vi=false}} + }, + { + full_name='lines', + type='number', scope={'global'}, + no_mkrc=true, + nodefault=true, + vi_def=true, + redraw={'everything'}, + varname='Rows', + defaults={if_true={vi=macros('DFLT_ROWS')}} + }, + { + full_name='linespace', abbreviation='lsp', + type='number', scope={'global'}, + vi_def=true, + redraw={'everything'}, + enable_if=false, + }, + { + full_name='lisp', + type='bool', scope={'buffer'}, + vi_def=true, + varname='p_lisp', + defaults={if_true={vi=false}} + }, + { + full_name='lispwords', abbreviation='lw', + type='string', list='comma', scope={'global', 'buffer'}, + deny_duplicates=true, + vi_def=true, + varname='p_lispwords', pv_name='p_lw', + defaults={if_true={vi=macros('LISPWORD_VALUE')}} + }, + { + full_name='list', + type='bool', scope={'window'}, + vi_def=true, + redraw={'current_window'}, + defaults={if_true={vi=false}} + }, + { + full_name='listchars', abbreviation='lcs', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vi_def=true, + redraw={'all_windows'}, + varname='p_lcs', + defaults={if_true={vi="eol:$"}} + }, + { + full_name='loadplugins', abbreviation='lpl', + type='bool', scope={'global'}, + vi_def=true, + varname='p_lpl', + defaults={if_true={vi=true}} + }, + { + full_name='magic', + type='bool', scope={'global'}, + vi_def=true, + varname='p_magic', + defaults={if_true={vi=true}} + }, + { + full_name='makeef', abbreviation='mef', + type='string', scope={'global'}, + secure=true, + vi_def=true, + expand=true, + varname='p_mef', + defaults={if_true={vi=""}} + }, + { + full_name='makeprg', abbreviation='mp', + type='string', scope={'global', 'buffer'}, + secure=true, + vi_def=true, + expand=true, + varname='p_mp', + defaults={if_true={vi="make"}} + }, + { + full_name='matchpairs', abbreviation='mps', + type='string', list='comma', scope={'buffer'}, + deny_duplicates=true, + vi_def=true, + alloced=true, + varname='p_mps', + defaults={if_true={vi="(:),{:},[:]"}} + }, + { + full_name='matchtime', abbreviation='mat', + type='number', scope={'global'}, + vi_def=true, + varname='p_mat', + defaults={if_true={vi=5}} + }, + { + full_name='maxcombine', abbreviation='mco', + type='number', scope={'global'}, + vi_def=true, + redraw={'curswant'}, + varname='p_mco', + defaults={if_true={vi=2}} + }, + { + full_name='maxfuncdepth', abbreviation='mfd', + type='number', scope={'global'}, + vi_def=true, + varname='p_mfd', + defaults={if_true={vi=100}} + }, + { + full_name='maxmapdepth', abbreviation='mmd', + type='number', scope={'global'}, + vi_def=true, + varname='p_mmd', + defaults={if_true={vi=1000}} + }, + { + full_name='maxmem', abbreviation='mm', + type='number', scope={'global'}, + vi_def=true, + varname='p_mm', + defaults={if_true={vi=macros('DFLT_MAXMEM')}} + }, + { + full_name='maxmempattern', abbreviation='mmp', + type='number', scope={'global'}, + vi_def=true, + varname='p_mmp', + defaults={if_true={vi=1000}} + }, + { + full_name='maxmemtot', abbreviation='mmt', + type='number', scope={'global'}, + vi_def=true, + varname='p_mmt', + defaults={if_true={vi=macros('DFLT_MAXMEMTOT')}} + }, + { + full_name='menuitems', abbreviation='mis', + type='number', scope={'global'}, + vi_def=true, + varname='p_mis', + defaults={if_true={vi=25}} + }, + { + full_name='mkspellmem', abbreviation='msm', + type='string', scope={'global'}, + secure=true, + vi_def=true, + expand=true, + varname='p_msm', + defaults={if_true={vi="460000,2000,500"}} + }, + { + full_name='modeline', abbreviation='ml', + type='bool', scope={'buffer'}, + vim=true, + varname='p_ml', + defaults={if_true={vi=false, vim=true}} + }, + { + full_name='modelines', abbreviation='mls', + type='number', scope={'global'}, + vi_def=true, + varname='p_mls', + defaults={if_true={vi=5}} + }, + { + full_name='modifiable', abbreviation='ma', + type='bool', scope={'buffer'}, + noglob=true, + vi_def=true, + varname='p_ma', + defaults={if_true={vi=true}} + }, + { + full_name='modified', abbreviation='mod', + type='bool', scope={'buffer'}, + no_mkrc=true, + vi_def=true, + redraw={'statuslines'}, + varname='p_mod', + defaults={if_true={vi=false}} + }, + { + full_name='more', + type='bool', scope={'global'}, + vim=true, + varname='p_more', + defaults={if_true={vi=false, vim=true}} + }, + { + full_name='mouse', + type='string', list='flags', scope={'global'}, + varname='p_mouse', + defaults={if_true={vi="", vim="a"}} + }, + { + full_name='mousefocus', abbreviation='mousef', + type='bool', scope={'global'}, + vi_def=true, + enable_if=false, + defaults={if_true={vi=false}} + }, + { + full_name='mousehide', abbreviation='mh', + type='bool', scope={'global'}, + vi_def=true, + enable_if=false, + defaults={if_true={vi=true}} + }, + { + full_name='mousemodel', abbreviation='mousem', + type='string', scope={'global'}, + vi_def=true, + varname='p_mousem', + defaults={if_true={vi="extend"}} + }, + { + full_name='mouseshape', abbreviation='mouses', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vi_def=true, + enable_if=false, + }, + { + full_name='mousetime', abbreviation='mouset', + type='number', scope={'global'}, + vi_def=true, + varname='p_mouset', + defaults={if_true={vi=500}} + }, + { + full_name='nrformats', abbreviation='nf', + type='string', list='comma', scope={'buffer'}, + deny_duplicates=true, + alloced=true, + varname='p_nf', + defaults={if_true={vi="octal,hex", vim="hex"}} + }, + { + full_name='number', abbreviation='nu', + type='bool', scope={'window'}, + vi_def=true, + redraw={'current_window'}, + defaults={if_true={vi=false}} + }, + { + full_name='numberwidth', abbreviation='nuw', + type='number', scope={'window'}, + vim=true, + redraw={'current_window'}, + defaults={if_true={vi=8, vim=4}} + }, + { + full_name='omnifunc', abbreviation='ofu', + type='string', scope={'buffer'}, + secure=true, + vi_def=true, + alloced=true, + varname='p_ofu', + defaults={if_true={vi=""}} + }, + { + full_name='opendevice', abbreviation='odev', + type='bool', scope={'global'}, + vi_def=true, + enable_if=false, + defaults={if_true={vi=false, vim=false}} + }, + { + full_name='operatorfunc', abbreviation='opfunc', + type='string', scope={'global'}, + secure=true, + vi_def=true, + varname='p_opfunc', + defaults={if_true={vi=""}} + }, + { + full_name='paragraphs', abbreviation='para', + type='string', scope={'global'}, + vi_def=true, + varname='p_para', + defaults={if_true={vi="IPLPPPQPP TPHPLIPpLpItpplpipbp"}} + }, + { + full_name='paste', + type='bool', scope={'global'}, + pri_mkrc=true, + vi_def=true, + varname='p_paste', + defaults={if_true={vi=false}} + }, + { + full_name='pastetoggle', abbreviation='pt', + type='string', scope={'global'}, + vi_def=true, + varname='p_pt', + defaults={if_true={vi=""}} + }, + { + full_name='patchexpr', abbreviation='pex', + type='string', scope={'global'}, + secure=true, + vi_def=true, + varname='p_pex', + defaults={if_true={vi=""}} + }, + { + full_name='patchmode', abbreviation='pm', + type='string', scope={'global'}, + normal_fname_chars=true, + vi_def=true, + varname='p_pm', + defaults={if_true={vi=""}} + }, + { + full_name='path', abbreviation='pa', + type='string', list='comma', scope={'global', 'buffer'}, + deny_duplicates=true, + vi_def=true, + expand=true, + varname='p_path', + defaults={if_true={vi=".,/usr/include,,"}} + }, + { + full_name='preserveindent', abbreviation='pi', + type='bool', scope={'buffer'}, + vi_def=true, + vim=true, + varname='p_pi', + defaults={if_true={vi=false}} + }, + { + full_name='previewheight', abbreviation='pvh', + type='number', scope={'global'}, + vi_def=true, + varname='p_pvh', + defaults={if_true={vi=12}} + }, + { + full_name='previewwindow', abbreviation='pvw', + type='bool', scope={'window'}, + noglob=true, + vi_def=true, + redraw={'statuslines'}, + defaults={if_true={vi=false}} + }, + { + full_name='printdevice', abbreviation='pdev', + type='string', scope={'global'}, + secure=true, + vi_def=true, + varname='p_pdev', + defaults={if_true={vi=""}} + }, + { + full_name='printencoding', abbreviation='penc', + type='string', scope={'global'}, + vi_def=true, + varname='p_penc', + defaults={if_true={vi=""}} + }, + { + full_name='printexpr', abbreviation='pexpr', + type='string', scope={'global'}, + vi_def=true, + varname='p_pexpr', + defaults={if_true={vi=""}} + }, + { + full_name='printfont', abbreviation='pfn', + type='string', scope={'global'}, + vi_def=true, + varname='p_pfn', + defaults={if_true={vi="courier"}} + }, + { + full_name='printheader', abbreviation='pheader', + type='string', scope={'global'}, + gettext=true, + vi_def=true, + varname='p_header', + defaults={if_true={vi=N_("%<%f%h%m%=Page %N")}} + }, + { + full_name='printmbcharset', abbreviation='pmbcs', + type='string', scope={'global'}, + vi_def=true, + varname='p_pmcs', + defaults={if_true={vi=""}} + }, + { + full_name='printmbfont', abbreviation='pmbfn', + type='string', scope={'global'}, + vi_def=true, + varname='p_pmfn', + defaults={if_true={vi=""}} + }, + { + full_name='printoptions', abbreviation='popt', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vi_def=true, + varname='p_popt', + defaults={if_true={vi=""}} + }, + { + full_name='prompt', + type='bool', scope={'global'}, + vi_def=true, + varname='p_prompt', + defaults={if_true={vi=true}} + }, + { + full_name='pumheight', abbreviation='ph', + type='number', scope={'global'}, + vi_def=true, + varname='p_ph', + defaults={if_true={vi=0}} + }, + { + full_name='quoteescape', abbreviation='qe', + type='string', scope={'buffer'}, + vi_def=true, + alloced=true, + varname='p_qe', + defaults={if_true={vi="\\"}} + }, + { + full_name='readonly', abbreviation='ro', + type='bool', scope={'buffer'}, + noglob=true, + vi_def=true, + redraw={'statuslines'}, + varname='p_ro', + defaults={if_true={vi=false}} + }, + { + full_name='redrawtime', abbreviation='rdt', + type='number', scope={'global'}, + vi_def=true, + varname='p_rdt', + defaults={if_true={vi=2000}} + }, + { + full_name='regexpengine', abbreviation='re', + type='number', scope={'global'}, + vi_def=true, + varname='p_re', + defaults={if_true={vi=0}} + }, + { + full_name='relativenumber', abbreviation='rnu', + type='bool', scope={'window'}, + vi_def=true, + redraw={'current_window'}, + defaults={if_true={vi=false}} + }, + { + full_name='remap', + type='bool', scope={'global'}, + vi_def=true, + varname='p_remap', + defaults={if_true={vi=true}} + }, + { + full_name='report', + type='number', scope={'global'}, + vi_def=true, + varname='p_report', + defaults={if_true={vi=2}} + }, + { + full_name='restorescreen', abbreviation='rs', + type='bool', scope={'global'}, + vi_def=true, + enable_if=false, + defaults={if_true={vi=true}} + }, + { + full_name='revins', abbreviation='ri', + type='bool', scope={'global'}, + vi_def=true, + vim=true, + varname='p_ri', + defaults={if_true={vi=false}} + }, + { + full_name='rightleft', abbreviation='rl', + type='bool', scope={'window'}, + vi_def=true, + redraw={'current_window'}, + defaults={if_true={vi=false}} + }, + { + full_name='rightleftcmd', abbreviation='rlc', + type='string', scope={'window'}, + vi_def=true, + alloced=true, + redraw={'current_window'}, + defaults={if_true={vi="search"}} + }, + { + full_name='ruler', abbreviation='ru', + type='bool', scope={'global'}, + vi_def=true, + vim=true, + redraw={'statuslines'}, + varname='p_ru', + defaults={if_true={vi=false}} + }, + { + full_name='rulerformat', abbreviation='ruf', + type='string', scope={'global'}, + vi_def=true, + alloced=true, + redraw={'statuslines'}, + varname='p_ruf', + defaults={if_true={vi=""}} + }, + { + full_name='runtimepath', abbreviation='rtp', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + secure=true, + vi_def=true, + expand=true, + varname='p_rtp', + defaults={if_true={vi=macros('DFLT_RUNTIMEPATH')}} + }, + { + full_name='scroll', abbreviation='scr', + type='number', scope={'window'}, + no_mkrc=true, + vi_def=true, + pv_name='p_scroll', + defaults={if_true={vi=12}} + }, + { + full_name='scrollbind', abbreviation='scb', + type='bool', scope={'window'}, + vi_def=true, + pv_name='p_scbind', + defaults={if_true={vi=false}} + }, + { + full_name='scrolljump', abbreviation='sj', + type='number', scope={'global'}, + vi_def=true, + vim=true, + varname='p_sj', + defaults={if_true={vi=1}} + }, + { + full_name='scrolloff', abbreviation='so', + type='number', scope={'global'}, + vi_def=true, + vim=true, + redraw={'all_windows'}, + varname='p_so', + defaults={if_true={vi=0}} + }, + { + full_name='scrollopt', abbreviation='sbo', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vi_def=true, + varname='p_sbo', + defaults={if_true={vi="ver,jump"}} + }, + { + full_name='sections', abbreviation='sect', + type='string', scope={'global'}, + vi_def=true, + varname='p_sections', + defaults={if_true={vi="SHNHH HUnhsh"}} + }, + { + full_name='secure', + type='bool', scope={'global'}, + secure=true, + vi_def=true, + varname='p_secure', + defaults={if_true={vi=false}} + }, + { + full_name='selection', abbreviation='sel', + type='string', scope={'global'}, + vi_def=true, + varname='p_sel', + defaults={if_true={vi="inclusive"}} + }, + { + full_name='selectmode', abbreviation='slm', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vi_def=true, + varname='p_slm', + defaults={if_true={vi=""}} + }, + { + full_name='sessionoptions', abbreviation='ssop', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vim=true, + varname='p_ssop', + defaults={if_true={ + vi="blank,buffers,curdir,folds,help,options,tabpages,winsize", + vim="blank,buffers,curdir,folds,help,tabpages,winsize" + }} + }, + { + full_name='shell', abbreviation='sh', + type='string', scope={'global'}, + secure=true, + vi_def=true, + expand=true, + varname='p_sh', + defaults={if_true={vi="sh"}} + }, + { + full_name='shellcmdflag', abbreviation='shcf', + type='string', scope={'global'}, + secure=true, + vi_def=true, + varname='p_shcf', + defaults={if_true={vi="-c"}} + }, + { + full_name='shellpipe', abbreviation='sp', + type='string', scope={'global'}, + secure=true, + vi_def=true, + varname='p_sp', + defaults={ + condition='UNIX', + if_true={vi="| tee"}, + if_false={vi=">"}, + } + }, + { + full_name='shellquote', abbreviation='shq', + type='string', scope={'global'}, + secure=true, + vi_def=true, + varname='p_shq', + defaults={if_true={vi=""}} + }, + { + full_name='shellredir', abbreviation='srr', + type='string', scope={'global'}, + secure=true, + vi_def=true, + varname='p_srr', + defaults={if_true={vi=">"}} + }, + { + full_name='shellslash', abbreviation='ssl', + type='bool', scope={'global'}, + vi_def=true, + varname='p_ssl', + enable_if='BACKSLASH_IN_FILENAME', + defaults={if_true={vi=false}} + }, + { + full_name='shelltemp', abbreviation='stmp', + type='bool', scope={'global'}, + varname='p_stmp', + defaults={if_true={vi=false, vim=true}} + }, + { + full_name='shellxquote', abbreviation='sxq', + type='string', scope={'global'}, + secure=true, + vi_def=true, + varname='p_sxq', + defaults={if_true={vi=""}} + }, + { + full_name='shellxescape', abbreviation='sxe', + type='string', scope={'global'}, + secure=true, + vi_def=true, + varname='p_sxe', + defaults={if_true={vi=""}} + }, + { + full_name='shiftround', abbreviation='sr', + type='bool', scope={'global'}, + vi_def=true, + vim=true, + varname='p_sr', + defaults={if_true={vi=false}} + }, + { + full_name='shiftwidth', abbreviation='sw', + type='number', scope={'buffer'}, + vi_def=true, + varname='p_sw', + defaults={if_true={vi=8}} + }, + { + full_name='shortmess', abbreviation='shm', + type='string', list='flags', scope={'global'}, + vim=true, + varname='p_shm', + defaults={if_true={vi="", vim="filnxtToO"}} + }, + { + full_name='showbreak', abbreviation='sbr', + type='string', scope={'global'}, + vi_def=true, + redraw={'all_windows'}, + varname='p_sbr', + defaults={if_true={vi=""}} + }, + { + full_name='showcmd', abbreviation='sc', + type='bool', scope={'global'}, + vim=true, + varname='p_sc', + defaults={ + condition='UNIX', + if_true={vi=false, vim=false}, + if_false={vi=false, vim=true}, + } + }, + { + full_name='showfulltag', abbreviation='sft', + type='bool', scope={'global'}, + vi_def=true, + varname='p_sft', + defaults={if_true={vi=false}} + }, + { + full_name='showmatch', abbreviation='sm', + type='bool', scope={'global'}, + vi_def=true, + varname='p_sm', + defaults={if_true={vi=false}} + }, + { + full_name='showmode', abbreviation='smd', + type='bool', scope={'global'}, + vim=true, + varname='p_smd', + defaults={if_true={vi=false, vim=true}} + }, + { + full_name='showtabline', abbreviation='stal', + type='number', scope={'global'}, + vi_def=true, + redraw={'all_windows'}, + varname='p_stal', + defaults={if_true={vi=1}} + }, + { + full_name='sidescroll', abbreviation='ss', + type='number', scope={'global'}, + vi_def=true, + varname='p_ss', + defaults={if_true={vi=0}} + }, + { + full_name='sidescrolloff', abbreviation='siso', + type='number', scope={'global'}, + vi_def=true, + vim=true, + redraw={'current_buffer'}, + varname='p_siso', + defaults={if_true={vi=0}} + }, + { + full_name='smartcase', abbreviation='scs', + type='bool', scope={'global'}, + vi_def=true, + vim=true, + varname='p_scs', + defaults={if_true={vi=false}} + }, + { + full_name='smartindent', abbreviation='si', + type='bool', scope={'buffer'}, + vi_def=true, + vim=true, + varname='p_si', + defaults={if_true={vi=false}} + }, + { + full_name='smarttab', abbreviation='sta', + type='bool', scope={'global'}, + vim=true, + varname='p_sta', + defaults={if_true={vi=false, vim=true}} + }, + { + full_name='softtabstop', abbreviation='sts', + type='number', scope={'buffer'}, + vi_def=true, + vim=true, + varname='p_sts', + defaults={if_true={vi=0}} + }, + { + full_name='spell', + type='bool', scope={'window'}, + vi_def=true, + redraw={'current_window'}, + defaults={if_true={vi=false}} + }, + { + full_name='spellcapcheck', abbreviation='spc', + type='string', scope={'buffer'}, + vi_def=true, + alloced=true, + redraw={'current_buffer'}, + varname='p_spc', + defaults={if_true={vi="[.?!]\\_[\\])'\" ]\\+"}} + }, + { + full_name='spellfile', abbreviation='spf', + type='string', list='comma', scope={'buffer'}, + secure=true, + vi_def=true, + alloced=true, + expand=true, + varname='p_spf', + defaults={if_true={vi=""}} + }, + { + full_name='spelllang', abbreviation='spl', + type='string', list='comma', scope={'buffer'}, + vi_def=true, + alloced=true, + expand=true, + redraw={'current_buffer'}, + varname='p_spl', + defaults={if_true={vi="en"}} + }, + { + full_name='spellsuggest', abbreviation='sps', + type='string', list='comma', scope={'global'}, + secure=true, + vi_def=true, + expand=true, + varname='p_sps', + defaults={if_true={vi="best"}} + }, + { + full_name='splitbelow', abbreviation='sb', + type='bool', scope={'global'}, + vi_def=true, + varname='p_sb', + defaults={if_true={vi=false}} + }, + { + full_name='splitright', abbreviation='spr', + type='bool', scope={'global'}, + vi_def=true, + varname='p_spr', + defaults={if_true={vi=false}} + }, + { + full_name='startofline', abbreviation='sol', + type='bool', scope={'global'}, + vi_def=true, + vim=true, + varname='p_sol', + defaults={if_true={vi=true}} + }, + { + full_name='statusline', abbreviation='stl', + type='string', scope={'global', 'window'}, + vi_def=true, + alloced=true, + redraw={'statuslines'}, + varname='p_stl', + defaults={if_true={vi=""}} + }, + { + full_name='suffixes', abbreviation='su', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vi_def=true, + varname='p_su', + defaults={if_true={vi=".bak,~,.o,.h,.info,.swp,.obj"}} + }, + { + full_name='suffixesadd', abbreviation='sua', + type='string', list='comma', scope={'buffer'}, + deny_duplicates=true, + vi_def=true, + alloced=true, + varname='p_sua', + defaults={if_true={vi=""}} + }, + { + full_name='swapfile', abbreviation='swf', + type='bool', scope={'buffer'}, + vi_def=true, + redraw={'statuslines'}, + varname='p_swf', + defaults={if_true={vi=true}} + }, + { + full_name='swapsync', abbreviation='sws', + type='string', scope={'global'}, + vi_def=true, + varname='p_sws', + defaults={if_true={vi="fsync"}} + }, + { + full_name='switchbuf', abbreviation='swb', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vi_def=true, + varname='p_swb', + defaults={if_true={vi=""}} + }, + { + full_name='synmaxcol', abbreviation='smc', + type='number', scope={'buffer'}, + vi_def=true, + redraw={'current_buffer'}, + varname='p_smc', + defaults={if_true={vi=3000}} + }, + { + full_name='syntax', abbreviation='syn', + type='string', scope={'buffer'}, + noglob=true, + normal_fname_chars=true, + vi_def=true, + alloced=true, + varname='p_syn', + defaults={if_true={vi=""}} + }, + { + full_name='tabline', abbreviation='tal', + type='string', scope={'global'}, + vi_def=true, + redraw={'all_windows'}, + varname='p_tal', + defaults={if_true={vi=""}} + }, + { + full_name='tabpagemax', abbreviation='tpm', + type='number', scope={'global'}, + vim=true, + varname='p_tpm', + defaults={if_true={vi=10, vim=50}} + }, + { + full_name='tabstop', abbreviation='ts', + type='number', scope={'buffer'}, + vi_def=true, + redraw={'current_buffer'}, + varname='p_ts', + defaults={if_true={vi=8}} + }, + { + full_name='tagbsearch', abbreviation='tbs', + type='bool', scope={'global'}, + vi_def=true, + varname='p_tbs', + defaults={if_true={vi=true}} + }, + { + full_name='taglength', abbreviation='tl', + type='number', scope={'global'}, + vi_def=true, + varname='p_tl', + defaults={if_true={vi=0}} + }, + { + full_name='tagrelative', abbreviation='tr', + type='bool', scope={'global'}, + vim=true, + varname='p_tr', + defaults={if_true={vi=false, vim=true}} + }, + { + full_name='tags', abbreviation='tag', + type='string', list='comma', scope={'global', 'buffer'}, + deny_duplicates=true, + vi_def=true, + expand=true, + varname='p_tags', + defaults={if_true={vi="./tags;,tags"}} + }, + { + full_name='tagstack', abbreviation='tgst', + type='bool', scope={'global'}, + vi_def=true, + varname='p_tgst', + defaults={if_true={vi=true}} + }, + { + full_name='termbidi', abbreviation='tbidi', + type='bool', scope={'global'}, + vi_def=true, + varname='p_tbidi', + defaults={if_true={vi=false}} + }, + { + full_name='termencoding', abbreviation='tenc', + type='string', scope={'global'}, + vi_def=true, + redraw={'everything'}, + defaults={if_true={vi=""}} + }, + { + full_name='terse', + type='bool', scope={'global'}, + vi_def=true, + varname='p_terse', + defaults={if_true={vi=false}} + }, + { + full_name='textwidth', abbreviation='tw', + type='number', scope={'buffer'}, + vi_def=true, + vim=true, + redraw={'current_buffer'}, + varname='p_tw', + defaults={if_true={vi=0}} + }, + { + full_name='thesaurus', abbreviation='tsr', + type='string', list='comma', scope={'global', 'buffer'}, + deny_duplicates=true, + vi_def=true, + expand=true, + varname='p_tsr', + defaults={if_true={vi=""}} + }, + { + full_name='tildeop', abbreviation='top', + type='bool', scope={'global'}, + vi_def=true, + vim=true, + varname='p_to', + defaults={if_true={vi=false}} + }, + { + full_name='timeout', abbreviation='to', + type='bool', scope={'global'}, + vi_def=true, + varname='p_timeout', + defaults={if_true={vi=true}} + }, + { + full_name='timeoutlen', abbreviation='tm', + type='number', scope={'global'}, + vi_def=true, + varname='p_tm', + defaults={if_true={vi=1000}} + }, + { + full_name='title', + type='bool', scope={'global'}, + vi_def=true, + varname='p_title', + defaults={if_true={vi=false}} + }, + { + full_name='titlelen', + type='number', scope={'global'}, + vi_def=true, + varname='p_titlelen', + defaults={if_true={vi=85}} + }, + { + 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")}} + }, + { + full_name='titlestring', + type='string', scope={'global'}, + vi_def=true, + varname='p_titlestring', + defaults={if_true={vi=""}} + }, + { + full_name='ttimeout', + type='bool', scope={'global'}, + vi_def=true, + vim=true, + varname='p_ttimeout', + defaults={if_true={vi=false}} + }, + { + full_name='ttimeoutlen', abbreviation='ttm', + type='number', scope={'global'}, + vi_def=true, + varname='p_ttm', + defaults={if_true={vi=-1}} + }, + { + full_name='ttyfast', abbreviation='tf', + type='bool', scope={'global'}, + no_mkrc=true, + vi_def=true, + varname='p_force_on', + defaults={if_true={vi=true}} + }, + { + full_name='undodir', abbreviation='udir', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + secure=true, + vi_def=true, + expand=true, + varname='p_udir', + defaults={if_true={vi="."}} + }, + { + full_name='undofile', abbreviation='udf', + type='bool', scope={'buffer'}, + vi_def=true, + vim=true, + varname='p_udf', + defaults={if_true={vi=false}} + }, + { + full_name='undolevels', abbreviation='ul', + type='number', scope={'global', 'buffer'}, + vi_def=true, + varname='p_ul', + defaults={ + condition={'!UNIX', '!WIN3264'}, + if_true={vi=100}, + if_false={vi=1000}, + } + }, + { + full_name='undoreload', abbreviation='ur', + type='number', scope={'global'}, + vi_def=true, + varname='p_ur', + defaults={if_true={vi=10000}} + }, + { + full_name='updatecount', abbreviation='uc', + type='number', scope={'global'}, + vi_def=true, + varname='p_uc', + defaults={if_true={vi=200}} + }, + { + full_name='updatetime', abbreviation='ut', + type='number', scope={'global'}, + vi_def=true, + varname='p_ut', + defaults={if_true={vi=4000}} + }, + { + full_name='verbose', abbreviation='vbs', + type='number', scope={'global'}, + vi_def=true, + varname='p_verbose', + defaults={if_true={vi=0}} + }, + { + full_name='verbosefile', abbreviation='vfile', + type='string', scope={'global'}, + secure=true, + vi_def=true, + expand=true, + varname='p_vfile', + defaults={if_true={vi=""}} + }, + { + full_name='viewdir', abbreviation='vdir', + type='string', scope={'global'}, + secure=true, + vi_def=true, + expand=true, + varname='p_vdir', + defaults={if_true={vi=macros('DFLT_VDIR')}} + }, + { + full_name='viewoptions', abbreviation='vop', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vi_def=true, + varname='p_vop', + defaults={if_true={vi="folds,options,cursor"}} + }, + { + full_name='viminfo', abbreviation='vi', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + secure=true, + varname='p_viminfo', + defaults={if_true={vi="", vim="!,'100,<50,s10,h"}} + }, + { + full_name='virtualedit', abbreviation='ve', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vi_def=true, + vim=true, + redraw={'curswant'}, + varname='p_ve', + defaults={if_true={vi="", vim=""}} + }, + { + full_name='visualbell', abbreviation='vb', + type='bool', scope={'global'}, + vi_def=true, + varname='p_vb', + defaults={if_true={vi=false}} + }, + { + full_name='warn', + type='bool', scope={'global'}, + vi_def=true, + varname='p_warn', + defaults={if_true={vi=true}} + }, + { + full_name='whichwrap', abbreviation='ww', + type='string', list='flagscomma', scope={'global'}, + vim=true, + varname='p_ww', + defaults={if_true={vi="", vim="b,s"}} + }, + { + full_name='wildchar', abbreviation='wc', + type='number', scope={'global'}, + vim=true, + varname='p_wc', + defaults={if_true={vi=macros('Ctrl_E'), vim=macros('TAB')}} + }, + { + full_name='wildcharm', abbreviation='wcm', + type='number', scope={'global'}, + vi_def=true, + varname='p_wcm', + defaults={if_true={vi=0}} + }, + { + full_name='wildignore', abbreviation='wig', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vi_def=true, + varname='p_wig', + defaults={if_true={vi=""}} + }, + { + full_name='wildignorecase', abbreviation='wic', + type='bool', scope={'global'}, + vi_def=true, + varname='p_wic', + defaults={if_true={vi=false}} + }, + { + full_name='wildmenu', abbreviation='wmnu', + type='bool', scope={'global'}, + vim=true, + varname='p_wmnu', + defaults={if_true={vi=false, vim=true}} + }, + { + full_name='wildmode', abbreviation='wim', + type='string', list='comma', scope={'global'}, + deny_duplicates=true, + vim=true, + varname='p_wim', + defaults={if_true={vi="", vim="list:longest,full"}} + }, + { + full_name='wildoptions', abbreviation='wop', + type='string', scope={'global'}, + vi_def=true, + varname='p_wop', + defaults={if_true={vi=""}} + }, + { + full_name='winaltkeys', abbreviation='wak', + type='string', scope={'global'}, + vi_def=true, + varname='p_wak', + defaults={if_true={vi="menu"}} + }, + { + full_name='window', abbreviation='wi', + type='number', scope={'global'}, + vi_def=true, + varname='p_window', + defaults={if_true={vi=0}} + }, + { + full_name='winheight', abbreviation='wh', + type='number', scope={'global'}, + vi_def=true, + varname='p_wh', + defaults={if_true={vi=1}} + }, + { + full_name='winfixheight', abbreviation='wfh', + type='bool', scope={'window'}, + vi_def=true, + redraw={'statuslines'}, + defaults={if_true={vi=false}} + }, + { + full_name='winfixwidth', abbreviation='wfw', + type='bool', scope={'window'}, + vi_def=true, + redraw={'statuslines'}, + defaults={if_true={vi=false}} + }, + { + full_name='winminheight', abbreviation='wmh', + type='number', scope={'global'}, + vi_def=true, + varname='p_wmh', + defaults={if_true={vi=1}} + }, + { + full_name='winminwidth', abbreviation='wmw', + type='number', scope={'global'}, + vi_def=true, + varname='p_wmw', + defaults={if_true={vi=1}} + }, + { + full_name='winwidth', abbreviation='wiw', + type='number', scope={'global'}, + vi_def=true, + varname='p_wiw', + defaults={if_true={vi=20}} + }, + { + full_name='wrap', + type='bool', scope={'window'}, + vi_def=true, + redraw={'current_window'}, + defaults={if_true={vi=true}} + }, + { + full_name='wrapmargin', abbreviation='wm', + type='number', scope={'buffer'}, + vi_def=true, + varname='p_wm', + defaults={if_true={vi=0}} + }, + { + full_name='wrapscan', abbreviation='ws', + type='bool', scope={'global'}, + vi_def=true, + varname='p_ws', + defaults={if_true={vi=true}} + }, + { + full_name='write', + type='bool', scope={'global'}, + vi_def=true, + varname='p_write', + defaults={if_true={vi=true}} + }, + { + full_name='writeany', abbreviation='wa', + type='bool', scope={'global'}, + vi_def=true, + varname='p_wa', + defaults={if_true={vi=false}} + }, + { + full_name='writebackup', abbreviation='wb', + type='bool', scope={'global'}, + vi_def=true, + vim=true, + varname='p_wb', + defaults={if_true={vi=true}} + }, + { + full_name='writedelay', abbreviation='wd', + type='number', scope={'global'}, + vi_def=true, + varname='p_wd', + defaults={if_true={vi=0}} + }, + } +} diff --git a/src/nvim/os/event.c b/src/nvim/os/event.c deleted file mode 100644 index 4c3a4581c3..0000000000 --- a/src/nvim/os/event.c +++ /dev/null @@ -1,178 +0,0 @@ -#include <assert.h> -#include <stdint.h> -#include <stdbool.h> -#include <stdlib.h> - -#include <uv.h> - -#include "nvim/os/event.h" -#include "nvim/os/input.h" -#include "nvim/msgpack_rpc/defs.h" -#include "nvim/msgpack_rpc/channel.h" -#include "nvim/msgpack_rpc/server.h" -#include "nvim/msgpack_rpc/helpers.h" -#include "nvim/os/signal.h" -#include "nvim/os/rstream.h" -#include "nvim/os/wstream.h" -#include "nvim/os/job.h" -#include "nvim/vim.h" -#include "nvim/memory.h" -#include "nvim/misc2.h" -#include "nvim/ui.h" -#include "nvim/screen.h" -#include "nvim/terminal.h" - -#include "nvim/lib/klist.h" - -// event will be cleaned up after it gets processed -#define _destroy_event(x) // do nothing -KLIST_INIT(Event, Event, _destroy_event) - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "os/event.c.generated.h" -#endif -// deferred_events: Events that should be processed as the K_EVENT special key -// immediate_events: Events that should be processed after exiting libuv event -// loop(to avoid recursion), but before returning from -// `event_poll` -static klist_t(Event) *deferred_events = NULL, *immediate_events = NULL; -static int deferred_events_allowed = 0; - -void event_init(void) -{ - // Initialize the event queues - deferred_events = kl_init(Event); - immediate_events = kl_init(Event); - // early msgpack-rpc initialization - msgpack_rpc_init_method_table(); - msgpack_rpc_helpers_init(); - // Initialize input events - input_init(); - // Timer to wake the event loop if a timeout argument is passed to - // `event_poll` - // Signals - signal_init(); - // Jobs - job_init(); - // finish mspgack-rpc initialization - channel_init(); - server_init(); - terminal_init(); -} - -void event_teardown(void) -{ - if (!deferred_events) { - // Not initialized(possibly a --version invocation) - return; - } - - process_events_from(immediate_events); - process_events_from(deferred_events); - input_stop(); - channel_teardown(); - job_teardown(); - server_teardown(); - signal_teardown(); - terminal_teardown(); - - // this last `uv_run` will return after all handles are stopped, it will - // also take care of finishing any uv_close calls made by other *_teardown - // functions. - do { - uv_run(uv_default_loop(), UV_RUN_DEFAULT); - } while (uv_loop_close(uv_default_loop())); -} - -// Wait for some event -void event_poll(int ms) -{ - static int recursive = 0; - - if (recursive++) { - abort(); // Should not re-enter uv_run - } - - uv_run_mode run_mode = UV_RUN_ONCE; - uv_timer_t timer; - - if (ms > 0) { - uv_timer_init(uv_default_loop(), &timer); - // Use a repeating timeout of ms milliseconds to make sure - // we do not block indefinitely for I/O. - uv_timer_start(&timer, timer_cb, (uint64_t)ms, (uint64_t)ms); - } else if (ms == 0) { - // For ms == 0, we need to do a non-blocking event poll by - // setting the run mode to UV_RUN_NOWAIT. - run_mode = UV_RUN_NOWAIT; - } - - loop(run_mode); - - if (ms > 0) { - // Ensure the timer handle is closed and run the event loop - // once more to let libuv perform it's cleanup - uv_timer_stop(&timer); - uv_close((uv_handle_t *)&timer, NULL); - loop(UV_RUN_NOWAIT); - } - - recursive--; // Can re-enter uv_run now - - // In case this is run before event_init, don't process any events. - if (immediate_events) { - process_events_from(immediate_events); - } -} - -bool event_has_deferred(void) -{ - return deferred_events_allowed && !kl_empty(deferred_events); -} - -void event_enable_deferred(void) -{ - ++deferred_events_allowed; -} - -void event_disable_deferred(void) -{ - --deferred_events_allowed; -} - -// Queue an event -void event_push(Event event, bool deferred) -{ - // Sometimes libuv will run pending callbacks(timer for example) before - // blocking for a poll. If this happens and the callback pushes a event to one - // of the queues, the event would only be processed after the poll - // returns(user hits a key for example). To avoid this scenario, we call - // uv_stop when a event is enqueued. - uv_stop(uv_default_loop()); - *kl_pushp(Event, deferred ? deferred_events : immediate_events) = event; -} - -void event_process(void) -{ - process_events_from(deferred_events); -} - -static void process_events_from(klist_t(Event) *queue) -{ - Event event; - - while (kl_shift(Event, queue, &event) == 0) { - event.handler(event); - } -} - -static void timer_cb(uv_timer_t *handle) -{ -} - -static void loop(uv_run_mode run_mode) -{ - DLOG("Enter event loop"); - uv_run(uv_default_loop(), run_mode); - DLOG("Exit event loop"); -} diff --git a/src/nvim/os/event.h b/src/nvim/os/event.h deleted file mode 100644 index db02b38c7f..0000000000 --- a/src/nvim/os/event.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef NVIM_OS_EVENT_H -#define NVIM_OS_EVENT_H - -#include <stdint.h> -#include <stdbool.h> - -#include "nvim/os/event_defs.h" -#include "nvim/os/job_defs.h" -#include "nvim/os/time.h" - -// Poll for events until a condition or timeout -#define event_poll_until(timeout, condition) \ - do { \ - int remaining = timeout; \ - uint64_t before = (remaining > 0) ? os_hrtime() : 0; \ - while (!(condition)) { \ - event_poll(remaining); \ - if (remaining == 0) { \ - break; \ - } else if (remaining > 0) { \ - uint64_t now = os_hrtime(); \ - remaining -= (int) ((now - before) / 1000000); \ - before = now; \ - if (remaining <= 0) { \ - break; \ - } \ - } \ - } \ - } while (0) - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "os/event.h.generated.h" -#endif - -#endif // NVIM_OS_EVENT_H diff --git a/src/nvim/os/event_defs.h b/src/nvim/os/event_defs.h deleted file mode 100644 index 2dd9403d9f..0000000000 --- a/src/nvim/os/event_defs.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef NVIM_OS_EVENT_DEFS_H -#define NVIM_OS_EVENT_DEFS_H - -#include <stdbool.h> - -#include "nvim/os/job_defs.h" -#include "nvim/os/rstream_defs.h" - -typedef struct event Event; -typedef void (*event_handler)(Event event); - -struct event { - void *data; - event_handler handler; -}; - -#endif // NVIM_OS_EVENT_DEFS_H diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 52c10d0ca7..5eeb275701 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -19,6 +19,15 @@ // Many fs functions from libuv return that value on success. static const int kLibuvSuccess = 0; +static uv_loop_t fs_loop; + + +// Initialize the fs module +void fs_init(void) +{ + uv_loop_init(&fs_loop); +} + /// Change to the given directory. /// @@ -184,7 +193,7 @@ int os_open(const char* path, int flags, int mode) FUNC_ATTR_NONNULL_ALL { uv_fs_t open_req; - int r = uv_fs_open(uv_default_loop(), &open_req, path, flags, mode, NULL); + int r = uv_fs_open(&fs_loop, &open_req, path, flags, mode, NULL); uv_fs_req_cleanup(&open_req); // r is the same as open_req.result (except for OOM: then only r is set). return r; @@ -197,7 +206,7 @@ static bool os_stat(const char *name, uv_stat_t *statbuf) FUNC_ATTR_NONNULL_ALL { uv_fs_t request; - int result = uv_fs_stat(uv_default_loop(), &request, name, NULL); + int result = uv_fs_stat(&fs_loop, &request, name, NULL); *statbuf = request.statbuf; uv_fs_req_cleanup(&request); return (result == kLibuvSuccess); @@ -224,7 +233,7 @@ int os_setperm(const char_u *name, int perm) FUNC_ATTR_NONNULL_ALL { uv_fs_t request; - int result = uv_fs_chmod(uv_default_loop(), &request, + int result = uv_fs_chmod(&fs_loop, &request, (const char*)name, perm, NULL); uv_fs_req_cleanup(&request); @@ -245,7 +254,7 @@ int os_fchown(int file_descriptor, uv_uid_t owner, uv_gid_t group) FUNC_ATTR_NONNULL_ALL { uv_fs_t request; - int result = uv_fs_fchown(uv_default_loop(), &request, file_descriptor, + int result = uv_fs_fchown(&fs_loop, &request, file_descriptor, owner, group, NULL); uv_fs_req_cleanup(&request); return result; @@ -294,7 +303,7 @@ int os_rename(const char_u *path, const char_u *new_path) FUNC_ATTR_NONNULL_ALL { uv_fs_t request; - int result = uv_fs_rename(uv_default_loop(), &request, + int result = uv_fs_rename(&fs_loop, &request, (const char *)path, (const char *)new_path, NULL); uv_fs_req_cleanup(&request); @@ -307,16 +316,64 @@ int os_rename(const char_u *path, const char_u *new_path) /// Make a directory. /// -/// @return `0` for success, non-zero for failure. +/// @return `0` for success, -errno for failure. int os_mkdir(const char *path, int32_t mode) FUNC_ATTR_NONNULL_ALL { uv_fs_t request; - int result = uv_fs_mkdir(uv_default_loop(), &request, path, mode, NULL); + int result = uv_fs_mkdir(&fs_loop, &request, path, mode, NULL); uv_fs_req_cleanup(&request); return result; } +/// Make a directory, with higher levels when needed +/// +/// @param[in] dir Directory to create. +/// @param[in] mode Permissions for the newly-created directory. +/// @param[out] failed_dir If it failed to create directory, then this +/// argument is set to an allocated string containing +/// the name of the directory which os_mkdir_recurse +/// failed to create. I.e. it will contain dir or any +/// of the higher level directories. +/// +/// @return `0` for success, -errno for failure. +int os_mkdir_recurse(const char *const dir, int32_t mode, + char **const failed_dir) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + // Get end of directory name in "dir". + // We're done when it's "/" or "c:/". + const size_t dirlen = strlen(dir); + char *const curdir = xmemdupz(dir, dirlen); + char *const past_head = (char *) get_past_head((char_u *) curdir); + char *e = curdir + dirlen; + const char *const real_end = e; + const char past_head_save = *past_head; + while (!os_isdir((char_u *) curdir)) { + e = (char *) path_tail_with_sep((char_u *) curdir); + if (e <= past_head) { + *past_head = NUL; + break; + } + *e = NUL; + } + while (e != real_end) { + if (e > past_head) { + *e = '/'; + } else { + *past_head = past_head_save; + } + e += strlen(e); + int ret; + if ((ret = os_mkdir(curdir, mode)) != 0) { + *failed_dir = curdir; + return ret; + } + } + xfree(curdir); + return 0; +} + /// Create a unique temporary directory. /// /// @param[in] template Template of the path to the directory with XXXXXX @@ -328,7 +385,7 @@ int os_mkdtemp(const char *template, char *path) FUNC_ATTR_NONNULL_ALL { uv_fs_t request; - int result = uv_fs_mkdtemp(uv_default_loop(), &request, template, NULL); + int result = uv_fs_mkdtemp(&fs_loop, &request, template, NULL); if (result == kLibuvSuccess) { STRNCPY(path, request.path, TEMP_FILE_PATH_MAXLEN); } @@ -343,7 +400,7 @@ int os_rmdir(const char *path) FUNC_ATTR_NONNULL_ALL { uv_fs_t request; - int result = uv_fs_rmdir(uv_default_loop(), &request, path, NULL); + int result = uv_fs_rmdir(&fs_loop, &request, path, NULL); uv_fs_req_cleanup(&request); return result; } @@ -356,7 +413,7 @@ int os_rmdir(const char *path) bool os_scandir(Directory *dir, const char *path) FUNC_ATTR_NONNULL_ALL { - int r = uv_fs_scandir(uv_default_loop(), &dir->request, path, 0, NULL); + int r = uv_fs_scandir(&fs_loop, &dir->request, path, 0, NULL); if (r <= 0) { os_closedir(dir); } @@ -388,7 +445,7 @@ int os_remove(const char *path) FUNC_ATTR_NONNULL_ALL { uv_fs_t request; - int result = uv_fs_unlink(uv_default_loop(), &request, path, NULL); + int result = uv_fs_unlink(&fs_loop, &request, path, NULL); uv_fs_req_cleanup(&request); return result; } @@ -413,7 +470,7 @@ bool os_fileinfo_link(const char *path, FileInfo *file_info) FUNC_ATTR_NONNULL_ALL { uv_fs_t request; - int result = uv_fs_lstat(uv_default_loop(), &request, path, NULL); + int result = uv_fs_lstat(&fs_loop, &request, path, NULL); file_info->stat = request.statbuf; uv_fs_req_cleanup(&request); return (result == kLibuvSuccess); @@ -428,7 +485,7 @@ bool os_fileinfo_fd(int file_descriptor, FileInfo *file_info) FUNC_ATTR_NONNULL_ALL { uv_fs_t request; - int result = uv_fs_fstat(uv_default_loop(), &request, file_descriptor, NULL); + int result = uv_fs_fstat(&fs_loop, &request, file_descriptor, NULL); file_info->stat = request.statbuf; uv_fs_req_cleanup(&request); return (result == kLibuvSuccess); diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index 74a5d3bc2e..09f162f79d 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -6,9 +6,8 @@ #include "nvim/api/private/defs.h" #include "nvim/os/input.h" -#include "nvim/os/event.h" -#include "nvim/os/rstream_defs.h" -#include "nvim/os/rstream.h" +#include "nvim/event/loop.h" +#include "nvim/event/rstream.h" #include "nvim/ascii.h" #include "nvim/vim.h" #include "nvim/ui.h" @@ -30,10 +29,11 @@ typedef enum { kInputEof } InbufPollResult; -static RStream *read_stream = NULL; -static RBuffer *read_buffer = NULL, *input_buffer = NULL; +static Stream read_stream = {.closed = true}; +static RBuffer *input_buffer = NULL; static bool input_eof = false; static int global_fd = 0; +static int events_enabled = 0; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/input.c.generated.h" @@ -54,32 +54,29 @@ int input_global_fd(void) void input_start(int fd) { - if (read_stream) { + if (!read_stream.closed) { return; } global_fd = fd; - read_buffer = rbuffer_new(READ_BUFFER_SIZE); - read_stream = rstream_new(read_cb, read_buffer, NULL); - rstream_set_file(read_stream, fd); - rstream_start(read_stream); + rstream_init_fd(&loop, &read_stream, fd, READ_BUFFER_SIZE, NULL); + rstream_start(&read_stream, read_cb); } void input_stop(void) { - if (!read_stream) { + if (read_stream.closed) { return; } - rstream_stop(read_stream); - rstream_free(read_stream); - read_stream = NULL; + rstream_stop(&read_stream); + stream_close(&read_stream, NULL); } // Low level input function int os_inchar(uint8_t *buf, int maxlen, int ms, int tb_change_cnt) { - if (rbuffer_pending(input_buffer)) { + if (rbuffer_size(input_buffer)) { return (int)rbuffer_read(input_buffer, (char *)buf, (size_t)maxlen); } @@ -108,14 +105,14 @@ int os_inchar(uint8_t *buf, int maxlen, int ms, int tb_change_cnt) return 0; } - if (rbuffer_pending(input_buffer)) { + if (rbuffer_size(input_buffer)) { // Safe to convert rbuffer_read to int, it will never overflow since we use // relatively small buffers. return (int)rbuffer_read(input_buffer, (char *)buf, (size_t)maxlen); } - // If there are deferred events, return the keys directly - if (event_has_deferred()) { + // If there are events, return the keys directly + if (pending_events()) { return push_event_key(buf, maxlen); } @@ -135,11 +132,21 @@ bool os_char_avail(void) // Check for CTRL-C typed by reading all available characters. void os_breakcheck(void) { - if (!disable_breakcheck && !got_int) { - event_poll(0); + if (!got_int) { + loop_poll_events(&loop, 0); } } +void input_enable_events(void) +{ + events_enabled++; +} + +void input_disable_events(void) +{ + events_enabled--; +} + /// Test whether a file descriptor refers to a terminal. /// /// @param fd File descriptor. @@ -153,7 +160,7 @@ size_t input_enqueue(String keys) { char *ptr = keys.data, *end = ptr + keys.size; - while (rbuffer_available(input_buffer) >= 6 && ptr < end) { + while (rbuffer_space(input_buffer) >= 6 && ptr < end) { uint8_t buf[6] = {0}; unsigned int new_size = trans_special((uint8_t **)&ptr, buf, true); @@ -285,7 +292,7 @@ static bool input_poll(int ms) prof_inchar_enter(); } - event_poll_until(ms, input_ready() || input_eof); + LOOP_PROCESS_EVENTS_UNTIL(&loop, NULL, ms, input_ready() || input_eof); if (do_profiling == PROF_YES && ms) { prof_inchar_exit(); @@ -309,16 +316,18 @@ static InbufPollResult inbuf_poll(int ms) return input_eof ? kInputEof : kInputNone; } -static void read_cb(RStream *rstream, void *data, bool at_eof) +static void read_cb(Stream *stream, RBuffer *buf, size_t c, void *data, + bool at_eof) { if (at_eof) { input_eof = true; } - char *buf = rbuffer_read_ptr(read_buffer); - size_t buf_size = rbuffer_pending(read_buffer); - (void)rbuffer_write(input_buffer, buf, buf_size); - rbuffer_consumed(read_buffer, buf_size); + assert(rbuffer_space(input_buffer) >= rbuffer_size(buf)); + RBUFFER_UNTIL_EMPTY(buf, ptr, len) { + (void)rbuffer_write(input_buffer, ptr, len); + rbuffer_consumed(buf, len); + } } static void process_interrupts(void) @@ -327,18 +336,16 @@ static void process_interrupts(void) return; } - char *inbuf = rbuffer_read_ptr(input_buffer); - size_t count = rbuffer_pending(input_buffer), consume_count = 0; - - for (int i = (int)count - 1; i >= 0; i--) { - if (inbuf[i] == 3) { + size_t consume_count = 0; + RBUFFER_EACH_REVERSE(input_buffer, c, i) { + if ((uint8_t)c == 3) { got_int = true; - consume_count = (size_t)i; + consume_count = i; break; } } - if (got_int) { + if (got_int && consume_count) { // Remove everything typed before the CTRL-C rbuffer_consumed(input_buffer, consume_count); } @@ -362,8 +369,8 @@ static int push_event_key(uint8_t *buf, int maxlen) static bool input_ready(void) { return typebuf_was_filled || // API call filled typeahead - rbuffer_pending(input_buffer) > 0 || // Input buffer filled - event_has_deferred(); // Events must be processed + rbuffer_size(input_buffer) || // Input buffer filled + pending_events(); // Events must be processed } // Exit because of an input read error. @@ -374,3 +381,8 @@ static void read_error_exit(void) STRCPY(IObuff, _("Vim: Error reading input, exiting...\n")); preserve_exit(); } + +static bool pending_events(void) +{ + return events_enabled && !queue_empty(loop.events); +} diff --git a/src/nvim/os/job.c b/src/nvim/os/job.c deleted file mode 100644 index 038d0e3c26..0000000000 --- a/src/nvim/os/job.c +++ /dev/null @@ -1,472 +0,0 @@ -#include <stdint.h> -#include <stdbool.h> - -#include <uv.h> - -#include "nvim/os/uv_helpers.h" -#include "nvim/os/job.h" -#include "nvim/os/job_defs.h" -#include "nvim/os/job_private.h" -#include "nvim/os/pty_process.h" -#include "nvim/os/rstream.h" -#include "nvim/os/rstream_defs.h" -#include "nvim/os/wstream.h" -#include "nvim/os/wstream_defs.h" -#include "nvim/os/event.h" -#include "nvim/os/event_defs.h" -#include "nvim/os/time.h" -#include "nvim/vim.h" -#include "nvim/memory.h" - -#ifdef HAVE_SYS_WAIT_H -# include <sys/wait.h> -#endif - -// {SIGNAL}_TIMEOUT is the time (in nanoseconds) that a job has to cleanly exit -// before we send SIGNAL to it -#define TERM_TIMEOUT 1000000000 -#define KILL_TIMEOUT (TERM_TIMEOUT * 2) -#define JOB_BUFFER_SIZE 0xFFFF - -#define close_job_stream(job, stream, type) \ - do { \ - if (job->stream) { \ - type##stream_free(job->stream); \ - job->stream = NULL; \ - if (!uv_is_closing((uv_handle_t *)job->proc_std##stream)) { \ - uv_close((uv_handle_t *)job->proc_std##stream, close_cb); \ - } \ - } \ - } while (0) - -#define close_job_in(job) close_job_stream(job, in, w) -#define close_job_out(job) close_job_stream(job, out, r) -#define close_job_err(job) close_job_stream(job, err, r) - -Job *table[MAX_RUNNING_JOBS] = {NULL}; -size_t stop_requests = 0; -uv_timer_t job_stop_timer; -uv_signal_t schld; - -// Some helpers shared in this module - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "os/job.c.generated.h" -#endif -// Callbacks for libuv - -/// Initializes job control resources -void job_init(void) -{ - uv_disable_stdio_inheritance(); - uv_timer_init(uv_default_loop(), &job_stop_timer); - uv_signal_init(uv_default_loop(), &schld); - uv_signal_start(&schld, chld_handler, SIGCHLD); -} - -/// Releases job control resources and terminates running jobs -void job_teardown(void) -{ - // Stop all jobs - for (int i = 0; i < MAX_RUNNING_JOBS; i++) { - Job *job; - if ((job = table[i]) != NULL) { - uv_kill(job->pid, SIGTERM); - job->term_sent = true; - job_stop(job); - } - } - - // Wait until all jobs are closed - event_poll_until(-1, !stop_requests); - uv_signal_stop(&schld); - uv_close((uv_handle_t *)&schld, NULL); - // Close the timer - uv_close((uv_handle_t *)&job_stop_timer, NULL); -} - -/// Tries to start a new job. -/// -/// @param[out] status The job id if the job started successfully, 0 if the job -/// table is full, -1 if the program could not be executed. -/// @return The job pointer if the job started successfully, NULL otherwise -Job *job_start(JobOptions opts, int *status) -{ - int i; - Job *job; - - // Search for a free slot in the table - for (i = 0; i < MAX_RUNNING_JOBS; i++) { - if (table[i] == NULL) { - break; - } - } - - if (i == MAX_RUNNING_JOBS) { - // No free slots - shell_free_argv(opts.argv); - *status = 0; - return NULL; - } - - job = xmalloc(sizeof(Job)); - // Initialize - job->id = i + 1; - *status = job->id; - job->status = -1; - job->refcount = 1; - job->stopped_time = 0; - job->term_sent = false; - job->in = NULL; - job->out = NULL; - job->err = NULL; - job->opts = opts; - job->closed = false; - - process_init(job); - - if (opts.writable) { - handle_set_job((uv_handle_t *)job->proc_stdin, job); - job->refcount++; - } - - if (opts.stdout_cb) { - handle_set_job((uv_handle_t *)job->proc_stdout, job); - job->refcount++; - } - - if (opts.stderr_cb) { - handle_set_job((uv_handle_t *)job->proc_stderr, job); - job->refcount++; - } - - // Spawn the job - if (!process_spawn(job)) { - if (opts.writable) { - uv_close((uv_handle_t *)job->proc_stdin, close_cb); - } - if (opts.stdout_cb) { - uv_close((uv_handle_t *)job->proc_stdout, close_cb); - } - if (opts.stderr_cb) { - uv_close((uv_handle_t *)job->proc_stderr, close_cb); - } - process_close(job); - event_poll(0); - // Manually invoke the close_cb to free the job resources - *status = -1; - return NULL; - } - - if (opts.writable) { - job->in = wstream_new(opts.maxmem); - wstream_set_stream(job->in, job->proc_stdin); - } - - // Start the readable streams - if (opts.stdout_cb) { - job->out = rstream_new(read_cb, rbuffer_new(JOB_BUFFER_SIZE), job); - rstream_set_stream(job->out, job->proc_stdout); - rstream_start(job->out); - } - - if (opts.stderr_cb) { - job->err = rstream_new(read_cb, rbuffer_new(JOB_BUFFER_SIZE), job); - rstream_set_stream(job->err, job->proc_stderr); - rstream_start(job->err); - } - // Save the job to the table - table[i] = job; - - return job; -} - -/// Finds a job instance by id -/// -/// @param id The job id -/// @return the Job instance -Job *job_find(int id) -{ - Job *job; - - if (id <= 0 || id > MAX_RUNNING_JOBS || !(job = table[id - 1]) - || job->stopped_time) { - return NULL; - } - - return job; -} - -/// Terminates a job. This is a non-blocking operation, but if the job exists -/// it's guaranteed to succeed(SIGKILL will eventually be sent) -/// -/// @param job The Job instance -void job_stop(Job *job) -{ - if (job->stopped_time) { - return; - } - - job->stopped_time = os_hrtime(); - if (job->opts.pty) { - // close all streams for pty jobs to send SIGHUP to the process - job_close_streams(job); - pty_process_close_master(job); - } else { - // Close the job's stdin. If the job doesn't close its own stdout/stderr, - // they will be closed when the job exits(possibly due to being terminated - // after a timeout) - close_job_in(job); - } - - if (!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(&job_stop_timer, job_stop_timer_cb, 100, 100); - } -} - -/// job_wait - synchronously wait for a job to finish -/// -/// @param job The job instance -/// @param ms Number of milliseconds to wait, 0 for not waiting, -1 for -/// waiting until the job quits. -/// @return returns the status code of the exited job. -1 if the job is -/// still running and the `timeout` has expired. Note that this is -/// indistinguishable from the process returning -1 by itself. Which -/// is possible on some OS. Returns -2 if the job was interrupted. -int job_wait(Job *job, int ms) FUNC_ATTR_NONNULL_ALL -{ - // The default status is -1, which represents a timeout - int status = -1; - bool interrupted = false; - - // Increase refcount to stop the job from being freed before we have a - // chance to get the status. - job->refcount++; - event_poll_until(ms, - // Until... - got_int || // interrupted by the user - job->refcount == 1); // job exited - - // 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; - job_stop(job); - if (ms == -1) { - // We can only return, if all streams/handles are closed and the job - // exited. - event_poll_until(-1, job->refcount == 1); - } else { - event_poll(0); - } - } - - if (job->refcount == 1) { - // Job exited, collect status and manually invoke close_cb to free the job - // resources - status = interrupted ? -2 : job->status; - job_close_streams(job); - job_decref(job); - } else { - job->refcount--; - } - - return status; -} - -/// Close the pipe used to write to the job. -/// -/// This can be used for example to indicate to the job process that no more -/// input is coming, and that it should shut down cleanly. -/// -/// It has no effect when the input pipe doesn't exist or was already -/// closed. -/// -/// @param job The job instance -void job_close_in(Job *job) FUNC_ATTR_NONNULL_ALL -{ - close_job_in(job); -} - -// Close the job stdout stream. -void job_close_out(Job *job) FUNC_ATTR_NONNULL_ALL -{ - close_job_out(job); -} - -// Close the job stderr stream. -void job_close_err(Job *job) FUNC_ATTR_NONNULL_ALL -{ - close_job_out(job); -} - -/// All writes that complete after calling this function will be reported -/// to `cb`. -/// -/// Use this function to be notified about the status of an in-flight write. -/// -/// @see {wstream_set_write_cb} -/// -/// @param job The job instance -/// @param cb The function that will be called on write completion or -/// failure. It will be called with the job as the `data` argument. -void job_write_cb(Job *job, wstream_cb cb) FUNC_ATTR_NONNULL_ALL -{ - wstream_set_write_cb(job->in, cb, job); -} - -/// Writes data to the job's stdin. This is a non-blocking operation, it -/// returns when the write request was sent. -/// -/// @param job The Job instance -/// @param buffer The buffer which contains the data to be written -/// @return true if the write request was successfully sent, false if writing -/// to the job stream failed (possibly because the OS buffer is full) -bool job_write(Job *job, WBuffer *buffer) -{ - return wstream_write(job->in, buffer); -} - -/// Get the job id -/// -/// @param job A pointer to the job -/// @return The job id -int job_id(Job *job) -{ - return job->id; -} - -// Get the job pid -int job_pid(Job *job) -{ - return job->pid; -} - -/// Get data associated with a job -/// -/// @param job A pointer to the job -/// @return The job data -void *job_data(Job *job) -{ - return job->opts.data; -} - -/// Resize the window for a pty job -bool job_resize(Job *job, uint16_t width, uint16_t height) -{ - if (!job->opts.pty) { - return false; - } - pty_process_resize(job, width, height); - return true; -} - -void job_close_streams(Job *job) -{ - close_job_in(job); - close_job_out(job); - close_job_err(job); -} - -JobOptions *job_opts(Job *job) -{ - return &job->opts; -} - -/// Iterates the table, sending SIGTERM to stopped jobs and SIGKILL to those -/// that didn't die from SIGTERM after a while(exit_timeout is 0). -static void job_stop_timer_cb(uv_timer_t *handle) -{ - Job *job; - uint64_t now = os_hrtime(); - - for (size_t i = 0; i < MAX_RUNNING_JOBS; i++) { - if ((job = table[i]) == NULL || !job->stopped_time) { - continue; - } - - uint64_t elapsed = now - job->stopped_time; - - if (!job->term_sent && elapsed >= TERM_TIMEOUT) { - ILOG("Sending SIGTERM to job(id: %d)", job->id); - uv_kill(job->pid, SIGTERM); - job->term_sent = true; - } else if (elapsed >= KILL_TIMEOUT) { - ILOG("Sending SIGKILL to job(id: %d)", job->id); - uv_kill(job->pid, SIGKILL); - process_close(job); - } - } -} - -// Wraps the call to std{out,err}_cb and emits a JobExit event if necessary. -static void read_cb(RStream *rstream, void *data, bool eof) -{ - Job *job = data; - - if (rstream == job->out) { - job->opts.stdout_cb(rstream, data, eof); - if (eof) { - close_job_out(job); - } - } else { - job->opts.stderr_cb(rstream, data, eof); - if (eof) { - close_job_err(job); - } - } -} - -static void close_cb(uv_handle_t *handle) -{ - job_decref(handle_get_job(handle)); -} - -static void job_exited(Event event) -{ - Job *job = event.data; - process_close(job); -} - -static void chld_handler(uv_signal_t *handle, int signum) -{ - int stat = 0; - int pid; - - do { - pid = waitpid(-1, &stat, WNOHANG); - } while (pid < 0 && errno == EINTR); - - if (pid <= 0) { - return; - } - - if (WIFSTOPPED(stat) || WIFCONTINUED(stat)) { - // Only care for processes that exited - return; - } - - Job *job = NULL; - // find the job corresponding to the exited pid - for (int i = 0; i < MAX_RUNNING_JOBS; i++) { - if ((job = table[i]) != NULL && job->pid == pid) { - if (WIFEXITED(stat)) { - job->status = WEXITSTATUS(stat); - } else if (WIFSIGNALED(stat)) { - job->status = WTERMSIG(stat); - } - if (exiting) { - // don't enqueue more events when exiting - process_close(job); - } else { - event_push((Event) {.handler = job_exited, .data = job}, false); - } - break; - } - } -} - diff --git a/src/nvim/os/job.h b/src/nvim/os/job.h deleted file mode 100644 index e0ca615626..0000000000 --- a/src/nvim/os/job.h +++ /dev/null @@ -1,21 +0,0 @@ -// Job is a short name we use to refer to child processes that run in parallel -// with the editor, probably executing long-running tasks and sending updates -// asynchronously. Communication happens through anonymous pipes connected to -// the job's std{in,out,err}. They are more like bash/zsh co-processes than the -// usual shell background job. The name 'Job' was chosen because it applies to -// the concept while being significantly shorter. -#ifndef NVIM_OS_JOB_H -#define NVIM_OS_JOB_H - -#include <stdint.h> -#include <stdbool.h> - -#include "nvim/os/rstream_defs.h" -#include "nvim/os/event_defs.h" -#include "nvim/os/wstream.h" -#include "nvim/os/wstream_defs.h" - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "os/job.h.generated.h" -#endif -#endif // NVIM_OS_JOB_H diff --git a/src/nvim/os/job_defs.h b/src/nvim/os/job_defs.h deleted file mode 100644 index 7fee900ac0..0000000000 --- a/src/nvim/os/job_defs.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef NVIM_OS_JOB_DEFS_H -#define NVIM_OS_JOB_DEFS_H - -#include <uv.h> -#include "nvim/os/rstream_defs.h" -#include "nvim/os/wstream_defs.h" - -#define MAX_RUNNING_JOBS 100 -typedef struct job Job; - -/// Function called when the job reads data -/// -/// @param id The job id -/// @param data Some data associated with the job by the caller -typedef void (*job_exit_cb)(Job *job, int status, void *data); - -// Job startup options -// job_exit_cb Callback that will be invoked when the job exits -// maxmem Maximum amount of memory used by the job WStream -typedef struct { - // Argument vector for the process. The first item is the - // executable to run. - // [consumed] - char **argv; - // Caller data that will be associated with the job - void *data; - // If true the job stdin will be available for writing with job_write, - // otherwise it will be redirected to /dev/null - bool writable; - // Callback that will be invoked when data is available on stdout. If NULL - // stdout will be redirected to /dev/null. - rstream_cb stdout_cb; - // Callback that will be invoked when data is available on stderr. If NULL - // stderr will be redirected to /dev/null. - rstream_cb stderr_cb; - // Callback that will be invoked when the job has exited and will not send - // data - job_exit_cb exit_cb; - // Maximum memory used by the job's WStream - size_t maxmem; - // Connect the job to a pseudo terminal - bool pty; - // Initial window dimensions if the job is connected to a pseudo terminal - uint16_t width, height; - // Value for the $TERM environment variable. A default value of "ansi" is - // assumed if NULL - char *term_name; -} JobOptions; - -#define JOB_OPTIONS_INIT ((JobOptions) { \ - .argv = NULL, \ - .data = NULL, \ - .writable = true, \ - .stdout_cb = NULL, \ - .stderr_cb = NULL, \ - .exit_cb = NULL, \ - .maxmem = 0, \ - .pty = false, \ - .width = 80, \ - .height = 24, \ - .term_name = NULL \ - }) -#endif // NVIM_OS_JOB_DEFS_H diff --git a/src/nvim/os/job_private.h b/src/nvim/os/job_private.h deleted file mode 100644 index 983106d918..0000000000 --- a/src/nvim/os/job_private.h +++ /dev/null @@ -1,118 +0,0 @@ -#ifndef NVIM_OS_JOB_PRIVATE_H -#define NVIM_OS_JOB_PRIVATE_H - -#include <stdlib.h> - -#include <uv.h> - -#include "nvim/os/rstream_defs.h" -#include "nvim/os/wstream_defs.h" -#include "nvim/os/pipe_process.h" -#include "nvim/os/pty_process.h" -#include "nvim/os/shell.h" -#include "nvim/log.h" -#include "nvim/memory.h" - -struct job { - // Job id the index in the job table plus one. - int id; - // Process id - int pid; - // Exit status code of the job process - int status; - // Number of references to the job. The job resources will only be freed by - // close_cb when this is 0 - int refcount; - // Time when job_stop was called for the job. - uint64_t stopped_time; - // If SIGTERM was already sent to the job(only send one before SIGKILL) - bool term_sent; - // Readable streams(std{out,err}) - RStream *out, *err; - // Writable stream(stdin) - WStream *in; - // Libuv streams representing stdin/stdout/stderr - uv_stream_t *proc_stdin, *proc_stdout, *proc_stderr; - // Extra data set by the process spawner - void *process; - // If process_close has been called on this job - bool closed; - // Startup options - JobOptions opts; -}; - -extern Job *table[]; -extern size_t stop_requests; -extern uv_timer_t job_stop_timer; - -static inline bool process_spawn(Job *job) -{ - return job->opts.pty ? pty_process_spawn(job) : pipe_process_spawn(job); -} - -static inline void process_init(Job *job) -{ - if (job->opts.pty) { - pty_process_init(job); - } else { - pipe_process_init(job); - } -} - -static inline void process_close(Job *job) -{ - if (job->closed) { - return; - } - job->closed = true; - if (job->opts.pty) { - pty_process_close(job); - } else { - pipe_process_close(job); - } -} - -static inline void process_destroy(Job *job) -{ - if (job->opts.pty) { - pty_process_destroy(job); - } else { - pipe_process_destroy(job); - } -} - -static inline void job_exit_callback(Job *job) -{ - // Free the slot now, 'exit_cb' may want to start another job to replace - // this one - table[job->id - 1] = NULL; - - if (job->opts.exit_cb) { - // Invoke the exit callback - job->opts.exit_cb(job, job->status, job->opts.data); - } - - if (stop_requests && !--stop_requests) { - // Stop the timer if no more stop requests are pending - DLOG("Stopping job kill timer"); - uv_timer_stop(&job_stop_timer); - } -} - -static inline void job_decref(Job *job) -{ - if (--job->refcount == 0) { - // Invoke the exit_cb - job_exit_callback(job); - // Free all memory allocated for the job - xfree(job->proc_stdin->data); - xfree(job->proc_stdout->data); - xfree(job->proc_stderr->data); - shell_free_argv(job->opts.argv); - process_destroy(job); - xfree(job); - } -} - - -#endif // NVIM_OS_JOB_PRIVATE_H diff --git a/src/nvim/os/os.h b/src/nvim/os/os.h index 3dd099890c..69bd1ff4fd 100644 --- a/src/nvim/os/os.h +++ b/src/nvim/os/os.h @@ -12,7 +12,6 @@ # include "os/mem.h.generated.h" # include "os/env.h.generated.h" # include "os/users.h.generated.h" -# include "os/stream.h.generated.h" #endif #endif // NVIM_OS_OS_H diff --git a/src/nvim/os/os_defs.h b/src/nvim/os/os_defs.h index ec94324df4..1d16111066 100644 --- a/src/nvim/os/os_defs.h +++ b/src/nvim/os/os_defs.h @@ -1,10 +1,143 @@ #ifndef NVIM_OS_OS_DEFS_H #define NVIM_OS_OS_DEFS_H +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/types.h> + #ifdef WIN32 # include "nvim/os/win_defs.h" #else # include "nvim/os/unix_defs.h" #endif +/* The number of arguments to a signal handler is configured here. */ +/* It used to be a long list of almost all systems. Any system that doesn't + * have an argument??? */ +#define SIGHASARG + +/* List 3 arg systems here. I guess __sgi, please test and correct me. jw. */ + +#ifdef SIGHASARG +# ifdef SIGHAS3ARGS +# define SIGDEFARG(s) (int s, int sig2, struct sigcontext *scont) +# define SIGDUMMYARG 0, 0, (struct sigcontext *)0 +# else +# define SIGDEFARG(s) (int s) +# define SIGDUMMYARG 0 +# endif +#else +# define SIGDEFARG(s) (void) +# define SIGDUMMYARG +#endif + +// On some systems, time.h should not be included together with sys/time.h. +#if !defined(HAVE_SYS_TIME_H) || defined(TIME_WITH_SYS_TIME) +# include <time.h> +#endif + +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#endif + +#if defined(DIRSIZ) && !defined(MAXNAMLEN) +# define MAXNAMLEN DIRSIZ +#endif + +#if defined(UFS_MAXNAMLEN) && !defined(MAXNAMLEN) +# define MAXNAMLEN UFS_MAXNAMLEN /* for dynix/ptx */ +#endif + +#if defined(NAME_MAX) && !defined(MAXNAMLEN) +# define MAXNAMLEN NAME_MAX /* for Linux before .99p3 */ +#endif + +// Default value. +#ifndef MAXNAMLEN +# define MAXNAMLEN 512 +#endif + +#define BASENAMELEN (MAXNAMLEN - 5) + +// Use the system path length if it makes sense. +#if defined(PATH_MAX) && (PATH_MAX > 1000) +# define MAXPATHL PATH_MAX +#else +# define MAXPATHL 1024 +#endif + +#ifndef FILETYPE_FILE +# define FILETYPE_FILE "filetype.vim" +#endif + +#ifndef FTPLUGIN_FILE +# define FTPLUGIN_FILE "ftplugin.vim" +#endif + +#ifndef INDENT_FILE +# define INDENT_FILE "indent.vim" +#endif + +#ifndef FTOFF_FILE +# define FTOFF_FILE "ftoff.vim" +#endif + +#ifndef FTPLUGOF_FILE +# define FTPLUGOF_FILE "ftplugof.vim" +#endif + +#ifndef INDOFF_FILE +# define INDOFF_FILE "indoff.vim" +#endif + +#define DFLT_ERRORFILE "errors.err" + +// Command-processing buffer. Use large buffers for all platforms. +#define CMDBUFFSIZE 1024 + +// Use up to 5 Mbyte for a buffer. +#ifndef DFLT_MAXMEM +# define DFLT_MAXMEM (5*1024) +#endif +// use up to 10 Mbyte for Vim. +#ifndef DFLT_MAXMEMTOT +# define DFLT_MAXMEMTOT (10*1024) +#endif + +#if !defined(S_ISDIR) && defined(S_IFDIR) +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) && defined(S_IFREG) +# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISBLK) && defined(S_IFBLK) +# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +#endif +#if !defined(S_ISSOCK) && defined(S_IFSOCK) +# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISFIFO) && defined(S_IFIFO) +# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISCHR) && defined(S_IFCHR) +# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +#endif + +// Note: Some systems need both string.h and strings.h (Savage). However, +// some systems can't handle both, only use string.h in that case. +#include <string.h> +#if defined(HAVE_STRINGS_H) && !defined(NO_STRINGS_WITH_STRING_H) +# include <strings.h> +#endif + +// For dup(3). +#define HAVE_DUP + +/// Function to convert -errno error to char * error description +/// +/// -errno errors are returned by a number of os functions. +#define os_strerror uv_strerror + #endif // NVIM_OS_OS_DEFS_H diff --git a/src/nvim/os/pipe_process.c b/src/nvim/os/pipe_process.c deleted file mode 100644 index 2ac305e967..0000000000 --- a/src/nvim/os/pipe_process.c +++ /dev/null @@ -1,110 +0,0 @@ -#include <stdbool.h> -#include <stdlib.h> - -#include <uv.h> - -#include "nvim/os/uv_helpers.h" -#include "nvim/os/job.h" -#include "nvim/os/job_defs.h" -#include "nvim/os/job_private.h" -#include "nvim/os/pipe_process.h" -#include "nvim/memory.h" - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "os/pipe_process.c.generated.h" -#endif - -typedef struct { - // Structures for process spawning/management used by libuv - uv_process_t proc; - uv_process_options_t proc_opts; - uv_stdio_container_t stdio[3]; - uv_pipe_t proc_stdin, proc_stdout, proc_stderr; -} UvProcess; - -void pipe_process_init(Job *job) -{ - UvProcess *pipeproc = xmalloc(sizeof(UvProcess)); - pipeproc->proc_opts.file = job->opts.argv[0]; - pipeproc->proc_opts.args = job->opts.argv; - pipeproc->proc_opts.stdio = pipeproc->stdio; - pipeproc->proc_opts.stdio_count = 3; - pipeproc->proc_opts.flags = UV_PROCESS_WINDOWS_HIDE; - pipeproc->proc_opts.exit_cb = exit_cb; - pipeproc->proc_opts.cwd = NULL; - pipeproc->proc_opts.env = NULL; - pipeproc->proc.data = NULL; - pipeproc->proc_stdin.data = NULL; - pipeproc->proc_stdout.data = NULL; - pipeproc->proc_stderr.data = NULL; - - // Initialize the job std{in,out,err} - pipeproc->stdio[0].flags = UV_IGNORE; - pipeproc->stdio[1].flags = UV_IGNORE; - pipeproc->stdio[2].flags = UV_IGNORE; - - handle_set_job((uv_handle_t *)&pipeproc->proc, job); - - if (job->opts.writable) { - uv_pipe_init(uv_default_loop(), &pipeproc->proc_stdin, 0); - pipeproc->stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; - pipeproc->stdio[0].data.stream = (uv_stream_t *)&pipeproc->proc_stdin; - } - - if (job->opts.stdout_cb) { - uv_pipe_init(uv_default_loop(), &pipeproc->proc_stdout, 0); - pipeproc->stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; - pipeproc->stdio[1].data.stream = (uv_stream_t *)&pipeproc->proc_stdout; - } - - if (job->opts.stderr_cb) { - uv_pipe_init(uv_default_loop(), &pipeproc->proc_stderr, 0); - pipeproc->stdio[2].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; - pipeproc->stdio[2].data.stream = (uv_stream_t *)&pipeproc->proc_stderr; - } - - job->proc_stdin = (uv_stream_t *)&pipeproc->proc_stdin; - job->proc_stdout = (uv_stream_t *)&pipeproc->proc_stdout; - job->proc_stderr = (uv_stream_t *)&pipeproc->proc_stderr; - job->process = pipeproc; -} - -void pipe_process_destroy(Job *job) -{ - UvProcess *pipeproc = job->process; - xfree(pipeproc->proc.data); - xfree(pipeproc); - job->process = NULL; -} - -bool pipe_process_spawn(Job *job) -{ - UvProcess *pipeproc = job->process; - - if (uv_spawn(uv_default_loop(), &pipeproc->proc, &pipeproc->proc_opts) != 0) { - return false; - } - - job->pid = pipeproc->proc.pid; - return true; -} - -void pipe_process_close(Job *job) -{ - UvProcess *pipeproc = job->process; - uv_close((uv_handle_t *)&pipeproc->proc, close_cb); -} - -static void exit_cb(uv_process_t *proc, int64_t status, int term_signal) -{ - Job *job = handle_get_job((uv_handle_t *)proc); - job->status = (int)status; - pipe_process_close(job); -} - -static void close_cb(uv_handle_t *handle) -{ - Job *job = handle_get_job(handle); - job_close_streams(job); - job_decref(job); -} diff --git a/src/nvim/os/pipe_process.h b/src/nvim/os/pipe_process.h deleted file mode 100644 index 17a4255ddc..0000000000 --- a/src/nvim/os/pipe_process.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef NVIM_OS_PIPE_PROCESS_H -#define NVIM_OS_PIPE_PROCESS_H - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "os/pipe_process.h.generated.h" -#endif -#endif // NVIM_OS_PIPE_PROCESS_H diff --git a/src/nvim/os/pty_process.h b/src/nvim/os/pty_process.h deleted file mode 100644 index 62fcd1671f..0000000000 --- a/src/nvim/os/pty_process.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef NVIM_OS_PTY_PROCESS_H -#define NVIM_OS_PTY_PROCESS_H - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "os/pty_process.h.generated.h" -#endif -#endif // NVIM_OS_PTY_PROCESS_H diff --git a/src/nvim/os/rstream.c b/src/nvim/os/rstream.c deleted file mode 100644 index 702f282d53..0000000000 --- a/src/nvim/os/rstream.c +++ /dev/null @@ -1,418 +0,0 @@ -#include <assert.h> -#include <stdint.h> -#include <stdbool.h> -#include <stdlib.h> - -#include <uv.h> - -#include "nvim/os/uv_helpers.h" -#include "nvim/os/rstream_defs.h" -#include "nvim/os/rstream.h" -#include "nvim/ascii.h" -#include "nvim/vim.h" -#include "nvim/memory.h" -#include "nvim/log.h" -#include "nvim/misc1.h" - -struct rbuffer { - char *data; - size_t capacity, rpos, wpos; - RStream *rstream; -}; - -struct rstream { - void *data; - uv_buf_t uvbuf; - size_t fpos; - RBuffer *buffer; - uv_stream_t *stream; - uv_idle_t *fread_idle; - uv_handle_type file_type; - uv_file fd; - rstream_cb cb; - bool free_handle; -}; - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "os/rstream.c.generated.h" -#endif - -/// Creates a new `RBuffer` instance. -RBuffer *rbuffer_new(size_t capacity) -{ - RBuffer *rv = xmalloc(sizeof(RBuffer)); - rv->data = xmalloc(capacity); - rv->capacity = capacity; - rv->rpos = rv->wpos = 0; - rv->rstream = NULL; - return rv; -} - -/// Advances `rbuffer` read pointers to consume data. If the associated -/// RStream had stopped because the buffer was full, this will restart it. -/// -/// This is called automatically by rbuffer_read, but when using -/// `rbuffer_read_ptr` directly, this needs to called after the data was -/// consumed. -void rbuffer_consumed(RBuffer *rbuffer, size_t count) -{ - rbuffer->rpos += count; - if (count && rbuffer->wpos == rbuffer->capacity) { - // `wpos` is at the end of the buffer, so free some space by moving unread - // data... - rbuffer_relocate(rbuffer); - if (rbuffer->rstream) { - // restart the associated RStream - rstream_start(rbuffer->rstream); - } - } -} - -/// Advances `rbuffer` write pointers. If the internal buffer becomes full, -/// this will stop the associated RStream instance. -void rbuffer_produced(RBuffer *rbuffer, size_t count) -{ - rbuffer->wpos += count; - DLOG("Received %u bytes from RStream(%p)", (size_t)count, rbuffer->rstream); - - rbuffer_relocate(rbuffer); - if (rbuffer->rstream && rbuffer->wpos == rbuffer->capacity) { - // The last read filled the buffer, stop reading for now - // - rstream_stop(rbuffer->rstream); - DLOG("Buffer for RStream(%p) is full, stopping it", rbuffer->rstream); - } -} - -/// Reads data from a `RBuffer` instance into a raw buffer. -/// -/// @param rbuffer The `RBuffer` instance -/// @param buffer The buffer which will receive the data -/// @param count Number of bytes that `buffer` can accept -/// @return The number of bytes copied into `buffer` -size_t rbuffer_read(RBuffer *rbuffer, char *buffer, size_t count) -{ - size_t read_count = rbuffer_pending(rbuffer); - - if (count < read_count) { - read_count = count; - } - - if (read_count > 0) { - memcpy(buffer, rbuffer_read_ptr(rbuffer), read_count); - rbuffer_consumed(rbuffer, read_count); - } - - return read_count; -} - -/// Copies data to `rbuffer` read queue. -/// -/// @param rbuffer the `RBuffer` instance -/// @param buffer The buffer containing data to be copied -/// @param count Number of bytes that should be copied -/// @return The number of bytes actually copied -size_t rbuffer_write(RBuffer *rbuffer, char *buffer, size_t count) -{ - size_t write_count = rbuffer_available(rbuffer); - - if (count < write_count) { - write_count = count; - } - - if (write_count > 0) { - memcpy(rbuffer_write_ptr(rbuffer), buffer, write_count); - rbuffer_produced(rbuffer, write_count); - } - - return write_count; -} - -/// Returns a pointer to a raw buffer containing the first byte available for -/// reading. -char *rbuffer_read_ptr(RBuffer *rbuffer) -{ - return rbuffer->data + rbuffer->rpos; -} - -/// Returns a pointer to a raw buffer containing the first byte available for -/// write. -char *rbuffer_write_ptr(RBuffer *rbuffer) -{ - return rbuffer->data + rbuffer->wpos; -} - -/// Returns the number of bytes ready for consumption in `rbuffer` -/// -/// @param rbuffer The `RBuffer` instance -/// @return The number of bytes ready for consumption -size_t rbuffer_pending(RBuffer *rbuffer) -{ - return rbuffer->wpos - rbuffer->rpos; -} - -/// Returns available space in `rbuffer` -/// -/// @param rbuffer The `RBuffer` instance -/// @return The space available in number of bytes -size_t rbuffer_available(RBuffer *rbuffer) -{ - return rbuffer->capacity - rbuffer->wpos; -} - -void rbuffer_free(RBuffer *rbuffer) -{ - xfree(rbuffer->data); - xfree(rbuffer); -} - -/// Creates a new RStream instance. A RStream encapsulates all the boilerplate -/// necessary for reading from a libuv stream. -/// -/// @param cb A function that will be called whenever some data is available -/// for reading with `rstream_read` -/// @param buffer RBuffer instance to associate with the RStream -/// @param data Some state to associate with the `RStream` instance -/// @return The newly-allocated `RStream` instance -RStream * rstream_new(rstream_cb cb, RBuffer *buffer, void *data) -{ - RStream *rv = xmalloc(sizeof(RStream)); - rv->buffer = buffer; - rv->buffer->rstream = rv; - rv->fpos = 0; - rv->data = data; - rv->cb = cb; - rv->stream = NULL; - rv->fread_idle = NULL; - rv->free_handle = false; - rv->file_type = UV_UNKNOWN_HANDLE; - - return rv; -} - -/// Returns the read pointer used by the rstream. -char *rstream_read_ptr(RStream *rstream) -{ - return rbuffer_read_ptr(rstream->buffer); -} - -/// Returns the number of bytes before the rstream is full. -size_t rstream_available(RStream *rstream) -{ - return rbuffer_available(rstream->buffer); -} - -/// Frees all memory allocated for a RStream instance -/// -/// @param rstream The `RStream` instance -void rstream_free(RStream *rstream) -{ - if (rstream->free_handle) { - if (rstream->fread_idle != NULL) { - uv_close((uv_handle_t *)rstream->fread_idle, close_cb); - } else { - uv_close((uv_handle_t *)rstream->stream, close_cb); - } - } - - rbuffer_free(rstream->buffer); - xfree(rstream); -} - -/// Sets the underlying `uv_stream_t` instance -/// -/// @param rstream The `RStream` instance -/// @param stream The new `uv_stream_t` instance -void rstream_set_stream(RStream *rstream, uv_stream_t *stream) -{ - handle_set_rstream((uv_handle_t *)stream, rstream); - rstream->stream = stream; -} - -/// Sets the underlying file descriptor that will be read from. Only pipes -/// and regular files are supported for now. -/// -/// @param rstream The `RStream` instance -/// @param file The file descriptor -void rstream_set_file(RStream *rstream, uv_file file) -{ - rstream->file_type = uv_guess_handle(file); - - if (rstream->free_handle) { - // If this is the second time we're calling this function, free the - // previously allocated memory - if (rstream->fread_idle != NULL) { - uv_close((uv_handle_t *)rstream->fread_idle, close_cb); - rstream->fread_idle = NULL; - } else { - uv_close((uv_handle_t *)rstream->stream, close_cb); - rstream->stream = NULL; - } - } - - if (rstream->file_type == UV_FILE) { - // Non-blocking file reads are simulated with an idle handle that reads - // in chunks of rstream->buffer_size, giving time for other events to - // be processed between reads. - rstream->fread_idle = xmalloc(sizeof(uv_idle_t)); - uv_idle_init(uv_default_loop(), rstream->fread_idle); - rstream->fread_idle->data = NULL; - handle_set_rstream((uv_handle_t *)rstream->fread_idle, rstream); - } else { - // Only pipes are supported for now - assert(rstream->file_type == UV_NAMED_PIPE - || rstream->file_type == UV_TTY); - rstream->stream = xmalloc(sizeof(uv_pipe_t)); - uv_pipe_init(uv_default_loop(), (uv_pipe_t *)rstream->stream, 0); - uv_pipe_open((uv_pipe_t *)rstream->stream, file); - rstream->stream->data = NULL; - handle_set_rstream((uv_handle_t *)rstream->stream, rstream); - } - - rstream->fd = file; - rstream->free_handle = true; -} - -/// Starts watching for events from a `RStream` instance. -/// -/// @param rstream The `RStream` instance -void rstream_start(RStream *rstream) -{ - if (rstream->file_type == UV_FILE) { - uv_idle_start(rstream->fread_idle, fread_idle_cb); - } else { - uv_read_start(rstream->stream, alloc_cb, read_cb); - } -} - -/// Stops watching for events from a `RStream` instance. -/// -/// @param rstream The `RStream` instance -void rstream_stop(RStream *rstream) -{ - if (rstream->file_type == UV_FILE) { - uv_idle_stop(rstream->fread_idle); - } else { - uv_read_stop(rstream->stream); - } -} - -/// Returns the number of bytes ready for consumption in `rstream` -size_t rstream_pending(RStream *rstream) -{ - return rbuffer_pending(rstream->buffer); -} - -/// Reads data from a `RStream` instance into a buffer. -/// -/// @param rstream The `RStream` instance -/// @param buffer The buffer which will receive the data -/// @param count Number of bytes that `buffer` can accept -/// @return The number of bytes copied into `buffer` -size_t rstream_read(RStream *rstream, char *buffer, size_t count) -{ - return rbuffer_read(rstream->buffer, buffer, count); -} - -RBuffer *rstream_buffer(RStream *rstream) -{ - return rstream->buffer; -} - -// Callbacks used by libuv - -// Called by libuv to allocate memory for reading. -static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf) -{ - RStream *rstream = handle_get_rstream(handle); - - buf->len = rbuffer_available(rstream->buffer); - buf->base = rbuffer_write_ptr(rstream->buffer); -} - -// Callback invoked by libuv after it copies the data into the buffer provided -// by `alloc_cb`. This is also called on EOF or when `alloc_cb` returns a -// 0-length buffer. -static void read_cb(uv_stream_t *stream, ssize_t cnt, const uv_buf_t *buf) -{ - RStream *rstream = handle_get_rstream((uv_handle_t *)stream); - - if (cnt <= 0) { - if (cnt != UV_ENOBUFS) { - DLOG("Closing RStream(%p)", rstream); - // Read error or EOF, either way stop the stream and invoke the callback - // with eof == true - uv_read_stop(stream); - rstream->cb(rstream, rstream->data, true); - } - return; - } - - // at this point we're sure that cnt is positive, no error occurred - size_t nread = (size_t) cnt; - - // Data was already written, so all we need is to update 'wpos' to reflect - // the space actually used in the buffer. - rbuffer_produced(rstream->buffer, nread); - rstream->cb(rstream, rstream->data, false); -} - -// Called by the by the 'idle' handle to emulate a reading event -static void fread_idle_cb(uv_idle_t *handle) -{ - uv_fs_t req; - RStream *rstream = handle_get_rstream((uv_handle_t *)handle); - - rstream->uvbuf.len = rbuffer_available(rstream->buffer); - rstream->uvbuf.base = rbuffer_write_ptr(rstream->buffer); - - // the offset argument to uv_fs_read is int64_t, could someone really try - // to read more than 9 quintillion (9e18) bytes? - // upcast is meant to avoid tautological condition warning on 32 bits - uintmax_t fpos_intmax = rstream->fpos; - if (fpos_intmax > INT64_MAX) { - ELOG("stream offset overflow"); - preserve_exit(); - } - - // Synchronous read - uv_fs_read( - uv_default_loop(), - &req, - rstream->fd, - &rstream->uvbuf, - 1, - (int64_t) rstream->fpos, - NULL); - - uv_fs_req_cleanup(&req); - - if (req.result <= 0) { - uv_idle_stop(rstream->fread_idle); - rstream->cb(rstream, rstream->data, true); - return; - } - - // no errors (req.result (ssize_t) is positive), it's safe to cast. - size_t nread = (size_t) req.result; - rbuffer_produced(rstream->buffer, nread); - rstream->fpos += nread; -} - -static void close_cb(uv_handle_t *handle) -{ - xfree(handle->data); - xfree(handle); -} - -static void rbuffer_relocate(RBuffer *rbuffer) -{ - assert(rbuffer->rpos <= rbuffer->wpos); - // Move data ... - memmove( - rbuffer->data, // ...to the beginning of the buffer(rpos 0) - rbuffer->data + rbuffer->rpos, // ...From the first unread position - rbuffer->wpos - rbuffer->rpos); // ...By the number of unread bytes - rbuffer->wpos -= rbuffer->rpos; - rbuffer->rpos = 0; -} diff --git a/src/nvim/os/rstream.h b/src/nvim/os/rstream.h deleted file mode 100644 index 713d1e77e6..0000000000 --- a/src/nvim/os/rstream.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef NVIM_OS_RSTREAM_H -#define NVIM_OS_RSTREAM_H - -#include <stdbool.h> -#include <stdint.h> -#include <uv.h> -#include "nvim/os/event_defs.h" - -#include "nvim/os/rstream_defs.h" - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "os/rstream.h.generated.h" -#endif -#endif // NVIM_OS_RSTREAM_H diff --git a/src/nvim/os/rstream_defs.h b/src/nvim/os/rstream_defs.h deleted file mode 100644 index 1d71160963..0000000000 --- a/src/nvim/os/rstream_defs.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef NVIM_OS_RSTREAM_DEFS_H -#define NVIM_OS_RSTREAM_DEFS_H - -#include <stdbool.h> - -typedef struct rbuffer RBuffer; -typedef struct rstream RStream; - -/// Type of function called when the RStream receives data -/// -/// @param rstream The RStream instance -/// @param data State associated with the RStream instance -/// @param eof If the stream reached EOF. -typedef void (*rstream_cb)(RStream *rstream, void *data, bool eof); - -#endif // NVIM_OS_RSTREAM_DEFS_H - diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index 2de3b1aeed..2d97c4bf4f 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -8,9 +8,9 @@ #include "nvim/ascii.h" #include "nvim/lib/kvec.h" #include "nvim/log.h" -#include "nvim/os/event.h" -#include "nvim/os/job.h" -#include "nvim/os/rstream.h" +#include "nvim/event/loop.h" +#include "nvim/event/uv_process.h" +#include "nvim/event/rstream.h" #include "nvim/os/shell.h" #include "nvim/os/signal.h" #include "nvim/types.h" @@ -189,7 +189,7 @@ static int do_os_system(char **argv, { // the output buffer DynamicBuffer buf = DYNAMIC_BUFFER_INIT; - rstream_cb data_cb = system_data_cb; + stream_read_cb data_cb = system_data_cb; if (nread) { *nread = 0; } @@ -204,48 +204,60 @@ static int do_os_system(char **argv, char prog[MAXPATHL]; xstrlcpy(prog, argv[0], MAXPATHL); - int status; - JobOptions opts = JOB_OPTIONS_INIT; - opts.argv = argv; - opts.data = &buf; - opts.writable = input != NULL; - opts.stdout_cb = data_cb; - opts.stderr_cb = data_cb; - opts.exit_cb = NULL; - Job *job = job_start(opts, &status); - - if (status <= 0) { + Stream in, out, err; + UvProcess uvproc = uv_process_init(&loop, &buf); + Process *proc = &uvproc.process; + Queue *events = queue_new_child(loop.events); + proc->events = events; + proc->argv = argv; + proc->in = input != NULL ? &in : NULL; + proc->out = &out; + proc->err = &err; + if (!process_spawn(proc)) { + loop_poll_events(&loop, 0); // Failed, probably due to `sh` not being executable if (!silent) { MSG_PUTS(_("\nCannot execute ")); msg_outtrans((char_u *)prog); msg_putchar('\n'); } + queue_free(events); 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 + // 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); + } + proc->out->events = NULL; + rstream_init(proc->out, 0); + rstream_start(proc->out, data_cb); + proc->err->events = NULL; + rstream_init(proc->err, 0); + rstream_start(proc->err, data_cb); + // write the input, if any if (input) { WBuffer *input_buffer = wstream_new_buffer((char *) input, len, 1, NULL); - if (!job_write(job, input_buffer)) { - // couldn't write, stop the job and tell the user about it - job_stop(job); + if (!wstream_write(&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 - job_write_cb(job, shell_write_cb); - } else { - // close the input stream, let the process know that no more input is - // coming - job_close_in(job); + wstream_set_write_cb(&in, shell_write_cb); } // invoke busy_start here so event_poll_until wont change the busy state for // the UI ui_busy_start(); ui_flush(); - status = job_wait(job, -1); + int status = process_wait(proc, -1, NULL); ui_busy_stop(); // prepare the out parameters if requested @@ -265,6 +277,9 @@ static int do_os_system(char **argv, } } + assert(queue_empty(events)); + queue_free(events); + return status; } @@ -283,25 +298,37 @@ static void dynamic_buffer_ensure(DynamicBuffer *buf, size_t desired) buf->data = xrealloc(buf->data, buf->cap); } -static void system_data_cb(RStream *rstream, void *data, bool eof) +static void system_data_cb(Stream *stream, RBuffer *buf, size_t count, + void *data, bool eof) { - Job *job = data; - DynamicBuffer *buf = job_data(job); + DynamicBuffer *dbuf = data; - size_t nread = rstream_pending(rstream); - - dynamic_buffer_ensure(buf, buf->len + nread + 1); - rstream_read(rstream, buf->data + buf->len, nread); - - buf->len += nread; + size_t nread = buf->size; + dynamic_buffer_ensure(dbuf, dbuf->len + nread + 1); + rbuffer_read(buf, dbuf->data + dbuf->len, nread); + dbuf->len += nread; } -static void out_data_cb(RStream *rstream, void *data, bool eof) +static void out_data_cb(Stream *stream, RBuffer *buf, size_t count, void *data, + bool eof) { - RBuffer *rbuffer = rstream_buffer(rstream); - size_t written = write_output(rbuffer_read_ptr(rbuffer), - rbuffer_pending(rbuffer), false, eof); - rbuffer_consumed(rbuffer, written); + size_t cnt; + char *ptr = rbuffer_read_ptr(buf, &cnt); + + if (!cnt) { + return; + } + + size_t written = write_output(ptr, cnt, false, eof); + // No output written, force emptying the Rbuffer if it is full. + if (!written && rbuffer_size(buf) == rbuffer_capacity(buf)) { + screen_del_lines(0, 0, 1, (int)Rows, NULL); + screen_puts_len((char_u *)ptr, (int)cnt, (int)Rows - 1, 0, 0); + written = cnt; + } + if (written) { + rbuffer_consumed(buf, written); + } } /// Parses a command string into a sequence of words, taking quotes into @@ -417,6 +444,7 @@ static size_t write_output(char *output, size_t remaining, bool to_buffer, if (!output) { return 0; } + char replacement_NUL = to_buffer ? NL : 1; char *start = output; size_t off = 0; @@ -424,9 +452,10 @@ static size_t write_output(char *output, size_t remaining, bool to_buffer, while (off < remaining) { if (output[off] == NL) { // Insert the line - output[off] = NUL; if (to_buffer) { - ml_append(curwin->w_cursor.lnum++, (char_u *)output, 0, false); + output[off] = NUL; + ml_append(curwin->w_cursor.lnum++, (char_u *)output, (int)off + 1, + false); } else { screen_del_lines(0, 0, 1, (int)Rows, NULL); screen_puts_len((char_u *)output, (int)off, lastrow, 0, 0); @@ -440,7 +469,7 @@ static size_t write_output(char *output, size_t remaining, bool to_buffer, if (output[off] == NUL) { // Translate NUL to NL - output[off] = NL; + output[off] = replacement_NUL; } off++; } @@ -467,8 +496,7 @@ static size_t write_output(char *output, size_t remaining, bool to_buffer, return (size_t)(output - start); } -static void shell_write_cb(WStream *wstream, void *data, int status) +static void shell_write_cb(Stream *stream, void *data, int status) { - Job *job = data; - job_close_in(job); + stream_close(stream, NULL); } diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c index f824543003..7158721433 100644 --- a/src/nvim/os/signal.c +++ b/src/nvim/os/signal.c @@ -11,12 +11,13 @@ #include "nvim/memory.h" #include "nvim/misc1.h" #include "nvim/misc2.h" +#include "nvim/event/signal.h" #include "nvim/os/signal.h" -#include "nvim/os/event.h" +#include "nvim/event/loop.h" -static uv_signal_t spipe, shup, squit, sterm; +static SignalWatcher spipe, shup, squit, sterm; #ifdef SIGPWR -static uv_signal_t spwr; +static SignalWatcher spwr; #endif static bool rejecting_deadly; @@ -27,40 +28,40 @@ static bool rejecting_deadly; void signal_init(void) { - uv_signal_init(uv_default_loop(), &spipe); - uv_signal_init(uv_default_loop(), &shup); - uv_signal_init(uv_default_loop(), &squit); - uv_signal_init(uv_default_loop(), &sterm); - uv_signal_start(&spipe, signal_cb, SIGPIPE); - uv_signal_start(&shup, signal_cb, SIGHUP); - uv_signal_start(&squit, signal_cb, SIGQUIT); - uv_signal_start(&sterm, signal_cb, SIGTERM); + signal_watcher_init(&loop, &spipe, NULL); + signal_watcher_init(&loop, &shup, NULL); + signal_watcher_init(&loop, &squit, NULL); + signal_watcher_init(&loop, &sterm, NULL); + signal_watcher_start(&spipe, on_signal, SIGPIPE); + signal_watcher_start(&shup, on_signal, SIGHUP); + signal_watcher_start(&squit, on_signal, SIGQUIT); + signal_watcher_start(&sterm, on_signal, SIGTERM); #ifdef SIGPWR - uv_signal_init(uv_default_loop(), &spwr); - uv_signal_start(&spwr, signal_cb, SIGPWR); + signal_watcher_init(&loop, &spwr, NULL); + signal_watcher_start(&spwr, on_signal, SIGPWR); #endif } void signal_teardown(void) { signal_stop(); - uv_close((uv_handle_t *)&spipe, NULL); - uv_close((uv_handle_t *)&shup, NULL); - uv_close((uv_handle_t *)&squit, NULL); - uv_close((uv_handle_t *)&sterm, NULL); + signal_watcher_close(&spipe, NULL); + signal_watcher_close(&shup, NULL); + signal_watcher_close(&squit, NULL); + signal_watcher_close(&sterm, NULL); #ifdef SIGPWR - uv_close((uv_handle_t *)&spwr, NULL); + signal_watcher_close(&spwr, NULL); #endif } void signal_stop(void) { - uv_signal_stop(&spipe); - uv_signal_stop(&shup); - uv_signal_stop(&squit); - uv_signal_stop(&sterm); + signal_watcher_stop(&spipe); + signal_watcher_stop(&shup); + signal_watcher_stop(&squit); + signal_watcher_stop(&sterm); #ifdef SIGPWR - uv_signal_stop(&spwr); + signal_watcher_stop(&spwr); #endif } @@ -111,19 +112,9 @@ static void deadly_signal(int signum) preserve_exit(); } -static void signal_cb(uv_signal_t *handle, int signum) +static void on_signal(SignalWatcher *handle, int signum, void *data) { assert(signum >= 0); - event_push((Event) { - .handler = on_signal_event, - .data = (void *)(uintptr_t)signum - }, false); -} - -static void on_signal_event(Event event) -{ - int signum = (int)(uintptr_t)event.data; - switch (signum) { #ifdef SIGPWR case SIGPWR: @@ -147,4 +138,3 @@ static void on_signal_event(Event event) break; } } - diff --git a/src/nvim/os/signal.h b/src/nvim/os/signal.h index 927437b2db..5d8cc6f661 100644 --- a/src/nvim/os/signal.h +++ b/src/nvim/os/signal.h @@ -1,8 +1,6 @@ #ifndef NVIM_OS_SIGNAL_H #define NVIM_OS_SIGNAL_H -#include "nvim/os/event_defs.h" - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/signal.h.generated.h" #endif diff --git a/src/nvim/os/stream.c b/src/nvim/os/stream.c deleted file mode 100644 index 0c448872c3..0000000000 --- a/src/nvim/os/stream.c +++ /dev/null @@ -1,30 +0,0 @@ -// Functions for working with stdio streams (as opposed to RStream/WStream). - -#include <stdio.h> -#include <stdbool.h> - -#include <uv.h> - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "os/stream.c.generated.h" -#endif - -/// Sets the stream associated with `fd` to "blocking" mode. -/// -/// @return `0` on success, or `-errno` on failure. -int stream_set_blocking(int fd, bool blocking) -{ - // Private loop to avoid conflict with existing watcher(s): - // uv__io_stop: Assertion `loop->watchers[w->fd] == w' failed. - uv_loop_t loop; - uv_pipe_t stream; - uv_loop_init(&loop); - uv_pipe_init(&loop, &stream, 0); - uv_pipe_open(&stream, fd); - int retval = uv_stream_set_blocking((uv_stream_t *)&stream, blocking); - uv_close((uv_handle_t *)&stream, NULL); - uv_run(&loop, UV_RUN_NOWAIT); // not necessary, but couldn't hurt. - uv_loop_close(&loop); - return retval; -} - diff --git a/src/nvim/os/time.c b/src/nvim/os/time.c index 590dfba797..ee17938afc 100644 --- a/src/nvim/os/time.c +++ b/src/nvim/os/time.c @@ -7,7 +7,7 @@ #include <uv.h> #include "nvim/os/time.h" -#include "nvim/os/event.h" +#include "nvim/event/loop.h" #include "nvim/vim.h" static uv_mutex_t delay_mutex; @@ -43,7 +43,7 @@ void os_delay(uint64_t milliseconds, bool ignoreinput) if (milliseconds > INT_MAX) { milliseconds = INT_MAX; } - event_poll_until((int)milliseconds, got_int); + LOOP_PROCESS_EVENTS_UNTIL(&loop, NULL, (int)milliseconds, got_int); } else { os_microdelay(milliseconds * 1000); } diff --git a/src/nvim/os/unix_defs.h b/src/nvim/os/unix_defs.h index 28ae89ff77..9ab4ba1c1a 100644 --- a/src/nvim/os/unix_defs.h +++ b/src/nvim/os/unix_defs.h @@ -1,7 +1,73 @@ #ifndef NVIM_OS_UNIX_DEFS_H #define NVIM_OS_UNIX_DEFS_H +#include <unistd.h> +#include <signal.h> + +// Defines BSD, if it's a BSD system. +#ifdef HAVE_SYS_PARAM_H +# include <sys/param.h> +#endif + + #define TEMP_DIR_NAMES {"$TMPDIR", "/tmp", ".", "~"} #define TEMP_FILE_PATH_MAXLEN 256 +#define HAVE_ACL (HAVE_POSIX_ACL || HAVE_SOLARIS_ACL) + +// Special wildcards that need to be handled by the shell. +#define SPECIAL_WILDCHAR "`'{" + +// Unix system-dependent file names +#ifndef SYS_VIMRC_FILE +# define SYS_VIMRC_FILE "$VIM/nvimrc" +#endif +#ifndef DFLT_HELPFILE +# define DFLT_HELPFILE "$VIMRUNTIME/doc/help.txt" +#endif +#ifndef SYNTAX_FNAME +# define SYNTAX_FNAME "$VIMRUNTIME/syntax/%s.vim" +#endif +#ifndef USR_EXRC_FILE +# define USR_EXRC_FILE "~/.exrc" +#endif +#ifndef USR_VIMRC_FILE +# define USR_VIMRC_FILE "~/.nvimrc" +#endif +#ifndef USR_VIMRC_FILE2 +# define USR_VIMRC_FILE2 "~/.nvim/nvimrc" +#endif +#ifndef EXRC_FILE +# define EXRC_FILE ".exrc" +#endif +#ifndef VIMRC_FILE +# define VIMRC_FILE ".nvimrc" +#endif +#ifndef VIMINFO_FILE +# define VIMINFO_FILE "~/.nviminfo" +#endif + +// Default for 'backupdir'. +#ifndef DFLT_BDIR +# define DFLT_BDIR ".,~/tmp,~/" +#endif + +// Default for 'directory'. +#ifndef DFLT_DIR +# define DFLT_DIR ".,~/tmp,/var/tmp,/tmp" +#endif + +// Default for 'viewdir'. +#ifndef DFLT_VDIR +# define DFLT_VDIR "~/.nvim/view" +#endif + +#ifdef RUNTIME_GLOBAL +# define DFLT_RUNTIMEPATH "~/.nvim," RUNTIME_GLOBAL ",$VIMRUNTIME," \ + RUNTIME_GLOBAL "/after,~/.nvim/after" +#else +# define DFLT_RUNTIMEPATH \ + "~/.nvim,$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after,~/.nvim/after" +#endif + #endif // NVIM_OS_UNIX_DEFS_H diff --git a/src/nvim/os/users.c b/src/nvim/os/users.c index a57ba41af1..637a86c74f 100644 --- a/src/nvim/os/users.c +++ b/src/nvim/os/users.c @@ -41,7 +41,12 @@ int os_get_usernames(garray_T *users) // Return OK if a name found. int os_get_user_name(char *s, size_t len) { +#ifdef UNIX return os_get_uname(getuid(), s, len); +#else + // TODO(equalsraf): Windows GetUserName() + return os_get_uname(0, s, len); +#endif } // Insert user name for "uid" in s[len]. diff --git a/src/nvim/os/uv_helpers.c b/src/nvim/os/uv_helpers.c deleted file mode 100644 index 89687bdac7..0000000000 --- a/src/nvim/os/uv_helpers.c +++ /dev/null @@ -1,98 +0,0 @@ -#include <assert.h> -#include <uv.h> - -#include "nvim/os/uv_helpers.h" -#include "nvim/vim.h" -#include "nvim/memory.h" - -/// Common structure that will always be assigned to the `data` field of -/// libuv handles. It has fields for many types of pointers, and allow a single -/// handle to contain data from many sources -typedef struct { - WStream *wstream; - RStream *rstream; - Job *job; -} HandleData; - - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "os/uv_helpers.c.generated.h" -#endif - -/// Gets the RStream instance associated with a libuv handle -/// -/// @param handle libuv handle -/// @return the RStream pointer -RStream *handle_get_rstream(uv_handle_t *handle) -{ - RStream *rv = init(handle)->rstream; - assert(rv != NULL); - return rv; -} - -/// Associates a RStream instance with a libuv handle -/// -/// @param handle libuv handle -/// @param rstream the RStream pointer -void handle_set_rstream(uv_handle_t *handle, RStream *rstream) -{ - init(handle)->rstream = rstream; -} - -/// Gets the WStream instance associated with a libuv handle -/// -/// @param handle libuv handle -/// @return the WStream pointer -WStream *handle_get_wstream(uv_handle_t *handle) -{ - WStream *rv = init(handle)->wstream; - assert(rv != NULL); - return rv; -} - -/// Associates a WStream instance with a libuv handle -/// -/// @param handle libuv handle -/// @param wstream the WStream pointer -void handle_set_wstream(uv_handle_t *handle, WStream *wstream) -{ - HandleData *data = init(handle); - data->wstream = wstream; -} - -/// Gets the Job instance associated with a libuv handle -/// -/// @param handle libuv handle -/// @return the Job pointer -Job *handle_get_job(uv_handle_t *handle) -{ - Job *rv = init(handle)->job; - assert(rv != NULL); - return rv; -} - -/// Associates a Job instance with a libuv handle -/// -/// @param handle libuv handle -/// @param job the Job pointer -void handle_set_job(uv_handle_t *handle, Job *job) -{ - init(handle)->job = job; -} - -static HandleData *init(uv_handle_t *handle) -{ - HandleData *rv; - - if (handle->data == NULL) { - rv = xmalloc(sizeof(HandleData)); - rv->rstream = NULL; - rv->wstream = NULL; - rv->job = NULL; - handle->data = rv; - } else { - rv = handle->data; - } - - return rv; -} diff --git a/src/nvim/os/uv_helpers.h b/src/nvim/os/uv_helpers.h deleted file mode 100644 index b49656bcb8..0000000000 --- a/src/nvim/os/uv_helpers.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef NVIM_OS_UV_HELPERS_H -#define NVIM_OS_UV_HELPERS_H - -#include <uv.h> - -#include "nvim/os/wstream_defs.h" -#include "nvim/os/rstream_defs.h" -#include "nvim/os/job_defs.h" - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "os/uv_helpers.h.generated.h" -#endif -#endif // NVIM_OS_UV_HELPERS_H diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h index bea147ad2d..19d796bd08 100644 --- a/src/nvim/os/win_defs.h +++ b/src/nvim/os/win_defs.h @@ -6,4 +6,19 @@ #define TEMP_DIR_NAMES {"$TMP", "$TEMP", "$USERPROFILE", ""} #define TEMP_FILE_PATH_MAXLEN _MAX_PATH +// Defines needed to fix the build on Windows: +// - USR_EXRC_FILE +// - USR_VIMRC_FILE +// - VIMINFO_FILE +// - DFLT_DIR +// - DFLT_BDIR +// - DFLT_VDIR +// - DFLT_RUNTIMEPATH +// - EXRC_FILE +// - VIMRC_FILE +// - SYNTAX_FNAME +// - DFLT_HELPFILE +// - SYS_VIMRC_FILE +// - SPECIAL_WILDCHAR + #endif // NVIM_OS_WIN_DEFS_H diff --git a/src/nvim/os/wstream.c b/src/nvim/os/wstream.c deleted file mode 100644 index 73896c381d..0000000000 --- a/src/nvim/os/wstream.c +++ /dev/null @@ -1,243 +0,0 @@ -#include <assert.h> -#include <stdint.h> -#include <stdbool.h> -#include <stdlib.h> - -#include <uv.h> - -#include "nvim/os/uv_helpers.h" -#include "nvim/os/wstream.h" -#include "nvim/os/wstream_defs.h" -#include "nvim/vim.h" -#include "nvim/memory.h" - -#define DEFAULT_MAXMEM 1024 * 1024 * 10 - -struct wstream { - uv_stream_t *stream; - // Memory currently used by pending buffers - size_t curmem; - // Maximum memory used by this instance - size_t maxmem; - // Number of pending requests - size_t pending_reqs; - bool freed, free_handle; - // (optional) Write callback and data - wstream_cb cb; - void *data; -}; - -struct wbuffer { - size_t size, refcount; - char *data; - wbuffer_data_finalizer cb; -}; - -typedef struct { - WStream *wstream; - WBuffer *buffer; - uv_write_t uv_req; -} WRequest; - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "os/wstream.c.generated.h" -#endif - -/// Creates a new WStream instance. A WStream encapsulates all the boilerplate -/// necessary for writing to a libuv stream. -/// -/// @param maxmem Maximum amount memory used by this `WStream` instance. If 0, -/// a default value of 10mb will be used. -/// @return The newly-allocated `WStream` instance -WStream * wstream_new(size_t maxmem) -{ - if (!maxmem) { - maxmem = DEFAULT_MAXMEM; - } - - WStream *rv = xmalloc(sizeof(WStream)); - rv->maxmem = maxmem; - rv->stream = NULL; - rv->curmem = 0; - rv->pending_reqs = 0; - rv->freed = false; - rv->free_handle = false; - rv->cb = NULL; - - return rv; -} - -/// Frees all memory allocated for a WStream instance -/// -/// @param wstream The `WStream` instance -void wstream_free(WStream *wstream) { - if (!wstream->pending_reqs) { - if (wstream->free_handle) { - uv_close((uv_handle_t *)wstream->stream, close_cb); - } else { - handle_set_wstream((uv_handle_t *)wstream->stream, NULL); - xfree(wstream); - } - } else { - wstream->freed = true; - } -} - -/// Sets the underlying `uv_stream_t` instance -/// -/// @param wstream The `WStream` instance -/// @param stream The new `uv_stream_t` instance -void wstream_set_stream(WStream *wstream, uv_stream_t *stream) -{ - handle_set_wstream((uv_handle_t *)stream, wstream); - wstream->stream = stream; -} - -/// Sets the underlying file descriptor that will be written to. Only pipes -/// are supported for now. -/// -/// @param wstream The `WStream` instance -/// @param file The file descriptor -void wstream_set_file(WStream *wstream, uv_file file) -{ - assert(uv_guess_handle(file) == UV_NAMED_PIPE || - uv_guess_handle(file) == UV_TTY); - wstream->stream = xmalloc(sizeof(uv_pipe_t)); - uv_pipe_init(uv_default_loop(), (uv_pipe_t *)wstream->stream, 0); - uv_pipe_open((uv_pipe_t *)wstream->stream, file); - wstream->stream->data = NULL; - handle_set_wstream((uv_handle_t *)wstream->stream, wstream); - wstream->free_handle = true; -} - -/// Sets a callback that will be called on completion of a write request, -/// indicating failure/success. -/// -/// This affects all requests currently in-flight as well. Overwrites any -/// possible earlier callback. -/// -/// @note This callback will not fire if the write request couldn't even be -/// queued properly (i.e.: when `wstream_write() returns an error`). -/// -/// @param wstream The `WStream` instance -/// @param cb The callback -/// @param data User-provided data that will be passed to `cb` -void wstream_set_write_cb(WStream *wstream, wstream_cb cb, void *data) - FUNC_ATTR_NONNULL_ARG(1) -{ - wstream->cb = cb; - wstream->data = data; -} - -/// Queues data for writing to the backing file descriptor of a `WStream` -/// instance. This will fail if the write would cause the WStream use more -/// memory than specified by `maxmem`. -/// -/// @param wstream The `WStream` instance -/// @param buffer The buffer which contains data to be written -/// @return false if the write failed -bool wstream_write(WStream *wstream, WBuffer *buffer) -{ - // This should not be called after a wstream was freed - assert(!wstream->freed); - - if (wstream->curmem > wstream->maxmem) { - goto err; - } - - wstream->curmem += buffer->size; - - WRequest *data = xmalloc(sizeof(WRequest)); - data->wstream = wstream; - data->buffer = buffer; - data->uv_req.data = data; - - uv_buf_t uvbuf; - uvbuf.base = buffer->data; - uvbuf.len = buffer->size; - - if (uv_write(&data->uv_req, wstream->stream, &uvbuf, 1, write_cb)) { - xfree(data); - goto err; - } - - wstream->pending_reqs++; - return true; - -err: - wstream_release_wbuffer(buffer); - return false; -} - -/// Creates a WBuffer object for holding output data. Instances of this -/// object can be reused across WStream instances, and the memory is freed -/// automatically when no longer needed(it tracks the number of references -/// internally) -/// -/// @param data Data stored by the WBuffer -/// @param size The size of the data array -/// @param refcount The number of references for the WBuffer. This will be used -/// by WStream instances to decide when a WBuffer should be freed. -/// @param cb Pointer to function that will be responsible for freeing -/// the buffer data(passing 'free' will work as expected). -/// @return The allocated WBuffer instance -WBuffer *wstream_new_buffer(char *data, - size_t size, - size_t refcount, - wbuffer_data_finalizer cb) -{ - WBuffer *rv = xmalloc(sizeof(WBuffer)); - rv->size = size; - rv->refcount = refcount; - rv->cb = cb; - rv->data = data; - - return rv; -} - -static void write_cb(uv_write_t *req, int status) -{ - WRequest *data = req->data; - - data->wstream->curmem -= data->buffer->size; - - wstream_release_wbuffer(data->buffer); - - if (data->wstream->cb) { - data->wstream->cb(data->wstream, - data->wstream->data, - status); - } - - data->wstream->pending_reqs--; - - if (data->wstream->freed && data->wstream->pending_reqs == 0) { - // Last pending write, free the wstream; - if (data->wstream->free_handle) { - uv_close((uv_handle_t *)data->wstream->stream, close_cb); - } else { - xfree(data->wstream); - } - } - - xfree(data); -} - -void wstream_release_wbuffer(WBuffer *buffer) -{ - if (!--buffer->refcount) { - if (buffer->cb) { - buffer->cb(buffer->data); - } - - xfree(buffer); - } -} - -static void close_cb(uv_handle_t *handle) -{ - xfree(handle_get_wstream(handle)); - xfree(handle->data); - xfree(handle); -} - diff --git a/src/nvim/os/wstream.h b/src/nvim/os/wstream.h deleted file mode 100644 index d0e9bef93a..0000000000 --- a/src/nvim/os/wstream.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef NVIM_OS_WSTREAM_H -#define NVIM_OS_WSTREAM_H - -#include <stdint.h> -#include <stdbool.h> -#include <uv.h> - -#include "nvim/os/wstream_defs.h" - -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "os/wstream.h.generated.h" -#endif -#endif // NVIM_OS_WSTREAM_H diff --git a/src/nvim/os/wstream_defs.h b/src/nvim/os/wstream_defs.h deleted file mode 100644 index cfa0bf0b60..0000000000 --- a/src/nvim/os/wstream_defs.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef NVIM_OS_WSTREAM_DEFS_H -#define NVIM_OS_WSTREAM_DEFS_H - -typedef struct wbuffer WBuffer; -typedef struct wstream WStream; -typedef void (*wbuffer_data_finalizer)(void *data); - -/// Type of function called when the WStream has information about a write -/// request. -/// -/// @param wstream The `WStream` instance -/// @param data User-defined data -/// @param status 0 on success, anything else indicates failure -typedef void (*wstream_cb)(WStream *wstream, - void *data, - int status); - -#endif // NVIM_OS_WSTREAM_DEFS_H - diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c index ccd0073db1..122b3a171d 100644 --- a/src/nvim/os_unix.c +++ b/src/nvim/os_unix.c @@ -47,13 +47,10 @@ #include "nvim/types.h" #include "nvim/os/os.h" #include "nvim/os/time.h" -#include "nvim/os/event.h" #include "nvim/os/input.h" #include "nvim/os/shell.h" #include "nvim/os/signal.h" -#include "nvim/os/job.h" #include "nvim/msgpack_rpc/helpers.h" -#include "nvim/msgpack_rpc/defs.h" #ifdef HAVE_STROPTS_H # include <stropts.h> diff --git a/src/nvim/os_unix_defs.h b/src/nvim/os_unix_defs.h deleted file mode 100644 index c66a81447c..0000000000 --- a/src/nvim/os_unix_defs.h +++ /dev/null @@ -1,216 +0,0 @@ -#ifndef NVIM_OS_UNIX_DEFS_LEGACY_H -#define NVIM_OS_UNIX_DEFS_LEGACY_H - -/* - * VIM - Vi IMproved by Bram Moolenaar - * - * Do ":help uganda" in Vim to read copying and usage conditions. - * Do ":help credits" in Vim to see a list of people who contributed. - */ - -#include <stdio.h> -#include <ctype.h> - -#include <sys/types.h> -#include <sys/stat.h> - -#include <stdlib.h> - -#ifdef HAVE_UNISTD_H -# include <unistd.h> -#endif - -#ifdef HAVE_SYS_PARAM_H -# include <sys/param.h> /* defines BSD, if it's a BSD system */ -#endif - -/* The number of arguments to a signal handler is configured here. */ -/* It used to be a long list of almost all systems. Any system that doesn't - * have an argument??? */ -#define SIGHASARG - -/* List 3 arg systems here. I guess __sgi, please test and correct me. jw. */ - -#ifdef SIGHASARG -# ifdef SIGHAS3ARGS -# define SIGDEFARG(s) (int s, int sig2, struct sigcontext *scont) -# define SIGDUMMYARG 0, 0, (struct sigcontext *)0 -# else -# define SIGDEFARG(s) (int s) -# define SIGDUMMYARG 0 -# endif -#else -# define SIGDEFARG(s) (void) -# define SIGDUMMYARG -#endif - -#if !defined(HAVE_SYS_TIME_H) || defined(TIME_WITH_SYS_TIME) -# include <time.h> /* on some systems time.h should not be - included together with sys/time.h */ -#endif -#ifdef HAVE_SYS_TIME_H -# include <sys/time.h> -#endif - -#include <signal.h> - -#if defined(DIRSIZ) && !defined(MAXNAMLEN) -# define MAXNAMLEN DIRSIZ -#endif - -#if defined(UFS_MAXNAMLEN) && !defined(MAXNAMLEN) -# define MAXNAMLEN UFS_MAXNAMLEN /* for dynix/ptx */ -#endif - -#if defined(NAME_MAX) && !defined(MAXNAMLEN) -# define MAXNAMLEN NAME_MAX /* for Linux before .99p3 */ -#endif - -/* - * Note: if MAXNAMLEN has the wrong value, you will get error messages - * for not being able to open the swap file. - */ -#if !defined(MAXNAMLEN) -# define MAXNAMLEN 512 /* for all other Unix */ -#endif - -#define BASENAMELEN (MAXNAMLEN - 5) - -/* - * Unix system-dependent file names - */ -#ifndef SYS_VIMRC_FILE -# define SYS_VIMRC_FILE "$VIM/nvimrc" -#endif -#ifndef DFLT_HELPFILE -# define DFLT_HELPFILE "$VIMRUNTIME/doc/help.txt" -#endif -#ifndef FILETYPE_FILE -# define FILETYPE_FILE "filetype.vim" -#endif -#ifndef FTPLUGIN_FILE -# define FTPLUGIN_FILE "ftplugin.vim" -#endif -#ifndef INDENT_FILE -# define INDENT_FILE "indent.vim" -#endif -#ifndef FTOFF_FILE -# define FTOFF_FILE "ftoff.vim" -#endif -#ifndef FTPLUGOF_FILE -# define FTPLUGOF_FILE "ftplugof.vim" -#endif -#ifndef INDOFF_FILE -# define INDOFF_FILE "indoff.vim" -#endif -#ifndef SYS_MENU_FILE -# define SYS_MENU_FILE "$VIMRUNTIME/menu.vim" -#endif - -#ifndef USR_EXRC_FILE -# define USR_EXRC_FILE "~/.exrc" -#endif - - -#ifndef USR_VIMRC_FILE -# define USR_VIMRC_FILE "~/.nvimrc" -#endif - - -#if !defined(USR_VIMRC_FILE2) -# define USR_VIMRC_FILE2 "~/.nvim/nvimrc" -#endif - -# ifndef VIMINFO_FILE -# define VIMINFO_FILE "~/.nviminfo" -# endif - -#ifndef EXRC_FILE -# define EXRC_FILE ".exrc" -#endif - -#ifndef VIMRC_FILE -# define VIMRC_FILE ".nvimrc" -#endif - - -#ifndef SYNTAX_FNAME -# define SYNTAX_FNAME "$VIMRUNTIME/syntax/%s.vim" -#endif - -#ifndef DFLT_BDIR -# define DFLT_BDIR ".,~/tmp,~/" /* default for 'backupdir' */ -#endif - -#ifndef DFLT_DIR -# define DFLT_DIR ".,~/tmp,/var/tmp,/tmp" /* default for 'directory' */ -#endif - -#ifndef DFLT_VDIR -# define DFLT_VDIR "~/.nvim/view" // default for 'viewdir' -#endif - -#define DFLT_ERRORFILE "errors.err" - -# ifdef RUNTIME_GLOBAL -# define DFLT_RUNTIMEPATH "~/.nvim," RUNTIME_GLOBAL ",$VIMRUNTIME," \ - RUNTIME_GLOBAL "/after,~/.nvim/after" -# else -# define DFLT_RUNTIMEPATH \ - "~/.nvim,$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after,~/.nvim/after" -# endif - -/* Special wildcards that need to be handled by the shell */ -#define SPECIAL_WILDCHAR "`'{" - -/* - * Unix has plenty of memory, use large buffers - */ -#define CMDBUFFSIZE 1024 /* size of the command processing buffer */ - -/* Use the system path length if it makes sense. */ -#if defined(PATH_MAX) && (PATH_MAX > 1000) -# define MAXPATHL PATH_MAX -#else -# define MAXPATHL 1024 -#endif - -# ifndef DFLT_MAXMEM -# define DFLT_MAXMEM (5*1024) /* use up to 5 Mbyte for a buffer */ -# endif -# ifndef DFLT_MAXMEMTOT -# define DFLT_MAXMEMTOT (10*1024) /* use up to 10 Mbyte for Vim */ -# endif - -#if !defined(S_ISDIR) && defined(S_IFDIR) -# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) -#endif -#if !defined(S_ISREG) && defined(S_IFREG) -# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) -#endif -#if !defined(S_ISBLK) && defined(S_IFBLK) -# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) -#endif -#if !defined(S_ISSOCK) && defined(S_IFSOCK) -# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) -#endif -#if !defined(S_ISFIFO) && defined(S_IFIFO) -# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) -#endif -#if !defined(S_ISCHR) && defined(S_IFCHR) -# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) -#endif - -/* Note: Some systems need both string.h and strings.h (Savage). However, - * some systems can't handle both, only use string.h in that case. */ -# include <string.h> -#if defined(HAVE_STRINGS_H) && !defined(NO_STRINGS_WITH_STRING_H) -# include <strings.h> -#endif - -#define HAVE_DUP /* have dup() */ - -/* We have three kinds of ACL support. */ -#define HAVE_ACL (HAVE_POSIX_ACL || HAVE_SOLARIS_ACL || HAVE_AIX_ACL) - -#endif // NVIM_OS_UNIX_DEFS_LEGACY_H diff --git a/src/nvim/path.c b/src/nvim/path.c index 152154e5f4..72980fcd0e 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -481,6 +481,21 @@ static size_t path_expand(garray_T *gap, const char_u *path, int flags) return do_path_expand(gap, path, 0, flags, false); } +static const char *scandir_next_with_dots(Directory *dir) +{ + static int count = 0; + if (dir == NULL) { // initialize + count = 0; + return NULL; + } + + count += 1; + if (count == 1 || count == 2) { + return (count == 1) ? "." : ".."; + } + return os_scandir_next(dir); +} + /// Implementation of path_expand(). /// /// Chars before `path + wildoff` do not get expanded. @@ -597,11 +612,12 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, *s = NUL; Directory dir; - // open the directory for scanning - if (os_scandir(&dir, *buf == NUL ? "." : (char *)buf)) { + if (os_scandir(&dir, *buf == NUL ? "." : (char *)buf) + || os_isdir(*buf == NUL ? (char_u *)"." : (char_u *)buf)) { // Find all matching entries. char_u *name; - while((name = (char_u *) os_scandir_next(&dir))) { + scandir_next_with_dots(NULL /* initialize */); + while((name = (char_u *) scandir_next_with_dots(&dir)) && name != NULL) { if ((name[0] != '.' || starts_with_dot) && ((regmatch.regprog != NULL && vim_regexec(®match, name, 0)) || ((flags & EW_NOTWILD) diff --git a/src/nvim/rbuffer.c b/src/nvim/rbuffer.c new file mode 100644 index 0000000000..0a04ba1954 --- /dev/null +++ b/src/nvim/rbuffer.c @@ -0,0 +1,214 @@ +#include <assert.h> +#include <stddef.h> +#include <string.h> + +#include "nvim/memory.h" +#include "nvim/vim.h" +#include "nvim/rbuffer.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "rbuffer.c.generated.h" +#endif + +/// Creates a new `RBuffer` instance. +RBuffer *rbuffer_new(size_t capacity) + FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET +{ + if (!capacity) { + capacity = 0xffff; + } + + RBuffer *rv = xmalloc(sizeof(RBuffer) + capacity); + rv->full_cb = rv->nonfull_cb = NULL; + rv->data = NULL; + rv->size = 0; + rv->write_ptr = rv->read_ptr = rv->start_ptr; + rv->end_ptr = rv->start_ptr + capacity; + return rv; +} + +void rbuffer_free(RBuffer *buf) +{ + xfree(buf); +} + +size_t rbuffer_size(RBuffer *buf) FUNC_ATTR_NONNULL_ALL +{ + return buf->size; +} + +size_t rbuffer_capacity(RBuffer *buf) FUNC_ATTR_NONNULL_ALL +{ + return (size_t)(buf->end_ptr - buf->start_ptr); +} + +size_t rbuffer_space(RBuffer *buf) FUNC_ATTR_NONNULL_ALL +{ + return rbuffer_capacity(buf) - buf->size; +} + +/// Return a pointer to a raw buffer containing the first empty slot available +/// for writing. The second argument is a pointer to the maximum number of +/// bytes that could be written. +/// +/// It is necessary to call this function twice to ensure all empty space was +/// used. See RBUFFER_UNTIL_FULL for a macro that simplifies this task. +char *rbuffer_write_ptr(RBuffer *buf, size_t *write_count) FUNC_ATTR_NONNULL_ALL +{ + if (buf->size == rbuffer_capacity(buf)) { + *write_count = 0; + return NULL; + } + + if (buf->write_ptr >= buf->read_ptr) { + *write_count = (size_t)(buf->end_ptr - buf->write_ptr); + } else { + *write_count = (size_t)(buf->read_ptr - buf->write_ptr); + } + + return buf->write_ptr; +} + +// Set read and write pointer for an empty RBuffer to the beginning of the +// buffer. +void rbuffer_reset(RBuffer *buf) FUNC_ATTR_NONNULL_ALL +{ + if (buf->size == 0) { + buf->write_ptr = buf->read_ptr = buf->start_ptr; + } +} + +/// Adjust `rbuffer` write pointer to reflect produced data. This is called +/// automatically by `rbuffer_write`, but when using `rbuffer_write_ptr` +/// directly, this needs to called after the data was copied to the internal +/// buffer. The write pointer will be wrapped if required. +void rbuffer_produced(RBuffer *buf, size_t count) FUNC_ATTR_NONNULL_ALL +{ + assert(count && count <= rbuffer_space(buf)); + + buf->write_ptr += count; + if (buf->write_ptr >= buf->end_ptr) { + // wrap around + buf->write_ptr -= rbuffer_capacity(buf); + } + + buf->size += count; + if (buf->full_cb && !rbuffer_space(buf)) { + buf->full_cb(buf, buf->data); + } +} + +/// Return a pointer to a raw buffer containing the first byte available +/// for reading. The second argument is a pointer to the maximum number of +/// bytes that could be read. +/// +/// It is necessary to call this function twice to ensure all available bytes +/// were read. See RBUFFER_UNTIL_EMPTY for a macro that simplifies this task. +char *rbuffer_read_ptr(RBuffer *buf, size_t *read_count) FUNC_ATTR_NONNULL_ALL +{ + if (!buf->size) { + *read_count = 0; + return NULL; + } + + if (buf->read_ptr < buf->write_ptr) { + *read_count = (size_t)(buf->write_ptr - buf->read_ptr); + } else { + *read_count = (size_t)(buf->end_ptr - buf->read_ptr); + } + + return buf->read_ptr; +} + +/// Adjust `rbuffer` read pointer to reflect consumed data. This is called +/// automatically by `rbuffer_read`, but when using `rbuffer_read_ptr` +/// directly, this needs to called after the data was copied from the internal +/// buffer. The read pointer will be wrapped if required. +void rbuffer_consumed(RBuffer *buf, size_t count) + FUNC_ATTR_NONNULL_ALL +{ + assert(count && count <= buf->size); + + buf->read_ptr += count; + if (buf->read_ptr >= buf->end_ptr) { + buf->read_ptr -= rbuffer_capacity(buf); + } + + bool was_full = buf->size == rbuffer_capacity(buf); + buf->size -= count; + if (buf->nonfull_cb && was_full) { + buf->nonfull_cb(buf, buf->data); + } +} + +// Higher level functions for copying from/to RBuffer instances and data +// pointers +size_t rbuffer_write(RBuffer *buf, char *src, size_t src_size) + FUNC_ATTR_NONNULL_ALL +{ + size_t size = src_size; + + RBUFFER_UNTIL_FULL(buf, wptr, wcnt) { + size_t copy_count = MIN(src_size, wcnt); + memcpy(wptr, src, copy_count); + rbuffer_produced(buf, copy_count); + + if (!(src_size -= copy_count)) { + return size; + } + + src += copy_count; + } + + return size - src_size; +} + +size_t rbuffer_read(RBuffer *buf, char *dst, size_t dst_size) + FUNC_ATTR_NONNULL_ALL +{ + size_t size = dst_size; + + RBUFFER_UNTIL_EMPTY(buf, rptr, rcnt) { + size_t copy_count = MIN(dst_size, rcnt); + memcpy(dst, rptr, copy_count); + rbuffer_consumed(buf, copy_count); + + if (!(dst_size -= copy_count)) { + return size; + } + + dst += copy_count; + } + + return size - dst_size; +} + +char *rbuffer_get(RBuffer *buf, size_t index) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET +{ + assert(index < buf->size); + char *rptr = buf->read_ptr + index; + if (rptr >= buf->end_ptr) { + rptr -= rbuffer_capacity(buf); + } + return rptr; +} + +int rbuffer_cmp(RBuffer *buf, const char *str, size_t count) + FUNC_ATTR_NONNULL_ALL +{ + assert(count <= buf->size); + size_t rcnt; + (void)rbuffer_read_ptr(buf, &rcnt); + size_t n = MIN(count, rcnt); + int rv = memcmp(str, buf->read_ptr, n); + count -= n; + size_t remaining = buf->size - rcnt; + + if (rv || !count || !remaining) { + return rv; + } + + return memcmp(str + n, buf->start_ptr, count); +} + diff --git a/src/nvim/rbuffer.h b/src/nvim/rbuffer.h new file mode 100644 index 0000000000..b205db0b5a --- /dev/null +++ b/src/nvim/rbuffer.h @@ -0,0 +1,83 @@ +// Ring buffer implementation. This is basically an array that wraps read/write +// pointers around the memory region. It should be more efficient than the old +// RBuffer which required memmove() calls to relocate read/write positions. +// +// The main purpose of RBuffer is simplify memory management when reading from +// uv_stream_t instances: +// +// - The event loop writes data to a RBuffer, advancing the write pointer +// - The main loop reads data, advancing the read pointer +// - If the buffer becomes full(size == capacity) the rstream is temporarily +// stopped(automatic backpressure handling) +// +// Reference: http://en.wikipedia.org/wiki/Circular_buffer +#ifndef NVIM_RBUFFER_H +#define NVIM_RBUFFER_H + +#include <stddef.h> +#include <stdint.h> + +// Macros that simplify working with the read/write pointers directly by hiding +// ring buffer wrap logic. Some examples: +// +// - Pass the write pointer to a function(write_data) that incrementally +// produces data, returning the number of bytes actually written to the +// ring buffer: +// +// RBUFFER_UNTIL_FULL(rbuf, ptr, cnt) +// rbuffer_produced(rbuf, write_data(state, ptr, cnt)); +// +// - Pass the read pointer to a function(read_data) that incrementally +// consumes data, returning the number of bytes actually read from the +// ring buffer: +// +// RBUFFER_UNTIL_EMPTY(rbuf, ptr, cnt) +// rbuffer_consumed(rbuf, read_data(state, ptr, cnt)); +// +// Note that the rbuffer_{produced,consumed} calls are necessary or these macros +// create infinite loops +#define RBUFFER_UNTIL_EMPTY(buf, rptr, rcnt) \ + for (size_t rcnt = 0, _r = 1; _r; _r = 0) \ + for (char *rptr = rbuffer_read_ptr(buf, &rcnt); \ + buf->size; \ + rptr = rbuffer_read_ptr(buf, &rcnt)) + +#define RBUFFER_UNTIL_FULL(buf, wptr, wcnt) \ + for (size_t wcnt = 0, _r = 1; _r; _r = 0) \ + for (char *wptr = rbuffer_write_ptr(buf, &wcnt); \ + rbuffer_space(buf); \ + wptr = rbuffer_write_ptr(buf, &wcnt)) + + +// Iteration +#define RBUFFER_EACH(buf, c, i) \ + for (size_t i = 0; i < buf->size; i = buf->size) \ + for (char c = 0; \ + i < buf->size ? ((int)(c = *rbuffer_get(buf, i))) || 1 : 0; \ + i++) + +#define RBUFFER_EACH_REVERSE(buf, c, i) \ + for (size_t i = buf->size; i != SIZE_MAX; i = SIZE_MAX) \ + for (char c = 0; \ + i-- > 0 ? ((int)(c = *rbuffer_get(buf, i))) || 1 : 0; \ + ) + +typedef struct rbuffer RBuffer; +/// Type of function invoked during certain events: +/// - When the RBuffer switches to the full state +/// - When the RBuffer switches to the non-full state +typedef void(*rbuffer_callback)(RBuffer *buf, void *data); + +struct rbuffer { + rbuffer_callback full_cb, nonfull_cb; + void *data; + size_t size; + char *end_ptr, *read_ptr, *write_ptr; + char start_ptr[]; +}; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "rbuffer.h.generated.h" +#endif + +#endif // NVIM_RBUFFER_H diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index dddd347822..e2c4b590d0 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -961,10 +961,10 @@ static void reg_equi_class(int c) REGMBC(0x107) REGMBC(0x109) REGMBC(0x10b) REGMBC(0x10d) return; - case 'd': CASEMBC(0x10f) CASEMBC(0x111) CASEMBC(0x1d0b) - CASEMBC(0x1e11) + case 'd': CASEMBC(0x10f) CASEMBC(0x111) CASEMBC(0x1e0b) + CASEMBC(0x1e0f) CASEMBC(0x1e11) regmbc('d'); REGMBC(0x10f) REGMBC(0x111) - REGMBC(0x1e0b) REGMBC(0x01e0f) REGMBC(0x1e11) + REGMBC(0x1e0b) REGMBC(0x1e0f) REGMBC(0x1e11) return; case 'e': case '\350': case '\351': case '\352': case '\353': CASEMBC(0x113) CASEMBC(0x115) CASEMBC(0x117) CASEMBC(0x119) @@ -1104,7 +1104,7 @@ static int get_coll_element(char_u **pp) int l = 1; char_u *p = *pp; - if (p[1] == '.') { + if (p[0] != NUL && p[1] == '.') { if (has_mbyte) l = (*mb_ptr2len)(p + 2); if (p[l + 2] == '.' && p[l + 3] == ']') { @@ -1120,12 +1120,10 @@ static int get_coll_element(char_u **pp) } static int reg_cpo_lit; /* 'cpoptions' contains 'l' flag */ -static int reg_cpo_bsl; /* 'cpoptions' contains '\' flag */ static void get_cpo_flags(void) { reg_cpo_lit = vim_strchr(p_cpo, CPO_LITERAL) != NULL; - reg_cpo_bsl = vim_strchr(p_cpo, CPO_BACKSL) != NULL; } /* @@ -1149,15 +1147,15 @@ static char_u *skip_anyof(char_u *p) if (*p != ']' && *p != NUL) mb_ptr_adv(p); } else if (*p == '\\' - && !reg_cpo_bsl && (vim_strchr(REGEXP_INRANGE, p[1]) != NULL || (!reg_cpo_lit && vim_strchr(REGEXP_ABBR, p[1]) != NULL))) p += 2; else if (*p == '[') { if (get_char_class(&p) == CLASS_NONE && get_equi_class(&p) == 0 - && get_coll_element(&p) == 0) - ++p; /* It was not a class name */ + && get_coll_element(&p) == 0 + && *p != NUL) + ++p; /* It is not a class name and not NUL */ } else ++p; } @@ -2221,7 +2219,7 @@ collection: } /* Handle \o40, \x20 and \u20AC style sequences */ - if (endc == '\\' && !reg_cpo_lit && !reg_cpo_bsl) + if (endc == '\\' && !reg_cpo_lit) endc = coll_get_char(); if (startc > endc) @@ -2244,10 +2242,8 @@ collection: * Only "\]", "\^", "\]" and "\\" are special in Vi. Vim * accepts "\t", "\e", etc., but only when the 'l' flag in * 'cpoptions' is not included. - * Posix doesn't recognize backslash at all. */ else if (*regparse == '\\' - && !reg_cpo_bsl && (vim_strchr(REGEXP_INRANGE, regparse[1]) != NULL || (!reg_cpo_lit && vim_strchr(REGEXP_ABBR, @@ -2848,7 +2844,7 @@ static int peekchr(void) /* * META contains everything that may be magic sometimes, * except ^ and $ ("\^" and "\$" are only magic after - * "\v"). We now fetch the next character and toggle its + * "\V"). We now fetch the next character and toggle its * magicness. Therefore, \ is so meta-magic that it is * not in META. */ diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index d9dc09b623..484dfe1e1f 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -270,8 +270,10 @@ typedef struct { /* When REG_MULTI is TRUE list.multi is used, otherwise list.line. */ union { struct multipos { - lpos_T start; - lpos_T end; + linenr_T start_lnum; + linenr_T end_lnum; + colnr_T start_col; + colnr_T end_col; } multi[NSUBEXP]; struct linepos { char_u *start; @@ -716,8 +718,8 @@ static int nfa_recognize_char_class(char_u *start, char_u *end, int extra_newl) */ static void nfa_emit_equi_class(int c) { -#define EMIT2(c) EMIT(c); EMIT(NFA_CONCAT); -# define EMITMBC(c) EMIT(c); EMIT(NFA_CONCAT); +#define EMIT2(c) EMIT(c); EMIT(NFA_CONCAT); +#define EMITMBC(c) EMIT(c); EMIT(NFA_CONCAT); if (enc_utf8 || STRCMP(p_enc, "latin1") == 0 || STRCMP(p_enc, "iso-8859-15") == 0) { @@ -906,10 +908,10 @@ static void nfa_emit_equi_class(int c) EMITMBC(0x10b) EMITMBC(0x10d) return; - case 'd': CASEMBC(0x10f) CASEMBC(0x111) CASEMBC(0x1d0b) - CASEMBC(0x1e11) + case 'd': CASEMBC(0x10f) CASEMBC(0x111) CASEMBC(0x1e0b) + CASEMBC(0x1e0f) CASEMBC(0x1e11) EMIT2('d'); EMITMBC(0x10f) EMITMBC(0x111) EMITMBC(0x1e0b) - EMITMBC(0x01e0f) EMITMBC(0x1e11) + EMITMBC(0x1e0f) EMITMBC(0x1e11) return; case 'e': case 0350: case 0351: case 0352: case 0353: @@ -1391,7 +1393,7 @@ static int nfa_regatom(void) * matched an unlimited number of times. NFA_NOPEN is * added only once at a position, while NFA_SPLIT is * added multiple times. This is more efficient than - * not allowsing NFA_SPLIT multiple times, it is used + * not allowing NFA_SPLIT multiple times, it is used * a lot. */ EMIT(NFA_NOPEN); break; @@ -1584,10 +1586,8 @@ collection: * Only "\]", "\^", "\]" and "\\" are special in Vi. Vim * accepts "\t", "\e", etc., but only when the 'l' flag in * 'cpoptions' is not included. - * Posix doesn't recognize backslash at all. */ if (*regparse == '\\' - && !reg_cpo_bsl && regparse + 1 <= endp && (vim_strchr(REGEXP_INRANGE, regparse[1]) != NULL || (!reg_cpo_lit @@ -3433,10 +3433,10 @@ static void log_subexpr(regsub_T *sub) if (REG_MULTI) fprintf(log_fd, "*** group %d, start: c=%d, l=%d, end: c=%d, l=%d\n", j, - sub->list.multi[j].start.col, - (int)sub->list.multi[j].start.lnum, - sub->list.multi[j].end.col, - (int)sub->list.multi[j].end.lnum); + sub->list.multi[j].start_col, + (int)sub->list.multi[j].start_lnum, + sub->list.multi[j].end_col, + (int)sub->list.multi[j].end_lnum); else { char *s = (char *)sub->list.line[j].start; char *e = (char *)sub->list.line[j].end; @@ -3537,8 +3537,10 @@ static void copy_ze_off(regsub_T *to, regsub_T *from) { if (nfa_has_zend) { if (REG_MULTI) { - if (from->list.multi[0].end.lnum >= 0) - to->list.multi[0].end = from->list.multi[0].end; + if (from->list.multi[0].end_lnum >= 0){ + to->list.multi[0].end_lnum = from->list.multi[0].end_lnum; + to->list.multi[0].end_col = from->list.multi[0].end_col; + } } else { if (from->list.line[0].end != NULL) to->list.line[0].end = from->list.line[0].end; @@ -3561,27 +3563,27 @@ static int sub_equal(regsub_T *sub1, regsub_T *sub2) if (REG_MULTI) { for (i = 0; i < todo; ++i) { if (i < sub1->in_use) - s1 = sub1->list.multi[i].start.lnum; + s1 = sub1->list.multi[i].start_lnum; else s1 = -1; if (i < sub2->in_use) - s2 = sub2->list.multi[i].start.lnum; + s2 = sub2->list.multi[i].start_lnum; else s2 = -1; if (s1 != s2) return FALSE; - if (s1 != -1 && sub1->list.multi[i].start.col - != sub2->list.multi[i].start.col) + if (s1 != -1 && sub1->list.multi[i].start_col + != sub2->list.multi[i].start_col) return FALSE; if (nfa_has_backref) { if (i < sub1->in_use) { - s1 = sub1->list.multi[i].end.lnum; + s1 = sub1->list.multi[i].end_lnum; } else { s1 = -1; } if (i < sub2->in_use) { - s2 = sub2->list.multi[i].end.lnum; + s2 = sub2->list.multi[i].end_lnum; } else { s2 = -1; } @@ -3589,7 +3591,7 @@ static int sub_equal(regsub_T *sub1, regsub_T *sub2) return FALSE; } if (s1 != -1 - && sub1->list.multi[i].end.col != sub2->list.multi[i].end.col) { + && sub1->list.multi[i].end_col != sub2->list.multi[i].end_col) { return FALSE; } } @@ -3639,7 +3641,7 @@ static void report_state(char *action, if (sub->in_use <= 0) col = -1; else if (REG_MULTI) - col = sub->list.multi[0].start.col; + col = sub->list.multi[0].start_col; else col = (int)(sub->list.line[0].start - regline); nfa_set_code(state->c); @@ -4025,22 +4027,24 @@ skip_add: * and restore it when it was in use. Otherwise fill any gap. */ if (REG_MULTI) { if (subidx < sub->in_use) { - save_lpos = sub->list.multi[subidx].start; + save_lpos.lnum = sub->list.multi[subidx].start_lnum; + save_lpos.col = sub->list.multi[subidx].start_col; save_in_use = -1; } else { save_in_use = sub->in_use; for (i = sub->in_use; i < subidx; ++i) { - sub->list.multi[i].start.lnum = -1; - sub->list.multi[i].end.lnum = -1; + sub->list.multi[i].start_lnum = -1; + sub->list.multi[i].end_lnum = -1; } sub->in_use = subidx + 1; } if (off == -1) { - sub->list.multi[subidx].start.lnum = reglnum + 1; - sub->list.multi[subidx].start.col = 0; + sub->list.multi[subidx].start_lnum = reglnum + 1; + sub->list.multi[subidx].start_col = 0; } else { - sub->list.multi[subidx].start.lnum = reglnum; - sub->list.multi[subidx].start.col = + + sub->list.multi[subidx].start_lnum = reglnum; + sub->list.multi[subidx].start_col = (colnr_T)(reginput - regline + off); } } else { @@ -4066,8 +4070,10 @@ skip_add: sub = &subs->norm; if (save_in_use == -1) { - if (REG_MULTI) - sub->list.multi[subidx].start = save_lpos; + if (REG_MULTI){ + sub->list.multi[subidx].start_lnum = save_lpos.lnum; + sub->list.multi[subidx].start_col = save_lpos.col; + } else sub->list.line[subidx].start = save_ptr; } else @@ -4076,7 +4082,7 @@ skip_add: case NFA_MCLOSE: if (nfa_has_zend && (REG_MULTI - ? subs->norm.list.multi[0].end.lnum >= 0 + ? subs->norm.list.multi[0].end_lnum >= 0 : subs->norm.list.line[0].end != NULL)) { /* Do not overwrite the position set by \ze. */ subs = addstate(l, state->out, subs, pim, off); @@ -4119,13 +4125,14 @@ skip_add: if (sub->in_use <= subidx) sub->in_use = subidx + 1; if (REG_MULTI) { - save_lpos = sub->list.multi[subidx].end; + save_lpos.lnum = sub->list.multi[subidx].end_lnum; + save_lpos.col = sub->list.multi[subidx].end_col; if (off == -1) { - sub->list.multi[subidx].end.lnum = reglnum + 1; - sub->list.multi[subidx].end.col = 0; + sub->list.multi[subidx].end_lnum = reglnum + 1; + sub->list.multi[subidx].end_col = 0; } else { - sub->list.multi[subidx].end.lnum = reglnum; - sub->list.multi[subidx].end.col = + sub->list.multi[subidx].end_lnum = reglnum; + sub->list.multi[subidx].end_col = (colnr_T)(reginput - regline + off); } /* avoid compiler warnings */ @@ -4145,8 +4152,10 @@ skip_add: else sub = &subs->norm; - if (REG_MULTI) - sub->list.multi[subidx].end = save_lpos; + if (REG_MULTI){ + sub->list.multi[subidx].end_lnum = save_lpos.lnum; + sub->list.multi[subidx].end_col = save_lpos.col; + } else sub->list.line[subidx].end = save_ptr; sub->in_use = save_in_use; @@ -4321,24 +4330,24 @@ retempty: } if (REG_MULTI) { - if (sub->list.multi[subidx].start.lnum < 0 - || sub->list.multi[subidx].end.lnum < 0) + if (sub->list.multi[subidx].start_lnum < 0 + || sub->list.multi[subidx].end_lnum < 0) goto retempty; - if (sub->list.multi[subidx].start.lnum == reglnum - && sub->list.multi[subidx].end.lnum == reglnum) { - len = sub->list.multi[subidx].end.col - - sub->list.multi[subidx].start.col; - if (cstrncmp(regline + sub->list.multi[subidx].start.col, + if (sub->list.multi[subidx].start_lnum == reglnum + && sub->list.multi[subidx].end_lnum == reglnum) { + len = sub->list.multi[subidx].end_col + - sub->list.multi[subidx].start_col; + if (cstrncmp(regline + sub->list.multi[subidx].start_col, reginput, &len) == 0) { *bytelen = len; return TRUE; } } else { if (match_with_backref( - sub->list.multi[subidx].start.lnum, - sub->list.multi[subidx].start.col, - sub->list.multi[subidx].end.lnum, - sub->list.multi[subidx].end.col, + sub->list.multi[subidx].start_lnum, + sub->list.multi[subidx].start_col, + sub->list.multi[subidx].end_lnum, + sub->list.multi[subidx].end_col, bytelen) == RA_MATCH) return TRUE; } @@ -4875,8 +4884,8 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm * it's the first MOPEN. */ if (toplevel) { if (REG_MULTI) { - m->norm.list.multi[0].start.lnum = reglnum; - m->norm.list.multi[0].start.col = (colnr_T)(reginput - regline); + m->norm.list.multi[0].start_lnum = reglnum; + m->norm.list.multi[0].start_col = (colnr_T)(reginput - regline); } else m->norm.list.line[0].start = reginput; m->norm.in_use = 1; @@ -4964,7 +4973,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm if (t->subs.norm.in_use <= 0) col = -1; else if (REG_MULTI) - col = t->subs.norm.list.multi[0].start.col; + col = t->subs.norm.list.multi[0].start_col; else col = (int)(t->subs.norm.list.line[0].start - regline); nfa_set_code(t->state->c); @@ -5216,7 +5225,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm * continue with what follows. */ if (REG_MULTI) /* TODO: multi-line match */ - bytelen = m->norm.list.multi[0].end.col + bytelen = m->norm.list.multi[0].end_col - (int)(reginput - regline); else bytelen = (int)(m->norm.list.line[0].end - reginput); @@ -6020,7 +6029,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm if (add) { if (REG_MULTI) - m->norm.list.multi[0].start.col = + m->norm.list.multi[0].start_col = (colnr_T)(reginput - regline) + clen; else m->norm.list.line[0].start = reginput + clen; @@ -6125,8 +6134,11 @@ static long nfa_regtry(nfa_regprog_T *prog, colnr_T col) cleanup_subexpr(); if (REG_MULTI) { for (i = 0; i < subs.norm.in_use; i++) { - reg_startpos[i] = subs.norm.list.multi[i].start; - reg_endpos[i] = subs.norm.list.multi[i].end; + reg_startpos[i].lnum = subs.norm.list.multi[i].start_lnum; + 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; } if (reg_startpos[0].lnum < 0) { @@ -6164,12 +6176,12 @@ static long nfa_regtry(nfa_regprog_T *prog, colnr_T col) struct multipos *mpos = &subs.synt.list.multi[i]; // Only accept single line matches that are valid. - if (mpos->start.lnum >= 0 - && mpos->start.lnum == mpos->end.lnum - && mpos->end.col >= mpos->start.col) { + if (mpos->start_lnum >= 0 + && mpos->start_lnum == mpos->end_lnum + && mpos->end_col >= mpos->start_col) { re_extmatch_out->matches[i] = - vim_strnsave(reg_getline(mpos->start.lnum) + mpos->start.col, - mpos->end.col - mpos->start.col); + vim_strnsave(reg_getline(mpos->start_lnum) + mpos->start_col, + mpos->end_col - mpos->start_col); } } else { struct linepos *lpos = &subs.synt.list.line[i]; diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 43cb6f4878..d57b84ad50 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -1951,6 +1951,28 @@ static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T } } + // Show colorcolumn in the fold line, but let cursorcolumn override it. + if (wp->w_p_cc_cols) { + int i = 0; + int j = wp->w_p_cc_cols[i]; + int old_txtcol = txtcol; + + while (j > -1) { + txtcol += j; + if (wp->w_p_wrap) { + txtcol -= wp->w_skipcol; + } else { + txtcol -= wp->w_leftcol; + } + if (txtcol >= 0 && txtcol < wp->w_width) { + ScreenAttrs[off + txtcol] = + hl_combine_attr(ScreenAttrs[off + txtcol], hl_attr(HLF_MC)); + } + txtcol = old_txtcol; + j = wp->w_p_cc_cols[++i]; + } + } + /* Show 'cursorcolumn' in the fold line. */ if (wp->w_p_cuc) { txtcol += wp->w_virtcol; diff --git a/src/nvim/search.c b/src/nvim/search.c index e10504973b..f91ac3bb9c 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -2199,7 +2199,6 @@ findpar ( linenr_T curr; bool did_skip; /* true after separating lines have been skipped */ bool first; /* true on first line */ - int posix = (vim_strchr(p_cpo, CPO_PARA) != NULL); linenr_T fold_first; /* first line of a closed fold */ linenr_T fold_last; /* last line of a closed fold */ bool fold_skipped; /* true if a closed fold was skipped this @@ -2220,12 +2219,7 @@ findpar ( fold_skipped = true; } - /* POSIX has it's own ideas of what a paragraph boundary is and it - * doesn't match historical Vi: It also stops at a "{" in the - * first column and at an empty line. */ - if (!first && did_skip && (startPS(curr, what, both) - || (posix && what == NUL && *ml_get(curr) == - '{'))) + if (!first && did_skip && startPS(curr, what, both)) break; if (fold_skipped) diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 63fc7b80a7..f9ed6faff9 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -331,7 +331,7 @@ #include "nvim/os/os.h" #include "nvim/os/input.h" -#ifndef UNIX // it's in os_unix_defs.h for Unix +#ifndef UNIX // it's in os/unix_defs.h for Unix # include <time.h> // for time_t #endif @@ -2328,8 +2328,16 @@ static void spell_load_lang(char_u *lang) } if (r == FAIL) { - smsg(_("Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\""), - lang, spell_enc(), lang); + if (starting) { + // Some startup file sets &spell, but the necessary files don't exist: + // try to prompt the user at VimEnter. Also set spell again. #3027 + do_cmdline_cmd( + "autocmd VimEnter * call spellfile#LoadFile(&spelllang)|set spell"); + } else { + smsg( + _("Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\""), + lang, spell_enc(), lang); + } } else if (sl.sl_slang != NULL) { // At least one file was loaded, now load ALL the additions. STRCPY(fname_enc + STRLEN(fname_enc) - 3, "add.spl"); @@ -4116,7 +4124,7 @@ static int badword_captype(char_u *word, char_u *end) // Delete the internal wordlist and its .spl file. void spell_delete_wordlist(void) { - char_u fname[MAXPATHL]; + char_u fname[MAXPATHL] = {0}; if (int_wordlist != NULL) { os_remove((char *)int_wordlist); diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 42d0421f0c..b9bc4c6d78 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -67,7 +67,9 @@ #include "nvim/ex_cmds.h" #include "nvim/window.h" #include "nvim/fileio.h" -#include "nvim/os/event.h" +#include "nvim/event/loop.h" +#include "nvim/event/time.h" +#include "nvim/os/input.h" #include "nvim/api/private/helpers.h" #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -80,7 +82,7 @@ // of data. #define REFRESH_DELAY 10 -static uv_timer_t refresh_timer; +static TimeWatcher refresh_timer; static bool refresh_pending = false; typedef struct { @@ -150,7 +152,7 @@ static VTermColor default_vt_bg_rgb; void terminal_init(void) { invalidated_terminals = pmap_new(ptr_t)(); - uv_timer_init(uv_default_loop(), &refresh_timer); + time_watcher_init(&loop, &refresh_timer, NULL); // initialize a rgb->color index map for cterm attributes(VTermScreenCell // only has RGB information and we need color indexes for terminal UIs) @@ -175,8 +177,8 @@ void terminal_init(void) void terminal_teardown(void) { - uv_timer_stop(&refresh_timer); - uv_close((uv_handle_t *)&refresh_timer, NULL); + time_watcher_stop(&refresh_timer); + time_watcher_close(&refresh_timer, NULL); pmap_free(ptr_t)(invalidated_terminals); map_free(int, int)(color_indexes); } @@ -323,7 +325,7 @@ void terminal_resize(Terminal *term, uint16_t width, uint16_t height) invalidate_terminal(term, -1, -1); } -void terminal_enter(bool process_deferred) +void terminal_enter(void) { Terminal *term = curbuf->terminal; assert(term && "should only be called when curbuf has a terminal"); @@ -352,15 +354,9 @@ void terminal_enter(bool process_deferred) bool got_bs = false; // True if the last input was <C-\> while (term->buf == curbuf) { - if (process_deferred) { - event_enable_deferred(); - } - + input_enable_events(); c = safe_vgetc(); - - if (process_deferred) { - event_disable_deferred(); - } + input_disable_events(); switch (c) { case K_LEFTMOUSE: @@ -380,7 +376,7 @@ void terminal_enter(bool process_deferred) break; case K_EVENT: - event_process(); + queue_process_events(loop.events); break; case Ctrl_N: @@ -426,7 +422,13 @@ void terminal_destroy(Terminal *term) term->buf->terminal = NULL; } term->buf = NULL; - pmap_del(ptr_t)(invalidated_terminals, term); + if (pmap_has(ptr_t)(invalidated_terminals, term)) { + // flush any pending changes to the buffer + block_autocmds(); + refresh_terminal(term); + unblock_autocmds(); + pmap_del(ptr_t)(invalidated_terminals, term); + } for (size_t i = 0 ; i < term->sb_current; i++) { xfree(term->sb_buffer[i]); } @@ -572,9 +574,10 @@ static int term_settermprop(VTermProp prop, VTermValue *val, void *data) case VTERM_PROP_TITLE: { Error err; - dict_set_value(term->buf->b_vars, - cstr_as_string("term_title"), - STRING_OBJ(cstr_as_string(val->string)), &err); + api_free_object(dict_set_value(term->buf->b_vars, + cstr_as_string("term_title"), + STRING_OBJ(cstr_as_string(val->string)), + &err)); break; } @@ -876,53 +879,52 @@ static void invalidate_terminal(Terminal *term, int start_row, int end_row) pmap_put(ptr_t)(invalidated_terminals, term, NULL); if (!refresh_pending) { - uv_timer_start(&refresh_timer, refresh_timer_cb, REFRESH_DELAY, 0); + time_watcher_start(&refresh_timer, refresh_timer_cb, REFRESH_DELAY, 0); refresh_pending = true; } } -// libuv timer callback. This will enqueue on_refresh to be processed as an -// event. -static void refresh_timer_cb(uv_timer_t *handle) +static void refresh_terminal(Terminal *term) { - event_push((Event) {.handler = on_refresh}, false); - refresh_pending = false; + // TODO(SplinterOfChaos): Find the condition that makes term->buf invalid. + bool valid = true; + if (!term->buf || !(valid = buf_valid(term->buf))) { + // destroyed by `close_buffer`. Dont do anything else + if (!valid) { + term->buf = NULL; + } + return; + } + bool pending_resize = term->pending_resize; + WITH_BUFFER(term->buf, { + refresh_size(term); + refresh_scrollback(term); + refresh_screen(term); + redraw_buf_later(term->buf, NOT_VALID); + }); + adjust_topline(term, pending_resize); } - -// Refresh all invalidated terminals -static void on_refresh(Event event) +// libuv timer callback. This will enqueue on_refresh to be processed as an +// event. +static void refresh_timer_cb(TimeWatcher *watcher, void *data) { if (exiting) { // bad things can happen if we redraw when exiting, and there's no need to // update the buffer. - return; + goto end; } Terminal *term; void *stub; (void)(stub); // don't process autocommands while updating terminal buffers block_autocmds(); map_foreach(invalidated_terminals, term, stub, { - // TODO(SplinterOfChaos): Find the condition that makes term->buf invalid. - bool valid = true; - if (!term->buf || !(valid = buf_valid(term->buf))) { - // destroyed by `close_buffer`. Dont do anything else - if (!valid) { - term->buf = NULL; - } - continue; - } - bool pending_resize = term->pending_resize; - WITH_BUFFER(term->buf, { - refresh_size(term); - refresh_scrollback(term); - refresh_screen(term); - redraw_buf_later(term->buf, NOT_VALID); - }); - adjust_topline(term, pending_resize); + refresh_terminal(term); }); pmap_clear(ptr_t)(invalidated_terminals); unblock_autocmds(); redraw(true); +end: + refresh_pending = false; } static void refresh_size(Terminal *term) diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index da99c6d1c4..c97ffc2ced 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -12,19 +12,19 @@ SCRIPTS := test_eval.out \ test11.out test12.out test13.out test14.out \ test17.out \ test24.out \ - test26.out test27.out test30.out \ + test30.out \ test32.out test34.out \ test36.out test37.out test39.out test40.out \ - test42.out test43.out test45.out \ - test46.out test47.out test48.out test49.out \ + test42.out test45.out \ + test47.out test48.out test49.out \ test52.out test53.out test55.out \ - test57.out test58.out test59.out \ - test62.out test63.out test64.out \ + test58.out test59.out \ + test64.out \ test68.out test69.out \ - test71.out test73.out \ - test79.out test80.out \ + test73.out \ + test79.out \ test83.out \ - test86.out test87.out test88.out \ + test88.out \ test_listlbr.out \ test_breakindent.out \ test_close_count.out \ @@ -144,7 +144,5 @@ test1.out: .gdbinit test1.in test49.out: test49.vim -test60.out: test60.vim - nolog: -rm -f test.log diff --git a/src/nvim/testdir/test57.in b/src/nvim/testdir/test57.in deleted file mode 100644 index 8d972e4a68..0000000000 --- a/src/nvim/testdir/test57.in +++ /dev/null @@ -1,500 +0,0 @@ -Tests for :sort command. vim: set ft=vim : - -STARTTEST -:so small.vim -:" -:/^t01:/+1,/^t02/-1sort -:/^t02:/+1,/^t03/-1sort n -:/^t03:/+1,/^t04/-1sort x -:/^t04:/+1,/^t05/-1sort u -:/^t05:/+1,/^t06/-1sort! -:/^t06:/+1,/^t07/-1sort! n -:/^t07:/+1,/^t08/-1sort! u -:/^t08:/+1,/^t09/-1sort o -:/^t09:/+1,/^t10/-1sort! x -:/^t10:/+1,/^t11/-1sort/./ -:/^t11:/+1,/^t12/-1sort/../ -:/^t12:/+1,/^t13/-1sort/../u -:/^t13:/+1,/^t14/-1sort/./n -:/^t14:/+1,/^t15/-1sort/./r -:/^t15:/+1,/^t16/-1sort/../r -:/^t16:/+1,/^t17/-1sort/./rn -:/^t17:/+1,/^t18/-1sort/\d/ -:/^t18:/+1,/^t19/-1sort/\d/r -:/^t19:/+1,/^t20/-1sort/\d/n -:/^t20:/+1,/^t21/-1sort/\d/rn -:/^t21:/+1,/^t22/-1sort/\d\d/ -:/^t22:/+1,/^t23/-1sort/\d\d/n -:/^t23:/+1,/^t24/-1sort/\d\d/x -:/^t24:/+1,/^t25/-1sort/\d\d/r -:/^t25:/+1,/^t26/-1sort/\d\d/rn -:/^t26:/+1,/^t27/-1sort/\d\d/rx -:/^t27:/+1,/^t28/-1sort no -:/^t01:/,$wq! test.out -ENDTEST - -t01: alphebetical -abc -ab -a -a321 -a123 -a122 -b321 -b123 -c123d - 123b -c321d -b322b -b321 -b321b - - -t02: numeric -abc -ab -a321 -a123 -a122 -a -x-22 -b321 -b123 - -c123d --24 - 123b -c321d -0 -b322b -b321 -b321b - - -t03: hexadecimal -abc -ab -a -a321 -a123 -a122 -b321 -b123 -c123d - 123b -c321d -b322b -b321 -b321b - - -t04: alpha, unique -abc -ab -a -a321 -a123 -a122 -b321 -b123 -c123d - 123b -c321d -b322b -b321 -b321b - - -t05: alpha, reverse -abc -ab -a -a321 -a123 -a122 -b321 -b123 -c123d - 123b -c321d -b322b -b321 -b321b - - -t06: numeric, reverse -abc -ab -a -a321 -a123 -a122 -b321 -b123 -c123d - 123b -c321d -b322b -b321 -b321b - - -t07: unique, reverse -abc -ab -a -a321 -a123 -a122 -b321 -b123 -c123d - 123b -c321d -b322b -b321 -b321b - - -t08: octal -abc -ab -a -a321 -a123 -a122 -b321 -b123 -c123d - 123b -c321d -b322b -b321 -b321b - - -t09: reverse, hexadecimal -abc -ab -a -a321 -a123 -a122 -b321 -b123 -c123d - 123b -c321d -b322b -b321 -b321b - - -t10: alpha, skip first character -abc -ab -a -a321 -a123 -a122 -b321 -b123 -c123d - 123b -c321d -b322b -b321 -b321b - - -t11: alpha, skip first 2 characters -abc -ab -a -a321 -a123 -a122 -b321 -b123 -c123d - 123b -c321d -b322b -b321 -b321b - - -t12: alpha, unique, skip first 2 characters -abc -ab -a -a321 -a123 -a122 -b321 -b123 -c123d - 123b -c321d -b322b -b321 -b321b - - -t13: numeric, skip first character -abc -ab -a -a321 -a123 -a122 -b321 -b123 -c123d - 123b -c321d -b322b -b321 -b321b - - -t14: alpha, sort on first character -abc -ab -a -a321 -a123 -a122 -b321 -b123 -c123d - 123b -c321d -b322b -b321 -b321b - - -t15: alpha, sort on first 2 characters -abc -ab -a -a321 -a123 -a122 -b321 -b123 -c123d - 123b -c321d -b322b -b321 -b321b - - -t16: numeric, sort on first character -abc -ab -a -a321 -a123 -a122 -b321 -b123 -c123d - 123b -c321d -b322b -b321 -b321b - - -t17: alpha, skip past first digit -abc -ab -a -a321 -a123 -a122 -b321 -b123 -c123d - 123b -c321d -b322b -b321 -b321b - - -t18: alpha, sort on first digit -abc -ab -a -a321 -a123 -a122 -b321 -b123 -c123d - 123b -c321d -b322b -b321 -b321b - - -t19: numeric, skip past first digit -abc -ab -a -a321 -a123 -a122 -b321 -b123 -c123d - 123b -c321d -b322b -b321 -b321b - - -t20: numeric, sort on first digit -abc -ab -a -a321 -a123 -a122 -b321 -b123 -c123d - 123b -c321d -b322b -b321 -b321b - - -t21: alpha, skip past first 2 digits -abc -ab -a -a321 -a123 -a122 -b321 -b123 -c123d - 123b -c321d -b322b -b321 -b321b - - -t22: numeric, skip past first 2 digits -abc -ab -a -a321 -a123 -a122 -b321 -b123 -c123d - 123b -c321d -b322b -b321 -b321b - - -t23: hexadecimal, skip past first 2 digits -abc -ab -a -a321 -a123 -a122 -b321 -b123 -c123d - 123b -c321d -b322b -b321 -b321b - - -t24: alpha, sort on first 2 digits -abc -ab -a -a321 -a123 -a122 -b321 -b123 -c123d - 123b -c321d -b322b -b321 -b321b - - -t25: numeric, sort on first 2 digits -abc -ab -a -a321 -a123 -a122 -b321 -b123 -c123d - 123b -c321d -b322b -b321 -b321b - - -t26: hexadecimal, sort on first 2 digits -abc -ab -a -a321 -a123 -a122 -b321 -b123 -c123d - 123b -c321d -b322b -b321 -b321b - - -t27: wrong arguments -abc -ab -a -a321 -a123 -a122 -b321 -b123 -c123d - 123b -c321d -b322b -b321 -b321b - - -t28: done - diff --git a/src/nvim/testdir/test57.ok b/src/nvim/testdir/test57.ok deleted file mode 100644 index aa3d373183..0000000000 --- a/src/nvim/testdir/test57.ok +++ /dev/null @@ -1,459 +0,0 @@ -t01: alphebetical - - - 123b -a -a122 -a123 -a321 -ab -abc -b123 -b321 -b321 -b321b -b322b -c123d -c321d -t02: numeric -abc -ab -a - - - --24 -x-22 -0 -a122 -a123 -b123 -c123d - 123b -a321 -b321 -c321d -b321 -b321b -b322b -t03: hexadecimal - - -a -ab -abc - 123b -a122 -a123 -a321 -b123 -b321 -b321 -b321b -b322b -c123d -c321d -t04: alpha, unique - - 123b -a -a122 -a123 -a321 -ab -abc -b123 -b321 -b321b -b322b -c123d -c321d -t05: alpha, reverse -c321d -c123d -b322b -b321b -b321 -b321 -b123 -abc -ab -a321 -a123 -a122 -a - 123b - - -t06: numeric, reverse -b322b -b321b -b321 -c321d -b321 -a321 - 123b -c123d -b123 -a123 -a122 - - -a -ab -abc -t07: unique, reverse -c321d -c123d -b322b -b321b -b321 -b123 -abc -ab -a321 -a123 -a122 -a - 123b - -t08: octal -abc -ab -a - - -a122 -a123 -b123 -c123d - 123b -a321 -b321 -c321d -b321 -b321b -b322b -t09: reverse, hexadecimal -c321d -c123d -b322b -b321b -b321 -b321 -b123 -a321 -a123 -a122 - 123b -abc -ab -a - - -t10: alpha, skip first character -a - - -a122 -a123 -b123 - 123b -c123d -a321 -b321 -b321 -b321b -c321d -b322b -ab -abc -t11: alpha, skip first 2 characters -ab -a - - -a321 -b321 -b321 -b321b -c321d -a122 -b322b -a123 -b123 - 123b -c123d -abc -t12: alpha, unique, skip first 2 characters -ab -a - -a321 -b321 -b321b -c321d -a122 -b322b -a123 -b123 - 123b -c123d -abc -t13: numeric, skip first character -abc -ab -a - - -a122 -a123 -b123 -c123d - 123b -a321 -b321 -c321d -b321 -b321b -b322b -t14: alpha, sort on first character - - - 123b -abc -ab -a -a321 -a123 -a122 -b321 -b123 -b322b -b321 -b321b -c123d -c321d -t15: alpha, sort on first 2 characters -a - - - 123b -a123 -a122 -a321 -abc -ab -b123 -b321 -b322b -b321 -b321b -c123d -c321d -t16: numeric, sort on first character -abc -ab -a -a321 -a123 -a122 -b321 -b123 -c123d - 123b -c321d -b322b -b321 -b321b - - -t17: alpha, skip past first digit -abc -ab -a - - -a321 -b321 -b321 -b321b -c321d -a122 -b322b -a123 -b123 - 123b -c123d -t18: alpha, sort on first digit -abc -ab -a - - -a123 -a122 -b123 -c123d - 123b -a321 -b321 -c321d -b322b -b321 -b321b -t19: numeric, skip past first digit -abc -ab -a - - -a321 -b321 -c321d -b321 -b321b -a122 -b322b -a123 -b123 -c123d - 123b -t20: numeric, sort on first digit -abc -ab -a - - -a123 -a122 -b123 -c123d - 123b -a321 -b321 -c321d -b322b -b321 -b321b -t21: alpha, skip past first 2 digits -abc -ab -a - - -a321 -b321 -b321 -b321b -c321d -a122 -b322b -a123 -b123 - 123b -c123d -t22: numeric, skip past first 2 digits -abc -ab -a - - -a321 -b321 -c321d -b321 -b321b -a122 -b322b -a123 -b123 -c123d - 123b -t23: hexadecimal, skip past first 2 digits -abc -ab -a - - -a321 -b321 -b321 -a122 -a123 -b123 -b321b -c321d -b322b - 123b -c123d -t24: alpha, sort on first 2 digits -abc -ab -a - - -a123 -a122 -b123 -c123d - 123b -a321 -b321 -c321d -b322b -b321 -b321b -t25: numeric, sort on first 2 digits -abc -ab -a - - -a123 -a122 -b123 -c123d - 123b -a321 -b321 -c321d -b322b -b321 -b321b -t26: hexadecimal, sort on first 2 digits -abc -ab -a - - -a123 -a122 -b123 -c123d - 123b -a321 -b321 -c321d -b322b -b321 -b321b -t27: wrong arguments -abc -ab -a -a321 -a123 -a122 -b321 -b123 -c123d - 123b -c321d -b322b -b321 -b321b - - -t28: done - diff --git a/src/nvim/testdir/test62.in b/src/nvim/testdir/test62.in deleted file mode 100644 index c201fe7137..0000000000 --- a/src/nvim/testdir/test62.in +++ /dev/null @@ -1,191 +0,0 @@ -Tests for tab pages - -STARTTEST -:so small.vim -:lang C -:" Simple test for opening and closing a tab page -:tabnew -:let nr = tabpagenr() -:q -:call append(line('$'), 'tab page ' . nr) -:unlet nr -:" -:" Open three tab pages and use ":tabdo" -:0tabnew -:1tabnew -:$tabnew -:tabdo call append(line('$'), 'this is tab page ' . tabpagenr()) -:tabclose! 2 -:tabrewind -:let line1 = getline('$') -:undo -:q -:tablast -:let line2 = getline('$') -:q! -:call append(line('$'), line1) -:call append(line('$'), line2) -:unlet line1 line2 -:" -:" Test for settabvar() and gettabvar() functions. Open a new tab page and -:" set 3 variables to a number, string and a list. Verify that the variables -:" are correctly set. -:tabnew -:tabfirst -:call settabvar(2, 'val_num', 100) -:call settabvar(2, 'val_str', 'SetTabVar test') -:call settabvar(2, 'val_list', ['red', 'blue', 'green']) -:" -:let test_status = 'gettabvar: fail' -:if gettabvar(2, 'val_num') == 100 && gettabvar(2, 'val_str') == 'SetTabVar test' && gettabvar(2, 'val_list') == ['red', 'blue', 'green'] -: let test_status = 'gettabvar: pass' -:endif -:call append(line('$'), test_status) -:" -:tabnext 2 -:let test_status = 'settabvar: fail' -:if t:val_num == 100 && t:val_str == 'SetTabVar test' && t:val_list == ['red', 'blue', 'green'] -: let test_status = 'settabvar: pass' -:endif -:tabclose -:call append(line('$'), test_status) -:" -:if has('gui') || has('clientserver') -:" Test for ":tab drop exist-file" to keep current window. -:sp test1 -:tab drop test1 -:let test_status = 'tab drop 1: fail' -:if tabpagenr('$') == 1 && winnr('$') == 2 && winnr() == 1 -: let test_status = 'tab drop 1: pass' -:endif -:close -:call append(line('$'), test_status) -:" -:" -:" Test for ":tab drop new-file" to keep current window of tabpage 1. -:split -:tab drop newfile -:let test_status = 'tab drop 2: fail' -:if tabpagenr('$') == 2 && tabpagewinnr(1, '$') == 2 && tabpagewinnr(1) == 1 -: let test_status = 'tab drop 2: pass' -:endif -:tabclose -:q -:call append(line('$'), test_status) -:" -:" -:" Test for ":tab drop multi-opend-file" to keep current tabpage and window. -:new test1 -:tabnew -:new test1 -:tab drop test1 -:let test_status = 'tab drop 3: fail' -:if tabpagenr() == 2 && tabpagewinnr(2, '$') == 2 && tabpagewinnr(2) == 1 -: let test_status = 'tab drop 3: pass' -:endif -:tabclose -:q -:call append(line('$'), test_status) -:else -:" :drop not supported -:call append(line('$'), 'tab drop 1: pass') -:call append(line('$'), 'tab drop 2: pass') -:call append(line('$'), 'tab drop 3: pass') -:endif -:" -:" -:for i in range(9) | tabnew | endfor -1gt -Go=tabpagenr()
-:tabmove 5 -i=tabpagenr()
-:tabmove -2 -i=tabpagenr()
-:tabmove +4 -i=tabpagenr()
-:tabmove -i=tabpagenr()
-:tabmove -20 -i=tabpagenr()
-:tabmove +20 -i=tabpagenr()
-:3tabmove -i=tabpagenr()
-:7tabmove 5 -i=tabpagenr()
-:let a='No error caught.' -:try -:tabmove foo -:catch E474 -:let a='E474 caught.' -:endtry -i=a
-:" -:" Test autocommands -:tabonly! -:let g:r=[] -:command -nargs=1 -bar C :call add(g:r, '=== ' . <q-args> . ' ===')|<args> -:function Test() - let hasau=has('autocmd') - if hasau - autocmd TabEnter * :call add(g:r, 'TabEnter') - autocmd WinEnter * :call add(g:r, 'WinEnter') - autocmd BufEnter * :call add(g:r, 'BufEnter') - autocmd TabLeave * :call add(g:r, 'TabLeave') - autocmd WinLeave * :call add(g:r, 'WinLeave') - autocmd BufLeave * :call add(g:r, 'BufLeave') - endif - let t:a='a' - C tab split - if !hasau - let g:r+=['WinLeave', 'TabLeave', 'WinEnter', 'TabEnter'] - endif - let t:a='b' - C tabnew - if !hasau - let g:r+=['WinLeave', 'TabLeave', 'WinEnter', 'TabEnter', 'BufLeave', 'BufEnter'] - endif - let t:a='c' - call add(g:r, join(map(range(1, tabpagenr('$')), 'gettabvar(v:val, "a")'))) - C call map(range(1, tabpagenr('$')), 'settabvar(v:val, ''a'', v:val*2)') - call add(g:r, join(map(range(1, tabpagenr('$')), 'gettabvar(v:val, "a")'))) - let w:a='a' - C vsplit - if !hasau - let g:r+=['WinLeave', 'WinEnter'] - endif - let w:a='a' - let tabn=tabpagenr() - let winr=range(1, winnr('$')) - C tabnext 1 - if !hasau - let g:r+=['BufLeave', 'WinLeave', 'TabLeave', 'WinEnter', 'TabEnter', 'BufEnter'] - endif - call add(g:r, join(map(copy(winr), 'gettabwinvar('.tabn.', v:val, "a")'))) - C call map(copy(winr), 'settabwinvar('.tabn.', v:val, ''a'', v:val*2)') - call add(g:r, join(map(copy(winr), 'gettabwinvar('.tabn.', v:val, "a")'))) - if hasau - augroup TabDestructive - autocmd TabEnter * :C tabnext 2 | C tabclose 3 - augroup END - C tabnext 3 - let g:r+=[tabpagenr().'/'.tabpagenr('$')] - autocmd! TabDestructive TabEnter - C tabnew - C tabnext 1 - autocmd TabDestructive TabEnter * nested :C tabnext 2 | C tabclose 3 - C tabnext 3 - let g:r+=[tabpagenr().'/'.tabpagenr('$')] - else - let g:r+=["=== tabnext 3 ===","BufLeave","WinLeave","TabLeave","WinEnter","TabEnter","=== tabnext 2 ===","=== tabclose 3 ===","2/2","=== tabnew ===","WinLeave","TabLeave","WinEnter","TabEnter","BufLeave","BufEnter","=== tabnext 1 ===","BufLeave","WinLeave","TabLeave","WinEnter","TabEnter","BufEnter","=== tabnext 3 ===","BufLeave","WinLeave","TabLeave","WinEnter","TabEnter","=== tabnext 2 ===","BufLeave","WinLeave","TabLeave","WinEnter","TabEnter","=== tabnext 2 ===","=== tabclose 3 ===","BufEnter","=== tabclose 3 ===","2/2",] - endif -endfunction -:call Test() -:$ put =g:r -:" -:" -:/^Results/,$w! test.out -:qa! -ENDTEST - -Results: diff --git a/src/nvim/testdir/test62.ok b/src/nvim/testdir/test62.ok deleted file mode 100644 index e35b2b1c67..0000000000 --- a/src/nvim/testdir/test62.ok +++ /dev/null @@ -1,88 +0,0 @@ -Results: -tab page 2 -this is tab page 3 -this is tab page 1 -this is tab page 4 -gettabvar: pass -settabvar: pass -tab drop 1: pass -tab drop 2: pass -tab drop 3: pass -1 -6 -4 -8 -10 -1 -10 -4 -6 -E474 caught. -=== tab split === -WinLeave -TabLeave -WinEnter -TabEnter -=== tabnew === -WinLeave -TabLeave -WinEnter -TabEnter -BufLeave -BufEnter -a b c -=== call map(range(1, tabpagenr('$')), 'settabvar(v:val, ''a'', v:val*2)') === -2 4 6 -=== vsplit === -WinLeave -WinEnter -=== tabnext 1 === -BufLeave -WinLeave -TabLeave -WinEnter -TabEnter -BufEnter -a a -=== call map(copy(winr), 'settabwinvar('.tabn.', v:val, ''a'', v:val*2)') === -2 4 -=== tabnext 3 === -BufLeave -WinLeave -TabLeave -WinEnter -TabEnter -=== tabnext 2 === -=== tabclose 3 === -2/2 -=== tabnew === -WinLeave -TabLeave -WinEnter -TabEnter -BufLeave -BufEnter -=== tabnext 1 === -BufLeave -WinLeave -TabLeave -WinEnter -TabEnter -BufEnter -=== tabnext 3 === -BufLeave -WinLeave -TabLeave -WinEnter -TabEnter -=== tabnext 2 === -BufLeave -WinLeave -TabLeave -WinEnter -TabEnter -=== tabnext 2 === -=== tabclose 3 === -BufEnter -=== tabclose 3 === -2/2 diff --git a/src/nvim/testdir/test79.in b/src/nvim/testdir/test79.in Binary files differindex 8278bd8000..afbf2083d2 100644 --- a/src/nvim/testdir/test79.in +++ b/src/nvim/testdir/test79.in diff --git a/src/nvim/testdir/test79.ok b/src/nvim/testdir/test79.ok Binary files differindex e22eee0b71..d4e0ae8819 100644 --- a/src/nvim/testdir/test79.ok +++ b/src/nvim/testdir/test79.ok diff --git a/src/nvim/testdir/test80.in b/src/nvim/testdir/test80.in deleted file mode 100644 index 406fb6dac7..0000000000 --- a/src/nvim/testdir/test80.in +++ /dev/null @@ -1,201 +0,0 @@ -Test for *sub-replace-special* and *sub-replace-expression* on substitue(). -Test for submatch() on substitue(). -Test for *:s%* on :substitute. - -STARTTEST -:so small.vim -ENDTEST - -TEST_1: - -STARTTEST -:set magic -:set cpo& -:$put =\"\n\nTEST_1:\" -:$put =substitute('A', 'A', '&&', '') -:$put =substitute('B', 'B', '\&', '') -:$put =substitute('C123456789', 'C\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)', '\0\9\8\7\6\5\4\3\2\1', '') -:$put =substitute('D', 'D', 'd', '') -:$put =substitute('E', 'E', '~', '') -:$put =substitute('F', 'F', '\~', '') -:$put =substitute('G', 'G', '\ugg', '') -:$put =substitute('H', 'H', '\Uh\Eh', '') -:$put =substitute('I', 'I', '\lII', '') -:$put =substitute('J', 'J', '\LJ\EJ', '') -:$put =substitute('K', 'K', '\Uk\ek', '') -:$put =substitute('lLl', 'L', '
', '') -:$put =substitute('mMm', 'M', '\r', '') -:$put =substitute('nNn', 'N', '\
', '') -:$put =substitute('oOo', 'O', '\n', '') -:$put =substitute('pPp', 'P', '\b', '') -:$put =substitute('qQq', 'Q', '\t', '') -:$put =substitute('rRr', 'R', '\\', '') -:$put =substitute('sSs', 'S', '\c', '') -:$put =substitute('uUu', 'U', \"\n\", '') -:$put =substitute('vVv', 'V', \"\b\", '') -:$put =substitute('wWw', 'W', \"\\\", '') -:$put =substitute('xXx', 'X', \"\r\", '') -:$put =substitute('Y', 'Y', '\L\uyYy\l\EY', '') -:$put =substitute('Z', 'Z', '\U\lZzZ\u\Ez', '') -/^TEST_2 -ENDTEST - -TEST_2: - -STARTTEST -:set nomagic -:set cpo& -:$put =\"\n\nTEST_2:\" -:$put =substitute('A', 'A', '&&', '') -:$put =substitute('B', 'B', '\&', '') -:$put =substitute('C123456789', 'C\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)', '\0\9\8\7\6\5\4\3\2\1', '') -:$put =substitute('D', 'D', 'd', '') -:$put =substitute('E', 'E', '~', '') -:$put =substitute('F', 'F', '\~', '') -:$put =substitute('G', 'G', '\ugg', '') -:$put =substitute('H', 'H', '\Uh\Eh', '') -:$put =substitute('I', 'I', '\lII', '') -:$put =substitute('J', 'J', '\LJ\EJ', '') -:$put =substitute('K', 'K', '\Uk\ek', '') -:$put =substitute('lLl', 'L', '
', '') -:$put =substitute('mMm', 'M', '\r', '') -:$put =substitute('nNn', 'N', '\
', '') -:$put =substitute('oOo', 'O', '\n', '') -:$put =substitute('pPp', 'P', '\b', '') -:$put =substitute('qQq', 'Q', '\t', '') -:$put =substitute('rRr', 'R', '\\', '') -:$put =substitute('sSs', 'S', '\c', '') -:$put =substitute('tTt', 'T', \"\r\", '') -:$put =substitute('uUu', 'U', \"\n\", '') -:$put =substitute('vVv', 'V', \"\b\", '') -:$put =substitute('wWw', 'W', \"\\\", '') -:$put =substitute('X', 'X', '\L\uxXx\l\EX', '') -:$put =substitute('Y', 'Y', '\U\lYyY\u\Ey', '') -/^TEST_3 -ENDTEST - -TEST_3: - -STARTTEST -:set magic& -:set cpo& -:$put =\"\n\nTEST_3:\" -:let y = substitute('aAa', 'A', '\="\\"', '') | $put =y -:let y = substitute('bBb', 'B', '\="\\\\"', '') | $put =y -:let y = substitute('cCc', 'C', '\="
"', '') | $put =y -:let y = substitute('dDd', 'D', '\="\\
"', '') | $put =y -:let y = substitute('eEe', 'E', '\="\\\\
"', '') | $put =y -:let y = substitute('fFf', 'F', '\="\\r"', '') | $put =y -:let y = substitute('jJj', 'J', '\="\\n"', '') | $put =y -:let y = substitute('kKk', 'K', '\="\r"', '') | $put =y -:let y = substitute('lLl', 'L', '\="\n"', '') | $put =y -/^TEST_4 -ENDTEST - -TEST_4: - -STARTTEST -:set magic& -:set cpo& -:$put =\"\n\nTEST_4:\" -:let y = substitute('aAa', 'A', '\=substitute(submatch(0), ".", "\\", "")', '') | $put =y -:let y = substitute('bBb', 'B', '\=substitute(submatch(0), ".", "\\\\", "")', '') | $put =y -:let y = substitute('cCc', 'C', '\=substitute(submatch(0), ".", "
", "")', '') | $put =y -:let y = substitute('dDd', 'D', '\=substitute(submatch(0), ".", "\\
", "")', '') | $put =y -:let y = substitute('eEe', 'E', '\=substitute(submatch(0), ".", "\\\\
", "")', '') | $put =y -:let y = substitute('fFf', 'F', '\=substitute(submatch(0), ".", "\\r", "")', '') | $put =y -:let y = substitute('jJj', 'J', '\=substitute(submatch(0), ".", "\\n", "")', '') | $put =y -:let y = substitute('kKk', 'K', '\=substitute(submatch(0), ".", "\r", "")', '') | $put =y -:let y = substitute('lLl', 'L', '\=substitute(submatch(0), ".", "\n", "")', '') | $put =y -/^TEST_5 -ENDTEST - -TEST_5: - -STARTTEST -:set magic& -:set cpo& -:$put =\"\n\nTEST_5:\" -:$put =substitute('A123456789', 'A\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)', '\=submatch(0) . submatch(9) . submatch(8) . submatch(7) . submatch(6) . submatch(5) . submatch(4) . submatch(3) . submatch(2) . submatch(1)', '') -:$put =substitute('A123456789', 'A\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)', '\=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)])', '') -/^TEST_6 -ENDTEST - -TEST_6: - -STARTTEST -:set magic& -:$put =\"\n\nTEST_6:\" -:set cpo+=/ -:$put =substitute('A', 'A', 'a', '') -:$put =substitute('B', 'B', '%', '') -:set cpo-=/ -:$put =substitute('C', 'C', 'c', '') -:$put =substitute('D', 'D', '%', '') -/^TEST_7 -ENDTEST - -TEST_7: - -STARTTEST -:set magic& -:set cpo& -:$put =\"\n\nTEST_7:\" -:$put =substitute('A
A', 'A.', '\=submatch(0)', '') -:$put =substitute(\"B\nB\", 'B.', '\=submatch(0)', '') -:$put =substitute(\"B\nB\", 'B.', '\=string(submatch(0, 1))', '') -:$put =substitute('-bb', '\zeb', 'a', 'g') -:$put =substitute('-bb', '\ze', 'c', 'g') -/^TEST_8 -ENDTEST - -TEST_8: - -STARTTEST -:set magic& -:set cpo& -:$put =\"\n\nTEST_8:\" -:$put =',,X' -:s/\(^\|,\)\ze\(,\|X\)/\1N/g -:$put =',,Y' -:s/\(^\|,\)\ze\(,\|Y\)/\1N/gc -a:$put =',,Z' -:s/\(^\|,\)\ze\(,\|Z\)/\1N/gc -yy/^TEST_9: -ENDTEST - -TEST_9: - -STARTTEST -:set magic& -:set cpo& -:$put =\"\n\nTEST_9:\" -:$put ='xxx' -:s/x/X/gc -yyq/^TEST_10: -ENDTEST - -TEST_10: - -STARTTEST -:set magic& -:set cpo& -:$put =\"\n\nTEST_10:\" -:let y = substitute('123', '\zs', 'a', 'g') | $put =y -:let y = substitute('123', '\zs.', 'a', 'g') | $put =y -:let y = substitute('123', '.\zs', 'a', 'g') | $put =y -:let y = substitute('123', '\ze', 'a', 'g') | $put =y -:let y = substitute('123', '\ze.', 'a', 'g') | $put =y -:let y = substitute('123', '.\ze', 'a', 'g') | $put =y -:let y = substitute('123', '1\|\ze', 'a', 'g') | $put =y -:let y = substitute('123', '1\zs\|[23]', 'a', 'g') | $put =y -/^TEST_11 -ENDTEST - -TEST_11: - -STARTTEST -:/^Results/,$wq! test.out -ENDTEST - -Results of test72: diff --git a/src/nvim/testdir/test80.ok b/src/nvim/testdir/test80.ok deleted file mode 100644 index b42f604a07..0000000000 --- a/src/nvim/testdir/test80.ok +++ /dev/null @@ -1,131 +0,0 @@ -Results of test72: - - -TEST_1: -AA -& -C123456789987654321 -d -~ -~ -Gg -Hh -iI -jJ -Kk -l
l -m
m -n
n -o -o -pp -q q -r\r -scs -u -u -vv -w\w -x
x -YyyY -zZZz - - -TEST_2: -AA -& -C123456789987654321 -d -~ -~ -Gg -Hh -iI -jJ -Kk -l
l -m
m -n
n -o -o -pp -q q -r\r -scs -t
t -u -u -vv -w\w -XxxX -yYYy - - -TEST_3: -a\a -b\\b -c
c -d\
d -e\\
e -f\rf -j\nj -k
k -l -l - - -TEST_4: -a\a -b\b -c
c -d
d -e\
e -f
f -j -j -k
k -l -l - - -TEST_5: -A123456789987654321 -[['A123456789'], ['9'], ['8'], ['7'], ['6'], ['5'], ['4'], ['3'], ['2'], ['1']] - - -TEST_6: -a -% -c -% - - -TEST_7: -A
A -B -B -['B -']B --abab -c-cbcbc - - -TEST_8: -N,,NX -N,,NY -N,,NZ - - -TEST_9: -XXx - - -TEST_10: -a1a2a3a -aaa -1a2a3a -a1a2a3a -a1a2a3 -aaa -aa2a3a -1aaa diff --git a/src/nvim/testdir/test86.in b/src/nvim/testdir/test86.in deleted file mode 100644 index 958bd57e29..0000000000 --- a/src/nvim/testdir/test86.in +++ /dev/null @@ -1,1422 +0,0 @@ -Tests for various python features. vim: set ft=vim : - -NOTE: This will cause errors when run under valgrind. -This would require recompiling Python with: - ./configure --without-pymalloc -See http://svn.python.org/view/python/trunk/Misc/README.valgrind?view=markup - -STARTTEST -:so small.vim -:set encoding=latin1 -:set noswapfile -:if !has('python') || has('nvim') | e! test.ok | wq! test.out | endif -:lang C -:fun Test() -:py import vim -:let l = [] -:py l=vim.bindeval('l') -:py f=vim.bindeval('function("strlen")') -:" Extending List directly with different types -:py l.extend([1, "as'd", [1, 2, f, {'a': 1}]]) -:$put =string(l) -:$put =string(l[-1]) -:try -: $put =string(l[-4]) -:catch -: $put =v:exception[:13] -:endtry -:" List assignment -:py l[0]=0 -:$put =string(l) -:py l[-2]=f -:$put =string(l) -:" -:" Extending Dictionary directly with different types -:let d = {} -:fun d.f() -: return 1 -:endfun -py << EOF -d=vim.bindeval('d') -d['1']='asd' -d.update(b=[1, 2, f]) -d.update((('-1', {'a': 1}),)) -d.update({'0': -1}) -dk = d.keys() -dv = d.values() -di = d.items() -cmpfun = lambda a, b: cmp(repr(a), repr(b)) -dk.sort(cmpfun) -dv.sort(cmpfun) -di.sort(cmpfun) -EOF -:$put =pyeval('d[''f''](self={})') -:$put =pyeval('repr(dk)') -:$put =substitute(pyeval('repr(dv)'),'0x\x\+','','g') -:$put =substitute(pyeval('repr(di)'),'0x\x\+','','g') -:for [key, Val] in sort(items(d)) -: $put =string(key) . ' : ' . string(Val) -: unlet key Val -:endfor -:py del dk -:py del di -:py del dv -:" -:" removing items with del -:py del l[2] -:$put =string(l) -:let l = range(8) -:py l=vim.bindeval('l') -:try -: py del l[:3] -: py del l[1:] -:catch -: $put =v:exception -:endtry -:$put =string(l) -:" -:py del d['-1'] -:py del d['f'] -:$put =string(pyeval('d.get(''b'', 1)')) -:$put =string(pyeval('d.pop(''b'')')) -:$put =string(pyeval('d.get(''b'', 1)')) -:$put =string(pyeval('d.pop(''1'', 2)')) -:$put =string(pyeval('d.pop(''1'', 2)')) -:$put =pyeval('repr(d.has_key(''0''))') -:$put =pyeval('repr(d.has_key(''1''))') -:$put =pyeval('repr(''0'' in d)') -:$put =pyeval('repr(''1'' in d)') -:$put =pyeval('repr(list(iter(d)))') -:$put =string(d) -:$put =pyeval('repr(d.popitem())') -:$put =pyeval('repr(d.get(''0''))') -:$put =pyeval('repr(list(iter(d)))') -:" -:" removing items out of range: silently skip items that don't exist -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:" The following two ranges delete nothing as they match empty list: -:py del l[2:1] -:$put =string(l) -:py del l[2:2] -:$put =string(l) -:py del l[2:3] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py del l[2:4] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py del l[2:5] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py del l[2:6] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:" The following two ranges delete nothing as they match empty list: -:py del l[-1:2] -:$put =string(l) -:py del l[-2:2] -:$put =string(l) -:py del l[-3:2] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py del l[-4:2] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py del l[-5:2] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py del l[-6:2] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py del l[::2] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py del l[3:0:-2] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py del l[2:4:-2] -:$put =string(l) -:" -:" Slice assignment to a list -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py l[0:0]=['a'] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py l[1:2]=['b'] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py l[2:4]=['c'] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py l[4:4]=['d'] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py l[-1:2]=['e'] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py l[-10:2]=['f'] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:py l[2:-10]=['g'] -:$put =string(l) -:let l = [] -:py l=vim.bindeval('l') -:py l[0:0]=['h'] -:$put =string(l) -:let l = range(8) -:py l=vim.bindeval('l') -:py l[2:6:2] = [10, 20] -:$put =string(l) -:let l = range(8) -:py l=vim.bindeval('l') -:py l[6:2:-2] = [10, 20] -:$put =string(l) -:let l = range(8) -:py l=vim.bindeval('l') -:py l[6:2] = () -:$put =string(l) -:let l = range(8) -:py l=vim.bindeval('l') -:py l[6:2:1] = () -:$put =string(l) -:let l = range(8) -:py l=vim.bindeval('l') -:py l[2:2:1] = () -:$put =string(l) -:" -:" Locked variables -:let l = [0, 1, 2, 3] -:py l=vim.bindeval('l') -:lockvar! l -:py l[2]='i' -:$put =string(l) -:unlockvar! l -:" -:" Function calls -py << EOF -import sys -def ee(expr, g=globals(), l=locals()): - try: - exec(expr, g, l) - except: - ei = sys.exc_info() - msg = sys.exc_info()[0].__name__ + ':' + repr(sys.exc_info()[1].args) - msg = msg.replace('TypeError:(\'argument 1 ', 'TypeError:(\'') - if expr.find('None') > -1: - msg = msg.replace('TypeError:(\'iteration over non-sequence\',)', - 'TypeError:("\'NoneType\' object is not iterable",)') - if expr.find('FailingNumber') > -1: - msg = msg.replace(', not \'FailingNumber\'', '').replace('"', '\'') - msg = msg.replace('TypeError:(\'iteration over non-sequence\',)', - 'TypeError:("\'FailingNumber\' object is not iterable",)') - if msg.find('(\'\'') > -1 or msg.find('(\'can\'t') > -1: - msg = msg.replace('(\'', '("').replace('\',)', '",)') - if expr == 'fd(self=[])': - # HACK: PyMapping_Check changed meaning - msg = msg.replace('AttributeError:(\'keys\',)', - 'TypeError:(\'unable to convert list to vim dictionary\',)') - vim.current.buffer.append(expr + ':' + msg) - else: - vim.current.buffer.append(expr + ':NOT FAILED') -EOF -:fun New(...) -: return ['NewStart']+a:000+['NewEnd'] -:endfun -:fun DictNew(...) dict -: return ['DictNewStart']+a:000+['DictNewEnd', self] -:endfun -:let l=[function('New'), function('DictNew')] -:py l=vim.bindeval('l') -:py l.extend(list(l[0](1, 2, 3))) -:$put =string(l) -:py l.extend(list(l[1](1, 2, 3, self={'a': 'b'}))) -:$put =string(l) -:py l.extend([l[0].name]) -:$put =string(l) -:py ee('l[1](1, 2, 3)') -:py f=l[0] -:delfunction New -:py ee('f(1, 2, 3)') -:let l=[0.0] -:py l=vim.bindeval('l') -:py l.extend([0.0]) -:$put =string(l) -:let messages=[] -:delfunction DictNew -py <<EOF -d=vim.bindeval('{}') -m=vim.bindeval('messages') -def em(expr, g=globals(), l=locals()): - try: - exec(expr, g, l) - except: - m.extend([sys.exc_type.__name__]) - -em('d["abc1"]') -em('d["abc1"]="\\0"') -em('d["abc1"]=vim') -em('d[""]=1') -em('d["a\\0b"]=1') -em('d[u"a\\0b"]=1') - -em('d.pop("abc1")') -em('d.popitem()') -del em -del m -EOF -:$put =messages -:unlet messages -:" locked and scope attributes -:let d={} | let dl={} | lockvar dl -:for s in split("d dl v: g:") -: let name=tr(s, ':', 's') -: execute 'py '.name.'=vim.bindeval("'.s.'")' -: let toput=s.' : '.join(map(['locked', 'scope'], 'v:val.":".pyeval(name.".".v:val)'), ';') -: $put =toput -:endfor -:silent! let d.abc2=1 -:silent! let dl.abc3=1 -:py d.locked=True -:py dl.locked=False -:silent! let d.def=1 -:silent! let dl.def=1 -:put ='d:'.string(d) -:put ='dl:'.string(dl) -:unlet d dl -: -:let l=[] | let ll=[] | lockvar ll -:for s in split("l ll") -: let name=tr(s, ':', 's') -: execute 'py '.name.'=vim.bindeval("'.s.'")' -: let toput=s.' : locked:'.pyeval(name.'.locked') -: $put =toput -:endfor -:silent! call extend(l, [0]) -:silent! call extend(ll, [0]) -:py l.locked=True -:py ll.locked=False -:silent! call extend(l, [1]) -:silent! call extend(ll, [1]) -:put ='l:'.string(l) -:put ='ll:'.string(ll) -:unlet l ll -:" -:" pyeval() -:let l=pyeval('range(3)') -:$put =string(l) -:let d=pyeval('{"a": "b", "c": 1, "d": ["e"]}') -:$put =sort(items(d)) -:let f=pyeval('0.0') -:$put =string(f) -:" Invalid values: -:for e in ['"\0"', '{"\0": 1}', 'undefined_name', 'vim'] -: try -: let v=pyeval(e) -: catch -: let toput=e.":\t".v:exception[:13] -: $put =toput -: endtry -:endfor -:" -:" threading -:let l = [0] -:py l=vim.bindeval('l') -py <<EOF -import threading -import time - -class T(threading.Thread): - def __init__(self): - threading.Thread.__init__(self) - self.t = 0 - self.running = True - - def run(self): - while self.running: - self.t += 1 - time.sleep(0.1) - -t = T() -del T -t.start() -EOF -:sleep 1 -:py t.running = False -:py t.join() -:py l[0] = t.t > 8 # check if the background thread is working -:py del time -:py del threading -:py del t -:$put =string(l) -:" -:" settrace -:let l = [] -:py l=vim.bindeval('l') -py <<EOF -import sys - -def traceit(frame, event, arg): - global l - if event == "line": - l.extend([frame.f_lineno]) - return traceit - -def trace_main(): - for i in range(5): - pass -EOF -:py sys.settrace(traceit) -:py trace_main() -:py sys.settrace(None) -:py del traceit -:py del trace_main -:$put =string(l) -:" -:" Slice -:py ll = vim.bindeval('[0, 1, 2, 3, 4, 5]') -:py l = ll[:4] -:$put =string(pyeval('l')) -:py l = ll[2:] -:$put =string(pyeval('l')) -:py l = ll[:-4] -:$put =string(pyeval('l')) -:py l = ll[-2:] -:$put =string(pyeval('l')) -:py l = ll[2:4] -:$put =string(pyeval('l')) -:py l = ll[4:2] -:$put =string(pyeval('l')) -:py l = ll[-4:-2] -:$put =string(pyeval('l')) -:py l = ll[-2:-4] -:$put =string(pyeval('l')) -:py l = ll[:] -:$put =string(pyeval('l')) -:py l = ll[0:6] -:$put =string(pyeval('l')) -:py l = ll[-10:10] -:$put =string(pyeval('l')) -:py l = ll[4:2:-1] -:$put =string(pyeval('l')) -:py l = ll[::2] -:$put =string(pyeval('l')) -:py l = ll[4:2:1] -:$put =string(pyeval('l')) -:py del l -:" -:" Vars -:let g:foo = 'bac' -:let w:abc3 = 'def' -:let b:baz = 'bar' -:let t:bar = 'jkl' -:try -: throw "Abc" -:catch -: put =pyeval('vim.vvars[''exception'']') -:endtry -:put =pyeval('vim.vars[''foo'']') -:put =pyeval('vim.current.window.vars[''abc3'']') -:put =pyeval('vim.current.buffer.vars[''baz'']') -:put =pyeval('vim.current.tabpage.vars[''bar'']') -:" -:" Options -:" paste: boolean, global -:" previewheight number, global -:" operatorfunc: string, global -:" number: boolean, window-local -:" numberwidth: number, window-local -:" colorcolumn: string, window-local -:" statusline: string, window-local/global -:" autoindent: boolean, buffer-local -:" shiftwidth: number, buffer-local -:" omnifunc: string, buffer-local -:" preserveindent: boolean, buffer-local/global -:" path: string, buffer-local/global -:let g:bufs=[bufnr('%')] -:new -:let g:bufs+=[bufnr('%')] -:vnew -:let g:bufs+=[bufnr('%')] -:wincmd j -:vnew -:let g:bufs+=[bufnr('%')] -:wincmd l -:fun RecVars(opt) -: let gval =string(eval('&g:'.a:opt)) -: let wvals=join(map(range(1, 4), 'v:val.":".string(getwinvar(v:val, "&".a:opt))')) -: let bvals=join(map(copy(g:bufs), 'v:val.":".string(getbufvar(v:val, "&".a:opt))')) -: put =' G: '.gval -: put =' W: '.wvals -: put =' B: '.wvals -:endfun -py << EOF -def e(s, g=globals(), l=locals()): - try: - exec(s, g, l) - except: - vim.command('return ' + repr(sys.exc_type.__name__)) - -def ev(s, g=globals(), l=locals()): - try: - return eval(s, g, l) - except: - vim.command('let exc=' + repr(sys.exc_type.__name__)) - return 0 -EOF -:fun E(s) -: python e(vim.eval('a:s')) -:endfun -:fun Ev(s) -: let r=pyeval('ev(vim.eval("a:s"))') -: if exists('exc') -: throw exc -: endif -: return r -:endfun -:py gopts1=vim.options -:py wopts1=vim.windows[2].options -:py wopts2=vim.windows[0].options -:py wopts3=vim.windows[1].options -:py bopts1=vim.buffers[vim.bindeval("g:bufs")[2]].options -:py bopts2=vim.buffers[vim.bindeval("g:bufs")[1]].options -:py bopts3=vim.buffers[vim.bindeval("g:bufs")[0]].options -:$put ='wopts iters equal: '.pyeval('list(wopts1) == list(wopts2)') -:$put ='bopts iters equal: '.pyeval('list(bopts1) == list(bopts2)') -:py gset=set(iter(gopts1)) -:py wset=set(iter(wopts1)) -:py bset=set(iter(bopts1)) -:set path=.,..,, -:let lst=[] -:let lst+=[['paste', 1, 0, 1, 2, 1, 1, 0 ]] -:let lst+=[['previewheight', 5, 1, 6, 'a', 0, 1, 0 ]] -:let lst+=[['operatorfunc', 'A', 'B', 'C', 2, 0, 1, 0 ]] -:let lst+=[['number', 0, 1, 1, 0, 1, 0, 1 ]] -:let lst+=[['numberwidth', 2, 3, 5, -100, 0, 0, 1 ]] -:let lst+=[['colorcolumn', '+1', '+2', '+3', 'abc4', 0, 0, 1 ]] -:let lst+=[['statusline', '1', '2', '4', 0, 0, 1, 1 ]] -:let lst+=[['autoindent', 0, 1, 1, 2, 1, 0, 2 ]] -:let lst+=[['shiftwidth', 0, 2, 1, 3, 0, 0, 2 ]] -:let lst+=[['omnifunc', 'A', 'B', 'C', 1, 0, 0, 2 ]] -:let lst+=[['preserveindent', 0, 1, 1, 2, 1, 1, 2 ]] -:let lst+=[['path', '.,,', ',,', '.', 0, 0, 1, 2 ]] -:for [oname, oval1, oval2, oval3, invval, bool, global, local] in lst -: py oname=vim.eval('oname') -: py oval1=vim.bindeval('oval1') -: py oval2=vim.bindeval('oval2') -: py oval3=vim.bindeval('oval3') -: if invval is 0 || invval is 1 -: py invval=bool(vim.bindeval('invval')) -: else -: py invval=vim.bindeval('invval') -: endif -: if bool -: py oval1=bool(oval1) -: py oval2=bool(oval2) -: py oval3=bool(oval3) -: endif -: put ='>>> '.oname -: $put =' g/w/b:'.pyeval('oname in gset').'/'.pyeval('oname in wset').'/'.pyeval('oname in bset') -: $put =' g/w/b (in):'.pyeval('oname in gopts1').'/'.pyeval('oname in wopts1').'/'.pyeval('oname in bopts1') -: for v in ['gopts1', 'wopts1', 'bopts1'] -: try -: put =' p/'.v.': '.Ev('repr('.v.'['''.oname.'''])') -: catch -: put =' p/'.v.'! '.v:exception -: endtry -: let r=E(v.'['''.oname.''']=invval') -: if r isnot 0 -: put =' inv: '.string(invval).'! '.r -: endif -: for vv in (v is# 'gopts1' ? [v] : [v, v[:-2].'2', v[:-2].'3']) -: let val=substitute(vv, '^.opts', 'oval', '') -: let r=E(vv.'['''.oname.''']='.val) -: if r isnot 0 -: put =' '.vv.'! '.r -: endif -: endfor -: endfor -: call RecVars(oname) -: for v in ['wopts3', 'bopts3'] -: let r=E('del '.v.'["'.oname.'"]') -: if r isnot 0 -: put =' del '.v.'! '.r -: endif -: endfor -: call RecVars(oname) -:endfor -:delfunction RecVars -:delfunction E -:delfunction Ev -:py del ev -:py del e -:only -:for buf in g:bufs[1:] -: execute 'bwipeout!' buf -:endfor -:py del gopts1 -:py del wopts1 -:py del wopts2 -:py del wopts3 -:py del bopts1 -:py del bopts2 -:py del bopts3 -:py del oval1 -:py del oval2 -:py del oval3 -:py del oname -:py del invval -:" -:" Test buffer object -:vnew -:put ='First line' -:put ='Second line' -:put ='Third line' -:1 delete _ -:py b=vim.current.buffer -:wincmd w -:mark a -:augroup BUFS -: autocmd BufFilePost * python cb.append(vim.eval('expand("<abuf>")') + ':BufFilePost:' + vim.eval('bufnr("%")')) -: autocmd BufFilePre * python cb.append(vim.eval('expand("<abuf>")') + ':BufFilePre:' + vim.eval('bufnr("%")')) -:augroup END -py << EOF -cb = vim.current.buffer -# Tests BufferAppend and BufferItem -cb.append(b[0]) -# Tests BufferSlice and BufferAssSlice -cb.append('abc5') # Will be overwritten -cb[-1:] = b[:-2] -# Test BufferLength and BufferAssSlice -cb.append('def') # Will not be overwritten -cb[len(cb):] = b[:] -# Test BufferAssItem and BufferMark -cb.append('ghi') # Will be overwritten -cb[-1] = repr((len(cb) - cb.mark('a')[0], cb.mark('a')[1])) -# Test BufferRepr -cb.append(repr(cb) + repr(b)) -# Modify foreign buffer -b.append('foo') -b[0]='bar' -b[0:0]=['baz'] -vim.command('call append("$", getbufline(%i, 1, "$"))' % b.number) -# Test assigning to name property -import os -old_name = cb.name -cb.name = 'foo' -cb.append(cb.name[-11:].replace(os.path.sep, '/')) -b.name = 'bar' -cb.append(b.name[-11:].replace(os.path.sep, '/')) -cb.name = old_name -cb.append(cb.name[-17:].replace(os.path.sep, '/')) -del old_name -# Test CheckBuffer -for _b in vim.buffers: - if _b is not cb: - vim.command('bwipeout! ' + str(_b.number)) -del _b -cb.append('valid: b:%s, cb:%s' % (repr(b.valid), repr(cb.valid))) -for expr in ('b[1]','b[:] = ["A", "B"]','b[:]','b.append("abc6")', 'b.name = "!"'): - try: - exec(expr) - except vim.error: - pass - else: - # Usually a SEGV here - # Should not happen in any case - cb.append('No exception for ' + expr) -vim.command('cd .') -del b -EOF -:augroup BUFS -: autocmd! -:augroup END -:augroup! BUFS -:" -:" Test vim.buffers object -:set hidden -:edit a -:buffer # -:edit b -:buffer # -:edit c -:buffer # -py << EOF -try: - from __builtin__ import next -except ImportError: - next = lambda o: o.next() -# Check GCing iterator that was not fully exhausted -i = iter(vim.buffers) -cb.append('i:' + str(next(i))) -# and also check creating more then one iterator at a time -i2 = iter(vim.buffers) -cb.append('i2:' + str(next(i2))) -cb.append('i:' + str(next(i))) -# The following should trigger GC and not cause any problems -del i -del i2 -i3 = iter(vim.buffers) -cb.append('i3:' + str(next(i3))) -del i3 - -prevnum = 0 -for b in vim.buffers: - # Check buffer order - if prevnum >= b.number: - cb.append('!!! Buffer numbers not in strictly ascending order') - # Check indexing: vim.buffers[number].number == number - cb.append(str(b.number) + ':' + repr(vim.buffers[b.number]) + '=' + repr(b)) - prevnum = b.number -del prevnum - -cb.append(str(len(vim.buffers))) - -bnums = list(map(lambda b: b.number, vim.buffers))[1:] - -# Test wiping out buffer with existing iterator -i4 = iter(vim.buffers) -cb.append('i4:' + str(next(i4))) -vim.command('bwipeout! ' + str(bnums.pop(0))) -try: - next(i4) -except vim.error: - pass -else: - cb.append('!!!! No vim.error') -i4 = iter(vim.buffers) -vim.command('bwipeout! ' + str(bnums.pop(-1))) -vim.command('bwipeout! ' + str(bnums.pop(-1))) -cb.append('i4:' + str(next(i4))) -try: - next(i4) -except StopIteration: - cb.append('StopIteration') -del i4 -del bnums -EOF -:" -:" Test vim.{tabpage,window}list and vim.{tabpage,window} objects -:tabnew 0 -:tabnew 1 -:vnew a.1 -:tabnew 2 -:vnew a.2 -:vnew b.2 -:vnew c.2 -py << EOF -cb.append('Number of tabs: ' + str(len(vim.tabpages))) -cb.append('Current tab pages:') -def W(w): - if repr(w).find('(unknown)') != -1: - return '<window object (unknown)>' - else: - return repr(w) - -start = len(cb) - -def Cursor(w): - if w.buffer is cb: - return repr((start - w.cursor[0], w.cursor[1])) - else: - return repr(w.cursor) - -for t in vim.tabpages: - cb.append(' ' + repr(t) + '(' + str(t.number) + ')' + ': ' + str(len(t.windows)) + ' windows, current is ' + W(t.window)) - cb.append(' Windows:') - for w in t.windows: - cb.append(' ' + W(w) + '(' + str(w.number) + ')' + ': displays buffer ' + repr(w.buffer) + '; cursor is at ' + Cursor(w)) - # Other values depend on the size of the terminal, so they are checked partly: - for attr in ('height', 'row', 'width', 'col'): - try: - aval = getattr(w, attr) - if type(aval) is not long: - raise TypeError - if aval < 0: - raise ValueError - except Exception: - cb.append('!!!!!! Error while getting attribute ' + attr + ': ' + sys.exc_type.__name__) - del aval - del attr - w.cursor = (len(w.buffer), 0) -del W -del Cursor -cb.append('Number of windows in current tab page: ' + str(len(vim.windows))) -if list(vim.windows) != list(vim.current.tabpage.windows): - cb.append('!!!!!! Windows differ') -EOF -:" -:" Test vim.current -py << EOF -def H(o): - return repr(o) -cb.append('Current tab page: ' + repr(vim.current.tabpage)) -cb.append('Current window: ' + repr(vim.current.window) + ': ' + H(vim.current.window) + ' is ' + H(vim.current.tabpage.window)) -cb.append('Current buffer: ' + repr(vim.current.buffer) + ': ' + H(vim.current.buffer) + ' is ' + H(vim.current.window.buffer)+ ' is ' + H(vim.current.tabpage.window.buffer)) -del H -# Assigning: fails -try: - vim.current.window = vim.tabpages[0].window -except ValueError: - cb.append('ValueError at assigning foreign tab window') - -for attr in ('window', 'tabpage', 'buffer'): - try: - setattr(vim.current, attr, None) - except TypeError: - cb.append('Type error at assigning None to vim.current.' + attr) -del attr - -# Assigning: success -vim.current.tabpage = vim.tabpages[-2] -vim.current.buffer = cb -vim.current.window = vim.windows[0] -vim.current.window.cursor = (len(vim.current.buffer), 0) -cb.append('Current tab page: ' + repr(vim.current.tabpage)) -cb.append('Current window: ' + repr(vim.current.window)) -cb.append('Current buffer: ' + repr(vim.current.buffer)) -cb.append('Current line: ' + repr(vim.current.line)) -ws = list(vim.windows) -ts = list(vim.tabpages) -for b in vim.buffers: - if b is not cb: - vim.command('bwipeout! ' + str(b.number)) -del b -cb.append('w.valid: ' + repr([w.valid for w in ws])) -cb.append('t.valid: ' + repr([t.valid for t in ts])) -del w -del t -del ts -del ws -EOF -:tabonly! -:only! -:" -:" Test types -py << EOF -for expr, attr in ( - ('vim.vars', 'Dictionary'), - ('vim.options', 'Options'), - ('vim.bindeval("{}")', 'Dictionary'), - ('vim.bindeval("[]")', 'List'), - ('vim.bindeval("function(\'tr\')")', 'Function'), - ('vim.current.buffer', 'Buffer'), - ('vim.current.range', 'Range'), - ('vim.current.window', 'Window'), - ('vim.current.tabpage', 'TabPage'), -): - cb.append(expr + ':' + attr + ':' + repr(type(eval(expr)) is getattr(vim, attr))) -del expr -del attr -EOF -:" -:" Test __dir__() method -py << EOF -for name, o in ( - ('current', vim.current), - ('buffer', vim.current.buffer), - ('window', vim.current.window), - ('tabpage', vim.current.tabpage), - ('range', vim.current.range), - ('dictionary', vim.bindeval('{}')), - ('list', vim.bindeval('[]')), - ('function', vim.bindeval('function("tr")')), - ('output', sys.stdout), - ): - cb.append(name + ':' + ','.join(dir(o))) -del name -del o -EOF -:" -:" Test vim.*.__new__ -:$put =string(pyeval('vim.Dictionary({})')) -:$put =string(pyeval('vim.Dictionary(a=1)')) -:$put =string(pyeval('vim.Dictionary(((''a'', 1),))')) -:$put =string(pyeval('vim.List()')) -:$put =string(pyeval('vim.List(iter(''abc7''))')) -:$put =string(pyeval('vim.Function(''tr'')')) -:" -:" Test stdout/stderr -:redir => messages -:py sys.stdout.write('abc8') ; sys.stdout.write('def') -:py sys.stderr.write('abc9') ; sys.stderr.write('def') -:py sys.stdout.writelines(iter('abcA')) -:py sys.stderr.writelines(iter('abcB')) -:redir END -:$put =string(substitute(messages, '\d\+', '', 'g')) -:" Test subclassing -:fun Put(...) -: $put =string(a:000) -: return a:000 -:endfun -py << EOF -class DupDict(vim.Dictionary): - def __setitem__(self, key, value): - super(DupDict, self).__setitem__(key, value) - super(DupDict, self).__setitem__('dup_' + key, value) -dd = DupDict() -dd['a'] = 'b' - -class DupList(vim.List): - def __getitem__(self, idx): - return [super(DupList, self).__getitem__(idx)] * 2 - -dl = DupList() -dl2 = DupList(iter('abcC')) -dl.extend(dl2[0]) - -class DupFun(vim.Function): - def __call__(self, arg): - return super(DupFun, self).__call__(arg, arg) - -df = DupFun('Put') -EOF -:$put =string(sort(keys(pyeval('dd')))) -:$put =string(pyeval('dl')) -:$put =string(pyeval('dl2')) -:$put =string(pyeval('df(2)')) -:$put =string(pyeval('dl') is# pyeval('dl')) -:$put =string(pyeval('dd') is# pyeval('dd')) -:$put =string(pyeval('df')) -:delfunction Put -py << EOF -del DupDict -del DupList -del DupFun -del dd -del dl -del dl2 -del df -EOF -:" -:" Test chdir -py << EOF -import os -fnamemodify = vim.Function('fnamemodify') -cb.append(fnamemodify('.', ':p:h:t')) -cb.append(vim.eval('@%')) -os.chdir('..') -path = fnamemodify('.', ':p:h:t') -if path != 'src': - # Running tests from a shadow directory, so move up another level - # This will result in @% looking like shadow/testdir/test86.in, hence the - # extra fnamemodify - os.chdir('..') - cb.append(fnamemodify('.', ':p:h:t')) - cb.append(fnamemodify(vim.eval('@%'), ':s?^%s.??' % path).replace(os.path.sep, '/')) - os.chdir(path) - del path -else: - cb.append(fnamemodify('.', ':p:h:t')) - cb.append(vim.eval('@%').replace(os.path.sep, '/')) -os.chdir('testdir') -cb.append(fnamemodify('.', ':p:h:t')) -cb.append(vim.eval('@%')) -del fnamemodify -EOF -:" -:" Test errors -:fun F() dict -:endfun -:fun D() -:endfun -py << EOF -d = vim.Dictionary() -ned = vim.Dictionary(foo='bar', baz='abcD') -dl = vim.Dictionary(a=1) -dl.locked = True -l = vim.List() -ll = vim.List('abcE') -ll.locked = True -nel = vim.List('abcO') -f = vim.Function('string') -fd = vim.Function('F') -fdel = vim.Function('D') -vim.command('delfunction D') - -def subexpr_test(expr, name, subexprs): - cb.append('>>> Testing %s using %s' % (name, expr)) - for subexpr in subexprs: - ee(expr % subexpr) - cb.append('<<< Finished') - -def stringtochars_test(expr): - return subexpr_test(expr, 'StringToChars', ( - '1', # Fail type checks - 'u"\\0"', # Fail PyString_AsStringAndSize(bytes, , NULL) check - '"\\0"', # Fail PyString_AsStringAndSize(object, , NULL) check - )) - -class Mapping(object): - def __init__(self, d): - self.d = d - - def __getitem__(self, key): - return self.d[key] - - def keys(self): - return self.d.keys() - - def items(self): - return self.d.items() - -def convertfrompyobject_test(expr, recurse=True): - # pydict_to_tv - stringtochars_test(expr % '{%s : 1}') - if recurse: - convertfrompyobject_test(expr % '{"abcF" : %s}', False) - # pymap_to_tv - stringtochars_test(expr % 'Mapping({%s : 1})') - if recurse: - convertfrompyobject_test(expr % 'Mapping({"abcG" : %s})', False) - # pyseq_to_tv - iter_test(expr) - return subexpr_test(expr, 'ConvertFromPyObject', ( - 'None', # Not conversible - '{"": 1}', # Empty key not allowed - '{u"": 1}', # Same, but with unicode object - 'FailingMapping()', # - 'FailingMappingKey()', # - 'FailingNumber()', # - )) - -def convertfrompymapping_test(expr): - convertfrompyobject_test(expr) - return subexpr_test(expr, 'ConvertFromPyMapping', ( - '[]', - )) - -def iter_test(expr): - return subexpr_test(expr, '*Iter*', ( - 'FailingIter()', - 'FailingIterNext()', - )) - -def number_test(expr, natural=False, unsigned=False): - if natural: - unsigned = True - return subexpr_test(expr, 'NumberToLong', ( - '[]', - 'None', - ) + (unsigned and ('-1',) or ()) - + (natural and ('0',) or ())) - -class FailingTrue(object): - def __nonzero__(self): - raise NotImplementedError('bool') - -class FailingIter(object): - def __iter__(self): - raise NotImplementedError('iter') - -class FailingIterNext(object): - def __iter__(self): - return self - - def next(self): - raise NotImplementedError('next') - -class FailingIterNextN(object): - def __init__(self, n): - self.n = n - - def __iter__(self): - return self - - def next(self): - if self.n: - self.n -= 1 - return 1 - else: - raise NotImplementedError('next N') - -class FailingMappingKey(object): - def __getitem__(self, item): - raise NotImplementedError('getitem:mappingkey') - - def keys(self): - return list("abcH") - -class FailingMapping(object): - def __getitem__(self): - raise NotImplementedError('getitem:mapping') - - def keys(self): - raise NotImplementedError('keys') - -class FailingList(list): - def __getitem__(self, idx): - if i == 2: - raise NotImplementedError('getitem:list') - else: - return super(FailingList, self).__getitem__(idx) - -class NoArgsCall(object): - def __call__(self): - pass - -class FailingCall(object): - def __call__(self, path): - raise NotImplementedError('call') - -class FailingNumber(object): - def __int__(self): - raise NotImplementedError('int') - -cb.append("> Output") -cb.append(">> OutputSetattr") -ee('del sys.stdout.softspace') -number_test('sys.stdout.softspace = %s', unsigned=True) -number_test('sys.stderr.softspace = %s', unsigned=True) -ee('sys.stdout.attr = None') -cb.append(">> OutputWrite") -ee('sys.stdout.write(None)') -cb.append(">> OutputWriteLines") -ee('sys.stdout.writelines(None)') -ee('sys.stdout.writelines([1])') -iter_test('sys.stdout.writelines(%s)') -cb.append("> VimCommand") -stringtochars_test('vim.command(%s)') -ee('vim.command("", 2)') -#! Not checked: vim->python exceptions translating: checked later -cb.append("> VimToPython") -#! Not checked: everything: needs errors in internal python functions -cb.append("> VimEval") -stringtochars_test('vim.eval(%s)') -ee('vim.eval("", FailingTrue())') -#! Not checked: everything: needs errors in internal python functions -cb.append("> VimEvalPy") -stringtochars_test('vim.bindeval(%s)') -ee('vim.eval("", 2)') -#! Not checked: vim->python exceptions translating: checked later -cb.append("> VimStrwidth") -stringtochars_test('vim.strwidth(%s)') -cb.append("> VimForeachRTP") -ee('vim.foreach_rtp(None)') -ee('vim.foreach_rtp(NoArgsCall())') -ee('vim.foreach_rtp(FailingCall())') -ee('vim.foreach_rtp(int, 2)') -cb.append('> import') -old_rtp = vim.options['rtp'] -vim.options['rtp'] = os.getcwd().replace(',', '\\,').replace('\\', '\\\\') -ee('import xxx_no_such_module_xxx') -ee('import failing_import') -ee('import failing') -vim.options['rtp'] = old_rtp -del old_rtp -cb.append("> Options") -cb.append(">> OptionsItem") -ee('vim.options["abcQ"]') -ee('vim.options[""]') -stringtochars_test('vim.options[%s]') -cb.append(">> OptionsContains") -stringtochars_test('%s in vim.options') -cb.append("> Dictionary") -cb.append(">> DictionaryConstructor") -ee('vim.Dictionary("abcI")') -##! Not checked: py_dict_alloc failure -cb.append(">> DictionarySetattr") -ee('del d.locked') -ee('d.locked = FailingTrue()') -ee('vim.vvars.locked = False') -ee('d.scope = True') -ee('d.xxx = True') -cb.append(">> _DictionaryItem") -ee('d.get("a", 2, 3)') -stringtochars_test('d.get(%s)') -ee('d.pop("a")') -ee('dl.pop("a")') -cb.append(">> DictionaryContains") -ee('"" in d') -ee('0 in d') -cb.append(">> DictionaryIterNext") -ee('for i in ned: ned["a"] = 1') -del i -cb.append(">> DictionaryAssItem") -ee('dl["b"] = 1') -stringtochars_test('d[%s] = 1') -convertfrompyobject_test('d["a"] = %s') -cb.append(">> DictionaryUpdate") -cb.append(">>> kwargs") -cb.append(">>> iter") -ee('d.update(FailingMapping())') -ee('d.update([FailingIterNext()])') -ee('d.update([FailingIterNextN(1)])') -iter_test('d.update(%s)') -convertfrompyobject_test('d.update(%s)') -stringtochars_test('d.update(((%s, 0),))') -convertfrompyobject_test('d.update((("a", %s),))') -cb.append(">> DictionaryPopItem") -ee('d.popitem(1, 2)') -cb.append(">> DictionaryHasKey") -ee('d.has_key()') -cb.append("> List") -cb.append(">> ListConstructor") -ee('vim.List(1, 2)') -ee('vim.List(a=1)') -iter_test('vim.List(%s)') -convertfrompyobject_test('vim.List([%s])') -cb.append(">> ListItem") -ee('l[1000]') -cb.append(">> ListAssItem") -ee('ll[1] = 2') -ee('l[1000] = 3') -cb.append(">> ListAssSlice") -ee('ll[1:100] = "abcJ"') -iter_test('l[:] = %s') -ee('nel[1:10:2] = "abcK"') -cb.append(repr(tuple(nel))) -ee('nel[1:10:2] = "a"') -cb.append(repr(tuple(nel))) -ee('nel[1:1:-1] = "a"') -cb.append(repr(tuple(nel))) -ee('nel[:] = FailingIterNextN(2)') -cb.append(repr(tuple(nel))) -convertfrompyobject_test('l[:] = [%s]') -cb.append(">> ListConcatInPlace") -iter_test('l.extend(%s)') -convertfrompyobject_test('l.extend([%s])') -cb.append(">> ListSetattr") -ee('del l.locked') -ee('l.locked = FailingTrue()') -ee('l.xxx = True') -cb.append("> Function") -cb.append(">> FunctionConstructor") -ee('vim.Function("123")') -ee('vim.Function("xxx_non_existent_function_xxx")') -ee('vim.Function("xxx#non#existent#function#xxx")') -cb.append(">> FunctionCall") -convertfrompyobject_test('f(%s)') -convertfrompymapping_test('fd(self=%s)') -cb.append("> TabPage") -cb.append(">> TabPageAttr") -ee('vim.current.tabpage.xxx') -cb.append("> TabList") -cb.append(">> TabListItem") -ee('vim.tabpages[1000]') -cb.append("> Window") -cb.append(">> WindowAttr") -ee('vim.current.window.xxx') -cb.append(">> WindowSetattr") -ee('vim.current.window.buffer = 0') -ee('vim.current.window.cursor = (100000000, 100000000)') -ee('vim.current.window.cursor = True') -number_test('vim.current.window.height = %s', unsigned=True) -number_test('vim.current.window.width = %s', unsigned=True) -ee('vim.current.window.xxxxxx = True') -cb.append("> WinList") -cb.append(">> WinListItem") -ee('vim.windows[1000]') -cb.append("> Buffer") -cb.append(">> StringToLine (indirect)") -ee('vim.current.buffer[0] = u"\\na"') -ee('vim.current.buffer[0] = "\\na"') -cb.append(">> SetBufferLine (indirect)") -ee('vim.current.buffer[0] = True') -cb.append(">> SetBufferLineList (indirect)") -ee('vim.current.buffer[:] = True') -ee('vim.current.buffer[:] = ["\\na", "bc"]') -cb.append(">> InsertBufferLines (indirect)") -ee('vim.current.buffer.append(None)') -ee('vim.current.buffer.append(["\\na", "bc"])') -ee('vim.current.buffer.append("\\nbc")') -cb.append(">> RBItem") -ee('vim.current.buffer[100000000]') -cb.append(">> RBAsItem") -ee('vim.current.buffer[100000000] = ""') -cb.append(">> BufferAttr") -ee('vim.current.buffer.xxx') -cb.append(">> BufferSetattr") -ee('vim.current.buffer.name = True') -ee('vim.current.buffer.xxx = True') -cb.append(">> BufferMark") -ee('vim.current.buffer.mark(0)') -ee('vim.current.buffer.mark("abcM")') -ee('vim.current.buffer.mark("!")') -cb.append(">> BufferRange") -ee('vim.current.buffer.range(1, 2, 3)') -cb.append("> BufMap") -cb.append(">> BufMapItem") -ee('vim.buffers[100000000]') -number_test('vim.buffers[%s]', natural=True) -cb.append("> Current") -cb.append(">> CurrentGetattr") -ee('vim.current.xxx') -cb.append(">> CurrentSetattr") -ee('vim.current.line = True') -ee('vim.current.buffer = True') -ee('vim.current.window = True') -ee('vim.current.tabpage = True') -ee('vim.current.xxx = True') -del d -del ned -del dl -del l -del ll -del nel -del f -del fd -del fdel -del subexpr_test -del stringtochars_test -del Mapping -del convertfrompyobject_test -del convertfrompymapping_test -del iter_test -del number_test -del FailingTrue -del FailingIter -del FailingIterNext -del FailingIterNextN -del FailingMapping -del FailingMappingKey -del FailingList -del NoArgsCall -del FailingCall -del FailingNumber -EOF -:delfunction F -:" -:" Test import -py << EOF -sys.path.insert(0, os.path.join(os.getcwd(), 'python_before')) -sys.path.append(os.path.join(os.getcwd(), 'python_after')) -vim.options['rtp'] = os.getcwd().replace(',', '\\,').replace('\\', '\\\\') -l = [] -def callback(path): - l.append(path[-len('/testdir'):].replace(os.path.sep, '/')) -vim.foreach_rtp(callback) -cb.append(repr(l)) -del l -def callback(path): - return path[-len('/testdir'):].replace(os.path.sep, '/') -cb.append(repr(vim.foreach_rtp(callback))) -del callback -from module import dir as d -from modulex import ddir -cb.append(d + ',' + ddir) -import before -cb.append(before.dir) -import after -cb.append(after.dir) -import topmodule as tm -import topmodule.submodule as tms -import topmodule.submodule.subsubmodule.subsubsubmodule as tmsss -cb.append(tm.__file__.replace('.pyc', '.py').replace(os.path.sep, '/')[-len('modulex/topmodule/__init__.py'):]) -cb.append(tms.__file__.replace('.pyc', '.py').replace(os.path.sep, '/')[-len('modulex/topmodule/submodule/__init__.py'):]) -cb.append(tmsss.__file__.replace('.pyc', '.py').replace(os.path.sep, '/')[-len('modulex/topmodule/submodule/subsubmodule/subsubsubmodule.py'):]) -del before -del after -del d -del ddir -del tm -del tms -del tmsss -EOF -:" -:" Test exceptions -:fun Exe(e) -: execute a:e -:endfun -py << EOF -Exe = vim.bindeval('function("Exe")') -ee('vim.command("throw \'abcN\'")') -ee('Exe("throw \'def\'")') -ee('vim.eval("Exe(\'throw \'\'ghi\'\'\')")') -ee('vim.eval("Exe(\'echoerr \'\'jkl\'\'\')")') -ee('vim.eval("Exe(\'xxx_non_existent_command_xxx\')")') -ee('vim.eval("xxx_unknown_function_xxx()")') -ee('vim.bindeval("Exe(\'xxx_non_existent_command_xxx\')")') -del Exe -EOF -:delfunction Exe -:" -:" Regression: interrupting vim.command propagates to next vim.command -py << EOF -def test_keyboard_interrupt(): - try: - vim.command('while 1 | endwhile') - except KeyboardInterrupt: - cb.append('Caught KeyboardInterrupt') - except Exception: - cb.append('!!!!!!!! Caught exception: ' + repr(sys.exc_info)) - else: - cb.append('!!!!!!!! No exception') - try: - vim.command('$ put =\'Running :put\'') - except KeyboardInterrupt: - cb.append('!!!!!!!! Caught KeyboardInterrupt') - except Exception: - cb.append('!!!!!!!! Caught exception: ' + repr(sys.exc_info)) - else: - cb.append('No exception') -EOF -:debuggreedy -:call inputsave() -:call feedkeys("s\ns\ns\ns\nq\n") -:redir => output -:debug silent! py test_keyboard_interrupt() -:redir END -:0 debuggreedy -:call inputrestore() -:silent $put =output -:unlet output -:py del test_keyboard_interrupt -:" -:" Cleanup -py << EOF -del cb -del ee -del sys -del os -del vim -EOF -:endfun -:" -:fun RunTest() -:let checkrefs = !empty($PYTHONDUMPREFS) -:let start = getline(1, '$') -:for i in range(checkrefs ? 10 : 1) -: if i != 0 -: %d _ -: call setline(1, start) -: endif -: call Test() -: if i == 0 -: let result = getline(1, '$') -: endif -:endfor -:if checkrefs -: %d _ -: call setline(1, result) -:endif -:endfun -:" -:call RunTest() -:delfunction RunTest -:delfunction Test -:call garbagecollect(1) -:" -:/^start:/,$wq! test.out -:" vim: et ts=4 isk-=\: -:call getchar() -ENDTEST - -start: diff --git a/src/nvim/testdir/test86.ok b/src/nvim/testdir/test86.ok deleted file mode 100644 index 257a5ee4cd..0000000000 --- a/src/nvim/testdir/test86.ok +++ /dev/null @@ -1,1266 +0,0 @@ -start: -[1, 'as''d', [1, 2, function('strlen'), {'a': 1}]] -[1, 2, function('strlen'), {'a': 1}] -Vim(put):E684: -[0, 'as''d', [1, 2, function('strlen'), {'a': 1}]] -[0, function('strlen'), [1, 2, function('strlen'), {'a': 1}]] -1 -['-1', '0', '1', 'b', 'f'] -['asd', -1L, <vim.Function '1'>, <vim.dictionary object at >, <vim.list object at >] -[('-1', <vim.dictionary object at >), ('0', -1L), ('1', 'asd'), ('b', <vim.list object at >), ('f', <vim.Function '1'>)] -'-1' : {'a': 1} -'0' : -1 -'1' : 'asd' -'b' : [1, 2, function('strlen')] -'f' : function('1') -[0, function('strlen')] -[3] -[1, 2, function('strlen')] -[1, 2, function('strlen')] -1 -'asd' -2 -True -False -True -False -['0'] -{'0': -1} -('0', -1L) -None -[] -[0, 1, 2, 3] -[0, 1, 2, 3] -[0, 1, 3] -[0, 1] -[0, 1] -[0, 1] -[0, 1, 2, 3] -[0, 1, 2, 3] -[0, 2, 3] -[2, 3] -[2, 3] -[2, 3] -[1, 3] -[0, 2] -[0, 1, 2, 3] -['a', 0, 1, 2, 3] -[0, 'b', 2, 3] -[0, 1, 'c'] -[0, 1, 2, 3, 'd'] -[0, 1, 2, 'e', 3] -['f', 2, 3] -[0, 1, 'g', 2, 3] -['h'] -[0, 1, 10, 3, 20, 5, 6, 7] -[0, 1, 2, 3, 20, 5, 10, 7] -[0, 1, 2, 3, 4, 5, 6, 7] -[0, 1, 2, 3, 4, 5, 6, 7] -[0, 1, 2, 3, 4, 5, 6, 7] -[0, 1, 2, 3] -[function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd'] -[function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd', 'DictNewStart', 1, 2, 3, 'DictNewEnd', {'a': 'b'}] -[function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd', 'DictNewStart', 1, 2, 3, 'DictNewEnd', {'a': 'b'}, 'New'] -l[1](1, 2, 3):error:('Vim:E725: Calling dict function without Dictionary: DictNew',) -f(1, 2, 3):error:('Vim:E117: Unknown function: New',) -[0.0, 0.0] -KeyError -TypeError -TypeError -ValueError -TypeError -TypeError -KeyError -KeyError -d : locked:0;scope:0 -dl : locked:1;scope:0 -v: : locked:2;scope:1 -g: : locked:0;scope:2 -d:{'abc2': 1} -dl:{'def': 1} -l : locked:0 -ll : locked:1 -l:[0] -ll:[1] -[0, 1, 2] -['a', 'b'] -['c', 1] -['d', ['e']] -0.0 -"\0": Vim(let):E859: -{"\0": 1}: Vim(let):E859: -undefined_name: Vim(let):Trace -vim: Vim(let):E859: -[1] -[1, 10, 11, 10, 11, 10, 11, 10, 11, 10, 11, 10, 1] -[0, 1, 2, 3] -[2, 3, 4, 5] -[0, 1] -[4, 5] -[2, 3] -[] -[2, 3] -[] -[0, 1, 2, 3, 4, 5] -[0, 1, 2, 3, 4, 5] -[0, 1, 2, 3, 4, 5] -[4, 3] -[0, 2, 4] -[] -Abc -bac -def -bar -jkl -wopts iters equal: 1 -bopts iters equal: 1 ->>> paste - g/w/b:1/0/0 - g/w/b (in):1/0/0 - p/gopts1: False - p/wopts1! KeyError - inv: 2! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1! KeyError - inv: 2! KeyError - bopts1! KeyError - bopts2! KeyError - bopts3! KeyError - G: 1 - W: 1:1 2:1 3:1 4:1 - B: 1:1 2:1 3:1 4:1 - del wopts3! KeyError - del bopts3! KeyError - G: 1 - W: 1:1 2:1 3:1 4:1 - B: 1:1 2:1 3:1 4:1 ->>> previewheight - g/w/b:1/0/0 - g/w/b (in):1/0/0 - p/gopts1: 12 - inv: 'a'! TypeError - p/wopts1! KeyError - inv: 'a'! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1! KeyError - inv: 'a'! KeyError - bopts1! KeyError - bopts2! KeyError - bopts3! KeyError - G: 5 - W: 1:5 2:5 3:5 4:5 - B: 1:5 2:5 3:5 4:5 - del wopts3! KeyError - del bopts3! KeyError - G: 5 - W: 1:5 2:5 3:5 4:5 - B: 1:5 2:5 3:5 4:5 ->>> operatorfunc - g/w/b:1/0/0 - g/w/b (in):1/0/0 - p/gopts1: '' - inv: 2! TypeError - p/wopts1! KeyError - inv: 2! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1! KeyError - inv: 2! KeyError - bopts1! KeyError - bopts2! KeyError - bopts3! KeyError - G: 'A' - W: 1:'A' 2:'A' 3:'A' 4:'A' - B: 1:'A' 2:'A' 3:'A' 4:'A' - del wopts3! KeyError - del bopts3! KeyError - G: 'A' - W: 1:'A' 2:'A' 3:'A' 4:'A' - B: 1:'A' 2:'A' 3:'A' 4:'A' ->>> number - g/w/b:0/1/0 - g/w/b (in):0/1/0 - p/gopts1! KeyError - inv: 0! KeyError - gopts1! KeyError - p/wopts1: False - p/bopts1! KeyError - inv: 0! KeyError - bopts1! KeyError - bopts2! KeyError - bopts3! KeyError - G: 0 - W: 1:1 2:1 3:0 4:0 - B: 1:1 2:1 3:0 4:0 - del wopts3! ValueError - del bopts3! KeyError - G: 0 - W: 1:1 2:1 3:0 4:0 - B: 1:1 2:1 3:0 4:0 ->>> numberwidth - g/w/b:0/1/0 - g/w/b (in):0/1/0 - p/gopts1! KeyError - inv: -100! KeyError - gopts1! KeyError - p/wopts1: 8 - inv: -100! error - p/bopts1! KeyError - inv: -100! KeyError - bopts1! KeyError - bopts2! KeyError - bopts3! KeyError - G: 8 - W: 1:3 2:5 3:2 4:8 - B: 1:3 2:5 3:2 4:8 - del wopts3! ValueError - del bopts3! KeyError - G: 8 - W: 1:3 2:5 3:2 4:8 - B: 1:3 2:5 3:2 4:8 ->>> colorcolumn - g/w/b:0/1/0 - g/w/b (in):0/1/0 - p/gopts1! KeyError - inv: 'abc4'! KeyError - gopts1! KeyError - p/wopts1: '' - inv: 'abc4'! error - p/bopts1! KeyError - inv: 'abc4'! KeyError - bopts1! KeyError - bopts2! KeyError - bopts3! KeyError - G: '' - W: 1:'+2' 2:'+3' 3:'+1' 4:'' - B: 1:'+2' 2:'+3' 3:'+1' 4:'' - del wopts3! ValueError - del bopts3! KeyError - G: '' - W: 1:'+2' 2:'+3' 3:'+1' 4:'' - B: 1:'+2' 2:'+3' 3:'+1' 4:'' ->>> statusline - g/w/b:1/1/0 - g/w/b (in):1/1/0 - p/gopts1: '' - inv: 0! TypeError - p/wopts1: None - inv: 0! TypeError - p/bopts1! KeyError - inv: 0! KeyError - bopts1! KeyError - bopts2! KeyError - bopts3! KeyError - G: '1' - W: 1:'2' 2:'4' 3:'1' 4:'1' - B: 1:'2' 2:'4' 3:'1' 4:'1' - del bopts3! KeyError - G: '1' - W: 1:'2' 2:'1' 3:'1' 4:'1' - B: 1:'2' 2:'1' 3:'1' 4:'1' ->>> autoindent - g/w/b:0/0/1 - g/w/b (in):0/0/1 - p/gopts1! KeyError - inv: 2! KeyError - gopts1! KeyError - p/wopts1! KeyError - inv: 2! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1: False - G: 0 - W: 1:0 2:1 3:0 4:1 - B: 1:0 2:1 3:0 4:1 - del wopts3! KeyError - del bopts3! ValueError - G: 0 - W: 1:0 2:1 3:0 4:1 - B: 1:0 2:1 3:0 4:1 ->>> shiftwidth - g/w/b:0/0/1 - g/w/b (in):0/0/1 - p/gopts1! KeyError - inv: 3! KeyError - gopts1! KeyError - p/wopts1! KeyError - inv: 3! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1: 8 - G: 8 - W: 1:0 2:2 3:8 4:1 - B: 1:0 2:2 3:8 4:1 - del wopts3! KeyError - del bopts3! ValueError - G: 8 - W: 1:0 2:2 3:8 4:1 - B: 1:0 2:2 3:8 4:1 ->>> omnifunc - g/w/b:0/0/1 - g/w/b (in):0/0/1 - p/gopts1! KeyError - inv: 1! KeyError - gopts1! KeyError - p/wopts1! KeyError - inv: 1! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1: '' - inv: 1! TypeError - G: '' - W: 1:'A' 2:'B' 3:'' 4:'C' - B: 1:'A' 2:'B' 3:'' 4:'C' - del wopts3! KeyError - del bopts3! ValueError - G: '' - W: 1:'A' 2:'B' 3:'' 4:'C' - B: 1:'A' 2:'B' 3:'' 4:'C' ->>> preserveindent - g/w/b:0/0/1 - g/w/b (in):0/0/1 - p/gopts1! KeyError - inv: 2! KeyError - gopts1! KeyError - p/wopts1! KeyError - inv: 2! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1: False - G: 0 - W: 1:0 2:1 3:0 4:1 - B: 1:0 2:1 3:0 4:1 - del wopts3! KeyError - del bopts3! ValueError - G: 0 - W: 1:0 2:1 3:0 4:1 - B: 1:0 2:1 3:0 4:1 ->>> path - g/w/b:1/0/1 - g/w/b (in):1/0/1 - p/gopts1: '.,..,,' - inv: 0! TypeError - p/wopts1! KeyError - inv: 0! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1: None - inv: 0! TypeError - G: '.,,' - W: 1:'.,,' 2:',,' 3:'.,,' 4:'.' - B: 1:'.,,' 2:',,' 3:'.,,' 4:'.' - del wopts3! KeyError - G: '.,,' - W: 1:'.,,' 2:',,' 3:'.,,' 4:'.,,' - B: 1:'.,,' 2:',,' 3:'.,,' 4:'.,,' -First line -First line -def -First line -Second line -Third line -(7, 2) -<buffer test86.in><buffer > -baz -bar -Second line -Third line -foo -1:BufFilePre:1 -1:BufFilePost:1 -testdir/foo -5:BufFilePre:5 -5:BufFilePost:5 -testdir/bar -1:BufFilePre:1 -1:BufFilePost:1 -testdir/test86.in -valid: b:False, cb:True -i:<buffer test86.in> -i2:<buffer test86.in> -i:<buffer a> -i3:<buffer test86.in> -1:<buffer test86.in>=<buffer test86.in> -8:<buffer a>=<buffer a> -9:<buffer b>=<buffer b> -10:<buffer c>=<buffer c> -4 -i4:<buffer test86.in> -i4:<buffer test86.in> -StopIteration -Number of tabs: 4 -Current tab pages: - <tabpage 0>(1): 1 windows, current is <window object (unknown)> - Windows: - <window object (unknown)>(1): displays buffer <buffer test86.in>; cursor is at (37, 0) - <tabpage 1>(2): 1 windows, current is <window object (unknown)> - Windows: - <window object (unknown)>(1): displays buffer <buffer 0>; cursor is at (1, 0) - <tabpage 2>(3): 2 windows, current is <window object (unknown)> - Windows: - <window object (unknown)>(1): displays buffer <buffer a.1>; cursor is at (1, 0) - <window object (unknown)>(2): displays buffer <buffer 1>; cursor is at (1, 0) - <tabpage 3>(4): 4 windows, current is <window 0> - Windows: - <window 0>(1): displays buffer <buffer c.2>; cursor is at (1, 0) - <window 1>(2): displays buffer <buffer b.2>; cursor is at (1, 0) - <window 2>(3): displays buffer <buffer a.2>; cursor is at (1, 0) - <window 3>(4): displays buffer <buffer 2>; cursor is at (1, 0) -Number of windows in current tab page: 4 -Current tab page: <tabpage 3> -Current window: <window 0>: <window 0> is <window 0> -Current buffer: <buffer c.2>: <buffer c.2> is <buffer c.2> is <buffer c.2> -ValueError at assigning foreign tab window -Type error at assigning None to vim.current.window -Type error at assigning None to vim.current.tabpage -Type error at assigning None to vim.current.buffer -Current tab page: <tabpage 2> -Current window: <window 0> -Current buffer: <buffer test86.in> -Current line: 'Type error at assigning None to vim.current.buffer' -w.valid: [True, False] -t.valid: [True, False, True, False] -vim.vars:Dictionary:True -vim.options:Options:True -vim.bindeval("{}"):Dictionary:True -vim.bindeval("[]"):List:True -vim.bindeval("function('tr')"):Function:True -vim.current.buffer:Buffer:True -vim.current.range:Range:True -vim.current.window:Window:True -vim.current.tabpage:TabPage:True -current:__dir__,__members__,buffer,line,range,tabpage,window -buffer:__dir__,__members__,append,mark,name,number,options,range,valid,vars -window:__dir__,__members__,buffer,col,cursor,height,number,options,row,tabpage,valid,vars -tabpage:__dir__,__members__,number,valid,vars,window,windows -range:__dir__,__members__,append,end,start -dictionary:__dir__,__members__,get,has_key,items,keys,locked,pop,popitem,scope,update,values -list:__dir__,__members__,extend,locked -function:__dir__,__members__,softspace -output:__dir__,__members__,flush,softspace,write,writelines -{} -{'a': 1} -{'a': 1} -[] -['a', 'b', 'c', '7'] -function('tr') -' -abcdef -line : -abcdef -abcA -line : -abcB' -['a', 'dup_a'] -['a', 'a'] -['a', 'b', 'c', 'C'] -[2, 2] -[2, 2] -1 -1 -function('Put') -testdir -test86.in -src -testdir/test86.in -testdir -test86.in -> Output ->> OutputSetattr -del sys.stdout.softspace:AttributeError:("can't delete OutputObject attributes",) ->>> Testing NumberToLong using sys.stdout.softspace = %s -sys.stdout.softspace = []:TypeError:('expected int(), long() or something supporting coercing to long(), but got list',) -sys.stdout.softspace = None:TypeError:('expected int(), long() or something supporting coercing to long(), but got NoneType',) -sys.stdout.softspace = -1:ValueError:('number must be greater or equal to zero',) -<<< Finished ->>> Testing NumberToLong using sys.stderr.softspace = %s -sys.stderr.softspace = []:TypeError:('expected int(), long() or something supporting coercing to long(), but got list',) -sys.stderr.softspace = None:TypeError:('expected int(), long() or something supporting coercing to long(), but got NoneType',) -sys.stderr.softspace = -1:ValueError:('number must be greater or equal to zero',) -<<< Finished -sys.stdout.attr = None:AttributeError:('invalid attribute: attr',) ->> OutputWrite -sys.stdout.write(None):TypeError:('coercing to Unicode: need string or buffer, NoneType found',) ->> OutputWriteLines -sys.stdout.writelines(None):TypeError:("'NoneType' object is not iterable",) -sys.stdout.writelines([1]):TypeError:('coercing to Unicode: need string or buffer, int found',) ->>> Testing *Iter* using sys.stdout.writelines(%s) -sys.stdout.writelines(FailingIter()):NotImplementedError:('iter',) -sys.stdout.writelines(FailingIterNext()):NotImplementedError:('next',) -<<< Finished -> VimCommand ->>> Testing StringToChars using vim.command(%s) -vim.command(1):TypeError:('expected str() or unicode() instance, but got int',) -vim.command(u"\0"):TypeError:('expected string without null bytes',) -vim.command("\0"):TypeError:('expected string without null bytes',) -<<< Finished -vim.command("", 2):TypeError:('command() takes exactly one argument (2 given)',) -> VimToPython -> VimEval ->>> Testing StringToChars using vim.eval(%s) -vim.eval(1):TypeError:('expected str() or unicode() instance, but got int',) -vim.eval(u"\0"):TypeError:('expected string without null bytes',) -vim.eval("\0"):TypeError:('expected string without null bytes',) -<<< Finished -vim.eval("", FailingTrue()):TypeError:('function takes exactly 1 argument (2 given)',) -> VimEvalPy ->>> Testing StringToChars using vim.bindeval(%s) -vim.bindeval(1):TypeError:('expected str() or unicode() instance, but got int',) -vim.bindeval(u"\0"):TypeError:('expected string without null bytes',) -vim.bindeval("\0"):TypeError:('expected string without null bytes',) -<<< Finished -vim.eval("", 2):TypeError:('function takes exactly 1 argument (2 given)',) -> VimStrwidth ->>> Testing StringToChars using vim.strwidth(%s) -vim.strwidth(1):TypeError:('expected str() or unicode() instance, but got int',) -vim.strwidth(u"\0"):TypeError:('expected string without null bytes',) -vim.strwidth("\0"):TypeError:('expected string without null bytes',) -<<< Finished -> VimForeachRTP -vim.foreach_rtp(None):TypeError:("'NoneType' object is not callable",) -vim.foreach_rtp(NoArgsCall()):TypeError:('__call__() takes exactly 1 argument (2 given)',) -vim.foreach_rtp(FailingCall()):NotImplementedError:('call',) -vim.foreach_rtp(int, 2):TypeError:('foreach_rtp() takes exactly one argument (2 given)',) -> import -import xxx_no_such_module_xxx:ImportError:('No module named xxx_no_such_module_xxx',) -import failing_import:ImportError:('No module named failing_import',) -import failing:NotImplementedError:() -> Options ->> OptionsItem -vim.options["abcQ"]:KeyError:('abcQ',) -vim.options[""]:ValueError:('empty keys are not allowed',) ->>> Testing StringToChars using vim.options[%s] -vim.options[1]:TypeError:('expected str() or unicode() instance, but got int',) -vim.options[u"\0"]:TypeError:('expected string without null bytes',) -vim.options["\0"]:TypeError:('expected string without null bytes',) -<<< Finished ->> OptionsContains ->>> Testing StringToChars using %s in vim.options -1 in vim.options:TypeError:('expected str() or unicode() instance, but got int',) -u"\0" in vim.options:TypeError:('expected string without null bytes',) -"\0" in vim.options:TypeError:('expected string without null bytes',) -<<< Finished -> Dictionary ->> DictionaryConstructor -vim.Dictionary("abcI"):ValueError:('expected sequence element of size 2, but got sequence of size 1',) ->> DictionarySetattr -del d.locked:AttributeError:('cannot delete vim.Dictionary attributes',) -d.locked = FailingTrue():NotImplementedError:('bool',) -vim.vvars.locked = False:TypeError:('cannot modify fixed dictionary',) -d.scope = True:AttributeError:('cannot set attribute scope',) -d.xxx = True:AttributeError:('cannot set attribute xxx',) ->> _DictionaryItem -d.get("a", 2, 3):TypeError:('function takes at most 2 arguments (3 given)',) ->>> Testing StringToChars using d.get(%s) -d.get(1):TypeError:('expected str() or unicode() instance, but got int',) -d.get(u"\0"):TypeError:('expected string without null bytes',) -d.get("\0"):TypeError:('expected string without null bytes',) -<<< Finished -d.pop("a"):KeyError:('a',) -dl.pop("a"):error:('dictionary is locked',) ->> DictionaryContains -"" in d:ValueError:('empty keys are not allowed',) -0 in d:TypeError:('expected str() or unicode() instance, but got int',) ->> DictionaryIterNext -for i in ned: ned["a"] = 1:RuntimeError:('hashtab changed during iteration',) ->> DictionaryAssItem -dl["b"] = 1:error:('dictionary is locked',) ->>> Testing StringToChars using d[%s] = 1 -d[1] = 1:TypeError:('expected str() or unicode() instance, but got int',) -d[u"\0"] = 1:TypeError:('expected string without null bytes',) -d["\0"] = 1:TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using d["a"] = {%s : 1} -d["a"] = {1 : 1}:TypeError:('expected str() or unicode() instance, but got int',) -d["a"] = {u"\0" : 1}:TypeError:('expected string without null bytes',) -d["a"] = {"\0" : 1}:TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using d["a"] = {"abcF" : {%s : 1}} -d["a"] = {"abcF" : {1 : 1}}:TypeError:('expected str() or unicode() instance, but got int',) -d["a"] = {"abcF" : {u"\0" : 1}}:TypeError:('expected string without null bytes',) -d["a"] = {"abcF" : {"\0" : 1}}:TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using d["a"] = {"abcF" : Mapping({%s : 1})} -d["a"] = {"abcF" : Mapping({1 : 1})}:TypeError:('expected str() or unicode() instance, but got int',) -d["a"] = {"abcF" : Mapping({u"\0" : 1})}:TypeError:('expected string without null bytes',) -d["a"] = {"abcF" : Mapping({"\0" : 1})}:TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using d["a"] = {"abcF" : %s} -d["a"] = {"abcF" : FailingIter()}:TypeError:('unable to convert FailingIter to vim structure',) -d["a"] = {"abcF" : FailingIterNext()}:NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using d["a"] = {"abcF" : %s} -d["a"] = {"abcF" : None}:TypeError:('unable to convert NoneType to vim structure',) -d["a"] = {"abcF" : {"": 1}}:ValueError:('empty keys are not allowed',) -d["a"] = {"abcF" : {u"": 1}}:ValueError:('empty keys are not allowed',) -d["a"] = {"abcF" : FailingMapping()}:NotImplementedError:('keys',) -d["a"] = {"abcF" : FailingMappingKey()}:NotImplementedError:('getitem:mappingkey',) -d["a"] = {"abcF" : FailingNumber()}:TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing StringToChars using d["a"] = Mapping({%s : 1}) -d["a"] = Mapping({1 : 1}):TypeError:('expected str() or unicode() instance, but got int',) -d["a"] = Mapping({u"\0" : 1}):TypeError:('expected string without null bytes',) -d["a"] = Mapping({"\0" : 1}):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using d["a"] = Mapping({"abcG" : {%s : 1}}) -d["a"] = Mapping({"abcG" : {1 : 1}}):TypeError:('expected str() or unicode() instance, but got int',) -d["a"] = Mapping({"abcG" : {u"\0" : 1}}):TypeError:('expected string without null bytes',) -d["a"] = Mapping({"abcG" : {"\0" : 1}}):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using d["a"] = Mapping({"abcG" : Mapping({%s : 1})}) -d["a"] = Mapping({"abcG" : Mapping({1 : 1})}):TypeError:('expected str() or unicode() instance, but got int',) -d["a"] = Mapping({"abcG" : Mapping({u"\0" : 1})}):TypeError:('expected string without null bytes',) -d["a"] = Mapping({"abcG" : Mapping({"\0" : 1})}):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using d["a"] = Mapping({"abcG" : %s}) -d["a"] = Mapping({"abcG" : FailingIter()}):TypeError:('unable to convert FailingIter to vim structure',) -d["a"] = Mapping({"abcG" : FailingIterNext()}):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using d["a"] = Mapping({"abcG" : %s}) -d["a"] = Mapping({"abcG" : None}):TypeError:('unable to convert NoneType to vim structure',) -d["a"] = Mapping({"abcG" : {"": 1}}):ValueError:('empty keys are not allowed',) -d["a"] = Mapping({"abcG" : {u"": 1}}):ValueError:('empty keys are not allowed',) -d["a"] = Mapping({"abcG" : FailingMapping()}):NotImplementedError:('keys',) -d["a"] = Mapping({"abcG" : FailingMappingKey()}):NotImplementedError:('getitem:mappingkey',) -d["a"] = Mapping({"abcG" : FailingNumber()}):TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing *Iter* using d["a"] = %s -d["a"] = FailingIter():TypeError:('unable to convert FailingIter to vim structure',) -d["a"] = FailingIterNext():NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using d["a"] = %s -d["a"] = None:TypeError:('unable to convert NoneType to vim structure',) -d["a"] = {"": 1}:ValueError:('empty keys are not allowed',) -d["a"] = {u"": 1}:ValueError:('empty keys are not allowed',) -d["a"] = FailingMapping():NotImplementedError:('keys',) -d["a"] = FailingMappingKey():NotImplementedError:('getitem:mappingkey',) -d["a"] = FailingNumber():TypeError:('long() argument must be a string or a number',) -<<< Finished ->> DictionaryUpdate ->>> kwargs ->>> iter -d.update(FailingMapping()):NotImplementedError:('keys',) -d.update([FailingIterNext()]):NotImplementedError:('next',) -d.update([FailingIterNextN(1)]):NotImplementedError:('next N',) ->>> Testing *Iter* using d.update(%s) -d.update(FailingIter()):NotImplementedError:('iter',) -d.update(FailingIterNext()):NotImplementedError:('next',) -<<< Finished ->>> Testing StringToChars using d.update({%s : 1}) -d.update({1 : 1}):TypeError:('expected str() or unicode() instance, but got int',) -d.update({u"\0" : 1}):TypeError:('expected string without null bytes',) -d.update({"\0" : 1}):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using d.update({"abcF" : {%s : 1}}) -d.update({"abcF" : {1 : 1}}):TypeError:('expected str() or unicode() instance, but got int',) -d.update({"abcF" : {u"\0" : 1}}):TypeError:('expected string without null bytes',) -d.update({"abcF" : {"\0" : 1}}):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using d.update({"abcF" : Mapping({%s : 1})}) -d.update({"abcF" : Mapping({1 : 1})}):TypeError:('expected str() or unicode() instance, but got int',) -d.update({"abcF" : Mapping({u"\0" : 1})}):TypeError:('expected string without null bytes',) -d.update({"abcF" : Mapping({"\0" : 1})}):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using d.update({"abcF" : %s}) -d.update({"abcF" : FailingIter()}):TypeError:('unable to convert FailingIter to vim structure',) -d.update({"abcF" : FailingIterNext()}):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using d.update({"abcF" : %s}) -d.update({"abcF" : None}):TypeError:('unable to convert NoneType to vim structure',) -d.update({"abcF" : {"": 1}}):ValueError:('empty keys are not allowed',) -d.update({"abcF" : {u"": 1}}):ValueError:('empty keys are not allowed',) -d.update({"abcF" : FailingMapping()}):NotImplementedError:('keys',) -d.update({"abcF" : FailingMappingKey()}):NotImplementedError:('getitem:mappingkey',) -d.update({"abcF" : FailingNumber()}):TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing StringToChars using d.update(Mapping({%s : 1})) -d.update(Mapping({1 : 1})):TypeError:('expected str() or unicode() instance, but got int',) -d.update(Mapping({u"\0" : 1})):TypeError:('expected string without null bytes',) -d.update(Mapping({"\0" : 1})):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using d.update(Mapping({"abcG" : {%s : 1}})) -d.update(Mapping({"abcG" : {1 : 1}})):TypeError:('expected str() or unicode() instance, but got int',) -d.update(Mapping({"abcG" : {u"\0" : 1}})):TypeError:('expected string without null bytes',) -d.update(Mapping({"abcG" : {"\0" : 1}})):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using d.update(Mapping({"abcG" : Mapping({%s : 1})})) -d.update(Mapping({"abcG" : Mapping({1 : 1})})):TypeError:('expected str() or unicode() instance, but got int',) -d.update(Mapping({"abcG" : Mapping({u"\0" : 1})})):TypeError:('expected string without null bytes',) -d.update(Mapping({"abcG" : Mapping({"\0" : 1})})):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using d.update(Mapping({"abcG" : %s})) -d.update(Mapping({"abcG" : FailingIter()})):TypeError:('unable to convert FailingIter to vim structure',) -d.update(Mapping({"abcG" : FailingIterNext()})):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using d.update(Mapping({"abcG" : %s})) -d.update(Mapping({"abcG" : None})):TypeError:('unable to convert NoneType to vim structure',) -d.update(Mapping({"abcG" : {"": 1}})):ValueError:('empty keys are not allowed',) -d.update(Mapping({"abcG" : {u"": 1}})):ValueError:('empty keys are not allowed',) -d.update(Mapping({"abcG" : FailingMapping()})):NotImplementedError:('keys',) -d.update(Mapping({"abcG" : FailingMappingKey()})):NotImplementedError:('getitem:mappingkey',) -d.update(Mapping({"abcG" : FailingNumber()})):TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing *Iter* using d.update(%s) -d.update(FailingIter()):NotImplementedError:('iter',) -d.update(FailingIterNext()):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using d.update(%s) -d.update(None):TypeError:("'NoneType' object is not iterable",) -d.update({"": 1}):ValueError:('empty keys are not allowed',) -d.update({u"": 1}):ValueError:('empty keys are not allowed',) -d.update(FailingMapping()):NotImplementedError:('keys',) -d.update(FailingMappingKey()):NotImplementedError:('getitem:mappingkey',) -d.update(FailingNumber()):TypeError:("'FailingNumber' object is not iterable",) -<<< Finished ->>> Testing StringToChars using d.update(((%s, 0),)) -d.update(((1, 0),)):TypeError:('expected str() or unicode() instance, but got int',) -d.update(((u"\0", 0),)):TypeError:('expected string without null bytes',) -d.update((("\0", 0),)):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using d.update((("a", {%s : 1}),)) -d.update((("a", {1 : 1}),)):TypeError:('expected str() or unicode() instance, but got int',) -d.update((("a", {u"\0" : 1}),)):TypeError:('expected string without null bytes',) -d.update((("a", {"\0" : 1}),)):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using d.update((("a", {"abcF" : {%s : 1}}),)) -d.update((("a", {"abcF" : {1 : 1}}),)):TypeError:('expected str() or unicode() instance, but got int',) -d.update((("a", {"abcF" : {u"\0" : 1}}),)):TypeError:('expected string without null bytes',) -d.update((("a", {"abcF" : {"\0" : 1}}),)):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using d.update((("a", {"abcF" : Mapping({%s : 1})}),)) -d.update((("a", {"abcF" : Mapping({1 : 1})}),)):TypeError:('expected str() or unicode() instance, but got int',) -d.update((("a", {"abcF" : Mapping({u"\0" : 1})}),)):TypeError:('expected string without null bytes',) -d.update((("a", {"abcF" : Mapping({"\0" : 1})}),)):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using d.update((("a", {"abcF" : %s}),)) -d.update((("a", {"abcF" : FailingIter()}),)):TypeError:('unable to convert FailingIter to vim structure',) -d.update((("a", {"abcF" : FailingIterNext()}),)):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using d.update((("a", {"abcF" : %s}),)) -d.update((("a", {"abcF" : None}),)):TypeError:('unable to convert NoneType to vim structure',) -d.update((("a", {"abcF" : {"": 1}}),)):ValueError:('empty keys are not allowed',) -d.update((("a", {"abcF" : {u"": 1}}),)):ValueError:('empty keys are not allowed',) -d.update((("a", {"abcF" : FailingMapping()}),)):NotImplementedError:('keys',) -d.update((("a", {"abcF" : FailingMappingKey()}),)):NotImplementedError:('getitem:mappingkey',) -d.update((("a", {"abcF" : FailingNumber()}),)):TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing StringToChars using d.update((("a", Mapping({%s : 1})),)) -d.update((("a", Mapping({1 : 1})),)):TypeError:('expected str() or unicode() instance, but got int',) -d.update((("a", Mapping({u"\0" : 1})),)):TypeError:('expected string without null bytes',) -d.update((("a", Mapping({"\0" : 1})),)):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using d.update((("a", Mapping({"abcG" : {%s : 1}})),)) -d.update((("a", Mapping({"abcG" : {1 : 1}})),)):TypeError:('expected str() or unicode() instance, but got int',) -d.update((("a", Mapping({"abcG" : {u"\0" : 1}})),)):TypeError:('expected string without null bytes',) -d.update((("a", Mapping({"abcG" : {"\0" : 1}})),)):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using d.update((("a", Mapping({"abcG" : Mapping({%s : 1})})),)) -d.update((("a", Mapping({"abcG" : Mapping({1 : 1})})),)):TypeError:('expected str() or unicode() instance, but got int',) -d.update((("a", Mapping({"abcG" : Mapping({u"\0" : 1})})),)):TypeError:('expected string without null bytes',) -d.update((("a", Mapping({"abcG" : Mapping({"\0" : 1})})),)):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using d.update((("a", Mapping({"abcG" : %s})),)) -d.update((("a", Mapping({"abcG" : FailingIter()})),)):TypeError:('unable to convert FailingIter to vim structure',) -d.update((("a", Mapping({"abcG" : FailingIterNext()})),)):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using d.update((("a", Mapping({"abcG" : %s})),)) -d.update((("a", Mapping({"abcG" : None})),)):TypeError:('unable to convert NoneType to vim structure',) -d.update((("a", Mapping({"abcG" : {"": 1}})),)):ValueError:('empty keys are not allowed',) -d.update((("a", Mapping({"abcG" : {u"": 1}})),)):ValueError:('empty keys are not allowed',) -d.update((("a", Mapping({"abcG" : FailingMapping()})),)):NotImplementedError:('keys',) -d.update((("a", Mapping({"abcG" : FailingMappingKey()})),)):NotImplementedError:('getitem:mappingkey',) -d.update((("a", Mapping({"abcG" : FailingNumber()})),)):TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing *Iter* using d.update((("a", %s),)) -d.update((("a", FailingIter()),)):TypeError:('unable to convert FailingIter to vim structure',) -d.update((("a", FailingIterNext()),)):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using d.update((("a", %s),)) -d.update((("a", None),)):TypeError:('unable to convert NoneType to vim structure',) -d.update((("a", {"": 1}),)):ValueError:('empty keys are not allowed',) -d.update((("a", {u"": 1}),)):ValueError:('empty keys are not allowed',) -d.update((("a", FailingMapping()),)):NotImplementedError:('keys',) -d.update((("a", FailingMappingKey()),)):NotImplementedError:('getitem:mappingkey',) -d.update((("a", FailingNumber()),)):TypeError:('long() argument must be a string or a number',) -<<< Finished ->> DictionaryPopItem -d.popitem(1, 2):TypeError:('popitem() takes no arguments (2 given)',) ->> DictionaryHasKey -d.has_key():TypeError:('has_key() takes exactly one argument (0 given)',) -> List ->> ListConstructor -vim.List(1, 2):TypeError:('function takes at most 1 argument (2 given)',) -vim.List(a=1):TypeError:('list constructor does not accept keyword arguments',) ->>> Testing *Iter* using vim.List(%s) -vim.List(FailingIter()):NotImplementedError:('iter',) -vim.List(FailingIterNext()):NotImplementedError:('next',) -<<< Finished ->>> Testing StringToChars using vim.List([{%s : 1}]) -vim.List([{1 : 1}]):TypeError:('expected str() or unicode() instance, but got int',) -vim.List([{u"\0" : 1}]):TypeError:('expected string without null bytes',) -vim.List([{"\0" : 1}]):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using vim.List([{"abcF" : {%s : 1}}]) -vim.List([{"abcF" : {1 : 1}}]):TypeError:('expected str() or unicode() instance, but got int',) -vim.List([{"abcF" : {u"\0" : 1}}]):TypeError:('expected string without null bytes',) -vim.List([{"abcF" : {"\0" : 1}}]):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using vim.List([{"abcF" : Mapping({%s : 1})}]) -vim.List([{"abcF" : Mapping({1 : 1})}]):TypeError:('expected str() or unicode() instance, but got int',) -vim.List([{"abcF" : Mapping({u"\0" : 1})}]):TypeError:('expected string without null bytes',) -vim.List([{"abcF" : Mapping({"\0" : 1})}]):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using vim.List([{"abcF" : %s}]) -vim.List([{"abcF" : FailingIter()}]):TypeError:('unable to convert FailingIter to vim structure',) -vim.List([{"abcF" : FailingIterNext()}]):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using vim.List([{"abcF" : %s}]) -vim.List([{"abcF" : None}]):TypeError:('unable to convert NoneType to vim structure',) -vim.List([{"abcF" : {"": 1}}]):ValueError:('empty keys are not allowed',) -vim.List([{"abcF" : {u"": 1}}]):ValueError:('empty keys are not allowed',) -vim.List([{"abcF" : FailingMapping()}]):NotImplementedError:('keys',) -vim.List([{"abcF" : FailingMappingKey()}]):NotImplementedError:('getitem:mappingkey',) -vim.List([{"abcF" : FailingNumber()}]):TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing StringToChars using vim.List([Mapping({%s : 1})]) -vim.List([Mapping({1 : 1})]):TypeError:('expected str() or unicode() instance, but got int',) -vim.List([Mapping({u"\0" : 1})]):TypeError:('expected string without null bytes',) -vim.List([Mapping({"\0" : 1})]):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using vim.List([Mapping({"abcG" : {%s : 1}})]) -vim.List([Mapping({"abcG" : {1 : 1}})]):TypeError:('expected str() or unicode() instance, but got int',) -vim.List([Mapping({"abcG" : {u"\0" : 1}})]):TypeError:('expected string without null bytes',) -vim.List([Mapping({"abcG" : {"\0" : 1}})]):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using vim.List([Mapping({"abcG" : Mapping({%s : 1})})]) -vim.List([Mapping({"abcG" : Mapping({1 : 1})})]):TypeError:('expected str() or unicode() instance, but got int',) -vim.List([Mapping({"abcG" : Mapping({u"\0" : 1})})]):TypeError:('expected string without null bytes',) -vim.List([Mapping({"abcG" : Mapping({"\0" : 1})})]):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using vim.List([Mapping({"abcG" : %s})]) -vim.List([Mapping({"abcG" : FailingIter()})]):TypeError:('unable to convert FailingIter to vim structure',) -vim.List([Mapping({"abcG" : FailingIterNext()})]):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using vim.List([Mapping({"abcG" : %s})]) -vim.List([Mapping({"abcG" : None})]):TypeError:('unable to convert NoneType to vim structure',) -vim.List([Mapping({"abcG" : {"": 1}})]):ValueError:('empty keys are not allowed',) -vim.List([Mapping({"abcG" : {u"": 1}})]):ValueError:('empty keys are not allowed',) -vim.List([Mapping({"abcG" : FailingMapping()})]):NotImplementedError:('keys',) -vim.List([Mapping({"abcG" : FailingMappingKey()})]):NotImplementedError:('getitem:mappingkey',) -vim.List([Mapping({"abcG" : FailingNumber()})]):TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing *Iter* using vim.List([%s]) -vim.List([FailingIter()]):TypeError:('unable to convert FailingIter to vim structure',) -vim.List([FailingIterNext()]):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using vim.List([%s]) -vim.List([None]):TypeError:('unable to convert NoneType to vim structure',) -vim.List([{"": 1}]):ValueError:('empty keys are not allowed',) -vim.List([{u"": 1}]):ValueError:('empty keys are not allowed',) -vim.List([FailingMapping()]):NotImplementedError:('keys',) -vim.List([FailingMappingKey()]):NotImplementedError:('getitem:mappingkey',) -vim.List([FailingNumber()]):TypeError:('long() argument must be a string or a number',) -<<< Finished ->> ListItem -l[1000]:IndexError:('list index out of range',) ->> ListAssItem -ll[1] = 2:error:('list is locked',) -l[1000] = 3:IndexError:('list index out of range',) ->> ListAssSlice -ll[1:100] = "abcJ":error:('list is locked',) ->>> Testing *Iter* using l[:] = %s -l[:] = FailingIter():NotImplementedError:('iter',) -l[:] = FailingIterNext():NotImplementedError:('next',) -<<< Finished -nel[1:10:2] = "abcK":ValueError:('attempt to assign sequence of size greater then 2 to extended slice',) -('a', 'b', 'c', 'O') -nel[1:10:2] = "a":ValueError:('attempt to assign sequence of size 1 to extended slice of size 2',) -('a', 'b', 'c', 'O') -nel[1:1:-1] = "a":ValueError:('attempt to assign sequence of size greater then 0 to extended slice',) -('a', 'b', 'c', 'O') -nel[:] = FailingIterNextN(2):NotImplementedError:('next N',) -('a', 'b', 'c', 'O') ->>> Testing StringToChars using l[:] = [{%s : 1}] -l[:] = [{1 : 1}]:TypeError:('expected str() or unicode() instance, but got int',) -l[:] = [{u"\0" : 1}]:TypeError:('expected string without null bytes',) -l[:] = [{"\0" : 1}]:TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using l[:] = [{"abcF" : {%s : 1}}] -l[:] = [{"abcF" : {1 : 1}}]:TypeError:('expected str() or unicode() instance, but got int',) -l[:] = [{"abcF" : {u"\0" : 1}}]:TypeError:('expected string without null bytes',) -l[:] = [{"abcF" : {"\0" : 1}}]:TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using l[:] = [{"abcF" : Mapping({%s : 1})}] -l[:] = [{"abcF" : Mapping({1 : 1})}]:TypeError:('expected str() or unicode() instance, but got int',) -l[:] = [{"abcF" : Mapping({u"\0" : 1})}]:TypeError:('expected string without null bytes',) -l[:] = [{"abcF" : Mapping({"\0" : 1})}]:TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using l[:] = [{"abcF" : %s}] -l[:] = [{"abcF" : FailingIter()}]:TypeError:('unable to convert FailingIter to vim structure',) -l[:] = [{"abcF" : FailingIterNext()}]:NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using l[:] = [{"abcF" : %s}] -l[:] = [{"abcF" : None}]:TypeError:('unable to convert NoneType to vim structure',) -l[:] = [{"abcF" : {"": 1}}]:ValueError:('empty keys are not allowed',) -l[:] = [{"abcF" : {u"": 1}}]:ValueError:('empty keys are not allowed',) -l[:] = [{"abcF" : FailingMapping()}]:NotImplementedError:('keys',) -l[:] = [{"abcF" : FailingMappingKey()}]:NotImplementedError:('getitem:mappingkey',) -l[:] = [{"abcF" : FailingNumber()}]:TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing StringToChars using l[:] = [Mapping({%s : 1})] -l[:] = [Mapping({1 : 1})]:TypeError:('expected str() or unicode() instance, but got int',) -l[:] = [Mapping({u"\0" : 1})]:TypeError:('expected string without null bytes',) -l[:] = [Mapping({"\0" : 1})]:TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using l[:] = [Mapping({"abcG" : {%s : 1}})] -l[:] = [Mapping({"abcG" : {1 : 1}})]:TypeError:('expected str() or unicode() instance, but got int',) -l[:] = [Mapping({"abcG" : {u"\0" : 1}})]:TypeError:('expected string without null bytes',) -l[:] = [Mapping({"abcG" : {"\0" : 1}})]:TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using l[:] = [Mapping({"abcG" : Mapping({%s : 1})})] -l[:] = [Mapping({"abcG" : Mapping({1 : 1})})]:TypeError:('expected str() or unicode() instance, but got int',) -l[:] = [Mapping({"abcG" : Mapping({u"\0" : 1})})]:TypeError:('expected string without null bytes',) -l[:] = [Mapping({"abcG" : Mapping({"\0" : 1})})]:TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using l[:] = [Mapping({"abcG" : %s})] -l[:] = [Mapping({"abcG" : FailingIter()})]:TypeError:('unable to convert FailingIter to vim structure',) -l[:] = [Mapping({"abcG" : FailingIterNext()})]:NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using l[:] = [Mapping({"abcG" : %s})] -l[:] = [Mapping({"abcG" : None})]:TypeError:('unable to convert NoneType to vim structure',) -l[:] = [Mapping({"abcG" : {"": 1}})]:ValueError:('empty keys are not allowed',) -l[:] = [Mapping({"abcG" : {u"": 1}})]:ValueError:('empty keys are not allowed',) -l[:] = [Mapping({"abcG" : FailingMapping()})]:NotImplementedError:('keys',) -l[:] = [Mapping({"abcG" : FailingMappingKey()})]:NotImplementedError:('getitem:mappingkey',) -l[:] = [Mapping({"abcG" : FailingNumber()})]:TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing *Iter* using l[:] = [%s] -l[:] = [FailingIter()]:TypeError:('unable to convert FailingIter to vim structure',) -l[:] = [FailingIterNext()]:NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using l[:] = [%s] -l[:] = [None]:TypeError:('unable to convert NoneType to vim structure',) -l[:] = [{"": 1}]:ValueError:('empty keys are not allowed',) -l[:] = [{u"": 1}]:ValueError:('empty keys are not allowed',) -l[:] = [FailingMapping()]:NotImplementedError:('keys',) -l[:] = [FailingMappingKey()]:NotImplementedError:('getitem:mappingkey',) -l[:] = [FailingNumber()]:TypeError:('long() argument must be a string or a number',) -<<< Finished ->> ListConcatInPlace ->>> Testing *Iter* using l.extend(%s) -l.extend(FailingIter()):NotImplementedError:('iter',) -l.extend(FailingIterNext()):NotImplementedError:('next',) -<<< Finished ->>> Testing StringToChars using l.extend([{%s : 1}]) -l.extend([{1 : 1}]):TypeError:('expected str() or unicode() instance, but got int',) -l.extend([{u"\0" : 1}]):TypeError:('expected string without null bytes',) -l.extend([{"\0" : 1}]):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using l.extend([{"abcF" : {%s : 1}}]) -l.extend([{"abcF" : {1 : 1}}]):TypeError:('expected str() or unicode() instance, but got int',) -l.extend([{"abcF" : {u"\0" : 1}}]):TypeError:('expected string without null bytes',) -l.extend([{"abcF" : {"\0" : 1}}]):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using l.extend([{"abcF" : Mapping({%s : 1})}]) -l.extend([{"abcF" : Mapping({1 : 1})}]):TypeError:('expected str() or unicode() instance, but got int',) -l.extend([{"abcF" : Mapping({u"\0" : 1})}]):TypeError:('expected string without null bytes',) -l.extend([{"abcF" : Mapping({"\0" : 1})}]):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using l.extend([{"abcF" : %s}]) -l.extend([{"abcF" : FailingIter()}]):TypeError:('unable to convert FailingIter to vim structure',) -l.extend([{"abcF" : FailingIterNext()}]):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using l.extend([{"abcF" : %s}]) -l.extend([{"abcF" : None}]):TypeError:('unable to convert NoneType to vim structure',) -l.extend([{"abcF" : {"": 1}}]):ValueError:('empty keys are not allowed',) -l.extend([{"abcF" : {u"": 1}}]):ValueError:('empty keys are not allowed',) -l.extend([{"abcF" : FailingMapping()}]):NotImplementedError:('keys',) -l.extend([{"abcF" : FailingMappingKey()}]):NotImplementedError:('getitem:mappingkey',) -l.extend([{"abcF" : FailingNumber()}]):TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing StringToChars using l.extend([Mapping({%s : 1})]) -l.extend([Mapping({1 : 1})]):TypeError:('expected str() or unicode() instance, but got int',) -l.extend([Mapping({u"\0" : 1})]):TypeError:('expected string without null bytes',) -l.extend([Mapping({"\0" : 1})]):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using l.extend([Mapping({"abcG" : {%s : 1}})]) -l.extend([Mapping({"abcG" : {1 : 1}})]):TypeError:('expected str() or unicode() instance, but got int',) -l.extend([Mapping({"abcG" : {u"\0" : 1}})]):TypeError:('expected string without null bytes',) -l.extend([Mapping({"abcG" : {"\0" : 1}})]):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using l.extend([Mapping({"abcG" : Mapping({%s : 1})})]) -l.extend([Mapping({"abcG" : Mapping({1 : 1})})]):TypeError:('expected str() or unicode() instance, but got int',) -l.extend([Mapping({"abcG" : Mapping({u"\0" : 1})})]):TypeError:('expected string without null bytes',) -l.extend([Mapping({"abcG" : Mapping({"\0" : 1})})]):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using l.extend([Mapping({"abcG" : %s})]) -l.extend([Mapping({"abcG" : FailingIter()})]):TypeError:('unable to convert FailingIter to vim structure',) -l.extend([Mapping({"abcG" : FailingIterNext()})]):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using l.extend([Mapping({"abcG" : %s})]) -l.extend([Mapping({"abcG" : None})]):TypeError:('unable to convert NoneType to vim structure',) -l.extend([Mapping({"abcG" : {"": 1}})]):ValueError:('empty keys are not allowed',) -l.extend([Mapping({"abcG" : {u"": 1}})]):ValueError:('empty keys are not allowed',) -l.extend([Mapping({"abcG" : FailingMapping()})]):NotImplementedError:('keys',) -l.extend([Mapping({"abcG" : FailingMappingKey()})]):NotImplementedError:('getitem:mappingkey',) -l.extend([Mapping({"abcG" : FailingNumber()})]):TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing *Iter* using l.extend([%s]) -l.extend([FailingIter()]):TypeError:('unable to convert FailingIter to vim structure',) -l.extend([FailingIterNext()]):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using l.extend([%s]) -l.extend([None]):TypeError:('unable to convert NoneType to vim structure',) -l.extend([{"": 1}]):ValueError:('empty keys are not allowed',) -l.extend([{u"": 1}]):ValueError:('empty keys are not allowed',) -l.extend([FailingMapping()]):NotImplementedError:('keys',) -l.extend([FailingMappingKey()]):NotImplementedError:('getitem:mappingkey',) -l.extend([FailingNumber()]):TypeError:('long() argument must be a string or a number',) -<<< Finished ->> ListSetattr -del l.locked:AttributeError:('cannot delete vim.List attributes',) -l.locked = FailingTrue():NotImplementedError:('bool',) -l.xxx = True:AttributeError:('cannot set attribute xxx',) -> Function ->> FunctionConstructor -vim.Function("123"):ValueError:('unnamed function 123 does not exist',) -vim.Function("xxx_non_existent_function_xxx"):ValueError:('function xxx_non_existent_function_xxx does not exist',) -vim.Function("xxx#non#existent#function#xxx"):NOT FAILED ->> FunctionCall ->>> Testing StringToChars using f({%s : 1}) -f({1 : 1}):TypeError:('expected str() or unicode() instance, but got int',) -f({u"\0" : 1}):TypeError:('expected string without null bytes',) -f({"\0" : 1}):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using f({"abcF" : {%s : 1}}) -f({"abcF" : {1 : 1}}):TypeError:('expected str() or unicode() instance, but got int',) -f({"abcF" : {u"\0" : 1}}):TypeError:('expected string without null bytes',) -f({"abcF" : {"\0" : 1}}):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using f({"abcF" : Mapping({%s : 1})}) -f({"abcF" : Mapping({1 : 1})}):TypeError:('expected str() or unicode() instance, but got int',) -f({"abcF" : Mapping({u"\0" : 1})}):TypeError:('expected string without null bytes',) -f({"abcF" : Mapping({"\0" : 1})}):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using f({"abcF" : %s}) -f({"abcF" : FailingIter()}):TypeError:('unable to convert FailingIter to vim structure',) -f({"abcF" : FailingIterNext()}):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using f({"abcF" : %s}) -f({"abcF" : None}):TypeError:('unable to convert NoneType to vim structure',) -f({"abcF" : {"": 1}}):ValueError:('empty keys are not allowed',) -f({"abcF" : {u"": 1}}):ValueError:('empty keys are not allowed',) -f({"abcF" : FailingMapping()}):NotImplementedError:('keys',) -f({"abcF" : FailingMappingKey()}):NotImplementedError:('getitem:mappingkey',) -f({"abcF" : FailingNumber()}):TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing StringToChars using f(Mapping({%s : 1})) -f(Mapping({1 : 1})):TypeError:('expected str() or unicode() instance, but got int',) -f(Mapping({u"\0" : 1})):TypeError:('expected string without null bytes',) -f(Mapping({"\0" : 1})):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using f(Mapping({"abcG" : {%s : 1}})) -f(Mapping({"abcG" : {1 : 1}})):TypeError:('expected str() or unicode() instance, but got int',) -f(Mapping({"abcG" : {u"\0" : 1}})):TypeError:('expected string without null bytes',) -f(Mapping({"abcG" : {"\0" : 1}})):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using f(Mapping({"abcG" : Mapping({%s : 1})})) -f(Mapping({"abcG" : Mapping({1 : 1})})):TypeError:('expected str() or unicode() instance, but got int',) -f(Mapping({"abcG" : Mapping({u"\0" : 1})})):TypeError:('expected string without null bytes',) -f(Mapping({"abcG" : Mapping({"\0" : 1})})):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using f(Mapping({"abcG" : %s})) -f(Mapping({"abcG" : FailingIter()})):TypeError:('unable to convert FailingIter to vim structure',) -f(Mapping({"abcG" : FailingIterNext()})):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using f(Mapping({"abcG" : %s})) -f(Mapping({"abcG" : None})):TypeError:('unable to convert NoneType to vim structure',) -f(Mapping({"abcG" : {"": 1}})):ValueError:('empty keys are not allowed',) -f(Mapping({"abcG" : {u"": 1}})):ValueError:('empty keys are not allowed',) -f(Mapping({"abcG" : FailingMapping()})):NotImplementedError:('keys',) -f(Mapping({"abcG" : FailingMappingKey()})):NotImplementedError:('getitem:mappingkey',) -f(Mapping({"abcG" : FailingNumber()})):TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing *Iter* using f(%s) -f(FailingIter()):TypeError:('unable to convert FailingIter to vim structure',) -f(FailingIterNext()):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using f(%s) -f(None):TypeError:('unable to convert NoneType to vim structure',) -f({"": 1}):ValueError:('empty keys are not allowed',) -f({u"": 1}):ValueError:('empty keys are not allowed',) -f(FailingMapping()):NotImplementedError:('keys',) -f(FailingMappingKey()):NotImplementedError:('getitem:mappingkey',) -f(FailingNumber()):TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing StringToChars using fd(self={%s : 1}) -fd(self={1 : 1}):TypeError:('expected str() or unicode() instance, but got int',) -fd(self={u"\0" : 1}):TypeError:('expected string without null bytes',) -fd(self={"\0" : 1}):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using fd(self={"abcF" : {%s : 1}}) -fd(self={"abcF" : {1 : 1}}):TypeError:('expected str() or unicode() instance, but got int',) -fd(self={"abcF" : {u"\0" : 1}}):TypeError:('expected string without null bytes',) -fd(self={"abcF" : {"\0" : 1}}):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using fd(self={"abcF" : Mapping({%s : 1})}) -fd(self={"abcF" : Mapping({1 : 1})}):TypeError:('expected str() or unicode() instance, but got int',) -fd(self={"abcF" : Mapping({u"\0" : 1})}):TypeError:('expected string without null bytes',) -fd(self={"abcF" : Mapping({"\0" : 1})}):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using fd(self={"abcF" : %s}) -fd(self={"abcF" : FailingIter()}):TypeError:('unable to convert FailingIter to vim structure',) -fd(self={"abcF" : FailingIterNext()}):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using fd(self={"abcF" : %s}) -fd(self={"abcF" : None}):TypeError:('unable to convert NoneType to vim structure',) -fd(self={"abcF" : {"": 1}}):ValueError:('empty keys are not allowed',) -fd(self={"abcF" : {u"": 1}}):ValueError:('empty keys are not allowed',) -fd(self={"abcF" : FailingMapping()}):NotImplementedError:('keys',) -fd(self={"abcF" : FailingMappingKey()}):NotImplementedError:('getitem:mappingkey',) -fd(self={"abcF" : FailingNumber()}):TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing StringToChars using fd(self=Mapping({%s : 1})) -fd(self=Mapping({1 : 1})):TypeError:('expected str() or unicode() instance, but got int',) -fd(self=Mapping({u"\0" : 1})):TypeError:('expected string without null bytes',) -fd(self=Mapping({"\0" : 1})):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using fd(self=Mapping({"abcG" : {%s : 1}})) -fd(self=Mapping({"abcG" : {1 : 1}})):TypeError:('expected str() or unicode() instance, but got int',) -fd(self=Mapping({"abcG" : {u"\0" : 1}})):TypeError:('expected string without null bytes',) -fd(self=Mapping({"abcG" : {"\0" : 1}})):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing StringToChars using fd(self=Mapping({"abcG" : Mapping({%s : 1})})) -fd(self=Mapping({"abcG" : Mapping({1 : 1})})):TypeError:('expected str() or unicode() instance, but got int',) -fd(self=Mapping({"abcG" : Mapping({u"\0" : 1})})):TypeError:('expected string without null bytes',) -fd(self=Mapping({"abcG" : Mapping({"\0" : 1})})):TypeError:('expected string without null bytes',) -<<< Finished ->>> Testing *Iter* using fd(self=Mapping({"abcG" : %s})) -fd(self=Mapping({"abcG" : FailingIter()})):TypeError:('unable to convert FailingIter to vim structure',) -fd(self=Mapping({"abcG" : FailingIterNext()})):NotImplementedError:('next',) -<<< Finished ->>> Testing ConvertFromPyObject using fd(self=Mapping({"abcG" : %s})) -fd(self=Mapping({"abcG" : None})):TypeError:('unable to convert NoneType to vim structure',) -fd(self=Mapping({"abcG" : {"": 1}})):ValueError:('empty keys are not allowed',) -fd(self=Mapping({"abcG" : {u"": 1}})):ValueError:('empty keys are not allowed',) -fd(self=Mapping({"abcG" : FailingMapping()})):NotImplementedError:('keys',) -fd(self=Mapping({"abcG" : FailingMappingKey()})):NotImplementedError:('getitem:mappingkey',) -fd(self=Mapping({"abcG" : FailingNumber()})):TypeError:('long() argument must be a string or a number',) -<<< Finished ->>> Testing *Iter* using fd(self=%s) -fd(self=FailingIter()):TypeError:('unable to convert FailingIter to vim dictionary',) -fd(self=FailingIterNext()):TypeError:('unable to convert FailingIterNext to vim dictionary',) -<<< Finished ->>> Testing ConvertFromPyObject using fd(self=%s) -fd(self=None):TypeError:('unable to convert NoneType to vim dictionary',) -fd(self={"": 1}):ValueError:('empty keys are not allowed',) -fd(self={u"": 1}):ValueError:('empty keys are not allowed',) -fd(self=FailingMapping()):NotImplementedError:('keys',) -fd(self=FailingMappingKey()):NotImplementedError:('getitem:mappingkey',) -fd(self=FailingNumber()):TypeError:('unable to convert FailingNumber to vim dictionary',) -<<< Finished ->>> Testing ConvertFromPyMapping using fd(self=%s) -fd(self=[]):TypeError:('unable to convert list to vim dictionary',) -<<< Finished -> TabPage ->> TabPageAttr -vim.current.tabpage.xxx:AttributeError:('xxx',) -> TabList ->> TabListItem -vim.tabpages[1000]:IndexError:('no such tab page',) -> Window ->> WindowAttr -vim.current.window.xxx:AttributeError:('xxx',) ->> WindowSetattr -vim.current.window.buffer = 0:TypeError:('readonly attribute: buffer',) -vim.current.window.cursor = (100000000, 100000000):error:('cursor position outside buffer',) -vim.current.window.cursor = True:TypeError:('argument must be 2-item sequence, not bool',) ->>> Testing NumberToLong using vim.current.window.height = %s -vim.current.window.height = []:TypeError:('expected int(), long() or something supporting coercing to long(), but got list',) -vim.current.window.height = None:TypeError:('expected int(), long() or something supporting coercing to long(), but got NoneType',) -vim.current.window.height = -1:ValueError:('number must be greater or equal to zero',) -<<< Finished ->>> Testing NumberToLong using vim.current.window.width = %s -vim.current.window.width = []:TypeError:('expected int(), long() or something supporting coercing to long(), but got list',) -vim.current.window.width = None:TypeError:('expected int(), long() or something supporting coercing to long(), but got NoneType',) -vim.current.window.width = -1:ValueError:('number must be greater or equal to zero',) -<<< Finished -vim.current.window.xxxxxx = True:AttributeError:('xxxxxx',) -> WinList ->> WinListItem -vim.windows[1000]:IndexError:('no such window',) -> Buffer ->> StringToLine (indirect) -vim.current.buffer[0] = u"\na":error:('string cannot contain newlines',) -vim.current.buffer[0] = "\na":error:('string cannot contain newlines',) ->> SetBufferLine (indirect) -vim.current.buffer[0] = True:TypeError:('bad argument type for built-in operation',) ->> SetBufferLineList (indirect) -vim.current.buffer[:] = True:TypeError:('bad argument type for built-in operation',) -vim.current.buffer[:] = ["\na", "bc"]:error:('string cannot contain newlines',) ->> InsertBufferLines (indirect) -vim.current.buffer.append(None):TypeError:('bad argument type for built-in operation',) -vim.current.buffer.append(["\na", "bc"]):error:('string cannot contain newlines',) -vim.current.buffer.append("\nbc"):error:('string cannot contain newlines',) ->> RBItem -vim.current.buffer[100000000]:IndexError:('line number out of range',) ->> RBAsItem -vim.current.buffer[100000000] = "":IndexError:('line number out of range',) ->> BufferAttr -vim.current.buffer.xxx:AttributeError:('xxx',) ->> BufferSetattr -vim.current.buffer.name = True:TypeError:('expected str() or unicode() instance, but got bool',) -vim.current.buffer.xxx = True:AttributeError:('xxx',) ->> BufferMark -vim.current.buffer.mark(0):TypeError:('expected str() or unicode() instance, but got int',) -vim.current.buffer.mark("abcM"):ValueError:('mark name must be a single character',) -vim.current.buffer.mark("!"):error:('invalid mark name',) ->> BufferRange -vim.current.buffer.range(1, 2, 3):TypeError:('function takes exactly 2 arguments (3 given)',) -> BufMap ->> BufMapItem -vim.buffers[100000000]:KeyError:(100000000,) ->>> Testing NumberToLong using vim.buffers[%s] -vim.buffers[[]]:TypeError:('expected int(), long() or something supporting coercing to long(), but got list',) -vim.buffers[None]:TypeError:('expected int(), long() or something supporting coercing to long(), but got NoneType',) -vim.buffers[-1]:ValueError:('number must be greater then zero',) -vim.buffers[0]:ValueError:('number must be greater then zero',) -<<< Finished -> Current ->> CurrentGetattr -vim.current.xxx:AttributeError:('xxx',) ->> CurrentSetattr -vim.current.line = True:TypeError:('bad argument type for built-in operation',) -vim.current.buffer = True:TypeError:('expected vim.Buffer object, but got bool',) -vim.current.window = True:TypeError:('expected vim.Window object, but got bool',) -vim.current.tabpage = True:TypeError:('expected vim.TabPage object, but got bool',) -vim.current.xxx = True:AttributeError:('xxx',) -['/testdir'] -'/testdir' -2,xx -before -after -pythonx/topmodule/__init__.py -pythonx/topmodule/submodule/__init__.py -pythonx/topmodule/submodule/subsubmodule/subsubsubmodule.py -vim.command("throw 'abcN'"):error:('abcN',) -Exe("throw 'def'"):error:('def',) -vim.eval("Exe('throw ''ghi''')"):error:('ghi',) -vim.eval("Exe('echoerr ''jkl''')"):error:('Vim(echoerr):jkl',) -vim.eval("Exe('xxx_non_existent_command_xxx')"):error:('Vim:E492: Not an editor command: xxx_non_existent_command_xxx',) -vim.eval("xxx_unknown_function_xxx()"):error:('Vim:E117: Unknown function: xxx_unknown_function_xxx',) -vim.bindeval("Exe('xxx_non_existent_command_xxx')"):error:('Vim:E492: Not an editor command: xxx_non_existent_command_xxx',) -Caught KeyboardInterrupt -Running :put -No exception - diff --git a/src/nvim/testdir/test87.in b/src/nvim/testdir/test87.in deleted file mode 100644 index cad778e858..0000000000 --- a/src/nvim/testdir/test87.in +++ /dev/null @@ -1,1399 +0,0 @@ -Tests for various python features. vim: set ft=vim : - -STARTTEST -:so small.vim -:set noswapfile -:if !has('python3') | e! test.ok | wq! test.out | endif -:lang C -:fun Test() -:py3 import vim -:let l = [] -:py3 l=vim.bindeval('l') -:py3 f=vim.bindeval('function("strlen")') -:" Extending List directly with different types -:py3 l+=[1, "as'd", [1, 2, f, {'a': 1}]] -:$put =string(l) -:$put =string(l[-1]) -:try -: $put =string(l[-4]) -:catch -: $put =v:exception[:13] -:endtry -:" List assignment -:py3 l[0]=0 -:$put =string(l) -:py3 l[-2]=f -:$put =string(l) -:" -:" Extending Dictionary directly with different types -:let d = {} -:fun d.f() -: return 1 -:endfun -py3 << EOF -d=vim.bindeval('d') -d['1']='asd' -d.update(b=[1, 2, f]) -d.update((('-1', {'a': 1}),)) -d.update({'0': -1}) -dk = d.keys() -dv = d.values() -di = d.items() -dk.sort(key=repr) -dv.sort(key=repr) -di.sort(key=repr) -EOF -:$put =py3eval('d[''f''](self={})') -:$put =py3eval('repr(dk)') -:$put =substitute(py3eval('repr(dv)'),'0x\x\+','','g') -:$put =substitute(py3eval('repr(di)'),'0x\x\+','','g') -:for [key, Val] in sort(items(d)) -: $put =string(key) . ' : ' . string(Val) -: unlet key Val -:endfor -:py3 del dk -:py3 del di -:py3 del dv -:" -:" removing items with del -:py3 del l[2] -:$put =string(l) -:let l = range(8) -:py3 l=vim.bindeval('l') -:try -: py3 del l[:3] -: py3 del l[1:] -:catch -: $put =v:exception -:endtry -:$put =string(l) -:" -:py3 del d['-1'] -:py3 del d['f'] -:$put =string(py3eval('d.get(''b'', 1)')) -:$put =string(py3eval('d.pop(''b'')')) -:$put =string(py3eval('d.get(''b'', 1)')) -:$put =string(py3eval('d.pop(''1'', 2)')) -:$put =string(py3eval('d.pop(''1'', 2)')) -:$put =py3eval('repr(d.has_key(''0''))') -:$put =py3eval('repr(d.has_key(''1''))') -:$put =py3eval('repr(''0'' in d)') -:$put =py3eval('repr(''1'' in d)') -:$put =py3eval('repr(list(iter(d)))') -:$put =string(d) -:$put =py3eval('repr(d.popitem())') -:$put =py3eval('repr(d.get(''0''))') -:$put =py3eval('repr(list(iter(d)))') -:" -:" removing items out of range: silently skip items that don't exist -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:" The following two ranges delete nothing as they match empty list: -:py3 del l[2:1] -:$put =string(l) -:py3 del l[2:2] -:$put =string(l) -:py3 del l[2:3] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 del l[2:4] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 del l[2:5] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 del l[2:6] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:" The following two ranges delete nothing as they match empty list: -:py3 del l[-1:2] -:$put =string(l) -:py3 del l[-2:2] -:$put =string(l) -:py3 del l[-3:2] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 del l[-4:2] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 del l[-5:2] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 del l[-6:2] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 del l[::2] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 del l[3:0:-2] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 del l[2:4:-2] -:$put =string(l) -:" -:" Slice assignment to a list -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 l[0:0]=['a'] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 l[1:2]=['b'] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 l[2:4]=['c'] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 l[4:4]=['d'] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 l[-1:2]=['e'] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 l[-10:2]=['f'] -:$put =string(l) -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:py3 l[2:-10]=['g'] -:$put =string(l) -:let l = [] -:py3 l=vim.bindeval('l') -:py3 l[0:0]=['h'] -:$put =string(l) -:let l = range(8) -:py3 l=vim.bindeval('l') -:py3 l[2:6:2] = [10, 20] -:$put =string(l) -:let l = range(8) -:py3 l=vim.bindeval('l') -:py3 l[6:2:-2] = [10, 20] -:$put =string(l) -:let l = range(8) -:py3 l=vim.bindeval('l') -:py3 l[6:2] = () -:$put =string(l) -:let l = range(8) -:py3 l=vim.bindeval('l') -:py3 l[6:2:1] = () -:$put =string(l) -:let l = range(8) -:py3 l=vim.bindeval('l') -:py3 l[2:2:1] = () -:$put =string(l) -:" -:" Locked variables -:let l = [0, 1, 2, 3] -:py3 l=vim.bindeval('l') -:lockvar! l -:py3 l[2]='i' -:$put =string(l) -:unlockvar! l -:" -:" Function calls -py3 << EOF -import sys -import re - -py33_type_error_pattern = re.compile('^__call__\(\) takes (\d+) positional argument but (\d+) were given$') - -def ee(expr, g=globals(), l=locals()): - cb = vim.current.buffer - try: - try: - exec(expr, g, l) - except Exception as e: - if sys.version_info >= (3, 3) and e.__class__ is AttributeError and str(e).find('has no attribute')>=0 and not str(e).startswith("'vim."): - cb.append(expr + ':' + repr((e.__class__, AttributeError(str(e)[str(e).rfind(" '") + 2:-1])))) - elif sys.version_info >= (3, 3) and e.__class__ is ImportError and str(e).find('No module named \'') >= 0: - cb.append(expr + ':' + repr((e.__class__, ImportError(str(e).replace("'", ''))))) - elif sys.version_info >= (3, 3) and e.__class__ is TypeError: - m = py33_type_error_pattern.search(str(e)) - if m: - msg = '__call__() takes exactly {0} positional argument ({1} given)'.format(m.group(1), m.group(2)) - cb.append(expr + ':' + repr((e.__class__, TypeError(msg)))) - else: - cb.append(expr + ':' + repr((e.__class__, e))) - else: - cb.append(expr + ':' + repr((e.__class__, e))) - else: - cb.append(expr + ':NOT FAILED') - except Exception as e: - cb.append(expr + '::' + repr((e.__class__, e))) -EOF -:fun New(...) -: return ['NewStart']+a:000+['NewEnd'] -:endfun -:fun DictNew(...) dict -: return ['DictNewStart']+a:000+['DictNewEnd', self] -:endfun -:let l=[function('New'), function('DictNew')] -:py3 l=vim.bindeval('l') -:py3 l.extend(list(l[0](1, 2, 3))) -:$put =string(l) -:py3 l.extend(list(l[1](1, 2, 3, self={'a': 'b'}))) -:$put =string(l) -:py3 l+=[l[0].name] -:$put =string(l) -:py3 ee('l[1](1, 2, 3)') -:py3 f=l[0] -:delfunction New -:py3 ee('f(1, 2, 3)') -:let l=[0.0] -:py3 l=vim.bindeval('l') -:py3 l.extend([0.0]) -:$put =string(l) -:let messages=[] -:delfunction DictNew -py3 <<EOF -d=vim.bindeval('{}') -m=vim.bindeval('messages') -def em(expr, g=globals(), l=locals()): - try: - exec(expr, g, l) - except Exception as e: - m.extend([e.__class__.__name__]) - -em('d["abc1"]') -em('d["abc1"]="\\0"') -em('d["abc1"]=vim') -em('d[""]=1') -em('d["a\\0b"]=1') -em('d[b"a\\0b"]=1') - -em('d.pop("abc1")') -em('d.popitem()') -del em -del m -EOF -:$put =messages -:unlet messages -:" locked and scope attributes -:let d={} | let dl={} | lockvar dl -:for s in split("d dl v: g:") -: let name=tr(s, ':', 's') -: execute 'py3 '.name.'=vim.bindeval("'.s.'")' -: let toput=s.' : '.join(map(['locked', 'scope'], 'v:val.":".py3eval(name.".".v:val)'), ';') -: $put =toput -:endfor -:silent! let d.abc2=1 -:silent! let dl.abc3=1 -:py3 d.locked=True -:py3 dl.locked=False -:silent! let d.def=1 -:silent! let dl.def=1 -:put ='d:'.string(d) -:put ='dl:'.string(dl) -:unlet d dl -: -:let l=[] | let ll=[] | lockvar ll -:for s in split("l ll") -: let name=tr(s, ':', 's') -: execute 'py3 '.name.'=vim.bindeval("'.s.'")' -: let toput=s.' : locked:'.py3eval(name.'.locked') -: $put =toput -:endfor -:silent! call extend(l, [0]) -:silent! call extend(ll, [0]) -:py3 l.locked=True -:py3 ll.locked=False -:silent! call extend(l, [1]) -:silent! call extend(ll, [1]) -:put ='l:'.string(l) -:put ='ll:'.string(ll) -:unlet l ll -:" -:" py3eval() -:let l=py3eval('[0, 1, 2]') -:$put =string(l) -:let d=py3eval('{"a": "b", "c": 1, "d": ["e"]}') -:$put =sort(items(d)) -:let f=py3eval('0.0') -:$put =string(f) -:" Invalid values: -:for e in ['"\0"', '{"\0": 1}', 'undefined_name', 'vim'] -: try -: let v=py3eval(e) -: catch -: let toput=e.":\t".v:exception[:13] -: $put =toput -: endtry -:endfor -:" -:" threading -:let l = [0] -:py3 l=vim.bindeval('l') -py3 <<EOF -import threading -import time - -class T(threading.Thread): - def __init__(self): - threading.Thread.__init__(self) - self.t = 0 - self.running = True - - def run(self): - while self.running: - self.t += 1 - time.sleep(0.1) - -t = T() -del T -t.start() -EOF -:sleep 1 -:py3 t.running = False -:py3 t.join() -:py3 l[0] = t.t > 8 # check if the background thread is working -:py3 del time -:py3 del threading -:py3 del t -:$put =string(l) -:" -:" settrace -:let l = [] -:py3 l=vim.bindeval('l') -py3 <<EOF -import sys - -def traceit(frame, event, arg): - global l - if event == "line": - l += [frame.f_lineno] - return traceit - -def trace_main(): - for i in range(5): - pass -EOF -:py3 sys.settrace(traceit) -:py3 trace_main() -:py3 sys.settrace(None) -:py3 del traceit -:py3 del trace_main -:$put =string(l) -:" -:" Slice -:py3 ll = vim.bindeval('[0, 1, 2, 3, 4, 5]') -:py3 l = ll[:4] -:$put =string(py3eval('l')) -:py3 l = ll[2:] -:$put =string(py3eval('l')) -:py3 l = ll[:-4] -:$put =string(py3eval('l')) -:py3 l = ll[-2:] -:$put =string(py3eval('l')) -:py3 l = ll[2:4] -:$put =string(py3eval('l')) -:py3 l = ll[4:2] -:$put =string(py3eval('l')) -:py3 l = ll[-4:-2] -:$put =string(py3eval('l')) -:py3 l = ll[-2:-4] -:$put =string(py3eval('l')) -:py3 l = ll[:] -:$put =string(py3eval('l')) -:py3 l = ll[0:6] -:$put =string(py3eval('l')) -:py3 l = ll[-10:10] -:$put =string(py3eval('l')) -:py3 l = ll[4:2:-1] -:$put =string(py3eval('l')) -:py3 l = ll[::2] -:$put =string(py3eval('l')) -:py3 l = ll[4:2:1] -:$put =string(py3eval('l')) -:py3 del l -:" -:" Vars -:let g:foo = 'bac' -:let w:abc3 = 'def' -:let b:baz = 'bar' -:let t:bar = 'jkl' -:try -: throw "Abc" -:catch -: put =py3eval('vim.vvars[''exception'']') -:endtry -:put =py3eval('vim.vars[''foo'']') -:put =py3eval('vim.current.window.vars[''abc3'']') -:put =py3eval('vim.current.buffer.vars[''baz'']') -:put =py3eval('vim.current.tabpage.vars[''bar'']') -:" -:" Options -:" paste: boolean, global -:" previewheight number, global -:" operatorfunc: string, global -:" number: boolean, window-local -:" numberwidth: number, window-local -:" colorcolumn: string, window-local -:" statusline: string, window-local/global -:" autoindent: boolean, buffer-local -:" shiftwidth: number, buffer-local -:" omnifunc: string, buffer-local -:" preserveindent: boolean, buffer-local/global -:" path: string, buffer-local/global -:let g:bufs=[bufnr('%')] -:new -:let g:bufs+=[bufnr('%')] -:vnew -:let g:bufs+=[bufnr('%')] -:wincmd j -:vnew -:let g:bufs+=[bufnr('%')] -:wincmd l -:fun RecVars(opt) -: let gval =string(eval('&g:'.a:opt)) -: let wvals=join(map(range(1, 4), 'v:val.":".string(getwinvar(v:val, "&".a:opt))')) -: let bvals=join(map(copy(g:bufs), 'v:val.":".string(getbufvar(v:val, "&".a:opt))')) -: put =' G: '.gval -: put =' W: '.wvals -: put =' B: '.wvals -:endfun -py3 << EOF -def e(s, g=globals(), l=locals()): - try: - exec(s, g, l) - except Exception as e: - vim.command('return ' + repr(e.__class__.__name__)) - -def ev(s, g=globals(), l=locals()): - try: - return eval(s, g, l) - except Exception as e: - vim.command('let exc=' + repr(e.__class__.__name__)) - return 0 -EOF -:fun E(s) -: python3 e(vim.eval('a:s')) -:endfun -:fun Ev(s) -: let r=py3eval('ev(vim.eval("a:s"))') -: if exists('exc') -: throw exc -: endif -: return r -:endfun -:py3 gopts1=vim.options -:py3 wopts1=vim.windows[2].options -:py3 wopts2=vim.windows[0].options -:py3 wopts3=vim.windows[1].options -:py3 bopts1=vim.buffers[vim.bindeval("g:bufs")[2]].options -:py3 bopts2=vim.buffers[vim.bindeval("g:bufs")[1]].options -:py3 bopts3=vim.buffers[vim.bindeval("g:bufs")[0]].options -:$put ='wopts iters equal: '.py3eval('list(wopts1) == list(wopts2)') -:$put ='bopts iters equal: '.py3eval('list(bopts1) == list(bopts2)') -:py3 gset=set(iter(gopts1)) -:py3 wset=set(iter(wopts1)) -:py3 bset=set(iter(bopts1)) -:set path=.,..,, -:let lst=[] -:let lst+=[['paste', 1, 0, 1, 2, 1, 1, 0 ]] -:let lst+=[['previewheight', 5, 1, 6, 'a', 0, 1, 0 ]] -:let lst+=[['operatorfunc', 'A', 'B', 'C', 2, 0, 1, 0 ]] -:let lst+=[['number', 0, 1, 1, 0, 1, 0, 1 ]] -:let lst+=[['numberwidth', 2, 3, 5, -100, 0, 0, 1 ]] -:let lst+=[['colorcolumn', '+1', '+2', '+3', 'abc4', 0, 0, 1 ]] -:let lst+=[['statusline', '1', '2', '4', 0, 0, 1, 1 ]] -:let lst+=[['autoindent', 0, 1, 1, 2, 1, 0, 2 ]] -:let lst+=[['shiftwidth', 0, 2, 1, 3, 0, 0, 2 ]] -:let lst+=[['omnifunc', 'A', 'B', 'C', 1, 0, 0, 2 ]] -:let lst+=[['preserveindent', 0, 1, 1, 2, 1, 1, 2 ]] -:let lst+=[['path', '.,,', ',,', '.', 0, 0, 1, 2 ]] -:for [oname, oval1, oval2, oval3, invval, bool, global, local] in lst -: py3 oname=vim.eval('oname') -: py3 oval1=vim.bindeval('oval1') -: py3 oval2=vim.bindeval('oval2') -: py3 oval3=vim.bindeval('oval3') -: if invval is 0 || invval is 1 -: py3 invval=bool(vim.bindeval('invval')) -: else -: py3 invval=vim.bindeval('invval') -: endif -: if bool -: py3 oval1=bool(oval1) -: py3 oval2=bool(oval2) -: py3 oval3=bool(oval3) -: endif -: put ='>>> '.oname -: $put =' g/w/b:'.py3eval('oname in gset').'/'.py3eval('oname in wset').'/'.py3eval('oname in bset') -: $put =' g/w/b (in):'.py3eval('oname in gopts1').'/'.py3eval('oname in wopts1').'/'.py3eval('oname in bopts1') -: for v in ['gopts1', 'wopts1', 'bopts1'] -: try -: put =' p/'.v.': '.Ev('repr('.v.'['''.oname.'''])') -: catch -: put =' p/'.v.'! '.v:exception -: endtry -: let r=E(v.'['''.oname.''']=invval') -: if r isnot 0 -: put =' inv: '.string(invval).'! '.r -: endif -: for vv in (v is# 'gopts1' ? [v] : [v, v[:-2].'2', v[:-2].'3']) -: let val=substitute(vv, '^.opts', 'oval', '') -: let r=E(vv.'['''.oname.''']='.val) -: if r isnot 0 -: put =' '.vv.'! '.r -: endif -: endfor -: endfor -: call RecVars(oname) -: for v in ['wopts3', 'bopts3'] -: let r=E('del '.v.'["'.oname.'"]') -: if r isnot 0 -: put =' del '.v.'! '.r -: endif -: endfor -: call RecVars(oname) -:endfor -:delfunction RecVars -:delfunction E -:delfunction Ev -:py3 del ev -:py3 del e -:only -:for buf in g:bufs[1:] -: execute 'bwipeout!' buf -:endfor -:py3 del gopts1 -:py3 del wopts1 -:py3 del wopts2 -:py3 del wopts3 -:py3 del bopts1 -:py3 del bopts2 -:py3 del bopts3 -:py3 del oval1 -:py3 del oval2 -:py3 del oval3 -:py3 del oname -:py3 del invval -:" -:" Test buffer object -:vnew -:put ='First line' -:put ='Second line' -:put ='Third line' -:1 delete _ -:py3 b=vim.current.buffer -:wincmd w -:mark a -:augroup BUFS -: autocmd BufFilePost * python3 cb.append(vim.eval('expand("<abuf>")') + ':BufFilePost:' + vim.eval('bufnr("%")')) -: autocmd BufFilePre * python3 cb.append(vim.eval('expand("<abuf>")') + ':BufFilePre:' + vim.eval('bufnr("%")')) -:augroup END -py3 << EOF -cb = vim.current.buffer -# Tests BufferAppend and BufferItem -cb.append(b[0]) -# Tests BufferSlice and BufferAssSlice -cb.append('abc5') # Will be overwritten -cb[-1:] = b[:-2] -# Test BufferLength and BufferAssSlice -cb.append('def') # Will not be overwritten -cb[len(cb):] = b[:] -# Test BufferAssItem and BufferMark -cb.append('ghi') # Will be overwritten -cb[-1] = repr((len(cb) - cb.mark('a')[0], cb.mark('a')[1])) -# Test BufferRepr -cb.append(repr(cb) + repr(b)) -# Modify foreign buffer -b.append('foo') -b[0]='bar' -b[0:0]=['baz'] -vim.command('call append("$", getbufline(%i, 1, "$"))' % b.number) -# Test assigning to name property -import os -old_name = cb.name -cb.name = 'foo' -cb.append(cb.name[-11:].replace(os.path.sep, '/')) -b.name = 'bar' -cb.append(b.name[-11:].replace(os.path.sep, '/')) -cb.name = old_name -cb.append(cb.name[-17:].replace(os.path.sep, '/')) -del old_name -# Test CheckBuffer -for _b in vim.buffers: - if _b is not cb: - vim.command('bwipeout! ' + str(_b.number)) -del _b -cb.append('valid: b:%s, cb:%s' % (repr(b.valid), repr(cb.valid))) -for expr in ('b[1]','b[:] = ["A", "B"]','b[:]','b.append("abc6")'): - try: - exec(expr) - except vim.error: - pass - else: - # Usually a SEGV here - # Should not happen in any case - cb.append('No exception for ' + expr) -vim.command('cd .') -del b -EOF -:" -:" Test vim.buffers object -:set hidden -:edit a -:buffer # -:edit b -:buffer # -:edit c -:buffer # -py3 << EOF -# Check GCing iterator that was not fully exhausted -i = iter(vim.buffers) -cb.append('i:' + str(next(i))) -# and also check creating more then one iterator at a time -i2 = iter(vim.buffers) -cb.append('i2:' + str(next(i2))) -cb.append('i:' + str(next(i))) -# The following should trigger GC and not cause any problems -del i -del i2 -i3 = iter(vim.buffers) -cb.append('i3:' + str(next(i3))) -del i3 - -prevnum = 0 -for b in vim.buffers: - # Check buffer order - if prevnum >= b.number: - cb.append('!!! Buffer numbers not in strictly ascending order') - # Check indexing: vim.buffers[number].number == number - cb.append(str(b.number) + ':' + repr(vim.buffers[b.number]) + '=' + repr(b)) - prevnum = b.number -del prevnum - -cb.append(str(len(vim.buffers))) - -bnums = list(map(lambda b: b.number, vim.buffers))[1:] - -# Test wiping out buffer with existing iterator -i4 = iter(vim.buffers) -cb.append('i4:' + str(next(i4))) -vim.command('bwipeout! ' + str(bnums.pop(0))) -try: - next(i4) -except vim.error: - pass -else: - cb.append('!!!! No vim.error') -i4 = iter(vim.buffers) -vim.command('bwipeout! ' + str(bnums.pop(-1))) -vim.command('bwipeout! ' + str(bnums.pop(-1))) -cb.append('i4:' + str(next(i4))) -try: - next(i4) -except StopIteration: - cb.append('StopIteration') -del i4 -del bnums -EOF -:" -:" Test vim.{tabpage,window}list and vim.{tabpage,window} objects -:tabnew 0 -:tabnew 1 -:vnew a.1 -:tabnew 2 -:vnew a.2 -:vnew b.2 -:vnew c.2 -py3 << EOF -cb.append('Number of tabs: ' + str(len(vim.tabpages))) -cb.append('Current tab pages:') - -def W(w): - if '(unknown)' in repr(w): - return '<window object (unknown)>' - else: - return repr(w) - -def Cursor(w, start=len(cb)): - if w.buffer is cb: - return repr((start - w.cursor[0], w.cursor[1])) - else: - return repr(w.cursor) - -for t in vim.tabpages: - cb.append(' ' + repr(t) + '(' + str(t.number) + ')' + ': ' + str(len(t.windows)) + ' windows, current is ' + W(t.window)) - cb.append(' Windows:') - for w in t.windows: - cb.append(' ' + W(w) + '(' + str(w.number) + ')' + ': displays buffer ' + repr(w.buffer) + '; cursor is at ' + Cursor(w)) - # Other values depend on the size of the terminal, so they are checked partly: - for attr in ('height', 'row', 'width', 'col'): - try: - aval = getattr(w, attr) - if type(aval) is not int: - raise TypeError - if aval < 0: - raise ValueError - except Exception as e: - cb.append('!!!!!! Error while getting attribute ' + attr + ': ' + e.__class__.__name__) - del aval - del attr - w.cursor = (len(w.buffer), 0) -del W -del Cursor -cb.append('Number of windows in current tab page: ' + str(len(vim.windows))) -if list(vim.windows) != list(vim.current.tabpage.windows): - cb.append('!!!!!! Windows differ') -EOF -:" -:" Test vim.current -py3 << EOF -def H(o): - return repr(o) -cb.append('Current tab page: ' + repr(vim.current.tabpage)) -cb.append('Current window: ' + repr(vim.current.window) + ': ' + H(vim.current.window) + ' is ' + H(vim.current.tabpage.window)) -cb.append('Current buffer: ' + repr(vim.current.buffer) + ': ' + H(vim.current.buffer) + ' is ' + H(vim.current.window.buffer)+ ' is ' + H(vim.current.tabpage.window.buffer)) -del H -# Assigning: fails -try: - vim.current.window = vim.tabpages[0].window -except ValueError: - cb.append('ValueError at assigning foreign tab window') - -for attr in ('window', 'tabpage', 'buffer'): - try: - setattr(vim.current, attr, None) - except TypeError: - cb.append('Type error at assigning None to vim.current.' + attr) -del attr - -# Assigning: success -vim.current.tabpage = vim.tabpages[-2] -vim.current.buffer = cb -vim.current.window = vim.windows[0] -vim.current.window.cursor = (len(vim.current.buffer), 0) -cb.append('Current tab page: ' + repr(vim.current.tabpage)) -cb.append('Current window: ' + repr(vim.current.window)) -cb.append('Current buffer: ' + repr(vim.current.buffer)) -cb.append('Current line: ' + repr(vim.current.line)) -ws = list(vim.windows) -ts = list(vim.tabpages) -for b in vim.buffers: - if b is not cb: - vim.command('bwipeout! ' + str(b.number)) -del b -cb.append('w.valid: ' + repr([w.valid for w in ws])) -cb.append('t.valid: ' + repr([t.valid for t in ts])) -del w -del t -del ts -del ws -EOF -:tabonly! -:only! -:" -:" Test types -py3 << EOF -for expr, attr in ( - ('vim.vars', 'Dictionary'), - ('vim.options', 'Options'), - ('vim.bindeval("{}")', 'Dictionary'), - ('vim.bindeval("[]")', 'List'), - ('vim.bindeval("function(\'tr\')")', 'Function'), - ('vim.current.buffer', 'Buffer'), - ('vim.current.range', 'Range'), - ('vim.current.window', 'Window'), - ('vim.current.tabpage', 'TabPage'), -): - cb.append(expr + ':' + attr + ':' + repr(type(eval(expr)) is getattr(vim, attr))) -del expr -del attr -EOF -:" -:" Test __dir__() method -py3 << EOF -for name, o in ( - ('current', vim.current), - ('buffer', vim.current.buffer), - ('window', vim.current.window), - ('tabpage', vim.current.tabpage), - ('range', vim.current.range), - ('dictionary', vim.bindeval('{}')), - ('list', vim.bindeval('[]')), - ('function', vim.bindeval('function("tr")')), - ('output', sys.stdout), - ): - cb.append(name + ':' + ','.join(dir(o))) -del name -del o -EOF -:" -:" Test vim.*.__new__ -:$put =string(py3eval('vim.Dictionary({})')) -:$put =string(py3eval('vim.Dictionary(a=1)')) -:$put =string(py3eval('vim.Dictionary(((''a'', 1),))')) -:$put =string(py3eval('vim.List()')) -:$put =string(py3eval('vim.List(iter(''abc7''))')) -:$put =string(py3eval('vim.Function(''tr'')')) -:" -:" Test stdout/stderr -:redir => messages -:py3 sys.stdout.write('abc8') ; sys.stdout.write('def') -:py3 sys.stderr.write('abc9') ; sys.stderr.write('def') -:py3 sys.stdout.writelines(iter('abcA')) -:py3 sys.stderr.writelines(iter('abcB')) -:redir END -:$put =string(substitute(messages, '\d\+', '', 'g')) -:" Test subclassing -:fun Put(...) -: $put =string(a:000) -: return a:000 -:endfun -py3 << EOF -class DupDict(vim.Dictionary): - def __setitem__(self, key, value): - super(DupDict, self).__setitem__(key, value) - super(DupDict, self).__setitem__('dup_' + key, value) -dd = DupDict() -dd['a'] = 'b' - -class DupList(vim.List): - def __getitem__(self, idx): - return [super(DupList, self).__getitem__(idx)] * 2 - -dl = DupList() -dl2 = DupList(iter('abcC')) -dl.extend(dl2[0]) - -class DupFun(vim.Function): - def __call__(self, arg): - return super(DupFun, self).__call__(arg, arg) - -df = DupFun('Put') -EOF -:$put =string(sort(keys(py3eval('dd')))) -:$put =string(py3eval('dl')) -:$put =string(py3eval('dl2')) -:$put =string(py3eval('df(2)')) -:$put =string(py3eval('dl') is# py3eval('dl')) -:$put =string(py3eval('dd') is# py3eval('dd')) -:$put =string(py3eval('df')) -:delfunction Put -py3 << EOF -del DupDict -del DupList -del DupFun -del dd -del dl -del dl2 -del df -EOF -:" -:" Test chdir -py3 << EOF -import os -fnamemodify = vim.Function('fnamemodify') -cb.append(str(fnamemodify('.', ':p:h:t'))) -cb.append(vim.eval('@%')) -os.chdir('..') -cb.append(str(fnamemodify('.', ':p:h:t'))) -cb.append(vim.eval('@%').replace(os.path.sep, '/')) -os.chdir('testdir') -cb.append(str(fnamemodify('.', ':p:h:t'))) -cb.append(vim.eval('@%')) -del fnamemodify -EOF -:" -:" Test errors -:fun F() dict -:endfun -:fun D() -:endfun -py3 << EOF -d = vim.Dictionary() -ned = vim.Dictionary(foo='bar', baz='abcD') -dl = vim.Dictionary(a=1) -dl.locked = True -l = vim.List() -ll = vim.List('abcE') -ll.locked = True -nel = vim.List('abcO') -f = vim.Function('string') -fd = vim.Function('F') -fdel = vim.Function('D') -vim.command('delfunction D') - -def subexpr_test(expr, name, subexprs): - cb.append('>>> Testing %s using %s' % (name, expr)) - for subexpr in subexprs: - ee(expr % subexpr) - cb.append('<<< Finished') - -def stringtochars_test(expr): - return subexpr_test(expr, 'StringToChars', ( - '1', # Fail type checks - 'b"\\0"', # Fail PyString_AsStringAndSize(object, , NULL) check - '"\\0"', # Fail PyString_AsStringAndSize(bytes, , NULL) check - )) - -class Mapping(object): - def __init__(self, d): - self.d = d - - def __getitem__(self, key): - return self.d[key] - - def keys(self): - return self.d.keys() - - def items(self): - return self.d.items() - -def convertfrompyobject_test(expr, recurse=True): - # pydict_to_tv - stringtochars_test(expr % '{%s : 1}') - if recurse: - convertfrompyobject_test(expr % '{"abcF" : %s}', False) - # pymap_to_tv - stringtochars_test(expr % 'Mapping({%s : 1})') - if recurse: - convertfrompyobject_test(expr % 'Mapping({"abcG" : %s})', False) - # pyseq_to_tv - iter_test(expr) - return subexpr_test(expr, 'ConvertFromPyObject', ( - 'None', # Not conversible - '{b"": 1}', # Empty key not allowed - '{"": 1}', # Same, but with unicode object - 'FailingMapping()', # - 'FailingMappingKey()', # - 'FailingNumber()', # - )) - -def convertfrompymapping_test(expr): - convertfrompyobject_test(expr) - return subexpr_test(expr, 'ConvertFromPyMapping', ( - '[]', - )) - -def iter_test(expr): - return subexpr_test(expr, '*Iter*', ( - 'FailingIter()', - 'FailingIterNext()', - )) - -def number_test(expr, natural=False, unsigned=False): - if natural: - unsigned = True - return subexpr_test(expr, 'NumberToLong', ( - '[]', - 'None', - ) + (('-1',) if unsigned else ()) - + (('0',) if natural else ())) - -class FailingTrue(object): - def __bool__(self): - raise NotImplementedError('bool') - -class FailingIter(object): - def __iter__(self): - raise NotImplementedError('iter') - -class FailingIterNext(object): - def __iter__(self): - return self - - def __next__(self): - raise NotImplementedError('next') - -class FailingIterNextN(object): - def __init__(self, n): - self.n = n - - def __iter__(self): - return self - - def __next__(self): - if self.n: - self.n -= 1 - return 1 - else: - raise NotImplementedError('next N') - -class FailingMappingKey(object): - def __getitem__(self, item): - raise NotImplementedError('getitem:mappingkey') - - def keys(self): - return list("abcH") - -class FailingMapping(object): - def __getitem__(self): - raise NotImplementedError('getitem:mapping') - - def keys(self): - raise NotImplementedError('keys') - -class FailingList(list): - def __getitem__(self, idx): - if i == 2: - raise NotImplementedError('getitem:list') - else: - return super(FailingList, self).__getitem__(idx) - -class NoArgsCall(object): - def __call__(self): - pass - -class FailingCall(object): - def __call__(self, path): - raise NotImplementedError('call') - -class FailingNumber(object): - def __int__(self): - raise NotImplementedError('int') - -cb.append("> Output") -cb.append(">> OutputSetattr") -ee('del sys.stdout.softspace') -number_test('sys.stdout.softspace = %s', unsigned=True) -number_test('sys.stderr.softspace = %s', unsigned=True) -ee('sys.stdout.attr = None') -cb.append(">> OutputWrite") -ee('sys.stdout.write(None)') -cb.append(">> OutputWriteLines") -ee('sys.stdout.writelines(None)') -ee('sys.stdout.writelines([1])') -iter_test('sys.stdout.writelines(%s)') -cb.append("> VimCommand") -stringtochars_test('vim.command(%s)') -ee('vim.command("", 2)') -#! Not checked: vim->python exceptions translating: checked later -cb.append("> VimToPython") -#! Not checked: everything: needs errors in internal python functions -cb.append("> VimEval") -stringtochars_test('vim.eval(%s)') -ee('vim.eval("", FailingTrue())') -#! Not checked: everything: needs errors in internal python functions -cb.append("> VimEvalPy") -stringtochars_test('vim.bindeval(%s)') -ee('vim.eval("", 2)') -#! Not checked: vim->python exceptions translating: checked later -cb.append("> VimStrwidth") -stringtochars_test('vim.strwidth(%s)') -cb.append("> VimForeachRTP") -ee('vim.foreach_rtp(None)') -ee('vim.foreach_rtp(NoArgsCall())') -ee('vim.foreach_rtp(FailingCall())') -ee('vim.foreach_rtp(int, 2)') -cb.append('> import') -old_rtp = vim.options['rtp'] -vim.options['rtp'] = os.getcwd().replace(',', '\\,').replace('\\', '\\\\') -ee('import xxx_no_such_module_xxx') -ee('import failing_import') -ee('import failing') -vim.options['rtp'] = old_rtp -del old_rtp -cb.append("> Options") -cb.append(">> OptionsItem") -ee('vim.options["abcQ"]') -ee('vim.options[""]') -stringtochars_test('vim.options[%s]') -cb.append(">> OptionsContains") -stringtochars_test('%s in vim.options') -cb.append("> Dictionary") -cb.append(">> DictionaryConstructor") -ee('vim.Dictionary("abcI")') -##! Not checked: py_dict_alloc failure -cb.append(">> DictionarySetattr") -ee('del d.locked') -ee('d.locked = FailingTrue()') -ee('vim.vvars.locked = False') -ee('d.scope = True') -ee('d.xxx = True') -cb.append(">> _DictionaryItem") -ee('d.get("a", 2, 3)') -stringtochars_test('d.get(%s)') -ee('d.pop("a")') -ee('dl.pop("a")') -cb.append(">> DictionaryContains") -ee('"" in d') -ee('0 in d') -cb.append(">> DictionaryIterNext") -ee('for i in ned: ned["a"] = 1') -del i -cb.append(">> DictionaryAssItem") -ee('dl["b"] = 1') -stringtochars_test('d[%s] = 1') -convertfrompyobject_test('d["a"] = %s') -cb.append(">> DictionaryUpdate") -cb.append(">>> kwargs") -cb.append(">>> iter") -ee('d.update(FailingMapping())') -ee('d.update([FailingIterNext()])') -ee('d.update([FailingIterNextN(1)])') -iter_test('d.update(%s)') -convertfrompyobject_test('d.update(%s)') -stringtochars_test('d.update(((%s, 0),))') -convertfrompyobject_test('d.update((("a", %s),))') -cb.append(">> DictionaryPopItem") -ee('d.popitem(1, 2)') -cb.append(">> DictionaryHasKey") -ee('d.has_key()') -cb.append("> List") -cb.append(">> ListConstructor") -ee('vim.List(1, 2)') -ee('vim.List(a=1)') -iter_test('vim.List(%s)') -convertfrompyobject_test('vim.List([%s])') -cb.append(">> ListItem") -ee('l[1000]') -cb.append(">> ListAssItem") -ee('ll[1] = 2') -ee('l[1000] = 3') -cb.append(">> ListAssSlice") -ee('ll[1:100] = "abcJ"') -iter_test('l[:] = %s') -ee('nel[1:10:2] = "abcK"') -cb.append(repr(tuple(nel))) -ee('nel[1:10:2] = "a"') -cb.append(repr(tuple(nel))) -ee('nel[1:1:-1] = "a"') -cb.append(repr(tuple(nel))) -ee('nel[:] = FailingIterNextN(2)') -cb.append(repr(tuple(nel))) -convertfrompyobject_test('l[:] = [%s]') -cb.append(">> ListConcatInPlace") -iter_test('l.extend(%s)') -convertfrompyobject_test('l.extend([%s])') -cb.append(">> ListSetattr") -ee('del l.locked') -ee('l.locked = FailingTrue()') -ee('l.xxx = True') -cb.append("> Function") -cb.append(">> FunctionConstructor") -ee('vim.Function("123")') -ee('vim.Function("xxx_non_existent_function_xxx")') -ee('vim.Function("xxx#non#existent#function#xxx")') -cb.append(">> FunctionCall") -convertfrompyobject_test('f(%s)') -convertfrompymapping_test('fd(self=%s)') -cb.append("> TabPage") -cb.append(">> TabPageAttr") -ee('vim.current.tabpage.xxx') -cb.append("> TabList") -cb.append(">> TabListItem") -ee('vim.tabpages[1000]') -cb.append("> Window") -cb.append(">> WindowAttr") -ee('vim.current.window.xxx') -cb.append(">> WindowSetattr") -ee('vim.current.window.buffer = 0') -ee('vim.current.window.cursor = (100000000, 100000000)') -ee('vim.current.window.cursor = True') -number_test('vim.current.window.height = %s', unsigned=True) -number_test('vim.current.window.width = %s', unsigned=True) -ee('vim.current.window.xxxxxx = True') -cb.append("> WinList") -cb.append(">> WinListItem") -ee('vim.windows[1000]') -cb.append("> Buffer") -cb.append(">> StringToLine (indirect)") -ee('vim.current.buffer[0] = "\\na"') -ee('vim.current.buffer[0] = b"\\na"') -cb.append(">> SetBufferLine (indirect)") -ee('vim.current.buffer[0] = True') -cb.append(">> SetBufferLineList (indirect)") -ee('vim.current.buffer[:] = True') -ee('vim.current.buffer[:] = ["\\na", "bc"]') -cb.append(">> InsertBufferLines (indirect)") -ee('vim.current.buffer.append(None)') -ee('vim.current.buffer.append(["\\na", "bc"])') -ee('vim.current.buffer.append("\\nbc")') -cb.append(">> RBItem") -ee('vim.current.buffer[100000000]') -cb.append(">> RBAsItem") -ee('vim.current.buffer[100000000] = ""') -cb.append(">> BufferAttr") -ee('vim.current.buffer.xxx') -cb.append(">> BufferSetattr") -ee('vim.current.buffer.name = True') -ee('vim.current.buffer.xxx = True') -cb.append(">> BufferMark") -ee('vim.current.buffer.mark(0)') -ee('vim.current.buffer.mark("abcM")') -ee('vim.current.buffer.mark("!")') -cb.append(">> BufferRange") -ee('vim.current.buffer.range(1, 2, 3)') -cb.append("> BufMap") -cb.append(">> BufMapItem") -ee('vim.buffers[100000000]') -number_test('vim.buffers[%s]', natural=True) -cb.append("> Current") -cb.append(">> CurrentGetattr") -ee('vim.current.xxx') -cb.append(">> CurrentSetattr") -ee('vim.current.line = True') -ee('vim.current.buffer = True') -ee('vim.current.window = True') -ee('vim.current.tabpage = True') -ee('vim.current.xxx = True') -del d -del ned -del dl -del l -del ll -del nel -del f -del fd -del fdel -del subexpr_test -del stringtochars_test -del Mapping -del convertfrompyobject_test -del convertfrompymapping_test -del iter_test -del number_test -del FailingTrue -del FailingIter -del FailingIterNext -del FailingIterNextN -del FailingMapping -del FailingMappingKey -del FailingList -del NoArgsCall -del FailingCall -del FailingNumber -EOF -:delfunction F -:" -:" Test import -py3 << EOF -sys.path.insert(0, os.path.join(os.getcwd(), 'python_before')) -sys.path.append(os.path.join(os.getcwd(), 'python_after')) -vim.options['rtp'] = os.getcwd().replace(',', '\\,').replace('\\', '\\\\') -l = [] -def callback(path): - l.append(os.path.relpath(path)) -vim.foreach_rtp(callback) -cb.append(repr(l)) -del l -def callback(path): - return os.path.relpath(path) -cb.append(repr(vim.foreach_rtp(callback))) -del callback -from module import dir as d -from modulex import ddir -cb.append(d + ',' + ddir) -import before -cb.append(before.dir) -import after -cb.append(after.dir) -import topmodule as tm -import topmodule.submodule as tms -import topmodule.submodule.subsubmodule.subsubsubmodule as tmsss -cb.append(tm.__file__.replace(os.path.sep, '/')[-len('modulex/topmodule/__init__.py'):]) -cb.append(tms.__file__.replace(os.path.sep, '/')[-len('modulex/topmodule/submodule/__init__.py'):]) -cb.append(tmsss.__file__.replace(os.path.sep, '/')[-len('modulex/topmodule/submodule/subsubmodule/subsubsubmodule.py'):]) -del before -del after -del d -del ddir -del tm -del tms -del tmsss -EOF -:" -:" Test exceptions -:fun Exe(e) -: execute a:e -:endfun -py3 << EOF -Exe = vim.bindeval('function("Exe")') -ee('vim.command("throw \'abcN\'")') -ee('Exe("throw \'def\'")') -ee('vim.eval("Exe(\'throw \'\'ghi\'\'\')")') -ee('vim.eval("Exe(\'echoerr \'\'jkl\'\'\')")') -ee('vim.eval("Exe(\'xxx_non_existent_command_xxx\')")') -ee('vim.eval("xxx_unknown_function_xxx()")') -ee('vim.bindeval("Exe(\'xxx_non_existent_command_xxx\')")') -del Exe -EOF -:delfunction Exe -:" -:" Regression: interrupting vim.command propagates to next vim.command -py3 << EOF -def test_keyboard_interrupt(): - try: - vim.command('while 1 | endwhile') - except KeyboardInterrupt: - cb.append('Caught KeyboardInterrupt') - except Exception as e: - cb.append('!!!!!!!! Caught exception: ' + repr(e)) - else: - cb.append('!!!!!!!! No exception') - try: - vim.command('$ put =\'Running :put\'') - except KeyboardInterrupt: - cb.append('!!!!!!!! Caught KeyboardInterrupt') - except Exception as e: - cb.append('!!!!!!!! Caught exception: ' + repr(e)) - else: - cb.append('No exception') -EOF -:debuggreedy -:call inputsave() -:call feedkeys("s\ns\ns\ns\nq\n") -:redir => output -:debug silent! py3 test_keyboard_interrupt() -:redir END -:0 debuggreedy -:call inputrestore() -:silent $put =output -:unlet output -:py3 del test_keyboard_interrupt -:" -:" Cleanup -py3 << EOF -del cb -del ee -del sys -del os -del vim -EOF -:endfun -:" -:fun RunTest() -:let checkrefs = !empty($PYTHONDUMPREFS) -:let start = getline(1, '$') -:for i in range(checkrefs ? 10 : 1) -: if i != 0 -: %d _ -: call setline(1, start) -: endif -: call Test() -: if i == 0 -: let result = getline(1, '$') -: endif -:endfor -:if checkrefs -: %d _ -: call setline(1, result) -:endif -:endfun -:" -:call RunTest() -:delfunction RunTest -:delfunction Test -:call garbagecollect(1) -:" -:/^start:/,$wq! test.out -:" vim: et ts=4 isk-=\: -:call getchar() -ENDTEST - -start: diff --git a/src/nvim/testdir/test87.ok b/src/nvim/testdir/test87.ok deleted file mode 100644 index d1ec84c6b8..0000000000 --- a/src/nvim/testdir/test87.ok +++ /dev/null @@ -1,1266 +0,0 @@ -start: -[1, 'as''d', [1, 2, function('strlen'), {'a': 1}]] -[1, 2, function('strlen'), {'a': 1}] -Vim(put):E684: -[0, 'as''d', [1, 2, function('strlen'), {'a': 1}]] -[0, function('strlen'), [1, 2, function('strlen'), {'a': 1}]] -1 -[b'-1', b'0', b'1', b'b', b'f'] -[-1, <vim.Function '1'>, <vim.dictionary object at >, <vim.list object at >, b'asd'] -[(b'-1', <vim.dictionary object at >), (b'0', -1), (b'1', b'asd'), (b'b', <vim.list object at >), (b'f', <vim.Function '1'>)] -'-1' : {'a': 1} -'0' : -1 -'1' : 'asd' -'b' : [1, 2, function('strlen')] -'f' : function('1') -[0, function('strlen')] -[3] -[1, 2, function('strlen')] -[1, 2, function('strlen')] -1 -'asd' -2 -True -False -True -False -[b'0'] -{'0': -1} -(b'0', -1) -None -[] -[0, 1, 2, 3] -[0, 1, 2, 3] -[0, 1, 3] -[0, 1] -[0, 1] -[0, 1] -[0, 1, 2, 3] -[0, 1, 2, 3] -[0, 2, 3] -[2, 3] -[2, 3] -[2, 3] -[1, 3] -[0, 2] -[0, 1, 2, 3] -['a', 0, 1, 2, 3] -[0, 'b', 2, 3] -[0, 1, 'c'] -[0, 1, 2, 3, 'd'] -[0, 1, 2, 'e', 3] -['f', 2, 3] -[0, 1, 'g', 2, 3] -['h'] -[0, 1, 10, 3, 20, 5, 6, 7] -[0, 1, 2, 3, 20, 5, 10, 7] -[0, 1, 2, 3, 4, 5, 6, 7] -[0, 1, 2, 3, 4, 5, 6, 7] -[0, 1, 2, 3, 4, 5, 6, 7] -[0, 1, 2, 3] -[function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd'] -[function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd', 'DictNewStart', 1, 2, 3, 'DictNewEnd', {'a': 'b'}] -[function('New'), function('DictNew'), 'NewStart', 1, 2, 3, 'NewEnd', 'DictNewStart', 1, 2, 3, 'DictNewEnd', {'a': 'b'}, 'New'] -l[1](1, 2, 3):(<class 'vim.error'>, error('Vim:E725: Calling dict function without Dictionary: DictNew',)) -f(1, 2, 3):(<class 'vim.error'>, error('Vim:E117: Unknown function: New',)) -[0.0, 0.0] -KeyError -TypeError -TypeError -ValueError -TypeError -TypeError -KeyError -KeyError -d : locked:0;scope:0 -dl : locked:1;scope:0 -v: : locked:2;scope:1 -g: : locked:0;scope:2 -d:{'abc2': 1} -dl:{'def': 1} -l : locked:0 -ll : locked:1 -l:[0] -ll:[1] -[0, 1, 2] -['a', 'b'] -['c', 1] -['d', ['e']] -0.0 -"\0": Vim(let):E859: -{"\0": 1}: Vim(let):E859: -undefined_name: Vim(let):Trace -vim: Vim(let):E859: -[1] -[1, 10, 11, 10, 11, 10, 11, 10, 11, 10, 11, 10, 1] -[0, 1, 2, 3] -[2, 3, 4, 5] -[0, 1] -[4, 5] -[2, 3] -[] -[2, 3] -[] -[0, 1, 2, 3, 4, 5] -[0, 1, 2, 3, 4, 5] -[0, 1, 2, 3, 4, 5] -[4, 3] -[0, 2, 4] -[] -Abc -bac -def -bar -jkl -wopts iters equal: 1 -bopts iters equal: 1 ->>> paste - g/w/b:1/0/0 - g/w/b (in):1/0/0 - p/gopts1: False - p/wopts1! KeyError - inv: 2! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1! KeyError - inv: 2! KeyError - bopts1! KeyError - bopts2! KeyError - bopts3! KeyError - G: 1 - W: 1:1 2:1 3:1 4:1 - B: 1:1 2:1 3:1 4:1 - del wopts3! KeyError - del bopts3! KeyError - G: 1 - W: 1:1 2:1 3:1 4:1 - B: 1:1 2:1 3:1 4:1 ->>> previewheight - g/w/b:1/0/0 - g/w/b (in):1/0/0 - p/gopts1: 12 - inv: 'a'! TypeError - p/wopts1! KeyError - inv: 'a'! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1! KeyError - inv: 'a'! KeyError - bopts1! KeyError - bopts2! KeyError - bopts3! KeyError - G: 5 - W: 1:5 2:5 3:5 4:5 - B: 1:5 2:5 3:5 4:5 - del wopts3! KeyError - del bopts3! KeyError - G: 5 - W: 1:5 2:5 3:5 4:5 - B: 1:5 2:5 3:5 4:5 ->>> operatorfunc - g/w/b:1/0/0 - g/w/b (in):1/0/0 - p/gopts1: b'' - inv: 2! TypeError - p/wopts1! KeyError - inv: 2! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1! KeyError - inv: 2! KeyError - bopts1! KeyError - bopts2! KeyError - bopts3! KeyError - G: 'A' - W: 1:'A' 2:'A' 3:'A' 4:'A' - B: 1:'A' 2:'A' 3:'A' 4:'A' - del wopts3! KeyError - del bopts3! KeyError - G: 'A' - W: 1:'A' 2:'A' 3:'A' 4:'A' - B: 1:'A' 2:'A' 3:'A' 4:'A' ->>> number - g/w/b:0/1/0 - g/w/b (in):0/1/0 - p/gopts1! KeyError - inv: 0! KeyError - gopts1! KeyError - p/wopts1: False - p/bopts1! KeyError - inv: 0! KeyError - bopts1! KeyError - bopts2! KeyError - bopts3! KeyError - G: 0 - W: 1:1 2:1 3:0 4:0 - B: 1:1 2:1 3:0 4:0 - del wopts3! ValueError - del bopts3! KeyError - G: 0 - W: 1:1 2:1 3:0 4:0 - B: 1:1 2:1 3:0 4:0 ->>> numberwidth - g/w/b:0/1/0 - g/w/b (in):0/1/0 - p/gopts1! KeyError - inv: -100! KeyError - gopts1! KeyError - p/wopts1: 8 - inv: -100! error - p/bopts1! KeyError - inv: -100! KeyError - bopts1! KeyError - bopts2! KeyError - bopts3! KeyError - G: 8 - W: 1:3 2:5 3:2 4:8 - B: 1:3 2:5 3:2 4:8 - del wopts3! ValueError - del bopts3! KeyError - G: 8 - W: 1:3 2:5 3:2 4:8 - B: 1:3 2:5 3:2 4:8 ->>> colorcolumn - g/w/b:0/1/0 - g/w/b (in):0/1/0 - p/gopts1! KeyError - inv: 'abc4'! KeyError - gopts1! KeyError - p/wopts1: b'' - inv: 'abc4'! error - p/bopts1! KeyError - inv: 'abc4'! KeyError - bopts1! KeyError - bopts2! KeyError - bopts3! KeyError - G: '' - W: 1:'+2' 2:'+3' 3:'+1' 4:'' - B: 1:'+2' 2:'+3' 3:'+1' 4:'' - del wopts3! ValueError - del bopts3! KeyError - G: '' - W: 1:'+2' 2:'+3' 3:'+1' 4:'' - B: 1:'+2' 2:'+3' 3:'+1' 4:'' ->>> statusline - g/w/b:1/1/0 - g/w/b (in):1/1/0 - p/gopts1: b'' - inv: 0! TypeError - p/wopts1: None - inv: 0! TypeError - p/bopts1! KeyError - inv: 0! KeyError - bopts1! KeyError - bopts2! KeyError - bopts3! KeyError - G: '1' - W: 1:'2' 2:'4' 3:'1' 4:'1' - B: 1:'2' 2:'4' 3:'1' 4:'1' - del bopts3! KeyError - G: '1' - W: 1:'2' 2:'1' 3:'1' 4:'1' - B: 1:'2' 2:'1' 3:'1' 4:'1' ->>> autoindent - g/w/b:0/0/1 - g/w/b (in):0/0/1 - p/gopts1! KeyError - inv: 2! KeyError - gopts1! KeyError - p/wopts1! KeyError - inv: 2! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1: False - G: 0 - W: 1:0 2:1 3:0 4:1 - B: 1:0 2:1 3:0 4:1 - del wopts3! KeyError - del bopts3! ValueError - G: 0 - W: 1:0 2:1 3:0 4:1 - B: 1:0 2:1 3:0 4:1 ->>> shiftwidth - g/w/b:0/0/1 - g/w/b (in):0/0/1 - p/gopts1! KeyError - inv: 3! KeyError - gopts1! KeyError - p/wopts1! KeyError - inv: 3! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1: 8 - G: 8 - W: 1:0 2:2 3:8 4:1 - B: 1:0 2:2 3:8 4:1 - del wopts3! KeyError - del bopts3! ValueError - G: 8 - W: 1:0 2:2 3:8 4:1 - B: 1:0 2:2 3:8 4:1 ->>> omnifunc - g/w/b:0/0/1 - g/w/b (in):0/0/1 - p/gopts1! KeyError - inv: 1! KeyError - gopts1! KeyError - p/wopts1! KeyError - inv: 1! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1: b'' - inv: 1! TypeError - G: '' - W: 1:'A' 2:'B' 3:'' 4:'C' - B: 1:'A' 2:'B' 3:'' 4:'C' - del wopts3! KeyError - del bopts3! ValueError - G: '' - W: 1:'A' 2:'B' 3:'' 4:'C' - B: 1:'A' 2:'B' 3:'' 4:'C' ->>> preserveindent - g/w/b:0/0/1 - g/w/b (in):0/0/1 - p/gopts1! KeyError - inv: 2! KeyError - gopts1! KeyError - p/wopts1! KeyError - inv: 2! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1: False - G: 0 - W: 1:0 2:1 3:0 4:1 - B: 1:0 2:1 3:0 4:1 - del wopts3! KeyError - del bopts3! ValueError - G: 0 - W: 1:0 2:1 3:0 4:1 - B: 1:0 2:1 3:0 4:1 ->>> path - g/w/b:1/0/1 - g/w/b (in):1/0/1 - p/gopts1: b'.,..,,' - inv: 0! TypeError - p/wopts1! KeyError - inv: 0! KeyError - wopts1! KeyError - wopts2! KeyError - wopts3! KeyError - p/bopts1: None - inv: 0! TypeError - G: '.,,' - W: 1:'.,,' 2:',,' 3:'.,,' 4:'.' - B: 1:'.,,' 2:',,' 3:'.,,' 4:'.' - del wopts3! KeyError - G: '.,,' - W: 1:'.,,' 2:',,' 3:'.,,' 4:'.,,' - B: 1:'.,,' 2:',,' 3:'.,,' 4:'.,,' -First line -First line -def -First line -Second line -Third line -(7, 2) -<buffer test87.in><buffer > -baz -bar -Second line -Third line -foo -1:BufFilePre:1 -1:BufFilePost:1 -testdir/foo -5:BufFilePre:5 -5:BufFilePost:5 -testdir/bar -1:BufFilePre:1 -1:BufFilePost:1 -testdir/test87.in -valid: b:False, cb:True -i:<buffer test87.in> -i2:<buffer test87.in> -i:<buffer a> -i3:<buffer test87.in> -1:<buffer test87.in>=<buffer test87.in> -8:<buffer a>=<buffer a> -9:<buffer b>=<buffer b> -10:<buffer c>=<buffer c> -4 -i4:<buffer test87.in> -i4:<buffer test87.in> -StopIteration -Number of tabs: 4 -Current tab pages: - <tabpage 0>(1): 1 windows, current is <window object (unknown)> - Windows: - <window object (unknown)>(1): displays buffer <buffer test87.in>; cursor is at (37, 0) - <tabpage 1>(2): 1 windows, current is <window object (unknown)> - Windows: - <window object (unknown)>(1): displays buffer <buffer 0>; cursor is at (1, 0) - <tabpage 2>(3): 2 windows, current is <window object (unknown)> - Windows: - <window object (unknown)>(1): displays buffer <buffer a.1>; cursor is at (1, 0) - <window object (unknown)>(2): displays buffer <buffer 1>; cursor is at (1, 0) - <tabpage 3>(4): 4 windows, current is <window 0> - Windows: - <window 0>(1): displays buffer <buffer c.2>; cursor is at (1, 0) - <window 1>(2): displays buffer <buffer b.2>; cursor is at (1, 0) - <window 2>(3): displays buffer <buffer a.2>; cursor is at (1, 0) - <window 3>(4): displays buffer <buffer 2>; cursor is at (1, 0) -Number of windows in current tab page: 4 -Current tab page: <tabpage 3> -Current window: <window 0>: <window 0> is <window 0> -Current buffer: <buffer c.2>: <buffer c.2> is <buffer c.2> is <buffer c.2> -ValueError at assigning foreign tab window -Type error at assigning None to vim.current.window -Type error at assigning None to vim.current.tabpage -Type error at assigning None to vim.current.buffer -Current tab page: <tabpage 2> -Current window: <window 0> -Current buffer: <buffer test87.in> -Current line: 'Type error at assigning None to vim.current.buffer' -w.valid: [True, False] -t.valid: [True, False, True, False] -vim.vars:Dictionary:True -vim.options:Options:True -vim.bindeval("{}"):Dictionary:True -vim.bindeval("[]"):List:True -vim.bindeval("function('tr')"):Function:True -vim.current.buffer:Buffer:True -vim.current.range:Range:True -vim.current.window:Window:True -vim.current.tabpage:TabPage:True -current:__dir__,buffer,line,range,tabpage,window -buffer:__dir__,append,mark,name,number,options,range,valid,vars -window:__dir__,buffer,col,cursor,height,number,options,row,tabpage,valid,vars -tabpage:__dir__,number,valid,vars,window,windows -range:__dir__,append,end,start -dictionary:__dir__,get,has_key,items,keys,locked,pop,popitem,scope,update,values -list:__dir__,extend,locked -function:__dir__,softspace -output:__dir__,flush,softspace,write,writelines -{} -{'a': 1} -{'a': 1} -[] -['a', 'b', 'c', '7'] -function('tr') -' -abcdef -line : -abcdef -abcA -line : -abcB' -['a', 'dup_a'] -['a', 'a'] -['a', 'b', 'c', 'C'] -[2, 2] -[2, 2] -1 -1 -function('Put') -b'testdir' -test87.in -b'src' -testdir/test87.in -b'testdir' -test87.in -> Output ->> OutputSetattr -del sys.stdout.softspace:(<class 'AttributeError'>, AttributeError("can't delete OutputObject attributes",)) ->>> Testing NumberToLong using sys.stdout.softspace = %s -sys.stdout.softspace = []:(<class 'TypeError'>, TypeError('expected int() or something supporting coercing to int(), but got list',)) -sys.stdout.softspace = None:(<class 'TypeError'>, TypeError('expected int() or something supporting coercing to int(), but got NoneType',)) -sys.stdout.softspace = -1:(<class 'ValueError'>, ValueError('number must be greater or equal to zero',)) -<<< Finished ->>> Testing NumberToLong using sys.stderr.softspace = %s -sys.stderr.softspace = []:(<class 'TypeError'>, TypeError('expected int() or something supporting coercing to int(), but got list',)) -sys.stderr.softspace = None:(<class 'TypeError'>, TypeError('expected int() or something supporting coercing to int(), but got NoneType',)) -sys.stderr.softspace = -1:(<class 'ValueError'>, ValueError('number must be greater or equal to zero',)) -<<< Finished -sys.stdout.attr = None:(<class 'AttributeError'>, AttributeError('invalid attribute: attr',)) ->> OutputWrite -sys.stdout.write(None):(<class 'TypeError'>, TypeError("Can't convert 'NoneType' object to str implicitly",)) ->> OutputWriteLines -sys.stdout.writelines(None):(<class 'TypeError'>, TypeError("'NoneType' object is not iterable",)) -sys.stdout.writelines([1]):(<class 'TypeError'>, TypeError("Can't convert 'int' object to str implicitly",)) ->>> Testing *Iter* using sys.stdout.writelines(%s) -sys.stdout.writelines(FailingIter()):(<class 'NotImplementedError'>, NotImplementedError('iter',)) -sys.stdout.writelines(FailingIterNext()):(<class 'NotImplementedError'>, NotImplementedError('next',)) -<<< Finished -> VimCommand ->>> Testing StringToChars using vim.command(%s) -vim.command(1):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -vim.command(b"\0"):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -vim.command("\0"):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished -vim.command("", 2):(<class 'TypeError'>, TypeError('command() takes exactly one argument (2 given)',)) -> VimToPython -> VimEval ->>> Testing StringToChars using vim.eval(%s) -vim.eval(1):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -vim.eval(b"\0"):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -vim.eval("\0"):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished -vim.eval("", FailingTrue()):(<class 'TypeError'>, TypeError('function takes exactly 1 argument (2 given)',)) -> VimEvalPy ->>> Testing StringToChars using vim.bindeval(%s) -vim.bindeval(1):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -vim.bindeval(b"\0"):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -vim.bindeval("\0"):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished -vim.eval("", 2):(<class 'TypeError'>, TypeError('function takes exactly 1 argument (2 given)',)) -> VimStrwidth ->>> Testing StringToChars using vim.strwidth(%s) -vim.strwidth(1):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -vim.strwidth(b"\0"):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -vim.strwidth("\0"):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished -> VimForeachRTP -vim.foreach_rtp(None):(<class 'TypeError'>, TypeError("'NoneType' object is not callable",)) -vim.foreach_rtp(NoArgsCall()):(<class 'TypeError'>, TypeError('__call__() takes exactly 1 positional argument (2 given)',)) -vim.foreach_rtp(FailingCall()):(<class 'NotImplementedError'>, NotImplementedError('call',)) -vim.foreach_rtp(int, 2):(<class 'TypeError'>, TypeError('foreach_rtp() takes exactly one argument (2 given)',)) -> import -import xxx_no_such_module_xxx:(<class 'ImportError'>, ImportError('No module named xxx_no_such_module_xxx',)) -import failing_import:(<class 'ImportError'>, ImportError('No module named failing_import',)) -import failing:(<class 'NotImplementedError'>, NotImplementedError()) -> Options ->> OptionsItem -vim.options["abcQ"]:(<class 'KeyError'>, KeyError('abcQ',)) -vim.options[""]:(<class 'ValueError'>, ValueError('empty keys are not allowed',)) ->>> Testing StringToChars using vim.options[%s] -vim.options[1]:(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -vim.options[b"\0"]:(<class 'TypeError'>, TypeError('expected bytes with no null',)) -vim.options["\0"]:(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->> OptionsContains ->>> Testing StringToChars using %s in vim.options -1 in vim.options:(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -b"\0" in vim.options:(<class 'TypeError'>, TypeError('expected bytes with no null',)) -"\0" in vim.options:(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished -> Dictionary ->> DictionaryConstructor -vim.Dictionary("abcI"):(<class 'ValueError'>, ValueError('expected sequence element of size 2, but got sequence of size 1',)) ->> DictionarySetattr -del d.locked:(<class 'AttributeError'>, AttributeError('cannot delete vim.Dictionary attributes',)) -d.locked = FailingTrue():(<class 'NotImplementedError'>, NotImplementedError('bool',)) -vim.vvars.locked = False:(<class 'TypeError'>, TypeError('cannot modify fixed dictionary',)) -d.scope = True:(<class 'AttributeError'>, AttributeError('cannot set attribute scope',)) -d.xxx = True:(<class 'AttributeError'>, AttributeError('cannot set attribute xxx',)) ->> _DictionaryItem -d.get("a", 2, 3):(<class 'TypeError'>, TypeError('function takes at most 2 arguments (3 given)',)) ->>> Testing StringToChars using d.get(%s) -d.get(1):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -d.get(b"\0"):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -d.get("\0"):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished -d.pop("a"):(<class 'KeyError'>, KeyError('a',)) -dl.pop("a"):(<class 'vim.error'>, error('dictionary is locked',)) ->> DictionaryContains -"" in d:(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -0 in d:(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) ->> DictionaryIterNext -for i in ned: ned["a"] = 1:(<class 'RuntimeError'>, RuntimeError('hashtab changed during iteration',)) ->> DictionaryAssItem -dl["b"] = 1:(<class 'vim.error'>, error('dictionary is locked',)) ->>> Testing StringToChars using d[%s] = 1 -d[1] = 1:(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -d[b"\0"] = 1:(<class 'TypeError'>, TypeError('expected bytes with no null',)) -d["\0"] = 1:(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using d["a"] = {%s : 1} -d["a"] = {1 : 1}:(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -d["a"] = {b"\0" : 1}:(<class 'TypeError'>, TypeError('expected bytes with no null',)) -d["a"] = {"\0" : 1}:(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using d["a"] = {"abcF" : {%s : 1}} -d["a"] = {"abcF" : {1 : 1}}:(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -d["a"] = {"abcF" : {b"\0" : 1}}:(<class 'TypeError'>, TypeError('expected bytes with no null',)) -d["a"] = {"abcF" : {"\0" : 1}}:(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using d["a"] = {"abcF" : Mapping({%s : 1})} -d["a"] = {"abcF" : Mapping({1 : 1})}:(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -d["a"] = {"abcF" : Mapping({b"\0" : 1})}:(<class 'TypeError'>, TypeError('expected bytes with no null',)) -d["a"] = {"abcF" : Mapping({"\0" : 1})}:(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using d["a"] = {"abcF" : %s} -d["a"] = {"abcF" : FailingIter()}:(<class 'TypeError'>, TypeError('unable to convert FailingIter to vim structure',)) -d["a"] = {"abcF" : FailingIterNext()}:(<class 'NotImplementedError'>, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using d["a"] = {"abcF" : %s} -d["a"] = {"abcF" : None}:(<class 'TypeError'>, TypeError('unable to convert NoneType to vim structure',)) -d["a"] = {"abcF" : {b"": 1}}:(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -d["a"] = {"abcF" : {"": 1}}:(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -d["a"] = {"abcF" : FailingMapping()}:(<class 'NotImplementedError'>, NotImplementedError('keys',)) -d["a"] = {"abcF" : FailingMappingKey()}:(<class 'NotImplementedError'>, NotImplementedError('getitem:mappingkey',)) -d["a"] = {"abcF" : FailingNumber()}:(<class 'NotImplementedError'>, NotImplementedError('int',)) -<<< Finished ->>> Testing StringToChars using d["a"] = Mapping({%s : 1}) -d["a"] = Mapping({1 : 1}):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -d["a"] = Mapping({b"\0" : 1}):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -d["a"] = Mapping({"\0" : 1}):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using d["a"] = Mapping({"abcG" : {%s : 1}}) -d["a"] = Mapping({"abcG" : {1 : 1}}):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -d["a"] = Mapping({"abcG" : {b"\0" : 1}}):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -d["a"] = Mapping({"abcG" : {"\0" : 1}}):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using d["a"] = Mapping({"abcG" : Mapping({%s : 1})}) -d["a"] = Mapping({"abcG" : Mapping({1 : 1})}):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -d["a"] = Mapping({"abcG" : Mapping({b"\0" : 1})}):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -d["a"] = Mapping({"abcG" : Mapping({"\0" : 1})}):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using d["a"] = Mapping({"abcG" : %s}) -d["a"] = Mapping({"abcG" : FailingIter()}):(<class 'TypeError'>, TypeError('unable to convert FailingIter to vim structure',)) -d["a"] = Mapping({"abcG" : FailingIterNext()}):(<class 'NotImplementedError'>, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using d["a"] = Mapping({"abcG" : %s}) -d["a"] = Mapping({"abcG" : None}):(<class 'TypeError'>, TypeError('unable to convert NoneType to vim structure',)) -d["a"] = Mapping({"abcG" : {b"": 1}}):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -d["a"] = Mapping({"abcG" : {"": 1}}):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -d["a"] = Mapping({"abcG" : FailingMapping()}):(<class 'NotImplementedError'>, NotImplementedError('keys',)) -d["a"] = Mapping({"abcG" : FailingMappingKey()}):(<class 'NotImplementedError'>, NotImplementedError('getitem:mappingkey',)) -d["a"] = Mapping({"abcG" : FailingNumber()}):(<class 'NotImplementedError'>, NotImplementedError('int',)) -<<< Finished ->>> Testing *Iter* using d["a"] = %s -d["a"] = FailingIter():(<class 'TypeError'>, TypeError('unable to convert FailingIter to vim structure',)) -d["a"] = FailingIterNext():(<class 'NotImplementedError'>, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using d["a"] = %s -d["a"] = None:(<class 'TypeError'>, TypeError('unable to convert NoneType to vim structure',)) -d["a"] = {b"": 1}:(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -d["a"] = {"": 1}:(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -d["a"] = FailingMapping():(<class 'NotImplementedError'>, NotImplementedError('keys',)) -d["a"] = FailingMappingKey():(<class 'NotImplementedError'>, NotImplementedError('getitem:mappingkey',)) -d["a"] = FailingNumber():(<class 'NotImplementedError'>, NotImplementedError('int',)) -<<< Finished ->> DictionaryUpdate ->>> kwargs ->>> iter -d.update(FailingMapping()):(<class 'NotImplementedError'>, NotImplementedError('keys',)) -d.update([FailingIterNext()]):(<class 'NotImplementedError'>, NotImplementedError('next',)) -d.update([FailingIterNextN(1)]):(<class 'NotImplementedError'>, NotImplementedError('next N',)) ->>> Testing *Iter* using d.update(%s) -d.update(FailingIter()):(<class 'NotImplementedError'>, NotImplementedError('iter',)) -d.update(FailingIterNext()):(<class 'NotImplementedError'>, NotImplementedError('next',)) -<<< Finished ->>> Testing StringToChars using d.update({%s : 1}) -d.update({1 : 1}):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -d.update({b"\0" : 1}):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -d.update({"\0" : 1}):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using d.update({"abcF" : {%s : 1}}) -d.update({"abcF" : {1 : 1}}):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -d.update({"abcF" : {b"\0" : 1}}):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -d.update({"abcF" : {"\0" : 1}}):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using d.update({"abcF" : Mapping({%s : 1})}) -d.update({"abcF" : Mapping({1 : 1})}):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -d.update({"abcF" : Mapping({b"\0" : 1})}):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -d.update({"abcF" : Mapping({"\0" : 1})}):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using d.update({"abcF" : %s}) -d.update({"abcF" : FailingIter()}):(<class 'TypeError'>, TypeError('unable to convert FailingIter to vim structure',)) -d.update({"abcF" : FailingIterNext()}):(<class 'NotImplementedError'>, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using d.update({"abcF" : %s}) -d.update({"abcF" : None}):(<class 'TypeError'>, TypeError('unable to convert NoneType to vim structure',)) -d.update({"abcF" : {b"": 1}}):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -d.update({"abcF" : {"": 1}}):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -d.update({"abcF" : FailingMapping()}):(<class 'NotImplementedError'>, NotImplementedError('keys',)) -d.update({"abcF" : FailingMappingKey()}):(<class 'NotImplementedError'>, NotImplementedError('getitem:mappingkey',)) -d.update({"abcF" : FailingNumber()}):(<class 'NotImplementedError'>, NotImplementedError('int',)) -<<< Finished ->>> Testing StringToChars using d.update(Mapping({%s : 1})) -d.update(Mapping({1 : 1})):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -d.update(Mapping({b"\0" : 1})):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -d.update(Mapping({"\0" : 1})):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using d.update(Mapping({"abcG" : {%s : 1}})) -d.update(Mapping({"abcG" : {1 : 1}})):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -d.update(Mapping({"abcG" : {b"\0" : 1}})):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -d.update(Mapping({"abcG" : {"\0" : 1}})):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using d.update(Mapping({"abcG" : Mapping({%s : 1})})) -d.update(Mapping({"abcG" : Mapping({1 : 1})})):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -d.update(Mapping({"abcG" : Mapping({b"\0" : 1})})):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -d.update(Mapping({"abcG" : Mapping({"\0" : 1})})):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using d.update(Mapping({"abcG" : %s})) -d.update(Mapping({"abcG" : FailingIter()})):(<class 'TypeError'>, TypeError('unable to convert FailingIter to vim structure',)) -d.update(Mapping({"abcG" : FailingIterNext()})):(<class 'NotImplementedError'>, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using d.update(Mapping({"abcG" : %s})) -d.update(Mapping({"abcG" : None})):(<class 'TypeError'>, TypeError('unable to convert NoneType to vim structure',)) -d.update(Mapping({"abcG" : {b"": 1}})):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -d.update(Mapping({"abcG" : {"": 1}})):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -d.update(Mapping({"abcG" : FailingMapping()})):(<class 'NotImplementedError'>, NotImplementedError('keys',)) -d.update(Mapping({"abcG" : FailingMappingKey()})):(<class 'NotImplementedError'>, NotImplementedError('getitem:mappingkey',)) -d.update(Mapping({"abcG" : FailingNumber()})):(<class 'NotImplementedError'>, NotImplementedError('int',)) -<<< Finished ->>> Testing *Iter* using d.update(%s) -d.update(FailingIter()):(<class 'NotImplementedError'>, NotImplementedError('iter',)) -d.update(FailingIterNext()):(<class 'NotImplementedError'>, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using d.update(%s) -d.update(None):(<class 'TypeError'>, TypeError("'NoneType' object is not iterable",)) -d.update({b"": 1}):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -d.update({"": 1}):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -d.update(FailingMapping()):(<class 'NotImplementedError'>, NotImplementedError('keys',)) -d.update(FailingMappingKey()):(<class 'NotImplementedError'>, NotImplementedError('getitem:mappingkey',)) -d.update(FailingNumber()):(<class 'TypeError'>, TypeError("'FailingNumber' object is not iterable",)) -<<< Finished ->>> Testing StringToChars using d.update(((%s, 0),)) -d.update(((1, 0),)):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -d.update(((b"\0", 0),)):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -d.update((("\0", 0),)):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using d.update((("a", {%s : 1}),)) -d.update((("a", {1 : 1}),)):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -d.update((("a", {b"\0" : 1}),)):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -d.update((("a", {"\0" : 1}),)):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using d.update((("a", {"abcF" : {%s : 1}}),)) -d.update((("a", {"abcF" : {1 : 1}}),)):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -d.update((("a", {"abcF" : {b"\0" : 1}}),)):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -d.update((("a", {"abcF" : {"\0" : 1}}),)):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using d.update((("a", {"abcF" : Mapping({%s : 1})}),)) -d.update((("a", {"abcF" : Mapping({1 : 1})}),)):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -d.update((("a", {"abcF" : Mapping({b"\0" : 1})}),)):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -d.update((("a", {"abcF" : Mapping({"\0" : 1})}),)):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using d.update((("a", {"abcF" : %s}),)) -d.update((("a", {"abcF" : FailingIter()}),)):(<class 'TypeError'>, TypeError('unable to convert FailingIter to vim structure',)) -d.update((("a", {"abcF" : FailingIterNext()}),)):(<class 'NotImplementedError'>, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using d.update((("a", {"abcF" : %s}),)) -d.update((("a", {"abcF" : None}),)):(<class 'TypeError'>, TypeError('unable to convert NoneType to vim structure',)) -d.update((("a", {"abcF" : {b"": 1}}),)):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -d.update((("a", {"abcF" : {"": 1}}),)):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -d.update((("a", {"abcF" : FailingMapping()}),)):(<class 'NotImplementedError'>, NotImplementedError('keys',)) -d.update((("a", {"abcF" : FailingMappingKey()}),)):(<class 'NotImplementedError'>, NotImplementedError('getitem:mappingkey',)) -d.update((("a", {"abcF" : FailingNumber()}),)):(<class 'NotImplementedError'>, NotImplementedError('int',)) -<<< Finished ->>> Testing StringToChars using d.update((("a", Mapping({%s : 1})),)) -d.update((("a", Mapping({1 : 1})),)):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -d.update((("a", Mapping({b"\0" : 1})),)):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -d.update((("a", Mapping({"\0" : 1})),)):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using d.update((("a", Mapping({"abcG" : {%s : 1}})),)) -d.update((("a", Mapping({"abcG" : {1 : 1}})),)):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -d.update((("a", Mapping({"abcG" : {b"\0" : 1}})),)):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -d.update((("a", Mapping({"abcG" : {"\0" : 1}})),)):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using d.update((("a", Mapping({"abcG" : Mapping({%s : 1})})),)) -d.update((("a", Mapping({"abcG" : Mapping({1 : 1})})),)):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -d.update((("a", Mapping({"abcG" : Mapping({b"\0" : 1})})),)):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -d.update((("a", Mapping({"abcG" : Mapping({"\0" : 1})})),)):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using d.update((("a", Mapping({"abcG" : %s})),)) -d.update((("a", Mapping({"abcG" : FailingIter()})),)):(<class 'TypeError'>, TypeError('unable to convert FailingIter to vim structure',)) -d.update((("a", Mapping({"abcG" : FailingIterNext()})),)):(<class 'NotImplementedError'>, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using d.update((("a", Mapping({"abcG" : %s})),)) -d.update((("a", Mapping({"abcG" : None})),)):(<class 'TypeError'>, TypeError('unable to convert NoneType to vim structure',)) -d.update((("a", Mapping({"abcG" : {b"": 1}})),)):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -d.update((("a", Mapping({"abcG" : {"": 1}})),)):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -d.update((("a", Mapping({"abcG" : FailingMapping()})),)):(<class 'NotImplementedError'>, NotImplementedError('keys',)) -d.update((("a", Mapping({"abcG" : FailingMappingKey()})),)):(<class 'NotImplementedError'>, NotImplementedError('getitem:mappingkey',)) -d.update((("a", Mapping({"abcG" : FailingNumber()})),)):(<class 'NotImplementedError'>, NotImplementedError('int',)) -<<< Finished ->>> Testing *Iter* using d.update((("a", %s),)) -d.update((("a", FailingIter()),)):(<class 'TypeError'>, TypeError('unable to convert FailingIter to vim structure',)) -d.update((("a", FailingIterNext()),)):(<class 'NotImplementedError'>, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using d.update((("a", %s),)) -d.update((("a", None),)):(<class 'TypeError'>, TypeError('unable to convert NoneType to vim structure',)) -d.update((("a", {b"": 1}),)):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -d.update((("a", {"": 1}),)):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -d.update((("a", FailingMapping()),)):(<class 'NotImplementedError'>, NotImplementedError('keys',)) -d.update((("a", FailingMappingKey()),)):(<class 'NotImplementedError'>, NotImplementedError('getitem:mappingkey',)) -d.update((("a", FailingNumber()),)):(<class 'NotImplementedError'>, NotImplementedError('int',)) -<<< Finished ->> DictionaryPopItem -d.popitem(1, 2):(<class 'TypeError'>, TypeError('popitem() takes no arguments (2 given)',)) ->> DictionaryHasKey -d.has_key():(<class 'TypeError'>, TypeError('has_key() takes exactly one argument (0 given)',)) -> List ->> ListConstructor -vim.List(1, 2):(<class 'TypeError'>, TypeError('function takes at most 1 argument (2 given)',)) -vim.List(a=1):(<class 'TypeError'>, TypeError('list constructor does not accept keyword arguments',)) ->>> Testing *Iter* using vim.List(%s) -vim.List(FailingIter()):(<class 'NotImplementedError'>, NotImplementedError('iter',)) -vim.List(FailingIterNext()):(<class 'NotImplementedError'>, NotImplementedError('next',)) -<<< Finished ->>> Testing StringToChars using vim.List([{%s : 1}]) -vim.List([{1 : 1}]):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -vim.List([{b"\0" : 1}]):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -vim.List([{"\0" : 1}]):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using vim.List([{"abcF" : {%s : 1}}]) -vim.List([{"abcF" : {1 : 1}}]):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -vim.List([{"abcF" : {b"\0" : 1}}]):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -vim.List([{"abcF" : {"\0" : 1}}]):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using vim.List([{"abcF" : Mapping({%s : 1})}]) -vim.List([{"abcF" : Mapping({1 : 1})}]):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -vim.List([{"abcF" : Mapping({b"\0" : 1})}]):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -vim.List([{"abcF" : Mapping({"\0" : 1})}]):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using vim.List([{"abcF" : %s}]) -vim.List([{"abcF" : FailingIter()}]):(<class 'TypeError'>, TypeError('unable to convert FailingIter to vim structure',)) -vim.List([{"abcF" : FailingIterNext()}]):(<class 'NotImplementedError'>, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using vim.List([{"abcF" : %s}]) -vim.List([{"abcF" : None}]):(<class 'TypeError'>, TypeError('unable to convert NoneType to vim structure',)) -vim.List([{"abcF" : {b"": 1}}]):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -vim.List([{"abcF" : {"": 1}}]):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -vim.List([{"abcF" : FailingMapping()}]):(<class 'NotImplementedError'>, NotImplementedError('keys',)) -vim.List([{"abcF" : FailingMappingKey()}]):(<class 'NotImplementedError'>, NotImplementedError('getitem:mappingkey',)) -vim.List([{"abcF" : FailingNumber()}]):(<class 'NotImplementedError'>, NotImplementedError('int',)) -<<< Finished ->>> Testing StringToChars using vim.List([Mapping({%s : 1})]) -vim.List([Mapping({1 : 1})]):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -vim.List([Mapping({b"\0" : 1})]):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -vim.List([Mapping({"\0" : 1})]):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using vim.List([Mapping({"abcG" : {%s : 1}})]) -vim.List([Mapping({"abcG" : {1 : 1}})]):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -vim.List([Mapping({"abcG" : {b"\0" : 1}})]):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -vim.List([Mapping({"abcG" : {"\0" : 1}})]):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using vim.List([Mapping({"abcG" : Mapping({%s : 1})})]) -vim.List([Mapping({"abcG" : Mapping({1 : 1})})]):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -vim.List([Mapping({"abcG" : Mapping({b"\0" : 1})})]):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -vim.List([Mapping({"abcG" : Mapping({"\0" : 1})})]):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using vim.List([Mapping({"abcG" : %s})]) -vim.List([Mapping({"abcG" : FailingIter()})]):(<class 'TypeError'>, TypeError('unable to convert FailingIter to vim structure',)) -vim.List([Mapping({"abcG" : FailingIterNext()})]):(<class 'NotImplementedError'>, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using vim.List([Mapping({"abcG" : %s})]) -vim.List([Mapping({"abcG" : None})]):(<class 'TypeError'>, TypeError('unable to convert NoneType to vim structure',)) -vim.List([Mapping({"abcG" : {b"": 1}})]):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -vim.List([Mapping({"abcG" : {"": 1}})]):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -vim.List([Mapping({"abcG" : FailingMapping()})]):(<class 'NotImplementedError'>, NotImplementedError('keys',)) -vim.List([Mapping({"abcG" : FailingMappingKey()})]):(<class 'NotImplementedError'>, NotImplementedError('getitem:mappingkey',)) -vim.List([Mapping({"abcG" : FailingNumber()})]):(<class 'NotImplementedError'>, NotImplementedError('int',)) -<<< Finished ->>> Testing *Iter* using vim.List([%s]) -vim.List([FailingIter()]):(<class 'TypeError'>, TypeError('unable to convert FailingIter to vim structure',)) -vim.List([FailingIterNext()]):(<class 'NotImplementedError'>, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using vim.List([%s]) -vim.List([None]):(<class 'TypeError'>, TypeError('unable to convert NoneType to vim structure',)) -vim.List([{b"": 1}]):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -vim.List([{"": 1}]):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -vim.List([FailingMapping()]):(<class 'NotImplementedError'>, NotImplementedError('keys',)) -vim.List([FailingMappingKey()]):(<class 'NotImplementedError'>, NotImplementedError('getitem:mappingkey',)) -vim.List([FailingNumber()]):(<class 'NotImplementedError'>, NotImplementedError('int',)) -<<< Finished ->> ListItem -l[1000]:(<class 'IndexError'>, IndexError('list index out of range',)) ->> ListAssItem -ll[1] = 2:(<class 'vim.error'>, error('list is locked',)) -l[1000] = 3:(<class 'IndexError'>, IndexError('list index out of range',)) ->> ListAssSlice -ll[1:100] = "abcJ":(<class 'vim.error'>, error('list is locked',)) ->>> Testing *Iter* using l[:] = %s -l[:] = FailingIter():(<class 'NotImplementedError'>, NotImplementedError('iter',)) -l[:] = FailingIterNext():(<class 'NotImplementedError'>, NotImplementedError('next',)) -<<< Finished -nel[1:10:2] = "abcK":(<class 'ValueError'>, ValueError('attempt to assign sequence of size greater then 2 to extended slice',)) -(b'a', b'b', b'c', b'O') -nel[1:10:2] = "a":(<class 'ValueError'>, ValueError('attempt to assign sequence of size 1 to extended slice of size 2',)) -(b'a', b'b', b'c', b'O') -nel[1:1:-1] = "a":(<class 'ValueError'>, ValueError('attempt to assign sequence of size greater then 0 to extended slice',)) -(b'a', b'b', b'c', b'O') -nel[:] = FailingIterNextN(2):(<class 'NotImplementedError'>, NotImplementedError('next N',)) -(b'a', b'b', b'c', b'O') ->>> Testing StringToChars using l[:] = [{%s : 1}] -l[:] = [{1 : 1}]:(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -l[:] = [{b"\0" : 1}]:(<class 'TypeError'>, TypeError('expected bytes with no null',)) -l[:] = [{"\0" : 1}]:(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using l[:] = [{"abcF" : {%s : 1}}] -l[:] = [{"abcF" : {1 : 1}}]:(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -l[:] = [{"abcF" : {b"\0" : 1}}]:(<class 'TypeError'>, TypeError('expected bytes with no null',)) -l[:] = [{"abcF" : {"\0" : 1}}]:(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using l[:] = [{"abcF" : Mapping({%s : 1})}] -l[:] = [{"abcF" : Mapping({1 : 1})}]:(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -l[:] = [{"abcF" : Mapping({b"\0" : 1})}]:(<class 'TypeError'>, TypeError('expected bytes with no null',)) -l[:] = [{"abcF" : Mapping({"\0" : 1})}]:(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using l[:] = [{"abcF" : %s}] -l[:] = [{"abcF" : FailingIter()}]:(<class 'TypeError'>, TypeError('unable to convert FailingIter to vim structure',)) -l[:] = [{"abcF" : FailingIterNext()}]:(<class 'NotImplementedError'>, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using l[:] = [{"abcF" : %s}] -l[:] = [{"abcF" : None}]:(<class 'TypeError'>, TypeError('unable to convert NoneType to vim structure',)) -l[:] = [{"abcF" : {b"": 1}}]:(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -l[:] = [{"abcF" : {"": 1}}]:(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -l[:] = [{"abcF" : FailingMapping()}]:(<class 'NotImplementedError'>, NotImplementedError('keys',)) -l[:] = [{"abcF" : FailingMappingKey()}]:(<class 'NotImplementedError'>, NotImplementedError('getitem:mappingkey',)) -l[:] = [{"abcF" : FailingNumber()}]:(<class 'NotImplementedError'>, NotImplementedError('int',)) -<<< Finished ->>> Testing StringToChars using l[:] = [Mapping({%s : 1})] -l[:] = [Mapping({1 : 1})]:(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -l[:] = [Mapping({b"\0" : 1})]:(<class 'TypeError'>, TypeError('expected bytes with no null',)) -l[:] = [Mapping({"\0" : 1})]:(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using l[:] = [Mapping({"abcG" : {%s : 1}})] -l[:] = [Mapping({"abcG" : {1 : 1}})]:(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -l[:] = [Mapping({"abcG" : {b"\0" : 1}})]:(<class 'TypeError'>, TypeError('expected bytes with no null',)) -l[:] = [Mapping({"abcG" : {"\0" : 1}})]:(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using l[:] = [Mapping({"abcG" : Mapping({%s : 1})})] -l[:] = [Mapping({"abcG" : Mapping({1 : 1})})]:(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -l[:] = [Mapping({"abcG" : Mapping({b"\0" : 1})})]:(<class 'TypeError'>, TypeError('expected bytes with no null',)) -l[:] = [Mapping({"abcG" : Mapping({"\0" : 1})})]:(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using l[:] = [Mapping({"abcG" : %s})] -l[:] = [Mapping({"abcG" : FailingIter()})]:(<class 'TypeError'>, TypeError('unable to convert FailingIter to vim structure',)) -l[:] = [Mapping({"abcG" : FailingIterNext()})]:(<class 'NotImplementedError'>, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using l[:] = [Mapping({"abcG" : %s})] -l[:] = [Mapping({"abcG" : None})]:(<class 'TypeError'>, TypeError('unable to convert NoneType to vim structure',)) -l[:] = [Mapping({"abcG" : {b"": 1}})]:(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -l[:] = [Mapping({"abcG" : {"": 1}})]:(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -l[:] = [Mapping({"abcG" : FailingMapping()})]:(<class 'NotImplementedError'>, NotImplementedError('keys',)) -l[:] = [Mapping({"abcG" : FailingMappingKey()})]:(<class 'NotImplementedError'>, NotImplementedError('getitem:mappingkey',)) -l[:] = [Mapping({"abcG" : FailingNumber()})]:(<class 'NotImplementedError'>, NotImplementedError('int',)) -<<< Finished ->>> Testing *Iter* using l[:] = [%s] -l[:] = [FailingIter()]:(<class 'TypeError'>, TypeError('unable to convert FailingIter to vim structure',)) -l[:] = [FailingIterNext()]:(<class 'NotImplementedError'>, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using l[:] = [%s] -l[:] = [None]:(<class 'TypeError'>, TypeError('unable to convert NoneType to vim structure',)) -l[:] = [{b"": 1}]:(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -l[:] = [{"": 1}]:(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -l[:] = [FailingMapping()]:(<class 'NotImplementedError'>, NotImplementedError('keys',)) -l[:] = [FailingMappingKey()]:(<class 'NotImplementedError'>, NotImplementedError('getitem:mappingkey',)) -l[:] = [FailingNumber()]:(<class 'NotImplementedError'>, NotImplementedError('int',)) -<<< Finished ->> ListConcatInPlace ->>> Testing *Iter* using l.extend(%s) -l.extend(FailingIter()):(<class 'NotImplementedError'>, NotImplementedError('iter',)) -l.extend(FailingIterNext()):(<class 'NotImplementedError'>, NotImplementedError('next',)) -<<< Finished ->>> Testing StringToChars using l.extend([{%s : 1}]) -l.extend([{1 : 1}]):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -l.extend([{b"\0" : 1}]):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -l.extend([{"\0" : 1}]):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using l.extend([{"abcF" : {%s : 1}}]) -l.extend([{"abcF" : {1 : 1}}]):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -l.extend([{"abcF" : {b"\0" : 1}}]):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -l.extend([{"abcF" : {"\0" : 1}}]):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using l.extend([{"abcF" : Mapping({%s : 1})}]) -l.extend([{"abcF" : Mapping({1 : 1})}]):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -l.extend([{"abcF" : Mapping({b"\0" : 1})}]):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -l.extend([{"abcF" : Mapping({"\0" : 1})}]):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using l.extend([{"abcF" : %s}]) -l.extend([{"abcF" : FailingIter()}]):(<class 'TypeError'>, TypeError('unable to convert FailingIter to vim structure',)) -l.extend([{"abcF" : FailingIterNext()}]):(<class 'NotImplementedError'>, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using l.extend([{"abcF" : %s}]) -l.extend([{"abcF" : None}]):(<class 'TypeError'>, TypeError('unable to convert NoneType to vim structure',)) -l.extend([{"abcF" : {b"": 1}}]):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -l.extend([{"abcF" : {"": 1}}]):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -l.extend([{"abcF" : FailingMapping()}]):(<class 'NotImplementedError'>, NotImplementedError('keys',)) -l.extend([{"abcF" : FailingMappingKey()}]):(<class 'NotImplementedError'>, NotImplementedError('getitem:mappingkey',)) -l.extend([{"abcF" : FailingNumber()}]):(<class 'NotImplementedError'>, NotImplementedError('int',)) -<<< Finished ->>> Testing StringToChars using l.extend([Mapping({%s : 1})]) -l.extend([Mapping({1 : 1})]):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -l.extend([Mapping({b"\0" : 1})]):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -l.extend([Mapping({"\0" : 1})]):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using l.extend([Mapping({"abcG" : {%s : 1}})]) -l.extend([Mapping({"abcG" : {1 : 1}})]):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -l.extend([Mapping({"abcG" : {b"\0" : 1}})]):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -l.extend([Mapping({"abcG" : {"\0" : 1}})]):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using l.extend([Mapping({"abcG" : Mapping({%s : 1})})]) -l.extend([Mapping({"abcG" : Mapping({1 : 1})})]):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -l.extend([Mapping({"abcG" : Mapping({b"\0" : 1})})]):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -l.extend([Mapping({"abcG" : Mapping({"\0" : 1})})]):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using l.extend([Mapping({"abcG" : %s})]) -l.extend([Mapping({"abcG" : FailingIter()})]):(<class 'TypeError'>, TypeError('unable to convert FailingIter to vim structure',)) -l.extend([Mapping({"abcG" : FailingIterNext()})]):(<class 'NotImplementedError'>, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using l.extend([Mapping({"abcG" : %s})]) -l.extend([Mapping({"abcG" : None})]):(<class 'TypeError'>, TypeError('unable to convert NoneType to vim structure',)) -l.extend([Mapping({"abcG" : {b"": 1}})]):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -l.extend([Mapping({"abcG" : {"": 1}})]):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -l.extend([Mapping({"abcG" : FailingMapping()})]):(<class 'NotImplementedError'>, NotImplementedError('keys',)) -l.extend([Mapping({"abcG" : FailingMappingKey()})]):(<class 'NotImplementedError'>, NotImplementedError('getitem:mappingkey',)) -l.extend([Mapping({"abcG" : FailingNumber()})]):(<class 'NotImplementedError'>, NotImplementedError('int',)) -<<< Finished ->>> Testing *Iter* using l.extend([%s]) -l.extend([FailingIter()]):(<class 'TypeError'>, TypeError('unable to convert FailingIter to vim structure',)) -l.extend([FailingIterNext()]):(<class 'NotImplementedError'>, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using l.extend([%s]) -l.extend([None]):(<class 'TypeError'>, TypeError('unable to convert NoneType to vim structure',)) -l.extend([{b"": 1}]):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -l.extend([{"": 1}]):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -l.extend([FailingMapping()]):(<class 'NotImplementedError'>, NotImplementedError('keys',)) -l.extend([FailingMappingKey()]):(<class 'NotImplementedError'>, NotImplementedError('getitem:mappingkey',)) -l.extend([FailingNumber()]):(<class 'NotImplementedError'>, NotImplementedError('int',)) -<<< Finished ->> ListSetattr -del l.locked:(<class 'AttributeError'>, AttributeError('cannot delete vim.List attributes',)) -l.locked = FailingTrue():(<class 'NotImplementedError'>, NotImplementedError('bool',)) -l.xxx = True:(<class 'AttributeError'>, AttributeError('cannot set attribute xxx',)) -> Function ->> FunctionConstructor -vim.Function("123"):(<class 'ValueError'>, ValueError('unnamed function 123 does not exist',)) -vim.Function("xxx_non_existent_function_xxx"):(<class 'ValueError'>, ValueError('function xxx_non_existent_function_xxx does not exist',)) -vim.Function("xxx#non#existent#function#xxx"):NOT FAILED ->> FunctionCall ->>> Testing StringToChars using f({%s : 1}) -f({1 : 1}):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -f({b"\0" : 1}):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -f({"\0" : 1}):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using f({"abcF" : {%s : 1}}) -f({"abcF" : {1 : 1}}):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -f({"abcF" : {b"\0" : 1}}):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -f({"abcF" : {"\0" : 1}}):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using f({"abcF" : Mapping({%s : 1})}) -f({"abcF" : Mapping({1 : 1})}):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -f({"abcF" : Mapping({b"\0" : 1})}):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -f({"abcF" : Mapping({"\0" : 1})}):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using f({"abcF" : %s}) -f({"abcF" : FailingIter()}):(<class 'TypeError'>, TypeError('unable to convert FailingIter to vim structure',)) -f({"abcF" : FailingIterNext()}):(<class 'NotImplementedError'>, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using f({"abcF" : %s}) -f({"abcF" : None}):(<class 'TypeError'>, TypeError('unable to convert NoneType to vim structure',)) -f({"abcF" : {b"": 1}}):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -f({"abcF" : {"": 1}}):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -f({"abcF" : FailingMapping()}):(<class 'NotImplementedError'>, NotImplementedError('keys',)) -f({"abcF" : FailingMappingKey()}):(<class 'NotImplementedError'>, NotImplementedError('getitem:mappingkey',)) -f({"abcF" : FailingNumber()}):(<class 'NotImplementedError'>, NotImplementedError('int',)) -<<< Finished ->>> Testing StringToChars using f(Mapping({%s : 1})) -f(Mapping({1 : 1})):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -f(Mapping({b"\0" : 1})):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -f(Mapping({"\0" : 1})):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using f(Mapping({"abcG" : {%s : 1}})) -f(Mapping({"abcG" : {1 : 1}})):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -f(Mapping({"abcG" : {b"\0" : 1}})):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -f(Mapping({"abcG" : {"\0" : 1}})):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using f(Mapping({"abcG" : Mapping({%s : 1})})) -f(Mapping({"abcG" : Mapping({1 : 1})})):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -f(Mapping({"abcG" : Mapping({b"\0" : 1})})):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -f(Mapping({"abcG" : Mapping({"\0" : 1})})):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using f(Mapping({"abcG" : %s})) -f(Mapping({"abcG" : FailingIter()})):(<class 'TypeError'>, TypeError('unable to convert FailingIter to vim structure',)) -f(Mapping({"abcG" : FailingIterNext()})):(<class 'NotImplementedError'>, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using f(Mapping({"abcG" : %s})) -f(Mapping({"abcG" : None})):(<class 'TypeError'>, TypeError('unable to convert NoneType to vim structure',)) -f(Mapping({"abcG" : {b"": 1}})):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -f(Mapping({"abcG" : {"": 1}})):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -f(Mapping({"abcG" : FailingMapping()})):(<class 'NotImplementedError'>, NotImplementedError('keys',)) -f(Mapping({"abcG" : FailingMappingKey()})):(<class 'NotImplementedError'>, NotImplementedError('getitem:mappingkey',)) -f(Mapping({"abcG" : FailingNumber()})):(<class 'NotImplementedError'>, NotImplementedError('int',)) -<<< Finished ->>> Testing *Iter* using f(%s) -f(FailingIter()):(<class 'TypeError'>, TypeError('unable to convert FailingIter to vim structure',)) -f(FailingIterNext()):(<class 'NotImplementedError'>, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using f(%s) -f(None):(<class 'TypeError'>, TypeError('unable to convert NoneType to vim structure',)) -f({b"": 1}):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -f({"": 1}):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -f(FailingMapping()):(<class 'NotImplementedError'>, NotImplementedError('keys',)) -f(FailingMappingKey()):(<class 'NotImplementedError'>, NotImplementedError('getitem:mappingkey',)) -f(FailingNumber()):(<class 'NotImplementedError'>, NotImplementedError('int',)) -<<< Finished ->>> Testing StringToChars using fd(self={%s : 1}) -fd(self={1 : 1}):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -fd(self={b"\0" : 1}):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -fd(self={"\0" : 1}):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using fd(self={"abcF" : {%s : 1}}) -fd(self={"abcF" : {1 : 1}}):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -fd(self={"abcF" : {b"\0" : 1}}):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -fd(self={"abcF" : {"\0" : 1}}):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using fd(self={"abcF" : Mapping({%s : 1})}) -fd(self={"abcF" : Mapping({1 : 1})}):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -fd(self={"abcF" : Mapping({b"\0" : 1})}):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -fd(self={"abcF" : Mapping({"\0" : 1})}):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using fd(self={"abcF" : %s}) -fd(self={"abcF" : FailingIter()}):(<class 'TypeError'>, TypeError('unable to convert FailingIter to vim structure',)) -fd(self={"abcF" : FailingIterNext()}):(<class 'NotImplementedError'>, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using fd(self={"abcF" : %s}) -fd(self={"abcF" : None}):(<class 'TypeError'>, TypeError('unable to convert NoneType to vim structure',)) -fd(self={"abcF" : {b"": 1}}):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -fd(self={"abcF" : {"": 1}}):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -fd(self={"abcF" : FailingMapping()}):(<class 'NotImplementedError'>, NotImplementedError('keys',)) -fd(self={"abcF" : FailingMappingKey()}):(<class 'NotImplementedError'>, NotImplementedError('getitem:mappingkey',)) -fd(self={"abcF" : FailingNumber()}):(<class 'NotImplementedError'>, NotImplementedError('int',)) -<<< Finished ->>> Testing StringToChars using fd(self=Mapping({%s : 1})) -fd(self=Mapping({1 : 1})):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -fd(self=Mapping({b"\0" : 1})):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -fd(self=Mapping({"\0" : 1})):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using fd(self=Mapping({"abcG" : {%s : 1}})) -fd(self=Mapping({"abcG" : {1 : 1}})):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -fd(self=Mapping({"abcG" : {b"\0" : 1}})):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -fd(self=Mapping({"abcG" : {"\0" : 1}})):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing StringToChars using fd(self=Mapping({"abcG" : Mapping({%s : 1})})) -fd(self=Mapping({"abcG" : Mapping({1 : 1})})):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -fd(self=Mapping({"abcG" : Mapping({b"\0" : 1})})):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -fd(self=Mapping({"abcG" : Mapping({"\0" : 1})})):(<class 'TypeError'>, TypeError('expected bytes with no null',)) -<<< Finished ->>> Testing *Iter* using fd(self=Mapping({"abcG" : %s})) -fd(self=Mapping({"abcG" : FailingIter()})):(<class 'TypeError'>, TypeError('unable to convert FailingIter to vim structure',)) -fd(self=Mapping({"abcG" : FailingIterNext()})):(<class 'NotImplementedError'>, NotImplementedError('next',)) -<<< Finished ->>> Testing ConvertFromPyObject using fd(self=Mapping({"abcG" : %s})) -fd(self=Mapping({"abcG" : None})):(<class 'TypeError'>, TypeError('unable to convert NoneType to vim structure',)) -fd(self=Mapping({"abcG" : {b"": 1}})):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -fd(self=Mapping({"abcG" : {"": 1}})):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -fd(self=Mapping({"abcG" : FailingMapping()})):(<class 'NotImplementedError'>, NotImplementedError('keys',)) -fd(self=Mapping({"abcG" : FailingMappingKey()})):(<class 'NotImplementedError'>, NotImplementedError('getitem:mappingkey',)) -fd(self=Mapping({"abcG" : FailingNumber()})):(<class 'NotImplementedError'>, NotImplementedError('int',)) -<<< Finished ->>> Testing *Iter* using fd(self=%s) -fd(self=FailingIter()):(<class 'TypeError'>, TypeError('unable to convert FailingIter to vim dictionary',)) -fd(self=FailingIterNext()):(<class 'TypeError'>, TypeError('unable to convert FailingIterNext to vim dictionary',)) -<<< Finished ->>> Testing ConvertFromPyObject using fd(self=%s) -fd(self=None):(<class 'TypeError'>, TypeError('unable to convert NoneType to vim dictionary',)) -fd(self={b"": 1}):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -fd(self={"": 1}):(<class 'ValueError'>, ValueError('empty keys are not allowed',)) -fd(self=FailingMapping()):(<class 'NotImplementedError'>, NotImplementedError('keys',)) -fd(self=FailingMappingKey()):(<class 'NotImplementedError'>, NotImplementedError('getitem:mappingkey',)) -fd(self=FailingNumber()):(<class 'TypeError'>, TypeError('unable to convert FailingNumber to vim dictionary',)) -<<< Finished ->>> Testing ConvertFromPyMapping using fd(self=%s) -fd(self=[]):(<class 'AttributeError'>, AttributeError('keys',)) -<<< Finished -> TabPage ->> TabPageAttr -vim.current.tabpage.xxx:(<class 'AttributeError'>, AttributeError("'vim.tabpage' object has no attribute 'xxx'",)) -> TabList ->> TabListItem -vim.tabpages[1000]:(<class 'IndexError'>, IndexError('no such tab page',)) -> Window ->> WindowAttr -vim.current.window.xxx:(<class 'AttributeError'>, AttributeError("'vim.window' object has no attribute 'xxx'",)) ->> WindowSetattr -vim.current.window.buffer = 0:(<class 'TypeError'>, TypeError('readonly attribute: buffer',)) -vim.current.window.cursor = (100000000, 100000000):(<class 'vim.error'>, error('cursor position outside buffer',)) -vim.current.window.cursor = True:(<class 'TypeError'>, TypeError('argument must be 2-item sequence, not bool',)) ->>> Testing NumberToLong using vim.current.window.height = %s -vim.current.window.height = []:(<class 'TypeError'>, TypeError('expected int() or something supporting coercing to int(), but got list',)) -vim.current.window.height = None:(<class 'TypeError'>, TypeError('expected int() or something supporting coercing to int(), but got NoneType',)) -vim.current.window.height = -1:(<class 'ValueError'>, ValueError('number must be greater or equal to zero',)) -<<< Finished ->>> Testing NumberToLong using vim.current.window.width = %s -vim.current.window.width = []:(<class 'TypeError'>, TypeError('expected int() or something supporting coercing to int(), but got list',)) -vim.current.window.width = None:(<class 'TypeError'>, TypeError('expected int() or something supporting coercing to int(), but got NoneType',)) -vim.current.window.width = -1:(<class 'ValueError'>, ValueError('number must be greater or equal to zero',)) -<<< Finished -vim.current.window.xxxxxx = True:(<class 'AttributeError'>, AttributeError('xxxxxx',)) -> WinList ->> WinListItem -vim.windows[1000]:(<class 'IndexError'>, IndexError('no such window',)) -> Buffer ->> StringToLine (indirect) -vim.current.buffer[0] = "\na":(<class 'vim.error'>, error('string cannot contain newlines',)) -vim.current.buffer[0] = b"\na":(<class 'vim.error'>, error('string cannot contain newlines',)) ->> SetBufferLine (indirect) -vim.current.buffer[0] = True:(<class 'TypeError'>, TypeError('bad argument type for built-in operation',)) ->> SetBufferLineList (indirect) -vim.current.buffer[:] = True:(<class 'TypeError'>, TypeError('bad argument type for built-in operation',)) -vim.current.buffer[:] = ["\na", "bc"]:(<class 'vim.error'>, error('string cannot contain newlines',)) ->> InsertBufferLines (indirect) -vim.current.buffer.append(None):(<class 'TypeError'>, TypeError('bad argument type for built-in operation',)) -vim.current.buffer.append(["\na", "bc"]):(<class 'vim.error'>, error('string cannot contain newlines',)) -vim.current.buffer.append("\nbc"):(<class 'vim.error'>, error('string cannot contain newlines',)) ->> RBItem -vim.current.buffer[100000000]:(<class 'IndexError'>, IndexError('line number out of range',)) ->> RBAsItem -vim.current.buffer[100000000] = "":(<class 'IndexError'>, IndexError('line number out of range',)) ->> BufferAttr -vim.current.buffer.xxx:(<class 'AttributeError'>, AttributeError("'vim.buffer' object has no attribute 'xxx'",)) ->> BufferSetattr -vim.current.buffer.name = True:(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got bool',)) -vim.current.buffer.xxx = True:(<class 'AttributeError'>, AttributeError('xxx',)) ->> BufferMark -vim.current.buffer.mark(0):(<class 'TypeError'>, TypeError('expected bytes() or str() instance, but got int',)) -vim.current.buffer.mark("abcM"):(<class 'ValueError'>, ValueError('mark name must be a single character',)) -vim.current.buffer.mark("!"):(<class 'vim.error'>, error('invalid mark name',)) ->> BufferRange -vim.current.buffer.range(1, 2, 3):(<class 'TypeError'>, TypeError('function takes exactly 2 arguments (3 given)',)) -> BufMap ->> BufMapItem -vim.buffers[100000000]:(<class 'KeyError'>, KeyError(100000000,)) ->>> Testing NumberToLong using vim.buffers[%s] -vim.buffers[[]]:(<class 'TypeError'>, TypeError('expected int() or something supporting coercing to int(), but got list',)) -vim.buffers[None]:(<class 'TypeError'>, TypeError('expected int() or something supporting coercing to int(), but got NoneType',)) -vim.buffers[-1]:(<class 'ValueError'>, ValueError('number must be greater then zero',)) -vim.buffers[0]:(<class 'ValueError'>, ValueError('number must be greater then zero',)) -<<< Finished -> Current ->> CurrentGetattr -vim.current.xxx:(<class 'AttributeError'>, AttributeError("'vim.currentdata' object has no attribute 'xxx'",)) ->> CurrentSetattr -vim.current.line = True:(<class 'TypeError'>, TypeError('bad argument type for built-in operation',)) -vim.current.buffer = True:(<class 'TypeError'>, TypeError('expected vim.Buffer object, but got bool',)) -vim.current.window = True:(<class 'TypeError'>, TypeError('expected vim.Window object, but got bool',)) -vim.current.tabpage = True:(<class 'TypeError'>, TypeError('expected vim.TabPage object, but got bool',)) -vim.current.xxx = True:(<class 'AttributeError'>, AttributeError('xxx',)) -['.'] -'.' -3,xx -before -after -pythonx/topmodule/__init__.py -pythonx/topmodule/submodule/__init__.py -pythonx/topmodule/submodule/subsubmodule/subsubsubmodule.py -vim.command("throw 'abcN'"):(<class 'vim.error'>, error('abcN',)) -Exe("throw 'def'"):(<class 'vim.error'>, error('def',)) -vim.eval("Exe('throw ''ghi''')"):(<class 'vim.error'>, error('ghi',)) -vim.eval("Exe('echoerr ''jkl''')"):(<class 'vim.error'>, error('Vim(echoerr):jkl',)) -vim.eval("Exe('xxx_non_existent_command_xxx')"):(<class 'vim.error'>, error('Vim:E492: Not an editor command: xxx_non_existent_command_xxx',)) -vim.eval("xxx_unknown_function_xxx()"):(<class 'vim.error'>, error('Vim:E117: Unknown function: xxx_unknown_function_xxx',)) -vim.bindeval("Exe('xxx_non_existent_command_xxx')"):(<class 'vim.error'>, error('Vim:E492: Not an editor command: xxx_non_existent_command_xxx',)) -Caught KeyboardInterrupt -Running :put -No exception - diff --git a/src/nvim/testdir/test_command_count.in b/src/nvim/testdir/test_command_count.in index dd3c108360..170c810923 100644 --- a/src/nvim/testdir/test_command_count.in +++ b/src/nvim/testdir/test_command_count.in @@ -141,6 +141,7 @@ STARTTEST :let buffers = '' :.,$-bufdo let buffers .= ' '.bufnr('%') :call add(g:lines, 'bufdo:' . buffers) +:3bd :let buffers = '' :3,7bufdo let buffers .= ' '.bufnr('%') :call add(g:lines, 'bufdo:' . buffers) diff --git a/src/nvim/testdir/test_command_count.ok b/src/nvim/testdir/test_command_count.ok index 8fdbc7748d..e74155ec1b 100644 --- a/src/nvim/testdir/test_command_count.ok +++ b/src/nvim/testdir/test_command_count.ok @@ -34,5 +34,5 @@ aaa: 0 bbb: 0 ccc: 0 argdo: c d e windo: 2 3 4 bufdo: 2 3 4 5 6 7 8 9 10 15 -bufdo: 3 4 5 6 7 +bufdo: 4 5 6 7 tabdo: 2 3 4 diff --git a/src/nvim/tui/term_input.inl b/src/nvim/tui/term_input.inl index d25cbb7ba1..c396557160 100644 --- a/src/nvim/tui/term_input.inl +++ b/src/nvim/tui/term_input.inl @@ -4,7 +4,8 @@ #include "nvim/misc2.h" #include "nvim/os/os.h" #include "nvim/os/input.h" -#include "nvim/os/rstream.h" +#include "nvim/event/rstream.h" +#include "nvim/event/time.h" #define PASTETOGGLE_KEY "<f37>" @@ -12,9 +13,8 @@ struct term_input { int in_fd; bool paste_enabled; TermKey *tk; - uv_timer_t timer_handle; - RBuffer *read_buffer; - RStream *read_stream; + TimeWatcher timer_handle; + Stream read_stream; }; static void forward_simple_utf8(TermKeyKey *key) @@ -107,7 +107,7 @@ static TermKeyResult tk_getkey(TermKey *tk, TermKeyKey *key, bool force) return force ? termkey_getkey_force(tk, key) : termkey_getkey(tk, key); } -static void timer_cb(uv_timer_t *handle); +static void timer_cb(TimeWatcher *watcher, void *data); static int get_key_code_timeout(void) { @@ -147,28 +147,26 @@ static void tk_getkeys(TermInput *input, bool force) if (ms > 0) { // Stop the current timer if already running - uv_timer_stop(&input->timer_handle); - uv_timer_start(&input->timer_handle, timer_cb, (uint32_t)ms, 0); + time_watcher_stop(&input->timer_handle); + time_watcher_start(&input->timer_handle, timer_cb, (uint32_t)ms, 0); } else { tk_getkeys(input, true); } } - -static void timer_cb(uv_timer_t *handle) +static void timer_cb(TimeWatcher *watcher, void *data) { - tk_getkeys(handle->data, true); + tk_getkeys(data, true); } static bool handle_bracketed_paste(TermInput *input) { - char *ptr = rbuffer_read_ptr(input->read_buffer); - size_t len = rbuffer_pending(input->read_buffer); - if (len > 5 && (!strncmp(ptr, "\x1b[200~", 6) - || !strncmp(ptr, "\x1b[201~", 6))) { - bool enable = ptr[4] == '0'; + if (rbuffer_size(input->read_stream.buffer) > 5 && + (!rbuffer_cmp(input->read_stream.buffer, "\x1b[200~", 6) || + !rbuffer_cmp(input->read_stream.buffer, "\x1b[201~", 6))) { + bool enable = *rbuffer_get(input->read_stream.buffer, 4) == '0'; // Advance past the sequence - rbuffer_consumed(input->read_buffer, 6); + rbuffer_consumed(input->read_stream.buffer, 6); if (input->paste_enabled == enable) { return true; } @@ -195,21 +193,25 @@ static bool handle_bracketed_paste(TermInput *input) static bool handle_forced_escape(TermInput *input) { - char *ptr = rbuffer_read_ptr(input->read_buffer); - size_t len = rbuffer_pending(input->read_buffer); - if (len > 1 && ptr[0] == ESC && ptr[1] == NUL) { + if (rbuffer_size(input->read_stream.buffer) > 1 + && !rbuffer_cmp(input->read_stream.buffer, "\x1b\x00", 2)) { // skip the ESC and NUL and push one <esc> to the input buffer - termkey_push_bytes(input->tk, ptr, 1); - rbuffer_consumed(input->read_buffer, 2); + size_t rcnt; + termkey_push_bytes(input->tk, rbuffer_read_ptr(input->read_stream.buffer, + &rcnt), 1); + rbuffer_consumed(input->read_stream.buffer, 2); tk_getkeys(input, true); return true; } return false; } -static void read_cb(RStream *rstream, void *rstream_data, bool eof) +static void restart_reading(void **argv); + +static void read_cb(Stream *stream, RBuffer *buf, size_t c, void *data, + bool eof) { - TermInput *input = rstream_data; + TermInput *input = data; if (eof) { if (input->in_fd == 0 && !os_isatty(0) && os_isatty(2)) { @@ -224,8 +226,8 @@ static void read_cb(RStream *rstream, void *rstream_data, bool eof) // // ls *.md | xargs nvim input->in_fd = 2; - rstream_set_file(input->read_stream, input->in_fd); - rstream_start(input->read_stream); + stream_close(&input->read_stream, NULL); + queue_put(loop.fast_events, restart_reading, 1, input); } else { input_done(); } @@ -236,19 +238,45 @@ static void read_cb(RStream *rstream, void *rstream_data, bool eof) if (handle_bracketed_paste(input) || handle_forced_escape(input)) { continue; } - char *ptr = rbuffer_read_ptr(input->read_buffer); - size_t len = rbuffer_pending(input->read_buffer); - // Find the next 'esc' and push everything up to it(excluding) - size_t i; - for (i = ptr[0] == ESC ? 1 : 0; i < len; i++) { - if (ptr[i] == '\x1b') { + + // Find the next 'esc' and push everything up to it(excluding). This is done + // so the `handle_bracketed_paste`/`handle_forced_escape` calls above work + // as expected. + size_t count = 0; + RBUFFER_EACH(input->read_stream.buffer, c, i) { + count = i + 1; + if (c == '\x1b' && count > 1) { + count--; break; } } - size_t consumed = termkey_push_bytes(input->tk, ptr, i); - rbuffer_consumed(input->read_buffer, consumed); - tk_getkeys(input, false); - } while (rbuffer_pending(input->read_buffer)); + + RBUFFER_UNTIL_EMPTY(input->read_stream.buffer, ptr, len) { + size_t consumed = termkey_push_bytes(input->tk, ptr, MIN(count, len)); + // termkey_push_bytes can return (size_t)-1, so it is possible that + // `consumed > input->read_stream.buffer->size`, but since tk_getkeys is + // called soon, it shouldn't happen + assert(consumed <= input->read_stream.buffer->size); + rbuffer_consumed(input->read_stream.buffer, consumed); + // Need to process the keys now since there's no guarantee "count" will + // fit into libtermkey's input buffer. + tk_getkeys(input, false); + if (!(count -= consumed)) { + break; + } + } + } while (rbuffer_size(input->read_stream.buffer)); + + // Make sure the next input escape sequence fits into the ring buffer + // without wrap around, otherwise it could be misinterpreted. + rbuffer_reset(input->read_stream.buffer); +} + +static void restart_reading(void **argv) +{ + TermInput *input = argv[0]; + rstream_init_fd(&loop, &input->read_stream, input->in_fd, 0xfff, input); + rstream_start(&input->read_stream, read_cb); } static TermInput *term_input_new(void) @@ -265,13 +293,10 @@ static TermInput *term_input_new(void) int curflags = termkey_get_canonflags(rv->tk); termkey_set_canonflags(rv->tk, curflags | TERMKEY_CANON_DELBS); // setup input handle - rv->read_buffer = rbuffer_new(0xfff); - rv->read_stream = rstream_new(read_cb, rv->read_buffer, rv); - rstream_set_file(rv->read_stream, rv->in_fd); - rstream_start(rv->read_stream); + rstream_init_fd(&loop, &rv->read_stream, rv->in_fd, 0xfff, rv); + rstream_start(&rv->read_stream, read_cb); // initialize a timer handle for handling ESC with libtermkey - uv_timer_init(uv_default_loop(), &rv->timer_handle); - rv->timer_handle.data = rv; + time_watcher_init(&loop, &rv->timer_handle, rv); // Set the pastetoggle option to a special key that will be sent when // \e[20{0,1}~/ are received Error err = ERROR_INIT; @@ -282,12 +307,13 @@ static TermInput *term_input_new(void) static void term_input_destroy(TermInput *input) { - uv_timer_stop(&input->timer_handle); - rstream_stop(input->read_stream); - rstream_free(input->read_stream); - uv_close((uv_handle_t *)&input->timer_handle, NULL); + time_watcher_stop(&input->timer_handle); + time_watcher_close(&input->timer_handle, NULL); + rstream_stop(&input->read_stream); + stream_close(&input->read_stream, NULL); termkey_destroy(input->tk); - event_poll(0); // Run once to remove references to input/timer handles + // Run once to remove references to input/timer handles + loop_poll_events(&loop, 0); xfree(input); } diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index fe29dbd961..57a2b896f7 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -1,6 +1,7 @@ #include <assert.h> #include <stdbool.h> #include <stdio.h> +#include <limits.h> #include <uv.h> #include <unibilium.h> @@ -13,7 +14,8 @@ #include "nvim/memory.h" #include "nvim/api/vim.h" #include "nvim/api/private/helpers.h" -#include "nvim/os/event.h" +#include "nvim/event/loop.h" +#include "nvim/event/signal.h" #include "nvim/tui/tui.h" #include "nvim/strings.h" @@ -43,7 +45,7 @@ typedef struct { uv_loop_t *write_loop; unibi_term *ut; uv_tty_t output_handle; - uv_signal_t winch_handle; + SignalWatcher winch_handle; Rect scroll_region; kvec_t(Rect) invalid_regions; int row, col; @@ -55,14 +57,17 @@ typedef struct { bool busy; HlAttrs attrs, print_attrs; Cell **screen; + int showing_mode; struct { int enable_mouse, disable_mouse; int enable_bracketed_paste, disable_bracketed_paste; - int enter_insert_mode, exit_insert_mode; + int enter_insert_mode, enter_replace_mode, exit_insert_mode; int set_rgb_foreground, set_rgb_background; } unibi_ext; } TUIData; +static bool volatile got_winch = false; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "tui/tui.c.generated.h" #endif @@ -96,11 +101,13 @@ UI *tui_start(void) data->can_use_terminal_scroll = true; data->bufpos = 0; data->bufsize = sizeof(data->buf) - CNORM_COMMAND_MAX_SIZE; + data->showing_mode = 0; data->unibi_ext.enable_mouse = -1; data->unibi_ext.disable_mouse = -1; data->unibi_ext.enable_bracketed_paste = -1; data->unibi_ext.disable_bracketed_paste = -1; data->unibi_ext.enter_insert_mode = -1; + data->unibi_ext.enter_replace_mode = -1; data->unibi_ext.exit_insert_mode = -1; // write output to stderr if stdout is not a tty @@ -132,9 +139,8 @@ UI *tui_start(void) update_size(ui); // listen for SIGWINCH - uv_signal_init(uv_default_loop(), &data->winch_handle); - uv_signal_start(&data->winch_handle, sigwinch_cb, SIGWINCH); - data->winch_handle.data = ui; + signal_watcher_init(&loop, &data->winch_handle, ui); + signal_watcher_start(&data->winch_handle, sigwinch_cb, SIGWINCH); ui->stop = tui_stop; ui->rgb = os_getenv("NVIM_TUI_ENABLE_TRUE_COLOR") != NULL; @@ -147,8 +153,7 @@ UI *tui_start(void) ui->busy_stop = tui_busy_stop; ui->mouse_on = tui_mouse_on; ui->mouse_off = tui_mouse_off; - ui->insert_mode = tui_insert_mode; - ui->normal_mode = tui_normal_mode; + ui->mode_change = tui_mode_change; ui->set_scroll_region = tui_set_scroll_region; ui->scroll = tui_scroll; ui->highlight_set = tui_highlight_set; @@ -172,12 +177,12 @@ static void tui_stop(UI *ui) TUIData *data = ui->data; // Destroy common stuff kv_destroy(data->invalid_regions); - uv_signal_stop(&data->winch_handle); - uv_close((uv_handle_t *)&data->winch_handle, NULL); + signal_watcher_stop(&data->winch_handle); + signal_watcher_close(&data->winch_handle, NULL); // Destroy input stuff term_input_destroy(data->input); // Destroy output stuff - tui_normal_mode(ui); + tui_mode_change(ui, NORMAL); tui_mouse_off(ui); unibi_out(ui, unibi_exit_attribute_mode); // cursor should be set to normal before exiting alternate screen @@ -200,23 +205,14 @@ static void tui_stop(UI *ui) xfree(ui); } -static void try_resize(Event ev) +static void sigwinch_cb(SignalWatcher *watcher, int signum, void *data) { - UI *ui = ev.data; + got_winch = true; + UI *ui = data; update_size(ui); ui_refresh(); } -static void sigwinch_cb(uv_signal_t *handle, int signum) -{ - // Queue the event because resizing can result in recursive event_poll calls - // FIXME(blueyed): TUI does not resize properly when not deferred. Why? #2322 - event_push((Event) { - .data = handle->data, - .handler = try_resize - }, true); -} - static bool attrs_differ(HlAttrs a1, HlAttrs a2) { return a1.foreground != a2.foreground || a1.background != a2.background @@ -350,6 +346,14 @@ static void tui_resize(UI *ui, int width, int height) data->scroll_region.left = 0; data->scroll_region.right = width - 1; data->row = data->col = 0; + + if (!got_winch) { // Try to resize the terminal window. + char r[16]; // enough for 9999x9999 + snprintf(r, sizeof(r), "\x1b[8;%d;%dt", height, width); + out(ui, r, strlen(r)); + } else { // Already handled the SIGWINCH signal; avoid double-resize. + got_winch = false; + } } static void tui_clear(UI *ui) @@ -398,16 +402,25 @@ static void tui_mouse_off(UI *ui) data->mouse_enabled = false; } -static void tui_insert_mode(UI *ui) +static void tui_mode_change(UI *ui, int mode) { TUIData *data = ui->data; - unibi_out(ui, data->unibi_ext.enter_insert_mode); -} -static void tui_normal_mode(UI *ui) -{ - TUIData *data = ui->data; - unibi_out(ui, data->unibi_ext.exit_insert_mode); + if (mode == INSERT) { + if (data->showing_mode != INSERT) { + unibi_out(ui, data->unibi_ext.enter_insert_mode); + } + } else if (mode == REPLACE) { + if (data->showing_mode != REPLACE) { + unibi_out(ui, data->unibi_ext.enter_replace_mode); + } + } else { + assert(mode == NORMAL); + if (data->showing_mode != NORMAL) { + unibi_out(ui, data->unibi_ext.exit_insert_mode); + } + } + data->showing_mode = mode; } static void tui_set_scroll_region(UI *ui, int top, int bot, int left, @@ -641,12 +654,22 @@ static void update_size(UI *ui) { TUIData *data = ui->data; int width = 0, height = 0; - // 1 - try from a system call(ioctl/TIOCGWINSZ on unix) + + // 1 - look for non-default 'columns' and 'lines' options during startup + if (starting != 0 && (Columns != DFLT_COLS || Rows != DFLT_ROWS)) { + assert(Columns >= INT_MIN && Columns <= INT_MAX); + assert(Rows >= INT_MIN && Rows <= INT_MAX); + width = (int)Columns; + height = (int)Rows; + goto end; + } + + // 2 - try from a system call(ioctl/TIOCGWINSZ on unix) if (!uv_tty_get_winsize(&data->output_handle, &width, &height)) { goto end; } - // 2 - use $LINES/$COLUMNS if available + // 3 - use $LINES/$COLUMNS if available const char *val; int advance; if ((val = os_getenv("LINES")) @@ -656,15 +679,15 @@ static void update_size(UI *ui) goto end; } - // 3- read from terminfo if available + // 4 - read from terminfo if available height = unibi_get_num(data->ut, unibi_lines); width = unibi_get_num(data->ut, unibi_columns); end: if (width <= 0 || height <= 0) { - // use a default of 80x24 - width = 80; - height = 24; + // use the defaults + width = DFLT_COLS; + height = DFLT_ROWS; } ui->width = width; @@ -756,12 +779,10 @@ static void fix_terminfo(TUIData *data) unibi_set_if_empty(ut, unibi_from_status_line, "\x07"); } - if (STARTS_WITH(term, "xterm") || STARTS_WITH(term, "rxvt") || inside_tmux) { - 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"); - } + 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"); #define XTERM_SETAF \ "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m" @@ -784,18 +805,25 @@ static void fix_terminfo(TUIData *data) #define TMUX_WRAP(seq) (inside_tmux ? "\x1bPtmux;\x1b" seq "\x1b\\" : seq) // Support changing cursor shape on some popular terminals. const char *term_prog = os_getenv("TERM_PROGRAM"); + const char *vte_version = os_getenv("VTE_VERSION"); - if ((term_prog && !strcmp(term_prog, "iTerm.app")) - || os_getenv("ITERM_SESSION_ID") != NULL) { - // iterm + if ((term_prog && !strcmp(term_prog, "Konsole")) + || os_getenv("KONSOLE_DBUS_SESSION") != NULL) { + // Konsole uses a proprietary escape code to set the cursor shape + // and does not suppport DECSCUSR. data->unibi_ext.enter_insert_mode = (int)unibi_add_ext_str(ut, NULL, TMUX_WRAP("\x1b]50;CursorShape=1;BlinkingCursorEnabled=1\x07")); + data->unibi_ext.enter_replace_mode = (int)unibi_add_ext_str(ut, NULL, + TMUX_WRAP("\x1b]50;CursorShape=2;BlinkingCursorEnabled=1\x07")); data->unibi_ext.exit_insert_mode = (int)unibi_add_ext_str(ut, NULL, TMUX_WRAP("\x1b]50;CursorShape=0;BlinkingCursorEnabled=0\x07")); - } else { - // xterm-like sequences for blinking bar and solid block + } else if (!vte_version || atoi(vte_version) >= 3900) { + // Assume that the terminal supports DECSCUSR unless it is an + // old VTE based terminal data->unibi_ext.enter_insert_mode = (int)unibi_add_ext_str(ut, NULL, TMUX_WRAP("\x1b[5 q")); + data->unibi_ext.enter_replace_mode = (int)unibi_add_ext_str(ut, NULL, + TMUX_WRAP("\x1b[3 q")); data->unibi_ext.exit_insert_mode = (int)unibi_add_ext_str(ut, NULL, TMUX_WRAP("\x1b[2 q")); } diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 088055777a..ad875367c9 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -23,7 +23,7 @@ #include "nvim/normal.h" #include "nvim/option.h" #include "nvim/os_unix.h" -#include "nvim/os/event.h" +#include "nvim/event/loop.h" #include "nvim/os/time.h" #include "nvim/os/input.h" #include "nvim/os/signal.h" @@ -121,7 +121,7 @@ void ui_update_encoding(void) // May update the shape of the cursor. void ui_cursor_shape(void) { - ui_change_mode(); + ui_mode_change(); } void ui_refresh(void) @@ -214,9 +214,9 @@ void ui_detach(UI *ui) shift_index++; } - ui_count--; - // schedule a refresh - event_push((Event) { .handler = refresh }, false); + if (--ui_count) { + ui_refresh(); + } } void ui_clear(void) @@ -469,32 +469,19 @@ static void flush_cursor_update(void) // Notify that the current mode has changed. Can be used to change cursor // shape, for example. -static void ui_change_mode(void) +static void ui_mode_change(void) { - static int showing_insert_mode = MAYBE; - + int mode; if (!full_screen) { return; } - - if (State & INSERT) { - if (showing_insert_mode != TRUE) { - UI_CALL(insert_mode); - } - showing_insert_mode = TRUE; - } else { - if (showing_insert_mode != FALSE) { - UI_CALL(normal_mode); - } - showing_insert_mode = FALSE; - } + /* Get a simple UI mode out of State. */ + if ((State & REPLACE) == REPLACE) + mode = REPLACE; + else if (State & INSERT) + mode = INSERT; + else + mode = NORMAL; + UI_CALL(mode_change, mode); conceal_check_cursur_line(); } - -static void refresh(Event event) -{ - if (ui_count) { - ui_refresh(); - } -} - diff --git a/src/nvim/ui.h b/src/nvim/ui.h index 76ceec7775..9cfd99c096 100644 --- a/src/nvim/ui.h +++ b/src/nvim/ui.h @@ -24,8 +24,7 @@ struct ui_t { void (*busy_stop)(UI *ui); void (*mouse_on)(UI *ui); void (*mouse_off)(UI *ui); - void (*insert_mode)(UI *ui); - void (*normal_mode)(UI *ui); + void (*mode_change)(UI *ui, int mode); void (*set_scroll_region)(UI *ui, int top, int bot, int left, int right); void (*scroll)(UI *ui, int count); void (*highlight_set)(UI *ui, HlAttrs attrs); diff --git a/src/nvim/version.c b/src/nvim/version.c index 7a7492bc46..1f8aaf6e69 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -71,9 +71,61 @@ static char *features[] = { // clang-format off static int included_patches[] = { + //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 NA //737, - //736, + 736, //735, //734, //733, @@ -141,8 +193,8 @@ static int included_patches[] = { //671, //670, //669 NA - //668, - //667, + 668, + 667, //666 NA //665, //664 NA @@ -163,7 +215,7 @@ static int included_patches[] = { //649, //648 NA //647 NA - //646, + 646, //645, //644 NA //643, @@ -209,7 +261,7 @@ static int included_patches[] = { //603, //602, 601, - //600, + 600, 599, //598, 597, diff --git a/src/nvim/vim.h b/src/nvim/vim.h index 127e385619..17f9cbc310 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -46,7 +46,7 @@ Error: configure did not run properly.Check auto/config.log. # define VIMPACKAGE "vim" #endif -#include "nvim/os_unix_defs.h" /* bring lots of system header files */ +#include "nvim/os/os_defs.h" /* bring lots of system header files */ #define NUMBUFLEN 30 /* length of a buffer to store a number in ASCII */ @@ -332,20 +332,15 @@ enum { /* 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 +#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. */ -#if !defined(FEAT_GUI_W32) && !defined(FEAT_GUI_X11) \ - && !defined(FEAT_GUI_GTK) && !defined(FEAT_GUI_MAC) -# define mch_errmsg(str) fprintf(stderr, "%s", (str)) -# define display_errors() fflush(stderr) -# define mch_msg(str) printf("%s", (str)) -#else -# define USE_MCH_ERRMSG -#endif +#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 */ |