From 3de62168c8ac2e59083aec095da0e1fa2db007e7 Mon Sep 17 00:00:00 2001 From: Riatre Foo Date: Wed, 10 Oct 2018 06:36:39 +0800 Subject: [PATCH] Fix additionalTextEdits -> textEdit hack for VS Code (#89) * Fix additionalTextEdits -> textEdit hack for VS Code Visual Studio Code filters the completion result according to textEdit.range and filterText, if the textEdit.range overlaps with existing text, we have to include it in filterText, otherwise it would be filtered out. * Fix has_open_paren in FilterCandidates --- src/messages/textDocument_completion.cc | 56 ++++++++++++------------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/src/messages/textDocument_completion.cc b/src/messages/textDocument_completion.cc index 062709cc..481d5959 100644 --- a/src/messages/textDocument_completion.cc +++ b/src/messages/textDocument_completion.cc @@ -143,9 +143,20 @@ template char *tofixedbase64(T input, char *out) { // when given 1000+ completion items. void FilterCandidates(Out_TextDocumentComplete *complete_response, const std::string &complete_text, lsPosition begin_pos, - lsPosition end_pos, bool has_open_paren) { + lsPosition end_pos, const std::string &buffer_line) { + assert(begin_pos.line == end_pos.line); auto &items = complete_response->result.items; + // People usually does not want to insert snippets or parenthesis when + // changing function or type names, e.g. "str.|()" or "std::|". + bool has_open_paren = false; + for (int c = end_pos.character; c < buffer_line.size(); ++c) { + if (buffer_line[c] == '(' || buffer_line[c] == '<') + has_open_paren = true; + if (!isspace(buffer_line[c])) + break; + } + auto finalize = [&]() { int max_num = g_config->completion.maxNum; if (items.size() > max_num) { @@ -155,14 +166,21 @@ void FilterCandidates(Out_TextDocumentComplete *complete_response, for (auto &item : items) { item.textEdit.range = lsRange{begin_pos, end_pos}; - if (has_open_paren) - item.textEdit.newText = item.label; + if (has_open_paren && item.filterText) + item.textEdit.newText = item.filterText.value(); // https://github.com/Microsoft/language-server-protocol/issues/543 // Order of textEdit and additionalTextEdits is unspecified. auto &edits = item.additionalTextEdits; if (edits.size() && edits[0].range.end == begin_pos) { - item.textEdit.range.start = edits[0].range.start; + lsPosition start = edits[0].range.start, end = edits[0].range.end; + item.textEdit.range.start = start; item.textEdit.newText = edits[0].newText + item.textEdit.newText; + if (start.line == begin_pos.line && item.filterText) { + item.filterText = + buffer_line.substr(start.character, + end.character - start.character) + + item.filterText.value(); + } edits.erase(edits.begin()); } // Compatibility @@ -217,27 +235,6 @@ void FilterCandidates(Out_TextDocumentComplete *complete_response, finalize(); } -// Returns true if position is an points to a '(' character in |lines|. Skips -// whitespace. -bool IsOpenParenOrAngle(const std::vector &lines, - const lsPosition &position) { - auto [c, l] = position; - while (l < lines.size()) { - const auto &line = lines[l]; - if (c >= line.size()) - return false; - if (line[c] == '(' || line[c] == '<') - return true; - if (!isspace(line[c])) - break; - if (++c >= line.size()) { - c = 0; - l++; - } - } - return false; -} - lsCompletionItemKind GetCompletionKind(CXCursorKind cursor_kind) { switch (cursor_kind) { case CXCursor_UnexposedDecl: @@ -550,7 +547,6 @@ struct Handler_TextDocumentCompletion params.position, &completion_text, &end_pos); ParseIncludeLineResult preprocess = ParseIncludeLine(buffer_line); - bool has_open_paren = IsOpenParenOrAngle(file->buffer_lines, end_pos); if (preprocess.ok && preprocess.keyword.compare("include") == 0) { Out_TextDocumentComplete out; @@ -568,14 +564,14 @@ struct Handler_TextDocumentCompletion begin_pos.character = 0; end_pos.character = (int)buffer_line.size(); FilterCandidates(&out, preprocess.pattern, begin_pos, end_pos, - has_open_paren); + buffer_line); DecorateIncludePaths(preprocess.match, &out.result.items); pipeline::WriteStdout(kMethodType, out); } else { std::string path = params.textDocument.uri.GetPath(); CompletionManager::OnComplete callback = - [completion_text, path, begin_pos, end_pos, has_open_paren, - id = request->id](CodeCompleteConsumer *OptConsumer) { + [completion_text, path, begin_pos, end_pos, + id = request->id, buffer_line](CodeCompleteConsumer *OptConsumer) { if (!OptConsumer) return; auto *Consumer = static_cast(OptConsumer); @@ -584,7 +580,7 @@ struct Handler_TextDocumentCompletion out.result.items = Consumer->ls_items; FilterCandidates(&out, completion_text, begin_pos, end_pos, - has_open_paren); + buffer_line); pipeline::WriteStdout(kMethodType, out); if (!Consumer->from_cache) { cache.WithLock([&]() {