107 changes: 107 additions & 0 deletions clang-tools-extra/clangd/Protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,113 @@ struct CodeActionParams {
parse(llvm::yaml::MappingNode *Params);
};

struct TextDocumentPositionParams {
/// The text document.
TextDocumentIdentifier textDocument;

/// The position inside the text document.
Position position;

static llvm::Optional<TextDocumentPositionParams>
parse(llvm::yaml::MappingNode *Params);
};

/// The kind of a completion entry.
enum class CompletionItemKind {
Missing = 0,
Text = 1,
Method = 2,
Function = 3,
Constructor = 4,
Field = 5,
Variable = 6,
Class = 7,
Interface = 8,
Module = 9,
Property = 10,
Unit = 11,
Value = 12,
Enum = 13,
Keyword = 14,
Snippet = 15,
Color = 16,
File = 17,
Reference = 18,
};

/// Defines whether the insert text in a completion item should be interpreted
/// as plain text or a snippet.
enum class InsertTextFormat {
Missing = 0,
/// The primary text to be inserted is treated as a plain string.
PlainText = 1,
/// The primary text to be inserted is treated as a snippet.
///
/// A snippet can define tab stops and placeholders with `$1`, `$2`
/// and `${3:foo}`. `$0` defines the final tab stop, it defaults to the end
/// of the snippet. Placeholders with equal identifiers are linked, that is
/// typing in one will update others too.
///
/// See also:
/// https//github.com/Microsoft/vscode/blob/master/src/vs/editor/contrib/snippet/common/snippet.md
Snippet = 2,
};

struct CompletionItem {
/// The label of this completion item. By default also the text that is
/// inserted when selecting this completion.
std::string label;

/// The kind of this completion item. Based of the kind an icon is chosen by
/// the editor.
CompletionItemKind kind = CompletionItemKind::Missing;

/// A human-readable string with additional information about this item, like
/// type or symbol information.
std::string detail;

/// A human-readable string that represents a doc-comment.
std::string documentation;

/// A string that should be used when comparing this item with other items.
/// When `falsy` the label is used.
std::string sortText;

/// A string that should be used when filtering a set of completion items.
/// When `falsy` the label is used.
std::string filterText;

/// A string that should be inserted to a document when selecting this
/// completion. When `falsy` the label is used.
std::string insertText;

/// The format of the insert text. The format applies to both the `insertText`
/// property and the `newText` property of a provided `textEdit`.
InsertTextFormat insertTextFormat = InsertTextFormat::Missing;

/// An edit which is applied to a document when selecting this completion.
/// When an edit is provided `insertText` is ignored.
///
/// Note: The range of the edit must be a single line range and it must
/// contain the position at which completion has been requested.
llvm::Optional<TextEdit> textEdit;

/// An optional array of additional text edits that are applied when selecting
/// this completion. Edits must not overlap with the main edit nor with
/// themselves.
std::vector<TextEdit> additionalTextEdits;

// TODO(krasimir): The following optional fields defined by the language
// server protocol are unsupported:
//
// command?: Command - An optional command that is executed *after* inserting
// this completion.
//
// data?: any - A data entry field that is preserved on a completion item
// between a completion and a completion resolve request.
static std::string unparse(const CompletionItem &P);
};

} // namespace clangd
} // namespace clang

Expand Down
22 changes: 22 additions & 0 deletions clang-tools-extra/clangd/ProtocolHandlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,25 @@ void CodeActionHandler::handleMethod(llvm::yaml::MappingNode *Params,
R"(, "result": [)" + Commands +
R"(]})");
}

void CompletionHandler::handleMethod(llvm::yaml::MappingNode *Params,
StringRef ID) {
auto TDPP = TextDocumentPositionParams::parse(Params);
if (!TDPP) {
Output.log("Failed to decode TextDocumentPositionParams!\n");
return;
}

auto Items = AST.codeComplete(TDPP->textDocument.uri, TDPP->position.line,
TDPP->position.character);
std::string Completions;
for (const auto &Item : Items) {
Completions += CompletionItem::unparse(Item);
Completions += ",";
}
if (!Completions.empty())
Completions.pop_back();
writeMessage(
R"({"jsonrpc":"2.0","id":)" + ID.str() +
R"(,"result":[)" + Completions + R"(]})");
}
13 changes: 12 additions & 1 deletion clang-tools-extra/clangd/ProtocolHandlers.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ struct InitializeHandler : Handler {
"documentFormattingProvider": true,
"documentRangeFormattingProvider": true,
"documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},
"codeActionProvider": true
"codeActionProvider": true,
"completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">"]}
}}})");
}
};
Expand Down Expand Up @@ -114,6 +115,16 @@ struct CodeActionHandler : Handler {
ASTManager &AST;
};

