aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBjörn Linse <bjorn.linse@gmail.com>2019-09-05 12:50:38 +0200
committerGitHub <noreply@github.com>2019-09-05 12:50:38 +0200
commit79ea47d47836154cfb5a9574faa5c2af8422486a (patch)
treee3498beaa706b1424e71d142d1d01b809056f59b /src
parent632ee8d2ed6e307cc4d9ca2cf9e3ba0531a8af51 (diff)
parentd5162afa2aee6609bf5d4f818639235b70c57a9a (diff)
downloadrneovim-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.c6
-rw-r--r--src/nvim/api/window.c32
-rw-r--r--src/nvim/buffer_defs.h4
-rw-r--r--src/nvim/eval.c26
-rw-r--r--src/nvim/eval.lua1
-rw-r--r--src/nvim/move.c75
-rw-r--r--src/nvim/testdir/test_cursor_func.vim28
-rw-r--r--src/nvim/window.c78
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.