mirror of
https://github.com/martinvonz/jj.git
synced 2025-02-05 11:05:46 +00:00
built-in pager: allow configuring streampager options
This also changes the default to be closer to `less -FRX`. Since this default last changed very recently in #4203, I didn't mention this in the Changelog. As discussed in https://github.com/jj-vcs/jj/pull/4203#discussion_r1914372214 I initially kept the config closer to streampager's (see https://github.com/jj-vcs/jj/compare/main...ilyagr:jj:streamopts?expand=1), but then decided to make it more generic, smaller, and hopefully easier to understand.
This commit is contained in:
parent
8aa29169ea
commit
f60014f3ee
5 changed files with 146 additions and 18 deletions
|
@ -67,7 +67,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|||
|
||||
* The builtin pager is switched to
|
||||
[streampager](https://github.com/markbt/streampager/). It can handle large
|
||||
inputs better.
|
||||
inputs better and can be configured.
|
||||
|
||||
* Conflicts materialized in the working copy before `jj 0.19.0` may no longer
|
||||
be parsed correctly. If you are using version 0.18.0 or earlier, check out a
|
||||
|
|
|
@ -107,6 +107,30 @@
|
|||
"description": "Pager to use for displaying command output",
|
||||
"default": "less -FRX"
|
||||
},
|
||||
"streampager": {
|
||||
"type": "object",
|
||||
"description": "':builtin' (streampager-based) pager configuration",
|
||||
"properties": {
|
||||
"interface": {
|
||||
"description": "Whether to quit automatically, whether to clear screen on startup/exit",
|
||||
"enum": [
|
||||
"quit-if-one-page",
|
||||
"full-screen-clear-output",
|
||||
"quit-quickly-or-clear-output"
|
||||
],
|
||||
"default": "never"
|
||||
},
|
||||
"wrapping": {
|
||||
"description": "Whether to wrap long lines",
|
||||
"enum": [
|
||||
"anywhere",
|
||||
"word",
|
||||
"none"
|
||||
],
|
||||
"default": "anywhere"
|
||||
}
|
||||
}
|
||||
},
|
||||
"diff": {
|
||||
"type": "object",
|
||||
"description": "Options for how diffs are displayed",
|
||||
|
|
|
@ -41,6 +41,10 @@ show-cryptographic-signatures = false
|
|||
[ui.movement]
|
||||
edit = false
|
||||
|
||||
[ui.streampager]
|
||||
interface = "quit-if-one-page"
|
||||
wrapping = "anywhere"
|
||||
|
||||
[snapshot]
|
||||
max-new-file-size = "1MiB"
|
||||
auto-track = "all()"
|
||||
|
|
|
@ -79,10 +79,19 @@ impl UiOutput {
|
|||
Ok(UiOutput::Paged { child, child_stdin })
|
||||
}
|
||||
|
||||
fn new_builtin_paged() -> streampager::Result<UiOutput> {
|
||||
fn new_builtin_paged(config: &StreampagerConfig) -> streampager::Result<UiOutput> {
|
||||
// This uselessly reads ~/.config/streampager/streampager.toml, even
|
||||
// though we then override the important options.
|
||||
// TODO(ilyagr): Fix this once a version of streampager with
|
||||
// https://github.com/facebook/sapling/pull/1011 is released.
|
||||
let mut pager = streampager::Pager::new_using_stdio()?;
|
||||
// TODO: should we set the interface mode to be "less -FRX" like?
|
||||
// It will override the user-configured values.
|
||||
pager.set_wrapping_mode(config.wrapping);
|
||||
pager.set_interface_mode(config.streampager_interface_mode());
|
||||
// We could make scroll-past-eof configurable, but I'm guessing people
|
||||
// will not miss it. If we do make it configurable, we should mention
|
||||
// that it's a bad idea to turn this on if `interface=quit-if-one-page`,
|
||||
// as it can leave a lot of empty lines on the screen after exiting.
|
||||
pager.set_scroll_past_eof(false);
|
||||
|
||||
// Use native pipe, which can be attached to child process. The stdout
|
||||
// stream could be an in-process channel, but the cost of extra syscalls
|
||||
|
@ -257,9 +266,59 @@ pub enum PaginationChoice {
|
|||
Auto,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize)]
|
||||
#[serde(rename_all(deserialize = "kebab-case"))]
|
||||
pub enum StreampagerAlternateScreenMode {
|
||||
QuitIfOnePage,
|
||||
FullScreenClearOutput,
|
||||
QuitQuicklyOrClearOutput,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize)]
|
||||
#[serde(rename_all(deserialize = "kebab-case"))]
|
||||
enum StreampagerWrappingMode {
|
||||
None,
|
||||
Word,
|
||||
Anywhere,
|
||||
}
|
||||
|
||||
impl From<StreampagerWrappingMode> for streampager::config::WrappingMode {
|
||||
fn from(val: StreampagerWrappingMode) -> Self {
|
||||
use streampager::config::WrappingMode;
|
||||
match val {
|
||||
StreampagerWrappingMode::None => WrappingMode::Unwrapped,
|
||||
StreampagerWrappingMode::Word => WrappingMode::WordBoundary,
|
||||
StreampagerWrappingMode::Anywhere => WrappingMode::GraphemeBoundary,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize)]
|
||||
#[serde(rename_all(deserialize = "kebab-case"))]
|
||||
struct StreampagerConfig {
|
||||
interface: StreampagerAlternateScreenMode,
|
||||
wrapping: StreampagerWrappingMode,
|
||||
// TODO: Add an `quit-quickly-delay-seconds` floating point option or a
|
||||
// `quit-quickly-delay` option that takes a 's' or 'ms' suffix. Note that as
|
||||
// of this writing, floating point numbers do not work with `--config`
|
||||
}
|
||||
|
||||
impl StreampagerConfig {
|
||||
fn streampager_interface_mode(&self) -> streampager::config::InterfaceMode {
|
||||
use streampager::config::InterfaceMode;
|
||||
use StreampagerAlternateScreenMode::*;
|
||||
match self.interface {
|
||||
// InterfaceMode::Direct not implemented
|
||||
FullScreenClearOutput => InterfaceMode::FullScreen,
|
||||
QuitIfOnePage => InterfaceMode::Hybrid,
|
||||
QuitQuicklyOrClearOutput => InterfaceMode::Delayed(std::time::Duration::from_secs(2)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum PagerConfig {
|
||||
Disabled,
|
||||
Builtin,
|
||||
Builtin(StreampagerConfig),
|
||||
External(CommandNameAndArgs),
|
||||
}
|
||||
|
||||
|
@ -270,7 +329,7 @@ impl PagerConfig {
|
|||
};
|
||||
match config.get("ui.pager")? {
|
||||
CommandNameAndArgs::String(name) if name == BUILTIN_PAGER_NAME => {
|
||||
Ok(PagerConfig::Builtin)
|
||||
Ok(PagerConfig::Builtin(config.get("ui.streampager")?))
|
||||
}
|
||||
_ => Ok(PagerConfig::External(config.get("ui.pager")?)),
|
||||
}
|
||||
|
@ -318,16 +377,18 @@ impl Ui {
|
|||
PagerConfig::Disabled => {
|
||||
return;
|
||||
}
|
||||
PagerConfig::Builtin => UiOutput::new_builtin_paged()
|
||||
.inspect_err(|err| {
|
||||
writeln!(
|
||||
self.warning_default(),
|
||||
"Failed to set up builtin pager: {err}",
|
||||
err = format_error_with_sources(err),
|
||||
)
|
||||
.ok();
|
||||
})
|
||||
.ok(),
|
||||
PagerConfig::Builtin(streampager_config) => {
|
||||
UiOutput::new_builtin_paged(streampager_config)
|
||||
.inspect_err(|err| {
|
||||
writeln!(
|
||||
self.warning_default(),
|
||||
"Failed to set up builtin pager: {err}",
|
||||
err = format_error_with_sources(err),
|
||||
)
|
||||
.ok();
|
||||
})
|
||||
.ok()
|
||||
}
|
||||
PagerConfig::External(command_name_and_args) => {
|
||||
UiOutput::new_paged(command_name_and_args)
|
||||
.inspect_err(|err| {
|
||||
|
|
|
@ -575,8 +575,8 @@ a `$`):
|
|||
`less -FRX` is the default pager in the absence of any other setting, except
|
||||
on Windows where it is `:builtin`.
|
||||
|
||||
The special value `:builtin` enables usage of the [integrated pager called
|
||||
`streampager`](https://github.com/markbt/streampager/).
|
||||
The special value `:builtin` enables usage of the [integrated
|
||||
pager](#builtin-pager).
|
||||
|
||||
If you are using a standard Linux distro, your system likely already has
|
||||
`$PAGER` set and that will be preferred over the built-in. To use the built-in:
|
||||
|
@ -598,6 +598,45 @@ paginate = "auto"
|
|||
paginate = "never"
|
||||
```
|
||||
|
||||
### Builtin pager
|
||||
|
||||
Our builtin pager is based on
|
||||
[`streampager`](https://github.com/markbt/streampager/) but is configured within
|
||||
`jj`'s config. It is configured via the `ui.streampager` table.
|
||||
|
||||
#### Wrapping
|
||||
|
||||
Wrapping performed by the pager happens *in addition to* any
|
||||
wrapping that `jj` itself does.
|
||||
|
||||
```toml
|
||||
[ui.streampager]
|
||||
wrapping = "anywhere" # wrap at screen edge (default)
|
||||
wrapping = "word" # wrap on word boundaries
|
||||
wrapping = "none" # strip long lines, allow scrolling
|
||||
# left and right like `less -S`
|
||||
```
|
||||
|
||||
#### Auto-exit, clearing the screen on startup or exit
|
||||
|
||||
You can configure whether the pager clears the screen on startup or exit, and
|
||||
whether it quits automatically on short inputs. When the pager auto-quits,
|
||||
features like word-wrapping are disabled.
|
||||
|
||||
```toml
|
||||
[ui.streampager]
|
||||
# Do not clear screen on exit. Use a full-screen interface for long
|
||||
# output only. Like `less -FX`.
|
||||
interface = "quit-if-one-page" # (default).
|
||||
# Always use a full-screen interface, ask the terminal to clear the
|
||||
# screen on exit. Like `less -+FX`.
|
||||
interface = "full-screen-clear-output"
|
||||
# Use the alternate screen if the input is either long or takes more
|
||||
# than 2 seconds to finish. Similar but not identical to `less -F -+X`.
|
||||
interface = "quit-quickly-or-clear-output"
|
||||
```
|
||||
|
||||
|
||||
### Processing contents to be paged
|
||||
|
||||
If you'd like to pass the output through a formatter e.g.
|
||||
|
|
Loading…
Reference in a new issue