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

feat: replace less with minus #100

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
80 changes: 80 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ colored = "2"
ctrlc = "3.4.1"
lazy_static = "1.4.0"
linemux = "0.3"
minus = { version = "5.5", features = ["search", "dynamic_output"] }
once_cell = "1.19.0"
rand = "0.8.5"
regex = "1.10.2"
Expand Down
51 changes: 6 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ A log file highlighter
- 🌈 Highlights numbers, dates, IP-addresses, UUIDs, URLs and more
- ⚙️ All highlight groups are customizable
- 🧬 Easy to integrate with other commands
- 🔍 Uses `less` under the hood for scrollback, search and filtering
- 🔍 Uses [`minus`](https://github.com/arijit79/minus) under the hood for scrollback, search and filtering

#

Expand All @@ -31,7 +31,7 @@ A log file highlighter
* [Watching folders](#watching-folders)
* [Customizing Highlight Groups](#customizing-highlight-groups)
* [Working with `stdin` and `stdout`](#working-with-stdin-and-stdout)
* [Using the pager `less`](#using-the-pager-less)
* [Using the pager `minus`](#using-the-pager-minus)
* [Settings](#settings)

***
Expand Down Expand Up @@ -79,10 +79,6 @@ cargo install --path .

Binary will be placed in `~/.cargo/bin`, make sure you add the folder to your `PATH` environment variable.

> [!NOTE]
> When building from source, make sure that you are using the latest version
> of [`less`](http://greenwoodsoftware.com/less/).

## Highlight Groups

### Dates
Expand Down Expand Up @@ -286,7 +282,7 @@ of words to be highlighted.

## Working with `stdin` and `stdout`

By default, `tailspin` will open a file in the pager `less`. However, if you pipe something into `tailspin`, it will
By default, `tailspin` will open a file in the pager `minus`. However, if you pipe something into `tailspin`, it will
print the highlighted output directly to `stdout`. This is similar to running `tspin [file] --print`.

To let `tailspin` highlight the logs of different commands, you can pipe the output of those commands into `tailspin`
Expand All @@ -298,47 +294,12 @@ cat /var/log/syslog | tspin
kubectl logs -f pod_name | tspin
```

## Using the pager `less`
## Using the pager `minus`

### Overview

`tailspin` uses `less` as its pager to view the highlighted log files. You can get more info on `less` via the **man**
command (`man less`) or by hitting the <kbd>h</kbd> button to access the help screen.

### Navigating

Navigating within `less` uses a set of keybindings that may be familiar to users of `vim` or other `vi`-like
editors. Here's a brief overview of the most useful navigation commands:

- <kbd>j</kbd>/<kbd>k</kbd>: Scroll one line up / down
- <kbd>d</kbd>/<kbd>u</kbd>: Scroll one half-page up / down
- <kbd>g</kbd>/<kbd>G</kbd>: Go to the top / bottom of the file

### Follow mode

When you run `tailspin` with the `-f` or `--follow` flag, it will scroll to the bottom and print new lines to the screen
as they're added to the file.

To stop following the file, interrupt with <kbd>Ctrl + C</kbd>. This will stop the tailing, but keep the
file open, allowing you to review the existing content.

To resume following the file from within `less`, press <kbd>Shift + F</kbd>.

### Search

Use <kbd>/</kbd> followed by your search query. For example, `/ERROR` finds the first occurrence of
**ERROR**.

After the search, <kbd>n</kbd> finds the next instance, and <kbd>N</kbd> finds the previous instance.

### Filtering

`less` allows filtering lines by a keyword, using <kbd>&</kbd> followed by the pattern. For instance, `&ERROR` shows
only lines with **ERROR**.

To only show lines containing either `ERROR` or `WARN`, use a regular expression: `&\(ERROR\|WARN\)`.

To clear the filter, use <kbd>&</kbd> with no pattern.
`tailspin` uses `minus` as its pager to view the highlighted log files.
You can see the available keyibdings [here](https://docs.rs/minus/latest/minus/index.html#standard-actions).

## Settings

Expand Down
2 changes: 1 addition & 1 deletion src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ fn get_output(has_data_from_stdin: bool, is_print_flag: bool) -> Output {
return Output::Stdout;
}

Output::TempFile
Output::Pager
}

fn determine_input(path: String) -> Result<Input, Error> {
Expand Down
82 changes: 17 additions & 65 deletions src/io/controller/mod.rs
Original file line number Diff line number Diff line change
@@ -1,87 +1,39 @@
use async_trait::async_trait;
use tokio::io;

use crate::io::presenter::empty::NoPresenter;
use crate::io::presenter::less::Less;
use crate::io::presenter::Present;
use crate::io::reader::command::CommandReader;
use crate::io::reader::linemux::Linemux;
use crate::io::reader::stdin::StdinReader;
use crate::io::reader::AsyncLineReader;
use crate::io::writer::stdout::StdoutWriter;
use crate::io::writer::temp_file::TempFile;

use crate::io::writer::AsyncLineWriter;
use crate::types::{Config, Input, Output};
use tokio::sync::oneshot::Sender;

pub struct Io {
reader: Box<dyn AsyncLineReader + Send>,
writer: Box<dyn AsyncLineWriter + Send>,
}
use super::reader::EOFSignaler;
use super::writer::minus::Minus;

pub struct Presenter {
presenter: Box<dyn Present + Send>,
}
pub type Reader = Box<dyn AsyncLineReader + Send>;
pub type Writer = Box<dyn AsyncLineWriter + Send>;

pub async fn get_io_and_presenter(config: Config, reached_eof_tx: Option<Sender<()>>) -> (Io, Presenter) {
let reader = get_reader(config.input, config.follow, config.tail, reached_eof_tx).await;
let (writer, presenter) = get_writer(config.output, config.follow).await;
pub async fn get_reader_and_writer(config: Config, eof_signaler: EOFSignaler) -> (Reader, Writer) {
let reader = get_reader(config.input, config.follow, config.tail, eof_signaler).await;
let writer = get_writer(config.output).await;

(Io { reader, writer }, Presenter { presenter })
(reader, writer)
}

async fn get_reader(
input: Input,
follow: bool,
tail: bool,
reached_eof_tx: Option<Sender<()>>,
) -> Box<dyn AsyncLineReader + Send> {
async fn get_reader(input: Input, follow: bool, tail: bool, eof_signaler: EOFSignaler) -> Reader {
match input {
Input::File(file_info) => {
Linemux::get_reader_single(file_info.path, file_info.line_count, follow, tail, reached_eof_tx).await
Linemux::get_reader_single(file_info.path, file_info.line_count, follow, tail, eof_signaler).await
}
Input::Folder(info) => Linemux::get_reader_multiple(info.folder_name, info.file_paths, reached_eof_tx).await,
Input::Stdin => StdinReader::get_reader(reached_eof_tx),
Input::Command(cmd) => CommandReader::get_reader(cmd, reached_eof_tx).await,
Input::Folder(info) => Linemux::get_reader_multiple(info.folder_name, info.file_paths, eof_signaler).await,
Input::Stdin => StdinReader::get_reader(eof_signaler),
Input::Command(cmd) => CommandReader::get_reader(cmd, eof_signaler).await,
}
}

async fn get_writer(output: Output, follow: bool) -> (Box<dyn AsyncLineWriter + Send>, Box<dyn Present + Send>) {
async fn get_writer(output: Output) -> Writer {
match output {
Output::TempFile => {
let result = TempFile::get_writer_result().await;
let writer = result.writer;
let temp_file_path = result.temp_file_path;

let presenter = Less::get_presenter(temp_file_path, follow);

(writer, presenter)
}
Output::Stdout => {
let writer = StdoutWriter::init();
let presenter = NoPresenter::get_presenter();

(writer, presenter)
}
}
}

#[async_trait]
impl AsyncLineReader for Io {
async fn next_line(&mut self) -> io::Result<Option<String>> {
self.reader.next_line().await
}
}

#[async_trait]
impl AsyncLineWriter for Io {
async fn write_line(&mut self, line: &str) -> io::Result<()> {
self.writer.write_line(line).await
}
}

impl Present for Presenter {
fn present(&self) {
self.presenter.present()
Output::Pager => Minus::init().await,
Output::Stdout => StdoutWriter::init(),
}
}
1 change: 0 additions & 1 deletion src/io/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
pub mod controller;
pub mod presenter;
pub mod reader;
pub mod writer;
15 changes: 0 additions & 15 deletions src/io/presenter/empty.rs

This file was deleted.