-
Notifications
You must be signed in to change notification settings - Fork 420
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1526 from filipw/feature/editorconfig
Added support for .editorconfig
- Loading branch information
Showing
28 changed files
with
756 additions
and
79 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
37 changes: 37 additions & 0 deletions
37
src/OmniSharp.Roslyn.CSharp/Services/EditorConfigWorkspaceOptionsProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
src/OmniSharp.Roslyn.CSharp/Services/Formatting/EditorConfig/EditorConfigOptionExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} |
100 changes: 100 additions & 0 deletions
100
src/OmniSharp.Roslyn.CSharp/Services/Formatting/EditorConfig/EditorConfigOptionsApplier.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.