diff --git a/src/lex_utils.cc b/src/lex_utils.cc index 9f49790e..591d6904 100644 --- a/src/lex_utils.cc +++ b/src/lex_utils.cc @@ -3,6 +3,7 @@ #include #include +#include // VSCode (UTF-16) disagrees with Emacs lsp-mode (UTF-8) on how to represent // text documents. @@ -197,35 +198,31 @@ void LexFunctionDeclaration(const std::string& buffer_content, *insert_text = result; } -std::string LexWordAroundPos(lsPosition position, const std::string& content) { - int index = GetOffsetForPosition(position, content); +std::string_view LexIdentifierAroundPos(lsPosition position, + std::string_view content) { + int start = GetOffsetForPosition(position, content); + int end = start + 1; + char c; - int start = index; - int end = index + 1; - - // We search for : before the cursor but not after to get the qualifier. - while (start > 0) { - char c = content[start - 1]; - if (isalnum(c) || c == '_' || c == ':') { - --start; - } else { + // We search for :: before the cursor but not after to get the qualifier. + for (; start > 0; start--) { + c = content[start - 1]; + if (isalnum(c) || c == '_') + ; + else if (c == ':' && start > 1 && content[start - 2] == ':') + start--; + else break; - } } - while (end < (int)content.size()) { - char c = content[end]; - if (isalnum(c) || c == '_') { - ++end; - } else { + for (; end < (int)content.size(); end++) + if (c = content[end], !(isalnum(c) || c == '_')) break; - } - } return content.substr(start, end - start); } -bool SubsequenceMatch(std::string_view search, std::string_view content) { +bool SubsequenceMatchIgnoreCase(std::string_view search, std::string_view content) { size_t j = 0; for (size_t i = 0; i < search.size(); i++) { char search_char = tolower(search[i]); @@ -280,33 +277,29 @@ TEST_SUITE("Offset") { TEST_SUITE("Substring") { TEST_CASE("match") { - // Sanity. - REQUIRE(SubsequenceMatch("a", "aa")); - REQUIRE(SubsequenceMatch("aa", "aa")); - // Empty string matches anything. - REQUIRE(SubsequenceMatch("", "")); - REQUIRE(SubsequenceMatch("", "aa")); + REQUIRE(SubsequenceMatchIgnoreCase("", "")); + REQUIRE(SubsequenceMatchIgnoreCase("", "aa")); // Match in start/middle/end. - REQUIRE(SubsequenceMatch("a", "abbbb")); - REQUIRE(SubsequenceMatch("a", "bbabb")); - REQUIRE(SubsequenceMatch("a", "bbbba")); - REQUIRE(SubsequenceMatch("aa", "aabbb")); - REQUIRE(SubsequenceMatch("aa", "bbaab")); - REQUIRE(SubsequenceMatch("aa", "bbbaa")); + REQUIRE(SubsequenceMatchIgnoreCase("a", "abbbb")); + REQUIRE(SubsequenceMatchIgnoreCase("a", "bbabb")); + REQUIRE(SubsequenceMatchIgnoreCase("a", "bbbba")); + REQUIRE(SubsequenceMatchIgnoreCase("aa", "aabbb")); + REQUIRE(SubsequenceMatchIgnoreCase("aa", "bbaab")); + REQUIRE(SubsequenceMatchIgnoreCase("aa", "bbbaa")); // Capitalization. - REQUIRE(SubsequenceMatch("aa", "aA")); - REQUIRE(SubsequenceMatch("aa", "Aa")); - REQUIRE(SubsequenceMatch("aa", "AA")); + REQUIRE(SubsequenceMatchIgnoreCase("aa", "aA")); + REQUIRE(SubsequenceMatchIgnoreCase("aa", "Aa")); + REQUIRE(SubsequenceMatchIgnoreCase("aa", "AA")); // Token skipping. - REQUIRE(SubsequenceMatch("ad", "abcd")); - REQUIRE(SubsequenceMatch("ad", "ABCD")); + REQUIRE(SubsequenceMatchIgnoreCase("ad", "abcd")); + REQUIRE(SubsequenceMatchIgnoreCase("ad", "ABCD")); // Ordering. - REQUIRE(!SubsequenceMatch("ad", "dcba")); + REQUIRE(!SubsequenceMatchIgnoreCase("ad", "dcba")); } TEST_CASE("skip") { @@ -420,34 +413,33 @@ TEST_SUITE("LexFunctionDeclaration") { TEST_SUITE("LexWordAroundPos") { TEST_CASE("edges") { std::string content = "Foobar"; - REQUIRE(LexWordAroundPos(CharPos(content, 'F'), content) == "Foobar"); - REQUIRE(LexWordAroundPos(CharPos(content, 'o'), content) == "Foobar"); - REQUIRE(LexWordAroundPos(CharPos(content, 'b'), content) == "Foobar"); - REQUIRE(LexWordAroundPos(CharPos(content, 'a'), content) == "Foobar"); - REQUIRE(LexWordAroundPos(CharPos(content, 'r'), content) == "Foobar"); + REQUIRE(LexIdentifierAroundPos(CharPos(content, 'F'), content) == "Foobar"); + REQUIRE(LexIdentifierAroundPos(CharPos(content, 'o'), content) == "Foobar"); + REQUIRE(LexIdentifierAroundPos(CharPos(content, 'b'), content) == "Foobar"); + REQUIRE(LexIdentifierAroundPos(CharPos(content, 'a'), content) == "Foobar"); + REQUIRE(LexIdentifierAroundPos(CharPos(content, 'r'), content) == "Foobar"); } TEST_CASE("simple") { std::string content = " Foobar "; - REQUIRE(LexWordAroundPos(CharPos(content, 'F'), content) == "Foobar"); - REQUIRE(LexWordAroundPos(CharPos(content, 'o'), content) == "Foobar"); - REQUIRE(LexWordAroundPos(CharPos(content, 'b'), content) == "Foobar"); - REQUIRE(LexWordAroundPos(CharPos(content, 'a'), content) == "Foobar"); - REQUIRE(LexWordAroundPos(CharPos(content, 'r'), content) == "Foobar"); + REQUIRE(LexIdentifierAroundPos(CharPos(content, 'F'), content) == "Foobar"); + REQUIRE(LexIdentifierAroundPos(CharPos(content, 'o'), content) == "Foobar"); + REQUIRE(LexIdentifierAroundPos(CharPos(content, 'b'), content) == "Foobar"); + REQUIRE(LexIdentifierAroundPos(CharPos(content, 'a'), content) == "Foobar"); + REQUIRE(LexIdentifierAroundPos(CharPos(content, 'r'), content) == "Foobar"); } - TEST_CASE("underscores and numbers") { - std::string content = " _my_t5ype7 "; - REQUIRE(LexWordAroundPos(CharPos(content, '_'), content) == "_my_t5ype7"); - REQUIRE(LexWordAroundPos(CharPos(content, '5'), content) == "_my_t5ype7"); - REQUIRE(LexWordAroundPos(CharPos(content, 'e'), content) == "_my_t5ype7"); - REQUIRE(LexWordAroundPos(CharPos(content, '7'), content) == "_my_t5ype7"); + TEST_CASE("underscores, numbers and ::") { + std::string content = " file:ns::_my_t5ype7 "; + REQUIRE(LexIdentifierAroundPos(CharPos(content, 'f'), content) == "file"); + REQUIRE(LexIdentifierAroundPos(CharPos(content, 's'), content) == "ns"); + REQUIRE(LexIdentifierAroundPos(CharPos(content, 'y'), content) == "ns::_my_t5ype7"); } TEST_CASE("dot, dash, colon are skipped") { std::string content = "1. 2- 3:"; - REQUIRE(LexWordAroundPos(CharPos(content, '1'), content) == "1"); - REQUIRE(LexWordAroundPos(CharPos(content, '2'), content) == "2"); - REQUIRE(LexWordAroundPos(CharPos(content, '3'), content) == "3"); + REQUIRE(LexIdentifierAroundPos(CharPos(content, '1'), content) == "1"); + REQUIRE(LexIdentifierAroundPos(CharPos(content, '2'), content) == "2"); + REQUIRE(LexIdentifierAroundPos(CharPos(content, '3'), content) == "3"); } } diff --git a/src/lex_utils.h b/src/lex_utils.h index 6d341781..ac5ccc20 100644 --- a/src/lex_utils.h +++ b/src/lex_utils.h @@ -35,10 +35,11 @@ void LexFunctionDeclaration(const std::string& buffer_content, std::string* insert_text, int* newlines_after_name); -std::string LexWordAroundPos(lsPosition position, const std::string& content); +std::string_view LexIdentifierAroundPos(lsPosition position, + std::string_view content); // Case-insensitive subsequence matching. -bool SubsequenceMatch(std::string_view search, std::string_view content); +bool SubsequenceMatchIgnoreCase(std::string_view search, std::string_view content); std::tuple SubsequenceCountSkip(std::string_view search, std::string_view content); diff --git a/src/messages/text_document_code_action.cc b/src/messages/text_document_code_action.cc index 3c0ac598..cb0f3168 100644 --- a/src/messages/text_document_code_action.cc +++ b/src/messages/text_document_code_action.cc @@ -447,8 +447,8 @@ struct TextDocumentCodeActionHandler // For error diagnostics, provide an action to resolve an include. // TODO: find a way to index diagnostic contents so line numbers // don't get mismatched when actively editing a file. - std::string include_query = - LexWordAroundPos(diag.range.start, working_file->buffer_content); + std::string_view include_query = + LexIdentifierAroundPos(diag.range.start, working_file->buffer_content); if (diag.severity == lsDiagnosticSeverity::Error && !include_query.empty()) { const size_t kMaxResults = 20; diff --git a/src/messages/text_document_definition.cc b/src/messages/text_document_definition.cc index 855709c4..9a0b4447 100644 --- a/src/messages/text_document_definition.cc +++ b/src/messages/text_document_definition.cc @@ -126,7 +126,7 @@ struct TextDocumentDefinitionHandler if (!has_symbol) { lsPosition position = request->params.position; const std::string& buffer = working_file->buffer_content; - std::string query = LexWordAroundPos(position, buffer); + std::string_view query = LexIdentifierAroundPos(position, buffer); bool has_scope = query.find(':') != std::string::npos; // For symbols whose short/detailed names contain |query| as a diff --git a/src/messages/workspace_symbol.cc b/src/messages/workspace_symbol.cc index cd22f9b9..c194dd3f 100644 --- a/src/messages/workspace_symbol.cc +++ b/src/messages/workspace_symbol.cc @@ -107,7 +107,7 @@ struct WorkspaceSymbolHandler : BaseMessageHandler { for (int i = 0; i < (int)db->symbols.size(); ++i) { std::string_view detailed_name = db->GetSymbolDetailedName(i); - if (SubsequenceMatch(query_without_space, detailed_name)) { + if (SubsequenceMatchIgnoreCase(query_without_space, detailed_name)) { // Do not show the same entry twice. if (!inserted_results.insert(std::string(detailed_name)).second) continue;