提交 77da8060 编写于 作者: H Heejae Chang

Merge pull request #1418 from heejaechang/solutioncrawler

changes around solution crawler.
......@@ -31,7 +31,7 @@ public async void Register(Workspace workspace)
{
try
{
var workerBackOffTimeSpanInMS = workspace.Options.GetOption(SolutionCrawlerOptions.PreviewBackOffTimeSpanInMS);
var workerBackOffTimeSpanInMS = workspace.Options.GetOption(InternalSolutionCrawlerOptions.PreviewBackOffTimeSpanInMS);
var analyzer = _provider.CreateIncrementalAnalyzer(workspace);
var source = s_cancellationTokens.GetValue(workspace, _ => new CancellationTokenSource());
......
......@@ -56,7 +56,7 @@ public static Type[] GetLanguageNeutralTypes()
typeof(TestExportProvider)
};
return types.Concat(TestHelpers.GetAllTypesWithStaticFieldsImplementingType(typeof(SolutionCrawlerOptions).Assembly, typeof(Microsoft.CodeAnalysis.Options.IOption)))
return types.Concat(TestHelpers.GetAllTypesWithStaticFieldsImplementingType(typeof(InternalSolutionCrawlerOptions).Assembly, typeof(Microsoft.CodeAnalysis.Options.IOption)))
.Concat(TestHelpers.GetAllTypesWithStaticFieldsImplementingType(typeof(EditorComponentOnOffOptions).Assembly, typeof(Microsoft.CodeAnalysis.Options.IOption)))
.Concat(TestHelpers.GetAllTypesWithStaticFieldsImplementingType(typeof(ServiceComponentOnOffOptions).Assembly, typeof(Microsoft.CodeAnalysis.Options.IOption)))
.Concat(TestHelpers.GetAllTypesWithStaticFieldsImplementingType(typeof(Microsoft.CodeAnalysis.Formatting.FormattingOptions).Assembly, typeof(Microsoft.CodeAnalysis.Options.IOption)))
......
......@@ -458,6 +458,8 @@ public async Task ReanalyzeAllDocumentsAsync(Project project, CancellationToken
{
foreach (var document in project.Documents)
{
cancellationToken.ThrowIfCancellationRequested();
await AppendDiagnosticsAsync(document, cancellationToken).ConfigureAwait(false);
}
}
......
......@@ -508,7 +508,7 @@
<Compile Include="SolutionCrawler\ISolutionCrawlerRegistrationService.cs" />
<Compile Include="SolutionCrawler\PredefinedInvocationReasons.cs" />
<Compile Include="SolutionCrawler\SolutionCrawlerLogger.cs" />
<Compile Include="SolutionCrawler\SolutionCrawlerOptions.cs" />
<Compile Include="SolutionCrawler\InternalSolutionCrawlerOptions.cs" />
<Compile Include="SolutionCrawler\SolutionCrawlerProgressReporter.cs" />
<Compile Include="SolutionCrawler\SolutionCrawlerService.cs" />
<Compile Include="SolutionCrawler\State\AbstractAnalyzerState.cs" />
......
......@@ -51,6 +51,27 @@ protected void UpdateLastAccessTime()
_lastAccessTimeInMS = Environment.TickCount;
}
protected async Task WaitForIdleAsync()
{
while (true)
{
if (this.CancellationToken.IsCancellationRequested)
{
return;
}
var diffInMS = Environment.TickCount - _lastAccessTimeInMS;
if (diffInMS >= _backOffTimeSpanInMS)
{
return;
}
// TODO: will safestart/unwarp capture cancellation exception?
var timeLeft = _backOffTimeSpanInMS - diffInMS;
await Task.Delay(Math.Max(MinimumDelayInMS, timeLeft), this.CancellationToken).ConfigureAwait(continueOnCapturedContext: false);
}
}
private async Task ProcessAsync()
{
while (true)
......@@ -80,27 +101,6 @@ private async Task ProcessAsync()
}
}
private async Task WaitForIdleAsync()
{
while (true)
{
if (this.CancellationToken.IsCancellationRequested)
{
return;
}
var diffInMS = Environment.TickCount - _lastAccessTimeInMS;
if (diffInMS >= _backOffTimeSpanInMS)
{
return;
}
// TODO: will safestart/unwarp capture cancellation exception?
var timeLeft = _backOffTimeSpanInMS - diffInMS;
await Task.Delay(Math.Max(MinimumDelayInMS, timeLeft), this.CancellationToken).ConfigureAwait(continueOnCapturedContext: false);
}
}
public virtual Task AsyncProcessorTask
{
get
......
......@@ -4,27 +4,29 @@
namespace Microsoft.CodeAnalysis.SolutionCrawler
{
internal static class SolutionCrawlerOptions
internal static class InternalSolutionCrawlerOptions
{
public const string OptionName = "SolutionCrawler";
[ExportOption]
public static readonly Option<bool> SolutionCrawler = new Option<bool>("FeatureManager/Components", "Solution Crawler", defaultValue: true);
[ExportOption]
public static readonly Option<int> ActiveFileWorkerBackOffTimeSpanInMS = new Option<int>("SolutionCrawler", "Active file worker backoff timespan in ms", defaultValue: 200);
public static readonly Option<int> ActiveFileWorkerBackOffTimeSpanInMS = new Option<int>(OptionName, "Active file worker backoff timespan in ms", defaultValue: 200);
[ExportOption]
public static readonly Option<int> AllFilesWorkerBackOffTimeSpanInMS = new Option<int>("SolutionCrawler", "All files worker backoff timespan in ms", defaultValue: 1500);
public static readonly Option<int> AllFilesWorkerBackOffTimeSpanInMS = new Option<int>(OptionName, "All files worker backoff timespan in ms", defaultValue: 1500);
[ExportOption]
public static readonly Option<int> EntireProjectWorkerBackOffTimeSpanInMS = new Option<int>("SolutionCrawler", "Entire project analysis worker backoff timespan in ms", defaultValue: 5000);
public static readonly Option<int> EntireProjectWorkerBackOffTimeSpanInMS = new Option<int>(OptionName, "Entire project analysis worker backoff timespan in ms", defaultValue: 5000);
[ExportOption]
public static readonly Option<int> SemanticChangeBackOffTimeSpanInMS = new Option<int>("SolutionCrawler", "Semantic change backoff timespan in ms", defaultValue: 100);
public static readonly Option<int> SemanticChangeBackOffTimeSpanInMS = new Option<int>(OptionName, "Semantic change backoff timespan in ms", defaultValue: 100);
[ExportOption]
public static readonly Option<int> ProjectPropagationBackOffTimeSpanInMS = new Option<int>("SolutionCrawler", "Project propagation backoff timespan in ms", defaultValue: 500);
public static readonly Option<int> ProjectPropagationBackOffTimeSpanInMS = new Option<int>(OptionName, "Project propagation backoff timespan in ms", defaultValue: 500);
[ExportOption]
public static readonly Option<int> PreviewBackOffTimeSpanInMS = new Option<int>("SolutionCrawler", "Preview backoff timespan in ms", defaultValue: 500);
public static readonly Option<int> PreviewBackOffTimeSpanInMS = new Option<int>(OptionName, "Preview backoff timespan in ms", defaultValue: 500);
}
}
......@@ -50,7 +50,7 @@ protected override bool TryTake_NoLock(DocumentId key, out WorkItem workInfo)
return false;
}
protected override bool TryTakeAnyWork_NoLock(ProjectId preferableProjectId, out WorkItem workItem)
protected override bool TryTakeAnyWork_NoLock(ProjectId preferableProjectId, ProjectDependencyGraph dependencyGraph, out WorkItem workItem)
{
// there must be at least one item in the map when this is called unless host is shutting down.
if (_documentWorkQueue.Count == 0)
......@@ -59,7 +59,7 @@ protected override bool TryTakeAnyWork_NoLock(ProjectId preferableProjectId, out
return false;
}
var documentId = GetBestDocumentId_NoLock(preferableProjectId);
var documentId = GetBestDocumentId_NoLock(preferableProjectId, dependencyGraph);
if (TryTake_NoLock(documentId, out workItem))
{
return true;
......@@ -68,18 +68,9 @@ protected override bool TryTakeAnyWork_NoLock(ProjectId preferableProjectId, out
return Contract.FailWithReturn<bool>("how?");
}
private DocumentId GetBestDocumentId_NoLock(ProjectId preferableProjectId)
private DocumentId GetBestDocumentId_NoLock(ProjectId preferableProjectId, ProjectDependencyGraph dependencyGraph)
{
var projectId = preferableProjectId;
if (projectId == null || !_documentWorkQueue.ContainsKey(projectId))
{
// explicitly iterate so that we can use struct enumerator
foreach (var pair in _documentWorkQueue)
{
projectId = pair.Key;
break;
}
}
var projectId = GetBestProjectId_NoLock(preferableProjectId, dependencyGraph);
var documentMap = _documentWorkQueue[projectId];
......@@ -104,6 +95,35 @@ private DocumentId GetBestDocumentId_NoLock(ProjectId preferableProjectId)
return lowPriorityDocumentId;
}
private ProjectId GetBestProjectId_NoLock(ProjectId projectId, ProjectDependencyGraph dependencyGraph)
{
if (projectId != null)
{
if (_documentWorkQueue.ContainsKey(projectId))
{
return projectId;
}
// see if there is any project that depends on this project has work item queued. if there is any, use that project
// as next project to process
foreach (var dependingProjectId in dependencyGraph.GetProjectsThatDirectlyDependOnThisProject(projectId))
{
if (_documentWorkQueue.ContainsKey(dependingProjectId))
{
return dependingProjectId;
}
}
}
// explicitly iterate so that we can use struct enumerator
foreach (var pair in _documentWorkQueue)
{
return pair.Key;
}
return Contract.FailWithReturn<ProjectId>("Shouldn't reach here");
}
protected override bool AddOrReplace_NoLock(WorkItem item)
{
// now document work
......
......@@ -49,7 +49,7 @@ protected override bool TryTake_NoLock(ProjectId key, out WorkItem workInfo)
return _projectWorkQueue.Remove(key);
}
protected override bool TryTakeAnyWork_NoLock(ProjectId preferableProjectId, out WorkItem workItem)
protected override bool TryTakeAnyWork_NoLock(ProjectId preferableProjectId, ProjectDependencyGraph dependencyGraph, out WorkItem workItem)
{
if (preferableProjectId != null)
{
......@@ -57,6 +57,14 @@ protected override bool TryTakeAnyWork_NoLock(ProjectId preferableProjectId, out
{
return true;
}
foreach (var dependingProjectId in dependencyGraph.GetProjectsThatDirectlyDependOnThisProject(preferableProjectId))
{
if (TryTake_NoLock(dependingProjectId, out workItem))
{
return true;
}
}
}
// explicitly iterate so that we can use struct enumerator
......
......@@ -39,7 +39,7 @@ public AsyncWorkItemQueue(SolutionCrawlerProgressReporter progressReporter)
protected abstract bool TryTake_NoLock(TKey key, out WorkItem workInfo);
protected abstract bool TryTakeAnyWork_NoLock(ProjectId preferableProjectId, out WorkItem workItem);
protected abstract bool TryTakeAnyWork_NoLock(ProjectId preferableProjectId, ProjectDependencyGraph dependencyGraph, out WorkItem workItem);
public bool HasAnyWork
{
......@@ -52,17 +52,6 @@ public bool HasAnyWork
}
}
public int WorkItemCount
{
get
{
lock (_gate)
{
return WorkItemCount_NoLock;
}
}
}
public void RemoveCancellationSource(object key)
{
lock (_gate)
......@@ -198,12 +187,12 @@ public bool TryTake(TKey key, out WorkItem workInfo, out CancellationTokenSource
}
}
public bool TryTakeAnyWork(ProjectId preferableProjectId, out WorkItem workItem, out CancellationTokenSource source)
public bool TryTakeAnyWork(ProjectId preferableProjectId, ProjectDependencyGraph dependencyGraph, out WorkItem workItem, out CancellationTokenSource source)
{
lock (_gate)
{
// there must be at least one item in the map when this is called unless host is shutting down.
if (TryTakeAnyWork_NoLock(preferableProjectId, out workItem))
if (TryTakeAnyWork_NoLock(preferableProjectId, dependencyGraph, out workItem))
{
if (!HasAnyWork_NoLock)
{
......
......@@ -45,6 +45,14 @@ private abstract class GlobalOperationAwareIdleProcessor : IdleProcessor
_globalOperationNotificationService.Stopped += OnGlobalOperationStopped;
}
protected Task GlobalOperationTask
{
get
{
return _globalOperationTask;
}
}
protected abstract void PauseOnGlobalOperation();
private void OnGlobalOperationStarted(object sender, EventArgs e)
......@@ -76,10 +84,30 @@ private void OnGlobalOperationStopped(object sender, GlobalOperationEventArgs e)
_globalOperationTask = SpecializedTasks.EmptyTask;
}
protected async Task GlobalOperationWaitAsync()
protected abstract Task HigherQueueOperationTask { get; }
protected abstract bool HigherQueueHasWorkItem { get; }
protected async Task WaitForHigherPriorityOperationsAsync()
{
// we wait for global operation if there is anything going on
await _globalOperationTask.ConfigureAwait(false);
do
{
// we wait for global operation and higher queue operation if there is anything going on
if (!this.GlobalOperationTask.IsCompleted || !this.HigherQueueOperationTask.IsCompleted)
{
await Task.WhenAll(this.GlobalOperationTask, this.HigherQueueOperationTask).ConfigureAwait(false);
}
// if there are no more work left for higher queue, then it is our time to go ahead
if (!HigherQueueHasWorkItem)
{
break;
}
// back off and wait for next time slot.
this.UpdateLastAccessTime();
await this.WaitForIdleAsync().ConfigureAwait(false);
}
while (true);
}
public virtual void Shutdown()
......
......@@ -59,6 +59,14 @@ public Task Running
}
}
public bool HasAnyWork
{
get
{
return _workItemQueue.HasAnyWork;
}
}
public void Enqueue(WorkItem item)
{
Contract.ThrowIfFalse(item.DocumentId != null, "can only enqueue a document work item");
......@@ -134,6 +142,7 @@ protected override async Task ExecuteAsync()
private bool GetNextWorkItem(out WorkItem workItem, out CancellationTokenSource documentCancellation)
{
// GetNextWorkItem since it can't fail. we still return bool to confirm that this never fail.
var documentId = _processor._documentTracker.GetActiveDocument();
if (documentId != null)
{
......@@ -143,7 +152,11 @@ private bool GetNextWorkItem(out WorkItem workItem, out CancellationTokenSource
}
}
return _workItemQueue.TryTakeAnyWork(preferableProjectId: null, workItem: out workItem, source: out documentCancellation);
return _workItemQueue.TryTakeAnyWork(
preferableProjectId: null,
dependencyGraph: this._processor.DependencyGraph,
workItem: out workItem,
source: out documentCancellation);
}
private async Task ProcessDocumentAsync(Solution solution, ImmutableArray<IIncrementalAnalyzer> analyzers, WorkItem workItem, CancellationTokenSource source)
......
......@@ -143,6 +143,14 @@ private Solution CurrentSolution
}
}
private ProjectDependencyGraph DependencyGraph
{
get
{
return CurrentSolution.GetProjectDependencyGraph();
}
}
private IEnumerable<DocumentId> GetOpenDocumentIds()
{
return _registration.Workspace.GetOpenDocumentIds();
......
......@@ -57,15 +57,12 @@ protected override async Task ExecuteAsync()
try
{
// we wait for global operation, higher and normal priority processor to finish its working
await Task.WhenAll(
this.Processor._highPriorityProcessor.Running,
this.Processor._normalPriorityProcessor.Running,
GlobalOperationWaitAsync()).ConfigureAwait(false);
await WaitForHigherPriorityOperationsAsync().ConfigureAwait(false);
// process any available project work, preferring the active project.
WorkItem workItem;
CancellationTokenSource projectCancellation;
if (_workItemQueue.TryTakeAnyWork(this.Processor.GetActiveProject(), out workItem, out projectCancellation))
if (_workItemQueue.TryTakeAnyWork(this.Processor.GetActiveProject(), this.Processor.DependencyGraph, out workItem, out projectCancellation))
{
await ProcessProjectAsync(this.Analyzers, workItem, projectCancellation).ConfigureAwait(false);
}
......@@ -76,6 +73,22 @@ protected override async Task ExecuteAsync()
}
}
protected override Task HigherQueueOperationTask
{
get
{
return Task.WhenAll(this.Processor._highPriorityProcessor.Running, this.Processor._normalPriorityProcessor.Running);
}
}
protected override bool HigherQueueHasWorkItem
{
get
{
return this.Processor._highPriorityProcessor.HasAnyWork || this.Processor._normalPriorityProcessor.HasAnyWork;
}
}
protected override void PauseOnGlobalOperation()
{
_workItemQueue.RequestCancellationOnRunningTasks();
......@@ -90,11 +103,25 @@ public void Enqueue(WorkItem item)
var added = _workItemQueue.AddOrReplace(item);
// lower priority queue gets lowest time slot possible. if there is any activity going on in higher queue, it drop whatever it has
// and let higher work item run
CancelRunningTaskIfHigherQueueHasWorkItem();
Logger.Log(FunctionId.WorkCoordinator_Project_Enqueue, s_enqueueLogger, Environment.TickCount, item.ProjectId, !added);
SolutionCrawlerLogger.LogWorkItemEnqueue(this.Processor._logAggregator, item.ProjectId);
}
private void CancelRunningTaskIfHigherQueueHasWorkItem()
{
if (!HigherQueueHasWorkItem)
{
return;
}
_workItemQueue.RequestCancellationOnRunningTasks();
}
private async Task ProcessProjectAsync(ImmutableArray<IIncrementalAnalyzer> analyzers, WorkItem workItem, CancellationTokenSource source)
{
if (this.CancellationToken.IsCancellationRequested)
......
......@@ -124,6 +124,14 @@ public Task Running
}
}
public bool HasAnyWork
{
get
{
return _workItemQueue.HasAnyWork;
}
}
protected override async Task ExecuteAsync()
{
if (this.CancellationToken.IsCancellationRequested)
......@@ -137,11 +145,7 @@ protected override async Task ExecuteAsync()
// mark it as running
_running = source.Task;
// we wait for global operation if there is anything going on
await GlobalOperationWaitAsync().ConfigureAwait(false);
// we wait for higher processor to finish its working
await this.Processor._highPriorityProcessor.Running.ConfigureAwait(false);
await WaitForHigherPriorityOperationsAsync().ConfigureAwait(false);
// okay, there must be at least one item in the map
await ResetStatesAsync().ConfigureAwait(false);
......@@ -155,7 +159,7 @@ protected override async Task ExecuteAsync()
// process one of documents remaining
var documentCancellation = default(CancellationTokenSource);
WorkItem workItem;
if (!_workItemQueue.TryTakeAnyWork(_currentProjectProcessing, out workItem, out documentCancellation))
if (!_workItemQueue.TryTakeAnyWork(_currentProjectProcessing, this.Processor.DependencyGraph, out workItem, out documentCancellation))
{
return;
}
......@@ -183,6 +187,22 @@ protected override async Task ExecuteAsync()
}
}
protected override Task HigherQueueOperationTask
{
get
{
return this.Processor._highPriorityProcessor.Running;
}
}
protected override bool HigherQueueHasWorkItem
{
get
{
return this.Processor._highPriorityProcessor.HasAnyWork;
}
}
protected override void PauseOnGlobalOperation()
{
_workItemQueue.RequestCancellationOnRunningTasks();
......
......@@ -54,21 +54,21 @@ private partial class WorkCoordinator
_eventProcessingQueue = new SimpleTaskQueue(TaskScheduler.Default);
var activeFileBackOffTimeSpanInMS = _optionService.GetOption(SolutionCrawlerOptions.ActiveFileWorkerBackOffTimeSpanInMS);
var allFilesWorkerBackOffTimeSpanInMS = _optionService.GetOption(SolutionCrawlerOptions.AllFilesWorkerBackOffTimeSpanInMS);
var entireProjectWorkerBackOffTimeSpanInMS = _optionService.GetOption(SolutionCrawlerOptions.EntireProjectWorkerBackOffTimeSpanInMS);
var activeFileBackOffTimeSpanInMS = _optionService.GetOption(InternalSolutionCrawlerOptions.ActiveFileWorkerBackOffTimeSpanInMS);
var allFilesWorkerBackOffTimeSpanInMS = _optionService.GetOption(InternalSolutionCrawlerOptions.AllFilesWorkerBackOffTimeSpanInMS);
var entireProjectWorkerBackOffTimeSpanInMS = _optionService.GetOption(InternalSolutionCrawlerOptions.EntireProjectWorkerBackOffTimeSpanInMS);
_documentAndProjectWorkerProcessor = new IncrementalAnalyzerProcessor(
listener, analyzerProviders, _registration,
activeFileBackOffTimeSpanInMS, allFilesWorkerBackOffTimeSpanInMS, entireProjectWorkerBackOffTimeSpanInMS, _shutdownToken);
var semanticBackOffTimeSpanInMS = _optionService.GetOption(SolutionCrawlerOptions.SemanticChangeBackOffTimeSpanInMS);
var projectBackOffTimeSpanInMS = _optionService.GetOption(SolutionCrawlerOptions.ProjectPropagationBackOffTimeSpanInMS);
var semanticBackOffTimeSpanInMS = _optionService.GetOption(InternalSolutionCrawlerOptions.SemanticChangeBackOffTimeSpanInMS);
var projectBackOffTimeSpanInMS = _optionService.GetOption(InternalSolutionCrawlerOptions.ProjectPropagationBackOffTimeSpanInMS);
_semanticChangeProcessor = new SemanticChangeProcessor(listener, _registration, _documentAndProjectWorkerProcessor, semanticBackOffTimeSpanInMS, projectBackOffTimeSpanInMS, _shutdownToken);
// if option is on
if (_optionService.GetOption(SolutionCrawlerOptions.SolutionCrawler))
if (_optionService.GetOption(InternalSolutionCrawlerOptions.SolutionCrawler))
{
_registration.Workspace.WorkspaceChanged += OnWorkspaceChanged;
_registration.Workspace.DocumentOpened += OnDocumentOpened;
......@@ -116,7 +116,7 @@ public void Shutdown(bool blockingShutdown)
private void OnOptionChanged(object sender, OptionChangedEventArgs e)
{
// if solution crawler got turned off or on.
if (e.Option == SolutionCrawlerOptions.SolutionCrawler)
if (e.Option == InternalSolutionCrawlerOptions.SolutionCrawler)
{
var value = (bool)e.Value;
if (value)
......
......@@ -52,20 +52,6 @@ internal static void SetLogger(IOptionService optionsService, string loggerName)
Logger.SetLogger(GetLogger(optionsService, loggerName));
}
/// <summary>
/// let ones such as Perf setup to share loggingChecker func
/// </summary>
internal static Func<FunctionId, bool> GetLoggingChecker(IOptionService optionsService)
{
var functionIds = Enum.GetValues(typeof(FunctionId)).Cast<FunctionId>();
var functionIdOptions = functionIds.ToDictionary(
id => id, id => optionsService.GetOption(FunctionIdOptions.GetOption(id)));
Func<FunctionId, bool> loggingChecker = (functionId) => functionIdOptions[functionId];
return loggingChecker;
}
/// <summary>
/// get string representation of functionId
/// </summary>
......@@ -109,9 +95,9 @@ private static ILogger GetLogger(IOptionService optionsService, string loggerNam
switch (loggerName)
{
case "EtwLogger":
return new EtwLogger(GetLoggingChecker(optionsService));
return new EtwLogger(Logger.GetLoggingChecker(optionsService));
case "TraceLogger":
return new TraceLogger(GetLoggingChecker(optionsService));
return new TraceLogger(Logger.GetLoggingChecker(optionsService));
default:
return EmptyLogger.Instance;
}
......
......@@ -90,6 +90,7 @@ internal static class Guids
public const string RoslynOptionPagePerformanceFunctionIdIdString = "0C537218-3BDD-4CC8-AC4B-CEC152D4871A";
public const string RoslynOptionPagePerformanceLoggersIdString = "236AC96F-A60D-4BD6-A480-D315151EDC2B";
public const string RoslynOptionPageInternalDiagnosticsIdString = "48993C4C-C619-42AD-B1C8-79378AD8BEF2";
public const string RoslynOptionPageInternalSolutionCrawlerIdString = "9702D3BD-F06C-4A6A-974B-7D0C2BC89A72";
public static readonly Guid RoslynPackageId = new Guid(RoslynPackageIdString);
public static readonly Guid RoslynCommandSetId = new Guid(RoslynCommandSetIdString);
......
......@@ -60,6 +60,30 @@ protected void BindToOption(CheckBox checkbox, PerLanguageOption<bool> optionKey
_bindingExpressions.Add(bindingExpression);
}
protected void BindToOption(TextBox textBox, Option<int> optionKey)
{
Binding binding = new Binding();
binding.Source = new OptionBinding<int>(OptionService, optionKey);
binding.Path = new PropertyPath("Value");
binding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit;
var bindingExpression = textBox.SetBinding(TextBox.TextProperty, binding);
_bindingExpressions.Add(bindingExpression);
}
protected void BindToOption(TextBox textBox, PerLanguageOption<int> optionKey, string languageName)
{
Binding binding = new Binding();
binding.Source = new PerLanguageOptionBinding<int>(OptionService, optionKey, languageName);
binding.Path = new PropertyPath("Value");
binding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit;
var bindingExpression = textBox.SetBinding(TextBox.TextProperty, binding);
_bindingExpressions.Add(bindingExpression);
}
internal virtual void LoadSettings()
{
foreach (var bindingExpression in _bindingExpressions)
......
......@@ -25,6 +25,16 @@ public AbstractSettingStoreOptionSerializer(IServiceProvider serviceProvider)
}
public virtual bool TryFetch(OptionKey optionKey, out object value)
{
return TryFetch(optionKey, (r, k, o) => r.GetValue(k, defaultValue: (bool)o.DefaultValue ? 1 : 0).Equals(1), out value);
}
public virtual bool TryPersist(OptionKey optionKey, object value)
{
return TryPersist(optionKey, value, (r, k, o, v) => r.SetValue(k, (bool)v ? 1 : 0, RegistryValueKind.DWord));
}
protected bool TryFetch(OptionKey optionKey, Func<RegistryKey, string, IOption, object> valueGetter, out object value)
{
if (this.RegistryKey == null)
{
......@@ -40,21 +50,21 @@ public virtual bool TryFetch(OptionKey optionKey, out object value)
lock (Gate)
{
using (var openSubKey = this.RegistryKey.OpenSubKey(collectionPathAndPropertyName.Item1))
using (var subKey = this.RegistryKey.OpenSubKey(collectionPathAndPropertyName.Item1))
{
if (openSubKey == null)
if (subKey == null)
{
value = null;
return false;
}
value = openSubKey.GetValue(collectionPathAndPropertyName.Item2, defaultValue: (bool)optionKey.Option.DefaultValue ? 1 : 0).Equals(1);
value = valueGetter(subKey, collectionPathAndPropertyName.Item2, optionKey.Option);
return true;
}
}
}
public virtual bool TryPersist(OptionKey optionKey, object value)
protected bool TryPersist(OptionKey optionKey, object value, Action<RegistryKey, string, IOption, object> valueSetter)
{
// We ignore languageName, since the current use of this class is only for
// language-specific options that apply to a single language. The underlying option
......@@ -74,10 +84,9 @@ public virtual bool TryPersist(OptionKey optionKey, object value)
{
using (var subKey = this.RegistryKey.CreateSubKey(collectionPathAndPropertyName.Item1))
{
subKey.SetValue(collectionPathAndPropertyName.Item2, (bool)value ? 1 : 0, RegistryValueKind.DWord);
valueSetter(subKey, collectionPathAndPropertyName.Item2, optionKey.Option, value);
return true;
}
return true;
}
}
}
......
......@@ -6,6 +6,7 @@
using Microsoft.CodeAnalysis.Editor.Shared.Options;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.SolutionCrawler;
using Microsoft.VisualStudio.Shell;
using Microsoft.Win32;
using Roslyn.Utilities;
......@@ -18,11 +19,10 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.Options
PerformanceFunctionIdOptionsProvider.Name,
LoggerOptions.FeatureName,
CacheOptions.FeatureName,
InternalDiagnosticsOptions.OptionName), Shared]
InternalDiagnosticsOptions.OptionName,
InternalSolutionCrawlerOptions.OptionName), Shared]
internal class InternalOptionSerializer : AbstractSettingStoreOptionSerializer
{
private const string CachePath = @"Roslyn\Internal\Performance\Cache";
[ImportingConstructor]
public InternalOptionSerializer(SVsServiceProvider serviceProvider)
: base(serviceProvider)
......@@ -51,28 +51,25 @@ public InternalOptionSerializer(SVsServiceProvider serviceProvider)
{
return Tuple.Create(@"Roslyn\Internal\Diagnostics", key.Name);
}
else if (key.Feature == InternalSolutionCrawlerOptions.OptionName)
{
return Tuple.Create(@"Roslyn\Internal\SolutionCrawler", key.Name);
}
else if (key.Feature == CacheOptions.FeatureName)
{
return Tuple.Create(@"Roslyn\Internal\Performance\Cache", key.Name);
}
throw ExceptionUtilities.Unreachable;
}
public override bool TryFetch(OptionKey optionKey, out object value)
{
if (optionKey.Option.Feature == CacheOptions.FeatureName)
switch (optionKey.Option.Feature)
{
lock (Gate)
{
using (var openSubKey = this.RegistryKey.OpenSubKey(CachePath))
{
if (openSubKey == null)
{
value = null;
return false;
}
value = openSubKey.GetValue(optionKey.Option.Name, defaultValue: optionKey.Option.DefaultValue);
return true;
}
}
case CacheOptions.FeatureName:
case InternalSolutionCrawlerOptions.OptionName:
return TryFetch(optionKey, (r, k, o) => r.GetValue(k, defaultValue: o.DefaultValue), out value);
}
return base.TryFetch(optionKey, out value);
......@@ -80,16 +77,11 @@ public override bool TryFetch(OptionKey optionKey, out object value)
public override bool TryPersist(OptionKey optionKey, object value)
{
if (optionKey.Option.Feature == CacheOptions.FeatureName)
switch (optionKey.Option.Feature)
{
lock (Gate)
{
using (var subKey = this.RegistryKey.CreateSubKey(CachePath))
{
subKey.SetValue(optionKey.Option.Name, value, optionKey.Option.Type == typeof(int) ? RegistryValueKind.DWord : RegistryValueKind.QWord);
return true;
}
}
case CacheOptions.FeatureName:
case InternalSolutionCrawlerOptions.OptionName:
return TryPersist(optionKey, value, (r, k, o, v) => r.SetValue(k, v, o.Type == typeof(int) ? RegistryValueKind.DWord : RegistryValueKind.QWord));
}
return base.TryPersist(optionKey, value);
......
......@@ -2,6 +2,7 @@
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Options;
......@@ -19,9 +20,7 @@ public InternalOptionsControl(string featureOptionName, IServiceProvider service
{
if (!option.IsPerLanguage)
{
var checkBox = new CheckBox() { Content = option.Name };
BindToOption(checkBox, (Option<bool>)option);
panel.Children.Add(checkBox);
AddOption(panel, option);
}
else
{
......@@ -51,11 +50,79 @@ public InternalOptionsControl(string featureOptionName, IServiceProvider service
this.Content = viewer;
}
private void AddPerLanguageOption(StackPanel panel, IOption option, string language)
private void AddOption(StackPanel panel, IOption option)
{
var checkBox = new CheckBox() { Content = option.Name + " [" + language + "]" };
BindToOption(checkBox, (PerLanguageOption<bool>)option, language);
panel.Children.Add(checkBox);
var uiElement = CreateControl(option);
if (uiElement != null)
{
panel.Children.Add(uiElement);
}
}
private void AddPerLanguageOption(StackPanel panel, IOption option, string languageName)
{
var uiElement = CreateControl(option, languageName);
if (uiElement != null)
{
panel.Children.Add(uiElement);
}
}
private UIElement CreateControl(IOption option, string languageName = null)
{
if (option.Type == typeof(bool))
{
var checkBox = new CheckBox() { Content = option.Name + GetLanguage(languageName) };
BindToCheckBox(checkBox, option, languageName);
return checkBox;
}
if (option.Type == typeof(int))
{
var label = new Label() { Content = option.Name + GetLanguage(languageName) };
var textBox = new TextBox();
BindToTextBox(textBox, option, languageName);
var panel = new StackPanel();
panel.Children.Add(label);
panel.Children.Add(textBox);
return panel;
}
return null;
}
private string GetLanguage(string languageName)
{
if (languageName == null)
{
return string.Empty;
}
return " [" + languageName + "]";
}
private void BindToCheckBox(CheckBox checkBox, IOption option, string languageName = null)
{
if (languageName == null)
{
BindToOption(checkBox, (Option<bool>)option);
return;
}
BindToOption(checkBox, (PerLanguageOption<bool>)option, languageName);
}
private void BindToTextBox(TextBox textBox, IOption option, string languageName = null)
{
if (languageName == null)
{
BindToOption(textBox, (Option<int>)option);
return;
}
BindToOption(textBox, (PerLanguageOption<int>)option, languageName);
}
}
}
// 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.Runtime.InteropServices;
using Microsoft.CodeAnalysis.SolutionCrawler;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.Options
{
[Guid(Guids.RoslynOptionPageInternalSolutionCrawlerIdString)]
internal class InternalSolutionCrawlerPage : AbstractOptionPage
{
protected override AbstractOptionPageControl CreateOptionPage(IServiceProvider serviceProvider)
{
return new InternalOptionsControl(InternalSolutionCrawlerOptions.OptionName, serviceProvider);
}
}
}
......@@ -106,6 +106,7 @@
<ItemGroup>
<Compile Include="DiagnosticsWindow.cs" />
<Compile Include="Guids.cs" />
<Compile Include="OptionPages\InternalSolutionCrawlerPage.cs" />
<Compile Include="OptionPages\InternalComponentsOnOffPage.cs" />
<Compile Include="OptionPages\InternalDiagnosticsPage.cs" />
<Compile Include="OptionPages\InternalFeaturesOnOffPage.cs" />
......
......@@ -34,6 +34,7 @@ namespace Roslyn.VisualStudio.DiagnosticsWindow
[ProvideOptionPage(typeof(PerformanceFunctionIdPage), @"Roslyn\Performance", @"FunctionId", categoryResourceID: 0, pageNameResourceID: 0, supportsAutomation: true, SupportsProfiles = false)]
[ProvideOptionPage(typeof(PerformanceLoggersPage), @"Roslyn\Performance", @"Loggers", categoryResourceID: 0, pageNameResourceID: 0, supportsAutomation: true, SupportsProfiles = false)]
[ProvideOptionPage(typeof(InternalDiagnosticsPage), @"Roslyn\Diagnostics", @"Internal", categoryResourceID: 0, pageNameResourceID: 0, supportsAutomation: true, SupportsProfiles = false)]
[ProvideOptionPage(typeof(InternalSolutionCrawlerPage), @"Roslyn\SolutionCrawler", @"Internal", categoryResourceID: 0, pageNameResourceID: 0, supportsAutomation: true, SupportsProfiles = false)]
// This attribute registers a tool window exposed by this package.
[ProvideToolWindow(typeof(DiagnosticsWindow))]
[Guid(GuidList.guidVisualStudioDiagnosticsWindowPkgString)]
......
......@@ -25,82 +25,11 @@ private static bool GetDefaultValue(FunctionId id)
{
switch (id)
{
case FunctionId.TestEvent_NotUsed:
return false;
case FunctionId.Tagger_AdornmentManager_OnLayoutChanged:
case FunctionId.Tagger_AdornmentManager_UpdateInvalidSpans:
case FunctionId.Tagger_BatchChangeNotifier_NotifyEditorNow:
case FunctionId.Tagger_BatchChangeNotifier_NotifyEditor:
case FunctionId.Tagger_TagSource_ProcessNewTags:
case FunctionId.Tagger_SyntacticClassification_TagComputer_GetTags:
case FunctionId.Tagger_SemanticClassification_TagProducer_ProduceTags:
case FunctionId.Tagger_BraceHighlighting_TagProducer_ProduceTags:
case FunctionId.Tagger_LineSeparator_TagProducer_ProduceTags:
case FunctionId.Tagger_Outlining_TagProducer_ProduceTags:
case FunctionId.Tagger_Highlighter_TagProducer_ProduceTags:
case FunctionId.Tagger_ReferenceHighlighting_TagProducer_ProduceTags:
return false;
case FunctionId.Tagger_Diagnostics_RecomputeTags:
case FunctionId.Tagger_Diagnostics_Updated:
return false;
case FunctionId.Workspace_SourceText_GetChangeRanges:
case FunctionId.Workspace_Recoverable_RecoverRootAsync:
case FunctionId.Workspace_Recoverable_RecoverRoot:
case FunctionId.Workspace_Recoverable_RecoverTextAsync:
case FunctionId.Workspace_Recoverable_RecoverText:
return false;
case FunctionId.Misc_NonReentrantLock_BlockingWait:
return false;
case FunctionId.Cache_Created:
case FunctionId.Cache_AddOrAccess:
case FunctionId.Cache_Remove:
case FunctionId.Cache_Evict:
case FunctionId.Cache_EvictAll:
case FunctionId.Cache_ItemRank:
return false;
case FunctionId.Simplifier_ReduceAsync:
case FunctionId.Simplifier_ExpandNode:
case FunctionId.Simplifier_ExpandToken:
return false;
case FunctionId.TemporaryStorageServiceFactory_ReadText:
case FunctionId.TemporaryStorageServiceFactory_WriteText:
case FunctionId.TemporaryStorageServiceFactory_ReadStream:
case FunctionId.TemporaryStorageServiceFactory_WriteStream:
return false;
case FunctionId.WorkCoordinator_DocumentWorker_Enqueue:
case FunctionId.WorkCoordinator_ProcessProjectAsync:
case FunctionId.WorkCoordinator_ProcessDocumentAsync:
case FunctionId.WorkCoordinator_SemanticChange_Enqueue:
case FunctionId.WorkCoordinator_SemanticChange_EnqueueFromMember:
case FunctionId.WorkCoordinator_SemanticChange_EnqueueFromType:
case FunctionId.WorkCoordinator_SemanticChange_FullProjects:
case FunctionId.WorkCoordinator_Project_Enqueue:
case FunctionId.WorkCoordinator_ActivieFileEnqueue:
return false;
case FunctionId.Diagnostics_SyntaxDiagnostic:
case FunctionId.Diagnostics_SemanticDiagnostic:
case FunctionId.Diagnostics_ProjectDiagnostic:
case FunctionId.Diagnostics_DocumentReset:
case FunctionId.Diagnostics_DocumentOpen:
case FunctionId.Diagnostics_RemoveDocument:
case FunctionId.Diagnostics_RemoveProject:
return false;
case FunctionId.SuggestedActions_HasSuggestedActionsAsync:
case FunctionId.SuggestedActions_GetSuggestedActions:
return false;
// change not to enable any etw events by default.
// we used to couple this to other logger such as code marker but now it is only specific to etw.
// each events should be enabled specifically when needed.
default:
return true;
return false;
}
}
}
......
......@@ -59,7 +59,7 @@ protected override string CreateMessage()
return string.Join("|", _map.Select(kv => string.Format("{0}={1}", kv.Key, kv.Value)));
}
public override void Free()
protected override void FreeCore()
{
if (_map != null)
{
......
......@@ -48,7 +48,7 @@ public static LogMessage Create<TArg>(Func<TArg, string> messageGetter, TArg arg
/// <summary>
/// Logger will call this to return LogMessage to its pool
/// </summary>
public abstract void Free();
protected abstract void FreeCore();
public string GetMessage()
{
......@@ -60,6 +60,13 @@ public string GetMessage()
return _message;
}
public void Free()
{
_message = null;
FreeCore();
}
private sealed class StaticLogMessage : LogMessage
{
private static readonly ObjectPool<StaticLogMessage> s_pool = SharedPools.Default<StaticLogMessage>();
......@@ -77,7 +84,7 @@ protected override string CreateMessage()
return _message;
}
public override void Free()
protected override void FreeCore()
{
if (_message == null)
{
......@@ -108,7 +115,7 @@ protected override string CreateMessage()
return _messageGetter();
}
public override void Free()
protected override void FreeCore()
{
if (_messageGetter == null)
{
......@@ -141,7 +148,7 @@ protected override string CreateMessage()
return _messageGetter(_arg);
}
public override void Free()
protected override void FreeCore()
{
if (_messageGetter == null)
{
......@@ -177,7 +184,7 @@ protected override string CreateMessage()
return _messageGetter(_arg0, _arg1);
}
public override void Free()
protected override void FreeCore()
{
if (_messageGetter == null)
{
......@@ -216,7 +223,7 @@ protected override string CreateMessage()
return _messageGetter(_arg0, _arg1, _arg2);
}
public override void Free()
protected override void FreeCore()
{
if (_messageGetter == null)
{
......@@ -258,7 +265,7 @@ protected override string CreateMessage()
return _messageGetter(_arg0, _arg1, _arg2, _arg3);
}
public override void Free()
protected override void FreeCore()
{
if (_messageGetter == null)
{
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册