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

Implement Naming Styles for editorconfig UI #58075

Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
883a2c5
move Language enum further down the stack
jmarolf Dec 1, 2021
d77a8f8
implement naming styles option parser with text span support
jmarolf Dec 1, 2021
a7e051a
add folder description
jmarolf Dec 2, 2021
5e5cbc8
Add namging style option support
jmarolf Dec 2, 2021
682a9df
add folder description
jmarolf Dec 2, 2021
a773abe
Add views for naming styles
jmarolf Dec 2, 2021
48f54b4
Update src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Nami…
jmarolf Dec 2, 2021
1886364
Update src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Edit…
jmarolf Dec 2, 2021
49088b0
Update src/EditorFeatures/Core/EditorConfigSettings/Updater/NamingSty…
jmarolf Dec 2, 2021
da90d81
Update src/EditorFeatures/Core/EditorConfigSettings/Updater/NamingSty…
jmarolf Dec 2, 2021
3a28801
lengthen the search box to align with the tabs above it
jmarolf Dec 2, 2021
cbce098
enable nullable on test files
jmarolf Dec 2, 2021
525cb16
de-duplicate local functions
jmarolf Dec 2, 2021
03da9e5
simplify boolean logic
jmarolf Dec 2, 2021
98006e6
use resource strings in tests
jmarolf Dec 2, 2021
b53f7d1
return immutable array for static severities
jmarolf Dec 2, 2021
74e9ca0
update readme with a relative path link
jmarolf Dec 2, 2021
761eb1e
using TryPeekNext
jmarolf Dec 3, 2021
ba7bdb3
using disposeable pooled objects
jmarolf Dec 3, 2021
2c9fed1
renamge rangeOpt to numberRange
jmarolf Dec 3, 2021
264eefb
fix Span and Section being swapped from the base
jmarolf Dec 6, 2021
b2ca734
update incorrect comment
jmarolf Dec 6, 2021
a321a3f
Combine Lex with static function
jmarolf Dec 6, 2021
132f6b0
return false if we fail to determine the language for a path
jmarolf Dec 6, 2021
cb5cfc0
use Lazy instead of fancy InterlockedInitialize
jmarolf Dec 6, 2021
86ef543
move search bar up by 12 pixels
jmarolf Dec 7, 2021
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// 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;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.AddImports;
Expand All @@ -12,7 +13,9 @@
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Editor.EditorConfigSettings;
using Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Data;
using Microsoft.CodeAnalysis.Editor.EditorConfigSettings.DataProvider;
using Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Updater;
using Microsoft.CodeAnalysis.EditorConfig;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
Expand All @@ -33,7 +36,7 @@ private static Workspace CreateWorkspaceWithProjectAndDocuments()
var workspace = new AdhocWorkspace(EditorTestCompositions.EditorFeatures.GetHostServices(), WorkspaceKind.Host);

Assert.True(workspace.TryApplyChanges(workspace.CurrentSolution
.AddProject(projectId, "proj1", "proj1.dll", LanguageNames.CSharp)
.AddProject(ProjectInfo.Create(projectId, VersionStamp.Create(), "proj1", "proj1.dll", LanguageNames.CSharp, filePath: "/a/b/proj1.csproj"))
.AddDocument(DocumentId.CreateNewId(projectId), "goo.cs", "public class Goo { }")
.AddAdditionalDocument(DocumentId.CreateNewId(projectId), "add.txt", "text")
.AddAnalyzerConfigDocument(DocumentId.CreateNewId(projectId), "editorcfg", SourceText.From(""), filePath: EditorconfigPath)));
Expand Down Expand Up @@ -372,5 +375,209 @@ public async Task TestWhitespaceSettingUpdaterService()
var update = Assert.Single(updates);
Assert.Equal("[*.cs]\r\ncsharp_new_line_before_else = false", update.NewText);
}

