提交 291ce3a8 编写于 作者: C CyrusNajmabadi

Add a little bit of type-safety to editor-config storage locations.

上级 014308ab
......@@ -67,6 +67,28 @@ public void TestOptionSerialization2()
Assert.Equal(default(bool), deserialized.Value);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)]
public void TestOptionEditorConfig1()
{
Assert.Null(CSharpCodeStyleOptions.ParseExpressionBodyPreference("true", null));
Assert.Null(CSharpCodeStyleOptions.ParseExpressionBodyPreference("false", null));
Assert.Null(CSharpCodeStyleOptions.ParseExpressionBodyPreference("when_on_single_line", null));
Assert.Null(CSharpCodeStyleOptions.ParseExpressionBodyPreference("true:blah", null));
Assert.Null(CSharpCodeStyleOptions.ParseExpressionBodyPreference("when_blah:error", null));
var option = CSharpCodeStyleOptions.ParseExpressionBodyPreference("false:error", null);
Assert.Equal(ExpressionBodyPreference.Never, option.Value);
Assert.Equal(NotificationOption.Error, option.Notification);
option = CSharpCodeStyleOptions.ParseExpressionBodyPreference("true:warning", null);
Assert.Equal(ExpressionBodyPreference.WhenPossible, option.Value);
Assert.Equal(NotificationOption.Warning, option.Notification);
option = CSharpCodeStyleOptions.ParseExpressionBodyPreference("when_on_single_line:suggestion", null);
Assert.Equal(ExpressionBodyPreference.WhenOnSingleLine, option.Value);
Assert.Equal(NotificationOption.Suggestion, option.Notification);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)]
public async Task TestUseExpressionBody1()
{
......
......@@ -22,7 +22,7 @@ public DocumentOptions(ICodingConventionsSnapshot codingConventionSnapshot, IErr
public bool TryGetDocumentOption(Document document, OptionKey option, OptionSet underlyingOptions, out object value)
{
var editorConfigPersistence = option.Option.StorageLocations.OfType<EditorConfigStorageLocation>().SingleOrDefault();
var editorConfigPersistence = option.Option.StorageLocations.OfType<IEditorConfigStorageLocation>().SingleOrDefault();
if (editorConfigPersistence == null)
{
value = null;
......
......@@ -11,37 +11,37 @@ internal static class CSharpCodeStyleOptions
public static readonly Option<CodeStyleOption<bool>> UseImplicitTypeForIntrinsicTypes = new Option<CodeStyleOption<bool>>(
nameof(CodeStyleOptions), nameof(UseImplicitTypeForIntrinsicTypes), defaultValue: CodeStyleOption<bool>.Default,
storageLocations: new OptionStorageLocation[] {
new EditorConfigStorageLocation("csharp_style_var_for_built_in_types"),
EditorConfigStorageLocation.ForBoolCodeStyleOption("csharp_style_var_for_built_in_types"),
new RoamingProfileStorageLocation("TextEditor.CSharp.Specific.UseImplicitTypeForIntrinsicTypes")});
public static readonly Option<CodeStyleOption<bool>> UseImplicitTypeWhereApparent = new Option<CodeStyleOption<bool>>(
nameof(CodeStyleOptions), nameof(UseImplicitTypeWhereApparent), defaultValue: CodeStyleOption<bool>.Default,
storageLocations: new OptionStorageLocation[] {
new EditorConfigStorageLocation("csharp_style_var_when_type_is_apparent"),
EditorConfigStorageLocation.ForBoolCodeStyleOption("csharp_style_var_when_type_is_apparent"),
new RoamingProfileStorageLocation("TextEditor.CSharp.Specific.UseImplicitTypeWhereApparent")});
public static readonly Option<CodeStyleOption<bool>> UseImplicitTypeWherePossible = new Option<CodeStyleOption<bool>>(
nameof(CodeStyleOptions), nameof(UseImplicitTypeWherePossible), defaultValue: CodeStyleOption<bool>.Default,
storageLocations: new OptionStorageLocation[] {
new EditorConfigStorageLocation("csharp_style_var_elsewhere"),
EditorConfigStorageLocation.ForBoolCodeStyleOption("csharp_style_var_elsewhere"),
new RoamingProfileStorageLocation("TextEditor.CSharp.Specific.UseImplicitTypeWherePossible")});
public static readonly Option<CodeStyleOption<bool>> PreferConditionalDelegateCall = new Option<CodeStyleOption<bool>>(
nameof(CodeStyleOptions), nameof(PreferConditionalDelegateCall), defaultValue: CodeStyleOptions.TrueWithSuggestionEnforcement,
storageLocations: new OptionStorageLocation[] {
new EditorConfigStorageLocation("csharp_style_conditional_delegate_call"),
EditorConfigStorageLocation.ForBoolCodeStyleOption("csharp_style_conditional_delegate_call"),
new RoamingProfileStorageLocation("TextEditor.CSharp.Specific.PreferConditionalDelegateCall")});
public static readonly Option<CodeStyleOption<bool>> PreferPatternMatchingOverAsWithNullCheck = new Option<CodeStyleOption<bool>>(
nameof(CodeStyleOptions), nameof(PreferPatternMatchingOverAsWithNullCheck), defaultValue: CodeStyleOptions.TrueWithSuggestionEnforcement,
storageLocations: new OptionStorageLocation[] {
new EditorConfigStorageLocation("csharp_style_pattern_matching_over_as_with_null_check"),
EditorConfigStorageLocation.ForBoolCodeStyleOption("csharp_style_pattern_matching_over_as_with_null_check"),
new RoamingProfileStorageLocation($"TextEditor.CSharp.Specific.{nameof(PreferPatternMatchingOverAsWithNullCheck)}")});
public static readonly Option<CodeStyleOption<bool>> PreferPatternMatchingOverIsWithCastCheck = new Option<CodeStyleOption<bool>>(
nameof(CodeStyleOptions), nameof(PreferPatternMatchingOverIsWithCastCheck), defaultValue: CodeStyleOptions.TrueWithSuggestionEnforcement,
storageLocations: new OptionStorageLocation[] {
new EditorConfigStorageLocation("csharp_style_pattern_matching_over_is_with_cast_check"),
EditorConfigStorageLocation.ForBoolCodeStyleOption("csharp_style_pattern_matching_over_is_with_cast_check"),
new RoamingProfileStorageLocation($"TextEditor.CSharp.Specific.{nameof(PreferPatternMatchingOverIsWithCastCheck)}")});
public static readonly CodeStyleOption<ExpressionBodyPreference> NeverWithNoneEnforcement =
......@@ -63,54 +63,66 @@ internal static class CSharpCodeStyleOptions
nameof(CodeStyleOptions), nameof(PreferExpressionBodiedConstructors),
defaultValue: NeverWithNoneEnforcement,
storageLocations: new OptionStorageLocation[] {
new EditorConfigStorageLocation("csharp_style_expression_bodied_constructors", s => ParseExpressionBodyPreference(s, ExpressionBodyPreference.Never)),
new EditorConfigStorageLocation<CodeStyleOption<ExpressionBodyPreference>>("csharp_style_expression_bodied_constructors", s => ParseExpressionBodyPreference(s, NeverWithNoneEnforcement)),
new RoamingProfileStorageLocation($"TextEditor.CSharp.Specific.{nameof(PreferExpressionBodiedConstructors)}")});
public static readonly Option<CodeStyleOption<ExpressionBodyPreference>> PreferExpressionBodiedMethods = new Option<CodeStyleOption<ExpressionBodyPreference>>(
nameof(CodeStyleOptions), nameof(PreferExpressionBodiedMethods),
defaultValue: NeverWithNoneEnforcement,
storageLocations: new OptionStorageLocation[] {
new EditorConfigStorageLocation("csharp_style_expression_bodied_methods", s => ParseExpressionBodyPreference(s, ExpressionBodyPreference.Never)),
new EditorConfigStorageLocation<CodeStyleOption<ExpressionBodyPreference>>("csharp_style_expression_bodied_methods", s => ParseExpressionBodyPreference(s, NeverWithNoneEnforcement)),
new RoamingProfileStorageLocation($"TextEditor.CSharp.Specific.{nameof(PreferExpressionBodiedMethods)}")});
public static readonly Option<CodeStyleOption<ExpressionBodyPreference>> PreferExpressionBodiedOperators = new Option<CodeStyleOption<ExpressionBodyPreference>>(
nameof(CodeStyleOptions), nameof(PreferExpressionBodiedOperators),
defaultValue: NeverWithNoneEnforcement,
storageLocations: new OptionStorageLocation[] {
new EditorConfigStorageLocation("csharp_style_expression_bodied_operators", s => ParseExpressionBodyPreference(s, ExpressionBodyPreference.Never)),
new EditorConfigStorageLocation<CodeStyleOption<ExpressionBodyPreference>>("csharp_style_expression_bodied_operators", s => ParseExpressionBodyPreference(s, NeverWithNoneEnforcement)),
new RoamingProfileStorageLocation($"TextEditor.CSharp.Specific.{nameof(PreferExpressionBodiedOperators)}")});
public static readonly Option<CodeStyleOption<ExpressionBodyPreference>> PreferExpressionBodiedProperties = new Option<CodeStyleOption<ExpressionBodyPreference>>(
nameof(CodeStyleOptions), nameof(PreferExpressionBodiedProperties),
defaultValue: WhenPossibleWithNoneEnforcement,
storageLocations: new OptionStorageLocation[] {
new EditorConfigStorageLocation("csharp_style_expression_bodied_properties", s => ParseExpressionBodyPreference(s, ExpressionBodyPreference.WhenOnSingleLine)),
new EditorConfigStorageLocation<CodeStyleOption<ExpressionBodyPreference>>("csharp_style_expression_bodied_properties", s => ParseExpressionBodyPreference(s, WhenPossibleWithNoneEnforcement)),
new RoamingProfileStorageLocation($"TextEditor.CSharp.Specific.{nameof(PreferExpressionBodiedProperties)}")});
public static readonly Option<CodeStyleOption<ExpressionBodyPreference>> PreferExpressionBodiedIndexers = new Option<CodeStyleOption<ExpressionBodyPreference>>(
nameof(CodeStyleOptions), nameof(PreferExpressionBodiedIndexers),
defaultValue: WhenPossibleWithNoneEnforcement,
storageLocations: new OptionStorageLocation[] {
new EditorConfigStorageLocation("csharp_style_expression_bodied_indexers", s => ParseExpressionBodyPreference(s, ExpressionBodyPreference.WhenOnSingleLine)),
new EditorConfigStorageLocation<CodeStyleOption<ExpressionBodyPreference>>("csharp_style_expression_bodied_indexers", s => ParseExpressionBodyPreference(s, WhenPossibleWithNoneEnforcement)),
new RoamingProfileStorageLocation($"TextEditor.CSharp.Specific.{nameof(PreferExpressionBodiedIndexers)}")});
public static readonly Option<CodeStyleOption<ExpressionBodyPreference>> PreferExpressionBodiedAccessors = new Option<CodeStyleOption<ExpressionBodyPreference>>(
nameof(CodeStyleOptions), nameof(PreferExpressionBodiedAccessors),
defaultValue: WhenPossibleWithNoneEnforcement,
storageLocations: new OptionStorageLocation[] {
new EditorConfigStorageLocation("csharp_style_expression_bodied_accessors", s => ParseExpressionBodyPreference(s, ExpressionBodyPreference.WhenOnSingleLine)),
new EditorConfigStorageLocation<CodeStyleOption<ExpressionBodyPreference>>("csharp_style_expression_bodied_accessors", s => ParseExpressionBodyPreference(s, WhenPossibleWithNoneEnforcement)),
new RoamingProfileStorageLocation($"TextEditor.CSharp.Specific.{nameof(PreferExpressionBodiedAccessors)}")});
private static object ParseExpressionBodyPreference(string value, ExpressionBodyPreference @default)
public static CodeStyleOption<ExpressionBodyPreference> ParseExpressionBodyPreference(
string optionString, CodeStyleOption<ExpressionBodyPreference> @default)
{
if (bool.TryParse(value, out var boolValue))
// optionString must be similar to true:error or when_on_single_line:suggestion.
if (CodeStyleHelpers.TryGetCodeStyleValueAndOptionalNotification(optionString,
out var value, out var notificationOpt))
{
return boolValue ? ExpressionBodyPreference.WhenPossible : ExpressionBodyPreference.Never;
}
if (value == "when_on_single_line")
{
return ExpressionBodyPreference.WhenOnSingleLine;
// A notification value must be provided.
if (notificationOpt != null)
{
if (bool.TryParse(value, out var boolValue))
{
return boolValue
? new CodeStyleOption<ExpressionBodyPreference>(ExpressionBodyPreference.WhenPossible, notificationOpt)
: new CodeStyleOption<ExpressionBodyPreference>(ExpressionBodyPreference.Never, notificationOpt);
}
if (value == "when_on_single_line")
{
return new CodeStyleOption<ExpressionBodyPreference>(ExpressionBodyPreference.WhenOnSingleLine, notificationOpt);
}
}
}
return @default;
......@@ -119,7 +131,7 @@ private static object ParseExpressionBodyPreference(string value, ExpressionBody
public static readonly Option<CodeStyleOption<bool>> PreferBraces = new Option<CodeStyleOption<bool>>(
nameof(CodeStyleOptions), nameof(PreferBraces), defaultValue: CodeStyleOptions.TrueWithNoneEnforcement,
storageLocations: new OptionStorageLocation[] {
new EditorConfigStorageLocation("csharp_prefer_braces"),
EditorConfigStorageLocation.ForBoolCodeStyleOption("csharp_prefer_braces"),
new RoamingProfileStorageLocation($"TextEditor.CSharp.Specific.{nameof(PreferBraces)}")});
public static IEnumerable<Option<CodeStyleOption<bool>>> GetCodeStyleOptions()
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Diagnostics;
namespace Microsoft.CodeAnalysis.CodeStyle
{
internal static class CodeStyleHelpers
{
public static CodeStyleOption<bool> ParseEditorConfigCodeStyleOption(string arg)
{
TryParseEditorConfigCodeStyleOption(arg, out var option);
return option;
}
public static bool TryParseEditorConfigCodeStyleOption(string arg, out CodeStyleOption<bool> option)
{
var args = arg.Split(':');
var isEnabled = false;
if (args.Length != 2)
if (TryGetCodeStyleValueAndOptionalNotification(
arg, out string value, out NotificationOption notificationOpt))
{
if (args.Length == 1)
// First value has to be true or false. Anything else is unsupported.
if (bool.TryParse(value.Trim(), out var isEnabled))
{
if (bool.TryParse(args[0].Trim(), out isEnabled) && !isEnabled)
// We allow 'false' to be provided without a notification option. However,
// 'true' must always be provided with a notification option.
if (isEnabled == false)
{
option = new CodeStyleOption<bool>(value: isEnabled, notification: NotificationOption.None);
notificationOpt = notificationOpt ?? NotificationOption.None;
option = new CodeStyleOption<bool>(false, notificationOpt);
return true;
}
else
else if (notificationOpt != null)
{
option = CodeStyleOption<bool>.Default;
return false;
option = new CodeStyleOption<bool>(true, notificationOpt);
return true;
}
}
option = CodeStyleOption<bool>.Default;
return false;
}
if (!bool.TryParse(args[0].Trim(), out isEnabled))
option = CodeStyleOption<bool>.Default;
return false;
}
/// <summary>
/// Given an editor-config code-style-option, gives back the constituent parts of the
/// option. For example, if the option is "true:error" then "true" will be returned
/// in <paramref name="value"/> and <see cref="NotificationOption.Error"/> will be returned
/// in <paramref name="notificationOpt"/>. Note that users are allowed to not provide
/// a NotificationOption, so <paramref name="notificationOpt"/> may be null. The caller
/// of this method must decide what to do in that case.
/// </summary>
public static bool TryGetCodeStyleValueAndOptionalNotification(
string arg, out string value, out NotificationOption notificationOpt)
{
var args = arg.Split(':');
Debug.Assert(args.Length > 0);
// We allow a single value to be provided in some cases. For example users
// can provide 'false' without supplying a notification as well. Allow the
// caller of this to determine what to do in this case.
if (args.Length == 1)
{
value = args[0];
notificationOpt = null;
return true;
}
if (args.Length == 2)
{
option = CodeStyleOption<bool>.Default;
return false;
// If we have two args, then the second must be a notification option. If
// it isn't, then this isn't a valid code style option at all.
if (TryParseNotification(args[1], out var localNotification))
{
value = args[0];
notificationOpt = localNotification;
return true;
}
}
switch (args[1].Trim())
// We only support 0 or 1 args. Anything else can't be parsed properly.
value = null;
notificationOpt = null;
return false;
}
public static bool TryParseNotification(string value, out NotificationOption notification)
{
switch (value.Trim())
{
case EditorConfigSeverityStrings.Silent: option = new CodeStyleOption<bool>(value: isEnabled, notification: NotificationOption.None); return true;
case EditorConfigSeverityStrings.Suggestion: option = new CodeStyleOption<bool>(value: isEnabled, notification: NotificationOption.Suggestion); return true;
case EditorConfigSeverityStrings.Warning: option = new CodeStyleOption<bool>(value: isEnabled, notification: NotificationOption.Warning); return true;
case EditorConfigSeverityStrings.Error: option = new CodeStyleOption<bool>(value: isEnabled, notification: NotificationOption.Error); return true;
default: option = new CodeStyleOption<bool>(value: isEnabled, notification: NotificationOption.None); return false;
case EditorConfigSeverityStrings.Silent: notification = NotificationOption.None; return true;
case EditorConfigSeverityStrings.Suggestion: notification = NotificationOption.Suggestion; return true;
case EditorConfigSeverityStrings.Warning: notification = NotificationOption.Warning; return true;
case EditorConfigSeverityStrings.Error: notification = NotificationOption.Error; return true;
}
notification = NotificationOption.None;
return false;
}
}
}
......@@ -35,7 +35,7 @@ public class CodeStyleOption<T> : ICodeStyleOption, IEquatable<CodeStyleOption<T
public CodeStyleOption(T value, NotificationOption notification)
{
Value = value;
Notification = notification;
Notification = notification ?? throw new ArgumentNullException(nameof(notification));
}
public T Value { get; set; }
......
......@@ -22,7 +22,7 @@ public class CodeStyleOptions
/// </summary>
public static readonly PerLanguageOption<CodeStyleOption<bool>> QualifyFieldAccess = new PerLanguageOption<CodeStyleOption<bool>>(nameof(CodeStyleOptions), nameof(QualifyFieldAccess), defaultValue: CodeStyleOption<bool>.Default,
storageLocations: new OptionStorageLocation[]{
new EditorConfigStorageLocation("dotnet_style_qualification_for_field"),
EditorConfigStorageLocation.ForBoolCodeStyleOption("dotnet_style_qualification_for_field"),
new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.QualifyFieldAccess")});
/// <summary>
......@@ -30,7 +30,7 @@ public class CodeStyleOptions
/// </summary>
public static readonly PerLanguageOption<CodeStyleOption<bool>> QualifyPropertyAccess = new PerLanguageOption<CodeStyleOption<bool>>(nameof(CodeStyleOptions), nameof(QualifyPropertyAccess), defaultValue: CodeStyleOption<bool>.Default,
storageLocations: new OptionStorageLocation[]{
new EditorConfigStorageLocation("dotnet_style_qualification_for_property"),
EditorConfigStorageLocation.ForBoolCodeStyleOption("dotnet_style_qualification_for_property"),
new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.QualifyPropertyAccess")});
/// <summary>
......@@ -38,7 +38,7 @@ public class CodeStyleOptions
/// </summary>
public static readonly PerLanguageOption<CodeStyleOption<bool>> QualifyMethodAccess = new PerLanguageOption<CodeStyleOption<bool>>(nameof(CodeStyleOptions), nameof(QualifyMethodAccess), defaultValue: CodeStyleOption<bool>.Default,
storageLocations: new OptionStorageLocation[]{
new EditorConfigStorageLocation("dotnet_style_qualification_for_method"),
EditorConfigStorageLocation.ForBoolCodeStyleOption("dotnet_style_qualification_for_method"),
new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.QualifyMethodAccess")});
/// <summary>
......@@ -46,7 +46,7 @@ public class CodeStyleOptions
/// </summary>
public static readonly PerLanguageOption<CodeStyleOption<bool>> QualifyEventAccess = new PerLanguageOption<CodeStyleOption<bool>>(nameof(CodeStyleOptions), nameof(QualifyEventAccess), defaultValue: CodeStyleOption<bool>.Default,
storageLocations: new OptionStorageLocation[]{
new EditorConfigStorageLocation("dotnet_style_qualification_for_event"),
EditorConfigStorageLocation.ForBoolCodeStyleOption("dotnet_style_qualification_for_event"),
new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.QualifyEventAccess")});
/// <summary>
......@@ -54,7 +54,7 @@ public class CodeStyleOptions
/// </summary>
public static readonly PerLanguageOption<CodeStyleOption<bool>> PreferIntrinsicPredefinedTypeKeywordInDeclaration = new PerLanguageOption<CodeStyleOption<bool>>(nameof(CodeStyleOptions), nameof(PreferIntrinsicPredefinedTypeKeywordInDeclaration), defaultValue: TrueWithNoneEnforcement,
storageLocations: new OptionStorageLocation[]{
new EditorConfigStorageLocation("dotnet_style_predefined_type_for_locals_parameters_members"),
EditorConfigStorageLocation.ForBoolCodeStyleOption("dotnet_style_predefined_type_for_locals_parameters_members"),
new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.PreferIntrinsicPredefinedTypeKeywordInDeclaration.CodeStyle")});
/// <summary>
......@@ -62,7 +62,7 @@ public class CodeStyleOptions
/// </summary>
public static readonly PerLanguageOption<CodeStyleOption<bool>> PreferIntrinsicPredefinedTypeKeywordInMemberAccess = new PerLanguageOption<CodeStyleOption<bool>>(nameof(CodeStyleOptions), nameof(PreferIntrinsicPredefinedTypeKeywordInMemberAccess), defaultValue: TrueWithNoneEnforcement,
storageLocations: new OptionStorageLocation[]{
new EditorConfigStorageLocation("dotnet_style_predefined_type_for_member_access"),
EditorConfigStorageLocation.ForBoolCodeStyleOption("dotnet_style_predefined_type_for_member_access"),
new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.PreferIntrinsicPredefinedTypeKeywordInMemberAccess.CodeStyle")});
internal static readonly PerLanguageOption<CodeStyleOption<bool>> PreferThrowExpression = new PerLanguageOption<CodeStyleOption<bool>>(
......@@ -70,7 +70,7 @@ public class CodeStyleOptions
nameof(PreferThrowExpression),
defaultValue: TrueWithSuggestionEnforcement,
storageLocations: new OptionStorageLocation[]{
new EditorConfigStorageLocation("csharp_style_throw_expression"),
EditorConfigStorageLocation.ForBoolCodeStyleOption("csharp_style_throw_expression"),
new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.PreferThrowExpression")});
internal static readonly PerLanguageOption<CodeStyleOption<bool>> PreferObjectInitializer = new PerLanguageOption<CodeStyleOption<bool>>(
......@@ -78,7 +78,7 @@ public class CodeStyleOptions
nameof(PreferObjectInitializer),
defaultValue: TrueWithSuggestionEnforcement,
storageLocations: new OptionStorageLocation[]{
new EditorConfigStorageLocation("dotnet_style_object_initializer"),
EditorConfigStorageLocation.ForBoolCodeStyleOption("dotnet_style_object_initializer"),
new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.PreferObjectInitializer")});
internal static readonly PerLanguageOption<CodeStyleOption<bool>> PreferCollectionInitializer = new PerLanguageOption<CodeStyleOption<bool>>(
......@@ -86,7 +86,7 @@ public class CodeStyleOptions
nameof(PreferCollectionInitializer),
defaultValue: TrueWithSuggestionEnforcement,
storageLocations: new OptionStorageLocation[]{
new EditorConfigStorageLocation("dotnet_style_collection_initializer"),
EditorConfigStorageLocation.ForBoolCodeStyleOption("dotnet_style_collection_initializer"),
new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.PreferCollectionInitializer")});
internal static readonly PerLanguageOption<bool> PreferObjectInitializer_FadeOutCode = new PerLanguageOption<bool>(
......@@ -106,7 +106,7 @@ public class CodeStyleOptions
nameof(PreferCoalesceExpression),
defaultValue: TrueWithSuggestionEnforcement,
storageLocations: new OptionStorageLocation[]{
new EditorConfigStorageLocation("dotnet_style_coalesce_expression"),
EditorConfigStorageLocation.ForBoolCodeStyleOption("dotnet_style_coalesce_expression"),
new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.PreferCoalesceExpression") });
internal static readonly PerLanguageOption<CodeStyleOption<bool>> PreferNullPropagation = new PerLanguageOption<CodeStyleOption<bool>>(
......@@ -114,7 +114,7 @@ public class CodeStyleOptions
nameof(PreferNullPropagation),
defaultValue: TrueWithSuggestionEnforcement,
storageLocations: new OptionStorageLocation[]{
new EditorConfigStorageLocation("dotnet_style_null_propagation"),
EditorConfigStorageLocation.ForBoolCodeStyleOption("dotnet_style_null_propagation"),
new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.PreferNullPropagation") });
internal static readonly PerLanguageOption<CodeStyleOption<bool>> PreferInlinedVariableDeclaration = new PerLanguageOption<CodeStyleOption<bool>>(
......@@ -122,7 +122,7 @@ public class CodeStyleOptions
nameof(PreferInlinedVariableDeclaration),
defaultValue: TrueWithSuggestionEnforcement,
storageLocations: new OptionStorageLocation[]{
new EditorConfigStorageLocation("csharp_style_inlined_variable_declaration"),
EditorConfigStorageLocation.ForBoolCodeStyleOption("csharp_style_inlined_variable_declaration"),
new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.PreferInlinedVariableDeclaration") });
internal static readonly PerLanguageOption<CodeStyleOption<bool>> PreferExplicitTupleNames = new PerLanguageOption<CodeStyleOption<bool>>(
......@@ -130,7 +130,7 @@ public class CodeStyleOptions
nameof(PreferExplicitTupleNames),
defaultValue: TrueWithSuggestionEnforcement,
storageLocations: new OptionStorageLocation[] {
new EditorConfigStorageLocation("dotnet_style_explicit_tuple_names"),
EditorConfigStorageLocation.ForBoolCodeStyleOption("dotnet_style_explicit_tuple_names"),
new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.PreferExplicitTupleNames") });
}
}
\ No newline at end of file
......@@ -8,7 +8,7 @@ internal class GenerationOptions
{
public static readonly PerLanguageOption<bool> PlaceSystemNamespaceFirst = new PerLanguageOption<bool>(nameof(GenerationOptions), nameof(PlaceSystemNamespaceFirst), defaultValue: true,
storageLocations: new OptionStorageLocation[] {
new EditorConfigStorageLocation("dotnet_sort_system_directives_first"),
EditorConfigStorageLocation.ForBoolOption("dotnet_sort_system_directives_first"),
new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.PlaceSystemNamespaceFirst")});
}
}
......@@ -9,17 +9,17 @@ public static class FormattingOptions
{
public static PerLanguageOption<bool> UseTabs { get; } =
new PerLanguageOption<bool>(nameof(FormattingOptions), nameof(UseTabs), defaultValue: false,
storageLocations: new EditorConfigStorageLocation("indent_style", s => s == "tab"));
storageLocations: new EditorConfigStorageLocation<bool>("indent_style", s => s == "tab"));
// This is also serialized by the Visual Studio-specific LanguageSettingsPersister
public static PerLanguageOption<int> TabSize { get; } =
new PerLanguageOption<int>(nameof(FormattingOptions), nameof(TabSize), defaultValue: 4,
storageLocations: new EditorConfigStorageLocation("tab_width"));
storageLocations: EditorConfigStorageLocation.ForInt32Option("tab_width"));
// This is also serialized by the Visual Studio-specific LanguageSettingsPersister
public static PerLanguageOption<int> IndentationSize { get; } =
new PerLanguageOption<int>(nameof(FormattingOptions), nameof(IndentationSize), defaultValue: 4,
storageLocations: new EditorConfigStorageLocation("indent_size"));
storageLocations: EditorConfigStorageLocation.ForInt32Option("indent_size"));
// This is also serialized by the Visual Studio-specific LanguageSettingsPersister
public static PerLanguageOption<IndentStyle> SmartIndent { get; } =
......@@ -27,13 +27,13 @@ public static class FormattingOptions
public static PerLanguageOption<string> NewLine { get; } =
new PerLanguageOption<string>(nameof(FormattingOptions), nameof(NewLine), defaultValue: "\r\n",
storageLocations: new EditorConfigStorageLocation("end_of_line", ParseEditorConfigEndOfLine));
storageLocations: new EditorConfigStorageLocation<string>("end_of_line", ParseEditorConfigEndOfLine));
internal static Option<bool> InsertFinalNewLine { get; } =
new Option<bool>(nameof(FormattingOptions), nameof(InsertFinalNewLine), defaultValue: false,
storageLocations: new EditorConfigStorageLocation("insert_final_newline"));
storageLocations: EditorConfigStorageLocation.ForBoolOption("insert_final_newline"));
private static object ParseEditorConfigEndOfLine(string endOfLineValue)
private static Optional<string> ParseEditorConfigEndOfLine(string endOfLineValue)
{
switch (endOfLineValue)
{
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.CodeAnalysis.CodeStyle;
namespace Microsoft.CodeAnalysis.Options
{
internal static class EditorConfigStorageLocation
{
public static EditorConfigStorageLocation<bool> ForBoolOption(string keyName)
=> new EditorConfigStorageLocation<bool>(keyName, s_parseBool);
public static EditorConfigStorageLocation<int> ForInt32Option(string keyName)
=> new EditorConfigStorageLocation<int>(keyName, s_parseInt32);
public static EditorConfigStorageLocation<CodeStyleOption<bool>> ForBoolCodeStyleOption(string keyName)
=> new EditorConfigStorageLocation<CodeStyleOption<bool>>(keyName, s_parseBoolCodeStyleOption);
private static Func<string, Optional<bool>> s_parseBool = ParseBool;
private static Optional<bool> ParseBool(string str)
=> bool.TryParse(str, out var result) ? result : new Optional<bool>();
private static Func<string, Optional<int>> s_parseInt32 = ParseInt32;
private static Optional<int> ParseInt32(string str)
=> int.TryParse(str, out var result) ? result : new Optional<int>();
private static Func<string, Optional<CodeStyleOption<bool>>> s_parseBoolCodeStyleOption = ParseBoolCodeStyleOption;
private static Optional<CodeStyleOption<bool>> ParseBoolCodeStyleOption(string str)
=> CodeStyleHelpers.TryParseEditorConfigCodeStyleOption(str, out var result) ? result : new Optional<CodeStyleOption<bool>>();
}
}
\ No newline at end of file
......@@ -3,45 +3,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles;
using static Microsoft.CodeAnalysis.CodeStyle.CodeStyleHelpers;
namespace Microsoft.CodeAnalysis.Options
{
/// <summary>
/// Specifies that an option should be read from an .editorconfig file.
/// </summary>
internal sealed class EditorConfigStorageLocation : OptionStorageLocation
internal sealed class EditorConfigStorageLocation<T> : OptionStorageLocation, IEditorConfigStorageLocation
{
public string KeyName { get; }
private Func<string, Type, object> _parseValue;
private readonly Func<string, Type, Optional<T>> _parseValue;
private static Func<string, Type, object> _cachedParseValue = (s, type) =>
{
if (type == typeof(int))
{
var value = 0;
return int.TryParse(s, out value) ? (object)value : null;
}
else if (type == typeof(bool))
{
var value = false;
return bool.TryParse(s, out value) ? (object)value : null;
}
else if (type == typeof(CodeStyleOption<bool>))
{
var value = CodeStyleOption<bool>.Default;
return TryParseEditorConfigCodeStyleOption(s, out value) ? value : null;
}
else
{
throw new NotSupportedException(WorkspacesResources.Option_0_has_an_unsupported_type_to_use_with_1_You_should_specify_a_parsing_function);
}
};
private Func<object, IReadOnlyDictionary<string, object>, Type, (object result, bool succeeded)> _tryParseDictionary;
private readonly Func<object, IReadOnlyDictionary<string, object>, Type, (object result, bool succeeded)> _tryParseDictionary;
private static Func<object, IReadOnlyDictionary<string, object>, Type, (object result, bool succeeded)> _cachedTryParseDictionary = (underlyingOption, dictionary, type) =>
{
......@@ -74,13 +49,22 @@ internal sealed class EditorConfigStorageLocation : OptionStorageLocation
}
};
public bool TryGetOption(object underlyingOption, IReadOnlyDictionary<string, object> allRawConventions, Type type, out object result)
bool IEditorConfigStorageLocation.TryGetOption(object underlyingOption, IReadOnlyDictionary<string, object> allRawConventions, Type type, out object result)
{
if (_parseValue != null && KeyName != null)
{
if (allRawConventions.TryGetValue(KeyName, out object value))
{
result = _parseValue(value.ToString(), type);
var optionalValue = _parseValue(value.ToString(), type);
if (optionalValue.HasValue)
{
result = optionalValue.Value;
}
else
{
result = null;
}
return result != null;
}
}
......@@ -95,13 +79,7 @@ public bool TryGetOption(object underlyingOption, IReadOnlyDictionary<string, ob
return false;
}
public EditorConfigStorageLocation(string keyName)
{
KeyName = keyName;
_parseValue = _cachedParseValue;
}
public EditorConfigStorageLocation(string keyName, Func<string, object> parseValue)
public EditorConfigStorageLocation(string keyName, Func<string, Optional<T>> parseValue)
{
KeyName = keyName;
......@@ -121,4 +99,4 @@ public EditorConfigStorageLocation(Func<object, IReadOnlyDictionary<string, obje
_tryParseDictionary = (underlyingOption, dictionary, type) => tryParseDictionary(underlyingOption, dictionary);
}
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
namespace Microsoft.CodeAnalysis.Options
{
internal interface IEditorConfigStorageLocation
{
bool TryGetOption(object underlyingOption, IReadOnlyDictionary<string, object> allRawConventions, Type type, out object value);
}
}
......@@ -102,7 +102,7 @@ public static class SimplificationOptions
/// </summary>
internal static PerLanguageOption<NamingStylePreferences> NamingPreferences { get; } = new PerLanguageOption<NamingStylePreferences>(nameof(SimplificationOptions), nameof(NamingPreferences), defaultValue: NamingStylePreferences.Default,
storageLocations: new OptionStorageLocation[]{
new EditorConfigStorageLocation(),
new EditorConfigStorageLocation<NamingStylePreferences>(),
new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.NamingPreferences")});
}
}
......@@ -388,6 +388,8 @@
<Compile Include="FindSymbols\SyntaxTree\SyntaxTreeIndex.DeclarationInfo.cs" />
<Compile Include="FindSymbols\SyntaxTree\SyntaxTreeIndex.IdentifierInfo.cs" />
<Compile Include="NamingStyles\Serialization\NamingStylePreferencesExtensions.cs" />
<Compile Include="Options\EditorConfig\EditorConfigStorageLocation.cs" />
<Compile Include="Options\EditorConfig\IEditorConfigStorageLocation.cs" />
<Compile Include="PatternMatching\PatternMatch.cs" />
<Compile Include="PatternMatching\PatternMatcher.AllLowerCamelCaseMatcher.cs" />
<Compile Include="PatternMatching\PatternMatcher.cs" />
......@@ -439,7 +441,7 @@
<Compile Include="FindSymbols\SymbolFinder_Remote.cs" />
<Compile Include="FindSymbols\SymbolTree\SymbolTreeInfo.FirstEntityHandleProvider.cs" />
<Compile Include="LanguageServices\SyntaxFactsService\AbstractSyntaxFactsService.cs" />
<Compile Include="Options\EditorConfigStorageLocation.cs" />
<Compile Include="Options\EditorConfig\EditorConfigStorageLocation`1.cs" />
<Compile Include="Options\GlobalOptionService.cs" />
<Compile Include="Options\IDocumentOptions.cs" />
<Compile Include="Options\IDocumentOptionsProvider.cs" />
......
......@@ -15,6 +15,7 @@ public class EditorConfigCodeStyleParserTests
[InlineData("true:suggestion", true, DiagnosticSeverity.Info)]
[InlineData("true:warning", true, DiagnosticSeverity.Warning)]
[InlineData("true:error", true, DiagnosticSeverity.Error)]
[InlineData("true", false, DiagnosticSeverity.Hidden)]
[InlineData("false:silent", false, DiagnosticSeverity.Hidden)]
[InlineData("false:suggestion", false, DiagnosticSeverity.Info)]
[InlineData("false:warning", false, DiagnosticSeverity.Warning)]
......@@ -43,7 +44,7 @@ static void TestParseEditorConfigCodeStyleOption(string args, bool isEnabled, Di
var codeStyleOption = new CodeStyleOption<bool>(value: isEnabled, notification: notificationOption);
var result = CodeStyleHelpers.ParseEditorConfigCodeStyleOption(args);
CodeStyleHelpers.TryParseEditorConfigCodeStyleOption(args, out var result);
Assert.True(result.Value == isEnabled,
$"Expected {nameof(isEnabled)} to be {isEnabled}, was {result.Value}");
Assert.True(result.Notification.Value == severity,
......
......@@ -16,7 +16,7 @@ public class EditorConfigStorageLocationTests
[Fact]
public static void TestEmptyDictionaryReturnNoNamingStylePreferencesObjectReturnsFalse()
{
var editorConfigStorageLocation = new EditorConfigStorageLocation();
var editorConfigStorageLocation = new EditorConfigStorageLocation<object>();
var result = editorConfigStorageLocation.TryGetOption(new object(), new Dictionary<string, object>(), typeof(NamingStylePreferences), out var @object);
Assert.False(result, "Expected TryParseReadonlyDictionary to return 'false' for empty dictionary");
}
......@@ -24,7 +24,7 @@ public static void TestEmptyDictionaryReturnNoNamingStylePreferencesObjectReturn
[Fact]
public static void TestEmptyDictionaryDefaultNamingStylePreferencesObjectReturnsFalse()
{
var editorConfigStorageLocation = new EditorConfigStorageLocation();
var editorConfigStorageLocation = new EditorConfigStorageLocation<object>();
var existingNamingStylePreferences = new NamingStylePreferences(
ImmutableArray.Create<SymbolSpecification>(),
ImmutableArray.Create<NamingStyle>(),
......@@ -53,7 +53,7 @@ public static void TestNonEmptyDictionaryReturnsTrue()
};
var existingNamingStylePreferences = ParseDictionary(initialDictionary);
var editorConfigStorageLocation = new EditorConfigStorageLocation();
var editorConfigStorageLocation = new EditorConfigStorageLocation<object>();
var newDictionary = new Dictionary<string, object>()
{
["dotnet_naming_rule.methods_and_properties_must_be_pascal_case.severity"] = "error",
......@@ -79,7 +79,7 @@ public static void TestNonEmptyDictionaryReturnsTrue()
[Fact]
public static void TestObjectTypeThrowsNotSupportedException()
{
var editorConfigStorageLocation = new EditorConfigStorageLocation();
var editorConfigStorageLocation = new EditorConfigStorageLocation<object>();
Assert.Throws<NotSupportedException>(() =>
{
editorConfigStorageLocation.TryGetOption(new object(), new Dictionary<string, object>(), typeof(object), out var @object);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册