Skip to content

Commit 159f06c

Browse files
committed
misc
1 parent 68477c8 commit 159f06c

5 files changed

Lines changed: 126 additions & 50 deletions

File tree

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
use std::{convert::Infallible, time::Duration};
2+
3+
use crossterm::event::{self, Event};
4+
5+
/// Trait for abstracting event polling so we can hardcode events in tests.
6+
pub(super) trait EventPolling {
7+
type Error: std::error::Error + Send + Sync + 'static;
8+
9+
fn poll(self, timeout: Duration) -> Result<impl IntoIterator<Item = Event>, Self::Error>;
10+
}
11+
12+
/// An [`EventPolling`] implementation that polls events for real using crossterm.
13+
#[derive(Copy, Clone)]
14+
pub(super) struct CrosstermEventPolling;
15+
16+
impl EventPolling for CrosstermEventPolling {
17+
type Error = std::io::Error;
18+
19+
fn poll(self, timeout: Duration) -> Result<impl IntoIterator<Item = Event>, Self::Error> {
20+
if event::poll(timeout)? {
21+
Ok(Some(event::read()?))
22+
} else {
23+
Ok(None)
24+
}
25+
}
26+
}
27+
28+
/// An [`EventPolling`] implementation that never yields events.
29+
///
30+
/// This is used for non-interactive runs where touching terminal input can stop the process when
31+
/// profilers launch the target in a background process group.
32+
#[derive(Copy, Clone)]
33+
pub(super) struct NoopEventPolling;
34+
35+
impl EventPolling for NoopEventPolling {
36+
type Error = Infallible;
37+
38+
fn poll(self, _timeout: Duration) -> Result<impl IntoIterator<Item = Event>, Self::Error> {
39+
Ok(None)
40+
}
41+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use std::time::{Duration, Instant};
2+
3+
#[derive(Debug)]
4+
pub(super) struct FpsCounter {
5+
timer: Instant,
6+
next_fps: u32,
7+
fps: u32,
8+
}
9+
10+
impl FpsCounter {
11+
pub(super) fn new() -> Self {
12+
Self {
13+
timer: Instant::now(),
14+
fps: 0,
15+
next_fps: 0,
16+
}
17+
}
18+
19+
pub(super) fn frame_finished(&mut self) {
20+
self.next_fps += 1;
21+
}
22+
23+
pub(super) fn update(&mut self) -> bool {
24+
if self.timer.elapsed() > Duration::from_secs(1) {
25+
self.fps = std::mem::take(&mut self.next_fps);
26+
self.timer = Instant::now();
27+
true
28+
} else {
29+
false
30+
}
31+
}
32+
33+
pub(super) fn fps(&self) -> u32 {
34+
self.fps
35+
}
36+
}

crates/but/src/command/legacy/status/tui/key_bind.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ pub(super) fn default_key_binds() -> KeyBinds {
2929
register_rub_but_api_mode_key_binds(&mut key_binds);
3030
}
3131
ModeDiscriminant::InlineReword => {
32-
register_global_key_binds(&mut key_binds, Vec::from([mode]));
3332
register_inline_reword_mode_key_binds(&mut key_binds);
33+
register_quit_key_binds(&mut key_binds, Vec::from([mode]));
3434
}
3535
ModeDiscriminant::Command => {
36-
register_global_key_binds(&mut key_binds, Vec::from([mode]));
3736
register_command_mode_key_binds(&mut key_binds);
37+
register_quit_key_binds(&mut key_binds, Vec::from([mode]));
3838
}
3939
ModeDiscriminant::Commit => {
4040
register_global_key_binds(&mut key_binds, Vec::from([mode]));
@@ -178,6 +178,15 @@ fn register_detail_key_binds(key_binds: &mut KeyBinds) {
178178
hide_from_hotbar: false,
179179
});
180180

181+
key_binds.register(StaticKeyBind {
182+
short_description: "command",
183+
chord_display: ":",
184+
key_matcher: press().code(KeyCode::Char(':')),
185+
modes: Vec::from([ModeDiscriminant::Details]),
186+
message: Message::Command(CommandMessage::Start),
187+
hide_from_hotbar: false,
188+
});
189+
181190
key_binds.register(StaticKeyBind {
182191
short_description: "normal mode",
183192
chord_display: "ctrl+[",
@@ -467,6 +476,15 @@ fn register_inline_reword_mode_key_binds(key_binds: &mut KeyBinds) {
467476
message: Message::EnterNormalMode,
468477
hide_from_hotbar: false,
469478
});
479+
480+
key_binds.register(StaticKeyBind {
481+
short_description: "normal mode",
482+
chord_display: "ctrl+[",
483+
key_matcher: press().control().code(KeyCode::Char('[')),
484+
modes: Vec::from([ModeDiscriminant::InlineReword]),
485+
message: Message::EnterNormalMode,
486+
hide_from_hotbar: true,
487+
});
470488
}
471489

472490
fn register_command_mode_key_binds(key_binds: &mut KeyBinds) {
@@ -487,6 +505,15 @@ fn register_command_mode_key_binds(key_binds: &mut KeyBinds) {
487505
message: Message::EnterNormalMode,
488506
hide_from_hotbar: false,
489507
});
508+
509+
key_binds.register(StaticKeyBind {
510+
short_description: "normal mode",
511+
chord_display: "ctrl+[",
512+
key_matcher: press().control().code(KeyCode::Char('[')),
513+
modes: Vec::from([ModeDiscriminant::Command]),
514+
message: Message::EnterNormalMode,
515+
hide_from_hotbar: true,
516+
});
490517
}
491518

