Skip to content

Commit

Permalink
Merge pull request #185 from kindermannhubert/object-formatter-format…
Browse files Browse the repository at this point in the history
…ting

Formatting of output
  • Loading branch information
kindermannhubert authored Oct 29, 2022
2 parents 0a8a2df + 8855650 commit dc9da91
Show file tree
Hide file tree
Showing 44 changed files with 5,435 additions and 88 deletions.
4 changes: 4 additions & 0 deletions .github/codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@ coverage:
patch:
default:
enabled: no
ignore:
- "**/Microsoft.CodeAnalysis.CSharp.Symbols" # ignore folders and all its contents
- "**/Microsoft.CodeAnalysis.PooledObjects" # ignore folders and all its contents
- "**/Microsoft.CodeAnalysis" # ignore folders and all its contents
2 changes: 1 addition & 1 deletion CSharpRepl.Services/CSharpRepl.Services.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="6.0.0" />
<PackageReference Include="NuGet.PackageManagement" Version="6.3.1" />
<PackageReference Include="PrettyPrompt" Version="4.0.0" />
<PackageReference Include="PrettyPrompt" Version="4.0.1" />
<PackageReference Include="System.IO.Abstractions" Version="17.2.3" />
</ItemGroup>

Expand Down
65 changes: 34 additions & 31 deletions CSharpRepl.Services/Completion/AutoCompleteService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Classification;
using Microsoft.CodeAnalysis.Completion;
using Microsoft.CodeAnalysis.QuickInfo;
using Microsoft.Extensions.Caching.Memory;
using PrettyPrompt.Highlighting;

Expand Down Expand Up @@ -66,43 +65,47 @@ FormattedString GetDisplayText(CompletionItem item)
if (classification is not null &&
highlighter.TryGetColor(classification, out var color))
{
Span<char> prefix = stackalloc char[3];
if (configuration.UseUnicode)
{
var symbol = classification switch
{
ClassificationTypeNames.Keyword => "🔑",
ClassificationTypeNames.MethodName or ClassificationTypeNames.ExtensionMethodName => "🟣",
ClassificationTypeNames.PropertyName => "🟡",
ClassificationTypeNames.FieldName or ClassificationTypeNames.ConstantName or ClassificationTypeNames.EnumMemberName => "🔵",
ClassificationTypeNames.EventName => "",
ClassificationTypeNames.ClassName or ClassificationTypeNames.RecordClassName => "🟨",
ClassificationTypeNames.InterfaceName => "🔷",
ClassificationTypeNames.StructName or ClassificationTypeNames.RecordStructName => "🟦",
ClassificationTypeNames.EnumName => "🟧",
ClassificationTypeNames.DelegateName => "💼",
ClassificationTypeNames.NamespaceName => "",
ClassificationTypeNames.TypeParameterName => "",
_ => "",
};

Debug.Assert(symbol.Length <= prefix.Length);
symbol.CopyTo(prefix);
prefix[symbol.Length] = ' ';
prefix = prefix[..(symbol.Length + 1)];
}
else
{
prefix = Span<char>.Empty;
}

var prefix = GetCompletionItemSymbolPrefix(classification, configuration.UseUnicode);
return new FormattedString($"{prefix}{text}", new FormatSpan(prefix.Length, text.Length, new ConsoleFormat(Foreground: color)));
}
}
return text;
}
}

public static string GetCompletionItemSymbolPrefix(string? classification, bool useUnicode)
{
Span<char> prefix = stackalloc char[3];
if (useUnicode)
{
var symbol = classification switch
{
ClassificationTypeNames.Keyword => "🔑",
ClassificationTypeNames.MethodName or ClassificationTypeNames.ExtensionMethodName => "🟣",
ClassificationTypeNames.PropertyName => "🟡",
ClassificationTypeNames.FieldName or ClassificationTypeNames.ConstantName or ClassificationTypeNames.EnumMemberName => "🔵",
ClassificationTypeNames.EventName => "",
ClassificationTypeNames.ClassName or ClassificationTypeNames.RecordClassName => "🟨",
ClassificationTypeNames.InterfaceName => "🔷",
ClassificationTypeNames.StructName or ClassificationTypeNames.RecordStructName => "🟦",
ClassificationTypeNames.EnumName => "🟧",
ClassificationTypeNames.DelegateName => "💼",
ClassificationTypeNames.NamespaceName => "",
ClassificationTypeNames.TypeParameterName => "",
_ => "",
}; ;
Debug.Assert(symbol.Length <= prefix.Length);
symbol.CopyTo(prefix);
prefix[symbol.Length] = ' ';
prefix = prefix[..(symbol.Length + 1)];
return prefix.ToString();
}
else
{
return "";
}
}

