1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
use std::{future::Future, sync::Arc};
use promkit_widgets::spinner;
use tokio::{sync::Mutex, task::JoinHandle};
/// Represent the different sections of the UI, which can be used to manage focus and input handling.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Index {
QueryEditor = 0,
Guide = 1,
Completion = 2,
JsonViewer = 3,
}
#[derive(PartialEq)]
/// Represent the current state of the JSON viewer,
/// which can be used to control rendering behavior
/// and manage concurrent tasks like query processing and spinner animation.
pub enum State {
/// The viewer is idle and ready for user interactions or query processing.
Idle,
/// The viewer is currently loading the JSON stream, which may involve deserialization
Loading,
/// The viewer is actively processing a jq query, which may involve executing the query
/// and updating the view with the results.
Processing,
}
pub struct Context {
/// The current state of the processor, which can be Idle, Loading, or Processing.
pub state: State,
/// Current active index for user input handling.
pub active_index: Index,
/// The current size of the terminal area.
///
/// PERF NOTE: This currently lives with `state/current_task` in the same mutex
/// for simplicity. If lock contention becomes visible, this can be split into
/// a dedicated shared store (e.g. `Arc<RwLock<(u16, u16)>>`) to reduce lock
/// granularity.
pub area: (u16, u16),
/// The current task being executed, if any.
pub current_task: Option<JoinHandle<()>>,
}
#[derive(Clone)]
pub struct SharedContext(Arc<Mutex<Context>>);
impl SharedContext {
pub fn new(area: (u16, u16)) -> Self {
Self(Arc::new(Mutex::new(Context {
state: State::Idle,
active_index: Index::QueryEditor,
area,
current_task: None,
})))
}
pub async fn area(&self) -> (u16, u16) {
let ctx = self.0.lock().await;
ctx.area
}
pub async fn set_area(&self, area: (u16, u16)) {
let mut ctx = self.0.lock().await;
ctx.area = area;
}
pub async fn active_index(&self) -> Index {
let ctx = self.0.lock().await;
ctx.active_index
}
/// Set the active index, which controls which input field is currently focused.
/// If the index is `Guide`, it will be ignored to prevent focus on the guide section.
pub async fn set_active_index(&self, index: Index) {
if index == Index::Guide {
return;
}
let mut ctx = self.0.lock().await;
ctx.active_index = index;
}
pub(crate) async fn lock(&self) -> tokio::sync::MutexGuard<'_, Context> {
self.0.lock().await
}
}
impl spinner::State for SharedContext {
fn is_idle(&self) -> impl Future<Output = bool> + Send {
let shared = self.0.clone();
async move {
let context = shared.lock().await;
context.state == State::Idle
}
}
}