diff options
author | Björn Linse <bjorn.linse@gmail.com> | 2019-09-05 12:50:38 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-09-05 12:50:38 +0200 |
commit | 79ea47d47836154cfb5a9574faa5c2af8422486a (patch) | |
tree | e3498beaa706b1424e71d142d1d01b809056f59b /src | |
parent | 632ee8d2ed6e307cc4d9ca2cf9e3ba0531a8af51 (diff) | |
parent | d5162afa2aee6609bf5d4f818639235b70c57a9a (diff) | |
download | rneovim-79ea47d47836154cfb5a9574faa5c2af8422486a.tar.gz rneovim-79ea47d47836154cfb5a9574faa5c2af8422486a.tar.bz2 rneovim-79ea47d47836154cfb5a9574faa5c2af8422486a.zip |
Merge pull request #10451 from bfredl/floatbuf
anchor float to buffer position
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_defs.h | 4 | ||||
-rw-r--r-- | src/nvim/eval.c | 26 | ||||
-rw-r--r-- | src/nvim/eval.lua | 1 | ||||
-rw-r--r-- | src/nvim/move.c | 75 | ||||
-rw-r--r-- | src/nvim/testdir/test_cursor_func.vim | 28 | ||||
-rw-r--r-- | src/nvim/window.c | 78 |
8 files changed, 231 insertions, 19 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_defs.h b/src/nvim/buffer_defs.h index 5e700940b0..83b8531b8b 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/eval.c b/src/nvim/eval.c index fa9777b651..70f38b793d 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 */ 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/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/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/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. |