DEV Community

Arijit Dey
Arijit Dey

Posted on

Minus turns two. A look back at these years and planning for the upcoming two

Table of Contents

  1. Going back in time
  2. Architectural changes over the years
    1. Moving from mutex to channels
    2. Partial screen updates
    3. Move away from async runtimes
  3. Planning for the upcoming
    1. Better way for modifying the keyboard/mouse action
    2. Improving partial screen updates
    3. Plugin system
    4. Hooks

First of all, for those who don’t know what minus is let me describe it for you…
minus is an asynchronous terminal paging library for the Rust programming language. It is similar to more/less but differs in the fact that it is a library rather than a binary and data/configuration can be fed into the pager while it is actively displaying text on the screen.
minus automatically updates the area of the screen when the text being displayed gets updated. See the Github for more information.

minus currently has 211 stars over on GitHub and more than 13K downloads on crates.io.

Two days ago, minus turned two years. Yesss! on 27th Nov ’20, I had pushed the initial commit for minus and in this post let’s take a look back from where we started and plan for where we have to go.

Going back in time

Before starting minus, I used to do minor contributions to the Pijul version control system for some past time. One day I filed up an issue
to add a pager for log outputs and diff similar to git. The original developer Pirrie seemed positive with the idea so I started looking into existing solutions starting from less all the way to Rust crates like pager and moins. But call it a forture or what none of them exactly satisfied our needs.

  • *more*: Seriously it doesn’t have much of the standard terminal paging features that any basic pager should have.
  • *less*: The universal terminal paging solution for all *NIX systems. Data can be fed into pager asynchronously and it will update the screen with the data update. unfortunately it isn’t available on Windows and other programs that depend on a pager like git are distributed in a environment like MinGW and Cygwin which provide all the basic GNU utilities for Windows. Pijul was never meant to be distributed like that and instead it was supposed to be distributed as a single binary.
  • *pager*: It isn’t a pager in itself but just facilitates in calling a pager binary with the data feed.
  • *moins*: It lacked some features like line numbering and searching at that time but the main deal breaker is the fact that we cannot asynchronously feed date into it.

Essentially we wanted a pager which could be bundled with the main pijul binary rather being a separate binary. Also we wanted the ability to feed data into the pager asynchronously so that it doesn’t block the terminal or the main thread of the pijul binary.

After being annoyed by all the options that just didn’t satisfied our needs, I did what is quite natural for a programmer… write your own
solution. I initially thought that at some point I would host it under Pijul’s namespace as a subproject but after a few days I decided to keep
it out of Pijul’s namespace because it might hinder its development speed as the developers of Pijul are quite busy managing their complicated VCS and I didn't want to hand down another project over their heads to manage.

After the v1.0 release, minus wasn’t immediately integrated into minus because it contained bugs and was not that featureful. So we decided to
delay it for later when minus reached a quite usable phase and then either me, or the Pijul devs would start integrating it inside
Pijul because it was the main target that minus was supposed to serve. But quite frankly neither me nor the Pijul devs got the time to
integrate minus and till date it isn’t integrated yet.

I no longer follow up with Pijul’s development as I don’t get much leisure time so I don’t know if they have planned anything for minus.

Architectural changes over the years

Minus has gone through many architectural changes over the years and it is quite difficult to list all of them here. But still I am laying some of the more important ones and what I learned from them over the years.

Moving from mutex to channels

minus had a mutex for communicating from the main application and the thread that reads user inputs from the terminal. But this allowed lots of
concurrency related bugs like deadlocks from the main application or other parts of minus. So in v5.0, we switched to channels for most of
the communications and used a mutex only for internal state management. This vastly reduced the surface area for concurrency related bugs.

Partial screen updates

For a long time minus drew the entire screen for every screen update, big or small. Printing text on a terminal is slow… so in v5.1 we
allowed minus to redraw only the parts of the terminal that is absolutely required. This is only done for simple user movement keys like
h, j, k, l etc.

Move away from async runtimes

In early versions of minus, it provided separate functions for tokio and async-std but since v5.0 minus removed all these for a single unified
functions that used native OS threads. Another reason to use threads is that terminal paging can run for long periods of time and these
runtimes provide tasks which are especially suitable for small I/O bound tasks. Using a OS native thread is much more reliable in this scenario.

Including all of these major changes there were many API and docs improvements over these years. Also I would like to take a moment to say a
very warm thank you to all the contributors who helped all the way along in this journey of minus.

Planning for the upcoming

It was a great journey all along and I am hoping that the upcoming years will be even more exciting. I have a few plans that I would like to get
implemented into minus in the next 2 years.

Better way for modifying the keyboard/mouse action

For adding/modifying/removing the actions that are emitted on a key press or mouse action: currently you need to redefine the trait function that handles this logic. A much better way would be to have a ergonomic API for handling each of these operations for an event type.
I have already done some groundwork on it but its still far from complete

Improving partial screen updates

Right now, minus intelligently updates the screen by only redrawing the lines that are absolutely required to. But this is only available for
simple movement keys like h, j, k, l, Up, Down, G etc. In future this should be available for all screen updates like search movements etc.

Plugin system

I would like to see more functionality inside minus but also want it to be kept out of the core codebase. Things like syntax highlighting for source code files, sending blocks of text to external programs via standard input/output etc would be amazing. To achieve this, minus should provide a way to inject plugins into its runtime and run them on specific user input. Plugins would be written in a interpreted language like Python and the plugins would be loaded from defined folders.

Hooks

Sometimes the main application needs to perform an action under certain states like before showing the display, after creating the display or when the user reaches the end of output. For this, minus should have hooks to which the application can bind certain functions to. Whenever
minus reaches these hook states, it will call the associated functions with it.

There is horizontal scrolling and many more. I do not know how many of them will be implemented into minus. If you want to help on implementing
any of these features you’re welcome to file a pull request on GitHub. If you need help while implementing or just any help related to minus in
general, hop onto our Discord or Matrix.

Lastly if you like my work even for a bit, please consider giving it a like on Github. I don’t ask for donations or sponsorships so giving a simple like really motivates me to do this kind of work.

That’s all from me. Peace off :-)

Top comments (0)