LibLine: Implement searching via up/down arrow keys

This commit is contained in:
AnotherTest 2020-04-20 17:20:31 +04:30 committed by Andreas Kling
parent 58912994ab
commit 42f06fc305
Notes: sideshowbarker 2024-07-19 07:26:30 +09:00
2 changed files with 79 additions and 44 deletions

View file

@ -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

View file

@ -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 };