2
2
// Licensed under the MIT license. See License.txt in the project root for license information.
3
3
4
4
using System ;
5
- using System . Collections . Immutable ;
6
5
using System . Composition ;
7
6
using System . IO ;
7
+ using Microsoft . AspNetCore . Razor . Telemetry ;
8
+ using Microsoft . AspNetCore . Razor . Utilities ;
8
9
using Microsoft . CodeAnalysis . Razor . Workspaces ;
9
10
10
11
namespace Microsoft . CodeAnalysis . Razor . ProjectSystem ;
@@ -16,55 +17,58 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem;
16
17
/// </summary>
17
18
[ Shared ]
18
19
[ Export ( typeof ( FallbackProjectManager ) ) ]
19
- internal sealed class FallbackProjectManager
20
+ [ method: ImportingConstructor ]
21
+ internal sealed class FallbackProjectManager (
22
+ ProjectConfigurationFilePathStore projectConfigurationFilePathStore ,
23
+ LanguageServerFeatureOptions languageServerFeatureOptions ,
24
+ ProjectSnapshotManagerAccessor projectSnapshotManagerAccessor ,
25
+ ITelemetryReporter telemetryReporter )
20
26
{
21
- private readonly ProjectConfigurationFilePathStore _projectConfigurationFilePathStore ;
22
- private readonly LanguageServerFeatureOptions _languageServerFeatureOptions ;
23
- private readonly ProjectSnapshotManagerAccessor _projectSnapshotManagerAccessor ;
24
-
25
- private ImmutableHashSet < ProjectId > _fallbackProjectIds = ImmutableHashSet < ProjectId > . Empty ;
26
-
27
- [ ImportingConstructor ]
28
- public FallbackProjectManager (
29
- ProjectConfigurationFilePathStore projectConfigurationFilePathStore ,
30
- LanguageServerFeatureOptions languageServerFeatureOptions ,
31
- ProjectSnapshotManagerAccessor projectSnapshotManagerAccessor )
32
- {
33
- _projectConfigurationFilePathStore = projectConfigurationFilePathStore ;
34
- _languageServerFeatureOptions = languageServerFeatureOptions ;
35
- _projectSnapshotManagerAccessor = projectSnapshotManagerAccessor ;
36
- }
27
+ private readonly ProjectConfigurationFilePathStore _projectConfigurationFilePathStore = projectConfigurationFilePathStore ;
28
+ private readonly LanguageServerFeatureOptions _languageServerFeatureOptions = languageServerFeatureOptions ;
29
+ private readonly ProjectSnapshotManagerAccessor _projectSnapshotManagerAccessor = projectSnapshotManagerAccessor ;
30
+ private readonly ITelemetryReporter _telemetryReporter = telemetryReporter ;
37
31
38
32
internal void DynamicFileAdded ( ProjectId projectId , ProjectKey razorProjectKey , string projectFilePath , string filePath )
39
33
{
40
- var project = _projectSnapshotManagerAccessor . Instance . GetLoadedProject ( razorProjectKey ) ;
41
- if ( _fallbackProjectIds . Contains ( projectId ) )
34
+ try
42
35
{
43
- // The project might have started as a fallback project, but it might have been upgraded by our getting CPS info
44
- // about it. In that case, leave the CPS bits to do the work
36
+ var project = _projectSnapshotManagerAccessor . Instance . GetLoadedProject ( razorProjectKey ) ;
45
37
if ( project is ProjectSnapshot { HostProject : FallbackHostProject } )
46
38
{
47
39
// If this is a fallback project, then Roslyn may not track documents in the project, so these dynamic file notifications
48
40
// are the only way to know about files in the project.
49
41
AddFallbackDocument ( razorProjectKey , filePath , projectFilePath ) ;
50
42
}
43
+ else if ( project is null )
44
+ {
45
+ // We have been asked to provide dynamic file info, which means there is a .razor or .cshtml file in the project
46
+ // but for some reason our project system doesn't know about the project. In these cases (often when people don't
47
+ // use the Razor or Web SDK) we spin up a fallback experience for them
48
+ AddFallbackProject ( projectId , filePath ) ;
49
+ }
51
50
}
52
- else if ( project is null )
51
+ catch ( Exception ex )
53
52
{
54
- // We have been asked to provide dynamic file info, which means there is a .razor or .cshtml file in the project
55
- // but for some reason our project system doesn't know about the project. In these cases (often when people don't
56
- // use the Razor or Web SDK) we spin up a fallback experience for them
57
- AddFallbackProject ( projectId , filePath ) ;
53
+ _telemetryReporter . ReportFault ( ex , "Error while trying to add fallback document to project" ) ;
58
54
}
59
55
}
60
56
61
- internal void DynamicFileRemoved ( ProjectId projectId , string projectFilePath , string filePath )
57
+ internal void DynamicFileRemoved ( ProjectId projectId , ProjectKey razorProjectKey , string projectFilePath , string filePath )
62
58
{
63
- if ( _fallbackProjectIds . Contains ( projectId ) )
59
+ try
60
+ {
61
+ var project = _projectSnapshotManagerAccessor . Instance . GetLoadedProject ( razorProjectKey ) ;
62
+ if ( project is ProjectSnapshot { HostProject : FallbackHostProject } )
63
+ {
64
+ // If this is a fallback project, then Roslyn may not track documents in the project, so these dynamic file notifications
65
+ // are the only way to know about files in the project.
66
+ RemoveFallbackDocument ( projectId , filePath , projectFilePath ) ;
67
+ }
68
+ }
69
+ catch ( Exception ex )
64
70
{
65
- // If this is a fallback project, then Roslyn may not track documents in the project, so these dynamic file notifications
66
- // are the only way to know about files in the project.
67
- RemoveFallbackDocument ( projectId , filePath , projectFilePath ) ;
71
+ _telemetryReporter . ReportFault ( ex , "Error while trying to remove fallback document from project" ) ;
68
72
}
69
73
}
70
74
@@ -82,11 +86,6 @@ private void AddFallbackProject(ProjectId projectId, string filePath)
82
86
return ;
83
87
}
84
88
85
- if ( ! ImmutableInterlocked . Update ( ref _fallbackProjectIds , ( set , id ) => set . Add ( id ) , project . Id ) )
86
- {
87
- return ;
88
- }
89
-
90
89
var rootNamespace = project . DefaultNamespace ;
91
90
92
91
// We create this as a fallback project so that other parts of the system can reason about them - eg we don't do code
@@ -107,15 +106,27 @@ private void AddFallbackProject(ProjectId projectId, string filePath)
107
106
private void AddFallbackDocument ( ProjectKey projectKey , string filePath , string projectFilePath )
108
107
{
109
108
var hostDocument = CreateHostDocument ( filePath , projectFilePath ) ;
109
+ if ( hostDocument is null )
110
+ {
111
+ return ;
112
+ }
113
+
110
114
var textLoader = new FileTextLoader ( filePath , defaultEncoding : null ) ;
111
115
_projectSnapshotManagerAccessor . Instance . DocumentAdded ( projectKey , hostDocument , textLoader ) ;
112
116
}
113
117
114
- private static HostDocument CreateHostDocument ( string filePath , string projectFilePath )
118
+ private static HostDocument ? CreateHostDocument ( string filePath , string projectFilePath )
115
119
{
116
- var targetPath = filePath . StartsWith ( projectFilePath , FilePathComparison . Instance )
117
- ? filePath [ projectFilePath . Length ..]
118
- : filePath ;
120
+ // The compiler only supports paths that are relative to the project root, so filter our files
121
+ // that don't match
122
+ var projectPath = FilePathNormalizer . GetNormalizedDirectoryName ( projectFilePath ) ;
123
+ var normalizedFilePath = FilePathNormalizer . Normalize ( filePath ) ;
124
+ if ( ! normalizedFilePath . StartsWith ( projectPath , FilePathComparison . Instance ) )
125
+ {
126
+ return null ;
127
+ }
128
+
129
+ var targetPath = filePath [ projectPath . Length ..] ;
119
130
var hostDocument = new HostDocument ( filePath , targetPath ) ;
120
131
return hostDocument ;
121
132
}
@@ -135,6 +146,11 @@ private void RemoveFallbackDocument(ProjectId projectId, string filePath, string
135
146
}
136
147
137
148
var hostDocument = CreateHostDocument ( filePath , projectFilePath ) ;
149
+ if ( hostDocument is null )
150
+ {
151
+ return ;
152
+ }
153
+
138
154
_projectSnapshotManagerAccessor . Instance . DocumentRemoved ( razorProjectKey , hostDocument ) ;
139
155
}
140
156
@@ -154,21 +170,4 @@ private void RemoveFallbackDocument(ProjectId projectId, string filePath, string
154
170
155
171
return project ;
156
172
}
157
-
158
- internal TestAccessor GetTestAccessor ( )
159
- {
160
- return new TestAccessor ( this ) ;
161
- }
162
-
163
- internal readonly struct TestAccessor
164
- {
165
- private readonly FallbackProjectManager _instance ;
166
-
167
- internal TestAccessor ( FallbackProjectManager instance )
168
- {
169
- _instance = instance ;
170
- }
171
-
172
- internal ImmutableHashSet < ProjectId > ProjectIds => _instance . _fallbackProjectIds ;
173
- }
174
173
}
0 commit comments