99 "fmt"
1010 "os"
1111 "strconv"
12- "strings"
1312 "sync"
1413 "sync/atomic"
1514
@@ -288,12 +287,11 @@ func (s *Session) viewOf(uri span.URI) (*View, error) {
288287 return v , nil
289288 }
290289 // Pick the best view for this file and memoize the result.
291- v , err := s .bestView (uri )
292- if err != nil {
293- return nil , err
290+ if len (s .views ) == 0 {
291+ return nil , fmt .Errorf ("no views in session" )
294292 }
295- s .viewMap [uri ] = v
296- return v , nil
293+ s .viewMap [uri ] = bestViewForURI ( uri , s . views )
294+ return s . viewMap [ uri ] , nil
297295}
298296
299297func (s * Session ) viewsOf (uri span.URI ) []* View {
@@ -302,7 +300,7 @@ func (s *Session) viewsOf(uri span.URI) []*View {
302300
303301 var views []* View
304302 for _ , view := range s .views {
305- if strings . HasPrefix ( string ( uri ), string ( view . Folder () )) {
303+ if source . InDir ( view . folder . Filename ( ), uri . Filename ( )) {
306304 views = append (views , view )
307305 }
308306 }
@@ -319,15 +317,12 @@ func (s *Session) Views() []source.View {
319317 return result
320318}
321319
322- // bestView finds the best view to associate a given URI with.
323- // viewMu must be held when calling this method.
324- func (s * Session ) bestView (uri span.URI ) (* View , error ) {
325- if len (s .views ) == 0 {
326- return nil , errors .Errorf ("no views in the session" )
327- }
320+ // bestViewForURI returns the most closely matching view for the given URI
321+ // out of the given set of views.
322+ func bestViewForURI (uri span.URI , views []* View ) * View {
328323 // we need to find the best view for this file
329324 var longest * View
330- for _ , view := range s . views {
325+ for _ , view := range views {
331326 if longest != nil && len (longest .Folder ()) > len (view .Folder ()) {
332327 continue
333328 }
@@ -336,16 +331,16 @@ func (s *Session) bestView(uri span.URI) (*View, error) {
336331 }
337332 }
338333 if longest != nil {
339- return longest , nil
334+ return longest
340335 }
341336 // Try our best to return a view that knows the file.
342- for _ , view := range s . views {
337+ for _ , view := range views {
343338 if view .knownFile (uri ) {
344- return view , nil
339+ return view
345340 }
346341 }
347342 // TODO: are there any more heuristics we can use?
348- return s . views [0 ], nil
343+ return views [0 ]
349344}
350345
351346func (s * Session ) removeView (ctx context.Context , view * View ) error {
@@ -405,7 +400,7 @@ func (s *Session) dropView(ctx context.Context, v *View) (int, error) {
405400}
406401
407402func (s * Session ) ModifyFiles (ctx context.Context , changes []source.FileModification ) error {
408- _ , _ , releases , err := s .DidModifyFiles (ctx , changes )
403+ _ , releases , err := s .DidModifyFiles (ctx , changes )
409404 for _ , release := range releases {
410405 release ()
411406 }
@@ -418,13 +413,13 @@ type fileChange struct {
418413 fileHandle source.VersionedFileHandle
419414}
420415
421- func (s * Session ) DidModifyFiles (ctx context.Context , changes []source.FileModification ) (map [span. URI ] source.View , map [source. View ]source. Snapshot , []func (), error ) {
416+ func (s * Session ) DidModifyFiles (ctx context.Context , changes []source.FileModification ) (map [source.Snapshot ][]span. URI , []func (), error ) {
422417 views := make (map [* View ]map [span.URI ]* fileChange )
423- bestViews := map [span.URI ]source. View {}
418+ affectedViews := map [span.URI ][] * View {}
424419
425420 overlays , err := s .updateOverlays (ctx , changes )
426421 if err != nil {
427- return nil , nil , nil , err
422+ return nil , nil , err
428423 }
429424 var forceReloadMetadata bool
430425 for _ , c := range changes {
@@ -433,12 +428,6 @@ func (s *Session) DidModifyFiles(ctx context.Context, changes []source.FileModif
433428 }
434429
435430 // Build the list of affected views.
436- bestView , err := s .viewOf (c .URI )
437- if err != nil {
438- return nil , nil , nil , err
439- }
440- bestViews [c .URI ] = bestView
441-
442431 var changedViews []* View
443432 for _ , view := range s .views {
444433 // Don't propagate changes that are outside of the view's scope
@@ -448,16 +437,25 @@ func (s *Session) DidModifyFiles(ctx context.Context, changes []source.FileModif
448437 }
449438 changedViews = append (changedViews , view )
450439 }
451- // If no view matched the change, assign it to the best view.
440+ // If the change is not relevant to any view, but the change is
441+ // happening in the editor, assign it the most closely matching view.
452442 if len (changedViews ) == 0 {
443+ if c .OnDisk {
444+ continue
445+ }
446+ bestView , err := s .viewOf (c .URI )
447+ if err != nil {
448+ return nil , nil , err
449+ }
453450 changedViews = append (changedViews , bestView )
454451 }
452+ affectedViews [c .URI ] = changedViews
455453
456454 // Apply the changes to all affected views.
457455 for _ , view := range changedViews {
458456 // Make sure that the file is added to the view.
459457 if _ , err := view .getFile (c .URI ); err != nil {
460- return nil , nil , nil , err
458+ return nil , nil , err
461459 }
462460 if _ , ok := views [view ]; ! ok {
463461 views [view ] = make (map [span.URI ]* fileChange )
@@ -471,7 +469,7 @@ func (s *Session) DidModifyFiles(ctx context.Context, changes []source.FileModif
471469 } else {
472470 fsFile , err := s .cache .getFile (ctx , c .URI )
473471 if err != nil {
474- return nil , nil , nil , err
472+ return nil , nil , err
475473 }
476474 content , err := fsFile .Read ()
477475 fh := & closedFile {fsFile }
@@ -484,14 +482,32 @@ func (s *Session) DidModifyFiles(ctx context.Context, changes []source.FileModif
484482 }
485483 }
486484
487- snapshots := map [source.View ]source.Snapshot {}
488485 var releases []func ()
486+ viewToSnapshot := map [* View ]* snapshot {}
489487 for view , changed := range views {
490488 snapshot , release := view .invalidateContent (ctx , changed , forceReloadMetadata )
491- snapshots [view ] = snapshot
492489 releases = append (releases , release )
490+ viewToSnapshot [view ] = snapshot
491+ }
492+
493+ // We only want to diagnose each changed file once, in the view to which
494+ // it "most" belongs. We do this by picking the best view for each URI,
495+ // and then aggregating the set of snapshots and their URIs (to avoid
496+ // diagnosing the same snapshot multiple times).
497+ snapshotURIs := map [source.Snapshot ][]span.URI {}
498+ for _ , mod := range changes {
499+ viewSlice , ok := affectedViews [mod .URI ]
500+ if ! ok || len (viewSlice ) == 0 {
501+ continue
502+ }
503+ view := bestViewForURI (mod .URI , viewSlice )
504+ snapshot , ok := viewToSnapshot [view ]
505+ if ! ok {
506+ panic (fmt .Sprintf ("no snapshot for view %s" , view .Folder ()))
507+ }
508+ snapshotURIs [snapshot ] = append (snapshotURIs [snapshot ], mod .URI )
493509 }
494- return bestViews , snapshots , releases , nil
510+ return snapshotURIs , releases , nil
495511}
496512
497513func (s * Session ) ExpandModificationsToDirectories (ctx context.Context , changes []source.FileModification ) []source.FileModification {
0 commit comments