private static async Task<FormattedString> GetExtendedDescriptionAsync(CompletionService completionService, Document document, CompletionItem item, SyntaxHighlighter highlighter)
{
var description = await completionService.GetDescriptionAsync(document, item);
Expand Down
13 changes: 13 additions & 0 deletions CSharpRepl.Services/Extensions/RoslynExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

using System;
using System.Reflection;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Classification;

Expand Down Expand Up @@ -93,4 +94,16 @@ public static Solution ApplyChanges(this Solution edit, Workspace workspace)
_ => null,
};
}

public static string? MemberTypeToClassificationTypeName(MemberTypes type)
{
return type switch
{
MemberTypes.Constructor or MemberTypes.Method => ClassificationTypeNames.MethodName,
MemberTypes.Event => ClassificationTypeNames.EventName,
MemberTypes.Field => ClassificationTypeNames.FieldName,
MemberTypes.Property => ClassificationTypeNames.PropertyName,
_ => null
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// 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.

#nullable disable

using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.Scripting.Hosting;

namespace Microsoft.CodeAnalysis.CSharp.Scripting.Hosting;

internal class CSharpMemberFilter : CommonMemberFilter
{
protected override bool IsGeneratedMemberName(string name)
{
// Generated fields, e.g. "<property_name>k__BackingField"
return GeneratedNames.IsGeneratedMemberName(name);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// 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.

#nullable disable

using System.Reflection;
using CSharpRepl.Services;
using CSharpRepl.Services.SyntaxHighlighting;
using Microsoft.CodeAnalysis.Scripting.Hosting;
using MemberFilter = Microsoft.CodeAnalysis.Scripting.Hosting.MemberFilter;

namespace Microsoft.CodeAnalysis.CSharp.Scripting.Hosting;

internal class CSharpObjectFormatterImpl : CommonObjectFormatter
{
protected override CommonTypeNameFormatter TypeNameFormatter { get; }
protected override CommonPrimitiveFormatter PrimitiveFormatter { get; }
protected override MemberFilter Filter { get; }

internal CSharpObjectFormatterImpl(SyntaxHighlighter syntaxHighlighter, Configuration config)
: base(syntaxHighlighter, config)
{
PrimitiveFormatter = new CSharpPrimitiveFormatter(syntaxHighlighter);
TypeNameFormatter = new CSharpTypeNameFormatter(PrimitiveFormatter);
Filter = new CSharpMemberFilter();
}

protected override string FormatRefKind(ParameterInfo parameter) => parameter.IsOut ? "out" : "ref";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// 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.Globalization;
using CSharpRepl.Services.SyntaxHighlighting;
using Microsoft.CodeAnalysis.Classification;
using Microsoft.CodeAnalysis.Scripting.Hosting;
using PrettyPrompt.Highlighting;

namespace Microsoft.CodeAnalysis.CSharp.Scripting.Hosting;

using static ObjectFormatterHelpers;

internal class CSharpPrimitiveFormatter : CommonPrimitiveFormatter
{
private readonly ConsoleFormat numericLiteralFormat;
private readonly ConsoleFormat stringLiteralFormat;
private readonly ConsoleFormat keywordFormat;

public CSharpPrimitiveFormatter(SyntaxHighlighter syntaxHighlighter)
{
numericLiteralFormat = new ConsoleFormat(Foreground: syntaxHighlighter.GetColor(ClassificationTypeNames.NumericLiteral));
stringLiteralFormat = new ConsoleFormat(Foreground: syntaxHighlighter.GetColor(ClassificationTypeNames.StringLiteral));
keywordFormat = new ConsoleFormat(Foreground: syntaxHighlighter.GetColor(ClassificationTypeNames.Keyword));
NullLiteral = new FormattedString("null", keywordFormat);
}

protected override FormattedString NullLiteral { get; }

protected override FormattedString FormatLiteral(bool value)
{
return new(ObjectDisplay.FormatLiteral(value), keywordFormat);
}

protected override FormattedString FormatLiteral(string value, bool useQuotes, bool escapeNonPrintable, int numberRadix = NumberRadixDecimal)
{
var options = GetObjectDisplayOptions(useQuotes: useQuotes, escapeNonPrintable: escapeNonPrintable, numberRadix: numberRadix);
return new(ObjectDisplay.FormatLiteral(value, options), stringLiteralFormat);
}

protected override FormattedString FormatLiteral(char c, bool useQuotes, bool escapeNonPrintable, bool includeCodePoints = false, int numberRadix = NumberRadixDecimal)
{
var options = GetObjectDisplayOptions(useQuotes: useQuotes, escapeNonPrintable: escapeNonPrintable, includeCodePoints: includeCodePoints, numberRadix: numberRadix);
return new(ObjectDisplay.FormatLiteral(c, options), stringLiteralFormat);
}

protected override FormattedString FormatLiteral(sbyte value, int numberRadix = NumberRadixDecimal, CultureInfo? cultureInfo = null)
{
return new(ObjectDisplay.FormatLiteral(value, GetObjectDisplayOptions(numberRadix: numberRadix), cultureInfo), numericLiteralFormat);
}

protected override FormattedString FormatLiteral(byte value, int numberRadix = NumberRadixDecimal, CultureInfo? cultureInfo = null)
{
return new(ObjectDisplay.FormatLiteral(value, GetObjectDisplayOptions(numberRadix: numberRadix), cultureInfo), numericLiteralFormat);
}

protected override FormattedString FormatLiteral(short value, int numberRadix = NumberRadixDecimal, CultureInfo? cultureInfo = null)
{
return new(ObjectDisplay.FormatLiteral(value, GetObjectDisplayOptions(numberRadix: numberRadix), cultureInfo), numericLiteralFormat);
}

protected override FormattedString FormatLiteral(ushort value, int numberRadix = NumberRadixDecimal, CultureInfo? cultureInfo = null)
{
return new(ObjectDisplay.FormatLiteral(value, GetObjectDisplayOptions(numberRadix: numberRadix), cultureInfo), numericLiteralFormat);
}

protected override FormattedString FormatLiteral(int value, int numberRadix = NumberRadixDecimal, CultureInfo? cultureInfo = null)
{
return new(ObjectDisplay.FormatLiteral(value, GetObjectDisplayOptions(numberRadix: numberRadix), cultureInfo), numericLiteralFormat);
}

protected override FormattedString FormatLiteral(uint value, int numberRadix = NumberRadixDecimal, CultureInfo? cultureInfo = null)
{
return new(ObjectDisplay.FormatLiteral(value, GetObjectDisplayOptions(numberRadix: numberRadix), cultureInfo), numericLiteralFormat);
}

protected override FormattedString FormatLiteral(long value, int numberRadix = NumberRadixDecimal, CultureInfo? cultureInfo = null)
{
return new(ObjectDisplay.FormatLiteral(value, GetObjectDisplayOptions(numberRadix: numberRadix), cultureInfo), numericLiteralFormat);
}

protected override FormattedString FormatLiteral(ulong value, int numberRadix = NumberRadixDecimal, CultureInfo? cultureInfo = null)
{
return new(ObjectDisplay.FormatLiteral(value, GetObjectDisplayOptions(numberRadix: numberRadix), cultureInfo), numericLiteralFormat);
}

protected override FormattedString FormatLiteral(double value, CultureInfo? cultureInfo = null)
{
return new(ObjectDisplay.FormatLiteral(value, ObjectDisplayOptions.None, cultureInfo), numericLiteralFormat);
}

protected override FormattedString FormatLiteral(float value, CultureInfo? cultureInfo = null)
{
return new(ObjectDisplay.FormatLiteral(value, ObjectDisplayOptions.None, cultureInfo), numericLiteralFormat);
}

protected override FormattedString FormatLiteral(decimal value, CultureInfo? cultureInfo = null)
{
return new(ObjectDisplay.FormatLiteral(value, ObjectDisplayOptions.None, cultureInfo), numericLiteralFormat);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// 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.

#nullable disable

using System;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.Scripting.Hosting;

namespace Microsoft.CodeAnalysis.CSharp.Scripting.Hosting;

internal class CSharpTypeNameFormatter : CommonTypeNameFormatter
{
protected override CommonPrimitiveFormatter PrimitiveFormatter { get; }

public CSharpTypeNameFormatter(CommonPrimitiveFormatter primitiveFormatter)
{
PrimitiveFormatter = primitiveFormatter;
}

protected override string GenericParameterOpening => "<";
protected override string GenericParameterClosing => ">";
protected override string ArrayOpening => "[";
protected override string ArrayClosing => "]";

protected override string GetPrimitiveTypeName(SpecialType type)
{
switch (type)
{
case SpecialType.System_Boolean: return "bool";
case SpecialType.System_Byte: return "byte";
case SpecialType.System_Char: return "char";
case SpecialType.System_Decimal: return "decimal";
case SpecialType.System_Double: return "double";
case SpecialType.System_Int16: return "short";
case SpecialType.System_Int32: return "int";
case SpecialType.System_Int64: return "long";
case SpecialType.System_SByte: return "sbyte";
case SpecialType.System_Single: return "float";
case SpecialType.System_String: return "string";
case SpecialType.System_UInt16: return "ushort";
case SpecialType.System_UInt32: return "uint";
case SpecialType.System_UInt64: return "ulong";
case SpecialType.System_Object: return "object";

default:
return null;
}
}

public override string FormatTypeName(Type type, CommonTypeNameFormatterOptions options)
{
string stateMachineName;
if (GeneratedNameParser.TryParseSourceMethodNameFromGeneratedName(type.Name, GeneratedNameKind.StateMachineType, out stateMachineName))
{
return stateMachineName;
}

return base.FormatTypeName(type, options);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// 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.

namespace Microsoft.CodeAnalysis.CSharp.Symbols;

internal static class GeneratedNameConstants
{
internal const char DotReplacementInTypeNames = '-';
internal const string SynthesizedLocalNamePrefix = "CS$";
internal const string SuffixSeparator = "__";
internal const char LocalFunctionNameTerminator = '|';
}
Loading

0 comments on commit dc9da91

Please sign in to comment.