Skip to content

Commit 3c965cb

Browse files
committed
Refresh open file diagnostics on config file change
1 parent bf00a76 commit 3c965cb

File tree

7 files changed

+296
-12
lines changed

7 files changed

+296
-12
lines changed

internal/api/api.go

+5
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,11 @@ func (api *API) PositionEncoding() lsproto.PositionEncodingKind {
119119
return lsproto.PositionEncodingKindUTF8
120120
}
121121

122+
// Client implements ProjectHost.
123+
func (api *API) Client() project.Client {
124+
return nil
125+
}
126+
122127
func (api *API) HandleRequest(id int, method string, payload []byte) ([]byte, error) {
123128
params, err := unmarshalPayload(method, payload)
124129
if err != nil {

internal/lsp/lsproto/jsonrpc.go

+21-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ type ID struct {
2929
int int32
3030
}
3131

32+
func NewIDString(str string) *ID {
33+
return &ID{str: str}
34+
}
35+
3236
func (id *ID) MarshalJSON() ([]byte, error) {
3337
if id.str != "" {
3438
return json.Marshal(id.str)
@@ -44,6 +48,13 @@ func (id *ID) UnmarshalJSON(data []byte) error {
4448
return json.Unmarshal(data, &id.int)
4549
}
4650

51+
func (id *ID) TryInt() (int32, bool) {
52+
if id == nil || id.str != "" {
53+
return 0, false
54+
}
55+
return id.int, true
56+
}
57+
4758
func (id *ID) MustInt() int32 {
4859
if id.str != "" {
4960
panic("ID is not an integer")
@@ -55,11 +66,20 @@ func (id *ID) MustInt() int32 {
5566

5667
type RequestMessage struct {
5768
JSONRPC JSONRPCVersion `json:"jsonrpc"`
58-
ID *ID `json:"id"`
69+
ID *ID `json:"id,omitempty"`
5970
Method Method `json:"method"`
6071
Params any `json:"params"`
6172
}
6273

74+
func NewRequestMessage(method Method, id *ID, params any) *RequestMessage {
75+
return &RequestMessage{
76+
JSONRPC: JSONRPCVersion{},
77+
ID: id,
78+
Method: method,
79+
Params: params,
80+
}
81+
}
82+
6383
func (r *RequestMessage) UnmarshalJSON(data []byte) error {
6484
var raw struct {
6585
JSONRPC JSONRPCVersion `json:"jsonrpc"`

internal/lsp/server.go

+110-5
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,20 @@ func NewServer(opts *ServerOptions) *Server {
3939
newLine: opts.NewLine,
4040
fs: opts.FS,
4141
defaultLibraryPath: opts.DefaultLibraryPath,
42+
watchers: make(map[project.WatcherHandle]struct{}),
4243
}
4344
}
4445

4546
var _ project.ServiceHost = (*Server)(nil)
47+
var _ project.Client = (*Server)(nil)
4648

4749
type Server struct {
4850
r *lsproto.BaseReader
4951
w *lsproto.BaseWriter
5052

5153
stderr io.Writer
5254

55+
clientSeq int32
5356
requestMethod string
5457
requestTime time.Time
5558

@@ -61,36 +64,95 @@ type Server struct {
6164
initializeParams *lsproto.InitializeParams
6265
positionEncoding lsproto.PositionEncodingKind
6366

67+
watcheEnabled bool
68+
watcherID int
69+
watchers map[project.WatcherHandle]struct{}
6470
logger *project.Logger
6571
projectService *project.Service
6672
converters *ls.Converters
6773
}
6874

69-
// FS implements project.ProjectServiceHost.
75+
// FS implements project.ServiceHost.
7076
func (s *Server) FS() vfs.FS {
7177
return s.fs
7278
}
7379

74-
// DefaultLibraryPath implements project.ProjectServiceHost.
80+
// DefaultLibraryPath implements project.ServiceHost.
7581
func (s *Server) DefaultLibraryPath() string {
7682
return s.defaultLibraryPath
7783
}
7884

79-
// GetCurrentDirectory implements project.ProjectServiceHost.
85+
// GetCurrentDirectory implements project.ServiceHost.
8086
func (s *Server) GetCurrentDirectory() string {
8187
return s.cwd
8288
}
8389

84-
// NewLine implements project.ProjectServiceHost.
90+
// NewLine implements project.ServiceHost.
8591
func (s *Server) NewLine() string {
8692
return s.newLine.GetNewLineCharacter()
8793
}
8894

89-
// Trace implements project.ProjectServiceHost.
95+
// Trace implements project.ServiceHost.
9096
func (s *Server) Trace(msg string) {
9197
s.Log(msg)
9298
}
9399

100+
// Client implements project.ServiceHost.
101+
func (s *Server) Client() project.Client {
102+
if !s.watcheEnabled {
103+
return nil
104+
}
105+
return s
106+
}
107+
108+
// WatchFiles implements project.Client.
109+
func (s *Server) WatchFiles(watchers []lsproto.FileSystemWatcher) (project.WatcherHandle, error) {
110+
watcherId := fmt.Sprintf("watcher-%d", s.watcherID)
111+
if err := s.sendRequest(lsproto.MethodClientRegisterCapability, &lsproto.RegistrationParams{
112+
Registrations: []lsproto.Registration{
113+
{
114+
Id: watcherId,
115+
Method: string(lsproto.MethodWorkspaceDidChangeWatchedFiles),
116+
RegisterOptions: ptrTo(lsproto.LSPAny(lsproto.DidChangeWatchedFilesRegistrationOptions{
117+
Watchers: watchers,
118+
})),
119+
},
120+
},
121+
}); err != nil {
122+
return "", fmt.Errorf("failed to register file watcher: %w", err)
123+
}
124+
125+
handle := project.WatcherHandle(watcherId)
126+
s.watchers[handle] = struct{}{}
127+
s.watcherID++
128+
return handle, nil
129+
}
130+
131+
// UnwatchFiles implements project.Client.
132+
func (s *Server) UnwatchFiles(handle project.WatcherHandle) error {
133+
if _, ok := s.watchers[handle]; ok {
134+
if err := s.sendRequest(lsproto.MethodClientUnregisterCapability, &lsproto.UnregistrationParams{
135+
Unregisterations: []lsproto.Unregistration{
136+
{
137+
Id: string(handle),
138+
Method: string(lsproto.MethodWorkspaceDidChangeWatchedFiles),
139+
},
140+
},
141+
}); err != nil {
142+
return fmt.Errorf("failed to unregister file watcher: %w", err)
143+
}
144+
delete(s.watchers, handle)
145+
return nil
146+
}
147+
148+
return fmt.Errorf("no file watcher exists with ID %s", handle)
149+
}
150+
151+
// PublishDiagnostics implements project.Client.
152+
func (s *Server) PublishDiagnostics(params *lsproto.PublishDiagnosticsParams) error {
153+
return s.sendNotification(lsproto.MethodTextDocumentPublishDiagnostics, params)
154+
}
155+
94156
func (s *Server) Run() error {
95157
for {
96158
req, err := s.read()
@@ -104,6 +166,11 @@ func (s *Server) Run() error {
104166
return err
105167
}
106168

169+
// TODO: handle response messages
170+
if req == nil {
171+
continue
172+
}
173+
107174
if s.initializeParams == nil {
108175
if req.Method == lsproto.MethodInitialize {
109176
if err := s.handleInitialize(req); err != nil {
@@ -131,12 +198,37 @@ func (s *Server) read() (*lsproto.RequestMessage, error) {
131198

132199
req := &lsproto.RequestMessage{}
133200
if err := json.Unmarshal(data, req); err != nil {
201+
res := &lsproto.ResponseMessage{}
202+
if err := json.Unmarshal(data, res); err == nil {
203+
// !!! TODO: handle response
204+
return nil, nil
205+
}
134206
return nil, fmt.Errorf("%w: %w", lsproto.ErrInvalidRequest, err)
135207
}
136208

137209
return req, nil
138210
}
139211

212+
func (s *Server) sendRequest(method lsproto.Method, params any) error {
213+
s.clientSeq++
214+
id := lsproto.NewIDString(fmt.Sprintf("ts%d", s.clientSeq))
215+
req := lsproto.NewRequestMessage(method, id, params)
216+
data, err := json.Marshal(req)
217+
if err != nil {
218+
return err
219+
}
220+
return s.w.Write(data)
221+
}
222+
223+
func (s *Server) sendNotification(method lsproto.Method, params any) error {
224+
req := lsproto.NewRequestMessage(method, nil /*id*/, params)
225+
data, err := json.Marshal(req)
226+
if err != nil {
227+
return err
228+
}
229+
return s.w.Write(data)
230+
}
231+
140232
func (s *Server) sendResult(id *lsproto.ID, result any) error {
141233
return s.sendResponse(&lsproto.ResponseMessage{
142234
ID: id,
@@ -188,6 +280,8 @@ func (s *Server) handleMessage(req *lsproto.RequestMessage) error {
188280
return s.handleDidSave(req)
189281
case *lsproto.DidCloseTextDocumentParams:
190282
return s.handleDidClose(req)
283+
case *lsproto.DidChangeWatchedFilesParams:
284+
return s.handleDidChangeWatchedFiles(req)
191285
case *lsproto.DocumentDiagnosticParams:
192286
return s.handleDocumentDiagnostic(req)
193287
case *lsproto.HoverParams:
@@ -255,9 +349,14 @@ func (s *Server) handleInitialize(req *lsproto.RequestMessage) error {
255349
}
256350

257351
func (s *Server) handleInitialized(req *lsproto.RequestMessage) error {
352+
if s.initializeParams.Capabilities.Workspace.DidChangeWatchedFiles != nil && *s.initializeParams.Capabilities.Workspace.DidChangeWatchedFiles.DynamicRegistration {
353+
s.watcheEnabled = true
354+
}
355+
258356
s.logger = project.NewLogger([]io.Writer{s.stderr}, "" /*file*/, project.LogLevelVerbose)
259357
s.projectService = project.NewService(s, project.ServiceOptions{
260358
Logger: s.logger,
359+
WatchEnabled: s.watcheEnabled,
261360
PositionEncoding: s.positionEncoding,
262361
})
263362

@@ -315,6 +414,12 @@ func (s *Server) handleDidClose(req *lsproto.RequestMessage) error {
315414
return nil
316415
}
317416

417+
func (s *Server) handleDidChangeWatchedFiles(req *lsproto.RequestMessage) error {
418+
params := req.Params.(*lsproto.DidChangeWatchedFilesParams)
419+
s.projectService.OnWatchedFilesChanged(params.Changes)
420+
return nil
421+
}
422+
318423
func (s *Server) handleDocumentDiagnostic(req *lsproto.RequestMessage) error {
319424
params := req.Params.(*lsproto.DocumentDiagnosticParams)
320425
file, project := s.getFileAndProject(params.TextDocument.Uri)

internal/project/host.go

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
11
package project
22

3-
import "github.com/microsoft/typescript-go/internal/vfs"
3+
import (
4+
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
5+
"github.com/microsoft/typescript-go/internal/vfs"
6+
)
7+
8+
type WatcherHandle string
9+
10+
type Client interface {
11+
WatchFiles(watchers []lsproto.FileSystemWatcher) (WatcherHandle, error)
12+
UnwatchFiles(handle WatcherHandle) error
13+
PublishDiagnostics(params *lsproto.PublishDiagnosticsParams) error
14+
}
415

516
type ServiceHost interface {
617
FS() vfs.FS
718
DefaultLibraryPath() string
819
GetCurrentDirectory() string
920
NewLine() string
21+
22+
Client() Client
1023
}

0 commit comments

Comments
 (0)