aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/eval.txt29
-rw-r--r--src/nvim/eval.c26
-rw-r--r--src/nvim/testdir/test_window_cmd.vim45
-rw-r--r--src/nvim/window.c109
4 files changed, 162 insertions, 47 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 8c81f35331..a7dbe294c7 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -8405,17 +8405,30 @@ winline() The result is a Number, which is the screen line of the cursor
*winnr()*
winnr([{arg}]) The result is a Number, which is the number of the current
window. The top window has number 1.
- When the optional argument is "$", the number of the
- last window is returned (the window count). >
- let window_count = winnr('$')
-< When the optional argument is "#", the number of the last
- accessed window is returned (where |CTRL-W_p| goes to).
- If there is no previous window or it is in another tab page 0
- is returned.
+
+ The optional argument {arg} supports the following values:
+ $ the number of the last window (the window
+ count).
+ # the number of the last accessed window (where
+ |CTRL-W_p| goes to). If there is no previous
+ window or it is in another tab page 0 is
+ returned.
+ {N}j the number of the Nth window below the
+ current window (where |CTRL-W_j| goes to).
+ {N}k the number of the Nth window above the current
+ window (where |CTRL-W_k| goes to).
+ {N}h the number of the Nth window left of the
+ current window (where |CTRL-W_h| goes to).
+ {N}l the number of the Nth window right of the
+ current window (where |CTRL-W_l| goes to).
The number can be used with |CTRL-W_w| and ":wincmd w"
|:wincmd|.
Also see |tabpagewinnr()| and |win_getid()|.
-
+ Examples: >
+ let window_count = winnr('$')
+ let prev_window = winnr('#')
+ let wnum = winnr('3k')
+<
*winrestcmd()*
winrestcmd() Returns a sequence of |:resize| commands that should restore
the current window sizes. Only works properly when no windows
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 9a67b01307..86333acbef 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -16755,6 +16755,7 @@ static int get_winnr(tabpage_T *tp, typval_T *argvar)
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.
@@ -16766,6 +16767,31 @@ static int get_winnr(tabpage_T *tp, typval_T *argvar)
nr = 0;
}
} else {
+ // Extract the window count (if specified). e.g. winnr('3j')
+ char_u *endp;
+ long count = strtol((char *)arg, (char **)&endp, 10);
+ if (count <= 0) {
+ // if count is not specified, default to 1
+ count = 1;
+ }
+ if (endp != NULL && *endp != '\0') {
+ if (strequal((char *)endp, "j")) {
+ twin = win_vert_neighbor(tp, twin, false, count);
+ } else if (strequal((char *)endp, "k")) {
+ twin = win_vert_neighbor(tp, twin, true, count);
+ } else if (strequal((char *)endp, "h")) {
+ twin = win_horz_neighbor(tp, twin, true, count);
+ } else if (strequal((char *)endp, "l")) {
+ twin = win_horz_neighbor(tp, twin, false, count);
+ } else {
+ invalid_arg = true;
+ }
+ } else {
+ invalid_arg = true;
+ }
+ }
+
+ if (invalid_arg) {
EMSG2(_(e_invexpr2), arg);
nr = 0;
}
diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim
index 57fb36abb8..d3b496635d 100644
--- a/src/nvim/testdir/test_window_cmd.vim
+++ b/src/nvim/testdir/test_window_cmd.vim
@@ -646,4 +646,49 @@ func Test_relative_cursor_second_line_after_resize()
let &so = so_save
endfunc
+" Tests for the winnr() function
+func Test_winnr()
+ only | tabonly
+ call assert_equal(1, winnr('j'))
+ call assert_equal(1, winnr('k'))
+ call assert_equal(1, winnr('h'))
+ call assert_equal(1, winnr('l'))
+
+ " create a set of horizontally and vertically split windows
+ leftabove new | wincmd p
+ leftabove new | wincmd p
+ rightbelow new | wincmd p
+ rightbelow new | wincmd p
+ leftabove vnew | wincmd p
+ leftabove vnew | wincmd p
+ rightbelow vnew | wincmd p
+ rightbelow vnew | wincmd p
+
+ call assert_equal(8, winnr('j'))
+ call assert_equal(2, winnr('k'))
+ call assert_equal(4, winnr('h'))
+ call assert_equal(6, winnr('l'))
+ call assert_equal(9, winnr('2j'))
+ call assert_equal(1, winnr('2k'))
+ call assert_equal(3, winnr('2h'))
+ call assert_equal(7, winnr('2l'))
+
+ " Error cases
+ call assert_fails("echo winnr('0.2k')", 'E15:')
+ call assert_equal(2, winnr('-2k'))
+ call assert_fails("echo winnr('-2xj')", 'E15:')
+ call assert_fails("echo winnr('j2j')", 'E15:')
+ call assert_fails("echo winnr('ll')", 'E15:')
+ call assert_fails("echo winnr('5')", 'E15:')
+ call assert_equal(4, winnr('0h'))
+
+ tabnew
+ call assert_equal(8, tabpagewinnr(1, 'j'))
+ call assert_equal(2, tabpagewinnr(1, 'k'))
+ call assert_equal(4, tabpagewinnr(1, 'h'))
+ call assert_equal(6, tabpagewinnr(1, 'l'))
+
+ only | tabonly
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/window.c b/src/nvim/window.c
index e135d7436d..237338f974 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -4019,24 +4019,25 @@ tabpage_T *win_find_tabpage(win_T *win)
return NULL;
}
-/*
- * Move to window above or below "count" times.
- */
-static void
-win_goto_ver (
- int up, /* TRUE to go to win above */
- long count
-)
+/// Get the above or below neighbor window of the specified window.
+///
+/// Returns the specified window if the neighbor is not found.
+/// Returns the previous window if the specifiecied window is a floating window.
+///
+/// @param up true for the above neighbor
+/// @param count nth neighbor window
+///
+/// @return found window
+win_T *win_vert_neighbor(tabpage_T *tp, win_T *wp, bool up, long count)
{
frame_T *fr;
frame_T *nfr;
frame_T *foundfr;
- foundfr = curwin->w_frame;
+ foundfr = wp->w_frame;
- if (curwin->w_floating) {
- win_goto(prevwin);
- return;
+ if (wp->w_floating) {
+ return prevwin;
}
while (count--) {
@@ -4046,14 +4047,17 @@ win_goto_ver (
*/
fr = foundfr;
for (;; ) {
- if (fr == topframe)
+ if (fr == tp->tp_topframe) {
goto end;
- if (up)
+ }
+ if (up) {
nfr = fr->fr_prev;
- else
+ } else {
nfr = fr->fr_next;
- if (fr->fr_parent->fr_layout == FR_COL && nfr != NULL)
+ }
+ if (fr->fr_parent->fr_layout == FR_COL && nfr != NULL) {
break;
+ }
fr = fr->fr_parent;
}
@@ -4067,11 +4071,12 @@ win_goto_ver (
}
fr = nfr->fr_child;
if (nfr->fr_layout == FR_ROW) {
- /* Find the frame at the cursor row. */
+ // Find the frame at the cursor row.
while (fr->fr_next != NULL
&& frame2win(fr)->w_wincol + fr->fr_width
- <= curwin->w_wincol + curwin->w_wcol)
+ <= wp->w_wincol + wp->w_wcol) {
fr = fr->fr_next;
+ }
}
if (nfr->fr_layout == FR_COL && up)
while (fr->fr_next != NULL)
@@ -4080,28 +4085,40 @@ win_goto_ver (
}
}
end:
- if (foundfr != NULL)
- win_goto(foundfr->fr_win);
+ return foundfr != NULL ? foundfr->fr_win : NULL;
}
-/*
- * Move to left or right window.
- */
-static void
-win_goto_hor (
- int left, /* TRUE to go to left win */
- long count
-)
+/// Move to window above or below "count" times.
+///
+/// @param up true to go to win above
+/// @param count go count times into direction
+static void win_goto_ver(bool up, long count)
+{
+ win_T *win = win_vert_neighbor(curtab, curwin, up, count);
+ if (win != NULL) {
+ win_goto(win);
+ }
+}
+
+/// Get the left or right neighbor window of the specified window.
+///
+/// Returns the specified window if the neighbor is not found.
+/// Returns the previous window if the specifiecied window is a floating window.
+///
+/// @param left true for the left neighbor
+/// @param count nth neighbor window
+///
+/// @return found window
+win_T *win_horz_neighbor(tabpage_T *tp, win_T *wp, bool left, long count)
{
frame_T *fr;
frame_T *nfr;
frame_T *foundfr;
- foundfr = curwin->w_frame;
+ foundfr = wp->w_frame;
- if (curwin->w_floating) {
- win_goto(prevwin);
- return;
+ if (wp->w_floating) {
+ return prevwin;
}
while (count--) {
@@ -4111,14 +4128,17 @@ win_goto_hor (
*/
fr = foundfr;
for (;; ) {
- if (fr == topframe)
+ if (fr == tp->tp_topframe) {
goto end;
- if (left)
+ }
+ if (left) {
nfr = fr->fr_prev;
- else
+ } else {
nfr = fr->fr_next;
- if (fr->fr_parent->fr_layout == FR_ROW && nfr != NULL)
+ }
+ if (fr->fr_parent->fr_layout == FR_ROW && nfr != NULL) {
break;
+ }
fr = fr->fr_parent;
}
@@ -4135,7 +4155,7 @@ win_goto_hor (
/* Find the frame at the cursor row. */
while (fr->fr_next != NULL
&& frame2win(fr)->w_winrow + fr->fr_height
- <= curwin->w_winrow + curwin->w_wrow)
+ <= wp->w_winrow + wp->w_wrow)
fr = fr->fr_next;
}
if (nfr->fr_layout == FR_ROW && left)
@@ -4145,8 +4165,19 @@ win_goto_hor (
}
}
end:
- if (foundfr != NULL)
- win_goto(foundfr->fr_win);
+ return foundfr != NULL ? foundfr->fr_win : NULL;
+}
+
+/// Move to left or right window.
+///
+/// @param left true to go to left window
+/// @param count go count times into direction
+static void win_goto_hor(bool left, long count)
+{
+ win_T *win = win_horz_neighbor(curtab, curwin, left, count);
+ if (win != NULL) {
+ win_goto(win);
+ }
}
/*