Skip to content

Commit

Permalink
Remove document options provider (#61228)
Browse files Browse the repository at this point in the history
* Remove IDocumentOptionsProvider

* Fold DocumentSpecificOptionSet into DocumentOptionSet
  • Loading branch information
tmat authored May 16, 2022
1 parent 557a497 commit 3663223
Show file tree
Hide file tree
Showing 19 changed files with 170 additions and 338 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Formatting;
using Microsoft.CodeAnalysis.Editor.UnitTests;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.TodoComments;
using Microsoft.CodeAnalysis.UnitTests;
using Microsoft.CodeAnalysis.VisualBasic.Formatting;
using Roslyn.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Formatting;

[UseExportProvider]
public class RazorLineFormattingOptionsTests
{
private static readonly TestComposition s_composition = EditorTestCompositions.EditorFeatures;

private class TestRazorDocumentServiceProvider : IDocumentServiceProvider
{
public TService? GetService<TService>() where TService : class, IDocumentService
=> typeof(TService) == typeof(DocumentPropertiesService) ? (TService?)(object)new PropertiesService() : null;

internal sealed class PropertiesService : DocumentPropertiesService
{
public override string? DiagnosticsLspClientName => "RazorCSharp";
}
}

[Fact]
public async Task FormatAsync()
{
var hostServices = s_composition.GetHostServices();

using var workspace = new AdhocWorkspace(hostServices);

var globalOptions = ((IMefHostExportProvider)hostServices).GetExportedValue<IGlobalOptionService>();
globalOptions.SetGlobalOption(new OptionKey(RazorLineFormattingOptionsStorage.UseTabs), true);
globalOptions.SetGlobalOption(new OptionKey(RazorLineFormattingOptionsStorage.TabSize), 10);

var project = workspace.AddProject("Test", LanguageNames.CSharp);

var source = @"
class C
{
void F () {}
}
";

var documentInfo = DocumentInfo.Create(
DocumentId.CreateNewId(project.Id),
name: "file.razor.g.cs",
folders: Array.Empty<string>(),
sourceCodeKind: SourceCodeKind.Regular,
loader: TextLoader.From(TextAndVersion.Create(SourceText.From(source), VersionStamp.Create(), "file.razor.g.cs")),
filePath: "file.razor.g.cs",
isGenerated: false,
designTimeOnly: true,
documentServiceProvider: new TestRazorDocumentServiceProvider());

var document = workspace.AddDocument(documentInfo);

#pragma warning disable RS0030 // Do not used banned APIs
var formattedDocument = await Formatter.FormatAsync(document, spans: null, options: null, CancellationToken.None);
#pragma warning restore RS0030 // Do not used banned APIs

var formattedText = await formattedDocument.GetTextAsync();

// document options override solution options:
AssertEx.Equal(@"
class C
{
" + "\t" + @"void F() { }
}
", formattedText.ToString());
}
}
2 changes: 0 additions & 2 deletions src/EditorFeatures/TestUtilities/Workspaces/TestWorkspace.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,6 @@ public TestWorkspace(
_backgroundParser.Start();

_metadataAsSourceFileService = ExportProvider.GetExportedValues<IMetadataAsSourceFileService>().FirstOrDefault();

RegisterDocumentOptionProviders(ExportProvider.GetExports<IDocumentOptionsProviderFactory, OrderableMetadata>());
}

internal static TestComposition GetComposition(TestComposition? composition)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Options.EditorConfig;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Text;
using static Microsoft.CodeAnalysis.CodeActions.CodeAction;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.Formatting;
/// <summary>
/// Formatting options for Razor design-time documents.
/// </summary>
internal class RazorLineFormattingOptionsStorage
internal static class RazorLineFormattingOptionsStorage
{
internal static readonly Option2<bool> UseTabs = new(
"RazorDesignTimeDocumentFormattingOptions", "UseTabs", LineFormattingOptions.Default.UseTabs);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,6 @@ public void SetOptions(OptionSet optionSet)
throw new NotImplementedException();
}

public void RegisterDocumentOptionsProvider(IDocumentOptionsProvider documentOptionsProvider)
{
throw new NotImplementedException();
}

public Task<OptionSet> GetUpdatedOptionSetForDocumentAsync(Document document, OptionSet optionSet, CancellationToken cancellationToken)
{
throw new NotImplementedException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,7 @@ public async Task<VisualStudioProject> CreateAndAddToWorkspaceAsync(
? filePath
: null;

// After the call to EnsureDocumentOptionProvidersInitializedAsync, everything can be off the UI thread.
// Thus, we have a ConfigureAwait(false) on the call and switch explicitly after.
await _visualStudioWorkspaceImpl.EnsureDocumentOptionProvidersInitializedAsync(cancellationToken).ConfigureAwait(false);
// Following can be off the UI thread.
await TaskScheduler.Default;

// From this point on, we start mutating the solution. So make us non cancellable.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,6 @@ internal abstract partial class VisualStudioWorkspaceImpl : VisualStudioWorkspac
internal FileWatchedPortableExecutableReferenceFactory FileWatchedReferenceFactory { get; }

private readonly Lazy<IProjectCodeModelFactory> _projectCodeModelFactory;
private readonly IEnumerable<Lazy<IDocumentOptionsProviderFactory, OrderableMetadata>> _documentOptionsProviderFactories;
private bool _documentOptionsProvidersInitialized = false;

private readonly Lazy<ExternalErrorDiagnosticUpdateSource> _lazyExternalErrorDiagnosticUpdateSource;
private readonly IAsynchronousOperationListener _workspaceListener;
Expand All @@ -145,7 +143,6 @@ public VisualStudioWorkspaceImpl(ExportProvider exportProvider, IAsyncServicePro
_textBufferFactoryService = exportProvider.GetExportedValue<ITextBufferFactoryService>();
_projectionBufferFactoryService = exportProvider.GetExportedValue<IProjectionBufferFactoryService>();
_projectCodeModelFactory = exportProvider.GetExport<IProjectCodeModelFactory>();
_documentOptionsProviderFactories = exportProvider.GetExports<IDocumentOptionsProviderFactory, OrderableMetadata>();

// We fetch this lazily because VisualStudioProjectFactory depends on VisualStudioWorkspaceImpl -- we have a circularity. Since this
// exists right now as a compat shim, we'll just do this.
Expand Down Expand Up @@ -2039,23 +2036,6 @@ await ApplyBatchChangeToWorkspaceAsync(solutionChanges =>
});
}

internal async Task EnsureDocumentOptionProvidersInitializedAsync(CancellationToken cancellationToken)
{
// HACK: switch to the UI thread, ensure we initialize our options provider which depends on a
// UI-affinitized experimentation service
await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);

_foregroundObject.AssertIsForeground();

if (_documentOptionsProvidersInitialized)
{
return;
}

_documentOptionsProvidersInitialized = true;
RegisterDocumentOptionProviders(_documentOptionsProviderFactories);
}

[PerformanceSensitive("https://github.com/dotnet/roslyn/issues/54137", AllowLocks = false)]
internal void SetMaxLanguageVersion(ProjectId projectId, string? maxLanguageVersion)
{
Expand Down
74 changes: 65 additions & 9 deletions src/Workspaces/Core/Portable/Options/DocumentOptionSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.ErrorReporting;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.Options
{
Expand All @@ -15,28 +20,79 @@ namespace Microsoft.CodeAnalysis.Options
/// </summary>
public sealed class DocumentOptionSet : OptionSet
{
private readonly OptionSet _backingOptionSet;
private readonly OptionSet _underlyingOptions;
private readonly StructuredAnalyzerConfigOptions? _configOptions;
private ImmutableDictionary<OptionKey, object?> _values;
private readonly string _language;

internal DocumentOptionSet(OptionSet backingOptionSet, string language)
internal DocumentOptionSet(StructuredAnalyzerConfigOptions? configOptions, OptionSet underlyingOptions, string language)
: this(configOptions, underlyingOptions, language, ImmutableDictionary<OptionKey, object?>.Empty)
{
}

private DocumentOptionSet(StructuredAnalyzerConfigOptions? configOptions, OptionSet underlyingOptions, string language, ImmutableDictionary<OptionKey, object?> values)
{
_backingOptionSet = backingOptionSet;
_language = language;
_configOptions = configOptions;
_underlyingOptions = underlyingOptions;
_values = values;
}

internal string Language => _language;

[PerformanceSensitive("https://github.com/dotnet/roslyn/issues/30819", AllowLocks = false)]
private protected override object? GetOptionCore(OptionKey optionKey)
=> _backingOptionSet.GetOption(optionKey);
{
// If we already know the document specific value, we're done
if (_values.TryGetValue(optionKey, out var value))
{
return value;
}

if (TryGetAnalyzerConfigOption(optionKey, out value))
{
// Cache and return
return ImmutableInterlocked.GetOrAdd(ref _values, optionKey, value);
}

// We don't have a document specific value, so forward
return _underlyingOptions.GetOption(optionKey);
}

private bool TryGetAnalyzerConfigOption(OptionKey option, out object? value)
{
if (_configOptions == null)
{
value = null;
return false;
}

var editorConfigPersistence = (IEditorConfigStorageLocation?)option.Option.StorageLocations.SingleOrDefault(static location => location is IEditorConfigStorageLocation);
if (editorConfigPersistence == null)
{
value = null;
return false;
}

try
{
return editorConfigPersistence.TryGetOption(_configOptions, option.Option.Type, out value);
}
catch (Exception e) when (FatalError.ReportAndCatch(e))
{
value = null;
return false;
}
}

public T GetOption<T>(PerLanguageOption<T> option)
=> _backingOptionSet.GetOption(option, _language);
=> GetOption(option, _language);

internal T GetOption<T>(PerLanguageOption2<T> option)
=> _backingOptionSet.GetOption(option, _language);
=> GetOption(option, _language);

public override OptionSet WithChangedOption(OptionKey optionAndLanguage, object? value)
=> new DocumentOptionSet(_backingOptionSet.WithChangedOption(optionAndLanguage, value), _language);
=> new DocumentOptionSet(_configOptions, _underlyingOptions, _language, _values.SetItem(optionAndLanguage, value));

/// <summary>
/// Creates a new <see cref="DocumentOptionSet" /> that contains the changed value.
Expand All @@ -53,10 +109,10 @@ internal DocumentOptionSet WithChangedOption<T>(PerLanguageOption2<T> option, T
private protected override AnalyzerConfigOptions CreateAnalyzerConfigOptions(IOptionService optionService, string? language)
{
Debug.Assert((language ?? _language) == _language, $"Use of a {nameof(DocumentOptionSet)} is not expected to differ from the language it was constructed with.");
return _backingOptionSet.AsAnalyzerConfigOptions(optionService, language ?? _language);
return base.CreateAnalyzerConfigOptions(optionService, language ?? _language);
}

internal override IEnumerable<OptionKey> GetChangedOptions(OptionSet optionSet)
=> _backingOptionSet.GetChangedOptions(optionSet);
=> GetChangedOptions(optionSet);
}
}

This file was deleted.

20 changes: 0 additions & 20 deletions src/Workspaces/Core/Portable/Options/IDocumentOptions.cs

This file was deleted.

Loading

0 comments on commit 3663223

Please sign in to comment.