neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

window.c (27873B)


      1 // eval/window.c: Window related builtin functions
      2 
      3 #include <stdbool.h>
      4 #include <stdint.h>
      5 #include <stdio.h>
      6 #include <stdlib.h>
      7 #include <string.h>
      8 
      9 #include "nvim/ascii_defs.h"
     10 #include "nvim/autocmd.h"
     11 #include "nvim/buffer.h"
     12 #include "nvim/cursor.h"
     13 #include "nvim/errors.h"
     14 #include "nvim/eval/funcs.h"
     15 #include "nvim/eval/typval.h"
     16 #include "nvim/eval/typval_defs.h"
     17 #include "nvim/eval/window.h"
     18 #include "nvim/ex_getln.h"
     19 #include "nvim/garray.h"
     20 #include "nvim/garray_defs.h"
     21 #include "nvim/gettext_defs.h"
     22 #include "nvim/globals.h"
     23 #include "nvim/macros_defs.h"
     24 #include "nvim/mark_defs.h"
     25 #include "nvim/memory.h"
     26 #include "nvim/message.h"
     27 #include "nvim/move.h"
     28 #include "nvim/normal.h"
     29 #include "nvim/option_vars.h"
     30 #include "nvim/os/fs.h"
     31 #include "nvim/pos_defs.h"
     32 #include "nvim/strings.h"
     33 #include "nvim/types_defs.h"
     34 #include "nvim/vim_defs.h"
     35 #include "nvim/window.h"
     36 
     37 #include "eval/window.c.generated.h"
     38 
     39 static const char *e_invalwindow = N_("E957: Invalid window number");
     40 static const char e_cannot_resize_window_in_another_tab_page[]
     41  = N_("E1308: Cannot resize a window in another tab page");
     42 
     43 bool win_has_winnr(win_T *wp, tabpage_T *tp)
     44  FUNC_ATTR_NONNULL_ALL
     45 {
     46  return (wp == (tp == curtab ? curwin : tp->tp_curwin))
     47         || (!wp->w_config.hide && wp->w_config.focusable);
     48 }
     49 
     50 static int win_getid(typval_T *argvars)
     51 {
     52  if (argvars[0].v_type == VAR_UNKNOWN) {
     53    return curwin->handle;
     54  }
     55  int winnr = (int)tv_get_number(&argvars[0]);
     56  win_T *wp;
     57  if (winnr <= 0) {
     58    return 0;
     59  }
     60 
     61  tabpage_T *tp = NULL;
     62  if (argvars[1].v_type == VAR_UNKNOWN) {
     63    tp = curtab;
     64    wp = firstwin;
     65  } else {
     66    int tabnr = (int)tv_get_number(&argvars[1]);
     67    FOR_ALL_TABS(tp2) {
     68      if (--tabnr == 0) {
     69        tp = tp2;
     70        break;
     71      }
     72    }
     73    if (tp == NULL) {
     74      return -1;
     75    }
     76    if (tp == curtab) {
     77      wp = firstwin;
     78    } else {
     79      wp = tp->tp_firstwin;
     80    }
     81  }
     82  for (; wp != NULL; wp = wp->w_next) {
     83    if ((winnr -= win_has_winnr(wp, tp)) == 0) {
     84      return wp->handle;
     85    }
     86  }
     87  return 0;
     88 }
     89 
     90 static void win_id2tabwin(typval_T *const argvars, typval_T *const rettv)
     91 {
     92  handle_T id = (handle_T)tv_get_number(&argvars[0]);
     93 
     94  int winnr = 1;
     95  int tabnr = 1;
     96  win_get_tabwin(id, &tabnr, &winnr);
     97 
     98  list_T *const list = tv_list_alloc_ret(rettv, 2);
     99  tv_list_append_number(list, tabnr);
    100  tv_list_append_number(list, winnr);
    101 }
    102 
    103 win_T *win_id2wp(int id)
    104 {
    105  return win_id2wp_tp(id, NULL);
    106 }
    107 
    108 /// Return the window and tab pointer of window "id".
    109 /// Returns NULL when not found.
    110 win_T *win_id2wp_tp(int id, tabpage_T **tpp)
    111 {
    112  FOR_ALL_TAB_WINDOWS(tp, wp) {
    113    if (wp->handle == id) {
    114      if (tpp != NULL) {
    115        *tpp = tp;
    116      }
    117      return wp;
    118    }
    119  }
    120 
    121  return NULL;
    122 }
    123 
    124 static int win_id2win(typval_T *argvars)
    125 {
    126  int nr = 1;
    127  int id = (int)tv_get_number(&argvars[0]);
    128 
    129  FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
    130    if (wp->handle == id) {
    131      return (win_has_winnr(wp, curtab) ? nr : 0);
    132    }
    133    nr += win_has_winnr(wp, curtab);
    134  }
    135  return 0;
    136 }
    137 
    138 void win_findbuf(typval_T *argvars, list_T *list)
    139 {
    140  int bufnr = (int)tv_get_number(&argvars[0]);
    141 
    142  FOR_ALL_TAB_WINDOWS(tp, wp) {
    143    if (wp->w_buffer->b_fnum == bufnr) {
    144      tv_list_append_number(list, wp->handle);
    145    }
    146  }
    147 }
    148 
    149 /// Find window specified by "vp" in tabpage "tp".
    150 ///
    151 /// @param tp  NULL for current tab page
    152 /// @return  current window if "vp" is number zero.
    153 ///          NULL if not found.
    154 win_T *find_win_by_nr(typval_T *vp, tabpage_T *tp)
    155 {
    156  int nr = (int)tv_get_number_chk(vp, NULL);
    157 
    158  if (nr < 0) {
    159    return NULL;
    160  }
    161 
    162  if (nr == 0) {
    163    return curwin;
    164  }
    165 
    166  // This method accepts NULL as an alias for curtab.
    167  if (tp == NULL) {
    168    tp = curtab;
    169  }
    170 
    171  FOR_ALL_WINDOWS_IN_TAB(wp, tp) {
    172    if (nr >= LOWEST_WIN_ID) {
    173      if (wp->handle == nr) {
    174        return wp;
    175      }
    176    } else if (--nr <= 0) {
    177      return wp;
    178    }
    179  }
    180  return NULL;
    181 }
    182 
    183 /// Find a window: When using a Window ID in any tab page, when using a number
    184 /// in the current tab page.
    185 /// Returns NULL when not found.
    186 win_T *find_win_by_nr_or_id(typval_T *vp)
    187 {
    188  int nr = (int)tv_get_number_chk(vp, NULL);
    189 
    190  if (nr >= LOWEST_WIN_ID) {
    191    return win_id2wp((int)tv_get_number(vp));
    192  }
    193 
    194  return find_win_by_nr(vp, NULL);
    195 }
    196 
    197 /// Find window specified by "wvp" in tabpage "tvp".
    198 win_T *find_tabwin(typval_T *wvp, typval_T *tvp)
    199 {
    200  win_T *wp = NULL;
    201  tabpage_T *tp = NULL;
    202 
    203  if (wvp->v_type != VAR_UNKNOWN) {
    204    if (tvp->v_type != VAR_UNKNOWN) {
    205      int n = (int)tv_get_number(tvp);
    206      if (n >= 0) {
    207        tp = find_tabpage(n);
    208      }
    209    } else {
    210      tp = curtab;
    211    }
    212 
    213    if (tp != NULL) {
    214      wp = find_win_by_nr(wvp, tp);
    215    }
    216  } else {
    217    wp = curwin;
    218  }
    219 
    220  return wp;
    221 }
    222 
    223 /// Get the layout of the given tab page for winlayout().
    224 static void get_framelayout(const frame_T *fr, list_T *l, bool outer)
    225 {
    226  if (fr == NULL) {
    227    return;
    228  }
    229 
    230  list_T *fr_list;
    231  if (outer) {
    232    // outermost call from f_winlayout()
    233    fr_list = l;
    234  } else {
    235    fr_list = tv_list_alloc(2);
    236    tv_list_append_list(l, fr_list);
    237  }
    238 
    239  if (fr->fr_layout == FR_LEAF) {
    240    if (fr->fr_win != NULL) {
    241      tv_list_append_string(fr_list, S_LEN("leaf"));
    242      tv_list_append_number(fr_list, fr->fr_win->handle);
    243    }
    244  } else {
    245    if (fr->fr_layout == FR_ROW) {
    246      tv_list_append_string(fr_list, S_LEN("row"));
    247    } else {
    248      tv_list_append_string(fr_list, S_LEN("col"));
    249    }
    250 
    251    list_T *const win_list = tv_list_alloc(kListLenUnknown);
    252    tv_list_append_list(fr_list, win_list);
    253    const frame_T *child = fr->fr_child;
    254    while (child != NULL) {
    255      get_framelayout(child, win_list, false);
    256      child = child->fr_next;
    257    }
    258  }
    259 }
    260 
    261 /// Common code for tabpagewinnr() and winnr().
    262 static int get_winnr(tabpage_T *tp, typval_T *argvar)
    263 {
    264  int nr = 1;
    265 
    266  win_T *twin = (tp == curtab) ? curwin : tp->tp_curwin;
    267  if (argvar->v_type != VAR_UNKNOWN) {
    268    bool invalid_arg = false;
    269    const char *const arg = tv_get_string_chk(argvar);
    270    if (arg == NULL) {
    271      nr = 0;  // Type error; errmsg already given.
    272    } else if (strcmp(arg, "$") == 0) {
    273      twin = (tp == curtab) ? lastwin : tp->tp_lastwin;
    274    } else if (strcmp(arg, "#") == 0) {
    275      twin = (tp == curtab) ? prevwin : tp->tp_prevwin;
    276      if (twin == NULL) {
    277        nr = 0;
    278      }
    279    } else {
    280      // Extract the window count (if specified). e.g. winnr('3j')
    281      char *endp;
    282      int count = (int)strtol(arg, &endp, 10);
    283      if (count <= 0) {
    284        // if count is not specified, default to 1
    285        count = 1;
    286      }
    287      if (endp != NULL && *endp != NUL) {
    288        if (strequal(endp, "j")) {
    289          twin = win_vert_neighbor(tp, twin, false, count);
    290        } else if (strequal(endp, "k")) {
    291          twin = win_vert_neighbor(tp, twin, true, count);
    292        } else if (strequal(endp, "h")) {
    293          twin = win_horz_neighbor(tp, twin, true, count);
    294        } else if (strequal(endp, "l")) {
    295          twin = win_horz_neighbor(tp, twin, false, count);
    296        } else {
    297          invalid_arg = true;
    298        }
    299      } else {
    300        invalid_arg = true;
    301      }
    302    }
    303 
    304    if (invalid_arg) {
    305      semsg(_(e_invexpr2), arg);
    306      nr = 0;
    307    }
    308  } else if (!win_has_winnr(twin, tp)) {
    309    nr = 0;
    310  }
    311 
    312  if (nr <= 0) {
    313    return 0;
    314  }
    315 
    316  nr = 0;
    317  win_T *wp = (tp == curtab) ? firstwin : tp->tp_firstwin;
    318  for (; wp != NULL; wp = wp->w_next) {
    319    nr += win_has_winnr(wp, tp);
    320    if (wp == twin) {
    321      break;
    322    }
    323  }
    324  if (wp == NULL) {
    325    nr = 0;  // didn't find it in this tabpage
    326  }
    327  return nr;
    328 }
    329 
    330 /// @return  information about a window as a dictionary.
    331 static dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr)
    332 {
    333  dict_T *const dict = tv_dict_alloc();
    334 
    335  // make sure w_botline is valid
    336  validate_botline_win(wp);
    337 
    338  tv_dict_add_nr(dict, S_LEN("tabnr"), tpnr);
    339  tv_dict_add_nr(dict, S_LEN("winnr"), winnr);
    340  tv_dict_add_nr(dict, S_LEN("winid"), wp->handle);
    341  tv_dict_add_nr(dict, S_LEN("height"), wp->w_view_height);
    342  tv_dict_add_nr(dict, S_LEN("status_height"), wp->w_status_height);
    343  tv_dict_add_nr(dict, S_LEN("winrow"), wp->w_winrow + 1);
    344  tv_dict_add_nr(dict, S_LEN("topline"), wp->w_topline);
    345  tv_dict_add_nr(dict, S_LEN("botline"), wp->w_botline - 1);
    346  tv_dict_add_nr(dict, S_LEN("leftcol"), wp->w_leftcol);
    347  tv_dict_add_nr(dict, S_LEN("winbar"), wp->w_winbar_height);
    348  tv_dict_add_nr(dict, S_LEN("width"), wp->w_view_width);
    349  tv_dict_add_nr(dict, S_LEN("bufnr"), wp->w_buffer->b_fnum);
    350  tv_dict_add_nr(dict, S_LEN("wincol"), wp->w_wincol + 1);
    351  tv_dict_add_nr(dict, S_LEN("textoff"), win_col_off(wp));
    352  tv_dict_add_nr(dict, S_LEN("terminal"), bt_terminal(wp->w_buffer));
    353  tv_dict_add_nr(dict, S_LEN("quickfix"), bt_quickfix(wp->w_buffer));
    354  tv_dict_add_nr(dict, S_LEN("loclist"),
    355                 (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL));
    356 
    357  // Add a reference to window variables
    358  tv_dict_add_dict(dict, S_LEN("variables"), wp->w_vars);
    359 
    360  return dict;
    361 }
    362 
    363 /// @return  information (variables, options, etc.) about a tab page
    364 ///          as a dictionary.
    365 static dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx)
    366 {
    367  dict_T *const dict = tv_dict_alloc();
    368 
    369  tv_dict_add_nr(dict, S_LEN("tabnr"), tp_idx);
    370 
    371  list_T *const l = tv_list_alloc(kListLenMayKnow);
    372  FOR_ALL_WINDOWS_IN_TAB(wp, tp) {
    373    tv_list_append_number(l, (varnumber_T)wp->handle);
    374  }
    375  tv_dict_add_list(dict, S_LEN("windows"), l);
    376 
    377  // Make a reference to tabpage variables
    378  tv_dict_add_dict(dict, S_LEN("variables"), tp->tp_vars);
    379 
    380  return dict;
    381 }
    382 
    383 /// "gettabinfo()" function
    384 void f_gettabinfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
    385 {
    386  tabpage_T *tparg = NULL;
    387 
    388  tv_list_alloc_ret(rettv, (argvars[0].v_type == VAR_UNKNOWN
    389                            ? 1
    390                            : kListLenMayKnow));
    391 
    392  if (argvars[0].v_type != VAR_UNKNOWN) {
    393    // Information about one tab page
    394    tparg = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL));
    395    if (tparg == NULL) {
    396      return;
    397    }
    398  }
    399 
    400  // Get information about a specific tab page or all tab pages
    401  int tpnr = 0;
    402  FOR_ALL_TABS(tp) {
    403    tpnr++;
    404    if (tparg != NULL && tp != tparg) {
    405      continue;
    406    }
    407    dict_T *const d = get_tabpage_info(tp, tpnr);
    408    tv_list_append_dict(rettv->vval.v_list, d);
    409    if (tparg != NULL) {
    410      return;
    411    }
    412  }
    413 }
    414 
    415 /// "getwininfo()" function
    416 void f_getwininfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
    417 {
    418  win_T *wparg = NULL;
    419 
    420  tv_list_alloc_ret(rettv, kListLenMayKnow);
    421 
    422  if (argvars[0].v_type != VAR_UNKNOWN) {
    423    wparg = win_id2wp((int)tv_get_number(&argvars[0]));
    424    if (wparg == NULL) {
    425      return;
    426    }
    427  }
    428 
    429  // Collect information about either all the windows across all the tab
    430  // pages or one particular window.
    431  int16_t tabnr = 0;
    432  FOR_ALL_TABS(tp) {
    433    tabnr++;
    434    int16_t winnr = 0;
    435    FOR_ALL_WINDOWS_IN_TAB(wp, tp) {
    436      winnr += win_has_winnr(wp, tp);
    437      if (wparg != NULL && wp != wparg) {
    438        continue;
    439      }
    440      dict_T *const d = get_win_info(wp, tabnr, win_has_winnr(wp, tp) ? winnr : 0);
    441      tv_list_append_dict(rettv->vval.v_list, d);
    442      if (wparg != NULL) {
    443        // found information about a specific window
    444        return;
    445      }
    446    }
    447  }
    448 }
    449 
    450 /// "getwinpos({timeout})" function
    451 void f_getwinpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
    452 {
    453  tv_list_alloc_ret(rettv, 2);
    454  tv_list_append_number(rettv->vval.v_list, -1);
    455  tv_list_append_number(rettv->vval.v_list, -1);
    456 }
    457 
    458 /// "getwinposx()" function
    459 void f_getwinposx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
    460 {
    461  rettv->vval.v_number = -1;
    462 }
    463 
    464 /// "getwinposy()" function
    465 void f_getwinposy(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
    466 {
    467  rettv->vval.v_number = -1;
    468 }
    469 
    470 /// "tabpagenr()" function
    471 void f_tabpagenr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
    472 {
    473  int nr = 1;
    474 
    475  if (argvars[0].v_type != VAR_UNKNOWN) {
    476    const char *const arg = tv_get_string_chk(&argvars[0]);
    477    nr = 0;
    478    if (arg != NULL) {
    479      if (strcmp(arg, "$") == 0) {
    480        nr = tabpage_index(NULL) - 1;
    481      } else if (strcmp(arg, "#") == 0) {
    482        nr = valid_tabpage(lastused_tabpage) ? tabpage_index(lastused_tabpage) : 0;
    483      } else {
    484        semsg(_(e_invexpr2), arg);
    485      }
    486    }
    487  } else {
    488    nr = tabpage_index(curtab);
    489  }
    490  rettv->vval.v_number = nr;
    491 }
    492 
    493 /// "tabpagewinnr()" function
    494 void f_tabpagewinnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
    495 {
    496  int nr = 1;
    497  tabpage_T *const tp = find_tabpage((int)tv_get_number(&argvars[0]));
    498  if (tp == NULL) {
    499    nr = 0;
    500  } else {
    501    nr = get_winnr(tp, &argvars[1]);
    502  }
    503  rettv->vval.v_number = nr;
    504 }
    505 
    506 /// Switch to a window for executing user code.
    507 /// Caller must call win_execute_after() later regardless of return value.
    508 ///
    509 /// @return  whether switching the window succeeded.
    510 bool win_execute_before(win_execute_T *args, win_T *wp, tabpage_T *tp)
    511 {
    512  args->wp = wp;
    513  args->curpos = wp->w_cursor;
    514  args->cwd_status = FAIL;
    515  args->apply_acd = false;
    516  args->save_sfname = NULL;
    517 
    518  // Getting and setting directory can be slow on some systems, only do
    519  // this when the current or target window/tab have a local directory or
    520  // 'acd' is set.
    521  if (curwin != wp
    522      && (curwin->w_localdir != NULL || wp->w_localdir != NULL
    523          || (curtab != tp && (curtab->tp_localdir != NULL || tp->tp_localdir != NULL))
    524          || p_acd)) {
    525    args->cwd_status = os_dirname(args->cwd, MAXPATHL);
    526  }
    527 
    528  // If 'acd' is set, check we are using that directory.  If yes, then
    529  // apply 'acd' afterwards, otherwise restore the current directory.
    530  if (args->cwd_status == OK && p_acd) {
    531    if (curbuf->b_sfname != NULL && curbuf->b_fname == curbuf->b_sfname) {
    532      args->save_sfname = xstrdup(curbuf->b_sfname);
    533    }
    534    do_autochdir();
    535    char autocwd[MAXPATHL];
    536    if (os_dirname(autocwd, MAXPATHL) == OK) {
    537      args->apply_acd = strcmp(args->cwd, autocwd) == 0;
    538    }
    539  }
    540 
    541  if (switch_win_noblock(&args->switchwin, wp, tp, true) == OK) {
    542    check_cursor(curwin);
    543    return true;
    544  }
    545  return false;
    546 }
    547 
    548 /// Restore the previous window after executing user code.
    549 void win_execute_after(win_execute_T *args)
    550 {
    551  restore_win_noblock(&args->switchwin, true);
    552 
    553  if (args->apply_acd) {
    554    xfree(args->save_sfname);
    555    do_autochdir();
    556  } else if (args->cwd_status == OK) {
    557    os_chdir(args->cwd);
    558    if (args->save_sfname != NULL) {
    559      xfree(curbuf->b_sfname);
    560      curbuf->b_sfname = args->save_sfname;
    561      curbuf->b_fname = curbuf->b_sfname;
    562    }
    563  }
    564 
    565  // Update the status line if the cursor moved.
    566  if (win_valid(args->wp) && !equalpos(args->curpos, args->wp->w_cursor)) {
    567    args->wp->w_redr_status = true;
    568  }
    569 
    570  // In case the command moved the cursor or changed the Visual area,
    571  // check it is valid.
    572  check_cursor(curwin);
    573  if (VIsual_active) {
    574    check_pos(curbuf, &VIsual);
    575  }
    576 }
    577 
    578 /// "win_execute(win_id, command)" function
    579 void f_win_execute(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
    580 {
    581  // Return an empty string if something fails.
    582  rettv->v_type = VAR_STRING;
    583  rettv->vval.v_string = NULL;
    584 
    585  int id = (int)tv_get_number(argvars);
    586  tabpage_T *tp;
    587  win_T *wp = win_id2wp_tp(id, &tp);
    588  if (wp == NULL || tp == NULL) {
    589    return;
    590  }
    591 
    592  win_execute_T win_execute_args;
    593  if (win_execute_before(&win_execute_args, wp, tp)) {
    594    execute_common(argvars, rettv, 1);
    595  }
    596  win_execute_after(&win_execute_args);
    597 }
    598 
    599 /// "win_findbuf()" function
    600 void f_win_findbuf(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
    601 {
    602  tv_list_alloc_ret(rettv, kListLenMayKnow);
    603  win_findbuf(argvars, rettv->vval.v_list);
    604 }
    605 
    606 /// "win_getid()" function
    607 void f_win_getid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
    608 {
    609  rettv->vval.v_number = win_getid(argvars);
    610 }
    611 
    612 /// "win_gotoid()" function
    613 void f_win_gotoid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
    614 {
    615  int id = (int)tv_get_number(&argvars[0]);
    616  if (curwin->handle == id) {
    617    // Nothing to do.
    618    rettv->vval.v_number = 1;
    619    return;
    620  }
    621 
    622  if (text_or_buf_locked()) {
    623    return;
    624  }
    625  FOR_ALL_TAB_WINDOWS(tp, wp) {
    626    if (wp->handle == id) {
    627      // When jumping to another buffer stop Visual mode.
    628      if (VIsual_active && wp->w_buffer != curbuf) {
    629        end_visual_mode();
    630      }
    631      goto_tabpage_win(tp, wp);
    632      rettv->vval.v_number = 1;
    633      return;
    634    }
    635  }
    636 }
    637 
    638 /// "win_id2tabwin()" function
    639 void f_win_id2tabwin(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
    640 {
    641  win_id2tabwin(argvars, rettv);
    642 }
    643 
    644 /// "win_id2win()" function
    645 void f_win_id2win(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
    646 {
    647  rettv->vval.v_number = win_id2win(argvars);
    648 }
    649 
    650 /// "win_move_separator()" function
    651 void f_win_move_separator(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
    652 {
    653  rettv->vval.v_number = false;
    654 
    655  win_T *wp = find_win_by_nr_or_id(&argvars[0]);
    656  if (wp == NULL || wp->w_floating) {
    657    return;
    658  }
    659  if (!win_valid(wp)) {
    660    emsg(_(e_cannot_resize_window_in_another_tab_page));
    661    return;
    662  }
    663 
    664  int offset = (int)tv_get_number(&argvars[1]);
    665  win_drag_vsep_line(wp, offset);
    666  rettv->vval.v_number = true;
    667 }
    668 
    669 /// "win_move_statusline()" function
    670 void f_win_move_statusline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
    671 {
    672  win_T *wp;
    673  int offset;
    674 
    675  rettv->vval.v_number = false;
    676 
    677  wp = find_win_by_nr_or_id(&argvars[0]);
    678  if (wp == NULL || wp->w_floating) {
    679    return;
    680  }
    681  if (!win_valid(wp)) {
    682    emsg(_(e_cannot_resize_window_in_another_tab_page));
    683    return;
    684  }
    685 
    686  offset = (int)tv_get_number(&argvars[1]);
    687  win_drag_status_line(wp, offset);
    688  rettv->vval.v_number = true;
    689 }
    690 
    691 /// "win_screenpos()" function
    692 void f_win_screenpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
    693 {
    694  tv_list_alloc_ret(rettv, 2);
    695  const win_T *const wp = find_win_by_nr_or_id(&argvars[0]);
    696  tv_list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_winrow + 1);
    697  tv_list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_wincol + 1);
    698 }
    699 
    700 /// "win_splitmove()" function
    701 void f_win_splitmove(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
    702 {
    703  win_T *wp = find_win_by_nr_or_id(&argvars[0]);
    704  win_T *targetwin = find_win_by_nr_or_id(&argvars[1]);
    705  win_T *oldwin = curwin;
    706 
    707  rettv->vval.v_number = -1;
    708 
    709  if (wp == NULL || targetwin == NULL || wp == targetwin
    710      || !win_valid(wp) || !win_valid(targetwin)
    711      || targetwin->w_floating) {
    712    emsg(_(e_invalwindow));
    713    return;
    714  }
    715 
    716  int flags = 0;
    717  int size = 0;
    718 
    719  if (argvars[2].v_type != VAR_UNKNOWN) {
    720    dict_T *d;
    721    dictitem_T *di;
    722 
    723    if (tv_check_for_nonnull_dict_arg(argvars, 2) == FAIL) {
    724      return;
    725    }
    726 
    727    d = argvars[2].vval.v_dict;
    728    if (tv_dict_get_number(d, "vertical")) {
    729      flags |= WSP_VERT;
    730    }
    731    if ((di = tv_dict_find(d, "rightbelow", -1)) != NULL) {
    732      flags |= tv_get_number(&di->di_tv) ? WSP_BELOW : WSP_ABOVE;
    733    }
    734    size = (int)tv_dict_get_number(d, "size");
    735  }
    736 
    737  // Check if we're allowed to continue before we bother switching windows.
    738  if (is_aucmd_win(wp) || text_or_buf_locked() || check_split_disallowed(wp) == FAIL) {
    739    return;
    740  }
    741 
    742  if (curwin != targetwin) {
    743    win_goto(targetwin);
    744  }
    745 
    746  // Autocommands may have sent us elsewhere or closed "wp" or "oldwin".
    747  if (curwin == targetwin && win_valid(wp)) {
    748    if (win_splitmove(wp, size, flags) == OK) {
    749      rettv->vval.v_number = 0;
    750    }
    751  } else {
    752    emsg(_(e_auabort));
    753  }
    754 
    755  if (oldwin != curwin && win_valid(oldwin)) {
    756    win_goto(oldwin);
    757  }
    758 }
    759 
    760 /// "win_gettype(nr)" function
    761 void f_win_gettype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
    762 {
    763  win_T *wp = curwin;
    764 
    765  rettv->v_type = VAR_STRING;
    766  rettv->vval.v_string = NULL;
    767  if (argvars[0].v_type != VAR_UNKNOWN) {
    768    wp = find_win_by_nr_or_id(&argvars[0]);
    769    if (wp == NULL) {
    770      rettv->vval.v_string = xstrdup("unknown");
    771      return;
    772    }
    773  }
    774  if (is_aucmd_win(wp)) {
    775    rettv->vval.v_string = xstrdup("autocmd");
    776  } else if (wp->w_p_pvw) {
    777    rettv->vval.v_string = xstrdup("preview");
    778  } else if (wp->w_floating) {
    779    rettv->vval.v_string = xstrdup("popup");
    780  } else if (wp == cmdwin_win) {
    781    rettv->vval.v_string = xstrdup("command");
    782  } else if (bt_quickfix(wp->w_buffer)) {
    783    rettv->vval.v_string = xstrdup((wp->w_llist_ref != NULL ? "loclist" : "quickfix"));
    784  }
    785 }
    786 
    787 /// "getcmdwintype()" function
    788 void f_getcmdwintype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
    789 {
    790  rettv->v_type = VAR_STRING;
    791  rettv->vval.v_string = NULL;
    792  rettv->vval.v_string = xmallocz(1);
    793  rettv->vval.v_string[0] = (char)cmdwin_type;
    794 }
    795 
    796 /// "winbufnr(nr)" function
    797 void f_winbufnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
    798 {
    799  win_T *wp = find_win_by_nr_or_id(&argvars[0]);
    800  if (wp == NULL) {
    801    rettv->vval.v_number = -1;
    802  } else {
    803    rettv->vval.v_number = wp->w_buffer->b_fnum;
    804  }
    805 }
    806 
    807 /// "wincol()" function
    808 void f_wincol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
    809 {
    810  validate_cursor(curwin);
    811  rettv->vval.v_number = curwin->w_wcol + 1;
    812 }
    813 
    814 /// "winheight(nr)" function
    815 void f_winheight(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
    816 {
    817  win_T *wp = find_win_by_nr_or_id(&argvars[0]);
    818  if (wp == NULL) {
    819    rettv->vval.v_number = -1;
    820  } else {
    821    rettv->vval.v_number = wp->w_view_height;
    822  }
    823 }
    824 
    825 /// "winlayout()" function
    826 void f_winlayout(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
    827 {
    828  tabpage_T *tp;
    829 
    830  tv_list_alloc_ret(rettv, 2);
    831 
    832  if (argvars[0].v_type == VAR_UNKNOWN) {
    833    tp = curtab;
    834  } else {
    835    tp = find_tabpage((int)tv_get_number(&argvars[0]));
    836    if (tp == NULL) {
    837      return;
    838    }
    839  }
    840 
    841  get_framelayout(tp->tp_topframe, rettv->vval.v_list, true);
    842 }
    843 
    844 /// "winline()" function
    845 void f_winline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
    846 {
    847  validate_cursor(curwin);
    848  rettv->vval.v_number = curwin->w_wrow + 1;
    849 }
    850 
    851 /// "winnr()" function
    852 void f_winnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
    853 {
    854  rettv->vval.v_number = get_winnr(curtab, &argvars[0]);
    855 }
    856 
    857 /// "winrestcmd()" function
    858 void f_winrestcmd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
    859 {
    860  char buf[50];
    861 
    862  garray_T ga;
    863  ga_init(&ga, (int)sizeof(char), 70);
    864 
    865  // Do this twice to handle some window layouts properly.
    866  for (int i = 0; i < 2; i++) {
    867    int winnr = 1;
    868    FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
    869      if (!win_has_winnr(wp, curtab)) {
    870        continue;
    871      }
    872      size_t buflen = vim_snprintf_safelen(buf, sizeof(buf),
    873                                           "%dresize %d|", winnr, wp->w_height);
    874      ga_concat_len(&ga, buf, buflen);
    875      buflen = vim_snprintf_safelen(buf, sizeof(buf),
    876                                    "vert %dresize %d|", winnr, wp->w_width);
    877      ga_concat_len(&ga, buf, buflen);
    878      winnr++;
    879    }
    880  }
    881  ga_append(&ga, NUL);
    882 
    883  rettv->vval.v_string = ga.ga_data;
    884  rettv->v_type = VAR_STRING;
    885 }
    886 
    887 /// "winrestview()" function
    888 void f_winrestview(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
    889 {
    890  if (tv_check_for_nonnull_dict_arg(argvars, 0) == FAIL) {
    891    return;
    892  }
    893 
    894  dict_T *dict = argvars[0].vval.v_dict;
    895  dictitem_T *di;
    896  if ((di = tv_dict_find(dict, S_LEN("lnum"))) != NULL) {
    897    curwin->w_cursor.lnum = (linenr_T)tv_get_number(&di->di_tv);
    898  }
    899  if ((di = tv_dict_find(dict, S_LEN("col"))) != NULL) {
    900    curwin->w_cursor.col = (colnr_T)tv_get_number(&di->di_tv);
    901  }
    902  if ((di = tv_dict_find(dict, S_LEN("coladd"))) != NULL) {
    903    curwin->w_cursor.coladd = (colnr_T)tv_get_number(&di->di_tv);
    904  }
    905  if ((di = tv_dict_find(dict, S_LEN("curswant"))) != NULL) {
    906    curwin->w_curswant = (colnr_T)tv_get_number(&di->di_tv);
    907    curwin->w_set_curswant = false;
    908  }
    909  if ((di = tv_dict_find(dict, S_LEN("topline"))) != NULL) {
    910    set_topline(curwin, (linenr_T)tv_get_number(&di->di_tv));
    911  }
    912  if ((di = tv_dict_find(dict, S_LEN("topfill"))) != NULL) {
    913    curwin->w_topfill = (int)tv_get_number(&di->di_tv);
    914  }
    915  if ((di = tv_dict_find(dict, S_LEN("leftcol"))) != NULL) {
    916    curwin->w_leftcol = (colnr_T)tv_get_number(&di->di_tv);
    917  }
    918  if ((di = tv_dict_find(dict, S_LEN("skipcol"))) != NULL) {
    919    curwin->w_skipcol = (colnr_T)tv_get_number(&di->di_tv);
    920  }
    921 
    922  check_cursor(curwin);
    923  win_new_height(curwin, curwin->w_height);
    924  win_new_width(curwin, curwin->w_width);
    925  changed_window_setting(curwin);
    926 
    927  if (curwin->w_topline <= 0) {
    928    curwin->w_topline = 1;
    929  }
    930  if (curwin->w_topline > curbuf->b_ml.ml_line_count) {
    931    curwin->w_topline = curbuf->b_ml.ml_line_count;
    932  }
    933  check_topfill(curwin, true);
    934 }
    935 
    936 /// "winsaveview()" function
    937 void f_winsaveview(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
    938 {
    939  tv_dict_alloc_ret(rettv);
    940  dict_T *dict = rettv->vval.v_dict;
    941 
    942  tv_dict_add_nr(dict, S_LEN("lnum"), (varnumber_T)curwin->w_cursor.lnum);
    943  tv_dict_add_nr(dict, S_LEN("col"), (varnumber_T)curwin->w_cursor.col);
    944  tv_dict_add_nr(dict, S_LEN("coladd"), (varnumber_T)curwin->w_cursor.coladd);
    945  update_curswant();
    946  tv_dict_add_nr(dict, S_LEN("curswant"), (varnumber_T)curwin->w_curswant);
    947 
    948  tv_dict_add_nr(dict, S_LEN("topline"), (varnumber_T)curwin->w_topline);
    949  tv_dict_add_nr(dict, S_LEN("topfill"), (varnumber_T)curwin->w_topfill);
    950  tv_dict_add_nr(dict, S_LEN("leftcol"), (varnumber_T)curwin->w_leftcol);
    951  tv_dict_add_nr(dict, S_LEN("skipcol"), (varnumber_T)curwin->w_skipcol);
    952 }
    953 
    954 /// "winwidth(nr)" function
    955 void f_winwidth(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
    956 {
    957  win_T *wp = find_win_by_nr_or_id(&argvars[0]);
    958  if (wp == NULL) {
    959    rettv->vval.v_number = -1;
    960  } else {
    961    rettv->vval.v_number = wp->w_view_width;
    962  }
    963 }
    964 
    965 /// Set "win" to be the curwin and "tp" to be the current tab page.
    966 /// restore_win() MUST be called to undo, also when FAIL is returned.
    967 /// No autocommands will be executed until restore_win() is called.
    968 ///
    969 /// @param no_display  if true the display won't be affected, no redraw is
    970 ///                    triggered, another tabpage access is limited.
    971 ///
    972 /// @return FAIL if switching to "win" failed.
    973 int switch_win(switchwin_T *switchwin, win_T *win, tabpage_T *tp, bool no_display)
    974 {
    975  block_autocmds();
    976  return switch_win_noblock(switchwin, win, tp, no_display);
    977 }
    978 
    979 // As switch_win() but without blocking autocommands.
    980 int switch_win_noblock(switchwin_T *switchwin, win_T *win, tabpage_T *tp, bool no_display)
    981 {
    982  CLEAR_POINTER(switchwin);
    983  switchwin->sw_curwin = curwin;
    984  if (win == curwin) {
    985    switchwin->sw_same_win = true;
    986  } else {
    987    // Disable Visual selection, because redrawing may fail.
    988    switchwin->sw_visual_active = VIsual_active;
    989    VIsual_active = false;
    990  }
    991 
    992  if (tp != NULL) {
    993    switchwin->sw_curtab = curtab;
    994    if (no_display) {
    995      unuse_tabpage(curtab);
    996      use_tabpage(tp);
    997    } else {
    998      goto_tabpage_tp(tp, false, false);
    999    }
   1000  }
   1001  if (!win_valid(win)) {
   1002    return FAIL;
   1003  }
   1004  curwin = win;
   1005  curbuf = curwin->w_buffer;
   1006  return OK;
   1007 }
   1008 
   1009 /// Restore current tabpage and window saved by switch_win(), if still valid.
   1010 /// When "no_display" is true the display won't be affected, no redraw is
   1011 /// triggered.
   1012 void restore_win(switchwin_T *switchwin, bool no_display)
   1013 {
   1014  restore_win_noblock(switchwin, no_display);
   1015  unblock_autocmds();
   1016 }
   1017 
   1018 /// As restore_win() but without unblocking autocommands.
   1019 void restore_win_noblock(switchwin_T *switchwin, bool no_display)
   1020 {
   1021  if (switchwin->sw_curtab != NULL && valid_tabpage(switchwin->sw_curtab)) {
   1022    if (no_display) {
   1023      win_T *const old_tp_curwin = curtab->tp_curwin;
   1024 
   1025      unuse_tabpage(curtab);
   1026      // Don't change the curwin of the tabpage we temporarily visited.
   1027      curtab->tp_curwin = old_tp_curwin;
   1028      use_tabpage(switchwin->sw_curtab);
   1029    } else {
   1030      goto_tabpage_tp(switchwin->sw_curtab, false, false);
   1031    }
   1032  }
   1033 
   1034  if (!switchwin->sw_same_win) {
   1035    VIsual_active = switchwin->sw_visual_active;
   1036  }
   1037 
   1038  if (win_valid(switchwin->sw_curwin)) {
   1039    curwin = switchwin->sw_curwin;
   1040    curbuf = curwin->w_buffer;
   1041  }
   1042 }