Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/cxx-gen-lsp/src/MetaModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ export function toCppType(type: Type): string {
case "string":
return "std::string";
case "integer":
return "int";
return "long";
case "uinteger":
return "long";
case "decimal":
Expand Down
11 changes: 10 additions & 1 deletion packages/cxx-gen-lsp/src/gen_fwd_h.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,12 @@ class LSPObject {
explicit LSPObject(json& repr): repr_(&repr) {}

[[nodiscard]] explicit operator bool() const { return repr_ != nullptr; }

[[nodiscard]] operator const json&() const { return *repr_; }
[[nodiscard]] auto get() const -> json& { return *repr_; }
[[nodiscard]] operator json&() { return *repr_; }

[[nodiscard]] auto get() const -> const json& { return *repr_; }
[[nodiscard]] auto get() -> json& { return *repr_; }

protected:
json* repr_{nullptr};
Expand All @@ -51,12 +55,17 @@ class LSPRequest : public LSPObject {
using LSPObject::LSPObject;

[[nodiscard]] auto id() const -> std::optional<std::variant<long, std::string>>;
auto id(std::optional<std::variant<long, std::string>>) -> LSPRequest&;

[[nodiscard]] auto method() const -> std::string;
};

class LSPResponse : public LSPObject {
public:
using LSPObject::LSPObject;

[[nodiscard]] auto id() const -> std::optional<std::variant<long, std::string>>;
auto id(std::optional<std::variant<long, std::string>>) -> LSPResponse&;
};

template <typename T>
Expand Down
122 changes: 51 additions & 71 deletions packages/cxx-gen-lsp/src/gen_requests_cc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,14 @@
// SOFTWARE.

import * as path from "node:path";
import { Enumeration, isRequest, MetaModel, Structure, toCppType, Type, TypeAlias } from "./MetaModel.js";
import { isRequest, MetaModel, Structure, toCppType, Type } from "./MetaModel.js";
import { writeFileSync } from "node:fs";
import { copyrightHeader } from "./copyrightHeader.js";
import { TypeGenerator } from "./gen_types_cc.js";

class RequestGenerator {
readonly structByName: Map<string, Structure>;
readonly enumByName: Map<string, Enumeration>;
readonly typeAliasByName: Map<string, TypeAlias>;
readonly model: MetaModel;
readonly outputDirectory: string;
out: string = "";

class RequestGenerator extends TypeGenerator {
constructor({ model, outputDirectory }: { model: MetaModel; outputDirectory: string }) {
this.model = model;
this.outputDirectory = outputDirectory;
this.structByName = new Map(model.structures.map((s) => [s.name, s]));
this.enumByName = new Map(model.enumerations.map((e) => [e.name, e]));
this.typeAliasByName = new Map(model.typeAliases.map((t) => [t.name, t]));
super({ model, outputDirectory });
}

genTypes() {
Expand All @@ -50,9 +40,44 @@ class RequestGenerator {
this.emit(` return id.get<long>();`);
this.emit(`}`);
this.emit();
this.emit(`auto LSPRequest::id(std::optional<std::variant<long, std::string>> id)`);
this.emit(` -> LSPRequest& {`);
this.emit(` if (!id.has_value()) {`);
this.emit(` repr_->erase("id");`);
this.emit(` return *this;`);
this.emit(` }`);
this.emit(` if (std::holds_alternative<long>(*id)) {`);
this.emit(` (*repr_)["id"] = std::get<long>(*id);`);
this.emit(` } else {`);
this.emit(` (*repr_)["id"] = std::get<std::string>(*id);`);
this.emit(` }`);
this.emit(` return *this;`);
this.emit(`}`);
this.emit();
this.emit(`auto LSPRequest::method() const -> std::string {`);
this.emit(` return repr_->at("method");`);
this.emit(`}`);
this.emit();
this.emit(`auto LSPResponse::id() const -> std::optional<std::variant<long, std::string>> {`);
this.emit(` if (!repr_->contains("id")) return std::nullopt;`);
this.emit(` const auto& id = repr_->at("id");`);
this.emit(` if (id.is_string()) return id.get<std::string>();`);
this.emit(` return id.get<long>();`);
this.emit(`}`);
this.emit();
this.emit(`auto LSPResponse::id(std::optional<std::variant<long, std::string>> id)`);
this.emit(` -> LSPResponse& {`);
this.emit(` if (!id.has_value()) {`);
this.emit(` repr_->erase("id");`);
this.emit(` return *this;`);
this.emit(` }`);
this.emit(` if (std::holds_alternative<long>(*id)) {`);
this.emit(` (*repr_)["id"] = std::get<long>(*id);`);
this.emit(` } else {`);
this.emit(` (*repr_)["id"] = std::get<std::string>(*id);`);
this.emit(` }`);
this.emit(` return *this;`);
this.emit(`}`);

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

Expand All @@ -65,12 +90,7 @@ class RequestGenerator {
this.emit(`}`);
this.emit();
this.emit(`auto ${typeName}::id(std::variant<long, std::string> id) -> ${typeName}& {`);
this.emit(` if (std::holds_alternative<long>(id)) {`);
this.emit(` (*repr_)["id"] = std::get<long>(id);`);
this.emit(` } else {`);
this.emit(` (*repr_)["id"] = std::get<std::string>(id);`);
this.emit(` }`);
this.emit(` return *this;`);
this.emit(` return static_cast<${typeName}&>(LSPRequest::id(std::move(id)));`);
this.emit(`}`);

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

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

this.emit();
this.emit(`auto ${responseTypeName}::id(long id) -> ${responseTypeName}& {`);
this.emit(` (*repr_)["id"] = id;`);
this.emit(` return *this;`);
this.emit(`}`);
this.emit();
this.emit(`auto ${responseTypeName}::id(std::string id) -> ${responseTypeName}& {`);
this.emit(` (*repr_)["id"] = std::move(id);`);
this.emit(` return *this;`);
this.emit(`}`);
this.emit();
this.emit(`auto ${responseTypeName}::result() const -> ${resultTypeName} {`);
switch (request.result.kind) {
case "base": {
if (request.result.name === "null") {
this.emit(` return nullptr;`);
} else {
this.emit(` return repr_->at("result").get<${toCppType(request.result)}>(); // base`);
}
break;
}

case "reference": {
if (this.structByName.has(request.result.name)) {
this.emit(` if (!repr_->contains("result")) (*repr_)["result"] = nullptr;`);
this.emit(` return ${resultTypeName}(repr_->at("result")); // reference`);
} else {
this.emit(` lsp_runtime_error("${responseTypeName}::result() - not implemented yet");`);
}
break;
}

default: {
this.emit(` lsp_runtime_error("${responseTypeName}::result() - not implemented yet");`);
}
} // swtch
this.emit(`}`);
this.emit();
this.emit(`auto ${responseTypeName}::result(${resultTypeName} result) -> ${responseTypeName}& {`);
switch (request.result.kind) {
case "base":
this.emit(` (*repr_)["result"] = std::move(result); // base`);
break;
default:
this.emit(` lsp_runtime_error("${responseTypeName}::result() - not implemented yet");`);
} // switch
this.emit(` return *this;`);
this.emit(`auto ${responseTypeName}::id(std::variant<long, std::string> id) -> ${responseTypeName}& {`);
this.emit(` return static_cast<${responseTypeName}&>(LSPResponse::id(std::move(id)));`);
this.emit(`}`);

// synthetic structure with result property

const structure: Structure = {
name: responseTypeName,
properties: [{ name: "result", type: request.result, optional: false }],
};

this.generateGetters({ structure, properties: structure.properties });
}
});

this.end();
}

emit(s: string = "") {
this.out += `${s}\n`;
}

getPropertyType({ type, optional }: { type: Type; optional?: boolean }): string {
let propertyType = toCppType(type);

Expand Down
8 changes: 4 additions & 4 deletions packages/cxx-gen-lsp/src/gen_requests_h.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,10 @@ export function gen_requests_h({ model, outputDirectory }: { model: MetaModel; o
emit(`class ${responseTypeName} final : public LSPResponse {`);
emit(`public:`);
emit(` using LSPResponse::LSPResponse;`);
emit(` using LSPResponse::id;`);
emit(` using Result = ${resultType};`);
emit();
emit(` [[nodiscard]] auto id() const -> std::variant<long, std::string>;`);
emit(` auto id(long id) -> ${responseTypeName}&;`);
emit(` auto id(std::string id) -> ${responseTypeName}&;`);
emit(` auto id(std::variant<long, std::string> id) -> ${responseTypeName}&;`);
emit();
emit(` [[nodiscard]] auto result() const -> ${resultType};`);
emit();
Expand All @@ -105,7 +105,7 @@ export function gen_requests_h({ model, outputDirectory }: { model: MetaModel; o

emit();
emit(`template <typename Visitor>`);
emit(`auto visit(Visitor&& visitor, const LSPRequest& request) -> void {`);
emit(`auto visit(Visitor&& visitor, LSPRequest request) -> void {`);
emit(`#define PROCESS_REQUEST_TYPE(NAME, METHOD) \\`);
emit(` if (request.method() == METHOD) \\`);
emit(` return visitor(static_cast<const NAME##Request&>(request));`);
Expand Down
6 changes: 4 additions & 2 deletions packages/cxx-gen-lsp/src/gen_types_cc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import {
import { writeFileSync } from "node:fs";
import { copyrightHeader } from "./copyrightHeader.js";

class TypeGenerator {
export class TypeGenerator {
readonly structByName: Map<string, Structure>;
readonly enumByName: Map<string, Enumeration>;
readonly typeAliasByName: Map<string, TypeAlias>;
Expand Down Expand Up @@ -202,7 +202,9 @@ class TypeGenerator {

switch (property.type.name) {
case "null":
throw new Error(`Unexpected null type`);
this.emit(`assert(value.is_null());`);
this.emit(`return nullptr;`);
return true;

case "string":
this.emit(`if (value.is_null()) value = "";`);
Expand Down
Loading