diff options
Diffstat (limited to 'source/slang/slang-language-server-document-symbols.cpp')
| -rw-r--r-- | source/slang/slang-language-server-document-symbols.cpp | 213 |
1 files changed, 213 insertions, 0 deletions
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 |
