Merge pull request #171 from kdheepak/speed-up-start-up

This commit is contained in:
Dheepak Krishnamurthy 2021-04-01 21:04:27 -06:00 committed by GitHub
commit 085b22854c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 280 additions and 240 deletions

1
Cargo.lock generated
View file

@ -1384,6 +1384,7 @@ dependencies = [
"futures", "futures",
"futures-timer", "futures-timer",
"itertools", "itertools",
"lazy_static",
"rand", "rand",
"regex", "regex",
"rustyline", "rustyline",

View file

@ -18,29 +18,30 @@ default = ["crossterm-backend"]
crossterm-backend = ["tui/crossterm", "crossterm"] crossterm-backend = ["tui/crossterm", "crossterm"]
[dependencies] [dependencies]
regex = "1"
itertools = "0.9"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
clap = "2.33"
cassowary = "0.3.0"
task-hookrs = { git = "https://github.com/matthiasbeyer/task-hookrs" }
rand = "0.7"
shlex = "0.1"
chrono = "0.4"
unicode-width = "0.1"
unicode-segmentation = "1.6"
tui = { version = "0.12", optional = true, default-features = false }
crossterm = { version = "0.17", optional = true, default-features = false, features = ["event-stream"] }
rustyline = "8"
uuid = { version = "0.8.1", features = ["serde", "v4"] }
better-panic = "0.2.0"
shellexpand = "2.1"
anyhow = "1" anyhow = "1"
async-std = { version = "1", features = ["attributes", "unstable"] } async-std = { version = "1", features = ["attributes", "unstable"] }
better-panic = "0.2.0"
cassowary = "0.3.0"
chrono = "0.4"
clap = "2.33"
crossterm = { version = "0.17", optional = true, default-features = false, features = ["event-stream"] }
dirs = "2.0.2"
futures = "0.3" futures = "0.3"
futures-timer = "3.0" futures-timer = "3.0"
dirs = "2.0.2" itertools = "0.9"
lazy_static = "1.4.0"
rand = "0.7"
regex = "1"
rustyline = "8"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
shellexpand = "2.1"
shlex = "0.1"
task-hookrs = { git = "https://github.com/matthiasbeyer/task-hookrs" }
tui = { version = "0.12", optional = true, default-features = false }
unicode-segmentation = "1.6"
unicode-width = "0.1"
uuid = { version = "0.8.1", features = ["serde", "v4"] }
[package.metadata.rpm] [package.metadata.rpm]
package = "taskwarrior-tui" package = "taskwarrior-tui"

View file

@ -32,14 +32,15 @@ use unicode_segmentation::UnicodeSegmentation;
use chrono::{Datelike, Local, NaiveDate, NaiveDateTime, TimeZone, Timelike}; use chrono::{Datelike, Local, NaiveDate, NaiveDateTime, TimeZone, Timelike};
use anyhow::Context as AnyhowContext;
use anyhow::Result; use anyhow::Result;
use async_std::prelude::*; use async_std::prelude::*;
use async_std::stream::StreamExt; use async_std::stream::StreamExt;
use async_std::task; use async_std::task;
use futures::future::join_all; use futures::future::join_all;
use futures::join;
use futures::stream::FuturesOrdered; use futures::stream::FuturesOrdered;
use futures::{join, try_join};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@ -66,8 +67,16 @@ use tui::{backend::CrosstermBackend, Terminal};
use regex::Regex; use regex::Regex;
use lazy_static::lazy_static;
use std::time::Instant;
const MAX_LINE: usize = 4096; const MAX_LINE: usize = 4096;
lazy_static! {
static ref START_TIME: Instant = Instant::now();
}
pub fn cmp(t1: &Task, t2: &Task) -> Ordering { pub fn cmp(t1: &Task, t2: &Task) -> Ordering {
let urgency1 = match t1.urgency() { let urgency1 = match t1.urgency() {
Some(f) => *f, Some(f) => *f,
@ -189,10 +198,31 @@ pub struct TaskwarriorTuiApp {
impl TaskwarriorTuiApp { impl TaskwarriorTuiApp {
pub fn new() -> Result<Self> { pub fn new() -> Result<Self> {
let c = Config::default()?; let output = std::process::Command::new("task")
let mut kc = KeyConfig::default(); .arg("rc.color=off")
kc.update()?; .arg("show")
.output()
.context("Unable to run `task show`.")
.unwrap();
if !output.status.success() {
let output = Command::new("task")
.arg("diagnostics")
.output()
.context("Unable to run `task diagnostics`.")
.unwrap();
panic!(
"Unable to run `task show`.\n{}\n{}\nPlease check your configuration or open a issue on github.",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr),
);
}
let data = String::from_utf8_lossy(&output.stdout);
let (c, kc) = task::block_on(async { try_join!(Config::new(&data), KeyConfig::new(&data)) })?;
let (w, h) = crossterm::terminal::size()?; let (w, h) = crossterm::terminal::size()?;
let mut app = Self { let mut app = Self {
should_quit: false, should_quit: false,
dirty: true, dirty: true,
@ -213,7 +243,7 @@ impl TaskwarriorTuiApp {
task_details_scroll: 0, task_details_scroll: 0,
task_report_show_info: c.uda_task_report_show_info, task_report_show_info: c.uda_task_report_show_info,
config: c, config: c,
task_report_table: TaskReportTable::new()?, task_report_table: TaskReportTable::new(&data)?,
calendar_year: Local::today().year(), calendar_year: Local::today().year(),
help_popup: Help::new(), help_popup: Help::new(),
contexts: vec![], contexts: vec![],
@ -224,6 +254,7 @@ impl TaskwarriorTuiApp {
filter_history_context: HistoryContext::new("filter.history"), filter_history_context: HistoryContext::new("filter.history"),
command_history_context: HistoryContext::new("command.history"), command_history_context: HistoryContext::new("command.history"),
}; };
for c in app.config.filter.chars() { for c in app.config.filter.chars() {
app.filter.insert(c, 1); app.filter.insert(c, 1);
} }
@ -874,7 +905,7 @@ impl TaskwarriorTuiApp {
pub fn update(&mut self, force: bool) -> Result<()> { pub fn update(&mut self, force: bool) -> Result<()> {
if force || self.dirty || self.tasks_changed_since(self.last_export)? { if force || self.dirty || self.tasks_changed_since(self.last_export)? {
self.last_export = Some(std::time::SystemTime::now()); self.last_export = Some(std::time::SystemTime::now());
self.task_report_table.export_headers()?; self.task_report_table.export_headers(None)?;
let _ = self.export_tasks(); let _ = self.export_tasks();
self.export_contexts()?; self.export_contexts()?;
self.update_tags(); self.update_tags();
@ -2323,6 +2354,14 @@ mod tests {
std::fs::remove_dir_all(cd).unwrap(); std::fs::remove_dir_all(cd).unwrap();
} }
#[test]
fn test_taskwarrior_timing() {
let app = TaskwarriorTuiApp::new();
if app.is_err() {
return;
}
}
#[test] #[test]
fn test_taskwarrior_tui() { fn test_taskwarrior_tui() {
let app = TaskwarriorTuiApp::new(); let app = TaskwarriorTuiApp::new();

View file

@ -1,9 +1,9 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use async_std::process::Command;
use async_std::task; use async_std::task;
use futures::join; use futures::join;
use std::collections::HashMap; use std::collections::HashMap;
use std::error::Error; use std::error::Error;
use std::process::Command;
use std::str; use std::str;
use tui::style::{Color, Modifier, Style}; use tui::style::{Color, Modifier, Style};
@ -64,34 +64,34 @@ pub struct Config {
} }
impl Config { impl Config {
pub fn default() -> Result<Self> { pub async fn new(data: &str) -> Result<Self> {
let bool_collection = Self::get_bool_collection(); let bool_collection = Self::get_bool_collection();
let enabled = true; let enabled = true;
let obfuscate = bool_collection.get("obfuscate").cloned().unwrap_or(false); let obfuscate = bool_collection.get("obfuscate").cloned().unwrap_or(false);
let print_empty_columns = bool_collection.get("print_empty_columns").cloned().unwrap_or(false); let print_empty_columns = bool_collection.get("print_empty_columns").cloned().unwrap_or(false);
let color = Self::get_color_collection(); let color = Self::get_color_collection(data);
let filter = Self::get_filter(); let filter = Self::get_filter(data);
let data_location = Self::get_data_location(); let data_location = Self::get_data_location(data);
let due = Self::get_due(); let due = Self::get_due(data);
let rule_precedence_color = Self::get_rule_precedence_color(); let rule_precedence_color = Self::get_rule_precedence_color(data);
let uda_tick_rate = Self::get_uda_tick_rate(); let uda_tick_rate = Self::get_uda_tick_rate(data);
let uda_prefill_task_metadata = Self::get_uda_prefill_task_metadata(); let uda_prefill_task_metadata = Self::get_uda_prefill_task_metadata(data);
let uda_task_detail_prefetch = Self::get_uda_task_detail_prefetch(); let uda_task_detail_prefetch = Self::get_uda_task_detail_prefetch(data);
let uda_task_report_show_info = Self::get_uda_task_report_show_info(); let uda_task_report_show_info = Self::get_uda_task_report_show_info(data);
let uda_task_report_looping = Self::get_uda_task_report_looping(); let uda_task_report_looping = Self::get_uda_task_report_looping(data);
let uda_selection_indicator = Self::get_uda_selection_indicator(); let uda_selection_indicator = Self::get_uda_selection_indicator(data);
let uda_mark_indicator = Self::get_uda_mark_indicator(); let uda_mark_indicator = Self::get_uda_mark_indicator(data);
let uda_unmark_indicator = Self::get_uda_unmark_indicator(); let uda_unmark_indicator = Self::get_uda_unmark_indicator(data);
let uda_selection_bold = Self::get_uda_selection_bold(); let uda_selection_bold = Self::get_uda_selection_bold(data);
let uda_selection_italic = Self::get_uda_selection_italic(); let uda_selection_italic = Self::get_uda_selection_italic(data);
let uda_selection_dim = Self::get_uda_selection_dim(); let uda_selection_dim = Self::get_uda_selection_dim(data);
let uda_selection_blink = Self::get_uda_selection_blink(); let uda_selection_blink = Self::get_uda_selection_blink(data);
let uda_calendar_months_per_row = Self::get_uda_months_per_row(); let uda_calendar_months_per_row = Self::get_uda_months_per_row(data);
let uda_style_calendar_title = Self::get_uda_style("calendar.title"); let uda_style_calendar_title = Self::get_uda_style("calendar.title", data);
let uda_style_context_active = Self::get_uda_style("context.active"); let uda_style_context_active = Self::get_uda_style("context.active", data);
let uda_shortcuts = Self::get_uda_shortcuts(); let uda_shortcuts = Self::get_uda_shortcuts(data);
let ( let (
color, color,
@ -115,31 +115,29 @@ impl Config {
uda_style_calendar_title, uda_style_calendar_title,
uda_style_context_active, uda_style_context_active,
uda_shortcuts, uda_shortcuts,
) = task::block_on(async { ) = join!(
join!( color,
color, filter,
filter, data_location,
data_location, due,
due, rule_precedence_color,
rule_precedence_color, uda_tick_rate,
uda_tick_rate, uda_prefill_task_metadata,
uda_prefill_task_metadata, uda_task_detail_prefetch,
uda_task_detail_prefetch, uda_task_report_show_info,
uda_task_report_show_info, uda_task_report_looping,
uda_task_report_looping, uda_selection_indicator,
uda_selection_indicator, uda_mark_indicator,
uda_mark_indicator, uda_unmark_indicator,
uda_unmark_indicator, uda_selection_bold,
uda_selection_bold, uda_selection_italic,
uda_selection_italic, uda_selection_dim,
uda_selection_dim, uda_selection_blink,
uda_selection_blink, uda_calendar_months_per_row,
uda_calendar_months_per_row, uda_style_calendar_title,
uda_style_calendar_title, uda_style_context_active,
uda_style_context_active, uda_shortcuts,
uda_shortcuts, );
)
});
let color = color?; let color = color?;
let uda_style_calendar_title = uda_style_calendar_title.unwrap_or_default(); let uda_style_calendar_title = uda_style_calendar_title.unwrap_or_default();
@ -177,31 +175,24 @@ impl Config {
HashMap::new() HashMap::new()
} }
async fn get_uda_shortcuts() -> Vec<String> { async fn get_uda_shortcuts(data: &str) -> Vec<String> {
let mut v = vec![]; let mut v = vec![];
for s in 0..=9 { for s in 0..=9 {
let c = format!("uda.taskwarrior-tui.shortcuts.{}", s); let c = format!("uda.taskwarrior-tui.shortcuts.{}", s);
let s = Self::get_config(&c).await.unwrap_or_default(); let s = Self::get_config(&c, data).await.unwrap_or_default();
v.push(s); v.push(s);
} }
v v
} }
async fn get_uda_style(config: &str) -> Option<Style> { async fn get_uda_style(config: &str, data: &str) -> Option<Style> {
let c = format!("uda.taskwarrior-tui.style.{}", config); let c = format!("uda.taskwarrior-tui.style.{}", config);
let s = Self::get_config(&c).await?; let s = Self::get_config(&c, data).await?;
Some(Self::get_tcolor(&s)) Some(Self::get_tcolor(&s))
} }
async fn get_color_collection() -> Result<HashMap<String, Style>> { async fn get_color_collection(data: &str) -> Result<HashMap<String, Style>> {
let mut color_collection = HashMap::new(); let mut color_collection = HashMap::new();
let output = async_std::process::Command::new("task")
.arg("rc.color=off")
.arg("show")
.output()
.await?;
let data = String::from_utf8_lossy(&output.stdout);
for line in data.split('\n') { for line in data.split('\n') {
if line.starts_with("color.") { if line.starts_with("color.") {
let mut i = line.split(' '); let mut i = line.split(' ');
@ -365,32 +356,7 @@ impl Config {
} }
} }
async fn get_config(config: &str) -> Option<String> { async fn get_config(config: &str, data: &str) -> Option<String> {
let output = async_std::process::Command::new("task")
.arg("rc.color=off")
.arg("show")
.arg(config)
.output()
.await
.with_context(|| format!("Unable to run `task show {}`.", config))
.unwrap();
if !output.status.success() {
let output = Command::new("task")
.arg("diagnostics")
.output()
.context("Unable to run `task diagnostics`.")
.unwrap();
panic!(
"Unable to run `task show {}`.\n{}\n{}\nPlease check your configuration or open a issue on github.",
config,
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr),
);
}
let data = String::from_utf8_lossy(&output.stdout);
for line in data.split('\n') { for line in data.split('\n') {
if line.starts_with(config) { if line.starts_with(config) {
return Some(line.trim_start_matches(config).trim_start().trim_end().to_string()); return Some(line.trim_start_matches(config).trim_start().trim_end().to_string());
@ -403,135 +369,135 @@ impl Config {
None None
} }
async fn get_due() -> usize { async fn get_due(data: &str) -> usize {
Self::get_config("due") Self::get_config("due", data)
.await .await
.unwrap_or_default() .unwrap_or_default()
.parse::<usize>() .parse::<usize>()
.unwrap_or(7) .unwrap_or(7)
} }
async fn get_rule_precedence_color() -> Vec<String> { async fn get_rule_precedence_color(data: &str) -> Vec<String> {
let data = Self::get_config("rule.precedence.color") let data = Self::get_config("rule.precedence.color", data)
.await .await
.context("Unable to parse `task show rule.precedence.color`.") .context("Unable to parse `task show rule.precedence.color`.")
.unwrap(); .unwrap();
data.split(',').map(|s| s.to_string()).collect::<Vec<_>>() data.split(',').map(|s| s.to_string()).collect::<Vec<_>>()
} }
async fn get_filter() -> String { async fn get_filter(data: &str) -> String {
let filter = Self::get_config("report.next.filter") let filter = Self::get_config("report.next.filter", data)
.await .await
.context("Unable to parse `task show report.next.filter`.") .context("Unable to parse `task show report.next.filter`.")
.unwrap(); .unwrap();
format!("{} ", filter) format!("{} ", filter)
} }
async fn get_data_location() -> String { async fn get_data_location(data: &str) -> String {
Self::get_config("data.location") Self::get_config("data.location", data)
.await .await
.context("Unable to parse `task show data.location`.") .context("Unable to parse `task show data.location`.")
.unwrap() .unwrap()
} }
async fn get_uda_prefill_task_metadata() -> bool { async fn get_uda_prefill_task_metadata(data: &str) -> bool {
Self::get_config("uda.taskwarrior-tui.task-report.pre-fill-task-meta-data") Self::get_config("uda.taskwarrior-tui.task-report.pre-fill-task-meta-data", data)
.await .await
.unwrap_or_default() .unwrap_or_default()
.get_bool() .get_bool()
.unwrap_or(false) .unwrap_or(false)
} }
async fn get_uda_tick_rate() -> u64 { async fn get_uda_tick_rate(data: &str) -> u64 {
Self::get_config("uda.taskwarrior-tui.tick-rate") Self::get_config("uda.taskwarrior-tui.tick-rate", data)
.await .await
.unwrap_or_default() .unwrap_or_default()
.parse::<u64>() .parse::<u64>()
.unwrap_or(250) .unwrap_or(250)
} }
async fn get_uda_task_detail_prefetch() -> usize { async fn get_uda_task_detail_prefetch(data: &str) -> usize {
Self::get_config("uda.taskwarrior-tui.task-report.task-detail-prefetch") Self::get_config("uda.taskwarrior-tui.task-report.task-detail-prefetch", data)
.await .await
.unwrap_or_default() .unwrap_or_default()
.parse::<usize>() .parse::<usize>()
.unwrap_or(10) .unwrap_or(10)
} }
async fn get_uda_task_report_show_info() -> bool { async fn get_uda_task_report_show_info(data: &str) -> bool {
Self::get_config("uda.taskwarrior-tui.task-report.show-info") Self::get_config("uda.taskwarrior-tui.task-report.show-info", data)
.await .await
.unwrap_or_default() .unwrap_or_default()
.get_bool() .get_bool()
.unwrap_or(true) .unwrap_or(true)
} }
async fn get_uda_task_report_looping() -> bool { async fn get_uda_task_report_looping(data: &str) -> bool {
Self::get_config("uda.taskwarrior-tui.task-report.looping") Self::get_config("uda.taskwarrior-tui.task-report.looping", data)
.await .await
.unwrap_or_default() .unwrap_or_default()
.get_bool() .get_bool()
.unwrap_or(true) .unwrap_or(true)
} }
async fn get_uda_selection_indicator() -> String { async fn get_uda_selection_indicator(data: &str) -> String {
let indicator = Self::get_config("uda.taskwarrior-tui.selection.indicator").await; let indicator = Self::get_config("uda.taskwarrior-tui.selection.indicator", data).await;
match indicator { match indicator {
None => "".to_string(), None => "".to_string(),
Some(indicator) => format!("{} ", indicator), Some(indicator) => format!("{} ", indicator),
} }
} }
async fn get_uda_mark_indicator() -> String { async fn get_uda_mark_indicator(data: &str) -> String {
let indicator = Self::get_config("uda.taskwarrior-tui.mark.indicator").await; let indicator = Self::get_config("uda.taskwarrior-tui.mark.indicator", data).await;
match indicator { match indicator {
None => "".to_string(), None => "".to_string(),
Some(indicator) => format!("{} ", indicator), Some(indicator) => format!("{} ", indicator),
} }
} }
async fn get_uda_unmark_indicator() -> String { async fn get_uda_unmark_indicator(data: &str) -> String {
let indicator = Self::get_config("uda.taskwarrior-tui.unmark.indicator").await; let indicator = Self::get_config("uda.taskwarrior-tui.unmark.indicator", data).await;
match indicator { match indicator {
None => " ".to_string(), None => " ".to_string(),
Some(indicator) => format!("{} ", indicator), Some(indicator) => format!("{} ", indicator),
} }
} }
async fn get_uda_selection_bold() -> bool { async fn get_uda_selection_bold(data: &str) -> bool {
Self::get_config("uda.taskwarrior-tui.selection.bold") Self::get_config("uda.taskwarrior-tui.selection.bold", data)
.await .await
.unwrap_or_default() .unwrap_or_default()
.get_bool() .get_bool()
.unwrap_or(true) .unwrap_or(true)
} }
async fn get_uda_selection_italic() -> bool { async fn get_uda_selection_italic(data: &str) -> bool {
Self::get_config("uda.taskwarrior-tui.selection.italic") Self::get_config("uda.taskwarrior-tui.selection.italic", data)
.await .await
.unwrap_or_default() .unwrap_or_default()
.get_bool() .get_bool()
.unwrap_or(false) .unwrap_or(false)
} }
async fn get_uda_selection_dim() -> bool { async fn get_uda_selection_dim(data: &str) -> bool {
Self::get_config("uda.taskwarrior-tui.selection.dim") Self::get_config("uda.taskwarrior-tui.selection.dim", data)
.await .await
.unwrap_or_default() .unwrap_or_default()
.get_bool() .get_bool()
.unwrap_or(false) .unwrap_or(false)
} }
async fn get_uda_selection_blink() -> bool { async fn get_uda_selection_blink(data: &str) -> bool {
Self::get_config("uda.taskwarrior-tui.selection.blink") Self::get_config("uda.taskwarrior-tui.selection.blink", data)
.await .await
.unwrap_or_default() .unwrap_or_default()
.get_bool() .get_bool()
.unwrap_or(false) .unwrap_or(false)
} }
async fn get_uda_months_per_row() -> usize { async fn get_uda_months_per_row(data: &str) -> usize {
Self::get_config("uda.taskwarrior-tui.calendar.months-per-row") Self::get_config("uda.taskwarrior-tui.calendar.months-per-row", data)
.await .await
.unwrap_or_default() .unwrap_or_default()
.parse::<usize>() .parse::<usize>()
@ -542,13 +508,6 @@ impl Config {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
#[test]
fn test_uda_configuration() {
assert_eq!(
None,
task::block_on(Config::get_config("uda.taskwarrior-tui.unmark.indicator"))
);
}
#[test] #[test]
fn test_colors() { fn test_colors() {

View file

@ -1,11 +1,12 @@
use crate::util::Key; use crate::util::Key;
use anyhow::anyhow; use anyhow::{anyhow, Result};
use anyhow::Result; use async_std::process::Command;
use async_std::task;
use futures::join;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashSet; use std::collections::HashSet;
use std::error::Error; use std::error::Error;
use std::hash::Hash; use std::hash::Hash;
use std::process::Command;
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct KeyConfig { pub struct KeyConfig {
@ -91,76 +92,119 @@ impl Default for KeyConfig {
} }
impl KeyConfig { impl KeyConfig {
pub fn update(&mut self) -> Result<()> { pub async fn new(data: &str) -> Result<Self> {
self.quit = self let mut kc = Self::default();
.get_config("uda.taskwarrior-tui.keyconfig.quit") kc.update(data).await?;
.unwrap_or(self.quit); Ok(kc)
self.refresh = self }
.get_config("uda.taskwarrior-tui.keyconfig.refresh")
.unwrap_or(self.refresh); pub async fn update(&mut self, data: &str) -> Result<()> {
self.go_to_bottom = self let quit = self.get_config("uda.taskwarrior-tui.keyconfig.quit", data);
.get_config("uda.taskwarrior-tui.keyconfig.go-to-bottom") let refresh = self.get_config("uda.taskwarrior-tui.keyconfig.refresh", data);
.unwrap_or(self.go_to_bottom); let go_to_bottom = self.get_config("uda.taskwarrior-tui.keyconfig.go-to-bottom", data);
self.go_to_top = self let go_to_top = self.get_config("uda.taskwarrior-tui.keyconfig.go-to-top", data);
.get_config("uda.taskwarrior-tui.keyconfig.go-to-top") let down = self.get_config("uda.taskwarrior-tui.keyconfig.down", data);
.unwrap_or(self.go_to_top); let up = self.get_config("uda.taskwarrior-tui.keyconfig.up", data);
self.down = self let page_down = self.get_config("uda.taskwarrior-tui.keyconfig.page-down", data);
.get_config("uda.taskwarrior-tui.keyconfig.down") let page_up = self.get_config("uda.taskwarrior-tui.keyconfig.page-up", data);
.unwrap_or(self.down); let delete = self.get_config("uda.taskwarrior-tui.keyconfig.delete", data);
self.up = self.get_config("uda.taskwarrior-tui.keyconfig.up").unwrap_or(self.up); let done = self.get_config("uda.taskwarrior-tui.keyconfig.done", data);
self.page_down = self let start_stop = self.get_config("uda.taskwarrior-tui.keyconfig.start-stop", data);
.get_config("uda.taskwarrior-tui.keyconfig.page-down") let select = self.get_config("uda.taskwarrior-tui.keyconfig.select", data);
.unwrap_or(self.page_down); let select_all = self.get_config("uda.taskwarrior-tui.keyconfig.select-all", data);
self.page_up = self let undo = self.get_config("uda.taskwarrior-tui.keyconfig.undo", data);
.get_config("uda.taskwarrior-tui.keyconfig.page-up") let edit = self.get_config("uda.taskwarrior-tui.keyconfig.edit", data);
.unwrap_or(self.page_up); let modify = self.get_config("uda.taskwarrior-tui.keyconfig.modify", data);
self.delete = self let shell = self.get_config("uda.taskwarrior-tui.keyconfig.shell", data);
.get_config("uda.taskwarrior-tui.keyconfig.delete") let log = self.get_config("uda.taskwarrior-tui.keyconfig.log", data);
.unwrap_or(self.delete); let add = self.get_config("uda.taskwarrior-tui.keyconfig.add", data);
self.done = self let annotate = self.get_config("uda.taskwarrior-tui.keyconfig.annotate", data);
.get_config("uda.taskwarrior-tui.keyconfig.done") let filter = self.get_config("uda.taskwarrior-tui.keyconfig.filter", data);
.unwrap_or(self.done); let zoom = self.get_config("uda.taskwarrior-tui.keyconfig.zoom", data);
self.start_stop = self let context_menu = self.get_config("uda.taskwarrior-tui.keyconfig.context-menu", data);
.get_config("uda.taskwarrior-tui.keyconfig.start-stop") let next_tab = self.get_config("uda.taskwarrior-tui.keyconfig.next-tab", data);
.unwrap_or(self.start_stop); let previous_tab = self.get_config("uda.taskwarrior-tui.keyconfig.previous-tab", data);
self.select = self
.get_config("uda.taskwarrior-tui.keyconfig.select") let (
.unwrap_or(self.select); quit,
self.select_all = self refresh,
.get_config("uda.taskwarrior-tui.keyconfig.select-all") go_to_bottom,
.unwrap_or(self.select_all); go_to_top,
self.undo = self down,
.get_config("uda.taskwarrior-tui.keyconfig.undo") up,
.unwrap_or(self.undo); page_down,
self.edit = self page_up,
.get_config("uda.taskwarrior-tui.keyconfig.edit") delete,
.unwrap_or(self.edit); done,
self.modify = self start_stop,
.get_config("uda.taskwarrior-tui.keyconfig.modify") select,
.unwrap_or(self.modify); select_all,
self.shell = self undo,
.get_config("uda.taskwarrior-tui.keyconfig.shell") edit,
.unwrap_or(self.shell); modify,
self.log = self.get_config("uda.taskwarrior-tui.keyconfig.log").unwrap_or(self.log); shell,
self.add = self.get_config("uda.taskwarrior-tui.keyconfig.add").unwrap_or(self.add); log,
self.annotate = self add,
.get_config("uda.taskwarrior-tui.keyconfig.annotate") annotate,
.unwrap_or(self.annotate); filter,
self.filter = self zoom,
.get_config("uda.taskwarrior-tui.keyconfig.filter") context_menu,
.unwrap_or(self.filter); next_tab,
self.zoom = self previous_tab,
.get_config("uda.taskwarrior-tui.keyconfig.zoom") ) = join!(
.unwrap_or(self.zoom); quit,
self.context_menu = self refresh,
.get_config("uda.taskwarrior-tui.keyconfig.context-menu") go_to_bottom,
.unwrap_or(self.context_menu); go_to_top,
self.next_tab = self down,
.get_config("uda.taskwarrior-tui.keyconfig.next-tab") up,
.unwrap_or(self.next_tab); page_down,
self.previous_tab = self page_up,
.get_config("uda.taskwarrior-tui.keyconfig.previous-tab") delete,
.unwrap_or(self.previous_tab); done,
start_stop,
select,
select_all,
undo,
edit,
modify,
shell,
log,
add,
annotate,
filter,
zoom,
context_menu,
next_tab,
previous_tab,
);
self.quit = quit.unwrap_or(self.quit);
self.refresh = refresh.unwrap_or(self.refresh);
self.go_to_bottom = go_to_bottom.unwrap_or(self.go_to_bottom);
self.go_to_top = go_to_top.unwrap_or(self.go_to_top);
self.down = down.unwrap_or(self.down);
self.up = up.unwrap_or(self.up);
self.page_down = page_down.unwrap_or(self.page_down);
self.page_up = page_up.unwrap_or(self.page_up);
self.delete = delete.unwrap_or(self.delete);
self.done = done.unwrap_or(self.done);
self.start_stop = start_stop.unwrap_or(self.start_stop);
self.select = select.unwrap_or(self.select);
self.select_all = select_all.unwrap_or(self.select_all);
self.undo = undo.unwrap_or(self.undo);
self.edit = edit.unwrap_or(self.edit);
self.modify = modify.unwrap_or(self.modify);
self.shell = shell.unwrap_or(self.shell);
self.log = log.unwrap_or(self.log);
self.add = add.unwrap_or(self.add);
self.annotate = annotate.unwrap_or(self.annotate);
self.filter = filter.unwrap_or(self.filter);
self.zoom = zoom.unwrap_or(self.zoom);
self.context_menu = context_menu.unwrap_or(self.context_menu);
self.next_tab = next_tab.unwrap_or(self.next_tab);
self.previous_tab = previous_tab.unwrap_or(self.previous_tab);
self.check() self.check()
} }
@ -202,16 +246,7 @@ impl KeyConfig {
} }
} }
fn get_config(&mut self, config: &str) -> Option<Key> { async fn get_config(&self, config: &str, data: &str) -> Option<Key> {
let output = Command::new("task")
.arg("rc.color=off")
.arg("show")
.arg(config)
.output()
.expect("Unable to run `task show`");
let data = String::from_utf8_lossy(&output.stdout);
for line in data.split('\n') { for line in data.split('\n') {
if line.starts_with(config) { if line.starts_with(config) {
let line = line.trim_start_matches(config).trim_start().trim_end().to_string(); let line = line.trim_start_matches(config).trim_start().trim_end().to_string();

View file

@ -43,7 +43,7 @@ pub struct TaskReportTable {
} }
impl TaskReportTable { impl TaskReportTable {
pub fn new() -> Result<Self> { pub fn new(data: &str) -> Result<Self> {
let virtual_tags = vec![ let virtual_tags = vec![
"PROJECT", "PROJECT",
"BLOCKED", "BLOCKED",
@ -86,16 +86,21 @@ impl TaskReportTable {
virtual_tags: virtual_tags.iter().map(|s| s.to_string()).collect::<Vec<_>>(), virtual_tags: virtual_tags.iter().map(|s| s.to_string()).collect::<Vec<_>>(),
description_width: 100, description_width: 100,
}; };
task_report_table.export_headers()?; task_report_table.export_headers(Some(data))?;
Ok(task_report_table) Ok(task_report_table)
} }
pub fn export_headers(&mut self) -> Result<()> { pub fn export_headers(&mut self, data: Option<&str>) -> Result<()> {
self.columns = vec![]; self.columns = vec![];
self.labels = vec![]; self.labels = vec![];
let output = Command::new("task").arg("show").arg("report.next.columns").output()?; let data = match data {
let data = String::from_utf8_lossy(&output.stdout); Some(s) => s.to_string(),
None => {
let output = Command::new("task").arg("show").arg("report.next.columns").output()?;
String::from_utf8_lossy(&output.stdout).into_owned()
}
};
for line in data.split('\n') { for line in data.split('\n') {
if line.starts_with("report.next.columns") { if line.starts_with("report.next.columns") {