Hacker News new | ask | show | jobs
by mulmen 2158 days ago
I kinda like using tooling with 40 years of refinement. Is it perfect? No. Do I need to resize a window with a progress bar? No. Would it be nice? Yeah, I guess. Am I underestimating the benefits? Almost certainly.

But what are the other sharp edges that come with the shiny new thing? Would it be an electron app? Yes, probably. Would it work? Maybe, in a couple years. There would be some major bugs first, and a lot of dead batteries.

I don’t think new devs are specifically worse than old ones. I do think there is immense value in using something that has been actively improved for your (well, at least my) entire lifetime.

There are lessons buried in there we don’t even begin to understand. Starting over means forgetting those lessons too.

The grass is always greener on the other side of the fence is what I am saying.

5 comments

It looks to me that the reality is closer to "40 years of hack over something that was not meant to be a standard (a popular hardware terminal from the 80s)".

I'll give you just one example of the madness that is sometimes required: the escape code \e[1m is doing "bold on". It's counterpart to reset \e[21m, is supposed to do "bold off", but in practice some terminals do "double underline" instead because this standard is not a real standard. As there is also no real way to detect capability, the only reliable way to do "bold off" that I could find is to intercept the stream of data sent to the terminal to compute the attribute state. When you want to do a "bold off" you do a \e[0m "reset all" instead and sent the control character to set the state again without bold [1].

A new standard would also allow to reliably use features like displaying images or links.

[1] https://github.com/MichaelMure/go-term-markdown/blob/master/...

So, what you're saying is, since there are too many competing and incompatible standards, what we need is another incompatible standard? :)

There are a lot of "better" terminal emulator standards out there. The problem is you need software that uses them. Perhaps this is the real "refinement" spoken of before -- these quirks are generally understood and software layers are built on top of them to abstract away the ugliness while keeping broad compatibility.

There are lots of ideas on how to make a better CLI -- from Plan9's rio/window to various XML/DOM based terminal emulator, to a smorgasbord of xterm-but-added-features emulators that add support for anything from inline images to transmitting entire files over the TTY. Ultimately, these are only as useful as the software that uses them, which often ends up being just a few tools the terminal emulator itself ships with.

Even Windows' alternative to TTYs has basically been supplanted by an xterm/VT-ish compatible TTY style interface for software compatibility reasons.

Relevant xkcd: https://xkcd.com/927/
I hate to say it, but the standard for C1 escape codes, ECMA-48, does list "\e[21m" as "double underline". I can see where one might think it does a "bold off" but alas, that isn't in the standard.

The standard also has a lot of redundancy, and some omissions (getting the size, but I think it was standardized long before TTYs were resizable).

A standard is a good idea, but I think ECMA-48 was a draft that was put into production too quickly.

My point exactly. Some terminals do the expected thing of doing "bold off" because that make the mapping of codes regular, some follow ECMA-48 and do "double underline" even if that means that "bold off" is not possible.

At the end, there is no way to reliably do either of them.

Not using bold properly means the terminal was written incorrectly, or the wrong termcap is being used to emulate a different terminal.

This isn't a bug with escape codes or with terminals that work. It is either that someone doesn't know which teminal they are using, or there is a bug in that specific terminal.

Not entirely. Well there is bold, normal, and dim. Early on someone realized there is no need for unbold and undim codes. So undim, 22 I believe, works for both, setting normal intensity. 21 was then free for something else.
I’d say all this is a feature.

This means that it’s difficult to write a flashy app with bold text, links and images.

I like programs with minimal text ui, so I don’t mind.

A new standard would also make it easier to fallback to no colors/decoration if the user wish so. At the moment, there is no way to do that without a flags or similar for each app. Even detecting if the terminal support colors is a pain.
> fallback to no colors/decoration if the user wish so

Fallbacks are frequently not implemented, not tested or not supported as well as the main feature.

> Even detecting if the terminal support colors is a pain.

As the end user, I don’t mind. This means that I’ll see colors only when it’s important.

I'm with you 100%, virtually nobody will take the time to code against a fallback profile like that. That's why most sites tend not to work without JavaScript regardless of whether their functionality strictly requires it.

Support for images concern many of us for that reason: sure it seems nice for a few cases used judiciously. But it feels counter-productive to the strengths of the CLI.

I would feel that way about curses/cli-based-gui too in theory, but in practice it's just top, vim, and less for the most part. Maybe I'm just too scared.

From what I've seen it's quite common to use a library to output those escape code. Given that, there is often a flag to disable all those colors/decoration at once without implementing a fallback specifically.

> Support for images concern many of us for that reason: sure it seems nice for a few cases used judiciously. But it feels counter-productive to the strengths of the CLI.

Of course those things should be used judiciously, but building CLI app on a broken standard only make it so much harder to build a quality app. You get a crappy end result just because it's difficult to do the "judiciously" part.

> I kinda like using tooling with 40 years of refinement

I would not call the modern terminal the result of 40 years of refinement - mostly for the reasons outlined by the parent poster, as well as those of the sibling posters.

If you look at the modern terminal through fresh eyes, 'buggy and inconsistent and clunky' would be how the average homo sapiens would describe it.

Just because there are reasons for why all of these warts are in place, doesn't mean that the program is not defective, as designed.

There are a lot of "Maybe"s in your second paragraph.

I'd say, yes, there are a lot of lessons buried in old code, but often, there is an equal amount of baggage we are too fearful to abandon because of backward compatibility and "bug became feature"-situations.

And even then, it's not you who is abandoning that, which makes the attitude even more baffling to me.

There still some pretty glaring issues, even after 40 years of refinement. Some I think could be iteratively fixed but not all of them (for example I believe #1 requires a significant breaking change to terminals).

1. You can't have an interactive program in a pipeline. For example, you can't do `gpg --decrypt foo.txt.gpg | nano | gpg --encrypt` because keyboard input on the console is handled through stdin. You can work around this by using a graphical editor in the pipeline, but this should be possible entirely over the command line.

2. You can't have hotkeys that are just modifier keys because the modifier keys only change the byte sent when pressing another key. So could couldn't have a hotkey "Shift" but you can have a hotkey "Shift-n".

3. How do you tell the difference between an escape sequence and the escape key? Escape sequences start with exactly the same byte used by the escape key

4. While we have escape sequences to move the cursor and detect the cursor location, in order to find dimensions of the terminal we have to resort to an ioctl.

5. Termcap. Because we can't assume some standard set of terminal features, and since my FreeBSD servers don't have a termcap file for alacritty, when I ssh into my FreeBSD servers my backspace key doesn't work (along with a lot of other things).

6. This ones a bit optional, but it would be nice to have a standard protocol for telling the terminal to render an image.

7. Abruptly killing a program that has put the terminal into raw mode leaves your terminal in raw mode.

What would I do to fix this:

1. Move keyboard input to a different file handle than pipes. This would require software to be patched, or at least a compatibility layer written to merge the pipes for legacy software.

2. Remap all the keyboard bytes. Every key press and every key up sends data to the keyboard input stream. Let the program maintain a keyup/keydown state map for handling modifier keys. This would require software to be patched or at least a compatibility layer written to remap the bytes.

3. As part of the new encoding for #2, we should make a better encoding for input. The current system seems a lot more "grown" than designed. I'd love to see something similar to UTF-8 where we get to encode a variable-length integer with the benefits of indicating byte length in the first byte and being able to detect if we're in a continuation byte. We could even use UTF-8 verbatim for most of the keyboard input but we'd need a special section for escape sequences like moving the cursor. I don't know enough about unicode to know if theres a section we could use for that, but worst case scenario we could use the 5 and 6-byte length utf-8 sequences that never made it to the final utf-8 spec despite being possible in the encoding.

4. As part of #3, add escape sequences to detect terminal dimensions.

5. Establish a new minimum set of features that are assumed to work, incorporating all these refinements over the past 40 years.

6. This could be part of the new encoding for #3

7. Automatically revert terminal to its original non-raw-mode state at the end of a program.

Finally, I'd recommend this blog post to appreciate how much of a mess terminal programming is: https://viewsourcecode.org/snaptoken/kilo/02.enteringRawMode...

> 2. You can't have hotkeys that are just modifier keys because the modifier keys only change the byte sent when pressing another key. So could couldn't have a hotkey "Shift" but you can have a hotkey "Shift-n".

Later model physical DEC terminals had a mode called PCTERM (with escape sequences to enter/exit it) in which the terminal would send PC keyboard scan codes for key presses. However, very few terminal emulators implement that.

The Telnet server and client in Windows NT implements a somewhat similar feature, the VTNT terminal type, in which keyboard scan codes and mouse movements are transmitted in a binary format which is based on the Windows console API. (Nowadays Telnet is mostly deprecated in favour of SSH; there is no reason why SSH clients and servers could not implement VTNT, but I haven't seen any do it.)

The technology already exists to solve this problem, and has for years. It is just that nobody appears to care about it enough for that technology to become widely adopted.

> For example, you can't do `gpg --decrypt foo.txt.gpg | nano | gpg --encrypt` because keyboard input on the console is handled through stdin.

I actually had a alias at one point that did something along the lines of (IIRC):

  | editor | -> | editor -i /dev/fd/3 -o /dev/fd/4 3<&0 4>&1 0<&2 1>&2 |
Since stderr (aka &2 aka /dev/pts/whatever) is opened read-write, you can dup it onto stdin to recover the terminal. Ideally, every editor should just support reading from stdin, interacting via stderr[0], and writing to stdout as a command line option though.

Edit: 0: or /dev/tty as jclulow suggested.

With respect to interactive programs in a pipeline, this can already work today if the software is written to allow it. While it is common for interactive software to use the standard descriptors for I/O they are not required to. You can instead open the magic device, /dev/tty, which gets you new handles to the controlling terminal for the process.
Here's a proposal to fix #2 and #3: http://www.leonerd.org.uk/hacks/fixterms/
1. You can have an interactive program in the pipeline, for example 'less' is usually called this way:

  cat some.file | less
Less reads 'stdin' and then reaches out to the actual console to respond to navigation keys. The console is reachable either via '/dev/tty' (POSIX) or, on Windows, via CreateFile()[1].

But yes, most programming tutorials (and probably many programs) make no distinction between the console and standard streams, hence the confusion. They absolutely should.

[1] https://docs.microsoft.com/en-us/windows/win32/api/fileapi/n...

> since my FreeBSD servers don't have a termcap file for alacritty, when I ssh into my FreeBSD servers my backspace key doesn't work (along with a lot of other things).

A hack I've got in .zshrc for this problem:

  function ssh {
   if [[ "${TERM}" = alacritty ]]; then
    env TERM=xterm-256color /usr/bin/ssh "$@"
   else
    /usr/bin/ssh "$@"
   fi
  }
Yeah, I create a .terminfo directory in my home folder on any box I ssh to and make sure it has the terminfo for the terminal I use.
For issue #3, it would have been nice had the TTY layer sent DLE ESC when someone hits the ESC key. As it is now, once you read the ESC key, you have to wait some number of milliseconds to see if more data is coming in, and if not, then just return a single key, else it's an escape sequence. Sigh.
I definitely enjoy the continuity, call me small-minded but I don’t want to see this stuff revolutionized either. Maybe because I came up in the 90s and am nostalgic for the VT220 etc, I don’t know. And it’s weird because I have little or no love for other old school stuff.
I would be happy if I never had to write a flaky text parser, for some command line tool, output again.
Then don’t, take json or some standard format with an off-the-shelf parser as stdin and write it as standard out. Then just use jq to munge data into json on one end and back out on the other.
> with an off-the-shelf parser as stdin

These only exist for well known tools. And, these usually explode if you're not careful about the characters in your filename.

That’s not what I’m saying: your program expects to receive standard input in json format. And uses your favorite JSON parsing library to handle stdin. Generate stdout as JSON with the same library. Then, use jq to generate the standard input and output.