Compare commits

...

5 Commits

Author SHA1 Message Date
Přemysl Eric Janouch ec1f1031cc
Implement search iteration
The interactive search now has its own keymap, overriding "input".

Closes #5
2021-07-17 14:19:37 +02:00
Přemysl Eric Janouch bc99b3dd48
Add a right-side prompt to the search 2021-07-17 13:04:42 +02:00
Přemysl Eric Janouch e948741864
Enable pushing the search in a certain direction
We want to make it possible to iterate all current matches.
2021-07-17 08:49:54 +02:00
Přemysl Eric Janouch 0adbac2066
Make search() return the number of matches 2021-07-17 07:54:03 +02:00
Přemysl Eric Janouch 2238db5a4e
Make removing characters invoke g.editor_on_change 2021-07-17 07:24:16 +02:00
1 changed files with 64 additions and 23 deletions

87
sdn.cpp
View File

@ -459,8 +459,13 @@ static map<wint_t, action> g_input_actions {
{CTRL ('A'), ACTION_INPUT_BEGINNING}, {KEY (HOME), ACTION_INPUT_BEGINNING}, {CTRL ('A'), ACTION_INPUT_BEGINNING}, {KEY (HOME), ACTION_INPUT_BEGINNING},
{CTRL ('E'), ACTION_INPUT_END}, {KEY (END), ACTION_INPUT_END}, {CTRL ('E'), ACTION_INPUT_END}, {KEY (END), ACTION_INPUT_END},
}; };
static map<wint_t, action> g_search_actions {
{CTRL ('P'), ACTION_UP}, {KEY (UP), ACTION_UP},
{CTRL ('N'), ACTION_DOWN}, {KEY (DOWN), ACTION_DOWN},
};
static const map<string, map<wint_t, action>*> g_binding_contexts { static const map<string, map<wint_t, action>*> g_binding_contexts {
{"normal", &g_normal_actions}, {"input", &g_input_actions}, {"normal", &g_normal_actions}, {"input", &g_input_actions},
{"search", &g_search_actions},
}; };
#define LS(XX) XX(NORMAL, "no") XX(FILE, "fi") XX(RESET, "rs") \ #define LS(XX) XX(NORMAL, "no") XX(FILE, "fi") XX(RESET, "rs") \
@ -527,16 +532,18 @@ static struct {
bool out_of_date; ///< Entries may be out of date bool out_of_date; ///< Entries may be out of date
const wchar_t *editor; ///< Prompt string for editing const wchar_t *editor; ///< Prompt string for editing
wstring editor_info; ///< Right-side prompt while editing
wstring editor_line; ///< Current user input wstring editor_line; ///< Current user input
int editor_cursor = 0; ///< Cursor position int editor_cursor = 0; ///< Cursor position
bool editor_inserting; ///< Inserting a literal character bool editor_inserting; ///< Inserting a literal character
void (*editor_on_change) (); ///< Callback on editor change void (*editor_on_change) (); ///< Callback on editor change
void (*editor_on_confirm) (); ///< Callback on editor confirmation void (*editor_on_confirm) (); ///< Callback on editor confirmation
map<action, void (*) ()> editor_on; ///< Handlers for custom actions
enum { AT_CURSOR, AT_BAR, AT_CWD, AT_INPUT, AT_CMDLINE, AT_COUNT }; enum { AT_CURSOR, AT_BAR, AT_CWD, AT_INPUT, AT_INFO, AT_CMDLINE, AT_COUNT };
chtype attrs[AT_COUNT] = {A_REVERSE, 0, A_BOLD, 0, 0}; chtype attrs[AT_COUNT] = {A_REVERSE, 0, A_BOLD, 0, A_ITALIC, 0};
const char *attr_names[AT_COUNT] = const char *attr_names[AT_COUNT] =
{"cursor", "bar", "cwd", "input", "cmdline"}; {"cursor", "bar", "cwd", "input", "info", "cmdline"};
map<int, chtype> ls_colors; ///< LS_COLORS decoded map<int, chtype> ls_colors; ///< LS_COLORS decoded
map<string, chtype> ls_exts; ///< LS_COLORS file extensions map<string, chtype> ls_exts; ///< LS_COLORS file extensions
@ -750,9 +757,16 @@ fun update () {
curs_set (0); curs_set (0);
if (g.editor) { if (g.editor) {
move (LINES - 1, 0); move (LINES - 1, 0);
auto prompt = apply_attrs (wstring (g.editor) + L": ", 0); auto prompt = apply_attrs (wstring (g.editor) + L": ", 0),
auto line = apply_attrs (g.editor_line, 0); line = apply_attrs (g.editor_line, 0),
print (prompt + line, COLS - 1); info = apply_attrs (g.editor_info, g.attrs[g.AT_INFO]);
auto info_width = compute_width (info);
if (print (prompt + line, COLS - 1) < COLS - info_width) {
move (LINES - 1, COLS - info_width);
print (info, info_width);
}
auto start = sanitize (prompt + line.substr (0, g.editor_cursor)); auto start = sanitize (prompt + line.substr (0, g.editor_cursor));
move (LINES - 1, compute_width (start)); move (LINES - 1, compute_width (start));
curs_set (1); curs_set (1);
@ -989,17 +1003,32 @@ fun show_help () {
fclose (contents); fclose (contents);
} }
fun search (const wstring &needle) { /// Stays on the current match when there are no better ones, unless it's pushed
int best = g.cursor, best_n = 0; fun search (const wstring &needle, int push) -> int {
for (int i = 0; i < int (g.entries.size ()); i++) { int best = g.cursor, best_n = 0, matches = 0, step = push != 0 ? push : 1;
auto o = (i + g.cursor) % g.entries.size (); for (int i = 0, count = g.entries.size (); i < count; i++) {
auto o = (g.cursor + (count + i * step) + (count + push)) % count;
int n = prefix_length (to_wide (g.entries[o].filename), needle); int n = prefix_length (to_wide (g.entries[o].filename), needle);
matches += n == needle.size ();
if (n > best_n) { if (n > best_n) {
best = o; best = o;
best_n = n; best_n = n;
} }
} }
g.cursor = best; g.cursor = best;
return matches;
}
fun search_interactive (int push) {
int matches = search (g.editor_line, push);
if (g.editor_line.empty ())
g.editor_info.clear ();
else if (matches == 0)
g.editor_info = L"(no match)";
else if (matches == 1)
g.editor_info = L"(1 match)";
else
g.editor_info = L"(" + to_wstring (matches) + L" matches)";
} }
fun fix_cursor_and_offset () { fun fix_cursor_and_offset () {
@ -1058,7 +1087,7 @@ fun pop_levels (const string& old_cwd) {
fix_cursor_and_offset (); fix_cursor_and_offset ();
if (!anchor.empty () && at_cursor ().filename != anchor) if (!anchor.empty () && at_cursor ().filename != anchor)
search (to_wide (anchor)); search (to_wide (anchor), 0);
} }
fun explode_path (const string &path, vector<string> &out) { fun explode_path (const string &path, vector<string> &out) {
@ -1189,25 +1218,37 @@ fun move_towards_spacing (int diff) -> bool {
} }
fun handle_editor (wint_t c) { fun handle_editor (wint_t c) {
auto i = g_input_actions.find (g.editor_inserting ? WEOF : c); auto action = ACTION_NONE;
if (g.editor_inserting) { if (g.editor_inserting) {
(void) halfdelay (1); (void) halfdelay (1);
g.editor_inserting = false; g.editor_inserting = false;
} else {
auto i = g_input_actions.find (c);
if (i != g_input_actions.end ())
action = i->second;
auto m = g_binding_contexts.find (to_mb (g.editor));
if (m != g_binding_contexts.end ()
&& (i = m->second->find (c)) != m->second->end ())
action = i->second;
} }
switch (i == g_input_actions.end () ? ACTION_NONE : i->second) { auto original = g.editor_line;
switch (action) {
case ACTION_INPUT_CONFIRM: case ACTION_INPUT_CONFIRM:
if (g.editor_on_confirm) if (g.editor_on_confirm)
g.editor_on_confirm (); g.editor_on_confirm ();
// Fall-through // Fall-through
case ACTION_INPUT_ABORT: case ACTION_INPUT_ABORT:
g.editor = 0; g.editor = 0;
g.editor_info.clear ();
g.editor_line.clear (); g.editor_line.clear ();
g.editor_cursor = 0; g.editor_cursor = 0;
g.editor_inserting = false; g.editor_inserting = false;
g.editor_on_change = nullptr; g.editor_on_change = nullptr;
g.editor_on_confirm = nullptr; g.editor_on_confirm = nullptr;
break; g.editor_on.clear ();
return;
case ACTION_INPUT_BEGINNING: case ACTION_INPUT_BEGINNING:
g.editor_cursor = 0; g.editor_cursor = 0;
break; break;
@ -1251,15 +1292,17 @@ fun handle_editor (wint_t c) {
g.editor_inserting = true; g.editor_inserting = true;
break; break;
default: default:
if (c & (ALT | SYM)) { if (auto handler = g.editor_on[action]) {
handler ();
} else if (c & (ALT | SYM)) {
beep (); beep ();
} else { } else {
g.editor_line.insert (g.editor_cursor, 1, c); g.editor_line.insert (g.editor_cursor, 1, c);
g.editor_cursor++; g.editor_cursor++;
if (g.editor_on_change)
g.editor_on_change ();
} }
} }
if (g.editor_on_change && g.editor_line != original)
g.editor_on_change ();
} }
fun handle (wint_t c) -> bool { fun handle (wint_t c) -> bool {
@ -1371,12 +1414,10 @@ fun handle (wint_t c) -> bool {
case ACTION_SEARCH: case ACTION_SEARCH:
g.editor = L"search"; g.editor = L"search";
g.editor_on_change = [] { g.editor_on_change = [] { search_interactive (0); };
search (g.editor_line); g.editor_on[ACTION_UP] = [] { search_interactive (-1); };
}; g.editor_on[ACTION_DOWN] = [] { search_interactive (+1); };
g.editor_on_confirm = [] { g.editor_on_confirm = [] { choose (at_cursor ()); };
choose (at_cursor ());
};
break; break;
case ACTION_RENAME_PREFILL: case ACTION_RENAME_PREFILL:
g.editor_line = to_wide (current.filename); g.editor_line = to_wide (current.filename);