mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 00:57:36 +00:00
LibLine: Implement searching via up/down arrow keys
This commit is contained in:
parent
58912994ab
commit
42f06fc305
2 changed files with 79 additions and 44 deletions
|
@ -68,6 +68,7 @@ void Editor::clear_line()
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
m_buffer.clear();
|
m_buffer.clear();
|
||||||
m_cursor = 0;
|
m_cursor = 0;
|
||||||
|
m_inline_search_cursor = m_cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Editor::insert(const String& string)
|
void Editor::insert(const String& string)
|
||||||
|
@ -82,12 +83,14 @@ void Editor::insert(const char ch)
|
||||||
if (m_cursor == m_buffer.size()) {
|
if (m_cursor == m_buffer.size()) {
|
||||||
m_buffer.append(ch);
|
m_buffer.append(ch);
|
||||||
m_cursor = m_buffer.size();
|
m_cursor = m_buffer.size();
|
||||||
|
m_inline_search_cursor = m_cursor;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_buffer.insert(m_cursor, ch);
|
m_buffer.insert(m_cursor, ch);
|
||||||
++m_chars_inserted_in_the_middle;
|
++m_chars_inserted_in_the_middle;
|
||||||
++m_cursor;
|
++m_cursor;
|
||||||
|
m_inline_search_cursor = m_cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Editor::register_character_input_callback(char ch, Function<bool(Editor&)> callback)
|
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:
|
case InputState::ExpectFinal:
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
case 'A': // up
|
case 'A': // up
|
||||||
if (m_history_cursor > 0)
|
{
|
||||||
--m_history_cursor;
|
m_searching_backwards = true;
|
||||||
if (m_history_cursor < m_history.size()) {
|
auto inline_search_cursor = m_inline_search_cursor;
|
||||||
auto& line = m_history[m_history_cursor];
|
String search_phrase { m_buffer.data(), inline_search_cursor };
|
||||||
m_buffer.clear();
|
if (search(search_phrase, true, true)) {
|
||||||
for (auto& c : line)
|
++m_search_offset;
|
||||||
m_buffer.append(c);
|
} else {
|
||||||
m_cursor = m_buffer.size();
|
insert(search_phrase);
|
||||||
m_refresh_needed = true;
|
|
||||||
}
|
}
|
||||||
|
m_inline_search_cursor = inline_search_cursor;
|
||||||
m_state = InputState::Free;
|
m_state = InputState::Free;
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
case 'B': // down
|
case 'B': // down
|
||||||
if (m_history_cursor < m_history.size())
|
{
|
||||||
++m_history_cursor;
|
auto inline_search_cursor = m_inline_search_cursor;
|
||||||
if (m_history_cursor < m_history.size()) {
|
String search_phrase { m_buffer.data(), inline_search_cursor };
|
||||||
auto& line = m_history[m_history_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();
|
m_buffer.clear();
|
||||||
for (auto& c : line)
|
insert(search_phrase);
|
||||||
m_buffer.append(c);
|
|
||||||
m_cursor = m_buffer.size();
|
|
||||||
m_refresh_needed = true;
|
m_refresh_needed = true;
|
||||||
}
|
}
|
||||||
|
m_inline_search_cursor = inline_search_cursor;
|
||||||
m_state = InputState::Free;
|
m_state = InputState::Free;
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
case 'D': // left
|
case 'D': // left
|
||||||
if (m_cursor > 0) {
|
if (m_cursor > 0) {
|
||||||
--m_cursor;
|
--m_cursor;
|
||||||
}
|
}
|
||||||
|
m_inline_search_cursor = m_cursor;
|
||||||
m_state = InputState::Free;
|
m_state = InputState::Free;
|
||||||
continue;
|
continue;
|
||||||
case 'C': // right
|
case 'C': // right
|
||||||
if (m_cursor < m_buffer.size()) {
|
if (m_cursor < m_buffer.size()) {
|
||||||
++m_cursor;
|
++m_cursor;
|
||||||
}
|
}
|
||||||
|
m_inline_search_cursor = m_cursor;
|
||||||
|
m_search_offset = 0;
|
||||||
m_state = InputState::Free;
|
m_state = InputState::Free;
|
||||||
continue;
|
continue;
|
||||||
case 'H':
|
case 'H':
|
||||||
m_cursor = 0;
|
m_cursor = 0;
|
||||||
|
m_inline_search_cursor = m_cursor;
|
||||||
|
m_search_offset = 0;
|
||||||
m_state = InputState::Free;
|
m_state = InputState::Free;
|
||||||
continue;
|
continue;
|
||||||
case 'F':
|
case 'F':
|
||||||
m_cursor = m_buffer.size();
|
m_cursor = m_buffer.size();
|
||||||
m_state = InputState::Free;
|
m_state = InputState::Free;
|
||||||
|
m_inline_search_cursor = m_cursor;
|
||||||
|
m_search_offset = 0;
|
||||||
continue;
|
continue;
|
||||||
case 'Z': // shift+tab
|
case 'Z': // shift+tab
|
||||||
reverse_tab = true;
|
reverse_tab = true;
|
||||||
|
@ -252,6 +272,7 @@ String Editor::get_line(const String& prompt)
|
||||||
}
|
}
|
||||||
m_buffer.remove(m_cursor);
|
m_buffer.remove(m_cursor);
|
||||||
m_refresh_needed = true;
|
m_refresh_needed = true;
|
||||||
|
m_search_offset = 0;
|
||||||
m_state = InputState::ExpectTerminator;
|
m_state = InputState::ExpectTerminator;
|
||||||
continue;
|
continue;
|
||||||
default:
|
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 (ch == '\t' || reverse_tab) {
|
||||||
if (!on_tab_complete_first_token || !on_tab_complete_other_token)
|
if (!on_tab_complete_first_token || !on_tab_complete_other_token)
|
||||||
continue;
|
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)
|
for (size_t i = m_next_suggestion_invariant_offset; i < shown_length; ++i)
|
||||||
m_buffer.remove(actual_offset);
|
m_buffer.remove(actual_offset);
|
||||||
m_cursor = actual_offset;
|
m_cursor = actual_offset;
|
||||||
|
m_inline_search_cursor = m_cursor;
|
||||||
m_refresh_needed = true;
|
m_refresh_needed = true;
|
||||||
}
|
}
|
||||||
m_last_shown_suggestion = m_suggestions[m_next_suggestion_index];
|
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_buffer.remove(m_cursor - 1);
|
||||||
--m_cursor;
|
--m_cursor;
|
||||||
|
m_inline_search_cursor = m_cursor;
|
||||||
// we will have to redraw :(
|
// we will have to redraw :(
|
||||||
m_refresh_needed = true;
|
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 = make<Editor>(true); // Has anyone seen 'Inception'?
|
||||||
m_search_editor->initialize();
|
m_search_editor->initialize();
|
||||||
m_search_editor->on_display_refresh = [this](Editor& search_editor) {
|
m_search_editor->on_display_refresh = [this](Editor& search_editor) {
|
||||||
int last_matching_offset = -1;
|
search(StringView { search_editor.buffer().data(), search_editor.buffer().size() });
|
||||||
|
|
||||||
// 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;
|
|
||||||
refresh_display();
|
refresh_display();
|
||||||
return;
|
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()
|
void Editor::recalculate_origin()
|
||||||
{
|
{
|
||||||
// changing the columns can affect our origin if
|
// changing the columns can affect our origin if
|
||||||
|
|
|
@ -160,6 +160,7 @@ private:
|
||||||
|
|
||||||
Style find_applicable_style(size_t offset) const;
|
Style find_applicable_style(size_t offset) const;
|
||||||
|
|
||||||
|
bool search(const StringView&, bool allow_empty = false, bool from_beginning = false);
|
||||||
inline void end_search()
|
inline void end_search()
|
||||||
{
|
{
|
||||||
m_is_searching = false;
|
m_is_searching = false;
|
||||||
|
@ -222,6 +223,7 @@ private:
|
||||||
bool m_is_searching { false };
|
bool m_is_searching { false };
|
||||||
bool m_reset_buffer_on_search_end { true };
|
bool m_reset_buffer_on_search_end { true };
|
||||||
size_t m_search_offset { 0 };
|
size_t m_search_offset { 0 };
|
||||||
|
bool m_searching_backwards { true };
|
||||||
size_t m_pre_search_cursor { 0 };
|
size_t m_pre_search_cursor { 0 };
|
||||||
Vector<char, 1024> m_pre_search_buffer;
|
Vector<char, 1024> m_pre_search_buffer;
|
||||||
|
|
||||||
|
@ -229,6 +231,7 @@ private:
|
||||||
ByteBuffer m_pending_chars;
|
ByteBuffer m_pending_chars;
|
||||||
size_t m_cursor { 0 };
|
size_t m_cursor { 0 };
|
||||||
size_t m_drawn_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_chars_inserted_in_the_middle { 0 };
|
||||||
size_t m_times_tab_pressed { 0 };
|
size_t m_times_tab_pressed { 0 };
|
||||||
size_t m_num_columns { 0 };
|
size_t m_num_columns { 0 };
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue