I read in the documentation that some time ago, the line edit logic was re-written to no longer use GNU readline, for better context-aware behaviour. However I miss the ability to jump back in history based on substrings. Holding up-arrow or CTRL-P to sequentially go back through history can be tedious, so I added some logic similar to the bash CTRL+R behaviour.
See patch below. Perhaps it could be improved, suggestions welcomed.
Index: src/LineInput.cc
===================================================================
--- src/LineInput.cc (revision 1477)
+++ src/LineInput.cc (working copy)
@@ -126,7 +126,9 @@
LineHistory::LineHistory(int maxl)
: current_line(0),
put(0),
- max_lines(maxl)
+ max_lines(maxl),
+ cur_search_substr( "" ),
+ last_search_line(0)
{
UCS_string u("xxx");
add_line(u);
@@ -135,7 +137,9 @@
LineHistory::LineHistory(const Nabla & nabla)
: current_line(0),
put(0),
- max_lines(1000)
+ max_lines(1000),
+ cur_search_substr( "" ),
+ last_search_line(0)
{
UCS_string u("xxx");
add_line(u);
@@ -308,6 +312,56 @@
return &hist_lines[current_line];
}
+//-----------------------------------------------------------------------------
+const void
+LineHistory::clear_search(void)
+{
+ cur_search_substr = "";
+}
+//-----------------------------------------------------------------------------
+const void
+LineHistory::update_search(UCS_string &cur_line)
+{
+ cur_search_substr = cur_line;
+}
+//-----------------------------------------------------------------------------
+const UCS_string *
+LineHistory::search(UCS_string &cur_line)
+{
+ if( hist_lines.size() == 0 ) return 0; // no history
+
+ // For now, a simple prefix search of hist_lines[]
+ int search_start_line = last_search_line - 1;
+ if( search_start_line < 0) {
+ search_start_line = hist_lines.size()-1;
+ }
+ int idx = search_start_line;
+ bool found = false;
+ do {
+ if( hist_lines[idx].substr_pos(cur_search_substr) >= 0 ) {
+ current_line = idx;
+ found = true;
+ continue;
+ }
+
+ idx--;
+ if( idx < 0 ) {
+ idx = hist_lines.size()-1;
+ }
+ if( idx == search_start_line ) {
+ break;
+ }
+ } while(!found);
+
+ if( !found ) {
+ idx = hist_lines.size()-1;
+ }
+
+ last_search_line = idx;
+ if( idx == 0 ) return 0;
+
+ return &hist_lines[current_line];
+}
//=============================================================================
LineEditContext::LineEditContext(LineInputMode mode, int rows, int cols,
LineHistory & hist, const UCS_string & prmt)
@@ -550,6 +604,13 @@
}
//-----------------------------------------------------------------------------
void
+LineEditContext::cursor_CLEAR_SEARCH()
+{
+ Log(LOG_get_line)
history.info(CERR << "cursor_CLEAR_SEARCH()") << endl;
+ history.clear_search();
+}
+//-----------------------------------------------------------------------------
+void
LineEditContext::cursor_UP()
{
Log(LOG_get_line)
history.info(CERR << "cursor_UP()") << endl;
@@ -608,6 +669,35 @@
move_idx(user_line.size());
Log(LOG_get_line)
history.info(CERR << "cursor_DOWN() done" << endl);
}
+//-----------------------------------------------------------------------------
+void
+LineEditContext::update_SEARCH(void)
+{
+ history.update_search(user_line);
+}
+//-----------------------------------------------------------------------------
+void
+LineEditContext::cursor_SEARCH()
+{
+ user_line_before_history = user_line;
+ history_entered = true;
+
+ const UCS_string * ucs = history.search(user_line_before_history);
+ if (ucs == 0) // no line above
+ {
+ Log(LOG_get_line) CERR << "hit top of history()" << endl;
+ Log(LOG_get_line)
history.info(CERR << "cursor_SEARCH() done" << endl);
+ return;
+ }
+
+ adjust_allocated_height();
+
+ uidx = 0;
+ user_line = *ucs;
+ refresh_from_cursor();
+ move_idx(user_line.size());
+ Log(LOG_get_line)
history.info(CERR << "cursor_SEARCH() done" << endl);
+}
//=============================================================================
LineInput::LineInput(bool do_read_history)
: history(uprefs.line_history_len),
@@ -863,7 +953,7 @@
user_line.clear();
-LineEditContext lec(mode, 24, Workspace::get_PW(), hist, prompt);
+ LineEditContext lec(mode, 24, Workspace::get_PW(), hist, prompt);
for (;;)
{
@@ -898,6 +988,10 @@
lec.cursor_UP();
continue;
+ case UNI_DC2: // ^R - search line history
+ lec.cursor_SEARCH();
+ continue;
+
case UNI_EOF: // end of file
eof = user_line.size() == 0;
break;
@@ -916,6 +1010,7 @@
case UNI_EOT: // ^D
lec.delete_char();
+ lec.update_SEARCH();
continue;
#else
case UNI_EOT: // ^D
@@ -926,18 +1021,22 @@
case UNI_BS: // ^H (backspace)
lec.backspc();
+ lec.update_SEARCH();
continue;
case UNI_HT: // ^I (tab)
lec.tab_expansion(mode);
+ lec.update_SEARCH();
continue;
case UNI_VT: // ^K
lec.cut_to_EOL();
+ lec.update_SEARCH();
continue;
case UNI_DELETE:
lec.delete_char();
+ lec.update_SEARCH();
continue;
case UNI_CR: // '\r' : ignore
@@ -944,10 +1043,12 @@
continue;
case UNI_LF: // '\n': done
+ lec.cursor_CLEAR_SEARCH();
break;
case UNI_EM: // ^Y
lec.paste();
+ lec.update_SEARCH();
continue;
case Invalid_Unicode:
@@ -955,6 +1056,7 @@
default: // regular APL character
lec.insert_char(uni);
+ lec.update_SEARCH();
continue;
}
@@ -1104,6 +1206,7 @@
case UNI_VT: return UNI_VT; // ^K
case UNI_SO: return UNI_CursorDown; // ^N
case UNI_DLE: return UNI_CursorUp; // ^P
+ case UNI_DC2: return UNI_DC2; // ^R
case UNI_EM: return UNI_EM; // ^Y
#ifdef WANT_CTRLD_DEL
case UNI_SUB: return UNI_SUB; // ^Z (as alt EOT, allowing ^D as delete-char)
Index: src/LineInput.hh
===================================================================
--- src/LineInput.hh (revision 1477)
+++ src/LineInput.hh (working copy)
@@ -77,6 +77,15 @@
/// move to next newer entry
const UCS_string * down();
+ /// update history search substring
+ const void clear_search(void);
+
+ /// update search substring (called when current line is edited)
+ const void update_search(UCS_string &cur_line);
+
+ /// find entries like current line in history
+ const UCS_string * search(UCS_string &cur_line);
+
/// print relevant indices
ostream & info(ostream & out) const
{ return out << " CUR=" << current_line
@@ -108,6 +117,12 @@
/// the max. history size
const int max_lines;
+ /// the current searched-for substring
+ UCS_string cur_search_substr;
+
+ /// the last searched-for line match
+ int last_search_line;
+
/// the history
UCS_string_vector hist_lines;
};
@@ -153,6 +168,10 @@
void move_idx(int new_idx)
{ uidx = new_idx; set_cursor(); }
+ /// get current cursor column
+ int get_idx(void)
+ { return uidx; }
+
/// set the cursor (writing the appropriate ESC sequence to CIN)
void set_cursor()
{ const int offs = uidx + prompt.size();
@@ -202,6 +221,9 @@
/// tab expansion
void tab_expansion(LineInputMode mode);
+ /// reset search substring
+ void cursor_CLEAR_SEARCH();
+
/// move backwards in history
void cursor_UP();
@@ -208,6 +230,11 @@
/// move forward in history
void cursor_DOWN();
+ void update_SEARCH();
+
+ /// search line history
+ void cursor_SEARCH();
+
/// return current user input
const UCS_string & get_user_line() const
{ return user_line; }
@@ -240,7 +267,7 @@
/// true if history was entered
bool history_entered;
- /// dito
+ /// ditto
UCS_string user_line_before_history;
/// a buffer for ^K/^Y