# Few questions about cli with crystal

**URL:** <https://forum.crystal-lang.org/t/few-questions-about-cli-with-crystal/5797>\
**Category:** Help & Support\
**Created:** [June 29, 2023, 7:47pm UTC](https://forum.crystal-lang.org/t/few-questions-about-cli-with-crystal/5797 "2023-06-29T19:47:24Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Fulgurance](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/fulgurance/32/1463_2.png) [@Fulgurance](https://forum.crystal-lang.org/u/Fulgurance)\
**Post date:** [June 29, 2023, 7:47pm UTC](https://forum.crystal-lang.org/t/few-questions-about-cli-with-crystal/5797/1 "2023-06-29T19:47:24Z")

</div>

Hi everyone!

As you know I am working on ISM, cli to manage a Linux system.

I am facing some smalls problem actually.

I would like to know if it’s possible when my program is running under TTY if it’s possible to print underlined characters (but I guess it’s not possible). If it’s not, how can I detect if the software is under a TTY and print differently characters?

The other thing is about to align items.

If for example in 3 different ligns I would like to align some text at the same column, is there any function / way to do that ?

Last question, if I print a sentence longer that the lign size, how can I manage with that ?  
Because ISM use sometime animated text, but if the text is longer than the lign, the text don’t erase the previous one, it will be duplicate

---

<div class="post-metadata">

**Author:** ![MistressRemilia](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/mistressremilia/32/2585_2.png) [@MistressRemilia](https://forum.crystal-lang.org/u/MistressRemilia)\
**Post date:** [June 29, 2023, 8:12pm UTC](https://forum.crystal-lang.org/t/few-questions-about-cli-with-crystal/5797/2 "2023-06-29T20:12:18Z")

</div>

> [@Fulgurance](#):
>
> I would like to know if it’s possible when my program is running under TTY if it’s possible to print underlined characters (but I guess it’s not possible). If it’s not, how can I detect if the software is under a TTY and print differently characters

In a Linux terminal emulator, this can be done with simple [ANSI sequences](https://en.wikipedia.org/wiki/ANSI_escape_code). The standard library has the Colorize module which has some ANSI stuff already in it, where you can do something like:

```crystal
STDOUT << "This text is " << "underlined".colorize.underline << " text\n"

```

Or with puts or printf or whatever you’d like.

Under the hood, it’s essentially equivalent to something like `STDOUT << "this is \e[4munderlined\e[24m text\n"`, but you don’t have to remember the actual escape sequences and codes.

> [@Fulgurance](#):
>
> The other thing is about to align items.
> 
> If for example in 3 different ligns I would like to align some text at the same column, is there any function / way to do that ?
> 
> Last question, if I print a sentence longer that the lign size, how can I manage with that ?  
> Because ISM use sometime animated text, but if the text is longer than the lign, the text don’t erase the previous one, it will be duplicate

Aligning is probably best done with printf and field widths. The documentation is [here](https://crystal-lang.org/api/1.8.2/toplevel.html#sprintf(format_string%2Cargs%3AArray%7CTuple)%3AString-class-method) and is very close (identical?) to printf in other languages.

For a sentence longer than the line size, I wrote my [own wrapping function](https://remilia.sdf.org/programming/docs/libremiliacr/RemiLib.html#buildWrapped%28text%3AString%2Cstr%3AIO%2C%2A%2Cindent%3AInt%3D0%2CfirstLineOffset%3AInt%3D0%2CmaxWidth%3AInt%3D80%2CindentChar%3AChar%3D%27%27%29-instance-method). The repo is [here](https://chiselapp.com/user/MistressRemilia/repository/libremiliacr/) if you want to use it. I don’t think there’s something like this in the standard library.

---

<div class="post-metadata">

**Author:** ![Fulgurance](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/fulgurance/32/1463_2.png) [@Fulgurance](https://forum.crystal-lang.org/u/Fulgurance)\
**Post date:** [June 29, 2023, 8:41pm UTC](https://forum.crystal-lang.org/t/few-questions-about-cli-with-crystal/5797/3 "2023-06-29T20:41:44Z")

</div>

Okay thanks a lot for your answer, I will try that when I will be back home.

Just only one thing I think you didn’t answer, how can I detect if the running terminal is a terminal emulator or a TTY?

---

<div class="post-metadata">

**Author:** ![MistressRemilia](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/mistressremilia/32/2585_2.png) [@MistressRemilia](https://forum.crystal-lang.org/u/MistressRemilia)\
**Post date:** [June 30, 2023, 2:09am UTC](https://forum.crystal-lang.org/t/few-questions-about-cli-with-crystal/5797/4 "2023-06-30T02:09:03Z")

</div>

That I’m not sure about. Technically a terminal is a TTY already.

---

<div class="post-metadata">

**Author:** ![pfischer](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/pfischer/32/330_2.png) [@pfischer](https://forum.crystal-lang.org/u/pfischer)\
**Post date:** [June 30, 2023, 10:37am UTC](https://forum.crystal-lang.org/t/few-questions-about-cli-with-crystal/5797/5 "2023-06-30T10:37:39Z")

</div>

If you want to distinguish between the TTY and the output to the file (\> or | on command line), just call this on the beggining of the app:

```crystal
Colorize.on_tty_only!

```

Doc: [Colorize - Crystal 1.8.2](https://crystal-lang.org/api/1.8.2/Colorize.html#on_tty_only%21-class-method)

---

<div class="post-metadata">

**Author:** ![Devonte](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/devonte/32/1927_2.png) [@Devonte](https://forum.crystal-lang.org/u/Devonte)\
**Post date:** [June 30, 2023, 5:35pm UTC](https://forum.crystal-lang.org/t/few-questions-about-cli-with-crystal/5797/6 "2023-06-30T17:35:16Z")

</div>

There is [`IO#tty?`](https://crystal-lang.org/api/1.8.2/IO.html#tty?:Bool-instance-method).

---

<div class="post-metadata">

**Author:** ![kees](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/kees/32/914_2.png) [@kees](https://forum.crystal-lang.org/u/kees)\
**Post date:** [July 1, 2023, 7:10am UTC](https://forum.crystal-lang.org/t/few-questions-about-cli-with-crystal/5797/7 "2023-07-01T07:10:22Z")

</div>

Did you have a look at the [curses library](https://en.wikipedia.org/wiki/Curses_%28programming_library%29)?

---

<div class="post-metadata">

**Author:** ![Fulgurance](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/fulgurance/32/1463_2.png) [@Fulgurance](https://forum.crystal-lang.org/u/Fulgurance)\
**Post date:** [September 1, 2023, 9:51am UTC](https://forum.crystal-lang.org/t/few-questions-about-cli-with-crystal/5797/8 "2023-09-01T09:51:31Z")

</div>

Hi guys, I am coming back to you because I have an another question about the CLI I am coding, because I almost finished to code all of the main features for my project.

Actually my CLI play sometime a text animation. But actually it’s just basically that:

`print text+"\r"`

But I am not happy to proceed like that, because if the terminal is resized, or if you are with a small resolution, the terminal will print duplicate text.

I would like to know how I can clear properly the text.

How should I proceed ?

---

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [September 1, 2023, 1:09pm UTC](https://forum.crystal-lang.org/t/few-questions-about-cli-with-crystal/5797/9 "2023-09-01T13:09:58Z")

</div>

> [@Fulgurance](#):
>
> I would like to know how I can clear properly the text.

Look into [ANSI Escape Codes · GitHub](https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797?permalink_comment_id=3857871#erase-functions). ANSI Escape Codes will allow you to do what you want.

---

<div class="post-metadata">

**Author:** ![Fulgurance](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/fulgurance/32/1463_2.png) [@Fulgurance](https://forum.crystal-lang.org/u/Fulgurance)\
**Post date:** [September 1, 2023, 2:15pm UTC](https://forum.crystal-lang.org/t/few-questions-about-cli-with-crystal/5797/10 "2023-09-01T14:15:56Z")

</div>

I am a bit lost.

If I would like to use that one:

 ![Screenshot_20230901_151420](https://canada1.discourse-cdn.com/flex036/uploads/crystal_lang/original/2X/9/99d35f20595c89a35acbca469fdce73fad519f48.png)

I should do like that ? Because it doesn’t work:

```auto
print "TestESC[1J"

```

Or like that?

```auto
"Test\x1B[1J"

```

---

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [September 1, 2023, 2:21pm UTC](https://forum.crystal-lang.org/t/few-questions-about-cli-with-crystal/5797/11 "2023-09-01T14:21:38Z")

</div>

`\x1B` yea. It’s listed up above in [ANSI Escape Codes · GitHub](https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797?permalink_comment_id=3857871#general-ascii-codes) as in general code used in all the other ones.

---

<div class="post-metadata">

**Author:** ![Fulgurance](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/fulgurance/32/1463_2.png) [@Fulgurance](https://forum.crystal-lang.org/u/Fulgurance)\
**Post date:** [September 1, 2023, 2:33pm UTC](https://forum.crystal-lang.org/t/few-questions-about-cli-with-crystal/5797/12 "2023-09-01T14:33:32Z")

</div>

So I am not sure the screenshot I shown is the solution.

The problem is, if the terminal is resized, or too small, how can I clean the right amout of characters ?

**I need to do like a backspace, but prevent the CLI to duplicate the text if the terminal is resized**

---

<div class="post-metadata">

**Author:** ![ralsina](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/ralsina/32/2139_2.png) [@ralsina](https://forum.crystal-lang.org/u/ralsina)\
**Post date:** [September 1, 2023, 8:23pm UTC](https://forum.crystal-lang.org/t/few-questions-about-cli-with-crystal/5797/13 "2023-09-01T20:23:34Z")

</div>

> [@pfischer](#):
>
> `Colorize.on_tty_only!`

Nice, I didn’t know that and I needed it :-)

---

<div class="post-metadata">

**Author:** ![paulocoghi](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/paulocoghi/32/1429_2.png) [@paulocoghi](https://forum.crystal-lang.org/u/paulocoghi)\
**Post date:** [September 3, 2023, 10:30am UTC](https://forum.crystal-lang.org/t/few-questions-about-cli-with-crystal/5797/14 "2023-09-03T10:30:20Z")

</div>

Wow! Valuable information here 🙂

---

<div class="post-metadata">

**Author:** ![Fulgurance](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/fulgurance/32/1463_2.png) [@Fulgurance](https://forum.crystal-lang.org/u/Fulgurance)\
**Post date:** [September 3, 2023, 12:38pm UTC](https://forum.crystal-lang.org/t/few-questions-about-cli-with-crystal/5797/15 "2023-09-03T12:38:47Z")

</div>

And about my question? XD

---

<div class="post-metadata">

**Author:** ![MistressRemilia](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/mistressremilia/32/2585_2.png) [@MistressRemilia](https://forum.crystal-lang.org/u/MistressRemilia)\
**Post date:** [September 3, 2023, 8:58pm UTC](https://forum.crystal-lang.org/t/few-questions-about-cli-with-crystal/5797/16 "2023-09-03T20:58:43Z")

</div>

> [@Fulgurance](#):
>
> The problem is, if the terminal is resized, or too small, how can I clean the right amout of characters ?
> 
> **I need to do like a backspace, but prevent the CLI to duplicate the text if the terminal is resized**

You might have to start tracking some state in your program since terminals aren’t going to be smart enough to know how to properly re-render text in all situations where the terminal size changes. You’ll have to track things like the current terminal size, how much you’ve printed, where the cursor is, etc…

On Unix platforms, you can get the terminal size like this (I don’t know if this got added to the stdlib since I originally wrote it, so it may already be present):

```crystal
lib LibC
  {% if flag?(:unix) %}
    # Bare minimum for the ioctl stuff we need

    TIOCGWINSZ = 0x5413u32

    struct Winsize
      ws_row : UInt16
      ws_col : UInt16
      ws_xpixel : UInt16
      ws_ypixel : UInt16
    end

    fun ioctl(fd : LibC::Int, what : LibC::ULong, ...) : LibC::Int
  {% end %}
end

{% if flag?(:unix) %}
  # Gets the height and width of the terminal (in that order).
  def getWinSize : Tuple(UInt16, UInt16)
    thing = LibC::Winsize.new
    LibC.ioctl(STDOUT.fd, LibC::TIOCGWINSZ, pointerof(thing))
    {thing.ws_row, thing.ws_col}
  end
{% end %}

```

From there, you’d move the cursor around, deleting and re-printing lines or segments of text as-needed.

The nuclear option would be to keep track of everything printed, then if the terminal is resized, completely clear it and redraw it 😛

I have absolutely no data to back this up, but my gut tells me that a user is more likely to grow the size of the terminal, not shrink it, unless they’re in a tiling window manager.

---

<div class="post-metadata">

**Author:** ![Fulgurance](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/fulgurance/32/1463_2.png) [@Fulgurance](https://forum.crystal-lang.org/u/Fulgurance)\
**Post date:** [September 5, 2023, 1:39pm UTC](https://forum.crystal-lang.org/t/few-questions-about-cli-with-crystal/5797/17 "2023-09-05T13:39:59Z")

</div>

So definitely it’s an answer. More tricky than I expected !

Okay I will put that on my list to do, but for later I think .

I didn’t think I will need to implement that. I thought there is a standard library for that

The only advantage I will have is my program is only intended to be for Linux, so I don’t need to check the platform

---

<div class="post-metadata">

**Author:** ![plambert](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/plambert/32/618_2.png) [@plambert](https://forum.crystal-lang.org/u/plambert)\
**Post date:** [September 6, 2023, 10:36pm UTC](https://forum.crystal-lang.org/t/few-questions-about-cli-with-crystal/5797/18 "2023-09-06T22:36:07Z")

</div>

The method I use to repaint the terminal and survive resizing is not perfect and can fail, but looks basically like this:

1. detect a tty with `STDOUT.tty?` or `STDERR.tty?` depending on where I’m writing, and if it’s not a tty, use `Log` to write updates instead of any of these things.
2. decide how many lines I need to use, and print that many linefeeds: `print '\n' * num_lines`
3. when printing my progress info, first send `"\e[%dA" % num_lines` in order to move up to where I should be
4. write my N lines of output, ending each line with `"\e[K"` to clear to end of line
5. in theory–i’ve not tried it–it should be possible to catch a SIGWNCH signal and use it to re-request the terminal dimensions in order to repaint appropriately. however, this can definitely still leave unexpected output if the resize causes lines to wrap that were not wrapped before. if this is an important problem, then i’d use the alternate screen (`"\e[?1049h"` to move to the alternate screen, `"\e[?1049l"` to move back when exiting the program) and instead of using the `"\e[A"` sequence to move up N lines, use `"\e[1;1H"` to always move to the start of the first line, allowing me to use the whole terminal display for my output.

---

<div class="post-metadata">

**Author:** ![MistressRemilia](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/mistressremilia/32/2585_2.png) [@MistressRemilia](https://forum.crystal-lang.org/u/MistressRemilia)\
**Post date:** [September 6, 2023, 11:29pm UTC](https://forum.crystal-lang.org/t/few-questions-about-cli-with-crystal/5797/19 "2023-09-06T23:29:44Z")

</div>

> [@plambert](#):
>
> n theory–i’ve not tried it–it should be possible to catch a SIGWNCH signal and use it to re-request the terminal dimensions in order to repaint appropriately.

I was trying that the other day and the signal didn’t want to fire, but this does seem to be a bit more ideal than constantly checking the terminal size every screen update. Maybe it was the terminal I was using ([Terminology](https://www.enlightenment.org/about-terminology)), or I could have been doing something wrong.

---

<div class="post-metadata">

**Author:** ![Fulgurance](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/fulgurance/32/1463_2.png) [@Fulgurance](https://forum.crystal-lang.org/u/Fulgurance)\
**Post date:** [September 22, 2023, 12:51pm UTC](https://forum.crystal-lang.org/t/few-questions-about-cli-with-crystal/5797/20 "2023-09-22T12:51:42Z")

</div>

I plan to use this solution, because I would like to make something really cool. I will try to use that, and I will come back to you if I have any questions
