Skip to content

Prettily display System.Text.Rune instance #27595

Open
Ankith-B/PowerShell
#1

Description

Summary of the new feature / enhancement

PS> [Text.Rune]65

IsAscii             : True
IsBmp               : True
Plane               : 0
Utf16SequenceLength : 1
Utf8SequenceLength  : 1
Value               : 65


PS> [char]65
A

The result of the former should contain the string A because the current display result doesn't tell you what character it represents.

i.e. it should be displayed as "A" or "A (U+0041)".

Proposed technical implementation details (optional)

PS> "{0} (U+{1:X04})" -f [Text.Rune]65, ([Text.Rune]65).Value
A (U+0041)

PS> ([Text.Rune]65).ToString()
A

PS> "{0} (U+{1:X04})" -f [Text.Rune]0x1F600, ([Text.Rune]0x1F600).Value
😀 (U+1F600)

PS> "{0} (U+{1:X04})" -f [Text.Rune]0x1F600, ([Text.Rune]0x1F600).Value
𠮷 (U+20BB7)

Activity

  1. tats-u commented on Jun 12, 2026

    @tats-u
    Author

    csharprepl and the watch expression view in the debugger treat Rune specially:

    PS> csharprepl
    Welcome to the C# REPL (Read Eval Print Loop)!
    Type C# expressions and statements at the prompt and press Enter to evaluate them.
    Type help to learn more, exit to quit, and clear to clear your terminal.
    
    > new System.Text.Rune(0x1f600)
    U+1F600 '😀'
    

    Your argument is off the mark. What I'm saying is that Rune should be treated specially like char and DateTime. Is it really acceptable to simply ignore the fact that the following display remains a mess?

    PS> "😀😀😀👨‍👨‍👧‍👧".EnumerateRunes())
    
    IsAscii             : False
    IsBmp               : False
    Plane               : 1
    Utf16SequenceLength : 2
    Utf8SequenceLength  : 4
    Value               : 128512
    
    IsAscii             : False
    IsBmp               : False
    Plane               : 1
    Utf16SequenceLength : 2
    Utf8SequenceLength  : 4
    Value               : 128512
    
    IsAscii             : False
    IsBmp               : False
    Plane               : 1
    Utf16SequenceLength : 2
    Utf8SequenceLength  : 4
    Value               : 128512
    
    IsAscii             : False
    IsBmp               : False
    Plane               : 1
    Utf16SequenceLength : 2
    Utf8SequenceLength  : 4
    Value               : 128104
    
    IsAscii             : False
    IsBmp               : True
    Plane               : 0
    Utf16SequenceLength : 1
    Utf8SequenceLength  : 3
    Value               : 8205
    
    IsAscii             : False
    IsBmp               : False
    Plane               : 1
    Utf16SequenceLength : 2
    Utf8SequenceLength  : 4
    Value               : 128104
    
    IsAscii             : False
    IsBmp               : True
    Plane               : 0
    Utf16SequenceLength : 1
    Utf8SequenceLength  : 3
    Value               : 8205
    
    IsAscii             : False
    IsBmp               : False
    Plane               : 1
    Utf16SequenceLength : 2
    Utf8SequenceLength  : 4
    Value               : 128103
    
    IsAscii             : False
    IsBmp               : True
    Plane               : 0
    Utf16SequenceLength : 1
    Utf8SequenceLength  : 3
    Value               : 8205
    
    IsAscii             : False
    IsBmp               : False
    Plane               : 1
    Utf16SequenceLength : 2
    Utf8SequenceLength  : 4
    Value               : 128103
    
  2. tats-u commented on Jun 13, 2026

    @tats-u
    Author

    For TimeSpan, many of its public properties are genuinely useful. Don't compare it to Rune.

    Property Why they suck
    isAscii Immediately apparent from the hexadecimal representation of the code point. Superfluous.
    isBmp Same as above.
    Plane Same as above.
    Utf16SequenceLength Same as above.
    Utf8SequenceLength Rarely used.
    Value Display in hexadecimal rather than decimal.
  3. tats-u commented on Jun 13, 2026

    @tats-u
    Author

    The most logical pretty display of a rune would be the compound character itself that it represents. End users of scripts are not interested in the hexadecimal representations of code points. As a developer it may be of interest while debugging, but for that you can use whatever tool you want to see the internal representation.

    In other words, you agree with this first option ("A") of:

    i.e. it should be displayed as "A" or "A (U+0041)".

    because all values in the TimeSpan are derived from the same tick count and are representations of the same value using different units.

    As you also think, regarding TimeSpan, I personally prefer a dedicated, human-friendly display over the current way of just dumping all the properties. I'm not sure what specific format would be best, though.

  4. 237dmitry commented on Jun 13, 2026

    @237dmitry

    End users of scripts are not interested in the hexadecimal representations of code points.

    So many people, so many tasks. Perhaps the author specifically needs a hexadecimal representation of the result. For example, to get a combined symbol.

    ([Text.Rune[]][int[]][char[]]"☀️").Value.foreach({ '{0:x}' -f $_})
    # 2600
    # fe0f
    
    "`u{2600}`u{fe0f}"    # ☀️
    
  5. tats-u commented on Jun 13, 2026

    @tats-u
    Author

    Whether to display each code point has its pros and cons, so personally I don't mind either way. You would likely agree that the current display is terrible and needs to be corrected to either display style.

    Pros:

    • Reveals the identity of invisible characters
    • Provides immediate visibility of character code points
    • Displays identically to the C# debugger

    Cons:

    • Inconsistency between ToCharArray and EnumerateRunes
    • Lacks consistency in display compared to char values
  6. tats-u commented on Jun 15, 2026

    @tats-u
    Author

    Stop writing that pointless, self-indulgent XML nonsense like you're some outsider. I'm definitely not looking for workarounds - can't you understand that yet?

  7. tats-u commented on Jun 15, 2026

    @tats-u
    Author

    They are rather complicated for a workaround.

  8. tats-u commented on Jun 15, 2026

    @tats-u
    Author

    The formatting of DateTime, which is a struct like Rune, is completely different across PowerShell, dotnet-repl, and CsharpRepl. Why is it considered wrong to ask for similar support for DateTimeOffset and Rune?

  9. rhubarb-geek-nz commented on Jun 15, 2026

    @rhubarb-geek-nz

    Stop writing that pointless, self-indulgent XML nonsense like you're some outsider. I'm definitely not looking for workarounds - can't you understand that yet?

    PowerShell is an extensible system, it is designed to allow new formatters to be added without having to change the core product. If you see a need you can simply add a formatter without waiting for it to be triaged, validated, approved, implemented and added to a future release where none of those steps are guaranteed. I was working through the process of how to implement this for the rune type. I have packaged my proof of concept so you only need to install it from PSGallery and import it into the process where you need it. If you don't like it, that is fine but what you are likely to get from the PowerShell team if approved is a similar view formatter like the other existing supported types.

  10. tats-u commented on Jun 15, 2026

    @tats-u
    Author

    It wasn't a workaround—just a proof of concept. I wish you'd mentioned that earlier; otherwise, you came off like an outsider who keeps interrupting ideas without understanding them. My misunderstanding was my fault—sorry about that.

  11. mklement0 commented on Jun 15, 2026

    @mklement0
    Contributor

    Thanks for the proof-of-concept, rhubarb-geek-nz.

    Here's example formatting applied to the output from [System.Text.Rune] 0x1F600:

    ToString Value
    -------- -----
    😀       0x1F600
    

    (For someone looking to experiment, run Install-PSResource rhubarb-geek-nz.RuneWithAView to install the module, then explicitly import the module (Import-Module rhubarb-geek-nz.RuneWithAView), which adds the custom formatting instructions for [System.Text.Rune] to the session via Update-FormatData)

    Personally, I think even more columns could be added, such as:

    • IsBmp to indicate if the rune is a BMP character (if it is not, the implication is that two [char] instances are required to represent it); true, you can deduce the same information from whether the code point is >= 0x10000, but this makes it easier to recognize at a glance.
    • An expandable string literal with the necessary escape sequence(s) to produce the rune; e.g. "`u{d83d}`u{de00}" in the case at hand; this would imply the UTF-16 code unit(s) that constitute the rune.
    • The byte(s) that make up the UTF-8 encoding of the rune at hand, e.g. 0xf0, 0x9f, 0x98, 0x80 in the case at hand.

    As an (inconsequential) aside:

  12. rhubarb-geek-nz commented on Jun 16, 2026

    @rhubarb-geek-nz
    • An expandable string literal with the necessary escape sequence(s) to produce the rune; e.g. "`u{d83d}`u{de00}" in the case at hand; this would imply the UTF-16 code unit(s) that constitute the rune.
    • The byte(s) that make up the UTF-8 encoding of the rune at hand, e.g. 0xf0, 0x9f, 0x98, 0x80 in the case at hand.

    I suggest those options should be available from a string rather than individual runes, if you want to get it for a rune then cast it to a string first. A string, rune or character are UNICODE representations and don't know about any byte encodings or conversions. System.Text.Encoding is used for that. The best I could find out of the box was

    PS> [String][Text.Rune]0x1F600 | Format-Hex
    
       Label: String (System.String) <18BE77D1>
    
              Offset Bytes                                           Ascii
                     00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
              ------ ----------------------------------------------- -----
    0000000000000000 F0 9F 98 80                                     �
    

    and

    PS> [string][Text.Rune]0x1F600 | Format-Hex -Encoding utf32
    
       Label: String (System.String) <1B381031>
    
              Offset Bytes                                           Ascii
                     00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
              ------ ----------------------------------------------- -----
    0000000000000000 00 F6 01 00                                      ö�
    

    A tool to perform the conversions you mention on strings would be more generally useful rather than individual runes. Eg convert an entire string to the escaped hex representation or plain hexadecimal UTF8 representations.

  13. rhubarb-geek-nz commented on Jun 16, 2026

    @rhubarb-geek-nz

    There are limits to what views can do, they can't be parameterized, you just have List, Table and Wide. Also they treat every record as an individual record no matter how small.

    In the case of runes, I suggest it is more useful using them as a collection, a single rune is just a very small collection. This is also the natural mode of PowerShell where it tries to treat multiples simply.

    I haven't found a pretty way of showing runes within strings in their PowerShell source code form, and this differs from C# source code form for strings.

    So rhubarb-geek-nz.ItEscapesMe will turn a string of non-ASCII characters into their PowerShell source form. Useful if your file system defaults to CP-850.

    PS> "Hello World" | Out-String | ConvertTo-EscapeString
    Hello World`r`n
    PS> [Text.Rune]128512 | ConvertTo-EscapeString
    `u{1F600}
    PS> "😀😁" | ConvertTo-EscapeString
    `u{1F600}`u{1F601}
    
  14. mklement0 commented on Jun 16, 2026

    @mklement0
    Contributor

    tl;dr:

    I'd personally be happy with the originally proposed representation, code point + printed character only
    However, in your proof-of-concept, I suggest renaming ToString to UnicodeCharacter; Value (as opposed to CodePoint) isn't a great name either, but it's better to stick with the actual property name.


    (Tangent alert.)

    Note that Rune is concerned with encoding, as evidenced by the Utf16SequenceLength and Utf8SequenceLength properties, albeit only abstractly; but perhaps what I proposed - which would be useful in both a rune and a string context - packs too much functionality into the formatting layer that the underlying type doesn't provide.

    As an aside, it is curious that Format-Hex defaults to UTF-8, given that the internal encoding of .NET strings is UTF-16; Format-Hex -Unicode [sic] is needed to glean the bytes that make up the latter encoding, in little Endian form.

    ConvertTo-EscapeString looks useful, thanks.
    Years ago I created Debug-String, which expresses the Unicode escapes as UTF-16 code units, however; e.g.
    '😀' | Debug-String -UnicodeEscapes yields `u{d83d}`u{de00}

  15. rhubarb-geek-nz commented on Jun 16, 2026

    @rhubarb-geek-nz

    While this is technically interesting I don't see this as being of general use. To me it seems an incredibly niche requirement for a very small set of developers.
    In my experience developers write deployment PowerShell scripts and operations run those scripts while following deployment instructions.
    I can't imagine a situation where I was trying to trouble-shoot a deployment with operations where I had to ask what the runes were saying.
    Aside from developers who have IDEs, SDKs and various other tools, what is the audience for this?

  16. rhubarb-geek-nz commented on Jun 17, 2026

    @rhubarb-geek-nz

    I suggest renaming ToString to UnicodeCharacter

    The reason I chose ToString is because

    • that is a method that people are familiar with
    • it is actually a dotnet string containing however many char values it needs
    • it is literally the result of calling ToString() on that rune, actually done with a simple cast to string in the XML
    • it is the string representation of the rune
    • the label is short

    Funnily enough the table view gives it italics because it detects it is not a real property

    This is at the moment proof of concept to demonstrate the mechanics, the labels are just window dressing. If you want to tweak the labels you can edit the XML to see what it looks like.

    I would not recommend a long label for a value that is only one character width.

  17. added
    WG-ReviewedA Working Group has reviewed this and made a recommendation
    Up-for-GrabsUp-for-grabs issues are not high priorities, and may be opportunities for external contributors
    and removed
    Needs-TriageThe issue is new and needs to be triaged by a work group.
    WG-NeedsReviewNeeds a review by the labeled Working Group
    on Sep 2, 2026
  18. theJasonHelmick commented on Sep 2, 2026

    @theJasonHelmick
    Collaborator

    The interactive WG has reviewed this issue. We are marking this as Up-For-Grabs and would like to see a table view.

  19. iSazonov commented on Sep 3, 2026

    @iSazonov
    Collaborator

    From the conclusion it is clear that a table view should be default.

    Still open questions are:

    1. Should we add a (1) column or (2) ETS property with graphical representation of the rune? In the default table view? In additional table view? Should we extend list view too?
    2. Makes sense to add new ETS method ToDebugString() like https://source.dot.net/#System.Private.CoreLib/src/runtime/src/libraries/System.Private.CoreLib/src/System/Text/Rune.cs,156 ?
  20. Ankith-B commented on Sep 6, 2026

    @Ankith-B

    I'd like to take this. Planning to add a Rune view to DotNetTypes_format_ps1xml.cs following the existing simple value-type formatters, displaying the character alongside its code point. Will submit a PR with a Pester test.

  21. GeeLaw commented on Sep 16, 2026

    @GeeLaw

    Ankith Burki (@Ankith-B) You opened a PR in your fork, which will not affect Microsoft's PowerShell repository. I also suggest that the format be simplified to make Rune mimic a primitive type. See Michael Klement (@mklement0) 's version 5 years ago: #15100 (comment)

  22. GeeLaw commented on Sep 21, 2026

    @GeeLaw

    On the table view... I'm currently developing a UTF-related library and the column headers I have used for table-viewing a UTF-related type in its string representation are... $_ (for UTF strings) or ToString() (for UTF encoding units).

  23. Ankith-B commented on Oct 1, 2026

    @Ankith-B

    Are there any edits I can make that would lead to it affecting Microsoft's PowerShell repository.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Issue-Enhancementthe issue is more of a feature request than a bugUp-for-GrabsUp-for-grabs issues are not high priorities, and may be opportunities for external contributorsWG-Interactive-Consolethe console experienceWG-ReviewedA Working Group has reviewed this and made a recommendation

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions