Skip to content

Conversation

@domenicomastrangelo
Copy link

@domenicomastrangelo domenicomastrangelo commented Jun 1, 2024

Updating print_line function in SimplePrinter to use line numbers when printing a line if line numbers are desired (self.config.style_components.numbers() / StyleComponent::LineNumbers).

This was a compatibility issue with cat until now as describe in issue #2935.

Updating of some integration tests was necessary as now we expect the output to contain line numbers.

.assert()
.success()
.stdout("dummy content\n");
.stdout(" 1 dummy content\n");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious, is this really the expected behavior? The test invokes bat without any line number style component arguments. I think it should stay as it was before - according to the issue being solved, line numbers should only be displayed when piping when the command line argument was explicitly passed, no?

Copy link
Author

@domenicomastrangelo domenicomastrangelo Jun 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch!

I added a check for handle type here

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this doesn't pass the test now, it seems like there may be an issue with the filename and the is_terminal() function or something like that

fn is_terminal(&self) -> bool

───────────────────────────────────────────
Returns true if the descriptor/handle refers to a terminal/tty.

On platforms where Rust does not know how to detect a terminal yet, this will return
false. This will also return false if an unexpected error occurred, such as from
passing an invalid file descriptor.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

debugging it shows self.config.style_components.numbers() returns true, is there a different way to know if line numbers are requested that i'm missing?

Copy link
Contributor

@einfachIrgendwer0815 einfachIrgendwer0815 Jun 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My guess would be the following:

The SimplePrinter is used whenever stdout is not interactive, --color is not set to always, --decorations is not set to always and --force-colorization is not set either (see app.rs, config.rs and controller.rs). --style controls which style components (such as header or line numbers) are used. The default for --style is changes,grid,header-filename,numbers,snip. This default currently stays the same even if loop_through == true (which means that SimplePrinter will be used). Currently, SimplePrinter just does not care about --style.

So, when loop_through == true, the default for --style should be plain instead. Then your changes should work since they do when you specify --style=plain explicitly.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the tip @einfachIrgendwer0815!

I updated the code and pushed the changes, now all the tests pass correctly and I removed all the integration test changes which were not necessary.

I also added a test for this specific case :)

@einfachIrgendwer0815
Copy link
Contributor

I think I've found a simpler approach. The following should do the trick:

diff --git a/src/bin/bat/app.rs b/src/bin/bat/app.rs
index 8f69870f..cf385c88 100644
--- a/src/bin/bat/app.rs
+++ b/src/bin/bat/app.rs
@@ -251,9 +251,10 @@ impl App {
             loop_through: !(self.interactive_output
                 || self.matches.get_one::<String>("color").map(|s| s.as_str()) == Some("always")
                 || self
                     .matches
                     .get_one::<String>("decorations")
                     .map(|s| s.as_str())
                     == Some("always")
-                || self.matches.get_flag("force-colorization")),
+                || self.matches.get_flag("force-colorization")
+                || self.matches.get_flag("number")),
             tab_width: self

That way, whenever -n/--number is specified the default InteractivePrinter is used instead of the SimplePrinter.

@Enselic
Copy link
Collaborator

Enselic commented Aug 16, 2025

I certainly prefer things to be simple, thanks for the insight. So this PR is not needed then? I'll close it but let me know if I misunderstood something.

@lmmx
Copy link
Contributor

lmmx commented Oct 16, 2025

I just came across this via #3316 and repeated the same idea in #3438 🙃

The issue reported in #2935 which remains in #3316 is that -n line numbers do not pipe.

It looks like a proposal was given above to continue this work (by editing or shipping as a fresh PR):

I think I've found a simpler approach. The following should do the trick:

diff --git a/src/bin/bat/app.rs b/src/bin/bat/app.rs
index 8f69870f..cf385c88 100644
--- a/src/bin/bat/app.rs
+++ b/src/bin/bat/app.rs
@@ -251,9 +251,10 @@ impl App {
             loop_through: !(self.interactive_output
                 || self.matches.get_one::<String>("color").map(|s| s.as_str()) == Some("always")
                 || self
                     .matches
                     .get_one::<String>("decorations")
                     .map(|s| s.as_str())
                     == Some("always")
-                || self.matches.get_flag("force-colorization")),
+                || self.matches.get_flag("force-colorization")
+                || self.matches.get_flag("number")),
             tab_width: self

That way, whenever -n/--number is specified the default InteractivePrinter is used instead of the SimplePrinter.

The logic for this now lives in the controller.rs module not app.rs but the same general idea as given above could be achieved by doing this at the branch point here where the SimplePrinter is gated:

bat/src/controller.rs

Lines 179 to 189 in a730eaa

let mut printer: Box<dyn Printer> = if self.config.loop_through {
Box::new(SimplePrinter::new(self.config))
} else {
Box::new(InteractivePrinter::new(
self.config,
self.assets,
&mut opened_input,
#[cfg(feature = "git")]
&line_changes,
)?)
};

I can put that in a fresh PR (#3438), it sounds like this is a viable approach

That is the same idea I took away from reading through, emphasis added:

My initial impression is that if this were to change it may surprise people if regular piping suddenly started decorating style.

But in my case I want it to, at least when I actively pass in a style like -n. Not sure if it'd be possible to distinguish someone passing in the default style vs. it being defaulted to.

Maybe we need:

  • a 3rd printer, neither Simple nor Interactive, but de-coloured?
  • an extra config flag, to record if the user explicitly set a flag?
    • As an initial approximation we could simply set this based on the presence of non-default/non-empty style components
      • ...in which case it doesn't need to be a new struct field at all
  • ❇️ something else... ❇️

Originally posted by @lmmx in #3316

matches.get_flag indeed gives such a way to distinguish active style setting!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants