From d61bf578f3ab10b20ad836901ae73c7424e4f3a6 Mon Sep 17 00:00:00 2001 From: Fangrui Song <i@maskray.me> Date: Sun, 6 Oct 2019 23:54:45 -0700 Subject: [PATCH] Add initialization option completion.placeholder; change client.snippetSupport: false to drop ( and < * client.snippetSupport: false => `foo` * client.snippetSupport: true + completion.placeholder: false => `foo($1)$0` `bar<$1>()$0` + completion.placeholder: true => `foo(${1:int a}, ${2:int b})$0` `bar<${1:typename T}>()$0` Note, client.snippetSupport is always false if the client does not support snippets. Close #412 --- src/config.hh | 16 ++++++++++++---- src/messages/initialize.cc | 3 +++ src/messages/textDocument_completion.cc | 23 +++++++++++++++++++---- 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/config.hh b/src/config.hh index 2bb30367..d986fe39 100644 --- a/src/config.hh +++ b/src/config.hh @@ -129,6 +129,8 @@ struct Config { bool hierarchicalDocumentSymbolSupport = true; // TextDocumentClientCapabilities.definition.linkSupport bool linkSupport = true; + + // If false, disable snippets and complete just the identifier part. // TextDocumentClientCapabilities.completion.completionItem.snippetSupport bool snippetSupport = true; } client; @@ -176,9 +178,6 @@ struct Config { // that implement their own filtering and sorting logic. bool filterAndSort = true; - // Maxmum number of results. - int maxNum = 100; - struct Include { // Regex patterns to match include completion candidates against. They // receive the absolute file path. @@ -200,6 +199,15 @@ struct Config { std::vector<std::string> whitelist; } include; + + // Maxmum number of results. + int maxNum = 100; + + // Add placeholder text. Effective only if client.snippetSupport is true. + // + // false: foo($1)$0 + // true: foo(${1:int a}, ${2:int b})$0 + bool placeholder = true; } completion; struct Diagnostics { @@ -343,7 +351,7 @@ REFLECT_STRUCT(Config::Completion::Include, blacklist, maxPathSize, suffixWhitelist, whitelist); REFLECT_STRUCT(Config::Completion, caseSensitivity, detailedLabel, dropOldRequests, duplicateOptional, filterAndSort, include, - maxNum); + maxNum, placeholder); REFLECT_STRUCT(Config::Diagnostics, blacklist, onChange, onOpen, onSave, spellChecking, whitelist) REFLECT_STRUCT(Config::Highlight, largeFileSize, lsRanges, blacklist, whitelist) diff --git a/src/messages/initialize.cc b/src/messages/initialize.cc index cf824ae5..bc820178 100644 --- a/src/messages/initialize.cc +++ b/src/messages/initialize.cc @@ -319,6 +319,9 @@ void do_initialize(MessageHandler *m, InitializeParam ¶m, didChangeWatchedFiles = capabilities.workspace.didChangeWatchedFiles.dynamicRegistration; + if (!g_config->client.snippetSupport) + g_config->completion.duplicateOptional = false; + // Ensure there is a resource directory. if (g_config->clang.resourceDir.empty()) g_config->clang.resourceDir = getDefaultResourceDirectory(); diff --git a/src/messages/textDocument_completion.cc b/src/messages/textDocument_completion.cc index 866198e0..220a63e1 100644 --- a/src/messages/textDocument_completion.cc +++ b/src/messages/textDocument_completion.cc @@ -23,6 +23,7 @@ limitations under the License. #include <clang/Sema/CodeCompleteConsumer.h> #include <clang/Sema/Sema.h> +#include <llvm/ADT/Twine.h> #if LLVM_VERSION_MAJOR < 8 #include <regex> @@ -382,7 +383,7 @@ void buildItem(const CodeCompletionResult &r, const CodeCompletionString &ccs, continue; } out[i].textEdit.newText += - "${" + std::to_string(out[i].parameters_.size()) + ":" + text + "}"; + ("${" + Twine(out[i].parameters_.size()) + ":" + text + "}").str(); out[i].insertTextFormat = InsertTextFormat::Snippet; } else if (kind != CodeCompletionString::CK_Informative) { out[i].textEdit.newText += text; @@ -462,9 +463,23 @@ public: buildItem(r, *ccs, ls_items); for (size_t j = first_idx; j < ls_items.size(); j++) { - if (g_config->client.snippetSupport && - ls_items[j].insertTextFormat == InsertTextFormat::Snippet) - ls_items[j].textEdit.newText += "$0"; + std::string &s = ls_items[j].textEdit.newText; + if (!g_config->client.snippetSupport) { + if (s.size()) { + // Delete non-identifier parts. + if (s.back() == '(' || s.back() == '<') + s.pop_back(); + else if (s.size() >= 2 && !s.compare(s.size() - 2, 2, "()")) + s.resize(s.size() - 2); + } + } else if (ls_items[j].insertTextFormat == InsertTextFormat::Snippet) { + if (!g_config->completion.placeholder) { + // foo(${1:int a}, ${2:int b}) -> foo($1)$0 + auto p = s.find("${"), q = s.rfind('}'); + s.replace(p, q - p + 1, "$1"); + } + s += "$0"; + } ls_items[j].priority_ = ccs->getPriority(); if (!g_config->completion.detailedLabel) { ls_items[j].detail = ls_items[j].label;