AbstractProject_Analyzers.cs 8.4 KB
Newer Older
1 2 3
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

using System;
4
using System.Collections.Generic;
5
using System.Collections.Immutable;
6
using System.IO;
7
using System.Linq;
8
using System.Threading;
9
using Microsoft.CodeAnalysis;
10
using Microsoft.CodeAnalysis.Host;
11
using Microsoft.VisualStudio.ComponentModelHost;
12
using Microsoft.VisualStudio.Shell.Interop;
13
using Roslyn.Utilities;
14 15 16

namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
{
17
    internal partial class AbstractProject
18
    {
19
        private AnalyzerFileWatcherService _analyzerFileWatcherService = null;
20
        private AnalyzerDependencyCheckingService _dependencyCheckingService = null;
21

22
        public void AddAnalyzerReference(string analyzerAssemblyFullPath)
23
        {
M
Matt Warren 已提交
24 25
            AssertIsForeground();

26
            if (CurrentProjectAnalyzersContains(analyzerAssemblyFullPath))
27 28 29 30 31
            {
                return;
            }

            var fileChangeService = (IVsFileChangeEx)this.ServiceProvider.GetService(typeof(SVsFileChangeEx));
32 33 34 35
            if (Workspace == null)
            {
                // This can happen only in tests.
                var testAnalyzer = new VisualStudioAnalyzer(analyzerAssemblyFullPath, fileChangeService, this.HostDiagnosticUpdateSource, this.Id, this.Workspace, loader: null, language: this.Language);
36
                this.AddOrUpdateAnalyzer(analyzerAssemblyFullPath, testAnalyzer);
37 38 39 40 41
                return;
            }

            var analyzerLoader = Workspace.Services.GetRequiredService<IAnalyzerService>().GetLoader();
            analyzerLoader.AddDependencyLocation(analyzerAssemblyFullPath);
42
            var analyzer = new VisualStudioAnalyzer(analyzerAssemblyFullPath, fileChangeService, this.HostDiagnosticUpdateSource, this.Id, this.Workspace, analyzerLoader, this.Language);
43
            this.AddOrUpdateAnalyzer(analyzerAssemblyFullPath, analyzer);
44 45 46 47

            if (_pushingChangesToWorkspaceHosts)
            {
                var analyzerReference = analyzer.GetReference();
48
                this.ProjectTracker.NotifyWorkspaceHosts(host => host.OnAnalyzerReferenceAdded(Id, analyzerReference));
49

50
                List<VisualStudioAnalyzer> existingReferencesWithLoadErrors = GetCurrentAnalyzers().Where(a => a.HasLoadErrors).ToList();
51 52 53

                foreach (var existingReference in existingReferencesWithLoadErrors)
                {
54
                    this.ProjectTracker.NotifyWorkspaceHosts(host => host.OnAnalyzerReferenceRemoved(Id, existingReference.GetReference()));
55
                    existingReference.Reset();
56
                    this.ProjectTracker.NotifyWorkspaceHosts(host => host.OnAnalyzerReferenceAdded(Id, existingReference.GetReference()));
57 58
                }

59
                GetAnalyzerDependencyCheckingService().CheckForConflictsAsync();
60
            }
61

62 63 64
            if (File.Exists(analyzerAssemblyFullPath))
            {
                GetAnalyzerFileWatcherService().AddPath(analyzerAssemblyFullPath);
65
                GetAnalyzerFileWatcherService().ErrorIfAnalyzerAlreadyLoaded(Id, analyzerAssemblyFullPath);
66 67 68 69 70
            }
            else
            {
                analyzer.UpdatedOnDisk += OnAnalyzerChanged;
            }
71 72
        }

73
        public void RemoveAnalyzerReference(string analyzerAssemblyFullPath)
74
        {
M
Matt Warren 已提交
75 76
            AssertIsForeground();

C
CyrusNajmabadi 已提交
77
            if (!TryGetAnalyzer(analyzerAssemblyFullPath, out var analyzer))
78 79 80 81
            {
                return;
            }

82 83 84
            if (Workspace == null)
            {
                // This can happen only in tests.
85
                RemoveAnalyzer(analyzerAssemblyFullPath);
86 87 88 89 90
                analyzer.Dispose();
                return;
            }

            GetAnalyzerFileWatcherService().RemoveAnalyzerAlreadyLoadedDiagnostics(Id, analyzerAssemblyFullPath);
91

92
            RemoveAnalyzer(analyzerAssemblyFullPath);
93 94 95 96

            if (_pushingChangesToWorkspaceHosts)
            {
                var analyzerReference = analyzer.GetReference();
97
                this.ProjectTracker.NotifyWorkspaceHosts(host => host.OnAnalyzerReferenceRemoved(Id, analyzerReference));
98 99

                GetAnalyzerDependencyCheckingService().CheckForConflictsAsync();
100 101 102 103 104 105 106
            }

            analyzer.Dispose();
        }

        public void SetRuleSetFile(string ruleSetFileFullPath)
        {
M
Matt Warren 已提交
107 108
            AssertIsForeground();

109 110 111 112 113 114 115 116 117 118 119 120
            if (ruleSetFileFullPath == null)
            {
                ruleSetFileFullPath = string.Empty;
            }

            if (!ruleSetFileFullPath.Equals(string.Empty))
            {
                // This is already a full path, but run it through GetFullPath to clean it (e.g., remove
                // extra backslashes).
                ruleSetFileFullPath = Path.GetFullPath(ruleSetFileFullPath);
            }

121 122
            if (this.RuleSetFile != null &&
                this.RuleSetFile.FilePath.Equals(ruleSetFileFullPath, StringComparison.OrdinalIgnoreCase))
123 124 125 126 127 128 129
            {
                return;
            }

            ResetAnalyzerRuleSet(ruleSetFileFullPath);
        }

130
        public void AddAdditionalFile(string additionalFilePath, Func<IVisualStudioHostDocument, bool> getIsInCurrentContext)
131
        {
M
Matt Warren 已提交
132 133
            AssertIsForeground();

134 135 136 137
            var document = this.DocumentProvider.TryGetDocumentForFile(
                this,
                filePath: additionalFilePath,
                sourceCodeKind: SourceCodeKind.Regular,
138
                getFolderNames: _ => SpecializedCollections.EmptyReadOnlyList<string>(),
139 140 141 142
                canUseTextBuffer: _ => true,
                updatedOnDiskHandler: s_additionalDocumentUpdatedOnDiskEventHandler,
                openedHandler: s_additionalDocumentOpenedEventHandler,
                closingHandler: s_additionalDocumentClosingEventHandler);
143 144 145 146 147 148

            if (document == null)
            {
                return;
            }

149
            AddAdditionalDocument(document, isCurrentContext: getIsInCurrentContext(document));
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
        }

        public void RemoveAdditionalFile(string additionalFilePath)
        {
            IVisualStudioHostDocument document = this.GetCurrentDocumentFromPath(additionalFilePath);
            if (document == null)
            {
                throw new InvalidOperationException("The document is not a part of the finalProject.");
            }

            RemoveAdditionalDocument(document);
        }

        private void ResetAnalyzerRuleSet(string ruleSetFileFullPath)
        {
            ClearAnalyzerRuleSet();
            SetAnalyzerRuleSet(ruleSetFileFullPath);
167
            ResetArgumentsAndUpdateOptions();
168 169 170 171 172 173
        }

        private void SetAnalyzerRuleSet(string ruleSetFileFullPath)
        {
            if (ruleSetFileFullPath.Length != 0)
            {
174 175
                this.RuleSetFile = this.ProjectTracker.RuleSetFileProvider.GetOrCreateRuleSet(ruleSetFileFullPath);
                this.RuleSetFile.UpdatedOnDisk += OnRuleSetFileUpdateOnDisk;
176 177 178 179 180
            }
        }

        private void ClearAnalyzerRuleSet()
        {
181
            if (this.RuleSetFile != null)
182
            {
183 184
                this.RuleSetFile.UpdatedOnDisk -= OnRuleSetFileUpdateOnDisk;
                this.RuleSetFile = null;
185 186 187
            }
        }

188 189
        // internal for testing purpose.
        internal void OnRuleSetFileUpdateOnDisk(object sender, EventArgs e)
190
        {
M
Matt Warren 已提交
191 192
            AssertIsForeground();

193
            var filePath = this.RuleSetFile.FilePath;
194 195 196

            ResetAnalyzerRuleSet(filePath);
        }
197 198 199 200 201 202

        private AnalyzerFileWatcherService GetAnalyzerFileWatcherService()
        {
            if (_analyzerFileWatcherService == null)
            {
                var componentModel = (IComponentModel)this.ServiceProvider.GetService(typeof(SComponentModel));
203
                Interlocked.CompareExchange(ref _analyzerFileWatcherService, componentModel.GetService<AnalyzerFileWatcherService>(), null);
204 205 206 207
            }

            return _analyzerFileWatcherService;
        }
208 209 210 211 212 213

        private AnalyzerDependencyCheckingService GetAnalyzerDependencyCheckingService()
        {
            if (_dependencyCheckingService == null)
            {
                var componentModel = (IComponentModel)this.ServiceProvider.GetService(typeof(SComponentModel));
214
                Interlocked.CompareExchange(ref _dependencyCheckingService, componentModel.GetService<AnalyzerDependencyCheckingService>(), null);
215 216 217 218
            }

            return _dependencyCheckingService;
        }
219
    }
220
}