提交 e7d33464 编写于 作者: A Andrew Casey

Introduce ObjectDisplayOptions.EscapeNonPrintableStringCharacters

It used to be always on in C# and implied by UseQuotes in VB.  Now it can
be manipulated separately.  This will allow us to print unescaped strings
in the C# interactive window.
上级 0bfa77a6
......@@ -255,8 +255,9 @@ public static string FormatLiteral(string value, ObjectDisplayOptions options)
}
var useQuotes = options.IncludesOption(ObjectDisplayOptions.UseQuotes);
var escapeNonPrintable = options.IncludesOption(ObjectDisplayOptions.EscapeNonPrintableStringCharacters);
var quote = useQuotes ? '"' : '\0';
if (!useQuotes && !ReplaceAny(value, quote))
if (!useQuotes && !(escapeNonPrintable && ReplaceAny(value, quote)))
{
return value;
}
......@@ -267,9 +268,16 @@ public static string FormatLiteral(string value, ObjectDisplayOptions options)
{
builder.Append(quote);
}
foreach (var c in value)
if (escapeNonPrintable)
{
FormatStringChar(builder, c, quote);
foreach (var c in value)
{
FormatStringChar(builder, c, quote);
}
}
else
{
builder.Append(value);
}
if (useQuotes)
{
......
......@@ -145,7 +145,7 @@ public static class SymbolDisplay
/// </remarks>
public static string FormatPrimitive(object obj, bool quoteStrings, bool useHexadecimalNumbers)
{
var options = ObjectDisplayOptions.None;
var options = ObjectDisplayOptions.EscapeNonPrintableStringCharacters;
if (quoteStrings)
{
options |= ObjectDisplayOptions.UseQuotes;
......@@ -168,7 +168,9 @@ public static string FormatPrimitive(object obj, bool quoteStrings, bool useHexa
/// </remarks>
public static string FormatLiteral(string value, bool quote)
{
return ObjectDisplay.FormatLiteral(value, quote ? ObjectDisplayOptions.UseQuotes : ObjectDisplayOptions.None);
var options = ObjectDisplayOptions.EscapeNonPrintableStringCharacters |
(quote ? ObjectDisplayOptions.UseQuotes : ObjectDisplayOptions.None);
return ObjectDisplay.FormatLiteral(value, options);
}
/// <summary>
......
......@@ -261,21 +261,33 @@ public void TypeSuffixes()
Assert.Equal("12.5M", FormatPrimitiveIncludingTypeSuffix(decimalValue, useHexadecimalNumbers: true));
}
[Fact]
public void StringEscaping()
{
const string value = "a\tb";
Assert.Equal("a\tb", ObjectDisplay.FormatPrimitive(value, ObjectDisplayOptions.None));
Assert.Equal("\"a\tb\"", ObjectDisplay.FormatPrimitive(value, ObjectDisplayOptions.UseQuotes));
Assert.Equal("a\\tb", ObjectDisplay.FormatPrimitive(value, ObjectDisplayOptions.EscapeNonPrintableStringCharacters));
Assert.Equal("\"a\\tb\"", ObjectDisplay.FormatPrimitive(value, ObjectDisplayOptions.UseQuotes | ObjectDisplayOptions.EscapeNonPrintableStringCharacters));
}
private string FormatPrimitive(object obj, bool quoteStrings = false)
{
return ObjectDisplay.FormatPrimitive(obj, quoteStrings ? ObjectDisplayOptions.UseQuotes : ObjectDisplayOptions.None);
var options = quoteStrings ? ObjectDisplayOptions.UseQuotes : ObjectDisplayOptions.None;
return ObjectDisplay.FormatPrimitive(obj, options | ObjectDisplayOptions.EscapeNonPrintableStringCharacters);
}
private string FormatPrimitiveUsingHexadecimalNumbers(object obj, bool quoteStrings = false)
{
var options = quoteStrings ? ObjectDisplayOptions.UseQuotes : ObjectDisplayOptions.None;
return ObjectDisplay.FormatPrimitive(obj, options | ObjectDisplayOptions.UseHexadecimalNumbers);
return ObjectDisplay.FormatPrimitive(obj, options | ObjectDisplayOptions.UseHexadecimalNumbers | ObjectDisplayOptions.EscapeNonPrintableStringCharacters);
}
private string FormatPrimitiveIncludingTypeSuffix(object obj, bool useHexadecimalNumbers = false)
{
var options = useHexadecimalNumbers ? ObjectDisplayOptions.UseHexadecimalNumbers : ObjectDisplayOptions.None;
return ObjectDisplay.FormatPrimitive(obj, options | ObjectDisplayOptions.IncludeTypeSuffix);
return ObjectDisplay.FormatPrimitive(obj, options | ObjectDisplayOptions.IncludeTypeSuffix | ObjectDisplayOptions.EscapeNonPrintableStringCharacters);
}
}
}
......@@ -31,8 +31,14 @@ internal enum ObjectDisplayOptions
UseHexadecimalNumbers = 1 << 2,
/// <summary>
/// Whether or not to quote character and string literals. In Visual Basic, this also enables pretty-listing of non-printable characters using ChrW function and vb* constants.
/// Whether or not to quote character and string literals.
/// </summary>
UseQuotes = 1 << 3,
/// <summary>
/// In C#, replace non-printable (e.g. control) characters with dedicated (e.g. \t) or unicode (\u0001) escape sequences.
/// In Visual Basic, replace non-printable characters with calls to ChrW and vb* constants.
/// </summary>
EscapeNonPrintableStringCharacters = 1 << 4,
}
}
......@@ -131,10 +131,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ObjectDisplay
Dim pooledBuilder = PooledStringBuilder.GetInstance()
Dim sb = pooledBuilder.Builder
Dim useQuotes = options.IncludesOption(ObjectDisplayOptions.UseQuotes)
Dim useHex = options.IncludesOption(ObjectDisplayOptions.UseHexadecimalNumbers)
For Each token As Integer In TokenizeString(value, useQuotes, useHex)
For Each token As Integer In TokenizeString(value, options)
sb.Append(ChrW(token And &HFFFF)) ' lower 16 bits of token contains the Unicode char value
Next
......@@ -362,9 +360,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ObjectDisplay
End Function
' TODO: consider making "token" returned by this function a structure to abstract bit masking operations
Friend Iterator Function TokenizeString(str As String, quote As Boolean, useHexadecimalNumbers As Boolean) As IEnumerable(Of Integer)
Friend Iterator Function TokenizeString(str As String, options As ObjectDisplayOptions) As IEnumerable(Of Integer)
Dim useQuotes = options.IncludesOption(ObjectDisplayOptions.UseQuotes)
Dim useHexadecimalNumbers = options.IncludesOption(ObjectDisplayOptions.UseHexadecimalNumbers)
Dim escapeNonPrintable = options.IncludesOption(ObjectDisplayOptions.EscapeNonPrintableStringCharacters)
If str.Length = 0 Then
If quote Then
If useQuotes Then
Yield Quotes()
Yield Quotes()
End If
......@@ -380,23 +382,26 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ObjectDisplay
Dim c = str(i)
i += 1
Dim wellKnown As String
Dim isNonPrintable As Boolean
Dim shouldEscape As Boolean
Dim isCrLf As Boolean
' vbCrLf
If c = s_Cr AndAlso i < str.Length AndAlso str(i) = s_Lf Then
If Not escapeNonPrintable Then
wellKnown = Nothing
shouldEscape = False
isCrLf = False
ElseIf c = s_Cr AndAlso i < str.Length AndAlso str(i) = s_Lf Then
wellKnown = "vbCrLf"
isNonPrintable = True
shouldEscape = True
isCrLf = True
i += 1
Else
wellKnown = GetWellKnownCharacterName(c)
isNonPrintable = wellKnown IsNot Nothing OrElse Not IsPrintable(c)
shouldEscape = wellKnown IsNot Nothing OrElse Not IsPrintable(c)
isCrLf = False
End If
If isNonPrintable Then
If quote Then
If shouldEscape Then
If useQuotes Then
If lastConcatenandWasQuoted Then
Yield Quotes()
lastConcatenandWasQuoted = False
......@@ -440,7 +445,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ObjectDisplay
Yield Character(c)
End If
Else
If isFirst AndAlso quote Then
If isFirst AndAlso useQuotes Then
Yield Quotes()
End If
......@@ -454,7 +459,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ObjectDisplay
End If
lastConcatenandWasQuoted = True
If c = """"c AndAlso quote Then
If c = """"c AndAlso useQuotes Then
Yield Quotes()
Yield Quotes()
Else
......@@ -463,7 +468,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ObjectDisplay
End If
End While
If quote AndAlso lastConcatenandWasQuoted Then
If useQuotes AndAlso lastConcatenandWasQuoted Then
Yield Quotes()
End If
End Function
......@@ -498,6 +503,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ObjectDisplay
Private Sub ValidateOptions(options As ObjectDisplayOptions)
' This option is not supported and has no meaning in Visual Basic...should not be passed...
Debug.Assert(Not options.IncludesOption(ObjectDisplayOptions.IncludeCodePoints))
Debug.Assert(Not options.IncludesOption(ObjectDisplayOptions.EscapeNonPrintableStringCharacters) Or options.IncludesOption(ObjectDisplayOptions.UseQuotes))
End Sub
End Module
......
......@@ -122,7 +122,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Public Function FormatPrimitive(obj As Object, quoteStrings As Boolean, useHexadecimalNumbers As Boolean) As String
Dim options = ObjectDisplayOptions.None
If quoteStrings Then
options = options Or ObjectDisplayOptions.UseQuotes
options = options Or ObjectDisplayOptions.UseQuotes Or ObjectDisplayOptions.EscapeNonPrintableStringCharacters
End If
If useHexadecimalNumbers Then
options = options Or ObjectDisplayOptions.UseHexadecimalNumbers
......@@ -135,7 +135,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Dim sb = pooledBuilder.Builder
Dim lastKind = -1
For Each token As Integer In ObjectDisplay.TokenizeString(str, quote:=True, useHexadecimalNumbers:=True)
For Each token As Integer In ObjectDisplay.TokenizeString(str, ObjectDisplayOptions.UseQuotes Or ObjectDisplayOptions.UseHexadecimalNumbers Or ObjectDisplayOptions.EscapeNonPrintableStringCharacters)
Dim kind = token >> 16
' merge contiguous tokens of the same kind into a single part
......
......@@ -370,7 +370,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
''' <summary> Creates a token with kind StringLiteralToken from a string value. </summary>
''' <param name="value">The string value to be represented by the returned token.</param>
Public Shared Function Literal(value As String) As SyntaxToken
Return Literal(VbObjectDisplay.FormatLiteral(value, ObjectDisplayOptions.UseQuotes), value)
Return Literal(VbObjectDisplay.FormatLiteral(value, ObjectDisplayOptions.UseQuotes Or ObjectDisplayOptions.EscapeNonPrintableStringCharacters), value)
End Function
''' <summary> Creates a token with kind StringLiteralToken from the text and corresponding string value. </summary>
......@@ -393,7 +393,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
''' <summary> Creates a token with kind CharacterLiteralToken from a character value. </summary>
''' <param name="value">The character value to be represented by the returned token.</param>
Public Shared Function Literal(value As Char) As SyntaxToken
Return Literal(VbObjectDisplay.FormatLiteral(value, ObjectDisplayOptions.UseQuotes), value)
Return Literal(VbObjectDisplay.FormatLiteral(value, ObjectDisplayOptions.UseQuotes Or ObjectDisplayOptions.EscapeNonPrintableStringCharacters), value)
End Function
''' <summary> Creates a token with kind CharacterLiteralToken from the text and corresponding character value. </summary>
......
......@@ -147,7 +147,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests
' non-printable characters are unchanged if quoting is disabled
Assert.Equal(s, FormatPrimitiveUsingHexadecimalNumbers(s, quoteStrings:=False))
Assert.Equal(s, ObjectDisplay.FormatLiteral(s, ObjectDisplayOptions.None))
Assert.Equal("""a"" & ChrW(&HFFFF) & ChrW(&HFFFE) & vbCrLf & ""b""", ObjectDisplay.FormatLiteral(s, ObjectDisplayOptions.UseQuotes Or ObjectDisplayOptions.UseHexadecimalNumbers))
Assert.Equal("""a"" & ChrW(&HFFFF) & ChrW(&HFFFE) & vbCrLf & ""b""", ObjectDisplay.FormatLiteral(s, ObjectDisplayOptions.UseQuotes Or ObjectDisplayOptions.EscapeNonPrintableStringCharacters Or ObjectDisplayOptions.UseHexadecimalNumbers))
' "well-known" characters:
Assert.Equal("""a"" & vbBack", FormatPrimitiveUsingHexadecimalNumbers("a" & vbBack, quoteStrings:=True))
......@@ -225,12 +225,22 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests
Assert.Equal("12.5D", FormatPrimitiveIncludingTypeSuffix(decimalValue, useHexadecimalNumbers:=True))
End Sub
<Fact>
Public Sub StringEscaping()
Const value = "a" & vbTab & "b"
Assert.Equal("a" & vbTab & "b", ObjectDisplay.FormatPrimitive(value, ObjectDisplayOptions.None))
Assert.Equal("""a" & vbTab & "b""", ObjectDisplay.FormatPrimitive(value, ObjectDisplayOptions.UseQuotes))
' Not allowed in VB: ObjectDisplay.FormatPrimitive(value, ObjectDisplayOptions.EscapeNonPrintableStringCharacters)
Assert.Equal("""a"" & vbTab & ""b""", ObjectDisplay.FormatPrimitive(value, ObjectDisplayOptions.UseQuotes Or ObjectDisplayOptions.EscapeNonPrintableStringCharacters))
End Sub
Private Function FormatPrimitive(obj As Object, Optional quoteStrings As Boolean = False) As String
Return ObjectDisplay.FormatPrimitive(obj, If(quoteStrings, ObjectDisplayOptions.UseQuotes, ObjectDisplayOptions.None))
Return ObjectDisplay.FormatPrimitive(obj, If(quoteStrings, ObjectDisplayOptions.UseQuotes Or ObjectDisplayOptions.EscapeNonPrintableStringCharacters, ObjectDisplayOptions.None))
End Function
Private Function FormatPrimitiveUsingHexadecimalNumbers(obj As Object, Optional quoteStrings As Boolean = False) As String
Dim options = If(quoteStrings, ObjectDisplayOptions.UseQuotes, ObjectDisplayOptions.None)
Dim options = If(quoteStrings, ObjectDisplayOptions.UseQuotes Or ObjectDisplayOptions.EscapeNonPrintableStringCharacters, ObjectDisplayOptions.None)
Return ObjectDisplay.FormatPrimitive(obj, options Or ObjectDisplayOptions.UseHexadecimalNumbers)
End Function
......
......@@ -205,7 +205,7 @@ internal override string FormatLiteral(char c, ObjectDisplayOptions options)
internal override string FormatLiteral(int value, ObjectDisplayOptions options)
{
return ObjectDisplay.FormatLiteral(value, options & ~ObjectDisplayOptions.UseQuotes);
return ObjectDisplay.FormatLiteral(value, options & ~(ObjectDisplayOptions.UseQuotes | ObjectDisplayOptions.EscapeNonPrintableStringCharacters));
}
internal override string FormatPrimitiveObject(object value, ObjectDisplayOptions options)
......@@ -217,5 +217,7 @@ internal override string FormatString(string str, ObjectDisplayOptions options)
{
return ObjectDisplay.FormatString(str, useQuotes: options.IncludesOption(ObjectDisplayOptions.UseQuotes));
}
internal override ObjectDisplayOptions QuotedStringOptions => ObjectDisplayOptions.UseQuotes;
}
}
......@@ -42,6 +42,17 @@ internal override bool IsWhitespace(char c)
return SyntaxFacts.IsWhitespace(c);
}
internal override ObjectDisplayOptions GetValueStringOptions(bool useQuotes)
{
var options = ObjectDisplayOptions.EscapeNonPrintableStringCharacters;
if (useQuotes)
{
options |= ObjectDisplayOptions.UseQuotes;
}
return options;
}
internal override string TrimAndGetFormatSpecifiers(string expression, out ReadOnlyCollection<string> formatSpecifiers)
{
expression = RemoveComments(expression);
......
......@@ -61,7 +61,7 @@ internal string GetValueString(DkmClrValue value, DkmInspectionContext inspectio
{
return IncludeObjectId(
value,
FormatPrimitive(value, options & ~ObjectDisplayOptions.UseQuotes, inspectionContext),
FormatPrimitive(value, options & ~(ObjectDisplayOptions.UseQuotes | ObjectDisplayOptions.EscapeNonPrintableStringCharacters), inspectionContext),
flags);
}
}
......@@ -348,7 +348,7 @@ internal string GetEditableValue(DkmClrValue value, DkmInspectionContext inspect
{
if (!value.IsNull)
{
return this.GetValueString(value, inspectionContext, ObjectDisplayOptions.UseQuotes, GetValueFlags.None);
return this.GetValueString(value, inspectionContext, QuotedStringOptions, GetValueFlags.None);
}
}
else if (type.IsCharacter())
......@@ -410,6 +410,8 @@ private static string IncludeObjectId(DkmClrValue value, string valueStr, GetVal
internal abstract string FormatString(string str, ObjectDisplayOptions options);
internal abstract ObjectDisplayOptions QuotedStringOptions { get; }
#endregion
}
}
// 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.Text;
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
......@@ -30,9 +29,7 @@ internal Formatter(string defaultFormat, string nullString, string staticMembers
string IDkmClrFormatter.GetValueString(DkmClrValue value, DkmInspectionContext inspectionContext, ReadOnlyCollection<string> formatSpecifiers)
{
var options = ((inspectionContext.EvaluationFlags & DkmEvaluationFlags.NoQuotes) == 0) ?
ObjectDisplayOptions.UseQuotes :
ObjectDisplayOptions.None;
ObjectDisplayOptions options = GetValueStringOptions((inspectionContext.EvaluationFlags & DkmEvaluationFlags.NoQuotes) == 0);
return GetValueString(value, inspectionContext, options, GetValueFlags.IncludeObjectId);
}
......@@ -67,6 +64,8 @@ string IDkmClrFormatter.GetUnderlyingString(DkmClrValue value, DkmInspectionCont
internal abstract bool IsWhitespace(char c);
internal abstract ObjectDisplayOptions GetValueStringOptions(bool useQuotes);
// Note: We could be less conservative (e.g. "new C()").
internal bool NeedsParentheses(string expr)
{
......
......@@ -171,6 +171,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
Return ObjectDisplay.FormatLiteral(str, options)
End Function
Friend Overrides ReadOnly Property QuotedStringOptions As ObjectDisplayOptions
Get
Return ObjectDisplayOptions.UseQuotes Or ObjectDisplayOptions.EscapeNonPrintableStringCharacters
End Get
End Property
End Class
End Namespace
\ No newline at end of file
......@@ -2,9 +2,6 @@
Imports System.Collections.ObjectModel
Imports Microsoft.CodeAnalysis.ExpressionEvaluator
Imports Microsoft.VisualStudio.Debugger.Clr
Imports Microsoft.VisualStudio.Debugger.ComponentInterfaces
Imports Microsoft.VisualStudio.Debugger.Evaluation
Imports Microsoft.VisualStudio.Debugger.Evaluation.ClrCompilation
Imports Microsoft.VisualStudio.Debugger.Metadata
......@@ -13,7 +10,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
''' <summary>
''' Computes string representations of <see cref="DkmClrValue"/> instances.
''' </summary>
Partial Friend NotInheritable Class VisualBasicFormatter : Inherits Formatter : Implements IDkmClrFormatter
Partial Friend NotInheritable Class VisualBasicFormatter : Inherits Formatter
''' <summary>
''' Singleton instance of VisualBasicFormatter (created using default constructor).
......@@ -26,25 +23,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
staticMembersString:=Resources.SharedMembers)
End Sub
Private Function IDkmClrFormatter_GetTypeName(inspectionContext As DkmInspectionContext, clrType As DkmClrType, clrTypeInfo As DkmClrCustomTypeInfo, formatSpecifiers As ReadOnlyCollection(Of String)) As String Implements IDkmClrFormatter.GetTypeName
Return GetTypeName(New TypeAndCustomInfo(clrType.GetLmrType(), clrTypeInfo), escapeKeywordIdentifiers:=False, sawInvalidIdentifier:=Nothing)
End Function
Private Function IDkmClrFormatter_GetUnderlyingString(clrValue As DkmClrValue, inspectionContext As DkmInspectionContext) As String Implements IDkmClrFormatter.GetUnderlyingString
Return GetUnderlyingString(clrValue, inspectionContext)
End Function
Private Function IDkmClrFormatter_GetValueString(clrValue As DkmClrValue, inspectionContext As DkmInspectionContext, formatSpecifiers As ReadOnlyCollection(Of String)) As String Implements IDkmClrFormatter.GetValueString
Dim options = If((inspectionContext.EvaluationFlags And DkmEvaluationFlags.NoQuotes) = 0,
ObjectDisplayOptions.UseQuotes,
ObjectDisplayOptions.None)
Return GetValueString(clrValue, inspectionContext, options, GetValueFlags.IncludeObjectId)
End Function
Private Function IDkmClrFormatter_HasUnderlyingString(clrValue As DkmClrValue, inspectionContext As DkmInspectionContext) As Boolean Implements IDkmClrFormatter.HasUnderlyingString
Return HasUnderlyingString(clrValue, inspectionContext)
End Function
Friend Overrides Function IsValidIdentifier(name As String) As Boolean
Return SyntaxFacts.IsValidIdentifier(name)
End Function
......@@ -61,6 +39,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator
Return SyntaxFacts.IsWhitespace(c)
End Function
Friend Overrides Function GetValueStringOptions(useQuotes As Boolean) As ObjectDisplayOptions
Return If(useQuotes,
ObjectDisplayOptions.UseQuotes Or ObjectDisplayOptions.EscapeNonPrintableStringCharacters,
ObjectDisplayOptions.None)
End Function
Friend Overrides Function TrimAndGetFormatSpecifiers(expression As String, ByRef formatSpecifiers As ReadOnlyCollection(Of String)) As String
expression = RemoveComments(expression)
expression = RemoveFormatSpecifiers(expression, formatSpecifiers)
......
......@@ -28,7 +28,7 @@ internal override string FormatLiteral(bool value)
internal override string FormatLiteral(string value, bool quote, bool useHexadecimalNumbers = false)
{
var options = ObjectDisplayOptions.None;
var options = ObjectDisplayOptions.EscapeNonPrintableStringCharacters;
if (quote)
{
options |= ObjectDisplayOptions.UseQuotes;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册