Compare commits

...

12 commits

Author SHA1 Message Date
mo8it 71f31d74bc Update deps 2024-08-17 16:57:58 +02:00
mo8it 72e557b3a9 Break help footer on narrow terminals 2024-08-17 16:54:44 +02:00
mo8it 3eaccbb61a Restore the terminal after an error in the list 2024-08-17 16:49:07 +02:00
mo8it b678bd8ed2 Disable mouse in the list 2024-08-17 16:34:43 +02:00
mo8it 2baa140615 q only quits the list 2024-08-17 15:53:34 +02:00
mo8it e760f07767 Make it clear that reset only resets one exercise 2024-08-17 15:53:24 +02:00
mo8it ca5d5f0a49 Remove dot for copy-pasta 2024-08-17 15:45:02 +02:00
mo8it 69b4fd49fc Only take a u8 to avoid huge output 2024-08-17 14:59:00 +02:00
mo8it 36f315c344 Add "the" 2024-08-17 14:56:52 +02:00
mo8it 8016f5ca2d Remove unneeded comma 2024-08-17 14:55:58 +02:00
mo8it 8ef2ff1257 Remove "Hello and" 2024-08-17 14:54:13 +02:00
mo8it 6ce31defb6 Ignore stdout of git init 2024-08-17 14:40:09 +02:00
10 changed files with 98 additions and 70 deletions

70
Cargo.lock generated
View file