492519
fn register_commit_mode_key_binds(key_binds: &mut KeyBinds) {

crates/but/src/command/legacy/status/tui/mod.rs

Lines changed: 20 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ use crate::{
3939
confirm::{Confirm, ConfirmMessage},
4040
cursor::{Cursor, is_selectable_in_mode},
4141
details::{Details, DetailsMessage, DetailsVisibility, RenderNextChunkResult},
42+
event_polling::{CrosstermEventPolling, EventPolling, NoopEventPolling},
43+
fps::FpsCounter,
4244
graph_extension::{ExtensionDirection, extend_connector_spans},
4345
highlight::{Highlights, with_highlight},
4446
key_bind::{KeyBinds, confirm_key_binds, default_key_binds},
@@ -63,6 +65,8 @@ use super::{
6365
mod confirm;
6466
mod cursor;
6567
mod details;
68+
mod event_polling;
69+
mod fps;
6670
mod graph_extension;
6771
mod highlight;
6872
mod key_bind;
@@ -134,44 +138,6 @@ pub(super) async fn render_tui(
134138
Ok(app.status_lines)
135139
}
136140

137-
/// Trait for abstracting event polling so we can hardcode events in tests.
138-
trait EventPolling {
139-
type Error: std::error::Error + Send + Sync + 'static;
140-
141-
fn poll(self, timeout: Duration) -> Result<impl IntoIterator<Item = Event>, Self::Error>;
142-
}
143-
144-
/// An [`EventPolling`] implementation that polls events for real using crossterm.
145-
#[derive(Copy, Clone)]
146-
struct CrosstermEventPolling;
147-
148-
impl EventPolling for CrosstermEventPolling {
149-
type Error = std::io::Error;
150-
151-
fn poll(self, timeout: Duration) -> Result<impl IntoIterator<Item = Event>, Self::Error> {
152-
if event::poll(timeout)? {
153-
Ok(Some(event::read()?))
154-
} else {
155-
Ok(None)
156-
}
157-
}
158-
}
159-
160-
/// An [`EventPolling`] implementation that never yields events.
161-
///
162-
/// This is used for non-interactive runs where touching terminal input can stop the process when
163-
/// profilers launch the target in a background process group.
164-
#[derive(Copy, Clone)]
165-
struct NoopEventPolling;
166-
167-
impl EventPolling for NoopEventPolling {
168-
type Error = std::io::Error;
169-
170-
fn poll(self, _timeout: Duration) -> Result<impl IntoIterator<Item = Event>, Self::Error> {
171-
Ok(None)
172-
}
173-
}
174-
175141
#[expect(clippy::too_many_arguments)]
176142
async fn render_loop<T, E>(
177143
app: &mut App,
@@ -242,7 +208,11 @@ where
242208
mode,
243209
)
244210
.await?;
211+
245212
render(app, terminal_guard)?;
213+
214+
app.fps.frame_finished();
215+
246216
Ok(())
247217
}
248218

@@ -332,6 +302,10 @@ where
332302
app.should_render = true;
333303
}
334304

305+
if app.fps.update() {
306+
app.should_render = true;
307+
}
308+
335309
Ok(())
336310
}
337311

