Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
fe48f5a
Copy generate_container_preview as generate_container_count
brewingcode Feb 16, 2022
6eedd02
Fix `cargo build` error
brewingcode Feb 16, 2022
8c82925
Add `JsonViewer.preview`, and have `fill_in_container_preview` read it
brewingcode Feb 16, 2022
1e6dbc1
Reduce complexity of `generate_container_count`
brewingcode Feb 16, 2022
f7e2c0b
Remove `preview` from `JsonViewer::new`, set it elsewhere
brewingcode Feb 16, 2022
a9a4225
Add test for `generate_container_count`
brewingcode Feb 16, 2022
8ab2644
Add "none" as an option to `preview`
brewingcode Feb 16, 2022
c1bfd33
Merge remote-tracking branch 'origin/master' into optional-container-…
brewingcode Feb 17, 2022
8ad349d
Update jless.help and usage.html
brewingcode Feb 17, 2022
6a41936
Fix `none` preview mode
brewingcode Feb 17, 2022
2ee1eb6
Fix .len() vs .chars().count()
brewingcode Feb 17, 2022
6f71ce0
Add "items" to disambiguate arrays
brewingcode Feb 18, 2022
26a6059
Disable Preview::None in Mode::Line
brewingcode Feb 18, 2022
452fafd
Merge remote-tracking branch 'origin/master' into optional-container-…
brewingcode Feb 21, 2022
41079e9
Fix lint errors and warnings
brewingcode Feb 21, 2022
edeaf29
Merge remote-tracking branch 'origin/master' into optional-container-…
brewingcode Feb 22, 2022
4bbcff7
Merge remote-tracking branch 'origin/main' into optional-container-co…
brewingcode Mar 2, 2022
73ae72a
Fix lint and trivial code style
brewingcode Mar 2, 2022
0f33939
Merge remote-tracking branch 'PaulJuliusMartinez/main' into optional-…
brewingcode Mar 6, 2025
5884b3e
fix merge conflicts
brewingcode Mar 6, 2025
343e8a9
replace clap with arg
brewingcode Mar 6, 2025
414dc64
remove PaulJulius `p` command
brewingcode Mar 6, 2025
7e7f664
switch to Preview::Count as default
brewingcode Mar 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ impl App {
};

let mut viewer = JsonViewer::new(flatjson, opt.mode);
viewer.set_preview(opt.preview);
viewer.scrolloff_setting = opt.scrolloff;

