Skip to content

Commit

Permalink
Handle grep output
Browse files Browse the repository at this point in the history
- Handle standard filepath:code and filepath:line_number:code output
  as produced by `git grep`, `rg -H`, `grep -H`, etc (with -n for line
  numbers).

- Retain the match highlighting as produced by the grep tool, and
  expose it in delta's color output styled with grep-match-style.
  (Note that --color=always is needed to retain the color if piping
  into delta, but not for `git grep` when delta is configured as git's
  pager)

- Special handling of -p, and -W options of `git grep`: these display
  the function context in which the matches occur.

- `navigate` keybindings jump between match function contexts under
  `git grep -p` and between matching lines under `git grep -W`.

Thanks @zachriggle for the proposal.
Fixes #769
  • Loading branch information
dandavison committed Nov 22, 2021
1 parent ce6258d commit 07892bc
Show file tree
Hide file tree
Showing 15 changed files with 1,342 additions and 82 deletions.
9 changes: 7 additions & 2 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Expand Up @@ -32,6 +32,8 @@ lazy_static = "1.4"
palette = "0.6.0"
pathdiff = "0.2.1"
regex = "1.4.6"
serde = { version = "1.0.118", features = ["derive"] }
serde_json = "1.0.70"
shell-words = "1.0.0"
smol_str = "0.1.18"
structopt = "0.3.25"
Expand Down
12 changes: 12 additions & 0 deletions README.md
Expand Up @@ -23,6 +23,7 @@
show = delta
log = delta
blame = delta
grep = delta
reflog = delta

[interactive]
Expand Down Expand Up @@ -64,6 +65,7 @@ Code evolves, and we all spend time studying diffs. Delta aims to make this both
- [Choosing colors (styles)](#choosing-colors-styles)
- [Line numbers](#line-numbers)
- [Side-by-side view](#side-by-side-view)
- [Grep](#grep)
- ["Features": named groups of settings](#features-named-groups-of-settings)
- [Custom themes](#custom-themes)
- [diff-highlight and diff-so-fancy emulation](#diff-highlight-and-diff-so-fancy-emulation)
Expand Down Expand Up @@ -151,6 +153,7 @@ Here's what `git show` can look like with git configured to use delta:
- Git style strings (foreground color, background color, font attributes) are supported for >20 stylable elements
- Side-by-side view with line-wrapping
- Line numbering
- Handles grep output with file paths from `rg`, `git grep`, `grep`, etc
- `diff-highlight` and `diff-so-fancy` emulation modes
- Stylable box/line decorations to draw attention to commit, file and hunk header sections.
- Support for Git's `--color-moved` feature.
Expand Down Expand Up @@ -410,6 +413,15 @@ In contrast, the long replacement line in the right panel overflows by almost an
For control over the details of line wrapping, see `--wrap-max-lines`, `--wrap-left-symbol`, `--wrap-right-symbol`, `--wrap-right-percent`, `--wrap-right-prefix-symbol`, `--inline-hint-style`.
Line wrapping was implemented by @th1000s.

### Grep

Delta applies syntax-highlighting and other enhancements to standard grep output such as from `git grep`, [ripgrep](https://github.com/BurntSushi/ripgrep/) (aka `rg`), grep, etc.
To use with `git grep`, set delta as the pager for `grep` in the `[pager]` section of your gitconfig. See the example at the [top of the page](#get-started).
Output from other grep tools can be piped to delta: e.g. `rg -Hn --color=always`, `grep -Hn --color=always`, etc.
To customize the colors and syntax highlighting, see `grep-match-line-style`, `grep-match-word-style`, `grep-contexct-line-style`, `grep-file-style`, `grep-line-number-style`.
Ripgrep's `rg --json` output format is supported; this avoids certain file name parsing ambiguities that are inevitable with the standard grep output formats.
Note that `git grep` can display the "function context" for matches and that delta handles this output specially: see the `-p` and `-W` options of `git grep`.

### "Features": named groups of settings

All delta options can go under the `[delta]` section in your git config file. However, you can also use named "features" to keep things organized: these are sections in git config like `[delta "my-feature"]`. Here's an example using two custom features:
Expand Down
34 changes: 34 additions & 0 deletions src/cli.rs
Expand Up @@ -445,6 +445,40 @@ pub struct Opt {
)]
pub blame_timestamp_format: String,

#[structopt(long = "grep-match-line-style")]
/// Style (foreground, background, attributes) for matching lines of code in
/// grep output. See STYLES section. Defaults to plus-style.
pub grep_match_line_style: Option<String>,

#[structopt(long = "grep-match-word-style")]
/// Style (foreground, background, attributes) for the specific matching
/// substrings within a matching line of code in grep output. See STYLES
/// section. Defaults to plus-style.
pub grep_match_word_style: Option<String>,

#[structopt(long = "grep-context-line-style")]
/// Style (foreground, background, attributes) for non-matching lines of
/// code in grep output. See STYLES section. Defaults to zero-style.
pub grep_context_line_style: Option<String>,

#[structopt(long = "grep-file-style")]
/// Style (foreground, background, attributes) for file paths in grep
/// output. See STYLES section. Defaults to hunk-header-file-path-style.
pub grep_file_style: Option<String>,

#[structopt(long = "grep-line-number-style")]
/// Style (foreground, background, attributes) for line numbers in grep
/// output. See STYLES section. Defaults to hunk-header-line-number-style.
pub grep_line_number_style: Option<String>,

#[structopt(long = "grep-separator-symbol", default_value = ":")]
/// Symbol used in grep output to separate file path (and line number) from
/// the line of file contents. Defaults to ":" for both match and context
/// lines, since many terminal emulators recognize constructs like
/// "/path/to/file:7:". However, standard grep output uses "-" for context
/// lines: set this option to "keep" to keep the original separator symbols.
pub grep_separator_symbol: String,

/// Default language used for syntax highlighting when this cannot be
/// inferred from a filename. It will typically make sense to set this in
/// per-repository git config (.git/config)
Expand Down
12 changes: 12 additions & 0 deletions src/config.rs
Expand Up @@ -80,6 +80,12 @@ pub struct Config {
pub git_config: Option<GitConfig>,
pub git_minus_style: Style,
pub git_plus_style: Style,
pub grep_context_line_style: Style,
pub grep_file_style: Style,
pub grep_line_number_style: Style,
pub grep_match_line_style: Style,
pub grep_match_word_style: Style,
pub grep_separator_symbol: String,
pub hunk_header_file_style: Style,
pub hunk_header_line_number_style: Style,
pub hunk_header_style_include_file_path: bool,
Expand Down Expand Up @@ -243,6 +249,12 @@ impl From<cli::Opt> for Config {
file_style: styles["file-style"],
git_config: opt.git_config,
git_config_entries: opt.git_config_entries,
grep_context_line_style: styles["grep-context-line-style"],
grep_file_style: styles["grep-file-style"],
grep_line_number_style: styles["grep-line-number-style"],
grep_match_line_style: styles["grep-match-line-style"],
grep_match_word_style: styles["grep-match-word-style"],
grep_separator_symbol: opt.grep_separator_symbol,
hunk_header_file_style: styles["hunk-header-file-style"],
hunk_header_line_number_style: styles["hunk-header-line-number-style"],
hunk_header_style: styles["hunk-header-style"],
Expand Down
10 changes: 9 additions & 1 deletion src/delta.rs
Expand Up @@ -23,6 +23,7 @@ pub enum State {
SubmoduleLog, // In a submodule section, with gitconfig diff.submodule = log
SubmoduleShort(String), // In a submodule section, with gitconfig diff.submodule = short
Blame(String, Option<String>), // In a line of `git blame` output (commit, repeat_blame_line).
Grep, // In a line of `git grep` output
Unknown,
// The following elements are created when a line is wrapped to display it:
HunkZeroWrapped, // Wrapped unchanged line
Expand Down Expand Up @@ -121,6 +122,7 @@ impl<'a> StateMachine<'a> {
|| self.handle_submodule_short_line()?
|| self.handle_hunk_line()?
|| self.handle_blame_line()?
|| self.handle_grep_line()?
|| self.should_skip_line()
|| self.emit_line_unchanged()?;
}
Expand All @@ -133,7 +135,13 @@ impl<'a> StateMachine<'a> {
fn ingest_line(&mut self, raw_line_bytes: &[u8]) {
// TODO: retain raw_line as Cow
self.raw_line = String::from_utf8_lossy(raw_line_bytes).to_string();
if self.config.max_line_length > 0 && self.raw_line.len() > self.config.max_line_length {
if self.config.max_line_length > 0
&& self.raw_line.len() > self.config.max_line_length
// We must not truncate ripgrep --json output
// TODO: An alternative might be to truncate `line` but retain
// `raw_line` untruncated?
&& !self.raw_line.starts_with('{')
{
self.raw_line = ansi::truncate_str(
&self.raw_line,
self.config.max_line_length,
Expand Down
2 changes: 1 addition & 1 deletion src/handlers/file_meta.rs
Expand Up @@ -210,7 +210,7 @@ fn get_file_extension_from_file_meta_line_file_path(path: &str) -> Option<&str>
}

/// Attempt to parse input as a file path and return extension as a &str.
fn get_extension(s: &str) -> Option<&str> {
pub fn get_extension(s: &str) -> Option<&str> {
let path = Path::new(s);
path.extension()
.and_then(|e| e.to_str())
Expand Down

0 comments on commit 07892bc

Please sign in to comment.