Compare commits

..

1 commit

Author SHA1 Message Date
xeruf
b19f2a85d8 feat: properly format tracked time 2024-07-28 11:37:36 +03:00
3 changed files with 49 additions and 73 deletions

View file

@ -36,13 +36,9 @@ Dots can be repeated to move to parent tasks
- `:[IND][COL]` - add / remove property column COL to IND or end - `:[IND][COL]` - add / remove property column COL to IND or end
- `>[TEXT]` - Complete active task and move to parent, with optional state description - `>[TEXT]` - Complete active task and move to parent, with optional state description
- `<[TEXT]` - Close active task and move to parent, with optional state description - `<[TEXT]` - Close active task and move to parent, with optional state description
- `|TEXT` - Set state for current task from text (also aliased to `/` for now)
- `-TEXT` - add text note (comment / description)
Property Filters:
- `#TAG` - filter by tag - `#TAG` - filter by tag
- `?TAG` - filter by state (type or description) - plain `?` to reset - `?TAG` - filter by state (type or description)
- `-TEXT` - add text note (comment / description)
State descriptions can be used for example for Kanban columns. State descriptions can be used for example for Kanban columns.
An active tag or state filter will also create new tasks with those corresponding attributes. An active tag or state filter will also create new tasks with those corresponding attributes.
@ -58,7 +54,7 @@ An active tag or state filter will also create new tasks with those correspondin
- `path` - name including parent tasks - `path` - name including parent tasks
- `rpath` - name including parent tasks up to active task - `rpath` - name including parent tasks up to active task
- `time` - time tracked - `time` - time tracked
- `rtime` - time tracked including subtasks - `ttime` - time tracked including subtasks
- TBI: `progress` - how many subtasks are complete - TBI: `progress` - how many subtasks are complete
- TBI: `progressp` - subtask completion in percent - TBI: `progressp` - subtask completion in percent

View file

@ -3,6 +3,7 @@ use std::fmt::Display;
use std::fs; use std::fs;
use std::fs::File; use std::fs::File;
use std::io::{BufRead, BufReader, stdin, stdout, Write}; use std::io::{BufRead, BufReader, stdin, stdout, Write};
use std::ops::Deref;
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;
use std::sync::mpsc; use std::sync::mpsc;
@ -87,7 +88,8 @@ async fn main() {
Ok(relay) => { Ok(relay) => {
or_print(client.add_relay(relay).await); or_print(client.add_relay(relay).await);
} }
_ => match File::open(&relayfile).map(|f| BufReader::new(f).lines().flatten()) { _ => {
match File::open(&relayfile).map(|f| BufReader::new(f).lines().flatten()) {
Ok(lines) => { Ok(lines) => {
for line in lines { for line in lines {
or_print(client.add_relay(line).await); or_print(client.add_relay(line).await);
@ -96,19 +98,20 @@ async fn main() {
Err(e) => { Err(e) => {
eprintln!("Could not read relays file: {}", e); eprintln!("Could not read relays file: {}", e);
if let Some(line) = prompt("Relay?") { if let Some(line) = prompt("Relay?") {
let url = if line.contains("://") { let url = if line.contains("://") { line } else { "wss://".to_string() + &line };
line or_print(
} else { client
"wss://".to_string() + &line .add_relay(url.clone())
}; .await,
or_print(client.add_relay(url.clone()).await).map(|bool| { ).map(|bool| {
if bool { if bool {
or_print(fs::write(&relayfile, url)); or_print(fs::write(&relayfile, url));
} }
}); });
}; };
} }
}, }
}
} }
//let proxy = Some(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 9050))); //let proxy = Some(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 9050)));
@ -185,11 +188,7 @@ async fn main() {
loop { loop {
tasks.print_tasks(); tasks.print_tasks();
print!( print!(" {}{}) ", tasks.get_task_path(tasks.get_position()), tasks.get_prompt_suffix());
" {}{}) ",
tasks.get_task_path(tasks.get_position()),
tasks.get_prompt_suffix()
);
stdout().flush().unwrap(); stdout().flush().unwrap();
match lines.next() { match lines.next() {
Some(Ok(input)) => { Some(Ok(input)) => {
@ -207,34 +206,28 @@ async fn main() {
let mut iter = input.chars(); let mut iter = input.chars();
let op = iter.next(); let op = iter.next();
let arg = if input.len() > 1 {
input[1..].trim()
} else {
""
};
match op { match op {
None => {} None => {}
Some(':') => match iter.next().and_then(|s| s.to_digit(10)) { Some(':') => match input[1..2].parse::<usize>() {
Some(digit) => { Ok(index) => {
let index = digit as usize; if input.len() == 2 {
let remaining = iter.collect::<String>().trim().to_string();
if remaining.is_empty() {
tasks.properties.remove(index); tasks.properties.remove(index);
continue; continue;
} }
let value = input[2..].trim().to_string(); let value = input[2..].to_string();
if tasks.properties.get(index) == Some(&value) { if tasks.properties.get(index) == Some(&value) {
tasks.properties.remove(index); tasks.properties.remove(index);
} else { } else {
tasks.properties.insert(index, value); tasks.properties.insert(index, value);
} }
} }
None => { Err(_) => {
let pos = tasks.properties.iter().position(|s| s == arg); let prop = &input[1..];
let pos = tasks.properties.iter().position(|s| s == &prop);
match pos { match pos {
None => { None => {
tasks.properties.push(arg.to_string()); tasks.properties.push(prop.to_string());
} }
Some(i) => { Some(i) => {
tasks.properties.remove(i); tasks.properties.remove(i);
@ -244,35 +237,25 @@ async fn main() {
}, },
Some('?') => { Some('?') => {
let arg = &input[1..];
tasks.set_state_filter(Some(arg.to_string()).filter(|s| !s.is_empty())); tasks.set_state_filter(Some(arg.to_string()).filter(|s| !s.is_empty()));
} }
Some('-') => tasks.add_note(arg), Some('-') => tasks.add_note(&input[1..]),
Some('>') => { Some('>') | Some('<') => {
tasks.update_state(arg, |_| Some(State::Done)); tasks.update_state(&input[1..], |_| {
tasks.move_up(); Some(if op.unwrap() == '<' {
} State::Closed
} else {
Some('<') => { State::Done
tasks.update_state(arg, |_| Some(State::Closed)); })
tasks.move_up(); });
} tasks.move_up()
Some('|') | Some('/') => {
match tasks.get_position() {
None => {
println!("First select a task to set its state!");
}
Some(id) => {
tasks.set_state_for(&id, arg);
tasks.move_to(tasks.get_position());
}
}
} }
Some('#') => { Some('#') => {
tasks.add_tag(arg.to_string()); tasks.add_tag(input[1..].to_string());
} }
Some('.') => { Some('.') => {

View file

@ -164,11 +164,8 @@ impl TaskState {
} }
pub(crate) fn matches_label(&self, label: &str) -> bool { pub(crate) fn matches_label(&self, label: &str) -> bool {
self.state == State::Active self.state == State::Active
|| self || self.name.as_ref().is_some_and(|n| n == label)
.name || self.state.to_string() == label
.as_ref()
.is_some_and(|n| n.eq_ignore_ascii_case(label))
|| self.state.to_string().eq_ignore_ascii_case(label)
} }
} }
impl fmt::Display for TaskState { impl fmt::Display for TaskState {