* [PATCH] kconfig: wrap long expressions in menuconfig
@ 2026-10-10 9:50 Julian Braha
2026-10-11 16:20 ` Miguel Ojeda
0 siblings, 1 reply; 3+ messages in thread
From: Julian Braha @ 2026-10-10 9:50 UTC (permalink / raw)
To: nathan, nsc
Cc: ojeda, a.hindborg, nico, rdunlap, grahamr, jakub.git,
linux-kbuild, linux-kernel, rust-for-linux, Julian Braha
In an offline conversation, Miguel Ojeda reported that scrolling
horizontally in menuconfig's search to read RUST's dependencies has been
slowing down debugging.
Let's wrap expressions (like in the 'Depends on', 'Selected by', or
'Range' sections) between tokens at the end of the window, such that each
line begins with the same indentation. Ensure the '!' negation stays
attached to the left side of the option symbol, and keep the value
annotation attached to the right side when wrapping.
Note that the 'help' textbox uses the same expression printer as search,
so this has the side effect of improving the formatting in 'help', too.
In the future, we should consider a larger revamp that would wrap all text
in the search window, allowing us to safely disable horizontal scrolling.
Assisted-by: LLM
Signed-off-by: Julian Braha <julianbraha@gmail.com>
---
scripts/kconfig/expr.c | 24 ++++++++++++++++--------
scripts/kconfig/lkc.h | 5 ++++-
scripts/kconfig/mconf.c | 2 +-
| 7 ++++++-
scripts/kconfig/nconf.c | 2 +-
scripts/kconfig/util.c | 1 +
6 files changed, 29 insertions(+), 12 deletions(-)
diff --git a/scripts/kconfig/expr.c b/scripts/kconfig/expr.c
index 2b91d16bf14f..f1e100c3c755 100644
--- a/scripts/kconfig/expr.c
+++ b/scripts/kconfig/expr.c
@@ -1155,21 +1155,28 @@ static void expr_print_gstr_helper(void *data, struct symbol *sym, const char *s
if (sym)
sym_str = sym_get_string_value(sym);
- if (gs->max_width) {
+ if (gs->max_width && !strchr(str, '\n')) {
unsigned extra_length = strlen(str);
const char *last_cr = strrchr(gs->s, '\n');
unsigned last_line_length;
-
- if (sym_str)
+ /* Ensure the value annotation stays attached to the symbol on wrap */
+ if (sym_str && sym->type != S_UNKNOWN)
extra_length += 4 + strlen(sym_str);
- if (!last_cr)
- last_cr = gs->s;
+ last_line_length = strlen(last_cr ? last_cr + 1 : gs->s);
- last_line_length = strlen(gs->s) - (last_cr - gs->s);
+ if (last_line_length > gs->indent &&
+ last_line_length + extra_length > gs->max_width) {
+ /* Ensure '!' stays attached to the symbol on wrap */
+ char *last = gs->s + strlen(gs->s) - 1;
+ bool negated = *last == '!';
- if ((last_line_length + extra_length) > gs->max_width)
- str_append(gs, "\\\n");
+ if (negated)
+ *last = '\0';
+ str_printf(gs, "\n%*s%s", gs->indent, "",
+ negated ? "!" : "");
+ str += strspn(str, " ");
+ }
}
str_append(gs, str);
@@ -1209,5 +1216,6 @@ static void expr_print_revdep(struct expr *e,
void expr_gstr_print_revdep(struct expr *e, struct gstr *gs,
tristate pr_type, const char *title)
{
+ gs->indent = strlen(" - ");
expr_print_revdep(e, expr_print_gstr_helper, gs, pr_type, &title);
}
diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h
index bbc99f75b416..f5fe3731db0c 100644
--- a/scripts/kconfig/lkc.h
+++ b/scripts/kconfig/lkc.h
@@ -65,6 +65,8 @@ struct gstr {
* wrapped not to exceed the max_width value
*/
int max_width;
+ /* Indentation of wrapped expression lines. */
+ int indent;
};
struct gstr str_new(void);
void str_free(struct gstr *gs);
@@ -101,7 +103,8 @@ const char *menu_get_prompt(const struct menu *menu);
struct menu *menu_get_parent_menu(struct menu *menu);
struct menu *menu_get_menu_or_parent_menu(struct menu *menu);
int get_jump_key_char(void);
-struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head);
+struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head,
+ int max_width);
void menu_get_ext_help(struct menu *menu, struct gstr *help);
void menu_dump(void);
diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c
index b8b7bba84a65..fb87f944d3f9 100644
--- a/scripts/kconfig/mconf.c
+++ b/scripts/kconfig/mconf.c
@@ -431,7 +431,7 @@ static void search_conf(void)
struct jump_key *pos, *tmp;
jump_key_char = 0;
- res = get_relations_str(sym_arr, &head);
+ res = get_relations_str(sym_arr, &head, getmaxx(stdscr) - 10);
set_subtitle();
dres = show_textbox_ext("Search Results", str_get(&res), 0, 0,
&vscroll, &hscroll,
--git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c
index bda3e2650216..31f12cfe981b 100644
--- a/scripts/kconfig/menu.c
+++ b/scripts/kconfig/menu.c
@@ -668,6 +668,7 @@ static void get_dep_str(struct gstr *r, const struct expr *expr,
{
if (!expr_is_yes(expr)) {
str_append(r, prefix);
+ r->indent = strlen(prefix);
expr_gstr_print(expr, r);
str_append(r, "\n");
}
@@ -743,6 +744,7 @@ static void get_symbol_props_str(struct gstr *r, struct symbol *sym,
bool hit = false;
struct property *prop;
+ r->indent = strlen(prefix);
for_all_properties(sym, prop, tok) {
if (!hit) {
str_append(r, prefix);
@@ -772,6 +774,7 @@ static void get_symbol_str(struct gstr *r, struct symbol *sym,
prop = sym_get_range_prop(sym);
if (prop) {
str_printf(r, "Range : ");
+ r->indent = strlen("Range : ");
expr_gstr_print(prop->expr, r);
str_append(r, "\n");
}
@@ -810,12 +813,14 @@ static void get_symbol_str(struct gstr *r, struct symbol *sym,
str_append(r, "\n\n");
}
-struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head)
+struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head,
+ int max_width)
{
struct symbol *sym;
struct gstr res = str_new();
int i;
+ res.max_width = max_width;
for (i = 0; sym_arr && (sym = sym_arr[i]); i++)
get_symbol_str(&res, sym, head);
if (!i)
diff --git a/scripts/kconfig/nconf.c b/scripts/kconfig/nconf.c
index 521700ed7152..fc39830a850b 100644
--- a/scripts/kconfig/nconf.c
+++ b/scripts/kconfig/nconf.c
@@ -741,7 +741,7 @@ static void search_conf(void)
.target = NULL,
};
jump_key_char = 0;
- res = get_relations_str(sym_arr, &head);
+ res = get_relations_str(sym_arr, &head, 0);
dres = show_scroll_win_ext(main_window,
"Search Results", str_get(&res),
&vscroll, &hscroll,
diff --git a/scripts/kconfig/util.c b/scripts/kconfig/util.c
index 0809aa061b6a..1de072e45ca3 100644
--- a/scripts/kconfig/util.c
+++ b/scripts/kconfig/util.c
@@ -77,6 +77,7 @@ struct gstr str_new(void)
gs.s = xmalloc(sizeof(char) * 64);
gs.len = 64;
gs.max_width = 0;
+ gs.indent = 0;
strcpy(gs.s, "\0");
return gs;
}
--
2.55.0
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH] kconfig: wrap long expressions in menuconfig
2026-10-10 9:50 [PATCH] kconfig: wrap long expressions in menuconfig Julian Braha
@ 2026-10-11 16:20 ` Miguel Ojeda
2026-10-11 18:22 ` Julian Braha
0 siblings, 1 reply; 3+ messages in thread
From: Miguel Ojeda @ 2026-10-11 16:20 UTC (permalink / raw)
To: Julian Braha
Cc: nathan, nsc, ojeda, a.hindborg, nico, rdunlap, grahamr,
jakub.git, linux-kbuild, linux-kernel, rust-for-linux
On Sat, Oct 10, 2026 at 11:50 AM Julian Braha <julianbraha@gmail.com> wrote:
>
> In an offline conversation, Miguel Ojeda reported that scrolling
> horizontally in menuconfig's search to read RUST's dependencies has been
> slowing down debugging.
Thanks for working on this!
This is definitely useful, because users (and ourselves) have had to
scroll horizontally too much for `CONFIG_RUST` :)
Maybe it could help to clarify here that "debugging" above means
understanding by users (i.e. not developers) why the dependencies are
not satisfied. `menuconfig`'s search is uniquely useful because it
shows the expressions with the current values; i.e. this is useful for
most users dealing with long configs like `RUST`'s.
Is this patch the live one you showed me back in the conference?
By the way, for context and future reference before I forget, I
suggested a few other bits:
- Wrapping on the top-level `&&` operators.
- Even better, wrapping on the actual `depends on` lines, rather
than every top-level `&&`, because sometimes they are logically
grouped, even if they may use themselves `&&` for the condition. But
IIRC we discussed that this would require tracking more state and/or
reworking part of the parser.
- For completeness, I also mentioned potentially considering to show
(a subset of) attached comments, but that is not really needed and it
is really low hanging fruit that is most likely not worth its
complexity: we already have descriptions and most options are not as
complex as `RUST`'s.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] kconfig: wrap long expressions in menuconfig
2026-10-11 16:20 ` Miguel Ojeda
@ 2026-10-11 18:22 ` Julian Braha
0 siblings, 0 replies; 3+ messages in thread
From: Julian Braha @ 2026-10-11 18:22 UTC (permalink / raw)
To: Miguel Ojeda
Cc: nathan, nsc, ojeda, a.hindborg, nico, rdunlap, grahamr,
jakub.git, linux-kbuild, linux-kernel, rust-for-linux
On 10/11/26 17:20, Miguel Ojeda wrote:
> On Sat, Oct 10, 2026 at 11:50 AM Julian Braha <julianbraha@gmail.com> wrote:
>> In an offline conversation, Miguel Ojeda reported that scrolling
>> horizontally in menuconfig's search to read RUST's dependencies has been
>> slowing down debugging.
> Thanks for working on this!
>
> This is definitely useful, because users (and ourselves) have had to
> scroll horizontally too much for `CONFIG_RUST` 🙂
Yeah the feedback I got from everyone I showed this to at LPC was, "any
form of wrapping is better than horizontal scrolling."
> Maybe it could help to clarify here that "debugging" above means
> understanding by users (i.e. not developers) why the dependencies are
> not satisfied. `menuconfig`'s search is uniquely useful because it
> shows the expressions with the current values; i.e. this is useful for
> most users dealing with long configs like `RUST`'s.
>
> Is this patch the live one you showed me back in the conference?
Sort of, the version I showed at LPC was completely vibe-coded, so I
could quickly get feedback on the visuals.
This patch still creates that same visual look, but I've cleaned up the
code.
>
> By the way, for context and future reference before I forget, I
> suggested a few other bits:
>
> - Wrapping on the top-level `&&` operators.
I personally liked this style when I tried it, but another maintainer at
LPC didn't: "I want menuconfig to take advantage of the full size of my
screen."
>
> - Even better, wrapping on the actual `depends on` lines, rather
> than every top-level `&&`, because sometimes they are logically
> grouped, even if they may use themselves `&&` for the condition. But
> IIRC we discussed that this would require tracking more state and/or
> reworking part of the parser.
Right, that would be a much larger change. I do intend to iterate on
this in the future, but wanted to start with something minimal.
>
> - For completeness, I also mentioned potentially considering to show
> (a subset of) attached comments, but that is not really needed and it
> is really low hanging fruit that is most likely not worth its
> complexity: we already have descriptions and most options are not as
> complex as `RUST`'s.
This idea turned out to be controversial. Another maintainer pointed out
that you don't always know exactly which lines the comment refers to.
Anyway, thanks for your feedback!
- Julian Braha
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-10-11 18:23 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-10 9:50 [PATCH] kconfig: wrap long expressions in menuconfig Julian Braha
2026-10-11 16:20 ` Miguel Ojeda
2026-10-11 18:22 ` Julian Braha
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®