Skip to content

Commit

Permalink
Merge pull request #1526 from filipw/feature/editorconfig
Browse files Browse the repository at this point in the history
Added support for .editorconfig
  • Loading branch information
david-driscoll authored Jun 23, 2019
2 parents 574b162 + 7842db3 commit 100e73f
Show file tree
Hide file tree
Showing 28 changed files with 756 additions and 79 deletions.
1 change: 1 addition & 0 deletions NuGet.Config
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
<add key="dotnet-core-redux" value="https://dotnet.myget.org/F/dotnet-cli/api/v3/index.json" />
<add key="OmniSharp" value="https://www.myget.org/F/omnisharp/api/v3/index.json" />
<add key="roslyn-myget" value="https://dotnet.myget.org/F/roslyn/api/v3/index.json" />
<add key="vs-editor" value="https://www.myget.org/F/vs-editor/api/v3/index.json" />
</packageSources>
</configuration>
2 changes: 2 additions & 0 deletions build/Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
<PackageReference Update="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0-preview.1" />
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="15.7.2" />
<PackageReference Update="Microsoft.TestPlatform.TranslationLayer" Version="15.7.2" />
<PackageReference Update="Microsoft.VisualStudio.CodingConventions" Version="1.1.20180503.2" />
<PackageReference Update="Microsoft.VisualStudio.Setup.Configuration.Interop" Version="1.14.114" />
<PackageReference Update="Microsoft.VisualStudio.SDK.EmbedInteropTypes" Version="15.0.12" />

Expand All @@ -68,6 +69,7 @@
<PackageReference Update="System.Collections.Immutable" Version="1.4.0" />
<PackageReference Update="System.ComponentModel.Composition" Version="4.5.0" />
<PackageReference Update="System.Composition" Version="1.0.31" />
<PackageReference Update="System.Reflection.DispatchProxy" Version="4.5.1" />
<PackageReference Update="System.Reflection.Metadata" Version="1.4.2" />
<PackageReference Update="System.Threading.Tasks.Dataflow" Version="4.9.0" />
<PackageReference Update="System.ValueTuple" Version="4.4.0" />
Expand Down
18 changes: 11 additions & 7 deletions src/OmniSharp.Host/WorkspaceInitializer.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Composition.Hosting;
using System.Linq;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
Expand All @@ -22,6 +23,7 @@ public static void Initialize(IServiceProvider serviceProvider, CompositionHost
var workspace = compositionHost.GetExport<OmniSharpWorkspace>();
var options = serviceProvider.GetRequiredService<IOptionsMonitor<OmniSharpOptions>>();
var configuration = serviceProvider.GetRequiredService<IConfigurationRoot>();
var omnisharpEnvironment = serviceProvider.GetRequiredService<IOmniSharpEnvironment>();

var projectEventForwarder = compositionHost.GetExport<ProjectEventForwarder>();
projectEventForwarder.Initialize();
Expand Down Expand Up @@ -50,7 +52,7 @@ public static void Initialize(IServiceProvider serviceProvider, CompositionHost
}
}

ProvideWorkspaceOptions(compositionHost, workspace, options, logger);
ProvideWorkspaceOptions(compositionHost, workspace, options, logger, omnisharpEnvironment);

