From 95aafd26ad633dad35fa6b17911f86a8980331fe Mon Sep 17 00:00:00 2001 From: Mikayla Hutchinson Date: Fri, 13 Sep 2024 20:17:14 -0400 Subject: [PATCH] Add Roslyn-inspired model for settings/options Nothing uses it yet but it'll be used by MonoDevelop.MSBuild, and at some point used by the XML formatter and XML editor. --- Core/IsExternalInit.cs | 7 +++ Core/Options/IOptionsReader.cs | 9 ++++ Core/Options/OptionReaderExtensions.cs | 15 +++++++ Core/Options/Option`1.cs | 50 ++++++++++++++++++++++ Core/Options/TextFormattingOptionValues.cs | 32 ++++++++++++++ Core/Options/TextFormattingOptions.cs | 46 ++++++++++++++++++++ Core/Options/XmlFormattingOptions.cs | 32 ++++++++++++++ Editor/Options/XmlEditorOptions.cs | 13 ++++++ 8 files changed, 204 insertions(+) create mode 100644 Core/IsExternalInit.cs create mode 100644 Core/Options/IOptionsReader.cs create mode 100644 Core/Options/OptionReaderExtensions.cs create mode 100644 Core/Options/Option`1.cs create mode 100644 Core/Options/TextFormattingOptionValues.cs create mode 100644 Core/Options/TextFormattingOptions.cs create mode 100644 Core/Options/XmlFormattingOptions.cs create mode 100644 Editor/Options/XmlEditorOptions.cs diff --git a/Core/IsExternalInit.cs b/Core/IsExternalInit.cs new file mode 100644 index 00000000..23c4077e --- /dev/null +++ b/Core/IsExternalInit.cs @@ -0,0 +1,7 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.CompilerServices; + +// Declare this to get init properties. See https://github.com/dotnet/roslyn/issues/45510#issuecomment-694977239 +internal static class IsExternalInit { } diff --git a/Core/Options/IOptionsReader.cs b/Core/Options/IOptionsReader.cs new file mode 100644 index 00000000..02e65633 --- /dev/null +++ b/Core/Options/IOptionsReader.cs @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace MonoDevelop.Xml.Options; + +public interface IOptionsReader +{ + bool TryGetOption (Option option, out T value); +} diff --git a/Core/Options/OptionReaderExtensions.cs b/Core/Options/OptionReaderExtensions.cs new file mode 100644 index 00000000..9cb14cd2 --- /dev/null +++ b/Core/Options/OptionReaderExtensions.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace MonoDevelop.Xml.Options; + +public static class OptionReaderExtensions +{ + public static T GetOption(this IOptionsReader options, Option option) + { + if (options.TryGetOption (option, out T value)) { + return value; + } + return option.DefaultValue; + } +} \ No newline at end of file diff --git a/Core/Options/Option`1.cs b/Core/Options/Option`1.cs new file mode 100644 index 00000000..09179676 --- /dev/null +++ b/Core/Options/Option`1.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +namespace MonoDevelop.Xml.Options; + +/// +/// Defines an option that may affect formatter, editor, analyzer or code fix behavior. +/// Some of these are read from .editorconfig, and others may be mapped to equivalent settings +/// of the host IDE. +/// +public class Option +{ + public Option(string name, T defaultValue, bool isEditorConfigOption) + { + Name = name; + DefaultValue = defaultValue; + IsEditorConfigOption = isEditorConfigOption; + } + + public Option(string name, T value, EditorConfigSerializer? serializer = null) : this(name, value, true) + { + Serializer = serializer; + } + + /// + /// A unique name for the option. If this is an editorconfig option, this will be used as the name + /// in .editorconfig. + /// + public string Name { get; } + + /// + /// The value to use for this option when no setting is found in EditorConfig or + /// in the host. + /// + public T DefaultValue { get; } + + /// + /// Whether this option will be read from .editorconfig. + /// + public bool IsEditorConfigOption { get; } + + /// + /// Optionally override the EditorConfig serialization behavior + /// + public EditorConfigSerializer? Serializer { get; } +} + +public record EditorConfigSerializer (Func Deserialize, Func Serialize); diff --git a/Core/Options/TextFormattingOptionValues.cs b/Core/Options/TextFormattingOptionValues.cs new file mode 100644 index 00000000..48d5b3e7 --- /dev/null +++ b/Core/Options/TextFormattingOptionValues.cs @@ -0,0 +1,32 @@ +// 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; + +namespace MonoDevelop.Xml.Options; + +// based on https://github.com/dotnet/roslyn/blob/df4ae6b81013ac45367372176b9c3135a35a7e3c/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/LineFormattingOptions.cs +/// +/// Captures common text formatting options values from an +/// so that they may be accessed more efficiently. +/// +public sealed record class TextFormattingOptionValues () +{ + public readonly TextFormattingOptionValues Default = new (); + + public bool UseTabs { get; init; } = false; + public int TabSize { get; init; } = 4; + public int IndentSize { get; init; } = 4; + public string NewLine { get; init; } = Environment.NewLine; + public bool TrimTrailingWhitespace { get; init; } = false; + + public TextFormattingOptionValues (IOptionsReader options) + : this () + { + UseTabs = options.GetOption (TextFormattingOptions.UseTabs); + TabSize = options.GetOption (TextFormattingOptions.TabSize); + IndentSize = options.GetOption (TextFormattingOptions.IndentSize); + NewLine = options.GetOption (TextFormattingOptions.NewLine); + } +} \ No newline at end of file diff --git a/Core/Options/TextFormattingOptions.cs b/Core/Options/TextFormattingOptions.cs new file mode 100644 index 00000000..bcfa5275 --- /dev/null +++ b/Core/Options/TextFormattingOptions.cs @@ -0,0 +1,46 @@ +// 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; + +namespace MonoDevelop.Xml.Options; + +// based on https://github.com/dotnet/roslyn/blob/199c241cef61d94e25fcfd0f6bcaa91faa35d515/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/FormattingOptions2.cs#L23 +/// +/// Options that control text formatting. Accessing these multiple times may be done more efficiently using . +/// +public class TextFormattingOptions +{ + public static readonly Option UseTabs = new ("indent_style", false, + new EditorConfigSerializer (str => str == "tab", value => value ? "tab" : "space") + ); + + public static readonly Option TabSize = new ("tab_size", 4, true); + + public static readonly Option IndentSize = new ("indent_size", 4, true); + + public static readonly Option NewLine = new ("end_of_line", Environment.NewLine, new EditorConfigSerializer ( + str => str switch { + "lf" => "\n", + "cr" => "\r", + "crlf" => "\r\n", + _ => Environment.NewLine + }, + value => value switch { + "\n" => "lf", + "\r" => "cr", + "\r\n" => "crlf", + _ => "unset" + })); + + + public static readonly Option InsertFinalNewline = new ("insert_final_newline", true, true); + + public static readonly Option TrimTrailingWhitespace = new ("trim_trailing_whitespace", true, true); + + public static readonly Option MaxLineLength = new ("max_line_length", null, new EditorConfigSerializer ( + str => str != "off" && int.TryParse (str, out var val) && val > 0 ? val : null, + val => val.HasValue && val.Value > 0 ? val.Value.ToString () : "off" + )); +} diff --git a/Core/Options/XmlFormattingOptions.cs b/Core/Options/XmlFormattingOptions.cs new file mode 100644 index 00000000..2c6df962 --- /dev/null +++ b/Core/Options/XmlFormattingOptions.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace MonoDevelop.Xml.Options; + +/// +/// Options that control XML formatting +/// +public static class XmlFormattingOptions +{ + public static readonly Option OmitXmlDeclaration = new ("xml_omit_declaration", false, true); + public static readonly Option IndentContent = new ("xml_indent_content", true, true); + + public static readonly Option AttributesOnNewLine = new ("xml_attributes_on_new_line", false, true); + public static readonly Option MaxAttributesPerLine = new ("xml_max_attributes_per_line", 10, true); + + public static readonly Option AlignAttributes = new ("xml_align_attributes", false, true); + public static readonly Option AlignAttributeValues = new ("xml_align_attribute_values", false, true); + public static readonly Option WrapAttributes = new ("xml_wrap_attributes", false, true); + public static readonly Option SpacesBeforeAssignment = new ("xml_spaces_before_assignment", 0, true); + public static readonly Option SpacesAfterAssignment = new ("xml_spaces_after_assignment", 0, true); + + public static readonly Option QuoteChar = new ("xml_quote_style", '"', new EditorConfigSerializer ( + str => str == "single" ? '\'' : '"', + val => val == '\'' ? "single" : "double" + )); + + public static readonly Option EmptyLinesBeforeStart = new ("xml_empty_lines_before_start", 0, true); + public static readonly Option EmptyLinesAfterStart = new ("xml_empty_lines_after_start", 0, true); + public static readonly Option EmptyLinesBeforeEnd = new ("xml_empty_lines_before_end", 0, true); + public static readonly Option EmptyLinesAfterEnd = new ("xml_empty_lines_after_end", 0, true); +} \ No newline at end of file diff --git a/Editor/Options/XmlEditorOptions.cs b/Editor/Options/XmlEditorOptions.cs new file mode 100644 index 00000000..b603694a --- /dev/null +++ b/Editor/Options/XmlEditorOptions.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace MonoDevelop.Xml.Options; + +/// +/// Options that control the behavior of the XML editor +/// +public static class XmlEditorOptions +{ + public static Option AutoInsertClosingTag = new ("xml_auto_insert_closing_tag", true, false); + public static Option AutoInsertAttributeValue = new ("xml_auto_insert_attribute_value", true, false); +}