@@ -8,13 +8,13 @@ import (
10 "path/filepath"
10 "path/filepath"
13 "sync/atomic"
12 "sync/atomic"
16 "github.com/charmbracelet/crush/internal/config"
15 "github.com/charmbracelet/crush/internal/config"
17 "github.com/charmbracelet/crush/internal/csync"
16 "github.com/charmbracelet/crush/internal/csync"
17+ "github.com/charmbracelet/crush/internal/fsext"
18 "github.com/charmbracelet/crush/internal/home"
18 "github.com/charmbracelet/crush/internal/home"
19 powernap "github.com/charmbracelet/x/powernap/pkg/lsp"
19 powernap "github.com/charmbracelet/x/powernap/pkg/lsp"
20 "github.com/charmbracelet/x/powernap/pkg/lsp/protocol"
20 "github.com/charmbracelet/x/powernap/pkg/lsp/protocol"
@@ -35,7 +35,7 @@ type Client struct {
35 debug bool
35 debug bool
37 // Working directory this LSP is scoped to.
37 // Working directory this LSP is scoped to.
38− workDir string
38+ cwd string
40 // File types this LSP server handles (e.g., .go, .rs, .py)
40 // File types this LSP server handles (e.g., .go, .rs, .py)
41 fileTypes []string
41 fileTypes []string
@@ -66,7 +66,14 @@ type Client struct {
68// New creates a new LSP client using the powernap implementation.
68// New creates a new LSP client using the powernap implementation.
72+ cfg config.LSPConfig,
69−func New(ctx context.Context, name string, cfg config.LSPConfig, resolver config.VariableResolver, debug bool) (*Client, error) {
73+ resolver config.VariableResolver,
70 client := &Client{
77 client := &Client{
71 name: name,
78 name: name,
72 fileTypes: cfg.FileTypes,
79 fileTypes: cfg.FileTypes,
@@ -76,6 +83,7 @@ func New(ctx context.Context, name string, cfg config.LSPConfig, resolver config
77 debug: debug,
84 debug: debug,
78 resolver: resolver,
85 resolver: resolver,
80 client.serverState.Store(StateStarting)
88 client.serverState.Store(StateStarting)
@@ -134,13 +142,7 @@ func (c *Client) Close(ctx context.Context) error {
135// createPowernapClient creates a new powernap client with the current configuration.
143// createPowernapClient creates a new powernap client with the current configuration.
136func (c *Client) createPowernapClient() error {
144func (c *Client) createPowernapClient() error {
137− workDir, err := os.Getwd()
139− return fmt.Errorf("failed to get working directory: %w", err)
142− rootURI := string(protocol.URIFromPath(workDir))
145+ rootURI := string(protocol.URIFromPath(c.cwd))
145 command, err := c.resolver.ResolveValue(c.config.Command)
147 command, err := c.resolver.ResolveValue(c.config.Command)
146 if err != nil {
148 if err != nil {
@@ -157,7 +159,7 @@ func (c *Client) createPowernapClient() error {
157 WorkspaceFolders: []protocol.WorkspaceFolder{
159 WorkspaceFolders: []protocol.WorkspaceFolder{
159 URI: rootURI,
161 URI: rootURI,
160− Name: filepath.Base(workDir),
162+ Name: filepath.Base(c.cwd),
@@ -321,15 +323,8 @@ type OpenFileInfo struct {
321// HandlesFile checks if this LSP client handles the given file based on its
323// HandlesFile checks if this LSP client handles the given file based on its
322// extension and whether it's within the working directory.
324// extension and whether it's within the working directory.
323func (c *Client) HandlesFile(path string) bool {
325func (c *Client) HandlesFile(path string) bool {
324− // Check if file is within working directory.
326+ if !fsext.HasPrefix(path, c.cwd) {
325− absPath, err := filepath.Abs(path)
327− slog.Debug("Cannot resolve path", "name", c.name, "file", path, "error", err)
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)
327+ slog.Debug("File outside workspace", "name", c.name, "file", path, "workDir", c.cwd)
333 return false
328 return false
335 return handlesFiletype(c.name, c.fileTypes, path)
330 return handlesFiletype(c.name, c.fileTypes, path)
@@ -472,31 +467,6 @@ func (c *Client) OpenFileOnDemand(ctx context.Context, filepath string) error {
472 return c.OpenFile(ctx, filepath)
467 return c.OpenFile(ctx, filepath)
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)
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)
485− // Give the LSP server a moment to process the file
486− time.Sleep(100 * time.Millisecond)
490− diagnostics, _ := c.diagnostics.Get(documentURI)
492− return diagnostics, nil
495−// ClearDiagnosticsForURI removes diagnostics for a specific URI from the cache.
496−func (c *Client) ClearDiagnosticsForURI(uri protocol.DocumentURI) {
497− c.diagnostics.Del(uri)
500// RegisterNotificationHandler registers a notification handler.
470// RegisterNotificationHandler registers a notification handler.
501func (c *Client) RegisterNotificationHandler(method string, handler transport.NotificationHandler) {
471func (c *Client) RegisterNotificationHandler(method string, handler transport.NotificationHandler) {
502 c.client.RegisterNotificationHandler(method, handler)
472 c.client.RegisterNotificationHandler(method, handler)
@@ -507,11 +477,6 @@ func (c *Client) RegisterServerRequestHandler(method string, handler transport.H
507 c.client.RegisterHandler(method, handler)
477 c.client.RegisterHandler(method, handler)
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)
515// openKeyConfigFiles opens important configuration files that help initialize the server.
480// openKeyConfigFiles opens important configuration files that help initialize the server.
516func (c *Client) openKeyConfigFiles(ctx context.Context) {
481func (c *Client) openKeyConfigFiles(ctx context.Context) {
517 wd, err := os.Getwd()
482 wd, err := os.Getwd()