Compare commits

...

810 commits

Author SHA1 Message Date
Loric ANDRE dd3b09a687 release: v5.7.0
Some checks failed
Pull request update / autoupdate (push) Has been cancelled
Release PR / prepare (push) Has been cancelled
Release PR / tag (push) Has been cancelled
Release PR / update-pr (push) Has been cancelled
2026-09-08 14:52:59 +02:00
LoricAndre 672639fd80
fix: stream preview command instead of waiting for completion (#1176)
closes #1174
2026-09-08 11:57:31 +00:00
dependabot[bot] a177153700
chore(deps): bump ratatui-image in the cargo-prod group (#1175)
Bumps the cargo-prod group with 1 update: [ratatui-image](https://github.com/ratatui/ratatui-image).


Updates `ratatui-image` from 11.0.7 to 11.0.8
- [Release notes](https://github.com/ratatui/ratatui-image/releases)
- [Changelog](https://github.com/ratatui/ratatui-image/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ratatui/ratatui-image/compare/v11.0.7...v11.0.8)

---
updated-dependencies:
- dependency-name: ratatui-image
  dependency-version: 11.0.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-prod
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-08 13:13:29 +02:00
Loric ANDRE 801d1ab036 docs: update benchmarks
Some checks failed
Pull request update / autoupdate (push) Has been cancelled
Release PR / prepare (push) Has been cancelled
Release PR / tag (push) Has been cancelled
Release PR / update-pr (push) Has been cancelled
2026-09-06 22:47:38 +02:00
LoricAndre 17c517fd02
chore: nitpicks after multithreading review (#1172)
Some checks are pending
Pull request update / autoupdate (push) Waiting to run
Release PR / prepare (push) Waiting to run
Release PR / update-pr (push) Blocked by required conditions
Release PR / tag (push) Waiting to run
* chore: nitpicks after multithreading review

* chore: remove call sites from ARCHITECTURE.md

* chore: minor reliability corrections
2026-09-05 20:35:27 +02:00
LoricAndre 0392eeb70d
ci: lock everything to make sure dependencies match (#1170)
Some checks failed
Pull request update / autoupdate (push) Has been cancelled
Release PR / prepare (push) Has been cancelled
Release PR / tag (push) Has been cancelled
Release PR / update-pr (push) Has been cancelled
* ci: lock everything to make sure dependencies match

* fix: build locked for msrv too
2026-09-04 11:56:33 +02:00
skim-rs-bot[bot] 085f86b127
release: v5.6.7 (#1169)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-09-04 09:31:02 +00:00
Loric ANDRE 16c6f0b386 chore: update dependencies after tinyvec breakage 2026-09-04 11:04:21 +02:00
LoricAndre d7b799b9e6
fix: make min-height work again (#1168)
Some checks are pending
Pull request update / autoupdate (push) Waiting to run
Release PR / prepare (push) Waiting to run
Release PR / update-pr (push) Blocked by required conditions
Release PR / tag (push) Waiting to run
* fix: make min-height work again

* fix: revert to String and add integration tests

* chore: misc warnings

* fix: windows tests

* fix: ci public api fails because of incompatible deps version between HEAD and release
2026-09-04 00:04:26 +02:00
dependabot[bot] a9b0fd4393
chore(deps): bump taiki-e/install-action in the gha-prod group (#1166)
Some checks failed
Pull request update / autoupdate (push) Has been cancelled
Release PR / prepare (push) Has been cancelled
Release PR / tag (push) Has been cancelled
Release PR / update-pr (push) Has been cancelled
Bumps the gha-prod group with 1 update: [taiki-e/install-action](https://github.com/taiki-e/install-action).


Updates `taiki-e/install-action` from 2.85.4 to 2.87.1
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Commits](https://github.com/taiki-e/install-action/compare/v2.85.4...v2.87.1)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-version: 2.87.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: gha-prod
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-02 09:06:02 +02:00
dependabot[bot] a53ada910a
chore(deps): bump the cargo-prod group with 2 updates (#1165)
Some checks are pending
Pull request update / autoupdate (push) Waiting to run
Release PR / prepare (push) Waiting to run
Release PR / update-pr (push) Blocked by required conditions
Release PR / tag (push) Waiting to run
Bumps the cargo-prod group with 2 updates: [indexmap](https://github.com/indexmap-rs/indexmap) and [which](https://github.com/harryfei/which-rs).


Updates `indexmap` from 2.14.0 to 2.14.1
- [Changelog](https://github.com/indexmap-rs/indexmap/blob/main/RELEASES.md)
- [Commits](https://github.com/indexmap-rs/indexmap/compare/2.14.0...2.14.1)

Updates `which` from 8.0.5 to 8.0.6
- [Release notes](https://github.com/harryfei/which-rs/releases)
- [Changelog](https://github.com/harryfei/which-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/harryfei/which-rs/compare/8.0.5...8.0.6)

---
updated-dependencies:
- dependency-name: indexmap
  dependency-version: 2.14.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-prod
- dependency-name: which
  dependency-version: 8.0.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-prod
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-01 11:04:49 +02:00
skim-rs-bot[bot] 4b962af9ed
release: v5.6.6 (#1163)
Some checks failed
Pull request update / autoupdate (push) Has been cancelled
Release PR / prepare (push) Has been cancelled
Release PR / tag (push) Has been cancelled
Release PR / update-pr (push) Has been cancelled
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-22 17:21:53 +02:00
VXNCXNX e8f5b3ae65
fix: path_name_offset returns bytes while Rank::begin is a char index (#1160)
The PathName tiebreak computes path_name_offset - begin, so mixing the
two units inflates the score for any path with a non-ASCII directory
component and ranks a filename match below a directory match.

Co-authored-by: VXNCXNX <VXNCXNX@users.noreply.github.com>
2026-08-22 13:23:57 +00:00
VXNCXNX d699733053
fix(filter): --filter hangs when the query is below --min-query-length (#1158)
Co-authored-by: VXNCXNX <vxncxnx@users.noreply.github.com>
2026-08-22 09:19:38 +00:00
LoricAndre 756b084841
ci: fix auto update branch 2026-08-21 23:07:31 +02:00
dependabot[bot] 52275d3a3e
chore(deps): bump frizbee from 0.12.0 to 0.13.0 in the cargo-prod group (#1162)
Bumps the cargo-prod group with 1 update: [frizbee](https://github.com/saghen/frizbee).


Updates `frizbee` from 0.12.0 to 0.13.0
- [Release notes](https://github.com/saghen/frizbee/releases)
- [Changelog](https://github.com/saghen/frizbee/blob/main/CHANGELOG.md)
- [Commits](https://github.com/saghen/frizbee/compare/v0.12.0...v0.13.0)

---
updated-dependencies:
- dependency-name: frizbee
  dependency-version: 0.13.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: cargo-prod
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-21 21:04:59 +00:00
Loric ANDRE 2ec03e630c ci: automatically update PRs on pushes to master 2026-08-21 22:32:29 +02:00
Loric ANDRE c0786aa9d3 chore: clippy & fmt after cargo update 2026-08-21 22:18:54 +02:00
LoricAndre bb80ac138e
fix: do not feed items through a fifo in zsh completions (#1164)
* fix(zsh): don't relay completion items through a named pipe

`_skim_complete` handed its candidates to skim through a fifo at the fixed
path `$TMPDIR/skim-complete-fifo-$$`, created by `_skim_feed_fifo` and fed by
a backgrounded `cat`. Opening a fifo for reading blocks until a writer opens
the other end, so if the feeder ever fails to get there -- it dies, or a stale
fifo left behind by an interrupted completion makes `mkfifo` fail, or the
leftover in the world-writable temp dir belongs to someone else -- the widget
blocks forever with nothing drawn and no way to interrupt it. Interrupted
completions also leaked the fifos that set this up.

That rendezvous is the only step of the completion path that can block
indefinitely, and `kill` is the only completion that reaches it without the
`**` trigger, so `kill<TAB>` is where it surfaces.

The fifo is not needed in the first place: the `_skim_complete_*` helpers pass
their candidates on `_skim_complete`'s stdin, and the command substitution that
runs skim inherits fd 0, so skim can read them directly. (fzf relays through a
fifo because its completion functions cannot pass their own stdin along; this
file is zsh-only and has no such constraint.)

Verified in a pty, in tmux and under kitty that `kill`, `ssh`, `export`,
`unset`, `unalias` and the path/dir completions all still populate skim,
that multi-select still runs through the `_post` filters (`kill<TAB>` still
inserts bare PIDs), and that no fifos are left in the temp dir.

Refs: https://github.com/skim-rs/skim/issues/1161

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PFgaE1MjSaUGAGtR4aqVzT

* cleanup comms

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-08-21 22:14:19 +02:00
VXNCXNX 9de4825e51
fix(matcher): an inverse query only checks the first --nth field (#1159)
Co-authored-by: VXNCXNX <vxncxnx@users.noreply.github.com>
2026-08-21 22:06:58 +02:00
skim-rs-bot[bot] 1b80cff3f7
release: v5.6.5 (#1157)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-16 09:35:40 +00:00
VXNCXNX 94022921a3
fix(field): an out-of-range field index silently matches field 1 (#1155)
* fix(field): saturate out-of-range field indices to i32 bounds

Replace unwrap_or(1)/unwrap_or(-1) with saturation to i32 bounds so out-of-range indices don't silently become field 1. Affects --nth, --with-nth, --hide-nth and {N} in --output-format.

* test(field): assert exit status and stderr for the output-format cases

Per review feedback: an empty stdout alone could pass if the placeholder
errored out instead of rendering empty.

---------

Co-authored-by: VXNCXNX <VXNCXNX@users.noreply.github.com>
2026-08-16 11:07:20 +02:00
VXNCXNX 59aecd4f2b
fix: add allow_hyphen_values to --with-nth (#1156)
Matches --nth and --hide-nth behavior to support documented negative indices syntax.

Co-authored-by: VXNCXNX <VXNCXNX@users.noreply.github.com>
2026-08-16 08:11:27 +00:00
dependabot[bot] fe45ed36b7
chore(deps): bump shell-quote from 0.7.2 to 0.8.0 in the cargo-prod group (#1154)
* chore(deps): bump shell-quote in the cargo-prod group

Bumps the cargo-prod group with 1 update: [shell-quote](https://github.com/allenap/shell-quote).


Updates `shell-quote` from 0.7.2 to 0.8.0
- [Commits](https://github.com/allenap/shell-quote/compare/v0.7.2...v0.8.0)

---
updated-dependencies:
- dependency-name: shell-quote
  dependency-version: 0.8.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: cargo-prod
...

Signed-off-by: dependabot[bot] <support@github.com>

* test: fix literal failing test

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Loric ANDRE <loric.andre@pm.me>
2026-08-11 13:21:43 +00:00
skim-rs-bot[bot] 01be03f0d5
release: v5.6.4 (#1153)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-10 10:39:35 +00:00
Loric ANDRE 733aaeb148 fix: avoid truncation by casting up instead of down 2026-08-10 12:12:00 +02:00
Loric ANDRE f6a302390c fix: different suseconds and time per platform 2026-08-10 12:10:25 +02:00
Loric ANDRE 5ff1bda145 fix: platform-dependant timevals 2026-08-10 12:05:32 +02:00
Loric ANDRE 070d0849b2 fix(image): detect picker from tty to support protocol detection with piped input 2026-08-10 11:58:44 +02:00
skim-rs-bot[bot] 5fc3b0c1a0
release: v5.6.3 (#1152)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-07 14:07:35 +02:00
Vimsucks a5bd0fefa6
fix: advance the input cursor by byte length in insert_str (#1151)
`cursor_pos` is a byte offset, as `move_cursor_to` shows by validating it with
`is_char_boundary` and clamping to `value.len()`. `insert_str` advanced it by
`chars().count()` instead, so any multibyte input left the cursor inside the
text that was just inserted.

The single-char `insert` already uses `c.len_utf8()` and was unaffected.

Pasting (or committing from an IME) "中文" left the cursor at byte 3 instead of
6, so the next chunk landed between the two characters: "中文" + "测试" came out
as "中测试文". Pure ASCII input never hit this because there char count equals
byte length.

Co-authored-by: zhuyang <zhuyang@qunhemail.com>
Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2026-08-07 11:27:15 +00:00
skim-rs-bot[bot] f3c03000b3
release: v5.6.2 (#1145)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-07 10:43:53 +00:00
Loric ANDRE 1dead2b3c6 fix: match double-width roman characters (closes #1149) 2026-08-07 12:11:23 +02:00
LoricAndre b17e93890a
fix: reorder batches in --tac mode (#1150)
closes #1148
2026-08-07 10:10:17 +00:00
dependabot[bot] d99d6cecc7
chore(deps): bump the cargo-prod group with 2 updates (#1147)
Bumps the cargo-prod group with 2 updates: [clap](https://github.com/clap-rs/clap) and [frizbee](https://github.com/saghen/frizbee).


Updates `clap` from 4.6.4 to 4.6.5
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.6.4...clap_complete-v4.6.5)

Updates `frizbee` from 0.11.0 to 0.12.0
- [Commits](https://github.com/saghen/frizbee/compare/v0.11.0...v0.12.0)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.6.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-prod
- dependency-name: frizbee
  dependency-version: 0.12.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: cargo-prod
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-04 11:23:58 +02:00
Loric ANDRE 7429ca3ec2 fix: assymetry in char_equal causing panic with some unicode characters 2026-08-02 14:39:28 +02:00
dependabot[bot] bc379b5f47
chore(deps): bump the gha-prod group with 3 updates (#1146)
Bumps the gha-prod group with 3 updates: [taiki-e/install-action](https://github.com/taiki-e/install-action), [actions/upload-artifact](https://github.com/actions/upload-artifact) and [actions/download-artifact](https://github.com/actions/download-artifact).


Updates `taiki-e/install-action` from 2 to 2.85.4
- [Release notes](https://github.com/taiki-e/install-action/releases)
- [Commits](https://github.com/taiki-e/install-action/compare/v2...v2.85.4)

Updates `actions/upload-artifact` from 4 to 7
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v7)

Updates `actions/download-artifact` from 7.0.0 to 8.0.1
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](37930b1c2a...3e5f45b2cf)

---
updated-dependencies:
- dependency-name: taiki-e/install-action
  dependency-version: 2.85.4
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: gha-prod
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: gha-prod
- dependency-name: actions/download-artifact
  dependency-version: 8.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: gha-prod
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-02 10:25:59 +02:00
Loric ANDRE d2ae57625f docs: preview command runs with sh/cmd, not SHELL 2026-07-28 13:37:43 +02:00
skim-rs-bot[bot] c398374a85
release: v5.6.1 (#1143)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-27 17:18:04 +00:00
Loric ANDRE 2303a15b0c ci: do not fail release on public API breakage 2026-07-27 18:47:58 +02:00
skim-rs-bot[bot] 794ddddb71
release: v5.6.0 (#1140)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-27 17:49:35 +02:00
Loric ANDRE 4745613342 ci: make coverage non-blocking to avoid issues with nightly rust 2026-07-26 16:23:32 +02:00
Loric ANDRE 29370cc004 docs: readd default keybindings to the manpage as an auto-generated separate subsection 2026-07-26 14:45:29 +02:00
LoricAndre cbfb7fac0f
feat: add the set-cmd action to change interactive mode command from bindings (#1142)
closes #1141
2026-07-25 17:43:04 +02:00
Loric ANDRE e929c512e3 fix(examples): gate malloc_trim to gcc in the multiple_runs example 2026-07-25 16:19:31 +02:00
LoricAndre 88ce5b97ac
test: replace tmux e2e harness with cross-platform Zellij harness (#1139)
* test: replace tmux e2e harness with cross-platform Zellij harness

Rewrite the end-to-end test harness to drive `sk` through Zellij instead of
tmux, keeping the same capabilities and public surface (ZellijController,
Keys, wait, sk, the sk_test! DSL and the line!/keys!/out! helpers) so the
existing tests port over with only import/type renames.

Zellij has no detached-server model like tmux, so the harness spawns a Zellij
client attached to an in-process pseudo-terminal via portable-pty (openpty on
Unix, ConPTY on Windows). Because Zellij 0.44+ and portable-pty are both
cross-platform, the harness — and the tests that only rely on it — are now
available on Windows too: the interactive tests (formerly unix.rs) are
un-gated. execute.rs, popup.rs and listen.rs stay unix-only for reasons
unrelated to the multiplexer (PermissionsExt, a mock sh/tmux binary, unix
sockets).

Key harness details:
- Session per test via `zellij attach --create` on a fixed 80x24 PTY.
- Keys injected as raw terminal bytes with `zellij action write`; screen read
  back with `zellij action dump-screen [--ansi]`, reversed to match the old
  bottom-anchored indexing.
- A generated config disables startup tips, pane frames, mouse mode and — the
  crucial bit — the kitty keyboard protocol, so injected legacy escape
  sequences (arrows, etc.) reach sk.
- All zellij CLI calls are run under a timeout and wait() has a wall-clock
  budget, so a wedged server surfaces as a fast retryable error instead of
  hanging a test.

popup.rs unsets $ZELLIJ and sets $TMUX so skim selects its tmux popup backend
(the mock) rather than the zellij one while running inside a Zellij pane.

Because each test spins up a full Zellij session, the e2e binaries are put in
a serialized nextest test-group; CI installs Zellij (all three OSes) in place
of tmux, and the obsolete tmux setup-scripts are removed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wTqRsi31RYJXEM3ZZQhQU

* ci: fix rustfmt and stop Windows from cancelling the other nextest legs

- Run `cargo +nightly fmt` on the new Zellij harness (rustfmt CI was red).
- Set `fail-fast: false` on the nextest matrix so a failing OS leg no longer
  cancels the others, giving a clear pass/fail signal per platform.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wTqRsi31RYJXEM3ZZQhQU

* ci: install zellij via winget on Windows

taiki-e/install-action has no prebuilt Zellij binary for Windows and falls
back to `cargo install zellij`, which fails building openssl-sys from source
on the runner. Install via winget on Windows instead (taiki-e still handles
Linux/macOS), and expose winget's shim dir on PATH for the test step.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wTqRsi31RYJXEM3ZZQhQU

* test/ci: address review feedback on the Zellij harness

- Pin the Windows winget Zellij install to 0.44.3 to match the Linux/macOS
  runners (reproducible CI).
- Drop the unused `&locale` YAML anchor (actionlint flagged it).
- `wait` now surfaces the last predicate error on timeout instead of a
  generic one, so a persistent failure keeps its diagnostic cause.
- `output_with_timeout` tears down the child and reader threads on a
  `try_wait` error instead of leaking them.
- Add rustdoc to the public harness surface (`sk`, `wait`, `Keys`,
  `ZellijController` and its methods).

Deliberately not changed: a non-zero `zellij` exit is still not treated as an
error (some `zellij action` calls exit non-zero in transient states — e.g.
inline `sk` viewport teardown — while returning usable output; propagating it
broke `inline_clear_on_exit`), and `to_lines` keeps trimming to preserve the
tmux-parity bottom-anchored indexing the ported tests rely on.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wTqRsi31RYJXEM3ZZQhQU

* ci: put the real zellij.exe dir on PATH for the Windows test step

The winget install succeeds, but its Links shim wasn't reliably visible to the
`cargo nextest` step's processes, so `which("zellij")` failed and every
interactive test panicked at setup. Locate the installed zellij.exe under the
WinGet Packages dir and add its directory to GITHUB_PATH instead, failing the
step loudly if it isn't found.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wTqRsi31RYJXEM3ZZQhQU

* ci: reload PATH from registry after the MSI zellij install on Windows

The winget Zellij package is an MSI installer that installs to Program Files
and updates the machine PATH in the registry, not a portable under
WinGet\Packages — so the previous "search Packages" lookup threw. Reload PATH
from the machine/user registry values (with a Program Files fallback), then
export zellij's directory via GITHUB_PATH for the test step.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wTqRsi31RYJXEM3ZZQhQU

* test/ci: gate interactive e2e tests off Windows

Enabling the interactive tests on the Windows runner surfaced a real gap: the
PATH/install issues are fixed (winget install works), but under the Windows
runner's ConPTY the Zellij session never renders — dump-screen stays empty and
wait_ready times out with "pane not rendered yet" for every interactive test.
That's a harness-runtime gap on Windows (and sk's escape-code disambiguation on
Windows would be a further blocker), so gate interactive.rs `#![cfg(not(windows))]`
with a TODO, keeping the harness code cross-platform.

Since no Windows test now uses the harness, drop the winget Zellij install from
the Windows leg; Linux/macOS still install it via taiki-e. Adjust the docs that
claimed the e2e tests run on Windows.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wTqRsi31RYJXEM3ZZQhQU

* test(e2e): gate Zellij harness tests to Linux only

The Zellij-backed e2e harness renders reliably under the Linux CI runner,
but on the macOS and Windows runners the pane never comes up under their
PTY (`wait_ready` times out with "pane not rendered yet"). Restrict all
four e2e test files (interactive, execute, popup, listen) to
`#![cfg(target_os = "linux")]`, install Zellij only on the Linux runner,
and update the harness/agent/architecture docs to match.

The harness code stays cross-platform so macOS/Windows e2e can be
re-enabled once their runners render the session.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wTqRsi31RYJXEM3ZZQhQU

* test(e2e): make the Zellij harness render on macOS and Windows

The Zellij e2e harness previously only came up reliably on the Linux CI
runner; on macOS and Windows the pane never rendered and every e2e test
timed out with "pane not rendered yet". Root cause (surfaced by capturing
the Zellij client's PTY output): Zellij's client/server startup handshake
is racy — the client occasionally dies with "Received empty unknown from
server" and the session never renders. It's rare on Linux (flaky) but
frequent on the cold macOS/Windows runners.

Harden the harness so it renders everywhere instead of gating tests to
Linux:

- Detect a dead session fast (drain thread flags client PTY EOF) and
  respawn a fresh session, up to SESSION_SPAWN_ATTEMPTS times, instead of
  waiting out the whole render budget and failing.
- Resolve the pane's shell to an absolute `bash` path via `which`; the
  Zellij server's own environment may not have `bash` on PATH on the
  macOS/Windows runners, which would leave the pane with no shell to render.
- Nudge the client's terminal size until the server gives the pane a
  non-zero geometry to render into (the initial size can be dropped under
  ConPTY / a cold runner).
- Give the first render its own longer budget and, on timeout, surface a
  tail of the Zellij client output for diagnosing runners we can't
  reproduce locally.

Un-gate the tests accordingly: interactive.rs (pure harness) now runs on
Linux, macOS and Windows; execute.rs/popup.rs/listen.rs go back to
#![cfg(unix)] (Linux + macOS) — their Windows-incompatibility is POSIX
mock binaries / a unix socket, unrelated to the multiplexer. CI installs
Zellij on all three OSes (taiki-e on Linux/macOS, winget on Windows) and
the nextest job gets a 45-minute cap so a harness regression fails fast.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wTqRsi31RYJXEM3ZZQhQU

* test(e2e): fix macOS session-name rejection via short ZELLIJ_SOCKET_DIR

The macOS runner failed every Zellij e2e test at CLI-parse time:

  error: Invalid value "skim_e2e_..." for '--session <SESSION>':
         session name must be less than 0 characters

This is not the render race the previous commit addressed. Zellij places
each session's unix socket at `$ZELLIJ_SOCKET_DIR/<protocol>/<session>`,
and a unix socket path is length-capped by the OS (~104 bytes on macOS).
Zellij's default base is `$TMPDIR/zellij-<uid>`; on the macOS runners
`$TMPDIR` is a long `/var/folders/…` path that leaves ~0 bytes for the
session name, so Zellij rejects every name and the client exits before it
attaches (zellij-org/zellij#4211). Linux's short `/run`|`/tmp` base never
hits this, which is why it only failed on macOS.

- Export ZELLIJ_SOCKET_DIR=/tmp/skim-zj (a short base) on every zellij
  invocation — the attached client, `action`, and `run` — so they share a
  short socket path well under the cap on Linux and macOS alike.
- Shorten session names (`sk_<=10 chars_<6 rand>`): several were derived
  from long test names (e.g. execute_interactive_child_keeps_receiving_
  keys_fullscreen) and exceeded Zellij's ~36-char limit and ate socket
  budget; the random suffix still keeps them unique.

Also fix a stale doc command in AGENTS.md (`cargo nextest --tests` ->
`cargo nextest run --tests`), per PR review.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wTqRsi31RYJXEM3ZZQhQU

* test(e2e): answer the DSR cursor-position probe so Windows renders

The Windows nextest leg hung on every interactive.rs e2e test:

  Error: pane not rendered within 60s. zellij client output tail:
  \u{1b}[6n

The captured client output was a single `ESC[6n` — a Device Status
Report requesting the cursor position. Under the Windows ConPTY the
Zellij client probes the terminal size by asking for the cursor position
and blocks until the terminal replies; on Unix the size comes from the
PTY ioctl, so the client never waits (which is why only Windows hung).

The harness owns the master PTY — it *is* the terminal — so the drain
thread now watches for `ESC[6n` and writes back a Cursor Position Report
(`ESC[24;80R`, reporting the 24x80 pane). This unblocks the client so the
pane renders. The reply is harmless on Linux/macOS (all 45 e2e tests
still pass there), keeping interactive.rs on all three platforms.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wTqRsi31RYJXEM3ZZQhQU

* test(e2e): address review nits in the Zellij harness

Follow-ups from PR review, none affecting the cross-platform fixes:

- zellij_socket_dir() now returns io::Result and propagates a
  create_dir_all failure through run()/action()/spawn_once() instead of
  swallowing it, so a socket-dir problem surfaces directly rather than as
  a confusing downstream Zellij error.
- Fix a latent typo in the (currently unused) assert_line!/line! macro:
  std::io::std::io::Error{,Kind} -> std::io::Error / std::io::ErrorKind,
  so the macro compiles if a test ever uses it.
- tempfile() returns an InvalidData error instead of panicking on a
  non-UTF-8 temp path.

Skipped the reviewer's suggestion to stop trimming captured output: the
trim is load-bearing. It drops Zellij's blank padding rows so capture()[0]
is the bottom content line that every test indexes against; stripping only
CR/LF would reintroduce ~20 empty rows and shift every index. No test
exercises intentionally-spaced items, so there is no real defect.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wTqRsi31RYJXEM3ZZQhQU

* test(e2e): silence unused_assignments warning in wait()

`last_err` was initialised to `None` and always overwritten before it
could be read, so the initial assignment was dead (unused_assignments
warning at the top of every test build). Return the current predicate
error directly on timeout instead of stashing it — same behaviour (the
most recent error is surfaced), no dead variable, no warning.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wTqRsi31RYJXEM3ZZQhQU

* test(e2e): wrap assert_line! timeout error to 120 columns

Pure formatting: split the Err/Error::new/format! construction in the
(rustfmt-skipped) assert_line! macro body across lines to satisfy the
repo's 120-column limit. No behaviour change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wTqRsi31RYJXEM3ZZQhQU

* test(e2e): guard against [-0] in the negative-index DSL macro

@method_neg_dispatch used `lines.len() >= $idx`, which is always true for
$idx == 0, so `@capture[-0]` would index `lines[lines.len()]` and panic.
Require `$idx > 0` in both the predicate and diagnostic paths so a `[-0]`
index falls through to the graceful "not enough lines" / "<no line>"
handling instead. No current test uses negative indices; this only closes
the latent edge case. Per PR review.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wTqRsi31RYJXEM3ZZQhQU

* test(e2e): use forward slashes for Windows paths in the bash command

With the DSR fix the Windows pane now renders and runs the command, which
surfaced the next issue: the harness drives a `bash` shell but embedded
native Windows paths (backslashes) into the command string. bash treats
`\` as an escape, so `.\target\release\sk.exe` collapsed to
`.targetreleasesk.exe` ("command not found") and the `C:\Users\...`
redirect/mv targets would mangle the same way.

Convert `\` to `/` for the `sk` binary and the outfile when building the
bash command in sk(); bash on Windows accepts `./target/release/sk.exe`
and `C:/Users/...`. On Unix the paths have no backslashes so it is a
no-op, and the stored outfile the test reads back keeps native separators.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wTqRsi31RYJXEM3ZZQhQU

* test(e2e): reap the Zellij client child in Drop

ZellijController::drop killed the client with child.kill() but never
waited on it, so on Unix each dropped controller left a zombie until the
test binary exited — and many controllers are created per binary. Pair
the kill with child.wait() (matching output_with_timeout) so the process
is reaped immediately. Per PR review.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wTqRsi31RYJXEM3ZZQhQU

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-23 16:32:29 +00:00
skim-rs-bot[bot] 9016a712a2
release: v5.5.0 (#1135)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-23 12:16:07 +00:00
Loric ANDRE 0415ea7bcd fix: do not panic on push/pop keyboard enhancement flags failure 2026-07-23 13:49:15 +02:00
LoricAndre 3644242897
feat: add double-click as a bindable trigger (#1134)
* feat: add double-click as a bindable trigger

closes #313

* typo
2026-07-21 14:24:47 +00:00
skim-rs-bot[bot] b2a732efa0
release: v5.4.0 (#1133)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-21 12:43:12 +00:00
LoricAndre 4f4cd15a42
docs: update coverage url 2026-07-21 14:14:34 +02:00
Loric ANDRE df683e34a7 ci: actually push apt 2026-07-21 13:56:24 +02:00
Loric ANDRE d4c878893b ci: publish apt repo 2026-07-21 13:52:54 +02:00
LoricAndre 776d708ede
feat: allow binding actions & more events (#1125)
* Add `start` and `load` events alongside `change`

Introduce `start` and `load` bindable events, mirroring the existing
`change` event, so `--bind start:<action>` and `--bind load:<action>`
work.

To avoid scattering magic high-F-key literals (`F(255)` for `change`),
add a `SkimEvent` enum in `binds.rs` with `Start`, `Load` and `Change`
variants that transparently convert to the reserved `KeyEvent`s used to
route them through the keymap. `parse_key` now accepts the friendly
names `start`, `load` and `change` via `SkimEvent::from_name`. The keymap
key type stays `KeyEvent`, so the public API is unchanged.

Firing:
- `change` is emitted by `on_query_changed` (now via the named variant).
- `start` fires exactly once when skim enters its event loop
  (`Skim::fire_start_event`).
- `load` fires once the reader has finished AND the freshly-read items
  have been rendered into the list, so a `load` binding acts on a
  fully-populated, stable list. It is re-armed on `reload`.

Add unit coverage for the event-name round-trip and integration tests
for `start` and `load` bindings, and document the events in
ARCHITECTURE.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019QKxusGbxLxqWdYyeqrb68

* Add result/focus/zero/one events and action follow-up bindings

Extend the bindable-event set and generalise binding so that actions can
themselves be bound.

New finder events (fired from the post-draw `Event::Render` path, so a
binding sees a stable, up-to-date list):
- `result` — filtering for the current query completed
- `focus`  — the focused item changed (cursor move or result update)
- `zero`   — a completed search has no matches
- `one`    — a completed search has exactly one match

`zero`/`one` read `MatcherControl::get_num_matched()` rather than the
rendered list count, which can briefly lag the matcher.

Actions as events: any action can be bound as if it were an event, so a
follow-up chain runs after it (e.g. `reload:first`, `first:last`). This is
parsed by `parse_action_binds` into `SkimOptions::action_binds` (keyed by
`Action::name`) and applied in `handle_action`, which now wraps the
per-variant `dispatch_action`.

- Keys win: a name shared by a key and an action binds the key; use an
  `act-` prefix to target the action (`act-up:down`).
- New `skip` action suppresses the triggering action's own behaviour, so
  `act-up:skip+down` remaps the up action to down and `up:skip` disables
  the up key.

Also fixes `parse_key` so a non-numeric `f…` name (e.g. `focus`, `first`)
falls through to name/event matching instead of erroring on the function-
key branch.

Adds unit and snapshot tests and documents everything in ARCHITECTURE.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019QKxusGbxLxqWdYyeqrb68

* Rename skip action to suppress and document it in the manpage

Rename the `skip` action to `suppress`, which more clearly conveys that
it cancels the triggering action's default behaviour. Add it to the
manpage actions list, noting that when bound to an action it suppresses
that action's default (so the rest of the chain runs in its place), and
when bound to a key it is equivalent to `ignore`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019QKxusGbxLxqWdYyeqrb68

* Fire finder events from callbacks instead of the render path

Move the synthetic finder events off the per-render check:

- `focus` now rides along with `App::on_selection_changed` (via a small
  `take_focus_event` helper), firing only when the focused item actually
  changes on cursor movement.
- `load`/`result`/`zero`/`one` track async reader/matcher completion, which
  has no synchronous callback, so `App::poll_completion_events` edge-triggers
  them from the `Heartbeat` handler rather than the render path. A `Render`
  is queued just before them so a list-inspecting binding (e.g. `load:first`)
  still sees the finished results.

This removes the branching that previously ran on every render tick and
keeps the event logic out of the unrelated `dispatch_action` arms.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019QKxusGbxLxqWdYyeqrb68

* misc tweaks incl. noremap

* fixes

* docs: document new binds

* coderabbit review

* fix: address Copilot review comments on action binds

- Split `--bind` specs with top-level comma splitting in `SkimOptions::build`
  so commas inside parenthesized action arguments (e.g.
  `act-up:execute(echo a,b)`) no longer garble follow-up bindings. Reuses the
  existing `split_top_level` helper (now `pub(crate)`), matching
  `KeyMap::add_keymaps_str`.
- Correct the misleading `load` event comment in `check_reader`: the event is
  fired from `App::poll_completion_events` (the heartbeat handler), not the
  render path.
- Add a unit test covering commas inside action arguments.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pxems1gNKKezFcxtZfURJp

* fix: harden action-trigger binds (logging, runtime bind/unbind, API surface)

Address three review findings on the action-trigger feature:

- Log dropped `--bind` specs: an unknown trigger name or an invalid
  follow-up chain in parse_action_binds is now reported via debug!
  instead of vanishing silently, matching the keymap path's behavior.
- Make the runtime `bind`/`unbind` actions manage action triggers as
  well as keys: `bind(act-up:last)` merges into action_binds and
  `unbind(act-up)` removes the trigger, with the same keys-win
  precedence as `--bind`. Trigger-name resolution is shared through a
  new binds::action_trigger_name helper.
- Narrow the new App fields (reader_done, load_event_fired,
  result_pending) to pub(crate): they are a Skim<->App coordination
  protocol, not public API.

Update the manpage (regenerated sk.1) and ARCHITECTURE.md accordingly,
and cover the new behavior with unit tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TgSqqsXn1GfQRZhQWAWi7t

* fix: don't abort the event loop on an invalid if-* branch chain

`if-*` branch chains are stored unparsed by `parse_action`, so an invalid
action name only surfaces when the binding fires. `dispatch_conditional`
propagated that parse error out of `App::handle_event`, killing the whole
finder mid-session on a bind typo. Log and skip the chain instead,
matching the invalid-chain handling of `parse_action_binds`.

Also refresh the stale line numbers in the ARCHITECTURE.md
cross-reference table.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TgSqqsXn1GfQRZhQWAWi7t

* fix: make sure to render before start in sync mode

* chore: push snaps

* fix: misc

* fix: address review comments on start event, if-* logging and docs

- skim.rs: retry the one-shot `start` event from `tick()` so a momentarily
  full bounded `event_tx` at the `start()`/`enter()` call sites can no longer
  drop it permanently. Idempotent via the `start_fired` guard.
- app.rs: log an invalid `if-*` conditional action chain at `warn!` instead of
  `debug!` so a misconfigured binding is discoverable by default.
- ARCHITECTURE.md: clarify that `Skim::check_reader` only records `reader_done`;
  `App::poll_completion_events` owns and emits `load`/`result`/`zero`/`one`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019QKxusGbxLxqWdYyeqrb68

* chore: minor formatting

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-21 11:42:47 +00:00
dependabot[bot] 76a27ed80c
chore(deps): bump serde_json in the cargo-prod group (#1131)
Bumps the cargo-prod group with 1 update: [serde_json](https://github.com/serde-rs/json).


Updates `serde_json` from 1.0.150 to 1.0.151
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.150...v1.0.151)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-version: 1.0.151
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-prod
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-21 11:02:20 +02:00
LoricAndre d7e294eb7d
fix: allow execute actions to run interactive commands (#1132)
* fix(tui): stop input reader and give execute() children their own tty

Interactive/ncurses programs run via an `execute()` action (e.g. `ncdu`)
would freeze after a few keystrokes. Two independent problems caused it:

1. skim's background input reader (the `EventStream` task started in
   `Tui::start`) kept reading the terminal while the child ran, so skim and
   the child raced for keystrokes on the same tty — roughly half the keys
   were stolen from the child.

2. The child inherited skim's stdin (fd 0), which is a pipe whenever items
   are piped in (`find | sk`). An interactive child then had no keyboard
   source at all.

Fix both:

- Add `Tui::stop_and_join`, which cancels the event-pump task and blocks
  until it has dropped its `EventStream`, guaranteeing skim has released the
  terminal before the child starts. `run_foreground` calls it before running
  the child and `Tui::start` after.

- Give the child its own stdin opened from the controlling terminal
  (`/dev/tty`, or `CONIN$` on Windows), falling back to inheriting skim's
  stdin if that fails.

Because running a foreground process needs the `Tui` (which `handle_action`
does not have), `Execute` now only expands the command and returns a new
`Event::RunExecute`, which `handle_event` runs via `run_foreground`. This
mirrors the existing `RunPreview` pattern. `execute-silent` is unchanged.

Update ARCHITECTURE.md (event dispatch table, terminal lifecycle, and
cross-reference line numbers) and add tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QBQ1chjHyN3JNTWqMMED44

* test(execute): add tmux e2e test; fix reader restart and post-execute repaint

Add a tmux-based integration test (tests/execute.rs, unix-gated so it runs
on the Linux and macOS CI legs) that drives an interactive child through an
`execute` action and asserts it keeps receiving keystrokes, then that skim
is interactive again once the child exits. It covers both the fullscreen and
inline (`--height`) layouts.

Writing the test surfaced two bugs in the execute reader-suspend work that
unit tests could not catch:

1. Reader never resumed. `Tui::stop_and_join` cancels the shared
   `CancellationToken`, but `Tui::start` reused that same token — and a
   cancelled token stays cancelled — so the respawned reader observed the
   cancellation immediately and exited without reading input. `start` now
   installs a fresh token on every call (also fixing the latent
   restart-while-running path).

2. Post-execute repaint hung when stdout was redirected. The repaint went
   through `Event::Redraw` → `tui.clear()`, and ratatui's `Terminal::clear`
   queries the cursor position, which crossterm writes to stdout via
   `ESC [ 6 n`. skim renders to stderr and its stdout is routinely redirected
   (`sk > file`), so the query reached no terminal, got no reply, and stalled
   the UI for seconds before erroring out. Replace it with
   `Tui::force_full_redraw`, which resets ratatui's diff buffers for a full
   repaint with no cursor query and works for both fullscreen and inline
   viewports.

Update ARCHITECTURE.md and the cross-reference table accordingly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QBQ1chjHyN3JNTWqMMED44

* ci: fix matrix

* fix: pop kitty keyboard flag before entering execute

* chore: remove duplication in backend.rs

* chore: refactor

* fix: kitty maintains a different set of flags in alt screen

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-21 09:01:53 +00:00
skim-rs-bot[bot] 5c1c3379fb
release: v5.3.2 (#1130)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-20 22:37:12 +00:00
LoricAndre 24a24619ea
ci: publish .deb, .rpm and winget packages on release (#1129)
* ci: add .deb and .rpm packages to releases via dist

Configure cargo-deb and cargo-generate-rpm to build Linux packages
containing the sk executable, the man pages (sk.1, sk-tmux.1) and the
bash/zsh/fish shell completions.

A new reusable workflow (package.yml) builds both packages and uploads
them under an artifacts-* name. It is wired into the release pipeline as
a dist global-artifacts-job in dist-workspace.toml, and release.yml is
regenerated with `dist generate` (not hand-edited) so dist's host job
attaches the packages to the GitHub Release.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WmNevtS7EV3vXyYRTVu7Aw

* ci: drop sk-tmux man page from .deb and .rpm packages

Package only the sk.1 man page; the sk-tmux.1 page is no longer shipped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WmNevtS7EV3vXyYRTVu7Aw

* ci: temporarily build release artifacts on PRs

Set dist pr-run-mode = "upload" so the .deb and .rpm (and the other
release artifacts) are built and uploaded on pull requests, allowing the
packages to be downloaded and verified before merging.

This is temporary and should be reverted before merge.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WmNevtS7EV3vXyYRTVu7Aw

* ci: install packaging tools via taiki-e/install-action

Address review feedback on package.yml:
- Install cargo-deb and cargo-generate-rpm with taiki-e/install-action
  (prebuilt binaries) plus a Swatinem/rust-cache step, matching the
  patterns used in test.yml, instead of compiling them with cargo install.
- Drop the `--output target/debian` flag from `cargo deb`; the default
  target/debian/ directory is what the collect step expects.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WmNevtS7EV3vXyYRTVu7Aw

* ci: build arm64 .deb and .rpm packages too

Turn the package job into a matrix that builds natively for both amd64
(ubuntu-22.04) and arm64 (ubuntu-22.04-arm), producing a .deb and .rpm
per architecture. Artifacts are uploaded under per-arch names so dist's
host job attaches all of them to the release.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WmNevtS7EV3vXyYRTVu7Aw

* ci: submit releases to winget via winget-releaser

Add a dist publish job that, after the GitHub Release is created, submits
the new version to the Windows Package Manager Community Repository using
vedantmgoyal9/winget-releaser and the Windows .zip artifact dist already
attaches to the release.

Uses the package identifier and installer regex from skim-rs/skim#769:
  identifier: skim-rs.skim
  installers-regex: '-pc-windows-msvc\.zip$'

Wired in through publish-jobs in dist-workspace.toml; release.yml is
regenerated with `dist generate` (not hand-edited). Prereleases are never
submitted. Requires a WINGET_TOKEN secret (a public_repo-scoped PAT that
owns a microsoft/winget-pkgs fork under skim-rs).

Refs: skim-rs/skim#769

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WmNevtS7EV3vXyYRTVu7Aw

* ci: validate package version against the release plan

Consume the `plan` input dist passes to the package job: assert the
crate version equals the version dist planned for this release, so the
source-built .deb/.rpm can't silently drift from the release.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WmNevtS7EV3vXyYRTVu7Aw

* Update .github/workflows/winget.yml

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update .github/workflows/package.yml

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* docs: document .deb, .rpm, winget and scoop installation

Add the new install methods to the README: winget and Scoop rows plus a
Debian/RPM section with install commands, covering amd64 and arm64.

Also clarify in winget.yml that WINGET_TOKEN must be a classic PAT
(fine-grained tokens can't open the winget-pkgs PR).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WmNevtS7EV3vXyYRTVu7Aw

* fix: install cargo-deb manually to avoid libc version mismatch

* ci: use blacksmith runners for long job

* fix: version spec for cargo install

* chore: revert pr action upload

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-07-20 22:11:51 +00:00
skim-rs-bot[bot] 6dbe37a7fa
release: v5.3.1 (#1128)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-19 21:42:17 +00:00
LoricAndre 6f94d80973
fix: ignore KeyEventState (#1127)
* fix: ignore KeyEventState

* test: add numlock ignore test

* Update src/tui/app_tests.rs

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-07-19 21:19:59 +00:00
skim-rs-bot[bot] 4d093a42e1
release: v5.3.0 (#1123)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-19 15:25:03 +02:00
Loric ANDRE 3c3f26ebc9 fix: kitty keyboard protocol
closes #1126
2026-07-19 14:53:51 +02:00
LoricAndre ad97bfa7de
feat: add bind and unbind actions (#1121)
* feat: add `bind` and `unbind` actions

Add two new actions that allow programmatic (re)binding of keys at
runtime, both through `--bind` keybindings and the IPC/listen socket:

- `bind(key:action[+action][,key:action…])` adds one or more bindings,
  reusing the same parsing/merging logic as the `--bind` CLI option.
  Existing bindings for the same key are replaced.
- `unbind(key[,key…])` removes the bindings for a comma-separated list
  of keys, mirroring fzf's `unbind(...)` semantics.

Because the `Action` enum derives serde when the `listen` feature is
enabled, both actions are drivable over the IPC socket for free.

Covered by unit tests for parsing (`event_tests.rs`) and dispatch
(`app_tests.rs`), plus IPC integration tests (`listen.rs`). Manpage and
ARCHITECTURE.md updated with the new actions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JYqqomFjYqqXd5NvkVxfbQ

* feat: add `bind` and `unbind` actions

* chore: generate files

* fixes

* fixes

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-18 14:22:41 +00:00
LoricAndre ff98133bbd
ci: add public-api check (#1124)
* ci: add public-api check

* test: try pushing an API breaking change

* fix: install nightly

* test: revert breaking
2026-07-18 15:57:01 +02:00
LoricAndre dce26d622a
feat: add --hide-nth to hide fields from display but keep them searchable (#1122)
* Add --hide-nth flag to hide fields while keeping them searchable

Introduce a `--hide-nth <fieldspec>` option that takes the same
comma-separated field index expressions as `--nth`/`--with-nth`. The
listed fields are removed from the displayed line but remain part of the
text used for matching, so a query can still match them. Characters in
the hidden fields are ignored for match highlighting and horizontal
scrolling.

Implementation:
- Resolve the fieldspec to byte ranges in the same coordinate space as
  the matching/display text and store them as `hidden_ranges` in
  DefaultSkimItem metadata, exposed via a new `SkimItem::hidden_ranges()`
  trait method. text()/output() keep the full text so hidden fields stay
  searchable and are preserved on output.
- DefaultSkimItem::display() removes hidden characters and remaps match
  highlight positions into visible coordinates (project_visible_text /
  project_match_indices); this path takes precedence over ANSI styling.
- ItemRenderer::render_item applies the same projection to derive the
  visible sub-line text and hscroll match range, so hidden characters are
  ignored for horizontal scrolling.

Add unit tests for range normalization/projection and item behavior,
plus insta snapshot tests covering display removal, searchability, and
hscroll. Update ARCHITECTURE.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BCunXeAFMYAFduTc8SNjSM

* Preserve ANSI colors for surviving text under --hide-nth

Previously the hidden-field rendering path was applied ahead of the ANSI
display branch and rebuilt the line from the ANSI-stripped text, so
combining --hide-nth with --ansi dropped the colors of the visible
fields.

Integrate hidden-field removal into the ANSI branch instead: after
parsing the styled spans, drop the hidden characters while preserving
each span's style (retain_visible_spans) and remap the match positions
into the resulting visible coordinate space, then run the normal
highlighting. The plain (non-ANSI) branch keeps its project-and-to_line
handling. Surviving characters now keep their ANSI colors while hidden
fields stay searchable.

Add unit tests for ANSI color preservation and remapped highlighting,
plus ANSI color-snapshot integration tests. Update ARCHITECTURE.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BCunXeAFMYAFduTc8SNjSM

* Set hidden fields via builder instead of DefaultSkimItem::new param

Remove the `hidden_fields` parameter from `DefaultSkimItem::new` and set
the hidden fields through a `hidden_fields(&[FieldRange], &Regex)`
builder method instead. The builder resolves the fields against the
item's own `text()` (the same coordinate space `new` would have used),
so the result is identical while keeping `new`'s signature unchanged for
its many existing call sites.

The reader chains `.hidden_fields(&opt.hidden_fields, &opt.delimiter)`
onto construction. Revert the extra `&[]` argument at the other call
sites (selector, fuzz target, tests) and update the hide-nth tests to
use the builder. Update ARCHITECTURE.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BCunXeAFMYAFduTc8SNjSM

* chore: generate files

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-18 13:13:43 +00:00
LoricAndre 55aa7dacd5
feat: add 'left' and right info display modes (#1120) 2026-07-18 12:34:21 +00:00
skim-rs-bot[bot] 57a7487323
release: v5.2.0 (#1119)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-17 17:58:07 +00:00
LoricAndre bb6c03f377
feat: reduce binary size by removing uncommon image formats and color_eyre (#1118)
* Shrink binary: trim image decoders and swap color-eyre for eyre

Two dependency changes that cut the default `sk` binary from 13.6 MiB to
8.55 MiB (-5.06 MiB, -37%) with no loss of core functionality:

- image: build the `image` crate with only the common decoders (png,
  jpeg, gif, webp) instead of its full default format set, and drop
  ratatui-image's `image-defaults`. This removes AVIF encoding (ravif,
  avif-serialize), OpenEXR (exr), TIFF, QOI and other decoders that are
  irrelevant to terminal image previews. Previewing those formats now
  falls back to the normal command preview.

- error handling: replace color-eyre with plain eyre. color-eyre only
  provided colored panic/error backtraces; skim used none of its
  Section/Help extension APIs. This drops the backtrace/gimli/addr2line/
  color-spantrace stack. `color_eyre::install()` is no longer needed.

Tests, benches and examples are migrated from color_eyre to eyre so the
crate is fully removed from the dependency graph.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BQtxeCS4gM7dumghqmNgST

* chore: fmt

* docs: ARCHITECTURE.md

* chore(flake): add cargo-bloat

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-17 17:29:43 +00:00
LoricAndre 21f7ef8795
feat: collapsed borders by default and --border-no-collapse flag (#1117)
* feat: collapsed borders by default and `--border-no-collapse` flag

* chore: generate files
2026-07-17 16:59:14 +00:00
skim-rs-bot[bot] f87ce2075d
release: v5.1.4 (#1116)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-17 12:45:54 +00:00
juneboku 29ca6f22bd
fix: preserve input order with --no-sort (#1115)
* fix: preserve input order with --no-sort

Workers grab 4096-item chunks from a shared queue, so the order in
which worker results are concatenated is nondeterministic. With
--no-sort the merged list was left in that arrival order, which
scrambles the display order once the item count exceeds
num_workers * chunk_size (~24k items on a 10-core machine) and makes
--filter output nondeterministic for large inputs.

Sort matched items by rank.index (the original input position) when
no_sort is set: each worker sorts its accumulator in prepare, and the
final merge exploits the k sorted runs, mirroring the sorted path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* avoid extra sorts by exploiting the thread_pool stability

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Loric ANDRE <loric.andre@pm.me>
2026-07-17 12:16:46 +00:00
skim-rs-bot[bot] b41a724a2a
release: v5.1.3 (#1112)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-16 21:58:51 +00:00
LoricAndre 04c0a5b6c8
ci: fix fixed viewport resize test in non-interactive env (#1114) 2026-07-16 21:30:34 +00:00
Loric ANDRE 9be7392911 fix: manually resize the Tui when not in fullscreen mode
closes #113
2026-07-16 22:14:17 +02:00
Loric ANDRE b1aa46917b chore: fix CHANGELOG duplication 2026-07-16 14:29:18 +02:00
skim-rs-bot[bot] 4047a5e3fd
release: v5.1.2 (#1111)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-16 12:36:16 +02:00
Loric ANDRE 95c360996a ci: add changelog as job output 2026-07-16 12:06:48 +02:00
Loric ANDRE 09479547eb ci: update app-id to client-id 2026-07-16 11:51:24 +02:00
Loric ANDRE 44b9cf2011 ci: add Release PR workflow 2026-07-16 11:43:44 +02:00
Loric ANDRE 8ea0afb63e release: v5.1.1 2026-07-16 10:55:28 +02:00
Lei Zhang 974bd267cc
fix: handle deprecated --expect flag in sk 4.x vim plugin (#1057)
* fix: handle deprecated --expect flag in sk 4.x vim plugin

sk 4.x deprecated --expect and no longer outputs the pressed key name
as the first line of results. This caused s:common_sink to silently
return without opening files, since it expected at least 2 lines
(key + filename). Now handles both old and new output formats.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(vim): replace deprecated expect with accept bindings

* fix(vim): shell-escape accept bindings

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Loric ANDRE <loric.andre@pm.me>
2026-07-16 10:19:04 +02:00
dependabot[bot] 815a9b4f94
chore(deps): bump the cargo-prod group with 3 updates (#1109)
* chore(deps): bump the cargo-prod group with 3 updates

Bumps the cargo-prod group with 3 updates: [frizbee](https://github.com/saghen/frizbee), [gungraun](https://github.com/gungraun/gungraun) and [thread_local](https://github.com/Amanieu/thread_local-rs).


Updates `frizbee` from 0.10.0 to 0.11.0
- [Commits](https://github.com/saghen/frizbee/compare/v0.10.0...v0.11.0)

Updates `gungraun` from 0.19.3 to 0.19.4
- [Release notes](https://github.com/gungraun/gungraun/releases)
- [Changelog](https://github.com/gungraun/gungraun/blob/main/CHANGELOG.md)
- [Commits](https://github.com/gungraun/gungraun/compare/v0.19.3...v0.19.4)

Updates `thread_local` from 1.1.9 to 1.1.10
- [Release notes](https://github.com/Amanieu/thread_local-rs/releases)
- [Changelog](https://github.com/Amanieu/thread_local-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Amanieu/thread_local-rs/compare/v1.1.9...v1.1.10)

---
updated-dependencies:
- dependency-name: frizbee
  dependency-version: 0.11.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: cargo-prod
- dependency-name: gungraun
  dependency-version: 0.19.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-prod
- dependency-name: thread_local
  dependency-version: 1.1.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-prod
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix: frizbee

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Loric ANDRE <loric.andre@pm.me>
2026-07-16 10:01:35 +02:00
Loric ANDRE c4bc5e1be4 release: v5.1.0 2026-07-09 14:34:06 +02:00
Loric ANDRE ef59339727 fix: unknown border values fallback to plain (closes #1107) 2026-07-09 13:58:59 +02:00
Loric ANDRE 1effd9a69d test: fix arinae max pat len test after constant update 2026-07-09 12:41:38 +02:00
Loric ANDRE 0a4d4b2ea0 fix: less restrictive max pattern in ari matcher 2026-07-09 12:00:38 +02:00
LoricAndre 026a622a9d
feat: add cargo-fuzz targets for hand-rolled text parsers (#1106)
* feat: add cargo-fuzz targets for hand-rolled text parsers

Skim's most panic-prone code is the hand-written byte/char-index
bookkeeping over untrusted input: ANSI stripping, --nth/--with-nth field
extraction, the fuzzy matching algorithms, the query->engine->match
pipeline, and the --bind key-map parser. Add five cargo-fuzz (libFuzzer)
targets covering each, asserting real invariants (char-boundary safety,
monotonic index mappings, match indices in bounds) rather than just
catching panics, plus a CI workflow that runs them on every push/PR
touching src/ or fuzz/ and a longer nightly session via cron.

* ci: wire fuzz targets into the existing test matrix

Replace the standalone fuzz.yml workflow with a `fuzz` job in the main
test.yml matrix, running each of the 5 fuzz targets for 60s (5 minutes
total per CI run) alongside nextest/clippy/msrv.

* ci: remove standalone fuzz workflow

Superseded by the fuzz job now in test.yml.

* ci: run fuzz job as a single job on the platform matrix

Reuse the existing linux/macos/windows matrix instead of a separate
per-target matrix; run all 5 fuzz targets sequentially in one step
(60s each, 5 minutes total). cargo-fuzz doesn't support Windows, so
the fuzzing step is skipped there while still installing the
toolchain for consistency with the rest of the matrix.

* ci: reuse existing yaml anchors in the fuzz job

Use *toolchain instead of a bespoke nightly-install step, matching
the coverage job's pattern of letting `cargo +nightly` auto-provision
the toolchain on demand.

* nix: add cargo-fuzz to the tests devShell

Makes cargo-fuzz available via `nix develop` alongside the other test
tooling, matching what CI installs for the fuzz job.

* ci: install cargo-fuzz via taiki-e/install-action

Matches how the other CI-only cargo subcommands (nextest, cargo-msrv,
cargo-llvm-cov) are installed, and is faster than compiling it from
source with cargo install.

* ci: force the native host target for cargo-fuzz

cargo-fuzz was picking a statically-linked musl target on the runner,
which fails since ASan can't link against a static libc. Pass the
actual host triple (from `rustc -vV`) explicitly so the sanitizer
build always targets the dynamically-linked gnu/darwin toolchain.

* ci: skip the whole fuzz job on windows

Rather than skipping just the fuzzing step, exclude the job entirely
for the windows-latest matrix entry via a job-level `if`, since
cargo-fuzz/libFuzzer has no Windows support.

* ci: gate the fuzz job with runner.os instead of matrix.os

Matches the runner.os-based conditionals already used elsewhere in
this workflow (the linux/macos dependency install steps) rather than
comparing matrix.os directly.

* ci: enable the fuzz job on windows without ASan

cargo-fuzz does support Windows, but AddressSanitizer on the MSVC
target needs the separate "C++ AddressSanitizer" VS component plus a
PATH tweak for its DLL, which this runner doesn't have configured.
Rather than skip the job, disable the sanitizer on Windows only
(--sanitizer none) and keep coverage-guided fuzzing there; our
targets assert via plain Rust panics so they don't depend on ASan.

* test: assert exact char_idx correctness in ansi_strip fuzz target

Replace the bounds-only char_idx check with an exact-equality check
against the char position of byte_pos in the original string. This
subsumes (and is stronger than) the monotonicity CodeRabbit flagged,
since strictly-increasing byte positions on char boundaries always
imply strictly-increasing char positions.

* ci: skip windows in fuzz job, scope job permissions

CI showed the Windows fuzz build fails with a real MSVC linker error
(LNK2001: unresolved __start/__stop___sancov_pcs) even with
--sanitizer none: MSVC's linker doesn't synthesize the section
boundary symbols that libFuzzer's coverage instrumentation requires,
so this is unrelated to the earlier ASan/PATH discussion and isn't
fixable by a sanitizer flag. Skip Windows via step-level `if`
(job-level `if` can't reference runner/matrix contexts). Also add an
explicit contents:read permissions block to the job.

* fix(fzy): fix unicode case-folding inconsistency causing overflow panic

The new fuzzy_match fuzz target found a real crash: FzyMatcher panicked
with "attempt to multiply with overflow" on choice="ű\0\0\0\u{1e}ű",
pattern="Űű".

Root cause: fzy_score's case-insensitive comparison used
char::to_ascii_lowercase (a no-op on non-ASCII letters like Ű/ű), while
the shared cheap_matches() prefilter (and the other matchers) use the
Unicode-aware char_equal(). This let cheap_matches accept a pattern
that fzy_score's own DP could then never actually align, since needle
char 'Ű' never matched any haystack position under ASCII-only folding.
The DP's SCORE_MIN sentinel ("impossible") isn't an absorbing element
under plain integer addition, so the broken alignment accumulated to a
value close to, but not exactly, SCORE_MIN, which then overflowed on
the final *SCORE_TO_SKIM conversion since only the exact sentinel was
special-cased.

Fix is_match to use the shared char_equal() so fzy.rs's case folding
matches cheap_matches and the other two matchers (skim.rs, clangd.rs
already do this). Also switch internal_to_skim_score to saturating_mul
as defense in depth, since fzy_score structurally always returns
Some(..) and has no other way to signal "no valid alignment" to the
caller.

* ci: try lld-link to get windows fuzzing working (no ASan)

MSVC ASan is documented broken on GitHub-hosted Windows runners
(actions/runner-images#8891 — ASan binaries crash with
STATUS_DLL_INIT_FAILED even with the runtime DLL on PATH, unresolved
upstream), so it's not viable here regardless of our config. Separately,
the sancov coverage instrumentation cargo-fuzz needs doesn't link with
MSVC's link.exe at all (missing __start/__stop section symbols).
Try switching the Windows leg to rustc's bundled LLD linker
(-C linker-flavor=lld-link -C link-self-contained=+linker) with
--sanitizer none, to at least get coverage-guided fuzzing (no ASan)
working there. Validating live against this PR's CI.

* ci: revert windows fuzzing attempt, exclude it again

The lld-link experiment ruled out the remaining option: LLD's COFF
driver hit the exact same missing __start/__stop___sancov_* symbols as
MSVC's link.exe. This confirms the section-boundary-symbol synthesis
libFuzzer's coverage instrumentation needs simply isn't implemented
for the COFF/Windows target in current LLVM/rustc — an upstream gap,
not a linker choice or CI config problem. Combined with MSVC ASan
being separately documented broken on GH-hosted Windows runners
(actions/runner-images#8891), there's no remaining avenue to try from
the workflow side. Back to excluding Windows from the fuzz job.

* ci: try windows fuzzing with default sanitizer + msvc dev env

Previous Windows attempts both used --sanitizer none, which removes
the ASan runtime that (on Windows) supplies the __start/__stop section
symbol shims libFuzzer's coverage instrumentation needs -- neither
linker synthesizes those on COFF. That's very likely why they failed
to link. Revert to the default sanitizer (address) and add
ilammy/msvc-dev-cmd to put the MSVC ASan DLL directory on PATH, per
the cargo-fuzz Windows setup guide and actions/runner-images#8891.
Testing live whether this builds, and whether the previously-reported
STATUS_DLL_INIT_FAILED runtime crash still reproduces on this runner
image.

* ci: point cargo at the real MSVC linker on windows

msvc-dev-cmd correctly set up Path, but Git Bash prepends its own
usr/bin ahead of it, so cargo picked up Git's coreutils `link`
(hardlink tool) instead of MSVC's link.exe. Set
CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER explicitly using
VCToolsInstallDir (set by msvc-dev-cmd) to sidestep PATH ordering
entirely.

* fix(event): parse_action returns None instead of panicking on missing args

The keymap_parse fuzz target found a real crash: KeyMap::from("/:if-")
panicked ("no arg specified for event if-") since parse_action's
documented behavior was to panic on if-* actions missing their
argument, even though the function already returns Option<Action> and
every other malformed/unrecognized action already resolves to None
via the surrounding parse_action_chain/KeyMap plumbing.

Fixed that case, and while checking for the same pattern elsewhere in
the function found four more reachable panics of the same kind
(add-char, execute, execute-silent, set-preview-cmd, set-query parsed
without their required argument), confirmed each panics via a small
repro before fixing. All now return None like every other malformed
action, consistent with the function's existing contract, instead of
panicking on user-supplied --bind strings.

* ci: add a single aggregate status check for branch rulesets

Add a ci-success job that depends on every other job in the workflow
and fails if any of them failed or were cancelled (tolerating
deploy-coverage-page's expected skip off master). This gives branch
protection / repository rulesets one stable check name to require,
instead of enumerating every matrix leg (nextest (linux), fuzz
(windows), etc.) individually.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-04 18:01:46 +02:00
Liam Dyer 8a1f2783a8
feat: bump frizbee to 0.10.0, thread local matcher (#1105)
* feat: bump frizbee to 0.10.0, thread local matcher

* feat: use frizbee on all architectures

* feat: use frizbee feature

* refactor: simplify frizbee config mutation

* docs: simplify frizbee thread local comment

* fixup! feat: use frizbee feature

* fixup! feat: use frizbee feature
2026-07-03 16:20:05 +00:00
Loric ANDRE 5a2dde8018 release: v5.0.0 2026-07-02 20:01:20 +02:00
dependabot[bot] 6c08be00c9
chore(deps): bump the gha-prod group with 4 updates (#1104)
Bumps the gha-prod group with 4 updates: [actions/checkout](https://github.com/actions/checkout), [emibcn/badge-action](https://github.com/emibcn/badge-action), [actions/upload-pages-artifact](https://github.com/actions/upload-pages-artifact) and [actions/deploy-pages](https://github.com/actions/deploy-pages).


Updates `actions/checkout` from 6 to 7
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v6...v7)

Updates `emibcn/badge-action` from 2.0.2 to 2.0.4
- [Release notes](https://github.com/emibcn/badge-action/releases)
- [Commits](https://github.com/emibcn/badge-action/compare/v2.0.2...v2.0.4)

Updates `actions/upload-pages-artifact` from 3 to 5
- [Release notes](https://github.com/actions/upload-pages-artifact/releases)
- [Commits](https://github.com/actions/upload-pages-artifact/compare/v3...v5)

Updates `actions/deploy-pages` from 4 to 5
- [Release notes](https://github.com/actions/deploy-pages/releases)
- [Commits](https://github.com/actions/deploy-pages/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: gha-prod
- dependency-name: emibcn/badge-action
  dependency-version: 2.0.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: gha-prod
- dependency-name: actions/upload-pages-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: gha-prod
- dependency-name: actions/deploy-pages
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: gha-prod
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-02 19:52:53 +02:00
LoricAndre f2ca01e187
feat!: feature-gate listen and image to allow opting out (#1103)
* feat!: feature-gate listen and image to allow opting out

This is breaking since disabling the default features now also disables
those. It is NOT breaking for cli users, only for library ones.

* fix: add warn on listener transfer failure
2026-06-29 15:35:50 +02:00
Loric ANDRE aeba919fab release: v4.10.0 2026-06-28 19:00:17 +02:00
Maximilian Roos bfec6e198d
feat(theme): add a themeable scrollbar color for the item list (#1101)
* feat(theme): add a themeable scrollbar color for the item list

The item-list scrollbar was the only rendered UI element without a
ColorTheme entry. ratatui's Scrollbar defaults thumb_style to an empty
Style, so the thumb merged nothing onto the cells it drew over and
inherited their fg/bg — most visibly the current-line highlight, which
the thumb adopted as the cursor scrolled past it.

Add a `scrollbar` color to ColorTheme, parse it from `--color`
(`scrollbar:<spec>`), default it per theme to the border color (the four
catppuccin themes use their muted `overlay0` instead), and pass it as the
Scrollbar thumb style. The thumb now reads as uniform chrome instead of
tracking whatever row sits under it. The colorless `none` theme leaves it
unset, so NO_COLOR still renders no thumb styling.

Documented in the README color table and the manpage; covered by theme
unit tests and @snap_color integration tests (default border color and a
custom --color=scrollbar override, both over the highlighted current line).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore: generate-files & misc

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Loric ANDRE <loric.andre@pm.me>
2026-06-28 18:48:28 +02:00
LoricAndre 5f4798325a
tests: improve coverage on matchers & algos (#1100)
* test(engine,fuzzy_matcher): add unit tests for branch coverage; fix use_cache(false) double-borrow

Add targeted unit tests to exercise every reachable branch in the `engine`
and `fuzzy_matcher` modules, measured with cargo-llvm-cov's branch coverage
on nightly. Tests use realistic inputs and assert concrete behaviour:

- engine: empty/offset/byte-range matching ranges in the fuzzy engine, the
  Frizbee and typo Arinae build paths, AND/OR empty-term filtering, and
  split-engine byte-range char exclusion.
- arinae: typo substitutions and deletions, non-ASCII dispatch, prefilter
  rejection paths, and direct kernel tests for the DP guards / band-skip /
  dead-row pruning that compute_banding makes unreachable through the API.
- clangd/fzy/skim/util: typo-DP substitution, deletion, gap and length-guard
  paths; ASCII/non-ASCII dispatch; single-char and score-only paths; and the
  assert_order failure diagnostics.

Fix a latent double-borrow bug: `use_cache(false)` in the clangd, skim and
fzy matchers called `RefCell::replace` on cache cells whose `RefMut` guards
were still alive, panicking on every match. Drop the guards before clearing
the caches so the option works (and is now covered by tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q93ttrw4JoXCjBezV2Skmm

* test(fuzzy_matcher): thread guard paths by isolating callees

Cover branches that are reachable only when the inner helper is invoked
directly with inputs the public matchers can never produce:

- clangd `match_bonus` with `Action::Miss` (callers always pass `Match`) —
  asserts the 30-point in-segment-after-miss penalty.
- fzy `internal_to_skim_score(SCORE_MIN)` sentinel mapping; the empty-pattern
  slow-path `n == 0` guard; and `fzy_score` driven with a non-subsequence
  needle so the position backtrace hits the column-0 fallback.

The branches that remain uncovered are now confirmed structurally
unreachable even via direct callee calls: const-generic monomorphization
artifacts, M-cell `!= SCORE_MIN` checks (an M-cell is never exactly the
sentinel after gap accumulation), a match cell at (i>0, j==0) that is always
SCORE_MIN, and short-circuit operands excluded by upstream invariants.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q93ttrw4JoXCjBezV2Skmm

* test(fuzzy_matcher/skim): thread arg-reachable guards in skim helpers

build_in_place_bonus's `b.len() > 1` and calculate_score_with_pos's
`op.is_none()` are unreachable through the public matcher (the real caller
never passes an empty choice or an over-wide column range), but they ARE
reachable by calling the private helpers directly with such arguments.
Cover both, leaving only genuinely argument-independent dead branches.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q93ttrw4JoXCjBezV2Skmm

* chore: misc checks & fixes

* fix: default bench arg

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* chore: remove magic number

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-27 00:16:15 +02:00
Loric ANDRE d14407196b release: v4.9.0 2026-06-26 00:53:37 +02:00
Jason Wang 7d4502201f
fix: refresh preview after mouse selection (#1095)
* fix: refresh preview after mouse selection

* Address mouse preview review feedback

* feat(ci): upload coverage report to pages for easier browsing (#1096)

* feat(ci): upload coverage report to pages for easier browsing

* fix: remove anchors

* fix: release report

* fix: coverage percent

* docs: update README

* chore: only on master

* test: add unit tests for the shell & manpage generators (#1098)

* test: add unit tests for the shell & manpage generators

* chore: propagate key bindings generation errors

* tests: improve coverage to 90% (#1099)

* tests: improve coverage to 90%

* feat: improve coverage

* remove most unix-only tests

* Update src/skim_tests.rs

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* fixes

* chore: misc

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* chore: cleanup

---------

Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Loric ANDRE <loric.andre@pm.me>
2026-06-26 00:36:07 +02:00
LoricAndre 7e2cdf3c8e
tests: improve coverage to 90% (#1099)
* tests: improve coverage to 90%

* feat: improve coverage

* remove most unix-only tests

* Update src/skim_tests.rs

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* fixes

* chore: misc

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-06-25 19:12:49 +00:00
LoricAndre 8187a12196
test: add unit tests for the shell & manpage generators (#1098)
* test: add unit tests for the shell & manpage generators

* chore: propagate key bindings generation errors
2026-06-24 18:18:15 +00:00
LoricAndre b56eed4125
feat(ci): upload coverage report to pages for easier browsing (#1096)
* feat(ci): upload coverage report to pages for easier browsing

* fix: remove anchors

* fix: release report

* fix: coverage percent

* docs: update README

* chore: only on master
2026-06-24 18:55:33 +02:00
LoricAndre e85cf6b708
chore(ci): use github's coverage (#1094)
* chore(ci): use github's coverage

* fix: perms

* chore: ci

* test(ci): comment perm to check triggers

* test(ci): readd perm

* fix(ci): test with dist config

* fix(ci): generate dist

* fix

* docs: remove codecov

* chore: delete codecov file
2026-06-24 12:51:44 +02:00
dependabot[bot] 7994e44412
chore(deps): bump the cargo-prod group with 4 updates (#1093)
Bumps the cargo-prod group with 4 updates: [log](https://github.com/rust-lang/log), [ratatui](https://github.com/ratatui/ratatui), [ratatui-image](https://github.com/ratatui/ratatui-image) and [ron](https://github.com/ron-rs/ron).


Updates `log` from 0.4.32 to 0.4.33
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.32...0.4.33)

Updates `ratatui` from 0.30.1 to 0.30.2
- [Release notes](https://github.com/ratatui/ratatui/releases)
- [Changelog](https://github.com/ratatui/ratatui/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ratatui/ratatui/compare/ratatui-v0.30.1...ratatui-v0.30.2)

Updates `ratatui-image` from 11.0.4 to 11.0.5
- [Release notes](https://github.com/ratatui/ratatui-image/releases)
- [Changelog](https://github.com/ratatui/ratatui-image/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ratatui/ratatui-image/compare/v11.0.4...v11.0.5)

Updates `ron` from 0.12.1 to 0.12.2
- [Release notes](https://github.com/ron-rs/ron/releases)
- [Changelog](https://github.com/ron-rs/ron/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ron-rs/ron/compare/v0.12.1...v0.12.2)

---
updated-dependencies:
- dependency-name: log
  dependency-version: 0.4.33
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-prod
- dependency-name: ratatui
  dependency-version: 0.30.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-prod
- dependency-name: ratatui-image
  dependency-version: 11.0.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-prod
- dependency-name: ron
  dependency-version: 0.12.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-prod
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-23 12:57:53 +00:00
Loric ANDRE fbd426f569 release: v4.8.0 2026-06-17 14:31:28 +02:00
Loric ANDRE 00165fbc98 docs: update ARCHITECTURE.md 2026-06-17 14:07:13 +02:00
Loric ANDRE 0de1a2acbb chore: format imports 2026-06-17 13:58:44 +02:00
Loric ANDRE 42fa8e143b chore: add image tests & examples 2026-06-17 13:56:37 +02:00
LoricAndre a5ca0c8885
feat: add image preview support & preview spinner while loading (#1081)
* chore: generate manpage

* feat: image preview

* feat: add spinner while preview loads

* chore: misc

* chore: generate files

* chore: review

* chore: generate lockfile

* feat: allow forcing halfblock backend for weird envs

* chore: generate files

* fix: avoid race conditions in preview
2026-06-17 08:21:44 +00:00
dependabot[bot] 1a223219fb
chore(deps): bump the cargo-prod group with 7 updates (#1092)
* chore(deps): bump the cargo-prod group with 7 updates

Bumps the cargo-prod group with 7 updates:

| Package | From | To |
| --- | --- | --- |
| [gungraun](https://github.com/gungraun/gungraun) | `0.19.1` | `0.19.2` |
| [log](https://github.com/rust-lang/log) | `0.4.31` | `0.4.32` |
| [memchr](https://github.com/BurntSushi/memchr) | `2.8.1` | `2.8.2` |
| [ratatui](https://github.com/ratatui/ratatui) | `0.30.0` | `0.30.1` |
| [regex](https://github.com/rust-lang/regex) | `1.12.3` | `1.12.4` |
| [which](https://github.com/harryfei/which-rs) | `8.0.2` | `8.0.3` |
| [insta](https://github.com/mitsuhiko/insta) | `1.47.2` | `1.48.0` |


Updates `gungraun` from 0.19.1 to 0.19.2
- [Release notes](https://github.com/gungraun/gungraun/releases)
- [Changelog](https://github.com/gungraun/gungraun/blob/main/CHANGELOG.md)
- [Commits](https://github.com/gungraun/gungraun/compare/v0.19.1...v0.19.2)

Updates `log` from 0.4.31 to 0.4.32
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.31...0.4.32)

Updates `memchr` from 2.8.1 to 2.8.2
- [Commits](https://github.com/BurntSushi/memchr/compare/2.8.1...2.8.2)

Updates `ratatui` from 0.30.0 to 0.30.1
- [Release notes](https://github.com/ratatui/ratatui/releases)
- [Changelog](https://github.com/ratatui/ratatui/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ratatui/ratatui/compare/ratatui-v0.30.0...ratatui-v0.30.1)

Updates `regex` from 1.12.3 to 1.12.4
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.12.3...1.12.4)

Updates `which` from 8.0.2 to 8.0.3
- [Release notes](https://github.com/harryfei/which-rs/releases)
- [Changelog](https://github.com/harryfei/which-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/harryfei/which-rs/compare/8.0.2...8.0.3)

Updates `insta` from 1.47.2 to 1.48.0
- [Release notes](https://github.com/mitsuhiko/insta/releases)
- [Changelog](https://github.com/mitsuhiko/insta/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mitsuhiko/insta/compare/1.47.2...1.48.0)

---
updated-dependencies:
- dependency-name: gungraun
  dependency-version: 0.19.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-prod
- dependency-name: log
  dependency-version: 0.4.32
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-prod
- dependency-name: memchr
  dependency-version: 2.8.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-prod
- dependency-name: ratatui
  dependency-version: 0.30.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-prod
- dependency-name: regex
  dependency-version: 1.12.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-prod
- dependency-name: which
  dependency-version: 8.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-prod
- dependency-name: insta
  dependency-version: 1.48.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: cargo-prod
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix: fix failing tests after ratatui update

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Loric ANDRE <loric.andre@pm.me>
2026-06-16 22:40:19 +02:00
LoricAndre b5b11a472f
fix: tmux sigpipe (#1090)
* fix: SIGPIPE when piped command in popup

* chore: export SKIM_DEFAULT_COMMAND when the cli feature is disabled

* fix: exit silently on write error
2026-06-12 10:06:43 +00:00
LoricAndre 6ddf0314e9
perf: optimize --tac pipeline (#1089) 2026-06-12 09:41:04 +00:00
LoricAndre 3f9e5e5687
fix: multiline popup cmd misread (#1088)
* fix: avoid passing cmd through tmux so we don't capture it when multiline

closes #1086

* chore: get default command in SkimOptions::from_env
2026-06-12 09:23:28 +00:00
LoricAndre 91e9cbe512
chore: add msrv checks (#1083)
* chore: add msrv checks

* fix
2026-06-09 13:55:51 +02:00
dependabot[bot] 9ec676ae88
chore(deps): bump the cargo-prod group with 4 updates (#1080)
Bumps the cargo-prod group with 4 updates: [gungraun](https://github.com/gungraun/gungraun), [log](https://github.com/rust-lang/log), [memchr](https://github.com/BurntSushi/memchr) and [serial_test](https://github.com/palfrey/serial_test).


Updates `gungraun` from 0.19.0 to 0.19.1
- [Release notes](https://github.com/gungraun/gungraun/releases)
- [Changelog](https://github.com/gungraun/gungraun/blob/main/CHANGELOG.md)
- [Commits](https://github.com/gungraun/gungraun/compare/v0.19.0...v0.19.1)

Updates `log` from 0.4.30 to 0.4.31
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.30...0.4.31)

Updates `memchr` from 2.8.0 to 2.8.1
- [Commits](https://github.com/BurntSushi/memchr/compare/2.8.0...2.8.1)

Updates `serial_test` from 3.4.0 to 3.5.0
- [Release notes](https://github.com/palfrey/serial_test/releases)
- [Commits](https://github.com/palfrey/serial_test/compare/v3.4.0...v3.5.0)

---
updated-dependencies:
- dependency-name: gungraun
  dependency-version: 0.19.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-prod
- dependency-name: log
  dependency-version: 0.4.31
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-prod
- dependency-name: memchr
  dependency-version: 2.8.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-prod
- dependency-name: serial_test
  dependency-version: 3.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: cargo-prod
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-03 21:42:25 +02:00
dependabot[bot] fef3cfd909
chore(deps): bump the cargo-prod group with 2 updates (#1079)
Bumps the cargo-prod group with 2 updates: [log](https://github.com/rust-lang/log) and [serde_json](https://github.com/serde-rs/json).


Updates `log` from 0.4.29 to 0.4.30
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.29...0.4.30)

Updates `serde_json` from 1.0.149 to 1.0.150
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.149...v1.0.150)

---
updated-dependencies:
- dependency-name: log
  dependency-version: 0.4.30
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-prod
- dependency-name: serde_json
  dependency-version: 1.0.150
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-prod
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-27 21:01:06 +02:00
Loric ANDRE 7e6115070b release: v4.7.0 2026-05-21 23:13:25 +02:00
LoricAndre 5ac979d42d
feat: add --disable-pattern option to selectively disable items (#1077)
* feat: add `--disable-pattern` option to selectively disable items

* chore: generate files

* fix: do not pre-select disabled items

* tests
2026-05-21 20:56:44 +00:00
Loric ANDRE c7860a1a16 chore: crate-level lints 2026-05-21 18:19:40 +02:00
Loric ANDRE a885c0b4fe fix(ari): tweak previous char bonuses 2026-05-21 16:25:57 +02:00
Loric ANDRE cb8becdc77 release: v4.6.3 2026-05-20 15:41:48 +02:00
LoricAndre 0ce5764b11
fix: item matching mismatching (closes #1071) (#1073) 2026-05-20 13:26:10 +00:00
LoricAndre d3592461fd
fix: re-enable --no-mouse (#1074)
closes #1072
2026-05-20 13:01:02 +00:00
dependabot[bot] fd7e8ac101
chore(deps): bump the cargo-prod group with 2 updates (#1070)
Bumps the cargo-prod group with 2 updates: [gungraun](https://github.com/gungraun/gungraun) and [shlex](https://github.com/comex/rust-shlex).


Updates `gungraun` from 0.18.2 to 0.19.0
- [Release notes](https://github.com/gungraun/gungraun/releases)
- [Changelog](https://github.com/gungraun/gungraun/blob/main/CHANGELOG.md)
- [Commits](https://github.com/gungraun/gungraun/compare/v0.18.2...v0.19.0)

Updates `shlex` from 1.3.0 to 2.0.1
- [Changelog](https://github.com/comex/rust-shlex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/comex/rust-shlex/commits)

---
updated-dependencies:
- dependency-name: gungraun
  dependency-version: 0.19.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: cargo-prod
- dependency-name: shlex
  dependency-version: 2.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: cargo-prod
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-19 20:22:14 +02:00
dependabot[bot] 32c1d105c9
chore(deps): bump the cargo-prod group with 3 updates (#1069)
Bumps the cargo-prod group with 3 updates: [clap_complete](https://github.com/clap-rs/clap), [nix](https://github.com/nix-rust/nix) and [tokio](https://github.com/tokio-rs/tokio).


Updates `clap_complete` from 4.6.3 to 4.6.5
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.6.3...clap_complete-v4.6.5)

Updates `nix` from 0.31.2 to 0.31.3
- [Changelog](https://github.com/nix-rust/nix/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nix-rust/nix/compare/v0.31.2...v0.31.3)

Updates `tokio` from 1.52.2 to 1.52.3
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.52.2...tokio-1.52.3)

---
updated-dependencies:
- dependency-name: clap_complete
  dependency-version: 4.6.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-prod
- dependency-name: nix
  dependency-version: 0.31.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-prod
- dependency-name: tokio
  dependency-version: 1.52.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-prod
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-12 18:49:11 +02:00
dependabot[bot] 168ac72776
chore(deps): bump tokio from 1.52.1 to 1.52.2 in the cargo-prod group (#1067)
Bumps the cargo-prod group with 1 update: [tokio](https://github.com/tokio-rs/tokio).


Updates `tokio` from 1.52.1 to 1.52.2
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.52.1...tokio-1.52.2)

---
updated-dependencies:
- dependency-name: tokio
  dependency-version: 1.52.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-prod
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2026-05-07 12:37:41 +00:00
LoricAndre e361a8dec0
chore(ci): readd PR check (#1068)
* chore(ci): readd PR check

* fix(ci): use pull_request event

* fix(ci): bash syntax

* fix(ci): pass git cliff context as jq-generated file

* fix(ci): simpler check for changes

* fix(ci): output message if no diff too

* fix(ci): rerun on edit

* fix(ci): exit codes

* chore: fix typos (ci should fail)

* chore: generate files
2026-05-07 12:20:49 +00:00
Loric ANDRE ec77fa7245 release: v4.6.2 2026-05-02 19:55:45 +02:00
Loric ANDRE c3bac9f697 fix(windows): use 1-based cursor position (closes #1065) 2026-05-02 19:39:42 +02:00
dependabot[bot] 19d2640fe1
chore(deps): bump clap_complete in the cargo-prod group (#1066)
Bumps the cargo-prod group with 1 update: [clap_complete](https://github.com/clap-rs/clap).


Updates `clap_complete` from 4.6.2 to 4.6.3
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.6.2...clap_complete-v4.6.3)

---
updated-dependencies:
- dependency-name: clap_complete
  dependency-version: 4.6.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: cargo-prod
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-02 19:18:59 +02:00
Loric ANDRE 8d92d05b8e release: v4.6.1 2026-04-26 19:59:49 +02:00
LoricAndre 0457fd2e62
fix: do not truncate display when longer than text (#1064)
* chore: group dependabot PRs

* fix: do not truncate display when longer than text (closes #1063)

* chore: comment review

* chore: review

* chore: stricter test
2026-04-26 15:18:10 +00:00
dependabot[bot] 6fb2cc5feb
chore(deps): bump tokio from 1.51.1 to 1.52.1 (#1059)
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.51.1 to 1.52.1.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.51.1...tokio-1.52.1)

---
updated-dependencies:
- dependency-name: tokio
  dependency-version: 1.52.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-21 10:45:48 +00:00
dependabot[bot] 87a2536193
chore(deps): bump clap from 4.6.0 to 4.6.1 (#1060)
Bumps [clap](https://github.com/clap-rs/clap) from 4.6.0 to 4.6.1.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.6.0...clap_complete-v4.6.1)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.6.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-21 10:28:52 +00:00
dependabot[bot] 44e8aef062
chore(deps): bump interprocess from 2.4.0 to 2.4.2 (#1058)
Bumps [interprocess](https://github.com/kotauskas/interprocess) from 2.4.0 to 2.4.2.
- [Release notes](https://github.com/kotauskas/interprocess/releases)
- [Commits](https://github.com/kotauskas/interprocess/compare/2.4.0...2.4.2)

---
updated-dependencies:
- dependency-name: interprocess
  dependency-version: 2.4.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-21 10:00:28 +00:00
LoricAndre b9b64e6591
chore: frizbee as a feature and remove quotes from output-format (#1062)
* fix: remove quotes from output-format

* chore: add frizbee feature
2026-04-21 09:45:54 +00:00
phanium cf49f60d96
fix(windows): raw_arg to avoid escape issue (#1061)
* fix(windows): raw_arg to avoid escape issue

* chore: tweaks

---------

Co-authored-by: Loric ANDRE <loric.andre@pm.me>
2026-04-21 11:26:35 +02:00
Loric ANDRE bf63404ad5 chore: delete dangerous (and not that useful) PR action in favor of a just recipe 2026-04-15 14:12:21 +02:00
Loric ANDRE ca986f444c release: v4.6.0 2026-04-14 13:52:54 +02:00
Loric ANDRE 98f9612e97 fix: remove breaking change introduced with 7375d30b 2026-04-14 13:39:26 +02:00
dependabot[bot] d6f2800b64
chore(deps): bump tui-term from 0.3.3 to 0.3.4 (#1038)
Bumps [tui-term](https://github.com/a-kenji/tui-term) from 0.3.3 to 0.3.4.
- [Release notes](https://github.com/a-kenji/tui-term/releases)
- [Changelog](https://github.com/a-kenji/tui-term/blob/development/CHANGELOG.md)
- [Commits](https://github.com/a-kenji/tui-term/compare/v0.3.3...v0.3.4)

---
updated-dependencies:
- dependency-name: tui-term
  dependency-version: 0.3.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2026-04-14 13:39:11 +02:00
dependabot[bot] de60d945b0
chore(deps): bump gungraun from 0.17.2 to 0.18.1 (#1052)
Bumps [gungraun](https://github.com/gungraun/gungraun) from 0.17.2 to 0.18.1.
- [Release notes](https://github.com/gungraun/gungraun/releases)
- [Changelog](https://github.com/gungraun/gungraun/blob/main/CHANGELOG.md)
- [Commits](https://github.com/gungraun/gungraun/compare/v0.17.2...v0.18.1)

---
updated-dependencies:
- dependency-name: gungraun
  dependency-version: 0.18.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2026-04-14 12:51:01 +02:00
Loric ANDRE 475dd515e5 fix: ignore missing files for ssh completion (closes #1054) 2026-04-14 11:52:23 +02:00
Loric ANDRE aa5cf82ef6 chore: unpin wide-used dependencies (closes #1050) 2026-04-14 11:45:58 +02:00
leaty 2cf64ebd46
fix: theme not being set with multiple values (#1049)
* fix: theme not being set with multiple values

* test(theme): add better theme override tests

---------

Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2026-04-11 14:41:38 +02:00
LoricAndre 7375d30bf1
feat: add more info variants (closes #1042) (#1048)
* feat: add more info variants (closes #1042)

* chore: generate completions & manpage

* docs: update info docs

---------

Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2026-04-11 14:12:22 +02:00
LoricAndre 4997e2d253
feat: add border none (closes #1041) (#1044)
* feat: add border none (closes #1041)

* chore: generate completions & manpage

* fix: add BorderType::ForceOff to handle popup and no-border

* chore: generate completions & manpage

---------

Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2026-04-10 17:44:45 +02:00
LoricAndre e531182a2b
feat: allow negative sizes (closes #1040) (#1043)
* feat: allow negative sizes (closes #1040)

* chore: generate completions & manpage

---------

Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2026-04-10 16:17:07 +02:00
dependabot[bot] c28d162890
chore(deps): bump gnuplot from 0.0.45 to 0.0.46 (#1039)
Bumps [gnuplot](https://github.com/SiegeLord/RustGnuplot) from 0.0.45 to 0.0.46.
- [Commits](https://github.com/SiegeLord/RustGnuplot/commits)

---
updated-dependencies:
- dependency-name: gnuplot
  dependency-version: 0.0.46
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-07 18:46:55 +00:00
Loric ANDRE ada7cc6264 release: v4.5.1 2026-04-07 20:33:07 +02:00
dependabot[bot] 80c8e1a62a
chore(ci/deps): bump actions/checkout from 4 to 6 (#1033)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-07 20:20:18 +02:00
dependabot[bot] 4ee04e61dd
chore(ci/deps): bump actions/create-github-app-token from 1 to 3 (#1034)
Bumps [actions/create-github-app-token](https://github.com/actions/create-github-app-token) from 1 to 3.
- [Release notes](https://github.com/actions/create-github-app-token/releases)
- [Commits](https://github.com/actions/create-github-app-token/compare/v1...v3)

---
updated-dependencies:
- dependency-name: actions/create-github-app-token
  dependency-version: '3'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-07 20:20:09 +02:00
dependabot[bot] bf8642a18e
chore(ci/deps): bump codecov/codecov-action from 5 to 6 (#1035)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5 to 6.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v5...v6)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-07 20:19:47 +02:00
Loric ANDRE a95ad14142 fix: show popup border unless --no-border (closes #1032) 2026-04-07 20:12:21 +02:00
Loric ANDRE 797ca70da5 docs: add bench graphs [skip ci] 2026-04-07 20:02:23 +02:00
Loric ANDRE 1eb51eecca release: v4.5.0 2026-04-07 11:15:39 +02:00
dependabot[bot] 931cd9afc9
chore(deps): bump tokio from 1.50.0 to 1.51.0 (#1029)
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.50.0 to 1.51.0.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.50.0...tokio-1.51.0)

---
updated-dependencies:
- dependency-name: tokio
  dependency-version: 1.51.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2026-04-07 11:01:21 +02:00
dependabot[bot] 06bfbc3e13
chore(deps): bump frizbee from 0.8.3 to 0.9.0 (#1030)
Bumps [frizbee](https://github.com/saghen/frizbee) from 0.8.3 to 0.9.0.
- [Commits](https://github.com/saghen/frizbee/compare/v0.8.3...v0.9.0)

---
updated-dependencies:
- dependency-name: frizbee
  dependency-version: 0.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2026-04-07 11:00:57 +02:00
dependabot[bot] 036fcc9455
chore(deps): bump indexmap from 2.13.0 to 2.13.1 (#1031)
Bumps [indexmap](https://github.com/indexmap-rs/indexmap) from 2.13.0 to 2.13.1.
- [Changelog](https://github.com/indexmap-rs/indexmap/blob/main/RELEASES.md)
- [Commits](https://github.com/indexmap-rs/indexmap/compare/2.13.0...2.13.1)

---
updated-dependencies:
- dependency-name: indexmap
  dependency-version: 2.13.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-07 08:59:39 +00:00
Loric ANDRE e227cafda2 feat: better performance on small datasets 2026-04-07 10:34:12 +02:00
Xeonacid 40f0e29448
chore: deps: Only use frizbee on x86_64 and aarch64 (#1028)
* chore: deps: Only use frizbee on x86_64 and aarch64

* chore: update docs for frizbee support

* chore: docs [skip ci]

---------

Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2026-04-04 14:32:57 +02:00
LoricAndre 6b355e144a
feat: rename tmux -> popup and add zellij (#1027)
* feat: rename tmux -> popup and add zellij

* chore: generate completions & manpage

* Apply suggestions from code review

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* chore: fixes

* chore: generate completions & manpage

* chore: misc, windows todo

* chore: disable popup on windows for now

* chore: generate completions & manpage

* fix: always quote using sh

* chore: expect

* fix: avoid nested popup invocations

* fix: tests

* fix: correctly gate popup

* chore(docs): update ARCHITECTURE.md [skip ci]

---------

Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-04-03 13:46:12 +02:00
Loric ANDRE a18e88aafd release: v4.4.0 2026-04-02 19:55:42 +02:00
LoricAndre 91e090e813
chore: better CI caching (#1026)
* chore: better CI caching

* chore: generate completions & manpage

* chore: add platform based key

* cache by runner os

* chore: trigger ci

* feat(bench): measure startup time

---------

Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2026-04-02 19:29:46 +02:00
Loric ANDRE fb17c90704 feat: add scrollbar 2026-04-01 13:44:52 +02:00
Loric ANDRE 39b120fbd8 release: v4.3.0 2026-03-31 19:29:36 +02:00
dependabot[bot] 230c2c7bd7
chore(deps): bump clap_mangen from 0.2.33 to 0.3.0 (#1025)
Bumps [clap_mangen](https://github.com/clap-rs/clap) from 0.2.33 to 0.3.0.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_mangen-v0.2.33...clap_mangen-v0.3.0)

---
updated-dependencies:
- dependency-name: clap_mangen
  dependency-version: 0.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-31 17:17:07 +00:00
dependabot[bot] e6a997e3af
chore(deps): bump ron from 0.12.0 to 0.12.1 (#1022)
Bumps [ron](https://github.com/ron-rs/ron) from 0.12.0 to 0.12.1.
- [Release notes](https://github.com/ron-rs/ron/releases)
- [Changelog](https://github.com/ron-rs/ron/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ron-rs/ron/compare/v0.12.0...v0.12.1)

---
updated-dependencies:
- dependency-name: ron
  dependency-version: 0.12.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-31 17:04:06 +00:00
dependabot[bot] 71f7ead51c
chore(deps): bump roff from 1.1.0 to 1.1.1 (#1021)
Bumps [roff](https://github.com/rust-cli/roff-rs) from 1.1.0 to 1.1.1.
- [Changelog](https://github.com/rust-cli/roff-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-cli/roff-rs/compare/v1.1.0...v1.1.1)

---
updated-dependencies:
- dependency-name: roff
  dependency-version: 1.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-31 16:50:29 +00:00
dependabot[bot] 040fe9ed2d
chore(deps): bump tui-term from 0.3.2 to 0.3.3 (#1023)
Bumps [tui-term](https://github.com/a-kenji/tui-term) from 0.3.2 to 0.3.3.
- [Release notes](https://github.com/a-kenji/tui-term/releases)
- [Changelog](https://github.com/a-kenji/tui-term/blob/development/CHANGELOG.md)
- [Commits](https://github.com/a-kenji/tui-term/compare/v0.3.2...v0.3.3)

---
updated-dependencies:
- dependency-name: tui-term
  dependency-version: 0.3.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-31 16:38:08 +00:00
dependabot[bot] 7ee36fdac3
chore(deps): bump insta from 1.47.1 to 1.47.2 (#1024)
Bumps [insta](https://github.com/mitsuhiko/insta) from 1.47.1 to 1.47.2.
- [Release notes](https://github.com/mitsuhiko/insta/releases)
- [Changelog](https://github.com/mitsuhiko/insta/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mitsuhiko/insta/compare/1.47.1...1.47.2)

---
updated-dependencies:
- dependency-name: insta
  dependency-version: 1.47.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-31 18:25:20 +02:00
LoricAndre 1a10e405f1
feat: improve matcher & reader performance (#1020)
* chore: migrate bench.py to rust to remove python deps

* feat: replace rayon with a custom thread pool manager

* wip: insert into item_list processed_items directly from matcher

* wip: perf optimizations

* wip: perf optimizations

* wip: reader perf optimizations

* fix: skip --bench injected in bench args

* chore: add ARCHITECTURE.md

* Update src/helper/item_reader.rs

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update src/matcher.rs

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* feat: use the same pool between reader and matcher

* chore: misc

* fix: tests

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-03-31 18:05:20 +02:00
Loric ANDRE 94c132a94f release: v4.2.0 2026-03-30 13:58:19 +02:00
LoricAndre 987d2a5ca7
feat: add multiline item rendering (#999)
* feat: add multiline item rendering

* chore: generate completions & manpage

* chore: use newline as default for multiline when read0 is set

* chore: generate completions & manpage

* chore: add `highlight-line'

* chore: generate completions & manpage

* feat: use multiline in history widgets

* feat: better snap tests

* feat(ci): show snap failures

* fix(ci): cross platform tests

---------

Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2026-03-30 13:57:09 +02:00
Loric ANDRE 7e5d441e4c release: v4.1.0 2026-03-30 10:09:55 +02:00
LoricAndre 9d12e9d420
feat: windows support (#1010)
* wip: windows support

* feat: windows support

* feat: add windows target to CI

* chore: generate completions & manpage

* Update src/util.rs

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* chore: cleanup & doc

* chore: generate completions & manpage

* chore: generate dist

* fix: reduplicate default test

* chore: regate tmux

* chore: remove useless test-utils feature

* fix(windows): ignore dirs in default_command

* docs: update shell docs for windows

* chore: generate completions & manpage

* chore(justfile): do not ignore failed tests

* fix: upload correct junit after profile change

* fix: always execute exit commands

* fix: windows-specific ctrl-c handling

* chore: misc docs & other updates

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* chore: generate completions & manpage

* chore: include license in MSI installer

---------

Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Your Name <you@example.com>
2026-03-29 21:46:42 +00:00
Loric ANDRE 65c2dcde11 fix: trim whitespace in zsh hist widget (closes #1019) 2026-03-29 23:14:44 +02:00
Loric ANDRE 6332195605 fix: broken pipe in bash alt-c (closes #1018) 2026-03-29 17:57:56 +02:00
Loric ANDRE eb5d5df44b chore: minor perf 2026-03-29 17:41:47 +02:00
Loric ANDRE 78231e5244 chore: refactor engine building 2026-03-28 12:19:12 +01:00
Loric ANDRE 90e23eb86e release: v4.0.1 2026-03-28 10:10:50 +01:00
dependabot[bot] 3757038aa4
chore(deps): bump clap_complete_nushell from 4.5.10 to 4.6.0 (#1013)
Bumps [clap_complete_nushell](https://github.com/clap-rs/clap) from 4.5.10 to 4.6.0.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete_nushell-v4.5.10...clap_complete_nushell-v4.6.0)

---
updated-dependencies:
- dependency-name: clap_complete_nushell
  dependency-version: 4.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2026-03-28 09:51:57 +01:00
dependabot[bot] cb037fbe27
chore(deps): bump roff from 1.0.0 to 1.1.0 (#1014)
Bumps [roff](https://github.com/rust-cli/roff-rs) from 1.0.0 to 1.1.0.
- [Changelog](https://github.com/rust-cli/roff-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-cli/roff-rs/compare/v1.0.0...v1.1.0)

---
updated-dependencies:
- dependency-name: roff
  dependency-version: 1.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2026-03-28 09:51:42 +01:00
dependabot[bot] 2a77f6b4c1
chore(deps): bump env_logger from 0.11.9 to 0.11.10 (#1012)
Bumps [env_logger](https://github.com/rust-cli/env_logger) from 0.11.9 to 0.11.10.
- [Release notes](https://github.com/rust-cli/env_logger/releases)
- [Changelog](https://github.com/rust-cli/env_logger/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-cli/env_logger/compare/v0.11.9...v0.11.10)

---
updated-dependencies:
- dependency-name: env_logger
  dependency-version: 0.11.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2026-03-28 09:51:32 +01:00
Loric ANDRE 7654782d64 chore: remove sonar 2026-03-28 09:41:58 +01:00
Loric ANDRE a8c56626bc chore: set rust msrv (closes #1017) 2026-03-28 09:38:28 +01:00
Douglas Griffith b9c3de302b
replace undefined awk behaviour (#1016)
I encountered a bug with the CTRL-R history behaviour in bash, and tracked it down to these awk commands. I'm not sure what changed, but according to the documentation `\0` is undefined behaviour:
https://pubs.opengroup.org/onlinepubs/9799919799/utilities/awk.html#tagtcjh_15

I've replaced the `\0` escape sequences with the `%c` conversion which converts the int argument to a single byte character. Ref:
https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap05.html
2026-03-26 23:20:43 +01:00
Loric ANDRE c0a9be6b5f fix(keybindings): make sure we use the correct filtering fields 2026-03-23 23:19:10 +01:00
Loric André d40af611b6 chore(ci): add sonar directly to test workflow 2026-03-19 21:29:15 +01:00
dependabot[bot] 1fd6292bbe
chore(deps): bump clap_complete from 4.5.66 to 4.6.0 (#1005)
Bumps [clap_complete](https://github.com/clap-rs/clap) from 4.5.66 to 4.6.0.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.66...clap_complete-v4.6.0)

---
updated-dependencies:
- dependency-name: clap_complete
  dependency-version: 4.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-19 15:55:17 +00:00
dependabot[bot] b74ed55946
chore(deps): bump clap from 4.5.60 to 4.6.0 (#1006)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.60 to 4.6.0.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.60...clap_complete-v4.6.0)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-19 15:44:48 +00:00
dependabot[bot] 257f0185b0
chore(deps): bump tempfile from 3.26.0 to 3.27.0 (#1004)
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.26.0 to 3.27.0.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.26.0...v3.27.0)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-version: 3.27.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-19 15:36:05 +00:00
dependabot[bot] 9626606745
chore(deps): bump clap_mangen from 0.2.31 to 0.2.33 (#1007)
* chore(deps): bump clap_mangen from 0.2.31 to 0.2.33

Bumps [clap_mangen](https://github.com/clap-rs/clap) from 0.2.31 to 0.2.33.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_mangen-v0.2.31...clap_mangen-v0.2.33)

---
updated-dependencies:
- dependency-name: clap_mangen
  dependency-version: 0.2.33
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore: generate completions & manpage

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2026-03-19 16:27:04 +01:00
dependabot[bot] 96f3320b15
chore(deps): bump frizbee from 0.8.2 to 0.8.3 (#1008)
Bumps [frizbee](https://github.com/saghen/frizbee) from 0.8.2 to 0.8.3.
- [Commits](https://github.com/saghen/frizbee/compare/v0.8.2...v0.8.3)

---
updated-dependencies:
- dependency-name: frizbee
  dependency-version: 0.8.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-19 16:26:36 +01:00
LoricAndre 7ae0802979
chore: add sonar analysis (#1009)
* chore: add sonar analysis

* chore: add sonar config
2026-03-19 16:25:38 +01:00
d 5028ef4d0b
chore: correct typos in code, comments and documentation (#1002)
* fix: correct typos in code, comments and documentation

* chore: generate completions & manpage

---------

Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2026-03-12 10:19:25 +00:00
LoricAndre 926c76ee16
docs: add benchmarks to README.md [skip ci] 2026-03-11 15:31:08 +01:00
Loric ANDRE a1f420a682 chore: clippy pedantic 2026-03-10 20:33:19 +01:00
Loric ANDRE 85c6964da5 release: v4.0.0 2026-03-10 16:25:28 +01:00
dependabot[bot] 4269838f32
chore(deps): bump which from 8.0.0 to 8.0.2 (#1000)
Bumps [which](https://github.com/harryfei/which-rs) from 8.0.0 to 8.0.2.
- [Release notes](https://github.com/harryfei/which-rs/releases)
- [Changelog](https://github.com/harryfei/which-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/harryfei/which-rs/compare/8.0.0...8.0.2)

---
updated-dependencies:
- dependency-name: which
  dependency-version: 8.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-10 16:02:40 +01:00
Loric ANDRE 95f09a455a chore: use python for bench script for comparison 2026-03-10 16:01:54 +01:00
Loric ANDRE 5b0323a9fb feat!: use Arinae as default algorithm
=== Results: ./target/release/sk [baseline] ===
Completed runs: 50 / 50
Average items matched: 2895782 / 10000000  (min: 2895782, max: 2895782)
Average time: 3.827s  (min: 3.576s, max: 4.090s)
Average items/second: 2615767  (min: 2445033, max: 2796365)
Average peak memory usage: 1589.2 MB  (min: 1518.6 MB, max: 1661.2 MB)
Average peak CPU usage: 528.9%  (min: 457.0%, max: 740.0%)

=== Results: /home/loric/.local/share/cargo/bin/sk ===
Completed runs: 50 / 50
Average items matched: 2895782 / 10000000  (min: 2895782, max: 2895782)
+0.0%
Average time: 3.930s  (min: 3.565s, max: 4.226s)  +2.7%
Average items/second: 2548674  (min: 2366263, max: 2804816)  -2.6%
Average peak memory usage: 1618.8 MB  (min: 1539.1 MB, max: 1680.6 MB)
+1.9%
Average peak CPU usage: 696.8%  (min: 608.0%, max: 875.0%)  +31.7%

=== Results: /home/loric/.nix-profile/bin/fzf ===
Completed runs: 50 / 50
Average items matched: 2895782 / 10000000  (min: 2895782, max: 2895782)
+0.0%
Average time: 5.421s  (min: 4.814s, max: 6.111s)  +41.7%
Average items/second: 1848269  (min: 1636444, max: 2077385)  -29.3%
Average peak memory usage: 2015.3 MB  (min: 1860.7 MB, max: 2173.9 MB)
+26.8%
Average peak CPU usage: 1301.1%  (min: 1229.0%, max: 1431.0%)  +146.0%

=== Comparison Summary (vs baseline: ./target/release/sk) ===
Binary                                       Avg time     Δ time
Avg rate     Δ rate
------------------------------------------------------------------------------------------
./target/release/sk                            3.827s   baseline
2615767   baseline
/home/loric/.local/share/cargo/bin/sk          3.930s      +2.7%
2548674      -2.6%
/home/loric/.nix-profile/bin/fzf               5.421s     +41.7%
1848269     -29.3%
2026-03-10 15:59:59 +01:00
Loric ANDRE 86d824e6cf chore!: default to disabled typos 2026-03-10 14:14:07 +01:00
LoricAndre ab514a54c9
feat!: internally compute indexes at match time (removes get/set_index) (#1001)
* chore: remove skim::Item run_items wrapper

* fix: properly trigger re-render on custom previews

* feat: add AppendItems event

* feat!: internally compute indexes at match time (removes get/set_index)

* chore: generate completions & manpage

* chore: better benchmarks

---------

Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2026-03-10 14:12:10 +01:00
Loric ANDRE 0ac1ac878d release: v3.7.0 2026-03-08 19:18:55 +01:00
Loric ANDRE 7d2efaf87f chore: review snapshots 2026-03-08 18:57:55 +01:00
Loric ANDRE 9c9e15c97b feat: add Skim::run_items API 2026-03-08 18:46:12 +01:00
Loric ANDRE 396f2953a9 chore: add gungraun benchmark 2026-03-07 15:45:34 +01:00
Loric ANDRE 349533fa1b fix: correctly init rank for and engine 2026-03-05 10:09:34 +01:00
Loric ANDRE e891a49ebe fix: use sum of scores in And engine 2026-03-05 09:30:38 +01:00
Loric ANDRE 0bf6e8fb32 feat: add scheme for better filepath matching in Arinae 2026-03-05 09:30:38 +01:00
Loric ANDRE 17adb040ac release: v3.6.2 2026-03-04 21:07:29 +01:00
Loric ANDRE 3a4b958f51 chore: allow using flag or env vars for both log level and file 2026-03-04 21:06:06 +01:00
LoricAndre ffddee2d54
chore: refactor app layout computations to take them out of the hot loop (#996)
* chore: refactor app layout computations to take them out of the hot loop

* chore: use Default for default theme

* fix: recursion loop on default theme
2026-03-04 17:43:13 +01:00
dependabot[bot] 07b40a3281
chore(deps): bump nix from 0.31.1 to 0.31.2 (#995)
* chore(deps): bump nix from 0.31.1 to 0.31.2

Bumps [nix](https://github.com/nix-rust/nix) from 0.31.1 to 0.31.2.
- [Changelog](https://github.com/nix-rust/nix/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nix-rust/nix/compare/v0.31.1...v0.31.2)

---
updated-dependencies:
- dependency-name: nix
  dependency-version: 0.31.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore: update libc & other deps now that nix permits it

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Loric ANDRE <loric.andre@pm.me>
2026-03-03 15:25:50 +01:00
Loric ANDRE 9ef09b7da6 release: v3.6.1 2026-03-03 11:54:51 +01:00
Loric ANDRE c5a3310ac4 chore: add bench plot scripts 2026-03-03 11:53:22 +01:00
Loric ANDRE b52dc0cfac chore: switch allocator to mimalloc for ~20% perf in some cases 2026-03-02 20:31:38 +01:00
Loric ANDRE 600d78815e release: v3.6.0 2026-03-02 18:04:50 +01:00
LoricAndre fa003bf7c5
feat(shell): colored history widgets & remove perl completely (#994)
* feat(shell): colored history widgets & remove perl completely

* docs: add variables to top doc

* chore: nitpick
2026-03-02 13:56:36 +00:00
LoricAndre c65274441a
feat: add Arinae algorithm (#990)
* feat: initial work on skim v3

wip

* wip: SW

* chore: refactor SkimV3 to make it more maintainable

* chore: remove SIMD batch scores

* fix: fix Skim V3 tests

* feat: small optimizations

* feat: bigger optimizations

* chore: generate completions & manpage

* chore: remove unused wide dependency

* chore: update deps

* chore: generate completions & manpage

* fix: make sure all subsequences pass in non-typos mode

* chore: trade some performance against more precision with typos

* feat: gain the performance back using unchecked accesses

* chore: remove failing tests

* feat: use banding across whole upper triangle

* chore: remove useless DEAD_COL checks

* feat: make sure we match everything `frizbee` does while enforcing first char

* feat: minor optimizations

* feat: more minor optimizations

* chore: tweak parameters to find a good balance between performance and accuracy

* chore: accept snap

* chore: penalize consecutive typos

* chore: revert consecutive typos penalization as it seems useless in practice

* wip: optimizations

* feat: multiple optimizations

* perf(skim_v3): use 2-row rolling buffer for score-only DP path

When compute_indices=false (fuzzy_match), the full (n+1)×mcols matrix
was allocated and populated even though traceback was never performed.
Introduce score_only_dp() which maintains only two rows at a time,
reducing memory from O(n×m) to O(m) and improving cache utilization
for long choice strings.

* perf(skim_v3): add early termination when DP rows are all-zero

Track consecutive rows where no cell has a positive score. After 2
consecutive dead rows, return None immediately: gap penalties can only
decrease existing scores, so no downstream row can produce a positive
result. Applied to both score_only_dp and full_dp.

* perf(skim_v3): add range_dp for fuzzy_match_range, avoiding full index vec

fuzzy_match_range previously called fuzzy_indices (full traceback collecting
every matched index) just to extract the first and last. Introduce range_dp
which performs the same full-matrix DP but during traceback only records the
begin and end positions, avoiding the Vec allocation and index collection.
Add range_consistent_with_indices test to verify correctness.

* perf(skim_v3): remove redundant is_subsequence scan in exact mode

In non-typo mode, is_subsequence was called before compute_banding, but
compute_banding -> compute_first_match_cols already validates the same
subsequence property (returning None if any pattern char is absent).
Remove the redundant O(m) scan and delete the now-unused is_subsequence
function. Typo mode retains cheap_typo_prefilter as its guard.

* perf(skim_v3): avoid clone in traceback by using mem::take on thread-local buffer

Previously full_dp returned indices via indices_ref.to_vec() which copies
all n index values into a new allocation. Replace with std::mem::take which
moves ownership of the populated Vec out of the thread-local without copying,
trading the reuse-across-calls benefit for zero-copy return per call.

* perf(skim_v3): tighten typo-mode upper band bound in typo_vband_row

Previously the upper column bound in typo mode was always m (the full
choice length), even for early rows where the diagonal sits far from the
right edge. Compute hi = (j + bandwidth).min(m) symmetrically with the
existing lower bound, skipping cells that cannot contribute to a valid
alignment and reducing work for short patterns on long strings.

* perf(skim_v3): use memchr SIMD for first-char search in prefilter and banding

Add memchr as a direct dependency and implement Atom::find_first_in with
a u8-specialization that calls memchr() for case-sensitive search and a
two-call min-of-two approach for case-insensitive. Use this in:
- cheap_typo_prefilter: first-character existence check
- find_first_char: typo-mode banding anchor computation
This replaces scalar byte-by-byte loops with SIMD-vectorized searches for
ASCII inputs, the common case.

* revert(skim_v3): restore m upper bound in typo_vband_row

The tightened hi = (j + bandwidth).min(m) bound incorrectly rejected valid
typo-mode alignments where the optimal path takes many LEFT (gap) steps
past the bandwidth boundary. The snapshot test confirms 5 fewer matches vs
the expected 37. Revert to hi = m; the affine gap penalty alone prevents
poor alignments from winning.

* perf(skim_v3): add ASCII fast path to char::eq_ignore_case

Replace the to_lowercase() iterator comparison with eq_ignore_ascii_case()
for the common case where both chars are ASCII. This avoids creating two
ToLowercase iterators per comparison in the non-ASCII DP path, using a
single bitwise comparison instead.

* perf(skim_v3): replace RefCell with UnsafeCell (TLCell) in thread-locals

ThreadLocal<RefCell<T>> incurs a runtime borrow-check on every access.
Since ThreadLocal already guarantees per-thread isolation and we never
re-enter the same thread-local within a single call stack, the RefCell
check is redundant.

Replace with TLCell<T>, a Send newtype over UnsafeCell<T>, and a tl_get_mut
helper that returns &mut T directly. Document the safety invariant at each
call site. Also remove the now-unused SWMatrix::zero constructor.

* fix(skim_v3): fix precompute_bonuses reserve logic

The previous reserve(cho.len().saturating_sub(buf.len())) computed the
needed additional capacity relative to the current length, which could
be wrong if buf.len() was stale (e.g. after a set_len call on a longer
buffer). Replace with clear() + reserve(cho.len()) for a correct and
clear-intent O(1) reset followed by a single exact reservation.

* guard: return None for pat.len() > MAX_PAT_LEN in exact mode

Patterns longer than MAX_PAT_LEN (16) used the stack-allocated
[usize; MAX_PAT_LEN] banding arrays with out-of-bounds indices,
causing undefined behaviour in the exact (non-typo) DP path.

Add an early return of None in compute_first_match_cols and
compute_last_match_cols so callers gracefully skip overlong patterns
rather than reading past the end of a fixed-size array.  Typo mode
is unaffected: its dummy arrays are never indexed by the pattern
length.

* perf: re-encode Dir::None=0 so CELL_ZERO is all-zero bytes

Previously Dir::None=3 made Cell::new(0,Dir::None) encode as
0x00030000, preventing bulk-zeroing with write_bytes(0).

Re-assign discriminants to None=0, Diag=1, Up=2, Left=3 so that
CELL_ZERO is now all-zero.  Update:
- Dir discriminants in the enum
- Cell::is_diag() (checks tag==1 instead of 0)
- compute_cell branchless arithmetic (base is Left=3, subtract 2 for
  Diag wins, 1 for Up wins; None=0 so no OR needed)
- score_only_dp: replace init loop with write_bytes(0)
- full_dp / range_dp: replace row-0 init loop with write_bytes(0)

* perf: 128-bit ASCII bitset for cheap_typo_prefilter tail scan

Add Atom::count_tail_present with a u8 specialisation that builds a
two-u64 presence bitset from the choice in a single O(m) pass, making
each subsequent pattern-char lookup O(1) instead of O(m).

The char (non-ASCII) path delegates to count_tail_present_ordered, the
same ordered linear scan that was previously inlined in the function.
The change is observationally equivalent: the prefilter remains a
lenient superset of the old check (unordered vs. ordered presence),
and the snapshot test count is unchanged.

* perf: early exit in count_tail_present_ordered when match is impossible

Add a hopeless-state check at the top of each iteration: if matched
plus remaining pattern chars cannot reach min_needed, bail out
immediately rather than completing the full scan.

This prunes the non-ASCII (char) ordered-scan fallback inside
cheap_typo_prefilter when the pattern is long and many chars are
missing from the choice.

* cleanup: remove unused constants SEPARATOR_MASK_LO/HI and FIRST_CHAR_BONUS_MULTIPLIER

All three were suppressed with #[allow(dead_code)] and are not
referenced by any live code.  SEPARATOR_TABLE is the active lookup;
the mask constants were documentation remnants.

* refactor: replace unsafe transmute in Cell::dir() and compute_cell with safe match

Both usages converted a u8 (guaranteed 0..=3) to Dir via transmute.
Replace with an exhaustive match on the 2-bit tag value — no unsafe
required, and the compiler generates the same conditional-move
sequence.

* perf: Atom::is_sep() trait method avoids u8→char→u32 in separator check

Add is_sep() to the Atom trait with a u8 specialisation that indexes
SEPARATOR_TABLE directly with self as usize, skipping the into::<char>
conversion required by the generic default.

Remove the now-unnecessary is_separator free function; callers use
prev.is_sep() instead.

* refactor: precompute_bonuses rewritten as safe iterator chain

Replace the unsafe raw-pointer write loop with a safe iterator that
starts with START_OF_STRING_BONUS and maps windows-of-2 to the
separator/camelCase bonus formula.  buf.extend() dispatches through
ExactSizeIterator, so no extra allocation occurs.

The safe form exposes the element-independent structure to the
compiler, enabling auto-vectorisation on release builds.

* refactor: extract match_slices_range; simplify run_range

Add match_slices_range<C: Atom> that mirrors match_slices but calls
range_dp instead of dispatch_dp.  run_range now delegates the ASCII
path to match_slices_range and keeps only the non-ASCII char-buf
setup inline, eliminating the duplicated prefilter + bonus +
range_dp block.

* mem: SWMatrix::resize shrinks when buffer is 4× over-allocated

After a one-off large input, the full-DP matrix buffer could hold
significantly more memory than typical inputs require.  Add a
shrink-or-cap heuristic: if the current capacity exceeds 4× the
needed size, truncate and shrink_to(2×needed) to release excess
memory without thrashing on stable-sized inputs.

* Revert "mem: SWMatrix::resize shrinks when buffer is 4× over-allocated"

This reverts commit 9c8571ebe8.

* Revert "refactor: replace unsafe transmute in Cell::dir() and compute_cell with safe match"

This reverts commit 8805fa14ce.

* Revert "perf: Atom::is_sep() trait method avoids u8→char→u32 in separator check"

This reverts commit 175f26af81.

* Revert "perf: early exit in count_tail_present_ordered when match is impossible"

This reverts commit 29721558f0.

* Revert "perf: 128-bit ASCII bitset for cheap_typo_prefilter tail scan"

This reverts commit d79947fcb5.

* Revert "refactor: extract match_slices_range; simplify run_range"

This reverts commit 0fb7f05513.

* Revert "perf(skim_v3): replace RefCell with UnsafeCell (TLCell) in thread-locals"

This reverts commit 0806683251.

* Revert "perf(skim_v3): add ASCII fast path to char::eq_ignore_case"

This reverts commit 069710ad7c.

* Revert "revert(skim_v3): restore m upper bound in typo_vband_row"

This reverts commit 90ffc46633.

* Revert "perf(skim_v3): tighten typo-mode upper band bound in typo_vband_row"

This reverts commit f38ca3a10d.

* Revert "perf(skim_v3): avoid clone in traceback by using mem::take on thread-local buffer"

This reverts commit ffa9a21167.

* Revert "perf(skim_v3): add early termination when DP rows are all-zero"

This reverts commit 073195be58.

* Revert "perf(skim_v3): use 2-row rolling buffer for score-only DP path"

This reverts commit 3acacaad74.

* fix: reverse only order of frizbee indices

* chore: rename & refactor into multiple files

* chore: optimizations to the main flow

* fix: correct banding in non-typo path

* chore: generate completions & manpage

* docs: add algorithms section to the README [skip ci]

* fix(ari): correctly bound vband low

* chore(ari): specific pre-separator bonuses

* fix(ari): boost consec a bit more to beat start/sep

* chore: generate completions & manpage

* feat: run matcher over chunks

* chore: adjust penalties to keep typos under subsequences

* chore: accept snapshot

* fix: replace greedy ordered prefilter with looser unordered

* chore: finish up rename

* chore: review

---------

Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2026-03-01 18:50:28 +01:00
faukah b47bda8902
flake.nix: drop flake-utils, add formatter (#992) 2026-02-25 18:29:40 +01:00
dependabot[bot] 205fdf610a
chore(deps): bump interprocess from 2.3.1 to 2.4.0 (#991)
Bumps [interprocess](https://github.com/kotauskas/interprocess) from 2.3.1 to 2.4.0.
- [Release notes](https://github.com/kotauskas/interprocess/releases)
- [Commits](https://github.com/kotauskas/interprocess/compare/2.3.1...2.4.0)

---
updated-dependencies:
- dependency-name: interprocess
  dependency-version: 2.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-24 10:01:03 +01:00
Loric ANDRE 5a56391190 feat: merge ranks in AndOr engine matcher 2026-02-23 14:45:09 +01:00
Loric ANDRE d87a6a34bf fix: make sure we drop Skim before returning the output 2026-02-22 16:51:56 +01:00
Loric ANDRE 7e462aa33e release: v3.5.0 2026-02-22 12:01:38 +01:00
Loric ANDRE e313acbffc chore: pin dependencies to the latest exact version 2026-02-22 11:50:19 +01:00
Loric ANDRE 5e8ff26024 fix: respect the and & or priority 2026-02-21 10:54:26 +01:00
Loric ANDRE 422b9dd0d7 fix: more precision on AndOr matches (closes #526) 2026-02-21 09:57:28 +01:00
Loric ANDRE 5d9b4df1e5 chore: add partial bench to measure the time it takes to setup skim 2026-02-20 17:05:05 +01:00
Loric ANDRE 684bbd2159 fix: correctly bind uppercase keys 2026-02-20 16:40:33 +01:00
LoricAndre 5daf7823eb
feat: add fzy matcher and --typos/--no-typos flag (#987)
* feat: first reimplementation of Fzy's algo

* feat: typo resistance using the `--typos` flag

* chore: generate completions & manpage

* feat: enable typo-resistance by default for fzy and frizbee

* chore: generate completions & manpage

* fix: tests & feature

---------

Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2026-02-20 15:12:06 +01:00
Conrad Hoffmann f2388c0985
chore(dep): frizbee 0.8.1 (#985)
Fixes: #984

Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2026-02-20 11:09:14 +00:00
Conrad Hoffmann 2161010be3
chore(dep): remove unused dependency 'beef' (#986) 2026-02-20 10:37:55 +00:00
Sisyphus1813 21e09295d6
Update README (#983) 2026-02-20 09:14:02 +01:00
Loric ANDRE f9e7760fdc release: v3.4.0 2026-02-19 21:03:23 +01:00
LoricAndre c7da16f167
feat: back to stable rust (#980)
* wip: stable rust, but no match indices

* feat: use restored indices api

* chore: use crates.io pushed 0.8.0

* chore: generate completions & manpage

* fix: remove nightly-specific coverage annotations

---------

Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2026-02-19 20:53:05 +01:00
Loric ANDRE 1a90f4f52f bench: add filter 2026-02-19 14:57:44 +01:00
Loric ANDRE 003243a93a feat: add --ellipsis option 2026-02-19 13:37:10 +01:00
Loric ANDRE d7117fbbc2 feat: add --print-current, --output-format (closes #981) 2026-02-18 22:14:49 +01:00
Loric ANDRE 5c11314bb6 feat: add set-header action (closes #768) 2026-02-18 19:33:29 +01:00
Loric ANDRE 943e073cee feat: allow setting delimiters in + expansions (closes #935) 2026-02-18 19:18:10 +01:00
Loric ANDRE 1b2da17e88 fix(ci): do not run benches 2026-02-18 16:40:35 +01:00
Loric ANDRE 091c7e8284 docs: benchmarks 2026-02-18 15:57:41 +01:00
Loric ANDRE 5f1d49d997 chore: add rust benchmark 2026-02-18 15:54:50 +01:00
Loric ANDRE 6b128b183b release: v3.3.0 2026-02-18 10:42:07 +01:00
Loric ANDRE 40c2ee6c41 chore: refactor Skim into its own file 2026-02-17 22:22:38 +01:00
figsoda 2a7bd1db11
feat: allow run_with to be run within a tokio runtime (#979)
* feat: allow run_with to be run within a tokio runtime

* feat: add async example
2026-02-17 21:05:50 +00:00
dependabot[bot] e7183acc70
chore(deps): bump clap from 4.5.58 to 4.5.59 (#977)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.58 to 4.5.59.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.58...clap_complete-v4.5.59)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.59
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-17 03:14:28 +01:00
dependabot[bot] 0d399ea4e7
chore(deps): bump roff from 0.2.2 to 1.0.0 (#976)
Bumps [roff](https://github.com/rust-cli/roff-rs) from 0.2.2 to 1.0.0.
- [Changelog](https://github.com/rust-cli/roff-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-cli/roff-rs/compare/v0.2.2...v1.0.0)

---
updated-dependencies:
- dependency-name: roff
  dependency-version: 1.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-17 03:14:18 +01:00
dependabot[bot] c8de29e239
chore(deps): bump futures from 0.3.31 to 0.3.32 (#975)
Bumps [futures](https://github.com/rust-lang/futures-rs) from 0.3.31 to 0.3.32.
- [Release notes](https://github.com/rust-lang/futures-rs/releases)
- [Changelog](https://github.com/rust-lang/futures-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/futures-rs/compare/0.3.31...0.3.32)

---
updated-dependencies:
- dependency-name: futures
  dependency-version: 0.3.32
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-17 03:14:04 +01:00
Loric ANDRE 480e147377 fix: possible None unwrap if ansi enabled but not in item 2026-02-16 14:27:16 +01:00
Loric ANDRE 65ba8cffaa feat: event-driven re-render (#949) 2026-02-16 11:05:33 +01:00
LoricAndre 4147eccb94
chore: unify filter mode & squeeze more perf (#974)
* refactor: move filter mode into run_with via should_enter

Remove the standalone `filter` function from main.rs and integrate
filter mode into the main `run_with` pipeline. When `options.filter`
is set, `should_enter` now waits for all items to be processed and
returns false (skipping TUI), and `App::results` returns all matched
items. This unifies filter mode with the rest of the codebase so it
benefits from all other flags (sorting, tiebreaks, etc.).

https://claude.ai/code/session_01T7pa2RRX85MvBtnH7PWtmw

* perf: optimize filter mode to match single-pass performance

Three changes that eliminate the performance regression from routing
filter mode through run_with:

1. should_enter(): Wait for reader to finish BEFORE starting matcher,
   then run matcher exactly once. The old polling loop called
   restart_matcher() repeatedly, each time resetting the ItemPool taken
   counter and re-processing all items from scratch. With 1M items
   arriving in batches, items were matched multiple times.

2. matcher.run(): Remove unnecessary .enumerate() (index was discarded)
   and remove item.clone() — into_par_iter() yields owned values so the
   Arc can be moved directly into MatchedItem.

3. App::results(): In filter mode, drain items instead of cloning to
   avoid 271K MatchedItem clones + Arc allocations.

Benchmark (1M file paths, query "test", 10 runs):
  Old standalone filter: 5.306s ± 0.085s
  New unified filter:    4.932s ± 0.099s  (1.08x faster)

https://claude.ai/code/session_01T7pa2RRX85MvBtnH7PWtmw

* perf: make restart_matcher incremental when force=false

When force=false, skip the item pool reset so take() only returns new
(untaken) items. The callback merges new sorted matches into the
existing sorted result list using an O(n+m) merge, instead of
replacing it. This fixes the root cause: previously every
restart_matcher call re-processed ALL items from scratch because
reset() set the taken counter to 0.

This benefits all polling callers (filter, select-1, exit-0, sync),
not just filter mode. Items arriving in batches are now each matched
exactly once, and matching overlaps with I/O since batches are
processed as they arrive.

When force=true (query changed), behavior is unchanged: full reset
and re-match.

Reverts the filter-specific workaround from the previous commit in
favor of this general fix; the original polling loop in should_enter
is now efficient.

Benchmark (1M file paths, query "test", 10 runs):
  Old standalone filter (master):  4.566s ± 0.055s
  Incremental restart_matcher:     3.654s ± 0.035s  (1.25x faster)

https://claude.ai/code/session_01T7pa2RRX85MvBtnH7PWtmw

* refactor: extract sorted_merge into MatchedItem method

Move the two-sorted-list merge from the restart_matcher closure into
MatchedItem::sorted_merge() for clarity and reusability. The method
merges two Vec<MatchedItem> lists that are already sorted by rank
into a single sorted Vec in O(n+m) time.

https://claude.ai/code/session_01T7pa2RRX85MvBtnH7PWtmw

* fix: preserve incremental matches across render cycles

When restart_matcher runs with force=false, the callback marks results
with a MergeStrategy so the render loop knows how to combine them with
existing items. Previously, render's .take() would drain processed_items
to None, and the next incremental callback would create a fresh batch —
causing the render to replace all items with just the latest batch,
losing previously matched items.

Now:
- Replace: full re-match (force=true), replaces item list entirely
- SortedMerge: incremental sorted results, merged into item_list.items
- Append: incremental unsorted results (--no-sort), appended

This fixes match count consistency in interactive mode. With 1M items
and query "test", match count is now 290,083 on every run (matching
fzf's consistency), vs wildly varying counts before (min 13, max 56,950).

https://claude.ai/code/session_01T7pa2RRX85MvBtnH7PWtmw

* chore: fmt & clippy

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-02-15 19:08:02 +01:00
kimono-koans ad91558749
feat: use a separate thread pool for Matcher runs (#961)
* Initial commit

* Disable Matcher when query is empty

* Revert

* Cleanup

* Use par_chunks for faster search

* Cleanup

* Skip updating atomic on every iter

* Item index unnecessary?

* Cleanup

* Build thread pool at App level

* Initial commit

* Disable Matcher when query is empty

* Revert

* Cleanup

* Use par_chunks for faster search

* Cleanup

* Skip updating atomic on every iter

* Item index unnecessary?

* Cleanup

* Build thread pool at App level

* Make suggested improvements

* chore: cleanup after rebase

* refactor: rewrite insta test harness to use fine-grained Skim:: methods

Split `impl Skim` into a generic `impl<Backend> Skim<Backend>` block so
that `Skim::init()`, `Skim::start()`, `Skim::tick()`, etc. work with
any backend, not just the default CrosstermBackend.

New public API on Skim<B>:
- `init_tui_with(tui)` – inject a caller-provided TUI (e.g. TestBackend)
- `app()` / `app_mut()` – access the application state
- `tui_ref()` / `tui_mut()` – access the TUI
- `app_and_tui()` – simultaneous mutable access to both (avoids borrow
  conflicts in render and handle_event calls)
- `final_event()` – inspect the quit event

TestHarness now wraps `Skim<TestBackend>` and initializes via
`Skim::init()` + `Skim::init_tui_with()`, sharing the production
init path (theme, reader, command expansion) instead of duplicating it.

https://claude.ai/code/session_016PtHKc9YVEpHftDxG5Nger

* chore: make insta harness more realistic

* Cleanup merge errors

* Remove duplicate check

* Cleanup

* No need to clone twice

* fix: fix thread pool race condition

---------

Co-authored-by: Loric ANDRE <loric.andre@pm.me>
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-15 17:34:46 +01:00
Loric ANDRE 19bfd34aaf release: v3.2.0 2026-02-13 15:29:57 +01:00
kimono-koans 5d9743f198
feat: further reduce DefaultSkimItem size (#967)
* Initial commit

* Cleanup

* Prevent some double allocation behavior

* User the proper conversion method

* Further attempt to avoid alloc

* Actually avoid alloc

* Cleanup

* Remove an unnecessary alloc

* Cleanup

* Avoid another alloc

---------

Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2026-02-13 15:18:26 +01:00
LoricAndre 65b7c86d7e
chore: enhance PR template [skip ci]
Updated the pull request template to include a checklist item for conventional commit titles and documentation updates.
2026-02-13 14:05:17 +01:00
Loric ANDRE 1e118caec6 fix: republish crate 2026-02-13 10:25:30 +01:00
Loric ANDRE a766762b5a release: v3.1.0 2026-02-13 10:01:15 +01:00
LoricAndre 98a9184055
feat: add set-preview-cmd action to change preview (#969)
* feat: add set-preview-cmd action to change preview

* chore: update manpage

* chore: generate completions & manpage

---------

Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2026-02-13 08:32:13 +00:00
LoricAndre de7eab7619
feat(cli): add SKIM_OPTIONS_FILE (#972)
* feat(cli): add SKIM_OPTIONS_FILE

* chore: generate completions & manpage

---------

Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2026-02-13 09:24:05 +01:00
Loric ANDRE b02ef7b878 chore: remove duplicate changelog entry 2026-02-12 15:44:39 +01:00
Loric ANDRE 17c801bb07 release: v3.0.1 2026-02-12 15:24:16 +01:00
tzachar 4749154e5b
docs: add link to sqlite_skim (#971) [skip ci] 2026-02-12 15:14:28 +01:00
Loric ANDRE 4c12048762 fix: restart_matcher race condition (closes #970) 2026-02-12 15:08:37 +01:00
Loric ANDRE 267fb0c72e release: v3.0.0 2026-02-12 12:10:29 +01:00
LoricAndre e9fb1877c0
feat(lib): add fine-grained control over skim's event loop (#968)
* feat(lib): add fine-grained control over skim's event loop

* fix: do not panic on reload

* fix: cleaner handling of reload
2026-02-12 11:57:02 +01:00
Loric ANDRE 6bb1008101 chore(ci): make codecov less aggressive 2026-02-11 19:51:49 +01:00
Loric ANDRE d2f0a7f952 fix: run preview on move after selection 2026-02-11 18:34:05 +01:00
Loric ANDRE 149b47f37d feat: readd --sync functionality 2026-02-11 18:22:47 +01:00
Loric ANDRE d11e627ba6 feat!: use smarter setters, remove the need for Some(...) and String::from() in setters 2026-02-11 18:18:35 +01:00
Loric ANDRE 495b9ae611 docs: detail pty preview window flag [skip ci] 2026-02-11 13:30:33 +01:00
LoricAndre 9d0fb5eec9
feat: reduce DefaultSkimItem memory footprint by around 25% by default (#966) 2026-02-11 13:24:49 +01:00
Loric ANDRE a4f32c5533 docs: update nightly version [skip ci] 2026-02-11 10:01:55 +01:00
Loric ANDRE 1b23121956 chore: update frizbee and nightly version 2026-02-11 10:00:56 +01:00
依云 87b14ef577
fix: implement cursor_pos_from_tty ourselves (#963)
* fix: implement cursor_pos_from_tty ourselves

termion's cursor_pos has a very short timeout that won't work across
oceans; also it uses the wrong clock.

There is no need to try stdout first or read from stdin; it always works
with /dev/tty.

Using select because poll is said to not work with /dev/tty on macOS.[^1]

Using nix because it's already at the toplevel of dependencies.

[^1]: https://docs.rs/rustix/1.1.3/rustix/event/fn.poll.html

* chore: get rid of termion
2026-02-11 09:38:02 +01:00
dependabot[bot] d48f3f5e32
chore(deps): bump rand from 0.9.2 to 0.10.0 (#958)
Bumps [rand](https://github.com/rust-random/rand) from 0.9.2 to 0.10.0.
- [Release notes](https://github.com/rust-random/rand/releases)
- [Changelog](https://github.com/rust-random/rand/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-random/rand/compare/rand_core-0.9.2...0.10.0)

---
updated-dependencies:
- dependency-name: rand
  dependency-version: 0.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-10 21:51:42 +00:00
dependabot[bot] db507d6f59
chore(deps): bump clap from 4.5.56 to 4.5.57 (#955)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.56 to 4.5.57.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.56...clap_complete-v4.5.57)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.57
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-10 14:01:13 +01:00
dependabot[bot] 812525d582
chore(deps): bump tempfile from 3.24.0 to 3.25.0 (#956)
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.24.0 to 3.25.0.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/commits)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-version: 3.25.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-10 14:00:52 +01:00
dependabot[bot] ef041436f7
chore(deps): bump interprocess from 2.2.3 to 2.3.1 (#957)
Bumps [interprocess](https://github.com/kotauskas/interprocess) from 2.2.3 to 2.3.1.
- [Release notes](https://github.com/kotauskas/interprocess/releases)
- [Commits](https://github.com/kotauskas/interprocess/compare/2.2.3...2.3.1)

---
updated-dependencies:
- dependency-name: interprocess
  dependency-version: 2.3.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-10 14:00:40 +01:00
dependabot[bot] c051be779e
chore(deps): bump regex from 1.12.2 to 1.12.3 (#959)
Bumps [regex](https://github.com/rust-lang/regex) from 1.12.2 to 1.12.3.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.12.2...1.12.3)

---
updated-dependencies:
- dependency-name: regex
  dependency-version: 1.12.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-10 14:00:27 +01:00
kimono-koans e23a259451
feat: optimize match_item to use ref and not Arc (#962) 2026-02-10 10:16:00 +01:00
Loric ANDRE fdd93644b4 chore: test matchers 2026-02-09 23:31:43 +01:00
Loric ANDRE 00d32b6f9d chore: add coverage flag to README 2026-02-09 22:55:41 +01:00
Loric ANDRE b3ebf9db92 chore: ignore coverage files [skip ci] 2026-02-09 22:48:31 +01:00
LoricAndre a08f6ac9b3
feat!: interactive pty preview & concurrency optimizations (#952)
This PR has grown beyond its initial scope due to me over-optimizing everything, but it leads to:

    Paving the way for future actually interactive previews
    Consistently better performance than fzf in our bench thanks to thread and concurrency optimizations as well as the use of kanal for the items channels

Given the scope, I'm marking this as breaking because:

    setting wrap in the preview window layout disables the pty since we don't want to manipulate the raw buffer to word-wrap it manually
    kanal channels work slightly differently and might break library usage, even though switching to them did not require any modifications of the examples so it's unlikely that users will see anything break


* fix: force cwd for preview

* fix: correctly set cwd & kill pty child in the right order

* fix: use std threads & reopen new pty for each preview

* feat: use tui-term for displaying

* feat: scroll in pty

* fix: make nested skim previews work

* fix: clippy mistake

* feat: reactive preview triggering

* chore: generate completions & manpage

* chore: optimizations & thread cleanup

* chore: use kanal for faster channels

* fix: tests

* fix: only send items if the matcher hasn't been killed in the meantime (#947)

* tests: add coverage

* tests: fix bin path with coverage

* tests: upload tests to codecov

* chore: make pty opt-in through preview-window

* chore: generate completions & manpage

---------

Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2026-02-09 21:43:04 +00:00
Loric ANDRE 719de73182 docs: specify nightly version in README 2026-02-09 19:02:47 +01:00
dependabot[bot] 8a8b2c6149
chore(deps): bump time from 0.3.46 to 0.3.47 (#948)
Bumps [time](https://github.com/time-rs/time) from 0.3.46 to 0.3.47.
- [Release notes](https://github.com/time-rs/time/releases)
- [Changelog](https://github.com/time-rs/time/blob/main/CHANGELOG.md)
- [Commits](https://github.com/time-rs/time/compare/v0.3.46...v0.3.47)

---
updated-dependencies:
- dependency-name: time
  dependency-version: 0.3.47
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-05 22:24:18 +01:00
dependabot[bot] b1b35d387f
chore(deps): bump bytes from 1.11.0 to 1.11.1 (#946)
Bumps [bytes](https://github.com/tokio-rs/bytes) from 1.11.0 to 1.11.1.
- [Release notes](https://github.com/tokio-rs/bytes/releases)
- [Changelog](https://github.com/tokio-rs/bytes/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tokio-rs/bytes/compare/v1.11.0...v1.11.1)

---
updated-dependencies:
- dependency-name: bytes
  dependency-version: 1.11.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-03 22:49:29 +00:00
Loric ANDRE d245fdd002 fix: manually patch modifiers (closes #945) 2026-02-03 23:38:40 +01:00
Loric ANDRE e93c152d8d release: v2.0.2 2026-02-02 20:13:02 +01:00
Loric ANDRE 827688b63c fix: fix preview_fn 2026-02-02 19:21:11 +01:00
Loric ANDRE 85b811ab3b chore: update interactive mode examples (closes #943) 2026-02-01 16:10:16 +01:00
Loric ANDRE 1c1279514b release: v2.0.1 2026-02-01 16:05:43 +01:00
LoricAndre ee0d6dcd19
fix(linux): run preview in a PTY (closes #894) (#897)
* Reapply "fix: run preview in a PTY (closes #894)"

This reverts commit c5eb948500.

* chore: use pty only on linux

* chore: add test
2026-02-01 15:57:41 +01:00
Loric ANDRE 2ac22b6344 release: v2.0.0 2026-01-31 10:25:41 +01:00
Loric ANDRE 015fd28db6 chore: add valgrind and thread sanitizer test profiles [skip ci] 2026-01-31 09:56:49 +01:00
LoricAndre b96c65507e
feat!: send & receive items in batches (#938)
* feat!: send & receive items in batches

* fix: add timeout

* Revert "fix: matcher race condition at startup"

This reverts commit 8e25217a01.

* fix: correctly init matcher_control as stopped
2026-01-29 17:57:39 +00:00
Loric ANDRE 5446f20e8c release: v1.11.2 2026-01-29 18:31:00 +01:00
Loric ANDRE d45f4c41fe fix: always make ctrl-d abort (closes #941) 2026-01-29 18:23:10 +01:00
Loric ANDRE 9f9771f79a release: v1.11.1 2026-01-29 13:08:24 +01:00
Loric ANDRE 77144056ca fix: do not enter TUI early if the matcher needs restarting before ingestion done (closes #940) 2026-01-29 12:53:14 +01:00
Loric ANDRE f31a844873 chore: reduce FPS for better performance 2026-01-29 12:38:07 +01:00
Loric ANDRE 099ab75f8a chore: add nix flake for dev [skip ci] 2026-01-28 17:40:26 +01:00
Loric ANDRE 923b018aa3 release: v1.11.0 2026-01-27 22:35:50 +01:00
Loric ANDRE c0a368b901 feat: add custom action for lib usage (closes #537) 2026-01-27 19:50:52 +01:00
Loric ANDRE edaea9f34a release: v1.10.0 2026-01-27 16:44:54 +01:00
Loric ANDRE 9654ad321a fix: always display item_list (closes #939) 2026-01-27 15:06:05 +01:00
LoricAndre 55b50cb3bf
feat: add --normalize to ignore accents etc. when matching (closes #453) (#914)
* feat: add `--normalize` to ignore accents etc. when matching (closes #453)

* chore: generate completions & manpage

* chore: merge master

* chore: generate completions & manpage

* chore: use a matcher engine for normalization

* chore: reset useless changes

---------

Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2026-01-27 14:04:51 +00:00
phanium c7c50a4896
fix: make -1 reset highlights (#937)
* fix: support -1 in to keep the original color

`fd --color=always| sk --color=hl👎reverse --ansi`

This is supported by fzf

* fix: do not override bg/fg when resetting one of them

---------

Co-authored-by: Loric ANDRE <loric.andre@pm.me>
Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2026-01-27 11:52:20 +00:00
Loric ANDRE 2b71c2bad9 fix(frizbee): correctly compute max_typos 2026-01-27 12:12:13 +01:00
Loric ANDRE d29c30c756 release: v1.9.1 2026-01-27 11:21:51 +01:00
phanium a99cae3ca2
fix: clear screen when not in fullscreenon bash & fish (#936)
* fix: clear screen when not in fullscreen

`SKIM_DEFAULT_OPTIONS= sk --height=90%` don't clear screen on exit.

It don't use alt screen so we clear the screen manually on exit.

* fixup! fix: clear screen when not in fullscreen

* test: use explicit escape key and move to platform-specific

---------

Co-authored-by: Loric ANDRE <loric.andre@pm.me>
2026-01-27 09:51:20 +00:00
dependabot[bot] 13c1b70d61
chore(deps): bump nix from 0.30.1 to 0.31.1 (#934)
Bumps [nix](https://github.com/nix-rust/nix) from 0.30.1 to 0.31.1.
- [Changelog](https://github.com/nix-rust/nix/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nix-rust/nix/compare/v0.30.1...v0.31.1)

---
updated-dependencies:
- dependency-name: nix
  dependency-version: 0.31.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-27 09:13:18 +01:00
Loric ANDRE 2710e648c9 release: v1.9.0 2026-01-26 21:46:53 +01:00
Loric ANDRE fbccdae64e feat(theme): add catppuccin themes 2026-01-26 21:45:13 +01:00
Loric ANDRE 30ef368c3e feat(frizbee): adaptive max_typos value 2026-01-26 21:14:26 +01:00
Loric ANDRE e5a0375e4a release: v1.8.1 2026-01-26 15:22:11 +01:00
Loric ANDRE 1d70a823e1 fix: correctly flush buffered stderr 2026-01-26 15:20:39 +01:00
Loric ANDRE 040fc6850e release: v1.8.0 2026-01-26 13:58:51 +01:00
Loric ANDRE 070b8347e4 fix: fix cursor blinking (closes #932) (credits to @lilydjwg) 2026-01-26 13:46:30 +01:00
Loric ANDRE 5eb9703547 feat: parse ansi codes in prompt 2026-01-26 13:37:32 +01:00
LoricAndre 5d938b05fe
fix: make select-1 and exit-0 work again (closes #916) (#933) 2026-01-26 13:14:51 +01:00
Loric ANDRE 0d67e1a126 chore: add matrix room to readme 2026-01-25 23:21:00 +01:00
Loric ANDRE 960a6ca064 fix: correctly overlay header styles 2026-01-25 23:04:41 +01:00
Loric ANDRE 788eb81334 tests: review snapshots 2026-01-25 21:56:28 +01:00
Loric ANDRE 6a6fea3e3a fix: header and header-lines order in reverse layout 2026-01-25 21:40:30 +01:00
Loric ANDRE 8cf06ab256 release: v1.7.2 2026-01-25 19:34:24 +01:00
Loric ANDRE 94c4bd51fe fix: correct cursor position when using reverse and border (closes #928) 2026-01-25 19:25:42 +01:00
Loric ANDRE 9ab5c7356a release: v1.7.1 2026-01-25 11:37:57 +01:00
Loric ANDRE d1494779ec fix: y cursor pos in reverse mode (closes #931) 2026-01-25 11:24:56 +01:00
Loric ANDRE c0f8cd4318 release: v1.7.0 2026-01-25 10:50:17 +01:00
Loric ANDRE 7ff7b01031 fix: correctly compute character width for cursor display (closes #929) 2026-01-25 10:41:57 +01:00
LoricAndre ec0658a9d4
feat: add borders to all widgets (#930) 2026-01-25 10:36:28 +01:00
Loric ANDRE a6613e4559 fix: re-enable query/cmd-query distinction and switching 2026-01-24 16:47:55 +01:00
Loric ANDRE bc9d159e30 fix: revert case-insensitive action_chain 2026-01-24 15:57:46 +01:00
Loric ANDRE c5a95a3845 fix: replace {n} with an empty string when no item is selected 2026-01-24 15:52:34 +01:00
Loric ANDRE 4e98ce34b3 fix: interrupt the reader thread when stopping 2026-01-24 15:52:02 +01:00
Loric ANDRE 6f548ae252 fix: fix printf sometimes replacing recursively 2026-01-24 14:22:31 +01:00
Loric ANDRE 505c5deae7 fix: correctly toggle prompt on ToggleInteractive (closes #925) 2026-01-24 11:06:00 +01:00
Loric ANDRE f4264c7cbe fix: correctly display all header lines 2026-01-24 10:24:50 +01:00
Loric ANDRE c2ab623851 fix: correctly merge base styles 2026-01-24 10:19:14 +01:00
Loric ANDRE 856c65bfc8 chore: cleanup changelog [skip ci] 2026-01-24 09:23:06 +01:00
Loric ANDRE d55c9b0487 release: v1.6.0 2026-01-23 19:30:56 +01:00
LoricAndre 1536eb9657
feat: add --remote flag to call remote (--listen) instances (#915)
* feat: add `--remote` flag to call remote (`--listen`) instances

* chore: generate completions & manpage

* docs: update docs

---------

Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2026-01-23 18:19:43 +00:00
Loric ANDRE 47e547a9ad tests: remove insta_ prefixes after finalizing tests migration 2026-01-23 17:58:05 +01:00
Loric ANDRE eef3e8dd28 fix: make no-sort work again 2026-01-23 17:48:51 +01:00
Loric ANDRE 653a3eb6b7 release: v1.5.4 2026-01-23 16:38:29 +01:00
Loric ANDRE 76812361b2 fix: translate frizbee's byte indices into char indices 2026-01-23 15:35:39 +01:00
Loric ANDRE 870267bda6 docs: customize man page 2026-01-23 14:44:26 +01:00
Loric ANDRE 96a4c69646 fix: correctly merge styles & do not reset them by default (#918) 2026-01-23 11:50:44 +01:00
Loric ANDRE 2cfd33ccc9 fix: remove unnecessary clone in printf 2026-01-22 21:54:41 +01:00
Loric ANDRE 16ef0f4a27 fix: do not override {} with {q} in interactive mode 2026-01-22 21:34:40 +01:00
Loric ANDRE 1c538e36b5 release: v1.5.3 2026-01-22 17:56:05 +01:00
Mathieu Lemay 554896db64
fix: Escape last ; in env var value before passing to tmux (#912)
* fix: Escape last ; in env var value before passing to tmux

* Use shell_quote for more robust quoting

* Use Sh quoting always

* Revert to original escaping, add test

---------

Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2026-01-22 16:49:18 +00:00
Loric ANDRE ebab1d70c4 fix: quote expanded items independently (#910) 2026-01-22 17:08:40 +01:00
Loric ANDRE 432a692e68 release: v1.5.2 2026-01-22 14:57:02 +01:00
Loric ANDRE 1b02d824c1 fix: strip ansi from expanded items (#910) 2026-01-22 14:39:42 +01:00
Loric ANDRE efb2ea8be5 fix: ignore {+} expressions when splitting action chains (closes #910) 2026-01-22 13:49:50 +01:00
Loric ANDRE c8acb4b976 release: v1.5.1 2026-01-22 12:45:37 +01:00
LoricAndre 3c5b2d4209
fix: correctly expand {+} to current when no items are selected (cl… (#913)
* fix: correctly expand `{+}` to current when no items are selected (closes #910)

* chore: generate completions & manpage

* test: add printf_plus unit tests

---------

Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2026-01-22 11:28:58 +00:00
Loric ANDRE ba50acd7f6 release: v1.5.0 2026-01-22 11:52:31 +01:00
Loric ANDRE 662e92f835 fix: make case option work with non-ascii input (closes #454) 2026-01-22 00:12:18 +01:00
LoricAndre 79672e348f
feat: add set-query action to update the input (closes #657) (#907)
* feat: add `set-query` action to update the input (closes #657)

* chore: generate completions & manpage

* test: add set_query tests

---------

Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2026-01-21 22:42:26 +00:00
Loric ANDRE 773293974e chore: fix tests link in PR template [skip ci] 2026-01-21 23:34:31 +01:00
Loric ANDRE c5f2a3bdc9 release: v1.4.0 2026-01-21 18:38:33 +01:00
LoricAndre 1ee19d3caa
feat: split-match (#906)
* wip: split-match

* chore: generate completions & manpage

* chore: use engine factory for split match

* chore: generate completions & manpage

* docs: update README.md with new option [skip ci]

---------

Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2026-01-21 18:35:30 +01:00
Loric ANDRE 1af0f643cb docs: reflect need for nightly rust in install section [skip ci] 2026-01-21 18:24:30 +01:00
Loric ANDRE d78ab85e41 release: v1.3.2 2026-01-21 16:57:41 +01:00
LoricAndre 0792967ea6
docs: add details to interactive mode in manpage (closes #805) (#816)
* docs: add details to interactive mode in manpage (closes #805)

* chore: generate completions & manpage

* docs: precise what happens when c and i are used separately

* chore: generate completions & manpage

* docs: add MODES section

* chore: generate completions & manpage

* chore: generate completions & manpage

---------

Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2026-01-21 16:49:22 +01:00
Loric ANDRE 3b145cce7f fix: better spinner debounce behavior to avoid flickering (closes #904) 2026-01-21 16:48:12 +01:00
LoricAndre d7b61e34cb
docs: update README install section 2026-01-21 16:27:08 +01:00
LoricAndre 6d0c310bea
test: use insta for applicable integration tests, making them cross-p… (#903)
* test: use insta for applicable integration tests, making them cross-platform

* fix: remove @cmd from insta tests

* fix: remove @cmd from insta tests

* fix: use printf instead of echo
2026-01-21 15:41:20 +01:00
Loric ANDRE 2b5a6ac66f release: v1.3.1 2026-01-21 09:23:04 +01:00
Loric ANDRE 6848e5afb6 fix: allow layout to override reverse (closes #901) 2026-01-21 09:15:47 +01:00
Loric ANDRE 6fb14848fe chore: optimized release builds 2026-01-20 21:18:47 +01:00
Loric ANDRE 66b12e7d43 test: more reproducible and more precise bench [skip ci] 2026-01-20 19:52:20 +01:00
Loric ANDRE 9e80134abf test: allow multiple bench runs for better consistency 2026-01-20 18:15:32 +01:00
Loric ANDRE d8eda628fe release: v1.3.0 2026-01-20 17:20:26 +01:00
LoricAndre 35a61fd147
feat: typo resistant matcher using frizbee from blink.cmp (#891)
* feat: typo resistant matcher using frizbee from blink.cmp

* chore: generate completions & manpage

* fix: back to stable rustc using fork

* chore: update lockfile

* chore: feature gate

* ci: update actions

* chore: generate completions & manpage

* ci: use rustup directly

* ci: fix feature name

---------

Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2026-01-20 15:43:43 +00:00
Loric ANDRE 540c4d7245 release: v1.2.0 2026-01-20 14:34:52 +01:00
LoricAndre 4d4a33542d
feat: add no-strip-ansi flag (#898)
* feat: add no-strip-ansi flag

* chore: generate completions & manpage

---------

Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2026-01-20 14:31:41 +01:00
Loric ANDRE c5eb948500 Revert "fix: run preview in a PTY (closes #894)"
This reverts commit 3b0e31387b.
2026-01-20 14:01:55 +01:00
Loric ANDRE 3b0e31387b fix: run preview in a PTY (closes #894) 2026-01-20 13:47:01 +01:00
Loric ANDRE b3a221b740 release: v1.1.2 2026-01-20 09:57:54 +01:00
Loric ANDRE 52bf6a247b fix: use ansi-stripped raw item in preview expansion 2026-01-20 09:48:06 +01:00
Loric ANDRE 3edc307768 fix: half page down scrolls down 2026-01-20 09:33:51 +01:00
Loric ANDRE 5fa0ffa884 release: v1.1.1 2026-01-19 23:01:10 +01:00
Loric ANDRE 102f7c79f2 test: fix preview_nul 2026-01-19 22:59:48 +01:00
Loric ANDRE 9d486b78fa fix: use item output for fields 2026-01-19 22:55:48 +01:00
Loric ANDRE c2c8bb6fe1 fix: parse ansi codes in header 2026-01-19 22:11:47 +01:00
Loric ANDRE da74c01404 fix: use item text in printf 2026-01-19 21:58:11 +01:00
Loric ANDRE 223414091a chore: update crossterm version requirement to pass crates.io publish checks 2026-01-19 21:35:15 +01:00
Loric ANDRE 47c5cf829c release: v1.1.0 2026-01-19 21:19:28 +01:00
LoricAndre f03894c25b
test: fix wrap test (#896)
* test: fix wrap test

* chore: generate completions & manpage

* fix: fix test

---------

Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2026-01-19 21:17:42 +01:00
Loric ANDRE 33833ff5a6 fix: preview on large binaries does not hang or mangle the tui 2026-01-19 19:42:48 +01:00
Loric ANDRE c12b32cdc3 fix: delete outside char boundaries 2026-01-19 18:00:14 +01:00
Loric ANDRE 3e0cbd1e06 feat: wrap items 2026-01-19 17:17:13 +01:00
Loric ANDRE 484eb6a75e release: v1.0.1 2026-01-19 12:21:48 +01:00
Loric ANDRE 8ae57f6d01 fix: disable compact_matcher feature 2026-01-19 12:14:36 +01:00
Loric ANDRE 93ffbcea88 release: v1.0.0 2026-01-19 09:33:39 +01:00
Loric ANDRE 931ae8bc91 release: v1.0.0-pre11 2026-01-17 19:39:49 +01:00
Loric ANDRE d17f5bd95d test: fix flaky tests 2026-01-17 19:39:24 +01:00
Loric ANDRE 6f2412dde3 fix: always use cursor/selector colors (#892) 2026-01-17 19:34:24 +01:00
Loric ANDRE be121a7be4 chore(changelog): ignore release commits 2026-01-17 16:44:07 +01:00
Loric ANDRE 7b6e0fd300 release: v1.0.0-pre10 2026-01-17 16:38:15 +01:00
Loric ANDRE 0c01f31898 chore: add pointer/marker as aliases for selector/multi-selector 2026-01-17 16:34:58 +01:00
Loric ANDRE 128fc245f1 fix: only expand selection in {+} for commands 2026-01-17 16:23:39 +01:00
Loric ANDRE acde4f82ca release: v1.0.0-pre9 2026-01-17 00:23:40 +01:00
Loric ANDRE 8e25217a01 fix: matcher race condition at startup 2026-01-17 00:19:59 +01:00
Loric ANDRE b144416d80 release: v1.0.0-pre8 2026-01-16 23:39:24 +01:00
Loric ANDRE f608ef9d24 feat: add print-header flag (and readd print-score) (closes #470) 2026-01-16 23:34:30 +01:00
Loric ANDRE 7c9b7194e0 test: remove useless listen tests 2026-01-16 22:32:58 +01:00
LoricAndre e25e166003
fix(ui): use current highlight for the current item (closes #889) (#890)
* fix(ui): use current highlight for the current item (closes #889)

* fix: misc
2026-01-16 17:54:02 +01:00
Loric ANDRE 8b4a048af9 release: v1.0.0-pre7 2026-01-16 16:46:23 +01:00
Loric ANDRE 577025224e fix: correctly parse wrap arg in preview options 2026-01-16 16:27:20 +01:00
LoricAndre 3782ae85e1
fix: fix listen flag on macos (#888)
* test: add vanilla test

* test: add vanilla test

* test: add vanilla test

* chore: better error

* fix: @dbg never times out

* chore: increase timeout

* chore: increase timeout

* fix: allow listen tests to retry
2026-01-16 15:58:07 +01:00
Loric ANDRE 1bf7b5266a test: add tests for listen flag 2026-01-16 10:01:28 +01:00
Loric ANDRE 0de00e972e feat: add listen flag (closes #719) 2026-01-15 21:02:32 +01:00
Loric ANDRE 1fef63e720 release: v1.0.0-pre6 2026-01-15 15:30:31 +01:00
Loric ANDRE d3efe18b0d fix: fix build without default features 2026-01-15 15:14:19 +01:00
Loric ANDRE e4dc4d02f0 feat: add --shell-bindings flag to get bindings at runtime 2026-01-15 15:07:25 +01:00
Loric ANDRE 8e3d143819 fix: disable completions without cli feature 2026-01-15 14:56:24 +01:00
Loric ANDRE 0a8087d8bb release: v1.0.0-pre6 2026-01-15 14:53:06 +01:00
Loric ANDRE 15b6a14e0f feat: add nushell completion support (closes #459) 2026-01-15 14:47:13 +01:00
Loric ANDRE 60f4c1d17b feat: add disabled flag (closes #500) 2026-01-15 14:11:53 +01:00
Loric ANDRE 48e60a932c feat: add cycle flag (closes #553) 2026-01-15 12:28:29 +01:00
Loric ANDRE 21586f911c chore: add exhaustive_match macro for enum building from str 2026-01-15 11:41:20 +01:00
Loric ANDRE 2dddd43ca0 release: v1.0.0-pre5 2026-01-15 10:35:47 +01:00
Loric ANDRE c7ae5bf1a2 feat(ui): allow setting modifiers (closes #871) 2026-01-15 10:35:09 +01:00
Loric ANDRE 56f3ff76c4 feat(ui): add selector and multi-selector options to set the itemlist icons 2026-01-14 21:16:59 +01:00
Loric ANDRE 56536b8eec release: v1.0.0-pre4 2026-01-14 19:14:26 +01:00
Loric ANDRE fe3f470b8a fix(cmd)!: always use sh for all command executions 2026-01-14 19:14:10 +01:00
Loric ANDRE 3dc64cfb90 feat: 120 FPS 2026-01-14 18:33:38 +01:00
Loric ANDRE 7e962392cd chore: regenerate CHANGELOG.md 2026-01-14 18:15:40 +01:00
Loric ANDRE 7a31fca789 chore: release v1.0.0-pre3 2026-01-14 17:23:09 +01:00
Loric ANDRE 26d84fae9b fix: fix terminal height management 2026-01-14 17:18:36 +01:00
Loric ANDRE c1a7ae610a chore: release 2026-01-14 16:22:02 +01:00
Loric ANDRE 94c4d76ca2 feat(ci): add crates.io publish to release CI 2026-01-14 16:19:18 +01:00
LoricAndre bd7af947e3
fix: manually acquire cursor pos (closes #885) (#886)
* fix: manually acquire cursor pos (closes #885)

* chore: bump version

* chore: generate completions & manpage

* chore: revert crate upgrades

* chore: generate completions & manpage

---------

Co-authored-by: Loric André <loric.andre@noreply.me>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2026-01-14 16:07:53 +01:00
LoricAndre 19d19eb6c6
Update issue templates 2026-01-13 21:56:28 +01:00
LoricAndre 904ec2fa3e
chore: remove unneeded deps (#884)
* chore: remove unneeded deps

* chore: generate completions & manpage

* chore: remove unneeded deps

* chore: fix PR template tests link

---------

Co-authored-by: Loric André <loric.andre@noreply.me>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2026-01-13 12:00:44 +01:00
LoricAndre c364c5f128
chore: remove workspace (#883)
* chore: remove workspace

* chore: regen dist config

* fix: readme path

* chore: extra-artifacts as files

* chore: generate dist CI

* chore: prepare for prerelease

* chore: switch to include + generate-files

* chore: use run step for generate-files

---------

Co-authored-by: Loric André <loric.andre@noreply.me>
2026-01-13 11:34:10 +01:00
LoricAndre b8dc423f9b
feat!(ui): ratatui migration (#864)
# Breaking changes
## Binds

  -  execute(...) will still run the command if no item is selected. To get the previous behavior back, use if-non-matched()+execute(...)
  -  field expansion in execute and preview will no longer support arbitrary spaces, for instance { } will not get expanded as {}
  -  interactive mode will not use stdin/skim default command when starting up.
  -  expect bind is deprecated
  -  interactive mode will now expand like other commands, except that {} will keep expanding to the current query.




* back to compiling

* work on item list & return the results

* add debounce to matcher

* feat: readd actions and binds

* clippy

* wip: use options in ui

* wip: ratatui

* feat: bring perf close to tuikit version

* chore: use list widget

* feat: working debounce on item reading

* chore: migrate to crossbeam channels

* chore: clippy & fmt

* chore: allow different backend for Tui

* feat: add matcher polling

* feat: page scrolling

* wip: statusline

* feat: working statusline

* tmp

* feat: 56/158 e2e passing

* feat: 67/158 e2e passing

* chore: generate completions & manpage

* feat: 72/158 e2e passing

* Claude/pr 864 e2e tests 011 c uyyt et2b q5n e drn aaf2 s (#873)

* docs(vim): convert FIXME to descriptive Note comment

Replace FIXME comment with a Note that accurately describes the
working directory restoration heuristic. The current implementation
is intentional and handles most use cases correctly. The comment now
documents the behavior rather than implying it needs fixing.

* feat(tui): replace todo!() panics with no-op stubs

Replace all todo!() macro panics with no-op implementations or basic
stubs for unimplemented ratatui features. This prevents crashes when
these actions are triggered during testing.

Changes:
- History navigation (NextHistory, PreviousHistory): no-op stubs
- Preview scrolling (Up/Down/Left/Right/PageUp/PageDown): no-op stubs
- Command/mode controls (RefreshCmd, RotateMode): no-op stubs
- Horizontal scrolling (ScrollLeft/Right): no-op stubs
- Preview toggles (TogglePreviewWrap, ToggleSort): no-op stubs
- Preview with position variants: basic implementations using existing
  preview methods

These features still need full implementation but won't panic now.

* test(e2e): increase wait timeout to fix slow test startup

Increased the wait timeout from 1 second (200 * 5ms) to 10 seconds
(400 * 25ms) to accommodate slower test startup times. This fixes
failures in tests that were timing out waiting for sk to initialize,
particularly the binds test suite which now passes all 6 tests.

* feat(tui): implement query history navigation

Implemented PreviousHistory and NextHistory actions for navigating
through query history using Ctrl-P and Ctrl-N.

- Added query_history, history_index, and saved_input fields to App struct
- Load history from options.query_history on initialization
- PreviousHistory (Ctrl-P): Navigate backward through history (newer to older)
- NextHistory (Ctrl-N): Navigate forward through history (older to newer)
- Saves current input when entering history, restores when returning

Manual testing confirms history navigation works correctly.

* feat(tui): implement preview scrolling

Implemented all preview scrolling actions for navigating preview content.

- Added scroll_y and scroll_x fields to Preview struct to track scroll position
- Implemented scroll_up, scroll_down, scroll_left, scroll_right methods
- Implemented page_up and page_down for full-page scrolling
- Updated render function to use scroll offsets via Paragraph::scroll()
- Preview content resets scroll position when content changes
- Implemented PreviewUp, PreviewDown, PreviewLeft, PreviewRight actions in App
- Implemented PreviewPageUp and PreviewPageDown actions in App

* fix(history): prevent duplicate history entries

Fixed critical bug where init_histories() was being called twice,
causing history entries to be duplicated. The issue was that
parse_args() called .build() and then main also called .build()
on the result, leading to init_histories() running twice.

Changed parse_args() to return unparsed options, letting main.rs
call .build() only once. This ensures history is loaded exactly
once without duplicates.

Before: history file would contain "a\nb\nc\na\nb\nc\nnew_query"
After: history file correctly contains "a\nb\nc\nnew_query"

* feat(tui): implement interactive mode

Implemented full interactive mode (-i flag) support for the ratatui migration.

**Key Features:**
- Command prompt ("c>") instead of query prompt (">") in interactive mode
- Separate command history navigation using cmd_history
- Command execution on history navigation via Event::Reload
- Support for --cmd-query initial command
- SkimOutput returns user's command in interactive mode

**Implementation Details:**
- Added cmd_history, cmd_history_index, and saved_cmd_input fields to App
- Modified Input initialization to use cmd_prompt and cmd_query in interactive mode
- Updated PreviousHistory/NextHistory to use cmd_history when options.interactive is true
- In interactive mode, history navigation triggers Event::Reload to execute commands
- Modified SkimOutput to return app.input as cmd in interactive mode

**Manual Testing:**
All interactive mode functionality verified working:
- Prompt displays "c>" correctly
- Ctrl-P/Ctrl-N navigate through command history (c→b→a→b)
- Typing updates command (b→bn)
- History file written correctly on exit

* fix(statusline): render space when spinner not shown for e2e tests

When the spinner is not displayed (reading and matching complete), the
status line needs to maintain its layout by rendering a space in the
spinner area. This ensures the status line format is "  N/N" (two spaces)
rather than " N/N" (one space), which is what the e2e tests expect.

Also simplified show_progress_indicators logic to check reading ||
matcher_running directly instead of using time-based thresholds.

This fixes all previously failing basic tests (defaults, binds, case,
history, tmux) which were timing out because they couldn't find the
expected status line format.

Test results after fix:
- binds: 6/6 passed
- case: 10/10 passed
- defaults: 4/4 passed
- history: 2/2 passed
- tmux: 2/2 passed

* fix(input): implement Yank action and fix BackwardKillWord

- Add insert_str() method to Input for inserting strings
- Fix Yank action to paste from yank_register instead of storing to it
- Fix delete_backward_word() to stop at non-word characters (alphanumeric only)
  instead of just whitespace, matching standard word deletion behavior

Test improvements:
- keys_ctrl_y: ✓ PASSED
- keys_alt_bspace: ✓ PASSED

Remaining failures: keys_ctrl_d, keys_ctrl_w, keys_ctrl_arrows, keys_tab, keys_btab

* fix(input): fix delete and word movement actions

- Fix delete() to use actual cursor position, not display position
- Change DeleteChar and DeleteCharEOF to use offset 0 (delete at cursor)
- Split word deletion into two functions:
  - delete_backward_word(): Uses alphanumeric boundaries (for Alt+Backspace)
  - delete_backward_to_whitespace(): Uses whitespace boundaries (for Ctrl+W)
- Update word movement to use alphanumeric word boundaries

Test improvements:
- keys_ctrl_d: ✓ PASSED (DeleteChar)
- keys_ctrl_w: ✓ PASSED (UnixWordRubout)
- keys_alt_bspace: ✓ STILL PASSING (BackwardKillWord)
- keys_ctrl_y: ✓ STILL PASSING (Yank)

Remaining: keys_ctrl_arrows needs adjustment for compound words

* fix(item_list): fix selection rendering to show only current item marker

Fixed the item list rendering to only show ">" for the current item,
not for selected items. This matches the expected behavior when not
using --multi flag.

Changes:
- Removed highlight_symbol from List widget (was adding extra space)
- Manually add ">" marker only for current item
- Add space after marker for consistent formatting ("> item" or "  item")
- Apply current item style only to current item

Test improvements:
- keys_tab: ✓ PASSED
- keys_btab: ✓ PASSED

Keys test suite: 21/22 passing (95%)
Remaining: keys_ctrl_arrows (compound word navigation)

* fix(input): separate word boundaries for deletion vs cursor movement

Split word boundary logic to handle two different behaviors:
- Alphanumeric boundaries for deletion (Alt+D, Alt+Backspace)
- Whitespace boundaries for cursor movement (Ctrl+Right, Ctrl+Left)

This allows compound words like "foo-bar" to be treated as:
- Single unit for cursor navigation (Ctrl+Right moves past entire word)
- Multiple words for deletion (Alt+D deletes only "foo")

Changes:
- find_next_word_end(): Uses alphanumeric boundaries for deletion
- find_compound_word_end(): Uses whitespace boundaries for movement
- move_cursor_forward_word(): Now uses compound word boundaries

Fixes keys_alt_d and keys_ctrl_arrows tests.
All 22 keys tests now passing.

* fix(item_list): respect multi-select mode for selection markers

Only show selection markers (">") in multi-select mode (-m flag).
In single-select mode, items should not display selection markers
even if they exist in the selection HashSet.

Changes:
- Added multi_select field to ItemList struct
- Set multi_select from options.multi in with_options()
- Only render selection marker when multi_select && is_selected
- Updated both normal and debug render functions

Fixes:
- bind_append_and_select: Shows ">>" in multi-select mode
- keys_tab/keys_btab: Shows only current marker in single-select mode

All 22 keys tests passing (100%).

* feat(interactive): fix interactive mode to not filter items on typing

In interactive mode, the input is a command to execute, not a filter query.
Items should be displayed without filtering until a command is executed.

Changes:
- Skip restart_matcher when typing/editing in interactive mode
  - AddChar, BackwardDeleteChar, BackwardKillWord, DeleteChar, DeleteCharEOF
  - KillWord, UnixLineDiscard, UnixWordRubout, Yank
- Use empty query for matcher in interactive mode
  - matcher.run() now uses empty Input in interactive mode
  - All items are shown regardless of what user types
  - Typing only updates the command, doesn't filter items

This fixes all 22 keys_interactive tests.
Now works correctly with piped stdin in interactive mode.

* test(interactive): add tests for command execution on typing

Added two tests to verify interactive mode command execution behavior:

1. keys_interactive.rs::interactive_command_execution()
   - Tests typing commands in interactive mode
   - Verifies "echo foo" executes and shows "foo"
   - Verifies clearing and typing "echo bar" shows "bar"

2. defaults.rs::interactive_mode_command_execution()
   - Same test in defaults suite for baseline behavior
   - Tests command execution without piped input

These tests currently fail as interactive mode doesn't execute
commands as you type - they need Event::Reload on each keystroke.

* fix(test): correct interactive mode tests to use --cmd with {} expansion

Fixed the interactive mode tests to properly test the actual behavior:
- Interactive mode executes the command passed via --cmd
- The {} placeholder in the command gets replaced with typed input
- Command re-executes automatically as you type

Test changes:
- Use --cmd "echo 'foo {}'" to provide the command template
- Typing "bar" should execute "echo 'foo bar'" and show "foo bar"
- Typing more or deleting triggers re-execution with new substitution

This is the correct interactive mode behavior, not executing arbitrary
typed commands.

* feat(interactive): implement command execution with {} expansion in interactive mode

In interactive mode with --cmd, the typed input now expands the {} placeholder
in the command and re-executes it on every keystroke (AddChar, BackwardDeleteChar,
BackwardKillWord, DeleteChar, DeleteCharEOF, KillWord, UnixLineDiscard,
UnixWordRubout, Yank).

Key changes:
- Modified expand_cmd() to use simple {} replacement in interactive mode
- Added Event::Reload handling to clear item_pool, item_list, and drain rx channel
- Interactive mode with --cmd now starts with no-op command (":") instead of
  executing the command initially
- Added drain_rx() method to ItemList to clear pending matches from channel
- Only execute commands on keystroke when both interactive mode AND --cmd are active

Added test for interactive mode command execution that verifies {} expansion
works correctly as the user types.

Fixes command execution in interactive mode to properly expand {} with typed input.

* fix(reload): don't clear displayed items during reload to avoid blank screen

When handling Event::Reload, keep the old items visible until new ones arrive
from the matcher. This prevents a flash of blank space and ensures tests that
check for immediate output updates work correctly.

The item_pool is still cleared to ensure the matcher processes only new items,
but item_list.items stays populated with the previous results until the new
matcher sends updated results through the rx channel.

Fixes binds tests that were timing out due to unexpected blank lines.

---------

Co-authored-by: Claude <noreply@anthropic.com>

* feat(e2e): use nextest to simplify e2e tests

* Claude/continue ratatui work 011 cv2 d vpg29 cp7 w3 z nfy djw (#874)

* fix(tui): add cancellation token support to event loop

The event loop task was not checking the cancellation token, causing
it to continue running even after the TUI was stopped. This resulted
in tests hanging indefinitely.

Changes:
- Clone cancellation token in start() method
- Add cancellation check as first branch in tokio::select!
- Replace unwrap() with _ = for send() calls to avoid panics
- Break out of loop when cancellation token is triggered

This fix resolves the hanging tests and allows proper cleanup.

* fix(interactive): execute command with initial query substitution

In interactive mode with --cmd, the command should execute immediately
with {} replaced by the initial query value (empty string or --query value).

Previously, it was using ':' as a no-op placeholder, which prevented
any results from showing up initially.

This fixes most of the interactive_mode_command_execution test.

* feat(tests): improve tmux capture and add line padding for trailing spaces

- Add -J flag to tmux capture-pane to preserve line structure
- Add debug logging for item rendering to trace data flow
- Implement line padding in item list to full area width

The interactive_mode_command_execution test expects trailing spaces
to be preserved (e.g., 'foo ' not 'foo'). However, ratatui doesn't
write trailing whitespace to terminals unless there's content after it,
and tmux doesn't capture whitespace that isn't written.

This is a known limitation of terminal rendering. The data structures
correctly contain 'foo ' with trailing space (verified by trace logs),
but it's lost in the terminal -> tmux -> capture pipeline.

All other integration tests pass successfully (50/53 e2e tests).

* fix(test): use starts_with for item matching to handle trailing space stripping

Terminal rendering doesn't preserve trailing whitespace, so use
starts_with() instead of exact equality for item text assertions.

This allows the test to pass while still validating the correct
content appears on screen.

All e2e tests now pass (54/54 integration tests).

* refactor: remove debug logging and line padding from item_list

Remove temporary debugging code and line padding logic that
was added during investigation of trailing space rendering.

The test fix using starts_with() is sufficient, so these
changes are no longer needed.

---------

Co-authored-by: Claude <noreply@anthropic.com>

* chore: add interactive mode init to breaking changes

* fix(tui): pass tiebreak options to matcher and sort items correctly (#875)

This fixes the tiebreak end-to-end tests by ensuring tiebreak options
are properly used in the ratatui implementation:

1. Pass RankBuilder with tiebreak criteria to matcher factory
2. Sort matched items by rank in ascending order (correct for how
   ranks are calculated with negative scores for better matches)
3. Apply sorting in both render methods when receiving new items

All 10 tiebreak tests now pass (previously 9/10 were timing out).

Co-authored-by: Claude <noreply@anthropic.com>

* docs: use nextest for all tests in AGENTS.md

* Add SkimWidget trait with from_options and render methods (#876)

* Add SkimWidget trait with from_options and render methods

- Create SkimWidget trait with:
  - from_options(options: &SkimOptions, theme: Arc<ColorTheme>) -> Self
  - render(&mut self, area: Rect, buf: &mut Buffer) -> SkimRender
- Create SkimRender struct with items_updated boolean field
- Implement SkimWidget for all TUI widgets:
  - App, ItemList, Input, StatusLine, Header, Preview
- Remove ratatui Widget trait implementations
- All widgets now initialize using SkimWidget::from_options
- Add Clone derive to SkimOptions, Input, and Preview
- Fix AppendAndSelect to use input.value instead of input

All widgets now use the custom SkimWidget trait instead of ratatui's
Widget trait, with centralized initialization through from_options.

* Remove Clone derive from SkimOptions

- Remove Clone derive from SkimOptions struct
- Remove SkimWidget trait implementation from App
- Add render_skim method to App that returns SkimRender
- Update App rendering to use render_skim instead of SkimWidget::render

App doesn't implement SkimWidget because it needs to own SkimOptions
rather than construct from a reference. The existing App::from_options
method takes ownership of SkimOptions as needed.

* Implement ratatui Widget trait for App instead of custom render method

Changed App to implement ratatui's Widget trait for &mut App<'_>
instead of having a custom render_skim method. This allows using
f.render_widget() directly in the draw closure. App does not implement
SkimWidget since it needs to own SkimOptions rather than just reference it.

* tests: better logging

* chore: fmt

* fix: remove clone derive from input widget

* fix: compact render syntax

* chore: rename with_options to from_options for Reader

* Replace with_options with from_options across all widgets

- Updated App::from_options to use SkimWidget::from_options for all widgets
- Removed with_options method definitions from Header, Input, StatusLine, and ItemList
- All widgets now exclusively use the SkimWidget trait's from_options method
- Removed empty impl block from StatusLine

---------

Co-authored-by: Claude <noreply@anthropic.com>

* tests: add fail-fast and retries to default profile

* Claude/fix ratatui tests 011 cv4gj exnq rd p4 al zg593o (#877)

* fix: update examples for ratatui migration

- Change Skim::run_with() to accept owned SkimOptions instead of &SkimOptions
- Update .bind() to accept KeyMap (from string) instead of Vec<String>
- Restructure option_builder.rs to avoid cloning SkimOptions
- Update all affected examples: cmd_collector, custom_item, custom_keybinding_actions, downcast, nth, option_builder, sample, selector

* test: fix failing unit tests for Rust behavior changes

- Update size tests to expect InvalidDigit instead of NegOverflow
  This aligns with current Rust standard library behavior when parsing
  negative numbers into unsigned integer types (u16)
- Fix printf test to expect spaces instead of newlines
  The implementation joins items with spaces, so the test expectation
  should match this behavior
- Update percent_neg test to expect full input string "-10%" in error

---------

Co-authored-by: Claude <noreply@anthropic.com>

* feat: only 33 tests remaining

* fix: preview tests passing

* chore: generate completions & manpage

* fix: fix printf test after adding quotes

* fix: fix with_nth tests

* fix: opt_multi tests

* feat: all tests but issue 361 passing

* chore: generate completions & manpage

* feat: all tests passing

* feat: all tests passing

* feat: compile without cli feature & cleanup

* chore: generate completions & manpage

* feat: update deps

* fix: update examples for ratatui

- Remove tuikit example (incompatible with ratatui, examples exist in skim-tuikit)
- Fix preview_callback example: remove & from Skim::run_with call
- Add PreviewCallback to prelude exports

* fix: update tests for rand 0.9 API

- Fix rand import: use rand::distr::Alphanumeric instead of rand::distributions
- Update random string generation to use sample_iter for rand 0.9 compatibility

All 204 tests now pass (1 flaky test passed on retry)

* fix: add missing preview_fn field in non-cli Default implementation

Ensures SkimOptions compiles with --no-default-features

* chore: fmt & clippy

* feat(ci): use nextest in CI

* chore(ci): increase timeout

* chore(ci): run tests in release mode

* refactor: consolidate workspace and inline skim-common

- Remove skim-tuikit (replaced by ratatui)
- Remove async-ratatui (experimental, not needed)
- Remove skim-common and move spinlock.rs directly into skim
- Update workspace to only include skim and xtask
- Simplify project structure for ratatui-based implementation

All tests still passing (207/207)

* fix(interactive): clear old items when reloading in interactive mode

- Add clear() method to ItemList to reset items, selection, cursor, and offset
- Drain item channel before clearing to prevent stale items from appearing
- Call item_list.clear() when handling Reload event

This ensures that when the input changes in interactive mode, old items
from the previous command are fully cleared before new results appear.

* chore(tests): more robust tests

* chore: fmt & clippy

* chore: fmt

* tests: fix remaining flakies hopefully

* chore(ci): add cache to build without cli job

* test(ci): test without env vars

* chore: use dev tty for crossterm input, to fix macos e2e panicing

* feat: better perf

* chore: generate completions & manpage

* feat: performance increase & ansi handling

* fix: lint

* chore: fmt

* fix(test): flaki bind_if_non_matched

* fix: fzf-lua & perf

* chore: docs

* chore: fmt

* chore: generate completions & manpage

* chore: bring fuzzy-matcher over from skim

* feat: perf equivalent to FZF for find /

* feat: better bench

* feat: better bench

* feat: insane perf

* feat: header tabstop

* chore: remove useless TODO comments

* fix: missing TODOs and binding overrides

* chore: generate completions & manpage

* chore: fmt & clippy

* fix: skip-to-pattern and scrolls

* chore: fmt & clippy

* chore: fmt

* chore: copilot review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* chore: generate completions & manpage

* docs: better contributing guide

* chore: cleanup

* fix: output selection in order

* chore: add pre-commit hook

* docs: add git hook to contributing guide

* fix: completely clear UI before exiting inline mode (#880)

* fix(tmux): allow early exit (#878)

* chore: add receiver_multi example (#848)

* feat: add preview scrolling with mouse (#849)

* chore: remove install script (closes #607)

* docs: ansi is a no-op in lib usage (#476)

* chore: generate completions & manpage

* chore: add CommandCollector and FuzzyEngine to prelude (closes #477)

* chore: generate completions & manpage

* feat: tac & change bind

* chore: generate completions & manpage

* fix: correctly init matcher (#524)

* fix: collect all items in filter mode & apply tac (#385)

* fix: glitches when starting with \\0 (fixes #547)

* chore: add test macro

* feat: use printf for interactive mode command expansion

* chore: migrate tests to new macro syntax

* chore: migrate remaining tests & stabilize some flakies

* chore: cleanup

* fix: with-nth broken when using null delimiter

* fix: do not override keymaps if unknown or empty action

* chore: generate completions & manpage

* docs: add ratatui badge to the README

* fix(tmux): show items even if no data got sent when the popup opens

* chore: PR review part 1

* fix: unicode chars handling in input

* feat: add man page generation to main binary

* chore: generate completions & manpage

* fix: build without cli feature

* fix: do not use eyre for clap errors

* feat: collect stderr with --show-cmd-error

* chore: remove breaking changes file

---------

Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-12 23:28:41 +01:00
rusty-snake 00b93d4586
Skim was removed from Fedora 43 repositories (#872) 2025-12-10 21:12:17 +01:00
peccu 59f4f48395
[shell] make awk regex compatible with macOS default awk (#845)
Unlike GNU awk (gawk), the default awk on macOS does not support \s, making the previous expression not truly equivalent to the original perl command. This change replaces the entire first column with an empty string, achieving the same substitution effect in a portable way.

refs https://github.com/junegunn/fzf/pull/2906
2025-08-23 23:49:50 +02:00
Shaik Azhar Madar 96b25952a4
Update tuikit link to proper path (#841) 2025-08-18 01:49:25 +02:00
skim-rs-bot[bot] 06c3a13ba7
chore(release): release (#835)
* chore(release): release

* chore: generate completions & manpage

* chore: clippy with new version

* chore: fmt

---------

Co-authored-by: skim-rs-bot[bot] <190268553+skim-rs-bot[bot]@users.noreply.github.com>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
Co-authored-by: LoricAndre <loric.andre@pm.me>
2025-08-09 19:34:52 +02:00
LoricAndre cbd1765881
fix: compile without the cli feature (#834)
* fix: compile without the cli feature

* chore: clippy

* chore: fmt
2025-08-09 19:07:13 +02:00
skim-rs-bot[bot] 706620c5b7
chore(release): release (#831)
* chore(release): release

* chore: generate completions & manpage

---------

Co-authored-by: skim-rs-bot[bot] <190268553+skim-rs-bot[bot]@users.noreply.github.com>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2025-08-02 18:36:04 +02:00
LoricAndre aa4a774d68 fix(ci): fetch whole history to avoid PR recreation 2025-08-02 18:30:04 +02:00
LoricAndre 48404fe4dd chore: remove unreleased section from changelog 2025-08-02 18:25:32 +02:00
LoricAndre 9c430c1381 chore(ci): revert to a more vanilla release-plz config 2025-08-02 18:16:36 +02:00
LoricAndre 8dbfd3ffbe fix(options): allow border to be used without args 2025-08-02 18:04:35 +02:00
LoricAndre 7b0f5683df feat(e2e): add Dockerfile to run E2E 2025-08-02 18:04:35 +02:00
LoricAndre 87ac95488e fix(cli): allow value for border arg
(https://github.com/skim-rs/skim/issues/628#issuecomment-3146445573)
2025-08-02 15:26:47 +02:00
dependabot[bot] 6907d0648e
chore(deps): bump clap_mangen from 0.2.28 to 0.2.29 (#827)
* chore(deps): bump clap_mangen from 0.2.28 to 0.2.29

Bumps [clap_mangen](https://github.com/clap-rs/clap) from 0.2.28 to 0.2.29.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_mangen-v0.2.28...clap_mangen-v0.2.29)

---
updated-dependencies:
- dependency-name: clap_mangen
  dependency-version: 0.2.29
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore: generate completions & manpage

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2025-08-01 23:13:33 +02:00
skim-rs-bot[bot] 58ec498450
chore(release): release (#826)
* chore(release): release

* chore: generate completions & manpage

---------

Co-authored-by: skim-rs-bot[bot] <190268553+skim-rs-bot[bot]@users.noreply.github.com>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2025-07-27 17:06:00 +02:00
dependabot[bot] 0f8268d6c2
chore(deps): bump clap_complete_nushell from 4.5.7 to 4.5.8 (#820)
---
updated-dependencies:
- dependency-name: clap_complete_nushell
  dependency-version: 4.5.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-26 20:36:40 +02:00
dependabot[bot] d052b725b1
chore(deps): bump clap_complete from 4.5.54 to 4.5.55 (#821)
---
updated-dependencies:
- dependency-name: clap_complete
  dependency-version: 4.5.55
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2025-07-26 20:36:24 +02:00
dependabot[bot] 0a5e6bf7ee
chore(deps): bump clap from 4.5.40 to 4.5.41 (#822)
---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.41
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-26 20:34:18 +02:00
dependabot[bot] 1c2606fad8
chore(deps): bump clap_mangen from 0.2.27 to 0.2.28 (#823)
---
updated-dependencies:
- dependency-name: clap_mangen
  dependency-version: 0.2.28
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-26 20:33:57 +02:00
skim-rs-bot[bot] 992814ce50
chore(release): release (#818)
* chore(release): release

* chore: generate completions & manpage

---------

Co-authored-by: skim-rs-bot[bot] <190268553+skim-rs-bot[bot]@users.noreply.github.com>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2025-06-29 18:27:08 +02:00
Azat Khuzhin f29f6194b8
Fix parsing position cursor report escape sequences (#783)
The format should be:

    Cursor position report (CPR): Answer is ESC [ y ; x R, where x,y is the cursor location.

But if "R" will goes before ";", then it will panic:

    > thread '<unnamed>' panicked at '`at` split index (is 53) should be <= len (is 31)', library/alloc/src/vec/mod.rs:2110:13
    stack backtrace:
       0:         0x2978e6ea - std::backtrace_rs::backtrace::libunwind::trace::ha9053a9a07ca49cb
                                   at /rustc/84c898d65adf2f39a5a98507f1fe0ce10a2b8dbc/library/std/src/../../backtrace/src/backtrace/libunwind.rs:93:5
       1:         0x2978e6ea - std::backtrace_rs::backtrace::trace_unsynchronized::h9c2852a457ad564e
                                   at /rustc/84c898d65adf2f39a5a98507f1fe0ce10a2b8dbc/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5
       2:         0x2978e6ea - std::sys_common::backtrace::_print_fmt::h457936fbfaa0070f
                                   at /rustc/84c898d65adf2f39a5a98507f1fe0ce10a2b8dbc/library/std/src/sys_common/backtrace.rs:65:5
       3:         0x2978e6ea - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::h5779d7bf7f70cb0c
                                   at /rustc/84c898d65adf2f39a5a98507f1fe0ce10a2b8dbc/library/std/src/sys_common/backtrace.rs:44:22
       4:         0x297f1dae - core::fmt::write::h5a4baaff1bcd3eb5
                                   at /rustc/84c898d65adf2f39a5a98507f1fe0ce10a2b8dbc/library/core/src/fmt/mod.rs:1232:17
       5:         0x29781695 - std::io::Write::write_fmt::h4bc1f301cb9e9cce
                                   at /rustc/84c898d65adf2f39a5a98507f1fe0ce10a2b8dbc/library/std/src/io/mod.rs:1684:15
       6:         0x2978e4b5 - std::sys_common::backtrace::_print::h5fcdc36060f177e8
                                   at /rustc/84c898d65adf2f39a5a98507f1fe0ce10a2b8dbc/library/std/src/sys_common/backtrace.rs:47:5
       7:         0x2978e4b5 - std::sys_common::backtrace::print::h54ca9458b876c8bf
                                   at /rustc/84c898d65adf2f39a5a98507f1fe0ce10a2b8dbc/library/std/src/sys_common/backtrace.rs:34:9
       8:         0x2979122f - std::panicking::default_hook::{{closure}}::hbe471161c7664ed6
                                   at /rustc/84c898d65adf2f39a5a98507f1fe0ce10a2b8dbc/library/std/src/panicking.rs:271:22
       9:         0x29790f6b - std::panicking::default_hook::ha3500da57aa4ac4f
                                   at /rustc/84c898d65adf2f39a5a98507f1fe0ce10a2b8dbc/library/std/src/panicking.rs:290:9
      10:         0x297918e8 - std::panicking::rust_panic_with_hook::h50c09d000dc561d2
                                   at /rustc/84c898d65adf2f39a5a98507f1fe0ce10a2b8dbc/library/std/src/panicking.rs:692:13
      11:         0x297917e9 - std::panicking::begin_panic_handler::{{closure}}::h9e2b2176e00e0d9c
                                   at /rustc/84c898d65adf2f39a5a98507f1fe0ce10a2b8dbc/library/std/src/panicking.rs:583:13
      12:         0x2978eb56 - std::sys_common::backtrace::__rust_end_short_backtrace::h5739b8e512c09d02
                                   at /rustc/84c898d65adf2f39a5a98507f1fe0ce10a2b8dbc/library/std/src/sys_common/backtrace.rs:150:18
      13:         0x297914f2 - rust_begin_unwind
                                   at /rustc/84c898d65adf2f39a5a98507f1fe0ce10a2b8dbc/library/std/src/panicking.rs:579:5
      14:         0x297ee103 - core::panicking::panic_fmt::hf33a1475b4dc5c3e
                                   at /rustc/84c898d65adf2f39a5a98507f1fe0ce10a2b8dbc/library/core/src/panicking.rs:64:14
      15:         0x297e26e6 - alloc::vec::Vec<T,A>::split_off::assert_failed::h5d4cf9fcf561634c
                                   at /rustc/84c898d65adf2f39a5a98507f1fe0ce10a2b8dbc/library/alloc/src/vec/mod.rs:2110:13
      16:         0x301f7415 - alloc::vec::Vec<T,A>::split_off::ha953b379fad89c1d
                                   at /rustc/84c898d65adf2f39a5a98507f1fe0ce10a2b8dbc/library/alloc/src/vec/mod.rs:2114:13
      17:         0x302039ee - tuikit::input::KeyBoard::parse_cursor_report::h8a29b19f3df8e0ff
2025-06-29 18:20:09 +02:00
LoricAndre 913deaf678
docs(e2e): add contributing section (#817)
Co-authored-by: LoricAndre <loric.andre@pm.me>
2025-06-22 21:32:22 +02:00
skim-rs-bot[bot] 1c1dffb734
chore(release): release (#815)
* chore(release): release

* chore: generate completions & manpage

---------

Co-authored-by: skim-rs-bot[bot] <190268553+skim-rs-bot[bot]@users.noreply.github.com>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2025-06-22 01:34:00 +02:00
LoricAndre 727be81929
fix: min-query-length in interactive mode (#814)
* Fix min-query-length in interactive mode

The min-query-length option wasn't working correctly in interactive mode
when using commands. This commit fixes the issue by checking both the
query and cmd_query depending on the current mode, ensuring min-query-length
is respected consistently in both interactive and normal modes.

* chore: fmt

* test: add e2e

* chore: fmt

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>
2025-06-22 01:27:19 +02:00
skim-rs-bot[bot] 3f3f2da83c
chore(release): release (#813)
* chore(release): release

* chore: generate completions & manpage

---------

Co-authored-by: skim-rs-bot[bot] <190268553+skim-rs-bot[bot]@users.noreply.github.com>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2025-06-22 01:13:05 +02:00
Mark Zaidelman 0f4b13e8c4
feat(ui): respect NO_COLOR environment variable (#804)
* feat(ui) Respect NO_COLOR environment variable

* Add NO_COLOR to man and readme

* chore: generate completions & manpage

* Really update manpage this time

* Add `enpty` color scheme to readme

* Rename "empty" color theme to "none"

This seems a better wording for what this theme is. However, empty is
still supported for backwards compatibility (though not mentioned in
readme and manpage, but it wasn't mentioned before anyway)
Since `ColorTheme::empty` is private, I felt it's okay to rename too to
follow suite.

* Add details to related readme paragraph

---------

Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2025-06-22 01:04:52 +02:00
skim-rs-bot[bot] e73e7f841b
chore(release): release (#811)
* chore(release): release

* chore: generate completions & manpage

---------

Co-authored-by: skim-rs-bot[bot] <190268553+skim-rs-bot[bot]@users.noreply.github.com>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2025-06-22 00:57:13 +02:00
dependabot[bot] 745f26b931
chore(deps): bump clap from 4.5.39 to 4.5.40 (#798)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.39 to 4.5.40.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.39...clap_complete-v4.5.40)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.40
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-22 00:27:18 +02:00
LoricAndre 71b82d0f58
feat: add min query length option (#806)
* feat: add min query length option

* chore: generate completions & manpage

* chore: fmt

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2025-06-22 00:25:55 +02:00
dependabot[bot] e66813518d
chore(deps): bump clap_complete_nushell from 4.5.6 to 4.5.7 (#797)
Bumps [clap_complete_nushell](https://github.com/clap-rs/clap) from 4.5.6 to 4.5.7.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete_nushell-v4.5.6...clap_complete_nushell-v4.5.7)

---
updated-dependencies:
- dependency-name: clap_complete_nushell
  dependency-version: 4.5.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-22 00:19:52 +02:00
dependabot[bot] ca229dba01
chore(deps): bump clap_complete from 4.5.52 to 4.5.54 (#799)
Bumps [clap_complete](https://github.com/clap-rs/clap) from 4.5.52 to 4.5.54.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.52...clap_complete-v4.5.54)

---
updated-dependencies:
- dependency-name: clap_complete
  dependency-version: 4.5.54
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-22 00:19:43 +02:00
dependabot[bot] 4f63ec5543
chore(deps): bump unicode-width from 0.2.0 to 0.2.1 (#800)
Bumps [unicode-width](https://github.com/unicode-rs/unicode-width) from 0.2.0 to 0.2.1.
- [Commits](https://github.com/unicode-rs/unicode-width/compare/v0.2.0...v0.2.1)

---
updated-dependencies:
- dependency-name: unicode-width
  dependency-version: 0.2.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-22 00:19:32 +02:00
dependabot[bot] cd929542a2
chore(deps): bump clap_mangen from 0.2.26 to 0.2.27 (#801)
Bumps [clap_mangen](https://github.com/clap-rs/clap) from 0.2.26 to 0.2.27.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_mangen-v0.2.26...clap_mangen-v0.2.27)

---
updated-dependencies:
- dependency-name: clap_mangen
  dependency-version: 0.2.27
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-22 00:15:34 +02:00
dependabot[bot] cdc3286b85
chore(deps): bump clap_complete from 4.5.51 to 4.5.52 (#794)
Bumps [clap_complete](https://github.com/clap-rs/clap) from 4.5.51 to 4.5.52.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.51...clap_complete-v4.5.52)

---
updated-dependencies:
- dependency-name: clap_complete
  dependency-version: 4.5.52
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-03 11:21:44 +02:00
skim-rs-bot[bot] f68bcb36f1
chore(release): release (#792)
* chore(release): release

* chore: generate completions & manpage

* Update CHANGELOG.md

---------

Co-authored-by: skim-rs-bot[bot] <190268553+skim-rs-bot[bot]@users.noreply.github.com>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2025-05-30 14:54:12 +02:00
LoricAndre 6b555b0d27 chore: generate changelog 2025-05-30 14:16:03 +02:00
LoricAndre 880b77fd40 fix(ci): remove version from pr name 2025-05-30 14:15:22 +02:00
LoricAndre 90d9355dca feat(shell): Improve shell completion with dynamic generation (#790)
* chore: add aider files to gitignore

* feat: add runtime shell completion generation with --shell flag

* chore: update shell completion to output directly to stdout

* feat: add shell completion generation support using clap_complete

* feat: improve shell completion with dynamic generation and better docs

* chore: update Cargo.lock

* chore: generate completions & manpage

* refactor: simplify shell completion code and improve imports

* Use Shell enum instead of strings for shell option

This change:
- Replaces Option<String> with Option<clap_complete::Shell> for the
shell option
- Removes string-to-enum conversion in main.rs
- Adds a note that while PowerShell completions are supported, Windows
is not supported for now
- Improves the documentation formatting

* chore: generate completions & manpage

* revert gitignore

* Fix formatting issues

* Reorder shell option and remove redundant comment

- Move the shell option out of the 'reserved for later use' section into
the Scripting section
- Remove comment about using the enum directly
- Fix duplicate comment line

* chore: generate completions & manpage

* Update README.md with shell completion details

- Explicitly list supported shells in the shell completions section
- Add note about Windows not being supported

* reword readme

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2025-05-30 14:15:22 +02:00
LoricAndre 2931b295b8
docs: improve wording in README and options.rs (#789)
* docs: improve wording in README and options.rs

- Make the tone more friendly and conversational

- Fix grammar and spelling issues

- Improve clarity in several sections

- Fix the spelling of 'literally' and other minor typos

* chore: generate completions & manpage

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2025-05-29 16:21:45 +02:00
LoricAndre 7a1744217b
docs: improve theming documentation (#788)
* docs: improve theming documentation

Add comprehensive documentation about skim's theming capabilities in both README and man page.

This includes information about base themes, customizable UI elements, color specification methods, and examples of theme customization.

Fixes #663

* style: fix formatting in options.rs

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>
2025-05-29 16:04:00 +02:00
LoricAndre 9cfbc0a470 docs(contributing): refine guidelines for GPT-assisted development
- Clarified the acceptance of GPT-assisted development with stricter evaluation criteria.
- Updated phrasing to discourage extensive refactoring due to review overhead.
- Improved wording for chatbot-related restrictions in PRs and issues.
2025-05-29 00:01:07 +02:00
skim-rs-bot[bot] 6ffe7c55a4
chore(release): release v0.17.3 (#782)
* chore(release): release v0.17.3

* chore: generate completions & manpage

* Update CHANGELOG.md

---------

Co-authored-by: skim-rs-bot[bot] <190268553+skim-rs-bot[bot]@users.noreply.github.com>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2025-05-20 22:39:57 +02:00
LoricAndre d36b67fb43 chore: generate changelog using git cliff 2025-05-20 22:35:33 +02:00
dependabot[bot] f5ba2f48cc
chore(deps): bump tempfile from 3.19.1 to 3.20.0 (#770)
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.19.1 to 3.20.0.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.19.1...v3.20.0)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-version: 3.20.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2025-05-20 22:30:52 +02:00
dependabot[bot] 8573f36254
chore(deps): bump clap_complete from 4.5.49 to 4.5.50 (#772)
* chore(deps): bump clap_complete from 4.5.49 to 4.5.50

Bumps [clap_complete](https://github.com/clap-rs/clap) from 4.5.49 to 4.5.50.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.49...clap_complete-v4.5.50)

---
updated-dependencies:
- dependency-name: clap_complete
  dependency-version: 4.5.50
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore: generate completions & manpage

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2025-05-20 22:16:50 +02:00
dependabot[bot] 97f09721c2
chore(deps): bump clap from 4.5.37 to 4.5.38 (#771)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.37 to 4.5.38.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.37...clap_complete-v4.5.38)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.38
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 22:15:45 +02:00
LoricAndre 8df581b122
fix(shell): remove duplocate tmux height arg fixes #776 (#778)
Co-authored-by: LoricAndre <loric.andre@pm.me>
2025-05-20 22:15:12 +02:00
LoricAndre b10acf48eb
fix(shell): fix zsh tmux args in key bindings (#777)
Co-authored-by: LoricAndre <loric.andre@pm.me>
2025-05-20 22:08:45 +02:00
Ajeet D'Souza cc03874b8f
bash: set keybinding right before printing special character (#774) 2025-05-17 22:29:04 +02:00
dependabot[bot] 991fbee680
chore(deps): bump clap_complete from 4.5.48 to 4.5.49 (#767)
* chore(deps): bump clap_complete from 4.5.48 to 4.5.49

Bumps [clap_complete](https://github.com/clap-rs/clap) from 4.5.48 to 4.5.49.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.48...clap_complete-v4.5.49)

---
updated-dependencies:
- dependency-name: clap_complete
  dependency-version: 4.5.49
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore: generate completions & manpage

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2025-05-09 14:41:11 +02:00
skim-rs-bot[bot] a3e88a9f65
chore(release): release v0.17.2 (#766)
* chore(release): release v0.17.2

* chore: generate completions & manpage

---------

Co-authored-by: skim-rs-bot[bot] <190268553+skim-rs-bot[bot]@users.noreply.github.com>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2025-05-04 22:35:50 +02:00
LoricAndre e0d01690d3 fix(ci): remove release commits filter 2025-05-04 22:29:35 +02:00
LoricAndre ed2b3fa811
fix(tmux): force sh as shell for tmux mode (#765)
* fix(tmux): force sh as shell for tmux mode

* chore: generate completions & manpage

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2025-05-04 22:26:35 +02:00
LoricAndre 4be4e5c266 chore(ci): remove temp workflow 2025-05-04 20:18:30 +02:00
LoricAndre df3480bcc8
create temp workflow 2025-05-04 20:15:48 +02:00
LoricAndre 5bba84f2cc feat(ci): manually update versions 2025-05-04 20:04:10 +02:00
LoricAndre 689157af20 chore(ci): update dependencies 2025-05-04 19:59:19 +02:00
LoricAndre 957c435709 fix(ci): fix broken ci after migration 2025-05-04 19:54:05 +02:00
LoricAndre c70ed2042f fix(cargo): fix tuikit readme path 2025-05-04 19:52:20 +02:00
LoricAndre 0a60090a6b
chore(cargo): update to 2024 edition (#764)
* chore(cargo): update to 2024 edition

* chore: fmt

* chore(tuikit): update README

* fix(tuikit): fix imports

* feat(ci): run all tests during pr

* fix(ci): exclude e2e from unit tests

* fix(tests): ignore error on stdout locking failure

* fix(tests): fix syntax

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>
2025-05-04 19:44:06 +02:00
LoricAndre d767bbe70c Revert "fix(ci): more generic pr name"
This reverts commit ef4bae6b29.
2025-05-04 19:06:29 +02:00
LoricAndre 2bab7d7e31 Revert "fix(ci): split release pr and gh release"
This reverts commit 0a822204ca.
2025-05-04 19:06:27 +02:00
LoricAndre 7980972d1d Revert "chore: release (#760)"
This reverts commit 8a29f270a2.
2025-05-04 19:06:25 +02:00
LoricAndre b83e0311ec Revert "test(ci): show context"
This reverts commit df455aa614.
2025-05-04 19:06:21 +02:00
LoricAndre e2eacdf12b Revert "test(ci): test trigger (#761)"
This reverts commit 074f4c1de7.
2025-05-04 19:06:20 +02:00
LoricAndre 103e90fdfe Revert "Test/ci triggers (#762)"
This reverts commit 8e686f9db4.
2025-05-04 19:06:18 +02:00
LoricAndre 708f9db876 Revert "Test/ci triggers (#763)"
This reverts commit 39410102a7.
2025-05-04 19:06:17 +02:00
LoricAndre 39410102a7
Test/ci triggers (#763)
* test(ci): test trigger

* fix(ci): add runs-on

* fix(ci): fix release condition

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>
2025-05-04 19:04:59 +02:00
LoricAndre 8e686f9db4
Test/ci triggers (#762)
* test(ci): test trigger

* fix(ci): add runs-on

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>
2025-05-04 18:59:26 +02:00
LoricAndre 074f4c1de7
test(ci): test trigger (#761)
Co-authored-by: LoricAndre <loric.andre@pm.me>
2025-05-04 18:58:00 +02:00
LoricAndre df455aa614 test(ci): show context 2025-05-04 18:54:55 +02:00
skim-rs-bot[bot] 8a29f270a2
chore: release (#760)
* chore: release

* chore: generate completions & manpage

---------

Co-authored-by: skim-rs-bot[bot] <190268553+skim-rs-bot[bot]@users.noreply.github.com>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2025-05-04 18:49:00 +02:00
LoricAndre 0a822204ca fix(ci): split release pr and gh release 2025-05-04 18:40:01 +02:00
LoricAndre ef4bae6b29 fix(ci): more generic pr name 2025-05-04 18:25:10 +02:00
LoricAndre 14241b3bb8 fix(cargo): fix tuikit re-export 2025-05-04 18:19:32 +02:00
LoricAndre 82f7807eaa chore(ci): only release after merge 2025-05-04 18:16:40 +02:00
LoricAndre f5f87e41b1 fix: fix local dependencies 2025-05-04 18:13:36 +02:00
LoricAndre a8dcc7ec4c fix(ci): add metadata to common package 2025-05-04 18:08:16 +02:00
LoricAndre 4ed7f0cbed fix(ci): remove leftover package 2025-05-04 18:04:46 +02:00
LoricAndre 8df7893f76 fix(cargo): fix workspace packages 2025-05-04 18:03:32 +02:00
LoricAndre 0df30b83fe chore(ci): make local packages publishable 2025-05-04 18:02:09 +02:00
LoricAndre 9c96d48e55 chore(ci): release on all commits 2025-05-04 17:54:34 +02:00
LoricAndre a7e5803c80 chore(ci): do not publish extra packages 2025-05-04 17:54:01 +02:00
LoricAndre a4b2ef550c
chore: release (#756)
* chore(ci): split release actions

* chore: release

* chore: generate completions & manpage

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2025-05-04 17:51:16 +02:00
LoricAndre 8c3dd32834 chore(ci): split release actions 2025-05-04 17:46:05 +02:00
LoricAndre 08f3d12ab5 chore(ci): update release-plz changelog format 2025-05-04 17:46:05 +02:00
LoricAndre 9bc221b14c chore(ci): update release-plz changelog format 2025-05-04 17:33:47 +02:00
LoricAndre adc4298f94 chore(ci): migrate to release-plz 2025-05-04 17:28:06 +02:00
LoricAndre 60b8ad1491 chore(ci): use previous versions for packages 2025-05-04 14:11:41 +02:00
LoricAndre 6a42c359e8 chore(ci): explicitely set last release sha 2025-05-04 14:08:25 +02:00
LoricAndre ac0ff31ba8 chore(ci): explicitely set root component 2025-05-04 14:03:44 +02:00
LoricAndre 1b008f5b7f chore(ci): set package names 2025-05-04 13:15:57 +02:00
LoricAndre 111039eefb chore(ci): unlink versions 2025-05-04 13:13:20 +02:00
LoricAndre 76040003ca chore(ci): better handling of packages in release 2025-05-04 13:05:43 +02:00
LoricAndre 661ba9f28a chore(ci): readd all packages as well as root 2025-05-04 12:59:58 +02:00
LoricAndre 7d9d682270 chore(ci): update manifest 2025-05-04 12:54:32 +02:00
LoricAndre c5395bfc88 chore(ci): release whole workspace at once 2025-05-04 12:52:42 +02:00
LoricAndre 19ea887465 chore(ci): release every package at the same version 2025-05-04 12:46:18 +02:00
LoricAndre d15b328146 chore(ci): readd all components 2025-05-04 12:41:39 +02:00
LoricAndre 87d2ce4ba8 chore(ci): test without extra packages 2025-05-04 12:34:23 +02:00
LoricAndre 99134b9af9 chore(ci): disable skim prefix in tag 2025-05-04 12:31:09 +02:00
LoricAndre 4ba771cf99 chore(ci): use linked changelog 2025-05-04 12:29:42 +02:00
LoricAndre 3bd7f3c774 chore(ci): revert action 2025-05-04 12:28:22 +02:00
LoricAndre aad912519e chore(ci): readd manifest manually 2025-05-04 12:27:08 +02:00
LoricAndre bda413b83e chore(ci): back to manifest release 2025-05-04 12:23:14 +02:00
dependabot[bot] 177fed3622
chore(deps): bump clap_complete from 4.5.47 to 4.5.48 (#747)
Bumps [clap_complete](https://github.com/clap-rs/clap) from 4.5.47 to 4.5.48.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.47...clap_complete-v4.5.48)

---
updated-dependencies:
- dependency-name: clap_complete
  dependency-version: 4.5.48
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-30 09:13:03 +02:00
LoricAndre f87ff6740b
feat(shell): readd completions (#726) (#739)
* feat(shell): readd completions (#726)

* chore: generate completions & manpage

* chore(shell): force reset colors & cursor

* docs(shell): add docs for shell modules (closes #315)

* chore: generate completions & manpage

* docs(shell): add docs on how to enable (closes #292)

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2025-04-27 23:12:13 +02:00
LoricAndre 1952435b9b
Update issue templates 2025-04-27 13:47:01 +02:00
LoricAndre 0a206adf23
chore(tuikit): bring skim-rs/tuikit#43 (#743)
Co-authored-by: LoricAndre <loric.andre@pm.me>
2025-04-26 22:08:43 +02:00
LoricAndre 73fd406827
feat(tui): add tuikit as workspace member and update (#741)
* feat(tui): add tuikit as workspace member and update

* chore: fmt

* chore: clippy

* chore: clippy & common spinlock

* chore: clippy on e2e

* docs(tuikit): add tuikit docs & example in skim crate

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>
2025-04-26 21:22:32 +02:00
skim-rs-bot[bot] e93e65c352
chore(master): release 0.16.2 (#738)
* chore(master): release 0.16.2

* chore: generate completions & manpage

* chore: generate completions & manpage

---------

Co-authored-by: skim-rs-bot[bot] <190268553+skim-rs-bot[bot]@users.noreply.github.com>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2025-04-26 20:03:50 +02:00
LoricAndre ba908a8243
chore: move changelog to subdir (#740)
Co-authored-by: LoricAndre <loric.andre@pm.me>
2025-04-26 19:57:51 +02:00
LoricAndre a51e21c34c docs(tmux): fix docs formatting 2025-04-26 19:53:03 +02:00
LoricAndre 9d55c89d10
Update issue templates 2025-04-26 19:30:16 +02:00
LoricAndre ed1c48d6d0
fix(filter): fix broken pipe while writing results to locked stdout (closes #733) (#737)
Co-authored-by: LoricAndre <loric.andre@pm.me>
2025-04-26 12:34:42 +02:00
LoricAndre a9ba87b892
fix(tmux): check if TMUX is set (closes #734) (#736)
Co-authored-by: LoricAndre <loric.andre@pm.me>
2025-04-26 12:34:23 +02:00
LoricAndre 9aa27a8f25 test(ci): try a simpler release-please config 2025-04-26 12:34:04 +02:00
LoricAndre e99de62297 docs(tmux): add note about env var (#732) 2025-04-26 12:15:21 +02:00
dependabot[bot] 4f9de7a115
chore(deps): bump clap from 4.5.32 to 4.5.37 (#735)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.32 to 4.5.37.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.32...clap_complete-v4.5.37)

---
updated-dependencies:
- dependency-name: clap
  dependency-version: 4.5.37
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-25 23:35:58 +02:00
dependabot[bot] dc45aef3e2
chore(deps): bump crossbeam-channel from 0.5.13 to 0.5.15 (#730)
Bumps [crossbeam-channel](https://github.com/crossbeam-rs/crossbeam) from 0.5.13 to 0.5.15.
- [Release notes](https://github.com/crossbeam-rs/crossbeam/releases)
- [Changelog](https://github.com/crossbeam-rs/crossbeam/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crossbeam-rs/crossbeam/compare/crossbeam-channel-0.5.13...crossbeam-channel-0.5.15)

---
updated-dependencies:
- dependency-name: crossbeam-channel
  dependency-version: 0.5.15
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-25 23:34:12 +02:00
dependabot[bot] 1e9e019885
chore(deps): bump tempfile from 3.19.0 to 3.19.1 (#725)
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.19.0 to 3.19.1.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.19.0...v3.19.1)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-25 23:33:17 +02:00
dependabot[bot] c7035857b9
chore(deps): bump time from 0.3.39 to 0.3.41 (#724)
Bumps [time](https://github.com/time-rs/time) from 0.3.39 to 0.3.41.
- [Release notes](https://github.com/time-rs/time/releases)
- [Changelog](https://github.com/time-rs/time/blob/main/CHANGELOG.md)
- [Commits](https://github.com/time-rs/time/compare/v0.3.39...v0.3.41)

---
updated-dependencies:
- dependency-name: time
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-25 23:32:57 +02:00
dependabot[bot] faeb2b4508
chore(deps): bump clap_complete from 4.5.46 to 4.5.47 (#722)
Bumps [clap_complete](https://github.com/clap-rs/clap) from 4.5.46 to 4.5.47.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.46...clap_complete-v4.5.47)

---
updated-dependencies:
- dependency-name: clap_complete
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-25 23:32:30 +02:00
dependabot[bot] a3df0be787
chore(deps): bump log from 0.4.26 to 0.4.27 (#723)
Bumps [log](https://github.com/rust-lang/log) from 0.4.26 to 0.4.27.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.26...0.4.27)

---
updated-dependencies:
- dependency-name: log
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-25 23:31:29 +02:00
LoricAndre d134e8d31a feat!(zsh): sort history items by timestamp 2025-04-25 20:12:06 +02:00
dependabot[bot] 284725d651
chore(deps): bump tempfile from 3.18.0 to 3.19.0 (#720)
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.18.0 to 3.19.0.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.18.0...v3.19.0)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-22 10:58:41 +01:00
LoricAndre fb896690b0
Update lib.rs
Some checks failed
Build & Test / unittests (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / unittests (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
release-please / release-please (push) Has been cancelled
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / clippy (push) Has been cancelled
Build & Test / rustfmt (push) Has been cancelled
release-please / Build release binaries (aarch64, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (arm, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (armv7, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (x86_64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (x86_64, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (aarch64, apple-darwin) (push) Has been cancelled
release-please / setup-release (push) Has been cancelled
release-please / publish-crate (push) Has been cancelled
2025-03-12 11:37:46 +01:00
dependabot[bot] 9a4b794122
chore(deps): bump clap from 4.5.31 to 4.5.32 (#717)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.31 to 4.5.32.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.5.31...clap_complete-v4.5.32)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-12 11:35:56 +01:00
dependabot[bot] 8e2cf254dc
chore(deps): bump indexmap from 2.7.1 to 2.8.0 (#716)
Bumps [indexmap](https://github.com/indexmap-rs/indexmap) from 2.7.1 to 2.8.0.
- [Changelog](https://github.com/indexmap-rs/indexmap/blob/main/RELEASES.md)
- [Commits](https://github.com/indexmap-rs/indexmap/compare/2.7.1...2.8.0)

---
updated-dependencies:
- dependency-name: indexmap
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-12 10:28:54 +01:00
dependabot[bot] fac63d5cb1
chore(deps): bump tempfile from 3.17.1 to 3.18.0 (#715)
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.17.1 to 3.18.0.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.17.1...v3.18.0)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-12 10:04:23 +01:00
dependabot[bot] cdde8a9032
chore(deps): bump time from 0.3.36 to 0.3.39 (#714)
Bumps [time](https://github.com/time-rs/time) from 0.3.36 to 0.3.39.
- [Release notes](https://github.com/time-rs/time/releases)
- [Changelog](https://github.com/time-rs/time/blob/main/CHANGELOG.md)
- [Commits](https://github.com/time-rs/time/compare/v0.3.36...v0.3.39)

---
updated-dependencies:
- dependency-name: time
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-12 10:03:56 +01:00
skim-rs-bot[bot] 81799d1f65
chore(master): release 0.16.1 (#712)
Some checks failed
release-please / release-please (push) Has been cancelled
Build & Test / unittests (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / unittests (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / clippy (push) Has been cancelled
Build & Test / rustfmt (push) Has been cancelled
release-please / setup-release (push) Has been cancelled
release-please / publish-crate (push) Has been cancelled
release-please / Build release binaries (aarch64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (aarch64, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (arm, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (armv7, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (x86_64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (x86_64, unknown-linux-musl) (push) Has been cancelled
* chore(master): release 0.16.1

* chore: generate completions & manpage

---------

Co-authored-by: skim-rs-bot[bot] <190268553+skim-rs-bot[bot]@users.noreply.github.com>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2025-03-06 19:05:04 +01:00
LoricAndre 47c5e1e54e fix: hasten deprecation of expect after #703
Some checks are pending
release-please / release-please (push) Waiting to run
release-please / setup-release (push) Blocked by required conditions
release-please / publish-crate (push) Blocked by required conditions
release-please / Build release binaries (aarch64, apple-darwin) (push) Blocked by required conditions
release-please / Build release binaries (aarch64, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (arm, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (armv7, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (x86_64, apple-darwin) (push) Blocked by required conditions
release-please / Build release binaries (x86_64, unknown-linux-musl) (push) Blocked by required conditions
Build & Test / unittests (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / unittests (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / clippy (push) Waiting to run
Build & Test / rustfmt (push) Waiting to run
2025-03-05 23:29:59 +01:00
dependabot[bot] 301a0dabeb
chore(deps): bump clap from 4.5.30 to 4.5.31 (#708)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.30 to 4.5.31.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.30...v4.5.31)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-05 23:29:56 +01:00
dependabot[bot] afc2abdbda
chore(deps): bump log from 0.4.25 to 0.4.26 (#706)
Bumps [log](https://github.com/rust-lang/log) from 0.4.25 to 0.4.26.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.25...0.4.26)

---
updated-dependencies:
- dependency-name: log
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-05 23:29:34 +01:00
dependabot[bot] 6457a2700c
chore(deps): bump clap_complete from 4.5.45 to 4.5.46 (#707)
Bumps [clap_complete](https://github.com/clap-rs/clap) from 4.5.45 to 4.5.46.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.45...clap_complete-v4.5.46)

---
updated-dependencies:
- dependency-name: clap_complete
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-05 23:23:53 +01:00
dependabot[bot] a88d9ae10c
chore(deps): bump chrono from 0.4.39 to 0.4.40 (#709)
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.39 to 0.4.40.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.39...v0.4.40)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-05 23:23:11 +01:00
dependabot[bot] 9f02f95fbe
chore(deps): bump clap from 4.5.27 to 4.5.30 (#700)
Some checks failed
release-please / release-please (push) Has been cancelled
Build & Test / unittests (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / unittests (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / clippy (push) Has been cancelled
Build & Test / rustfmt (push) Has been cancelled
release-please / setup-release (push) Has been cancelled
release-please / publish-crate (push) Has been cancelled
release-please / Build release binaries (aarch64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (aarch64, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (arm, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (armv7, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (x86_64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (x86_64, unknown-linux-musl) (push) Has been cancelled
* chore(deps): bump clap from 4.5.27 to 4.5.30

Bumps [clap](https://github.com/clap-rs/clap) from 4.5.27 to 4.5.30.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.27...clap_complete-v4.5.30)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore: migrate to clap markdown

* chore: fmt

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: LoricAndre <loric.andre@pm.me>
Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2025-02-20 22:17:53 +01:00
dependabot[bot] b696fbbc1b
chore(deps): bump tempfile from 3.16.0 to 3.17.1 (#701)
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.16.0 to 3.17.1.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.16.0...v3.17.1)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-20 22:16:45 +01:00
dependabot[bot] d7f07acafe
chore(deps): bump clap_complete from 4.5.43 to 4.5.45 (#702)
Bumps [clap_complete](https://github.com/clap-rs/clap) from 4.5.43 to 4.5.45.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.43...clap_complete-v4.5.45)

---
updated-dependencies:
- dependency-name: clap_complete
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-20 22:15:17 +01:00
dependabot[bot] 08e025bd00
chore(deps): bump vte from 0.14.1 to 0.15.0 (#696)
Bumps [vte](https://github.com/alacritty/vte) from 0.14.1 to 0.15.0.
- [Release notes](https://github.com/alacritty/vte/releases)
- [Changelog](https://github.com/alacritty/vte/blob/master/CHANGELOG.md)
- [Commits](https://github.com/alacritty/vte/compare/v0.14.1...v0.15.0)

---
updated-dependencies:
- dependency-name: vte
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-20 22:11:19 +01:00
dependabot[bot] c3e8d8cc9e
chore(deps): bump which from 7.0.1 to 7.0.2 (#699)
Some checks failed
release-please / release-please (push) Has been cancelled
Build & Test / unittests (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / unittests (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / clippy (push) Has been cancelled
Build & Test / rustfmt (push) Has been cancelled
release-please / setup-release (push) Has been cancelled
release-please / publish-crate (push) Has been cancelled
release-please / Build release binaries (aarch64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (aarch64, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (arm, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (armv7, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (x86_64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (x86_64, unknown-linux-musl) (push) Has been cancelled
Bumps [which](https://github.com/harryfei/which-rs) from 7.0.1 to 7.0.2.
- [Release notes](https://github.com/harryfei/which-rs/releases)
- [Changelog](https://github.com/harryfei/which-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/harryfei/which-rs/compare/7.0.1...7.0.2)

---
updated-dependencies:
- dependency-name: which
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-11 10:07:26 +01:00
dependabot[bot] 25cd4cdd03
chore(deps): bump tempfile from 3.15.0 to 3.16.0 (#695)
Some checks failed
Build & Test / clippy (push) Has been cancelled
Build & Test / rustfmt (push) Has been cancelled
release-please / release-please (push) Has been cancelled
Build & Test / unittests (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / unittests (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
release-please / setup-release (push) Has been cancelled
release-please / publish-crate (push) Has been cancelled
release-please / Build release binaries (aarch64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (aarch64, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (arm, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (armv7, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (x86_64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (x86_64, unknown-linux-musl) (push) Has been cancelled
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.15.0 to 3.16.0.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.15.0...v3.16.0)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-04 08:49:19 +01:00
dependabot[bot] 5698c6d8fd
chore(deps): bump rand from 0.8.5 to 0.9.0 (#694)
Some checks failed
release-please / release-please (push) Has been cancelled
Build & Test / unittests (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / unittests (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / clippy (push) Has been cancelled
Build & Test / rustfmt (push) Has been cancelled
release-please / setup-release (push) Has been cancelled
release-please / publish-crate (push) Has been cancelled
release-please / Build release binaries (aarch64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (aarch64, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (arm, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (armv7, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (x86_64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (x86_64, unknown-linux-musl) (push) Has been cancelled
* chore(deps): bump rand from 0.8.5 to 0.9.0

Bumps [rand](https://github.com/rust-random/rand) from 0.8.5 to 0.9.0.
- [Release notes](https://github.com/rust-random/rand/releases)
- [Changelog](https://github.com/rust-random/rand/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-random/rand/compare/0.8.5...0.9.0)

---
updated-dependencies:
- dependency-name: rand
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix: fix breaking changes in rand

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: LoricAndre <loric.andre@pm.me>
2025-01-29 14:45:43 +01:00
dependabot[bot] 4102e5aaa1
chore(deps): bump clap_complete from 4.5.42 to 4.5.43 (#693)
Some checks failed
release-please / release-please (push) Has been cancelled
Build & Test / clippy (push) Has been cancelled
Build & Test / rustfmt (push) Has been cancelled
Build & Test / unittests (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / unittests (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
release-please / setup-release (push) Has been cancelled
release-please / publish-crate (push) Has been cancelled
release-please / Build release binaries (aarch64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (aarch64, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (arm, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (armv7, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (x86_64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (x86_64, unknown-linux-musl) (push) Has been cancelled
Bumps [clap_complete](https://github.com/clap-rs/clap) from 4.5.42 to 4.5.43.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.42...clap_complete-v4.5.43)

---
updated-dependencies:
- dependency-name: clap_complete
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-28 11:18:12 +01:00
LoricAndre b4138a5029
chore: manually update release-please manifest after release
Some checks failed
Build & Test / unittests (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / unittests (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / clippy (push) Has been cancelled
Build & Test / rustfmt (push) Has been cancelled
release-please / release-please (push) Has been cancelled
release-please / Build release binaries (arm, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (armv7, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (x86_64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (x86_64, unknown-linux-musl) (push) Has been cancelled
release-please / setup-release (push) Has been cancelled
release-please / publish-crate (push) Has been cancelled
release-please / Build release binaries (aarch64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (aarch64, unknown-linux-musl) (push) Has been cancelled
2025-01-23 20:40:53 +01:00
skim-rs-bot[bot] 1dcdf06b5c
chore: release master (#691)
Some checks are pending
release-please / release-please (push) Waiting to run
release-please / setup-release (push) Blocked by required conditions
release-please / publish-crate (push) Blocked by required conditions
release-please / Build release binaries (aarch64, apple-darwin) (push) Blocked by required conditions
release-please / Build release binaries (aarch64, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (arm, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (armv7, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (x86_64, apple-darwin) (push) Blocked by required conditions
release-please / Build release binaries (x86_64, unknown-linux-musl) (push) Blocked by required conditions
Build & Test / unittests (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / unittests (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / clippy (push) Waiting to run
Build & Test / rustfmt (push) Waiting to run
* chore: release master

* chore: generate completions & manpage

---------

Co-authored-by: skim-rs-bot[bot] <190268553+skim-rs-bot[bot]@users.noreply.github.com>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2025-01-23 17:13:09 +01:00
alexxbb 226d9951fa
feat: add preview callback (#407)
* Draft

* Cleanup

* fix: correctly set index when passing items directly through the lib

* chore: cleanup before merge

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>
2025-01-23 15:07:39 +01:00
LoricAndre 5152be1945
fix(term): clamp height option (#690)
Some checks are pending
release-please / release-please (push) Waiting to run
release-please / setup-release (push) Blocked by required conditions
release-please / publish-crate (push) Blocked by required conditions
release-please / Build release binaries (aarch64, apple-darwin) (push) Blocked by required conditions
release-please / Build release binaries (aarch64, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (arm, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (armv7, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (x86_64, apple-darwin) (push) Blocked by required conditions
release-please / Build release binaries (x86_64, unknown-linux-musl) (push) Blocked by required conditions
Build & Test / unittests (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / unittests (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / clippy (push) Waiting to run
Build & Test / rustfmt (push) Waiting to run
Co-authored-by: LoricAndre <loric.andre@pm.me>
2025-01-23 00:27:54 +01:00
LoricAndre 03da73b0f4 fix(docs): fix README lib example 2025-01-23 00:05:39 +01:00
LoricAndre 68a9dda12e test(ci): try removing the packages altogether 2025-01-23 00:01:28 +01:00
LoricAndre 9f928a0dc3 test(ci): test previous fixes 2025-01-22 23:59:27 +01:00
LoricAndre 950e7cd86f test(ci): test previous fixes 2025-01-22 23:57:57 +01:00
skim-rs-bot[bot] efe7789eff
chore: release master (#672)
Co-authored-by: skim-rs-bot[bot] <190268553+skim-rs-bot[bot]@users.noreply.github.com>
2025-01-22 23:56:18 +01:00
LoricAndre 44342c69b8 chore(ci): try releasing as 0.1.0 2025-01-22 23:54:01 +01:00
LoricAndre 55e7b51af9 chore(ci): do not search commits on e2e & xtask 2025-01-22 23:50:53 +01:00
LoricAndre 69d98ef269 chore(ci): try to fix release-please on extra packages 2025-01-22 23:45:44 +01:00
LoricAndre da499af34f
chore: fix clippy warning in rust 1.84 (#688)
Co-authored-by: LoricAndre <loric.andre@pm.me>
2025-01-22 23:25:26 +01:00
LoricAndre c2f98e0c6e
chore: remove lazy_static (#687)
* chore: remove lazy_static

* chore: fix clippy warning

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>
2025-01-22 23:25:04 +01:00
dependabot[bot] bf49f68c92
chore(deps): bump indexmap from 2.7.0 to 2.7.1 (#684)
Some checks failed
release-please / release-please (push) Has been cancelled
Build & Test / unittests (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / unittests (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / clippy (push) Has been cancelled
Build & Test / rustfmt (push) Has been cancelled
release-please / setup-release (push) Has been cancelled
release-please / publish-crate (push) Has been cancelled
release-please / Build release binaries (aarch64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (aarch64, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (arm, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (armv7, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (x86_64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (x86_64, unknown-linux-musl) (push) Has been cancelled
Bumps [indexmap](https://github.com/indexmap-rs/indexmap) from 2.7.0 to 2.7.1.
- [Changelog](https://github.com/indexmap-rs/indexmap/blob/master/RELEASES.md)
- [Commits](https://github.com/indexmap-rs/indexmap/compare/2.7.0...2.7.1)

---
updated-dependencies:
- dependency-name: indexmap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-21 11:00:10 +01:00
dependabot[bot] 17d77f40e2
chore(deps): bump clap from 4.5.26 to 4.5.27 (#685)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.26 to 4.5.27.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.26...clap_complete-v4.5.27)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-21 10:59:41 +01:00
dependabot[bot] 6d1aa4236c
chore(deps): bump log from 0.4.22 to 0.4.25 (#686)
Bumps [log](https://github.com/rust-lang/log) from 0.4.22 to 0.4.25.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.22...0.4.25)

---
updated-dependencies:
- dependency-name: log
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-21 10:59:12 +01:00
Alexandre GV. 44bc7965a4
doc(readme): correct fzf library statement in README (#679)
Some checks failed
release-please / release-please (push) Has been cancelled
Build & Test / unittests (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / unittests (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / clippy (push) Has been cancelled
Build & Test / rustfmt (push) Has been cancelled
release-please / setup-release (push) Has been cancelled
release-please / publish-crate (push) Has been cancelled
release-please / Build release binaries (aarch64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (aarch64, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (arm, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (armv7, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (x86_64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (x86_64, unknown-linux-musl) (push) Has been cancelled
* Correct fzf library statement

* Nuance with "clearly"

* docs: update differences to fzf

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>
Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2025-01-15 19:00:38 +01:00
dependabot[bot] 4fa222a9d9
chore(deps): bump vte from 0.13.1 to 0.14.1 (#680)
Some checks are pending
release-please / release-please (push) Waiting to run
release-please / setup-release (push) Blocked by required conditions
release-please / publish-crate (push) Blocked by required conditions
release-please / Build release binaries (aarch64, apple-darwin) (push) Blocked by required conditions
release-please / Build release binaries (aarch64, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (arm, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (armv7, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (x86_64, apple-darwin) (push) Blocked by required conditions
release-please / Build release binaries (x86_64, unknown-linux-musl) (push) Blocked by required conditions
Build & Test / unittests (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / unittests (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / clippy (push) Waiting to run
Build & Test / rustfmt (push) Waiting to run
Bumps [vte](https://github.com/alacritty/vte) from 0.13.1 to 0.14.1.
- [Release notes](https://github.com/alacritty/vte/releases)
- [Changelog](https://github.com/alacritty/vte/blob/master/CHANGELOG.md)
- [Commits](https://github.com/alacritty/vte/compare/v0.13.1...v0.14.1)

---
updated-dependencies:
- dependency-name: vte
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-14 19:33:58 +01:00
dependabot[bot] 0fabdf9704
chore(deps): bump clap from 4.5.23 to 4.5.26 (#682)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.23 to 4.5.26.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.23...clap_complete-v4.5.26)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-14 19:29:40 +01:00
dependabot[bot] 8a21a9672f
chore(deps): bump clap_complete from 4.5.40 to 4.5.42 (#681)
Some checks are pending
release-please / release-please (push) Waiting to run
release-please / setup-release (push) Blocked by required conditions
release-please / publish-crate (push) Blocked by required conditions
release-please / Build release binaries (aarch64, apple-darwin) (push) Blocked by required conditions
release-please / Build release binaries (aarch64, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (arm, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (armv7, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (x86_64, apple-darwin) (push) Blocked by required conditions
release-please / Build release binaries (x86_64, unknown-linux-musl) (push) Blocked by required conditions
Build & Test / unittests (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / unittests (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / clippy (push) Waiting to run
Build & Test / rustfmt (push) Waiting to run
Bumps [clap_complete](https://github.com/clap-rs/clap) from 4.5.40 to 4.5.42.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.40...clap_complete-v4.5.42)

---
updated-dependencies:
- dependency-name: clap_complete
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-14 01:11:54 +01:00
dependabot[bot] 28e3034399
chore(deps): bump clap_mangen from 0.2.24 to 0.2.26 (#683)
* chore(deps): bump clap_mangen from 0.2.24 to 0.2.26

Bumps [clap_mangen](https://github.com/clap-rs/clap) from 0.2.24 to 0.2.26.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_mangen-v0.2.24...clap_mangen-v0.2.26)

---
updated-dependencies:
- dependency-name: clap_mangen
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore: generate completions & manpage

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2025-01-14 01:10:34 +01:00
dependabot[bot] a4f06514f4
chore(deps): bump clap_complete_nushell from 4.5.4 to 4.5.5 (#677)
Some checks failed
Build & Test / unittests (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
release-please / release-please (push) Has been cancelled
Build & Test / unittests (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / clippy (push) Has been cancelled
Build & Test / rustfmt (push) Has been cancelled
release-please / setup-release (push) Has been cancelled
release-please / publish-crate (push) Has been cancelled
release-please / Build release binaries (aarch64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (aarch64, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (arm, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (armv7, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (x86_64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (x86_64, unknown-linux-musl) (push) Has been cancelled
Bumps [clap_complete_nushell](https://github.com/clap-rs/clap) from 4.5.4 to 4.5.5.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete_nushell-v4.5.4...clap_complete_nushell-v4.5.5)

---
updated-dependencies:
- dependency-name: clap_complete_nushell
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-07 16:31:31 +01:00
dependabot[bot] fc141fcb9a
chore(deps): bump tempfile from 3.14.0 to 3.15.0 (#676)
Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.14.0 to 3.15.0.
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.14.0...v3.15.0)

---
updated-dependencies:
- dependency-name: tempfile
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-07 16:31:06 +01:00
dependabot[bot] a55e9c97ac
chore(deps): bump vte from 0.13.0 to 0.13.1 (#675)
Bumps [vte](https://github.com/alacritty/vte) from 0.13.0 to 0.13.1.
- [Release notes](https://github.com/alacritty/vte/releases)
- [Changelog](https://github.com/alacritty/vte/blob/master/CHANGELOG.md)
- [Commits](https://github.com/alacritty/vte/compare/v0.13.0...v0.13.1)

---
updated-dependencies:
- dependency-name: vte
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-07 16:30:03 +01:00
dependabot[bot] c9282818c6
chore(deps): bump shell-quote from 0.7.1 to 0.7.2 (#674)
Bumps [shell-quote](https://github.com/allenap/shell-quote) from 0.7.1 to 0.7.2.
- [Commits](https://github.com/allenap/shell-quote/compare/v0.7.1...v0.7.2)

---
updated-dependencies:
- dependency-name: shell-quote
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-07 16:29:38 +01:00
skim-rs-bot[bot] 74b2ea971d
chore: release master (#670)
Some checks failed
release-please / release-please (push) Has been cancelled
Build & Test / unittests (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / unittests (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / clippy (push) Has been cancelled
Build & Test / rustfmt (push) Has been cancelled
release-please / setup-release (push) Has been cancelled
release-please / publish-crate (push) Has been cancelled
release-please / Build release binaries (aarch64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (aarch64, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (arm, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (armv7, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (x86_64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (x86_64, unknown-linux-musl) (push) Has been cancelled
* chore: release master

* chore: generate completions & manpage

---------

Co-authored-by: skim-rs-bot[bot] <190268553+skim-rs-bot[bot]@users.noreply.github.com>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2024-12-27 15:13:50 +01:00
Felipe Gallois b265179f20
fix: remove atty (#671)
This was done previously in #587, but I suspect it was unwillingly
reintroduced in #586. I found it while running trivy and it still
complained about an atty issue, although the project's history showed
that it has already been removed
2024-12-27 15:05:47 +01:00
skim-rs-bot[bot] 422bd35a57
chore: release master (#656)
Some checks are pending
release-please / release-please (push) Waiting to run
release-please / setup-release (push) Blocked by required conditions
release-please / publish-crate (push) Blocked by required conditions
release-please / Build release binaries (aarch64, apple-darwin) (push) Blocked by required conditions
release-please / Build release binaries (aarch64, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (arm, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (armv7, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (x86_64, apple-darwin) (push) Blocked by required conditions
release-please / Build release binaries (x86_64, unknown-linux-musl) (push) Blocked by required conditions
Build & Test / unittests (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / unittests (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / clippy (push) Waiting to run
Build & Test / rustfmt (push) Waiting to run
* chore: release master

* chore: generate completions & manpage

---------

Co-authored-by: skim-rs-bot[bot] <190268553+skim-rs-bot[bot]@users.noreply.github.com>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2024-12-26 18:27:14 +01:00
dependabot[bot] 7914c6f291 chore(deps): bump env_logger from 0.11.5 to 0.11.6 (#668)
Bumps [env_logger](https://github.com/rust-cli/env_logger) from 0.11.5 to 0.11.6.
- [Release notes](https://github.com/rust-cli/env_logger/releases)
- [Changelog](https://github.com/rust-cli/env_logger/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-cli/env_logger/compare/v0.11.5...v0.11.6)

---
updated-dependencies:
- dependency-name: env_logger
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-26 17:34:02 +01:00
dependabot[bot] fdbf4ff380 chore(deps): bump clap_complete from 4.5.39 to 4.5.40 (#667)
Bumps [clap_complete](https://github.com/clap-rs/clap) from 4.5.39 to 4.5.40.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.39...clap_complete-v4.5.40)

---
updated-dependencies:
- dependency-name: clap_complete
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-26 17:34:02 +01:00
dependabot[bot] 290a9231c9 chore(deps): bump which from 7.0.0 to 7.0.1 (#666)
Bumps [which](https://github.com/harryfei/which-rs) from 7.0.0 to 7.0.1.
- [Release notes](https://github.com/harryfei/which-rs/releases)
- [Changelog](https://github.com/harryfei/which-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/harryfei/which-rs/compare/7.0.0...7.0.1)

---
updated-dependencies:
- dependency-name: which
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-26 17:34:02 +01:00
Elio Grieco 920e7743e9 fix: update rank to follow the readded index tiebreak (#669) 2024-12-26 17:33:32 +01:00
dependabot[bot] 8c35953121
chore(deps): bump clap_complete from 4.5.38 to 4.5.39 (#665)
Some checks failed
Build & Test / clippy (push) Has been cancelled
Build & Test / rustfmt (push) Has been cancelled
release-please / release-please (push) Has been cancelled
Build & Test / unittests (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / unittests (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
release-please / setup-release (push) Has been cancelled
release-please / publish-crate (push) Has been cancelled
release-please / Build release binaries (aarch64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (aarch64, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (arm, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (armv7, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (x86_64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (x86_64, unknown-linux-musl) (push) Has been cancelled
Bumps [clap_complete](https://github.com/clap-rs/clap) from 4.5.38 to 4.5.39.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.38...clap_complete-v4.5.39)

---
updated-dependencies:
- dependency-name: clap_complete
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-17 09:36:12 +01:00
dependabot[bot] 37e96629c3
chore(deps): bump chrono from 0.4.38 to 0.4.39 (#661)
Some checks failed
release-please / release-please (push) Has been cancelled
Build & Test / unittests (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / unittests (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / clippy (push) Has been cancelled
Build & Test / rustfmt (push) Has been cancelled
release-please / setup-release (push) Has been cancelled
release-please / publish-crate (push) Has been cancelled
release-please / Build release binaries (aarch64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (aarch64, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (arm, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (armv7, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (x86_64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (x86_64, unknown-linux-musl) (push) Has been cancelled
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.38 to 0.4.39.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.38...v0.4.39)

---
updated-dependencies:
- dependency-name: chrono
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-10 16:31:53 +01:00
dependabot[bot] e9cd731969
chore(deps): bump clap from 4.5.21 to 4.5.23 (#662)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.21 to 4.5.23.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.21...clap_complete-v4.5.23)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-10 16:27:19 +01:00
Björn Steinbrink 3eb55911be
fix: Fix non-functional vim plugin (#659)
Some checks failed
release-please / release-please (push) Has been cancelled
Build & Test / unittests (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / unittests (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / clippy (push) Has been cancelled
Build & Test / rustfmt (push) Has been cancelled
release-please / setup-release (push) Has been cancelled
release-please / publish-crate (push) Has been cancelled
release-please / Build release binaries (aarch64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (aarch64, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (arm, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (armv7, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (x86_64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (x86_64, unknown-linux-musl) (push) Has been cancelled
The vim plugin uses --expect and expects the key used to accept the
selection on the first line of output. Commit bcee1f4 "feat!: do not
check for expect before printing the argument of accept… (#625)" broke
that by not outputting anything if the selection was made using the
default "enter" key. We can workaround that by explicitly adding
"enter" to the --expect argument, so that it once again shows up in the
output.

Fixes skim-rs/skim.vim#25
2024-12-09 00:58:39 +01:00
skim-rs-bot[bot] 1121509428
chore: release master (#655)
Some checks failed
Build & Test / unittests (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / unittests (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
release-please / release-please (push) Has been cancelled
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / clippy (push) Has been cancelled
Build & Test / rustfmt (push) Has been cancelled
release-please / publish-crate (push) Has been cancelled
release-please / setup-release (push) Has been cancelled
release-please / Build release binaries (aarch64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (aarch64, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (arm, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (armv7, unknown-linux-musl) (push) Has been cancelled
release-please / Build release binaries (x86_64, apple-darwin) (push) Has been cancelled
release-please / Build release binaries (x86_64, unknown-linux-musl) (push) Has been cancelled
* chore: release master

* chore: generate completions & manpage

---------

Co-authored-by: skim-rs-bot[bot] <190268553+skim-rs-bot[bot]@users.noreply.github.com>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2024-12-04 15:17:34 +01:00
LoricAndre 9db1478ab1
ci: fix release config 2024-12-04 15:05:03 +01:00
LoricAndre a3869e55c1
ci: fix release-please config 2024-12-04 15:01:22 +01:00
LoricAndre 0992947bd5
update release-please manifest 2024-12-04 14:59:01 +01:00
LoricAndre 3cca5c1823
Revert "chore: release master (#647)" (#654)
This reverts commit c171f70389.
2024-12-04 14:56:54 +01:00
skim-rs-bot[bot] c171f70389
chore: release master (#647)
* chore: release master

* chore: generate completions & manpage

---------

Co-authored-by: skim-rs-bot[bot] <190268553+skim-rs-bot[bot]@users.noreply.github.com>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2024-12-04 14:46:59 +01:00
LoricAndre 04d79e664e docs: add note about fuzziness of interactive examples (fixes #543)
Some checks are pending
release-please / release-please (push) Waiting to run
release-please / setup-release (push) Blocked by required conditions
release-please / publish-crate (push) Blocked by required conditions
release-please / Build release binaries (aarch64, apple-darwin) (push) Blocked by required conditions
release-please / Build release binaries (aarch64, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (arm, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (armv7, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (x86_64, apple-darwin) (push) Blocked by required conditions
release-please / Build release binaries (x86_64, unknown-linux-musl) (push) Blocked by required conditions
Build & Test / unittests (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / unittests (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / clippy (push) Waiting to run
Build & Test / rustfmt (push) Waiting to run
2024-12-03 14:01:29 +01:00
LoricAndre 1abf545ed9
fix: fix --tmux quoting (#643)
* fix: fix --tmux quoting

* fix e2e

* use fifo for input

* fix macos e2e

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>
2024-12-03 11:21:35 +01:00
Kirisame bdaac9c8ff
docs: missing backtick in install commands (#646) 2024-12-03 09:02:24 +01:00
dependabot[bot] a6e233d3ca
chore(deps): bump indexmap from 2.6.0 to 2.7.0 (#645)
Bumps [indexmap](https://github.com/indexmap-rs/indexmap) from 2.6.0 to 2.7.0.
- [Changelog](https://github.com/indexmap-rs/indexmap/blob/master/RELEASES.md)
- [Commits](https://github.com/indexmap-rs/indexmap/compare/2.6.0...2.7.0)

---
updated-dependencies:
- dependency-name: indexmap
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-03 09:01:37 +01:00
LoricAndre 3d99879d4e fix: revert README overwrite
Some checks are pending
release-please / release-please (push) Waiting to run
release-please / setup-release (push) Blocked by required conditions
release-please / publish-crate (push) Blocked by required conditions
release-please / Build release binaries (aarch64, apple-darwin) (push) Blocked by required conditions
release-please / Build release binaries (aarch64, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (arm, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (armv7, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (x86_64, apple-darwin) (push) Blocked by required conditions
release-please / Build release binaries (x86_64, unknown-linux-musl) (push) Blocked by required conditions
Build & Test / unittests (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / unittests (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / clippy (push) Waiting to run
Build & Test / rustfmt (push) Waiting to run
2024-12-02 19:49:01 +01:00
skim-rs-bot[bot] 17c261af1c
chore: release master (#642)
Some checks are pending
release-please / release-please (push) Waiting to run
release-please / setup-release (push) Blocked by required conditions
release-please / publish-crate (push) Blocked by required conditions
release-please / Build release binaries (aarch64, apple-darwin) (push) Blocked by required conditions
release-please / Build release binaries (aarch64, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (arm, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (armv7, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (x86_64, apple-darwin) (push) Blocked by required conditions
release-please / Build release binaries (x86_64, unknown-linux-musl) (push) Blocked by required conditions
Build & Test / unittests (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / unittests (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / clippy (push) Waiting to run
Build & Test / rustfmt (push) Waiting to run
* chore: release master

* chore: generate completions & manpage

---------

Co-authored-by: skim-rs-bot[bot] <190268553+skim-rs-bot[bot]@users.noreply.github.com>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2024-12-01 16:19:19 +01:00
LoricAndre 7a7a1b4e3c chore: cargo fmt 2024-12-01 16:07:16 +01:00
LoricAndre e3637e068e fix: clippy pedantic on lib.rs 2024-12-01 16:01:44 +01:00
LoricAndre 3ff3832881 fix: fix token permissions for release file 2024-12-01 15:56:05 +01:00
skim-rs-bot[bot] 3f6a6bed85
chore: release master (#641)
Some checks are pending
release-please / release-please (push) Waiting to run
release-please / setup-release (push) Blocked by required conditions
release-please / publish-crate (push) Blocked by required conditions
release-please / Build release binaries (aarch64, apple-darwin) (push) Blocked by required conditions
release-please / Build release binaries (aarch64, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (arm, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (armv7, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (x86_64, apple-darwin) (push) Blocked by required conditions
release-please / Build release binaries (x86_64, unknown-linux-musl) (push) Blocked by required conditions
Build & Test / unittests (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / unittests (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / clippy (push) Waiting to run
Build & Test / rustfmt (push) Waiting to run
* chore: release master

* chore: generate completions & manpage

---------

Co-authored-by: skim-rs-bot[bot] <190268553+skim-rs-bot[bot]@users.noreply.github.com>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2024-12-01 15:50:59 +01:00
LoricAndre 621fa1e4c5 chore: cargo fmt 2024-12-01 15:46:02 +01:00
LoricAndre 04541d515b fix: clippy pedantic on main.rs 2024-12-01 15:44:03 +01:00
LoricAndre db30b25241 chore: remove cli feature from skim 2024-12-01 15:26:50 +01:00
LoricAndre b1dfcc7e30 fix: fix missing var in CI 2024-12-01 15:21:17 +01:00
skim-rs-bot[bot] dd41167d42
chore: release master (#640)
* chore: release master

* chore: generate completions & manpage

---------

Co-authored-by: skim-rs-bot[bot] <190268553+skim-rs-bot[bot]@users.noreply.github.com>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2024-12-01 15:15:18 +01:00
LoricAndre 0963b974ff fix: make item module public (closes #568) 2024-12-01 15:08:32 +01:00
LoricAndre f24b0a9bbd fix: do not run tests in release workflow 2024-12-01 15:06:45 +01:00
github-actions[bot] 246c8d86f3
chore: release master (#639)
* chore: release master

* chore: generate completions & manpage

---------

Co-authored-by: skim-rs-bot[bot] <190268553+skim-rs-bot[bot]@users.noreply.github.com>
Co-authored-by: Skim bot <skim-bot@skim-rs.github.io>
2024-12-01 15:00:15 +01:00
LoricAndre 993ba4a3ae ci: cleanup 2024-12-01 14:56:09 +01:00
LoricAndre a534f3593b ci: avoid duplicate test runs 2024-12-01 14:49:32 +01:00
LoricAndre 26dc2f6c88 ci: exit success when nothing to commit 2024-12-01 14:46:26 +01:00
LoricAndre b624463543 ci: use token in checkout 2024-12-01 14:40:37 +01:00
LoricAndre 358135342e ci: skip ci on modified files 2024-12-01 13:57:15 +01:00
LoricAndre c1679cce68 ci: manually use gh app for push 2024-12-01 13:51:12 +01:00
LoricAndre 4eda4c40fd ci: use gh app for push 2024-12-01 13:40:55 +01:00
LoricAndre d6edf78474 ci: use gh app for token 2024-12-01 13:28:11 +01:00
LoricAndre 5c4d21d55d ci: use PAT for release-please to trigger downstream ci 2024-12-01 13:16:12 +01:00
LoricAndre a4bad87721 ci: simplify release ci 2024-12-01 13:09:13 +01:00
LoricAndre a4ffca712b ci: use cache for xtask 2024-12-01 12:55:11 +01:00
LoricAndre c22515ef39 ci: test push with explicit ref 2024-12-01 12:52:50 +01:00
LoricAndre 2d36bbac61 ci: fix push 2024-12-01 12:40:46 +01:00
LoricAndre cc78aec4aa fix ci
Some checks are pending
release-please / Build release binaries (arm, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (armv7, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (x86_64, apple-darwin) (push) Blocked by required conditions
release-please / Build release binaries (x86_64, unknown-linux-musl) (push) Blocked by required conditions
release-please / release-artifacts (push) Blocked by required conditions
release-please / release-please (push) Waiting to run
release-please / setup-release (push) Blocked by required conditions
release-please / publish-crate (push) Blocked by required conditions
release-please / Build release binaries (aarch64, apple-darwin) (push) Blocked by required conditions
release-please / Build release binaries (aarch64, unknown-linux-musl) (push) Blocked by required conditions
Build & Test / unittests (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / unittests (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / clippy (push) Waiting to run
Build & Test / rustfmt (push) Waiting to run
2024-12-01 01:46:40 +01:00
LoricAndre e995715dcd fix pr ci 2024-12-01 01:44:06 +01:00
LoricAndre f7f8a5c259 fix ci 2024-12-01 01:42:24 +01:00
LoricAndre 36c47578f2 fix: fix urls in cargo.toml 2024-12-01 01:39:45 +01:00
LoricAndre 3277354116 fix: fix ci 2024-12-01 01:37:54 +01:00
LoricAndre 05e7842268
ci: generate files in PR (#638)
Co-authored-by: LoricAndre <loric.andre@pm.me>
2024-12-01 01:37:14 +01:00
github-actions[bot] 45f0c36175
chore: release master (#637)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-12-01 01:28:33 +01:00
LoricAndre 682ea271c7 fix: set toolchain 2024-12-01 01:25:03 +01:00
github-actions[bot] e81e632f2b chore: release master (#636)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-12-01 01:25:03 +01:00
LoricAndre 7b70685049 test(ci): check for string 2024-12-01 01:25:03 +01:00
LoricAndre f5728dcb6e dbg 2024-12-01 01:25:03 +01:00
LoricAndre 0bc1136316 test(ci): fix dash in test 2024-12-01 01:25:03 +01:00
LoricAndre fabf9c4180 test(ci): add debug 2024-12-01 01:25:03 +01:00
github-actions[bot] a6c5df549b chore: release master (#635)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-12-01 01:25:03 +01:00
LoricAndre 57bd3c7c76 fix(ci): replace underscore with dashes 2024-12-01 01:25:03 +01:00
LoricAndre 00b04cd6a7 test(ci): test with subpath 2024-12-01 01:25:03 +01:00
github-actions[bot] 03c3529fbf chore: release master (#634)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-12-01 01:25:03 +01:00
LoricAndre c3d4e368cd fix(ci): skip extra packages 2024-12-01 01:25:03 +01:00
LoricAndre 262bce09d7 chore: cleanup failed releases 2024-12-01 01:25:03 +01:00
LoricAndre 39e7614ba2 test(ci): test with crate root 2024-12-01 01:25:03 +01:00
LoricAndre 60b9b19d74 fix(ci): remove extra modules 2024-12-01 01:25:03 +01:00
github-actions[bot] 099e47641a chore: release master (#633)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-12-01 01:25:03 +01:00
LoricAndre a5b81818d6 feat(tui): add info hidden (#630)
* feat(tui): add info hidden

* [skip ci] compgen & mangen

* use match for easier handling of future options

* refactor model into mod

* fmt

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>
2024-12-01 01:25:03 +01:00
LoricAndre b496259003 fix(ci): use stable toolchain 2024-12-01 01:25:03 +01:00
LoricAndre 8b8920c3f9 fix(ci): fix extra dot 2024-12-01 01:25:03 +01:00
LoricAndre f60d64b0c0 fix(ci): fix templating 2024-12-01 01:25:03 +01:00
LoricAndre f89a366a5e fix(ci): remove string from cond 2024-12-01 01:25:03 +01:00
github-actions[bot] 99d2863bac chore: release master (#632)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-12-01 01:25:03 +01:00
LoricAndre 5146457f76 fix(ci): use correct tag 2024-12-01 01:25:03 +01:00
LoricAndre 921667ac73 test(ci): try downgrading cargo.toml 2024-12-01 00:04:32 +01:00
LoricAndre 8c10712c16 fix(ci): bump minor for feat 2024-11-30 23:58:24 +01:00
LoricAndre 8933d31033 fix(ci): do not bump major 2024-11-30 23:57:26 +01:00
LoricAndre 1a73d6cda0 fix(ci): use absolute path for changelog 2024-11-30 23:53:07 +01:00
LoricAndre 51893fd4c9 fix(ci): set skim changelog path 2024-11-30 23:48:05 +01:00
LoricAndre 4fd81a5a45 fix(ci): set skim version 2024-11-30 23:43:45 +01:00
LoricAndre 5ba98dfe75 fix(ci): test with different release-please config 2024-11-30 23:40:06 +01:00
LoricAndre f4a515d2f7 fix(ci): use release-please in workspace root
Some checks are pending
release-please / release-please (push) Waiting to run
release-please / setup-release (push) Blocked by required conditions
release-please / publish-crate (push) Blocked by required conditions
release-please / Build release binaries (aarch64, apple-darwin) (push) Blocked by required conditions
release-please / Build release binaries (aarch64, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (arm, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (armv7, unknown-linux-musl) (push) Blocked by required conditions
release-please / Build release binaries (x86_64, apple-darwin) (push) Blocked by required conditions
release-please / Build release binaries (x86_64, unknown-linux-musl) (push) Blocked by required conditions
release-please / release-files (push) Blocked by required conditions
release-please / release-artifacts (push) Blocked by required conditions
Build & Test / unittests (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / unittests (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / clippy (push) Waiting to run
Build & Test / rustfmt (push) Waiting to run
2024-11-30 23:32:56 +01:00
LoricAndre 0090a564f1 fix(ci): add intermediary step for release 2024-11-30 23:25:41 +01:00
LoricAndre 77ecb0a298 fix(ci): use different syntax for conditions 2024-11-30 23:20:14 +01:00
LoricAndre 1b8d7c5a7c fix(ci): remove needs in release-please condition 2024-11-30 23:17:14 +01:00
LoricAndre 427377a900 fix(ci): use subpath for release-please outputs 2024-11-30 23:13:44 +01:00
LoricAndre 7212bf24c6 fix(ci): release-please permissions on job level 2024-11-30 23:04:41 +01:00
LoricAndre c9f9025da9 fix: undo sk-tmux deprecation 2024-11-30 23:01:21 +01:00
LoricAndre fefff76481 fix(ci): set release-please path 2024-11-30 20:59:31 +01:00
LoricAndre ec393a1b7f fix(ci): fix clippy os 2024-11-30 20:56:27 +01:00
LoricAndre eae1424372 ci: fix condition 2024-11-30 20:35:01 +01:00
LoricAndre cd0c23eb63 ci: fix release-please target branch 2024-11-30 20:33:08 +01:00
LoricAndre 74ac67058d ci: readd crate to release-please 2024-11-30 20:31:48 +01:00
LoricAndre 7f0dec4b48
test: migrate e2e to rust (#629)
* wip: enhance release ci

* update PR checklist

* wip: rewrite e2e in rust

* add e2e cargo module

* rewrite all e2e tests in rust

* remove python tests

* clippy & fmt

* use cargo tests in ci

* use which to get tmux

* check len

* check all lens

* increase time out and parallelism

* fix no hscroll typo

* better check for print0

* add sleep

* fix execute_0

* update release ci

* try optimizing tests

* better sleep

* Revert "better sleep"

This reverts commit b966367999.

* split ci

* cleanup

* test without anchor

* use release-please

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>
2024-11-30 20:29:14 +01:00
LoricAndre d9419c640b release v0.14.3 (fix release CI)
Some checks failed
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / clippy (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / clippy (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / rustfmt (push) Has been cancelled
2024-11-28 17:24:55 +01:00
LoricAndre d7177531f1 release v0.14.2 (fix release CI) 2024-11-28 17:19:14 +01:00
LoricAndre c28f81cc00 release v0.14.1 (fix release CI) 2024-11-28 17:14:18 +01:00
LoricAndre 68f1c408ce release v0.14.0 2024-11-28 17:03:30 +01:00
LoricAndre f6eb98e514
docs: add fzf-lua and nu_plugin_skim to the README (#626)
* docs: add fzf-lua and nu_plugin_skim to the README

* work on the doc

* [skip ci] add repology table

* [skip ci] remove icon deps from fzf-lua doc

* [skip ci] update toc

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>
2024-11-27 21:44:18 +01:00
LoricAndre a2d8c3f602
feat: add --tmux flag (deprecates sk-tmux, fixes #596) (#603)
* feat: add `--tmux` flag (deprecates sk-tmux, fixes #596)

* clippy & fmt

* chore: generate doc & completions

* fix: copy stdin to tmux call

* fix: ignore stdin if not piped

* fix: stream stdin instead of punctual copy

* remove tmp dir & clippy

* fmt

* test: add basic tmux test

* fix tests

* use regex to fix tests on macos

* add unittests

* add deprecation warning to the readme

* use tmux flag in key-bindings

* fix tests on macos

* chore: improve PR ci (#617)

* Bump unicode-width from 0.1.14 to 0.2.0 (#616)

Bumps [unicode-width](https://github.com/unicode-rs/unicode-width) from 0.1.14 to 0.2.0.
- [Commits](https://github.com/unicode-rs/unicode-width/compare/v0.1.14...v0.2.0)

---
updated-dependencies:
- dependency-name: unicode-width
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump nix from 0.25.1 to 0.29.0 (#614)

Bumps [nix](https://github.com/nix-rust/nix) from 0.25.1 to 0.29.0.
- [Changelog](https://github.com/nix-rust/nix/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nix-rust/nix/compare/v0.25.1...v0.29.0)

---
updated-dependencies:
- dependency-name: nix
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump env_logger from 0.9.3 to 0.11.5 (#615)

Bumps [env_logger](https://github.com/rust-cli/env_logger) from 0.9.3 to 0.11.5.
- [Release notes](https://github.com/rust-cli/env_logger/releases)
- [Changelog](https://github.com/rust-cli/env_logger/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-cli/env_logger/compare/v0.9.3...v0.11.5)

---
updated-dependencies:
- dependency-name: env_logger
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>

* chore: improve PR ci

* disable cache on fmt job

* add title check

* clean

* Create pr.yml

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: LoricAndre <loric.andre@pm.me>

* revert mangen & compgen for merge

* merge master

* fix

* revert readme merge issues

* make TmuxOptions private

* directly invoke tmux

* better handling of tmux

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: LoricAndre <loric.andre@pm.me>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-27 21:43:57 +01:00
LoricAndre be53f1f865
chore: remove ci dir (#627)
Some checks are pending
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / clippy (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / clippy (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / rustfmt (push) Waiting to run
Co-authored-by: LoricAndre <loric.andre@pm.me>
2024-11-27 21:13:10 +01:00
LoricAndre bcee1f4c02
feat!: do not check for expect before printing the argument of accept… (#625)
* feat!: do not check for expect before printing the argument of accept(...)

* fix tests

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>
2024-11-27 20:01:11 +01:00
LoricAndre 4144879f00
fix: allow combined multiple args (fixes #622) (#623)
Some checks are pending
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / clippy (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / clippy (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / rustfmt (push) Waiting to run
* fix: allow combined multiple args (fixes #622)

* fmt

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>
2024-11-27 11:28:53 +01:00
khafatech 4b4795e9bc
Fix example link and some typo in Readme (#605)
Some checks are pending
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / clippy (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / clippy (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / rustfmt (push) Waiting to run
* Fix example link in README.md

* Fix typo and improve punctuation

* change example url to the correct repo

* Fix some links and wording

* Add support for install on Linux armv7l, aarch64 and Darwin arm64 (#413)

Also,

- Use the Github API to download the latest `version`
- Use the new release artefact format
- Fix grep pattern to work across Mac / Linux
- Fix version check to strip out `sk` prefix
- Exit when existing version already matches

This allows the install script to work with Raspbian running on a
Raspberry Pi

* chore: remove some platform-specific quirkinesses from e2e (#602)

* chore: remove some platform-specific quirkinesses from e2e

* debug

* Revert "debug"

This reverts commit ed065c3e26.

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>

* Improve grammar and wording in the readme

---------

Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
Co-authored-by: Rohan Jain <crodjer@proton.me>
Co-authored-by: LoricAndre <loric.andre@pm.me>
2024-11-26 21:03:29 +01:00
Praveen Perera 1bc4653b68
docs: link all PRs, issues, commits and authors in CHANGELOG (#621)
Some checks are pending
Build & Test / e2e (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / e2e (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / clippy (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / clippy (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / rustfmt (push) Waiting to run
Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2024-11-26 19:55:40 +01:00
LoricAndre 7f54f5d5d6 add changelog generation to release ci 2024-11-26 19:29:45 +01:00
Praveen Perera f93389fa98
docs: update changelog from github releases (#620) 2024-11-26 18:07:23 +01:00
LoricAndre e649d084c3 chore: improve PR ci (#617)
* Bump unicode-width from 0.1.14 to 0.2.0 (#616)

Bumps [unicode-width](https://github.com/unicode-rs/unicode-width) from 0.1.14 to 0.2.0.
- [Commits](https://github.com/unicode-rs/unicode-width/compare/v0.1.14...v0.2.0)

---
updated-dependencies:
- dependency-name: unicode-width
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump nix from 0.25.1 to 0.29.0 (#614)

Bumps [nix](https://github.com/nix-rust/nix) from 0.25.1 to 0.29.0.
- [Changelog](https://github.com/nix-rust/nix/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nix-rust/nix/compare/v0.25.1...v0.29.0)

---
updated-dependencies:
- dependency-name: nix
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump env_logger from 0.9.3 to 0.11.5 (#615)

Bumps [env_logger](https://github.com/rust-cli/env_logger) from 0.9.3 to 0.11.5.
- [Release notes](https://github.com/rust-cli/env_logger/releases)
- [Changelog](https://github.com/rust-cli/env_logger/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-cli/env_logger/compare/v0.9.3...v0.11.5)

---
updated-dependencies:
- dependency-name: env_logger
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>

* chore: improve PR ci

* disable cache on fmt job

* add title check

* clean

* Create pr.yml

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: LoricAndre <loric.andre@pm.me>
2024-11-26 18:01:02 +01:00
dependabot[bot] 24ccef193b chore: bump env_logger from 0.9.3 to 0.11.5 (#615)
Bumps [env_logger](https://github.com/rust-cli/env_logger) from 0.9.3 to
0.11.5.
- [Release notes](https://github.com/rust-cli/env_logger/releases)
-
[Changelog](https://github.com/rust-cli/env_logger/blob/main/CHANGELOG.md)
-
[Commits](https://github.com/rust-cli/env_logger/compare/v0.9.3...v0.11.5)

---
updated-dependencies:
- dependency-name: env_logger
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot]
<49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: LoricAndre
<57358788+LoricAndre@users.noreply.github.com>
2024-11-26 17:22:07 +01:00
dependabot[bot] c1314ea4cc chore: bump nix from 0.25.1 to 0.29.0 (#614)
Bumps [nix](https://github.com/nix-rust/nix) from 0.25.1 to 0.29.0.
- [Changelog](https://github.com/nix-rust/nix/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nix-rust/nix/compare/v0.25.1...v0.29.0)

---
updated-dependencies:
- dependency-name: nix
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot]
<49699333+dependabot[bot]@users.noreply.github.com>
2024-11-26 17:22:07 +01:00
dependabot[bot] a23c3ff90e chore: bump unicode-width from 0.1.14 to 0.2.0 (#616)
Bumps [unicode-width](https://github.com/unicode-rs/unicode-width) from
0.1.14 to 0.2.0.
-
[Commits](https://github.com/unicode-rs/unicode-width/compare/v0.1.14...v0.2.0)

---
updated-dependencies:
- dependency-name: unicode-width
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot]
<49699333+dependabot[bot]@users.noreply.github.com>
2024-11-26 17:22:07 +01:00
LoricAndre 0befe8d206
feat: readd index tiebreak (#609)
* readd index tiebreak

* clippy & fmt

* doc

* use index tiebreak in shell scripts

* revert man & completions for merge

* compgen & mangen

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>
2024-11-26 16:07:31 +01:00
LoricAndre ad909c379f release v0.13.0
Some checks failed
Build & Test / test (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / test (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / clippy (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / clippy (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / rustfmt (push) Has been cancelled
2024-11-25 16:55:22 +01:00
LoricAndre 33ca4023c1
feat: allow more flexibility for use as a library (#613)
* fix: revert options and context split

* readd selector to options

* make RankCriteria public

* fmt

* fix selector override

* add examples

* clippy

* revert mangen & compgen

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>
2024-11-25 16:54:24 +01:00
LoricAndre 53612a720a
chore: add pull request template (#608)
Some checks are pending
Build & Test / rustfmt (push) Waiting to run
Build & Test / test (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / test (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / clippy (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / clippy (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
* Create pull_request_template.md

* Update pull_request_template.md

* Update pull_request_template.md
2024-11-24 23:39:14 +01:00
LoricAndre dc5eb1ee19 release v0.12.0 2024-11-24 23:35:39 +01:00
LoricAndre 4b47244922
feat: add reload action (#604)
* feat: add reload action

* add tests for reload

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>
2024-11-24 23:33:50 +01:00
LoricAndre 4f1b2e1637 release v0.11.12
Some checks are pending
Build & Test / test (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / test (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / clippy (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / clippy (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / rustfmt (push) Waiting to run
2024-11-24 15:47:05 +01:00
LoricAndre bd73f62112
fix: remove index tiebreak from shell bindings (#611)
* fix: remove index tiebreak from shell bindings until the opetion is readded

* Update key-bindings.fish

* Update key-bindings.zsh
2024-11-24 15:41:11 +01:00
LoricAndre 63f4e336c7
chore: remove some platform-specific quirkinesses from e2e (#602)
Some checks failed
Build & Test / test (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / test (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / clippy (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / clippy (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / rustfmt (push) Has been cancelled
* chore: remove some platform-specific quirkinesses from e2e

* debug

* Revert "debug"

This reverts commit ed065c3e26.

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>
2024-11-22 20:55:56 +01:00
Rohan Jain 08a8a74716
Add support for install on Linux armv7l, aarch64 and Darwin arm64 (#413)
Also,

- Use the Github API to download the latest `version`
- Use the new release artefact format
- Fix grep pattern to work across Mac / Linux
- Fix version check to strip out `sk` prefix
- Exit when existing version already matches

This allows the install script to work with Raspbian running on a
Raspberry Pi
2024-11-22 19:47:48 +01:00
LoricAndre a4f1d90e7a
Fix workflow badge & remove references to lotabout in repo url
Some checks are pending
Build & Test / rustfmt (push) Waiting to run
Build & Test / test (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / test (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / clippy (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / clippy (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
2024-11-22 17:27:32 +01:00
LoricAndre 32d9e70af4 release v0.11.11 2024-11-22 16:42:17 +01:00
LoricAndre 56d2e794e1
hotfix: readd version arg (#606)
* hotfix: readd version arg

* add test

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>
2024-11-22 16:41:12 +01:00
LoricAndre 014903c8d9
Update README.md 2024-11-22 13:19:58 +01:00
LoricAndre ab708046e4
Update README.md 2024-11-22 13:19:31 +01:00
LoricAndre 8a8f273557 revert tag for next release
Some checks are pending
Build & Test / test (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / test (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / clippy (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / clippy (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / rustfmt (push) Waiting to run
2024-11-21 21:20:46 +01:00
LoricAndre 100c58b888 force to tag 2024-11-21 21:09:02 +01:00
LoricAndre ec57496441 force to tag 2024-11-21 21:05:52 +01:00
LoricAndre 7ed9cd5471 force changelog from tag 2024-11-21 21:02:19 +01:00
LoricAndre e9b2ff5126 bump version 2024-11-21 20:55:49 +01:00
LoricAndre f4708ffa6b fix readme name 2024-11-21 20:54:01 +01:00
LoricAndre b99b68ec64 fix package name 2024-11-21 20:50:13 +01:00
LoricAndre d6c6e25a2f import updated ci 2024-11-21 20:47:53 +01:00
LoricAndre d90e846053 use default token 2024-11-21 20:42:17 +01:00
LoricAndre dc457f67bb fix permissions 2024-11-21 20:40:11 +01:00
LoricAndre aa03781ef7 fix: fix github publish action 2024-11-21 20:36:38 +01:00
LoricAndre 7df8b77739
feat: use clap & derive for options, manpage & completions (#586)
Some checks failed
Build & Test / test (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / test (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / clippy (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / clippy (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / rustfmt (push) Has been cancelled
* feat: use clap & derive for options, manpage & completions

* clippy & fmt

* fix: correctly handle errors

* fix: disable doctest

* fix: readd replstr

* fix: reserve --tmux until it is implemented

* explicit panic messages for history files

* fix after merge

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>
2024-11-20 17:01:08 +01:00
dependabot[bot] a61f67ec63
Bump env_logger from 0.9.3 to 0.11.5 (#601)
Some checks are pending
Build & Test / test (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / test (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / clippy (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / clippy (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / rustfmt (push) Waiting to run
Bumps [env_logger](https://github.com/rust-cli/env_logger) from 0.9.3 to 0.11.5.
- [Release notes](https://github.com/rust-cli/env_logger/releases)
- [Changelog](https://github.com/rust-cli/env_logger/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rust-cli/env_logger/compare/v0.9.3...v0.11.5)

---
updated-dependencies:
- dependency-name: env_logger
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-19 16:42:55 +01:00
otto-dev 064b7b78e2
Return items of multi-select in order of selection (#468)
Some checks are pending
Build & Test / test (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / test (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / clippy (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / clippy (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / rustfmt (push) Waiting to run
* Return items of multi-select in order of selection

* chore: update deps and fix lots of clippy lints

* cargo update

* Fix more lints + ci

* Include repology reference

`skim` is packaged in more then the listed repositories. By referring to repology everyone can easily check whether their package manager of choice has it. Also the list at repology auto updates and includes the information which versions are packaged.

* Add light colors parsing support

* Update ci.yml

* Update ci.yml

* Make bin only deps set optional

* transparency on start

* release notes

* bump

* bump to 0.10.1

* fix: print version from Cargo.toml with latest clap

Upgrading to clap 3 has changed some behavior in clap, as the
version option is automatically populated. This means the custom
code has never been executed.

Lets fix this by using the clap built in functionality to automatically
use the crate version in the builtin version option.

This fixes commit 7d922a02a0

Signed-off-by: Levente Polyak <levente@leventepolyak.net>

* bump version

* update defer-drop to v1.3.0

* Update Cargo.toml

* bump

* Update README.md

* bump

* doc(discord): discord invitation link

* README.md: "Package Managers": add Portage

* document display issue + fix

* Update sk.1

* Update sk-tmux.1

* remove -K and -R flags from tmux popup: uknown flags (#551)

Co-authored-by: ymnejmi <ymnejmi@github.com>

* chore: fix clippy

* Create dependabot.yml

* Bump log from 0.4.17 to 0.4.22 (#581)

Bumps [log](https://github.com/rust-lang/log) from 0.4.17 to 0.4.22.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.17...0.4.22)

---
updated-dependencies:
- dependency-name: log
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump vte from 0.11.0 to 0.13.0 (#582)

Bumps [vte](https://github.com/alacritty/vte) from 0.11.0 to 0.13.0.
- [Release notes](https://github.com/alacritty/vte/releases)
- [Changelog](https://github.com/alacritty/vte/blob/master/CHANGELOG.md)
- [Commits](https://github.com/alacritty/vte/compare/v0.11.0...v0.13.0)

---
updated-dependencies:
- dependency-name: vte
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump derive_builder from 0.11.2 to 0.20.2 (#583)

Bumps [derive_builder](https://github.com/colin-kiegel/rust-derive-builder) from 0.11.2 to 0.20.2.
- [Release notes](https://github.com/colin-kiegel/rust-derive-builder/releases)
- [Commits](https://github.com/colin-kiegel/rust-derive-builder/compare/v0.11.2...v0.20.2)

---
updated-dependencies:
- dependency-name: derive_builder
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* bootstrap contributing.md

* Bump shlex from 1.1.0 to 1.3.0 (#556)

Bumps [shlex](https://github.com/comex/rust-shlex) from 1.1.0 to 1.3.0.
- [Changelog](https://github.com/comex/rust-shlex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/comex/rust-shlex/commits)

---
updated-dependencies:
- dependency-name: shlex
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Cargo: remove unuseful entries (#382)

* feat: use IndexMap to order selected items

* test: add e2e

* update lock after rebase

* prepare merge

* update lock

* clippy

* Delete test.Dockerfile

* fix lockfile

* fix dep version regressions

---------

Signed-off-by: Levente Polyak <levente@leventepolyak.net>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: grant0417 <grant@ggurvis.com>
Co-authored-by: grant0417 <grantgurvis@gmail.com>
Co-authored-by: Merlin <megoettlinger@gmail.com>
Co-authored-by: TD-Sky <three-dim-sky@foxmail.com>
Co-authored-by: Grant G <grant0417@users.noreply.github.com>
Co-authored-by: EdenEast <edenofest@gmail.com>
Co-authored-by: yazgoo <yazgoo@gmail.com>
Co-authored-by: Levente Polyak <levente@leventepolyak.net>
Co-authored-by: onatm <onat.mercan@truelayer.com>
Co-authored-by: yazgoo <yazgoo@users.noreply.github.com>
Co-authored-by: Vitaly Zdanevich <zdanevich.vitaly@ya.ru>
Co-authored-by: sisrfeng <53520949+sisrfeng@users.noreply.github.com>
Co-authored-by: ymnejmi <134085326+ymnejmi@users.noreply.github.com>
Co-authored-by: ymnejmi <ymnejmi@github.com>
Co-authored-by: LoricAndre <loric.andre@pm.me>
Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Marco Ieni <11428655+MarcoIeni@users.noreply.github.com>
2024-11-18 22:16:23 +01:00
LoricAndre c932a1fe1c
chore: remove bitflags (#579)
* chore: remove bitflags

* fix: add args-override-self to allow multiple args occurence

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>
2024-11-18 17:16:12 +01:00
Jorge Gomez 5ef1b593b0
add guix package manager instructions (#487)
Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2024-11-18 17:14:48 +01:00
Swapnil Bhowmik a8869d7d91
Added installation command for void linux (#560)
* Update README.md

* Update README.md

Co-authored-by: yazgoo <yazgoo@users.noreply.github.com>

---------

Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
Co-authored-by: yazgoo <yazgoo@users.noreply.github.com>
2024-11-18 17:13:21 +01:00
LoricAndre 838ba211c6
chore: remove atty (#587)
Co-authored-by: LoricAndre <loric.andre@pm.me>
2024-11-18 17:04:39 +01:00
dependabot[bot] 8222a3f7d2
Remove dependency to time crate (#591)
* Bump time from 0.3.15 to 0.3.36

Bumps [time](https://github.com/time-rs/time) from 0.3.15 to 0.3.36.
- [Release notes](https://github.com/time-rs/time/releases)
- [Changelog](https://github.com/time-rs/time/blob/main/CHANGELOG.md)
- [Commits](https://github.com/time-rs/time/compare/v0.3.15...v0.3.36)

---
updated-dependencies:
- dependency-name: time
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore: remove time from deps

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: LoricAndre <loric.andre@pm.me>
Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2024-11-18 16:57:55 +01:00
Noah 17c38c54e2
ansi parser: fix dim / half-bright (#563)
Some checks failed
Build & Test / test (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / test (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / clippy (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / clippy (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / rustfmt (push) Has been cancelled
* ansi parser: fix dim / half-bright

Fixes #495

* trigger ci

---------

Co-authored-by: LoricAndre <loric.andre@pm.me>
2024-11-12 16:12:27 +01:00
dependabot[bot] d95f6bc0cc
Bump unicode-width from 0.1.10 to 0.2.0 (#580)
Bumps [unicode-width](https://github.com/unicode-rs/unicode-width) from 0.1.10 to 0.2.0.
- [Commits](https://github.com/unicode-rs/unicode-width/commits)

---
updated-dependencies:
- dependency-name: unicode-width
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-12 15:25:11 +01:00
dependabot[bot] da19402871
Bump nix from 0.25.0 to 0.29.0 (#589)
Bumps [nix](https://github.com/nix-rust/nix) from 0.25.0 to 0.29.0.
- [Changelog](https://github.com/nix-rust/nix/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nix-rust/nix/compare/v0.25.0...v0.29.0)

---
updated-dependencies:
- dependency-name: nix
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-12 15:25:02 +01:00
dependabot[bot] d6de28274b
Bump lazy_static from 1.4.0 to 1.5.0 (#590)
Bumps [lazy_static](https://github.com/rust-lang-nursery/lazy-static.rs) from 1.4.0 to 1.5.0.
- [Release notes](https://github.com/rust-lang-nursery/lazy-static.rs/releases)
- [Commits](https://github.com/rust-lang-nursery/lazy-static.rs/compare/1.4.0...1.5.0)

---
updated-dependencies:
- dependency-name: lazy_static
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-12 15:11:28 +01:00
dependabot[bot] d937372313
Bump regex from 1.6.0 to 1.11.1 (#592)
Bumps [regex](https://github.com/rust-lang/regex) from 1.6.0 to 1.11.1.
- [Release notes](https://github.com/rust-lang/regex/releases)
- [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/regex/compare/1.6.0...1.11.1)

---
updated-dependencies:
- dependency-name: regex
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-12 14:48:16 +01:00
Jinzhou Zhang 966d8f50dd
fix #398 shift-up/down was bind to wrong action (#399)
Some checks failed
Build & Test / test (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / test (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / clippy (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / clippy (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / rustfmt (push) Has been cancelled
Was PreviewPageUp/Down, should be PreviewUp/Down

Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2024-11-09 22:35:52 +01:00
Renée Gyllensvaan e0cd6483cf
Pass --cmd-history instead of --history for interactive commands in vim (#461) 2024-11-09 22:18:27 +01:00
Jakub Jirutka 054d76048f
[shell] replace perl with awk in key-bindings.zsh and include date or time if available (#449)
* [shell] replace perl with awk in key-bindings.zsh

Unlike awk, which is even defined in POSIX, perl is not pre-installed
on all *nix systems. This awk command is functionally equivalent to
the original perl command.

* [shell] include date or time in Zsh history if available

If extended_history is enabled, the history widget will display:

     12  2022-04-02   echo "older command"
    123  today'16:30  echo "today's command"
2024-11-09 22:15:59 +01:00
Robert Spencer 703b224e6e
Implement std::ops::Add for AnsiString (#433)
Co-authored-by: LoricAndre <57358788+LoricAndre@users.noreply.github.com>
2024-11-09 22:06:19 +01:00
Marco Ieni 81f391db97
Cargo: remove unuseful entries (#382)
Some checks failed
Build & Test / test (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / test (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / clippy (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Has been cancelled
Build & Test / clippy (macos, macos-latest, stable, x86_64-apple-darwin) (push) Has been cancelled
Build & Test / rustfmt (push) Has been cancelled
2024-11-07 23:57:24 +01:00
dependabot[bot] 17f37b8674
Bump shlex from 1.1.0 to 1.3.0 (#556)
Bumps [shlex](https://github.com/comex/rust-shlex) from 1.1.0 to 1.3.0.
- [Changelog](https://github.com/comex/rust-shlex/blob/master/CHANGELOG.md)
- [Commits](https://github.com/comex/rust-shlex/commits)

---
updated-dependencies:
- dependency-name: shlex
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-07 23:54:56 +01:00
yazgoo 6b63ea452d
Merge pull request #585 from yazgoo/add-contributing.md
bootstrap contributing.md
2024-11-07 19:50:49 +01:00
yazgoo d68823339f bootstrap contributing.md 2024-11-07 19:46:01 +01:00
dependabot[bot] 70f9e4c25d
Bump derive_builder from 0.11.2 to 0.20.2 (#583)
Some checks are pending
Build & Test / test (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / test (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / clippy (linux, ubuntu-latest, stable, x86_64-unknown-linux-musl) (push) Waiting to run
Build & Test / clippy (macos, macos-latest, stable, x86_64-apple-darwin) (push) Waiting to run
Build & Test / rustfmt (push) Waiting to run
Bumps [derive_builder](https://github.com/colin-kiegel/rust-derive-builder) from 0.11.2 to 0.20.2.
- [Release notes](https://github.com/colin-kiegel/rust-derive-builder/releases)
- [Commits](https://github.com/colin-kiegel/rust-derive-builder/compare/v0.11.2...v0.20.2)

---
updated-dependencies:
- dependency-name: derive_builder
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-07 18:24:40 +01:00
dependabot[bot] 0b699430bd
Bump vte from 0.11.0 to 0.13.0 (#582)
Bumps [vte](https://github.com/alacritty/vte) from 0.11.0 to 0.13.0.
- [Release notes](https://github.com/alacritty/vte/releases)
- [Changelog](https://github.com/alacritty/vte/blob/master/CHANGELOG.md)
- [Commits](https://github.com/alacritty/vte/compare/v0.11.0...v0.13.0)

---
updated-dependencies:
- dependency-name: vte
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-07 18:21:43 +01:00
dependabot[bot] 2c733f6199
Bump log from 0.4.17 to 0.4.22 (#581)
Bumps [log](https://github.com/rust-lang/log) from 0.4.17 to 0.4.22.
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.17...0.4.22)

---
updated-dependencies:
- dependency-name: log
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-07 18:19:38 +01:00
LoricAndre bd400934ee
Create dependabot.yml 2024-11-07 18:09:06 +01:00
LoricAndre 8a579837da chore: fix clippy 2024-11-07 17:12:16 +01:00
ymnejmi a00e78c69d
remove -K and -R flags from tmux popup: uknown flags (#551)
Co-authored-by: ymnejmi <ymnejmi@github.com>
2024-11-07 16:46:33 +01:00
yazgoo 291fc34c58
Merge pull request #528 from sisrfeng/wf_PR 2023-05-04 10:51:23 +02:00
yazgoo 46a05168c9
Merge pull request #529 from yazgoo/master 2023-05-04 09:42:57 +02:00
yazgoo cd1f85710c document display issue + fix 2023-05-04 09:38:18 +02:00
sisrfeng 3ebe82e6bf
Update sk-tmux.1 2023-05-01 19:08:00 +08:00
sisrfeng db8e0be287
Update sk.1 2023-05-01 19:07:39 +08:00
yazgoo f9438c390d
Merge pull request #522 from vitaly-zdanevich/patch-1 2023-03-22 18:24:19 +01:00
yazgoo f857e277ac
Merge pull request #523 from TD-Sky/discord 2023-03-19 11:24:28 +01:00
TD-Sky 75a0b81fcb doc(discord): discord invitation link 2023-03-15 11:46:21 +08:00
Vitaly Zdanevich 6b735d1273
README.md: "Package Managers": add Portage 2023-03-13 14:24:30 +03:00
yazgoo db9f194c12
Merge pull request #519 from yazgoo/master 2023-03-02 14:05:48 +01:00
yazgoo bdd5aeb396 bump 2023-03-02 13:54:33 +01:00
yazgoo 08c61964c6
Merge pull request #517 from lotabout/yazgoo-patch-1
Update README.md
2023-02-23 17:57:41 +01:00
yazgoo 589c1bd2a3
Update README.md 2023-02-23 17:56:52 +01:00
yazgoo 29c993cd30
Merge pull request #516 from lotabout/bump
bump
2023-02-23 17:27:58 +01:00
yazgoo 2c2c776da3
bump 2023-02-23 17:23:17 +01:00
yazgoo 67e2f09d46
Update Cargo.toml 2023-02-23 17:12:42 +01:00
yazgoo b4e7a9ae2a
Merge pull request #488 from onatm/master
update defer-drop to v1.3.0
2023-02-23 17:02:50 +01:00
yazgoo fbb270b772
Merge pull request #492 from yazgoo/master 2022-11-08 23:53:12 +01:00
yazgoo 41f139142f bump version 2022-11-08 23:51:53 +01:00
yazgoo 7b58296dcf
Merge pull request #489 from anthraxx/fix/clap-version-option 2022-11-08 23:48:18 +01:00
Levente Polyak 8b145ad23c fix: print version from Cargo.toml with latest clap
Upgrading to clap 3 has changed some behavior in clap, as the
version option is automatically populated. This means the custom
code has never been executed.

Lets fix this by using the clap built in functionality to automatically
use the crate version in the builtin version option.

This fixes commit 7d922a02a0

Signed-off-by: Levente Polyak <levente@leventepolyak.net>
2022-11-03 20:28:56 +01:00
onatm 206843df90
update defer-drop to v1.3.0 2022-11-03 12:28:22 +00:00
yazgoo 9def5d2fce
Merge pull request #475 from EdenEast/chore/cli-deps-cleanup 2022-10-29 08:52:20 +02:00
yazgoo a18291495a
Merge branch 'master' into chore/cli-deps-cleanup 2022-10-29 08:41:34 +02:00
yazgoo afe16670dc
Merge pull request #486 from yazgoo/master 2022-10-28 22:29:16 +02:00
yazgoo d0690b62bd bump to 0.10.1 2022-10-28 22:27:34 +02:00
yazgoo a2edae8fc4
Merge pull request #485 from yazgoo/master 2022-10-28 22:13:17 +02:00
yazgoo af9dd2a868 bump 2022-10-28 22:12:37 +02:00
yazgoo 4647c25af7
Merge pull request #484 from yazgoo/master 2022-10-28 22:10:40 +02:00
yazgoo d1135f42b5 release notes 2022-10-28 22:09:53 +02:00
EdenEast 35d6296560 Make bin only deps set optional 2022-10-27 23:47:28 -04:00
820 changed files with 73980 additions and 10810 deletions

5
.codegraph/.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
# CodeGraph data files — local to each machine, not for committing.
# Ignore everything in .codegraph/ except this file itself, so transient
# files (the database, daemon.pid, sockets, logs) never show up in git.
*
!.gitignore

5
.config/insta.yaml Normal file
View file

@ -0,0 +1,5 @@
behavior:
output: minimal
test:
runner: nextest

79
.config/nextest.toml Normal file
View file

@ -0,0 +1,79 @@
experimental = ["wrapper-scripts"]
# The end-to-end tests drive `sk` inside a Zellij session that each test creates
# and tears down itself (see tests/common/zellij.rs), so — unlike the previous
# tmux-based harness — no shared multiplexer session needs to be set up here.
# Valgrind wrapper for memory leak detection
[scripts.wrapper.valgrind]
command = [
"valgrind",
"--leak-check=full",
"--show-leak-kinds=all",
"--track-origins=yes",
"--error-exitcode=1",
"--suppressions=.config/valgrind.supp"
]
# The end-to-end tests each spin up a full Zellij session (server + client +
# PTY). Running many concurrently overwhelms the machine and makes the
# timing-sensitive tests flaky, so serialize everything in this group while the
# rest of the suite keeps running in parallel. (Under raw `cargo test`, pass
# `--test-threads=1` for the e2e test binaries instead.)
[test-groups]
e2e = { max-threads = 1 }
[profile.default]
fail-fast = false
retries = 2
[[profile.default.overrides]]
filter = 'binary(interactive) | binary(listen) | binary(popup) | binary(execute)'
test-group = 'e2e'
[profile.default.junit]
path = "junit.xml"
[profile.ci]
retries = 9
[[profile.ci.overrides]]
filter = 'binary(interactive) | binary(listen) | binary(popup) | binary(execute)'
test-group = 'e2e'
# Valgrind profile for memory leak detection
# Usage: cargo nextest run --profile valgrind
#
# Note: Valgrind can detect memory leaks but does NOT detect dangling threads.
# For thread leak detection, use ThreadSanitizer instead (see below).
[profile.valgrind]
fail-fast = false
retries = 2
test-threads = 1 # Run tests serially to avoid interleaved valgrind output
[[profile.valgrind.scripts]]
platform = "cfg(unix)"
run-wrapper = "valgrind"
# ThreadSanitizer profile for detecting data races and thread issues
# Usage:
# 1. First build with sanitizer (rebuilds stdlib and all deps):
# RUSTFLAGS="-Zsanitizer=thread" cargo +nightly build --tests -Zbuild-std --target x86_64-unknown-linux-gnu
# 2. Then run tests:
# TSAN_OPTIONS="detect_deadlocks=1" cargo +nightly nextest run --profile tsan --target x86_64-unknown-linux-gnu
#
# Note: ThreadSanitizer can detect:
# - Data races (concurrent unsynchronized access to memory)
# - Deadlocks (with TSAN_OPTIONS=detect_deadlocks=1)
# - Thread leaks (threads not joined before program exit)
#
# Requirements:
# - Rust nightly (for -Zsanitizer and -Zbuild-std flags)
# - The -Zbuild-std flag rebuilds the standard library with ThreadSanitizer
# instrumentation to avoid ABI mismatch errors
#
# Important: This takes a long time on first build as it recompiles everything
# including the standard library with ThreadSanitizer instrumentation.
#
# Environment variables:
# TSAN_OPTIONS="detect_deadlocks=1 second_deadlock_stack=1"
[profile.tsan]
fail-fast = false
retries = 3
test-threads = 1 # TSan requires running tests serially

53
.config/valgrind.supp Normal file
View file

@ -0,0 +1,53 @@
# Valgrind suppressions for skim tests
# This file suppresses known false positives from Rust stdlib and system libraries
# Rust std allocations that are intentionally not freed at program exit
{
rust_std_exit_cleanup
Memcheck:Leak
...
fun:*std*
}
# Thread-local storage cleanup
{
thread_local_cleanup
Memcheck:Leak
...
fun:pthread_create*
}
# Tokio runtime allocations
{
tokio_runtime
Memcheck:Leak
...
fun:*tokio*runtime*
}
# Crossterm/terminal allocations
{
crossterm_terminal
Memcheck:Leak
...
fun:*crossterm*
}
# Libc thread initialization
{
libc_thread_init
Memcheck:Leak
match-leak-kinds: possible
...
fun:calloc
fun:allocate_dtv
}
# DL allocations
{
dl_init
Memcheck:Leak
match-leak-kinds: possible
...
fun:*dl_*
}

8
.dockerignore Normal file
View file

@ -0,0 +1,8 @@
target/
.github/
bin/
plugin/
man/
shell/
*.md
install

4
.envrc Normal file
View file

@ -0,0 +1,4 @@
export _skim_flake_profile="default"
# .envrc.local lets you override the _skim_flake_profile if needed: `export _skim_flake_profile="full"`
source_env_if_exists .envrc.local
use flake .#"$_skim_flake_profile"

9
.githooks/pre-commit Executable file
View file

@ -0,0 +1,9 @@
set -xeuo pipefail
cargo +nightly fmt --check --all
cargo clippy --all-targets -- -Dwarnings
cargo clippy --no-default-features -- -Dwarnings
cargo clippy --no-default-features --features cli -- -Dwarnings
cargo clippy --no-default-features --features image -- -Dwarnings
cargo clippy --no-default-features --features listen -- -Dwarnings
cargo clippy --no-default-features --features frizbee -- -Dwarnings

75
.github/CONTRIBUTING.md vendored Normal file
View file

@ -0,0 +1,75 @@
# Contributor Guide
## Development environment
A [Nix flake](../flake.nix) is provided with opt-in package groups. The default shell contains only the base build tools (`rustup`, `just`); richer environments are available as named shells:
| Shell | Extra packages |
|---|---|
| `nix develop` | `rustup`, `just` |
| `nix develop .#tests` | + nextest, cargo-insta, cargo-llvm-cov, tmux |
| `nix develop .#utils` | + hyperfine, cargo-edit, cargo-public-api, git-cliff |
| `nix develop .#gungraun` | + valgrind, libclang, binutils |
| `nix develop .#bench` | + uv, matplotlib, requests (for `bench.py`) |
| `nix develop .#vagrant` | + vagrant, rsync (for Windows testing) |
| `nix develop .#full` | everything above |
## Running tests
All tests can be run by using [cargo-nextest](https://nexte.st/), which can be installed using `cargo install cargo-nextest` of following the instructions on the website.
You will need `tmux` to run some integration tests.
You can then run `cargo nextest run --release`, which should automatically build a release binary, run the unit tests and the integration tests.
Most integration tests use [cargo insta](https://insta.rs). If you need to add some tests or re-review them, you will need to install it, and run tests with `cargo insta test --tests --review`, which will let you review snapshots.
Note: you can run the tests without `--release`, but expect more flaky tests since the timings will be looser. I would advise testing manually any debug test failure if you have doubts.
Note2: A dockerfile is available if you want to run the tests inside docker. There is little to no cache, so the test will need to rebuild most of the application after each change.
To use it, build the image with `docker build -f test.dockerfile . -t skim-test` then run it using `docker run --rm -it skim-test`.
## Windows testing
A [Vagrantfile](../Vagrantfile) is provided to spin up a headless Windows Server 2022 Core VM via KVM/libvirt, letting you test Windows compatibility without a GUI.
**Host prerequisites (NixOS):**
```nix
virtualisation.libvirtd.enable = true;
users.users.<you>.extraGroups = [ "libvirtd" ]; # log out/in after applying
```
**Usage:**
```sh
nix develop .#vagrant
vagrant up # first boot: ~15-20 min, downloads box + provisions
vagrant ssh # connect to the VM
vagrant halt # stop the VM
vagrant destroy # delete the VM
```
Inside the VM the project root is synced to `C:\vagrant`. Re-sync after local changes with `vagrant rsync`. To build:
```powershell
cd C:\vagrant
cargo build
cargo test
```
## Submitting code
To avoid using up CI minutes uselessly, make sure that :
- You run `cargo clippy` and `cargo fmt` before pushing any code to an open PR.
- Your PR's title respects [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/).
Not respecting these guidelines could end up consuming all our minutes and preventing us from testing and releasing any new code until the end of the month.
Note: a git pre-commit hook is available in .githooks/pre-commit which will make the clippy & fmt checks. To use it, run `git config core.hooksPath ".githooks"`.
## Vibe Coding guidelines
Any code generated partially or completely using LLMs will be treated the same way as if you wrote it yourself.
This means that you are expected to understand if fully and are responsible for it.

33
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

@ -0,0 +1,33 @@
---
name: Bug report
about: Report a bug encountered using `skim`
title: "[BUG] xxx"
labels: bug
assignees: LoricAndre
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Environment (please complete the following information):**
- OS (`uname -a`):
- `skim` version (`sk --version`):
- Shell and version:
- Variables (`env | grep '^SKIM'`):
**Additional context**
Add any other context about the problem here.

27
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,27 @@
version: 2
updates:
- package-ecosystem: cargo
directory: "/"
schedule:
interval: weekly
commit-message:
prefix: chore(deps)
prefix-development: chore(dev-deps)
groups:
cargo-prod:
dependency-type: production
cargo-dev:
dependency-type: development
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: monthly
commit-message:
prefix: chore(deps)
prefix-development: chore(dev-deps)
exclude-paths:
- .github/workflows/release.yml
groups:
gha-prod:
patterns:
- "*"

35
.github/pr-title-checker-config.json vendored Normal file
View file

@ -0,0 +1,35 @@
{
"LABEL": {
"name": "invalid-title",
"color": "B60205"
},
"CHECKS": {
"prefixes": [
"feat: ",
"feature: ",
"fix: ",
"bugfix: ",
"perf: ",
"refactor: ",
"test: ",
"tests: ",
"build: ",
"ci: ",
"doc: ",
"docs: ",
"style: ",
"chore: ",
"other: "
],
"regexp": "^\\w+(\\([a-z_-]+\\))?: ",
"regexpFlags": "",
"ignoreLabels": [
"skip-title-check"
]
},
"MESSAGES": {
"success": "PR title is valid",
"failure": "PR title is invalid",
"notice": ""
}
}

11
.github/pull_request_template.md vendored Normal file
View file

@ -0,0 +1,11 @@
## Checklist
- [ ] The title of my PR follows [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/)
- [ ] I have updated the documentation (`README.md`, comments, `src/manpage.rs` and/or `src/options.rs` if applicable)
- [ ] I have added unit tests
- [ ] I have added [integration tests](https://github.com/skim-rs/skim/tree/master/tests)
- [ ] I have linked all related issues or PRs
## Description of the changes

75
.github/workflows/apt.yml vendored Normal file
View file

@ -0,0 +1,75 @@
name: Deploy APT repo
# Publishes the release's .deb packages as a (flat) APT repository under
# /apt on the gh-pages branch, alongside the coverage report at /coverage.
# keep_files preserves the coverage content (and vice versa).
#
# Requires APT_GPG_PRIVATE_KEY to be set :
#
# ```sh
# export GNUPGHOME=$(mktemp -d)
# cat > "$GNUPGHOME/skim-key" <<'EOF'
# %no-protection
# Key-Type: eddsa
# Key-Curve: ed25519
# Subkey-Type: ecdh
# Subkey-Curve: cv25519
# Name-Real: skim apt repo
# Name-Email: apt@skim-rs.github.io
# Expire-Date: 0
# %commit
# EOF
# gpg --batch --gen-key "$GNUPGHOME/skim-key" 2>&1 | tail -3
# KEYID=$(gpg --list-secret-keys --with-colons apt@skim-rs.github.io | awk -F: '/^sec:/{print $5; exit}')
# # Copy the output of the line below
# gpg --export-secret-keys --armor "$KEYID"
# ```
on:
workflow_call:
inputs:
plan:
required: true
type: string
jobs:
apt:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Download release .deb packages
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ fromJson(inputs.plan).announcement_tag }}
run: |
mkdir -p apt
gh release download "$TAG" --repo "$GITHUB_REPOSITORY" --pattern '*.deb' --dir apt
ls -l apt
- name: Build APT index
run: |
sudo apt-get update
sudo apt-get install -y dpkg-dev apt-utils
cd apt
dpkg-scanpackages --multiversion . > Packages
gzip -k -f Packages
apt-ftparchive release . > Release
- name: Sign the repo
# Skipped when the secret is absent; the repo then stays unsigned
# ([trusted=yes]). Add APT_GPG_PRIVATE_KEY (an ASCII-armored private
# key) to enable signed-by verification.
env:
GPG_KEY: ${{ secrets.APT_GPG_PRIVATE_KEY }}
run: |
echo "$GPG_KEY" | gpg --batch --import
KEYID=$(gpg --list-secret-keys --with-colons | awk -F: '/^sec:/{print $5; exit}')
cd apt
gpg --batch --yes --default-key "$KEYID" --clearsign -o InRelease Release
gpg --batch --yes --default-key "$KEYID" -abs -o Release.gpg Release
gpg --export --armor "$KEYID" > skim-archive-keyring.asc
- name: Deploy to gh-pages under /apt
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: apt
destination_dir: apt
keep_files: true

View file

@ -1,134 +0,0 @@
name: Build & Test
on:
workflow_dispatch:
pull_request:
push:
branches:
- master
jobs:
test:
name: test
runs-on: ${{matrix.os}}
strategy:
matrix:
build: [linux, macos]
include:
- build: linux
os: ubuntu-latest
rust: stable
target: x86_64-unknown-linux-musl
- build: macos
os: macos-latest
rust: stable
target: x86_64-apple-darwin
steps:
- name: Install dependencies (for Linux)
run: |
sudo apt-get install zsh
python3 -V
tmux -V
locale
if: runner.os == 'Linux'
env:
HOMEBREW_NO_AUTO_UPDATE: 1
- name: Install dependencies (for MacOS)
run: |
brew install tmux
brew install zsh
python3 -V
tmux -V
locale
if: runner.os == 'macOS'
env:
HOMEBREW_NO_AUTO_UPDATE: 1
- name: Checkout repository
uses: actions/checkout@v2
with:
fetch-depth: 1
- name: Install correct toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
- name: Build
run: cargo build --release
- name: Run Tests
run: tmux new-session -d && python3 test/test_skim.py --verbose
env:
LC_ALL: en_US.UTF-8
TERM: xterm-256color
- name: Cache
uses: Swatinem/rust-cache@v1
clippy:
name: clippy
runs-on: ${{matrix.os}}
strategy:
matrix:
build: [linux, macos]
include:
- build: linux
os: ubuntu-latest
rust: stable
target: x86_64-unknown-linux-musl
- build: macos
os: macos-latest
rust: stable
target: x86_64-apple-darwin
steps:
- name: Install dependencies (for Linux)
run: |
sudo apt-get install zsh
python3 -V
tmux -V
locale
if: runner.os == 'Linux'
env:
HOMEBREW_NO_AUTO_UPDATE: 1
- name: Install dependencies (for MacOS)
run: |
brew install tmux
brew install zsh
python3 -V
tmux -V
locale
if: runner.os == 'macOS'
env:
HOMEBREW_NO_AUTO_UPDATE: 1
- name: Checkout repository
uses: actions/checkout@v2
with:
fetch-depth: 1
- name: Install correct toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
override: true
components: clippy
- name: Clippy
run: cargo clippy
- name: Cache
uses: Swatinem/rust-cache@v1
rustfmt:
name: rustfmt
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
profile: minimal
components: rustfmt
- name: Check formatting
run: |
cargo fmt --all -- --check

View file

@ -1,29 +0,0 @@
name: Release to crates.io
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
fetch-depth: 1
- name: Install correct toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
- name: Run cargo check
uses: actions-rs/cargo@v1
with:
command: check
- name: Login crates.io
run: cargo login ${CRATES_IO_TOKEN}
env:
CRATES_IO_TOKEN: ${{ secrets.CRATES_IO_TOKEN }}
- run: cargo publish

80
.github/workflows/package.yml vendored Normal file
View file

@ -0,0 +1,80 @@
name: Build Linux packages
# Custom dist job (wired in through `global-artifacts-jobs` in dist-workspace.toml).
# Builds a `.deb` and a `.rpm` for amd64 and arm64, each containing the `sk`
# executable, the man page and the shell completions, then uploads them under an
# `artifacts-*` name so that dist's `host` job attaches them to the GitHub
# Release. Both architectures build natively (no cross-compilation) on their
# respective GitHub-hosted runners.
on:
workflow_call:
inputs:
plan:
required: true
type: string
jobs:
linux-packages:
name: Build .deb and .rpm (${{ matrix.arch }})
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
runner: ubuntu-22.04
- arch: arm64
runner: ubuntu-22.04-arm
permissions:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v7
with:
fetch-depth: 1
- name: Install rust toolchain
run: rustup toolchain install
- name: Check the crate version matches the release plan
# These packages are built from the checked-out source (the same tree
# dist releases from), so the crate version must match the version dist
# planned for this release. Consume the plan to guard against drift.
env:
ANNOUNCEMENT_TAG: ${{ fromJson(inputs.plan).announcement_tag }}
run: |
plan_version="${ANNOUNCEMENT_TAG#v}"
crate_version="$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].version')"
echo "release plan: '$plan_version' | crate: '$crate_version'"
if [ -n "$plan_version" ] && [ "$plan_version" != "$crate_version" ]; then
echo "::error::Cargo.toml version ($crate_version) does not match the release plan ($plan_version)"
exit 1
fi
- name: Install cargo-generate-rpm
uses: taiki-e/install-action@v2.87.1
with:
tool: cargo-generate-rpm@0.21
- name: Setup cargo cache
uses: Swatinem/rust-cache@v2
# cargo-deb's prebuilt binary links GLIBC_2.39, which ubuntu-22.04
# (glibc 2.35) lacks. Build it from source so it links the runner's
# glibc. We stay on 22.04 on purpose: the sk binary it packages must
# keep a low glibc floor for the .deb to run on older distros.
- name: Install cargo-deb
run: cargo install 'cargo-deb@^3' --locked
- name: Build release binary
run: cargo build --release --bin sk
- name: Build .deb package
run: cargo deb --no-build
- name: Build .rpm package
run: cargo generate-rpm
- name: Collect packages
run: |
mkdir -p target/linux-packages
cp target/debian/*.deb target/linux-packages/
cp target/generate-rpm/*.rpm target/linux-packages/
ls -l target/linux-packages/
- name: Upload packages
uses: actions/upload-artifact@v7
with:
name: artifacts-linux-packages-${{ matrix.arch }}
path: target/linux-packages/*
if-no-files-found: error

13
.github/workflows/pr-auto-update.yml vendored Normal file
View file

@ -0,0 +1,13 @@
name: Pull request update
on:
push:
branches: [master]
jobs:
autoupdate:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: allonsy-studio/actions-pr-auto-update@5fbf9661cbf5c1f092ec33e5a1cfbe70dc5bd81c

104
.github/workflows/pr.yml vendored Normal file
View file

@ -0,0 +1,104 @@
on:
pull_request:
types:
- opened
- synchronize
- reopened
- edited
jobs:
check-title:
runs-on: ubuntu-latest
steps:
- name: Create git-cliff mock context
env:
CONTEXT: >
[
{
"commits": [{
"id": "foo",
"message": $msg,
"links": [],
"author": {
"name": "",
"timestamp": 1
},
"committer": {
"name": "",
"timestamp": 1
},
"merge_commit": false,
"github": {
"pr_labels": [],
"is_first_time": false
},
"gitlab": {
"pr_labels": [],
"is_first_time": false
},
"gitea": {
"pr_labels": [],
"is_first_time": false
},
"bitbucket": {
"pr_labels": [],
"is_first_time": false
},
"azure_devops": {
"pr_labels": [],
"is_first_time": false
},
}],
"github": { "contributors": [] },
"gitlab": { "contributors": [] },
"gitea": { "contributors": [] },
"bitbucket": { "contributors": [] },
"azure_devops": { "contributors": [] },
"submodule_commits": {}
}
]
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
jq -nc --arg msg "$PR_TITLE" "$CONTEXT" | tee git-cliff-context.json
- name: Generate mock changelog entry
id: git-cliff
uses: orhun/git-cliff-action@v4
with:
args: >
--from-context git-cliff-context.json -s all
- name: Check generated entry
run: |
echo "Generated:"
cat "${{ steps.git-cliff.outputs.changelog }}"
echo "Checking..."
cat "${{ steps.git-cliff.outputs.changelog }}" | grep -Ev '^(## \[unreleased\]|)$' | grep -q '^.\+$'
check-generated-files:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Cache
uses: Swatinem/rust-cache@v2
with:
key: ${{ runner.os }}
add-job-id-key: "false"
add-rust-environment-hash-key: "false"
env-vars: "____"
cache-on-failure: "true"
cache-all-crates: "true"
- name: Generate files
run: |
cargo run --locked -- --man > ./man/man1/sk.1
cargo run --locked -- --shell bash > ./shell/completion.bash
cargo run --locked -- --shell zsh > ./shell/completion.zsh
cargo run --locked -- --shell fish > ./shell/completion.fish
cargo run --locked -- --shell nushell > ./shell/completion.nu
- name: Check diff
run: |
if git diff --exit-code; then
echo "No changes to generated files found, PR is safe to merge"
exit 0
else
echo "Found changes to generated files, regenerate them locally and push them using 'just generate-files'"
exit 1
fi

View file

@ -1,140 +0,0 @@
name: Publish to Github
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
jobs:
create-release:
name: Create Github Release
runs-on: ubuntu-latest
steps:
- name: Create artifacts directory
run: mkdir artifacts
- name: Get the release version from the tag
run: |
# Apparently, this is the right way to get a tag name. Really?
#
# See: https://github.community/t5/GitHub-Actions/How-to-get-just-the-tag-name/m-p/32167/highlight/true#M1027
echo "SK_VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
- name: Create Release
id: release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: ${{ env.SK_VERSION }}
body: ${{ env.SK_VERSION }}
draft: false
prerelease: false
- name: Save release upload URL to artifact
run: echo "${{ steps.release.outputs.upload_url }}" > artifacts/release-upload-url
- name: Save version number to artifact
run: echo "${{ env.SK_VERSION }}" > artifacts/release-version
- name: Upload artifacts
uses: actions/upload-artifact@v1
with:
name: artifacts
path: artifacts
publish-to-github:
name: Publish to Github
needs: ['create-release']
runs-on: ${{matrix.os}}
strategy:
matrix:
build: [linux, arm, arm-v7, macos]
include:
- build: linux
os: ubuntu-latest
rust: stable
target: x86_64-unknown-linux-musl
cross: false
- build: arm
os: ubuntu-latest
rust: stable
target: arm-unknown-linux-gnueabihf
cross: true
- build: arm-v7
os: ubuntu-latest
rust: stable
target: armv7-unknown-linux-gnueabihf
cross: true
- build: macos
os: macos-latest
rust: stable
target: x86_64-apple-darwin
cross: false
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
fetch-depth: 1
- name: Install correct toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust }}
target: ${{ matrix.target }}
use-cross: ${{ matrix.cross }}
override: true
- name: Get release download URL
uses: actions/download-artifact@v1
with:
name: artifacts
path: artifacts
- name: Set release upload URL and release version
shell: bash
run: |
release_upload_url="$(cat artifacts/release-upload-url)"
echo "RELEASE_UPLOAD_URL=$release_upload_url" >> $GITHUB_ENV
echo "release upload url: $RELEASE_UPLOAD_URL"
release_version="$(cat artifacts/release-version)"
echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV
echo "release version: $RELEASE_VERSION"
- name: build
uses: actions-rs/cargo@v1
with:
use-cross: ${{ matrix.cross }}
command: build
args: --release --target ${{ matrix.target }}
- name: Package Artifacts
run: |
src=$(pwd)
stage=
case $RUNNER_OS in
Linux)
stage=$(mktemp -d)
;;
macOS)
stage=$(mktemp -d -t tmp)
;;
esac
echo "src is: $src"
echo "stage is: $stage"
cp target/${{ matrix.target }}/release/sk $stage/
cd $stage
ASSET_NAME="skim-${{ env.RELEASE_VERSION }}-${{ matrix.target }}.tar.gz"
ASSET_PATH="$src/$ASSET_NAME"
echo "ASSET_NAME=$ASSET_NAME" >> $GITHUB_ENV
echo "ASSET_PATH=$ASSET_PATH" >> $GITHUB_ENV
tar czf $ASSET_PATH *
cd $src
- name: Upload release archive
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ env.RELEASE_UPLOAD_URL }}
asset_path: ${{ env.ASSET_PATH }}
asset_name: ${{ env.ASSET_NAME }}
asset_content_type: application/octet-stream

24
.github/workflows/publish.yml vendored Normal file
View file

@ -0,0 +1,24 @@
name: Publish cargo crate
on:
workflow_call:
inputs:
plan:
required: true
type: string
jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v7
- run: rustup toolchain install
- name: Cache
uses: Swatinem/rust-cache@v2
- name: Login
run: cargo login ${CRATES_IO_TOKEN}
env:
CRATES_IO_TOKEN: ${{ secrets.CRATES_IO_TOKEN }}
- name: Publish
run: cargo publish

175
.github/workflows/release-pr.yml vendored Normal file
View file

@ -0,0 +1,175 @@
name: Release PR
on:
push:
branches: [master]
pull_request:
branches: [master]
types: [closed]
permissions:
contents: read
concurrency:
group: release-pr-${{ github.event_name }}
cancel-in-progress: true
jobs:
prepare:
if: "${{ github.event_name == 'push' && !startsWith(github.event.head_commit.message, 'release: v') }}"
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
changelog: ${{ steps.changelog.outputs.content }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
with:
fetch-depth: 0
persist-credentials: false
- uses: taiki-e/install-action@742a3317eac7bd62f91cd888b4eead5e784ba833 # v2
with:
tool: git-cliff
- id: version
name: Compute next version
shell: bash
run: |
version="$(git cliff --bumped-version)"
version="${version#v}"
[[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$ ]]
if git rev-parse --verify --quiet "refs/tags/v$version"; then
echo "No releasable changes since v$version"
exit 0
fi
echo "version=$version" >> "$GITHUB_OUTPUT"
- name: Prepare release files
if: steps.version.outputs.version != ''
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
sed -i 's/^version = ".*"/version = "'"$VERSION"'"/' Cargo.toml
SKIM_DEFAULT_OPTIONS='' cargo run -- --man > man/man1/sk.1
SKIM_DEFAULT_OPTIONS='' cargo run -- --shell bash > shell/completion.bash
SKIM_DEFAULT_OPTIONS='' cargo run -- --shell zsh > shell/completion.zsh
SKIM_DEFAULT_OPTIONS='' cargo run -- --shell fish > shell/completion.fish
SKIM_DEFAULT_OPTIONS='' cargo run -- --shell nushell > shell/completion.nu
git cliff -p CHANGELOG.md -t "v$VERSION" -u
cargo generate-lockfile
echo "$VERSION" > shell/version.txt
git diff --binary -- CHANGELOG.md Cargo.lock Cargo.toml man shell > "$RUNNER_TEMP/release.patch"
test -s "$RUNNER_TEMP/release.patch"
- id: changelog
name: Render release changelog
if: steps.version.outputs.version != ''
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
delimiter="changelog-$(cat /proc/sys/kernel/random/uuid)"
{
echo "content<<$delimiter"
git cliff -u -t "v$VERSION" --strip all
echo "$delimiter"
} >> "$GITHUB_OUTPUT"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: steps.version.outputs.version != ''
with:
name: release-${{ github.sha }}
path: ${{ runner.temp }}/release.patch
if-no-files-found: error
update-pr:
needs: prepare
if: needs.prepare.outputs.version != ''
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
with:
ref: ${{ github.sha }}
fetch-depth: 0
persist-credentials: false
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: release-${{ github.sha }}
path: ${{ runner.temp }}/release
- name: Create the release commit
env:
VERSION: ${{ needs.prepare.outputs.version }}
run: |
git fetch origin master
test "$(git rev-parse origin/master)" = "$GITHUB_SHA"
git apply "$RUNNER_TEMP/release/release.patch"
unexpected="$(git diff --name-only | grep -Ev '^(CHANGELOG.md|Cargo.lock|Cargo.toml|man/|shell/)' || true)"
test -z "$unexpected"
git switch -c release-pr
git config user.name github-actions[bot]
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
git add CHANGELOG.md Cargo.lock Cargo.toml man shell
git commit -m "release: v$VERSION"
# Mint the write token only on this fresh runner, after repository code has finished executing.
- id: app-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
with:
client-id: ${{ vars.SKIM_RS_BOT_CLIENT_ID }}
private-key: ${{ secrets.SKIM_RS_BOT_PRIVATE_KEY }}
permission-contents: write
permission-pull-requests: write
- name: Push branch and create or update PR
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
VERSION: ${{ needs.prepare.outputs.version }}
CHANGELOG: ${{ needs.prepare.outputs.changelog }}
shell: bash
run: |
auth="$(printf 'x-access-token:%s' "$GH_TOKEN" | base64 -w0)"
git -c http.extraheader="AUTHORIZATION: basic $auth" push --force origin HEAD:release-pr
cat > "$RUNNER_TEMP/pr-body.md" <<EOF
Automated release preparation for v$VERSION.
This branch is rebuilt from master as one commit on every push. Merge it with the repository's required squash merge; the resulting commit will be tagged v$VERSION and picked up by the release workflow.
EOF
printf '%s\n' "$CHANGELOG" >> "$RUNNER_TEMP/pr-body.md"
pr="$(gh pr list --base master --head release-pr --state open --json number --jq '.[0].number')"
if [[ -n "$pr" ]]; then
gh pr edit "$pr" --title "release: v$VERSION" --body-file "$RUNNER_TEMP/pr-body.md"
else
gh pr create --base master --head release-pr --title "release: v$VERSION" --body-file "$RUNNER_TEMP/pr-body.md"
fi
tag:
if: >-
github.event_name == 'pull_request' &&
github.event.pull_request.merged == true &&
github.event.pull_request.head.ref == 'release-pr' &&
github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
with:
ref: ${{ github.event.pull_request.merge_commit_sha }}
persist-credentials: false
sparse-checkout: Cargo.toml
- id: release
name: Validate release merge
env:
PR_TITLE: ${{ github.event.pull_request.title }}
shell: bash
run: |
version="$(awk -F '"' '/^version = "/ { print $2; exit }' Cargo.toml)"
[[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$ ]]
test "$PR_TITLE" = "release: v$version"
echo "tag=v$version" >> "$GITHUB_OUTPUT"
# No checked-out code runs after this repository-scoped, contents-only token is minted.
- id: app-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
with:
client-id: ${{ vars.SKIM_RS_BOT_CLIENT_ID }}
private-key: ${{ secrets.SKIM_RS_BOT_PRIVATE_KEY }}
permission-contents: write
- name: Tag the squash commit
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
SHA: ${{ github.event.pull_request.merge_commit_sha }}
TAG: ${{ steps.release.outputs.tag }}
run: gh api --method POST "repos/$GITHUB_REPOSITORY/git/refs" -f ref="refs/tags/$TAG" -f sha="$SHA"

359
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,359 @@
# This file was autogenerated by dist: https://axodotdev.github.io/cargo-dist
#
# Copyright 2022-2024, axodotdev
# SPDX-License-Identifier: MIT or Apache-2.0
#
# CI that:
#
# * checks for a Git Tag that looks like a release
# * builds artifacts with dist (archives, installers, hashes)
# * uploads those artifacts to temporary workflow zip
# * on success, uploads the artifacts to a GitHub Release
#
# Note that the GitHub Release will be created with a generated
# title/body based on your changelogs.
name: Release
permissions:
"contents": "write"
# This task will run whenever you push a git tag that looks like a version
# like "1.0.0", "v0.1.0-prerelease.1", "my-app/0.1.0", "releases/v1.0.0", etc.
# Various formats will be parsed into a VERSION and an optional PACKAGE_NAME, where
# PACKAGE_NAME must be the name of a Cargo package in your workspace, and VERSION
# must be a Cargo-style SemVer Version (must have at least major.minor.patch).
#
# If PACKAGE_NAME is specified, then the announcement will be for that
# package (erroring out if it doesn't have the given version or isn't dist-able).
#
# If PACKAGE_NAME isn't specified, then the announcement will be for all
# (dist-able) packages in the workspace with that version (this mode is
# intended for workspaces with only one dist-able package, or with all dist-able
# packages versioned/released in lockstep).
#
# If you push multiple tags at once, separate instances of this workflow will
# spin up, creating an independent announcement for each one. However, GitHub
# will hard limit this to 3 tags per commit, as it will assume more tags is a
# mistake.
#
# If there's a prerelease-style suffix to the version, then the release(s)
# will be marked as a prerelease.
on:
pull_request:
push:
tags:
- '**[0-9]+.[0-9]+.[0-9]+*'
jobs:
# Run 'dist plan' (or host) to determine what tasks we need to do
plan:
runs-on: "ubuntu-22.04"
outputs:
val: ${{ steps.plan.outputs.manifest }}
tag: ${{ !github.event.pull_request && github.ref_name || '' }}
tag-flag: ${{ !github.event.pull_request && format('--tag={0}', github.ref_name) || '' }}
publishing: ${{ !github.event.pull_request }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
submodules: recursive
- name: Install dist
# we specify bash to get pipefail; it guards against the `curl` command
# failing. otherwise `sh` won't catch that `curl` returned non-0
shell: bash
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.32.0/cargo-dist-installer.sh | sh"
- name: Cache dist
uses: actions/upload-artifact@v7
with:
name: cargo-dist-cache
path: ~/.cargo/bin/dist
# sure would be cool if github gave us proper conditionals...
# so here's a doubly-nested ternary-via-truthiness to try to provide the best possible
# functionality based on whether this is a pull_request, and whether it's from a fork.
# (PRs run on the *source* but secrets are usually on the *target* -- that's *good*
# but also really annoying to build CI around when it needs secrets to work right.)
- id: plan
run: |
dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json
echo "dist ran successfully"
cat plan-dist-manifest.json
echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT"
- name: "Upload dist-manifest.json"
uses: actions/upload-artifact@v7
with:
name: artifacts-plan-dist-manifest
path: plan-dist-manifest.json
custom-test:
uses: ./.github/workflows/test.yml
secrets: inherit
permissions:
"contents": "write"
"id-token": "write"
# Build and packages all the platform-specific things
build-local-artifacts:
name: build-local-artifacts (${{ join(matrix.targets, ', ') }})
# Let the initial task tell us to not run (currently very blunt)
needs:
- plan
- custom-test
if: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix.include != null && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload') }}
strategy:
fail-fast: false
# Target platforms/runners are computed by dist in create-release.
# Each member of the matrix has the following arguments:
#
# - runner: the github runner
# - dist-args: cli flags to pass to dist
# - install-dist: expression to run to install dist on the runner
#
# Typically there will be:
# - 1 "global" task that builds universal installers
# - N "local" tasks that build each platform's binaries and platform-specific installers
matrix: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }}
runs-on: ${{ matrix.runner }}
container: ${{ matrix.container && matrix.container.image || null }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json
steps:
- name: enable windows longpaths
run: |
git config --global core.longpaths true
- uses: actions/checkout@v6
with:
persist-credentials: false
submodules: recursive
- name: Install Rust non-interactively if not already installed
if: ${{ matrix.container }}
run: |
if ! command -v cargo > /dev/null 2>&1; then
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
fi
- name: Install dist
run: ${{ matrix.install_dist.run }}
# Get the dist-manifest
- name: Fetch local artifacts
uses: actions/download-artifact@v8
with:
pattern: artifacts-*
path: target/distrib/
merge-multiple: true
- name: Install dependencies
run: |
${{ matrix.packages_install }}
- name: Build artifacts
run: |
# Actually do builds and make zips and whatnot
dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json
echo "dist ran successfully"
- id: cargo-dist
name: Post-build
# We force bash here just because github makes it really hard to get values up
# to "real" actions without writing to env-vars, and writing to env-vars has
# inconsistent syntax between shell and powershell.
shell: bash
run: |
# Parse out what we just built and upload it to scratch storage
echo "paths<<EOF" >> "$GITHUB_OUTPUT"
dist print-upload-files-from-manifest --manifest dist-manifest.json >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
- name: "Upload artifacts"
uses: actions/upload-artifact@v7
with:
name: artifacts-build-local-${{ join(matrix.targets, '_') }}
path: |
${{ steps.cargo-dist.outputs.paths }}
${{ env.BUILD_MANIFEST_NAME }}
# Build and package all the platform-agnostic(ish) things
build-global-artifacts:
needs:
- plan
- build-local-artifacts
runs-on: "ubuntu-22.04"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
submodules: recursive
- name: Install cached dist
uses: actions/download-artifact@v8
with:
name: cargo-dist-cache
path: ~/.cargo/bin/
- run: chmod +x ~/.cargo/bin/dist
# Get all the local artifacts for the global tasks to use (for e.g. checksums)
- name: Fetch local artifacts
uses: actions/download-artifact@v8
with:
pattern: artifacts-*
path: target/distrib/
merge-multiple: true
- id: cargo-dist
shell: bash
run: |
dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json
echo "dist ran successfully"
# Parse out what we just built and upload it to scratch storage
echo "paths<<EOF" >> "$GITHUB_OUTPUT"
jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
- name: "Upload artifacts"
uses: actions/upload-artifact@v7
with:
name: artifacts-build-global
path: |
${{ steps.cargo-dist.outputs.paths }}
${{ env.BUILD_MANIFEST_NAME }}
custom-package:
needs:
- plan
- build-local-artifacts
uses: ./.github/workflows/package.yml
with:
plan: ${{ needs.plan.outputs.val }}
secrets: inherit
# Determines if we should publish/announce
host:
needs:
- plan
- build-local-artifacts
- build-global-artifacts
- custom-package
# Only run if we're "publishing", and only if plan, local and global didn't fail (skipped is fine)
if: ${{ always() && needs.plan.result == 'success' && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.custom-package.result == 'skipped' || needs.custom-package.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
runs-on: "ubuntu-22.04"
outputs:
val: ${{ steps.host.outputs.manifest }}
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
submodules: recursive
- name: Install cached dist
uses: actions/download-artifact@v8
with:
name: cargo-dist-cache
path: ~/.cargo/bin/
- run: chmod +x ~/.cargo/bin/dist
# Fetch artifacts from scratch-storage
- name: Fetch artifacts
uses: actions/download-artifact@v8
with:
pattern: artifacts-*
path: target/distrib/
merge-multiple: true
- id: host
shell: bash
run: |
dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json
echo "artifacts uploaded and released successfully"
cat dist-manifest.json
echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT"
- name: "Upload dist-manifest.json"
uses: actions/upload-artifact@v7
with:
# Overwrite the previous copy
name: artifacts-dist-manifest
path: dist-manifest.json
# Create a GitHub Release while uploading all files to it
- name: "Download GitHub Artifacts"
uses: actions/download-artifact@v8
with:
pattern: artifacts-*
path: artifacts
merge-multiple: true
- name: Cleanup
run: |
# Remove the granular manifests
rm -f artifacts/*-dist-manifest.json
- name: Create GitHub Release
env:
PRERELEASE_FLAG: "${{ fromJson(steps.host.outputs.manifest).announcement_is_prerelease && '--prerelease' || '' }}"
ANNOUNCEMENT_TITLE: "${{ fromJson(steps.host.outputs.manifest).announcement_title }}"
ANNOUNCEMENT_BODY: "${{ fromJson(steps.host.outputs.manifest).announcement_github_body }}"
RELEASE_COMMIT: "${{ github.sha }}"
run: |
# Write and read notes from a file to avoid quoting breaking things
echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt
gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/*
custom-publish:
needs:
- plan
- host
if: ${{ !fromJson(needs.plan.outputs.val).announcement_is_prerelease || fromJson(needs.plan.outputs.val).publish_prereleases }}
uses: ./.github/workflows/publish.yml
with:
plan: ${{ needs.plan.outputs.val }}
secrets: inherit
# publish jobs get escalated permissions
permissions:
"id-token": "write"
"packages": "write"
custom-winget:
needs:
- plan
- host
if: ${{ !fromJson(needs.plan.outputs.val).announcement_is_prerelease || fromJson(needs.plan.outputs.val).publish_prereleases }}
uses: ./.github/workflows/winget.yml
with:
plan: ${{ needs.plan.outputs.val }}
secrets: inherit
# publish jobs get escalated permissions
permissions:
"id-token": "write"
"packages": "write"
custom-apt:
needs:
- plan
- host
if: ${{ !fromJson(needs.plan.outputs.val).announcement_is_prerelease || fromJson(needs.plan.outputs.val).publish_prereleases }}
uses: ./.github/workflows/apt.yml
with:
plan: ${{ needs.plan.outputs.val }}
secrets: inherit
# publish jobs get escalated permissions
permissions:
"contents": "write"
"id-token": "write"
announce:
needs:
- plan
- host
- custom-publish
- custom-winget
- custom-apt
# use "always() && ..." to allow us to wait for all publish jobs while
# still allowing individual publish jobs to skip themselves (for prereleases).
# "host" however must run to completion, no skipping allowed!
if: ${{ always() && needs.host.result == 'success' && (needs.custom-publish.result == 'skipped' || needs.custom-publish.result == 'success') && (needs.custom-winget.result == 'skipped' || needs.custom-winget.result == 'success') && (needs.custom-apt.result == 'skipped' || needs.custom-apt.result == 'success') }}
runs-on: "ubuntu-22.04"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
submodules: recursive

304
.github/workflows/test.yml vendored Normal file
View file

@ -0,0 +1,304 @@
name: Build & Test
on:
workflow_dispatch:
workflow_call:
inputs:
plan:
required: false
type: string
# pull_request: # No need to trigger on PR, cargo-dist already does
push:
branches:
- master
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
nextest:
runs-on: ${{matrix.runner}}
# Cap the wall-clock so a harness regression that makes the Zellij pane never
# render (each `wait_ready` then burning its full budget) fails the leg in
# minutes instead of letting a serialized e2e run drag on for hours.
timeout-minutes: 45
strategy:
# Report every OS independently: a failure on one (e.g. Windows) must not
# cancel the others, so we still get a clear signal from Linux and macOS.
fail-fast: false
matrix: &matrix
build: [linux, macos, windows]
include:
- build: linux
runner: ubuntu-latest
- build: macos
runner: macos-latest
- build: windows
runner: windows-latest
permissions:
contents: read
steps:
- &zellij-install
# The e2e tests drive `sk` inside a Zellij session (Zellij 0.44+ runs on
# Linux, macOS and Windows). taiki-e/install-action fetches a prebuilt
# binary on Linux and macOS; it has no Windows binary and `cargo install
# zellij` fails building openssl from source on the runner, so Windows
# installs via winget below.
name: Install zellij (Linux/macOS)
if: runner.os != 'Windows'
uses: taiki-e/install-action@v2.87.1
with:
tool: zellij@0.44.3
- name: Install zellij (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
winget install --exact --id Zellij.Zellij --version 0.44.3 --source winget --accept-source-agreements --accept-package-agreements --disable-interactivity
# The winget package is an MSI that installs zellij and adds its dir to
# the machine PATH in the registry. Neither the current process nor
# GITHUB_PATH sees that until reloaded, so refresh PATH from the
# registry (with a Program Files fallback), then expose zellij's dir to
# later steps.
$env:PATH = [Environment]::GetEnvironmentVariable('Path','Machine') + ';' + [Environment]::GetEnvironmentVariable('Path','User') + ';' + $env:PATH
$exe = (Get-Command zellij.exe -ErrorAction SilentlyContinue).Source
if (-not $exe) { $exe = (Get-ChildItem 'C:\Program Files' -Recurse -Filter zellij.exe -ErrorAction SilentlyContinue | Select-Object -First 1).FullName }
if (-not $exe) { throw "zellij.exe not found after winget install" }
$dir = Split-Path -Parent $exe
Write-Host "zellij installed at: $dir"
Add-Content -Path $env:GITHUB_PATH -Value $dir
& $exe --version
- name: Show locale
run: locale
if: runner.os != 'Windows'
- &checkout
name: Checkout repository
uses: actions/checkout@v7
with:
fetch-depth: 1
- &toolchain
name: Install rust toolchain
run: rustup toolchain install
- &nextest-install
name: Install nextest
uses: taiki-e/install-action@v2.87.1
with:
tool: nextest@0.9
- &cache
name: Setup cargo cache
uses: Swatinem/rust-cache@v2
- name: Run doctests
run: cargo test --doc
- name: "Run tests"
# Do not use `--all-targets` to avoid running benches
run: cargo nextest run --release --locked --profile ci --bins --lib --examples --tests
env:
LC_ALL: en_US.UTF-8
TERM: xterm-256color
- name: Show snapshot diffs on failure
if: failure()
run: |
find tests/snapshots/ -name "*.snap.new" | while read -r new_snap; do
base="${new_snap%.new}"
echo "=== Snapshot diff: $base ==="
echo "new: $new_snap"
cat "$new_snap"
if [ -f "$base" ]; then
echo "old: $base"
cat "$base"
diff "$base" "$new_snap"
fi
done
shell: bash
coverage:
runs-on: ubuntu-latest
continue-on-error: true
permissions:
contents: write
steps:
- *zellij-install
- *checkout
- *toolchain
- *nextest-install
- uses: taiki-e/install-action@v2.87.1
with:
tool: cargo-llvm-cov@0.8
- *cache
- name: "Run tests with coverage"
# Do not use `--all-targets` to avoid running benches
run: |
cargo +nightly llvm-cov nextest --locked --release --profile ci --branch --bins --lib --examples --tests --no-report
cargo +nightly llvm-cov report --release --cobertura --output-path coverage.xml
cargo +nightly llvm-cov report --release --html
echo "COVERAGE_PERCENT=$(cargo +nightly llvm-cov report --release | tail -n1 | awk '{ print $13 }')" | tee --append $GITHUB_ENV
env:
LC_ALL: en_US.UTF-8
TERM: xterm-256color
- name: "Generate coverage badge"
if: &if-master github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
uses: emibcn/badge-action@v2.0.4
with:
label: 'Coverage'
status: ${{ env.COVERAGE_PERCENT }}
color: 'blue'
path: 'target/llvm-cov/html/coverage.svg'
- name: "Deploy coverage to gh-pages under /coverage"
if: *if-master
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: target/llvm-cov/html
destination_dir: coverage
keep_files: true
clippy:
runs-on: ${{matrix.runner}}
strategy:
matrix: *matrix
steps:
- *checkout
- *toolchain
- *cache
- name: Clippy
run: cargo clippy
rustfmt:
runs-on: ${{matrix.runner}}
strategy:
matrix: *matrix
steps:
- *checkout
- *toolchain
- name: Check formatting
run: |
cargo fmt --all -- --check
clippy-no-default-features:
runs-on: ${{matrix.runner}}
strategy:
matrix: *matrix
steps:
- *checkout
- *toolchain
- *cache
- name: Run clippy with specific features
run: |
cargo clippy --locked --no-default-features -- -Dwarnings
cargo clippy --locked --no-default-features --features cli -- -Dwarnings
cargo clippy --locked --no-default-features --features image -- -Dwarnings
cargo clippy --locked --no-default-features --features listen -- -Dwarnings
cargo clippy --locked --no-default-features --features frizbee -- -Dwarnings
msrv:
runs-on: ubuntu-latest
steps:
- *checkout
- *toolchain
- name: MSRV Verify
run: cargo +1.91.0 build --release --locked --all-targets
fuzz:
permissions:
contents: read
runs-on: ${{matrix.runner}}
strategy:
matrix: *matrix
steps:
- *checkout
- *toolchain
- *cache
- name: Set up MSVC dev environment
# Without a sanitizer, libFuzzer's coverage instrumentation fails to
# link on Windows: neither MSVC's link.exe nor LLD's COFF driver
# synthesize the __start/__stop section-boundary symbols it needs
# (an ELF/Mach-O-only linker feature). MSVC's AddressSanitizer
# runtime provides an equivalent shim for those symbols, so it's
# required for the link to succeed at all, not just extra bug
# detection. It needs its DLL directory on PATH at run time, which
# this action sets up (see
# https://rust-fuzz.github.io/book/cargo-fuzz/windows/setup.html).
if: runner.os == 'Windows'
uses: ilammy/msvc-dev-cmd@v1
- uses: taiki-e/install-action@v2.87.1
with:
tool: cargo-fuzz@0.13
- name: Run fuzz targets
shell: bash
# 5 targets * 60s = 5 minutes of fuzzing total per run.
# Force the actual host target: the sanitizer build can't link
# against a statically-linked libc (e.g. if CARGO_BUILD_TARGET
# defaults to a musl target elsewhere in the matrix).
run: |
host_target="$(rustc +nightly -vV | sed -n 's/^host: //p')"
if [ "$RUNNER_OS" = "Windows" ]; then
# Git Bash's own coreutils `link` (for hardlinks) sits ahead of
# the MSVC one on PATH within this shell, so cargo/rustc would
# otherwise invoke the wrong `link.exe`. Point at the real MSVC
# linker explicitly, using the dev env ilammy/msvc-dev-cmd set up.
export CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER="${VCToolsInstallDir}bin\\HostX64\\x64\\link.exe"
fi
for target in ansi_strip field_extract fuzzy_match query_match keymap_parse; do
cargo +nightly fuzz run "$target" --target "$host_target" -- -max_total_time=60
done
- name: Upload crash artifacts
if: failure()
uses: actions/upload-artifact@v7
with:
name: fuzz-crashes-${{ runner.os }}
path: fuzz/artifacts/
if-no-files-found: ignore
# Single aggregate status check for branch protection / repository rulesets,
# so they don't need to enumerate every matrix leg by name individually.
public-api:
name: Check for public API breaking changes
permissions:
contents: read
continue-on-error: true
runs-on: ubuntu-latest
steps:
- *checkout
- *toolchain
- *cache
- uses: taiki-e/install-action@v2.87.1
with:
tool: cargo-public-api@0.52
- run: rustup toolchain install nightly-x86_64-unknown-linux-gnu
name: Install nightly toolchain
# Compare two repository revisions so cargo-public-api uses their committed
# lockfiles. A registry comparison resolves the released crate again and can
# fail when a new, broken transitive dependency is published.
- run: git fetch --force --tags
name: Fetch release tags
- run: |
latest_tag="$(git tag --list 'v*' --sort=-version:refname | head -n1)"
cargo public-api diff "${latest_tag}..HEAD" --deny removed --deny changed
name: Run cargo-public-api
ci-success:
name: CI Success
if: always()
needs:
- nextest
- coverage
- clippy
- rustfmt
- clippy-no-default-features
- msrv
- fuzz
runs-on: ubuntu-latest
steps:
- name: Check all required jobs succeeded
run: |
echo "${{ toJson(needs) }}"
if [[ "${{ contains(needs.*.result, 'failure') }}" == "true" || "${{ contains(needs.*.result, 'cancelled') }}" == "true" ]]; then
echo "One or more required jobs failed or were cancelled."
exit 1
fi
echo "All required jobs passed."

38
.github/workflows/winget.yml vendored Normal file
View file

@ -0,0 +1,38 @@
name: Publish to winget
# Custom dist publish job (wired in through `publish-jobs` in dist-workspace.toml).
# After dist creates the GitHub Release, this submits the new version to the
# Windows Package Manager Community Repository (microsoft/winget-pkgs) using the
# Windows .zip artifact that dist already builds and attaches to the release.
#
# Requires a `WINGET_TOKEN` repository secret: a *classic* GitHub PAT with the
# `public_repo` scope that owns a fork of microsoft/winget-pkgs under the
# `fork-user` account (see below). Fine-grained tokens are not supported -- they
# can commit but cannot open the cross-fork PR to winget-pkgs. Without it the job
# fails at submission time.
on:
workflow_call:
inputs:
plan:
required: true
type: string
secrets:
WINGET_TOKEN:
required: true
description: "GitHub PAT with public_repo scope for winget-pkgs PR"
jobs:
winget:
name: Publish winget package
# winget-pkgs only accepts stable versions; never submit prereleases.
if: ${{ !fromJson(inputs.plan).announcement_is_prerelease }}
runs-on: ubuntu-latest
steps:
- name: Submit skim to the Windows Package Manager Community Repository
uses: vedantmgoyal9/winget-releaser@v2
with:
identifier: skim-rs.skim
installers-regex: '-pc-windows-msvc\.zip$'
release-tag: ${{ fromJson(inputs.plan).announcement_tag }}
fork-user: skim-rs
token: ${{ secrets.WINGET_TOKEN }}

27
.gitignore vendored
View file

@ -10,6 +10,31 @@
# Generated by Cargo
/target/
/bin/*
/bin/sk
.idea/
.ropeproject/
.direnv
# Coverage
lcov.info
codecov.json
*.profraw
# Profiling
profile.json.gz
*.data
*.data.old
flamegraph.svg
cachegrind.out.*
/scripts/data/
__pycache__/
# Vagrant
.vagrant/
.envrc.local
/public
.codex
/coverage.xml

View file

@ -1,2 +1,2 @@
error_on_line_overflow = false
max_width = 120
imports_granularity = "Module"

148
AGENTS.md Normal file
View file

@ -0,0 +1,148 @@
# Skim Agent Guidelines
## Build/Test/Lint Commands
- Build: `cargo build [--release]`
- Run: `cargo run [--release]`
- Test (all): `cargo nextest run`
- Test (single): `cargo nextest test_name`
- Integration/E2E tests: `cargo nextest run --tests` (drives `sk` through Zellij under the hood; needs `zellij` >= 0.44 and `bash` on `$PATH`)
- Memory leak detection: `cargo nextest run --profile valgrind`
- Thread leak/race detection:
1. Build: `RUSTFLAGS="-Zsanitizer=thread" cargo +nightly build --tests -Zbuild-std --target x86_64-unknown-linux-gnu`
2. Run: `TSAN_OPTIONS="detect_deadlocks=1" cargo +nightly nextest run --profile tsan --target x86_64-unknown-linux-gnu`
- Lint: `cargo clippy`
- Format: `cargo +nightly fmt` (check only: `cargo +nightly fmt --check`)
- Fuzz (requires nightly + `cargo install cargo-fuzz`): `cargo +nightly fuzz run <target>` — see `fuzz/README.md` for target list
## Code Style
- Format with 120 char line width (defined in .rustfmt.toml)
- Use standard Rust naming conventions (snake_case for functions/variables, CamelCase for types)
- Organize imports by standard library, external crates, then internal modules
- Prefer Option/Result types for error handling over panicking
- Use proper error propagation with `?` operator
- Document public API with rustdoc comments
- Use meaningful type annotations, especially for public functions
- Follow the existing structure for new modules (see src/engine/ or src/model/)
- Implement relevant traits (SkimItem, etc.) for new types when needed
## Architecture Documentation
- `ARCHITECTURE.md` documents the full architecture: data flow, operating modes, subsystems, threading model, and public API.
- **Update `ARCHITECTURE.md` whenever you make structural changes**, including:
- Adding, removing, or renaming modules, structs, or traits
- Changing the data flow between subsystems (reader → pool → matcher → TUI)
- Adding new operating modes or modifying existing ones
- Changing the threading model or synchronization primitives
- Adding or removing public API surface (`SkimItem`, `SkimOptions`, `SkimOutput`, etc.)
- Changing the event/action system or key binding infrastructure
## Testing
The end-to-end tests drive a real `sk` process through a terminal, using the
Zellij-backed harness in `tests/common/zellij.rs` (`ZellijController` + the
`sk_test!` DSL). It requires `zellij` (>= 0.44) and `bash` on `$PATH`. The
harness is cross-platform (Linux, macOS and Windows). The pure-harness tests in
`interactive.rs` run on all three platforms; `execute.rs`, `popup.rs` and
`listen.rs` stay `#![cfg(unix)]` for reasons unrelated to the multiplexer (they
install POSIX mock binaries / bind a unix socket), so they run on Linux and
macOS. A few harness details make the non-Linux runners work: the pane's shell
is resolved to an absolute `bash` path (the Zellij server's environment may lack
`bash` on `PATH`); `ZELLIJ_SOCKET_DIR` is forced to a short path so the session's
unix socket path stays under the OS cap (macOS's default `$TMPDIR` is too long);
the drain thread answers the client's cursor-position report (`ESC[6n`), which
the Windows ConPTY client blocks on to learn the terminal size; and `wait_ready`
nudges the client's terminal size until the server gives the pane a non-zero
geometry to render into. The harness drives Zellij with:
- `zellij attach --create <session>` (spawned on an in-process PTY via
`portable-pty`) to start a detached session; `SKIM_DEFAULT_OPTIONS` and friends
are cleared on the spawned process.
- `zellij --session <session> action write <bytes...>` to inject keystrokes.
- `zellij --session <session> action dump-screen [--ansi]` to capture the pane.
When exploring manually you can reproduce the same flow with those commands; the
config the harness writes disables startup tips, pane frames and the kitty
keyboard protocol (so injected legacy escape sequences reach `sk`).
## Insta Snapshot Tests
Most TUI behaviour is covered by insta snapshot tests in `tests/`. The
infrastructure lives in `tests/common/insta.rs` and is exposed through two
macros: `snap!` and `insta_test!`.
### `insta_test!` — writing tests
**Simple variant** (single snapshot, no interaction):
```rust
insta_test!(my_test, ["item1", "item2"], &["--opt1", "opt2"]);
insta_test!(my_test, @cmd "printf 'a\nb'", &["--ansi"]);
insta_test!(my_test, @interactive, &["-i", "--cmd", "echo {q}"]);
```
**DSL variant** (multiple snapshots with interaction between them):
```rust
insta_test!(my_test, ["a", "b", "c"], &["--multi"], {
@snap; // take a snapshot (cell text only)
@snap_color; // snapshot cell styling (fg/bg/modifier) instead
@key Up; // send a named key (Enter, Down, Tab, …)
@char 'f'; // send a single character
@type "foo"; // type a string
@ctrl 'w'; // Ctrl+key
@alt 'b'; // Alt+key
@shift Tab; // Shift+key
@action Last; // send an Action variant (no args)
@action Down(1); // send an Action variant (with args)
@snap; // take another snapshot
@assert(|h| condition); // boolean assertion (does not snapshot)
@exited 0; // assert the app exited with this code
});
```
### Snapshot file naming
| Macro form | File pattern | Example |
|---|---|---|
| Simple variant | `{file}__{test}.snap` | `options__opt_wrap.snap` |
| DSL variant — Nth `@snap` | `{file}__{test}@{NNN}.snap` | `options__opt_cycle@002.snap` |
| DSL variant — Nth `@snap_color` | `{file}__{test}@color{NNN}.snap` | `ansi__ansi_flag_enabled@color002.snap` |
DSL snapshots use a zero-padded three-digit suffix (`@001`, `@002`, …) so that
`cargo insta review` presents them in the order they were taken.
### Snapshot front-matter
Every snapshot includes a `description` field in its YAML front-matter. For a
DSL test the description shows the input, options, and the DSL commands that
ran **since the previous `@snap`**, making it easy to understand what state
each screenshot captures:
```
description: "input: items [\"a\", \"b\", \"c\"]\noptions: --multi\nafter:\n @key Up\n @shift Tab"
```
The `expression` field is intentionally omitted (`omit_expression = true`) to
keep the files free of internal implementation details.
### Snapshot workflow
Generate / update snapshots:
```sh
# Generate all missing snapshots and accept them immediately:
INSTA_UPDATE=always cargo nextest run
# Generate missing snapshots as .snap.new files for manual review:
INSTA_UPDATE=new cargo nextest run
cargo insta review # accept / reject interactively
```
When **adding new tests** that produce snapshots:
1. Write the test with `@snap` markers.
2. Run `INSTA_UPDATE=always cargo nextest run --test <file> <test_name>` to
generate the initial snapshot files.
3. Inspect the generated `.snap` files to verify the rendered output is correct.
4. Commit both the test and its snapshot files.
When **changing rendering logic** that affects many existing snapshots:
1. Delete the affected `.snap` files:
`find tests/snapshots -name "prefix__*.snap" -delete`
2. Regenerate: `INSTA_UPDATE=always cargo nextest run`
3. Review the diff with `git diff tests/snapshots/` before committing.

1401
ARCHITECTURE.md Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

1
CLAUDE.md Symbolic link
View file

@ -0,0 +1 @@
AGENTS.md

3567
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,15 +1,15 @@
[package]
name = "skim"
version = "0.9.4"
authors = ["Zhang Jinzhou <lotabout@gmail.com>"]
version = "5.7.0"
authors = ["Loric ANDRE", "Zhang Jinzhou <lotabout@gmail.com>"]
description = "Fuzzy Finder in rust!"
documentation = "https://docs.rs/skim"
homepage = "https://github.com/lotabout/skim"
repository = "https://github.com/lotabout/skim"
readme = "README.md"
repository = "https://github.com/skim-rs/skim"
keywords = ["fuzzy", "menu", "util"]
license = "MIT"
edition = "2018"
edition = "2024"
rust-version = "1.91"
default-run = "sk"
[lib]
name = "skim"
@ -18,33 +18,162 @@ path = "src/lib.rs"
[[bin]]
name = "sk"
path = "src/bin/main.rs"
[dependencies]
nix = "0.25.0"
atty = "0.2.14"
regex = "1.6.0"
lazy_static = "1.4.0"
shlex = "1.1.0"
unicode-width = "0.1.9"
log = "0.4.17"
env_logger = "0.9.0"
time = "0.3.13"
clap = "3.2.22"
tuikit = "0.5.0"
vte = "0.11.0"
fuzzy-matcher = "0.3.7"
rayon = "1.5.3"
derive_builder = "0.11.2"
bitflags = "1.3.2"
timer = "0.2.0"
chrono = "0.4.22"
crossbeam = "0.8.2"
beef = "0.5.2" # compact cow
defer-drop = "1.2.0"
required-features = ["cli"]
[features]
default = []
# Default is destined to the CLI, not to library usage.
default = ["cli", "frizbee", "image", "listen"]
# Everything needed to use skim as a cli (argument parsing, shell integrations...). This should not be needed for most libraries.
cli = ["dep:clap", "dep:clap_complete", "dep:clap_complete_nushell", "dep:shlex", "dep:env_logger", "dep:clap_mangen"]
# Include frizbee as a matching algorithm
frizbee = ["dep:frizbee"]
# Enable image previews (renders the preview argument as an image)
image = ["dep:image", "dep:ratatui-image"]
# Enable the IPC socket (--listen / --remote), driving skim from other processes
listen = ["dep:interprocess", "dep:ron", "dep:serde"]
# Enable gungraun (Valgrind-based) benchmarks
gungraun = ["dep:gungraun"]
[profile.release]
lto = true
codegen-units = 1
opt-level = 2
strip = true
# Kept for compatibility
[profile.dist]
inherits = "release"
[profile.release-debug]
inherits = "release"
debug = true
strip = false
[profile.dev]
debug = "line-tables-only"
split-debuginfo = "unpacked"
[profile.dev.package."*"]
debug = false
[lints.rust]
missing_docs = "warn"
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage, coverage_nightly)'] }
[lints.clippy]
pedantic = { level = "warn", priority = -1 }
incompatible_msrv = "warn"
collapsible_match = "allow"
default_trait_access = "allow"
[dependencies]
ansi-to-tui = "8.0.1"
assert_enum_variants = "0.1.2"
clap = { version = "4.6.1" , optional = true, features = ["cargo", "derive", "unstable-markdown"] }
clap_complete = { version = "4.6.5", optional = true }
clap_complete_nushell = { version = "4.6.0", optional = true }
clap_mangen = { version = "0.3.0", optional = true }
eyre = "0.6.12"
# Crossterm's version is selected by ratatui
crossterm = { version = ">=0.0.0", features = ["event-stream", "use-dev-tty", "libc"] }
derive_builder = "0.20.2"
derive_more = { version = "2.1.1", features = ["debug", "eq"] }
env_logger = { version = "0.11.10", optional = true, features = ["humantime"] }
frizbee = { version = "=0.13.0", optional = true }
futures = "0.3.32"
gungraun = { version = "0.19.1", optional = true }
image = { version = "0.25.10", optional = true, default-features = false, features = ["png", "jpeg", "gif", "webp"] }
indexmap = "2.13.1"
interprocess = { version = "2.4.2", features = ["tokio"], optional = true }
kanal = "0.1.1"
log = "0.4.31"
memchr = "2.8.1"
mimalloc = { version = "0.1.48", features = ["v3"] }
nix = { version = "0.31.3", features = ["fs", "poll", "signal"] }
portable-pty = "0.9.0"
ratatui = "0.30.0"
ratatui-image = { version = "11.0.4", features = ["crossterm"], default-features = false, optional = true }
regex = "1.12.3"
roff = "1.1.1"
ron = { version = "0.12.1", optional = true }
serde = { version = "1.0.228", features = ["derive"], optional = true }
shell-quote = "0.8.0"
shlex = { version = "2.0.1", optional = true }
tempfile = "3.27.0"
thiserror = "2.0.18"
thread_local = "1.1.9"
tokio = { version = "1.52.3", features = ["macros", "net", "rt-multi-thread", "sync", "time", "tokio-macros"] }
tokio-util = "0.7.18"
tui-term = "0.3.4"
unicode-display-width = "0.3.0"
unicode-normalization = "0.1.25"
which = "8.0.2"
[dev-dependencies]
criterion = { version = "0.8.2", features = ["async_tokio"] }
gnuplot = "0.0.46"
insta = "1.47"
rand = "0.10.0"
serde_json = { version = "=1.0.151" }
serial_test = "=3.5.0"
[[bench]]
name = "read_and_match"
harness = false
[[bench]]
name = "filter"
harness = false
[[bench]]
name = "partial"
harness = false
[[bench]]
name = "matcher_micro"
harness = false
[[bench]]
name = "gungraun"
harness = false
required-features = ["gungraun"]
[[bench]]
name = "cli"
harness = false
bench = false
[package.metadata.wix]
upgrade-guid = "6DDAED06-EBE2-41C4-94F5-CB03F2A4B92E"
path-guid = "05D1A327-19E0-4C82-B5EC-6B28E40C691B"
license = false
eula = false
# Debian package (.deb) built via `cargo deb`.
# Ships the `sk` executable, the man pages and the shell completions.
[package.metadata.deb]
maintainer = "Loric ANDRE, Zhang Jinzhou <lotabout@gmail.com>"
copyright = "The skim developers"
section = "utils"
priority = "optional"
extended-description = "skim is a general purpose fuzzy finder written in Rust, usable as a command line tool and as a library."
assets = [
["target/release/sk", "usr/bin/", "755"],
["man/man1/sk.1", "usr/share/man/man1/", "644"],
["shell/completion.bash", "usr/share/bash-completion/completions/sk", "644"],
["shell/completion.zsh", "usr/share/zsh/vendor-completions/_sk", "644"],
["shell/completion.fish", "usr/share/fish/vendor_completions.d/sk.fish", "644"],
["README.md", "usr/share/doc/skim/README.md", "644"],
]
# RPM package (.rpm) built via `cargo generate-rpm`.
# Ships the `sk` executable, the man pages and the shell completions.
[package.metadata.generate-rpm]
assets = [
{ source = "target/release/sk", dest = "/usr/bin/sk", mode = "755" },
{ source = "man/man1/sk.1", dest = "/usr/share/man/man1/sk.1", mode = "644", doc = true },
{ source = "shell/completion.bash", dest = "/usr/share/bash-completion/completions/sk", mode = "644" },
{ source = "shell/completion.zsh", dest = "/usr/share/zsh/site-functions/_sk", mode = "644" },
{ source = "shell/completion.fish", dest = "/usr/share/fish/vendor_completions.d/sk.fish", mode = "644" },
{ source = "README.md", dest = "/usr/share/doc/skim/README.md", mode = "644", doc = true },
{ source = "LICENSE", dest = "/usr/share/doc/skim/LICENSE", mode = "644", doc = true },
]

582
README.md
View file

@ -1,109 +1,206 @@
[![Crates.io](https://img.shields.io/crates/v/skim.svg)](https://crates.io/crates/skim)
[![Build & Test](https://github.com/lotabout/skim/workflows/Build%20&%20Test/badge.svg)](https://github.com/lotabout/skim/actions?query=workflow%3A%22Build+%26+Test%22)
[![Packaging status](https://repology.org/badge/tiny-repos/skim.svg)](https://repology.org/project/skim/versions)
<p align="center">
<a href="https://crates.io/crates/skim">
<img src="https://img.shields.io/crates/v/skim.svg" alt="Crates.io" />
</a>
<a href="https://github.com/skim-rs/skim/actions?query=workflow%3A%22Build+%26+Test%22+event%3Apush">
<img src="https://github.com/skim-rs/skim/actions/workflows/test.yml/badge.svg?event=push" alt="Build & Test" />
</a>
<a href="https://skim-rs.github.io/skim/coverage" >
<img src="https://skim-rs.github.io/skim/coverage/coverage.svg" alt="coverage badge" />
</a>
<a href="https://repology.org/project/skim-fuzzy-finder/versions">
<img src="https://repology.org/badge/tiny-repos/skim-fuzzy-finder.svg" alt="Packaging status" />
</a>
<a href="https://discord.gg/23PuxttufP">
<img alt="Skim Discord" src="https://img.shields.io/discord/1031830957432504361?label=&color=7389d8&labelColor=6a7ec2&logoColor=ffffff&logo=discord" />
</a>
<a href="https://matrix.to/#/#skim:matrix.org">
<img alt="Skim Matrix room" src="https://img.shields.io/badge/matrix-%23000000?style=flat&logo=matrix&logoColor=white" />
</a>
<a href="https://ratatui.rs">
<img alt="Built with Ratatui" src="https://ratatui.rs/built-with-ratatui/badge.svg" />
</a>
</p>
> Life is short, skim!
Half of our life is spent on navigation: files, lines, commands… You need skim!
It is a general fuzzy finder that saves you time.
We spend so much of our time navigating through files, lines, and commands. That's where Skim comes in!
It's a powerful fuzzy finder designed to make your workflow faster and more efficient.
[![skim demo](https://asciinema.org/a/pIfwazaM0mTHA8F7qRbjrqOnm.svg)](https://asciinema.org/a/pIfwazaM0mTHA8F7qRbjrqOnm)
skim provides a single executable: `sk`. Basically anywhere you would want to use
`grep`, try `sk` instead.
Skim provides a single executable called `sk`. Think of it as a smarter alternative to tools like
`grep` - once you try it, you'll wonder how you ever lived without it!
# Table of contents
- [Installation](#installation)
* [Package Managers](#package-managers)
* [Manually](#manually)
- [Usage](#usage)
- [As Filter](#as-filter)
- [As Interactive Interface](#as-interactive-interface)
- [Key Bindings](#key-bindings)
- [Search Syntax](#search-syntax)
- [Exit code](#exit-code)
* [As Vim plugin](#as-vim-plugin)
* [As filter](#as-filter)
* [As Interactive Interface](#as-interactive-interface)
* [Shell Bindings](#shell-bindings)
* [Key Bindings](#key-bindings)
* [Search Syntax](#search-syntax)
* [exit code](#exit-code)
- [Tools compatible with `skim`](#tools-compatible-with-skim)
* [fzf-lua neovim plugin](#fzf-lua-neovim-plugin)
* [nu_plugin_skim](#nu_plugin_skim)
- [Customization](#customization)
- [Keymap to redefine](#keymap)
- [Sort Criteria](#sort-criteria)
- [Color Scheme](#color-scheme)
- [Misc](#misc)
- [Advance Topics](#advance-topics)
- [Interactive Mode](#interactive-mode)
- [Executing external programs](#executing-external-programs)
- [Preview Window](#preview-window)
- [Fields Support](#fields-support)
- [Use as a Library](#use-as-a-library)
* [Keymap](#keymap)
* [Sort Criteria](#sort-criteria)
* [Color Scheme](#color-scheme)
* [Misc](#misc)
- [Advanced Topics](#advanced-topics)
* [Interactive mode](#interactive-mode)
+ [How does it work?](#how-does-it-work)
* [Executing external programs](#executing-external-programs)
* [Algorithms](#algorithms)
* [Preview Window](#preview-window)
+ [How does it work?](#how-does-it-work-1)
* [Fields support](#fields-support)
* [Use as a library](#use-as-a-library)
* [Benchmarks](#benchmarks)
- [FAQ](#faq)
- [How to ignore files?](#how-to-ignore-files)
- [Some files are not shown in vim plugin](#some-files-are-not-shown-in-vim-plugin)
- [Differences to fzf](#differences-to-fzf)
* [How to ignore files?](#how-to-ignore-files)
* [Some files are not shown in Vim plugin](#some-files-are-not-shown-in-vim-plugin)
- [Differences from fzf](#differences-from-fzf)
- [How to contribute](#how-to-contribute)
* [Windows compatibility testing](#windows-compatibility-testing)
- [Troubleshooting](#troubleshooting)
* [No line feed issues with nix, FreeBSD, termux](#no-line-feed-issues-with-nix-freebsd-termux)
# Installation
The skim project contains several components:
1. `sk` executable -- the core.
2. `sk-tmux` -- script for launching `sk` in a tmux pane.
3. Vim/Nvim plugin -- to call `sk` inside Vim/Nvim. check [skim.vim](https://github.com/lotabout/skim.vim) for more Vim support.
1. `sk` executable - the core program
2. Vim/Nvim plugin - to call `sk` inside Vim/Nvim. Check [skim.vim](https://github.com/skim-rs/skim/blob/master/plugin/skim.vim) for Vim support.
## Package Managers
| Distribution | Package Manager | Command |
| -------------- | ----------------- | ------------------------- |
| macOS | Homebrew | `brew install sk` |
| macOS | MacPorts | `sudo port install skim` |
| Fedora | dnf | `dnf install skim` |
| Alpine | apk | `apk add skim` |
| Arch | pacman | `pacman -S skim` |
| OS | Package Manager | Command |
| -------------- | --------------- | ---------------------------- |
| macOS | Homebrew | `brew install sk` |
| macOS | MacPorts | `sudo port install skim` |
| Alpine | apk | `apk add skim` |
| Arch | pacman | `pacman -S skim` |
| Fedora | COPR | see below |
| Gentoo | Portage | `emerge --ask app-misc/skim` |
| Guix | guix | `guix install skim` |
| Void | XBPS | `xbps-install -S skim` |
| Windows | winget | `winget install skim` |
| Windows | Scoop | `scoop install skim` |
| Debian/Ubuntu | apt | see below |
| Fedora/RHEL | dnf | see below |
See [repology](https://repology.org/project/skim/versions) for a comprehensive overview of package availability.
<a href="https://repology.org/project/skim-fuzzy-finder/versions">
<img src="https://repology.org/badge/vertical-allrepos/skim-fuzzy-finder.svg?columns=4" alt="Packaging status">
</a>
### Debian/Ubuntu
## Install as Vim plugin
A custom APT repository is available and updated automatically during each release:
Via vim-plug (recommended):
1. Import the signing key
```vim
Plug 'lotabout/skim', { 'dir': '~/.skim', 'do': './install' }
With wget:
```sh
sudo mkdir -p /etc/apt/keyrings
sudo wget -O /etc/apt/keyrings/skim.asc https://skim-rs.github.io/skim/apt/skim-archive-keyring.asc
```
## Hard Core
Or with cURL:
```sh
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://skim-rs.github.io/skim/apt/skim-archive-keyring.asc | sudo tee /etc/apt/keyrings/skim.asc > /dev/null
```
2. Add the repository
```sh
echo 'deb [signed-by=/etc/apt/keyrings/skim.asc] https://skim-rs.github.io/skim/apt ./' | sudo tee /etc/apt/sources.list.d/skim.list
sudo apt-get update
```
3. Install
```sh
sudo apt-get install skim
```
Alternatively, `.deb` packages are attached directly to every [release](https://github.com/skim-rs/skim/releases/latest).
Download the one matching your architecture and run `sudo dpkg -i skim_*_amd64.deb`
### Fedora/RHEL
Up-to-date Fedora/RHEL packages are provided via an unofficial community-maintained COPR repository.
```bash
sudo dnf copr enable sisyphus1813/skim
sudo dnf install skim
```
Alternatively, `.rpm` packages are attached directly to every [release](https://github.com/skim-rs/skim/releases/latest).
Download it and run `sudo rpm -i skim-*.x86_64.rpm`
## Manually
Any of the following applies:
- Using Git
- Using the install script:
```sh
$ git clone --depth 1 git@github.com:lotabout/skim.git ~/.skim
$ ~/.skim/install
# Always check the content of the script before running it !
$ curl --proto '=https' --tlsv1.2 -LsSf https://github.com/skim-rs/skim/releases/latest/download/skim-installer.sh | sh
```
- Using Binary: directly [download the sk executable](https://github.com/lotabout/skim/releases).
- Using Binary: Simply [download the sk executable](https://github.com/skim-rs/skim/releases) directly.
- Install from [crates.io](https://crates.io/): `cargo install skim`
- Build Manually
- Build Manually:
```sh
$ git clone --depth 1 git@github.com:lotabout/skim.git ~/.skim
$ git clone --depth 1 git@github.com:skim-rs/skim.git ~/.skim
$ cd ~/.skim
$ cargo install
$ cargo build --release
$ # put the resulting `target/release/sk` executable on your PATH.
$ # Add the resulting `target/release/sk` executable to your PATH
```
You will then have access to:
- The man page, which you can either write to the correct path or run `man --local-file <(sk --man)`
- The shell completions (and optional keybinds), using `source <(sk --shell \<shell> \[--shell-bindings])`, see below for details
# Usage
skim can be used as a general filter (like `grep`) or as an interactive
interface for invoking commands.
Skim can be used either as a general filter (similar to `grep`) or as an interactive
interface for running commands.
## As Vim plugin (on neovim, checkout [fzf-lua](https://github.com/ibhagwan/fzf-lua) with the skim profile)
Via vim-plug (recommended):
Install skim, then :
```vim
Plug 'skim-rs/skim'
```
## As filter
Try the following
Here are some examples to get you started:
```bash
# directly invoke skim
sk
# or pipe some input to it: (press TAB key select multiple items with -m enabled)
# Or pipe some input to it (press TAB key to select multiple items when -m is enabled)
vim $(find . -name "*.rs" | sk -m)
```
The above command will allow you to select files with ".rs" extension and open
the ones you selected in Vim.
This last command lets you select files with the ".rs" extension and opens
your selections in Vim - a great time-saver for developers!
## As Interactive Interface
@ -116,17 +213,63 @@ project directory:
```sh
# works with grep
sk --ansi -i -c 'grep -rI --color=always --line-number "{}" .'
sk --ansi -i -c 'grep -rI --color=always --line-number {q} .'
# works with ack
sk --ansi -i -c 'ack --color "{}"'
sk --ansi -i -c 'ack --color {q}'
# works with ag
sk --ansi -i -c 'ag --color "{}"'
sk --ansi -i -c 'ag --color {q}'
# works with rg
sk --ansi -i -c 'rg --color=always --line-number "{}"'
sk --ansi -i -c 'rg --color=always --line-number {q}'
```
> **Note**: In these examples, `{q}` will be literally expanded to the current input query (wrapped in single quotes).
> This means these examples will search for the exact query string, not fuzzily.
> For fuzzy searching, pipe the command output into `sk` without using interactive mode.
![interactive mode demo](https://cloud.githubusercontent.com/assets/1527040/21603930/655d859a-d1db-11e6-9fec-c25099d30a12.gif)
## Shell Bindings
Bindings for Fish, Bash and Zsh are available in the `shell` directory:
- `completion.{shell}` contains the completion scripts for `sk` cli usage
- `key-bindings.{shell}` contains key-binds and shell integrations:
- `ctrl-t` to select a file through `sk`
- `ctrl-r` to select an history entry through `sk`
- `alt-c` to `cd` into a directory selected through `sk`
- (not available in `fish`) `**` to complete file paths, for example `ls **<tab>` will show a `sk` widget to select a folder
To enable these features, source the `key-bindings.{shell}` file and set up completions according to your shell's documentation or see below.
### Shell Completions
You can generate shell completions for your preferred shell using the `--shell` flag with one of the supported shells: `bash`, `zsh`, `fish`, `powershell`, or `elvish`:
#### Option 1: Source directly in your current shell session
```sh
# For bash
source <(sk --shell bash)
# For zsh
source <(sk --shell zsh)
# For fish
sk --shell fish | source
```
#### Option 2: Save to a file to be loaded automatically on shell startup
```sh
# For bash, add to ~/.bashrc
echo 'source <(sk --shell bash)' >> ~/.bashrc # Or save to ~/.bash_completion
# For zsh, add to ~/.zshrc
sk --shell zsh > ~/.zfunc/_sk # Create ~/.zfunc directory and add to fpath in ~/.zshrc
# For fish, add to ~/.config/fish/completions/
sk --shell fish > ~/.config/fish/completions/sk.fish
```
## Key Bindings
Some commonly used key bindings:
@ -140,12 +283,12 @@ Some commonly used key bindings:
| TAB | Toggle selection and move down (with `-m`) |
| Shift-TAB | Toggle selection and move up (with `-m`) |
For full list of key bindings, check out the [man
page](https://github.com/lotabout/skim/blob/master/man/man1/sk.1) (`man sk`).
For a complete list of key bindings, refer to the [man
page](https://github.com/skim-rs/skim/blob/master/man/man1/sk.1) (`man sk`).
## Search Syntax
`skim` borrowed `fzf`'s syntax for matching items:
`skim` borrows `fzf`'s syntax for matching items:
| Token | Match type | Description |
|----------|----------------------------|-----------------------------------|
@ -163,10 +306,14 @@ page](https://github.com/lotabout/skim/blob/master/man/man1/sk.1) (`man sk`).
- ` | ` means `OR` (note the spaces around `|`). With the term `.md$ |
.markdown$`, `skim` will search for items ends with either `.md` or
`.markdown`.
- `OR` has higher precedence. So `readme .md$ | .markdown$` is grouped into
- `OR` has higher precedence. For example, `readme .md$ | .markdown$` is interpreted as
`readme AND (.md$ OR .markdown$)`.
In case that you want to use regular expressions, `skim` provides `regex` mode:
- When using the `--split-match` option, each part around spaces or `|` will be matched in a split way:
- If the option's value (defaulting to `:`) is absent from the query, do a normal match
- If it is present, match everything before to everything before it in the items, and everything after it (including potential other occurrences of the delimiter) to the part after it in the items. This is particularly useful when piping in input from `rg` to match on both file name and content.
If you prefer using regular expressions, `skim` offers a `regex` mode:
```sh
sk --regex
@ -176,11 +323,44 @@ You can switch to `regex` mode dynamically by pressing `Ctrl-R` (Rotate Mode).
## exit code
| Exit Code | Meaning |
|-----------|-----------------------------------|
| 0 | Exit normally |
| 1 | No Match found |
| 130 | Abort by Ctrl-C/Ctrl-G/ESC/etc... |
| Exit Code | Meaning |
|-----------|-------------------------------------|
| 0 | Exited normally |
| 1 | No Match found |
| 130 | Aborted by Ctrl-C/Ctrl-G/ESC/etc... |
# Tools compatible with `skim`
These tools are or aim to be compatible with `skim`:
## [fzf-lua neovim plugin](https://github.com/ibhagwan/fzf-lua)
A [neovim](https://neovim.io) plugin allowing fzf and skim to be used in a to navigate your code.
Install it with your package manager, following the README. For instance, with `lazy.nvim`:
```lua
{
"ibhagwan/fzf-lua",
-- enable `sk` support instead of the default `fzf`
opts = {'skim'}
}
```
## [nu_plugin_skim](https://github.com/idanarye/nu_plugin_skim)
A [nushell](https://www.nushell.sh/) plugin to allow for better interaction between skim and nushell.
Following the instruction in the plugin's README, you can install it with cargo:
```nu
cargo install nu_plugin_skim
plugin add ~/.cargo/bin/nu_plugin_skim
```
## [sqlite extension](https://github.com/tzachar/sqlite_skim)
An `sqlite` loadable module which enables a `skim_score` function in SQL
queries.
# Customization
@ -189,7 +369,7 @@ list of options.
## Keymap
Specify the bindings with comma separated pairs (no space allowed), example:
Specify the bindings with comma separated pairs (no space allowed). For example:
```sh
sk --bind 'alt-a:select-all,alt-d:deselect-all'
@ -201,29 +381,84 @@ See the _KEY BINDINGS_ section of the man page for details.
## Sort Criteria
There are five sort keys for results: `score, index, begin, end, length`, you can
There are five sort keys for results: `score, index, begin, end, length`. You can
specify how the records are sorted by `sk --tiebreak score,index,-begin` or any
other order you want.
## Color Scheme
It is a high chance that you are a better artist than me. Luckily you won't
be stuck with the default colors, `skim` supports customization of the color scheme.
You probably have your own aesthetic preferences! Fortunately, you aren't
limited to the default appearance - Skim supports comprehensive customization of its color scheme.
```sh
--color=[BASE_SCHEME][,COLOR:ANSI]
```
The configuration of colors starts with the name of the base color scheme,
followed by custom color mappings. For example:
Skim also respects the `NO_COLOR` environment variable. Set it to anything and `sk` (and many other terminal apps) will disable all colored output. See [no-color.org](https://no-color.org/) for more details.
### Available Base Color Schemes
Skim comes with several built-in color schemes that you can use as a starting point:
```sh
sk --color=dark # Default dark theme (256 colors)
sk --color=light # Light theme (256 colors)
sk --color=16 # Simple 16-color theme
sk --color=bw # Minimal black & white theme (no colors, just styles)
sk --color=none # Minimal black & white theme (no colors, no styles)
sk --color=molokai # Molokai-inspired theme (256 colors)
```
### Customizing Colors
You can customize individual UI elements by specifying color values after the base scheme:
```sh
sk --color=current_bg:24
sk --color=light,fg:232,bg:255,current_bg:116,info:27
```
See `--color` option in the man page for details.
Colors can be specified in several ways:
- ANSI colors (0-255): `sk --color=fg:232,bg:255`
- RGB hex values: `sk --color=fg:#FF0000` (red text)
### Available Color Customization Options
The following UI elements can be customized:
| Element | Description | Example |
|--------------------|---------------------------------------------|--------------------------------|
| `fg` | Normal text foreground color | `--color=fg:232` |
| `bg` | Normal text background color | `--color=bg:255` |
| `matched` | Matched text in search results | `--color=matched:108` |
| `matched_bg` | Background of matched text | `--color=matched_bg:0` |
| `current` | Current line foreground color | `--color=current:254` |
| `current_bg` | Current line background color | `--color=current_bg:236` |
| `current_match` | Matched text in current line | `--color=current_match:151` |
| `current_match_bg` | Background of matched text in current line | `--color=current_match_bg:236` |
| `spinner` | Progress indicator color | `--color=spinner:148` |
| `info` | Information line color | `--color=info:144` |
| `prompt` | Prompt color | `--color=prompt:110` |
| `cursor` | Cursor color | `--color=cursor:161` |
| `selected` | Selected item marker color | `--color=selected:168` |
| `header` | Header text color | `--color=header:109` |
| `border` | Border color for preview/layout | `--color=border:59` |
| `scrollbar` | Item list scrollbar thumb color | `--color=scrollbar:59` |
### Examples
```sh
# Use light theme but change the current line background
sk --color=light,current_bg:24
# Custom theme with multiple colors
sk --color=dark,matched:#00FF00,current:#FFFFFF,current_bg:#000080
# High contrast theme
sk --color=fg:232,bg:255,matched:160,current:255,current_bg:20
```
For more details, check the man page (`man sk`).
## Misc
@ -234,28 +469,28 @@ See `--color` option in the man page for details.
## Interactive mode
With "interactive mode", you could invoke command dynamically. Try out:
In **interactive mode**, you can invoke a command dynamically. Try it out:
```sh
sk --ansi -i -c 'rg --color=always --line-number "{}"'
sk --ansi -i -c 'rg --color=always --line-number {q}'
```
How it works?
### How does it work?
![skim's interactive mode](https://user-images.githubusercontent.com/1527040/53381293-461ce380-39ab-11e9-8e86-7c3bbfd557bc.png)
![How Skim's interactive mode works](https://user-images.githubusercontent.com/1527040/53381293-461ce380-39ab-11e9-8e86-7c3bbfd557bc.png)
- Skim could accept two kinds of source: command output or piped input
- Skim accepts two kinds of sources: Command output or piped input
- Skim has two kinds of prompts: A query prompt to specify the query pattern and a
command prompt to specify the "arguments" of the command
- `-c` is used to specify the command to execute while defaults to `SKIM_DEFAULT_COMMAND`
- `-i` is to tell skim open command prompt on startup, which will show `c>` by default.
- `-c` is used to specify the command to execute and defaults to `SKIM_DEFAULT_COMMAND`
- `-i` tells skim to open command prompt on startup, which will show `c>` by default.
If you want to further narrow down the results returned by the command, press
To further narrow down the results returned by the command, press
`Ctrl-Q` to toggle interactive mode.
## Executing external programs
You can set up key bindings for starting external processes without leaving skim (`execute`, `execute-silent`).
You can configure key bindings to start external processes without leaving Skim (`execute`, `execute-silent`).
```sh
# Press F1 to open the file with less without leaving skim
@ -263,20 +498,29 @@ You can set up key bindings for starting external processes without leaving skim
sk --bind 'f1:execute(less -f {}),ctrl-y:execute-silent(echo {} | pbcopy)+abort'
```
## Algorithms
Skim offers multiple algorithms, check the help or manpage for an exhaustive list. Among them are:
- `skim_v2`, the default algorithm, loosely based on `fzf`'s algorithm
- `frizbee`, uses [frizbee](https://crates.io/frizbee), the typo-resistant algorithm from the [blink.cmp](https://github.com/saghen/blink.cmp) neovim plugin
- `fzy`, based on [fzy](https://github.com/jhawthorn/fzy/)'s algorithm expanded for basic typo-resistance
- `arinae`, skim's newest algorithm, designed in-house with typo-resistance in mind, expanding on all the above to make typo-resistant matching feel more natural while keeping the per-item performance up to the best standards
## Preview Window
This is a great feature of fzf that skim borrows. For example, we use 'ag' to
find the matched lines, once we narrow down to the target lines, we want to
find the matched lines, and once we narrow down to the target lines, we want to
finally decide which lines to pick by checking the context around the line.
`grep` and `ag` has an option `--context`, skim can do better with preview
window. For example:
`grep` and `ag` have the option `--context`, and skim can make use of `--context` for
a better preview window. For example:
```sh
sk --ansi -i -c 'ag --color "{}"' --preview "preview.sh {}"
sk --ansi -i -c 'ag --color {q}' --preview "preview.sh {}"
```
(Note the [preview.sh](https://github.com/junegunn/fzf.vim/blob/master/bin/preview.sh) is a script to print the context given filename:lines:columns)
You got things like this:
(Note that [preview.sh](https://github.com/junegunn/fzf.vim/blob/master/bin/preview.sh) is a script to print the context given filename:lines:columns)
You get things like this:
![preview demo](https://user-images.githubusercontent.com/1527040/30677573-0cee622e-9ebf-11e7-8316-c741324ecb3a.png)
@ -288,13 +532,12 @@ command to get the output, and print the output on the preview window.
Sometimes you don't need the whole line for invoking the command. In this case
you can use `{}`, `{1..}`, `{..3}` or `{1..5}` to select the fields. The
syntax is explained in the section "Fields Support".
syntax is explained in the section [Fields Support](#filds-support).
Last, you might want to configure the position of preview windows, use
`--preview-window`.
Lastly, you might want to configure the position of preview window with `--preview-window`:
- `--preview-window up:30%` to put the window in the up position with height
30% of the total height of skim.
- `--preview-window left:10:wrap`, to specify the `wrap` allows the preview
- `--preview-window left:10:wrap` to specify the `wrap` allows the preview
window to wrap the output of the preview command.
- `--preview-window wrap:hidden` to hide the preview window at startup, later
it can be shown by the action `toggle-preview`.
@ -315,12 +558,12 @@ but not matching line number or column number.
You can use `sk --delimiter ':' --nth 1` to achieve this.
Also you can use `--with-nth` to re-arrange the order of fields.
You can also use `--with-nth` to re-arrange the order of fields.
**Range Syntax**
- `<num>` -- to specify the `num`-th fields, starting with 1.
- `start..` -- starting from the `start`-th fields, and the rest.
- `start..` -- starting from the `start`-th fields and the rest.
- `..end` -- starting from the `0`-th field, all the way to `end`-th field,
including `end`.
- `start..end` -- starting from `start`-th field, all the way to `end`-th
@ -334,9 +577,14 @@ First, add skim into your `Cargo.toml`:
```toml
[dependencies]
skim = "*"
skim = { version = "<version>", default-features = false, features = [..] }
```
_Note on features_:
- the `cli` feature is required to use skim as a cli, it *should* not be needed when using it as a library.
### Basic usage
Then try to run this simple example:
```rust
@ -346,7 +594,7 @@ use std::io::Cursor;
pub fn main() {
let options = SkimOptionsBuilder::default()
.height(Some("50%"))
.height("50%")
.multi(true)
.build()
.unwrap();
@ -364,11 +612,18 @@ pub fn main() {
.unwrap_or_else(|| Vec::new());
for item in selected_items.iter() {
print!("{}{}", item.output(), "\n");
println!("{}", item.output());
}
}
```
### Fine-grained usage
You can also gain fine-grained usage of skim as a library using `tokio` and async code, allowing you to dynamically interact with
### Internal workings
Given an `Option<SkimItemReceiver>`, skim will read items accordingly, do its
job and bring us back the user selection including the selected items, the
query, etc. Note that:
@ -381,17 +636,25 @@ Trait `SkimItem` is provided to customize how a line could be displayed,
compared and previewed. It is implemented by default for `AsRef<str>`
Plus, `SkimItemReader` is a helper to convert a `BufRead` into
`SkimItemReceiver` (we can easily turn a `File` for `String` into `BufRead`).
So that you could deal with strings or files easily.
`SkimItemReceiver` (we can easily turn a `File` or `String` into `BufRead`),
so that you could deal with strings or files easily.
Check more examples under [examples/](https://github.com/lotabout/skim/tree/master/examples) directory.
Check out more examples under the [examples/](https://github.com/skim-rs/skim/tree/master/skim/examples) directory.
## Benchmarks
This benchmarks runs the interactive interface in a tmux session, and waits for the UI to stabilize.
![benchmark graphs](./bench.png)
You can generate the graphs by using `just bench-plot` or running the recipe manually in GNU bash.
# FAQ
## How to ignore files?
Skim invokes `find .` to fetch a list of files for filtering. You can override
that by setting the environment variable `SKIM_DEFAULT_COMMAND`. For example:
this by setting the environment variable `SKIM_DEFAULT_COMMAND`. For example:
```sh
$ SKIM_DEFAULT_COMMAND="fd --type f || git ls-tree -r --name-only HEAD || rg --files || find ."
@ -402,38 +665,119 @@ You could put it in your `.bashrc` or `.zshrc` if you like it to be default.
## Some files are not shown in Vim plugin
If you use the Vim plugin and execute the `:SK` command, you might find some
If you use the Vim plugin and execute the `:SK` command, you may find some
of your files not shown.
As described in [#3](https://github.com/lotabout/skim/issues/3), in the Vim
As described in [#3](https://github.com/skim-rs/skim/issues/3), in the Vim
plugin, `SKIM_DEFAULT_COMMAND` is set to the command by default:
```vim
let $SKIM_DEFAULT_COMMAND = "git ls-tree -r --name-only HEAD || rg --files || ag -l -g \"\" || find ."
```
That means, the files not recognized by git will not shown. Either override the
default with `let $SKIM_DEFAULT_COMMAND = ''` or find the missing file by
This means files not recognized by git won't be shown. You can either override the
default with `let $SKIM_DEFAULT_COMMAND = ''` or locate the missing files by
yourself.
# Differences to fzf
# Differences from fzf
[fzf](https://github.com/junegunn/fzf) is a command-line fuzzy finder written
in Go and [skim](https://github.com/lotabout/skim) tries to implement a new one
in Go and [skim](https://github.com/skim-rs/skim) tries to implement a new one
in Rust!
This project is written from scratch. Some decisions of implementation are
different from fzf. For example:
1. `skim` is a binary as well as a library while fzf is only a binary.
2. `skim` has an interactive mode.
3. `skim` supports pre-selection
4. The fuzzy search algorithm is different.
5. ~~UI of showing matched items. `fzf` will show only the range matched while
`skim` will show each character matched.~~ (fzf has this now)
6. ~~`skim`'s range syntax is Git style~~: now it is the same with fzf.
1. `skim` has an interactive mode.
2. `skim` supports pre-selection.
3. The fuzzy search algorithm is different.
More generally, `skim`'s maintainers allow themselves some freedom of implementation.
The goal is to keep `skim` as feature-full as `fzf` is, but the command flags might differ.
# How to contribute
[Create new issues](https://github.com/lotabout/skim/issues/new) if you meet any bugs
[Create new issues](https://github.com/skim-rs/skim/issues/new) if you encounter any bugs
or have any ideas. Pull requests are warmly welcomed.
## Windows compatibility testing
A `Vagrantfile` is included to spin up a headless Windows Server 2022 Core VM for testing
Windows compatibility without needing a GUI. It requires [VirtualBox](https://www.virtualbox.org/)
and [Vagrant](https://www.vagrantup.com/) on your host (`vagrant` is included in the Nix dev
shell via `flake.nix`).
```sh
vagrant up # First boot: downloads the box and provisions (~1520 min)
ssh -p 2222 vagrant@localhost # Password: vagrant
```
Inside the VM, the project root is mounted at `C:\vagrant`:
```powershell
cd C:\vagrant
cargo build
cargo test
```
Subsequent boots are fast — provisioning only runs once:
```sh
vagrant halt # Stop the VM
vagrant up # Resume
vagrant destroy # Delete the VM entirely
```
# Troubleshooting
To troubleshoot what's happening, you can set the environment variable `SKIM_LOG` or the flag `--log-level` to either `debug` or even `trace`, and set the environment variable `SKIM_LOG_FILE` or the flag `--log-file` to a path. You can then read those logs during or after the execution to better understand what's happening. Don't hesitate to add those logs to an issue if you need help.
## No line feed issues with nix, FreeBSD, termux
If you encounter display issues like:
```bash
$ for n in {1..10}; do echo "$n"; done | sk
0/10 0/0.> 10/10 10 9 8 7 6 5 4 3 2> 1
```
For example
- https://github.com/skim-rs/skim/issues/412
- https://github.com/skim-rs/skim/issues/455
You need to set TERMINFO or TERMINFO_DIRS to the path of a correct terminfo database path
For example, with termux, you can add this in your bashrc:
```
export TERMINFO=/data/data/com.termux/files/usr/share/terminfo
```
# Benchmarks
## Interactive benchmark (`cli`)
The `cli` bench benchmarks skim (or any compatible binary) against other versions or fzf by running the interactive interface inside a tmux session and polling the status line until the matched count stabilises. This is by no means a precise or foolproof measurement, but it has the added benefit of benchmarking against `fzf` and of providing resource metrics (peak RSS and CPU).
```sh
cargo bench --bench cli -- run # defaults: sk, 1 M items, query "test"
cargo bench --bench cli -- run sk -n 500000 -q foo # bare name resolved via $PATH
cargo bench --bench cli -- run ./old/sk ./new/sk -r 5 # compare two binaries, 5 runs each
cargo bench --bench cli -- run sk -f input.txt -q search # use an existing file
cargo bench --bench cli -- generate -f testdata.txt -n 2000000 # generate input file and exit
cargo bench --bench cli -- run sk --perf # record perf data (auto-named file)
cargo bench --bench cli -- run sk --strace # record strace data (auto-named file)
cargo bench --bench cli -- run sk -p perf.data # record perf data to perf.data
cargo bench --bench cli -- run sk -j # JSON output
cargo bench --bench cli -- run sk -r 3 -- --tiebreak=index # pass extra flags to sk
```
Binary names are resolved to absolute paths via `which` before use, so bare names like `sk` or `fzf` work as long as they are on `$PATH`.
### Criterion benchmarks
Criterion benchmarks are available to measure skim's performance more precisely.
To run them, you need to generate input data using `cargo bench --bench cli -- -g benches/fixtures/10M.txt -n 10000000 && cargo bench --bench cli -- -g benches/fixtures/1M.txt -n 1000000`, then run `cargo bench -j 1`.
These will run for several minutes.

121
Vagrantfile vendored Normal file
View file

@ -0,0 +1,121 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
#
# Windows development VM for testing skim on Windows without a GUI.
#
# Prerequisites (host, NixOS):
# virtualisation.libvirtd.enable = true;
# users.users.<you>.extraGroups = [ "libvirtd" ]; # then log out/in
#
# Usage:
# vagrant up # First boot: downloads box, provisions (~15-20 min)
# vagrant up --provision # Re-run provisioning on existing VM
# vagrant ssh # SSH in via vagrant
# vagrant ssh-config # Show IP/key if you prefer a raw ssh command
# vagrant halt # Stop the VM
# vagrant destroy # Delete the VM
#
# Inside the VM:
# cd C:\vagrant # Project root (synced from host, see note below)
# cargo build # Build skim
# cargo test # Run tests
#
# Note: The first `vagrant up` requires internet access on the VM to install
# packages via Chocolatey.
Vagrant.configure("2") do |config|
# Windows Server 2022 Core — minimal footprint, no desktop GUI.
# Box source: https://app.vagrantup.com/gusztavvargadr/boxes/windows-server-2022-standard-core
config.vm.box = "gusztavvargadr/windows-server-2022-standard-core"
# Vagrant manages the VM via WinRM (the Windows default).
config.vm.communicator = "winrm"
config.winrm.username = "vagrant"
config.winrm.password = "vagrant"
config.winrm.timeout = 600 # provisioning can take a while on first boot
config.vm.provider "libvirt" do |lv|
lv.driver = "kvm"
lv.memory = 2048
lv.cpus = 2
end
# rsync is used for the synced folder because libvirt has no native
# shared-folder support for Windows guests. rsync must be present on the
# guest, so the folder is disabled on boot and synced via a post-provision
# trigger (after Chocolatey installs rsync below).
# Re-sync manually at any time with: vagrant rsync
# rsync is used because libvirt has no native shared-folder support for
# Windows guests. On a brand-new VM the very first `vagrant up` will fail
# the rsync step (rsync not yet installed on the guest); run
# `vagrant provision && vagrant rsync` to recover, or just
# `vagrant destroy && vagrant up` after the box is cached locally.
# cwRsync (the Windows rsync from Chocolatey) uses Cygwin paths, so the
# guest path must use /cygdrive/c/... rather than a bare /vagrant.
config.vm.synced_folder ".", "/cygdrive/c/vagrant", type: "rsync",
rsync__exclude: [".git/", "target/", ".jj/"],
rsync__args: ["--verbose", "--archive", "--delete", "--copy-links", "--no-owner", "--no-group"]
# ---------------------------------------------------------------------------
# Provisioning: configure OpenSSH + install Rust toolchain via Chocolatey.
# The box ships with Win32-OpenSSH already present, so we only configure it.
# Runs once on `vagrant up`; re-run with `vagrant provision`.
# ---------------------------------------------------------------------------
config.vm.provision "shell", privileged: true, inline: <<-'POWERSHELL'
$ErrorActionPreference = "Stop"
# --- Chocolatey -------------------------------------------------------------
Write-Host "==> Installing Chocolatey..."
if (-not (Get-Command choco -ErrorAction SilentlyContinue)) {
Set-ExecutionPolicy Bypass -Scope Process -Force
[System.Net.ServicePointManager]::SecurityProtocol =
[System.Net.ServicePointManager]::SecurityProtocol -bor 3072
Invoke-Expression (
(New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')
)
}
# --- OpenSSH Server ---------------------------------------------------------
# The box ships with Win32-OpenSSH binaries at C:\Program Files\OpenSSH-Win64.
# Re-run install-sshd.ps1 to register the service (idempotent; safe to
# re-run if the service is already present).
Write-Host "==> Registering and starting sshd..."
& "C:\Program Files\OpenSSH-Win64\install-sshd.ps1"
Set-Service -Name sshd -StartupType Automatic
Start-Service -Name sshd
# Use PowerShell as the default shell for SSH sessions.
$regPath = "HKLM:\SOFTWARE\OpenSSH"
if (-not (Test-Path $regPath)) { New-Item -Path $regPath -Force | Out-Null }
Set-ItemProperty -Path $regPath -Name DefaultShell `
-Value "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
# Allow inbound SSH through the Windows firewall.
$rule = Get-NetFirewallRule -Name "OpenSSH-Server-In-TCP" -ErrorAction SilentlyContinue
if (-not $rule) {
New-NetFirewallRule -Name "OpenSSH-Server-In-TCP" `
-DisplayName "OpenSSH Server (sshd)" `
-Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
}
# --- Rust, Git, rsync -------------------------------------------------------
Write-Host "==> Installing Rust, Git, rsync, and MinGW..."
choco install -y rust git rsync mingw
$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") +
";" + [System.Environment]::GetEnvironmentVariable("Path", "User")
# Add MinGW bin to the persistent system PATH so dlltool.exe is found in
# SSH sessions (which don't run the Chocolatey shim refresh).
$mingwBin = "C:\ProgramData\mingw64\mingw64\bin"
$machinePath = [System.Environment]::GetEnvironmentVariable("Path", "Machine")
if ($machinePath -notlike "*$mingwBin*") {
[System.Environment]::SetEnvironmentVariable("Path", "$machinePath;$mingwBin", "Machine")
}
Write-Host ""
Write-Host "==> Provisioning complete."
Write-Host " SSH into the VM: vagrant ssh-config (then ssh to the reported IP)"
Write-Host " Build skim: cd C:\vagrant && cargo build"
POWERSHELL
end

BIN
bench.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

1855
benches/cli.rs Normal file

File diff suppressed because it is too large Load diff

285
benches/filter.rs Normal file
View file

@ -0,0 +1,285 @@
#![allow(missing_docs, clippy::pedantic)]
use std::fs;
use criterion::{Criterion, criterion_group, criterion_main};
use skim::Typos;
use skim::helper::item::DefaultSkimItem;
use skim::prelude::*;
const CHUNK_SIZE: usize = 1024;
fn load_lines(file: &str) -> Vec<String> {
let data = fs::read_to_string(format!("benches/fixtures/{file}")).expect("{file} missing");
data.lines().map(|l| l.to_string()).collect()
}
fn prepare(file: &str, opt_builder: &mut SkimOptionsBuilder) -> (SkimOptions, SkimItemReceiver) {
let lines = load_lines(file);
let opts = opt_builder.build().unwrap();
let (tx, rx) = unbounded();
let mut chunk_size = 0;
let mut chunk = Vec::new();
for line in lines {
if chunk_size >= CHUNK_SIZE {
tx.send(chunk).unwrap();
chunk_size = 0;
chunk = Vec::new();
}
chunk.push(Arc::new(DefaultSkimItem::from(line)) as Arc<dyn SkimItem>);
}
tx.send(chunk).unwrap();
(opts, rx)
}
fn criterion_benchmark_10m(c: &mut Criterion) {
c.bench_function("filter_10M_regex", |b| {
b.iter_batched(
|| prepare("10M.txt", SkimOptionsBuilder::default().filter("test").regex(true)),
|(opts, rx)| Skim::run_with(opts, Some(rx)),
criterion::BatchSize::SmallInput,
);
});
#[cfg(feature = "frizbee")]
c.bench_function("filter_10M_frizbee", |b| {
b.iter_batched(
|| {
prepare(
"10M.txt",
SkimOptionsBuilder::default()
.filter("test")
.algorithm(FuzzyAlgorithm::Frizbee)
.typos(Typos::Disabled),
)
},
|(opts, rx)| Skim::run_with(opts, Some(rx)),
criterion::BatchSize::SmallInput,
);
});
#[cfg(feature = "frizbee")]
c.bench_function("filter_10M_frizbee_typos", |b| {
b.iter_batched(
|| {
prepare(
"10M.txt",
SkimOptionsBuilder::default()
.filter("test")
.algorithm(FuzzyAlgorithm::Frizbee)
.typos(Typos::Smart),
)
},
|(opts, rx)| Skim::run_with(opts, Some(rx)),
criterion::BatchSize::SmallInput,
);
});
c.bench_function("filter_10M_clangd", |b| {
b.iter_batched(
|| {
prepare(
"10M.txt",
SkimOptionsBuilder::default()
.filter("test")
.algorithm(FuzzyAlgorithm::Clangd),
)
},
|(opts, rx)| Skim::run_with(opts, Some(rx)),
criterion::BatchSize::SmallInput,
);
});
c.bench_function("filter_10M_fzy", |b| {
b.iter_batched(
|| {
prepare(
"10M.txt",
SkimOptionsBuilder::default()
.filter("test")
.algorithm(FuzzyAlgorithm::Fzy)
.typos(Typos::Disabled),
)
},
|(opts, rx)| Skim::run_with(opts, Some(rx)),
criterion::BatchSize::SmallInput,
);
});
c.bench_function("filter_10M_fzy_typos", |b| {
b.iter_batched(
|| {
prepare(
"10M.txt",
SkimOptionsBuilder::default()
.filter("test")
.algorithm(FuzzyAlgorithm::Fzy)
.typos(Typos::Smart),
)
},
|(opts, rx)| Skim::run_with(opts, Some(rx)),
criterion::BatchSize::SmallInput,
);
});
c.bench_function("filter_10M_arinae", |b| {
b.iter_batched(
|| {
prepare(
"10M.txt",
SkimOptionsBuilder::default()
.filter("test")
.algorithm(FuzzyAlgorithm::Arinae)
.typos(Typos::Disabled),
)
},
|(opts, rx)| Skim::run_with(opts, Some(rx)),
criterion::BatchSize::SmallInput,
);
});
c.bench_function("filter_10M_arinae_typos", |b| {
b.iter_batched(
|| {
prepare(
"10M.txt",
SkimOptionsBuilder::default()
.filter("test")
.algorithm(FuzzyAlgorithm::Arinae)
.typos(Typos::Smart),
)
},
|(opts, rx)| Skim::run_with(opts, Some(rx)),
criterion::BatchSize::SmallInput,
);
});
}
fn criterion_benchmark_1m(c: &mut Criterion) {
c.bench_function("filter_1M_regex", |b| {
b.iter_batched(
|| prepare("1M.txt", SkimOptionsBuilder::default().filter("test").regex(true)),
|(opts, rx)| Skim::run_with(opts, Some(rx)),
criterion::BatchSize::SmallInput,
);
});
#[cfg(feature = "frizbee")]
c.bench_function("filter_1M_frizbee", |b| {
b.iter_batched(
|| {
prepare(
"1M.txt",
SkimOptionsBuilder::default()
.filter("test")
.algorithm(FuzzyAlgorithm::Frizbee)
.typos(Typos::Disabled),
)
},
|(opts, rx)| Skim::run_with(opts, Some(rx)),
criterion::BatchSize::SmallInput,
);
});
#[cfg(feature = "frizbee")]
c.bench_function("filter_1M_frizbee_typos", |b| {
b.iter_batched(
|| {
prepare(
"1M.txt",
SkimOptionsBuilder::default()
.filter("test")
.algorithm(FuzzyAlgorithm::Frizbee)
.typos(Typos::Smart),
)
},
|(opts, rx)| Skim::run_with(opts, Some(rx)),
criterion::BatchSize::SmallInput,
);
});
c.bench_function("filter_1M_clangd", |b| {
b.iter_batched(
|| {
prepare(
"1M.txt",
SkimOptionsBuilder::default()
.filter("test")
.algorithm(FuzzyAlgorithm::Clangd),
)
},
|(opts, rx)| Skim::run_with(opts, Some(rx)),
criterion::BatchSize::SmallInput,
);
});
c.bench_function("filter_1M_fzy", |b| {
b.iter_batched(
|| {
prepare(
"1M.txt",
SkimOptionsBuilder::default()
.filter("test")
.algorithm(FuzzyAlgorithm::Fzy)
.typos(Typos::Disabled),
)
},
|(opts, rx)| Skim::run_with(opts, Some(rx)),
criterion::BatchSize::SmallInput,
);
});
c.bench_function("filter_1M_fzy_typos", |b| {
b.iter_batched(
|| {
prepare(
"1M.txt",
SkimOptionsBuilder::default()
.filter("test")
.algorithm(FuzzyAlgorithm::Fzy)
.typos(Typos::Smart),
)
},
|(opts, rx)| Skim::run_with(opts, Some(rx)),
criterion::BatchSize::SmallInput,
);
});
c.bench_function("filter_1M_arinae", |b| {
b.iter_batched(
|| {
prepare(
"1M.txt",
SkimOptionsBuilder::default()
.filter("test")
.algorithm(FuzzyAlgorithm::Arinae)
.typos(Typos::Disabled),
)
},
|(opts, rx)| Skim::run_with(opts, Some(rx)),
criterion::BatchSize::SmallInput,
);
});
c.bench_function("filter_1M_arinae_typos", |b| {
b.iter_batched(
|| {
prepare(
"1M.txt",
SkimOptionsBuilder::default()
.filter("test")
.algorithm(FuzzyAlgorithm::Arinae)
.typos(Typos::Smart),
)
},
|(opts, rx)| Skim::run_with(opts, Some(rx)),
criterion::BatchSize::SmallInput,
);
});
c.bench_function("filter_1M_andor", |b| {
b.iter_batched(
|| prepare("1M.txt", SkimOptionsBuilder::default().filter("boot foo | mnt foo")),
|(opts, rx)| Skim::run_with(opts, Some(rx)),
criterion::BatchSize::SmallInput,
);
});
}
criterion_group!(
name = benches_10m;
config = Criterion::default().sample_size(10);
targets = criterion_benchmark_10m
);
criterion_group!(
name = benches_1m;
config = Criterion::default().sample_size(100);
targets = criterion_benchmark_1m
);
criterion_main!(benches_1m, benches_10m);

2
benches/fixtures/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
!.gitignore
*

67
benches/gungraun.rs Normal file
View file

@ -0,0 +1,67 @@
#![allow(missing_docs, clippy::pedantic)]
use gungraun::{library_benchmark, library_benchmark_group, main};
use std::fs;
use std::hint::black_box;
use skim::CaseMatching;
use skim::fuzzy_matcher::FuzzyMatcher;
use skim::fuzzy_matcher::arinae::ArinaeMatcher;
use skim::fuzzy_matcher::frizbee::FrizbeeMatcher;
use skim::prelude::SkimMatcherV2;
fn load_lines() -> Vec<String> {
let data = fs::read_to_string("benches/fixtures/1M.txt").expect("1M.txt missing");
data.lines().map(|l| l.to_string()).collect()
}
#[inline(always)]
fn bench_matcher(m: impl FuzzyMatcher, lines: Vec<String>) -> u64 {
let mut count = 0u64;
for line in &lines {
if m.fuzzy_indices(line, "test").is_some() {
count += 1;
}
}
count
}
#[library_benchmark]
fn skim_v2() -> u64 {
bench_matcher(SkimMatcherV2::default().smart_case(), black_box(load_lines()))
}
#[library_benchmark]
fn frizbee() -> u64 {
bench_matcher(
FrizbeeMatcher::default().case(CaseMatching::Smart).max_typos(Some(0)),
black_box(load_lines()),
)
}
#[library_benchmark]
fn frizbee_typos() -> u64 {
bench_matcher(
FrizbeeMatcher::default().case(CaseMatching::Smart).max_typos(Some(1)),
black_box(load_lines()),
)
}
#[library_benchmark]
fn arinae() -> u64 {
bench_matcher(
ArinaeMatcher::new(CaseMatching::Smart, false, false),
black_box(load_lines()),
)
}
#[library_benchmark]
fn arinae_typos() -> u64 {
bench_matcher(
ArinaeMatcher::new(CaseMatching::Smart, true, false),
black_box(load_lines()),
)
}
library_benchmark_group!(
name = benches,
benchmarks = [skim_v2, frizbee, frizbee_typos, arinae, arinae_typos]
);
main!(library_benchmark_groups = benches);

151
benches/matcher_micro.rs Normal file
View file

@ -0,0 +1,151 @@
#![allow(missing_docs, clippy::pedantic)]
//! Microbenchmark that isolates the fuzzy matcher DP from all other overhead
//! (I/O, threading, sorting).
use std::fs;
use criterion::{Criterion, criterion_group, criterion_main};
use skim::CaseMatching;
use skim::fuzzy_matcher::FuzzyMatcher;
use skim::fuzzy_matcher::arinae::ArinaeMatcher;
#[cfg(feature = "frizbee")]
use skim::fuzzy_matcher::frizbee::FrizbeeMatcher;
use skim::prelude::SkimMatcherV2;
fn load_lines() -> Vec<String> {
let data = fs::read_to_string("benches/fixtures/100K.txt").expect("100K.txt missing");
data.lines().map(|l| l.to_string()).collect()
}
fn bench_matcher(c: &mut Criterion) {
let lines = load_lines();
c.bench_function("micro_skim_v2", |b| {
let m = SkimMatcherV2::default().smart_case();
b.iter(|| {
let mut count = 0u64;
for line in &lines {
if m.fuzzy_indices(line, "test").is_some() {
count += 1;
}
}
count
});
});
#[cfg(feature = "frizbee")]
c.bench_function("micro_frizbee_score", |b| {
let m = FrizbeeMatcher::default().case(CaseMatching::Smart).max_typos(Some(0));
b.iter(|| {
let mut count = 0u64;
for line in &lines {
if m.fuzzy_match(line, "test").is_some() {
count += 1;
}
}
count
});
});
#[cfg(feature = "frizbee")]
c.bench_function("micro_frizbee", |b| {
let m = FrizbeeMatcher::default().case(CaseMatching::Smart).max_typos(Some(0));
b.iter(|| {
let mut count = 0u64;
for line in &lines {
if m.fuzzy_indices(line, "test").is_some() {
count += 1;
}
}
count
});
});
#[cfg(feature = "frizbee")]
c.bench_function("micro_typos_frizbee", |b| {
let m = FrizbeeMatcher::default().case(CaseMatching::Smart).max_typos(Some(1));
b.iter(|| {
let mut count = 0u64;
for line in &lines {
if m.fuzzy_indices(line, "test").is_some() {
count += 1;
}
}
count
});
});
c.bench_function("micro_arinae", |b| {
let m = ArinaeMatcher::new(CaseMatching::Smart, false, false);
b.iter(|| {
let mut count = 0u64;
for line in &lines {
if m.fuzzy_indices(line, "test").is_some() {
count += 1;
}
}
count
});
});
c.bench_function("micro_arinae_range", |b| {
let m = ArinaeMatcher::new(CaseMatching::Smart, false, false);
b.iter(|| {
let mut count = 0u64;
for line in &lines {
if m.fuzzy_match_range(line, "test").is_some() {
count += 1;
}
}
count
});
});
c.bench_function("micro_arinae_score", |b| {
let m = ArinaeMatcher::new(CaseMatching::Smart, false, false);
b.iter(|| {
let mut count = 0u64;
for line in &lines {
if m.fuzzy_match(line, "test").is_some() {
count += 1;
}
}
count
});
});
c.bench_function("micro_typos_arinae", |b| {
let m = ArinaeMatcher::new(CaseMatching::Smart, true, false);
b.iter(|| {
let mut count = 0u64;
for line in &lines {
if m.fuzzy_indices(line, "test").is_some() {
count += 1;
}
}
count
});
});
c.bench_function("micro_typos_arinae_range", |b| {
let m = ArinaeMatcher::new(CaseMatching::Smart, true, false);
b.iter(|| {
let mut count = 0u64;
for line in &lines {
if m.fuzzy_match_range(line, "test").is_some() {
count += 1;
}
}
count
});
});
c.bench_function("micro_typos_arinae_score", |b| {
let m = ArinaeMatcher::new(CaseMatching::Smart, true, false);
b.iter(|| {
let mut count = 0u64;
for line in &lines {
if m.fuzzy_match(line, "test").is_some() {
count += 1;
}
}
count
});
});
}
criterion_group!(benches, bench_matcher);
criterion_main!(benches);

291
benches/partial.rs Normal file
View file

@ -0,0 +1,291 @@
#![allow(missing_docs, clippy::pedantic)]
use std::fs::File;
use std::io::{BufRead, BufReader, BufWriter, Cursor, Stderr};
use std::time::Duration;
use clap::Parser as _;
use criterion::{BatchSize, Criterion, Throughput, criterion_group, criterion_main};
use ratatui::backend::TestBackend;
use ratatui::prelude::CrosstermBackend;
use skim::prelude::*;
/// Small inline fixture — fast to load, good for latency benchmarks.
const SMALL_ITEMS: &[&str] = &[
"src/main.rs",
"src/lib.rs",
"src/options.rs",
"src/skim.rs",
"benches/partial.rs",
"tests/common/insta.rs",
"Cargo.toml",
"README.md",
];
/// Path to the medium fixture shipped with the repo (≈664 lines).
const FIXTURE_DEFAULT: &str = "benches/fixtures/default.txt";
/// Path to the large fixture (100 000 lines). Skip in low-time CI runs if absent.
const FIXTURE_100K: &str = "benches/fixtures/100K.txt";
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
/// Read a fixture file into memory and return each non-empty line as a `String`.
fn load_fixture(path: &str) -> Vec<String> {
let f = File::open(path).unwrap_or_else(|e| panic!("cannot open fixture {path}: {e}"));
BufReader::new(f)
.lines()
.map_while(Result::ok)
.filter(|l| !l.is_empty())
.collect()
}
/// Build a newline-separated `Vec<u8>` from a slice of items.
fn items_to_bytes(items: &[impl AsRef<str>]) -> Vec<u8> {
let mut buf = Vec::new();
for item in items {
buf.extend_from_slice(item.as_ref().as_bytes());
buf.push(b'\n');
}
buf
}
/// Create a `SkimItemReceiver` that will produce `items` via the full
/// `SkimItemReader` pipeline (the same path used when skim reads stdin).
fn make_receiver(items: &[impl AsRef<str>], options: &SkimOptions) -> SkimItemReceiver {
let reader_opts = SkimItemReaderOption::from_options(options);
let item_reader = SkimItemReader::new(reader_opts);
item_reader.of_bufread(Cursor::new(items_to_bytes(items)))
}
/// Spin-wait until the reader is done and the matcher has stopped (mirrors the
/// logic in `tests/common/insta.rs`). Panics after `timeout`.
fn wait_until_done(skim: &mut Skim<TestBackend>, timeout: Duration) {
let start = std::time::Instant::now();
while !skim.reader_done() {
assert!(start.elapsed() < timeout, "timeout waiting for reader");
skim.check_reader();
std::thread::sleep(Duration::from_millis(1));
}
skim.check_reader();
while !skim.app().matcher_control.stopped() {
assert!(start.elapsed() < timeout, "timeout waiting for matcher");
std::thread::sleep(Duration::from_millis(1));
}
}
// ---------------------------------------------------------------------------
// Benchmark group
// ---------------------------------------------------------------------------
fn criterion_benchmark(c: &mut Criterion) {
// -----------------------------------------------------------------------
// Phase 0 — Options
// -----------------------------------------------------------------------
c.bench_function("parse_options", |b| {
b.iter(|| SkimOptions::parse_from(Vec::<&str>::new()));
});
// `from_env` merges SKIM_DEFAULT_OPTIONS, SKIM_OPTIONS_FILE, and argv.
// In a clean test environment it should behave identically to parse_from,
// but the code path is different and may regress independently.
c.bench_function("options_from_env", |b| {
b.iter(SkimOptions::from_env);
});
// `build()` post-processes raw options: expands keymaps, normalises
// tiebreak / layout, reads history files, etc.
c.bench_function("options_build", |b| {
b.iter_batched(
|| SkimOptions::parse_from(Vec::<&str>::new()),
|opts| opts.build(),
BatchSize::SmallInput,
);
});
// -----------------------------------------------------------------------
// Phase 1 — SkimItemReader pipeline construction
// -----------------------------------------------------------------------
// Building the reader option struct from a SkimOptions (called in sk_main
// before Skim::init).
c.bench_function("item_reader_option_from_options", |b| {
let opts = SkimOptions::default().build();
b.iter(|| SkimItemReaderOption::from_options(&opts));
});
// Constructing a new SkimItemReader (spawns a thread-pool).
c.bench_function("item_reader_new", |b| {
b.iter_batched(
|| {
let opts = SkimOptions::default().build();
SkimItemReaderOption::from_options(&opts)
},
SkimItemReader::new,
BatchSize::SmallInput,
);
});
// `of_bufread` starts the I/O dispatcher and pool threads but does NOT
// wait for all items to be processed; it just returns the receiver.
// Benchmark with the default fixture to get a realistic buffer.
c.bench_function("of_bufread_setup", |b| {
let opts = SkimOptions::default().build();
let data = items_to_bytes(&load_fixture(FIXTURE_DEFAULT));
b.iter_batched(
|| {
let reader_opts = SkimItemReaderOption::from_options(&opts);
(SkimItemReader::new(reader_opts), data.clone())
},
|(reader, bytes)| {
// Calling of_bufread starts the background threads; we
// deliberately drop the receiver immediately so they clean up.
let _rx = reader.of_bufread(Cursor::new(bytes));
},
BatchSize::SmallInput,
);
});
// -----------------------------------------------------------------------
// Phase 2 — Skim::init (unchanged from before, kept for comparison)
// -----------------------------------------------------------------------
c.bench_function("init", |b| {
b.iter_batched(
|| SkimOptions::default().build(),
|options: SkimOptions| Skim::<CrosstermBackend<BufWriter<Stderr>>>::init(options, None),
BatchSize::SmallInput,
);
});
c.bench_function("init_with_source", |b| {
b.iter_batched(
|| {
let (_tx, rx) = bounded(8);
(SkimOptions::default().build(), rx)
},
|input: (SkimOptions, SkimItemReceiver)| {
Skim::<CrosstermBackend<BufWriter<Stderr>>>::init(input.0, Some(input.1))
},
BatchSize::SmallInput,
);
});
// -----------------------------------------------------------------------
// Phase 2+3 — Skim::init + start
// -----------------------------------------------------------------------
c.bench_function("start", |b| {
b.iter_batched(
|| Skim::<CrosstermBackend<BufWriter<Stderr>>>::init(SkimOptions::default().build(), None).unwrap(),
|mut skim: Skim| skim.start(),
BatchSize::SmallInput,
);
});
// -----------------------------------------------------------------------
// Phase 3+4 — ingest + match with realistic data
// -----------------------------------------------------------------------
// These benchmarks measure end-to-end reader+matcher throughput: time from
// start() until all items are ingested and the matcher has finished its
// first pass. They are the closest in-process analog to the CLI startup
// time measured by `cargo bench --bench cli`.
// Small inline fixture (8 items) — baseline latency floor.
c.bench_function("ingest_and_match_small", |b| {
b.iter_batched(
|| {
let opts = SkimOptions::default().build();
let rx = make_receiver(SMALL_ITEMS, &opts);
Skim::<TestBackend>::init(opts, Some(rx)).unwrap()
},
|mut skim: Skim<TestBackend>| {
skim.start();
wait_until_done(&mut skim, Duration::from_secs(5));
},
BatchSize::SmallInput,
);
});
// Medium fixture (≈664 lines from FIXTURE_DEFAULT).
{
let items = load_fixture(FIXTURE_DEFAULT);
let n = items.len() as u64;
let mut group = c.benchmark_group("ingest_and_match_default_fixture");
group.throughput(Throughput::Elements(n));
group.bench_function("ingest_and_match", |b| {
b.iter_batched(
|| {
let opts = SkimOptions::default().build();
let rx = make_receiver(&items, &opts);
Skim::<TestBackend>::init(opts, Some(rx)).unwrap()
},
|mut skim: Skim<TestBackend>| {
skim.start();
wait_until_done(&mut skim, Duration::from_secs(10));
},
BatchSize::SmallInput,
);
});
group.finish();
}
// Large fixture (100 000 lines). Only run when the file exists so the
// bench suite does not fail in environments without the fixture.
if std::path::Path::new(FIXTURE_100K).exists() {
let items = load_fixture(FIXTURE_100K);
let n = items.len() as u64;
let mut group = c.benchmark_group("ingest_and_match_100k_fixture");
group.throughput(Throughput::Elements(n));
// Fewer samples: each iteration loads 100 k items.
group.sample_size(10);
group.bench_function("ingest_and_match", |b| {
b.iter_batched(
|| {
let opts = SkimOptions::default().build();
let rx = make_receiver(&items, &opts);
Skim::<TestBackend>::init(opts, Some(rx)).unwrap()
},
|mut skim: Skim<TestBackend>| {
skim.start();
wait_until_done(&mut skim, Duration::from_secs(30));
},
BatchSize::SmallInput,
);
});
group.finish();
}
// -----------------------------------------------------------------------
// Phase 25 — full_setup (unchanged from before, kept for comparison)
// -----------------------------------------------------------------------
c.bench_function("full_setup", |b| {
b.iter(|| {
let mut options = SkimOptions::default().build();
if let Some(ref filter_query) = options.filter
&& options.query.is_none()
{
options.query = Some(filter_query.clone());
}
let mut skim = Skim::init(options, None).unwrap();
skim.start();
if skim.should_enter() {
skim.init_tui().unwrap();
}
});
});
}
criterion_group!(
name = benches;
config = Criterion::default().sample_size(100);
targets = criterion_benchmark
);
criterion_main!(benches);

131
benches/read_and_match.rs Normal file
View file

@ -0,0 +1,131 @@
#![allow(missing_docs, clippy::pedantic)]
use criterion::{Criterion, criterion_group, criterion_main};
use eyre::{Ok, Result};
use skim::prelude::*;
async fn wait_until_done(mut opts: SkimOptions) -> Result<SkimOutput> {
opts.cmd = Some(String::from("cat benches/fixtures/10M.txt"));
let mut skim = Skim::init(opts, None)?;
skim.start();
skim.init_tui()?;
skim.enter().await?;
while !skim.tick().await? {
if skim.reader_done() && skim.matcher_stopped() {
skim.event_sender().send(Event::Action(Action::Accept(None))).await?;
}
}
Ok(skim.output())
}
fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("default", |b| {
let rt = tokio::runtime::Runtime::new().unwrap();
b.to_async(rt)
.iter(async || wait_until_done(SkimOptions::default()).await);
});
c.bench_function("query", |b| {
let rt = tokio::runtime::Runtime::new().unwrap();
b.to_async(rt)
.iter(async || wait_until_done(SkimOptionsBuilder::default().query("test").build().unwrap()).await);
});
#[cfg(feature = "frizbee")]
c.bench_function("query_frizbee", |b| {
let rt = tokio::runtime::Runtime::new().unwrap();
b.to_async(rt).iter(async || {
wait_until_done(
SkimOptionsBuilder::default()
.query("test")
.algorithm(FuzzyAlgorithm::Frizbee)
.no_typos(true)
.build()
.unwrap(),
)
.await
});
});
c.bench_function("query_ari", |b| {
let rt = tokio::runtime::Runtime::new().unwrap();
b.to_async(rt).iter(async || {
wait_until_done(
SkimOptionsBuilder::default()
.query("test")
.algorithm(FuzzyAlgorithm::Arinae)
.no_typos(true)
.build()
.unwrap(),
)
.await
});
});
#[cfg(feature = "frizbee")]
c.bench_function("query_frizbee_typos", |b| {
let rt = tokio::runtime::Runtime::new().unwrap();
b.to_async(rt).iter(async || {
wait_until_done(
SkimOptionsBuilder::default()
.query("test")
.algorithm(FuzzyAlgorithm::Frizbee)
.build()
.unwrap(),
)
.await
});
});
c.bench_function("query_ari_typos", |b| {
let rt = tokio::runtime::Runtime::new().unwrap();
b.to_async(rt).iter(async || {
wait_until_done(
SkimOptionsBuilder::default()
.query("test")
.algorithm(FuzzyAlgorithm::Arinae)
.build()
.unwrap(),
)
.await
});
});
c.bench_function("typing", |b| {
let rt = tokio::runtime::Runtime::new().unwrap();
b.to_async(rt).iter(async || {
let mut skim = Skim::init(SkimOptionsBuilder::default().cmd("cat bench_data.txt").build()?, None)?;
skim.start();
skim.init_tui()?;
skim.enter().await?;
let s = skim.event_sender();
let mut sent = false;
let mut done_since = 0;
let mut done = false;
while !skim.tick().await? {
if skim.reader_done() && skim.matcher_stopped() {
if done {
done_since += 1;
} else {
done_since = 1;
}
if sent && done_since > 50 {
s.send(Event::Action(Action::Accept(None))).await?;
} else if !sent {
s.send(Event::Action(Action::AddChar('t'))).await?;
s.send(Event::Action(Action::AddChar('e'))).await?;
s.send(Event::Action(Action::AddChar('s'))).await?;
s.send(Event::Action(Action::AddChar('t'))).await?;
sent = true;
}
done = true;
} else {
done = false;
}
}
Ok(skim.output())
});
});
}
criterion_group!(
name = benches;
config = Criterion::default().sample_size(10);
targets = criterion_benchmark
);
criterion_main!(benches);

View file

@ -28,6 +28,8 @@
# sk-tmux: starts sk in a tmux pane
# usage: sk-tmux [LAYOUT OPTIONS] [--] [SK OPTIONS]
echo "[WRN] This script is deprecated in favor of \`sk --tmux\` and will be removed in a later release" >&2
fail() {
>&2 echo "$1"
exit 2
@ -84,7 +86,7 @@ while [[ $# -gt 0 ]]; do
;;
-p*|-w*|-h*|-x*|-y*|-d*|-u*|-r*|-l*)
if [[ "$arg" =~ ^-[pwhxy] ]]; then
[[ "$opt" =~ "-K -E" ]] || opt="-K -E"
[[ "$opt" =~ "-E" ]] || opt="-E"
elif [[ "$arg" =~ ^.[lr] ]]; then
opt="-h"
if [[ "$arg" =~ ^.l ]]; then
@ -165,7 +167,7 @@ fi
args=("${args[@]}" "--no-height")
# Handle zoomed tmux pane without popup options by moving it to a temp window
if [[ ! "$opt" =~ "-K -E" ]] && tmux list-panes -F '#F' | grep -q Z; then
if [[ ! "$opt" =~ "-E" ]] && tmux list-panes -F '#F' | grep -q Z; then
zoomed_without_popup=1
original_window=$(tmux display-message -p "#{window_id}")
tmp_window=$(tmux new-window -d -P -F "#{window_id}" "bash -c 'while :; do for c in \\| / - '\\;' do sleep 0.2; printf \"\\r\$c sk-tmux is running\\r\"; done; done'")
@ -207,7 +209,7 @@ trap 'cleanup 1' SIGUSR1
trap 'cleanup' EXIT
envs="export TERM=$TERM "
[[ "$opt" =~ "-K -E" ]] && SKIM_DEFAULT_OPTIONS="--margin 0,1 $SKIM_DEFAULT_OPTIONS"
[[ "$opt" =~ "-E" ]] && SKIM_DEFAULT_OPTIONS="--margin 0,1 $SKIM_DEFAULT_OPTIONS"
[[ -n "$SKIM_DEFAULT_OPTIONS" ]] && envs="$envs SKIM_DEFAULT_OPTIONS=$(printf %q "$SKIM_DEFAULT_OPTIONS")"
[[ -n "$SKIM_DEFAULT_COMMAND" ]] && envs="$envs SKIM_DEFAULT_COMMAND=$(printf %q "$SKIM_DEFAULT_COMMAND")"
echo "$envs;" > "$argsf"
@ -221,7 +223,7 @@ close="; trap - EXIT SIGINT SIGTERM $close"
export TMUX=$(cut -d , -f 1,2 <<< "$TMUX")
mkfifo -m o+w $fifo2
if [[ "$opt" =~ "-K -E" ]]; then
if [[ "$opt" =~ "-E" ]]; then
cat $fifo2 &
if [[ -n "$term" ]] || [[ -t 0 ]]; then
cat <<< "\"$sk\" $opts > $fifo2; out=\$? $close; exit \$out" >> $argsf
@ -230,7 +232,7 @@ if [[ "$opt" =~ "-K -E" ]]; then
cat <<< "\"$sk\" $opts < $fifo1 > $fifo2; out=\$? $close; exit \$out" >> $argsf
cat <&0 > $fifo1 &
fi
tmux popup -d "$PWD" "${tmux_args[@]}" $opt -R "bash $argsf" > /dev/null 2>&1
tmux popup -d "$PWD" "${tmux_args[@]}" $opt "bash $argsf" > /dev/null 2>&1
exit $?
fi

View file

@ -1,33 +0,0 @@
# This script takes care of building your crate and packaging it for release
set -ex
main() {
local src=$(pwd) \
stage=
case $TRAVIS_OS_NAME in
linux)
stage=$(mktemp -d)
;;
osx)
stage=$(mktemp -d -t tmp)
;;
esac
test -f Cargo.lock || cargo generate-lockfile
# TODO Update this to build the artifacts that matter to you
cross build --release --target $TARGET
# TODO Update this to package the right artifacts
cp target/$TARGET/release/sk $stage/
cd $stage
tar czf $src/$CRATE_NAME-$TRAVIS_TAG-$TARGET.tar.gz *
cd $src
rm -rf $stage
}
main

View file

@ -1,66 +0,0 @@
set -ex
main() {
local target=
if [ $TRAVIS_OS_NAME = linux ]; then
target=x86_64-unknown-linux-musl
sort=sort
else
target=x86_64-apple-darwin
sort=gsort # for `sort --sort-version`, from brew's coreutils.
fi
# Builds for iOS are done on OSX, but require the specific target to be
# installed.
case $TARGET in
aarch64-apple-ios)
rustup target install aarch64-apple-ios
;;
armv7-apple-ios)
rustup target install armv7-apple-ios
;;
armv7s-apple-ios)
rustup target install armv7s-apple-ios
;;
i386-apple-ios)
rustup target install i386-apple-ios
;;
x86_64-apple-ios)
rustup target install x86_64-apple-ios
;;
esac
# This fetches latest stable release
local tag=$(git ls-remote --tags --refs --exit-code https://github.com/japaric/cross \
| cut -d/ -f3 \
| grep -E '^v[0.1.0-9.]+$' \
| $sort --version-sort \
| tail -n1)
curl -LSfs https://japaric.github.io/trust/install.sh | \
sh -s -- \
--force \
--git japaric/cross \
--tag $tag \
--target $target
# For test
case $TARGET in
x86_64-unknown-linux-gnu|i686-unknown-linux-gnu)
python3 -V
tmux -V
sudo apt-get install -y zsh
stty cols 80
;;
x86_64-apple-darwin|i686-apple-darwin)
HOMEBREW_NO_AUTO_UPDATE=1 brew install tmux
HOMEBREW_NO_AUTO_UPDATE=1 brew install zsh
python -V
python3 -V
tmux -V
stty cols 80
;;
esac
}
main

View file

@ -1,30 +0,0 @@
# This script takes care of testing
set -ex
main() {
if [ ! -z $DISABLE_TESTS ]; then
return
fi
cross test --release --target $TARGET
cross build --release --target $TARGET
mkdir -p target/release
cp target/$TARGET/release/sk target/release
case $TARGET in
x86_64-unknown-linux-gnu|i686-unknown-linux-gnu|x86_64-unknown-linux-musl)
# run the integration test
tmux new "python3 test/test_skim.py &> out && touch ok" && cat out && [ -e ok ]
;;
x86_64-apple-darwin|i686-apple-darwin)
# run the integration test
tmux new "python3 test/test_skim.py &> out && touch ok" && cat out && [ -e ok ]
;;
*)
;;
esac
}
if [ -z $TRAVIS_TAG ]; then
main
fi

121
cliff.toml Normal file
View file

@ -0,0 +1,121 @@
# git-cliff ~ configuration file
# https://git-cliff.org/docs/configuration
[changelog]
# A Tera template to be rendered for each release in the changelog.
# See https://keats.github.io/tera/docs/#introduction
body = """
{%- macro remote_url() -%}
https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }}
{%- endmacro -%}
{% if version %}\
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
{% else %}\
## [unreleased]
{% endif %}\
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | striptags | trim | upper_first }}
{% for commit in commits %}
- {% if commit.scope %}*({{ commit.scope }})* {% endif %}\
{% if commit.breaking %}[**breaking**] {% endif %}\
{{ commit.message | upper_first }}\
{% endfor %}
{% endfor %}
{%- if github.contributors | filter(attribute="is_first_time", value=true) | length != 0 %}
### New Contributors
{%- endif -%}
{% for contributor in github.contributors | filter(attribute="is_first_time", value=true) %}
* @{{ contributor.username }} made their first contribution
{%- if contributor.pr_number %} in \
[#{{ contributor.pr_number }}]({{ self::remote_url() }}/pull/{{ contributor.pr_number }}) \
{%- endif %}
{%- endfor %}\n
{%- if github.contributors | filter(attribute="is_first_time", value=true) | length != 0 %}{% raw %}\n{% endraw -%}{% endif %}
"""
header = """
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
"""
# Remove leading and trailing whitespaces from the changelog's body.
footer = "<!-- generated by git-cliff -->"
trim = true
# Render body even when there are no releases to process.
render_always = true
# An array of regex based postprocessors to modify the changelog.
postprocessors = [
# Replace the placeholder <REPO> with a URL.
#{ pattern = '<REPO>', replace = "https://github.com/orhun/git-cliff" },
]
# render body even when there are no releases to process
# render_always = true
# output file path
# output = "test.md"
[git]
# Parse commits according to the conventional commits specification.
# See https://www.conventionalcommits.org
conventional_commits = true
# Exclude commits that do not match the conventional commits specification.
filter_unconventional = true
# Require all commits to be conventional.
# Takes precedence over filter_unconventional.
require_conventional = false
# Split commits on newlines, treating each line as an individual commit.
split_commits = false
# An array of regex based parsers to modify commit messages prior to further processing.
commit_preprocessors = [
# Allow commits named `type!(scope): desc` and treat them like `type(scope)!: desc`
{ pattern = '(.*)!\((.*)\):(.*)', replace = "$1($2)!:$3" },
]
# Prevent commits that are breaking from being excluded by commit parsers.
protect_breaking_commits = false
# An array of regex based parsers for extracting data from the commit message.
# Assigns commits to groups.
# Optionally sets the commit's scope and can decide to exclude commits from further processing.
commit_parsers = [
{ message = "^feat", group = "<!-- 0 -->🚀 Features" },
{ message = "^fix", group = "<!-- 1 -->🐛 Bug Fixes" },
{ message = "^doc", group = "<!-- 3 -->📚 Documentation" },
{ message = "^perf", group = "<!-- 4 -->⚡ Performance" },
{ message = "^refactor", group = "<!-- 2 -->🚜 Refactor" },
{ message = "^style", group = "<!-- 5 -->🎨 Styling" },
{ message = "^test", group = "<!-- 6 -->🧪 Testing" },
{ message = "^ci", group = "<!-- 7 -->🤖 CI" },
{ message = "^chore\\(release\\): prepare for", skip = true },
{ message = "^chore\\(deps.*\\)", skip = true },
{ message = "^chore\\(pr\\)", skip = true },
{ message = "^chore\\(pull\\)", skip = true },
{ message = "^chore", group = "<!-- 8 -->⚙️ Miscellaneous Tasks" },
{ body = ".*security", group = "<!-- 9 -->🛡️ Security" },
{ message = "^revert", group = "<!-- 10 -->◀️ Revert" },
{ message = "^release", skip = true },
{ message = ".*", group = "<!-- 11 -->💼 Other" },
]
# Exclude commits that are not matched by any commit parser.
filter_commits = false
# Fail on a commit that is not matched by any commit parser.
fail_on_unmatched_commit = false
# An array of link parsers for extracting external references, and turning them into URLs, using regex.
link_parsers = []
# Include only the tags that belong to the current branch.
use_branch_tags = false
# Order releases topologically instead of chronologically.
topo_order = false
# Order commits topologically instead of chronologically.
topo_order_commits = true
# Order of commits in each group/release within the changelog.
# Allowed values: newest, oldest
sort_commits = "oldest"
# Process submodules commits
recurse_submodules = false

35
dist-workspace.toml Normal file
View file

@ -0,0 +1,35 @@
[workspace]
members = ["cargo:."]
# Config for 'dist'
[dist]
# The preferred dist version to use in CI (Cargo.toml SemVer syntax)
cargo-dist-version = "0.32.0"
# CI backends to support
ci = "github"
# The installers to generate for each app
installers = ["shell", "powershell", "msi"]
# Target platforms to build apps for (Rust target-triple syntax)
targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "aarch64-unknown-linux-musl", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl", "x86_64-pc-windows-msvc"]
# Path that installers should place binaries in
install-path = "CARGO_HOME"
# Whether to install an updater program
install-updater = false
# Extra static files to include in each App (path relative to this Cargo.toml's dir)
include = ["./man/", "./shell/"]
# Plan jobs to run in CI
plan-jobs = ["./test"]
# Global artifacts jobs to run in CI
global-artifacts-jobs = ["./package"]
# Publish jobs to run in CI
publish-jobs = ["./publish", "./winget", "./apt"]
# Whether to publish prereleases to package managers
publish-prereleases = true
[dist.github-custom-job-permissions.test]
contents = "write"
id-token = "write"
[dist.github-custom-job-permissions.apt]
contents = "write"
id-token = "write"

BIN
examples/Lenna.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 463 KiB

33
examples/ansi.rs Normal file
View file

@ -0,0 +1,33 @@
//! Demonstrates selecting ANSI-colored command output.
extern crate skim;
use skim::prelude::*;
use skim::reader::CommandCollector;
fn main() {
env_logger::init();
let glogm = "git log --oneline --color=always | head -n10";
let options = SkimOptionsBuilder::default()
.height("50%")
.cmd(glogm)
.preview("echo {}")
.multi(true)
.reverse(true)
.cmd_collector(Rc::new(RefCell::new(SkimItemReader::new(
SkimItemReaderOption::default().ansi(true),
))) as Rc<RefCell<dyn CommandCollector>>)
.build()
.unwrap();
log::debug!("Options: ansi {}", options.ansi);
let selected_items = Skim::run_with(options, None)
.map(|out| out.selected_items)
.unwrap_or_default();
for item in &selected_items {
println!("selected: {}", item.output());
}
}

10
examples/async.rs Normal file
View file

@ -0,0 +1,10 @@
//! Runs skim from an async Tokio entry point.
use skim::Skim;
use skim::prelude::SkimOptionsBuilder;
#[tokio::main]
async fn main() {
let options = SkimOptionsBuilder::default().build().unwrap();
Skim::run_with(options, None).unwrap();
}

19
examples/base.rs Normal file
View file

@ -0,0 +1,19 @@
//! Demonstrates basic item selection with inline status information.
use skim::prelude::*;
use skim::tui::statusline::InfoDisplay;
fn main() -> eyre::Result<()> {
let opts = SkimOptionsBuilder::default()
.multi(true)
.reverse(true)
.info(InfoDisplay::Inline)
.build()?;
let res = Skim::run_items(opts, ["hello", "world"])?;
for item in res.selected_items {
println!("Selected {} (id {})", item.output(), item.rank.index);
}
Ok(())
}

54
examples/cmd_collector.rs Normal file
View file

@ -0,0 +1,54 @@
//! Demonstrates collecting items from a command.
extern crate skim;
use reader::CommandCollector;
use skim::prelude::*;
struct BasicSkimItem {
value: String,
}
impl SkimItem for BasicSkimItem {
fn text(&self) -> Cow<'_, str> {
Cow::Borrowed(&self.value)
}
}
struct BasicCmdCollector {
pub items: Vec<String>,
}
impl CommandCollector for BasicCmdCollector {
fn invoke(&mut self, _cmd: &str, _components_to_stop: Arc<AtomicUsize>) -> (SkimItemReceiver, Sender<i32>) {
let (tx, rx) = unbounded();
let (tx_interrupt, _rx_interrupt) = unbounded();
let mut batch = Vec::new();
while let Some(value) = self.items.pop() {
let item = BasicSkimItem { value };
batch.push(Arc::from(item) as Arc<dyn SkimItem>);
}
if !batch.is_empty() {
tx.send(batch).unwrap();
}
(rx, tx_interrupt)
}
}
fn main() {
let cmd_collector = BasicCmdCollector {
items: vec![String::from("foo"), String::from("bar"), String::from("baz")],
};
let options = SkimOptionsBuilder::default()
.cmd_collector(Rc::from(RefCell::from(cmd_collector)))
.build()
.unwrap();
let selected_items = Skim::run_with(options, None)
.map(|out| out.selected_items)
.unwrap_or_default();
for item in &selected_items {
println!("{}", item.output());
}
}

View file

@ -0,0 +1,96 @@
//! Demonstrates binding custom action callbacks to keyboard shortcuts.
extern crate skim;
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use skim::prelude::*;
use skim::tui::event::{Action, ActionCallback, Event};
use std::io::Cursor;
/// Runs the custom action keybinding example.
///
/// It shows how to:
/// 1. Create custom action callbacks (both sync and async)
/// 2. Bind them to specific key combinations
/// 3. Use them interactively in skim
fn main() {
// Create a synchronous callback that adds a prefix to the query.
// Use `new_sync` for plain closures that do not need to await anything.
let add_prefix_callback = ActionCallback::new_sync(|app: &mut skim::tui::App| {
// Get current query and add prefix
let current_query = app.input.value.clone();
// Clear the line first, then add new content
let mut events = vec![Event::Action(Action::UnixLineDiscard)];
let prefix = "TODO: ";
for ch in prefix.chars() {
events.push(Event::Action(Action::AddChar(ch)));
}
// Add back the original query
for ch in current_query.chars() {
events.push(Event::Action(Action::AddChar(ch)));
}
Ok(events)
});
// Create an async callback that selects all and exits.
// Use `new` for async closures or blocks that may await futures.
let select_all_callback = ActionCallback::new(|app: &mut skim::tui::App| {
let count = app.item_pool.len();
async move {
// Async work could go here (e.g. HTTP requests, file I/O, …).
Ok(vec![
Event::Action(Action::SelectAll),
Event::Action(Action::Accept(Some(format!("Selected {count} items")))),
])
}
});
// Build basic options
let mut options = SkimOptionsBuilder::default()
.multi(true)
.prompt("Select> ")
.header("<C-p>: add prefix to prompt\t<C-a>: select all and exit with count")
.build()
.unwrap();
// Now manually add custom keybindings to the keymap
// We can access the keymap directly since it's public
// Bind Ctrl-P to add prefix
options.keymap.insert(
KeyEvent::new(KeyCode::Char('p'), KeyModifiers::CONTROL),
vec![Action::Custom(add_prefix_callback)],
);
// Bind Ctrl-A to select all with message
options.keymap.insert(
KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL),
vec![Action::Custom(select_all_callback)],
);
// Create sample items
let items = [
"Write documentation",
"Fix bug #123",
"Implement feature X",
"Review pull request",
"Update dependencies",
"Refactor module Y",
"Add unit tests",
"Optimize performance",
];
let item_reader = SkimItemReader::default();
let input = items.join("\n");
let item_source = item_reader.of_bufread(Cursor::new(input));
// Run skim with our custom keybindings
if let Ok(output) = Skim::run_with(options, Some(item_source)) {
println!("output: {output:?}");
} else {
println!("\nAborted!");
}
}

View file

@ -1,3 +1,5 @@
//! Demonstrates using a custom item type with skim.
extern crate skim;
use skim::prelude::*;
@ -6,7 +8,7 @@ struct MyItem {
}
impl SkimItem for MyItem {
fn text(&self) -> Cow<str> {
fn text(&self) -> Cow<'_, str> {
Cow::Borrowed(&self.inner)
}
@ -19,31 +21,35 @@ impl SkimItem for MyItem {
}
}
pub fn main() {
fn main() {
let options = SkimOptionsBuilder::default()
.height(Some("50%"))
.height("50%")
.multi(true)
.preview(Some("")) // preview should be specified to enable preview window
.preview("") // preview should be specified to enable preview window
.build()
.unwrap();
env_logger::init();
let (tx_item, rx_item): (SkimItemSender, SkimItemReceiver) = unbounded();
let _ = tx_item.send(Arc::new(MyItem {
inner: "color aaaa".to_string(),
}));
let _ = tx_item.send(Arc::new(MyItem {
inner: "bbbb".to_string(),
}));
let _ = tx_item.send(Arc::new(MyItem {
inner: "ccc".to_string(),
}));
let _ = tx_item.send(vec![
Arc::new(MyItem {
inner: "color aaaa".to_string(),
}) as Arc<dyn SkimItem>,
Arc::new(MyItem {
inner: "bbbb".to_string(),
}) as Arc<dyn SkimItem>,
Arc::new(MyItem {
inner: "ccc".to_string(),
}) as Arc<dyn SkimItem>,
]);
drop(tx_item); // so that skim could know when to stop waiting for more items.
let selected_items = Skim::run_with(&options, Some(rx_item))
let selected_items = Skim::run_with(options, Some(rx_item))
.map(|out| out.selected_items)
.unwrap_or_else(Vec::new);
.unwrap_or_default();
for item in selected_items.iter() {
for item in &selected_items {
println!("{}", item.output());
}
}

View file

@ -1,32 +1,39 @@
//! Demonstrates binding custom key combinations to custom actions.
extern crate skim;
use crossterm::event::{KeyCode, KeyModifiers};
use skim::prelude::*;
// No action is actually performed on your filesystem!
// This example only produce friendly print statements!
fn fake_delete_item(item: &str) {
println!("Deleting item `{}`...", item);
println!("Deleting item `{item}`...");
}
fn fake_create_item(item: &str) {
println!("Creating a new item `{}`...", item);
println!("Creating a new item `{item}`...");
}
pub fn main() {
fn main() {
// Note: `accept` is a keyword used define custom actions.
// For full list of accepted keywords see `parse_event` in `src/event.rs`.
// `delete` and `create` are arbitrary keywords used for this example.
let options = SkimOptionsBuilder::default()
.multi(true)
.bind(vec!["bs:abort", "Enter:accept"])
.bind(vec!["bs:abort".into(), "enter:accept".into()])
.build()
.unwrap();
Skim::run_with(&options, None).map(|out| match out.final_key {
// Delete each selected item
Key::Backspace => out.selected_items.iter().for_each(|i| fake_delete_item(&i.text())),
// Create a new item based on the query
Key::Enter => fake_create_item(out.query.as_ref()),
_ => (),
});
if let Ok(out) = Skim::run_with(options, None) {
match (out.final_key.code, out.final_key.modifiers) {
// Delete each selected item
(KeyCode::Backspace, KeyModifiers::NONE) => {
out.selected_items.iter().for_each(|i| fake_delete_item(&i.text()));
}
// Create a new item based on the query
(KeyCode::Enter, KeyModifiers::NONE) => fake_create_item(out.query.as_ref()),
_ => (),
}
}
}

View file

@ -1,48 +1,50 @@
//! Illustrates downcasting custom structs that implement `SkimItem`.
extern crate skim;
use skim::prelude::*;
/// This example illustrates downcasting custom structs that implement
/// `SkimItem` after calling `Skim::run_with`.
#[derive(Debug, Clone)]
struct Item {
text: String,
}
impl SkimItem for Item {
fn text(&self) -> Cow<str> {
fn text(&self) -> Cow<'_, str> {
Cow::Borrowed(&self.text)
}
fn preview(&self, _context: PreviewContext) -> ItemPreview {
ItemPreview::Text(self.text.to_owned())
ItemPreview::Text(self.text.clone())
}
}
pub fn main() {
fn main() {
let options = SkimOptionsBuilder::default()
.height(Some("50%"))
.height("50%")
.multi(true)
.preview(Some(""))
.preview("")
.build()
.unwrap();
let (tx, rx): (SkimItemSender, SkimItemReceiver) = unbounded();
tx.send(Arc::new(Item { text: "a".to_string() })).unwrap();
tx.send(Arc::new(Item { text: "b".to_string() })).unwrap();
tx.send(Arc::new(Item { text: "c".to_string() })).unwrap();
tx.send(vec![
Arc::new(Item { text: "a".into() }) as Arc<dyn SkimItem>,
Arc::new(Item { text: "b".into() }) as Arc<dyn SkimItem>,
Arc::new(Item { text: "c".into() }) as Arc<dyn SkimItem>,
])
.unwrap();
drop(tx);
let selected_items = Skim::run_with(&options, Some(rx))
let selected_items = Skim::run_with(options, Some(rx))
.map(|out| out.selected_items)
.unwrap_or_else(Vec::new)
.unwrap_or_default()
.iter()
.map(|selected_item| (**selected_item).as_any().downcast_ref::<Item>().unwrap().to_owned())
.map(|selected_item| selected_item.downcast_item::<Item>().unwrap().to_owned())
.collect::<Vec<Item>>();
for item in selected_items {
println!("{:?}", item);
println!("{item:?}");
}
}

36
examples/fine-grain.rs Normal file
View file

@ -0,0 +1,36 @@
//! Demonstrates fine-grained control over skim lifecycle events.
extern crate skim;
use eyre::Result;
use skim::prelude::*;
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<()> {
let options = SkimOptionsBuilder::default().height("50%").multi(true).build()?;
let (tx_item, rx_item): (SkimItemSender, SkimItemReceiver) = unbounded();
let mut skim = Skim::init(options, Some(rx_item))?;
skim.start();
skim.init_tui()?;
let event_tx = skim.event_sender();
skim.enter().await?;
let output = skim
.run_until(async move {
for i in 1..=10 {
let _ = event_tx.try_send(Event::ClearItems);
let _ = tx_item.send(vec![Arc::new(format!("item {i}")) as Arc<dyn SkimItem>]);
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
}
})
.await?;
for item in &output.selected_items {
println!("{}", item.output());
}
Ok(())
}

View file

@ -0,0 +1,65 @@
//! Demonstrates fuzzy matching lines with selectable matching algorithms.
use skim::fuzzy_matcher::FuzzyMatcher;
use skim::fuzzy_matcher::clangd::ClangdMatcher;
use skim::fuzzy_matcher::skim::SkimMatcherV2;
use std::env;
use std::io::{self, BufRead};
use std::process::exit;
type IndexType = usize;
fn main() {
let args: Vec<String> = env::args().collect();
// arg parsing (manually)
let mut arg_iter = args.iter().skip(1);
let mut pattern = String::new();
let mut algorithm = Some("skim");
while let Some(arg) = arg_iter.next() {
if arg == "--algo" {
algorithm = arg_iter.next().map(String::as_ref);
} else {
pattern.clone_from(arg);
}
}
if pattern.is_empty() {
eprintln!("Usage: echo <piped_input> | fz --algo [skim|clangd] <pattern>");
exit(1);
}
let matcher: Box<dyn FuzzyMatcher> = match algorithm {
Some("skim" | "skim_v2") => Box::new(SkimMatcherV2::default()),
Some("clangd") => Box::new(ClangdMatcher::default()),
_ => panic!("Algorithm not supported: {algorithm:?}"),
};
let stdin = io::stdin();
for line in stdin.lock().lines() {
if let Ok(line) = line
&& let Some((score, indices)) = matcher.fuzzy_indices(&line, &pattern)
{
println!("{:8}: {}", score, wrap_matches(&line, &indices));
}
}
}
fn wrap_matches(line: &str, indices: &[IndexType]) -> String {
let mut ret = String::new();
let mut peekable = indices.iter().peekable();
let ansi_invert: &str = str::from_utf8(&[27, b'[', b'7', b'm']).unwrap();
let ansi_reset: &str = str::from_utf8(&[27, b'[', b'0', b'm']).unwrap();
for (idx, ch) in line.chars().enumerate() {
let next_id = **peekable.peek().unwrap_or(&&(line.len() as IndexType));
if next_id == (idx as IndexType) {
ret.push_str(format!("{ansi_invert}{ch}{ansi_reset}").as_str());
peekable.next();
} else {
ret.push(ch);
}
}
ret
}

25
examples/image.rs Normal file
View file

@ -0,0 +1,25 @@
//! Demonstrates image previews using a literal image path.
//!
//! Run with:
//! `cargo run --example image`
use skim::options::ImageProtocol;
use skim::prelude::*;
fn main() -> eyre::Result<()> {
env_logger::init();
let options = SkimOptionsBuilder::default()
.preview("{}")
.preview_window("right:60%")
.image(ImageProtocol::Halfblocks)
.build()?;
let output = Skim::run_items(options, ["examples/Lenna.png"])?;
for item in &output.selected_items {
println!("{}", item.output());
}
Ok(())
}

26
examples/multiple_runs.rs Normal file
View file

@ -0,0 +1,26 @@
//! Runs skim repeatedly to check that runs clean up their worker threads.
use skim::Skim;
use skim::prelude::SkimOptionsBuilder;
// Hint: use `ps -T -p $(pgrep -f target/debug/examples/multiple_runs)` to watch threads while the
// different invocations run, and make sure none is leaking through
fn main() {
for i in 0..3 {
let opts = SkimOptionsBuilder::default()
.header(format!("run {i}"))
.cmd("cat benches/fixtures/10M.txt")
.build()
.unwrap();
let res = Skim::run_with(opts, None).unwrap();
#[cfg(all(target_os = "linux", target_env = "gnu"))]
unsafe {
nix::libc::malloc_trim(0);
}
println!(
"run {i}: {:?}, sleeping for 5 secs",
res.selected_items.first().map(|x| x.output())
);
std::thread::sleep(std::time::Duration::from_secs(5));
}
}

View file

@ -1,22 +1,25 @@
//! Demonstrates matching against selected fields with the `nth` option.
extern crate skim;
use skim::prelude::*;
use std::io::Cursor;
/// `nth` option is supported by SkimItemReader.
/// Runs the `nth` example.
///
/// `nth` option is supported by `SkimItemReader`.
/// In the example below, with `nth=2` set, only `123` could be matched.
pub fn main() {
fn main() {
let input = "foo 123";
let options = SkimOptionsBuilder::default().query(Some("f")).build().unwrap();
let item_reader = SkimItemReader::new(SkimItemReaderOption::default().nth("2").build());
let options = SkimOptionsBuilder::default().query("f").build().unwrap();
let item_reader = SkimItemReader::new(SkimItemReaderOption::default().nth(vec!["2"].into_iter()).build());
let items = item_reader.of_bufread(Cursor::new(input));
let selected_items = Skim::run_with(&options, Some(items))
let selected_items = Skim::run_with(options, Some(items))
.map(|out| out.selected_items)
.unwrap_or_else(Vec::new);
.unwrap_or_default();
for item in selected_items.iter() {
for item in &selected_items {
println!("{}", item.output());
}
}

View file

@ -1,36 +1,35 @@
//! Demonstrates configuring skim with `SkimOptionsBuilder`.
extern crate skim;
use skim::prelude::*;
use std::io::Cursor;
pub fn main() {
let options = SkimOptionsBuilder::default()
.height(Some("50%"))
.multi(true)
.build()
.unwrap();
fn main() {
let item_reader = SkimItemReader::default();
//==================================================
// first run
let options = SkimOptionsBuilder::default().height("50%").multi(true).build().unwrap();
let input = "aaaaa\nbbbb\nccc";
let items = item_reader.of_bufread(Cursor::new(input));
let selected_items = Skim::run_with(&options, Some(items))
let selected_items = Skim::run_with(options, Some(items))
.map(|out| out.selected_items)
.unwrap_or_else(Vec::new);
.unwrap_or_default();
for item in selected_items.iter() {
for item in &selected_items {
println!("{}", item.output());
}
//==================================================
// second run
let options = SkimOptionsBuilder::default().height("50%").multi(true).build().unwrap();
let input = "11111\n22222\n333333333";
let items = item_reader.of_bufread(Cursor::new(input));
let selected_items = Skim::run_with(&options, Some(items))
let selected_items = Skim::run_with(options, Some(items))
.map(|out| out.selected_items)
.unwrap_or_else(Vec::new);
.unwrap_or_default();
for item in selected_items.iter() {
for item in &selected_items {
println!("{}", item.output());
}
}

View file

@ -0,0 +1,27 @@
//! Demonstrates generating preview content with a callback.
use std::io::Cursor;
use skim::prelude::*;
fn main() {
env_logger::init();
let options = SkimOptionsBuilder::default()
.multi(true)
.preview_fn(PreviewCallback::from(|items: Vec<Arc<dyn SkimItem>>| {
items.iter().map(|s| s.text().to_ascii_uppercase()).collect::<Vec<_>>()
}))
.build()
.unwrap();
let item_reader = SkimItemReader::default();
let input = "aaaaa\nbbbb\nccc";
let items = item_reader.of_bufread(Cursor::new(input));
let selected_items = Skim::run_with(options, Some(items))
.map(|out| out.selected_items)
.unwrap_or_default();
for item in &selected_items {
println!("{}", item.output());
}
}

View file

@ -0,0 +1,20 @@
//! Sends multiple batches of items through a skim item receiver.
use std::sync::Arc;
use skim::prelude::*;
fn main() {
let (sender, receiver): (SkimItemSender, SkimItemReceiver) = unbounded();
let mut batch = Vec::new();
for num in 1..=8 {
batch.push(Arc::new(format!("Option {num}")) as Arc<dyn SkimItem>);
}
sender.send(batch).unwrap();
drop(sender); // bug replicates even without this
let _ = Skim::run_with(
SkimOptionsBuilder::default().multi(true).build().unwrap(),
Some(receiver),
);
}

View file

@ -1,14 +1,16 @@
//! Minimal example that runs skim and prints selected items.
extern crate skim;
use skim::prelude::*;
pub fn main() {
fn main() {
let options = SkimOptions::default();
let selected_items = Skim::run_with(&options, None)
let selected_items = Skim::run_with(options, None)
.map(|out| out.selected_items)
.unwrap_or_else(Vec::new);
.unwrap_or_default();
for item in selected_items.iter() {
for item in &selected_items {
println!("{}", item.output());
}
}

34
examples/selector.rs Normal file
View file

@ -0,0 +1,34 @@
//! Demonstrates a custom selector implementation.
extern crate skim;
use skim::prelude::*;
struct BasicSelector {
pub pat: String,
}
impl Selector for BasicSelector {
fn should_select(&self, _index: usize, item: &dyn SkimItem) -> bool {
item.text().contains(&self.pat)
}
}
fn main() {
let selector = BasicSelector {
pat: String::from("examples"),
};
let options = SkimOptionsBuilder::default()
.multi(true)
.selector(Rc::from(selector))
.query("skim/")
.build()
.unwrap();
let selected_items = Skim::run_with(options, None)
.map(|out| out.selected_items)
.unwrap_or_default();
for item in &selected_items {
println!("{}", item.output());
}
}

23
examples/tick.rs Normal file
View file

@ -0,0 +1,23 @@
//! Demonstrates manually driving skim ticks.
use skim::prelude::*;
#[tokio::main]
async fn main() -> eyre::Result<()> {
let opts = SkimOptionsBuilder::default().cmd("cat bench_data.txt").build()?;
println!("START");
let mut skim = Skim::init(opts, None)?;
skim.start();
skim.init_tui()?;
skim.enter().await?;
while !skim.tick().await? {
if skim.reader_done() && skim.matcher_stopped() {
skim.event_sender()
.send(Event::Action(Action::Accept(Some(String::from("Done")))))
.await?;
}
}
println!("DONE: {:?}", skim.output());
eyre::Ok(())
}

24
flake.lock Normal file
View file

@ -0,0 +1,24 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1782545840,
"narHash": "sha256-CAi8oAZaE6pTkcYQBnnOlvmfMgG/p1AO0FohKKN3J7I=",
"rev": "3d46470bb3030020f7e1361f33514854f5bfa86d",
"type": "tarball",
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.11pre1023445.3d46470bb303/nixexprs.tar.xz?lastModified=1782545840&rev=3d46470bb3030020f7e1361f33514854f5bfa86d"
},
"original": {
"type": "tarball",
"url": "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

85
flake.nix Normal file
View file

@ -0,0 +1,85 @@
{
description = "Nix flake for skim development";
inputs.nixpkgs.url = "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz";
outputs =
inputs:
let
inherit (inputs.nixpkgs) lib;
systems = lib.systems.flakeExposed;
eachSystem = lib.genAttrs systems;
pkgsFor =
system:
import inputs.nixpkgs {
inherit system;
config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ "vagrant" ];
};
in
{
devShells = eachSystem (
system:
let
pkgs = pkgsFor system;
# --- package groups -------------------------------------------------------
base = with pkgs; [
rustup
just
];
tests = with pkgs; [
cargo-nextest
cargo-insta
cargo-llvm-cov
cargo-fuzz
tmux
];
utils = with pkgs; [
hyperfine
cargo-edit
cargo-public-api
cargo-msrv
git-cliff
cargo-dist
cargo-cross
cargo-xwin
gnuplot
llvm
cargo-bloat
cargo-public-api
];
gungraun = with pkgs; [
valgrind
libclang
binutils
];
vagrantDeps = with pkgs; [
vagrant
rsync
];
# --- shell hooks (only groups that need env vars) -------------------------
gungraunHook = ''
export LIBCLANG_PATH="${pkgs.libclang.lib}/lib"
export LD_LIBRARY_PATH="${pkgs.valgrind.out}/lib:$LD_LIBRARY_PATH"
'';
vagrantHook = ''
export VAGRANT_LIBVIRT_OVMF_CODE="${pkgs.OVMF.fd}/FV/OVMF_CODE.fd"
'';
mkShell = packages: shellHook: pkgs.mkShellNoCC { inherit packages shellHook; };
in
{
default = mkShell base "";
tests = mkShell (base ++ tests) "";
utils = mkShell (base ++ utils) "";
dev = mkShell (base ++ tests ++ utils) "";
gungraun = mkShell (base ++ gungraun) gungraunHook;
vagrant = mkShell (base ++ vagrantDeps) vagrantHook;
full = mkShell (base ++ tests ++ utils ++ gungraun ++ vagrantDeps) (gungraunHook + vagrantHook);
}
);
formatter = eachSystem (system: (pkgsFor system).nixfmt);
};
}

4
fuzz/.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
target
corpus
artifacts
coverage

2267
fuzz/Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

52
fuzz/Cargo.toml Normal file
View file

@ -0,0 +1,52 @@
[package]
name = "skim-fuzz"
version = "0.0.0"
publish = false
edition = "2024"
[package.metadata]
cargo-fuzz = true
[dependencies]
arbitrary = { version = "1", features = ["derive"] }
libfuzzer-sys = "0.4"
regex = "1"
[dependencies.skim]
path = ".."
default-features = false
[[bin]]
name = "ansi_strip"
path = "fuzz_targets/ansi_strip.rs"
test = false
doc = false
bench = false
[[bin]]
name = "field_extract"
path = "fuzz_targets/field_extract.rs"
test = false
doc = false
bench = false
[[bin]]
name = "fuzzy_match"
path = "fuzz_targets/fuzzy_match.rs"
test = false
doc = false
bench = false
[[bin]]
name = "query_match"
path = "fuzz_targets/query_match.rs"
test = false
doc = false
bench = false
[[bin]]
name = "keymap_parse"
path = "fuzz_targets/keymap_parse.rs"
test = false
doc = false
bench = false

57
fuzz/README.md Normal file
View file

@ -0,0 +1,57 @@
# Fuzzing
This directory contains [`cargo-fuzz`](https://github.com/rust-fuzz/cargo-fuzz)
(libFuzzer) targets for skim's hand-written, untrusted-input-facing parsers:
text that flows in from stdin, `--ansi` sequences, `--nth`/`--with-nth` field
specs, the search query syntax, and `--bind` key maps. These are exactly the
places where skim does manual byte/char-index bookkeeping on attacker- or
data-controlled strings, which is the most panic-prone code in the project.
## Targets
| Target | Exercises |
|-------------------|----------------------------------------------------------------------------|
| `ansi_strip` | `helper::item::strip_ansi` — ANSI escape stripping & byte/char index map |
| `field_extract` | `field::{FieldRange, get_string_by_field, parse_matching_fields, parse_transform_fields}``--nth`/`--with-nth` |
| `fuzzy_match` | `fuzzy_matcher::{skim, fzy, clangd}` — the fuzzy matching algorithms |
| `query_match` | `Matcher::create_engine_factory` + `DefaultSkimItem` — the full query → engine → match pipeline (exact/regex/AND-OR/fuzzy, with ANSI) |
| `keymap_parse` | `binds::KeyMap` — the `--bind` key-map parser |
Each target asserts more than "doesn't panic" where a cheap invariant is
available (e.g. reported match indices must be valid char indices into the
matched text, index mappings must stay monotonic and land on char
boundaries).
## Running
Install `cargo-fuzz` (requires a nightly toolchain):
```sh
cargo install cargo-fuzz
```
Run a target:
```sh
cargo +nightly fuzz run ansi_strip
```
Run for a bounded time (useful in CI or for a quick check):
```sh
cargo +nightly fuzz run query_match -- -max_total_time=60
```
## Reproducing a crash
`cargo fuzz run` writes failing inputs to `fuzz/artifacts/<target>/`. Replay one with:
```sh
cargo +nightly fuzz run <target> fuzz/artifacts/<target>/crash-<hash>
```
## Adding a target
Add a new `fuzz_targets/<name>.rs`, register it in `fuzz/Cargo.toml`'s
`[[bin]]` list, and prefer asserting a real invariant of the function under
test (bounds, monotonicity, round-tripping) rather than only catching panics.

View file

@ -0,0 +1,37 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
use skim::helper::item::strip_ansi;
// `strip_ansi` hand-parses ESC sequences while tracking a byte/char index
// mapping back to the original string. It is run on every line read from
// stdin when `--ansi` is set, so it must never panic on adversarial input
// and the mapping it returns must stay internally consistent.
fuzz_target!(|input: &str| {
let (stripped, mapping) = strip_ansi(input);
assert_eq!(
mapping.len(),
stripped.chars().count(),
"mapping length must match the number of chars in the stripped string"
);
let mut prev_byte_pos = None;
for &(byte_pos, char_idx) in &mapping {
assert!(
input.is_char_boundary(byte_pos),
"byte_pos {byte_pos} is not a char boundary in the original string"
);
// char_idx must be exactly the char position of byte_pos in the
// original string; this is stronger than (and implies) monotonicity.
let expected_char_idx = input[..byte_pos].chars().count();
assert_eq!(
char_idx, expected_char_idx,
"char_idx must equal the char position of byte_pos in the original string"
);
if let Some(prev) = prev_byte_pos {
assert!(prev < byte_pos, "byte positions in mapping must be strictly increasing");
}
prev_byte_pos = Some(byte_pos);
}
});

View file

@ -0,0 +1,47 @@
#![no_main]
use arbitrary::Arbitrary;
use libfuzzer_sys::fuzz_target;
use regex::Regex;
use skim::field::{FieldRange, get_string_by_field, parse_matching_fields, parse_transform_fields};
// Fuzzes the --nth/--with-nth field range parser and extractor, which slices
// arbitrary user-supplied text on an arbitrary user-supplied delimiter regex.
#[derive(Arbitrary, Debug)]
struct FieldFuzzInput<'a> {
delimiter_pattern: &'a str,
text: &'a str,
range_specs: Vec<&'a str>,
}
fuzz_target!(|input: FieldFuzzInput| {
// Bound the delimiter pattern length so we spend fuzzing time on the
// field logic rather than on the regex engine's own parser.
if input.delimiter_pattern.len() > 32 {
return;
}
let Ok(delimiter) = Regex::new(input.delimiter_pattern) else {
return;
};
let fields: Vec<FieldRange> = input
.range_specs
.iter()
.filter_map(|s| FieldRange::from_str(s))
.collect();
// `fields` can repeat/overlap ranges, so the transformed text is not
// bounded by the input length; just check it doesn't panic.
let _ = parse_transform_fields(&delimiter, input.text, &fields);
for (begin, end) in parse_matching_fields(&delimiter, input.text, &fields) {
assert!(begin <= end, "field range must not be inverted");
assert!(end <= input.text.len(), "field range must stay within the text");
// Slicing must not panic: begin/end must land on char boundaries.
let _ = &input.text[begin..end];
}
for field in &fields {
let _ = get_string_by_field(&delimiter, input.text, field);
}
});

View file

@ -0,0 +1,39 @@
#![no_main]
use arbitrary::Arbitrary;
use libfuzzer_sys::fuzz_target;
use skim::fuzzy_matcher::FuzzyMatcher;
use skim::fuzzy_matcher::clangd::ClangdMatcher;
use skim::fuzzy_matcher::fzy::FzyMatcher;
use skim::fuzzy_matcher::skim::SkimMatcherV2;
// Fuzzes the fuzzy matching algorithms directly on arbitrary unicode
// (choice, pattern) pairs. These run a lot of hand-written index/DP-matrix
// arithmetic over `char` boundaries, so they're prone to panics (overflow,
// out-of-bounds) on adversarial unicode input, and the returned match
// indices must always be valid character indices into `choice`.
#[derive(Arbitrary, Debug)]
struct MatchInput<'a> {
choice: &'a str,
pattern: &'a str,
}
fuzz_target!(|input: MatchInput| {
let skim_matcher = SkimMatcherV2::default();
let fzy_matcher = FzyMatcher::default();
let clangd_matcher = ClangdMatcher::default();
let matchers: [&dyn FuzzyMatcher; 3] = [&skim_matcher, &fzy_matcher, &clangd_matcher];
let num_chars = input.choice.chars().count();
for matcher in matchers {
if let Some((_score, indices)) = matcher.fuzzy_indices(input.choice, input.pattern) {
for &idx in &indices {
assert!(
idx < num_chars,
"match index {idx} out of bounds for choice with {num_chars} chars"
);
}
}
}
});

View file

@ -0,0 +1,10 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
use skim::binds::KeyMap;
// Fuzzes the `--bind` key-map parser, which splits an arbitrary user-supplied
// string on commas and colons to build key/action bindings.
fuzz_target!(|input: &str| {
let _ = KeyMap::from(input);
});

View file

@ -0,0 +1,58 @@
#![no_main]
use arbitrary::Arbitrary;
use libfuzzer_sys::fuzz_target;
use regex::Regex;
use skim::helper::item::DefaultSkimItem;
use skim::matcher::Matcher;
use skim::{SkimItem, SkimOptions};
// End-to-end fuzz of the query -> engine -> match pipeline: builds a real
// `DefaultSkimItem` (exercising ANSI stripping / field transforms) and
// matches it with an engine built the same way skim builds it from CLI
// options (exact/regex/andor/fuzzy-algorithm wrapping), using an arbitrary
// query string. Checks that matching never panics and that any reported
// match range stays within the bounds of the text that was actually matched.
#[derive(Arbitrary, Debug)]
struct QueryInput<'a> {
query: &'a str,
text: &'a str,
exact: bool,
regex: bool,
ansi: bool,
case: u8,
}
fuzz_target!(|input: QueryInput| {
// The regex engine path takes the query as a user-supplied pattern; keep
// it short so fuzzing time goes into skim's logic, not regex parsing.
if input.regex && input.query.len() > 32 {
return;
}
let mut options = SkimOptions::default();
options.exact = input.exact;
options.regex = input.regex;
options.case = match input.case % 3 {
0 => skim::CaseMatching::Respect,
1 => skim::CaseMatching::Ignore,
_ => skim::CaseMatching::Smart,
};
let factory = Matcher::create_engine_factory(&options);
let engine = factory.create_engine_with_case(input.query, options.case);
let delimiter = Regex::new(" ").unwrap();
let item = DefaultSkimItem::new(input.text, input.ansi, &[], &[], &delimiter);
if let Some(result) = engine.match_item(&item) {
let matched_text = item.text();
let num_chars = matched_text.chars().count();
for idx in result.range_char_indices(&matched_text) {
assert!(
idx <= num_chars,
"matched char index {idx} out of bounds ({num_chars} chars)"
);
}
}
});

68
install
View file

@ -1,68 +0,0 @@
#!/usr/bin/env bash
# This script trys to download the correct version of binary from github.
# You can download it manually and put it(i.e. `sk`) under `bin/`.
#
# If you know rust or have rust installed, you can build it with
# `cargo build --release`
set -u
version="0.9.4"
cd "$(dirname "${BASH_SOURCE[0]}")"
skim_base="$(pwd)"
check_binary() {
echo -n " - Checking skim executable ... "
local output
output=$("$skim_base"/bin/sk --version 2>&1)
if [ $? -ne 0 ]; then
echo "Error: $output"
elif [ "$version" != "$output" ]; then
echo "$output != $version"
else
echo "$output"
return 0
fi
rm -f "$skim_base"/bin/sk
return 1
}
# download version
download() {
echo "Downloading bin/sk ..."
mkdir -p "$skim_base"/bin && cd "$skim_base"/bin
if [ $? -ne 0 ]; then
binary_error="Failed to create bin directory"
return
fi
check_binary
local url=https://github.com/lotabout/skim/releases/download/v$version/${1}.tar.gz
echo "Downloading: $url"
if command -v curl > /dev/null; then
curl -fL $url | tar xz
elif command -v wget > /dev/null; then
wget -O - $url | tar xz
else
binary_error="curl or wget not found"
return
fi
if [ ! -f $1 ]; then
binary_error="Failed to download ${1}"
return
fi
}
archi=$(uname -sm)
case "$archi" in
Darwin\ x86_64) download skim-v$version-${binary_arch:-x86_64}-apple-darwin;;
Linux\ x86_64) download skim-v$version-${binary_arch:-x86_64}-unknown-linux-musl;;
*) ;;
esac
echo "Done :)"

99
justfile Normal file
View file

@ -0,0 +1,99 @@
alias pr := pr-review
bump-version version:
sed -i 's/^version = ".*"/version = "{{ version }}"/' ./Cargo.toml
generate-files:
SKIM_DEFAULT_OPTIONS= cargo run -- --man > ./man/man1/sk.1
SKIM_DEFAULT_OPTIONS= cargo run -- --shell bash > ./shell/completion.bash
SKIM_DEFAULT_OPTIONS= cargo run -- --shell zsh > ./shell/completion.zsh
SKIM_DEFAULT_OPTIONS= cargo run -- --shell fish > ./shell/completion.fish
SKIM_DEFAULT_OPTIONS= cargo run -- --shell nushell > ./shell/completion.nu
changelog version:
git cliff -p CHANGELOG.md -t 'v{{ version }}' -u
release version: (bump-version version) generate-files (changelog version) test
cargo generate-lockfile
echo '{{ version }}' > shell/version.txt
git add CHANGELOG.md Cargo.lock Cargo.toml man/ shell/
git commit -m 'release: v{{ version }}'
git tag 'v{{ version }}'
read -p "Press any key to confirm pushing tag v{{ version }}"
git push
git push --tags
auto-release:
just release $(git cliff --bumped-version | sed 's/v\(.*\)/\1/')
test target="":
cargo test --doc
cargo nextest run {{ target }}
# Each e2e test creates and tears down its own Zellij session, so there is
# no shared multiplexer session to clean up here.
bench-plot bins="./target/release/sk sk fzf":
#!/usr/bin/env bash
set -euo pipefail
declare -A inputs=(
["1"]=1
["10"]=10
["100"]=100
["1K"]=1000
["10K"]=10000
["100K"]=100000
["1M"]=1000000
["10M"]=10000000
["100M"]=100000000
)
echo "" > /tmp/bench.json
for f in "${!inputs[@]}"; do
p="benches/fixtures/$f.txt"
n="${inputs[$f]}"
if [ ! -f "$p" ]; then
cargo bench --bench cli -- generate -n "$n" -f "$p"
fi
# The formula for `--stable-secs` might need adjusting if the number of results varies between runs with the same input
cargo bench --bench cli -- run {{ bins }} -f "$p" --runs 10 --stable-secs "$(( 3 * $n / 10000000 )).5" --json >> /tmp/bench.json
done
cargo bench --bench cli -- plot -i /tmp/bench.json
pr-review id="":
#!/usr/bin/env bash
set -euo pipefail
PR_ID="{{ id }}"
if [[ -z "$PR_ID" ]]; then
PR_ID="$(gh pr list | sk | cut -d' ' -f1)"
fi
echo "Checking out PR $PR_ID"
gh pr checkout "$PR_ID"
# Check the PR title
PR_TITLE="$(gh pr view | head -n1 | sed 's@title:\s\+@@;s@skim-rs/skim#.*@@')"
echo "Checking $PR_TITLE by simulating a git cliff generation"
git cliff --from-context <(echo '[{"commits":[{"id": "foo", "message": "'$PR_TITLE'", "links": [], "author": {"name": "","timestamp":1}, "committer": {"name": "", "timestamp":0}, "merge_commit": false,"github":{"pr_labels":[], "is_first_time": false},"gitlab":{"pr_labels":[],"is_first_time":false},"gitea":{"pr_labels":[],"is_first_time":false},"bitbucket":{"pr_labels":[],"is_first_time":false},"azure_devops":{"pr_labels":[],"is_first_time":false}}],"submodule_commits":{},"github":{"contributors":[]},"gitlab":{"contributors":[]},"gitea":{"contributors":[]},"bitbucket":{"contributors":[]},"azure_devops":{"contributors":[]}}]') -s all | grep -Ev '^(## \[unreleased\]|)$' | grep -q '^.\+$'
echo "Check done, PR title is valid"
git diff master
tty -s && read -p "Confirm code review ?"
just generate-files
(git add man/ shell/ && git commit -m 'chore: generate-files' && git push) || echo "Nothing to do"
coverage *args="":
cargo +nightly llvm-cov nextest --ignore-run-fail --branch --lib --bins --examples --tests {{ args }}
cargo +nightly llvm-cov report --html
clippy *args="":
cargo clippy --all-targets {{ args }}
fmt *args="":
cargo +nightly fmt {{ args }}

View file

@ -22,7 +22,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
..
.TH sk-tmux 1 "Oct 2018" "sk 0.17.5" "sk-tmux - open sk in tmux split pane"
.TH sk-tmux 1 "Oct 2018" "sk 0.10.4" "sk-tmux - open sk in tmux split pane"
.SH NAME
sk-tmux - open sk in tmux split pane

File diff suppressed because it is too large Load diff

View file

@ -136,38 +136,12 @@ function! s:default_layout()
\ : { 'down': '~40%' }
endfunction
function! skim#install()
if s:is_win && !has('win32unix')
let script = s:base_dir.'/install.ps1'
if !filereadable(script)
throw script.' not found'
endif
let script = 'powershell -ExecutionPolicy Bypass -file ' . script
else
let script = s:base_dir.'/install'
if !executable(script)
throw script.' not found'
endif
let script .= ' --bin'
endif
call s:warn('Running skim installer ...')
call system(script)
if v:shell_error
throw 'Failed to download skim: '.script
endif
endfunction
function! skim#exec()
if !exists('s:exec')
if executable(s:skim_rs)
let s:exec = s:skim_rs
elseif executable('sk')
let s:exec = 'sk'
elseif input('skim executable not found. Download binary? (y/n) ') =~? '^y'
redraw
call skim#install()
return skim#exec()
else
redraw
throw 'skim executable not found'
@ -342,6 +316,12 @@ function! skim#wrap(...)
endif
endif
" Interactive commands should use --cmd-history for query history
let history_option = '--history'
if index(opts['options'], '-i') != -1
let history_option = '--cmd-history'
endif
" Colors: g:skim_colors
let opts.options = s:defaults() .' '. s:evaluate_opts(get(opts, 'options', ''))
@ -352,13 +332,13 @@ function! skim#wrap(...)
call mkdir(dir, 'p')
endif
let history = skim#shellescape(dir.'/'.name)
let opts.options = join(['--history', history, opts.options])
let opts.options = join([history_option, history, opts.options])
endif
" Action: g:skim_action
if !s:has_any(opts, ['sink', 'sink*'])
let opts._action = get(g:, 'skim_action', s:default_action)
let opts.options .= ' --expect='.join(keys(opts._action), ',')
let opts.options .= ' '.skim#shellescape('--bind='.join(map(keys(opts._action), 'v:val.":accept(".v:val.")"'), ','))
function! opts.sink(lines) abort
return s:common_sink(self._action, a:lines)
endfunction
@ -512,21 +492,17 @@ function! s:dopopd()
return
endif
" FIXME: We temporarily change the working directory to 'dir' entry
" Note: We temporarily change the working directory to 'dir' entry
" of options dictionary (set to the current working directory if not given)
" before running skim.
"
" e.g. call skim#run({'dir': '/tmp', 'source': 'ls', 'sink': 'e'})
"
" After processing the sink function, we have to restore the current working
" directory. But doing so may not be desirable if the function changed the
" working directory on purpose.
"
" So how can we tell if we should do it or not? A simple heuristic we use
" here is that we change directory only if the current working directory
" matches 'dir' entry. However, it is possible that the sink function did
" change the directory to 'dir'. In that case, the user will have an
" unexpected result.
" After processing the sink function, we restore the current working
" directory using a heuristic: we only change directory if the current
" working directory matches 'dir' entry. This handles most cases correctly,
" though it may not restore the directory if the sink function explicitly
" changed to the 'dir' path.
if s:skim_getcwd() ==# w:skim_pushd.dir && (!&autochdir || w:skim_pushd.bufname ==# bufname(''))
execute w:skim_pushd.command s:escape(w:skim_pushd.origin)
endif
@ -931,6 +907,7 @@ function! s:popup(opts) abort
endfunction
let s:default_action = {
\ 'enter': 'edit',
\ 'ctrl-t': 'tab split',
\ 'ctrl-x': 'split',
\ 'ctrl-v': 'vsplit' }

4
rust-toolchain.toml Normal file
View file

@ -0,0 +1,4 @@
[toolchain]
channel = "stable"
profile = "default"
components = ["rust-analyzer"]

View file

@ -1,373 +1,438 @@
# ____ ____
# / __/___ / __/
# / /_/_ / / /_
# / __/ / /_/ __/
# /_/ /___/_/ completion.bash
#
# - $SKIM_TMUX (default: 0)
# - $SKIM_TMUX_OPTS (default: empty)
# - $SKIM_COMPLETION_TRIGGER (default: '**')
# - $SKIM_COMPLETION_OPTS (default: empty)
# copied and modified from https://github.com/junegunn/fzf/blob/master/shell/completion.bash
if [[ $- =~ i ]]; then
# To use custom commands instead of find, override _skim_compgen_{path,dir}
if ! declare -f _skim_compgen_path > /dev/null; then
_skim_compgen_path() {
echo "$1"
command find -L "$1" \
-name .git -prune -o -name .hg -prune -o -name .svn -prune -o \( -type d -o -type f -o -type l \) \
-a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@'
}
fi
if ! declare -f _skim_compgen_dir > /dev/null; then
_skim_compgen_dir() {
command find -L "$1" \
-name .git -prune -o -name .hg -prune -o -name .svn -prune -o -type d \
-a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@'
}
fi
###########################################################
# To redraw line after skim closes (printf '\e[5n')
bind '"\e[0n": redraw-current-line'
__skim_comprun() {
if [ "$(type -t _skim_comprun 2>&1)" = function ]; then
_skim_comprun "$@"
elif [ -n "$TMUX_PANE" ] && { [ "${SKIM_TMUX:-0}" != 0 ] || [ -n "$SKIM_TMUX_OPTS" ]; }; then
shift
sk-tmux ${SKIM_TMUX_OPTS:--d${SKIM_TMUX_HEIGHT:-40%}} -- "$@"
else
shift
sk "$@"
fi
}
__skim_orig_completion_filter() {
sed 's/^\(.*-F\) *\([^ ]*\).* \([^ ]*\)$/export _skim_orig_completion_\3="\1 %s \3 #\2"; [[ "\1" = *" -o nospace "* ]] \&\& [[ ! "$__skim_nospace_commands" = *" \3 "* ]] \&\& __skim_nospace_commands="$__skim_nospace_commands \3 ";/' |
awk -F= '{OFS = FS} {gsub(/[^A-Za-z0-9_= ;]/, "_", $1);}1'
}
_skim_opts_completion() {
local cur prev opts
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
opts="
-x --extended
-e --exact
--algo
-i +i
-n --nth
--with-nth
-d --delimiter
+s --no-sort
--tac
--tiebreak
-m --multi
--no-mouse
--bind
--cycle
--no-hscroll
--jump-labels
--height
--literal
--reverse
--margin
--inline-info
--prompt
--pointer
--marker
--header
--header-lines
--ansi
--tabstop
--color
--no-bold
--history
--history-size
--preview
--preview-window
-q --query
-1 --select-1
-0 --exit-0
-f --filter
--print-query
--expect
--sync"
case "${prev}" in
--tiebreak)
COMPREPLY=( $(compgen -W "length begin end index" -- "$cur") )
return 0
;;
--color)
COMPREPLY=( $(compgen -W "dark light 16 bw" -- "$cur") )
return 0
;;
--history)
_sk() {
local i cur prev opts cmd
COMPREPLY=()
return 0
;;
esac
if [[ "$cur" =~ ^-|\+ ]]; then
COMPREPLY=( $(compgen -W "${opts}" -- "$cur") )
return 0
fi
return 0
}
_skim_handle_dynamic_completion() {
local cmd orig_var orig ret orig_cmd orig_complete
cmd="$1"
shift
orig_cmd="$1"
orig_var="_skim_orig_completion_$cmd"
orig="${!orig_var##*#}"
if [ -n "$orig" ] && type "$orig" > /dev/null 2>&1; then
$orig "$@"
elif [ -n "$_skim_completion_loader" ]; then
orig_complete=$(complete -p "$orig_cmd" 2> /dev/null)
_completion_loader "$@"
ret=$?
# _completion_loader may not have updated completion for the command
if [ "$(complete -p "$orig_cmd" 2> /dev/null)" != "$orig_complete" ]; then
eval "$(complete | command grep " -F.* $orig_cmd$" | __skim_orig_completion_filter)"
if [[ "$__skim_nospace_commands" = *" $orig_cmd "* ]]; then
eval "${orig_complete/ -F / -o nospace -F }"
else
eval "$orig_complete"
fi
fi
return $ret
fi
}
__skim_generic_path_completion() {
local cur base dir leftover matches trigger cmd
cmd="${COMP_WORDS[0]//[^A-Za-z0-9_=]/_}"
COMPREPLY=()
trigger=${SKIM_COMPLETION_TRIGGER-'**'}
cur="${COMP_WORDS[COMP_CWORD]}"
if [[ "$cur" == *"$trigger" ]]; then
base=${cur:0:${#cur}-${#trigger}}
eval "base=$base"
[[ $base = *"/"* ]] && dir="$base"
while true; do
if [ -z "$dir" ] || [ -d "$dir" ]; then
leftover=${base/#"$dir"}
leftover=${leftover/#\/}
[ -z "$dir" ] && dir='.'
[ "$dir" != "/" ] && dir="${dir/%\//}"
matches=$(eval "$1 $(printf %q "$dir")" | SKIM_DEFAULT_OPTIONS="--height ${SKIM_TMUX_HEIGHT:-40%} --reverse $SKIM_DEFAULT_OPTIONS $SKIM_COMPLETION_OPTS $2" __skim_comprun "$4" -q "$leftover" | while read -r item; do
printf "%q$3 " "$item"
done)
matches=${matches% }
[[ -z "$3" ]] && [[ "$__skim_nospace_commands" = *" ${COMP_WORDS[0]} "* ]] && matches="$matches "
if [ -n "$matches" ]; then
COMPREPLY=( "$matches" )
else
COMPREPLY=( "$cur" )
fi
printf '\e[5n'
return 0
fi
dir=$(dirname "$dir")
[[ "$dir" =~ /$ ]] || dir="$dir"/
done
else
shift
shift
shift
_skim_handle_dynamic_completion "$cmd" "$@"
fi
}
_skim_complete() {
# Split arguments around --
local args rest str_arg i sep
args=("$@")
sep=
for i in "${!args[@]}"; do
if [[ "${args[$i]}" = -- ]]; then
sep=$i
break
fi
done
if [[ -n "$sep" ]]; then
str_arg=
rest=("${args[@]:$((sep + 1)):${#args[@]}}")
args=("${args[@]:0:$sep}")
else
str_arg=$1
args=()
shift
rest=("$@")
fi
local cur selected trigger cmd post
post="$(caller 0 | awk '{print $2}')_post"
type -t "$post" > /dev/null 2>&1 || post=cat
cmd="${COMP_WORDS[0]//[^A-Za-z0-9_=]/_}"
trigger=${SKIM_COMPLETION_TRIGGER-'**'}
cur="${COMP_WORDS[COMP_CWORD]}"
if [[ "$cur" == *"$trigger" ]]; then
cur=${cur:0:${#cur}-${#trigger}}
selected=$(SKIM_DEFAULT_OPTIONS="--height ${SKIM_TMUX_HEIGHT:-40%} --reverse $SKIM_DEFAULT_OPTIONS $SKIM_COMPLETION_OPTS $str_arg" __skim_comprun "${rest[0]}" "${args[@]}" -q "$cur" | $post | tr '\n' ' ')
selected=${selected% } # Strip trailing space not to repeat "-o nospace"
if [ -n "$selected" ]; then
COMPREPLY=("$selected")
if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
cur="$2"
else
COMPREPLY=("$cur")
cur="${COMP_WORDS[COMP_CWORD]}"
fi
printf '\e[5n'
return 0
else
_skim_handle_dynamic_completion "$cmd" "${rest[@]}"
fi
}
prev="$3"
cmd=""
opts=""
_skim_path_completion() {
__skim_generic_path_completion _skim_compgen_path "-m" "" "$@"
}
for i in "${COMP_WORDS[@]:0:COMP_CWORD}"
do
case "${cmd},${i}" in
",$1")
cmd="sk"
;;
*)
;;
esac
done
# Deprecated. No file only completion.
_skim_file_completion() {
_skim_path_completion "$@"
}
_skim_dir_completion() {
__skim_generic_path_completion _skim_compgen_dir "" "/" "$@"
}
_skim_complete_kill() {
local trigger=${SKIM_COMPLETION_TRIGGER-'**'}
local cur="${COMP_WORDS[COMP_CWORD]}"
if [[ -z "$cur" ]]; then
COMP_WORDS[$COMP_CWORD]=$trigger
elif [[ "$cur" != *"$trigger" ]]; then
return 1
fi
_skim_proc_completion "$@"
}
_skim_proc_completion() {
_skim_complete -m --preview 'echo {}' --preview-window down:3:wrap --min-height 15 -- "$@" < <(
command ps -ef | sed 1d
)
}
_skim_proc_completion_post() {
awk '{print $2}'
}
_skim_host_completion() {
_skim_complete --no-multi -- "$@" < <(
command cat <(command tail -n +1 ~/.ssh/config ~/.ssh/config.d/* /etc/ssh/ssh_config 2> /dev/null | command grep -i '^\s*host\(name\)\? ' | awk '{for (i = 2; i <= NF; i++) print $1 " " $i}' | command grep -v '[*?]') \
<(command grep -oE '^[[a-z0-9.,:-]+' ~/.ssh/known_hosts | tr ',' '\n' | tr -d '[' | awk '{ print $1 " " $1 }') \
<(command grep -v '^\s*\(#\|$\)' /etc/hosts | command grep -Fv '0.0.0.0') |
awk '{if (length($2) > 0) {print $2}}' | sort -u
)
}
_skim_var_completion() {
_skim_complete -m -- "$@" < <(
declare -xp | sed 's/=.*//' | sed 's/.* //'
)
}
_skim_alias_completion() {
_skim_complete -m -- "$@" < <(
alias | sed 's/=.*//' | sed 's/.* //'
)
}
# skim options
complete -o default -F _skim_opts_completion sk
d_cmds="${SKIM_COMPLETION_DIR_COMMANDS:-cd pushd rmdir}"
a_cmds="
awk cat diff diff3
emacs emacsclient ex file ftp g++ gcc gvim head hg java
javac ld less more mvim nvim patch perl python ruby
sed sftp sort source tail tee uniq vi view vim wc xdg-open
basename bunzip2 bzip2 chmod chown curl cp dirname du
find git grep gunzip gzip hg jar
ln ls mv open rm rsync scp
svn tar unzip zip"
# Preserve existing completion
eval "$(complete |
sed -E '/-F/!d; / _skim/d; '"/ ($(echo $d_cmds $a_cmds | sed 's/ /|/g; s/+/\\+/g'))$/"'!d' |
__skim_orig_completion_filter)"
if type _completion_loader > /dev/null 2>&1; then
_skim_completion_loader=1
fi
__skim_defc() {
local cmd func opts orig_var orig def
cmd="$1"
func="$2"
opts="$3"
orig_var="_skim_orig_completion_${cmd//[^A-Za-z0-9_]/_}"
orig="${!orig_var}"
if [ -n "$orig" ]; then
printf -v def "$orig" "$func"
eval "$def"
else
complete -F "$func" $opts "$cmd"
fi
}
# Anything
for cmd in $a_cmds; do
__skim_defc "$cmd" _skim_path_completion "-o default -o bashdefault"
done
# Directory
for cmd in $d_cmds; do
__skim_defc "$cmd" _skim_dir_completion "-o nospace -o dirnames"
done
# Kill completion (supports empty completion trigger)
complete -F _skim_complete_kill -o default -o bashdefault kill
unset cmd d_cmds a_cmds
_skim_setup_completion() {
local kind fn cmd
kind=$1
fn=_skim_${1}_completion
if [[ $# -lt 2 ]] || ! type -t "$fn" > /dev/null; then
echo "usage: ${FUNCNAME[0]} path|dir|var|alias|host|proc COMMANDS..."
return 1
fi
shift
eval "$(complete -p "$@" 2> /dev/null | grep -v "$fn" | __skim_orig_completion_filter)"
for cmd in "$@"; do
case "$kind" in
dir) __skim_defc "$cmd" "$fn" "-o nospace -o dirnames" ;;
var) __skim_defc "$cmd" "$fn" "-o default -o nospace -v" ;;
alias) __skim_defc "$cmd" "$fn" "-a" ;;
*) __skim_defc "$cmd" "$fn" "-o default -o bashdefault" ;;
case "${cmd}" in
sk)
opts="-t -n -d -e -b -m -c -i -I -p -q -1 -0 -f -x -h -V --tac --min-query-length --no-sort --tiebreak --nth --with-nth --hide-nth --delimiter --exact --regex --algo --case --typos --no-typos --normalize --split-match --last-match --scheme --bind --multi --no-multi --no-mouse --cmd --interactive --color --highlight-line --no-hscroll --keep-right --skip-to-pattern --no-clear-if-empty --no-clear-start --no-clear --show-cmd-error --cycle --disabled --disable-pattern --layout --reverse --height --no-height --min-height --margin --prompt --cmd-prompt --selector --multi-selector --ansi --tabstop --ellipsis --info --no-info --inline-info --header --header-lines --border --border-no-collapse --no-border --wrap --multiline --scrollbar --no-scrollbar --history --history-size --cmd-history --cmd-history-size --preview --preview-window --image --query --cmd-query --read0 --print0 --print-query --print-cmd --print-score --print-header --print-current --output-format --no-strip-ansi --select-1 --exit-0 --sync --pre-select-n --pre-select-pat --pre-select-items --pre-select-file --filter --shell --shell-bindings --man --listen --remote --popup --log-level --log-file --flags --extended --literal --hscroll-off --filepath-word --jump-labels --no-bold --phony --tail --style --no-color --padding --border-label --border-label-pos --wrap-sign --no-multi-line --raw --track --gap --gap-line --freeze-left --freeze-right --scroll-off --gutter --gutter-raw --marker-multi-line --list-border --list-label --list-label-pos --no-input --info-command --separator --no-separator --ghost --input-border --input-label --input-label-pos --preview-label --preview-label-pos --header-first --header-border --header-lines-border --footer --footer-border --footer-label --footer-label-pos --with-shell --expect --help --version"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
fi
case "${prev}" in
--min-query-length)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--tiebreak)
COMPREPLY=($(compgen -W "score -score begin -begin end -end length -length index -index pathname -pathname" -- "${cur}"))
return 0
;;
-t)
COMPREPLY=($(compgen -W "score -score begin -begin end -end length -length index -index pathname -pathname" -- "${cur}"))
return 0
;;
--nth)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-n)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--with-nth)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--hide-nth)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--delimiter)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-d)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--algo)
COMPREPLY=($(compgen -W "arinae clangd fzy frizbee skim_v2" -- "${cur}"))
return 0
;;
--case)
COMPREPLY=($(compgen -W "respect ignore smart" -- "${cur}"))
return 0
;;
--typos)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--split-match)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--scheme)
COMPREPLY=($(compgen -W "default path history" -- "${cur}"))
return 0
;;
--bind)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-b)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--cmd)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-c)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-I)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--color)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--skip-to-pattern)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--disable-pattern)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--layout)
COMPREPLY=($(compgen -W "default reverse reverse-list" -- "${cur}"))
return 0
;;
--height)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--min-height)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--margin)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--prompt)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-p)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--cmd-prompt)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--selector)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--multi-selector)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--tabstop)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--ellipsis)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--info)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--header)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--header-lines)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--border)
COMPREPLY=($(compgen -W "force-off none plain rounded double thick light-double-dashed heavy-double-dashed light-triple-dashed heavy-triple-dashed light-quadruple-dashed heavy-quadruple-dashed quadrant-inside quadrant-outside" -- "${cur}"))
return 0
;;
--multiline)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--scrollbar)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--history)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--history-size)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--cmd-history)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--cmd-history-size)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--preview)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--preview-window)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--image)
COMPREPLY=($(compgen -W "detect halfblocks" -- "${cur}"))
return 0
;;
--query)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-q)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--cmd-query)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--output-format)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--pre-select-n)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--pre-select-pat)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--pre-select-items)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--pre-select-file)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--filter)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-f)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--shell)
COMPREPLY=($(compgen -W "bash elvish fish nushell power-shell zsh" -- "${cur}"))
return 0
;;
--listen)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--remote)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--popup)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--log-level)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--log-file)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--flags)
COMPREPLY=($(compgen -W "no-preview-pty show-score show-index single-reader single-matcher" -- "${cur}"))
return 0
;;
--hscroll-off)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--jump-labels)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--tail)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--style)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--padding)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--border-label)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--border-label-pos)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--wrap-sign)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--gap)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--gap-line)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--freeze-left)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--freeze-right)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--scroll-off)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--gutter)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--gutter-raw)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--marker-multi-line)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--list-border)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--list-label)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--list-label-pos)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--info-command)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--separator)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--ghost)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--input-border)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--input-label)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--input-label-pos)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--preview-label)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--preview-label-pos)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--header-border)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--header-lines-border)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--footer)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--footer-border)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--footer-label)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--footer-label-pos)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--with-shell)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--expect)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;
esac
done
}
# Environment variables / Aliases / Hosts
_skim_setup_completion 'var' export unset
_skim_setup_completion 'alias' unalias
_skim_setup_completion 'host' ssh telnet
if [[ "${BASH_VERSINFO[0]}" -eq 4 && "${BASH_VERSINFO[1]}" -ge 4 || "${BASH_VERSINFO[0]}" -gt 4 ]]; then
complete -F _sk -o nosort -o bashdefault -o default sk
else
complete -F _sk -o bashdefault -o default sk
fi

190
shell/completion.fish Normal file
View file

@ -0,0 +1,190 @@
complete -c sk -l min-query-length -d 'Minimum query length to start showing results' -r
complete -c sk -s t -l tiebreak -d 'Comma-separated list of sort criteria to apply when the scores are tied.' -r -f -a "score\t''
-score\t''
begin\t''
-begin\t''
end\t''
-end\t''
length\t''
-length\t''
index\t''
-index\t''
pathname\t''
-pathname\t''"
complete -c sk -s n -l nth -d 'Fields to be matched' -r
complete -c sk -l with-nth -d 'Fields to be transformed' -r
complete -c sk -l hide-nth -d 'Fields to hide from display while keeping them searchable' -r
complete -c sk -s d -l delimiter -d 'Delimiter between fields' -r
complete -c sk -l algo -d 'Fuzzy matching algorithm' -r -f -a "arinae\t'Arinae: typo-resistant & natural algorithm, default'
clangd\t'Clangd fuzzy matching algorithm'
fzy\t'Fzy matching algorithm (https://github.com/jhawthorn/fzy)'
frizbee\t'Frizbee matching algorithm, typo resistant'
skim_v2\t'Previous skim fuzzy matching algorithm (v2)'"
complete -c sk -l case -d 'Case sensitivity' -r -f -a "respect\t'Case-sensitive matching'
ignore\t'Case-insensitive matching'
smart\t'Smart case: case-insensitive unless query contains uppercase'"
complete -c sk -l typos -d 'Enable typo-tolerant matching' -r
complete -c sk -l split-match -d 'Enable split matching and set delimiter' -r
complete -c sk -l scheme -r -f -a "default\t'Default scheme, no modifications to the options'
path\t'Path scheme: will find the furthest match in the item and set pathname as the main tiebreak'
history\t'History scheme: will force index as the first tiebreak'"
complete -c sk -s b -l bind -d 'Comma-separated key, event, and action bindings' -r
complete -c sk -s c -l cmd -d 'Command to invoke dynamically in interactive mode' -r
complete -c sk -s I -d 'Replace replstr with the selected item in commands' -r
complete -c sk -l color -d 'Set color theme' -r
complete -c sk -l skip-to-pattern -d 'Show the matched pattern at the line start' -r
complete -c sk -l disable-pattern -d 'Disable items based on this regex pattern' -r
complete -c sk -l layout -d 'Set layout' -r -f -a "default\t'Display from the bottom of the screen'
reverse\t'Display from the top of the screen'
reverse-list\t'Display from the top of the screen, prompt at the bottom'"
complete -c sk -l height -d 'Height of skim\'s window' -r
complete -c sk -l min-height -d 'Minimum height of skim\'s window as a non-negative row count' -r
complete -c sk -l margin -d 'Screen margin' -r
complete -c sk -s p -l prompt -d 'Set prompt' -r
complete -c sk -l cmd-prompt -d 'Set prompt in command mode' -r
complete -c sk -l selector -d 'Set selected item icon' -r
complete -c sk -l multi-selector -d 'Set multi-selected item icon' -r
complete -c sk -l tabstop -d 'Number of spaces that make up a tab' -r
complete -c sk -l ellipsis -d 'The characters used to display truncated lines' -r
complete -c sk -l info -d 'Set matching result count display position' -r
complete -c sk -l header -d 'Set header, displayed next to the info' -r
complete -c sk -l header-lines -d 'Number of lines of the input treated as header' -r
complete -c sk -l border -d 'Draw borders around the UI components' -r -f -a "force-off\t'ForceOff disables borders around popups too set with no_border'
none\t''
plain\t''
rounded\t''
double\t''
thick\t''
light-double-dashed\t''
heavy-double-dashed\t''
light-triple-dashed\t''
heavy-triple-dashed\t''
light-quadruple-dashed\t''
heavy-quadruple-dashed\t''
quadrant-inside\t''
quadrant-outside\t''"
complete -c sk -l multiline -d 'Split item text into multiple display lines at the given separator character defaults to \\n if read0 is set, and \\\\n if not (matching literal \\n in text)' -r
complete -c sk -l scrollbar -d 'Set scrollbar style for the item list' -r
complete -c sk -l history -d 'History file' -r
complete -c sk -l history-size -d 'Maximum number of query history entries to keep' -r
complete -c sk -l cmd-history -d 'Command history file' -r
complete -c sk -l cmd-history-size -d 'Maximum number of query history entries to keep' -r
complete -c sk -l preview -d 'Preview command' -r
complete -c sk -l preview-window -d 'Preview window layout' -r
complete -c sk -l image -d 'Enable image preview' -r -f -a "detect\t'Default: automatically detect the available backend at startup'
halfblocks\t'Force halfblocks if you want blurry previews but a faster startup or if the detection fails'"
complete -c sk -s q -l query -d 'Initial query' -r
complete -c sk -l cmd-query -d 'Initial query in interactive mode' -r
complete -c sk -l output-format -d 'Set the output format If set, overrides all print_ options Will be expanded the same way as preview or commands' -r
complete -c sk -l pre-select-n -d 'Pre-select the first n items in multi-selection mode' -r
complete -c sk -l pre-select-pat -d 'Pre-select the matched items in multi-selection mode' -r
complete -c sk -l pre-select-items -d 'Pre-select the items separated by newline character' -r
complete -c sk -l pre-select-file -d 'Pre-select the items read from this file' -r
complete -c sk -s f -l filter -d 'Query for filter mode' -r
complete -c sk -l shell -d 'Generate shell completion script' -r -f -a "bash\t'Bourne Again SHell'
elvish\t'Elvish shell'
fish\t'Friendly Interactive SHell'
nushell\t'Nushell (nu)'
power-shell\t'PowerShell'
zsh\t'Zsh'"
complete -c sk -l listen -d 'Run an IPC socket with optional name (defaults to sk)' -r
complete -c sk -l remote -d 'Send commands to an IPC socket with optional name (defaults to sk)' -r
complete -c sk -l popup -d 'Run in a tmux or zellij popup' -r
complete -c sk -l log-level -d 'Set the log level' -r
complete -c sk -l log-file -d 'Pipe log output to a file' -r
complete -c sk -l flags -d 'Feature flags' -r -f -a "no-preview-pty\t'Disable preview PTY on Linux'
show-score\t'Display the item\'s match score before its value in the item list (for matcher debugging)'
show-index\t'Display the item\'s index before its value in the item list'
single-reader\t'Limit the reader thread pool to a single thread'
single-matcher\t'Limit the matcher thread pool to a single thread'"
complete -c sk -l hscroll-off -r
complete -c sk -l jump-labels -r
complete -c sk -l tail -r
complete -c sk -l style -r
complete -c sk -l padding -r
complete -c sk -l border-label -r
complete -c sk -l border-label-pos -r
complete -c sk -l wrap-sign -r
complete -c sk -l gap -r
complete -c sk -l gap-line -r
complete -c sk -l freeze-left -r
complete -c sk -l freeze-right -r
complete -c sk -l scroll-off -r
complete -c sk -l gutter -r
complete -c sk -l gutter-raw -r
complete -c sk -l marker-multi-line -r
complete -c sk -l list-border -r
complete -c sk -l list-label -r
complete -c sk -l list-label-pos -r
complete -c sk -l info-command -r
complete -c sk -l separator -r
complete -c sk -l ghost -r
complete -c sk -l input-border -r
complete -c sk -l input-label -r
complete -c sk -l input-label-pos -r
complete -c sk -l preview-label -r
complete -c sk -l preview-label-pos -r
complete -c sk -l header-border -r
complete -c sk -l header-lines-border -r
complete -c sk -l footer -r
complete -c sk -l footer-border -r
complete -c sk -l footer-label -r
complete -c sk -l footer-label-pos -r
complete -c sk -l with-shell -r
complete -c sk -l expect -d 'Deprecated, kept for compatibility purposes. See accept() bind instead' -r
complete -c sk -l tac -d 'Show results in reverse order'
complete -c sk -l no-sort -d 'Do not sort the results'
complete -c sk -s e -l exact -d 'Run in exact mode'
complete -c sk -l regex -d 'Start in regex mode instead of fuzzy-match'
complete -c sk -l no-typos -d 'Disable typo-tolerant matching'
complete -c sk -l normalize -d 'Normalize unicode characters'
complete -c sk -l last-match -d 'Highlight the last match found, not the first one This makes tiebreak more pertinent on path items where we want to prioritize a match on the last parts'
complete -c sk -s m -l multi -d 'Enable multiple selection'
complete -c sk -l no-multi -d 'Disable multiple selection'
complete -c sk -l no-mouse -d 'Disable mouse'
complete -c sk -s i -l interactive -d 'Start skim in interactive mode'
complete -c sk -l highlight-line -d 'Highlight the entire current line, not just the text'
complete -c sk -l no-hscroll -d 'Disable horizontal scroll'
complete -c sk -l keep-right -d 'Keep the right end of the line visible on overflow'
complete -c sk -l no-clear-if-empty -d 'Do not clear previous line if the command returns an empty result'
complete -c sk -l no-clear-start -d 'Do not clear items on start'
complete -c sk -l no-clear -d 'Do not clear screen on exit'
complete -c sk -l show-cmd-error -d 'Show error message if command fails'
complete -c sk -l cycle -d 'Cycle the results by wrapping around when scrolling'
complete -c sk -l disabled -d 'Disable matching entirely'
complete -c sk -l reverse -d 'Shorthand for reverse layout'
complete -c sk -l no-height -d 'Disable height (force full screen)'
complete -c sk -l ansi -d 'Parse ANSI color codes in input strings'
complete -c sk -l no-info -d 'Alias for --info=hidden'
complete -c sk -l inline-info -d 'Alias for --info=inline'
complete -c sk -l border-no-collapse -d 'Do not collapse adjacent borders into a shared row or column'
complete -c sk -l no-border -d 'Disables all borders, including in tmux/zellij popups'
complete -c sk -l wrap -d 'Wrap items in the item list'
complete -c sk -l no-scrollbar -d 'Disable the scrollbar in the item list'
complete -c sk -l read0 -d 'Read input delimited by ASCII NUL(\\0) characters'
complete -c sk -l print0 -d 'Print output delimited by ASCII NUL(\\0) characters'
complete -c sk -l print-query -d 'Print the query as the first line'
complete -c sk -l print-cmd -d 'Print the command as the first line (after print-query)'
complete -c sk -l print-score -d 'Print the score after each item'
complete -c sk -l print-header -d 'Print the header as the first line (after print-score)'
complete -c sk -l print-current -d 'Print the current (highlighted) item as the first line (after print-header)'
complete -c sk -l no-strip-ansi -d 'Print the ANSI codes, making the output exactly match the input even when --ansi is on'
complete -c sk -s 1 -l select-1 -d 'Do not enter the TUI if the query passed in -q matches only one item and return it'
complete -c sk -s 0 -l exit-0 -d 'Do not enter the TUI if the query passed in -q does not match any item'
complete -c sk -l sync -d 'Synchronous search for multi-staged filtering'
complete -c sk -l shell-bindings -d 'Generate shell key bindings - only for bash, zsh and fish'
complete -c sk -l man -d 'Generate man page and output it to stdout'
complete -c sk -s x -l extended
complete -c sk -l literal
complete -c sk -l filepath-word
complete -c sk -l no-bold
complete -c sk -l phony
complete -c sk -l no-color
complete -c sk -l no-multi-line
complete -c sk -l raw
complete -c sk -l track
complete -c sk -l no-input
complete -c sk -l no-separator
complete -c sk -l header-first
complete -c sk -s h -l help -d 'Print help (see more with \'--help\')'
complete -c sk -s V -l version -d 'Print version'

191
shell/completion.nu Normal file
View file

@ -0,0 +1,191 @@
module completions {
def "nu-complete sk tiebreak" [] {
[ "score" "-score" "begin" "-begin" "end" "-end" "length" "-length" "index" "-index" "pathname" "-pathname" ]
}
def "nu-complete sk algorithm" [] {
[ "arinae" "clangd" "fzy" "frizbee" "skim_v2" ]
}
def "nu-complete sk case" [] {
[ "respect" "ignore" "smart" ]
}
def "nu-complete sk scheme" [] {
[ "default" "path" "history" ]
}
def "nu-complete sk layout" [] {
[ "default" "reverse" "reverse-list" ]
}
def "nu-complete sk border" [] {
[ "force-off" "none" "plain" "rounded" "double" "thick" "light-double-dashed" "heavy-double-dashed" "light-triple-dashed" "heavy-triple-dashed" "light-quadruple-dashed" "heavy-quadruple-dashed" "quadrant-inside" "quadrant-outside" ]
}
def "nu-complete sk image" [] {
[ "detect" "halfblocks" ]
}
def "nu-complete sk shell" [] {
[ "bash" "elvish" "fish" "nushell" "power-shell" "zsh" ]
}
def "nu-complete sk flags" [] {
[ "no-preview-pty" "show-score" "show-index" "single-reader" "single-matcher" ]
}
# Fuzzy Finder in rust!
export extern sk [
--tac # Show results in reverse order
--min-query-length: string # Minimum query length to start showing results
--no-sort # Do not sort the results
--tiebreak(-t): string@"nu-complete sk tiebreak" # Comma-separated list of sort criteria to apply when the scores are tied.
--nth(-n): string # Fields to be matched
--with-nth: string # Fields to be transformed
--hide-nth: string # Fields to hide from display while keeping them searchable
--delimiter(-d): string # Delimiter between fields
--exact(-e) # Run in exact mode
--regex # Start in regex mode instead of fuzzy-match
--algo: string@"nu-complete sk algorithm" # Fuzzy matching algorithm
--case: string@"nu-complete sk case" # Case sensitivity
--typos: string # Enable typo-tolerant matching
--no-typos # Disable typo-tolerant matching
--normalize # Normalize unicode characters
--split-match: string # Enable split matching and set delimiter
--last-match # Highlight the last match found, not the first one This makes tiebreak more pertinent on path items where we want to prioritize a match on the last parts
--scheme: string@"nu-complete sk scheme"
--bind(-b): string # Comma-separated key, event, and action bindings
--multi(-m) # Enable multiple selection
--no-multi # Disable multiple selection
--no-mouse # Disable mouse
--cmd(-c): string # Command to invoke dynamically in interactive mode
--interactive(-i) # Start skim in interactive mode
-I: string # Replace replstr with the selected item in commands
--color: string # Set color theme
--highlight-line # Highlight the entire current line, not just the text
--no-hscroll # Disable horizontal scroll
--keep-right # Keep the right end of the line visible on overflow
--skip-to-pattern: string # Show the matched pattern at the line start
--no-clear-if-empty # Do not clear previous line if the command returns an empty result
--no-clear-start # Do not clear items on start
--no-clear # Do not clear screen on exit
--show-cmd-error # Show error message if command fails
--cycle # Cycle the results by wrapping around when scrolling
--disabled # Disable matching entirely
--disable-pattern: string # Disable items based on this regex pattern
--layout: string@"nu-complete sk layout" # Set layout
--reverse # Shorthand for reverse layout
--height: string # Height of skim's window
--no-height # Disable height (force full screen)
--min-height: string # Minimum height of skim's window as a non-negative row count
--margin: string # Screen margin
--prompt(-p): string # Set prompt
--cmd-prompt: string # Set prompt in command mode
--selector: string # Set selected item icon
--multi-selector: string # Set multi-selected item icon
--ansi # Parse ANSI color codes in input strings
--tabstop: string # Number of spaces that make up a tab
--ellipsis: string # The characters used to display truncated lines
--info: string # Set matching result count display position
--no-info # Alias for --info=hidden
--inline-info # Alias for --info=inline
--header: string # Set header, displayed next to the info
--header-lines: string # Number of lines of the input treated as header
--border: string@"nu-complete sk border" # Draw borders around the UI components
--border-no-collapse # Do not collapse adjacent borders into a shared row or column
--no-border # Disables all borders, including in tmux/zellij popups
--wrap # Wrap items in the item list
--multiline: string # Split item text into multiple display lines at the given separator character defaults to \n if read0 is set, and \\n if not (matching literal \n in text)
--scrollbar: string # Set scrollbar style for the item list
--no-scrollbar # Disable the scrollbar in the item list
--history: string # History file
--history-size: string # Maximum number of query history entries to keep
--cmd-history: string # Command history file
--cmd-history-size: string # Maximum number of query history entries to keep
--preview: string # Preview command
--preview-window: string # Preview window layout
--image: string@"nu-complete sk image" # Enable image preview
--query(-q): string # Initial query
--cmd-query: string # Initial query in interactive mode
--read0 # Read input delimited by ASCII NUL(\0) characters
--print0 # Print output delimited by ASCII NUL(\0) characters
--print-query # Print the query as the first line
--print-cmd # Print the command as the first line (after print-query)
--print-score # Print the score after each item
--print-header # Print the header as the first line (after print-score)
--print-current # Print the current (highlighted) item as the first line (after print-header)
--output-format: string # Set the output format If set, overrides all print_ options Will be expanded the same way as preview or commands
--no-strip-ansi # Print the ANSI codes, making the output exactly match the input even when --ansi is on
--select-1(-1) # Do not enter the TUI if the query passed in -q matches only one item and return it
--exit-0(-0) # Do not enter the TUI if the query passed in -q does not match any item
--sync # Synchronous search for multi-staged filtering
--pre-select-n: string # Pre-select the first n items in multi-selection mode
--pre-select-pat: string # Pre-select the matched items in multi-selection mode
--pre-select-items: string # Pre-select the items separated by newline character
--pre-select-file: string # Pre-select the items read from this file
--filter(-f): string # Query for filter mode
--shell: string@"nu-complete sk shell" # Generate shell completion script
--shell-bindings # Generate shell key bindings - only for bash, zsh and fish
--man # Generate man page and output it to stdout
--listen: string # Run an IPC socket with optional name (defaults to sk)
--remote: string # Send commands to an IPC socket with optional name (defaults to sk)
--popup: string # Run in a tmux or zellij popup
--log-level: string # Set the log level
--log-file: string # Pipe log output to a file
--flags: string@"nu-complete sk flags" # Feature flags
--extended(-x)
--literal
--hscroll-off: string
--filepath-word
--jump-labels: string
--no-bold
--phony
--tail: string
--style: string
--no-color
--padding: string
--border-label: string
--border-label-pos: string
--wrap-sign: string
--no-multi-line
--raw
--track
--gap: string
--gap-line: string
--freeze-left: string
--freeze-right: string
--scroll-off: string
--gutter: string
--gutter-raw: string
--marker-multi-line: string
--list-border: string
--list-label: string
--list-label-pos: string
--no-input
--info-command: string
--separator: string
--no-separator
--ghost: string
--input-border: string
--input-label: string
--input-label-pos: string
--preview-label: string
--preview-label-pos: string
--header-first
--header-border: string
--header-lines-border: string
--footer: string
--footer-border: string
--footer-label: string
--footer-label-pos: string
--with-shell: string
--expect: string # Deprecated, kept for compatibility purposes. See accept() bind instead
--help(-h) # Print help (see more with '--help')
--version(-V) # Print version
]
}
export use completions *

View file

@ -1,329 +1,226 @@
# ____ ____
# / __/___ / __/
# / /_/_ / / /_
# / __/ / /_/ __/
# /_/ /___/_/ completion.zsh
#
# - $SKIM_TMUX (default: 0)
# - $SKIM_TMUX_OPTS (default: '-d 40%')
# - $SKIM_COMPLETION_TRIGGER (default: '**')
# - $SKIM_COMPLETION_OPTS (default: empty)
#compdef sk
# Both branches of the following `if` do the same thing -- define
# __skim_completion_options such that `eval $__skim_completion_options` sets
# all options to the same values they currently have. We'll do just that at
# the bottom of the file after changing options to what we prefer.
#
# IMPORTANT: Until we get to the `emulate` line, all words that *can* be quoted
# *must* be quoted in order to prevent alias expansion. In addition, code must
# be written in a way works with any set of zsh options. This is very tricky, so
# careful when you change it.
#
# Start by loading the builtin zsh/parameter module. It provides `options`
# associative array that stores current shell options.
if 'zmodload' 'zsh/parameter' 2>'/dev/null' && (( ${+options} )); then
# This is the fast branch and it gets taken on virtually all Zsh installations.
#
# ${(kv)options[@]} expands to array of keys (option names) and values ("on"
# or "off"). The subsequent expansion# with (j: :) flag joins all elements
# together separated by spaces. __skim_completion_options ends up with a value
# like this: "options=(shwordsplit off aliases on ...)".
__skim_completion_options="options=(${(j: :)${(kv)options[@]}})"
autoload -U is-at-least
_sk() {
typeset -A opt_args
typeset -a _arguments_options
local ret=1
if is-at-least 5.2; then
_arguments_options=(-s -S -C)
else
_arguments_options=(-s -C)
fi
local context curcontext="$curcontext" state line
_arguments "${_arguments_options[@]}" : \
'--min-query-length=[Minimum query length to start showing results]:MIN_QUERY_LENGTH:_default' \
'*-t+[Comma-separated list of sort criteria to apply when the scores are tied.]:TIEBREAK:(score -score begin -begin end -end length -length index -index pathname -pathname)' \
'*--tiebreak=[Comma-separated list of sort criteria to apply when the scores are tied.]:TIEBREAK:(score -score begin -begin end -end length -length index -index pathname -pathname)' \
'*-n+[Fields to be matched]:NTH:_default' \
'*--nth=[Fields to be matched]:NTH:_default' \
'*--with-nth=[Fields to be transformed]:WITH_NTH:_default' \
'*--hide-nth=[Fields to hide from display while keeping them searchable]:HIDE_NTH:_default' \
'-d+[Delimiter between fields]:DELIMITER:_default' \
'--delimiter=[Delimiter between fields]:DELIMITER:_default' \
'--algo=[Fuzzy matching algorithm]:ALGORITHM:((arinae\:"Arinae\: typo-resistant & natural algorithm, default"
clangd\:"Clangd fuzzy matching algorithm"
fzy\:"Fzy matching algorithm (https\://github.com/jhawthorn/fzy)"
frizbee\:"Frizbee matching algorithm, typo resistant"
skim_v2\:"Previous skim fuzzy matching algorithm (v2)"))' \
'--case=[Case sensitivity]:CASE:((respect\:"Case-sensitive matching"
ignore\:"Case-insensitive matching"
smart\:"Smart case\: case-insensitive unless query contains uppercase"))' \
'--typos=[Enable typo-tolerant matching]::TYPOS:_default' \
'--split-match=[Enable split matching and set delimiter]::SPLIT_MATCH:_default' \
'--scheme=[]:SCHEME:((default\:"Default scheme, no modifications to the options"
path\:"Path scheme\: will find the furthest match in the item and set pathname as the main tiebreak"
history\:"History scheme\: will force index as the first tiebreak"))' \
'*-b+[Comma-separated key, event, and action bindings]::BIND:_default' \
'*--bind=[Comma-separated key, event, and action bindings]::BIND:_default' \
'-c+[Command to invoke dynamically in interactive mode]:CMD:_default' \
'--cmd=[Command to invoke dynamically in interactive mode]:CMD:_default' \
'-I+[Replace replstr with the selected item in commands]:REPLSTR:_default' \
'--color=[Set color theme]:COLOR:_default' \
'--skip-to-pattern=[Show the matched pattern at the line start]:SKIP_TO_PATTERN:_default' \
'--disable-pattern=[Disable items based on this regex pattern]:DISABLE_PATTERN:_default' \
'--layout=[Set layout]:LAYOUT:((default\:"Display from the bottom of the screen"
reverse\:"Display from the top of the screen"
reverse-list\:"Display from the top of the screen, prompt at the bottom"))' \
'--height=[Height of skim'\''s window]:HEIGHT:_default' \
'--min-height=[Minimum height of skim'\''s window as a non-negative row count]:MIN_HEIGHT:_default' \
'--margin=[Screen margin]:MARGIN:_default' \
'-p+[Set prompt]:PROMPT:_default' \
'--prompt=[Set prompt]:PROMPT:_default' \
'--cmd-prompt=[Set prompt in command mode]:CMD_PROMPT:_default' \
'--selector=[Set selected item icon]:SELECTOR_ICON:_default' \
'--multi-selector=[Set multi-selected item icon]:MULTI_SELECT_ICON:_default' \
'--tabstop=[Number of spaces that make up a tab]:TABSTOP:_default' \
'--ellipsis=[The characters used to display truncated lines]:ELLIPSIS:_default' \
'--info=[Set matching result count display position]:INFO:_default' \
'--header=[Set header, displayed next to the info]:HEADER:_default' \
'--header-lines=[Number of lines of the input treated as header]:HEADER_LINES:_default' \
'--border=[Draw borders around the UI components]::BORDER:((force-off\:"ForceOff disables borders around popups too set with no_border"
none\:""
plain\:""
rounded\:""
double\:""
thick\:""
light-double-dashed\:""
heavy-double-dashed\:""
light-triple-dashed\:""
heavy-triple-dashed\:""
light-quadruple-dashed\:""
heavy-quadruple-dashed\:""
quadrant-inside\:""
quadrant-outside\:""))' \
'--multiline=[Split item text into multiple display lines at the given separator character defaults to \\n if read0 is set, and \\\\n if not (matching literal \\n in text)]::MULTILINE:_default' \
'--scrollbar=[Set scrollbar style for the item list]:THUMB:_default' \
'--history=[History file]:HISTORY_FILE:_default' \
'--history-size=[Maximum number of query history entries to keep]:HISTORY_SIZE:_default' \
'--cmd-history=[Command history file]:CMD_HISTORY_FILE:_default' \
'--cmd-history-size=[Maximum number of query history entries to keep]:CMD_HISTORY_SIZE:_default' \
'--preview=[Preview command]:PREVIEW:_default' \
'--preview-window=[Preview window layout]:PREVIEW_WINDOW:_default' \
'--image=[Enable image preview]::IMAGE:((detect\:"Default\: automatically detect the available backend at startup"
halfblocks\:"Force halfblocks if you want blurry previews but a faster startup or if the detection fails"))' \
'-q+[Initial query]:QUERY:_default' \
'--query=[Initial query]:QUERY:_default' \
'--cmd-query=[Initial query in interactive mode]:CMD_QUERY:_default' \
'--output-format=[Set the output format If set, overrides all print_ options Will be expanded the same way as preview or commands]:OUTPUT_FORMAT:_default' \
'--pre-select-n=[Pre-select the first n items in multi-selection mode]:PRE_SELECT_N:_default' \
'--pre-select-pat=[Pre-select the matched items in multi-selection mode]:PRE_SELECT_PAT:_default' \
'--pre-select-items=[Pre-select the items separated by newline character]:PRE_SELECT_ITEMS:_default' \
'--pre-select-file=[Pre-select the items read from this file]:PRE_SELECT_FILE:_default' \
'-f+[Query for filter mode]:FILTER:_default' \
'--filter=[Query for filter mode]:FILTER:_default' \
'--shell=[Generate shell completion script]:SHELL:((bash\:"Bourne Again SHell"
elvish\:"Elvish shell"
fish\:"Friendly Interactive SHell"
nushell\:"Nushell (nu)"
power-shell\:"PowerShell"
zsh\:"Zsh"))' \
'--listen=[Run an IPC socket with optional name (defaults to sk)]::LISTEN:_default' \
'--remote=[Send commands to an IPC socket with optional name (defaults to sk)]::REMOTE:_default' \
'--popup=[Run in a tmux or zellij popup]::POPUP:_default' \
'--log-level=[Set the log level]:LOG_LEVEL:_default' \
'--log-file=[Pipe log output to a file]:LOG_FILE:_default' \
'*--flags=[Feature flags]:FLAGS:((no-preview-pty\:"Disable preview PTY on Linux"
show-score\:"Display the item'\''s match score before its value in the item list (for matcher debugging)"
show-index\:"Display the item'\''s index before its value in the item list"
single-reader\:"Limit the reader thread pool to a single thread"
single-matcher\:"Limit the matcher thread pool to a single thread"))' \
'--hscroll-off=[]:HSCROLL_OFF:_default' \
'--jump-labels=[]:JUMP_LABELS:_default' \
'--tail=[]:TAIL:_default' \
'--style=[]:STYLE:_default' \
'--padding=[]:PADDING:_default' \
'--border-label=[]:BORDER_LABEL:_default' \
'--border-label-pos=[]:BORDER_LABEL_POS:_default' \
'--wrap-sign=[]:WRAP_SIGN:_default' \
'--gap=[]:GAP:_default' \
'--gap-line=[]:GAP_LINE:_default' \
'--freeze-left=[]:FREEZE_LEFT:_default' \
'--freeze-right=[]:FREEZE_RIGHT:_default' \
'--scroll-off=[]:SCROLL_OFF:_default' \
'--gutter=[]:GUTTER:_default' \
'--gutter-raw=[]:GUTTER_RAW:_default' \
'--marker-multi-line=[]:MARKER_MULTI_LINE:_default' \
'--list-border=[]:LIST_BORDER:_default' \
'--list-label=[]:LIST_LABEL:_default' \
'--list-label-pos=[]:LIST_LABEL_POS:_default' \
'--info-command=[]:INFO_COMMAND:_default' \
'--separator=[]:SEPARATOR:_default' \
'--ghost=[]:GHOST:_default' \
'--input-border=[]:INPUT_BORDER:_default' \
'--input-label=[]:INPUT_LABEL:_default' \
'--input-label-pos=[]:INPUT_LABEL_POS:_default' \
'--preview-label=[]:PREVIEW_LABEL:_default' \
'--preview-label-pos=[]:PREVIEW_LABEL_POS:_default' \
'--header-border=[]:HEADER_BORDER:_default' \
'--header-lines-border=[]:HEADER_LINES_BORDER:_default' \
'--footer=[]:FOOTER:_default' \
'--footer-border=[]:FOOTER_BORDER:_default' \
'--footer-label=[]:FOOTER_LABEL:_default' \
'--footer-label-pos=[]:FOOTER_LABEL_POS:_default' \
'--with-shell=[]:WITH_SHELL:_default' \
'--expect=[Deprecated, kept for compatibility purposes. See accept() bind instead]:EXPECT:_default' \
'--tac[Show results in reverse order]' \
'--no-sort[Do not sort the results]' \
'-e[Run in exact mode]' \
'--exact[Run in exact mode]' \
'--regex[Start in regex mode instead of fuzzy-match]' \
'--no-typos[Disable typo-tolerant matching]' \
'--normalize[Normalize unicode characters]' \
'--last-match[Highlight the last match found, not the first one This makes tiebreak more pertinent on path items where we want to prioritize a match on the last parts]' \
'-m[Enable multiple selection]' \
'--multi[Enable multiple selection]' \
'--no-multi[Disable multiple selection]' \
'--no-mouse[Disable mouse]' \
'-i[Start skim in interactive mode]' \
'--interactive[Start skim in interactive mode]' \
'--highlight-line[Highlight the entire current line, not just the text]' \
'--no-hscroll[Disable horizontal scroll]' \
'--keep-right[Keep the right end of the line visible on overflow]' \
'--no-clear-if-empty[Do not clear previous line if the command returns an empty result]' \
'--no-clear-start[Do not clear items on start]' \
'--no-clear[Do not clear screen on exit]' \
'--show-cmd-error[Show error message if command fails]' \
'--cycle[Cycle the results by wrapping around when scrolling]' \
'--disabled[Disable matching entirely]' \
'--reverse[Shorthand for reverse layout]' \
'--no-height[Disable height (force full screen)]' \
'--ansi[Parse ANSI color codes in input strings]' \
'--no-info[Alias for --info=hidden]' \
'--inline-info[Alias for --info=inline]' \
'--border-no-collapse[Do not collapse adjacent borders into a shared row or column]' \
'--no-border[Disables all borders, including in tmux/zellij popups]' \
'--wrap[Wrap items in the item list]' \
'--no-scrollbar[Disable the scrollbar in the item list]' \
'--read0[Read input delimited by ASCII NUL(\\0) characters]' \
'--print0[Print output delimited by ASCII NUL(\\0) characters]' \
'--print-query[Print the query as the first line]' \
'--print-cmd[Print the command as the first line (after print-query)]' \
'--print-score[Print the score after each item]' \
'--print-header[Print the header as the first line (after print-score)]' \
'--print-current[Print the current (highlighted) item as the first line (after print-header)]' \
'--no-strip-ansi[Print the ANSI codes, making the output exactly match the input even when --ansi is on]' \
'-1[Do not enter the TUI if the query passed in -q matches only one item and return it]' \
'--select-1[Do not enter the TUI if the query passed in -q matches only one item and return it]' \
'-0[Do not enter the TUI if the query passed in -q does not match any item]' \
'--exit-0[Do not enter the TUI if the query passed in -q does not match any item]' \
'--sync[Synchronous search for multi-staged filtering]' \
'--shell-bindings[Generate shell key bindings - only for bash, zsh and fish]' \
'--man[Generate man page and output it to stdout]' \
'-x[]' \
'--extended[]' \
'--literal[]' \
'--filepath-word[]' \
'--no-bold[]' \
'--phony[]' \
'--no-color[]' \
'--no-multi-line[]' \
'--raw[]' \
'--track[]' \
'--no-input[]' \
'--no-separator[]' \
'--header-first[]' \
'-h[Print help (see more with '\''--help'\'')]' \
'--help[Print help (see more with '\''--help'\'')]' \
'-V[Print version]' \
'--version[Print version]' \
&& ret=0
}
(( $+functions[_sk_commands] )) ||
_sk_commands() {
local commands; commands=()
_describe -t commands 'sk commands' commands "$@"
}
if [ "$funcstack[1]" = "_sk" ]; then
_sk "$@"
else
# This branch is much slower because it forks to get the names of all
# zsh options. It's possible to eliminate this fork but it's not worth the
# trouble because this branch gets taken only on very ancient or broken
# zsh installations.
() {
# That `()` above defines an anonymous function. This is essentially a scope
# for local parameters. We use it to avoid polluting global scope.
'local' '__skim_opt'
__skim_completion_options="setopt"
# `set -o` prints one line for every zsh option. Each line contains option
# name, some spaces, and then either "on" or "off". We just want option names.
# Expansion with (@f) flag splits a string into lines. The outer expansion
# removes spaces and everything that follow them on every line. __skim_opt
# ends up iterating over option names: shwordsplit, aliases, etc.
for __skim_opt in "${(@)${(@f)$(set -o)}%% *}"; do
if [[ -o "$__skim_opt" ]]; then
# Option $__skim_opt is currently on, so remember to set it back on.
__skim_completion_options+=" -o $__skim_opt"
else
# Option $__skim_opt is currently off, so remember to set it back off.
__skim_completion_options+=" +o $__skim_opt"
fi
done
# The value of __skim_completion_options here looks like this:
# "setopt +o shwordsplit -o aliases ..."
}
compdef _sk sk
fi
# Enable the default zsh options (those marked with <Z> in `man zshoptions`)
# but without `aliases`. Aliases in functions are expanded when functions are
# defined, so if we disable aliases here, we'll be sure to have no pesky
# aliases in any of our functions. This way we won't need prefix every
# command with `command` or to quote every word to defend against global
# aliases. Note that `aliases` is not the only option that's important to
# control. There are several others that could wreck havoc if they are set
# to values we don't expect. With the following `emulate` command we
# sidestep this issue entirely.
'emulate' 'zsh' '-o' 'no_aliases'
# This brace is the start of try-always block. The `always` part is like
# `finally` in lesser languages. We use it to *always* restore user options.
{
# Bail out if not interactive shell.
[[ -o interactive ]] || return 0
# To use custom commands instead of find, override _skim_compgen_{path,dir}
if ! declare -f _skim_compgen_path > /dev/null; then
_skim_compgen_path() {
echo "$1"
command find -L "$1" \
-name .git -prune -o -name .hg -prune -o -name .svn -prune -o \( -type d -o -type f -o -type l \) \
-a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@'
}
fi
if ! declare -f _skim_compgen_dir > /dev/null; then
_skim_compgen_dir() {
command find -L "$1" \
-name .git -prune -o -name .hg -prune -o -name .svn -prune -o -type d \
-a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@'
}
fi
###########################################################
__skim_comprun() {
if [[ "$(type _skim_comprun 2>&1)" =~ function ]]; then
_skim_comprun "$@"
elif [ -n "$TMUX_PANE" ] && { [ "${SKIM_TMUX:-0}" != 0 ] || [ -n "$SKIM_TMUX_OPTS" ]; }; then
shift
if [ -n "$SKIM_TMUX_OPTS" ]; then
sk-tmux ${(Q)${(Z+n+)SKIM_TMUX_OPTS}} -- "$@"
else
sk-tmux -d ${SKIM_TMUX_HEIGHT:-40%} -- "$@"
fi
else
shift
sk "$@"
fi
}
# Extract the name of the command. e.g. foo=1 bar baz**<tab>
__skim_extract_command() {
local token tokens
tokens=(${(z)1})
for token in $tokens; do
token=${(Q)token}
if [[ "$token" =~ [[:alnum:]] && ! "$token" =~ "=" ]]; then
echo "$token"
return
fi
done
echo "${tokens[1]}"
}
__skim_generic_path_completion() {
local base lbuf cmd compgen skim_opts suffix tail dir leftover matches
base=$1
lbuf=$2
cmd=$(__skim_extract_command "$lbuf")
compgen=$3
skim_opts=$4
suffix=$5
tail=$6
setopt localoptions nonomatch
eval "base=$base"
[[ $base = *"/"* ]] && dir="$base"
while [ 1 ]; do
if [[ -z "$dir" || -d ${dir} ]]; then
leftover=${base/#"$dir"}
leftover=${leftover/#\/}
[ -z "$dir" ] && dir='.'
[ "$dir" != "/" ] && dir="${dir/%\//}"
matches=$(eval "$compgen $(printf %q "$dir")" | SKIM_DEFAULT_OPTIONS="--height ${SKIM_TMUX_HEIGHT:-40%} --reverse $SKIM_DEFAULT_OPTIONS $SKIM_COMPLETION_OPTS" __skim_comprun "$cmd" ${(Q)${(Z+n+)skim_opts}} -q "$leftover" | while read item; do
echo -n "${(q)item}$suffix "
done)
matches=${matches% }
if [ -n "$matches" ]; then
LBUFFER="$lbuf$matches$tail"
fi
zle reset-prompt
break
fi
dir=$(dirname "$dir")
dir=${dir%/}/
done
}
_skim_path_completion() {
__skim_generic_path_completion "$1" "$2" _skim_compgen_path \
"-m" "" " "
}
_skim_dir_completion() {
__skim_generic_path_completion "$1" "$2" _skim_compgen_dir \
"" "/" ""
}
_skim_feed_fifo() (
command rm -f "$1"
mkfifo "$1"
cat <&0 > "$1" &
)
_skim_complete() {
setopt localoptions ksh_arrays
# Split arguments around --
local args rest str_arg i sep
args=("$@")
sep=
for i in {0..${#args[@]}}; do
if [[ "${args[$i]}" = -- ]]; then
sep=$i
break
fi
done
if [[ -n "$sep" ]]; then
str_arg=
rest=("${args[@]:$((sep + 1)):${#args[@]}}")
args=("${args[@]:0:$sep}")
else
str_arg=$1
args=()
shift
rest=("$@")
fi
local fifo lbuf cmd matches post
fifo="${TMPDIR:-/tmp}/skim-complete-fifo-$$"
lbuf=${rest[0]}
cmd=$(__skim_extract_command "$lbuf")
post="${funcstack[1]}_post"
type $post > /dev/null 2>&1 || post=cat
_skim_feed_fifo "$fifo"
matches=$(SKIM_DEFAULT_OPTIONS="--height ${SKIM_TMUX_HEIGHT:-40%} --reverse $SKIM_DEFAULT_OPTIONS $SKIM_COMPLETION_OPTS $str_arg" __skim_comprun "$cmd" "${args[@]}" -q "${(Q)prefix}" < "$fifo" | $post | tr '\n' ' ')
if [ -n "$matches" ]; then
LBUFFER="$lbuf$matches"
fi
zle reset-prompt
command rm -f "$fifo"
}
_skim_complete_telnet() {
_skim_complete --no-multi -- "$@" < <(
command grep -v '^\s*\(#\|$\)' /etc/hosts | command grep -Fv '0.0.0.0' |
awk '{if (length($2) > 0) {print $2}}' | sort -u
)
}
_skim_complete_ssh() {
_skim_complete --no-multi -- "$@" < <(
setopt localoptions nonomatch
command cat <(command tail -n +1 ~/.ssh/config ~/.ssh/config.d/* /etc/ssh/ssh_config 2> /dev/null | command grep -i '^\s*host\(name\)\? ' | awk '{for (i = 2; i <= NF; i++) print $1 " " $i}' | command grep -v '[*?]') \
<(command grep -oE '^[[a-z0-9.,:-]+' ~/.ssh/known_hosts | tr ',' '\n' | tr -d '[' | awk '{ print $1 " " $1 }') \
<(command grep -v '^\s*\(#\|$\)' /etc/hosts | command grep -Fv '0.0.0.0') |
awk '{if (length($2) > 0) {print $2}}' | sort -u
)
}
_skim_complete_export() {
_skim_complete -m -- "$@" < <(
declare -xp | sed 's/=.*//' | sed 's/.* //'
)
}
_skim_complete_unset() {
_skim_complete -m -- "$@" < <(
declare -xp | sed 's/=.*//' | sed 's/.* //'
)
}
_skim_complete_unalias() {
_skim_complete --no-multi -- "$@" < <(
alias | sed 's/=.*//'
)
}
_skim_complete_kill() {
_skim_complete -m --preview 'echo {}' --preview-window down:3:wrap --min-height 15 -- "$@" < <(
command ps -ef | sed 1d
)
}
_skim_complete_kill_post() {
awk '{print $2}'
}
skim-completion() {
local tokens cmd prefix trigger tail matches lbuf d_cmds
setopt localoptions noshwordsplit noksh_arrays noposixbuiltins
# http://zsh.sourceforge.net/FAQ/zshfaq03.html
# http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion-Flags
tokens=(${(z)LBUFFER})
if [ ${#tokens} -lt 1 ]; then
zle ${skim_default_completion:-expand-or-complete}
return
fi
cmd=$(__skim_extract_command "$LBUFFER")
# Explicitly allow for empty trigger.
trigger=${SKIM_COMPLETION_TRIGGER-'**'}
[ -z "$trigger" -a ${LBUFFER[-1]} = ' ' ] && tokens+=("")
# When the trigger starts with ';', it becomes a separate token
if [[ ${LBUFFER} = *"${tokens[-2]}${tokens[-1]}" ]]; then
tokens[-2]="${tokens[-2]}${tokens[-1]}"
tokens=(${tokens[0,-2]})
fi
lbuf=$LBUFFER
tail=${LBUFFER:$(( ${#LBUFFER} - ${#trigger} ))}
# Kill completion (do not require trigger sequence)
if [ "$cmd" = kill -a ${LBUFFER[-1]} = ' ' ]; then
tail=$trigger
tokens+=$trigger
lbuf="$lbuf$trigger"
fi
# Trigger sequence given
if [ ${#tokens} -gt 1 -a "$tail" = "$trigger" ]; then
d_cmds=(${=SKIM_COMPLETION_DIR_COMMANDS:-cd pushd rmdir})
[ -z "$trigger" ] && prefix=${tokens[-1]} || prefix=${tokens[-1]:0:-${#trigger}}
[ -n "${tokens[-1]}" ] && lbuf=${lbuf:0:-${#tokens[-1]}}
if eval "type _skim_complete_${cmd} > /dev/null"; then
prefix="$prefix" eval _skim_complete_${cmd} ${(q)lbuf}
elif [ ${d_cmds[(i)$cmd]} -le ${#d_cmds} ]; then
_skim_dir_completion "$prefix" "$lbuf"
else
_skim_path_completion "$prefix" "$lbuf"
fi
# Fall back to default completion
else
zle ${skim_default_completion:-expand-or-complete}
fi
}
[ -z "$skim_default_completion" ] && {
binding=$(bindkey '^I')
[[ $binding =~ 'undefined-key' ]] || skim_default_completion=$binding[(s: :w)2]
unset binding
}
zle -N skim-completion
bindkey '^I' skim-completion
} always {
# Restore the original options.
eval $__skim_completion_options
'unset' '__skim_completion_options'
}

View file

@ -1,97 +1,400 @@
# ____ ____
# / __/___ / __/
# / /_/_ / / /_
# / __/ / /_/ __/
# /_/ /___/_/ key-bindings.bash
# skim key bindings for bash
#
# - $SKIM_TMUX_OPTS
# - $SKIM_CTRL_T_COMMAND
# - $SKIM_CTRL_T_OPTS
# - $SKIM_CTRL_R_OPTS
# - $SKIM_CTRL_R_IDX_COLOR (default: \033[2m, set NO_COLOR to disable)
# - $SKIM_ALT_C_COMMAND
# - $SKIM_ALT_C_OPTS
# - $SKIM_COMPLETION_TRIGGER (default: '**')
# - $SKIM_COMPLETION_OPTS (default: empty)
# Key bindings
# ------------
# copied and modified from https://github.com/junegunn/fzf/blob/master/shell/key-bindings.bash
__skim_select__() {
local cmd="${SKIM_CTRL_T_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
local cmd="${SKIM_CTRL_T_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
-o -type f -print \
-o -type d -print \
-o -type l -print 2> /dev/null | cut -b3-"}"
eval "$cmd" | SKIM_DEFAULT_OPTIONS="--height ${SKIM_TMUX_HEIGHT:-40%} --reverse $SKIM_DEFAULT_OPTIONS $SKIM_CTRL_T_OPTS" $(__skimcmd) -m "$@" | while read -r item; do
printf '%q ' "$item"
done
echo
eval "$cmd" | SKIM_DEFAULT_OPTIONS="--reverse $SKIM_DEFAULT_OPTIONS $SKIM_CTRL_T_OPTS" $(__skimcmd) -m "$@" | while read -r item; do
printf '%q ' "$item"
done
echo
}
if [[ $- =~ i ]]; then
__skimcmd() {
[ -n "$TMUX_PANE" ] && { [ "${SKIM_TMUX:-0}" != 0 ] || [ -n "$SKIM_TMUX_OPTS" ]; } &&
echo "sk-tmux ${SKIM_TMUX_OPTS:--d${SKIM_TMUX_HEIGHT:-40%}} -- " || echo "sk"
}
__skimcmd() {
[ -n "$TMUX_PANE" ] && { [ "${SKIM_TMUX:-0}" != 0 ] || [ -n "$SKIM_TMUX_OPTS" ]; } &&
echo "sk --tmux=${SKIM_TMUX_OPTS:-center,${SKIM_TMUX_HEIGHT:-40%}}" || echo "sk"
}
skim-file-widget() {
local selected="$(__skim_select__)"
READLINE_LINE="${READLINE_LINE:0:$READLINE_POINT}$selected${READLINE_LINE:$READLINE_POINT}"
READLINE_POINT=$(( READLINE_POINT + ${#selected} ))
}
skim-file-widget() {
local selected="$(__skim_select__)"
READLINE_LINE="${READLINE_LINE:0:$READLINE_POINT}$selected${READLINE_LINE:$READLINE_POINT}"
READLINE_POINT=$((READLINE_POINT + ${#selected}))
}
__skim_cd__() {
local cmd dir
cmd="${SKIM_ALT_C_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
-o -type d -print 2> /dev/null | cut -b3-"}"
dir=$(eval "$cmd" | SKIM_DEFAULT_OPTIONS="--height ${SKIM_TMUX_HEIGHT:-40%} --reverse $SKIM_DEFAULT_OPTIONS $SKIM_ALT_C_OPTS" $(__skimcmd) --no-multi) && printf 'cd %q' "$dir"
}
__skim_cd__() {
local cmd dir
cmd="${SKIM_ALT_C_COMMAND:-"command find -L . -mindepth 1 \\( -path '*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' -o -fstype 'proc' \\) -prune \
-o -type d -printf '%P\\n' 2>/dev/null"}"
dir=$(eval "$cmd" | SKIM_DEFAULT_OPTIONS="--reverse $SKIM_DEFAULT_OPTIONS $SKIM_ALT_C_OPTS" $(__skimcmd) --no-multi)
if [ -n "$dir" ]; then
printf 'cd %q' "$dir"
fi
}
__skim_history__() {
local output
output=$(
builtin fc -lnr -2147483648 |
last_hist=$(HISTTIMEFORMAT='' builtin history 1) perl -n -l0 -e 'BEGIN { getc; $/ = "\n\t"; $HISTCMD = $ENV{last_hist} + 1 } s/^[ *]//; print $HISTCMD - $. . "\t$_" if !$seen{$_}++' |
SKIM_DEFAULT_OPTIONS="--height ${SKIM_TMUX_HEIGHT:-40%} $SKIM_DEFAULT_OPTIONS -n2..,.. --tiebreak=index --bind=ctrl-r:toggle-sort $SKIM_CTRL_R_OPTS --no-multi --read0" $(__skimcmd) --query "$READLINE_LINE"
) || return
READLINE_LINE=${output#*$'\t'}
if [ -z "$READLINE_POINT" ]; then
echo "$READLINE_LINE"
else
READLINE_POINT=0x7fffffff
fi
}
__skim_history__() {
local output
local c_idx='' c_reset='' ansi_opt=''
if [[ ! -v NO_COLOR ]]; then
c_idx="${SKIM_CTRL_R_IDX_COLOR:-\033[2m}"
c_reset='\033[0m'
ansi_opt='--ansi'
fi
output=$(
builtin fc -lnr -2147483648 |
last_hist=$(HISTTIMEFORMAT='' builtin history 1) awk -v last_hist="$last_hist" -v c_idx="$c_idx" -v c_reset="$c_reset" '
BEGIN { HISTCMD = last_hist + 1; cmd = ""; idx = 0 }
/^\t/ {
if (cmd != "" && !seen[cmd]++) printf "%s%d%s\t%s%c", c_idx, HISTCMD - idx, c_reset, cmd, 0
idx++; cmd = substr($0, 2); sub(/^[ *]/, "", cmd); next
}
{ cmd = cmd "\n" $0 }
END { if (cmd != "" && !seen[cmd]++) printf "%s%d%s\t%s%c", c_idx, HISTCMD - idx, c_reset, cmd, 0 }
' |
SKIM_DEFAULT_OPTIONS="$SKIM_DEFAULT_OPTIONS -n2..,.. --bind=ctrl-r:toggle-sort $SKIM_CTRL_R_OPTS --no-multi --read0 --multiline $ansi_opt" $(__skimcmd) --query "$READLINE_LINE"
) || return
echo -e "\033[0m"
READLINE_LINE=${output#*$'\t'}
if [ -z "$READLINE_POINT" ]; then
echo "$READLINE_LINE"
else
READLINE_POINT=0x7fffffff
fi
}
# Required to refresh the prompt after skim
bind -m emacs-standard '"\er": redraw-current-line'
# Required to refresh the prompt after skim
bind -m emacs-standard '"\er": redraw-current-line'
bind -m vi-command '"\C-z": emacs-editing-mode'
bind -m vi-insert '"\C-z": emacs-editing-mode'
bind -m emacs-standard '"\C-z": vi-editing-mode'
bind -m vi-command '"\C-z": emacs-editing-mode'
bind -m vi-insert '"\C-z": emacs-editing-mode'
bind -m emacs-standard '"\C-z": vi-editing-mode'
if [ "${BASH_VERSINFO[0]}" -lt 4 ]; then
# CTRL-T - Paste the selected file path into the command line
bind -m emacs-standard '"\C-t": " \C-b\C-k \C-u`__skim_select__`\e\C-e\er\C-a\C-y\C-h\C-e\e \C-y\ey\C-x\C-x\C-f"'
bind -m vi-command '"\C-t": "\C-z\C-t\C-z"'
bind -m vi-insert '"\C-t": "\C-z\C-t\C-z"'
if [ "${BASH_VERSINFO[0]}" -lt 4 ]; then
# CTRL-T - Paste the selected file path into the command line
bind -m emacs-standard '"\C-t": " \C-b\C-k \C-u`__skim_select__`\e\C-e\er\C-a\C-y\C-h\C-e\e \C-y\ey\C-x\C-x\C-f"'
bind -m vi-command '"\C-t": "\C-z\C-t\C-z"'
bind -m vi-insert '"\C-t": "\C-z\C-t\C-z"'
# CTRL-R - Paste the selected command from history into the command line
bind -m emacs-standard '"\C-r": "\C-e \C-u\C-y\ey\C-u"$(__skim_history__)"\e\C-e\er"'
bind -m vi-command '"\C-r": "\C-z\C-r\C-z"'
bind -m vi-insert '"\C-r": "\C-z\C-r\C-z"'
else
# CTRL-T - Paste the selected file path into the command line
bind -m emacs-standard -x '"\C-t": skim-file-widget'
bind -m vi-command -x '"\C-t": skim-file-widget'
bind -m vi-insert -x '"\C-t": skim-file-widget'
# CTRL-R - Paste the selected command from history into the command line
bind -m emacs-standard '"\C-r": "\C-e \C-u\C-y\ey\C-u"$(__skim_history__)"\e\C-e\er"'
bind -m vi-command '"\C-r": "\C-z\C-r\C-z"'
bind -m vi-insert '"\C-r": "\C-z\C-r\C-z"'
else
# CTRL-T - Paste the selected file path into the command line
bind -m emacs-standard -x '"\C-t": skim-file-widget'
bind -m vi-command -x '"\C-t": skim-file-widget'
bind -m vi-insert -x '"\C-t": skim-file-widget'
# CTRL-R - Paste the selected command from history into the command line
bind -m emacs-standard -x '"\C-r": __skim_history__'
bind -m vi-command -x '"\C-r": __skim_history__'
bind -m vi-insert -x '"\C-r": __skim_history__'
fi
# CTRL-R - Paste the selected command from history into the command line
bind -m emacs-standard -x '"\C-r": __skim_history__'
bind -m vi-command -x '"\C-r": __skim_history__'
bind -m vi-insert -x '"\C-r": __skim_history__'
fi
# ALT-C - cd into the selected directory
bind -m emacs-standard '"\ec": " \C-b\C-k \C-u`__skim_cd__`\e\C-e\er\C-m\C-y\C-h\e \C-y\ey\C-x\C-x\C-d"'
bind -m vi-command '"\ec": "\C-z\ec\C-z"'
bind -m vi-insert '"\ec": "\C-z\ec\C-z"'
# ALT-C - cd into the selected directory
bind -m emacs-standard '"\ec": " \C-b\C-k \C-u`__skim_cd__`\e\C-e\er\C-m\C-y\C-h\e \C-y\ey\C-x\C-x\C-d"'
bind -m vi-command '"\ec": "\C-z\ec\C-z"'
bind -m vi-insert '"\ec": "\C-z\ec\C-z"'
# Completion
if ! declare -f _skim_compgen_path >/dev/null; then
_skim_compgen_path() {
echo "$1"
command find -L "$1" \
-name .git -prune -o -name .hg -prune -o -name .svn -prune -o \( -type d -o -type f -o -type l \) \
-a -not -path "$1" -print 2>/dev/null | sed 's@^\./@@'
}
fi
if ! declare -f _skim_compgen_dir >/dev/null; then
_skim_compgen_dir() {
command find -L "$1" \
-name .git -prune -o -name .hg -prune -o -name .svn -prune -o -type d \
-a -not -path "$1" -print 2>/dev/null | sed 's@^\./@@'
}
fi
###########################################################
__skim_comprun() {
if [ "$(type -t _skim_comprun 2>&1)" = function ]; then
_skim_comprun "$@"
elif [ -n "$TMUX_PANE" ] && { [ "${SKIM_TMUX:-0}" != 0 ] || [ -n "$SKIM_TMUX_OPTS" ]; }; then
shift
sk --tmux=${SKIM_TMUX_OPTS:-center,${SKIM_TMUX_HEIGHT:-40%}} "$@"
else
shift
sk "$@"
fi
}
__skim_orig_completion_filter() {
sed 's/^\(.*-F\) *\([^ ]*\).* \([^ ]*\)$/export _skim_orig_completion_\3="\1 %s \3 #\2"; [[ "\1" = *" -o nospace "* ]] \&\& [[ ! "$__skim_nospace_commands" = *" \3 "* ]] \&\& __skim_nospace_commands="$__skim_nospace_commands \3 ";/' |
awk -F= '{OFS = FS} {gsub(/[^A-Za-z0-9_= ;]/, "_", $1);}1'
}
_skim_handle_dynamic_completion() {
local cmd orig_var orig ret orig_cmd orig_complete
cmd="$1"
shift
orig_cmd="$1"
orig_var="_skim_orig_completion_$cmd"
orig="${!orig_var##*#}"
if [ -n "$orig" ] && type "$orig" >/dev/null 2>&1; then
$orig "$@"
elif [ -n "$_skim_completion_loader" ]; then
orig_complete=$(complete -p "$orig_cmd" 2>/dev/null)
_completion_loader "$@"
ret=$?
# _completion_loader may not have updated completion for the command
if [ "$(complete -p "$orig_cmd" 2>/dev/null)" != "$orig_complete" ]; then
eval "$(complete | command grep " -F.* $orig_cmd$" | __skim_orig_completion_filter)"
if [[ "$__skim_nospace_commands" = *" $orig_cmd "* ]]; then
eval "${orig_complete/ -F / -o nospace -F }"
else
eval "$orig_complete"
fi
fi
return $ret
fi
}
__skim_generic_path_completion() {
local cur base dir leftover matches trigger cmd
cmd="${COMP_WORDS[0]//[^A-Za-z0-9_=]/_}"
COMPREPLY=()
trigger=${SKIM_COMPLETION_TRIGGER-'**'}
cur="${COMP_WORDS[COMP_CWORD]}"
if [[ "$cur" == *"$trigger" ]]; then
base=${cur:0:${#cur}-${#trigger}}
eval "base=$base"
[[ $base = *"/"* ]] && dir="$base"
while true; do
if [ -z "$dir" ] || [ -d "$dir" ]; then
leftover=${base/#"$dir"/}
leftover=${leftover/#\//}
[ -z "$dir" ] && dir='.'
[ "$dir" != "/" ] && dir="${dir/%\//}"
matches=$(eval "$1 $(printf %q "$dir")" | SKIM_DEFAULT_OPTIONS="--reverse $SKIM_DEFAULT_OPTIONS $SKIM_COMPLETION_OPTS $2" __skim_comprun "$4" -q "$leftover" | while read -r item; do
printf "%q$3 " "$item"
done)
matches=${matches% }
[[ -z "$3" ]] && [[ "$__skim_nospace_commands" = *" ${COMP_WORDS[0]} "* ]] && matches="$matches "
if [ -n "$matches" ]; then
COMPREPLY=("$matches")
else
COMPREPLY=("$cur")
fi
# To redraw line after skim closes (printf '\e[5n')
bind '"\e[0n": redraw-current-line'
printf '\e[5n'
return 0
fi
dir=$(dirname "$dir")
[[ "$dir" =~ /$ ]] || dir="$dir"/
done
else
shift
shift
shift
_skim_handle_dynamic_completion "$cmd" "$@"
fi
}
_skim_complete() {
# Split arguments around --
local args rest str_arg i sep
args=("$@")
sep=
for i in "${!args[@]}"; do
if [[ "${args[$i]}" = -- ]]; then
sep=$i
break
fi
done
if [[ -n "$sep" ]]; then
str_arg=
rest=("${args[@]:$((sep + 1)):${#args[@]}}")
args=("${args[@]:0:$sep}")
else
str_arg=$1
args=()
shift
rest=("$@")
fi
local cur selected trigger cmd post
post="$(caller 0 | awk '{print $2}')_post"
type -t "$post" >/dev/null 2>&1 || post=cat
cmd="${COMP_WORDS[0]//[^A-Za-z0-9_=]/_}"
trigger=${SKIM_COMPLETION_TRIGGER-'**'}
cur="${COMP_WORDS[COMP_CWORD]}"
if [[ "$cur" == *"$trigger" ]]; then
cur=${cur:0:${#cur}-${#trigger}}
selected=$(SKIM_DEFAULT_OPTIONS="--reverse $SKIM_DEFAULT_OPTIONS $SKIM_COMPLETION_OPTS $str_arg" __skim_comprun "${rest[0]}" "${args[@]}" -q "$cur" | $post | tr '\n' ' ')
selected=${selected% } # Strip trailing space not to repeat "-o nospace"
if [ -n "$selected" ]; then
COMPREPLY=("$selected")
else
COMPREPLY=("$cur")
fi
# To redraw line after skim closes (printf '\e[5n')
bind '"\e[0n": redraw-current-line'
printf '\e[5n'
echo -e "\033[0m"
return 0
else
_skim_handle_dynamic_completion "$cmd" "${rest[@]}"
fi
}
_skim_path_completion() {
__skim_generic_path_completion _skim_compgen_path "-m" "" "$@"
}
# Deprecated. No file only completion.
_skim_file_completion() {
_skim_path_completion "$@"
}
_skim_dir_completion() {
__skim_generic_path_completion _skim_compgen_dir "" "/" "$@"
}
_skim_complete_kill() {
local trigger=${SKIM_COMPLETION_TRIGGER-'**'}
local cur="${COMP_WORDS[COMP_CWORD]}"
if [[ -z "$cur" ]]; then
COMP_WORDS[$COMP_CWORD]=$trigger
elif [[ "$cur" != *"$trigger" ]]; then
return 1
fi
_skim_proc_completion "$@"
}
_skim_proc_completion() {
_skim_complete -m --preview 'echo {}' --preview-window down:3:wrap --min-height 15 -- "$@" < <(
command ps -ef | sed 1d
)
}
_skim_proc_completion_post() {
awk '{print $2}'
}
_skim_host_completion() {
_skim_complete --no-multi -- "$@" < <(
command cat <(command tail -n +1 ~/.ssh/config ~/.ssh/config.d/* /etc/ssh/ssh_config 2>/dev/null | command grep -i '^\s*host\(name\)\? ' | awk '{for (i = 2; i <= NF; i++) print $1 " " $i}' | command grep -v '[*?]') \
<(command grep -soE '^[[a-z0-9.,:-]+' ~/.ssh/known_hosts | tr ',' '\n' | tr -d '[' | awk '{ print $1 " " $1 }') \
<(command grep -sv '^\s*\(#\|$\)' /etc/hosts | command grep -Fv '0.0.0.0') |
awk '{if (length($2) > 0) {print $2}}' | sort -u
)
}
_skim_var_completion() {
_skim_complete -m -- "$@" < <(
declare -xp | sed 's/=.*//' | sed 's/.* //'
)
}
_skim_alias_completion() {
_skim_complete -m -- "$@" < <(
alias | sed 's/=.*//' | sed 's/.* //'
)
}
d_cmds="${SKIM_COMPLETION_DIR_COMMANDS:-cd pushd rmdir}"
a_cmds="
awk cat diff diff3
emacs emacsclient ex file ftp g++ gcc gvim head hg java
javac ld less more mvim nvim patch perl python ruby
sed sftp sort source tail tee uniq vi view vim wc xdg-open
basename bunzip2 bzip2 chmod chown curl cp dirname du
find git grep gunzip gzip hg jar
ln ls mv open rm rsync scp
svn tar unzip zip"
# Preserve existing completion
eval "$(complete |
sed -E '/-F/!d; / _skim/d; '"/ ($(echo $d_cmds $a_cmds | sed 's/ /|/g; s/+/\\+/g'))$/"'!d' |
__skim_orig_completion_filter)"
if type _completion_loader >/dev/null 2>&1; then
_skim_completion_loader=1
fi
__skim_defc() {
local cmd func opts orig_var orig def
cmd="$1"
func="$2"
opts="$3"
orig_var="_skim_orig_completion_${cmd//[^A-Za-z0-9_]/_}"
orig="${!orig_var}"
if [ -n "$orig" ]; then
printf -v def "$orig" "$func"
eval "$def"
else
complete -F "$func" $opts "$cmd"
fi
}
# Anything
for cmd in $a_cmds; do
__skim_defc "$cmd" _skim_path_completion "-o default -o bashdefault"
done
# Directory
for cmd in $d_cmds; do
__skim_defc "$cmd" _skim_dir_completion "-o nospace -o dirnames"
done
# Kill completion (supports empty completion trigger)
complete -F _skim_complete_kill -o default -o bashdefault kill
unset cmd d_cmds a_cmds
_skim_setup_completion() {
local kind fn cmd
kind=$1
fn=_skim_${1}_completion
if [[ $# -lt 2 ]] || ! type -t "$fn" >/dev/null; then
echo "usage: ${FUNCNAME[0]} path|dir|var|alias|host|proc COMMANDS..."
return 1
fi
shift
eval "$(complete -p "$@" 2>/dev/null | grep -v "$fn" | __skim_orig_completion_filter)"
for cmd in "$@"; do
case "$kind" in
dir) __skim_defc "$cmd" "$fn" "-o nospace -o dirnames" ;;
var) __skim_defc "$cmd" "$fn" "-o default -o nospace -v" ;;
alias) __skim_defc "$cmd" "$fn" "-a" ;;
*) __skim_defc "$cmd" "$fn" "-o default -o bashdefault" ;;
esac
done
}
# Environment variables / Aliases / Hosts
_skim_setup_completion 'var' export unset
_skim_setup_completion 'alias' unalias
_skim_setup_completion 'host' ssh telnet
fi

View file

@ -1,11 +1,5 @@
#!/bin/fish
# completion.fish
# copied and modified from https://github.com/junegunn/fzf/blob/master/shell/key-bindings.fish
# ____ ____
# / __/___ / __/
# / /_/_ / / /_
# / __/ / /_/ __/
# /_/ /___/_/ key-bindings.fish
# skim key bindings for fish
#
# - $SKIM_TMUX_OPTS
# - $SKIM_CTRL_T_COMMAND
@ -13,6 +7,8 @@
# - $SKIM_CTRL_R_OPTS
# - $SKIM_ALT_C_COMMAND
# - $SKIM_ALT_C_OPTS
# - $SKIM_COMPLETION_TRIGGER (default: '**')
# - $SKIM_COMPLETION_OPTS (default: empty)
# Key bindings
# ------------
@ -32,9 +28,8 @@ function skim_key_bindings
-o -type d -print \
-o -type l -print 2> /dev/null | sed 's@^\./@@'"
test -n "$SKIM_TMUX_HEIGHT"; or set SKIM_TMUX_HEIGHT 40%
begin
set -lx SKIM_DEFAULT_OPTIONS "--height $SKIM_TMUX_HEIGHT --reverse $SKIM_DEFAULT_OPTIONS $SKIM_CTRL_T_OPTS"
set -lx SKIM_DEFAULT_OPTIONS "--reverse $SKIM_DEFAULT_OPTIONS $SKIM_CTRL_T_OPTS"
eval "$SKIM_CTRL_T_COMMAND | "(__skimcmd)' -m --query "'$skim_query'"' | while read -l r; set result $result $r; end
end
if [ -z "$result" ]
@ -52,9 +47,8 @@ function skim_key_bindings
end
function skim-history-widget -d "Show command history"
test -n "$SKIM_TMUX_HEIGHT"; or set SKIM_TMUX_HEIGHT 40%
begin
set -lx SKIM_DEFAULT_OPTIONS "--height $SKIM_TMUX_HEIGHT $SKIM_DEFAULT_OPTIONS --tiebreak=index --bind=ctrl-r:toggle-sort $SKIM_CTRL_R_OPTS --no-multi"
set -lx SKIM_DEFAULT_OPTIONS "$SKIM_DEFAULT_OPTIONS --bind=ctrl-r:toggle-sort $SKIM_CTRL_R_OPTS --no-multi"
set -l FISH_MAJOR (echo $version | cut -f1 -d.)
set -l FISH_MINOR (echo $version | cut -f2 -d.)
@ -63,7 +57,7 @@ function skim_key_bindings
# history's -z flag was added in fish 2.4.0, so don't use it for versions
# before 2.4.0.
if [ "$FISH_MAJOR" -gt 2 -o \( "$FISH_MAJOR" -eq 2 -a "$FISH_MINOR" -ge 4 \) ];
history -z | eval (__skimcmd) --read0 --print0 -q '(commandline)' | read -lz result
history -z | eval (__skimcmd) --read0 --multiline --print0 -q '(commandline)' | read -lz result
and commandline -- $result
else
history | eval (__skimcmd) -q '(commandline)' | read -l result
@ -81,9 +75,8 @@ function skim_key_bindings
test -n "$SKIM_ALT_C_COMMAND"; or set -l SKIM_ALT_C_COMMAND "
command find -L \$dir -mindepth 1 \\( -path \$dir'*/\\.*' -o -fstype 'sysfs' -o -fstype 'devfs' -o -fstype 'devtmpfs' \\) -prune \
-o -type d -print 2> /dev/null | sed 's@^\./@@'"
test -n "$SKIM_TMUX_HEIGHT"; or set SKIM_TMUX_HEIGHT 40%
begin
set -lx SKIM_DEFAULT_OPTIONS "--height $SKIM_TMUX_HEIGHT --reverse $SKIM_DEFAULT_OPTIONS $SKIM_ALT_C_OPTS"
set -lx SKIM_DEFAULT_OPTIONS "--reverse $SKIM_DEFAULT_OPTIONS $SKIM_ALT_C_OPTS"
eval "$SKIM_ALT_C_COMMAND | "(__skimcmd)' --no-multi --query "'$skim_query'"' | read -l result
if [ -n "$result" ]
@ -101,9 +94,9 @@ function skim_key_bindings
test -n "$SKIM_TMUX"; or set SKIM_TMUX 0
test -n "$SKIM_TMUX_HEIGHT"; or set SKIM_TMUX_HEIGHT 40%
if [ -n "$SKIM_TMUX_OPTS" ]
echo "sk-tmux $SKIM_TMUX_OPTS -- "
echo "sk --tmux=$SKIM_TMUX_OPTS "
else if [ $SKIM_TMUX -eq 1 ]
echo "sk-tmux -d$SKIM_TMUX_HEIGHT -- "
echo "sk --tmux=center,$SKIM_TMUX_HEIGHT"
else
echo "sk"
end
@ -160,5 +153,4 @@ function skim_key_bindings
echo $dir
end
end

View file

@ -1,23 +1,22 @@
# ____ ____
# / __/___ / __/
# / /_/_ / / /_
# / __/ / /_/ __/
# /_/ /___/_/ key-bindings.zsh
# skim key bindings for zsh
#
# - $SKIM_TMUX_OPTS
# - $SKIM_CTRL_T_COMMAND
# - $SKIM_CTRL_T_OPTS
# - $SKIM_CTRL_R_OPTS
# - $SKIM_CTRL_R_IDX_COLOR (default: \033[2m, set NO_COLOR to disable)
# - $SKIM_CTRL_R_DATE_COLOR (default: \033[32m, set NO_COLOR to disable)
# - $SKIM_ALT_C_COMMAND
# - $SKIM_ALT_C_OPTS
# - $SKIM_COMPLETION_TRIGGER (default: '**')
# - $SKIM_COMPLETION_OPTS (default: empty)
# Key bindings
# ------------
# The code at the top and the bottom of this file is the same as in completion.zsh.
# Refer to that file for explanation.
if 'zmodload' 'zsh/parameter' 2>'/dev/null' && (( ${+options} )); then
__skim_key_bindings_options="options=(${(j: :)${(kv)options[@]}})"
__skim_completion_options="options=(${(j: :)${(kv)options[@]}})"
else
() {
__skim_key_bindings_options="setopt"
@ -30,6 +29,17 @@ else
fi
done
}
() {
'local' '__skim_opt'
__skim_completion_options="setopt"
for __skim_opt in "${(@)${(@f)$(set -o)}%% *}"; do
if [[ -o "$__skim_opt" ]]; then
__skim_completion_options+=" -o $__skim_opt"
else
__skim_completion_options+=" +o $__skim_opt"
fi
done
}
fi
'emulate' 'zsh' '-o' 'no_aliases'
@ -47,7 +57,7 @@ __fsel() {
setopt localoptions pipefail no_aliases 2> /dev/null
REPORTTIME_=$REPORTTIME
unset REPORTTIME
eval "$cmd" | SKIM_DEFAULT_OPTIONS="--height ${SKIM_TMUX_HEIGHT:-40%} --reverse $SKIM_DEFAULT_OPTIONS $SKIM_CTRL_T_OPTS" $(__skimcmd) -m "$@" | while read item; do
eval "$cmd" | SKIM_DEFAULT_OPTIONS="--reverse $SKIM_DEFAULT_OPTIONS $SKIM_CTRL_T_OPTS" $(__skimcmd) -m "$@" | while read item; do
echo -n "${(q)item} "
done
local ret=$?
@ -61,7 +71,7 @@ __fsel() {
__skimcmd() {
[ -n "$TMUX_PANE" ] && { [ "${SKIM_TMUX:-0}" != 0 ] || [ -n "$SKIM_TMUX_OPTS" ]; } &&
echo "sk-tmux ${SKIM_TMUX_OPTS:--d${SKIM_TMUX_HEIGHT:-40%}} -- " || echo "sk"
echo "sk --tmux=${SKIM_TMUX_OPTS:-center,${SKIM_TMUX_HEIGHT:-40%}}" || echo "sk"
}
skim-file-widget() {
@ -90,7 +100,7 @@ skim-cd-widget() {
setopt localoptions pipefail no_aliases 2> /dev/null
REPORTTIME_=$REPORTTIME
unset REPORTTIME
local dir="$(eval "$cmd" | SKIM_DEFAULT_OPTIONS="--height ${SKIM_TMUX_HEIGHT:-40%} --reverse $SKIM_DEFAULT_OPTIONS $SKIM_ALT_C_OPTS" $(__skimcmd) --no-multi)"
local dir="$(eval "$cmd" | SKIM_DEFAULT_OPTIONS="--reverse $SKIM_DEFAULT_OPTIONS $SKIM_ALT_C_OPTS" $(__skimcmd) --no-multi)"
if ! [ -z $REPORTTIME_ ]; then
REPORTTIME=$REPORTTIME_
fi
@ -109,6 +119,7 @@ skim-cd-widget() {
local ret=$?
unset dir # ensure this doesn't end up appearing in prompt expansion
zle skim-redraw-prompt
tput cnorm
return $ret
}
zle -N skim-cd-widget
@ -118,8 +129,35 @@ bindkey '\ec' skim-cd-widget
skim-history-widget() {
local selected num
setopt localoptions noglobsubst noposixbuiltins pipefail no_aliases 2> /dev/null
selected=( $(fc -rl 1 | perl -ne 'print if !$seen{(/^\s*[0-9]+\**\s+(.*)/, $1)}++' |
SKIM_DEFAULT_OPTIONS="--height ${SKIM_TMUX_HEIGHT:-40%} $SKIM_DEFAULT_OPTIONS -n2..,.. --tiebreak=index --bind=ctrl-r:toggle-sort $SKIM_CTRL_R_OPTS --query=${(qqq)LBUFFER} --no-multi" $(__skimcmd)) )
local c_idx='' c_date='' c_reset='' ansi_opt=''
if [[ ! -v NO_COLOR ]]; then
c_idx="${SKIM_CTRL_R_IDX_COLOR:-\033[2m}"
c_date="${SKIM_CTRL_R_DATE_COLOR:-\033[32m}"
c_reset='\033[0m'
ansi_opt='--ansi'
fi
local awk_filter='{ $1=$1; cmd=$0; sub(/^[ \t]*[0-9]+\**[ \t]+/, "", cmd); if (!seen[cmd]++) { idx=$1; sub(idx, c_idx idx c_reset); print } }'
local n=2 fc_opts=''
if [[ -o extended_history ]]; then
local today=$(date +%Y-%m-%d)
# For today's commands, replace date ($2) with "today", otherwise remove time ($3).
# And filter out duplicates.
awk_filter='{
$1=$1;
cmd = $0; sub(/^[ \t]*[0-9]+\**[ \t]+[^ \t]+[ \t]+[^ \t]+[ \t]+/, "", cmd)
if (!seen[cmd]++) {
time = $3; date = $2; idx = $1
if (date == today) sub(date " " time " ", c_date "today@" time c_reset "\t")
else sub(date " " time " ", c_date date c_reset "\t")
sub(idx, c_idx idx c_reset)
print
}
}'
fc_opts='-i'
n=3
fi
selected=( $(fc -rl $fc_opts 1 | awk -v c_idx="$c_idx" -v c_date="$c_date" -v c_reset="$c_reset" -v today="$today" "$awk_filter" | sed 's/\\n/\\n\t/g' |
SKIM_DEFAULT_OPTIONS="$SKIM_DEFAULT_OPTIONS -n$n..,.. --bind=ctrl-r:toggle-sort $SKIM_CTRL_R_OPTS --query=${(qqq)LBUFFER} --no-multi $ansi_opt --tabstop=20 --multiline" $(__skimcmd)) )
local ret=$?
if [ -n "$selected" ]; then
num=$selected[1]
@ -128,12 +166,254 @@ skim-history-widget() {
fi
fi
zle reset-prompt
tput cnorm
return $ret
}
zle -N skim-history-widget
bindkey '^R' skim-history-widget
# Completion
# To use custom commands instead of find, override _skim_compgen_{path,dir}
if ! declare -f _skim_compgen_path > /dev/null; then
_skim_compgen_path() {
echo "$1"
command find -L "$1" \
-name .git -prune -o -name .hg -prune -o -name .svn -prune -o \( -type d -o -type f -o -type l \) \
-a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@'
}
fi
if ! declare -f _skim_compgen_dir > /dev/null; then
_skim_compgen_dir() {
command find -L "$1" \
-name .git -prune -o -name .hg -prune -o -name .svn -prune -o -type d \
-a -not -path "$1" -print 2> /dev/null | sed 's@^\./@@'
}
fi
###########################################################
__skim_comprun() {
if [[ "$(type _skim_comprun 2>&1)" =~ function ]]; then
_skim_comprun "$@"
elif [ -n "$TMUX_PANE" ] && { [ "${SKIM_TMUX:-0}" != 0 ] || [ -n "$SKIM_TMUX_OPTS" ]; }; then
shift
if [ -n "$SKIM_TMUX_OPTS" ]; then
sk --tmux=${(Q)${(Z+n+)SKIM_TMUX_OPTS}} "$@"
else
sk --tmux=${SKIM_TMUX_OPTS:-center,${SKIM_TMUX_HEIGHT:-40%}} "$@"
fi
else
shift
sk "$@"
fi
}
# Extract the name of the command. e.g. foo=1 bar baz**<tab>
__skim_extract_command() {
local token tokens
tokens=(${(z)1})
for token in $tokens; do
token=${(Q)token}
if [[ "$token" =~ [[:alnum:]] && ! "$token" =~ "=" ]]; then
echo "$token"
return
fi
done
echo "${tokens[1]}"
}
__skim_generic_path_completion() {
local base lbuf cmd compgen skim_opts suffix tail dir leftover matches
base=$1
lbuf=$2
cmd=$(__skim_extract_command "$lbuf")
compgen=$3
skim_opts=$4
suffix=$5
tail=$6
setopt localoptions nonomatch
eval "base=$base"
[[ $base = *"/"* ]] && dir="$base"
while [ 1 ]; do
if [[ -z "$dir" || -d ${dir} ]]; then
leftover=${base/#"$dir"}
leftover=${leftover/#\/}
[ -z "$dir" ] && dir='.'
[ "$dir" != "/" ] && dir="${dir/%\//}"
matches=$(eval "$compgen $(printf %q "$dir")" | SKIM_DEFAULT_OPTIONS="--reverse $SKIM_DEFAULT_OPTIONS $SKIM_COMPLETION_OPTS" __skim_comprun "$cmd" ${(Q)${(Z+n+)skim_opts}} -q "$leftover" | while read item; do
echo -n "${(q)item}$suffix "
done)
matches=${matches% }
if [ -n "$matches" ]; then
LBUFFER="$lbuf$matches$tail"
fi
zle reset-prompt
break
fi
dir=$(dirname "$dir")
dir=${dir%/}/
done
}
_skim_path_completion() {
__skim_generic_path_completion "$1" "$2" _skim_compgen_path \
"-m" "" " "
}
_skim_dir_completion() {
__skim_generic_path_completion "$1" "$2" _skim_compgen_dir \
"" "/" ""
}
_skim_complete() {
setopt localoptions ksh_arrays
# Split arguments around --
local args rest str_arg i sep
args=("$@")
sep=
for i in {0..${#args[@]}}; do
if [[ "${args[$i]}" = -- ]]; then
sep=$i
break
fi
done
if [[ -n "$sep" ]]; then
str_arg=
rest=("${args[@]:$((sep + 1)):${#args[@]}}")
args=("${args[@]:0:$sep}")
else
str_arg=$1
args=()
shift
rest=("$@")
fi
local lbuf cmd matches post
lbuf=${rest[0]}
cmd=$(__skim_extract_command "$lbuf")
post="${funcstack[1]}_post"
type $post > /dev/null 2>&1 || post=cat
matches=$(SKIM_DEFAULT_OPTIONS="--reverse $SKIM_DEFAULT_OPTIONS $SKIM_COMPLETION_OPTS $str_arg" __skim_comprun "$cmd" "${args[@]}" -q "${(Q)prefix}" | $post | tr '\n' ' ')
if [ -n "$matches" ]; then
LBUFFER="$lbuf$matches"
fi
zle reset-prompt
}
_skim_complete_telnet() {
_skim_complete --no-multi -- "$@" < <(
command grep -sv '^\s*\(#\|$\)' /etc/hosts | command grep -Fv '0.0.0.0' |
awk '{if (length($2) > 0) {print $2}}' | sort -u
)
}
_skim_complete_ssh() {
_skim_complete --no-multi -- "$@" < <(
setopt localoptions nonomatch
command cat <(command tail -n +1 ~/.ssh/config ~/.ssh/config.d/* /etc/ssh/ssh_config 2> /dev/null | command grep -i '^\s*host\(name\)\? ' | awk '{for (i = 2; i <= NF; i++) print $1 " " $i}' | command grep -v '[*?]') \
<(command grep -osE '^[[a-z0-9.,:-]+' ~/.ssh/known_hosts | tr ',' '\n' | tr -d '[' | awk '{ print $1 " " $1 }') \
<(command grep -sv '^\s*\(#\|$\)' /etc/hosts | command grep -Fv '0.0.0.0') |
awk '{if (length($2) > 0) {print $2}}' | sort -u
)
}
_skim_complete_export() {
_skim_complete -m -- "$@" < <(
declare -xp | sed 's/=.*//' | sed 's/.* //'
)
}
_skim_complete_unset() {
_skim_complete -m -- "$@" < <(
declare -xp | sed 's/=.*//' | sed 's/.* //'
)
}
_skim_complete_unalias() {
_skim_complete --no-multi -- "$@" < <(
alias | sed 's/=.*//'
)
}
_skim_complete_kill() {
_skim_complete -m --preview 'echo {}' --preview-window down:3:wrap --min-height 15 -- "$@" < <(
command ps -ef | sed 1d
)
}
_skim_complete_kill_post() {
awk '{print $2}'
}
skim-completion() {
local tokens cmd prefix trigger tail matches lbuf d_cmds
setopt localoptions noshwordsplit noksh_arrays noposixbuiltins
# http://zsh.sourceforge.net/FAQ/zshfaq03.html
# http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion-Flags
tokens=(${(z)LBUFFER})
if [ ${#tokens} -lt 1 ]; then
zle ${skim_default_completion:-expand-or-complete}
return
fi
cmd=$(__skim_extract_command "$LBUFFER")
# Explicitly allow for empty trigger.
trigger=${SKIM_COMPLETION_TRIGGER-'**'}
[ -z "$trigger" -a ${LBUFFER[-1]} = ' ' ] && tokens+=("")
# When the trigger starts with ';', it becomes a separate token
if [[ ${LBUFFER} = *"${tokens[-2]}${tokens[-1]}" ]]; then
tokens[-2]="${tokens[-2]}${tokens[-1]}"
tokens=(${tokens[0,-2]})
fi
lbuf=$LBUFFER
tail=${LBUFFER:$(( ${#LBUFFER} - ${#trigger} ))}
# Kill completion (do not require trigger sequence)
if [ "$cmd" = kill -a ${LBUFFER[-1]} = ' ' ]; then
tail=$trigger
tokens+=$trigger
lbuf="$lbuf$trigger"
fi
# Trigger sequence given
if [ ${#tokens} -gt 1 -a "$tail" = "$trigger" ]; then
d_cmds=(${=SKIM_COMPLETION_DIR_COMMANDS:-cd pushd rmdir})
[ -z "$trigger" ] && prefix=${tokens[-1]} || prefix=${tokens[-1]:0:-${#trigger}}
[ -n "${tokens[-1]}" ] && lbuf=${lbuf:0:-${#tokens[-1]}}
if eval "type _skim_complete_${cmd} > /dev/null"; then
prefix="$prefix" eval _skim_complete_${cmd} ${(q)lbuf}
elif [ ${d_cmds[(i)$cmd]} -le ${#d_cmds} ]; then
_skim_dir_completion "$prefix" "$lbuf"
else
_skim_path_completion "$prefix" "$lbuf"
fi
tput cnorm
# Fall back to default completion
else
zle ${skim_default_completion:-expand-or-complete}
fi
}
[ -z "$skim_default_completion" ] && {
binding=$(bindkey '^I')
[[ $binding =~ 'undefined-key' ]] || skim_default_completion=$binding[(s: :w)2]
unset binding
}
zle -N skim-completion
bindkey '^I' skim-completion
} always {
eval $__skim_key_bindings_options
'unset' '__skim_key_bindings_options'
eval $__skim_completion_options
'unset' '__skim_completion_options'
}

1
shell/version.txt Normal file
View file

@ -0,0 +1 @@
5.7.0

View file

@ -1,609 +0,0 @@
// Parse ANSI attr code
use std::default::Default;
use beef::lean::Cow;
use std::cmp::max;
use tuikit::prelude::*;
use vte::{Params, Perform};
/// An ANSI Parser, will parse one line at a time.
///
/// It will cache the latest attribute used, that means if an attribute affect multiple
/// lines, the parser will recognize it.
#[derive(Debug, Default)]
pub struct ANSIParser {
partial_str: String,
last_attr: Attr,
stripped: String,
stripped_char_count: usize,
fragments: Vec<(Attr, (u32, u32))>, // [char_index_start, char_index_end)
}
impl Perform for ANSIParser {
fn print(&mut self, ch: char) {
self.partial_str.push(ch);
}
fn execute(&mut self, byte: u8) {
match byte {
// \b to delete character back
0x08 => {
self.partial_str.pop();
}
// put back \0 \r \n \t
0x00 | 0x0d | 0x0A | 0x09 => self.partial_str.push(byte as char),
// ignore all others
_ => trace!("AnsiParser:execute ignored {:?}", byte),
}
}
fn hook(&mut self, params: &Params, _intermediates: &[u8], _ignore: bool, _action: char) {
trace!("AnsiParser:hook ignored {:?}", params);
}
fn put(&mut self, byte: u8) {
trace!("AnsiParser:put ignored {:?}", byte);
}
fn unhook(&mut self) {
trace!("AnsiParser:unhook ignored");
}
fn osc_dispatch(&mut self, params: &[&[u8]], _bell_terminated: bool) {
trace!("AnsiParser:osc ignored {:?}", params);
}
fn csi_dispatch(&mut self, params: &Params, _intermediates: &[u8], _ignore: bool, action: char) {
// https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
// Only care about graphic modes, ignore all others
if action != 'm' {
trace!("ignore: params: {:?}, action : {:?}", params, action);
return;
}
// \[[m => means reset
let mut attr = if params.is_empty() {
Attr::default()
} else {
self.last_attr
};
let mut iter = params.iter();
while let Some(code) = iter.next() {
match code[0] {
0 => attr = Attr::default(),
1 => attr.effect |= Effect::BOLD,
2 => attr.effect |= !Effect::BOLD,
4 => attr.effect |= Effect::UNDERLINE,
5 => attr.effect |= Effect::BLINK,
7 => attr.effect |= Effect::REVERSE,
num @ 30..=37 => attr.fg = Color::AnsiValue((num - 30) as u8),
38 => match iter.next() {
Some(&[2]) => {
// ESC[ 38;2;<r>;<g>;<b> m Select RGB foreground color
let (r, g, b) = match (iter.next(), iter.next(), iter.next()) {
(Some(r), Some(g), Some(b)) => (r[0] as u8, g[0] as u8, b[0] as u8),
_ => {
trace!("ignore CSI {:?} m", params);
continue;
}
};
attr.fg = Color::Rgb(r, g, b);
}
Some(&[5]) => {
// ESC[ 38;5;<n> m Select foreground color
let color = match iter.next() {
Some(color) => color[0] as u8,
None => {
trace!("ignore CSI {:?} m", params);
continue;
}
};
attr.fg = Color::AnsiValue(color);
}
_ => {
trace!("error on parsing CSI {:?} m", params);
}
},
39 => attr.fg = Color::Default,
num @ 40..=47 => attr.bg = Color::AnsiValue((num - 40) as u8),
48 => match iter.next() {
Some(&[2]) => {
// ESC[ 48;2;<r>;<g>;<b> m Select RGB background color
let (r, g, b) = match (iter.next(), iter.next(), iter.next()) {
(Some(r), Some(g), Some(b)) => (r[0] as u8, g[0] as u8, b[0] as u8),
_ => {
trace!("ignore CSI {:?} m", params);
continue;
}
};
attr.bg = Color::Rgb(r, g, b);
}
Some(&[5]) => {
// ESC[ 48;5;<n> m Select background color
let color = match iter.next() {
Some(color) => color[0] as u8,
None => {
trace!("ignore CSI {:?} m", params);
continue;
}
};
attr.bg = Color::AnsiValue(color);
}
_ => {
trace!("ignore CSI {:?} m", params);
}
},
49 => attr.bg = Color::Default,
num @ 90..=97 => attr.fg = Color::AnsiValue((num - 82) as u8),
num @ 100..=107 => attr.bg = Color::AnsiValue((num - 92) as u8),
_ => {
trace!("ignore CSI {:?} m", params);
}
}
}
self.attr_change(attr);
}
fn esc_dispatch(&mut self, _intermediates: &[u8], _ignore: bool, _byte: u8) {
// ESC characters are replaced with \[
self.partial_str.push('"');
self.partial_str.push('[');
}
}
impl ANSIParser {
/// save the partial_str into fragments with current attr
fn save_str(&mut self) {
if self.partial_str.is_empty() {
return;
}
let string = std::mem::take(&mut self.partial_str);
let string_char_count = string.chars().count();
self.fragments.push((
self.last_attr,
(
self.stripped_char_count as u32,
(self.stripped_char_count + string_char_count) as u32,
),
));
self.stripped_char_count += string_char_count;
self.stripped.push_str(&string);
}
// accept a new attr
fn attr_change(&mut self, new_attr: Attr) {
if new_attr == self.last_attr {
return;
}
self.save_str();
self.last_attr = new_attr;
}
pub fn parse_ansi(&mut self, text: &str) -> AnsiString<'static> {
let mut statemachine = vte::Parser::new();
for byte in text.as_bytes() {
statemachine.advance(self, *byte);
}
self.save_str();
let stripped = std::mem::take(&mut self.stripped);
self.stripped_char_count = 0;
let fragments = std::mem::take(&mut self.fragments);
AnsiString::new_string(stripped, fragments)
}
}
/// A String that contains ANSI state (e.g. colors)
///
/// It is internally represented as Vec<(attr, string)>
#[derive(Clone, Debug)]
pub struct AnsiString<'a> {
stripped: Cow<'a, str>,
// attr: start, end
fragments: Option<Vec<(Attr, (u32, u32))>>,
}
impl<'a> AnsiString<'a> {
pub fn new_empty() -> Self {
Self {
stripped: Cow::borrowed(""),
fragments: None,
}
}
fn new_raw_string(string: String) -> Self {
Self {
stripped: Cow::owned(string),
fragments: None,
}
}
fn new_raw_str(str_ref: &'a str) -> Self {
Self {
stripped: Cow::borrowed(str_ref),
fragments: None,
}
}
/// assume the fragments are ordered by (start, end) while end is exclusive
pub fn new_str(stripped: &'a str, fragments: Vec<(Attr, (u32, u32))>) -> Self {
let fragments_empty = fragments.is_empty() || (fragments.len() == 1 && fragments[0].0 == Attr::default());
Self {
stripped: Cow::borrowed(stripped),
fragments: if fragments_empty { None } else { Some(fragments) },
}
}
/// assume the fragments are ordered by (start, end) while end is exclusive
pub fn new_string(stripped: String, fragments: Vec<(Attr, (u32, u32))>) -> Self {
let fragments_empty = fragments.is_empty() || (fragments.len() == 1 && fragments[0].0 == Attr::default());
Self {
stripped: Cow::owned(stripped),
fragments: if fragments_empty { None } else { Some(fragments) },
}
}
pub fn parse(raw: &'a str) -> AnsiString<'static> {
ANSIParser::default().parse_ansi(raw)
}
#[inline]
pub fn is_empty(&self) -> bool {
self.stripped.is_empty()
}
#[inline]
pub fn into_inner(self) -> std::borrow::Cow<'a, str> {
std::borrow::Cow::Owned(self.stripped.into_owned())
}
pub fn iter(&'a self) -> Box<dyn Iterator<Item = (char, Attr)> + 'a> {
if self.fragments.is_none() {
return Box::new(self.stripped.chars().map(|c| (c, Attr::default())));
}
Box::new(AnsiStringIterator::new(
&self.stripped,
self.fragments.as_ref().unwrap(),
))
}
pub fn has_attrs(&self) -> bool {
self.fragments.is_some()
}
#[inline]
pub fn stripped(&self) -> &str {
&self.stripped
}
pub fn override_attrs(&mut self, attrs: Vec<(Attr, (u32, u32))>) {
if attrs.is_empty() {
// pass
} else if self.fragments.is_none() {
self.fragments = Some(attrs);
} else {
let current_fragments = self.fragments.take().expect("unreachable");
let new_fragments = merge_fragments(&current_fragments, &attrs);
self.fragments.replace(new_fragments);
}
}
}
impl<'a> From<&'a str> for AnsiString<'a> {
fn from(s: &'a str) -> AnsiString<'a> {
AnsiString::new_raw_str(s)
}
}
impl From<String> for AnsiString<'static> {
fn from(s: String) -> Self {
AnsiString::new_raw_string(s)
}
}
// (text, indices, highlight attribute) -> AnsiString
impl<'a> From<(&'a str, &'a [usize], Attr)> for AnsiString<'a> {
fn from((text, indices, attr): (&'a str, &'a [usize], Attr)) -> Self {
let fragments = indices
.iter()
.map(|&idx| (attr, (idx as u32, 1 + idx as u32)))
.collect();
AnsiString::new_str(text, fragments)
}
}
/// An iterator over all the (char, attr) characters.
pub struct AnsiStringIterator<'a> {
fragments: &'a [(Attr, (u32, u32))],
fragment_idx: usize,
chars_iter: std::iter::Enumerate<std::str::Chars<'a>>,
}
impl<'a> AnsiStringIterator<'a> {
pub fn new(stripped: &'a str, fragments: &'a [(Attr, (u32, u32))]) -> Self {
Self {
fragments,
fragment_idx: 0,
chars_iter: stripped.chars().enumerate(),
}
}
}
impl<'a> Iterator for AnsiStringIterator<'a> {
type Item = (char, Attr);
fn next(&mut self) -> Option<Self::Item> {
match self.chars_iter.next() {
Some((char_idx, char)) => {
// update fragment_idx
loop {
if self.fragment_idx >= self.fragments.len() {
break;
}
let (_attr, (_start, end)) = self.fragments[self.fragment_idx];
if char_idx < (end as usize) {
break;
} else {
self.fragment_idx += 1;
}
}
let (attr, (start, end)) = if self.fragment_idx >= self.fragments.len() {
(Attr::default(), (char_idx as u32, 1 + char_idx as u32))
} else {
self.fragments[self.fragment_idx]
};
if (start as usize) <= char_idx && char_idx < (end as usize) {
Some((char, attr))
} else {
Some((char, Attr::default()))
}
}
None => None,
}
}
}
fn merge_fragments(old: &[(Attr, (u32, u32))], new: &[(Attr, (u32, u32))]) -> Vec<(Attr, (u32, u32))> {
let mut ret = vec![];
let mut i = 0;
let mut j = 0;
let mut os = 0;
while i < old.len() && j < new.len() {
let (oa, (o_start, oe)) = old[i];
let (na, (ns, ne)) = new[j];
os = max(os, o_start);
if ns <= os && ne >= oe {
// [--old--] | [--old--] | [--old--] | [--old--]
// [----new----] | [---new---] | [---new---] | [--new--]
i += 1; // skip old
} else if ns <= os {
// [--old--] | [--old--] | [--old--] | [---old---]
// [--new--] | [--new--] | [--new--] | [--new--]
ret.push((na, (ns, ne)));
os = ne;
j += 1;
} else if ns >= oe {
// [--old--] | [--old--]
// [--new--] | [--new--]
ret.push((oa, (os, oe)));
i += 1;
} else {
// [---old---] | [---old---] | [--old--]
// [--new--] | [--new--] | [--new--]
ret.push((oa, (os, ns)));
os = ns;
}
}
if i < old.len() {
for &(oa, (s, e)) in old[i..].iter() {
ret.push((oa, (max(os, s), e)))
}
}
if j < new.len() {
ret.extend_from_slice(&new[j..]);
}
ret
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ansi_iterator() {
let input = "\x1B[48;2;5;10;15m\x1B[38;2;70;130;180mhi\x1B[0m";
let ansistring = ANSIParser::default().parse_ansi(input);
let mut it = ansistring.iter();
let attr = Attr {
fg: Color::Rgb(70, 130, 180),
bg: Color::Rgb(5, 10, 15),
..Attr::default()
};
assert_eq!(Some(('h', attr)), it.next());
assert_eq!(Some(('i', attr)), it.next());
assert_eq!(None, it.next());
assert_eq!(ansistring.stripped(), "hi");
}
#[test]
fn test_highlight_indices() {
let text = "abc";
let indices: Vec<usize> = vec![1];
let attr = Attr {
fg: Color::Rgb(70, 130, 180),
bg: Color::Rgb(5, 10, 15),
..Attr::default()
};
let ansistring = AnsiString::from((text, &indices as &[usize], attr));
let mut it = ansistring.iter();
assert_eq!(Some(('a', Attr::default())), it.next());
assert_eq!(Some(('b', attr)), it.next());
assert_eq!(Some(('c', Attr::default())), it.next());
assert_eq!(None, it.next());
}
#[test]
fn test_normal_string() {
let input = "ab";
let ansistring = ANSIParser::default().parse_ansi(input);
assert_eq!(false, ansistring.has_attrs());
let mut it = ansistring.iter();
assert_eq!(Some(('a', Attr::default())), it.next());
assert_eq!(Some(('b', Attr::default())), it.next());
assert_eq!(None, it.next());
assert_eq!(ansistring.stripped(), "ab");
}
#[test]
fn test_multiple_attributes() {
let input = "\x1B[1;31mhi";
let ansistring = ANSIParser::default().parse_ansi(input);
let mut it = ansistring.iter();
let attr = Attr {
fg: Color::AnsiValue(1),
effect: Effect::BOLD,
..Attr::default()
};
assert_eq!(Some(('h', attr)), it.next());
assert_eq!(Some(('i', attr)), it.next());
assert_eq!(None, it.next());
assert_eq!(ansistring.stripped(), "hi");
}
#[test]
fn test_reset() {
let input = "\x1B[35mA\x1B[mB";
let ansistring = ANSIParser::default().parse_ansi(input);
assert_eq!(ansistring.fragments.as_ref().map(|x| x.len()).unwrap(), 2);
assert_eq!(ansistring.stripped(), "AB");
}
#[test]
fn test_multi_bytes() {
let input = "中`\x1B[0m\x1B[1m\x1B[31mXYZ\x1B[0ms`";
let ansistring = ANSIParser::default().parse_ansi(input);
let mut it = ansistring.iter();
let default_attr = Attr::default();
let annotated = Attr {
fg: Color::AnsiValue(1),
effect: Effect::BOLD,
..default_attr
};
assert_eq!(Some(('中', default_attr)), it.next());
assert_eq!(Some(('`', default_attr)), it.next());
assert_eq!(Some(('X', annotated)), it.next());
assert_eq!(Some(('Y', annotated)), it.next());
assert_eq!(Some(('Z', annotated)), it.next());
assert_eq!(Some(('s', default_attr)), it.next());
assert_eq!(Some(('`', default_attr)), it.next());
assert_eq!(None, it.next());
}
#[test]
fn test_merge_fragments() {
let ao = Attr::default();
let an = Attr::default().bg(Color::BLUE);
assert_eq!(
merge_fragments(&[(ao, (0, 1)), (ao, (1, 2))], &[]),
vec![(ao, (0, 1)), (ao, (1, 2))]
);
assert_eq!(
merge_fragments(&[], &[(an, (0, 1)), (an, (1, 2))]),
vec![(an, (0, 1)), (an, (1, 2))]
);
assert_eq!(
merge_fragments(&[(ao, (1, 3)), (ao, (5, 6)), (ao, (9, 10))], &[(an, (0, 1))]),
vec![(an, (0, 1)), (ao, (1, 3)), (ao, (5, 6)), (ao, (9, 10))]
);
assert_eq!(
merge_fragments(&[(ao, (1, 3)), (ao, (5, 7)), (ao, (9, 11))], &[(an, (0, 2))]),
vec![(an, (0, 2)), (ao, (2, 3)), (ao, (5, 7)), (ao, (9, 11))]
);
assert_eq!(
merge_fragments(&[(ao, (1, 3)), (ao, (5, 7)), (ao, (9, 11))], &[(an, (0, 3))]),
vec![(an, (0, 3)), (ao, (5, 7)), (ao, (9, 11))]
);
assert_eq!(
merge_fragments(
&[(ao, (1, 3)), (ao, (5, 7)), (ao, (9, 11))],
&[(an, (0, 6)), (an, (6, 7))]
),
vec![(an, (0, 6)), (an, (6, 7)), (ao, (9, 11))]
);
assert_eq!(
merge_fragments(&[(ao, (1, 3)), (ao, (5, 7)), (ao, (9, 11))], &[(an, (1, 2))]),
vec![(an, (1, 2)), (ao, (2, 3)), (ao, (5, 7)), (ao, (9, 11))]
);
assert_eq!(
merge_fragments(&[(ao, (1, 3)), (ao, (5, 7)), (ao, (9, 11))], &[(an, (1, 3))]),
vec![(an, (1, 3)), (ao, (5, 7)), (ao, (9, 11))]
);
assert_eq!(
merge_fragments(&[(ao, (1, 3)), (ao, (5, 7)), (ao, (9, 11))], &[(an, (1, 4))]),
vec![(an, (1, 4)), (ao, (5, 7)), (ao, (9, 11))]
);
assert_eq!(
merge_fragments(&[(ao, (1, 3)), (ao, (5, 7)), (ao, (9, 11))], &[(an, (2, 3))]),
vec![(ao, (1, 2)), (an, (2, 3)), (ao, (5, 7)), (ao, (9, 11))]
);
assert_eq!(
merge_fragments(&[(ao, (1, 3)), (ao, (5, 7)), (ao, (9, 11))], &[(an, (2, 4))]),
vec![(ao, (1, 2)), (an, (2, 4)), (ao, (5, 7)), (ao, (9, 11))]
);
assert_eq!(
merge_fragments(&[(ao, (1, 3)), (ao, (5, 7)), (ao, (9, 11))], &[(an, (2, 6))]),
vec![(ao, (1, 2)), (an, (2, 6)), (ao, (6, 7)), (ao, (9, 11))]
);
}
#[test]
fn test_multi_byte_359() {
// https://github.com/lotabout/skim/issues/359
let highlight = Attr::default().effect(Effect::BOLD);
let ansistring = AnsiString::new_str("ああa", vec![(highlight, (2, 3))]);
let mut it = ansistring.iter();
assert_eq!(Some(('あ', Attr::default())), it.next());
assert_eq!(Some(('あ', Attr::default())), it.next());
assert_eq!(Some(('a', highlight)), it.next());
assert_eq!(None, it.next());
}
}

View file

@ -1,474 +1,205 @@
//! Command-line interface for skim fuzzy finder.
//!
//! This binary provides the `sk` command-line tool for fuzzy finding and filtering.
#![cfg_attr(coverage, allow(unused_features), feature(coverage_attribute))]
extern crate clap;
extern crate env_logger;
#[macro_use]
extern crate log;
extern crate atty;
extern crate shlex;
extern crate skim;
extern crate time;
use derive_builder::Builder;
use std::env;
use eyre::{Result, eyre};
#[cfg(feature = "listen")]
use interprocess::bound_util::RefWrite;
#[cfg(feature = "listen")]
use interprocess::local_socket::ToNsName as _;
#[cfg(feature = "listen")]
use interprocess::local_socket::traits::Stream as _;
use log::trace;
#[cfg(feature = "listen")]
use skim::binds::parse_action_chain;
use skim::reader::CommandCollector;
use std::fs::File;
use std::io::{BufRead, BufReader, BufWriter, Write};
use std::io;
use std::io::{BufReader, BufWriter, IsTerminal, Write};
use clap::{App, Arg, ArgMatches};
use skim::prelude::*;
const VERSION: &str = env!("CARGO_PKG_VERSION");
fn init_logger(opts: &SkimOptions) {
let target = if let Some(ref log_file) = opts.log_file.as_ref().or(std::env::var("SKIM_LOG_FILE").ok().as_ref()) {
env_logger::Target::Pipe(Box::new(File::create(log_file).expect("Failed to create log file")))
} else {
env_logger::Target::Stdout
};
const USAGE: &str = "
Usage: sk [options]
let env_var = "SKIM_LOG";
Options
-h, --help print this help menu
--version print out the current version of skim
let format = |buf: &mut env_logger::fmt::Formatter, record: &log::Record<'_>| {
writeln!(
buf,
"[{} {} {} ({}:{})] [{}/{:?}] {}",
buf.timestamp_nanos(),
record.level().as_str(),
record.module_path().unwrap_or("sk"),
record.file().unwrap_or_default(),
record.line().unwrap_or_default(),
std::thread::current().name().unwrap_or("?"),
std::thread::current().id(),
record.args()
)
};
Search
--tac reverse the order of search result
--no-sort Do not sort the result
-t, --tiebreak [score,begin,end,-score,length...]
comma seperated criteria
-n, --nth 1,2..5 specify the fields to be matched
--with-nth 1,2..5 specify the fields to be transformed
-d, --delimiter \\t specify the delimiter(in REGEX) for fields
-e, --exact start skim in exact mode
--regex use regex instead of fuzzy match
--algo=TYPE Fuzzy matching algorithm:
[skim_v1|skim_v2|clangd] (default: skim_v2)
--case [respect,ignore,smart] (default: smart)
case sensitive or not
Interface
-b, --bind KEYBINDS comma seperated keybindings, in KEY:ACTION
such as 'ctrl-j:accept,ctrl-k:kill-line'
-m, --multi Enable Multiple Selection
--no-multi Disable Multiple Selection
--no-mouse Disable mouse events
-c, --cmd ag command to invoke dynamically
-i, --interactive Start skim in interactive(command) mode
--color [BASE][,COLOR:ANSI]
change color theme
--no-hscroll Disable horizontal scroll
--keep-right Keep the right end of the line visible on overflow
--skip-to-pattern Line starts with the start of matched pattern
--no-clear-if-empty Do not clear previous items if command returns empty result
--no-clear-start Do not clear on start
--show-cmd-error Send command error message if command fails
Layout
--layout=LAYOUT Choose layout: [default|reverse|reverse-list]
--height=HEIGHT Height of skim's window (--height 40%)
--no-height Disable height feature
--min-height=HEIGHT Minimum height when --height is given by percent
(default: 10)
--margin=MARGIN Screen Margin (TRBL / TB,RL / T,RL,B / T,R,B,L)
e.g. (sk --margin 1,10%)
-p, --prompt '> ' prompt string for query mode
--cmd-prompt '> ' prompt string for command mode
Display
--ansi parse ANSI color codes for input strings
--tabstop=SPACES Number of spaces for a tab character (default: 8)
--inline-info Display info next to query
--header=STR Display STR next to info
--header-lines=N The first N lines of the input are treated as header
History
--history=FILE History file
--history-size=N Maximum number of query history entries (default: 1000)
--cmd-history=FILE command History file
--cmd-history-size=N Maximum number of command history entries (default: 1000)
Preview
--preview=COMMAND command to preview current highlighted line ({})
We can specify the fields. e.g. ({1}, {..3}, {0..})
--preview-window=OPT Preview window layout (default: right:50%)
[up|down|left|right][:SIZE[%]][:hidden][:+SCROLL[-OFFSET]]
Scripting
-q, --query \"\" specify the initial query
--cmd-query \"\" specify the initial query for interactive mode
--expect KEYS comma seperated keys that can be used to complete skim
--read0 Read input delimited by ASCII NUL(\\0) characters
--print0 Print output delimited by ASCII NUL(\\0) characters
--no-clear-start Do not clear screen on start
--no-clear Do not clear screen on exit
--print-query Print query as the first line
--print-cmd Print command query as the first line (after --print-query)
--print-score Print matching score in filter output (with --filter)
-1, --select-1 Automatically select the only match
-0, --exit-0 Exit immediately when there's no match
--sync Synchronous search for multi-staged filtering
--pre-select-n=NUM Pre-select the first n items in multi-selection mode
--pre-select-pat=REGEX
Pre-select the matched items in multi-selection mode
--pre-select-items=$'item1\\nitem2'
Pre-select the items separated by newline character
--pre-select-file=FILENAME
Pre-select the items read from file
Environment variables
SKIM_DEFAULT_COMMAND Default command to use when input is tty
SKIM_DEFAULT_OPTIONS Default options (e.g. '--ansi --regex')
You should not include other environment variables
(e.g. '-c \"$HOME/bin/ag\"')
Removed
-I replstr replace `replstr` with the selected item
Reserved (not used for now)
--extended
--literal
--cycle
--hscroll-off=COL
--filepath-word
--jump-labels=CHARS
--border
--no-bold
--info
--pointer
--marker
--phony
";
const DEFAULT_HISTORY_SIZE: usize = 1000;
//------------------------------------------------------------------------------
fn main() {
env_logger::builder().format_timestamp_nanos().init();
match real_main() {
Ok(exit_code) => std::process::exit(exit_code),
Err(err) => {
// if downstream pipe is closed, exit silently, see PR#279
if err.kind() == std::io::ErrorKind::BrokenPipe {
std::process::exit(0)
}
std::process::exit(2)
}
if let Some(level) = opts.log_level {
env_logger::builder()
.filter_level(level)
.parse_env(env_var)
.target(target)
.format(format)
.init();
} else {
env_logger::builder()
.parse_env(env_var)
.target(target)
.format(format)
.init();
}
}
#[rustfmt::skip]
fn real_main() -> Result<i32, std::io::Error> {
let mut stdout = std::io::stdout();
//------------------------------------------------------------------------------
fn main() -> Result<()> {
let mut opts = SkimOptions::from_env().unwrap_or_else(|e| {
e.exit();
});
init_logger(&opts);
let mut args = Vec::new();
// Build the options after setting the log target
opts = opts.build();
trace!("Command line: {:?}", std::env::args());
args.push(env::args().next().expect("there should be at least one arg: the application name"));
args.extend(env::var("SKIM_DEFAULT_OPTIONS")
.ok()
.and_then(|val| shlex::split(&val))
.unwrap_or_default());
for arg in env::args().skip(1) {
args.push(arg);
// Shell completion scripts
if let Some(shell) = opts.shell {
// Generate completion script directly to stdout
skim::shell::generate_completions(&shell, &mut std::io::stdout());
if opts.shell_bindings {
skim::shell::generate_key_bindings(&shell, &mut std::io::stdout())?;
}
return Ok(());
}
// Man page
if opts.man {
crate::manpage::generate(&mut std::io::stdout())?;
return Ok(());
}
//------------------------------------------------------------------------------
// parse options
let opts = App::new("sk")
.author("Jinzhou Zhang<lotabout@gmail.com>")
.arg(Arg::with_name("help").long("help").short('h'))
.arg(Arg::with_name("version").long("version").short('v'))
.arg(Arg::with_name("bind").long("bind").short('b').multiple(true).takes_value(true))
.arg(Arg::with_name("multi").long("multi").short('m').multiple(true))
.arg(Arg::with_name("no-multi").long("no-multi").multiple(true))
.arg(Arg::with_name("prompt").long("prompt").short('p').multiple(true).takes_value(true).default_value("> "))
.arg(Arg::with_name("cmd-prompt").long("cmd-prompt").multiple(true).takes_value(true).default_value("c> "))
.arg(Arg::with_name("expect").long("expect").multiple(true).takes_value(true))
.arg(Arg::with_name("tac").long("tac").multiple(true))
.arg(Arg::with_name("tiebreak").long("tiebreak").short('t').multiple(true).takes_value(true))
.arg(Arg::with_name("ansi").long("ansi").multiple(true))
.arg(Arg::with_name("exact").long("exact").short('e').multiple(true))
.arg(Arg::with_name("cmd").long("cmd").short('c').multiple(true).takes_value(true))
.arg(Arg::with_name("interactive").long("interactive").short('i').multiple(true))
.arg(Arg::with_name("query").long("query").short('q').multiple(true).takes_value(true))
.arg(Arg::with_name("cmd-query").long("cmd-query").multiple(true).takes_value(true))
.arg(Arg::with_name("regex").long("regex").multiple(true))
.arg(Arg::with_name("delimiter").long("delimiter").short('d').multiple(true).takes_value(true))
.arg(Arg::with_name("nth").long("nth").short('n').multiple(true).takes_value(true))
.arg(Arg::with_name("with-nth").long("with-nth").multiple(true).takes_value(true))
.arg(Arg::with_name("replstr").short('I').multiple(true).takes_value(true))
.arg(Arg::with_name("color").long("color").multiple(true).takes_value(true))
.arg(Arg::with_name("margin").long("margin").multiple(true).takes_value(true).default_value("0,0,0,0"))
.arg(Arg::with_name("min-height").long("min-height").multiple(true).takes_value(true).default_value("10"))
.arg(Arg::with_name("height").long("height").multiple(true).takes_value(true).default_value("100%"))
.arg(Arg::with_name("no-height").long("no-height").multiple(true))
.arg(Arg::with_name("no-clear").long("no-clear").multiple(true))
.arg(Arg::with_name("no-clear-start").long("no-clear-start").multiple(true))
.arg(Arg::with_name("no-mouse").long("no-mouse").multiple(true))
.arg(Arg::with_name("preview").long("preview").multiple(true).takes_value(true))
.arg(Arg::with_name("preview-window").long("preview-window").multiple(true).takes_value(true).default_value("right:50%"))
.arg(Arg::with_name("reverse").long("reverse").multiple(true))
.arg(Arg::with_name("algorithm").long("algo").multiple(true).takes_value(true).default_value("skim_v2"))
.arg(Arg::with_name("case").long("case").multiple(true).takes_value(true).default_value("smart"))
.arg(Arg::with_name("literal").long("literal").multiple(true))
.arg(Arg::with_name("cycle").long("cycle").multiple(true))
.arg(Arg::with_name("no-hscroll").long("no-hscroll").multiple(true))
.arg(Arg::with_name("hscroll-off").long("hscroll-off").multiple(true).takes_value(true).default_value("10"))
.arg(Arg::with_name("filepath-word").long("filepath-word").multiple(true))
.arg(Arg::with_name("jump-labels").long("jump-labels").multiple(true).takes_value(true).default_value("abcdefghijklmnopqrstuvwxyz"))
.arg(Arg::with_name("border").long("border").multiple(true))
.arg(Arg::with_name("inline-info").long("inline-info").multiple(true))
.arg(Arg::with_name("header").long("header").multiple(true).takes_value(true).default_value(""))
.arg(Arg::with_name("header-lines").long("header-lines").multiple(true).takes_value(true).default_value("0"))
.arg(Arg::with_name("tabstop").long("tabstop").multiple(true).takes_value(true).default_value("8"))
.arg(Arg::with_name("no-bold").long("no-bold").multiple(true))
.arg(Arg::with_name("history").long("history").multiple(true).takes_value(true))
.arg(Arg::with_name("cmd-history").long("cmd-history").multiple(true).takes_value(true))
.arg(Arg::with_name("history-size").long("history-size").multiple(true).takes_value(true).default_value("1000"))
.arg(Arg::with_name("cmd-history-size").long("cmd-history-size").multiple(true).takes_value(true).default_value("1000"))
.arg(Arg::with_name("print-query").long("print-query").multiple(true))
.arg(Arg::with_name("print-cmd").long("print-cmd").multiple(true))
.arg(Arg::with_name("print-score").long("print-score").multiple(true))
.arg(Arg::with_name("read0").long("read0").multiple(true))
.arg(Arg::with_name("print0").long("print0").multiple(true))
.arg(Arg::with_name("sync").long("sync").multiple(true))
.arg(Arg::with_name("extended").long("extended").short('x').multiple(true))
.arg(Arg::with_name("no-sort").long("no-sort").multiple(true))
.arg(Arg::with_name("select-1").long("select-1").short('1').multiple(true))
.arg(Arg::with_name("exit-0").long("exit-0").short('0').multiple(true))
.arg(Arg::with_name("filter").long("filter").short('f').takes_value(true).multiple(true))
.arg(Arg::with_name("layout").long("layout").multiple(true).takes_value(true).default_value("default"))
.arg(Arg::with_name("keep-right").long("keep-right").multiple(true))
.arg(Arg::with_name("skip-to-pattern").long("skip-to-pattern").multiple(true).takes_value(true).default_value(""))
.arg(Arg::with_name("pre-select-n").long("pre-select-n").multiple(true).takes_value(true).default_value("0"))
.arg(Arg::with_name("pre-select-pat").long("pre-select-pat").multiple(true).takes_value(true).default_value(""))
.arg(Arg::with_name("pre-select-items").long("pre-select-items").multiple(true).takes_value(true))
.arg(Arg::with_name("pre-select-file").long("pre-select-file").multiple(true).takes_value(true).default_value(""))
.arg(Arg::with_name("no-clear-if-empty").long("no-clear-if-empty").multiple(true))
.arg(Arg::with_name("show-cmd-error").long("show-cmd-error").multiple(true))
.get_matches_from(args);
if opts.is_present("help") {
write!(stdout, "{}", USAGE)?;
return Ok(0);
#[cfg(feature = "listen")]
if let Some(remote) = opts.remote {
let ns_name = remote
.to_ns_name::<interprocess::local_socket::GenericNamespaced>()
.unwrap();
let stream = interprocess::local_socket::Stream::connect(ns_name)?;
let mut action_chain = String::new();
loop {
action_chain.clear();
let len = std::io::stdin().read_line(&mut action_chain)?;
log::debug!("Got line {} from stdin", action_chain.trim());
if len == 0 {
break;
}
let actions = parse_action_chain(action_chain.trim())?;
for act in actions {
stream
.as_write()
.write_all(format!("{}\n", ron::ser::to_string(&act)?).as_bytes())?;
log::debug!("Sent action {act:?} to listener");
}
}
return Ok(());
}
if opts.is_present("version") {
writeln!(stdout, "{}", VERSION)?;
return Ok(0);
match sk_main(opts) {
Ok(exit_code) => std::process::exit(exit_code),
Err(err) => match err.downcast_ref::<clap::error::Error>() {
Some(e) => e.exit(),
None => Err(eyre!(err)),
},
}
}
//------------------------------------------------------------------------------
let mut options = parse_options(&opts);
let preview_window_joined = opts.values_of("preview-window").map(|x| x.collect::<Vec<_>>().join(":"));
options.preview_window = preview_window_joined.as_deref();
//------------------------------------------------------------------------------
// initialize collector
let item_reader_option = SkimItemReaderOption::default()
.ansi(opts.is_present("ansi"))
.delimiter(opts.values_of("delimiter").and_then(|vals| vals.last()).unwrap_or(""))
.with_nth(opts.values_of("with-nth").and_then(|vals| vals.last()).unwrap_or(""))
.nth(opts.values_of("nth").and_then(|vals| vals.last()).unwrap_or(""))
.read0(opts.is_present("read0"))
.show_error(opts.is_present("show-cmd-error"))
.build();
let cmd_collector = Rc::new(RefCell::new(SkimItemReader::new(item_reader_option)));
options.cmd_collector = cmd_collector.clone();
//------------------------------------------------------------------------------
// read in the history file
let fz_query_histories = opts.values_of("history").and_then(|vals| vals.last());
let cmd_query_histories = opts.values_of("cmd-history").and_then(|vals| vals.last());
let query_history = fz_query_histories.and_then(|filename| read_file_lines(filename).ok()).unwrap_or_default();
let cmd_history = cmd_query_histories.and_then(|filename| read_file_lines(filename).ok()).unwrap_or_default();
if fz_query_histories.is_some() || cmd_query_histories.is_some() {
options.query_history = &query_history;
options.cmd_history = &cmd_history;
// bind ctrl-n and ctrl-p to handle history
options.bind.insert(0, "ctrl-p:previous-history,ctrl-n:next-history");
/// Returns `None` if the popup should not open, otherwise run the popup and return the result
#[cfg(unix)]
#[allow(clippy::option_option)]
fn check_and_run_popup(opts: &SkimOptions) -> Option<Option<SkimOutput>> {
if opts.popup.is_some() && popup::check_env() {
Some(crate::popup::run_with(opts))
} else {
None
}
}
#[cfg(not(unix))]
#[allow(clippy::option_option)]
fn check_and_run_popup(_opts: &SkimOptions) -> Option<Option<SkimOutput>> {
None
}
fn sk_main(mut opts: SkimOptions) -> Result<i32> {
let reader_opts = SkimItemReaderOption::from_options(&opts);
let cmd_collector = Rc::new(RefCell::new(SkimItemReader::new(reader_opts)));
opts.cmd_collector = cmd_collector.clone() as Rc<RefCell<dyn CommandCollector>>;
let cmd_history = opts.cmd_history.clone();
let cmd_history_size = opts.cmd_history_size;
let cmd_history_file = opts.cmd_history_file.clone();
let query_history = opts.query_history.clone();
let history_size = opts.history_size;
let history_file = opts.history_file.clone();
//------------------------------------------------------------------------------
// handle pre-selection options
let pre_select_n: Option<usize> = opts.values_of("pre-select-n").and_then(|vals| vals.last()).and_then(|s| s.parse().ok());
let pre_select_pat = opts.values_of("pre-select-pat").and_then(|vals| vals.last());
let pre_select_items: Option<Vec<String>> = opts.values_of("pre-select-items").map(|vals| vals.flat_map(|m|m.split('\n')).map(|s|s.to_string()).collect());
let pre_select_file = opts.values_of("pre-select-file").and_then(|vals| vals.last());
if pre_select_n.is_some() || pre_select_pat.is_some() || pre_select_items.is_some() || pre_select_file.is_some() {
let first_n = pre_select_n.unwrap_or(0);
let pattern = pre_select_pat.unwrap_or("");
let preset_items = pre_select_items.unwrap_or_default();
let preset_file = pre_select_file.and_then(|filename| read_file_lines(filename).ok()).unwrap_or_default();
let selector = DefaultSkimSelector::default()
.first_n(first_n)
.regex(pattern)
.preset(preset_items)
.preset(preset_file);
options.selector = Some(Rc::new(selector));
}
let options = options;
//------------------------------------------------------------------------------
let bin_options = BinOptionsBuilder::default()
.filter(opts.values_of("filter").and_then(|vals| vals.last()))
.print_query(opts.is_present("print-query"))
.print_cmd(opts.is_present("print-cmd"))
.output_ending(if opts.is_present("print0") { "\0" } else { "\n" })
.build()
.expect("");
//------------------------------------------------------------------------------
// read from pipe or command
let rx_item = if atty::isnt(atty::Stream::Stdin) {
let rx_item = cmd_collector.borrow().of_bufread(BufReader::new(std::io::stdin()));
Some(rx_item)
} else {
None
};
//------------------------------------------------------------------------------
// filter mode
if opts.is_present("filter") {
return filter(&bin_options, &options, rx_item);
}
//------------------------------------------------------------------------------
let output = Skim::run_with(&options, rx_item);
if output.is_none() { // error
return Ok(135);
}
let bin_options = BinOptions::from_opts(&opts);
//------------------------------------------------------------------------------
// output
let output = output.unwrap();
if output.is_abort {
let Some(result) = check_and_run_popup(&opts).unwrap_or_else(|| {
// read from pipe or command
let rx_item = if io::stdin().is_terminal() || (opts.interactive && opts.cmd.is_some()) {
None
} else {
let rx_item = cmd_collector.borrow().of_bufread(BufReader::new(std::io::stdin()));
Some(rx_item)
};
Skim::run_with(opts, rx_item).ok()
}) else {
return Ok(135);
};
log::debug!("result: {result:?}");
if result.is_abort {
return Ok(130);
}
// output query
if bin_options.print_query {
write!(stdout, "{}{}", output.query, bin_options.output_ending)?;
}
if bin_options.print_cmd {
write!(stdout, "{}{}", output.cmd, bin_options.output_ending)?;
}
if opts.is_present("expect") {
match output.final_event {
Event::EvActAccept(Some(accept_key)) => {
write!(stdout, "{}{}", accept_key, bin_options.output_ending)?;
}
Event::EvActAccept(None) => {
write!(stdout, "{}", bin_options.output_ending)?;
}
_ => {}
}
}
for item in output.selected_items.iter() {
write!(stdout, "{}{}", item.output(), bin_options.output_ending)?;
// Output — use a large BufWriter to batch all writes into a few syscalls
// instead of one syscall per item (Rust's default LineWriter flushes on \n).
{
let stdout = io::stdout();
let mut out = BufWriter::with_capacity(1 << 20, stdout.lock());
result.write_output(&mut out, &bin_options)?;
out.flush()?;
}
//------------------------------------------------------------------------------
// write the history with latest item
if let Some(file) = fz_query_histories {
let limit = opts.values_of("history-size").and_then(|vals| vals.last())
.and_then(|size| size.parse::<usize>().ok())
.unwrap_or(DEFAULT_HISTORY_SIZE);
write_history_to_file(&query_history, &output.query, limit, file)?;
if let Some(file) = history_file {
let limit = history_size;
write_history_to_file(&query_history, &result.query, limit, &file)?;
}
if let Some(file) = cmd_query_histories {
let limit = opts.values_of("cmd-history-size").and_then(|vals| vals.last())
.and_then(|size| size.parse::<usize>().ok())
.unwrap_or(DEFAULT_HISTORY_SIZE);
write_history_to_file(&cmd_history, &output.cmd, limit, file)?;
if let Some(file) = cmd_history_file {
let limit = cmd_history_size;
write_history_to_file(&cmd_history, &result.cmd, limit, &file)?;
}
Ok(if output.selected_items.is_empty() { 1 } else { 0 })
}
fn parse_options(options: &ArgMatches) -> SkimOptions<'_> {
SkimOptionsBuilder::default()
.color(options.values_of("color").and_then(|vals| vals.last()))
.min_height(options.values_of("min-height").and_then(|vals| vals.last()))
.no_height(options.is_present("no-height"))
.height(options.values_of("height").and_then(|vals| vals.last()))
.margin(options.values_of("margin").and_then(|vals| vals.last()))
.preview(options.values_of("preview").and_then(|vals| vals.last()))
.cmd(options.values_of("cmd").and_then(|vals| vals.last()))
.query(options.values_of("query").and_then(|vals| vals.last()))
.cmd_query(options.values_of("cmd-query").and_then(|vals| vals.last()))
.interactive(options.is_present("interactive"))
.prompt(options.values_of("prompt").and_then(|vals| vals.last()))
.cmd_prompt(options.values_of("cmd-prompt").and_then(|vals| vals.last()))
.bind(
options
.values_of("bind")
.map(|x| x.collect::<Vec<_>>())
.unwrap_or_default(),
)
.expect(options.values_of("expect").map(|x| x.collect::<Vec<_>>().join(",")))
.multi(if options.is_present("no-multi") {
false
} else {
options.is_present("multi")
})
.layout(options.values_of("layout").and_then(|vals| vals.last()).unwrap_or(""))
.reverse(options.is_present("reverse"))
.no_hscroll(options.is_present("no-hscroll"))
.no_mouse(options.is_present("no-mouse"))
.no_clear(options.is_present("no-clear"))
.no_clear_start(options.is_present("no-clear-start"))
.tabstop(options.values_of("tabstop").and_then(|vals| vals.last()))
.tiebreak(options.values_of("tiebreak").map(|x| x.collect::<Vec<_>>().join(",")))
.tac(options.is_present("tac"))
.nosort(options.is_present("no-sort"))
.exact(options.is_present("exact"))
.regex(options.is_present("regex"))
.delimiter(options.values_of("delimiter").and_then(|vals| vals.last()))
.inline_info(options.is_present("inline-info"))
.header(options.values_of("header").and_then(|vals| vals.last()))
.header_lines(
options
.values_of("header-lines")
.and_then(|vals| vals.last())
.map(|s| s.parse::<usize>().unwrap_or(0))
.unwrap_or(0),
)
.layout(options.values_of("layout").and_then(|vals| vals.last()).unwrap_or(""))
.algorithm(FuzzyAlgorithm::of(
options.values_of("algorithm").and_then(|vals| vals.last()).unwrap(),
))
.case(match options.value_of("case") {
Some("smart") => CaseMatching::Smart,
Some("ignore") => CaseMatching::Ignore,
_ => CaseMatching::Respect,
})
.keep_right(options.is_present("keep-right"))
.skip_to_pattern(
options
.values_of("skip-to-pattern")
.and_then(|vals| vals.last())
.unwrap_or(""),
)
.select1(options.is_present("select-1"))
.exit0(options.is_present("exit-0"))
.sync(options.is_present("sync"))
.no_clear_if_empty(options.is_present("no-clear-if-empty"))
.build()
.unwrap()
}
fn read_file_lines(filename: &str) -> Result<Vec<String>, std::io::Error> {
let file = File::open(filename)?;
let ret = BufReader::new(file).lines().collect();
debug!("file content: {:?}", ret);
ret
Ok(i32::from(result.selected_items.is_empty()))
}
fn write_history_to_file(
@ -477,11 +208,11 @@ fn write_history_to_file(
limit: usize,
filename: &str,
) -> Result<(), std::io::Error> {
if orig_history.last().map(|l| l.as_str()) == Some(latest) {
if orig_history.last().map(String::as_str) == Some(latest) {
// no point of having at the end of the history 5x the same command...
return Ok(());
}
let additional_lines = if latest.trim().is_empty() { 0 } else { 1 };
let additional_lines = usize::from(!latest.trim().is_empty());
let start_index = if orig_history.len() + additional_lines > limit {
orig_history.len() + additional_lines - limit
} else {
@ -497,69 +228,51 @@ fn write_history_to_file(
Ok(())
}
#[derive(Builder)]
pub struct BinOptions<'a> {
filter: Option<&'a str>,
output_ending: &'a str,
print_query: bool,
print_cmd: bool,
}
#[cfg(test)]
#[cfg_attr(coverage, coverage(off))]
mod tests {
use super::*;
pub fn filter(
bin_option: &BinOptions,
options: &SkimOptions,
source: Option<SkimItemReceiver>,
) -> Result<i32, std::io::Error> {
let mut stdout = std::io::stdout();
let default_command = match env::var("SKIM_DEFAULT_COMMAND").as_ref().map(String::as_ref) {
Ok("") | Err(_) => "find .".to_owned(),
Ok(val) => val.to_owned(),
};
let query = bin_option.filter.unwrap_or("");
let cmd = options.cmd.unwrap_or(&default_command);
// output query
if bin_option.print_query {
write!(stdout, "{}{}", query, bin_option.output_ending)?;
fn read(path: &std::path::Path) -> String {
std::fs::read_to_string(path).unwrap_or_default()
}
if bin_option.print_cmd {
write!(stdout, "{}{}", cmd, bin_option.output_ending)?;
#[test]
fn write_history_appends_latest_entry() {
let dir = tempfile::tempdir().unwrap();
let file = dir.path().join("hist");
let file_str = file.to_str().unwrap();
write_history_to_file(&["a".to_string(), "b".to_string()], "c", 10, file_str).unwrap();
assert_eq!(read(&file), "a\nb\nc");
}
//------------------------------------------------------------------------------
// matcher
let engine_factory: Box<dyn MatchEngineFactory> = if options.regex {
Box::new(RegexEngineFactory::builder())
} else {
let fuzzy_engine_factory = ExactOrFuzzyEngineFactory::builder()
.fuzzy_algorithm(options.algorithm)
.exact_mode(options.exact)
.build();
Box::new(AndOrEngineFactory::new(fuzzy_engine_factory))
};
#[test]
fn write_history_skips_duplicate_of_last() {
let dir = tempfile::tempdir().unwrap();
let file = dir.path().join("hist");
let file_str = file.to_str().unwrap();
// The latest equals the last entry → nothing is written, no file created.
write_history_to_file(&["a".to_string(), "b".to_string()], "b", 10, file_str).unwrap();
assert!(!file.exists());
}
let engine = engine_factory.create_engine_with_case(query, options.case);
#[test]
fn write_history_truncates_to_limit() {
let dir = tempfile::tempdir().unwrap();
let file = dir.path().join("hist");
let file_str = file.to_str().unwrap();
// limit 2 with 3 existing + 1 new keeps only the newest entries.
write_history_to_file(&["a".to_string(), "b".to_string(), "c".to_string()], "d", 2, file_str).unwrap();
assert_eq!(read(&file), "c\nd");
}
//------------------------------------------------------------------------------
// start
let components_to_stop = Arc::new(AtomicUsize::new(0));
let stream_of_item = source.unwrap_or_else(|| {
let cmd_collector = options.cmd_collector.clone();
let (ret, _control) = cmd_collector.borrow_mut().invoke(cmd, components_to_stop);
ret
});
let mut num_matched = 0;
stream_of_item
.into_iter()
.filter_map(|item| engine.match_item(item.clone()).map(|result| (item, result)))
.try_for_each(|(item, _match_result)| {
num_matched += 1;
write!(stdout, "{}{}", item.output(), bin_option.output_ending)
})?;
Ok(if num_matched == 0 { 1 } else { 0 })
#[test]
fn write_history_empty_latest_does_not_count_towards_limit() {
let dir = tempfile::tempdir().unwrap();
let file = dir.path().join("hist");
let file_str = file.to_str().unwrap();
// An empty latest adds 0 to the length, so no truncation occurs at limit 3.
write_history_to_file(&["a".to_string(), "b".to_string(), "c".to_string()], "", 3, file_str).unwrap();
assert_eq!(read(&file), "a\nb\nc\n");
}
}

423
src/binds.rs Normal file
View file

@ -0,0 +1,423 @@
//! Key binding configuration and parsing.
//!
//! This module provides utilities for parsing and managing keyboard shortcuts
//! and their associated actions in skim.
use std::collections::HashMap;
use std::ops::{Deref, DerefMut};
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use eyre::{Result, eyre};
use crate::tui::actions::{self, Action};
/// Synthetic events that skim fires internally and that can be bound to actions
/// via the keymap, exactly like a real key press.
///
/// The keymap is keyed by crossterm's [`KeyEvent`], which cannot express
/// "the query changed" or "reading finished" directly. Each variant is
/// therefore represented *transparently* as a reserved function-key code in the
/// high-`F` range (`F(248)``F(255)`) that no real terminal ever emits.
/// Giving these reserved codes named variants keeps them in one place instead
/// of scattering magic function-key literals across the codebase, and lets
/// [`parse_key`] accept every friendly event name.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum SkimEvent {
/// Fired once, when skim has started up and entered its event loop.
Start,
/// Fired when the reader finishes producing items (once per read; a
/// `reload` starts a new read and fires it again).
Load,
/// Fired whenever the query changes.
Change,
/// Fired when filtering for the current query completes and the result
/// list is ready.
Result,
/// Fired when the focused item changes (cursor movement or a result update).
Focus,
/// Fired when a completed search yields no matches.
Zero,
/// Fired when a completed search yields exactly one match.
One,
/// Fired after two left mouse-button presses no more than 500 ms apart.
DoubleClick,
}
impl SkimEvent {
/// The reserved [`KeyCode`] used to route this event through the keymap.
#[must_use]
pub const fn key_code(self) -> KeyCode {
match self {
SkimEvent::Change => KeyCode::F(255),
SkimEvent::Start => KeyCode::F(254),
SkimEvent::Load => KeyCode::F(253),
SkimEvent::Result => KeyCode::F(252),
SkimEvent::Focus => KeyCode::F(251),
SkimEvent::Zero => KeyCode::F(250),
SkimEvent::One => KeyCode::F(249),
SkimEvent::DoubleClick => KeyCode::F(248),
}
}
/// The reserved [`KeyEvent`] used to route this event through the keymap.
#[must_use]
pub const fn key_event(self) -> KeyEvent {
KeyEvent::new(self.key_code(), KeyModifiers::NONE)
}
/// Parses an event name (`start`, `load`, `change`) into a [`SkimEvent`].
///
/// Returns `None` if the name is not a recognised event.
#[must_use]
pub fn from_name(name: &str) -> Option<Self> {
match name {
"start" => Some(SkimEvent::Start),
"load" => Some(SkimEvent::Load),
"change" => Some(SkimEvent::Change),
"result" => Some(SkimEvent::Result),
"focus" => Some(SkimEvent::Focus),
"zero" => Some(SkimEvent::Zero),
"one" => Some(SkimEvent::One),
"double-click" => Some(SkimEvent::DoubleClick),
_ => None,
}
}
}
impl From<SkimEvent> for KeyEvent {
fn from(event: SkimEvent) -> Self {
event.key_event()
}
}
/// A map of key events to their associated actions
#[derive(Clone, Debug)]
pub struct KeyMap(pub HashMap<KeyEvent, Vec<Action>>);
impl Deref for KeyMap {
type Target = HashMap<KeyEvent, Vec<Action>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for KeyMap {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<&str> for KeyMap {
fn from(value: &str) -> Self {
parse_keymaps(split_top_level(value, ',').into_iter())
}
}
impl Default for KeyMap {
fn default() -> Self {
get_default_key_map()
}
}
impl KeyMap {
/// Adds keymaps from a comma-separated string.
pub(crate) fn add_keymaps_str(&mut self, source: &str) {
self.add_keymaps(split_top_level(source, ',').into_iter());
}
/// Adds keymaps from the source, parsing them using `parse_keymap`
pub fn add_keymaps<'a, T>(&mut self, source: T)
where
T: Iterator<Item = &'a str>,
{
for map in source {
if let Ok((key, action_chain)) = parse_keymap(map) {
self.bind(key, action_chain)
.unwrap_or_else(|err| debug!("Failed to bind key {map}: {err}"));
} else {
debug!("Failed to parse key: {map}");
}
}
}
fn bind(&mut self, key: &str, action_chain: Vec<Action>) -> Result<()> {
let key = parse_key(key)?;
// remove the key for existing keymap;
let _ = self.remove(&key);
self.entry(key).or_insert(action_chain);
Ok(())
}
}
/// Returns the default key bindings for skim
#[rustfmt::skip]
#[must_use]
pub fn get_default_key_map() -> KeyMap {
let mut ret = HashMap::new();
ret.insert(KeyEvent::new(KeyCode::Down, KeyModifiers::NONE), vec![Action::Down(1)]);
ret.insert(KeyEvent::new(KeyCode::Up, KeyModifiers::NONE), vec![Action::Up(1)]);
ret.insert(KeyEvent::new(KeyCode::PageUp, KeyModifiers::NONE), vec![Action::PageUp(1)]);
ret.insert(KeyEvent::new(KeyCode::PageDown, KeyModifiers::NONE), vec![Action::PageDown(1)]);
ret.insert(KeyEvent::new(KeyCode::End, KeyModifiers::NONE), vec![Action::EndOfLine]);
ret.insert(KeyEvent::new(KeyCode::Home, KeyModifiers::NONE), vec![Action::BeginningOfLine]);
ret.insert(KeyEvent::new(KeyCode::Delete, KeyModifiers::NONE), vec![Action::DeleteChar]);
ret.insert(KeyEvent::new(KeyCode::Tab, KeyModifiers::NONE), vec![Action::Toggle, Action::Down(1)]);
ret.insert(KeyEvent::new(KeyCode::BackTab, KeyModifiers::all()), vec![Action::Toggle, Action::Up(1)]);
ret.insert(KeyEvent::new(KeyCode::Esc, KeyModifiers::NONE), vec![Action::Abort]);
ret.insert(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE), vec![Action::Accept(None)]);
ret.insert(KeyEvent::new(KeyCode::Left, KeyModifiers::NONE), vec![Action::BackwardChar]);
ret.insert(KeyEvent::new(KeyCode::Right, KeyModifiers::NONE), vec![Action::ForwardChar]);
ret.insert(KeyEvent::new(KeyCode::Backspace, KeyModifiers::NONE), vec![Action::BackwardDeleteChar]);
ret.insert(SkimEvent::DoubleClick.key_event(), vec![Action::Accept(None)]);
ret.insert(KeyEvent::new(KeyCode::Left, KeyModifiers::SHIFT), vec![Action::BackwardWord]);
ret.insert(KeyEvent::new(KeyCode::Right, KeyModifiers::SHIFT), vec![Action::ForwardWord]);
ret.insert(KeyEvent::new(KeyCode::Up, KeyModifiers::SHIFT), vec![Action::PreviewUp(1)]);
ret.insert(KeyEvent::new(KeyCode::Down, KeyModifiers::SHIFT), vec![Action::PreviewDown(1)]);
ret.insert(KeyEvent::new(KeyCode::Tab, KeyModifiers::SHIFT), vec![Action::Toggle, Action::Up(1)]);
ret.insert(KeyEvent::new(KeyCode::BackTab, KeyModifiers::SHIFT), vec![Action::Toggle, Action::Up(1)]);
ret.insert(KeyEvent::new(KeyCode::Home, KeyModifiers::SHIFT), vec![Action::BeginningOfLine]);
ret.insert(KeyEvent::new(KeyCode::Left, KeyModifiers::CONTROL), vec![Action::BackwardWord]);
ret.insert(KeyEvent::new(KeyCode::Right, KeyModifiers::CONTROL), vec![Action::ForwardWord]);
ret.insert(KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL), vec![Action::BeginningOfLine]);
ret.insert(KeyEvent::new(KeyCode::Char('b'), KeyModifiers::CONTROL), vec![Action::BackwardChar]);
ret.insert(KeyEvent::new(KeyCode::Char('c'), KeyModifiers::CONTROL), vec![Action::Abort]);
ret.insert(KeyEvent::new(KeyCode::Char('d'), KeyModifiers::CONTROL), vec![Action::Abort]);
ret.insert(KeyEvent::new(KeyCode::Char('e'), KeyModifiers::CONTROL), vec![Action::EndOfLine]);
ret.insert(KeyEvent::new(KeyCode::Char('f'), KeyModifiers::CONTROL), vec![Action::ForwardChar]);
ret.insert(KeyEvent::new(KeyCode::Char('g'), KeyModifiers::CONTROL), vec![Action::Abort]);
ret.insert(KeyEvent::new(KeyCode::Char('h'), KeyModifiers::CONTROL), vec![Action::BackwardDeleteChar]);
ret.insert(KeyEvent::new(KeyCode::Char('j'), KeyModifiers::CONTROL), vec![Action::Down(1)]);
ret.insert(KeyEvent::new(KeyCode::Char('k'), KeyModifiers::CONTROL), vec![Action::Up(1)]);
ret.insert(KeyEvent::new(KeyCode::Char('l'), KeyModifiers::CONTROL), vec![Action::ClearScreen]);
ret.insert(KeyEvent::new(KeyCode::Char('n'), KeyModifiers::CONTROL), vec![Action::Down(1)]);
ret.insert(KeyEvent::new(KeyCode::Char('p'), KeyModifiers::CONTROL), vec![Action::Up(1)]);
ret.insert(KeyEvent::new(KeyCode::Char('q'), KeyModifiers::CONTROL), vec![Action::ToggleInteractive]);
ret.insert(KeyEvent::new(KeyCode::Char('r'), KeyModifiers::CONTROL), vec![Action::RotateMode]);
ret.insert(KeyEvent::new(KeyCode::Char('u'), KeyModifiers::CONTROL), vec![Action::UnixLineDiscard]);
ret.insert(KeyEvent::new(KeyCode::Char('w'), KeyModifiers::CONTROL), vec![Action::UnixWordRubout]);
ret.insert(KeyEvent::new(KeyCode::Char('y'), KeyModifiers::CONTROL), vec![Action::Yank]);
ret.insert(KeyEvent::new(KeyCode::Backspace, KeyModifiers::ALT), vec![Action::BackwardKillWord]);
ret.insert(KeyEvent::new(KeyCode::Char('b'), KeyModifiers::ALT), vec![Action::BackwardWord]);
ret.insert(KeyEvent::new(KeyCode::Char('d'), KeyModifiers::ALT), vec![Action::KillWord]);
ret.insert(KeyEvent::new(KeyCode::Char('f'), KeyModifiers::ALT), vec![Action::ForwardWord]);
ret.insert(KeyEvent::new(KeyCode::Char('h'), KeyModifiers::ALT), vec![Action::ScrollLeft(1)]);
ret.insert(KeyEvent::new(KeyCode::Char('l'), KeyModifiers::ALT), vec![Action::ScrollRight(1)]);
KeyMap(ret)
}
/// Parses a key str into a crossterm `KeyEvent`.
///
/// In addition to keyboard names, accepts all names recognized by
/// [`SkimEvent::from_name`], including `change`, `start`, and `double-click`.
///
/// # Errors
/// Returns an error if the key string is empty, contains an unknown modifier,
/// or does not correspond to a recognised key name.
pub fn parse_key(key: &str) -> Result<KeyEvent> {
if key.is_empty() {
return Err(eyre!("Cannot parse empty key"));
}
if let Some(event) = SkimEvent::from_name(key) {
return Ok(event.key_event());
}
let parts = key.split('-').collect::<Vec<&str>>();
let mut mods = KeyModifiers::NONE;
if parts.len() > 1 {
let mod_strs = &parts[..parts.len() - 1];
for mod_str in mod_strs {
mods |= match *mod_str {
"ctrl" => KeyModifiers::CONTROL,
"alt" => KeyModifiers::ALT,
"shift" => KeyModifiers::SHIFT,
s => return Err(eyre!("Failed to parse {} as key modifier", s)),
}
}
}
let key = parts.last().unwrap_or(&"").to_string();
let keycode: KeyCode;
if key.len() == 1 {
let char = key.chars().next().unwrap_or_default();
if char.is_uppercase() {
mods |= KeyModifiers::SHIFT;
keycode = KeyCode::Char(char.to_ascii_lowercase());
} else {
keycode = KeyCode::Char(char);
}
} else if let Some(f) = key.strip_prefix('f')
&& let Ok(f_index) = f.parse::<u8>()
{
// A function key like `f10`. If the suffix isn't numeric (e.g. `focus`,
// `first`), fall through to the named-key / event matching below.
keycode = KeyCode::F(f_index);
} else {
keycode = match key.as_str() {
"space" => KeyCode::Char(' '),
"enter" => KeyCode::Enter,
"bspace" | "bs" => KeyCode::Backspace,
"up" => KeyCode::Up,
"down" => KeyCode::Down,
"left" => KeyCode::Left,
"right" => KeyCode::Right,
"tab" => KeyCode::Tab,
"btab" => KeyCode::BackTab,
"esc" => KeyCode::Esc,
"home" => KeyCode::Home,
"end" => KeyCode::End,
"pgup" => KeyCode::PageUp,
"pgdown" => KeyCode::PageDown,
s => match SkimEvent::from_name(s) {
Some(event) => event.key_code(),
None => return Err(eyre!("Unknown key {}", s)),
},
}
}
debug!("parsed key {keycode:?} and mods {mods:?}");
Ok(KeyEvent::new(keycode, mods))
}
/// Parse an iterator of keymaps into a `KeyMap`
pub fn parse_keymaps<'a, T>(maps: T) -> KeyMap
where
T: Iterator<Item = &'a str>,
{
let mut res = KeyMap::default();
res.add_keymaps(maps);
res
}
pub(crate) fn split_top_level(value: &str, separator: char) -> Vec<&str> {
let mut depth = 0_u32;
let mut start = 0;
let mut parts = Vec::new();
for (index, ch) in value.char_indices() {
match ch {
'(' => depth += 1,
')' => depth = depth.saturating_sub(1),
_ if ch == separator && depth == 0 => {
parts.push(&value[start..index]);
start = index + ch.len_utf8();
}
_ => {}
}
}
parts.push(&value[start..]);
parts
}
/// Parses follow-up action bindings from raw `--bind` specs.
///
/// Any action can be bound as if it were an event: when the "key" of a bind is
/// not a real key but is a known action name, the bound chain becomes a
/// *follow-up* that runs right after that action. For example `reload:first`
/// queues `first` immediately after a `reload`. The returned map is keyed by the
/// action's canonical name (see [`Action::name`](crate::tui::actions::Action::name)),
/// so it can be looked up directly from the action that just ran.
///
/// Keys take precedence: if the "key" resolves to a real key it is left to the
/// key map, so a name shared by a key and an action (e.g. `up`) always binds the
/// key. To target the action in that case, prefix it with `act-` (`act-up`).
#[must_use]
pub fn parse_action_binds<'a, T>(maps: T) -> HashMap<String, Vec<Action>>
where
T: Iterator<Item = &'a str>,
{
let mut res = HashMap::new();
for map in maps {
let Some((key, chain)) = map.split_once(':') else {
continue;
};
// Keys win: anything that parses as a real key is not an action trigger.
if parse_key(key).is_ok() {
continue;
}
let Some(name) = action_trigger_name(key) else {
debug!("Ignoring bind `{map}`: `{key}` is neither a key nor an action");
continue;
};
match parse_action_chain(chain) {
Ok(actions) => {
res.insert(name.to_string(), actions);
}
Err(err) => debug!("Ignoring bind `{map}`: invalid action chain `{chain}`: {err}"),
}
}
res
}
/// Resolves a bind trigger to the canonical name of the action it targets
/// (see [`Action::name`](crate::tui::actions::Action::name)). `act-<name>`
/// explicitly targets the action `<name>`, even when `<name>` is also a key;
/// without the prefix, a bare action name works too. Returns `None` if the
/// name is not a known action.
///
/// This performs pure name resolution: callers that want "keys win" semantics
/// (e.g. [`parse_action_binds`]) must check [`parse_key`] first.
pub(crate) fn action_trigger_name(trigger: &str) -> Option<&'static str> {
let action_name = trigger.strip_prefix("act-").unwrap_or(trigger);
// Some actions require an argument when executed, but their canonical
// name is still valid as a trigger. `()` supplies the parser's empty
// placeholder solely for name validation.
let action = actions::parse_action(action_name).or_else(|| actions::parse_action(&format!("{action_name}()")))?;
Some(action.name())
}
/// Parses an action chain, separated by '+'s into the corresponding actions
///
/// # Errors
/// Returns an error if the action chain is empty or contains only unknown actions.
pub fn parse_action_chain(action_chain: &str) -> Result<Vec<Action>> {
let mut actions: Vec<Action> = vec![];
let mut split = split_top_level(action_chain, '+').into_iter();
while let Some(mut s) = split.next().map(String::from) {
if (s.starts_with("if-") || s.ends_with('{'))
&& let Some(otherwise) = split.next()
{
s += &(String::from("+") + otherwise);
}
if let Some(act) = actions::parse_action(&s) {
actions.push(act);
}
}
if actions.is_empty() {
Err(eyre!("Empty action chain or unknown action `{}`", action_chain))
} else {
Ok(actions)
}
}
/// Parse a single keymap and return the key and action(s)
///
/// # Errors
/// Returns an error if the string is empty, missing a colon separator, or the
/// action chain cannot be parsed.
pub fn parse_keymap(key_action: &str) -> Result<(&str, Vec<Action>)> {
if key_action.is_empty() {
return Err(eyre!("Got an empty keybind, skipping"));
}
debug!("got key_action: {key_action:?}");
let (key, action_chain) = key_action
.split_once(':')
.ok_or(eyre!("Failed to parse {} as key and action", key_action))?;
debug!("parsed key_action: {key:?}: {action_chain:?}");
Ok((key, parse_action_chain(action_chain)?))
}
#[cfg(test)]
#[path = "binds_tests.rs"]
mod tests;

309
src/binds_tests.rs Normal file
View file

@ -0,0 +1,309 @@
use super::*;
use actions::Action::*;
#[test]
fn test_parse_action_chain() {
let parsed = parse_action_chain(
"execute-silent:1 {}+execute-silent:2 {+}+execute-silent:3 {+n}+reload+if-query-empty:reload+up",
);
assert!(parsed.is_ok());
let res = parsed.unwrap();
assert_eq!(
res,
vec![
ExecuteSilent("1 {}".into()),
ExecuteSilent("2 {+}".into()),
ExecuteSilent("3 {+n}".into()),
Reload(None),
IfQueryEmpty("reload".into(), Some("up".into())),
]
);
}
#[test]
fn test_parse_key() {
assert_eq!(
parse_key("a").unwrap(),
KeyEvent::new(KeyCode::Char('a'), KeyModifiers::empty())
);
assert_eq!(
parse_key("A").unwrap(),
KeyEvent::new(KeyCode::Char('a'), KeyModifiers::SHIFT)
);
assert_eq!(
parse_key("alt-a").unwrap(),
KeyEvent::new(KeyCode::Char('a'), KeyModifiers::ALT)
);
assert_eq!(
parse_key("alt-A").unwrap(),
KeyEvent::new(KeyCode::Char('a'), KeyModifiers::ALT | KeyModifiers::SHIFT)
);
assert_eq!(
parse_key("alt-shift-a").unwrap(),
KeyEvent::new(KeyCode::Char('a'), KeyModifiers::ALT | KeyModifiers::SHIFT)
);
assert_eq!(
parse_key("ctrl-a").unwrap(),
KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL)
);
assert_eq!(
parse_key("ctrl-A").unwrap(),
KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL | KeyModifiers::SHIFT)
);
assert_eq!(
parse_key("ctrl-shift-a").unwrap(),
KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL | KeyModifiers::SHIFT)
);
assert_eq!(
parse_key("f10").unwrap(),
KeyEvent::new(KeyCode::F(10), KeyModifiers::empty())
);
assert_eq!(
parse_key("space").unwrap(),
KeyEvent::new(KeyCode::Char(' '), KeyModifiers::empty())
);
assert_eq!(
parse_key("enter").unwrap(),
KeyEvent::new(KeyCode::Enter, KeyModifiers::empty())
);
assert_eq!(
parse_key("bspace").unwrap(),
KeyEvent::new(KeyCode::Backspace, KeyModifiers::empty())
);
assert_eq!(
parse_key("bs").unwrap(),
KeyEvent::new(KeyCode::Backspace, KeyModifiers::empty())
);
assert_eq!(
parse_key("up").unwrap(),
KeyEvent::new(KeyCode::Up, KeyModifiers::empty())
);
assert_eq!(
parse_key("down").unwrap(),
KeyEvent::new(KeyCode::Down, KeyModifiers::empty())
);
assert_eq!(
parse_key("left").unwrap(),
KeyEvent::new(KeyCode::Left, KeyModifiers::empty())
);
assert_eq!(
parse_key("right").unwrap(),
KeyEvent::new(KeyCode::Right, KeyModifiers::empty())
);
assert_eq!(
parse_key("tab").unwrap(),
KeyEvent::new(KeyCode::Tab, KeyModifiers::empty())
);
assert_eq!(
parse_key("btab").unwrap(),
KeyEvent::new(KeyCode::BackTab, KeyModifiers::empty())
);
assert_eq!(
parse_key("esc").unwrap(),
KeyEvent::new(KeyCode::Esc, KeyModifiers::empty())
);
assert_eq!(
parse_key("home").unwrap(),
KeyEvent::new(KeyCode::Home, KeyModifiers::empty())
);
assert_eq!(
parse_key("end").unwrap(),
KeyEvent::new(KeyCode::End, KeyModifiers::empty())
);
assert_eq!(
parse_key("pgup").unwrap(),
KeyEvent::new(KeyCode::PageUp, KeyModifiers::empty())
);
assert_eq!(
parse_key("pgdown").unwrap(),
KeyEvent::new(KeyCode::PageDown, KeyModifiers::empty())
);
assert_eq!(
parse_key("change").unwrap(),
KeyEvent::new(KeyCode::F(255), KeyModifiers::empty())
);
}
#[test]
fn skim_event_name_roundtrip() {
// Named events resolve to distinct reserved key events and back.
for (name, event) in [
("start", SkimEvent::Start),
("load", SkimEvent::Load),
("change", SkimEvent::Change),
("result", SkimEvent::Result),
("focus", SkimEvent::Focus),
("zero", SkimEvent::Zero),
("one", SkimEvent::One),
("double-click", SkimEvent::DoubleClick),
] {
assert_eq!(SkimEvent::from_name(name), Some(event));
assert_eq!(parse_key(name).unwrap(), KeyEvent::from(event));
}
// Unknown names are not events.
assert_eq!(SkimEvent::from_name("nope"), None);
// A binding referencing an event name resolves to an action chain.
let keymap = KeyMap::from("start:first,load:last,change:first,double-click:accept");
for event in [
SkimEvent::Start,
SkimEvent::Load,
SkimEvent::Change,
SkimEvent::DoubleClick,
] {
assert!(keymap.get(&event.key_event()).is_some());
}
}
#[test]
fn double_click_accepts_by_default() {
assert_eq!(
get_default_key_map().get(&SkimEvent::DoubleClick.key_event()),
Some(&vec![Accept(None)])
);
}
#[test]
fn parse_key_error_cases() {
// Empty input.
assert!(parse_key("").is_err());
// Unknown modifier.
assert!(parse_key("hyper-a").is_err());
// Unknown key name.
assert!(parse_key("notakey").is_err());
// Invalid function-key index.
assert!(parse_key("fXY").is_err());
}
#[test]
fn keymap_from_str_parses_bindings() {
let keymap = KeyMap::from("ctrl-a:abort,enter:accept");
// Both keys resolve to action chains.
assert!(keymap.get(&parse_key("ctrl-a").unwrap()).is_some());
assert!(keymap.get(&parse_key("enter").unwrap()).is_some());
}
#[test]
fn keymap_from_str_preserves_nested_bind_separators() {
let keymap = KeyMap::from("ctrl-z:bind(ctrl-x:abort+up),ctrl-w:unbind(ctrl-x,ctrl-y)");
assert_eq!(
keymap.get(&parse_key("ctrl-z").unwrap()),
Some(&vec![Bind("ctrl-x:abort+up".into())])
);
assert_eq!(
keymap.get(&parse_key("ctrl-w").unwrap()),
Some(&vec![Unbind("ctrl-x,ctrl-y".into())])
);
}
#[test]
fn parse_action_chain_preserves_nested_bind_chain() {
assert_eq!(
parse_action_chain("bind(ctrl-x:abort+up)").unwrap(),
vec![Bind("ctrl-x:abort+up".into())]
);
}
#[test]
fn parse_keymaps_collects_iterator() {
let keymap = parse_keymaps(["ctrl-x:abort", "up:up"].into_iter());
assert!(keymap.get(&parse_key("ctrl-x").unwrap()).is_some());
}
#[test]
fn action_binds_key_wins_over_action() {
// A bare action name that is not a key binds the action as a follow-up.
let binds = parse_action_binds(["first:last"].into_iter());
assert_eq!(binds.get("first"), Some(&vec![Last]));
// A name that is also a real key (`up`) is left to the key map, so it is
// NOT registered as an action trigger.
let binds = parse_action_binds(["up:down"].into_iter());
assert!(!binds.contains_key("up"));
// `act-` forces the action interpretation even for a key-shaped name.
let binds = parse_action_binds(["act-up:down"].into_iter());
assert_eq!(binds.get("up"), Some(&vec![Down(1)]));
// Actions that require arguments when executed are still valid triggers.
let binds = parse_action_binds(
[
"act-add-char:last",
"act-execute:last",
"act-execute-silent:last",
"act-set-preview-cmd:last",
"act-set-query:last",
]
.into_iter(),
);
for name in ["add-char", "execute", "execute-silent", "set-preview-cmd", "set-query"] {
assert_eq!(binds.get(name), Some(&vec![Last]), "missing trigger `{name}`");
}
}
#[test]
fn action_binds_parse_suppress_chain() {
// `suppress` is parsed like any other action and kept in the chain.
let binds = parse_action_binds(["act-up:suppress+down"].into_iter());
assert_eq!(binds.get("up"), Some(&vec![Suppress, Down(1)]));
}
#[test]
fn action_trigger_name_resolves_actions() {
// `act-` targets the action explicitly; a bare action name works too.
assert_eq!(action_trigger_name("act-up"), Some("up"));
assert_eq!(action_trigger_name("first"), Some("first"));
// Argument-taking actions resolve by name alone.
assert_eq!(action_trigger_name("act-execute"), Some("execute"));
// Unknown names are not triggers.
assert_eq!(action_trigger_name("nope"), None);
assert_eq!(action_trigger_name("act-nope"), None);
}
#[test]
fn action_binds_invalid_specs_are_skipped() {
// An unknown trigger and an invalid chain are both dropped (with a debug
// log) without affecting valid binds in the same list.
let binds = parse_action_binds(["nokey:last", "act-up:not-an-action", "first:last"].into_iter());
assert_eq!(binds.len(), 1);
assert_eq!(binds.get("first"), Some(&vec![Last]));
}
#[test]
fn action_binds_split_top_level_preserves_commas_in_args() {
// A single `--bind` spec containing a comma inside `(...)` must not be split
// there: `options.rs` uses `split_top_level(part, ',')` so the comma stays
// part of the action argument instead of garbling the follow-up binding.
let spec = "act-up:execute(echo a,b),first:last";
let binds = parse_action_binds(split_top_level(spec, ',').into_iter());
assert_eq!(binds.get("up"), Some(&vec![Execute(String::from("echo a,b"))]));
assert_eq!(binds.get("first"), Some(&vec![Last]));
}
#[test]
fn parse_action_chain_unknown_is_error() {
assert!(parse_action_chain("not-a-real-action").is_err());
}
#[test]
fn parse_action_chain_accept_execute_reload_with_args() {
// `accept:hello`, `execute(...)` and `reload(...)` parse to the expected actions.
assert_eq!(
parse_action_chain("accept:hello").unwrap(),
vec![Accept(Some("hello".into()))]
);
assert_eq!(
parse_action_chain("execute(echo foo)").unwrap(),
vec![Execute("echo foo".into())]
);
assert_eq!(
parse_action_chain("reload(echo hello)").unwrap(),
vec![Reload(Some("echo hello".into()))]
);
assert_eq!(parse_action_chain("reload").unwrap(), vec![Reload(None)]);
}

View file

@ -28,10 +28,10 @@ impl MatchAllEngine {
}
impl MatchEngine for MatchAllEngine {
fn match_item(&self, item: Arc<dyn SkimItem>) -> Option<MatchResult> {
let item_len = item.text().len();
fn match_item(&self, item: &dyn SkimItem) -> Option<MatchResult> {
let item_text = item.text();
Some(MatchResult {
rank: self.rank_builder.build_rank(0, 0, 0, item_len),
rank: self.rank_builder.build_rank(0, 0, 0, &item_text),
matched_range: MatchRange::ByteRange(0, 0),
})
}
@ -42,3 +42,30 @@ impl Display for MatchAllEngine {
write!(f, "Noop")
}
}
#[cfg(test)]
#[cfg_attr(coverage, coverage(off))]
mod tests {
use super::*;
#[test]
fn matches_every_item_with_empty_range() {
let engine = MatchAllEngine::builder().build();
let result = engine.match_item(&"anything".to_string()).unwrap();
assert_eq!(result.matched_range, MatchRange::ByteRange(0, 0));
}
#[test]
fn rank_builder_override_is_used() {
let engine = MatchAllEngine::builder()
.rank_builder(Arc::new(RankBuilder::default()))
.build();
assert!(engine.match_item(&"x".to_string()).is_some());
}
#[test]
fn display_is_noop() {
let engine = MatchAllEngine::builder().build();
assert_eq!(format!("{engine}"), "Noop");
}
}

View file

@ -1,6 +1,6 @@
use std::fmt::{Display, Error, Formatter};
use std::sync::Arc;
use crate::fuzzy_matcher::MatchIndices;
use crate::{MatchEngine, MatchRange, MatchResult, SkimItem};
//------------------------------------------------------------------------------
@ -25,15 +25,14 @@ impl OrEngine {
}
impl MatchEngine for OrEngine {
fn match_item(&self, item: Arc<dyn SkimItem>) -> Option<MatchResult> {
for engine in &self.engines {
let result = engine.match_item(Arc::clone(&item));
if result.is_some() {
return result;
}
}
fn match_item(&self, item: &dyn SkimItem) -> Option<MatchResult> {
let result = self
.engines
.iter()
.map(|e| e.match_item(item))
.max_by_key(|res| res.as_ref().map(|matched| matched.rank.score));
None
result?
}
}
@ -44,7 +43,7 @@ impl Display for OrEngine {
"(Or: {})",
self.engines
.iter()
.map(|e| format!("{}", e))
.map(|e| format!("{e}"))
.collect::<Vec<_>>()
.join(", ")
)
@ -71,21 +70,32 @@ impl AndEngine {
self
}
fn merge_matched_items(&self, items: Vec<MatchResult>, text: &str) -> MatchResult {
let rank = items[0].rank;
let mut ranges = vec![];
fn merge_matched_items(items: Vec<MatchResult>, text: &str) -> MatchResult {
let mut ranges = MatchIndices::new();
let mut rank = crate::Rank {
score: 0,
begin: i32::MAX,
end: i32::MIN,
..items[0].rank
};
for item in items {
match item.matched_range {
MatchRange::ByteRange(..) => {
ranges.extend(item.range_char_indices(text));
}
MatchRange::CharRange(start, end) => {
ranges.extend(start..end);
}
MatchRange::Chars(vec) => {
ranges.extend(vec.iter());
ranges.extend(vec.iter().copied());
}
}
rank.score = rank.score.saturating_add(item.rank.score);
rank.begin = rank.begin.min(item.rank.begin);
rank.end = rank.end.max(item.rank.end);
}
ranges.sort();
ranges.sort_unstable();
ranges.dedup();
MatchResult {
rank,
@ -95,18 +105,22 @@ impl AndEngine {
}
impl MatchEngine for AndEngine {
fn match_item(&self, item: Arc<dyn SkimItem>) -> Option<MatchResult> {
// mock
fn match_item(&self, item: &dyn SkimItem) -> Option<MatchResult> {
// Fast path: single sub-engine — skip merge entirely.
if self.engines.len() == 1 {
return self.engines[0].match_item(item);
}
let mut results = vec![];
for engine in &self.engines {
let result = engine.match_item(Arc::clone(&item))?;
let result = engine.match_item(item)?;
results.push(result);
}
if results.is_empty() {
None
} else {
Some(self.merge_matched_items(results, &item.text()))
Some(Self::merge_matched_items(results, &item.text()))
}
}
}
@ -118,9 +132,13 @@ impl Display for AndEngine {
"(And: {})",
self.engines
.iter()
.map(|e| format!("{}", e))
.map(|e| format!("{e}"))
.collect::<Vec<_>>()
.join(", ")
)
}
}
#[cfg(test)]
#[path = "andor_tests.rs"]
mod tests;

101
src/engine/andor_tests.rs Normal file
View file

@ -0,0 +1,101 @@
use super::*;
use crate::engine::exact::{ExactEngine, ExactMatchingParam};
fn exact(query: &str) -> Box<dyn MatchEngine> {
Box::new(ExactEngine::builder(query, ExactMatchingParam::default()).build())
}
#[test]
fn or_engine_matches_if_any_subengine_matches() {
let engine = OrEngine::builder().engines(vec![exact("foo"), exact("zzz")]).build();
assert!(engine.match_item(&"a foo bar".to_string()).is_some());
}
#[test]
fn or_engine_returns_none_when_no_subengine_matches() {
let engine = OrEngine::builder().engines(vec![exact("xxx"), exact("zzz")]).build();
assert!(engine.match_item(&"a foo bar".to_string()).is_none());
}
#[test]
fn or_engine_empty_returns_none() {
let engine = OrEngine::builder().build();
assert!(engine.match_item(&"anything".to_string()).is_none());
}
#[test]
fn and_engine_single_engine_fast_path() {
let engine = AndEngine::builder().engines(vec![exact("foo")]).build();
assert!(engine.match_item(&"foobar".to_string()).is_some());
assert!(engine.match_item(&"nope".to_string()).is_none());
}
#[test]
fn and_engine_requires_all_subengines_to_match() {
let engine = AndEngine::builder().engines(vec![exact("foo"), exact("bar")]).build();
// Both substrings present -> matched, ranges merged.
let result = engine.match_item(&"foo and bar".to_string());
assert!(result.is_some());
let result = result.unwrap();
assert!(matches!(result.matched_range, MatchRange::Chars(_)));
// Missing one substring -> no match.
assert!(engine.match_item(&"foo only".to_string()).is_none());
}
#[test]
fn and_engine_empty_returns_none() {
// With no sub-engines the multi-engine path collects nothing and bails.
let engine = AndEngine::builder().build();
assert!(engine.match_item(&"anything".to_string()).is_none());
}
#[test]
fn display_formats_combinators() {
let or = OrEngine::builder().engines(vec![exact("a")]).build();
assert!(format!("{or}").starts_with("(Or:"));
let and = AndEngine::builder().engines(vec![exact("a")]).build();
assert!(format!("{and}").starts_with("(And:"));
}
fn result(range: MatchRange, score: i32, begin: i32, end: i32) -> MatchResult {
MatchResult {
rank: crate::Rank {
score,
begin,
end,
..Default::default()
},
matched_range: range,
}
}
#[test]
fn merge_handles_char_range_and_chars_variants() {
// CharRange expands to its index span; Chars copies indices verbatim.
// Scores are summed and begin/end take the widest span.
let merged = AndEngine::merge_matched_items(
vec![
result(MatchRange::CharRange(0, 2), 5, 0, 2),
result(MatchRange::Chars(vec![4, 5]), 3, 4, 5),
],
"abcdef",
);
assert_eq!(merged.rank.score, 8);
assert_eq!(merged.rank.begin, 0);
assert_eq!(merged.rank.end, 5);
assert_eq!(merged.matched_range, MatchRange::Chars(vec![0, 1, 4, 5]));
}
#[test]
fn merge_dedups_and_sorts_overlapping_ranges() {
let merged = AndEngine::merge_matched_items(
vec![
result(MatchRange::Chars(vec![3, 1]), 1, 1, 3),
result(MatchRange::CharRange(1, 3), 1, 1, 3),
],
"abcdef",
);
// Sorted and de-duplicated union of {3,1} and {1,2}.
assert_eq!(merged.matched_range, MatchRange::Chars(vec![1, 2, 3]));
}

Some files were not shown because too many files have changed in this diff Show more