[Fact, Trait(Traits.Feature, Traits.Features.EditorConfigUI)]
public async Task TestNamingStyleSettingsUpdater()
{
var workspace = CreateWorkspaceWithProjectAndDocuments();
var settingsProviderFactory = workspace.Services.GetRequiredService<IWorkspaceSettingsProviderFactory<NamingStyleSetting>>();
var settingsProvider = settingsProviderFactory.GetForFile("/a/b/config");
var model = new TestViewModel();
settingsProvider.RegisterViewModel(model);
var dataSnapShot = settingsProvider.GetCurrentDataSnapshot();
Assert.Equal(3, dataSnapShot.Length);

var setting0 = dataSnapShot[0];
var setting1 = dataSnapShot[1];
var setting2 = dataSnapShot[2];

setting0.ChangeSeverity(ReportDiagnostic.Error);

var newText = await settingsProvider.GetChangedEditorConfigAsync(SourceText.From(string.Empty));
var fileText = newText.ToString();
Assert.Equal(ExpectedInitialEditorConfig, fileText);
Assert.Equal(ReportDiagnostic.Error, setting0.Severity);

setting1.ChangeSeverity(ReportDiagnostic.Error);
setting2.ChangeSeverity(ReportDiagnostic.Error);

newText = await settingsProvider.GetChangedEditorConfigAsync(newText);
fileText = newText.ToString();
Assert.Equal(ExpectedEditorConfigAfterAllSeveritiesChanged, fileText);
Assert.Equal(ReportDiagnostic.Error, setting0.Severity);
Assert.Equal(ReportDiagnostic.Error, setting0.Severity);

var selectedStyleIndex0 = Array.IndexOf(setting0.AllStyles, setting0.StyleName);
Assert.Equal(1, selectedStyleIndex0);

setting0.ChangeStyle(0);
newText = await settingsProvider.GetChangedEditorConfigAsync(newText);
fileText = newText.ToString();
Assert.Equal(ExpectedEditorConfigAfterSymbolSpecChange, fileText);
Assert.Equal("pascal_case", setting0.StyleName);
}

private const string ExpectedInitialEditorConfig =
@"
[*.{cs,vb}]
#### Naming styles ####

# Naming rules

dotnet_naming_rule.interface_should_be_begins_with_i.severity = error
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i

dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case

dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case

# Symbol specifications

dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =

dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =

dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =

# Naming styles

dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case

dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case

dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
";

private const string ExpectedEditorConfigAfterAllSeveritiesChanged =
@"
[*.{cs,vb}]
#### Naming styles ####

# Naming rules

dotnet_naming_rule.interface_should_be_begins_with_i.severity = error
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i

dotnet_naming_rule.types_should_be_pascal_case.severity = error
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case

dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = error
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case

# Symbol specifications

dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =

dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =

dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =

# Naming styles

dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case

dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case

dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
";

private const string ExpectedEditorConfigAfterSymbolSpecChange =
@"
[*.{cs,vb}]
#### Naming styles ####

# Naming rules

dotnet_naming_rule.interface_should_be_begins_with_i.severity = error
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = pascal_case

dotnet_naming_rule.types_should_be_pascal_case.severity = error
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case

dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = error
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case

# Symbol specifications

dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =

dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =

dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =

# Naming styles

dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case

dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case

dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
";

private class TestViewModel : ISettingsEditorViewModel
{
public void NotifyOfUpdate() { }

Task<SourceText> ISettingsEditorViewModel.UpdateEditorConfigAsync(SourceText sourceText)
{
throw new NotImplementedException();
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ internal partial class SettingsAggregator : ISettingsAggregator
private readonly Workspace _workspace;
private readonly ISettingsProviderFactory<AnalyzerSetting> _analyzerProvider;
private ISettingsProviderFactory<WhitespaceSetting> _whitespaceProvider;
private ISettingsProviderFactory<NamingStyleSetting> _namingStyleProvider;
private ISettingsProviderFactory<CodeStyleSetting> _codeStyleProvider;

public SettingsAggregator(Workspace workspace)
Expand All @@ -24,6 +25,7 @@ public SettingsAggregator(Workspace workspace)
_workspace.WorkspaceChanged += UpdateProviders;
_whitespaceProvider = GetOptionsProviderFactory<WhitespaceSetting>(_workspace);
_codeStyleProvider = GetOptionsProviderFactory<CodeStyleSetting>(_workspace);
_namingStyleProvider = GetOptionsProviderFactory<NamingStyleSetting>(_workspace);
_analyzerProvider = GetOptionsProviderFactory<AnalyzerSetting>(_workspace);
}

Expand All @@ -41,6 +43,7 @@ private void UpdateProviders(object? sender, WorkspaceChangeEventArgs e)
case WorkspaceChangeKind.ProjectChanged:
_whitespaceProvider = GetOptionsProviderFactory<WhitespaceSetting>(_workspace);
_codeStyleProvider = GetOptionsProviderFactory<CodeStyleSetting>(_workspace);
_namingStyleProvider = GetOptionsProviderFactory<NamingStyleSetting>(_workspace);
break;
default:
break;
Expand All @@ -59,6 +62,11 @@ private void UpdateProviders(object? sender, WorkspaceChangeEventArgs e)
return (ISettingsProvider<TData>)_whitespaceProvider.GetForFile(fileName);
}

