diff --git a/include/Server/LSPConverter.h b/include/Server/LSPConverter.h index 7c91d7c9..a495ec99 100644 --- a/include/Server/LSPConverter.h +++ b/include/Server/LSPConverter.h @@ -1,50 +1,57 @@ #pragma once #include "Config.h" +#include "Protocol.h" #include "Async/Async.h" #include "Feature/Hover.h" #include "Feature/InlayHint.h" #include "Feature/FoldingRange.h" +#include "Feature/DocumentLink.h" #include "Feature/DocumentSymbol.h" #include "Feature/SemanticToken.h" -#include "Feature/DocumentLink.h" -#include "Server/Protocol.h" namespace clice { +enum class PositionEncodingKind : std::uint8_t { + UTF8 = 0, + UTF16, + UTF32, +}; + /// Responsible for converting between LSP and internal types. class LSPConverter { public: - using Result = async::Task; + json::Value initialize(json::Value value); - proto::InitializeResult initialize(json::Value value); - - auto encoding() { - return params.capabilities.general.positionEncodings[0]; + PositionEncodingKind encoding() { + return kind; } - auto& capabilities() { - return params.capabilities; + llvm::StringRef workspace() { + return workspacePath; } - /// The path of the workspace. - llvm::StringRef workspace(); - public: /// Convert a position into an offset relative to the beginning of the file. - uint32_t convert(llvm::StringRef content, proto::Position position); + std::uint32_t convert(llvm::StringRef content, proto::Position position); - proto::SemanticTokens transform(llvm::StringRef content, - llvm::ArrayRef tokens); + /// Convert `TextDocumentParams` to file path. + std::string convert(proto::TextDocumentParams params); - std::vector transform(llvm::StringRef content, - llvm::ArrayRef foldings); + json::Value convert(llvm::StringRef content, const feature::Hover& hover); - std::vector transform(llvm::StringRef content, - llvm::ArrayRef links); + json::Value convert(llvm::StringRef content, const feature::InlayHints& hints); + + json::Value convert(llvm::StringRef content, const feature::FoldingRanges& foldings); + + json::Value convert(llvm::StringRef content, const feature::DocumentLinks& links); + + json::Value convert(llvm::StringRef content, const feature::DocumentSymbols& symbols); + + json::Value convert(llvm::StringRef content, const feature::SemanticTokens& tokens); private: - proto::InitializeParams params; + PositionEncodingKind kind; std::string workspacePath; }; diff --git a/include/Server/Protocol.h b/include/Server/Protocol.h index 942ae2a7..442241c8 100644 --- a/include/Server/Protocol.h +++ b/include/Server/Protocol.h @@ -25,19 +25,6 @@ using DocumentUri = std::string; using URI = std::string; -struct None {}; - -/// A set of predefined position encoding kinds. -struct PositionEncodingKind : refl::Enum { - using Enum::Enum; - - constexpr inline static std::string_view UTF8 = "utf-8"; - constexpr inline static std::string_view UTF16 = "utf-16"; - constexpr inline static std::string_view UTF32 = "utf-32"; - - constexpr inline static std::array All = {UTF8, UTF16, UTF32}; -}; - struct Position { /// Line position in a document (zero-based). uinteger line; @@ -46,22 +33,18 @@ struct Position { /// The meaning of this offset is determined by the negotiated /// `PositionEncodingKind`. uinteger character; + + constexpr friend bool operator== (const Position&, const Position&) = default; }; -constexpr bool operator== (const proto::Position& lhs, const proto::Position rhs) { - return lhs.character == rhs.character && lhs.line == rhs.line; -} - -constexpr auto operator<=> (const proto::Position& lhs, const proto::Position rhs) { - return std::tie(lhs.line, lhs.character) <=> std::tie(rhs.line, rhs.character); -} - struct Range { /// The range's start position. Position start; /// The range's end position. Position end; + + constexpr friend bool operator== (const Range&, const Range&) = default; }; struct Location { @@ -80,23 +63,6 @@ struct TextEdit { string newText; }; -struct TextDocumentSyncKind : refl::Enum { - using Enum::Enum; - - enum Kind : std::uint8_t { - /// Documents should not be synced at all. - None = 0, - - /// Documents are synced by always sending the full content of the document. - Full = 1, - - /// Documents are synced by sending the full content on open. After that - /// only - /// incremental updates to the document are sent. - Incremental = 2, - }; -}; - struct TextDocumentItem { /// The text document's URI. DocumentUri uri; @@ -117,48 +83,6 @@ struct TextDocumentIdentifier { DocumentUri uri; }; -struct VersionedTextDocumentIdentifier { - /// The text document's URI. - DocumentUri uri; - /// The version number of this document. - /// - /// The version number of a document will increase after each change, - /// including undo/redo. The number doesn't need to be consecutive. - integer version; -}; - -/// An event describing a change to a text document. If only a text is provided -/// it is considered to be the full content of the document. -struct TextDocumentContentChangeEvent { - /// The range of the document that changed. - Range range; - - /// The new text for the provided range. - string text; -}; - -struct DidChangeTextDocumentParams { - /// The document that did change. The version number points - /// to the version after all provided content changes have - /// been applied. - VersionedTextDocumentIdentifier textDocument; - - /// The actual content changes. The content changes describe single state - /// changes to the document. So if there are two content changes c1 (at - /// array index 0) and c2 (at array index 1) for a document in state S then - /// c1 moves the document from S to S' and c2 from S' to S''. So c1 is - /// computed on the state S and c2 is computed on the state S'. - /// - /// To mirror the content of a document using change events use the following - /// approach: - /// - start with the same initial content - /// - apply the 'textDocument/didChange' notifications in the order you - /// receive them. - /// - apply the `TextDocumentContentChangeEvent`s in a single notification - /// in the order you receive them. - std::vector contentChanges; -}; - struct TextDocumentPositionParams { /// The text document. TextDocumentIdentifier textDocument; @@ -167,39 +91,12 @@ struct TextDocumentPositionParams { Position position; }; -using MarkupKind = string; - -struct MarkupContent { - /// The type of the Markup. - MarkupKind kind = "markdown"; - - /// The content itself. - string value; +enum class TextDocumentSyncKind { + None = 0, + Full = 1, + Incremental = 2, }; -struct DidOpenTextDocumentParams { - /// The document that was opened. - TextDocumentItem textDocument; -}; - -struct DidSaveTextDocumentParams { - /// The document that was saved. - TextDocumentIdentifier textDocument; - - /// Optional the content when saved. Depends on the includeText value - /// when the save notifcation was requested. - string text; -}; - -struct DidCloseTextDocumentParams { - /// The document that was closed. - TextDocumentIdentifier textDocument; -}; - -} // namespace clice::proto - -namespace clice::proto { - struct WorkspaceFolder { /// The associated URI for this workspace folder. URI uri; @@ -209,248 +106,77 @@ struct WorkspaceFolder { std::string name; }; -struct DidChangeWatchedFilesParams {}; +enum class ErrorCodes { + // Defined by JSON-RPC + ParseError = -32700, + InvalidRequest = -32600, + MethodNotFound = -32601, + InvalidParams = -32602, + InternalError = -32603, -struct ClientCapabilities { - /// General client capabilities. - struct { - /// The position encodings supported by the client. Client and server - /// have to agree on the same position encoding to ensure that offsets - /// (e.g. character position in a line) are interpreted the same on both - /// side. - /// - /// To keep the protocol backwards compatible the following applies: if - /// the value 'utf-16' is missing from the array of position encodings - /// servers can assume that the client supports UTF-16. UTF-16 is - /// therefore a mandatory encoding. - /// - /// If omitted it defaults to ['utf-16']. - /// - /// Implementation considerations: since the conversion from one encoding - /// into another requires the content of the file / line the conversion - /// is best done where the file is read which is usually on the server - /// side. - std::vector positionEncodings = {PositionEncodingKind::UTF16}; - } general; + /** + * Error code indicating that a server received a notification or + * request before the server has received the `initialize` request. + */ + ServerNotInitialized = -32002, + UnknownErrorCode = -32001, + + /** + * A request failed but it was syntactically correct, e.g the + * method name was known and the parameters were valid. The error + * message should contain human readable information about why + * the request failed. + * + * @since 3.17.0 + */ + RequestFailed = -32803, + + /** + * The server cancelled the request. This error code should + * only be used for requests that explicitly support being + * server cancellable. + * + * @since 3.17.0 + */ + ServerCancelled = -32802, + + /** + * The server detected that the content of a document got + * modified outside normal conditions. A server should + * NOT send this error code if it detects a content change + * in it unprocessed messages. The result even computed + * on an older state might still be useful for the client. + * + * If a client decides that a result is not of any use anymore + * the client should cancel the request. + */ + ContentModified = -32801, + + /** + * The client has canceled a request and a server has detected + * the cancel. + */ + RequestCancelled = -32800, }; -struct InitializeParams { - /// Information about the client. - struct { - /// The name of the client as defined by the client. - std::string name; - - /// The client's version as defined by the client. - std::string version; - } clientInfo; - - /// The capabilities provided by the client (editor or tool). - ClientCapabilities capabilities; - - /// The workspace folders configured in the client when the server starts. - /// This property is only available if the client supports workspace folders. - /// It can be `null` if the client supports workspace folders but none are - /// configured. - std::vector workspaceFolders; -}; - -struct SemanticTokensOptions { - /// The legend used by the server. - struct SemanticTokensLegend { - /// The token types a server uses. - std::vector tokenTypes; - - /// The token modifiers a server uses. - std::vector tokenModifiers; - } legend; - - /// Server supports providing semantic tokens for a specific range - /// of a document. - bool range = false; - - /// Server supports providing semantic tokens for a full document. - bool full = true; -}; - -struct SemanticTokens { - /// The actual tokens. - std::vector data; -}; - -/// A set of predefined range kinds. -enum class FoldingRangeKind { - /// Folding range for a comment. - Comment, - - /// Folding range for imports or includes. - Imports, - - /// Folding range for a region. - Region, -}; - -struct DocumentLink { - Range range; - URI target; -}; - -/// Represents a folding range. To be valid, start and end line must be bigger -/// than zero and smaller than the number of lines in the document. Clients -/// are free to ignore invalid ranges. -struct FoldingRange { - /// The zero-based start line of the range to fold. The folded area starts - /// after the line's last character. To be valid, the end must be zero or - /// larger and smaller than the number of lines in the document. - uint32_t startLine; - - /// The zero-based character offset from where the folded range starts. If - /// not defined, defaults to the length of the start line. - std::optional startCharacter; - - /// The zero-based end line of the range to fold. The folded area ends with - /// the line's last character. To be valid, the end must be zero or larger - /// and smaller than the number of lines in the document. - uint32_t endLine; - - /// The zero-based character offset before the folded range ends. If not - /// defined, defaults to the length of the end line. - std::optional endCharacter; - - /// Describes the kind of the folding range such as `comment` or `region`. - /// The kind is used to categorize folding ranges and used by commands like - /// 'Fold all comments'. See [FoldingRangeKind](#FoldingRangeKind) for an - /// enumeration of standardized kinds. - FoldingRangeKind kind; - - /// The text that the client should show when the specified range is - /// collapsed. If not defined or not supported by the client, a default - /// will be chosen by the client. - /// - /// @since 3.17.0 - proposed - std::optional collapsedText; -}; - -/// Server Capability. -struct ServerCapabilities { - /// The position encoding the server picked from the encodings offered - /// by the client via the client capability `general.positionEncodings`. - /// - /// If the client didn't provide any position encodings the only valid - /// value that a server can return is 'utf-16'. - /// - /// If omitted it defaults to 'utf-16'. - PositionEncodingKind positionEncoding = PositionEncodingKind::UTF16; - - /// Defines how text documents are synced. Is either a detailed structure - /// defining each notification or for backwards compatibility the - /// TextDocumentSyncKind number. If omitted it defaults to - /// `TextDocumentSyncKind.None`. - TextDocumentSyncKind textDocumentSync = TextDocumentSyncKind::None; - - /// The server provides go to declaration support. - bool declarationProvider = true; - - /// The server provides goto definition support. - bool definitionProvider = true; - - /// The server provides goto type definition support. - bool typeDefinitionProvider = true; - - /// The server provides goto implementation support. - bool implementationProvider = true; - - /// The server provides find references support. - bool referencesProvider = true; - - /// The server provides call hierarchy support. - bool callHierarchyProvider = true; - - /// The server provides type hierarchy support. - bool typeHierarchyProvider = true; - - /// The server provides semantic tokens support. - SemanticTokensOptions semanticTokensProvider; - - struct DocumentLinkOptions { - /// Document links have a resolve provider as well. - bool resolveProvider = false; - }; - - /// The server provides document link support. - DocumentLinkOptions documentLinkProvider; - - /// The server provides folding provider support. - bool foldingRangeProvider = true; -}; - -struct InitializeResult { - /// The capabilities the language server provides. - ServerCapabilities capabilities; - - /// Information about the server. - struct { - /// The name of the server as defined by the server. - std::string name; - - /// The server's version as defined by the server. - std::string version; - } serverInfo; -}; - -struct InitializedParams {}; - -} // namespace clice::proto - -namespace clice::proto { - struct TextDocumentParams { /// The text document. TextDocumentIdentifier textDocument; }; -enum class SemanticTokenTypes { - Namespace, - Type, - Class, - Enum, - Interface, - Struct, - TypeParameter, - Parameter, - Variable, - Property, - EnumMember, - Event, - Function, - Method, - Macro, - Keyword, - Modifier, - Comment, - String, - Number, - Regexp, - Operator, - Decorator -}; - -using SemanticTokensParams = TextDocumentParams; - -using FoldingRangeParams = TextDocumentParams; - -using DocumentLinkParams = TextDocumentParams; - -using DocumentSymbolParams = TextDocumentParams; - enum class SymbolKind {}; -struct DocumentSymbol { - std::string name; - std::string detail; - SymbolKind kind; - Range range; - Range selectionRange; - std::vector children; +struct ResolveProvider { + bool resolveProvider; +}; + +struct SemanticTokenOptions { + struct { + std::vector tokenTypes; + std::vector tokenModifiers; + } legend; + + bool full = true; }; struct HeaderContext { diff --git a/include/Server/Server.h b/include/Server/Server.h index 5e811143..aed8ae16 100644 --- a/include/Server/Server.h +++ b/include/Server/Server.h @@ -10,63 +10,6 @@ namespace clice { -namespace proto { - -enum class ErrorCodes { - // Defined by JSON-RPC - ParseError = -32700, - InvalidRequest = -32600, - MethodNotFound = -32601, - InvalidParams = -32602, - InternalError = -32603, - - /** - * Error code indicating that a server received a notification or - * request before the server has received the `initialize` request. - */ - ServerNotInitialized = -32002, - UnknownErrorCode = -32001, - - /** - * A request failed but it was syntactically correct, e.g the - * method name was known and the parameters were valid. The error - * message should contain human readable information about why - * the request failed. - * - * @since 3.17.0 - */ - RequestFailed = -32803, - - /** - * The server cancelled the request. This error code should - * only be used for requests that explicitly support being - * server cancellable. - * - * @since 3.17.0 - */ - ServerCancelled = -32802, - - /** - * The server detected that the content of a document got - * modified outside normal conditions. A server should - * NOT send this error code if it detects a content change - * in it unprocessed messages. The result even computed - * on an older state might still be useful for the client. - * - * If a client decides that a result is not of any use anymore - * the client should cancel the request. - */ - ContentModified = -32801, - - /** - * The client has canceled a request and a server has detected - * the cancel. - */ - RequestCancelled = -32800, -}; - -} - class Server { public: Server(); @@ -88,6 +31,9 @@ public: /// Handle notifications, a notification doesn't require response. async::Task<> onNotification(llvm::StringRef method, json::Value value); + /// Handle notifications `context/ + async::Task<> onFileOperation(llvm::StringRef method, json::Value value); + private: /// Send a request to the client. async::Task<> request(llvm::StringRef method, json::Value params); @@ -106,7 +52,6 @@ private: json::Value registerOptions); private: - async::Task<> initialize(json::Value value); public: std::uint32_t id = 0; diff --git a/src/Server/LSPConverter.cpp b/src/Server/LSPConverter.cpp index 41f40a35..fdc35d49 100644 --- a/src/Server/LSPConverter.cpp +++ b/src/Server/LSPConverter.cpp @@ -65,7 +65,7 @@ bool iterateCodepoints(llvm::StringRef content, const Callback& callback) { /// Convert a proto::Position to a file offset in the content with the specified encoding kind. std::uint32_t toOffset(llvm::StringRef content, - proto::PositionEncodingKind kind, + PositionEncodingKind kind, proto::Position position) { std::uint32_t offset = 0; for(auto i = 0; i < position.line; i++) { @@ -80,13 +80,13 @@ std::uint32_t toOffset(llvm::StringRef content, content = content.take_until([](char c) { return c == '\n'; }); assert(position.character <= content.size() && "Character value is out of range"); - if(kind == proto::PositionEncodingKind::UTF8) { + if(kind == PositionEncodingKind::UTF8) { offset += position.character; return offset; } - if(kind == proto::PositionEncodingKind::UTF16) { - iterateCodepoints(content, [&](size_t utf8Length, size_t utf16Length) { + if(kind == PositionEncodingKind::UTF16) { + iterateCodepoints(content, [&](std::uint32_t utf8Length, std::uint32_t utf16Length) { assert(position.character >= utf16Length && "Character value is out of range"); position.character -= utf16Length; offset += utf8Length; @@ -95,8 +95,8 @@ std::uint32_t toOffset(llvm::StringRef content, return offset; } - if(kind == proto::PositionEncodingKind::UTF32) { - iterateCodepoints(content, [&](size_t utf8Length, size_t) { + if(kind == PositionEncodingKind::UTF32) { + iterateCodepoints(content, [&](std::uint32_t utf8Length, std::uint32_t) { assert(position.character >= 1 && "Character value is out of range"); position.character -= 1; offset += utf8Length; @@ -109,12 +109,12 @@ std::uint32_t toOffset(llvm::StringRef content, } /// Remeasure the length (character count) of the content with the specified encoding kind. -std::uint32_t remeasure(llvm::StringRef content, proto::PositionEncodingKind kind) { - if(kind == proto::PositionEncodingKind::UTF8) { +std::uint32_t remeasure(llvm::StringRef content, PositionEncodingKind kind) { + if(kind == PositionEncodingKind::UTF8) { return content.size(); } - if(kind == proto::PositionEncodingKind::UTF16) { + if(kind == PositionEncodingKind::UTF16) { std::uint32_t length = 0; iterateCodepoints(content, [&](std::uint32_t, std::uint32_t utf16Length) { length += utf16Length; @@ -123,7 +123,7 @@ std::uint32_t remeasure(llvm::StringRef content, proto::PositionEncodingKind kin return length; } - if(kind == proto::PositionEncodingKind::UTF32) { + if(kind == PositionEncodingKind::UTF32) { std::uint32_t length = 0; iterateCodepoints(content, [&](std::uint32_t, std::uint32_t) { length += 1; @@ -137,7 +137,7 @@ std::uint32_t remeasure(llvm::StringRef content, proto::PositionEncodingKind kin class PositionConverter { public: - PositionConverter(llvm::StringRef content, proto::PositionEncodingKind encoding) : + PositionConverter(llvm::StringRef content, PositionEncodingKind encoding) : content(content), encoding(encoding) {} /// Convert a offset to a proto::Position with given encoding. @@ -196,7 +196,7 @@ public: } } - proto::Position toPosition2(uint32_t offset) { + proto::Position lookup(uint32_t offset) { auto it = cache.find(offset); assert(it != cache.end() && "Offset is not cached"); return it->second; @@ -214,51 +214,97 @@ private: llvm::DenseMap cache; llvm::StringRef content; - proto::PositionEncodingKind encoding; + PositionEncodingKind encoding; }; } // namespace -proto::InitializeResult LSPConverter::initialize(json::Value value) { - params = json::deserialize(value); +json::Value LSPConverter::convert(llvm::StringRef content, const feature::Hover& hover) { + return json::Value(nullptr); +} - proto::InitializeResult result = {}; - result.serverInfo.name = "clice"; - result.serverInfo.version = "0.0.1"; +json::Value LSPConverter::convert(llvm::StringRef content, const feature::InlayHints& hints) { + return json::Value(nullptr); +} - auto& semantictokens = result.capabilities.semanticTokensProvider; - for(auto& name: SymbolKind::all()) { - std::string type{name}; - type[0] = std::tolower(type[0]); - semantictokens.legend.tokenTypes.emplace_back(std::move(type)); +json::Value LSPConverter::convert(llvm::StringRef content, const feature::FoldingRanges& foldings) { + PositionConverter converter(content, encoding()); + converter.toPositions(foldings, [](auto&& folding) { return folding.range; }); + + json::Array result; + for(auto&& folding: foldings) { + auto [beginOffset, endOffset] = folding.range; + auto [beginLine, beginChar] = converter.lookup(beginOffset); + auto [endLine, endChar] = converter.lookup(endOffset); + + auto object = json::Object{ + {"startLine", beginLine}, + {"startCharacter", beginChar}, + {"endLine", endLine }, + {"kind", "region" }, + }; + + result.push_back(std::move(object)); + } + return result; +} + +json::Value LSPConverter::convert(llvm::StringRef content, const feature::DocumentLinks& links) { + PositionConverter converter(content, encoding()); + + json::Array result; + for(auto& link: links) { + proto::Range range{ + converter.toPosition(link.range.begin), + converter.toPosition(link.range.end), + }; + + auto object = json::Object{ + /// The range of document link. + {"range", json::serialize(range)}, + /// Target file URI. + {"target", fs::toURI(link.file) }, + }; + + result.emplace_back(std::move(object)); } return result; } -llvm::StringRef LSPConverter::workspace() { - if(workspacePath.empty()) { - workspacePath = fs::toPath(params.workspaceFolders[0].uri); - } - return workspacePath; +json::Value LSPConverter::convert(llvm::StringRef content, + const feature::DocumentSymbols& symbols) { + PositionConverter converter(content, encoding()); + + struct DocumentSymbol { + std::string name; + std::string detail; + SymbolKind kind; + proto::Range range; + proto::Range selectionRange; + std::vector children; + }; + + json::Array result; + + /// TODO: Implementation. + + return result; } -proto::SemanticTokens LSPConverter::transform(llvm::StringRef content, - llvm::ArrayRef tokens) { - proto::SemanticTokens result; +json::Value LSPConverter::convert(llvm::StringRef content, const feature::SemanticTokens& tokens) { + std::vector groups; auto addGroup = [&](uint32_t line, uint32_t character, uint32_t length, SymbolKind kind, SymbolModifiers modifiers) { - result.data.emplace_back(line); - result.data.emplace_back(character); - result.data.emplace_back(length); - result.data.emplace_back(kind.value()); - - /// FIXME: - result.data.emplace_back(0); + groups.emplace_back(line); + groups.emplace_back(character); + groups.emplace_back(length); + groups.emplace_back(kind.value()); + groups.emplace_back(0); }; PositionConverter converter(content, encoding()); @@ -321,50 +367,99 @@ proto::SemanticTokens LSPConverter::transform(llvm::StringRef content, lastChar = beginChar; } - return result; + return json::Object{ + /// The actual tokens. + {"data", json::serialize(groups)}, + }; } -std::vector - LSPConverter::transform(llvm::StringRef content, - llvm::ArrayRef foldings) { - std::vector result; +namespace proto { - PositionConverter converter(content, encoding()); - converter.toPositions(foldings, [](auto&& folding) { return folding.range; }); +struct InitializeParams { + struct ClientInfo { + std::string name; + std::string version; + } clientInfo; - for(auto&& folding: foldings) { - auto [beginOffset, endOffset] = folding.range; - auto [beginLine, beginChar] = converter.toPosition2(beginOffset); - auto [endLine, endChar] = converter.toPosition2(endOffset); + struct ClientCapabilities { + struct General { + std::vector positionEncodings; + } general; + } capabilities; - result.emplace_back(proto::FoldingRange{ - .startLine = beginLine, - .startCharacter = beginChar, - .endLine = endLine, - /// FIXME: Figure out how to handle end character. - .endCharacter = endChar - 1, - .kind = proto::FoldingRangeKind::Region, - .collapsedText = folding.text, - }); + std::vector workspaceFolders; +}; + +struct InitializeResult { + struct ServerInfo { + std::string name; + std::string version; + } serverInfo; + + struct ServerCapabilities { + std::string positionEncoding; + TextDocumentSyncKind textDocumentSync = TextDocumentSyncKind::Incremental; + + bool declarationProvider = true; + bool definitionProvider = true; + bool typeDefinitionProvider = true; + bool implementationProvider = true; + bool callHierarchyProvider = true; + bool typeHierarchyProvider = true; + + bool hoverProvider = true; + ResolveProvider inlayHintProvider = {true}; + bool foldingRangeProvider = true; + ResolveProvider documentLinkProvider = {false}; + bool documentSymbolProvider = true; + SemanticTokenOptions semanticTokensProvider; + + /// TODO: + /// completionProvider + /// signatureHelpProvider + /// codeLensProvider + /// codeActionProvider + /// documentFormattingProvider + /// documentRangeFormattingProvider + /// renameProvider + /// diagnosticProvider + } capabilities; +}; + +} // namespace proto + +json::Value LSPConverter::initialize(json::Value value) { + auto params = json::deserialize(value); + + auto& encodings = params.capabilities.general.positionEncodings; + /// Select the first one encoding if any. + if(encodings.empty()) { + kind = PositionEncodingKind::UTF16; + } else if(encodings[0] == "utf-8") { + kind = PositionEncodingKind::UTF8; + } else if(encodings[0] == "utf-16") { + kind = PositionEncodingKind::UTF16; + } else if(encodings[0] == "utf-32") { + kind = PositionEncodingKind::UTF32; } - return result; -} + workspacePath = fs::toPath(params.workspaceFolders[0].uri); -std::vector - LSPConverter::transform(llvm::StringRef content, llvm::ArrayRef links) { - PositionConverter converter(content, encoding()); + proto::InitializeResult result{ + .serverInfo = {"clice", "0.0.1"}, + .capabilities = { + .positionEncoding = encodings.empty() ? "utf-16" : encodings[0], + } + }; - std::vector result; - for(auto& link: links) { - proto::Range range{ - converter.toPosition(link.range.begin), - converter.toPosition(link.range.end), - }; - result.emplace_back(range, fs::toURI(link.file)); + auto& semanticTokensProvider = result.capabilities.semanticTokensProvider; + for(auto name: SymbolKind::all()) { + std::string type{name}; + type[0] = std::tolower(type[0]); + semanticTokensProvider.legend.tokenTypes.emplace_back(std::move(type)); } - return result; + return json::serialize(result); } } // namespace clice diff --git a/src/Server/Lifecycle.cpp b/src/Server/Lifecycle.cpp deleted file mode 100644 index 72ff54a1..00000000 --- a/src/Server/Lifecycle.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "Server/Server.h" -#include "Support/FileSystem.h" - -namespace clice { - -async::Task<> Server::initialize(json::Value value) { - converter.initialize(std::move(value)); - - proto::InitializeResult result = {}; - - /// Initialize the server info. - result.serverInfo.name = "clice"; - result.serverInfo.version = "0.0.1"; - - co_await response(std::move(id), json::serialize(result)); - co_return; -} - -// async::Task<> Server::onInitialize(json::Value id, const proto::InitializeParams& params) { -// proto::InitializeResult result = {}; -// result.serverInfo.name = "clice"; -// result.serverInfo.version = "0.0.1"; -// -// /// Set `SemanticTokensOptions` -// for(auto kind: SymbolKind::all()) { -// std::string name{kind}; -// name[0] = std::tolower(name[0]); -// result.capabilities.semanticTokensProvider.legend.tokenTypes.emplace_back(std::move(name)); -// } -// -// result.capabilities.semanticTokensProvider.legend.tokenModifiers = { -// -// }; -// -// co_await response(std::move(id), json::serialize(result)); -// -// auto workplace = SourceConverter::toPath(params.workspaceFolders[0].uri); -// config::init(workplace); -// -// for(auto& dir: config::server.compile_commands_dirs) { -// llvm::SmallString<128> path = {dir}; -// path::append(path, "compile_commands.json"); -// database.updateCommands(path); -// } -// } - -/// async::Task<> Server::onInitialized(const proto::InitializedParams& params) { -/// co_return; -/// } -/// -/// async::Task<> Server::onExit(const proto::None&) { -/// co_return; -/// } -/// -/// async::Task<> Server::onShutdown(json::Value id, const proto::None&) { -/// co_return; -/// } - -} // namespace clice diff --git a/src/Server/Server.cpp b/src/Server/Server.cpp index 853814c7..ad9b6c94 100644 --- a/src/Server/Server.cpp +++ b/src/Server/Server.cpp @@ -76,38 +76,40 @@ async::Task Server::onRequest(llvm::StringRef method, json::Value v } async::Task Server::onTextDocument(llvm::StringRef method, json::Value value) { + using SemanticTokensParams = proto::TextDocumentParams; + using FoldingRangeParams = proto::TextDocumentParams; + using DocumentLinkParams = proto::TextDocumentParams; + using DocumentSymbolParams = proto::TextDocumentParams; + if(method == "semanticTokens/full") { - auto params2 = json::deserialize(value); + auto params2 = json::deserialize(value); auto path = fs::toPath(params2.textDocument.uri); std::string buffer; if(auto index = co_await indexer.getFeatureIndex(buffer, path)) { - co_return json::serialize( - converter.transform(index->content(), index->semanticTokens())); + co_return converter.convert(index->content(), index->semanticTokens()); } else { co_return json::Value(nullptr); } } else if(method == "foldingRange") { - auto params2 = json::deserialize(value); + auto params2 = json::deserialize(value); auto path = fs::toPath(params2.textDocument.uri); std::string buffer; if(auto index = co_await indexer.getFeatureIndex(buffer, path)) { - co_return json::serialize( - converter.transform(index->content(), index->foldingRanges())); + co_return converter.convert(index->content(), index->foldingRanges()); } else { co_return json::Value(nullptr); } } else if(method == "documentLink") { - auto params2 = json::deserialize(value); + auto params2 = json::deserialize(value); auto path = fs::toPath(params2.textDocument.uri); std::string buffer; if(auto index = co_await indexer.getFeatureIndex(buffer, path)) { - co_return json::serialize( - converter.transform(index->content(), index->documentLinks())); + co_return converter.convert(index->content(), index->documentLinks()); } else { co_return json::Value(nullptr); } @@ -144,6 +146,10 @@ async::Task Server::onIndex(llvm::StringRef method, json::Value val } async::Task<> Server::onNotification(llvm::StringRef method, json::Value value) { + if(method.consume_front("textDocument/")) { + /// co_await onFileOperation(method, std::move(value)); + } + if(method.consume_front("index/")) { if(method == "all") { indexer.indexAll(); diff --git a/unittests/Feature/Hover.cpp b/unittests/Feature/Hover.cpp index d74e1574..b4abe6b4 100644 --- a/unittests/Feature/Hover.cpp +++ b/unittests/Feature/Hover.cpp @@ -22,7 +22,7 @@ struct DeclCollector : public clang::RecursiveASTVisitor { struct Hover : TestFixture { llvm::StringMap decls; - void run(llvm::StringRef code, proto::Range range = {}) { + void run(llvm::StringRef code, LocalSourceRange range = {}) { addMain("main.cpp", code); compile(); DeclCollector collector; diff --git a/unittests/Index/USR.cpp b/unittests/Index/USR.cpp index 69101150..b2bb8391 100644 --- a/unittests/Index/USR.cpp +++ b/unittests/Index/USR.cpp @@ -1,4 +1,3 @@ -#include "Server/Protocol.h" #include "Support/Logger.h" #include "Test/CTest.h" #include "Index/USR.h"