diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/compiler-core/slang-language-server-protocol.cpp | 30 | ||||
| -rw-r--r-- | source/compiler-core/slang-language-server-protocol.h | 87 | ||||
| -rw-r--r-- | source/slang/slang-language-server-ast-lookup.cpp | 6 | ||||
| -rw-r--r-- | source/slang/slang-language-server-document-symbols.cpp | 213 | ||||
| -rw-r--r-- | source/slang/slang-language-server-document-symbols.h | 14 | ||||
| -rw-r--r-- | source/slang/slang-language-server-semantic-tokens.cpp | 6 | ||||
| -rw-r--r-- | source/slang/slang-language-server.cpp | 38 | ||||
| -rw-r--r-- | source/slang/slang-language-server.h | 3 | ||||
| -rw-r--r-- | source/slang/slang-parser.cpp | 17 | ||||
| -rw-r--r-- | source/slang/slang-workspace-version.cpp | 6 |
10 files changed, 413 insertions, 7 deletions
diff --git a/source/compiler-core/slang-language-server-protocol.cpp b/source/compiler-core/slang-language-server-protocol.cpp index 713d209dd..51799f05e 100644 --- a/source/compiler-core/slang-language-server-protocol.cpp +++ b/source/compiler-core/slang-language-server-protocol.cpp @@ -215,6 +215,7 @@ static const StructRttiInfo _makeServerCapabilitiesRtti() builder.addField("completionProvider", &obj.completionProvider); builder.addField("semanticTokensProvider", &obj.semanticTokensProvider); builder.addField("signatureHelpProvider", &obj.signatureHelpProvider); + builder.addField("documentSymbolProvider", &obj.documentSymbolProvider); builder.ignoreUnknownFields(); return builder.make(); } @@ -574,6 +575,35 @@ const StructRttiInfo LogMessageParams::g_rttiInfo = _makeLogMessageParamsRtti(); const UnownedStringSlice LogMessageParams::methodName = UnownedStringSlice::fromLiteral("window/logMessage"); +static const StructRttiInfo _makeDocumentSymbolParamsRtti() +{ + DocumentSymbolParams obj; + StructRttiBuilder builder( + &obj, "LanguageServerProtocol::DocumentSymbolParams", &WorkDoneProgressParams::g_rttiInfo); + builder.addField("textDocument", &obj.textDocument); + builder.ignoreUnknownFields(); + return builder.make(); +} +const StructRttiInfo DocumentSymbolParams::g_rttiInfo = _makeDocumentSymbolParamsRtti(); +const UnownedStringSlice DocumentSymbolParams::methodName = + UnownedStringSlice::fromLiteral("textDocument/documentSymbol"); + +static const StructRttiInfo _makeDocumentSymbolRtti() +{ + DocumentSymbol obj; + StructRttiBuilder builder( + &obj, "LanguageServerProtocol::DocumentSymbol", nullptr); + builder.addField("name", &obj.name); + builder.addField("detail", &obj.detail); + builder.addField("kind", &obj.kind); + builder.addField("range", &obj.range); + builder.addField("selectionRange", &obj.selectionRange); + builder.addField("children", &obj.children); + builder.ignoreUnknownFields(); + return builder.make(); +} +const StructRttiInfo DocumentSymbol::g_rttiInfo = _makeDocumentSymbolRtti(); + } // namespace LanguageServerProtocol } diff --git a/source/compiler-core/slang-language-server-protocol.h b/source/compiler-core/slang-language-server-protocol.h index 11446bd0b..7d9d8d5cb 100644 --- a/source/compiler-core/slang-language-server-protocol.h +++ b/source/compiler-core/slang-language-server-protocol.h @@ -238,6 +238,7 @@ struct ServerCapabilities TextDocumentSyncOptions textDocumentSync; bool hoverProvider = false; bool definitionProvider = false; + bool documentSymbolProvider = false; CompletionOptions completionProvider; SemanticTokensOptions semanticTokensProvider; SignatureHelpOptions signatureHelpProvider; @@ -740,5 +741,91 @@ struct LogMessageParams static const UnownedStringSlice methodName; }; +struct DocumentSymbolParams : WorkDoneProgressParams +{ + /** + * The text document. + */ + TextDocumentIdentifier textDocument; + + static const StructRttiInfo g_rttiInfo; + static const UnownedStringSlice methodName; +}; + +typedef int SymbolKind; +const int kSymbolKindFile = 1; +const int kSymbolKindModule = 2; +const int kSymbolKindNamespace = 3; +const int kSymbolKindPackage = 4; +const int kSymbolKindClass = 5; +const int kSymbolKindMethod = 6; +const int kSymbolKindProperty = 7; +const int kSymbolKindField = 8; +const int kSymbolKindConstructor = 9; +const int kSymbolKindEnum = 10; +const int kSymbolKindInterface = 11; +const int kSymbolKindFunction = 12; +const int kSymbolKindVariable = 13; +const int kSymbolKindConstant = 14; +const int kSymbolKindString = 15; +const int kSymbolKindNumber = 16; +const int kSymbolKindBoolean = 17; +const int kSymbolKindArray = 18; +const int kSymbolKindObject = 19; +const int kSymbolKindKey = 20; +const int kSymbolKindNull = 21; +const int kSymbolKindEnumMember = 22; +const int kSymbolKindStruct = 23; +const int kSymbolKindEvent = 24; +const int kSymbolKindOperator = 25; +const int kSymbolKindTypeParameter = 26; + +/** + * Represents programming constructs like variables, classes, interfaces etc. + * that appear in a document. Document symbols can be hierarchical and they + * have two ranges: one that encloses its definition and one that points to its + * most interesting range, e.g. the range of an identifier. + */ +struct DocumentSymbol { + + /** + * The name of this symbol. Will be displayed in the user interface and + * therefore must not be an empty string or a string only consisting of + * white spaces. + */ + String name; + + /** + * More detail for this symbol, e.g the signature of a function. + */ + String detail; + + /** + * The kind of this symbol. + */ + SymbolKind kind; + + /** + * The range enclosing this symbol not including leading/trailing whitespace + * but everything else like comments. This information is typically used to + * determine if the clients cursor is inside the symbol to reveal in the + * symbol in the UI. + */ + Range range; + + /** + * The range that should be selected and revealed when this symbol is being + * picked, e.g. the name of a function. Must be contained by the `range`. + */ + Range selectionRange; + + /** + * Children of this symbol, e.g. properties of a class. + */ + List<DocumentSymbol> children; + + static const StructRttiInfo g_rttiInfo; +}; + } // namespace LanguageServerProtocol } // namespace Slang diff --git a/source/slang/slang-language-server-ast-lookup.cpp b/source/slang/slang-language-server-ast-lookup.cpp index 3a35e699b..0e5a68687 100644 --- a/source/slang/slang-language-server-ast-lookup.cpp +++ b/source/slang/slang-language-server-ast-lookup.cpp @@ -542,6 +542,12 @@ bool _findAstNodeImpl(ASTLookupContext& context, SyntaxNode* node) if (visitor.dispatchIfNotNull(typedefDecl->type.exp)) return true; } + else if (auto extDecl = as<ExtensionDecl>(node)) + { + ASTLookupExprVisitor visitor(&context); + if (visitor.dispatchIfNotNull(extDecl->targetType.exp)) + return true; + } for (auto modifier : decl->modifiers) { if (auto hlslSemantic = as<HLSLSemantic>(modifier)) diff --git a/source/slang/slang-language-server-document-symbols.cpp b/source/slang/slang-language-server-document-symbols.cpp new file mode 100644 index 000000000..b2b213b02 --- /dev/null +++ b/source/slang/slang-language-server-document-symbols.cpp @@ -0,0 +1,213 @@ +#include "slang-language-server-document-symbols.h" + +namespace Slang +{ + struct GetDocumentSymbolContext + { + HashSet<Decl*> processedDecls; + DocumentVersion* doc; + Linkage* linkage; + UnownedStringSlice fileName; + }; + + static LanguageServerProtocol::SymbolKind _getSymbolKind(Decl* decl) + { + if (as<StructDecl>(decl)) + { + return LanguageServerProtocol::kSymbolKindStruct; + } + if (as<ClassDecl>(decl)) + { + return LanguageServerProtocol::kSymbolKindClass; + } + if (as<InterfaceDecl>(decl)) + { + return LanguageServerProtocol::kSymbolKindInterface; + } + if (as<FuncDecl>(decl)) + { + return as<AggTypeDecl>(decl->parentDecl) ? LanguageServerProtocol::kSymbolKindMethod + : LanguageServerProtocol::kSymbolKindFunction; + } + if (as<PropertyDecl>(decl)) + { + return LanguageServerProtocol::kSymbolKindProperty; + } + if (as<ConstructorDecl>(decl)) + { + return LanguageServerProtocol::kSymbolKindConstructor; + } + if (as<AssocTypeDecl>(decl)) + { + return LanguageServerProtocol::kSymbolKindTypeParameter; + } + if (as<VarDeclBase>(decl)) + { + if (decl->findModifier<ConstModifier>()) + return LanguageServerProtocol::kSymbolKindConstant; + return as<AggTypeDecl>(decl->parentDecl) ? LanguageServerProtocol::kSymbolKindField + : LanguageServerProtocol::kSymbolKindVariable; + } + if (as<TypeDefDecl>(decl)) + { + return LanguageServerProtocol::kSymbolKindClass; + } + if (as<GenericTypeParamDecl>(decl)) + { + return LanguageServerProtocol::kSymbolKindTypeParameter; + } + if (as<GenericValueParamDecl>(decl)) + { + return LanguageServerProtocol::kSymbolKindConstant; + } + if (as<EnumDecl>(decl)) + { + return LanguageServerProtocol::kSymbolKindEnum; + } + if (as<EnumCaseDecl>(decl)) + { + return LanguageServerProtocol::kSymbolKindEnumMember; + } + if (as<NamespaceDecl>(decl)) + { + return LanguageServerProtocol::kSymbolKindNamespace; + } + if (as<ExtensionDecl>(decl)) + { + return LanguageServerProtocol::kSymbolKindClass; + } + if (as<SubscriptDecl>(decl)) + { + return LanguageServerProtocol::kSymbolKindOperator; + } + return -1; + } + static SourceLoc _findClosingSourceLoc(Decl* decl) + { + if (auto func = as<FunctionDeclBase>(decl)) + { + if (auto block = as<BlockStmt>(func->body)) + { + return block->closingSourceLoc; + } + else if (func->body) + { + return func->body->loc; + } + } + if (auto container = as<ContainerDecl>(decl)) + { + return container->closingSourceLoc; + } + return SourceLoc(); + } + + static NameLoc _getDeclRefExprNameLoc(Expr* expr) + { + if (auto varExpr = as<VarExpr>(expr)) + { + return NameLoc(varExpr->name, varExpr->loc); + } + else if (auto appBase = as<AppExprBase>(expr)) + { + return _getDeclRefExprNameLoc(appBase->functionExpr); + } + return NameLoc(); + } + static NameLoc _getDeclNameLoc(Decl* decl) + { + if (auto extDecl = as<ExtensionDecl>(decl)) + { + return _getDeclRefExprNameLoc(extDecl->targetType.exp); + } + return decl->nameAndLoc; + } + + static void _getDocumentSymbolsImpl( + GetDocumentSymbolContext& context, + Decl* parent, + List<LanguageServerProtocol::DocumentSymbol>& childSymbols) + { + auto containerDecl = as<ContainerDecl>(parent); + if (!containerDecl) + return; + if (!context.processedDecls.Add(parent)) + return; + auto srcManager = context.linkage->getSourceManager(); + for (auto child : containerDecl->members) + { + if (auto genericDecl = as<GenericDecl>(child)) + { + child = genericDecl->inner; + } + LanguageServerProtocol::SymbolKind kind = _getSymbolKind(child); + if (kind <= 0) + continue; + NameLoc nameLoc = _getDeclNameLoc(child); + if (!nameLoc.name) + continue; + if (nameLoc.name->text.getLength() == 0) + continue; + if (!nameLoc.loc.isValid()) + continue; + auto humaneLoc = srcManager->getHumaneLoc(nameLoc.loc, SourceLocType::Actual); + if (humaneLoc.line == 0) + continue; + if (context.fileName.endsWithCaseInsensitive( + Path::getFileName(humaneLoc.pathInfo.foundPath).getUnownedSlice())) + { + LanguageServerProtocol::DocumentSymbol sym; + sym.name = nameLoc.name->text; + sym.kind = kind; + Index line, col; + context.doc->oneBasedUTF8LocToZeroBasedUTF16Loc( + humaneLoc.line, humaneLoc.column, line, col); + sym.selectionRange.start.line = (int)line; + sym.selectionRange.start.character = (int)col; + sym.selectionRange.end.line = (int)line; + sym.selectionRange.end.character = (int)(col + nameLoc.name->text.getLength()); + sym.range.start.line = (int)line; + sym.range.start.character = 0; + sym.range.end.line = (int)line; + sym.range.end.character = sym.selectionRange.end.character; + // Now try to find the end of the decl. + auto closingLoc = _findClosingSourceLoc(child); + if (closingLoc.isValid()) + { + auto closingHumaneLoc = srcManager->getHumaneLoc(closingLoc, SourceLocType::Actual); + context.doc->oneBasedUTF8LocToZeroBasedUTF16Loc( + closingHumaneLoc.line, closingHumaneLoc.column, line, col); + sym.range.end.line = (int)line; + sym.range.end.character = (int)col; + } + if (auto childContainerDecl = as<ContainerDecl>(child)) + { + // Recurse + bool shouldRecurse = true; + if (as<CallableDecl>(child)) + shouldRecurse = false; + if (as<PropertyDecl>(child)) + shouldRecurse = false; + if (shouldRecurse) + { + _getDocumentSymbolsImpl(context, child, sym.children); + } + } + childSymbols.add(_Move(sym)); + } + } + } + +List<LanguageServerProtocol::DocumentSymbol> getDocumentSymbols( + Linkage* linkage, Module* module, UnownedStringSlice fileName, DocumentVersion* doc) +{ + GetDocumentSymbolContext context; + context.fileName = fileName; + context.doc = doc; + context.linkage = linkage; + List<LanguageServerProtocol::DocumentSymbol> result; + _getDocumentSymbolsImpl(context, module->getModuleDecl(), result); + return result; +} + +} // namespace Slang diff --git a/source/slang/slang-language-server-document-symbols.h b/source/slang/slang-language-server-document-symbols.h new file mode 100644 index 000000000..65e84a6b2 --- /dev/null +++ b/source/slang/slang-language-server-document-symbols.h @@ -0,0 +1,14 @@ +#pragma once + +#include "../../slang.h" +#include "../core/slang-basic.h" +#include "slang-ast-all.h" +#include "slang-syntax.h" +#include "slang-compiler.h" +#include "slang-workspace-version.h" + +namespace Slang +{ +List<LanguageServerProtocol::DocumentSymbol> getDocumentSymbols( + Linkage* linkage, Module* module, UnownedStringSlice fileName, DocumentVersion* doc); +} // namespace Slang diff --git a/source/slang/slang-language-server-semantic-tokens.cpp b/source/slang/slang-language-server-semantic-tokens.cpp index 806fa69ba..ff9056e56 100644 --- a/source/slang/slang-language-server-semantic-tokens.cpp +++ b/source/slang/slang-language-server-semantic-tokens.cpp @@ -373,6 +373,10 @@ void ASTIterator<CallbackFunc>::visitDecl(DeclBase* decl) { visitExpr(typedefDecl->type.exp); } + else if (auto extDecl = as<ExtensionDecl>(decl)) + { + visitExpr(extDecl->targetType.exp); + } if (auto container = as<ContainerDecl>(decl)) { for (auto member : container->members) @@ -436,7 +440,7 @@ List<SemanticToken> getSemanticTokens(Linkage* linkage, Module* module, UnownedS List<SemanticToken> result; auto maybeInsertToken = [&](const SemanticToken& token) { - if (token.line >= 0 && token.col >= 0 && token.length > 0 && + if (token.line > 0 && token.col > 0 && token.length > 0 && token.type != SemanticTokenType::NormalText) result.add(token); }; diff --git a/source/slang/slang-language-server.cpp b/source/slang/slang-language-server.cpp index 5aa8d0e14..1ad0e28e9 100644 --- a/source/slang/slang-language-server.cpp +++ b/source/slang/slang-language-server.cpp @@ -20,6 +20,7 @@ #include "slang-language-server-ast-lookup.h" #include "slang-language-server-completion.h" #include "slang-language-server-semantic-tokens.h" +#include "slang-language-server-document-symbols.h" #include "slang-ast-print.h" #include "slang-doc-markdown-writer.h" #include "../../tools/platform/performance-counter.h" @@ -110,12 +111,13 @@ SlangResult LanguageServer::parseNextMessage() result.capabilities.workspace.workspaceFolders.changeNotifications = false; result.capabilities.hoverProvider = true; result.capabilities.definitionProvider = true; - for (auto ch : getCommitChars()) - result.capabilities.completionProvider.allCommitCharacters.add(ch); + result.capabilities.documentSymbolProvider = true; result.capabilities.completionProvider.triggerCharacters.add("."); result.capabilities.completionProvider.triggerCharacters.add(":"); result.capabilities.completionProvider.resolveProvider = true; result.capabilities.completionProvider.workDoneToken = ""; + for (auto ch : getCommitChars()) + result.capabilities.completionProvider.allCommitCharacters.add(ch); result.capabilities.semanticTokensProvider.full = true; result.capabilities.semanticTokensProvider.range = false; result.capabilities.signatureHelpProvider.triggerCharacters.add("("); @@ -731,6 +733,28 @@ SlangResult LanguageServer::signatureHelp( return SLANG_OK; } +SlangResult LanguageServer::documentSymbol( + const LanguageServerProtocol::DocumentSymbolParams& args, const JSONValue& responseId) +{ + 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; + } + auto version = m_workspace->getCurrentVersion(); + Module* parsedModule = version->getOrLoadModule(canonicalPath); + if (!parsedModule) + { + 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); + return SLANG_OK; +} + void LanguageServer::publishDiagnostics() { time_t timeNow = 0; @@ -923,6 +947,12 @@ SlangResult LanguageServer::queueJSONCall(JSONRPCCall call) SLANG_RETURN_ON_FAIL(m_connection->toNativeArgsOrSendError(call.params, &args, call.id)); cmd.completionResolveArgs = args; } + else if (call.method == DocumentSymbolParams::methodName) + { + DocumentSymbolParams args; + SLANG_RETURN_ON_FAIL(m_connection->toNativeArgsOrSendError(call.params, &args, call.id)); + cmd.documentSymbolArgs = args; + } else if (call.method == DidChangeConfigurationParams::methodName) { DidChangeConfigurationParams args; @@ -980,6 +1010,10 @@ SlangResult LanguageServer::runCommand(Command& call) { return completionResolve(call.completionResolveArgs.get(), call.id); } + else if (call.method == DocumentSymbolParams::methodName) + { + return documentSymbol(call.documentSymbolArgs.get(), call.id); + } else if (call.method == DidChangeConfigurationParams::methodName) { return didChangeConfiguration(call.changeConfigArgs.get()); diff --git a/source/slang/slang-language-server.h b/source/slang/slang-language-server.h index 898037144..b873576e8 100644 --- a/source/slang/slang-language-server.h +++ b/source/slang/slang-language-server.h @@ -56,6 +56,7 @@ struct Command Optional<LanguageServerProtocol::CompletionParams> completionArgs; Optional<LanguageServerProtocol::CompletionItem> completionResolveArgs; + Optional<LanguageServerProtocol::DocumentSymbolParams> documentSymbolArgs; Optional<LanguageServerProtocol::DidChangeConfigurationParams> changeConfigArgs; Optional<LanguageServerProtocol::SignatureHelpParams> signatureHelpArgs; Optional<LanguageServerProtocol::DefinitionParams> definitionArgs; @@ -103,6 +104,8 @@ public: 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); private: SlangResult parseNextMessage(); diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp index aaf175812..c8a990ab1 100644 --- a/source/slang/slang-parser.cpp +++ b/source/slang/slang-parser.cpp @@ -2889,7 +2889,6 @@ namespace Slang decl->targetType = parser->ParseTypeExp(); parseOptionalInheritanceClause(parser, decl); parseDeclBody(parser, decl); - return decl; } @@ -3199,9 +3198,14 @@ namespace Slang if( parser->tokenReader.peekTokenType() == TokenType::LBrace ) { decl->body = parser->parseBlockStatement(); + if (auto block = as<BlockStmt>(decl->body)) + { + decl->closingSourceLoc = block->closingSourceLoc; + } } else { + decl->closingSourceLoc = parser->tokenReader.peekLoc(); parser->ReadToken(TokenType::Semicolon); } @@ -3216,14 +3220,18 @@ namespace Slang if( AdvanceIf(parser, TokenType::LBrace) ) { // We want to parse nested "accessor" declarations - while( !AdvanceIfMatch(parser, MatchedTokenType::CurlyBraces) ) + Token closingToken; + while (!AdvanceIfMatch(parser, MatchedTokenType::CurlyBraces, &closingToken)) { auto accessor = parseAccessorDecl(parser); AddMember(decl, accessor); } + decl->closingSourceLoc = closingToken.loc; } else { + decl->closingSourceLoc = parser->tokenReader.peekLoc(); + parser->ReadToken(TokenType::Semicolon); // empty body should be treated like `{ get; }` @@ -3970,8 +3978,8 @@ namespace Slang { parseOptionalInheritanceClause(parser, decl); parser->ReadToken(TokenType::LBrace); - - while(!AdvanceIfMatch(parser, MatchedTokenType::CurlyBraces)) + Token closingToken; + while (!AdvanceIfMatch(parser, MatchedTokenType::CurlyBraces, &closingToken)) { EnumCaseDecl* caseDecl = parseEnumCaseDecl(parser); AddMember(decl, caseDecl); @@ -3981,6 +3989,7 @@ namespace Slang parser->ReadToken(TokenType::Comma); } + decl->closingSourceLoc = closingToken.loc; return decl; }); } diff --git a/source/slang/slang-workspace-version.cpp b/source/slang/slang-workspace-version.cpp index 2742c083c..781d8a3bc 100644 --- a/source/slang/slang-workspace-version.cpp +++ b/source/slang/slang-workspace-version.cpp @@ -390,6 +390,12 @@ ArrayView<Index> DocumentVersion::getUTF16Boundaries(Index line) void DocumentVersion::oneBasedUTF8LocToZeroBasedUTF16Loc( Index inLine, Index inCol, Index& outLine, Index& outCol) { + if (inLine <= 0) + { + outLine = 0; + outCol = 0; + } + Index rsLine = inLine - 1; auto line = lines[rsLine]; auto bounds = getUTF16Boundaries(inLine); |
