diff --git a/src/config.hh b/src/config.hh index 4b0d6471..751a712c 100644 --- a/src/config.hh +++ b/src/config.hh @@ -117,6 +117,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; @@ -164,9 +166,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. @@ -188,6 +187,15 @@ struct Config { std::vector 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 { @@ -331,7 +339,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 18fa4d0c..ca046bc5 100644 --- a/src/messages/initialize.cc +++ b/src/messages/initialize.cc @@ -307,6 +307,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 5178e459..b5a556ca 100644 --- a/src/messages/textDocument_completion.cc +++ b/src/messages/textDocument_completion.cc @@ -11,6 +11,7 @@ #include #include +#include #if LLVM_VERSION_MAJOR < 8 #include @@ -370,7 +371,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; @@ -450,9 +451,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;