mirror of
https://github.com/neovim/neovim
synced 2025-07-16 01:01:49 +00:00
Problem: Non-visible/focusable windows are assigned a window number,
whereas commands that use this window number skip over them.
Solution: Skip over non-visible/focusable windows when computing
the window number, unless it is made the current window
through the API in which case an identifiable window number
is still useful. This also ensures it matches the window
number of the window entered by `<winnr>wincmd w` since
403fcacf
.
1020 lines
26 KiB
C
1020 lines
26 KiB
C
// eval/window.c: Window related builtin functions
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "nvim/ascii_defs.h"
|
|
#include "nvim/autocmd.h"
|
|
#include "nvim/buffer.h"
|
|
#include "nvim/cursor.h"
|
|
#include "nvim/errors.h"
|
|
#include "nvim/eval/funcs.h"
|
|
#include "nvim/eval/typval.h"
|
|
#include "nvim/eval/typval_defs.h"
|
|
#include "nvim/eval/window.h"
|
|
#include "nvim/ex_getln.h"
|
|
#include "nvim/garray.h"
|
|
#include "nvim/garray_defs.h"
|
|
#include "nvim/gettext_defs.h"
|
|
#include "nvim/globals.h"
|
|
#include "nvim/macros_defs.h"
|
|
#include "nvim/mark_defs.h"
|
|
#include "nvim/memory.h"
|
|
#include "nvim/message.h"
|
|
#include "nvim/move.h"
|
|
#include "nvim/option_vars.h"
|
|
#include "nvim/os/fs.h"
|
|
#include "nvim/pos_defs.h"
|
|
#include "nvim/types_defs.h"
|
|
#include "nvim/vim_defs.h"
|
|
#include "nvim/window.h"
|
|
|
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
|
# include "eval/window.c.generated.h"
|
|
#endif
|
|
|
|
static const char *e_invalwindow = N_("E957: Invalid window number");
|
|
static const char e_cannot_resize_window_in_another_tab_page[]
|
|
= N_("E1308: Cannot resize a window in another tab page");
|
|
|
|
bool win_has_winnr(win_T *wp)
|
|
{
|
|
return wp == curwin || (!wp->w_config.hide && wp->w_config.focusable);
|
|
}
|
|
|
|
static int win_getid(typval_T *argvars)
|
|
{
|
|
if (argvars[0].v_type == VAR_UNKNOWN) {
|
|
return curwin->handle;
|
|
}
|
|
int winnr = (int)tv_get_number(&argvars[0]);
|
|
win_T *wp;
|
|
if (winnr <= 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (argvars[1].v_type == VAR_UNKNOWN) {
|
|
wp = firstwin;
|
|
} else {
|
|
tabpage_T *tp = NULL;
|
|
int tabnr = (int)tv_get_number(&argvars[1]);
|
|
FOR_ALL_TABS(tp2) {
|
|
if (--tabnr == 0) {
|
|
tp = tp2;
|
|
break;
|
|
}
|
|
}
|
|
if (tp == NULL) {
|
|
return -1;
|
|
}
|
|
if (tp == curtab) {
|
|
wp = firstwin;
|
|
} else {
|
|
wp = tp->tp_firstwin;
|
|
}
|
|
}
|
|
for (; wp != NULL; wp = wp->w_next) {
|
|
if ((winnr -= win_has_winnr(wp)) == 0) {
|
|
return wp->handle;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void win_id2tabwin(typval_T *const argvars, typval_T *const rettv)
|
|
{
|
|
handle_T id = (handle_T)tv_get_number(&argvars[0]);
|
|
|
|
int winnr = 1;
|
|
int tabnr = 1;
|
|
win_get_tabwin(id, &tabnr, &winnr);
|
|
|
|
list_T *const list = tv_list_alloc_ret(rettv, 2);
|
|
tv_list_append_number(list, tabnr);
|
|
tv_list_append_number(list, winnr);
|
|
}
|
|
|
|
win_T *win_id2wp(int id)
|
|
{
|
|
return win_id2wp_tp(id, NULL);
|
|
}
|
|
|
|
/// Return the window and tab pointer of window "id".
|
|
/// Returns NULL when not found.
|
|
win_T *win_id2wp_tp(int id, tabpage_T **tpp)
|
|
{
|
|
FOR_ALL_TAB_WINDOWS(tp, wp) {
|
|
if (wp->handle == id) {
|
|
if (tpp != NULL) {
|
|
*tpp = tp;
|
|
}
|
|
return wp;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int win_id2win(typval_T *argvars)
|
|
{
|
|
int nr = 1;
|
|
int id = (int)tv_get_number(&argvars[0]);
|
|
|
|
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
|
|
if (wp->handle == id) {
|
|
return (win_has_winnr(wp) ? nr : 0);
|
|
}
|
|
nr += win_has_winnr(wp);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void win_findbuf(typval_T *argvars, list_T *list)
|
|
{
|
|
int bufnr = (int)tv_get_number(&argvars[0]);
|
|
|
|
FOR_ALL_TAB_WINDOWS(tp, wp) {
|
|
if (wp->w_buffer->b_fnum == bufnr) {
|
|
tv_list_append_number(list, wp->handle);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Find window specified by "vp" in tabpage "tp".
|
|
///
|
|
/// @param tp NULL for current tab page
|
|
/// @return current window if "vp" is number zero.
|
|
/// NULL if not found.
|
|
win_T *find_win_by_nr(typval_T *vp, tabpage_T *tp)
|
|
{
|
|
int nr = (int)tv_get_number_chk(vp, NULL);
|
|
|
|
if (nr < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
if (nr == 0) {
|
|
return curwin;
|
|
}
|
|
|
|
// This method accepts NULL as an alias for curtab.
|
|
if (tp == NULL) {
|
|
tp = curtab;
|
|
}
|
|
|
|
FOR_ALL_WINDOWS_IN_TAB(wp, tp) {
|
|
if (nr >= LOWEST_WIN_ID) {
|
|
if (wp->handle == nr) {
|
|
return wp;
|
|
}
|
|
} else if (--nr <= 0) {
|
|
return wp;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/// Find a window: When using a Window ID in any tab page, when using a number
|
|
/// in the current tab page.
|
|
win_T *find_win_by_nr_or_id(typval_T *vp)
|
|
{
|
|
int nr = (int)tv_get_number_chk(vp, NULL);
|
|
|
|
if (nr >= LOWEST_WIN_ID) {
|
|
return win_id2wp((int)tv_get_number(vp));
|
|
}
|
|
|
|
return find_win_by_nr(vp, NULL);
|
|
}
|
|
|
|
/// Find window specified by "wvp" in tabpage "tvp".
|
|
win_T *find_tabwin(typval_T *wvp, typval_T *tvp)
|
|
{
|
|
win_T *wp = NULL;
|
|
tabpage_T *tp = NULL;
|
|
|
|
if (wvp->v_type != VAR_UNKNOWN) {
|
|
if (tvp->v_type != VAR_UNKNOWN) {
|
|
int n = (int)tv_get_number(tvp);
|
|
if (n >= 0) {
|
|
tp = find_tabpage(n);
|
|
}
|
|
} else {
|
|
tp = curtab;
|
|
}
|
|
|
|
if (tp != NULL) {
|
|
wp = find_win_by_nr(wvp, tp);
|
|
}
|
|
} else {
|
|
wp = curwin;
|
|
}
|
|
|
|
return wp;
|
|
}
|
|
|
|
/// Get the layout of the given tab page for winlayout().
|
|
static void get_framelayout(const frame_T *fr, list_T *l, bool outer)
|
|
{
|
|
if (fr == NULL) {
|
|
return;
|
|
}
|
|
|
|
list_T *fr_list;
|
|
if (outer) {
|
|
// outermost call from f_winlayout()
|
|
fr_list = l;
|
|
} else {
|
|
fr_list = tv_list_alloc(2);
|
|
tv_list_append_list(l, fr_list);
|
|
}
|
|
|
|
if (fr->fr_layout == FR_LEAF) {
|
|
if (fr->fr_win != NULL) {
|
|
tv_list_append_string(fr_list, "leaf", -1);
|
|
tv_list_append_number(fr_list, fr->fr_win->handle);
|
|
}
|
|
} else {
|
|
tv_list_append_string(fr_list, fr->fr_layout == FR_ROW ? "row" : "col", -1);
|
|
|
|
list_T *const win_list = tv_list_alloc(kListLenUnknown);
|
|
tv_list_append_list(fr_list, win_list);
|
|
const frame_T *child = fr->fr_child;
|
|
while (child != NULL) {
|
|
get_framelayout(child, win_list, false);
|
|
child = child->fr_next;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Common code for tabpagewinnr() and winnr().
|
|
static int get_winnr(tabpage_T *tp, typval_T *argvar)
|
|
{
|
|
int nr = 1;
|
|
|
|
win_T *twin = (tp == curtab) ? curwin : tp->tp_curwin;
|
|
if (argvar->v_type != VAR_UNKNOWN) {
|
|
bool invalid_arg = false;
|
|
const char *const arg = tv_get_string_chk(argvar);
|
|
if (arg == NULL) {
|
|
nr = 0; // Type error; errmsg already given.
|
|
} else if (strcmp(arg, "$") == 0) {
|
|
twin = (tp == curtab) ? lastwin : tp->tp_lastwin;
|
|
} else if (strcmp(arg, "#") == 0) {
|
|
twin = (tp == curtab) ? prevwin : tp->tp_prevwin;
|
|
if (twin == NULL) {
|
|
nr = 0;
|
|
}
|
|
} else {
|
|
// Extract the window count (if specified). e.g. winnr('3j')
|
|
char *endp;
|
|
int count = (int)strtol(arg, &endp, 10);
|
|
if (count <= 0) {
|
|
// if count is not specified, default to 1
|
|
count = 1;
|
|
}
|
|
if (endp != NULL && *endp != NUL) {
|
|
if (strequal(endp, "j")) {
|
|
twin = win_vert_neighbor(tp, twin, false, count);
|
|
} else if (strequal(endp, "k")) {
|
|
twin = win_vert_neighbor(tp, twin, true, count);
|
|
} else if (strequal(endp, "h")) {
|
|
twin = win_horz_neighbor(tp, twin, true, count);
|
|
} else if (strequal(endp, "l")) {
|
|
twin = win_horz_neighbor(tp, twin, false, count);
|
|
} else {
|
|
invalid_arg = true;
|
|
}
|
|
} else {
|
|
invalid_arg = true;
|
|
}
|
|
}
|
|
|
|
if (invalid_arg) {
|
|
semsg(_(e_invexpr2), arg);
|
|
nr = 0;
|
|
}
|
|
} else if (!win_has_winnr(twin)) {
|
|
nr = 0;
|
|
}
|
|
|
|
if (nr <= 0) {
|
|
return 0;
|
|
}
|
|
|
|
nr = 0;
|
|
win_T *wp = (tp == curtab) ? firstwin : tp->tp_firstwin;
|
|
for (; wp != NULL; wp = wp->w_next) {
|
|
nr += win_has_winnr(wp);
|
|
if (wp == twin) {
|
|
break;
|
|
}
|
|
}
|
|
if (wp == NULL) {
|
|
nr = 0; // didn't find it in this tabpage
|
|
}
|
|
return nr;
|
|
}
|
|
|
|
/// @return information about a window as a dictionary.
|
|
static dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr)
|
|
{
|
|
dict_T *const dict = tv_dict_alloc();
|
|
|
|
// make sure w_botline is valid
|
|
validate_botline(wp);
|
|
|
|
tv_dict_add_nr(dict, S_LEN("tabnr"), tpnr);
|
|
tv_dict_add_nr(dict, S_LEN("winnr"), winnr);
|
|
tv_dict_add_nr(dict, S_LEN("winid"), wp->handle);
|
|
tv_dict_add_nr(dict, S_LEN("height"), wp->w_view_height);
|
|
tv_dict_add_nr(dict, S_LEN("winrow"), wp->w_winrow + 1);
|
|
tv_dict_add_nr(dict, S_LEN("topline"), wp->w_topline);
|
|
tv_dict_add_nr(dict, S_LEN("botline"), wp->w_botline - 1);
|
|
tv_dict_add_nr(dict, S_LEN("leftcol"), wp->w_leftcol);
|
|
tv_dict_add_nr(dict, S_LEN("winbar"), wp->w_winbar_height);
|
|
tv_dict_add_nr(dict, S_LEN("width"), wp->w_view_width);
|
|
tv_dict_add_nr(dict, S_LEN("bufnr"), wp->w_buffer->b_fnum);
|
|
tv_dict_add_nr(dict, S_LEN("wincol"), wp->w_wincol + 1);
|
|
tv_dict_add_nr(dict, S_LEN("textoff"), win_col_off(wp));
|
|
tv_dict_add_nr(dict, S_LEN("terminal"), bt_terminal(wp->w_buffer));
|
|
tv_dict_add_nr(dict, S_LEN("quickfix"), bt_quickfix(wp->w_buffer));
|
|
tv_dict_add_nr(dict, S_LEN("loclist"),
|
|
(bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL));
|
|
|
|
// Add a reference to window variables
|
|
tv_dict_add_dict(dict, S_LEN("variables"), wp->w_vars);
|
|
|
|
return dict;
|
|
}
|
|
|
|
/// @return information (variables, options, etc.) about a tab page
|
|
/// as a dictionary.
|
|
static dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx)
|
|
{
|
|
dict_T *const dict = tv_dict_alloc();
|
|
|
|
tv_dict_add_nr(dict, S_LEN("tabnr"), tp_idx);
|
|
|
|
list_T *const l = tv_list_alloc(kListLenMayKnow);
|
|
FOR_ALL_WINDOWS_IN_TAB(wp, tp) {
|
|
tv_list_append_number(l, (varnumber_T)wp->handle);
|
|
}
|
|
tv_dict_add_list(dict, S_LEN("windows"), l);
|
|
|
|
// Make a reference to tabpage variables
|
|
tv_dict_add_dict(dict, S_LEN("variables"), tp->tp_vars);
|
|
|
|
return dict;
|
|
}
|
|
|
|
/// "gettabinfo()" function
|
|
void f_gettabinfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|
{
|
|
tabpage_T *tparg = NULL;
|
|
|
|
tv_list_alloc_ret(rettv, (argvars[0].v_type == VAR_UNKNOWN
|
|
? 1
|
|
: kListLenMayKnow));
|
|
|
|
if (argvars[0].v_type != VAR_UNKNOWN) {
|
|
// Information about one tab page
|
|
tparg = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL));
|
|
if (tparg == NULL) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Get information about a specific tab page or all tab pages
|
|
int tpnr = 0;
|
|
FOR_ALL_TABS(tp) {
|
|
tpnr++;
|
|
if (tparg != NULL && tp != tparg) {
|
|
continue;
|
|
}
|
|
dict_T *const d = get_tabpage_info(tp, tpnr);
|
|
tv_list_append_dict(rettv->vval.v_list, d);
|
|
if (tparg != NULL) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// "getwininfo()" function
|
|
void f_getwininfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|
{
|
|
win_T *wparg = NULL;
|
|
|
|
tv_list_alloc_ret(rettv, kListLenMayKnow);
|
|
|
|
if (argvars[0].v_type != VAR_UNKNOWN) {
|
|
wparg = win_id2wp((int)tv_get_number(&argvars[0]));
|
|
if (wparg == NULL) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Collect information about either all the windows across all the tab
|
|
// pages or one particular window.
|
|
int16_t tabnr = 0;
|
|
FOR_ALL_TABS(tp) {
|
|
tabnr++;
|
|
int16_t winnr = 0;
|
|
FOR_ALL_WINDOWS_IN_TAB(wp, tp) {
|
|
winnr += win_has_winnr(wp);
|
|
if (wparg != NULL && wp != wparg) {
|
|
continue;
|
|
}
|
|
dict_T *const d = get_win_info(wp, tabnr, winnr);
|
|
tv_list_append_dict(rettv->vval.v_list, d);
|
|
if (wparg != NULL) {
|
|
// found information about a specific window
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// "getwinpos({timeout})" function
|
|
void f_getwinpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|
{
|
|
tv_list_alloc_ret(rettv, 2);
|
|
tv_list_append_number(rettv->vval.v_list, -1);
|
|
tv_list_append_number(rettv->vval.v_list, -1);
|
|
}
|
|
|
|
/// "getwinposx()" function
|
|
void f_getwinposx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|
{
|
|
rettv->vval.v_number = -1;
|
|
}
|
|
|
|
/// "getwinposy()" function
|
|
void f_getwinposy(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|
{
|
|
rettv->vval.v_number = -1;
|
|
}
|
|
|
|
/// "tabpagenr()" function
|
|
void f_tabpagenr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|
{
|
|
int nr = 1;
|
|
|
|
if (argvars[0].v_type != VAR_UNKNOWN) {
|
|
const char *const arg = tv_get_string_chk(&argvars[0]);
|
|
nr = 0;
|
|
if (arg != NULL) {
|
|
if (strcmp(arg, "$") == 0) {
|
|
nr = tabpage_index(NULL) - 1;
|
|
} else if (strcmp(arg, "#") == 0) {
|
|
nr = valid_tabpage(lastused_tabpage) ? tabpage_index(lastused_tabpage) : 0;
|
|
} else {
|
|
semsg(_(e_invexpr2), arg);
|
|
}
|
|
}
|
|
} else {
|
|
nr = tabpage_index(curtab);
|
|
}
|
|
rettv->vval.v_number = nr;
|
|
}
|
|
|
|
/// "tabpagewinnr()" function
|
|
void f_tabpagewinnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|
{
|
|
int nr = 1;
|
|
tabpage_T *const tp = find_tabpage((int)tv_get_number(&argvars[0]));
|
|
if (tp == NULL) {
|
|
nr = 0;
|
|
} else {
|
|
nr = get_winnr(tp, &argvars[1]);
|
|
}
|
|
rettv->vval.v_number = nr;
|
|
}
|
|
|
|
/// Switch to a window for executing user code.
|
|
/// Caller must call win_execute_after() later regardless of return value.
|
|
///
|
|
/// @return whether switching the window succeeded.
|
|
bool win_execute_before(win_execute_T *args, win_T *wp, tabpage_T *tp)
|
|
{
|
|
args->wp = wp;
|
|
args->curpos = wp->w_cursor;
|
|
args->cwd_status = FAIL;
|
|
args->apply_acd = false;
|
|
|
|
// Getting and setting directory can be slow on some systems, only do
|
|
// this when the current or target window/tab have a local directory or
|
|
// 'acd' is set.
|
|
if (curwin != wp
|
|
&& (curwin->w_localdir != NULL || wp->w_localdir != NULL
|
|
|| (curtab != tp && (curtab->tp_localdir != NULL || tp->tp_localdir != NULL))
|
|
|| p_acd)) {
|
|
args->cwd_status = os_dirname(args->cwd, MAXPATHL);
|
|
}
|
|
|
|
// If 'acd' is set, check we are using that directory. If yes, then
|
|
// apply 'acd' afterwards, otherwise restore the current directory.
|
|
if (args->cwd_status == OK && p_acd) {
|
|
do_autochdir();
|
|
char autocwd[MAXPATHL];
|
|
if (os_dirname(autocwd, MAXPATHL) == OK) {
|
|
args->apply_acd = strcmp(args->cwd, autocwd) == 0;
|
|
}
|
|
}
|
|
|
|
if (switch_win_noblock(&args->switchwin, wp, tp, true) == OK) {
|
|
check_cursor(curwin);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Restore the previous window after executing user code.
|
|
void win_execute_after(win_execute_T *args)
|
|
{
|
|
restore_win_noblock(&args->switchwin, true);
|
|
|
|
if (args->apply_acd) {
|
|
do_autochdir();
|
|
} else if (args->cwd_status == OK) {
|
|
os_chdir(args->cwd);
|
|
}
|
|
|
|
// Update the status line if the cursor moved.
|
|
if (win_valid(args->wp) && !equalpos(args->curpos, args->wp->w_cursor)) {
|
|
args->wp->w_redr_status = true;
|
|
}
|
|
|
|
// In case the command moved the cursor or changed the Visual area,
|
|
// check it is valid.
|
|
check_cursor(curwin);
|
|
if (VIsual_active) {
|
|
check_pos(curbuf, &VIsual);
|
|
}
|
|
}
|
|
|
|
/// "win_execute(win_id, command)" function
|
|
void f_win_execute(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|
{
|
|
// Return an empty string if something fails.
|
|
rettv->v_type = VAR_STRING;
|
|
rettv->vval.v_string = NULL;
|
|
|
|
int id = (int)tv_get_number(argvars);
|
|
tabpage_T *tp;
|
|
win_T *wp = win_id2wp_tp(id, &tp);
|
|
if (wp == NULL || tp == NULL) {
|
|
return;
|
|
}
|
|
|
|
win_execute_T win_execute_args;
|
|
if (win_execute_before(&win_execute_args, wp, tp)) {
|
|
execute_common(argvars, rettv, 1);
|
|
}
|
|
win_execute_after(&win_execute_args);
|
|
}
|
|
|
|
/// "win_findbuf()" function
|
|
void f_win_findbuf(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|
{
|
|
tv_list_alloc_ret(rettv, kListLenMayKnow);
|
|
win_findbuf(argvars, rettv->vval.v_list);
|
|
}
|
|
|
|
/// "win_getid()" function
|
|
void f_win_getid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|
{
|
|
rettv->vval.v_number = win_getid(argvars);
|
|
}
|
|
|
|
/// "win_gotoid()" function
|
|
void f_win_gotoid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|
{
|
|
int id = (int)tv_get_number(&argvars[0]);
|
|
if (curwin->handle == id) {
|
|
// Nothing to do.
|
|
rettv->vval.v_number = 1;
|
|
return;
|
|
}
|
|
|
|
if (text_or_buf_locked()) {
|
|
return;
|
|
}
|
|
FOR_ALL_TAB_WINDOWS(tp, wp) {
|
|
if (wp->handle == id) {
|
|
goto_tabpage_win(tp, wp);
|
|
rettv->vval.v_number = 1;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// "win_id2tabwin()" function
|
|
void f_win_id2tabwin(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|
{
|
|
win_id2tabwin(argvars, rettv);
|
|
}
|
|
|
|
/// "win_id2win()" function
|
|
void f_win_id2win(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|
{
|
|
rettv->vval.v_number = win_id2win(argvars);
|
|
}
|
|
|
|
/// "win_move_separator()" function
|
|
void f_win_move_separator(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|
{
|
|
rettv->vval.v_number = false;
|
|
|
|
win_T *wp = find_win_by_nr_or_id(&argvars[0]);
|
|
if (wp == NULL || wp->w_floating) {
|
|
return;
|
|
}
|
|
if (!win_valid(wp)) {
|
|
emsg(_(e_cannot_resize_window_in_another_tab_page));
|
|
return;
|
|
}
|
|
|
|
int offset = (int)tv_get_number(&argvars[1]);
|
|
win_drag_vsep_line(wp, offset);
|
|
rettv->vval.v_number = true;
|
|
}
|
|
|
|
/// "win_move_statusline()" function
|
|
void f_win_move_statusline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|
{
|
|
win_T *wp;
|
|
int offset;
|
|
|
|
rettv->vval.v_number = false;
|
|
|
|
wp = find_win_by_nr_or_id(&argvars[0]);
|
|
if (wp == NULL || wp->w_floating) {
|
|
return;
|
|
}
|
|
if (!win_valid(wp)) {
|
|
emsg(_(e_cannot_resize_window_in_another_tab_page));
|
|
return;
|
|
}
|
|
|
|
offset = (int)tv_get_number(&argvars[1]);
|
|
win_drag_status_line(wp, offset);
|
|
rettv->vval.v_number = true;
|
|
}
|
|
|
|
/// "win_screenpos()" function
|
|
void f_win_screenpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|
{
|
|
tv_list_alloc_ret(rettv, 2);
|
|
const win_T *const wp = find_win_by_nr_or_id(&argvars[0]);
|
|
tv_list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_winrow + 1);
|
|
tv_list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_wincol + 1);
|
|
}
|
|
|
|
/// "win_splitmove()" function
|
|
void f_win_splitmove(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|
{
|
|
win_T *wp = find_win_by_nr_or_id(&argvars[0]);
|
|
win_T *targetwin = find_win_by_nr_or_id(&argvars[1]);
|
|
win_T *oldwin = curwin;
|
|
|
|
rettv->vval.v_number = -1;
|
|
|
|
if (wp == NULL || targetwin == NULL || wp == targetwin
|
|
|| !win_valid(wp) || !win_valid(targetwin)
|
|
|| targetwin->w_floating) {
|
|
emsg(_(e_invalwindow));
|
|
return;
|
|
}
|
|
|
|
int flags = 0;
|
|
int size = 0;
|
|
|
|
if (argvars[2].v_type != VAR_UNKNOWN) {
|
|
dict_T *d;
|
|
dictitem_T *di;
|
|
|
|
if (tv_check_for_nonnull_dict_arg(argvars, 2) == FAIL) {
|
|
return;
|
|
}
|
|
|
|
d = argvars[2].vval.v_dict;
|
|
if (tv_dict_get_number(d, "vertical")) {
|
|
flags |= WSP_VERT;
|
|
}
|
|
if ((di = tv_dict_find(d, "rightbelow", -1)) != NULL) {
|
|
flags |= tv_get_number(&di->di_tv) ? WSP_BELOW : WSP_ABOVE;
|
|
}
|
|
size = (int)tv_dict_get_number(d, "size");
|
|
}
|
|
|
|
// Check if we're allowed to continue before we bother switching windows.
|
|
if (is_aucmd_win(wp) || text_or_buf_locked() || check_split_disallowed(wp) == FAIL) {
|
|
return;
|
|
}
|
|
|
|
if (curwin != targetwin) {
|
|
win_goto(targetwin);
|
|
}
|
|
|
|
// Autocommands may have sent us elsewhere or closed "wp" or "oldwin".
|
|
if (curwin == targetwin && win_valid(wp)) {
|
|
if (win_splitmove(wp, size, flags) == OK) {
|
|
rettv->vval.v_number = 0;
|
|
}
|
|
} else {
|
|
emsg(_(e_auabort));
|
|
}
|
|
|
|
if (oldwin != curwin && win_valid(oldwin)) {
|
|
win_goto(oldwin);
|
|
}
|
|
}
|
|
|
|
/// "win_gettype(nr)" function
|
|
void f_win_gettype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|
{
|
|
win_T *wp = curwin;
|
|
|
|
rettv->v_type = VAR_STRING;
|
|
rettv->vval.v_string = NULL;
|
|
if (argvars[0].v_type != VAR_UNKNOWN) {
|
|
wp = find_win_by_nr_or_id(&argvars[0]);
|
|
if (wp == NULL) {
|
|
rettv->vval.v_string = xstrdup("unknown");
|
|
return;
|
|
}
|
|
}
|
|
if (is_aucmd_win(wp)) {
|
|
rettv->vval.v_string = xstrdup("autocmd");
|
|
} else if (wp->w_p_pvw) {
|
|
rettv->vval.v_string = xstrdup("preview");
|
|
} else if (wp->w_floating) {
|
|
rettv->vval.v_string = xstrdup("popup");
|
|
} else if (wp == cmdwin_win) {
|
|
rettv->vval.v_string = xstrdup("command");
|
|
} else if (bt_quickfix(wp->w_buffer)) {
|
|
rettv->vval.v_string = xstrdup((wp->w_llist_ref != NULL ? "loclist" : "quickfix"));
|
|
}
|
|
}
|
|
|
|
/// "getcmdwintype()" function
|
|
void f_getcmdwintype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|
{
|
|
rettv->v_type = VAR_STRING;
|
|
rettv->vval.v_string = NULL;
|
|
rettv->vval.v_string = xmallocz(1);
|
|
rettv->vval.v_string[0] = (char)cmdwin_type;
|
|
}
|
|
|
|
/// "winbufnr(nr)" function
|
|
void f_winbufnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|
{
|
|
win_T *wp = find_win_by_nr_or_id(&argvars[0]);
|
|
if (wp == NULL) {
|
|
rettv->vval.v_number = -1;
|
|
} else {
|
|
rettv->vval.v_number = wp->w_buffer->b_fnum;
|
|
}
|
|
}
|
|
|
|
/// "wincol()" function
|
|
void f_wincol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|
{
|
|
validate_cursor(curwin);
|
|
rettv->vval.v_number = curwin->w_wcol + 1;
|
|
}
|
|
|
|
/// "winheight(nr)" function
|
|
void f_winheight(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|
{
|
|
win_T *wp = find_win_by_nr_or_id(&argvars[0]);
|
|
if (wp == NULL) {
|
|
rettv->vval.v_number = -1;
|
|
} else {
|
|
rettv->vval.v_number = wp->w_view_height;
|
|
}
|
|
}
|
|
|
|
/// "winlayout()" function
|
|
void f_winlayout(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|
{
|
|
tabpage_T *tp;
|
|
|
|
tv_list_alloc_ret(rettv, 2);
|
|
|
|
if (argvars[0].v_type == VAR_UNKNOWN) {
|
|
tp = curtab;
|
|
} else {
|
|
tp = find_tabpage((int)tv_get_number(&argvars[0]));
|
|
if (tp == NULL) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
get_framelayout(tp->tp_topframe, rettv->vval.v_list, true);
|
|
}
|
|
|
|
/// "winline()" function
|
|
void f_winline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|
{
|
|
validate_cursor(curwin);
|
|
rettv->vval.v_number = curwin->w_wrow + 1;
|
|
}
|
|
|
|
/// "winnr()" function
|
|
void f_winnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|
{
|
|
rettv->vval.v_number = get_winnr(curtab, &argvars[0]);
|
|
}
|
|
|
|
/// "winrestcmd()" function
|
|
void f_winrestcmd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|
{
|
|
char buf[50];
|
|
|
|
garray_T ga;
|
|
ga_init(&ga, (int)sizeof(char), 70);
|
|
|
|
// Do this twice to handle some window layouts properly.
|
|
for (int i = 0; i < 2; i++) {
|
|
int winnr = 1;
|
|
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
|
|
if (!win_has_winnr(wp)) {
|
|
continue;
|
|
}
|
|
snprintf(buf, sizeof(buf), "%dresize %d|", winnr,
|
|
wp->w_height);
|
|
ga_concat(&ga, buf);
|
|
snprintf(buf, sizeof(buf), "vert %dresize %d|", winnr,
|
|
wp->w_width);
|
|
ga_concat(&ga, buf);
|
|
winnr++;
|
|
}
|
|
}
|
|
ga_append(&ga, NUL);
|
|
|
|
rettv->vval.v_string = ga.ga_data;
|
|
rettv->v_type = VAR_STRING;
|
|
}
|
|
|
|
/// "winrestview()" function
|
|
void f_winrestview(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|
{
|
|
if (tv_check_for_nonnull_dict_arg(argvars, 0) == FAIL) {
|
|
return;
|
|
}
|
|
|
|
dict_T *dict = argvars[0].vval.v_dict;
|
|
dictitem_T *di;
|
|
if ((di = tv_dict_find(dict, S_LEN("lnum"))) != NULL) {
|
|
curwin->w_cursor.lnum = (linenr_T)tv_get_number(&di->di_tv);
|
|
}
|
|
if ((di = tv_dict_find(dict, S_LEN("col"))) != NULL) {
|
|
curwin->w_cursor.col = (colnr_T)tv_get_number(&di->di_tv);
|
|
}
|
|
if ((di = tv_dict_find(dict, S_LEN("coladd"))) != NULL) {
|
|
curwin->w_cursor.coladd = (colnr_T)tv_get_number(&di->di_tv);
|
|
}
|
|
if ((di = tv_dict_find(dict, S_LEN("curswant"))) != NULL) {
|
|
curwin->w_curswant = (colnr_T)tv_get_number(&di->di_tv);
|
|
curwin->w_set_curswant = false;
|
|
}
|
|
if ((di = tv_dict_find(dict, S_LEN("topline"))) != NULL) {
|
|
set_topline(curwin, (linenr_T)tv_get_number(&di->di_tv));
|
|
}
|
|
if ((di = tv_dict_find(dict, S_LEN("topfill"))) != NULL) {
|
|
curwin->w_topfill = (int)tv_get_number(&di->di_tv);
|
|
}
|
|
if ((di = tv_dict_find(dict, S_LEN("leftcol"))) != NULL) {
|
|
curwin->w_leftcol = (colnr_T)tv_get_number(&di->di_tv);
|
|
}
|
|
if ((di = tv_dict_find(dict, S_LEN("skipcol"))) != NULL) {
|
|
curwin->w_skipcol = (colnr_T)tv_get_number(&di->di_tv);
|
|
}
|
|
|
|
check_cursor(curwin);
|
|
win_new_height(curwin, curwin->w_height);
|
|
win_new_width(curwin, curwin->w_width);
|
|
changed_window_setting(curwin);
|
|
|
|
if (curwin->w_topline <= 0) {
|
|
curwin->w_topline = 1;
|
|
}
|
|
if (curwin->w_topline > curbuf->b_ml.ml_line_count) {
|
|
curwin->w_topline = curbuf->b_ml.ml_line_count;
|
|
}
|
|
check_topfill(curwin, true);
|
|
}
|
|
|
|
/// "winsaveview()" function
|
|
void f_winsaveview(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|
{
|
|
tv_dict_alloc_ret(rettv);
|
|
dict_T *dict = rettv->vval.v_dict;
|
|
|
|
tv_dict_add_nr(dict, S_LEN("lnum"), (varnumber_T)curwin->w_cursor.lnum);
|
|
tv_dict_add_nr(dict, S_LEN("col"), (varnumber_T)curwin->w_cursor.col);
|
|
tv_dict_add_nr(dict, S_LEN("coladd"), (varnumber_T)curwin->w_cursor.coladd);
|
|
update_curswant();
|
|
tv_dict_add_nr(dict, S_LEN("curswant"), (varnumber_T)curwin->w_curswant);
|
|
|
|
tv_dict_add_nr(dict, S_LEN("topline"), (varnumber_T)curwin->w_topline);
|
|
tv_dict_add_nr(dict, S_LEN("topfill"), (varnumber_T)curwin->w_topfill);
|
|
tv_dict_add_nr(dict, S_LEN("leftcol"), (varnumber_T)curwin->w_leftcol);
|
|
tv_dict_add_nr(dict, S_LEN("skipcol"), (varnumber_T)curwin->w_skipcol);
|
|
}
|
|
|
|
/// "winwidth(nr)" function
|
|
void f_winwidth(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|
{
|
|
win_T *wp = find_win_by_nr_or_id(&argvars[0]);
|
|
if (wp == NULL) {
|
|
rettv->vval.v_number = -1;
|
|
} else {
|
|
rettv->vval.v_number = wp->w_view_width;
|
|
}
|
|
}
|
|
|
|
/// Set "win" to be the curwin and "tp" to be the current tab page.
|
|
/// restore_win() MUST be called to undo, also when FAIL is returned.
|
|
/// No autocommands will be executed until restore_win() is called.
|
|
///
|
|
/// @param no_display if true the display won't be affected, no redraw is
|
|
/// triggered, another tabpage access is limited.
|
|
///
|
|
/// @return FAIL if switching to "win" failed.
|
|
int switch_win(switchwin_T *switchwin, win_T *win, tabpage_T *tp, bool no_display)
|
|
{
|
|
block_autocmds();
|
|
return switch_win_noblock(switchwin, win, tp, no_display);
|
|
}
|
|
|
|
// As switch_win() but without blocking autocommands.
|
|
int switch_win_noblock(switchwin_T *switchwin, win_T *win, tabpage_T *tp, bool no_display)
|
|
{
|
|
CLEAR_POINTER(switchwin);
|
|
switchwin->sw_curwin = curwin;
|
|
if (win == curwin) {
|
|
switchwin->sw_same_win = true;
|
|
} else {
|
|
// Disable Visual selection, because redrawing may fail.
|
|
switchwin->sw_visual_active = VIsual_active;
|
|
VIsual_active = false;
|
|
}
|
|
|
|
if (tp != NULL) {
|
|
switchwin->sw_curtab = curtab;
|
|
if (no_display) {
|
|
unuse_tabpage(curtab);
|
|
use_tabpage(tp);
|
|
} else {
|
|
goto_tabpage_tp(tp, false, false);
|
|
}
|
|
}
|
|
if (!win_valid(win)) {
|
|
return FAIL;
|
|
}
|
|
curwin = win;
|
|
curbuf = curwin->w_buffer;
|
|
return OK;
|
|
}
|
|
|
|
/// Restore current tabpage and window saved by switch_win(), if still valid.
|
|
/// When "no_display" is true the display won't be affected, no redraw is
|
|
/// triggered.
|
|
void restore_win(switchwin_T *switchwin, bool no_display)
|
|
{
|
|
restore_win_noblock(switchwin, no_display);
|
|
unblock_autocmds();
|
|
}
|
|
|
|
/// As restore_win() but without unblocking autocommands.
|
|
void restore_win_noblock(switchwin_T *switchwin, bool no_display)
|
|
{
|
|
if (switchwin->sw_curtab != NULL && valid_tabpage(switchwin->sw_curtab)) {
|
|
if (no_display) {
|
|
win_T *const old_tp_curwin = curtab->tp_curwin;
|
|
|
|
unuse_tabpage(curtab);
|
|
// Don't change the curwin of the tabpage we temporarily visited.
|
|
curtab->tp_curwin = old_tp_curwin;
|
|
use_tabpage(switchwin->sw_curtab);
|
|
} else {
|
|
goto_tabpage_tp(switchwin->sw_curtab, false, false);
|
|
}
|
|
}
|
|
|
|
if (!switchwin->sw_same_win) {
|
|
VIsual_active = switchwin->sw_visual_active;
|
|
}
|
|
|
|
if (win_valid(switchwin->sw_curwin)) {
|
|
curwin = switchwin->sw_curwin;
|
|
curbuf = curwin->w_buffer;
|
|
}
|
|
}
|