diff --git a/internal/lsp/client.go b/internal/lsp/client.go index fc07059..aff9630 100644 --- a/internal/lsp/client.go +++ b/internal/lsp/client.go @@ -116,12 +116,12 @@ func (c *Client) RegisterServerRequestHandler(method string, handler ServerReque func (c *Client) InitializeLSPClient(ctx context.Context, workspaceDir string) (*protocol.InitializeResult, error) { initParams := &protocol.InitializeParams{ WorkspaceFoldersInitializeParams: protocol.WorkspaceFoldersInitializeParams{ - WorkspaceFolders: []protocol.WorkspaceFolder{ - { - URI: protocol.URI("file://" + workspaceDir), - Name: workspaceDir, - }, - }, + WorkspaceFolders: []protocol.WorkspaceFolder{ + { + URI: protocol.URI(string(protocol.URIFromPath(workspaceDir))), + Name: workspaceDir, + }, + }, }, XInitializeParams: protocol.XInitializeParams{ @@ -131,7 +131,7 @@ func (c *Client) InitializeLSPClient(ctx context.Context, workspaceDir string) ( Version: "0.1.0", }, RootPath: workspaceDir, - RootURI: protocol.DocumentUri("file://" + workspaceDir), + RootURI: protocol.URIFromPath(workspaceDir), Capabilities: protocol.ClientCapabilities{ Workspace: protocol.WorkspaceClientCapabilities{ Configuration: true, @@ -215,7 +215,7 @@ func (c *Client) InitializeLSPClient(ctx context.Context, workspaceDir string) ( } // LSP sepecific Initialization - path := strings.ToLower(c.Cmd.Path) + path := strings.ToLower(c.Cmd.Path) switch { case strings.Contains(path, "typescript-language-server"): err := initializeTypescriptLanguageServer(ctx, c, workspaceDir) @@ -287,13 +287,13 @@ type OpenFileInfo struct { } func (c *Client) OpenFile(ctx context.Context, filepath string) error { - uri := fmt.Sprintf("file://%s", filepath) + docURI := protocol.URIFromPath(filepath) - c.openFilesMu.Lock() - if _, exists := c.openFiles[uri]; exists { - c.openFilesMu.Unlock() - return nil // Already open - } + c.openFilesMu.Lock() + if _, exists := c.openFiles[string(docURI)]; exists { + c.openFilesMu.Unlock() + return nil // Already open + } c.openFilesMu.Unlock() // Skip files that do not exist or cannot be read @@ -302,41 +302,41 @@ func (c *Client) OpenFile(ctx context.Context, filepath string) error { return fmt.Errorf("error reading file: %w", err) } - params := protocol.DidOpenTextDocumentParams{ - TextDocument: protocol.TextDocumentItem{ - URI: protocol.DocumentUri(uri), - LanguageID: DetectLanguageID(uri), - Version: 1, - Text: string(content), - }, - } + params := protocol.DidOpenTextDocumentParams{ + TextDocument: protocol.TextDocumentItem{ + URI: docURI, + LanguageID: DetectLanguageID(filepath), + Version: 1, + Text: string(content), + }, + } if err := c.Notify(ctx, "textDocument/didOpen", params); err != nil { return err } - c.openFilesMu.Lock() - c.openFiles[uri] = &OpenFileInfo{ - Version: 1, - URI: protocol.DocumentUri(uri), - } - c.openFilesMu.Unlock() + c.openFilesMu.Lock() + c.openFiles[string(docURI)] = &OpenFileInfo{ + Version: 1, + URI: docURI, + } + c.openFilesMu.Unlock() - lspLogger.Debug("Opened file: %s", filepath) + lspLogger.Debug("Opened file: %s", filepath) return nil } func (c *Client) NotifyChange(ctx context.Context, filepath string) error { - uri := fmt.Sprintf("file://%s", filepath) + docURI := protocol.URIFromPath(filepath) content, err := os.ReadFile(filepath) if err != nil { return fmt.Errorf("error reading file: %w", err) } - c.openFilesMu.Lock() - fileInfo, isOpen := c.openFiles[uri] + c.openFilesMu.Lock() + fileInfo, isOpen := c.openFiles[string(docURI)] if !isOpen { c.openFilesMu.Unlock() return fmt.Errorf("cannot notify change for unopened file: %s", filepath) @@ -347,58 +347,58 @@ func (c *Client) NotifyChange(ctx context.Context, filepath string) error { version := fileInfo.Version c.openFilesMu.Unlock() - params := protocol.DidChangeTextDocumentParams{ - TextDocument: protocol.VersionedTextDocumentIdentifier{ - TextDocumentIdentifier: protocol.TextDocumentIdentifier{ - URI: protocol.DocumentUri(uri), - }, - Version: version, - }, - ContentChanges: []protocol.TextDocumentContentChangeEvent{ - { - Value: protocol.TextDocumentContentChangeWholeDocument{ - Text: string(content), - }, - }, - }, - } + params := protocol.DidChangeTextDocumentParams{ + TextDocument: protocol.VersionedTextDocumentIdentifier{ + TextDocumentIdentifier: protocol.TextDocumentIdentifier{ + URI: docURI, + }, + Version: version, + }, + ContentChanges: []protocol.TextDocumentContentChangeEvent{ + { + Value: protocol.TextDocumentContentChangeWholeDocument{ + Text: string(content), + }, + }, + }, + } return c.Notify(ctx, "textDocument/didChange", params) } func (c *Client) CloseFile(ctx context.Context, filepath string) error { - uri := fmt.Sprintf("file://%s", filepath) - - c.openFilesMu.Lock() - if _, exists := c.openFiles[uri]; !exists { - c.openFilesMu.Unlock() - return nil // Already closed - } - c.openFilesMu.Unlock() - - params := protocol.DidCloseTextDocumentParams{ - TextDocument: protocol.TextDocumentIdentifier{ - URI: protocol.DocumentUri(uri), - }, - } + docURI := protocol.URIFromPath(filepath) + + c.openFilesMu.Lock() + if _, exists := c.openFiles[string(docURI)]; !exists { + c.openFilesMu.Unlock() + return nil // Already closed + } + c.openFilesMu.Unlock() + + params := protocol.DidCloseTextDocumentParams{ + TextDocument: protocol.TextDocumentIdentifier{ + URI: docURI, + }, + } lspLogger.Debug("Closing file: %s", params.TextDocument.URI.Dir()) if err := c.Notify(ctx, "textDocument/didClose", params); err != nil { return err } - c.openFilesMu.Lock() - delete(c.openFiles, uri) - c.openFilesMu.Unlock() + c.openFilesMu.Lock() + delete(c.openFiles, string(docURI)) + c.openFilesMu.Unlock() return nil } func (c *Client) IsFileOpen(filepath string) bool { - uri := fmt.Sprintf("file://%s", filepath) - c.openFilesMu.RLock() - defer c.openFilesMu.RUnlock() - _, exists := c.openFiles[uri] - return exists + uri := string(protocol.URIFromPath(filepath)) + c.openFilesMu.RLock() + defer c.openFilesMu.RUnlock() + _, exists := c.openFiles[uri] + return exists } // CloseAllFiles closes all currently open files @@ -406,12 +406,12 @@ func (c *Client) CloseAllFiles(ctx context.Context) { c.openFilesMu.Lock() filesToClose := make([]string, 0, len(c.openFiles)) - // First collect all URIs that need to be closed - for uri := range c.openFiles { - // Convert URI back to file path by trimming "file://" prefix - filePath := strings.TrimPrefix(uri, "file://") - filesToClose = append(filesToClose, filePath) - } + // First collect all URIs that need to be closed + for uri := range c.openFiles { + // Convert URI back to file path using protocol utilities + filePath := protocol.DocumentUri(uri).Path() + filesToClose = append(filesToClose, filePath) + } c.openFilesMu.Unlock() // Then close them all diff --git a/internal/tools/diagnostics.go b/internal/tools/diagnostics.go index 5b96b40..0646ac0 100644 --- a/internal/tools/diagnostics.go +++ b/internal/tools/diagnostics.go @@ -31,7 +31,7 @@ func GetDiagnosticsForFile(ctx context.Context, client *lsp.Client, filePath str time.Sleep(time.Second * 3) // Convert the file path to URI format - uri := protocol.DocumentUri("file://" + filePath) + uri := protocol.URIFromPath(filePath) // Request fresh diagnostics diagParams := protocol.DocumentDiagnosticParams{ diff --git a/internal/tools/execute-codelens.go b/internal/tools/execute-codelens.go index 11e02af..e8c8817 100644 --- a/internal/tools/execute-codelens.go +++ b/internal/tools/execute-codelens.go @@ -21,7 +21,7 @@ func ExecuteCodeLens(ctx context.Context, client *lsp.Client, filePath string, i // Get code lenses docIdentifier := protocol.TextDocumentIdentifier{ - URI: protocol.DocumentUri("file://" + filePath), + URI: protocol.URIFromPath(filePath), } params := protocol.CodeLensParams{ diff --git a/internal/tools/get-codelens.go b/internal/tools/get-codelens.go index c6f3611..654530a 100644 --- a/internal/tools/get-codelens.go +++ b/internal/tools/get-codelens.go @@ -21,7 +21,7 @@ func GetCodeLens(ctx context.Context, client *lsp.Client, filePath string) (stri // Create document identifier docIdentifier := protocol.TextDocumentIdentifier{ - URI: protocol.DocumentUri("file://" + filePath), + URI: protocol.URIFromPath(filePath), } // Request code lens from LSP diff --git a/internal/tools/hover.go b/internal/tools/hover.go index 874d527..d14cfc0 100644 --- a/internal/tools/hover.go +++ b/internal/tools/hover.go @@ -24,7 +24,7 @@ func GetHoverInfo(ctx context.Context, client *lsp.Client, filePath string, line Line: uint32(line - 1), Character: uint32(column - 1), } - uri := protocol.DocumentUri("file://" + filePath) + uri := protocol.URIFromPath(filePath) params.TextDocument = protocol.TextDocumentIdentifier{ URI: uri, } diff --git a/internal/tools/lsp-utilities.go b/internal/tools/lsp-utilities.go index ae7d70b..d8f42df 100644 --- a/internal/tools/lsp-utilities.go +++ b/internal/tools/lsp-utilities.go @@ -60,7 +60,7 @@ func GetFullDefinition(ctx context.Context, client *lsp.Client, startLocation pr if found { // Convert URI to filesystem path - filePath, err := url.PathUnescape(strings.TrimPrefix(string(startLocation.URI), "file://")) + filePath, err := url.PathUnescape(protocol.DocumentUri(string(startLocation.URI)).Path()) if err != nil { return "", protocol.Location{}, fmt.Errorf("failed to unescape URI: %w", err) } diff --git a/internal/tools/references.go b/internal/tools/references.go index cb424e5..f900999 100644 --- a/internal/tools/references.go +++ b/internal/tools/references.go @@ -94,7 +94,7 @@ func FindReferences(ctx context.Context, client *lsp.Client, symbolName string) for _, uriStr := range uris { uri := protocol.DocumentUri(uriStr) fileRefs := refsByFile[uri] - filePath := strings.TrimPrefix(uriStr, "file://") + filePath := protocol.DocumentUri(uriStr).Path() // Format file header fileInfo := fmt.Sprintf("---\n\n%s\nReferences in File: %d\n", diff --git a/internal/tools/rename-symbol.go b/internal/tools/rename-symbol.go index f51e3e7..423aa20 100644 --- a/internal/tools/rename-symbol.go +++ b/internal/tools/rename-symbol.go @@ -21,7 +21,7 @@ func RenameSymbol(ctx context.Context, client *lsp.Client, filePath string, line } // Convert 1-indexed line/column to 0-indexed for LSP protocol - uri := protocol.DocumentUri("file://" + filePath) + uri := protocol.URIFromPath(filePath) position := protocol.Position{ Line: uint32(line - 1), Character: uint32(column - 1), diff --git a/internal/tools/utilities.go b/internal/tools/utilities.go index e5beb28..7f7e423 100644 --- a/internal/tools/utilities.go +++ b/internal/tools/utilities.go @@ -11,7 +11,7 @@ import ( ) func ExtractTextFromLocation(loc protocol.Location) (string, error) { - path := strings.TrimPrefix(string(loc.URI), "file://") + path := protocol.DocumentUri(string(loc.URI)).Path() content, err := os.ReadFile(path) if err != nil { diff --git a/internal/watcher/watcher.go b/internal/watcher/watcher.go index 6e7a0b8..830a28c 100644 --- a/internal/watcher/watcher.go +++ b/internal/watcher/watcher.go @@ -220,7 +220,7 @@ func (w *WorkspaceWatcher) WatchWorkspace(ctx context.Context, workspacePath str return } - uri := fmt.Sprintf("file://%s", event.Name) + uri := string(protocol.URIFromPath(event.Name)) // Check if this is a file (not a directory) and should be excluded isFile := false