diff options
Diffstat (limited to 'src')
33 files changed, 592 insertions, 225 deletions
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 35508fde6b..760c95eb5b 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -43,10 +43,10 @@ void remote_ui_disconnect(uint64_t channel_id) return; } UIData *data = ui->data; - // destroy pending screen updates - api_free_array(data->buffer); + api_free_array(data->buffer); // Destroy pending screen updates. pmap_del(uint64_t)(connected_uis, channel_id); xfree(ui->data); + ui->data = NULL; // Flag UI as "stopped". ui_detach_impl(ui); xfree(ui); } diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 6f0c78fde4..e5da5b835b 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -129,7 +129,7 @@ read_buffer( if (read_stdin) { // Set or reset 'modified' before executing autocommands, so that // it can be changed there. - if (!readonlymode && !bufempty()) { + if (!readonlymode && !BUFEMPTY()) { changed(); } else if (retval != FAIL) { unchanged(curbuf, false); @@ -1616,7 +1616,7 @@ buf_T * buflist_new(char_u *ffname, char_u *sfname, linenr_T lnum, int flags) && curbuf != NULL && curbuf->b_ffname == NULL && curbuf->b_nwindows <= 1 - && (curbuf->b_ml.ml_mfp == NULL || bufempty())) { + && (curbuf->b_ml.ml_mfp == NULL || BUFEMPTY())) { buf = curbuf; /* It's like this buffer is deleted. Watch out for autocommands that * change curbuf! If that happens, allocate a new buffer anyway. */ @@ -1872,7 +1872,7 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit) // If 'switchbuf' contains "split", "vsplit" or "newtab" and the // current buffer isn't empty: open new tab or window if (wp == NULL && (swb_flags & (SWB_VSPLIT | SWB_SPLIT | SWB_NEWTAB)) - && !bufempty()) { + && !BUFEMPTY()) { if (swb_flags & SWB_NEWTAB) { tabpage_new(); } else if (win_split(0, (swb_flags & SWB_VSPLIT) ? WSP_VERT : 0) @@ -4462,11 +4462,12 @@ do_arg_all ( last_curwin = curwin; last_curtab = curtab; win_enter(lastwin, false); - /* ":drop all" should re-use an empty window to avoid "--remote-tab" - * leaving an empty tab page when executed locally. */ - if (keep_tabs && bufempty() && curbuf->b_nwindows == 1 - && curbuf->b_ffname == NULL && !curbuf->b_changed) + // ":drop all" should re-use an empty window to avoid "--remote-tab" + // leaving an empty tab page when executed locally. + if (keep_tabs && BUFEMPTY() && curbuf->b_nwindows == 1 + && curbuf->b_ffname == NULL && !curbuf->b_changed) { use_firstwin = TRUE; + } for (i = 0; i < count && i < opened_len && !got_int; ++i) { if (alist == &global_alist && i == global_alist.al_ga.ga_len - 1) diff --git a/src/nvim/diff.c b/src/nvim/diff.c index a32ac645e3..c2ab57a59b 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -941,9 +941,6 @@ void ex_diffpatch(exarg_T *eap) } #endif - // patch probably has written over the screen - redraw_later(CLEAR); - // Delete any .orig or .rej file created. STRCPY(buf, tmp_new); STRCAT(buf, ".orig"); @@ -2270,7 +2267,7 @@ void ex_diffgetput(exarg_T *eap) } } - buf_empty = bufempty(); + buf_empty = BUFEMPTY(); added = 0; for (i = 0; i < count; ++i) { diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 649357c7ea..e67ac7d49f 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -6907,7 +6907,9 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty) if (try_match && *look == keytyped) { return true; } - look++; + if (*look != NUL) { + look++; + } } /* @@ -7473,13 +7475,11 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) int oldState; int cpc[MAX_MCO]; /* composing characters */ - /* - * can't delete anything in an empty file - * can't backup past first character in buffer - * can't backup past starting point unless 'backspace' > 1 - * can backup to a previous line if 'backspace' == 0 - */ - if (bufempty() + // can't delete anything in an empty file + // can't backup past first character in buffer + // can't backup past starting point unless 'backspace' > 1 + // can backup to a previous line if 'backspace' == 0 + if (BUFEMPTY() || (!revins_on && ((curwin->w_cursor.lnum == 1 && curwin->w_cursor.col == 0) || (!can_bs(BS_START) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 07a9e9286d..1d483eee18 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1279,7 +1279,7 @@ varnumber_T call_func_retnr(char_u *func, int argc, char *call_func_retstr(const char *const func, const int argc, const char_u *const *const argv, const bool safe) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC { typval_T rettv; // All arguments are passed as strings, no conversion to number. @@ -10671,6 +10671,9 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) "windows", "winaltkeys", "writebackup", +#if defined(HAVE_WSL) + "wsl", +#endif "nvim", }; @@ -10730,6 +10733,17 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) n = true; } + if (STRICMP(name, "ruby") == 0 && n == true) { + char *rubyhost = call_func_retstr("provider#ruby#Detect", 0, NULL, true); + if (rubyhost) { + if (*rubyhost == NUL) { + // Invalid rubyhost executable. Gem is probably not installed. + n = false; + } + xfree(rubyhost); + } + } + rettv->vval.v_number = n; } diff --git a/src/nvim/event/loop.c b/src/nvim/event/loop.c index 55ef0261d9..d92464f17b 100644 --- a/src/nvim/event/loop.c +++ b/src/nvim/event/loop.c @@ -33,6 +33,9 @@ void loop_init(Loop *loop, void *data) loop->poll_timer.data = xmalloc(sizeof(bool)); // "timeout expired" flag } +/// Processes one `Loop.uv` event (at most). +/// Processes all `Loop.fast_events` events. +/// /// @returns true if `ms` timeout was reached bool loop_poll_events(Loop *loop, int ms) { diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index a2f8994403..ba51f18518 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -1870,8 +1870,7 @@ static int command_line_changed(CommandLineState *s) end_pos = curwin->w_cursor; // shutup gcc 4 } - - // Disable 'hlsearch' highlighting if the pattern matches8.0.1304 + // Disable 'hlsearch' highlighting if the pattern matches // everything. Avoids a flash when typing "foo\|". if (empty_pattern(ccline.cmdbuff)) { SET_NO_HLSEARCH(true); @@ -4109,12 +4108,12 @@ static int showmatches(expand_T *xp, int wildmenu) msg_start(); /* prepare for paging */ } - if (got_int) - got_int = FALSE; /* only int. the completion, not the cmd line */ - else if (wildmenu) - win_redr_status_matches(xp, num_files, files_found, 0, showtail); - else { - /* find the length of the longest file name */ + if (got_int) { + got_int = false; // only int. the completion, not the cmd line + } else if (wildmenu) { + win_redr_status_matches(xp, num_files, files_found, -1, showtail); + } else { + // find the length of the longest file name maxlen = 0; for (i = 0; i < num_files; ++i) { if (!showtail && (xp->xp_context == EXPAND_FILES diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 1f4cd22754..44d74c92cd 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -5088,14 +5088,12 @@ void buf_reload(buf_T *buf, int orig_mode) flags |= READ_KEEP_UNDO; } - /* - * To behave like when a new file is edited (matters for - * BufReadPost autocommands) we first need to delete the current - * buffer contents. But if reading the file fails we should keep - * the old contents. Can't use memory only, the file might be - * too big. Use a hidden buffer to move the buffer contents to. - */ - if (bufempty() || saved == FAIL) { + // To behave like when a new file is edited (matters for + // BufReadPost autocommands) we first need to delete the current + // buffer contents. But if reading the file fails we should keep + // the old contents. Can't use memory only, the file might be + // too big. Use a hidden buffer to move the buffer contents to. + if (BUFEMPTY() || saved == FAIL) { savebuf = NULL; } else { // Allocate a buffer without putting it in the buffer list. @@ -5128,7 +5126,7 @@ void buf_reload(buf_T *buf, int orig_mode) if (savebuf != NULL && bufref_valid(&bufref) && buf == curbuf) { // Put the text back from the save buffer. First // delete any lines that readfile() added. - while (!bufempty()) { + while (!BUFEMPTY()) { if (ml_delete(buf->b_ml.ml_line_count, false) == FAIL) { break; } diff --git a/src/nvim/macros.h b/src/nvim/macros.h index a98c1e05a0..61834c6499 100644 --- a/src/nvim/macros.h +++ b/src/nvim/macros.h @@ -28,15 +28,11 @@ /// @return `s, sizeof(s) - 1` #define S_LEN(s) (s), (sizeof(s) - 1) -/* - * lineempty() - return TRUE if the line is empty - */ -#define lineempty(p) (*ml_get(p) == NUL) +/// LINEEMPTY() - return TRUE if the line is empty +#define LINEEMPTY(p) (*ml_get(p) == NUL) -/* - * bufempty() - return TRUE if the current buffer is empty - */ -#define bufempty() (curbuf->b_ml.ml_line_count == 1 && *ml_get((linenr_T)1) == \ +/// BUFEMPTY() - return TRUE if the current buffer is empty +#define BUFEMPTY() (curbuf->b_ml.ml_line_count == 1 && *ml_get((linenr_T)1) == \ NUL) /* @@ -75,7 +71,7 @@ do { \ if (*p_langmap \ && (condition) \ - && (p_lrm || KeyTyped) \ + && (p_lrm || (vgetc_busy ? typebuf_maplen() == 0 : KeyTyped)) \ && !KeyStuffed \ && (c) >= 0) \ { \ diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index d908a022f1..6f636f643a 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -608,8 +608,7 @@ bool mouse_scroll_horiz(int dir) return leftcol_changed(); } -// Adjust the clicked column position if there are concealed characters -// before the current column. But only when it's absolutely necessary. +/// Adjusts the clicked column position when 'conceallevel' > 0 static int mouse_adjust_click(win_T *wp, int row, int col) { if (!(wp->w_p_cole > 0 && curbuf->b_p_smc > 0 @@ -617,64 +616,102 @@ static int mouse_adjust_click(win_T *wp, int row, int col) return col; } - int end = (colnr_T)STRLEN(ml_get(wp->w_cursor.lnum)); - int vend = getviscol2(end, 0); + // `col` is the position within the current line that is highlighted by the + // cursor without consideration for concealed characters. The current line is + // scanned *up to* `col`, nudging it left or right when concealed characters + // are encountered. + // + // chartabsize() is used to keep track of the virtual column position relative + // to the line's bytes. For example: if col == 9 and the line starts with a + // tab that's 8 columns wide, we would want the cursor to be highlighting the + // second byte, not the ninth. + + linenr_T lnum = wp->w_cursor.lnum; + char_u *line = ml_get(lnum); + char_u *ptr = line; + char_u *ptr_end = line; + char_u *ptr_row_offset = line; // Where we begin adjusting `ptr_end` - if (col >= vend) { - return col; + // Find the offset where scanning should begin. + int offset = wp->w_leftcol; + if (row > 0) { + offset += row * (wp->w_width - win_col_off(wp) - win_col_off2(wp) - + wp->w_leftcol + wp->w_skipcol); } - int i = wp->w_leftcol; + int vcol; + + if (offset) { + // Skip everything up to an offset since nvim takes care of displaying the + // correct portion of the line when horizontally scrolling. + // When 'wrap' is enabled, only the row (of the wrapped line) needs to be + // checked for concealed characters. + vcol = 0; + while (vcol < offset && *ptr != NUL) { + vcol += chartabsize(ptr, vcol); + ptr += utfc_ptr2len(ptr); + } - if (row > 0) { - i += row * (wp->w_width - win_col_off(wp) - win_col_off2(wp) - - wp->w_leftcol) + wp->w_skipcol; + ptr_row_offset = ptr; } - int start_col = i; - int matchid; - int last_matchid; - int bcol = end - (vend - col); + // Align `ptr_end` with `col` + vcol = offset; + ptr_end = ptr_row_offset; + while (vcol < col && *ptr_end != NUL) { + vcol += chartabsize(ptr_end, vcol); + ptr_end += utfc_ptr2len(ptr_end); + } - while (i < bcol) { - matchid = syn_get_concealed_id(wp, wp->w_cursor.lnum, i); + int matchid; + int prev_matchid; + int nudge = 0; + int cwidth = 0; + + vcol = offset; + +#define incr() nudge++; ptr_end += utfc_ptr2len(ptr_end) +#define decr() nudge--; ptr_end -= utfc_ptr2len(ptr_end) + + while (ptr < ptr_end && *ptr != NUL) { + cwidth = chartabsize(ptr, vcol); + vcol += cwidth; + if (cwidth > 1 && *ptr == '\t' && nudge > 0) { + // A tab will "absorb" any previous adjustments. + cwidth = MIN(cwidth, nudge); + while (cwidth > 0) { + decr(); + cwidth--; + } + } + matchid = syn_get_concealed_id(wp, lnum, (colnr_T)(ptr - line)); if (matchid != 0) { if (wp->w_p_cole == 3) { - bcol++; + incr(); } else { - if (row > 0 && i == start_col) { - // Check if the current concealed character is actually part of - // the previous wrapped row's conceal group. - last_matchid = syn_get_concealed_id(wp, wp->w_cursor.lnum, - i - 1); - if (last_matchid == matchid) { - bcol++; - } - } else if (wp->w_p_cole == 1 - || (wp->w_p_cole == 2 - && (lcs_conceal != NUL - || syn_get_sub_char() != NUL))) { + if (!(row > 0 && ptr == ptr_row_offset) + && (wp->w_p_cole == 1 || (wp->w_p_cole == 2 + && (lcs_conceal != NUL + || syn_get_sub_char() != NUL)))) { // At least one placeholder character will be displayed. - bcol--; + decr(); } - last_matchid = matchid; + prev_matchid = matchid; - // Adjust for concealed text that spans more than one character. - do { - i++; - bcol++; - matchid = syn_get_concealed_id(wp, wp->w_cursor.lnum, i); - } while (last_matchid == matchid); + while (prev_matchid == matchid && *ptr != NUL) { + incr(); + ptr += utfc_ptr2len(ptr); + matchid = syn_get_concealed_id(wp, lnum, (colnr_T)(ptr - line)); + } continue; } } - i++; + ptr += utfc_ptr2len(ptr); } - return getviscol2(bcol, 0); + return col + nudge; } - diff --git a/src/nvim/move.c b/src/nvim/move.c index 134ffcd7dc..6548d351a6 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -155,12 +155,11 @@ void update_topline(void) old_topline = curwin->w_topline; old_topfill = curwin->w_topfill; - /* - * If the buffer is empty, always set topline to 1. - */ - if (bufempty()) { /* special case - file is empty */ - if (curwin->w_topline != 1) + // If the buffer is empty, always set topline to 1. + if (BUFEMPTY()) { // special case - file is empty + if (curwin->w_topline != 1) { redraw_later(NOT_VALID); + } curwin->w_topline = 1; curwin->w_botline = 2; curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index ab20be7246..47044a6072 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -5031,26 +5031,21 @@ static void nv_right(cmdarg_T *cap) if ((!PAST_LINE && oneright() == false) || (PAST_LINE && *get_cursor_pos_ptr() == NUL) ) { - /* - * <Space> wraps to next line if 'whichwrap' has 's'. - * 'l' wraps to next line if 'whichwrap' has 'l'. - * CURS_RIGHT wraps to next line if 'whichwrap' has '>'. - */ - if ( ((cap->cmdchar == ' ' - && vim_strchr(p_ww, 's') != NULL) - || (cap->cmdchar == 'l' - && vim_strchr(p_ww, 'l') != NULL) - || (cap->cmdchar == K_RIGHT - && vim_strchr(p_ww, '>') != NULL)) - && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { - /* When deleting we also count the NL as a character. - * Set cap->oap->inclusive when last char in the line is - * included, move to next line after that */ - if ( cap->oap->op_type != OP_NOP - && !cap->oap->inclusive - && !lineempty(curwin->w_cursor.lnum)) + // <Space> wraps to next line if 'whichwrap' has 's'. + // 'l' wraps to next line if 'whichwrap' has 'l'. + // CURS_RIGHT wraps to next line if 'whichwrap' has '>'. + if (((cap->cmdchar == ' ' && vim_strchr(p_ww, 's') != NULL) + || (cap->cmdchar == 'l' && vim_strchr(p_ww, 'l') != NULL) + || (cap->cmdchar == K_RIGHT && vim_strchr(p_ww, '>') != NULL)) + && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { + // When deleting we also count the NL as a character. + // Set cap->oap->inclusive when last char in the line is + // included, move to next line after that + if (cap->oap->op_type != OP_NOP + && !cap->oap->inclusive + && !LINEEMPTY(curwin->w_cursor.lnum)) { cap->oap->inclusive = true; - else { + } else { ++curwin->w_cursor.lnum; curwin->w_cursor.col = 0; curwin->w_cursor.coladd = 0; @@ -5060,12 +5055,14 @@ static void nv_right(cmdarg_T *cap) continue; } if (cap->oap->op_type == OP_NOP) { - /* Only beep and flush if not moved at all */ - if (n == cap->count1) + // Only beep and flush if not moved at all + if (n == cap->count1) { beep_flush(); + } } else { - if (!lineempty(curwin->w_cursor.lnum)) + if (!LINEEMPTY(curwin->w_cursor.lnum)) { cap->oap->inclusive = true; + } } break; } else if (PAST_LINE) { @@ -5123,13 +5120,12 @@ static void nv_left(cmdarg_T *cap) coladvance((colnr_T)MAXCOL); curwin->w_set_curswant = true; - /* When the NL before the first char has to be deleted we - * put the cursor on the NUL after the previous line. - * This is a very special case, be careful! - * Don't adjust op_end now, otherwise it won't work. */ - if ( (cap->oap->op_type == OP_DELETE - || cap->oap->op_type == OP_CHANGE) - && !lineempty(curwin->w_cursor.lnum)) { + // When the NL before the first char has to be deleted we + // put the cursor on the NUL after the previous line. + // This is a very special case, be careful! + // Don't adjust op_end now, otherwise it won't work. + if ((cap->oap->op_type == OP_DELETE || cap->oap->op_type == OP_CHANGE) + && !LINEEMPTY(curwin->w_cursor.lnum)) { char_u *cp = get_cursor_pos_ptr(); if (*cp != NUL) { @@ -6098,10 +6094,11 @@ static void n_swapchar(cmdarg_T *cap) pos_T startpos; int did_change = 0; - if (checkclearopq(cap->oap)) + if (checkclearopq(cap->oap)) { return; + } - if (lineempty(curwin->w_cursor.lnum) && vim_strchr(p_ww, '~') == NULL) { + if (LINEEMPTY(curwin->w_cursor.lnum) && vim_strchr(p_ww, '~') == NULL) { clearopbeep(cap->oap); return; } diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 3a338b1417..665a102253 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -2052,10 +2052,11 @@ void op_insert(oparg_T *oap, long count1) curwin->w_cursor = oap->end; check_cursor_col(); - /* Works just like an 'i'nsert on the next character. */ - if (!lineempty(curwin->w_cursor.lnum) - && oap->start_vcol != oap->end_vcol) + // Works just like an 'i'nsert on the next character. + if (!LINEEMPTY(curwin->w_cursor.lnum) + && oap->start_vcol != oap->end_vcol) { inc_cursor(); + } } } @@ -2180,9 +2181,10 @@ int op_change(oparg_T *oap) } else if (op_delete(oap) == FAIL) return FALSE; - if ((l > curwin->w_cursor.col) && !lineempty(curwin->w_cursor.lnum) - && !virtual_op) + if ((l > curwin->w_cursor.col) && !LINEEMPTY(curwin->w_cursor.lnum) + && !virtual_op) { inc_cursor(); + } // check for still on same line (<CR> in inserted text meaningless) // skip blank lines too @@ -2855,25 +2857,30 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) } } else if (y_type == kMTLineWise) { lnum = curwin->w_cursor.lnum; - /* Correct line number for closed fold. Don't move the cursor yet, - * u_save() uses it. */ - if (dir == BACKWARD) + // Correct line number for closed fold. Don't move the cursor yet, + // u_save() uses it. + if (dir == BACKWARD) { (void)hasFolding(lnum, &lnum, NULL); - else + } else { (void)hasFolding(lnum, NULL, &lnum); - if (dir == FORWARD) - ++lnum; - /* In an empty buffer the empty line is going to be replaced, include - * it in the saved lines. */ - if ((bufempty() ? u_save(0, 2) : u_save(lnum - 1, lnum)) == FAIL) + } + if (dir == FORWARD) { + lnum++; + } + // In an empty buffer the empty line is going to be replaced, include + // it in the saved lines. + if ((BUFEMPTY() ? u_save(0, 2) : u_save(lnum - 1, lnum)) == FAIL) { goto end; - if (dir == FORWARD) + } + if (dir == FORWARD) { curwin->w_cursor.lnum = lnum - 1; - else + } else { curwin->w_cursor.lnum = lnum; - curbuf->b_op_start = curwin->w_cursor; /* for mark_adjust() */ - } else if (u_save_cursor() == FAIL) + } + curbuf->b_op_start = curwin->w_cursor; // for mark_adjust() + } else if (u_save_cursor() == FAIL) { goto end; + } yanklen = (int)STRLEN(y_array[0]); @@ -3997,7 +4004,7 @@ format_lines ( && (do_second_indent || do_number_indent) && prev_is_end_par && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { - if (do_second_indent && !lineempty(curwin->w_cursor.lnum + 1)) { + if (do_second_indent && !LINEEMPTY(curwin->w_cursor.lnum + 1)) { if (leader_len == 0 && next_leader_len == 0) { /* no comment found */ second_indent = diff --git a/src/nvim/option.c b/src/nvim/option.c index fa2e6b169b..aa1f5f3fe7 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -1059,7 +1059,7 @@ void set_init_3(void) xfree(p); } - if (bufempty()) { + if (BUFEMPTY()) { int idx_ffs = findoption_len(S_LEN("ffs")); // Apply the first entry of 'fileformats' to the initial buffer. diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index e32c6e05d2..c205e4b3af 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -189,6 +189,7 @@ static int do_os_system(char **argv, { out_data_decide_throttle(0); // Initialize throttle decider. out_data_ring(NULL, 0); // Initialize output ring-buffer. + bool has_input = (input != NULL && input[0] != '\0'); // the output buffer DynamicBuffer buf = DYNAMIC_BUFFER_INIT; @@ -212,7 +213,7 @@ static int do_os_system(char **argv, MultiQueue *events = multiqueue_new_child(main_loop.events); proc->events = events; proc->argv = argv; - int status = process_spawn(proc, input != NULL, true, true); + int status = process_spawn(proc, has_input, true, true); if (status) { loop_poll_events(&main_loop, 0); // Failed, probably 'shell' is not executable. @@ -231,7 +232,7 @@ static int do_os_system(char **argv, // deal with stream events as fast a possible. It prevents closing the // streams while there's still data in the OS buffer (due to the process // exiting before all data is read). - if (input != NULL) { + if (has_input) { wstream_init(&proc->in, 0); } rstream_init(&proc->out, 0); @@ -240,8 +241,8 @@ static int do_os_system(char **argv, rstream_start(&proc->err, data_cb, &buf); // write the input, if any - if (input) { - WBuffer *input_buffer = wstream_new_buffer((char *) input, len, 1, NULL); + if (has_input) { + WBuffer *input_buffer = wstream_new_buffer((char *)input, len, 1, NULL); if (!wstream_write(&proc->in, input_buffer)) { // couldn't write, stop the process and tell the user about it diff --git a/src/nvim/path.c b/src/nvim/path.c index 09cede8805..21ac064c30 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -2202,7 +2202,13 @@ static int path_get_absolute_path(const char_u *fname, char_u *buf, // expand it if forced or not an absolute path if (force || !path_is_absolute_path(fname)) { - if ((p = vim_strrchr(fname, PATHSEP)) != NULL) { + p = vim_strrchr(fname, '/'); +#ifdef WIN32 + if (p == NULL) { + p = vim_strrchr(fname, '\\'); + } +#endif + if (p != NULL) { // relative to root if (p == fname) { // only one path component diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index 2462975c9b..bf79e4b84f 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -593,7 +593,7 @@ static int pum_set_selected(int n, int repeat) && (curbuf->b_p_bt[2] == 'f') && (curbuf->b_p_bh[0] == 'w')) { // Already a "wipeout" buffer, make it empty. - while (!bufempty()) { + while (!BUFEMPTY()) { ml_delete((linenr_T)1, FALSE); } } else { diff --git a/src/nvim/search.c b/src/nvim/search.c index 9ae5754f80..bbcac45369 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -314,8 +314,7 @@ void free_search_patterns(void) /// It's similar but different from save_search_patterns() and /// restore_search_patterns(), because the search pattern must be restored when /// cancelling incremental searching even if it's called inside user functions. - void -save_last_search_pattern(void) +void save_last_search_pattern(void) { saved_last_search_spat = spats[RE_SEARCH]; if (spats[RE_SEARCH].pat != NULL) { @@ -325,8 +324,7 @@ save_last_search_pattern(void) saved_no_hlsearch = no_hlsearch; } - void -restore_last_search_pattern(void) +void restore_last_search_pattern(void) { xfree(spats[RE_SEARCH].pat); spats[RE_SEARCH] = saved_last_search_spat; @@ -335,8 +333,7 @@ restore_last_search_pattern(void) SET_NO_HLSEARCH(saved_no_hlsearch); } - char_u * -last_search_pattern(void) +char_u *last_search_pattern(void) { return spats[RE_SEARCH].pat; } @@ -2263,10 +2260,11 @@ int findsent(int dir, long count) break; found_dot = TRUE; } - if (decl(&pos) == -1) + if (decl(&pos) == -1) { break; - /* when going forward: Stop in front of empty line */ - if (lineempty(pos.lnum) && dir == FORWARD) { + } + // when going forward: Stop in front of empty line + if (LINEEMPTY(pos.lnum) && dir == FORWARD) { incl(&pos); goto found; } @@ -2570,10 +2568,12 @@ int bck_word(long count, int bigword, int stop) */ while (cls() == 0) { if (curwin->w_cursor.col == 0 - && lineempty(curwin->w_cursor.lnum)) + && LINEEMPTY(curwin->w_cursor.lnum)) { goto finished; - if (dec_cursor() == -1) /* hit start of file, stop here */ + } + if (dec_cursor() == -1) { // hit start of file, stop here return OK; + } } /* @@ -2637,10 +2637,12 @@ int end_word(long count, int bigword, int stop, int empty) */ while (cls() == 0) { if (empty && curwin->w_cursor.col == 0 - && lineempty(curwin->w_cursor.lnum)) + && LINEEMPTY(curwin->w_cursor.lnum)) { goto finished; - if (inc_cursor() == -1) /* hit end of file, stop here */ + } + if (inc_cursor() == -1) { // hit end of file, stop here return FAIL; + } } /* @@ -2693,10 +2695,12 @@ bckend_word ( * Move backward to end of the previous word */ while (cls() == 0) { - if (curwin->w_cursor.col == 0 && lineempty(curwin->w_cursor.lnum)) + if (curwin->w_cursor.col == 0 && LINEEMPTY(curwin->w_cursor.lnum)) { break; - if ((i = dec_cursor()) == -1 || (eol && i == 1)) + } + if ((i = dec_cursor()) == -1 || (eol && i == 1)) { return OK; + } } } return OK; diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 38392509a5..34eb2fdf1b 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -7147,7 +7147,7 @@ void ex_spelldump(exarg_T *eap) set_option_value("spl", dummy, (char *)spl, OPT_LOCAL); xfree(spl); - if (!bufempty()) { + if (!BUFEMPTY()) { return; } diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 05bc6c9d96..40479e05dd 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -881,7 +881,8 @@ static void syn_start_line(void) } next_match_idx = -1; - ++current_line_id; + current_line_id++; + next_seqnr = 1; } /* @@ -1599,6 +1600,7 @@ get_syntax_attr ( current_id = 0; current_trans_id = 0; current_flags = 0; + current_seqnr = 0; return 0; } @@ -2042,6 +2044,7 @@ syn_current_attr ( current_id = 0; current_trans_id = 0; current_flags = 0; + current_seqnr = 0; if (cur_si != NULL) { for (int idx = current_state.ga_len - 1; idx >= 0; --idx) { sip = &CUR_STATE(idx); diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index 2dc42be652..bd9842ea19 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -7,12 +7,11 @@ ifeq ($(OS),Windows_NT) else NVIM_PRG ?= ../../../build/bin/nvim endif -TMPDIR ?= Xtest-tmpdir SCRIPTSOURCE := ../../../runtime export SHELL := sh export NVIM_PRG := $(NVIM_PRG) -export TMPDIR +export TMPDIR := $(abspath ../../../Xtest-tmpdir) SCRIPTS_DEFAULT = \ test14.out \ @@ -53,6 +52,7 @@ NEW_TESTS ?= \ test_diffmode.res \ test_farsi.res \ test_filter_map.res \ + test_findfile.res \ test_fnameescape.res \ test_fold.res \ test_ga.res \ @@ -77,6 +77,7 @@ NEW_TESTS ?= \ test_mksession_utf8.res \ test_nested_function.res \ test_normal.res \ + test_number.res \ test_options.res \ test_profile.res \ test_quickfix.res \ diff --git a/src/nvim/testdir/shared.vim b/src/nvim/testdir/shared.vim index d8e2d1d718..4925b04a82 100644 --- a/src/nvim/testdir/shared.vim +++ b/src/nvim/testdir/shared.vim @@ -9,8 +9,12 @@ func! Fix_truncated_tmpfile(fname) if $TMPDIR ==# '' throw '$TMPDIR is empty' endif - if a:fname !~# $TMPDIR - throw '$TMPDIR not in fname: '.a:fname + let tmpdir_tail = fnamemodify(substitute($TMPDIR, '[\/]\+$', '', 'g'), ':t') + if tmpdir_tail ==# '' + throw 'empty tmpdir_tail' + endif + if a:fname !~# tmpdir_tail + throw printf('$TMPDIR (%s) not in fname: %s', tmpdir_tail, a:fname) endif let last2segments = matchstr(a:fname, '[\/][^\/]\+[\/][^\/]\+$') return $TMPDIR.last2segments diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index 6f572e9b79..6df5aae677 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -12,6 +12,7 @@ source test_expr_utf8.vim source test_feedkeys.vim source test_filter_cmd.vim source test_filter_map.vim +source test_findfile.vim source test_float_func.vim source test_functions.vim source test_ga.vim diff --git a/src/nvim/testdir/test_findfile.vim b/src/nvim/testdir/test_findfile.vim new file mode 100644 index 0000000000..d9a89801ea --- /dev/null +++ b/src/nvim/testdir/test_findfile.vim @@ -0,0 +1,25 @@ +" Test for findfile() +" +func Test_findfile() + new + let cwd=getcwd() + cd .. + + " Tests may be run from a shadow directory, so an extra cd needs to be done to + " get above src/ + if fnamemodify(getcwd(), ':t') != 'src' + cd ../.. + else + cd .. + endif + set ssl + + call assert_equal('src/nvim/testdir/test_findfile.vim', findfile('test_findfile.vim','src/nvim/test*')) + exe "cd" cwd + cd .. + call assert_equal('testdir/test_findfile.vim', findfile('test_findfile.vim','test*')) + call assert_equal('testdir/test_findfile.vim', findfile('test_findfile.vim','testdir')) + + exe "cd" cwd + q! +endfunc diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index c28d76e66f..307f62e17a 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -1075,7 +1075,6 @@ func! Test_normal19_z_spell() if !has("spell") || !has('syntax') return endif - " let $TMPDIR=fnamemodify($TMPDIR, ':.') new call append(0, ['1 good', '2 goood', '3 goood']) set spell spellfile=./Xspellfile.add spelllang=en @@ -1122,7 +1121,6 @@ func! Test_normal19_z_spell() " Test for zG let a=execute('unsilent norm! V$zG') call assert_match("Word '2 goood' added to .*", a) - set shortmess= let fname=matchstr(a, 'to\s\+\zs\f\+$') let fname=Fix_truncated_tmpfile(fname) let cnt=readfile(fname) diff --git a/src/nvim/testdir/test_number.vim b/src/nvim/testdir/test_number.vim new file mode 100644 index 0000000000..59debcea0d --- /dev/null +++ b/src/nvim/testdir/test_number.vim @@ -0,0 +1,254 @@ +" Test for 'number' and 'relativenumber' + +source view_util.vim + +func! s:screen_lines(start, end) abort + return ScreenLines([a:start, a:end], 8) +endfunc + +func! s:compare_lines(expect, actual) + call assert_equal(a:expect, a:actual) +endfunc + +func! s:test_windows(h, w) abort + call NewWindow(a:h, a:w) +endfunc + +func! s:close_windows() abort + call CloseWindow() +endfunc + +func! s:validate_cursor() abort + " update skipcol. + " wincol(): + " f_wincol + " -> validate_cursor + " -> curs_columns + call wincol() +endfunc + +func Test_set_options() + set nu rnu + call assert_equal(1, &nu) + call assert_equal(1, &rnu) + + call s:test_windows(10, 20) + call assert_equal(1, &nu) + call assert_equal(1, &rnu) + call s:close_windows() + + set nu& rnu& +endfunc + +func Test_set_global_and_local() + " setlocal must NOT reset the other global value + set nonu nornu + setglobal nu + setlocal rnu + call assert_equal(1, &g:nu) + + set nonu nornu + setglobal rnu + setlocal nu + call assert_equal(1, &g:rnu) + + " setglobal MUST reset the other global value + set nonu nornu + setglobal nu + setglobal rnu + call assert_equal(1, &g:nu) + + set nonu nornu + setglobal rnu + setglobal nu + call assert_equal(1, &g:rnu) + + " set MUST reset the other global value + set nonu nornu + set nu + set rnu + call assert_equal(1, &g:nu) + + set nonu nornu + set rnu + set nu + call assert_equal(1, &g:rnu) + + set nu& rnu& +endfunc + +func Test_number() + call s:test_windows(10, 20) + call setline(1, ["abcdefghij", "klmnopqrst", "uvwxyzABCD", "EFGHIJKLMN", "OPQRSTUVWX", "YZ"]) + setl number + let lines = s:screen_lines(1, 4) + let expect = [ +\ " 1 abcd", +\ " 2 klmn", +\ " 3 uvwx", +\ " 4 EFGH", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +func Test_relativenumber() + call s:test_windows(10, 20) + call setline(1, ["abcdefghij", "klmnopqrst", "uvwxyzABCD", "EFGHIJKLMN", "OPQRSTUVWX", "YZ"]) + 3 + setl relativenumber + let lines = s:screen_lines(1, 6) + let expect = [ +\ " 2 abcd", +\ " 1 klmn", +\ " 0 uvwx", +\ " 1 EFGH", +\ " 2 OPQR", +\ " 3 YZ ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +func Test_number_with_relativenumber() + call s:test_windows(10, 20) + call setline(1, ["abcdefghij", "klmnopqrst", "uvwxyzABCD", "EFGHIJKLMN", "OPQRSTUVWX", "YZ"]) + 4 + setl number relativenumber + let lines = s:screen_lines(1, 6) + let expect = [ +\ " 3 abcd", +\ " 2 klmn", +\ " 1 uvwx", +\ "4 EFGH", +\ " 1 OPQR", +\ " 2 YZ ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +func Test_number_with_linewrap1() + call s:test_windows(3, 20) + normal! 61ia + setl number wrap + call s:validate_cursor() + let lines = s:screen_lines(1, 3) + let expect = [ +\ "--1 aaaa", +\ " aaaa", +\ " aaaa", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +" Pending: https://groups.google.com/forum/#!topic/vim_dev/tzNKP7EDWYI +func XTest_number_with_linewrap2() + call s:test_windows(3, 20) + normal! 61ia + setl number wrap + call s:validate_cursor() + 0 + call s:validate_cursor() + let lines = s:screen_lines(1, 3) + let expect = [ +\ " 1 aaaa", +\ " aaaa", +\ " aaaa", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +" Pending: https://groups.google.com/forum/#!topic/vim_dev/tzNKP7EDWYI +func XTest_number_with_linewrap3() + call s:test_windows(4, 20) + normal! 81ia + setl number wrap + call s:validate_cursor() + setl nonumber + call s:validate_cursor() + let lines = s:screen_lines(1, 4) + let expect = [ +\ "aaaaaaaa", +\ "aaaaaaaa", +\ "aaaaaaaa", +\ "a ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +func Test_numberwidth() + call s:test_windows(10, 20) + call setline(1, repeat(['aaaa'], 10)) + setl number numberwidth=6 + let lines = s:screen_lines(1, 3) + let expect = [ +\ " 1 aa", +\ " 2 aa", +\ " 3 aa", +\ ] + call s:compare_lines(expect, lines) + + set relativenumber + let lines = s:screen_lines(1, 3) + let expect = [ +\ "1 aa", +\ " 1 aa", +\ " 2 aa", +\ ] + call s:compare_lines(expect, lines) + + set nonumber + let lines = s:screen_lines(1, 3) + let expect = [ +\ " 0 aa", +\ " 1 aa", +\ " 2 aa", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + +func Test_numberwidth_adjusted() + call s:test_windows(10, 20) + call setline(1, repeat(['aaaa'], 10000)) + setl number numberwidth=4 + let lines = s:screen_lines(1, 3) + let expect = [ +\ " 1 aa", +\ " 2 aa", +\ " 3 aa", +\ ] + call s:compare_lines(expect, lines) + + $ + let lines = s:screen_lines(8, 10) + let expect = [ +\ " 9998 aa", +\ " 9999 aa", +\ "10000 aa", +\ ] + call s:compare_lines(expect, lines) + + setl relativenumber + let lines = s:screen_lines(8, 10) + let expect = [ +\ " 2 aa", +\ " 1 aa", +\ "10000 aa", +\ ] + call s:compare_lines(expect, lines) + + setl nonumber + let lines = s:screen_lines(8, 10) + let expect = [ +\ " 2 aaaa", +\ " 1 aaaa", +\ " 0 aaaa", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index d1536b5c43..3c1b0050b5 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -119,6 +119,13 @@ func Check_dir_option(name) call assert_fails("set " . a:name . "=/not.*there", "E474:") endfunc +func Test_cinkeys() + " This used to cause invalid memory access + set cindent cinkeys=0 + norm a + set cindent& cinkeys& +endfunc + func Test_dictionary() call Check_dir_option('dictionary') endfunc @@ -152,8 +159,8 @@ func Test_set_completion() " Expand directories. call feedkeys(":set cdpath=./\<C-A>\<C-B>\"\<CR>", 'tx') - call assert_match(' ./samples/ ', @:) - call assert_notmatch(' ./small.vim ', @:) + call assert_match('./samples/ ', @:) + call assert_notmatch('./small.vim ', @:) " Expand files and directories. call feedkeys(":set tags=./\<C-A>\<C-B>\"\<CR>", 'tx') diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim index 366a6ee1e2..b662279c6d 100644 --- a/src/nvim/testdir/test_syntax.vim +++ b/src/nvim/testdir/test_syntax.vim @@ -356,24 +356,24 @@ func Test_conceal() set conceallevel=0 call assert_equal('123456 ', ScreenLines(2, 7)[0]) - call assert_equal([[0, ''], [0, ''], [0, ''], [0, ''], [0, ''], [0, '']], map(range(1, 6), 'synconcealed(2, v:val)[0:1]')) + call assert_equal([[0, '', 0], [0, '', 0], [0, '', 0], [0, '', 0], [0, '', 0], [0, '', 0]], map(range(1, 6), 'synconcealed(2, v:val)')) set conceallevel=1 call assert_equal('1X 6 ', ScreenLines(2, 7)[0]) - call assert_equal([[0, ''], [1, 'X'], [1, 'X'], [1, ' '], [1, ' '], [0, '']], map(range(1, 6), 'synconcealed(2, v:val)[0:1]')) + call assert_equal([[0, '', 0], [1, 'X', 1], [1, 'X', 1], [1, ' ', 2], [1, ' ', 2], [0, '', 0]], map(range(1, 6), 'synconcealed(2, v:val)')) set conceallevel=1 set listchars=conceal:Y - call assert_equal([[0, ''], [1, 'X'], [1, 'X'], [1, 'Y'], [1, 'Y'], [0, '']], map(range(1, 6), 'synconcealed(2, v:val)[0:1]')) + call assert_equal([[0, '', 0], [1, 'X', 1], [1, 'X', 1], [1, 'Y', 2], [1, 'Y', 2], [0, '', 0]], map(range(1, 6), 'synconcealed(2, v:val)')) call assert_equal('1XY6 ', ScreenLines(2, 7)[0]) set conceallevel=2 call assert_match('1X6 ', ScreenLines(2, 7)[0]) - call assert_equal([[0, ''], [1, 'X'], [1, 'X'], [1, ''], [1, ''], [0, '']], map(range(1, 6), 'synconcealed(2, v:val)[0:1]')) + call assert_equal([[0, '', 0], [1, 'X', 1], [1, 'X', 1], [1, '', 2], [1, '', 2], [0, '', 0]], map(range(1, 6), 'synconcealed(2, v:val)')) set conceallevel=3 call assert_match('16 ', ScreenLines(2, 7)[0]) - call assert_equal([[0, ''], [1, ''], [1, ''], [1, ''], [1, ''], [0, '']], map(range(1, 6), 'synconcealed(2, v:val)[0:1]')) + call assert_equal([[0, '', 0], [1, '', 1], [1, '', 1], [1, '', 2], [1, '', 2], [0, '', 0]], map(range(1, 6), 'synconcealed(2, v:val)')) syn clear set conceallevel& diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index f3383eb006..70e19e1d93 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -69,7 +69,6 @@ typedef struct { typedef struct { UIBridgeData *bridge; Loop *loop; - bool stop; unibi_var_t params[9]; char buf[OUTBUF_SIZE]; size_t bufpos; @@ -124,7 +123,7 @@ static bool cursor_style_enabled = false; UI *tui_start(void) { - UI *ui = xcalloc(1, sizeof(UI)); + UI *ui = xcalloc(1, sizeof(UI)); // Freed by ui_bridge_stop(). ui->stop = tui_stop; ui->rgb = p_tgc; ui->resize = tui_resize; @@ -324,11 +323,11 @@ static void tui_terminal_stop(UI *ui) static void tui_stop(UI *ui) { tui_terminal_stop(ui); - TUIData *data = ui->data; - data->stop = true; + // Flag UI as "stopped". Needed by tui_scheduler (called from main thread). + ui->data = NULL; } -// Main function of the TUI thread +/// Main function of the TUI thread. static void tui_main(UIBridgeData *bridge, UI *ui) { Loop tui_loop; @@ -349,7 +348,6 @@ static void tui_main(UIBridgeData *bridge, UI *ui) #endif term_input_init(&data->input, &tui_loop); tui_terminal_start(ui); - data->stop = false; // Allow main thread to continue, we are ready to handle UI callbacks. CONTINUE(bridge); @@ -358,17 +356,17 @@ static void tui_main(UIBridgeData *bridge, UI *ui) event_create(show_termcap_event, 1, data->ut)); // "Active" loop: first ~100 ms of startup. - for (size_t ms = 0; ms < 100 && !data->stop;) { + for (size_t ms = 0; ms < 100 && !ui_is_stopped(ui);) { ms += (loop_poll_events(&tui_loop, 20) ? 20 : 1); } - if (!data->stop) { + if (!ui_is_stopped(ui)) { tui_terminal_after_startup(ui); // Tickle `main_loop` with a dummy event, else the initial "focus-gained" // terminal response may not get processed until user hits a key. loop_schedule_deferred(&main_loop, event_create(tui_dummy_event, 0)); } // "Passive" (I/O-driven) loop: TUI thread "main loop". - while (!data->stop) { + while (!ui_is_stopped(ui)) { loop_poll_events(&tui_loop, -1); // tui_loop.events is never processed } @@ -380,16 +378,19 @@ static void tui_main(UIBridgeData *bridge, UI *ui) loop_close(&tui_loop, false); kv_destroy(data->invalid_regions); xfree(data); - xfree(ui); } static void tui_dummy_event(void **argv) { } +/// Handoff point between the main (ui_bridge) thread and the TUI thread. static void tui_scheduler(Event event, void *d) { UI *ui = d; + if (ui_is_stopped(ui)) { + return; // tui_stop was handled, teardown underway. + } TUIData *data = ui->data; loop_schedule(data->loop, event); // `tui_loop` local to tui_main(). } @@ -1812,6 +1813,12 @@ static const char *tui_tk_ti_getstr(const char *name, const char *value, if (value != NULL && strequal(stty_erase, value)) { return stty_erase[0] == DEL ? CTRL_H_STR : DEL_STR; } + } else if (strequal(name, "key_mouse")) { + DLOG("libtermkey:kmous=%s", value); + // If key_mouse is found, libtermkey uses its terminfo driver (driver-ti.c) + // for mouse input, which by accident only supports X10 protocol. + // Force libtermkey to fallback to its CSI driver (driver-csi.c). #7948 + return NULL; } return value; diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 81da88c54a..8aec923538 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -143,6 +143,12 @@ void ui_builtin_stop(void) UI_CALL(stop); } +/// Returns true if UI `ui` is stopped. +bool ui_is_stopped(UI *ui) +{ + return ui->data == NULL; +} + bool ui_rgb_attached(void) { for (size_t i = 0; i < ui_count; i++) { @@ -404,7 +410,7 @@ void ui_start_highlight(int attr_code) { current_attr_code = attr_code; - if (!ui_count) { + if (!ui_active()) { return; } @@ -415,7 +421,7 @@ void ui_stop_highlight(void) { current_attr_code = HL_NORMAL; - if (!ui_count) { + if (!ui_active()) { return; } diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c index 0a69cf0ecb..16dd42ebaa 100644 --- a/src/nvim/ui_bridge.c +++ b/src/nvim/ui_bridge.c @@ -118,11 +118,12 @@ static void ui_bridge_stop(UI *b) if (stopped) { break; } - loop_poll_events(&main_loop, 10); + loop_poll_events(&main_loop, 10); // Process one event. } uv_thread_join(&bridge->ui_thread); uv_mutex_destroy(&bridge->mutex); uv_cond_destroy(&bridge->cond); + xfree(bridge->ui); // Threads joined, now safe to free UI container. #7922 ui_detach_impl(b); xfree(b); } diff --git a/src/nvim/undo.c b/src/nvim/undo.c index b902f82f31..35857510fc 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -2268,12 +2268,14 @@ static void u_undoredo(int undo) curhead->uh_entry = newlist; curhead->uh_flags = new_flags; - if ((old_flags & UH_EMPTYBUF) && bufempty()) + if ((old_flags & UH_EMPTYBUF) && BUFEMPTY()) { curbuf->b_ml.ml_flags |= ML_EMPTY; - if (old_flags & UH_CHANGED) + } + if (old_flags & UH_CHANGED) { changed(); - else + } else { unchanged(curbuf, FALSE); + } /* * restore marks from before undo/redo diff --git a/src/nvim/version.c b/src/nvim/version.c index e627022ea6..c95be072cf 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -823,27 +823,27 @@ static const int included_patches[] = { // 677, // 676, // 675, - // 674, - // 673, - // 672, + 674, + 673, + 672, // 671, // 670, // 669, - // 668, - // 667, - // 666, - // 665, - // 664, + 668, + 667, + 666, + 665, + 664, // 663, - // 662, - // 661, - // 660, - // 659, + 662, + 661, + 660, + 659, 658, 657, - // 656, - // 655, - // 654, + 656, + 655, + 654, // 653, 652, // 651, @@ -898,7 +898,7 @@ static const int included_patches[] = { // 602, 601, // 600, - // 599, + 599, // 598, 597, // 596, @@ -969,7 +969,7 @@ static const int included_patches[] = { // 531, // 530, // 529, - // 528, + 528, // 527, // 526, // 525, @@ -1049,13 +1049,13 @@ static const int included_patches[] = { // 451, // 450, // 449, - // 448, + 448, // 447, // 446, // 445, - // 444, - // 443, - // 442, + 444, + 443, + 442, // 441, // 440, // 439, @@ -1064,14 +1064,14 @@ static const int included_patches[] = { // 436, // 435, // 434, - // 433, + 433, // 432, - // 431, + 431, // 430, // 429, // 428, - // 427, - // 426, + 427, + 426, // 425, // 424, 423, @@ -1138,8 +1138,8 @@ static const int included_patches[] = { // 362, // 361, 360, - // 359, - // 358, + 359, + 358, // 357, // 356, // 355, @@ -1498,7 +1498,6 @@ static const int included_patches[] = { 2, 1, 0, - }; // clang-format on @@ -1722,7 +1721,7 @@ static void version_msg(char *s) /// Show the intro message when not editing a file. void maybe_intro_message(void) { - if (bufempty() + if (BUFEMPTY() && (curbuf->b_fname == NULL) && (firstwin->w_next == NULL) && (vim_strchr(p_shm, SHM_INTRO) == NULL)) { |