Skip to content

imdaveho/tuitty-rs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

tuitty-rs

A cross-platform, interoperable, simplfied terminal library that is meant to be wrapped by multiple languages.

tuitty-banner

tui • tty \ˈtwē-dē \ n. - text-based user interface library for teletype writers (aka. terminals)

NOTE: (Nov. 8, 2019) - This library is still in alpha stage and the API is still in flux. However, the core concepts and goals outlined below will remain fairly stable. Contributions are most welcome! 😄

Table of Contents

✨ Features

(Back to top)

  • Cross-platform (Linux, Mac, Windows)
  • Focused (read: small) API footprint - unified, consistent capabilities across terminals
    • avoid leaky abstractions that force you to think about what may or may not work
    • prefer to keep dependencies limited (Unix: libc, Windows: winapi) and avoid including the kitchen sink
  • Thread-safe (guarantees provided by Rust's Send + Sync traits)
  • Cursor navigation - eg. goto(col, row), move up/down/left/right
  • Screen manipulations - eg. resize, clear, print, enter/leave alternate screen
  • Styling output - eg. fg and bg colors, bold, dim, underline
  • Terminal settings - eg. raw/cooked, hide/show cursor, mouse on/off
  • User input handling - eg. keyboard/mouse events
  • Minimal memory unsafe code: only OS specific calls and FFI which follows the Rust FFI Nomicon very closely

💭 Rationale

(Back to top)

  • Why not use curses?

    Show response
    While [n/pd]curses is widely used and wrapped, there is also plenty issues regarding them: wide character support, cross-platform support, C-style/low-level imports that reduce clarity, etc.
  • Why not use blessings (Python), tty-tk (Ruby), terminal-kit (Node), or insert project (insert language)?

    Show response

    As you can see, there is already a proliferation of various implementations of terminal libraries...and yes I'm aware of the irony that this project is +:one: to the list of implementations out there.

    However, unlike other attempts, what this project intends to do is to create a unifying API across languages that eliminates the need to repeat yourself. This is actually very similar to how asdf-vm addressed the proliferation of "version managers" like rbenv, gvm, nvm, and pyenv. By creating something unifying and extensible, users won't have to re-discover and re-learn a new API every time they switch programming languages.

    Additionally, many of the implementations out there do not provide cross-platform support (mainly Windows Console), which I'm specifically targeting with this project.

  • Why the command line? Why cross-platform? Why, why, why?!

    Show response
    At the end of the day, many development workflows begin and end with a terminal prompt. I wanted to learn and better understand this critical component of a software engineer's journey. Consequently, this process has gotten me familiar with systems programming languages (Rust, Go, C, and Nim), low-level OS syscalls, the Windows Console API, and countless other intangibles that have made me a more well-rounded individual.

📔 Definitions

(Back to top)

Cross-platform

Expand description
  • Needs to consistently work on MacOS, Linux, and Windows
    • BSDs and others would be secondary

  • Needs to work on these architectures:
    • ARM - 32/64-bit
    • Intel - 32/64-bit
    • AMD - 32/64-bit

Interoperable

Expand description
  • Needs to be portable to multiple languages (ones that have an FFI with C)
    • C has too many ⏳💣💥 so such interoperability is provided by Rust (maybe Nim)

Simplified

Expand description
  • Basic functionality scoped to the below:
    • Cursor actions (motion)
    • Screen actions (printing/clearing)
    • Output actions (styling)
    • Term mode actions (raw/cooked)
    • Input event handling

  • Implemented with as little "in the middle" as possible
    • Tight scoping allows us to focus on specific elements to optimize performance rather than peanut-buttering across too many concerns

  • Being clear > being clever
    • Rust actually provides great options for abstractions (eg. Traits, macros) but these should be carefully considered over a more straight-forward method—even if they are more idiomatic Rust. Often, traits and macros make code less understandable for newcomers as they can be/get quite "magical".
    • The analogy that comes to mind is that, for the longest time, Go(lang) did not want to provide generics because the feeling was that they reduced readability and made the language more complex. Instead, the tradeoff made was that some repetition was more beneficial towards maintainable code than bluntly trying to be DRY. Likewise, to keep things simplified, I'd rather repeat things that make what is going on obvious and less opaque.

⚡ Getting Started

(Back to top)

API Design

tuitty's architectural design attempts to mirror reality. There are actually two (2) feedback loops happening when an application begins:

  1. The "outer loop": a User receives visual cues from the terminal and, in response, does things that emits input events (eg. pressing keys on a keyboard), which in turn does stuff to the terminal, and

  2. The "inner loop": the Application receives a signal or request, processes or fetches application state/data accordingly, updates the application state, and performs operations to the view, which causes the "stuff" to be done to the view that provides the visual cue to the User.

Bear in mind, it's just a loop!
The mental model to bear in mind is similar to the Flux pattern for React.js popularized by Facebook.
Unidirectional data flow in Flux
Phase 1: Receiving an Input Event from the User

The Dispatcher replicates the parsed InputEvent and sends it to each listening Event Handle.

input-flow
Phase 2: App Requests some internal state

For example, a Signal was received to get the character underneath the cursor. This requires a Request made to the Dispatcher to fetch the cursor position and the character at the corresponding location in the internal screen buffer.

state-flow
Phase 3: App Signals an appropriate Action to be taken

Perhaps, you want to take the character at position and print it somewhere else on the screen, like a copy + paste operation.

signal-flow

After the terminal updates, the User will receive that visual cue and provide more inputs for the cycle to start over again.

Is this really a big deal?

These separate diagrams were meant to help build a mental model regarding how the internals of the library work. It is helpful to understand that the Dispatcher is responsible for sending and receiving Signal or Request messages that either does stuff (signal actions) or fetches stuff (request app state). This uses channels under the hood.

This is important, because on Unix systems, in order to parse user input, you would have to read stdin. But that would be a blocking call. If you wanted to run things concurrently (eg. autocomplete, syntax checking, etc), you would have to read things asynchronously through a spawned thread. It would be impractical to spawn a thread every time you wanted a concurrent process to read from stdin. Also, why would you need more than a single process reading and parsing from stdin? Instead of a thread, this implementation creates a new channel that receives InputEvents from a single reader of stdin that is within the Dispatcher.

Similarly, if you wanted to take actions on the terminal, in the previous paradigm, terminal actions were methods with an object that also held some mutable state (eg. screen buffers, multiple screen contexts, etc). It wasn't clear how that would cross the FFI boundary when attempting multi-threaded or async/await event loops in other languages. Passing a mutable Box<T> (heap allocated chunk of memory) seemed like a bad idea. However, with this pattern in a similar manner, multiple entities can send Signals and make Requests to the Dispatcher to be handled safely.

Like I mentioned previously, this is not a pattern that was invented for this particular library. Rather, this pattern pulled inspiration from reactive programming (Rx.js), the actor model / concurrency via message passing (Kafka, Erlang), and web frameworks like Elm, React.js (aforementioned Flux), and re-frame. Actually, the documentation for re-frame has a similar diagram: (see right). The relevant parts are mainly 1-5 since the web stuff is irrelevant here. But notice how similar the flows are to each other. It has been well-documented and proven how these patterns reduce compexity and errors and improve maintainability and speed of development.

Dispatcher

(Back to top)

Event Handle

(Back to top)

Tested Terminals

(Back to top)

  • Windows 10 - Cmd.exe (legacy and modern modes)
  • Windows 10 - PowerShell (legacy and modern modes)
  • Windows 10 - git-bash (w/ winpty)
  • Ubuntu 17.04 - gnome-terminal

🔮 Aspirations, but not Guarantees

(Back to top)

Expand description
  • High performance (can't expect it all to be there as a v1)
  • Work flawlessly on all platforms, all architectures, etc. (this is non-trivial)
  • Cover all world languages and keyboard layouts (unicode is hard)
  • Match idomatic paradigms across programming languages (eager to adopt the best from each)
  • Have feature X from this other library Y (eager to evaluate and learn from)
  • Completeness (not always is the terminal the best tool for the job; we won't force a square peg into a round hole)

Contributing

(Back to top)

Please read CONTRIBUTING.md for details on our code of conduct, and the process for submitting pull requests to us.

Specifically, there are labels created for each of these areas:

Versioning

(Back to top)

We use SemVer(-ish) for versioning. For the versions available, see the TBD

Authors

(Back to top)

  • imdaveho - Creator and project maintainer (profile)

License

(Back to top)

This project is licensed under the MIT License - see the LICENSE.md file for details

Closing Shoutouts 👏

(Back to top)

nanos gigantum humeris insidentes

Many thanks to the authors and projects below for various implementations that have inspired this project.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages