Skip to content

Commit

Permalink
Add Roslyn-inspired model for settings/options
Browse files Browse the repository at this point in the history
Nothing uses it yet but it'll be used by MonoDevelop.MSBuild,
and at some point used by the XML formatter and XML editor.
  • Loading branch information
mhutch committed Sep 14, 2024
1 parent e5c608d commit 95aafd2
Show file tree
Hide file tree
Showing 8 changed files with 204 additions and 0 deletions.
7 changes: 7 additions & 0 deletions Core/IsExternalInit.cs
Original file line number Diff line number Diff line change
@@ -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 { }
9 changes: 9 additions & 0 deletions Core/Options/IOptionsReader.cs
Original file line number Diff line number Diff line change
@@ -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<T> (Option<T> option, out T value);
}
15 changes: 15 additions & 0 deletions Core/Options/OptionReaderExtensions.cs
Original file line number Diff line number Diff line change
@@ -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<T>(this IOptionsReader options, Option<T> option)
{
if (options.TryGetOption<T> (option, out T value)) {
return value;
}
return option.DefaultValue;
}
}
50 changes: 50 additions & 0 deletions Core/Options/Option`1.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// 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.
/// </summary>
public class Option<T>
{
public Option(string name, T defaultValue, bool isEditorConfigOption)
{
Name = name;
DefaultValue = defaultValue;
IsEditorConfigOption = isEditorConfigOption;
}

public Option(string name, T value, EditorConfigSerializer<T>? serializer = null) : this(name, value, true)
{
Serializer = serializer;
}

/// <summary>
/// A unique name for the option. If this is an editorconfig option, this will be used as the name
/// in .editorconfig.
/// </summary>
public string Name { get; }

/// <summary>
/// The value to use for this option when no setting is found in EditorConfig or
/// in the host.
/// </summary>
public T DefaultValue { get; }

/// <summary>
/// Whether this option will be read from .editorconfig.
/// </summary>
public bool IsEditorConfigOption { get; }

/// <summary>
/// Optionally override the EditorConfig serialization behavior
/// </summary>
public EditorConfigSerializer<T>? Serializer { get; }
}

public record EditorConfigSerializer<T> (Func<string, T> Deserialize, Func<T, string> Serialize);
32 changes: 32 additions & 0 deletions Core/Options/TextFormattingOptionValues.cs
Original file line number Diff line number Diff line change
@@ -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
/// <summary>
/// Captures common text formatting options values from an <see cref="IOptionsReader"/>
/// so that they may be accessed more efficiently.
/// </summary>
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);
}
}
46 changes: 46 additions & 0 deletions Core/Options/TextFormattingOptions.cs
Original file line number Diff line number Diff line change
@@ -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
/// <summary>
/// Options that control text formatting. Accessing these multiple times may be done more efficiently using <see cref="TextFormattingOptionValues"/>.
/// </summary>
public class TextFormattingOptions
{
public static readonly Option<bool> UseTabs = new ("indent_style", false,
new EditorConfigSerializer<bool> (str => str == "tab", value => value ? "tab" : "space")
);

public static readonly Option<int> TabSize = new ("tab_size", 4, true);

public static readonly Option<int> IndentSize = new ("indent_size", 4, true);

public static readonly Option<string> NewLine = new ("end_of_line", Environment.NewLine, new EditorConfigSerializer<string> (
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<bool> InsertFinalNewline = new ("insert_final_newline", true, true);

public static readonly Option<bool> TrimTrailingWhitespace = new ("trim_trailing_whitespace", true, true);

public static readonly Option<int?> MaxLineLength = new ("max_line_length", null, new EditorConfigSerializer<int?> (
str => str != "off" && int.TryParse (str, out var val) && val > 0 ? val : null,
val => val.HasValue && val.Value > 0 ? val.Value.ToString () : "off"
));
}
32 changes: 32 additions & 0 deletions Core/Options/XmlFormattingOptions.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// Options that control XML formatting
/// </summary>
public static class XmlFormattingOptions
{
public static readonly Option<bool> OmitXmlDeclaration = new ("xml_omit_declaration", false, true);
public static readonly Option<bool> IndentContent = new ("xml_indent_content", true, true);

public static readonly Option<bool> AttributesOnNewLine = new ("xml_attributes_on_new_line", false, true);
public static readonly Option<int> MaxAttributesPerLine = new ("xml_max_attributes_per_line", 10, true);

public static readonly Option<bool> AlignAttributes = new ("xml_align_attributes", false, true);
public static readonly Option<bool> AlignAttributeValues = new ("xml_align_attribute_values", false, true);
public static readonly Option<bool> WrapAttributes = new ("xml_wrap_attributes", false, true);
public static readonly Option<int> SpacesBeforeAssignment = new ("xml_spaces_before_assignment", 0, true);
public static readonly Option<int> SpacesAfterAssignment = new ("xml_spaces_after_assignment", 0, true);

public static readonly Option<char> QuoteChar = new ("xml_quote_style", '"', new EditorConfigSerializer<char> (
str => str == "single" ? '\'' : '"',
val => val == '\'' ? "single" : "double"
));

public static readonly Option<int> EmptyLinesBeforeStart = new ("xml_empty_lines_before_start", 0, true);
public static readonly Option<int> EmptyLinesAfterStart = new ("xml_empty_lines_after_start", 0, true);
public static readonly Option<int> EmptyLinesBeforeEnd = new ("xml_empty_lines_before_end", 0, true);
public static readonly Option<int> EmptyLinesAfterEnd = new ("xml_empty_lines_after_end", 0, true);
}
13 changes: 13 additions & 0 deletions Editor/Options/XmlEditorOptions.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// Options that control the behavior of the XML editor
/// </summary>
public static class XmlEditorOptions
{
public static Option<bool> AutoInsertClosingTag = new ("xml_auto_insert_closing_tag", true, false);
public static Option<bool> AutoInsertAttributeValue = new ("xml_auto_insert_attribute_value", true, false);
}

0 comments on commit 95aafd2

Please sign in to comment.