diff --git a/CHANGELOG.md b/CHANGELOG.md index 73406762..226ca6a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,8 @@ Feature enhancements: Add `--null-data` flag, which makes ripgrep use NUL as a line terminator. * [FEATURE #997](https://github.com/BurntSushi/ripgrep/issues/997): The `--passthru` flag now works with the `--replace` flag. +* FEATURE: + Add `--line-buffered` and `--block-buffered` for forcing a buffer strategy. Bug fixes: diff --git a/complete/_rg b/complete/_rg index e35b60df..2bbdf992 100644 --- a/complete/_rg +++ b/complete/_rg @@ -44,6 +44,12 @@ _rg() { '(: * -)'{-h,--help}'[display help information]' '(: * -)'{-V,--version}'[display version information]' + + '(buffered)' # buffering options + '--line-buffered[force line buffering]' + $no"--no-line-buffered[don't force line buffering]" + '--block-buffered[force block buffering]' + $no"--no-block-buffered[don't force block buffering]" + + '(case)' # Case-sensitivity options {-i,--ignore-case}'[search case-insensitively]' {-s,--case-sensitive}'[search case-sensitively]' diff --git a/src/app.rs b/src/app.rs index 039b6980..e0e1eda7 100644 --- a/src/app.rs +++ b/src/app.rs @@ -544,6 +544,7 @@ pub fn all_args_and_flags() -> Vec { // "positive" flag. flag_after_context(&mut args); flag_before_context(&mut args); + flag_block_buffered(&mut args); flag_byte_offset(&mut args); flag_case_sensitive(&mut args); flag_color(&mut args); @@ -571,6 +572,7 @@ pub fn all_args_and_flags() -> Vec { flag_ignore_file(&mut args); flag_invert_match(&mut args); flag_json(&mut args); + flag_line_buffered(&mut args); flag_line_number(&mut args); flag_line_regexp(&mut args); flag_max_columns(&mut args); @@ -685,6 +687,36 @@ This overrides the --context flag. args.push(arg); } +fn flag_block_buffered(args: &mut Vec) { + const SHORT: &str = "Force block buffering."; + const LONG: &str = long!("\ +When enabled, ripgrep will use block buffering. That is, whenever a matching +line is found, it will be written to an in-memory buffer and will not be +written to stdout until the buffer reaches a certain size. This is the default +when ripgrep's stdout is redirected to a pipeline or a file. When ripgrep's +stdout is connected to a terminal, line buffering will be used. Forcing block +buffering can be useful when dumping a large amount of contents to a terminal. + +Forceful block buffering can be disabled with --no-block-buffered. Note that +using --no-block-buffered causes ripgrep to revert to its default behavior of +automatically detecting the buffering strategy. To force line buffering, use +the --line-buffered flag. +"); + let arg = RGArg::switch("block-buffered") + .help(SHORT).long_help(LONG) + .overrides("no-block-buffered") + .overrides("line-buffered") + .overrides("no-line-buffered"); + args.push(arg); + + let arg = RGArg::switch("no-block-buffered") + .hidden() + .overrides("block-buffered") + .overrides("line-buffered") + .overrides("no-line-buffered"); + args.push(arg); +} + fn flag_byte_offset(args: &mut Vec) { const SHORT: &str = "Print the 0-based byte offset for each matching line."; @@ -1245,6 +1277,37 @@ The JSON Lines format can be disabled with --no-json. args.push(arg); } +fn flag_line_buffered(args: &mut Vec) { + const SHORT: &str = "Force line buffering."; + const LONG: &str = long!("\ +When enabled, ripgrep will use line buffering. That is, whenever a matching +line is found, it will be flushed to stdout immediately. This is the default +when ripgrep's stdout is connected to a terminal, but otherwise, ripgrep will +use block buffering, which is typically faster. This flag forces ripgrep to +use line buffering even if it would otherwise use block buffering. This is +typically useful in shell pipelines, e.g., +'tail -f something.log | rg foo --line-buffered | rg bar'. + +Forceful line buffering can be disabled with --no-line-buffered. Note that +using --no-line-buffered causes ripgrep to revert to its default behavior of +automatically detecting the buffering strategy. To force block buffering, use +the --block-buffered flag. +"); + let arg = RGArg::switch("line-buffered") + .help(SHORT).long_help(LONG) + .overrides("no-line-buffered") + .overrides("block-buffered") + .overrides("no-block-buffered"); + args.push(arg); + + let arg = RGArg::switch("no-line-buffered") + .hidden() + .overrides("line-buffered") + .overrides("block-buffered") + .overrides("no-block-buffered"); + args.push(arg); +} + fn flag_line_number(args: &mut Vec) { const SHORT: &str = "Show line numbers."; const LONG: &str = long!("\ diff --git a/src/args.rs b/src/args.rs index 2decefe7..f8a29cae 100644 --- a/src/args.rs +++ b/src/args.rs @@ -314,7 +314,14 @@ impl Args { /// Execute the given function with a writer to stdout that enables color /// support based on the command line configuration. pub fn stdout(&self) -> cli::StandardStream { - cli::stdout(self.matches().color_choice()) + let color = self.matches().color_choice(); + if self.matches().is_present("line-buffered") { + cli::stdout_buffered_line(color) + } else if self.matches().is_present("block-buffered") { + cli::stdout_buffered_block(color) + } else { + cli::stdout(color) + } } /// Return the type definitions compiled into ripgrep.