sharkdp / bat

A cat(1) clone with wings.
Apache License 2.0
49.58k stars 1.25k forks source link

Show enclosing context for highlight #2228

Open zachriggle opened 2 years ago

zachriggle commented 2 years ago

Bat is great! Hopefully my sponsorship helps pay for 🍺 money!

Background

When using bat in many of my workflows, I make use of the --highlight-line flag to highlight specific lines.

Currently, I rely on git to display --function-context to get the bounds of the enclosing scope. This works well, but relies on very fragile and error-prone regular expressions.

For example, try git grep -b -W memcpy or git log -b -p -W memcpy inside a random C\C++ project. Neat output, but no syntax highlighting!

I have some plumbing that parses Git's output and passes it to e.g. bat -H 123 -r 100:150 flags on the command line -- but this is hacky, error-prone, displays too much information due to Git's implementation. (from matching line, search backward for a function/class signature, then search forward for the next one -- and display all lines). All of this plumbing is written in Zsh and it's a hack that I don't feel comfortable sharing.

Feature

It would be nice if bat had similar functionality to show the enclosing scope (ObjC block, ObjC selector, function, method, class, namespace) based on where the highlighted line(s) are.

Since bat pardoes syntax highlighting, I expect there is some way to get the enclosing scope in a much more reliable fashion.

This would also remove the need to pass -r in bat -H 123 -r 100:150 -- with this feature you might instead pass bat -W 123 or bat --function-context 123.

Implementation Suggestions

Since scopes may be nested (namespace → class / implementation → selector → block → block → block → conditional → loop) it may not be easy to discern how many scopes to go outward. I suggest having the containing function / method / selector implementation as the default. If the highlighted line is an e.g. struct or interface / declaration / definition / implementation, the entire struct or class definition should be shown.

If the highlighted line is a global or otherwise only contained by a namespace (global or named), only that line should be shown -- though perhaps there should be an option to toggle this (to show the whole file for globals, or the whole namespace if enclosed).

For additional scopes that are not displayed (e.g. if we are showing a line inside a method, but the method is declared within a class definition, which is itself inside a named scope) it might be useful to show the lines which constitute the namespace / class / struct name.

The way that git handles --function-context with global variables is just to show everything before the line until a function signature is found, and everything after until a function signature is found. This results in way too much being displayed, and in many cases the entire file is shown.

keith-hall commented 2 years ago

Just to check, are you aware of https://github.com/dandavison/delta, which is similar to bat but has much deeper git diff integration ? :) or is the idea that given a line number, you want to see the enclosing scope context, and git is just an example, such that bat still feels like a better fit?

zachriggle commented 2 years ago

delta doesn't actually have integration for this -- it's still parsing a standard git diff and all the magic is in git.

I'd like to see the feature in bat ❤️

keith-hall commented 2 years ago

Understood, thanks for the clarification. If/when we decide to do this, it will require a fair amount of work - currently, bat doesn't know anything about the text it colors - it simply passes the line of text, a "context" (i.e. "this line is inside a block comment started previously") and a color scheme to a highlighter function and gets a series of string slices with color details. To implement this, bat would need to receive and keep track of tokens and their "scopes" and remember positions of indexed symbols etc to know which is the closest prior function / class definition etc. Edit to add: this would then match how Sublime Merge does it.

maciejmatczak commented 1 year ago

--line-range was brought up in initial message, I would like add observation here. I am missing a context input for bat that relates to highlight or line range inputs, like -C in grep.

With current implementation of --line-range, it's like: we have lines before, we have lines after... but no way to select before/after simultaneously without additional calculations.

cohml commented 9 months ago

no way to select before/after simultaneously without additional calculations.

That doesn't seem like too much added headache over what bat already suppports:

bat -H 123 --line-range $(( 123 - 10 )):$(( 123 + 10 ))

Or save the "anchor line" and optionally a context window as variables and then just reference them:

function bat_highlight_context() {
    local file="${1:?}"
    local line="${2:?}"
    local context_size="${3:-10}"
    local line_range="$(( $line - $context_size )):$(( $line + $context_size ))"
    bat $file -H $line --line-range $line_range
}

bat_highlight_context foo.sh 123