Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ and [jiq](https://github.com/fiatjaf/jiq).
- [Object Identifier-Index](https://jqlang.github.io/jq/manual/#object-identifier-index)
- [Array Index](https://jqlang.github.io/jq/manual/#array-index)
- Hint message to evaluate the filter
- Keeps the last successful result visible when a query returns null or fails,
so the view stays stable while you fix the filter

## Installation

Expand Down
5 changes: 5 additions & 0 deletions src/guide.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ pub enum GuideMessage {
NoSuggestionFound(String),
JqReturnedNull(String),
JqFailed(String),
/// The query is empty, so the full input is shown unfiltered.
NoFilter,
}

/// Represent an action to be performed on the guide.
Expand Down Expand Up @@ -66,6 +68,9 @@ fn message_to_state(message: GuideMessage) -> status::State {
GuideMessage::JqFailed(e) => {
status::State::new(format!("jq failed: `{e}`"), Severity::Error)
}
GuideMessage::NoFilter => {
status::State::new("no filter — showing full input", Severity::Success)
}
}
}

Expand Down
47 changes: 39 additions & 8 deletions src/json_viewer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ pub enum RenderTrigger {
pub struct JsonViewer {
state: jsonstream::State,
json: Vec<serde_json::Value>,
/// The most recent non-null successful query result. Used to keep the view
/// stable when a later query returns null or fails, instead of resetting to
/// the whole input document. Empty until the first successful result.
last_good: Vec<serde_json::Value>,
keybinds: JsonViewerKeybinds,
}

Expand Down Expand Up @@ -89,21 +93,37 @@ impl JsonViewer {
area: (u16, u16),
input: String,
) -> (Option<GuideMessage>, Option<StyledGraphemes>) {
// An empty (or whitespace-only) query means "no filter": reset to the
// full input document fresh. jaq rejects an empty program as invalid, so
// without this guard an explicit clear (e.g. Ctrl+U) is treated like an
// incomplete filter mid-typing — falling back to the stale last result,
// or on a fresh view silently dumping the whole input with no hint.
if input.trim().is_empty() {
self.last_good = Vec::new();
self.state.stream = JsonStream::new(self.json.iter());
return (
Some(GuideMessage::NoFilter),
Some(self.state.create_graphemes(area.0, area.1)),
);
}

match json::run_jaq(&input, &self.json) {
Ok(ret) => {
let mut guide = None;
if ret.iter().all(|val| *val == Value::Null) {
guide = Some(GuideMessage::JqReturnedNull(input));

self.state.stream = JsonStream::new(self.json.iter());
// `all` is vacuously true for an empty result, so a filter that
// produces nothing is treated like null: keep the view stable.
let guide = if ret.iter().all(|val| *val == Value::Null) {
self.show_last_good();
Some(GuideMessage::JqReturnedNull(input))
} else {
self.state.stream = JsonStream::new(ret.iter());
}
self.last_good = ret;
self.state.stream = JsonStream::new(self.last_good.iter());
None
};

(guide, Some(self.state.create_graphemes(area.0, area.1)))
}
Err(e) => {
self.state.stream = JsonStream::new(self.json.iter());
self.show_last_good();

(
Some(GuideMessage::JqFailed(e.to_string())),
Expand All @@ -112,6 +132,16 @@ impl JsonViewer {
}
}
}

/// Point the view at the most recent successful result, falling back to the
/// original input document if no query has succeeded yet.
fn show_last_good(&mut self) {
self.state.stream = if self.last_good.is_empty() {
JsonStream::new(self.json.iter())
} else {
JsonStream::new(self.last_good.iter())
};
}
}

/// Initialize the JSON viewer with the given input, configuration, keybinds, and shared context.
Expand Down Expand Up @@ -158,6 +188,7 @@ pub async fn initialize(

Ok(Arc::new(Mutex::new(JsonViewer {
json: input_stream,
last_good: Vec::new(),
state,
keybinds,
})))
Expand Down