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 }