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