From b84bde88b1b4f8c1c85cfef4ffa5621e7dae80d5 Mon Sep 17 00:00:00 2001 From: ykiko Date: Fri, 22 Nov 2024 16:48:35 +0800 Subject: [PATCH] Add some lsp functions. --- include/Basic/Document.h | 27 ++++ include/Basic/Location.h | 6 + include/Feature/CodeAction.h | 9 ++ include/Feature/CodeCompletion.h | 2 + include/Feature/CodeLens.h | 7 + include/Feature/DocumentHighlight.h | 9 ++ include/Feature/DocumentLink.h | 9 ++ include/Feature/DocumentSymbol.h | 4 +- include/Feature/FoldingRange.h | 7 + include/Feature/Formatting.h | 10 ++ include/Feature/Hover.h | 9 ++ include/Feature/InlayHint.h | 9 ++ include/Feature/Lookup.h | 89 +++++++++++ include/Feature/SignatureHelp.h | 2 + include/Server/Server.h | 228 ++++++++++++++++++++++------ src/Server/Server.cpp | 56 ++++--- 16 files changed, 408 insertions(+), 75 deletions(-) create mode 100644 include/Feature/CodeLens.h create mode 100644 include/Feature/DocumentHighlight.h create mode 100644 include/Feature/Lookup.h diff --git a/include/Basic/Document.h b/include/Basic/Document.h index 7c28d769..9cc72a4e 100644 --- a/include/Basic/Document.h +++ b/include/Basic/Document.h @@ -1,6 +1,7 @@ #pragma once #include "Location.h" +#include "Support/Reflection.h" namespace clice::proto { @@ -19,4 +20,30 @@ struct TextDocumentItem { string text; }; +struct TextDocumentIdentifier { + /// The text document's URI. + DocumentUri uri; +}; + +CLICE_RECORD(VersionedTextDocumentIdentifier, TextDocumentIdentifier) { + /// 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; +}; + +struct TextDocumentContentChangeEvent { + /// The new text of the whole document. + string text; +}; + +struct TextDocumentPositionParams { + /// The text document. + TextDocumentIdentifier textDocument; + + /// The position inside the text document. + Position position; +}; + } // namespace clice::proto diff --git a/include/Basic/Location.h b/include/Basic/Location.h index 846362f8..0971272c 100644 --- a/include/Basic/Location.h +++ b/include/Basic/Location.h @@ -39,4 +39,10 @@ struct TextEdit { string newText; }; +struct Location { + DocumentUri uri; + + Range range; +}; + } // namespace clice::proto diff --git a/include/Feature/CodeAction.h b/include/Feature/CodeAction.h index e69de29b..99185266 100644 --- a/include/Feature/CodeAction.h +++ b/include/Feature/CodeAction.h @@ -0,0 +1,9 @@ +#pragma once + +#include "Basic/Document.h" + +namespace clice::proto { + +struct CodeActionParams {}; + +} // namespace clice::proto diff --git a/include/Feature/CodeCompletion.h b/include/Feature/CodeCompletion.h index a4e31b22..803e2dfd 100644 --- a/include/Feature/CodeCompletion.h +++ b/include/Feature/CodeCompletion.h @@ -60,6 +60,8 @@ struct CompletionItem { // ... }; +struct CompletionParams {}; + } // namespace clice::proto namespace clice { diff --git a/include/Feature/CodeLens.h b/include/Feature/CodeLens.h new file mode 100644 index 00000000..175c7904 --- /dev/null +++ b/include/Feature/CodeLens.h @@ -0,0 +1,7 @@ +#include "Basic/Document.h" + +namespace clice::proto { + +struct CodeLensParams {}; + +} // namespace clice::proto diff --git a/include/Feature/DocumentHighlight.h b/include/Feature/DocumentHighlight.h new file mode 100644 index 00000000..49453ad2 --- /dev/null +++ b/include/Feature/DocumentHighlight.h @@ -0,0 +1,9 @@ +#pragma once + +#include "Basic/Document.h" + +namespace clice::proto { + +using DocumentHighlightParams = TextDocumentPositionParams; + +} diff --git a/include/Feature/DocumentLink.h b/include/Feature/DocumentLink.h index e69de29b..8e2be9b1 100644 --- a/include/Feature/DocumentLink.h +++ b/include/Feature/DocumentLink.h @@ -0,0 +1,9 @@ +#pragma once + +#include "Basic/Document.h" + +namespace clice::proto { + +struct DocumentLinkParams {}; + +} // namespace clice::proto diff --git a/include/Feature/DocumentSymbol.h b/include/Feature/DocumentSymbol.h index b7155a8e..c685bcc8 100644 --- a/include/Feature/DocumentSymbol.h +++ b/include/Feature/DocumentSymbol.h @@ -29,4 +29,6 @@ enum class SymbolKind { TypeParameter = 26, }; -} +struct DocumentSymbolParams {}; + +} // namespace clice::proto diff --git a/include/Feature/FoldingRange.h b/include/Feature/FoldingRange.h index e69de29b..81c52fd8 100644 --- a/include/Feature/FoldingRange.h +++ b/include/Feature/FoldingRange.h @@ -0,0 +1,7 @@ +#include "Basic/Document.h" + +namespace clice::proto { + +struct FoldingRangeParams {}; + +} // namespace clice::proto diff --git a/include/Feature/Formatting.h b/include/Feature/Formatting.h index 8b137891..486a8e66 100644 --- a/include/Feature/Formatting.h +++ b/include/Feature/Formatting.h @@ -1 +1,11 @@ +#pragma once +#include "Basic/Document.h" + +namespace clice::proto { + +struct DocumentFormattingParams {}; + +struct DocumentRangeFormattingParams {}; + +} // namespace clice::proto diff --git a/include/Feature/Hover.h b/include/Feature/Hover.h index e69de29b..5544c456 100644 --- a/include/Feature/Hover.h +++ b/include/Feature/Hover.h @@ -0,0 +1,9 @@ +#pragma once + +#include "Basic/Document.h" + +namespace clice::proto { + +struct HoverParams {}; + +} // namespace clice::proto diff --git a/include/Feature/InlayHint.h b/include/Feature/InlayHint.h index e69de29b..4ac2d582 100644 --- a/include/Feature/InlayHint.h +++ b/include/Feature/InlayHint.h @@ -0,0 +1,9 @@ +#pragma once + +#include "Basic/Document.h" + +namespace clice::proto { + +struct InlayHintParams {}; + +} // namespace clice::proto diff --git a/include/Feature/Lookup.h b/include/Feature/Lookup.h new file mode 100644 index 00000000..bb1dbc81 --- /dev/null +++ b/include/Feature/Lookup.h @@ -0,0 +1,89 @@ +#include "Basic/Document.h" + +namespace clice::proto { + +using DeclarationParams = TextDocumentPositionParams; + +using DeclarationResult = std::vector; + +using DefinitionParams = TextDocumentPositionParams; + +using DefinitionResult = std::vector; + +using TypeDefinitionParams = TextDocumentPositionParams; + +using TypeDefinitionResult = std::vector; + +using ImplementationParams = TextDocumentPositionParams; + +using ImplementationResult = std::vector; + +using ReferenceParams = TextDocumentPositionParams; + +using ReferenceResult = std::vector; + +using CallHierarchyPrepareParams = TextDocumentPositionParams; + +struct CallHierarchyItem { + /// The name of this item. + string name; + + /// FIXME: ... +}; + +using CallHierarchyPrepareResult = std::vector; + +struct CallHierarchyIncomingCallsParams { + CallHierarchyItem item; +}; + +struct CallHierarchyIncomingCall { + /// The item that makes the call. + CallHierarchyItem from; + + /// The range at which at which the calls appears. This is relative to the caller + /// denoted by `from`. + std::vector fromRange; +}; + +using CallHierarchyIncomingCallsResult = std::vector; + +struct CallHierarchyOutgoingCallsParams { + CallHierarchyItem item; +}; + +struct CallHierarchyOutgoingCall { + /// The item that is called. + CallHierarchyItem to; + + /// The range at which this item is called. This is relative to the caller + /// denoted by `to`. + std::vector toRange; +}; + +using CallHierarchyOutgoingCallsResult = std::vector; + +struct TypeHierarchyItem { + /// The name of the type. + string name; + + /// FIXME: ... +}; + +using TypeHierarchyPrepareParams = TextDocumentPositionParams; + +using TypeHierarchyPrepareResult = std::vector; + +struct TypeHierarchySupertypesParams { + TypeHierarchyItem item; +}; + +using TypeHierarchySupertypesResult = std::vector; + +struct TypeHierarchySubtypesParams { + TypeHierarchyItem item; +}; + +using TypeHierarchySubtypesResult = std::vector; + +} // namespace clice::proto diff --git a/include/Feature/SignatureHelp.h b/include/Feature/SignatureHelp.h index 8f92b56f..3bc11f14 100644 --- a/include/Feature/SignatureHelp.h +++ b/include/Feature/SignatureHelp.h @@ -18,6 +18,8 @@ struct SignatureHelp { std::vector signatures; }; +struct SignatureHelpParams {}; + } // namespace clice::proto namespace clice { diff --git a/include/Server/Server.h b/include/Server/Server.h index f1dfd5b1..c965ed20 100644 --- a/include/Server/Server.h +++ b/include/Server/Server.h @@ -1,11 +1,26 @@ #pragma once -#include "Server/Config.h" -#include "Basic/Document.h" -#include "Support/JSON.h" -#include "llvm/ADT/FunctionExtras.h" #include "Async.h" +#include "Config.h" +#include "Basic/Document.h" #include "Compiler/Compiler.h" +#include "Support/JSON.h" + +#include "Feature/Lookup.h" +#include "Feature/DocumentHighlight.h" +#include "Feature/DocumentLink.h" +#include "Feature/Hover.h" +#include "Feature/CodeLens.h" +#include "Feature/FoldingRange.h" +#include "Feature/DocumentSymbol.h" +#include "Feature/SemanticTokens.h" +#include "Feature/InlayHint.h" +#include "Feature/CodeCompletion.h" +#include "Feature/SignatureHelp.h" +#include "Feature/CodeAction.h" +#include "Feature/Formatting.h" + +#include "llvm/ADT/FunctionExtras.h" namespace clice::proto { @@ -33,6 +48,8 @@ struct Workplace { string name; }; +struct None {}; + struct InitializeParams { /// Information about the client ClientInfo clientInfo; @@ -74,7 +91,31 @@ struct InitializedParams {}; struct DidOpenTextDocumentParams { /// The document that was opened. - TextDocumentItem document; + TextDocumentItem textDocument; +}; + +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. + std::vector contentChanges; +}; + +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 @@ -90,55 +131,42 @@ private: }; class LSPServer { -public: - promise onInitialize(json::Value id, const proto::InitializeParams& params); - - promise onInitialized(const proto::InitializedParams& params); - - promise onShutdown(json::Value id); - - promise onExit(); - -public: - promise onDidOpen(const proto::DidOpenTextDocumentParams& document); - public: LSPServer() { - addRequest("initialize", &LSPServer::onInitialize); - addNotification("initialized", &LSPServer::onInitialized); - addRequest("shutdown", &LSPServer::onShutdown); - addNotification("exit", &LSPServer::onExit); - addNotification("textDocument/didOpen", &LSPServer::onDidOpen); - } + addMethod("initialize", &LSPServer::onInitialize); + addMethod("initialized", &LSPServer::onInitialized); + addMethod("shutdown", &LSPServer::onShutdown); + addMethod("exit", &LSPServer::onExit); - template - void addRequest(llvm::StringRef name, - promise (LSPServer::*method)(json::Value, const Param&)) { - requests.try_emplace(name, - [this, method](json::Value id, json::Value value) -> promise { - co_await (this->*method)(std::move(id), - json::deserialize(value)); - }); - } + addMethod("textDocument/didOpen", &LSPServer::onDidOpen); + addMethod("textDocument/didChange", &LSPServer::onDidChange); + addMethod("textDocument/didSave", &LSPServer::onDidSave); + addMethod("textDocument/didClose", &LSPServer::onDidClose); - void addRequest(llvm::StringRef name, promise (LSPServer::*method)(json::Value)) { - requests.try_emplace(name, - [this, method](json::Value id, json::Value value) -> promise { - co_await (this->*method)(std::move(id)); - }); - } - - template - void addNotification(llvm::StringRef name, promise (LSPServer::*method)(const Param&)) { - notifications.try_emplace(name, [this, method](json::Value value) -> promise { - co_await (this->*method)(json::deserialize(value)); - }); - } - - void addNotification(llvm::StringRef name, promise (LSPServer::*method)()) { - notifications.try_emplace(name, [this, method](json::Value value) -> promise { - co_await (this->*method)(); - }); + addMethod("textDocument/declaration", &LSPServer::onGotoDeclaration); + addMethod("textDocument/definition", &LSPServer::onGotoDefinition); + addMethod("textDocument/typeDefinition", &LSPServer::onGotoTypeDefinition); + addMethod("textDocument/implementation", &LSPServer::onGotoImplementation); + addMethod("textDocument/references", &LSPServer::onFindReferences); + addMethod("textDocument/callHierarchy/prepare", &LSPServer::onPrepareCallHierarchy); + addMethod("textDocument/callHierarchy/incomingCalls", &LSPServer::onIncomingCall); + addMethod("textDocument/callHierarchy/outgoingCalls", &LSPServer::onOutgoingCall); + addMethod("textDocument/typeHierarchy/prepare", &LSPServer::onPrepareTypeHierarchy); + addMethod("textDocument/typeHierarchy/supertypes", &LSPServer::onSupertypes); + addMethod("textDocument/typeHierarchy/subtypes", &LSPServer::onSubtypes); + addMethod("textDocument/documentHighlight", &LSPServer::onDocumentHighlight); + addMethod("textDocument/documentLink", &LSPServer::onDocumentLink); + addMethod("textDocument/hover", &LSPServer::onHover); + addMethod("textDocument/codeLens", &LSPServer::onCodeLens); + addMethod("textDocument/foldingRange", &LSPServer::onFoldingRange); + addMethod("textDocument/documentSymbol", &LSPServer::onDocumentSymbol); + addMethod("textDocument/semanticTokens", &LSPServer::onSemanticTokens); + addMethod("textDocument/inlayHint", &LSPServer::onInlayHint); + addMethod("textDocument/completion", &LSPServer::onCodeCompletion); + addMethod("textDocument/signatureHelp", &LSPServer::onSignatureHelp); + addMethod("textDocument/codeAction", &LSPServer::onCodeAction); + addMethod("textDocument/formatting", &LSPServer::onFormatting); + addMethod("textDocument/rangeFormatting", &LSPServer::onRangeFormatting); } promise dispatch(json::Value value, Writer& writer); @@ -147,6 +175,106 @@ private: using onRequest = llvm::unique_function(json::Value, json::Value)>; using onNotification = llvm::unique_function(json::Value)>; + template + void addMethod(llvm::StringRef name, + promise (LSPServer::*method)(json::Value, const Param&)) { + requests.try_emplace(name, + [this, method](json::Value id, json::Value value) -> promise { + co_await (this->*method)(std::move(id), + json::deserialize(value)); + }); + } + + template + void addMethod(llvm::StringRef name, promise (LSPServer::*method)(const Param&)) { + notifications.try_emplace(name, [this, method](json::Value value) -> promise { + co_await (this->*method)(json::deserialize(value)); + }); + } + +private: + /// ============================================================================ + /// Lifestyle Message + /// ============================================================================ + + promise onInitialize(json::Value id, const proto::InitializeParams& params); + + promise onInitialized(const proto::InitializedParams& params); + + promise onShutdown(json::Value id, const proto::None&); + + promise onExit(const proto::None&); + + /// ============================================================================ + /// Document Synchronization + /// ============================================================================ + + promise onDidOpen(const proto::DidOpenTextDocumentParams& document); + + promise onDidChange(const proto::DidChangeTextDocumentParams& document); + + promise onDidSave(const proto::DidSaveTextDocumentParams& document); + + promise onDidClose(const proto::DidCloseTextDocumentParams& document); + + /// ============================================================================ + /// Language Features + /// ============================================================================ + + promise onGotoDeclaration(json::Value id, const proto::DeclarationParams& params); + + promise onGotoDefinition(json::Value id, const proto::DefinitionParams& params); + + promise onGotoTypeDefinition(json::Value id, const proto::TypeDefinitionParams& params); + + promise onGotoImplementation(json::Value id, const proto::ImplementationParams& params); + + promise onFindReferences(json::Value id, const proto::ReferenceParams& params); + + promise onPrepareCallHierarchy(json::Value id, + const proto::CallHierarchyPrepareParams& params); + + promise onIncomingCall(json::Value id, + const proto::CallHierarchyIncomingCallsParams& params); + + promise onOutgoingCall(json::Value id, + const proto::CallHierarchyOutgoingCallsParams& params); + + promise onPrepareTypeHierarchy(json::Value id, + const proto::TypeHierarchyPrepareParams& params); + + promise onSupertypes(json::Value id, const proto::TypeHierarchySupertypesParams& params); + + promise onSubtypes(json::Value id, const proto::TypeHierarchySubtypesParams& params); + + promise onDocumentHighlight(json::Value id, const proto::DocumentHighlightParams& params); + + promise onDocumentLink(json::Value id, const proto::DocumentLinkParams& params); + + promise onHover(json::Value id, const proto::HoverParams& params); + + promise onCodeLens(json::Value id, const proto::CodeLensParams& params); + + promise onFoldingRange(json::Value id, const proto::FoldingRangeParams& params); + + promise onDocumentSymbol(json::Value id, const proto::DocumentSymbolParams& params); + + promise onSemanticTokens(json::Value id, const proto::SemanticTokensParams& params); + + promise onInlayHint(json::Value id, const proto::InlayHintParams& params); + + promise onCodeCompletion(json::Value id, const proto::CompletionParams& params); + + promise onSignatureHelp(json::Value id, const proto::SignatureHelpParams& params); + + promise onCodeAction(json::Value id, const proto::CodeActionParams& params); + + promise onFormatting(json::Value id, const proto::DocumentFormattingParams& params); + + promise onRangeFormatting(json::Value id, + const proto::DocumentRangeFormattingParams& params); + +private: Writer* writer; llvm::StringMap requests; llvm::StringMap notifications; diff --git a/src/Server/Server.cpp b/src/Server/Server.cpp index d20c5289..5dc97f7d 100644 --- a/src/Server/Server.cpp +++ b/src/Server/Server.cpp @@ -1,27 +1,9 @@ #include "Server/Server.h" +#include "Basic/URI.h" namespace clice { promise CacheManager::buildPCH(std::string file, std::string content) { - co_await async::schedule_task([&]() { - // TODO: lookup - std::vector args = { - "clang++", - "-std=c++20", - file.c_str(), - "-resource-dir", - "/home/ykiko/C++/clice2/build/lib/clang/20", - }; - Compiler compiler(file, content, args); - - auto bounds = clang::Lexer::ComputePreamble(content, compiler.pp().getLangOpts(), false); - - llvm::outs() << "Generating PCH\n"; - - compiler.generatePCH("/home/ykiko/C++/clice2/build/cache/xxx.pch", - bounds.Size, - bounds.PreambleEndsAtStartOfLine); - }); co_return; } @@ -29,6 +11,7 @@ promise CacheManager::buildPCH(std::string file, std::string content) { promise LSPServer::onInitialize(json::Value id, const proto::InitializeParams& params) { llvm::outs() << "onInitialize\n"; writer->write(std::move(id), json::serialize(proto::InitializeResult())); + async::sleep(std::chrono::seconds(10)); co_return; } @@ -37,18 +20,43 @@ promise LSPServer::onInitialized(const proto::InitializedParams& params) { co_return; } -promise LSPServer::onExit() { +promise LSPServer::onExit(const proto::None&) { llvm::outs() << "onExit\n"; co_return; } -promise LSPServer::onShutdown(json::Value id) { +promise LSPServer::onShutdown(json::Value id, const proto::None&) { llvm::outs() << "onShutdown\n"; co_return; } promise LSPServer::onDidOpen(const proto::DidOpenTextDocumentParams& params) { - llvm::outs() << "onDidOpen\n"; + llvm::outs() << "onDidOpen: " << params.textDocument.uri << "\n"; + auto path = URI::resolve(params.textDocument.uri); + llvm::StringRef content = params.textDocument.text; + + co_await async::schedule_task([&]() { + // TODO: lookup + std::vector args = { + "clang++", + "-std=c++20", + path.c_str(), + "-resource-dir", + "/home/ykiko/C++/clice2/build/lib/clang/20", + }; + Compiler compiler(path, content, args); + + auto bounds = clang::Lexer::ComputePreamble(content, {}, false); + + llvm::outs() << "Generating PCH\n"; + + compiler.generatePCH("/home/ykiko/C++/clice2/build/cache/xxx.pch", + bounds.Size, + bounds.PreambleEndsAtStartOfLine); + }); + + llvm::outs() << "build PCH success\n"; + co_return; } @@ -61,13 +69,13 @@ promise LSPServer::dispatch(json::Value value, Writer& writer) { auto name = *method->getAsString(); if(auto id = object->get("id")) { if(auto iter = requests.find(name); iter != requests.end()) { - co_await iter->second(std::move(*id), std::move(*object)); + co_await iter->second(std::move(*id), std::move(*object->get("params"))); } else { llvm::errs() << "Unknown request: " << name << "\n"; } } else { if(auto iter = notifications.find(name); iter != notifications.end()) { - co_await iter->second(std::move(*object)); + co_await iter->second(std::move(*object->get("params"))); } else { llvm::errs() << "Unknown notification: " << name << "\n"; }