2012-08-23 18:55:46

by Benjamin Poirier

[permalink] [raw]
Subject: [PATCH v2 0/6] menuconfig: jump to search results

This patch series adds "jump to" keys (similar to the cscope interface) to the
search results of "make menuconfig" so that we can go directly to the menu
entry for a config option after searching for it.

Patches 1-4 implement the basic functionality.
Patches 5-6 are an optional improvement.

Changes v1-v2:
* add a short description to patches 2 & 3
* correct checkpatch style warnings on patch 6

It should be targeted for v3.7

Enjoy,
-Benjamin


2012-08-23 18:55:57

by Benjamin Poirier

[permalink] [raw]
Subject: [PATCH v2 1/6] menuconfig: Remove superfluous conditionnal

Because end_reached is set to 0 before the loop, the test "!end_reached" is
always true and can be removed. This structure was perhaps copied from the
similar one in back_lines().

Signed-off-by: Benjamin Poirier <[email protected]>
---
scripts/kconfig/lxdialog/textbox.c | 8 +++-----
1 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/scripts/kconfig/lxdialog/textbox.c b/scripts/kconfig/lxdialog/textbox.c
index 4e5de60..264a2b9 100644
--- a/scripts/kconfig/lxdialog/textbox.c
+++ b/scripts/kconfig/lxdialog/textbox.c
@@ -357,10 +357,8 @@ static char *get_line(void)
end_reached = 0;
while (*page != '\n') {
if (*page == '\0') {
- if (!end_reached) {
- end_reached = 1;
- break;
- }
+ end_reached = 1;
+ break;
} else if (i < MAX_LEN)
line[i++] = *(page++);
else {
@@ -373,7 +371,7 @@ static char *get_line(void)
if (i <= MAX_LEN)
line[i] = '\0';
if (!end_reached)
- page++; /* move pass '\n' */
+ page++; /* move past '\n' */

return line;
}
--
1.7.7

2012-08-23 18:56:06

by Benjamin Poirier

[permalink] [raw]
Subject: [PATCH v2 4/6] menuconfig: Add jump keys to search results

makes it possible to jump directly to the menu for a configuration entry after
having searched for it with '/'. If this menu is not currently accessible we
jump to the nearest accessible parent instead. After exiting this menu, the
user is returned to the search results where he may jump further in or
elsewhere.

Signed-off-by: Benjamin Poirier <[email protected]>
---
scripts/kconfig/expr.h | 2 +
scripts/kconfig/lkc_proto.h | 6 +++-
scripts/kconfig/mconf.c | 64 +++++++++++++++++++++++++++++--------------
scripts/kconfig/menu.c | 55 +++++++++++++++++++++++++++++-------
scripts/kconfig/nconf.c | 2 +-
5 files changed, 94 insertions(+), 35 deletions(-)

diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h
index d4ecce8..52f4246 100644
--- a/scripts/kconfig/expr.h
+++ b/scripts/kconfig/expr.h
@@ -173,6 +173,8 @@ struct menu {
#define MENU_CHANGED 0x0001
#define MENU_ROOT 0x0002

+#define JUMP_NB 9
+
extern struct file *file_list;
extern struct file *current_file;
struct file *lookup_file(const char *name);
diff --git a/scripts/kconfig/lkc_proto.h b/scripts/kconfig/lkc_proto.h
index 47fe9c3..946c2cb3 100644
--- a/scripts/kconfig/lkc_proto.h
+++ b/scripts/kconfig/lkc_proto.h
@@ -21,8 +21,10 @@ P(menu_get_root_menu,struct menu *,(struct menu *menu));
P(menu_get_parent_menu,struct menu *,(struct menu *menu));
P(menu_has_help,bool,(struct menu *menu));
P(menu_get_help,const char *,(struct menu *menu));
-P(get_symbol_str, void, (struct gstr *r, struct symbol *sym));
-P(get_relations_str, struct gstr, (struct symbol **sym_arr));
+P(get_symbol_str, int, (struct gstr *r, struct symbol *sym, struct menu
+ **jumps, int jump_nb));
+P(get_relations_str, struct gstr, (struct symbol **sym_arr, struct menu
+ **jumps));
P(menu_get_ext_help,void,(struct menu *menu, struct gstr *help));

/* symbol.c */
diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c
index c57cc6a..b389e47 100644
--- a/scripts/kconfig/mconf.c
+++ b/scripts/kconfig/mconf.c
@@ -236,16 +236,19 @@ search_help[] = N_(
"Result:\n"
"-----------------------------------------------------------------\n"
"Symbol: FOO [=m]\n"
+ "Type : tristate\n"
"Prompt: Foo bus is used to drive the bar HW\n"
- "Defined at drivers/pci/Kconfig:47\n"
- "Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
- "Location:\n"
- " -> Bus options (PCI, PCMCIA, EISA, ISA)\n"
- " -> PCI support (PCI [=y])\n"
- " -> PCI access mode (<choice> [=y])\n"
- "Selects: LIBCRC32\n"
- "Selected by: BAR\n"
+ " Defined at drivers/pci/Kconfig:47\n"
+ " Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
+ " Location:\n"
+ " -> Bus options (PCI, PCMCIA, EISA, ISA)\n"
+ " -> PCI support (PCI [=y])\n"
+ "(1) -> PCI access mode (<choice> [=y])\n"
+ " Selects: LIBCRC32\n"
+ " Selected by: BAR\n"
"-----------------------------------------------------------------\n"
+ "o The line 'Type:' shows the type of the configuration option for\n"
+ " this symbol (boolean, tristate, string, ...)\n"
"o The line 'Prompt:' shows the text used in the menu structure for\n"
" this symbol\n"
"o The 'Defined at' line tell at what file / line number the symbol\n"
@@ -254,8 +257,12 @@ search_help[] = N_(
" this symbol to be visible in the menu (selectable)\n"
"o The 'Location:' lines tell where in the menu structure this symbol\n"
" is located\n"
- " A location followed by a [=y] indicate that this is a selectable\n"
- " menu item - and current value is displayed inside brackets.\n"
+ " A location followed by a [=y] indicates that this is a\n"
+ " selectable menu item - and the current value is displayed inside\n"
+ " brackets.\n"
+ " Press the key in the (#) prefix to jump directly to that\n"
+ " location. You will be returned to the current search results\n"
+ " after exiting this new menu.\n"
"o The 'Selects:' line tell what symbol will be automatically\n"
" selected if this symbol is selected (y or m)\n"
"o The 'Selected by' line tell what symbol has selected this symbol\n"
@@ -275,7 +282,7 @@ static int single_menu_mode;
static int show_all_options;
static int saved_x, saved_y;

-static void conf(struct menu *menu);
+static void conf(struct menu *menu, struct menu *active_menu);
static void conf_choice(struct menu *menu);
static void conf_string(struct menu *menu);
static void conf_load(void);
@@ -309,7 +316,9 @@ static void search_conf(void)
struct symbol **sym_arr;
struct gstr res;
char *dialog_input;
- int dres;
+ int dres, vscroll = 0, hscroll = 0;
+ bool again;
+
again:
dialog_clear();
dres = dialog_inputbox(_("Search Configuration Parameter"),
@@ -332,10 +341,24 @@ again:
dialog_input += strlen(CONFIG_);

sym_arr = sym_re_search(dialog_input);
- res = get_relations_str(sym_arr);
+ do {
+ struct menu *jumps[JUMP_NB] = {0};
+ int keys[JUMP_NB + 1] = {0}, i;
+
+ res = get_relations_str(sym_arr, jumps);
+ for (i = 0; i < JUMP_NB && jumps[i]; i++)
+ keys[i] = '1' + i;
+ dres = show_textbox_ext(_("Search Results"), str_get(&res), 0,
+ 0, keys, &vscroll, &hscroll);
+ again = false;
+ for (i = 0; i < JUMP_NB && jumps[i]; i++)
+ if (dres == keys[i]) {
+ conf(jumps[i]->parent, jumps[i]);
+ again = true;
+ }
+ str_free(&res);
+ } while (again);
free(sym_arr);
- show_textbox(_("Search Results"), str_get(&res), 0, 0);
- str_free(&res);
}

static void build_conf(struct menu *menu)
@@ -516,12 +539,11 @@ conf_childs:
indent -= doint;
}

-static void conf(struct menu *menu)
+static void conf(struct menu *menu, struct menu *active_menu)
{
struct menu *submenu;
const char *prompt = menu_get_prompt(menu);
struct symbol *sym;
- struct menu *active_menu = NULL;
int res;
int s_scroll = 0;

@@ -564,13 +586,13 @@ static void conf(struct menu *menu)
if (single_menu_mode)
submenu->data = (void *) (long) !submenu->data;
else
- conf(submenu);
+ conf(submenu, NULL);
break;
case 't':
if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
conf_choice(submenu);
else if (submenu->prompt->type == P_MENU)
- conf(submenu);
+ conf(submenu, NULL);
break;
case 's':
conf_string(submenu);
@@ -609,7 +631,7 @@ static void conf(struct menu *menu)
if (item_is_tag('t'))
sym_toggle_tristate_value(sym);
else if (item_is_tag('m'))
- conf(submenu);
+ conf(submenu, NULL);
break;
case 7:
search_conf();
@@ -881,7 +903,7 @@ int main(int ac, char **av)

set_config_filename(conf_get_configname());
do {
- conf(&rootmenu);
+ conf(&rootmenu, NULL);
res = handle_exit();
} while (res == KEY_ESC);

diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c
index 8c2a97e..a524185 100644
--- a/scripts/kconfig/menu.c
+++ b/scripts/kconfig/menu.c
@@ -507,10 +507,12 @@ const char *menu_get_help(struct menu *menu)
return "";
}

-static void get_prompt_str(struct gstr *r, struct property *prop)
+static int get_prompt_str(struct gstr *r, struct property *prop, struct menu
+ **jumps, int jump_nb)
{
int i, j;
- struct menu *submenu[8], *menu;
+ char header[4];
+ struct menu *submenu[8], *menu, *location = NULL;

str_printf(r, _("Prompt: %s\n"), _(prop->text));
str_printf(r, _(" Defined at %s:%d\n"), prop->menu->file->name,
@@ -521,13 +523,34 @@ static void get_prompt_str(struct gstr *r, struct property *prop)
str_append(r, "\n");
}
menu = prop->menu->parent;
- for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent)
+ for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent) {
+ bool accessible = menu_is_visible(menu);
+
submenu[i++] = menu;
+ if (location == NULL && accessible)
+ location = menu;
+ }
+ if (jumps && jump_nb < JUMP_NB && location) {
+ if (menu_is_visible(prop->menu)) {
+ /*
+ * There is not enough room to put the hint at the
+ * beginning of the "Prompt" line. Put the hint on the
+ * last "Location" line even when it would belong on
+ * the former.
+ */
+ jumps[jump_nb] = prop->menu;
+ } else
+ jumps[jump_nb] = location;
+ snprintf(header, 4, "(%d)", jump_nb + 1);
+ } else
+ location = NULL;
+
if (i > 0) {
str_printf(r, _(" Location:\n"));
- for (j = 4; --i >= 0; j += 2) {
+ for (j = 1; --i >= 0; j += 2) {
menu = submenu[i];
- str_printf(r, "%*c-> %s", j, ' ', _(menu_get_prompt(menu)));
+ str_printf(r, "%s%*c-> %s", menu == location ? header
+ : " ", j, ' ', _(menu_get_prompt(menu)));
if (menu->sym) {
str_printf(r, " (%s [=%s])", menu->sym->name ?
menu->sym->name : _("<choice>"),
@@ -536,12 +559,20 @@ static void get_prompt_str(struct gstr *r, struct property *prop)
str_append(r, "\n");
}
}
+
+ return location ? 1 : 0;
}

-void get_symbol_str(struct gstr *r, struct symbol *sym)
+/*
+ * jumps is optional and may be NULL
+ * returns the number of jumps inserted
+ */
+int get_symbol_str(struct gstr *r, struct symbol *sym, struct menu **jumps,
+ int jump_nb)
{
bool hit;
struct property *prop;
+ int i = 0;

if (sym && sym->name) {
str_printf(r, "Symbol: %s [=%s]\n", sym->name,
@@ -557,7 +588,7 @@ void get_symbol_str(struct gstr *r, struct symbol *sym)
}
}
for_all_prompts(sym, prop)
- get_prompt_str(r, prop);
+ i += get_prompt_str(r, prop, jumps, jump_nb + i);
hit = false;
for_all_properties(sym, prop, P_SELECT) {
if (!hit) {
@@ -575,16 +606,18 @@ void get_symbol_str(struct gstr *r, struct symbol *sym)
str_append(r, "\n");
}
str_append(r, "\n\n");
+
+ return i;
}

-struct gstr get_relations_str(struct symbol **sym_arr)
+struct gstr get_relations_str(struct symbol **sym_arr, struct menu **jumps)
{
struct symbol *sym;
struct gstr res = str_new();
- int i;
+ int i, jump_nb = 0;

for (i = 0; sym_arr && (sym = sym_arr[i]); i++)
- get_symbol_str(&res, sym);
+ jump_nb += get_symbol_str(&res, sym, jumps, jump_nb);
if (!i)
str_append(&res, _("No matches found.\n"));
return res;
@@ -603,5 +636,5 @@ void menu_get_ext_help(struct menu *menu, struct gstr *help)
}
str_printf(help, "%s\n", _(help_text));
if (sym)
- get_symbol_str(help, sym);
+ get_symbol_str(help, sym, NULL, 0);
}
diff --git a/scripts/kconfig/nconf.c b/scripts/kconfig/nconf.c
index 1704a85..87d4b15 100644
--- a/scripts/kconfig/nconf.c
+++ b/scripts/kconfig/nconf.c
@@ -721,7 +721,7 @@ again:
dialog_input += strlen(CONFIG_);

sym_arr = sym_re_search(dialog_input);
- res = get_relations_str(sym_arr);
+ res = get_relations_str(sym_arr, NULL);
free(sym_arr);
show_scroll_win(main_window,
_("Search Results"), str_get(&res));
--
1.7.7

2012-08-23 18:56:14

by Benjamin Poirier

[permalink] [raw]
Subject: [PATCH v2 5/6] menuconfig: Do not open code textbox scroll up/down

We don't need to explicitely use ncurses' scroll(). ncurses performs
vertical-motion optimization at wrefresh() time.

Using strace I confirmed that with the following patch curses still sends only
the new line of text to the terminal when scrolling up/down one line at a
time.

Signed-off-by: Benjamin Poirier <[email protected]>
---
scripts/kconfig/lxdialog/textbox.c | 55 +++++++----------------------------
1 files changed, 11 insertions(+), 44 deletions(-)

diff --git a/scripts/kconfig/lxdialog/textbox.c b/scripts/kconfig/lxdialog/textbox.c
index 506a095..3b3c5c4 100644
--- a/scripts/kconfig/lxdialog/textbox.c
+++ b/scripts/kconfig/lxdialog/textbox.c
@@ -166,40 +166,12 @@ do_resize:
case 'K': /* Previous line */
case 'k':
case KEY_UP:
- if (!begin_reached) {
- int passed_end = 0;
-
- back_lines(page_length + 1);
-
- /* We don't call print_page() here but use
- * scrolling to ensure faster screen update.
- * However, 'end_reached' and 'page_length'
- * should still be updated, and 'page' should
- * point to start of next page. This is done
- * by calling get_line() in the following
- * 'for' loop. */
- scrollok(box, TRUE);
- wscrl(box, -1); /* Scroll box region down one line */
- scrollok(box, FALSE);
- page_length = 0;
- for (i = 0; i < boxh; i++) {
- if (!i) {
- /* print first line of page */
- print_line(box, 0, boxw);
- wnoutrefresh(box);
- } else
- /* Called to update 'end_reached' and 'page' */
- get_line();
- if (!passed_end)
- page_length++;
- if (end_reached && !passed_end)
- passed_end = 1;
- }
+ if (begin_reached)
+ break;

- print_position(dialog);
- wmove(dialog, cur_y, cur_x); /* Restore cursor position */
- wrefresh(dialog);
- }
+ back_lines(page_length + 1);
+ refresh_text_box(dialog, box, boxh, boxw, cur_y,
+ cur_x);
break;
case 'B': /* Previous page */
case 'b':
@@ -214,17 +186,12 @@ do_resize:
case 'J': /* Next line */
case 'j':
case KEY_DOWN:
- if (!end_reached) {
- begin_reached = 0;
- scrollok(box, TRUE);
- scroll(box); /* Scroll box region up one line */
- scrollok(box, FALSE);
- print_line(box, boxh - 1, boxw);
- wnoutrefresh(box);
- print_position(dialog);
- wmove(dialog, cur_y, cur_x); /* Restore cursor position */
- wrefresh(dialog);
- }
+ if (end_reached)
+ break;
+
+ back_lines(page_length - 1);
+ refresh_text_box(dialog, box, boxh, boxw, cur_y,
+ cur_x);
break;
case KEY_NPAGE: /* Next page */
case ' ':
--
1.7.7

2012-08-23 18:56:33

by Benjamin Poirier

[permalink] [raw]
Subject: [PATCH v2 6/6] menuconfig: Assign jump keys per-page instead of globally

At the moment, keys 1-9 are assigned to the first 9 search results. This patch
makes them assigned to the first 9 results per-page instead. We are much less
likely to run out of keys that way.

Signed-off-by: Benjamin Poirier <[email protected]>
---
scripts/kconfig/expr.h | 9 ++++
scripts/kconfig/lkc_proto.h | 8 ++--
scripts/kconfig/lxdialog/dialog.h | 9 +++-
scripts/kconfig/lxdialog/textbox.c | 67 ++++++++++++++++++++------------
scripts/kconfig/mconf.c | 73 ++++++++++++++++++++++++++++-------
scripts/kconfig/menu.c | 54 ++++++++++++++-------------
6 files changed, 148 insertions(+), 72 deletions(-)

diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h
index 52f4246..bd2e098 100644
--- a/scripts/kconfig/expr.h
+++ b/scripts/kconfig/expr.h
@@ -12,6 +12,7 @@ extern "C" {

#include <assert.h>
#include <stdio.h>
+#include <sys/queue.h>
#ifndef __cplusplus
#include <stdbool.h>
#endif
@@ -173,6 +174,14 @@ struct menu {
#define MENU_CHANGED 0x0001
#define MENU_ROOT 0x0002

+struct jump_key {
+ CIRCLEQ_ENTRY(jump_key) entries;
+ size_t offset;
+ struct menu *target;
+ int index;
+};
+CIRCLEQ_HEAD(jk_head, jump_key);
+
#define JUMP_NB 9

extern struct file *file_list;
diff --git a/scripts/kconfig/lkc_proto.h b/scripts/kconfig/lkc_proto.h
index 946c2cb3..1d1c085 100644
--- a/scripts/kconfig/lkc_proto.h
+++ b/scripts/kconfig/lkc_proto.h
@@ -21,10 +21,10 @@ P(menu_get_root_menu,struct menu *,(struct menu *menu));
P(menu_get_parent_menu,struct menu *,(struct menu *menu));
P(menu_has_help,bool,(struct menu *menu));
P(menu_get_help,const char *,(struct menu *menu));
-P(get_symbol_str, int, (struct gstr *r, struct symbol *sym, struct menu
- **jumps, int jump_nb));
-P(get_relations_str, struct gstr, (struct symbol **sym_arr, struct menu
- **jumps));
+P(get_symbol_str, void, (struct gstr *r, struct symbol *sym, struct jk_head
+ *head));
+P(get_relations_str, struct gstr, (struct symbol **sym_arr, struct jk_head
+ *head));
P(menu_get_ext_help,void,(struct menu *menu, struct gstr *help));

/* symbol.c */
diff --git a/scripts/kconfig/lxdialog/dialog.h b/scripts/kconfig/lxdialog/dialog.h
index 8e7f43b..2a15c86 100644
--- a/scripts/kconfig/lxdialog/dialog.h
+++ b/scripts/kconfig/lxdialog/dialog.h
@@ -209,8 +209,13 @@ int first_alpha(const char *string, const char *exempt);
int dialog_yesno(const char *title, const char *prompt, int height, int width);
int dialog_msgbox(const char *title, const char *prompt, int height,
int width, int pause);
-int dialog_textbox(const char *title, const char *file, int height, int width,
- int *keys, int *_vscroll, int *_hscroll);
+
+
+typedef void (*update_text_fn)(char *buf, size_t start, size_t end, void
+ *_data);
+int dialog_textbox(const char *title, char *tbuf, int initial_height,
+ int initial_width, int *keys, int *_vscroll, int *_hscroll,
+ update_text_fn update_text, void *data);
int dialog_menu(const char *title, const char *prompt,
const void *selected, int *s_scroll);
int dialog_checklist(const char *title, const char *prompt, int height,
diff --git a/scripts/kconfig/lxdialog/textbox.c b/scripts/kconfig/lxdialog/textbox.c
index 3b3c5c4..a48bb93 100644
--- a/scripts/kconfig/lxdialog/textbox.c
+++ b/scripts/kconfig/lxdialog/textbox.c
@@ -22,23 +22,25 @@
#include "dialog.h"

static void back_lines(int n);
-static void print_page(WINDOW * win, int height, int width);
-static void print_line(WINDOW * win, int row, int width);
+static void print_page(WINDOW *win, int height, int width, update_text_fn
+ update_text, void *data);
+static void print_line(WINDOW *win, int row, int width);
static char *get_line(void);
static void print_position(WINDOW * win);

static int hscroll;
static int begin_reached, end_reached, page_length;
-static const char *buf;
-static const char *page;
+static char *buf;
+static char *page;

/*
* refresh window content
*/
static void refresh_text_box(WINDOW *dialog, WINDOW *box, int boxh, int boxw,
- int cur_y, int cur_x)
+ int cur_y, int cur_x, update_text_fn update_text,
+ void *data)
{
- print_page(box, boxh, boxw);
+ print_page(box, boxh, boxw, update_text, data);
print_position(dialog);
wmove(dialog, cur_y, cur_x); /* Restore cursor position */
wrefresh(dialog);
@@ -49,9 +51,11 @@ static void refresh_text_box(WINDOW *dialog, WINDOW *box, int boxh, int boxw,
* Display text from a file in a dialog box.
*
* keys is a null-terminated array
+ * update_text() may not add or remove any '\n' or '\0' in tbuf
*/
-int dialog_textbox(const char *title, const char *tbuf, int initial_height,
- int initial_width, int *keys, int *_vscroll, int *_hscroll)
+int dialog_textbox(const char *title, char *tbuf, int initial_height,
+ int initial_width, int *keys, int *_vscroll, int *_hscroll,
+ update_text_fn update_text, void *data)
{
int i, x, y, cur_x, cur_y, key = 0;
int height, width, boxh, boxw;
@@ -131,7 +135,8 @@ do_resize:

/* Print first page of text */
attr_clear(box, boxh, boxw, dlg.dialog.atr);
- refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
+ refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x, update_text,
+ data);

while (!done) {
key = wgetch(dialog);
@@ -150,7 +155,8 @@ do_resize:
begin_reached = 1;
page = buf;
refresh_text_box(dialog, box, boxh, boxw,
- cur_y, cur_x);
+ cur_y, cur_x, update_text,
+ data);
}
break;
case 'G': /* Last page */
@@ -160,8 +166,8 @@ do_resize:
/* point to last char in buf */
page = buf + strlen(buf);
back_lines(boxh);
- refresh_text_box(dialog, box, boxh, boxw,
- cur_y, cur_x);
+ refresh_text_box(dialog, box, boxh, boxw, cur_y,
+ cur_x, update_text, data);
break;
case 'K': /* Previous line */
case 'k':
@@ -171,7 +177,7 @@ do_resize:

back_lines(page_length + 1);
refresh_text_box(dialog, box, boxh, boxw, cur_y,
- cur_x);
+ cur_x, update_text, data);
break;
case 'B': /* Previous page */
case 'b':
@@ -180,8 +186,8 @@ do_resize:
if (begin_reached)
break;
back_lines(page_length + boxh);
- refresh_text_box(dialog, box, boxh, boxw,
- cur_y, cur_x);
+ refresh_text_box(dialog, box, boxh, boxw, cur_y,
+ cur_x, update_text, data);
break;
case 'J': /* Next line */
case 'j':
@@ -191,7 +197,7 @@ do_resize:

back_lines(page_length - 1);
refresh_text_box(dialog, box, boxh, boxw, cur_y,
- cur_x);
+ cur_x, update_text, data);
break;
case KEY_NPAGE: /* Next page */
case ' ':
@@ -200,8 +206,8 @@ do_resize:
break;

begin_reached = 0;
- refresh_text_box(dialog, box, boxh, boxw,
- cur_y, cur_x);
+ refresh_text_box(dialog, box, boxh, boxw, cur_y,
+ cur_x, update_text, data);
break;
case '0': /* Beginning of line */
case 'H': /* Scroll left */
@@ -216,8 +222,8 @@ do_resize:
hscroll--;
/* Reprint current page to scroll horizontally */
back_lines(page_length);
- refresh_text_box(dialog, box, boxh, boxw,
- cur_y, cur_x);
+ refresh_text_box(dialog, box, boxh, boxw, cur_y,
+ cur_x, update_text, data);
break;
case 'L': /* Scroll right */
case 'l':
@@ -227,8 +233,8 @@ do_resize:
hscroll++;
/* Reprint current page to scroll horizontally */
back_lines(page_length);
- refresh_text_box(dialog, box, boxh, boxw,
- cur_y, cur_x);
+ refresh_text_box(dialog, box, boxh, boxw, cur_y,
+ cur_x, update_text, data);
break;
case KEY_ESC:
if (on_key_esc(dialog) == KEY_ESC)
@@ -301,12 +307,23 @@ static void back_lines(int n)
}

/*
- * Print a new page of text. Called by dialog_textbox().
+ * Print a new page of text.
*/
-static void print_page(WINDOW * win, int height, int width)
+static void print_page(WINDOW *win, int height, int width, update_text_fn
+ update_text, void *data)
{
int i, passed_end = 0;

+ if (update_text) {
+ char *end;
+
+ for (i = 0; i < height; i++)
+ get_line();
+ end = page;
+ back_lines(height);
+ update_text(buf, page - buf, end - buf, data);
+ }
+
page_length = 0;
for (i = 0; i < height; i++) {
print_line(win, i, width);
@@ -319,7 +336,7 @@ static void print_page(WINDOW * win, int height, int width)
}

/*
- * Print a new line of text. Called by dialog_textbox() and print_page().
+ * Print a new line of text.
*/
static void print_line(WINDOW * win, int row, int width)
{
diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c
index b389e47..4cf920a 100644
--- a/scripts/kconfig/mconf.c
+++ b/scripts/kconfig/mconf.c
@@ -287,8 +287,9 @@ static void conf_choice(struct menu *menu);
static void conf_string(struct menu *menu);
static void conf_load(void);
static void conf_save(void);
-static int show_textbox_ext(const char *title, const char *text, int r, int c,
- int *keys, int *vscroll, int *hscroll);
+static int show_textbox_ext(const char *title, char *text, int r, int c,
+ int *keys, int *vscroll, int *hscroll,
+ update_text_fn update_text, void *data);
static void show_textbox(const char *title, const char *text, int r, int c);
static void show_helptext(const char *title, const char *text);
static void show_help(struct menu *menu);
@@ -311,6 +312,39 @@ static void set_config_filename(const char *config_filename)
}


+struct search_data {
+ struct jk_head *head;
+ struct menu **targets;
+ int *keys;
+};
+
+static void update_text(char *buf, size_t start, size_t end, void *_data)
+{
+ struct search_data *data = _data;
+ struct jump_key *pos;
+ int k = 0;
+
+ CIRCLEQ_FOREACH(pos, data->head, entries) {
+ if (pos->offset >= start && pos->offset < end) {
+ char header[4];
+
+ if (k < JUMP_NB) {
+ int key = '0' + (pos->index % JUMP_NB) + 1;
+
+ sprintf(header, "(%c)", key);
+ data->keys[k] = key;
+ data->targets[k] = pos->target;
+ k++;
+ } else {
+ sprintf(header, " ");
+ }
+
+ memcpy(buf + pos->offset, header, sizeof(header) - 1);
+ }
+ }
+ data->keys[k] = 0;
+}
+
static void search_conf(void)
{
struct symbol **sym_arr;
@@ -342,18 +376,24 @@ again:

sym_arr = sym_re_search(dialog_input);
do {
- struct menu *jumps[JUMP_NB] = {0};
- int keys[JUMP_NB + 1] = {0}, i;
+ struct jk_head head = CIRCLEQ_HEAD_INITIALIZER(head);
+ struct menu *targets[JUMP_NB];
+ int keys[JUMP_NB + 1], i;
+ struct search_data data = {
+ .head = &head,
+ .targets = targets,
+ .keys = keys,
+ };

- res = get_relations_str(sym_arr, jumps);
- for (i = 0; i < JUMP_NB && jumps[i]; i++)
- keys[i] = '1' + i;
- dres = show_textbox_ext(_("Search Results"), str_get(&res), 0,
- 0, keys, &vscroll, &hscroll);
+ res = get_relations_str(sym_arr, &head);
+ dres = show_textbox_ext(_("Search Results"), (char *)
+ str_get(&res), 0, 0, keys, &vscroll,
+ &hscroll, &update_text, (void *)
+ &data);
again = false;
- for (i = 0; i < JUMP_NB && jumps[i]; i++)
+ for (i = 0; i < JUMP_NB && keys[i]; i++)
if (dres == keys[i]) {
- conf(jumps[i]->parent, jumps[i]);
+ conf(targets[i]->parent, targets[i]);
again = true;
}
str_free(&res);
@@ -643,16 +683,19 @@ static void conf(struct menu *menu, struct menu *active_menu)
}
}

-static int show_textbox_ext(const char *title, const char *text, int r, int c,
- int *keys, int *vscroll, int *hscroll)
+static int show_textbox_ext(const char *title, char *text, int r, int c, int
+ *keys, int *vscroll, int *hscroll, update_text_fn
+ update_text, void *data)
{
dialog_clear();
- return dialog_textbox(title, text, r, c, keys, vscroll, hscroll);
+ return dialog_textbox(title, text, r, c, keys, vscroll, hscroll,
+ update_text, data);
}

static void show_textbox(const char *title, const char *text, int r, int c)
{
- show_textbox_ext(title, text, r, c, (int []) {0}, NULL, NULL);
+ show_textbox_ext(title, (char *) text, r, c, (int []) {0}, NULL, NULL,
+ NULL, NULL);
}

static void show_helptext(const char *title, const char *text)
diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c
index a524185..a3cade6 100644
--- a/scripts/kconfig/menu.c
+++ b/scripts/kconfig/menu.c
@@ -507,12 +507,12 @@ const char *menu_get_help(struct menu *menu)
return "";
}

-static int get_prompt_str(struct gstr *r, struct property *prop, struct menu
- **jumps, int jump_nb)
+static void get_prompt_str(struct gstr *r, struct property *prop,
+ struct jk_head *head)
{
int i, j;
- char header[4];
struct menu *submenu[8], *menu, *location = NULL;
+ struct jump_key *jump;

str_printf(r, _("Prompt: %s\n"), _(prop->text));
str_printf(r, _(" Defined at %s:%d\n"), prop->menu->file->name,
@@ -530,7 +530,9 @@ static int get_prompt_str(struct gstr *r, struct property *prop, struct menu
if (location == NULL && accessible)
location = menu;
}
- if (jumps && jump_nb < JUMP_NB && location) {
+ if (head && location) {
+ jump = malloc(sizeof(struct jump_key));
+
if (menu_is_visible(prop->menu)) {
/*
* There is not enough room to put the hint at the
@@ -538,19 +540,26 @@ static int get_prompt_str(struct gstr *r, struct property *prop, struct menu
* last "Location" line even when it would belong on
* the former.
*/
- jumps[jump_nb] = prop->menu;
+ jump->target = prop->menu;
} else
- jumps[jump_nb] = location;
- snprintf(header, 4, "(%d)", jump_nb + 1);
- } else
- location = NULL;
+ jump->target = location;
+
+ if (CIRCLEQ_EMPTY(head))
+ jump->index = 0;
+ else
+ jump->index = CIRCLEQ_LAST(head)->index + 1;
+
+ CIRCLEQ_INSERT_TAIL(head, jump, entries);
+ }

if (i > 0) {
str_printf(r, _(" Location:\n"));
- for (j = 1; --i >= 0; j += 2) {
+ for (j = 4; --i >= 0; j += 2) {
menu = submenu[i];
- str_printf(r, "%s%*c-> %s", menu == location ? header
- : " ", j, ' ', _(menu_get_prompt(menu)));
+ if (head && location && menu == location)
+ jump->offset = r->len - 1;
+ str_printf(r, "%*c-> %s", j, ' ',
+ _(menu_get_prompt(menu)));
if (menu->sym) {
str_printf(r, " (%s [=%s])", menu->sym->name ?
menu->sym->name : _("<choice>"),
@@ -559,20 +568,15 @@ static int get_prompt_str(struct gstr *r, struct property *prop, struct menu
str_append(r, "\n");
}
}
-
- return location ? 1 : 0;
}

/*
- * jumps is optional and may be NULL
- * returns the number of jumps inserted
+ * head is optional and may be NULL
*/
-int get_symbol_str(struct gstr *r, struct symbol *sym, struct menu **jumps,
- int jump_nb)
+void get_symbol_str(struct gstr *r, struct symbol *sym, struct jk_head *head)
{
bool hit;
struct property *prop;
- int i = 0;

if (sym && sym->name) {
str_printf(r, "Symbol: %s [=%s]\n", sym->name,
@@ -588,7 +592,7 @@ int get_symbol_str(struct gstr *r, struct symbol *sym, struct menu **jumps,
}
}
for_all_prompts(sym, prop)
- i += get_prompt_str(r, prop, jumps, jump_nb + i);
+ get_prompt_str(r, prop, head);
hit = false;
for_all_properties(sym, prop, P_SELECT) {
if (!hit) {
@@ -606,18 +610,16 @@ int get_symbol_str(struct gstr *r, struct symbol *sym, struct menu **jumps,
str_append(r, "\n");
}
str_append(r, "\n\n");
-
- return i;
}

-struct gstr get_relations_str(struct symbol **sym_arr, struct menu **jumps)
+struct gstr get_relations_str(struct symbol **sym_arr, struct jk_head *head)
{
struct symbol *sym;
struct gstr res = str_new();
- int i, jump_nb = 0;
+ int i;

for (i = 0; sym_arr && (sym = sym_arr[i]); i++)
- jump_nb += get_symbol_str(&res, sym, jumps, jump_nb);
+ get_symbol_str(&res, sym, head);
if (!i)
str_append(&res, _("No matches found.\n"));
return res;
@@ -636,5 +638,5 @@ void menu_get_ext_help(struct menu *menu, struct gstr *help)
}
str_printf(help, "%s\n", _(help_text));
if (sym)
- get_symbol_str(help, sym, NULL, 0);
+ get_symbol_str(help, sym, NULL);
}
--
1.7.7

2012-08-23 18:56:58

by Benjamin Poirier

[permalink] [raw]
Subject: [PATCH v2 3/6] menuconfig: Extend dialog_textbox so that it can return to a scrolled position

We can now display other UI elements (menus) "on top" of a textbox and then
seemingly come back to it in the same state it was left.

Signed-off-by: Benjamin Poirier <[email protected]>
---
scripts/kconfig/lxdialog/dialog.h | 2 +-
scripts/kconfig/lxdialog/textbox.c | 24 +++++++++++++++++++++++-
scripts/kconfig/mconf.c | 8 ++++----
3 files changed, 28 insertions(+), 6 deletions(-)

diff --git a/scripts/kconfig/lxdialog/dialog.h b/scripts/kconfig/lxdialog/dialog.h
index 014c029..8e7f43b 100644
--- a/scripts/kconfig/lxdialog/dialog.h
+++ b/scripts/kconfig/lxdialog/dialog.h
@@ -210,7 +210,7 @@ int dialog_yesno(const char *title, const char *prompt, int height, int width);
int dialog_msgbox(const char *title, const char *prompt, int height,
int width, int pause);
int dialog_textbox(const char *title, const char *file, int height, int width,
- int *keys);
+ int *keys, int *_vscroll, int *_hscroll);
int dialog_menu(const char *title, const char *prompt,
const void *selected, int *s_scroll);
int dialog_checklist(const char *title, const char *prompt, int height,
diff --git a/scripts/kconfig/lxdialog/textbox.c b/scripts/kconfig/lxdialog/textbox.c
index eb4ee92..506a095 100644
--- a/scripts/kconfig/lxdialog/textbox.c
+++ b/scripts/kconfig/lxdialog/textbox.c
@@ -51,7 +51,7 @@ static void refresh_text_box(WINDOW *dialog, WINDOW *box, int boxh, int boxw,
* keys is a null-terminated array
*/
int dialog_textbox(const char *title, const char *tbuf, int initial_height,
- int initial_width, int *keys)
+ int initial_width, int *keys, int *_vscroll, int *_hscroll)
{
int i, x, y, cur_x, cur_y, key = 0;
int height, width, boxh, boxw;
@@ -65,6 +65,15 @@ int dialog_textbox(const char *title, const char *tbuf, int initial_height,
buf = tbuf;
page = buf; /* page is pointer to start of page to be displayed */

+ if (_vscroll && *_vscroll) {
+ begin_reached = 0;
+
+ for (i = 0; i < *_vscroll; i++)
+ get_line();
+ }
+ if (_hscroll)
+ hscroll = *_hscroll;
+
do_resize:
getmaxyx(stdscr, height, width);
if (height < 8 || width < 8)
@@ -275,6 +284,19 @@ do_resize:
}
delwin(box);
delwin(dialog);
+ if (_vscroll) {
+ const char *s;
+
+ s = buf;
+ *_vscroll = 0;
+ back_lines(page_length);
+ while (s < page && (s = strchr(s, '\n'))) {
+ (*_vscroll)++;
+ s++;
+ }
+ }
+ if (_hscroll)
+ *_hscroll = hscroll;
return key;
}

diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c
index 116e5da..c57cc6a 100644
--- a/scripts/kconfig/mconf.c
+++ b/scripts/kconfig/mconf.c
@@ -281,7 +281,7 @@ static void conf_string(struct menu *menu);
static void conf_load(void);
static void conf_save(void);
static int show_textbox_ext(const char *title, const char *text, int r, int c,
- int *keys);
+ int *keys, int *vscroll, int *hscroll);
static void show_textbox(const char *title, const char *text, int r, int c);
static void show_helptext(const char *title, const char *text);
static void show_help(struct menu *menu);
@@ -622,15 +622,15 @@ static void conf(struct menu *menu)
}

static int show_textbox_ext(const char *title, const char *text, int r, int c,
- int *keys)
+ int *keys, int *vscroll, int *hscroll)
{
dialog_clear();
- return dialog_textbox(title, text, r, c, keys);
+ return dialog_textbox(title, text, r, c, keys, vscroll, hscroll);
}

static void show_textbox(const char *title, const char *text, int r, int c)
{
- show_textbox_ext(title, text, r, c, (int []) {0});
+ show_textbox_ext(title, text, r, c, (int []) {0}, NULL, NULL);
}

static void show_helptext(const char *title, const char *text)
--
1.7.7

2012-08-23 18:57:37

by Benjamin Poirier

[permalink] [raw]
Subject: [PATCH v2 2/6] menuconfig: Extend dialog_textbox so that it can exit on arbitrary keypresses

The caller will be able to perform actions based on hotkeys in the displayed
text.

Signed-off-by: Benjamin Poirier <[email protected]>
---
scripts/kconfig/lxdialog/dialog.h | 3 ++-
scripts/kconfig/lxdialog/textbox.c | 31 +++++++++++++++++++++----------
scripts/kconfig/mconf.c | 12 ++++++++++--
3 files changed, 33 insertions(+), 13 deletions(-)

diff --git a/scripts/kconfig/lxdialog/dialog.h b/scripts/kconfig/lxdialog/dialog.h
index b5211fc..014c029 100644
--- a/scripts/kconfig/lxdialog/dialog.h
+++ b/scripts/kconfig/lxdialog/dialog.h
@@ -209,7 +209,8 @@ int first_alpha(const char *string, const char *exempt);
int dialog_yesno(const char *title, const char *prompt, int height, int width);
int dialog_msgbox(const char *title, const char *prompt, int height,
int width, int pause);
-int dialog_textbox(const char *title, const char *file, int height, int width);
+int dialog_textbox(const char *title, const char *file, int height, int width,
+ int *keys);
int dialog_menu(const char *title, const char *prompt,
const void *selected, int *s_scroll);
int dialog_checklist(const char *title, const char *prompt, int height,
diff --git a/scripts/kconfig/lxdialog/textbox.c b/scripts/kconfig/lxdialog/textbox.c
index 264a2b9..eb4ee92 100644
--- a/scripts/kconfig/lxdialog/textbox.c
+++ b/scripts/kconfig/lxdialog/textbox.c
@@ -47,14 +47,16 @@ static void refresh_text_box(WINDOW *dialog, WINDOW *box, int boxh, int boxw,

/*
* Display text from a file in a dialog box.
+ *
+ * keys is a null-terminated array
*/
-int dialog_textbox(const char *title, const char *tbuf,
- int initial_height, int initial_width)
+int dialog_textbox(const char *title, const char *tbuf, int initial_height,
+ int initial_width, int *keys)
{
int i, x, y, cur_x, cur_y, key = 0;
int height, width, boxh, boxw;
- int passed_end;
WINDOW *dialog, *box;
+ bool done = false;

begin_reached = 1;
end_reached = 0;
@@ -122,7 +124,7 @@ do_resize:
attr_clear(box, boxh, boxw, dlg.dialog.atr);
refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);

- while ((key != KEY_ESC) && (key != '\n')) {
+ while (!done) {
key = wgetch(dialog);
switch (key) {
case 'E': /* Exit */
@@ -130,9 +132,9 @@ do_resize:
case 'X':
case 'x':
case 'q':
- delwin(box);
- delwin(dialog);
- return 0;
+ case '\n':
+ done = true;
+ break;
case 'g': /* First page */
case KEY_HOME:
if (!begin_reached) {
@@ -156,6 +158,8 @@ do_resize:
case 'k':
case KEY_UP:
if (!begin_reached) {
+ int passed_end = 0;
+
back_lines(page_length + 1);

/* We don't call print_page() here but use
@@ -169,7 +173,6 @@ do_resize:
wscrl(box, -1); /* Scroll box region down one line */
scrollok(box, FALSE);
page_length = 0;
- passed_end = 0;
for (i = 0; i < boxh; i++) {
if (!i) {
/* print first line of page */
@@ -252,7 +255,8 @@ do_resize:
cur_y, cur_x);
break;
case KEY_ESC:
- key = on_key_esc(dialog);
+ if (on_key_esc(dialog) == KEY_ESC)
+ done = true;
break;
case KEY_RESIZE:
back_lines(height);
@@ -260,11 +264,18 @@ do_resize:
delwin(dialog);
on_key_resize();
goto do_resize;
+ default:
+ for (i = 0; keys[i]; i++) {
+ if (key == keys[i]) {
+ done = true;
+ break;
+ }
+ }
}
}
delwin(box);
delwin(dialog);
- return key; /* ESC pressed */
+ return key;
}

/*
diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c
index f584a28..116e5da 100644
--- a/scripts/kconfig/mconf.c
+++ b/scripts/kconfig/mconf.c
@@ -280,6 +280,8 @@ static void conf_choice(struct menu *menu);
static void conf_string(struct menu *menu);
static void conf_load(void);
static void conf_save(void);
+static int show_textbox_ext(const char *title, const char *text, int r, int c,
+ int *keys);
static void show_textbox(const char *title, const char *text, int r, int c);
static void show_helptext(const char *title, const char *text);
static void show_help(struct menu *menu);
@@ -619,10 +621,16 @@ static void conf(struct menu *menu)
}
}

-static void show_textbox(const char *title, const char *text, int r, int c)
+static int show_textbox_ext(const char *title, const char *text, int r, int c,
+ int *keys)
{
dialog_clear();
- dialog_textbox(title, text, r, c);
+ return dialog_textbox(title, text, r, c, keys);
+}
+
+static void show_textbox(const char *title, const char *text, int r, int c)
+{
+ show_textbox_ext(title, text, r, c, (int []) {0});
}

static void show_helptext(const char *title, const char *text)
--
1.7.7

2012-08-24 15:50:05

by Borislav Petkov

[permalink] [raw]
Subject: Re: [PATCH v2 0/6] menuconfig: jump to search results

On Thu, Aug 23, 2012 at 02:55:02PM -0400, Benjamin Poirier wrote:
> This patch series adds "jump to" keys (similar to the cscope interface) to the
> search results of "make menuconfig" so that we can go directly to the menu
> entry for a config option after searching for it.
>
> Patches 1-4 implement the basic functionality.
> Patches 5-6 are an optional improvement.
>
> Changes v1-v2:
> * add a short description to patches 2 & 3
> * correct checkpatch style warnings on patch 6
>
> It should be targeted for v3.7

Ok, did a quick test on -rc3 here. Is it a feature that if I do a
search, go to the result, then do a search again and go to another
result from the second search... and do that a couple of times, clicking
on Exit at the lower part of the menu returns me back to all the
searches I had done? Much like a stack.

I mean, I don't mind it that way, it is workable - I'm just asking :).

Thanks.

--
Regards/Gruss,
Boris.

2012-08-24 17:25:27

by Benjamin Poirier

[permalink] [raw]
Subject: Re: [PATCH v2 0/6] menuconfig: jump to search results

On 2012/08/24 17:49, Borislav Petkov wrote:
> On Thu, Aug 23, 2012 at 02:55:02PM -0400, Benjamin Poirier wrote:
> > This patch series adds "jump to" keys (similar to the cscope interface) to the
> > search results of "make menuconfig" so that we can go directly to the menu
> > entry for a config option after searching for it.
> >
> > Patches 1-4 implement the basic functionality.
> > Patches 5-6 are an optional improvement.
> >
> > Changes v1-v2:
> > * add a short description to patches 2 & 3
> > * correct checkpatch style warnings on patch 6
> >
> > It should be targeted for v3.7
>
> Ok, did a quick test on -rc3 here. Is it a feature that if I do a
> search, go to the result, then do a search again and go to another
> result from the second search... and do that a couple of times, clicking
> on Exit at the lower part of the menu returns me back to all the
> searches I had done? Much like a stack.
>
> I mean, I don't mind it that way, it is workable - I'm just asking :).

That's how it's meant, yes. As stated in the log for patch 4:
menuconfig: Add jump keys to search results
[...] After exiting this menu, the user is returned to the
search results where he may jump further in or elsewhere.
... and after exiting the search results the user is returned to the
menu where he was before performing the search. That's the current
behavior.

A likely alternative would be that jumping to a menu entry would exit
the search results, forget the current position in the menus and go to
the new one as if the user had navigated to it from the root menu.

The current mconf.c code structure lends itself much better to the
"stacked" way of doing things. Each navigation to a deeper menu
corresponds to a nested conf() call. To do it the second way we'd have
to add logic to exit these nested conf() calls and redo new ones without
user intervention or change how it's handled altogether.

It seemed to me that choosing one approach over the other was debatable
UI-wise but that the first approach was definitely simpler code-wise.

2012-08-27 12:38:02

by Dirk Gouders

[permalink] [raw]
Subject: Re: [PATCH v2 4/6] menuconfig: Add jump keys to search results

Benjamin Poirier <[email protected]> writes:

> makes it possible to jump directly to the menu for a configuration entry after
> having searched for it with '/'. If this menu is not currently accessible we
> jump to the nearest accessible parent instead. After exiting this menu, the
> user is returned to the search results where he may jump further in or
> elsewhere.

I applied your patches, played with menuconfig and did not notice any
problems. What I especially like is that if I am guided to a nearest
accessible parent in cases where a menu is not currently accessible and
do modifications there, these modifications are visible in the search
results when I return to them.

Dirk