Merge pull request #495 from kdheepak/update

feat: Update dependencies 
This commit is contained in:
Dheepak Krishnamurthy 2023-05-26 23:35:56 -04:00 committed by GitHub
commit dee5c0c5f6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 7977 additions and 8104 deletions

View file

@ -110,6 +110,19 @@ jobs:
toolchain: stable
override: true
# - name: Build
# uses: actions-rs/cargo@v1
# with:
# command: build
# args: --release
# - name: Publish
# uses: actions-rs/cargo@v1
# with:
# command: publish
# env:
# CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
homebrew:
name: Bump Homebrew formula
runs-on: macos-latest

View file

@ -1 +1,2 @@
max_width = 120
max_width = 150
tab_spaces = 2

923
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -17,37 +17,37 @@ default = ["crossterm-backend"]
crossterm-backend = ["tui/crossterm", "crossterm"]
[dependencies]
anyhow = "1.0.56"
anyhow = "1.0.71"
better-panic = "0.3.0"
cassowary = "0.3.0"
chrono = "0.4.19"
clap = { version = "3.1.6", features = ["derive"] }
crossterm = { version = "0.25.0", optional = true, default-features = false, features = [
"event-stream"
chrono = "0.4.24"
clap = { version = "4.3.0", features = ["derive"] }
crossterm = { version = "0.26.1", optional = true, default-features = false, features = [
"event-stream",
] }
dirs = "4.0.0"
futures = "0.3.21"
itertools = "0.10.3"
dirs = "5.0.1"
futures = "0.3.28"
itertools = "0.10.5"
lazy_static = "1.4.0"
log = "0.4.14"
log4rs = "1.0.0"
path-clean = "0.1.0"
log = "0.4.17"
log4rs = "1.2.0"
path-clean = "1.0.1"
rand = "0.8.5"
regex = "1.5.5"
rustyline = "10.0.0"
serde = { version = "1.0.136", features = ["derive"] }
serde_json = "1.0.79"
shellexpand = "2.1.0"
regex = "1.8.3"
rustyline = { version = "11.0.0", features = ["with-file-history", "derive"] }
serde = { version = "1.0.163", features = ["derive"] }
serde_json = "1.0.96"
shellexpand = "3.1.0"
shlex = "1.1.0"
task-hookrs = { git = "https://github.com/kdheepak/task-hookrs" }
tokio = { version = "1.17.0", features = ["full"] }
tokio-stream = "0.1.3"
tui = { version = "0.19.0", optional = true, default-features = false }
unicode-segmentation = "1.9.0"
tokio = { version = "1.28.1", features = ["full"] }
tokio-stream = "0.1.14"
tui = { package = "ratatui", version = "0.20.1" }
unicode-segmentation = "1.10.1"
unicode-truncate = "0.2.0"
unicode-width = "0.1.9"
uuid = { version = "0.8.2", features = ["serde", "v4"] }
versions = "4.0.0"
unicode-width = "0.1.10"
uuid = { version = "1.3.3", features = ["serde", "v4"] }
versions = "4.1.0"
[package.metadata.rpm]
package = "taskwarrior-tui"
@ -64,6 +64,6 @@ incremental = true
lto = "off"
[build-dependencies]
clap = { version = "3.1.6", features = ["derive"] }
clap_complete = "3.1.1"
clap = { version = "4.3.0", features = ["derive"] }
clap_complete = "4.3.0"
shlex = "1.1.0"

View file

@ -22,7 +22,6 @@ A Terminal User Interface (TUI) for [Taskwarrior](https://taskwarrior.org/) that
![](https://user-images.githubusercontent.com/1813121/159858280-3ca31e9a-fc38-4547-a92d-36a7758cf5dc.gif)
### Showcase
<details>
@ -125,7 +124,11 @@ uda.taskwarrior-tui.report.next.filter=(status:pending or status:waiting)
### References / Resources
If you like `taskwarrior-tui`, please consider donating to [me](https://github.com/sponsors/kdheepak), [`@GothenburgBitFactory`](https://github.com/sponsors/GothenburgBitFactory) or a charity of your choice.
If you like `taskwarrior-tui`, please consider donating to
- [`kdheepak`](https://github.com/sponsors/kdheepak)
- [`@GothenburgBitFactory`](https://github.com/sponsors/GothenburgBitFactory)
- and/or a charity of your choice.
<details>
<summary>Additional resources</summary>

View file

@ -23,10 +23,10 @@ _taskwarrior-tui() {
'--taskrc=[Sets the .taskrc file using the TASKRC environment variable for taskwarrior]:FILE: ' \
'-r+[Sets default report]:STRING: ' \
'--report=[Sets default report]:STRING: ' \
'-h[Print help information]' \
'--help[Print help information]' \
'-V[Print version information]' \
'--version[Print version information]' \
'-h[Print help]' \
'--help[Print help]' \
'-V[Print version]' \
'--version[Print version]' \
&& ret=0
}
@ -36,4 +36,8 @@ _taskwarrior-tui_commands() {
_describe -t commands 'taskwarrior-tui commands' commands "$@"
}
_taskwarrior-tui "$@"
if [ "$funcstack[1]" = "_taskwarrior-tui" ]; then
_taskwarrior-tui "$@"
else
compdef _taskwarrior-tui taskwarrior-tui
fi

View file

@ -29,10 +29,10 @@ Register-ArgumentCompleter -Native -CommandName 'taskwarrior-tui' -ScriptBlock {
[CompletionResult]::new('--taskrc', 'taskrc', [CompletionResultType]::ParameterName, 'Sets the .taskrc file using the TASKRC environment variable for taskwarrior')
[CompletionResult]::new('-r', 'r', [CompletionResultType]::ParameterName, 'Sets default report')
[CompletionResult]::new('--report', 'report', [CompletionResultType]::ParameterName, 'Sets default report')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help information')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help information')
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Print version information')
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version information')
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
[CompletionResult]::new('-V', 'V', [CompletionResultType]::ParameterName, 'Print version')
[CompletionResult]::new('--version', 'version', [CompletionResultType]::ParameterName, 'Print version')
break
}
})

View file

@ -1,5 +1,5 @@
_taskwarrior-tui() {
local i cur prev opts cmds
local i cur prev opts cmd
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
@ -8,8 +8,8 @@ _taskwarrior-tui() {
for i in ${COMP_WORDS[@]}
do
case "${i}" in
"$1")
case "${cmd},${i}" in
",$1")
cmd="taskwarrior__tui"
;;
*)
@ -19,7 +19,7 @@ _taskwarrior-tui() {
case "${cmd}" in
taskwarrior__tui)
opts="-h -V -d -c -r --help --version --data --config --taskdata --taskrc --report"
opts="-d -c -r -h -V --data --config --taskdata --taskrc --report --help --version"
if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0

View file

@ -3,5 +3,5 @@ complete -c taskwarrior-tui -s c -l config -d 'Sets the config folder for taskwa
complete -c taskwarrior-tui -l taskdata -d 'Sets the .task folder using the TASKDATA environment variable for taskwarrior' -r
complete -c taskwarrior-tui -l taskrc -d 'Sets the .taskrc file using the TASKRC environment variable for taskwarrior' -r
complete -c taskwarrior-tui -s r -l report -d 'Sets default report' -r
complete -c taskwarrior-tui -s h -l help -d 'Print help information'
complete -c taskwarrior-tui -s V -l version -d 'Print version information'
complete -c taskwarrior-tui -s h -l help -d 'Print help'
complete -c taskwarrior-tui -s V -l version -d 'Print version'

2
justfile Normal file
View file

@ -0,0 +1,2 @@
clean:
rm -rf tests/data/.task tests/data/.config

File diff suppressed because it is too large Load diff

View file

@ -5,7 +5,7 @@ use std::fmt;
const COL_WIDTH: usize = 21;
use chrono::{format::Fixed, Date, Datelike, Duration, FixedOffset, Local, Month, NaiveDate, NaiveDateTime, TimeZone};
use chrono::{format::Fixed, DateTime, Datelike, Duration, FixedOffset, Local, Month, NaiveDate, NaiveDateTime, TimeZone};
use tui::{
buffer::Buffer,
@ -24,7 +24,7 @@ pub struct Calendar<'a> {
pub month: u32,
pub style: Style,
pub months_per_row: usize,
pub date_style: Vec<(Date<FixedOffset>, Style)>,
pub date_style: Vec<(NaiveDate, Style)>,
pub today_style: Style,
pub start_on_monday: bool,
pub title_background_color: Color,
@ -32,8 +32,8 @@ pub struct Calendar<'a> {
impl<'a> Default for Calendar<'a> {
fn default() -> Calendar<'a> {
let year = Local::today().year();
let month = Local::today().month();
let year = Local::now().year();
let month = Local::now().month();
Calendar {
block: None,
style: Style::default(),
@ -72,7 +72,7 @@ impl<'a> Calendar<'a> {
self
}
pub fn date_style(mut self, date_style: Vec<(Date<FixedOffset>, Style)>) -> Self {
pub fn date_style(mut self, date_style: Vec<(NaiveDate, Style)>) -> Self {
self.date_style = date_style;
self
}
@ -112,17 +112,17 @@ impl<'a> Widget for Calendar<'a> {
}
let style = self.style;
let today = Local::today();
let today = Local::now();
let year = self.year;
let month = self.month;
let months: Vec<_> = (0..12).collect();
let mut days: Vec<(Date<FixedOffset>, Date<FixedOffset>)> = months
let mut days: Vec<(NaiveDate, NaiveDate)> = months
.iter()
.map(|i| {
let first = Date::from_utc(NaiveDate::from_ymd(year, i + 1, 1), *Local::now().offset());
let first = NaiveDate::from_ymd_opt(year, i + 1, 1).unwrap();
let num_days = if self.start_on_monday {
first.weekday().num_days_from_monday()
} else {
@ -179,7 +179,7 @@ impl<'a> Widget for Calendar<'a> {
} else {
"Su Mo Tu We Th Fr Sa"
};
buf.set_string(x as u16, y, days_string, style.add_modifier(Modifier::UNDERLINED));
buf.set_string(x, y, days_string, style.add_modifier(Modifier::UNDERLINED));
x += 21 + 1;
}
y += 1;
@ -202,13 +202,13 @@ impl<'a> Widget for Calendar<'a> {
if let Some(i) = index {
style = self.date_style[i].1;
}
if d.1 == Local::today() {
if d.1 == Local::now().date_naive() {
buf.set_string(x, y, s, self.today_style);
} else {
buf.set_string(x, y, s, style);
}
x += 3;
d.1 = Date::from_utc(d.1.naive_local() + Duration::days(1), *Local::now().offset());
d.1 += Duration::days(1);
}
moredays |= d.0.month() == d.1.month() || d.1 < d.0;
}
@ -228,24 +228,14 @@ impl<'a> Widget for Calendar<'a> {
&mut months
.iter()
.map(|i| {
let first = Date::from_utc(
NaiveDate::from_ymd(self.year + new_year as i32, i + 1, 1),
*Local::now().offset(),
);
(
first,
first - Duration::days(i64::from(first.weekday().num_days_from_sunday())),
)
let first = NaiveDate::from_ymd_opt(self.year + new_year as i32, i + 1, 1).unwrap();
(first, first - Duration::days(i64::from(first.weekday().num_days_from_sunday())))
})
.collect::<Vec<_>>(),
);
let x = area.x;
let s = format!(
"{year:^width$}",
year = self.year as usize + new_year,
width = area.width as usize
);
let s = format!("{year:^width$}", year = self.year as usize + new_year, width = area.width as usize);
let mut style = Style::default().add_modifier(Modifier::UNDERLINED);
if self.year + new_year as i32 == today.year() {
style = style.add_modifier(Modifier::BOLD);

View file

@ -3,7 +3,7 @@ use clap::Arg;
const APP_VERSION: &str = env!("CARGO_PKG_VERSION");
const APP_NAME: &str = env!("CARGO_PKG_NAME");
pub fn generate_cli_app() -> clap::Command<'static> {
pub fn generate_cli_app() -> clap::Command {
let mut app = clap::Command::new(APP_NAME)
.version(APP_VERSION)
.author("Dheepak Krishnamurthy <@kdheepak>")
@ -14,7 +14,7 @@ pub fn generate_cli_app() -> clap::Command<'static> {
.long("data")
.value_name("FOLDER")
.help("Sets the data folder for taskwarrior-tui")
.takes_value(true),
.action(clap::ArgAction::Set),
)
.arg(
Arg::new("config")
@ -22,21 +22,21 @@ pub fn generate_cli_app() -> clap::Command<'static> {
.long("config")
.value_name("FOLDER")
.help("Sets the config folder for taskwarrior-tui (currently not used)")
.takes_value(true),
.action(clap::ArgAction::Set),
)
.arg(
Arg::new("taskdata")
.long("taskdata")
.value_name("FOLDER")
.help("Sets the .task folder using the TASKDATA environment variable for taskwarrior")
.takes_value(true),
.action(clap::ArgAction::Set),
)
.arg(
Arg::new("taskrc")
.long("taskrc")
.value_name("FILE")
.help("Sets the .taskrc file using the TASKRC environment variable for taskwarrior")
.takes_value(true),
.action(clap::ArgAction::Set),
)
.arg(
Arg::new("report")
@ -44,7 +44,7 @@ pub fn generate_cli_app() -> clap::Command<'static> {
.long("report")
.value_name("STRING")
.help("Sets default report")
.takes_value(true),
.action(clap::ArgAction::Set),
);
app.set_bin_name(APP_NAME);

View file

@ -8,11 +8,11 @@ use tui::{
Terminal,
};
use rustyline::error::ReadlineError;
use rustyline::highlight::{Highlighter, MatchingBracketHighlighter};
use rustyline::hint::Hinter;
use rustyline::line_buffer::LineBuffer;
use rustyline::Context;
use rustyline::{error::ReadlineError, history::FileHistory};
use unicode_segmentation::Graphemes;
use unicode_segmentation::UnicodeSegmentation;
@ -46,10 +46,8 @@ impl TaskwarriorTuiCompletionHelper {
.iter()
.filter_map(|(context, candidate)| {
if context == &self.context
&& (candidate.starts_with(&word[..pos])
|| candidate.to_lowercase().starts_with(&word[..pos].to_lowercase()))
&& (!self.input.contains(candidate)
|| !self.input.to_lowercase().contains(&candidate.to_lowercase()))
&& (candidate.starts_with(&word[..pos]) || candidate.to_lowercase().starts_with(&word[..pos].to_lowercase()))
&& (!self.input.contains(candidate) || !self.input.to_lowercase().contains(&candidate.to_lowercase()))
{
Some((
candidate.clone(), // display
@ -101,11 +99,7 @@ impl CompletionList {
state: ListState::default(),
current: String::new(),
pos: 0,
helper: TaskwarriorTuiCompletionHelper {
candidates,
context,
input,
},
helper: TaskwarriorTuiCompletionHelper { candidates, context, input },
}
}
@ -178,7 +172,7 @@ impl CompletionList {
}
pub fn candidates(&self) -> Vec<Completion> {
let hist = rustyline::history::History::new();
let hist = FileHistory::new();
let ctx = rustyline::Context::new(&hist);
let (pos, candidates) = self.helper.complete(&self.current, self.pos, &ctx).unwrap();
candidates

View file

@ -158,19 +158,16 @@ impl Config {
let uda_background_process = Self::get_uda_background_process(data);
let uda_background_process_period = Self::get_uda_background_process_period(data);
let uda_style_report_selection = uda_style_report_selection.unwrap_or_default();
let uda_style_report_scrollbar =
uda_style_report_scrollbar.unwrap_or_else(|| Style::default().fg(Color::Black));
let uda_style_report_scrollbar = uda_style_report_scrollbar.unwrap_or_else(|| Style::default().fg(Color::Black));
let uda_style_report_scrollbar_area = uda_style_report_scrollbar_area.unwrap_or_default();
let uda_style_calendar_title = uda_style_calendar_title.unwrap_or_default();
let uda_style_calendar_today =
uda_style_calendar_today.unwrap_or_else(|| Style::default().add_modifier(Modifier::BOLD));
let uda_style_calendar_today = uda_style_calendar_today.unwrap_or_else(|| Style::default().add_modifier(Modifier::BOLD));
let uda_style_navbar = uda_style_navbar.unwrap_or_else(|| Style::default().add_modifier(Modifier::REVERSED));
let uda_style_command = uda_style_command.unwrap_or_else(|| Style::default().add_modifier(Modifier::REVERSED));
let uda_style_context_active = uda_style_context_active.unwrap_or_default();
let uda_style_report_completion_pane = uda_style_report_completion_pane
.unwrap_or_else(|| Style::default().fg(Color::Black).bg(Color::Rgb(223, 223, 223)));
let uda_style_report_completion_pane_highlight =
uda_style_report_completion_pane_highlight.unwrap_or(uda_style_report_completion_pane);
let uda_style_report_completion_pane =
uda_style_report_completion_pane.unwrap_or_else(|| Style::default().fg(Color::Black).bg(Color::Rgb(223, 223, 223)));
let uda_style_report_completion_pane_highlight = uda_style_report_completion_pane_highlight.unwrap_or(uda_style_report_completion_pane);
let uda_quick_tag_name = Self::get_uda_quick_tag_name(data);
let uda_task_report_prompt_on_delete = Self::get_uda_task_report_prompt_on_delete(data);
let uda_task_report_prompt_on_done = Self::get_uda_task_report_prompt_on_done(data);
@ -457,10 +454,7 @@ impl Config {
}
fn get_due(data: &str) -> usize {
Self::get_config("due", data)
.unwrap_or_default()
.parse::<usize>()
.unwrap_or(7)
Self::get_config("due", data).unwrap_or_default().parse::<usize>().unwrap_or(7)
}
fn get_weekstart(data: &str) -> bool {
@ -485,10 +479,7 @@ impl Config {
fn get_filter(data: &str, report: &str) -> Result<String> {
if report == "all" {
Ok("".into())
} else if let Some(s) = Self::get_config(
format!("uda.taskwarrior-tui.task-report.{}.filter", report).as_str(),
data,
) {
} else if let Some(s) = Self::get_config(format!("uda.taskwarrior-tui.task-report.{}.filter", report).as_str(), data) {
Ok(s)
} else {
Ok(Self::get_config(format!("report.{}.filter", report).as_str(), data).unwrap_or_default())
@ -509,10 +500,7 @@ impl Config {
}
fn get_uda_auto_insert_double_quotes_on_annotate(data: &str) -> bool {
Self::get_config(
"uda.taskwarrior-tui.task-report.auto-insert-double-quotes-on-annotate",
data,
)
Self::get_config("uda.taskwarrior-tui.task-report.auto-insert-double-quotes-on-annotate", data)
.unwrap_or_default()
.get_bool()
.unwrap_or(true)
@ -644,13 +632,7 @@ impl Config {
let indicator = Self::get_config("uda.taskwarrior-tui.scrollbar.indicator", data);
match indicator {
None => FULL.to_string(),
Some(indicator) => format!(
"{}",
indicator
.chars()
.next()
.unwrap_or_else(|| FULL.to_string().chars().next().unwrap())
),
Some(indicator) => format!("{}", indicator.chars().next().unwrap_or_else(|| FULL.to_string().chars().next().unwrap())),
}
}
@ -660,9 +642,7 @@ impl Config {
None => DOUBLE_VERTICAL.to_string(),
Some(area) => format!(
"{}",
area.chars()
.next()
.unwrap_or_else(|| DOUBLE_VERTICAL.to_string().chars().next().unwrap())
area.chars().next().unwrap_or_else(|| DOUBLE_VERTICAL.to_string().chars().next().unwrap())
),
}
}

View file

@ -5,10 +5,7 @@ use serde::{Deserialize, Serialize};
use tokio::{sync::mpsc, sync::oneshot, task::JoinHandle};
use crossterm::event::{
KeyCode::{
BackTab, Backspace, Char, Delete, Down, End, Enter, Esc, Home, Insert, Left, Null, PageDown, PageUp, Right,
Tab, Up, F,
},
KeyCode::{BackTab, Backspace, Char, Delete, Down, End, Enter, Esc, Home, Insert, Left, Null, PageDown, PageUp, Right, Tab, Up, F},
KeyModifiers,
};
@ -127,11 +124,6 @@ impl EventLoop {
});
}
Self {
tx,
rx,
tick_rate,
abort,
}
Self { tx, rx, tick_rate, abort }
}
}

View file

@ -1,19 +1,20 @@
use anyhow::{anyhow, Result};
use rustyline::error::ReadlineError;
use rustyline::history::DefaultHistory;
use rustyline::history::History;
use rustyline::history::SearchDirection;
use std::fs::File;
use std::path::{Path, PathBuf};
pub struct HistoryContext {
history: History,
history: DefaultHistory,
history_index: Option<usize>,
data_path: PathBuf,
}
impl HistoryContext {
pub fn new(filename: &str) -> Self {
let history = History::new();
let history = DefaultHistory::new();
let data_path = if let Ok(s) = std::env::var("TASKWARRIOR_TUI_DATA") {
PathBuf::from(s)
@ -23,8 +24,7 @@ impl HistoryContext {
.expect("Unable to create configuration directory for taskwarrior-tui")
};
std::fs::create_dir_all(&data_path)
.unwrap_or_else(|_| panic!("Unable to create configuration directory in {:?}", &data_path));
std::fs::create_dir_all(&data_path).unwrap_or_else(|_| panic!("Unable to create configuration directory in {:?}", &data_path));
let data_path = data_path.join(filename);
@ -51,7 +51,7 @@ impl HistoryContext {
Ok(())
}
pub fn history(&self) -> &History {
pub fn history(&self) -> &DefaultHistory {
&self.history
}
@ -83,9 +83,7 @@ impl HistoryContext {
} else {
let hi = self.history_index().unwrap();
if hi == self.history.len().saturating_sub(1) && dir == SearchDirection::Forward
|| hi == 0 && dir == SearchDirection::Reverse
{
if hi == self.history.len().saturating_sub(1) && dir == SearchDirection::Forward || hi == 0 && dir == SearchDirection::Reverse {
return None;
}
@ -96,14 +94,22 @@ impl HistoryContext {
};
log::debug!("Using history index = {} for searching", history_index);
return if let Some(history_index) = self.history.starts_with(buf, history_index, dir) {
return if let Some(history_index) = self.history.starts_with(buf, history_index, dir).unwrap() {
log::debug!("Found index {:?}", history_index);
log::debug!("Previous index {:?}", self.history_index);
self.history_index = Some(history_index.idx);
Some(history_index.entry.to_string())
} else if buf.is_empty() {
self.history_index = Some(history_index);
self.history.get(history_index).cloned()
Some(
self
.history
.get(history_index, SearchDirection::Forward)
.unwrap()
.unwrap()
.entry
.to_string(),
)
} else {
log::debug!("History index = {}. Found no match.", history_index);
None
@ -111,10 +117,12 @@ impl HistoryContext {
}
pub fn add(&mut self, buf: &str) {
if self.history.add(buf) {
if let Ok(x) = self.history.add(buf) {
if x {
self.reset();
}
}
}
pub fn reset(&mut self) {
self.history_index = None

View file

@ -202,11 +202,7 @@ impl KeyConfig {
return Some(KeyCode::Char(line.chars().next().unwrap()));
}
} else if line.starts_with(&config.replace('-', "_")) {
let line = line
.trim_start_matches(&config.replace('-', "_"))
.trim_start()
.trim_end()
.to_string();
let line = line.trim_start_matches(&config.replace('-', "_")).trim_start().trim_end().to_string();
if line.len() == 1 {
return Some(KeyCode::Char(line.chars().next().unwrap()));
}

View file

@ -18,6 +18,7 @@ mod scrollbar;
mod table;
mod task_report;
mod ui;
mod utils;
use log::{debug, error, info, log_enabled, trace, warn, Level, LevelFilter};
use log4rs::append::file::FileAppender;
@ -73,10 +74,7 @@ pub fn initialize_logging() {
.build(data_local_dir.join("taskwarrior-tui.log"))
.expect("Failed to build log file appender.");
let levelfilter = match std::env::var("TASKWARRIOR_TUI_LOG_LEVEL")
.unwrap_or_else(|_| "info".to_string())
.as_str()
{
let levelfilter = match std::env::var("TASKWARRIOR_TUI_LOG_LEVEL").unwrap_or_else(|_| "info".to_string()).as_str() {
"off" => LevelFilter::Off,
"warn" => LevelFilter::Warn,
"info" => LevelFilter::Info,
@ -128,11 +126,12 @@ fn main() -> Result<()> {
let matches = cli::generate_cli_app().get_matches();
let config = matches.value_of("config");
let data = matches.value_of("data");
let taskrc = matches.value_of("taskrc");
let taskdata = matches.value_of("taskdata");
let report = matches.value_of("report").unwrap_or("next");
let config = matches.get_one::<String>("config");
let data = matches.get_one::<String>("data");
let taskrc = matches.get_one::<String>("taskrc");
let taskdata = matches.get_one::<String>("taskdata");
let binding = String::from("next");
let report = matches.get_one::<String>("report").unwrap_or(&binding);
if let Some(e) = config {
if env::var("TASKWARRIOR_TUI_CONFIG").is_err() {
@ -161,10 +160,7 @@ fn main() -> Result<()> {
if let Some(e) = taskrc {
if env::var("TASKRC").is_err() {
// if environment variable is not set, this env::var returns an error
env::set_var(
"TASKRC",
absolute_path(PathBuf::from(e)).expect("Unable to get path for taskrc"),
)
env::set_var("TASKRC", absolute_path(PathBuf::from(e)).expect("Unable to get path for taskrc"))
} else {
warn!("TASKRC environment variable cannot be set.")
}
@ -173,10 +169,7 @@ fn main() -> Result<()> {
if let Some(e) = taskdata {
if env::var("TASKDATA").is_err() {
// if environment variable is not set, this env::var returns an error
env::set_var(
"TASKDATA",
absolute_path(PathBuf::from(e)).expect("Unable to get path for taskdata"),
)
env::set_var("TASKDATA", absolute_path(PathBuf::from(e)).expect("Unable to get path for taskdata"))
} else {
warn!("TASKDATA environment variable cannot be set.")
}

View file

@ -62,12 +62,7 @@ impl ContextsState {
Self {
table_state: TableState::default(),
report_height: 0,
columns: vec![
NAME.to_string(),
TYPE.to_string(),
DEFINITION.to_string(),
ACTIVE.to_string(),
],
columns: vec![NAME.to_string(), TYPE.to_string(), DEFINITION.to_string(), ACTIVE.to_string()],
rows: vec![],
}
}
@ -116,12 +111,7 @@ impl ContextsState {
let definition = line.replacen(name, "", 1);
let definition = definition.replacen(typ, "", 1);
let definition = definition.strip_suffix(active).unwrap_or_default();
let context = ContextDetails::new(
name.to_string(),
definition.trim().to_string(),
active.to_string(),
typ.to_string(),
);
let context = ContextDetails::new(name.to_string(), definition.trim().to_string(), active.to_string(), typ.to_string());
self.rows.push(context);
}
if self.rows.iter().any(|r| r.active != "no") {
@ -132,12 +122,7 @@ impl ContextsState {
} else {
self.rows.insert(
0,
ContextDetails::new(
"none".to_string(),
"".to_string(),
"yes".to_string(),
"read".to_string(),
),
ContextDetails::new("none".to_string(), "".to_string(), "yes".to_string(), "read".to_string()),
);
}
Ok(())

View file

@ -23,6 +23,7 @@ use crate::app::{Mode, TaskwarriorTui};
use crate::event::KeyCode;
use crate::pane::Pane;
use crate::table::TableState;
use crate::utils::Changeset;
use itertools::Itertools;
use std::cmp::min;
use std::collections::{HashMap, HashSet};
@ -99,14 +100,7 @@ impl ProjectsState {
let rows = self
.rows
.iter()
.map(|c| {
vec![
c.name.clone(),
c.remaining.to_string(),
c.avg_age.to_string(),
c.complete.clone(),
]
})
.map(|c| vec![c.name.clone(), c.remaining.to_string(), c.avg_age.to_string(), c.complete.clone()])
.collect();
let headers = self.columns.clone();
(rows, headers)
@ -114,9 +108,7 @@ impl ProjectsState {
pub fn last_line(&self, line: &str) -> bool {
let words = line.trim().split(' ').map(|s| s.trim()).collect::<Vec<&str>>();
return words.len() == 2
&& words[0].chars().map(|c| c.is_numeric()).all(|b| b)
&& (words[1] == "project" || words[1] == "projects");
return words.len() == 2 && words[0].chars().map(|c| c.is_numeric()).all(|b| b) && (words[1] == "project" || words[1] == "projects");
}
pub fn update_data(&mut self) -> Result<()> {
@ -191,6 +183,6 @@ fn update_task_filter_by_selection(app: &mut TaskwarriorTui) -> Result<()> {
let mut filter = current_filter.replace(&last_project_pattern, "");
filter = format!("{}{}", filter, new_project_pattern);
app.filter.update(filter.as_str(), filter.len());
app.filter.update(filter.as_str(), filter.len(), &mut Changeset::default());
Ok(())
}

View file

@ -335,9 +335,7 @@ where
ccs.push(match *constraint {
Constraint::Length(v) => variables[i] | EQ(MEDIUM) | f64::from(v),
Constraint::Percentage(v) => variables[i] | EQ(WEAK) | (f64::from(v * area.width) / 100.0),
Constraint::Ratio(n, d) => {
variables[i] | EQ(WEAK) | (f64::from(area.width) * f64::from(n) / f64::from(d))
}
Constraint::Ratio(n, d) => variables[i] | EQ(WEAK) | (f64::from(area.width) * f64::from(n) / f64::from(d)),
Constraint::Min(v) => variables[i] | GE(WEAK) | f64::from(v),
Constraint::Max(v) => variables[i] | LE(WEAK) | f64::from(v),
});
@ -448,10 +446,7 @@ where
});
for (i, row) in self.rows.skip(state.offset).take(remaining).enumerate() {
let (data, style, symbol) = match row {
Row::Data(d) | Row::StyledData(d, _)
if Some(i) == state.current_selection().map(|s| s - state.offset) =>
{
match state.mode {
Row::Data(d) | Row::StyledData(d, _) if Some(i) == state.current_selection().map(|s| s - state.offset) => match state.mode {
TableMode::MultipleSelection => {
if state.marked.contains(&(i + state.offset)) {
(d, highlight_style, mark_highlight_symbol.to_string())
@ -460,8 +455,7 @@ where
}
}
TableMode::SingleSelection => (d, highlight_style, highlight_symbol.to_string()),
}
}
},
Row::Data(d) => {
if state.marked.contains(&(i + state.offset)) {
(d, default_style, mark_symbol.to_string())

View file

@ -39,67 +39,37 @@ pub fn vague_format_date_time(from_dt: NaiveDateTime, to_dt: NaiveDateTime, with
if seconds >= 60 * 60 * 24 * 365 {
return if with_remainder {
format!(
"{}{}y{}mo",
minus,
seconds / year,
(seconds - year * (seconds / year)) / month
)
format!("{}{}y{}mo", minus, seconds / year, (seconds - year * (seconds / year)) / month)
} else {
format!("{}{}y", minus, seconds / year)
};
} else if seconds >= 60 * 60 * 24 * 90 {
return if with_remainder {
format!(
"{}{}mo{}w",
minus,
seconds / month,
(seconds - month * (seconds / month)) / week
)
format!("{}{}mo{}w", minus, seconds / month, (seconds - month * (seconds / month)) / week)
} else {
format!("{}{}mo", minus, seconds / month)
};
} else if seconds >= 60 * 60 * 24 * 14 {
return if with_remainder {
format!(
"{}{}w{}d",
minus,
seconds / week,
(seconds - week * (seconds / week)) / day
)
format!("{}{}w{}d", minus, seconds / week, (seconds - week * (seconds / week)) / day)
} else {
format!("{}{}w", minus, seconds / week)
};
} else if seconds >= 60 * 60 * 24 {
return if with_remainder {
format!(
"{}{}d{}h",
minus,
seconds / day,
(seconds - day * (seconds / day)) / hour
)
format!("{}{}d{}h", minus, seconds / day, (seconds - day * (seconds / day)) / hour)
} else {
format!("{}{}d", minus, seconds / day)
};
} else if seconds >= 60 * 60 {
return if with_remainder {
format!(
"{}{}h{}min",
minus,
seconds / hour,
(seconds - hour * (seconds / hour)) / minute
)
format!("{}{}h{}min", minus, seconds / hour, (seconds - hour * (seconds / hour)) / minute)
} else {
format!("{}{}h", minus, seconds / hour)
};
} else if seconds >= 60 {
return if with_remainder {
format!(
"{}{}min{}s",
minus,
seconds / minute,
(seconds - minute * (seconds / minute))
)
format!("{}{}min{}s", minus, seconds / minute, (seconds - minute * (seconds / minute)))
} else {
format!("{}{}min", minus, seconds / minute)
};
@ -396,12 +366,7 @@ impl TaskReportTable {
None => "".to_string(),
},
"tags" => match task.tags() {
Some(v) => v
.iter()
.filter(|t| !self.virtual_tags.contains(t))
.cloned()
.collect::<Vec<_>>()
.join(","),
Some(v) => v.iter().filter(|t| !self.virtual_tags.contains(t)).cloned().collect::<Vec<_>>().join(","),
None => "".to_string(),
},
"recur" => match task.recur() {

19
src/utils.rs Normal file
View file

@ -0,0 +1,19 @@
use rustyline::line_buffer::ChangeListener;
use rustyline::line_buffer::DeleteListener;
use rustyline::line_buffer::Direction;
/// Undo manager
#[derive(Default)]
pub struct Changeset {}
impl DeleteListener for Changeset {
fn delete(&mut self, idx: usize, string: &str, _: Direction) {}
}
impl ChangeListener for Changeset {
fn insert_char(&mut self, idx: usize, c: char) {}
fn insert_str(&mut self, idx: usize, string: &str) {}
fn replace(&mut self, idx: usize, old: &str, new: &str) {}
}