diff options
| author | Yong He <yonghe@outlook.com> | 2024-10-27 14:27:52 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-10-27 14:27:52 -0700 |
| commit | 1dd6ec26776081274604a94a96a1d87818830e82 (patch) | |
| tree | 25b3a7d2df98cc152986b04725f61e69e1492b3a /source | |
| parent | faa7d6ba4d73d8e3d89f083deea103afda456a21 (diff) | |
Export language server to wasm. (#5419)
Diffstat (limited to 'source')
| -rw-r--r-- | source/compiler-core/slang-language-server-protocol.h | 30 | ||||
| -rw-r--r-- | source/slang-wasm/CMakeLists.txt | 11 | ||||
| -rw-r--r-- | source/slang-wasm/slang-wasm-bindings.cpp | 146 | ||||
| -rw-r--r-- | source/slang-wasm/slang-wasm.cpp | 310 | ||||
| -rw-r--r-- | source/slang-wasm/slang-wasm.h | 123 | ||||
| -rw-r--r-- | source/slang/slang-language-server-completion.cpp | 43 | ||||
| -rw-r--r-- | source/slang/slang-language-server-completion.h | 41 | ||||
| -rw-r--r-- | source/slang/slang-language-server.cpp | 422 | ||||
| -rw-r--r-- | source/slang/slang-language-server.h | 111 |
9 files changed, 1003 insertions, 234 deletions
diff --git a/source/compiler-core/slang-language-server-protocol.h b/source/compiler-core/slang-language-server-protocol.h index ff5cd394e..506969dcc 100644 --- a/source/compiler-core/slang-language-server-protocol.h +++ b/source/compiler-core/slang-language-server-protocol.h @@ -6,6 +6,7 @@ #include "../../source/core/slang-rtti-info.h" #include "../../source/compiler-core/slang-json-value.h" +#include <optional> namespace Slang { @@ -1124,3 +1125,32 @@ struct DocumentFormattingParams } // namespace LanguageServerProtocol } // namespace Slang + +namespace Slang +{ + template<typename T> + struct LanguageServerResult + { + SlangResult returnCode; + bool isNull = true; + T result; + LanguageServerResult() + { + returnCode = SLANG_OK; + } + LanguageServerResult(std::nullopt_t) + { + returnCode = SLANG_OK; + } + LanguageServerResult(const T& value) + { + result = value; + isNull = false; + returnCode = SLANG_OK; + } + LanguageServerResult(SlangResult code) + { + returnCode = code; + } + }; +} diff --git a/source/slang-wasm/CMakeLists.txt b/source/slang-wasm/CMakeLists.txt index d821415e7..75bbe3b69 100644 --- a/source/slang-wasm/CMakeLists.txt +++ b/source/slang-wasm/CMakeLists.txt @@ -9,7 +9,16 @@ if (EMSCRIPTEN) EXECUTABLE EXCLUDE_FROM_ALL USE_FEWER_WARNINGS - LINK_WITH_PRIVATE miniz lz4_static slang core compiler-core + LINK_WITH_PRIVATE + miniz + lz4_static + slang + core + compiler-core + slang-capability-defs + slang-capability-lookup + slang-reflect-headers + slang-lookup-tables INCLUDE_DIRECTORIES_PUBLIC ${slang_SOURCE_DIR}/include . ) # To generate binding code diff --git a/source/slang-wasm/slang-wasm-bindings.cpp b/source/slang-wasm/slang-wasm-bindings.cpp index d033f3846..360dec6eb 100644 --- a/source/slang-wasm/slang-wasm-bindings.cpp +++ b/source/slang-wasm/slang-wasm-bindings.cpp @@ -74,4 +74,150 @@ EMSCRIPTEN_BINDINGS(slang) return_value_policy::take_ownership()); register_vector<slang::wgsl::ComponentType*>("ComponentTypeList"); + + register_vector<std::string>("StringList"); + register_optional<std::vector<std::string>>(); + + value_object<slang::wgsl::lsp::Position>("Position") + .field("line", &slang::wgsl::lsp::Position::line) + .field("character", &slang::wgsl::lsp::Position::character); + + value_object<slang::wgsl::lsp::Range>("Range") + .field("start", &slang::wgsl::lsp::Range::start) + .field("end", &slang::wgsl::lsp::Range::end); + + value_object<slang::wgsl::lsp::Location>("Location") + .field("uri", &slang::wgsl::lsp::Location::uri) + .field("range", &slang::wgsl::lsp::Location::range); + register_vector<slang::wgsl::lsp::Location>("LocationList"); + register_optional<std::vector<slang::wgsl::lsp::Location>>(); + + value_object<slang::wgsl::lsp::TextEdit>("TextEdit") + .field("range", &slang::wgsl::lsp::TextEdit::range) + .field("text", &slang::wgsl::lsp::TextEdit::text); + register_optional<slang::wgsl::lsp::TextEdit>(); + register_vector<slang::wgsl::lsp::TextEdit>("TextEditList"); + register_optional<std::vector<slang::wgsl::lsp::TextEdit>>(); + + value_object<slang::wgsl::lsp::MarkupContent>("MarkupContent") + .field("kind", &slang::wgsl::lsp::MarkupContent::kind) + .field("value", &slang::wgsl::lsp::MarkupContent::value); + register_optional<slang::wgsl::lsp::MarkupContent>(); + + value_object<slang::wgsl::lsp::Hover>("Hover") + .field("contents", &slang::wgsl::lsp::Hover::contents) + .field("range", &slang::wgsl::lsp::Hover::range); + register_optional<slang::wgsl::lsp::Hover>(); + + value_object<slang::wgsl::lsp::CompletionItem>("CompletionItem") + .field("label", &slang::wgsl::lsp::CompletionItem::label) + .field("kind", &slang::wgsl::lsp::CompletionItem::kind) + .field("detail", &slang::wgsl::lsp::CompletionItem::detail) + .field("documentation", &slang::wgsl::lsp::CompletionItem::documentation) + .field("textEdit", &slang::wgsl::lsp::CompletionItem::textEdit) + .field("data", &slang::wgsl::lsp::CompletionItem::data) + .field("commitCharacters", &slang::wgsl::lsp::CompletionItem::commitCharacters); + register_optional<slang::wgsl::lsp::CompletionItem>(); + register_vector<slang::wgsl::lsp::CompletionItem>("CompletionItemList"); + register_optional<std::vector<slang::wgsl::lsp::CompletionItem>>(); + + value_object<slang::wgsl::lsp::CompletionContext>("CompletionContext") + .field("triggerKind", &slang::wgsl::lsp::CompletionContext::triggerKind) + .field("triggerCharacter", &slang::wgsl::lsp::CompletionContext::triggerCharacter); + + value_array<std::array<uint32_t, 2>>("array_uint_2") + .element(emscripten::index<0>()) + .element(emscripten::index<1>()); + + value_object<slang::wgsl::lsp::ParameterInformation>("ParameterInformation") + .field("label", &slang::wgsl::lsp::ParameterInformation::label) + .field("documentation", &slang::wgsl::lsp::ParameterInformation::documentation); + + register_vector<slang::wgsl::lsp::ParameterInformation>("ParameterInformationList"); + + value_object<slang::wgsl::lsp::SignatureInformation>("SignatureInformation") + .field("label", &slang::wgsl::lsp::SignatureInformation::label) + .field("documentation", &slang::wgsl::lsp::SignatureInformation::documentation) + .field("parameters", &slang::wgsl::lsp::SignatureInformation::parameters); + + register_vector<slang::wgsl::lsp::SignatureInformation>("SignatureInformationList"); + + value_object<slang::wgsl::lsp::SignatureHelp>("SignatureHelp") + .field("signatures", &slang::wgsl::lsp::SignatureHelp::signatures) + .field("activeSignature", &slang::wgsl::lsp::SignatureHelp::activeSignature) + .field("activeParameter", &slang::wgsl::lsp::SignatureHelp::activeParameter); + register_optional<slang::wgsl::lsp::SignatureHelp>(); + + value_object<slang::wgsl::lsp::DocumentSymbol>("DocumentSymbol") + .field("name", &slang::wgsl::lsp::DocumentSymbol::name) + .field("detail", &slang::wgsl::lsp::DocumentSymbol::detail) + .field("kind", &slang::wgsl::lsp::DocumentSymbol::kind) + .field("range", &slang::wgsl::lsp::DocumentSymbol::range) + .field("selectionRange", &slang::wgsl::lsp::DocumentSymbol::selectionRange) + .field("children", &slang::wgsl::lsp::DocumentSymbol::children); + + register_vector<slang::wgsl::lsp::DocumentSymbol>("DocumentSymbolList"); + register_optional<std::vector<slang::wgsl::lsp::DocumentSymbol>>(); + + value_object<slang::wgsl::lsp::Diagnostics>("Diagnostics") + .field("code", &slang::wgsl::lsp::Diagnostics::code) + .field("range", &slang::wgsl::lsp::Diagnostics::range) + .field("severity", &slang::wgsl::lsp::Diagnostics::severity) + .field("message", &slang::wgsl::lsp::Diagnostics::message); + register_vector<slang::wgsl::lsp::Diagnostics>("DiagnosticsList"); + register_optional<std::vector<slang::wgsl::lsp::Diagnostics>>(); + + register_vector<uint32_t>("Uint32List"); + register_optional<std::vector<uint32_t>>(); + + class_<slang::wgsl::lsp::LanguageServer>("LanguageServer") + .function( + "didOpenTextDocument", + &slang::wgsl::lsp::LanguageServer::didOpenTextDocument, + allow_raw_pointers()) + .function( + "didCloseTextDocument", + &slang::wgsl::lsp::LanguageServer::didCloseTextDocument, + allow_raw_pointers()) + .function( + "didChangeTextDocument", + &slang::wgsl::lsp::LanguageServer::didChangeTextDocument, + allow_raw_pointers()) + .function( + "hover", + &slang::wgsl::lsp::LanguageServer::hover, + allow_raw_pointers()) + .function( + "gotoDefinition", + &slang::wgsl::lsp::LanguageServer::gotoDefinition, + allow_raw_pointers()) + .function( + "completion", + &slang::wgsl::lsp::LanguageServer::completion, + allow_raw_pointers()) + .function( + "completionResolve", + &slang::wgsl::lsp::LanguageServer::completionResolve, + allow_raw_pointers()) + .function( + "semanticTokens", + &slang::wgsl::lsp::LanguageServer::semanticTokens, + allow_raw_pointers()) + .function( + "signatureHelp", + &slang::wgsl::lsp::LanguageServer::signatureHelp, + allow_raw_pointers()) + .function( + "documentSymbol", + &slang::wgsl::lsp::LanguageServer::documentSymbol, + allow_raw_pointers()) + .function( + "getDiagnostics", + &slang::wgsl::lsp::LanguageServer::getDiagnostics, + allow_raw_pointers()); + + function( + "createLanguageServer", + &slang::wgsl::lsp::createLanguageServer, + return_value_policy::take_ownership()); } diff --git a/source/slang-wasm/slang-wasm.cpp b/source/slang-wasm/slang-wasm.cpp index 6fbe2dc6c..886efc5f7 100644 --- a/source/slang-wasm/slang-wasm.cpp +++ b/source/slang-wasm/slang-wasm.cpp @@ -5,6 +5,7 @@ #include "slang-wasm.h" #include "../core/slang-blob.h" #include "../core/slang-exception.h" +#include "../slang/slang-language-server.h" using namespace slang; @@ -261,5 +262,314 @@ emscripten::val ComponentType::getEntryPointCodeSpirv(int entryPointIndex, int t ptr)); } +namespace lsp +{ + Position translate(Slang::LanguageServerProtocol::Position p) + { + Position result; + result.line = p.line; + result.character = p.character; + return result; + } + Range translate(Slang::LanguageServerProtocol::Range r) + { + Range result; + result.start = translate(r.start); + result.end = translate(r.end); + return result; + } + Location translate(Slang::LanguageServerProtocol::Location l) + { + Location result; + result.uri = l.uri.getBuffer(); + result.range = translate(l.range); + return result; + } + Slang::LanguageServerProtocol::Position translate(Position p) + { + Slang::LanguageServerProtocol::Position result; + result.line = p.line; + result.character = p.character; + return result; + } + Slang::LanguageServerProtocol::Range translate(Range r) + { + Slang::LanguageServerProtocol::Range result; + result.start = translate(r.start); + result.end = translate(r.end); + return result; + } + Slang::LanguageServerProtocol::Location translate(Location l) + { + Slang::LanguageServerProtocol::Location result; + result.uri = l.uri.c_str(); + result.range = translate(l.range); + return result; + } + + LanguageServer::LanguageServer() + { + Slang::LanguageServerStartupOptions options = {}; + m_core = new Slang::LanguageServerCore(options); + init(); + } + + LanguageServer::~LanguageServer() + { + delete m_core; + } + + void LanguageServer::init() + { + Slang::LanguageServerProtocol::InitializeParams args = {}; + Slang::LanguageServerProtocol::WorkspaceFolder folder = {}; + folder.uri = "file:///"; + folder.name = "/"; + args.workspaceFolders.add(folder); + m_core->init(args); + } + + void LanguageServer::didOpenTextDocument(std::string uri, std::string text) + { + Slang::LanguageServerProtocol::DidOpenTextDocumentParams args = {}; + args.textDocument.uri = uri.c_str(); + args.textDocument.languageId = "slang"; + args.textDocument.text = text.c_str(); + m_core->didOpenTextDocument(args); + } + + void LanguageServer::didCloseTextDocument(std::string uri) + { + Slang::LanguageServerProtocol::DidCloseTextDocumentParams args = {}; + args.textDocument.uri = uri.c_str(); + m_core->didCloseTextDocument(args); + } + + void LanguageServer::didChangeTextDocument(std::string uri, const std::vector<lsp::TextEdit>& changes) + { + Slang::LanguageServerProtocol::DidChangeTextDocumentParams args = {}; + args.textDocument.uri = uri.c_str(); + for (auto change : changes) + { + Slang::LanguageServerProtocol::TextDocumentContentChangeEvent lspChange; + lspChange.text = change.text.c_str(); + lspChange.range = translate(change.range); + args.contentChanges.add(lspChange); + } + m_core->didChangeTextDocument(args); + } + + std::optional<lsp::Hover> LanguageServer::hover(std::string uri, lsp::Position position) + { + Slang::LanguageServerProtocol::HoverParams args = {}; + args.textDocument.uri = uri.c_str(); + args.position = translate(position); + auto coreResult = m_core->hover(args); + if (coreResult.isNull) + return std::nullopt; + lsp::Hover result; + result.contents.kind = coreResult.result.contents.kind.getBuffer(); + result.contents.value = coreResult.result.contents.value.getBuffer(); + result.range = translate(coreResult.result.range); + return result; + } + + std::optional<std::vector<lsp::Location>> LanguageServer::gotoDefinition(std::string uri, lsp::Position position) + { + Slang::LanguageServerProtocol::DefinitionParams args = {}; + args.textDocument.uri = uri.c_str(); + args.position = translate(position); + auto coreResult = m_core->gotoDefinition(args); + if (coreResult.isNull) + return std::nullopt; + std::vector<lsp::Location> result; + for (auto location : coreResult.result) + result.push_back(translate(location)); + return result; + } + + std::optional<std::vector<lsp::CompletionItem>> LanguageServer::completion( + std::string uri, lsp::Position position, CompletionContext context) + { + Slang::LanguageServerProtocol::CompletionParams args = {}; + args.textDocument.uri = uri.c_str(); + args.position = translate(position); + args.context.triggerKind = context.triggerKind; + args.context.triggerCharacter = context.triggerCharacter.c_str(); + auto coreResult = m_core->completion(args); + if (coreResult.isNull) + return std::nullopt; + std::vector<lsp::CompletionItem> result; + for (auto item : coreResult.result.items) + { + lsp::CompletionItem completionItem; + completionItem.label = item.label.getBuffer(); + completionItem.kind = item.kind; + completionItem.detail = item.detail.getBuffer(); + MarkupContent documentation; + documentation.kind = item.documentation.kind.getBuffer(); + documentation.value = item.documentation.value.getBuffer(); + completionItem.documentation = documentation; + completionItem.textEdit = std::nullopt; + completionItem.data = item.data.getBuffer(); + std::vector<std::string> commitCharacters; + for (auto character : item.commitCharacters) + commitCharacters.push_back(character.getBuffer()); + completionItem.commitCharacters = commitCharacters; + result.push_back(completionItem); + } + return result; + } + + std::optional<lsp::CompletionItem> LanguageServer::completionResolve(lsp::CompletionItem args) + { + Slang::LanguageServerProtocol::CompletionItem coreArgs = {}; + coreArgs.label = args.label.c_str(); + coreArgs.kind = args.kind; + coreArgs.detail = args.detail.c_str(); + if (args.documentation.has_value()) + { + coreArgs.documentation.kind = args.documentation.value().kind.c_str(); + coreArgs.documentation.value = args.documentation.value().value.c_str(); + } + coreArgs.data = args.data.c_str(); + if (args.commitCharacters.has_value()) + { + for (auto character : args.commitCharacters.value()) + coreArgs.commitCharacters.add(character.c_str()); + } + Slang::LanguageServerProtocol::TextEditCompletionItem editItem; + editItem.label = coreArgs.label; + editItem.kind = coreArgs.kind; + editItem.detail = coreArgs.detail; + editItem.documentation.kind = coreArgs.documentation.kind; + editItem.documentation.value = coreArgs.documentation.value; + editItem.data = coreArgs.data; + for (auto character : coreArgs.commitCharacters) + editItem.commitCharacters.add(character); + auto coreResult = m_core->completionResolve(coreArgs, editItem); + if (coreResult.isNull) + return std::nullopt; + lsp::CompletionItem result; + result.label = coreResult.result.label.getBuffer(); + result.kind = coreResult.result.kind; + result.detail = coreResult.result.detail.getBuffer(); + MarkupContent documentation; + documentation.kind = coreResult.result.documentation.kind.getBuffer(); + documentation.value = coreResult.result.documentation.value.getBuffer(); + result.documentation = documentation; + result.textEdit = std::nullopt; + result.data = coreResult.result.data.getBuffer(); + std::vector<std::string> commitCharacters; + for (auto character : coreResult.result.commitCharacters) + commitCharacters.push_back(character.getBuffer()); + result.commitCharacters = commitCharacters; + return result; + } + + std::optional<std::vector<uint32_t>> LanguageServer::semanticTokens(std::string uri) + { + Slang::LanguageServerProtocol::SemanticTokensParams args = {}; + args.textDocument.uri = uri.c_str(); + auto coreResult = m_core->semanticTokens(args); + if (coreResult.isNull) + return std::nullopt; + std::vector<uint32_t> result; + result.reserve((size_t)coreResult.result.data.getCount()); + for (auto token : coreResult.result.data) + result.push_back(token); + return result; + } + + std::optional<lsp::SignatureHelp> LanguageServer::signatureHelp(std::string uri, lsp::Position position) + { + Slang::LanguageServerProtocol::SignatureHelpParams args = {}; + args.textDocument.uri = uri.c_str(); + args.position = translate(position); + auto coreResult = m_core->signatureHelp(args); + if (coreResult.isNull) + return std::nullopt; + lsp::SignatureHelp result; + for (auto signature : coreResult.result.signatures) + { + lsp::SignatureInformation signatureInfo; + signatureInfo.label = signature.label.getBuffer(); + signatureInfo.documentation.kind = signature.documentation.kind.getBuffer(); + signatureInfo.documentation.value = signature.documentation.value.getBuffer(); + for (auto parameter : signature.parameters) + { + lsp::ParameterInformation parameterInfo; + parameterInfo.label[0] = parameter.label[0]; + parameterInfo.label[1] = parameter.label[1]; + parameterInfo.documentation.kind = parameter.documentation.kind.getBuffer(); + parameterInfo.documentation.value = parameter.documentation.value.getBuffer(); + signatureInfo.parameters.push_back(parameterInfo); + } + result.signatures.push_back(signatureInfo); + } + result.activeSignature = coreResult.result.activeSignature; + result.activeParameter = coreResult.result.activeParameter; + return result; + } + + lsp::DocumentSymbol translate(Slang::LanguageServerProtocol::DocumentSymbol symbol) + { + lsp::DocumentSymbol result; + result.name = symbol.name.getBuffer(); + result.detail = symbol.detail.getBuffer(); + result.kind = symbol.kind; + result.range = translate(symbol.range); + result.selectionRange = translate(symbol.selectionRange); + for (auto child : symbol.children) + result.children.push_back(translate(child)); + return result; + } + + std::optional<std::vector<lsp::DocumentSymbol>> LanguageServer::documentSymbol(std::string uri) + { + Slang::LanguageServerProtocol::DocumentSymbolParams args = {}; + args.textDocument.uri = uri.c_str(); + auto coreResult = m_core->documentSymbol(args); + if (coreResult.isNull) + return std::nullopt; + std::vector<lsp::DocumentSymbol> result; + for (auto symbol : coreResult.result) + { + auto documentSymbol = translate(symbol); + result.push_back(documentSymbol); + } + return result; + } + + std::optional<std::vector<lsp::Diagnostics>> LanguageServer::getDiagnostics(std::string uri) + { + std::vector<lsp::Diagnostics> result; + auto module = m_core->m_workspace->getCurrentVersion()->getOrLoadModule( + Slang::URI::fromString(Slang::UnownedStringSlice(uri.c_str())).getPath()); + if (!module) + return std::nullopt; + for (auto& docDiag: m_core->m_workspace->getCurrentVersion()->diagnostics) + { + for (auto& message: docDiag.second.messages) + { + lsp::Diagnostics diag; + diag.code = Slang::String(message.code).getBuffer(); + diag.range = translate(message.range); + diag.severity = (int)message.severity; + diag.message = message.message.getBuffer(); + result.push_back(diag); + } + } + return result; + } + + + LanguageServer* createLanguageServer() + { + return new LanguageServer(); + } + +} + } // namespace wgsl } // namespace slang diff --git a/source/slang-wasm/slang-wasm.h b/source/slang-wasm/slang-wasm.h index a54cfe1ea..5a299453c 100644 --- a/source/slang-wasm/slang-wasm.h +++ b/source/slang-wasm/slang-wasm.h @@ -4,6 +4,11 @@ #include <unordered_map> #include <emscripten/val.h> +namespace Slang +{ + class LanguageServerCore; +} + namespace slang { namespace wgsl @@ -118,5 +123,123 @@ private: GlobalSession* createGlobalSession(); +namespace lsp +{ + struct Position + { + int line = -1; + int character = -1; + }; + + struct Range + { + Position start; + Position end; + }; + + struct Location + { + std::string uri; + Range range; + }; + + struct TextEdit + { + Range range; + std::string text; + }; + + struct MarkupContent + { + std::string kind; + std::string value; + }; + + struct Hover + { + MarkupContent contents; + Range range; + }; + + struct CompletionItem + { + std::string label; + int kind; + std::string detail; + std::string data; + std::optional<MarkupContent> documentation; + std::optional<TextEdit> textEdit; + std::optional<std::vector<std::string>> commitCharacters; + }; + + struct CompletionContext + { + int triggerKind = 1; + std::string triggerCharacter; + }; + + struct ParameterInformation + { + uint32_t label[2] = { 0, 0 }; + MarkupContent documentation; + }; + + struct SignatureInformation + { + std::string label; + MarkupContent documentation; + std::vector<ParameterInformation> parameters; + }; + + struct SignatureHelp + { + std::vector<SignatureInformation> signatures; + uint32_t activeSignature = 0; + uint32_t activeParameter = 0; + }; + + struct DocumentSymbol + { + std::string name; + std::string detail; + int kind = 0; + Range range; + Range selectionRange; + std::vector<DocumentSymbol> children; + }; + + struct Diagnostics + { + std::string code; + Range range; + std::string message; + int severity; + }; + + class LanguageServer + { + private: + Slang::LanguageServerCore* m_core = nullptr; + void init(); + public: + LanguageServer(); + ~LanguageServer(); + void didOpenTextDocument(std::string uri, std::string text); + void didCloseTextDocument(std::string uri); + void didChangeTextDocument(std::string uri, const std::vector<lsp::TextEdit>& changes); + std::optional<lsp::Hover> hover(std::string uri, lsp::Position position); + std::optional<std::vector<lsp::Location>> gotoDefinition(std::string uri, lsp::Position position); + std::optional<std::vector<lsp::CompletionItem>> completion( + std::string uri, lsp::Position position, CompletionContext context); + std::optional<lsp::CompletionItem> completionResolve(lsp::CompletionItem args); + std::optional<std::vector<uint32_t>> semanticTokens(std::string uri); + std::optional<lsp::SignatureHelp> signatureHelp(std::string uri, lsp::Position position); + std::optional<std::vector<lsp::DocumentSymbol>> documentSymbol(std::string uri); + std::optional<std::vector<lsp::Diagnostics>> getDiagnostics(std::string uri); + }; + + LanguageServer* createLanguageServer(); +} + } // namespace wgsl } // namespace slang diff --git a/source/slang/slang-language-server-completion.cpp b/source/slang/slang-language-server-completion.cpp index 14613d08b..074336ea6 100644 --- a/source/slang/slang-language-server-completion.cpp +++ b/source/slang/slang-language-server-completion.cpp @@ -84,7 +84,7 @@ bool isDeclKeyword(const UnownedStringSlice& slice) return false; } -SlangResult CompletionContext::tryCompleteHLSLSemantic() +LanguageServerResult<CompletionResult> CompletionContext::tryCompleteHLSLSemantic() { if (version->linkage->contentAssistInfo.completionSuggestions.scopeKind != CompletionSuggestions::ScopeKind::HLSLSemantics) @@ -99,23 +99,20 @@ SlangResult CompletionContext::tryCompleteHLSLSemantic() item.kind = LanguageServerProtocol::kCompletionItemKindKeyword; items.add(item); } - server->m_connection->sendResult(&items, responseId); - return SLANG_OK; + return CompletionResult(_Move(items)); } -SlangResult CompletionContext::tryCompleteAttributes() +LanguageServerResult<CompletionResult> CompletionContext::tryCompleteAttributes() { if (version->linkage->contentAssistInfo.completionSuggestions.scopeKind != CompletionSuggestions::ScopeKind::Attribute) { return SLANG_FAIL; } - List<LanguageServerProtocol::CompletionItem> items = collectAttributes(); - server->m_connection->sendResult(&items, responseId); - return SLANG_OK; + return collectAttributes(); } -List<LanguageServerProtocol::TextEditCompletionItem> CompletionContext::gatherFileAndModuleCompletionItems( +CompletionResult CompletionContext::gatherFileAndModuleCompletionItems( const String& prefixPath, bool translateModuleName, bool isImportString, @@ -299,10 +296,10 @@ List<LanguageServerProtocol::TextEditCompletionItem> CompletionContext::gatherFi } } } - return context.items; + return CompletionResult(_Move(context.items)); } -SlangResult CompletionContext::tryCompleteImport() +LanguageServerResult<CompletionResult> CompletionContext::tryCompleteImport() { const char* prefixes[] = { "import ", "__include ", "implementing " }; UnownedStringSlice lineContent; @@ -358,13 +355,11 @@ validLine:; prefixSB.appendChar(ch); } auto prefix = prefixSB.produceString(); - auto items = gatherFileAndModuleCompletionItems( + return gatherFileAndModuleCompletionItems( prefix, true, false, line - 1, fileNameEnd, lastPos + 1, sectionEnd, 0); - server->m_connection->sendResult(&items, responseId); - return SLANG_OK; } -SlangResult CompletionContext::tryCompleteRawFileName(UnownedStringSlice lineContent, Index pos, bool isImportString) +LanguageServerResult<CompletionResult> CompletionContext::tryCompleteRawFileName(UnownedStringSlice lineContent, Index pos, bool isImportString) { while (pos < lineContent.getLength() && (lineContent[pos] != '\"' && lineContent[pos] != '<')) pos++; @@ -405,7 +400,7 @@ SlangResult CompletionContext::tryCompleteRawFileName(UnownedStringSlice lineCon prefixSB.appendChar(ch); } auto prefix = prefixSB.produceString(); - auto items = gatherFileAndModuleCompletionItems( + return gatherFileAndModuleCompletionItems( prefix, false, isImportString, @@ -414,11 +409,9 @@ SlangResult CompletionContext::tryCompleteRawFileName(UnownedStringSlice lineCon lastPos + 1, sectionEnd, closingChar); - server->m_connection->sendResult(&items, responseId); - return SLANG_OK; } -SlangResult CompletionContext::tryCompleteInclude() +LanguageServerResult<CompletionResult> CompletionContext::tryCompleteInclude() { auto lineContent = doc->getLine(line); if (!lineContent.startsWith("#")) @@ -437,14 +430,12 @@ SlangResult CompletionContext::tryCompleteInclude() return tryCompleteRawFileName(lineContent, pos, false); } -SlangResult CompletionContext::tryCompleteMemberAndSymbol() +LanguageServerResult<CompletionResult> CompletionContext::tryCompleteMemberAndSymbol() { - List<LanguageServerProtocol::CompletionItem> items = collectMembersAndSymbols(); - server->m_connection->sendResult(&items, responseId); - return SLANG_OK; + return collectMembersAndSymbols(); } -List<LanguageServerProtocol::CompletionItem> CompletionContext::collectMembersAndSymbols() +CompletionResult CompletionContext::collectMembersAndSymbols() { auto linkage = version->linkage; if (linkage->contentAssistInfo.completionSuggestions.scopeKind == @@ -611,7 +602,7 @@ List<LanguageServerProtocol::CompletionItem> CompletionContext::collectMembersAn return result; } -List<LanguageServerProtocol::CompletionItem> CompletionContext::createCapabilityCandidates() +CompletionResult CompletionContext::createCapabilityCandidates() { List<LanguageServerProtocol::CompletionItem> result; List<UnownedStringSlice> names; @@ -629,7 +620,7 @@ List<LanguageServerProtocol::CompletionItem> CompletionContext::createCapability return result; } -List<LanguageServerProtocol::CompletionItem> CompletionContext::createSwizzleCandidates( +CompletionResult CompletionContext::createSwizzleCandidates( Type* type, IntegerLiteralValue elementCount[2]) { List<LanguageServerProtocol::CompletionItem> result; @@ -738,7 +729,7 @@ LanguageServerProtocol::CompletionItem CompletionContext::generateGUIDCompletion return resultItem; } -List<LanguageServerProtocol::CompletionItem> CompletionContext::collectAttributes() +CompletionResult CompletionContext::collectAttributes() { List<LanguageServerProtocol::CompletionItem> result; for (auto& item : version->linkage->contentAssistInfo.completionSuggestions.candidateItems) diff --git a/source/slang/slang-language-server-completion.h b/source/slang/slang-language-server-completion.h index d3910bcfd..0158a709f 100644 --- a/source/slang/slang-language-server-completion.h +++ b/source/slang/slang-language-server-completion.h @@ -3,10 +3,11 @@ #include "slang-workspace-version.h" #include "slang-language-server-ast-lookup.h" +#include "../compiler-core/slang-language-server-protocol.h" namespace Slang { -class LanguageServer; +class LanguageServerCore; enum class CommitCharacterBehavior { @@ -15,34 +16,46 @@ enum class CommitCharacterBehavior All }; +struct CompletionResult +{ + List<LanguageServerProtocol::CompletionItem> items; + List<LanguageServerProtocol::TextEditCompletionItem> textEditItems; + CompletionResult() = default; + CompletionResult(List<LanguageServerProtocol::CompletionItem>&& other) + :items(_Move(other)) + {} + CompletionResult(List<LanguageServerProtocol::TextEditCompletionItem>&& other) + :textEditItems(_Move(other)) + {} +}; + struct CompletionContext { - LanguageServer* server; + LanguageServerCore* server; Index cursorOffset; WorkspaceVersion* version; DocumentVersion* doc; Module* parsedModule; - JSONValue responseId; UnownedStringSlice canonicalPath; CommitCharacterBehavior commitCharacterBehavior; Int line; Int col; - SlangResult tryCompleteMemberAndSymbol(); - SlangResult tryCompleteHLSLSemantic(); - SlangResult tryCompleteAttributes(); - SlangResult tryCompleteImport(); - SlangResult tryCompleteInclude(); - SlangResult tryCompleteRawFileName(UnownedStringSlice lineContent, Index fileNameStartPos, bool isImportString); + LanguageServerResult<CompletionResult> tryCompleteMemberAndSymbol(); + LanguageServerResult<CompletionResult> tryCompleteHLSLSemantic(); + LanguageServerResult<CompletionResult> tryCompleteAttributes(); + LanguageServerResult<CompletionResult> tryCompleteImport(); + LanguageServerResult<CompletionResult> tryCompleteInclude(); + LanguageServerResult<CompletionResult> tryCompleteRawFileName(UnownedStringSlice lineContent, Index fileNameStartPos, bool isImportString); - List<LanguageServerProtocol::CompletionItem> collectMembersAndSymbols(); - List<LanguageServerProtocol::CompletionItem> createSwizzleCandidates( + CompletionResult collectMembersAndSymbols(); + CompletionResult createSwizzleCandidates( Type* baseType, IntegerLiteralValue elementCount[2]); - List<LanguageServerProtocol::CompletionItem> createCapabilityCandidates(); - List<LanguageServerProtocol::CompletionItem> collectAttributes(); + CompletionResult createCapabilityCandidates(); + CompletionResult collectAttributes(); LanguageServerProtocol::CompletionItem generateGUIDCompletionItem(); - List<LanguageServerProtocol::TextEditCompletionItem> gatherFileAndModuleCompletionItems( + CompletionResult gatherFileAndModuleCompletionItems( const String& prefixPath, bool translateModuleName, bool isImportString, diff --git a/source/slang/slang-language-server.cpp b/source/slang/slang-language-server.cpp index 94debde40..80d9c2ef8 100644 --- a/source/slang/slang-language-server.cpp +++ b/source/slang/slang-language-server.cpp @@ -30,6 +30,9 @@ #include "slang-mangle.h" #include "../../tools/platform/performance-counter.h" +#define SLANG_LS_RETURN_ON_SUCCESS(x) { auto _res = (x); if (_res.returnCode == SLANG_OK) return _res; } +#define SLANG_LS_RETURN_ON_FAIL(x) { auto _res = (x); if (SLANG_FAILED(_res.returnCode)) return _res; } + namespace Slang { using namespace LanguageServerProtocol; @@ -42,12 +45,8 @@ ArrayView<const char*> getCommitChars() return makeArrayView(_commitCharsArray, SLANG_COUNT_OF(_commitCharsArray)); } -SlangResult LanguageServer::init(const InitializeParams& args) +SlangResult LanguageServerCore::init(const InitializeParams& args) { - SLANG_RETURN_ON_FAIL(m_connection->initWithStdStreams(JSONRPCConnection::CallStyle::Object)); - - m_typeMap = JSONNativeUtil::getTypeFuncsMap(); - m_workspaceFolders = args.workspaceFolders; m_workspace = new Workspace(); List<URI> rootUris; @@ -59,7 +58,16 @@ SlangResult LanguageServer::init(const InitializeParams& args) return SLANG_OK; } -slang::IGlobalSession* LanguageServer::getOrCreateGlobalSession() +SlangResult LanguageServer::init(const InitializeParams& args) +{ + SLANG_RETURN_ON_FAIL(m_connection->initWithStdStreams(JSONRPCConnection::CallStyle::Object)); + + m_typeMap = JSONNativeUtil::getTypeFuncsMap(); + + return m_core.init(args); +} + +slang::IGlobalSession* LanguageServerCore::getOrCreateGlobalSession() { if (!m_session) { @@ -146,7 +154,7 @@ SlangResult LanguageServer::parseNextMessage() serverInfo.name = "SlangLanguageServer"; serverInfo.version = "1.8"; - if (m_options.isVisualStudio) + if (m_core.m_options.isVisualStudio) { VSInitializeResult vsResult; vsResult.serverInfo = serverInfo; @@ -220,13 +228,18 @@ SlangResult LanguageServer::parseNextMessage() } } -SlangResult LanguageServer::didOpenTextDocument(const DidOpenTextDocumentParams& args) +SlangResult LanguageServerCore::didOpenTextDocument(const DidOpenTextDocumentParams& args) { String canonicalPath = uriToCanonicalPath(args.textDocument.uri); m_workspace->openDoc(canonicalPath, args.textDocument.text); return SLANG_OK; } +SlangResult LanguageServer::didOpenTextDocument(const DidOpenTextDocumentParams& args) +{ + return m_core.didOpenTextDocument(args); +} + static bool isBoolType(Type* t) { auto basicType = as<BasicExpressionType>(t); @@ -515,12 +528,24 @@ HumaneSourceLoc getModuleLoc(SourceManager* manager, ContainerDecl* moduleDecl) SlangResult LanguageServer::hover( const LanguageServerProtocol::HoverParams& args, const JSONValue& responseId) { + auto result = m_core.hover(args); + if (SLANG_FAILED(result.returnCode) || result.isNull) + { + m_connection->sendResult(NullResponse::get(), responseId); + return SLANG_OK; + } + m_connection->sendResult(&result.result, responseId); + return SLANG_OK; +} + +LanguageServerResult<LanguageServerProtocol::Hover> LanguageServerCore::hover( + const LanguageServerProtocol::HoverParams& args) +{ String canonicalPath = uriToCanonicalPath(args.textDocument.uri); RefPtr<DocumentVersion> doc; if (!m_workspace->openedDocuments.tryGetValue(canonicalPath, doc)) { - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + return std::nullopt; } Index line, col; doc->zeroBasedUTF16LocToOneBasedUTF8Loc(args.position.line, args.position.character, line, col); @@ -531,8 +556,7 @@ SlangResult LanguageServer::hover( Module* parsedModule = version->getOrLoadModule(canonicalPath); if (!parsedModule) { - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + return LanguageServerResult<LanguageServerProtocol::Hover>(); } auto findResult = findASTNodesAt( doc.Ptr(), @@ -544,10 +568,8 @@ SlangResult LanguageServer::hover( col); if (findResult.getCount() == 0 || findResult[0].path.getCount() == 0) { - if (SLANG_SUCCEEDED(tryGetMacroHoverInfo(version, doc, line, col, responseId))) - return SLANG_OK; - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + SLANG_LS_RETURN_ON_SUCCESS(tryGetMacroHoverInfo(version, doc, line, col)); + return std::nullopt; } StringBuilder sb; @@ -836,27 +858,37 @@ SlangResult LanguageServer::hover( } if (sb.getLength() == 0) { - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + return std::nullopt; } else { hover.contents.kind = "markdown"; hover.contents.value = sb.produceString(); - m_connection->sendResult(&hover, responseId); - return SLANG_OK; + return hover; } } SlangResult LanguageServer::gotoDefinition( const LanguageServerProtocol::DefinitionParams& args, const JSONValue& responseId) { + auto result = m_core.gotoDefinition(args); + if (SLANG_FAILED(result.returnCode) || result.isNull) + { + m_connection->sendResult(NullResponse::get(), responseId); + return SLANG_OK; + } + m_connection->sendResult(&result.result, responseId); + return SLANG_OK; +} + +LanguageServerResult<List<LanguageServerProtocol::Location>> LanguageServerCore::gotoDefinition( + const LanguageServerProtocol::DefinitionParams& args) +{ String canonicalPath = uriToCanonicalPath(args.textDocument.uri); RefPtr<DocumentVersion> doc; if (!m_workspace->openedDocuments.tryGetValue(canonicalPath, doc)) { - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + return std::nullopt; } Index line, col; doc->zeroBasedUTF16LocToOneBasedUTF8Loc(args.position.line, args.position.character, line, col); @@ -867,8 +899,7 @@ SlangResult LanguageServer::gotoDefinition( Module* parsedModule = version->getOrLoadModule(canonicalPath); if (!parsedModule) { - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + return std::nullopt; } auto findResult = findASTNodesAt( doc.Ptr(), @@ -880,12 +911,9 @@ SlangResult LanguageServer::gotoDefinition( col); if (findResult.getCount() == 0 || findResult[0].path.getCount() == 0) { - if (SLANG_SUCCEEDED(tryGotoMacroDefinition(version, doc, line, col, responseId))) - return SLANG_OK; - if (SLANG_SUCCEEDED(tryGotoFileInclude(version, doc, line, responseId))) - return SLANG_OK; - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + SLANG_LS_RETURN_ON_SUCCESS(tryGotoMacroDefinition(version, doc, line, col)); + SLANG_LS_RETURN_ON_SUCCESS(tryGotoFileInclude(version, doc, line)); + return std::nullopt; } struct LocationResult { @@ -948,8 +976,7 @@ SlangResult LanguageServer::gotoDefinition( } if (locations.getCount() == 0) { - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + return std::nullopt; } else { @@ -971,8 +998,7 @@ SlangResult LanguageServer::gotoDefinition( results.add(result); } } - m_connection->sendResult(&results, responseId); - return SLANG_OK; + return results; } } @@ -989,13 +1015,25 @@ template <typename Func> Deferred<Func> makeDeferred(const Func& f) { return Def SlangResult LanguageServer::completion( const LanguageServerProtocol::CompletionParams& args, const JSONValue& responseId) { + auto result = m_core.completion(args); + if (SLANG_FAILED(result.returnCode) || result.isNull) + m_connection->sendResult(NullResponse::get(), responseId); + else if (result.result.items.getCount()) + m_connection->sendResult(&result.result.items, responseId); + else + m_connection->sendResult(&result.result.textEditItems, responseId); + return SLANG_OK; +} + +LanguageServerResult<CompletionResult> LanguageServerCore::completion( + const LanguageServerProtocol::CompletionParams& args) +{ String canonicalPath = uriToCanonicalPath(args.textDocument.uri); RefPtr<DocumentVersion> doc; if (!m_workspace->openedDocuments.tryGetValue(canonicalPath, doc)) { - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + return std::nullopt; } // Don't show completion at case label or after single '>' operator. @@ -1014,8 +1052,7 @@ SlangResult LanguageServer::completion( auto prevCharPos = args.position.character - 2; if (prevCharPos >= 0 && prevCharPos < line.getLength() && line[prevCharPos] != requiredPrevChar) { - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + return std::nullopt; } } } @@ -1026,8 +1063,7 @@ SlangResult LanguageServer::completion( auto cursorOffset = doc->getOffset(utf8Line, utf8Col); if (cursorOffset == -1 || doc->getText().getLength() == 0) { - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + return std::nullopt; } // Ajust cursor position to the beginning of the current/last identifier. @@ -1040,8 +1076,7 @@ SlangResult LanguageServer::completion( // Never show suggestions when the user is typing a number. if (cursorOffset + 1 >= 0 && cursorOffset + 1 < doc->getText().getLength() && CharUtil::isDigit(doc->getText()[cursorOffset + 1])) { - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + return std::nullopt; } // Always create a new workspace version for the completion request since we @@ -1057,7 +1092,6 @@ SlangResult LanguageServer::completion( context.cursorOffset = cursorOffset; context.version = version; context.doc = doc.Ptr(); - context.responseId = responseId; context.canonicalPath = canonicalPath.getUnownedSlice(); context.line = utf8Line; context.col = utf8Col; @@ -1069,22 +1103,15 @@ SlangResult LanguageServer::completion( context.commitCharacterBehavior = CommitCharacterBehavior::Disabled; } - if (SLANG_SUCCEEDED(context.tryCompleteInclude())) - { - return SLANG_OK; - } - if (SLANG_SUCCEEDED(context.tryCompleteImport())) - { - return SLANG_OK; - } + SLANG_LS_RETURN_ON_SUCCESS(context.tryCompleteInclude()); + SLANG_LS_RETURN_ON_SUCCESS(context.tryCompleteImport()); if (args.context.triggerKind == LanguageServerProtocol::kCompletionTriggerKindTriggerCharacter && (args.context.triggerCharacter == "\"" || args.context.triggerCharacter == "/")) { // Trigger characters '"' and '/' are for include only. - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + return std::nullopt; } @@ -1099,49 +1126,48 @@ SlangResult LanguageServer::completion( Module* parsedModule = version->getOrLoadModule(canonicalPath); if (!parsedModule) { - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + return std::nullopt; } context.parsedModule = parsedModule; - if (SLANG_SUCCEEDED(context.tryCompleteAttributes())) - { - return SLANG_OK; - } + SLANG_LS_RETURN_ON_SUCCESS(context.tryCompleteAttributes()); // Don't generate completion suggestions after typing '['. if (args.context.triggerKind == LanguageServerProtocol::kCompletionTriggerKindTriggerCharacter && args.context.triggerCharacter == "[") { - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + return std::nullopt; } - if (SLANG_SUCCEEDED(context.tryCompleteHLSLSemantic())) - { - return SLANG_OK; - } - if (SLANG_SUCCEEDED(context.tryCompleteMemberAndSymbol())) + SLANG_LS_RETURN_ON_SUCCESS(context.tryCompleteHLSLSemantic()); + SLANG_LS_RETURN_ON_SUCCESS(context.tryCompleteMemberAndSymbol()); + return std::nullopt; +} + +SlangResult LanguageServer::completionResolve( + const LanguageServerProtocol::CompletionItem& args, const LanguageServerProtocol::TextEditCompletionItem& editItem, const JSONValue& responseId) +{ + auto result = m_core.completionResolve(args, editItem); + if (SLANG_FAILED(result.returnCode) || result.isNull) { + m_connection->sendResult(NullResponse::get(), responseId); return SLANG_OK; } - m_connection->sendResult(NullResponse::get(), responseId); + m_connection->sendResult(&result.result, responseId); return SLANG_OK; } -SlangResult LanguageServer::completionResolve( - const LanguageServerProtocol::CompletionItem& args, const LanguageServerProtocol::TextEditCompletionItem& editItem, const JSONValue& responseId) +LanguageServerResult<LanguageServerProtocol::CompletionItem> LanguageServerCore::completionResolve( + const LanguageServerProtocol::CompletionItem& args, const LanguageServerProtocol::TextEditCompletionItem& editItem) { if (args.data.getLength() == 0) { if (editItem.textEdit.newText.getLength()) { - m_connection->sendResult(&editItem, responseId); - return SLANG_OK; + return std::nullopt; } - m_connection->sendResult(&args, responseId); - return SLANG_OK; + return args; } LanguageServerProtocol::CompletionItem resolvedItem = args; @@ -1149,8 +1175,7 @@ SlangResult LanguageServer::completionResolve( auto version = m_workspace->getCurrentCompletionVersion(); if (!version || !version->linkage) { - m_connection->sendResult(&resolvedItem, responseId); - return SLANG_OK; + return resolvedItem; } SLANG_AST_BUILDER_RAII(version->linkage->getASTBuilder()); auto& candidateItems = version->linkage->contentAssistInfo.completionSuggestions.candidateItems; @@ -1163,20 +1188,31 @@ SlangResult LanguageServer::completionResolve( resolvedItem.documentation.value = docSB.produceString(); resolvedItem.documentation.kind = "markdown"; } - m_connection->sendResult(&resolvedItem, responseId); - return SLANG_OK; + return resolvedItem; } SlangResult LanguageServer::semanticTokens( const LanguageServerProtocol::SemanticTokensParams& args, const JSONValue& responseId) { + auto result = m_core.semanticTokens(args); + if (SLANG_FAILED(result.returnCode) || result.isNull) + { + m_connection->sendResult(NullResponse::get(), responseId); + return SLANG_OK; + } + m_connection->sendResult(&result.result, responseId); + return SLANG_OK; +} + +LanguageServerResult<LanguageServerProtocol::SemanticTokens> LanguageServerCore::semanticTokens( + const LanguageServerProtocol::SemanticTokensParams& args) +{ String canonicalPath = uriToCanonicalPath(args.textDocument.uri); RefPtr<DocumentVersion> doc; if (!m_workspace->openedDocuments.tryGetValue(canonicalPath, doc)) { - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + return std::nullopt; } auto version = m_workspace->getCurrentVersion(); @@ -1185,8 +1221,7 @@ SlangResult LanguageServer::semanticTokens( Module* parsedModule = version->getOrLoadModule(canonicalPath); if (!parsedModule) { - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + return std::nullopt; } auto tokens = getSemanticTokens(version->linkage, parsedModule, canonicalPath.getUnownedSlice(), doc.Ptr()); @@ -1204,11 +1239,10 @@ SlangResult LanguageServer::semanticTokens( SemanticTokens response; response.resultId = ""; response.data = getEncodedTokens(tokens); - m_connection->sendResult(&response, responseId); - return SLANG_OK; + return response; } -String LanguageServer::getExprDeclSignature(Expr* expr, String* outDocumentation, List<Slang::Range<Index>>* outParamRanges) +String LanguageServerCore::getExprDeclSignature(Expr* expr, String* outDocumentation, List<Slang::Range<Index>>* outParamRanges) { if (auto declRefExpr = as<DeclRefExpr>(expr)) { @@ -1285,7 +1319,7 @@ String LanguageServer::getExprDeclSignature(Expr* expr, String* outDocumentation return printer.getString(); } -String LanguageServer::getDeclRefSignature(DeclRef<Decl> declRef, String* outDocumentation, List<Slang::Range<Index>>* outParamRanges) +String LanguageServerCore::getDeclRefSignature(DeclRef<Decl> declRef, String* outDocumentation, List<Slang::Range<Index>>* outParamRanges) { auto version = m_workspace->getCurrentVersion(); SLANG_AST_BUILDER_RAII(version->linkage->getASTBuilder()); @@ -1314,12 +1348,24 @@ String LanguageServer::getDeclRefSignature(DeclRef<Decl> declRef, String* outDoc SlangResult LanguageServer::signatureHelp( const LanguageServerProtocol::SignatureHelpParams& args, const JSONValue& responseId) { + auto result = m_core.signatureHelp(args); + if (SLANG_FAILED(result.returnCode) || result.isNull) + { + m_connection->sendResult(NullResponse::get(), responseId); + return SLANG_OK; + } + m_connection->sendResult(&result.result, responseId); + return SLANG_OK; +} + +LanguageServerResult<LanguageServerProtocol::SignatureHelp> LanguageServerCore::signatureHelp( + const LanguageServerProtocol::SignatureHelpParams& args) +{ String canonicalPath = uriToCanonicalPath(args.textDocument.uri); RefPtr<DocumentVersion> doc; if (!m_workspace->openedDocuments.tryGetValue(canonicalPath, doc)) { - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + return std::nullopt; } Index line, col; doc->zeroBasedUTF16LocToOneBasedUTF8Loc(args.position.line, args.position.character, line, col); @@ -1330,8 +1376,7 @@ SlangResult LanguageServer::signatureHelp( Module* parsedModule = version->getOrLoadModule(canonicalPath); if (!parsedModule) { - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + return std::nullopt; } auto findResult = findASTNodesAt( @@ -1345,8 +1390,7 @@ SlangResult LanguageServer::signatureHelp( if (findResult.getCount() == 0) { - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + return std::nullopt; } AppExprBase* appExpr = nullptr; @@ -1373,14 +1417,12 @@ SlangResult LanguageServer::signatureHelp( } if (!appExpr) { - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + return std::nullopt; } if (appExpr->argumentDelimeterLocs.getCount() == 0) { - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + return std::nullopt; } auto funcExpr = appExpr->functionExpr; @@ -1399,8 +1441,7 @@ SlangResult LanguageServer::signatureHelp( } if (!funcExpr) { - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + return std::nullopt; } SignatureHelp response; @@ -1534,19 +1575,30 @@ SlangResult LanguageServer::signatureHelp( } } - m_connection->sendResult(&response, responseId); - return SLANG_OK; + return response; } SlangResult LanguageServer::documentSymbol( const LanguageServerProtocol::DocumentSymbolParams& args, const JSONValue& responseId) { + auto result = m_core.documentSymbol(args); + if (SLANG_FAILED(result.returnCode) || result.isNull) + { + m_connection->sendResult(NullResponse::get(), responseId); + return SLANG_OK; + } + m_connection->sendResult(&result.result, responseId); + return SLANG_OK; +} + +LanguageServerResult<List<LanguageServerProtocol::DocumentSymbol>> LanguageServerCore::documentSymbol( + const LanguageServerProtocol::DocumentSymbolParams& args) +{ String canonicalPath = uriToCanonicalPath(args.textDocument.uri); RefPtr<DocumentVersion> doc; if (!m_workspace->openedDocuments.tryGetValue(canonicalPath, doc)) { - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + return std::nullopt; } auto version = m_workspace->getCurrentVersion(); SLANG_AST_BUILDER_RAII(version->linkage->getASTBuilder()); @@ -1554,22 +1606,31 @@ SlangResult LanguageServer::documentSymbol( Module* parsedModule = version->getOrLoadModule(canonicalPath); if (!parsedModule) { + return std::nullopt; + } + List<DocumentSymbol> symbols = getDocumentSymbols(version->linkage, parsedModule, canonicalPath.getUnownedSlice(), doc.Ptr()); + return symbols; +} + +SlangResult LanguageServer::inlayHint(const LanguageServerProtocol::InlayHintParams& args, const JSONValue& responseId) +{ + auto result = m_core.inlayHint(args); + if (SLANG_FAILED(result.returnCode) || result.isNull) + { m_connection->sendResult(NullResponse::get(), responseId); return SLANG_OK; } - List<DocumentSymbol> symbols = getDocumentSymbols(version->linkage, parsedModule, canonicalPath.getUnownedSlice(), doc.Ptr()); - m_connection->sendResult(&symbols, responseId); + m_connection->sendResult(&result.result, responseId); return SLANG_OK; } -SlangResult LanguageServer::inlayHint(const LanguageServerProtocol::InlayHintParams& args, const JSONValue& responseId) +LanguageServerResult<List<LanguageServerProtocol::InlayHint>> LanguageServerCore::inlayHint(const LanguageServerProtocol::InlayHintParams& args) { String canonicalPath = uriToCanonicalPath(args.textDocument.uri); RefPtr<DocumentVersion> doc; if (!m_workspace->openedDocuments.tryGetValue(canonicalPath, doc)) { - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + return std::nullopt; } auto version = m_workspace->getCurrentVersion(); SLANG_AST_BUILDER_RAII(version->linkage->getASTBuilder()); @@ -1577,8 +1638,7 @@ SlangResult LanguageServer::inlayHint(const LanguageServerProtocol::InlayHintPar Module* parsedModule = version->getOrLoadModule(canonicalPath); if (!parsedModule) { - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + return std::nullopt; } List<InlayHint> hints = getInlayHints( version->linkage, @@ -1587,8 +1647,7 @@ SlangResult LanguageServer::inlayHint(const LanguageServerProtocol::InlayHintPar doc.Ptr(), args.range, m_inlayHintOptions); - m_connection->sendResult(&hints, responseId); - return SLANG_OK; + return hints; } List<LanguageServerProtocol::TextEdit> translateTextEdits(DocumentVersion* doc, List<Edit>& edits) @@ -1615,12 +1674,24 @@ List<LanguageServerProtocol::TextEdit> translateTextEdits(DocumentVersion* doc, SlangResult LanguageServer::formatting(const LanguageServerProtocol::DocumentFormattingParams& args, const JSONValue& responseId) { + auto result = m_core.formatting(args); + if (SLANG_FAILED(result.returnCode) || result.isNull) + { + m_connection->sendResult(NullResponse::get(), responseId); + return SLANG_OK; + } + m_connection->sendResult(&result.result, responseId); + return SLANG_OK; +} + +LanguageServerResult<List<LanguageServerProtocol::TextEdit>> LanguageServerCore::formatting( + const LanguageServerProtocol::DocumentFormattingParams& args) +{ String canonicalPath = uriToCanonicalPath(args.textDocument.uri); RefPtr<DocumentVersion> doc; if (!m_workspace->openedDocuments.tryGetValue(canonicalPath, doc)) { - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + return std::nullopt; } if (m_formatOptions.clangFormatLocation.getLength() == 0) m_formatOptions.clangFormatLocation = findClangFormatTool(); @@ -1629,18 +1700,29 @@ SlangResult LanguageServer::formatting(const LanguageServerProtocol::DocumentFor List<TextRange> exclusionRange = extractFormattingExclusionRanges(doc->getText().getUnownedSlice()); auto edits = formatSource(doc->getText().getUnownedSlice(), -1, -1, -1, exclusionRange, options); auto textEdits = translateTextEdits(doc, edits); - m_connection->sendResult(&textEdits, responseId); - return SLANG_OK; + return textEdits; } SlangResult LanguageServer::rangeFormatting(const LanguageServerProtocol::DocumentRangeFormattingParams& args, const JSONValue& responseId) { + auto result = m_core.rangeFormatting(args); + if (SLANG_FAILED(result.returnCode) || result.isNull) + { + m_connection->sendResult(NullResponse::get(), responseId); + return SLANG_OK; + } + m_connection->sendResult(&result.result, responseId); + return SLANG_OK; +} + +LanguageServerResult<List<LanguageServerProtocol::TextEdit>> LanguageServerCore::rangeFormatting( + const LanguageServerProtocol::DocumentRangeFormattingParams& args) +{ String canonicalPath = uriToCanonicalPath(args.textDocument.uri); RefPtr<DocumentVersion> doc; if (!m_workspace->openedDocuments.tryGetValue(canonicalPath, doc)) { - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + return std::nullopt; } Index endLine, endCol; doc->zeroBasedUTF16LocToOneBasedUTF8Loc(args.range.end.line, args.range.end.character, endLine, endCol); @@ -1653,23 +1735,33 @@ SlangResult LanguageServer::rangeFormatting(const LanguageServerProtocol::Docume List<TextRange> exclusionRange = extractFormattingExclusionRanges(doc->getText().getUnownedSlice()); auto edits = formatSource(doc->getText().getUnownedSlice(), args.range.start.line, args.range.end.line, endOffset, exclusionRange, options); auto textEdits = translateTextEdits(doc, edits); - m_connection->sendResult(&textEdits, responseId); - return SLANG_OK; + return textEdits; } SlangResult LanguageServer::onTypeFormatting(const LanguageServerProtocol::DocumentOnTypeFormattingParams& args, const JSONValue& responseId) { + auto result = m_core.onTypeFormatting(args); + if (SLANG_FAILED(result.returnCode) || result.isNull) + { + m_connection->sendResult(NullResponse::get(), responseId); + return SLANG_OK; + } + m_connection->sendResult(&result.result, responseId); + return SLANG_OK; +} + +LanguageServerResult<List<LanguageServerProtocol::TextEdit>> LanguageServerCore::onTypeFormatting( + const LanguageServerProtocol::DocumentOnTypeFormattingParams& args) +{ String canonicalPath = uriToCanonicalPath(args.textDocument.uri); RefPtr<DocumentVersion> doc; if (!m_workspace->openedDocuments.tryGetValue(canonicalPath, doc)) { - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + return std::nullopt; } if (args.ch == ":" && !doc->getLine((Int)args.position.line + 1).trim().startsWith("case ")) { - m_connection->sendResult(NullResponse::get(), responseId); - return SLANG_OK; + return std::nullopt; } if (m_formatOptions.clangFormatLocation.getLength() == 0) m_formatOptions.clangFormatLocation = findClangFormatTool(); @@ -1682,20 +1774,18 @@ SlangResult LanguageServer::onTypeFormatting(const LanguageServerProtocol::Docum List<TextRange> exclusionRange = extractFormattingExclusionRanges(doc->getText().getUnownedSlice()); auto edits = formatSource(doc->getText().getUnownedSlice(), args.position.line, args.position.line, cursorOffset, exclusionRange, options); auto textEdits = translateTextEdits(doc, edits); - m_connection->sendResult(&textEdits, responseId); - return SLANG_OK; + return textEdits; } void LanguageServer::publishDiagnostics() { - if (std::chrono::system_clock::now() - m_lastDiagnosticUpdateTime < std::chrono::milliseconds(1000)) { return; } m_lastDiagnosticUpdateTime = std::chrono::system_clock::now(); - auto version = m_workspace->getCurrentVersion(); + auto version = m_core.m_workspace->getCurrentVersion(); SLANG_AST_BUILDER_RAII(version->linkage->getASTBuilder()); // Send updates to clear diagnostics for files that no longer have any messages. @@ -1747,7 +1837,7 @@ void LanguageServer::updatePredefinedMacros(const JSONValue& macros) List<String> predefinedMacros; if (SLANG_SUCCEEDED(converter.convert(macros, &predefinedMacros))) { - if (m_workspace->updatePredefinedMacros(predefinedMacros)) + if (m_core.m_workspace->updatePredefinedMacros(predefinedMacros)) { sendRefreshRequests(m_connection); } @@ -1764,7 +1854,7 @@ void LanguageServer::updateSearchPaths(const JSONValue& value) List<String> searchPaths; if (SLANG_SUCCEEDED(converter.convert(value, &searchPaths))) { - if (m_workspace->updateSearchPaths(searchPaths)) + if (m_core.m_workspace->updateSearchPaths(searchPaths)) { sendRefreshRequests(m_connection); } @@ -1781,7 +1871,7 @@ void LanguageServer::updateSearchInWorkspace(const JSONValue& value) bool searchPaths; if (SLANG_SUCCEEDED(converter.convert(value, &searchPaths))) { - if (m_workspace->updateSearchInWorkspace(searchPaths)) + if (m_core.m_workspace->updateSearchInWorkspace(searchPaths)) { sendRefreshRequests(m_connection); } @@ -1800,15 +1890,15 @@ void LanguageServer::updateCommitCharacters(const JSONValue& jsonValue) { if (value == "on") { - m_commitCharacterBehavior = CommitCharacterBehavior::All; + m_core.m_commitCharacterBehavior = CommitCharacterBehavior::All; } else if (value == "off") { - m_commitCharacterBehavior = CommitCharacterBehavior::Disabled; + m_core.m_commitCharacterBehavior = CommitCharacterBehavior::Disabled; } else { - m_commitCharacterBehavior = CommitCharacterBehavior::MembersOnly; + m_core.m_commitCharacterBehavior = CommitCharacterBehavior::MembersOnly; } } } @@ -1819,17 +1909,17 @@ void LanguageServer::updateFormattingOptions(const JSONValue& clangFormatLoc, co auto container = m_connection->getContainer(); JSONToNativeConverter converter(container, &m_typeMap, m_connection->getSink()); if (clangFormatLoc.isValid()) - converter.convert(clangFormatLoc, &m_formatOptions.clangFormatLocation); + converter.convert(clangFormatLoc, &m_core.m_formatOptions.clangFormatLocation); if (clangFormatStyle.isValid()) - converter.convert(clangFormatStyle, &m_formatOptions.style); + converter.convert(clangFormatStyle, &m_core.m_formatOptions.style); if (clangFormatFallbackStyle.isValid()) - converter.convert(clangFormatFallbackStyle, &m_formatOptions.fallbackStyle); + converter.convert(clangFormatFallbackStyle, &m_core.m_formatOptions.fallbackStyle); if (allowLineBreakOnType.isValid()) - converter.convert(allowLineBreakOnType, &m_formatOptions.allowLineBreakInOnTypeFormatting); + converter.convert(allowLineBreakOnType, &m_core.m_formatOptions.allowLineBreakInOnTypeFormatting); if (allowLineBreakInRange.isValid()) - converter.convert(allowLineBreakInRange, &m_formatOptions.allowLineBreakInRangeFormatting); - if (m_formatOptions.style.getLength() == 0) - m_formatOptions.style = Slang::FormatOptions().style; + converter.convert(allowLineBreakInRange, &m_core.m_formatOptions.allowLineBreakInRangeFormatting); + if (m_core.m_formatOptions.style.getLength() == 0) + m_core.m_formatOptions.style = Slang::FormatOptions().style; } void LanguageServer::updateInlayHintOptions(const JSONValue& deducedTypes, const JSONValue& parameterNames) @@ -1840,13 +1930,13 @@ void LanguageServer::updateInlayHintOptions(const JSONValue& deducedTypes, const bool showParameterNames = false; converter.convert(deducedTypes, &showDeducedType); converter.convert(parameterNames, &showParameterNames); - if (showDeducedType != m_inlayHintOptions.showDeducedType || showParameterNames != m_inlayHintOptions.showParameterNames) + if (showDeducedType != m_core.m_inlayHintOptions.showDeducedType || showParameterNames != m_core.m_inlayHintOptions.showParameterNames) { m_connection->sendCall( UnownedStringSlice("workspace/inlayHint/refresh"), JSONValue::makeInt(0)); } - m_inlayHintOptions.showDeducedType = showDeducedType; - m_inlayHintOptions.showParameterNames = showParameterNames; + m_core.m_inlayHintOptions.showDeducedType = showDeducedType; + m_core.m_inlayHintOptions.showParameterNames = showParameterNames; } void LanguageServer::updateTraceOptions(const JSONValue& value) @@ -1921,7 +2011,7 @@ void LanguageServer::logMessage(int type, String message) m_connection->sendCall(LanguageServerProtocol::LogMessageParams::methodName, &args); } -FormatOptions LanguageServer::getFormatOptions(Workspace* workspace, FormatOptions inOptions) +FormatOptions LanguageServerCore::getFormatOptions(Workspace* workspace, FormatOptions inOptions) { FormatOptions result = inOptions; if (workspace->rootDirectories.getCount()) @@ -1934,8 +2024,8 @@ FormatOptions LanguageServer::getFormatOptions(Workspace* workspace, FormatOptio return result; } -SlangResult LanguageServer::tryGetMacroHoverInfo( - WorkspaceVersion* version, DocumentVersion* doc, Index line, Index col, JSONValue responseId) +LanguageServerResult<LanguageServerProtocol::Hover> LanguageServerCore::tryGetMacroHoverInfo( + WorkspaceVersion* version, DocumentVersion* doc, Index line, Index col) { Index startOffset = 0; auto identifier = doc->peekIdentifier(line, col, startOffset); @@ -1981,12 +2071,11 @@ SlangResult LanguageServer::tryGetMacroHoverInfo( appendDefinitionLocation(sb, m_workspace, humaneLoc); hover.contents.kind = "markdown"; hover.contents.value = sb.produceString(); - m_connection->sendResult(&hover, responseId); - return SLANG_OK; + return hover; } -SlangResult LanguageServer::tryGotoMacroDefinition( - WorkspaceVersion* version, DocumentVersion* doc, Index line, Index col, JSONValue responseId) +LanguageServerResult<List<Location>> LanguageServerCore::tryGotoMacroDefinition( + WorkspaceVersion* version, DocumentVersion* doc, Index line, Index col) { Index startOffset = 0; auto identifier = doc->peekIdentifier(line, col, startOffset); @@ -1997,7 +2086,9 @@ SlangResult LanguageServer::tryGotoMacroDefinition( return SLANG_FAIL; auto humaneLoc = version->linkage->getSourceManager()->getHumaneLoc(def->loc, SourceLocType::Actual); - LanguageServerProtocol::Location result; + List<LanguageServerProtocol::Location> results; + results.setCount(1); + auto& result = results[0]; result.uri = URI::fromLocalFilePath(humaneLoc.pathInfo.foundPath.getUnownedSlice()).uri; Index outLine, outCol; doc->oneBasedUTF8LocToZeroBasedUTF16Loc(humaneLoc.line, humaneLoc.column, outLine, outCol); @@ -2005,12 +2096,11 @@ SlangResult LanguageServer::tryGotoMacroDefinition( result.range.start.character = (int)outCol; result.range.end.line = (int)outLine; result.range.end.character = (int)(outCol + identifier.getLength()); - m_connection->sendResult(&result, responseId); - return SLANG_OK; + return results; } -SlangResult LanguageServer::tryGotoFileInclude( - WorkspaceVersion* version, DocumentVersion* doc, Index line, JSONValue responseId) +LanguageServerResult<List<Location>> LanguageServerCore::tryGotoFileInclude( + WorkspaceVersion* version, DocumentVersion* doc, Index line) { auto lineContent = doc->getLine(line).trim(); if (!lineContent.startsWith("#") || lineContent.indexOf(UnownedStringSlice("include")) == -1) @@ -2021,14 +2111,15 @@ SlangResult LanguageServer::tryGotoFileInclude( version->linkage->getSourceManager()->getHumaneLoc(include.loc, SourceLocType::Actual); if (includeLoc.line == line && includeLoc.pathInfo.foundPath == doc->getPath()) { - LanguageServerProtocol::Location result; + List<LanguageServerProtocol::Location> results; + results.setCount(1); + auto& result = results[0]; result.uri = URI::fromLocalFilePath(include.path.getUnownedSlice()).uri; result.range.start.line = 0; result.range.start.character = 0; result.range.end.line = 0; result.range.end.character = 0; - m_connection->sendResult(&result, responseId); - return SLANG_OK; + return results; } } return SLANG_FAIL; @@ -2267,17 +2358,28 @@ void LanguageServer::processCommands() SlangResult LanguageServer::didCloseTextDocument(const DidCloseTextDocumentParams& args) { + resetDiagnosticUpdateTime(); + return m_core.didCloseTextDocument(args); +} + +SlangResult LanguageServerCore::didCloseTextDocument(const DidCloseTextDocumentParams& args) +{ String canonicalPath = uriToCanonicalPath(args.textDocument.uri); m_workspace->closeDoc(canonicalPath); - resetDiagnosticUpdateTime(); return SLANG_OK; } + SlangResult LanguageServer::didChangeTextDocument(const DidChangeTextDocumentParams& args) { + resetDiagnosticUpdateTime(); + return m_core.didChangeTextDocument(args); +} + +SlangResult LanguageServerCore::didChangeTextDocument(const DidChangeTextDocumentParams& args) +{ String canonicalPath = uriToCanonicalPath(args.textDocument.uri); for (auto change : args.contentChanges) m_workspace->changeDoc(canonicalPath, change.range, change.text); - resetDiagnosticUpdateTime(); return SLANG_OK; } @@ -2297,7 +2399,7 @@ SlangResult LanguageServer::didChangeConfiguration( void LanguageServer::update() { - if (!m_workspace) + if (!m_core.m_workspace) return; publishDiagnostics(); } diff --git a/source/slang/slang-language-server.h b/source/slang/slang-language-server.h index 0a157819d..a0d5420db 100644 --- a/source/slang/slang-language-server.h +++ b/source/slang/slang-language-server.h @@ -84,11 +84,8 @@ struct LanguageServerStartupOptions SLANG_API void parse(int argc, const char* const* argv); }; -class LanguageServer +class LanguageServerCore { -private: - static const int kConfigResponseId = 0x1213; - public: enum class TraceOptions { @@ -96,26 +93,95 @@ public: Messages, Verbose }; - bool m_initialized = false; - TraceOptions m_traceOptions = TraceOptions::Off; CommitCharacterBehavior m_commitCharacterBehavior = CommitCharacterBehavior::MembersOnly; - RefPtr<JSONRPCConnection> m_connection; ComPtr<slang::IGlobalSession> m_session; RefPtr<Workspace> m_workspace; - Dictionary<String, String> m_lastPublishedDiagnostics; - std::chrono::time_point<std::chrono::system_clock> m_lastDiagnosticUpdateTime; FormatOptions m_formatOptions; Slang::InlayHintOptions m_inlayHintOptions; - bool m_quit = false; List<LanguageServerProtocol::WorkspaceFolder> m_workspaceFolders; - RttiTypeFuncsMap m_typeMap; LanguageServerStartupOptions m_options; - LanguageServer(LanguageServerStartupOptions options) + LanguageServerCore(LanguageServerStartupOptions options) : m_options(options) {} SlangResult init(const LanguageServerProtocol::InitializeParams& args); + SlangResult didOpenTextDocument(const LanguageServerProtocol::DidOpenTextDocumentParams& args); + SlangResult didCloseTextDocument( + const LanguageServerProtocol::DidCloseTextDocumentParams& args); + SlangResult didChangeTextDocument( + const LanguageServerProtocol::DidChangeTextDocumentParams& args); + LanguageServerResult<LanguageServerProtocol::Hover> hover(const LanguageServerProtocol::HoverParams& args); + LanguageServerResult<List<LanguageServerProtocol::Location>> gotoDefinition( + const LanguageServerProtocol::DefinitionParams& args); + + LanguageServerResult<CompletionResult> completion( + const LanguageServerProtocol::CompletionParams& args); + LanguageServerResult<LanguageServerProtocol::CompletionItem> completionResolve( + const LanguageServerProtocol::CompletionItem& args, const LanguageServerProtocol::TextEditCompletionItem& editItem); + LanguageServerResult<LanguageServerProtocol::SemanticTokens> semanticTokens( + const LanguageServerProtocol::SemanticTokensParams& args); + LanguageServerResult<LanguageServerProtocol::SignatureHelp> signatureHelp( + const LanguageServerProtocol::SignatureHelpParams& args); + LanguageServerResult<List<LanguageServerProtocol::DocumentSymbol>> documentSymbol( + const LanguageServerProtocol::DocumentSymbolParams& args); + LanguageServerResult<List<LanguageServerProtocol::InlayHint>> inlayHint( + const LanguageServerProtocol::InlayHintParams& args); + LanguageServerResult<List<LanguageServerProtocol::TextEdit>> formatting( + const LanguageServerProtocol::DocumentFormattingParams& args); + LanguageServerResult<List<LanguageServerProtocol::TextEdit>> rangeFormatting( + const LanguageServerProtocol::DocumentRangeFormattingParams& args); + LanguageServerResult<List<LanguageServerProtocol::TextEdit>> onTypeFormatting( + const LanguageServerProtocol::DocumentOnTypeFormattingParams& args); + String getExprDeclSignature(Expr* expr, String* outDocumentation, List<Slang::Range<Index>>* outParamRanges); + String getDeclRefSignature(DeclRef<Decl> declRef, String* outDocumentation, List<Slang::Range<Index>>* outParamRanges); +private: + slang::IGlobalSession* getOrCreateGlobalSession(); + + FormatOptions getFormatOptions(Workspace* workspace, FormatOptions inOptions); + LanguageServerResult<LanguageServerProtocol::Hover> tryGetMacroHoverInfo( + WorkspaceVersion* version, + DocumentVersion* doc, + Index line, + Index col); + LanguageServerResult<List<LanguageServerProtocol::Location>> tryGotoMacroDefinition( + WorkspaceVersion* version, + DocumentVersion* doc, + Index line, + Index col); + LanguageServerResult<List<LanguageServerProtocol::Location>> tryGotoFileInclude( + WorkspaceVersion* version, + DocumentVersion* doc, + Index line); +}; + +class LanguageServer +{ +private: + static const int kConfigResponseId = 0x1213; + +public: + enum class TraceOptions + { + Off, + Messages, + Verbose + }; + + bool m_quit = false; + LanguageServerCore m_core; + RefPtr<JSONRPCConnection> m_connection; + RttiTypeFuncsMap m_typeMap; + bool m_initialized = false; + TraceOptions m_traceOptions = TraceOptions::Off; + std::chrono::time_point<std::chrono::system_clock> m_lastDiagnosticUpdateTime; + Dictionary<String, String> m_lastPublishedDiagnostics; + + LanguageServer(LanguageServerStartupOptions options) + : m_core(options) + {} + + SlangResult init(const LanguageServerProtocol::InitializeParams& args); SlangResult execute(); void update(); void updateConfigFromJSON(const JSONValue& jsonVal); @@ -147,11 +213,8 @@ public: const LanguageServerProtocol::DocumentRangeFormattingParams& args, const JSONValue& responseId); SlangResult onTypeFormatting( const LanguageServerProtocol::DocumentOnTypeFormattingParams& args, const JSONValue& responseId); - String getExprDeclSignature(Expr* expr, String* outDocumentation, List<Slang::Range<Index>>* outParamRanges); - String getDeclRefSignature(DeclRef<Decl> declRef, String* outDocumentation, List<Slang::Range<Index>>* outParamRanges); private: SlangResult parseNextMessage(); - slang::IGlobalSession* getOrCreateGlobalSession(); void resetDiagnosticUpdateTime(); void publishDiagnostics(); void updatePredefinedMacros(const JSONValue& macros); @@ -166,24 +229,6 @@ private: void registerCapability(const char* methodName); void logMessage(int type, String message); - FormatOptions getFormatOptions(Workspace* workspace, FormatOptions inOptions); - SlangResult tryGetMacroHoverInfo( - WorkspaceVersion* version, - DocumentVersion* doc, - Index line, - Index col, - JSONValue responseId); - SlangResult tryGotoMacroDefinition( - WorkspaceVersion* version, - DocumentVersion* doc, - Index line, - Index col, - JSONValue responseId); - SlangResult tryGotoFileInclude( - WorkspaceVersion* version, - DocumentVersion* doc, - Index line, - JSONValue responseId); List<Command> commands; SlangResult queueJSONCall(JSONRPCCall call); SlangResult runCommand(Command& cmd); |
