Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move Options and CodeStyle APIs to shared layer #42323

Merged
59 commits merged into from
Mar 24, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
6692e76
Move common Options and CodeStyle related public and internal types i…
mavasani Mar 10, 2020
cd860f7
Add API overloads for new shared layer options types to options servi…
mavasani Mar 10, 2020
a2c78bd
Make similar changes to FormattingOptions and C# FormattingOptions - …
mavasani Mar 10, 2020
762c09c
Some more follow-up changes in CodeStyle related files
mavasani Mar 10, 2020
2848a31
Updating formatting rules and engine to use the shared layer formatti…
mavasani Mar 10, 2020
70b4089
VB CodeStyle related changes. We still haven't moved VB CodeStyle opt…
mavasani Mar 10, 2020
3ba9025
Move bunch of options in Workspaces and Features layers to use the ne…
mavasani Mar 10, 2020
7077431
Move the base CodeStyle and naming analyzer to the new options relate…
mavasani Mar 10, 2020
30b4e99
Some cleanup
mavasani Mar 10, 2020
e96fd8f
VB formatting engine and rules moved to new formatting options API in…
mavasani Mar 10, 2020
e182acd
Move all the shared layer analyzers to use the new options and code s…
mavasani Mar 10, 2020
2143656
Move all the IDE (Features layer) analyzer to the new options and cod…
mavasani Mar 10, 2020
a9da22c
Add BannedSymbols.txt for Features layer to prevent accidental use of…
mavasani Mar 10, 2020
b9cf84d
Changes to CodeStyle layer to delete linked files and trim public APIs
mavasani Mar 10, 2020
00b3593
Move EditorFeatures layer to new options types
mavasani Mar 10, 2020
6e1e5dd
Move VS layer and some of the options it depends on to the new intern…
mavasani Mar 10, 2020
74b90a8
Move part of EditorConfigFileGenerator that can serialize naming opti…
mavasani Mar 10, 2020
f1b4348
Split the CodeStyle test utilities project into separate projects for…
mavasani Mar 10, 2020
cbead33
Fix some build and test failures
mavasani Mar 10, 2020
e4c7e8e
Cleanup the conditional directives in analyzer/fixer tests in shared …
mavasani Mar 10, 2020
7e3c47e
Tons of test changes to move them to the new Options APIs.
mavasani Mar 10, 2020
c765e1f
Commit for all project file, resx moves and xlf file changes.
mavasani Mar 10, 2020
11d03e1
Fix nullable warning
mavasani Mar 11, 2020
80a215d
Merge remote-tracking branch 'upstream/master' into OptionsRefactor
mavasani Mar 11, 2020
d6bc6f6
Merge remote-tracking branch 'upstream/master' into OptionsRefactor
mavasani Mar 16, 2020
193ac53
Use OptionDefinition to ensure options are equtable - this is needed …
mavasani Mar 17, 2020
8b547f4
Merge remote-tracking branch 'upstream/master' into OptionsRefactor
mavasani Mar 17, 2020
b704f66
Fix merge conflict
mavasani Mar 17, 2020
c45db4c
Override equality operators
mavasani Mar 17, 2020
fb10c5e
Retain the exception checks from public APIs - these are guarded by u…
mavasani Mar 17, 2020
04056c0
Switch to explicit conversion operators between option types
mavasani Mar 18, 2020
de4d5d9
Merge remote-tracking branch 'upstream/master' into OptionsRefactor
mavasani Mar 18, 2020
7301aa3
Fix merge conflicts
mavasani Mar 18, 2020
0ff090f
Add inheritdoc documentation comments for public Options API. I will …
mavasani Mar 18, 2020
f47f6d1
Fix test failures caused by prior commit
mavasani Mar 18, 2020
50287de
Switch back to implicit operators for conversions to and from CodeSty…
mavasani Mar 18, 2020
4985a18
Address feedback
mavasani Mar 19, 2020
0066c5c
Merge remote-tracking branch 'upstream/master' into OptionsRefactor
mavasani Mar 19, 2020
f52f4fe
Revert unintentional commit
mavasani Mar 19, 2020
736ba89
Address more feedback
mavasani Mar 19, 2020
0f22449
Merge remote-tracking branch 'upstream/master' into OptionsRefactor
mavasani Mar 19, 2020
9793b0b
Fix build break from the refactoring in the prior commit
mavasani Mar 19, 2020
6192d8e
Revert changes to PythiaOptions in ExternalAccessLayer
mavasani Mar 19, 2020
759dc07
Fix RS0038 (use null instead of default) in test code
mavasani Mar 19, 2020
e8464de
Merge remote-tracking branch 'upstream/master' into OptionsRefactor
mavasani Mar 19, 2020
8961e3b
Revert API changes to options used by internal partners through IVT
mavasani Mar 19, 2020
d02be0c
Revert changes to EditorComponentOnOffOptions - these are actually no…
mavasani Mar 20, 2020
02f40aa
Add back cref to PreferBracesPreference.WhenMultiline
mavasani Mar 20, 2020
a56b5a7
Enable nullable reference types for operators
sharwell Mar 20, 2020
717c508
Unwrap CodeStyleOption<T> without allocating
sharwell Mar 20, 2020
17ce912
Improve accuracy of TestHostAnalyzers
sharwell Mar 20, 2020
c533f82
Use explicit operators, since they are no longer used implicitly
sharwell Mar 20, 2020
6878054
Ensure that we use "CodeStyleOption" as the serialization element nam…
mavasani Mar 20, 2020
f139867
Address feedback
mavasani Mar 20, 2020
6aa9798
Add matrix of tests for Get/Set option APIs on OptionSet and OptionSe…
mavasani Mar 20, 2020
81ff9eb
Address feedback
mavasani Mar 20, 2020
17fbad5
Merge remote-tracking branch 'upstream/master' into OptionsRefactor
mavasani Mar 20, 2020
4c01f51
Merge remote-tracking branch 'upstream/master' into OptionsRefactor
mavasani Mar 23, 2020
950b3b6
Fix conflicts
mavasani Mar 23, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 20 additions & 158 deletions src/Workspaces/Core/Portable/CodeStyle/CodeStyleOption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,194 +3,56 @@
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Xml.Linq;
using Microsoft.CodeAnalysis.Diagnostics;