struct CompletionHandler : Handler {
CompletionHandler(JSONOutput &Output, ASTManager &AST)
: Handler(Output), AST(AST) {}

void handleMethod(llvm::yaml::MappingNode *Params, StringRef ID) override;

private:
ASTManager &AST;
};

} // namespace clangd
} // namespace clang

Expand Down
69 changes: 69 additions & 0 deletions clang-tools-extra/test/clangd/completion.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# RUN: clangd -run-synchronously < %s | FileCheck %s
# It is absolutely vital that this file has CRLF line endings.
#
Content-Length: 125

{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}

Content-Length: 208

{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///main.cpp","languageId":"cpp","version":1,"text":"#include <vector>\nint main() {\n std::vector<int> v;\n v.\n}\n"}}}

Content-Length: 148

{"jsonrpc":"2.0","id":1,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":4}}}
# The order of results returned by ASTUnit CodeComplete seems to be
# nondeterministic, so we check regardless of order.
#
# CHECK: {"jsonrpc":"2.0","id":1,"result":[
# CHECK-DAG: {"label":"_M_allocate"}
# CHECK-DAG: {"label":"_M_allocate_and_copy"}
# CHECK-DAG: {"label":"_M_assign_aux"}
# CHECK-DAG: {"label":"_M_assign_dispatch"}
# CHECK-DAG: {"label":"_M_check_len"}
# CHECK-DAG: {"label":"_M_create_storage"
# CHECK-DAG: {"label":"_M_deallocate"}
# CHECK-DAG: {"label":"_M_erase_at_end"}
# CHECK-DAG: {"label":"_M_fill_assign"}
# CHECK-DAG: {"label":"_M_fill_initialize"}
# CHECK-DAG: {"label":"_M_fill_insert"}
# CHECK-DAG: {"label":"_M_get_Tp_allocator"}
# CHECK-DAG: {"label":"_M_impl"}
# CHECK-DAG: {"label":"_M_initialize_dispatch"}
# CHECK-DAG: {"label":"_M_insert_aux"}
# CHECK-DAG: {"label":"_M_insert_dispatch"}
# CHECK-DAG: {"label":"_M_range_check"}
# CHECK-DAG: {"label":"_M_range_initialize"}
# CHECK-DAG: {"label":"_M_range_insert"}
# CHECK-DAG: {"label":"_Vector_base"}
# CHECK-DAG: {"label":"assign"}
# CHECK-DAG: {"label":"at"}
# CHECK-DAG: {"label":"back"}
# CHECK-DAG: {"label":"begin"}
# CHECK-DAG: {"label":"capacity"}
# CHECK-DAG: {"label":"clear"}
# CHECK-DAG: {"label":"data"}
# CHECK-DAG: {"label":"empty"}
# CHECK-DAG: {"label":"end"}
# CHECK-DAG: {"label":"erase"}
# CHECK-DAG: {"label":"front"}
# CHECK-DAG: {"label":"get_allocator"}
# CHECK-DAG: {"label":"insert"}
# CHECK-DAG: {"label":"max_size"}
# CHECK-DAG: {"label":"operator="}
# CHECK-DAG: {"label":"operator[]"}
# CHECK-DAG: {"label":"pop_back"}
# CHECK-DAG: {"label":"push_back"}
# CHECK-DAG: {"label":"rbegin"}
# CHECK-DAG: {"label":"rend"}
# CHECK-DAG: {"label":"reserve"}
# CHECK-DAG: {"label":"resize"}
# CHECK-DAG: {"label":"size"}
# CHECK-DAG: {"label":"swap"}
# CHECK-DAG: {"label":"vector"}
# CHECK-DAG: {"label":"~_Vector_base"}
# CHECK-DAG: {"label":"~vector"}
# CHECK: ]}
Content-Length: 44

{"jsonrpc":"2.0","id":3,"method":"shutdown"}
5 changes: 3 additions & 2 deletions clang-tools-extra/test/clangd/formatting.test
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
Content-Length: 125

{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
# CHECK: Content-Length: 332
# CHECK: Content-Length: 424
# CHECK: {"jsonrpc":"2.0","id":0,"result":{"capabilities":{
# CHECK: "textDocumentSync": 1,
# CHECK: "documentFormattingProvider": true,
# CHECK: "documentRangeFormattingProvider": true,
# CHECK: "documentOnTypeFormattingProvider": {"firstTriggerCharacter":"}","moreTriggerCharacter":[]},
# CHECK: "codeActionProvider": true
# CHECK: "codeActionProvider": true,
# CHECK: "completionProvider": {"resolveProvider": false, "triggerCharacters": [".",">"]}
# CHECK: }}}
#
Content-Length: 193
Expand Down