Merge pull request #10451 from bfredl/floatbuf

anchor float to buffer position
This commit is contained in:
Björn Linse
2019-09-05 12:50:38 +02:00
committed by GitHub
10 changed files with 579 additions and 19 deletions

View File

@@ -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

View File

@@ -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;

View File

@@ -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, \

View File

@@ -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
*/

View File

@@ -266,6 +266,7 @@ return {
screenattr={args=2},
screenchar={args=2},
screencol={},
screenpos={args=3},
screenrow={},
search={args={1, 4}},
searchdecl={args={1, 3}},

View File

@@ -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);
}
/*

View File

@@ -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

View File

@@ -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.