mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-28 02:21:57 +00:00
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
This commit is contained in:
parent
5a1ed4c943
commit
ac2d921ab9
@ -155,9 +155,20 @@ template <typename T> char *tofixedbase64(T input, char *out) {
|
|||||||
// when given 1000+ completion items.
|
// when given 1000+ completion items.
|
||||||
void FilterCandidates(Out_TextDocumentComplete *complete_response,
|
void FilterCandidates(Out_TextDocumentComplete *complete_response,
|
||||||
const std::string &complete_text, lsPosition begin_pos,
|
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;
|
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::|<int>".
|
||||||
|
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 = [&]() {
|
auto finalize = [&]() {
|
||||||
int max_num = g_config->completion.maxNum;
|
int max_num = g_config->completion.maxNum;
|
||||||
if (items.size() > max_num) {
|
if (items.size() > max_num) {
|
||||||
@ -167,14 +178,21 @@ void FilterCandidates(Out_TextDocumentComplete *complete_response,
|
|||||||
|
|
||||||
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)
|
if (has_open_paren && item.filterText)
|
||||||
item.textEdit.newText = item.label;
|
item.textEdit.newText = item.filterText.value();
|
||||||
// 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;
|
||||||
if (edits.size() && edits[0].range.end == begin_pos) {
|
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;
|
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());
|
edits.erase(edits.begin());
|
||||||
}
|
}
|
||||||
// Compatibility
|
// Compatibility
|
||||||
@ -229,27 +247,6 @@ void FilterCandidates(Out_TextDocumentComplete *complete_response,
|
|||||||
finalize();
|
finalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if position is an points to a '(' character in |lines|. Skips
|
|
||||||
// whitespace.
|
|
||||||
bool IsOpenParenOrAngle(const std::vector<std::string> &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) {
|
lsCompletionItemKind GetCompletionKind(CXCursorKind cursor_kind) {
|
||||||
switch (cursor_kind) {
|
switch (cursor_kind) {
|
||||||
case CXCursor_UnexposedDecl:
|
case CXCursor_UnexposedDecl:
|
||||||
@ -562,7 +559,6 @@ struct Handler_TextDocumentCompletion
|
|||||||
params.position, &completion_text, &end_pos);
|
params.position, &completion_text, &end_pos);
|
||||||
|
|
||||||
ParseIncludeLineResult preprocess = ParseIncludeLine(buffer_line);
|
ParseIncludeLineResult preprocess = ParseIncludeLine(buffer_line);
|
||||||
bool has_open_paren = IsOpenParenOrAngle(file->buffer_lines, end_pos);
|
|
||||||
|
|
||||||
if (preprocess.ok && preprocess.keyword.compare("include") == 0) {
|
if (preprocess.ok && preprocess.keyword.compare("include") == 0) {
|
||||||
Out_TextDocumentComplete out;
|
Out_TextDocumentComplete out;
|
||||||
@ -580,14 +576,14 @@ struct Handler_TextDocumentCompletion
|
|||||||
begin_pos.character = 0;
|
begin_pos.character = 0;
|
||||||
end_pos.character = (int)buffer_line.size();
|
end_pos.character = (int)buffer_line.size();
|
||||||
FilterCandidates(&out, preprocess.pattern, begin_pos, end_pos,
|
FilterCandidates(&out, preprocess.pattern, begin_pos, end_pos,
|
||||||
has_open_paren);
|
buffer_line);
|
||||||
DecorateIncludePaths(preprocess.match, &out.result.items);
|
DecorateIncludePaths(preprocess.match, &out.result.items);
|
||||||
pipeline::WriteStdout(kMethodType, out);
|
pipeline::WriteStdout(kMethodType, out);
|
||||||
} else {
|
} else {
|
||||||
std::string path = params.textDocument.uri.GetPath();
|
std::string path = params.textDocument.uri.GetPath();
|
||||||
CompletionManager::OnComplete callback =
|
CompletionManager::OnComplete callback =
|
||||||
[completion_text, path, begin_pos, end_pos, has_open_paren,
|
[completion_text, path, begin_pos, end_pos,
|
||||||
id = request->id](CodeCompleteConsumer *OptConsumer) {
|
id = request->id, buffer_line](CodeCompleteConsumer *OptConsumer) {
|
||||||
if (!OptConsumer)
|
if (!OptConsumer)
|
||||||
return;
|
return;
|
||||||
auto *Consumer = static_cast<CompletionConsumer *>(OptConsumer);
|
auto *Consumer = static_cast<CompletionConsumer *>(OptConsumer);
|
||||||
@ -596,7 +592,7 @@ struct Handler_TextDocumentCompletion
|
|||||||
out.result.items = Consumer->ls_items;
|
out.result.items = Consumer->ls_items;
|
||||||
|
|
||||||
FilterCandidates(&out, completion_text, begin_pos, end_pos,
|
FilterCandidates(&out, completion_text, begin_pos, end_pos,
|
||||||
has_open_paren);
|
buffer_line);
|
||||||
pipeline::WriteStdout(kMethodType, out);
|
pipeline::WriteStdout(kMethodType, out);
|
||||||
if (!Consumer->from_cache) {
|
if (!Consumer->from_cache) {
|
||||||
cache.WithLock([&]() {
|
cache.WithLock([&]() {
|
||||||
|
Loading…
Reference in New Issue
Block a user