Skip to content

Commit

Permalink
feat: add support for a global config file (#85)
Browse files Browse the repository at this point in the history
  • Loading branch information
fritzrehde authored Jan 21, 2024
1 parent 0e967d1 commit ee9c273
Show file tree
Hide file tree
Showing 16 changed files with 749 additions and 218 deletions.
140 changes: 140 additions & 0 deletions Cargo.lock

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

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ strum = { version = "0.25.0", features = ["derive"] }
tabled = { version = "0.14.0", features = ["color"] }
terminal_size = "0.3.0"
owo-colors = "3.5.0"
dirs = "5.0.1"

# Config for 'cargo dist'
[workspace.metadata.dist]
Expand All @@ -52,3 +53,6 @@ pr-run-mode = "plan"
[profile.dist]
inherits = "release"
lto = "thin"

[dev-dependencies]
derive_builder = "0.12.0"
84 changes: 48 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,27 @@
![screenshot](https://raw.githubusercontent.com/fritzrehde/i/master/watchbind/screenshot-light.png#gh-light-mode-only)
![screenshot](https://raw.githubusercontent.com/fritzrehde/i/master/watchbind/screenshot-dark.png#gh-dark-mode-only)


## Table of Contents

- [Features](#features)
- [Installation](#installation)
- [Customizations](#customizations)
- [Keybindings](#customizations)
- [Formatting with Field Separators and Field Selections](#formatting-with-field-separators-and-field-selections)
- [How it works](#how-it-works)
- [Configuration](#configuration)
- [Keybindings](#keybindings)
- [Styling](#styling)
- [Formatting with Field Separators and Field Selections](#formatting-with-field-separators-and-field-selections)
- [State Management](#state-management)
- [Tips](#tips)


## Features

- **Customizability**: All keybindings and styles (colors and boldness) are customizable.
- **Flexibility**: You can specify settings using CLI options, a TOML config file, or both.
- **Flexibility**: Settings can be configured through CLI arguments, a local TOML config file, a global TOML config file, or a combination of these.
- **Speed**: Written in asynchronous Rust with [Tokio](https://tokio.rs/).


## Installation

### From binaries
Expand All @@ -33,7 +38,7 @@ The [releases page](https://github.com/fritzrehde/watchbind/releases) contains p

### From [crates.io](https://crates.io/crates/watchbind)

```shell
```sh
cargo install watchbind
```

Expand All @@ -57,22 +62,28 @@ pacman -S watchbind
apk add watchbind
```


## How it works

Watchbind is a command-line tool that aims to help you build custom TUIs from static CLI commands very easily.
It works by specifying a "watched command" that outputs some lines to stdout that you want to observe.
Then, we make this static output **dynamic** by re-executing it at a specified watch rate, and we make the TUI **interactive** through custom keybindings that can operate on the individual output lines.


## Customizations
## Configuration

There are several ways to customize the settings:
1. A TOML config file, specified with `watchbind --config-file <FILE>`, overrides all default settings ([examples/](examples/)).
2. The command-line options override all other settings (i.e. all TOML and default settings).
There are several ways to configure watchbind's settings:
1. **CLI arguments** (see `watchbind -h` for all available arguments).
2. A **local TOML config file**, specified with `watchbind --local-config-file <FILE>`.
3. A **global TOML config file**, located either in the user-specified `WATCHBIND_CONFIG_DIR` environment variable or in the default config directory (see `watchbind -h` for the OS-specific default config directory), is loaded automatically if it exists.
The difference between the local and global config files is that the latter must not be specified with `--local-config-file <FILE>` on every watchbind invocation, making it convenient for global settings that should apply to all watchbind invocations.

All ways of configuring `watchbind` (TOML and CLI options) can be used at the same time, and `watchbind` will automatically figure out which settings to use according to the above hierarchy.
All configuration ways can be used at the same time, and `watchbind` will determine which settings to use according to the following configuration hierarchy:
```
CLI arguments > local TOML config file > global TOML config file
```
where `a > b` means: If the config setting `X` is configured in both `a` and `b`, the value of `X` from `a` is used.

Personally, I recommend using the CLI options for small one liners and a TOML config file for more complex scripts.

### Keybindings

Expand Down Expand Up @@ -185,27 +196,6 @@ The environment variable `lines` set to all selected lines, or if none are selec
All set environment variables `ENV` will be made available in all future spawned commands/processes, including the watched command, any executed subcommands, as well as commands executed in `set-env` operations.
If multiple lines are selected, they will be separated by newlines in `lines`.

### State management

The `set-env` and `unset-env` operations allow you to manage state through environment variables.
Additionally, you can use the `initial-env` option to specify a list of `set-env` commands that will be executed **before** the first execution of the watched command.
This powerful combination allows you to set some initial state with `initial-env`, reference that state directly in the watched command, and update the state with keybindings at runtime with `set-env`.

### Formatting with Field Separators and Field Selections

`watchbind` supports some extra formatting features reminiscent of the Unix `cut` command:

- **Field Separators**:
Define a separator/delimiter to segment your command's output into distinct fields.
Each separator will be replaced with an [elastic tabstop](https://nick-gravgaard.com/elastic-tabstops/), resulting in a "table"-like structure, similar to the `cut -d <SEPARATOR> -t` command.

- **Field Selections**:
Choose only specific fields to display.
You can specify a comma-separated list of the indexes (starting at index 1) for individual fields (`X`), ranges (`X-Y`), or the capture of all fields from X onwards (`X-`).
For instance, the field selection `1,3-4,6-` will display the first, third and fourth fields, as well as all fields from the sixth onwards.

**Important**: The `lines` passed to the `exec --` operations will remain unformatted, i.e. will not have the separators replaced with elastic tabstops and will not have non-selected fields ommitted.

### Styling

Foreground colors, background colors and boldness can be customized.
Expand Down Expand Up @@ -248,6 +238,28 @@ non-bold # Make sure nothing is bold (i.e. remove any bold styling from inpu
unspecified # Don't applying any styling => use style from ANSI input text
```

### Formatting with Field Separators and Field Selections

`watchbind` supports some extra formatting features reminiscent of the Unix `cut` command:

- **Field Separators**:
Define a separator/delimiter to segment your command's output into distinct fields.
Each separator will be replaced with an [elastic tabstop](https://nick-gravgaard.com/elastic-tabstops/), resulting in a "table"-like structure, similar to the `cut -d <SEPARATOR> -t` command.

- **Field Selections**:
Choose only specific fields to display.
You can specify a comma-separated list of the indexes (starting at index 1) for individual fields (`X`), ranges (`X-Y`), or the capture of all fields from X onwards (`X-`).
For instance, the field selection `1,3-4,6-` will display the first, third and fourth fields, as well as all fields from the sixth onwards.

**Important**: The `lines` passed to the `exec --` operations will remain unformatted, i.e. will not have the separators replaced with elastic tabstops and will not have non-selected fields ommitted.

### State management

The `set-env` and `unset-env` operations allow you to manage state through environment variables.
Additionally, you can use the `initial-env` option to specify a list of `set-env` commands that will be executed **before** the first execution of the watched command.
This powerful combination allows you to set some initial state with `initial-env`, reference that state directly in the watched command, and update the state with keybindings at runtime with `set-env`.


## Tips

### Keybindings on selected lines that delete some of the input lines
Expand Down Expand Up @@ -278,11 +290,11 @@ Finally, we want to remove our the selection of the now removed lines, so we cal
### Piping

If you want to use pipes in your watched command on the command-line, make sure to escape the pipe symbol like so:
```
```sh
watchbind ls \| grep "test"
```
or put quotes around the watched command
```
```sh
watchbind "ls | grep test"
```
Otherwise, the shell will think you want to pipe the output of `watchbind exec -- ls` to `grep test`.
Expand All @@ -292,13 +304,13 @@ Otherwise, the shell will think you want to pipe the output of `watchbind exec -
The commands you bind to keys will be executed in a subshell using `sh -c`.

This means you can run a command like
```
```sh
watchbind --bind "enter:notify-send \$lines" ls
```
and the environment variable `$lines` will contain the line the cursor is currently on.

But note that
```
```sh
watchbind --bind "enter:notify-send $lines" ls
```
will not work as expected, because `$lines` will be replaced in the shell you are running the `watchbind` command from.
3 changes: 2 additions & 1 deletion src/config/fields/field_selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use std::{ops::RangeInclusive, str::FromStr};

/// A collection of field selection ranges. The overlapping of multiple ranges
/// is tolerated and should be optimized by the underlying data structure.
#[derive(Clone, Deserialize)]
#[derive(Debug, Clone, Deserialize)]
#[cfg_attr(test, derive(PartialEq))]
pub struct FieldSelections(#[serde(deserialize_with = "deserialize_ranges")] Ranges<usize>);

/// Describes a range of fields that should be included in the selection.
Expand Down
Loading

0 comments on commit ee9c273

Please sign in to comment.