From 541bc15cd964f7595427c00495a6c11ffc132632 Mon Sep 17 00:00:00 2001 From: Simon Ernst Date: Mon, 2 Dec 2024 15:16:07 +0100 Subject: [PATCH] Add document symbol support --- main.go | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/main.go b/main.go index 93ade26..2f8b9cf 100644 --- a/main.go +++ b/main.go @@ -54,6 +54,7 @@ type ServerCapabilities struct { CompletionProvider *CompletionOptions `json:"completionProvider,omitempty"` DefinitionProvider bool `json:"definitionProvider,omitempty"` WorkspaceSymbolProvider bool `json:"workspaceSymbolProvider,omitempty"` + DocumentSymbolProvider bool `json:"documentSymbolProvider,omitempty"` } // TextDocumentSyncOptions defines options for text document synchronization @@ -73,6 +74,11 @@ type WorkspaceSymbolParams struct { Query string `json:"query"` } +// DocumentSymbolParams represents the parameters for the 'textDocument/documentSymbol' request +type DocumentSymbolParams struct { + TextDocument TextDocumentIdentifier `json:"textDocument"` +} + // SymbolInformation represents information about a symbol type SymbolInformation struct { Name string `json:"name"` @@ -389,6 +395,8 @@ func handleRequest(server *Server, req RPCRequest) { handleDefinition(server, req) case "workspace/symbol": handleWorkspaceSymbol(server, req) + case "textDocument/documentSymbol": + handleDocumentSymbol(server, req) default: // Method not found sendError(req.ID, -32601, "Method not found", nil) @@ -425,6 +433,7 @@ func handleInitialize(server *Server, req RPCRequest) { }, WorkspaceSymbolProvider: true, DefinitionProvider: true, + DocumentSymbolProvider: true, }, } @@ -716,6 +725,72 @@ func handleWorkspaceSymbol(server *Server, req RPCRequest) { sendResult(req.ID, symbols) } +// handleDocumentSymbol processes the 'textDocument/documentSymbol' request +func handleDocumentSymbol(server *Server, req RPCRequest) { + var params DocumentSymbolParams + err := json.Unmarshal(req.Params, ¶ms) + if err != nil { + sendError(req.ID, -32602, "Invalid params", nil) + return + } + + filePath := uriToPath(params.TextDocument.URI) + + server.mu.Lock() + defer server.mu.Unlock() + + var symbols []SymbolInformation + + for _, entry := range server.tagEntries { + // Check if the symbol belongs to the requested document + absolutePath := filepath.Join(server.rootPath, entry.Path) + absolutePath, err := filepath.Abs(absolutePath) + if err != nil { + log.Printf("Failed to get absolute path for %s: %v", entry.Path, err) + continue + } + + requestedPath, err := filepath.Abs(filePath) + if err != nil { + log.Printf("Failed to get absolute path for %s: %v", filePath, err) + continue + } + + if absolutePath != requestedPath { + continue + } + + kind, err := GetLSPSymbolKind(entry.Kind) + if err != nil { + // Skip symbols with unknown kinds + continue + } + + uri := filepathToURI(absolutePath) + + // Retrieve file content + content, err := server.cache.GetOrLoadFileContent(absolutePath) + if err != nil { + log.Printf("Failed to get content for file %s: %v", absolutePath, err) + continue + } + + // Find the symbol's range within the file + symbolRange := findSymbolRangeInFile(content, entry.Name, entry.Line) + + symbol := SymbolInformation{ + Name: entry.Name, + Kind: kind, + Location: Location{URI: uri, Range: symbolRange}, + ContainerName: entry.Scope, + } + + symbols = append(symbols, symbol) + } + + sendResult(req.ID, symbols) +} + // readFileLines reads the content of a file and returns it as a slice of lines func readFileLines(filePath string) ([]string, error) { contentBytes, err := os.ReadFile(filePath)