From 9523bcbfe1da5ce6670df28a6d372beb1868daa0 Mon Sep 17 00:00:00 2001 From: AnotherTest Date: Sat, 9 Jan 2021 03:32:19 +0330 Subject: [PATCH] Shell: Fix completing barewords with escapes e.g. completing `foo\ bar` now works as expected. --- Shell/AST.cpp | 2 +- Shell/Shell.cpp | 44 ++++++++++++++++++++++++-------------------- Shell/Shell.h | 18 ++++++++++++++++++ 3 files changed, 43 insertions(+), 21 deletions(-) diff --git a/Shell/AST.cpp b/Shell/AST.cpp index 70d3f3220b..11b7ba7ed8 100644 --- a/Shell/AST.cpp +++ b/Shell/AST.cpp @@ -232,8 +232,8 @@ Vector 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(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 {}; diff --git a/Shell/Shell.cpp b/Shell/Shell.cpp index 0bc877ce58..e9c9a794f0 100644 --- a/Shell/Shell.cpp +++ b/Shell/Shell.cpp @@ -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 Shell::complete() Vector 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 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, diff --git a/Shell/Shell.h b/Shell/Shell.h index e88e01cc60..7a357ac870 100644 --- a/Shell/Shell.h +++ b/Shell/Shell.h @@ -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 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; +} + }