Skip to content

Commit 4c2f635

Browse files
committed
feat: add home/end support
1 parent 12b046a commit 4c2f635

File tree

4 files changed

+70
-18
lines changed

4 files changed

+70
-18
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242

4343
> Read the f*cking manual NOW!
4444
45-
RTFM - blazingly fast util for searching manuals & docs. Blazingly fast terminal utility for exploring man pages and tldr cheatsheets.
45+
RTFM - blazingly fast util for exploring man pages and tldr cheatsheets.
4646

4747
We are support [tldr](https://github.com/tldr-pages/tldr). You should install `man` and `tldr`.
4848

src/man_db.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ impl ManDb {
122122
let output_str = String::from_utf8_lossy(&output.stdout);
123123
let mut man_map = HashMap::new();
124124
let mut commands = Vec::new();
125-
let re = Regex::new(r"\((\d)\)").unwrap();
125+
let re = Regex::new(r"\((\d)\)")?;
126126

127127
for line in output_str.lines() {
128128
if let Some((name, desc)) = line.split_once(" - ") {

src/trie.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ mod trie_tests {
9090
trie.insert("pythonic");
9191

9292
let results = trie.words_starting_with("ru");
93-
assert_eq!(results, vec!["ruby", "rust"]);
93+
assert_eq!(results, vec!["rust", "ruby"]);
9494

9595
let results = trie.words_starting_with("rust");
9696
assert_eq!(results, vec!["rust"]);

src/tui.rs

Lines changed: 67 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@ use crossterm::{
66
KeyModifiers,
77
},
88
execute,
9-
terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
9+
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
1010
};
1111
use std::sync::Arc;
1212
use std::time::{Duration, Instant};
1313
use tui::{
14-
Terminal,
1514
backend::CrosstermBackend,
1615
layout::{Constraint, Direction, Layout, Rect},
1716
style::{Color, Modifier, Style},
1817
text::{Span, Spans},
1918
widgets::{Block, Borders, List, ListItem, ListState, Paragraph, Wrap},
19+
Terminal,
2020
};
2121

2222
const PAGE_SIZE: usize = 30;
@@ -71,6 +71,14 @@ enum PageSource {
7171
Tldr,
7272
}
7373

74+
fn scroll_to_top(app: &mut AppState) {
75+
app.man_page.scroll = 0;
76+
}
77+
78+
fn scroll_to_bottom(app: &mut AppState) {
79+
app.man_page.scroll = app.man_page.content.len().saturating_sub(PAGE_SIZE);
80+
}
81+
7482
/// Runs the TUI application
7583
pub async fn run_tui(man_db: ManDb) -> Result<()> {
7684
enable_raw_mode()?;
@@ -129,10 +137,13 @@ pub async fn run_tui(man_db: ManDb) -> Result<()> {
129137

130138
// Handle Ctrl combinations first
131139
if let KeyEvent {
132-
code: KeyCode::Char('c'),
133-
modifiers: KeyModifiers::CONTROL,
134-
..
135-
} = key { break }
140+
code: KeyCode::Char('c'),
141+
modifiers: KeyModifiers::CONTROL,
142+
..
143+
} = key
144+
{
145+
break;
146+
}
136147

137148
match key.code {
138149
KeyCode::Char('q') => break,
@@ -149,6 +160,20 @@ pub async fn run_tui(man_db: ManDb) -> Result<()> {
149160
}
150161
_ => handle_key(&mut app, key).await,
151162
}
163+
164+
match key {
165+
KeyEvent {
166+
code: KeyCode::Home,
167+
modifiers: KeyModifiers::CONTROL,
168+
..
169+
} => scroll_to_top(&mut app), // Исправлено: добавлено &mut
170+
KeyEvent {
171+
code: KeyCode::End,
172+
modifiers: KeyModifiers::CONTROL,
173+
..
174+
} => scroll_to_bottom(&mut app), // Исправлено: добавлено &mut
175+
_ => {}
176+
}
152177
}
153178
}
154179

@@ -224,6 +249,18 @@ async fn handle_command_list_keys(app: &mut AppState, key: KeyEvent) {
224249
app.last_input_time = Instant::now();
225250
}
226251
}
252+
KeyCode::Home if commands_len > 0 => {
253+
app.command_list.selected_idx = 0;
254+
update_list_scroll(app);
255+
app.pending_man_load = true;
256+
app.last_input_time = Instant::now();
257+
}
258+
KeyCode::End if commands_len > 0 => {
259+
app.command_list.selected_idx = commands_len - 1;
260+
update_list_scroll(app);
261+
app.pending_man_load = true;
262+
app.last_input_time = Instant::now();
263+
}
227264
KeyCode::PageUp if commands_len > 0 => {
228265
app.command_list.selected_idx = app
229266
.command_list
@@ -254,7 +291,11 @@ fn update_list_scroll(app: &mut AppState) {
254291
let visible_height = app.command_list.visible_range.1 - app.command_list.visible_range.0;
255292
let selected_idx = app.command_list.selected_idx;
256293

257-
if selected_idx < app.command_list.list_scroll {
294+
if selected_idx == 0 {
295+
app.command_list.list_scroll = 0;
296+
} else if selected_idx == app.command_list.filtered_commands.len() - 1 {
297+
app.command_list.list_scroll = selected_idx.saturating_sub(visible_height - 1);
298+
} else if selected_idx < app.command_list.list_scroll {
258299
app.command_list.list_scroll = selected_idx;
259300
} else if selected_idx >= app.command_list.list_scroll + visible_height {
260301
app.command_list.list_scroll = selected_idx - visible_height + 1;
@@ -304,8 +345,16 @@ async fn load_current_page(app: &mut AppState) {
304345

305346
fn handle_man_page_keys(app: &mut AppState, key: KeyEvent) {
306347
match key.code {
348+
KeyCode::Char('f') => {
349+
app.focus = Focus::Search;
350+
app.search.query.clear();
351+
}
307352
KeyCode::Up => app.man_page.scroll = app.man_page.scroll.saturating_sub(1),
308353
KeyCode::Down => app.man_page.scroll = app.man_page.scroll.saturating_add(1),
354+
KeyCode::Home => app.man_page.scroll = 0,
355+
KeyCode::End => {
356+
app.man_page.scroll = app.man_page.content.len().saturating_sub(PAGE_SIZE)
357+
}
309358
KeyCode::PageUp => app.man_page.scroll = app.man_page.scroll.saturating_sub(PAGE_SIZE),
310359
KeyCode::PageDown => {
311360
app.man_page.scroll = (app.man_page.scroll + PAGE_SIZE)
@@ -319,6 +368,8 @@ fn handle_man_page_keys(app: &mut AppState, key: KeyEvent) {
319368

320369
fn handle_search_keys(app: &mut AppState, key: KeyEvent) {
321370
match key.code {
371+
KeyCode::Char('j') => next_search_match(app),
372+
KeyCode::Char('k') => prev_search_match(app),
322373
KeyCode::Enter => {
323374
update_search_matches(app);
324375
app.focus = Focus::ManPage;
@@ -396,7 +447,7 @@ fn render_ui<B: tui::backend::Backend>(f: &mut tui::Frame<B>, app: &mut AppState
396447
Constraint::Length(3),
397448
Constraint::Min(10),
398449
]
399-
.as_ref(),
450+
.as_ref(),
400451
)
401452
.split(f.size());
402453

@@ -412,18 +463,19 @@ fn render_status_bar<B: tui::backend::Backend>(f: &mut tui::Frame<B>, app: &AppS
412463
};
413464

414465
let status = if app.loading {
415-
format!("Loading {source_label}...")
466+
format!("Loading {}...", source_label)
416467
} else {
417468
let x = &*format!(
418-
"RTFM // {source_label} PAGE [Tab:Switch /:Search n/N:Next/Prev t:Toggle]"
469+
"RTFM // {} PAGE [Tab:Switch /:Search t:Toggle Home/End]",
470+
source_label
419471
);
420472
match app.focus {
421-
Focus::CommandList => "RTFM // COMMAND LIST [Tab:Switch]",
473+
Focus::CommandList => "RTFM // COMMAND LIST [Tab:Switch Home/End]",
422474
Focus::ManPage => x,
423475
Focus::Search => "RTFM // SEARCH MODE [Enter:Apply Esc:Cancel]",
424476
}
425-
.parse()
426-
.unwrap()
477+
.parse()
478+
.unwrap()
427479
};
428480

429481
let status_bar = Paragraph::new(status)
@@ -503,7 +555,7 @@ fn render_command_list_items<B: tui::backend::Backend>(
503555
.iter()
504556
.map(|cmd| {
505557
let prefix = { " " };
506-
ListItem::new(format!("{prefix}{cmd}"))
558+
ListItem::new(format!("{}{}", prefix, cmd))
507559
})
508560
.collect();
509561

@@ -656,4 +708,4 @@ fn syntax_highlight(line: &str) -> Vec<Span> {
656708
}
657709

658710
spans
659-
}
711+
}

0 commit comments

Comments
 (0)