#if CODE_STYLE
namespace Microsoft.CodeAnalysis.Internal.Options
#else
namespace Microsoft.CodeAnalysis.CodeStyle
#endif
{
internal interface ICodeStyleOption
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to shared layer

{
XElement ToXElement();
object Value { get; }
NotificationOption Notification { get; }
ICodeStyleOption WithValue(object value);
ICodeStyleOption WithNotification(NotificationOption notification);
}

/// <summary>
/// Represents a code style option and an associated notification option. Supports
/// being instantiated with T as a <see cref="bool"/> or an <c>enum type</c>.
///
/// CodeStyleOption also has some basic support for migration a <see cref="bool"/> option
/// forward to an <c>enum type</c> option. Specifically, if a previously serialized
/// bool-CodeStyleOption is then deserialized into an enum-CodeStyleOption then 'false'
/// values will be migrated to have the 0-value of the enum, and 'true' values will be
/// migrated to have the 1-value of the enum.
///
/// Similarly, enum-type code options will serialize out in a way that is compatible with
/// hosts that expect the value to be a boolean. Specifically, if the enum value is 0 or 1
/// then those values will write back as false/true.
/// </summary>
/// <inheritdoc cref="CodeStyleOption2{T}"/>
public class CodeStyleOption<T> : ICodeStyleOption, IEquatable<CodeStyleOption<T>>
{
private readonly CodeStyleOption2<T> _codeStyleOptionImpl;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the existing code from this type was copied directly into CodeStyleOption2<T> being added in shared layer. This public type now just has a handle to the instance of CodeStyleOption2<T> that has all the implementation. Very similar approach has been used for rest of the public types in this PR.

public static CodeStyleOption<T> Default => new CodeStyleOption<T>(default, NotificationOption.Silent);

private const int SerializationVersion = 1;

private NotificationOption _notification;
internal CodeStyleOption(CodeStyleOption2<T> codeStyleOptionImpl)
{
_codeStyleOptionImpl = codeStyleOptionImpl;
}

public CodeStyleOption(T value, NotificationOption notification)
: this(new CodeStyleOption2<T>(value, notification ?? throw new ArgumentNullException(nameof(notification))))
mavasani marked this conversation as resolved.
Show resolved Hide resolved
{
Value = value;
_notification = notification ?? throw new ArgumentNullException(nameof(notification));
}

public T Value { get; set; }
public T Value
{
get => _codeStyleOptionImpl.Value;
set => _codeStyleOptionImpl.Value = value;
}

object ICodeStyleOption.Value => this.Value;
NotificationOption2 ICodeStyleOption.Notification => _codeStyleOptionImpl.Notification;
ICodeStyleOption ICodeStyleOption.WithValue(object value) => new CodeStyleOption<T>((T)value, Notification);
ICodeStyleOption ICodeStyleOption.WithNotification(NotificationOption notification) => new CodeStyleOption<T>(Value, notification);

private int EnumValueAsInt32 => (int)(object)Value;
ICodeStyleOption ICodeStyleOption.WithNotification(NotificationOption2 notification) => new CodeStyleOption<T>(Value, notification);

public NotificationOption Notification
{
get => _notification;
set => _notification = value ?? throw new ArgumentNullException(nameof(value));
get => _codeStyleOptionImpl.Notification;
set => _codeStyleOptionImpl.Notification = value ?? throw new ArgumentNullException(nameof(value));
mavasani marked this conversation as resolved.
Show resolved Hide resolved
}

public XElement ToXElement() =>
new XElement(nameof(CodeStyleOption<T>), // `nameof()` returns just "CodeStyleOption"
new XAttribute(nameof(SerializationVersion), SerializationVersion),
new XAttribute("Type", GetTypeNameForSerialization()),
new XAttribute(nameof(Value), GetValueForSerialization()),
new XAttribute(nameof(DiagnosticSeverity), Notification.Severity.ToDiagnosticSeverity() ?? DiagnosticSeverity.Hidden));

private object GetValueForSerialization()
{
if (typeof(T) == typeof(string))
{
return Value;
}
else if (typeof(T) == typeof(bool))
{
return Value;
}
else if (IsZeroOrOneValueOfEnum())
{
return EnumValueAsInt32 == 1;
}
else
{
return EnumValueAsInt32;
}
}

private string GetTypeNameForSerialization()
{
if (typeof(T) == typeof(string))
{
return nameof(String);
}
if (typeof(T) == typeof(bool) || IsZeroOrOneValueOfEnum())
{
return nameof(Boolean);
}
else
{
return nameof(Int32);
}
}

private bool IsZeroOrOneValueOfEnum()
{
var intVal = EnumValueAsInt32;
return intVal == 0 || intVal == 1;
}
public XElement ToXElement() => _codeStyleOptionImpl.ToXElement();

public static CodeStyleOption<T> FromXElement(XElement element)
{
var typeAttribute = element.Attribute("Type");
var valueAttribute = element.Attribute(nameof(Value));
var severityAttribute = element.Attribute(nameof(DiagnosticSeverity));
var version = (int)element.Attribute(nameof(SerializationVersion));

if (typeAttribute == null || valueAttribute == null || severityAttribute == null)
{
// data from storage is corrupt, or nothing has been stored yet.
return Default;
}

if (version != SerializationVersion)
{
return Default;
}

var parser = GetParser(typeAttribute.Value);
var value = parser(valueAttribute.Value);
var severity = (DiagnosticSeverity)Enum.Parse(typeof(DiagnosticSeverity), severityAttribute.Value);

return new CodeStyleOption<T>(value, severity switch
{
DiagnosticSeverity.Hidden => NotificationOption.Silent,
DiagnosticSeverity.Info => NotificationOption.Suggestion,
DiagnosticSeverity.Warning => NotificationOption.Warning,
DiagnosticSeverity.Error => NotificationOption.Error,
_ => throw new ArgumentException(nameof(element)),
});
}

private static Func<string, T> GetParser(string type)
=> type switch
{
nameof(Boolean) =>
// Try to map a boolean value. Either map it to true/false if we're a
// CodeStyleOption<bool> or map it to the 0 or 1 value for an enum if we're
// a CodeStyleOption<SomeEnumType>.
(Func<string, T>)(v => Convert(bool.Parse(v))),
nameof(Int32) => v => Convert(int.Parse(v)),
nameof(String) => v => (T)(object)v,
_ => throw new ArgumentException(nameof(type)),
};

private static T Convert(bool b)
{
// If we had a bool and we wanted a bool, then just return this value.
if (typeof(T) == typeof(bool))
{
return (T)(object)b;
}

// Map booleans to the 1/0 value of the enum.
return b ? (T)(object)1 : (T)(object)0;
}

private static T Convert(int i)
{
// We got an int, but we wanted a bool. Map 0 to false, 1 to true, and anything else to default.
if (typeof(T) == typeof(bool))
{
return (T)(object)(i == 1);
}

// If had an int and we wanted an enum, then just return this value.
return (T)(object)(i);
}
=> new CodeStyleOption<T>(CodeStyleOption2<T>.FromXElement(element));

public bool Equals(CodeStyleOption<T> other)
=> EqualityComparer<T>.Default.Equals(Value, other.Value) &&
Notification == other.Notification;
=> _codeStyleOptionImpl.Equals(other?._codeStyleOptionImpl);

public override bool Equals(object obj)
=> obj is CodeStyleOption<T> option &&
Equals(option);

public override int GetHashCode()
=> unchecked((Notification.GetHashCode() * (int)0xA5555529) + Value.GetHashCode());
=> _codeStyleOptionImpl.GetHashCode();
}
}
Loading