Permalink
Switch branches/tags
version-2.9.0 version-2.8.2 version-2.8.0 version-2.7.0-beta3 version-2.6.0-beta3 version-2.4.0 version-2.3.5 version-2.3.4 version-2.3.2 version-2.3.2-beta1 version-2.3.0-beta3 version-2.3.0-beta2 version-2.3.0-beta1 version-2.2.0 version-2.1.0 version-2.0.0 version-2.0.0-rc4 version-2.0.0-rc3 version-2.0.0-rc2 version-2.0.0-rc version-2.0.0-beta5 version-2.0.0-beta4 version-2.0.0-beta3 version-2.0.0-beta1 version-1.3.2 version-1.3.1 version-1.3.0 version-1.3.0-beta1-20160429-01 version-1.2.2 version-1.2.1 version-1.2.0 version-1.2.0-beta1-20160108-01 version-1.2.0-beta version-1.2.0-beta-20151211-01 version-1.1.1 version-1.1.0 version-1.1.0-rc1-20151109-01 version-1.0.0 version-1.0.0-beta1-20141031-01 toolset_5 toolset_3 toolset_2 toolset_1_1 toolset_1 Visual.Studio.2015.Update.1.RC Visual.Studio.2015.Update.1.CTP Visual-Studio-2017 Visual-Studio-2017-Version-15.8 Visual-Studio-2017-Version-15.7.2 Visual-Studio-2017-Version-15.7 Visual-Studio-2017-Version-15.6 Visual-Studio-2017-Version-15.5 Visual-Studio-2017-Version-15.4 Visual-Studio-2017-Version-15.3.5 Visual-Studio-2017-Version-15.3.4 Visual-Studio-2017-Version-15.3.2 Visual-Studio-2017-Version-15.3 Visual-Studio-2017-Version-15.2 Visual-Studio-2017-Version-15.1 Visual-Studio-2017-RC4 Visual-Studio-2017-RC3 Visual-Studio-2017-RC2 Visual-Studio-2017-RC Visual-Studio-2017-Preview-Version-15.3 Visual-Studio-2017-Preview-6-Version-15.7 Visual-Studio-2017-Preview-3-Version-15.4 Visual-Studio-2017-Preview-3-Version-15.3 Visual-Studio-2017-Preview-2-Version-15.4 Visual-Studio-2017-Preview-2-Version-15.3 Visual-Studio-2017-Preview-1-Version-15.4 Visual-Studio-2015 Visual-Studio-2015-Update-3 Visual-Studio-2015-Update-3-Micro-Update-1 Visual-Studio-2015-Update-2 Visual-Studio-2015-Update-2-RC Visual-Studio-2015-Update-2-Micro-Update-3 Visual-Studio-2015-Update-2-Micro-Update-1 Visual-Studio-2015-Update-1 Visual-Studio-2015-Update-1-RC Visual-Studio-2015-Update-1-CTP Visual-Studio-2015-RC Visual-Studio-2015-Preview Visual-Studio-2015-CTP-6 Visual-Studio-2015-CTP-5 Visual-Studio-15-Preview Visual-Studio-15-Preview-5 Visual-Studio-15-Preview-4 Visual-Studio-15-Preview-3 VS.Toolset.Roslyn.1.1.0-beta1-20150727-01 VS.Tools.X86.Managed.V45.1.0.150513.2 Oss.Scan.2015.03.13 Oss.Scan.2013.03.13 NetFx.Toolset.150729
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
173 lines (148 sloc) 7.04 KB
// 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.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.CodeRefactorings;
namespace Microsoft.CodeAnalysis.ConvertNumericLiteral
{
internal abstract class AbstractConvertNumericLiteralCodeRefactoringProvider : CodeRefactoringProvider
{
public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
{
var document = context.Document;
var cancellationToken = context.CancellationToken;
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var numericToken = await root.SyntaxTree.GetTouchingTokenAsync(context.Span.Start,
token => syntaxFacts.IsNumericLiteralExpression(token.Parent), cancellationToken).ConfigureAwait(false);
if (numericToken == default)
{
return;
}
if (numericToken.ContainsDiagnostics)
{
return;
}
if (context.Span.Length > 0 &&
context.Span != numericToken.Span)
{
return;
}
var syntaxNode = numericToken.Parent;
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var symbol = semanticModel.GetTypeInfo(syntaxNode, cancellationToken).Type;
if (symbol == null)
{
return;
}
if (!IsIntegral(symbol.SpecialType))
{
return;
}
var valueOpt = semanticModel.GetConstantValue(syntaxNode);
if (!valueOpt.HasValue)
{
return;
}
var value = IntegerUtilities.ToInt64(valueOpt.Value);
var numericText = numericToken.ToString();
var (hexPrefix, binaryPrefix) = GetNumericLiteralPrefixes();
var (prefix, number, suffix) = GetNumericLiteralParts(numericText, hexPrefix, binaryPrefix);
var kind = string.IsNullOrEmpty(prefix) ? NumericKind.Decimal
: prefix.Equals(hexPrefix, StringComparison.OrdinalIgnoreCase) ? NumericKind.Hexadecimal
: prefix.Equals(binaryPrefix, StringComparison.OrdinalIgnoreCase) ? NumericKind.Binary
: NumericKind.Unknown;
if (kind == NumericKind.Unknown)
{
return;
}
if (kind != NumericKind.Decimal)
{
RegisterRefactoringWithResult(value.ToString(), FeaturesResources.Convert_to_decimal);
}
if (kind != NumericKind.Binary)
{
RegisterRefactoringWithResult(binaryPrefix + Convert.ToString(value, 2), FeaturesResources.Convert_to_binary);
}
if (kind != NumericKind.Hexadecimal)
{
RegisterRefactoringWithResult(hexPrefix + value.ToString("X"), FeaturesResources.Convert_to_hex);
}
const string DigitSeparator = "_";
if (numericText.Contains(DigitSeparator))
{
RegisterRefactoringWithResult(prefix + number.Replace(DigitSeparator, string.Empty), FeaturesResources.Remove_separators);
}
else
{
switch (kind)
{
case NumericKind.Decimal when number.Length > 3:
RegisterRefactoringWithResult(AddSeparators(number, 3), FeaturesResources.Separate_thousands);
break;
case NumericKind.Hexadecimal when number.Length > 4:
RegisterRefactoringWithResult(hexPrefix + AddSeparators(number, 4), FeaturesResources.Separate_words);
break;
case NumericKind.Binary when number.Length > 4:
RegisterRefactoringWithResult(binaryPrefix + AddSeparators(number, 4), FeaturesResources.Separate_nibbles);
break;
}
}
void RegisterRefactoringWithResult(string text, string title)
{
context.RegisterRefactoring(new MyCodeAction(title, c =>
{
var generator = SyntaxGenerator.GetGenerator(document);
var updatedToken = generator.NumericLiteralToken(text + suffix, (ulong)value)
.WithTriviaFrom(numericToken);
var updatedRoot = root.ReplaceToken(numericToken, updatedToken);
return Task.FromResult(document.WithSyntaxRoot(updatedRoot));
}));
}
}
private static (string prefix, string number, string suffix) GetNumericLiteralParts(string numericText, string hexPrefix, string binaryPrefix)
{
// Match literal text and extract out base prefix, type suffix and the number itself.
var groups = Regex.Match(numericText, $"({hexPrefix}|{binaryPrefix})?([_0-9a-f]+)(.*)", RegexOptions.IgnoreCase).Groups;
return (prefix: groups[1].Value, number: groups[2].Value, suffix: groups[3].Value);
}
private static string AddSeparators(string numericText, int interval)
{
// Insert digit separators in the given interval.
var result = Regex.Replace(numericText, $"(.{{{interval}}})", "_$1", RegexOptions.RightToLeft);
// Fix for the case "0x_1111" that is not supported yet.
return result[0] == '_' ? result.Substring(1) : result;
}
private static bool IsIntegral(SpecialType specialType)
{
switch (specialType)
{
case SpecialType.System_Byte:
case SpecialType.System_SByte:
case SpecialType.System_Int16:
case SpecialType.System_UInt16:
case SpecialType.System_Int32:
case SpecialType.System_UInt32:
case SpecialType.System_Int64:
case SpecialType.System_UInt64:
return true;
default:
return false;
}
}
protected abstract (string hexPrefix, string binaryPrefix) GetNumericLiteralPrefixes();
private enum NumericKind { Unknown, Decimal, Binary, Hexadecimal }
private sealed class MyCodeAction : CodeAction.DocumentChangeAction
{
public MyCodeAction(string title, Func<CancellationToken, Task<Document>> createChangedDocument) : base(title, createChangedDocument)
{
}
}
}
}