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