mirror of
https://github.com/kdheepak/taskwarrior-tui.git
synced 2025-08-25 08:47:18 +02:00
fix: Fix multiple line context bug 🐛
This commit is contained in:
parent
7a5393860e
commit
279b63874b
6 changed files with 232 additions and 166 deletions
197
src/app.rs
197
src/app.rs
|
@ -2,7 +2,6 @@ use crate::calendar::Calendar;
|
||||||
use crate::completion::{get_start_word_under_cursor, CompletionList};
|
use crate::completion::{get_start_word_under_cursor, CompletionList};
|
||||||
use crate::config;
|
use crate::config;
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::context::Context;
|
|
||||||
use crate::event::Key;
|
use crate::event::Key;
|
||||||
use crate::event::{Event, Events};
|
use crate::event::{Event, Events};
|
||||||
use crate::help::Help;
|
use crate::help::Help;
|
||||||
|
@ -70,6 +69,7 @@ use regex::Regex;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
use crate::app::Action::Report;
|
use crate::app::Action::Report;
|
||||||
|
use crate::pane::context::{ContextDetails, ContextsState};
|
||||||
use crate::pane::project::ProjectsState;
|
use crate::pane::project::ProjectsState;
|
||||||
use crate::pane::Pane;
|
use crate::pane::Pane;
|
||||||
use crossterm::style::style;
|
use crossterm::style::style;
|
||||||
|
@ -170,7 +170,6 @@ pub struct TaskwarriorTui {
|
||||||
pub should_quit: bool,
|
pub should_quit: bool,
|
||||||
pub dirty: bool,
|
pub dirty: bool,
|
||||||
pub task_table_state: TableState,
|
pub task_table_state: TableState,
|
||||||
pub context_table_state: TableState,
|
|
||||||
pub current_context_filter: String,
|
pub current_context_filter: String,
|
||||||
pub current_context: String,
|
pub current_context: String,
|
||||||
pub command: LineBuffer,
|
pub command: LineBuffer,
|
||||||
|
@ -192,18 +191,18 @@ pub struct TaskwarriorTui {
|
||||||
pub task_report_height: u16,
|
pub task_report_height: u16,
|
||||||
pub task_details_scroll: u16,
|
pub task_details_scroll: u16,
|
||||||
pub help_popup: Help,
|
pub help_popup: Help,
|
||||||
pub contexts: Vec<Context>,
|
|
||||||
pub last_export: Option<SystemTime>,
|
pub last_export: Option<SystemTime>,
|
||||||
pub keyconfig: KeyConfig,
|
pub keyconfig: KeyConfig,
|
||||||
pub terminal_width: u16,
|
pub terminal_width: u16,
|
||||||
pub terminal_height: u16,
|
pub terminal_height: u16,
|
||||||
pub filter_history_context: HistoryContext,
|
pub filter_history: HistoryContext,
|
||||||
pub command_history_context: HistoryContext,
|
pub command_history: HistoryContext,
|
||||||
pub history_status: Option<String>,
|
pub history_status: Option<String>,
|
||||||
pub completion_list: CompletionList,
|
pub completion_list: CompletionList,
|
||||||
pub show_completion_pane: bool,
|
pub show_completion_pane: bool,
|
||||||
pub report: String,
|
pub report: String,
|
||||||
pub projects: ProjectsState,
|
pub projects: ProjectsState,
|
||||||
|
pub contexts: ContextsState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TaskwarriorTui {
|
impl TaskwarriorTui {
|
||||||
|
@ -238,7 +237,6 @@ impl TaskwarriorTui {
|
||||||
should_quit: false,
|
should_quit: false,
|
||||||
dirty: true,
|
dirty: true,
|
||||||
task_table_state: TableState::default(),
|
task_table_state: TableState::default(),
|
||||||
context_table_state: TableState::default(),
|
|
||||||
tasks: vec![],
|
tasks: vec![],
|
||||||
task_details: HashMap::new(),
|
task_details: HashMap::new(),
|
||||||
marked: HashSet::new(),
|
marked: HashSet::new(),
|
||||||
|
@ -259,18 +257,18 @@ impl TaskwarriorTui {
|
||||||
task_report_table: TaskReportTable::new(&data, report)?,
|
task_report_table: TaskReportTable::new(&data, report)?,
|
||||||
calendar_year: Local::today().year(),
|
calendar_year: Local::today().year(),
|
||||||
help_popup: Help::new(),
|
help_popup: Help::new(),
|
||||||
contexts: vec![],
|
|
||||||
last_export: None,
|
last_export: None,
|
||||||
keyconfig: kc,
|
keyconfig: kc,
|
||||||
terminal_width: w,
|
terminal_width: w,
|
||||||
terminal_height: h,
|
terminal_height: h,
|
||||||
filter_history_context: HistoryContext::new("filter.history"),
|
filter_history: HistoryContext::new("filter.history"),
|
||||||
command_history_context: HistoryContext::new("command.history"),
|
command_history: HistoryContext::new("command.history"),
|
||||||
history_status: None,
|
history_status: None,
|
||||||
completion_list: CompletionList::with_items(vec![]),
|
completion_list: CompletionList::with_items(vec![]),
|
||||||
show_completion_pane: false,
|
show_completion_pane: false,
|
||||||
report: report.to_string(),
|
report: report.to_string(),
|
||||||
projects: ProjectsState::new(),
|
projects: ProjectsState::new(),
|
||||||
|
contexts: ContextsState::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
for c in app.config.filter.chars() {
|
for c in app.config.filter.chars() {
|
||||||
|
@ -281,9 +279,9 @@ impl TaskwarriorTui {
|
||||||
|
|
||||||
app.update(true)?;
|
app.update(true)?;
|
||||||
|
|
||||||
app.filter_history_context.load()?;
|
app.filter_history.load()?;
|
||||||
app.filter_history_context.add(app.filter.as_str());
|
app.filter_history.add(app.filter.as_str());
|
||||||
app.command_history_context.load()?;
|
app.command_history.load()?;
|
||||||
app.task_background();
|
app.task_background();
|
||||||
Ok(app)
|
Ok(app)
|
||||||
}
|
}
|
||||||
|
@ -831,17 +829,17 @@ impl TaskwarriorTui {
|
||||||
let maximum_column_width = area.width;
|
let maximum_column_width = area.width;
|
||||||
let widths = self.calculate_widths(&contexts, &headers, maximum_column_width);
|
let widths = self.calculate_widths(&contexts, &headers, maximum_column_width);
|
||||||
|
|
||||||
let selected = self.context_table_state.current_selection().unwrap_or_default();
|
let selected = self.contexts.table_state.current_selection().unwrap_or_default();
|
||||||
let header = headers.iter();
|
let header = headers.iter();
|
||||||
let mut rows = vec![];
|
let mut rows = vec![];
|
||||||
let mut highlight_style = Style::default();
|
let mut highlight_style = Style::default();
|
||||||
for (i, context) in contexts.iter().enumerate() {
|
for (i, context) in contexts.iter().enumerate() {
|
||||||
let mut style = Style::default();
|
let mut style = Style::default();
|
||||||
if &self.contexts[i].active == "yes" {
|
if &self.contexts.rows[i].active == "yes" {
|
||||||
style = self.config.uda_style_context_active;
|
style = self.config.uda_style_context_active;
|
||||||
}
|
}
|
||||||
rows.push(Row::StyledData(context.iter(), style));
|
rows.push(Row::StyledData(context.iter(), style));
|
||||||
if i == self.context_table_state.current_selection().unwrap_or_default() {
|
if i == self.contexts.table_state.current_selection().unwrap_or_default() {
|
||||||
highlight_style = style;
|
highlight_style = style;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -874,7 +872,7 @@ impl TaskwarriorTui {
|
||||||
.highlight_symbol(&self.config.uda_selection_indicator)
|
.highlight_symbol(&self.config.uda_selection_indicator)
|
||||||
.widths(&constraints);
|
.widths(&constraints);
|
||||||
|
|
||||||
f.render_stateful_widget(t, area, &mut self.context_table_state);
|
f.render_stateful_widget(t, area, &mut self.contexts.table_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_completion_pop_up(&mut self, f: &mut Frame<impl Backend>, rect: Rect, cursor_position: usize) {
|
fn draw_completion_pop_up(&mut self, f: &mut Frame<impl Backend>, rect: Rect, cursor_position: usize) {
|
||||||
|
@ -1221,11 +1219,12 @@ impl TaskwarriorTui {
|
||||||
pub fn get_all_contexts(&self) -> (Vec<Vec<String>>, Vec<String>) {
|
pub fn get_all_contexts(&self) -> (Vec<Vec<String>>, Vec<String>) {
|
||||||
let contexts = self
|
let contexts = self
|
||||||
.contexts
|
.contexts
|
||||||
|
.rows
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|c| &c.typ == "read")
|
.filter(|c| &c.type_ == "read")
|
||||||
.map(|c| vec![c.name.clone(), c.description.clone(), c.active.clone()])
|
.map(|c| vec![c.name.clone(), c.definition.clone(), c.active.clone()])
|
||||||
.collect();
|
.collect();
|
||||||
let headers = vec!["Name".to_string(), "Description".to_string(), "Active".to_string()];
|
let headers = vec!["Name".to_string(), "Definition".to_string(), "Active".to_string()];
|
||||||
(contexts, headers)
|
(contexts, headers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1240,7 +1239,8 @@ impl TaskwarriorTui {
|
||||||
self.last_export = Some(std::time::SystemTime::now());
|
self.last_export = Some(std::time::SystemTime::now());
|
||||||
self.task_report_table.export_headers(None, &self.report)?;
|
self.task_report_table.export_headers(None, &self.report)?;
|
||||||
self.export_tasks()?;
|
self.export_tasks()?;
|
||||||
self.export_contexts()?;
|
self.projects.update_data()?;
|
||||||
|
self.contexts.update_data()?;
|
||||||
self.update_tags();
|
self.update_tags();
|
||||||
self.task_details.clear();
|
self.task_details.clear();
|
||||||
self.dirty = false;
|
self.dirty = false;
|
||||||
|
@ -1248,7 +1248,6 @@ impl TaskwarriorTui {
|
||||||
}
|
}
|
||||||
self.cursor_fix();
|
self.cursor_fix();
|
||||||
self.update_task_table_state();
|
self.update_task_table_state();
|
||||||
self.projects.update_data()?;
|
|
||||||
if self.task_report_show_info {
|
if self.task_report_show_info {
|
||||||
task::block_on(self.update_task_details())?;
|
task::block_on(self.update_task_details())?;
|
||||||
}
|
}
|
||||||
|
@ -1278,8 +1277,8 @@ impl TaskwarriorTui {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save_history(&mut self) -> Result<()> {
|
pub fn save_history(&mut self) -> Result<()> {
|
||||||
self.filter_history_context.write()?;
|
self.filter_history.write()?;
|
||||||
self.command_history_context.write()?;
|
self.command_history.write()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1376,7 +1375,7 @@ impl TaskwarriorTui {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn context_next(&mut self) {
|
pub fn context_next(&mut self) {
|
||||||
let i = match self.context_table_state.current_selection() {
|
let i = match self.contexts.table_state.current_selection() {
|
||||||
Some(i) => {
|
Some(i) => {
|
||||||
if i >= self.contexts.len() - 1 {
|
if i >= self.contexts.len() - 1 {
|
||||||
0
|
0
|
||||||
|
@ -1386,11 +1385,11 @@ impl TaskwarriorTui {
|
||||||
}
|
}
|
||||||
None => 0,
|
None => 0,
|
||||||
};
|
};
|
||||||
self.context_table_state.select(Some(i));
|
self.contexts.table_state.select(Some(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn context_previous(&mut self) {
|
pub fn context_previous(&mut self) {
|
||||||
let i = match self.context_table_state.current_selection() {
|
let i = match self.contexts.table_state.current_selection() {
|
||||||
Some(i) => {
|
Some(i) => {
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
self.contexts.len() - 1
|
self.contexts.len() - 1
|
||||||
|
@ -1400,13 +1399,13 @@ impl TaskwarriorTui {
|
||||||
}
|
}
|
||||||
None => 0,
|
None => 0,
|
||||||
};
|
};
|
||||||
self.context_table_state.select(Some(i));
|
self.contexts.table_state.select(Some(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn context_select(&mut self) -> Result<()> {
|
pub fn context_select(&mut self) -> Result<()> {
|
||||||
let i = self.context_table_state.current_selection().unwrap_or_default();
|
let i = self.contexts.table_state.current_selection().unwrap_or_default();
|
||||||
let mut command = Command::new("task");
|
let mut command = Command::new("task");
|
||||||
command.arg("context").arg(&self.contexts[i].name);
|
command.arg("context").arg(&self.contexts.rows[i].name);
|
||||||
command.output()?;
|
command.output()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1530,58 +1529,6 @@ impl TaskwarriorTui {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn export_contexts(&mut self) -> Result<()> {
|
|
||||||
let output = Command::new("task").arg("context").output()?;
|
|
||||||
let data = String::from_utf8_lossy(&output.stdout);
|
|
||||||
|
|
||||||
self.contexts = vec![];
|
|
||||||
|
|
||||||
for (i, line) in data.trim().split('\n').enumerate() {
|
|
||||||
if line.starts_with(" ") && line.trim().starts_with("write") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let line = line.trim();
|
|
||||||
if line.is_empty() || line == "Use 'task context none' to unset the current context." {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if i == 0 || i == 1 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let mut s = line.split_whitespace();
|
|
||||||
let name = s.next().unwrap_or_default();
|
|
||||||
let typ = s.next().unwrap_or_default();
|
|
||||||
let active = s.last().unwrap_or_default();
|
|
||||||
let definition = line.replacen(name, "", 1);
|
|
||||||
let definition = definition.replacen(typ, "", 1);
|
|
||||||
let definition = definition.strip_suffix(active).unwrap_or_default();
|
|
||||||
let context = Context::new(
|
|
||||||
name.to_string(),
|
|
||||||
definition.trim().to_string(),
|
|
||||||
active.to_string(),
|
|
||||||
typ.to_string(),
|
|
||||||
);
|
|
||||||
self.contexts.push(context);
|
|
||||||
}
|
|
||||||
if self.contexts.iter().any(|r| r.active != "no") {
|
|
||||||
self.contexts.insert(
|
|
||||||
0,
|
|
||||||
Context::new("none".to_string(), "".to_string(), "no".to_string(), "read".to_string()),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
self.contexts.insert(
|
|
||||||
0,
|
|
||||||
Context::new(
|
|
||||||
"none".to_string(),
|
|
||||||
"".to_string(),
|
|
||||||
"yes".to_string(),
|
|
||||||
"read".to_string(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_task_files_max_mtime(&self) -> Result<SystemTime> {
|
fn get_task_files_max_mtime(&self) -> Result<SystemTime> {
|
||||||
let data_dir = shellexpand::tilde(&self.config.data_location).into_owned();
|
let data_dir = shellexpand::tilde(&self.config.data_location).into_owned();
|
||||||
["backlog.data", "completed.data", "pending.data"]
|
["backlog.data", "completed.data", "pending.data"]
|
||||||
|
@ -2502,8 +2449,8 @@ impl TaskwarriorTui {
|
||||||
}
|
}
|
||||||
} else if input == self.keyconfig.modify {
|
} else if input == self.keyconfig.modify {
|
||||||
self.mode = Mode::Tasks(Action::Modify);
|
self.mode = Mode::Tasks(Action::Modify);
|
||||||
self.command_history_context.last();
|
self.command_history.last();
|
||||||
// self.history_status = Some(format!(" {} / {}", self.command_history_context.history_index() + 1, self.command_history_context.history_len()));
|
// self.history_status = Some(format!(" {} / {}", self.command_history.history_index() + 1, self.command_history.history_len()));
|
||||||
self.update_completion_list();
|
self.update_completion_list();
|
||||||
match self.task_table_state.mode() {
|
match self.task_table_state.mode() {
|
||||||
TableMode::SingleSelection => match self.task_current() {
|
TableMode::SingleSelection => match self.task_current() {
|
||||||
|
@ -2550,25 +2497,25 @@ impl TaskwarriorTui {
|
||||||
self.mode = Mode::Tasks(Action::Subprocess);
|
self.mode = Mode::Tasks(Action::Subprocess);
|
||||||
} else if input == self.keyconfig.log {
|
} else if input == self.keyconfig.log {
|
||||||
self.mode = Mode::Tasks(Action::Log);
|
self.mode = Mode::Tasks(Action::Log);
|
||||||
self.command_history_context.last();
|
self.command_history.last();
|
||||||
// self.history_status = Some(format!(" {} / {}", self.command_history_context.history_index() + 1, self.command_history_context.history_len()));
|
// self.history_status = Some(format!(" {} / {}", self.command_history.history_index() + 1, self.command_history.history_len()));
|
||||||
self.update_completion_list();
|
self.update_completion_list();
|
||||||
} else if input == self.keyconfig.add {
|
} else if input == self.keyconfig.add {
|
||||||
self.mode = Mode::Tasks(Action::Add);
|
self.mode = Mode::Tasks(Action::Add);
|
||||||
self.command_history_context.last();
|
self.command_history.last();
|
||||||
// self.history_status = Some(format!(" {} / {}", self.command_history_context.history_index() + 1, self.command_history_context.history_len()));
|
// self.history_status = Some(format!(" {} / {}", self.command_history.history_index() + 1, self.command_history.history_len()));
|
||||||
self.update_completion_list();
|
self.update_completion_list();
|
||||||
} else if input == self.keyconfig.annotate {
|
} else if input == self.keyconfig.annotate {
|
||||||
self.mode = Mode::Tasks(Action::Annotate);
|
self.mode = Mode::Tasks(Action::Annotate);
|
||||||
self.command_history_context.last();
|
self.command_history.last();
|
||||||
// self.history_status = Some(format!(" {} / {}", self.command_history_context.history_index() + 1, self.command_history_context.history_len()));
|
// self.history_status = Some(format!(" {} / {}", self.command_history.history_index() + 1, self.command_history.history_len()));
|
||||||
self.update_completion_list();
|
self.update_completion_list();
|
||||||
} else if input == self.keyconfig.help {
|
} else if input == self.keyconfig.help {
|
||||||
self.mode = Mode::Tasks(Action::HelpPopup);
|
self.mode = Mode::Tasks(Action::HelpPopup);
|
||||||
} else if input == self.keyconfig.filter {
|
} else if input == self.keyconfig.filter {
|
||||||
self.mode = Mode::Tasks(Action::Filter);
|
self.mode = Mode::Tasks(Action::Filter);
|
||||||
self.filter_history_context.last();
|
self.filter_history.last();
|
||||||
// self.history_status = Some(format!(" {} / {}", self.filter_history_context.history_index() + 1, self.filter_history_context.history_len()));
|
// self.history_status = Some(format!(" {} / {}", self.filter_history.history_index() + 1, self.filter_history.history_len()));
|
||||||
self.update_completion_list();
|
self.update_completion_list();
|
||||||
} else if input == Key::Char(':') {
|
} else if input == Key::Char(':') {
|
||||||
self.mode = Mode::Tasks(Action::Jump);
|
self.mode = Mode::Tasks(Action::Jump);
|
||||||
|
@ -2716,7 +2663,7 @@ impl TaskwarriorTui {
|
||||||
match self.task_modify() {
|
match self.task_modify() {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
self.mode = Mode::Tasks(Action::Report);
|
self.mode = Mode::Tasks(Action::Report);
|
||||||
self.command_history_context.add(self.modify.as_str());
|
self.command_history.add(self.modify.as_str());
|
||||||
self.modify.update("", 0);
|
self.modify.update("", 0);
|
||||||
self.update(true)?;
|
self.update(true)?;
|
||||||
}
|
}
|
||||||
|
@ -2746,30 +2693,30 @@ impl TaskwarriorTui {
|
||||||
if self.show_completion_pane && !self.completion_list.is_empty() {
|
if self.show_completion_pane && !self.completion_list.is_empty() {
|
||||||
self.completion_list.previous();
|
self.completion_list.previous();
|
||||||
} else if let Some(s) = self
|
} else if let Some(s) = self
|
||||||
.command_history_context
|
.command_history
|
||||||
.history_search(&self.modify.as_str()[..self.modify.pos()], HistoryDirection::Reverse)
|
.history_search(&self.modify.as_str()[..self.modify.pos()], HistoryDirection::Reverse)
|
||||||
{
|
{
|
||||||
let p = self.modify.pos();
|
let p = self.modify.pos();
|
||||||
self.modify.update("", 0);
|
self.modify.update("", 0);
|
||||||
self.modify.update(&s, std::cmp::min(s.len(), p));
|
self.modify.update(&s, std::cmp::min(s.len(), p));
|
||||||
// self.history_status = Some(format!(" {} / {}", self.command_history_context.history_index() + 1, self.command_history_context.history_len()));
|
// self.history_status = Some(format!(" {} / {}", self.command_history.history_index() + 1, self.command_history.history_len()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Key::Down => {
|
Key::Down => {
|
||||||
if self.show_completion_pane && !self.completion_list.is_empty() {
|
if self.show_completion_pane && !self.completion_list.is_empty() {
|
||||||
self.completion_list.next();
|
self.completion_list.next();
|
||||||
} else if let Some(s) = self
|
} else if let Some(s) = self
|
||||||
.command_history_context
|
.command_history
|
||||||
.history_search(&self.modify.as_str()[..self.modify.pos()], HistoryDirection::Forward)
|
.history_search(&self.modify.as_str()[..self.modify.pos()], HistoryDirection::Forward)
|
||||||
{
|
{
|
||||||
let p = self.modify.pos();
|
let p = self.modify.pos();
|
||||||
self.modify.update("", 0);
|
self.modify.update("", 0);
|
||||||
self.modify.update(&s, std::cmp::min(s.len(), p));
|
self.modify.update(&s, std::cmp::min(s.len(), p));
|
||||||
// self.history_status = Some(format!(" {} / {}", self.command_history_context.history_index() + 1, self.command_history_context.history_len()));
|
// self.history_status = Some(format!(" {} / {}", self.command_history.history_index() + 1, self.command_history.history_len()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.command_history_context.last();
|
self.command_history.last();
|
||||||
handle_movement(&mut self.modify, input);
|
handle_movement(&mut self.modify, input);
|
||||||
self.update_input_for_completion();
|
self.update_input_for_completion();
|
||||||
}
|
}
|
||||||
|
@ -2816,7 +2763,7 @@ impl TaskwarriorTui {
|
||||||
match self.task_log() {
|
match self.task_log() {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
self.mode = Mode::Tasks(Action::Report);
|
self.mode = Mode::Tasks(Action::Report);
|
||||||
self.command_history_context.add(self.command.as_str());
|
self.command_history.add(self.command.as_str());
|
||||||
self.command.update("", 0);
|
self.command.update("", 0);
|
||||||
// self.history_status = None;
|
// self.history_status = None;
|
||||||
self.update(true)?;
|
self.update(true)?;
|
||||||
|
@ -2847,30 +2794,30 @@ impl TaskwarriorTui {
|
||||||
if self.show_completion_pane && !self.completion_list.is_empty() {
|
if self.show_completion_pane && !self.completion_list.is_empty() {
|
||||||
self.completion_list.previous();
|
self.completion_list.previous();
|
||||||
} else if let Some(s) = self
|
} else if let Some(s) = self
|
||||||
.command_history_context
|
.command_history
|
||||||
.history_search(&self.command.as_str()[..self.command.pos()], HistoryDirection::Reverse)
|
.history_search(&self.command.as_str()[..self.command.pos()], HistoryDirection::Reverse)
|
||||||
{
|
{
|
||||||
let p = self.command.pos();
|
let p = self.command.pos();
|
||||||
self.command.update("", 0);
|
self.command.update("", 0);
|
||||||
self.command.update(&s, std::cmp::min(s.len(), p));
|
self.command.update(&s, std::cmp::min(s.len(), p));
|
||||||
// self.history_status = Some(format!(" {} / {}", self.command_history_context.history_index() + 1, self.command_history_context.history_len()));
|
// self.history_status = Some(format!(" {} / {}", self.command_history.history_index() + 1, self.command_history.history_len()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Key::Down => {
|
Key::Down => {
|
||||||
if self.show_completion_pane && !self.completion_list.is_empty() {
|
if self.show_completion_pane && !self.completion_list.is_empty() {
|
||||||
self.completion_list.next();
|
self.completion_list.next();
|
||||||
} else if let Some(s) = self
|
} else if let Some(s) = self
|
||||||
.command_history_context
|
.command_history
|
||||||
.history_search(&self.command.as_str()[..self.command.pos()], HistoryDirection::Forward)
|
.history_search(&self.command.as_str()[..self.command.pos()], HistoryDirection::Forward)
|
||||||
{
|
{
|
||||||
let p = self.command.pos();
|
let p = self.command.pos();
|
||||||
self.command.update("", 0);
|
self.command.update("", 0);
|
||||||
self.command.update(&s, std::cmp::min(s.len(), p));
|
self.command.update(&s, std::cmp::min(s.len(), p));
|
||||||
// self.history_status = Some(format!(" {} / {}", self.command_history_context.history_index() + 1, self.command_history_context.history_len()));
|
// self.history_status = Some(format!(" {} / {}", self.command_history.history_index() + 1, self.command_history.history_len()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.command_history_context.last();
|
self.command_history.last();
|
||||||
handle_movement(&mut self.command, input);
|
handle_movement(&mut self.command, input);
|
||||||
self.update_input_for_completion();
|
self.update_input_for_completion();
|
||||||
}
|
}
|
||||||
|
@ -2899,7 +2846,7 @@ impl TaskwarriorTui {
|
||||||
match self.task_annotate() {
|
match self.task_annotate() {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
self.mode = Mode::Tasks(Action::Report);
|
self.mode = Mode::Tasks(Action::Report);
|
||||||
self.command_history_context.add(self.command.as_str());
|
self.command_history.add(self.command.as_str());
|
||||||
self.command.update("", 0);
|
self.command.update("", 0);
|
||||||
// self.history_status = None;
|
// self.history_status = None;
|
||||||
self.update(true)?;
|
self.update(true)?;
|
||||||
|
@ -2929,31 +2876,31 @@ impl TaskwarriorTui {
|
||||||
if self.show_completion_pane && !self.completion_list.is_empty() {
|
if self.show_completion_pane && !self.completion_list.is_empty() {
|
||||||
self.completion_list.previous();
|
self.completion_list.previous();
|
||||||
} else if let Some(s) = self
|
} else if let Some(s) = self
|
||||||
.command_history_context
|
.command_history
|
||||||
.history_search(&self.command.as_str()[..self.command.pos()], HistoryDirection::Reverse)
|
.history_search(&self.command.as_str()[..self.command.pos()], HistoryDirection::Reverse)
|
||||||
{
|
{
|
||||||
let p = self.command.pos();
|
let p = self.command.pos();
|
||||||
self.command.update("", 0);
|
self.command.update("", 0);
|
||||||
self.command.update(&s, std::cmp::min(s.len(), p));
|
self.command.update(&s, std::cmp::min(s.len(), p));
|
||||||
// self.history_status = Some(format!(" {} / {}", self.command_history_context.history_index() + 1, self.command_history_context.history_len()));
|
// self.history_status = Some(format!(" {} / {}", self.command_history.history_index() + 1, self.command_history.history_len()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Key::Down => {
|
Key::Down => {
|
||||||
if self.show_completion_pane && !self.completion_list.is_empty() {
|
if self.show_completion_pane && !self.completion_list.is_empty() {
|
||||||
self.completion_list.next();
|
self.completion_list.next();
|
||||||
} else if let Some(s) = self
|
} else if let Some(s) = self
|
||||||
.command_history_context
|
.command_history
|
||||||
.history_search(&self.command.as_str()[..self.command.pos()], HistoryDirection::Forward)
|
.history_search(&self.command.as_str()[..self.command.pos()], HistoryDirection::Forward)
|
||||||
{
|
{
|
||||||
let p = self.command.pos();
|
let p = self.command.pos();
|
||||||
self.command.update("", 0);
|
self.command.update("", 0);
|
||||||
self.command.update(&s, std::cmp::min(s.len(), p));
|
self.command.update(&s, std::cmp::min(s.len(), p));
|
||||||
// self.history_status = Some(format!(" {} / {}", self.command_history_context.history_index() + 1, self.command_history_context.history_len()));
|
// self.history_status = Some(format!(" {} / {}", self.command_history.history_index() + 1, self.command_history.history_len()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
self.command_history_context.last();
|
self.command_history.last();
|
||||||
handle_movement(&mut self.command, input);
|
handle_movement(&mut self.command, input);
|
||||||
self.update_input_for_completion();
|
self.update_input_for_completion();
|
||||||
}
|
}
|
||||||
|
@ -3001,7 +2948,7 @@ impl TaskwarriorTui {
|
||||||
match self.task_add() {
|
match self.task_add() {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
self.mode = Mode::Tasks(Action::Report);
|
self.mode = Mode::Tasks(Action::Report);
|
||||||
self.command_history_context.add(self.command.as_str());
|
self.command_history.add(self.command.as_str());
|
||||||
self.command.update("", 0);
|
self.command.update("", 0);
|
||||||
// self.history_status = None;
|
// self.history_status = None;
|
||||||
self.update(true)?;
|
self.update(true)?;
|
||||||
|
@ -3031,13 +2978,13 @@ impl TaskwarriorTui {
|
||||||
if self.show_completion_pane && !self.completion_list.is_empty() {
|
if self.show_completion_pane && !self.completion_list.is_empty() {
|
||||||
self.completion_list.previous();
|
self.completion_list.previous();
|
||||||
} else if let Some(s) = self
|
} else if let Some(s) = self
|
||||||
.command_history_context
|
.command_history
|
||||||
.history_search(&self.command.as_str()[..self.command.pos()], HistoryDirection::Reverse)
|
.history_search(&self.command.as_str()[..self.command.pos()], HistoryDirection::Reverse)
|
||||||
{
|
{
|
||||||
let p = self.command.pos();
|
let p = self.command.pos();
|
||||||
self.command.update("", 0);
|
self.command.update("", 0);
|
||||||
self.command.update(&s, std::cmp::min(s.len(), p));
|
self.command.update(&s, std::cmp::min(s.len(), p));
|
||||||
// self.history_status = Some(format!(" {} / {}", self.command_history_context.history_index() + 1, self.command_history_context.history_len()));
|
// self.history_status = Some(format!(" {} / {}", self.command_history.history_index() + 1, self.command_history.history_len()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3045,17 +2992,17 @@ impl TaskwarriorTui {
|
||||||
if self.show_completion_pane && !self.completion_list.is_empty() {
|
if self.show_completion_pane && !self.completion_list.is_empty() {
|
||||||
self.completion_list.next();
|
self.completion_list.next();
|
||||||
} else if let Some(s) = self
|
} else if let Some(s) = self
|
||||||
.command_history_context
|
.command_history
|
||||||
.history_search(&self.command.as_str()[..self.command.pos()], HistoryDirection::Forward)
|
.history_search(&self.command.as_str()[..self.command.pos()], HistoryDirection::Forward)
|
||||||
{
|
{
|
||||||
let p = self.command.pos();
|
let p = self.command.pos();
|
||||||
self.command.update("", 0);
|
self.command.update("", 0);
|
||||||
self.command.update(&s, std::cmp::min(s.len(), p));
|
self.command.update(&s, std::cmp::min(s.len(), p));
|
||||||
// self.history_status = Some(format!(" {} / {}", self.command_history_context.history_index() + 1, self.command_history_context.history_len()));
|
// self.history_status = Some(format!(" {} / {}", self.command_history.history_index() + 1, self.command_history.history_len()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.command_history_context.last();
|
self.command_history.last();
|
||||||
handle_movement(&mut self.command, input);
|
handle_movement(&mut self.command, input);
|
||||||
self.update_input_for_completion();
|
self.update_input_for_completion();
|
||||||
}
|
}
|
||||||
|
@ -3067,7 +3014,7 @@ impl TaskwarriorTui {
|
||||||
self.completion_list.unselect();
|
self.completion_list.unselect();
|
||||||
} else {
|
} else {
|
||||||
self.mode = Mode::Tasks(Action::Report);
|
self.mode = Mode::Tasks(Action::Report);
|
||||||
self.filter_history_context.add(self.filter.as_str());
|
self.filter_history.add(self.filter.as_str());
|
||||||
// self.history_status = None;
|
// self.history_status = None;
|
||||||
self.update(true)?;
|
self.update(true)?;
|
||||||
}
|
}
|
||||||
|
@ -3084,7 +3031,7 @@ impl TaskwarriorTui {
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
} else {
|
} else {
|
||||||
self.mode = Mode::Tasks(Action::Report);
|
self.mode = Mode::Tasks(Action::Report);
|
||||||
self.filter_history_context.add(self.filter.as_str());
|
self.filter_history.add(self.filter.as_str());
|
||||||
// self.history_status = None;
|
// self.history_status = None;
|
||||||
self.update(true)?;
|
self.update(true)?;
|
||||||
}
|
}
|
||||||
|
@ -3093,13 +3040,13 @@ impl TaskwarriorTui {
|
||||||
if self.show_completion_pane && !self.completion_list.is_empty() {
|
if self.show_completion_pane && !self.completion_list.is_empty() {
|
||||||
self.completion_list.previous();
|
self.completion_list.previous();
|
||||||
} else if let Some(s) = self
|
} else if let Some(s) = self
|
||||||
.filter_history_context
|
.filter_history
|
||||||
.history_search(&self.filter.as_str()[..self.filter.pos()], HistoryDirection::Reverse)
|
.history_search(&self.filter.as_str()[..self.filter.pos()], HistoryDirection::Reverse)
|
||||||
{
|
{
|
||||||
let p = self.filter.pos();
|
let p = self.filter.pos();
|
||||||
self.filter.update("", 0);
|
self.filter.update("", 0);
|
||||||
self.filter.update(&s, std::cmp::min(p, s.len()));
|
self.filter.update(&s, std::cmp::min(p, s.len()));
|
||||||
// self.history_status = Some(format!(" {} / {}", self.filter_history_context.history_index() + 1, self.filter_history_context.history_len()));
|
// self.history_status = Some(format!(" {} / {}", self.filter_history.history_index() + 1, self.filter_history.history_len()));
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3107,13 +3054,13 @@ impl TaskwarriorTui {
|
||||||
if self.show_completion_pane && !self.completion_list.is_empty() {
|
if self.show_completion_pane && !self.completion_list.is_empty() {
|
||||||
self.completion_list.next();
|
self.completion_list.next();
|
||||||
} else if let Some(s) = self
|
} else if let Some(s) = self
|
||||||
.filter_history_context
|
.filter_history
|
||||||
.history_search(&self.filter.as_str()[..self.filter.pos()], HistoryDirection::Forward)
|
.history_search(&self.filter.as_str()[..self.filter.pos()], HistoryDirection::Forward)
|
||||||
{
|
{
|
||||||
let p = self.filter.pos();
|
let p = self.filter.pos();
|
||||||
self.filter.update("", 0);
|
self.filter.update("", 0);
|
||||||
self.filter.update(&s, std::cmp::min(p, s.len()));
|
self.filter.update(&s, std::cmp::min(p, s.len()));
|
||||||
// self.history_status = Some(format!(" {} / {}", self.filter_history_context.history_index() + 1, self.filter_history_context.history_len()));
|
// self.history_status = Some(format!(" {} / {}", self.filter_history.history_index() + 1, self.filter_history.history_len()));
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3603,10 +3550,10 @@ mod tests {
|
||||||
assert_eq!(app.tasks.len(), 26);
|
assert_eq!(app.tasks.len(), 26);
|
||||||
assert_eq!(app.current_context_filter, "");
|
assert_eq!(app.current_context_filter, "");
|
||||||
|
|
||||||
assert_eq!(app.context_table_state.current_selection(), Some(0));
|
assert_eq!(app.contexts.table_state.current_selection(), Some(0));
|
||||||
app.context_next();
|
app.context_next();
|
||||||
app.context_select().unwrap();
|
app.context_select().unwrap();
|
||||||
assert_eq!(app.context_table_state.current_selection(), Some(1));
|
assert_eq!(app.contexts.table_state.current_selection(), Some(1));
|
||||||
|
|
||||||
assert!(app.get_context().is_ok());
|
assert!(app.get_context().is_ok());
|
||||||
assert!(app.update(true).is_ok());
|
assert!(app.update(true).is_ok());
|
||||||
|
@ -3614,10 +3561,10 @@ mod tests {
|
||||||
assert_eq!(app.tasks.len(), 1);
|
assert_eq!(app.tasks.len(), 1);
|
||||||
assert_eq!(app.current_context_filter, "+finance -private");
|
assert_eq!(app.current_context_filter, "+finance -private");
|
||||||
|
|
||||||
assert_eq!(app.context_table_state.current_selection(), Some(1));
|
assert_eq!(app.contexts.table_state.current_selection(), Some(1));
|
||||||
app.context_previous();
|
app.context_previous();
|
||||||
app.context_select().unwrap();
|
app.context_select().unwrap();
|
||||||
assert_eq!(app.context_table_state.current_selection(), Some(0));
|
assert_eq!(app.contexts.table_state.current_selection(), Some(0));
|
||||||
|
|
||||||
assert!(app.get_context().is_ok());
|
assert!(app.get_context().is_ok());
|
||||||
assert!(app.update(true).is_ok());
|
assert!(app.update(true).is_ok());
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
use std::cmp;
|
|
||||||
|
|
||||||
use tui::{
|
|
||||||
buffer::Buffer,
|
|
||||||
layout::{Alignment, Rect},
|
|
||||||
style::{Modifier, Style},
|
|
||||||
text::{Span, Spans, Text},
|
|
||||||
widgets::{Block, BorderType, Borders, Clear, Paragraph, StatefulWidget, Widget},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
|
||||||
pub struct Context {
|
|
||||||
pub name: String,
|
|
||||||
pub description: String,
|
|
||||||
pub active: String,
|
|
||||||
pub typ: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Context {
|
|
||||||
pub fn new(name: String, description: String, active: String, typ: String) -> Self {
|
|
||||||
Self {
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
active,
|
|
||||||
typ,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -32,7 +32,6 @@ mod calendar;
|
||||||
mod cli;
|
mod cli;
|
||||||
mod completion;
|
mod completion;
|
||||||
mod config;
|
mod config;
|
||||||
mod context;
|
|
||||||
mod event;
|
mod event;
|
||||||
mod help;
|
mod help;
|
||||||
mod history;
|
mod history;
|
||||||
|
|
144
src/pane/context.rs
Normal file
144
src/pane/context.rs
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
use anyhow::Context as AnyhowContext;
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
const NAME: &str = "Name";
|
||||||
|
const TYPE: &str = "Remaining";
|
||||||
|
const DEFINITION: &str = "Avg age";
|
||||||
|
const ACTIVE: &str = "Complete";
|
||||||
|
|
||||||
|
use chrono::{Datelike, Duration, Local, Month, NaiveDate, NaiveDateTime, TimeZone};
|
||||||
|
|
||||||
|
use tui::{
|
||||||
|
buffer::Buffer,
|
||||||
|
layout::{Alignment, Rect},
|
||||||
|
style::{Color, Modifier, Style},
|
||||||
|
symbols,
|
||||||
|
text::{Span, Spans, Text},
|
||||||
|
widgets::{Block, BorderType, Borders, Clear, Paragraph, StatefulWidget, Widget},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::app::{Action, Mode, TaskwarriorTui};
|
||||||
|
use crate::event::Key;
|
||||||
|
use crate::pane::Pane;
|
||||||
|
use crate::table::TableState;
|
||||||
|
use itertools::Itertools;
|
||||||
|
use std::cmp;
|
||||||
|
use std::cmp::min;
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::error::Error;
|
||||||
|
use std::process::{Command, Output};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct ContextDetails {
|
||||||
|
pub name: String,
|
||||||
|
pub definition: String,
|
||||||
|
pub active: String,
|
||||||
|
pub type_: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContextDetails {
|
||||||
|
pub fn new(name: String, definition: String, active: String, type_: String) -> Self {
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
definition,
|
||||||
|
active,
|
||||||
|
type_,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ContextsState {
|
||||||
|
pub table_state: TableState,
|
||||||
|
pub report_height: u16,
|
||||||
|
pub columns: Vec<String>,
|
||||||
|
pub rows: Vec<ContextDetails>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContextsState {
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
table_state: TableState::default(),
|
||||||
|
report_height: 0,
|
||||||
|
columns: vec![
|
||||||
|
NAME.to_string(),
|
||||||
|
TYPE.to_string(),
|
||||||
|
DEFINITION.to_string(),
|
||||||
|
ACTIVE.to_string(),
|
||||||
|
],
|
||||||
|
rows: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn simplified_view(&mut self) -> (Vec<Vec<String>>, Vec<String>) {
|
||||||
|
let rows = self
|
||||||
|
.rows
|
||||||
|
.iter()
|
||||||
|
.map(|c| vec![c.name.clone(), c.type_.clone(), c.definition.clone(), c.active.clone()])
|
||||||
|
.collect();
|
||||||
|
let headers = self.columns.clone();
|
||||||
|
(rows, headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.rows.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_data(&mut self) -> Result<()> {
|
||||||
|
let output = Command::new("task").arg("context").output()?;
|
||||||
|
let data = String::from_utf8_lossy(&output.stdout);
|
||||||
|
|
||||||
|
self.rows = vec![];
|
||||||
|
for (i, line) in data.trim().split('\n').enumerate() {
|
||||||
|
if line.starts_with(" ") && line.trim().starts_with("write") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if line.starts_with(" ") && !(line.trim().ends_with("yes") || line.trim().ends_with("no")) {
|
||||||
|
let definition = line.trim();
|
||||||
|
if let Some(c) = self.rows.last_mut() {
|
||||||
|
(*c).definition = format!("{} {}", c.definition, definition);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let line = line.trim();
|
||||||
|
if line.is_empty() || line == "Use 'task context none' to unset the current context." {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if i == 0 || i == 1 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut s = line.split_whitespace();
|
||||||
|
let name = s.next().unwrap_or_default();
|
||||||
|
let typ = s.next().unwrap_or_default();
|
||||||
|
let active = s.last().unwrap_or_default();
|
||||||
|
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(),
|
||||||
|
);
|
||||||
|
self.rows.push(context);
|
||||||
|
}
|
||||||
|
if self.rows.iter().any(|r| r.active != "no") {
|
||||||
|
self.rows.insert(
|
||||||
|
0,
|
||||||
|
ContextDetails::new("none".to_string(), "".to_string(), "no".to_string(), "read".to_string()),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
self.rows.insert(
|
||||||
|
0,
|
||||||
|
ContextDetails::new(
|
||||||
|
"none".to_string(),
|
||||||
|
"".to_string(),
|
||||||
|
"yes".to_string(),
|
||||||
|
"read".to_string(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ use crate::event::Key;
|
||||||
use clap::App;
|
use clap::App;
|
||||||
use std::ops::Index;
|
use std::ops::Index;
|
||||||
|
|
||||||
|
pub mod context;
|
||||||
pub mod project;
|
pub mod project;
|
||||||
|
|
||||||
pub trait Pane {
|
pub trait Pane {
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
// Based on https://gist.github.com/diwic/5c20a283ca3a03752e1a27b0f3ebfa30
|
|
||||||
// See https://old.reddit.com/r/rust/comments/4xneq5/the_calendar_example_challenge_ii_why_eddyb_all/
|
|
||||||
|
|
||||||
use anyhow::Context as AnyhowContext;
|
use anyhow::Context as AnyhowContext;
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
@ -66,6 +63,7 @@ impl ProjectsState {
|
||||||
rows: vec![],
|
rows: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pattern_by_marked(app: &mut TaskwarriorTui) -> String {
|
fn pattern_by_marked(app: &mut TaskwarriorTui) -> String {
|
||||||
let mut project_pattern = String::new();
|
let mut project_pattern = String::new();
|
||||||
if !app.projects.marked.is_empty() {
|
if !app.projects.marked.is_empty() {
|
||||||
|
@ -74,16 +72,17 @@ impl ProjectsState {
|
||||||
if input.as_str() == "(none)" {
|
if input.as_str() == "(none)" {
|
||||||
input = " ".to_string();
|
input = " ".to_string();
|
||||||
}
|
}
|
||||||
if idx > 0 {
|
if idx == 0 {
|
||||||
project_pattern = format!("{} or project:{}", project_pattern, input);
|
|
||||||
} else {
|
|
||||||
project_pattern = format!("\'(project:{}", input);
|
project_pattern = format!("\'(project:{}", input);
|
||||||
|
} else {
|
||||||
|
project_pattern = format!("{} or project:{}", project_pattern, input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
project_pattern = format!("{})\'", project_pattern);
|
project_pattern = format!("{})\'", project_pattern);
|
||||||
}
|
}
|
||||||
project_pattern
|
project_pattern
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn toggle_mark(&mut self) {
|
pub fn toggle_mark(&mut self) {
|
||||||
if !self.list.is_empty() {
|
if !self.list.is_empty() {
|
||||||
let selected = self.current_selection;
|
let selected = self.current_selection;
|
||||||
|
@ -92,6 +91,7 @@ impl ProjectsState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn simplified_view(&mut self) -> (Vec<Vec<String>>, Vec<String>) {
|
pub fn simplified_view(&mut self) -> (Vec<Vec<String>>, Vec<String>) {
|
||||||
let rows = self
|
let rows = self
|
||||||
.rows
|
.rows
|
||||||
|
@ -165,11 +165,13 @@ impl ProjectsState {
|
||||||
self.list = self.rows.iter().map(|x| x.name.clone()).collect_vec();
|
self.list = self.rows.iter().map(|x| x.name.clone()).collect_vec();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_table_state(&mut self) {
|
fn update_table_state(&mut self) {
|
||||||
self.table_state.select(Some(self.current_selection));
|
self.table_state.select(Some(self.current_selection));
|
||||||
if self.marked.is_empty() {
|
if self.marked.is_empty() {
|
||||||
self.table_state.single_selection();
|
self.table_state.single_selection();
|
||||||
}
|
} else {
|
||||||
|
self.table_state.multiple_selection();
|
||||||
self.table_state.clear();
|
self.table_state.clear();
|
||||||
for project in &self.marked {
|
for project in &self.marked {
|
||||||
let index = self.list.iter().position(|x| x == project);
|
let index = self.list.iter().position(|x| x == project);
|
||||||
|
@ -177,6 +179,7 @@ impl ProjectsState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Pane for ProjectsState {
|
impl Pane for ProjectsState {
|
||||||
fn handle_input(app: &mut TaskwarriorTui, input: Key) -> Result<()> {
|
fn handle_input(app: &mut TaskwarriorTui, input: Key) -> Result<()> {
|
||||||
|
@ -218,7 +221,7 @@ fn update_task_filter_by_selection(app: &mut TaskwarriorTui) -> Result<()> {
|
||||||
app.projects.toggle_mark();
|
app.projects.toggle_mark();
|
||||||
let new_project_pattern = ProjectsState::pattern_by_marked(app);
|
let new_project_pattern = ProjectsState::pattern_by_marked(app);
|
||||||
let current_filter = app.filter.as_str();
|
let current_filter = app.filter.as_str();
|
||||||
app.filter_history_context.add(current_filter);
|
app.filter_history.add(current_filter);
|
||||||
|
|
||||||
let mut filter = current_filter.replace(&last_project_pattern, "");
|
let mut filter = current_filter.replace(&last_project_pattern, "");
|
||||||
filter = format!("{}{}", filter, new_project_pattern);
|
filter = format!("{}{}", filter, new_project_pattern);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue