treegrep is a regex pattern matcher that displays results in a tree structure with an interface to jump to matched text.
examples, editor integrations, and help.
crates.io | GitHub | AUR | NetBSD
- cargo:
cargo install treegrep - releases: Download from releases
- manual:
git clone https://github.com/4imothy/treegrep cd treegrep cargo build --release
neovim
- sample installation using lazy.nvim
return {
'4imothy/treegrep',
build = function()
require('treegrep').build_tgrep()
end,
config = function()
require('treegrep').setup({
selection_file = '/tmp/tgrep-select',
repeat_file = '/tmp/tgrep-repeat',
})
vim.keymap.set('n', '<leader>tt', function() require('treegrep').tgrep_with('--menu') end)
vim.keymap.set('n', '<leader>tr', function() require('treegrep').tgrep_with('--repeat --select') end)
vim.keymap.set('n', '<leader>tm', function() require('treegrep').tgrep_with('--menu --repeat') end)
vim.keymap.set('n', '<leader>tf', function() require('treegrep').tgrep_with('--files --select') end)
end,
}helix
- sample keybind to run treegrep and open selection
C-t = [
':sh rm -f /tmp/tgrep-select',
':insert-output tgrep --menu --selection-file=/tmp/tgrep-select --repeat-file=/tmp/tgrep-repeat > /dev/tty',
':open %sh{ f=$(sed -n 1p /tmp/tgrep-select); l=$(sed -n 2p /tmp/tgrep-select); [ -n "$l" ] && echo "$f:$l" || echo "$f"; }',
':redraw',
':set mouse false',
':set mouse true',
]vim
- sample installation using vim-plug
Plug '4imothy/treegrep', {'do': {-> TgrepBuild()}}
let g:tgrep_selection_file = '/tmp/tgrep-select'
let g:tgrep_repeat_file = '/tmp/tgrep-repeat'
nnoremap <leader>tt :call TgrepWith('--menu')<cr>
nnoremap <leader>tr :call TgrepWith('--repeat --select')<cr>
nnoremap <leader>tm :call TgrepWith('--menu --repeat')<cr>
nnoremap <leader>tf :call TgrepWith('--files --select')<cr>tgrep --regexp \bstruct\s+\w+ --regexp \bimpl\s+\w+ --path src --line-number --context=1 --count
src: 10
├──term.rs: 1
│ ├──-1:
│ ├──15: pub struct Term<'a> {
│ ╰──+1: pub height: u16,
├──matcher.rs: 3
│ ├──-1:
│ ├──20: struct Matcher {
│ ├──+1: combined: RegexMatcher,
│ ├──-1:
│ ├──25: impl Matcher {
│ ├──+1: fn new(patterns: &[String]) -> Result<Self, Message> {
│ ├──-1:
│ ├──44: struct MatchSink<'a> {
│ ╰──+1: lines: Vec<Line>,
├──args_menu.rs: 1
│ ├──-1:
│ ├──21: pub struct ArgsMenu<'a, 'b> {
│ ╰──+1: term: &'a mut term::Term<'b>,
├──errors.rs: 4
│ ├──-1:
│ ├──14: pub struct Message {
│ ├──+1: pub mes: String,
│ ├──-1: }
│ ├──17: impl Error for Message {}
│ ├──+1:
│ ├──-1:
│ ├──34: impl fmt::Debug for Message {
│ ├──+1: fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
│ ├──-1:
│ ├──40: impl fmt::Display for Message {
│ ╰──+1: fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
├──style.rs: 1
│ ├──-1:
│ ├──31: pub struct DisplayRepeater<T>(T, usize);
│ ╰──+1: impl<T: Display> Display for DisplayRepeater<T> {
├──match_system.rs: 8
│ ├──-1:
│ ├──23: pub struct Directory {
│ ├──+1: pub path: PathBuf,
│ ├──-1:
│ ├──30: impl Directory {
│ ├──+1: pub fn new(path: &Path) -> Result<Self, Message> {
│ ├──-1:
│ ├──41: pub struct File {
│ ├──+1: pub path: PathBuf,
│ ├──-1:
│ ├──47: impl File {
│ ├──+1: pub fn from_pathbuf(path: PathBuf) -> Result<Self, Message> {
│ ├──-1: #[cfg_attr(test, derive(PartialEq, Debug))]
│ ├──72: pub struct Match {
│ ├──+1: pub regexp_id: usize,
│ ├──-1:
│ ├──78: impl Match {
│ ├──+1: pub fn new(regexp_id: usize, start: usize, end: usize) -> Self {
│ ├──-1:
│ ├──103: pub struct Line {
│ ├──+1: pub content: String,
│ ├──-1:
│ ├──110: impl Line {
│ ╰──+1: pub fn new(content: String, mut matches: Vec<Match>, line_num: usize) -> Self {
├──select_menu.rs: 5
│ ├──-1:
│ ├──81: impl OpenStrategy {
│ ├──+1: fn from(editor: &str) -> Self {
│ ├──-1:
│ ├──93: pub struct SelectMenu<'a, 'b> {
│ ├──+1: jump: JumpLocation,
│ ├──-1:
│ ├──110: struct Window {
│ ├──+1: first: isize,
│ ├──-1:
│ ├──115: impl Window {
│ ├──+1: pub fn shift_up(&mut self) {
│ ├──-1:
│ ├──136: impl JumpLocation {
│ ╰──+1: fn default() -> Self {
├──config.rs: 6
│ ├──-1:
│ ├──15: pub struct KeyBindings {
│ ├──+1: pub down: Vec<KeyCode>,
│ ├──-1:
│ ├──35: pub struct Characters {
│ ├──+1: pub bl: char,
│ ├──-1:
│ ├──51: pub struct Colors {
│ ├──+1: pub file: Color,
│ ├──-1:
│ ├──62: impl args::Color {
│ ├──+1: fn get(&self) -> Color {
│ ├──-1:
│ ├──80: pub struct Config {
│ ├──+1: pub path: PathBuf,
│ ├──-1:
│ ├──186: impl Config {
│ ╰──+1: pub fn get_styling(matches: &ArgMatches) -> (bool, bool) {
├──args.rs: 7
│ ├──-1:
│ ├──19: pub struct ArgInfo {
│ ├──+1: pub id: &'static str,
│ ├──-1:
│ ├──25: impl ArgInfo {
│ ├──+1: const fn new(id: &'static str, h: &'static str, s: Option<char>) -> Self {
│ ├──-1:
│ ├──40: impl ValueEnum for OpenStrategy {
│ ├──+1: fn value_variants<'a>() -> &'a [Self] {
│ ├──-1: #[derive(Clone)]
│ ├──85: struct ColorParser;
│ ├──+1:
│ ├──87: impl clap::builder::TypedValueParser for ColorParser {
│ ├──+1: type Value = Color;
│ ├──-1: #[derive(Clone)]
│ ├──173: struct KeyCodeParser;
│ ├──+1:
│ ├──175: impl clap::builder::TypedValueParser for KeyCodeParser {
│ ╰──+1: type Value = KeyCode;
╰──writer.rs: 9
├──-1:
├──26: pub struct OpenInfo<'a> {
├──+1: pub path: &'a Path,
├──-1:
├──37: struct PathDisplay<'a> {
├──+1: prefix: Option<Vec<PrefixComponent>>,
├──-1:
├──150: struct LineDisplay<'a> {
├──+1: prefix: Vec<PrefixComponent>,
├──-1:
├──279: struct LongBranchDisplay<'a> {
├──+1: prefix: Vec<PrefixComponent>,
├──-1:
├──320: struct OverviewDisplay {
├──+1: dirs: usize,
├──-1:
├──328: impl Entry for OverviewDisplay {
├──+1: fn open_info(&self) -> Result<OpenInfo<'_>, Message> {
├──-1:
├──340: impl Display for OverviewDisplay {
├──+1: fn fmt(&self, f: &mut std::fmt::Formatter) -> fmt::Result {
├──-1:
├──388: impl Directory {
├──+1: fn to_lines<'a>(
├──-1:
├──473: impl File {
╰──+1: fn to_lines<'a>(
tgrep Print src/select_menu.rs --trim --line-number --char-vertical=| --char-horizontal=- --char-top-left=+ --char-top-right=+ --char-bottom-left=+ --char-bottom-right=+ --char-tee=+ --char-ellipsis=|
select_menu.rs
+--15: style::{Print, SetBackgroundColor},
+--214: queue!(self.term, Print(&self.lines[orig]))?;
+--216: queue!(self.term, Print(config().chars.ellipsis))?;
+--711: Print(style::style_with(config().chars.selected_indicator.as_str(), c)),
+--715: queue!(self.term, Print(config().chars.selected_indicator.as_str()),)?;
+--725: Print(config().chars.selected_indicator_clear.as_str()),
+--741: Print(format!(
+--753: Print(format!(
+--766: Print(format!(
tgrep --files --hidden --glob=!.git
treegrep
├──src
│ ├──match_system.rs
│ ├──args.rs
│ ├──writer.rs
│ ├──main.rs
│ ├──errors.rs
│ ├──select_menu.rs
│ ├──style.rs
│ ├──log.rs
│ ├──config.rs
│ ├──matcher.rs
│ ├──term.rs
│ ╰──args_menu.rs
├──doc
│ ├──treegrep.vim9.txt
│ ╰──treegrep.nvim.txt
├──.github
│ ╰──workflows
│ ├──update_readme
│ ├──test.yml
│ ├──update_readme.yml
│ ╰──cr.yml
├──benchmarks
│ ├──runner
│ ╰──times
├──lua
│ ╰──treegrep.lua
├──plugin
│ ╰──treegrep.vim
├──tests
│ ├──pool
│ │ ╰──alice_adventures_in_wonderland_by_lewis_carroll.txt
│ ├──targets
│ │ ├──files_1
│ │ ├──wide_2
│ │ ├──links_4
│ │ ├──links_3
│ │ ├──links_2
│ │ ├──files_long_branch_expr_2
│ │ ├──glob_exclusion
│ │ ├──no_matches
│ │ ├──files_long_branch_1
│ │ ├──context_b1
│ │ ├──context_a1
│ │ ├──files_long_branch_expr_count_2
│ │ ├──overview_dir
│ │ ├──wide_1
│ │ ├──files_2
│ │ ├──line_number
│ │ ├──deep
│ │ ├──context_c1
│ │ ├──links_1
│ │ ├──count
│ │ ├──colon
│ │ ├──overview_file
│ │ ├──files_long_branch_expr_1
│ │ ├──overlapping
│ │ ├──file
│ │ ├──max_depth
│ │ ├──files_long_branch_2
│ │ ├──glob_inclusion
│ │ ├──files_long_branch_expr_count_1
│ │ ╰──files_with_expr
│ ├──utils.rs
│ ├──tests.rs
│ ╰──file_system.rs
├──.gitignore
├──README.md
├──Cargo.lock
├──rustfmt.toml
├──Cargo.toml
╰──LICENSE
tgrep --files --long-branch --hidden --glob=!.git
treegrep
├──src
│ ├──match_system.rs, args.rs, writer.rs, main.rs, errors.rs
│ ├──select_menu.rs, style.rs, log.rs, config.rs, matcher.rs
│ ╰──term.rs, args_menu.rs
├──doc
│ ╰──treegrep.vim9.txt, treegrep.nvim.txt
├──.github
│ ╰──workflows
│ ╰──update_readme, test.yml, update_readme.yml, cr.yml
├──benchmarks
│ ╰──runner, times
├──lua
│ ╰──treegrep.lua
├──plugin
│ ╰──treegrep.vim
├──tests
│ ├──pool
│ │ ╰──alice_adventures_in_wonderland_by_lewis_carroll.txt
│ ├──targets
│ │ ├──max_depth, files_long_branch_2, glob_inclusion, files_long_branch_expr_count_1, overview_file
│ │ ├──files_with_expr, files_long_branch_expr_1, overlapping, file, links_1
│ │ ├──colon, count, context_c1, deep, line_number
│ │ ├──files_2, wide_1, overview_dir, files_long_branch_expr_count_2, files_long_branch_1
│ │ ├──context_b1, context_a1, glob_exclusion, no_matches, files_long_branch_expr_2
│ │ ╰──files_1, wide_2, links_4, links_3, links_2
│ ╰──utils.rs, tests.rs, file_system.rs
├──.gitignore, README.md, Cargo.lock, LICENSE, rustfmt.toml
╰──Cargo.toml
treegrep 1.3.0
by Timothy Cronin
home page: https://github.com/4imothy/treegrep
regex pattern matcher that displays results in a tree structure with an interface to jump to matched text
tgrep [OPTIONS] [positional regexp] [positional target]
arguments:
[positional regexp]
a regex expression to search for
[positional target]
the path to search, if not provided, search the current directory
options:
-e, --regexp <>
a regex expression to search for
-p, --path <>
the path to search, if not provided, search the current directory
-s, --select
results are shown in a selection interface for opening
-f, --files
if an expression is given, hide matched content, otherwise, show the files that would be searched
-., --hidden
search hidden files
-n, --line-number
show line number of match
-c, --count
display number of files matched in directory and number of lines matched in a file
-g, --glob <>
rules match .gitignore globs, but ! has inverted meaning, overrides other ignore logic
-l, --links
search linked paths
-o, --overview
conclude results with an overview
-d, --max-depth <>
the max depth to search
-C, --context <>
number of lines to show before and after each match
-B, --before-context <>
number of lines to show before each match
-A, --after-context <>
number of lines to show after each match
--max-length <>
set the max length for a matched line
--menu
provide arguments and select results through an interface
--no-ignore
don't use ignore files
--trim
trim whitespace at the beginning of lines
--threads <>
set the appropriate number of threads to use
--long-branch
multiple files from the same directory are shown on the same branch
--editor <>
command used to open selections
--open-like <>
command line syntax for opening a file at a line
[possible values: vi, hx, code, jed, default]
--completions <>
generate completions for given shell
[possible values: bash, elvish, fish, powershell, zsh]
--selection-file <>
file to write selection to (first line: file path, second line: line number if applicable)
--repeat-file <>
file where arguments are saved
--repeat
repeats the last saved search
--no-color
don't use colors
--no-bold
don't bold anything
--file-color <>
black, white, red, green, yellow, blue, magenta, cyan, grey, rgb(_._._), ansi(_)
--dir-color <>
black, white, red, green, yellow, blue, magenta, cyan, grey, rgb(_._._), ansi(_)
--text-color <>
black, white, red, green, yellow, blue, magenta, cyan, grey, rgb(_._._), ansi(_)
--line-number-color <>
black, white, red, green, yellow, blue, magenta, cyan, grey, rgb(_._._), ansi(_)
--branch-color <>
black, white, red, green, yellow, blue, magenta, cyan, grey, rgb(_._._), ansi(_)
--match-colors <>
black, white, red, green, yellow, blue, magenta, cyan, grey, rgb(_._._), ansi(_)
--selected-indicator-color <>
black, white, red, green, yellow, blue, magenta, cyan, grey, rgb(_._._), ansi(_)
--selected-bg-color <>
black, white, red, green, yellow, blue, magenta, cyan, grey, rgb(_._._), ansi(_)
--search-highlight-color <>
black, white, red, green, yellow, blue, magenta, cyan, grey, rgb(_._._), ansi(_)
--prefix-len <>
number of characters to show before a match
[default: 3]
--long-branch-each <>
number of files to print on each branch
[default: 5]
--char-vertical <>
vertical branch character
--char-horizontal <>
horizontal branch character
--char-top-left <>
top-left corner character
--char-top-right <>
top-right corner character
--char-bottom-left <>
bottom-left corner character
--char-bottom-right <>
bottom-right corner character
--char-tee <>
tee branch character
--char-ellipsis <>
folding indicator character
--selected-indicator <>
selected indicator characters
[default: "─❱ "]
--key-down <>
move down
[default: down j n]
--key-up <>
move up
[default: up k p]
--key-big-down <>
big jump down
[default: J N]
--key-big-up <>
big jump up
[default: K P]
--key-down-path <>
next path
[default: } ]]
--key-up-path <>
previous path
[default: { []
--key-down-same-depth <>
next path at same depth
[default: ) d]
--key-up-same-depth <>
previous path at same depth
[default: ( u]
--key-top <>
go to top
[default: home g <]
--key-bottom <>
go to bottom
[default: end G >]
--key-page-down <>
page down
[default: pagedown f]
--key-page-up <>
page up
[default: pageup b]
--key-center <>
center cursor
[default: z l]
--key-help <>
show help
[default: h]
--key-quit <>
quit
[default: q]
--key-open <>
open selection
[default: enter]
--key-fold <>
fold/unfold path
[default: tab]
--key-search <>
search within results
[default: / s]
-h, --help
print help
-V, --version
print version
arguments are prefixed with the contents of the TREEGREP_DEFAULT_OPTS environment variable