diff options
62 files changed, 982 insertions, 390 deletions
diff --git a/.editorconfig b/.editorconfig index c0b80fdf47..b117784d13 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,7 +5,7 @@ indent_style = space indent_size = 2 end_of_line = lf insert_final_newline = true -charset = utf_8 +charset = utf-8 [{Makefile,**/Makefile,runtime/doc/*.txt}] indent_style = tab diff --git a/CMakeLists.txt b/CMakeLists.txt index 02b555d540..fa020bf55c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,8 +96,8 @@ endif() # Set default build type. if(NOT CMAKE_BUILD_TYPE) - message(STATUS "CMAKE_BUILD_TYPE not given, defaulting to 'Debug'.") - set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build." FORCE) + message(STATUS "CMAKE_BUILD_TYPE not given, defaulting to 'Debug'") + set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build" FORCE) endif() # Set available build types for CMake GUIs. @@ -137,7 +137,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Default to -O2 on release builds. if(CMAKE_C_FLAGS_RELEASE MATCHES "-O3") - message(STATUS "Replacing -O3 in CMAKE_C_FLAGS_RELEASE with -O2.") + message(STATUS "Replacing -O3 in CMAKE_C_FLAGS_RELEASE with -O2") string(REPLACE "-O3" "-O2" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") endif() @@ -205,12 +205,12 @@ main(void) " HAS_ACCEPTABLE_FORTIFY) if(NOT HAS_ACCEPTABLE_FORTIFY) - message(STATUS "Unsupported _FORTIFY_SOURCE found, forcing _FORTIFY_SOURCE=1.") + message(STATUS "Unsupported _FORTIFY_SOURCE found, forcing _FORTIFY_SOURCE=1") # Extract possible prefix to _FORTIFY_SOURCE (e.g. -Wp,-D_FORTIFY_SOURCE). STRING(REGEX MATCH "[^\ ]+-D_FORTIFY_SOURCE" _FORTIFY_SOURCE_PREFIX "${CMAKE_C_FLAGS}") STRING(REPLACE "-D_FORTIFY_SOURCE" "" _FORTIFY_SOURCE_PREFIX "${_FORTIFY_SOURCE_PREFIX}" ) if(NOT _FORTIFY_SOURCE_PREFIX STREQUAL "") - message(STATUS "Detected _FORTIFY_SOURCE Prefix=${_FORTIFY_SOURCE_PREFIX}.") + message(STATUS "Detected _FORTIFY_SOURCE Prefix=${_FORTIFY_SOURCE_PREFIX}") endif() # -U in add_definitions doesn't end up in the correct spot, so we add it to # the flags variable instead. @@ -222,7 +222,7 @@ endif() if(CMAKE_EXE_LINKER_FLAGS MATCHES "--sort-common" OR CMAKE_SHARED_LINKER_FLAGS MATCHES "--sort-common" OR CMAKE_MODULE_LINKER_FLAGS MATCHES "--sort-common") - message(STATUS "Removing --sort-common from linker flags.") + message(STATUS "Removing --sort-common from linker flags") string(REGEX REPLACE ",--sort-common(=[^,]+)?" "" CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}") string(REGEX REPLACE ",--sort-common(=[^,]+)?" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}") string(REGEX REPLACE ",--sort-common(=[^,]+)?" "" CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS}") @@ -309,10 +309,10 @@ if(CMAKE_C_COMPILER_ID STREQUAL "GNU") endif() endif() -option(TRAVIS_CI_BUILD "Travis/QuickBuild CI. Extra flags will be set." OFF) +option(TRAVIS_CI_BUILD "Travis/QuickBuild CI, extra flags will be set" OFF) if(TRAVIS_CI_BUILD) - message(STATUS "Travis/QuickBuild CI build enabled.") + message(STATUS "Travis/QuickBuild CI build enabled") add_definitions(-Werror) if(DEFINED ENV{BUILD_32BIT}) # Get some test coverage for unsigned char @@ -433,11 +433,11 @@ option(CLANG_TSAN "Enable Clang thread sanitizer for nvim binary." OFF) if((CLANG_ASAN_UBSAN AND CLANG_MSAN) OR (CLANG_ASAN_UBSAN AND CLANG_TSAN) OR (CLANG_MSAN AND CLANG_TSAN)) - message(FATAL_ERROR "Sanitizers cannot be enabled simultaneously.") + message(FATAL_ERROR "Sanitizers cannot be enabled simultaneously") endif() if((CLANG_ASAN_UBSAN OR CLANG_MSAN OR CLANG_TSAN) AND NOT CMAKE_C_COMPILER_ID MATCHES "Clang") - message(FATAL_ERROR "Sanitizers are only supported for Clang.") + message(FATAL_ERROR "Sanitizers are only supported for Clang") endif() if(CMAKE_SYSTEM_NAME MATCHES "OpenBSD|FreeBSD|Windows") # see #5318 @@ -449,7 +449,7 @@ endif() if(ENABLE_JEMALLOC) if(CLANG_ASAN_UBSAN OR CLANG_MSAN OR CLANG_TSAN) - message(STATUS "Sanitizers have been enabled; don't use jemalloc.") + message(STATUS "Sanitizers enabled; disabling jemalloc") else() find_package(JeMalloc REQUIRED) include_directories(SYSTEM ${JEMALLOC_INCLUDE_DIRS}) @@ -505,10 +505,10 @@ else() endif() if(NOT LUA_PRG_WORKS) - message(FATAL_ERROR "A suitable Lua interpreter was not found.") + message(FATAL_ERROR "Failed to find a Lua 5.1-compatible interpreter") endif() -message(STATUS "Using the Lua interpreter ${LUA_PRG}.") +message(STATUS "Using Lua interpreter: ${LUA_PRG}") # Setup busted. find_program(BUSTED_PRG NAMES busted busted.bat) @@ -44,10 +44,11 @@ BUILD_CMD = $(BUILD_TOOL) $(VERBOSE_FLAG) # Extra CMake flags which extend the default set CMAKE_EXTRA_FLAGS ?= DEPS_CMAKE_FLAGS ?= -USE_BUNDLED_DEPS ?= +# Back-compat: USE_BUNDLED_DEPS was the old name. +USE_BUNDLED ?= $(USE_BUNDLED_DEPS) -ifneq (,$(USE_BUNDLED_DEPS)) - BUNDLED_CMAKE_FLAG := -DUSE_BUNDLED=$(USE_BUNDLED_DEPS) +ifneq (,$(USE_BUNDLED)) + BUNDLED_CMAKE_FLAG := -DUSE_BUNDLED=$(USE_BUNDLED) endif ifneq (,$(findstring functionaltest-lua,$(MAKECMDGOALS))) @@ -76,12 +77,12 @@ build/.ran-cmake: | deps touch $@ deps: | build/.ran-third-party-cmake -ifeq ($(call filter-true,$(USE_BUNDLED_DEPS)),) +ifeq ($(call filter-true,$(USE_BUNDLED)),) +$(BUILD_CMD) -C $(DEPS_BUILD_DIR) endif build/.ran-third-party-cmake: -ifeq ($(call filter-true,$(USE_BUNDLED_DEPS)),) +ifeq ($(call filter-true,$(USE_BUNDLED)),) mkdir -p $(DEPS_BUILD_DIR) cd $(DEPS_BUILD_DIR) && \ $(CMAKE_PRG) -G '$(BUILD_TYPE)' $(BUNDLED_CMAKE_FLAG) $(BUNDLED_LUA_CMAKE_FLAG) \ @@ -59,7 +59,7 @@ To list all targets: cmake --build build --target help -To skip "bundled" dependencies define `USE_BUNDLED_DEPS=NO` (CMake option: `USE_BUNDLED=NO`). +To skip "bundled" (`third-party/*`) dependencies define `USE_BUNDLED=NO`. See [the wiki](https://github.com/neovim/neovim/wiki/Building-Neovim) for details. diff --git a/cmake/GenerateHelptags.cmake.in b/cmake/GenerateHelptags.cmake.in index 3ced2c0b17..db6f15b1be 100644 --- a/cmake/GenerateHelptags.cmake.in +++ b/cmake/GenerateHelptags.cmake.in @@ -8,7 +8,7 @@ else() HELPTAGS_WORKING_DIRECTORY) endif() -message(STATUS "Generating helptags in ${HELPTAGS_WORKING_DIRECTORY}.") +message(STATUS "Generating helptags in ${HELPTAGS_WORKING_DIRECTORY}") if(EXISTS ${HELPTAGS_WORKING_DIRECTORY}/) message(STATUS "${HELPTAGS_WORKING_DIRECTORY} already exists") # If the doc directory already exists, helptags could fail due to duplicate diff --git a/cmake/LuaHelpers.cmake b/cmake/LuaHelpers.cmake index 3cba47412b..0239460f2b 100644 --- a/cmake/LuaHelpers.cmake +++ b/cmake/LuaHelpers.cmake @@ -17,7 +17,7 @@ endfunction() function(check_lua_deps LUA_PRG_PATH MODULES RESULT_VAR) # Check if the lua interpreter at the given path # satisfies all Neovim dependencies - message(STATUS "Checking Lua interpreter ${LUA_PRG_PATH}") + message(STATUS "Checking Lua interpreter: ${LUA_PRG_PATH}") if(NOT EXISTS ${LUA_PRG_PATH}) message(STATUS "[${LUA_PRG_PATH}] file not found") diff --git a/cmake/RunLuacheck.cmake b/cmake/RunLuacheck.cmake index 5129541cd8..4887e562a5 100644 --- a/cmake/RunLuacheck.cmake +++ b/cmake/RunLuacheck.cmake @@ -18,5 +18,5 @@ execute_process( if(NOT res EQUAL 0) message(STATUS "Output to stderr:\n${err}") - message(FATAL_ERROR "Linting tests failed with error: ${res}.") + message(FATAL_ERROR "Linting tests failed with error: ${res}") endif() diff --git a/cmake/RunTests.cmake b/cmake/RunTests.cmake index a51990e925..a764b19850 100644 --- a/cmake/RunTests.cmake +++ b/cmake/RunTests.cmake @@ -50,5 +50,5 @@ file(REMOVE_RECURSE ${WORKING_DIR}/Xtest-tmpdir) if(NOT res EQUAL 0) message(STATUS "Output to stderr:\n${err}") - message(FATAL_ERROR "Running ${TEST_TYPE} tests failed with error: ${res}.") + message(FATAL_ERROR "${TEST_TYPE} tests failed with error: ${res}") endif() diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index 8ca78f2782..21f2dfc58a 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -149,9 +149,9 @@ function! s:system(cmd, ...) abort endfunction function! s:get_page(path) abort - " Disable hard-wrap by setting $MANWIDTH to a high value. - " Use soft wrap instead (ftplugin/man.vim sets 'wrap', 'breakindent'). - let manwidth = 9999 + " Disable hard-wrap by using a big $MANWIDTH (max 1000 on some systems #9065). + " We use soft wrap: ftplugin/man.vim sets wrap/breakindent/…. + let manwidth = 999 " Force MANPAGER=cat to ensure Vim is not recursively invoked (by man-db). " http://comments.gmane.org/gmane.editors.vim.devel/29085 " Set MAN_KEEP_FORMATTING so Debian man doesn't discard backspaces. @@ -168,9 +168,10 @@ function! s:put_page(page) abort silent keepjumps 1delete _ endwhile " XXX: nroff justifies text by filling it with whitespace. That interacts - " badly with our use of $MANWIDTH=9999. Hack around this by using a fixed + " badly with our use of $MANWIDTH=999. Hack around this by using a fixed " size for those whitespace regions. - silent! keeppatterns keepjumps %s/\s\{999,}/\=repeat(' ', 10)/g + silent! keeppatterns keepjumps %s/\s\{199,}/\=repeat(' ', 10)/g + 1 lua require("man").highlight_man_page() setlocal filetype=man endfunction diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 064c08c190..2e1d89c524 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -9136,6 +9136,14 @@ This does NOT work: > variables are automatically deleted when the function ends. +:unl[et] ${env-name} ... *:unlet-environment* *:unlet-$* + Remove environment variable {env-name}. + Can mix {name} and ${env-name} in one :unlet command. + No error message is given for a non-existing + variable, also without !. + If the system does not support deleting an environment + variable, it is made emtpy. + :lockv[ar][!] [depth] {name} ... *:lockvar* *:lockv* Lock the internal variable {name}. Locking means that it can no longer be changed (until it is unlocked). diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index dd9f9ad0f3..34c4db4047 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -358,6 +358,8 @@ argument. instance a `nvim_get_api_info` call so that UI features can be safely detected by the UI before attaching. + See |ui-startup| for more information about UI startup. + To embed nvim without using the UI protocol, `--headless` should be supplied together with `--embed`. Then initialization is performed without waiting for an UI. This is also equivalent diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index 6a73061aca..c3664ece18 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -4665,8 +4665,8 @@ cterm={attr-list} *attr-list* *highlight-cterm* *E418* Note that "bold" can be used here and by using a bold font. They have the same effect. - If running in a terminal, "undercurl" acts as an alias for "underline". - It is set using |highlight-guisp|. + "undercurl" falls back to "underline" in a terminal that does not + support it. The color is set using |highlight-guisp|. start={term-list} *highlight-start* *E422* stop={term-list} *term-list* *highlight-stop* @@ -4799,7 +4799,8 @@ guifg={color-name} *highlight-guifg* guibg={color-name} *highlight-guibg* guisp={color-name} *highlight-guisp* These give the foreground (guifg), background (guibg) and special - (guisp) color to use in the GUI. "guisp" is used for undercurl. + (guisp) color to use in the GUI. "guisp" is used for undercurl + and underline. There are a few special names: NONE no color (transparent) bg use normal background color diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt index 42ce7a5edf..a0f1b0770e 100644 --- a/runtime/doc/ui.txt +++ b/runtime/doc/ui.txt @@ -16,10 +16,12 @@ RPC API. The UI model consists of a terminal-like grid with a single, monospace font size. Some elements (UI "widgets") can be drawn separately from the grid ("externalized"). + *ui-options* -After connecting to Nvim (usually a spawned, embedded instance) use the -|nvim_ui_attach()| API method to tell Nvim that your program wants to draw the -Nvim screen grid with a size of width × height cells. `options` must be +The |nvim_ui_attach()| API method is used to tell Nvim that your program wants to +draw the Nvim screen grid with a size of width × height cells. This is typically +done by an embedder, see |ui-startup| below for details, but an UI can also +connect to a running nvim instance and invoke this method. `options` must be a dictionary with these (optional) keys: `rgb` Decides the color format. *ui-rgb* Set true (default) for 24-bit RGB colors. @@ -29,7 +31,7 @@ a dictionary with these (optional) keys: `ext_tabline` Externalize the tabline. |ui-tabline| `ext_cmdline` Externalize the cmdline. |ui-cmdline| `ext_wildmenu` Externalize the wildmenu. |ui-wildmenu| - `ext_newgrid` Use new revision of the grid events. |ui-newgrid| + `ext_linegrid` Use new revision of the grid events. |ui-linegrid| `ext_hlstate` Use detailed highlight state. |ui-hlstate| Specifying a non-existent option is an error. UIs can check the |api-metadata| @@ -43,22 +45,26 @@ Nvim sends msgpack-rpc notifications to all attached UIs, with method name Each update event is itself an array whose first element is the event name and remaining elements are event-parameter tuples. This allows multiple events of the same kind to be sent in a row without the event name being repeated. This -batching is mostly used for "put", because each "put" event puts contents in -one screen cell, but clients must be prepared for multiple argument sets being -batched for all event kinds. - -Events must be handled in-order. The user should only see the updated screen -state after all events in the same "redraw" batch are processed (not any -intermediate state after processing only part of the array). +batching is mostly used for "grid_line", because each "grid_line" event puts +contents in one screen line, but clients must be prepared for multiple argument +sets being batched for all event kinds. + +Events must be handled in-order. A "flush" event is sent when nvim is done +redrawing the entire screen (so that all windows have a consistent view of +buffer state, options etc). Clients should be prepared that several "redraw" +batches are sent before the entire screen has been redrawn, and only the last +batch will end in "flush". The user should only see the final state when +"flush" is sent, and not any intermediate state after processing only part of +the batch array, nor after a batch not ending with "flush". By default, Nvim sends |ui-global| and |ui-grid-old| events; these suffice to implement a terminal-like interface. However there are two revisions of the -grid part of the protocol. The newer revision |ui-newgrid|, enabled by -`ext_newgrid` option, has some improvements, such as a more efficient -representation of highlighted text, simplified events and room for futher -enhancements that will use multiple grids. The older revision is available and -used by default only for backwards compatibility reasons. New UIs are strongly -recommended to use |ui-newgrid|, as further protocol extensions will require it. +grid part of the protocol. The newer revision |ui-linegrid|, enabled by +`ext_linegrid` option, has a more effecient representation of text (especially +highlighted text), and room for futher enhancements that will use +multiple grids. The older revision is available and used by default only for +backwards compatibility reasons. New UIs are strongly recommended to use +|ui-linegrid|, as further protocol extensions will require it. Nvim optionally sends screen elements "semantically" as structured events instead of raw grid-lines, controlled by |ui-ext-options|. The UI must present @@ -69,8 +75,44 @@ to existing update kinds. Clients must be prepared to ignore such extensions, for forward-compatibility. |api-contract| ============================================================================== +UI startup *ui-startup* + +Nvim defines a standard procedure for how an embedding UI should interact with +the startup phase of Nvim. When spawning the nvim process, use the |--embed| flag +but not the |--headless| flag. The started Nvim process will pause before loading +startup files and reading buffers, and give the UI a chance to invoke requests +to do early initialization. As soon as the UI invokes |nvim_ui_attach()|, the +startup will continue. + +A simple UI only need to do a single |nvim_ui_attach()| request and then +be prepared to handle any UI event. A more featureful UI, which might need +additional configuration of the nvim process, should use the following startup +procedure: + +1. Invoke |nvim_get_api_info()|, if this is needed to setup the client library + and/or to get the list of supported UI extensions. +2. At this time, any configuration that should be happen before init.vim + loading should be done. Buffers and windows are not available at this + point, but this could be used to set |g:| variables visible to init.vim +3. If the UI wants to do additional setup after the init.vim file was loaded + register an autocmd for VimEnter at this point: > + + nvim_command("autocmd VimEnter * call rpcrequest(1, 'vimenter')") + +<4. Now invoke |nvim_ui_attach()|. The UI will need to handle keyboard input + at this point, as sourcing init.vim and loading buffers might lead to + blocking prompts. +5. If step 3 was used, nvim will send a blocking "vimenter" request to the + UI. Inside this request handler, the UI can safely do any initialization + before entering normal mode, for instance reading variables set by + init.vim. + +============================================================================== Global Events *ui-global* +The following events will always be available, and describe global state of +the editor. + ["set_title", title] ["set_icon", icon] Set the window title, and icon (minimized) window title, respectively. @@ -144,15 +186,15 @@ Global Events *ui-global* would conflict with other usages of the mouse. It is safe for a client to ignore this and always send mouse events. -["busy_on"] -["busy_off"] +["busy_start"] +["busy_stop"] Nvim started or stopped being busy, and possibly not responsive to user input. This could be indicated to the user by hiding the cursor. ["suspend"] - |:suspend| command or |CTRL-Z| mapping is used. A terminal client (or other - client where it makes sense) could suspend itself. Other clients can - safely ignore it. + |:suspend| command or |CTRL-Z| mapping is used. A terminal client (or + another client where it makes sense) could suspend itself. Other + clients can safely ignore it. ["update_menu"] The menu mappings changed. @@ -161,16 +203,24 @@ Global Events *ui-global* ["visual_bell"] Notify the user with an audible or visual bell, respectively. +["flush"] + Nvim is done redrawing the screen. For an implementation that renders + to an internal buffer, this is the time to display the redrawn parts + to the user. + ============================================================================== -Grid Events (new revision) *ui-newgrid* +Grid Events (line-based) *ui-linegrid* -These events are used if `ext_newgrid` option is set (recommended for all new -UIs). +These events are used if `ext_linegrid` option is set (recommended for all new +UIs). The biggest change compared to previous revision is to use a single +event `grid_line` to update the contents of a screen line (where the old +protocol used a combination of cursor, highlight and text events) Most of these events take a `grid` index as first parameter. Grid 1 is the global grid used by default for the entire editor screen state. Grids other -than that will be defined by future extensions. Just activating the `ext_newgrid` -option by itself will never cause any additional grids to be created. +than that will be defined by future extensions. Just activating the +`ext_linegrid` option by itself will never cause any additional grids to be +created. Highlight attribute groups are predefined. UIs should maintain a table to map numerical highlight `id`:s to the actual attributes. @@ -299,10 +349,10 @@ numerical highlight `id`:s to the actual attributes. from `set_scroll_region` which was end-inclusive. ============================================================================== -Grid Events (first revision) *ui-grid-old* +Legacy Grid Events (cell based) *ui-grid-old* -This is an older representation of the screen grid, used if `ext_newgrid` -option is not set. +This is an older representation of the screen grid, used if `ext_linegrid` +option is not set. New UIs should use |ui-linegrid|. ["resize", width, height] The grid is resized to `width` and `height` cells. @@ -389,9 +439,8 @@ option is not set. ============================================================================== Detailed highlight state Extension *ui-hlstate* - Only sent if `ext_hlstate` option is set in |ui-options|. `ext_hlstate` implies -`ext_newgrid`. +`ext_linegrid`. By default, nvim will only describe grid cells using the final calculated higlight attributes, as described by the dict keys in |ui-event-highlight_set|. diff --git a/scripts/vim-patch.sh b/scripts/vim-patch.sh index bba76ffa97..de0d040b47 100755 --- a/scripts/vim-patch.sh +++ b/scripts/vim-patch.sh @@ -93,7 +93,7 @@ get_vim_sources() { exit 1 fi echo "Updating Vim sources: ${VIM_SOURCE_DIR}" - git pull && + git pull --ff && msg_ok "Updated Vim sources." || msg_err "Could not update Vim sources; ignoring error." fi @@ -156,8 +156,8 @@ preprocess_patch() { # Remove "Last change ..." changes in doc files. 2>/dev/null $nvim --cmd 'set dir=/tmp' +'%s/^@@.*\n.*For Vim version.*Last change.*\n.*For Vim version.*Last change.*//' +w +q "$file" - # Remove some testdir/Make_*.mak files - local na_src_testdir='Make_amiga.mak\|Make_dos.mak\|Make_ming.mak\|Make_vms.mms' + # Remove screen dumps, testdir/Make_*.mak files + local na_src_testdir='Make_amiga.mak\|Make_dos.mak\|Make_ming.mak\|Make_vms.mms\|dumps/.*.dump' 2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/src/testdir/\<\%('${na_src_testdir}'\)\>@norm! d/\v(^diff)|%$
' +w +q "$file" # Remove version.c #7555 diff --git a/src/clint.py b/src/clint.py index 9fd93ce143..34af5d15fd 100755 --- a/src/clint.py +++ b/src/clint.py @@ -49,6 +49,7 @@ from __future__ import unicode_literals import codecs import copy +import fileinput import getopt import math # for log import os @@ -65,7 +66,7 @@ _USAGE = """ Syntax: clint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] [--counting=total|toplevel|detailed] [--root=subdir] [--linelength=digits] [--record-errors=file] - [--suppress-errors=file] + [--suppress-errors=file] [--stdin-filename=filename] <file> [file] ... The style guidelines this tries to follow are those in @@ -167,6 +168,9 @@ Syntax: clint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] suppress-errors=file Errors listed in the given file will not be reported. + + stdin-filename=filename + Use specified filename when reading from stdin (file "-"). """ # We categorize each error message we print. Here are the categories. @@ -3456,10 +3460,12 @@ def ProcessFile(filename, vlevel, extra_check_functions=[]): # is processed. if filename == '-': - lines = codecs.StreamReaderWriter(sys.stdin, - codecs.getreader('utf8'), - codecs.getwriter('utf8'), - 'replace').read().split('\n') + stdin = sys.stdin.read() + if sys.version_info < (3, 0): + stdin = stdin.decode('utf8') + lines = stdin.split('\n') + if _cpplint_state.stdin_filename is not None: + filename = _cpplint_state.stdin_filename else: lines = codecs.open( filename, 'r', 'utf8', 'replace').read().split('\n') @@ -3540,7 +3546,9 @@ def ParseArguments(args): 'linelength=', 'extensions=', 'record-errors=', - 'suppress-errors=']) + 'suppress-errors=', + 'stdin-filename=', + ]) except getopt.GetoptError: PrintUsage('Invalid arguments.') @@ -3550,6 +3558,7 @@ def ParseArguments(args): counting_style = '' record_errors_file = None suppress_errors_file = None + stdin_filename = None for (opt, val) in opts: if opt == '--help': @@ -3586,6 +3595,8 @@ def ParseArguments(args): record_errors_file = val elif opt == '--suppress-errors': suppress_errors_file = val + elif opt == '--stdin-filename': + stdin_filename = val if not filenames: PrintUsage('No files were specified.') @@ -3596,6 +3607,7 @@ def ParseArguments(args): _SetCountingStyle(counting_style) _SuppressErrorsFrom(suppress_errors_file) _RecordErrorsTo(record_errors_file) + _cpplint_state.stdin_filename = stdin_filename return filenames diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index b49de7abf3..01f8c9f71c 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -127,7 +127,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, } if (ui->ui_ext[kUIHlState]) { - ui->ui_ext[kUINewgrid] = true; + ui->ui_ext[kUILinegrid] = true; } UIData *data = xmalloc(sizeof(UIData)); @@ -227,11 +227,11 @@ static void ui_set_option(UI *ui, bool init, String name, Object value, return; } bool boolval = value.data.boolean; - if (!init && i == kUINewgrid && boolval != ui->ui_ext[i]) { + if (!init && i == kUILinegrid && boolval != ui->ui_ext[i]) { // There shouldn't be a reason for an UI to do this ever // so explicitly don't support this. api_set_error(error, kErrorTypeValidation, - "ext_newgrid option cannot be changed"); + "ext_linegrid option cannot be changed"); } ui->ui_ext[i] = boolval; if (!init) { @@ -271,10 +271,10 @@ static void push_call(UI *ui, const char *name, Array args) static void remote_ui_grid_clear(UI *ui, Integer grid) { Array args = ARRAY_DICT_INIT; - if (ui->ui_ext[kUINewgrid]) { + if (ui->ui_ext[kUILinegrid]) { ADD(args, INTEGER_OBJ(grid)); } - const char *name = ui->ui_ext[kUINewgrid] ? "grid_clear" : "clear"; + const char *name = ui->ui_ext[kUILinegrid] ? "grid_clear" : "clear"; push_call(ui, name, args); } @@ -282,12 +282,12 @@ static void remote_ui_grid_resize(UI *ui, Integer grid, Integer width, Integer height) { Array args = ARRAY_DICT_INIT; - if (ui->ui_ext[kUINewgrid]) { + if (ui->ui_ext[kUILinegrid]) { ADD(args, INTEGER_OBJ(grid)); } ADD(args, INTEGER_OBJ(width)); ADD(args, INTEGER_OBJ(height)); - const char *name = ui->ui_ext[kUINewgrid] ? "grid_resize" : "resize"; + const char *name = ui->ui_ext[kUILinegrid] ? "grid_resize" : "resize"; push_call(ui, name, args); } @@ -295,7 +295,7 @@ static void remote_ui_grid_scroll(UI *ui, Integer grid, Integer top, Integer bot, Integer left, Integer right, Integer rows, Integer cols) { - if (ui->ui_ext[kUINewgrid]) { + if (ui->ui_ext[kUILinegrid]) { Array args = ARRAY_DICT_INIT; ADD(args, INTEGER_OBJ(grid)); ADD(args, INTEGER_OBJ(top)); @@ -341,7 +341,7 @@ static void remote_ui_default_colors_set(UI *ui, Integer rgb_fg, push_call(ui, "default_colors_set", args); // Deprecated - if (!ui->ui_ext[kUINewgrid]) { + if (!ui->ui_ext[kUILinegrid]) { args = (Array)ARRAY_DICT_INIT; ADD(args, INTEGER_OBJ(ui->rgb ? rgb_fg : cterm_fg - 1)); push_call(ui, "update_fg", args); @@ -359,7 +359,7 @@ static void remote_ui_default_colors_set(UI *ui, Integer rgb_fg, static void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs, Array info) { - if (!ui->ui_ext[kUINewgrid]) { + if (!ui->ui_ext[kUILinegrid]) { return; } Array args = ARRAY_DICT_INIT; @@ -397,7 +397,7 @@ static void remote_ui_highlight_set(UI *ui, int id) static void remote_ui_grid_cursor_goto(UI *ui, Integer grid, Integer row, Integer col) { - if (ui->ui_ext[kUINewgrid]) { + if (ui->ui_ext[kUILinegrid]) { Array args = ARRAY_DICT_INIT; ADD(args, INTEGER_OBJ(grid)); ADD(args, INTEGER_OBJ(row)); @@ -442,7 +442,7 @@ static void remote_ui_raw_line(UI *ui, Integer grid, Integer row, const sattr_T *attrs) { UIData *data = ui->data; - if (ui->ui_ext[kUINewgrid]) { + if (ui->ui_ext[kUILinegrid]) { Array args = ARRAY_DICT_INIT; ADD(args, INTEGER_OBJ(grid)); ADD(args, INTEGER_OBJ(row)); @@ -508,9 +508,10 @@ static void remote_ui_flush(UI *ui) { UIData *data = ui->data; if (data->buffer.size > 0) { - if (!ui->ui_ext[kUINewgrid]) { + if (!ui->ui_ext[kUILinegrid]) { remote_ui_cursor_goto(ui, data->cursor_row, data->cursor_col); } + push_call(ui, "flush", (Array)ARRAY_DICT_INIT); rpc_send_event(data->channel_id, "redraw", data->buffer); data->buffer = (Array)ARRAY_DICT_INIT; } @@ -549,7 +550,7 @@ static Array translate_firstarg(UI *ui, Array args) static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed) { - if (!ui->ui_ext[kUINewgrid]) { + if (!ui->ui_ext[kUILinegrid]) { // the representation of highlights in cmdline changed, translate back // never consumes args if (strequal(name, "cmdline_show")) { diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index 456ad0c8cc..9ddf788376 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -63,7 +63,7 @@ void set_scroll_region(Integer top, Integer bot, Integer left, Integer right) void scroll(Integer count) FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; -// Second revison of the grid protocol, used with ext_newgrid ui option +// Second revison of the grid protocol, used with ext_linegrid ui option void default_colors_set(Integer rgb_fg, Integer rgb_bg, Integer rgb_sp, Integer cterm_fg, Integer cterm_bg) FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL; diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index a28d9774ab..68f6ff303b 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -4533,9 +4533,10 @@ do_arg_all ( use_firstwin = true; } - for (i = 0; i < count && i < opened_len && !got_int; ++i) { - if (alist == &global_alist && i == global_alist.al_ga.ga_len - 1) - arg_had_last = TRUE; + for (i = 0; i < count && i < opened_len && !got_int; i++) { + if (alist == &global_alist && i == global_alist.al_ga.ga_len - 1) { + arg_had_last = true; + } if (opened[i] > 0) { /* Move the already present window to below the current window */ if (curwin->w_arg_idx != i) { diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 2c12534b21..9c678168bb 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2818,6 +2818,18 @@ static void ex_unletlock(exarg_T *eap, char_u *argstart, int deep) lval_T lv; do { + if (*arg == '$') { + const char *name = (char *)++arg; + + if (get_env_len((const char_u **)&arg) == 0) { + EMSG2(_(e_invarg2), name - 1); + return; + } + os_unsetenv(name); + arg = skipwhite(arg); + continue; + } + // Parse the name and find the end. char_u *const name_end = (char_u *)get_lval(arg, NULL, &lv, true, eap->skip || error, @@ -14664,6 +14676,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) static char *e_invact = N_("E927: Invalid action: '%s'"); const char *title = NULL; int action = ' '; + static int recursive = 0; rettv->vval.v_number = -1; dict_T *d = NULL; @@ -14671,6 +14684,9 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) if (list_arg->v_type != VAR_LIST) { EMSG(_(e_listreq)); return; + } else if (recursive != 0) { + EMSG(_(e_au_recursive)); + return; } typval_T *action_arg = &args[1]; @@ -14712,10 +14728,12 @@ skip_args: title = (wp ? "setloclist()" : "setqflist()"); } + recursive++; list_T *const l = list_arg->vval.v_list; if (set_errorlist(wp, l, action, (char_u *)title, d) == OK) { rettv->vval.v_number = 0; } + recursive--; } /* diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 328f46443f..e72bb7b870 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -222,7 +222,7 @@ return { pathshorten={args=1}, pow={args=2}, prevnonblank={args=1}, - printf={args=varargs(2)}, + printf={args=varargs(1)}, pumvisible={}, py3eval={args=1}, pyeval={args=1}, diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index aaa4dbdfb7..a091862e42 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -424,6 +424,7 @@ void ex_sort(exarg_T *eap) sort_abort = sort_ic = sort_rx = sort_nr = sort_flt = 0; size_t format_found = 0; + bool change_occurred = false; // Buffer contents changed. for (p = eap->arg; *p != NUL; ++p) { if (ascii_iswhite(*p)) { @@ -584,8 +585,16 @@ void ex_sort(exarg_T *eap) // Insert the lines in the sorted order below the last one. lnum = eap->line2; - for (i = 0; i < count; ++i) { - s = ml_get(nrs[eap->forceit ? count - i - 1 : i].lnum); + for (i = 0; i < count; i++) { + const linenr_T get_lnum = nrs[eap->forceit ? count - i - 1 : i].lnum; + + // If the original line number of the line being placed is not the same + // as "lnum" (accounting for offset), we know that the buffer changed. + if (get_lnum + ((linenr_T)count - 1) != lnum) { + change_occurred = true; + } + + s = ml_get(get_lnum); if (!unique || i == 0 || (sort_ic ? STRICMP(s, sortbuf1) : STRCMP(s, sortbuf1)) != 0) { // Copy the line into a buffer, it may become invalid in @@ -614,10 +623,13 @@ void ex_sort(exarg_T *eap) if (deleted > 0) { mark_adjust(eap->line2 - deleted, eap->line2, (long)MAXLNUM, -deleted, false); + msgmore(-deleted); } else if (deleted < 0) { mark_adjust(eap->line2, MAXLNUM, -deleted, 0L, false); } - changed_lines(eap->line1, 0, eap->line2 + 1, -deleted, true); + if (change_occurred || deleted != 0) { + changed_lines(eap->line1, 0, eap->line2 + 1, -deleted, true); + } curwin->w_cursor.lnum = eap->line1; beginline(BL_WHITE | BL_FIX); diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 6e695a8897..90fb7b8bc3 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -1285,7 +1285,6 @@ void dialog_changed(buf_T *buf, bool checkall) int ret; exarg_T ea; - assert(buf->b_fname != NULL); dialog_msg(buff, _("Save changes to \"%s\"?"), buf->b_fname); if (checkall) { ret = vim_dialog_yesnoallcancel(VIM_QUESTION, NULL, buff, 1); diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index ad51de46ee..2a733f5831 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -3260,8 +3260,15 @@ const char * set_one_cmd_context( while ((xp->xp_pattern = (char_u *)strchr(arg, ' ')) != NULL) { arg = (const char *)xp->xp_pattern + 1; } + xp->xp_context = EXPAND_USER_VARS; xp->xp_pattern = (char_u *)arg; + + if (*xp->xp_pattern == '$') { + xp->xp_context = EXPAND_ENV_VARS; + xp->xp_pattern++; + } + break; case CMD_function: @@ -6536,6 +6543,13 @@ void alist_expand(int *fnum_list, int fnum_len) void alist_set(alist_T *al, int count, char_u **files, int use_curbuf, int *fnum_list, int fnum_len) { int i; + static int recursive = 0; + + if (recursive) { + EMSG(_(e_au_recursive)); + return; + } + recursive++; alist_clear(al); ga_grow(&al->al_ga, count); @@ -6560,8 +6574,10 @@ void alist_set(alist_T *al, int count, char_u **files, int use_curbuf, int *fnum xfree(files); } - if (al == &global_alist) - arg_had_last = FALSE; + if (al == &global_alist) { + arg_had_last = false; + } + recursive--; } /* @@ -9040,8 +9056,10 @@ makeopens( // cursor can be set. This is done again below. // winminheight and winminwidth need to be set to avoid an error if the // user has set winheight or winwidth. - if (put_line(fd, "set winminheight=1 winminwidth=1 winheight=1 winwidth=1") - == FAIL) { + if (put_line(fd, "set winminheight=0") == FAIL + || put_line(fd, "set winheight=1") == FAIL + || put_line(fd, "set winminwidth=0") == FAIL + || put_line(fd, "set winwidth=1") == FAIL) { return FAIL; } if (nr > 1 && ses_winsizes(fd, restore_size, tab_firstwin) == FAIL) { diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 4983484100..1ec00b1e25 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -793,9 +793,11 @@ static int command_line_execute(VimState *state, int key) no_mapping--; // CTRL-\ e doesn't work when obtaining an expression, unless it // is in a mapping. - if (s->c != Ctrl_N && s->c != Ctrl_G && (s->c != 'e' - || (ccline.cmdfirstc == '=' - && KeyTyped))) { + if (s->c != Ctrl_N + && s->c != Ctrl_G + && (s->c != 'e' + || (ccline.cmdfirstc == '=' && KeyTyped) + || cmdline_star > 0)) { vungetc(s->c); s->c = Ctrl_BSL; } else if (s->c == 'e') { @@ -1350,7 +1352,8 @@ static int command_line_handle_key(CommandLineState *s) // a new one... new_cmdpos = -1; if (s->c == '=') { - if (ccline.cmdfirstc == '=') { // can't do this recursively + if (ccline.cmdfirstc == '=' // can't do this recursively + || cmdline_star > 0) { // or when typing a password beep_flush(); s->c = ESC; } else { @@ -5587,6 +5590,9 @@ static struct cmdline_info *get_ccline_ptr(void) */ char_u *get_cmdline_str(void) { + if (cmdline_star > 0) { + return NULL; + } struct cmdline_info *p = get_ccline_ptr(); if (p == NULL) diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 9bba2379cd..2dc8073b1e 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -528,8 +528,8 @@ EXTERN buf_T *curbuf INIT(= NULL); // currently active buffer */ EXTERN alist_T global_alist; /* global argument list */ EXTERN int max_alist_id INIT(= 0); ///< the previous argument list id -EXTERN int arg_had_last INIT(= FALSE); /* accessed last file in - global_alist */ +EXTERN bool arg_had_last INIT(= false); // accessed last file in + // global_alist EXTERN int ru_col; /* column for ruler */ EXTERN int ru_wid; /* 'rulerfmt' width of ruler when non-zero */ @@ -1099,6 +1099,8 @@ EXTERN char_u e_notset[] INIT(= N_("E764: Option '%s' is not set")); EXTERN char_u e_invalidreg[] INIT(= N_("E850: Invalid register name")); EXTERN char_u e_dirnotf[] INIT(= N_( "E919: Directory not found in '%s': \"%s\"")); +EXTERN char_u e_au_recursive[] INIT(= N_( + "E952: Autocommand caused recursive behavior")); EXTERN char_u e_unsupportedoption[] INIT(= N_("E519: Option not supported")); EXTERN char_u e_fnametoolong[] INIT(= N_("E856: Filename too long")); EXTERN char_u e_float_as_string[] INIT(= N_("E806: using Float as a String")); diff --git a/src/nvim/log.c b/src/nvim/log.c index 503c4b122c..4d912c452b 100644 --- a/src/nvim/log.c +++ b/src/nvim/log.c @@ -109,6 +109,12 @@ bool logmsg(int log_level, const char *context, const char *func_name, return false; } +#ifdef EXITFREE + // Logging after we've already started freeing all our memory will only cause + // pain. We need access to VV_PROGPATH, homedir, etc. + assert(!entered_free_all_mem); +#endif + log_lock(); bool ret = false; FILE *log_file = open_log_file(); diff --git a/src/nvim/main.c b/src/nvim/main.c index 41e8c42803..af54e62393 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -258,6 +258,14 @@ int main(int argc, char **argv) // Process the command line arguments. File names are put in the global // argument list "global_alist". command_line_scan(¶ms); + + if (embedded_mode) { + const char *err; + if (!channel_from_stdio(true, CALLBACK_READER_INIT, &err)) { + abort(); + } + } + server_init(params.listen_addr); if (GARGCOUNT > 0) { @@ -848,10 +856,6 @@ static void command_line_scan(mparm_T *parmp) headless_mode = true; } else if (STRICMP(argv[0] + argv_idx, "embed") == 0) { embedded_mode = true; - const char *err; - if (!channel_from_stdio(true, CALLBACK_READER_INIT, &err)) { - abort(); - } } else if (STRNICMP(argv[0] + argv_idx, "listen", 6) == 0) { want_argument = true; argv_idx += 6; @@ -1624,9 +1628,10 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd) win_close(curwin, true); advance = false; } - if (arg_idx == GARGCOUNT - 1) - arg_had_last = TRUE; - ++arg_idx; + if (arg_idx == GARGCOUNT - 1) { + arg_had_last = true; + } + arg_idx++; } os_breakcheck(); if (got_int) { diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 5602a29f50..ec0238e7c9 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -535,6 +535,7 @@ void ml_open_file(buf_T *buf) void check_need_swap(int newfile) { int old_msg_silent = msg_silent; // might be reset by an E325 message + msg_silent = 0; // If swap dialog prompts for input, user needs to see it! if (curbuf->b_may_swap && (!curbuf->b_p_ro || !newfile)) { ml_open_file(curbuf); diff --git a/src/nvim/move.c b/src/nvim/move.c index 4a2874abeb..442e5d6dff 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -98,6 +98,11 @@ static void comp_botline(win_T *wp) static linenr_T last_cursorline = 0; +void reset_cursorline(void) +{ + last_cursorline = 0; +} + // Redraw when w_cline_row changes and 'relativenumber' or 'cursorline' is set. static void redraw_for_cursorline(win_T *wp) { diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 45620bfc54..f87de52a82 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1269,7 +1269,8 @@ static void normal_redraw(NormalState *s) xfree(p); } - if (need_fileinfo) { // show file info after redraw + // show fileinfo after redraw + if (need_fileinfo && !shortmess(SHM_FILEINFO)) { fileinfo(false, true, false); need_fileinfo = false; } diff --git a/src/nvim/option.c b/src/nvim/option.c index 7cda42ef20..eb2780ce7a 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2434,7 +2434,7 @@ did_set_string_option ( int did_chartab = FALSE; char_u **gvarp; bool free_oldval = (options[opt_idx].flags & P_ALLOCED); - int ft_changed = false; + bool value_changed = false; /* Get the global option to compare with, otherwise we would have to check * two values for all local options. */ @@ -3155,11 +3155,13 @@ did_set_string_option ( if (!valid_filetype(*varp)) { errmsg = e_invarg; } else { - ft_changed = STRCMP(oldval, *varp) != 0; + value_changed = STRCMP(oldval, *varp) != 0; } } else if (gvarp == &p_syn) { if (!valid_filetype(*varp)) { errmsg = e_invarg; + } else { + value_changed = STRCMP(oldval, *varp) != 0; } } else if (varp == &curwin->w_p_winhl) { if (!parse_winhl_opt(curwin)) { @@ -3235,14 +3237,28 @@ did_set_string_option ( */ /* When 'syntax' is set, load the syntax of that name */ if (varp == &(curbuf->b_p_syn)) { - apply_autocmds(EVENT_SYNTAX, curbuf->b_p_syn, - curbuf->b_fname, TRUE, curbuf); + static int syn_recursive = 0; + + syn_recursive++; + // Only pass true for "force" when the value changed or not used + // recursively, to avoid endless recurrence. + apply_autocmds(EVENT_SYNTAX, curbuf->b_p_syn, curbuf->b_fname, + value_changed || syn_recursive == 1, curbuf); + syn_recursive--; } else if (varp == &(curbuf->b_p_ft)) { // 'filetype' is set, trigger the FileType autocommand - if (!(opt_flags & OPT_MODELINE) || ft_changed) { + // Skip this when called from a modeline and the filetype was + // already set to this value. + if (!(opt_flags & OPT_MODELINE) || value_changed) { + static int ft_recursive = 0; + + ft_recursive++; did_filetype = true; - apply_autocmds(EVENT_FILETYPE, curbuf->b_p_ft, - curbuf->b_fname, true, curbuf); + // Only pass true for "force" when the value changed or not + // used recursively, to avoid endless recurrence. + apply_autocmds(EVENT_FILETYPE, curbuf->b_p_ft, curbuf->b_fname, + value_changed || ft_recursive == 1, curbuf); + ft_recursive--; // Just in case the old "curbuf" is now invalid if (varp != &(curbuf->b_p_ft)) { varp = NULL; @@ -3705,6 +3721,9 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, } else if ((int *)varp == &p_lnr) { // 'langnoremap' -> !'langremap' p_lrm = !p_lnr; + } else if ((int *)varp == &curwin->w_p_cul && !value && old_value) { + // 'cursorline' + reset_cursorline(); // 'undofile' } else if ((int *)varp == &curbuf->b_p_udf || (int *)varp == &p_udf) { // Only take action when the option was set. When reset we do not diff --git a/src/nvim/os_unix.c b/src/nvim/os_unix.c index 27660712da..09ba718302 100644 --- a/src/nvim/os_unix.c +++ b/src/nvim/os_unix.c @@ -149,11 +149,12 @@ void mch_exit(int r) stream_set_blocking(input_global_fd(), true); // normalize stream (#2598) } + ILOG("Nvim exit: %d", r); + #ifdef EXITFREE free_all_mem(); #endif - ILOG("Nvim exit: %d", r); exit(r); } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index fe6a15c5fc..ec4b31c40d 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -4588,14 +4588,18 @@ void status_redraw_all(void) } } -/* - * mark all status lines of the current buffer for redraw - */ +/// Marks all status lines of the current buffer for redraw. void status_redraw_curbuf(void) { + status_redraw_buf(curbuf); +} + +/// Marks all status lines of the specified buffer for redraw. +void status_redraw_buf(buf_T *buf) +{ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp->w_status_height != 0 && wp->w_buffer == curbuf) { - wp->w_redr_status = TRUE; + if (wp->w_status_height != 0 && wp->w_buffer == buf) { + wp->w_redr_status = true; redraw_later(VALID); } } diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 4c054dc8e0..e0e1897b88 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -6884,7 +6884,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) // "fg", which have been changed now. highlight_attr_set_all(); - if (!ui_is_external(kUINewgrid) && starting == 0) { + if (!ui_is_external(kUILinegrid) && starting == 0) { // Older UIs assume that we clear the screen after normal group is // changed ui_refresh(); diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index d83986cb15..d831979022 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -668,6 +668,7 @@ static void buf_set_term_title(buf_T *buf, char *title) false, &err); api_clear_error(&err); + status_redraw_buf(buf); } static int term_settermprop(VTermProp prop, VTermValue *val, void *data) diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index f10163e351..36dcdc3386 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -38,6 +38,7 @@ source test_sort.vim source test_source_utf8.vim source test_sha256.vim source test_statusline.vim +source test_suspend.vim source test_syn_attr.vim source test_tabline.vim " source test_tabpage.vim diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index ad967c528c..aaf32dff04 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -166,6 +166,9 @@ function Test_printf_spec_s() endfunc function Test_printf_misc() + call assert_equal('123', printf('123')) + call assert_fails("call printf('123', 3)", "E767:") + call assert_equal('123', printf('%d', 123)) call assert_equal('123', printf('%i', 123)) call assert_equal('123', printf('%D', 123)) diff --git a/src/nvim/testdir/test_getcwd.vim b/src/nvim/testdir/test_getcwd.vim index 15eab2abbb..194963f694 100644 --- a/src/nvim/testdir/test_getcwd.vim +++ b/src/nvim/testdir/test_getcwd.vim @@ -89,3 +89,15 @@ function Test_GetCwd() call assert_equal("y Xdir2 1", GetCwdInfo(2, tp_nr)) call assert_equal("z Xdir3 1", GetCwdInfo(1, tp_nr)) endfunc + +function Test_GetCwd_lcd_shellslash() + new + let root = fnamemodify('/', ':p') + exe 'lcd '.root + let cwd = getcwd() + if !exists('+shellslash') || &shellslash + call assert_equal(cwd[-1:], '/') + else + call assert_equal(cwd[-1:], '\') + endif +endfunc diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim index 023332c90a..999d4dbd4a 100644 --- a/src/nvim/testdir/test_listdict.vim +++ b/src/nvim/testdir/test_listdict.vim @@ -106,6 +106,43 @@ func Test_list_range_assign() call assert_equal([1, 2], l) endfunc +" Test removing items in list +func Test_list_func_remove() + " Test removing 1 element + let l = [1, 2, 3, 4] + call assert_equal(1, remove(l, 0)) + call assert_equal([2, 3, 4], l) + + let l = [1, 2, 3, 4] + call assert_equal(2, remove(l, 1)) + call assert_equal([1, 3, 4], l) + + let l = [1, 2, 3, 4] + call assert_equal(4, remove(l, -1)) + call assert_equal([1, 2, 3], l) + + " Test removing range of element(s) + let l = [1, 2, 3, 4] + call assert_equal([3], remove(l, 2, 2)) + call assert_equal([1, 2, 4], l) + + let l = [1, 2, 3, 4] + call assert_equal([2, 3], remove(l, 1, 2)) + call assert_equal([1, 4], l) + + let l = [1, 2, 3, 4] + call assert_equal([2, 3], remove(l, -3, -2)) + call assert_equal([1, 4], l) + + " Test invalid cases + let l = [1, 2, 3, 4] + call assert_fails("call remove(l, 5)", 'E684:') + call assert_fails("call remove(l, 1, 5)", 'E684:') + call assert_fails("call remove(l, 3, 2)", 'E16:') + call assert_fails("call remove(1, 0)", 'E712:') + call assert_fails("call remove(l, l)", 'E745:') +endfunc + " Tests for Dictionary type func Test_dict() @@ -222,6 +259,17 @@ func Test_script_local_dict_func() unlet g:dict endfunc +" Test removing items in la dictionary +func Test_dict_func_remove() + let d = {1:'a', 2:'b', 3:'c'} + call assert_equal('b', remove(d, 2)) + call assert_equal({1:'a', 3:'c'}, d) + + call assert_fails("call remove(d, 1, 2)", 'E118:') + call assert_fails("call remove(d, 'a')", 'E716:') + call assert_fails("call remove(d, [])", 'E730:') +endfunc + " Nasty: remove func from Dict that's being called (works) func Test_dict_func_remove_in_use() let d = {1:1} diff --git a/src/nvim/testdir/test_sort.vim b/src/nvim/testdir/test_sort.vim index 4fddb47b58..14d008a17f 100644 --- a/src/nvim/testdir/test_sort.vim +++ b/src/nvim/testdir/test_sort.vim @@ -1,13 +1,13 @@ -" Test sort() +" Tests for the "sort()" function and for the ":sort" command. -:func Compare1(a, b) abort +func Compare1(a, b) abort call sort(range(3), 'Compare2') return a:a - a:b -:endfunc +endfunc -:func Compare2(a, b) abort +func Compare2(a, b) abort return a:a - a:b -:endfunc +endfunc func Test_sort_strings() " numbers compared as strings @@ -45,7 +45,7 @@ func Test_sort_default() call assert_fails('call sort([3.3, 1, "2"], 3)', "E474") endfunc -" Tests for the :sort command +" Tests for the ":sort" command. func Test_sort_cmd() let tests = [ \ { @@ -1167,18 +1167,87 @@ func Test_sort_cmd() \ '1.234', \ '123.456' \ ] - \ } + \ }, + \ { + \ 'name' : 'alphabetical, sorted input', + \ 'cmd' : 'sort', + \ 'input' : [ + \ 'a', + \ 'b', + \ 'c', + \ ], + \ 'expected' : [ + \ 'a', + \ 'b', + \ 'c', + \ ] + \ }, + \ { + \ 'name' : 'alphabetical, sorted input, unique at end', + \ 'cmd' : 'sort u', + \ 'input' : [ + \ 'aa', + \ 'bb', + \ 'cc', + \ 'cc', + \ ], + \ 'expected' : [ + \ 'aa', + \ 'bb', + \ 'cc', + \ ] + \ }, \ ] for t in tests enew! call append(0, t.input) $delete _ - exe t.cmd + setlocal nomodified + execute t.cmd + call assert_equal(t.expected, getline(1, '$'), t.name) + + " Previously, the ":sort" command would set 'modified' even if the buffer + " contents did not change. Here, we check that this problem is fixed. + if t.input == t.expected + call assert_false(&modified, t.name . ': &mod is not correct') + else + call assert_true(&modified, t.name . ': &mod is not correct') + endif endfor call assert_fails('sort no', 'E474') enew! endfunc + +func Test_sort_cmd_report() + enew! + call append(0, repeat([1], 3) + repeat([2], 3) + repeat([3], 3)) + $delete _ + setlocal nomodified + let res = execute('%sort u') + + call assert_equal([1,2,3], map(getline(1, '$'), 'v:val+0')) + call assert_match("6 fewer lines", res) + enew! + call append(0, repeat([1], 3) + repeat([2], 3) + repeat([3], 3)) + $delete _ + setlocal nomodified report=10 + let res = execute('%sort u') + + call assert_equal([1,2,3], map(getline(1, '$'), 'v:val+0')) + call assert_equal("", res) + enew! + call append(0, repeat([1], 3) + repeat([2], 3) + repeat([3], 3)) + $delete _ + setl report&vim + setlocal nomodified + let res = execute('1g/^/%sort u') + + call assert_equal([1,2,3], map(getline(1, '$'), 'v:val+0')) + " the output comes from the :g command, not from the :sort + call assert_match("6 fewer lines", res) + enew! + endfunc diff --git a/src/nvim/testdir/test_suspend.vim b/src/nvim/testdir/test_suspend.vim new file mode 100644 index 0000000000..462173e8cc --- /dev/null +++ b/src/nvim/testdir/test_suspend.vim @@ -0,0 +1,51 @@ +" Test :suspend + +source shared.vim + +func Test_suspend() + if !has('terminal') || !executable('/bin/sh') + return + endif + + let buf = term_start('/bin/sh') + " Wait for shell prompt. + call WaitForAssert({-> assert_match('$ $', term_getline(buf, '.'))}) + + call term_sendkeys(buf, v:progpath + \ . " --clean -X" + \ . " -c 'set nu'" + \ . " -c 'call setline(1, \"foo\")'" + \ . " Xfoo\<CR>") + " Cursor in terminal buffer should be on first line in spawned vim. + call WaitForAssert({-> assert_equal(' 1 foo', term_getline(buf, '.'))}) + + for suspend_cmd in [":suspend\<CR>", + \ ":stop\<CR>", + \ ":suspend!\<CR>", + \ ":stop!\<CR>", + \ "\<C-Z>"] + " Suspend and wait for shell prompt. + call term_sendkeys(buf, suspend_cmd) + call WaitForAssert({-> assert_match('$ $', term_getline(buf, '.'))}) + + " Without 'autowrite', buffer should not be written. + call assert_equal(0, filereadable('Xfoo')) + + call term_sendkeys(buf, "fg\<CR>") + call WaitForAssert({-> assert_equal(' 1 foo', term_getline(buf, '.'))}) + endfor + + " Test that :suspend! with 'autowrite' writes content of buffers if modified. + call term_sendkeys(buf, ":set autowrite\<CR>") + call assert_equal(0, filereadable('Xfoo')) + call term_sendkeys(buf, ":suspend\<CR>") + " Wait for shell prompt. + call WaitForAssert({-> assert_match('$ $', term_getline(buf, '.'))}) + call assert_equal(['foo'], readfile('Xfoo')) + call term_sendkeys(buf, "fg\<CR>") + call WaitForAssert({-> assert_equal(' 1 foo', term_getline(buf, '.'))}) + + exe buf . 'bwipe!' + call delete('Xfoo') + set autowrite& +endfunc diff --git a/src/nvim/testdir/test_unlet.vim b/src/nvim/testdir/test_unlet.vim index 3f06058d03..b02bdaab3b 100644 --- a/src/nvim/testdir/test_unlet.vim +++ b/src/nvim/testdir/test_unlet.vim @@ -28,3 +28,37 @@ endfunc func Test_unlet_fails() call assert_fails('unlet v:["count"]', 'E46:') endfunc + +func Test_unlet_env() + let envcmd = has('win32') ? 'set' : 'env' + + let $FOOBAR = 'test' + let found = 0 + for kv in split(system(envcmd), "\r*\n") + if kv == 'FOOBAR=test' + let found = 1 + endif + endfor + call assert_equal(1, found) + + unlet $FOOBAR + let found = 0 + for kv in split(system(envcmd), "\r*\n") + if kv == 'FOOBAR=test' + let found = 1 + endif + endfor + call assert_equal(0, found) + + unlet $MUST_NOT_BE_AN_ERROR +endfunc + +func Test_unlet_complete() + let g:FOOBAR = 1 + call feedkeys(":unlet g:FOO\t\n", 'tx') + call assert_true(!exists('g:FOOBAR')) + + let $FOOBAR = 1 + call feedkeys(":unlet $FOO\t\n", 'tx') + call assert_true(!exists('$FOOBAR') || empty($FOOBAR)) +endfunc diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 713fe6a2e5..3ed0fe0cd6 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -118,6 +118,7 @@ typedef struct { int resize_screen; int reset_scroll_region; int set_cursor_style, reset_cursor_style; + int enter_undercurl_mode, exit_undercurl_mode, set_underline_color; } unibi_ext; } TUIData; @@ -156,7 +157,7 @@ UI *tui_start(void) ui->raw_line = tui_raw_line; memset(ui->ui_ext, 0, sizeof(ui->ui_ext)); - ui->ui_ext[kUINewgrid] = true; + ui->ui_ext[kUILinegrid] = true; return ui_bridge_attach(ui, tui_main, tui_scheduler); } @@ -438,15 +439,16 @@ static void sigwinch_cb(SignalWatcher *watcher, int signum, void *data) static bool attrs_differ(HlAttrs a1, HlAttrs a2, bool rgb) { if (rgb) { - // TODO(bfredl): when we start to support special color, - // rgb_sp_color must be added here return a1.rgb_fg_color != a2.rgb_fg_color || a1.rgb_bg_color != a2.rgb_bg_color - || a1.rgb_ae_attr != a2.rgb_ae_attr; + || a1.rgb_ae_attr != a2.rgb_ae_attr + || a1.rgb_sp_color != a2.rgb_sp_color; } else { return a1.cterm_fg_color != a2.cterm_fg_color || a1.cterm_bg_color != a2.cterm_bg_color - || a1.cterm_ae_attr != a2.cterm_ae_attr; + || a1.cterm_ae_attr != a2.cterm_ae_attr + || (a1.cterm_ae_attr & (HL_UNDERLINE|HL_UNDERCURL) + && a1.rgb_sp_color != a2.rgb_sp_color); } } @@ -483,12 +485,21 @@ static void update_attrs(UI *ui, HlAttrs attrs) bool italic = attr & HL_ITALIC; bool reverse = attr & HL_INVERSE; bool standout = attr & HL_STANDOUT; - bool underline = attr & (HL_UNDERLINE), undercurl = attr & (HL_UNDERCURL); + + bool underline; + bool undercurl; + if (data->unibi_ext.enter_undercurl_mode) { + underline = attr & HL_UNDERLINE; + undercurl = attr & HL_UNDERCURL; + } else { + underline = (attr & HL_UNDERLINE) || (attr & HL_UNDERCURL); + undercurl = false; + } if (unibi_get_str(data->ut, unibi_set_attributes)) { - if (bold || reverse || underline || undercurl || standout) { + if (bold || reverse || underline || standout) { UNIBI_SET_NUM_VAR(data->params[0], standout); - UNIBI_SET_NUM_VAR(data->params[1], underline || undercurl); + UNIBI_SET_NUM_VAR(data->params[1], underline); UNIBI_SET_NUM_VAR(data->params[2], reverse); UNIBI_SET_NUM_VAR(data->params[3], 0); // blink UNIBI_SET_NUM_VAR(data->params[4], 0); // dim @@ -507,7 +518,7 @@ static void update_attrs(UI *ui, HlAttrs attrs) if (bold) { unibi_out(ui, unibi_enter_bold_mode); } - if (underline || undercurl) { + if (underline) { unibi_out(ui, unibi_enter_underline_mode); } if (standout) { @@ -520,6 +531,18 @@ static void update_attrs(UI *ui, HlAttrs attrs) if (italic) { unibi_out(ui, unibi_enter_italics_mode); } + if (undercurl && data->unibi_ext.enter_undercurl_mode) { + unibi_out_ext(ui, data->unibi_ext.enter_undercurl_mode); + } + if ((undercurl || underline) && data->unibi_ext.set_underline_color) { + int color = attrs.rgb_sp_color; + if (color != -1) { + UNIBI_SET_NUM_VAR(data->params[0], (color >> 16) & 0xff); // red + UNIBI_SET_NUM_VAR(data->params[1], (color >> 8) & 0xff); // green + UNIBI_SET_NUM_VAR(data->params[2], color & 0xff); // blue + unibi_out_ext(ui, data->unibi_ext.set_underline_color); + } + } if (ui->rgb) { if (fg != -1) { UNIBI_SET_NUM_VAR(data->params[0], (fg >> 16) & 0xff); // red @@ -573,7 +596,7 @@ static void print_cell(UI *ui, UCell *ptr) // Printing the next character finally advances the cursor. final_column_wrap(ui); } - update_attrs(ui, ptr->attrs); + update_attrs(ui, kv_A(data->attrs, ptr->attr)); out(ui, ptr->data, strlen(ptr->data)); grid->col++; if (data->immediate_wrap_after_last_column) { @@ -589,7 +612,8 @@ static bool cheap_to_print(UI *ui, int row, int col, int next) UCell *cell = grid->cells[row] + col; while (next) { next--; - if (attrs_differ(cell->attrs, data->print_attrs, ui->rgb)) { + if (attrs_differ(kv_A(data->attrs, cell->attr), + data->print_attrs, ui->rgb)) { if (data->default_attr) { return false; } @@ -763,43 +787,31 @@ static void clear_region(UI *ui, int top, int bot, int left, int right, cursor_goto(ui, data->row, data->col); } -static bool can_use_scroll(UI * ui) +static void set_scroll_region(UI *ui, int top, int bot, int left, int right) { TUIData *data = ui->data; UGrid *grid = &data->grid; - return data->scroll_region_is_full_screen - || (data->can_change_scroll_region - && ((grid->left == 0 && grid->right == ui->width - 1) - || data->can_set_lr_margin - || data->can_set_left_right_margin)); -} - -static void set_scroll_region(UI *ui) -{ - TUIData *data = ui->data; - UGrid *grid = &data->grid; - - UNIBI_SET_NUM_VAR(data->params[0], grid->top); - UNIBI_SET_NUM_VAR(data->params[1], grid->bot); + UNIBI_SET_NUM_VAR(data->params[0], top); + UNIBI_SET_NUM_VAR(data->params[1], bot); unibi_out(ui, unibi_change_scroll_region); - if (grid->left != 0 || grid->right != ui->width - 1) { + if (left != 0 || right != ui->width - 1) { unibi_out_ext(ui, data->unibi_ext.enable_lr_margin); if (data->can_set_lr_margin) { - UNIBI_SET_NUM_VAR(data->params[0], grid->left); - UNIBI_SET_NUM_VAR(data->params[1], grid->right); + UNIBI_SET_NUM_VAR(data->params[0], left); + UNIBI_SET_NUM_VAR(data->params[1], right); unibi_out(ui, unibi_set_lr_margin); } else { - UNIBI_SET_NUM_VAR(data->params[0], grid->left); + UNIBI_SET_NUM_VAR(data->params[0], left); unibi_out(ui, unibi_set_left_margin_parm); - UNIBI_SET_NUM_VAR(data->params[0], grid->right); + UNIBI_SET_NUM_VAR(data->params[0], right); unibi_out(ui, unibi_set_right_margin_parm); } } unibi_goto(ui, grid->row, grid->col); } -static void reset_scroll_region(UI *ui) +static void reset_scroll_region(UI *ui, bool fullwidth) { TUIData *data = ui->data; UGrid *grid = &data->grid; @@ -811,7 +823,7 @@ static void reset_scroll_region(UI *ui) UNIBI_SET_NUM_VAR(data->params[1], ui->height - 1); unibi_out(ui, unibi_change_scroll_region); } - if (grid->left != 0 || grid->right != ui->width - 1) { + if (!fullwidth) { if (data->can_set_lr_margin) { UNIBI_SET_NUM_VAR(data->params[0], 0); UNIBI_SET_NUM_VAR(data->params[1], ui->width - 1); @@ -848,7 +860,7 @@ static void tui_grid_resize(UI *ui, Integer g, Integer width, Integer height) unibi_out_ext(ui, data->unibi_ext.resize_screen); // DECSLPP does not reset the scroll region. if (data->scroll_region_is_full_screen) { - reset_scroll_region(ui); + reset_scroll_region(ui, ui->width == grid->width); } } else { // Already handled the SIGWINCH signal; avoid double-resize. got_winch = false; @@ -1006,28 +1018,35 @@ static void tui_mode_change(UI *ui, String mode, Integer mode_idx) data->showing_mode = (ModeShape)mode_idx; } -static void tui_grid_scroll(UI *ui, Integer g, Integer top, Integer bot, - Integer left, Integer right, +static void tui_grid_scroll(UI *ui, Integer g, Integer startrow, Integer endrow, + Integer startcol, Integer endcol, Integer rows, Integer cols) { TUIData *data = ui->data; UGrid *grid = &data->grid; - ugrid_set_scroll_region(&data->grid, (int)top, (int)bot-1, - (int)left, (int)right-1); + int top = (int)startrow, bot = (int)endrow-1; + int left = (int)startcol, right = (int)endcol-1; - data->scroll_region_is_full_screen = - left == 0 && right == ui->width - && top == 0 && bot == ui->height; + bool fullwidth = left == 0 && right == ui->width-1; + data->scroll_region_is_full_screen = fullwidth + && top == 0 && bot == ui->height-1; int clear_top, clear_bot; - ugrid_scroll(grid, (int)rows, &clear_top, &clear_bot); + ugrid_scroll(grid, top, bot, left, right, (int)rows, + &clear_top, &clear_bot); + + bool can_scroll = data->scroll_region_is_full_screen + || (data->can_change_scroll_region + && ((left == 0 && right == ui->width - 1) + || data->can_set_lr_margin + || data->can_set_left_right_margin)); - if (can_use_scroll(ui)) { + if (can_scroll) { // Change terminal scroll region and move cursor to the top if (!data->scroll_region_is_full_screen) { - set_scroll_region(ui); + set_scroll_region(ui, top, bot, left, right); } - cursor_goto(ui, grid->top, grid->left); + cursor_goto(ui, top, left); // also set default color attributes or some terminals can become funny update_attrs(ui, data->clear_attrs); @@ -1049,19 +1068,19 @@ static void tui_grid_scroll(UI *ui, Integer g, Integer top, Integer bot, // Restore terminal scroll region and cursor if (!data->scroll_region_is_full_screen) { - reset_scroll_region(ui); + reset_scroll_region(ui, fullwidth); } cursor_goto(ui, data->row, data->col); if (!(data->bce || no_bg(ui, data->clear_attrs))) { // Scrolling will leave wrong background in the cleared area on non-BCE // terminals. Update the cleared area. - clear_region(ui, clear_top, clear_bot, grid->left, grid->right, + clear_region(ui, clear_top, clear_bot, left, right, data->clear_attrs); } } else { // Mark the entire scroll region as invalid for redrawing later - invalidate(ui, grid->top, grid->bot, grid->left, grid->right); + invalidate(ui, top, bot, left, right); } } @@ -1220,7 +1239,8 @@ static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol, UGrid *grid = &data->grid; for (Integer c = startcol; c < endcol; c++) { memcpy(grid->cells[linerow][c].data, chunk[c-startcol], sizeof(schar_T)); - grid->cells[linerow][c].attrs = kv_A(data->attrs, attrs[c-startcol]); + assert((size_t)attrs[c-startcol] < kv_size(data->attrs)); + grid->cells[linerow][c].attr = attrs[c-startcol]; } UGRID_FOREACH_CELL(grid, (int)linerow, (int)linerow, (int)startcol, (int)endcol-1, { @@ -1231,7 +1251,7 @@ static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol, if (clearcol > endcol) { HlAttrs cl_attrs = kv_A(data->attrs, (size_t)clearattr); ugrid_clear_chunk(grid, (int)linerow, (int)endcol, (int)clearcol, - cl_attrs); + (sattr_T)clearattr); clear_region(ui, (int)linerow, (int)linerow, (int)endcol, (int)clearcol-1, cl_attrs); } @@ -1419,6 +1439,18 @@ static int unibi_find_ext_str(unibi_term *ut, const char *name) return -1; } +static int unibi_find_ext_bool(unibi_term *ut, const char *name) +{ + size_t max = unibi_count_ext_bool(ut); + for (size_t i = 0; i < max; i++) { + const char * n = unibi_get_ext_bool_name(ut, i); + if (n && 0 == strcmp(n, name)) { + return (int)i; + } + } + return -1; +} + /// Patches the terminfo records after loading from system or built-in db. /// Several entries in terminfo are known to be deficient or outright wrong; /// and several terminal emulators falsely announce incorrect terminal types. @@ -1446,6 +1478,7 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, || terminfo_is_term_family(term, "iterm2") || terminfo_is_term_family(term, "iTerm.app") || terminfo_is_term_family(term, "iTerm2.app"); + bool alacritty = terminfo_is_term_family(term, "alacritty"); // None of the following work over SSH; see :help TERM . bool iterm_pretending_xterm = xterm && iterm_env; bool konsole_pretending_xterm = xterm && konsole; @@ -1640,6 +1673,7 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, // per analysis of VT100Terminal.m || iterm || iterm_pretending_xterm || teraterm // per TeraTerm "Supported Control Functions" doco + || alacritty // https://github.com/jwilm/alacritty/pull/608 // Some linux-type terminals implement the xterm extension. // Example: console-terminal-emulator from the nosh toolset. || (linuxvt @@ -1817,6 +1851,22 @@ static void augment_terminfo(TUIData *data, const char *term, ut, "ext.enable_mouse", "\x1b[?1002h\x1b[?1006h"); data->unibi_ext.disable_mouse = (int)unibi_add_ext_str( ut, "ext.disable_mouse", "\x1b[?1002l\x1b[?1006l"); + + int ext_bool_Su = unibi_find_ext_bool(ut, "Su"); // used by kitty + if (vte_version >= 5102 + || (ext_bool_Su != -1 && unibi_get_ext_bool(ut, (size_t)ext_bool_Su))) { + data->unibi_ext.enter_undercurl_mode = (int)unibi_add_ext_str( + ut, "ext.enter_undercurl_mode", "\x1b[4:3m"); + data->unibi_ext.exit_undercurl_mode = (int)unibi_add_ext_str( + ut, "ext.exit_undercurl_mode", "\x1b[4:0m"); + if (has_colon_rgb) { + data->unibi_ext.set_underline_color = (int)unibi_add_ext_str( + ut, "ext.set_underline_color", "\x1b[58:2:%p1%d:%p2%d:%p3%dm"); + } else { + data->unibi_ext.set_underline_color = (int)unibi_add_ext_str( + ut, "ext.set_underline_color", "\x1b[58:2:%p1%d:%p2%d:%p3%dm"); + } + } } static void flush_buf(UI *ui) diff --git a/src/nvim/ugrid.c b/src/nvim/ugrid.c index 36936970f8..e2b92d7112 100644 --- a/src/nvim/ugrid.c +++ b/src/nvim/ugrid.c @@ -16,7 +16,6 @@ void ugrid_init(UGrid *grid) { - grid->attrs = HLATTRS_INIT; grid->cells = NULL; } @@ -33,10 +32,6 @@ void ugrid_resize(UGrid *grid, int width, int height) grid->cells[i] = xcalloc((size_t)width, sizeof(UCell)); } - grid->top = 0; - grid->bot = height - 1; - grid->left = 0; - grid->right = width - 1; grid->row = grid->col = 0; grid->width = width; grid->height = height; @@ -44,13 +39,12 @@ void ugrid_resize(UGrid *grid, int width, int height) void ugrid_clear(UGrid *grid) { - clear_region(grid, 0, grid->height-1, 0, grid->width-1, - HLATTRS_INIT); + clear_region(grid, 0, grid->height-1, 0, grid->width-1, 0); } -void ugrid_clear_chunk(UGrid *grid, int row, int col, int endcol, HlAttrs attrs) +void ugrid_clear_chunk(UGrid *grid, int row, int col, int endcol, sattr_T attr) { - clear_region(grid, row, row, col, endcol-1, attrs); + clear_region(grid, row, row, col, endcol-1, attr); } void ugrid_goto(UGrid *grid, int row, int col) @@ -59,25 +53,18 @@ void ugrid_goto(UGrid *grid, int row, int col) grid->col = col; } -void ugrid_set_scroll_region(UGrid *grid, int top, int bot, int left, int right) -{ - grid->top = top; - grid->bot = bot; - grid->left = left; - grid->right = right; -} - -void ugrid_scroll(UGrid *grid, int count, int *clear_top, int *clear_bot) +void ugrid_scroll(UGrid *grid, int top, int bot, int left, int right, + int count, int *clear_top, int *clear_bot) { // Compute start/stop/step for the loop below int start, stop, step; if (count > 0) { - start = grid->top; - stop = grid->bot - count + 1; + start = top; + stop = bot - count + 1; step = 1; } else { - start = grid->bot; - stop = grid->top - count - 1; + start = bot; + stop = top - count - 1; step = -1; } @@ -85,10 +72,10 @@ void ugrid_scroll(UGrid *grid, int count, int *clear_top, int *clear_bot) // Copy cell data for (i = start; i != stop; i += step) { - UCell *target_row = grid->cells[i] + grid->left; - UCell *source_row = grid->cells[i + count] + grid->left; + UCell *target_row = grid->cells[i] + left; + UCell *source_row = grid->cells[i + count] + left; memcpy(target_row, source_row, - sizeof(UCell) * (size_t)(grid->right - grid->left + 1)); + sizeof(UCell) * (size_t)(right - left + 1)); } // clear cells in the emptied region, @@ -99,32 +86,16 @@ void ugrid_scroll(UGrid *grid, int count, int *clear_top, int *clear_bot) *clear_bot = stop; *clear_top = stop + count + 1; } - clear_region(grid, *clear_top, *clear_bot, grid->left, grid->right, - HLATTRS_INIT); -} - -UCell *ugrid_put(UGrid *grid, uint8_t *text, size_t size) -{ - UCell *cell = grid->cells[grid->row] + grid->col; - cell->data[size] = 0; - cell->attrs = grid->attrs; - assert(size <= CELLBYTES); - - if (text) { - memcpy(cell->data, text, size); - } - - grid->col += 1; - return cell; + clear_region(grid, *clear_top, *clear_bot, left, right, 0); } static void clear_region(UGrid *grid, int top, int bot, int left, int right, - HlAttrs attrs) + sattr_T attr) { UGRID_FOREACH_CELL(grid, top, bot, left, right, { cell->data[0] = ' '; cell->data[1] = 0; - cell->attrs = attrs; + cell->attr = attr; }); } diff --git a/src/nvim/ugrid.h b/src/nvim/ugrid.h index 04e027bd46..af78fe91c5 100644 --- a/src/nvim/ugrid.h +++ b/src/nvim/ugrid.h @@ -11,14 +11,12 @@ typedef struct ugrid UGrid; struct ucell { char data[CELLBYTES + 1]; - HlAttrs attrs; + sattr_T attr; }; struct ugrid { - int top, bot, left, right; int row, col; int width, height; - HlAttrs attrs; UCell **cells; }; diff --git a/src/nvim/ui.h b/src/nvim/ui.h index df489f569f..d89ad60ce7 100644 --- a/src/nvim/ui.h +++ b/src/nvim/ui.h @@ -15,7 +15,7 @@ typedef enum { kUITabline, kUIWildmenu, #define kUIGlobalCount (kUIWildmenu+1) - kUINewgrid, + kUILinegrid, kUIHlState, kUIExtCount, } UIExtension; @@ -25,7 +25,7 @@ EXTERN const char *ui_ext_names[] INIT(= { "ext_popupmenu", "ext_tabline", "ext_wildmenu", - "ext_newgrid", + "ext_linegrid", "ext_hlstate", }); diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 1e6be5d824..df0507ed41 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -891,7 +891,7 @@ static u_header_T *unserialize_uhp(bufinfo_T *bi, for (;; ) { int len = undo_read_byte(bi); - if (len == 0) { + if (len == 0 || len == EOF) { break; } int what = undo_read_byte(bi); diff --git a/test/functional/api/version_spec.lua b/test/functional/api/version_spec.lua index e0472977cc..c3b332c8e7 100644 --- a/test/functional/api/version_spec.lua +++ b/test/functional/api/version_spec.lua @@ -156,6 +156,6 @@ describe("ui_options in metadata", function() local api = helpers.call('api_info') local options = api.ui_options eq({'rgb', 'ext_cmdline', 'ext_popupmenu', - 'ext_tabline', 'ext_wildmenu', 'ext_newgrid', 'ext_hlstate'}, options) + 'ext_tabline', 'ext_wildmenu', 'ext_linegrid', 'ext_hlstate'}, options) end) end) diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 5261f57ca7..5ca133267d 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -1254,7 +1254,7 @@ describe('API', function() ext_popupmenu = false, ext_tabline = false, ext_wildmenu = false, - ext_newgrid = screen._options.ext_newgrid or false, + ext_linegrid = screen._options.ext_linegrid or false, ext_hlstate=false, height = 4, rgb = true, diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index 2a67453bce..15121261c7 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -4,6 +4,7 @@ local Screen = require('test.functional.ui.screen') local clear = helpers.clear local command = helpers.command local eq = helpers.eq +local eval = helpers.eval local feed = helpers.feed local funcs = helpers.funcs local nvim_prog = helpers.nvim_prog @@ -196,5 +197,10 @@ describe('startup', function() funcs.system({nvim_prog, '-n', '-es' }, { 'set encoding', '' })) end) + + it('does not crash if --embed is given twice', function() + clear{args={'--embed'}} + eq(2, eval('1+1')) + end) end) diff --git a/test/functional/ex_cmds/recover_spec.lua b/test/functional/ex_cmds/recover_spec.lua deleted file mode 100644 index cb68c29b9a..0000000000 --- a/test/functional/ex_cmds/recover_spec.lua +++ /dev/null @@ -1,76 +0,0 @@ -local helpers = require('test.functional.helpers')(after_each) -local lfs = require('lfs') -local feed_command, eq, clear, eval, feed, expect, source = - helpers.feed_command, helpers.eq, helpers.clear, helpers.eval, helpers.feed, - helpers.expect, helpers.source -local command = helpers.command -local ok = helpers.ok -local rmdir = helpers.rmdir - -describe(':recover', function() - before_each(clear) - - it('fails if given a non-existent swapfile', function() - local swapname = 'bogus-swapfile' - feed_command('recover '..swapname) -- This should not segfault. #2117 - eq('E305: No swap file found for '..swapname, eval('v:errmsg')) - end) - -end) - -describe(':preserve', function() - local swapdir = lfs.currentdir()..'/testdir_recover_spec' - before_each(function() - clear() - rmdir(swapdir) - lfs.mkdir(swapdir) - end) - after_each(function() - command('%bwipeout!') - rmdir(swapdir) - end) - - it("saves to custom 'directory' and (R)ecovers (issue #1836)", function() - local testfile = 'testfile_recover_spec' - -- Put swapdir at the start of the 'directory' list. #1836 - -- Note: `set swapfile` *must* go after `set directory`: otherwise it may - -- attempt to create a swapfile in different directory. - local init = [[ - set directory^=]]..swapdir:gsub([[\]], [[\\]])..[[// - set swapfile fileformat=unix undolevels=-1 - ]] - - source(init) - command('edit! '..testfile) - feed('isometext<esc>') - command('preserve') - source('redir => g:swapname | silent swapname | redir END') - - local swappath1 = eval('g:swapname') - - --TODO(justinmk): this is an ugly hack to force `helpers` to support - --multiple sessions. - local nvim2 = helpers.spawn({helpers.nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed'}, - true) - helpers.set_session(nvim2) - - source(init) - - -- Use the "SwapExists" event to choose the (R)ecover choice at the dialog. - command('autocmd SwapExists * let v:swapchoice = "r"') - command('silent edit! '..testfile) - source('redir => g:swapname | silent swapname | redir END') - - local swappath2 = eval('g:swapname') - - expect('sometext') - -- swapfile from session 1 should end in .swp - eq(testfile..'.swp', string.match(swappath1, '[^%%]+$')) - -- swapfile from session 2 should end in .swo - eq(testfile..'.swo', string.match(swappath2, '[^%%]+$')) - -- Verify that :swapname was not truncated (:help 'shortmess'). - ok(nil == string.find(swappath1, '%.%.%.')) - ok(nil == string.find(swappath2, '%.%.%.')) - end) - -end) diff --git a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua new file mode 100644 index 0000000000..577a26178a --- /dev/null +++ b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua @@ -0,0 +1,154 @@ +local Screen = require('test.functional.ui.screen') +local helpers = require('test.functional.helpers')(after_each) +local lfs = require('lfs') +local feed_command, eq, eval, expect, source = + helpers.feed_command, helpers.eq, helpers.eval, helpers.expect, helpers.source +local clear = helpers.clear +local command = helpers.command +local feed = helpers.feed +local nvim_prog = helpers.nvim_prog +local ok = helpers.ok +local rmdir = helpers.rmdir +local set_session = helpers.set_session +local spawn = helpers.spawn + +describe(':recover', function() + before_each(clear) + + it('fails if given a non-existent swapfile', function() + local swapname = 'bogus-swapfile' + feed_command('recover '..swapname) -- This should not segfault. #2117 + eq('E305: No swap file found for '..swapname, eval('v:errmsg')) + end) + +end) + +describe(':preserve', function() + local swapdir = lfs.currentdir()..'/Xtest_recover_dir' + before_each(function() + clear() + rmdir(swapdir) + lfs.mkdir(swapdir) + end) + after_each(function() + command('%bwipeout!') + rmdir(swapdir) + end) + + it("saves to custom 'directory' and (R)ecovers #1836", function() + local testfile = 'Xtest_recover_file1' + -- Put swapdir at the start of the 'directory' list. #1836 + -- Note: `set swapfile` *must* go after `set directory`: otherwise it may + -- attempt to create a swapfile in different directory. + local init = [[ + set directory^=]]..swapdir:gsub([[\]], [[\\]])..[[// + set swapfile fileformat=unix undolevels=-1 + ]] + + source(init) + command('edit! '..testfile) + feed('isometext<esc>') + command('preserve') + source('redir => g:swapname | silent swapname | redir END') + + local swappath1 = eval('g:swapname') + + -- Start another Nvim instance. + local nvim2 = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed'}, + true) + set_session(nvim2) + + source(init) + + -- Use the "SwapExists" event to choose the (R)ecover choice at the dialog. + command('autocmd SwapExists * let v:swapchoice = "r"') + command('silent edit! '..testfile) + source('redir => g:swapname | silent swapname | redir END') + + local swappath2 = eval('g:swapname') + + expect('sometext') + -- swapfile from session 1 should end in .swp + eq(testfile..'.swp', string.match(swappath1, '[^%%]+$')) + -- swapfile from session 2 should end in .swo + eq(testfile..'.swo', string.match(swappath2, '[^%%]+$')) + -- Verify that :swapname was not truncated (:help 'shortmess'). + ok(nil == string.find(swappath1, '%.%.%.')) + ok(nil == string.find(swappath2, '%.%.%.')) + end) + +end) + +describe('swapfile detection', function() + local swapdir = lfs.currentdir()..'/Xtest_swapdialog_dir' + before_each(function() + clear() + rmdir(swapdir) + lfs.mkdir(swapdir) + end) + after_each(function() + command('%bwipeout!') + rmdir(swapdir) + end) + + it('always show swapfile dialog #8840 #9027', function() + local testfile = 'Xtest_swapdialog_file1' + -- Put swapdir at the start of the 'directory' list. #1836 + -- Note: `set swapfile` *must* go after `set directory`: otherwise it may + -- attempt to create a swapfile in different directory. + local init = [[ + set directory^=]]..swapdir:gsub([[\]], [[\\]])..[[// + set swapfile fileformat=unix undolevels=-1 hidden + ]] + + local expected_no_dialog = '^'..(' '):rep(256)..'|\n' + for _=1,37 do + expected_no_dialog = expected_no_dialog..'~'..(' '):rep(255)..'|\n' + end + expected_no_dialog = expected_no_dialog..testfile..(' '):rep(216)..'0,0-1 All|\n' + expected_no_dialog = expected_no_dialog..(' '):rep(256)..'|\n' + + source(init) + command('edit! '..testfile) + feed('isometext<esc>') + command('preserve') + + -- Start another Nvim instance. + local nvim2 = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed'}, + true) + set_session(nvim2) + local screen2 = Screen.new(256, 40) + screen2:attach() + source(init) + + -- With shortmess+=F + command('set shortmess+=F') + feed(':edit '..testfile..'<CR>') + screen2:expect{any=[[E325: ATTENTION.*]]..'\n'..[[Found a swap file by the name ".*]] + ..[[Xtest_swapdialog_dir[/\].*]]..testfile..[[%.swp"]]} + feed('e') -- Chose "Edit" at the swap dialog. + screen2:expect(expected_no_dialog) + + -- With :silent and shortmess+=F + feed(':silent edit %<CR>') + screen2:expect{any=[[Found a swap file by the name ".*]] + ..[[Xtest_swapdialog_dir[/\].*]]..testfile..[[%.swp"]]} + feed('e') -- Chose "Edit" at the swap dialog. + screen2:expect(expected_no_dialog) + + -- With :silent! and shortmess+=F + feed(':silent! edit %<CR>') + screen2:expect{any=[[Found a swap file by the name ".*]] + ..[[Xtest_swapdialog_dir[/\].*]]..testfile..[[%.swp"]]} + feed('e') -- Chose "Edit" at the swap dialog. + screen2:expect(expected_no_dialog) + + -- With API (via eval/VimL) call and shortmess+=F + feed(':call nvim_command("edit %")<CR>') + screen2:expect{any=[[Found a swap file by the name ".*]] + ..[[Xtest_swapdialog_dir[/\].*]]..testfile..[[%.swp"]]} + feed('e') -- Chose "Edit" at the swap dialog. + feed('<c-c>') + screen2:expect(expected_no_dialog) + end) +end) diff --git a/test/functional/options/shortmess_spec.lua b/test/functional/options/shortmess_spec.lua index 96823476de..8ea9a19464 100644 --- a/test/functional/options/shortmess_spec.lua +++ b/test/functional/options/shortmess_spec.lua @@ -1,43 +1,96 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') +local clear = helpers.clear local command = helpers.command -local clear, feed_command = helpers.clear, helpers.feed_command - -if helpers.pending_win32(pending) then return end +local eq = helpers.eq +local eval = helpers.eval +local feed = helpers.feed describe("'shortmess'", function() local screen before_each(function() clear() - screen = Screen.new(25, 5) + screen = Screen.new(42, 5) screen:attach() end) - after_each(function() - screen:detach() - end) - describe('"F" flag', function() - it('hides messages about the files read', function() - command("set shortmess-=F") - feed_command('e test') + it('hides :edit fileinfo messages', function() + command('set hidden') + command('set shortmess-=F') + feed(':edit foo<CR>') + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + "foo" [New File] | + ]]) + eq(1, eval('bufnr("%")')) + + command('set shortmess+=F') + feed(':edit bar<CR>') + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + :edit bar | + ]]) + eq(2, eval('bufnr("%")')) + end) + + it('hides :bnext, :bprevious fileinfo messages', function() + command('set hidden') + command('set shortmess-=F') + feed(':edit foo<CR>') + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + "foo" [New File] | + ]]) + eq(1, eval('bufnr("%")')) + feed(':edit bar<CR>') + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + "bar" [New File] | + ]]) + eq(2, eval('bufnr("%")')) + feed(':bprevious<CR>') + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + "foo" [New file] --No lines in buffer-- | + ]]) + eq(1, eval('bufnr("%")')) + + command('set shortmess+=F') + feed(':bnext<CR>') screen:expect([[ - ^ | - ~ | - ~ | - ~ | - "test" is a directory | + ^ | + ~ | + ~ | + ~ | + :bnext | ]]) - feed_command('set shortmess=F') - feed_command('e test') + eq(2, eval('bufnr("%")')) + feed(':bprevious<CR>') screen:expect([[ - ^ | - ~ | - ~ | - ~ | - :e test | + ^ | + ~ | + ~ | + ~ | + :bprevious | ]]) + eq(1, eval('bufnr("%")')) end) end) end) diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 351038e521..09f80ca849 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -258,10 +258,10 @@ describe('tui', function() feed_data(':echo map(nvim_list_uis(), {k,v -> sort(items(v))})\013') screen:expect([=[ [[['ext_cmdline', v:false], ['ext_hlstate', v:fals| - e], ['ext_newgrid', v:true], ['ext_popupmenu', v:f| - alse], ['ext_tabline', v:false], ['ext_wildmenu', | - v:false], ['height', 6], ['rgb', v:false], ['width| - ', 50]]] | + e], ['ext_linegrid', v:true], ['ext_popupmenu', v:| + false], ['ext_tabline', v:false], ['ext_wildmenu',| + v:false], ['height', 6], ['rgb', v:false], ['widt| + h', 50]]] | {10:Press ENTER or type command to continue}{1: } | {3:-- TERMINAL --} | ]=]) diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua index d39b24a00f..af26a6d88f 100644 --- a/test/functional/ui/cmdline_spec.lua +++ b/test/functional/ui/cmdline_spec.lua @@ -4,13 +4,13 @@ local clear, feed = helpers.clear, helpers.feed local source = helpers.source local command = helpers.command -local function test_cmdline(newgrid) +local function test_cmdline(linegrid) local screen before_each(function() clear() screen = Screen.new(25, 5) - screen:attach({rgb=true, ext_cmdline=true, ext_newgrid=newgrid}) + screen:attach({rgb=true, ext_cmdline=true, ext_linegrid=linegrid}) screen:set_default_attr_ids({ [1] = {bold = true, foreground = Screen.colors.Blue1}, [2] = {reverse = true}, @@ -608,7 +608,7 @@ local function test_cmdline(newgrid) end) end --- the representation of cmdline and cmdline_block contents changed with ext_newgrid +-- the representation of cmdline and cmdline_block contents changed with ext_linegrid -- (which uses indexed highlights) so make sure to test both describe('ui/ext_cmdline', function() test_cmdline(true) end) describe('ui/ext_cmdline (legacy highlights)', function() test_cmdline(false) end) diff --git a/test/functional/ui/embed_spec.lua b/test/functional/ui/embed_spec.lua index 6a5227803d..4fc93c3b63 100644 --- a/test/functional/ui/embed_spec.lua +++ b/test/functional/ui/embed_spec.lua @@ -5,14 +5,14 @@ local feed = helpers.feed local eq = helpers.eq local clear = helpers.clear -local function test_embed(ext_newgrid) +local function test_embed(ext_linegrid) local screen local function startup(...) clear{headless=false, args={...}} -- attach immediately after startup, for early UI screen = Screen.new(60, 8) - screen:attach{ext_newgrid=ext_newgrid} + screen:attach{ext_linegrid=ext_linegrid} screen:set_default_attr_ids({ [1] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, [2] = {bold = true, foreground = Screen.colors.SeaGreen4}, @@ -77,5 +77,5 @@ local function test_embed(ext_newgrid) end) end -describe('--embed UI on startup (ext_newgrid=true)', function() test_embed(true) end) -describe('--embed UI on startup (ext_newgrid=false)', function() test_embed(false) end) +describe('--embed UI on startup (ext_linegrid=true)', function() test_embed(true) end) +describe('--embed UI on startup (ext_linegrid=false)', function() test_embed(false) end) diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua index 4b6fbc0d74..7f3bf3e97b 100644 --- a/test/functional/ui/highlight_spec.lua +++ b/test/functional/ui/highlight_spec.lua @@ -40,24 +40,24 @@ describe('highlight: `:syntax manual`', function() end) it("works with buffer switch and 'hidden'", function() - feed_command('e tmp1.vim') - feed_command('e Xtest-functional-ui-highlight.tmp.vim') - feed_command('filetype on') - feed_command('syntax manual') - feed_command('set ft=vim') - feed_command('set syntax=ON') + command('e tmp1.vim') + command('e Xtest-functional-ui-highlight.tmp.vim') + command('filetype on') + command('syntax manual') + command('set ft=vim') + command('set syntax=ON') feed('iecho 1<esc>0') - feed_command('set hidden') - feed_command('w') - feed_command('bn') + command('set hidden') + command('w') + command('bn') feed_command('bp') screen:expect([[ {1:^echo} 1 | {0:~ }| {0:~ }| {0:~ }| - <f 1 --100%-- col 1 | + :bp | ]]) end) @@ -919,7 +919,7 @@ describe("'winhighlight' highlight", function() aa | {0:~ }| {4:[No Name] [+] }| - <f 1 --100%-- col 1 | + | ]]) end) diff --git a/test/functional/ui/options_spec.lua b/test/functional/ui/options_spec.lua index 322a94763f..99aae16183 100644 --- a/test/functional/ui/options_spec.lua +++ b/test/functional/ui/options_spec.lua @@ -30,15 +30,15 @@ describe('ui receives option updates', function() ext_popupmenu=false, ext_tabline=false, ext_wildmenu=false, - ext_newgrid=false, + ext_linegrid=false, ext_hlstate=false, } it("for defaults", function() screen:attach() - -- NB: UI test suite can be run in both "newgrid" and legacy grid mode. + -- NB: UI test suite can be run in both "linegrid" and legacy grid mode. -- In both cases check that the received value is the one requested. - defaults.ext_newgrid = screen._options.ext_newgrid or false + defaults.ext_linegrid = screen._options.ext_linegrid or false screen:expect(function() eq(defaults, screen.options) end) @@ -46,7 +46,7 @@ describe('ui receives option updates', function() it("when setting options", function() screen:attach() - defaults.ext_newgrid = screen._options.ext_newgrid or false + defaults.ext_linegrid = screen._options.ext_linegrid or false local changed = {} for k,v in pairs(defaults) do changed[k] = v @@ -95,7 +95,7 @@ describe('ui receives option updates', function() end screen:attach({ext_cmdline=true, ext_wildmenu=true}) - defaults.ext_newgrid = screen._options.ext_newgrid or false + defaults.ext_linegrid = screen._options.ext_linegrid or false changed.ext_cmdline = true changed.ext_wildmenu = true screen:expect(function() diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index 3831968f5b..691bf9f64c 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -185,11 +185,11 @@ function Screen:attach(options) if options == nil then options = {} end - if options.ext_newgrid == nil then - options.ext_newgrid = true + if options.ext_linegrid == nil then + options.ext_linegrid = true end self._options = options - self._clear_attrs = (options.ext_newgrid and {{},{}}) or {} + self._clear_attrs = (options.ext_linegrid and {{},{}}) or {} uimeths.attach(self._width, self._height, options) if self._options.rgb == nil then -- nvim defaults to rgb=true internally, @@ -386,9 +386,13 @@ function Screen:wait(check, timeout) local err, checked = false local success_seen = false local failure_after_success = false + local did_flush = true local function notification_cb(method, args) assert(method == 'redraw') - self:_redraw(args) + did_flush = self:_redraw(args) + if not did_flush then + return + end err = check() checked = true if not err then @@ -402,7 +406,9 @@ function Screen:wait(check, timeout) return true end run(nil, notification_cb, nil, timeout or self.timeout) - if not checked then + if not did_flush then + err = "no flush received" + elseif not checked then err = check() end @@ -431,7 +437,8 @@ function Screen:sleep(ms) end function Screen:_redraw(updates) - for _, update in ipairs(updates) do + local did_flush = false + for k, update in ipairs(updates) do -- print('--') -- print(require('inspect')(update)) local method = update[1] @@ -446,7 +453,11 @@ function Screen:_redraw(updates) self._on_event(method, update[i]) end end + if k == #updates and method == "flush" then + did_flush = true + end end + return did_flush end function Screen:set_on_event_handler(callback) @@ -472,6 +483,10 @@ function Screen:_handle_resize(width, height) } end +function Screen:_handle_flush() +end + + function Screen:_handle_grid_resize(grid, width, height) assert(grid == 1) self:_handle_resize(width, height) @@ -609,6 +624,7 @@ function Screen:_handle_highlight_set(attrs) end function Screen:_handle_put(str) + assert(not self._options.ext_linegrid) local cell = self._rows[self._cursor.row][self._cursor.col] cell.text = str cell.attrs = self._attrs @@ -617,6 +633,7 @@ function Screen:_handle_put(str) end function Screen:_handle_grid_line(grid, row, col, items) + assert(self._options.ext_linegrid) assert(grid == 1) local line = self._rows[row+1] local colpos = col+1 @@ -764,7 +781,7 @@ function Screen:_row_repr(row, attr_state) local current_attr_id for i = 1, self._width do local attrs = row[i].attrs - if self._options.ext_newgrid then + if self._options.ext_linegrid then attrs = attrs[(self._options.rgb and 1) or 2] end local attr_id = self:_get_attr_id(attr_state, attrs, row[i].hl_id) @@ -820,7 +837,7 @@ function Screen:_chunks_repr(chunks, attr_state) for i, chunk in ipairs(chunks) do local hl, text = unpack(chunk) local attrs - if self._options.ext_newgrid then + if self._options.ext_linegrid then attrs = self._attr_table[hl][1] else attrs = hl diff --git a/test/functional/ui/screen_basic_spec.lua b/test/functional/ui/screen_basic_spec.lua index 957d8c0915..31825bdbf4 100644 --- a/test/functional/ui/screen_basic_spec.lua +++ b/test/functional/ui/screen_basic_spec.lua @@ -48,13 +48,13 @@ describe('screen', function() end) end) -local function screen_tests(newgrid) +local function screen_tests(linegrid) local screen before_each(function() clear() screen = Screen.new() - screen:attach({rgb=true,ext_newgrid=newgrid}) + screen:attach({rgb=true,ext_linegrid=linegrid}) screen:set_default_attr_ids( { [0] = {bold=true, foreground=255}, [1] = {bold=true, reverse=true}, diff --git a/test/helpers.lua b/test/helpers.lua index 013fe60596..6ef7a456f4 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -1,6 +1,7 @@ local assert = require('luassert') local luv = require('luv') local lfs = require('lfs') +local relpath = require('pl.path').relpath local quote_me = '[^.%w%+%-%@%_%/]' -- complement (needn't quote) local function shell_quote(str) @@ -244,7 +245,7 @@ local function check_cores(app, force) -- Workspace-local $TMPDIR, scrubbed and pattern-escaped. -- "./Xtest-tmpdir/" => "Xtest%-tmpdir" local local_tmpdir = (tmpdir_is_local(tmpdir_get()) - and tmpdir_get():gsub('^[ ./]+',''):gsub('%/+$',''):gsub('([^%w])', '%%%1') + and relpath(tmpdir_get()):gsub('^[ ./]+',''):gsub('%/+$',''):gsub('([^%w])', '%%%1') or nil) local db_cmd if hasenv('NVIM_TEST_CORE_GLOB_DIRECTORY') then @@ -645,8 +646,38 @@ local function hexdump(str) return dump .. hex .. string.rep(" ", 8 - len % 8) .. asc end -local function read_file(name) - local file = io.open(name, 'r') +-- Reads text lines from `filename` into a table. +-- +-- filename: path to file +-- start: start line (1-indexed), negative means "lines before end" (tail) +local function read_file_list(filename, start) + local lnum = (start ~= nil and type(start) == 'number') and start or 1 + local tail = (lnum < 0) + local maxlines = tail and math.abs(lnum) or nil + local file = io.open(filename, 'r') + if not file then + return nil + end + local lines = {} + local i = 1 + for line in file:lines() do + if i >= start then + table.insert(lines, line) + if #lines > maxlines then + table.remove(lines, 1) + end + end + i = i + 1 + end + file:close() + return lines +end + +-- Reads the entire contents of `filename` into a string. +-- +-- filename: path to file +local function read_file(filename) + local file = io.open(filename, 'r') if not file then return nil end @@ -684,18 +715,13 @@ end -- Also removes the file, if the current environment looks like CI. local function read_nvim_log() local logfile = os.getenv('NVIM_LOG_FILE') or '.nvimlog' - local logtext = read_file(logfile) - local lines = {} - for l in string.gmatch(logtext or '', "[^\n]+") do -- Split at newlines. - table.insert(lines, l) - end + local keep = isCI() and 999 or 10 + local lines = read_file_list(logfile, -keep) or {} local log = (('-'):rep(78)..'\n' ..string.format('$NVIM_LOG_FILE: %s\n', logfile) - ..(logtext and (isCI() and '' or '(last 10 lines)\n') or '(empty)\n')) - local keep = (isCI() and #lines or math.min(10, #lines)) - local startidx = math.max(1, #lines - keep + 1) - for i = startidx, (startidx + keep - 1) do - log = log..lines[i]..'\n' + ..(#lines > 0 and '(last '..tostring(keep)..' lines)\n' or '(empty)\n')) + for _,line in ipairs(lines) do + log = log..line..'\n' end log = log..('-'):rep(78)..'\n' if isCI() then @@ -733,6 +759,7 @@ local module = { popen_r = popen_r, popen_w = popen_w, read_file = read_file, + read_file_list = read_file_list, read_nvim_log = read_nvim_log, repeated_read_cmd = repeated_read_cmd, shallowcopy = shallowcopy, diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt index f770999c0f..4ca00b26cd 100644 --- a/third-party/CMakeLists.txt +++ b/third-party/CMakeLists.txt @@ -88,7 +88,7 @@ if(MINGW AND CMAKE_GENERATOR MATCHES "Ninja") if(NOT MAKE_PRG) message(FATAL_ERROR "GNU Make for mingw32 is required to build the dependecies.") else() - message(STATUS "Found GNU Make for mingw32 at ${MAKE_PRG}") + message(STATUS "Found GNU Make for mingw32: ${MAKE_PRG}") endif() endif() @@ -141,8 +141,8 @@ set(LIBTERMKEY_SHA256 6c0d87c94ab9915e76ecd313baec08dedf3bd56de83743d9aa923a0819 set(LIBVTERM_URL https://github.com/neovim/libvterm/archive/3f62ac6b7bdffda39d68f723fb1806dfd6d6382d.tar.gz) set(LIBVTERM_SHA256 1c8b318370f00f831f43e3ec86a48984250e3ee5c76beb106a421c9a42286ac5) -set(JEMALLOC_URL https://github.com/jemalloc/jemalloc/releases/download/5.1.0/jemalloc-5.1.0.tar.bz2) -set(JEMALLOC_SHA256 5396e61cc6103ac393136c309fae09e44d74743c86f90e266948c50f3dbb7268) +set(JEMALLOC_URL https://github.com/jemalloc/jemalloc/releases/download/4.5.0/jemalloc-4.5.0.tar.bz2) +set(JEMALLOC_SHA256 9409d85664b4f135b77518b0b118c549009dc10f6cba14557d170476611f6780) set(LUV_URL https://github.com/luvit/luv/archive/1.9.1-1.tar.gz) set(LUV_SHA256 562b9efaad30aa051a40eac9ade0c3df48bb8186763769abe47ec3fb3edb1268) |