summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnotherTest <ali.mpfard@gmail.com>2020-04-20 17:20:31 +0430
committerAndreas Kling <kling@serenityos.org>2020-04-20 17:27:30 +0200
commit42f06fc305e093bd31915984b50b96242ca11157 (patch)
treec5f3f46cb912220b18fb05a81669bfb82994900a
parent58912994ab47848d9883e30c0c0d4ece611e8637 (diff)
downloadserenity-42f06fc305e093bd31915984b50b96242ca11157.zip
LibLine: Implement searching via up/down arrow keys
-rw-r--r--Libraries/LibLine/Editor.cpp120
-rw-r--r--Libraries/LibLine/Editor.h3
2 files changed, 79 insertions, 44 deletions
diff --git a/Libraries/LibLine/Editor.cpp b/Libraries/LibLine/Editor.cpp
index fe97776cd0..9f258a47bf 100644
--- a/Libraries/LibLine/Editor.cpp
+++ b/Libraries/LibLine/Editor.cpp
@@ -68,6 +68,7 @@ void Editor::clear_line()
fflush(stdout);
m_buffer.clear();
m_cursor = 0;
+ m_inline_search_cursor = m_cursor;
}
void Editor::insert(const String& string)
@@ -82,12 +83,14 @@ void Editor::insert(const char ch)
if (m_cursor == m_buffer.size()) {
m_buffer.append(ch);
m_cursor = m_buffer.size();
+ m_inline_search_cursor = m_cursor;
return;
}
m_buffer.insert(m_cursor, ch);
++m_chars_inserted_in_the_middle;
++m_cursor;
+ m_inline_search_cursor = m_cursor;
}
void Editor::register_character_input_callback(char ch, Function<bool(Editor&)> callback)
@@ -195,50 +198,67 @@ String Editor::get_line(const String& prompt)
case InputState::ExpectFinal:
switch (ch) {
case 'A': // up
- if (m_history_cursor > 0)
- --m_history_cursor;
- if (m_history_cursor < m_history.size()) {
- auto& line = m_history[m_history_cursor];
- m_buffer.clear();
- for (auto& c : line)
- m_buffer.append(c);
- m_cursor = m_buffer.size();
- m_refresh_needed = true;
+ {
+ m_searching_backwards = true;
+ auto inline_search_cursor = m_inline_search_cursor;
+ String search_phrase { m_buffer.data(), inline_search_cursor };
+ if (search(search_phrase, true, true)) {
+ ++m_search_offset;
+ } else {
+ insert(search_phrase);
}
+ m_inline_search_cursor = inline_search_cursor;
m_state = InputState::Free;
continue;
+ }
case 'B': // down
- if (m_history_cursor < m_history.size())
- ++m_history_cursor;
- if (m_history_cursor < m_history.size()) {
- auto& line = m_history[m_history_cursor];
+ {
+ auto inline_search_cursor = m_inline_search_cursor;
+ String search_phrase { m_buffer.data(), inline_search_cursor };
+ auto search_changed_directions = m_searching_backwards;
+ m_searching_backwards = false;
+ if (m_search_offset > 0) {
+ m_search_offset -= 1 + search_changed_directions;
+ if (!search(search_phrase, true, true)) {
+ insert(search_phrase);
+ }
+ } else {
+ m_search_offset = 0;
+ m_cursor = 0;
m_buffer.clear();
- for (auto& c : line)
- m_buffer.append(c);
- m_cursor = m_buffer.size();
+ insert(search_phrase);
m_refresh_needed = true;
}
+ m_inline_search_cursor = inline_search_cursor;
m_state = InputState::Free;
continue;
+ }
case 'D': // left
if (m_cursor > 0) {
--m_cursor;
}
+ m_inline_search_cursor = m_cursor;
m_state = InputState::Free;
continue;
case 'C': // right
if (m_cursor < m_buffer.size()) {
++m_cursor;
}
+ m_inline_search_cursor = m_cursor;
+ m_search_offset = 0;
m_state = InputState::Free;
continue;
case 'H':
m_cursor = 0;
+ m_inline_search_cursor = m_cursor;
+ m_search_offset = 0;
m_state = InputState::Free;
continue;
case 'F':
m_cursor = m_buffer.size();
m_state = InputState::Free;
+ m_inline_search_cursor = m_cursor;
+ m_search_offset = 0;
continue;
case 'Z': // shift+tab
reverse_tab = true;
@@ -252,6 +272,7 @@ String Editor::get_line(const String& prompt)
}
m_buffer.remove(m_cursor);
m_refresh_needed = true;
+ m_search_offset = 0;
m_state = InputState::ExpectTerminator;
continue;
default:
@@ -278,6 +299,8 @@ String Editor::get_line(const String& prompt)
}
}
+ m_search_offset = 0; // reset search offset on any key
+
if (ch == '\t' || reverse_tab) {
if (!on_tab_complete_first_token || !on_tab_complete_other_token)
continue;
@@ -379,6 +402,7 @@ String Editor::get_line(const String& prompt)
for (size_t i = m_next_suggestion_invariant_offset; i < shown_length; ++i)
m_buffer.remove(actual_offset);
m_cursor = actual_offset;
+ m_inline_search_cursor = m_cursor;
m_refresh_needed = true;
}
m_last_shown_suggestion = m_suggestions[m_next_suggestion_index];
@@ -525,6 +549,7 @@ String Editor::get_line(const String& prompt)
}
m_buffer.remove(m_cursor - 1);
--m_cursor;
+ m_inline_search_cursor = m_cursor;
// we will have to redraw :(
m_refresh_needed = true;
};
@@ -580,34 +605,7 @@ String Editor::get_line(const String& prompt)
m_search_editor = make<Editor>(true); // Has anyone seen 'Inception'?
m_search_editor->initialize();
m_search_editor->on_display_refresh = [this](Editor& search_editor) {
- int last_matching_offset = -1;
-
- // do not search for empty strings
- if (search_editor.buffer().size() > 0) {
- size_t search_offset = m_search_offset;
- StringView search_term { search_editor.buffer().data(), search_editor.buffer().size() };
- for (size_t i = m_history_cursor; i > 0; --i) {
- if (m_history[i - 1].contains(search_term)) {
- last_matching_offset = i - 1;
- if (search_offset == 0)
- break;
- --search_offset;
- }
- }
-
- if (last_matching_offset == -1) {
- fputc('\a', stdout);
- fflush(stdout);
- return;
- }
- }
-
- m_buffer.clear();
- m_cursor = 0;
- if (last_matching_offset >= 0)
- insert(m_history[last_matching_offset]);
- // always needed
- m_refresh_needed = true;
+ search(StringView { search_editor.buffer().data(), search_editor.buffer().size() });
refresh_display();
return;
};
@@ -691,6 +689,40 @@ String Editor::get_line(const String& prompt)
}
}
+bool Editor::search(const StringView& phrase, bool allow_empty, bool from_beginning)
+{
+
+ int last_matching_offset = -1;
+
+ // do not search for empty strings
+ if (allow_empty || phrase.length() > 0) {
+ size_t search_offset = m_search_offset;
+ for (size_t i = m_history_cursor; i > 0; --i) {
+ auto contains = from_beginning ? m_history[i - 1].starts_with(phrase) : m_history[i - 1].contains(phrase);
+ if (contains) {
+ last_matching_offset = i - 1;
+ if (search_offset == 0)
+ break;
+ --search_offset;
+ }
+ }
+
+ if (last_matching_offset == -1) {
+ fputc('\a', stdout);
+ fflush(stdout);
+ }
+ }
+
+ m_buffer.clear();
+ m_cursor = 0;
+ if (last_matching_offset >= 0) {
+ insert(m_history[last_matching_offset]);
+ }
+ // always needed
+ m_refresh_needed = true;
+ return last_matching_offset >= 0;
+}
+
void Editor::recalculate_origin()
{
// changing the columns can affect our origin if
diff --git a/Libraries/LibLine/Editor.h b/Libraries/LibLine/Editor.h
index aa735c531b..4909ea458f 100644
--- a/Libraries/LibLine/Editor.h
+++ b/Libraries/LibLine/Editor.h
@@ -160,6 +160,7 @@ private:
Style find_applicable_style(size_t offset) const;
+ bool search(const StringView&, bool allow_empty = false, bool from_beginning = false);
inline void end_search()
{
m_is_searching = false;
@@ -222,6 +223,7 @@ private:
bool m_is_searching { false };
bool m_reset_buffer_on_search_end { true };
size_t m_search_offset { 0 };
+ bool m_searching_backwards { true };
size_t m_pre_search_cursor { 0 };
Vector<char, 1024> m_pre_search_buffer;
@@ -229,6 +231,7 @@ private:
ByteBuffer m_pending_chars;
size_t m_cursor { 0 };
size_t m_drawn_cursor { 0 };
+ size_t m_inline_search_cursor { 0 };
size_t m_chars_inserted_in_the_middle { 0 };
size_t m_times_tab_pressed { 0 };
size_t m_num_columns { 0 };