Showing preview only (2,298K chars total). Download the full file or copy to clipboard to get everything.
Repository: skim-rs/skim
Branch: master
Commit: d40af611b6d9
Files: 632
Total size: 2.1 MB
Directory structure:
gitextract_4duz2xmi/
├── .config/
│ ├── insta.yaml
│ ├── nextest.toml
│ └── valgrind.supp
├── .dockerignore
├── .envrc
├── .githooks/
│ └── pre-commit
├── .github/
│ ├── CONTRIBUTING.md
│ ├── ISSUE_TEMPLATE/
│ │ └── bug_report.md
│ ├── dependabot.yml
│ ├── pr-title-checker-config.json
│ ├── pull_request_template.md
│ └── workflows/
│ ├── pr.yml
│ ├── publish.yml
│ ├── release.yml
│ └── test.yml
├── .gitignore
├── .rustfmt.toml
├── AGENTS.md
├── CHANGELOG.md
├── Cargo.toml
├── LICENSE
├── README.md
├── bench.py
├── benches/
│ ├── filter.rs
│ ├── gungraun.rs
│ ├── matcher_micro.rs
│ ├── partial.rs
│ └── read_and_match.rs
├── bin/
│ └── sk-tmux
├── cliff.toml
├── codecov.yml
├── dist-workspace.toml
├── examples/
│ ├── ansi.rs
│ ├── async.rs
│ ├── base.rs
│ ├── cmd_collector.rs
│ ├── custom_action_keybinding.rs
│ ├── custom_item.rs
│ ├── custom_keybinding_actions.rs
│ ├── downcast.rs
│ ├── fine-grain.rs
│ ├── fuzzy_matcher_fz.rs
│ ├── multiple_runs.rs
│ ├── nth.rs
│ ├── option_builder.rs
│ ├── preview_callback.rs
│ ├── receiver_multi.rs
│ ├── sample.rs
│ ├── selector.rs
│ └── tick.rs
├── flake.nix
├── justfile
├── man/
│ └── man1/
│ ├── sk-tmux.1
│ └── sk.1
├── plugin/
│ └── skim.vim
├── rust-toolchain.toml
├── shell/
│ ├── LICENSE
│ ├── README.md
│ ├── completion.bash
│ ├── completion.fish
│ ├── completion.nu
│ ├── completion.zsh
│ ├── key-bindings.bash
│ ├── key-bindings.fish
│ ├── key-bindings.zsh
│ └── version.txt
├── sonar-project.properties
├── src/
│ ├── bin/
│ │ └── main.rs
│ ├── binds.rs
│ ├── engine/
│ │ ├── all.rs
│ │ ├── andor.rs
│ │ ├── exact.rs
│ │ ├── factory.rs
│ │ ├── fuzzy.rs
│ │ ├── mod.rs
│ │ ├── normalized.rs
│ │ ├── regexp.rs
│ │ ├── split.rs
│ │ └── util.rs
│ ├── field.rs
│ ├── fuzzy_matcher/
│ │ ├── arinae/
│ │ │ ├── algo.rs
│ │ │ ├── atom.rs
│ │ │ ├── banding.rs
│ │ │ ├── constants.rs
│ │ │ ├── helpers.rs
│ │ │ ├── matrix.rs
│ │ │ ├── mod.rs
│ │ │ ├── prefilter.rs
│ │ │ └── tests.rs
│ │ ├── clangd.rs
│ │ ├── frizbee.rs
│ │ ├── fzy.rs
│ │ ├── mod.rs
│ │ ├── skim.rs
│ │ └── util.rs
│ ├── helper/
│ │ ├── item.rs
│ │ ├── item_reader.rs
│ │ ├── macros.rs
│ │ ├── mod.rs
│ │ └── selector.rs
│ ├── item.rs
│ ├── lib.rs
│ ├── manpage.rs
│ ├── matcher.rs
│ ├── options.rs
│ ├── output.rs
│ ├── prelude.rs
│ ├── reader.rs
│ ├── shell.rs
│ ├── skim.rs
│ ├── skim_item.rs
│ ├── spinlock.rs
│ ├── theme.rs
│ ├── tmux.rs
│ ├── tui/
│ │ ├── app.rs
│ │ ├── backend.rs
│ │ ├── event.rs
│ │ ├── header.rs
│ │ ├── input.rs
│ │ ├── item_list.rs
│ │ ├── layout.rs
│ │ ├── mod.rs
│ │ ├── options.rs
│ │ ├── preview.rs
│ │ ├── statusline.rs
│ │ ├── util.rs
│ │ └── widget.rs
│ └── util.rs
├── test.dockerfile
└── tests/
├── ansi.rs
├── binds.rs
├── case.rs
├── common/
│ ├── insta.rs
│ ├── mod.rs
│ └── tmux.rs
├── defaults.rs
├── highlighting.rs
├── history.rs
├── issues.rs
├── keys.rs
├── keys_interactive.rs
├── layout.rs
├── listen.rs
├── matcher.rs
├── normalize.rs
├── options.rs
├── preview.rs
├── snapshots/
│ ├── ansi__prompt_ansi.snap
│ ├── binds__bind_append_and_select-2.snap
│ ├── binds__bind_append_and_select-3.snap
│ ├── binds__bind_append_and_select.snap
│ ├── binds__bind_change-2.snap
│ ├── binds__bind_change-3.snap
│ ├── binds__bind_change.snap
│ ├── binds__bind_first_last-2.snap
│ ├── binds__bind_first_last-3.snap
│ ├── binds__bind_first_last-4.snap
│ ├── binds__bind_first_last.snap
│ ├── binds__bind_if_non_matched-2.snap
│ ├── binds__bind_if_non_matched-3.snap
│ ├── binds__bind_if_non_matched.snap
│ ├── binds__bind_set_header_change-2.snap
│ ├── binds__bind_set_header_change.snap
│ ├── binds__bind_set_header_from_empty-2.snap
│ ├── binds__bind_set_header_from_empty.snap
│ ├── binds__bind_set_header_to_empty-2.snap
│ ├── binds__bind_set_header_to_empty.snap
│ ├── binds__bind_set_preview_cmd-2.snap
│ ├── binds__bind_set_preview_cmd-3.snap
│ ├── binds__bind_set_preview_cmd.snap
│ ├── binds__bind_set_query_basic-2.snap
│ ├── binds__bind_set_query_basic.snap
│ ├── binds__bind_set_query_expand-2.snap
│ ├── binds__bind_set_query_expand.snap
│ ├── binds__bind_set_query_fields-2.snap
│ ├── binds__bind_set_query_fields.snap
│ ├── binds__bind_set_query_to_itself-2.snap
│ ├── binds__bind_set_query_to_itself-3.snap
│ ├── binds__bind_set_query_to_itself-4.snap
│ ├── binds__bind_set_query_to_itself.snap
│ ├── binds__bind_toggle_interactive-2.snap
│ ├── binds__bind_toggle_interactive.snap
│ ├── binds__bind_toggle_interactive_queries-2.snap
│ ├── binds__bind_toggle_interactive_queries-3.snap
│ ├── binds__bind_toggle_interactive_queries-4.snap
│ ├── binds__bind_toggle_interactive_queries-5.snap
│ ├── binds__bind_toggle_interactive_queries.snap
│ ├── binds__bind_top_alias-2.snap
│ ├── binds__bind_top_alias-3.snap
│ ├── binds__bind_top_alias.snap
│ ├── case__case_ignore_different-2.snap
│ ├── case__case_ignore_different.snap
│ ├── case__case_ignore_exact-2.snap
│ ├── case__case_ignore_exact.snap
│ ├── case__case_ignore_lower-2.snap
│ ├── case__case_ignore_lower.snap
│ ├── case__case_ignore_no_match-2.snap
│ ├── case__case_ignore_no_match.snap
│ ├── case__case_non_ascii-2.snap
│ ├── case__case_non_ascii-3.snap
│ ├── case__case_non_ascii.snap
│ ├── case__case_respect_exact-2.snap
│ ├── case__case_respect_exact.snap
│ ├── case__case_respect_lower-2.snap
│ ├── case__case_respect_lower.snap
│ ├── case__case_respect_no_match-2.snap
│ ├── case__case_respect_no_match.snap
│ ├── case__case_smart_exact-2.snap
│ ├── case__case_smart_exact.snap
│ ├── case__case_smart_lower-2.snap
│ ├── case__case_smart_lower.snap
│ ├── case__case_smart_no_match-2.snap
│ ├── case__case_smart_no_match.snap
│ ├── defaults__interactive_mode_command_execution-2.snap
│ ├── defaults__interactive_mode_command_execution-3.snap
│ ├── defaults__interactive_mode_command_execution.snap
│ ├── defaults__unicode_input-2.snap
│ ├── defaults__unicode_input-3.snap
│ ├── defaults__unicode_input-4.snap
│ ├── defaults__unicode_input.snap
│ ├── defaults__vanilla.snap
│ ├── defaults__vanilla_basic.snap
│ ├── issues__issue_359_multi_regex_unicode.snap
│ ├── issues__issue_361_literal_space_control.snap
│ ├── issues__issue_361_literal_space_invert.snap
│ ├── issues__issue_547_null_match-2.snap
│ ├── issues__issue_547_null_match.snap
│ ├── issues__issue_929_double_width_chars-2.snap
│ ├── issues__issue_929_double_width_chars.snap
│ ├── keys__keys_alt_b-2.snap
│ ├── keys__keys_alt_b.snap
│ ├── keys__keys_alt_bspace-2.snap
│ ├── keys__keys_alt_bspace.snap
│ ├── keys__keys_alt_d-2.snap
│ ├── keys__keys_alt_d-3.snap
│ ├── keys__keys_alt_d-4.snap
│ ├── keys__keys_alt_d.snap
│ ├── keys__keys_alt_f-2.snap
│ ├── keys__keys_alt_f-3.snap
│ ├── keys__keys_alt_f.snap
│ ├── keys__keys_arrows-2.snap
│ ├── keys__keys_arrows-3.snap
│ ├── keys__keys_arrows.snap
│ ├── keys__keys_basic-2.snap
│ ├── keys__keys_basic.snap
│ ├── keys__keys_bspace-2.snap
│ ├── keys__keys_bspace.snap
│ ├── keys__keys_btab-2.snap
│ ├── keys__keys_btab.snap
│ ├── keys__keys_ctrl_a-2.snap
│ ├── keys__keys_ctrl_a.snap
│ ├── keys__keys_ctrl_arrows-2.snap
│ ├── keys__keys_ctrl_arrows-3.snap
│ ├── keys__keys_ctrl_arrows-4.snap
│ ├── keys__keys_ctrl_arrows.snap
│ ├── keys__keys_ctrl_b-2.snap
│ ├── keys__keys_ctrl_b-3.snap
│ ├── keys__keys_ctrl_b.snap
│ ├── keys__keys_ctrl_c.snap
│ ├── keys__keys_ctrl_d-2.snap
│ ├── keys__keys_ctrl_d-3.snap
│ ├── keys__keys_ctrl_d.snap
│ ├── keys__keys_ctrl_e-2.snap
│ ├── keys__keys_ctrl_e-3.snap
│ ├── keys__keys_ctrl_e.snap
│ ├── keys__keys_ctrl_f-2.snap
│ ├── keys__keys_ctrl_f-3.snap
│ ├── keys__keys_ctrl_f.snap
│ ├── keys__keys_ctrl_h-2.snap
│ ├── keys__keys_ctrl_h.snap
│ ├── keys__keys_ctrl_k-2.snap
│ ├── keys__keys_ctrl_k.snap
│ ├── keys__keys_ctrl_u-2.snap
│ ├── keys__keys_ctrl_u.snap
│ ├── keys__keys_ctrl_w-2.snap
│ ├── keys__keys_ctrl_w.snap
│ ├── keys__keys_ctrl_y-2.snap
│ ├── keys__keys_ctrl_y-3.snap
│ ├── keys__keys_ctrl_y.snap
│ ├── keys__keys_tab-2.snap
│ ├── keys__keys_tab-3.snap
│ ├── keys__keys_tab.snap
│ ├── keys__keys_tab_empty-2.snap
│ ├── keys__keys_tab_empty-3.snap
│ ├── keys__keys_tab_empty.snap
│ ├── keys_interactive__keys_interactive_alt_b-2.snap
│ ├── keys_interactive__keys_interactive_alt_b.snap
│ ├── keys_interactive__keys_interactive_alt_bspace-2.snap
│ ├── keys_interactive__keys_interactive_alt_bspace.snap
│ ├── keys_interactive__keys_interactive_alt_d-2.snap
│ ├── keys_interactive__keys_interactive_alt_d-3.snap
│ ├── keys_interactive__keys_interactive_alt_d-4.snap
│ ├── keys_interactive__keys_interactive_alt_d.snap
│ ├── keys_interactive__keys_interactive_alt_f-2.snap
│ ├── keys_interactive__keys_interactive_alt_f-3.snap
│ ├── keys_interactive__keys_interactive_alt_f.snap
│ ├── keys_interactive__keys_interactive_arrows-2.snap
│ ├── keys_interactive__keys_interactive_arrows-3.snap
│ ├── keys_interactive__keys_interactive_arrows.snap
│ ├── keys_interactive__keys_interactive_basic-2.snap
│ ├── keys_interactive__keys_interactive_basic.snap
│ ├── keys_interactive__keys_interactive_bspace-2.snap
│ ├── keys_interactive__keys_interactive_bspace.snap
│ ├── keys_interactive__keys_interactive_btab-2.snap
│ ├── keys_interactive__keys_interactive_btab.snap
│ ├── keys_interactive__keys_interactive_ctrl_a-2.snap
│ ├── keys_interactive__keys_interactive_ctrl_a.snap
│ ├── keys_interactive__keys_interactive_ctrl_arrows-2.snap
│ ├── keys_interactive__keys_interactive_ctrl_arrows-3.snap
│ ├── keys_interactive__keys_interactive_ctrl_arrows-4.snap
│ ├── keys_interactive__keys_interactive_ctrl_arrows.snap
│ ├── keys_interactive__keys_interactive_ctrl_b-2.snap
│ ├── keys_interactive__keys_interactive_ctrl_b-3.snap
│ ├── keys_interactive__keys_interactive_ctrl_b.snap
│ ├── keys_interactive__keys_interactive_ctrl_c.snap
│ ├── keys_interactive__keys_interactive_ctrl_d-2.snap
│ ├── keys_interactive__keys_interactive_ctrl_d-3.snap
│ ├── keys_interactive__keys_interactive_ctrl_d.snap
│ ├── keys_interactive__keys_interactive_ctrl_e-2.snap
│ ├── keys_interactive__keys_interactive_ctrl_e-3.snap
│ ├── keys_interactive__keys_interactive_ctrl_e.snap
│ ├── keys_interactive__keys_interactive_ctrl_f-2.snap
│ ├── keys_interactive__keys_interactive_ctrl_f-3.snap
│ ├── keys_interactive__keys_interactive_ctrl_f.snap
│ ├── keys_interactive__keys_interactive_ctrl_h-2.snap
│ ├── keys_interactive__keys_interactive_ctrl_h.snap
│ ├── keys_interactive__keys_interactive_ctrl_k-2.snap
│ ├── keys_interactive__keys_interactive_ctrl_k.snap
│ ├── keys_interactive__keys_interactive_ctrl_m.snap
│ ├── keys_interactive__keys_interactive_ctrl_u-2.snap
│ ├── keys_interactive__keys_interactive_ctrl_u.snap
│ ├── keys_interactive__keys_interactive_ctrl_w-2.snap
│ ├── keys_interactive__keys_interactive_ctrl_w.snap
│ ├── keys_interactive__keys_interactive_ctrl_y-2.snap
│ ├── keys_interactive__keys_interactive_ctrl_y-3.snap
│ ├── keys_interactive__keys_interactive_ctrl_y.snap
│ ├── keys_interactive__keys_interactive_enter.snap
│ ├── keys_interactive__keys_interactive_tab-2.snap
│ ├── keys_interactive__keys_interactive_tab-3.snap
│ ├── keys_interactive__keys_interactive_tab.snap
│ ├── layout__layout_border.snap
│ ├── layout__layout_default.snap
│ ├── layout__layout_reverse.snap
│ ├── layout__layout_reverse_border.snap
│ ├── layout__layout_reverse_list.snap
│ ├── layout__layout_reverse_list_border.snap
│ ├── matcher__matcher_arinae.snap
│ ├── matcher__matcher_arinae_typos.snap
│ ├── matcher__matcher_clangd.snap
│ ├── matcher__matcher_default.snap
│ ├── matcher__matcher_frizbee.snap
│ ├── matcher__matcher_frizbee_typos.snap
│ ├── matcher__matcher_fzy.snap
│ ├── matcher__matcher_fzy_typos.snap
│ ├── matcher__matcher_skim_v1.snap
│ ├── matcher__matcher_skim_v2.snap
│ ├── matcher__matcher_skim_v3.snap
│ ├── matcher__matcher_skim_v3_typos.snap
│ ├── normalize__insta_no_normalize_accented_item-2.snap
│ ├── normalize__insta_no_normalize_accented_item.snap
│ ├── normalize__insta_normalize_accented_item_unaccented_query-2.snap
│ ├── normalize__insta_normalize_accented_item_unaccented_query.snap
│ ├── normalize__insta_normalize_case_insensitive-2.snap
│ ├── normalize__insta_normalize_case_insensitive.snap
│ ├── normalize__insta_normalize_combined_chars-2.snap
│ ├── normalize__insta_normalize_combined_chars-3.snap
│ ├── normalize__insta_normalize_combined_chars.snap
│ ├── normalize__insta_normalize_cyrillic-2.snap
│ ├── normalize__insta_normalize_cyrillic.snap
│ ├── normalize__insta_normalize_exact_match-2.snap
│ ├── normalize__insta_normalize_exact_match.snap
│ ├── normalize__insta_normalize_multiple_diacritics-2.snap
│ ├── normalize__insta_normalize_multiple_diacritics-3.snap
│ ├── normalize__insta_normalize_multiple_diacritics.snap
│ ├── normalize__insta_normalize_negation-2.snap
│ ├── normalize__insta_normalize_negation.snap
│ ├── normalize__insta_normalize_prefix-2.snap
│ ├── normalize__insta_normalize_prefix.snap
│ ├── normalize__insta_normalize_suffix-2.snap
│ ├── normalize__insta_normalize_suffix.snap
│ ├── normalize__insta_normalize_umlauts-2.snap
│ ├── normalize__insta_normalize_umlauts-3.snap
│ ├── normalize__insta_normalize_umlauts.snap
│ ├── normalize__insta_normalize_unaccented_item_accented_query-2.snap
│ ├── normalize__insta_normalize_unaccented_item_accented_query.snap
│ ├── options__opt_border_double.snap
│ ├── options__opt_border_heavy_double_dashed.snap
│ ├── options__opt_border_heavy_quadruple_dashed.snap
│ ├── options__opt_border_heavy_triple_dashed.snap
│ ├── options__opt_border_light_double_dashed.snap
│ ├── options__opt_border_light_quadruple_dashed.snap
│ ├── options__opt_border_light_triple_dashed.snap
│ ├── options__opt_border_plain.snap
│ ├── options__opt_border_quadrant_inside.snap
│ ├── options__opt_border_quadrant_outside.snap
│ ├── options__opt_border_rounded.snap
│ ├── options__opt_border_thick.snap
│ ├── options__opt_cycle-2.snap
│ ├── options__opt_cycle-3.snap
│ ├── options__opt_cycle.snap
│ ├── options__opt_cycle_header_lines-2.snap
│ ├── options__opt_cycle_header_lines-3.snap
│ ├── options__opt_cycle_header_lines.snap
│ ├── options__opt_disabled-2.snap
│ ├── options__opt_disabled.snap
│ ├── options__opt_ellipsis.snap
│ ├── options__opt_exit_0_enter.snap
│ ├── options__opt_header_inline_info.snap
│ ├── options__opt_header_lines_1.snap
│ ├── options__opt_header_lines_all.snap
│ ├── options__opt_header_lines_inline_info.snap
│ ├── options__opt_header_lines_reverse.snap
│ ├── options__opt_header_lines_reverse_inline_info.snap
│ ├── options__opt_header_multiline.snap
│ ├── options__opt_header_only.snap
│ ├── options__opt_header_reverse.snap
│ ├── options__opt_header_reverse_inline_info.snap
│ ├── options__opt_hscroll_begin.snap
│ ├── options__opt_hscroll_end.snap
│ ├── options__opt_hscroll_middle.snap
│ ├── options__opt_info_control-2.snap
│ ├── options__opt_info_control.snap
│ ├── options__opt_info_default-2.snap
│ ├── options__opt_info_default.snap
│ ├── options__opt_info_hidden.snap
│ ├── options__opt_info_inline-2.snap
│ ├── options__opt_info_inline.snap
│ ├── options__opt_inline_info-2.snap
│ ├── options__opt_inline_info.snap
│ ├── options__opt_min_query_length-2.snap
│ ├── options__opt_min_query_length-3.snap
│ ├── options__opt_min_query_length.snap
│ ├── options__opt_min_query_length_interactive-2.snap
│ ├── options__opt_min_query_length_interactive-3.snap
│ ├── options__opt_min_query_length_interactive.snap
│ ├── options__opt_multi-2.snap
│ ├── options__opt_multi-3.snap
│ ├── options__opt_multi.snap
│ ├── options__opt_multi_selector-2.snap
│ ├── options__opt_multi_selector.snap
│ ├── options__opt_multiple_flags_cmd_prompt.snap
│ ├── options__opt_multiple_flags_cmd_query.snap
│ ├── options__opt_multiple_flags_combined_nth-2.snap
│ ├── options__opt_multiple_flags_combined_nth.snap
│ ├── options__opt_multiple_flags_combined_with_nth.snap
│ ├── options__opt_multiple_flags_interactive.snap
│ ├── options__opt_multiple_flags_layout_and_reverse.snap
│ ├── options__opt_multiple_flags_prompt.snap
│ ├── options__opt_multiple_flags_reverse.snap
│ ├── options__opt_multiple_flags_reverse_and_layout.snap
│ ├── options__opt_no_clear_if_empty-2.snap
│ ├── options__opt_no_clear_if_empty.snap
│ ├── options__opt_no_hscroll.snap
│ ├── options__opt_no_info.snap
│ ├── options__opt_no_sort-2.snap
│ ├── options__opt_no_sort.snap
│ ├── options__opt_nth_1-2.snap
│ ├── options__opt_nth_1-3.snap
│ ├── options__opt_nth_1-4.snap
│ ├── options__opt_nth_1.snap
│ ├── options__opt_nth_2-2.snap
│ ├── options__opt_nth_2-3.snap
│ ├── options__opt_nth_2-4.snap
│ ├── options__opt_nth_2.snap
│ ├── options__opt_nth_4-2.snap
│ ├── options__opt_nth_4-3.snap
│ ├── options__opt_nth_4-4.snap
│ ├── options__opt_nth_4.snap
│ ├── options__opt_nth_neg_1-2.snap
│ ├── options__opt_nth_neg_1-3.snap
│ ├── options__opt_nth_neg_1-4.snap
│ ├── options__opt_nth_neg_1.snap
│ ├── options__opt_nth_neg_2-2.snap
│ ├── options__opt_nth_neg_2-3.snap
│ ├── options__opt_nth_neg_2-4.snap
│ ├── options__opt_nth_neg_2.snap
│ ├── options__opt_nth_neg_4-2.snap
│ ├── options__opt_nth_neg_4-3.snap
│ ├── options__opt_nth_neg_4-4.snap
│ ├── options__opt_nth_neg_4.snap
│ ├── options__opt_nth_neg_oob-2.snap
│ ├── options__opt_nth_neg_oob.snap
│ ├── options__opt_nth_oob-2.snap
│ ├── options__opt_nth_oob.snap
│ ├── options__opt_nth_range_closed-2.snap
│ ├── options__opt_nth_range_closed-3.snap
│ ├── options__opt_nth_range_closed-4.snap
│ ├── options__opt_nth_range_closed-5.snap
│ ├── options__opt_nth_range_closed-6.snap
│ ├── options__opt_nth_range_closed-7.snap
│ ├── options__opt_nth_range_closed-8.snap
│ ├── options__opt_nth_range_closed.snap
│ ├── options__opt_nth_range_dec-2.snap
│ ├── options__opt_nth_range_dec.snap
│ ├── options__opt_nth_range_from_start-2.snap
│ ├── options__opt_nth_range_from_start-3.snap
│ ├── options__opt_nth_range_from_start-4.snap
│ ├── options__opt_nth_range_from_start.snap
│ ├── options__opt_nth_range_to_end-2.snap
│ ├── options__opt_nth_range_to_end-3.snap
│ ├── options__opt_nth_range_to_end-4.snap
│ ├── options__opt_nth_range_to_end.snap
│ ├── options__opt_pre_select_items.snap
│ ├── options__opt_pre_select_n.snap
│ ├── options__opt_pre_select_pat.snap
│ ├── options__opt_replstr-2.snap
│ ├── options__opt_replstr.snap
│ ├── options__opt_select_1_enter.snap
│ ├── options__opt_selector.snap
│ ├── options__opt_skip_to_pattern-2.snap
│ ├── options__opt_skip_to_pattern-3.snap
│ ├── options__opt_skip_to_pattern.snap
│ ├── options__opt_tabstop_1.snap
│ ├── options__opt_tabstop_3.snap
│ ├── options__opt_tabstop_default.snap
│ ├── options__opt_tac.snap
│ ├── options__opt_tac_with_header_lines.snap
│ ├── options__opt_with_nth_1.snap
│ ├── options__opt_with_nth_2.snap
│ ├── options__opt_with_nth_4.snap
│ ├── options__opt_with_nth_neg_1.snap
│ ├── options__opt_with_nth_neg_2.snap
│ ├── options__opt_with_nth_neg_4.snap
│ ├── options__opt_with_nth_oob.snap
│ ├── options__opt_with_nth_oob_4.snap
│ ├── options__opt_with_nth_preview.snap
│ ├── options__opt_with_nth_range_closed.snap
│ ├── options__opt_with_nth_range_desc.snap
│ ├── options__opt_with_nth_range_from_start.snap
│ ├── options__opt_with_nth_range_to_end.snap
│ ├── options__opt_wrap.snap
│ ├── preview__preview_navigation-2.snap
│ ├── preview__preview_navigation.snap
│ ├── preview__preview_no_pty__preview_no_pty_flag.snap
│ ├── preview__preview_no_pty__preview_no_pty_navigation-2.snap
│ ├── preview__preview_no_pty__preview_no_pty_navigation.snap
│ ├── preview__preview_no_pty__preview_no_pty_nowrap.snap
│ ├── preview__preview_no_pty__preview_no_pty_nul_char.snap
│ ├── preview__preview_no_pty__preview_no_pty_offset_expr.snap
│ ├── preview__preview_no_pty__preview_no_pty_offset_fixed.snap
│ ├── preview__preview_no_pty__preview_no_pty_offset_fixed_and_expr.snap
│ ├── preview__preview_no_pty__preview_no_pty_plus-2.snap
│ ├── preview__preview_no_pty__preview_no_pty_plus-3.snap
│ ├── preview__preview_no_pty__preview_no_pty_plus-4.snap
│ ├── preview__preview_no_pty__preview_no_pty_plus-5.snap
│ ├── preview__preview_no_pty__preview_no_pty_plus.snap
│ ├── preview__preview_no_pty__preview_no_pty_preserve_quotes.snap
│ ├── preview__preview_no_pty__preview_no_pty_window_down.snap
│ ├── preview__preview_no_pty__preview_no_pty_window_left.snap
│ ├── preview__preview_no_pty__preview_no_pty_window_up.snap
│ ├── preview__preview_no_pty__preview_no_pty_wrap.snap
│ ├── preview__preview_no_pty_linux.snap
│ ├── preview__preview_nowrap.snap
│ ├── preview__preview_nul_char.snap
│ ├── preview__preview_offset_expr.snap
│ ├── preview__preview_offset_fixed.snap
│ ├── preview__preview_offset_fixed_and_expr.snap
│ ├── preview__preview_plus-2.snap
│ ├── preview__preview_plus-3.snap
│ ├── preview__preview_plus-4.snap
│ ├── preview__preview_plus-5.snap
│ ├── preview__preview_plus.snap
│ ├── preview__preview_preserve_quotes.snap
│ ├── preview__preview_pty__preview_pty_flag.snap
│ ├── preview__preview_pty__preview_pty_navigation-2.snap
│ ├── preview__preview_pty__preview_pty_navigation.snap
│ ├── preview__preview_pty__preview_pty_nowrap.snap
│ ├── preview__preview_pty__preview_pty_nul_char.snap
│ ├── preview__preview_pty__preview_pty_offset_expr.snap
│ ├── preview__preview_pty__preview_pty_offset_fixed.snap
│ ├── preview__preview_pty__preview_pty_offset_fixed_and_expr.snap
│ ├── preview__preview_pty__preview_pty_plus-2.snap
│ ├── preview__preview_pty__preview_pty_plus-3.snap
│ ├── preview__preview_pty__preview_pty_plus-4.snap
│ ├── preview__preview_pty__preview_pty_plus-5.snap
│ ├── preview__preview_pty__preview_pty_plus.snap
│ ├── preview__preview_pty__preview_pty_preserve_quotes.snap
│ ├── preview__preview_pty__preview_pty_window_down.snap
│ ├── preview__preview_pty__preview_pty_window_left.snap
│ ├── preview__preview_pty__preview_pty_window_up.snap
│ ├── preview__preview_pty__preview_pty_wrap.snap
│ ├── preview__preview_pty_linux.snap
│ ├── preview__preview_window_down.snap
│ ├── preview__preview_window_left.snap
│ ├── preview__preview_window_up.snap
│ ├── preview__preview_wrap.snap
│ ├── split_match__split_match_both_parts-2.snap
│ ├── split_match__split_match_both_parts.snap
│ ├── split_match__split_match_custom_delimiter-2.snap
│ ├── split_match__split_match_custom_delimiter.snap
│ ├── split_match__split_match_delimiter_in_query_not_item-2.snap
│ ├── split_match__split_match_delimiter_in_query_not_item.snap
│ ├── split_match__split_match_empty_after-2.snap
│ ├── split_match__split_match_empty_after.snap
│ ├── split_match__split_match_empty_before-2.snap
│ ├── split_match__split_match_empty_before.snap
│ ├── split_match__split_match_multiple_delimiters_in_item-2.snap
│ ├── split_match__split_match_multiple_delimiters_in_item.snap
│ ├── split_match__split_match_no_delimiter_in_item-2.snap
│ ├── split_match__split_match_no_delimiter_in_item.snap
│ ├── split_match__split_match_or-2.snap
│ ├── split_match__split_match_or.snap
│ ├── split_match__split_match_query_before_delimiter-2.snap
│ ├── split_match__split_match_query_before_delimiter.snap
│ ├── tiebreak__tiebreak_begin-2.snap
│ ├── tiebreak__tiebreak_begin.snap
│ ├── tiebreak__tiebreak_default-2.snap
│ ├── tiebreak__tiebreak_default.snap
│ ├── tiebreak__tiebreak_end-2.snap
│ ├── tiebreak__tiebreak_end.snap
│ ├── tiebreak__tiebreak_index-2.snap
│ ├── tiebreak__tiebreak_index.snap
│ ├── tiebreak__tiebreak_length-2.snap
│ ├── tiebreak__tiebreak_length.snap
│ ├── tiebreak__tiebreak_neg_begin-2.snap
│ ├── tiebreak__tiebreak_neg_begin.snap
│ ├── tiebreak__tiebreak_neg_end-2.snap
│ ├── tiebreak__tiebreak_neg_end.snap
│ ├── tiebreak__tiebreak_neg_index-2.snap
│ ├── tiebreak__tiebreak_neg_index.snap
│ ├── tiebreak__tiebreak_neg_length-2.snap
│ ├── tiebreak__tiebreak_neg_length.snap
│ ├── tiebreak__tiebreak_neg_pathname-2.snap
│ ├── tiebreak__tiebreak_neg_pathname.snap
│ ├── tiebreak__tiebreak_neg_score-2.snap
│ ├── tiebreak__tiebreak_neg_score.snap
│ ├── tiebreak__tiebreak_pathname-2.snap
│ └── tiebreak__tiebreak_pathname.snap
├── split_match.rs
├── tiebreak.rs
├── tmux.rs
└── unix.rs
================================================
FILE CONTENTS
================================================
================================================
FILE: .config/insta.yaml
================================================
behavior:
output: minimal
test:
runner: nextest
================================================
FILE: .config/nextest.toml
================================================
experimental = ["setup-scripts", "wrapper-scripts"]
[scripts.setup.stop-tmux]
command = "sh -c 'tmux kill-session -t skim_e2e || true'"
[scripts.setup.start-tmux]
command = "tmux new-session -d -s skim_e2e -n skim_e2e"
# 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"
]
[profile.default]
fail-fast = false
retries = 9
[[profile.default.scripts]]
platform = "cfg(unix)"
setup = ["stop-tmux", "start-tmux"]
[profile.default.junit]
path = "junit.xml"
# Valgrind profile for memory leak detection
# Usage: cargo nextest run --profile valgrind --features test-utils
#
# 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)"
setup = ["stop-tmux", "start-tmux"]
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 --features test-utils -Zbuild-std --target x86_64-unknown-linux-gnu
# 2. Then run tests:
# TSAN_OPTIONS="detect_deadlocks=1" cargo +nightly nextest run --profile tsan --features test-utils --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
[[profile.tsan.scripts]]
platform = "cfg(unix)"
setup = ["stop-tmux", "start-tmux"]
================================================
FILE: .config/valgrind.supp
================================================
# 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_*
}
================================================
FILE: .dockerignore
================================================
target/
.github/
bin/
plugin/
man/
shell/
*.md
install
================================================
FILE: .envrc
================================================
use flake
================================================
FILE: .githooks/pre-commit
================================================
set -xeuo pipefail
cargo fmt --check --all
cargo clippy --all-targets --features test-utils -- -Dwarnings
cargo check --no-default-features
================================================
FILE: .github/CONTRIBUTING.md
================================================
# Contributor Guide
## 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 --features test-utils`, 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 --features test-utils --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. However, the tests won't run without the `test-utils` feature, used to create test backends.
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`.
## 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.
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
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.
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: "cargo"
directory: "/"
schedule:
interval: "weekly"
commit-message:
prefix: chore(deps)
prefix-development: chore(dev-deps)
================================================
FILE: .github/pr-title-checker-config.json
================================================
{
"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": ""
}
}
================================================
FILE: .github/pull_request_template.md
================================================
## 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
_Note_: [codecov](https://codecov.io) runs on the PR on this repo, but feel free to ignore it.
================================================
FILE: .github/workflows/pr.yml
================================================
name: "Pull Requests"
on:
pull_request_target:
types:
- opened
- edited
- synchronize
- labeled
- unlabeled
jobs:
check:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: thehanimo/pr-title-checker@v1.4.3
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
pass_on_octokit_error: false
configuration_path: .github/pr-title-checker-config.json
generate-files:
runs-on: ubuntu-22.04
permissions:
contents: write
steps:
- uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ vars.SKIM_RS_BOT_APP_ID }}
private-key: ${{ secrets.SKIM_RS_BOT_PRIVATE_KEY }}
- name: Checkout Git repo
uses: actions/checkout@v6
with:
fetch-depth: 0
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
token: ${{ steps.app-token.outputs.token }}
- run: rustup toolchain install
- name: Cache
uses: Swatinem/rust-cache@v2
- name: Generate files
run: |
cargo run -- --man > man/man1/sk.1
cargo run -- --shell bash > shell/completion.bash
cargo run -- --shell zsh > shell/completion.zsh
cargo run -- --shell fish > shell/completion.fish
cargo run -- --shell nushell > shell/completion.nu
- name: Push modified files
run: |
git branch -v
git config user.email "skim-bot@skim-rs.github.io"
git config user.name "Skim bot"
git commit -am 'chore: generate completions & manpage' || exit 0
git push
================================================
FILE: .github/workflows/publish.yml
================================================
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@v6
- 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
================================================
FILE: .github/workflows/release.yml
================================================
# 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@v4
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.30.3/cargo-dist-installer.sh | sh"
- name: Cache dist
uses: actions/upload-artifact@v4
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@v4
with:
name: artifacts-plan-dist-manifest
path: plan-dist-manifest.json
custom-test:
uses: ./.github/workflows/test.yml
secrets: inherit
# 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@v4
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@v4
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@v4
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@v4
with:
persist-credentials: false
submodules: recursive
- name: Install cached dist
uses: actions/download-artifact@v4
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@v4
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@v4
with:
name: artifacts-build-global
path: |
${{ steps.cargo-dist.outputs.paths }}
${{ env.BUILD_MANIFEST_NAME }}
# Determines if we should publish/announce
host:
needs:
- plan
- build-local-artifacts
- build-global-artifacts
# 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.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@v4
with:
persist-credentials: false
submodules: recursive
- name: Install cached dist
uses: actions/download-artifact@v4
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@v4
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@v4
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@v4
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"
announce:
needs:
- plan
- host
- custom-publish
# 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') }}
runs-on: "ubuntu-22.04"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
submodules: recursive
================================================
FILE: .github/workflows/test.yml
================================================
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.os}}
strategy:
matrix:
build: [linux, macos]
include:
- build: linux
os: ubuntu-latest
target: x86_64-unknown-linux-musl
- build: macos
os: macos-latest
target: x86_64-apple-darwin
steps:
- name: "[linux] Install dependencies"
run: |
sudo apt-get install tmux
tmux -V
locale
if: runner.os == 'Linux'
- name: "[macos] Install dependencies"
run: |
brew install tmux
tmux -V
locale
if: runner.os == 'macOS'
env:
HOMEBREW_NO_AUTO_UPDATE: 1
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- run: rustup toolchain install
- uses: taiki-e/install-action@v2
with:
tool: nextest@0.9
- uses: taiki-e/install-action@v2
with:
tool: cargo-llvm-cov@0.8
- name: 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 llvm-cov nextest --release --features test-utils --bins --lib --examples --tests --codecov --output-path codecov.json
env:
LC_ALL: en_US.UTF-8
TERM: xterm-256color
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: codecov.json
fail_ci_if_error: false
continue-on-error: true
- name: Upload test results to Codecov
uses: codecov/codecov-action@v5
with:
report_type: test_results
token: ${{ secrets.CODECOV_TOKEN }}
files: target/nextest/default/junit.xml
fail_ci_if_error: false
continue-on-error: true
clippy:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 1
- run: rustup toolchain install
- name: Cache
uses: Swatinem/rust-cache@v2
- name: Clippy
run: cargo clippy
rustfmt:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- run: rustup toolchain install
- name: Check formatting
run: |
cargo fmt --all -- --check
sonarqube:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: SonarQube Scan
uses: SonarSource/sonarqube-scan-action@v6
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
build-no-default-features:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- run: rustup toolchain install
- name: Cache
uses: Swatinem/rust-cache@v2
- name: Build without any feature
run: |
cargo build --no-default-features
================================================
FILE: .gitignore
================================================
# Compiled files
*.o
*.so
*.rlib
*.dll
# Executables
*.exe
# Generated by Cargo
/target/
/bin/sk
.idea/
.ropeproject/
bench_data.txt
.direnv
# Coverage
lcov.info
codecov.json
*.profraw
benches/fixtures/*.txt
# Profiling
profile.json.gz
perf.data.old
perf.data
flamegraph.svg
cachegrind.out.*
/scripts/data/
================================================
FILE: .rustfmt.toml
================================================
max_width = 120
================================================
FILE: AGENTS.md
================================================
# Skim Agent Guidelines
## Build/Test/Lint Commands
- Build: `cargo build [--release]`
- Run: `cargo run [--release]`
- Test (all): `cargo nextest --features test-utils`
- Test (single): `cargo nextest test_name --features test-utils`
- Integration/E2E tests: `cargo nextest --tests --features test-utils` (will need tmux under the hood)
- Memory leak detection: `cargo nextest run --profile valgrind --features test-utils`
- Thread leak/race detection:
1. Build: `RUSTFLAGS="-Zsanitizer=thread" cargo +nightly build --tests --features test-utils -Zbuild-std --target x86_64-unknown-linux-gnu`
2. Run: `TSAN_OPTIONS="detect_deadlocks=1" cargo +nightly nextest run --profile tsan --features test-utils --target x86_64-unknown-linux-gnu`
- Lint: `cargo clippy`
- Format: `cargo fmt` (check only: `cargo fmt --check`)
## 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
## Project Structure
- Core functionality in `skim/src/`
- Common utilities in `skim-common/`
- Task automation in `xtask/`
## Testing
This application can be tested by :
- creating a new `tmux` session in the background (`tmux new-session -s <session name> -d`)
- creating a new named tmux window in that session : `tmux new-window -d -P -F '#I' -n <window name> -t <session name>` and configuring the pane naming using `tmux set-window-option -t <window name> pane-base-index 0`
- sending the command to run and input using `tmux send-keys -t <window name> <keys>`
- when ready, capturing the window using `tmux capture-pane -b <window name> -t <window name>.0` and then saving the capture to a file using `tmux save-buffer -b <window name> <output file>`
================================================
FILE: CHANGELOG.md
================================================
# 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).
## [4.0.0] - 2026-03-10
### Changes
`sk` is now beating `fzf` in interactive matching for time, peak memory usage and CPU usage ! See the benchmarks below for details, including a brief explanation of the benchmark itself.
This release brings multiple breaking changes, please read the following if you have doubts about the update.
#### Default to the Arinae matcher
The biggest change of them all is that the default algorithm is now `Arinae`, skim's latest and most performant algorithm, featuring typo-resistance. If you want to keep using `SkimV2`, pass `--algo skim_v2` and please take the time to open an issue explaining why if possible.
This also makes the `--scheme` option available to the default matcher, allowing for more refined matching scenarii.
#### Default to non-typo resistant
Typo-resistant behavior is now disable by default, add `--typos` to your skim invocation or `SKIM_DEFAULT_OPTIONS` to get it back. Unless you were using the `frizbee`, `fzy` or `arinae` matcher, this should not impact you.
#### Removal of the `skim_v1` algorithm
The `SkimV1` algorithm, skim's initial algorithm, has been deprecated for years now, and it has now been removed.
#### SkimItem index (library only)
`SkimItem::get_index` and `SkimItem::set_index` are gone, and all index handling is now done internally.
### Benchmarks
This benchmarks runs the interactive interface in a tmux session, and waits for the UI to stabilize.
It uses a 10 million path-like ASCII items input file, and the query `test`.
```
=== Results: sk v4.0.0 [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: sk v3.7.0 ===
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: fzf 0.70.0 ===
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: sk v4.0.0) ===
Binary Avg time Δ time Avg rate Δ rate
------------------------------------------------------------------------------
sk v4.0.0 3.827s baseline 2615767 baseline
sk v3.7.0 3.930s +2.7% 2548674 -2.6%
fzf 0.70.0 5.421s +41.7% 1848269 -29.3%
```
### 🚀 Features
- [**breaking**] Internally compute indexes at match time (removes get/set_index) (#1001)
- [**breaking**] Use Arinae as default algorithm
### ⚙️ Miscellaneous Tasks
- [**breaking**] Default to disabled typos
- Use python for bench script for comparison
## [3.7.0] - 2026-03-08
This adds a new library API: `Skim::run_items`. Using this, you don't need to send the items, the library handles it for you.
Instead of writing this:
```rust
use skim::prelude::*;
let (rx, tx) = unbounded();
// We need MySkimItem to override the set_index/get_index methods
struct MySkimItem {
value: String,
index: usize
}
impl SkimItem for MySkimItem {
// Implement the default members
}
tx.send(MySkimItem { value: String::from("foo"), index: 0 });
tx.send(MySkimItem { value: String::from("bar"), index: 1 });
let res = Skim::run_with(options, Some(rx));
```
You can simply write this:
```rust
use skim::prelude::*;
let res = Skim::run_with(options, ["foo", "bar"]);
```
It will automatically convert any iterator of <impl SkimItem> by adding an `index` field and then send it, before running skim.
### 🚀 Features
- Add `scheme` for better filepath matching in Arinae
- Add Skim::run_items API
### 🐛 Bug Fixes
- Use sum of scores in And engine
- Correctly init rank for and engine
### ⚙️ Miscellaneous Tasks
- Add gungraun benchmark
- Review snapshots
## [3.6.2] - 2026-03-04
### ⚙️ Miscellaneous Tasks
- Refactor app layout computations to take them out of the hot loop (#996)
- Allow using flag or env vars for both log level and file
## [3.6.1] - 2026-03-03
### ⚙️ Miscellaneous Tasks
- Switch allocator to mimalloc for ~20% perf in some cases
- Add bench plot scripts
## [3.6.0] - 2026-03-02
This version adds the Arinae algorithm as an option.
Arinae is designed to become skim's default algorithm in the future.
Technically, it uses Smith-Waterman and a modified Levenshtein distance with affine gaps for scoring, as well as multiple optimizations (the main ones being a loose prefilter and checks for early dismissal of paths that cannot lead to the best match). It also forbids typos on the first char of the query.
In practice, it should feel close to FZY's scoring with typos disabled, but with a more natural behavior regarding typos as Frizbee or other algorithms.
These other algorithms usually work by allowing a set number of typos using 3D matrices for computations, the max-typos value being set based on the length of the query. In practice, that meant that tes will match exactly, but test will allow one typo, meaning that typing a single character will change the filtered items completely. This algorithm will instead penalize typos, not block them completely.
This algorithm does not aim to revolution anything, but it aims at making typo-resistant fuzzy matching feel more like an actual alternative to the current options (namely FZF and FZY), while maintaining per-item performance at least as good as the current algorithms.
### 🚀 Features
- Merge ranks in AndOr engine matcher
- Add Arinae algorithm (#990)
- *(shell)* Colored history widgets & remove perl completely (#994)
### 🐛 Bug Fixes
- Make sure we drop Skim before returning the output
### 💼 Other
- Drop flake-utils, add formatter (#992)
### New Contributors
* @faukah made their first contribution in [#992](https://github.com/skim-rs/skim/pull/992)
## [3.5.0] - 2026-02-22
### 🚀 Features
- Add fzy matcher and `--typos`/`--no-typos` flag (#987)
### 🐛 Bug Fixes
- Correctly bind uppercase keys
- More precision on AndOr matches (closes #526)
- Respect the and & or priority
### ⚙️ Miscellaneous Tasks
- *(dep)* Remove unused dependency 'beef' (#986)
- *(dep)* Frizbee 0.8.1 (#985)
- Add partial bench to measure the time it takes to setup skim
- Pin dependencies to the latest exact version
### New Contributors
* @bitfehler made their first contribution in [#985](https://github.com/skim-rs/skim/pull/985)
* @Sisyphus1813 made their first contribution in [#983](https://github.com/skim-rs/skim/pull/983)
## [3.4.0] - 2026-02-19
### 🚀 Features
- Allow setting delimiters in `+` expansions (closes #935)
- Add set-header action (closes #768)
- Add `--print-current`, `--output-format` (closes #981)
- Add --ellipsis option
- Back to stable rust (#980)
### 🐛 Bug Fixes
- *(ci)* Do not run benches
### 💼 Other
- Add filter
### 📚 Documentation
- Benchmarks
### ⚙️ Miscellaneous Tasks
- Add rust benchmark
## [3.3.0] - 2026-02-18
### 🚀 Features
- Use a separate thread pool for Matcher runs (#961)
- Event-driven re-render (#949)
- Allow run_with to be run within a tokio runtime (#979)
### 🐛 Bug Fixes
- Possible None unwrap if ansi enabled but not in item
### ⚙️ Miscellaneous Tasks
- Unify filter mode & squeeze more perf (#974)
- Refactor Skim into its own file
### New Contributors
* @figsoda made their first contribution in [#979](https://github.com/skim-rs/skim/pull/979)
## [3.2.0] - 2026-02-13
### 🚀 Features
- Further reduce DefaultSkimItem size (#967)
### ⚙️ Miscellaneous Tasks
- Enhance PR template [skip ci]
## [3.1.1] - 2026-02-13
### 🐛 Bug Fixes
- Republish crate
## [3.1.0] - 2026-02-13
### 🚀 Features
- *(cli)* Add SKIM_OPTIONS_FILE (#972)
- Add set-preview-cmd action to change preview (#969)
## [3.0.1] - 2026-02-12
### 🐛 Bug Fixes
- Restart_matcher race condition (closes #970)
### 📚 Documentation
- Add link to sqlite_skim (#971) [skip ci]
### New Contributors
* @tzachar made their first contribution in [#971](https://github.com/skim-rs/skim/pull/971)
## [3.0.0] - 2026-02-12
### Changes
#### For everyone
Memory usage was optimized, bringing a ~25% reduction in memory usage in our benches. Thanks to @kimono-koans for the initial idea and help with this.
#### For CLI users
- Added a `:pty` preview-window flag that will make the preview run in a PTY. This allows for more interactive commands and more complicated display, including running `sk` itself inside `sk`'s preview (run `SKIM_DEFAULT_OPTIONS='--preview "sk" --preview-window ":pty"' sk` for the extreme version).
Note: the `pty` preview does not take input (for now). For instance, scrolling in paged output will not work.
- Readded `--sync` functionality after it was broken in v1
#### For library users
- A more fine-grained control over skim's event loop is now possible using `tokio` and the new methods on `Skim`. Check them out if you want to interact with the skim instance while it is running.
- [**breaking**] The `SkimOptionsBuilder`'s setter methods are back to taking raw `&str`s instead of `String`s. This should match the behavior pre-v1, sorry for the extra work that some of you already put in to migrate.
### 🚀 Features
- Interactive pty preview & concurrency optimizations (#952)
- Optimize match_item to use ref and not Arc (#962)
- Reduce DefaultSkimItem memory footprint by around 25% by default (#966)
- [**breaking**] Use smarter setters, remove the need for Some(...) and String::from() in setters
- Readd `--sync` functionality
- *(lib)* Add fine-grained control over skim's event loop (#968)
### 🐛 Bug Fixes
- Manually patch modifiers (closes #945)
- Implement cursor_pos_from_tty ourselves (#963)
- Run preview on move after selection
### 📚 Documentation
- Specify nightly version in README
- Update nightly version [skip ci]
- Detail `pty` preview window flag [skip ci]
### ⚙️ Miscellaneous Tasks
- Ignore coverage files [skip ci]
- Add coverage flag to README
- Test matchers
- Update frizbee and nightly version
- *(ci)* Make codecov less aggressive
### New Contributors
* @kimono-koans made their first contribution in [#962](https://github.com/skim-rs/skim/pull/962)
## [2.0.2] - 2026-02-02
### 🐛 Bug Fixes
- Fix preview_fn
### ⚙️ Miscellaneous Tasks
- Update interactive mode examples (closes #943)
## [2.0.1] - 2026-02-01
### 🐛 Bug Fixes
- *(linux)* Run preview in a PTY (closes #894) (#897)
## [2.0.0] - 2026-01-31
### 🚀 Features
- [**breaking**] **library** Send & receive items in batches (#938)
This should not affect TUI users other than in improving the app's performance
### ⚙️ Miscellaneous Tasks
- Add valgrind and thread sanitizer test profiles [skip ci]
## [1.11.2] - 2026-01-29
### 🐛 Bug Fixes
- Always make ctrl-d abort (closes #941)
## [1.11.1] - 2026-01-29
### 🐛 Bug Fixes
- Do not enter TUI early if the matcher needs restarting before ingestion done (closes #940)
### ⚙️ Miscellaneous Tasks
- Add nix flake for dev [skip ci]
- Reduce FPS for better performance
## [1.11.0] - 2026-01-27
### 🚀 Features
- Add custom action for lib usage (closes #537)
## [1.10.0] - 2026-01-27
### 🚀 Features
- Add `--normalize` to ignore accents etc. when matching (closes #453) (#914)
### 🐛 Bug Fixes
- *(frizbee)* Correctly compute max_typos
- Make `-1` reset highlights (#937)
- Always display item_list (closes #939)
## [1.9.1] - 2026-01-27
### 🐛 Bug Fixes
- Clear screen when not in fullscreenon bash & fish (#936)
### New Contributors
* @phanen made their first contribution in [#936](https://github.com/skim-rs/skim/pull/936)
## [1.9.0] - 2026-01-26
### 🚀 Features
- *(frizbee)* Adaptive max_typos value
- *(theme)* Add catppuccin themes
## [1.8.1] - 2026-01-26
### 🐛 Bug Fixes
- Correctly flush buffered stderr
## [1.8.0] - 2026-01-26
### 🚀 Features
- Parse ansi codes in prompt
### 🐛 Bug Fixes
- Header and header-lines order in reverse layout
- Correctly overlay header styles
- Make select-1 and exit-0 work again (closes #916) (#933)
- Fix cursor blinking (closes #932) (credits to @lilydjwg)
### 🧪 Testing
- Review snapshots
### ⚙️ Miscellaneous Tasks
- Add matrix room to readme
## [1.7.2] - 2026-01-25
### 🐛 Bug Fixes
- Correct cursor position when using reverse and border (closes #928)
## [1.7.1] - 2026-01-25
### 🐛 Bug Fixes
- Y cursor pos in reverse mode (closes #931)
## [1.7.0] - 2026-01-25
### 🚀 Features
- Add borders to all widgets (#930)
### 🐛 Bug Fixes
- Correctly merge base styles
- Correctly display all header lines
- Correctly toggle prompt on ToggleInteractive (closes #925)
- Fix printf sometimes replacing recursively
- Interrupt the reader thread when stopping
- Replace {n} with an empty string when no item is selected
- Revert case-insensitive action_chain
- Re-enable query/cmd-query distinction and switching
- Correctly compute character width for cursor display (closes #929)
### ⚙️ Miscellaneous Tasks
- Cleanup changelog [skip ci]
## [1.6.0] - 2026-01-23
### 🚀 Features
- Add `--remote` flag to call remote (`--listen`) instances (#915)
### 🐛 Bug Fixes
- Make no-sort work again
### 🧪 Testing
- Remove insta_ prefixes after finalizing tests migration
## [1.5.4] - 2026-01-23
### 🐛 Bug Fixes
- Do not override {} with {q} in interactive mode
- Remove unnecessary clone in printf
- Correctly merge styles & do not reset them by default (#918)
- Translate frizbee's byte indices into char indices
### 📚 Documentation
- Customize man page
## [1.5.3] - 2026-01-22
### 🐛 Bug Fixes
- Quote expanded items independently (#910)
- Escape last `;` in env var value before passing to tmux (#912)
### New Contributors
* @mathieu-lemay made their first contribution in [#912](https://github.com/skim-rs/skim/pull/912)
## [1.5.2] - 2026-01-22
### 🐛 Bug Fixes
- Ignore `{+}` expressions when splitting action chains (closes #910)
- Strip ansi from expanded items (#910)
## [1.5.1] - 2026-01-22
### 🐛 Bug Fixes
- Correctly expand `{+}` to current when no items are selected (cl… (#913)
## [1.5.0] - 2026-01-22
### 🚀 Features
- Add `set-query` action to update the input (closes #657) (#907)
### 🐛 Bug Fixes
- Make case option work with non-ascii input (closes #454)
### ⚙️ Miscellaneous Tasks
- Fix tests link in PR template [skip ci]
## [1.4.0] - 2026-01-21
### 🚀 Features
- Split-match (#906)
### 📚 Documentation
- Reflect need for nightly rust in install section [skip ci]
## [1.3.2] - 2026-01-21
### 🐛 Bug Fixes
- Better spinner debounce behavior to avoid flickering (closes #904)
### 📚 Documentation
- Update README install section
- Add details to interactive mode in manpage (closes #805) (#816)
### 🧪 Testing
- Use insta for applicable integration tests, making them cross-p… (#903)
## [1.3.1] - 2026-01-21
### 🐛 Bug Fixes
- Allow layout to override reverse (closes #901)
### 🧪 Testing
- Allow multiple bench runs for better consistency
- More reproducible and more precise bench [skip ci]
### ⚙️ Miscellaneous Tasks
- Optimized release builds
## [1.3.0] - 2026-01-20
### 🚀 Features
- Typo resistant matcher using frizbee from blink.cmp (#891)
## [1.2.0] - 2026-01-20
### 🚀 Features
- Add no-strip-ansi flag (#898)
### 🐛 Bug Fixes
- Run preview in a PTY (closes #894)
## [1.1.2] - 2026-01-20
### 🐛 Bug Fixes
- Half page down scrolls down
- Use ansi-stripped raw item in preview expansion
## [1.1.1] - 2026-01-19
### 🐛 Bug Fixes
- Use item text in printf
- Parse ansi codes in header
- Use item output for fields
### 🧪 Testing
- Fix preview_nul
### ⚙️ Miscellaneous Tasks
- Update crossterm version requirement to pass crates.io publish checks
## [1.1.0] - 2026-01-19
### 🚀 Features
- Wrap items
### 🐛 Bug Fixes
- Delete outside char boundaries
- Preview on large binaries does not hang or mangle the tui
### 🧪 Testing
- Fix wrap test (#896)
## [1.0.1] - 2026-01-19
### 🐛 Bug Fixes
- Disable compact_matcher feature
## [1.0.0-pre11] - 2026-01-17
### 🐛 Bug Fixes
- Always use cursor/selector colors (#892)
### 🧪 Testing
- Fix flaky tests
### ⚙️ Miscellaneous Tasks
- *(changelog)* Ignore release commits
## [1.0.0-pre10] - 2026-01-17
### 🐛 Bug Fixes
- Only expand selection in {+} for commands
### ⚙️ Miscellaneous Tasks
- Add pointer/marker as aliases for selector/multi-selector
## [1.0.0-pre9] - 2026-01-16
### 🐛 Bug Fixes
- Matcher race condition at startup
## [1.0.0-pre8] - 2026-01-16
### 🚀 Features
- Add print-header flag (and readd print-score) (closes #470)
### 🐛 Bug Fixes
- *(ui)* Use current highlight for the current item (closes #889) (#890)
### 🧪 Testing
- Remove useless listen tests
## [1.0.0-pre7] - 2026-01-16
### 🚀 Features
- Add `listen` flag (closes #719)
### 🐛 Bug Fixes
- Fix listen flag on macos (#888)
- Correctly parse wrap arg in preview options
### 🧪 Testing
- Add tests for listen flag
## [1.0.0-pre6] - 2026-01-15
### 🚀 Features
- Add cycle flag (closes #553)
- Add disabled flag (closes #500)
- Add nushell completion support (closes #459)
- Add --shell-bindings flag to get bindings at runtime
### 🐛 Bug Fixes
- Disable completions without cli feature
- Fix build without default features
### ⚙️ Miscellaneous Tasks
- Add exhaustive_match macro for enum building from str
## [1.0.0-pre5] - 2026-01-15
### 🚀 Features
- *(ui)* Add selector and multi-selector options to set the itemlist icons
- *(ui)* Allow setting modifiers (closes #871)
## [1.0.0-pre4] - 2026-01-14
### 🚀 Features
- 120 FPS
### 🐛 Bug Fixes
- *(cmd)* [**breaking**] Always use `sh` for all command executions
### ⚙️ Miscellaneous Tasks
- Regenerate CHANGELOG.md
## [1.0.0-pre3] - 2026-01-14
### 🐛 Bug Fixes
- Fix terminal height management
### ⚙️ Miscellaneous Tasks
- Release v1.0.0-pre3
## [1.0.0-pre2] - 2026-01-14
### 🚀 Features
- *(ci)* Add crates.io publish to release CI
### 🐛 Bug Fixes
- Manually acquire cursor pos (closes #885) (#886)
### ⚙️ Miscellaneous Tasks
- Remove unneeded deps (#884)
- Release
## [1.0.0-pre1] - 2026-01-13
### 🚀 Features
- *(ui)* [**breaking**] Ratatui migration (#864)
### ⚙️ Miscellaneous Tasks
- Remove workspace (#883)
### New Contributors
* @rusty-snake made their first contribution in [#872](https://github.com/skim-rs/skim/pull/872)
* @peccu made their first contribution in [#845](https://github.com/skim-rs/skim/pull/845)
* @azarmadr made their first contribution in [#841](https://github.com/skim-rs/skim/pull/841)
## [0.20.5] - 2025-08-09
### 🐛 Bug Fixes
- Compile without the cli feature (#834)
### ⚙️ Miscellaneous Tasks
- *(release)* Release (#835)
## [0.20.4] - 2025-08-02
### 🚀 Features
- *(e2e)* Add Dockerfile to run E2E
### 🐛 Bug Fixes
- *(options)* Allow border to be used without args
- *(ci)* Fetch whole history to avoid PR recreation
### ⚙️ Miscellaneous Tasks
- *(ci)* Revert to a more vanilla release-plz config
- Remove unreleased section from changelog
- *(release)* Release (#831)
## [0.20.3] - 2025-07-27
### ⚙️ Miscellaneous Tasks
- *(release)* Release (#826)
## [0.20.2] - 2025-06-29
### 📚 Documentation
- *(e2e)* Add contributing section (#817)
### ⚙️ Miscellaneous Tasks
- *(release)* Release (#818)
### New Contributors
* @azat made their first contribution in [#783](https://github.com/skim-rs/skim/pull/783)
## [0.20.1] - 2025-06-21
### 🐛 Bug Fixes
- Min-query-length in interactive mode (#814)
### ⚙️ Miscellaneous Tasks
- *(release)* Release (#815)
## [0.20.0] - 2025-06-21
### 🚀 Features
- *(ui)* Respect NO_COLOR environment variable (#804)
### ⚙️ Miscellaneous Tasks
- *(release)* Release (#813)
### New Contributors
* @saidelmark made their first contribution in [#804](https://github.com/skim-rs/skim/pull/804)
## [0.19.0] - 2025-06-21
### 🚀 Features
- Add min query length option (#806)
### ⚙️ Miscellaneous Tasks
- *(release)* Release (#811)
## [0.18.0] - 2025-05-30
### 🚀 Features
- *(shell)* Improve shell completion with dynamic generation (#790)
### 🐛 Bug Fixes
- *(ci)* Remove version from pr name
### 📚 Documentation
- *(contributing)* Refine guidelines for GPT-assisted development
- Improve theming documentation (#788)
- Improve wording in README and options.rs (#789)
### ⚙️ Miscellaneous Tasks
- Generate changelog
- *(release)* Release (#792)
## [0.17.3] - 2025-05-20
### 🐛 Bug Fixes
- *(shell)* Fix zsh tmux args in key bindings (#777)
- *(shell)* Remove duplocate tmux height arg fixes #776 (#778)
### 💼 Other
- Set keybinding right before printing special character (#774)
### ⚙️ Miscellaneous Tasks
- Generate changelog using git cliff
- *(release)* Release v0.17.3 (#782)
### New Contributors
* @ajeetdsouza made their first contribution in [#774](https://github.com/skim-rs/skim/pull/774)
## [0.17.2] - 2025-05-04
### 🐛 Bug Fixes
- *(tmux)* Force sh as shell for tmux mode (#765)
- *(ci)* Remove release commits filter
### ⚙️ Miscellaneous Tasks
- *(ci)* Remove temp workflow
- *(release)* Release v0.17.2 (#766)
## [0.17.1] - 2025-05-04
### 🚀 Features
- *(ci)* Manually update versions
### 🐛 Bug Fixes
- *(cargo)* Fix tuikit re-export
- *(ci)* More generic pr name
- *(ci)* Split release pr and gh release
- *(cargo)* Fix tuikit readme path
- *(ci)* Fix broken ci after migration
### 🧪 Testing
- *(ci)* Show context
- *(ci)* Test trigger (#761)
### ⚙️ Miscellaneous Tasks
- *(ci)* Only release after merge
- Release (#760)
- *(cargo)* Update to 2024 edition (#764)
- *(ci)* Update dependencies
## [0.17.0] - 2025-05-04
### 🐛 Bug Fixes
- Fix local dependencies
## [common-v0.1.0] - 2025-05-04
### 🚀 Features
- *(tui)* Add tuikit as workspace member and update (#741)
- *(shell)* Readd completions (#726) (#739)
### 🐛 Bug Fixes
- *(cargo)* Fix workspace packages
- *(ci)* Remove leftover package
- *(ci)* Add metadata to common package
### ⚙️ Miscellaneous Tasks
- *(tuikit)* Bring skim-rs/tuikit#43 (#743)
- *(ci)* Back to manifest release
- *(ci)* Readd manifest manually
- *(ci)* Revert action
- *(ci)* Use linked changelog
- *(ci)* Disable skim prefix in tag
- *(ci)* Test without extra packages
- *(ci)* Readd all components
- *(ci)* Release every package at the same version
- *(ci)* Release whole workspace at once
- *(ci)* Update manifest
- *(ci)* Readd all packages as well as root
- *(ci)* Better handling of packages in release
- *(ci)* Unlink versions
- *(ci)* Set package names
- *(ci)* Explicitely set root component
- *(ci)* Explicitely set last release sha
- *(ci)* Use previous versions for packages
- *(ci)* Migrate to release-plz
- *(ci)* Update release-plz changelog format
- *(ci)* Update release-plz changelog format
- *(ci)* Split release actions
- Release (#756)
- *(ci)* Do not publish extra packages
- *(ci)* Release on all commits
- *(ci)* Make local packages publishable
## [0.16.2] - 2025-04-26
### 🚀 Features
- *(zsh)* [**breaking**] Sort history items by timestamp
### 🐛 Bug Fixes
- *(tmux)* Check if TMUX is set (closes #734) (#736)
- *(filter)* Fix broken pipe while writing results to locked stdout (closes #733) (#737)
### 📚 Documentation
- *(tmux)* Add note about env var (#732)
- *(tmux)* Fix docs formatting
### 🧪 Testing
- *(ci)* Try a simpler release-please config
### ⚙️ Miscellaneous Tasks
- Move changelog to subdir (#740)
- *(master)* Release 0.16.2 (#738)
## [0.16.1] - 2025-03-06
### 🐛 Bug Fixes
- Hasten deprecation of expect after #703
### ⚙️ Miscellaneous Tasks
- Manually update release-please manifest after release
- *(master)* Release 0.16.1 (#712)
## [0.16.0] - 2025-01-23
### 🚀 Features
- Add preview callback (#407)
### 🐛 Bug Fixes
- *(docs)* Fix README lib example
- *(term)* Clamp height option (#690)
### 📚 Documentation
- *(readme)* Correct fzf library statement in README (#679)
### 🧪 Testing
- *(ci)* Test previous fixes
- *(ci)* Test previous fixes
- *(ci)* Try removing the packages altogether
### ⚙️ Miscellaneous Tasks
- Remove lazy_static (#687)
- Fix clippy warning in rust 1.84 (#688)
- *(ci)* Try to fix release-please on extra packages
- *(ci)* Do not search commits on e2e & xtask
- *(ci)* Try releasing as 0.1.0
- Release master (#672)
- Release master (#691)
### New Contributors
* @alexxbb made their first contribution in [#407](https://github.com/skim-rs/skim/pull/407)
* @alexandregv made their first contribution in [#679](https://github.com/skim-rs/skim/pull/679)
## [0.15.7] - 2024-12-27
### 🐛 Bug Fixes
- Remove atty (#671)
### ⚙️ Miscellaneous Tasks
- Release master (#670)
### New Contributors
* @gallois made their first contribution in [#671](https://github.com/skim-rs/skim/pull/671)
## [0.15.6] - 2024-12-26
### 🐛 Bug Fixes
- Fix non-functional vim plugin (#659)
- Update rank to follow the readded index tiebreak (#669)
### ⚙️ Miscellaneous Tasks
- Release master (#656)
### New Contributors
* @egrieco made their first contribution
* @dotdash made their first contribution in [#659](https://github.com/skim-rs/skim/pull/659)
## [0.15.5] - 2024-12-04
### 🐛 Bug Fixes
- Revert README overwrite
- Fix --tmux quoting (#643)
### 📚 Documentation
- Missing backtick in install commands (#646)
- Add note about fuzziness of interactive examples (fixes #543)
### ⚙️ Miscellaneous Tasks
- Release master (#647)
- Fix release-please config
- Fix release config
- Release master (#655)
### New Contributors
* @genskyff made their first contribution in [#646](https://github.com/skim-rs/skim/pull/646)
## [0.15.4] - 2024-12-01
### 🐛 Bug Fixes
- Fix token permissions for release file
- Clippy pedantic on lib.rs
### ⚙️ Miscellaneous Tasks
- Cargo fmt
- Release master (#642)
## [0.15.3] - 2024-12-01
### 🐛 Bug Fixes
- Fix missing var in CI
- Clippy pedantic on main.rs
### ⚙️ Miscellaneous Tasks
- Remove cli feature from skim
- Cargo fmt
- Release master (#641)
## [0.15.2] - 2024-12-01
### 🐛 Bug Fixes
- Do not run tests in release workflow
- Make item module public (closes #568)
### ⚙️ Miscellaneous Tasks
- Release master (#640)
### New Contributors
* @skim-rs-bot[bot] made their first contribution in [#640](https://github.com/skim-rs/skim/pull/640)
## [0.15.1] - 2024-12-01
### 🐛 Bug Fixes
- Fix ci
- Fix urls in cargo.toml
### ⚙️ Miscellaneous Tasks
- Generate files in PR (#638)
- Fix push
- Test push with explicit ref
- Use cache for xtask
- Simplify release ci
- Use PAT for release-please to trigger downstream ci
- Use gh app for token
- Use gh app for push
- Manually use gh app for push
- Skip ci on modified files
- Use token in checkout
- Exit success when nothing to commit
- Avoid duplicate test runs
- Cleanup
- Release master (#639)
## [0.15.0] - 2024-12-01
### 🚀 Features
- *(tui)* Add info hidden (#630)
### 🐛 Bug Fixes
- *(ci)* Fix clippy os
- *(ci)* Set release-please path
- Undo sk-tmux deprecation
- *(ci)* Release-please permissions on job level
- *(ci)* Use subpath for release-please outputs
- *(ci)* Remove needs in release-please condition
- *(ci)* Use different syntax for conditions
- *(ci)* Add intermediary step for release
- *(ci)* Use release-please in workspace root
- *(ci)* Test with different release-please config
- *(ci)* Set skim version
- *(ci)* Set skim changelog path
- *(ci)* Use absolute path for changelog
- *(ci)* Do not bump major
- *(ci)* Bump minor for feat
- *(ci)* Use correct tag
- *(ci)* Remove string from cond
- *(ci)* Fix templating
- *(ci)* Fix extra dot
- *(ci)* Use stable toolchain
- *(ci)* Remove extra modules
- *(ci)* Skip extra packages
- *(ci)* Replace underscore with dashes
- Set toolchain
### 🧪 Testing
- Migrate e2e to rust (#629)
- *(ci)* Try downgrading cargo.toml
- *(ci)* Test with crate root
- *(ci)* Test with subpath
- *(ci)* Add debug
- *(ci)* Fix dash in test
- *(ci)* Check for string
### ⚙️ Miscellaneous Tasks
- Readd crate to release-please
- Fix release-please target branch
- Fix condition
- Release master (#632)
- Release master (#633)
- Cleanup failed releases
- Release master (#634)
- Release master (#635)
- Release master (#636)
- Release master (#637)
### New Contributors
* @github-actions[bot] made their first contribution in [#637](https://github.com/skim-rs/skim/pull/637)
## [0.14.3] - 2024-11-28
### 🚀 Features
- Readd index tiebreak (#609)
- [**breaking**] Do not check for expect before printing the argument of accept… (#625)
- Add `--tmux` flag (deprecates sk-tmux, fixes #596) (#603)
### 🐛 Bug Fixes
- Allow combined multiple args (fixes #622) (#623)
### 📚 Documentation
- Update changelog from github releases (#620)
- Link all PRs, issues, commits and authors in CHANGELOG (#621)
- Add fzf-lua and nu_plugin_skim to the README (#626)
### ⚙️ Miscellaneous Tasks
- Bump unicode-width from 0.1.14 to 0.2.0 (#616)
- Bump nix from 0.25.1 to 0.29.0 (#614)
- Bump env_logger from 0.9.3 to 0.11.5 (#615)
- Improve PR ci (#617)
- Remove ci dir (#627)
### New Contributors
* @khafatech made their first contribution in [#605](https://github.com/skim-rs/skim/pull/605)
* @praveenperera made their first contribution in [#621](https://github.com/skim-rs/skim/pull/621)
## [0.13.0] - 2024-11-25
### 🚀 Features
- Allow more flexibility for use as a library (#613)
### ⚙️ Miscellaneous Tasks
- Add pull request template (#608)
## [0.12.0] - 2024-11-24
### 🚀 Features
- Add reload action (#604)
## [0.11.12] - 2024-11-24
### 🐛 Bug Fixes
- Remove index tiebreak from shell bindings (#611)
### ⚙️ Miscellaneous Tasks
- Remove some platform-specific quirkinesses from e2e (#602)
### New Contributors
* @crodjer made their first contribution in [#413](https://github.com/skim-rs/skim/pull/413)
## [0.11.11] - 2024-11-22
### 💼 Other
- Readd version arg (#606)
## [0.11.1] - 2024-11-21
### 🐛 Bug Fixes
- Fix github publish action
## [0.11.0] - 2024-11-20
### 🚀 Features
- Use clap & derive for options, manpage & completions (#586)
### 💼 Other
- "Package Managers": add Portage
- Remove unuseful entries (#382)
### 📚 Documentation
- *(discord)* Discord invitation link
### ⚙️ Miscellaneous Tasks
- Fix clippy
- Remove atty (#587)
- Remove bitflags (#579)
### New Contributors
* @LoricAndre made their first contribution in [#586](https://github.com/skim-rs/skim/pull/586)
* @otto-dev made their first contribution in [#468](https://github.com/skim-rs/skim/pull/468)
* @jgarte made their first contribution in [#487](https://github.com/skim-rs/skim/pull/487)
* @iamb4uc made their first contribution in [#560](https://github.com/skim-rs/skim/pull/560)
* @hellux made their first contribution in [#563](https://github.com/skim-rs/skim/pull/563)
* @reneegyllensvaan made their first contribution in [#461](https://github.com/skim-rs/skim/pull/461)
* @jirutka made their first contribution in [#449](https://github.com/skim-rs/skim/pull/449)
* @rspencer01 made their first contribution in [#433](https://github.com/skim-rs/skim/pull/433)
* @marcoieni made their first contribution in [#382](https://github.com/skim-rs/skim/pull/382)
* @ymnejmi made their first contribution in [#551](https://github.com/skim-rs/skim/pull/551)
* @sisrfeng made their first contribution
* @vitaly-zdanevich made their first contribution
## [0.10.2] - 2022-11-08
### 🐛 Bug Fixes
- Print version from Cargo.toml with latest clap
### New Contributors
* @anthraxx made their first contribution
## [0.10.0] - 2022-10-28
### ⚙️ Miscellaneous Tasks
- Update deps and fix lots of clippy lints
### New Contributors
* @yazgoo made their first contribution in [#472](https://github.com/skim-rs/skim/pull/472)
* @EdenEast made their first contribution
* @grant0417 made their first contribution
* @mgttlinger made their first contribution
* @TD-Sky made their first contribution
* @dependabot[bot] made their first contribution
* @io12 made their first contribution
* @terror made their first contribution
* @PCouaillier made their first contribution
* @sweenu made their first contribution
## [0.9.4] - 2021-02-15
### 💼 Other
- Update
### ⚙️ Miscellaneous Tasks
- *(cargo)* Fix documentation link
### New Contributors
* @x4121 made their first contribution
* @Mephistophiles made their first contribution
* @n8henrie made their first contribution
* @marcusbuffett made their first contribution
* @mb720 made their first contribution
* @pickfire made their first contribution
* @sirwindfield made their first contribution
## [0.9.3] - 2020-11-02
### 🐛 Bug Fixes
- Ansi parse error for multi-bytes string
## [0.9.1] - 2020-10-20
### 🚀 Features
- Support initial scroll for preview window
### 🐛 Bug Fixes
- Ansi merge fragments (typo)
- Tiebreak should contains score by default
- Reduce flickering of preview window
- Multiple preview options won't merge
- Clippy
- Pre-select-items select '' by default
- Preview's scroll could be 0
## [0.9.0] - 2020-10-18
### 🚀 Features
- Unicode spinner
- Implement `--keep-right`
- Support skip-to-pattern
### 🐛 Bug Fixes
- Orderedvec won't preserve insertion order
- Upgrade fuzzy-matcher to fix wrong matching indices
- Ensure the matching range is within bound
- Some options are broken (introduced by 08bc067)
- Do no auto scroll for customized items
- Multiple selection (regression in 1d72fca)
### 💼 Other
- Ansi color were not shown for DefaultSkimItem
### 🚜 Refactor
- Demangle lib and bin implementations
- Separate MatchResult from MatchedItem
### New Contributors
* @pkubik made their first contribution
* @wucke13 made their first contribution
## [0.8.2] - 2020-06-26
### 🐛 Bug Fixes
- Preview's fields should based on orig text
### 💼 Other
- Move filter function to binary
- Exit gracefully on SIGPIPE error(see PR#279)
- Handle print0 parameters correctly in filter mode
### 🚜 Refactor
- DefaultSkimItem now accept string
### New Contributors
* @marsam made their first contribution
* @caixiangyue made their first contribution
* @emmanueltouzery made their first contribution
* @BlindingDark made their first contribution
* @aldhsu made their first contribution
## [0.8.0] - 2020-02-23
### 🚀 Features
- Support left click event on selection list
### 🐛 Bug Fixes
- Ensure screen is rendered with item
### 💼 Other
- "enter" key not printed with expect keys
- Support case insensitive in exact mode
- Case insensitive + refactor engine
## [0.7.0] - 2020-01-15
### 💼 Other
- *(src/ansi.rs)* Use pattern match to destruct Option wrapper.
### 📚 Documentation
- Add installation instructions for arch linux
### ⚙️ Miscellaneous Tasks
- Update derive_builder to 0.9
### New Contributors
* @ammgws made their first contribution
* @alexreg made their first contribution
* @cireu made their first contribution
## [0.6.7] - 2019-05-31
### 💼 Other
- Use as a library: remove extraneous line in example code.
- Remove extraneous line.
- Remove extraneous line.
- Add crates.io svg.
### New Contributors
* @chmp made their first contribution
* @ngirard made their first contribution
## [0.6.5] - 2019-04-01
### 🐛 Bug Fixes
- Wrong matches on empty lines
## [0.6.3] - 2019-03-25
### 🐛 Bug Fixes
- Number of matched items not show correctly
- Matcher is slow to kill
## [0.6.2] - 2019-03-19
### 🚀 Features
- Header-lines
### 🐛 Bug Fixes
- Compilation error of examples
## [0.6.0] - 2019-03-17
### 💼 Other
- Rotate mode
## [0.5.3] - 2019-02-20
### 💼 Other
- Create new variable for lines used by skim
- Update usage string.
- Return slice instead of new vector
- Draw status after query
- Return early if possible
### New Contributors
* @dfreese made their first contribution
* @lilydjwg made their first contribution
* @RemiliaForever made their first contribution
* @bennyyip made their first contribution
* @Konfekt made their first contribution
* @Lompik made their first contribution
* @light4 made their first contribution
## [0.3.0] - 2017-09-21
### 🐛 Bug Fixes
- Main window did not earse correctly
- Some lines now shown if too long
- Skim cannot show empty lines
- Alternate screen is not switched off on exit
- Ansi color not shown correctly in main area
- Toggle will panic if there is no item matched
### New Contributors
* @tiziano88 made their first contribution
* @supermarin made their first contribution
## [0.2.1-beta.2] - 2017-01-19
### 🚜 Refactor
- Use filter_map instead of map then filter
### New Contributors
* @anchepiece made their first contribution
* @brookst made their first contribution
* @SirVer made their first contribution
* @akiradeveloper made their first contribution
## [0.2.0] - 2017-01-03
### 🐛 Bug Fixes
- Model will not redraw from the 1 line
- Reader: reader and sender will lock each other.
### New Contributors
* @leoyvens made their first contribution
* @mohamedhayibor made their first contribution
## [0.1.1-rc2] - 2016-07-19
### 🐛 Bug Fixes
- #4 exit with non-zero status on cancel.
- Fields result in incorrect output with ANSI enabled.
### 💼 Other
- Remove debug code
## [0.1-alpha] - 2016-07-01
### New Contributors
* @lotabout made their first contribution
* @ made their first contribution
<!-- generated by git-cliff -->
================================================
FILE: Cargo.toml
================================================
[package]
name = "skim"
version = "4.0.0"
authors = ["Loric ANDRE", "Zhang Jinzhou <lotabout@gmail.com>"]
description = "Fuzzy Finder in rust!"
documentation = "https://docs.rs/skim"
homepage = "https://github.com/skim-rs/skim"
repository = "https://github.com/skim-rs/skim"
readme = "README.md"
keywords = ["fuzzy", "menu", "util"]
license = "MIT"
edition = "2024"
[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
[lib]
name = "skim"
path = "src/lib.rs"
[[bin]]
name = "sk"
path = "src/bin/main.rs"
required-features = ["cli"]
[dependencies]
assert_enum_variants = "=0.1.2"
clap = { version = "=4.6.0" , optional = true, features = ["cargo", "derive", "unstable-markdown"] }
clap_complete = { version = "=4.6.0", optional = true }
clap_mangen = { version = "=0.2.33", optional = true }
derive_builder = "=0.20.2"
env_logger = { version = "=0.11.9", optional = true, features = ["humantime"] }
indexmap = "=2.13.0"
log = "=0.4.29"
nix = { version = "=0.31.2", features = ["fs", "poll"] }
rand = "=0.10.0"
rayon = "=1.11.0"
regex = "=1.12.3"
shell-quote = "=0.7.2"
shlex = { version = "=1.3.0", optional = true }
tokio = { version = "=1.50.0", features = ["macros", "net", "rt-multi-thread", "sync", "time", "tokio-macros"] }
which = "=8.0.2"
ratatui = "=0.30.0"
color-eyre = "=0.6.5"
ansi-to-tui = "=8.0.1"
futures = "=0.3.32"
tokio-util = "=0.7.18"
thiserror = "=2.0.18"
tempfile = "=3.27.0"
crossterm = { version = ">=0.0.0", features = ["event-stream", "use-dev-tty", "libc"] }
thread_local = "=1.1.9"
memchr = "=2.8.0"
clap_complete_nushell = "=4.5.10"
interprocess = { version = "=2.4.0", features = ["tokio"] }
serde = { version = "=1.0.228", features = ["derive"] }
ron = "=0.12.0"
frizbee = { version = "=0.8.3" }
roff = "=1.0.0"
unicode-display-width = "=0.3.0"
unicode-normalization = "=0.1.25"
derive_more = { version = "=2.1.1", features = ["debug", "eq"] }
portable-pty = "=0.9.0"
tui-term = "=0.3.2"
kanal = "=0.1.1"
mimalloc = { version = "0.1.48", features = ["v3"] }
gungraun = { version = "0.17.2", optional = true }
[features]
default = ["cli"]
# Everyting needed to use skim as a cli (argument parsing, shell integrations...)
cli = ["dep:clap", "dep:clap_complete", "dep:shlex", "dep:env_logger", "dep:clap_mangen"]
# Enable test utilities (e.g., Tui::new_for_test)
test-utils = []
# Enable gungraun (Valgrind-based) benchmarks
gungraun = ["dep:gungraun"]
[dev-dependencies]
criterion = { version = "0.8.2", features = ["async_tokio"] }
insta = "1.46"
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(feature, values("test-utils", "gungraun"))', 'cfg(coverage, coverage_nightly)'] }
[[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"]
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2016 Jinzhou Zhang
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
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.
================================================
FILE: README.md
================================================
<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://codecov.io/gh/skim-rs/skim" >
<img src="https://codecov.io/gh/skim-rs/skim/graph/badge.svg?token=RtIDgmDdzX" alt="codecov 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!
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.
[](https://asciinema.org/a/pIfwazaM0mTHA8F7qRbjrqOnm)
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 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](#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 from fzf](#differences-from-fzf)
- [How to contribute](#how-to-contribute)
- [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 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
| 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` |
<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>
### Fedora
Up to date Fedora packages are provided via an unofficial community-maintained COPR repository.
```bash
sudo dnf copr enable sisyphus1813/skim
sudo dnf install skim
```
## Manually
Any of the following applies:
- Using the install script:
```sh
# 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: 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:
```sh
$ git clone --depth 1 git@github.com:skim-rs/skim.git ~/.skim
$ cd ~/.skim
$ cargo build --release
$ # 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 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
Here are some examples to get you started:
```bash
# directly invoke skim
sk
# Or pipe some input to it (press TAB key to select multiple items when -m is enabled)
vim $(find . -name "*.rs" | sk -m)
```
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
`skim` can invoke other commands dynamically. Normally you would want to
integrate it with [grep](https://www.gnu.org/software/grep/),
[ack](https://github.com/petdance/ack2),
[ag](https://github.com/ggreer/the_silver_searcher), or
[rg](https://github.com/BurntSushi/ripgrep) for searching contents in a
project directory:
```sh
# works with grep
sk --ansi -i -c 'grep -rI --color=always --line-number {q} .'
# works with ack
sk --ansi -i -c 'ack --color {q}'
# works with ag
sk --ansi -i -c 'ag --color {q}'
# works with rg
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.

## 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`:
> **Note:** While PowerShell completions are supported, Windows is not supported for now.
#### 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:
| Key | Action |
|------------------:|--------------------------------------------|
| Enter | Accept (select current one and quit) |
| ESC/Ctrl-G | Abort |
| Ctrl-P/Up | Move cursor up |
| Ctrl-N/Down | Move cursor Down |
| TAB | Toggle selection and move down (with `-m`) |
| Shift-TAB | Toggle selection and move up (with `-m`) |
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` borrows `fzf`'s syntax for matching items:
| Token | Match type | Description |
|----------|----------------------------|-----------------------------------|
| `text` | fuzzy-match | items that match `text` |
| `^music` | prefix-exact-match | items that start with `music` |
| `.mp3$` | suffix-exact-match | items that end with `.mp3` |
| `'wild` | exact-match (quoted) | items that include `wild` |
| `!fire` | inverse-exact-match | items that do not include `fire` |
| `!.mp3$` | inverse-suffix-exact-match | items that do not end with `.mp3` |
`skim` also supports the combination of tokens.
- Whitespace has the meaning of `AND`. With the term `src main`, `skim` will search
for items that match **both** `src` and `main`.
- ` | ` 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. For example, `readme .md$ | .markdown$` is interpreted as
`readme AND (.md$ OR .markdown$)`.
- 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
```
You can switch to `regex` mode dynamically by pressing `Ctrl-R` (Rotate Mode).
## exit code
| 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
The doc here is only a preview, please check the man page (`man sk`) for a full
list of options.
## Keymap
Specify the bindings with comma separated pairs (no space allowed). For example:
```sh
sk --bind 'alt-a:select-all,alt-d:deselect-all'
```
Additionally, use `+` to concatenate actions, such as `execute-silent(echo {} | pbcopy)+abort`.
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
specify how the records are sorted by `sk --tiebreak score,index,-begin` or any
other order you want.
## 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]
```
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=light,fg:232,bg:255,current_bg:116,info:27
```
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` |
### 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
- `--ansi`: to parse ANSI color codes (e.g., `\e[32mABC`) of the data source
- `--regex`: use the query as regular expression to match the data source
# Advanced Topics
## Interactive mode
In **interactive mode**, you can invoke a command dynamically. Try it out:
```sh
sk --ansi -i -c 'rg --color=always --line-number {q}'
```
### How does it work?

- 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 and defaults to `SKIM_DEFAULT_COMMAND`
- `-i` tells skim to open command prompt on startup, which will show `c>` by default.
To further narrow down the results returned by the command, press
`Ctrl-Q` to toggle interactive mode.
## Executing external programs
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
# Press CTRL-Y to copy the line to clipboard and aborts skim (requires pbcopy)
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`([crate](https://crates.io/frizbee), the typo-resistant algorithm used in 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, 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` 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 {q}' --preview "preview.sh {}"
```
(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:

### How does it work?
If the preview command is given by the `--preview` option, skim will replace the
`{}` with the current highlighted line surrounded by single quotes, call the
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](#filds-support).
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
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`.
## Fields support
Normally only plugin users need to understand this.
For example, you have the data source with the format:
```sh
<filename>:<line number>:<column number>
```
However, you want to search `<filename>` only when typing in queries. That
means when you type `21`, you want to find a `<filename>` that contains `21`,
but not matching line number or column number.
You can use `sk --delimiter ':' --nth 1` to achieve this.
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.
- `..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
field, including `end`.
## Use as a library
Skim can be used as a library in your Rust crates.
First, add skim into your `Cargo.toml`:
```toml
[dependencies]
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
extern crate skim;
use skim::prelude::*;
use std::io::Cursor;
pub fn main() {
let options = SkimOptionsBuilder::default()
.height("50%")
.multi(true)
.build()
.unwrap();
let input = "aaaaa\nbbbb\nccc".to_string();
// `SkimItemReader` is a helper to turn any `BufRead` into a stream of `SkimItem`
// `SkimItem` was implemented for `AsRef<str>` by default
let item_reader = SkimItemReader::default();
let items = item_reader.of_bufread(Cursor::new(input));
// `run_with` would read and show items from the stream
let selected_items = Skim::run_with(&options, Some(items))
.map(|out| out.selected_items)
.unwrap_or_else(|| Vec::new());
for item in selected_items.iter() {
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:
- `SkimItemReceiver` is `crossbeam::channel::Receiver<Arc<dyn SkimItem>>`
- If it is none, it will invoke the given command and read items from command output
- Otherwise, it will read the items from the (crossbeam) channel.
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` or `String` into `BufRead`),
so that you could deal with strings or files easily.
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.
It uses a 10 million path-like ASCII items input file, and the query test.
```
=== Results: sk v4.0.0 [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: sk v3.7.0 ===
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: fzf 0.70.0 ===
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: sk v4.0.0) ===
Binary Avg time Δ time Avg rate Δ rate
------------------------------------------------------------------------------
sk v4.0.0 3.827s baseline 2615767 baseline
sk v3.7.0 3.930s +2.7% 2548674 -2.6%
fzf 0.70.0 5.421s +41.7% 1848269 -29.3%
```
TL;DR: sk v4.0.0 is ~30% faster than fzf 0.70.0 with a third of the CPU usage and less memory usage
# FAQ
## How to ignore files?
Skim invokes `find .` to fetch a list of files for filtering. You can override
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 ."
$ sk
```
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 may find some
of your files not shown.
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 ."
```
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 from fzf
[fzf](https://github.com/junegunn/fzf) is a command-line fuzzy finder written
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` 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/skim-rs/skim/issues/new) if you encounter any bugs
or have any ideas. Pull requests are warmly welcomed.
# 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
## Shell script
The `bench.py` script is available to benchmark the code against other versions or fzf using `tmux` and querying the output. This is by no means a precise or foolproof way of running benchmarks, but it has the added benefit of allowing us to benchmark against `fzf` and of giving us resource metrics.
You can use it directly using `./bench.py <binary> -n <number of items> -r <number of runs>`, or generate the data using `./bench.py -g <output file> -n <number of items>`, then `./bench.py <binary> -f <file> -r <number of runs>`
### Criterion benchmarks
Criterion benchmarks are available to measure skim's performance more precisely.
To run them, you need to generate input data using `./bench.py -g benches/fixtures/10M.txt -n 10000000 && ./bench.py -g benches/fixtures/1M.txt -n 1000000`, then run `cargo bench -j 1`.
These will run for several minutes.
================================================
FILE: bench.py
================================================
#!/usr/bin/env python3
"""
Benchmark script to measure ingestion + matching rate in skim interactive mode.
This measures how fast skim can ingest items and display matched results.
Usage: bench.py [BINARY_PATH ...] [-n|--num-items NUM] [-q|--query QUERY]
[-r|--runs RUNS] [-w|--warmup N] [-f|--file FILE]
[-g|--generate-file FILE] [-j|--json] [-- EXTRA_ARGS...]
Arguments:
BINARY_PATH ... One or more paths to binaries (default: ./target/release/sk)
When multiple are given they run in round-robin and the
first is used as the baseline for +/- comparisons.
-n, --num-items NUM Number of items to generate (default: 1000000)
-q, --query QUERY Query string to search (default: "test")
-r, --runs RUNS Number of benchmark runs per binary (default: 1)
-w, --warmup N Number of warmup runs per binary (default: 1)
-f, --file FILE Use existing file as input instead of generating
-g, --generate-file FILE Generate test data to file and exit
-j, --json Output results as JSON
-- Pass remaining arguments to the binary
Examples:
./bench.py # Use defaults
./bench.py ./target/release/sk -n 500000 -q foo
./bench.py ./old/sk ./new/sk -r 5 # Compare two binaries
./bench.py -r 5 # Run 5 times and show average
./bench.py -f input.txt -q search # Use existing file
./bench.py -g testdata.txt -n 2000000 # Generate file and exit
"""
import json
import math
import os
import random
import subprocess
import sys
import tempfile
import time
# ---------------------------------------------------------------------------
# Defaults
# ---------------------------------------------------------------------------
DEFAULT_BINARY = "./target/release/sk"
DEFAULT_NUM_ITEMS = 1_000_000
DEFAULT_QUERY = "test"
DEFAULT_RUNS = 1
DEFAULT_WARMUP = 1
# Stability / timeout tuning (mirrors bench.sh values)
REQUIRED_STABLE_S = 5.0 # seconds the matched count must be unchanged
MAX_WAIT_S = 60.0 # hard timeout per run
CHECK_INTERVAL_S = 0.05 # polling interval
# ---------------------------------------------------------------------------
# Test-data generation
# ---------------------------------------------------------------------------
WORDS = [
"home",
"usr",
"etc",
"var",
"opt",
"tmp",
"dev",
"proc",
"sys",
"lib",
"bin",
"sbin",
"boot",
"mnt",
"media",
"src",
"test",
"config",
"data",
"logs",
"cache",
"backup",
"docs",
"images",
"videos",
"audio",
"downloads",
"uploads",
"temp",
"shared",
]
def generate_test_data(output_file: str, num_items: int) -> None:
rng = random.Random()
with open(output_file, "w") as fh:
for i in range(1, num_items + 1):
depth = rng.randint(2, 10)
parts = [rng.choice(WORDS) for _ in range(depth)]
fh.write("/".join(parts) + f"_{i}\n")
# ---------------------------------------------------------------------------
# Argument parsing (no argparse – stdlib only, but argparse IS stdlib…
# however, to keep the spirit of "no dependencies" we use manual parsing
# since argparse is a stdlib module, not a third-party dep. We use it.)
# ---------------------------------------------------------------------------
def parse_args(argv):
"""Return (binaries, opts, extra_args)."""
import argparse
# Split off everything after "--"
extra_args = []
if "--" in argv:
sep_idx = argv.index("--")
extra_args = argv[sep_idx + 1 :]
argv = argv[:sep_idx]
parser = argparse.ArgumentParser(
description="Skim benchmark script",
add_help=True,
)
parser.add_argument(
"binaries",
nargs="*",
metavar="BINARY_PATH",
help="Path(s) to binary (default: ./target/release/sk)",
)
parser.add_argument("-n", "--num-items", type=int, default=DEFAULT_NUM_ITEMS)
parser.add_argument("-q", "--query", default=DEFAULT_QUERY)
parser.add_argument("-r", "--runs", type=int, default=DEFAULT_RUNS)
parser.add_argument("-w", "--warmup", type=int, default=DEFAULT_WARMUP)
parser.add_argument("-f", "--file", default="")
parser.add_argument("-g", "--generate-file", default="")
parser.add_argument("-j", "--json", action="store_true")
opts = parser.parse_args(argv)
if not opts.binaries:
opts.binaries = [DEFAULT_BINARY]
if opts.file and opts.generate_file:
parser.error("Cannot use both --file and --generate-file")
return opts.binaries, opts, extra_args
# ---------------------------------------------------------------------------
# Resource monitor (background thread)
# ---------------------------------------------------------------------------
import threading
class ResourceMonitor(threading.Thread):
"""Sample CPU and RSS of *pid* every 50 ms until the process exits."""
def __init__(self, pid: int):
super().__init__(daemon=True)
self.pid = pid
self.peak_mem_kb: int = 0 # RSS in kB
self.peak_cpu: float = 0.0 # %CPU
def run(self):
while True:
try:
result = subprocess.run(
["ps", "-p", str(self.pid), "-o", "rss=,%cpu="],
capture_output=True,
text=True,
)
line = result.stdout.strip()
if not line:
break
parts = line.split()
if len(parts) >= 2:
try:
mem = int(parts[0])
cpu = float(parts[1])
if mem > self.peak_mem_kb:
self.peak_mem_kb = mem
if cpu > self.peak_cpu:
self.peak_cpu = cpu
except ValueError:
pass
except Exception:
break
time.sleep(0.05)
# ---------------------------------------------------------------------------
# Single run
# ---------------------------------------------------------------------------
def _find_sk_pid(pane_pid: int, binary_path: str) -> int:
"""Try for up to 5 s to find the sk child PID under *pane_pid*."""
for _ in range(50):
time.sleep(0.1)
try:
result = subprocess.run(
["pgrep", "-P", str(pane_pid), "-f", binary_path],
capture_output=True,
text=True,
)
pids = result.stdout.strip().splitlines()
if pids:
return int(pids[0])
except Exception:
pass
return 0
def run_once(
binary_path: str,
query: str,
tmp_file: str,
num_items: int,
extra_args: list,
run_index: int,
session_suffix: str,
) -> dict:
"""
Execute one benchmark run against *binary_path*.
Returns a dict with keys: elapsed_s, rate, matched, peak_mem_kb, peak_cpu,
completed.
"""
session_name = f"skim_bench_{os.getpid()}_{session_suffix}_{run_index}"
status_fd, status_file = tempfile.mkstemp(prefix="skim_bench_status_")
os.close(status_fd)
env = os.environ.copy()
env["SHELL"] = "/bin/sh"
env.pop("HISTFILE", None)
env.pop("FZF_DEFAULT_OPTS", None)
env.pop("SKIM_DEFAULT_OPTIONS", None)
try:
# Create tmux session
subprocess.run(
["tmux", "new-session", "-s", session_name, "-d"],
check=True,
env=env,
capture_output=True,
)
# Clear env vars inside the session
for cmd in [
"unset HISTFILE",
"unset FZF_DEFAULT_OPTS",
"unset SKIM_DEFAULT_OPTIONS",
]:
subprocess.run(
["tmux", "send-keys", "-t", session_name, cmd, "Enter"],
check=True,
capture_output=True,
)
time.sleep(0.1)
# Build the command string
extra_str = " ".join(extra_args)
cmd_str = f"cat {tmp_file} | {binary_path} --query '{query}' {extra_str}"
subprocess.run(
["tmux", "send-keys", "-t", session_name, cmd_str, "Enter"],
check=True,
capture_output=True,
)
start_ns = time.perf_counter_ns()
# Locate sk PID for resource monitoring
pane_pid = 0
try:
r = subprocess.run(
["tmux", "list-panes", "-t", session_name, "-F", "#{pane_pid}"],
capture_output=True,
text=True,
)
pane_pid = int(r.stdout.strip().splitlines()[0])
except Exception:
pass
sk_pid = 0
monitor = None
if pane_pid:
sk_pid = _find_sk_pid(pane_pid, binary_path)
if sk_pid:
monitor = ResourceMonitor(sk_pid)
monitor.start()
# Poll for matcher completion
completed = False
matched_count = 0
prev_matched_count = -1
stable_start: float = 0.0
end_ns = 0
loop_start = time.monotonic()
while True:
time.sleep(CHECK_INTERVAL_S)
now = time.monotonic()
if now - loop_start >= MAX_WAIT_S:
break
# Early exit if sk process is gone
if sk_pid:
try:
os.kill(sk_pid, 0)
except ProcessLookupError:
break
# Capture tmux pane
try:
subprocess.run(
[
"tmux",
"capture-pane",
"-b",
f"status-{session_name}",
"-t",
session_name,
],
capture_output=True,
)
subprocess.run(
[
"tmux",
"save-buffer",
"-b",
f"status-{session_name}",
status_file,
],
capture_output=True,
)
except Exception:
continue
# Parse "matched/total" from status line
try:
with open(status_file) as fh:
content = fh.read()
except OSError:
continue
import re
m = re.search(r"(\d+)/(\d+)", content)
if not m:
continue
mc = int(m.group(1))
total = int(m.group(2))
if total == num_items:
if mc != prev_matched_count:
prev_matched_count = mc
matched_count = mc
stable_start = time.monotonic()
end_ns = time.perf_counter_ns()
elif stable_start > 0:
if time.monotonic() - stable_start >= REQUIRED_STABLE_S:
completed = True
break
if end_ns == 0:
end_ns = time.perf_counter_ns()
# Exit skim
subprocess.run(
["tmux", "send-keys", "-t", session_name, "Escape"],
capture_output=True,
)
time.sleep(0.1)
# Wait for monitor
if monitor is not None:
monitor.join(timeout=2.0)
elapsed_s = (end_ns - start_ns) / 1e9
rate = num_items / elapsed_s if elapsed_s > 0 else 0
peak_mem_kb = monitor.peak_mem_kb if monitor and monitor.peak_mem_kb else 0
peak_cpu = monitor.peak_cpu if monitor and monitor.peak_cpu else 0.0
return {
"elapsed_s": elapsed_s,
"rate": rate,
"matched": matched_count,
"peak_mem_kb": peak_mem_kb if peak_mem_kb else None,
"peak_cpu": peak_cpu if peak_cpu else None,
"completed": completed,
}
finally:
subprocess.run(
["tmux", "kill-session", "-t", session_name],
capture_output=True,
)
try:
os.unlink(status_file)
except OSError:
pass
# ---------------------------------------------------------------------------
# Aggregate statistics
# ---------------------------------------------------------------------------
def _avg(values):
vals = [v for v in values if v is not None]
return sum(vals) / len(vals) if vals else None
def _min(values):
vals = [v for v in values if v is not None]
return min(vals) if vals else None
def _max(values):
vals = [v for v in values if v is not None]
return max(vals) if vals else None
def aggregate(results: list) -> dict:
completed_results = [r for r in results if r["completed"]]
times = [r["elapsed_s"] for r in completed_results]
rates = [r["rate"] for r in completed_results]
matched = [r["matched"] for r in completed_results]
mems = [r["peak_mem_kb"] for r in completed_results]
cpus = [r["peak_cpu"] for r in completed_results]
completed = len(completed_results)
return {
"completed": completed,
"runs": len(results),
"avg_time": _avg(times),
"min_time": _min(times),
"max_time": _max(times),
"avg_rate": _avg(rates),
"min_rate": _min(rates),
"max_rate": _max(rates),
"avg_matched": _avg(matched),
"min_matched": _min(matched),
"max_matched": _max(matched),
"avg_mem": _avg(mems),
"min_mem": _min(mems),
"max_mem": _max(mems),
"avg_cpu": _avg(cpus),
"min_cpu": _min(cpus),
"max_cpu": _max(cpus),
}
# ---------------------------------------------------------------------------
# Formatting helpers
# ---------------------------------------------------------------------------
def _pct(baseline, value):
"""Return a +/-XX.X% string comparing *value* to *baseline*."""
if baseline is None or value is None or baseline == 0:
return ""
diff = (value - baseline) / abs(baseline) * 100
sign = "+" if diff >= 0 else ""
return f"{sign}{diff:.1f}%"
def _fmt_mem(kb):
if kb is None:
return None
return kb / 1024 # MB
def _fmt_optional(value, fmt):
if value is None:
return "N/A"
return fmt.format(value)
# ---------------------------------------------------------------------------
# Output
# ---------------------------------------------------------------------------
def print_human(
binary_label: str,
agg: dict,
num_items: int,
baseline: dict | None = None,
is_baseline: bool = False,
):
tag = " [baseline]" if is_baseline else ""
print(f"\n=== Results: {binary_label}{tag} ===")
print(f"Completed runs: {agg['completed']} / {agg['runs']}")
def cmp(key, baseline_key=None):
"""Format comparison vs baseline for a given aggregate key."""
bk = baseline_key or key
if baseline is None or is_baseline:
return ""
return " " + _pct(baseline.get(bk), agg.get(key))
# Items matched
avg_m = _fmt_optional(agg["avg_matched"], "{:.0f}")
min_m = _fmt_optional(agg["min_matched"], "{:.0f}")
max_m = _fmt_optional(agg["max_matched"], "{:.0f}")
print(
f"Average items matched: {avg_m} / {num_items}"
f" (min: {min_m}, max: {max_m})"
f"{cmp('avg_matched')}"
)
# Time
avg_t = _fmt_optional(agg["avg_time"], "{:.3f}s")
min_t = _fmt_optional(agg["min_time"], "{:.3f}s")
max_t = _fmt_optional(agg["max_time"], "{:.3f}s")
# Lower time is better, so flip sign for display
time_cmp = ""
if (
baseline
and not is_baseline
and baseline.get("avg_time")
and agg.get("avg_time")
):
diff = (
(agg["avg_time"] - baseline["avg_time"]) / abs(baseline["avg_time"]) * 100
)
sign = "+" if diff >= 0 else ""
time_cmp = f" {sign}{diff:.1f}%"
print(f"Average time: {avg_t} (min: {min_t}, max: {max_t}){time_cmp}")
# Rate
avg_r = _fmt_optional(agg["avg_rate"], "{:.0f}")
min_r = _fmt_optional(agg["min_rate"], "{:.0f}")
max_r = _fmt_optional(agg["max_rate"], "{:.0f}")
print(
f"Average items/second: {avg_r} (min: {min_r}, max: {max_r}){cmp('avg_rate')}"
)
# Memory
if agg["avg_mem"] is not None:
avg_mb = _fmt_mem(agg["avg_mem"])
min_mb = _fmt_mem(agg["min_mem"])
max_mb = _fmt_mem(agg["max_mem"])
print(
f"Average peak memory usage: {avg_mb:.1f} MB"
f" (min: {min_mb:.1f} MB, max: {max_mb:.1f} MB)"
f"{cmp('avg_mem')}"
)
# CPU
if agg["avg_cpu"] is not None:
avg_c = _fmt_optional(agg["avg_cpu"], "{:.1f}%")
min_c = _fmt_optional(agg["min_cpu"], "{:.1f}%")
max_c = _fmt_optional(agg["max_cpu"], "{:.1f}%")
print(
f"Average peak CPU usage: {avg_c}"
f" (min: {min_c}, max: {max_c})"
f"{cmp('avg_cpu')}"
)
def print_json_multi(binaries: list, aggregates: list, num_items: int, runs: int):
output = []
for binary, agg in zip(binaries, aggregates):
entry = {
"binary": binary,
"num_items": num_items,
"runs": runs,
"completed_runs": agg["completed"],
"items_matched": {
"avg": agg["avg_matched"],
"min": agg["min_matched"],
"max": agg["max_matched"],
},
"time_s": {
"avg": agg["avg_time"],
"min": agg["min_time"],
"max": agg["max_time"],
},
"items_per_second": {
"avg": agg["avg_rate"],
"min": agg["min_rate"],
"max": agg["max_rate"],
},
"peak_memory_kb": {
"avg": agg["avg_mem"],
"min": agg["min_mem"],
"max": agg["max_mem"],
},
"peak_cpu": {
"avg": agg["avg_cpu"],
"min": agg["min_cpu"],
"max": agg["max_cpu"],
},
}
output.append(entry)
print(json.dumps(output if len(output) > 1 else output[0]))
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
def main():
import re # ensure import at top of main scope for run_once
binaries, opts, extra_args = parse_args(sys.argv[1:])
num_items = opts.num_items
query = opts.query
runs = opts.runs
warmup = opts.warmup
input_file = opts.file
generate_file = opts.generate_file
as_json = opts.json
# ---- generate-file mode ------------------------------------------------
if generate_file:
print(f"Generating {num_items} items to {generate_file}...", file=sys.stderr)
generate_test_data(generate_file, num_items)
print(f"Generated {num_items} items successfully", file=sys.stderr)
return
# ---- prepare input data ------------------------------------------------
cleanup_input = False
if input_file:
if not os.path.isfile(input_file):
print(f"Error: Input file '{input_file}' not found", file=sys.stderr)
sys.exit(1)
tmp_file = input_file
with open(input_file) as fh:
num_items = sum(1 for _ in fh)
print(f"Using input file with {num_items} items", file=sys.stderr)
else:
fd, tmp_file = tempfile.mkstemp(prefix="skim_bench_input_")
os.close(fd)
cleanup_input = True
print("Generating test data...", file=sys.stderr)
generate_test_data(tmp_file, num_items)
try:
# ---- header ---------------------------------------------------------
binary_list = ", ".join(binaries)
print(f"=== Skim Ingestion + Matching Benchmark ===", file=sys.stderr)
print(
f"Binaries: {binary_list} | Items: {num_items} | "
f"Query: '{query}' | Warmup: {warmup} | Runs: {runs} (per binary)",
file=sys.stderr,
)
if input_file:
print(f"Input file: {input_file}", file=sys.stderr)
if extra_args:
print(f"Extra args: {' '.join(extra_args)}", file=sys.stderr)
# ---- warmup (results discarded) -------------------------------------
if warmup > 0:
print(f"\n=== Warmup ({warmup} run(s) per binary) ===", file=sys.stderr)
for bi, binary in enumerate(binaries):
for wu in range(1, warmup + 1):
print(
f" Warmup {wu}/{warmup} — {binary} ...",
file=sys.stderr,
)
run_once(
binary_path=binary,
query=query,
tmp_file=tmp_file,
num_items=num_items,
extra_args=extra_args,
run_index=wu,
session_suffix=f"warmup_b{bi}",
)
# ---- run benchmark in round-robin -----------------------------------
# all_results[i] = list of per-run dicts for binaries[i]
all_results = [[] for _ in binaries]
for run_num in range(1, runs + 1):
for bi, binary in enumerate(binaries):
label = f"[{os.path.basename(binary)}]"
if runs > 1 or len(binaries) > 1:
print(
f"\n=== Run {run_num}/{runs} — binary {bi + 1}/{len(binaries)}: {binary} ===",
file=sys.stderr,
)
result = run_once(
binary_path=binary,
query=query,
tmp_file=tmp_file,
num_items=num_items,
extra_args=extra_args,
run_index=run_num,
session_suffix=f"b{bi}",
)
all_results[bi].append(result)
if runs > 1 or len(binaries) > 1:
status = "COMPLETED" if result["completed"] else "TIMEOUT"
print(f"Status: {status}", file=sys.stderr)
print(
f"Items matched: {result['matched']} / {num_items}",
file=sys.stderr,
)
print(f"Total time: {result['elapsed_s']:.3f}s", file=sys.stderr)
print(f"Items/second: {result['rate']:.0f}", file=sys.stderr)
if result["peak_mem_kb"]:
print(
f"Peak memory usage: {result['peak_mem_kb'] / 1024:.1f} MB",
file=sys.stderr,
)
if result["peak_cpu"]:
print(
f"Peak CPU usage: {result['peak_cpu']:.1f}%",
file=sys.stderr,
)
# ---- aggregate ------------------------------------------------------
aggregates = [aggregate(all_results[i]) for i in range(len(binaries))]
# ---- output ---------------------------------------------------------
if as_json:
print_json_multi(binaries, aggregates, num_items, runs)
else:
baseline_agg = aggregates[0]
for i, (binary, agg) in enumerate(zip(binaries, aggregates)):
print_human(
binary_label=binary,
agg=agg,
num_items=num_items,
baseline=baseline_agg if len(binaries) > 1 else None,
is_baseline=(i == 0),
)
# Summary comparison table when multiple binaries
if len(binaries) > 1:
print(f"\n=== Comparison Summary (vs baseline: {binaries[0]}) ===")
header = f"{'Binary':<40} {'Avg time':>12} {'Δ time':>10} {'Avg rate':>14} {'Δ rate':>10}"
print(header)
print("-" * len(header))
for i, (binary, agg) in enumerate(zip(binaries, aggregates)):
t = (
f"{agg['avg_time']:.3f}s"
if agg["avg_time"] is not None
else "N/A"
)
r = (
f"{agg['avg_rate']:.0f}"
if agg["avg_rate"] is not None
else "N/A"
)
if i == 0:
dt = "baseline"
dr = "baseline"
else:
dt = _pct(baseline_agg["avg_time"], agg["avg_time"])
dr = _pct(baseline_agg["avg_rate"], agg["avg_rate"])
name = os.path.basename(binary) if len(binary) > 40 else binary
print(f"{name:<40} {t:>12} {dt:>10} {r:>14} {dr:>10}")
finally:
if cleanup_input:
try:
os.unlink(tmp_file)
except OSError:
pass
if __name__ == "__main__":
main()
================================================
FILE: benches/filter.rs
================================================
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,
);
});
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,
);
});
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,
);
});
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,
);
});
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);
================================================
FILE: benches/gungraun.rs
================================================
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);
================================================
FILE: benches/matcher_micro.rs
================================================
//! 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;
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
});
});
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
});
});
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);
================================================
FILE: benches/partial.rs
================================================
use std::io::{BufWriter, Stderr};
use clap::Parser as _;
use criterion::{Criterion, criterion_group, criterion_main};
use ratatui::prelude::CrosstermBackend;
use skim::prelude::*;
fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("parse_options", |b| {
b.iter(|| SkimOptions::parse_from(Vec::<&str>::new()));
});
c.bench_function("init", |b| {
b.iter_batched(
|| SkimOptions::default().build(),
|options: SkimOptions| Skim::<CrosstermBackend<BufWriter<Stderr>>>::init(options, None),
criterion::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))
},
criterion::BatchSize::SmallInput,
);
});
c.bench_function("start", |b| {
b.iter_batched(
|| Skim::<CrosstermBackend<BufWriter<Stderr>>>::init(SkimOptions::default().build(), None).unwrap(),
|mut skim: Skim| skim.start(),
criterion::BatchSize::SmallInput,
);
});
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);
================================================
FILE: benches/read_and_match.rs
================================================
use color_eyre::eyre::{Ok, Result};
use criterion::{Criterion, criterion_group, criterion_main};
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);
});
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
});
});
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);
================================================
FILE: bin/sk-tmux
================================================
#!/usr/bin/env bash
# The MIT License (MIT)
#
# Copyright (c) 2019 Jinzhou Zhang
# Copyright (c) 2016 Junegunn Choi
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# 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.
#
# Modified by Jinzhouz Zhang
# 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
}
sk="$(command -v sk 2> /dev/null)" || sk="$(dirname "$0")/sk"
[[ -x "$sk" ]] || fail 'sk executable not found'
tmux_args=()
args=()
opt=""
skip=""
swap=""
close=""
term=""
[[ -n "$LINES" ]] && lines=$LINES || lines=$(tput lines) || lines=$(tmux display-message -p "#{pane_height}")
[[ -n "$COLUMNS" ]] && columns=$COLUMNS || columns=$(tput cols) || columns=$(tmux display-message -p "#{pane_width}")
help() {
>&2 echo 'usage: sk-tmux [LAYOUT OPTIONS] [--] [SK OPTIONS]
LAYOUT OPTIONS:
(default layout: -d 50%)
Popup window (requires tmux 3.2 or above):
-p [WIDTH[%][,HEIGHT[%]]] (default: 50%)
-w WIDTH[%]
-h HEIGHT[%]
-x COL
-y ROW
Split pane:
-u [HEIGHT[%]] Split above (up)
-d [HEIGHT[%]] Split below (down)
-l [WIDTH[%]] Split left
-r [WIDTH[%]] Split right
'
exit
}
while [[ $# -gt 0 ]]; do
arg="$1"
shift
[[ -z "$skip" ]] && case "$arg" in
-)
term=1
;;
--help)
help
;;
--version)
echo "sk-tmux (with sk $("$sk" --version))"
exit
;;
-p*|-w*|-h*|-x*|-y*|-d*|-u*|-r*|-l*)
if [[ "$arg" =~ ^-[pwhxy] ]]; then
[[ "$opt" =~ "-E" ]] || opt="-E"
elif [[ "$arg" =~ ^.[lr] ]]; then
opt="-h"
if [[ "$arg" =~ ^.l ]]; then
opt="$opt -d"
swap="; swap-pane -D ; select-pane -L"
close="; tmux swap-pane -D"
fi
else
opt=""
if [[ "$arg" =~ ^.u ]]; then
opt="$opt -d"
swap="; swap-pane -D ; select-pane -U"
close="; tmux swap-pane -D"
fi
fi
if [[ ${#arg} -gt 2 ]]; then
size="${arg:2}"
else
if [[ "$1" =~ ^[0-9%,]+$ ]] || [[ "$1" =~ ^[A-Z]$ ]]; then
size="$1"
shift
else
continue
fi
fi
if [[ "$arg" =~ ^-p ]]; then
if [[ -n "$size" ]]; then
w=${size%%,*}
h=${size##*,}
opt="$opt -w$w -h$h"
fi
elif [[ "$arg" =~ ^-[whxy] ]]; then
opt="$opt ${arg:0:2}$size"
elif [[ "$size" =~ %$ ]]; then
size=${size:0:((${#size}-1))}
if [[ -n "$swap" ]]; then
opt="$opt -p $(( 100 - size ))"
else
opt="$opt -p $size"
fi
else
if [[ -n "$swap" ]]; then
if [[ "$arg" =~ ^.l ]]; then
max=$columns
else
max=$lines
fi
size=$(( max - size ))
[[ $size -lt 0 ]] && size=0
opt="$opt -l $size"
else
opt="$opt -l $size"
fi
fi
;;
--)
# "--" can be used to separate sk-tmux options from sk options to
# avoid conflicts
skip=1
tmux_args=("${args[@]}")
args=()
continue
;;
*)
args+=("$arg")
;;
esac
[[ -n "$skip" ]] && args+=("$arg")
done
if [[ -z "$TMUX" ]]; then
"$sk" "${args[@]}"
exit $?
fi
# --height option is not allowed
args=("${args[@]}" "--no-height")
# Handle zoomed tmux pane without popup options by moving it to a temp window
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'")
tmux swap-pane -t $tmp_window \; select-window -t $tmp_window
fi
set -e
# Clean up named pipes on exit
id=$RANDOM
argsf="${TMPDIR:-/tmp}/sk-args-$id"
fifo1="${TMPDIR:-/tmp}/sk-fifo1-$id"
fifo2="${TMPDIR:-/tmp}/sk-fifo2-$id"
fifo3="${TMPDIR:-/tmp}/sk-fifo3-$id"
tmux_win_opts=( $(tmux show-window-options remain-on-exit \; show-window-options synchronize-panes | sed '/ off/d; s/^/set-window-option /; s/$/ \\;/') )
cleanup() {
\rm -f $argsf $fifo1 $fifo2 $fifo3
# Restore tmux window options
if [[ "${#tmux_win_opts[@]}" -gt 0 ]]; then
eval "tmux ${tmux_win_opts[*]}"
fi
# Remove temp window if we were zoomed without popup options
if [[ -n "$zoomed_without_popup" ]]; then
tmux display-message -p "#{window_id}" > /dev/null
tmux swap-pane -t $original_window \; \
select-window -t $original_window \; \
kill-window -t $tmp_window \; \
resize-pane -Z
fi
if [ $# -gt 0 ]; then
trap - EXIT
exit 130
fi
}
trap 'cleanup 1' SIGUSR1
trap 'cleanup' EXIT
envs="export TERM=$TERM "
[[ "$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"
# Build arguments to sk
opts=$(printf "%q " "${args[@]}")
pppid=$$
echo -n "trap 'kill -SIGUSR1 -$pppid' EXIT SIGINT SIGTERM;" >> $argsf
close="; trap - EXIT SIGINT SIGTERM $close"
export TMUX=$(cut -d , -f 1,2 <<< "$TMUX")
mkfifo -m o+w $fifo2
if [[ "$opt" =~ "-E" ]]; then
cat $fifo2 &
if [[ -n "$term" ]] || [[ -t 0 ]]; then
cat <<< "\"$sk\" $opts > $fifo2; out=\$? $close; exit \$out" >> $argsf
else
mkfifo $fifo1
cat <<< "\"$sk\" $opts < $fifo1 > $fifo2; out=\$? $close; exit \$out" >> $argsf
cat <&0 > $fifo1 &
fi
tmux popup -d "$PWD" "${tmux_args[@]}" $opt "bash $argsf" > /dev/null 2>&1
exit $?
fi
mkfifo -m o+w $fifo3
if [[ -n "$term" ]] || [[ -t 0 ]]; then
cat <<< "\"$sk\" $opts > $fifo2; echo \$? > $fifo3 $close" >> $argsf
else
mkfifo $fifo1
cat <<< "\"$sk\" $opts < $fifo1 > $fifo2; echo \$? > $fifo3 $close" >> $argsf
cat <&0 > $fifo1 &
fi
tmux set-window-option synchronize-panes off \;\
set-window-option remain-on-exit off \;\
split-window -c "$PWD" $opt "${tmux_args[@]}" "bash -c 'exec -a sk bash $argsf'" $swap \
> /dev/null 2>&1 || { "$sk" "${args[@]}"; exit $?; }
cat $fifo2
exit "$(cat $fifo3)"
================================================
FILE: cliff.toml
================================================
# 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 = "^chore\\(release\\): prepare for", skip = true },
{ message = "^chore\\(deps.*\\)", skip = true },
{ message = "^chore\\(pr\\)", skip = true },
{ message = "^chore\\(pull\\)", skip = true },
{ message = "^chore|^ci", group = "<!-- 7 -->⚙️ Miscellaneous Tasks" },
{ body = ".*security", group = "<!-- 8 -->🛡️ Security" },
{ message = "^revert", group = "<!-- 9 -->◀️ Revert" },
{ message = "^release", skip = true },
{ message = ".*", group = "<!-- 10 -->💼 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
================================================
FILE: codecov.yml
================================================
# Make Codecov statuses informational so coverage is visible but won't fail PRs
coverage:
status:
default_rules:
flag_coverage_not_uploaded_behavior: pass
project:
default:
target: auto
threshold: 0
informational: true
patch:
default:
target: auto
threshold: 0
informational: true
================================================
FILE: dist-workspace.toml
================================================
[workspace]
members = ["cargo:."]
# Config for 'dist'
[dist]
# The preferred dist version to use in CI (Cargo.toml SemVer syntax)
cargo-dist-version = "0.30.3"
# CI backends to support
ci = "github"
# The installers to generate for each app
installers = ["shell"]
# Target platforms to build apps for (Rust target-triple syntax)
targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl"]
# Path that installers should place binaries in
install-path = "CARGO_HOME"
# Whether to install an updater program
install-updater = false
include = ["./man/", "./shell/"]
plan-jobs = ["./test"]
publish-jobs = ["./publish"]
publish-prereleases = true
================================================
FILE: examples/ansi.rs
================================================
extern crate skim;
use skim::{prelude::*, reader::CommandCollector};
pub 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.iter() {
println!("selected: {}", item.output());
}
}
================================================
FILE: examples/async.rs
================================================
use skim::{Skim, prelude::SkimOptionsBuilder};
#[tokio::main]
async fn main() {
let options = SkimOptionsBuilder::default().build().unwrap();
Skim::run_with(options, None).unwrap();
}
================================================
FILE: examples/base.rs
================================================
use skim::prelude::*;
fn main() -> color_eyre::Result<()> {
let opts = SkimOptionsBuilder::default().multi(true).reverse(true).build()?;
let res = Skim::run_items(opts, ["hello", "world"])?;
for item in res.selected_items {
println!("Selected {} (id {})", item.output(), item.rank.index);
}
Ok(())
}
================================================
FILE: examples/cmd_collector.rs
================================================
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.iter() {
println!("{}", item.output());
}
}
================================================
FILE: examples/custom_action_keybinding.rs
================================================
extern crate skim;
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use skim::prelude::*;
use skim::tui::event::{Action, ActionCallback, Event};
use std::io::Cursor;
/// This example demonstrates how to bind custom action callbacks to keyboard shortcuts.
///
/// 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!");
}
}
================================================
FILE: examples/custom_item.rs
================================================
extern crate skim;
use skim::prelude::*;
struct MyItem {
inner: String,
}
impl SkimItem for MyItem {
fn text(&self) -> Cow<'_, str> {
Cow::Borrowed(&self.inner)
}
fn preview(&self, _context: PreviewContext) -> ItemPreview {
if self.inner.starts_with("color") {
ItemPreview::AnsiText(format!("\x1b[31mhello:\x1b[m\n{}", self.inner))
} else {
ItemPreview::Text(format!("hello:\n{}", self.inner))
}
}
}
fn main() {
let options = SkimOptionsBuilder::default()
.height("50%")
.multi(true)
.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(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))
.map(|out| out.selected_items)
.unwrap_or_default();
for item in selected_items.iter() {
println!("{}", item.output());
}
}
================================================
FILE: examples/custom_keybinding_actions.rs
================================================
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}`...");
}
fn fake_create_item(item: &str) {
println!("Creating a new item `{item}`...");
}
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".into(), "enter:accept".into()])
.build()
.unwrap();
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()),
_ => (),
}
};
}
================================================
FILE: examples/downcast.rs
================================================
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> {
Cow::Borrowed(&self.text)
}
fn preview(&self, _context: PreviewContext) -> ItemPreview {
ItemPreview::Text(self.text.to_owned())
}
}
pub fn main() {
let options = SkimOptionsBuilder::default()
.height("50%")
.multi(true)
.preview("")
.build()
.unwrap();
let (tx, rx): (SkimItemSender, SkimItemReceiver) = unbounded();
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))
.map(|out| out.selected_items)
.unwrap_or_default()
.iter()
.map(|selected_item| selected_item.downcast_item::<Item>().unwrap().to_owned())
.collect::<Vec<Item>>();
for item in selected_items {
println!("{item:?}");
}
}
================================================
FILE: examples/fine-grain.rs
================================================
extern crate skim;
use color_eyre::Result;
use skim::prelude::*;
#[tokio::main(flavor = "current_thread")]
pub 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.iter() {
println!("{}", item.output());
}
Ok(())
}
================================================
FILE: examples/fuzzy_matcher_fz.rs
================================================
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;
pub fn main() {
let args: Vec<String> = env::args().collect();
// arg parsing (manually)
let mut arg_iter = args.iter().skip(1);
let mut pattern = "".to_string();
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 = arg.to_string();
}
}
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") | Some("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
}
================================================
FILE: examples/multiple_runs.rs
================================================
use skim::{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(target_os = "linux")]
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));
}
}
================================================
FILE: examples/nth.rs
================================================
extern crate skim;
use skim::prelude::*;
use std::io::Cursor;
/// `nth` option is supported by SkimItemReader.
/// In the example below, with `nth=2` set, only `123` could be matched.
pub fn main() {
let input = "foo 123";
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))
.map(|out| out.selected_items)
.unwrap_or_default();
for item in selected_items.iter() {
println!("{}", item.output());
}
}
================================================
FILE: examples/option_builder.rs
================================================
extern crate skim;
use skim::prelude::*;
use std::io::Cursor;
pub 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))
.map(|out| out.selected_items)
.unwrap_or_default();
for item in selected_items.iter() {
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))
.map(|out| out.selected_items)
.unwrap_or_default();
for item in selected_items.iter() {
println!("{}", item.output());
}
}
================================================
FILE: examples/preview_callback.rs
================================================
use std::io::Cursor;
use skim::prelude::*;
pub 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.iter() {
println!("{}", item.output());
}
}
================================================
FILE: examples/receiver_multi.rs
================================================
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),
);
}
================================================
FILE: examples/sample.rs
================================================
extern crate skim;
use skim::prelude::*;
pub fn main() {
let options = SkimOptions::default();
let selected_items = Skim::run_with(options, None)
.map(|out| out.selected_items)
.unwrap_or_default();
for item in selected_items.iter() {
println!("{}", item.output());
}
}
================================================
FILE: examples/selector.rs
================================================
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)
}
}
pub 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.iter() {
println!("{}", item.output());
}
}
================================================
FILE: examples/tick.rs
================================================
use skim::prelude::*;
#[tokio::main]
pub async fn main() -> color_eyre::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());
color_eyre::eyre::Ok(())
}
================================================
FILE: flake.nix
================================================
{
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 = inputs.nixpkgs.legacyPackages;
in {
devShells = eachSystem (system: {
default = pkgsFor.${system}.mkShellNoCC {
packages = with pkgsFor.${system}; [
cargo-nextest
cargo-insta
cargo-llvm-cov
cargo-edit
cargo-public-api
git-cliff
libclang
binutils
tmux
rustup
just
hyperfine
uv
valgrind
python313Packages.matplotlib
python313Packages.requests
];
shellHook = let
pkgs = pkgsFor.${system};
in ''
export LIBCLANG_PATH="${pkgs.libclang.lib}/lib"
export LD_LIBRARY_PATH="${pkgs.valgrind.out}/lib:$LD_LIBRARY_PATH"
'';
};
});
formatter = eachSystem (system: pkgsFor.${system}.nixfmt);
};
}
================================================
FILE: justfile
================================================
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:
git switch master
git pull
just release $(git cliff --bumped-version | sed 's/v\(.*\)/\1/')
test target="":
cargo test --doc
-cargo nextest run --features test-utils {{ target }}
tmux kill-session -t skim_e2e
================================================
FILE: man/man1/sk-tmux.1
================================================
.ig
The MIT License (MIT)
Copyright (c) 2019 Jinzhou Zhang
Copyright (c) 2017 Junegunn Choi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
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.10.4" "sk-tmux - open sk in tmux split pane"
.SH NAME
sk-tmux - open sk in tmux split pane
.SH SYNOPSIS
.B sk-tmux [-u|-d [HEIGHT[%]]] [-l|-r [WIDTH[%]]] [--] [sk OPTIONS]
.SH DESCRIPTION
sk-tmux is a wrapper script for sk that opens sk in a tmux split pane. It is
designed to work just like sk except that it does not take up the whole
screen. You can safely use sk-tmux instead of sk in your scripts as the extra
options will be silently ignored if you're not on tmux.
.SH OPTIONS
.SS Layout
(default: \fB-d 50%\fR)
.TP
.B "-u [height[%]]"
Split above (up)
.TP
.B "-d [height[%]]"
Split below (down)
.TP
.B "-l [width[%]]"
Split left
.TP
.B "-r [width[%]]"
Split right
================================================
FILE: man/man1/sk.1
================================================
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.TH sk 1 "sk 4.0.0"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.SH NAME
sk \- Fuzzy Finder in rust!
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.SH SYNOPSIS
\fBsk\fR [\fB\-\-tac\fR] [\fB\-\-min\-query\-length\fR] [\fB\-\-no\-sort\fR] [\fB\-t\fR|\fB\-\-tiebreak\fR] [\fB\-n\fR|\fB\-\-nth\fR] [\fB\-\-with\-nth\fR] [\fB\-d\fR|\fB\-\-delimiter\fR] [\fB\-e\fR|\fB\-\-exact\fR] [\fB\-\-regex\fR] [\fB\-\-algo\fR] [\fB\-\-case\fR] [\fB\-\-typos\fR] [\fB\-\-no\-typos\fR] [\fB\-\-normalize\fR] [\fB\-\-split\-match\fR] [\fB\-\-last\-match\fR] [\fB\-\-scheme\fR] [\fB\-b\fR|\fB\-\-bind\fR] [\fB\-m\fR|\fB\-\-multi\fR] [\fB\-\-no\-multi\fR] [\fB\-\-no\-mouse\fR] [\fB\-c\fR|\fB\-\-cmd\fR] [\fB\-i\fR|\fB\-\-interactive\fR] [\fB\-I \fR] [\fB\-\-color\fR] [\fB\-\-no\-hscroll\fR] [\fB\-\-keep\-right\fR] [\fB\-\-skip\-to\-pattern\fR] [\fB\-\-no\-clear\-if\-empty\fR] [\fB\-\-no\-clear\-start\fR] [\fB\-\-no\-clear\fR] [\fB\-\-show\-cmd\-error\fR] [\fB\-\-cycle\fR] [\fB\-\-disabled\fR] [\fB\-\-layout\fR] [\fB\-\-reverse\fR] [\fB\-\-height\fR] [\fB\-\-no\-height\fR] [\fB\-\-min\-height\fR] [\fB\-\-margin\fR] [\fB\-p\fR|\fB\-\-prompt\fR] [\fB\-\-cmd\-prompt\fR] [\fB\-\-selector\fR] [\fB\-\-multi\-selector\fR] [\fB\-\-ansi\fR] [\fB\-\-tabstop\fR] [\fB\-\-info\fR] [\fB\-\-no\-info\fR] [\fB\-\-inline\-info\fR] [\fB\-\-header\fR] [\fB\-\-header\-lines\fR] [\fB\-\-border\fR] [\fB\-\-wrap\fR] [\fB\-\-history\fR] [\fB\-\-history\-size\fR] [\fB\-\-cmd\-history\fR] [\fB\-\-cmd\-history\-size\fR] [\fB\-\-preview\fR] [\fB\-\-preview\-window\fR] [\fB\-q\fR|\fB\-\-query\fR] [\fB\-\-cmd\-query\fR] [\fB\-\-read0\fR] [\fB\-\-print0\fR] [\fB\-\-print\-query\fR] [\fB\-\-print\-cmd\fR] [\fB\-\-print\-score\fR] [\fB\-\-print\-header\fR] [\fB\-\-print\-current\fR] [\fB\-\-output\-format\fR] [\fB\-\-no\-strip\-ansi\fR] [\fB\-1\fR|\fB\-\-select\-1\fR] [\fB\-0\fR|\fB\-\-exit\-0\fR] [\fB\-\-sync\fR] [\fB\-\-pre\-select\-n\fR] [\fB\-\-pre\-select\-pat\fR] [\fB\-\-pre\-select\-items\fR] [\fB\-\-pre\-select\-file\fR] [\fB\-f\fR|\fB\-\-filter\fR] [\fB\-\-shell\fR] [\fB\-\-shell\-bindings\fR] [\fB\-\-man\fR] [\fB\-\-listen\fR] [\fB\-\-remote\fR] [\fB\-\-tmux\fR] [\fB\-\-log\-level\fR] [\fB\-\-log\-file\fR] [\fB\-\-expect\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR]
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.SH OPTIONS
.TP
\fB\-h\fR, \fB\-\-help\fR
Print help (see a summary with \*(Aq\-h\*(Aq)
.TP
\fB\-V\fR, \fB\-\-version\fR
Print version
.SH SEARCH
.TP
\fB\-\-tac\fR
Show results in reverse order
Often used in combination with \-\-no\-sort
.TP
\fB\-\-min\-query\-length\fR \fI<MIN_QUERY_LENGTH>\fR
Minimum query length to start showing results
Only show results when the query is at least this many characters long
.TP
\fB\-\-no\-sort\fR
Do not sort the results
Often used in combination with \-\-tac Example: history | sk \-\-tac \-\-no\-sort
.TP
\fB\-t\fR, \fB\-\-tiebreak\fR \fI<TIEBREAK>\fR [default: score,begin,end]
Comma\-separated list of sort criteria to apply when the scores are tied.
* **score**: Score of the fuzzy match algorithm
\- Each criterion could be negated, e.g. (\-index)
\- Each criterion should appear only once in the list
.br
.br
\fIPossible values:\fR
.RS 14
.IP \(bu 2
score
.IP \(bu 2
\-score
.IP \(bu 2
begin
.IP \(bu 2
\-begin
.IP \(bu 2
end
.IP \(bu 2
\-end
.IP \(bu 2
length
.IP \(bu 2
\-length
.IP \(bu 2
index
.IP \(bu 2
\-index
.IP \(bu 2
pathname
.IP \(bu 2
\-pathname
.RE
.TP
\fB\-n\fR, \fB\-\-nth\fR \fI<NTH>\fR [default: ]
Fields to be matched
A field index expression can be a non\-zero integer or a range expression (`[BEGIN]..[END]`).
`\-\-nth` and `\-\-with\-nth` take a comma\-separated list of field index expressions.
**Examples:**
1 The 1st field
2 The 2nd field
\-1 The last field
\-2 The 2nd to last field
3..5 From the 3rd field to the 5th field
2.. From the 2nd field to the last field
..\-3 From the 1st field to the 3rd to the last field
.. All the fields
.TP
\fB\-\-with\-nth\fR \fI<WITH_NTH>\fR [default: ]
Fields to be transformed
See nth for the details
.TP
\fB\-d\fR, \fB\-\-delimiter\fR \fI<DELIMITER>\fR [default: [\\t\\n ]+]
Delimiter between fields
In regex format, default to AWK\-style. Escape sequences like \\x00, \\t, \\n are supported.
.TP
\fB\-e\fR, \fB\-\-exact\fR
Run in exact mode
.TP
\fB\-\-regex\fR
Start in regex mode instead of fuzzy\-match
.TP
\fB\-\-algo\fR \fI<ALGORITHM>\fR [default: arinae]
Fuzzy matching algorithm
arinae (ari) Latest algorithm
`skim_v2` Legacy skim algorithm
clangd Used in clangd for keyword completion
fzy Algorithm from fzy (<https://github.com/jhawthorn/fzy>)
.br
.br
\fIPossible values:\fR
.RS 14
.IP \(bu 2
skim_v2: Improved skim fuzzy matching algorithm (v2)
.IP \(bu 2
clangd: Clangd fuzzy matching algorithm
.IP \(bu 2
fzy: Fzy matching algorithm (https://github.com/jhawthorn/fzy)
.IP \(bu 2
frizbee: Frizbee matching algorithm, typo resistant
.IP \(bu 2
arinae: Arinae: typo\-resistant & natural algorithm, default
.RE
.TP
\fB\-\-case\fR \fI<CASE>\fR [default: smart]
Case sensitivity
Determines whether or not to ignore case while matching Note: this is not used for the Frizbee matcher, it uses a penalty system to favor case\-sensitivity without enforcing it
.br
.br
\fIPossible values:\fR
.RS 14
.IP \(bu 2
respect: Case\-sensitive matching
.IP \(bu 2
ignore: Case\-insensitive matching
.IP \(bu 2
smart: Smart case: case\-insensitive unless query contains uppercase
.RE
.TP
\fB\-\-typos\fR [\fI<TYPOS>\fR] [default: disabled]
Enable typo\-tolerant matching
When passed without a value (\-\-typos), uses adaptive formula (pattern_length / 4). When passed with a value (e.g. \-\-typos=2), uses that exact number as the maximum allowed typos. \-\-typos=0 explicitly disables typo tolerance. Applies to both fzy and frizbee matchers.
.TP
\fB\-\-no\-typos\fR
Disable typo\-resistant matching
.TP
\fB\-\-normalize\fR
Normalize unicode characters
When set, normalize accents and other unicode diacritics/others
.TP
\fB\-\-split\-match\fR [\fI<SPLIT_MATCH>...\fR]
Enable split matching and set delimiter
Split matching runs the matcher in splits: foo:bar will match all items matching foo, then :, then bar if the delimiter is present, or match normally if not.
.TP
\fB\-\-last\-match\fR
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
.TP
\fB\-\-scheme\fR \fI<SCHEME>\fR [default: default]
.br
\fIPossible values:\fR
.RS 14
.IP \(bu 2
default: Default scheme, no modifications to the options
.IP \(bu 2
path: Path scheme: will find the furthest match in the item and set pathname as the main tiebreak
.IP \(bu 2
history: History scheme: will force index as the first tiebreak
.RE
.SH INTERFACE
.TP
\fB\-b\fR, \fB\-\-bind\fR [\fI<BIND>...\fR] [default: ]
Comma separated list of bindings
You can customize key bindings of sk with `\-\-bind` option which takes a comma\-separated list of
key binding expressions. Each key binding expression follows the following format: `<key>:<action>`
See the [KEYBINDS] section for details
**Example**: `sk \-\-bind=ctrl\-j:accept,ctrl\-k:kill\-line`
## Multiple actions can be chained using + separator.
**Example**: `sk \-\-bind \*(Aqctrl\-a:select\-all+accept\*(Aq`
# Special behaviors
With `execute(...)` and `reload(...)` action, you can execute arbitrary commands without leaving sk.
For example, you can turn sk into a simple file browser by binding enter key to less command like follows:
```bash
sk \-\-bind "enter:execute(less {})"
```
Note: if no argument is supplied to reload, the default command is run.
You can use the same placeholder expressions as in \-\-preview.
sk switches to the alternate screen when executing a command. However, if the command is ex‐
pected to complete quickly, and you are not interested in its output, you might want to use exe‐
cute\-silent instead, which silently executes the command without the switching. Note that sk
will not be responsive until the command is complete. For asynchronous execution, start your
command as a background process (i.e. appending &).
With if\-query\-empty and if\-query\-not\-empty action, you could specify the action to execute de‐
pends on the query condition. For example:
`sk \-\-bind \*(Aqctrl\-d:if\-query\-empty(abort)+delete\-char\*(Aq`
If the query is empty, skim will execute abort action, otherwise execute delete\-char action. It
is equal to ‘delete\-char/eof‘.
.TP
\fB\-m\fR, \fB\-\-multi\fR
Enable multiple selection
Uses Tab and S\-Tab by default for selection
.TP
\fB\-\-no\-multi\fR
Disable multiple selection
.TP
\fB\-\-no\-mouse\fR
Disable mouse
.TP
\fB\-c\fR, \fB\-\-cmd\fR \fI<CMD>\fR
Command to invoke dynamically in interactive mode
Will be invoked using sh \-c
.TP
\fB\-i\fR, \fB\-\-interactive\fR
Start skim in interactive mode
In interactive mode, sk will run the command specified by \-\-cmd option and display the results.
.TP
\fB\-I\fR \fI<REPLSTR>\fR [default: {}]
Replace replstr with the selected item in commands
.TP
\fB\-\-color\fR \fI<COLOR>\fR
Set color theme
Format: [BASE][,COLOR:ANSI[:ATTR1:ATTR2:..]]
See [THEME] section for details
.TP
\fB\-\-no\-hscroll\fR
Disable horizontal scroll
.TP
\fB\-\-keep\-right\fR
Keep the right end of the line visible on overflow
Effective only when the query string is empty
.TP
\fB\-\-skip\-to\-pattern\fR \fI<SKIP_TO_PATTERN>\fR
Show the matched pattern at the line start
Line will start with the start of the matched pattern. Effective only when the query
string is empty. Was designed to skip showing starts of paths of rg/grep results.
e.g. sk \-i \-c "rg {q} \-\-color=always" \-\-skip\-to\-pattern \*(Aq[^/]*:\*(Aq \-\-ansi
.TP
\fB\-\-no\-clear\-if\-empty\fR
Do not clear previous line if the command returns an empty result
Do not clear previous items if new command returns empty result. This might be useful to
reduce flickering when typing new commands and the half\-complete commands are not valid.
This is not the default behavior because similar use cases for grep and rg have already been op‐
timized where empty query results actually mean "empty" and previous results should be
cleared.
.TP
\fB\-\-no\-clear\-start\fR
Do not clear items on start
.TP
\fB\-\-no\-clear\fR
Do not clear screen on exit
Do not clear finder interface on exit. If skim was started in full screen mode, it will not switch back to the original screen, so you\*(Aqll have to manually run tput rmcup to return. This option can be used to avoid flickering of the screen when your application needs to start skim multiple times in order.
.TP
\fB\-\-show\-cmd\-error\fR
Show er
gitextract_4duz2xmi/
├── .config/
│ ├── insta.yaml
│ ├── nextest.toml
│ └── valgrind.supp
├── .dockerignore
├── .envrc
├── .githooks/
│ └── pre-commit
├── .github/
│ ├── CONTRIBUTING.md
│ ├── ISSUE_TEMPLATE/
│ │ └── bug_report.md
│ ├── dependabot.yml
│ ├── pr-title-checker-config.json
│ ├── pull_request_template.md
│ └── workflows/
│ ├── pr.yml
│ ├── publish.yml
│ ├── release.yml
│ └── test.yml
├── .gitignore
├── .rustfmt.toml
├── AGENTS.md
├── CHANGELOG.md
├── Cargo.toml
├── LICENSE
├── README.md
├── bench.py
├── benches/
│ ├── filter.rs
│ ├── gungraun.rs
│ ├── matcher_micro.rs
│ ├── partial.rs
│ └── read_and_match.rs
├── bin/
│ └── sk-tmux
├── cliff.toml
├── codecov.yml
├── dist-workspace.toml
├── examples/
│ ├── ansi.rs
│ ├── async.rs
│ ├── base.rs
│ ├── cmd_collector.rs
│ ├── custom_action_keybinding.rs
│ ├── custom_item.rs
│ ├── custom_keybinding_actions.rs
│ ├── downcast.rs
│ ├── fine-grain.rs
│ ├── fuzzy_matcher_fz.rs
│ ├── multiple_runs.rs
│ ├── nth.rs
│ ├── option_builder.rs
│ ├── preview_callback.rs
│ ├── receiver_multi.rs
│ ├── sample.rs
│ ├── selector.rs
│ └── tick.rs
├── flake.nix
├── justfile
├── man/
│ └── man1/
│ ├── sk-tmux.1
│ └── sk.1
├── plugin/
│ └── skim.vim
├── rust-toolchain.toml
├── shell/
│ ├── LICENSE
│ ├── README.md
│ ├── completion.bash
│ ├── completion.fish
│ ├── completion.nu
│ ├── completion.zsh
│ ├── key-bindings.bash
│ ├── key-bindings.fish
│ ├── key-bindings.zsh
│ └── version.txt
├── sonar-project.properties
├── src/
│ ├── bin/
│ │ └── main.rs
│ ├── binds.rs
│ ├── engine/
│ │ ├── all.rs
│ │ ├── andor.rs
│ │ ├── exact.rs
│ │ ├── factory.rs
│ │ ├── fuzzy.rs
│ │ ├── mod.rs
│ │ ├── normalized.rs
│ │ ├── regexp.rs
│ │ ├── split.rs
│ │ └── util.rs
│ ├── field.rs
│ ├── fuzzy_matcher/
│ │ ├── arinae/
│ │ │ ├── algo.rs
│ │ │ ├── atom.rs
│ │ │ ├── banding.rs
│ │ │ ├── constants.rs
│ │ │ ├── helpers.rs
│ │ │ ├── matrix.rs
│ │ │ ├── mod.rs
│ │ │ ├── prefilter.rs
│ │ │ └── tests.rs
│ │ ├── clangd.rs
│ │ ├── frizbee.rs
│ │ ├── fzy.rs
│ │ ├── mod.rs
│ │ ├── skim.rs
│ │ └── util.rs
│ ├── helper/
│ │ ├── item.rs
│ │ ├── item_reader.rs
│ │ ├── macros.rs
│ │ ├── mod.rs
│ │ └── selector.rs
│ ├── item.rs
│ ├── lib.rs
│ ├── manpage.rs
│ ├── matcher.rs
│ ├── options.rs
│ ├── output.rs
│ ├── prelude.rs
│ ├── reader.rs
│ ├── shell.rs
│ ├── skim.rs
│ ├── skim_item.rs
│ ├── spinlock.rs
│ ├── theme.rs
│ ├── tmux.rs
│ ├── tui/
│ │ ├── app.rs
│ │ ├── backend.rs
│ │ ├── event.rs
│ │ ├── header.rs
│ │ ├── input.rs
│ │ ├── item_list.rs
│ │ ├── layout.rs
│ │ ├── mod.rs
│ │ ├── options.rs
│ │ ├── preview.rs
│ │ ├── statusline.rs
│ │ ├── util.rs
│ │ └── widget.rs
│ └── util.rs
├── test.dockerfile
└── tests/
├── ansi.rs
├── binds.rs
├── case.rs
├── common/
│ ├── insta.rs
│ ├── mod.rs
│ └── tmux.rs
├── defaults.rs
├── highlighting.rs
├── history.rs
├── issues.rs
├── keys.rs
├── keys_interactive.rs
├── layout.rs
├── listen.rs
├── matcher.rs
├── normalize.rs
├── options.rs
├── preview.rs
├── snapshots/
│ ├── ansi__prompt_ansi.snap
│ ├── binds__bind_append_and_select-2.snap
│ ├── binds__bind_append_and_select-3.snap
│ ├── binds__bind_append_and_select.snap
│ ├── binds__bind_change-2.snap
│ ├── binds__bind_change-3.snap
│ ├── binds__bind_change.snap
│ ├── binds__bind_first_last-2.snap
│ ├── binds__bind_first_last-3.snap
│ ├── binds__bind_first_last-4.snap
│ ├── binds__bind_first_last.snap
│ ├── binds__bind_if_non_matched-2.snap
│ ├── binds__bind_if_non_matched-3.snap
│ ├── binds__bind_if_non_matched.snap
│ ├── binds__bind_set_header_change-2.snap
│ ├── binds__bind_set_header_change.snap
│ ├── binds__bind_set_header_from_empty-2.snap
│ ├── binds__bind_set_header_from_empty.snap
│ ├── binds__bind_set_header_to_empty-2.snap
│ ├── binds__bind_set_header_to_empty.snap
│ ├── binds__bind_set_preview_cmd-2.snap
│ ├── binds__bind_set_preview_cmd-3.snap
│ ├── binds__bind_set_preview_cmd.snap
│ ├── binds__bind_set_query_basic-2.snap
│ ├── binds__bind_set_query_basic.snap
│ ├── binds__bind_set_query_expand-2.snap
│ ├── binds__bind_set_query_expand.snap
│ ├── binds__bind_set_query_fields-2.snap
│ ├── binds__bind_set_query_fields.snap
│ ├── binds__bind_set_query_to_itself-2.snap
│ ├── binds__bind_set_query_to_itself-3.snap
│ ├── binds__bind_set_query_to_itself-4.snap
│ ├── binds__bind_set_query_to_itself.snap
│ ├── binds__bind_toggle_interactive-2.snap
│ ├── binds__bind_toggle_interactive.snap
│ ├── binds__bind_toggle_interactive_queries-2.snap
│ ├── binds__bind_toggle_interactive_queries-3.snap
│ ├── binds__bind_toggle_interactive_queries-4.snap
│ ├── binds__bind_toggle_interactive_queries-5.snap
│ ├── binds__bind_toggle_interactive_queries.snap
│ ├── binds__bind_top_alias-2.snap
│ ├── binds__bind_top_alias-3.snap
│ ├── binds__bind_top_alias.snap
│ ├── case__case_ignore_different-2.snap
│ ├── case__case_ignore_different.snap
│ ├── case__case_ignore_exact-2.snap
│ ├── case__case_ignore_exact.snap
│ ├── case__case_ignore_lower-2.snap
│ ├── case__case_ignore_lower.snap
│ ├── case__case_ignore_no_match-2.snap
│ ├── case__case_ignore_no_match.snap
│ ├── case__case_non_ascii-2.snap
│ ├── case__case_non_ascii-3.snap
│ ├── case__case_non_ascii.snap
│ ├── case__case_respect_exact-2.snap
│ ├── case__case_respect_exact.snap
│ ├── case__case_respect_lower-2.snap
│ ├── case__case_respect_lower.snap
│ ├── case__case_respect_no_match-2.snap
│ ├── case__case_respect_no_match.snap
│ ├── case__case_smart_exact-2.snap
│ ├── case__case_smart_exact.snap
│ ├── case__case_smart_lower-2.snap
│ ├── case__case_smart_lower.snap
│ ├── case__case_smart_no_match-2.snap
│ ├── case__case_smart_no_match.snap
│ ├── defaults__interactive_mode_command_execution-2.snap
│ ├── defaults__interactive_mode_command_execution-3.snap
│ ├── defaults__interactive_mode_command_execution.snap
│ ├── defaults__unicode_input-2.snap
│ ├── defaults__unicode_input-3.snap
│ ├── defaults__unicode_input-4.snap
│ ├── defaults__unicode_input.snap
│ ├── defaults__vanilla.snap
│ ├── defaults__vanilla_basic.snap
│ ├── issues__issue_359_multi_regex_unicode.snap
│ ├── issues__issue_361_literal_space_control.snap
│ ├── issues__issue_361_literal_space_invert.snap
│ ├── issues__issue_547_null_match-2.snap
│ ├── issues__issue_547_null_match.snap
│ ├── issues__issue_929_double_width_chars-2.snap
│ ├── issues__issue_929_double_width_chars.snap
│ ├── keys__keys_alt_b-2.snap
│ ├── keys__keys_alt_b.snap
│ ├── keys__keys_alt_bspace-2.snap
│ ├── keys__keys_alt_bspace.snap
│ ├── keys__keys_alt_d-2.snap
│ ├── keys__keys_alt_d-3.snap
│ ├── keys__keys_alt_d-4.snap
│ ├── keys__keys_alt_d.snap
│ ├── keys__keys_alt_f-2.snap
│ ├── keys__keys_alt_f-3.snap
│ ├── keys__keys_alt_f.snap
│ ├── keys__keys_arrows-2.snap
│ ├── keys__keys_arrows-3.snap
│ ├── keys__keys_arrows.snap
│ ├── keys__keys_basic-2.snap
│ ├── keys__keys_basic.snap
│ ├── keys__keys_bspace-2.snap
│ ├── keys__keys_bspace.snap
│ ├── keys__keys_btab-2.snap
│ ├── keys__keys_btab.snap
│ ├── keys__keys_ctrl_a-2.snap
│ ├── keys__keys_ctrl_a.snap
│ ├── keys__keys_ctrl_arrows-2.snap
│ ├── keys__keys_ctrl_arrows-3.snap
│ ├── keys__keys_ctrl_arrows-4.snap
│ ├── keys__keys_ctrl_arrows.snap
│ ├── keys__keys_ctrl_b-2.snap
│ ├── keys__keys_ctrl_b-3.snap
│ ├── keys__keys_ctrl_b.snap
│ ├── keys__keys_ctrl_c.snap
│ ├── keys__keys_ctrl_d-2.snap
│ ├── keys__keys_ctrl_d-3.snap
│ ├── keys__keys_ctrl_d.snap
│ ├── keys__keys_ctrl_e-2.snap
│ ├── keys__keys_ctrl_e-3.snap
│ ├── keys__keys_ctrl_e.snap
│ ├── keys__keys_ctrl_f-2.snap
│ ├── keys__keys_ctrl_f-3.snap
│ ├── keys__keys_ctrl_f.snap
│ ├── keys__keys_ctrl_h-2.snap
│ ├── keys__keys_ctrl_h.snap
│ ├── keys__keys_ctrl_k-2.snap
│ ├── keys__keys_ctrl_k.snap
│ ├── keys__keys_ctrl_u-2.snap
│ ├── keys__keys_ctrl_u.snap
│ ├── keys__keys_ctrl_w-2.snap
│ ├── keys__keys_ctrl_w.snap
│ ├── keys__keys_ctrl_y-2.snap
│ ├── keys__keys_ctrl_y-3.snap
│ ├── keys__keys_ctrl_y.snap
│ ├── keys__keys_tab-2.snap
│ ├── keys__keys_tab-3.snap
│ ├── keys__keys_tab.snap
│ ├── keys__keys_tab_empty-2.snap
│ ├── keys__keys_tab_empty-3.snap
│ ├── keys__keys_tab_empty.snap
│ ├── keys_interactive__keys_interactive_alt_b-2.snap
│ ├── keys_interactive__keys_interactive_alt_b.snap
│ ├── keys_interactive__keys_interactive_alt_bspace-2.snap
│ ├── keys_interactive__keys_interactive_alt_bspace.snap
│ ├── keys_interactive__keys_interactive_alt_d-2.snap
│ ├── keys_interactive__keys_interactive_alt_d-3.snap
│ ├── keys_interactive__keys_interactive_alt_d-4.snap
│ ├── keys_interactive__keys_interactive_alt_d.snap
│ ├── keys_interactive__keys_interactive_alt_f-2.snap
│ ├── keys_interactive__keys_interactive_alt_f-3.snap
│ ├── keys_interactive__keys_interactive_alt_f.snap
│ ├── keys_interactive__keys_interactive_arrows-2.snap
│ ├── keys_interactive__keys_interactive_arrows-3.snap
│ ├── keys_interactive__keys_interactive_arrows.snap
│ ├── keys_interactive__keys_interactive_basic-2.snap
│ ├── keys_interactive__keys_interactive_basic.snap
│ ├── keys_interactive__keys_interactive_bspace-2.snap
│ ├── keys_interactive__keys_interactive_bspace.snap
│ ├── keys_interactive__keys_interactive_btab-2.snap
│ ├── keys_interactive__keys_interactive_btab.snap
│ ├── keys_interactive__keys_interactive_ctrl_a-2.snap
│ ├── keys_interactive__keys_interactive_ctrl_a.snap
│ ├── keys_interactive__keys_interactive_ctrl_arrows-2.snap
│ ├── keys_interactive__keys_interactive_ctrl_arrows-3.snap
│ ├── keys_interactive__keys_interactive_ctrl_arrows-4.snap
│ ├── keys_interactive__keys_interactive_ctrl_arrows.snap
│ ├── keys_interactive__keys_interactive_ctrl_b-2.snap
│ ├── keys_interactive__keys_interactive_ctrl_b-3.snap
│ ├── keys_interactive__keys_interactive_ctrl_b.snap
│ ├── keys_interactive__keys_interactive_ctrl_c.snap
│ ├── keys_interactive__keys_interactive_ctrl_d-2.snap
│ ├── keys_interactive__keys_interactive_ctrl_d-3.snap
│ ├── keys_interactive__keys_interactive_ctrl_d.snap
│ ├── keys_interactive__keys_interactive_ctrl_e-2.snap
│ ├── keys_interactive__keys_interactive_ctrl_e-3.snap
│ ├── keys_interactive__keys_interactive_ctrl_e.snap
│ ├── keys_interactive__keys_interactive_ctrl_f-2.snap
│ ├── keys_interactive__keys_interactive_ctrl_f-3.snap
│ ├── keys_interactive__keys_interactive_ctrl_f.snap
│ ├── keys_interactive__keys_interactive_ctrl_h-2.snap
│ ├── keys_interactive__keys_interactive_ctrl_h.snap
│ ├── keys_interactive__keys_interactive_ctrl_k-2.snap
│ ├── keys_interactive__keys_interactive_ctrl_k.snap
│ ├── keys_interactive__keys_interactive_ctrl_m.snap
│ ├── keys_interactive__keys_interactive_ctrl_u-2.snap
│ ├── keys_interactive__keys_interactive_ctrl_u.snap
│ ├── keys_interactive__keys_interactive_ctrl_w-2.snap
│ ├── keys_interactive__keys_interactive_ctrl_w.snap
│ ├── keys_interactive__keys_interactive_ctrl_y-2.snap
│ ├── keys_interactive__keys_interactive_ctrl_y-3.snap
│ ├── keys_interactive__keys_interactive_ctrl_y.snap
│ ├── keys_interactive__keys_interactive_enter.snap
│ ├── keys_interactive__keys_interactive_tab-2.snap
│ ├── keys_interactive__keys_interactive_tab-3.snap
│ ├── keys_interactive__keys_interactive_tab.snap
│ ├── layout__layout_border.snap
│ ├── layout__layout_default.snap
│ ├── layout__layout_reverse.snap
│ ├── layout__layout_reverse_border.snap
│ ├── layout__layout_reverse_list.snap
│ ├── layout__layout_reverse_list_border.snap
│ ├── matcher__matcher_arinae.snap
│ ├── matcher__matcher_arinae_typos.snap
│ ├── matcher__matcher_clangd.snap
│ ├── matcher__matcher_default.snap
│ ├── matcher__matcher_frizbee.snap
│ ├── matcher__matcher_frizbee_typos.snap
│ ├── matcher__matcher_fzy.snap
│ ├── matcher__matcher_fzy_typos.snap
│ ├── matcher__matcher_skim_v1.snap
│ ├── matcher__matcher_skim_v2.snap
│ ├── matcher__matcher_skim_v3.snap
│ ├── matcher__matcher_skim_v3_typos.snap
│ ├── normalize__insta_no_normalize_accented_item-2.snap
│ ├── normalize__insta_no_normalize_accented_item.snap
│ ├── normalize__insta_normalize_accented_item_unaccented_query-2.snap
│ ├── normalize__insta_normalize_accented_item_unaccented_query.snap
│ ├── normalize__insta_normalize_case_insensitive-2.snap
│ ├── normalize__insta_normalize_case_insensitive.snap
│ ├── normalize__insta_normalize_combined_chars-2.snap
│ ├── normalize__insta_normalize_combined_chars-3.snap
│ ├── normalize__insta_normalize_combined_chars.snap
│ ├── normalize__insta_normalize_cyrillic-2.snap
│ ├── normalize__insta_normalize_cyrillic.snap
│ ├── normalize__insta_normalize_exact_match-2.snap
│ ├── normalize__insta_normalize_exact_match.snap
│ ├── normalize__insta_normalize_multiple_diacritics-2.snap
│ ├── normalize__insta_normalize_multiple_diacritics-3.snap
│ ├── normalize__insta_normalize_multiple_diacritics.snap
│ ├── normalize__insta_normalize_negation-2.snap
│ ├── normalize__insta_normalize_negation.snap
│ ├── normalize__insta_normalize_prefix-2.snap
│ ├── normalize__insta_normalize_prefix.snap
│ ├── normalize__insta_normalize_suffix-2.snap
│ ├── normalize__insta_normalize_suffix.snap
│ ├── normalize__insta_normalize_umlauts-2.snap
│ ├── normalize__insta_normalize_umlauts-3.snap
│ ├── normalize__insta_normalize_umlauts.snap
│ ├── normalize__insta_normalize_unaccented_item_accented_query-2.snap
│ ├── normalize__insta_normalize_unaccented_item_accented_query.snap
│ ├── options__opt_border_double.snap
│ ├── options__opt_border_heavy_double_dashed.snap
│ ├── options__opt_border_heavy_quadruple_dashed.snap
│ ├── options__opt_border_heavy_triple_dashed.snap
│ ├── options__opt_border_light_double_dashed.snap
│ ├── options__opt_border_light_quadruple_dashed.snap
│ ├── options__opt_border_light_triple_dashed.snap
│ ├── options__opt_border_plain.snap
│ ├── options__opt_border_quadrant_inside.snap
│ ├── options__opt_border_quadrant_outside.snap
│ ├── options__opt_border_rounded.snap
│ ├── options__opt_border_thick.snap
│ ├── options__opt_cycle-2.snap
│ ├── options__opt_cycle-3.snap
│ ├── options__opt_cycle.snap
│ ├── options__opt_cycle_header_lines-2.snap
│ ├── options__opt_cycle_header_lines-3.snap
│ ├── options__opt_cycle_header_lines.snap
│ ├── options__opt_disabled-2.snap
│ ├── options__opt_disabled.snap
│ ├── options__opt_ellipsis.snap
│ ├── options__opt_exit_0_enter.snap
│ ├── options__opt_header_inline_info.snap
│ ├── options__opt_header_lines_1.snap
│ ├── options__opt_header_lines_all.snap
│ ├── options__opt_header_lines_inline_info.snap
│ ├── options__opt_header_lines_reverse.snap
│ ├── options__opt_header_lines_reverse_inline_info.snap
│ ├── options__opt_header_multiline.snap
│ ├── options__opt_header_only.snap
│ ├── options__opt_header_reverse.snap
│ ├── options__opt_header_reverse_inline_info.snap
│ ├── options__opt_hscroll_begin.snap
│ ├── options__opt_hscroll_end.snap
│ ├── options__opt_hscroll_middle.snap
│ ├── options__opt_info_control-2.snap
│ ├── options__opt_info_control.snap
│ ├── options__opt_info_default-2.snap
│ ├── options__opt_info_default.snap
│ ├── options__opt_info_hidden.snap
│ ├── options__opt_info_inline-2.snap
│ ├── options__opt_info_inline.snap
│ ├── options__opt_inline_info-2.snap
│ ├── options__opt_inline_info.snap
│ ├── options__opt_min_query_length-2.snap
│ ├── options__opt_min_query_length-3.snap
│ ├── options__opt_min_query_length.snap
│ ├── options__opt_min_query_length_interactive-2.snap
│ ├── options__opt_min_query_length_interactive-3.snap
│ ├── options__opt_min_query_length_interactive.snap
│ ├── options__opt_multi-2.snap
│ ├── options__opt_multi-3.snap
│ ├── options__opt_multi.snap
│ ├── options__opt_multi_selector-2.snap
│ ├── options__opt_multi_selector.snap
│ ├── options__opt_multiple_flags_cmd_prompt.snap
│ ├── options__opt_multiple_flags_cmd_query.snap
│ ├── options__opt_multiple_flags_combined_nth-2.snap
│ ├── options__opt_multiple_flags_combined_nth.snap
│ ├── options__opt_multiple_flags_combined_with_nth.snap
│ ├── options__opt_multiple_flags_interactive.snap
│ ├── options__opt_multiple_flags_layout_and_reverse.snap
│ ├── options__opt_multiple_flags_prompt.snap
│ ├── options__opt_multiple_flags_reverse.snap
│ ├── options__opt_multiple_flags_reverse_and_layout.snap
│ ├── options__opt_no_clear_if_empty-2.snap
│ ├── options__opt_no_clear_if_empty.snap
│ ├── options__opt_no_hscroll.snap
│ ├── options__opt_no_info.snap
│ ├── options__opt_no_sort-2.snap
│ ├── options__opt_no_sort.snap
│ ├── options__opt_nth_1-2.snap
│ ├── options__opt_nth_1-3.snap
│ ├── options__opt_nth_1-4.snap
│ ├── options__opt_nth_1.snap
│ ├── options__opt_nth_2-2.snap
│ ├── options__opt_nth_2-3.snap
│ ├── options__opt_nth_2-4.snap
│ ├── options__opt_nth_2.snap
│ ├── options__opt_nth_4-2.snap
│ ├── options__opt_nth_4-3.snap
│ ├── options__opt_nth_4-4.snap
│ ├── options__opt_nth_4.snap
│ ├── options__opt_nth_neg_1-2.snap
│ ├── options__opt_nth_neg_1-3.snap
│ ├── options__opt_nth_neg_1-4.snap
│ ├── options__opt_nth_neg_1.snap
│ ├── options__opt_nth_neg_2-2.snap
│ ├── options__opt_nth_neg_2-3.snap
│ ├── options__opt_nth_neg_2-4.snap
│ ├── options__opt_nth_neg_2.snap
│ ├── options__opt_nth_neg_4-2.snap
│ ├── options__opt_nth_neg_4-3.snap
│ ├── options__opt_nth_neg_4-4.snap
│ ├── options__opt_nth_neg_4.snap
│ ├── options__opt_nth_neg_oob-2.snap
│ ├── options__opt_nth_neg_oob.snap
│ ├── options__opt_nth_oob-2.snap
│ ├── options__opt_nth_oob.snap
│ ├── options__opt_nth_range_closed-2.snap
│ ├── options__opt_nth_range_closed-3.snap
│ ├── options__opt_nth_range_closed-4.snap
│ ├── options__opt_nth_range_closed-5.snap
│ ├── options__opt_nth_range_closed-6.snap
│ ├── options__opt_nth_range_closed-7.snap
│ ├── options__opt_nth_range_closed-8.snap
│ ├── options__opt_nth_range_closed.snap
│ ├── options__opt_nth_range_dec-2.snap
│ ├── options__opt_nth_range_dec.snap
│ ├── options__opt_nth_range_from_start-2.snap
│ ├── options__opt_nth_range_from_start-3.snap
│ ├── options__opt_nth_range_from_start-4.snap
│ ├── options__opt_nth_range_from_start.snap
│ ├── options__opt_nth_range_to_end-2.snap
│ ├── options__opt_nth_range_to_end-3.snap
│ ├── options__opt_nth_range_to_end-4.snap
│ ├── options__opt_nth_range_to_end.snap
│ ├── options__opt_pre_select_items.snap
│ ├── options__opt_pre_select_n.snap
│ ├── options__opt_pre_select_pat.snap
│ ├── options__opt_replstr-2.snap
│ ├── options__opt_replstr.snap
│ ├── options__opt_select_1_enter.snap
│ ├── options__opt_selector.snap
│ ├── options__opt_skip_to_pattern-2.snap
│ ├── options__opt_skip_to_pattern-3.snap
│ ├── options__opt_skip_to_pattern.snap
│ ├── options__opt_tabstop_1.snap
│ ├── options__opt_tabstop_3.snap
│ ├── options__opt_tabstop_default.snap
│ ├── options__opt_tac.snap
│ ├── options__opt_tac_with_header_lines.snap
│ ├── options__opt_with_nth_1.snap
│ ├── options__opt_with_nth_2.snap
│ ├── options__opt_with_nth_4.snap
│ ├── options__opt_with_nth_neg_1.snap
│ ├── options__opt_with_nth_neg_2.snap
│ ├── options__opt_with_nth_neg_4.snap
│ ├── options__opt_with_nth_oob.snap
│ ├── options__opt_with_nth_oob_4.snap
│ ├── options__opt_with_nth_preview.snap
│ ├── options__opt_with_nth_range_closed.snap
│ ├── options__opt_with_nth_range_desc.snap
│ ├── options__opt_with_nth_range_from_start.snap
│ ├── options__opt_with_nth_range_to_end.snap
│ ├── options__opt_wrap.snap
│ ├── preview__preview_navigation-2.snap
│ ├── preview__preview_navigation.snap
│ ├── preview__preview_no_pty__preview_no_pty_flag.snap
│ ├── preview__preview_no_pty__preview_no_pty_navigation-2.snap
│ ├── preview__preview_no_pty__preview_no_pty_navigation.snap
│ ├── preview__preview_no_pty__preview_no_pty_nowrap.snap
│ ├── preview__preview_no_pty__preview_no_pty_nul_char.snap
│ ├── preview__preview_no_pty__preview_no_pty_offset_expr.snap
│ ├── preview__preview_no_pty__preview_no_pty_offset_fixed.snap
│ ├── preview__preview_no_pty__preview_no_pty_offset_fixed_and_expr.snap
│ ├── preview__preview_no_pty__preview_no_pty_plus-2.snap
│ ├── preview__preview_no_pty__preview_no_pty_plus-3.snap
│ ├── preview__preview_no_pty__preview_no_pty_plus-4.snap
│ ├── preview__preview_no_pty__preview_no_pty_plus-5.snap
│ ├── preview__preview_no_pty__preview_no_pty_plus.snap
│ ├── preview__preview_no_pty__preview_no_pty_preserve_quotes.snap
│ ├── preview__preview_no_pty__preview_no_pty_window_down.snap
│ ├── preview__preview_no_pty__preview_no_pty_window_left.snap
│ ├── preview__preview_no_pty__preview_no_pty_window_up.snap
│ ├── preview__preview_no_pty__preview_no_pty_wrap.snap
│ ├── preview__preview_no_pty_linux.snap
│ ├── preview__preview_nowrap.snap
│ ├── preview__preview_nul_char.snap
│ ├── preview__preview_offset_expr.snap
│ ├── preview__preview_offset_fixed.snap
│ ├── preview__preview_offset_fixed_and_expr.snap
│ ├── preview__preview_plus-2.snap
│ ├── preview__preview_plus-3.snap
│ ├── preview__preview_plus-4.snap
│ ├── preview__preview_plus-5.snap
│ ├── preview__preview_plus.snap
│ ├── preview__preview_preserve_quotes.snap
│ ├── preview__preview_pty__preview_pty_flag.snap
│ ├── preview__preview_pty__preview_pty_navigation-2.snap
│ ├── preview__preview_pty__preview_pty_navigation.snap
│ ├── preview__preview_pty__preview_pty_nowrap.snap
│ ├── preview__preview_pty__preview_pty_nul_char.snap
│ ├── preview__preview_pty__preview_pty_offset_expr.snap
│ ├── preview__preview_pty__preview_pty_offset_fixed.snap
│ ├── preview__preview_pty__preview_pty_offset_fixed_and_expr.snap
│ ├── preview__preview_pty__preview_pty_plus-2.snap
│ ├── preview__preview_pty__preview_pty_plus-3.snap
│ ├── preview__preview_pty__preview_pty_plus-4.snap
│ ├── preview__preview_pty__preview_pty_plus-5.snap
│ ├── preview__preview_pty__preview_pty_plus.snap
│ ├── preview__preview_pty__preview_pty_preserve_quotes.snap
│ ├── preview__preview_pty__preview_pty_window_down.snap
│ ├── preview__preview_pty__preview_pty_window_left.snap
│ ├── preview__preview_pty__preview_pty_window_up.snap
│ ├── preview__preview_pty__preview_pty_wrap.snap
│ ├── preview__preview_pty_linux.snap
│ ├── preview__preview_window_down.snap
│ ├── preview__preview_window_left.snap
│ ├── preview__preview_window_up.snap
│ ├── preview__preview_wrap.snap
│ ├── split_match__split_match_both_parts-2.snap
│ ├── split_match__split_match_both_parts.snap
│ ├── split_match__split_match_custom_delimiter-2.snap
│ ├── split_match__split_match_custom_delimiter.snap
│ ├── split_match__split_match_delimiter_in_query_not_item-2.snap
│ ├── split_match__split_match_delimiter_in_query_not_item.snap
│ ├── split_match__split_match_empty_after-2.snap
│ ├── split_match__split_match_empty_after.snap
│ ├── split_match__split_match_empty_before-2.snap
│ ├── split_match__split_match_empty_before.snap
│ ├── split_match__split_match_multiple_delimiters_in_item-2.snap
│ ├── split_match__split_match_multiple_delimiters_in_item.snap
│ ├── split_match__split_match_no_delimiter_in_item-2.snap
│ ├── split_match__split_match_no_delimiter_in_item.snap
│ ├── split_match__split_match_or-2.snap
│ ├── split_match__split_match_or.snap
│ ├── split_match__split_match_query_before_delimiter-2.snap
│ ├── split_match__split_match_query_before_delimiter.snap
│ ├── tiebreak__tiebreak_begin-2.snap
│ ├── tiebreak__tiebreak_begin.snap
│ ├── tiebreak__tiebreak_default-2.snap
│ ├── tiebreak__tiebreak_default.snap
│ ├── tiebreak__tiebreak_end-2.snap
│ ├── tiebreak__tiebreak_end.snap
│ ├── tiebreak__tiebreak_index-2.snap
│ ├── tiebreak__tiebreak_index.snap
│ ├── tiebreak__tiebreak_length-2.snap
│ ├── tiebreak__tiebreak_length.snap
│ ├── tiebreak__tiebreak_neg_begin-2.snap
│ ├── tiebreak__tiebreak_neg_begin.snap
│ ├── tiebreak__tiebreak_neg_end-2.snap
│ ├── tiebreak__tiebreak_neg_end.snap
│ ├── tiebreak__tiebreak_neg_index-2.snap
│ ├── tiebreak__tiebreak_neg_index.snap
│ ├── tiebreak__tiebreak_neg_length-2.snap
│ ├── tiebreak__tiebreak_neg_length.snap
│ ├── tiebreak__tiebreak_neg_pathname-2.snap
│ ├── tiebreak__tiebreak_neg_pathname.snap
│ ├── tiebreak__tiebreak_neg_score-2.snap
│ ├── tiebreak__tiebreak_neg_score.snap
│ ├── tiebreak__tiebreak_pathname-2.snap
│ └── tiebreak__tiebreak_pathname.snap
├── split_match.rs
├── tiebreak.rs
├── tmux.rs
└── unix.rs
SYMBOL INDEX (1110 symbols across 91 files)
FILE: bench.py
function generate_test_data (line 94) | def generate_test_data(output_file: str, num_items: int) -> None:
function parse_args (line 110) | def parse_args(argv):
class ResourceMonitor (line 157) | class ResourceMonitor(threading.Thread):
method __init__ (line 160) | def __init__(self, pid: int):
method run (line 166) | def run(self):
function _find_sk_pid (line 198) | def _find_sk_pid(pane_pid: int, binary_path: str) -> int:
function run_once (line 216) | def run_once(
function _avg (line 413) | def _avg(values):
function _min (line 418) | def _min(values):
function _max (line 423) | def _max(values):
function aggregate (line 428) | def aggregate(results: list) -> dict:
function _pct (line 463) | def _pct(baseline, value):
function _fmt_mem (line 472) | def _fmt_mem(kb):
function _fmt_optional (line 478) | def _fmt_optional(value, fmt):
function print_human (line 489) | def print_human(
function print_json_multi (line 567) | def print_json_multi(binaries: list, aggregates: list, num_items: int, r...
function main (line 610) | def main():
FILE: benches/filter.rs
constant CHUNK_SIZE (line 9) | const CHUNK_SIZE: usize = 1024;
function load_lines (line 10) | fn load_lines(file: &str) -> Vec<String> {
function prepare (line 15) | fn prepare(file: &str, opt_builder: &mut SkimOptionsBuilder) -> (SkimOpt...
function criterion_benchmark_10m (line 33) | fn criterion_benchmark_10m(c: &mut Criterion) {
function criterion_benchmark_1m (line 147) | fn criterion_benchmark_1m(c: &mut Criterion) {
FILE: benches/gungraun.rs
function load_lines (line 11) | fn load_lines() -> Vec<String> {
function bench_matcher (line 17) | fn bench_matcher(m: impl FuzzyMatcher, lines: Vec<String>) -> u64 {
function skim_v2 (line 28) | fn skim_v2() -> u64 {
function frizbee (line 32) | fn frizbee() -> u64 {
function frizbee_typos (line 39) | fn frizbee_typos() -> u64 {
function arinae (line 46) | fn arinae() -> u64 {
function arinae_typos (line 53) | fn arinae_typos() -> u64 {
FILE: benches/matcher_micro.rs
function load_lines (line 14) | fn load_lines() -> Vec<String> {
function bench_matcher (line 19) | fn bench_matcher(c: &mut Criterion) {
FILE: benches/partial.rs
function criterion_benchmark (line 9) | fn criterion_benchmark(c: &mut Criterion) {
FILE: benches/read_and_match.rs
function wait_until_done (line 6) | async fn wait_until_done(mut opts: SkimOptions) -> Result<SkimOutput> {
function criterion_benchmark (line 20) | fn criterion_benchmark(c: &mut Criterion) {
FILE: examples/ansi.rs
function main (line 4) | pub fn main() {
FILE: examples/async.rs
function main (line 4) | async fn main() {
FILE: examples/base.rs
function main (line 3) | fn main() -> color_eyre::Result<()> {
FILE: examples/cmd_collector.rs
type BasicSkimItem (line 5) | struct BasicSkimItem {
method text (line 10) | fn text(&self) -> Cow<'_, str> {
type BasicCmdCollector (line 15) | struct BasicCmdCollector {
method invoke (line 20) | fn invoke(&mut self, _cmd: &str, _components_to_stop: Arc<AtomicUsize>) ...
function main (line 36) | fn main() {
FILE: examples/custom_action_keybinding.rs
function main (line 13) | fn main() {
FILE: examples/custom_item.rs
type MyItem (line 4) | struct MyItem {
method text (line 9) | fn text(&self) -> Cow<'_, str> {
method preview (line 13) | fn preview(&self, _context: PreviewContext) -> ItemPreview {
function main (line 22) | fn main() {
FILE: examples/custom_keybinding_actions.rs
function fake_delete_item (line 8) | fn fake_delete_item(item: &str) {
function fake_create_item (line 12) | fn fake_create_item(item: &str) {
function main (line 16) | fn main() {
FILE: examples/downcast.rs
type Item (line 8) | struct Item {
method text (line 13) | fn text(&self) -> Cow<'_, str> {
method preview (line 17) | fn preview(&self, _context: PreviewContext) -> ItemPreview {
function main (line 22) | pub fn main() {
FILE: examples/fine-grain.rs
function main (line 6) | pub async fn main() -> Result<()> {
FILE: examples/fuzzy_matcher_fz.rs
type IndexType (line 8) | type IndexType = usize;
function main (line 10) | pub fn main() {
function wrap_matches (line 47) | fn wrap_matches(line: &str, indices: &[IndexType]) -> String {
FILE: examples/multiple_runs.rs
function main (line 5) | fn main() {
FILE: examples/nth.rs
function main (line 7) | pub fn main() {
FILE: examples/option_builder.rs
function main (line 5) | pub fn main() {
FILE: examples/preview_callback.rs
function main (line 5) | pub fn main() {
FILE: examples/receiver_multi.rs
function main (line 5) | fn main() {
FILE: examples/sample.rs
function main (line 4) | pub fn main() {
FILE: examples/selector.rs
type BasicSelector (line 4) | struct BasicSelector {
method should_select (line 9) | fn should_select(&self, _index: usize, item: &dyn SkimItem) -> bool {
function main (line 14) | pub fn main() {
FILE: examples/tick.rs
function main (line 4) | pub async fn main() -> color_eyre::eyre::Result<()> {
FILE: src/bin/main.rs
function init_logger (line 28) | fn init_logger(opts: &SkimOptions) {
function main (line 69) | fn main() -> Result<()> {
function sk_main (line 128) | fn sk_main(mut opts: SkimOptions) -> Result<i32> {
function write_history_to_file (line 250) | fn write_history_to_file(
type BinOptions (line 279) | pub struct BinOptions {
FILE: src/binds.rs
type KeyMap (line 19) | pub struct KeyMap(pub HashMap<KeyEvent, Vec<Action>>);
method from (line 35) | fn from(value: &str) -> Self {
method add_keymaps (line 48) | pub fn add_keymaps<'a, T>(&mut self, source: T)
method bind (line 61) | fn bind(&mut self, key: &str, action_chain: Vec<Action>) -> Result<()> {
type Target (line 22) | type Target = HashMap<KeyEvent, Vec<Action>>;
method deref (line 24) | fn deref(&self) -> &Self::Target {
method deref_mut (line 29) | fn deref_mut(&mut self) -> &mut Self::Target {
method default (line 41) | fn default() -> Self {
function get_default_key_map (line 74) | pub fn get_default_key_map() -> KeyMap {
function parse_key (line 141) | pub fn parse_key(key: &str) -> Result<KeyEvent> {
function parse_keymaps (line 200) | pub fn parse_keymaps<'a, T>(maps: T) -> KeyMap
function parse_action_chain (line 213) | pub fn parse_action_chain(action_chain: &str) -> Result<Vec<Action>> {
function parse_keymap (line 239) | pub fn parse_keymap(key_action: &str) -> Result<(&str, Vec<Action>)> {
function test_parse_action_chain (line 256) | fn test_parse_action_chain() {
function test_parse_key (line 274) | fn test_parse_key() {
FILE: src/engine/all.rs
type MatchAllEngine (line 9) | pub struct MatchAllEngine {
method builder (line 14) | pub fn builder() -> Self {
method rank_builder (line 20) | pub fn rank_builder(mut self, rank_builder: Arc<RankBuilder>) -> Self {
method build (line 25) | pub fn build(self) -> Self {
method match_item (line 31) | fn match_item(&self, item: &dyn SkimItem) -> Option<MatchResult> {
method fmt (line 41) | fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
FILE: src/engine/andor.rs
type OrEngine (line 8) | pub struct OrEngine {
method builder (line 13) | pub fn builder() -> Self {
method engines (line 17) | pub fn engines(mut self, mut engines: Vec<Box<dyn MatchEngine>>) -> Se...
method build (line 22) | pub fn build(self) -> Self {
method match_item (line 28) | fn match_item(&self, item: &dyn SkimItem) -> Option<MatchResult> {
method fmt (line 40) | fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
type AndEngine (line 55) | pub struct AndEngine {
method builder (line 60) | pub fn builder() -> Self {
method engines (line 64) | pub fn engines(mut self, mut engines: Vec<Box<dyn MatchEngine>>) -> Se...
method build (line 69) | pub fn build(self) -> Self {
method merge_matched_items (line 73) | fn merge_matched_items(items: Vec<MatchResult>, text: &str) -> MatchRe...
method match_item (line 105) | fn match_item(&self, item: &dyn SkimItem) -> Option<MatchResult> {
method fmt (line 122) | fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
FILE: src/engine/exact.rs
type ExactMatchingParam (line 12) | pub struct ExactMatchingParam {
type ExactEngine (line 21) | pub struct ExactEngine {
method builder (line 30) | pub fn builder(query: &str, param: ExactMatchingParam) -> Self {
method rank_builder (line 66) | pub fn rank_builder(mut self, rank_builder: Arc<RankBuilder>) -> Self {
method build (line 71) | pub fn build(self) -> Self {
method match_item (line 77) | fn match_item(&self, item: &dyn SkimItem) -> Option<MatchResult> {
method fmt (line 111) | fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
FILE: src/engine/factory.rs
type ExactOrFuzzyEngineFactory (line 17) | pub struct ExactOrFuzzyEngineFactory {
method builder (line 29) | pub fn builder() -> Self {
method exact_mode (line 42) | pub fn exact_mode(mut self, exact_mode: bool) -> Self {
method fuzzy_algorithm (line 49) | pub fn fuzzy_algorithm(mut self, fuzzy_algorithm: FuzzyAlgorithm) -> S...
method rank_builder (line 56) | pub fn rank_builder(mut self, rank_builder: Arc<RankBuilder>) -> Self {
method typos (line 67) | pub fn typos(mut self, typos: Typos) -> Self {
method filter_mode (line 74) | pub fn filter_mode(mut self, filter_mode: bool) -> Self {
method last_match (line 81) | pub fn last_match(mut self, last_match: bool) -> Self {
method build (line 88) | pub fn build(self) -> Self {
method create_engine_with_case (line 94) | fn create_engine_with_case(&self, query: &str, case: CaseMatching) -> Bo...
type AndOrEngineFactory (line 164) | pub struct AndOrEngineFactory {
method new (line 170) | pub fn new(factory: impl MatchEngineFactory + 'static) -> Self {
method parse_andor (line 176) | fn parse_andor(&self, query: &str, case: CaseMatching) -> Box<dyn Matc...
method mask_escape_space (line 211) | fn mask_escape_space(string: &str) -> String {
method unmask_escape_space (line 215) | fn unmask_escape_space(string: &str) -> String {
method create_engine_with_case (line 221) | fn create_engine_with_case(&self, query: &str, case: CaseMatching) -> Bo...
type RegexEngineFactory (line 228) | pub struct RegexEngineFactory {
method builder (line 235) | pub fn builder() -> Self {
method rank_builder (line 243) | pub fn rank_builder(mut self, rank_builder: Arc<RankBuilder>) -> Self {
method build (line 250) | pub fn build(self) -> Self {
method create_engine_with_case (line 256) | fn create_engine_with_case(&self, query: &str, case: CaseMatching) -> Bo...
function test_engine_factory (line 268) | fn test_engine_factory() {
FILE: src/engine/fuzzy.rs
type FuzzyAlgorithm (line 19) | pub enum FuzzyAlgorithm {
constant BYTES_1M (line 34) | const BYTES_1M: usize = 1024 * 1024 * 1024;
type FuzzyEngineBuilder (line 39) | pub struct FuzzyEngineBuilder {
method query (line 58) | pub fn query(mut self, query: &str) -> Self {
method case (line 63) | pub fn case(mut self, case: CaseMatching) -> Self {
method algorithm (line 68) | pub fn algorithm(mut self, algorithm: FuzzyAlgorithm) -> Self {
method rank_builder (line 73) | pub fn rank_builder(mut self, rank_builder: Arc<RankBuilder>) -> Self {
method typos (line 78) | pub fn typos(mut self, typos: Typos) -> Self {
method filter_mode (line 83) | pub fn filter_mode(mut self, filter_mode: bool) -> Self {
method last_match (line 88) | pub fn last_match(mut self, last_match: bool) -> Self {
method effective_max_typos (line 98) | fn effective_max_typos(&self) -> Option<usize> {
method build (line 107) | pub fn build(self) -> FuzzyEngine {
type FuzzyEngine (line 160) | pub struct FuzzyEngine {
method builder (line 170) | pub fn builder() -> FuzzyEngineBuilder {
method match_item (line 176) | fn match_item(&self, item: &dyn SkimItem) -> Option<MatchResult> {
method fmt (line 261) | fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
FILE: src/engine/normalized.rs
type NormalizedEngine (line 14) | pub struct NormalizedEngine {
method new (line 21) | pub fn new(inner: Box<dyn MatchEngine>) -> Self {
method match_item (line 27) | fn match_item(&self, item: &dyn SkimItem) -> Option<MatchResult> {
method fmt (line 54) | fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
type NormalizedItem (line 60) | struct NormalizedItem(String);
method text (line 63) | fn text(&self) -> Cow<'_, str> {
type NormalizedEngineFactory (line 72) | pub struct NormalizedEngineFactory {
method new (line 78) | pub fn new(inner: impl MatchEngineFactory + 'static) -> Self {
method create_engine_with_case (line 84) | fn create_engine_with_case(&self, query: &str, case: CaseMatching) -> Bo...
FILE: src/engine/regexp.rs
type RegexEngine (line 15) | pub struct RegexEngine {
method builder (line 21) | pub fn builder(query: &str, case: CaseMatching) -> Self {
method rank_builder (line 37) | pub fn rank_builder(mut self, rank_builder: Arc<RankBuilder>) -> Self {
method build (line 42) | pub fn build(self) -> Self {
method match_item (line 48) | fn match_item(&self, item: &dyn SkimItem) -> Option<MatchResult> {
method fmt (line 79) | fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
FILE: src/engine/split.rs
type SplitMatchEngine (line 11) | pub struct SplitMatchEngine {
method new (line 22) | pub fn new(before_engine: Box<dyn MatchEngine>, after_engine: Box<dyn ...
method match_item (line 32) | fn match_item(&self, item: &dyn SkimItem) -> Option<MatchResult> {
method fmt (line 96) | fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
type StringItem (line 106) | struct StringItem(String);
method text (line 109) | fn text(&self) -> std::borrow::Cow<'_, str> {
type SplitMatchEngineFactory (line 118) | pub struct SplitMatchEngineFactory {
method new (line 125) | pub fn new(inner: impl MatchEngineFactory + 'static, delimiter: char) ...
method create_engine_with_case (line 134) | fn create_engine_with_case(&self, query: &str, case: crate::CaseMatching...
FILE: src/engine/util.rs
function normalize_with_char_mapping (line 9) | pub fn normalize_with_char_mapping(s: &str) -> (String, Vec<usize>) {
function map_char_indices_to_original (line 30) | pub fn map_char_indices_to_original(normalized_indices: &[usize], char_m...
function normalize_with_byte_mapping (line 41) | pub fn normalize_with_byte_mapping(s: &str) -> (String, Vec<usize>) {
function map_byte_range_to_original (line 66) | pub fn map_byte_range_to_original(
function regex_match (line 96) | pub fn regex_match(choice: &str, pattern: Option<&Regex>) -> Option<(usi...
function contains_upper (line 102) | pub fn contains_upper(string: &str) -> bool {
FILE: src/field.rs
type FieldRange (line 17) | pub enum FieldRange {
method from_str (line 31) | pub fn from_str(range: &str) -> Option<FieldRange> {
method to_index_pair (line 67) | pub fn to_index_pair(&self, length: usize) -> Option<(usize, usize)> {
method translate_neg (line 108) | fn translate_neg(idx: i32, length: usize) -> usize {
function get_ranges_by_delimiter (line 117) | fn get_ranges_by_delimiter(delimiter: &Regex, text: &str) -> Vec<(usize,...
function get_string_by_field (line 133) | pub fn get_string_by_field<'a>(delimiter: &Regex, text: &'a str, field: ...
function get_string_by_range (line 147) | pub fn get_string_by_range<'a>(delimiter: &Regex, text: &'a str, range: ...
function parse_matching_fields (line 156) | pub fn parse_matching_fields(delimiter: &Regex, text: &str, fields: &[Fi...
function parse_transform_fields (line 172) | pub fn parse_transform_fields(delimiter: &Regex, text: &str, fields: &[F...
function test_parse_range (line 190) | fn test_parse_range() {
function test_parse_field_range (line 212) | fn test_parse_field_range() {
function test_parse_transform_fields (line 262) | fn test_parse_transform_fields() {
function test_parse_matching_fields (line 296) | fn test_parse_matching_fields() {
function test_null_delimiter (line 335) | fn test_null_delimiter() {
function test_get_string_by_field (line 359) | fn test_get_string_by_field() {
FILE: src/fuzzy_matcher/arinae/algo.rs
function compute_cell (line 30) | fn compute_cell<const ALLOW_TYPOS: bool>(
function full_dp (line 114) | pub(super) fn full_dp<const ALLOW_TYPOS: bool, const COMPUTE_INDICES: bo...
function range_dp (line 373) | pub(super) fn range_dp<const ALLOW_TYPOS: bool, C: Atom>(
FILE: src/fuzzy_matcher/arinae/atom.rs
type Atom (line 6) | pub(super) trait Atom: PartialEq + Into<char> + Copy {
method eq (line 8) | fn eq(self, other: Self, respect_case: bool) -> bool
method eq_ignore_case (line 18) | fn eq_ignore_case(self, other: Self) -> bool;
method is_lowercase (line 19) | fn is_lowercase(self) -> bool;
method find_first_in (line 27) | fn find_first_in(self, haystack: &[Self], respect_case: bool) -> Optio...
method separator_bonus (line 35) | fn separator_bonus(self) -> Score {
method eq_ignore_case (line 46) | fn eq_ignore_case(self, b: Self) -> bool {
method is_lowercase (line 50) | fn is_lowercase(self) -> bool {
method find_first_in (line 57) | fn find_first_in(self, haystack: &[Self], respect_case: bool) -> Optio...
method eq_ignore_case (line 82) | fn eq_ignore_case(self, b: Self) -> bool {
method is_lowercase (line 86) | fn is_lowercase(self) -> bool {
FILE: src/fuzzy_matcher/arinae/banding.rs
type BandingInfo (line 10) | pub(super) struct BandingInfo {
function compute_banding (line 23) | pub(super) fn compute_banding<const ALLOW_TYPOS: bool, C: Atom>(
function typo_vband_row (line 58) | pub(super) fn typo_vband_row(i: usize, m: usize, bandwidth: usize, j_fir...
function compute_first_match_cols (line 72) | fn compute_first_match_cols<C: Atom>(pat: &[C], cho: &[C], respect_case:...
FILE: src/fuzzy_matcher/arinae/constants.rs
constant MATCH_BONUS (line 7) | pub(super) const MATCH_BONUS: Score = 18;
constant START_OF_STRING_BONUS (line 10) | pub(super) const START_OF_STRING_BONUS: Score = 16;
constant CAMEL_CASE_BONUS (line 13) | pub(super) const CAMEL_CASE_BONUS: Score = 6;
constant CONSECUTIVE_BONUS (line 16) | pub(super) const CONSECUTIVE_BONUS: Score = 11;
constant GAP_OPEN (line 19) | pub(super) const GAP_OPEN: Score = 6;
constant GAP_EXTEND (line 22) | pub(super) const GAP_EXTEND: Score = 4;
constant TYPO_PENALTY (line 24) | pub(super) const TYPO_PENALTY: Score = 10;
constant MISMATCH_PENALTY (line 27) | pub(super) const MISMATCH_PENALTY: Score = 16;
constant MAX_PAT_LEN (line 30) | pub(super) const MAX_PAT_LEN: usize = 32;
constant TYPO_BAND_SLACK (line 37) | pub(super) const TYPO_BAND_SLACK: usize = 4;
constant SEPARATOR_TABLE (line 46) | pub(super) const SEPARATOR_TABLE: [Score; 128] = {
FILE: src/fuzzy_matcher/arinae/helpers.rs
function find_first_char (line 12) | pub(super) fn find_first_char<C: Atom>(pat: &[C], cho: &[C], respect_cas...
function compute_last_match_cols (line 18) | pub(super) fn compute_last_match_cols<C: Atom>(
function compute_row_col_bounds (line 53) | pub(super) fn compute_row_col_bounds(
FILE: src/fuzzy_matcher/arinae/matrix.rs
type Dir (line 9) | pub(super) enum Dir {
type Cell (line 28) | pub(super) struct Cell(u32);
method fmt (line 33) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method new (line 43) | pub(super) const fn new(score: Score, dir: Dir) -> Cell {
method score (line 48) | pub(super) fn score(self) -> Score {
method dir (line 55) | pub(super) fn dir(self) -> Dir {
method is_diag (line 64) | pub(super) fn is_diag(self) -> bool {
constant CELL_ZERO (line 30) | pub(super) const CELL_ZERO: Cell = Cell::new(0, Dir::None);
type SWMatrix (line 70) | pub(super) struct SWMatrix {
method zero (line 77) | pub fn zero(rows: usize, cols: usize) -> Self {
method resize (line 82) | pub fn resize(&mut self, rows: usize, cols: usize) {
FILE: src/fuzzy_matcher/arinae/mod.rs
type Score (line 51) | type Score = i16;
function precompute_bonuses (line 53) | fn precompute_bonuses<C: Atom>(cho: &[C], buf: &mut Vec<Score>) {
type ArinaeMatcher (line 73) | pub struct ArinaeMatcher {
method new (line 88) | pub fn new(case: CaseMatching, allow_typos: bool, use_last_match: bool...
method respect_case (line 98) | fn respect_case<C: Atom>(&self, pattern: &[C]) -> bool {
method dispatch_dp (line 105) | fn dispatch_dp<C: Atom>(
method match_slices (line 126) | fn match_slices<C: Atom>(&self, cho: &[C], pat: &[C], compute_indices:...
method run (line 151) | fn run(&self, choice: &str, pattern: &str, compute_indices: bool) -> O...
method run_range (line 195) | fn run_range(&self, choice: &str, pattern: &str) -> Option<(ScoreType,...
method fuzzy_match (line 264) | fn fuzzy_match(&self, choice: &str, pattern: &str) -> Option<ScoreType> {
method fuzzy_match_range (line 269) | fn fuzzy_match_range(&self, choice: &str, pattern: &str) -> Option<(Scor...
method fuzzy_indices (line 273) | fn fuzzy_indices(&self, choice: &str, pattern: &str) -> Option<(ScoreTyp...
FILE: src/fuzzy_matcher/arinae/prefilter.rs
function cheap_typo_prefilter (line 33) | pub(super) fn cheap_typo_prefilter<C: Atom>(pattern: &[C], choice: &[C],...
function tail_freq_check (line 74) | fn tail_freq_check<C: Atom>(pattern: &[C], window: &[C], respect_case: b...
FILE: src/fuzzy_matcher/arinae/tests.rs
function matcher (line 4) | fn matcher() -> ArinaeMatcher {
function matcher_typos (line 8) | fn matcher_typos() -> ArinaeMatcher {
function score (line 15) | fn score(choice: &str, pattern: &str) -> Option<i64> {
function score_typos (line 19) | fn score_typos(choice: &str, pattern: &str) -> Option<i64> {
function indices (line 23) | fn indices(choice: &str, pattern: &str) -> Option<MatchIndices> {
function empty_pattern_always_matches (line 30) | fn empty_pattern_always_matches() {
function empty_choice_never_matches (line 36) | fn empty_choice_never_matches() {
function exact_match_scores_positive (line 41) | fn exact_match_scores_positive() {
function no_match_returns_none (line 46) | fn no_match_returns_none() {
function subsequence_match (line 51) | fn subsequence_match() {
function contiguous_beats_scattered (line 60) | fn contiguous_beats_scattered() {
function fewer_gaps_beats_more_gaps (line 70) | fn fewer_gaps_beats_more_gaps() {
function word_start_bonus (line 77) | fn word_start_bonus() {
function start_of_string_bonus (line 87) | fn start_of_string_bonus() {
function consecutive_match_preferred (line 94) | fn consecutive_match_preferred() {
function camel_case_bonus (line 104) | fn camel_case_bonus() {
function smart_case_insensitive_lowercase_pattern (line 113) | fn smart_case_insensitive_lowercase_pattern() {
function smart_case_sensitive_uppercase_pattern (line 123) | fn smart_case_sensitive_uppercase_pattern() {
function respect_case (line 134) | fn respect_case() {
function ignore_case (line 145) | fn ignore_case() {
function no_typos_rejects_mismatch (line 157) | fn no_typos_rejects_mismatch() {
function typos_accepts_mismatch (line 162) | fn typos_accepts_mismatch() {
function no_typos_rejects_transposition (line 167) | fn no_typos_rejects_transposition() {
function typos_accepts_transposition (line 172) | fn typos_accepts_transposition() {
function exact_match_same_with_and_without_typos (line 177) | fn exact_match_same_with_and_without_typos() {
function typo_match_scores_less_than_exact (line 187) | fn typo_match_scores_less_than_exact() {
function indices_exact_match (line 196) | fn indices_exact_match() {
function transposition_matches (line 202) | fn transposition_matches() {
function reader_ranking (line 217) | fn reader_ranking() {
function ordering_ab (line 231) | fn ordering_ab() {
function ordering_print (line 242) | fn ordering_print() {
function score_only_matches_full_dp (line 255) | fn score_only_matches_full_dp() {
function non_ascii_matching (line 281) | fn non_ascii_matching() {
function all_subsequences_must_match (line 290) | fn all_subsequences_must_match() {
function score_and_full_dp_same (line 318) | fn score_and_full_dp_same() {
function range_consistent_with_indices (line 332) | fn range_consistent_with_indices() {
function typo_prefilter_no_false_negative_on_extension (line 380) | fn typo_prefilter_no_false_negative_on_extension() {
function use_last_match_prefers_later_occurrence (line 394) | fn use_last_match_prefers_later_occurrence() {
function no_use_last_match_prefers_first_occurrence (line 406) | fn no_use_last_match_prefers_first_occurrence() {
FILE: src/fuzzy_matcher/clangd.rs
type CaseMatching (line 30) | enum CaseMatching {
type ClangdMatcher (line 38) | pub struct ClangdMatcher {
method ignore_case (line 61) | pub fn ignore_case(mut self) -> Self {
method smart_case (line 68) | pub fn smart_case(mut self) -> Self {
method respect_case (line 75) | pub fn respect_case(mut self) -> Self {
method use_cache (line 82) | pub fn use_cache(mut self, use_cache: bool) -> Self {
method contains_upper (line 87) | fn contains_upper(string: &str) -> bool {
method is_case_sensitive (line 97) | fn is_case_sensitive(&self, pattern: &str) -> bool {
method default (line 48) | fn default() -> Self {
method fuzzy_indices (line 107) | fn fuzzy_indices(&self, choice: &str, pattern: &str) -> Option<(ScoreTyp...
method fuzzy_match (line 172) | fn fuzzy_match(&self, choice: &str, pattern: &str) -> Option<ScoreType> {
function fuzzy_indices (line 210) | pub fn fuzzy_indices(line: &str, pattern: &str) -> Option<(ScoreType, Ma...
function fuzzy_match (line 216) | pub fn fuzzy_match(line: &str, pattern: &str) -> Option<ScoreType> {
function build_graph (line 222) | fn build_graph(line: &[char], pattern: &[char], compressed: bool, case_s...
function adjust_score (line 329) | fn adjust_score(score: ScoreType, num_line_chars: usize) -> ScoreType {
constant AWFUL_SCORE (line 340) | const AWFUL_SCORE: ScoreType = -(1 << 30);
type Action (line 343) | enum Action {
type Score (line 349) | struct Score {
method default (line 357) | fn default() -> Self {
function skip_penalty (line 367) | fn skip_penalty(_ch_idx: usize, ch: char, last_action: Action) -> ScoreT...
function allow_match (line 382) | fn allow_match(pat_ch: char, line_ch: char, case_sensitive: bool) -> bool {
function match_bonus (line 386) | fn match_bonus(
function print_dp (line 438) | fn print_dp(line: &str, pattern: &str, dp: &[Vec<Score>]) {
function wrap_fuzzy_match (line 474) | fn wrap_fuzzy_match(line: &str, pattern: &str) -> Option<String> {
function test_match_or_not (line 480) | fn test_match_or_not() {
function test_match_quality (line 492) | fn test_match_quality() {
FILE: src/fuzzy_matcher/frizbee.rs
constant RESPECT_CASE_BONUS (line 9) | const RESPECT_CASE_BONUS: u16 = 10000;
type FrizbeeMatcher (line 15) | pub struct FrizbeeMatcher {
method max_typos (line 23) | pub fn max_typos(mut self, typos: Option<usize>) -> Self {
method case (line 29) | pub fn case(mut self, case: CaseMatching) -> Self {
method fuzzy_indices (line 36) | fn fuzzy_indices(&self, choice: &str, pattern: &str) -> Option<(ScoreTyp...
method fuzzy_match (line 71) | fn fuzzy_match(&self, choice: &str, pattern: &str) -> Option<i64> {
FILE: src/fuzzy_matcher/fzy.rs
constant SCORE_MIN (line 52) | const SCORE_MIN: i64 = i64::MIN / 2;
constant SCORE_MAX (line 55) | const SCORE_MAX: i64 = i64::MAX / 2;
constant SCORE_GAP_LEADING (line 57) | const SCORE_GAP_LEADING: i64 = -1;
constant SCORE_GAP_TRAILING (line 58) | const SCORE_GAP_TRAILING: i64 = -1;
constant SCORE_GAP_INNER (line 59) | const SCORE_GAP_INNER: i64 = -2;
constant SCORE_MATCH_CONSECUTIVE (line 61) | const SCORE_MATCH_CONSECUTIVE: i64 = 200;
constant SCORE_MATCH_SLASH (line 62) | const SCORE_MATCH_SLASH: i64 = 180;
constant SCORE_MATCH_WORD (line 63) | const SCORE_MATCH_WORD: i64 = 160;
constant SCORE_MATCH_CAPITAL (line 64) | const SCORE_MATCH_CAPITAL: i64 = 140;
constant SCORE_MATCH_DOT (line 65) | const SCORE_MATCH_DOT: i64 = 120;
constant SCORE_TYPO (line 68) | const SCORE_TYPO: i64 = -300;
constant MATCH_MAX_LEN (line 71) | const MATCH_MAX_LEN: usize = 1024;
constant SCORE_TO_SKIM (line 75) | const SCORE_TO_SKIM: i64 = 5;
function bonus_index (line 82) | fn bonus_index(ch: char) -> usize {
function compute_bonus (line 91) | fn compute_bonus(prev_ch: char, ch: char) -> i64 {
function precompute_bonus (line 115) | fn precompute_bonus(haystack: &[char]) -> Vec<i64> {
function is_match (line 126) | fn is_match(
function fzy_score (line 144) | fn fzy_score(
function can_match_with_typos (line 307) | fn can_match_with_typos(
type TypoDpBuffers (line 364) | struct TypoDpBuffers {
function fzy_score_typos_rolling (line 381) | fn fzy_score_typos_rolling(
function fzy_score_typos_full (line 509) | fn fzy_score_typos_full(
function internal_to_skim_score (line 684) | fn internal_to_skim_score(score: i64) -> ScoreType {
type CaseMatching (line 699) | enum CaseMatching {
type FzyMatcher (line 712) | pub struct FzyMatcher {
method ignore_case (line 741) | pub fn ignore_case(mut self) -> Self {
method smart_case (line 748) | pub fn smart_case(mut self) -> Self {
method respect_case (line 755) | pub fn respect_case(mut self) -> Self {
method use_cache (line 762) | pub fn use_cache(mut self, use_cache: bool) -> Self {
method max_typos (line 772) | pub fn max_typos(mut self, max_typos: Option<usize>) -> Self {
method contains_upper (line 777) | fn contains_upper(string: &str) -> bool {
method is_case_sensitive (line 781) | fn is_case_sensitive(&self, pattern: &str) -> bool {
method default (line 724) | fn default() -> Self {
method fuzzy_indices (line 791) | fn fuzzy_indices(&self, choice: &str, pattern: &str) -> Option<(ScoreTyp...
method fuzzy_match (line 872) | fn fuzzy_match(&self, choice: &str, pattern: &str) -> Option<ScoreType> {
function fuzzy_indices (line 957) | pub fn fuzzy_indices(choice: &str, pattern: &str) -> Option<(ScoreType, ...
function fuzzy_match (line 964) | pub fn fuzzy_match(choice: &str, pattern: &str) -> Option<ScoreType> {
function wrap_fuzzy_match (line 977) | fn wrap_fuzzy_match(choice: &str, pattern: &str) -> Option<String> {
function test_no_match (line 983) | fn test_no_match() {
function test_has_match (line 990) | fn test_has_match() {
function test_exact_match_is_max (line 997) | fn test_exact_match_is_max() {
function test_match_indices (line 1004) | fn test_match_indices() {
function test_consecutive_bonus (line 1010) | fn test_consecutive_bonus() {
function test_word_boundary_bonus (line 1021) | fn test_word_boundary_bonus() {
function test_path_separator_bonus (line 1029) | fn test_path_separator_bonus() {
function test_camel_case_bonus (line 1037) | fn test_camel_case_bonus() {
function test_shorter_match_preferred (line 1045) | fn test_shorter_match_preferred() {
function test_match_quality_ordering (line 1053) | fn test_match_quality_ordering() {
function test_unicode_match (line 1061) | fn test_unicode_match() {
function test_smart_case (line 1070) | fn test_smart_case() {
function test_respect_case (line 1078) | fn test_respect_case() {
function test_long_haystack (line 1085) | fn test_long_haystack() {
function test_typo_no_typos_behaves_like_default (line 1096) | fn test_typo_no_typos_behaves_like_default() {
function test_typo_substitution_single (line 1108) | fn test_typo_substitution_single() {
function test_typo_substitution_returns_none_when_too_many_typos (line 1114) | fn test_typo_substitution_returns_none_when_too_many_typos() {
function test_typo_needle_deletion (line 1126) | fn test_typo_needle_deletion() {
function test_typo_exact_match_scores_higher_than_typo_match (line 1135) | fn test_typo_exact_match_scores_higher_than_typo_match() {
function test_typo_subsequence_beats_typo (line 1143) | fn test_typo_subsequence_beats_typo() {
function test_typo_indices_substitution (line 1151) | fn test_typo_indices_substitution() {
function test_typo_indices_needle_deletion (line 1160) | fn test_typo_indices_needle_deletion() {
function test_typo_max_typos_none_is_zero_overhead (line 1170) | fn test_typo_max_typos_none_is_zero_overhead() {
function test_typo_realistic_filename (line 1187) | fn test_typo_realistic_filename() {
function test_typo_two_typos (line 1197) | fn test_typo_two_typos() {
function test_typo_empty_pattern (line 1204) | fn test_typo_empty_pattern() {
function test_typo_pattern_longer_than_haystack (line 1210) | fn test_typo_pattern_longer_than_haystack() {
FILE: src/fuzzy_matcher/mod.rs
type IndexType (line 17) | pub(crate) type IndexType = usize;
type ScoreType (line 18) | pub(crate) type ScoreType = i64;
type MatchIndices (line 20) | pub(crate) type MatchIndices = Vec<IndexType>;
type FuzzyMatcher (line 23) | pub trait FuzzyMatcher: Send + Sync {
method fuzzy_indices (line 25) | fn fuzzy_indices(&self, choice: &str, pattern: &str) -> Option<(i64, M...
method fuzzy_match (line 28) | fn fuzzy_match(&self, choice: &str, pattern: &str) -> Option<i64> {
method fuzzy_match_range (line 40) | fn fuzzy_match_range(&self, choice: &str, pattern: &str) -> Option<(i6...
FILE: src/fuzzy_matcher/skim.rs
type SkimScoreConfig (line 31) | pub struct SkimScoreConfig {
method default (line 74) | fn default() -> Self {
type Movement (line 95) | enum Movement {
type MatrixCell (line 104) | struct MatrixCell {
method reset (line 131) | pub fn reset(&mut self) {
constant MATRIX_CELL_NEG_INFINITY (line 115) | const MATRIX_CELL_NEG_INFINITY: i32 = i16::MIN as i32;
method default (line 118) | fn default() -> Self {
type ScoreMatrix (line 142) | struct ScoreMatrix<'a> {
function new (line 150) | pub fn new(matrix: &'a mut Vec<MatrixCell>, rows: usize, cols: usize) ->...
function get_index (line 156) | fn get_index(&self, row: usize, col: usize) -> usize {
function get_row (line 160) | fn get_row(&self, row: usize) -> &[MatrixCell] {
type Output (line 167) | type Output = MatrixCell;
function index (line 169) | fn index(&self, index: (usize, usize)) -> &Self::Output {
function index_mut (line 175) | fn index_mut(&mut self, index: (usize, usize)) -> &mut Self::Output {
function fmt (line 181) | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
type CharType (line 237) | enum CharType {
method of (line 247) | pub fn of(ch: char) -> Self {
type CharRole (line 278) | enum CharRole {
method of_type (line 286) | pub fn of_type(prev: CharType, cur: CharType) -> Self {
type CaseMatching (line 297) | enum CaseMatching {
type SkimMatcherV2 (line 354) | pub struct SkimMatcherV2 {
method score_config (line 386) | pub fn score_config(mut self, score_config: SkimScoreConfig) -> Self {
method element_limit (line 393) | pub fn element_limit(mut self, elements: usize) -> Self {
method ignore_case (line 400) | pub fn ignore_case(mut self) -> Self {
method smart_case (line 407) | pub fn smart_case(mut self) -> Self {
method respect_case (line 414) | pub fn respect_case(mut self) -> Self {
method use_cache (line 421) | pub fn use_cache(mut self, use_cache: bool) -> Self {
method debug (line 428) | pub fn debug(mut self, debug: bool) -> Self {
method build_score_matrix (line 434) | fn build_score_matrix(
method build_in_place_bonus (line 534) | fn build_in_place_bonus(&self, choice: &[char], b: &mut [i32]) {
method adjust_row_idx (line 550) | fn adjust_row_idx(row_idx: usize, compressed: bool) -> usize {
method calculate_match_score (line 556) | fn calculate_match_score(&self, c: char, p: char, case_sensitive: bool...
method in_place_bonus (line 573) | fn in_place_bonus(&self, prev_char_type: CharType, char_type: CharType...
method contains_upper (line 582) | fn contains_upper(string: &str) -> bool {
method fuzzy (line 590) | pub fn fuzzy(&self, choice: &str, pattern: &str, with_pos: bool) -> Op...
method simple_match (line 697) | pub fn simple_match(
method calculate_score_with_pos (line 735) | fn calculate_score_with_pos(
method default (line 368) | fn default() -> Self {
method fuzzy_indices (line 806) | fn fuzzy_indices(&self, choice: &str, pattern: &str) -> Option<(ScoreTyp...
method fuzzy_match (line 811) | fn fuzzy_match(&self, choice: &str, pattern: &str) -> Option<ScoreType> {
function wrap_fuzzy_match (line 822) | fn wrap_fuzzy_match(matcher: &dyn FuzzyMatcher, line: &str, pattern: &st...
function test_match_or_not (line 829) | fn test_match_or_not() {
function test_match_quality (line 848) | fn test_match_quality() {
function simple_match (line 876) | fn simple_match(
function test_match_or_not_simple (line 890) | fn test_match_or_not_simple() {
function test_match_or_not_v2 (line 920) | fn test_match_or_not_v2() {
function test_case_option_v2 (line 940) | fn test_case_option_v2() {
function test_matcher_quality_v2 (line 958) | fn test_matcher_quality_v2() {
function test_reuse_should_not_affect_indices (line 979) | fn test_reuse_should_not_affect_indices() {
FILE: src/fuzzy_matcher/util.rs
function cheap_matches (line 3) | pub fn cheap_matches(choice: &[char], pattern: &[char], case_sensitive: ...
function char_equal (line 29) | pub fn char_equal(a: char, b: char, case_sensitive: bool) -> bool {
type CharType (line 48) | pub enum CharType {
function char_type_of (line 56) | pub fn char_type_of(ch: char) -> CharType {
type CharRole (line 69) | pub enum CharRole {
function char_role (line 89) | pub fn char_role(prev: char, cur: char) -> CharRole {
function assert_order (line 99) | pub fn assert_order(matcher: &dyn FuzzyMatcher, pattern: &str, choices: ...
function filter_and_sort (line 118) | pub fn filter_and_sort(matcher: &dyn FuzzyMatcher, pattern: &str, lines:...
function wrap_matches (line 128) | pub fn wrap_matches(line: &str, indices: &[IndexType]) -> String {
FILE: src/helper/item.rs
type DefaultSkimItem (line 23) | pub struct DefaultSkimItem {
method new (line 55) | pub fn new(
method contains_ansi_escape (line 173) | fn contains_ansi_escape(s: &str) -> bool {
method stripped_text (line 179) | pub fn stripped_text(&self) -> Option<&str> {
method orig_text (line 191) | pub fn orig_text(&self) -> Option<&str> {
method ansi_info (line 203) | pub fn ansi_info(&self) -> Option<&Vec<(usize, usize)>> {
method matching_ranges (line 215) | pub fn matching_ranges(&self) -> Option<&[(usize, usize)]> {
method get_display_text (line 229) | pub fn get_display_text(&self) -> &str {
method from (line 235) | fn from(value: String) -> Self {
type DefaultSkimItemMetadata (line 33) | pub struct DefaultSkimItemMetadata {
method text (line 245) | fn text(&self) -> Cow<'_, str> {
method output (line 254) | fn output(&self) -> Cow<'_, str> {
method get_matching_ranges (line 262) | fn get_matching_ranges(&self) -> Option<&[(usize, usize)]> {
method display (line 270) | fn display(&self, context: DisplayContext) -> Line<'_> {
function strip_ansi (line 467) | pub fn strip_ansi(text: &str) -> (String, Vec<(usize, usize)>) {
function escape_ansi (line 537) | fn escape_ansi(raw: &str) -> String {
function test_strip_ansi (line 546) | fn test_strip_ansi() {
function test_ansi_matching_and_display (line 642) | fn test_ansi_matching_and_display() {
function test_ansi_char_indices_mapping (line 683) | fn test_ansi_char_indices_mapping() {
function test_text_returns_stripped (line 717) | fn test_text_returns_stripped() {
function test_highlighting_applied (line 753) | fn test_highlighting_applied() {
function test_char_range_highlighting (line 791) | fn test_char_range_highlighting() {
function test_byte_range_highlighting (line 831) | fn test_byte_range_highlighting() {
function test_matching_with_ansi_basic (line 871) | fn test_matching_with_ansi_basic() {
function test_null_delimiter_with_matching_fields (line 899) | fn test_null_delimiter_with_matching_fields() {
FILE: src/helper/item_reader.rs
constant DELIMITER_STR (line 18) | const DELIMITER_STR: &str = r"[\t\n ]+";
constant READ_BUFFER_SIZE (line 19) | const READ_BUFFER_SIZE: usize = 1024;
constant ITEMS_BUFFER_SIZE (line 20) | const ITEMS_BUFFER_SIZE: usize = 1024;
constant SEND_TIMEOUT_MS (line 21) | const SEND_TIMEOUT_MS: u64 = 100;
type CollectorInput (line 23) | pub enum CollectorInput {
type SkimItemReaderOption (line 30) | pub struct SkimItemReaderOption {
method from_options (line 57) | pub fn from_options(options: &SkimOptions) -> Self {
method buf_size (line 79) | pub fn buf_size(mut self, buf_size: usize) -> Self {
method line_ending (line 86) | pub fn line_ending(mut self, line_ending: u8) -> Self {
method ansi (line 93) | pub fn ansi(mut self, enable: bool) -> Self {
method delimiter (line 100) | pub fn delimiter(mut self, delimiter: Regex) -> Self {
method with_nth (line 107) | pub fn with_nth<'a, T>(mut self, with_nth: T) -> Self
method transform_fields (line 117) | pub fn transform_fields(mut self, transform_fields: Vec<FieldRange>) -...
method nth (line 124) | pub fn nth<'a, T>(mut self, nth: T) -> Self
method matching_fields (line 134) | pub fn matching_fields(mut self, matching_fields: Vec<FieldRange>) -> ...
method read0 (line 141) | pub fn read0(mut self, enable: bool) -> Self {
method show_error (line 152) | pub fn show_error(mut self, show_error: bool) -> Self {
method build (line 159) | pub fn build(self) -> Self {
method is_simple (line 165) | pub fn is_simple(&self) -> bool {
method default (line 41) | fn default() -> Self {
type SkimItemReader (line 171) | pub struct SkimItemReader {
method new (line 186) | pub fn new(option: SkimItemReaderOption) -> Self {
method option (line 194) | pub fn option(mut self, option: SkimItemReaderOption) -> Self {
method of_bufread (line 202) | pub fn of_bufread(&self, source: impl BufRead + Send + 'static) -> Ski...
method read_lines_into_items (line 213) | fn read_lines_into_items(
method raw_bufread (line 285) | fn raw_bufread(&self, source: impl BufRead + Send + 'static) -> SkimIt...
method read_and_collect_from_command (line 298) | fn read_and_collect_from_command(
method default (line 176) | fn default() -> Self {
method invoke (line 390) | fn invoke(
type CommandOutput (line 399) | type CommandOutput = (Option<Child>, Box<dyn BufRead + Send>);
function get_command_output (line 401) | fn get_command_output(cmd: &str, send_error: bool) -> Result<CommandOutp...
FILE: src/helper/selector.rs
type DefaultSkimSelector (line 9) | pub struct DefaultSkimSelector {
method first_n (line 18) | pub fn first_n(mut self, first_n: usize) -> Self {
method preset (line 26) | pub fn preset(mut self, preset: impl IntoIterator<Item = String>) -> S...
method regex (line 39) | pub fn regex(mut self, regex: &str) -> Self {
method should_select (line 49) | fn should_select(&self, index: usize, item: &dyn SkimItem) -> bool {
function test_first_n (line 76) | pub fn test_first_n() {
function test_preset (line 86) | pub fn test_preset() {
function test_regex (line 95) | pub fn test_regex() {
function test_all_together (line 105) | pub fn test_all_together() {
FILE: src/item.rs
type RankBuilder (line 25) | pub struct RankBuilder {
method new (line 40) | pub fn new(mut criterion: Vec<RankCriteria>) -> Self {
method criteria (line 51) | pub fn criteria(&self) -> &[RankCriteria] {
method path_name_offset (line 57) | fn path_name_offset(text: &str) -> i32 {
method build_rank (line 68) | pub fn build_rank(&self, score: i32, begin: usize, end: usize, item_te...
method default (line 30) | fn default() -> Self {
method sort_key (line 87) | pub fn sort_key(&self, criteria: &[RankCriteria]) -> [i32; 5] {
type MatchedItem (line 116) | pub struct MatchedItem {
method fmt (line 128) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method sorted_merge (line 159) | pub fn sorted_merge(existing: Vec<MatchedItem>, incoming: Vec<MatchedI...
method merge_into_sorted (line 230) | pub fn merge_into_sorted(existing: &mut Vec<MatchedItem>, incoming: Ve...
method downcast_item (line 260) | pub fn downcast_item<T: SkimItem>(&self) -> Option<&T> {
method hash (line 139) | fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
type Target (line 146) | type Target = Arc<dyn SkimItem>;
method deref (line 148) | fn deref(&self) -> &Self::Target {
method eq (line 268) | fn eq(&self, other: &Self) -> bool {
method partial_cmp (line 276) | fn partial_cmp(&self, other: &Self) -> Option<CmpOrd> {
method cmp (line 282) | fn cmp(&self, other: &Self) -> CmpOrd {
constant ITEM_POOL_CAPACITY (line 289) | const ITEM_POOL_CAPACITY: usize = 16384;
type ItemPool (line 292) | pub struct ItemPool {
method new (line 332) | pub fn new() -> Self {
method from_options (line 338) | pub fn from_options(options: &crate::SkimOptions) -> Self {
method len (line 351) | pub fn len(&self) -> usize {
method is_empty (line 356) | pub fn is_empty(&self) -> bool {
method num_not_taken (line 361) | pub fn num_not_taken(&self) -> usize {
method num_taken (line 366) | pub fn num_taken(&self) -> usize {
method clear (line 371) | pub fn clear(&self) {
method reset (line 381) | pub fn reset(&self) {
method append (line 389) | pub fn append(&self, mut items: Vec<Arc<dyn SkimItem>>) -> usize {
method take (line 434) | pub fn take(&self) -> Vec<Arc<dyn SkimItem>> {
method reserved (line 444) | pub fn reserved(&self) -> Vec<Arc<dyn SkimItem>> {
method default (line 316) | fn default() -> Self {
type ItemPoolGuard (line 451) | pub struct ItemPoolGuard<'a, T: Sized + 'a> {
type Target (line 457) | type Target = [T];
method deref (line 459) | fn deref(&self) -> &[T] {
type RankCriteria (line 467) | pub enum RankCriteria {
method value_variants (line 496) | fn value_variants<'a>() -> &'a [Self] {
method to_possible_value (line 516) | fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
FILE: src/lib.rs
type AsAny (line 79) | pub trait AsAny {
method as_any (line 81) | fn as_any(&self) -> &dyn Any;
method as_any_mut (line 83) | fn as_any_mut(&mut self) -> &mut dyn Any;
method as_any (line 87) | fn as_any(&self) -> &dyn Any {
method as_any_mut (line 91) | fn as_any_mut(&mut self) -> &mut dyn Any {
type Matches (line 100) | pub enum Matches {
type DisplayContext (line 114) | pub struct DisplayContext {
method to_line (line 134) | pub fn to_line(self, cow: Cow<str>) -> Line {
type PreviewContext (line 205) | pub struct PreviewContext<'a> {
type PreviewPosition (line 229) | pub struct PreviewPosition {
type ItemPreview (line 241) | pub enum ItemPreview {
type CaseMatching (line 264) | pub enum CaseMatching {
type Typos (line 278) | pub enum Typos {
method from (line 289) | fn from(n: usize) -> Self {
type MatchRange (line 299) | pub enum MatchRange {
type Rank (line 313) | pub struct Rank {
type MatchResult (line 331) | pub struct MatchResult {
method range_char_indices (line 341) | pub fn range_char_indices(&self, text: &str) -> MatchIndices {
type MatchEngine (line 354) | pub trait MatchEngine: Sync + Send + Display {
method match_item (line 356) | fn match_item(&self, item: &dyn SkimItem) -> Option<MatchResult>;
type MatchEngineFactory (line 360) | pub trait MatchEngineFactory {
method create_engine_with_case (line 362) | fn create_engine_with_case(&self, query: &str, case: CaseMatching) -> ...
method create_engine (line 364) | fn create_engine(&self, query: &str) -> Box<dyn MatchEngine> {
type Selector (line 373) | pub trait Selector {
method should_select (line 375) | fn should_select(&self, index: usize, item: &dyn SkimItem) -> bool;
type SkimItemSender (line 380) | pub type SkimItemSender = kanal::Sender<Vec<Arc<dyn SkimItem>>>;
type SkimItemReceiver (line 382) | pub type SkimItemReceiver = kanal::Receiver<Vec<Arc<dyn SkimItem>>>;
FILE: src/manpage.rs
constant THEME_SECTION (line 11) | const THEME_SECTION: &str = "
constant EXIT_CODES_SECTION (line 61) | const EXIT_CODES_SECTION: &str = "
constant NORMAL_MODE_SS (line 68) | const NORMAL_MODE_SS: &str = "
constant INTERACTIVE_MODE_SS (line 73) | const INTERACTIVE_MODE_SS: &str = "
constant KEYS_SS (line 83) | const KEYS_SS: &str = "
constant ACTIONS_SS (line 123) | const ACTIONS_SS: &str = "
constant REMOTE_SECTION (line 186) | const REMOTE_SECTION: &str = "
function parse_str (line 197) | fn parse_str(src: &str) -> Vec<Inline> {
function section (line 206) | fn section(c: &mut Roff, name: &str, content: &str) {
function subsection (line 211) | fn subsection(c: &mut Roff, name: &str, content: &str) {
function generate (line 224) | pub fn generate<W>(w: &mut W) -> Result<()>
FILE: src/matcher.rs
type MatcherControl (line 19) | pub struct MatcherControl {
method get_num_processed (line 40) | pub fn get_num_processed(&self) -> usize {
method get_num_matched (line 46) | pub fn get_num_matched(&self) -> usize {
method kill (line 51) | pub fn kill(&mut self) {
method stopped (line 57) | pub fn stopped(&self) -> bool {
method default (line 27) | fn default() -> Self {
method drop (line 63) | fn drop(&mut self) {
type Matcher (line 70) | pub struct Matcher {
method builder (line 79) | pub fn builder(engine_factory: Rc<dyn MatchEngineFactory>) -> Self {
method case (line 89) | pub fn case(mut self, case_matching: CaseMatching) -> Self {
method rank_builder (line 96) | pub fn rank_builder(mut self, rank_builder: Arc<RankBuilder>) -> Self {
method build (line 103) | pub fn build(self) -> Self {
method create_engine_factory (line 112) | pub fn create_engine_factory(options: &SkimOptions) -> Rc<dyn MatchEng...
method create_engine_factory_with_builder (line 121) | pub fn create_engine_factory_with_builder(options: &SkimOptions) -> (R...
method from_options (line 163) | pub fn from_options(options: &SkimOptions) -> Self {
method case_matching (line 173) | pub fn case_matching(&self) -> CaseMatching {
method engine_factory (line 179) | pub fn engine_factory(&self) -> &Rc<dyn MatchEngineFactory> {
method run (line 187) | pub fn run<C>(
FILE: src/options.rs
function parse_delimiter_value (line 27) | fn parse_delimiter_value(s: &str) -> Result<Regex, String> {
function parse_typos (line 38) | fn parse_typos(s: &str) -> Result<Typos, String> {
type MatchScheme (line 53) | pub enum MatchScheme {
type SkimOptions (line 77) | pub struct SkimOptions {
method build (line 1146) | pub fn build(mut self) -> Self {
method init_histories (line 1199) | pub fn init_histories(&mut self) {
method from_env (line 1219) | pub fn from_env() -> Result<Self, clap::Error> {
method default (line 979) | fn default() -> Self {
method build (line 1138) | pub fn build(&mut self) -> Result<SkimOptions, SkimOptionsBuilderError> {
type FeatureFlag (line 1283) | pub enum FeatureFlag {
FILE: src/output.rs
type SkimOutput (line 6) | pub struct SkimOutput {
FILE: src/reader.rs
type CommandCollector (line 15) | pub trait CommandCollector {
method invoke (line 23) | fn invoke(
type ReaderControl (line 31) | pub struct ReaderControl {
method kill (line 40) | pub fn kill(&mut self) {
method take (line 53) | pub fn take(&self) -> Vec<Arc<dyn SkimItem>> {
method is_done (line 62) | pub fn is_done(&self) -> bool {
method drop (line 69) | fn drop(&mut self) {
type Reader (line 75) | pub struct Reader {
method from_options (line 83) | pub fn from_options(options: &SkimOptions) -> Self {
method source (line 92) | pub fn source(mut self, rx_item: Option<SkimItemReceiver>) -> Self {
method run (line 98) | pub fn run(&mut self, app_tx: Sender<Vec<Arc<dyn SkimItem>>>, cmd: &st...
method collect (line 124) | pub fn collect(&mut self, item_pool: Arc<ItemPool>, cmd: &str) -> Read...
method default (line 153) | fn default() -> Self {
function collect_items (line 161) | fn collect_items<F>(components_to_stop: Arc<AtomicUsize>, rx_item: SkimI...
FILE: src/shell.rs
type Shell (line 8) | pub enum Shell {
function generate_completions (line 24) | pub fn generate_completions(sh: &Shell) {
function generate_key_bindings (line 46) | pub fn generate_key_bindings(sh: &Shell) {
FILE: src/skim.rs
type Skim (line 17) | pub struct Skim<Backend = ratatui::backend::CrosstermBackend<BufWriter<S...
method run_with (line 56) | pub fn run_with(options: SkimOptions, source: Option<SkimItemReceiver>...
method run_items (line 98) | pub fn run_items<I, T>(options: SkimOptions, items: I) -> Result<SkimO...
method init_tui (line 122) | pub fn init_tui(&mut self) -> Result<()> {
function init (line 137) | pub fn init(options: SkimOptions, source: Option<SkimItemReceiver>) -> R...
function start (line 179) | pub fn start(&mut self) {
function handle_reload (line 189) | pub fn handle_reload(&mut self, new_cmd: &str) {
function check_reader (line 213) | pub fn check_reader(&mut self) -> bool {
function reader_done (line 229) | pub fn reader_done(&self) -> bool {
function matcher_stopped (line 238) | pub fn matcher_stopped(&self) -> bool {
function init_tui_with (line 246) | pub fn init_tui_with(&mut self, tui: Tui<Backend>) {
function app (line 251) | pub fn app(&self) -> &App {
function app_mut (line 256) | pub fn app_mut(&mut self) -> &mut App {
function tui_ref (line 265) | pub fn tui_ref(&self) -> &Tui<Backend> {
function tui_mut (line 274) | pub fn tui_mut(&mut self) -> &mut Tui<Backend> {
function app_and_tui (line 287) | pub fn app_and_tui(&mut self) -> (&mut App, &mut Tui<Backend>) {
function final_event (line 295) | pub fn final_event(&self) -> &Event {
function event_sender (line 310) | pub fn event_sender(&self) -> tokio::sync::mpsc::Sender<Event> {
function enter (line 333) | pub async fn enter(&mut self) -> Result<()> {
function should_enter (line 347) | pub fn should_enter(&mut self) -> bool {
function init_listener (line 422) | fn init_listener(&mut self) -> Result<()> {
function output (line 441) | pub fn output(mut self) -> SkimOutput {
function should_quit (line 479) | pub fn should_quit(&self) -> bool {
function tick (line 506) | pub async fn tick(&mut self) -> Result<bool> {
function run (line 578) | pub async fn run(&mut self) -> Result<()> {
function run_until (line 612) | pub async fn run_until<F: Future + 'static>(mut self, user_task: F) -> R...
FILE: src/skim_item.rs
type SkimItem (line 49) | pub trait SkimItem: AsAny + Send + Sync + 'static {
method text (line 51) | fn text(&self) -> Cow<'_, str>;
method display (line 54) | fn display(&self, context: DisplayContext) -> Line<'_> {
method preview (line 60) | fn preview(&self, _context: PreviewContext) -> ItemPreview {
method output (line 69) | fn output(&self) -> Cow<'_, str> {
method get_matching_ranges (line 75) | fn get_matching_ranges(&self) -> Option<&[(usize, usize)]> {
method text (line 84) | fn text(&self) -> Cow<'_, str> {
method fmt (line 90) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method fmt (line 95) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
FILE: src/spinlock.rs
type SpinLock (line 15) | pub struct SpinLock<T: ?Sized> {
type SpinLockGuard (line 24) | pub struct SpinLockGuard<'a, T: ?Sized + 'a> {
function new (line 32) | pub fn new(pool: &'a SpinLock<T>) -> SpinLockGuard<'a, T> {
function new (line 41) | pub fn new(t: T) -> SpinLock<T> {
function lock (line 51) | pub fn lock(&self) -> SpinLockGuard<'_, T> {
type Target (line 62) | type Target = T;
method deref (line 64) | fn deref(&self) -> &T {
method deref_mut (line 70) | fn deref_mut(&mut self) -> &mut T {
method drop (line 77) | fn drop(&mut self) {
function smoke (line 95) | fn smoke() {
function lots_and_lots (line 102) | fn lots_and_lots() {
function test_mutex_unsized (line 137) | fn test_mutex_unsized() {
FILE: src/theme.rs
type ColorTheme (line 18) | pub struct ColorTheme {
method init_from_options (line 56) | pub fn init_from_options(options: &SkimOptions) -> ColorTheme {
method none (line 69) | fn none() -> Self {
method bw (line 87) | fn bw() -> Self {
method default16 (line 97) | fn default16() -> Self {
method dark256 (line 114) | fn dark256() -> Self {
method molokai256 (line 131) | fn molokai256() -> Self {
method light256 (line 148) | fn light256() -> Self {
method catppuccin_mocha (line 166) | fn catppuccin_mocha() -> Self {
method catppuccin_macchiato (line 193) | fn catppuccin_macchiato() -> Self {
method catppuccin_latte (line 220) | fn catppuccin_latte() -> Self {
method catppuccin_frappe (line 247) | fn catppuccin_frappe() -> Self {
method set_color (line 274) | fn set_color(&mut self, name: &str, spec: &str) {
method from_options (line 352) | fn from_options(color: &str) -> Self {
method default (line 47) | fn default() -> Self {
function set_style (line 380) | fn set_style(s: &mut Style, layer: &str, color: Option<Color>, modifier:...
function test_base_themes (line 397) | fn test_base_themes() {
function test_from_options_base_themes (line 425) | fn test_from_options_base_themes() {
function test_ansi_color_parsing (line 449) | fn test_ansi_color_parsing() {
function test_rgb_hex_color_parsing (line 459) | fn test_rgb_hex_color_parsing() {
function test_color_with_modifiers (line 472) | fn test_color_with_modifiers() {
function test_modifier_shortcuts (line 491) | fn test_modifier_shortcuts() {
function test_regular_modifier_reset (line 513) | fn test_regular_modifier_reset() {
function test_multiple_color_components (line 524) | fn test_multiple_color_components() {
function test_component_name_aliases (line 538) | fn test_component_name_aliases() {
function test_background_color (line 561) | fn test_background_color() {
function test_base_theme_with_overrides (line 571) | fn test_base_theme_with_overrides() {
function test_all_component_names (line 580) | fn test_all_component_names() {
function test_invalid_color_graceful_handling (line 621) | fn test_invalid_color_graceful_handling() {
function test_init_from_options (line 639) | fn test_init_from_options() {
function test_complex_color_spec (line 650) | fn test_complex_color_spec() {
function test_minus_one_color_reset (line 663) | fn test_minus_one_color_reset() {
FILE: src/tmux.rs
type TmuxWindowDir (line 32) | enum TmuxWindowDir {
method from (line 41) | fn from(value: &str) -> Self {
type TmuxOptions (line 54) | struct TmuxOptions<'a> {
type SkimTmuxOutput (line 61) | struct SkimTmuxOutput {
method text (line 66) | fn text(&self) -> Cow<'_, str> {
function from (line 72) | fn from(value: &'a String) -> Self {
function run_with (line 109) | pub fn run_with(opts: &SkimOptions) -> Option<SkimOutput> {
function push_quoted_arg (line 348) | fn push_quoted_arg(args_str: &mut String, arg: &str) {
function sanitize_value (line 365) | fn sanitize_value(value: String) -> String {
function check (line 379) | fn check(input: &str, height: &str, width: &str, x: &str, y: &str) {
function tmux_options_default (line 387) | fn tmux_options_default() {
function tmux_options_center (line 391) | fn tmux_options_center() {
function tmux_options_top (line 400) | fn tmux_options_top() {
function tmux_options_bottom (line 409) | fn tmux_options_bottom() {
function tmux_options_left (line 418) | fn tmux_options_left() {
function tmux_options_right (line 427) | fn tmux_options_right() {
function test_sanitize_value (line 437) | fn test_sanitize_value() {
FILE: src/tui/app.rs
constant FRAME_TIME_MS (line 42) | const FRAME_TIME_MS: u128 = 1000 / 30;
constant MATCHER_DEBOUNCE_MS (line 43) | const MATCHER_DEBOUNCE_MS: u128 = 200;
constant HIDE_GRACE_MS (line 44) | const HIDE_GRACE_MS: u128 = 500;
type App (line 47) | pub struct App {
method from_options (line 243) | pub fn from_options(options: SkimOptions, theme: Arc<crate::theme::Col...
method resize (line 301) | pub fn resize(&mut self, cols: u16, rows: u16) {
method calculate_preview_offset (line 309) | fn calculate_preview_offset(&self, offset_expr: &str) -> u16 {
method needs_render (line 338) | fn needs_render(&mut self) {
method on_items_updated (line 343) | fn on_items_updated(&mut self) {
method on_selection_changed (line 352) | fn on_selection_changed() -> Vec<Event> {
method on_query_changed (line 357) | fn on_query_changed(&mut self) -> Vec<Event> {
method update_spinner (line 370) | fn update_spinner(&mut self) {
method run_preview (line 390) | fn run_preview<B: Backend>(&mut self, tui: &mut Tui<B>) -> Result<()>
method handle_event (line 507) | pub fn handle_event<B: Backend>(&mut self, tui: &mut Tui<B>, event: &E...
method handle_items (line 620) | pub fn handle_items(&mut self, items: Vec<Arc<dyn SkimItem>>) {
method handle_key (line 625) | fn handle_key(&mut self, key: &KeyEvent) -> Vec<Event> {
method handle_action (line 654) | fn handle_action(&mut self, act: &Action) -> Result<Vec<Event>> {
method results (line 1134) | pub fn results(&mut self) -> Vec<MatchedItem> {
method restart_matcher (line 1152) | pub fn restart_matcher(&mut self, force: bool) {
method yank (line 1240) | fn yank(&mut self, contents: String) {
method expand_cmd (line 1249) | pub fn expand_cmd(&self, cmd: &str, quote_args: bool) -> String {
method restart_matcher_debounced (line 1263) | fn restart_matcher_debounced(&mut self) {
method handle_mouse (line 1281) | fn handle_mouse<B: Backend>(&mut self, mouse_event: MouseEvent, tui: &...
method toggle_spinner (line 1330) | fn toggle_spinner(&mut self) {
method render (line 124) | fn render(self, area: Rect, buf: &mut Buffer) {
method default (line 181) | fn default() -> Self {
FILE: src/tui/backend.rs
constant TICK_RATE (line 22) | const TICK_RATE: f64 = 12.;
type Tui (line 26) | pub struct Tui<B: Backend = ratatui::backend::CrosstermBackend<BufWriter...
method new_with_height (line 52) | pub fn new_with_height(height: Size) -> Result<Self> {
function new_with_height_and_backend (line 71) | pub fn new_with_height_and_backend(backend: B, height: Size) -> Result<S...
function new_for_test (line 123) | pub fn new_for_test(backend: B) -> Result<Self> {
function enter (line 141) | pub fn enter(&mut self) -> Result<()> {
function exit (line 156) | pub fn exit(&mut self) -> Result<()> {
function stop (line 180) | pub fn stop(&self) {
function cancel (line 184) | pub fn cancel(&self) {
function start (line 188) | pub fn start(&mut self) {
function next (line 237) | pub async fn next(&mut self) -> Option<Event> {
type Target (line 246) | type Target = ratatui::Terminal<B>;
method deref (line 248) | fn deref(&self) -> &Self::Target {
method deref_mut (line 257) | fn deref_mut(&mut self) -> &mut Self::Target {
method drop (line 266) | fn drop(&mut self) {
function set_panic_hook (line 274) | fn set_panic_hook() {
FILE: src/tui/event.rs
type BoxError (line 9) | type BoxError = Box<dyn std::error::Error + Sync + Send>;
type BoxFuture (line 10) | type BoxFuture<'a> = Pin<Box<dyn Future<Output = Result<Vec<Event>, BoxE...
type AsyncCallbackFn (line 16) | trait AsyncCallbackFn: Send {
method call (line 17) | fn call<'a>(&'a self, app: &'a mut crate::tui::App) -> BoxFuture<'a>;
method call (line 28) | fn call<'a>(&'a self, app: &'a mut crate::tui::App) -> BoxFuture<'a> {
method call (line 40) | fn call<'a>(&'a self, app: &'a mut crate::tui::App) -> BoxFuture<'a> {
type AsyncFnWrapper (line 21) | struct AsyncFnWrapper<F>(F);
type SyncFnWrapper (line 34) | struct SyncFnWrapper<F>(F);
type ActionCallback (line 54) | pub struct ActionCallback(Arc<Mutex<dyn AsyncCallbackFn>>);
method fmt (line 57) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method new (line 71) | pub fn new<F, Fut>(f: F) -> Self
method new_sync (line 89) | pub fn new_sync<F>(f: F) -> Self
method call (line 99) | pub(crate) fn call(&self, app: &mut crate::tui::App) -> Result<Vec<Eve...
type Event (line 112) | pub enum Event {
type Action (line 155) | pub enum Action {
function parse_action (line 307) | pub fn parse_action(raw_action: &str) -> Option<Action> {
FILE: src/tui/header.rs
type Header (line 29) | pub struct Header {
method theme (line 52) | pub fn theme(mut self, theme: Arc<ColorTheme>) -> Self {
method height (line 62) | pub fn height(&self) -> u16 {
method set_header_lines (line 72) | pub fn set_header_lines(&mut self, items: Vec<Arc<dyn SkimItem>>) {
method header_text (line 78) | fn header_text<'a>(&self) -> Text<'a> {
function apply_tabstop (line 86) | fn apply_tabstop(text: &str, tabstop: usize) -> String {
method from_options (line 105) | fn from_options(options: &SkimOptions, theme: Arc<ColorTheme>) -> Self {
method render (line 133) | fn render(&mut self, area: Rect, buf: &mut Buffer) -> SkimRender {
FILE: src/tui/input.rs
constant SPINNER_DURATION (line 18) | const SPINNER_DURATION: u32 = 200;
constant SPINNERS_UNICODE (line 19) | const SPINNERS_UNICODE: [char; 10] = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦',...
type StatusInfo (line 23) | pub struct StatusInfo {
method left_title (line 49) | pub fn left_title(&self) -> String {
method inline_separator (line 89) | pub fn inline_separator(&self) -> char {
method inline_status (line 104) | pub fn inline_status(&self) -> String {
method right_title (line 130) | pub fn right_title(&self) -> String {
type Input (line 135) | pub struct Input {
method insert (line 174) | pub fn insert(&mut self, c: char) {
method insert_str (line 179) | pub fn insert_str(&mut self, s: &str) {
method nchars (line 188) | fn nchars(&self) -> usize {
method delete (line 191) | pub fn delete(&mut self, offset: i32) -> Option<char> {
method move_cursor (line 207) | pub fn move_cursor(&mut self, offset: i32) {
method move_cursor_to (line 219) | pub fn move_cursor_to(&mut self, pos: u16) {
method move_to_end (line 226) | pub fn move_to_end(&mut self) {
method is_word_char (line 236) | fn is_word_char(ch: char) -> bool {
method find_next_word_end (line 241) | fn find_next_word_end(&self, start_pos: usize) -> usize {
method find_compound_word_end (line 266) | fn find_compound_word_end(&self, start_pos: usize) -> usize {
method find_prev_word_start (line 291) | fn find_prev_word_start(&self, start_pos: usize) -> usize {
method find_delete_backward_pos (line 315) | fn find_delete_backward_pos(&self, start_pos: usize) -> usize {
method delete_backward_word (line 343) | pub fn delete_backward_word(&mut self) -> String {
method delete_backward_to_whitespace (line 359) | pub fn delete_backward_to_whitespace(&mut self) -> String {
method delete_forward_word (line 382) | pub fn delete_forward_word(&mut self) -> String {
method move_cursor_forward_word (line 391) | pub fn move_cursor_forward_word(&mut self) {
method move_cursor_backward_word (line 396) | pub fn move_cursor_backward_word(&mut self) {
method delete_to_beginning (line 400) | pub fn delete_to_beginning(&mut self) -> String {
method cursor_pos (line 406) | pub fn cursor_pos(&self) -> u16 {
method switch_mode (line 411) | pub fn switch_mode(&mut self) {
method default (line 156) | fn default() -> Self {
method from_options (line 419) | fn from_options(options: &SkimOptions, theme: Arc<ColorTheme>) -> Self {
method render (line 443) | fn render(&mut self, area: Rect, buf: &mut Buffer) -> SkimRender {
type Target (line 553) | type Target = String;
method deref (line 555) | fn deref(&self) -> &Self::Target {
method deref_mut (line 561) | fn deref_mut(&mut self) -> &mut Self::Target {
FILE: src/tui/item_list.rs
type MergeStrategy (line 24) | pub(crate) enum MergeStrategy {
type ProcessedItems (line 35) | pub(crate) struct ProcessedItems {
method default (line 41) | fn default() -> Self {
type ItemList (line 50) | pub struct ItemList {
method cursor (line 121) | fn cursor(&self) -> usize {
method count (line 129) | pub fn count(&self) -> usize {
method selected (line 135) | pub fn selected(&self) -> Option<MatchedItem> {
method append (line 140) | pub fn append(&mut self, items: &mut Vec<MatchedItem>) {
method calc_skip_width (line 147) | fn calc_skip_width(&self, text: &str) -> usize {
method calc_hscroll (line 158) | fn calc_hscroll(
method apply_hscroll (line 268) | fn apply_hscroll<'a>(
method char_index_at_width (line 353) | fn char_index_at_width(&self, line: &Line<'_>, target_width: usize) ->...
method expand_tabs (line 377) | fn expand_tabs(&self, text: &str, start_width: usize) -> String {
method toggle_at (line 396) | pub fn toggle_at(&mut self, index: usize) {
method toggle (line 409) | pub fn toggle(&mut self) {
method toggle_all (line 413) | pub fn toggle_all(&mut self) {
method select (line 420) | pub fn select(&mut self) {
method select_row (line 426) | pub fn select_row(&mut self, index: usize) {
method select_all (line 431) | pub fn select_all(&mut self) {
method clear_selection (line 437) | pub fn clear_selection(&mut self) {
method clear (line 441) | pub fn clear(&mut self) {
method scroll_by (line 449) | pub fn scroll_by(&mut self, offset: i32) {
method select_previous (line 469) | pub fn select_previous(&mut self) {
method select_next (line 473) | pub fn select_next(&mut self) {
method jump_to_first (line 477) | pub fn jump_to_first(&mut self) {
method jump_to_last (line 483) | pub fn jump_to_last(&mut self) {
method default (line 84) | fn default() -> Self {
method from_options (line 491) | fn from_options(options: &SkimOptions, theme: Arc<ColorTheme>) -> Self {
method render (line 588) | fn render(&mut self, area: ratatui::prelude::Rect, buf: &mut ratatui::pr...
function toggle_item (line 813) | fn toggle_item(sel: &mut IndexSet<MatchedItem>, item: &MatchedItem) {
FILE: src/tui/layout.rs
type PreviewPlacement (line 30) | enum PreviewPlacement {
type LayoutTemplate (line 48) | pub struct LayoutTemplate {
method from_options (line 76) | pub fn from_options(options: &SkimOptions, header_height: u16) -> Self {
method apply (line 164) | pub fn apply(&self, area: Rect) -> AppLayout {
type AppLayout (line 212) | pub struct AppLayout {
method compute (line 231) | pub fn compute(area: Rect, options: &SkimOptions, header_height: u16) ...
function size_to_constraint (line 240) | fn size_to_constraint(size: Size) -> Constraint {
function area (line 258) | fn area() -> Rect {
function assert_full_width (line 265) | fn assert_full_width(rect: Rect, area: Rect, label: &str) {
function assert_vertically_adjacent (line 272) | fn assert_vertically_adjacent(a: Rect, b: Rect, label: &str) {
function assert_horizontally_adjacent (line 278) | fn assert_horizontally_adjacent(a: Rect, b: Rect, label: &str) {
function compute (line 284) | fn compute(options: &SkimOptions) -> AppLayout {
function compute_with_header_height (line 288) | fn compute_with_header_height(options: &SkimOptions, header_height: u16)...
function opts (line 292) | fn opts() -> SkimOptionsBuilder {
function default_no_preview_no_header (line 299) | fn default_no_preview_no_header() {
function default_inline_info_no_header (line 318) | fn default_inline_info_no_header() {
function default_hidden_info_no_header (line 328) | fn default_hidden_info_no_header() {
function default_with_header (line 338) | fn default_with_header() {
function default_with_multiline_header (line 355) | fn default_with_multiline_header() {
function default_with_header_lines (line 365) | fn default_with_header_lines() {
function template_apply_different_areas (line 377) | fn template_apply_different_areas() {
function reverse_no_preview_no_header (line 396) | fn reverse_no_preview_no_header() {
function reverse_with_header (line 411) | fn reverse_with_header() {
function reverse_list_no_preview_no_header (line 429) | fn reverse_list_no_preview_no_header() {
function reverse_list_with_header (line 441) | fn reverse_list_with_header() {
function default_preview_right_50_percent (line 457) | fn default_preview_right_50_percent() {
function default_preview_left_30_percent (line 477) | fn default_preview_left_30_percent() {
function default_preview_right_fixed_20 (line 495) | fn default_preview_right_fixed_20() {
function reverse_preview_left (line 509) | fn reverse_preview_left() {
function default_preview_up_50_percent (line 529) | fn default_preview_up_50_percent() {
function default_preview_down_50_percent (line 551) | fn default_preview_down_50_percent() {
function default_preview_up_fixed_8 (line 573) | fn default_preview_up_fixed_8() {
function preview_hidden_produces_no_preview_area (line 592) | fn preview_hidden_produces_no_preview_area() {
function no_preview_command_produces_no_preview_area (line 606) | fn no_preview_command_produces_no_preview_area() {
function default_with_borders_no_header (line 618) | fn default_with_borders_no_header() {
function default_with_borders_and_header (line 629) | fn default_with_borders_and_header() {
function reverse_with_borders (line 645) | fn reverse_with_borders() {
function all_areas_non_overlapping_default (line 663) | fn all_areas_non_overlapping_default() {
function all_areas_non_overlapping_reverse (line 695) | fn all_areas_non_overlapping_reverse() {
function total_height_is_area_height_default (line 721) | fn total_height_is_area_height_default() {
function total_width_is_area_width_with_right_preview (line 730) | fn total_width_is_area_width_with_right_preview() {
function total_height_is_area_height_with_down_preview (line 742) | fn total_height_is_area_height_with_down_preview() {
function reverse_list_with_header_and_preview_right (line 756) | fn reverse_list_with_header_and_preview_right() {
function very_small_area (line 779) | fn very_small_area() {
FILE: src/tui/mod.rs
type Size (line 37) | pub enum Size {
type Error (line 82) | type Error = SizeParseError;
method try_from (line 84) | fn try_from(value: &str) -> Result<Self, Self::Error> {
type Direction (line 46) | pub enum Direction {
type Error (line 58) | type Error = &'static str;
method try_from (line 59) | fn try_from(value: &str) -> Result<Self, Self::Error> {
type SizeParseError (line 72) | pub enum SizeParseError {
method default (line 106) | fn default() -> Self {
type BorderType (line 117) | pub enum BorderType {
function from (line 138) | fn from(val: BorderType) -> Self {
function fixed_success (line 161) | fn fixed_success() {
function percent_success (line 165) | fn percent_success() {
function fixed_neg (line 169) | fn fixed_neg() {
function percent_neg (line 177) | fn percent_neg() {
function percent_over_100 (line 185) | fn percent_over_100() {
function fixed_invalid_char (line 192) | fn fixed_invalid_char() {
function percent_invalid_char (line 200) | fn percent_invalid_char() {
function percent_empty (line 208) | fn percent_empty() {
FILE: src/tui/options.rs
type TuiLayout (line 6) | pub enum TuiLayout {
type PreviewLayout (line 18) | pub struct PreviewLayout {
method from (line 47) | fn from(value: &str) -> Self {
method default (line 34) | fn default() -> Self {
function test_preview_layout_direction_only (line 95) | fn test_preview_layout_direction_only() {
function test_preview_layout_with_size (line 113) | fn test_preview_layout_with_size() {
function test_preview_layout_with_offset (line 126) | fn test_preview_layout_with_offset() {
function test_preview_layout_with_size_and_offset (line 141) | fn test_preview_layout_with_size_and_offset() {
function test_preview_layout_with_hidden (line 154) | fn test_preview_layout_with_hidden() {
function test_preview_layout_complex (line 166) | fn test_preview_layout_complex() {
FILE: src/tui/preview.rs
type PreviewCallbackFn (line 28) | pub type PreviewCallbackFn = dyn Fn(Vec<Arc<dyn SkimItem>>) -> Vec<Strin...
constant PREVIEW_MAX_BYTES (line 29) | const PREVIEW_MAX_BYTES: usize = 1024 * 1024;
constant VT_SCROLLBACK (line 30) | const VT_SCROLLBACK: usize = 100_000;
type PreviewContent (line 33) | pub(crate) enum PreviewContent {
method default (line 41) | fn default() -> Self {
type PreviewCallback (line 48) | pub struct PreviewCallback {
method from (line 56) | fn from(func: F) -> Self {
type Target (line 62) | type Target = dyn Fn(Vec<Arc<dyn SkimItem>>) -> Vec<String> + Send + S...
method deref (line 64) | fn deref(&self) -> &Self::Target {
type Preview (line 69) | pub struct Preview {
method size_to_offset (line 113) | fn size_to_offset(&self, size: super::Size, is_vertical: bool) -> u16 {
method init_pty (line 124) | fn init_pty(&mut self) {
method filter_and_respond_to_queries (line 142) | fn filter_and_respond_to_queries(data: &[u8], writer: &mut Box<dyn std...
method content (line 180) | pub fn content(&mut self, content: &[u8]) -> Result<()> {
method content_with_position (line 192) | pub fn content_with_position(&mut self, content: &[u8], position: crat...
method scroll_up (line 205) | pub fn scroll_up(&mut self, lines: u16) {
method scroll_down (line 209) | pub fn scroll_down(&mut self, lines: u16) {
method scroll_left (line 225) | pub fn scroll_left(&mut self, cols: u16) {
method scroll_right (line 229) | pub fn scroll_right(&mut self, cols: u16) {
method set_offset (line 233) | pub fn set_offset(&mut self, offset: u16) {
method page_up (line 237) | pub fn page_up(&mut self) {
method page_down (line 242) | pub fn page_down(&mut self) {
method kill (line 247) | pub fn kill(&mut self) {
method spawn (line 273) | pub fn spawn<B: Backend>(&mut self, tui: &mut Tui<B>, cmd: &str) -> Re...
method default (line 90) | fn default() -> Self {
method drop (line 444) | fn drop(&mut self) {
method from_options (line 454) | fn from_options(options: &SkimOptions, theme: Arc<ColorTheme>) -> Self {
method render (line 480) | fn render(&mut self, area: ratatui::prelude::Rect, buf: &mut ratatui::pr...
FILE: src/tui/statusline.rs
type InfoDisplay (line 8) | pub enum InfoDisplay {
method value_variants (line 20) | fn value_variants<'a>() -> &'a [Self] {
method to_possible_value (line 25) | fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
FILE: src/tui/util.rs
function char_display_width (line 14) | pub fn char_display_width(c: char) -> usize {
function wrap_text (line 21) | pub fn wrap_text(input: Text, width: usize) -> Text {
function merge_styles (line 69) | pub(crate) fn merge_styles(left: Style, right: Style) -> Style {
function style_span (line 92) | pub(crate) fn style_span(span: &mut Span, style: Style) {
function style_line (line 95) | pub(crate) fn style_line(line: &mut Line, style: Style) {
function style_text (line 98) | pub(crate) fn style_text(text: &mut Text, style: Style) {
function find_osc_end (line 103) | pub(crate) fn find_osc_end(data: &[u8]) -> Option<usize> {
function find_csi_end (line 118) | pub(crate) fn find_csi_end(data: &[u8]) -> Option<usize> {
function handle_osc_query (line 129) | pub(crate) fn handle_osc_query(seq: &[u8], writer: &mut Box<dyn std::io:...
function handle_csi_query (line 167) | pub(crate) fn handle_csi_query(seq: &[u8], writer: &mut Box<dyn std::io:...
type RawMode (line 207) | struct RawMode;
method new (line 210) | fn new() -> io::Result<Self> {
method drop (line 217) | fn drop(&mut self) {
function cursor_pos_from_tty (line 222) | pub(crate) fn cursor_pos_from_tty() -> io::Result<(u16, u16)> {
function test_wrap_text_no_wrap_needed (line 284) | fn test_wrap_text_no_wrap_needed() {
function test_wrap_text_exact_width (line 293) | fn test_wrap_text_exact_width() {
function test_wrap_text_simple_wrap (line 302) | fn test_wrap_text_simple_wrap() {
function test_wrap_text_preserves_style (line 313) | fn test_wrap_text_preserves_style() {
function test_wrap_text_multiple_spans (line 330) | fn test_wrap_text_multiple_spans() {
function test_wrap_text_multiple_lines (line 352) | fn test_wrap_text_multiple_lines() {
function test_wrap_text_unicode_characters (line 363) | fn test_wrap_text_unicode_characters() {
function test_wrap_text_zero_width_characters (line 373) | fn test_wrap_text_zero_width_characters() {
function test_wrap_text_width_one (line 383) | fn test_wrap_text_width_one() {
function test_wrap_text_empty_input (line 396) | fn test_wrap_text_empty_input() {
function test_wrap_text_preserves_multiple_styles (line 406) | fn test_wrap_text_preserves_multiple_styles() {
function test_merge_styles (line 434) | fn test_merge_styles() {
function test_style_text (line 459) | fn test_style_text() {
FILE: src/tui/widget.rs
type SkimRender (line 11) | pub struct SkimRender {
method bitor_assign (line 19) | fn bitor_assign(&mut self, rhs: Self) {
type SkimWidget (line 26) | pub trait SkimWidget: Sized {
method from_options (line 28) | fn from_options(options: &SkimOptions, theme: Arc<ColorTheme>) -> Self;
method render (line 31) | fn render(&mut self, area: Rect, buf: &mut Buffer) -> SkimRender;
FILE: src/util.rs
function unescape_delimiter (line 31) | pub fn unescape_delimiter(s: &str) -> String {
function read_file_lines (line 77) | pub fn read_file_lines(filename: &str) -> std::result::Result<Vec<String...
function printf (line 92) | pub fn printf<'a>(
function make_item (line 255) | fn make_item(s: &'static str) -> MatchedItem {
function test_unescape_delimiter (line 265) | fn test_unescape_delimiter() {
function test_regex_null_byte_matching (line 281) | fn test_regex_null_byte_matching() {
function test_printf (line 298) | fn test_printf() {
function test_printf_plus (line 322) | fn test_printf_plus() {
function test_printf_norec (line 351) | fn test_printf_norec() {
function test_printf_replstr (line 367) | fn test_printf_replstr() {
FILE: tests/common/insta.rs
type TestHarness (line 26) | pub struct TestHarness {
method tick (line 44) | pub fn tick(&mut self) -> Result<()> {
method process_event (line 67) | fn process_event(&mut self, event: Event) -> Result<()> {
method send (line 93) | pub fn send(&mut self, event: Event) -> Result<()> {
method key (line 104) | pub fn key(&mut self, key: KeyEvent) -> Result<()> {
method char (line 112) | pub fn char(&mut self, c: char) -> Result<()> {
method type_str (line 117) | pub fn type_str(&mut self, s: &str) -> Result<()> {
method action (line 125) | pub fn action(&mut self, action: Action) -> Result<()> {
method wait_for_completion (line 140) | fn wait_for_completion(&mut self) -> Result<()> {
method buffer_view (line 161) | pub fn buffer_view(&self) -> String {
method prepare_snap (line 169) | pub fn prepare_snap(&mut self) -> Result<()> {
method snap (line 194) | pub fn snap(&mut self) -> Result<()> {
method wait_for_reader_and_matcher (line 211) | pub fn wait_for_reader_and_matcher(&mut self) -> Result<()> {
method wait_for_matcher (line 234) | pub fn wait_for_matcher(&mut self) -> Result<()> {
method handle_remaining_events (line 267) | pub fn handle_remaining_events(&mut self) -> Result<()> {
method heartbeat (line 317) | pub fn heartbeat(&mut self) -> Result<()> {
method app_exit_code (line 328) | pub fn app_exit_code(&self) -> Option<i32> {
function enter_sized_with_source (line 355) | fn enter_sized_with_source(
function enter_sized (line 385) | pub fn enter_sized(options: SkimOptions, width: u16, height: u16) -> Res...
function enter (line 390) | pub fn enter(options: SkimOptions) -> Result<TestHarness> {
function enter_default (line 395) | pub fn enter_default() -> Result<TestHarness> {
function enter_items (line 404) | pub fn enter_items<I, S>(items: I, options: SkimOptions) -> Result<TestH...
function enter_cmd (line 432) | pub fn enter_cmd(cmd: &str, options: SkimOptions) -> Result<TestHarness> {
function enter_interactive (line 454) | pub fn enter_interactive(options: SkimOptions) -> Result<TestHarness> {
function parse_options (line 462) | pub fn parse_options(args: &[&str]) -> SkimOptions {
FILE: tests/common/tmux.rs
function sk (line 18) | pub fn sk(outfile: &str, opts: &[&str]) -> String {
function wait (line 29) | pub fn wait<F, T>(pred: F) -> Result<T>
type Keys (line 42) | pub enum Keys<'a> {
method fmt (line 59) | fn fmt(&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt...
type TmuxController (line 79) | pub struct TmuxController {
method run (line 96) | pub fn run(args: &[&str]) -> Result<Vec<String>> {
method new_named (line 107) | pub fn new_named(name: &str) -> Result<Self> {
method new (line 142) | pub fn new() -> Result<Self> {
method send_keys (line 151) | pub fn send_keys(&self, keys: &[Keys]) -> std::io::Result<()> {
method tempfile (line 161) | pub fn tempfile(&self) -> Result<String> {
method capture (line 170) | pub fn capture(&self) -> Result<Vec<String>> {
method capture_colored (line 200) | pub fn capture_colored(&self) -> Result<Vec<String>> {
method until (line 229) | pub fn until<F>(&self, pred: F) -> std::io::Result<()>
method output (line 250) | pub fn output(&self) -> Result<Vec<String>> {
method output_from (line 262) | pub fn output_from(&self, outfile: &str) -> Result<Vec<String>> {
method start_sk (line 282) | pub fn start_sk(&mut self, stdin_cmd: Option<&str>, opts: &[&str]) -> ...
method default (line 86) | fn default() -> Self {
method drop (line 298) | fn drop(&mut self) {
FILE: tests/history.rs
function query_history (line 13) | fn query_history() -> Result<()> {
function cmd_history (line 54) | fn cmd_history() -> Result<()> {
FILE: tests/layout.rs
function args (line 5) | fn args<'a>(extra: &[&'a str]) -> Vec<&'a str> {
FILE: tests/listen.rs
function connect (line 16) | fn connect(name: &str) -> Result<Child> {
function send (line 23) | fn send(child: &mut Child, msg: &str) -> Result<()> {
function setup (line 30) | fn setup(name: &str, extra_args: &[&str]) -> Result<(TmuxController, Chi...
function listen_up (line 50) | fn listen_up() -> std::io::Result<()> {
function listen_down (line 62) | fn listen_down() -> std::io::Result<()> {
function listen_abort (line 76) | fn listen_abort() -> std::io::Result<()> {
function listen_accept (line 87) | fn listen_accept() -> std::io::Result<()> {
function listen_accept_key (line 99) | fn listen_accept_key() -> std::io::Result<()> {
function listen_add_char (line 110) | fn listen_add_char() -> std::io::Result<()> {
function listen_backward_char (line 120) | fn listen_backward_char() -> std::io::Result<()> {
function listen_backward_delete_char (line 133) | fn listen_backward_delete_char() -> std::io::Result<()> {
function listen_backward_delete_char_eof (line 145) | fn listen_backward_delete_char_eof() -> std::io::Result<()> {
function listen_backward_kill_word (line 157) | fn listen_backward_kill_word() -> std::io::Result<()> {
function listen_backward_word (line 169) | fn listen_backward_word() -> std::io::Result<()> {
function listen_end_of_line (line 182) | fn listen_end_of_line() -> std::io::Result<()> {
function listen_first (line 196) | fn listen_first() -> std::io::Result<()> {
function listen_forward_char (line 208) | fn listen_forward_char() -> std::io::Result<()> {
function listen_forward_word (line 222) | fn listen_forward_word() -> std::io::Result<()> {
function listen_kill_line (line 236) | fn listen_kill_line() -> std::io::Result<()> {
function listen_kill_word (line 249) | fn listen_kill_word() -> std::io::Result<()> {
function listen_last (line 262) | fn listen_last() -> std::io::Result<()> {
function listen_reload_cmd (line 275) | fn listen_reload_cmd() -> std::io::Result<()> {
function listen_select_all (line 286) | fn listen_select_all() -> std::io::Result<()> {
function listen_select_row (line 298) | fn listen_select_row() -> std::io::Result<()> {
function listen_select (line 310) | fn listen_select() -> std::io::Result<()> {
function listen_toggle (line 320) | fn listen_toggle() -> std::io::Result<()> {
function listen_toggle_all (line 332) | fn listen_toggle_all() -> std::io::Result<()> {
function listen_toggle_in (line 344) | fn listen_toggle_in() -> std::io::Result<()> {
function listen_toggle_out (line 358) | fn listen_toggle_out() -> std::io::Result<()> {
function listen_top (line 372) | fn listen_top() -> std::io::Result<()> {
function listen_unix_line_discard (line 384) | fn listen_unix_line_discard() -> std::io::Result<()> {
function listen_unix_word_rubout (line 396) | fn listen_unix_word_rubout() -> std::io::Result<()> {
function listen_yank (line 408) | fn listen_yank() -> std::io::Result<()> {
FILE: tests/matcher.rs
constant INPUT_ITEMS (line 5) | const INPUT_ITEMS: [&str; 49] = [
FILE: tests/options.rs
constant LONG_INPUT (line 380) | const LONG_INPUT: &str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...
function opt_select_1 (line 472) | fn opt_select_1() -> std::io::Result<()> {
function opt_exit_0 (line 485) | fn opt_exit_0() -> std::io::Result<()> {
FILE: tests/preview.rs
constant PREVIEW (line 5) | const PREVIEW: &str = "printf \"=%.0s\\n\" $(seq 1 1000)";
FILE: tests/tmux.rs
function setup_tmux_mock (line 14) | fn setup_tmux_mock(tmux: &TmuxController) -> Result<String> {
function get_tmux_cmd (line 38) | fn get_tmux_cmd(outfile: &str) -> Result<String> {
function tmux_vanilla (line 45) | fn tmux_vanilla() -> Result<()> {
function tmux_output_format (line 64) | fn tmux_output_format() -> Result<()> {
function tmux_stdin (line 93) | fn tmux_stdin() -> Result<()> {
function tmux_quote_bash (line 106) | fn tmux_quote_bash() -> Result<()> {
function tmux_quote_zsh (line 122) | fn tmux_quote_zsh() -> Result<()> {
function tmux_quote_sh (line 141) | fn tmux_quote_sh() -> Result<()> {
function tmux_quote_fish (line 157) | fn tmux_quote_fish() -> Result<()> {
FILE: tests/unix.rs
function default_command (line 17) | fn default_command() -> Result<()> {
function default_options (line 38) | fn default_options() -> Result<()> {
function options_file (line 50) | fn options_file() -> Result<()> {
function bind_reload_no_arg (line 290) | fn bind_reload_no_arg() -> Result<()> {
Condensed preview — 632 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,309K chars).
[
{
"path": ".config/insta.yaml",
"chars": 53,
"preview": "behavior:\n output: minimal\n\ntest:\n runner: nextest\n"
},
{
"path": ".config/nextest.toml",
"chars": 2445,
"preview": "experimental = [\"setup-scripts\", \"wrapper-scripts\"]\n\n[scripts.setup.stop-tmux]\ncommand = \"sh -c 'tmux kill-session -t sk"
},
{
"path": ".config/valgrind.supp",
"chars": 810,
"preview": "# Valgrind suppressions for skim tests\n# This file suppresses known false positives from Rust stdlib and system librarie"
},
{
"path": ".dockerignore",
"chars": 54,
"preview": "target/\n.github/\nbin/\nplugin/\nman/\nshell/\n*.md\ninstall"
},
{
"path": ".envrc",
"chars": 10,
"preview": "use flake\n"
},
{
"path": ".githooks/pre-commit",
"chars": 141,
"preview": "set -xeuo pipefail\n\ncargo fmt --check --all\ncargo clippy --all-targets --features test-utils -- -Dwarnings\ncargo check -"
},
{
"path": ".github/CONTRIBUTING.md",
"chars": 2083,
"preview": "# Contributor Guide\n\n## Running tests\n\nAll tests can be run by using [cargo-nextest](https://nexte.st/), which can be in"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 736,
"preview": "---\nname: Bug report\nabout: Report a bug encountered using `skim`\ntitle: \"[BUG] xxx\"\nlabels: bug\nassignees: LoricAndre\n\n"
},
{
"path": ".github/dependabot.yml",
"chars": 197,
"preview": "version: 2\nupdates:\n - package-ecosystem: \"cargo\"\n directory: \"/\"\n schedule:\n interval: \"weekly\"\n commit-"
},
{
"path": ".github/pr-title-checker-config.json",
"chars": 595,
"preview": "{\n \"LABEL\": {\n \"name\": \"invalid-title\",\n \"color\": \"B60205\"\n },\n \"CHECKS\": {\n \"prefixes\": [\n \"feat: \",\n "
},
{
"path": ".github/pull_request_template.md",
"chars": 531,
"preview": "## Checklist\n\n- [ ] The title of my PR follows [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/)\n- "
},
{
"path": ".github/workflows/pr.yml",
"chars": 1732,
"preview": "name: \"Pull Requests\"\non:\n pull_request_target:\n types:\n - opened\n - edited\n - synchronize\n - la"
},
{
"path": ".github/workflows/publish.yml",
"chars": 519,
"preview": "name: Publish cargo crate\n\non:\n workflow_call:\n inputs:\n plan:\n required: true\n type: string\n\njob"
},
{
"path": ".github/workflows/release.yml",
"chars": 13028,
"preview": "# This file was autogenerated by dist: https://axodotdev.github.io/cargo-dist\n#\n# Copyright 2022-2024, axodotdev\n# SPDX-"
},
{
"path": ".github/workflows/test.yml",
"chars": 3488,
"preview": "name: Build & Test\n\non:\n workflow_dispatch:\n workflow_call:\n inputs:\n plan:\n required: false\n ty"
},
{
"path": ".gitignore",
"chars": 314,
"preview": "# Compiled files\n*.o\n*.so\n*.rlib\n*.dll\n\n# Executables\n*.exe\n\n# Generated by Cargo\n/target/\n\n/bin/sk\n.idea/\n.ropeproject/"
},
{
"path": ".rustfmt.toml",
"chars": 15,
"preview": "max_width = 120"
},
{
"path": "AGENTS.md",
"chars": 2209,
"preview": "# Skim Agent Guidelines\n\n## Build/Test/Lint Commands\n- Build: `cargo build [--release]`\n- Run: `cargo run [--release]`\n-"
},
{
"path": "CHANGELOG.md",
"chars": 37695,
"preview": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Change"
},
{
"path": "Cargo.toml",
"chars": 3055,
"preview": "[package]\nname = \"skim\"\nversion = \"4.0.0\"\nauthors = [\"Loric ANDRE\", \"Zhang Jinzhou <lotabout@gmail.com>\"]\ndescription = "
},
{
"path": "LICENSE",
"chars": 1080,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2016 Jinzhou Zhang\n\nPermission is hereby granted, free of charge, to any person obt"
},
{
"path": "README.md",
"chars": 28804,
"preview": "<p align=\"center\">\n <a href=\"https://crates.io/crates/skim\">\n <img src=\"https://img.shields.io/crates/v/skim.svg\" al"
},
{
"path": "bench.py",
"chars": 25695,
"preview": "#!/usr/bin/env python3\n\"\"\"\nBenchmark script to measure ingestion + matching rate in skim interactive mode.\nThis measures"
},
{
"path": "benches/filter.rs",
"chars": 9083,
"preview": "use std::fs;\n\nuse criterion::{Criterion, criterion_group, criterion_main};\n\nuse skim::Typos;\nuse skim::helper::item::Def"
},
{
"path": "benches/gungraun.rs",
"chars": 1719,
"preview": "use gungraun::{library_benchmark, library_benchmark_group, main};\nuse std::fs;\nuse std::hint::black_box;\n\nuse skim::Case"
},
{
"path": "benches/matcher_micro.rs",
"chars": 4045,
"preview": "//! Microbenchmark that isolates the fuzzy matcher DP from all other overhead\n//! (I/O, threading, sorting).\n\nuse std::f"
},
{
"path": "benches/partial.rs",
"chars": 1995,
"preview": "use std::io::{BufWriter, Stderr};\n\nuse clap::Parser as _;\nuse criterion::{Criterion, criterion_group, criterion_main};\n\n"
},
{
"path": "benches/read_and_match.rs",
"chars": 4423,
"preview": "use color_eyre::eyre::{Ok, Result};\nuse criterion::{Criterion, criterion_group, criterion_main};\n\nuse skim::prelude::*;\n"
},
{
"path": "bin/sk-tmux",
"chars": 7433,
"preview": "#!/usr/bin/env bash\n\n# The MIT License (MIT)\n#\n# Copyright (c) 2019 Jinzhou Zhang\n# Copyright (c) 2016 Junegunn Choi\n#\n#"
},
{
"path": "cliff.toml",
"chars": 4983,
"preview": "# git-cliff ~ configuration file\n# https://git-cliff.org/docs/configuration\n\n\n[changelog]\n# A Tera template to be render"
},
{
"path": "codecov.yml",
"chars": 361,
"preview": "# Make Codecov statuses informational so coverage is visible but won't fail PRs\ncoverage:\n status:\n default_rules:\n "
},
{
"path": "dist-workspace.toml",
"chars": 726,
"preview": "[workspace]\nmembers = [\"cargo:.\"]\n\n# Config for 'dist'\n[dist]\n# The preferred dist version to use in CI (Cargo.toml SemV"
},
{
"path": "examples/ansi.rs",
"chars": 821,
"preview": "extern crate skim;\nuse skim::{prelude::*, reader::CommandCollector};\n\npub fn main() {\n env_logger::init();\n\n let g"
},
{
"path": "examples/async.rs",
"chars": 193,
"preview": "use skim::{Skim, prelude::SkimOptionsBuilder};\n\n#[tokio::main]\nasync fn main() {\n let options = SkimOptionsBuilder::d"
},
{
"path": "examples/base.rs",
"chars": 331,
"preview": "use skim::prelude::*;\n\nfn main() -> color_eyre::Result<()> {\n let opts = SkimOptionsBuilder::default().multi(true).re"
},
{
"path": "examples/cmd_collector.rs",
"chars": 1371,
"preview": "extern crate skim;\nuse reader::CommandCollector;\nuse skim::prelude::*;\n\nstruct BasicSkimItem {\n value: String,\n}\n\nimp"
},
{
"path": "examples/custom_action_keybinding.rs",
"chars": 3153,
"preview": "extern crate skim;\nuse crossterm::event::{KeyCode, KeyEvent, KeyModifiers};\nuse skim::prelude::*;\nuse skim::tui::event::"
},
{
"path": "examples/custom_item.rs",
"chars": 1444,
"preview": "extern crate skim;\nuse skim::prelude::*;\n\nstruct MyItem {\n inner: String,\n}\n\nimpl SkimItem for MyItem {\n fn text(&"
},
{
"path": "examples/custom_keybinding_actions.rs",
"chars": 1258,
"preview": "extern crate skim;\nuse crossterm::event::{KeyCode, KeyModifiers};\nuse skim::prelude::*;\n\n// No action is actually perfor"
},
{
"path": "examples/downcast.rs",
"chars": 1270,
"preview": "extern crate skim;\nuse skim::prelude::*;\n\n/// This example illustrates downcasting custom structs that implement\n/// `Sk"
},
{
"path": "examples/fine-grain.rs",
"chars": 940,
"preview": "extern crate skim;\nuse color_eyre::Result;\nuse skim::prelude::*;\n\n#[tokio::main(flavor = \"current_thread\")]\npub async fn"
},
{
"path": "examples/fuzzy_matcher_fz.rs",
"chars": 1998,
"preview": "use skim::fuzzy_matcher::FuzzyMatcher;\nuse skim::fuzzy_matcher::clangd::ClangdMatcher;\nuse skim::fuzzy_matcher::skim::Sk"
},
{
"path": "examples/multiple_runs.rs",
"chars": 795,
"preview": "use skim::{Skim, prelude::SkimOptionsBuilder};\n\n// Hint: use `ps -T -p $(pgrep -f target/debug/examples/multiple_runs)` "
},
{
"path": "examples/nth.rs",
"chars": 697,
"preview": "extern crate skim;\nuse skim::prelude::*;\nuse std::io::Cursor;\n\n/// `nth` option is supported by SkimItemReader.\n/// In t"
},
{
"path": "examples/option_builder.rs",
"chars": 1098,
"preview": "extern crate skim;\nuse skim::prelude::*;\nuse std::io::Cursor;\n\npub fn main() {\n let item_reader = SkimItemReader::def"
},
{
"path": "examples/preview_callback.rs",
"chars": 727,
"preview": "use std::io::Cursor;\n\nuse skim::prelude::*;\n\npub fn main() {\n env_logger::init();\n let options = SkimOptionsBuilde"
},
{
"path": "examples/receiver_multi.rs",
"chars": 488,
"preview": "use std::sync::Arc;\n\nuse skim::prelude::*;\n\nfn main() {\n let (sender, receiver): (SkimItemSender, SkimItemReceiver) ="
},
{
"path": "examples/sample.rs",
"chars": 313,
"preview": "extern crate skim;\nuse skim::prelude::*;\n\npub fn main() {\n let options = SkimOptions::default();\n\n let selected_it"
},
{
"path": "examples/selector.rs",
"chars": 723,
"preview": "extern crate skim;\nuse skim::prelude::*;\n\nstruct BasicSelector {\n pub pat: String,\n}\n\nimpl Selector for BasicSelector"
},
{
"path": "examples/tick.rs",
"chars": 624,
"preview": "use skim::prelude::*;\n\n#[tokio::main]\npub async fn main() -> color_eyre::eyre::Result<()> {\n let opts = SkimOptionsBu"
},
{
"path": "flake.nix",
"chars": 1132,
"preview": "{\n description = \"Nix flake for skim development\";\n\n inputs.nixpkgs.url = \"https://channels.nixos.org/nixpkgs-unstable"
},
{
"path": "justfile",
"chars": 1191,
"preview": "bump-version version:\n sed -i 's/^version = \".*\"/version = \"{{ version }}\"/' ./Cargo.toml\n\ngenerate-files:\n SKIM_D"
},
{
"path": "man/man1/sk-tmux.1",
"chars": 1828,
"preview": ".ig\nThe MIT License (MIT)\n\nCopyright (c) 2019 Jinzhou Zhang\nCopyright (c) 2017 Junegunn Choi\n\nPermission is hereby grant"
},
{
"path": "man/man1/sk.1",
"chars": 31240,
"preview": ".ie \\n(.g .ds Aq \\(aq\n.el .ds Aq '\n.TH sk 1 \"sk 4.0.0\" \n.ie \\n(.g .ds Aq \\(aq\n.el .ds Aq '\n.SH NAME\nsk \\- Fuzzy Finder "
},
{
"path": "plugin/skim.vim",
"chars": 29011,
"preview": "\" Copyright (c) 2017 Junegunn Choi\n\"\n\" MIT License\n\"\n\" Permission is hereby granted, free of charge, to any person obtai"
},
{
"path": "rust-toolchain.toml",
"chars": 82,
"preview": "[toolchain]\nchannel = \"stable\"\nprofile = \"default\"\ncomponents = [\"rust-analyzer\"]\n"
},
{
"path": "shell/LICENSE",
"chars": 1113,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2016 Jinzhou Zhang\nCopyright (c) 2016 Junegunn Choi\n\nPermission is hereby granted, "
},
{
"path": "shell/README.md",
"chars": 350,
"preview": "This directory contains the shell bindings for skim. \n\nNote that the content in this directory is copied from\n[fzf](http"
},
{
"path": "shell/completion.bash",
"chars": 16151,
"preview": "_sk() {\n local i cur prev opts cmd\n COMPREPLY=()\n if [[ \"${BASH_VERSINFO[0]}\" -ge 4 ]]; then\n cur=\"$2\"\n "
},
{
"path": "shell/completion.fish",
"chars": 9835,
"preview": "complete -c sk -l min-query-length -d 'Minimum query length to start showing results' -r\ncomplete -c sk -s t -l tiebreak"
},
{
"path": "shell/completion.nu",
"chars": 8756,
"preview": "module completions {\n\n def \"nu-complete sk tiebreak\" [] {\n [ \"score\" \"-score\" \"begin\" \"-begin\" \"end\" \"-end\" \"length\""
},
{
"path": "shell/completion.zsh",
"chars": 10598,
"preview": "#compdef sk\n\nautoload -U is-at-least\n\n_sk() {\n typeset -A opt_args\n typeset -a _arguments_options\n local ret=1\n"
},
{
"path": "shell/key-bindings.bash",
"chars": 12611,
"preview": "# skim key bindings for bash\n#\n# - $SKIM_TMUX_OPTS\n# - $SKIM_CTRL_T_COMMAND\n# - $SKIM_CTRL_T_OPTS\n# - $SKIM_CTRL_R_OPTS\n"
},
{
"path": "shell/key-bindings.fish",
"chars": 5134,
"preview": "#!/bin/fish\n# skim key bindings for fish\n#\n# - $SKIM_TMUX_OPTS\n# - $SKIM_CTRL_T_COMMAND\n# - $SKIM_CTRL_T_OPTS\n# - $SKIM_"
},
{
"path": "shell/key-bindings.zsh",
"chars": 12464,
"preview": "# skim key bindings for zsh\n#\n# - $SKIM_TMUX_OPTS\n# - $SKIM_CTRL_T_COMMAND\n# - $SKIM_CTRL_T_OPTS\n# - $SKIM_CTRL_R_OPTS\n#"
},
{
"path": "shell/version.txt",
"chars": 6,
"preview": "4.0.0\n"
},
{
"path": "sonar-project.properties",
"chars": 371,
"preview": "sonar.projectKey=skim-rs_skim\nsonar.organization=skim\n\n\n# This is the name and version displayed in the SonarCloud UI.\n#"
},
{
"path": "src/bin/main.rs",
"chars": 9297,
"preview": "//! Command-line interface for skim fuzzy finder.\n//!\n//! This binary provides the `sk` command-line tool for fuzzy find"
},
{
"path": "src/binds.rs",
"chars": 14613,
"preview": "//! Key binding configuration and parsing.\n//!\n//! This module provides utilities for parsing and managing keyboard shor"
},
{
"path": "src/engine/all.rs",
"chars": 1091,
"preview": "use std::fmt::{Display, Error, Formatter};\nuse std::sync::Arc;\n\nuse crate::item::RankBuilder;\nuse crate::{MatchEngine, M"
},
{
"path": "src/engine/andor.rs",
"chars": 3420,
"preview": "use std::fmt::{Display, Error, Formatter};\n\nuse crate::fuzzy_matcher::MatchIndices;\nuse crate::{MatchEngine, MatchRange,"
},
{
"path": "src/engine/exact.rs",
"chars": 3364,
"preview": "use crate::engine::util::{contains_upper, regex_match};\nuse crate::item::RankBuilder;\nuse crate::{CaseMatching, MatchEng"
},
{
"path": "src/engine/factory.rs",
"chars": 10023,
"preview": "use regex::Regex;\n\nuse crate::engine::all::MatchAllEngine;\nuse crate::engine::andor::{AndEngine, OrEngine};\nuse crate::e"
},
{
"path": "src/engine/fuzzy.rs",
"chars": 9379,
"preview": "use std::cmp::min;\nuse std::fmt::{Display, Error, Formatter};\nuse std::sync::Arc;\n\nuse crate::fuzzy_matcher::MatchIndice"
},
{
"path": "src/engine/mod.rs",
"chars": 136,
"preview": "pub mod all;\npub mod andor;\npub mod exact;\npub mod factory;\npub mod fuzzy;\npub mod normalized;\npub mod regexp;\npub mod s"
},
{
"path": "src/engine/normalized.rs",
"chars": 3393,
"preview": "//! Normalized match engine for matching with Unicode normalization (removing diacritics).\n//!\n//! This engine wraps ano"
},
{
"path": "src/engine/regexp.rs",
"chars": 2527,
"preview": "use std::fmt::{Display, Error, Formatter};\nuse std::sync::Arc;\n\nuse regex::Regex;\n\nuse crate::engine::util::regex_match;"
},
{
"path": "src/engine/split.rs",
"chars": 5651,
"preview": "//! Split match engine for matching against different parts of items based on a delimiter.\n//!\n//! This engine splits bo"
},
{
"path": "src/engine/util.rs",
"chars": 3966,
"preview": "use crate::fuzzy_matcher::MatchIndices;\nuse regex::Regex;\nuse unicode_normalization::UnicodeNormalization;\n\n/// Normaliz"
},
{
"path": "src/field.rs",
"chars": 17761,
"preview": "//! Field extraction and parsing utilities.\n//!\n//! This module provides utilities for parsing field ranges and extracti"
},
{
"path": "src/fuzzy_matcher/arinae/algo.rs",
"chars": 22580,
"preview": "//! Arinae's algo itself\n\nuse std::cell::RefCell;\n\nuse thread_local::ThreadLocal;\n\nuse crate::fuzzy_matcher::{IndexType,"
},
{
"path": "src/fuzzy_matcher/arinae/atom.rs",
"chars": 3143,
"preview": "//! Byte/Char helpers\nuse super::Score;\nuse super::constants::SEPARATOR_TABLE;\nuse memchr::memchr;\n\npub(super) trait Ato"
},
{
"path": "src/fuzzy_matcher/arinae/banding.rs",
"chars": 3475,
"preview": "//! Banding utils\n//! Banding is the process of calculating the pertinent parts of the matrix to our specific\n//! comput"
},
{
"path": "src/fuzzy_matcher/arinae/constants.rs",
"chars": 2424,
"preview": "// ---------------------------------------------------------------------------\n// Scoring constants\n// -----------------"
},
{
"path": "src/fuzzy_matcher/arinae/helpers.rs",
"chars": 3371,
"preview": "//! &[dyn Atom] manipulation helpers\n\nuse super::Atom;\nuse super::constants::MAX_PAT_LEN;\n\n/// Find the 1-indexed column"
},
{
"path": "src/fuzzy_matcher/arinae/matrix.rs",
"chars": 2956,
"preview": "//! Base structs for the matching algorithm: Cell & `SWMatrix`\n\nuse super::Score;\n\n/// Direction the optimal path took t"
},
{
"path": "src/fuzzy_matcher/arinae/mod.rs",
"chars": 10796,
"preview": "//! Arinae fuzzy matching algorithm.\n//!\n//! Uses a Smith-Waterman local alignment approach with affine gap penalties\n//"
},
{
"path": "src/fuzzy_matcher/arinae/prefilter.rs",
"chars": 5219,
"preview": "//! Prefilters running before the algo to optimize performance on unmatchable items\n\nuse super::Atom;\nuse super::constan"
},
{
"path": "src/fuzzy_matcher/arinae/tests.rs",
"chars": 12511,
"preview": "use super::*;\nuse crate::fuzzy_matcher::FuzzyMatcher;\n\nfn matcher() -> ArinaeMatcher {\n ArinaeMatcher::default()\n}\n\nf"
},
{
"path": "src/fuzzy_matcher/clangd.rs",
"chars": 16379,
"preview": "//! The fuzzy matching algorithm used in clangd.\n//! https://github.com/llvm-mirror/clang-tools-extra/blob/master/clangd"
},
{
"path": "src/fuzzy_matcher/frizbee.rs",
"chars": 3086,
"preview": "//! Matcher using <https://crates.io/crates/frizbee>\nuse frizbee::{Scoring, smith_waterman::SmithWatermanMatcher};\n\nuse "
},
{
"path": "src/fuzzy_matcher/fzy.rs",
"chars": 42270,
"preview": "//! Fuzzy matching algorithm based on fzy by John Hawthorn.\n//! https://github.com/jhawthorn/fzy\n//!\n//! This implements"
},
{
"path": "src/fuzzy_matcher/mod.rs",
"chars": 1903,
"preview": "//! Fuzzy matching algorithms and implementations.\n//!\n//! This module provides different fuzzy matching algorithms incl"
},
{
"path": "src/fuzzy_matcher/skim.rs",
"chars": 34085,
"preview": "//! The fuzzy matching algorithm used by skim\n//!\n//! # Example:\n//! ```\n//! use skim::fuzzy_matcher::FuzzyMatcher;\n//! "
},
{
"path": "src/fuzzy_matcher/util.rs",
"chars": 4251,
"preview": "use super::{FuzzyMatcher, IndexType, ScoreType};\n\npub fn cheap_matches(choice: &[char], pattern: &[char], case_sensitive"
},
{
"path": "src/helper/item.rs",
"chars": 36053,
"preview": "//! Skim item helpers\n//! Including the `DefaultSkimItem`\nuse crate::field::{FieldRange, parse_matching_fields, parse_tr"
},
{
"path": "src/helper/item_reader.rs",
"chars": 14045,
"preview": "//! Helper utilities for converting input sources into skim item streams.\n\nuse std::error::Error;\nuse std::io::{BufRead,"
},
{
"path": "src/helper/macros.rs",
"chars": 3063,
"preview": "/// Macro for exhaustive matching that ensures all enum variants are covered\n///\n/// This macro wraps a match expression"
},
{
"path": "src/helper/mod.rs",
"chars": 120,
"preview": "//! Skim helpers\npub mod item;\npub(crate) mod item_reader;\n#[macro_use]\npub(crate) mod macros;\npub(crate) mod selector;\n"
},
{
"path": "src/helper/selector.rs",
"chars": 3313,
"preview": "use std::collections::HashSet;\n\nuse regex::Regex;\n\nuse crate::{Selector, SkimItem};\n\n/// Default implementation of the s"
},
{
"path": "src/item.rs",
"chars": 18493,
"preview": "//! Item representation and management.\n//!\n//! This module provides the core item types used by skim, including ranked "
},
{
"path": "src/lib.rs",
"chars": 12510,
"preview": "//! Skim is a fuzzy finder library for Rust.\n//!\n//! It provides a fast and customizable way to filter and select items "
},
{
"path": "src/manpage.rs",
"chars": 11170,
"preview": "//! Provides what's needed to generate skim's man page\nuse std::io::Write;\n\nuse clap::CommandFactory;\nuse clap_mangen::M"
},
{
"path": "src/matcher.rs",
"chars": 12370,
"preview": "//! This module contains the matching coordinator\nuse rayon::ThreadPool;\nuse std::rc::Rc;\nuse std::sync::Arc;\nuse std::s"
},
{
"path": "src/options.rs",
"chars": 49649,
"preview": "//! Configuration options for skim.\n//!\n//! This module provides the `SkimOptions` struct and builder for configuring\n//"
},
{
"path": "src/output.rs",
"chars": 906,
"preview": "use crate::item::MatchedItem;\nuse crate::tui::Event;\n\n/// Output from running skim, containing the final selection and s"
},
{
"path": "src/prelude.rs",
"chars": 892,
"preview": "//! Convenience re-exports of commonly used types.\n//!\n//! This module provides a convenient way to import all the commo"
},
{
"path": "src/reader.rs",
"chars": 6857,
"preview": "//! Reader is used for reading items from datasource (e.g. stdin or command output)\n//!\n//! After reading in a line, rea"
},
{
"path": "src/shell.rs",
"chars": 1721,
"preview": "//! Provides helpers to easily generate shell completions\nuse clap::CommandFactory;\n\nuse crate::SkimOptions;\n\n/// Availa"
},
{
"path": "src/skim.rs",
"chars": 22334,
"preview": "//! Module containing skim's entry point\nuse std::env;\nuse std::io::{BufWriter, Stderr};\nuse std::sync::Arc;\nuse std::ti"
},
{
"path": "src/skim_item.rs",
"chars": 3281,
"preview": "use ratatui::text::Line;\nuse std::{\n borrow::Cow,\n fmt::{Debug, Display},\n};\n\nuse crate::{AsAny, DisplayContext, I"
},
{
"path": "src/spinlock.rs",
"chars": 3742,
"preview": "//! `SpinLock` implemented using `AtomicBool`\n//! Just like Mutex except:\n//!\n//! 1. It uses CAS for locking, more effic"
},
{
"path": "src/theme.rs",
"chars": 27156,
"preview": "//! Handle the color theme\nuse ratatui::style::{Color, Modifier, Style};\n\nuse crate::options::SkimOptions;\n\n/// The colo"
},
{
"path": "src/tmux.rs",
"chars": 15039,
"preview": "//! Tmux integration utilities.\n//!\n//! This module provides functionality for running skim within tmux panes,\n//! allow"
},
{
"path": "src/tui/app.rs",
"chars": 54918,
"preview": "use std::process::{Command, Stdio};\nuse std::rc::Rc;\nuse std::sync::Arc;\nuse std::sync::atomic::{AtomicBool, Ordering};\n"
},
{
"path": "src/tui/backend.rs",
"chars": 9902,
"preview": "use std::io::BufWriter;\nuse std::ops::{Deref, DerefMut};\nuse std::sync::Once;\n\nuse color_eyre::eyre::Result;\nuse crosste"
},
{
"path": "src/tui/event.rs",
"chars": 15710,
"preview": "use std::future::Future;\nuse std::pin::Pin;\nuse std::sync::{Arc, Mutex};\n\nuse crate::exhaustive_match;\nuse crossterm::ev"
},
{
"path": "src/tui/header.rs",
"chars": 6566,
"preview": "//! Header display widget for skim's TUI.\n//!\n//! This module provides the header widget that displays static text above"
},
{
"path": "src/tui/input.rs",
"chars": 20180,
"preview": "use std::fmt::Write as _;\nuse std::ops::{Deref, DerefMut};\nuse std::time::Instant;\n\nuse ansi_to_tui::IntoText;\nuse ratat"
},
{
"path": "src/tui/item_list.rs",
"chars": 31030,
"preview": "use std::{rc::Rc, sync::Arc};\n\nuse indexmap::IndexSet;\nuse ratatui::text::{Line, Span};\nuse ratatui::widgets::{Block, Bo"
},
{
"path": "src/tui/layout.rs",
"chars": 29882,
"preview": "//! Layout computation for skim's TUI.\n//!\n//! The layout logic is split into two phases so that the expensive option-\n/"
},
{
"path": "src/tui/mod.rs",
"chars": 6593,
"preview": "//! Terminal UI components and rendering.\n//!\n//! This module provides the terminal user interface components for skim,\n"
},
{
"path": "src/tui/options.rs",
"chars": 5500,
"preview": "use crate::tui::{Direction, Size};\n\n/// Layout configuration for the TUI\n#[derive(Default, Debug, Clone, Copy, PartialEq"
},
{
"path": "src/tui/preview.rs",
"chars": 21988,
"preview": "use ansi_to_tui::IntoText;\nuse color_eyre::eyre::{Result, eyre};\nuse portable_pty::{PtyPair, PtySize, native_pty_system}"
},
{
"path": "src/tui/statusline.rs",
"chars": 931,
"preview": "#[cfg(feature = \"cli\")]\nuse clap::ValueEnum;\n#[cfg(feature = \"cli\")]\nuse clap::builder::PossibleValue;\n\n/// Display mode"
},
{
"path": "src/tui/util.rs",
"chars": 16024,
"preview": "use crossterm::terminal;\nuse ratatui::{\n style::Style,\n text::{Line, Span, Text},\n};\nuse std::fs::OpenOptions;\nuse"
},
{
"path": "src/tui/widget.rs",
"chars": 916,
"preview": "use ratatui::buffer::Buffer;\nuse ratatui::layout::Rect;\nuse std::ops::BitOrAssign;\nuse std::sync::Arc;\n\nuse crate::optio"
},
{
"path": "src/util.rs",
"chars": 14360,
"preview": "use crate::field::FieldRange;\nuse crate::field::get_string_by_field;\nuse crate::helper::item::strip_ansi;\nuse crate::ite"
},
{
"path": "test.dockerfile",
"chars": 238,
"preview": "FROM rust:1-slim\n\nRUN apt-get update && apt-get install -y tmux bsdmainutils && apt-get clean\nCOPY rust-toolchain.toml ."
},
{
"path": "tests/ansi.rs",
"chars": 1901,
"preview": "#[allow(dead_code)]\n#[macro_use]\nmod common;\n\nuse common::tmux::Keys::*;\n\nsk_test!(test_ansi_flag_enabled, @cmd \"echo -e"
},
{
"path": "tests/binds.rs",
"chars": 3042,
"preview": "#[allow(dead_code)]\n#[macro_use]\nmod common;\n\n// Test if-non-matched action: deletes character when no match\ninsta_test!"
},
{
"path": "tests/case.rs",
"chars": 1854,
"preview": "#[allow(dead_code)]\n#[macro_use]\nmod common;\n\n// Smart case: lowercase query matches case-insensitively\ninsta_test!(case"
},
{
"path": "tests/common/insta.rs",
"chars": 26237,
"preview": "use std::io::Cursor;\n\nuse clap::Parser;\nuse color_eyre::Result;\nuse crossterm::event::{KeyCode, KeyEvent, KeyModifiers};"
},
{
"path": "tests/common/mod.rs",
"chars": 704,
"preview": "#[macro_use]\npub mod insta;\n#[macro_use]\npub mod tmux;\n\n#[cfg(all(debug_assertions, coverage))]\npub static SK: &str =\n "
},
{
"path": "tests/common/tmux.rs",
"chars": 24781,
"preview": "use std::{\n fmt::{Display, Formatter},\n fs::File,\n io::{BufReader, ErrorKind, Read, Result},\n path::Path,\n "
},
{
"path": "tests/defaults.rs",
"chars": 1182,
"preview": "#[allow(dead_code)]\n#[macro_use]\nmod common;\n\ninsta_test!(vanilla_basic, [\"1\", \"2\", \"3\"], &[]);\n\n// Using 100 items to r"
},
{
"path": "tests/highlighting.rs",
"chars": 1665,
"preview": "#[allow(dead_code)]\n#[macro_use]\nmod common;\n\nuse common::tmux::Keys::*;\n\nsk_test!(highlight_match, @cmd \"echo -e 'apple"
},
{
"path": "tests/history.rs",
"chars": 2286,
"preview": "#[allow(dead_code)]\nmod common;\n\nuse common::tmux::Keys::*;\nuse common::tmux::TmuxController;\nuse std::fs::File;\nuse std"
},
{
"path": "tests/issues.rs",
"chars": 575,
"preview": "#[allow(dead_code)]\n#[macro_use]\nmod common;\n\ninsta_test!(issue_359_multi_regex_unicode, [\"ああa\"], &[\"--regex\", \"-q\", \"a\""
},
{
"path": "tests/keys.rs",
"chars": 4069,
"preview": "#[allow(dead_code)]\n#[macro_use]\nmod common;\n\n// Using 100 items to test filtering and navigation (representative of lar"
},
{
"path": "tests/keys_interactive.rs",
"chars": 4263,
"preview": "#[allow(dead_code)]\n#[macro_use]\nmod common;\n\n// Basic interactive mode test\ninsta_test!(keys_interactive_basic, [\"1\", \""
},
{
"path": "tests/layout.rs",
"chars": 1320,
"preview": "#[allow(dead_code)]\n#[macro_use]\nmod common;\n\nfn args<'a>(extra: &[&'a str]) -> Vec<&'a str> {\n let base_args = &[\n "
},
{
"path": "tests/listen.rs",
"chars": 11251,
"preview": "#[allow(dead_code)]\n#[macro_use]\nmod common;\n\nuse common::tmux::Keys::*;\nuse rand::{RngExt as _, distr::Alphabetic};\nuse"
},
{
"path": "tests/matcher.rs",
"chars": 2253,
"preview": "#[allow(dead_code)]\n#[macro_use]\nmod common;\n\nconst INPUT_ITEMS: [&str; 49] = [\n \"src/util.rs\",\n \"src/completions."
},
{
"path": "tests/normalize.rs",
"chars": 2497,
"preview": "#[allow(dead_code)]\n#[macro_use]\nmod common;\n\n// Test normalize: accented item matches unaccented query\ninsta_test!(inst"
},
{
"path": "tests/options.rs",
"chars": 12414,
"preview": "use crate::common::SK;\nuse std::process::Command;\n\n#[allow(dead_code)]\n#[macro_use]\nmod common;\n\ninsta_test!(opt_with_nt"
},
{
"path": "tests/preview.rs",
"chars": 4151,
"preview": "#[allow(dead_code)]\n#[macro_use]\nmod common;\n\nconst PREVIEW: &str = \"printf \\\"=%.0s\\\\n\\\" $(seq 1 1000)\";\n\ninsta_test!(pr"
},
{
"path": "tests/snapshots/ansi__prompt_ansi.snap",
"chars": 2070,
"preview": "---\nsource: tests/ansi.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_append_and_select-2.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_append_and_select-3.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_append_and_select.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_change-2.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_change-3.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_change.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_first_last-2.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_first_last-3.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_first_last-4.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_first_last.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_if_non_matched-2.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_if_non_matched-3.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_if_non_matched.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_set_header_change-2.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_set_header_change.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_set_header_from_empty-2.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_set_header_from_empty.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_set_header_to_empty-2.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_set_header_to_empty.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_set_preview_cmd-2.snap",
"chars": 2090,
"preview": "---\nsource: tests/binds.rs\nassertion_line: 101\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_set_preview_cmd-3.snap",
"chars": 2090,
"preview": "---\nsource: tests/binds.rs\nassertion_line: 101\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_set_preview_cmd.snap",
"chars": 2090,
"preview": "---\nsource: tests/binds.rs\nassertion_line: 101\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_set_query_basic-2.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_set_query_basic.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_set_query_expand-2.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_set_query_expand.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_set_query_fields-2.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_set_query_fields.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_set_query_to_itself-2.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_set_query_to_itself-3.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_set_query_to_itself-4.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_set_query_to_itself.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_toggle_interactive-2.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_toggle_interactive.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_toggle_interactive_queries-2.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_toggle_interactive_queries-3.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_toggle_interactive_queries-4.snap",
"chars": 2071,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_toggle_interactive_queries-5.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_toggle_interactive_queries.snap",
"chars": 2071,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_top_alias-2.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_top_alias-3.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/binds__bind_top_alias.snap",
"chars": 2070,
"preview": "---\nsource: tests/binds.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/case__case_ignore_different-2.snap",
"chars": 2069,
"preview": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/case__case_ignore_different.snap",
"chars": 2069,
"preview": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/case__case_ignore_exact-2.snap",
"chars": 2069,
"preview": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/case__case_ignore_exact.snap",
"chars": 2069,
"preview": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/case__case_ignore_lower-2.snap",
"chars": 2069,
"preview": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/case__case_ignore_lower.snap",
"chars": 2069,
"preview": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/case__case_ignore_no_match-2.snap",
"chars": 2069,
"preview": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/case__case_ignore_no_match.snap",
"chars": 2069,
"preview": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/case__case_non_ascii-2.snap",
"chars": 2069,
"preview": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\" "
},
{
"path": "tests/snapshots/case__case_non_ascii-3.snap",
"chars": 2069,
"preview": "---\nsource: tests/case.rs\nexpression: buf + & cursor_pos\n---\n\" "
}
]
// ... and 432 more files (download for full content)
About this extraction
This page contains the full source code of the skim-rs/skim GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 632 files (2.1 MB), approximately 577.0k tokens, and a symbol index with 1110 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.