xref: /vim-8.2.3635/src/evalwindow.c (revision b16ad968)
1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
3  * VIM - Vi IMproved	by Bram Moolenaar
4  *
5  * Do ":help uganda"  in Vim to read copying and usage conditions.
6  * Do ":help credits" in Vim to see a list of people who contributed.
7  * See README.txt for an overview of the Vim source code.
8  */
9 
10 /*
11  * evalwindow.c: Window related builtin functions
12  */
13 
14 #include "vim.h"
15 
16 #if defined(FEAT_EVAL) || defined(PROTO)
17 
18     static int
19 win_getid(typval_T *argvars)
20 {
21     int	    winnr;
22     win_T   *wp;
23 
24     if (argvars[0].v_type == VAR_UNKNOWN)
25 	return curwin->w_id;
26     winnr = tv_get_number(&argvars[0]);
27     if (winnr > 0)
28     {
29 	if (argvars[1].v_type == VAR_UNKNOWN)
30 	    wp = firstwin;
31 	else
32 	{
33 	    tabpage_T	*tp;
34 	    int		tabnr = tv_get_number(&argvars[1]);
35 
36 	    FOR_ALL_TABPAGES(tp)
37 		if (--tabnr == 0)
38 		    break;
39 	    if (tp == NULL)
40 		return -1;
41 	    if (tp == curtab)
42 		wp = firstwin;
43 	    else
44 		wp = tp->tp_firstwin;
45 	}
46 	for ( ; wp != NULL; wp = wp->w_next)
47 	    if (--winnr == 0)
48 		return wp->w_id;
49     }
50     return 0;
51 }
52 
53     static void
54 win_id2tabwin(typval_T *argvars, list_T *list)
55 {
56     win_T	*wp;
57     tabpage_T   *tp;
58     int		winnr = 1;
59     int		tabnr = 1;
60     int		id = tv_get_number(&argvars[0]);
61 
62     FOR_ALL_TABPAGES(tp)
63     {
64 	FOR_ALL_WINDOWS_IN_TAB(tp, wp)
65 	{
66 	    if (wp->w_id == id)
67 	    {
68 		list_append_number(list, tabnr);
69 		list_append_number(list, winnr);
70 		return;
71 	    }
72 	    ++winnr;
73 	}
74 	++tabnr;
75 	winnr = 1;
76     }
77     list_append_number(list, 0);
78     list_append_number(list, 0);
79 }
80 
81 /*
82  * Return the window pointer of window "id".
83  */
84     win_T *
85 win_id2wp(int id)
86 {
87     return win_id2wp_tp(id, NULL);
88 }
89 
90 /*
91  * Return the window and tab pointer of window "id".
92  */
93     win_T *
94 win_id2wp_tp(int id, tabpage_T **tpp)
95 {
96     win_T	*wp;
97     tabpage_T   *tp;
98 
99     FOR_ALL_TAB_WINDOWS(tp, wp)
100 	if (wp->w_id == id)
101 	{
102 	    if (tpp != NULL)
103 		*tpp = tp;
104 	    return wp;
105 	}
106 #ifdef FEAT_PROP_POPUP
107     // popup windows are in separate lists
108      FOR_ALL_TABPAGES(tp)
109 	 for (wp = tp->tp_first_popupwin; wp != NULL; wp = wp->w_next)
110 	     if (wp->w_id == id)
111 	     {
112 		 if (tpp != NULL)
113 		     *tpp = tp;
114 		 return wp;
115 	     }
116     for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
117 	if (wp->w_id == id)
118 	{
119 	    if (tpp != NULL)
120 		*tpp = tp;
121 	    return wp;
122 	}
123 #endif
124 
125     return NULL;
126 }
127 
128     static int
129 win_id2win(typval_T *argvars)
130 {
131     win_T   *wp;
132     int	    nr = 1;
133     int	    id = tv_get_number(&argvars[0]);
134 
135     FOR_ALL_WINDOWS(wp)
136     {
137 	if (wp->w_id == id)
138 	    return nr;
139 	++nr;
140     }
141     return 0;
142 }
143 
144     void
145 win_findbuf(typval_T *argvars, list_T *list)
146 {
147     win_T	*wp;
148     tabpage_T   *tp;
149     int		bufnr = tv_get_number(&argvars[0]);
150 
151     FOR_ALL_TAB_WINDOWS(tp, wp)
152 	    if (wp->w_buffer->b_fnum == bufnr)
153 		list_append_number(list, wp->w_id);
154 }
155 
156 /*
157  * Find window specified by "vp" in tabpage "tp".
158  */
159     win_T *
160 find_win_by_nr(
161     typval_T	*vp,
162     tabpage_T	*tp)	// NULL for current tab page
163 {
164     win_T	*wp;
165     int		nr = (int)tv_get_number_chk(vp, NULL);
166 
167     if (nr < 0)
168 	return NULL;
169     if (nr == 0)
170 	return curwin;
171 
172     FOR_ALL_WINDOWS_IN_TAB(tp, wp)
173     {
174 	if (nr >= LOWEST_WIN_ID)
175 	{
176 	    if (wp->w_id == nr)
177 		return wp;
178 	}
179 	else if (--nr <= 0)
180 	    break;
181     }
182     if (nr >= LOWEST_WIN_ID)
183     {
184 #ifdef FEAT_PROP_POPUP
185 	// check tab-local popup windows
186 	for (wp = tp->tp_first_popupwin; wp != NULL; wp = wp->w_next)
187 	    if (wp->w_id == nr)
188 		return wp;
189 	// check global popup windows
190 	for (wp = first_popupwin; wp != NULL; wp = wp->w_next)
191 	    if (wp->w_id == nr)
192 		return wp;
193 #endif
194 	return NULL;
195     }
196     return wp;
197 }
198 
199 /*
200  * Find a window: When using a Window ID in any tab page, when using a number
201  * in the current tab page.
202  * Returns NULL when not found.
203  */
204     win_T *
205 find_win_by_nr_or_id(typval_T *vp)
206 {
207     int	nr = (int)tv_get_number_chk(vp, NULL);
208 
209     if (nr >= LOWEST_WIN_ID)
210 	return win_id2wp(tv_get_number(vp));
211     return find_win_by_nr(vp, NULL);
212 }
213 
214 /*
215  * Find window specified by "wvp" in tabpage "tvp".
216  * Returns the tab page in 'ptp'
217  */
218     win_T *
219 find_tabwin(
220     typval_T	*wvp,	// VAR_UNKNOWN for current window
221     typval_T	*tvp,	// VAR_UNKNOWN for current tab page
222     tabpage_T	**ptp)
223 {
224     win_T	*wp = NULL;
225     tabpage_T	*tp = NULL;
226     long	n;
227 
228     if (wvp->v_type != VAR_UNKNOWN)
229     {
230 	if (tvp->v_type != VAR_UNKNOWN)
231 	{
232 	    n = (long)tv_get_number(tvp);
233 	    if (n >= 0)
234 		tp = find_tabpage(n);
235 	}
236 	else
237 	    tp = curtab;
238 
239 	if (tp != NULL)
240 	{
241 	    wp = find_win_by_nr(wvp, tp);
242 	    if (wp == NULL && wvp->v_type == VAR_NUMBER
243 						&& wvp->vval.v_number != -1)
244 		// A window with the specified number is not found
245 		tp = NULL;
246 	}
247     }
248     else
249     {
250 	wp = curwin;
251 	tp = curtab;
252     }
253 
254     if (ptp != NULL)
255 	*ptp = tp;
256 
257     return wp;
258 }
259 
260 /*
261  * Get the layout of the given tab page for winlayout().
262  */
263     static void
264 get_framelayout(frame_T *fr, list_T *l, int outer)
265 {
266     frame_T	*child;
267     list_T	*fr_list;
268     list_T	*win_list;
269 
270     if (fr == NULL)
271 	return;
272 
273     if (outer)
274 	// outermost call from f_winlayout()
275 	fr_list = l;
276     else
277     {
278 	fr_list = list_alloc();
279 	if (fr_list == NULL)
280 	    return;
281 	list_append_list(l, fr_list);
282     }
283 
284     if (fr->fr_layout == FR_LEAF)
285     {
286 	if (fr->fr_win != NULL)
287 	{
288 	    list_append_string(fr_list, (char_u *)"leaf", -1);
289 	    list_append_number(fr_list, fr->fr_win->w_id);
290 	}
291     }
292     else
293     {
294 	list_append_string(fr_list,
295 	     fr->fr_layout == FR_ROW ?  (char_u *)"row" : (char_u *)"col", -1);
296 
297 	win_list = list_alloc();
298 	if (win_list == NULL)
299 	    return;
300 	list_append_list(fr_list, win_list);
301 	child = fr->fr_child;
302 	while (child != NULL)
303 	{
304 	    get_framelayout(child, win_list, FALSE);
305 	    child = child->fr_next;
306 	}
307     }
308 }
309 
310 /*
311  * Common code for tabpagewinnr() and winnr().
312  */
313     static int
314 get_winnr(tabpage_T *tp, typval_T *argvar)
315 {
316     win_T	*twin;
317     int		nr = 1;
318     win_T	*wp;
319     char_u	*arg;
320 
321     twin = (tp == curtab) ? curwin : tp->tp_curwin;
322     if (argvar->v_type != VAR_UNKNOWN)
323     {
324 	int	invalid_arg = FALSE;
325 
326 	arg = tv_get_string_chk(argvar);
327 	if (arg == NULL)
328 	    nr = 0;		// type error; errmsg already given
329 	else if (STRCMP(arg, "$") == 0)
330 	    twin = (tp == curtab) ? lastwin : tp->tp_lastwin;
331 	else if (STRCMP(arg, "#") == 0)
332 	{
333 	    twin = (tp == curtab) ? prevwin : tp->tp_prevwin;
334 	    if (twin == NULL)
335 		nr = 0;
336 	}
337 	else
338 	{
339 	    long	count;
340 	    char_u	*endp;
341 
342 	    // Extract the window count (if specified). e.g. winnr('3j')
343 	    count = strtol((char *)arg, (char **)&endp, 10);
344 	    if (count <= 0)
345 		count = 1;	// if count is not specified, default to 1
346 	    if (endp != NULL && *endp != '\0')
347 	    {
348 		if (STRCMP(endp, "j") == 0)
349 		    twin = win_vert_neighbor(tp, twin, FALSE, count);
350 		else if (STRCMP(endp, "k") == 0)
351 		    twin = win_vert_neighbor(tp, twin, TRUE, count);
352 		else if (STRCMP(endp, "h") == 0)
353 		    twin = win_horz_neighbor(tp, twin, TRUE, count);
354 		else if (STRCMP(endp, "l") == 0)
355 		    twin = win_horz_neighbor(tp, twin, FALSE, count);
356 		else
357 		    invalid_arg = TRUE;
358 	    }
359 	    else
360 		invalid_arg = TRUE;
361 	}
362 
363 	if (invalid_arg)
364 	{
365 	    semsg(_(e_invexpr2), arg);
366 	    nr = 0;
367 	}
368     }
369 
370     if (nr > 0)
371 	for (wp = (tp == curtab) ? firstwin : tp->tp_firstwin;
372 					      wp != twin; wp = wp->w_next)
373 	{
374 	    if (wp == NULL)
375 	    {
376 		// didn't find it in this tabpage
377 		nr = 0;
378 		break;
379 	    }
380 	    ++nr;
381 	}
382     return nr;
383 }
384 
385 /*
386  * Returns information about a window as a dictionary.
387  */
388     static dict_T *
389 get_win_info(win_T *wp, short tpnr, short winnr)
390 {
391     dict_T	*dict;
392 
393     dict = dict_alloc();
394     if (dict == NULL)
395 	return NULL;
396 
397     dict_add_number(dict, "tabnr", tpnr);
398     dict_add_number(dict, "winnr", winnr);
399     dict_add_number(dict, "winid", wp->w_id);
400     dict_add_number(dict, "height", wp->w_height);
401     dict_add_number(dict, "winrow", wp->w_winrow + 1);
402     dict_add_number(dict, "topline", wp->w_topline);
403     dict_add_number(dict, "botline", wp->w_botline - 1);
404 #ifdef FEAT_MENU
405     dict_add_number(dict, "winbar", wp->w_winbar_height);
406 #endif
407     dict_add_number(dict, "width", wp->w_width);
408     dict_add_number(dict, "wincol", wp->w_wincol + 1);
409     dict_add_number(dict, "bufnr", wp->w_buffer->b_fnum);
410 
411 #ifdef FEAT_TERMINAL
412     dict_add_number(dict, "terminal", bt_terminal(wp->w_buffer));
413 #endif
414 #ifdef FEAT_QUICKFIX
415     dict_add_number(dict, "quickfix", bt_quickfix(wp->w_buffer));
416     dict_add_number(dict, "loclist",
417 		      (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL));
418 #endif
419 
420     // Add a reference to window variables
421     dict_add_dict(dict, "variables", wp->w_vars);
422 
423     return dict;
424 }
425 
426 /*
427  * Returns information (variables, options, etc.) about a tab page
428  * as a dictionary.
429  */
430     static dict_T *
431 get_tabpage_info(tabpage_T *tp, int tp_idx)
432 {
433     win_T	*wp;
434     dict_T	*dict;
435     list_T	*l;
436 
437     dict = dict_alloc();
438     if (dict == NULL)
439 	return NULL;
440 
441     dict_add_number(dict, "tabnr", tp_idx);
442 
443     l = list_alloc();
444     if (l != NULL)
445     {
446 	for (wp = (tp == curtab) ? firstwin : tp->tp_firstwin;
447 						   wp != NULL; wp = wp->w_next)
448 	    list_append_number(l, (varnumber_T)wp->w_id);
449 	dict_add_list(dict, "windows", l);
450     }
451 
452     // Make a reference to tabpage variables
453     dict_add_dict(dict, "variables", tp->tp_vars);
454 
455     return dict;
456 }
457 
458 /*
459  * "gettabinfo()" function
460  */
461     void
462 f_gettabinfo(typval_T *argvars, typval_T *rettv)
463 {
464     tabpage_T	*tp, *tparg = NULL;
465     dict_T	*d;
466     int		tpnr = 0;
467 
468     if (rettv_list_alloc(rettv) != OK)
469 	return;
470 
471     if (argvars[0].v_type != VAR_UNKNOWN)
472     {
473 	// Information about one tab page
474 	tparg = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL));
475 	if (tparg == NULL)
476 	    return;
477     }
478 
479     // Get information about a specific tab page or all tab pages
480     FOR_ALL_TABPAGES(tp)
481     {
482 	tpnr++;
483 	if (tparg != NULL && tp != tparg)
484 	    continue;
485 	d = get_tabpage_info(tp, tpnr);
486 	if (d != NULL)
487 	    list_append_dict(rettv->vval.v_list, d);
488 	if (tparg != NULL)
489 	    return;
490     }
491 }
492 
493 /*
494  * "getwininfo()" function
495  */
496     void
497 f_getwininfo(typval_T *argvars, typval_T *rettv)
498 {
499     tabpage_T	*tp;
500     win_T	*wp = NULL, *wparg = NULL;
501     dict_T	*d;
502     short	tabnr = 0, winnr;
503 
504     if (rettv_list_alloc(rettv) != OK)
505 	return;
506 
507     if (argvars[0].v_type != VAR_UNKNOWN)
508     {
509 	wparg = win_id2wp(tv_get_number(&argvars[0]));
510 	if (wparg == NULL)
511 	    return;
512     }
513 
514     // Collect information about either all the windows across all the tab
515     // pages or one particular window.
516     FOR_ALL_TABPAGES(tp)
517     {
518 	tabnr++;
519 	winnr = 0;
520 	FOR_ALL_WINDOWS_IN_TAB(tp, wp)
521 	{
522 	    winnr++;
523 	    if (wparg != NULL && wp != wparg)
524 		continue;
525 	    d = get_win_info(wp, tabnr, winnr);
526 	    if (d != NULL)
527 		list_append_dict(rettv->vval.v_list, d);
528 	    if (wparg != NULL)
529 		// found information about a specific window
530 		return;
531 	}
532     }
533 }
534 
535 /*
536  * "getwinpos({timeout})" function
537  */
538     void
539 f_getwinpos(typval_T *argvars UNUSED, typval_T *rettv)
540 {
541     int x = -1;
542     int y = -1;
543 
544     if (rettv_list_alloc(rettv) == FAIL)
545 	return;
546 #if defined(FEAT_GUI) \
547 	|| (defined(HAVE_TGETENT) && defined(FEAT_TERMRESPONSE)) \
548 	|| defined(MSWIN)
549     {
550 	varnumber_T timeout = 100;
551 
552 	if (argvars[0].v_type != VAR_UNKNOWN)
553 	    timeout = tv_get_number(&argvars[0]);
554 
555 	(void)ui_get_winpos(&x, &y, timeout);
556     }
557 #endif
558     list_append_number(rettv->vval.v_list, (varnumber_T)x);
559     list_append_number(rettv->vval.v_list, (varnumber_T)y);
560 }
561 
562 
563 /*
564  * "getwinposx()" function
565  */
566     void
567 f_getwinposx(typval_T *argvars UNUSED, typval_T *rettv)
568 {
569     rettv->vval.v_number = -1;
570 #if defined(FEAT_GUI) \
571 	|| (defined(HAVE_TGETENT) && defined(FEAT_TERMRESPONSE)) \
572 	|| defined(MSWIN)
573 
574     {
575 	int	    x, y;
576 
577 	if (ui_get_winpos(&x, &y, 100) == OK)
578 	    rettv->vval.v_number = x;
579     }
580 #endif
581 }
582 
583 /*
584  * "getwinposy()" function
585  */
586     void
587 f_getwinposy(typval_T *argvars UNUSED, typval_T *rettv)
588 {
589     rettv->vval.v_number = -1;
590 #if defined(FEAT_GUI) \
591 	|| (defined(HAVE_TGETENT) && defined(FEAT_TERMRESPONSE)) \
592 	|| defined(MSWIN)
593     {
594 	int	    x, y;
595 
596 	if (ui_get_winpos(&x, &y, 100) == OK)
597 	    rettv->vval.v_number = y;
598     }
599 #endif
600 }
601 
602 /*
603  * "tabpagenr()" function
604  */
605     void
606 f_tabpagenr(typval_T *argvars UNUSED, typval_T *rettv)
607 {
608     int		nr = 1;
609     char_u	*arg;
610 
611     if (argvars[0].v_type != VAR_UNKNOWN)
612     {
613 	arg = tv_get_string_chk(&argvars[0]);
614 	nr = 0;
615 	if (arg != NULL)
616 	{
617 	    if (STRCMP(arg, "$") == 0)
618 		nr = tabpage_index(NULL) - 1;
619 	    else
620 		semsg(_(e_invexpr2), arg);
621 	}
622     }
623     else
624 	nr = tabpage_index(curtab);
625     rettv->vval.v_number = nr;
626 }
627 
628 /*
629  * "tabpagewinnr()" function
630  */
631     void
632 f_tabpagewinnr(typval_T *argvars UNUSED, typval_T *rettv)
633 {
634     int		nr = 1;
635     tabpage_T	*tp;
636 
637     tp = find_tabpage((int)tv_get_number(&argvars[0]));
638     if (tp == NULL)
639 	nr = 0;
640     else
641 	nr = get_winnr(tp, &argvars[1]);
642     rettv->vval.v_number = nr;
643 }
644 
645 /*
646  * "win_execute()" function
647  */
648     void
649 f_win_execute(typval_T *argvars, typval_T *rettv)
650 {
651     int		id = (int)tv_get_number(argvars);
652     tabpage_T	*tp;
653     win_T	*wp = win_id2wp_tp(id, &tp);
654     win_T	*save_curwin;
655     tabpage_T	*save_curtab;
656 
657     if (wp != NULL && tp != NULL)
658     {
659 	pos_T	curpos = wp->w_cursor;
660 
661 	if (switch_win_noblock(&save_curwin, &save_curtab, wp, tp, TRUE) == OK)
662 	{
663 	    check_cursor();
664 	    execute_common(argvars, rettv, 1);
665 	}
666 	restore_win_noblock(save_curwin, save_curtab, TRUE);
667 
668 	// Update the status line if the cursor moved.
669 	if (win_valid(wp) && !EQUAL_POS(curpos, wp->w_cursor))
670 	    wp->w_redr_status = TRUE;
671     }
672 }
673 
674 /*
675  * "win_findbuf()" function
676  */
677     void
678 f_win_findbuf(typval_T *argvars, typval_T *rettv)
679 {
680     if (rettv_list_alloc(rettv) != FAIL)
681 	win_findbuf(argvars, rettv->vval.v_list);
682 }
683 
684 /*
685  * "win_getid()" function
686  */
687     void
688 f_win_getid(typval_T *argvars, typval_T *rettv)
689 {
690     rettv->vval.v_number = win_getid(argvars);
691 }
692 
693 /*
694  * "win_gotoid()" function
695  */
696     void
697 f_win_gotoid(typval_T *argvars, typval_T *rettv)
698 {
699     win_T	*wp;
700     tabpage_T   *tp;
701     int		id = tv_get_number(&argvars[0]);
702 
703 #ifdef FEAT_CMDWIN
704     if (cmdwin_type != 0)
705     {
706 	emsg(_(e_cmdwin));
707 	return;
708     }
709 #endif
710     FOR_ALL_TAB_WINDOWS(tp, wp)
711 	if (wp->w_id == id)
712 	{
713 	    goto_tabpage_win(tp, wp);
714 	    rettv->vval.v_number = 1;
715 	    return;
716 	}
717 }
718 
719 /*
720  * "win_id2tabwin()" function
721  */
722     void
723 f_win_id2tabwin(typval_T *argvars, typval_T *rettv)
724 {
725     if (rettv_list_alloc(rettv) != FAIL)
726 	win_id2tabwin(argvars, rettv->vval.v_list);
727 }
728 
729 /*
730  * "win_id2win()" function
731  */
732     void
733 f_win_id2win(typval_T *argvars, typval_T *rettv)
734 {
735     rettv->vval.v_number = win_id2win(argvars);
736 }
737 
738 /*
739  * "win_screenpos()" function
740  */
741     void
742 f_win_screenpos(typval_T *argvars, typval_T *rettv)
743 {
744     win_T	*wp;
745 
746     if (rettv_list_alloc(rettv) == FAIL)
747 	return;
748 
749     wp = find_win_by_nr_or_id(&argvars[0]);
750     list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_winrow + 1);
751     list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_wincol + 1);
752 }
753 
754 /*
755  * Move the window wp into a new split of targetwin in a given direction
756  */
757     static void
758 win_move_into_split(win_T *wp, win_T *targetwin, int size, int flags)
759 {
760     int	    dir;
761     int	    height = wp->w_height;
762     win_T   *oldwin = curwin;
763 
764     if (wp == targetwin)
765 	return;
766 
767     // Jump to the target window
768     if (curwin != targetwin)
769 	win_goto(targetwin);
770 
771     // Remove the old window and frame from the tree of frames
772     (void)winframe_remove(wp, &dir, NULL);
773     win_remove(wp, NULL);
774     last_status(FALSE);	    // may need to remove last status line
775     (void)win_comp_pos();   // recompute window positions
776 
777     // Split a window on the desired side and put the old window there
778     (void)win_split_ins(size, flags, wp, dir);
779 
780     // If splitting horizontally, try to preserve height
781     if (size == 0 && !(flags & WSP_VERT))
782     {
783 	win_setheight_win(height, wp);
784 	if (p_ea)
785 	    win_equal(wp, TRUE, 'v');
786     }
787 
788 #if defined(FEAT_GUI)
789     // When 'guioptions' includes 'L' or 'R' may have to remove or add
790     // scrollbars.  Have to update them anyway.
791     gui_may_update_scrollbars();
792 #endif
793 
794     if (oldwin != curwin)
795 	win_goto(oldwin);
796 }
797 
798 /*
799  * "win_splitmove()" function
800  */
801     void
802 f_win_splitmove(typval_T *argvars, typval_T *rettv)
803 {
804     win_T   *wp;
805     win_T   *targetwin;
806     int     flags = 0, size = 0;
807 
808     wp = find_win_by_nr_or_id(&argvars[0]);
809     targetwin = find_win_by_nr_or_id(&argvars[1]);
810 
811     if (wp == NULL || targetwin == NULL || wp == targetwin
812 	    || !win_valid(wp) || !win_valid(targetwin))
813     {
814         emsg(_(e_invalwindow));
815 	rettv->vval.v_number = -1;
816 	return;
817     }
818 
819     if (argvars[2].v_type != VAR_UNKNOWN)
820     {
821         dict_T      *d;
822         dictitem_T  *di;
823 
824         if (argvars[2].v_type != VAR_DICT || argvars[2].vval.v_dict == NULL)
825         {
826             emsg(_(e_invarg));
827             return;
828         }
829 
830         d = argvars[2].vval.v_dict;
831         if (dict_get_number(d, (char_u *)"vertical"))
832             flags |= WSP_VERT;
833         if ((di = dict_find(d, (char_u *)"rightbelow", -1)) != NULL)
834             flags |= tv_get_number(&di->di_tv) ? WSP_BELOW : WSP_ABOVE;
835         size = (int)dict_get_number(d, (char_u *)"size");
836     }
837 
838     win_move_into_split(wp, targetwin, size, flags);
839 }
840 
841 /*
842  * "winbufnr(nr)" function
843  */
844     void
845 f_winbufnr(typval_T *argvars, typval_T *rettv)
846 {
847     win_T	*wp;
848 
849     wp = find_win_by_nr_or_id(&argvars[0]);
850     if (wp == NULL)
851 	rettv->vval.v_number = -1;
852     else
853 	rettv->vval.v_number = wp->w_buffer->b_fnum;
854 }
855 
856 /*
857  * "wincol()" function
858  */
859     void
860 f_wincol(typval_T *argvars UNUSED, typval_T *rettv)
861 {
862     validate_cursor();
863     rettv->vval.v_number = curwin->w_wcol + 1;
864 }
865 
866 /*
867  * "winheight(nr)" function
868  */
869     void
870 f_winheight(typval_T *argvars, typval_T *rettv)
871 {
872     win_T	*wp;
873 
874     wp = find_win_by_nr_or_id(&argvars[0]);
875     if (wp == NULL)
876 	rettv->vval.v_number = -1;
877     else
878 	rettv->vval.v_number = wp->w_height;
879 }
880 
881 /*
882  * "winlayout()" function
883  */
884     void
885 f_winlayout(typval_T *argvars, typval_T *rettv)
886 {
887     tabpage_T	*tp;
888 
889     if (rettv_list_alloc(rettv) != OK)
890 	return;
891 
892     if (argvars[0].v_type == VAR_UNKNOWN)
893 	tp = curtab;
894     else
895     {
896 	tp = find_tabpage((int)tv_get_number(&argvars[0]));
897 	if (tp == NULL)
898 	    return;
899     }
900 
901     get_framelayout(tp->tp_topframe, rettv->vval.v_list, TRUE);
902 }
903 
904 /*
905  * "winline()" function
906  */
907     void
908 f_winline(typval_T *argvars UNUSED, typval_T *rettv)
909 {
910     validate_cursor();
911     rettv->vval.v_number = curwin->w_wrow + 1;
912 }
913 
914 /*
915  * "winnr()" function
916  */
917     void
918 f_winnr(typval_T *argvars UNUSED, typval_T *rettv)
919 {
920     int		nr = 1;
921 
922     nr = get_winnr(curtab, &argvars[0]);
923     rettv->vval.v_number = nr;
924 }
925 
926 /*
927  * "winrestcmd()" function
928  */
929     void
930 f_winrestcmd(typval_T *argvars UNUSED, typval_T *rettv)
931 {
932     win_T	*wp;
933     int		winnr = 1;
934     garray_T	ga;
935     char_u	buf[50];
936 
937     ga_init2(&ga, (int)sizeof(char), 70);
938     FOR_ALL_WINDOWS(wp)
939     {
940 	sprintf((char *)buf, "%dresize %d|", winnr, wp->w_height);
941 	ga_concat(&ga, buf);
942 	sprintf((char *)buf, "vert %dresize %d|", winnr, wp->w_width);
943 	ga_concat(&ga, buf);
944 	++winnr;
945     }
946     ga_append(&ga, NUL);
947 
948     rettv->vval.v_string = ga.ga_data;
949     rettv->v_type = VAR_STRING;
950 }
951 
952 /*
953  * "winrestview()" function
954  */
955     void
956 f_winrestview(typval_T *argvars, typval_T *rettv UNUSED)
957 {
958     dict_T	*dict;
959 
960     if (argvars[0].v_type != VAR_DICT
961 	    || (dict = argvars[0].vval.v_dict) == NULL)
962 	emsg(_(e_invarg));
963     else
964     {
965 	if (dict_find(dict, (char_u *)"lnum", -1) != NULL)
966 	    curwin->w_cursor.lnum = (linenr_T)dict_get_number(dict, (char_u *)"lnum");
967 	if (dict_find(dict, (char_u *)"col", -1) != NULL)
968 	    curwin->w_cursor.col = (colnr_T)dict_get_number(dict, (char_u *)"col");
969 	if (dict_find(dict, (char_u *)"coladd", -1) != NULL)
970 	    curwin->w_cursor.coladd = (colnr_T)dict_get_number(dict, (char_u *)"coladd");
971 	if (dict_find(dict, (char_u *)"curswant", -1) != NULL)
972 	{
973 	    curwin->w_curswant = (colnr_T)dict_get_number(dict, (char_u *)"curswant");
974 	    curwin->w_set_curswant = FALSE;
975 	}
976 
977 	if (dict_find(dict, (char_u *)"topline", -1) != NULL)
978 	    set_topline(curwin, (linenr_T)dict_get_number(dict, (char_u *)"topline"));
979 #ifdef FEAT_DIFF
980 	if (dict_find(dict, (char_u *)"topfill", -1) != NULL)
981 	    curwin->w_topfill = (int)dict_get_number(dict, (char_u *)"topfill");
982 #endif
983 	if (dict_find(dict, (char_u *)"leftcol", -1) != NULL)
984 	    curwin->w_leftcol = (colnr_T)dict_get_number(dict, (char_u *)"leftcol");
985 	if (dict_find(dict, (char_u *)"skipcol", -1) != NULL)
986 	    curwin->w_skipcol = (colnr_T)dict_get_number(dict, (char_u *)"skipcol");
987 
988 	check_cursor();
989 	win_new_height(curwin, curwin->w_height);
990 	win_new_width(curwin, curwin->w_width);
991 	changed_window_setting();
992 
993 	if (curwin->w_topline <= 0)
994 	    curwin->w_topline = 1;
995 	if (curwin->w_topline > curbuf->b_ml.ml_line_count)
996 	    curwin->w_topline = curbuf->b_ml.ml_line_count;
997 #ifdef FEAT_DIFF
998 	check_topfill(curwin, TRUE);
999 #endif
1000     }
1001 }
1002 
1003 /*
1004  * "winsaveview()" function
1005  */
1006     void
1007 f_winsaveview(typval_T *argvars UNUSED, typval_T *rettv)
1008 {
1009     dict_T	*dict;
1010 
1011     if (rettv_dict_alloc(rettv) == FAIL)
1012 	return;
1013     dict = rettv->vval.v_dict;
1014 
1015     dict_add_number(dict, "lnum", (long)curwin->w_cursor.lnum);
1016     dict_add_number(dict, "col", (long)curwin->w_cursor.col);
1017     dict_add_number(dict, "coladd", (long)curwin->w_cursor.coladd);
1018     update_curswant();
1019     dict_add_number(dict, "curswant", (long)curwin->w_curswant);
1020 
1021     dict_add_number(dict, "topline", (long)curwin->w_topline);
1022 #ifdef FEAT_DIFF
1023     dict_add_number(dict, "topfill", (long)curwin->w_topfill);
1024 #endif
1025     dict_add_number(dict, "leftcol", (long)curwin->w_leftcol);
1026     dict_add_number(dict, "skipcol", (long)curwin->w_skipcol);
1027 }
1028 
1029 /*
1030  * "winwidth(nr)" function
1031  */
1032     void
1033 f_winwidth(typval_T *argvars, typval_T *rettv)
1034 {
1035     win_T	*wp;
1036 
1037     wp = find_win_by_nr_or_id(&argvars[0]);
1038     if (wp == NULL)
1039 	rettv->vval.v_number = -1;
1040     else
1041 	rettv->vval.v_number = wp->w_width;
1042 }
1043 #endif // FEAT_EVAL
1044 
1045 #if defined(FEAT_EVAL) || defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) \
1046 	|| defined(PROTO)
1047 /*
1048  * Set "win" to be the curwin and "tp" to be the current tab page.
1049  * restore_win() MUST be called to undo, also when FAIL is returned.
1050  * No autocommands will be executed until restore_win() is called.
1051  * When "no_display" is TRUE the display won't be affected, no redraw is
1052  * triggered, another tabpage access is limited.
1053  * Returns FAIL if switching to "win" failed.
1054  */
1055     int
1056 switch_win(
1057     win_T	**save_curwin,
1058     tabpage_T	**save_curtab,
1059     win_T	*win,
1060     tabpage_T	*tp,
1061     int		no_display)
1062 {
1063     block_autocmds();
1064     return switch_win_noblock(save_curwin, save_curtab, win, tp, no_display);
1065 }
1066 
1067 /*
1068  * As switch_win() but without blocking autocommands.
1069  */
1070     int
1071 switch_win_noblock(
1072     win_T	**save_curwin,
1073     tabpage_T	**save_curtab,
1074     win_T	*win,
1075     tabpage_T	*tp,
1076     int		no_display)
1077 {
1078     *save_curwin = curwin;
1079     if (tp != NULL)
1080     {
1081 	*save_curtab = curtab;
1082 	if (no_display)
1083 	{
1084 	    curtab->tp_firstwin = firstwin;
1085 	    curtab->tp_lastwin = lastwin;
1086 	    curtab = tp;
1087 	    firstwin = curtab->tp_firstwin;
1088 	    lastwin = curtab->tp_lastwin;
1089 	}
1090 	else
1091 	    goto_tabpage_tp(tp, FALSE, FALSE);
1092     }
1093     if (!win_valid(win))
1094 	return FAIL;
1095     curwin = win;
1096     curbuf = curwin->w_buffer;
1097     return OK;
1098 }
1099 
1100 /*
1101  * Restore current tabpage and window saved by switch_win(), if still valid.
1102  * When "no_display" is TRUE the display won't be affected, no redraw is
1103  * triggered.
1104  */
1105     void
1106 restore_win(
1107     win_T	*save_curwin,
1108     tabpage_T	*save_curtab,
1109     int		no_display)
1110 {
1111     restore_win_noblock(save_curwin, save_curtab, no_display);
1112     unblock_autocmds();
1113 }
1114 
1115 /*
1116  * As restore_win() but without unblocking autocommands.
1117  */
1118     void
1119 restore_win_noblock(
1120     win_T	*save_curwin,
1121     tabpage_T	*save_curtab,
1122     int		no_display)
1123 {
1124     if (save_curtab != NULL && valid_tabpage(save_curtab))
1125     {
1126 	if (no_display)
1127 	{
1128 	    curtab->tp_firstwin = firstwin;
1129 	    curtab->tp_lastwin = lastwin;
1130 	    curtab = save_curtab;
1131 	    firstwin = curtab->tp_firstwin;
1132 	    lastwin = curtab->tp_lastwin;
1133 	}
1134 	else
1135 	    goto_tabpage_tp(save_curtab, FALSE, FALSE);
1136     }
1137     if (win_valid(save_curwin))
1138     {
1139 	curwin = save_curwin;
1140 	curbuf = curwin->w_buffer;
1141     }
1142 # ifdef FEAT_PROP_POPUP
1143     else if (WIN_IS_POPUP(curwin))
1144 	// original window was closed and now we're in a popup window: Go
1145 	// to the first valid window.
1146 	win_goto(firstwin);
1147 # endif
1148 }
1149 #endif
1150