let screen_writer =
Expand Down Expand Up @@ -311,12 +312,6 @@ impl App {
None
}
}
KeyEvent(Key::Char('p')) => {
self.input_state = InputState::PendingPCommand;
self.input_buffer.clear();
self.buffer_input(b'p');
None
}
KeyEvent(Key::Char('y')) => {
match &self.clipboard_context {
Ok(_) => {
Expand Down Expand Up @@ -463,6 +458,7 @@ impl App {
Key::End => Some(Action::FocusBottom),
Key::Char('%') => Some(Action::FocusMatchingPair),
Key::Char('m') => Some(Action::ToggleMode),
Key::Char('p') => Some(Action::TogglePreview),
Key::Char('<') => {
self.screen_writer
.decrease_indentation_level(self.viewer.flatjson.2 as u16);
Expand Down
6 changes: 6 additions & 0 deletions src/jless.help
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@

Space Toggle the collapsed state of the currently focused node.

DISPLAY

m Toggle display mode: line (default), data

p Cycle preview mode: full (default), count, none

SCROLLING

^e * Scroll down one line (or N lines).
Expand Down
181 changes: 162 additions & 19 deletions src/lineprinter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::search::MatchRangeIter;
use crate::terminal;
use crate::terminal::{Color, Style, Terminal};
use crate::truncatedstrview::TruncatedStrView;
use crate::viewer::Mode;
use crate::viewer::{Mode, Preview};

// This module is responsible for printing single lines of JSON to
// the screen, complete with syntax highlighting and highlighting
Expand Down Expand Up @@ -138,6 +138,7 @@ pub struct LineNumber {

pub struct LinePrinter<'a, 'b> {
pub mode: Mode,
pub preview: Preview,
pub terminal: &'a mut dyn Terminal,

// The entire FlatJson data structure and the specific line
Expand Down Expand Up @@ -796,27 +797,44 @@ impl<'a, 'b> LinePrinter<'a, 'b> {
available_space -= 1;
}

let always_quote_string_object_keys = self.mode == Mode::Line;
let is_nested = false;
let mut used_space = self.generate_container_preview(
row,
available_space,
is_nested,
always_quote_string_object_keys,
)?;
let mut used_space = 0;

if self.preview == Preview::Full {
let always_quote_string_object_keys = self.mode == Mode::Line;
let is_nested = false;
let mut used_space = self.generate_container_preview(
row,
available_space,
is_nested,
always_quote_string_object_keys,
)?;
let always_quote_string_object_keys = self.mode == Mode::Line;

if self.trailing_comma {
used_space += 1;
if self.trailing_comma {
self.highlight_str(
",",
Some(self.row.range.end),
(
&highlighting::DEFAULT_STYLE,
&highlighting::SEARCH_MATCH_HIGHLIGHTED,
),
)?;
used_space += 1;
if self.trailing_comma {
self.highlight_str(
",",
Some(self.row.range.end),
(
&highlighting::DEFAULT_STYLE,
&highlighting::SEARCH_MATCH_HIGHLIGHTED,
),
)?;
}
}
} else if self.preview == Preview::None {
self.highlight_str(
" ",
Some(self.row.range.end),
(
&highlighting::DEFAULT_STYLE,
&highlighting::SEARCH_MATCH_HIGHLIGHTED,
),
)?;
used_space = 1;
} else {
used_space = self.generate_container_count(row, available_space)?;
}

Ok(used_space)
Expand Down Expand Up @@ -944,6 +962,55 @@ impl<'a, 'b> LinePrinter<'a, 'b> {
Ok(num_printed)
}

// Similar to generate_container_preview, except it only prints the count
// of the number of children:
// - number of key-val pairs in a hash, ie "{ 3 }"
// - number of items in an array, ie "[ 2 ]"
fn generate_container_count(
&mut self,
row: &Row,
available_space: isize,
) -> Result<isize, fmt::Error> {
debug_assert!(row.is_opening_of_container());

// Minimum amount of space required == 3: […]
if available_space < 3 {
return Ok(0);
}

let mut next_sibling = row.first_child();
let mut count: usize = 0;
while let OptionIndex::Index(child) = next_sibling {
next_sibling = self.flatjson[child].next_sibling;
count += 1;
}

let container_type = row.value.container_type().unwrap();
let mut count_str = format!(
"{} {} item{} {}",
container_type.open_str(),
count,
if count == 1 { "" } else { "s" },
container_type.close_str()
);

if count_str.len() as isize > available_space {
count_str = format!(
"{}…{}",
container_type.open_str(),
container_type.close_str()
);
}

self.highlight_str(
&count_str,
Some(self.row.range.start),
highlighting::PREVIEW_STYLES,
)?;
let len = count_str.chars().count() as isize;
Ok(len)
}

// {a…: …, …}
//
// [a, …]
Expand Down Expand Up @@ -1214,6 +1281,7 @@ mod tests {
) -> LinePrinter<'a, 'a> {
LinePrinter {
mode: Mode::Data,
preview: Preview::Full,
terminal,
flatjson,
row: &flatjson[index],
Expand Down Expand Up @@ -1855,6 +1923,81 @@ mod tests {
Ok(())
}

#[test]
fn test_generate_countainer_count() -> std::fmt::Result {
let json_arr = r#"[1,2,3] "#;
let json_obj = r#"{"a":1, "b":2}"#;
let json_one = r#"{"c":3}"#;
// 012345678901234 (14 chars)
let fj_arr = parse_top_level_json(json_arr.to_owned()).unwrap();
let fj_obj = parse_top_level_json(json_obj.to_owned()).unwrap();
let fj_one = parse_top_level_json(json_one.to_owned()).unwrap();

let mut term = TextOnlyTerminal::new();
let mut line: LinePrinter = LinePrinter {
preview: Preview::Count,
..default_line_printer(&mut term, &fj_arr, 0)
};

for (available_space, used_space, expected) in
vec![(14, 11, r#"[ 3 items ]"#), (4, 3, r#"[…]"#), (2, 0, r#""#)].into_iter()
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I think the linter is being overzealous here, and in the rest of the test_ functions in this commit. It's way harder to read this vec![...] on a single line. They were on their own lines with specific spacing to make it easier for humans to read.

{
let used = line.generate_container_count(&fj_arr[0], available_space)?;
assert_eq!(
expected,
line.terminal.output(),
"expected preview with {} available columns (used up {} columns)",
available_space,
UnicodeWidthStr::width(line.terminal.output()),
);
assert_eq!(used_space, used);

line.terminal.clear_output();
}

line = LinePrinter {
preview: Preview::Count,
..default_line_printer(&mut term, &fj_obj, 0)
};

for (available_space, used_space, expected) in
vec![(14, 11, r#"{ 2 items }"#), (4, 3, r#"{…}"#), (2, 0, r#""#)].into_iter()
{
let used = line.generate_container_count(&fj_obj[0], available_space)?;
assert_eq!(
expected,
line.terminal.output(),
"expected preview with {} available columns (used up {} columns)",
available_space,
UnicodeWidthStr::width(line.terminal.output()),
);
assert_eq!(used_space, used);

line.terminal.clear_output();
}

line = LinePrinter {
preview: Preview::Count,
..default_line_printer(&mut term, &fj_one, 0)
};

for (available_space, used_space, expected) in vec![(14, 10, r#"{ 1 item }"#)].into_iter() {
let used = line.generate_container_count(&fj_one[0], available_space)?;
assert_eq!(
expected,
line.terminal.output(),
"expected preview with {} available columns (used up {} columns)",
available_space,
UnicodeWidthStr::width(line.terminal.output()),
);
assert_eq!(used_space, used);

line.terminal.clear_output();
}

Ok(())
}

#[test]
fn test_generate_array_preview() -> fmt::Result {
let json = r#"[1, {"x": true}, null, "hello", true]"#;
Expand Down
11 changes: 10 additions & 1 deletion src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::path::PathBuf;

use clap::{ArgAction, Parser, ValueEnum};

use crate::viewer::Mode;
use crate::viewer::{Mode, Preview};

#[derive(PartialEq, Eq, Copy, Clone, Debug, ValueEnum)]
pub enum DataFormat {
Expand All @@ -29,6 +29,15 @@ pub struct Opt {
#[arg(short, long, value_enum, hide_possible_values = true, default_value_t = Mode::Data)]
pub mode: Mode,

/// Initial preview of container nodes. In full mode (--preview full;
/// the default), containers will be rendered as much as they can be in
/// the width of the terminal. In count mode (--preview count), only
/// the child node count will be rendered. In none mode (--preview none),
/// no preview will be rendered at all. This can be toggled by pressing
/// 'p'.
#[arg(short, long, value_enum, hide_possible_values = true, default_value_t = Preview::Count)]
pub preview: Preview,

// This godforsaken configuration to get both --line-numbers and --no-line-numbers to
// work (with --line-numbers as the default) and --relative-line-numbers and
// --no-relative-line-numbers to work (with --no-relative-line-numbers as the default)
Expand Down
1 change: 1 addition & 0 deletions src/screenwriter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ impl ScreenWriter {

let mut line = lp::LinePrinter {
mode: viewer.mode,
preview: viewer.get_preview(),
terminal: &mut self.terminal,

flatjson: &viewer.flatjson,
Expand Down
Loading