@@ -2,6 +2,7 @@ package project
2
2
3
3
import (
4
4
"slices"
5
+ "sync"
5
6
6
7
"github.com/microsoft/typescript-go/internal/core"
7
8
"github.com/microsoft/typescript-go/internal/ls"
@@ -25,7 +26,8 @@ type ScriptInfo struct {
25
26
matchesDiskText bool
26
27
deferredDelete bool
27
28
28
- containingProjects []* Project
29
+ containingProjectsMu sync.RWMutex
30
+ containingProjects []* Project
29
31
30
32
fs vfs.FS
31
33
}
@@ -68,6 +70,12 @@ func (s *ScriptInfo) Version() int {
68
70
return s .version
69
71
}
70
72
73
+ func (s * ScriptInfo ) ContainingProjects () []* Project {
74
+ s .containingProjectsMu .RLock ()
75
+ defer s .containingProjectsMu .RUnlock ()
76
+ return slices .Clone (s .containingProjects )
77
+ }
78
+
71
79
func (s * ScriptInfo ) reloadIfNeeded () {
72
80
if s .pendingReloadFromDisk {
73
81
if newText , ok := s .fs .ReadFile (s .fileName ); ok {
@@ -97,6 +105,15 @@ func (s *ScriptInfo) close(fileExists bool) {
97
105
s .pendingReloadFromDisk = true
98
106
s .markContainingProjectsAsDirty ()
99
107
}
108
+
109
+ s .containingProjectsMu .Lock ()
110
+ defer s .containingProjectsMu .Unlock ()
111
+ for _ , project := range slices .Clone (s .containingProjects ) {
112
+ if project .kind == KindInferred && project .isRoot (s ) {
113
+ project .RemoveFile (s , fileExists )
114
+ s .detachFromProjectLocked (project )
115
+ }
116
+ }
100
117
}
101
118
102
119
func (s * ScriptInfo ) setText (newText string ) {
@@ -106,6 +123,8 @@ func (s *ScriptInfo) setText(newText string) {
106
123
}
107
124
108
125
func (s * ScriptInfo ) markContainingProjectsAsDirty () {
126
+ s .containingProjectsMu .RLock ()
127
+ defer s .containingProjectsMu .RUnlock ()
109
128
for _ , project := range s .containingProjects {
110
129
project .MarkFileAsDirty (s .path )
111
130
}
@@ -115,7 +134,9 @@ func (s *ScriptInfo) markContainingProjectsAsDirty() {
115
134
// and returns true if the script info was newly attached.
116
135
func (s * ScriptInfo ) attachToProject (project * Project ) bool {
117
136
if ! s .isAttached (project ) {
137
+ s .containingProjectsMu .Lock ()
118
138
s .containingProjects = append (s .containingProjects , project )
139
+ s .containingProjectsMu .Unlock ()
119
140
if project .compilerOptions .PreserveSymlinks != core .TSTrue {
120
141
s .ensureRealpath (project .FS ())
121
142
}
@@ -126,13 +147,17 @@ func (s *ScriptInfo) attachToProject(project *Project) bool {
126
147
}
127
148
128
149
func (s * ScriptInfo ) isAttached (project * Project ) bool {
150
+ s .containingProjectsMu .RLock ()
151
+ defer s .containingProjectsMu .RUnlock ()
129
152
return slices .Contains (s .containingProjects , project )
130
153
}
131
154
132
155
func (s * ScriptInfo ) isOrphan () bool {
133
156
if s .deferredDelete {
134
157
return true
135
158
}
159
+ s .containingProjectsMu .RLock ()
160
+ defer s .containingProjectsMu .RUnlock ()
136
161
for _ , project := range s .containingProjects {
137
162
if ! project .isOrphan () {
138
163
return false
@@ -148,6 +173,8 @@ func (s *ScriptInfo) editContent(change ls.TextChange) {
148
173
149
174
func (s * ScriptInfo ) ensureRealpath (fs vfs.FS ) {
150
175
if s .realpath == "" {
176
+ s .containingProjectsMu .RLock ()
177
+ defer s .containingProjectsMu .RUnlock ()
151
178
if len (s .containingProjects ) == 0 {
152
179
panic ("scriptInfo must be attached to a project before calling ensureRealpath" )
153
180
}
@@ -168,17 +195,25 @@ func (s *ScriptInfo) getRealpathIfDifferent() (tspath.Path, bool) {
168
195
}
169
196
170
197
func (s * ScriptInfo ) detachAllProjects () {
198
+ s .containingProjectsMu .Lock ()
199
+ defer s .containingProjectsMu .Unlock ()
171
200
for _ , project := range s .containingProjects {
172
201
// !!!
173
202
// if (isConfiguredProject(p)) {
174
203
// p.getCachedDirectoryStructureHost().addOrDeleteFile(this.fileName, this.path, FileWatcherEventKind.Deleted);
175
204
// }
176
- project .RemoveFile (s , false /*fileExists*/ , false /*detachFromProject*/ )
205
+ project .RemoveFile (s , false /*fileExists*/ )
177
206
}
178
207
s .containingProjects = nil
179
208
}
180
209
181
210
func (s * ScriptInfo ) detachFromProject (project * Project ) {
211
+ s .containingProjectsMu .Lock ()
212
+ defer s .containingProjectsMu .Unlock ()
213
+ s .detachFromProjectLocked (project )
214
+ }
215
+
216
+ func (s * ScriptInfo ) detachFromProjectLocked (project * Project ) {
182
217
if index := slices .Index (s .containingProjects , project ); index != - 1 {
183
218
s .containingProjects = slices .Delete (s .containingProjects , index , index + 1 )
184
219
}
@@ -193,6 +228,8 @@ func (s *ScriptInfo) delayReloadNonMixedContentFile() {
193
228
}
194
229
195
230
func (s * ScriptInfo ) containedByDeferredClosedProject () bool {
231
+ s .containingProjectsMu .RLock ()
232
+ defer s .containingProjectsMu .RUnlock ()
196
233
return slices .IndexFunc (s .containingProjects , func (project * Project ) bool {
197
234
return project .deferredClose
198
235
}) != - 1
0 commit comments