mirror of
https://github.com/rust-lang/rustlings.git
synced 2024-12-25 23:10:30 +00:00
Compare commits
8 commits
385cc7b58c
...
a8afae7cce
Author | SHA1 | Date | |
---|---|---|---|
|
a8afae7cce | ||
|
eff2ce8a23 | ||
|
fd33c29b26 | ||
|
f49164e69b | ||
|
9bc7bbe4b4 | ||
|
46ad25f925 | ||
|
e59c65cf45 | ||
|
1c27aeead9 |
13 changed files with 161 additions and 42 deletions
36
Cargo.lock
generated
36
Cargo.lock
generated
|
@ -4,9 +4,9 @@ version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstream"
|
name = "anstream"
|
||||||
version = "0.6.17"
|
version = "0.6.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338"
|
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"anstyle-parse",
|
"anstyle-parse",
|
||||||
|
@ -19,9 +19,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle"
|
name = "anstyle"
|
||||||
version = "1.0.9"
|
version = "1.0.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56"
|
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-parse"
|
name = "anstyle-parse"
|
||||||
|
@ -53,9 +53,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.91"
|
version = "1.0.93"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8"
|
checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
|
@ -170,9 +170,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "2.1.1"
|
version = "2.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
|
checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "filetime"
|
name = "filetime"
|
||||||
|
@ -197,9 +197,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.15.0"
|
version = "0.15.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
|
checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
|
@ -286,9 +286,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.161"
|
version = "0.2.162"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
|
checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libredox"
|
name = "libredox"
|
||||||
|
@ -438,9 +438,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.38"
|
version = "0.38.40"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a"
|
checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
"errno",
|
"errno",
|
||||||
|
@ -581,9 +581,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.85"
|
version = "2.0.87"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
|
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -592,9 +592,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.13.0"
|
version = "3.14.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b"
|
checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"fastrand",
|
"fastrand",
|
||||||
|
|
|
@ -46,7 +46,7 @@ include = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.91"
|
anyhow = "1.0.93"
|
||||||
clap = { version = "4.5.20", features = ["derive"] }
|
clap = { version = "4.5.20", features = ["derive"] }
|
||||||
crossterm = { version = "0.28.1", default-features = false, features = ["windows", "events"] }
|
crossterm = { version = "0.28.1", default-features = false, features = ["windows", "events"] }
|
||||||
notify = "7.0.0"
|
notify = "7.0.0"
|
||||||
|
@ -60,7 +60,7 @@ toml_edit.workspace = true
|
||||||
rustix = { version = "0.38.38", default-features = false, features = ["std", "stdio", "termios"] }
|
rustix = { version = "0.38.38", default-features = false, features = ["std", "stdio", "termios"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3.13.0"
|
tempfile = "3.14.0"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
|
|
|
@ -26,6 +26,8 @@ bin = [
|
||||||
{ name = "functions4_sol", path = "../solutions/02_functions/functions4.rs" },
|
{ name = "functions4_sol", path = "../solutions/02_functions/functions4.rs" },
|
||||||
{ name = "functions5", path = "../exercises/02_functions/functions5.rs" },
|
{ name = "functions5", path = "../exercises/02_functions/functions5.rs" },
|
||||||
{ name = "functions5_sol", path = "../solutions/02_functions/functions5.rs" },
|
{ name = "functions5_sol", path = "../solutions/02_functions/functions5.rs" },
|
||||||
|
{ name = "functions6", path = "../exercises/02_functions/functions6.rs" },
|
||||||
|
{ name = "functions6_sol", path = "../solutions/02_functions/functions6.rs" },
|
||||||
{ name = "if1", path = "../exercises/03_if/if1.rs" },
|
{ name = "if1", path = "../exercises/03_if/if1.rs" },
|
||||||
{ name = "if1_sol", path = "../solutions/03_if/if1.rs" },
|
{ name = "if1_sol", path = "../solutions/03_if/if1.rs" },
|
||||||
{ name = "if2", path = "../exercises/03_if/if2.rs" },
|
{ name = "if2", path = "../exercises/03_if/if2.rs" },
|
||||||
|
@ -60,6 +62,8 @@ bin = [
|
||||||
{ name = "move_semantics4_sol", path = "../solutions/06_move_semantics/move_semantics4.rs" },
|
{ name = "move_semantics4_sol", path = "../solutions/06_move_semantics/move_semantics4.rs" },
|
||||||
{ name = "move_semantics5", path = "../exercises/06_move_semantics/move_semantics5.rs" },
|
{ name = "move_semantics5", path = "../exercises/06_move_semantics/move_semantics5.rs" },
|
||||||
{ name = "move_semantics5_sol", path = "../solutions/06_move_semantics/move_semantics5.rs" },
|
{ name = "move_semantics5_sol", path = "../solutions/06_move_semantics/move_semantics5.rs" },
|
||||||
|
{ name = "move_semantics6", path = "../exercises/06_move_semantics/move_semantics6.rs" },
|
||||||
|
{ name = "move_semantics6_sol", path = "../solutions/06_move_semantics/move_semantics6.rs" },
|
||||||
{ name = "structs1", path = "../exercises/07_structs/structs1.rs" },
|
{ name = "structs1", path = "../exercises/07_structs/structs1.rs" },
|
||||||
{ name = "structs1_sol", path = "../solutions/07_structs/structs1.rs" },
|
{ name = "structs1_sol", path = "../solutions/07_structs/structs1.rs" },
|
||||||
{ name = "structs2", path = "../exercises/07_structs/structs2.rs" },
|
{ name = "structs2", path = "../exercises/07_structs/structs2.rs" },
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
# Functions
|
# Functions
|
||||||
|
|
||||||
Here, you'll learn how to write functions and how the Rust compiler can help you debug errors even
|
Here, you'll learn how to write functions and how the Rust compiler can help you debug errors even
|
||||||
in more complex code.
|
in more complex code. You will also learn what is the difference with closures.
|
||||||
|
|
||||||
## Further information
|
## Further information
|
||||||
|
|
||||||
- [How Functions Work](https://doc.rust-lang.org/book/ch03-03-how-functions-work.html)
|
- [How Functions Work](https://doc.rust-lang.org/book/ch03-03-how-functions-work.html)
|
||||||
|
- [Closures](https://doc.rust-lang.org/book/ch13-01-closures.html)
|
||||||
|
|
19
exercises/02_functions/functions6.rs
Normal file
19
exercises/02_functions/functions6.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
// functions6.rs
|
||||||
|
//
|
||||||
|
// Here you can practice special functions called `closures`, that can capture
|
||||||
|
// variables of their parent context.
|
||||||
|
// Fix the code below to make it compile, without changing the two closure
|
||||||
|
// definitions.
|
||||||
|
//
|
||||||
|
// Execute `rustlings hint functions6` or use the `hint` watch subcommand for
|
||||||
|
// some hints.
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// TODO: ensure the definition of captured variable
|
||||||
|
let closure_1 = |input_var: u32| -> u32 {input_var + outer_var};
|
||||||
|
println!("Closure#1 returns {}", closure_1(5));
|
||||||
|
|
||||||
|
let closure_2 = |input_var| println!("Closure#2 (input_var {})", input_var);
|
||||||
|
closure_2(2);
|
||||||
|
closure_2("5"); // TODO: look at the captured variable type here
|
||||||
|
}
|
25
exercises/06_move_semantics/move_semantics6.rs
Normal file
25
exercises/06_move_semantics/move_semantics6.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
// move_semantics6.rs
|
||||||
|
//
|
||||||
|
// Here you will practice how mutable/immutable borrowing works in the context
|
||||||
|
// of a closure.
|
||||||
|
//
|
||||||
|
// Try to fix this code to make it compile and not panic.
|
||||||
|
// You can't change anything except removing 1 line.
|
||||||
|
//
|
||||||
|
// Execute `rustlings hint move_semantics7` or use the `hint` watch subcommand
|
||||||
|
// for a hint.
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut counter = 0;
|
||||||
|
|
||||||
|
let mut increment = || {
|
||||||
|
counter += 1;
|
||||||
|
println!("counter equals {}", counter);
|
||||||
|
};
|
||||||
|
|
||||||
|
increment();
|
||||||
|
let _reborrowed_counter = &counter; // TODO: figure out where to put this borrowing instruction
|
||||||
|
increment();
|
||||||
|
|
||||||
|
assert_eq!(counter, 2);
|
||||||
|
}
|
|
@ -3,7 +3,7 @@
|
||||||
| Exercise | Book Chapter |
|
| Exercise | Book Chapter |
|
||||||
| ---------------------- | ------------------- |
|
| ---------------------- | ------------------- |
|
||||||
| variables | §3.1 |
|
| variables | §3.1 |
|
||||||
| functions | §3.3 |
|
| functions | §3.3, §13.1 |
|
||||||
| if | §3.5 |
|
| if | §3.5 |
|
||||||
| primitive_types | §3.2, §4.3 |
|
| primitive_types | §3.2, §4.3 |
|
||||||
| vecs | §8.1 |
|
| vecs | §8.1 |
|
||||||
|
|
|
@ -11,3 +11,6 @@ cargo clippy -- --deny warnings
|
||||||
cargo fmt --all --check
|
cargo fmt --all --check
|
||||||
cargo test --workspace --all-targets
|
cargo test --workspace --all-targets
|
||||||
cargo run -- dev check --require-solutions
|
cargo run -- dev check --require-solutions
|
||||||
|
|
||||||
|
# MSRV
|
||||||
|
cargo +1.80 run -- dev check --require-solutions
|
||||||
|
|
|
@ -187,6 +187,20 @@ There are two solutions:
|
||||||
1. Add the `return` keyword before `num * num;`
|
1. Add the `return` keyword before `num * num;`
|
||||||
2. Remove the semicolon `;` after `num * num`"""
|
2. Remove the semicolon `;` after `num * num`"""
|
||||||
|
|
||||||
|
[[exercises]]
|
||||||
|
name = "functions6"
|
||||||
|
dir = "02_functions"
|
||||||
|
test = false
|
||||||
|
hint = """
|
||||||
|
Hint FIX #1: Closures can capture variables defined in the outer context.
|
||||||
|
|
||||||
|
Hint FIX #2: Closures can infer both input and returned types, when they are not
|
||||||
|
specified in the signature. But the closure cannot be reused with different
|
||||||
|
input types.
|
||||||
|
|
||||||
|
Read more about closures in the rust book dedicated section:
|
||||||
|
https://doc.rust-lang.org/book/ch13-01-closures.html"""
|
||||||
|
|
||||||
# IF
|
# IF
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
|
@ -391,6 +405,18 @@ The first problem is that `get_char` is taking ownership of the string. So
|
||||||
Once you've fixed that, `string_uppercase`'s function signature will also need
|
Once you've fixed that, `string_uppercase`'s function signature will also need
|
||||||
to be adjusted."""
|
to be adjusted."""
|
||||||
|
|
||||||
|
[[exercises]]
|
||||||
|
name = "move_semantics6"
|
||||||
|
dir = "06_move_semantics"
|
||||||
|
test = false
|
||||||
|
hint = """
|
||||||
|
When a closure captures a variable to modify it, it actually borrows that variable
|
||||||
|
as a mutable reference. In this exercise, the closure mutably borrows the `counter`
|
||||||
|
variable, thus, any attempt to borrow `counter` between closure calls leads to an error.
|
||||||
|
|
||||||
|
You cannot immutably borrow a variable if a mutable closure is
|
||||||
|
called later in the scope."""
|
||||||
|
|
||||||
# STRUCTS
|
# STRUCTS
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
|
|
9
solutions/02_functions/functions6.rs
Normal file
9
solutions/02_functions/functions6.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
fn main() {
|
||||||
|
let outer_var = 1;
|
||||||
|
let closure_1 = |input_var: u32| -> u32 { input_var + outer_var };
|
||||||
|
println!("Closure#1 returns {}", closure_1(5));
|
||||||
|
|
||||||
|
let closure_2 = |input_var| println!("Closure#2 (input_var {})", input_var);
|
||||||
|
closure_2(2);
|
||||||
|
closure_2(5);
|
||||||
|
}
|
14
solutions/06_move_semantics/move_semantics6.rs
Normal file
14
solutions/06_move_semantics/move_semantics6.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
fn main() {
|
||||||
|
let mut counter = 0;
|
||||||
|
|
||||||
|
let mut increment = || {
|
||||||
|
counter += 1;
|
||||||
|
println!("counter equals {}", counter);
|
||||||
|
};
|
||||||
|
|
||||||
|
increment();
|
||||||
|
increment();
|
||||||
|
let _reborrowed_counter = &counter;
|
||||||
|
|
||||||
|
assert_eq!(counter, 2);
|
||||||
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
cursor::{MoveTo, MoveToNextLine},
|
cursor::{MoveTo, MoveToNextLine},
|
||||||
style::{Attribute, Color, ResetColor, SetAttribute, SetBackgroundColor, SetForegroundColor},
|
style::{
|
||||||
|
Attribute, Attributes, Color, ResetColor, SetAttribute, SetAttributes, SetForegroundColor,
|
||||||
|
},
|
||||||
terminal::{self, BeginSynchronizedUpdate, Clear, ClearType, EndSynchronizedUpdate},
|
terminal::{self, BeginSynchronizedUpdate, Clear, ClearType, EndSynchronizedUpdate},
|
||||||
QueueableCommand,
|
QueueableCommand,
|
||||||
};
|
};
|
||||||
|
@ -19,6 +21,9 @@ use crate::{
|
||||||
use super::scroll_state::ScrollState;
|
use super::scroll_state::ScrollState;
|
||||||
|
|
||||||
const COL_SPACING: usize = 2;
|
const COL_SPACING: usize = 2;
|
||||||
|
const SELECTED_ROW_ATTRIBUTES: Attributes = Attributes::none()
|
||||||
|
.with(Attribute::Reverse)
|
||||||
|
.with(Attribute::Bold);
|
||||||
|
|
||||||
fn next_ln(stdout: &mut StdoutLock) -> io::Result<()> {
|
fn next_ln(stdout: &mut StdoutLock) -> io::Result<()> {
|
||||||
stdout
|
stdout
|
||||||
|
@ -41,6 +46,7 @@ pub struct ListState<'a> {
|
||||||
app_state: &'a mut AppState,
|
app_state: &'a mut AppState,
|
||||||
scroll_state: ScrollState,
|
scroll_state: ScrollState,
|
||||||
name_col_padding: Vec<u8>,
|
name_col_padding: Vec<u8>,
|
||||||
|
path_col_padding: Vec<u8>,
|
||||||
filter: Filter,
|
filter: Filter,
|
||||||
term_width: u16,
|
term_width: u16,
|
||||||
term_height: u16,
|
term_height: u16,
|
||||||
|
@ -52,13 +58,18 @@ impl<'a> ListState<'a> {
|
||||||
stdout.queue(Clear(ClearType::All))?;
|
stdout.queue(Clear(ClearType::All))?;
|
||||||
|
|
||||||
let name_col_title_len = 4;
|
let name_col_title_len = 4;
|
||||||
let name_col_width = app_state
|
let path_col_title_len = 4;
|
||||||
.exercises()
|
let (name_col_width, path_col_width) = app_state.exercises().iter().fold(
|
||||||
.iter()
|
(name_col_title_len, path_col_title_len),
|
||||||
.map(|exercise| exercise.name.len())
|
|(name_col_width, path_col_width), exercise| {
|
||||||
.max()
|
(
|
||||||
.map_or(name_col_title_len, |max| max.max(name_col_title_len));
|
name_col_width.max(exercise.name.len()),
|
||||||
|
path_col_width.max(exercise.path.len()),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
);
|
||||||
let name_col_padding = vec![b' '; name_col_width + COL_SPACING];
|
let name_col_padding = vec![b' '; name_col_width + COL_SPACING];
|
||||||
|
let path_col_padding = vec![b' '; path_col_width];
|
||||||
|
|
||||||
let filter = Filter::None;
|
let filter = Filter::None;
|
||||||
let n_rows_with_filter = app_state.exercises().len();
|
let n_rows_with_filter = app_state.exercises().len();
|
||||||
|
@ -73,6 +84,7 @@ impl<'a> ListState<'a> {
|
||||||
app_state,
|
app_state,
|
||||||
scroll_state,
|
scroll_state,
|
||||||
name_col_padding,
|
name_col_padding,
|
||||||
|
path_col_padding,
|
||||||
filter,
|
filter,
|
||||||
// Set by `set_term_size`
|
// Set by `set_term_size`
|
||||||
term_width: 0,
|
term_width: 0,
|
||||||
|
@ -105,7 +117,7 @@ impl<'a> ListState<'a> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_exericse_name(&self, writer: &mut MaxLenWriter, exercise: &Exercise) -> io::Result<()> {
|
fn draw_exercise_name(&self, writer: &mut MaxLenWriter, exercise: &Exercise) -> io::Result<()> {
|
||||||
if !self.search_query.is_empty() {
|
if !self.search_query.is_empty() {
|
||||||
if let Some((pre_highlight, highlight, post_highlight)) = exercise
|
if let Some((pre_highlight, highlight, post_highlight)) = exercise
|
||||||
.name
|
.name
|
||||||
|
@ -119,7 +131,7 @@ impl<'a> ListState<'a> {
|
||||||
writer.write_str(pre_highlight)?;
|
writer.write_str(pre_highlight)?;
|
||||||
writer.stdout.queue(SetForegroundColor(Color::Magenta))?;
|
writer.stdout.queue(SetForegroundColor(Color::Magenta))?;
|
||||||
writer.write_str(highlight)?;
|
writer.write_str(highlight)?;
|
||||||
writer.stdout.queue(ResetColor)?;
|
writer.stdout.queue(SetForegroundColor(Color::Reset))?;
|
||||||
return writer.write_str(post_highlight);
|
return writer.write_str(post_highlight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,14 +155,12 @@ impl<'a> ListState<'a> {
|
||||||
let mut writer = MaxLenWriter::new(stdout, self.term_width as usize);
|
let mut writer = MaxLenWriter::new(stdout, self.term_width as usize);
|
||||||
|
|
||||||
if self.scroll_state.selected() == Some(row_offset + n_displayed_rows) {
|
if self.scroll_state.selected() == Some(row_offset + n_displayed_rows) {
|
||||||
writer.stdout.queue(SetBackgroundColor(Color::Rgb {
|
|
||||||
r: 40,
|
|
||||||
g: 40,
|
|
||||||
b: 40,
|
|
||||||
}))?;
|
|
||||||
// The crab emoji has the width of two ascii chars.
|
// The crab emoji has the width of two ascii chars.
|
||||||
writer.add_to_len(2);
|
writer.add_to_len(2);
|
||||||
writer.stdout.write_all("🦀".as_bytes())?;
|
writer.stdout.write_all("🦀".as_bytes())?;
|
||||||
|
writer
|
||||||
|
.stdout
|
||||||
|
.queue(SetAttributes(SELECTED_ROW_ATTRIBUTES))?;
|
||||||
} else {
|
} else {
|
||||||
writer.write_ascii(b" ")?;
|
writer.write_ascii(b" ")?;
|
||||||
}
|
}
|
||||||
|
@ -170,8 +180,9 @@ impl<'a> ListState<'a> {
|
||||||
writer.write_ascii(b"PENDING")?;
|
writer.write_ascii(b"PENDING")?;
|
||||||
}
|
}
|
||||||
writer.stdout.queue(SetForegroundColor(Color::Reset))?;
|
writer.stdout.queue(SetForegroundColor(Color::Reset))?;
|
||||||
|
writer.write_ascii(b" ")?;
|
||||||
|
|
||||||
self.draw_exericse_name(&mut writer, exercise)?;
|
self.draw_exercise_name(&mut writer, exercise)?;
|
||||||
|
|
||||||
writer.write_ascii(&self.name_col_padding[exercise.name.len()..])?;
|
writer.write_ascii(&self.name_col_padding[exercise.name.len()..])?;
|
||||||
|
|
||||||
|
@ -183,6 +194,8 @@ impl<'a> ListState<'a> {
|
||||||
exercise.terminal_file_link(&mut writer)?;
|
exercise.terminal_file_link(&mut writer)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
writer.write_ascii(&self.path_col_padding[exercise.path.len()..])?;
|
||||||
|
|
||||||
next_ln(stdout)?;
|
next_ln(stdout)?;
|
||||||
stdout.queue(ResetColor)?;
|
stdout.queue(ResetColor)?;
|
||||||
n_displayed_rows += 1;
|
n_displayed_rows += 1;
|
||||||
|
|
|
@ -20,6 +20,10 @@ use crate::{
|
||||||
|
|
||||||
use super::{terminal_event::terminal_event_handler, InputPauseGuard, WatchEvent};
|
use super::{terminal_event::terminal_event_handler, InputPauseGuard, WatchEvent};
|
||||||
|
|
||||||
|
const HEADING_ATTRIBUTES: Attributes = Attributes::none()
|
||||||
|
.with(Attribute::Bold)
|
||||||
|
.with(Attribute::Underlined);
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq)]
|
||||||
enum DoneStatus {
|
enum DoneStatus {
|
||||||
DoneWithSolution(String),
|
DoneWithSolution(String),
|
||||||
|
@ -209,9 +213,7 @@ impl<'a> WatchState<'a> {
|
||||||
|
|
||||||
if self.show_hint {
|
if self.show_hint {
|
||||||
stdout
|
stdout
|
||||||
.queue(SetAttributes(
|
.queue(SetAttributes(HEADING_ATTRIBUTES))?
|
||||||
Attributes::from(Attribute::Bold).with(Attribute::Underlined),
|
|
||||||
))?
|
|
||||||
.queue(SetForegroundColor(Color::Cyan))?;
|
.queue(SetForegroundColor(Color::Cyan))?;
|
||||||
stdout.write_all(b"Hint")?;
|
stdout.write_all(b"Hint")?;
|
||||||
stdout.queue(ResetColor)?;
|
stdout.queue(ResetColor)?;
|
||||||
|
@ -267,6 +269,9 @@ impl<'a> WatchState<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_all_exercises(&mut self, stdout: &mut StdoutLock) -> Result<ExercisesProgress> {
|
pub fn check_all_exercises(&mut self, stdout: &mut StdoutLock) -> Result<ExercisesProgress> {
|
||||||
|
// Ignore any input until checking all exercises is done.
|
||||||
|
let _input_pause_guard = InputPauseGuard::scoped_pause();
|
||||||
|
|
||||||
if let Some(first_pending_exercise_ind) = self.app_state.check_all_exercises(stdout)? {
|
if let Some(first_pending_exercise_ind) = self.app_state.check_all_exercises(stdout)? {
|
||||||
// Only change exercise if the current one is done.
|
// Only change exercise if the current one is done.
|
||||||
if self.app_state.current_exercise().done {
|
if self.app_state.current_exercise().done {
|
||||||
|
|
Loading…
Reference in a new issue