2

Roslyn 会让我将文件添加到 AdhocWorkspace 并在它们上运行格式化程序,但我不知道如何指定格式化选项。我想.editorconfig从命令行传入的文件中派生它们

var adhoc = new AdhocWorkspace();

var solutionInfo = SolutionInfo.Create(SolutionId.CreateNewId(), 
                                       VersionStamp.Default, 
                                       "MySolution");
adhoc.AddSolution(solutionInfo);

var projectInfo = ProjectInfo.Create(ProjectId.CreateNewId(), 
                                     VersionStamp.Default, 
                                     "MyProject", 
                                     "MyProject.dll", 
                                     Microsoft.CodeAnalysis.LanguageNames.CSharp);
adhoc.AddProject(projectInfo);

foreach(var arg in args) {
  var sourceText = SourceText.From(File.OpenText(arg).BaseStream);
  adhoc.AddDocument(projectInfo.Id, arg, sourceText);
}

foreach(var document in adhoc.CurrentSolution.Projects.First().Documents){
  var formattedDocument = Formatter.FormatAsync(document, adhoc.Options).Result;
  var formattedText = formattedDocument.GetTextAsync().Result;
  Console.WriteLine(formattedText);
}

adhoc.Options = adhoc.Options.WithChangedOption(...)将让我更改一个选项,但我希望选项来自.editorconfig文件。有没有 API 可以让我这样做?

4

1 回答 1

2

我找到了这个项目:https ://github.com/dotnet/format

你可以在这里找到代码: https ://github.com/dotnet/format/blob/afc9284d4fb44cb695e17f10d2268944471a5c3b/src/Utilities/EditorConfigOptionsApplier.cs

我了解它是如何工作的,并像这样简化它以解析.editorconfig:

    public static OptionSet SimplifiedParsing(Workspace space)
    {
        OptionSet optionsSet = space.Options;

        //reading .editorconfig
        var editorConfigDictionary = File.ReadAllLines(@".editorconfig")
            //here I take only the ones for csharp
            .Where(x => x.StartsWith("csharp"))
            .Select(x =>  x.Split(" = "))
            .ToDictionary(x => x[0], y => y[1]);

        var commonOptionsType = typeof(FormattingOptions);
        var csharpOptionsType = typeof(CSharpFormattingOptions);
        var formattingTypes = new[] {commonOptionsType, csharpOptionsType};

        var optionType = typeof(IOption);

        //here we are filtering all the methods from formattingTypes classes, with reflection, which parse the options by keys which are in editor config
        var allParsingMethods = formattingTypes
            .SelectMany(t => t.GetProperties(BindingFlags.Public | BindingFlags.Static | BindingFlags.GetProperty))
            .Where(p => optionType.IsAssignableFrom(p.PropertyType))
            .Select(p => (IOption) p.GetValue(null))
            .Select(GetParsingMethod)
            .Where(ows => ows.Item2 != null)
            .ToImmutableArray();

        foreach ((IOption, OptionStorageLocation, MethodInfo) parsingMethod in allParsingMethods)
        {
            //taking info for invoking TryGetOption
            var (option, editorConfigStorage, tryGetOptionMethod) = parsingMethod;

            object result = null;
            //arguments for reflection invocation
            var args = new[] {editorConfigDictionary, option.Type, result};

            //editorConfigStorage instance on which to call the method
            //invoking bool TryGetOption(IReadOnlyDictionary<string, string> rawOptions, Type type, out object result)
            var isOptionPresent = (bool) tryGetOptionMethod.Invoke(editorConfigStorage, args);
            result = args[2];

            if (isOptionPresent)
            {
                var optionKey = new OptionKey(option, option.IsPerLanguage ? LanguageNames.CSharp : null);
                //if option is parsed -> its present and we can add it to OptionSet
                optionsSet = optionsSet.WithChangedOption(optionKey, result);
            }
        }

        return optionsSet;

        //helpers
        (IOption, OptionStorageLocation, MethodInfo) GetParsingMethod(IOption option)
        {
            var editorConfigStorage = option.StorageLocations.IsDefaultOrEmpty
                ? null
                : option.StorageLocations.FirstOrDefault(IsEditorConfigStorage);
            //getting TryGetOption method of EditorConfigStorageLocation<T> 
            // original method signature:
            // public bool TryGetOption(IReadOnlyDictionary<string, string> rawOptions, Type type, out object result)
            var tryGetOptionMethod = editorConfigStorage?.GetType().GetMethod("TryGetOption");
            return (option, editorConfigStorage, tryGetOptionMethod);
        }

        bool IsEditorConfigStorage(OptionStorageLocation storageLocation)
        {
            return storageLocation.GetType().FullName
                .StartsWith("Microsoft.CodeAnalysis.Options.EditorConfigStorageLocation");
        }
    }
于 2020-02-13T16:32:04.700 回答