Files
eepp/src/tools/ecode/plugins/lsp/lspprotocol.hpp
Martín Lucas Golini bd96005bf8 ecode: Implemented LSP document symbol tree with search, integrated with the breadcrumb.
eepp: Added a new flag to UIWindow to create ephemeral windows (windows that close on focus loss).
2024-08-26 01:09:50 -03:00

629 lines
15 KiB
C++

#ifndef ECODE_LSPCLIENTPROTOCOL_HPP
#define ECODE_LSPCLIENTPROTOCOL_HPP
#include <eepp/ui/doc/textdocument.hpp>
#include <nlohmann/json.hpp>
#include <string>
using namespace EE;
using namespace EE::UI::Doc;
using namespace EE::Network;
// The LSP protocol will be consumed by any plugin, and it's not exclusive to the LSP plugin
// The protocol should be taken as a reference on how to implement the comunication between plugins
namespace ecode {
enum class LSPErrorCode {
// Defined by JSON RPC
ParseError = -32700,
InvalidRequest = -32600,
MethodNotFound = -32601,
InvalidParams = -32602,
InternalError = -32603,
serverErrorStart = -32099,
serverErrorEnd = -32000,
ServerNotInitialized = -32002,
UnknownErrorCode = -32001,
// Defined by the protocol.
RequestCancelled = -32800,
ContentModified = -32801
};
struct LSPPosition {
URI uri;
TextPosition pos;
};
struct LSPLocation {
URI uri;
TextRange range;
};
struct LSPWorkspaceFolder {
URI uri;
std::string name;
bool isEmpty() { return uri.empty(); }
};
enum class LSPDocumentSyncKind { None = 0, Full = 1, Incremental = 2 };
struct LSPSaveOptions {
bool includeText = false;
};
struct LSPTextDocumentSyncOptions {
LSPDocumentSyncKind change = LSPDocumentSyncKind::None;
LSPSaveOptions save;
};
struct LSPCompletionOptions {
bool provider = false;
bool resolveProvider = false;
std::vector<char> triggerCharacters;
};
struct LSPSignatureHelpOptions {
bool provider = false;
std::vector<char> triggerCharacters;
};
struct LSPDocumentOnTypeFormattingOptions : public LSPSignatureHelpOptions {};
struct SemanticTokenTypes {
enum : Uint32 {
Unknown = String::hash( "unknown" ),
Namespace = String::hash( "namespace" ),
Type = String::hash( "type" ),
Class = String::hash( "class" ),
Enum = String::hash( "enum" ),
Interface = String::hash( "interface" ),
Struct = String::hash( "struct" ),
TypeParameter = String::hash( "typeParameter" ),
Parameter = String::hash( "parameter" ),
Variable = String::hash( "variable" ),
Property = String::hash( "property" ),
EnumMember = String::hash( "enumMember" ),
Event = String::hash( "event" ),
Function = String::hash( "function" ),
Method = String::hash( "method" ),
Macro = String::hash( "macro" ),
Keyword = String::hash( "keyword" ),
Modifier = String::hash( "modifier" ),
Comment = String::hash( "comment" ),
Str = String::hash( "string" ),
Number = String::hash( "number" ),
Regexp = String::hash( "regexp" ),
Operator = String::hash( "operator" ),
Decorator = String::hash( "decorator" ),
Member = String::hash( "member" ),
};
};
// Ref:
// https://microsoft.github.io/language-server-protocol/specification#textDocument_semanticTokens
struct SemanticTokensLegend {
/**
* The token types a server uses.
*/
std::vector<std::string> tokenTypes;
/**
* The token modifiers a server uses.
*/
std::vector<std::string> tokenModifiers;
};
struct LSPSemanticTokensOptions {
bool full = false;
bool fullDelta = false;
bool range = false;
SemanticTokensLegend legend;
};
struct LSPWorkspaceFoldersServerCapabilities {
bool supported = false;
bool changeNotifications = false;
};
struct LSPCodeLensOptions {
bool supported = false;
bool resolveProvider = false;
};
struct LSPServerCapabilities {
bool ready = false;
std::vector<std::string> languages;
LSPTextDocumentSyncOptions textDocumentSync;
bool hoverProvider = false;
LSPCompletionOptions completionProvider;
LSPSignatureHelpOptions signatureHelpProvider;
bool astProvider = false;
bool definitionProvider = false;
// official extension as of 3.14.0
bool declarationProvider = false;
bool typeDefinitionProvider = false;
bool referencesProvider = false;
bool implementationProvider = false;
bool documentSymbolProvider = false;
bool documentHighlightProvider = false;
bool documentFormattingProvider = false;
bool documentRangeFormattingProvider = false;
bool foldingRangeProvider = false;
LSPCodeLensOptions codeLensProvider;
bool workspaceSymbolProvider = false;
LSPDocumentOnTypeFormattingOptions documentOnTypeFormattingProvider;
bool renameProvider = false;
// CodeActionOptions not useful/considered at present
bool codeActionProvider = false;
LSPSemanticTokensOptions semanticTokenProvider;
// workspace caps flattened
// (other parts not useful/considered at present)
LSPWorkspaceFoldersServerCapabilities workspaceFolders;
bool selectionRangeProvider = false;
bool executeCommandProvider = false;
};
enum class LSPMessageType { Error = 1, Warning = 2, Info = 3, Log = 4 };
struct LSPMessageActionItem {
std::string title;
};
struct LSPShowMessageParams {
LSPMessageType type{ LSPMessageType::Log };
std::string message;
std::vector<LSPMessageActionItem> actions;
bool allowCopy{ false };
};
struct LSPTextDocumentContentChangeEvent {
TextRange range;
std::string text;
};
enum class LSPDiagnosticSeverity {
Unknown = 0,
Error = 1,
Warning = 2,
Information = 3,
Hint = 4,
};
using LSPTextEdit = LSPTextDocumentContentChangeEvent;
struct LSPVersionedTextDocumentIdentifier {
URI uri;
int version = -1;
};
struct LSPTextDocumentEdit {
LSPVersionedTextDocumentIdentifier textDocument;
std::vector<LSPTextEdit> edits;
};
struct LSPWorkspaceEdit {
// supported part for now
std::map<URI, std::vector<LSPTextEdit>> changes;
std::vector<LSPTextDocumentEdit> documentChanges;
};
struct LSPDiagnosticRelatedInformation {
// empty url / invalid range when absent
LSPLocation location;
std::string message;
};
struct LSPDiagnosticsCodeAction {
std::string title;
std::string kind;
bool isPreferred{ false };
LSPWorkspaceEdit edit;
};
struct LSPDiagnostic {
TextRange range;
LSPDiagnosticSeverity severity{ LSPDiagnosticSeverity::Unknown };
std::string code;
std::string source;
std::string message;
std::vector<LSPDiagnosticRelatedInformation> relatedInformation;
std::vector<LSPDiagnosticsCodeAction> codeActions;
nlohmann::json data;
};
struct LSPPublishDiagnosticsParams {
URI uri;
std::vector<LSPDiagnostic> diagnostics;
};
struct LSPCommand {
std::string title;
std::string command;
// pretty opaque
nlohmann::json arguments;
};
struct LSPCodeAction {
std::string title;
std::string kind;
std::vector<LSPDiagnostic> diagnostics;
LSPWorkspaceEdit edit;
LSPCommand command;
bool isPreferred{ false };
};
struct LSPCodeLens {
TextRange range;
LSPCommand command;
nlohmann::json data;
};
enum class LSPWorkDoneProgressKind { Begin, Report, End };
struct LSPWorkDoneProgressValue {
LSPWorkDoneProgressKind kind;
std::string title;
std::string message;
bool cancellable;
unsigned percentage;
};
template <typename T> struct LSPProgressParams {
// number or string
nlohmann::json token;
T value;
};
enum class LSPSymbolKind {
File = 1,
Module = 2,
Namespace = 3,
Package = 4,
Class = 5,
Method = 6,
Property = 7,
Field = 8,
Constructor = 9,
Enum = 10,
Interface = 11,
Function = 12,
Variable = 13,
Constant = 14,
String = 15,
Number = 16,
Boolean = 17,
Array = 18,
Object = 19,
Key = 20,
Null = 21,
EnumMember = 22,
Struct = 23,
Event = 24,
Operator = 25,
TypeParameter = 26,
};
class LSPSymbolKindHelper {
public:
// TODO: Complete this list
static std::string toIconString( const LSPSymbolKind& kind ) {
switch ( kind ) {
case LSPSymbolKind::Method:
return "symbol-method";
case LSPSymbolKind::Function:
return "symbol-function";
case LSPSymbolKind::Constructor:
return "symbol-constructor";
case LSPSymbolKind::Field:
return "symbol-field";
case LSPSymbolKind::Variable:
return "symbol-variable";
case LSPSymbolKind::Class:
return "symbol-class";
case LSPSymbolKind::Interface:
return "symbol-interface";
case LSPSymbolKind::Module:
return "symbol-module";
case LSPSymbolKind::Property:
return "symbol-property";
case LSPSymbolKind::Enum:
return "symbol-enum";
case LSPSymbolKind::File:
return "symbol-file";
case LSPSymbolKind::EnumMember:
return "symbol-enum-member";
case LSPSymbolKind::Constant:
return "symbol-constant";
case LSPSymbolKind::Struct:
return "symbol-struct";
case LSPSymbolKind::Event:
return "symbol-event";
case LSPSymbolKind::Operator:
return "symbol-operator";
case LSPSymbolKind::TypeParameter:
return "symbol-type-parameter";
case LSPSymbolKind::Namespace:
return "symbol-namespace";
case LSPSymbolKind::Package:
return "symbol-package";
case LSPSymbolKind::String:
return "symbol-string";
case LSPSymbolKind::Number:
return "symbol-number";
case LSPSymbolKind::Boolean:
return "symbol-boolean";
case LSPSymbolKind::Array:
return "symbol-array";
case LSPSymbolKind::Object:
return "symbol-object";
case LSPSymbolKind::Key:
return "symbol-key";
case LSPSymbolKind::Null:
return "symbol-null";
}
return "symbol-text";
}
};
struct LSPSymbolInformation {
LSPSymbolInformation() = default;
LSPSymbolInformation( const std::string& _name, LSPSymbolKind _kind, TextRange _range,
const std::string& _detail ) :
name( _name ), detail( _detail ), kind( _kind ), range( _range ) {}
std::string name;
std::string detail;
LSPSymbolKind kind{ LSPSymbolKind::File };
URI url;
TextRange range;
TextRange selectionRange;
double score = 0.0;
std::vector<LSPSymbolInformation> children;
LSPSymbolInformation* parent{ nullptr };
void updateParentsRefs() {
for ( auto& child : children ) {
child.parent = this;
child.updateParentsRefs();
}
}
};
using LSPSymbolInformationList = std::vector<LSPSymbolInformation>;
struct LSPSymbolInformationListHelper {
static bool isFlat( const LSPSymbolInformationList& list ) {
for ( const auto& l : list )
if ( !l.children.empty() )
return false;
return true;
}
static LSPSymbolInformationList flatten( const LSPSymbolInformationList& list ) {
LSPSymbolInformationList newList;
for ( const auto& l : list ) {
if ( l.children.empty() ) {
newList.push_back( l );
} else {
auto nl = l;
nl.children.clear();
newList.push_back( nl );
auto clist = flatten( l.children );
for ( const auto& cl : clist )
newList.push_back( cl );
}
}
return newList;
}
};
using LSPWorkDoneProgressParams = LSPProgressParams<LSPWorkDoneProgressValue>;
struct LSPResponseError {
LSPErrorCode code{};
std::string message;
nlohmann::json data;
};
enum class LSPMarkupKind { None = 0, PlainText = 1, MarkDown = 2 };
struct LSPMarkupContent {
LSPMarkupKind kind = LSPMarkupKind::None;
std::string value;
};
struct LSPHover {
std::vector<LSPMarkupContent> contents;
TextRange range;
};
enum class LSPCompletionItemKind {
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,
Folder = 19,
EnumMember = 20,
Constant = 21,
Struct = 22,
Event = 23,
Operator = 24,
TypeParameter = 25,
};
class LSPCompletionItemHelper {
public:
static std::string toIconString( const LSPCompletionItemKind& kind ) {
switch ( kind ) {
case LSPCompletionItemKind::Text:
return "symbol-text";
case LSPCompletionItemKind::Method:
return "symbol-method";
case LSPCompletionItemKind::Function:
return "symbol-function";
case LSPCompletionItemKind::Constructor:
return "symbol-constructor";
case LSPCompletionItemKind::Field:
return "symbol-field";
case LSPCompletionItemKind::Variable:
return "symbol-variable";
case LSPCompletionItemKind::Class:
return "symbol-class";
case LSPCompletionItemKind::Interface:
return "symbol-interface";
case LSPCompletionItemKind::Module:
return "symbol-module";
case LSPCompletionItemKind::Property:
return "symbol-property";
case LSPCompletionItemKind::Unit:
return "symbol-unit";
case LSPCompletionItemKind::Value:
return "symbol-value";
case LSPCompletionItemKind::Enum:
return "symbol-enum";
case LSPCompletionItemKind::Keyword:
return "symbol-keyword";
case LSPCompletionItemKind::Snippet:
return "symbol-snippet";
case LSPCompletionItemKind::Color:
return "symbol-color";
case LSPCompletionItemKind::File:
return "symbol-file";
case LSPCompletionItemKind::Reference:
return "symbol-reference";
case LSPCompletionItemKind::Folder:
return "symbol-folder";
case LSPCompletionItemKind::EnumMember:
return "symbol-enum-member";
case LSPCompletionItemKind::Constant:
return "symbol-constant";
case LSPCompletionItemKind::Struct:
return "symbol-struct";
case LSPCompletionItemKind::Event:
return "symbol-event";
case LSPCompletionItemKind::Operator:
return "symbol-operator";
case LSPCompletionItemKind::TypeParameter:
return "symbol-type-parameter";
}
return "symbol-text";
}
};
struct LSPCompletionItem {
std::string label;
LSPCompletionItemKind kind;
std::string detail;
LSPMarkupContent documentation;
std::string sortText;
std::string insertText;
std::string filterText;
LSPTextEdit textEdit;
std::vector<LSPTextEdit> additionalTextEdits;
};
struct LSPCompletionList {
bool isIncomplete{ false };
std::vector<LSPCompletionItem> items;
};
struct LSPSelectionRange {
TextRange range;
std::shared_ptr<LSPSelectionRange> parent;
};
struct LSPParameterInformation {
// offsets into overall signature label
// (-1 if invalid)
int start{ -1 };
int end{ -1 };
};
struct LSPSignatureInformation {
std::string label;
LSPMarkupContent documentation;
std::vector<LSPParameterInformation> parameters;
};
struct LSPSignatureHelp {
std::vector<LSPSignatureInformation> signatures;
int activeSignature{ 0 };
int activeParameter{ 0 };
};
struct LSPConverter {
static TextPosition parsePosition( const nlohmann::json& data ) {
if ( data.contains( "line" ) && data.contains( "character" ) )
return { data["line"].get<Int64>(), data["character"].get<Int64>() };
return {};
}
static TextRange parseRange( const nlohmann::json& range ) {
if ( !range.contains( "start" ) || !range.contains( "end" ) )
return {};
auto startpos = parsePosition( range["start"] );
auto endpos = parsePosition( range["end"] );
return { startpos, endpos };
}
};
struct LSPShowDocumentParams {
URI uri;
bool external{ true };
bool takeFocus{ false };
TextRange selection;
};
struct LSPApplyWorkspaceEditParams {
std::string label;
LSPWorkspaceEdit edit;
};
struct LSPApplyWorkspaceEditResponse {
bool applied;
std::string failureReason;
};
struct LSPSemanticTokensEdit {
Uint32 start = 0;
Uint32 deleteCount = 0;
std::vector<Int32> data;
};
struct LSPSemanticTokensDelta {
std::string resultId;
std::vector<LSPSemanticTokensEdit> edits;
std::vector<Int32> data;
};
enum class LSPFoldingRangeKind : String::HashType {
Comment = String::hash( "comment" ),
Imports = String::hash( "imports" ),
Region = String::hash( "region" ),
};
struct LSPFoldingRange {
unsigned int startLine;
unsigned int endLine;
LSPFoldingRangeKind kind{ LSPFoldingRangeKind::Region };
};
} // namespace ecode
#endif // ECODE_LSPCLIENTPROTOCOL_HPP