1

Hopefully this isn't impossible, but I want access to the console's ability to print that little blinking cursor.

I'm implementing a mini terminal that can sit inside Write-Progress whilst another program writes to the actual console in the foreground.
So far I've managed to put in the control codes for the usual colours, negatives, italics, and blink, but the best I can do for the cursor itself is an underline next to where you're typing.

This is confusing as the user not being able to see where exactly they're inserting in when arrowing back in the input, and is more complicated as the dev, writing two codepoints on and after (or before if highlighting backwards..) the caret character, than just being able to insert a single zero-width one.

Obviously the console has the ability to draw these glyphs since it's right there, but is there any way to get at it similar to how we can almost get at these other features?
At this point I'd accept control codes that work in terminals other than Windows Terminal; just so I can take that to them as a feature request. I just cant find anything.

enter image description here

Hashbrown
  • 12,091
  • 8
  • 72
  • 95
  • What about ``"`u{23B8}"``? – Mathias R. Jessen Jun 25 '23 at 15:46
  • 1
    That just prints a regular character, which is still locked in width with the monospace font. [Try to print two adjoining characters and see how it differs to the prompt caret when typing](https://i.imgur.com/D26zfry.png) – Hashbrown Jun 25 '23 at 16:05
  • I must have misread your question.... why would you need the "cursor" character in the middle of the string? – Mathias R. Jessen Jun 25 '23 at 16:08
  • because when you're typing in the terminal I'm writing, the underline doesn't cut it if you're [going back to edit something you wrote](https://i.imgur.com/jHau3Iu.png) – Hashbrown Jun 25 '23 at 16:13
  • you dont need to replace the whole cell, just blink+negative works, you can see that in the clip right at the end; I use it when the user has highlighted some text (which means I cant use it when *not* highlighting text, because it'd be ambiguous) – Hashbrown Jun 25 '23 at 16:35
  • then how will you know which end of the highlight is the cursor? – Hashbrown Jun 25 '23 at 16:49
  • @mklement0 of course it makes a difference! [It tells the user](https://i.imgur.com/nEb9drM.gif) whether continuing to press shift-left/right (or just left/right to exit highlight) will grow/shrink (or exit) at the left or right! This is definitely a discussion (on "why") now, and isn't going to answer the question – Hashbrown Jun 26 '23 at 04:14
  • No worries. Yeah I already acknowledged that comment; that's exactly what's already implemented. You can see it in the gif when `ShiftRightArrow` appears. What makes the original suggestion not-feasible is there's no distinction between highlighting one character and no highlight at all (both reverse and blink a single char). It is cool to know that pwsh finally has [the styling stuff just included now](https://stackoverflow.com/a/60684606/2518317), until it's installed by default via windows update though I need to keep my stuff portable (so no `"\`u{12345}"` :( ) – Hashbrown Jun 26 '23 at 13:18
  • nah, as linked I already had a solution for ansi codes years before it was implemented in 7, [strikingly similar with `$style.underline`/`$style.nounderline`](https://gist.github.com/Hashbrown777/a5a02e2fd3eeed4485d4ba073ef3b143#file-style-ps1). What I'd really want is a poly for is `"hello\`u{12345}there"`, where my solution was an *almost* as unintrusive [`'hello',0x12345,'there' | Unicode`](https://gist.github.com/Hashbrown777/a5a02e2fd3eeed4485d4ba073ef3b143#file-unicode-ps1), but without something akin to javascript's template literal `raw` you dont really get access to escape sequences – Hashbrown Jun 27 '23 at 01:48
  • @mklement0 Nah the point of a polyfill is to ease development by providing a stable implementation of API's you know for platforms you do not natively support, no wheel reinventing. I have no need of it because I already natively supported PS5 (*and* 7), and it would be *harder* to use the polyfill because I need to 'compile' it first? If this existed *before*? Sure. But looking again even though you've preloaded it it's just a mess and I'm quite fond of the elegance of mine (not to mention it's not even complete; it doesn't look like it supports RGB and XTERM colours!). – Hashbrown Jun 27 '23 at 05:07

1 Answers1

0

Preface:

  • The following builds in part on your own efforts and feedback.

  • The suggested solution is an imperfect cursor emulation, which may or may not work for you; hopefully it is at least helpful to future readers.


  • To fully emulate a cursor visually, you'd need two features that I don't think that any terminal exposes (if anybody knows of a terminal that does, do let us know):

    • The ability to superimpose one glyph (symbolizing a cursor) over another (the character in a cell),
    • The ability to make only the superimposed one blink.
  • If you're willing to forgo the specific thin-bar cursor style that Windows Terminal uses (notably, different terminals have different default cursor styles, but usually offer customization), you can use a combination of reverse video and blinking for the display cell that represents the emulated cursor position; this is the style that the native macOS terminal (Terminal.app) uses by default, for instance.

    • While you're also using reverse video - but not blinking - for selecting (highlighting) text, the blinking aspect serves to visually distinguish selected text from the cursor.

    • The only edge case that requires a compromise is the case where a single cell is selected at the cursor position.

      • In insert mode - the default - these two cases must be visually distinct to inform the user what will happen when they type a character: in the single-cell selection case, the typed character replaces the current one in the cell, whereas in the cursor-position-only case the typed characters is inserted.

      • Since a single cell cannot be both blinking and non-blinking, the compromise is to then not blink. That is, a single-cell selection at the cursor position would then only reverse video, which would serve to distinguish it from the non-selection cursor position.

    • Limitations:

      • The blinking seems to be slower than the blinking rate of the actual cursor in most terminal applications; not sure if this can be controlled.

      • Some terminals have support for blinking disabled by default, and require opt-in via their settings; an example is iTerm2.app, a popular third-party terminal for macOS.

      • A currently moot point regarding insert vs. replace (overstrike) mode:

        • Using a whole-cell cursor emulation doesn't allow you to visually signal which mode is in effect, the way that conhost.exe windows do - but not Windows Terminal (the cursor height increases in replace mode).

        • However:

          • PowerShell uses the PSReadLine module for its command-line editing, which fundamentally doesn't support replace (overstrike mode) via the usual Insert keypress on Windows, as of v2.2.6 (the version that ships with the most recent stable PowerShell version as of this writing, v7.3.5)

          • Unix terminals apparently fundamentally do not themselves support an insert/replace toggle; while switching between these modes can be done in vi editing mode, using Esc-Shift-R to switch to replace mode, and Esc-I to switch back to insert mode (with Set-PSReadLineOption -EditMode vi, this technique even works on Windows), the cursor never reflects that.

The following sample code and screenshot demonstrate this:

  • Note: The code uses the automatic $PSStyle variable, which has properties with symbolic names for ANSI (VT) escape sequences, which can be embedded inside "..." via $(...). $PSStyle requires PowerShell (Core) 7.2+, but a polyfill for Windows PowerShell is provided in the bottom section.

  • Before $PSStyle became available, you've created a custom version, which offers additional features, notably templates for arbitrary RGB colors and XTerm colors.

# Note: In Windows PowerShell, place the polyfill from below here.

# Define helper variables for styling selections and the cursor.
$sel1 = $PSStyle.Reverse
$sel0 = $PSStyle.ReverseOff
$cur1 = $PSStyle.Reverse + $PSStyle.Blink
$cur0 = $PSStyle.ReverseOff + $PSStyle.BlinkOff

# Print sample strings.
"A cursor only: ${cur1} ${cur0}"
"${sel1}Selected text with the cursor on one en${sel0}${cur1}d${cur0}"
"${sel1}Selected text${sel0} with separate cursor (requires mouse support): ${cur1} ${cur0}"
"Edge case: Cursor position coincides with single-character selection${sel1}:${sel0}"

This produces the following display:

screenshot of emulated cursors


$PSStyle polyfill for Windows PowerShell:
  • The following emulates the automatic $PSStyle variable - which is available in PowerShell (Core) 7.2+ only - for use in lower versions, notably Windows PowerShell.

  • Note that only the properties with general-purpose ANSI (VT) escape sequences are emulated, such as $PSStyle.Blink and $PSStyle.Foreground.Yellow

if ($null -eq $PSStyle) {
  $PSStyle = [ordered] @{}
   $PSStyle.Reset = "$([char]27)[0m"
   $PSStyle.BlinkOff = "$([char]27)[25m"
   $PSStyle.Blink = "$([char]27)[5m"
   $PSStyle.BoldOff = "$([char]27)[22m"
   $PSStyle.Bold = "$([char]27)[1m"
   $PSStyle.DimOff = "$([char]27)[22m"
   $PSStyle.Dim = "$([char]27)[2m"
   $PSStyle.Hidden = "$([char]27)[8m"
   $PSStyle.HiddenOff = "$([char]27)[28m"
   $PSStyle.Reverse = "$([char]27)[7m"
   $PSStyle.ReverseOff = "$([char]27)[27m"
   $PSStyle.ItalicOff = "$([char]27)[23m"
   $PSStyle.Italic = "$([char]27)[3m"
   $PSStyle.UnderlineOff = "$([char]27)[24m"
   $PSStyle.Underline = "$([char]27)[4m"
   $PSStyle.StrikethroughOff = "$([char]27)[29m"
   $PSStyle.Strikethrough = "$([char]27)[9m"
   $PSStyle.Foreground = [ordered] @{}
     $PSStyle.Foreground.Black = "$([char]27)[30m"
     $PSStyle.Foreground.Red = "$([char]27)[31m"
     $PSStyle.Foreground.Green = "$([char]27)[32m"
     $PSStyle.Foreground.Yellow = "$([char]27)[33m"
     $PSStyle.Foreground.Blue = "$([char]27)[34m"
     $PSStyle.Foreground.Magenta = "$([char]27)[35m"
     $PSStyle.Foreground.Cyan = "$([char]27)[36m"
     $PSStyle.Foreground.White = "$([char]27)[37m"
     $PSStyle.Foreground.BrightBlack = "$([char]27)[90m"
     $PSStyle.Foreground.BrightRed = "$([char]27)[91m"
     $PSStyle.Foreground.BrightGreen = "$([char]27)[92m"
     $PSStyle.Foreground.BrightYellow = "$([char]27)[93m"
     $PSStyle.Foreground.BrightBlue = "$([char]27)[94m"
     $PSStyle.Foreground.BrightMagenta = "$([char]27)[95m"
     $PSStyle.Foreground.BrightCyan = "$([char]27)[96m"
     $PSStyle.Foreground.BrightWhite = "$([char]27)[97m"
   $PSStyle.Background = [ordered] @{}
     $PSStyle.Background.Black = "$([char]27)[40m"
     $PSStyle.Background.Red = "$([char]27)[41m"
     $PSStyle.Background.Green = "$([char]27)[42m"
     $PSStyle.Background.Yellow = "$([char]27)[43m"
     $PSStyle.Background.Blue = "$([char]27)[44m"
     $PSStyle.Background.Magenta = "$([char]27)[45m"
     $PSStyle.Background.Cyan = "$([char]27)[46m"
     $PSStyle.Background.White = "$([char]27)[47m"
     $PSStyle.Background.BrightBlack = "$([char]27)[100m"
     $PSStyle.Background.BrightRed = "$([char]27)[101m"
     $PSStyle.Background.BrightGreen = "$([char]27)[102m"
     $PSStyle.Background.BrightYellow = "$([char]27)[103m"
     $PSStyle.Background.BrightBlue = "$([char]27)[104m"
     $PSStyle.Background.BrightMagenta = "$([char]27)[105m"
     $PSStyle.Background.BrightCyan = "$([char]27)[106m"
     $PSStyle.Background.BrightWhite = "$([char]27)[107m"
}
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • mm, I think the real answer is `"Hopefully it isn't impossible..."; yeah sorry buddy it is :(`. I tried a few terminals in a real x-session on linux and they don't even have a cursor like windows terminal (they just flash the whole char as if in Replace mode :/ ). And how they deal with highlighting? Only one supported it (via kbd)! *And* that one that did; it didn't support highlighting one character. It looks like the solution I had (underline, then negative for a selection with a blink on the actual caret) is the best one can do to communicate all states – Hashbrown Jun 28 '23 at 01:03
  • One did support overline, and another did *seem* to support left-line (EXACTLY what we want), but it was exceptionally buggy, highlighting to the left of the whole line and sporadically enabling and disabling itself (the blue bar on Konsole) https://i.imgur.com/qGzzLnC.png Hopefully one day, like with pwsh' new style and unicode escape sequence support Windows Terminal can add exposed whatever it's doing and in so reintroduce support for this old forgotten sequence.. and have a leg-up on linux! – Hashbrown Jun 28 '23 at 01:05
  • @Hashbrown, having an insert-vs.-replace (overstrike) indicator would be nice, but is currently a moot point on Windows, and fundamentally moot in Unix terminals - please see my update. On a meta note: I suggest cleaning up your comments on your question, now that I've removed mine. – mklement0 Jun 28 '23 at 16:44
  • @Hashbrown: Given that terminals aren't designed for multiple cursors and for letting programs emulate cursors, I'd be surprised if such APIs are ever introduced, but if you end up submitting a feature request at https://github.com/microsoft/terminal/issues, please share the link here. – mklement0 Jun 28 '23 at 16:56
  • nah I like the history and an explanation should anyone else come in with the same reasoning. I mean there's n-curses, drawille, and powershell didn't even support [displaying] unicode until windows terminal; there's always room for improvement outside of the thoughts of the original devs! If it happens one day for this, like with all the other necro'd questions, it'll be put here haha – Hashbrown Jun 29 '23 at 03:18
  • @Hashbrown: Nah, no one's gonna read a conversation with one half missing, especially in comments. Nah, PowerShell had Unicode support since v1; however, for (more) complete Unicode _rendering_, historically you had to switch to a different font in regular console windows (`conhost.exe`). – mklement0 Jun 29 '23 at 12:12
  • "even support [displaying] unicode" that's literally what I said – Hashbrown Jun 29 '23 at 12:52
  • @Hashbrown: Nah, that was ambiguous and potentially confusing, so I clarified. I think this conversation has run its course. – mklement0 Jun 29 '23 at 12:53
  • @Hashbrown: Your abrasive communication style notwithstanding, you made good points and cleared up some of my misconceptions. When I felt I had clarity, I wrote up the findings in an answer _aimed at future readers_. I had a hunch _you_ wouldn't appreciate it, and the preface provided enough context so that you would know whether the too-long-for-_you_ answer was worth your time. I learned something in the process, and perhaps future readers will too. I also learned that, after "of course!", a dozen "nah"s, and your most recent comment, I am not interesting in engaging with you anymore. – mklement0 Jun 29 '23 at 13:21