@@ -341,6 +315,7 @@ where
341315
anyhow::Error: From<<T::Backend as Backend>::Error>,
342316
{
343317
if std::mem::take(&mut app.should_render) {
318+
let _span = tracing::trace_span!("render").entered();
344319
terminal_guard.terminal_mut().draw(|frame| {
345320
app.renders += 1;
346321
app.render(frame)
@@ -370,6 +345,7 @@ struct App {
370345
options: TuiLaunchOptions,
371346
delayed_messages: Vec<Message>,
372347
incoming_out_of_band_messages: Vec<Rc<Receiver<Message>>>,
348+
fps: FpsCounter,
373349
}
374350

375351
impl App {
@@ -407,6 +383,7 @@ impl App {
407383
highlight: Default::default(),
408384
delayed_messages: Default::default(),
409385
incoming_out_of_band_messages: Default::default(),
386+
fps: FpsCounter::new(),
410387
confirm: None,
411388
details,
412389
options,
@@ -1756,10 +1733,6 @@ impl App {
17561733
}
17571734

17581735
fn handle_enter_command_mode(&mut self) {
1759-
if !matches!(self.mode, Mode::Normal) {
1760-
return;
1761-
}
1762-
17631736
let mut textarea = TextArea::default();
17641737
textarea.set_cursor_line_style(Style::default());
17651738
textarea.move_cursor(CursorMove::End);
@@ -1855,7 +1828,6 @@ impl App {
18551828
Some(*commit_id)
18561829
}
18571830

1858-
#[tracing::instrument(level = Level::TRACE, skip_all)]
18591831
fn render(&self, frame: &mut Frame) {
18601832
let content_layout =
18611833
Layout::vertical([Constraint::Min(1), Constraint::Length(1)]).split(frame.area());
@@ -2514,20 +2486,23 @@ impl App {
25142486
}
25152487

25162488
fn render_debug(&self, area: Rect, frame: &mut Frame) {
2517-
let renders = once(ListItem::new("Renders").black().on_blue())
2518-
.chain(once(ListItem::new(format!("{}", self.renders))));
2489+
let renders = once(ListItem::new("FPS").black().on_blue()).chain(once(ListItem::new(
2490+
format!("{} FPS ({} renders)", self.fps.fps(), self.renders),
2491+
)));
25192492

25202493
let details_selection = format!("{:#?}", self.details.selection());
25212494
let details_selection = once(ListItem::new("Details selection").black().on_blue()).chain(
25222495
details_selection
25232496
.lines()
2497+
.take(100)
25242498
.map(|line| ListItem::new(line.to_owned())),
25252499
);
25262500

25272501
let status_selection = format!("{:#?}", self.cursor.selected_line(&self.status_lines));
25282502
let status_selection = once(ListItem::new("Status selection").black().on_blue()).chain(
25292503
status_selection
25302504
.lines()
2505+
.take(100)
25312506
.map(|line| ListItem::new(line.to_owned())),
25322507
);
25332508

crates/but/src/command/legacy/status/tui/rub_from_detail_view.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@ pub(super) fn route_operation<'a>(
1414
source: &'a CommittedHunk,
1515
target: &'a CliId,
1616
) -> Option<Operation<'a>> {
17-
// TODO(david): support more operations
18-
let todo_ = ();
19-
2017
match target {
2118
CliId::Commit { commit_id, .. } => Some(Operation::CommittedHunkToCommit {
2219
source,

0 commit comments

Comments
 (0)