#pragma once #include "../compiler-core/slang-json-rpc-connection.h" #include "../compiler-core/slang-json-rpc.h" #include "../core/slang-range.h" #include "slang-language-server-auto-format.h" #include "slang-language-server-completion.h" #include "slang-language-server-inlay-hints.h" #include "slang-workspace-version.h" #include "slang.h" #include namespace Slang { ArrayView getCommitChars(); struct Command { PersistentJSONValue id; String method; template struct Optional { public: T* value = nullptr; bool isValid() { return value != nullptr; } T& operator=(const T& val) { delete value; value = new T(val); return *value; } T& operator=(Optional&& other) { if (other.isValid()) *this = (other.get()); other.value = nullptr; return *value; } T& get() { SLANG_ASSERT(isValid()); return *value; } Optional() = default; Optional(const Optional& other) { if (other.isValid()) *this = (other.get()); } Optional(Optional&& other) { if (other.isValid()) *this = (other.get()); other.value = nullptr; } ~Optional() { delete value; } }; Optional completionArgs; Optional completionResolveArgs; Optional textEditCompletionResolveArgs; Optional documentSymbolArgs; Optional inlayHintArgs; Optional formattingArgs; Optional rangeFormattingArgs; Optional onTypeFormattingArgs; Optional changeConfigArgs; Optional signatureHelpArgs; Optional definitionArgs; Optional semanticTokenArgs; Optional hoverArgs; Optional openDocArgs; Optional changeDocArgs; Optional closeDocArgs; Optional cancelArgs; }; struct LanguageServerStartupOptions { // Are we working with Visual Studio client? bool isVisualStudio = false; // A flag to control periodic diagnostic update. Defaults to true. bool periodicDiagnosticUpdate = true; SLANG_API void parse(int argc, const char* const* argv); }; class LanguageServerCore { public: enum class TraceOptions { Off, Messages, Verbose }; CommitCharacterBehavior m_commitCharacterBehavior = CommitCharacterBehavior::MembersOnly; ComPtr m_session; RefPtr m_workspace; FormatOptions m_formatOptions; Slang::InlayHintOptions m_inlayHintOptions; List m_workspaceFolders; LanguageServerStartupOptions m_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 hover( const LanguageServerProtocol::HoverParams& args); LanguageServerResult> gotoDefinition( const LanguageServerProtocol::DefinitionParams& args); LanguageServerResult completion( const LanguageServerProtocol::CompletionParams& args); LanguageServerResult completionResolve( const LanguageServerProtocol::CompletionItem& args, const LanguageServerProtocol::TextEditCompletionItem& editItem); LanguageServerResult semanticTokens( const LanguageServerProtocol::SemanticTokensParams& args); LanguageServerResult signatureHelp( const LanguageServerProtocol::SignatureHelpParams& args); LanguageServerResult> documentSymbol( const LanguageServerProtocol::DocumentSymbolParams& args); LanguageServerResult> inlayHint( const LanguageServerProtocol::InlayHintParams& args); LanguageServerResult> formatting( const LanguageServerProtocol::DocumentFormattingParams& args); LanguageServerResult> rangeFormatting( const LanguageServerProtocol::DocumentRangeFormattingParams& args); LanguageServerResult> onTypeFormatting( const LanguageServerProtocol::DocumentOnTypeFormattingParams& args); String getExprDeclSignature( Expr* expr, String* outDocumentation, List>* outParamRanges); String getDeclRefSignature( DeclRef declRef, String* outDocumentation, List>* outParamRanges); private: slang::IGlobalSession* getOrCreateGlobalSession(); FormatOptions getFormatOptions(Workspace* workspace, FormatOptions inOptions); LanguageServerResult tryGetMacroHoverInfo( WorkspaceVersion* version, DocumentVersion* doc, Index line, Index col); LanguageServerResult> tryGotoMacroDefinition( WorkspaceVersion* version, DocumentVersion* doc, Index line, Index col); LanguageServerResult> 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 m_connection; RttiTypeFuncsMap m_typeMap; bool m_initialized = false; TraceOptions m_traceOptions = TraceOptions::Off; std::chrono::time_point m_lastDiagnosticUpdateTime; Dictionary m_lastPublishedDiagnostics; HashSet m_pendingModulesToUpdateDiagnostics; void removePendingModuleToUpdateDiagnostics(const String& uri); LanguageServer(LanguageServerStartupOptions options) : m_core(options) { } SlangResult init(const LanguageServerProtocol::InitializeParams& args); SlangResult execute(); void update(); void updateConfigFromJSON(const JSONValue& jsonVal); SlangResult didOpenTextDocument(const LanguageServerProtocol::DidOpenTextDocumentParams& args); SlangResult didCloseTextDocument( const LanguageServerProtocol::DidCloseTextDocumentParams& args); SlangResult didChangeTextDocument( const LanguageServerProtocol::DidChangeTextDocumentParams& args); SlangResult didChangeConfiguration( const LanguageServerProtocol::DidChangeConfigurationParams& args); SlangResult hover(const LanguageServerProtocol::HoverParams& args, const JSONValue& responseId); SlangResult gotoDefinition( const LanguageServerProtocol::DefinitionParams& args, const JSONValue& responseId); SlangResult completion( const LanguageServerProtocol::CompletionParams& args, const JSONValue& responseId); SlangResult completionResolve( const LanguageServerProtocol::CompletionItem& args, const LanguageServerProtocol::TextEditCompletionItem& editItem, const JSONValue& responseId); SlangResult semanticTokens( const LanguageServerProtocol::SemanticTokensParams& args, const JSONValue& responseId); SlangResult signatureHelp( const LanguageServerProtocol::SignatureHelpParams& args, const JSONValue& responseId); SlangResult documentSymbol( const LanguageServerProtocol::DocumentSymbolParams& args, const JSONValue& responseId); SlangResult inlayHint( const LanguageServerProtocol::InlayHintParams& args, const JSONValue& responseId); SlangResult formatting( const LanguageServerProtocol::DocumentFormattingParams& args, const JSONValue& responseId); SlangResult rangeFormatting( const LanguageServerProtocol::DocumentRangeFormattingParams& args, const JSONValue& responseId); SlangResult onTypeFormatting( const LanguageServerProtocol::DocumentOnTypeFormattingParams& args, const JSONValue& responseId); private: SlangResult parseNextMessage(); void resetDiagnosticUpdateTime(); void publishDiagnostics(); void updatePredefinedMacros(const JSONValue& macros); void updateSearchPaths(const JSONValue& value); void updateSearchInWorkspace(const JSONValue& value); void updateCommitCharacters(const JSONValue& value); void updateFormattingOptions( const JSONValue& enableFormatOnType, const JSONValue& clangFormatLoc, const JSONValue& clangFormatStyle, const JSONValue& clangFormatFallbackStyle, const JSONValue& allowLineBreakOnType, const JSONValue& allowLineBreakInRange); void updateInlayHintOptions(const JSONValue& deducedTypes, const JSONValue& parameterNames); void updateTraceOptions(const JSONValue& value); void sendConfigRequest(); void registerCapability(const char* methodName); void logMessage(int type, String message); List commands; SlangResult queueJSONCall(JSONRPCCall call); SlangResult runCommand(Command& cmd); void processCommands(); }; inline bool _isIdentifierChar(char ch) { return ch >= '0' && ch <= '9' || ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch == '_'; } SLANG_API SlangResult runLanguageServer(LanguageServerStartupOptions options); SLANG_API SlangResult getBuiltinModuleSource(const UnownedStringSlice& moduleName, slang::IBlob** blob); } // namespace Slang