diff --git a/.travis.yml b/.travis.yml index 325b5e7b56..8c8bb2c5ec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -76,7 +76,7 @@ addons: - valgrind - xclip homebrew: - update: false + update: true packages: - ccache - ninja diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c3c20d23f..b6dd57862b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,8 +114,8 @@ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY # version string, else they are combined with the result of `git describe`. set(NVIM_VERSION_MAJOR 0) set(NVIM_VERSION_MINOR 4) -set(NVIM_VERSION_PATCH 0) -set(NVIM_VERSION_PRERELEASE "") # for package maintainers +set(NVIM_VERSION_PATCH 4) +set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers # API level set(NVIM_API_LEVEL 6) # Bump this after any API change. @@ -314,6 +314,11 @@ if(UNIX) endif() endif() +check_c_compiler_flag(-fno-common HAVE_FNO_COMMON) +if (HAVE_FNO_COMMON) + add_compile_options(-fno-common) +endif() + check_c_compiler_flag(-fdiagnostics-color=auto HAS_DIAG_COLOR_FLAG) if(HAS_DIAG_COLOR_FLAG) if(CMAKE_GENERATOR MATCHES "Ninja") @@ -454,17 +459,17 @@ if((CLANG_ASAN_UBSAN OR CLANG_MSAN OR CLANG_TSAN) AND NOT CMAKE_C_COMPILER_ID MA message(FATAL_ERROR "Sanitizers are only supported for Clang") endif() +if(ENABLE_LIBICONV) + find_package(Iconv REQUIRED) + include_directories(SYSTEM ${Iconv_INCLUDE_DIRS}) +endif() + if(ENABLE_LIBINTL) # LibIntl (not Intl) selects our FindLibIntl.cmake script. #8464 find_package(LibIntl REQUIRED) include_directories(SYSTEM ${LibIntl_INCLUDE_DIRS}) endif() -if(ENABLE_LIBICONV) - find_package(Iconv REQUIRED) - include_directories(SYSTEM ${Iconv_INCLUDE_DIRS}) -endif() - # Determine platform's threading library. Set CMAKE_THREAD_PREFER_PTHREAD # explicitly to indicate a strong preference for pthread. set(CMAKE_THREAD_PREFER_PTHREAD ON) diff --git a/appveyor.yml b/appveyor.yml index 756c7fea2e..bb7bb1c4e9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -40,6 +40,3 @@ cache: artifacts: - path: build/Neovim.zip - path: build/bin/nvim.exe -branches: - only: - - master diff --git a/ci/before_install.sh b/ci/before_install.sh index 5cb6894b8c..f21f541474 100755 --- a/ci/before_install.sh +++ b/ci/before_install.sh @@ -26,7 +26,7 @@ if [[ "${TRAVIS_OS_NAME}" != osx ]] && command -v pyenv; then echo 'Setting Python versions via pyenv' # Prefer Python 2 over 3 (more conservative). - pyenv global 2.7.15:3.7 + pyenv global 2.7.15:3.7.1 echo 'Updated Python info:' ( diff --git a/cmake/FindLibIntl.cmake b/cmake/FindLibIntl.cmake index 5663098147..09eafb786a 100644 --- a/cmake/FindLibIntl.cmake +++ b/cmake/FindLibIntl.cmake @@ -38,6 +38,9 @@ endif() if (LibIntl_LIBRARY) list(APPEND CMAKE_REQUIRED_LIBRARIES "${LibIntl_LIBRARY}") endif() +if (MSVC) + list(APPEND CMAKE_REQUIRED_LIBRARIES ${ICONV_LIBRARY}) +endif() check_c_source_compiles(" #include @@ -48,6 +51,9 @@ int main(int argc, char** argv) { bind_textdomain_codeset(\"foo\", \"bar\"); textdomain(\"foo\"); }" HAVE_WORKING_LIBINTL) +if (MSVC) + list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES ${ICONV_LIBRARY}) +endif() if (LibIntl_INCLUDE_DIR) list(REMOVE_ITEM CMAKE_REQUIRED_INCLUDES "${LibIntl_INCLUDE_DIR}") endif() diff --git a/runtime/autoload/netrw.vim b/runtime/autoload/netrw.vim index a5b47e06d5..9b1266c4ca 100644 --- a/runtime/autoload/netrw.vim +++ b/runtime/autoload/netrw.vim @@ -688,10 +688,6 @@ fun! netrw#Explore(indx,dosplit,style,...) endif " save registers - if has("clipboard") - sil! let keepregstar = @* - sil! let keepregplus = @+ - endif sil! let keepregslash= @/ " if dosplit @@ -915,10 +911,6 @@ fun! netrw#Explore(indx,dosplit,style,...) " call Decho("..case Nexplore with starpat=".starpat.": (indx=".indx.")",'~'.expand("")) if !exists("w:netrw_explore_list") " sanity check NetrwKeepj call netrw#ErrorMsg(s:WARNING,"using Nexplore or improperly; see help for netrw-starstar",40) - if has("clipboard") - sil! let @* = keepregstar - sil! let @+ = keepregplus - endif sil! let @/ = keepregslash " call Dret("netrw#Explore") return @@ -940,10 +932,6 @@ fun! netrw#Explore(indx,dosplit,style,...) " call Decho("case Pexplore with starpat=".starpat.": (indx=".indx.")",'~'.expand("")) if !exists("w:netrw_explore_list") " sanity check NetrwKeepj call netrw#ErrorMsg(s:WARNING,"using Pexplore or improperly; see help for netrw-starstar",41) - if has("clipboard") - sil! let @* = keepregstar - sil! let @+ = keepregplus - endif sil! let @/ = keepregslash " call Dret("netrw#Explore") return @@ -995,10 +983,6 @@ fun! netrw#Explore(indx,dosplit,style,...) catch /^Vim\%((\a\+)\)\=:E480/ keepalt call netrw#ErrorMsg(s:WARNING,'no files matched pattern<'.pattern.'>',45) if &hls | let keepregslash= s:ExplorePatHls(pattern) | endif - if has("clipboard") - sil! let @* = keepregstar - sil! let @+ = keepregplus - endif sil! let @/ = keepregslash " call Dret("netrw#Explore : no files matched pattern") return @@ -1031,10 +1015,6 @@ fun! netrw#Explore(indx,dosplit,style,...) if w:netrw_explore_listlen == 0 || (w:netrw_explore_listlen == 1 && w:netrw_explore_list[0] =~ '\*\*\/') keepalt NetrwKeepj call netrw#ErrorMsg(s:WARNING,"no files matched",42) - if has("clipboard") - sil! let @* = keepregstar - sil! let @+ = keepregplus - endif sil! let @/ = keepregslash " call Dret("netrw#Explore : no files matched") return @@ -1079,10 +1059,6 @@ fun! netrw#Explore(indx,dosplit,style,...) if !exists("g:netrw_quiet") keepalt NetrwKeepj call netrw#ErrorMsg(s:WARNING,"your vim needs the +path_extra feature for Exploring with **!",44) endif - if has("clipboard") - sil! let @* = keepregstar - sil! let @+ = keepregplus - endif sil! let @/ = keepregslash " call Dret("netrw#Explore : missing +path_extra") return @@ -1152,10 +1128,6 @@ fun! netrw#Explore(indx,dosplit,style,...) " there's no danger of a late FocusGained event on initialization. " Consequently, set s:netrw_events to 2. let s:netrw_events= 2 - if has("clipboard") - sil! let @* = keepregstar - sil! let @+ = keepregplus - endif sil! let @/ = keepregslash " call Dret("netrw#Explore : @/<".@/.">") endfun @@ -9559,10 +9531,6 @@ fun! s:NetrwWideListing() let newcolstart = w:netrw_bannercnt + fpc let newcolend = newcolstart + fpc - 1 " call Decho("bannercnt=".w:netrw_bannercnt." fpl=".w:netrw_fpl." fpc=".fpc." newcol[".newcolstart.",".newcolend."]",'~'.expand("")) - if has("clipboard") - sil! let keepregstar = @* - sil! let keepregplus = @+ - endif while line("$") >= newcolstart if newcolend > line("$") | let newcolend= line("$") | endif let newcolqty= newcolend - newcolstart @@ -9575,10 +9543,6 @@ fun! s:NetrwWideListing() exe "sil! NetrwKeepj ".newcolstart.','.newcolend.'d _' exe 'sil! NetrwKeepj '.w:netrw_bannercnt endwhile - if has("clipboard") - sil! let @*= keepregstar - sil! let @+= keepregplus - endif exe "sil! NetrwKeepj ".w:netrw_bannercnt.',$s/\s\+$//e' NetrwKeepj call histdel("/",-1) exe 'nno w :call search(''^.\\|\s\s\zs\S'',''W'')'."\" diff --git a/runtime/autoload/provider/node.vim b/runtime/autoload/provider/node.vim index b2a3b3ee08..c5d5e87729 100644 --- a/runtime/autoload/provider/node.vim +++ b/runtime/autoload/provider/node.vim @@ -51,6 +51,9 @@ function! provider#node#Detect() abort if exists('g:node_host_prog') return expand(g:node_host_prog) endif + if !executable('node') + return '' + endif if !s:is_minimum_version(v:null, 6, 0) return '' endif diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 188f7fc2e2..525475b60b 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2348,7 +2348,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'fillchars'* *'fcs'* 'fillchars' 'fcs' string (default "") - local to window + global or local to window |global-local| Characters to fill the statuslines and vertical separators. It is a comma separated list of items: @@ -3657,7 +3657,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'listchars'* *'lcs'* 'listchars' 'lcs' string (default: "tab:> ,trail:-,nbsp:+" Vi default: "eol:$") - local to window + global or local to window |global-local| Strings to use in 'list' mode and for the |:list| command. It is a comma separated list of string settings. @@ -3698,9 +3698,9 @@ A jump table for the options with a short description can be found at |Q_op|. off and the line continues beyond the right of the screen. *lcs-precedes* - precedes:c Character to show in the first column, when 'wrap' - is off and there is text preceding the character - visible in the first column. + precedes:c Character to show in the first visible column of the + physical line, when there is text preceding the + character visible in the first column. *lcs-conceal* conceal:c Character to show in place of concealed text, when 'conceallevel' is set to 1. A space when omitted. @@ -4583,6 +4583,16 @@ A jump table for the options with a short description can be found at |Q_op|. RedrawDebugRecompose guibg=Red redraw generated by the compositor itself, due to a grid being moved or deleted. + nothrottle Turn off throttling of the message grid. This is an + optimization that joins many small scrolls to one + larger scroll when drawing the message area (with + 'display' msgsep flag active). + invalid Enable stricter checking (abort) of inconsistencies + of the internal screen state. This is mostly + useful when running nvim inside a debugger (and + the test suite). + nodelta Send all internally redrawn cells to the UI, even if + they are unchanged from the already displayed state. *'redrawtime'* *'rdt'* 'redrawtime' 'rdt' number (default 2000) @@ -6908,7 +6918,6 @@ A jump table for the options with a short description can be found at |Q_op|. global The number of milliseconds to wait for each character sent to the screen. When positive, characters are sent to the UI one by one. - When negative, all redrawn characters cause a delay, even if the - character already was displayed by the UI. For debugging purposes. + See 'redrawdebug' for more options. For debugging purposes. vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/nvim.appdata.xml b/runtime/nvim.appdata.xml index 32d3c523c6..025de1b5a9 100644 --- a/runtime/nvim.appdata.xml +++ b/runtime/nvim.appdata.xml @@ -26,6 +26,10 @@ + + + + diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 2056cb07e3..d915ac9db5 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -1363,6 +1363,9 @@ Dictionary copy_dictionary(Dictionary dict) Object copy_object(Object obj) { switch (obj.type) { + case kObjectTypeBuffer: + case kObjectTypeTabpage: + case kObjectTypeWindow: case kObjectTypeNil: case kObjectTypeBoolean: case kObjectTypeInteger: diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 602733fd31..60d386949b 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1266,7 +1266,7 @@ Boolean nvim_paste(String data, Boolean crlf, Integer phase, Error *err) draining = true; goto theend; } - if (!(State & CMDLINE) && !(State & INSERT) && (phase == -1 || phase == 1)) { + if (!(State & (CMDLINE | INSERT)) && (phase == -1 || phase == 1)) { ResetRedobuff(); AppendCharToRedobuff('a'); // Dot-repeat. } @@ -1284,7 +1284,7 @@ Boolean nvim_paste(String data, Boolean crlf, Integer phase, Error *err) } } } - if (!(State & CMDLINE) && !(State & INSERT) && (phase == -1 || phase == 3)) { + if (!(State & (CMDLINE | INSERT)) && (phase == -1 || phase == 3)) { AppendCharToRedobuff(ESC); // Dot-repeat. } theend: diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua index c223679596..a52789c795 100644 --- a/src/nvim/auevents.lua +++ b/src/nvim/auevents.lua @@ -21,9 +21,9 @@ return { 'BufWritePre', -- before writing a buffer 'ChanInfo', -- info was received about channel 'ChanOpen', -- channel was opened - 'CmdLineChanged', -- command line was modified - 'CmdLineEnter', -- after entering cmdline mode - 'CmdLineLeave', -- before leaving cmdline mode + 'CmdlineChanged', -- command line was modified + 'CmdlineEnter', -- after entering cmdline mode + 'CmdlineLeave', -- before leaving cmdline mode 'CmdUndefined', -- command undefined 'CmdWinEnter', -- after entering the cmdline window 'CmdWinLeave', -- before leaving the cmdline window diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index b50c764ac3..1a925f2fd6 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -3532,6 +3532,12 @@ int build_stl_str_hl( if (n == curitem && group_start_userhl == group_end_userhl) { out_p = t; group_len = 0; + // do not use the highlighting from the removed group + for (n = groupitems[groupdepth] + 1; n < curitem; n++) { + if (items[n].type == Highlight) { + items[n].type = Empty; + } + } } } diff --git a/src/nvim/channel.c b/src/nvim/channel.c index f9102fa0e2..3b7ef8346e 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -15,7 +15,6 @@ #include "nvim/ascii.h" static bool did_stdio = false; -PMap(uint64_t) *channels = NULL; /// next free id for a job or rpc channel /// 1 is reserved for stdio channel diff --git a/src/nvim/channel.h b/src/nvim/channel.h index c733e276be..9d26852ce5 100644 --- a/src/nvim/channel.h +++ b/src/nvim/channel.h @@ -85,7 +85,7 @@ struct Channel { bool callback_scheduled; }; -EXTERN PMap(uint64_t) *channels; +EXTERN PMap(uint64_t) *channels INIT(= NULL); #ifdef INCLUDE_GENERATED_DECLARATIONS # include "channel.h.generated.h" diff --git a/src/nvim/eval.c b/src/nvim/eval.c index e39f36957c..e1ee0fed09 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -8492,7 +8492,7 @@ static void f_getenv(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (p == NULL) { rettv->v_type = VAR_SPECIAL; - rettv->vval.v_number = kSpecialVarNull; + rettv->vval.v_special = kSpecialVarNull; return; } rettv->vval.v_string = p; @@ -15441,7 +15441,7 @@ static void f_setenv(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *name = tv_get_string_buf(&argvars[0], namebuf); if (argvars[1].v_type == VAR_SPECIAL - && argvars[1].vval.v_number == kSpecialVarNull) { + && argvars[1].vval.v_special == kSpecialVarNull) { os_unsetenv(name); } else { os_setenv(name, tv_get_string_buf(&argvars[1], valbuf), 1); @@ -18747,8 +18747,9 @@ static void f_visualmode(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_wildmenumode(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - if (wild_menu_showing) + if (wild_menu_showing || ((State & CMDLINE) && pum_visible())) { rettv->vval.v_number = 1; + } } /// "win_findbuf()" function @@ -20871,6 +20872,7 @@ void ex_echo(exarg_T *eap) char_u *arg = eap->arg; typval_T rettv; bool atstart = true; + bool need_clear = true; const int did_emsg_before = did_emsg; if (eap->skip) @@ -20913,7 +20915,7 @@ void ex_echo(exarg_T *eap) char *tofree = encode_tv2echo(&rettv, NULL); if (*tofree != NUL) { msg_ext_set_kind("echo"); - msg_multiline_attr(tofree, echo_attr, true); + msg_multiline_attr(tofree, echo_attr, true, &need_clear); } xfree(tofree); } @@ -20926,7 +20928,9 @@ void ex_echo(exarg_T *eap) emsg_skip--; } else { // remove text that may still be there from the command - msg_clr_eos(); + if (need_clear) { + msg_clr_eos(); + } if (eap->cmdidx == CMD_echo) { msg_end(); } diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index 0b04170cac..7163339ae0 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -33,7 +33,7 @@ typedef double float_T; enum { DO_NOT_FREE_CNT = (INT_MAX / 2) }; /// Additional values for tv_list_alloc() len argument -enum { +enum ListLenSpecials { /// List length is not known in advance /// /// To be used when there is neither a way to know how many elements will be @@ -49,7 +49,7 @@ enum { /// /// To be used when it looks impractical to determine list length. kListLenMayKnow = -3, -} ListLenSpecials; +}; /// Maximal possible value of varnumber_T variable #define VARNUMBER_MAX INT64_MAX diff --git a/src/nvim/event/loop.c b/src/nvim/event/loop.c index a01bbc9888..960afca69f 100644 --- a/src/nvim/event/loop.c +++ b/src/nvim/event/loop.c @@ -116,6 +116,20 @@ void loop_on_put(MultiQueue *queue, void *data) uv_stop(&loop->uv); } +#if !defined(EXITFREE) +static void loop_walk_cb(uv_handle_t *handle, void *arg) +{ + if (!uv_is_closing(handle)) { + uv_close(handle, NULL); + } +} +#endif + +/// Closes `loop` and its handles, and frees its structures. +/// +/// @param loop Loop to destroy +/// @param wait Wait briefly for handles to deref +/// /// @returns false if the loop could not be closed gracefully bool loop_close(Loop *loop, bool wait) { @@ -126,18 +140,34 @@ bool loop_close(Loop *loop, bool wait) uv_close((uv_handle_t *)&loop->poll_timer, timer_close_cb); uv_close((uv_handle_t *)&loop->async, NULL); uint64_t start = wait ? os_hrtime() : 0; + bool didstop = false; while (true) { - uv_run(&loop->uv, wait ? UV_RUN_DEFAULT : UV_RUN_NOWAIT); - if (!uv_loop_close(&loop->uv) || !wait) { + // Run the loop to tickle close-callbacks (which should then free memory). + // Use UV_RUN_NOWAIT to avoid a hang. #11820 + uv_run(&loop->uv, didstop ? UV_RUN_DEFAULT : UV_RUN_NOWAIT); + if ((uv_loop_close(&loop->uv) != UV_EBUSY) || !wait) { break; } - if (os_hrtime() - start >= 2 * 1000000000) { + uint64_t elapsed_s = (os_hrtime() - start) / 1000000000; // seconds + if (elapsed_s >= 2) { // Some libuv resource was not correctly deref'd. Log and bail. rv = false; ELOG("uv_loop_close() hang?"); log_uv_handles(&loop->uv); break; } +#if defined(EXITFREE) + (void)didstop; +#else + if (!didstop) { + // Loop won’t block for I/O after this. + uv_stop(&loop->uv); + // XXX: Close all (lua/luv!) handles. But loop_walk_cb() does not call + // resource-specific close-callbacks, so this leaks memory... + uv_walk(&loop->uv, loop_walk_cb, NULL); + didstop = true; + } +#endif } multiqueue_free(loop->fast_events); multiqueue_free(loop->thread_events); diff --git a/src/nvim/getchar.h b/src/nvim/getchar.h index 01f60ccf49..f0b52079aa 100644 --- a/src/nvim/getchar.h +++ b/src/nvim/getchar.h @@ -10,12 +10,12 @@ /// Values for "noremap" argument of ins_typebuf() /// /// Also used for map->m_noremap and menu->noremap[]. -enum { +enum RemapValues { REMAP_YES = 0, ///< Allow remapping. REMAP_NONE = -1, ///< No remapping. REMAP_SCRIPT = -2, ///< Remap script-local mappings only. REMAP_SKIP = -3, ///< No remapping for first char. -} RemapValues; +}; // Argument for flush_buffers(). typedef enum { diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index a03e97490d..0fa43c1184 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -189,11 +189,14 @@ paste = (function() if mode == 'c' and not got_line1 then -- cmdline-mode: paste only 1 line. got_line1 = (#lines > 1) vim.api.nvim_set_option('paste', true) -- For nvim_input(). - local line1, _ = string.gsub(lines[1], '[\r\n\012\027]', ' ') -- Scrub. + local line1 = lines[1]:gsub('<', ''):gsub('[\r\n\012\027]', ' ') -- Scrub. vim.api.nvim_input(line1) vim.api.nvim_set_option('paste', false) - elseif mode ~= 'c' then -- Else: discard remaining cmdline-mode chunks. - if phase < 2 and mode ~= 'i' and mode ~= 'R' and mode ~= 't' then + elseif mode ~= 'c' then + if phase < 2 and mode:find('^[vV\22sS\19]') then + vim.api.nvim_command([[exe "normal! \"]]) + vim.api.nvim_put(lines, 'c', false, true) + elseif phase < 2 and not mode:find('^[iRt]') then vim.api.nvim_put(lines, 'c', true, true) -- XXX: Normal-mode: workaround bad cursor-placement after first chunk. vim.api.nvim_command('normal! a') diff --git a/src/nvim/main.c b/src/nvim/main.c index be1f08bb46..8b55839cf6 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -10,6 +10,7 @@ #include #include "nvim/ascii.h" +#include "nvim/channel.h" #include "nvim/vim.h" #include "nvim/main.h" #include "nvim/aucmd.h" @@ -284,6 +285,12 @@ int main(int argc, char **argv) fname = get_fname(¶ms, cwd); } + // Recovery mode without a file name: List swap files. + // In this case, no UI is needed. + if (recoverymode && fname == NULL) { + headless_mode = true; + } + TIME_MSG("expanding arguments"); if (params.diff_mode && params.window_count == -1) diff --git a/src/nvim/message.c b/src/nvim/message.c index 30e906cd5f..b48c5c519e 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -228,20 +228,25 @@ int msg_attr(const char *s, const int attr) } /// similar to msg_outtrans_attr, but support newlines and tabs. -void msg_multiline_attr(const char *s, int attr, bool check_int) +void msg_multiline_attr(const char *s, int attr, + bool check_int, bool *need_clear) FUNC_ATTR_NONNULL_ALL { const char *next_spec = s; - while (next_spec != NULL && (!check_int || !got_int)) { + while (next_spec != NULL) { + if (check_int && got_int) { + return; + } next_spec = strpbrk(s, "\t\n\r"); if (next_spec != NULL) { // Printing all char that are before the char found by strpbrk msg_outtrans_len_attr((char_u *)s, next_spec - s, attr); - if (*next_spec != TAB) { + if (*next_spec != TAB && *need_clear) { msg_clr_eos(); + *need_clear = false; } msg_putchar_attr((uint8_t)(*next_spec), attr); s = next_spec + 1; @@ -253,6 +258,7 @@ void msg_multiline_attr(const char *s, int attr, bool check_int) if (*s != NUL) { msg_outtrans_attr((char_u *)s, attr); } + return; } @@ -311,12 +317,15 @@ bool msg_attr_keep(char_u *s, int attr, bool keep, bool multiline) if (buf != NULL) s = buf; + bool need_clear = true; if (multiline) { - msg_multiline_attr((char *)s, attr, false); + msg_multiline_attr((char *)s, attr, false, &need_clear); } else { msg_outtrans_attr(s, attr); } - msg_clr_eos(); + if (need_clear) { + msg_clr_eos(); + } retval = msg_end(); if (keep && retval && vim_strsize(s) < (int)(Rows - cmdline_row - 1) @@ -2561,10 +2570,15 @@ static int do_more_prompt(int typed_char) msgchunk_T *mp; int i; + // If headless mode is enabled and no input is required, this variable + // will be true. However If server mode is enabled, the message "--more--" + // should be displayed. + bool no_need_more = headless_mode && !embedded_mode; + // We get called recursively when a timer callback outputs a message. In // that case don't show another prompt. Also when at the hit-Enter prompt // and nothing was typed. - if (entered || (State == HITRETURN && typed_char == 0)) { + if (no_need_more || entered || (State == HITRETURN && typed_char == 0)) { return false; } entered = true; diff --git a/src/nvim/msgpack_rpc/channel.h b/src/nvim/msgpack_rpc/channel.h index 9ff5abdc5f..90e1c7d48b 100644 --- a/src/nvim/msgpack_rpc/channel.h +++ b/src/nvim/msgpack_rpc/channel.h @@ -15,7 +15,7 @@ /// HACK: os/input.c drains this queue immediately before blocking for input. /// Events on this queue are async-safe, but they need the resolved state /// of os_inchar(), so they are processed "just-in-time". -MultiQueue *ch_before_blocking_events; +EXTERN MultiQueue *ch_before_blocking_events INIT(= NULL); #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/option.c b/src/nvim/option.c index 3ccc67eb14..1b56d88d9f 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -2204,10 +2204,10 @@ static void didset_options2(void) (void)opt_strings_flags(p_cb, p_cb_values, &cb_flags, true); // Parse default for 'fillchars'. - (void)set_chars_option(curwin, &curwin->w_p_fcs); + (void)set_chars_option(curwin, &curwin->w_p_fcs, true); // Parse default for 'listchars'. - (void)set_chars_option(curwin, &curwin->w_p_lcs); + (void)set_chars_option(curwin, &curwin->w_p_lcs, true); // Parse default for 'wildmode'. check_opt_wim(); @@ -2661,11 +2661,11 @@ did_set_string_option( errmsg = e_invarg; } else { FOR_ALL_TAB_WINDOWS(tp, wp) { - if (set_chars_option(wp, &wp->w_p_lcs) != NULL) { + if (set_chars_option(wp, &wp->w_p_lcs, true) != NULL) { errmsg = (char_u *)_("E834: Conflicts with value of 'listchars'"); goto ambw_end; } - if (set_chars_option(wp, &wp->w_p_fcs) != NULL) { + if (set_chars_option(wp, &wp->w_p_fcs, true) != NULL) { errmsg = (char_u *)_("E835: Conflicts with value of 'fillchars'"); goto ambw_end; } @@ -2866,10 +2866,26 @@ ambw_end: } s = skip_to_option_part(s); } - } else if (varp == &curwin->w_p_lcs) { // 'listchars' - errmsg = set_chars_option(curwin, varp); - } else if (varp == &curwin->w_p_fcs) { // 'fillchars' - errmsg = set_chars_option(curwin, varp); + } else if (varp == &p_lcs) { // 'listchars' + errmsg = set_chars_option(curwin, varp, false); + if (!errmsg) { + FOR_ALL_TAB_WINDOWS(tp, wp) { + set_chars_option(wp, &wp->w_p_lcs, true); + } + } + redraw_all_later(NOT_VALID); + } else if (varp == &curwin->w_p_lcs) { // local 'listchars' + errmsg = set_chars_option(curwin, varp, true); + } else if (varp == &p_fcs) { // 'fillchars' + errmsg = set_chars_option(curwin, varp, false); + if (!errmsg) { + FOR_ALL_TAB_WINDOWS(tp, wp) { + set_chars_option(wp, &wp->w_p_fcs, true); + } + } + redraw_all_later(NOT_VALID); + } else if (varp == &curwin->w_p_fcs) { // local 'fillchars' + errmsg = set_chars_option(curwin, varp, true); } else if (varp == &p_cedit) { // 'cedit' errmsg = check_cedit(); } else if (varp == &p_vfile) { // 'verbosefile' @@ -3499,7 +3515,7 @@ skip: /// /// @param varp either &curwin->w_p_lcs or &curwin->w_p_fcs /// @return error message, NULL if it's OK. -static char_u *set_chars_option(win_T *wp, char_u **varp) +static char_u *set_chars_option(win_T *wp, char_u **varp, bool set) { int round, i, len, entries; char_u *p, *s; @@ -3534,12 +3550,18 @@ static char_u *set_chars_option(win_T *wp, char_u **varp) { &wp->w_p_lcs_chars.conceal, "conceal", NUL }, }; - if (varp == &wp->w_p_lcs) { + if (varp == &p_lcs || varp == &wp->w_p_lcs) { tab = lcs_tab; entries = ARRAY_SIZE(lcs_tab); + if (varp == &wp->w_p_lcs && wp->w_p_lcs[0] == NUL) { + varp = &p_lcs; + } } else { tab = fcs_tab; entries = ARRAY_SIZE(fcs_tab); + if (varp == &wp->w_p_fcs && wp->w_p_fcs[0] == NUL) { + varp = &p_fcs; + } if (*p_ambw == 'd') { // XXX: If ambiwidth=double then "|" and "·" take 2 columns, which is // forbidden (TUI limitation?). Set old defaults. @@ -3552,7 +3574,7 @@ static char_u *set_chars_option(win_T *wp, char_u **varp) } // first round: check for valid value, second round: assign values - for (round = 0; round <= 1; round++) { + for (round = 0; round <= set ? 1 : 0; round++) { if (round > 0) { // After checking that the value is valid: set defaults for (i = 0; i < entries; i++) { @@ -3560,7 +3582,7 @@ static char_u *set_chars_option(win_T *wp, char_u **varp) *(tab[i].cp) = tab[i].def; } } - if (varp == &wp->w_p_lcs) { + if (varp == &p_lcs || varp == &wp->w_p_lcs) { wp->w_p_lcs_chars.tab1 = NUL; wp->w_p_lcs_chars.tab3 = NUL; } @@ -4306,6 +4328,10 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, if (value < 0) { errmsg = e_positive; } + } else if (pp == &p_wd) { + if (value < 0) { + errmsg = e_positive; + } } // Don't change the value and return early if validation failed. @@ -5556,6 +5582,16 @@ void unset_global_local_option(char *name, void *from) case PV_MENC: clear_string_option(&buf->b_p_menc); break; + case PV_LCS: + clear_string_option(&((win_T *)from)->w_p_lcs); + set_chars_option((win_T *)from, &((win_T *)from)->w_p_lcs, true); + redraw_win_later((win_T *)from, NOT_VALID); + break; + case PV_FCS: + clear_string_option(&((win_T *)from)->w_p_fcs); + set_chars_option((win_T *)from, &((win_T *)from)->w_p_fcs, true); + redraw_win_later((win_T *)from, NOT_VALID); + break; } } @@ -5591,6 +5627,8 @@ static char_u *get_varp_scope(vimoption_T *p, int opt_flags) case PV_LW: return (char_u *)&(curbuf->b_p_lw); case PV_BKC: return (char_u *)&(curbuf->b_p_bkc); case PV_MENC: return (char_u *)&(curbuf->b_p_menc); + case PV_FCS: return (char_u *)&(curwin->w_p_fcs); + case PV_LCS: return (char_u *)&(curwin->w_p_lcs); } return NULL; // "cannot happen" } @@ -5649,6 +5687,10 @@ static char_u *get_varp(vimoption_T *p) ? (char_u *)&(curbuf->b_p_lw) : p->var; case PV_MENC: return *curbuf->b_p_menc != NUL ? (char_u *)&(curbuf->b_p_menc) : p->var; + case PV_FCS: return *curwin->w_p_fcs != NUL + ? (char_u *)&(curwin->w_p_fcs) : p->var; + case PV_LCS: return *curwin->w_p_lcs != NUL + ? (char_u *)&(curwin->w_p_lcs) : p->var; case PV_ARAB: return (char_u *)&(curwin->w_p_arab); case PV_LIST: return (char_u *)&(curwin->w_p_list); @@ -5745,8 +5787,6 @@ static char_u *get_varp(vimoption_T *p) case PV_KMAP: return (char_u *)&(curbuf->b_p_keymap); case PV_SCL: return (char_u *)&(curwin->w_p_scl); case PV_WINHL: return (char_u *)&(curwin->w_p_winhl); - case PV_FCS: return (char_u *)&(curwin->w_p_fcs); - case PV_LCS: return (char_u *)&(curwin->w_p_lcs); case PV_WINBL: return (char_u *)&(curwin->w_p_winbl); default: IEMSG(_("E356: get_varp ERROR")); } @@ -5888,8 +5928,8 @@ void didset_window_options(win_T *wp) { check_colorcolumn(wp); briopt_check(wp); - set_chars_option(wp, &wp->w_p_fcs); - set_chars_option(wp, &wp->w_p_lcs); + set_chars_option(wp, &wp->w_p_fcs, true); + set_chars_option(wp, &wp->w_p_lcs, true); parse_winhl_opt(wp); // sets w_hl_needs_update also for w_p_winbl wp->w_grid.blending = wp->w_p_winbl > 0; } diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 08df495250..042f909307 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -484,6 +484,7 @@ EXTERN long p_linespace; // 'linespace' EXTERN char_u *p_lispwords; // 'lispwords' EXTERN long p_ls; // 'laststatus' EXTERN long p_stal; // 'showtabline' +EXTERN char_u *p_lcs; // 'listchars' EXTERN int p_lz; // 'lazyredraw' EXTERN int p_lpl; // 'loadplugins' @@ -518,10 +519,18 @@ EXTERN long p_pyx; // 'pyxversion' EXTERN char_u *p_rdb; // 'redrawdebug' EXTERN unsigned rdb_flags; # ifdef IN_OPTION_C -static char *(p_rdb_values[]) = { "compositor", "nothrottle", NULL }; +static char *(p_rdb_values[]) = { + "compositor", + "nothrottle", + "invalid", + "nodelta", + NULL +}; # endif # define RDB_COMPOSITOR 0x001 # define RDB_NOTHROTTLE 0x002 +# define RDB_INVALID 0x004 +# define RDB_NODELTA 0x008 EXTERN long p_rdt; // 'redrawtime' EXTERN int p_remap; // 'remap' @@ -644,6 +653,7 @@ EXTERN long p_ul; ///< 'undolevels' EXTERN long p_ur; ///< 'undoreload' EXTERN long p_uc; ///< 'updatecount' EXTERN long p_ut; ///< 'updatetime' +EXTERN char_u *p_fcs; ///< 'fillchar' EXTERN char_u *p_shada; ///< 'shada' EXTERN char *p_shadafile; ///< 'shadafile' EXTERN char_u *p_vdir; ///< 'viewdir' diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 52e788944b..fc1ac91229 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -804,11 +804,12 @@ return { }, { full_name='fillchars', abbreviation='fcs', - type='string', list='onecomma', scope={'window'}, + type='string', list='onecomma', scope={'global', 'window'}, deny_duplicates=true, vi_def=true, alloced=true, redraw={'current_window'}, + varname='p_fcs', defaults={if_true={vi=''}} }, { @@ -1420,11 +1421,12 @@ return { }, { full_name='listchars', abbreviation='lcs', - type='string', list='onecomma', scope={'window'}, + type='string', list='onecomma', scope={'global', 'window'}, deny_duplicates=true, vim=true, alloced=true, redraw={'current_window'}, + varname='p_lcs', defaults={if_true={vi="eol:$", vim="tab:> ,trail:-,nbsp:+"}} }, { diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index f5dbf0694e..54fdd7961c 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -135,7 +135,16 @@ int os_setenv(const char *name, const char *value, int overwrite) } #endif uv_mutex_lock(&mutex); - int r = uv_os_setenv(name, value); + int r; +#ifdef WIN32 + // libintl uses getenv() for LC_ALL/LANG/etc., so we must use _putenv_s(). + if (striequal(name, "LC_ALL") || striequal(name, "LANGUAGE") + || striequal(name, "LANG") || striequal(name, "LC_MESSAGES")) { + r = _putenv_s(name, value); // NOLINT + assert(r == 0); + } +#endif + r = uv_os_setenv(name, value); assert(r != UV_EINVAL); // Destroy the old map item. Do this AFTER uv_os_setenv(), because `value` // could be a previous os_getenv() result. diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c index ba6226ef9d..112de9fed8 100644 --- a/src/nvim/os/signal.c +++ b/src/nvim/os/signal.c @@ -50,22 +50,13 @@ void signal_init(void) signal_watcher_init(&main_loop, &shup, NULL); signal_watcher_init(&main_loop, &squit, NULL); signal_watcher_init(&main_loop, &sterm, NULL); -#ifdef SIGPIPE - signal_watcher_start(&spipe, on_signal, SIGPIPE); -#endif - signal_watcher_start(&shup, on_signal, SIGHUP); -#ifdef SIGQUIT - signal_watcher_start(&squit, on_signal, SIGQUIT); -#endif - signal_watcher_start(&sterm, on_signal, SIGTERM); #ifdef SIGPWR signal_watcher_init(&main_loop, &spwr, NULL); - signal_watcher_start(&spwr, on_signal, SIGPWR); #endif #ifdef SIGUSR1 signal_watcher_init(&main_loop, &susr1, NULL); - signal_watcher_start(&susr1, on_signal, SIGUSR1); #endif + signal_start(); } void signal_teardown(void) @@ -83,11 +74,33 @@ void signal_teardown(void) #endif } +void signal_start(void) +{ +#ifdef SIGPIPE + signal_watcher_start(&spipe, on_signal, SIGPIPE); +#endif + signal_watcher_start(&shup, on_signal, SIGHUP); +#ifdef SIGQUIT + signal_watcher_start(&squit, on_signal, SIGQUIT); +#endif + signal_watcher_start(&sterm, on_signal, SIGTERM); +#ifdef SIGPWR + signal_watcher_start(&spwr, on_signal, SIGPWR); +#endif +#ifdef SIGUSR1 + signal_watcher_start(&susr1, on_signal, SIGUSR1); +#endif +} + void signal_stop(void) { +#ifdef SIGPIPE signal_watcher_stop(&spipe); +#endif signal_watcher_stop(&shup); +#ifdef SIGQUIT signal_watcher_stop(&squit); +#endif signal_watcher_stop(&sterm); #ifdef SIGPWR signal_watcher_stop(&spwr); diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 301f72fa79..38674a7a1f 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -4022,10 +4022,10 @@ static void vgr_display_fname(char_u *fname) static buf_T *vgr_load_dummy_buf(char_u *fname, char_u *dirname_start, char_u *dirname_now) { - char_u *save_ei = NULL; - // Don't do Filetype autocommands to avoid loading syntax and // indent scripts, a great speed improvement. + char_u *save_ei = au_event_disable(",Filetype"); + long save_mls = p_mls; p_mls = 0; diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 5bcd2c808d..4b4b2c416f 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -347,6 +347,17 @@ int update_screen(int type) grid_clear_line(&default_grid, default_grid.line_offset[i], Columns, false); } + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (wp->w_floating) { + continue; + } + if (W_ENDROW(wp) > valid) { + wp->w_redr_type = MAX(wp->w_redr_type, NOT_VALID); + } + if (W_ENDROW(wp) + wp->w_status_height > valid) { + wp->w_redr_status = true; + } + } } msg_grid_set_pos(Rows-p_ch, false); msg_grid_invalid = false; @@ -577,11 +588,14 @@ void conceal_check_cursor_line(void) /// Whether cursorline is drawn in a special way /// /// If true, both old and new cursorline will need -/// need to be redrawn when moving cursor within windows. +/// to be redrawn when moving cursor within windows. +/// TODO(bfredl): VIsual_active shouldn't be needed, but is used to fix a glitch +/// caused by scrolling. bool win_cursorline_standout(const win_T *wp) FUNC_ATTR_NONNULL_ALL { - return wp->w_p_cul || (wp->w_p_cole > 0 && !conceal_cursor_line(wp)); + return wp->w_p_cul + || (wp->w_p_cole > 0 && (VIsual_active || !conceal_cursor_line(wp))); } /* @@ -871,7 +885,7 @@ static void win_update(win_T *wp) if (wp->w_lines[0].wl_lnum != wp->w_topline) i += diff_check_fill(wp, wp->w_lines[0].wl_lnum) - wp->w_old_topfill; - if (i < wp->w_grid.Rows - 2) { // less than a screen off + if (i != 0 && i < wp->w_grid.Rows - 2) { // less than a screen off // Try to insert the correct number of lines. // If not the last window, delete the lines at the bottom. // win_ins_lines may fail when the terminal can't do it. @@ -2403,10 +2417,10 @@ win_line ( filler_todo = filler_lines; // Cursor line highlighting for 'cursorline' in the current window. - if (wp->w_p_cul && lnum == wp->w_cursor.lnum) { + if (lnum == wp->w_cursor.lnum) { // Do not show the cursor line when Visual mode is active, because it's // not clear what is selected then. - if (!(wp == curwin && VIsual_active)) { + if (wp->w_p_cul && !(wp == curwin && VIsual_active)) { int cul_attr = win_hl_attr(wp, HLF_CUL); HlAttrs ae = syn_attr2entry(cul_attr); @@ -3756,14 +3770,12 @@ win_line ( char_attr = hl_combine_attr(char_attr, extra_attr); } - /* - * Handle the case where we are in column 0 but not on the first - * character of the line and the user wants us to show us a - * special character (via 'listchars' option "precedes:". - */ + // Handle the case where we are in column 0 but not on the first + // character of the line and the user wants us to show us a + // special character (via 'listchars' option "precedes:". if (lcs_prec_todo != NUL && wp->w_p_list - && (wp->w_p_wrap ? wp->w_skipcol > 0 : wp->w_leftcol > 0) + && (wp->w_p_wrap ? (wp->w_skipcol > 0 && row == 0) : wp->w_leftcol > 0) && filler_todo <= 0 && draw_state > WL_NR && c != NUL) { @@ -4007,7 +4019,7 @@ win_line ( break; } - ++vcol; + vcol += cells; } } @@ -4015,10 +4027,13 @@ win_line ( if (wp->w_buffer->terminal) { // terminal buffers may need to highlight beyond the end of the // logical line - while (col < grid->Columns) { + int n = wp->w_p_rl ? -1 : 1; + while (col >= 0 && col < grid->Columns) { schar_from_ascii(linebuf_char[off], ' '); - linebuf_attr[off++] = term_attrs[vcol++]; - col++; + linebuf_attr[off] = term_attrs[vcol]; + off += n; + vcol += n; + col += n; } } grid_put_linebuf(grid, row, 0, col, grid->Columns, wp->w_p_rl, wp, @@ -4353,7 +4368,7 @@ static int grid_char_needs_redraw(ScreenGrid *grid, int off_from, int off_to, || (line_off2cells(linebuf_char, off_from, off_from + cols) > 1 && schar_cmp(linebuf_char[off_from + 1], grid->chars[off_to + 1]))) - || p_wd < 0)); + || rdb_flags & RDB_NODELTA)); } /// Move one buffered line to the window grid, but only the characters that @@ -5103,6 +5118,8 @@ win_redr_custom ( win_T *ewp; int p_crb_save; + ScreenGrid *grid = &default_grid; + /* There is a tiny chance that this gets called recursively: When * redrawing a status line triggers redrawing the ruler or tabline. * Avoid trouble by not allowing recursion. */ @@ -5142,10 +5159,11 @@ win_redr_custom ( } maxwidth = wp->w_width - col; if (!wp->w_status_height) { + grid = &msg_grid_adj; row = Rows - 1; maxwidth--; // writing in last column may cause scrolling fillchar = ' '; - attr = 0; + attr = HL_ATTR(HLF_MSG); } use_sandbox = was_set_insecurely((char_u *)"rulerformat", 0); @@ -5195,13 +5213,13 @@ win_redr_custom ( /* * Draw each snippet with the specified highlighting. */ - grid_puts_line_start(&default_grid, row); + grid_puts_line_start(grid, row); curattr = attr; p = buf; for (n = 0; hltab[n].start != NULL; n++) { int textlen = (int)(hltab[n].start - p); - grid_puts_len(&default_grid, p, textlen, row, col, curattr); + grid_puts_len(grid, p, textlen, row, col, curattr); col += vim_strnsize(p, textlen); p = hltab[n].start; @@ -5215,7 +5233,7 @@ win_redr_custom ( curattr = highlight_user[hltab[n].userhl - 1]; } // Make sure to use an empty string instead of p, if p is beyond buf + len. - grid_puts(&default_grid, p >= buf + len ? (char_u *)"" : p, row, col, + grid_puts(grid, p >= buf + len ? (char_u *)"" : p, row, col, curattr); grid_puts_line_flush(false); @@ -7060,7 +7078,7 @@ static void win_redr_ruler(win_T *wp, int always) } else { row = Rows - 1; fillchar = ' '; - attr = 0; + attr = HL_ATTR(HLF_MSG); width = Columns; off = 0; } diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 91f3da1793..a311e103a5 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -1508,11 +1508,11 @@ line_read_in: * compute the first offset. */ if (state == TS_BINARY) { - // Get the tag file size. - if ((filesize = vim_lseek(fileno(fp), (off_T)0L, SEEK_END)) <= 0) { + if (vim_fseek(fp, 0, SEEK_END) != 0) { state = TS_LINEAR; } else { - vim_lseek(fileno(fp), (off_T)0L, SEEK_SET); + filesize = vim_ftell(fp); + vim_fseek(fp, 0, SEEK_SET); /* Calculate the first read offset in the file. Start * the search in the middle of the file. */ diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 8fcc8bf0a5..7609006906 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -220,8 +220,6 @@ Terminal *terminal_open(TerminalOptions opts) rv->sb_size = (size_t)curbuf->b_p_scbk; rv->sb_buffer = xmalloc(sizeof(ScrollbackLine *) * rv->sb_size); - vterm_state_set_bold_highbright(state, true); - // Configure the color palette. Try to get the color from: // // - b:terminal_color_{NUM} diff --git a/src/nvim/testdir/test_display.vim b/src/nvim/testdir/test_display.vim index 5feb59eef1..1c2f5a05ff 100644 --- a/src/nvim/testdir/test_display.vim +++ b/src/nvim/testdir/test_display.vim @@ -69,3 +69,59 @@ func! Test_display_foldtext_mbyte() set foldtext& fillchars& foldmethod& fdc& bw! endfunc + +func Test_display_listchars_precedes() + set fillchars+=vert:\| + call NewWindow(10, 10) + " Need a physical line that wraps over the complete + " window size + call append(0, repeat('aaa aaa aa ', 10)) + call append(1, repeat(['bbb bbb bbb bbb'], 2)) + " remove blank trailing line + $d + set list nowrap + call cursor(1, 1) + " move to end of line and scroll 2 characters back + norm! $2zh + let lines=ScreenLines([1,4], winwidth(0)+1) + let expect = [ + \ " aaa aa $ |", + \ "$ |", + \ "$ |", + \ "~ |", + \ ] + call assert_equal(expect, lines) + set list listchars+=precedes:< nowrap + call cursor(1, 1) + " move to end of line and scroll 2 characters back + norm! $2zh + let lines = ScreenLines([1,4], winwidth(0)+1) + let expect = [ + \ "write_loop, &data->output_handle.pipe, 0); uv_pipe_open(&data->output_handle.pipe, data->out_fd); } + flush_buf(ui); } static void terminfo_stop(UI *ui) @@ -1231,7 +1233,9 @@ static void suspend_event(void **argv) tui_terminal_stop(ui); data->cont_received = false; stream_set_blocking(input_global_fd(), true); // normalize stream (#2598) + signal_stop(); kill(0, SIGTSTP); + signal_start(); while (!data->cont_received) { // poll the event loop until SIGCONT is received loop_poll_events(data->loop, -1); @@ -1598,6 +1602,12 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, unibi_set_if_empty(ut, unibi_set_lr_margin, "\x1b[%i%p1%d;%p2%ds"); unibi_set_if_empty(ut, unibi_set_left_margin_parm, "\x1b[%i%p1%ds"); unibi_set_if_empty(ut, unibi_set_right_margin_parm, "\x1b[%i;%p2%ds"); + } else { + // Fix things advertised via TERM=xterm, for non-xterm. + if (unibi_get_str(ut, unibi_set_lr_margin)) { + ILOG("Disabling smglr with TERM=xterm for non-xterm."); + unibi_set_str(ut, unibi_set_lr_margin, NULL); + } } #ifdef WIN32 diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c index 20ffc1b88e..7d3ecfa0b8 100644 --- a/src/nvim/ui_compositor.c +++ b/src/nvim/ui_compositor.c @@ -425,6 +425,15 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, flags = flags & ~kLineFlagWrap; } + for (int i = skipstart; i < (endcol-skipend)-startcol; i++) { + if (attrbuf[i] < 0) { + if (rdb_flags & RDB_INVALID) { + abort(); + } else { + attrbuf[i] = 0; + } + } + } ui_composed_call_raw_line(1, row, startcol+skipstart, endcol-skipend, endcol-skipend, 0, flags, (const schar_T *)linebuf+skipstart, @@ -535,6 +544,11 @@ static void ui_comp_raw_line(UI *ui, Integer grid, Integer row, } else { compose_debug(row, row+1, startcol, endcol, dbghl_normal, false); compose_debug(row, row+1, endcol, clearcol, dbghl_clear, true); +#ifndef NDEBUG + for (int i = 0; i < endcol-startcol; i++) { + assert(attrs[i] >= 0); + } +#endif ui_composed_call_raw_line(1, row, startcol, endcol, clearcol, clearattr, flags, chunk, attrs); } diff --git a/src/nvim/viml/parser/expressions.h b/src/nvim/viml/parser/expressions.h index 23e172da75..838a742271 100644 --- a/src/nvim/viml/parser/expressions.h +++ b/src/nvim/viml/parser/expressions.h @@ -326,7 +326,7 @@ struct expr_ast_node { } data; }; -enum { +enum ExprParserFlags { /// Allow multiple expressions in a row: e.g. for :echo /// /// Parser will still parse only one of them though. @@ -345,7 +345,7 @@ enum { // viml_expressions_parser.c, nvim_parse_expression() flags parsing // alongside with its documentation and flag sets in check_parsing() // function in expressions parser functional and unit tests. -} ExprParserFlags; +}; /// AST error definition typedef struct { diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index cc10d36a10..96822803bf 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -277,6 +277,24 @@ describe('startup', function() [4] = {bold = true, foreground = Screen.colors.Blue1}, }}) end) + + it('fixed hang issue with --headless (#11386)', function() + local expected = '' + local period = 100 + for i = 1, period - 1 do + expected = expected .. i .. '\r\n' + end + expected = expected .. period + eq( + expected, + -- FIXME(codehex): We should really set a timeout for the system function. + -- If this test fails, there will be a waiting input state. + funcs.system({nvim_prog, '-u', 'NONE', '-c', + 'for i in range(1, 100) | echo i | endfor | quit', + '--headless' + }) + ) + end) end) describe('sysinit', function() diff --git a/test/functional/fixtures/api_level_6.mpack b/test/functional/fixtures/api_level_6.mpack index b348f86beb..1c939d5931 100644 Binary files a/test/functional/fixtures/api_level_6.mpack and b/test/functional/fixtures/api_level_6.mpack differ diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index b29161e34c..bd36bac062 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -38,7 +38,7 @@ module.nvim_prog = ( module.nvim_set = ( 'set shortmess+=IS background=light noswapfile noautoindent' ..' laststatus=1 undodir=. directory=. viewdir=. backupdir=.' - ..' belloff= wildoptions-=pum noshowcmd noruler nomore') + ..' belloff= wildoptions-=pum noshowcmd noruler nomore redrawdebug=invalid') module.nvim_argv = { module.nvim_prog, '-u', 'NONE', '-i', 'NONE', '--cmd', module.nvim_set, '--embed'} diff --git a/test/functional/options/chars_spec.lua b/test/functional/options/chars_spec.lua index 1330c29e61..edca6fec63 100644 --- a/test/functional/options/chars_spec.lua +++ b/test/functional/options/chars_spec.lua @@ -71,15 +71,28 @@ describe("'fillchars'", function() shouldfail('eob:xy') -- two ascii chars shouldfail('eob:\255', 'eob:') -- invalid UTF-8 end) - it('is local to window', function() - clear() - screen = Screen.new(50, 5) - screen:attach() + it('has global value', function() + screen:try_resize(50, 5) insert("foo\nbar") command('set laststatus=0') command('1,2fold') command('vsplit') command('set fillchars=fold:x') + screen:expect([[ + ^+-- 2 lines: fooxxxxxxxx│+-- 2 lines: fooxxxxxxx| + ~ │~ | + ~ │~ | + ~ │~ | + | + ]]) + end) + it('has local window value', function() + screen:try_resize(50, 5) + insert("foo\nbar") + command('set laststatus=0') + command('1,2fold') + command('vsplit') + command('setl fillchars=fold:x') screen:expect([[ ^+-- 2 lines: fooxxxxxxxx│+-- 2 lines: foo·······| ~ │~ | @@ -100,16 +113,25 @@ describe("'listchars'", function() screen:attach() end) - after_each(function() - screen:detach() - end) - - it('is local to window', function() + it('has global value', function() feed('i') - command('set laststatus=0') - command('set list listchars=tab:<->') + command('set list laststatus=0') command('vsplit') - command('set listchars&') + command('set listchars=tab:<->') + screen:expect([[ + <------><------>^<------> │<------><------><------>| + ~ │~ | + ~ │~ | + ~ │~ | + | + ]]) + end) + it('has value local to window', function() + feed('i') + command('set list laststatus=0') + command('setl listchars=tab:<->') + command('vsplit') + command('setl listchars<') screen:expect([[ > > ^> │<------><------><------>| ~ │~ | diff --git a/test/functional/provider/nodejs_spec.lua b/test/functional/provider/nodejs_spec.lua index 07a00f8a8c..c4979994be 100644 --- a/test/functional/provider/nodejs_spec.lua +++ b/test/functional/provider/nodejs_spec.lua @@ -7,11 +7,8 @@ local eval = helpers.eval local retry = helpers.retry do - clear() - if missing_provider('node') then - pending("Missing nodejs host, or nodejs version is too old.", function()end) - return - end + pending("nodejs test times out on 0.4.x branch", function() end) + return end before_each(function() diff --git a/test/functional/provider/python3_spec.lua b/test/functional/provider/python3_spec.lua index b319d5e948..d254edc7d5 100644 --- a/test/functional/provider/python3_spec.lua +++ b/test/functional/provider/python3_spec.lua @@ -10,13 +10,14 @@ local pcall_err = helpers.pcall_err do clear() - if missing_provider('python3') then + local reason = missing_provider('python3') + if reason then it(':python3 reports E319 if provider is missing', function() local expected = [[Vim%(py3.*%):E319: No "python3" provider found.*]] matches(expected, pcall_err(command, 'py3 print("foo")')) matches(expected, pcall_err(command, 'py3file foo')) end) - pending('Python 3 (or the pynvim module) is broken/missing', function() end) + pending(string.format('Python 3 (or the pynvim module) is broken/missing (%s)', reason), function() end) return end end diff --git a/test/functional/provider/python_spec.lua b/test/functional/provider/python_spec.lua index 986f10b2e9..d60d8d1001 100644 --- a/test/functional/provider/python_spec.lua +++ b/test/functional/provider/python_spec.lua @@ -18,13 +18,14 @@ local pcall_err = helpers.pcall_err do clear() - if missing_provider('python') then + local reason = missing_provider('python') + if reason then it(':python reports E319 if provider is missing', function() local expected = [[Vim%(py.*%):E319: No "python" provider found.*]] matches(expected, pcall_err(command, 'py print("foo")')) matches(expected, pcall_err(command, 'pyfile foo')) end) - pending('Python 2 (or the pynvim module) is broken/missing', function() end) + pending(string.format('Python 2 (or the pynvim module) is broken/missing (%s)', reason), function() end) return end end diff --git a/test/functional/provider/ruby_spec.lua b/test/functional/provider/ruby_spec.lua index d20adde2ef..bb7d23ede6 100644 --- a/test/functional/provider/ruby_spec.lua +++ b/test/functional/provider/ruby_spec.lua @@ -18,13 +18,14 @@ local pcall_err = helpers.pcall_err do clear() - if missing_provider('ruby') then + local reason = missing_provider('ruby') + if reason then it(':ruby reports E319 if provider is missing', function() local expected = [[Vim%(ruby.*%):E319: No "ruby" provider found.*]] matches(expected, pcall_err(command, 'ruby puts "foo"')) matches(expected, pcall_err(command, 'rubyfile foo')) end) - pending("Missing neovim RubyGem.", function() end) + pending(string.format('Missing neovim RubyGem (%s)', reason), function() end) return end end diff --git a/test/functional/terminal/buffer_spec.lua b/test/functional/terminal/buffer_spec.lua index 1763574bf9..c6ea0c0e6b 100644 --- a/test/functional/terminal/buffer_spec.lua +++ b/test/functional/terminal/buffer_spec.lua @@ -5,6 +5,7 @@ local wait = helpers.wait local eval, feed_command, source = helpers.eval, helpers.feed_command, helpers.source local eq, neq = helpers.eq, helpers.neq local write_file = helpers.write_file +local command= helpers.command describe(':terminal buffer', function() local screen @@ -224,6 +225,22 @@ describe(':terminal buffer', function() neq('terminal', eval('&buftype')) end) end) + + it('it works with set rightleft #11438', function() + local columns = eval('&columns') + feed(string.rep('a', columns)) + command('set rightleft') + screen:expect([[ + ydaer ytt| + {1:a}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + | + | + | + | + {3:-- TERMINAL --} | + ]]) + command('bdelete!') + end) end) describe('No heap-buffer-overflow when using', function() diff --git a/test/functional/terminal/helpers.lua b/test/functional/terminal/helpers.lua index f6cab6bd1e..d909888613 100644 --- a/test/functional/terminal/helpers.lua +++ b/test/functional/terminal/helpers.lua @@ -52,7 +52,7 @@ local function screen_setup(extra_rows, command, cols, opts) [3] = {bold = true}, [4] = {foreground = 12}, [5] = {bold = true, reverse = true}, - [6] = {background = 11}, + -- 6 was a duplicate item [7] = {foreground = 130}, [8] = {foreground = 15, background = 1}, -- error message [9] = {foreground = 4}, diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index c55093cb0f..37c50af398 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -306,6 +306,49 @@ describe('TUI', function() expect_child_buf_lines({''}) end) + it('paste: select-mode', function() + feed_data('ithis is line 1\nthis is line 2\nline 3 is here\n\027') + wait_for_mode('n') + screen:expect{grid=[[ + this is line 1 | + this is line 2 | + line 3 is here | + {1: } | + {5:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]} + -- Select-mode. Use to move down. + feed_data('gg04lgh\14\14') + wait_for_mode('s') + feed_data('\027[200~') + feed_data('just paste it™') + feed_data('\027[201~') + screen:expect{grid=[[ + thisjust paste it™{1:3} is here | + | + {4:~ }| + {4:~ }| + {5:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]} + -- Undo. + feed_data('u') + expect_child_buf_lines{ + 'this is line 1', + 'this is line 2', + 'line 3 is here', + '', + } + -- Redo. + feed_data('\18') -- + expect_child_buf_lines{ + 'thisjust paste it™3 is here', + '', + } + end) + it('paste: terminal mode', function() feed_data(':set statusline=^^^^^^^\n') feed_data(':terminal '..nvim_dir..'/tty-test\n') @@ -539,7 +582,7 @@ describe('TUI', function() | {4:~ }| {5: }| - {8:paste: Error executing lua: vim.lua:197: Vim:E21: }| + {8:paste: Error executing lua: vim.lua:200: Vim:E21: }| {8:Cannot make changes, 'modifiable' is off} | {10:Press ENTER or type command to continue}{1: } | {3:-- TERMINAL --} | @@ -578,6 +621,23 @@ describe('TUI', function() ]]) end) + it('paste: less-than sign in cmdline #11088', function() + local expected = '<' + feed_data(':') + wait_for_mode('c') + -- "bracketed paste" + feed_data('\027[200~'..expected..'\027[201~') + screen:expect{grid=[[ + | + {4:~ }| + {4:~ }| + {4:~ }| + {5:[No Name] }| + :<{1: } | + {3:-- TERMINAL --} | + ]]} + end) + it('paste: big burst of input', function() feed_data(':set ruler\n') local t = {} diff --git a/test/functional/ui/bufhl_spec.lua b/test/functional/ui/bufhl_spec.lua index bcccef84b6..d8ca947645 100644 --- a/test/functional/ui/bufhl_spec.lua +++ b/test/functional/ui/bufhl_spec.lua @@ -31,6 +31,9 @@ describe('Buffer highlighting', function() [14] = {background = Screen.colors.Gray90}, [15] = {background = Screen.colors.Gray90, bold = true, foreground = Screen.colors.Brown}, [16] = {foreground = Screen.colors.Magenta, background = Screen.colors.Gray90}, + [17] = {foreground = Screen.colors.Magenta, background = Screen.colors.LightRed}, + [18] = {background = Screen.colors.LightRed}, + [19] = {foreground = Screen.colors.Blue1, background = Screen.colors.LightRed}, }) end) @@ -516,6 +519,32 @@ describe('Buffer highlighting', function() | ]]) end) + + it('works with color column', function() + eq(-1, set_virtual_text(-1, 3, {{"暗x事", "Comment"}}, {})) + screen:expect{grid=[[ + ^1 + 2 {3:=}{2: 3} | + 3 + {11:ERROR:} invalid syntax | + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5| + , 5, 5, 5, 5, 5, 5, Lorem ipsum dolor s| + x = 4 {12:暗x事} | + {1:~ }| + {1:~ }| + | + ]]} + + command("set colorcolumn=9") + screen:expect{grid=[[ + ^1 + 2 {3:=}{2: }{17:3} | + 3 + {11:ERROR:} invalid syntax | + 5, 5, 5,{18: }5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5| + , 5, 5, 5, 5, 5, 5, Lorem ipsum dolor s| + x = 4 {12:暗}{19:x}{12:事} | + {1:~ }| + {1:~ }| + | + ]]} + end) end) it('and virtual text use the same namespace counter', function() diff --git a/test/functional/ui/diff_spec.lua b/test/functional/ui/diff_spec.lua index 8eb2bbf779..572ed5c695 100644 --- a/test/functional/ui/diff_spec.lua +++ b/test/functional/ui/diff_spec.lua @@ -3,6 +3,8 @@ local Screen = require('test.functional.ui.screen') local feed = helpers.feed local clear = helpers.clear +local command = helpers.command +local insert = helpers.insert local write_file = helpers.write_file describe('Diff mode screen', function() @@ -957,3 +959,75 @@ int main(int argc, char **argv) end) end) end) + +it('win_update redraws lines properly', function() + local screen + clear() + screen = Screen.new(30, 10) + screen:attach() + screen:set_default_attr_ids({ + [1] = {bold = true, foreground = Screen.colors.Blue1}, + [2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red}, + [3] = {background = Screen.colors.Red, foreground = Screen.colors.Grey100, special = Screen.colors.Yellow}, + [4] = {bold = true, foreground = Screen.colors.SeaGreen4}, + [5] = {special = Screen.colors.Yellow}, + [6] = {special = Screen.colors.Yellow, bold = true, foreground = Screen.colors.SeaGreen4}, + [7] = {foreground = Screen.colors.Grey0, background = Screen.colors.Grey100}, + [8] = {foreground = Screen.colors.Gray90, background = Screen.colors.Grey100}, + [9] = {foreground = tonumber('0x00000c'), background = Screen.colors.Grey100}, + [10] = {background = Screen.colors.Grey100, bold = true, foreground = tonumber('0xe5e5ff')}, + [11] = {background = Screen.colors.Grey100, bold = true, foreground = tonumber('0x2b8452')}, + [12] = {bold = true, reverse = true}, + [13] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.WebGray}, + [14] = {reverse = true}, + [15] = {background = Screen.colors.LightBlue}, + [16] = {background = Screen.colors.LightCyan1, bold = true, foreground = Screen.colors.Blue1}, + [17] = {bold = true, background = Screen.colors.Red}, + [18] = {background = Screen.colors.LightMagenta}, + }) + + insert([[ + 1 + + + 2 + 1a + ]]) + command("vnew") + insert([[ + 2 + 2a + 2b + ]]) + command("windo diffthis") + command("windo 1") + screen:expect{grid=[[ + {13: }{16:-------}{14:│}{13: }{15:^1 }| + {13: }{16:-------}{14:│}{13: }{15: }| + {13: }{16:-------}{14:│}{13: }{15: }| + {13: }2 {14:│}{13: }2 | + {13: }{17:2}{18:a }{14:│}{13: }{17:1}{18:a }| + {13: }{15:2b }{14:│}{13: }{16:------------------}| + {13: } {14:│}{13: } | + {1:~ }{14:│}{1:~ }| + {14:') + feed('') + feed('') + feed('') + feed('') + screen:expect{grid=[[ + {13: }{16:-------}{14:│}{13: }{15:1 }| + {13: }{16:-------}{14:│}{13: }{15: }| + {13: }{16:-------}{14:│}{13: }{15:^ }| + {13: }2 {14:│}{13: }2 | + {13: }{17:2}{18:a }{14:│}{13: }{17:1}{18:a }| + {13: }{15:2b }{14:│}{13: }{16:------------------}| + {13: } {14:│}{13: } | + {1:~ }{14:│}{1:~ }| + {14:') + screen:expect([[ + {0:<}aaaaaaaaaaaaaaaaaaa| + aaaaaaaaaaaaaaaaaaaa| + aaaaaaaaaaaaaaaaaaaa| + aaaaaaaaa^a{0:¬} | + | + ]]) + feed('0') + screen:expect([[ + ^aaaaaaaaaaaaaaaaaaaa| + aaaaaaaaaaaaaaaaaaaa| + aaaaaaaaaaaaaaaaaaaa| + aaaaaaaaaaaaaaaaaaaa| + | + ]]) + end) + it("'listchar' in visual mode", function() screen:set_default_attr_ids({ [1] = {background=Screen.colors.Grey90}, diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua index 875e4092a6..9f71b11fb6 100644 --- a/test/functional/ui/messages_spec.lua +++ b/test/functional/ui/messages_spec.lua @@ -810,6 +810,9 @@ describe('ui/builtin messages', function() [4] = {bold = true, foreground = Screen.colors.SeaGreen4}, [5] = {foreground = Screen.colors.Blue1}, [6] = {bold = true, foreground = Screen.colors.Magenta}, + [7] = {background = Screen.colors.Grey20}, + [8] = {reverse = true}, + [9] = {background = Screen.colors.LightRed} }) end) @@ -902,6 +905,151 @@ vimComment xxx match /\s"[^\-:.%#=*].*$/ms=s+1,lc=1 excludenl contains=@vim meths.command_output('syntax list vimComment')) -- luacheck: pop end) + + it('supports ruler with laststatus=0', function() + command("set ruler laststatus=0") + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + 0,0-1 All | + ]]} + + command("hi MsgArea guibg=#333333") + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {7: 0,0-1 All }| + ]]} + + command("set rulerformat=%15(%c%V\\ %p%%%)") + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {7: 0,0-1 100% }| + ]]} + end) + + it('supports echo with CRLF line separators', function() + feed(':echo "line 1\\r\\nline 2"') + screen:expect{grid=[[ + | + {1:~ }| + {1:~ }| + {3: }| + line 1 | + line 2 | + {4:Press ENTER or type command to continue}^ | + ]]} + + feed(':echo "abc\\rz"') + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + zbc | + ]]} + end) + + it('redraws NOT_VALID correctly after message', function() + -- edge case: only one window was set NOT_VALID. Orginal report + -- used :make, but fake it using one command to set the current + -- window NOT_VALID and another to show a long message. + screen:try_resize(80, 7) + command("set more") + feed(':new') + screen:expect{grid=[[ + | + {1:~ }| + {8:[No Name] }| + ^ | + {1:~ }| + {3:[No Name] }| + :new | + ]]} + + feed(':set colorcolumn=10 | digraphs') + screen:expect{grid=[[ + :set colorcolumn=10 | digraphs | + NU {5:^@} 10 SH {5:^A} 1 SX {5:^B} 2 EX {5:^C} 3 ET {5:^D} 4 EQ {5:^E} 5 | + AK {5:^F} 6 BL {5:^G} 7 BS {5:^H} 8 HT {5:^I} 9 LF {5:^@} 10 VT {5:^K} 11 | + FF {5:^L} 12 CR {5:^M} 13 SO {5:^N} 14 SI {5:^O} 15 DL {5:^P} 16 D1 {5:^Q} 17 | + D2 {5:^R} 18 D3 {5:^S} 19 D4 {5:^T} 20 NK {5:^U} 21 SY {5:^V} 22 EB {5:^W} 23 | + CN {5:^X} 24 EM {5:^Y} 25 SB {5:^Z} 26 EC {5:^[} 27 FS {5:^\} 28 GS {5:^]} 29 | + {4:-- More --}^ | + ]]} + + feed('q') + screen:expect{grid=[[ + | + {1:~ }| + {8:[No Name] }| + ^ {9: } | + {1:~ }| + {3:[No Name] }| + | + ]]} + + -- edge case: just covers statusline + feed(':set colorcolumn=5 | lua error("x\\n\\nx")') + screen:expect{grid=[[ + | + {1:~ }| + {3: }| + {2:E5105: Error while calling lua chunk: [string ""]:1: x} | + | + {2:x} | + {4:Press ENTER or type command to continue}^ | + ]]} + + feed('') + screen:expect{grid=[[ + | + {1:~ }| + {8:[No Name] }| + ^ {9: } | + {1:~ }| + {3:[No Name] }| + | + ]]} + + -- edge case: just covers lowest window line + feed(':set colorcolumn=5 | lua error("x\\n\\n\\nx")') + screen:expect{grid=[[ + | + {3: }| + {2:E5105: Error while calling lua chunk: [string ""]:1: x} | + | + | + {2:x} | + {4:Press ENTER or type command to continue}^ | + ]]} + + feed('') + screen:expect{grid=[[ + | + {1:~ }| + {8:[No Name] }| + ^ {9: } | + {1:~ }| + {3:[No Name] }| + | + ]]} + end) end) describe('ui/ext_messages', function() @@ -1089,7 +1237,7 @@ aliquip ex ea commodo consequat.]]) it('can be quit', function() screen:try_resize(25,5) - feed(':echon join(map(range(0, &lines*2), "v:val"), "\\n")') + feed(':echon join(map(range(0, &lines*10), "v:val"), "\\n")') screen:expect{grid=[[ 0 | 1 | diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua index 3bd6b81ff1..42a31b2c75 100644 --- a/test/functional/ui/mouse_spec.lua +++ b/test/functional/ui/mouse_spec.lua @@ -12,7 +12,10 @@ describe('ui/mouse/input', function() clear() meths.set_option('mouse', 'a') meths.set_option('list', true) - meths.set_option('listchars', 'eol:$') + -- NB: this is weird, but mostly irrelevant to the test + -- So I didn't bother to change it + command('set listchars=eol:$') + command('setl listchars=nbsp:x') screen = Screen.new(25, 5) screen:attach() screen:set_default_attr_ids({ @@ -814,7 +817,8 @@ describe('ui/mouse/input', function() feed_command('set concealcursor=ni') feed_command('set nowrap') - feed_command('set shiftwidth=2 tabstop=4 list listchars=tab:>-') + feed_command('set shiftwidth=2 tabstop=4 list') + feed_command('setl listchars=tab:>-') feed_command('syntax match NonText "\\*" conceal') feed_command('syntax match NonText "cats" conceal cchar=X') feed_command('syntax match NonText "x" conceal cchar=>') diff --git a/test/functional/ui/multibyte_spec.lua b/test/functional/ui/multibyte_spec.lua index 3e63353ad2..2ce879fd85 100644 --- a/test/functional/ui/multibyte_spec.lua +++ b/test/functional/ui/multibyte_spec.lua @@ -127,6 +127,10 @@ describe('multibyte rendering: statusline', function() before_each(function() clear() screen = Screen.new(40, 4) + screen:set_default_attr_ids({ + [1] = {bold = true, foreground = Screen.colors.Blue1}, + [2] = {bold = true, reverse = true}, + }) screen:attach() command('set laststatus=2') end) @@ -139,8 +143,8 @@ describe('multibyte rendering: statusline', function() command('set statusline=你好') screen:expect([[ ^ | - ~ | - 你好 | + {1:~ }| + {2:你好 }| | ]]) end) @@ -148,8 +152,8 @@ describe('multibyte rendering: statusline', function() command('set statusline=abc') screen:expect([[ ^ | - ~ | - abc | + {1:~ }| + {2:abc }| | ]]) end) @@ -157,8 +161,8 @@ describe('multibyte rendering: statusline', function() command('set statusline=Ÿ') screen:expect([[ ^ | - ~ | - <9f> | + {1:~ }| + {2:<9f> }| | ]]) end) @@ -167,8 +171,8 @@ describe('multibyte rendering: statusline', function() -- o + U+1DF0 + U+20EF + U+0338 + U+20D0 + U+20E7 + U+20DD screen:expect([[ ^ | - ~ | - o̸⃯ᷰ⃐⃧⃝ | + {1:~ }| + {2:o̸⃯ᷰ⃐⃧⃝ }| | ]]) end) @@ -177,9 +181,19 @@ describe('multibyte rendering: statusline', function() -- U+9F + U+1DF0 + U+20EF + U+0338 + U+20D0 + U+20E7 + U+20DD screen:expect([[ ^ | - ~ | - <9f><1df0><20ef><0338><20d0><20e7><20dd>| + {1:~ }| + {2:<9f><1df0><20ef><0338><20d0><20e7><20dd>}| | ]]) end) + + it('hidden group %( %) does not cause invalid unicode', function() + command("let &statusline = '%#StatColorHi2#%(✓%#StatColorHi2#%) Q≡'") + screen:expect{grid=[[ + ^ | + {1:~ }| + {2: Q≡ }| + | + ]]} + end) end) diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua index ae2136f451..37eb550835 100644 --- a/test/functional/ui/popupmenu_spec.lua +++ b/test/functional/ui/popupmenu_spec.lua @@ -516,6 +516,7 @@ describe('ui/ext_popupmenu', function() {1:~ }| :sign ^ | ]]) + eq(0, funcs.wildmenumode()) feed('') screen:expect{grid=[[ @@ -530,6 +531,7 @@ describe('ui/ext_popupmenu', function() {1:~ }| :sign define^ | ]], popupmenu={items=wild_expected, pos=0, anchor={1, 9, 6}}} + eq(1, funcs.wildmenumode()) feed('') screen:expect{grid=[[ @@ -589,6 +591,7 @@ describe('ui/ext_popupmenu', function() :sign unplace^ | ]], popupmenu={items=wild_expected, pos=5, anchor={1, 9, 6}}} feed('') + eq(0, funcs.wildmenumode()) -- check positioning with multibyte char in pattern command("e långfile1") diff --git a/test/functional/ui/syntax_conceal_spec.lua b/test/functional/ui/syntax_conceal_spec.lua index 00e94ef94b..566d183f11 100644 --- a/test/functional/ui/syntax_conceal_spec.lua +++ b/test/functional/ui/syntax_conceal_spec.lua @@ -1,6 +1,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local clear, feed, command = helpers.clear, helpers.feed, helpers.command +local eq = helpers.eq local insert = helpers.insert describe('Screen', function() @@ -17,6 +18,7 @@ describe('Screen', function() [3] = {reverse = true}, [4] = {bold = true}, [5] = {background = Screen.colors.Yellow}, + [6] = {background = Screen.colors.LightGrey}, } ) end) @@ -823,5 +825,96 @@ describe('Screen', function() ]]) end) end) + + it('redraws properly with concealcursor in visual mode', function() + command('set concealcursor=v conceallevel=2') + + feed('10Ofoo barf bar barf eggs') + feed(':3o aggV') + screen:expect{grid=[[ + ^f{6:oo }{1:b}{6: bar }{1:b}{6: eggs} | + foo {1:b} bar {1:b} eggs | + foo {1:b} bar {1:b} eggs | + a | + foo {1:b} bar {1:b} eggs | + foo {1:b} bar {1:b} eggs | + foo {1:b} bar {1:b} eggs | + foo {1:b} bar {1:b} eggs | + foo {1:b} bar {1:b} eggs | + {4:-- VISUAL LINE --} | + ]]} + feed(string.rep('j', 15)) + screen:expect{grid=[[ + {6:foo }{1:b}{6: bar }{1:b}{6: eggs} | + {6:foo }{1:b}{6: bar }{1:b}{6: eggs} | + {6:foo }{1:b}{6: bar }{1:b}{6: eggs} | + {6:foo }{1:b}{6: bar }{1:b}{6: eggs} | + {6:foo }{1:b}{6: bar }{1:b}{6: eggs} | + {6:foo }{1:b}{6: bar }{1:b}{6: eggs} | + {6:foo }{1:b}{6: bar }{1:b}{6: eggs} | + {6:foo }{1:b}{6: bar }{1:b}{6: eggs} | + ^f{6:oo }{1:b}{6: bar }{1:b}{6: eggs} | + {4:-- VISUAL LINE --} | + ]]} + feed(string.rep('k', 15)) + screen:expect{grid=[[ + ^f{6:oo }{1:b}{6: bar }{1:b}{6: eggs} | + foo {1:b} bar {1:b} eggs | + foo {1:b} bar {1:b} eggs | + a | + foo {1:b} bar {1:b} eggs | + foo {1:b} bar {1:b} eggs | + foo {1:b} bar {1:b} eggs | + foo {1:b} bar {1:b} eggs | + foo {1:b} bar {1:b} eggs | + {4:-- VISUAL LINE --} | + ]]} + end) + end) + + it('redraws not too much with conceallevel=1', function() + command('set conceallevel=1') + command('set redrawdebug+=nodelta') + + insert([[ + aaa + bbb + ccc + ]]) + screen:expect{grid=[[ + aaa | + bbb | + ccc | + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]} + + -- XXX: hack to get notifications, and check only a single line is + -- updated. Could use next_msg() also. + local orig_handle_grid_line = screen._handle_grid_line + local grid_lines = {} + function screen._handle_grid_line(self, grid, row, col, items) + table.insert(grid_lines, {row, col, items}) + orig_handle_grid_line(self, grid, row, col, items) + end + feed('k') + screen:expect{grid=[[ + aaa | + bbb | + ^ccc | + | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]} + eq(grid_lines, {{2, 0, {{'c', 0, 3}}}}) end) end) diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt index 43cad34aae..4cd18efcf3 100644 --- a/third-party/CMakeLists.txt +++ b/third-party/CMakeLists.txt @@ -160,8 +160,8 @@ set(UNIBILIUM_SHA256 29815283c654277ef77a3adcc8840db79ddbb20a0f0b0c8f648bd8cd49a set(LIBTERMKEY_URL http://www.leonerd.org.uk/code/libtermkey/libtermkey-0.21.1.tar.gz) set(LIBTERMKEY_SHA256 cecbf737f35d18f433c8d7864f63c0f878af41f8bd0255a3ebb16010dc044d5f) -set(LIBVTERM_URL https://github.com/neovim/libvterm/archive/4a5fa43e0dbc0db4fe67d40d788d60852864df9e.tar.gz) -set(LIBVTERM_SHA256 49b3cf2dcb988b887671b1011cfeac98ff81bb5c23fb4ac34b91a59524992935) +set(LIBVTERM_URL https://github.com/neovim/libvterm/archive/7c72294d84ce20da4c27362dbd7fa4b08cfc91da.tar.gz) +set(LIBVTERM_SHA256 f30c4d43e0c6db3e0912daf7188d98fbf6ee88f97589d72f6f304e5db48826a8) set(LUV_VERSION 1.30.0-0) set(LUV_URL https://github.com/luvit/luv/archive/${LUV_VERSION}.tar.gz) @@ -177,8 +177,8 @@ set(GPERF_SHA256 588546b945bba4b70b6a3a616e80b4ab466e3f33024a352fc2198112cdbb3ae set(WINTOOLS_URL https://github.com/neovim/deps/raw/2f9acbecf06365c10baa3c0087f34a54c9c6f949/opt/win32tools.zip) set(WINTOOLS_SHA256 8bfce7e3a365721a027ce842f2ec1cf878f1726233c215c05964aac07300798c) -set(WINGUI_URL https://github.com/equalsraf/neovim-qt/releases/download/v0.2.14/neovim-qt.zip) -set(WINGUI_SHA256 dfcb1f7d25d4907dc1d4f20edd71ff9eb4762196225106bec01274dd668fb04c) +set(WINGUI_URL https://github.com/equalsraf/neovim-qt/releases/download/v0.2.15/neovim-qt.zip) +set(WINGUI_SHA256 b519ecb80b60522d25043f2d076a55656f5fbe5adf7f7e2943e5d8b161043987) set(WIN32YANK_X86_URL https://github.com/equalsraf/win32yank/releases/download/v0.0.4/win32yank-x86.zip) set(WIN32YANK_X86_SHA256 62f34e5a46c5d4a7b3f3b512e1ff7b77fedd432f42581cbe825233a996eed62c) diff --git a/third-party/cmake/BuildLibtermkey.cmake b/third-party/cmake/BuildLibtermkey.cmake index b2332ed65a..10e98fbab3 100644 --- a/third-party/cmake/BuildLibtermkey.cmake +++ b/third-party/cmake/BuildLibtermkey.cmake @@ -48,6 +48,7 @@ ExternalProject_Add(libtermkey PREFIX=${DEPS_INSTALL_DIR} PKG_CONFIG_PATH=${DEPS_LIB_DIR}/pkgconfig CFLAGS=-fPIC + LDFLAGS+=-static ${DEFAULT_MAKE_CFLAGS} install) endif() diff --git a/third-party/cmake/BuildLibvterm.cmake b/third-party/cmake/BuildLibvterm.cmake index e4649986af..61c1c90fa6 100644 --- a/third-party/cmake/BuildLibvterm.cmake +++ b/third-party/cmake/BuildLibvterm.cmake @@ -58,6 +58,7 @@ else() set(LIBVTERM_INSTALL_COMMAND ${MAKE_PRG} CC=${DEPS_C_COMPILER} PREFIX=${DEPS_INSTALL_DIR} CFLAGS=-fPIC + LDFLAGS+=-static ${DEFAULT_MAKE_CFLAGS} install) endif() diff --git a/third-party/cmake/BuildUnibilium.cmake b/third-party/cmake/BuildUnibilium.cmake index e9deeb4987..74c1cbddb0 100644 --- a/third-party/cmake/BuildUnibilium.cmake +++ b/third-party/cmake/BuildUnibilium.cmake @@ -40,6 +40,7 @@ else() BUILD_COMMAND ${MAKE_PRG} CC=${DEPS_C_COMPILER} PREFIX=${DEPS_INSTALL_DIR} CFLAGS=-fPIC + LDFLAGS+=-static INSTALL_COMMAND ${MAKE_PRG} PREFIX=${DEPS_INSTALL_DIR} install) endif() diff --git a/third-party/cmake/GettextCMakeLists.txt b/third-party/cmake/GettextCMakeLists.txt index 5a6253df3b..e76059c9dd 100644 --- a/third-party/cmake/GettextCMakeLists.txt +++ b/third-party/cmake/GettextCMakeLists.txt @@ -8,6 +8,9 @@ endmacro() file(READ gettext-runtime/config.h.in CONFIG_CONTENT) string(REPLACE "#undef HAVE_GETCWD" "#define HAVE_GETCWD 1" CONFIG_CONTENT ${CONFIG_CONTENT}) +string(REPLACE "#undef HAVE_ICONV_H" "#define HAVE_ICONV_H 1" CONFIG_CONTENT ${CONFIG_CONTENT}) +string(REPLACE "#undef HAVE_ICONV" "#define HAVE_ICONV 1" CONFIG_CONTENT ${CONFIG_CONTENT}) +string(REPLACE "#undef ICONV_CONST" "#define ICONV_CONST const" CONFIG_CONTENT ${CONFIG_CONTENT}) string(REPLACE "#undef uintmax_t" " #if _WIN64 # define intmax_t long long @@ -54,7 +57,8 @@ PREFIX_LIST_ITEMS(libintl_SOURCES "gettext-runtime/intl/") add_library(libintl ${libintl_SOURCES}) set_property(TARGET libintl APPEND PROPERTY INCLUDE_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR}/gettext-runtime - ${CMAKE_CURRENT_BINARY_DIR}/gettext-runtime/intl) + ${CMAKE_CURRENT_BINARY_DIR}/gettext-runtime/intl + ${LIBICONV_INCLUDE_DIRS}) set_property(TARGET libintl APPEND PROPERTY COMPILE_DEFINITIONS BUILDING_LIBINTL IN_LIBINTL @@ -72,6 +76,8 @@ string(REPLACE "#undef ENDIANNESS" "#define ENDIANNESS 0" CONFIG_CONTENT ${CONFI string(REPLACE "#undef GNULIB_FWRITEERROR" "#define GNULIB_FWRITEERROR 1" CONFIG_CONTENT ${CONFIG_CONTENT}) string(REPLACE "#undef HAVE_DECL_STRERROR_R" "#define HAVE_DECL_STRERROR_R 0" CONFIG_CONTENT ${CONFIG_CONTENT}) string(REPLACE "#undef HAVE_DUP2" "#define HAVE_DUP2 1" CONFIG_CONTENT ${CONFIG_CONTENT}) +string(REPLACE "#undef HAVE_ICONV_H" "#define HAVE_ICONV_H 1" CONFIG_CONTENT ${CONFIG_CONTENT}) +string(REPLACE "#undef HAVE_ICONV" "#define HAVE_ICONV 1" CONFIG_CONTENT ${CONFIG_CONTENT}) string(REPLACE "#undef HAVE_LIBUNISTRING" "#define HAVE_LIBUNISTRING 1" CONFIG_CONTENT ${CONFIG_CONTENT}) string(REPLACE "#undef HAVE_STDINT_H_WITH_UINTMAX" "#define HAVE_STDINT_H_WITH_UINTMAX 1" CONFIG_CONTENT ${CONFIG_CONTENT}) string(REPLACE "#undef HAVE_STDINT_H" "#define HAVE_STDINT_H 1" CONFIG_CONTENT ${CONFIG_CONTENT}) @@ -126,7 +132,7 @@ set(GLIBC_SOURCE mbchar.c mbslen.c mbsstr.c mbswidth.c obstack.c ostream.c html-ostream.c fd-ostream.c styled-ostream.c progname.c html-styled-ostream.c printf-args.c printf-parse.c propername.c quotearg.c rawmemchr.c safe-read.c safe-write.c - stpcpy.c stpncpy.c strchrnul.c striconveh.c striconveha.c strnlen1.c + stpcpy.c stpncpy.c strchrnul.c striconv.c striconveh.c striconveha.c strnlen1.c term-ostream.c term-styled-ostream.c tparm.c trim.c gcd.c gl_linkedhash_list.c uniconv/u8-conv-from-enc.c unictype/ctype_space.c unilbrk/lbrktables.c unilbrk/u8-possible-linebreaks.c unilbrk/u8-width-linebreaks.c @@ -135,7 +141,7 @@ set(GLIBC_SOURCE unistr/u8-mbtouc-unsafe-aux.c unistr/u8-mbtouc-unsafe.c unistr/u8-mbtouc.c unistr/u8-mbtoucr.c unistr/u8-prev.c unistr/u8-uctomb-aux.c unistr/u8-uctomb.c uniwidth/width.c vasnprintf.c vasprintf.c wcwidth.c xasprintf.c - xconcat-filename.c xerror.c xmalloc.c xstrdup.c xvasprintf.c glib/ghash.c + xconcat-filename.c xerror.c xmalloc.c xstrdup.c xstriconv.c xstriconveh.c xvasprintf.c glib/ghash.c glib/glist.c glib/gmessages.c glib/gprimes.c glib/gstrfuncs.c glib/gstring.c libcroco/cr-additional-sel.c libcroco/cr-attr-sel.c libcroco/cr-cascade.c libcroco/cr-declaration.c libcroco/cr-doc-handler.c libcroco/cr-enc-handler.c