Files
clice/docs/protocol.md
2024-09-11 15:44:14 +08:00

3.0 KiB

This file dictionary mainly describes the Language Server Protocol Specification. Every interface in the protocol is defined as corresponding struct.

For example, Position has the following definition in the protocol:

interface Position {
	line: uinteger;
	character: uinteger;
}

then the corresponding representation in C++ is:

struct Position {
    uinteger line;
    uinteger character;
};

We use template meta programming to reflect the protocol type so that the serialization and deserialization can be done automatically. The trick is only suitable for type which is aggregate and default constructible and does not have base classes. But there are many types in the protocol which are defined through inheritance. For example, DeclarationParams is defined as:

export interface DeclarationParams extends TextDocumentPositionParams,
	WorkDoneProgressParams, PartialResultParams {
}

We use Combine to resolve the problem. Combine is defined as:

template<typename... Ts>
struct Combine : Ts... {};

Then DeclarationParams can be defined as:

using DeclarationParams = Combine<
    TextDocumentPositionParams, 
    WorkDoneProgressParams, 
    PartialResultParams
>;

Compared to direct inheritance, this way allows us to get the base class types of DeclarationParams. If the interface also has data members, you should define a struct to hold the data members separately and add it to Combine list.

For example, we have ReferenceParams definition as follow:

export interface ReferenceParams extends TextDocumentPositionParams,
	WorkDoneProgressParams, PartialResultParams {
	context: ReferenceContext;
}

Then we can define ReferenceParams as:

struct ReferenceParamsBody {
    ReferenceContext context;
};

using ReferenceParams = Combine<
    TextDocumentPositionParams, 
    WorkDoneProgressParams, 
    PartialResultParams,
    ReferenceParamsBody
>;

For number enum in TypeScript, it can define as corresponding enum in C++ directly, for string enum, we define it as struct with static members.

export namespace MarkupKind {
	export const PlainText: 'plaintext' = 'plaintext';
	export const Markdown: 'markdown' = 'markdown';
}
export type MarkupKind = 'plaintext' | 'markdown';
struct MarkupKind {
    std::string_view m_value;

    constexpr MarkupKind(std::string_view value) : m_value(value) {}

    constexpr inline static std::string_view PlainText = "plaintext";
    constexpr inline static std::string_view Markdown = "markdown";
};