luke.b//blog

Look at the beautiful scrolling 😍

a gif of nomad showing scrolling mostly working in lessandvim

Above is a GIF showing nomad running git log, man less and vim. Scrolling is possible through selecting a sub terminal and then scrolling the mouse as per usual.

What you won’t see in the GIF is that I’ve also implemented a historical buffer so that nomad can be used to page through output that is longer than a viewport!

The final feature I need to implement is wrapping lines, and I think that’s my last annoyance at this point in time.

Oh and I should really write tests for the control sequence parsing and basically try (yet again) to optimise it and fix the ambiguity issues.

repo: https://github.com/lukebarnard1/nomad-term

ok. so I hate ANSI control sequences.

This is a good sign, I think it means I’m a sane human being with a reasonable sense of what makes a readable, easy to parse programming language.

ANSI control sequences are not sane.

What makes me say this is that some of them can be mistaken for others, namely the sequence for “Delete one line” and an invalid sequence for “a mouse cursor event happened here”.

I mean seriously.

And this crap is baked into the logic of every terminal that wants to support any well-known terminal-based program.

And you might be thinking, “well if one of them is invalid, just choose the valid one” but my control sequence parser is terrible and parses a sequence if it’s the only one that matches and there are 0 matching ones if the next character were to be matched. This is some kind of greedy prefix matching, which is needed because sequences don’t end with a sensible terminating character, but rather ANY CHARACTER YOU COULD THINK OF. breaths heavily

Anyway, I’ve pushed a really horrible hack to master and I think I’m going to call it a night.

On the plus side, I can now scroll vim and less in nomad, which is epic. (Ok, so you can’t scroll up in vim. ffs)

repo: https://github.com/lukebarnard1/nomad-term

For some reason, Terminal.app will send scroll events to the program if flags 1 and 47 are set… vim relies on this.

If only one of these are set, I can’t seem to reproduce the behavior where Terminal.app sends scroll events to e.g. bash. Weird.

The control sequences that NeoVim sends to stdout when it starts are as follows:

{"params":[47],"code":"DECSET"}
{"params":[1],"code":"DECSET"}
{"params":[2004],"code":"DECSET"}
{"params":[1004],"code":"DECSET"}
{"params":[2],"code":"DECSCUSR"}
{"params":[2],"code":"DECSCUSR"}

The single parameter indicates which flag to control, and the code DECSET is the name of the sequence that should cause the flag to be set. DECRST is the code for marking the flag as unset.

  • DECSET 47 = “Use Alternate Screen Buffer”
  • DECSET 1 = “Application Cursor Keys”
  • DECSET 2004 = “Set bracketed paste mode”
  • DECSET 1004 = “Send FocusIn/FocusOut events”
  • DECSCUSR 2 = “Set cursor style…steady block”

I’ve implemented scrolling in nomad subterminals!! This makes working with long pages of historical program output a lot easier.

Next I will need to allow programs running in nomad to control whether they receive mouse controls, which for now I will restrict to scrolling (but in future maybe I could enable other mouse events).

I made some good progress this evening:

  • nomad requests mouse tracking from the terminal it runs in
  • it receives mouse tracking control sequences and detects scroll events, adjusting a number variable that tracks the current scroll position

Next I’ll have to figure out how to store lines that are pushed out of viewport by inserting new lines. Currently these are discarded, but scrolling them into view should now be possible.

I know it’s wrong but I want to do some coding whilst watching Star Wars with my mum and step dad.

Scrolling in nomad-term, here I come.

WOW. I found something cool.

If I run $ printf '\e[?1000h' in Terminal.app, cursor events are sent to the program in some format that indicates scrolling, clicking and mouse location but I’m not exactly sure how this format works.

I think the details are explained here: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking

I think I’ll get back to this tomorrow though. At least I made some progress.

And bread is done I think! :)

I think I might have to change my approach to this.

One of the cases I want to solve for is paging over a lot of output from a program whilst using bash.

But bash already interprets the up/down arrows by cycling through history, so arrow keys can’t be used for a second purpose here.

And actually, this applies to other programs that interpret arrow key presses.

Through the art of … logging things, I’ve determined that the control sequences that are sent when the arrow keys are pressed are:

  • up = ESC [ A
  • down = ESC [ B
  • right = ESC [ C
  • left = ESC [ D

The up and down arrows should move the scroll window of the subterminal, but all of these should be proxied to the program unaltered…

I’ve just realised that they are proxied (as expected) to vim, the thing I’m using to edit this log file…

Something to investigate later, first vertical pagination in nomad.

page 16 of 27 after before