mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 15:40:19 +00:00
Shell: Tab completion for paths
If the cursor is in front of a token that is not the first token, we try to split it on the last slash. If there is a slash, the first part is the directory to search and the second part is the token to complete. If there is no slash, we search the current directory and use the entire token for completion. If we find a single match and it's a directory, we add a slash. If it's a normal file, we add a space, unless there already is one. Also renamed cut_mismatching_chars() parameters to be more appropriate.
This commit is contained in:
parent
4ae8d929b4
commit
61f2704d58
Notes:
sideshowbarker
2024-07-19 10:53:06 +09:00
Author: https://github.com/willmcpherson2 Commit: https://github.com/SerenityOS/serenity/commit/61f2704d580 Pull-request: https://github.com/SerenityOS/serenity/pull/869
2 changed files with 68 additions and 5 deletions
|
@ -98,10 +98,10 @@ void LineEditor::cache_path()
|
|||
quick_sort(m_path.begin(), m_path.end(), AK::is_less_than<String>);
|
||||
}
|
||||
|
||||
void LineEditor::cut_mismatching_chars(String& completion, const String& program, size_t token_length)
|
||||
void LineEditor::cut_mismatching_chars(String& completion, const String& other, size_t start_compare)
|
||||
{
|
||||
size_t i = token_length;
|
||||
while (i < completion.length() && i < program.length() && completion[i] == program[i])
|
||||
size_t i = start_compare;
|
||||
while (i < completion.length() && i < other.length() && completion[i] == other[i])
|
||||
++i;
|
||||
completion = completion.substring(0, i);
|
||||
}
|
||||
|
@ -139,6 +139,66 @@ void LineEditor::tab_complete_first_token(const String& token)
|
|||
insert(' ');
|
||||
}
|
||||
|
||||
void LineEditor::tab_complete_other_token(String& token)
|
||||
{
|
||||
String path;
|
||||
|
||||
int last_slash = (int)token.length() - 1;
|
||||
while (last_slash >= 0 && token[last_slash] != '/')
|
||||
--last_slash;
|
||||
|
||||
if (last_slash >= 0) {
|
||||
// Split on the last slash. We'll use the first part as the directory
|
||||
// to search and the second part as the token to complete.
|
||||
path = token.substring(0, (size_t)last_slash + 1);
|
||||
if (path[0] != '/')
|
||||
path = String::format("%s/%s", g.cwd.characters(), path.characters());
|
||||
path = canonicalized_path(path);
|
||||
token = token.substring((size_t)last_slash + 1, token.length() - (size_t)last_slash - 1);
|
||||
} else {
|
||||
// We have no slashes, so the directory to search is the current
|
||||
// directory and the token to complete is just the original token.
|
||||
path = g.cwd;
|
||||
}
|
||||
|
||||
String completion;
|
||||
|
||||
bool seen_others = false;
|
||||
CDirIterator files(path, CDirIterator::SkipDots);
|
||||
while (files.has_next()) {
|
||||
auto file = files.next_path();
|
||||
if (file.starts_with(token)) {
|
||||
if (completion.is_empty()) {
|
||||
completion = file; // Will only be set once.
|
||||
} else {
|
||||
cut_mismatching_chars(completion, file, token.length());
|
||||
if (completion.is_empty()) // We cut everything off!
|
||||
return;
|
||||
seen_others = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (completion.is_empty())
|
||||
return;
|
||||
|
||||
// If we have characters to add, add them.
|
||||
if (completion.length() > token.length())
|
||||
insert(completion.substring(token.length(), completion.length() - token.length()));
|
||||
// If we have a single match and it's a directory, we add a slash. If it's
|
||||
// a regular file, we add a space, unless we already have one.
|
||||
if (!seen_others) {
|
||||
String file_path = String::format("%s/%s", path.characters(), completion.characters());
|
||||
struct stat program_status;
|
||||
int stat_error = stat(file_path.characters(), &program_status);
|
||||
if (!stat_error) {
|
||||
if (S_ISDIR(program_status.st_mode))
|
||||
insert('/');
|
||||
else if (m_cursor == (size_t)m_buffer.size() || m_buffer[(int)m_cursor] != ' ')
|
||||
insert(' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String LineEditor::get_line(const String& prompt)
|
||||
{
|
||||
fputs(prompt.characters(), stdout);
|
||||
|
@ -295,9 +355,10 @@ String LineEditor::get_line(const String& prompt)
|
|||
|
||||
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).
|
||||
if (is_first_token)
|
||||
tab_complete_first_token(token);
|
||||
else
|
||||
tab_complete_other_token(token);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/BinarySearch.h>
|
||||
#include <AK/FileSystemPath.h>
|
||||
#include <AK/QuickSort.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Vector.h>
|
||||
|
@ -23,8 +24,9 @@ private:
|
|||
void clear_line();
|
||||
void insert(const String&);
|
||||
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& other, size_t start_compare);
|
||||
void tab_complete_first_token(const String&);
|
||||
void tab_complete_other_token(String&);
|
||||
void vt_save_cursor();
|
||||
void vt_restore_cursor();
|
||||
void vt_clear_to_end_of_line();
|
||||
|
|
Loading…
Reference in a new issue