if (typeof(TData) == typeof(NamingStyleSetting))
{
return (ISettingsProvider<TData>)_namingStyleProvider.GetForFile(fileName);
}

if (typeof(TData) == typeof(CodeStyleSetting))
{
return (ISettingsProvider<TData>)_codeStyleProvider.GetForFile(fileName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Updater;
using Microsoft.CodeAnalysis.EditorConfig;

namespace Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Data
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// 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;
using System.Linq;
using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles;
using Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Updater;
using Microsoft.CodeAnalysis.EditorConfig.Parsing.NamingStyles;
using Microsoft.CodeAnalysis.NamingStyles;

namespace Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Data
{
internal class NamingStyleSetting
{
private NamingStyle[] _allStyles;
private readonly NamingStyleSettingsUpdater? _settingsUpdater;

public NamingStyleSetting(
NamingRule namingRule,
NamingStyle[] allStyles,
NamingStyleSettingsUpdater settingsUpdater,
string? fileName = null)
{
Style = namingRule.NamingStyle;
_allStyles = allStyles;
Type = namingRule.SymbolSpecification;
Severity = namingRule.EnforcementLevel;
_settingsUpdater = settingsUpdater;
Location = new SettingLocation(fileName is null ? LocationKind.VisualStudio : LocationKind.EditorConfig, fileName);
}

private NamingStyleSetting()
{
_allStyles = Array.Empty<NamingStyle>();
}

public event EventHandler<EventArgs>? SettingChanged;

internal static NamingStyleSetting FromParseResult(NamingStyleOption namingStyleOption)
{
return new NamingStyleSetting
{
Style = namingStyleOption.NamingScheme.AsNamingStyle(),
Type = namingStyleOption.ApplicableSymbolInfo.AsSymbolSpecification(),
Severity = namingStyleOption.Severity,
Location = new SettingLocation(LocationKind.EditorConfig, namingStyleOption.Section.FilePath)
};
}

internal NamingStyle Style { get; set; }
internal SymbolSpecification? Type { get; set; }

public string StyleName => Style.Name;
public string[] AllStyles => _allStyles.Select(style => style.Name).ToArray();
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be ImmutableArray<string>

public string TypeName => Type?.Name ?? string.Empty;
public ReportDiagnostic Severity { get; private set; }
public SettingLocation? Location { get; protected set; }

private void OnSettingChanged((object, object?) setting)
{
if (setting is (ReportDiagnostic severity, _))
{
Severity = severity;
SettingChanged?.Invoke(this, EventArgs.Empty);
}

if (setting is (NamingStyle style, NamingStyle[] allStyles))
{
Style = style;
_allStyles = allStyles;
SettingChanged?.Invoke(this, EventArgs.Empty);
}
}

internal void ChangeSeverity(ReportDiagnostic severity)
{
if (Location is not null)
{
Location = Location with { LocationKind = LocationKind.EditorConfig };
_settingsUpdater?.QueueUpdate((OnSettingChanged, this), severity);
}
}

internal void ChangeStyle(int selectedIndex)
{
if (selectedIndex > -1 && selectedIndex < _allStyles.Length && Location is not null)
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this throw index out of bounds?

{
Location = Location with { LocationKind = LocationKind.EditorConfig };
_settingsUpdater?.QueueUpdate((OnSettingChanged, this), _allStyles[selectedIndex]);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Data;
using Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Extensions;
using Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Updater;
using Microsoft.CodeAnalysis.EditorConfig;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;

Expand Down
Loading