feat: implement priority parsing from task string

This commit is contained in:
xeruf 2024-11-09 14:40:18 +01:00
parent dd78a2f460
commit f33d890d7f
3 changed files with 84 additions and 17 deletions

View File

@ -82,6 +82,46 @@ as you work.
The currently active task is automatically time-tracked.
To stop time-tracking completely, simply move to the root of all tasks.
Time-tracking by default recursively summarizes
### Priorities
Task priorities can be set as any natural number,
with higher numbers denoting higher priorities.
The syntax here allows for very convenient incremental usage:
By default, using priorities between 1 and 9 is recommended,
with an exemplary interpretation like this:
* 1 Ideas / "Someday"
* 2 Later
* 3 Soon
* 4 Relevant
* 5 Important
* 9 DO NOW
Internally, when giving a single digit, a 0 is appended,
so that the default priorities increment in steps of 10.
So in case you need more than 10 priorities,
instead of stacking them on top,
you can granularly add them in between.
For example, `12` is in between `1` and `2`
which are equivalent to `10` and `20`,
not above `9` but above `09`!
By default, only tasks with priority `35` and upward are shown
so you can focus on what matters,
but you can temporarily override that using `**PRIO`.
### Quick Access
Paper-based lists are often popular because you can quickly put down a bunch of items.
Mostr offers three useful workflows depending on the use-case:
If you want to TBC...
- temporary task with subtasks (especially handy for progression)
- Filter by recently created
- Pin to bookmarks
- high priority
## Reference

View File

@ -1,10 +1,11 @@
use crate::task::{State, MARKER_PARENT};
use crate::tasks::HIGH_PRIO;
use itertools::Itertools;
use log::info;
use nostr_sdk::TagStandard::Hashtag;
use nostr_sdk::{Alphabet, EventBuilder, EventId, Kind, Tag, TagStandard};
use std::collections::HashSet;
use crate::task::{State, MARKER_PARENT};
use nostr_sdk::{Alphabet, EventBuilder, EventId, Kind, Tag, TagKind, TagStandard};
use std::borrow::Cow;
use std::iter::once;
pub const TASK_KIND: Kind = Kind::GitIssue;
pub const PROCEDURE_KIND_ID: u16 = 1639;
@ -25,6 +26,8 @@ pub const PROP_KINDS: [Kind; 6] = [
PROCEDURE_KIND,
];
pub const PRIO: &str = "priority";
// TODO: use formatting - bold / heading / italics - and generate from code
/// Helper for available properties.
pub const PROPERTY_COLUMNS: &str =
@ -95,16 +98,31 @@ pub(crate) fn extract_hashtags(input: &str) -> impl Iterator<Item=Tag> + '_ {
/// as well as various embedded tags.
///
/// Expects sanitized input.
pub(crate) fn extract_tags(input: &str) -> (&str, Vec<Tag>) {
match input.split_once(" # ") {
None => (input, extract_hashtags(input).collect_vec()),
Some((name, tags)) => {
let tags = extract_hashtags(name)
.chain(tags.split_ascii_whitespace().map(to_hashtag))
.collect();
(name, tags)
pub(crate) fn extract_tags(input: &str) -> (String, Vec<Tag>) {
let words = input.split_ascii_whitespace();
let mut prio = None;
let result = words.filter(|s| {
if s.starts_with('*') {
if s.len() == 1 {
prio = Some(HIGH_PRIO);
return false
}
return match s[1..].parse::<u16>() {
Ok(num) => {
prio = Some(num * (if s.len() > 2 { 1 } else { 10 }));
false
},
_ => true,
}
}
true
}).collect_vec();
let mut split = result.split(|e| { e == &"#" });
let main = split.next().unwrap().join(" ");
let tags = extract_hashtags(&main)
.chain(split.flatten().map(|s| to_hashtag(&s)))
.chain(prio.map(|p| to_prio_tag(&p.to_string()))).collect();
(main, tags)
}
fn to_hashtag(tag: &str) -> Tag {
@ -137,9 +155,14 @@ pub(crate) fn is_hashtag(tag: &Tag) -> bool {
.is_some_and(|letter| letter.character == Alphabet::T)
}
pub(crate) fn to_prio_tag(value: &str) -> Tag {
Tag::custom(TagKind::Custom(Cow::from(PRIO)), [if value.len() < 2 { format!("{value}0") } else { value.to_string() }])
}
#[test]
fn test_extract_tags() {
assert_eq!(extract_tags("Hello from #mars with #greetings # yeah done-it"),
("Hello from #mars with #greetings", ["mars", "greetings", "yeah", "done-it"].into_iter().map(to_hashtag).collect()))
assert_eq!(extract_tags("Hello from #mars with #greetings *4 # yeah done-it"),
("Hello from #mars with #greetings".to_string(),
["mars", "greetings", "yeah", "done-it"].into_iter().map(to_hashtag)
.chain(once(Tag::custom(TagKind::Custom(Cow::from(PRIO)), [40.to_string()]))).collect()))
}

View File

@ -19,6 +19,10 @@ use regex::bytes::Regex;
use tokio::sync::mpsc::Sender;
use TagStandard::Hashtag;
const DEFAULT_PRIO: u16 = 25;
pub const HIGH_PRIO: u16 = 85;
/// Amount of seconds to treat as "now"
const MAX_OFFSET: u64 = 9;
fn now() -> Timestamp {
Timestamp::now() + MAX_OFFSET
@ -856,7 +860,7 @@ impl TasksRelay {
pub(crate) fn make_task_with(&mut self, input: &str, tags: impl IntoIterator<Item=Tag>, set_state: bool) -> EventId {
let (input, input_tags) = extract_tags(input.trim());
let id = self.submit(
build_task(input, input_tags, None)
build_task(&input, input_tags, None)
.add_tags(self.tags.iter().cloned())
.add_tags(tags)
);
@ -1060,7 +1064,7 @@ impl TasksRelay {
}
let (input, tags) = extract_tags(note.trim());
self.submit(
build_task(input, tags, Some(("activity", Kind::TextNote)))
build_task(&input, tags, Some(("activity", Kind::TextNote)))
.add_tags(self.parent_tag())
.add_tags(self.tags.iter().cloned())
)