Shell: Fix completing barewords with escapes

e.g. completing `foo\ bar` now works as expected.
This commit is contained in:
AnotherTest 2021-01-09 03:32:19 +03:30 committed by Andreas Kling
parent 7059ca9b15
commit 9523bcbfe1
Notes: sideshowbarker 2024-07-18 23:58:27 +09:00
3 changed files with 43 additions and 21 deletions

View file

@ -232,8 +232,8 @@ Vector<Line::CompletionSuggestion> Node::complete_for_editor(Shell& shell, size_
auto matching_node = hit_test_result.matching_node;
if (matching_node) {
if (matching_node->is_bareword()) {
auto corrected_offset = offset - matching_node->position().start_offset;
auto* node = static_cast<BarewordLiteral*>(matching_node.ptr());
auto corrected_offset = find_offset_into_node(node->text(), offset - matching_node->position().start_offset);
if (corrected_offset > node->text().length())
return {};

View file

@ -1114,30 +1114,35 @@ String Shell::escape_token_for_single_quotes(const String& token)
return builder.build();
}
bool Shell::is_special(char c)
{
switch (c) {
case '\'':
case '"':
case '$':
case '|':
case '>':
case '<':
case '(':
case ')':
case '{':
case '}':
case '&':
case '\\':
case ' ':
return true;
default:
return false;
}
}
String Shell::escape_token(const String& token)
{
StringBuilder builder;
for (auto c : token) {
switch (c) {
case '\'':
case '"':
case '$':
case '|':
case '>':
case '<':
case '(':
case ')':
case '{':
case '}':
case '&':
case '\\':
case ' ':
if (is_special(c))
builder.append('\\');
break;
default:
break;
}
builder.append(c);
}
@ -1256,7 +1261,6 @@ Vector<Line::CompletionSuggestion> Shell::complete()
Vector<Line::CompletionSuggestion> Shell::complete_path(const String& base, const String& part, size_t offset)
{
auto token = offset ? part.substring_view(0, offset) : "";
StringView original_token = token;
String path;
ssize_t last_slash = token.length() - 1;
@ -1294,7 +1298,7 @@ Vector<Line::CompletionSuggestion> Shell::complete_path(const String& base, cons
// `/foo/', but rather just `bar...'
auto token_length = escape_token(token).length();
if (m_editor)
m_editor->suggest(token_length, original_token.length() - token_length);
m_editor->suggest(token_length, last_slash + 1);
// only suggest dot-files if path starts with a dot
Core::DirIterator files(path,

View file

@ -156,6 +156,7 @@ public:
static String escape_token_for_single_quotes(const String& token);
static String escape_token(const String& token);
static String unescape_token(const String& token);
static bool is_special(char c);
static bool is_glob(const StringView&);
static Vector<StringView> split_path(const StringView&);
@ -330,4 +331,21 @@ static constexpr bool is_word_character(char c)
return c == '_' || (c <= 'Z' && c >= 'A') || (c <= 'z' && c >= 'a');
}
inline size_t find_offset_into_node(const String& unescaped_text, size_t escaped_offset)
{
size_t unescaped_offset = 0;
size_t offset = 0;
for (auto& c : unescaped_text) {
if (offset == escaped_offset)
return unescaped_offset;
if (Shell::is_special(c))
++offset;
++offset;
++unescaped_offset;
}
return unescaped_offset;
}
}