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
This commit is contained in:
Fangrui Song 2019-10-06 23:54:45 -07:00
parent aa9668a8fc
commit 2bffff7b0b
3 changed files with 34 additions and 8 deletions

View File

@ -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<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 {
@ -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)

View File

@ -307,6 +307,9 @@ void do_initialize(MessageHandler *m, InitializeParam &param,
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();

View File

@ -11,6 +11,7 @@
#include <clang/Sema/CodeCompleteConsumer.h>
#include <clang/Sema/Sema.h>
#include <llvm/ADT/Twine.h>
#if LLVM_VERSION_MAJOR < 8
#include <regex>
@ -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;