public void ProjectConfigurationFileChanged(ProjectConfigurationFileChangeEventArgs args) { if (args is null) { throw new ArgumentNullException(nameof(args)); } _projectSnapshotManagerDispatcher.AssertDispatcherThread(); switch (args.Kind) { case RazorFileChangeKind.Changed: { var configurationFilePath = _filePathNormalizer.Normalize(args.ConfigurationFilePath); if (!args.TryDeserialize(out var projectRazorJson)) { if (!_configurationToProjectMap.TryGetValue(configurationFilePath, out var associatedProjectFilePath)) { // Could not resolve an associated project file, noop. _logger.LogWarning("Failed to deserialize configuration file after change for an unknown project. Configuration file path: '{0}'", configurationFilePath); return; } else { _logger.LogWarning("Failed to deserialize configuration file after change for project '{0}': '{1}'", associatedProjectFilePath, configurationFilePath); } // We found the last associated project file for the configuration file. Reset the project since we can't // accurately determine its configurations. EnqueueUpdateProject(associatedProjectFilePath, projectRazorJson: null); return; } var projectFilePath = _filePathNormalizer.Normalize(projectRazorJson.FilePath); _logger.LogInformation("Project configuration file changed for project '{0}': '{1}'", projectFilePath, configurationFilePath); EnqueueUpdateProject(projectFilePath, projectRazorJson); break; } case RazorFileChangeKind.Added: { var configurationFilePath = _filePathNormalizer.Normalize(args.ConfigurationFilePath); if (!args.TryDeserialize(out var projectRazorJson)) { // Given that this is the first time we're seeing this configuration file if we can't deserialize it // then we have to noop. _logger.LogWarning("Failed to deserialize configuration file on configuration added event. Configuration file path: '{0}'", configurationFilePath); return; } var projectFilePath = _filePathNormalizer.Normalize(projectRazorJson.FilePath); _configurationToProjectMap[configurationFilePath] = projectFilePath; _projectService.AddProject(projectFilePath); _logger.LogInformation("Project configuration file added for project '{0}': '{1}'", projectFilePath, configurationFilePath); EnqueueUpdateProject(projectFilePath, projectRazorJson); break; } case RazorFileChangeKind.Removed: { var configurationFilePath = _filePathNormalizer.Normalize(args.ConfigurationFilePath); if (!_configurationToProjectMap.TryGetValue(configurationFilePath, out var projectFilePath)) { // Failed to deserialize the initial project configuration file on add so we can't remove the configuration file because it doesn't exist in the list. _logger.LogWarning("Failed to resolve associated project on configuration removed event. Configuration file path: '{0}'", configurationFilePath); return; } _configurationToProjectMap.Remove(configurationFilePath); _logger.LogInformation("Project configuration file removed for project '{0}': '{1}'", projectFilePath, configurationFilePath); EnqueueUpdateProject(projectFilePath, projectRazorJson: null); break; } } void UpdateProject(string projectFilePath, ProjectRazorJson?projectRazorJson) { if (projectFilePath is null) { throw new ArgumentNullException(nameof(projectFilePath)); } if (projectRazorJson is null) { ResetProject(projectFilePath); return; } var projectWorkspaceState = projectRazorJson.ProjectWorkspaceState ?? ProjectWorkspaceState.Default; var documents = projectRazorJson.Documents ?? Array.Empty <DocumentSnapshotHandle>(); _projectService.UpdateProject( projectRazorJson.FilePath, projectRazorJson.Configuration, projectRazorJson.RootNamespace, projectWorkspaceState, documents); } async Task UpdateAfterDelayAsync(string projectFilePath) { await Task.Delay(EnqueueDelay).ConfigureAwait(true); var delayedProjectInfo = ProjectInfoMap[projectFilePath]; UpdateProject(projectFilePath, delayedProjectInfo.ProjectRazorJson); } void EnqueueUpdateProject(string projectFilePath, ProjectRazorJson?projectRazorJson) { projectFilePath = _filePathNormalizer.Normalize(projectFilePath); if (!ProjectInfoMap.ContainsKey(projectFilePath)) { ProjectInfoMap[projectFilePath] = new DelayedProjectInfo(); } var delayedProjectInfo = ProjectInfoMap[projectFilePath]; delayedProjectInfo.ProjectRazorJson = projectRazorJson; if (delayedProjectInfo.ProjectUpdateTask is null || delayedProjectInfo.ProjectUpdateTask.IsCompleted) { delayedProjectInfo.ProjectUpdateTask = UpdateAfterDelayAsync(projectFilePath); } } void ResetProject(string projectFilePath) { _projectService.UpdateProject( projectFilePath, configuration: null, rootNamespace: null, ProjectWorkspaceState.Default, Array.Empty <DocumentSnapshotHandle>()); } }
public void ProjectConfigurationFileChanged(ProjectConfigurationFileChangeEventArgs args) { if (args is null) { throw new ArgumentNullException(nameof(args)); } _foregroundDispatcher.AssertForegroundThread(); switch (args.Kind) { case RazorFileChangeKind.Changed: { if (!args.TryDeserialize(out var handle)) { var configurationFilePath = _filePathNormalizer.Normalize(args.ConfigurationFilePath); if (!_configurationToProjectMap.TryGetValue(configurationFilePath, out var projectFilePath)) { // Could not resolve an associated project file, noop. return; } // We found the last associated project file for the configuration file. Reset the project since we can't // accurately determine its configurations. EnqueueUpdateProject(projectFilePath, snapshotHandle: null); return; } EnqueueUpdateProject(handle.FilePath, handle); break; } case RazorFileChangeKind.Added: { if (!args.TryDeserialize(out var handle)) { // Given that this is the first time we're seeing this configuration file if we can't deserialize it // then we have to noop. return; } var projectFilePath = _filePathNormalizer.Normalize(handle.FilePath); var configurationFilePath = _filePathNormalizer.Normalize(args.ConfigurationFilePath); _configurationToProjectMap[configurationFilePath] = projectFilePath; _projectService.AddProject(projectFilePath); EnqueueUpdateProject(projectFilePath, handle); break; } case RazorFileChangeKind.Removed: { var configurationFilePath = _filePathNormalizer.Normalize(args.ConfigurationFilePath); if (!_configurationToProjectMap.TryGetValue(configurationFilePath, out var projectFilePath)) { // Failed to deserialize the initial handle on add so we can't remove the configuration file because it doesn't exist in the list. return; } _configurationToProjectMap.Remove(configurationFilePath); EnqueueUpdateProject(projectFilePath, snapshotHandle: null); break; } } void UpdateProject(string projectFilePath, FullProjectSnapshotHandle handle) { if (projectFilePath is null) { throw new ArgumentNullException(nameof(projectFilePath)); } if (handle is null) { ResetProject(projectFilePath); return; } var projectWorkspaceState = handle.ProjectWorkspaceState ?? ProjectWorkspaceState.Default; var documents = handle.Documents ?? Array.Empty <DocumentSnapshotHandle>(); _projectService.UpdateProject( handle.FilePath, handle.Configuration, handle.RootNamespace, projectWorkspaceState, documents); } async Task UpdateAfterDelayAsync(string projectFilePath) { await Task.Delay(EnqueueDelay).ConfigureAwait(true); var delayedProjectInfo = _projectInfoMap[projectFilePath]; UpdateProject(projectFilePath, delayedProjectInfo.FullProjectSnapshotHandle); } void EnqueueUpdateProject(string projectFilePath, FullProjectSnapshotHandle snapshotHandle) { if (!_projectInfoMap.ContainsKey(projectFilePath)) { _projectInfoMap[projectFilePath] = new DelayedProjectInfo(); } var delayedProjectInfo = _projectInfoMap[projectFilePath]; delayedProjectInfo.FullProjectSnapshotHandle = snapshotHandle; if (delayedProjectInfo.ProjectUpdateTask is null || delayedProjectInfo.ProjectUpdateTask.IsCompleted) { delayedProjectInfo.ProjectUpdateTask = UpdateAfterDelayAsync(projectFilePath); } } void ResetProject(string projectFilePath) { _projectService.UpdateProject( projectFilePath, configuration: null, rootNamespace: null, ProjectWorkspaceState.Default, Array.Empty <DocumentSnapshotHandle>()); } }