@@ -8,13 +8,13 @@ import (
88 "maps"
99 "os"
1010 "path/filepath"
11− "strings"
1211 "sync"
1312 "sync/atomic"
1413 "time"
1514
1615 "github.com/charmbracelet/crush/internal/config"
1716 "github.com/charmbracelet/crush/internal/csync"
17+ "github.com/charmbracelet/crush/internal/fsext"
1818 "github.com/charmbracelet/crush/internal/home"
1919 powernap "github.com/charmbracelet/x/powernap/pkg/lsp"
2020 "github.com/charmbracelet/x/powernap/pkg/lsp/protocol"
@@ -35,7 +35,7 @@ type Client struct {
3535 debug bool
3636
3737 // Working directory this LSP is scoped to.
38− workDir string
38+ cwd string
3939
4040 // File types this LSP server handles (e.g., .go, .rs, .py)
4141 fileTypes []string
@@ -66,7 +66,14 @@ type Client struct {
6666}
6767
6868// New creates a new LSP client using the powernap implementation.
69−func New(ctx context.Context, name string, cfg config.LSPConfig, resolver config.VariableResolver, debug bool) (*Client, error) {
69+func New(
70+ ctx context.Context,
71+ name string,
72+ cfg config.LSPConfig,
73+ resolver config.VariableResolver,
74+ cwd string,
75+ debug bool,
76+) (*Client, error) {
7077 client := &Client{
7178 name: name,
7279 fileTypes: cfg.FileTypes,
@@ -76,6 +83,7 @@ func New(ctx context.Context, name string, cfg config.LSPConfig, resolver config
7683 ctx: ctx,
7784 debug: debug,
7885 resolver: resolver,
86+ cwd: cwd,
7987 }
8088 client.serverState.Store(StateStarting)
8189
@@ -134,13 +142,7 @@ func (c *Client) Close(ctx context.Context) error {
134142
135143// createPowernapClient creates a new powernap client with the current configuration.
136144func (c *Client) createPowernapClient() error {
137− workDir, err := os.Getwd()
138− if err != nil {
139− return fmt.Errorf("failed to get working directory: %w", err)
140− }
141−
142− rootURI := string(protocol.URIFromPath(workDir))
143− c.workDir = workDir
145+ rootURI := string(protocol.URIFromPath(c.cwd))
144146
145147 command, err := c.resolver.ResolveValue(c.config.Command)
146148 if err != nil {
@@ -157,7 +159,7 @@ func (c *Client) createPowernapClient() error {
157159 WorkspaceFolders: []protocol.WorkspaceFolder{
158160 {
159161 URI: rootURI,
160− Name: filepath.Base(workDir),
162+ Name: filepath.Base(c.cwd),
161163 },
162164 },
163165 }
@@ -321,15 +323,8 @@ type OpenFileInfo struct {
321323// HandlesFile checks if this LSP client handles the given file based on its
322324// extension and whether it's within the working directory.
323325func (c *Client) HandlesFile(path string) bool {
324− // Check if file is within working directory.
325− absPath, err := filepath.Abs(path)
326− if err != nil {
327− slog.Debug("Cannot resolve path", "name", c.name, "file", path, "error", err)
328− return false
329− }
330− relPath, err := filepath.Rel(c.workDir, absPath)
331− if err != nil || strings.HasPrefix(relPath, "..") {
332− slog.Debug("File outside workspace", "name", c.name, "file", path, "workDir", c.workDir)
326+ if !fsext.HasPrefix(path, c.cwd) {
327+ slog.Debug("File outside workspace", "name", c.name, "file", path, "workDir", c.cwd)
333328 return false
334329 }
335330 return handlesFiletype(c.name, c.fileTypes, path)
@@ -472,31 +467,6 @@ func (c *Client) OpenFileOnDemand(ctx context.Context, filepath string) error {
472467 return c.OpenFile(ctx, filepath)
473468}
474469
475−// GetDiagnosticsForFile ensures a file is open and returns its diagnostics.
476−func (c *Client) GetDiagnosticsForFile(ctx context.Context, filepath string) ([]protocol.Diagnostic, error) {
477− documentURI := protocol.URIFromPath(filepath)
478−
479− // Make sure the file is open
480− if !c.IsFileOpen(filepath) {
481− if err := c.OpenFile(ctx, filepath); err != nil {
482− return nil, fmt.Errorf("failed to open file for diagnostics: %w", err)
483− }
484−
485− // Give the LSP server a moment to process the file
486− time.Sleep(100 * time.Millisecond)
487− }
488−
489− // Get diagnostics
490− diagnostics, _ := c.diagnostics.Get(documentURI)
491−
492− return diagnostics, nil
493−}
494−
495−// ClearDiagnosticsForURI removes diagnostics for a specific URI from the cache.
496−func (c *Client) ClearDiagnosticsForURI(uri protocol.DocumentURI) {
497− c.diagnostics.Del(uri)
498−}
499−
500470// RegisterNotificationHandler registers a notification handler.
501471func (c *Client) RegisterNotificationHandler(method string, handler transport.NotificationHandler) {
502472 c.client.RegisterNotificationHandler(method, handler)
@@ -507,11 +477,6 @@ func (c *Client) RegisterServerRequestHandler(method string, handler transport.H
507477 c.client.RegisterHandler(method, handler)
508478}
509479
510−// DidChangeWatchedFiles sends a workspace/didChangeWatchedFiles notification to the server.
511−func (c *Client) DidChangeWatchedFiles(ctx context.Context, params protocol.DidChangeWatchedFilesParams) error {
512− return c.client.NotifyDidChangeWatchedFiles(ctx, params.Changes)
513−}
514−
515480// openKeyConfigFiles opens important configuration files that help initialize the server.
516481func (c *Client) openKeyConfigFiles(ctx context.Context) {
517482 wd, err := os.Getwd()