From a8412100921ffa12e1a050225a92eb0904aa8ad0 Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Sun, 21 Jun 2026 02:03:43 +0100 Subject: [PATCH] perf(lsp): emit workspace-relative paths in definition/references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit lsp_query printed absolute paths from URIToFile (e.g. the full /Users/.../internal/... prefix repeated per result) while file_search and the AST summary already relativize. On a find_references across many files that ~60-char root prefix repeats per file group — wasted tokens for the LLM consumer. Extract a Server.relPath helper (dedups the AST summary's inline block) and apply it to definition and references display paths; the absolute path is kept where the file is actually re-read for previews. Adds a relPath unit test (in-workspace relativized, out-of-workspace unchanged). --- internal/server/features_test.go | 14 ++++++++++++++ internal/server/handlers_ast.go | 22 +++++++++++++--------- internal/server/handlers_lsp.go | 6 ++++-- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/internal/server/features_test.go b/internal/server/features_test.go index e5bb585..8552952 100644 --- a/internal/server/features_test.go +++ b/internal/server/features_test.go @@ -213,6 +213,20 @@ func TestASTQuerySkipsDependencyDirs(t *testing.T) { } } +// TestRelPath verifies workspace-relative display paths (token saving) with a +// safe fallback to the original path for anything outside the root. +func TestRelPath(t *testing.T) { + srv, tmpDir := newFeaturesServer(t) + if got := srv.relPath(filepath.Join(tmpDir, "pkg", "a.go")); got != filepath.Join("pkg", "a.go") { + t.Errorf("in-workspace path should be relativized, got %q", got) + } + // Outside the workspace root → returned unchanged (never a ../ escape string). + outside := "/definitely/outside/x.go" + if got := srv.relPath(outside); got != outside { + t.Errorf("out-of-workspace path should be returned as-is, got %q", got) + } +} + // ---- Feature 3 (ast_query): pagination cursor ---- // TestASTQueryFirstPageCursor is a strict regression guard: page 1 must emit a diff --git a/internal/server/handlers_ast.go b/internal/server/handlers_ast.go index f7a460f..52e37ec 100644 --- a/internal/server/handlers_ast.go +++ b/internal/server/handlers_ast.go @@ -263,15 +263,7 @@ func (s *Server) generateASTSummary(ctx context.Context, path string, content [] var sb strings.Builder - // Get relative path - relPath := path - if absPath, err := filepath.Abs(path); err == nil { - if rel, err := filepath.Rel(s.cfg.WorkspaceRoot, absPath); err == nil && !strings.HasPrefix(rel, "..") { - relPath = rel - } - } - - fmt.Fprintf(&sb, "**%s** (%d lines, %s)\n\n", relPath, countLines(content), lang) + fmt.Fprintf(&sb, "**%s** (%d lines, %s)\n\n", s.relPath(path), countLines(content), lang) sb.WriteString("Symbols:\n") for _, sym := range symbols { @@ -282,6 +274,18 @@ func (s *Server) generateASTSummary(ctx context.Context, path string, content [] return sb.String() } +// relPath renders path relative to the workspace root for display, saving the +// tokens an absolute path would cost. Falls back to the original path when it +// can't be relativized (outside the root, or on error). +func (s *Server) relPath(path string) string { + if absPath, err := filepath.Abs(path); err == nil { + if rel, err := filepath.Rel(s.cfg.WorkspaceRoot, absPath); err == nil && !strings.HasPrefix(rel, "..") { + return rel + } + } + return path +} + // symbolKindIcon returns an icon/prefix for a symbol kind. func symbolKindIcon(kind protocol.SymbolKind) string { switch kind { diff --git a/internal/server/handlers_lsp.go b/internal/server/handlers_lsp.go index 3e5a99d..0ea596b 100644 --- a/internal/server/handlers_lsp.go +++ b/internal/server/handlers_lsp.go @@ -181,7 +181,7 @@ func (s *Server) lspDefinition(ctx context.Context, file string, line, col int, for _, loc := range locations { filePath := lsp.URIToFile(loc.URI) - fmt.Fprintf(&output, "**%s:%d:%d**\n", filePath, loc.Range.Start.Line+1, loc.Range.Start.Character+1) + fmt.Fprintf(&output, "**%s:%d:%d**\n", s.relPath(filePath), loc.Range.Start.Line+1, loc.Range.Start.Character+1) preview := s.readFilePreview(filePath, loc.Range.Start.Line+1, 3) if preview != "" { @@ -210,7 +210,9 @@ func (s *Server) lspReferences(ctx context.Context, file string, line, col int, fileGroups := make(map[string][]lsp.Location) fileOrder := make([]string, 0) for _, loc := range locations { - filePath := lsp.URIToFile(loc.URI) + // Relativize for display; references are not re-read so the absolute + // path isn't needed downstream. + filePath := s.relPath(lsp.URIToFile(loc.URI)) if _, seen := fileGroups[filePath]; !seen { fileOrder = append(fileOrder, filePath) }