|
// 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.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles;
using Microsoft.CodeAnalysis.EditorConfig;
using Microsoft.CodeAnalysis.EditorConfig.Parsing;
using Microsoft.CodeAnalysis.NamingStyles;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
using NamingStylesParser = Microsoft.CodeAnalysis.EditorConfig.Parsing.NamingStyles.EditorConfigNamingStylesParser;
namespace Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Updater;
internal static class SourceTextExtensions
{
public static SourceText WithNamingStyles(this SourceText sourceText, IGlobalOptionService globalOptions)
{
var (common, csharp, visualBasic) = GetPreferencesForAllLanguages(globalOptions);
sourceText = WithNamingStyles(sourceText, csharp, Language.CSharp);
sourceText = WithNamingStyles(sourceText, visualBasic, Language.VisualBasic);
return WithNamingStyles(sourceText, common, Language.CSharp | Language.VisualBasic);
}
private static SourceText WithNamingStyles(SourceText sourceText, IEnumerable<NamingRule> rules, Language language)
{
if (rules.Any())
{
var parseResult = NamingStylesParser.Parse(sourceText, null); // file path unnecessary here
var newNamingStyleSection = new StringBuilder();
if (parseResult.TryGetSectionForLanguage(language, out var existingSection))
{
var span = new TextSpan(existingSection.Span.End, 0);
AppendNamingStylePreferencesToEditorConfig(rules, newNamingStyleSection, GetLanguageString(language));
return WithChanges(sourceText, span, newNamingStyleSection.ToString());
}
else
{
var span = new TextSpan(sourceText.Length, 0);
newNamingStyleSection.Append("\r\n");
newNamingStyleSection.Append(Section.GetHeaderTextForLanguage(language));
AppendNamingStylePreferencesToEditorConfig(rules, newNamingStyleSection, GetLanguageString(language));
return WithChanges(sourceText, span, newNamingStyleSection.ToString());
}
}
return sourceText;
static SourceText WithChanges(SourceText sourceText, TextSpan span, string newText)
{
var textChange = new TextChange(span, newText);
return sourceText.WithChanges(textChange);
}
static string? GetLanguageString(Language language)
{
if (language.HasFlag(Language.CSharp) && language.HasFlag(Language.VisualBasic))
{
return null;
}
else if (language.HasFlag(Language.CSharp))
{
return LanguageNames.CSharp;
}
else if (language.HasFlag(Language.VisualBasic))
{
return LanguageNames.VisualBasic;
}
throw new InvalidOperationException("Invalid language specified");
}
}
private static void AppendNamingStylePreferencesToEditorConfig(IEnumerable<NamingRule> namingRules, StringBuilder editorconfig, string? language = null)
{
var symbolSpecifications = namingRules.Select(x => x.SymbolSpecification).ToImmutableArray();
var namingStyles = namingRules.Select(x => x.NamingStyle).ToImmutableArray();
var serializedNamingRules = namingRules.Select(x => new SerializableNamingRule()
{
EnforcementLevel = x.EnforcementLevel,
NamingStyleID = x.NamingStyle.ID,
SymbolSpecificationID = x.SymbolSpecification.ID
}).ToImmutableArray();
language ??= LanguageNames.CSharp;
NamingStylePreferencesEditorConfigSerializer.AppendNamingStylePreferencesToEditorConfig(
symbolSpecifications,
namingStyles,
serializedNamingRules,
language,
editorconfig);
}
private static (IEnumerable<NamingRule> Common, IEnumerable<NamingRule> CSharp, IEnumerable<NamingRule> VisualBasic) GetPreferencesForAllLanguages(IGlobalOptionService globalOptions)
{
var csharpNamingStylePreferences = globalOptions.GetOption(NamingStyleOptions.NamingPreferences, LanguageNames.CSharp);
var vbNamingStylePreferences = globalOptions.GetOption(NamingStyleOptions.NamingPreferences, LanguageNames.VisualBasic);
var commonOptions = GetCommonOptions(csharpNamingStylePreferences, vbNamingStylePreferences);
var csharpOnlyOptions = GetOptionsUniqueOptions(csharpNamingStylePreferences, commonOptions);
var vbOnlyOptions = GetOptionsUniqueOptions(vbNamingStylePreferences, commonOptions);
return (commonOptions, csharpOnlyOptions, vbOnlyOptions);
static IEnumerable<NamingRule> GetCommonOptions(NamingStylePreferences csharp, NamingStylePreferences visualBasic)
=> csharp.Rules.NamingRules.Intersect(visualBasic.Rules.NamingRules, NamingRuleComparerIgnoreGUIDs.Instance);
static IEnumerable<NamingRule> GetOptionsUniqueOptions(NamingStylePreferences csharp, IEnumerable<NamingRule> common)
=> csharp.Rules.NamingRules.Except(common, NamingRuleComparerIgnoreGUIDs.Instance);
}
private class NamingRuleComparerIgnoreGUIDs : IEqualityComparer<NamingRule>
{
private static readonly Lazy<NamingRuleComparerIgnoreGUIDs> s_lazyInstance = new(() => new NamingRuleComparerIgnoreGUIDs());
public static NamingRuleComparerIgnoreGUIDs Instance => s_lazyInstance.Value;
public bool Equals(NamingRule left, NamingRule right)
{
return left.EnforcementLevel == right.EnforcementLevel &&
NamingStyleComparerIgnoreGUIDs.Instance.Equals(left.NamingStyle, right.NamingStyle) &&
SymbolSpecificationComparerIgnoreGUIDs.Instance.Equals(left.SymbolSpecification, right.SymbolSpecification);
}
public int GetHashCode(NamingRule rule)
{
var enforcementLevelHashCode = (int)rule.EnforcementLevel;
var namingStyleHashCode = NamingStyleComparerIgnoreGUIDs.Instance.GetHashCode(rule.NamingStyle);
var symbolSpecificationHashCode = SymbolSpecificationComparerIgnoreGUIDs.Instance.GetHashCode(rule.SymbolSpecification);
return Hash.Combine(enforcementLevelHashCode, Hash.Combine(namingStyleHashCode, symbolSpecificationHashCode));
}
private class NamingStyleComparerIgnoreGUIDs : IEqualityComparer<NamingStyle>
{
private static readonly Lazy<NamingStyleComparerIgnoreGUIDs> s_lazyInstance = new(() => new NamingStyleComparerIgnoreGUIDs());
public static NamingStyleComparerIgnoreGUIDs Instance => s_lazyInstance.Value;
public bool Equals(NamingStyle left, NamingStyle right)
{
return StringComparer.OrdinalIgnoreCase.Equals(left.Name, right.Name) &&
StringComparer.Ordinal.Equals(left.Prefix, right.Prefix) &&
StringComparer.Ordinal.Equals(left.Suffix, right.Suffix) &&
StringComparer.Ordinal.Equals(left.WordSeparator, right.WordSeparator) &&
left.CapitalizationScheme == right.CapitalizationScheme;
}
public int GetHashCode(NamingStyle style)
{
return Hash.Combine(StringComparer.OrdinalIgnoreCase.GetHashCode(style.Name),
Hash.Combine(StringComparer.Ordinal.GetHashCode(style.Prefix),
Hash.Combine(StringComparer.Ordinal.GetHashCode(style.Suffix),
Hash.Combine(StringComparer.Ordinal.GetHashCode(style.WordSeparator),
(int)style.CapitalizationScheme))));
}
}
private class SymbolSpecificationComparerIgnoreGUIDs : IEqualityComparer<SymbolSpecification>
{
private static readonly Lazy<SymbolSpecificationComparerIgnoreGUIDs> s_lazyInstance = new(() => new SymbolSpecificationComparerIgnoreGUIDs());
public static SymbolSpecificationComparerIgnoreGUIDs Instance => s_lazyInstance.Value;
public bool Equals(SymbolSpecification? left, SymbolSpecification? right)
{
if (left is null && right is null)
{
return true;
}
if (left is null || right is null)
{
return false;
}
return StringComparer.OrdinalIgnoreCase.Equals(left!.Name, right!.Name) &&
left.RequiredModifierList.SequenceEqual(right.RequiredModifierList) &&
left.ApplicableAccessibilityList.SequenceEqual(right.ApplicableAccessibilityList) &&
left.ApplicableSymbolKindList.SequenceEqual(right.ApplicableSymbolKindList);
}
public int GetHashCode(SymbolSpecification symbolSpecification)
{
return Hash.Combine(StringComparer.OrdinalIgnoreCase.GetHashCode(symbolSpecification.Name),
Hash.Combine(Hash.CombineValues(symbolSpecification.RequiredModifierList),
Hash.Combine(Hash.CombineValues(symbolSpecification.ApplicableAccessibilityList),
Hash.CombineValues(symbolSpecification.ApplicableSymbolKindList))));
}
}
}
}
|