diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/api/vim.c | 6 | ||||
-rw-r--r-- | src/nvim/api/window.c | 32 | ||||
-rw-r--r-- | src/nvim/buffer.c | 10 | ||||
-rw-r--r-- | src/nvim/buffer_defs.h | 4 | ||||
-rw-r--r-- | src/nvim/change.c | 12 | ||||
-rw-r--r-- | src/nvim/eval.c | 28 | ||||
-rw-r--r-- | src/nvim/eval.lua | 1 | ||||
-rw-r--r-- | src/nvim/ex_cmds2.c | 4 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 2 | ||||
-rw-r--r-- | src/nvim/fileio.c | 12 | ||||
-rw-r--r-- | src/nvim/memline.c | 2 | ||||
-rw-r--r-- | src/nvim/message.c | 6 | ||||
-rw-r--r-- | src/nvim/move.c | 75 | ||||
-rw-r--r-- | src/nvim/option_defs.h | 15 | ||||
-rw-r--r-- | src/nvim/os/signal.c | 2 | ||||
-rw-r--r-- | src/nvim/quickfix.c | 216 | ||||
-rw-r--r-- | src/nvim/testdir/test_changedtick.vim | 38 | ||||
-rw-r--r-- | src/nvim/testdir/test_cursor_func.vim | 28 | ||||
-rw-r--r-- | src/nvim/testdir/test_quickfix.vim | 32 | ||||
-rw-r--r-- | src/nvim/undo.c | 2 | ||||
-rw-r--r-- | src/nvim/version.c | 406 | ||||
-rw-r--r-- | src/nvim/window.c | 78 |
22 files changed, 669 insertions, 342 deletions
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 2034fea770..27344fc093 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1059,6 +1059,12 @@ fail: /// - "SE" south-east /// - `height`: window height (in character cells). Minimum of 1. /// - `width`: window width (in character cells). Minimum of 1. +/// - 'bufpos': position float relative text inside the window `win` (only +/// when relative="win"). Takes a tuple of [line, column] where +/// both are zero-index. Note: `row` and `col` if present, still +/// applies relative this positio. By default `row=1` and `col=0` +/// is used (with default NW anchor), to make the float +/// behave like a tooltip under the buffer text. /// - `row`: row position. Screen cell height are used as unit. Can be /// floating point. /// - `col`: column position. Screen cell width is used as unit. Can be diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index e279edebde..02670c0513 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -503,25 +503,33 @@ Dictionary nvim_win_get_config(Window window, Error *err) return rv; } - PUT(rv, "focusable", BOOLEAN_OBJ(wp->w_float_config.focusable)); - PUT(rv, "external", BOOLEAN_OBJ(wp->w_float_config.external)); + FloatConfig *config = &wp->w_float_config; + + PUT(rv, "focusable", BOOLEAN_OBJ(config->focusable)); + PUT(rv, "external", BOOLEAN_OBJ(config->external)); if (wp->w_floating) { - PUT(rv, "width", INTEGER_OBJ(wp->w_float_config.width)); - PUT(rv, "height", INTEGER_OBJ(wp->w_float_config.height)); - if (!wp->w_float_config.external) { - if (wp->w_float_config.relative == kFloatRelativeWindow) { - PUT(rv, "win", INTEGER_OBJ(wp->w_float_config.window)); + PUT(rv, "width", INTEGER_OBJ(config->width)); + PUT(rv, "height", INTEGER_OBJ(config->height)); + if (!config->external) { + if (config->relative == kFloatRelativeWindow) { + PUT(rv, "win", INTEGER_OBJ(config->window)); + if (config->bufpos.lnum >= 0) { + Array pos = ARRAY_DICT_INIT; + ADD(pos, INTEGER_OBJ(config->bufpos.lnum)); + ADD(pos, INTEGER_OBJ(config->bufpos.col)); + PUT(rv, "bufpos", ARRAY_OBJ(pos)); + } } PUT(rv, "anchor", STRING_OBJ(cstr_to_string( - float_anchor_str[wp->w_float_config.anchor]))); - PUT(rv, "row", FLOAT_OBJ(wp->w_float_config.row)); - PUT(rv, "col", FLOAT_OBJ(wp->w_float_config.col)); + float_anchor_str[config->anchor]))); + PUT(rv, "row", FLOAT_OBJ(config->row)); + PUT(rv, "col", FLOAT_OBJ(config->col)); } } - const char *rel = (wp->w_floating && !wp->w_float_config.external - ? float_relative_str[wp->w_float_config.relative] : ""); + const char *rel = (wp->w_floating && !config->external + ? float_relative_str[config->relative] : ""); PUT(rv, "relative", STRING_OBJ(cstr_to_string(rel))); return rv; diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index f8e07a471f..8fd4360aed 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -144,7 +144,7 @@ read_buffer( if (!readonlymode && !BUFEMPTY()) { changed(); } else if (retval != FAIL) { - unchanged(curbuf, false); + unchanged(curbuf, false, true); } apply_autocmds_retval(EVENT_STDINREADPOST, NULL, NULL, false, @@ -299,7 +299,7 @@ int open_buffer( || (aborting() && vim_strchr(p_cpo, CPO_INTMOD) != NULL)) { changed(); } else if (retval != FAIL && !read_stdin && !read_fifo) { - unchanged(curbuf, false); + unchanged(curbuf, false, true); } save_file_ff(curbuf); // keep this fileformat @@ -641,13 +641,11 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) } } -/* - * Make buffer not contain a file. - */ +/// Make buffer not contain a file. void buf_clear_file(buf_T *buf) { buf->b_ml.ml_line_count = 1; - unchanged(buf, true); + unchanged(buf, true, true); buf->b_p_eol = true; buf->b_start_eol = true; buf->b_p_bomb = false; diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 117e3f42fe..16c7804be0 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -1006,7 +1006,7 @@ typedef enum { kFloatRelativeCursor = 2, } FloatRelative; -EXTERN const char *const float_relative_str[] INIT(= { "editor", "window", +EXTERN const char *const float_relative_str[] INIT(= { "editor", "win", "cursor" }); typedef enum { @@ -1016,6 +1016,7 @@ typedef enum { typedef struct { Window window; + lpos_T bufpos; int height, width; double row, col; FloatAnchor anchor; @@ -1026,6 +1027,7 @@ typedef struct { } FloatConfig; #define FLOAT_CONFIG_INIT ((FloatConfig){ .height = 0, .width = 0, \ + .bufpos = { -1, 0 }, \ .row = 0, .col = 0, .anchor = 0, \ .relative = 0, .external = false, \ .focusable = true, \ diff --git a/src/nvim/change.c b/src/nvim/change.c index f8a4cf4282..3401f8a0a8 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -478,9 +478,11 @@ changed_lines( } } -/// Called when the changed flag must be reset for buffer "buf". -/// When "ff" is true also reset 'fileformat'. -void unchanged(buf_T *buf, int ff) +/// Called when the changed flag must be reset for buffer `buf`. +/// When `ff` is true also reset 'fileformat'. +/// When `always_inc_changedtick` is true b:changedtick is incremented even +/// when the changed flag was off. +void unchanged(buf_T *buf, int ff, bool always_inc_changedtick) { if (buf->b_changed || (ff && file_ff_differs(buf, false))) { buf->b_changed = false; @@ -491,8 +493,10 @@ void unchanged(buf_T *buf, int ff) check_status(buf); redraw_tabline = true; need_maketitle = true; // set window title later + buf_inc_changedtick(buf); + } else if (always_inc_changedtick) { + buf_inc_changedtick(buf); } - buf_inc_changedtick(buf); } /// Insert string "p" at the cursor position. Stops at a NUL byte. diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 98448ff1f9..8e848483a7 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -14781,6 +14781,32 @@ static void f_screencol(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = ui_current_col() + 1; } +/// "screenpos({winid}, {lnum}, {col})" function +static void f_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + pos_T pos; + int row = 0; + int scol = 0, ccol = 0, ecol = 0; + + tv_dict_alloc_ret(rettv); + dict_T *dict = rettv->vval.v_dict; + + win_T *wp = find_win_by_nr_or_id(&argvars[0]); + if (wp == NULL) { + return; + } + + pos.lnum = tv_get_number(&argvars[1]); + pos.col = tv_get_number(&argvars[2]) - 1; + pos.coladd = 0; + textpos2screenpos(wp, &pos, &row, &scol, &ccol, &ecol, false); + + tv_dict_add_nr(dict, S_LEN("row"), row); + tv_dict_add_nr(dict, S_LEN("col"), scol); + tv_dict_add_nr(dict, S_LEN("curscol"), ccol); + tv_dict_add_nr(dict, S_LEN("endcol"), ecol); +} + /* * "screenrow()" function */ @@ -20895,7 +20921,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); + msg_multiline_attr(tofree, echo_attr, true); } xfree(tofree); } diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 0ad9ef5dac..ab5ff57c2f 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -266,6 +266,7 @@ return { screenattr={args=2}, screenchar={args=2}, screencol={}, + screenpos={args=3}, screenrow={}, search={args={1, 4}}, searchdecl={args={1, 3}}, diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 964b884460..affdda0386 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -1323,7 +1323,7 @@ void dialog_changed(buf_T *buf, bool checkall) (void)buf_write_all(buf, false); } } else if (ret == VIM_NO) { - unchanged(buf, true); + unchanged(buf, true, false); } else if (ret == VIM_ALL) { // Write all modified files that can be written. // Skip readonly buffers, these need to be confirmed @@ -1348,7 +1348,7 @@ void dialog_changed(buf_T *buf, bool checkall) } else if (ret == VIM_DISCARDALL) { // mark all buffers as unchanged FOR_ALL_BUFFERS(buf2) { - unchanged(buf2, true); + unchanged(buf2, true, false); } } } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 34717b631e..b97c886094 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1527,7 +1527,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, // // where 'addr' is: // - // % (entire file) + // % (entire file) // $ [+-NUM] // 'x [+-NUM] (where x denotes a currently defined mark) // . [+-NUM] diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 517325a2cd..4cf42b41e9 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -3485,10 +3485,10 @@ restore_backup: if (reset_changed && whole && !append && !write_info.bw_conv_error && (overwriting || vim_strchr(p_cpo, CPO_PLUS) != NULL)) { - unchanged(buf, true); + unchanged(buf, true, false); const varnumber_T changedtick = buf_get_changedtick(buf); if (buf->b_last_changedtick + 1 == changedtick) { - // changedtick is always incremented in unchanged() but that + // b:changedtick may be incremented in unchanged() but that // should not trigger a TextChanged event. buf->b_last_changedtick = changedtick; } @@ -5107,14 +5107,14 @@ void buf_reload(buf_T *buf, int orig_mode) } (void)move_lines(savebuf, buf); } - } else if (buf == curbuf) { /* "buf" still valid */ - /* Mark the buffer as unmodified and free undo info. */ - unchanged(buf, TRUE); + } else if (buf == curbuf) { // "buf" still valid. + // Mark the buffer as unmodified and free undo info. + unchanged(buf, true, true); if ((flags & READ_KEEP_UNDO) == 0) { u_blockfree(buf); u_clearall(buf); } else { - /* Mark all undo states as changed. */ + // Mark all undo states as changed. u_unchanged(curbuf); } } diff --git a/src/nvim/memline.c b/src/nvim/memline.c index b69812a389..15dd2767a2 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -1003,7 +1003,7 @@ void ml_recover(void) set_option_value("fenc", 0L, (char *)b0_fenc, OPT_LOCAL); xfree(b0_fenc); } - unchanged(curbuf, TRUE); + unchanged(curbuf, true, true); bnum = 1; /* start with block 1 */ page_count = 1; /* which is 1 page */ diff --git a/src/nvim/message.c b/src/nvim/message.c index ac731210d7..b5a5f656a0 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -222,12 +222,12 @@ 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) +void msg_multiline_attr(const char *s, int attr, bool check_int) FUNC_ATTR_NONNULL_ALL { const char *next_spec = s; - while (next_spec != NULL) { + while (next_spec != NULL && (!check_int || !got_int)) { next_spec = strpbrk(s, "\t\n\r"); if (next_spec != NULL) { @@ -306,7 +306,7 @@ bool msg_attr_keep(char_u *s, int attr, bool keep, bool multiline) s = buf; if (multiline) { - msg_multiline_attr((char *)s, attr); + msg_multiline_attr((char *)s, attr, false); } else { msg_outtrans_attr(s, attr); } diff --git a/src/nvim/move.c b/src/nvim/move.c index 4a87f82eb7..e6fee9999f 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -95,6 +95,8 @@ static void comp_botline(win_T *wp) wp->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP; set_empty_rows(wp, done); + + win_check_anchored_floats(wp); } void reset_cursorline(void) @@ -310,6 +312,7 @@ void update_topline(void) } } curwin->w_valid |= VALID_TOPLINE; + win_check_anchored_floats(curwin); /* * Need to redraw when topline changed. @@ -827,7 +830,8 @@ void curs_columns( new_leftcol = 0; if (new_leftcol != (int)curwin->w_leftcol) { curwin->w_leftcol = new_leftcol; - /* screen has to be redrawn with new curwin->w_leftcol */ + win_check_anchored_floats(curwin); + // screen has to be redrawn with new curwin->w_leftcol redraw_later(NOT_VALID); } } @@ -943,6 +947,74 @@ void curs_columns( curwin->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL; } +/// Compute the screen position of text character at "pos" in window "wp" +/// The resulting values are one-based, zero when character is not visible. +/// +/// @param[out] rowp screen row +/// @param[out] scolp start screen column +/// @param[out] ccolp cursor screen column +/// @param[out] ecolp end screen column +void textpos2screenpos(win_T *wp, pos_T *pos, int *rowp, int *scolp, + int *ccolp, int *ecolp, bool local) +{ + colnr_T scol = 0, ccol = 0, ecol = 0; + int row = 0; + int rowoff = 0; + colnr_T coloff = 0; + bool visible_row = false; + + if (pos->lnum >= wp->w_topline && pos->lnum < wp->w_botline) { + row = plines_m_win(wp, wp->w_topline, pos->lnum - 1) + 1; + visible_row = true; + } else if (pos->lnum < wp->w_topline) { + row = 0; + } else { + row = wp->w_height_inner; + } + + bool existing_row = (pos->lnum > 0 + && pos->lnum <= wp->w_buffer->b_ml.ml_line_count); + + if ((local && existing_row) || visible_row) { + colnr_T off; + colnr_T col; + int width; + + getvcol(wp, pos, &scol, &ccol, &ecol); + + // similar to what is done in validate_cursor_col() + col = scol; + off = win_col_off(wp); + col += off; + width = wp->w_width - off + win_col_off2(wp); + + // long line wrapping, adjust row + if (wp->w_p_wrap && col >= (colnr_T)wp->w_width && width > 0) { + // use same formula as what is used in curs_columns() + rowoff = visible_row ? ((col - wp->w_width) / width + 1) : 0; + col -= rowoff * width; + } + + col -= wp->w_leftcol; + + if (col >= 0 && col < width) { + coloff = col - scol + (local ? 0 : wp->w_wincol) + 1; + } else { + scol = ccol = ecol = 0; + // character is left or right of the window + if (local) { + coloff = col < 0 ? -1 : wp->w_width_inner + 1; + } else { + row = 0; + } + } + } + *rowp = (local ? 0 : wp->w_winrow) + row + rowoff; + *scolp = scol + coloff; + *ccolp = ccol + coloff; + *ecolp = ecol + coloff; +} + /* * Scroll the current window down by "line_count" logical lines. "CTRL-Y" */ @@ -1099,6 +1171,7 @@ check_topfill ( } } } + win_check_anchored_floats(curwin); } /* diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 3aa2f47907..d846bd0ae0 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -21,13 +21,14 @@ #define SREQ_WIN 1 // Request window-local option value #define SREQ_BUF 2 // Request buffer-local option value -/* - * Default values for 'errorformat'. - * The "%f|%l| %m" one is used for when the contents of the quickfix window is - * written to a file. - */ -#define DFLT_EFM \ - "%*[^\"]\"%f\"%*\\D%l: %m,\"%f\"%*\\D%l: %m,%-G%f:%l: (Each undeclared identifier is reported only once,%-G%f:%l: for each function it appears in.),%-GIn file included from %f:%l:%c:,%-GIn file included from %f:%l:%c\\,,%-GIn file included from %f:%l:%c,%-GIn file included from %f:%l,%-G%*[ ]from %f:%l:%c,%-G%*[ ]from %f:%l:,%-G%*[ ]from %f:%l\\,,%-G%*[ ]from %f:%l,%f:%l:%c:%m,%f(%l):%m,%f:%l:%m,\"%f\"\\, line %l%*\\D%c%*[^ ] %m,%D%*\\a[%*\\d]: Entering directory %*[`']%f',%X%*\\a[%*\\d]: Leaving directory %*[`']%f',%D%*\\a: Entering directory %*[`']%f',%X%*\\a: Leaving directory %*[`']%f',%DMaking %*\\a in %f,%f|%l| %m" +// Default values for 'errorformat'. +// The "%f|%l| %m" one is used for when the contents of the quickfix window is +// written to a file. +#ifdef WIN32 +# define DFLT_EFM "%f(%l) \\=: %t%*\\D%n: %m,%*[^\"]\"%f\"%*\\D%l: %m,%f(%l) \\=: %m,%*[^ ] %f %l: %m,%f:%l:%c:%m,%f(%l):%m,%f:%l:%m,%f|%l| %m" +#else +# define DFLT_EFM "%*[^\"]\"%f\"%*\\D%l: %m,\"%f\"%*\\D%l: %m,%-G%f:%l: (Each undeclared identifier is reported only once,%-G%f:%l: for each function it appears in.),%-GIn file included from %f:%l:%c:,%-GIn file included from %f:%l:%c\\,,%-GIn file included from %f:%l:%c,%-GIn file included from %f:%l,%-G%*[ ]from %f:%l:%c,%-G%*[ ]from %f:%l:,%-G%*[ ]from %f:%l\\,,%-G%*[ ]from %f:%l,%f:%l:%c:%m,%f(%l):%m,%f:%l:%m,\"%f\"\\, line %l%*\\D%c%*[^ ] %m,%D%*\\a[%*\\d]: Entering directory %*[`']%f',%X%*\\a[%*\\d]: Leaving directory %*[`']%f',%D%*\\a: Entering directory %*[`']%f',%X%*\\a: Leaving directory %*[`']%f',%DMaking %*\\a in %f,%f|%l| %m" +#endif #define DFLT_GREPFORMAT "%f:%l:%m,%f:%l%m,%f %l%m" diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c index 20f68233e7..ba6226ef9d 100644 --- a/src/nvim/os/signal.c +++ b/src/nvim/os/signal.c @@ -145,6 +145,8 @@ static void deadly_signal(int signum) // Set the v:dying variable. set_vim_var_nr(VV_DYING, 1); + WLOG("got signal %d (%s)", signum, signal_name(signum)); + snprintf((char *)IObuff, sizeof(IObuff), "Vim: Caught deadly signal '%s'\n", signal_name(signum)); diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 80477c6947..26687f14f0 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -1016,7 +1016,8 @@ qf_init_end: /// Set the title of the specified quickfix list. Frees the previous title. /// Prepends ':' to the title. -static void qf_store_title(qf_info_T *qi, int qf_idx, char_u *title) +static void qf_store_title(qf_info_T *qi, int qf_idx, const char_u *title) + FUNC_ATTR_NONNULL_ARG(1) { XFREE_CLEAR(qi->qf_lists[qf_idx].qf_title); @@ -1025,7 +1026,7 @@ static void qf_store_title(qf_info_T *qi, int qf_idx, char_u *title) char_u *p = xmallocz(len); qi->qf_lists[qf_idx].qf_title = p; - xstrlcpy((char *)p, (char *)title, len + 1); + xstrlcpy((char *)p, (const char *)title, len + 1); } } @@ -4693,7 +4694,7 @@ static int qf_getprop_qfidx(qf_info_T *qi, dict_T *what) if (di->di_tv.vval.v_number != 0) { qf_idx = (int)di->di_tv.vval.v_number - 1; if (qf_idx < 0 || qf_idx >= qi->qf_listcount) { - qf_idx = -1; + qf_idx = INVALID_QFIDX; } } } else if (di->di_tv.v_type == VAR_STRING @@ -4701,7 +4702,7 @@ static int qf_getprop_qfidx(qf_info_T *qi, dict_T *what) // Get the last quickfix list number qf_idx = qi->qf_listcount - 1; } else { - qf_idx = -1; + qf_idx = INVALID_QFIDX; } } @@ -4713,7 +4714,7 @@ static int qf_getprop_qfidx(qf_info_T *qi, dict_T *what) qf_idx = qf_id2nr(qi, (unsigned)di->di_tv.vval.v_number); } } else { - qf_idx = -1; + qf_idx = INVALID_QFIDX; } } @@ -4829,7 +4830,7 @@ int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict) } // List is not present or is empty - if (qi == NULL || qi->qf_listcount == 0 || qf_idx == -1) { + if (qi == NULL || qi->qf_listcount == 0 || qf_idx == INVALID_QFIDX) { return qf_getprop_defaults(qi, flags, retdict); } @@ -4978,18 +4979,17 @@ static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list, return retval; } -static int qf_set_properties(qf_info_T *qi, dict_T *what, int action, - char_u *title) +// Get the quickfix list index from 'nr' or 'id' +static int qf_setprop_get_qfidx( + const qf_info_T *qi, + const dict_T *what, + int action, + bool *newlist) + FUNC_ATTR_NONNULL_ALL { dictitem_T *di; - int retval = FAIL; - int newlist = false; - char_u *errorformat = p_efm; - - if (action == ' ' || qi->qf_curlist == qi->qf_listcount) { - newlist = true; - } int qf_idx = qi->qf_curlist; // default is the current list + if ((di = tv_dict_find(what, S_LEN("nr"))) != NULL) { // Use the specified quickfix/location list if (di->di_tv.v_type == VAR_NUMBER) { @@ -5002,37 +5002,140 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action, // When creating a new list, accept qf_idx pointing to the next // non-available list and add the new list at the end of the // stack. - newlist = true; - qf_idx = qi->qf_listcount - 1; + *newlist = true; + qf_idx = qi->qf_listcount > 0 ? qi->qf_listcount - 1 : 0; } else if (qf_idx < 0 || qf_idx >= qi->qf_listcount) { - return FAIL; + return INVALID_QFIDX; } else if (action != ' ') { - newlist = false; // use the specified list + *newlist = false; // use the specified list } } else if (di->di_tv.v_type == VAR_STRING && strequal((const char *)di->di_tv.vval.v_string, "$")) { if (qi->qf_listcount > 0) { qf_idx = qi->qf_listcount - 1; - } else if (newlist) { + } else if (*newlist) { qf_idx = 0; } else { - return FAIL; + return INVALID_QFIDX; } } else { - return FAIL; + return INVALID_QFIDX; } } - if (!newlist && (di = tv_dict_find(what, S_LEN("id"))) != NULL) { + if (!*newlist && (di = tv_dict_find(what, S_LEN("id"))) != NULL) { // Use the quickfix/location list with the specified id - if (di->di_tv.v_type == VAR_NUMBER) { - qf_idx = qf_id2nr(qi, (unsigned)di->di_tv.vval.v_number); - if (qf_idx == -1) { - return FAIL; // List not found - } - } else { + if (di->di_tv.v_type != VAR_NUMBER) { + return INVALID_QFIDX; + } + return qf_id2nr(qi, (unsigned)di->di_tv.vval.v_number); + } + + return qf_idx; +} + +// Set the quickfix list title. +static int qf_setprop_title(qf_info_T *qi, int qf_idx, const dict_T *what, + const dictitem_T *di) + FUNC_ATTR_NONNULL_ALL +{ + if (di->di_tv.v_type != VAR_STRING) { + return FAIL; + } + + xfree(qi->qf_lists[qf_idx].qf_title); + qi->qf_lists[qf_idx].qf_title = + (char_u *)tv_dict_get_string(what, "title", true); + if (qf_idx == qi->qf_curlist) { + qf_update_win_titlevar(qi); + } + + return OK; +} + +// Set quickfix list items/entries. +static int qf_setprop_items(qf_info_T *qi, int qf_idx, dictitem_T *di, + int action) + FUNC_ATTR_NONNULL_ALL +{ + if (di->di_tv.v_type != VAR_LIST) { + return FAIL; + } + + char_u *title_save = vim_strsave(qi->qf_lists[qf_idx].qf_title); + const int retval = qf_add_entries(qi, qf_idx, di->di_tv.vval.v_list, + title_save, + action == ' ' ? 'a' : action); + xfree(title_save); + + return retval; +} + +// Set quickfix list items/entries from a list of lines. +static int qf_setprop_items_from_lines( + qf_info_T *qi, + int qf_idx, + const dict_T *what, + dictitem_T *di, + int action) + FUNC_ATTR_NONNULL_ALL +{ + char_u *errorformat = p_efm; + dictitem_T *efm_di; + int retval = FAIL; + + // Use the user supplied errorformat settings (if present) + if ((efm_di = tv_dict_find(what, S_LEN("efm"))) != NULL) { + if (efm_di->di_tv.v_type != VAR_STRING + || efm_di->di_tv.vval.v_string == NULL) { return FAIL; } + errorformat = efm_di->di_tv.vval.v_string; + } + + // Only a List value is supported + if (di->di_tv.v_type != VAR_LIST || di->di_tv.vval.v_list == NULL) { + return FAIL; + } + + if (action == 'r') { + qf_free_items(qi, qf_idx); + } + if (qf_init_ext(qi, qf_idx, NULL, NULL, &di->di_tv, errorformat, + false, (linenr_T)0, (linenr_T)0, NULL, NULL) > 0) { + retval = OK; + } + + return retval; +} + +// Set quickfix list context. +static int qf_setprop_context(qf_info_T *qi, int qf_idx, dictitem_T *di) + FUNC_ATTR_NONNULL_ALL +{ + tv_free(qi->qf_lists[qf_idx].qf_ctx); + typval_T *ctx = xcalloc(1, sizeof(typval_T)); + if (ctx != NULL) { + tv_copy(&di->di_tv, ctx); + } + qi->qf_lists[qf_idx].qf_ctx = ctx; + + return OK; +} + +// Set quickfix/location list properties (title, items, context). +// Also used to add items from parsing a list of lines. +// Used by the setqflist() and setloclist() VimL functions. +static int qf_set_properties(qf_info_T *qi, const dict_T *what, int action, + char_u *title) + FUNC_ATTR_NONNULL_ALL +{ + dictitem_T *di; + int retval = FAIL; + bool newlist = action == ' ' || qi->qf_curlist == qi->qf_listcount; + int qf_idx = qf_setprop_get_qfidx(qi, what, action, &newlist); + if (qf_idx == INVALID_QFIDX) { // List not found + return FAIL; } if (newlist) { @@ -5042,63 +5145,16 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action, } if ((di = tv_dict_find(what, S_LEN("title"))) != NULL) { - if (di->di_tv.v_type == VAR_STRING) { - xfree(qi->qf_lists[qf_idx].qf_title); - qi->qf_lists[qf_idx].qf_title = (char_u *)tv_dict_get_string( - what, "title", true); - if (qf_idx == qi->qf_curlist) { - qf_update_win_titlevar(qi); - } - retval = OK; - } + retval = qf_setprop_title(qi, qf_idx, what, di); } if ((di = tv_dict_find(what, S_LEN("items"))) != NULL) { - if (di->di_tv.v_type == VAR_LIST) { - assert(qi->qf_lists[qf_idx].qf_title != NULL); - char_u *title_save = vim_strsave(qi->qf_lists[qf_idx].qf_title); - - retval = qf_add_entries(qi, qf_idx, di->di_tv.vval.v_list, - title_save, action == ' ' ? 'a' : action); - if (action == 'r') { - // When replacing the quickfix list entries using - // qf_add_entries(), the title is set with a ':' prefix. - // Restore the title with the saved title. - xfree(qi->qf_lists[qf_idx].qf_title); - qi->qf_lists[qf_idx].qf_title = vim_strsave(title_save); - } - xfree(title_save); - } + retval = qf_setprop_items(qi, qf_idx, di, action); } - - if ((di = tv_dict_find(what, S_LEN("efm"))) != NULL) { - if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL) { - return FAIL; - } - errorformat = di->di_tv.vval.v_string; - } - if ((di = tv_dict_find(what, S_LEN("lines"))) != NULL) { - // Only a List value is supported - if (di->di_tv.v_type == VAR_LIST && di->di_tv.vval.v_list != NULL) { - if (action == 'r') { - qf_free_items(qi, qf_idx); - } - if (qf_init_ext(qi, qf_idx, NULL, NULL, &di->di_tv, errorformat, - false, (linenr_T)0, (linenr_T)0, NULL, NULL) > 0) { - retval = OK; - } - } else { - return FAIL; - } + retval = qf_setprop_items_from_lines(qi, qf_idx, what, di, action); } - if ((di = tv_dict_find(what, S_LEN("context"))) != NULL) { - tv_free(qi->qf_lists[qf_idx].qf_ctx); - - typval_T *ctx = xcalloc(1, sizeof(typval_T)); - tv_copy(&di->di_tv, ctx); - qi->qf_lists[qf_idx].qf_ctx = ctx; - retval = OK; + retval = qf_setprop_context(qi, qf_idx, di); } if (retval == OK) { diff --git a/src/nvim/testdir/test_changedtick.vim b/src/nvim/testdir/test_changedtick.vim index 3a91bb54aa..c789cdc1bc 100644 --- a/src/nvim/testdir/test_changedtick.vim +++ b/src/nvim/testdir/test_changedtick.vim @@ -55,3 +55,41 @@ func Test_changedtick_fixed() call assert_fails('unlet d["changedtick"]', 'E46:') endfunc + +func Test_changedtick_not_incremented_with_write() + new + let fname = "XChangeTick" + exe 'w ' .. fname + + " :write when the buffer is not changed does not increment changedtick + let expected = b:changedtick + w + call assert_equal(expected, b:changedtick) + + " :write when the buffer IS changed DOES increment changedtick + let expected = b:changedtick + 1 + setlocal modified + w + call assert_equal(expected, b:changedtick) + + " Two ticks: change + write + let expected = b:changedtick + 2 + call setline(1, 'hello') + w + call assert_equal(expected, b:changedtick) + + " Two ticks: start insert + write + let expected = b:changedtick + 2 + normal! o + w + call assert_equal(expected, b:changedtick) + + " Three ticks: start insert + change + write + let expected = b:changedtick + 3 + normal! ochanged + w + call assert_equal(expected, b:changedtick) + + bwipe + call delete(fname) +endfunc diff --git a/src/nvim/testdir/test_cursor_func.vim b/src/nvim/testdir/test_cursor_func.vim index 6bc9535aaf..037918fa31 100644 --- a/src/nvim/testdir/test_cursor_func.vim +++ b/src/nvim/testdir/test_cursor_func.vim @@ -64,3 +64,31 @@ func Test_curswant_with_cursorline() call assert_equal(6, winsaveview().curswant) quit! endfunc + +func Test_screenpos() + rightbelow new + rightbelow 20vsplit + call setline(1, ["\tsome text", "long wrapping line here", "next line"]) + redraw + let winid = win_getid() + let [winrow, wincol] = win_screenpos(winid) + call assert_equal({'row': winrow, + \ 'col': wincol + 0, + \ 'curscol': wincol + 7, + \ 'endcol': wincol + 7}, screenpos(winid, 1, 1)) + call assert_equal({'row': winrow, + \ 'col': wincol + 13, + \ 'curscol': wincol + 13, + \ 'endcol': wincol + 13}, screenpos(winid, 1, 7)) + call assert_equal({'row': winrow + 2, + \ 'col': wincol + 1, + \ 'curscol': wincol + 1, + \ 'endcol': wincol + 1}, screenpos(winid, 2, 22)) + setlocal number + call assert_equal({'row': winrow + 3, + \ 'col': wincol + 9, + \ 'curscol': wincol + 9, + \ 'endcol': wincol + 9}, screenpos(winid, 2, 22)) + close + bwipe! +endfunc diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 90abb30da1..440bf47f4b 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -1167,6 +1167,13 @@ func Test_efm2() call assert_equal(1, len(l), string(l)) call assert_equal('|| msg2', l[0].text) + " When matching error lines, case should be ignored. Test for this. + set noignorecase + let l=getqflist({'lines' : ['Xtest:FOO10:Line 20'], 'efm':'%f:foo%l:%m'}) + call assert_equal(10, l.items[0].lnum) + call assert_equal('Line 20', l.items[0].text) + set ignorecase& + new | only let &efm = save_efm endfunc @@ -1802,6 +1809,9 @@ func Xproperty_tests(cchar) call assert_equal(0, s) let d = g:Xgetlist({"title":1}) call assert_equal('Sample', d.title) + " Try setting title to a non-string value + call assert_equal(-1, g:Xsetlist([], 'a', {'title' : ['Test']})) + call assert_equal('Sample', g:Xgetlist({"title":1}).title) Xopen call assert_equal('Sample', w:quickfix_title) @@ -1950,6 +1960,9 @@ func Xproperty_tests(cchar) call g:Xsetlist([], 'a', {'items' : [{'filename':'F1', 'lnum':10}]}) call assert_equal(10, g:Xgetlist({'items':1}).items[0].lnum) + " Try setting the items using a string + call assert_equal(-1, g:Xsetlist([], ' ', {'items' : 'Test'})) + " Save and restore the quickfix stack call g:Xsetlist([], 'f') call assert_equal(0, g:Xgetlist({'nr':'$'}).nr) @@ -3481,14 +3494,17 @@ func Xautocmd_changelist(cchar) call assert_equal(4, line('.')) autocmd! QuickFixCmdPost - " Test for grep/lgrep - call g:Xsetlist([], 'f') - Xexpr 'Xtestfile1:2:Line2' - autocmd QuickFixCmdPost * Xolder - silent Xgrep Line5 Xtestfile2 - call assert_equal('Xtestfile2', bufname('')) - call assert_equal(5, line('.')) - autocmd! QuickFixCmdPost + " The grepprg may not be set on non-Unix systems + if has('unix') + " Test for grep/lgrep + call g:Xsetlist([], 'f') + Xexpr 'Xtestfile1:2:Line2' + autocmd QuickFixCmdPost * Xolder + silent Xgrep Line5 Xtestfile2 + call assert_equal('Xtestfile2', bufname('')) + call assert_equal(5, line('.')) + autocmd! QuickFixCmdPost + endif " Test for vimgrep/lvimgrep call g:Xsetlist([], 'f') diff --git a/src/nvim/undo.c b/src/nvim/undo.c index c8343941d2..8ec36a8fe3 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -2294,7 +2294,7 @@ static void u_undoredo(int undo, bool do_buf_event) if (old_flags & UH_CHANGED) { changed(); } else { - unchanged(curbuf, FALSE); + unchanged(curbuf, false, true); } // because the calls to changed()/unchanged() above will bump changedtick diff --git a/src/nvim/version.c b/src/nvim/version.c index 74a4852def..0eb005d5df 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -69,17 +69,17 @@ NULL // clang-format off static const int included_patches[] = { 1849, - // 1848, + 1848, 1847, - // 1846, + 1846, 1845, // 1844, 1843, // 1842, - // 1841, + 1841, 1840, 1839, - // 1838, + 1838, 1837, 1836, // 1835, @@ -87,23 +87,23 @@ static const int included_patches[] = { 1833, 1832, 1831, - // 1830, + 1830, 1829, 1828, 1827, 1826, 1825, 1824, - // 1823, + 1823, 1822, - // 1821, - // 1820, + 1821, + 1820, 1819, - // 1818, + 1818, // 1817, 1816, // 1815, - // 1814, + 1814, 1813, // 1812, 1811, @@ -111,18 +111,18 @@ static const int included_patches[] = { 1809, 1808, 1807, - // 1806, + 1806, 1805, // 1804, 1803, - // 1802, - // 1801, + 1802, + 1801, 1800, 1799, // 1798, // 1797, - // 1796, - // 1795, + 1796, + 1795, // 1794, // 1793, 1792, @@ -137,26 +137,26 @@ static const int included_patches[] = { // 1783, 1782, 1781, - // 1780, + 1780, 1779, 1778, 1777, // 1776, - // 1775, + 1775, // 1774, 1773, // 1772, // 1771, // 1770, // 1769, - // 1768, + 1768, // 1767, 1766, 1765, 1764, 1763, - // 1762, - // 1761, + 1762, + 1761, 1760, // 1759, 1758, @@ -164,22 +164,22 @@ static const int included_patches[] = { 1756, 1755, // 1754, - // 1753, + 1753, // 1752, 1751, 1750, 1749, 1748, // 1747, - // 1746, + 1746, // 1745, // 1744, // 1743, // 1742, 1741, - // 1740, + 1740, 1739, - // 1738, + 1738, 1737, 1736, 1735, @@ -188,113 +188,113 @@ static const int included_patches[] = { // 1732, 1731, 1730, - // 1729, - // 1728, + 1729, + 1728, 1727, 1726, // 1725, - // 1724, + 1724, 1723, // 1722, - // 1721, - // 1720, + 1721, + 1720, 1719, - // 1718, + 1718, 1717, - // 1716, - // 1715, - // 1714, + 1716, + 1715, + 1714, // 1713, // 1712, // 1711, 1710, - // 1709, + 1709, 1708, 1707, // 1706, 1705, 1704, - // 1703, + 1703, 1702, 1701, 1700, 1699, 1698, - // 1697, + 1697, 1696, - // 1695, - // 1694, + 1695, + 1694, 1693, 1692, - // 1691, - // 1690, + 1691, + 1690, 1689, - // 1688, + 1688, 1687, 1686, // 1685, - // 1684, + 1684, 1683, 1682, - // 1681, - // 1680, + 1681, + 1680, 1679, 1678, 1677, 1676, 1675, 1674, - // 1673, + 1673, 1672, // 1671, - // 1670, + 1670, 1669, // 1668, - // 1667, + 1667, // 1666, // 1665, - // 1664, + 1664, 1663, - // 1662, + 1662, 1661, // 1660, 1659, 1658, - // 1657, - // 1656, + 1657, + 1656, // 1655, 1654, - // 1653, - // 1652, + 1653, + 1652, // 1651, 1650, 1649, - // 1648, + 1648, // 1647, 1646, - // 1645, - // 1644, - // 1643, - // 1642, + 1645, + 1644, + 1643, + 1642, // 1641, - // 1640, + 1640, 1639, - // 1638, - // 1637, - // 1636, + 1638, + 1637, + 1636, 1635, 1634, 1633, - // 1632, - // 1631, + 1632, + 1631, 1630, - // 1629, - // 1628, + 1629, + 1628, 1627, 1626, 1625, // 1624, - // 1623, + 1623, 1622, // 1621, 1620, @@ -302,28 +302,28 @@ static const int included_patches[] = { 1618, // 1617, // 1616, - // 1615, + 1615, 1614, 1613, // 1612, - // 1611, + 1611, 1610, // 1609, 1608, 1607, 1606, - // 1605, + 1605, // 1604, 1603, 1602, 1601, 1600, // 1599, - // 1598, + 1598, 1597, // 1596, 1595, - // 1594, + 1594, // 1593, // 1592, // 1591, @@ -341,7 +341,7 @@ static const int included_patches[] = { 1579, // 1578, 1577, - // 1576, + 1576, 1575, // 1574, 1573, @@ -365,44 +365,44 @@ static const int included_patches[] = { 1555, // 1554, 1553, - // 1552, + 1552, // 1551, // 1550, 1549, 1548, 1547, - // 1546, + 1546, 1545, // 1544, // 1543, - // 1542, + 1542, 1541, // 1540, // 1539, // 1538, - // 1537, + 1537, 1536, - // 1535, - // 1534, - // 1533, + 1535, + 1534, + 1533, 1532, // 1531, 1530, // 1529, 1528, // 1527, - // 1526, + 1526, // 1525, 1524, // 1523, // 1522, - // 1521, + 1521, // 1520, 1519, 1518, 1517, 1516, - // 1515, + 1515, 1514, 1513, 1512, @@ -433,15 +433,15 @@ static const int included_patches[] = { 1487, 1486, 1485, - // 1484, + 1484, 1483, - // 1482, + 1482, 1481, - // 1480, - // 1479, + 1480, + 1479, 1478, - // 1477, - // 1476, + 1477, + 1476, 1475, 1474, 1473, @@ -455,7 +455,7 @@ static const int included_patches[] = { 1465, 1464, // 1463, - // 1462, + 1462, // 1461, // 1460, // 1459, @@ -487,8 +487,8 @@ static const int included_patches[] = { 1433, 1432, 1431, - // 1430, - // 1429, + 1430, + 1429, 1428, 1427, 1426, @@ -531,17 +531,17 @@ static const int included_patches[] = { 1389, // 1388, 1387, - // 1386, + 1386, 1385, 1384, 1383, // 1382, - // 1381, + 1381, 1380, 1379, 1378, 1377, - // 1376, + 1376, // 1375, 1374, 1373, @@ -549,16 +549,16 @@ static const int included_patches[] = { // 1371, 1370, 1369, - // 1368, + 1368, // 1367, // 1366, 1365, 1364, 1363, - // 1362, + 1362, 1361, - // 1360, - // 1359, + 1360, + 1359, // 1358, 1357, // 1356, @@ -570,14 +570,14 @@ static const int included_patches[] = { 1350, // 1349, 1348, - // 1347, + 1347, // 1346, // 1345, // 1344, 1343, - // 1342, + 1342, // 1341, - // 1340, + 1340, // 1339, 1338, 1337, @@ -589,20 +589,20 @@ static const int included_patches[] = { 1331, // 1330, 1329, - // 1328, + 1328, 1327, 1326, 1325, 1324, - // 1323, + 1323, 1322, // 1321, // 1320, // 1319, // 1318, - // 1317, - // 1316, - // 1315, + 1317, + 1316, + 1315, 1314, 1313, // 1312, @@ -621,7 +621,7 @@ static const int included_patches[] = { 1299, 1298, // 1297, - // 1296, + 1296, // 1295, 1294, // 1293, @@ -631,7 +631,7 @@ static const int included_patches[] = { 1289, 1288, // 1287, - // 1286, + 1286, 1285, 1284, 1283, @@ -640,10 +640,10 @@ static const int included_patches[] = { 1280, 1279, 1278, - // 1277, + 1277, // 1276, 1275, - // 1274, + 1274, 1273, 1272, 1271, @@ -656,9 +656,9 @@ static const int included_patches[] = { // 1264, 1263, 1262, - // 1261, - // 1260, - // 1259, + 1261, + 1260, + 1259, 1258, 1257, 1256, @@ -671,17 +671,17 @@ static const int included_patches[] = { 1249, 1248, 1247, - // 1246, + 1246, 1245, // 1244, 1243, 1242, - // 1241, + 1241, // 1240, - // 1239, + 1239, 1238, 1237, - // 1236, + 1236, 1235, 1234, 1233, @@ -700,11 +700,11 @@ static const int included_patches[] = { 1220, 1219, 1218, - // 1217, + 1217, 1216, 1215, 1214, - // 1213, + 1213, 1212, 1211, 1210, @@ -715,7 +715,7 @@ static const int included_patches[] = { 1205, 1204, // 1203, - // 1202, + 1202, 1201, 1200, 1199, @@ -724,7 +724,7 @@ static const int included_patches[] = { 1196, 1195, // 1194, - // 1193, + 1193, 1192, 1191, 1190, @@ -738,24 +738,24 @@ static const int included_patches[] = { 1182, 1181, 1180, - // 1179, + 1179, 1178, - // 1177, + 1177, // 1176, 1175, // 1174, - // 1173, + 1173, 1172, 1171, // 1170, 1169, 1168, - // 1167, + 1167, 1166, 1165, - // 1164, + 1164, 1163, - // 1162, + 1162, 1161, 1160, 1159, @@ -768,10 +768,10 @@ static const int included_patches[] = { 1152, 1151, 1150, - // 1149, + 1149, 1148, 1147, - // 1146, + 1146, 1145, 1144, 1143, @@ -786,22 +786,22 @@ static const int included_patches[] = { // 1134, 1133, 1132, - // 1131, + 1131, 1130, // 1129, 1128, // 1127, - // 1126, + 1126, // 1125, 1124, // 1123, 1122, 1121, - // 1120, + 1120, // 1119, 1118, - // 1117, - // 1116, + 1117, + 1116, 1115, 1114, // 1113, @@ -811,18 +811,18 @@ static const int included_patches[] = { // 1109, 1108, 1107, - // 1106, + 1106, 1105, 1104, - // 1103, + 1103, // 1102, - // 1101, - // 1100, + 1101, + 1100, 1099, 1098, // 1097, // 1096, - // 1095, + 1095, 1094, 1093, 1092, @@ -830,24 +830,24 @@ static const int included_patches[] = { 1090, 1089, 1088, - // 1087, + 1087, 1086, 1085, 1084, - // 1083, + 1083, 1082, - // 1081, - // 1080, + 1081, + 1080, 1079, 1078, 1077, - // 1076, - // 1075, + 1076, + 1075, // 1074, 1073, 1072, 1071, - // 1070, + 1070, 1069, 1068, 1067, @@ -859,16 +859,16 @@ static const int included_patches[] = { 1061, // 1060, 1059, - // 1058, - // 1057, + 1058, + 1057, 1056, - // 1055, - // 1054, - // 1053, - // 1052, + 1055, + 1054, + 1053, + 1052, // 1051, 1050, - // 1049, + 1049, 1048, 1047, 1046, @@ -878,12 +878,12 @@ static const int included_patches[] = { 1042, 1041, 1040, - // 1039, + 1039, // 1038, 1037, - // 1036, + 1036, // 1035, - // 1034, + 1034, 1033, 1032, 1031, @@ -902,13 +902,13 @@ static const int included_patches[] = { 1018, 1017, 1016, - // 1015, + 1015, 1014, - // 1013, + 1013, 1012, - // 1011, + 1011, 1010, - // 1009, + 1009, 1008, 1007, 1006, @@ -922,45 +922,45 @@ static const int included_patches[] = { 998, 997, 996, - // 995, - // 994, + 995, + 994, 993, - // 992, - // 991, - // 990, + 992, + 991, + 990, 989, 988, - // 987, + 987, 986, - // 985, - // 984, + 985, + 984, 983, - // 982, - // 981, + 982, + 981, 980, - // 979, - // 978, - // 977, + 979, + 978, + 977, // 976, 975, 974, - // 973, + 973, 972, 971, // 970, 969, - // 968, + 968, 967, 966, - // 965, - // 964, - // 963, + 965, + 964, + 963, 962, 961, // 960, - // 959, - // 958, - // 957, + 959, + 958, + 957, // 956, 955, 954, @@ -970,34 +970,34 @@ static const int included_patches[] = { 950, 949, 948, - // 947, + 947, 946, 945, 944, - // 943, - // 942, + 943, + 942, // 941, - // 940, - // 939, - // 938, + 940, + 939, + 938, // 937, - // 936, - // 935, + 936, + 935, // 934, 933, // 932, 931, - // 930, - // 929, + 930, + 929, 928, - // 927, - // 926, + 927, + 926, 925, - // 924, - // 923, + 924, + 923, 922, 921, - // 920, + 920, 919, // 918, // 917, @@ -1008,8 +1008,8 @@ static const int included_patches[] = { // 912, 911, // 910, - // 909, - // 908, + 909, + 908, 907, 906, 905, @@ -1614,14 +1614,14 @@ static const int included_patches[] = { 306, 305, 304, - // 303, + 303, 302, 301, 300, 299, 298, 297, - // 296, + 296, 295, 294, 293, @@ -1679,14 +1679,14 @@ static const int included_patches[] = { 241, 240, 239, - // 238, + 238, 237, 236, 235, 234, // 233, 232, - // 231, + 231, 230, 229, 228, diff --git a/src/nvim/window.c b/src/nvim/window.c index 6861e19ca7..315b5ef759 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -652,6 +652,17 @@ void win_config_float(win_T *wp, FloatConfig fconfig) } } +void win_check_anchored_floats(win_T *win) +{ + for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) { + // float might be anchored to moved window + if (wp->w_float_config.relative == kFloatRelativeWindow + && wp->w_float_config.window == win->handle) { + wp->w_pos_changed = true; + } + } +} + static void ui_ext_win_position(win_T *wp) { if (!wp->w_floating) { @@ -673,6 +684,13 @@ static void ui_ext_win_position(win_T *wp) screen_adjust_grid(&grid, &row_off, &col_off); row += row_off; col += col_off; + if (c.bufpos.lnum >= 0) { + pos_T pos = { c.bufpos.lnum+1, c.bufpos.col, 0 }; + int trow, tcol, tcolc, tcole; + textpos2screenpos(win, &pos, &trow, &tcol, &tcolc, &tcole, true); + row += trow-1; + col += tcol-1; + } } api_clear_error(&dummy); } @@ -745,6 +763,18 @@ static bool parse_float_relative(String relative, FloatRelative *out) return true; } +static bool parse_float_bufpos(Array bufpos, lpos_T *out) +{ + if (bufpos.size != 2 + || bufpos.items[0].type != kObjectTypeInteger + || bufpos.items[1].type != kObjectTypeInteger) { + return false; + } + out->lnum = bufpos.items[0].data.integer; + out->col = bufpos.items[1].data.integer; + return true; +} + bool parse_float_config(Dictionary config, FloatConfig *fconfig, bool reconf, Error *err) { @@ -753,6 +783,7 @@ bool parse_float_config(Dictionary config, FloatConfig *fconfig, bool reconf, bool has_row = false, has_col = false, has_relative = false; bool has_external = false, has_window = false; bool has_width = false, has_height = false; + bool has_bufpos = false; for (size_t i = 0; i < config.size; i++) { char *key = config.items[i].key.data; @@ -832,6 +863,18 @@ bool parse_float_config(Dictionary config, FloatConfig *fconfig, bool reconf, return false; } fconfig->window = val.data.integer; + } else if (!strcmp(key, "bufpos")) { + if (val.type != kObjectTypeArray) { + api_set_error(err, kErrorTypeValidation, + "'bufpos' key must be Array"); + return false; + } + if (!parse_float_bufpos(val.data.array, &fconfig->bufpos)) { + api_set_error(err, kErrorTypeValidation, + "Invalid value of 'bufpos' key"); + return false; + } + has_bufpos = true; } else if (!strcmp(key, "external")) { if (val.type == kObjectTypeInteger) { fconfig->external = val.data.integer; @@ -886,6 +929,21 @@ bool parse_float_config(Dictionary config, FloatConfig *fconfig, bool reconf, fconfig->window = curwin->handle; } + if (has_window && !has_bufpos) { + fconfig->bufpos.lnum = -1; + } + + if (has_bufpos) { + if (!has_row) { + fconfig->row = (fconfig->anchor & kFloatAnchorSouth) ? 0 : 1; + has_row = true; + } + if (!has_col) { + fconfig->col = 0; + has_col = true; + } + } + if (has_relative && has_external) { api_set_error(err, kErrorTypeValidation, "Only one of 'relative' and 'external' must be used"); @@ -4732,7 +4790,8 @@ void shell_new_rows(void) if (!frame_check_height(topframe, h)) frame_new_height(topframe, h, FALSE, FALSE); - (void)win_comp_pos(); /* recompute w_winrow and w_wincol */ + (void)win_comp_pos(); // recompute w_winrow and w_wincol + win_reconfig_floats(); // The size of floats might change compute_cmdrow(); curtab->tp_ch_used = p_ch; @@ -4753,7 +4812,8 @@ void shell_new_columns(void) frame_new_width(topframe, Columns, false, false); } - (void)win_comp_pos(); /* recompute w_winrow and w_wincol */ + (void)win_comp_pos(); // recompute w_winrow and w_wincol + win_reconfig_floats(); // The size of floats might change } /* @@ -4809,15 +4869,23 @@ int win_comp_pos(void) frame_comp_pos(topframe, &row, &col); - // Too often, but when we support anchoring floats to split windows, - // this will be needed for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) { - win_config_float(wp, wp->w_float_config); + // float might be anchored to moved window + if (wp->w_float_config.relative == kFloatRelativeWindow) { + wp->w_pos_changed = true; + } } return row; } +void win_reconfig_floats(void) +{ + for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) { + win_config_float(wp, wp->w_float_config); + } +} + /* * Update the position of the windows in frame "topfrp", using the width and * height of the frames. |