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