提交 6f017b72 编写于 作者: H Heejae Chang 提交者: GitHub

Merge pull request #14278 from heejaechang/dynamicincremental

added a way to dynamically add new incremental analyzer to solution c…
......@@ -108,6 +108,12 @@ public async void Unregister(Workspace workspace, bool blockingShutdown = false)
// ask it to reset its stages for the given workspace
_owner._analyzerService.ShutdownAnalyzerFrom(_workspace);
}
public void AddAnalyzerProvider(IIncrementalAnalyzerProvider provider, IncrementalAnalyzerProviderMetadata metadata)
{
// preview solution crawler doesn't support adding and removing analyzer dynamically
throw new NotSupportedException();
}
}
}
}
......@@ -42,6 +42,37 @@ public async Task RegisterService()
}
}
[Fact]
public async Task DynamicallyAddAnalyzer()
{
using (var workspace = new WorkCoordinatorWorkspace(SolutionCrawler))
{
// create solution and wait for it to settle
var solution = GetInitialSolutionInfo(workspace);
workspace.OnSolutionAdded(solution);
await WaitWaiterAsync(workspace.ExportProvider);
// create solution crawler and add new analyzer provider dynamically
var service = new SolutionCrawlerRegistrationService(
SpecializedCollections.EmptyEnumerable<Lazy<IIncrementalAnalyzerProvider, IncrementalAnalyzerProviderMetadata>>(),
GetListeners(workspace.ExportProvider));
service.Register(workspace);
var provider = new AnalyzerProvider(new Analyzer());
service.AddAnalyzerProvider(provider, Metadata.Crawler);
// wait for everything to settle
await WaitAsync(service, workspace);
service.Unregister(workspace);
// check whether everything ran as expected
Assert.Equal(10, provider.Analyzer.SyntaxDocumentIds.Count);
Assert.Equal(10, provider.Analyzer.DocumentIds.Count);
}
}
[Fact, WorkItem(747226, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/747226")]
public async Task SolutionAdded_Simple()
{
......@@ -997,23 +1028,23 @@ protected override void Dispose(bool finalize)
private class AnalyzerProvider : IIncrementalAnalyzerProvider
{
private readonly Analyzer _analyzer;
public readonly Analyzer Analyzer;
public AnalyzerProvider(Analyzer analyzer)
{
_analyzer = analyzer;
Analyzer = analyzer;
}
public IIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace)
{
return _analyzer;
return Analyzer;
}
}
internal class Metadata : IncrementalAnalyzerProviderMetadata
{
public Metadata(params string[] workspaceKinds)
: base(new Dictionary<string, object> { { "WorkspaceKinds", workspaceKinds }, { "HighPriorityForActiveFile", false } })
: base(new Dictionary<string, object> { { "WorkspaceKinds", workspaceKinds }, { "HighPriorityForActiveFile", false }, { "Name", "TestAnalyzer" } })
{
}
......
......@@ -87,18 +87,20 @@ public static void LogOptionChanged(int correlationId, bool value)
}));
}
public static void LogActiveFileAnalyzers(int correlationId, Workspace workspace, ImmutableArray<IIncrementalAnalyzer> reordered)
public static void LogAnalyzers(int correlationId, Workspace workspace, ImmutableArray<IIncrementalAnalyzer> reordered, bool onlyHighPriorityAnalyzer)
{
LogAnalyzersWorker(
FunctionId.IncrementalAnalyzerProcessor_ActiveFileAnalyzers, FunctionId.IncrementalAnalyzerProcessor_ActiveFileAnalyzer,
correlationId, workspace, reordered);
}
public static void LogAnalyzers(int correlationId, Workspace workspace, ImmutableArray<IIncrementalAnalyzer> reordered)
{
LogAnalyzersWorker(
FunctionId.IncrementalAnalyzerProcessor_Analyzers, FunctionId.IncrementalAnalyzerProcessor_Analyzer,
correlationId, workspace, reordered);
if (onlyHighPriorityAnalyzer)
{
LogAnalyzersWorker(
FunctionId.IncrementalAnalyzerProcessor_ActiveFileAnalyzers, FunctionId.IncrementalAnalyzerProcessor_ActiveFileAnalyzer,
correlationId, workspace, reordered);
}
else
{
LogAnalyzersWorker(
FunctionId.IncrementalAnalyzerProcessor_Analyzers, FunctionId.IncrementalAnalyzerProcessor_Analyzer,
correlationId, workspace, reordered);
}
}
private static void LogAnalyzersWorker(
......
......@@ -23,9 +23,10 @@ internal partial class SolutionCrawlerRegistrationService : ISolutionCrawlerRegi
private readonly SolutionCrawlerProgressReporter _progressReporter;
private readonly IAsynchronousOperationListener _listener;
private readonly ImmutableDictionary<string, ImmutableArray<Lazy<IIncrementalAnalyzerProvider, IncrementalAnalyzerProviderMetadata>>> _analyzerProviders;
private readonly Dictionary<Workspace, WorkCoordinator> _documentWorkCoordinatorMap;
private ImmutableDictionary<string, ImmutableArray<Lazy<IIncrementalAnalyzerProvider, IncrementalAnalyzerProviderMetadata>>> _analyzerProviders;
[ImportingConstructor]
public SolutionCrawlerRegistrationService(
[ImportMany] IEnumerable<Lazy<IIncrementalAnalyzerProvider, IncrementalAnalyzerProviderMetadata>> analyzerProviders,
......@@ -84,6 +85,39 @@ public void Unregister(Workspace workspace, bool blockingShutdown = false)
SolutionCrawlerLogger.LogUnregistration(coordinator.CorrelationId);
}
public void AddAnalyzerProvider(IIncrementalAnalyzerProvider provider, IncrementalAnalyzerProviderMetadata metadata)
{
// now update all existing work coordinator
lock (_gate)
{
var lazyProvider = new Lazy<IIncrementalAnalyzerProvider, IncrementalAnalyzerProviderMetadata>(() => provider, metadata);
// update existing map for future solution crawler registration - no need for interlock but this makes add or update easier
ImmutableInterlocked.AddOrUpdate(ref _analyzerProviders, metadata.Name, (n) => ImmutableArray.Create(lazyProvider), (n, v) => v.Add(lazyProvider));
// assert map integrity
AssertAnalyzerProviders(_analyzerProviders);
// find existing coordinator to update
var lazyProviders = _analyzerProviders[metadata.Name];
foreach (var kv in _documentWorkCoordinatorMap)
{
var workspace = kv.Key;
var coordinator = kv.Value;
Lazy<IIncrementalAnalyzerProvider, IncrementalAnalyzerProviderMetadata> picked;
if (!TryGetProvider(workspace.Kind, lazyProviders, out picked) || picked != lazyProvider)
{
// check whether new provider belong to current workspace
continue;
}
var analyzer = lazyProvider.Value.CreateIncrementalAnalyzer(workspace);
coordinator.AddAnalyzer(analyzer, metadata.HighPriorityForActiveFile);
}
}
}
public void Reanalyze(Workspace workspace, IIncrementalAnalyzer analyzer, IEnumerable<ProjectId> projectIds, IEnumerable<DocumentId> documentIds, bool highPriority)
{
lock (_gate)
......
// 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;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Internal.Log;
......@@ -18,14 +19,22 @@ private sealed partial class IncrementalAnalyzerProcessor
private abstract class AbstractPriorityProcessor : GlobalOperationAwareIdleProcessor
{
protected readonly IncrementalAnalyzerProcessor Processor;
private readonly object _gate;
private Lazy<ImmutableArray<IIncrementalAnalyzer>> _lazyAnalyzers;
public AbstractPriorityProcessor(
IAsynchronousOperationListener listener,
IncrementalAnalyzerProcessor processor,
Lazy<ImmutableArray<IIncrementalAnalyzer>> lazyAnalyzers,
IGlobalOperationNotificationService globalOperationNotificationService,
int backOffTimeSpanInMs,
CancellationToken shutdownToken) :
base(listener, globalOperationNotificationService, backOffTimeSpanInMs, shutdownToken)
{
_gate = new object();
_lazyAnalyzers = lazyAnalyzers;
this.Processor = processor;
if (this.Processor._documentTracker != null)
......@@ -34,22 +43,24 @@ private abstract class AbstractPriorityProcessor : GlobalOperationAwareIdleProce
}
}
private void OnNonRoslynBufferTextChanged(object sender, EventArgs e)
public ImmutableArray<IIncrementalAnalyzer> Analyzers
{
// There are 2 things incremental processor takes care of
//
// #1 is making sure we delay processing any work until there is enough idle (ex, typing) in host.
// #2 is managing cancellation and pending works.
//
// we used to do #1 and #2 only for Roslyn files. and that is usually fine since most of time solution contains only roslyn files.
//
// but for mixed solution (ex, Roslyn files + HTML + JS + CSS), #2 still makes sense but #1 doesn't. We want
// to pause any work while something is going on in other project types as well.
//
// we need to make sure we play nice with neighbors as well.
//
// now, we don't care where changes are coming from. if there is any change in host, we pause ourselves for a while.
this.UpdateLastAccessTime();
get
{
lock (_gate)
{
return _lazyAnalyzers.Value;
}
}
}
public void AddAnalyzer(IIncrementalAnalyzer analyzer)
{
lock (_gate)
{
var analyzers = _lazyAnalyzers.Value;
_lazyAnalyzers = new Lazy<ImmutableArray<IIncrementalAnalyzer>>(() => analyzers.Add(analyzer));
}
}
protected override void PauseOnGlobalOperation()
......@@ -101,6 +112,24 @@ public override void Shutdown()
this.Processor._documentTracker.NonRoslynBufferTextChanged -= OnNonRoslynBufferTextChanged;
}
}
private void OnNonRoslynBufferTextChanged(object sender, EventArgs e)
{
// There are 2 things incremental processor takes care of
//
// #1 is making sure we delay processing any work until there is enough idle (ex, typing) in host.
// #2 is managing cancellation and pending works.
//
// we used to do #1 and #2 only for Roslyn files. and that is usually fine since most of time solution contains only roslyn files.
//
// but for mixed solution (ex, Roslyn files + HTML + JS + CSS), #2 still makes sense but #1 doesn't. We want
// to pause any work while something is going on in other project types as well.
//
// we need to make sure we play nice with neighbors as well.
//
// now, we don't care where changes are coming from. if there is any change in host, we pause ourselves for a while.
this.UpdateLastAccessTime();
}
}
}
}
......
......@@ -20,22 +20,23 @@ private sealed partial class IncrementalAnalyzerProcessor
private sealed class HighPriorityProcessor : IdleProcessor
{
private readonly IncrementalAnalyzerProcessor _processor;
private readonly ImmutableArray<IIncrementalAnalyzer> _analyzers;
private readonly AsyncDocumentWorkItemQueue _workItemQueue;
private Lazy<ImmutableArray<IIncrementalAnalyzer>> _lazyAnalyzers;
// whether this processor is running or not
private Task _running;
public HighPriorityProcessor(
IAsynchronousOperationListener listener,
IncrementalAnalyzerProcessor processor,
ImmutableArray<IIncrementalAnalyzer> analyzers,
Lazy<ImmutableArray<IIncrementalAnalyzer>> lazyAnalyzers,
int backOffTimeSpanInMs,
CancellationToken shutdownToken) :
base(listener, backOffTimeSpanInMs, shutdownToken)
{
_processor = processor;
_analyzers = analyzers;
_lazyAnalyzers = lazyAnalyzers;
_running = SpecializedTasks.EmptyTask;
_workItemQueue = new AsyncDocumentWorkItemQueue(processor._registration.ProgressReporter, processor._registration.Workspace);
......@@ -59,6 +60,13 @@ public bool HasAnyWork
}
}
public void AddAnalyzer(IIncrementalAnalyzer analyzer)
{
var analyzers = _lazyAnalyzers.Value;
_lazyAnalyzers = new Lazy<ImmutableArray<IIncrementalAnalyzer>>(() => analyzers.Add(analyzer));
}
public void Enqueue(WorkItem item)
{
Contract.ThrowIfFalse(item.DocumentId != null, "can only enqueue a document work item");
......@@ -119,7 +127,7 @@ protected override async Task ExecuteAsync()
var solution = _processor.CurrentSolution;
// okay now we have work to do
await ProcessDocumentAsync(solution, _analyzers, workItem, documentCancellation).ConfigureAwait(false);
await ProcessDocumentAsync(solution, _lazyAnalyzers.Value, workItem, documentCancellation).ConfigureAwait(false);
}
catch (Exception e) when (FatalError.ReportUnlessCanceled(e))
{
......
// 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;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
......@@ -52,18 +53,18 @@ private partial class IncrementalAnalyzerProcessor
_lazyDiagnosticAnalyzerService = new Lazy<IDiagnosticAnalyzerService>(() => GetDiagnosticAnalyzerService(analyzerProviders));
// create active file analyzers right away
var activeFileAnalyzers = GetActiveFileIncrementalAnalyzers(_registration, analyzerProviders);
var analyzersGetter = new AnalyzersGetter(analyzerProviders);
// create non active file analyzers lazily.
var lazyAllAnalyzers = new Lazy<ImmutableArray<IIncrementalAnalyzer>>(() => GetIncrementalAnalyzers(_registration, analyzerProviders));
// create analyzers lazily.
var lazyActiveFileAnalyzers = new Lazy<ImmutableArray<IIncrementalAnalyzer>>(() => GetIncrementalAnalyzers(_registration, analyzersGetter, onlyHighPriorityAnalyzer: true));
var lazyAllAnalyzers = new Lazy<ImmutableArray<IIncrementalAnalyzer>>(() => GetIncrementalAnalyzers(_registration, analyzersGetter, onlyHighPriorityAnalyzer: false));
// event and worker queues
_documentTracker = _registration.GetService<IDocumentTrackingService>();
var globalNotificationService = _registration.GetService<IGlobalOperationNotificationService>();
_highPriorityProcessor = new HighPriorityProcessor(listener, this, activeFileAnalyzers, highBackOffTimeSpanInMs, shutdownToken);
_highPriorityProcessor = new HighPriorityProcessor(listener, this, lazyActiveFileAnalyzers, highBackOffTimeSpanInMs, shutdownToken);
_normalPriorityProcessor = new NormalPriorityProcessor(listener, this, lazyAllAnalyzers, globalNotificationService, normalBackOffTimeSpanInMs, shutdownToken);
_lowPriorityProcessor = new LowPriorityProcessor(listener, this, lazyAllAnalyzers, globalNotificationService, lowBackOffTimeSpanInMs, shutdownToken);
}
......@@ -75,34 +76,14 @@ private IDiagnosticAnalyzerService GetDiagnosticAnalyzerService(IEnumerable<Lazy
return (IDiagnosticAnalyzerService)analyzerProviders.Where(p => p.Value is IDiagnosticAnalyzerService).SingleOrDefault()?.Value;
}
private static ImmutableArray<IIncrementalAnalyzer> GetActiveFileIncrementalAnalyzers(
Registration registration, IEnumerable<Lazy<IIncrementalAnalyzerProvider, IncrementalAnalyzerProviderMetadata>> providers)
private ImmutableArray<IIncrementalAnalyzer> GetIncrementalAnalyzers(Registration registration, AnalyzersGetter analyzersGetter, bool onlyHighPriorityAnalyzer)
{
var orderedAnalyzers = GetOrderedAnalyzers(registration, providers.Where(p => p.Metadata.HighPriorityForActiveFile));
var orderedAnalyzers = analyzersGetter.GetOrderedAnalyzers(registration.Workspace, onlyHighPriorityAnalyzer);
SolutionCrawlerLogger.LogActiveFileAnalyzers(registration.CorrelationId, registration.Workspace, orderedAnalyzers);
SolutionCrawlerLogger.LogAnalyzers(registration.CorrelationId, registration.Workspace, orderedAnalyzers, onlyHighPriorityAnalyzer);
return orderedAnalyzers;
}
private static ImmutableArray<IIncrementalAnalyzer> GetIncrementalAnalyzers(
Registration registration, IEnumerable<Lazy<IIncrementalAnalyzerProvider, IncrementalAnalyzerProviderMetadata>> providers)
{
var orderedAnalyzers = GetOrderedAnalyzers(registration, providers);
SolutionCrawlerLogger.LogAnalyzers(registration.CorrelationId, registration.Workspace, orderedAnalyzers);
return orderedAnalyzers;
}
private static ImmutableArray<IIncrementalAnalyzer> GetOrderedAnalyzers(
Registration registration, IEnumerable<Lazy<IIncrementalAnalyzerProvider, IncrementalAnalyzerProviderMetadata>> providers)
{
// Sort list so BaseDiagnosticIncrementalAnalyzers (if any) come first. OrderBy orders 'false' keys before 'true'.
return providers.Select(p => p.Value.CreateIncrementalAnalyzer(registration.Workspace))
.WhereNotNull()
.OrderBy(a => !(a is BaseDiagnosticIncrementalAnalyzer))
.ToImmutableArray();
}
public void Enqueue(WorkItem item)
{
Contract.ThrowIfNull(item.DocumentId);
......@@ -112,6 +93,17 @@ public void Enqueue(WorkItem item)
_lowPriorityProcessor.Enqueue(item);
}
public void AddAnalyzer(IIncrementalAnalyzer analyzer, bool highPriorityForActiveFile)
{
if (highPriorityForActiveFile)
{
_highPriorityProcessor.AddAnalyzer(analyzer);
}
_normalPriorityProcessor.AddAnalyzer(analyzer);
_lowPriorityProcessor.AddAnalyzer(analyzer);
}
public void Shutdown()
{
_highPriorityProcessor.Shutdown();
......@@ -326,6 +318,45 @@ private class NullDisposable : IDisposable
public void Dispose() { }
}
private class AnalyzersGetter
{
private readonly List<Lazy<IIncrementalAnalyzerProvider, IncrementalAnalyzerProviderMetadata>> _analyzerProviders;
private readonly Dictionary<Workspace, ImmutableArray<ValueTuple<IIncrementalAnalyzer, bool>>> _analyzerMap;
public AnalyzersGetter(IEnumerable<Lazy<IIncrementalAnalyzerProvider, IncrementalAnalyzerProviderMetadata>> analyzerProviders)
{
_analyzerMap = new Dictionary<Workspace, ImmutableArray<ValueTuple<IIncrementalAnalyzer, bool>>>();
_analyzerProviders = analyzerProviders.ToList();
}
public ImmutableArray<IIncrementalAnalyzer> GetOrderedAnalyzers(Workspace workspace, bool onlyHighPriorityAnalyzer)
{
lock (_analyzerMap)
{
ImmutableArray<ValueTuple<IIncrementalAnalyzer, bool>> analyzers;
if (!_analyzerMap.TryGetValue(workspace, out analyzers))
{
// Sort list so BaseDiagnosticIncrementalAnalyzers (if any) come first. OrderBy orders 'false' keys before 'true'.
analyzers = _analyzerProviders.Select(p => ValueTuple.Create(p.Value.CreateIncrementalAnalyzer(workspace), p.Metadata.HighPriorityForActiveFile))
.Where(t => t.Item1 != null)
.OrderBy(t => !(t.Item1 is BaseDiagnosticIncrementalAnalyzer))
.ToImmutableArray();
_analyzerMap[workspace] = analyzers;
}
if (onlyHighPriorityAnalyzer)
{
// include only high priority analyzer for active file
return analyzers.Where(t => t.Item2).Select(t => t.Item1).ToImmutableArray();
}
// return all analyzers
return analyzers.Select(t => t.Item1).ToImmutableArray();
}
}
}
}
}
}
......
......@@ -21,7 +21,6 @@ private sealed partial class IncrementalAnalyzerProcessor
{
private sealed class LowPriorityProcessor : AbstractPriorityProcessor
{
private readonly Lazy<ImmutableArray<IIncrementalAnalyzer>> _lazyAnalyzers;
private readonly AsyncProjectWorkItemQueue _workItemQueue;
public LowPriorityProcessor(
......@@ -31,22 +30,13 @@ private sealed class LowPriorityProcessor : AbstractPriorityProcessor
IGlobalOperationNotificationService globalOperationNotificationService,
int backOffTimeSpanInMs,
CancellationToken shutdownToken) :
base(listener, processor, globalOperationNotificationService, backOffTimeSpanInMs, shutdownToken)
base(listener, processor, lazyAnalyzers, globalOperationNotificationService, backOffTimeSpanInMs, shutdownToken)
{
_lazyAnalyzers = lazyAnalyzers;
_workItemQueue = new AsyncProjectWorkItemQueue(processor._registration.ProgressReporter, processor._registration.Workspace);
Start();
}
internal ImmutableArray<IIncrementalAnalyzer> Analyzers
{
get
{
return _lazyAnalyzers.Value;
}
}
protected override Task WaitAsync(CancellationToken cancellationToken)
{
return _workItemQueue.WaitAsync(cancellationToken);
......@@ -63,7 +53,7 @@ protected override async Task ExecuteAsync()
WorkItem workItem;
CancellationTokenSource projectCancellation;
if (_workItemQueue.TryTakeAnyWork(
this.Processor.GetActiveProject(), this.Processor.DependencyGraph, this.Processor.DiagnosticAnalyzerService,
this.Processor.GetActiveProject(), this.Processor.DependencyGraph, this.Processor.DiagnosticAnalyzerService,
out workItem, out projectCancellation))
{
await ProcessProjectAsync(this.Analyzers, workItem, projectCancellation).ConfigureAwait(false);
......
......@@ -27,10 +27,7 @@ private sealed class NormalPriorityProcessor : AbstractPriorityProcessor
private const int MaxHighPriorityQueueCache = 29;
private readonly AsyncDocumentWorkItemQueue _workItemQueue;
private readonly Lazy<ImmutableArray<IIncrementalAnalyzer>> _lazyAnalyzers;
private readonly ConcurrentDictionary<DocumentId, IDisposable> _higherPriorityDocumentsNotProcessed;
private readonly HashSet<ProjectId> _currentSnapshotVersionTrackingSet;
private ProjectId _currentProjectProcessing;
......@@ -47,10 +44,8 @@ private sealed class NormalPriorityProcessor : AbstractPriorityProcessor
IGlobalOperationNotificationService globalOperationNotificationService,
int backOffTimeSpanInMs,
CancellationToken shutdownToken) :
base(listener, processor, globalOperationNotificationService, backOffTimeSpanInMs, shutdownToken)
base(listener, processor, lazyAnalyzers, globalOperationNotificationService, backOffTimeSpanInMs, shutdownToken)
{
_lazyAnalyzers = lazyAnalyzers;
_running = SpecializedTasks.EmptyTask;
_workItemQueue = new AsyncDocumentWorkItemQueue(processor._registration.ProgressReporter, processor._registration.Workspace);
_higherPriorityDocumentsNotProcessed = new ConcurrentDictionary<DocumentId, IDisposable>(concurrencyLevel: 2, capacity: 20);
......@@ -63,14 +58,6 @@ private sealed class NormalPriorityProcessor : AbstractPriorityProcessor
Start();
}
internal ImmutableArray<IIncrementalAnalyzer> Analyzers
{
get
{
return _lazyAnalyzers.Value;
}
}
public void Enqueue(WorkItem item)
{
Contract.ThrowIfFalse(item.DocumentId != null, "can only enqueue a document work item");
......
......@@ -81,6 +81,16 @@ public int CorrelationId
get { return _registration.CorrelationId; }
}
public void AddAnalyzer(IIncrementalAnalyzer analyzer, bool highPriorityForActiveFile)
{
// add analyzer
_documentAndProjectWorkerProcessor.AddAnalyzer(analyzer, highPriorityForActiveFile);
// and ask to re-analyze whole solution for the given analyzer
var set = _registration.CurrentSolution.Projects.SelectMany(p => p.DocumentIds).ToSet();
Reanalyze(analyzer, set);
}
public void Shutdown(bool blockingShutdown)
{
_optionService.OptionChanged -= OnOptionChanged;
......@@ -510,7 +520,7 @@ private async Task EnqueueProjectConfigurationChangeWorkItemAsync(ProjectChanges
projectConfigurationChange = projectConfigurationChange.With(InvocationReasons.ProjectParseOptionChanged);
}
if (projectChanges.GetAddedMetadataReferences().Any() ||
if (projectChanges.GetAddedMetadataReferences().Any() ||
projectChanges.GetAddedProjectReferences().Any() ||
projectChanges.GetAddedAnalyzerReferences().Any() ||
projectChanges.GetRemovedMetadataReferences().Any() ||
......
......@@ -11,5 +11,7 @@ internal interface ISolutionCrawlerRegistrationService : IWorkspaceService
{
void Register(Workspace workspace);
void Unregister(Workspace workspace, bool blockingShutdown = false);
void AddAnalyzerProvider(IIncrementalAnalyzerProvider provider, IncrementalAnalyzerProviderMetadata metadata);
}
}
......@@ -17,5 +17,12 @@ public IncrementalAnalyzerProviderMetadata(IDictionary<string, object> data)
this.Name = (string)data.GetValueOrDefault("Name");
this.WorkspaceKinds = (string[])data.GetValueOrDefault("WorkspaceKinds");
}
public IncrementalAnalyzerProviderMetadata(string name, bool highPriorityForActiveFile, params string[] workspaceKinds)
{
this.HighPriorityForActiveFile = highPriorityForActiveFile;
this.Name = name;
this.WorkspaceKinds = workspaceKinds;
}
}
}
// 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;
using System.Composition;
using Microsoft.CodeAnalysis.Host.Mef;
......@@ -21,5 +22,10 @@ public void Unregister(Workspace workspace, bool blockingShutdown = false)
{
// base implementation do nothing.
}
public void AddAnalyzerProvider(IIncrementalAnalyzerProvider provider, IncrementalAnalyzerProviderMetadata metadata)
{
// base implementation do nothing.
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册