Skip to content

Commit 56a1ff5

Browse files
committed
chore: Support variants in LSP responses
Signed-off-by: Roberto Raggi <[email protected]>
1 parent f1d597f commit 56a1ff5

File tree

14 files changed

+1168
-2068
lines changed

14 files changed

+1168
-2068
lines changed

packages/cxx-gen-lsp/src/MetaModel.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ export function toCppType(type: Type): string {
199199
case "string":
200200
return "std::string";
201201
case "integer":
202-
return "int";
202+
return "long";
203203
case "uinteger":
204204
return "long";
205205
case "decimal":

packages/cxx-gen-lsp/src/gen_fwd_h.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,12 @@ class LSPObject {
3939
explicit LSPObject(json& repr): repr_(&repr) {}
4040
4141
[[nodiscard]] explicit operator bool() const { return repr_ != nullptr; }
42+
4243
[[nodiscard]] operator const json&() const { return *repr_; }
43-
[[nodiscard]] auto get() const -> json& { return *repr_; }
44+
[[nodiscard]] operator json&() { return *repr_; }
45+
46+
[[nodiscard]] auto get() const -> const json& { return *repr_; }
47+
[[nodiscard]] auto get() -> json& { return *repr_; }
4448
4549
protected:
4650
json* repr_{nullptr};
@@ -51,12 +55,17 @@ class LSPRequest : public LSPObject {
5155
using LSPObject::LSPObject;
5256
5357
[[nodiscard]] auto id() const -> std::optional<std::variant<long, std::string>>;
58+
auto id(std::optional<std::variant<long, std::string>>) -> LSPRequest&;
59+
5460
[[nodiscard]] auto method() const -> std::string;
5561
};
5662
5763
class LSPResponse : public LSPObject {
5864
public:
5965
using LSPObject::LSPObject;
66+
67+
[[nodiscard]] auto id() const -> std::optional<std::variant<long, std::string>>;
68+
auto id(std::optional<std::variant<long, std::string>>) -> LSPResponse&;
6069
};
6170
6271
template <typename T>

packages/cxx-gen-lsp/src/gen_requests_cc.ts

Lines changed: 51 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,14 @@
1919
// SOFTWARE.
2020

2121
import * as path from "node:path";
22-
import { Enumeration, isRequest, MetaModel, Structure, toCppType, Type, TypeAlias } from "./MetaModel.js";
22+
import { isRequest, MetaModel, Structure, toCppType, Type } from "./MetaModel.js";
2323
import { writeFileSync } from "node:fs";
2424
import { copyrightHeader } from "./copyrightHeader.js";
25+
import { TypeGenerator } from "./gen_types_cc.js";
2526

26-
class RequestGenerator {
27-
readonly structByName: Map<string, Structure>;
28-
readonly enumByName: Map<string, Enumeration>;
29-
readonly typeAliasByName: Map<string, TypeAlias>;
30-
readonly model: MetaModel;
31-
readonly outputDirectory: string;
32-
out: string = "";
33-
27+
class RequestGenerator extends TypeGenerator {
3428
constructor({ model, outputDirectory }: { model: MetaModel; outputDirectory: string }) {
35-
this.model = model;
36-
this.outputDirectory = outputDirectory;
37-
this.structByName = new Map(model.structures.map((s) => [s.name, s]));
38-
this.enumByName = new Map(model.enumerations.map((e) => [e.name, e]));
39-
this.typeAliasByName = new Map(model.typeAliases.map((t) => [t.name, t]));
29+
super({ model, outputDirectory });
4030
}
4131

4232
genTypes() {
@@ -50,9 +40,44 @@ class RequestGenerator {
5040
this.emit(` return id.get<long>();`);
5141
this.emit(`}`);
5242
this.emit();
43+
this.emit(`auto LSPRequest::id(std::optional<std::variant<long, std::string>> id)`);
44+
this.emit(` -> LSPRequest& {`);
45+
this.emit(` if (!id.has_value()) {`);
46+
this.emit(` repr_->erase("id");`);
47+
this.emit(` return *this;`);
48+
this.emit(` }`);
49+
this.emit(` if (std::holds_alternative<long>(*id)) {`);
50+
this.emit(` (*repr_)["id"] = std::get<long>(*id);`);
51+
this.emit(` } else {`);
52+
this.emit(` (*repr_)["id"] = std::get<std::string>(*id);`);
53+
this.emit(` }`);
54+
this.emit(` return *this;`);
55+
this.emit(`}`);
56+
this.emit();
5357
this.emit(`auto LSPRequest::method() const -> std::string {`);
5458
this.emit(` return repr_->at("method");`);
5559
this.emit(`}`);
60+
this.emit();
61+
this.emit(`auto LSPResponse::id() const -> std::optional<std::variant<long, std::string>> {`);
62+
this.emit(` if (!repr_->contains("id")) return std::nullopt;`);
63+
this.emit(` const auto& id = repr_->at("id");`);
64+
this.emit(` if (id.is_string()) return id.get<std::string>();`);
65+
this.emit(` return id.get<long>();`);
66+
this.emit(`}`);
67+
this.emit();
68+
this.emit(`auto LSPResponse::id(std::optional<std::variant<long, std::string>> id)`);
69+
this.emit(` -> LSPResponse& {`);
70+
this.emit(` if (!id.has_value()) {`);
71+
this.emit(` repr_->erase("id");`);
72+
this.emit(` return *this;`);
73+
this.emit(` }`);
74+
this.emit(` if (std::holds_alternative<long>(*id)) {`);
75+
this.emit(` (*repr_)["id"] = std::get<long>(*id);`);
76+
this.emit(` } else {`);
77+
this.emit(` (*repr_)["id"] = std::get<std::string>(*id);`);
78+
this.emit(` }`);
79+
this.emit(` return *this;`);
80+
this.emit(`}`);
5681

5782
const requestsAndNotifications = [...this.model.requests, ...this.model.notifications];
5883

@@ -65,12 +90,7 @@ class RequestGenerator {
6590
this.emit(`}`);
6691
this.emit();
6792
this.emit(`auto ${typeName}::id(std::variant<long, std::string> id) -> ${typeName}& {`);
68-
this.emit(` if (std::holds_alternative<long>(id)) {`);
69-
this.emit(` (*repr_)["id"] = std::get<long>(id);`);
70-
this.emit(` } else {`);
71-
this.emit(` (*repr_)["id"] = std::get<std::string>(id);`);
72-
this.emit(` }`);
73-
this.emit(` return *this;`);
93+
this.emit(` return static_cast<${typeName}&>(LSPRequest::id(std::move(id)));`);
7494
this.emit(`}`);
7595

7696
if (request.params) {
@@ -90,66 +110,26 @@ class RequestGenerator {
90110

91111
if (isRequest(request) && request.result) {
92112
const responseTypeName = typeName.replace(/Request$/, "Response");
93-
const resultTypeName = toCppType(request.result);
94113

95114
this.emit();
96-
this.emit(`auto ${responseTypeName}::id(long id) -> ${responseTypeName}& {`);
97-
this.emit(` (*repr_)["id"] = id;`);
98-
this.emit(` return *this;`);
99-
this.emit(`}`);
100-
this.emit();
101-
this.emit(`auto ${responseTypeName}::id(std::string id) -> ${responseTypeName}& {`);
102-
this.emit(` (*repr_)["id"] = std::move(id);`);
103-
this.emit(` return *this;`);
104-
this.emit(`}`);
105-
this.emit();
106-
this.emit(`auto ${responseTypeName}::result() const -> ${resultTypeName} {`);
107-
switch (request.result.kind) {
108-
case "base": {
109-
if (request.result.name === "null") {
110-
this.emit(` return nullptr;`);
111-
} else {
112-
this.emit(` return repr_->at("result").get<${toCppType(request.result)}>(); // base`);
113-
}
114-
break;
115-
}
116-
117-
case "reference": {
118-
if (this.structByName.has(request.result.name)) {
119-
this.emit(` if (!repr_->contains("result")) (*repr_)["result"] = nullptr;`);
120-
this.emit(` return ${resultTypeName}(repr_->at("result")); // reference`);
121-
} else {
122-
this.emit(` lsp_runtime_error("${responseTypeName}::result() - not implemented yet");`);
123-
}
124-
break;
125-
}
126-
127-
default: {
128-
this.emit(` lsp_runtime_error("${responseTypeName}::result() - not implemented yet");`);
129-
}
130-
} // swtch
131-
this.emit(`}`);
132-
this.emit();
133-
this.emit(`auto ${responseTypeName}::result(${resultTypeName} result) -> ${responseTypeName}& {`);
134-
switch (request.result.kind) {
135-
case "base":
136-
this.emit(` (*repr_)["result"] = std::move(result); // base`);
137-
break;
138-
default:
139-
this.emit(` lsp_runtime_error("${responseTypeName}::result() - not implemented yet");`);
140-
} // switch
141-
this.emit(` return *this;`);
115+
this.emit(`auto ${responseTypeName}::id(std::variant<long, std::string> id) -> ${responseTypeName}& {`);
116+
this.emit(` return static_cast<${responseTypeName}&>(LSPResponse::id(std::move(id)));`);
142117
this.emit(`}`);
118+
119+
// synthetic structure with result property
120+
121+
const structure: Structure = {
122+
name: responseTypeName,
123+
properties: [{ name: "result", type: request.result, optional: false }],
124+
};
125+
126+
this.generateGetters({ structure, properties: structure.properties });
143127
}
144128
});
145129

146130
this.end();
147131
}
148132

149-
emit(s: string = "") {
150-
this.out += `${s}\n`;
151-
}
152-
153133
getPropertyType({ type, optional }: { type: Type; optional?: boolean }): string {
154134
let propertyType = toCppType(type);
155135

packages/cxx-gen-lsp/src/gen_requests_h.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,10 @@ export function gen_requests_h({ model, outputDirectory }: { model: MetaModel; o
9191
emit(`class ${responseTypeName} final : public LSPResponse {`);
9292
emit(`public:`);
9393
emit(` using LSPResponse::LSPResponse;`);
94+
emit(` using LSPResponse::id;`);
95+
emit(` using Result = ${resultType};`);
9496
emit();
95-
emit(` [[nodiscard]] auto id() const -> std::variant<long, std::string>;`);
96-
emit(` auto id(long id) -> ${responseTypeName}&;`);
97-
emit(` auto id(std::string id) -> ${responseTypeName}&;`);
97+
emit(` auto id(std::variant<long, std::string> id) -> ${responseTypeName}&;`);
9898
emit();
9999
emit(` [[nodiscard]] auto result() const -> ${resultType};`);
100100
emit();
@@ -105,7 +105,7 @@ export function gen_requests_h({ model, outputDirectory }: { model: MetaModel; o
105105

106106
emit();
107107
emit(`template <typename Visitor>`);
108-
emit(`auto visit(Visitor&& visitor, const LSPRequest& request) -> void {`);
108+
emit(`auto visit(Visitor&& visitor, LSPRequest request) -> void {`);
109109
emit(`#define PROCESS_REQUEST_TYPE(NAME, METHOD) \\`);
110110
emit(` if (request.method() == METHOD) \\`);
111111
emit(` return visitor(static_cast<const NAME##Request&>(request));`);

packages/cxx-gen-lsp/src/gen_types_cc.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import {
3232
import { writeFileSync } from "node:fs";
3333
import { copyrightHeader } from "./copyrightHeader.js";
3434

35-
class TypeGenerator {
35+
export class TypeGenerator {
3636
readonly structByName: Map<string, Structure>;
3737
readonly enumByName: Map<string, Enumeration>;
3838
readonly typeAliasByName: Map<string, TypeAlias>;
@@ -202,7 +202,9 @@ class TypeGenerator {
202202

203203
switch (property.type.name) {
204204
case "null":
205-
throw new Error(`Unexpected null type`);
205+
this.emit(`assert(value.is_null());`);
206+
this.emit(`return nullptr;`);
207+
return true;
206208

207209
case "string":
208210
this.emit(`if (value.is_null()) value = "";`);

0 commit comments

Comments
 (0)