@ -116,9 +116,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.14" version = "4.5.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c937d4061031a6d0c8da4b9a4f98a172fc2976dfb1c19213a9cf7d0d3c837e36" checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -126,9 +126,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.14" version = "4.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85379ba512b21a328adf887e85f7742d12e96eb31f3ef077df4ffc26b506ffed" checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -197,7 +197,7 @@ checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
dependencies = [ dependencies = [
"bitflags 2.6.0", "bitflags 2.6.0",
"crossterm_winapi", "crossterm_winapi",
"mio 1.0.1", "mio 1.0.2",
"parking_lot", "parking_lot",
"rustix", "rustix",
"signal-hook", "signal-hook",
@ -244,14 +244,14 @@ checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
[[package]] [[package]]
name = "filetime" name = "filetime"
version = "0.2.23" version = "0.2.24"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" checksum = "bf401df4a4e3872c4fe8151134cf483738e74b67fc934d6532c882b3d24a4550"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"redox_syscall 0.4.1", "libredox",
"windows-sys 0.52.0", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
@ -287,9 +287,9 @@ checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.3.0" version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown", "hashbrown",
@ -368,9 +368,20 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.155" version = "0.2.156"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" checksum = "a5f43f184355eefb8d17fc948dbecf6c13be3c141f20d834ae842193a448c72a"
[[package]]
name = "libredox"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
"bitflags 2.6.0",
"libc",
"redox_syscall",
]
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
@ -423,9 +434,9 @@ dependencies = [
[[package]] [[package]]
name = "mio" name = "mio"
version = "1.0.1" version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
dependencies = [ dependencies = [
"hermit-abi", "hermit-abi",
"libc", "libc",
@ -497,7 +508,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"redox_syscall 0.5.3", "redox_syscall",
"smallvec", "smallvec",
"windows-targets 0.52.6", "windows-targets 0.52.6",
] ]
@ -547,15 +558,6 @@ dependencies = [
"unicode-width", "unicode-width",
] ]
[[package]]
name = "redox_syscall"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [
"bitflags 1.3.2",
]
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.5.3" version = "0.5.3"
@ -633,18 +635,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.205" version = "1.0.208"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e33aedb1a7135da52b7c21791455563facbbcc43d0f0f66165b42c21b3dfb150" checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.205" version = "1.0.208"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692d6f5ac90220161d6774db30c662202721e64aed9058d2c394f451261420c1" checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -653,9 +655,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.122" version = "1.0.125"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da" checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr", "memchr",
@ -689,7 +691,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
dependencies = [ dependencies = [
"libc", "libc",
"mio 1.0.1", "mio 1.0.2",
"signal-hook", "signal-hook",
] ]
@ -744,9 +746,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.72" version = "2.0.74"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View file

@ -19,7 +19,7 @@ edition = "2021" # On Update: Update the edition of the `rustfmt` command that c
rust-version = "1.80" rust-version = "1.80"
[workspace.dependencies] [workspace.dependencies]
serde = { version = "1.0.205", features = ["derive"] } serde = { version = "1.0.208", features = ["derive"] }
toml_edit = { version = "0.22.20", default-features = false, features = ["parse", "serde"] } toml_edit = { version = "0.22.20", default-features = false, features = ["parse", "serde"] }
[package] [package]
@ -48,12 +48,12 @@ include = [
[dependencies] [dependencies]
ahash = { version = "0.8.11", default-features = false } ahash = { version = "0.8.11", default-features = false }
anyhow = "1.0.86" anyhow = "1.0.86"
clap = { version = "4.5.14", features = ["derive"] } clap = { version = "4.5.16", features = ["derive"] }
notify-debouncer-mini = { version = "0.4.1", default-features = false } notify-debouncer-mini = { version = "0.4.1", default-features = false }
os_pipe = "1.2.1" os_pipe = "1.2.1"
ratatui = { version = "0.28.0", default-features = false, features = ["crossterm"] } ratatui = { version = "0.28.0", default-features = false, features = ["crossterm"] }
rustlings-macros = { path = "rustlings-macros", version = "=6.2.0" } rustlings-macros = { path = "rustlings-macros", version = "=6.2.0" }
serde_json = "1.0.122" serde_json = "1.0.125"
serde.workspace = true serde.workspace = true
toml_edit.workspace = true toml_edit.workspace = true

View file

@ -1,4 +1,4 @@
// TODO: We sometimes encourage you to keep trying things on a given exercise, // TODO: We sometimes encourage you to keep trying things on a given exercise
// even after you already figured it out. If you got everything working and feel // even after you already figured it out. If you got everything working and feel
// ready for the next exercise, enter `n` in the terminal. // ready for the next exercise, enter `n` in the terminal.
// //
@ -6,8 +6,7 @@
// Try adding a new `println!` and check the updated output in the terminal. // Try adding a new `println!` and check the updated output in the terminal.
fn main() { fn main() {
println!("Hello and"); println!(r#" Welcome to... "#);
println!(r#" welcome to... "#);
println!(r#" _ _ _ "#); println!(r#" _ _ _ "#);
println!(r#" _ __ _ _ ___| |_| (_)_ __ __ _ ___ "#); println!(r#" _ __ _ _ ___| |_| (_)_ __ __ _ ___ "#);
println!(r#" | '__| | | / __| __| | | '_ \ / _` / __| "#); println!(r#" | '__| | | / __| __| | | '_ \ / _` / __| "#);

View file

@ -1,5 +1,5 @@
fn main() { fn main() {
// TODO: Add missing keyword. // TODO: Add the missing keyword.
x = 5; x = 5;
println!("x has the value {x}"); println!("x has the value {x}");

View file

@ -1,4 +1,4 @@
fn call_me(num: u32) { fn call_me(num: u8) {
for i in 0..num { for i in 0..num {
println!("Ring! Call number {}", i + 1); println!("Ring! Call number {}", i + 1);
} }

View file

@ -6,7 +6,7 @@
// of `Option<String>`. // of `Option<String>`.
fn generate_nametag_text(name: String) -> Option<String> { fn generate_nametag_text(name: String) -> Option<String> {
if name.is_empty() { if name.is_empty() {
// Empty names aren't allowed. // Empty names aren't allowed
None None
} else { } else {
Some(format!("Hi! My name is {name}")) Some(format!("Hi! My name is {name}"))

View file

@ -1,4 +1,4 @@
fn call_me(num: u32) { fn call_me(num: u8) {
for i in 0..num { for i in 0..num {
println!("Ring! Call number {}", i + 1); println!("Ring! Call number {}", i + 1);
} }

View file

@ -139,13 +139,14 @@ pub fn init() -> Result<()> {
let _ = Command::new("git") let _ = Command::new("git")
.arg("init") .arg("init")
.stdin(Stdio::null()) .stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null()) .stderr(Stdio::null())
.status(); .status();
} }
writeln!( writeln!(
stdout, stdout,
"\n{}\n\n{}", "{}\n\n{}",
"Initialization done ✓".green(), "Initialization done ✓".green(),
POST_INIT_MSG.bold(), POST_INIT_MSG.bold(),
)?; )?;

View file

@ -1,14 +1,14 @@
use anyhow::Result; use anyhow::{Context, Result};
use ratatui::{ use ratatui::{
backend::CrosstermBackend, backend::CrosstermBackend,
crossterm::{ crossterm::{
event::{self, Event, KeyCode, KeyEventKind}, event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind},
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
ExecutableCommand, QueueableCommand,
}, },
Terminal, Terminal,
}; };
use std::io; use std::io::{self, StdoutLock, Write};
use crate::app_state::AppState; use crate::app_state::AppState;
@ -16,12 +16,8 @@ use self::state::{Filter, UiState};
mod state; mod state;
pub fn list(app_state: &mut AppState) -> Result<()> { fn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> {
let mut stdout = io::stdout().lock(); let mut terminal = Terminal::new(CrosstermBackend::new(stdout))?;
stdout.execute(EnterAlternateScreen)?;
enable_raw_mode()?;
let mut terminal = Terminal::new(CrosstermBackend::new(&mut stdout))?;
terminal.clear()?; terminal.clear()?;
let mut ui_state = UiState::new(app_state); let mut ui_state = UiState::new(app_state);
@ -30,7 +26,7 @@ pub fn list(app_state: &mut AppState) -> Result<()> {
terminal.try_draw(|frame| ui_state.draw(frame).map_err(io::Error::other))?; terminal.try_draw(|frame| ui_state.draw(frame).map_err(io::Error::other))?;
let key = loop { let key = loop {
match event::read()? { match event::read().context("Failed to read terminal event")? {
Event::Key(key) => match key.kind { Event::Key(key) => match key.kind {
KeyEventKind::Press | KeyEventKind::Repeat => break key, KeyEventKind::Press | KeyEventKind::Repeat => break key,
KeyEventKind::Release => (), KeyEventKind::Release => (),
@ -85,9 +81,25 @@ pub fn list(app_state: &mut AppState) -> Result<()> {
} }
} }
drop(terminal);
stdout.execute(LeaveAlternateScreen)?;
disable_raw_mode()?;
Ok(()) Ok(())
} }
pub fn list(app_state: &mut AppState) -> Result<()> {
let mut stdout = io::stdout().lock();
stdout
.queue(EnterAlternateScreen)?
.queue(EnableMouseCapture)?
.flush()?;
enable_raw_mode()?;
let res = handle_list(app_state, &mut stdout);
// Restore the terminal even if we got an error.
stdout
.queue(LeaveAlternateScreen)?
.queue(DisableMouseCapture)?
.flush()?;
disable_raw_mode()?;
res
}

View file

@ -2,11 +2,11 @@ use anyhow::{Context, Result};
use ratatui::{ use ratatui::{
layout::{Constraint, Rect}, layout::{Constraint, Rect},
style::{Style, Stylize}, style::{Style, Stylize},
text::{Line, Span}, text::{Span, Text},
widgets::{Block, Borders, HighlightSpacing, Paragraph, Row, Table, TableState}, widgets::{Block, Borders, HighlightSpacing, Paragraph, Row, Table, TableState},
Frame, Frame,
}; };
use std::fmt::Write; use std::{fmt::Write, mem};
use crate::{app_state::AppState, progress_bar::progress_bar_ratatui}; use crate::{app_state::AppState, progress_bar::progress_bar_ratatui};
@ -162,6 +162,9 @@ impl<'a> UiState<'a> {
pub fn draw(&mut self, frame: &mut Frame) -> Result<()> { pub fn draw(&mut self, frame: &mut Frame) -> Result<()> {
let area = frame.area(); let area = frame.area();
let narrow = area.width < 95;
let narrow_u16 = u16::from(narrow);
let table_height = area.height - 3 - narrow_u16;
frame.render_stateful_widget( frame.render_stateful_widget(
&self.table, &self.table,
@ -169,7 +172,7 @@ impl<'a> UiState<'a> {
x: 0, x: 0,
y: 0, y: 0,
width: area.width, width: area.width,
height: area.height - 3, height: table_height,
}, },
&mut self.table_state, &mut self.table_state,
); );
@ -183,7 +186,7 @@ impl<'a> UiState<'a> {
.block(Block::default().borders(Borders::BOTTOM)), .block(Block::default().borders(Borders::BOTTOM)),
Rect { Rect {
x: 0, x: 0,
y: area.height - 3, y: table_height,
width: area.width, width: area.width,
height: 2, height: 2,
}, },
@ -191,10 +194,19 @@ impl<'a> UiState<'a> {
let message = if self.message.is_empty() { let message = if self.message.is_empty() {
// Help footer. // Help footer.
let mut text = Text::default();
let mut spans = Vec::with_capacity(4); let mut spans = Vec::with_capacity(4);
spans.push(Span::raw( spans.push(Span::raw(
"↓/j ↑/k home/g end/G │ <c>ontinue at │ <r>eset │ filter ", "↓/j ↑/k home/g end/G │ <c>ontinue at │ <r>eset exercise │",
)); ));
if narrow {
text.push_line(mem::take(&mut spans));
spans.push(Span::raw("filter "));
} else {
spans.push(Span::raw(" filter "));
}
match self.filter { match self.filter {
Filter::Done => { Filter::Done => {
spans.push("<d>one".underlined().magenta()); spans.push("<d>one".underlined().magenta());
@ -206,18 +218,20 @@ impl<'a> UiState<'a> {
} }
Filter::None => spans.push(Span::raw("<d>one/<p>ending")), Filter::None => spans.push(Span::raw("<d>one/<p>ending")),
} }
spans.push(Span::raw(" │ <q>uit"));
Line::from(spans) spans.push(Span::raw(" │ <q>uit list"));
text.push_line(spans);
text
} else { } else {
Line::from(self.message.as_str().light_blue()) Text::from(self.message.as_str().light_blue())
}; };
frame.render_widget( frame.render_widget(
message, message,
Rect { Rect {
x: 0, x: 0,
y: area.height - 1, y: table_height + 2,
width: area.width, width: area.width,
height: 1, height: 1 + narrow_u16,
}, },
); );