11package lsp
22
33import (
4+ "context"
45 "sync"
56
7+ "github.com/rs/zerolog/log"
68 protocol "github.com/tliron/glsp/protocol_3_16"
79)
810
11+ // TODO: update docs
912// In memory store for diagnostics. Applies mutex locking for concurrent access.
10- type DiagnosticsStore struct {
13+ type DiagnosticsService struct {
1114 diagnostics map [ProjectId ]map [protocol.DocumentUri ][]protocol.Diagnostic
12- mu sync.Mutex
15+ listeners map [ProjectId ]map [LanguageId ]* listenerInfo
16+ mu sync.RWMutex
1317}
1418
15- func (d * DiagnosticsStore ) Get (projectId ProjectId , uri protocol.DocumentUri ) ([]protocol.Diagnostic , bool ) {
16- d .mu .Lock ()
17- defer d .mu .Unlock ()
19+ type listenerInfo struct {
20+ cancel context.CancelFunc
21+ channel chan protocol.PublishDiagnosticsParams
22+ }
23+
24+ func NewDiagnosticsService () * DiagnosticsService {
25+ return & DiagnosticsService {
26+ diagnostics : make (map [ProjectId ]map [protocol.DocumentUri ][]protocol.Diagnostic ),
27+ listeners : make (map [ProjectId ]map [LanguageId ]* listenerInfo ),
28+ }
29+ }
30+
31+ func (d * DiagnosticsService ) Get (projectId ProjectId , uri protocol.DocumentUri ) ([]protocol.Diagnostic , bool ) {
32+ d .mu .RLock ()
33+ defer d .mu .RUnlock ()
1834
1935 if diagnostics , ok := d .diagnostics [projectId ]; ok {
2036 if diagnostics , ok := diagnostics [uri ]; ok {
@@ -25,7 +41,7 @@ func (d *DiagnosticsStore) Get(projectId ProjectId, uri protocol.DocumentUri) ([
2541 return nil , false
2642}
2743
28- func (d * DiagnosticsStore ) Set (projectId ProjectId , uri protocol.DocumentUri , diagnostics []protocol.Diagnostic ) {
44+ func (d * DiagnosticsService ) set (projectId ProjectId , uri protocol.DocumentUri , diagnostics []protocol.Diagnostic ) {
2945 d .mu .Lock ()
3046 defer d .mu .Unlock ()
3147
@@ -36,15 +52,76 @@ func (d *DiagnosticsStore) Set(projectId ProjectId, uri protocol.DocumentUri, di
3652 d.diagnostics [projectId ][uri ] = diagnostics
3753}
3854
39- func (d * DiagnosticsStore ) DeleteAllForProject (projectId ProjectId ) {
55+ func (d * DiagnosticsService ) StartListener (projectId ProjectId , languageId LanguageId , channel chan protocol. PublishDiagnosticsParams ) {
4056 d .mu .Lock ()
4157 defer d .mu .Unlock ()
4258
43- delete (d .diagnostics , projectId )
59+ if _ , exists := d .listeners [projectId ]; ! exists {
60+ d .listeners [projectId ] = make (map [LanguageId ]* listenerInfo )
61+ }
62+
63+ // TODO: should we cancel or return error?
64+ // Cancel existing listener if any
65+ if info , exists := d.listeners [projectId ][languageId ]; exists {
66+ info .cancel ()
67+ close (info .channel )
68+ }
69+
70+ ctx , cancel := context .WithCancel (context .Background ())
71+ d.listeners [projectId ][languageId ] = & listenerInfo {
72+ cancel : cancel ,
73+ channel : channel ,
74+ }
75+
76+ go d .listen (ctx , projectId , languageId , channel )
4477}
4578
46- func NewDiagnosticsStore () * DiagnosticsStore {
47- return & DiagnosticsStore {
48- diagnostics : make (map [ProjectId ]map [protocol.DocumentUri ][]protocol.Diagnostic ),
79+ func (d * DiagnosticsService ) StopListener (projectId ProjectId , languageId LanguageId ) {
80+ d .mu .Lock ()
81+ defer d .mu .Unlock ()
82+
83+ if projectListeners , exists := d .listeners [projectId ]; exists {
84+ if info , exists := projectListeners [languageId ]; exists {
85+ info .cancel ()
86+ close (info .channel )
87+ delete (projectListeners , languageId )
88+ }
89+ if len (projectListeners ) == 0 {
90+ delete (d .listeners , projectId )
91+ }
4992 }
5093}
94+
95+ func (d * DiagnosticsService ) listen (ctx context.Context , projectId ProjectId , languageId LanguageId , channel chan protocol.PublishDiagnosticsParams ) {
96+ for {
97+ select {
98+ case <- ctx .Done ():
99+ return
100+ case diagnostics , ok := <- channel :
101+ if ! ok {
102+ return
103+ }
104+ d .set (projectId , diagnostics .URI , diagnostics .Diagnostics )
105+ log .Debug ().
106+ Str ("projectId" , string (projectId )).
107+ Str ("languageId" , string (languageId )).
108+ Str ("uri" , string (diagnostics .URI )).
109+ Msgf ("Received diagnostics: %d" , len (diagnostics .Diagnostics ))
110+ }
111+ }
112+ }
113+
114+ func (d * DiagnosticsService ) DeleteAllForProject (projectId ProjectId ) {
115+ d .mu .Lock ()
116+ defer d .mu .Unlock ()
117+
118+ delete (d .diagnostics , projectId )
119+ if projectListeners , exists := d .listeners [projectId ]; exists {
120+ for _ , info := range projectListeners {
121+ info .cancel ()
122+ close (info .channel )
123+ }
124+ delete (d .listeners , projectId )
125+ }
126+ }
127+
0 commit comments