// Mark the workspace as initialized
workspace.Initialized = true;
Expand All @@ -59,7 +61,7 @@ public static void Initialize(IServiceProvider serviceProvider, CompositionHost
// run workspace options providers automatically
options.OnChange(o =>
{
ProvideWorkspaceOptions(compositionHost, workspace, options, logger);
ProvideWorkspaceOptions(compositionHost, workspace, options, logger, omnisharpEnvironment);
});

logger.LogInformation("Configuration finished.");
Expand All @@ -69,21 +71,23 @@ private static void ProvideWorkspaceOptions(
CompositionHost compositionHost,
OmniSharpWorkspace workspace,
IOptionsMonitor<OmniSharpOptions> options,
ILogger logger)
ILogger logger,
IOmniSharpEnvironment omnisharpEnvironment)
{
// run all workspace options providers
foreach (var workspaceOptionsProvider in compositionHost.GetExports<IWorkspaceOptionsProvider>())
var workspaceOptionsProviders = compositionHost.GetExports<IWorkspaceOptionsProvider>().OrderBy(x => x.Order);
foreach (var workspaceOptionsProvider in workspaceOptionsProviders)
{
var providerName = workspaceOptionsProvider.GetType().FullName;

try
{
LoggerExtensions.LogInformation(logger, $"Invoking Workspace Options Provider: {providerName}");
workspace.Options = workspaceOptionsProvider.Process(workspace.Options, options.CurrentValue);
LoggerExtensions.LogInformation(logger, $"Invoking Workspace Options Provider: {providerName}, Order: {workspaceOptionsProvider.Order}");
workspace.Options = workspaceOptionsProvider.Process(workspace.Options, options.CurrentValue, omnisharpEnvironment);
}
catch (Exception e)
{
var message = $"The workspace options provider '{providerName}' threw exception during initialization.";
var message = $"The workspace options provider '{providerName}' threw exception during execution.";
logger.LogError(e, message);
}
}
Expand Down
1 change: 1 addition & 0 deletions src/OmniSharp.Roslyn.CSharp/OmniSharp.Roslyn.CSharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" />
<PackageReference Include="Microsoft.VisualStudio.CodingConventions" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Composition;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Formatting;
using Microsoft.CodeAnalysis.Options;
Expand All @@ -11,6 +12,8 @@ namespace OmniSharp.Roslyn.CSharp.Services
[Export(typeof(IWorkspaceOptionsProvider)), Shared]
public class CSharpFormattingWorkspaceOptionsProvider : IWorkspaceOptionsProvider
{
public int Order => 0;

private static OptionSet GetOptions(OptionSet optionSet, FormattingOptions formattingOptions)
{
return optionSet
Expand Down Expand Up @@ -93,6 +96,9 @@ private static BinaryOperatorSpacingOptions BinaryOperatorSpacingOptionForString
}
}

public OptionSet Process(OptionSet workOptionSet, OmniSharpOptions omniSharpOptions) => GetOptions(workOptionSet, omniSharpOptions.FormattingOptions);
public OptionSet Process(OptionSet currentOptionSet, OmniSharpOptions options, IOmniSharpEnvironment omnisharpEnvironment)
{
return GetOptions(currentOptionSet, options.FormattingOptions);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System.Composition;
using System.IO;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Options;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.CodingConventions;
using OmniSharp.Options;
using OmniSharp.Roslyn.CSharp.Services.Formatting.EditorConfig;
using OmniSharp.Roslyn.Options;

namespace OmniSharp.Roslyn.CSharp.Services
{
[Export(typeof(IWorkspaceOptionsProvider)), Shared]
public class EditorConfigWorkspaceOptionsProvider : IWorkspaceOptionsProvider
{
private readonly ILoggerFactory _loggerFactory;

public int Order => -100;

[ImportingConstructor]
public EditorConfigWorkspaceOptionsProvider(ILoggerFactory loggerFactory)
{
_loggerFactory = loggerFactory;
}

public OptionSet Process(OptionSet currentOptionSet, OmniSharpOptions omnisharpOptions, IOmniSharpEnvironment omnisharpEnvironment)
{
if (!omnisharpOptions.FormattingOptions.EnableEditorConfigSupport) return currentOptionSet;

// this is a dummy file that doesn't exist, but we simply want to tell .editorconfig to load *.cs specific settings
var filePath = Path.Combine(omnisharpEnvironment.TargetDirectory, "omnisharp.cs");
var changedOptionSet = currentOptionSet.WithEditorConfigOptions(filePath, _loggerFactory).GetAwaiter().GetResult();
return changedOptionSet;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System.Composition;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.Extensions.Logging;
using OmniSharp.Mef;
using OmniSharp.Models.CodeFormat;
using OmniSharp.Options;
using OmniSharp.Roslyn.CSharp.Workers.Formatting;

namespace OmniSharp.Roslyn.CSharp.Services.Formatting
Expand All @@ -11,11 +13,15 @@ namespace OmniSharp.Roslyn.CSharp.Services.Formatting
public class CodeFormatService : IRequestHandler<CodeFormatRequest, CodeFormatResponse>
{
private readonly OmniSharpWorkspace _workspace;
private readonly OmniSharpOptions _omnisharpOptions;
private readonly ILoggerFactory _loggerFactory;

[ImportingConstructor]
public CodeFormatService(OmniSharpWorkspace workspace)
public CodeFormatService(OmniSharpWorkspace workspace, OmniSharpOptions omnisharpOptions, ILoggerFactory loggerFactory)
{
_workspace = workspace;
_omnisharpOptions = omnisharpOptions;
_loggerFactory = loggerFactory;
}

public async Task<CodeFormatResponse> Handle(CodeFormatRequest request)
Expand All @@ -28,14 +34,14 @@ public async Task<CodeFormatResponse> Handle(CodeFormatRequest request)

if (request.WantsTextChanges)
{
var textChanges = await FormattingWorker.GetFormattedTextChanges(document);
var textChanges = await FormattingWorker.GetFormattedTextChanges(document, _omnisharpOptions, _loggerFactory);
return new CodeFormatResponse()
{
Changes = textChanges
};
}

var newText = await FormattingWorker.GetFormattedText(document);
var newText = await FormattingWorker.GetFormattedText(document, _omnisharpOptions, _loggerFactory);

return new CodeFormatResponse
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Options;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.CodingConventions;

namespace OmniSharp.Roslyn.CSharp.Services.Formatting.EditorConfig
{
internal static class EditorConfigOptionExtensions
{
public static async Task<OptionSet> WithEditorConfigOptions(this OptionSet optionSet, string path, ILoggerFactory loggerFactory)
{
if (!Path.IsPathRooted(path))
{
path = Directory.GetCurrentDirectory();
}

var codingConventionsManager = CodingConventionsManagerFactory.CreateCodingConventionsManager();
var optionsApplier = new EditorConfigOptionsApplier(loggerFactory);
var context = await codingConventionsManager.GetConventionContextAsync(path, CancellationToken.None);

if (context != null && context.CurrentConventions != null)
{
return optionsApplier.ApplyConventions(optionSet, context.CurrentConventions, LanguageNames.CSharp);
}


return optionSet;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// adapted from https://github.com/dotnet/format/blob/master/src/Utilities/EditorConfigOptionsApplier.cs
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.Formatting;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Simplification;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.CodingConventions;

namespace OmniSharp.Roslyn.CSharp.Services.Formatting.EditorConfig
{
internal class EditorConfigOptionsApplier
{
private static readonly List<(IOption, OptionStorageLocation, MethodInfo)> _optionsWithStorage;
private readonly ILogger _logger;

static EditorConfigOptionsApplier()
{
_optionsWithStorage = new List<(IOption, OptionStorageLocation, MethodInfo)>();
_optionsWithStorage.AddRange(GetPropertyBasedOptionsWithStorageFromTypes(typeof(FormattingOptions), typeof(CSharpFormattingOptions), typeof(SimplificationOptions)));
_optionsWithStorage.AddRange(GetFieldBasedOptionsWithStorageFromTypes(typeof(CodeStyleOptions), typeof(CSharpFormattingOptions).Assembly.GetType("Microsoft.CodeAnalysis.CSharp.CodeStyle.CSharpCodeStyleOptions")));
}

public EditorConfigOptionsApplier(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<EditorConfigOptionsApplier>();
}

public OptionSet ApplyConventions(OptionSet optionSet, ICodingConventionsSnapshot codingConventions, string languageName)
{
try
{
var adjustedConventions = codingConventions.AllRawConventions.ToDictionary(kvp => kvp.Key, kvp => (string)kvp.Value);
_logger.LogDebug($"All raw discovered .editorconfig options: {string.Join(Environment.NewLine, adjustedConventions.Select(kvp => $"{kvp.Key}={kvp.Value}"))}");

foreach (var optionWithStorage in _optionsWithStorage)
{
if (TryGetConventionValue(optionWithStorage, adjustedConventions, out var value))
{
var option = optionWithStorage.Item1;
_logger.LogTrace($"Applying .editorconfig option {option.Name}");
var optionKey = new OptionKey(option, option.IsPerLanguage ? languageName : null);
optionSet = optionSet.WithChangedOption(optionKey, value);
}
}
}
catch (Exception e)
{
_logger.LogError(e, "There was an error when applying .editorconfig options.");
}

return optionSet;
}

internal static IEnumerable<(IOption, OptionStorageLocation, MethodInfo)> GetPropertyBasedOptionsWithStorageFromTypes(params Type[] types)
=> types
.SelectMany(t => t.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.GetProperty))
.Where(p => typeof(IOption).IsAssignableFrom(p.PropertyType)).Select(p => (IOption)p.GetValue(null))
.Select(GetOptionWithStorage).Where(ows => ows.Item2 != null);

internal static IEnumerable<(IOption, OptionStorageLocation, MethodInfo)> GetFieldBasedOptionsWithStorageFromTypes(params Type[] types)
=> types
.SelectMany(t => t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static))
.Where(p => typeof(IOption).IsAssignableFrom(p.FieldType)).Select(p => (IOption)p.GetValue(null))
.Select(GetOptionWithStorage).Where(ows => ows.Item2 != null);

internal static (IOption, OptionStorageLocation, MethodInfo) GetOptionWithStorage(IOption option)
{
var editorConfigStorage = !option.StorageLocations.IsDefaultOrEmpty
? option.StorageLocations.FirstOrDefault(IsEditorConfigStorage)
: null;

var tryGetOptionMethod = editorConfigStorage?.GetType().GetMethod("TryGetOption");
return (option, editorConfigStorage, tryGetOptionMethod);
}

internal static bool IsEditorConfigStorage(OptionStorageLocation storageLocation)
=> storageLocation.GetType().FullName.StartsWith("Microsoft.CodeAnalysis.Options.EditorConfigStorageLocation") ||
storageLocation.GetType().FullName.StartsWith("Microsoft.CodeAnalysis.Options.NamingStylePreferenceEditorConfigStorageLocation");

internal static bool TryGetConventionValue((IOption, OptionStorageLocation, MethodInfo) optionWithStorage, Dictionary<string, string> adjustedConventions, out object value)
{
var (option, editorConfigStorage, tryGetOptionMethod) = optionWithStorage;
value = null;

var args = new object[] { option, adjustedConventions, option.Type, value };

var isOptionPresent = (bool)tryGetOptionMethod.Invoke(editorConfigStorage, args);
value = args[3];

return isOptionPresent;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using OmniSharp.Mef;
using OmniSharp.Models.Format;
using OmniSharp.Options;
using OmniSharp.Roslyn.CSharp.Workers.Formatting;

namespace OmniSharp.Roslyn.CSharp.Services.Formatting
Expand All @@ -12,11 +15,15 @@ namespace OmniSharp.Roslyn.CSharp.Services.Formatting
public class FormatAfterKeystrokeService : IRequestHandler<FormatAfterKeystrokeRequest, FormatRangeResponse>
{
private readonly OmniSharpWorkspace _workspace;
private readonly OmniSharpOptions _omnisharpOptions;
private readonly ILoggerFactory _loggerFactory;

[ImportingConstructor]
public FormatAfterKeystrokeService(OmniSharpWorkspace workspace)
public FormatAfterKeystrokeService(OmniSharpWorkspace workspace, OmniSharpOptions omnisharpOptions, ILoggerFactory loggerFactory)
{
_workspace = workspace;
_omnisharpOptions = omnisharpOptions;
_loggerFactory = loggerFactory;
}

public async Task<FormatRangeResponse> Handle(FormatAfterKeystrokeRequest request)
Expand All @@ -29,7 +36,7 @@ public async Task<FormatRangeResponse> Handle(FormatAfterKeystrokeRequest reques

var text = await document.GetTextAsync();
int position = text.Lines.GetPosition(new LinePosition(request.Line, request.Column));
var changes = await FormattingWorker.GetFormattingChangesAfterKeystroke(document, position, request.Char);
var changes = await FormattingWorker.GetFormattingChangesAfterKeystroke(document, position, request.Char, _omnisharpOptions, _loggerFactory);

return new FormatRangeResponse()
{
Expand Down
Loading

0 comments on commit 100e73f

Please sign in to comment.