From 15dd552610ab8509782cfd0d07fc5d41105ff69c Mon Sep 17 00:00:00 2001 From: scturtle Date: Sat, 3 Feb 2018 22:36:07 +0800 Subject: [PATCH] Complex include completion. --- src/lex_utils.cc | 68 +++++++++++++----------- src/lex_utils.h | 14 ++++- src/messages/text_document_completion.cc | 24 ++++----- 3 files changed, 59 insertions(+), 47 deletions(-) diff --git a/src/lex_utils.cc b/src/lex_utils.cc index e3f81cd6..49fa40cb 100644 --- a/src/lex_utils.cc +++ b/src/lex_utils.cc @@ -45,38 +45,42 @@ lsPosition CharPos(std::string_view search, return result; } -std::tuple ShouldRunIncludeCompletion( - const std::string& line) { - size_t start = 0; - while (start < line.size() && isspace(line[start])) - ++start; - // Must start with '#'. - if (start >= line.size() || line[start] != '#') - return std::make_tuple(false, "", ""); - ++start; - // Ingore "include" and following spaces. - if (line.compare(start, 7, "include") == 0) { - start += 7; - while (start < line.size() && isspace(line[start])) - ++start; - } - // Determine the surrounding characters. - std::string surround; - if (line[start] == '"') { - surround = "\"\""; - ++start; - } else if (line[start] == '<') { - surround = "<>"; - ++start; - } else - surround = "<>"; - // Fix the prefix for completion. - size_t end = start; - while (end < line.size() && line[end] != '\"' && line[end] != '>') - ++end; - std::string prefix = line.substr(start, end - start); +ParseIncludeLineResult ParseIncludeLine(const std::string& line) { + static const std::regex pattern( + "(\\s*)" // [1]: spaces before '#' + "#" // + "(\\s*)" // [2]: spaces after '#' + "([^\\s\"<]*)" // [3]: "include" + "(\\s*)" // [4]: spaces before quote + "([\"<])?" // [5]: the first quote char + "([^\\s\">]*)" // [6]: path of file + "[\">]?" // + "(.*)"); // [7]: suffix after quote char + std::smatch match; + bool ok = std::regex_match(line, match, pattern); + std::string text = match[3].str() + match[6].str(); + return {ok, text, match}; +} - return std::make_tuple(true, surround, prefix); +void DecorateIncludePaths(const std::smatch& match, + std::vector* items) { + char quote0, quote1; + if (match[5].compare("\"") == 0) + quote0 = quote1 = '"'; + else + quote0 = '<', quote1 = '>'; + + std::string spaces_between_include_and_quote = + match[3].compare("include") == 0 ? match[4].str() : " "; + + std::string prefix = match[1].str() + '#' + match[2].str() + "include" + + spaces_between_include_and_quote + quote0; + std::string suffix = std::string(1, quote1) + match[7].str(); + + for (lsCompletionItem& item : *items) { + item.textEdit->newText = prefix + item.textEdit->newText + suffix; + item.label = prefix + item.label.substr(7) + suffix; + } } // TODO: eliminate |line_number| param. @@ -229,6 +233,8 @@ bool SubsequenceMatch(std::string_view search, std::string_view content) { return true; } +// Find discontinous |search| in |content|. +// Return |found| and the count of skipped chars before found. std::tuple SubsequenceCountSkip(std::string_view search, std::string_view content) { bool hasUppercaseLetter = std::any_of(search.begin(), search.end(), isupper); diff --git a/src/lex_utils.h b/src/lex_utils.h index 41d2a5eb..8f8af88f 100644 --- a/src/lex_utils.h +++ b/src/lex_utils.h @@ -4,6 +4,7 @@ #include +#include #include #include @@ -14,8 +15,17 @@ lsPosition CharPos(std::string_view search, char character, int character_offset = 0); -std::tuple ShouldRunIncludeCompletion( - const std::string& line); +struct ParseIncludeLineResult +{ + bool ok; + std::string text; // include the "include" part + std::smatch match; +}; + +ParseIncludeLineResult ParseIncludeLine(const std::string& line); + +void DecorateIncludePaths(const std::smatch& match, + std::vector* items); // TODO: eliminate |line_number| param. optional ExtractQuotedRange(int line_number, const std::string& line); diff --git a/src/messages/text_document_completion.cc b/src/messages/text_document_completion.cc index 88966a2c..6a44a541 100644 --- a/src/messages/text_document_completion.cc +++ b/src/messages/text_document_completion.cc @@ -233,11 +233,9 @@ struct TextDocumentCompletionHandler : MessageHandler { &existing_completion); } - bool yes; - std::string surround, prefix; - std::tie(yes, surround, prefix) = ShouldRunIncludeCompletion(buffer_line); + ParseIncludeLineResult result = ParseIncludeLine(buffer_line); - if (yes) { + if (result.ok) { Out_TextDocumentComplete out; out.id = request->id; @@ -252,22 +250,20 @@ struct TextDocumentCompletionHandler : MessageHandler { lock.unlock(); } - FilterAndSortCompletionResponse(&out, prefix, - config->completion.filterAndSort); + // Needed by |FilterAndSortCompletionResponse|. + // Will be removed in |DecorateIncludePaths|. + for (lsCompletionItem& item : out.result.items) + item.label = "include" + item.label; + + FilterAndSortCompletionResponse(&out, result.text, + config->completion.filterAndSort); + DecorateIncludePaths(result.match, &out.result.items); - auto decorator = [&](std::string& text) { - std::string result = "#include "; - result += surround[0] + text + surround[1]; - text = result; - }; - LOG_S(INFO) << "DEBUG prefix " << prefix; for (lsCompletionItem& item : out.result.items) { item.textEdit->range.start.line = request->params.position.line; item.textEdit->range.start.character = 0; item.textEdit->range.end.line = request->params.position.line; item.textEdit->range.end.character = (int)buffer_line.size(); - decorator(item.textEdit->newText); - decorator(item.label); } QueueManager::WriteStdout(IpcId::TextDocumentCompletion, out);