Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse terminal escape codes into a useful representation #15

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

noelwelsh
Copy link
Contributor

Parse the mess of escape codes the terminal uses to encode key presses into a useful representation.

Closes #9

This is essential for parsing terminal key codes.
s/Peak/Peek

Mistakes will be made when coding near midnight
This will probably be more useful than peek in most situations.
This is not complete, but it already does the basics.

- Define Key type
- Define KeyReader effect for reading Keys
- Implement basic KeyReader for terminal escape codes. Not complete
- Implement for JLineTerminal
- Update Prompt example to use KeyReader
Users will have to interact with Eof, Timeout, and Key. They shouldn't
need to import things from `terminus.effect`, so move these types into `terminus`.
/** Enumeration describing the key presses we recognize from the terminal */
enum Key {

/** A standard character, like 'A' or 'z.' */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about adding something like:

case class KeyStroke(keys: Seq[Key])

So instead of ControlShiftI, you'd have KeyStroke(Seq(Control, Shift, Character('i')))?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's a good idea, though I'd rather not use a Seq (unordered and space inefficient) compared to a more compact representation like that in Crossterm: https://docs.rs/crossterm/0.28.1/crossterm/event/struct.KeyEvent.html

- Refactor keys into separate elements for key modifiers and key codes

- Implement `KeyReader` on Native which causes many changes to the
  implementation:

  - Add non-blocking read

  - Switch `fgetc` to `read` so they don't use the same buffer

  - Switching modes take effect immediately and doesn't flush
    input (flushing input means we lose key codes)

  - Don't translate CR to NL

- Implement `Prompt` as a generic (cross-backend) example

For efficiency we may need to rethink how we handle input to store a
character buffer. I think the longest key code is 4 characters, so we
could read 4 characters at a time and keep a buffer of elements. Dealing
with unicode (i.e. characters longer than one byte) is also an open question.
private val reader = terminal.reader()
private val writer = terminal.writer()

def peek(duration: Duration): Timeout | Eof | Char =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does peek look at the buffer without clearing it?

package terminus

opaque type KeyModifier = Byte
object KeyModifier {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the use of bitflags for keystroke modifiers. What are Super, Hyper, and Meta? Do they vary based on keyboard/operating system?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Super is, I think, usually mapped to the Windows key, or the squiggle on Mac keyboards. The other two aren't used so much, I think. I think they were originally found on Lisp machines. Just more of the ancient baggage the terminal carries.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Abstract key parsing
2 participants