mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-21 23:25:07 +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
49dd0ed558
commit
3de62168c8
@ -143,9 +143,20 @@ template <typename T> char *tofixedbase64(T input, char *out) {
|
||||
// when given 1000+ completion items.
|
||||
void FilterCandidates(Out_TextDocumentComplete *complete_response,
|
||||
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;
|
||||
|
||||
// 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 = [&]() {
|
||||
int max_num = g_config->completion.maxNum;
|
||||
if (items.size() > max_num) {
|
||||
@ -155,14 +166,21 @@ void FilterCandidates(Out_TextDocumentComplete *complete_response,
|
||||
|
||||
for (auto &item : items) {
|
||||
item.textEdit.range = lsRange{begin_pos, end_pos};
|
||||
if (has_open_paren)
|
||||
item.textEdit.newText = item.label;
|
||||
if (has_open_paren && item.filterText)
|
||||
item.textEdit.newText = item.filterText.value();
|
||||
// https://github.com/Microsoft/language-server-protocol/issues/543
|
||||
// Order of textEdit and additionalTextEdits is unspecified.
|
||||
auto &edits = item.additionalTextEdits;
|
||||
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;
|
||||
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());
|
||||
}
|
||||
// Compatibility
|
||||
@ -217,27 +235,6 @@ void FilterCandidates(Out_TextDocumentComplete *complete_response,
|
||||
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) {
|
||||
switch (cursor_kind) {
|
||||
case CXCursor_UnexposedDecl:
|
||||
@ -550,7 +547,6 @@ struct Handler_TextDocumentCompletion
|
||||
params.position, &completion_text, &end_pos);
|
||||
|
||||
ParseIncludeLineResult preprocess = ParseIncludeLine(buffer_line);
|
||||
bool has_open_paren = IsOpenParenOrAngle(file->buffer_lines, end_pos);
|
||||
|
||||
if (preprocess.ok && preprocess.keyword.compare("include") == 0) {
|
||||
Out_TextDocumentComplete out;
|
||||
@ -568,14 +564,14 @@ struct Handler_TextDocumentCompletion
|
||||
begin_pos.character = 0;
|
||||
end_pos.character = (int)buffer_line.size();
|
||||
FilterCandidates(&out, preprocess.pattern, begin_pos, end_pos,
|
||||
has_open_paren);
|
||||
buffer_line);
|
||||
DecorateIncludePaths(preprocess.match, &out.result.items);
|
||||
pipeline::WriteStdout(kMethodType, out);
|
||||
} else {
|
||||
std::string path = params.textDocument.uri.GetPath();
|
||||
CompletionManager::OnComplete callback =
|
||||
[completion_text, path, begin_pos, end_pos, has_open_paren,
|
||||
id = request->id](CodeCompleteConsumer *OptConsumer) {
|
||||
[completion_text, path, begin_pos, end_pos,
|
||||
id = request->id, buffer_line](CodeCompleteConsumer *OptConsumer) {
|
||||
if (!OptConsumer)
|
||||
return;
|
||||
auto *Consumer = static_cast<CompletionConsumer *>(OptConsumer);
|
||||
@ -584,7 +580,7 @@ struct Handler_TextDocumentCompletion
|
||||
out.result.items = Consumer->ls_items;
|
||||
|
||||
FilterCandidates(&out, completion_text, begin_pos, end_pos,
|
||||
has_open_paren);
|
||||
buffer_line);
|
||||
pipeline::WriteStdout(kMethodType, out);
|
||||
if (!Consumer->from_cache) {
|
||||
cache.WithLock([&]() {
|
||||
|
Loading…
Reference in New Issue
Block a user