Skip to content

Commit 70cec06

Browse files
committed
Add initial responses to member completion requests in the LSP server
1 parent a2c00e1 commit 70cec06

File tree

10 files changed

+149
-12
lines changed

10 files changed

+149
-12
lines changed

src/frontend/cxx/frontend.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,15 @@
2626
#include <cxx/lexer.h>
2727
#include <cxx/lsp/lsp_server.h>
2828
#include <cxx/macos_toolchain.h>
29+
#include <cxx/name_printer.h>
2930
#include <cxx/preprocessor.h>
3031
#include <cxx/private/path.h>
3132
#include <cxx/scope.h>
3233
#include <cxx/symbol_printer.h>
3334
#include <cxx/symbols.h>
3435
#include <cxx/translation_unit.h>
36+
#include <cxx/type_printer.h>
37+
#include <cxx/types.h>
3538
#include <cxx/wasm32_wasi_toolchain.h>
3639
#include <cxx/windows_toolchain.h>
3740

src/frontend/cxx/verify_diagnostics_client.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ auto VerifyDiagnosticsClient::hasErrors() const -> bool {
7676
}
7777

7878
void VerifyDiagnosticsClient::report(const Diagnostic& diagnostic) {
79+
if (!shouldReportErrors()) return;
80+
7981
if (verify_) {
8082
reportedDiagnostics_.push_back(diagnostic);
8183
return;

src/lsp/cxx/lsp/cxx_document.cc

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,28 @@
2323
#include <cxx/ast.h>
2424
#include <cxx/control.h>
2525
#include <cxx/gcc_linux_toolchain.h>
26+
#include <cxx/lsp/enums.h>
2627
#include <cxx/lsp/types.h>
2728
#include <cxx/macos_toolchain.h>
29+
#include <cxx/name_printer.h>
2830
#include <cxx/preprocessor.h>
2931
#include <cxx/private/path.h>
3032
#include <cxx/scope.h>
3133
#include <cxx/symbol_printer.h>
3234
#include <cxx/symbols.h>
3335
#include <cxx/translation_unit.h>
36+
#include <cxx/type_printer.h>
37+
#include <cxx/types.h>
3438
#include <cxx/wasm32_wasi_toolchain.h>
3539
#include <cxx/windows_toolchain.h>
3640

3741
#ifndef CXX_NO_THREADS
3842
#include <atomic>
3943
#endif
4044

45+
#include <format>
46+
#include <iostream>
47+
4148
namespace cxx::lsp {
4249

4350
namespace {
@@ -74,6 +81,7 @@ struct CxxDocument::Private {
7481
Diagnostics diagnosticsClient;
7582
TranslationUnit unit{&diagnosticsClient};
7683
std::shared_ptr<Toolchain> toolchain;
84+
Vector<CompletionItem> completionItems;
7785

7886
#ifndef CXX_NO_THREADS
7987
std::atomic<bool> cancelled{false};
@@ -202,10 +210,20 @@ void CxxDocument::cancel() {
202210

203211
auto CxxDocument::fileName() const -> const std::string& { return d->fileName; }
204212

205-
void CxxDocument::requestCodeCompletion(std::uint32_t line,
206-
std::uint32_t column) {
213+
void CxxDocument::codeCompletionAt(std::string source, std::uint32_t line,
214+
std::uint32_t column,
215+
Vector<CompletionItem> completionItems) {
216+
std::swap(d->completionItems, completionItems);
217+
207218
auto& unit = d->unit;
219+
220+
(void)unit.blockErrors(true);
221+
208222
unit.preprocessor()->requestCodeCompletionAt(line, column);
223+
224+
parse(std::move(source));
225+
226+
std::swap(d->completionItems, completionItems);
209227
}
210228

211229
void CxxDocument::parse(std::string source) {
@@ -227,13 +245,37 @@ void CxxDocument::parse(std::string source) {
227245

228246
unit.endPreprocessing();
229247

248+
auto stopParsingPredicate = [this] { return isCancelled(); };
249+
250+
auto complete = [this](const CodeCompletionContext& context) {
251+
if (auto memberCompletionContext =
252+
std::get_if<MemberCompletionContext>(&context)) {
253+
// simple member completion
254+
auto objectType = memberCompletionContext->objectType;
255+
256+
if (auto pointerType = type_cast<PointerType>(objectType)) {
257+
objectType = type_cast<ClassType>(pointerType->elementType());
258+
}
259+
260+
if (auto classType = type_cast<ClassType>(objectType)) {
261+
auto classSymbol = classType->symbol();
262+
for (auto member : classSymbol->members()) {
263+
if (!member->name()) continue;
264+
auto item = d->completionItems.emplace_back();
265+
item.label(to_string(member->name()));
266+
}
267+
}
268+
}
269+
};
270+
230271
unit.parse(ParserConfiguration{
231272
.checkTypes = cli.opt_fcheck,
232273
.fuzzyTemplateResolution = true,
233274
.staticAssert = cli.opt_fstatic_assert || cli.opt_fcheck,
234275
.reflect = !cli.opt_fno_reflect,
235276
.templates = cli.opt_ftemplates,
236-
.stopParsingPredicate = [this] { return isCancelled(); },
277+
.stopParsingPredicate = stopParsingPredicate,
278+
.complete = complete,
237279
});
238280
}
239281

src/lsp/cxx/lsp/cxx_document.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,11 @@ class CxxDocument {
3939

4040
[[nodiscard]] auto fileName() const -> const std::string&;
4141

42-
void requestCodeCompletion(std::uint32_t line, std::uint32_t column);
43-
4442
void parse(std::string source);
4543

44+
void codeCompletionAt(std::string source, std::uint32_t line,
45+
std::uint32_t column, Vector<CompletionItem> result);
46+
4647
[[nodiscard]] auto version() const -> long;
4748
[[nodiscard]] auto diagnostics() const -> Vector<Diagnostic>;
4849

src/lsp/cxx/lsp/lsp_server.cc

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -493,22 +493,24 @@ void Server::operator()(CompletionRequest request) {
493493
auto column = request.params().position().character();
494494

495495
const auto& text = documentContents_.at(uri);
496-
auto value = text.value;
496+
auto source = text.value;
497497

498498
run([=, this, fileName = pathFromUri(uri)] {
499499
withUnsafeJson([&](json storage) {
500+
CompletionResponse response(storage);
501+
response.id(request.id());
502+
500503
// the version is not relevant for code completion requests as we don't
501504
// need to store the document in the cache.
502505
auto cxxDocument = std::make_shared<CxxDocument>(cli, std::move(fileName),
503506
/*version=*/0);
504507

508+
auto completionItems = response.result<Vector<CompletionItem>>();
509+
505510
// cxx expects 1-based line and column numbers
506-
cxxDocument->requestCodeCompletion(line + 1, column + 1);
507-
cxxDocument->parse(std::move(value));
511+
cxxDocument->codeCompletionAt(std::move(source), line + 1, column + 1,
512+
completionItems);
508513

509-
CompletionResponse response(storage);
510-
response.id(request.id());
511-
auto completionItems = response.result<Vector<CompletionItem>>();
512514
sendToClient(response);
513515
});
514516
});

src/parser/cxx/diagnostics_client.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ class DiagnosticsClient {
4848
[[nodiscard]] auto fatalErrors() const -> bool { return fatalErrors_; }
4949
void setFatalErrors(bool fatalErrors) { fatalErrors_ = fatalErrors; }
5050

51+
[[nodiscard]] auto shouldReportErrors() const -> bool {
52+
return !blockErrors_;
53+
}
54+
5155
auto blockErrors(bool blockErrors = true) -> bool {
5256
std::swap(blockErrors_, blockErrors);
5357
return blockErrors;

src/parser/cxx/parser.cc

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,15 @@
3838
#include <algorithm>
3939
#include <cstring>
4040
#include <format>
41+
#include <iostream>
4142
#include <ranges>
43+
#include <unordered_set>
44+
45+
#include "cxx/cxx_fwd.h"
46+
#include "cxx/parser_fwd.h"
47+
#include "cxx/source_location.h"
48+
#include "cxx/symbols_fwd.h"
49+
#include "cxx/token_fwd.h"
4250

4351
namespace cxx {
4452

@@ -1271,6 +1279,20 @@ void Parser::parse_skip_declaration(bool& skipping) {
12711279
skipping = true;
12721280
}
12731281

1282+
auto Parser::parse_completion(SourceLocation& loc) -> bool {
1283+
// if already reported a completion, return false
1284+
if (didAcceptCompletionToken_) return false;
1285+
1286+
// if there is no completer, return false
1287+
if (!config_.complete) return false;
1288+
1289+
if (!match(TokenKind::T_CODE_COMPLETION, loc)) return false;
1290+
1291+
didAcceptCompletionToken_ = true;
1292+
1293+
return true;
1294+
}
1295+
12741296
auto Parser::parse_primary_expression(ExpressionAST*& yyast,
12751297
const ExprContext& ctx) -> bool {
12761298
UnqualifiedIdAST* name = nullptr;
@@ -2434,6 +2456,22 @@ auto Parser::parse_member_expression(ExpressionAST*& yyast) -> bool {
24342456

24352457
ast->isTemplateIntroduced = match(TokenKind::T_TEMPLATE, ast->templateLoc);
24362458

2459+
const Type* objectType = nullptr;
2460+
2461+
if (ast->baseExpression) {
2462+
// test if the base expression has a type
2463+
objectType = ast->baseExpression->type;
2464+
}
2465+
2466+
if (SourceLocation completionLoc;
2467+
objectType && parse_completion(completionLoc)) {
2468+
// trigger the completion
2469+
config_.complete(MemberCompletionContext{
2470+
.objectType = objectType,
2471+
.accessOp = ast->accessOp,
2472+
});
2473+
}
2474+
24372475
if (!parse_unqualified_id(ast->unqualifiedId, ast->nestedNameSpecifier,
24382476
ast->isTemplateIntroduced,
24392477
/*inRequiresClause*/ false))

src/parser/cxx/parser.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ class Parser final {
108108
static auto prec(TokenKind tk) -> Prec;
109109

110110
[[nodiscard]] auto shouldStopParsing() const -> bool {
111+
if (didAcceptCompletionToken_) return true;
111112
if (config_.stopParsingPredicate) return config_.stopParsingPredicate();
112113
return false;
113114
}
@@ -141,6 +142,8 @@ class Parser final {
141142

142143
void parse_translation_unit(UnitAST*& yyast);
143144

145+
[[nodiscard]] auto parse_completion(SourceLocation& loc) -> bool;
146+
144147
[[nodiscard]] auto parse_id(const Identifier* id, SourceLocation& loc)
145148
-> bool;
146149
[[nodiscard]] auto parse_nospace() -> bool;
@@ -871,7 +874,7 @@ class Parser final {
871874
std::uint32_t cursor_ = 0;
872875
int templateParameterDepth_ = -1;
873876
int templateParameterCount_ = 0;
874-
877+
bool didAcceptCompletionToken_ = false;
875878
std::vector<FunctionDefinitionAST*> pendingFunctionDefinitions_;
876879

877880
template <typename T>

src/parser/cxx/parser_fwd.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,37 @@
2121

2222
#pragma once
2323

24+
#include <cxx/symbols_fwd.h>
25+
#include <cxx/token_fwd.h>
26+
#include <cxx/types_fwd.h>
27+
2428
#include <functional>
29+
#include <variant>
2530

2631
namespace cxx {
2732

2833
class Parser;
2934

35+
struct UnqualifiedCompletionContext {
36+
Scope* scope = nullptr;
37+
};
38+
39+
struct MemberCompletionContext {
40+
const Type* objectType = nullptr;
41+
TokenKind accessOp = TokenKind::T_DOT;
42+
};
43+
44+
using CodeCompletionContext =
45+
std::variant<UnqualifiedCompletionContext, MemberCompletionContext>;
46+
3047
struct ParserConfiguration {
3148
bool checkTypes = false;
3249
bool fuzzyTemplateResolution = false;
3350
bool staticAssert = false;
3451
bool reflect = true;
3552
bool templates = false;
3653
std::function<bool()> stopParsingPredicate;
54+
std::function<void(const CodeCompletionContext&)> complete;
3755
};
3856

3957
} // namespace cxx
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# RUN: %cxx -lsp-test < %s | %filecheck %s
2+
3+
{ "method": "initialize", "id": 0 }
4+
5+
# CHECK: "id": 0
6+
7+
{ "method": "textDocument/didOpen", "id": 1, "params": { "textDocument": { "uri": "test:///source.cc", "version": 0, "text": "struct P { int x, y; }; void ff() { P p; p.\n\n\n}" } } }
8+
9+
{ "method": "$/setTrace", "id": 2, "params": { "value": "verbose" } }
10+
11+
{ "method": "textDocument/completion", "id": 3, "params": { "textDocument": { "uri": "test:///source.cc" }, "position": { "line": 2, "character": 1 } } }
12+
13+
# CHECK: "message": "Did receive CompletionRequest"
14+
# CHECK: "id": 3
15+
# CHECK: "result":
16+
# CHECK: "label": "x"
17+
# CHECK: "label": "y"
18+
19+
{ "method": "shutdown", "id": 4 }
20+
21+
{ "method": "exit" }
22+
23+
24+

0 commit comments

Comments
 (0)