This patch was created with great support from the LLMs (Claude Code Sonnet 4.6, Gemini 3) 👍
Without their support, it would have taken more than two months and might not have been completed 😄
The implementation is complete. I've added all the tests I can think of and they all pass.
https://github.com/vim/vim/pull/19622
(17 files)
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
@zeertzjq commented on this pull request.
In src/window.c:
> @@ -1486,6 +1487,11 @@ win_split_ins(
/*
* equalize the window sizes.
+ * Note: this win_equal() runs before get_winopts() changes the new
⬇️ Suggested change
- * Note: this win_equal() runs before get_winopts() changes the new + * Note: this win_equal() runs before get_winopts() and changes the new
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@h-east pushed 1 commit.
—
View it on GitHub or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@h-east commented on this pull request.
In src/window.c:
> @@ -1486,6 +1487,11 @@ win_split_ins(
/*
* equalize the window sizes.
+ * Note: this win_equal() runs before get_winopts() changes the new
no need for and.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
'statuslineopt' window-local Demo.
Set the statusline to 3 lines for the current window only, and keep it at 1 line for the others.
demo_stlo.vim:
vim9script set ls=2 set statuslineopt=maxheight:3 &statusline = '%<%f ' \ .. '%m%r%h%w[%{&ff}][%{(&fenc!=""?&fenc:&enc).(&bomb?":bom":"")}] ' \ .. 'DEMO%=%B%=%l,%c%V %P' def StlEnter() &l:statusline = '%<%f%@' \ .. '%m%r%h%w[%{&ff}][%{(&fenc!=""?&fenc:&enc).(&bomb?":bom":"")}]%@' \ .. 'DEMO%=%B%=%l,%c%V %P' enddef def StlLeave() setlocal statusline< enddef augroup MyStatusLine autocmd! autocmd WinEnter,BufEnter * StlEnter() autocmd WinLeave * StlLeave() augroup END StlEnter()
vim --clean --cmd "lang C" -S demo_stlo.vim +h +sp +"h uganda.txt"
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
@chrisbra commented on this pull request.
In src/window.c:
>
- comp_col();
- redraw_all_later(UPD_SOME_VALID);
+ // Suppress errors: %{expr} or %!expr may not be safe to evaluate here.
+ ++emsg_off;
+ wp->w_stl_rendered_height =
+ get_stl_rendered_height(wp, wp->w_p_stl, opt_name, OPT_LOCAL);
+ --emsg_off;
}
Those two functions are a bit strange and too similar. How about this single function insead:
void update_stl_rendered_height(win_T *wp) { char_u *opt_name = (char_u *)"statusline"; char_u *stl_val = (wp == NULL) ? p_stl : wp->w_p_stl; int opt_idx = (wp == NULL) ? 0 : OPT_LOCAL; int *height = (wp == NULL) ? &stl_rendered_height : &wp->w_stl_rendered_height; // Suppress errors: %{expr} or %!expr may not be safe to evaluate here // (e.g. during option-set callback). Errors will be raised at redraw time. ++emsg_off; *height = get_stl_rendered_height(wp == NULL ? curwin : wp, stl_val, opt_name, opt_idx); --emsg_off; }
And then just pass wither a wp or NULL at all the call sites?
In src/optionstr.c:
> +
+ if (wp != NULL)
+ {
+ frame_change_statusline_height();
+ actual_stlh = MIN(wp->w_p_stlo_mh,
+ wp->w_height + wp->w_status_height - p_wmh);
+ }
+ else
+ actual_stlh = frame_change_statusline_height();
+
+ // Only re-serialize when the window actually has a status line shown.
+ if (actual_stlh > 0)
+ {
+ update_stlo_maxheight(varp, actual_stlh);
+ // Re-sync parsed members with the updated string.
+ statuslineopt_changed(*varp, wp);
here we are parsing the statuslineopt a second time. Can we improve this?
In src/optionstr.c:
> + IOSIZE - len, "maxheight:%d", actual_stlh);
+ need_comma = true;
+ }
+ }
+ else
+ {
+ if (need_comma)
+ IObuff[len++] = ',';
+ mch_memmove(IObuff + len, tok, tok_len);
+ len += tok_len;
+ need_comma = true;
+ }
+ }
+ IObuff[len] = NUL;
+ free_string_option(*varp);
+ *varp = vim_strsave(IObuff);
I am a bit worried about using global IObuff here and it could get corrupted by a recursive call here. Can we use a local buffer instead?
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@h-east pushed 4 commits.
You are receiving this because you are subscribed to this thread.![]()
@h-east commented on this pull request.
In src/window.c:
>
- comp_col();
- redraw_all_later(UPD_SOME_VALID);
+ // Suppress errors: %{expr} or %!expr may not be safe to evaluate here.
+ ++emsg_off;
+ wp->w_stl_rendered_height =
+ get_stl_rendered_height(wp, wp->w_p_stl, opt_name, OPT_LOCAL);
+ --emsg_off;
}
Merged update_stl_rendered_height() and update_win_stl_rendered_height().
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@h-east commented on this pull request.
In src/optionstr.c:
> +
+ if (wp != NULL)
+ {
+ frame_change_statusline_height();
+ actual_stlh = MIN(wp->w_p_stlo_mh,
+ wp->w_height + wp->w_status_height - p_wmh);
+ }
+ else
+ actual_stlh = frame_change_statusline_height();
+
+ // Only re-serialize when the window actually has a status line shown.
+ if (actual_stlh > 0)
+ {
+ update_stlo_maxheight(varp, actual_stlh);
+ // Re-sync parsed members with the updated string.
+ statuslineopt_changed(*varp, wp);
Fixed.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
> + IOSIZE - len, "maxheight:%d", actual_stlh);
+ need_comma = true;
+ }
+ }
+ else
+ {
+ if (need_comma)
+ IObuff[len++] = ',';
+ mch_memmove(IObuff + len, tok, tok_len);
+ len += tok_len;
+ need_comma = true;
+ }
+ }
+ IObuff[len] = NUL;
+ free_string_option(*varp);
+ *varp = vim_strsave(IObuff);
Stopped using IObuff in update_stlo_maxheight().
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@Copilot commented on this pull request.
This PR updates Vim’s multi-line statusline support by making 'statuslineopt' ('stlo') window-local, so different windows can independently control statusline height behavior while still supporting a consistent global mode.
Changes:
'statuslineopt' a global-local (window-local) option, including propagation/inheritance behavior across splits.fixedheight handling).Copilot reviewed 17 out of 17 changed files in this pull request and generated 4 comments.
Show a summary per file| File | Description |
|---|---|
| src/window.c | Core logic changes for statusline rendered height caching, window-local 'stlo', and frame resizing/equalization interactions. |
| src/optionstr.c | Updates did_set_statusline() / did_set_statuslineopt() to recompute rendered height and reserialize best-effort maxheight. |
| src/optiondefs.h | Marks 'statuslineopt' as a window option via PV_STLO. |
| src/option.h | Adds WV_STLO window-option index. |
| src/option.c | Adds window-local plumbing for 'stlo' (get_varp_scope/get_varp/unset/copy/check/clear). |
| src/buffer.c | Ensures rendered height cache stays correct across option inheritance and suppresses errors when computing rendered height. |
| src/structs.h | Adds window-local storage for 'stlo' plus per-window rendered-height cache for local 'stl'. |
| src/proto/window.pro | Updates prototypes for new/changed window statusline functions. |
| src/testdir/test_statuslineopt.vim | Adds extensive tests for rendered-height behavior, fixedheight, and window-local 'stlo' inheritance. |
| src/testdir/test_options.vim | Extends global-local option tests to include 'statuslineopt'. |
| src/testdir/dumps/Test_statuslineopt_* | Adds new screendumps covering :new, :sp, CTRL-W_, and CTRL-W= behaviors with mixed global/local settings. |
| runtime/doc/windows.txt | Updates help text to reflect window-local statusline height configuration. |
| runtime/doc/options.txt | Updates option docs for new window-local behavior and fixedheight semantics/examples. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
In src/window.c:
>
return OK;
}
#endif
/*
- * Return the number of lines used by the status line.
- * "wp" is not used currently because 'statuslineopt' is a global option.
- * NULL is passed when called from layout calculations (e.g. 'laststatus')
- * where no specific window context is available.
+ * Return the number of status lines for window "wp".
+ * When "wp" is NULL, return the global maximum height.
*/
int
statusline_height(win_T *wp UNUSED)
In statusline_height(), the parameter is still marked UNUSED, but it is now used throughout the function. Please drop the UNUSED annotation to avoid misleading readers (and potential compiler attribute oddities).
⬇️ Suggested change-statusline_height(win_T *wp UNUSED) +statusline_height(win_T *wp)
In src/window.c:
> + // get the minimal height from each frame in this row
+ c = 0;
+ FOR_ALL_FRAMES(frp, topfrp->fr_child)
+ {
+ n = frame_wincount_in_height(frp);
+ if (n > c)
+ c = n;
+ }
+ }
+ else
+ {
+ // Add up the minimal heights for all frames in this column.
+ c = 0;
+ FOR_ALL_FRAMES(frp, topfrp->fr_child)
+ c += frame_wincount_in_height(frp);
The comments inside frame_wincount_in_height() refer to “minimal height(s)”, but this helper is counting windows (max across FR_ROW, sum across FR_COL). Please update these comments so they match what the code actually computes.
> @@ -125,7 +125,10 @@ when the last window also has a status line: You can change the contents of the status line with the 'statusline' option. This option can be local to the window, so that you can have a different status line in each window. -You can change the height of the status line with the 'statuslineopt' option. +The height of the status line etc. can be changed with the 'statuslineopt' +option. +These option can be local to the window, so that you can have a different
Grammar: “These option can be local to the window” should be “This option can be…” (referring to 'statuslineopt') or “These options can be…” (referring to both 'statusline' and 'statuslineopt').
⬇️ Suggested change-These option can be local to the window, so that you can have a different +These options can be local to the window, so that you can have a different
In src/window.c:
> + * Return the number of status lines for window "wp". + * When "wp" is NULL, return the global maximum height.
The header comment for statusline_height() says that when wp is NULL it returns the “global maximum height”, but the function now returns the effective height (e.g. 1 when not fixedheight and the default statusline is in use, or clamped to rendered height). Please update the comment to reflect the new behavior.
⬇️ Suggested change- * Return the number of status lines for window "wp". - * When "wp" is NULL, return the global maximum height. + * Return the effective number of status lines for window "wp", taking + * 'statuslineopt' and the rendered statusline height into account. + * When "wp" is NULL, use the global 'statuslineopt' settings.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@h-east pushed 8 commits.
You are receiving this because you are subscribed to this thread.![]()
@h-east commented on this pull request.
In src/window.c:
> + // get the minimal height from each frame in this row
+ c = 0;
+ FOR_ALL_FRAMES(frp, topfrp->fr_child)
+ {
+ n = frame_wincount_in_height(frp);
+ if (n > c)
+ c = n;
+ }
+ }
+ else
+ {
+ // Add up the minimal heights for all frames in this column.
+ c = 0;
+ FOR_ALL_FRAMES(frp, topfrp->fr_child)
+ c += frame_wincount_in_height(frp);
fixed
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
> + * Return the number of status lines for window "wp". + * When "wp" is NULL, return the global maximum height.
fixed
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@h-east commented on this pull request.
> @@ -125,7 +125,10 @@ when the last window also has a status line: You can change the contents of the status line with the 'statusline' option. This option can be local to the window, so that you can have a different status line in each window. -You can change the height of the status line with the 'statuslineopt' option. +The height of the status line etc. can be changed with the 'statuslineopt' +option. +These option can be local to the window, so that you can have a different
fixed
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@h-east commented on this pull request.
In src/window.c:
>
return OK;
}
#endif
/*
- * Return the number of lines used by the status line.
- * "wp" is not used currently because 'statuslineopt' is a global option.
- * NULL is passed when called from layout calculations (e.g. 'laststatus')
- * where no specific window context is available.
+ * Return the number of status lines for window "wp".
+ * When "wp" is NULL, return the global maximum height.
*/
int
statusline_height(win_T *wp UNUSED)
fixed
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@h-east commented on this pull request.
In src/window.c:
>
return OK;
}
#endif
/*
- * Return the number of lines used by the status line.
- * "wp" is not used currently because 'statuslineopt' is a global option.
- * NULL is passed when called from layout calculations (e.g. 'laststatus')
- * where no specific window context is available.
+ * Return the number of status lines for window "wp".
+ * When "wp" is NULL, return the global maximum height.
*/
int
statusline_height(win_T *wp UNUSED)
Hmm, need it for the tiny build.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
thanks
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
After this patch my windows build (clang 21.1.8 via msys2) fails if FEAT_DIFF is not defined:
clang -c -I. -Iproto -DWIN32 -DWINVER=0x0A00 -D_WIN32_WINNT=0x0A00 -DHAVE_PATHDEF -DFEAT_NORMAL -DHAVE_STDINT_H -D__USE_MINGW_ANSI_S TDIO -pipe -Wall -O3 -fomit-frame-pointer -fpie -fPIE -Db_lto=true -Db_lto_mode=thin -march=native -DFEAT_GUI_MSWIN -DFEAT_CLIPBOARD window.c -o gobjx86-64/window.o window.c:7751:35: error: no member named 'w_topfill' in 'struct window_S' 7751 | int saved_topfill = wp->w_topfill; | ~~ ^ window.c:7754:10: error: no member named 'w_topfill' in 'struct window_S' 7754 | wp->w_topfill = saved_topfill; | ~~ ^ 2 errors generated. make: *** [Make_cyg_ming.mak:1273: objx86-64/window.o] Error 1 make: Target 'all' not remade because of errors.
Applying this patch fixes it for me:
--- window.20260313-074725.c 2026-03-13 07:47:25 +1100 +++ window.c 2026-03-13 07:55:38 +1100 @@ -7748,10 +7748,14 @@ // and let update_topline() handle cursor visibility for // curwin during redraw. linenr_T saved_topline = wp->w_topline; +#ifdef FEAT_DIFF int saved_topfill = wp->w_topfill; +#endif win_new_height(wp, win_free_height - wp->w_status_height); wp->w_topline = saved_topline; +#ifdef FEAT_DIFF wp->w_topfill = saved_topfill; +#endif wp->w_valid &= ~(VALID_WROW | VALID_CROW | VALID_BOTLINE | VALID_BOTLINE_AP); }
Cheers
John
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
thanks, I merge it
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()