varabyte / kotter

A declarative, Kotlin-idiomatic API for writing dynamic console applications.
Apache License 2.0
558 stars 16 forks source link

Strip ansi codes out of calls to textLine #12

Closed bitspittle closed 1 year ago

bitspittle commented 2 years ago

Kotter aims to be opinionated about its ansi output and (via the Virtual Console) the subset of ansi it supports, so don't let users add whatever they want into their strings

cbcmg commented 2 years ago

Hi. I just used kotter for the first time in a production app, and I love it. Great work. :)

I've previously been using jansi and some hand-rolled cursor manipulation for my apps, and need to write multi-coloured strings and e.g. hyperlinks to the console. Do you think there's any chance you might allow this kind of output via kotter? Thanks very much.

Some sample code I use...

val String.ansi
  get() = Ansi.ansi().render(this).toString()

val msg = "@|yellow restoring session|@ @|green $id |@".ansi

fun makeConsoleHyperlink(url: String, text: String, plain: Boolean = false) = if (plain) "$text ($url)" else makeOsc8Hyperlink(url, text)

fun makeOsc8Hyperlink(url: String, text: String) = "\u001B]8;;$url\u001B\\$text\u001B]8;;\u001B\\"
bitspittle commented 2 years ago

Hi. I just used kotter for the first time in a production app, and I love it. Great work. :)

Wow, thank you so much! I hope it was fun to use and is holding up for you in production.

... need to write multi-coloured strings ...

Running across the @|...| syntax in Ansi and not liking it was one of the things that motivated me to write Kotter in the first place :). I found that syntax somewhat bulky, fragile to typos, hard to read in longer strings, and annoying once you needed to start nesting.

So I wouldn't put support for it in the main library. That said, it might be possible for me to create a kotterx module which adds extension methods for working with Ansi formatted strings. So in that case you'd be able to do something like:

section {
   "@|yellow restoring session|@ @|green $id|@".renderInto(this)
}.run { ... }

I wouldn't be able to prioritize working on that for a while. Would you be amenable for now to just extending RenderScope in your codebase?

fun RenderScope.renderMessage() {
  yellow { text("restoring session") }
  text(' ')
  green { text(id) }
}

section {
   renderMessage()
}.run { ... }

This is a bit more verbose for sure but clearer and you can mix & match fg / bg / text effects together.

However, if you really wanted to use Ansi formatted strings and if you're feeling adventurous, you might try implementing that extension method yourself in your own codebase. It would look like this:

private class Chunk(val text: String, val color: Color? = null)

// e.g. "@|yellow restoring session|@ @|green $id|@" produces
// listOf(Chunk("restoring session", Yellow), Chunk(" "), Chunk("$id", Green))
private fun String.parseIntoChunks(): List<Chunk> {
    // TODO: The hard part goes here
}

fun String.renderInto(renderScope: RenderScope) {
   val chunks = this.parseIntoChunks()
   renderScope.apply {
       scopedState {
          for (chunk in chunks) {
             chunk.color?.let { color(it) } ?: clearColor()
             text(chunk.text)
          }
      }
   }
}

(The above is written from memory so I can't guarantee it would actually work without additional tweaks)

hyperlinks to the console.

I'm honestly unfamiliar with OSC sequences so I'd have to look into them more. I saw that they were an extension before so I didn't really pursue them.

By the way, I use hyperlinks in my own projects and it seems like all the terminals I've tried handle clicking on them if you hold CTRL / CMD down.

Here's a screenshot from my other project Kobweb:

kotter-hyperlink

and here's the link to the code that renders it.

Is there anything that OSC hyperlinks give you that I'm missing? Nevermind I figured it out shortly after sending out this comment. You'd be able to create links backing regular text. I'll add a feature request right now to look into that, although whether it will go in or not will depend on how widespread support for OSC hyperlinks are.

cbcmg commented 2 years ago

I found that syntax somewhat bulky, fragile to typos, hard to read in longer strings, and annoying once you needed to start nesting

Completely fair. It was all I had so it was what I used. For the first app I'm converting, using your recommended RenderScope style is perfectly adequate. I'll see how things turn out as I convert others.

Nevermind I figured it out shortly

: ) Yep, I've got some long urls (e.g. with GUIDs) in them that benefit from browser style link rendering.

I use iTerm, the one terminal to rule them all, so I'm spoilt for features. But I do have users using Gnome and Windows Terminal, so I need to keep them in mind.

I'll let you know how my kotter adventures go. Thanks again.

bitspittle commented 2 years ago

I'll see how things turn out as I convert others.

I appreciate your feedback. Feel free to keep letting me know how it goes. I've been mostly working in a bubble so it's very useful for me.

I use iTerm...

Yeah, when I make changes I test on ...

Linux: the default terminal, Konsole Mac: the default terminal, iTerm Windows: cmd, powershell, Windows Terminal

If hyperlinks work on all of those then I'll definitely implement them, probably even soon-ish (but no promises!)

bitspittle commented 1 year ago

I'm removing this issue as a 1.0 blocker.

I may still work on it before then, but I feel like this library is still fine without the feature. A user likely won't try to write ANSI escape codes into textLine unless they know what they're doing, and if they break their program because of it, they can just back out that code.

Also, leaving the ability to write ansi codes into textLine may be a secret way to allow people to add features that I myself can't support (or choose not to support because the feature isn't standard across enough terminals).

bitspittle commented 1 year ago

I'm going to go ahead and close this issue. If users actually run into problems with using ANSI commands in the actual Kotter programs, then I'll revisit it.