Skip to content

Commit 055ce1b

Browse files
committed
changeg filewatcher to send didChangeWatchedFiles and properly register capabilities
1 parent 254f3c4 commit 055ce1b

File tree

7 files changed

+568
-318
lines changed

7 files changed

+568
-318
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ Add something like the following configuration to your Claude Desktop settings (
7171
"run",
7272
"github.com/isaacphi/mcp-language-server@latest",
7373
"--workspace",
74-
"/Users/you/dev/yourpythoncodebase",
74+
"/Users/you/dev/yourcodebase",
7575
"--lsp",
7676
"/opt/homebrew/bin/pyright-langserver",
7777
"--",
@@ -87,7 +87,7 @@ Add something like the following configuration to your Claude Desktop settings (
8787

8888
Replace:
8989

90-
- `/Users/you/dev/yourpythoncodebase` with the absolute path to your project
90+
- `/Users/you/dev/yourcodebase` with the absolute path to your project
9191
- `/opt/homebrew/bin/pyright-langserver` with the path to your language server (found using `which` command e.g. `which pyright-langserver`)
9292
- Any aruments after `--` are sent as arguments to your language server.
9393
- Any env variables are passed on to the language server. Some may be necessary for you language server. For example, `gopls` required `GOPATH` and `GOCACHE` in order for me to get it working properly.

internal/lsp/client.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@ func (c *Client) InitializeLSPClient(ctx context.Context, workspaceDir string) (
139139
DynamicRegistration: true,
140140
},
141141
DidChangeWatchedFiles: protocol.DidChangeWatchedFilesClientCapabilities{
142-
DynamicRegistration: true,
142+
DynamicRegistration: true,
143+
RelativePatternSupport: true,
143144
},
144145
},
145146
TextDocument: protocol.TextDocumentClientCapabilities{
@@ -363,12 +364,9 @@ func (c *Client) CloseFile(ctx context.Context, filepath string) error {
363364
},
364365
}
365366
log.Println("Closing", params.TextDocument.URI.Dir())
366-
// TODO: properly close files. typescript-language-server currently
367-
// doesn't work properly whith my file watcher implementation
368-
369-
// if err := c.Notify(ctx, "textDocument/didClose", params); err != nil {
370-
// return err
371-
// }
367+
if err := c.Notify(ctx, "textDocument/didClose", params); err != nil {
368+
return err
369+
}
372370

373371
c.openFilesMu.Lock()
374372
delete(c.openFiles, uri)

internal/lsp/initializeTypescriptLanguageServer.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,3 @@ func findAllTypeScriptFiles(rootDir string) ([]string, error) {
6262

6363
return tsFiles, nil
6464
}
65-

internal/lsp/server-request-handlers.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,35 @@ func HandleWorkspaceConfiguration(params json.RawMessage) (interface{}, error) {
1515
}
1616

1717
func HandleRegisterCapability(params json.RawMessage) (interface{}, error) {
18+
var registerParams protocol.RegistrationParams
19+
if err := json.Unmarshal(params, &registerParams); err != nil {
20+
log.Printf("Error unmarshaling registration params: %v", err)
21+
return nil, err
22+
}
23+
24+
for _, reg := range registerParams.Registrations {
25+
log.Printf("Registration received for method: %s, id: %s", reg.Method, reg.ID)
26+
27+
switch reg.Method {
28+
case "workspace/didChangeWatchedFiles":
29+
// Parse the registration options
30+
optionsJSON, err := json.Marshal(reg.RegisterOptions)
31+
if err != nil {
32+
log.Printf("Error marshaling registration options: %v", err)
33+
continue
34+
}
35+
36+
var options protocol.DidChangeWatchedFilesRegistrationOptions
37+
if err := json.Unmarshal(optionsJSON, &options); err != nil {
38+
log.Printf("Error unmarshaling registration options: %v", err)
39+
continue
40+
}
41+
42+
// Store the file watchers registrations
43+
notifyFileWatchRegistration(reg.ID, options.Watchers)
44+
}
45+
}
46+
1847
return nil, nil
1948
}
2049

@@ -33,6 +62,24 @@ func HandleApplyEdit(params json.RawMessage) (interface{}, error) {
3362
return protocol.ApplyWorkspaceEditResult{Applied: true}, nil
3463
}
3564

65+
// FileWatchRegistrationHandler is a function that will be called when file watch registrations are received
66+
type FileWatchRegistrationHandler func(id string, watchers []protocol.FileSystemWatcher)
67+
68+
// fileWatchHandler holds the current handler for file watch registrations
69+
var fileWatchHandler FileWatchRegistrationHandler
70+
71+
// RegisterFileWatchHandler sets the handler for file watch registrations
72+
func RegisterFileWatchHandler(handler FileWatchRegistrationHandler) {
73+
fileWatchHandler = handler
74+
}
75+
76+
// notifyFileWatchRegistration notifies the handler about new file watch registrations
77+
func notifyFileWatchRegistration(id string, watchers []protocol.FileSystemWatcher) {
78+
if fileWatchHandler != nil {
79+
fileWatchHandler(id, watchers)
80+
}
81+
}
82+
3683
// Notifications
3784

3885
func HandleServerMessage(params json.RawMessage) {
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package protocol
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
)
7+
8+
// PatternInfo is an interface for types that represent glob patterns
9+
type PatternInfo interface {
10+
GetPattern() string
11+
GetBasePath() string
12+
isPattern() // marker method
13+
}
14+
15+
// StringPattern implements PatternInfo for string patterns
16+
type StringPattern struct {
17+
Pattern string
18+
}
19+
20+
func (p StringPattern) GetPattern() string { return p.Pattern }
21+
func (p StringPattern) GetBasePath() string { return "" }
22+
func (p StringPattern) isPattern() {}
23+
24+
// RelativePatternInfo implements PatternInfo for RelativePattern
25+
type RelativePatternInfo struct {
26+
RP RelativePattern
27+
BasePath string
28+
}
29+
30+
func (p RelativePatternInfo) GetPattern() string { return string(p.RP.Pattern) }
31+
func (p RelativePatternInfo) GetBasePath() string { return p.BasePath }
32+
func (p RelativePatternInfo) isPattern() {}
33+
34+
// AsPattern converts GlobPattern to a PatternInfo object
35+
func (g *GlobPattern) AsPattern() (PatternInfo, error) {
36+
if g.Value == nil {
37+
return nil, fmt.Errorf("nil pattern")
38+
}
39+
40+
switch v := g.Value.(type) {
41+
case string:
42+
return StringPattern{Pattern: v}, nil
43+
case RelativePattern:
44+
// Handle BaseURI which could be string or DocumentUri
45+
basePath := ""
46+
switch baseURI := v.BaseURI.Value.(type) {
47+
case string:
48+
basePath = strings.TrimPrefix(baseURI, "file://")
49+
case DocumentUri:
50+
basePath = strings.TrimPrefix(string(baseURI), "file://")
51+
default:
52+
return nil, fmt.Errorf("unknown BaseURI type: %T", v.BaseURI.Value)
53+
}
54+
return RelativePatternInfo{RP: v, BasePath: basePath}, nil
55+
default:
56+
return nil, fmt.Errorf("unknown pattern type: %T", g.Value)
57+
}
58+
}

0 commit comments

Comments
 (0)