diff --git a/src/config.h b/src/config.h index ac2582e3..96ed1cd6 100644 --- a/src/config.h +++ b/src/config.h @@ -101,7 +101,8 @@ struct Config { // An example value is { ".h", ".hpp" } // // This is significantly faster than using a regex. - std::vector includeCompletionWhitelistLiteralEnding; + std::vector includeCompletionWhitelistLiteralEnding = { + ".h", ".hpp", ".hh"}; // Regex patterns to match include completion candidates against. They // receive the absolute file path. // diff --git a/src/include_complete.cc b/src/include_complete.cc index 5f10aaa3..e5a90b9f 100644 --- a/src/include_complete.cc +++ b/src/include_complete.cc @@ -78,30 +78,21 @@ bool TrimPath(Project* project, lsCompletionItem BuildCompletionItem(Config* config, const std::string& path, - bool use_angle_brackets, + bool /*use_angle_brackets*/, bool is_stl) { lsCompletionItem item; - if (use_angle_brackets) - item.label = "#include <" + ElideLongPath(config, path) + ">"; - else - item.label = "#include \"" + ElideLongPath(config, path) + "\""; - + item.label = ElideLongPath(config, path); item.detail = path; - - // Replace the entire existing content. - // NOTE: When submitting completion items, textEdit->range must be updated. item.textEdit = lsTextEdit(); - if (use_angle_brackets) - item.textEdit->newText = "#include <" + path + ">"; - else - item.textEdit->newText = "#include \"" + path + "\""; - + item.textEdit->newText = path; item.insertTextFormat = lsInsertTextFormat::PlainText; - if (is_stl) + if (is_stl) { item.kind = lsCompletionItemKind::Module; - else + item.priority_ = 2; + } else { item.kind = lsCompletionItemKind::File; - + item.priority_ = 1; + } return item; } @@ -127,8 +118,8 @@ void IncludeComplete::Rescan() { Timer timer; InsertStlIncludes(); - InsertIncludesFromDirectory(config_->projectRoot, - false /*use_angle_brackets*/); + // InsertIncludesFromDirectory(config_->projectRoot, + // false /*use_angle_brackets*/); for (const std::string& dir : project_->quote_include_directories) InsertIncludesFromDirectory(dir, false /*use_angle_brackets*/); for (const std::string& dir : project_->angle_include_directories) diff --git a/src/lex_utils.cc b/src/lex_utils.cc index 0f5bd63d..e0619f57 100644 --- a/src/lex_utils.cc +++ b/src/lex_utils.cc @@ -45,11 +45,38 @@ lsPosition CharPos(const std::string& search, return result; } -bool ShouldRunIncludeCompletion(const std::string& line) { +std::tuple ShouldRunIncludeCompletion( + const std::string& line) { size_t start = 0; while (start < line.size() && isspace(line[start])) ++start; - return start < line.size() && line[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); + + return std::make_tuple(true, surround, prefix); } // TODO: eliminate |line_number| param. diff --git a/src/lex_utils.h b/src/lex_utils.h index 69355ba4..9933bded 100644 --- a/src/lex_utils.h +++ b/src/lex_utils.h @@ -3,6 +3,7 @@ #include "language_server_api.h" #include +#include // Utility method to map |position| to an offset inside of |content|. int GetOffsetForPosition(lsPosition position, const std::string& content); @@ -11,7 +12,8 @@ lsPosition CharPos(const std::string& search, char character, int character_offset = 0); -bool ShouldRunIncludeCompletion(const std::string& line); +std::tuple ShouldRunIncludeCompletion( + const std::string& line); // TODO: eliminate |line_number| param. optional ExtractQuotedRange(int line_number, const std::string& line); diff --git a/src/messages/initialize.cc b/src/messages/initialize.cc index f93f7bf9..636c35cb 100644 --- a/src/messages/initialize.cc +++ b/src/messages/initialize.cc @@ -38,7 +38,7 @@ struct lsCompletionOptions { // for // '::' and '>' for '->'. See // https://github.com/Microsoft/language-server-protocol/issues/138. - std::vector triggerCharacters = {".", ":", ">", "#"}; + std::vector triggerCharacters = {".", ":", ">", "#", "<", "\""}; }; MAKE_REFLECT_STRUCT(lsCompletionOptions, resolveProvider, triggerCharacters); diff --git a/src/messages/text_document_completion.cc b/src/messages/text_document_completion.cc index 1286e511..7cfd2d25 100644 --- a/src/messages/text_document_completion.cc +++ b/src/messages/text_document_completion.cc @@ -256,9 +256,17 @@ struct TextDocumentCompletionHandler : MessageHandler { std::string character = *request->params.context->triggerCharacter; char preceding_index = request->params.position.character - 2; + // If the character is '"' or '<', make sure the line is start with '#'. + if (character == "\"" || character == "<") { + size_t i = 0; + while (i < buffer_line.size() && isspace(buffer_line[i])) + ++i; + if (i >= buffer_line.size() || buffer_line[i] != '#') + did_fail_check = true; + } // If the character is > or : and we are at the start of the line, do not // show completion results. - if ((character == ">" || character == ":") && preceding_index < 0) { + else if ((character == ">" || character == ":") && preceding_index < 0) { did_fail_check = true; } // If the character is > but - does not preced it, or if it is : and : @@ -283,7 +291,11 @@ struct TextDocumentCompletionHandler : MessageHandler { &existing_completion); } - if (ShouldRunIncludeCompletion(buffer_line)) { + bool yes; + std::string surround, prefix; + std::tie(yes, surround, prefix) = ShouldRunIncludeCompletion(buffer_line); + + if (yes) { Out_TextDocumentComplete out; out.id = request->id; @@ -296,19 +308,26 @@ struct TextDocumentCompletionHandler : MessageHandler { include_complete->completion_items.end()); if (lock) lock.unlock(); - - // Update textEdit params. - 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(); - } } - TrimInPlace(buffer_line); - FilterAndSortCompletionResponse(&out, buffer_line, + FilterAndSortCompletionResponse(&out, prefix, config->completion.filterAndSort); + + 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); } else { // If existing completion is empty, dont return clang-based completion