mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 02:27:43 +00:00
Shell: Improve tab completion behaviour
A space is added if only one match is found, but we avoid adding redundant spaces. We complete "empty" tokens, i.e. when the cursor is at the start of the line or in front of a space. For example: mkdir test cd test touch test chmod +x test export PATH=/home/anon/test Now if you press tab, or space and then tab, you will get "test". Notice that you also get a space. Completion is now done relative to the cursor. You can enter two words and then go back and complete the first one.
This commit is contained in:
parent
244e525c73
commit
4ae8d929b4
2 changed files with 28 additions and 14 deletions
|
@ -106,10 +106,8 @@ void LineEditor::cut_mismatching_chars(String& completion, const String& program
|
||||||
completion = completion.substring(0, i);
|
completion = completion.substring(0, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LineEditor::tab_complete_first_token()
|
void LineEditor::tab_complete_first_token(const String& token)
|
||||||
{
|
{
|
||||||
String token = String::copy(m_buffer);
|
|
||||||
|
|
||||||
auto match = binary_search(m_path.data(), m_path.size(), token, [](const String& token, const String& program) -> int {
|
auto match = binary_search(m_path.data(), m_path.size(), token, [](const String& token, const String& program) -> int {
|
||||||
return strncmp(token.characters(), program.characters(), token.length());
|
return strncmp(token.characters(), program.characters(), token.length());
|
||||||
});
|
});
|
||||||
|
@ -122,15 +120,23 @@ void LineEditor::tab_complete_first_token()
|
||||||
// other program names starting with our token and cut off any mismatching
|
// other program names starting with our token and cut off any mismatching
|
||||||
// characters.
|
// characters.
|
||||||
|
|
||||||
|
bool seen_others = false;
|
||||||
int index = match - m_path.data();
|
int index = match - m_path.data();
|
||||||
for (int i = index - 1; i >= 0 && m_path[i].starts_with(token); --i)
|
for (int i = index - 1; i >= 0 && m_path[i].starts_with(token); --i) {
|
||||||
cut_mismatching_chars(completion, m_path[i], token.length());
|
cut_mismatching_chars(completion, m_path[i], token.length());
|
||||||
for (int i = index + 1; i < m_path.size() && m_path[i].starts_with(token); ++i)
|
seen_others = true;
|
||||||
|
}
|
||||||
|
for (int i = index + 1; i < m_path.size() && m_path[i].starts_with(token); ++i) {
|
||||||
cut_mismatching_chars(completion, m_path[i], token.length());
|
cut_mismatching_chars(completion, m_path[i], token.length());
|
||||||
|
seen_others = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (token.length() == completion.length())
|
// If we have characters to add, add them.
|
||||||
return;
|
if (completion.length() > token.length())
|
||||||
insert(completion.substring(token.length(), completion.length() - token.length()));
|
insert(completion.substring(token.length(), completion.length() - token.length()));
|
||||||
|
// If we have a single match, we add a space, unless we already have one.
|
||||||
|
if (!seen_others && (m_cursor == (size_t)m_buffer.size() || m_buffer[(int)m_cursor] != ' '))
|
||||||
|
insert(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
String LineEditor::get_line(const String& prompt)
|
String LineEditor::get_line(const String& prompt)
|
||||||
|
@ -270,20 +276,28 @@ String LineEditor::get_line(const String& prompt)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ch == '\t') {
|
if (ch == '\t') {
|
||||||
if (m_buffer.is_empty())
|
bool is_empty_token = m_cursor == 0 || m_buffer[(int)m_cursor - 1] == ' ';
|
||||||
continue;
|
|
||||||
|
int token_start = (int)m_cursor - 1;
|
||||||
|
if (!is_empty_token) {
|
||||||
|
while (token_start >= 0 && m_buffer[token_start] != ' ')
|
||||||
|
--token_start;
|
||||||
|
++token_start;
|
||||||
|
}
|
||||||
|
|
||||||
bool is_first_token = true;
|
bool is_first_token = true;
|
||||||
for (const auto& character : m_buffer) {
|
for (int i = token_start - 1; i >= 0; --i) {
|
||||||
if (isspace(character)) {
|
if (m_buffer[i] != ' ') {
|
||||||
is_first_token = false;
|
is_first_token = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String token = is_empty_token ? String() : String(&m_buffer[token_start], m_cursor - (size_t)token_start);
|
||||||
|
|
||||||
// FIXME: Implement tab-completion for other tokens (paths).
|
// FIXME: Implement tab-completion for other tokens (paths).
|
||||||
if (is_first_token)
|
if (is_first_token)
|
||||||
tab_complete_first_token();
|
tab_complete_first_token(token);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ private:
|
||||||
void insert(const String&);
|
void insert(const String&);
|
||||||
void insert(const char);
|
void insert(const char);
|
||||||
void cut_mismatching_chars(String& completion, const String& program, size_t token_length);
|
void cut_mismatching_chars(String& completion, const String& program, size_t token_length);
|
||||||
void tab_complete_first_token();
|
void tab_complete_first_token(const String&);
|
||||||
void vt_save_cursor();
|
void vt_save_cursor();
|
||||||
void vt_restore_cursor();
|
void vt_restore_cursor();
|
||||||
void vt_clear_to_end_of_line();
|
void vt_clear_to_end_of_line();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue