completion: delete insertText; don't set filterText if it is the same as label

It decreases Content-Length: from 32K to 25K for the following case:

 #include <bits/stdc++.h>
int main() { std::| }

Also

* make results deterministic when completion text is empty
* sort by newText, label, filterText
This commit is contained in:
Fangrui Song 2018-11-23 11:53:51 -08:00
parent 9d767ffa99
commit 8fc6b8a9f4
2 changed files with 51 additions and 40 deletions

View File

@ -119,10 +119,9 @@ struct CompletionItem {
std::string label; std::string label;
CompletionItemKind kind = CompletionItemKind::Text; CompletionItemKind kind = CompletionItemKind::Text;
std::string detail; std::string detail;
std::optional<std::string> documentation; std::string documentation;
std::string sortText; std::string sortText;
std::optional<std::string> filterText; std::string filterText;
std::string insertText;
InsertTextFormat insertTextFormat = InsertTextFormat::PlainText; InsertTextFormat insertTextFormat = InsertTextFormat::PlainText;
TextEdit textEdit; TextEdit textEdit;
std::vector<TextEdit> additionalTextEdits; std::vector<TextEdit> additionalTextEdits;

View File

@ -22,9 +22,23 @@ using namespace llvm;
MAKE_REFLECT_TYPE_PROXY(InsertTextFormat); MAKE_REFLECT_TYPE_PROXY(InsertTextFormat);
MAKE_REFLECT_TYPE_PROXY(CompletionItemKind); MAKE_REFLECT_TYPE_PROXY(CompletionItemKind);
MAKE_REFLECT_STRUCT(CompletionItem, label, kind, detail, documentation,
sortText, filterText, insertText, insertTextFormat, void Reflect(Writer &vis, CompletionItem &v) {
textEdit, additionalTextEdits); REFLECT_MEMBER_START();
REFLECT_MEMBER(label);
REFLECT_MEMBER(kind);
REFLECT_MEMBER(detail);
if (v.documentation.size())
REFLECT_MEMBER(documentation);
REFLECT_MEMBER(sortText);
if (v.filterText.size())
REFLECT_MEMBER(filterText);
REFLECT_MEMBER(insertTextFormat);
REFLECT_MEMBER(textEdit);
if (v.additionalTextEdits.size())
REFLECT_MEMBER(additionalTextEdits);
REFLECT_MEMBER_END();
}
namespace { namespace {
struct CompletionList { struct CompletionList {
@ -55,7 +69,6 @@ void DecorateIncludePaths(const std::smatch &match,
item.textEdit.newText = item.textEdit.newText =
prefix + quote0 + item.textEdit.newText + quote1 + suffix; prefix + quote0 + item.textEdit.newText + quote1 + suffix;
item.label = prefix + quote0 + item.label + quote1 + suffix; item.label = prefix + quote0 + item.label + quote1 + suffix;
item.filterText = std::nullopt;
} }
} }
@ -112,8 +125,8 @@ void FilterCandidates(CompletionList &result, const std::string &complete_text,
std::string sort(4, ' '); std::string sort(4, ' ');
for (auto &item : items) { for (auto &item : items) {
item.textEdit.range = lsRange{begin_pos, end_pos}; item.textEdit.range = lsRange{begin_pos, end_pos};
if (has_open_paren && item.filterText) if (has_open_paren)
item.textEdit.newText = *item.filterText; item.textEdit.newText = item.filterText;
// https://github.com/Microsoft/language-server-protocol/issues/543 // https://github.com/Microsoft/language-server-protocol/issues/543
// Order of textEdit and additionalTextEdits is unspecified. // Order of textEdit and additionalTextEdits is unspecified.
auto &edits = item.additionalTextEdits; auto &edits = item.additionalTextEdits;
@ -121,48 +134,44 @@ void FilterCandidates(CompletionList &result, const std::string &complete_text,
Position start = edits[0].range.start, end = edits[0].range.end; Position start = edits[0].range.start, end = edits[0].range.end;
item.textEdit.range.start = start; item.textEdit.range.start = start;
item.textEdit.newText = edits[0].newText + item.textEdit.newText; item.textEdit.newText = edits[0].newText + item.textEdit.newText;
if (start.line == begin_pos.line && item.filterText) { if (start.line == begin_pos.line) {
item.filterText = item.filterText =
buffer_line.substr(start.character, buffer_line.substr(start.character,
end.character - start.character) + end.character - start.character) +
*item.filterText; item.filterText;
} }
edits.erase(edits.begin()); edits.erase(edits.begin());
} }
if (item.filterText == item.label)
item.filterText.clear();
for (auto i = sort.size(); i && ++sort[i - 1] == 'A';) for (auto i = sort.size(); i && ++sort[i - 1] == 'A';)
sort[--i] = ' '; sort[--i] = ' ';
item.sortText = sort; item.sortText = sort;
// Compatibility
item.insertText = item.textEdit.newText;
} }
}; };
// No complete text; don't run any filtering logic except to trim the items. if (!g_config->completion.filterAndSort) {
if (!g_config->completion.filterAndSort || complete_text.empty()) {
finalize(); finalize();
return; return;
} }
// Make sure all items have |filterText| set, code that follow needs it. if (complete_text.size()) {
for (auto &item : items) { // Fuzzy match and remove awful candidates.
if (!item.filterText) bool sensitive = g_config->completion.caseSensitivity;
item.filterText = item.label; FuzzyMatcher fuzzy(complete_text, sensitive);
for (CompletionItem &item : items) {
const std::string &filter =
item.filterText.size() ? item.filterText : item.label;
item.score_ = ReverseSubseqMatch(complete_text, filter, sensitive) >= 0
? fuzzy.Match(filter)
: FuzzyMatcher::kMinScore;
}
items.erase(std::remove_if(items.begin(), items.end(),
[](const CompletionItem &item) {
return item.score_ <= FuzzyMatcher::kMinScore;
}),
items.end());
} }
// Fuzzy match and remove awful candidates.
bool sensitive = g_config->completion.caseSensitivity;
FuzzyMatcher fuzzy(complete_text, sensitive);
for (auto &item : items) {
item.score_ =
ReverseSubseqMatch(complete_text, *item.filterText, sensitive) >= 0
? fuzzy.Match(*item.filterText)
: FuzzyMatcher::kMinScore;
}
items.erase(std::remove_if(items.begin(), items.end(),
[](const CompletionItem &item) {
return item.score_ <= FuzzyMatcher::kMinScore;
}),
items.end());
std::sort(items.begin(), items.end(), std::sort(items.begin(), items.end(),
[](const CompletionItem &lhs, const CompletionItem &rhs) { [](const CompletionItem &lhs, const CompletionItem &rhs) {
int t = int(lhs.additionalTextEdits.size() - int t = int(lhs.additionalTextEdits.size() -
@ -173,9 +182,13 @@ void FilterCandidates(CompletionList &result, const std::string &complete_text,
return lhs.score_ > rhs.score_; return lhs.score_ > rhs.score_;
if (lhs.priority_ != rhs.priority_) if (lhs.priority_ != rhs.priority_)
return lhs.priority_ < rhs.priority_; return lhs.priority_ < rhs.priority_;
if (lhs.filterText->size() != rhs.filterText->size()) t = lhs.textEdit.newText.compare(rhs.textEdit.newText);
return lhs.filterText->size() < rhs.filterText->size(); if (t)
return *lhs.filterText < *rhs.filterText; return t < 0;
t = lhs.label.compare(rhs.label);
if (t)
return t < 0;
return lhs.filterText < rhs.filterText;
}); });
// Trim result. // Trim result.
@ -281,8 +294,7 @@ void BuildItem(const CodeCompletionResult &R, const CodeCompletionString &CCS,
case CodeCompletionString::CK_TypedText: case CodeCompletionString::CK_TypedText:
text = Chunk.Text; text = Chunk.Text;
for (auto i = first; i < out.size(); i++) for (auto i = first; i < out.size(); i++)
if (Kind == CodeCompletionString::CK_TypedText && !out[i].filterText) out[i].filterText = text;
out[i].filterText = text;
break; break;
case CodeCompletionString::CK_Placeholder: case CodeCompletionString::CK_Placeholder:
text = Chunk.Text; text = Chunk.Text;
@ -402,7 +414,7 @@ public:
ls_items[j].priority_ = CCS->getPriority(); ls_items[j].priority_ = CCS->getPriority();
if (!g_config->completion.detailedLabel) { if (!g_config->completion.detailedLabel) {
ls_items[j].detail = ls_items[j].label; ls_items[j].detail = ls_items[j].label;
ls_items[j].label = ls_items[j].filterText.value_or(""); ls_items[j].label = ls_items[j].filterText;
} }
} }
#if LLVM_VERSION_MAJOR >= 7 #if LLVM_VERSION_MAJOR >= 7