From 241def9c7619c437aad1bb620be8891e61707d8d Mon Sep 17 00:00:00 2001 From: Yong He Date: Thu, 16 Jun 2022 01:50:43 -0700 Subject: Language server: document symbols (#2287) * Language Server: Document Symbol outline. * Fix highlighting of extension decls. Co-authored-by: Yong He --- .../slang-language-server-document-symbols.cpp | 213 +++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 source/slang/slang-language-server-document-symbols.cpp (limited to 'source/slang/slang-language-server-document-symbols.cpp') 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 processedDecls; + DocumentVersion* doc; + Linkage* linkage; + UnownedStringSlice fileName; + }; + + static LanguageServerProtocol::SymbolKind _getSymbolKind(Decl* decl) + { + if (as(decl)) + { + return LanguageServerProtocol::kSymbolKindStruct; + } + if (as(decl)) + { + return LanguageServerProtocol::kSymbolKindClass; + } + if (as(decl)) + { + return LanguageServerProtocol::kSymbolKindInterface; + } + if (as(decl)) + { + return as(decl->parentDecl) ? LanguageServerProtocol::kSymbolKindMethod + : LanguageServerProtocol::kSymbolKindFunction; + } + if (as(decl)) + { + return LanguageServerProtocol::kSymbolKindProperty; + } + if (as(decl)) + { + return LanguageServerProtocol::kSymbolKindConstructor; + } + if (as(decl)) + { + return LanguageServerProtocol::kSymbolKindTypeParameter; + } + if (as(decl)) + { + if (decl->findModifier()) + return LanguageServerProtocol::kSymbolKindConstant; + return as(decl->parentDecl) ? LanguageServerProtocol::kSymbolKindField + : LanguageServerProtocol::kSymbolKindVariable; + } + if (as(decl)) + { + return LanguageServerProtocol::kSymbolKindClass; + } + if (as(decl)) + { + return LanguageServerProtocol::kSymbolKindTypeParameter; + } + if (as(decl)) + { + return LanguageServerProtocol::kSymbolKindConstant; + } + if (as(decl)) + { + return LanguageServerProtocol::kSymbolKindEnum; + } + if (as(decl)) + { + return LanguageServerProtocol::kSymbolKindEnumMember; + } + if (as(decl)) + { + return LanguageServerProtocol::kSymbolKindNamespace; + } + if (as(decl)) + { + return LanguageServerProtocol::kSymbolKindClass; + } + if (as(decl)) + { + return LanguageServerProtocol::kSymbolKindOperator; + } + return -1; + } + static SourceLoc _findClosingSourceLoc(Decl* decl) + { + if (auto func = as(decl)) + { + if (auto block = as(func->body)) + { + return block->closingSourceLoc; + } + else if (func->body) + { + return func->body->loc; + } + } + if (auto container = as(decl)) + { + return container->closingSourceLoc; + } + return SourceLoc(); + } + + static NameLoc _getDeclRefExprNameLoc(Expr* expr) + { + if (auto varExpr = as(expr)) + { + return NameLoc(varExpr->name, varExpr->loc); + } + else if (auto appBase = as(expr)) + { + return _getDeclRefExprNameLoc(appBase->functionExpr); + } + return NameLoc(); + } + static NameLoc _getDeclNameLoc(Decl* decl) + { + if (auto extDecl = as(decl)) + { + return _getDeclRefExprNameLoc(extDecl->targetType.exp); + } + return decl->nameAndLoc; + } + + static void _getDocumentSymbolsImpl( + GetDocumentSymbolContext& context, + Decl* parent, + List& childSymbols) + { + auto containerDecl = as(parent); + if (!containerDecl) + return; + if (!context.processedDecls.Add(parent)) + return; + auto srcManager = context.linkage->getSourceManager(); + for (auto child : containerDecl->members) + { + if (auto genericDecl = as(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(child)) + { + // Recurse + bool shouldRecurse = true; + if (as(child)) + shouldRecurse = false; + if (as(child)) + shouldRecurse = false; + if (shouldRecurse) + { + _getDocumentSymbolsImpl(context, child, sym.children); + } + } + childSymbols.add(_Move(sym)); + } + } + } + +List getDocumentSymbols( + Linkage* linkage, Module* module, UnownedStringSlice fileName, DocumentVersion* doc) +{ + GetDocumentSymbolContext context; + context.fileName = fileName; + context.doc = doc; + context.linkage = linkage; + List result; + _getDocumentSymbolsImpl(context, module->getModuleDecl(), result); + return result; +} + +} // namespace Slang -- cgit v1.2.3