Fix all clippy warnings in `meli` crate
parent
d921b3c320
commit
9cb66ef818
|
@ -50,7 +50,9 @@ pub fn override_derive(filenames: &[(&str, &str)]) {
|
||||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//! This module is automatically generated by build.rs.
|
#![allow(clippy::derivable_impls)]
|
||||||
|
|
||||||
|
//! This module is automatically generated by config_macros.rs.
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
"##
|
"##
|
||||||
|
|
38
src/bin.rs
38
src/bin.rs
|
@ -42,6 +42,8 @@ extern crate serde_json;
|
||||||
extern crate smallvec;
|
extern crate smallvec;
|
||||||
extern crate termion;
|
extern crate termion;
|
||||||
|
|
||||||
|
use structopt::StructOpt;
|
||||||
|
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static GLOBAL: System = System;
|
static GLOBAL: System = System;
|
||||||
|
|
||||||
|
@ -120,6 +122,7 @@ fn notify(
|
||||||
Ok(r)
|
Ok(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "cli-docs")]
|
||||||
fn parse_manpage(src: &str) -> Result<ManPages> {
|
fn parse_manpage(src: &str) -> Result<ManPages> {
|
||||||
match src {
|
match src {
|
||||||
"" | "meli" | "main" => Ok(ManPages::Main),
|
"" | "meli" | "main" => Ok(ManPages::Main),
|
||||||
|
@ -132,8 +135,7 @@ fn parse_manpage(src: &str) -> Result<ManPages> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use structopt::StructOpt;
|
#[cfg(feature = "cli-docs")]
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
/// Choose manpage
|
/// Choose manpage
|
||||||
enum ManPages {
|
enum ManPages {
|
||||||
|
@ -193,9 +195,11 @@ enum SubCommand {
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, StructOpt)]
|
||||||
struct ManOpt {
|
struct ManOpt {
|
||||||
#[structopt(default_value = "meli", possible_values=&["meli", "conf", "themes"], value_name="PAGE", parse(try_from_str = parse_manpage))]
|
#[structopt(default_value = "meli", possible_values=&["meli", "conf", "themes"], value_name="PAGE", parse(try_from_str = parse_manpage))]
|
||||||
|
#[cfg(feature = "cli-docs")]
|
||||||
page: ManPages,
|
page: ManPages,
|
||||||
/// If true, output text in stdout instead of spawning $PAGER.
|
/// If true, output text in stdout instead of spawning $PAGER.
|
||||||
#[structopt(long = "no-raw", alias = "no-raw", value_name = "bool")]
|
#[structopt(long = "no-raw", alias = "no-raw", value_name = "bool")]
|
||||||
|
#[cfg(feature = "cli-docs")]
|
||||||
no_raw: Option<Option<bool>>,
|
no_raw: Option<Option<bool>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,7 +247,7 @@ fn run_app(opt: Opt) -> Result<()> {
|
||||||
#[cfg(feature = "cli-docs")]
|
#[cfg(feature = "cli-docs")]
|
||||||
Some(SubCommand::Man(manopt)) => {
|
Some(SubCommand::Man(manopt)) => {
|
||||||
let ManOpt { page, no_raw } = manopt;
|
let ManOpt { page, no_raw } = manopt;
|
||||||
const MANPAGES: [&'static [u8]; 3] = [
|
const MANPAGES: [&[u8]; 3] = [
|
||||||
include_bytes!(concat!(env!("OUT_DIR"), "/meli.txt.gz")),
|
include_bytes!(concat!(env!("OUT_DIR"), "/meli.txt.gz")),
|
||||||
include_bytes!(concat!(env!("OUT_DIR"), "/meli.conf.txt.gz")),
|
include_bytes!(concat!(env!("OUT_DIR"), "/meli.conf.txt.gz")),
|
||||||
include_bytes!(concat!(env!("OUT_DIR"), "/meli-themes.txt.gz")),
|
include_bytes!(concat!(env!("OUT_DIR"), "/meli-themes.txt.gz")),
|
||||||
|
@ -255,10 +259,9 @@ fn run_app(opt: Opt) -> Result<()> {
|
||||||
str::parse::<usize>(unsafe {
|
str::parse::<usize>(unsafe {
|
||||||
std::str::from_utf8_unchecked(gz.header().unwrap().comment().unwrap())
|
std::str::from_utf8_unchecked(gz.header().unwrap().comment().unwrap())
|
||||||
})
|
})
|
||||||
.expect(&format!(
|
.unwrap_or_else(|_| {
|
||||||
"{:?} was not compressed with size comment header",
|
panic!("{:?} was not compressed with size comment header", page)
|
||||||
page
|
}),
|
||||||
)),
|
|
||||||
);
|
);
|
||||||
gz.read_to_string(&mut v)?;
|
gz.read_to_string(&mut v)?;
|
||||||
|
|
||||||
|
@ -271,19 +274,18 @@ fn run_app(opt: Opt) -> Result<()> {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if unsafe { libc::isatty(libc::STDOUT_FILENO) != 1 } {
|
||||||
if unsafe { libc::isatty(libc::STDOUT_FILENO) != 1 } {
|
println!("{}", &v);
|
||||||
println!("{}", &v);
|
return Ok(());
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
let mut handle = Command::new(std::env::var("PAGER").unwrap_or("more".to_string()))
|
let mut handle =
|
||||||
.stdin(Stdio::piped())
|
Command::new(std::env::var("PAGER").unwrap_or_else(|_| "more".to_string()))
|
||||||
.stdout(Stdio::inherit())
|
.stdin(Stdio::piped())
|
||||||
.stderr(Stdio::inherit())
|
.stdout(Stdio::inherit())
|
||||||
.spawn()?;
|
.stderr(Stdio::inherit())
|
||||||
|
.spawn()?;
|
||||||
handle.stdin.take().unwrap().write_all(v.as_bytes())?;
|
handle.stdin.take().unwrap().write_all(v.as_bytes())?;
|
||||||
handle.wait()?;
|
handle.wait()?;
|
||||||
|
|
||||||
|
@ -314,7 +316,7 @@ fn run_app(opt: Opt) -> Result<()> {
|
||||||
}
|
}
|
||||||
Some(SubCommand::PrintLoadedThemes) => {
|
Some(SubCommand::PrintLoadedThemes) => {
|
||||||
let s = conf::FileSettings::new()?;
|
let s = conf::FileSettings::new()?;
|
||||||
print!("{}", s.terminal.themes.to_string());
|
print!("{}", s.terminal.themes);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
Some(SubCommand::PrintDefaultTheme) => {
|
Some(SubCommand::PrintDefaultTheme) => {
|
||||||
|
|
|
@ -116,7 +116,7 @@ impl TokenStream {
|
||||||
}
|
}
|
||||||
*s = &s[ptr..];
|
*s = &s[ptr..];
|
||||||
//println!("\t before s.is_empty() {:?} {:?}", t, s);
|
//println!("\t before s.is_empty() {:?} {:?}", t, s);
|
||||||
if s.is_empty() || &*s == &" " {
|
if s.is_empty() || *s == " " {
|
||||||
match t.inner() {
|
match t.inner() {
|
||||||
Literal(lit) => {
|
Literal(lit) => {
|
||||||
sugg.insert(format!("{}{}", if s.is_empty() { " " } else { "" }, lit));
|
sugg.insert(format!("{}{}", if s.is_empty() { " " } else { "" }, lit));
|
||||||
|
@ -126,7 +126,7 @@ impl TokenStream {
|
||||||
//println!("adding empty suggestions for {:?}", t);
|
//println!("adding empty suggestions for {:?}", t);
|
||||||
let mut _s = *s;
|
let mut _s = *s;
|
||||||
let mut m = t.matches(&mut _s, sugg);
|
let mut m = t.matches(&mut _s, sugg);
|
||||||
tokens.extend(m.drain(..));
|
tokens.append(&mut m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Seq(_s) => {}
|
Seq(_s) => {}
|
||||||
|
@ -167,7 +167,7 @@ impl TokenStream {
|
||||||
let mut _s = *s;
|
let mut _s = *s;
|
||||||
let mut m = t.matches(&mut _s, sugg);
|
let mut m = t.matches(&mut _s, sugg);
|
||||||
if !m.is_empty() {
|
if !m.is_empty() {
|
||||||
tokens.extend(m.drain(..));
|
tokens.append(&mut m);
|
||||||
//println!("_s is empty {}", _s.is_empty());
|
//println!("_s is empty {}", _s.is_empty());
|
||||||
cont = !_s.is_empty();
|
cont = !_s.is_empty();
|
||||||
*s = _s;
|
*s = _s;
|
||||||
|
@ -262,7 +262,7 @@ define_commands!([
|
||||||
desc: "set [seen/unseen], toggles message's Seen flag.",
|
desc: "set [seen/unseen], toggles message's Seen flag.",
|
||||||
tokens: &[One(Literal("set")), One(Alternatives(&[to_stream!(One(Literal("seen"))), to_stream!(One(Literal("unseen")))]))],
|
tokens: &[One(Literal("set")), One(Alternatives(&[to_stream!(One(Literal("seen"))), to_stream!(One(Literal("unseen")))]))],
|
||||||
parser: (
|
parser: (
|
||||||
fn seen_flag<'a>(input: &'a [u8]) -> IResult<&'a [u8], Action> {
|
fn seen_flag(input: &'_ [u8]) -> IResult<&'_ [u8], Action> {
|
||||||
let (input, _) = tag("set")(input.trim())?;
|
let (input, _) = tag("set")(input.trim())?;
|
||||||
let (input, _) = is_a(" ")(input)?;
|
let (input, _) = is_a(" ")(input)?;
|
||||||
let (input, ret) = alt((map(tag("seen"), |_| Listing(SetSeen)), map(tag("unseen"), |_| Listing(SetUnseen))))(input)?;
|
let (input, ret) = alt((map(tag("seen"), |_| Listing(SetSeen)), map(tag("unseen"), |_| Listing(SetUnseen))))(input)?;
|
||||||
|
@ -275,7 +275,7 @@ define_commands!([
|
||||||
desc: "delete message",
|
desc: "delete message",
|
||||||
tokens: &[One(Literal("delete"))],
|
tokens: &[One(Literal("delete"))],
|
||||||
parser: (
|
parser: (
|
||||||
fn delete_message<'a>(input: &'a [u8]) -> IResult<&'a [u8], Action> {
|
fn delete_message(input: &'_ [u8]) -> IResult<&'_ [u8], Action> {
|
||||||
let (input, ret) = map(preceded(tag("delete"), eof), |_| Listing(Delete))(input)?;
|
let (input, ret) = map(preceded(tag("delete"), eof), |_| Listing(Delete))(input)?;
|
||||||
let (input, _) = eof(input)?;
|
let (input, _) = eof(input)?;
|
||||||
Ok((input, ret))
|
Ok((input, ret))
|
||||||
|
@ -536,7 +536,7 @@ define_commands!([
|
||||||
desc: "filter EXECUTABLE ARGS",
|
desc: "filter EXECUTABLE ARGS",
|
||||||
tokens: &[One(Literal("filter")), One(Filepath), ZeroOrMore(QuotedStringValue)],
|
tokens: &[One(Literal("filter")), One(Filepath), ZeroOrMore(QuotedStringValue)],
|
||||||
parser:(
|
parser:(
|
||||||
fn filter<'a>(input: &'a [u8]) -> IResult<&'a [u8], Action> {
|
fn filter(input: &'_ [u8]) -> IResult<&'_ [u8], Action> {
|
||||||
let (input, _) = tag("filter")(input.trim())?;
|
let (input, _) = tag("filter")(input.trim())?;
|
||||||
let (input, _) = is_a(" ")(input)?;
|
let (input, _) = is_a(" ")(input)?;
|
||||||
let (input, cmd) = map_res(not_line_ending, std::str::from_utf8)(input)?;
|
let (input, cmd) = map_res(not_line_ending, std::str::from_utf8)(input)?;
|
||||||
|
@ -1080,7 +1080,7 @@ pub fn command_completion_suggestions(input: &str) -> Vec<String> {
|
||||||
}
|
}
|
||||||
if let Some((s, Filepath)) = _m.last() {
|
if let Some((s, Filepath)) = _m.last() {
|
||||||
let p = std::path::Path::new(s);
|
let p = std::path::Path::new(s);
|
||||||
sugg.extend(p.complete(true).into_iter().map(|m| m.into()));
|
sugg.extend(p.complete(true).into_iter());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sugg.into_iter()
|
sugg.into_iter()
|
||||||
|
|
|
@ -29,7 +29,7 @@ pub use self::contact_list::*;
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum ViewMode {
|
enum ViewMode {
|
||||||
ReadOnly,
|
ReadOnly,
|
||||||
Discard(UIDialog<char>),
|
Discard(Box<UIDialog<char>>),
|
||||||
Edit,
|
Edit,
|
||||||
//New,
|
//New,
|
||||||
}
|
}
|
||||||
|
@ -113,27 +113,21 @@ impl ContactManager {
|
||||||
self.form = FormWidget::new(("Save".into(), true));
|
self.form = FormWidget::new(("Save".into(), true));
|
||||||
self.form.add_button(("Cancel(Esc)".into(), false));
|
self.form.add_button(("Cancel(Esc)".into(), false));
|
||||||
self.form
|
self.form
|
||||||
.push(("NAME".into(), self.card.name().to_string().into()));
|
.push(("NAME".into(), self.card.name().to_string()));
|
||||||
self.form.push((
|
self.form.push((
|
||||||
"ADDITIONAL NAME".into(),
|
"ADDITIONAL NAME".into(),
|
||||||
self.card.additionalname().to_string().into(),
|
self.card.additionalname().to_string(),
|
||||||
));
|
|
||||||
self.form.push((
|
|
||||||
"NAME PREFIX".into(),
|
|
||||||
self.card.name_prefix().to_string().into(),
|
|
||||||
));
|
|
||||||
self.form.push((
|
|
||||||
"NAME SUFFIX".into(),
|
|
||||||
self.card.name_suffix().to_string().into(),
|
|
||||||
));
|
));
|
||||||
self.form
|
self.form
|
||||||
.push(("E-MAIL".into(), self.card.email().to_string().into()));
|
.push(("NAME PREFIX".into(), self.card.name_prefix().to_string()));
|
||||||
self.form
|
self.form
|
||||||
.push(("URL".into(), self.card.url().to_string().into()));
|
.push(("NAME SUFFIX".into(), self.card.name_suffix().to_string()));
|
||||||
self.form
|
self.form
|
||||||
.push(("KEY".into(), self.card.key().to_string().into()));
|
.push(("E-MAIL".into(), self.card.email().to_string()));
|
||||||
|
self.form.push(("URL".into(), self.card.url().to_string()));
|
||||||
|
self.form.push(("KEY".into(), self.card.key().to_string()));
|
||||||
for (k, v) in self.card.extra_properties() {
|
for (k, v) in self.card.extra_properties() {
|
||||||
self.form.push((k.to_string().into(), v.to_string().into()));
|
self.form.push((k.to_string().into(), v.to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,26 +162,20 @@ impl Component for ContactManager {
|
||||||
(set_y(upper_left, get_y(upper_left) + 2), bottom_right),
|
(set_y(upper_left, get_y(upper_left) + 2), bottom_right),
|
||||||
context,
|
context,
|
||||||
);
|
);
|
||||||
match self.mode {
|
if let ViewMode::Discard(ref mut selector) = self.mode {
|
||||||
ViewMode::Discard(ref mut selector) => {
|
/* Let user choose whether to quit with/without saving or cancel */
|
||||||
/* Let user choose whether to quit with/without saving or cancel */
|
selector.draw(grid, area, context);
|
||||||
selector.draw(grid, area, context);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
context.dirty_areas.push_back(area);
|
context.dirty_areas.push_back(area);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
|
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
|
||||||
match event {
|
if let UIEvent::ConfigReload { old_settings: _ } = event {
|
||||||
UIEvent::ConfigReload { old_settings: _ } => {
|
self.theme_default = crate::conf::value(context, "theme_default");
|
||||||
self.theme_default = crate::conf::value(context, "theme_default");
|
self.content = CellBuffer::new_with_context(100, 1, None, context);
|
||||||
self.content = CellBuffer::new_with_context(100, 1, None, context);
|
self.initialized = false;
|
||||||
self.initialized = false;
|
self.set_dirty(true);
|
||||||
self.set_dirty(true);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
match self.mode {
|
match self.mode {
|
||||||
ViewMode::Discard(ref mut selector) => {
|
ViewMode::Discard(ref mut selector) => {
|
||||||
|
@ -209,9 +197,7 @@ impl Component for ContactManager {
|
||||||
match self.form.buttons_result() {
|
match self.form.buttons_result() {
|
||||||
None => {}
|
None => {}
|
||||||
Some(true) => {
|
Some(true) => {
|
||||||
let fields = std::mem::replace(&mut self.form, FormWidget::default())
|
let fields = std::mem::take(&mut self.form).collect().unwrap();
|
||||||
.collect()
|
|
||||||
.unwrap();
|
|
||||||
let fields: HashMap<String, String> = fields
|
let fields: HashMap<String, String> = fields
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(s, v)| {
|
.map(|(s, v)| {
|
||||||
|
@ -290,7 +276,7 @@ impl Component for ContactManager {
|
||||||
|
|
||||||
let parent_id = self.parent_id;
|
let parent_id = self.parent_id;
|
||||||
/* Play it safe and ask user for confirmation */
|
/* Play it safe and ask user for confirmation */
|
||||||
self.mode = ViewMode::Discard(UIDialog::new(
|
self.mode = ViewMode::Discard(Box::new(UIDialog::new(
|
||||||
"this contact has unsaved changes",
|
"this contact has unsaved changes",
|
||||||
vec![
|
vec![
|
||||||
('x', "quit without saving".to_string()),
|
('x', "quit without saving".to_string()),
|
||||||
|
@ -305,7 +291,7 @@ impl Component for ContactManager {
|
||||||
_ => None,
|
_ => None,
|
||||||
})),
|
})),
|
||||||
context,
|
context,
|
||||||
));
|
)));
|
||||||
self.set_dirty(true);
|
self.set_dirty(true);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ enum ViewMode {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct AccountMenuEntry {
|
struct AccountMenuEntry {
|
||||||
name: String,
|
name: String,
|
||||||
hash: AccountHash,
|
_hash: AccountHash,
|
||||||
// Index in the config account vector.
|
// Index in the config account vector.
|
||||||
index: usize,
|
index: usize,
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,7 @@ impl ContactList {
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, (h, a))| AccountMenuEntry {
|
.map(|(i, (h, a))| AccountMenuEntry {
|
||||||
name: a.name().to_string(),
|
name: a.name().to_string(),
|
||||||
hash: *h,
|
_hash: *h,
|
||||||
index: i,
|
index: i,
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -216,7 +216,6 @@ impl ContactList {
|
||||||
((0, 0), (message.len() - 1, 0)),
|
((0, 0), (message.len() - 1, 0)),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,7 +240,7 @@ impl ContactList {
|
||||||
self.dirty = false;
|
self.dirty = false;
|
||||||
let mut y = get_y(upper_left);
|
let mut y = get_y(upper_left);
|
||||||
for a in &self.accounts {
|
for a in &self.accounts {
|
||||||
self.print_account(grid, (set_y(upper_left, y), bottom_right), &a, context);
|
self.print_account(grid, (set_y(upper_left, y), bottom_right), a, context);
|
||||||
y += 1;
|
y += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,6 +392,7 @@ impl ContactList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PageMovement::PageDown(multiplier) => {
|
PageMovement::PageDown(multiplier) => {
|
||||||
|
#[allow(clippy::comparison_chain)]
|
||||||
if self.new_cursor_pos + rows * multiplier < self.length {
|
if self.new_cursor_pos + rows * multiplier < self.length {
|
||||||
self.new_cursor_pos += rows * multiplier;
|
self.new_cursor_pos += rows * multiplier;
|
||||||
} else if self.new_cursor_pos + rows * multiplier > self.length {
|
} else if self.new_cursor_pos + rows * multiplier > self.length {
|
||||||
|
@ -782,7 +782,7 @@ impl Component for ContactList {
|
||||||
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
UIEvent::Input(Key::Char(c)) if c >= '0' && c <= '9' => {
|
UIEvent::Input(Key::Char(c)) if ('0'..='9').contains(&c) => {
|
||||||
self.cmd_buf.push(c);
|
self.cmd_buf.push(c);
|
||||||
context
|
context
|
||||||
.replies
|
.replies
|
||||||
|
|
|
@ -116,19 +116,11 @@ enum ViewMode {
|
||||||
|
|
||||||
impl ViewMode {
|
impl ViewMode {
|
||||||
fn is_edit(&self) -> bool {
|
fn is_edit(&self) -> bool {
|
||||||
if let ViewMode::Edit = self {
|
matches!(self, ViewMode::Edit)
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_edit_attachments(&self) -> bool {
|
fn is_edit_attachments(&self) -> bool {
|
||||||
if let ViewMode::EditAttachments { .. } = self {
|
matches!(self, ViewMode::EditAttachments { .. })
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,7 +152,7 @@ impl Composer {
|
||||||
form: FormWidget::default(),
|
form: FormWidget::default(),
|
||||||
mode: ViewMode::Edit,
|
mode: ViewMode::Edit,
|
||||||
#[cfg(feature = "gpgme")]
|
#[cfg(feature = "gpgme")]
|
||||||
gpg_state: gpg::GpgComposeState::new(),
|
gpg_state: gpg::GpgComposeState::default(),
|
||||||
dirty: true,
|
dirty: true,
|
||||||
has_changes: false,
|
has_changes: false,
|
||||||
embed_area: ((0, 0), (0, 0)),
|
embed_area: ((0, 0), (0, 0)),
|
||||||
|
@ -249,11 +241,7 @@ impl Composer {
|
||||||
ret.draft
|
ret.draft
|
||||||
.set_header("In-Reply-To", envelope.message_id_display().into());
|
.set_header("In-Reply-To", envelope.message_id_display().into());
|
||||||
|
|
||||||
if let Some(reply_to) = envelope
|
if let Some(reply_to) = envelope.other_headers().get("To").map(|v| v.as_str()) {
|
||||||
.other_headers()
|
|
||||||
.get("To")
|
|
||||||
.and_then(|v| v.as_str().try_into().ok())
|
|
||||||
{
|
|
||||||
let to: &str = reply_to;
|
let to: &str = reply_to;
|
||||||
let extra_identities = &account.settings.account.extra_identities;
|
let extra_identities = &account.settings.account.extra_identities;
|
||||||
if let Some(extra) = extra_identities
|
if let Some(extra) = extra_identities
|
||||||
|
@ -288,30 +276,26 @@ impl Composer {
|
||||||
.and_then(|v| v.as_str().try_into().ok())
|
.and_then(|v| v.as_str().try_into().ok())
|
||||||
{
|
{
|
||||||
to.insert(reply_to);
|
to.insert(reply_to);
|
||||||
|
} else if let Some(reply_to) = envelope
|
||||||
|
.other_headers()
|
||||||
|
.get("Reply-To")
|
||||||
|
.and_then(|v| v.as_str().try_into().ok())
|
||||||
|
{
|
||||||
|
to.insert(reply_to);
|
||||||
} else {
|
} else {
|
||||||
if let Some(reply_to) = envelope
|
to.extend(envelope.from().iter().cloned());
|
||||||
.other_headers()
|
|
||||||
.get("Reply-To")
|
|
||||||
.and_then(|v| v.as_str().try_into().ok())
|
|
||||||
{
|
|
||||||
to.insert(reply_to);
|
|
||||||
} else {
|
|
||||||
to.extend(envelope.from().iter().cloned());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
to.extend(envelope.to().iter().cloned());
|
to.extend(envelope.to().iter().cloned());
|
||||||
if let Some(ours) = TryInto::<Address>::try_into(
|
if let Ok(ours) = TryInto::<Address>::try_into(
|
||||||
crate::components::mail::get_display_name(context, coordinates.0).as_str(),
|
crate::components::mail::get_display_name(context, coordinates.0).as_str(),
|
||||||
)
|
) {
|
||||||
.ok()
|
|
||||||
{
|
|
||||||
to.remove(&ours);
|
to.remove(&ours);
|
||||||
}
|
}
|
||||||
ret.draft.set_header("To", {
|
ret.draft.set_header("To", {
|
||||||
let mut ret: String =
|
let mut ret: String =
|
||||||
to.into_iter()
|
to.into_iter()
|
||||||
.fold(String::new(), |mut s: String, n: Address| {
|
.fold(String::new(), |mut s: String, n: Address| {
|
||||||
s.extend(n.to_string().chars());
|
s.push_str(&n.to_string());
|
||||||
s.push_str(", ");
|
s.push_str(", ");
|
||||||
s
|
s
|
||||||
});
|
});
|
||||||
|
@ -320,14 +304,12 @@ impl Composer {
|
||||||
ret
|
ret
|
||||||
});
|
});
|
||||||
ret.draft.set_header("Cc", envelope.field_cc_to_string());
|
ret.draft.set_header("Cc", envelope.field_cc_to_string());
|
||||||
|
} else if let Some(reply_to) = envelope.other_headers().get("Mail-Reply-To") {
|
||||||
|
ret.draft.set_header("To", reply_to.to_string());
|
||||||
|
} else if let Some(reply_to) = envelope.other_headers().get("Reply-To") {
|
||||||
|
ret.draft.set_header("To", reply_to.to_string());
|
||||||
} else {
|
} else {
|
||||||
if let Some(reply_to) = envelope.other_headers().get("Mail-Reply-To") {
|
ret.draft.set_header("To", envelope.field_from_to_string());
|
||||||
ret.draft.set_header("To", reply_to.to_string());
|
|
||||||
} else if let Some(reply_to) = envelope.other_headers().get("Reply-To") {
|
|
||||||
ret.draft.set_header("To", reply_to.to_string());
|
|
||||||
} else {
|
|
||||||
ret.draft.set_header("To", envelope.field_from_to_string());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ret.draft.body = {
|
ret.draft.body = {
|
||||||
let mut ret = attribution_string(
|
let mut ret = attribution_string(
|
||||||
|
@ -481,7 +463,7 @@ To: {}
|
||||||
let header_values = self.form.values_mut();
|
let header_values = self.form.values_mut();
|
||||||
let draft_header_map = self.draft.headers_mut();
|
let draft_header_map = self.draft.headers_mut();
|
||||||
for (k, v) in draft_header_map.iter_mut() {
|
for (k, v) in draft_header_map.iter_mut() {
|
||||||
if let Some(ref vn) = header_values.get(k.as_str()) {
|
if let Some(vn) = header_values.get(k.as_str()) {
|
||||||
*v = vn.as_str().to_string();
|
*v = vn.as_str().to_string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -498,23 +480,22 @@ To: {}
|
||||||
if k == "To" || k == "Cc" || k == "Bcc" {
|
if k == "To" || k == "Cc" || k == "Bcc" {
|
||||||
self.form.push_cl((
|
self.form.push_cl((
|
||||||
k.into(),
|
k.into(),
|
||||||
headers[k].to_string().into(),
|
headers[k].to_string(),
|
||||||
Box::new(move |c, term| {
|
Box::new(move |c, term| {
|
||||||
let book: &AddressBook = &c.accounts[&account_hash].address_book;
|
let book: &AddressBook = &c.accounts[&account_hash].address_book;
|
||||||
let results: Vec<String> = book.search(term);
|
let results: Vec<String> = book.search(term);
|
||||||
results
|
results
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|r| AutoCompleteEntry::from(r))
|
.map(AutoCompleteEntry::from)
|
||||||
.collect::<Vec<AutoCompleteEntry>>()
|
.collect::<Vec<AutoCompleteEntry>>()
|
||||||
}),
|
}),
|
||||||
));
|
));
|
||||||
} else if k == "From" {
|
} else if k == "From" {
|
||||||
self.form.push_cl((
|
self.form.push_cl((
|
||||||
k.into(),
|
k.into(),
|
||||||
headers[k].to_string().into(),
|
headers[k].to_string(),
|
||||||
Box::new(move |c, _term| {
|
Box::new(move |c, _term| {
|
||||||
let results: Vec<(String, String)> = c
|
c.accounts
|
||||||
.accounts
|
|
||||||
.values()
|
.values()
|
||||||
.map(|acc| {
|
.map(|acc| {
|
||||||
let addr = if let Some(display_name) =
|
let addr = if let Some(display_name) =
|
||||||
|
@ -548,15 +529,12 @@ To: {}
|
||||||
|
|
||||||
(addr, desc)
|
(addr, desc)
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.map(AutoCompleteEntry::from)
|
||||||
results
|
|
||||||
.into_iter()
|
|
||||||
.map(|r| AutoCompleteEntry::from(r))
|
|
||||||
.collect::<Vec<AutoCompleteEntry>>()
|
.collect::<Vec<AutoCompleteEntry>>()
|
||||||
}),
|
}),
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
self.form.push((k.into(), headers[k].to_string().into()));
|
self.form.push((k.into(), headers[k].to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -858,7 +836,7 @@ impl Component for Composer {
|
||||||
clear_area(grid, embed_area, theme_default);
|
clear_area(grid, embed_area, theme_default);
|
||||||
copy_area(
|
copy_area(
|
||||||
grid,
|
grid,
|
||||||
&guard.grid.buffer(),
|
guard.grid.buffer(),
|
||||||
embed_area,
|
embed_area,
|
||||||
((0, 0), pos_dec(guard.grid.terminal_size, (1, 1))),
|
((0, 0), pos_dec(guard.grid.terminal_size, (1, 1))),
|
||||||
);
|
);
|
||||||
|
@ -871,7 +849,7 @@ impl Component for Composer {
|
||||||
let guard = embed_pty.lock().unwrap();
|
let guard = embed_pty.lock().unwrap();
|
||||||
copy_area(
|
copy_area(
|
||||||
grid,
|
grid,
|
||||||
&guard.grid.buffer(),
|
guard.grid.buffer(),
|
||||||
embed_area,
|
embed_area,
|
||||||
((0, 0), pos_dec(guard.grid.terminal_size, (1, 1))),
|
((0, 0), pos_dec(guard.grid.terminal_size, (1, 1))),
|
||||||
);
|
);
|
||||||
|
@ -1120,8 +1098,7 @@ impl Component for Composer {
|
||||||
UIEvent::FinishedUIDialog(id, ref mut result),
|
UIEvent::FinishedUIDialog(id, ref mut result),
|
||||||
) if selector.id() == *id => {
|
) if selector.id() == *id => {
|
||||||
if let Some(to_val) = result.downcast_mut::<String>() {
|
if let Some(to_val) = result.downcast_mut::<String>() {
|
||||||
self.draft
|
self.draft.set_header("To", std::mem::take(to_val));
|
||||||
.set_header("To", std::mem::replace(to_val, String::new()));
|
|
||||||
self.update_form();
|
self.update_form();
|
||||||
}
|
}
|
||||||
self.mode = ViewMode::Edit;
|
self.mode = ViewMode::Edit;
|
||||||
|
@ -1677,7 +1654,7 @@ impl Component for Composer {
|
||||||
log(
|
log(
|
||||||
format!(
|
format!(
|
||||||
"Executing: sh -c \"{}\"",
|
"Executing: sh -c \"{}\"",
|
||||||
editor_command.replace("\"", "\\\"")
|
editor_command.replace('"', "\\\"")
|
||||||
),
|
),
|
||||||
DEBUG,
|
DEBUG,
|
||||||
);
|
);
|
||||||
|
@ -1804,9 +1781,10 @@ impl Component for Composer {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Action::Compose(ComposeAction::AddAttachmentFilePicker(ref command)) => {
|
Action::Compose(ComposeAction::AddAttachmentFilePicker(ref command)) => {
|
||||||
let command = if let Some(ref cmd) = command
|
let command = if let Some(cmd) =
|
||||||
.as_ref()
|
command
|
||||||
.or_else(|| context.settings.terminal.file_picker_command.as_ref())
|
.as_ref()
|
||||||
|
.or(context.settings.terminal.file_picker_command.as_ref())
|
||||||
{
|
{
|
||||||
cmd.as_str()
|
cmd.as_str()
|
||||||
} else {
|
} else {
|
||||||
|
@ -1823,7 +1801,7 @@ impl Component for Composer {
|
||||||
}
|
}
|
||||||
|
|
||||||
log(
|
log(
|
||||||
format!("Executing: sh -c \"{}\"", command.replace("\"", "\\\"")),
|
format!("Executing: sh -c \"{}\"", command.replace('"', "\\\"")),
|
||||||
DEBUG,
|
DEBUG,
|
||||||
);
|
);
|
||||||
match Command::new("sh")
|
match Command::new("sh")
|
||||||
|
@ -1838,7 +1816,7 @@ impl Component for Composer {
|
||||||
debug!(&String::from_utf8_lossy(&stderr));
|
debug!(&String::from_utf8_lossy(&stderr));
|
||||||
for path in stderr.split(|c| [b'\0', b'\t', b'\n'].contains(c)) {
|
for path in stderr.split(|c| [b'\0', b'\t', b'\n'].contains(c)) {
|
||||||
match melib::email::compose::attachment_from_file(
|
match melib::email::compose::attachment_from_file(
|
||||||
&String::from_utf8_lossy(&path).as_ref(),
|
&String::from_utf8_lossy(path).as_ref(),
|
||||||
) {
|
) {
|
||||||
Ok(a) => {
|
Ok(a) => {
|
||||||
self.draft.attachments_mut().push(a);
|
self.draft.attachments_mut().push(a);
|
||||||
|
@ -1848,7 +1826,7 @@ impl Component for Composer {
|
||||||
context.replies.push_back(UIEvent::Notification(
|
context.replies.push_back(UIEvent::Notification(
|
||||||
Some(format!(
|
Some(format!(
|
||||||
"could not add attachment: {}",
|
"could not add attachment: {}",
|
||||||
String::from_utf8_lossy(&path)
|
String::from_utf8_lossy(path)
|
||||||
)),
|
)),
|
||||||
err.to_string(),
|
err.to_string(),
|
||||||
Some(NotificationType::Error(
|
Some(NotificationType::Error(
|
||||||
|
@ -1984,7 +1962,7 @@ impl Component for Composer {
|
||||||
Some(Box::new(move |id: ComponentId, results: &[char]| {
|
Some(Box::new(move |id: ComponentId, results: &[char]| {
|
||||||
Some(UIEvent::FinishedUIDialog(
|
Some(UIEvent::FinishedUIDialog(
|
||||||
id,
|
id,
|
||||||
Box::new(results.get(0).map(|c| *c).unwrap_or('n')),
|
Box::new(results.get(0).copied().unwrap_or('n')),
|
||||||
))
|
))
|
||||||
})),
|
})),
|
||||||
context,
|
context,
|
||||||
|
@ -2033,7 +2011,7 @@ impl Component for Composer {
|
||||||
Some(Box::new(move |id: ComponentId, results: &[char]| {
|
Some(Box::new(move |id: ComponentId, results: &[char]| {
|
||||||
Some(UIEvent::FinishedUIDialog(
|
Some(UIEvent::FinishedUIDialog(
|
||||||
id,
|
id,
|
||||||
Box::new(results.get(0).map(|c| *c).unwrap_or('n')),
|
Box::new(results.get(0).copied().unwrap_or('n')),
|
||||||
))
|
))
|
||||||
})),
|
})),
|
||||||
context,
|
context,
|
||||||
|
@ -2136,7 +2114,7 @@ pub fn send_draft(
|
||||||
let body: AttachmentBuilder = Attachment::new(
|
let body: AttachmentBuilder = Attachment::new(
|
||||||
content_type,
|
content_type,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
std::mem::replace(&mut draft.body, String::new()).into_bytes(),
|
std::mem::take(&mut draft.body).into_bytes(),
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
draft.attachments.insert(0, body);
|
draft.attachments.insert(0, body);
|
||||||
|
@ -2195,6 +2173,7 @@ pub fn send_draft_async(
|
||||||
let format_flowed = *account_settings!(context[account_hash].composing.format_flowed);
|
let format_flowed = *account_settings!(context[account_hash].composing.format_flowed);
|
||||||
let event_sender = context.sender.clone();
|
let event_sender = context.sender.clone();
|
||||||
#[cfg(feature = "gpgme")]
|
#[cfg(feature = "gpgme")]
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
let mut filters_stack: Vec<
|
let mut filters_stack: Vec<
|
||||||
Box<
|
Box<
|
||||||
dyn FnOnce(
|
dyn FnOnce(
|
||||||
|
@ -2207,7 +2186,7 @@ pub fn send_draft_async(
|
||||||
#[cfg(feature = "gpgme")]
|
#[cfg(feature = "gpgme")]
|
||||||
if gpg_state.sign_mail.is_true() && !gpg_state.encrypt_mail.is_true() {
|
if gpg_state.sign_mail.is_true() && !gpg_state.encrypt_mail.is_true() {
|
||||||
filters_stack.push(Box::new(crate::components::mail::pgp::sign_filter(
|
filters_stack.push(Box::new(crate::components::mail::pgp::sign_filter(
|
||||||
gpg_state.sign_keys.clone(),
|
gpg_state.sign_keys,
|
||||||
)?));
|
)?));
|
||||||
} else if gpg_state.encrypt_mail.is_true() {
|
} else if gpg_state.encrypt_mail.is_true() {
|
||||||
filters_stack.push(Box::new(crate::components::mail::pgp::encrypt_filter(
|
filters_stack.push(Box::new(crate::components::mail::pgp::encrypt_filter(
|
||||||
|
@ -2216,7 +2195,7 @@ pub fn send_draft_async(
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
gpg_state.encrypt_keys.clone(),
|
gpg_state.encrypt_keys,
|
||||||
)?));
|
)?));
|
||||||
}
|
}
|
||||||
let send_mail = account_settings!(context[account_hash].composing.send_mail).clone();
|
let send_mail = account_settings!(context[account_hash].composing.send_mail).clone();
|
||||||
|
@ -2233,11 +2212,11 @@ pub fn send_draft_async(
|
||||||
let mut body: AttachmentBuilder = Attachment::new(
|
let mut body: AttachmentBuilder = Attachment::new(
|
||||||
content_type,
|
content_type,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
std::mem::replace(&mut draft.body, String::new()).into_bytes(),
|
std::mem::take(&mut draft.body).into_bytes(),
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
if !draft.attachments.is_empty() {
|
if !draft.attachments.is_empty() {
|
||||||
let mut parts = std::mem::replace(&mut draft.attachments, Vec::new());
|
let mut parts = std::mem::take(&mut draft.attachments);
|
||||||
parts.insert(0, body);
|
parts.insert(0, body);
|
||||||
let boundary = ContentType::make_boundary(&parts);
|
let boundary = ContentType::make_boundary(&parts);
|
||||||
body = Attachment::new(
|
body = Attachment::new(
|
||||||
|
@ -2261,7 +2240,7 @@ pub fn send_draft_async(
|
||||||
let message = Arc::new(draft.finalise()?);
|
let message = Arc::new(draft.finalise()?);
|
||||||
let ret = send_cb(message.clone()).await;
|
let ret = send_cb(message.clone()).await;
|
||||||
let is_ok = ret.is_ok();
|
let is_ok = ret.is_ok();
|
||||||
if !is_ok || (store_sent_mail && is_ok) {
|
if !is_ok || store_sent_mail {
|
||||||
event_sender
|
event_sender
|
||||||
.send(ThreadEvent::UIEvent(UIEvent::Callback(CallbackFn(
|
.send(ThreadEvent::UIEvent(UIEvent::Callback(CallbackFn(
|
||||||
Box::new(move |context| {
|
Box::new(move |context| {
|
||||||
|
|
|
@ -37,7 +37,7 @@ impl Default for EditAttachmentCursor {
|
||||||
pub enum EditAttachmentMode {
|
pub enum EditAttachmentMode {
|
||||||
Overview,
|
Overview,
|
||||||
Edit {
|
Edit {
|
||||||
inner: FormWidget<FormButtonActions>,
|
inner: Box<FormWidget<FormButtonActions>>,
|
||||||
no: usize,
|
no: usize,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ impl EditAttachments {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EditAttachmentsRefMut<'_, '_> {
|
impl EditAttachmentsRefMut<'_, '_> {
|
||||||
fn new_edit_widget(&self, no: usize) -> Option<FormWidget<FormButtonActions>> {
|
fn new_edit_widget(&self, no: usize) -> Option<Box<FormWidget<FormButtonActions>>> {
|
||||||
if no >= self.draft.attachments().len() {
|
if no >= self.draft.attachments().len() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ impl EditAttachmentsRefMut<'_, '_> {
|
||||||
ret.add_button(("Cancel".into(), FormButtonActions::Cancel));
|
ret.add_button(("Cancel".into(), FormButtonActions::Cancel));
|
||||||
ret.push(("Filename".into(), filename.unwrap_or_default().to_string()));
|
ret.push(("Filename".into(), filename.unwrap_or_default().to_string()));
|
||||||
ret.push(("Mime type".into(), mime_type.to_string()));
|
ret.push(("Mime type".into(), mime_type.to_string()));
|
||||||
Some(ret)
|
Some(Box::new(ret))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ pub enum KeySelection {
|
||||||
err: MeliError,
|
err: MeliError,
|
||||||
},
|
},
|
||||||
Loaded {
|
Loaded {
|
||||||
widget: UIDialog<melib::gpgme::Key>,
|
widget: Box<UIDialog<melib::gpgme::Key>>,
|
||||||
keys: Vec<melib::gpgme::Key>,
|
keys: Vec<melib::gpgme::Key>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,7 @@ impl Component for KeySelection {
|
||||||
match Self::new(
|
match Self::new(
|
||||||
*secret,
|
*secret,
|
||||||
*local,
|
*local,
|
||||||
std::mem::replace(pattern, String::new()),
|
std::mem::take(pattern),
|
||||||
*allow_remote_lookup,
|
*allow_remote_lookup,
|
||||||
context,
|
context,
|
||||||
) {
|
) {
|
||||||
|
@ -166,7 +166,7 @@ impl Component for KeySelection {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let mut widget = UIDialog::new(
|
let mut widget = Box::new(UIDialog::new(
|
||||||
"select key",
|
"select key",
|
||||||
keys.iter()
|
keys.iter()
|
||||||
.map(|k| {
|
.map(|k| {
|
||||||
|
@ -185,12 +185,12 @@ impl Component for KeySelection {
|
||||||
move |id: ComponentId, results: &[melib::gpgme::Key]| {
|
move |id: ComponentId, results: &[melib::gpgme::Key]| {
|
||||||
Some(UIEvent::FinishedUIDialog(
|
Some(UIEvent::FinishedUIDialog(
|
||||||
id,
|
id,
|
||||||
Box::new(results.get(0).map(|k| k.clone())),
|
Box::new(results.get(0).cloned()),
|
||||||
))
|
))
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
context,
|
context,
|
||||||
);
|
));
|
||||||
widget.set_dirty(true);
|
widget.set_dirty(true);
|
||||||
*self = KeySelection::Loaded { widget, keys };
|
*self = KeySelection::Loaded { widget, keys };
|
||||||
}
|
}
|
||||||
|
@ -275,8 +275,8 @@ pub struct GpgComposeState {
|
||||||
pub sign_keys: Vec<melib::gpgme::Key>,
|
pub sign_keys: Vec<melib::gpgme::Key>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GpgComposeState {
|
impl Default for GpgComposeState {
|
||||||
pub fn new() -> Self {
|
fn default() -> Self {
|
||||||
GpgComposeState {
|
GpgComposeState {
|
||||||
sign_mail: ToggleFlag::Unset,
|
sign_mail: ToggleFlag::Unset,
|
||||||
encrypt_mail: ToggleFlag::Unset,
|
encrypt_mail: ToggleFlag::Unset,
|
||||||
|
|
|
@ -176,7 +176,6 @@ struct AccountMenuEntry {
|
||||||
name: String,
|
name: String,
|
||||||
hash: AccountHash,
|
hash: AccountHash,
|
||||||
index: usize,
|
index: usize,
|
||||||
visible: bool,
|
|
||||||
entries: SmallVec<[MailboxMenuEntry; 16]>,
|
entries: SmallVec<[MailboxMenuEntry; 16]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -380,7 +379,7 @@ pub trait MailListingTrait: ListingTrait {
|
||||||
.map(|&env_hash| account.operation(env_hash).and_then(|mut op| op.as_bytes()))
|
.map(|&env_hash| account.operation(env_hash).and_then(|mut op| op.as_bytes()))
|
||||||
.collect::<Result<Vec<_>>>();
|
.collect::<Result<Vec<_>>>();
|
||||||
let path_ = path.to_path_buf();
|
let path_ = path.to_path_buf();
|
||||||
let format = format.clone().unwrap_or_default();
|
let format = (*format).unwrap_or_default();
|
||||||
let collection = account.collection.clone();
|
let collection = account.collection.clone();
|
||||||
let (sender, mut receiver) = crate::jobs::oneshot::channel();
|
let (sender, mut receiver) = crate::jobs::oneshot::channel();
|
||||||
let fut: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>> =
|
let fut: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>> =
|
||||||
|
@ -521,11 +520,11 @@ pub trait ListingTrait: Component {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ListingComponent {
|
pub enum ListingComponent {
|
||||||
Plain(PlainListing),
|
Plain(Box<PlainListing>),
|
||||||
Threaded(ThreadListing),
|
Threaded(Box<ThreadListing>),
|
||||||
Compact(CompactListing),
|
Compact(Box<CompactListing>),
|
||||||
Conversations(ConversationsListing),
|
Conversations(Box<ConversationsListing>),
|
||||||
Offline(OfflineListing),
|
Offline(Box<OfflineListing>),
|
||||||
}
|
}
|
||||||
use crate::ListingComponent::*;
|
use crate::ListingComponent::*;
|
||||||
|
|
||||||
|
@ -534,11 +533,11 @@ impl core::ops::Deref for ListingComponent {
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
match &self {
|
match &self {
|
||||||
Compact(ref l) => l,
|
Compact(ref l) => l.as_ref(),
|
||||||
Plain(ref l) => l,
|
Plain(ref l) => l.as_ref(),
|
||||||
Threaded(ref l) => l,
|
Threaded(ref l) => l.as_ref(),
|
||||||
Conversations(ref l) => l,
|
Conversations(ref l) => l.as_ref(),
|
||||||
Offline(ref l) => l,
|
Offline(ref l) => l.as_ref(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -546,11 +545,11 @@ impl core::ops::Deref for ListingComponent {
|
||||||
impl core::ops::DerefMut for ListingComponent {
|
impl core::ops::DerefMut for ListingComponent {
|
||||||
fn deref_mut(&mut self) -> &mut (dyn MailListingTrait + 'static) {
|
fn deref_mut(&mut self) -> &mut (dyn MailListingTrait + 'static) {
|
||||||
match self {
|
match self {
|
||||||
Compact(l) => l,
|
Compact(l) => l.as_mut(),
|
||||||
Plain(l) => l,
|
Plain(l) => l.as_mut(),
|
||||||
Threaded(l) => l,
|
Threaded(l) => l.as_mut(),
|
||||||
Conversations(l) => l,
|
Conversations(l) => l.as_mut(),
|
||||||
Offline(l) => l,
|
Offline(l) => l.as_mut(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -611,7 +610,6 @@ pub struct Listing {
|
||||||
accounts: Vec<AccountMenuEntry>,
|
accounts: Vec<AccountMenuEntry>,
|
||||||
status: Option<AccountStatus>,
|
status: Option<AccountStatus>,
|
||||||
dirty: bool,
|
dirty: bool,
|
||||||
visible: bool,
|
|
||||||
cursor_pos: (usize, MenuEntryCursor),
|
cursor_pos: (usize, MenuEntryCursor),
|
||||||
menu_cursor_pos: (usize, MenuEntryCursor),
|
menu_cursor_pos: (usize, MenuEntryCursor),
|
||||||
menu_content: CellBuffer,
|
menu_content: CellBuffer,
|
||||||
|
@ -755,12 +753,10 @@ impl Component for Listing {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
UIEvent::StartupCheck(ref f) => {
|
UIEvent::StartupCheck(ref f)
|
||||||
if self.component.coordinates().1 == *f {
|
if self.component.coordinates().1 == *f && !self.startup_checks_rate.tick() =>
|
||||||
if !self.startup_checks_rate.tick() {
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
UIEvent::Timer(n) if *n == self.startup_checks_rate.id() => {
|
UIEvent::Timer(n) if *n == self.startup_checks_rate.id() => {
|
||||||
if self.startup_checks_rate.active {
|
if self.startup_checks_rate.active {
|
||||||
|
@ -1127,7 +1123,7 @@ impl Component for Listing {
|
||||||
Action::Listing(ListingAction::Import(file_path, mailbox_path)) => {
|
Action::Listing(ListingAction::Import(file_path, mailbox_path)) => {
|
||||||
let account = &mut context.accounts[self.cursor_pos.0];
|
let account = &mut context.accounts[self.cursor_pos.0];
|
||||||
if let Err(err) = account
|
if let Err(err) = account
|
||||||
.mailbox_by_path(&mailbox_path)
|
.mailbox_by_path(mailbox_path)
|
||||||
.and_then(|mailbox_hash| {
|
.and_then(|mailbox_hash| {
|
||||||
Ok((
|
Ok((
|
||||||
std::fs::read(&file_path).chain_err_summary(|| {
|
std::fs::read(&file_path).chain_err_summary(|| {
|
||||||
|
@ -1687,7 +1683,7 @@ impl Component for Listing {
|
||||||
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
UIEvent::Input(Key::Char(c)) if c >= '0' && c <= '9' => {
|
UIEvent::Input(Key::Char(c)) if ('0'..='9').contains(&c) => {
|
||||||
self.cmd_buf.push(c);
|
self.cmd_buf.push(c);
|
||||||
self.component.set_modifier_active(true);
|
self.component.set_modifier_active(true);
|
||||||
context
|
context
|
||||||
|
@ -1808,7 +1804,6 @@ impl Listing {
|
||||||
name: a.name().to_string(),
|
name: a.name().to_string(),
|
||||||
hash: *h,
|
hash: *h,
|
||||||
index: i,
|
index: i,
|
||||||
visible: true,
|
|
||||||
entries,
|
entries,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1818,7 +1813,6 @@ impl Listing {
|
||||||
component: Offline(OfflineListing::new((first_account_hash, 0))),
|
component: Offline(OfflineListing::new((first_account_hash, 0))),
|
||||||
accounts: account_entries,
|
accounts: account_entries,
|
||||||
status: None,
|
status: None,
|
||||||
visible: true,
|
|
||||||
dirty: true,
|
dirty: true,
|
||||||
cursor_pos: (0, MenuEntryCursor::Mailbox(0)),
|
cursor_pos: (0, MenuEntryCursor::Mailbox(0)),
|
||||||
menu_cursor_pos: (0, MenuEntryCursor::Mailbox(0)),
|
menu_cursor_pos: (0, MenuEntryCursor::Mailbox(0)),
|
||||||
|
@ -1954,7 +1948,6 @@ impl Listing {
|
||||||
|
|
||||||
#[derive(Copy, Debug, Clone)]
|
#[derive(Copy, Debug, Clone)]
|
||||||
struct Line {
|
struct Line {
|
||||||
visible: bool,
|
|
||||||
collapsed: bool,
|
collapsed: bool,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
inc: usize,
|
inc: usize,
|
||||||
|
@ -1990,7 +1983,7 @@ impl Listing {
|
||||||
indentation,
|
indentation,
|
||||||
has_sibling,
|
has_sibling,
|
||||||
mailbox_hash,
|
mailbox_hash,
|
||||||
visible,
|
visible: _,
|
||||||
collapsed,
|
collapsed,
|
||||||
},
|
},
|
||||||
) in self.accounts[aidx].entries.iter().enumerate()
|
) in self.accounts[aidx].entries.iter().enumerate()
|
||||||
|
@ -1999,7 +1992,6 @@ impl Listing {
|
||||||
match context.accounts[self.accounts[aidx].index][&mailbox_hash].status {
|
match context.accounts[self.accounts[aidx].index][&mailbox_hash].status {
|
||||||
crate::conf::accounts::MailboxStatus::Failed(_) => {
|
crate::conf::accounts::MailboxStatus::Failed(_) => {
|
||||||
lines.push(Line {
|
lines.push(Line {
|
||||||
visible,
|
|
||||||
collapsed,
|
collapsed,
|
||||||
depth,
|
depth,
|
||||||
inc: i,
|
inc: i,
|
||||||
|
@ -2012,7 +2004,6 @@ impl Listing {
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
lines.push(Line {
|
lines.push(Line {
|
||||||
visible,
|
|
||||||
collapsed,
|
collapsed,
|
||||||
depth,
|
depth,
|
||||||
inc: i,
|
inc: i,
|
||||||
|
|
|
@ -172,9 +172,12 @@ pub struct CompactListing {
|
||||||
/// Cache current view.
|
/// Cache current view.
|
||||||
data_columns: DataColumns,
|
data_columns: DataColumns,
|
||||||
rows_drawn: SegmentTree,
|
rows_drawn: SegmentTree,
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
rows: Vec<((usize, (ThreadHash, EnvelopeHash)), EntryStrings)>,
|
rows: Vec<((usize, (ThreadHash, EnvelopeHash)), EntryStrings)>,
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
search_job: Option<(String, JoinHandle<Result<SmallVec<[EnvelopeHash; 512]>>>)>,
|
search_job: Option<(String, JoinHandle<Result<SmallVec<[EnvelopeHash; 512]>>>)>,
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
select_job: Option<(String, JoinHandle<Result<SmallVec<[EnvelopeHash; 512]>>>)>,
|
select_job: Option<(String, JoinHandle<Result<SmallVec<[EnvelopeHash; 512]>>>)>,
|
||||||
filter_term: String,
|
filter_term: String,
|
||||||
filtered_selection: Vec<ThreadHash>,
|
filtered_selection: Vec<ThreadHash>,
|
||||||
|
@ -220,7 +223,7 @@ impl MailListingTrait for CompactListing {
|
||||||
.flatten()
|
.flatten()
|
||||||
.chain(cursor_iter.into_iter().flatten())
|
.chain(cursor_iter.into_iter().flatten())
|
||||||
.cloned();
|
.cloned();
|
||||||
SmallVec::from_iter(iter.into_iter())
|
SmallVec::from_iter(iter)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fill the `self.data_columns` `CellBuffers` with the contents of the account mailbox the user has
|
/// Fill the `self.data_columns` `CellBuffers` with the contents of the account mailbox the user has
|
||||||
|
@ -324,6 +327,7 @@ impl MailListingTrait for CompactListing {
|
||||||
self.length = 0;
|
self.length = 0;
|
||||||
let mut rows = Vec::with_capacity(1024);
|
let mut rows = Vec::with_capacity(1024);
|
||||||
let mut min_width = (0, 0, 0, 0);
|
let mut min_width = (0, 0, 0, 0);
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
let mut row_widths: (
|
let mut row_widths: (
|
||||||
SmallVec<[u8; 1024]>,
|
SmallVec<[u8; 1024]>,
|
||||||
SmallVec<[u8; 1024]>,
|
SmallVec<[u8; 1024]>,
|
||||||
|
@ -404,11 +408,7 @@ impl MailListingTrait for CompactListing {
|
||||||
);
|
);
|
||||||
/* subject */
|
/* subject */
|
||||||
row_widths.3.push(
|
row_widths.3.push(
|
||||||
(entry_strings
|
(entry_strings.flag.grapheme_width()
|
||||||
.flag
|
|
||||||
.grapheme_width()
|
|
||||||
.try_into()
|
|
||||||
.unwrap_or(255)
|
|
||||||
+ 1
|
+ 1
|
||||||
+ entry_strings.subject.grapheme_width()
|
+ entry_strings.subject.grapheme_width()
|
||||||
+ 1
|
+ 1
|
||||||
|
@ -876,8 +876,8 @@ impl fmt::Display for CompactListing {
|
||||||
|
|
||||||
impl CompactListing {
|
impl CompactListing {
|
||||||
pub const DESCRIPTION: &'static str = "compact listing";
|
pub const DESCRIPTION: &'static str = "compact listing";
|
||||||
pub fn new(coordinates: (AccountHash, MailboxHash)) -> Self {
|
pub fn new(coordinates: (AccountHash, MailboxHash)) -> Box<Self> {
|
||||||
CompactListing {
|
Box::new(CompactListing {
|
||||||
cursor_pos: (coordinates.0, 1, 0),
|
cursor_pos: (coordinates.0, 1, 0),
|
||||||
new_cursor_pos: (coordinates.0, coordinates.1, 0),
|
new_cursor_pos: (coordinates.0, coordinates.1, 0),
|
||||||
length: 0,
|
length: 0,
|
||||||
|
@ -905,8 +905,9 @@ impl CompactListing {
|
||||||
modifier_active: false,
|
modifier_active: false,
|
||||||
modifier_command: None,
|
modifier_command: None,
|
||||||
id: ComponentId::new_v4(),
|
id: ComponentId::new_v4(),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_entry_string(
|
fn make_entry_string(
|
||||||
&self,
|
&self,
|
||||||
e: &Envelope,
|
e: &Envelope,
|
||||||
|
@ -1423,7 +1424,7 @@ impl CompactListing {
|
||||||
}
|
}
|
||||||
/* Set fg color for flags */
|
/* Set fg color for flags */
|
||||||
let mut x = 0;
|
let mut x = 0;
|
||||||
if self.selection.get(&thread_hash).cloned().unwrap_or(false) {
|
if self.selection.get(thread_hash).cloned().unwrap_or(false) {
|
||||||
x += selected_flag_len;
|
x += selected_flag_len;
|
||||||
}
|
}
|
||||||
if thread.snoozed() {
|
if thread.snoozed() {
|
||||||
|
@ -1887,7 +1888,7 @@ impl Component for CompactListing {
|
||||||
UIEvent::EnvelopeRename(ref old_hash, ref new_hash) => {
|
UIEvent::EnvelopeRename(ref old_hash, ref new_hash) => {
|
||||||
let account = &context.accounts[&self.cursor_pos.0];
|
let account = &context.accounts[&self.cursor_pos.0];
|
||||||
let threads = account.collection.get_threads(self.cursor_pos.1);
|
let threads = account.collection.get_threads(self.cursor_pos.1);
|
||||||
if !account.collection.contains_key(&new_hash) {
|
if !account.collection.contains_key(new_hash) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let new_env_thread_node_hash = account.collection.get_env(*new_hash).thread();
|
let new_env_thread_node_hash = account.collection.get_env(*new_hash).thread();
|
||||||
|
@ -1917,7 +1918,7 @@ impl Component for CompactListing {
|
||||||
UIEvent::EnvelopeUpdate(ref env_hash) => {
|
UIEvent::EnvelopeUpdate(ref env_hash) => {
|
||||||
let account = &context.accounts[&self.cursor_pos.0];
|
let account = &context.accounts[&self.cursor_pos.0];
|
||||||
let threads = account.collection.get_threads(self.cursor_pos.1);
|
let threads = account.collection.get_threads(self.cursor_pos.1);
|
||||||
if !account.collection.contains_key(&env_hash) {
|
if !account.collection.contains_key(env_hash) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let new_env_thread_node_hash = account.collection.get_env(*env_hash).thread();
|
let new_env_thread_node_hash = account.collection.get_env(*env_hash).thread();
|
||||||
|
|
|
@ -101,8 +101,10 @@ pub struct ConversationsListing {
|
||||||
subsort: (SortField, SortOrder),
|
subsort: (SortField, SortOrder),
|
||||||
all_threads: HashSet<ThreadHash>,
|
all_threads: HashSet<ThreadHash>,
|
||||||
order: HashMap<ThreadHash, usize>,
|
order: HashMap<ThreadHash, usize>,
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
rows: std::result::Result<Vec<((usize, (ThreadHash, EnvelopeHash)), EntryStrings)>, String>,
|
rows: std::result::Result<Vec<((usize, (ThreadHash, EnvelopeHash)), EntryStrings)>, String>,
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
search_job: Option<(String, JoinHandle<Result<SmallVec<[EnvelopeHash; 512]>>>)>,
|
search_job: Option<(String, JoinHandle<Result<SmallVec<[EnvelopeHash; 512]>>>)>,
|
||||||
filter_term: String,
|
filter_term: String,
|
||||||
filtered_selection: Vec<ThreadHash>,
|
filtered_selection: Vec<ThreadHash>,
|
||||||
|
@ -148,7 +150,7 @@ impl MailListingTrait for ConversationsListing {
|
||||||
.flatten()
|
.flatten()
|
||||||
.chain(cursor_iter.into_iter().flatten())
|
.chain(cursor_iter.into_iter().flatten())
|
||||||
.cloned();
|
.cloned();
|
||||||
SmallVec::from_iter(iter.into_iter())
|
SmallVec::from_iter(iter)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn refresh_mailbox(&mut self, context: &mut Context, force: bool) {
|
fn refresh_mailbox(&mut self, context: &mut Context, force: bool) {
|
||||||
|
@ -376,7 +378,7 @@ impl ListingTrait for ConversationsListing {
|
||||||
if let Err(message) = self.rows.as_ref() {
|
if let Err(message) = self.rows.as_ref() {
|
||||||
clear_area(grid, area, self.color_cache.theme_default);
|
clear_area(grid, area, self.color_cache.theme_default);
|
||||||
write_string_to_grid(
|
write_string_to_grid(
|
||||||
&message,
|
message,
|
||||||
grid,
|
grid,
|
||||||
self.color_cache.theme_default.fg,
|
self.color_cache.theme_default.fg,
|
||||||
self.color_cache.theme_default.bg,
|
self.color_cache.theme_default.bg,
|
||||||
|
@ -584,8 +586,8 @@ impl ConversationsListing {
|
||||||
const DESCRIPTION: &'static str = "conversations listing";
|
const DESCRIPTION: &'static str = "conversations listing";
|
||||||
//const PADDING_CHAR: char = ' '; //░';
|
//const PADDING_CHAR: char = ' '; //░';
|
||||||
|
|
||||||
pub fn new(coordinates: (AccountHash, MailboxHash)) -> Self {
|
pub fn new(coordinates: (AccountHash, MailboxHash)) -> Box<Self> {
|
||||||
ConversationsListing {
|
Box::new(ConversationsListing {
|
||||||
cursor_pos: (coordinates.0, 1, 0),
|
cursor_pos: (coordinates.0, 1, 0),
|
||||||
new_cursor_pos: (coordinates.0, coordinates.1, 0),
|
new_cursor_pos: (coordinates.0, coordinates.1, 0),
|
||||||
length: 0,
|
length: 0,
|
||||||
|
@ -609,7 +611,7 @@ impl ConversationsListing {
|
||||||
modifier_active: false,
|
modifier_active: false,
|
||||||
modifier_command: None,
|
modifier_command: None,
|
||||||
id: ComponentId::new_v4(),
|
id: ComponentId::new_v4(),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn make_entry_string(
|
pub(super) fn make_entry_string(
|
||||||
|
@ -714,8 +716,7 @@ impl ConversationsListing {
|
||||||
.settings
|
.settings
|
||||||
.listing
|
.listing
|
||||||
.datetime_fmt
|
.datetime_fmt
|
||||||
.as_ref()
|
.as_deref()
|
||||||
.map(String::as_str)
|
|
||||||
.or(Some("%Y-%m-%d %T")),
|
.or(Some("%Y-%m-%d %T")),
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
|
@ -1226,7 +1227,7 @@ impl Component for ConversationsListing {
|
||||||
UIEvent::EnvelopeRename(ref old_hash, ref new_hash) => {
|
UIEvent::EnvelopeRename(ref old_hash, ref new_hash) => {
|
||||||
let account = &context.accounts[&self.cursor_pos.0];
|
let account = &context.accounts[&self.cursor_pos.0];
|
||||||
let threads = account.collection.get_threads(self.cursor_pos.1);
|
let threads = account.collection.get_threads(self.cursor_pos.1);
|
||||||
if !account.collection.contains_key(&new_hash) {
|
if !account.collection.contains_key(new_hash) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let env_thread_node_hash = account.collection.get_env(*new_hash).thread();
|
let env_thread_node_hash = account.collection.get_env(*new_hash).thread();
|
||||||
|
@ -1258,7 +1259,7 @@ impl Component for ConversationsListing {
|
||||||
UIEvent::EnvelopeUpdate(ref env_hash) => {
|
UIEvent::EnvelopeUpdate(ref env_hash) => {
|
||||||
let account = &context.accounts[&self.cursor_pos.0];
|
let account = &context.accounts[&self.cursor_pos.0];
|
||||||
let threads = account.collection.get_threads(self.cursor_pos.1);
|
let threads = account.collection.get_threads(self.cursor_pos.1);
|
||||||
if !account.collection.contains_key(&env_hash) {
|
if !account.collection.contains_key(env_hash) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let env_thread_node_hash = account.collection.get_env(*env_hash).thread();
|
let env_thread_node_hash = account.collection.get_env(*env_hash).thread();
|
||||||
|
|
|
@ -41,7 +41,7 @@ impl MailListingTrait for OfflineListing {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_focused_items(&self, _context: &Context) -> SmallVec<[ThreadHash; 8]> {
|
fn get_focused_items(&self, _context: &Context) -> SmallVec<[ThreadHash; 8]> {
|
||||||
return SmallVec::new();
|
SmallVec::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn refresh_mailbox(&mut self, _context: &mut Context, _force: bool) {}
|
fn refresh_mailbox(&mut self, _context: &mut Context, _force: bool) {}
|
||||||
|
@ -93,14 +93,14 @@ impl fmt::Display for OfflineListing {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OfflineListing {
|
impl OfflineListing {
|
||||||
pub fn new(cursor_pos: (AccountHash, MailboxHash)) -> Self {
|
pub fn new(cursor_pos: (AccountHash, MailboxHash)) -> Box<Self> {
|
||||||
OfflineListing {
|
Box::new(OfflineListing {
|
||||||
cursor_pos,
|
cursor_pos,
|
||||||
_row_updates: SmallVec::new(),
|
_row_updates: SmallVec::new(),
|
||||||
_selection: HashMap::default(),
|
_selection: HashMap::default(),
|
||||||
dirty: true,
|
dirty: true,
|
||||||
id: ComponentId::new_v4(),
|
id: ComponentId::new_v4(),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -133,6 +133,7 @@ pub struct PlainListing {
|
||||||
/// Cache current view.
|
/// Cache current view.
|
||||||
data_columns: DataColumns,
|
data_columns: DataColumns,
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
search_job: Option<(String, JoinHandle<Result<SmallVec<[EnvelopeHash; 512]>>>)>,
|
search_job: Option<(String, JoinHandle<Result<SmallVec<[EnvelopeHash; 512]>>>)>,
|
||||||
filter_term: String,
|
filter_term: String,
|
||||||
filtered_selection: Vec<EnvelopeHash>,
|
filtered_selection: Vec<EnvelopeHash>,
|
||||||
|
@ -311,7 +312,7 @@ impl MailListingTrait for PlainListing {
|
||||||
let roots = items
|
let roots = items
|
||||||
.filter_map(|r| threads.groups[&r].root().map(|r| r.root))
|
.filter_map(|r| threads.groups[&r].root().map(|r| r.root))
|
||||||
.collect::<_>();
|
.collect::<_>();
|
||||||
let thread_nodes: &HashMap<ThreadNodeHash, ThreadNode> = &threads.thread_nodes();
|
let thread_nodes: &HashMap<ThreadNodeHash, ThreadNode> = threads.thread_nodes();
|
||||||
let env_hash_iter = Box::new(
|
let env_hash_iter = Box::new(
|
||||||
threads
|
threads
|
||||||
.threads_group_iter(roots)
|
.threads_group_iter(roots)
|
||||||
|
@ -685,8 +686,8 @@ impl fmt::Display for PlainListing {
|
||||||
|
|
||||||
impl PlainListing {
|
impl PlainListing {
|
||||||
const DESCRIPTION: &'static str = "plain listing";
|
const DESCRIPTION: &'static str = "plain listing";
|
||||||
pub fn new(coordinates: (AccountHash, MailboxHash)) -> Self {
|
pub fn new(coordinates: (AccountHash, MailboxHash)) -> Box<Self> {
|
||||||
PlainListing {
|
Box::new(PlainListing {
|
||||||
cursor_pos: (0, 1, 0),
|
cursor_pos: (0, 1, 0),
|
||||||
new_cursor_pos: (coordinates.0, coordinates.1, 0),
|
new_cursor_pos: (coordinates.0, coordinates.1, 0),
|
||||||
length: 0,
|
length: 0,
|
||||||
|
@ -714,7 +715,7 @@ impl PlainListing {
|
||||||
|
|
||||||
movement: None,
|
movement: None,
|
||||||
id: ComponentId::new_v4(),
|
id: ComponentId::new_v4(),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_entry_string(&self, e: EnvelopeRef, context: &Context) -> EntryStrings {
|
fn make_entry_string(&self, e: EnvelopeRef, context: &Context) -> EntryStrings {
|
||||||
|
@ -884,7 +885,7 @@ impl PlainListing {
|
||||||
Box::new(self.local_collection.iter().cloned())
|
Box::new(self.local_collection.iter().cloned())
|
||||||
as Box<dyn Iterator<Item = EnvelopeHash>>
|
as Box<dyn Iterator<Item = EnvelopeHash>>
|
||||||
} else {
|
} else {
|
||||||
Box::new(self.filtered_selection.iter().map(|h| *h))
|
Box::new(self.filtered_selection.iter().cloned())
|
||||||
as Box<dyn Iterator<Item = EnvelopeHash>>
|
as Box<dyn Iterator<Item = EnvelopeHash>>
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1234,7 +1235,7 @@ impl Component for PlainListing {
|
||||||
.flatten()
|
.flatten()
|
||||||
.chain(cursor_iter.into_iter().flatten())
|
.chain(cursor_iter.into_iter().flatten())
|
||||||
.cloned();
|
.cloned();
|
||||||
let stack: SmallVec<[_; 8]> = SmallVec::from_iter(iter.into_iter());
|
let stack: SmallVec<[_; 8]> = SmallVec::from_iter(iter);
|
||||||
for i in stack {
|
for i in stack {
|
||||||
self.perform_action(context, i, a);
|
self.perform_action(context, i, a);
|
||||||
}
|
}
|
||||||
|
|
|
@ -241,6 +241,7 @@ impl MailListingTrait for ThreadListing {
|
||||||
}
|
}
|
||||||
let mut rows = Vec::with_capacity(1024);
|
let mut rows = Vec::with_capacity(1024);
|
||||||
let mut min_width = (0, 0, 0, 0, 0);
|
let mut min_width = (0, 0, 0, 0, 0);
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
let mut row_widths: (
|
let mut row_widths: (
|
||||||
SmallVec<[u8; 1024]>,
|
SmallVec<[u8; 1024]>,
|
||||||
SmallVec<[u8; 1024]>,
|
SmallVec<[u8; 1024]>,
|
||||||
|
@ -260,7 +261,7 @@ impl MailListingTrait for ThreadListing {
|
||||||
.filter_map(|r| threads.groups[&r].root().map(|r| r.root))
|
.filter_map(|r| threads.groups[&r].root().map(|r| r.root))
|
||||||
.collect::<_>();
|
.collect::<_>();
|
||||||
let mut iter = threads.threads_group_iter(roots).peekable();
|
let mut iter = threads.threads_group_iter(roots).peekable();
|
||||||
let thread_nodes: &HashMap<ThreadNodeHash, ThreadNode> = &threads.thread_nodes();
|
let thread_nodes: &HashMap<ThreadNodeHash, ThreadNode> = threads.thread_nodes();
|
||||||
/* This is just a desugared for loop so that we can use .peek() */
|
/* This is just a desugared for loop so that we can use .peek() */
|
||||||
let mut idx = 0;
|
let mut idx = 0;
|
||||||
let mut prev_group = ThreadHash::null();
|
let mut prev_group = ThreadHash::null();
|
||||||
|
@ -737,8 +738,8 @@ impl fmt::Display for ThreadListing {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ThreadListing {
|
impl ThreadListing {
|
||||||
pub fn new(coordinates: (AccountHash, MailboxHash)) -> Self {
|
pub fn new(coordinates: (AccountHash, MailboxHash)) -> Box<Self> {
|
||||||
ThreadListing {
|
Box::new(ThreadListing {
|
||||||
cursor_pos: (coordinates.0, 0, 0),
|
cursor_pos: (coordinates.0, 0, 0),
|
||||||
new_cursor_pos: (coordinates.0, coordinates.1, 0),
|
new_cursor_pos: (coordinates.0, coordinates.1, 0),
|
||||||
length: 0,
|
length: 0,
|
||||||
|
@ -757,7 +758,7 @@ impl ThreadListing {
|
||||||
initialised: false,
|
initialised: false,
|
||||||
movement: None,
|
movement: None,
|
||||||
id: ComponentId::new_v4(),
|
id: ComponentId::new_v4(),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn highlight_line_self(&mut self, _idx: usize, _context: &Context) {
|
fn highlight_line_self(&mut self, _idx: usize, _context: &Context) {
|
||||||
|
@ -879,7 +880,7 @@ impl ThreadListing {
|
||||||
EntryStrings {
|
EntryStrings {
|
||||||
date: DateString(ConversationsListing::format_date(context, e.date())),
|
date: DateString(ConversationsListing::format_date(context, e.date())),
|
||||||
subject: SubjectString(subject),
|
subject: SubjectString(subject),
|
||||||
flag: FlagString(format!("{}", if e.has_attachments() { "📎" } else { "" },)),
|
flag: FlagString((if e.has_attachments() { "📎" } else { "" }).to_string()),
|
||||||
from: FromString(address_list!((e.from()) as comma_sep_list)),
|
from: FromString(address_list!((e.from()) as comma_sep_list)),
|
||||||
tags: TagString(tags, colors),
|
tags: TagString(tags, colors),
|
||||||
}
|
}
|
||||||
|
@ -1243,7 +1244,7 @@ impl Component for ThreadListing {
|
||||||
}
|
}
|
||||||
UIEvent::EnvelopeRename(ref old_hash, ref new_hash) => {
|
UIEvent::EnvelopeRename(ref old_hash, ref new_hash) => {
|
||||||
let account = &context.accounts[&self.cursor_pos.0];
|
let account = &context.accounts[&self.cursor_pos.0];
|
||||||
if !account.collection.contains_key(&new_hash) {
|
if !account.collection.contains_key(new_hash) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if let Some(row) = self.order.remove(old_hash) {
|
if let Some(row) = self.order.remove(old_hash) {
|
||||||
|
|
|
@ -39,7 +39,7 @@ pub async fn verify(a: Attachment) -> Result<()> {
|
||||||
let (data, sig) =
|
let (data, sig) =
|
||||||
melib_pgp::verify_signature(&a).chain_err_summary(|| "Could not verify signature.")?;
|
melib_pgp::verify_signature(&a).chain_err_summary(|| "Could not verify signature.")?;
|
||||||
let mut ctx = Context::new()?;
|
let mut ctx = Context::new()?;
|
||||||
let sig = ctx.new_data_mem(&sig.body().trim())?;
|
let sig = ctx.new_data_mem(sig.body().trim())?;
|
||||||
let data = ctx.new_data_mem(&data)?;
|
let data = ctx.new_data_mem(&data)?;
|
||||||
ctx.verify(sig, data)?.await
|
ctx.verify(sig, data)?.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -292,7 +292,7 @@ impl Component for AccountStatus {
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
line += 1;
|
line += 1;
|
||||||
for (name, status) in extensions.into_iter() {
|
for (name, status) in extensions.iter() {
|
||||||
width = self.content.size().0;
|
width = self.content.size().0;
|
||||||
write_string_to_grid(
|
write_string_to_grid(
|
||||||
name.trim_at_boundary(30),
|
name.trim_at_boundary(30),
|
||||||
|
@ -418,7 +418,7 @@ impl Component for AccountStatus {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
UIEvent::Input(ref key) if shortcut!(key == shortcuts["general"]["scroll_right"]) => {
|
UIEvent::Input(ref key) if shortcut!(key == shortcuts["general"]["scroll_right"]) => {
|
||||||
self.cursor.0 = self.cursor.0 + 1;
|
self.cursor.0 += 1;
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -428,7 +428,7 @@ impl Component for AccountStatus {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
UIEvent::Input(ref key) if shortcut!(key == shortcuts["general"]["scroll_down"]) => {
|
UIEvent::Input(ref key) if shortcut!(key == shortcuts["general"]["scroll_down"]) => {
|
||||||
self.cursor.1 = self.cursor.1 + 1;
|
self.cursor.1 += 1;
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ enum ViewMode {
|
||||||
Source(Source),
|
Source(Source),
|
||||||
//Ansi(RawBuffer),
|
//Ansi(RawBuffer),
|
||||||
Subview,
|
Subview,
|
||||||
ContactSelector(UIDialog<Card>),
|
ContactSelector(Box<UIDialog<Card>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ViewMode {
|
impl Default for ViewMode {
|
||||||
|
@ -76,69 +76,65 @@ impl ViewMode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
fn is_attachment(&self) -> bool {
|
fn is_attachment(&self) -> bool {
|
||||||
match self {
|
matches!(self, ViewMode::Attachment(_))
|
||||||
ViewMode::Attachment(_) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_contact_selector(&self) -> bool {
|
fn is_contact_selector(&self) -> bool {
|
||||||
match self {
|
matches!(self, ViewMode::ContactSelector(_))
|
||||||
ViewMode::ContactSelector(_) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum AttachmentDisplay {
|
pub enum AttachmentDisplay {
|
||||||
Alternative {
|
Alternative {
|
||||||
inner: Attachment,
|
inner: Box<Attachment>,
|
||||||
shown_display: usize,
|
shown_display: usize,
|
||||||
display: Vec<AttachmentDisplay>,
|
display: Vec<AttachmentDisplay>,
|
||||||
},
|
},
|
||||||
InlineText {
|
InlineText {
|
||||||
inner: Attachment,
|
inner: Box<Attachment>,
|
||||||
comment: Option<String>,
|
comment: Option<String>,
|
||||||
text: String,
|
text: String,
|
||||||
},
|
},
|
||||||
InlineOther {
|
InlineOther {
|
||||||
inner: Attachment,
|
inner: Box<Attachment>,
|
||||||
},
|
},
|
||||||
Attachment {
|
Attachment {
|
||||||
inner: Attachment,
|
inner: Box<Attachment>,
|
||||||
},
|
},
|
||||||
SignedPending {
|
SignedPending {
|
||||||
inner: Attachment,
|
inner: Box<Attachment>,
|
||||||
display: Vec<AttachmentDisplay>,
|
display: Vec<AttachmentDisplay>,
|
||||||
handle: JoinHandle<Result<()>>,
|
handle: JoinHandle<Result<()>>,
|
||||||
job_id: JobId,
|
job_id: JobId,
|
||||||
},
|
},
|
||||||
SignedFailed {
|
SignedFailed {
|
||||||
inner: Attachment,
|
inner: Box<Attachment>,
|
||||||
display: Vec<AttachmentDisplay>,
|
display: Vec<AttachmentDisplay>,
|
||||||
error: MeliError,
|
error: MeliError,
|
||||||
},
|
},
|
||||||
SignedUnverified {
|
SignedUnverified {
|
||||||
inner: Attachment,
|
inner: Box<Attachment>,
|
||||||
display: Vec<AttachmentDisplay>,
|
display: Vec<AttachmentDisplay>,
|
||||||
},
|
},
|
||||||
SignedVerified {
|
SignedVerified {
|
||||||
inner: Attachment,
|
inner: Box<Attachment>,
|
||||||
display: Vec<AttachmentDisplay>,
|
display: Vec<AttachmentDisplay>,
|
||||||
description: String,
|
description: String,
|
||||||
},
|
},
|
||||||
EncryptedPending {
|
EncryptedPending {
|
||||||
inner: Attachment,
|
inner: Box<Attachment>,
|
||||||
handle: JoinHandle<Result<(melib::pgp::DecryptionMetadata, Vec<u8>)>>,
|
handle: JoinHandle<Result<(melib::pgp::DecryptionMetadata, Vec<u8>)>>,
|
||||||
},
|
},
|
||||||
EncryptedFailed {
|
EncryptedFailed {
|
||||||
inner: Attachment,
|
inner: Box<Attachment>,
|
||||||
error: MeliError,
|
error: MeliError,
|
||||||
},
|
},
|
||||||
EncryptedSuccess {
|
EncryptedSuccess {
|
||||||
inner: Attachment,
|
inner: Box<Attachment>,
|
||||||
plaintext: Attachment,
|
plaintext: Box<Attachment>,
|
||||||
plaintext_display: Vec<AttachmentDisplay>,
|
plaintext_display: Vec<AttachmentDisplay>,
|
||||||
description: String,
|
description: String,
|
||||||
},
|
},
|
||||||
|
@ -191,8 +187,8 @@ enum MailViewState {
|
||||||
},
|
},
|
||||||
Loaded {
|
Loaded {
|
||||||
bytes: Vec<u8>,
|
bytes: Vec<u8>,
|
||||||
env: Envelope,
|
env: Box<Envelope>,
|
||||||
body: Attachment,
|
body: Box<Attachment>,
|
||||||
display: Vec<AttachmentDisplay>,
|
display: Vec<AttachmentDisplay>,
|
||||||
body_text: String,
|
body_text: String,
|
||||||
links: Vec<Link>,
|
links: Vec<Link>,
|
||||||
|
@ -313,9 +309,10 @@ impl MailView {
|
||||||
.get_env_mut(self.coordinates.2)
|
.get_env_mut(self.coordinates.2)
|
||||||
.populate_headers(&bytes);
|
.populate_headers(&bytes);
|
||||||
}
|
}
|
||||||
let env =
|
let env = Box::new(
|
||||||
account.collection.get_env(self.coordinates.2).clone();
|
account.collection.get_env(self.coordinates.2).clone(),
|
||||||
let body = AttachmentBuilder::new(&bytes).build();
|
);
|
||||||
|
let body = Box::new(AttachmentBuilder::new(&bytes).build());
|
||||||
let display = Self::attachment_to(
|
let display = Self::attachment_to(
|
||||||
&body,
|
&body,
|
||||||
context,
|
context,
|
||||||
|
@ -415,7 +412,7 @@ impl MailView {
|
||||||
..
|
..
|
||||||
} => (
|
} => (
|
||||||
bytes,
|
bytes,
|
||||||
self.attachment_displays_to_text(&display, context, false),
|
self.attachment_displays_to_text(display, context, false),
|
||||||
env,
|
env,
|
||||||
),
|
),
|
||||||
MailViewState::Error { .. } => {
|
MailViewState::Error { .. } => {
|
||||||
|
@ -489,13 +486,13 @@ impl MailView {
|
||||||
if !acc.ends_with("\n\n") {
|
if !acc.ends_with("\n\n") {
|
||||||
acc.push_str("\n\n");
|
acc.push_str("\n\n");
|
||||||
}
|
}
|
||||||
acc.push_str(&text);
|
acc.push_str(text);
|
||||||
}
|
}
|
||||||
InlineText {
|
InlineText {
|
||||||
inner: _,
|
inner: _,
|
||||||
text,
|
text,
|
||||||
comment: _,
|
comment: _,
|
||||||
} => acc.push_str(&text),
|
} => acc.push_str(text),
|
||||||
InlineOther { inner } => {
|
InlineOther { inner } => {
|
||||||
if !acc.ends_with("\n\n") {
|
if !acc.ends_with("\n\n") {
|
||||||
acc.push_str("\n\n");
|
acc.push_str("\n\n");
|
||||||
|
@ -550,7 +547,7 @@ impl MailView {
|
||||||
if description.is_empty() {
|
if description.is_empty() {
|
||||||
acc.push_str("Verified signature.\n\n");
|
acc.push_str("Verified signature.\n\n");
|
||||||
} else {
|
} else {
|
||||||
acc.push_str(&description);
|
acc.push_str(description);
|
||||||
acc.push_str("\n\n");
|
acc.push_str("\n\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -574,7 +571,7 @@ impl MailView {
|
||||||
if description.is_empty() {
|
if description.is_empty() {
|
||||||
acc.push_str("Succesfully decrypted.\n\n");
|
acc.push_str("Succesfully decrypted.\n\n");
|
||||||
} else {
|
} else {
|
||||||
acc.push_str(&description);
|
acc.push_str(description);
|
||||||
acc.push_str("\n\n");
|
acc.push_str("\n\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -672,35 +669,32 @@ impl MailView {
|
||||||
s.push(' ');
|
s.push(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
s.extend(att.to_string().chars());
|
s.push_str(&att.to_string());
|
||||||
paths.push(cur_path.clone());
|
paths.push(cur_path.clone());
|
||||||
match att.content_type {
|
if matches!(att.content_type, ContentType::Multipart { .. }) {
|
||||||
ContentType::Multipart { .. } => {
|
let mut iter = (0..sub_att_display_vec.len()).peekable();
|
||||||
let mut iter = (0..sub_att_display_vec.len()).peekable();
|
if has_sibling {
|
||||||
if has_sibling {
|
branches.push(true);
|
||||||
branches.push(true);
|
} else {
|
||||||
} else {
|
branches.push(false);
|
||||||
branches.push(false);
|
|
||||||
}
|
|
||||||
while let Some(i) = iter.next() {
|
|
||||||
*idx += 1;
|
|
||||||
cur_path.push(i);
|
|
||||||
append_entry(
|
|
||||||
(idx, (depth + 1, &sub_att_display_vec[i])),
|
|
||||||
branches,
|
|
||||||
paths,
|
|
||||||
cur_path,
|
|
||||||
iter.peek() != None,
|
|
||||||
s,
|
|
||||||
);
|
|
||||||
if Some(i) == default_alternative {
|
|
||||||
s.push_str(" (displayed by default)");
|
|
||||||
}
|
|
||||||
cur_path.pop();
|
|
||||||
}
|
|
||||||
branches.pop();
|
|
||||||
}
|
}
|
||||||
_ => {}
|
while let Some(i) = iter.next() {
|
||||||
|
*idx += 1;
|
||||||
|
cur_path.push(i);
|
||||||
|
append_entry(
|
||||||
|
(idx, (depth + 1, &sub_att_display_vec[i])),
|
||||||
|
branches,
|
||||||
|
paths,
|
||||||
|
cur_path,
|
||||||
|
iter.peek() != None,
|
||||||
|
s,
|
||||||
|
);
|
||||||
|
if Some(i) == default_alternative {
|
||||||
|
s.push_str(" (displayed by default)");
|
||||||
|
}
|
||||||
|
cur_path.pop();
|
||||||
|
}
|
||||||
|
branches.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -735,7 +729,9 @@ impl MailView {
|
||||||
active_jobs: &mut HashSet<JobId>,
|
active_jobs: &mut HashSet<JobId>,
|
||||||
) {
|
) {
|
||||||
if a.content_disposition.kind.is_attachment() || a.content_type == "message/rfc822" {
|
if a.content_disposition.kind.is_attachment() || a.content_type == "message/rfc822" {
|
||||||
acc.push(AttachmentDisplay::Attachment { inner: a.clone() });
|
acc.push(AttachmentDisplay::Attachment {
|
||||||
|
inner: Box::new(a.clone()),
|
||||||
|
});
|
||||||
} else if a.content_type().is_text_html() {
|
} else if a.content_type().is_text_html() {
|
||||||
let bytes = decode(a, None);
|
let bytes = decode(a, None);
|
||||||
let filter_invocation =
|
let filter_invocation =
|
||||||
|
@ -764,7 +760,7 @@ impl MailView {
|
||||||
));
|
));
|
||||||
let text = String::from_utf8_lossy(&bytes).to_string();
|
let text = String::from_utf8_lossy(&bytes).to_string();
|
||||||
acc.push(AttachmentDisplay::InlineText {
|
acc.push(AttachmentDisplay::InlineText {
|
||||||
inner: a.clone(),
|
inner: Box::new(a.clone()),
|
||||||
comment,
|
comment,
|
||||||
text,
|
text,
|
||||||
});
|
});
|
||||||
|
@ -785,7 +781,7 @@ impl MailView {
|
||||||
)
|
)
|
||||||
.to_string();
|
.to_string();
|
||||||
acc.push(AttachmentDisplay::InlineText {
|
acc.push(AttachmentDisplay::InlineText {
|
||||||
inner: a.clone(),
|
inner: Box::new(a.clone()),
|
||||||
comment,
|
comment,
|
||||||
text,
|
text,
|
||||||
});
|
});
|
||||||
|
@ -794,7 +790,7 @@ impl MailView {
|
||||||
} else if a.is_text() {
|
} else if a.is_text() {
|
||||||
let bytes = decode(a, None);
|
let bytes = decode(a, None);
|
||||||
acc.push(AttachmentDisplay::InlineText {
|
acc.push(AttachmentDisplay::InlineText {
|
||||||
inner: a.clone(),
|
inner: Box::new(a.clone()),
|
||||||
comment: None,
|
comment: None,
|
||||||
text: String::from_utf8_lossy(&bytes).to_string(),
|
text: String::from_utf8_lossy(&bytes).to_string(),
|
||||||
});
|
});
|
||||||
|
@ -838,7 +834,7 @@ impl MailView {
|
||||||
rec(a, context, coordinates, &mut display, active_jobs);
|
rec(a, context, coordinates, &mut display, active_jobs);
|
||||||
}
|
}
|
||||||
acc.push(AttachmentDisplay::Alternative {
|
acc.push(AttachmentDisplay::Alternative {
|
||||||
inner: a.clone(),
|
inner: Box::new(a.clone()),
|
||||||
shown_display: chosen_attachment_idx,
|
shown_display: chosen_attachment_idx,
|
||||||
display,
|
display,
|
||||||
});
|
});
|
||||||
|
@ -847,7 +843,7 @@ impl MailView {
|
||||||
#[cfg(not(feature = "gpgme"))]
|
#[cfg(not(feature = "gpgme"))]
|
||||||
{
|
{
|
||||||
acc.push(AttachmentDisplay::SignedUnverified {
|
acc.push(AttachmentDisplay::SignedUnverified {
|
||||||
inner: a.clone(),
|
inner: Box::new(a.clone()),
|
||||||
display: {
|
display: {
|
||||||
let mut v = vec![];
|
let mut v = vec![];
|
||||||
rec(&parts[0], context, coordinates, &mut v, active_jobs);
|
rec(&parts[0], context, coordinates, &mut v, active_jobs);
|
||||||
|
@ -869,7 +865,7 @@ impl MailView {
|
||||||
StatusEvent::NewJob(handle.job_id),
|
StatusEvent::NewJob(handle.job_id),
|
||||||
));
|
));
|
||||||
acc.push(AttachmentDisplay::SignedPending {
|
acc.push(AttachmentDisplay::SignedPending {
|
||||||
inner: a.clone(),
|
inner: Box::new(a.clone()),
|
||||||
job_id: handle.job_id,
|
job_id: handle.job_id,
|
||||||
display: {
|
display: {
|
||||||
let mut v = vec![];
|
let mut v = vec![];
|
||||||
|
@ -880,7 +876,7 @@ impl MailView {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
acc.push(AttachmentDisplay::SignedUnverified {
|
acc.push(AttachmentDisplay::SignedUnverified {
|
||||||
inner: a.clone(),
|
inner: Box::new(a.clone()),
|
||||||
display: {
|
display: {
|
||||||
let mut v = vec![];
|
let mut v = vec![];
|
||||||
rec(&parts[0], context, coordinates, &mut v, active_jobs);
|
rec(&parts[0], context, coordinates, &mut v, active_jobs);
|
||||||
|
@ -896,7 +892,7 @@ impl MailView {
|
||||||
#[cfg(not(feature = "gpgme"))]
|
#[cfg(not(feature = "gpgme"))]
|
||||||
{
|
{
|
||||||
acc.push(AttachmentDisplay::EncryptedFailed {
|
acc.push(AttachmentDisplay::EncryptedFailed {
|
||||||
inner: a.clone(),
|
inner: Box::new(a.clone()),
|
||||||
error: MeliError::new("Cannot decrypt: meli must be compiled with libgpgme support."),
|
error: MeliError::new("Cannot decrypt: meli must be compiled with libgpgme support."),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -914,12 +910,12 @@ impl MailView {
|
||||||
StatusEvent::NewJob(handle.job_id),
|
StatusEvent::NewJob(handle.job_id),
|
||||||
));
|
));
|
||||||
acc.push(AttachmentDisplay::EncryptedPending {
|
acc.push(AttachmentDisplay::EncryptedPending {
|
||||||
inner: a.clone(),
|
inner: Box::new(a.clone()),
|
||||||
handle,
|
handle,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
acc.push(AttachmentDisplay::EncryptedFailed {
|
acc.push(AttachmentDisplay::EncryptedFailed {
|
||||||
inner: a.clone(),
|
inner: Box::new(a.clone()),
|
||||||
error: MeliError::new("Undecrypted."),
|
error: MeliError::new("Undecrypted."),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -963,17 +959,13 @@ impl MailView {
|
||||||
} else {
|
} else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
if let Some(path) =
|
if let Some(path) = self.attachment_paths.get(lidx).and_then(|path| {
|
||||||
self.attachment_paths.get(lidx).and_then(
|
if !path.is_empty() {
|
||||||
|path| {
|
Some(path)
|
||||||
if path.len() > 0 {
|
} else {
|
||||||
Some(path)
|
None
|
||||||
} else {
|
}
|
||||||
None
|
}) {
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
{
|
|
||||||
let first = path[0];
|
let first = path[0];
|
||||||
use AttachmentDisplay::*;
|
use AttachmentDisplay::*;
|
||||||
let root_attachment = match &display[first] {
|
let root_attachment = match &display[first] {
|
||||||
|
@ -1022,14 +1014,11 @@ impl MailView {
|
||||||
if path.is_empty() {
|
if path.is_empty() {
|
||||||
return Some(a);
|
return Some(a);
|
||||||
}
|
}
|
||||||
match a.content_type {
|
if let ContentType::Multipart { ref parts, .. } = a.content_type {
|
||||||
ContentType::Multipart { ref parts, .. } => {
|
let first = path[0];
|
||||||
let first = path[0];
|
if first < parts.len() {
|
||||||
if first < parts.len() {
|
return find_attachment(&parts[first], &path[1..]);
|
||||||
return find_attachment(&parts[first], &path[1..]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -1080,7 +1069,7 @@ impl MailView {
|
||||||
entries.push((new_card, format!("{}", addr)));
|
entries.push((new_card, format!("{}", addr)));
|
||||||
}
|
}
|
||||||
drop(envelope);
|
drop(envelope);
|
||||||
self.mode = ViewMode::ContactSelector(Selector::new(
|
self.mode = ViewMode::ContactSelector(Box::new(Selector::new(
|
||||||
"select contacts to add",
|
"select contacts to add",
|
||||||
entries,
|
entries,
|
||||||
false,
|
false,
|
||||||
|
@ -1088,7 +1077,7 @@ impl MailView {
|
||||||
Some(UIEvent::FinishedUIDialog(id, Box::new(results.to_vec())))
|
Some(UIEvent::FinishedUIDialog(id, Box::new(results.to_vec())))
|
||||||
})),
|
})),
|
||||||
context,
|
context,
|
||||||
));
|
)));
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
self.initialised = false;
|
self.initialised = false;
|
||||||
}
|
}
|
||||||
|
@ -1420,10 +1409,7 @@ impl Component for MailView {
|
||||||
err.to_string(),
|
err.to_string(),
|
||||||
Some(NotificationType::Error(err.kind)),
|
Some(NotificationType::Error(err.kind)),
|
||||||
));
|
));
|
||||||
log(
|
log(format!("Failed to open envelope: {}", err), ERROR);
|
||||||
format!("Failed to open envelope: {}", err.to_string()),
|
|
||||||
ERROR,
|
|
||||||
);
|
|
||||||
self.init_futures(context);
|
self.init_futures(context);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1444,7 +1430,7 @@ impl Component for MailView {
|
||||||
let mut text = "Viewing attachment. Press `r` to return \n".to_string();
|
let mut text = "Viewing attachment. Press `r` to return \n".to_string();
|
||||||
if let Some(attachment) = self.open_attachment(aidx, context) {
|
if let Some(attachment) = self.open_attachment(aidx, context) {
|
||||||
if attachment.is_html() {
|
if attachment.is_html() {
|
||||||
self.subview = Some(Box::new(HtmlView::new(&attachment, context)));
|
self.subview = Some(Box::new(HtmlView::new(attachment, context)));
|
||||||
self.mode = ViewMode::Subview;
|
self.mode = ViewMode::Subview;
|
||||||
} else {
|
} else {
|
||||||
text.push_str(&attachment.text());
|
text.push_str(&attachment.text());
|
||||||
|
@ -1475,7 +1461,7 @@ impl Component for MailView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ViewMode::Normal if body.is_html() => {
|
ViewMode::Normal if body.is_html() => {
|
||||||
self.subview = Some(Box::new(HtmlView::new(&body, context)));
|
self.subview = Some(Box::new(HtmlView::new(body, context)));
|
||||||
self.mode = ViewMode::Subview;
|
self.mode = ViewMode::Subview;
|
||||||
}
|
}
|
||||||
ViewMode::Normal
|
ViewMode::Normal
|
||||||
|
@ -1497,13 +1483,12 @@ impl Component for MailView {
|
||||||
} =>
|
} =>
|
||||||
{
|
{
|
||||||
self.subview = Some(Box::new(HtmlView::new(
|
self.subview = Some(Box::new(HtmlView::new(
|
||||||
&body
|
body.content_type
|
||||||
.content_type
|
|
||||||
.parts()
|
.parts()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.into_iter()
|
.iter()
|
||||||
.find(|a| a.is_html())
|
.find(|a| a.is_html())
|
||||||
.unwrap_or(&body),
|
.unwrap_or(body),
|
||||||
context,
|
context,
|
||||||
)));
|
)));
|
||||||
self.mode = ViewMode::Subview;
|
self.mode = ViewMode::Subview;
|
||||||
|
@ -1541,7 +1526,7 @@ impl Component for MailView {
|
||||||
if !ret.ends_with("\n\n") {
|
if !ret.ends_with("\n\n") {
|
||||||
ret.push_str("\n\n");
|
ret.push_str("\n\n");
|
||||||
}
|
}
|
||||||
ret.extend(body_text.chars());
|
ret.push_str(body_text);
|
||||||
if !ret.ends_with("\n\n") {
|
if !ret.ends_with("\n\n") {
|
||||||
ret.push_str("\n\n");
|
ret.push_str("\n\n");
|
||||||
}
|
}
|
||||||
|
@ -1721,10 +1706,8 @@ impl Component for MailView {
|
||||||
self.force_draw_headers = true;
|
self.force_draw_headers = true;
|
||||||
if self.pager.cursor_pos() == 0 {
|
if self.pager.cursor_pos() == 0 {
|
||||||
self.headers_cursor = self.headers_cursor.saturating_sub(1);
|
self.headers_cursor = self.headers_cursor.saturating_sub(1);
|
||||||
} else {
|
} else if self.pager.process_event(event, context) {
|
||||||
if self.pager.process_event(event, context) {
|
return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self.pager.set_dirty(true);
|
self.pager.set_dirty(true);
|
||||||
return true;
|
return true;
|
||||||
|
@ -1767,11 +1750,13 @@ impl Component for MailView {
|
||||||
.get_env_mut(self.coordinates.2)
|
.get_env_mut(self.coordinates.2)
|
||||||
.populate_headers(&bytes);
|
.populate_headers(&bytes);
|
||||||
}
|
}
|
||||||
let env = context.accounts[&self.coordinates.0]
|
let env = Box::new(
|
||||||
.collection
|
context.accounts[&self.coordinates.0]
|
||||||
.get_env(self.coordinates.2)
|
.collection
|
||||||
.clone();
|
.get_env(self.coordinates.2)
|
||||||
let body = AttachmentBuilder::new(&bytes).build();
|
.clone(),
|
||||||
|
);
|
||||||
|
let body = Box::new(AttachmentBuilder::new(&bytes).build());
|
||||||
let display = Self::attachment_to(
|
let display = Self::attachment_to(
|
||||||
&body,
|
&body,
|
||||||
context,
|
context,
|
||||||
|
@ -1823,9 +1808,11 @@ impl Component for MailView {
|
||||||
*d = AttachmentDisplay::SignedVerified {
|
*d = AttachmentDisplay::SignedVerified {
|
||||||
inner: std::mem::replace(
|
inner: std::mem::replace(
|
||||||
inner,
|
inner,
|
||||||
AttachmentBuilder::new(&[]).build(),
|
Box::new(
|
||||||
|
AttachmentBuilder::new(&[]).build(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
display: std::mem::replace(display, vec![]),
|
display: std::mem::take(display),
|
||||||
description: String::new(),
|
description: String::new(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1833,9 +1820,11 @@ impl Component for MailView {
|
||||||
*d = AttachmentDisplay::SignedFailed {
|
*d = AttachmentDisplay::SignedFailed {
|
||||||
inner: std::mem::replace(
|
inner: std::mem::replace(
|
||||||
inner,
|
inner,
|
||||||
AttachmentBuilder::new(&[]).build(),
|
Box::new(
|
||||||
|
AttachmentBuilder::new(&[]).build(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
display: std::mem::replace(display, vec![]),
|
display: std::mem::take(display),
|
||||||
error,
|
error,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1851,9 +1840,10 @@ impl Component for MailView {
|
||||||
Ok(None) => { /* something happened, perhaps a worker thread panicked */
|
Ok(None) => { /* something happened, perhaps a worker thread panicked */
|
||||||
}
|
}
|
||||||
Ok(Some(Ok((metadata, decrypted_bytes)))) => {
|
Ok(Some(Ok((metadata, decrypted_bytes)))) => {
|
||||||
let plaintext =
|
let plaintext = Box::new(
|
||||||
AttachmentBuilder::new(&decrypted_bytes)
|
AttachmentBuilder::new(&decrypted_bytes)
|
||||||
.build();
|
.build(),
|
||||||
|
);
|
||||||
let plaintext_display = Self::attachment_to(
|
let plaintext_display = Self::attachment_to(
|
||||||
&plaintext,
|
&plaintext,
|
||||||
context,
|
context,
|
||||||
|
@ -1863,7 +1853,9 @@ impl Component for MailView {
|
||||||
*d = AttachmentDisplay::EncryptedSuccess {
|
*d = AttachmentDisplay::EncryptedSuccess {
|
||||||
inner: std::mem::replace(
|
inner: std::mem::replace(
|
||||||
inner,
|
inner,
|
||||||
AttachmentBuilder::new(&[]).build(),
|
Box::new(
|
||||||
|
AttachmentBuilder::new(&[]).build(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
plaintext,
|
plaintext,
|
||||||
plaintext_display,
|
plaintext_display,
|
||||||
|
@ -1874,7 +1866,9 @@ impl Component for MailView {
|
||||||
*d = AttachmentDisplay::EncryptedFailed {
|
*d = AttachmentDisplay::EncryptedFailed {
|
||||||
inner: std::mem::replace(
|
inner: std::mem::replace(
|
||||||
inner,
|
inner,
|
||||||
AttachmentBuilder::new(&[]).build(),
|
Box::new(
|
||||||
|
AttachmentBuilder::new(&[]).build(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
error,
|
error,
|
||||||
};
|
};
|
||||||
|
@ -1888,9 +1882,9 @@ impl Component for MailView {
|
||||||
let mut new_body_text = String::new();
|
let mut new_body_text = String::new();
|
||||||
if let MailViewState::Loaded { ref display, .. } = self.state {
|
if let MailViewState::Loaded { ref display, .. } = self.state {
|
||||||
new_body_text =
|
new_body_text =
|
||||||
self.attachment_displays_to_text(&display, context, true);
|
self.attachment_displays_to_text(display, context, true);
|
||||||
let (paths, attachment_tree_s) =
|
let (paths, attachment_tree_s) =
|
||||||
self.attachment_displays_to_tree(&display);
|
self.attachment_displays_to_tree(display);
|
||||||
self.attachment_tree = attachment_tree_s;
|
self.attachment_tree = attachment_tree_s;
|
||||||
self.attachment_paths = paths;
|
self.attachment_paths = paths;
|
||||||
}
|
}
|
||||||
|
@ -2042,7 +2036,7 @@ impl Component for MailView {
|
||||||
.get(&env_hash)
|
.get(&env_hash)
|
||||||
.map(|env| env.message_id_display())
|
.map(|env| env.message_id_display())
|
||||||
.unwrap_or_else(|| "Not found".into()),
|
.unwrap_or_else(|| "Not found".into()),
|
||||||
err.to_string()
|
err
|
||||||
);
|
);
|
||||||
log(&err_string, ERROR);
|
log(&err_string, ERROR);
|
||||||
context.replies.push_back(UIEvent::Notification(
|
context.replies.push_back(UIEvent::Notification(
|
||||||
|
@ -2088,7 +2082,7 @@ impl Component for MailView {
|
||||||
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
UIEvent::Input(Key::Char(c)) if c >= '0' && c <= '9' => {
|
UIEvent::Input(Key::Char(c)) if ('0'..='9').contains(&c) => {
|
||||||
self.cmd_buf.push(c);
|
self.cmd_buf.push(c);
|
||||||
context
|
context
|
||||||
.replies
|
.replies
|
||||||
|
@ -2218,7 +2212,7 @@ impl Component for MailView {
|
||||||
if let Ok(command) = query_default_app(&attachment_type) {
|
if let Ok(command) = query_default_app(&attachment_type) {
|
||||||
let p = create_temp_file(
|
let p = create_temp_file(
|
||||||
&decode(attachment, None),
|
&decode(attachment, None),
|
||||||
filename.as_ref().map(|s| s.as_str()),
|
filename.as_deref(),
|
||||||
None,
|
None,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
@ -2400,10 +2394,7 @@ impl Component for MailView {
|
||||||
err.to_string(),
|
err.to_string(),
|
||||||
Some(NotificationType::Error(err.kind)),
|
Some(NotificationType::Error(err.kind)),
|
||||||
));
|
));
|
||||||
log(
|
log(format!("Failed to open envelope: {}", err), ERROR);
|
||||||
format!("Failed to open envelope: {}", err.to_string()),
|
|
||||||
ERROR,
|
|
||||||
);
|
|
||||||
self.init_futures(context);
|
self.init_futures(context);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -2424,11 +2415,7 @@ impl Component for MailView {
|
||||||
Some(NotificationType::Error(melib::ErrorKind::External)),
|
Some(NotificationType::Error(melib::ErrorKind::External)),
|
||||||
));
|
));
|
||||||
log(
|
log(
|
||||||
format!(
|
format!("Failed to create file at {}: {}", path.display(), err),
|
||||||
"Failed to create file at {}: {}",
|
|
||||||
path.display(),
|
|
||||||
err.to_string()
|
|
||||||
),
|
|
||||||
ERROR,
|
ERROR,
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
|
@ -2461,10 +2448,7 @@ impl Component for MailView {
|
||||||
err.to_string(),
|
err.to_string(),
|
||||||
Some(NotificationType::Error(err.kind)),
|
Some(NotificationType::Error(err.kind)),
|
||||||
));
|
));
|
||||||
log(
|
log(format!("Failed to open envelope: {}", err), ERROR);
|
||||||
format!("Failed to open envelope: {}", err.to_string()),
|
|
||||||
ERROR,
|
|
||||||
);
|
|
||||||
self.init_futures(context);
|
self.init_futures(context);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -2490,11 +2474,7 @@ impl Component for MailView {
|
||||||
Some(NotificationType::Error(melib::ErrorKind::External)),
|
Some(NotificationType::Error(melib::ErrorKind::External)),
|
||||||
));
|
));
|
||||||
log(
|
log(
|
||||||
format!(
|
format!("Failed to create file at {}: {}", path.display(), err),
|
||||||
"Failed to create file at {}: {}",
|
|
||||||
path.display(),
|
|
||||||
err.to_string()
|
|
||||||
),
|
|
||||||
ERROR,
|
ERROR,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2521,11 +2501,7 @@ impl Component for MailView {
|
||||||
Some(NotificationType::Error(melib::ErrorKind::External)),
|
Some(NotificationType::Error(melib::ErrorKind::External)),
|
||||||
));
|
));
|
||||||
log(
|
log(
|
||||||
format!(
|
format!("Failed to create file at {}: {}", path.display(), err),
|
||||||
"Failed to create file at {}: {}",
|
|
||||||
path.display(),
|
|
||||||
err.to_string()
|
|
||||||
),
|
|
||||||
ERROR,
|
ERROR,
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -36,10 +36,7 @@ enum ViewMode {
|
||||||
|
|
||||||
impl ViewMode {
|
impl ViewMode {
|
||||||
fn is_attachment(&self) -> bool {
|
fn is_attachment(&self) -> bool {
|
||||||
match self {
|
matches!(self, ViewMode::Attachment(_))
|
||||||
ViewMode::Attachment(_) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +50,7 @@ pub struct EnvelopeView {
|
||||||
mode: ViewMode,
|
mode: ViewMode,
|
||||||
mail: Mail,
|
mail: Mail,
|
||||||
|
|
||||||
account_hash: AccountHash,
|
_account_hash: AccountHash,
|
||||||
cmd_buf: String,
|
cmd_buf: String,
|
||||||
id: ComponentId,
|
id: ComponentId,
|
||||||
}
|
}
|
||||||
|
@ -69,7 +66,7 @@ impl EnvelopeView {
|
||||||
mail: Mail,
|
mail: Mail,
|
||||||
pager: Option<Pager>,
|
pager: Option<Pager>,
|
||||||
subview: Option<Box<dyn Component>>,
|
subview: Option<Box<dyn Component>>,
|
||||||
account_hash: AccountHash,
|
_account_hash: AccountHash,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
EnvelopeView {
|
EnvelopeView {
|
||||||
pager,
|
pager,
|
||||||
|
@ -77,7 +74,7 @@ impl EnvelopeView {
|
||||||
dirty: true,
|
dirty: true,
|
||||||
mode: ViewMode::Normal,
|
mode: ViewMode::Normal,
|
||||||
mail,
|
mail,
|
||||||
account_hash,
|
_account_hash,
|
||||||
cmd_buf: String::with_capacity(4),
|
cmd_buf: String::with_capacity(4),
|
||||||
id: ComponentId::new_v4(),
|
id: ComponentId::new_v4(),
|
||||||
}
|
}
|
||||||
|
@ -87,7 +84,7 @@ impl EnvelopeView {
|
||||||
fn attachment_to_text(&self, body: &Attachment, context: &mut Context) -> String {
|
fn attachment_to_text(&self, body: &Attachment, context: &mut Context) -> String {
|
||||||
let finder = LinkFinder::new();
|
let finder = LinkFinder::new();
|
||||||
let body_text = String::from_utf8_lossy(&decode_rec(
|
let body_text = String::from_utf8_lossy(&decode_rec(
|
||||||
&body,
|
body,
|
||||||
Some(Box::new(|a: &Attachment, v: &mut Vec<u8>| {
|
Some(Box::new(|a: &Attachment, v: &mut Vec<u8>| {
|
||||||
if a.content_type().is_text_html() {
|
if a.content_type().is_text_html() {
|
||||||
let settings = &context.settings;
|
let settings = &context.settings;
|
||||||
|
@ -107,14 +104,13 @@ impl EnvelopeView {
|
||||||
err.to_string(),
|
err.to_string(),
|
||||||
Some(NotificationType::Error(melib::ErrorKind::External)),
|
Some(NotificationType::Error(melib::ErrorKind::External)),
|
||||||
));
|
));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
Ok(mut html_filter) => {
|
Ok(mut html_filter) => {
|
||||||
html_filter
|
html_filter
|
||||||
.stdin
|
.stdin
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.write_all(&v)
|
.write_all(v)
|
||||||
.expect("Failed to write to stdin");
|
.expect("Failed to write to stdin");
|
||||||
*v = format!(
|
*v = format!(
|
||||||
"Text piped through `{}`. Press `v` to open in web browser. \n\n",
|
"Text piped through `{}`. Press `v` to open in web browser. \n\n",
|
||||||
|
@ -286,7 +282,7 @@ impl Component for EnvelopeView {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
ViewMode::Attachment(aidx) if body.attachments()[aidx].is_html() => {
|
ViewMode::Attachment(aidx) if body.attachments()[aidx].is_html() => {
|
||||||
let attachment = &body.attachments()[aidx];
|
let attachment = &body.attachments()[aidx];
|
||||||
self.subview = Some(Box::new(HtmlView::new(&attachment, context)));
|
self.subview = Some(Box::new(HtmlView::new(attachment, context)));
|
||||||
}
|
}
|
||||||
ViewMode::Normal if body.is_html() => {
|
ViewMode::Normal if body.is_html() => {
|
||||||
self.subview = Some(Box::new(HtmlView::new(&body, context)));
|
self.subview = Some(Box::new(HtmlView::new(&body, context)));
|
||||||
|
@ -336,7 +332,7 @@ impl Component for EnvelopeView {
|
||||||
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
UIEvent::Input(Key::Char(c)) if c >= '0' && c <= '9' => {
|
UIEvent::Input(Key::Char(c)) if ('0'..='9').contains(&c) => {
|
||||||
self.cmd_buf.push(c);
|
self.cmd_buf.push(c);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -368,71 +364,70 @@ impl Component for EnvelopeView {
|
||||||
.replies
|
.replies
|
||||||
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
||||||
|
|
||||||
{
|
if let Some(u) = self.mail.body().attachments().get(lidx) {
|
||||||
if let Some(u) = self.mail.body().attachments().get(lidx) {
|
match u.content_type() {
|
||||||
match u.content_type() {
|
ContentType::MessageRfc822 => {
|
||||||
ContentType::MessageRfc822 => {
|
self.mode = ViewMode::Subview;
|
||||||
self.mode = ViewMode::Subview;
|
let colors = crate::conf::value(context, "mail.view.body");
|
||||||
let colors = crate::conf::value(context, "mail.view.body");
|
self.subview = Some(Box::new(Pager::from_string(
|
||||||
self.subview = Some(Box::new(Pager::from_string(
|
String::from_utf8_lossy(&decode_rec(u, None)).to_string(),
|
||||||
String::from_utf8_lossy(&decode_rec(u, None)).to_string(),
|
Some(context),
|
||||||
Some(context),
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
colors,
|
||||||
colors,
|
)));
|
||||||
)));
|
}
|
||||||
}
|
|
||||||
|
|
||||||
ContentType::Text { .. }
|
ContentType::Text { .. }
|
||||||
| ContentType::PGPSignature
|
| ContentType::PGPSignature
|
||||||
| ContentType::CMSSignature => {
|
| ContentType::CMSSignature => {
|
||||||
self.mode = ViewMode::Attachment(lidx);
|
self.mode = ViewMode::Attachment(lidx);
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
ContentType::Multipart { .. } => {
|
ContentType::Multipart { .. } => {
|
||||||
context.replies.push_back(UIEvent::StatusEvent(
|
context.replies.push_back(UIEvent::StatusEvent(
|
||||||
StatusEvent::DisplayMessage(
|
StatusEvent::DisplayMessage(
|
||||||
"Multipart attachments are not supported yet.".to_string(),
|
"Multipart attachments are not supported yet.".to_string(),
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
ContentType::Other { .. } => {
|
ContentType::Other { .. } => {
|
||||||
let attachment_type = u.mime_type();
|
let attachment_type = u.mime_type();
|
||||||
let filename = u.filename();
|
let filename = u.filename();
|
||||||
if let Ok(command) = query_default_app(&attachment_type) {
|
if let Ok(command) = query_default_app(&attachment_type) {
|
||||||
let p = create_temp_file(
|
let p = create_temp_file(
|
||||||
&decode(u, None),
|
&decode(u, None),
|
||||||
filename.as_ref().map(|s| s.as_str()),
|
filename.as_deref(),
|
||||||
None,
|
None,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
let (exec_cmd, argument) = super::desktop_exec_to_command(
|
let (exec_cmd, argument) = super::desktop_exec_to_command(
|
||||||
&command,
|
&command,
|
||||||
p.path.display().to_string(),
|
p.path.display().to_string(),
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
match Command::new(&exec_cmd)
|
match Command::new(&exec_cmd)
|
||||||
.arg(&argument)
|
.arg(&argument)
|
||||||
.stdin(Stdio::piped())
|
.stdin(Stdio::piped())
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.spawn()
|
.spawn()
|
||||||
{
|
{
|
||||||
Ok(child) => {
|
Ok(child) => {
|
||||||
context.temp_files.push(p);
|
context.temp_files.push(p);
|
||||||
context.children.push(child);
|
context.children.push(child);
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
context.replies.push_back(UIEvent::StatusEvent(
|
|
||||||
StatusEvent::DisplayMessage(format!(
|
|
||||||
"Failed to start `{} {}`: {}",
|
|
||||||
&exec_cmd, &argument, err
|
|
||||||
)),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
Err(err) => {
|
||||||
context.replies.push_back(UIEvent::StatusEvent(
|
context.replies.push_back(UIEvent::StatusEvent(
|
||||||
|
StatusEvent::DisplayMessage(format!(
|
||||||
|
"Failed to start `{} {}`: {}",
|
||||||
|
&exec_cmd, &argument, err
|
||||||
|
)),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
context.replies.push_back(UIEvent::StatusEvent(
|
||||||
StatusEvent::DisplayMessage(if let Some(filename) = filename.as_ref() {
|
StatusEvent::DisplayMessage(if let Some(filename) = filename.as_ref() {
|
||||||
format!(
|
format!(
|
||||||
"Couldn't find a default application for file {} (type {})",
|
"Couldn't find a default application for file {} (type {})",
|
||||||
|
@ -445,29 +440,28 @@ impl Component for EnvelopeView {
|
||||||
attachment_type
|
attachment_type
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ContentType::OctetStream { .. } => {
|
|
||||||
context.replies.push_back(UIEvent::StatusEvent(
|
|
||||||
StatusEvent::DisplayMessage(
|
|
||||||
"application/octet-stream isn't supported yet".to_string(),
|
|
||||||
),
|
|
||||||
));
|
));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
ContentType::OctetStream { .. } => {
|
||||||
context.replies.push_back(UIEvent::StatusEvent(
|
context.replies.push_back(UIEvent::StatusEvent(
|
||||||
StatusEvent::DisplayMessage(format!(
|
StatusEvent::DisplayMessage(
|
||||||
"Attachment `{}` not found.",
|
"application/octet-stream isn't supported yet".to_string(),
|
||||||
lidx
|
),
|
||||||
)),
|
));
|
||||||
));
|
return true;
|
||||||
return true;
|
}
|
||||||
}
|
}
|
||||||
};
|
} else {
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::DisplayMessage(format!(
|
||||||
|
"Attachment `{}` not found.",
|
||||||
|
lidx
|
||||||
|
))));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
UIEvent::Input(Key::Char('g'))
|
UIEvent::Input(Key::Char('g'))
|
||||||
|
@ -492,22 +486,16 @@ impl Component for EnvelopeView {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let url_launcher = context
|
let url_launcher = context.settings.pager.url_launcher.as_deref().unwrap_or(
|
||||||
.settings
|
#[cfg(target_os = "macos")]
|
||||||
.pager
|
{
|
||||||
.url_launcher
|
"open"
|
||||||
.as_ref()
|
},
|
||||||
.map(|s| s.as_str())
|
#[cfg(not(target_os = "macos"))]
|
||||||
.unwrap_or(
|
{
|
||||||
#[cfg(target_os = "macos")]
|
"xdg-open"
|
||||||
{
|
},
|
||||||
"open"
|
);
|
||||||
},
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
|
||||||
{
|
|
||||||
"xdg-open"
|
|
||||||
},
|
|
||||||
);
|
|
||||||
match Command::new(url_launcher)
|
match Command::new(url_launcher)
|
||||||
.arg(url)
|
.arg(url)
|
||||||
.stdin(Stdio::piped())
|
.stdin(Stdio::piped())
|
||||||
|
|
|
@ -415,11 +415,7 @@ impl ThreadView {
|
||||||
idx: usize,
|
idx: usize,
|
||||||
context: &Context,
|
context: &Context,
|
||||||
) {
|
) {
|
||||||
let visibles: Vec<&usize> = self
|
let visibles: Vec<&usize> = self.visible_entries.iter().flat_map(|v| v.iter()).collect();
|
||||||
.visible_entries
|
|
||||||
.iter()
|
|
||||||
.flat_map(|ref v| v.iter())
|
|
||||||
.collect();
|
|
||||||
if idx == *visibles[self.cursor_pos] {
|
if idx == *visibles[self.cursor_pos] {
|
||||||
let theme_default = crate::conf::value(context, "theme_default");
|
let theme_default = crate::conf::value(context, "theme_default");
|
||||||
let bg_color = crate::conf::value(context, "highlight").bg;
|
let bg_color = crate::conf::value(context, "highlight").bg;
|
||||||
|
@ -514,11 +510,8 @@ impl ThreadView {
|
||||||
if page_no != prev_page_no {
|
if page_no != prev_page_no {
|
||||||
clear_area(grid, area, crate::conf::value(context, "theme_default"));
|
clear_area(grid, area, crate::conf::value(context, "theme_default"));
|
||||||
}
|
}
|
||||||
let visibles: Vec<&usize> = self
|
let visibles: Vec<&usize> =
|
||||||
.visible_entries
|
self.visible_entries.iter().flat_map(|v| v.iter()).collect();
|
||||||
.iter()
|
|
||||||
.flat_map(|ref v| v.iter())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
for (visible_entry_counter, v) in visibles.iter().skip(top_idx).take(rows).enumerate() {
|
for (visible_entry_counter, v) in visibles.iter().skip(top_idx).take(rows).enumerate() {
|
||||||
if visible_entry_counter >= rows {
|
if visible_entry_counter >= rows {
|
||||||
|
@ -595,11 +588,8 @@ impl ThreadView {
|
||||||
self.cursor_pos = self.new_cursor_pos;
|
self.cursor_pos = self.new_cursor_pos;
|
||||||
/* If cursor position has changed, remove the highlight from the previous position and
|
/* If cursor position has changed, remove the highlight from the previous position and
|
||||||
* apply it in the new one. */
|
* apply it in the new one. */
|
||||||
let visibles: Vec<&usize> = self
|
let visibles: Vec<&usize> =
|
||||||
.visible_entries
|
self.visible_entries.iter().flat_map(|v| v.iter()).collect();
|
||||||
.iter()
|
|
||||||
.flat_map(|ref v| v.iter())
|
|
||||||
.collect();
|
|
||||||
for &idx in &[old_cursor_pos, self.cursor_pos] {
|
for &idx in &[old_cursor_pos, self.cursor_pos] {
|
||||||
let entry_idx = *visibles[idx];
|
let entry_idx = *visibles[idx];
|
||||||
let src_area = { get_entry_area(entry_idx, &self.entries) };
|
let src_area = { get_entry_area(entry_idx, &self.entries) };
|
||||||
|
@ -948,11 +938,7 @@ impl ThreadView {
|
||||||
|
|
||||||
/// Current position in self.entries (not in drawn entries which might exclude nonvisible ones)
|
/// Current position in self.entries (not in drawn entries which might exclude nonvisible ones)
|
||||||
fn current_pos(&self) -> usize {
|
fn current_pos(&self) -> usize {
|
||||||
let visibles: Vec<&usize> = self
|
let visibles: Vec<&usize> = self.visible_entries.iter().flat_map(|v| v.iter()).collect();
|
||||||
.visible_entries
|
|
||||||
.iter()
|
|
||||||
.flat_map(|ref v| v.iter())
|
|
||||||
.collect();
|
|
||||||
*visibles[self.new_cursor_pos]
|
*visibles[self.new_cursor_pos]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -999,15 +985,12 @@ impl Component for ThreadView {
|
||||||
self.dirty = false;
|
self.dirty = false;
|
||||||
}
|
}
|
||||||
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
|
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
|
||||||
match event {
|
if let UIEvent::Action(Listing(OpenInNewTab)) = event {
|
||||||
UIEvent::Action(Listing(OpenInNewTab)) => {
|
/* Handle this before self.mailview does */
|
||||||
/* Handle this before self.mailview does */
|
context
|
||||||
context
|
.replies
|
||||||
.replies
|
.push_back(UIEvent::Action(Tab(New(Some(Box::new(self.clone()))))));
|
||||||
.push_back(UIEvent::Action(Tab(New(Some(Box::new(self.clone()))))));
|
return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.show_mailview && self.mailview.process_event(event, context) {
|
if self.show_mailview && self.mailview.process_event(event, context) {
|
||||||
|
|
|
@ -169,7 +169,7 @@ mod dbus {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Passes notifications to a user defined shell command
|
/// Passes notifications to a user defined shell command
|
||||||
#[derive(Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct NotificationCommand {}
|
pub struct NotificationCommand {}
|
||||||
|
|
||||||
impl NotificationCommand {
|
impl NotificationCommand {
|
||||||
|
@ -204,7 +204,7 @@ impl Component for NotificationCommand {
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log(
|
log(
|
||||||
format!("Could not run notification script: {}.", err.to_string()),
|
format!("Could not run notification script: {}.", err),
|
||||||
ERROR,
|
ERROR,
|
||||||
);
|
);
|
||||||
debug!("Could not run notification script: {:?}", err);
|
debug!("Could not run notification script: {:?}", err);
|
||||||
|
@ -226,10 +226,7 @@ impl Component for NotificationCommand {
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log(
|
log(
|
||||||
format!(
|
format!("Could not run notification script: {}.", err),
|
||||||
"Could not run notification script: {}.",
|
|
||||||
err.to_string()
|
|
||||||
),
|
|
||||||
ERROR,
|
ERROR,
|
||||||
);
|
);
|
||||||
debug!("Could not run notification script: {:?}", err);
|
debug!("Could not run notification script: {:?}", err);
|
||||||
|
|
|
@ -67,7 +67,7 @@ pub struct StatusBar {
|
||||||
done_jobs: HashSet<JobId>,
|
done_jobs: HashSet<JobId>,
|
||||||
scroll_contexts: IndexMap<ComponentId, ScrollContext>,
|
scroll_contexts: IndexMap<ComponentId, ScrollContext>,
|
||||||
|
|
||||||
auto_complete: AutoComplete,
|
auto_complete: Box<AutoComplete>,
|
||||||
cmd_history: Vec<String>,
|
cmd_history: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,7 +136,7 @@ impl StatusBar {
|
||||||
.set_bg(attribute.bg)
|
.set_bg(attribute.bg)
|
||||||
.set_attrs(attribute.attrs);
|
.set_attrs(attribute.attrs);
|
||||||
}
|
}
|
||||||
let offset = self.status.find('|').unwrap_or_else(|| self.status.len());
|
let offset = self.status.find('|').unwrap_or(self.status.len());
|
||||||
if y < get_y(bottom_right!(area)) + 1 {
|
if y < get_y(bottom_right!(area)) + 1 {
|
||||||
for x in get_x(upper_left!(area))
|
for x in get_x(upper_left!(area))
|
||||||
..std::cmp::min(
|
..std::cmp::min(
|
||||||
|
@ -209,8 +209,7 @@ impl StatusBar {
|
||||||
.settings
|
.settings
|
||||||
.terminal
|
.terminal
|
||||||
.mouse_flag
|
.mouse_flag
|
||||||
.as_ref()
|
.as_deref()
|
||||||
.map(|s| s.as_str())
|
|
||||||
.unwrap_or("🖱️ ")
|
.unwrap_or("🖱️ ")
|
||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
|
@ -362,7 +361,7 @@ impl Component for StatusBar {
|
||||||
}
|
}
|
||||||
|
|
||||||
suggestions.sort_by(|a, b| a.entry.cmp(&b.entry));
|
suggestions.sort_by(|a, b| a.entry.cmp(&b.entry));
|
||||||
suggestions.dedup_by(|a, b| &a.entry == &b.entry);
|
suggestions.dedup_by(|a, b| a.entry == b.entry);
|
||||||
if self.auto_complete.set_suggestions(suggestions) {
|
if self.auto_complete.set_suggestions(suggestions) {
|
||||||
let len = self.auto_complete.suggestions().len() - 1;
|
let len = self.auto_complete.suggestions().len() - 1;
|
||||||
self.auto_complete.set_cursor(len);
|
self.auto_complete.set_cursor(len);
|
||||||
|
@ -529,7 +528,7 @@ impl Component for StatusBar {
|
||||||
self.container.set_dirty(true);
|
self.container.set_dirty(true);
|
||||||
}
|
}
|
||||||
UIEvent::ChangeMode(m) => {
|
UIEvent::ChangeMode(m) => {
|
||||||
let offset = self.status.find('|').unwrap_or_else(|| self.status.len());
|
let offset = self.status.find('|').unwrap_or(self.status.len());
|
||||||
self.status.replace_range(
|
self.status.replace_range(
|
||||||
..offset,
|
..offset,
|
||||||
&format!(
|
&format!(
|
||||||
|
@ -540,8 +539,7 @@ impl Component for StatusBar {
|
||||||
.settings
|
.settings
|
||||||
.terminal
|
.terminal
|
||||||
.mouse_flag
|
.mouse_flag
|
||||||
.as_ref()
|
.as_deref()
|
||||||
.map(|s| s.as_str())
|
|
||||||
.unwrap_or("🖱️ ")
|
.unwrap_or("🖱️ ")
|
||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
|
@ -559,7 +557,7 @@ impl Component for StatusBar {
|
||||||
.replies
|
.replies
|
||||||
.push_back(UIEvent::Command(self.ex_buffer.as_str().to_string()));
|
.push_back(UIEvent::Command(self.ex_buffer.as_str().to_string()));
|
||||||
}
|
}
|
||||||
if parse_command(&self.ex_buffer.as_str().as_bytes()).is_ok()
|
if parse_command(self.ex_buffer.as_str().as_bytes()).is_ok()
|
||||||
&& self.cmd_history.last().map(String::as_str)
|
&& self.cmd_history.last().map(String::as_str)
|
||||||
!= Some(self.ex_buffer.as_str())
|
!= Some(self.ex_buffer.as_str())
|
||||||
{
|
{
|
||||||
|
@ -1138,7 +1136,7 @@ impl Component for Tabbed {
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
write_string_to_grid(
|
write_string_to_grid(
|
||||||
&k,
|
k,
|
||||||
&mut self.help_content,
|
&mut self.help_content,
|
||||||
self.theme_default.fg,
|
self.theme_default.fg,
|
||||||
self.theme_default.bg,
|
self.theme_default.bg,
|
||||||
|
@ -1485,13 +1483,13 @@ impl Component for Tabbed {
|
||||||
self.help_screen_cursor.1 = self.help_screen_cursor.1.saturating_sub(1);
|
self.help_screen_cursor.1 = self.help_screen_cursor.1.saturating_sub(1);
|
||||||
}
|
}
|
||||||
_ if shortcut!(key == shortcuts["general"]["scroll_down"]) => {
|
_ if shortcut!(key == shortcuts["general"]["scroll_down"]) => {
|
||||||
self.help_screen_cursor.1 = self.help_screen_cursor.1 + 1;
|
self.help_screen_cursor.1 += 1;
|
||||||
}
|
}
|
||||||
_ if shortcut!(key == shortcuts["general"]["scroll_left"]) => {
|
_ if shortcut!(key == shortcuts["general"]["scroll_left"]) => {
|
||||||
self.help_screen_cursor.0 = self.help_screen_cursor.0.saturating_sub(1);
|
self.help_screen_cursor.0 = self.help_screen_cursor.0.saturating_sub(1);
|
||||||
}
|
}
|
||||||
_ if shortcut!(key == shortcuts["general"]["scroll_right"]) => {
|
_ if shortcut!(key == shortcuts["general"]["scroll_right"]) => {
|
||||||
self.help_screen_cursor.0 = self.help_screen_cursor.0 + 1;
|
self.help_screen_cursor.0 += 1;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
/* ignore, don't pass to components below the shortcut panel */
|
/* ignore, don't pass to components below the shortcut panel */
|
||||||
|
@ -1600,7 +1598,7 @@ impl Component for RawBuffer {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
UIEvent::Input(Key::Right) => {
|
UIEvent::Input(Key::Right) => {
|
||||||
self.cursor.0 = self.cursor.0 + 1;
|
self.cursor.0 += 1;
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -1610,7 +1608,7 @@ impl Component for RawBuffer {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
UIEvent::Input(Key::Down) => {
|
UIEvent::Input(Key::Down) => {
|
||||||
self.cursor.1 = self.cursor.1 + 1;
|
self.cursor.1 += 1;
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -1641,9 +1639,6 @@ impl RawBuffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn title(&self) -> &str {
|
pub fn title(&self) -> &str {
|
||||||
self.title
|
self.title.as_deref().unwrap_or("untitled")
|
||||||
.as_ref()
|
|
||||||
.map(String::as_str)
|
|
||||||
.unwrap_or("untitled")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -760,7 +760,7 @@ impl<T: PartialEq + Debug + Clone + Sync + Send, F: 'static + Sync + Send> Selec
|
||||||
) -> Selector<T, F> {
|
) -> Selector<T, F> {
|
||||||
let entry_titles = entries
|
let entry_titles = entries
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.map(|(_id, ref mut title)| std::mem::replace(title, String::new()))
|
.map(|(_id, ref mut title)| std::mem::take(title))
|
||||||
.collect::<Vec<String>>();
|
.collect::<Vec<String>>();
|
||||||
let mut identifiers: Vec<(T, bool)> =
|
let mut identifiers: Vec<(T, bool)> =
|
||||||
entries.into_iter().map(|(id, _)| (id, false)).collect();
|
entries.into_iter().map(|(id, _)| (id, false)).collect();
|
||||||
|
@ -812,7 +812,7 @@ impl<T: PartialEq + Debug + Clone + Sync + Send, F: 'static + Sync + Send> Selec
|
||||||
if self.single_only {
|
if self.single_only {
|
||||||
for (i, e) in self.entry_titles.iter().enumerate() {
|
for (i, e) in self.entry_titles.iter().enumerate() {
|
||||||
write_string_to_grid(
|
write_string_to_grid(
|
||||||
&e,
|
e,
|
||||||
&mut content,
|
&mut content,
|
||||||
self.theme_default.fg,
|
self.theme_default.fg,
|
||||||
self.theme_default.bg,
|
self.theme_default.bg,
|
||||||
|
@ -824,7 +824,7 @@ impl<T: PartialEq + Debug + Clone + Sync + Send, F: 'static + Sync + Send> Selec
|
||||||
} else {
|
} else {
|
||||||
for (i, e) in self.entry_titles.iter().enumerate() {
|
for (i, e) in self.entry_titles.iter().enumerate() {
|
||||||
write_string_to_grid(
|
write_string_to_grid(
|
||||||
&format!("[ ] {}", &e),
|
&format!("[ ] {}", e),
|
||||||
&mut content,
|
&mut content,
|
||||||
self.theme_default.fg,
|
self.theme_default.fg,
|
||||||
self.theme_default.bg,
|
self.theme_default.bg,
|
||||||
|
|
|
@ -39,7 +39,6 @@ pub struct Pager {
|
||||||
colors: ThemeAttribute,
|
colors: ThemeAttribute,
|
||||||
initialised: bool,
|
initialised: bool,
|
||||||
show_scrollbar: bool,
|
show_scrollbar: bool,
|
||||||
content: CellBuffer,
|
|
||||||
filtered_content: Option<(String, Result<CellBuffer>)>,
|
filtered_content: Option<(String, Result<CellBuffer>)>,
|
||||||
text_lines: Vec<String>,
|
text_lines: Vec<String>,
|
||||||
line_breaker: LineBreakText,
|
line_breaker: LineBreakText,
|
||||||
|
@ -57,8 +56,10 @@ impl Pager {
|
||||||
pub const DESCRIPTION: &'static str = "pager";
|
pub const DESCRIPTION: &'static str = "pager";
|
||||||
const PAGES_AHEAD_TO_RENDER_NO: usize = 16;
|
const PAGES_AHEAD_TO_RENDER_NO: usize = 16;
|
||||||
pub fn new(context: &Context) -> Self {
|
pub fn new(context: &Context) -> Self {
|
||||||
let mut ret = Pager::default();
|
let mut ret = Pager {
|
||||||
ret.minimum_width = context.settings.pager.minimum_width;
|
minimum_width: context.settings.pager.minimum_width,
|
||||||
|
..Pager::default()
|
||||||
|
};
|
||||||
ret.set_colors(crate::conf::value(context, "theme_default"))
|
ret.set_colors(crate::conf::value(context, "theme_default"))
|
||||||
.set_reflow(if context.settings.pager.split_long_lines {
|
.set_reflow(if context.settings.pager.split_long_lines {
|
||||||
Reflow::All
|
Reflow::All
|
||||||
|
@ -178,10 +179,7 @@ impl Pager {
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.spawn()
|
.spawn()
|
||||||
.chain_err_summary(|| "Failed to start pager filter process")?;
|
.chain_err_summary(|| "Failed to start pager filter process")?;
|
||||||
let stdin = filter_child
|
let stdin = filter_child.stdin.as_mut().ok_or("failed to open stdin")?;
|
||||||
.stdin
|
|
||||||
.as_mut()
|
|
||||||
.ok_or_else(|| "failed to open stdin")?;
|
|
||||||
stdin
|
stdin
|
||||||
.write_all(text.as_bytes())
|
.write_all(text.as_bytes())
|
||||||
.chain_err_summary(|| "Failed to write to stdin")?;
|
.chain_err_summary(|| "Failed to write to stdin")?;
|
||||||
|
@ -196,7 +194,7 @@ impl Pager {
|
||||||
for b in out {
|
for b in out {
|
||||||
embedded.process_byte(&mut dev_null, b);
|
embedded.process_byte(&mut dev_null, b);
|
||||||
}
|
}
|
||||||
Ok(std::mem::replace(embedded.buffer_mut(), Default::default()))
|
Ok(std::mem::take(embedded.buffer_mut()))
|
||||||
};
|
};
|
||||||
let buf = _f(cmd, &self.text);
|
let buf = _f(cmd, &self.text);
|
||||||
if let Some((width, height)) = buf.as_ref().ok().map(CellBuffer::size) {
|
if let Some((width, height)) = buf.as_ref().ok().map(CellBuffer::size) {
|
||||||
|
@ -301,7 +299,7 @@ impl Pager {
|
||||||
Ok(ref content) => {
|
Ok(ref content) => {
|
||||||
copy_area(
|
copy_area(
|
||||||
grid,
|
grid,
|
||||||
&content,
|
content,
|
||||||
area,
|
area,
|
||||||
(
|
(
|
||||||
(
|
(
|
||||||
|
@ -353,7 +351,7 @@ impl Pager {
|
||||||
(upper_left, bottom_right),
|
(upper_left, bottom_right),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
if l.starts_with("⤷") {
|
if l.starts_with('⤷') {
|
||||||
grid[upper_left]
|
grid[upper_left]
|
||||||
.set_fg(crate::conf::value(context, "highlight").fg)
|
.set_fg(crate::conf::value(context, "highlight").fg)
|
||||||
.set_attrs(crate::conf::value(context, "highlight").attrs);
|
.set_attrs(crate::conf::value(context, "highlight").attrs);
|
||||||
|
|
|
@ -42,7 +42,7 @@ impl Default for FormFocus {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Field {
|
pub enum Field {
|
||||||
Text(UText, Option<(AutoCompleteFn, AutoComplete)>),
|
Text(UText, Option<(AutoCompleteFn, Box<AutoComplete>)>),
|
||||||
Choice(Vec<Cow<'static, str>>, Cursor),
|
Choice(Vec<Cow<'static, str>>, Cursor),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -456,6 +456,10 @@ impl<T: 'static + std::fmt::Debug + Copy + Default + Send + Sync> FormWidget<T>
|
||||||
self.layout.len()
|
self.layout.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.layout.len() == 0
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_button(&mut self, val: (Cow<'static, str>, T)) {
|
pub fn add_button(&mut self, val: (Cow<'static, str>, T)) {
|
||||||
self.buttons.push(val);
|
self.buttons.push(val);
|
||||||
}
|
}
|
||||||
|
@ -1003,7 +1007,7 @@ impl Component for AutoComplete {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AutoComplete {
|
impl AutoComplete {
|
||||||
pub fn new(entries: Vec<AutoCompleteEntry>) -> Self {
|
pub fn new(entries: Vec<AutoCompleteEntry>) -> Box<Self> {
|
||||||
let mut ret = AutoComplete {
|
let mut ret = AutoComplete {
|
||||||
entries: Vec::new(),
|
entries: Vec::new(),
|
||||||
content: CellBuffer::default(),
|
content: CellBuffer::default(),
|
||||||
|
@ -1012,7 +1016,7 @@ impl AutoComplete {
|
||||||
id: ComponentId::new_v4(),
|
id: ComponentId::new_v4(),
|
||||||
};
|
};
|
||||||
ret.set_suggestions(entries);
|
ret.set_suggestions(entries);
|
||||||
ret
|
Box::new(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_suggestions(&mut self, entries: Vec<AutoCompleteEntry>) -> bool {
|
pub fn set_suggestions(&mut self, entries: Vec<AutoCompleteEntry>) -> bool {
|
||||||
|
@ -1401,7 +1405,7 @@ impl Component for ProgressSpinner {
|
||||||
if self.active {
|
if self.active {
|
||||||
write_string_to_grid(
|
write_string_to_grid(
|
||||||
match self.kind.as_ref() {
|
match self.kind.as_ref() {
|
||||||
Ok(kind) => (Self::KINDS[*kind].1)[self.stage].as_ref(),
|
Ok(kind) => (Self::KINDS[*kind].1)[self.stage],
|
||||||
Err(custom) => custom[self.stage].as_ref(),
|
Err(custom) => custom[self.stage].as_ref(),
|
||||||
},
|
},
|
||||||
grid,
|
grid,
|
||||||
|
|
23
src/conf.rs
23
src/conf.rs
|
@ -250,7 +250,7 @@ impl From<FileAccount> for AccountConf {
|
||||||
let root_mailbox = x.root_mailbox.clone();
|
let root_mailbox = x.root_mailbox.clone();
|
||||||
let identity = x.identity.clone();
|
let identity = x.identity.clone();
|
||||||
let display_name = x.display_name.clone();
|
let display_name = x.display_name.clone();
|
||||||
let order = x.order.clone();
|
let order = x.order;
|
||||||
let mailboxes = x
|
let mailboxes = x
|
||||||
.mailboxes
|
.mailboxes
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -365,13 +365,14 @@ impl FileSettings {
|
||||||
|
|
||||||
pub fn validate(path: PathBuf, interactive: bool, clear_extras: bool) -> Result<Self> {
|
pub fn validate(path: PathBuf, interactive: bool, clear_extras: bool) -> Result<Self> {
|
||||||
let s = pp::pp(&path)?;
|
let s = pp::pp(&path)?;
|
||||||
let map: toml::map::Map<String, toml::value::Value> = toml::from_str(&s).map_err(|e| {
|
let map: toml::map::Map<String, toml::value::Value> =
|
||||||
MeliError::new(format!(
|
toml::from_str(&s).map_err(|err| {
|
||||||
"{}:\nConfig file is invalid TOML: {}",
|
MeliError::new(format!(
|
||||||
path.display(),
|
"{}:\nConfig file is invalid TOML: {}",
|
||||||
e.to_string()
|
path.display(),
|
||||||
))
|
err
|
||||||
})?;
|
))
|
||||||
|
})?;
|
||||||
/*
|
/*
|
||||||
* Check that a global composing option is set and return a user-friendly error message because the
|
* Check that a global composing option is set and return a user-friendly error message because the
|
||||||
* default serde one is confusing.
|
* default serde one is confusing.
|
||||||
|
@ -410,11 +411,11 @@ This is required so that you don't accidentally start meli and find out later th
|
||||||
path.display()
|
path.display()
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
let mut s: FileSettings = toml::from_str(&s).map_err(|e| {
|
let mut s: FileSettings = toml::from_str(&s).map_err(|err| {
|
||||||
MeliError::new(format!(
|
MeliError::new(format!(
|
||||||
"{}:\nConfig file contains errors: {}",
|
"{}:\nConfig file contains errors: {}",
|
||||||
path.display(),
|
path.display(),
|
||||||
e.to_string()
|
err
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
let backends = melib::backends::Backends::new();
|
let backends = melib::backends::Backends::new();
|
||||||
|
@ -831,7 +832,7 @@ mod pp {
|
||||||
}
|
}
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
return Ok(("", None));
|
Ok(("", None))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,18 +89,11 @@ impl Default for MailboxStatus {
|
||||||
|
|
||||||
impl MailboxStatus {
|
impl MailboxStatus {
|
||||||
pub fn is_available(&self) -> bool {
|
pub fn is_available(&self) -> bool {
|
||||||
if let MailboxStatus::Available = self {
|
matches!(self, MailboxStatus::Available)
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_parsing(&self) -> bool {
|
pub fn is_parsing(&self) -> bool {
|
||||||
if let MailboxStatus::Parsing(_, _) = self {
|
matches!(self, MailboxStatus::Parsing(_, _))
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +125,7 @@ impl MailboxEntry {
|
||||||
if let Some(name) = self.conf.mailbox_conf.alias.as_ref() {
|
if let Some(name) = self.conf.mailbox_conf.alias.as_ref() {
|
||||||
name
|
name
|
||||||
} else {
|
} else {
|
||||||
&self.ref_mailbox.name()
|
self.ref_mailbox.name()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,6 +158,7 @@ pub enum JobRequest {
|
||||||
},
|
},
|
||||||
Fetch {
|
Fetch {
|
||||||
mailbox_hash: MailboxHash,
|
mailbox_hash: MailboxHash,
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
handle: JoinHandle<(
|
handle: JoinHandle<(
|
||||||
Option<Result<Vec<Envelope>>>,
|
Option<Result<Vec<Envelope>>>,
|
||||||
Pin<Box<dyn Stream<Item = Result<Vec<Envelope>>> + Send + 'static>>,
|
Pin<Box<dyn Stream<Item = Result<Vec<Envelope>>> + Send + 'static>>,
|
||||||
|
@ -345,26 +339,17 @@ impl core::fmt::Display for JobRequest {
|
||||||
|
|
||||||
impl JobRequest {
|
impl JobRequest {
|
||||||
pub fn is_watch(&self) -> bool {
|
pub fn is_watch(&self) -> bool {
|
||||||
match self {
|
matches!(self, JobRequest::Watch { .. })
|
||||||
JobRequest::Watch { .. } => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_fetch(&self, mailbox_hash: MailboxHash) -> bool {
|
pub fn is_fetch(&self, mailbox_hash: MailboxHash) -> bool {
|
||||||
match self {
|
matches!(self, JobRequest::Fetch {
|
||||||
JobRequest::Fetch {
|
mailbox_hash: h, ..
|
||||||
mailbox_hash: h, ..
|
} if *h == mailbox_hash)
|
||||||
} if *h == mailbox_hash => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_online(&self) -> bool {
|
pub fn is_online(&self) -> bool {
|
||||||
match self {
|
matches!(self, JobRequest::IsOnline { .. })
|
||||||
JobRequest::IsOnline { .. } => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,7 +373,6 @@ impl Drop for Account {
|
||||||
let writer = io::BufWriter::new(f);
|
let writer = io::BufWriter::new(f);
|
||||||
if let Err(err) = serde_json::to_writer(writer, &self.address_book) {
|
if let Err(err) = serde_json::to_writer(writer, &self.address_book) {
|
||||||
eprintln!("{}", err);
|
eprintln!("{}", err);
|
||||||
return;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
/*
|
/*
|
||||||
|
@ -455,7 +439,7 @@ impl Account {
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let data_dir = xdg::BaseDirectories::with_profile("meli", &name).unwrap();
|
let data_dir = xdg::BaseDirectories::with_profile("meli", &name).unwrap();
|
||||||
let mut address_book = AddressBook::with_account(&settings.account());
|
let mut address_book = AddressBook::with_account(settings.account());
|
||||||
|
|
||||||
if let Ok(data) = data_dir.place_data_file("addressbook") {
|
if let Ok(data) = data_dir.place_data_file("addressbook") {
|
||||||
if data.exists() {
|
if data.exists() {
|
||||||
|
@ -730,7 +714,7 @@ impl Account {
|
||||||
format!(
|
format!(
|
||||||
"Failed to update envelope {} in cache: {}",
|
"Failed to update envelope {} in cache: {}",
|
||||||
envelope.message_id_display(),
|
envelope.message_id_display(),
|
||||||
err.to_string()
|
err
|
||||||
),
|
),
|
||||||
melib::ERROR,
|
melib::ERROR,
|
||||||
);
|
);
|
||||||
|
@ -804,7 +788,7 @@ impl Account {
|
||||||
"Failed to update envelope {} in cache: {}",
|
"Failed to update envelope {} in cache: {}",
|
||||||
self.collection.envelopes.read().unwrap()[&env_hash]
|
self.collection.envelopes.read().unwrap()[&env_hash]
|
||||||
.message_id_display(),
|
.message_id_display(),
|
||||||
err.to_string()
|
err
|
||||||
),
|
),
|
||||||
melib::ERROR,
|
melib::ERROR,
|
||||||
);
|
);
|
||||||
|
@ -834,7 +818,7 @@ impl Account {
|
||||||
"Failed to update envelope {} in cache: {}",
|
"Failed to update envelope {} in cache: {}",
|
||||||
&self.collection.envelopes.read().unwrap()[&new_hash]
|
&self.collection.envelopes.read().unwrap()[&new_hash]
|
||||||
.message_id_display(),
|
.message_id_display(),
|
||||||
err.to_string()
|
err
|
||||||
),
|
),
|
||||||
melib::ERROR,
|
melib::ERROR,
|
||||||
);
|
);
|
||||||
|
@ -961,7 +945,7 @@ impl Account {
|
||||||
"Failed to remove envelope {} [{}] in cache: {}",
|
"Failed to remove envelope {} [{}] in cache: {}",
|
||||||
&envelopes[&env_hash].message_id_display(),
|
&envelopes[&env_hash].message_id_display(),
|
||||||
env_hash,
|
env_hash,
|
||||||
err.to_string()
|
err
|
||||||
),
|
),
|
||||||
melib::ERROR,
|
melib::ERROR,
|
||||||
);
|
);
|
||||||
|
@ -1193,14 +1177,10 @@ impl Account {
|
||||||
self.special_use_mailbox(SpecialUsageMailbox::Normal),
|
self.special_use_mailbox(SpecialUsageMailbox::Normal),
|
||||||
] {
|
] {
|
||||||
if let Some(mailbox_hash) = mailbox {
|
if let Some(mailbox_hash) = mailbox {
|
||||||
if let Err(e) = self.save(bytes, *mailbox_hash, Some(flags)) {
|
if let Err(err) = self.save(bytes, *mailbox_hash, Some(flags)) {
|
||||||
debug!("{:?} could not save msg", e);
|
debug!("{:?} could not save msg", err);
|
||||||
melib::log(
|
melib::log(
|
||||||
format!(
|
format!("Could not save in '{}' mailbox: {}.", *mailbox_hash, err),
|
||||||
"Could not save in '{}' mailbox: {}.",
|
|
||||||
*mailbox_hash,
|
|
||||||
e.to_string()
|
|
||||||
),
|
|
||||||
melib::ERROR,
|
melib::ERROR,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1311,9 +1291,7 @@ impl Account {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
melib::log(&error_message, melib::LoggingLevel::ERROR);
|
melib::log(&error_message, melib::LoggingLevel::ERROR);
|
||||||
return Err(
|
return Err(MeliError::new(error_message).set_summary("Message not sent."));
|
||||||
MeliError::new(error_message.clone()).set_summary("Message not sent.")
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
@ -1334,11 +1312,11 @@ impl Account {
|
||||||
}
|
}
|
||||||
SendMail::ServerSubmission => {
|
SendMail::ServerSubmission => {
|
||||||
if self.backend_capabilities.supports_submission {
|
if self.backend_capabilities.supports_submission {
|
||||||
let job = self.backend.write().unwrap().submit(
|
let job =
|
||||||
message.clone().into_bytes(),
|
self.backend
|
||||||
None,
|
.write()
|
||||||
None,
|
.unwrap()
|
||||||
)?;
|
.submit(message.into_bytes(), None, None)?;
|
||||||
|
|
||||||
let handle = if self.backend_capabilities.is_async {
|
let handle = if self.backend_capabilities.is_async {
|
||||||
self.job_executor.spawn_specialized(job)
|
self.job_executor.spawn_specialized(job)
|
||||||
|
@ -1348,8 +1326,8 @@ impl Account {
|
||||||
self.insert_job(handle.job_id, JobRequest::SendMessageBackground { handle });
|
self.insert_job(handle.job_id, JobRequest::SendMessageBackground { handle });
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
return Err(MeliError::new("Server does not support submission.")
|
Err(MeliError::new("Server does not support submission.")
|
||||||
.set_summary("Message not sent."));
|
.set_summary("Message not sent."))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1400,8 +1378,9 @@ impl Account {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
melib::log(&error_message, melib::LoggingLevel::ERROR);
|
melib::log(&error_message, melib::LoggingLevel::ERROR);
|
||||||
return Err(MeliError::new(error_message.clone())
|
return Err(
|
||||||
.set_summary("Message not sent."));
|
MeliError::new(error_message).set_summary("Message not sent.")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1423,8 +1402,8 @@ impl Account {
|
||||||
fut.await?;
|
fut.await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
return Err(MeliError::new("Server does not support submission.")
|
Err(MeliError::new("Server does not support submission.")
|
||||||
.set_summary("Message not sent."));
|
.set_summary("Message not sent."))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1537,11 +1516,7 @@ impl Account {
|
||||||
.mailbox_entries
|
.mailbox_entries
|
||||||
.iter()
|
.iter()
|
||||||
.find(|(_, f)| f.conf.mailbox_conf().usage == Some(special_use));
|
.find(|(_, f)| f.conf.mailbox_conf().usage == Some(special_use));
|
||||||
if let Some(ret) = ret.as_ref() {
|
ret.as_ref().map(|ret| ret.1.ref_mailbox.hash())
|
||||||
Some(ret.1.ref_mailbox.hash())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Call only in Context::is_online, since only Context can launch the watcher threads if an
|
/* Call only in Context::is_online, since only Context can launch the watcher threads if an
|
||||||
|
@ -1575,7 +1550,7 @@ impl Account {
|
||||||
self.insert_job(handle.job_id, JobRequest::IsOnline { handle });
|
self.insert_job(handle.job_id, JobRequest::IsOnline { handle });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return self.is_online.clone();
|
self.is_online.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn search(
|
pub fn search(
|
||||||
|
@ -2272,7 +2247,7 @@ fn build_mailboxes_order(
|
||||||
node
|
node
|
||||||
}
|
}
|
||||||
|
|
||||||
tree.push(rec(*h, &mailbox_entries, 0));
|
tree.push(rec(*h, mailbox_entries, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2356,7 +2331,6 @@ fn build_mailboxes_order(
|
||||||
fn rec(
|
fn rec(
|
||||||
node: &mut MailboxNode,
|
node: &mut MailboxNode,
|
||||||
mailbox_entries: &IndexMap<MailboxHash, MailboxEntry>,
|
mailbox_entries: &IndexMap<MailboxHash, MailboxEntry>,
|
||||||
depth: usize,
|
|
||||||
mut indentation: u32,
|
mut indentation: u32,
|
||||||
has_sibling: bool,
|
has_sibling: bool,
|
||||||
) {
|
) {
|
||||||
|
@ -2379,16 +2353,10 @@ fn build_mailboxes_order(
|
||||||
}
|
}
|
||||||
while let Some(i) = iter.next() {
|
while let Some(i) = iter.next() {
|
||||||
let c = &mut node.children[i];
|
let c = &mut node.children[i];
|
||||||
rec(
|
rec(c, mailbox_entries, indentation, iter.peek() != None);
|
||||||
c,
|
|
||||||
mailbox_entries,
|
|
||||||
depth + 1,
|
|
||||||
indentation,
|
|
||||||
iter.peek() != None,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rec(node, &mailbox_entries, 1, 0, false);
|
rec(node, mailbox_entries, 0, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,9 @@
|
||||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//! This module is automatically generated by build.rs.
|
#![allow(clippy::derivable_impls)]
|
||||||
|
|
||||||
|
//! This module is automatically generated by config_macros.rs.
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
|
|
@ -34,7 +34,7 @@ macro_rules! shortcut {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct Shortcuts {
|
pub struct Shortcuts {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
@ -55,21 +55,6 @@ pub struct Shortcuts {
|
||||||
pub pager: PagerShortcuts,
|
pub pager: PagerShortcuts,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Shortcuts {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
general: GeneralShortcuts::default(),
|
|
||||||
listing: ListingShortcuts::default(),
|
|
||||||
composing: ComposingShortcuts::default(),
|
|
||||||
compact_listing: CompactListingShortcuts::default(),
|
|
||||||
contact_list: ContactListShortcuts::default(),
|
|
||||||
envelope_view: EnvelopeViewShortcuts::default(),
|
|
||||||
thread_view: ThreadViewShortcuts::default(),
|
|
||||||
pager: PagerShortcuts::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DotAddressable for Shortcuts {
|
impl DotAddressable for Shortcuts {
|
||||||
fn lookup(&self, parent_field: &str, path: &[&str]) -> Result<String> {
|
fn lookup(&self, parent_field: &str, path: &[&str]) -> Result<String> {
|
||||||
match path.first() {
|
match path.first() {
|
||||||
|
|
|
@ -28,7 +28,7 @@ use serde::{Deserialize, Deserializer};
|
||||||
use std::collections::{hash_map::DefaultHasher, HashMap, HashSet};
|
use std::collections::{hash_map::DefaultHasher, HashMap, HashSet};
|
||||||
use std::hash::Hasher;
|
use std::hash::Hasher;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone, Serialize)]
|
#[derive(Default, Debug, Deserialize, Clone, Serialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct TagsSettings {
|
pub struct TagsSettings {
|
||||||
#[serde(default, deserialize_with = "tag_color_de")]
|
#[serde(default, deserialize_with = "tag_color_de")]
|
||||||
|
@ -37,15 +37,6 @@ pub struct TagsSettings {
|
||||||
pub ignore_tags: HashSet<u64>,
|
pub ignore_tags: HashSet<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TagsSettings {
|
|
||||||
fn default() -> Self {
|
|
||||||
TagsSettings {
|
|
||||||
colors: Default::default(),
|
|
||||||
ignore_tags: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tag_set_de<'de, D, T: std::convert::From<HashSet<u64>>>(
|
pub fn tag_set_de<'de, D, T: std::convert::From<HashSet<u64>>>(
|
||||||
deserializer: D,
|
deserializer: D,
|
||||||
) -> std::result::Result<T, D::Error>
|
) -> std::result::Result<T, D::Error>
|
||||||
|
|
|
@ -102,7 +102,7 @@ pub fn attrs(context: &Context, key: &'static str) -> Attr {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn unlink<'k, 't: 'k>(theme: &'t Theme, key: &'k Cow<'static, str>) -> ThemeAttribute {
|
fn unlink<'k, 't: 'k>(theme: &'t Theme, key: &'k str) -> ThemeAttribute {
|
||||||
ThemeAttribute {
|
ThemeAttribute {
|
||||||
fg: unlink_fg(theme, &ColorField::Fg, key),
|
fg: unlink_fg(theme, &ColorField::Fg, key),
|
||||||
bg: unlink_bg(theme, &ColorField::Bg, key),
|
bg: unlink_bg(theme, &ColorField::Bg, key),
|
||||||
|
@ -111,11 +111,7 @@ fn unlink<'k, 't: 'k>(theme: &'t Theme, key: &'k Cow<'static, str>) -> ThemeAttr
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn unlink_fg<'k, 't: 'k>(
|
fn unlink_fg<'k, 't: 'k>(theme: &'t Theme, mut field: &'k ColorField, mut key: &'k str) -> Color {
|
||||||
theme: &'t Theme,
|
|
||||||
mut field: &'k ColorField,
|
|
||||||
mut key: &'k Cow<'static, str>,
|
|
||||||
) -> Color {
|
|
||||||
loop {
|
loop {
|
||||||
match field {
|
match field {
|
||||||
ColorField::LikeSelf | ColorField::Fg => match &theme[key].fg {
|
ColorField::LikeSelf | ColorField::Fg => match &theme[key].fg {
|
||||||
|
@ -166,11 +162,7 @@ fn unlink_fg<'k, 't: 'k>(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn unlink_bg<'k, 't: 'k>(
|
fn unlink_bg<'k, 't: 'k>(theme: &'t Theme, mut field: &'k ColorField, mut key: &'k str) -> Color {
|
||||||
theme: &'t Theme,
|
|
||||||
mut field: &'k ColorField,
|
|
||||||
mut key: &'k Cow<'static, str>,
|
|
||||||
) -> Color {
|
|
||||||
loop {
|
loop {
|
||||||
match field {
|
match field {
|
||||||
ColorField::LikeSelf | ColorField::Bg => match &theme[key].bg {
|
ColorField::LikeSelf | ColorField::Bg => match &theme[key].bg {
|
||||||
|
@ -220,7 +212,7 @@ fn unlink_bg<'k, 't: 'k>(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn unlink_attrs<'k, 't: 'k>(theme: &'t Theme, mut key: &'k Cow<'static, str>) -> Attr {
|
fn unlink_attrs<'k, 't: 'k>(theme: &'t Theme, mut key: &'k str) -> Attr {
|
||||||
loop {
|
loop {
|
||||||
match &theme[key].attrs {
|
match &theme[key].attrs {
|
||||||
ThemeValue::Link(ref new_key, ()) => key = new_key,
|
ThemeValue::Link(ref new_key, ()) => key = new_key,
|
||||||
|
@ -416,8 +408,8 @@ impl<'de> Deserialize<'de> for ThemeValue<Attr> {
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
if let Ok(s) = <String>::deserialize(deserializer) {
|
if let Ok(s) = <String>::deserialize(deserializer) {
|
||||||
if s.starts_with("$") {
|
if let Some(stripped) = s.strip_prefix('$') {
|
||||||
Ok(ThemeValue::Alias(s[1..].to_string().into()))
|
Ok(ThemeValue::Alias(stripped.to_string().into()))
|
||||||
} else if let Ok(c) = Attr::from_string_de::<'de, D, String>(s.clone()) {
|
} else if let Ok(c) = Attr::from_string_de::<'de, D, String>(s.clone()) {
|
||||||
Ok(ThemeValue::Value(c))
|
Ok(ThemeValue::Value(c))
|
||||||
} else {
|
} else {
|
||||||
|
@ -467,8 +459,8 @@ impl<'de> Deserialize<'de> for ThemeValue<Color> {
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
if let Ok(s) = <String>::deserialize(deserializer) {
|
if let Ok(s) = <String>::deserialize(deserializer) {
|
||||||
if s.starts_with("$") {
|
if let Some(stripped) = s.strip_prefix('$') {
|
||||||
Ok(ThemeValue::Alias(s[1..].to_string().into()))
|
Ok(ThemeValue::Alias(stripped.to_string().into()))
|
||||||
} else if let Ok(c) = Color::from_string_de::<'de, D>(s.clone()) {
|
} else if let Ok(c) = Color::from_string_de::<'de, D>(s.clone()) {
|
||||||
Ok(ThemeValue::Value(c))
|
Ok(ThemeValue::Value(c))
|
||||||
} else if s.ends_with(".fg") {
|
} else if s.ends_with(".fg") {
|
||||||
|
@ -606,37 +598,26 @@ mod regexp {
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
loop {
|
loop {
|
||||||
let next_byte_offset = self.pcre_iter.next();
|
let next_byte_offset = self.pcre_iter.next()?;
|
||||||
if next_byte_offset.is_none() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let next_byte_offset = next_byte_offset.unwrap();
|
|
||||||
if next_byte_offset.is_err() {
|
if next_byte_offset.is_err() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let next_byte_offset = next_byte_offset.unwrap();
|
let next_byte_offset = next_byte_offset.unwrap();
|
||||||
|
|
||||||
let mut next_char_index = self.char_indices.next();
|
let mut next_char_index = self.char_indices.next()?;
|
||||||
if next_char_index.is_none() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
while next_byte_offset.start() < next_char_index.unwrap().0 {
|
while next_byte_offset.start() < next_char_index.0 {
|
||||||
self.char_offset += 1;
|
self.char_offset += 1;
|
||||||
next_char_index = self.char_indices.next();
|
next_char_index = self.char_indices.next()?;
|
||||||
if next_char_index.is_none() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let start = self.char_offset;
|
let start = self.char_offset;
|
||||||
|
|
||||||
while next_byte_offset.end()
|
while next_byte_offset.end()
|
||||||
>= self
|
> self
|
||||||
.char_indices
|
.char_indices
|
||||||
.next()
|
.next()
|
||||||
.map(|(v, _)| v)
|
.map(|(v, _)| v)
|
||||||
.unwrap_or(next_byte_offset.end())
|
.unwrap_or_else(|| next_byte_offset.end())
|
||||||
+ 1
|
|
||||||
{
|
{
|
||||||
self.char_offset += 1;
|
self.char_offset += 1;
|
||||||
}
|
}
|
||||||
|
@ -1219,7 +1200,7 @@ impl Themes {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
pub fn validate(&self) -> Result<()> {
|
pub fn validate(&self) -> Result<()> {
|
||||||
let hash_set: HashSet<&'static str> = DEFAULT_KEYS.into_iter().map(|k| *k).collect();
|
let hash_set: HashSet<&'static str> = DEFAULT_KEYS.iter().copied().collect();
|
||||||
Themes::validate_keys("light", &self.light, &hash_set)?;
|
Themes::validate_keys("light", &self.light, &hash_set)?;
|
||||||
Themes::validate_keys("dark", &self.dark, &hash_set)?;
|
Themes::validate_keys("dark", &self.dark, &hash_set)?;
|
||||||
for (name, t) in self.other_themes.iter() {
|
for (name, t) in self.other_themes.iter() {
|
||||||
|
@ -1255,51 +1236,49 @@ impl Themes {
|
||||||
t => self.other_themes.get(t).unwrap_or(&self.dark),
|
t => self.other_themes.get(t).unwrap_or(&self.dark),
|
||||||
};
|
};
|
||||||
let mut ret = String::new();
|
let mut ret = String::new();
|
||||||
ret.extend(format!("[terminal.themes.{}]\n", key).chars());
|
ret.push_str(&format!("[terminal.themes.{}]\n", key));
|
||||||
if unlink {
|
if unlink {
|
||||||
for k in theme.keys() {
|
for k in theme.keys() {
|
||||||
ret.extend(
|
ret.push_str(&format!(
|
||||||
format!(
|
"\"{}\" = {{ fg = {}, bg = {}, attrs = {} }}\n",
|
||||||
"\"{}\" = {{ fg = {}, bg = {}, attrs = {} }}\n",
|
k,
|
||||||
k,
|
toml::to_string(&unlink_fg(theme, &ColorField::Fg, k)).unwrap(),
|
||||||
toml::to_string(&unlink_fg(&theme, &ColorField::Fg, k)).unwrap(),
|
toml::to_string(&unlink_bg(theme, &ColorField::Bg, k)).unwrap(),
|
||||||
toml::to_string(&unlink_bg(&theme, &ColorField::Bg, k)).unwrap(),
|
toml::to_string(&unlink_attrs(theme, k)).unwrap(),
|
||||||
toml::to_string(&unlink_attrs(&theme, k)).unwrap(),
|
));
|
||||||
)
|
|
||||||
.chars(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for k in theme.keys() {
|
for k in theme.keys() {
|
||||||
ret.extend(
|
ret.push_str(&format!(
|
||||||
format!(
|
"\"{}\" = {{ fg = {}, bg = {}, attrs = {} }}\n",
|
||||||
"\"{}\" = {{ fg = {}, bg = {}, attrs = {} }}\n",
|
k,
|
||||||
k,
|
toml::to_string(&theme[k].fg).unwrap(),
|
||||||
toml::to_string(&theme[k].fg).unwrap(),
|
toml::to_string(&theme[k].bg).unwrap(),
|
||||||
toml::to_string(&theme[k].bg).unwrap(),
|
toml::to_string(&theme[k].attrs).unwrap(),
|
||||||
toml::to_string(&theme[k].attrs).unwrap(),
|
));
|
||||||
)
|
|
||||||
.chars(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
pub fn to_string(&self) -> String {
|
}
|
||||||
let mut ret = String::new();
|
|
||||||
ret.extend(self.key_to_string("dark", true).chars());
|
impl std::fmt::Display for Themes {
|
||||||
|
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
ret.push_str("\n\n");
|
let mut ret = String::new();
|
||||||
ret.extend(self.key_to_string("light", true).chars());
|
ret.push_str(&self.key_to_string("dark", true));
|
||||||
for name in self.other_themes.keys() {
|
|
||||||
ret.push_str("\n\n");
|
ret.push_str("\n\n");
|
||||||
ret.extend(self.key_to_string(name, true).chars());
|
ret.push_str(&self.key_to_string("light", true));
|
||||||
}
|
for name in self.other_themes.keys() {
|
||||||
ret
|
ret.push_str("\n\n");
|
||||||
}
|
ret.push_str(&self.key_to_string(name, true));
|
||||||
|
}
|
||||||
|
write!(fmt, "{}", ret)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Themes {
|
impl Default for Themes {
|
||||||
|
#[allow(clippy::needless_update)]
|
||||||
fn default() -> Themes {
|
fn default() -> Themes {
|
||||||
let mut light = IndexMap::default();
|
let mut light = IndexMap::default();
|
||||||
let mut dark = IndexMap::default();
|
let mut dark = IndexMap::default();
|
||||||
|
@ -1762,9 +1741,9 @@ impl Serialize for Themes {
|
||||||
new_map.insert(
|
new_map.insert(
|
||||||
k.clone(),
|
k.clone(),
|
||||||
ThemeAttribute {
|
ThemeAttribute {
|
||||||
fg: unlink_fg(&t, &ColorField::Fg, k),
|
fg: unlink_fg(t, &ColorField::Fg, k),
|
||||||
bg: unlink_bg(&t, &ColorField::Bg, k),
|
bg: unlink_bg(t, &ColorField::Bg, k),
|
||||||
attrs: unlink_attrs(&t, k),
|
attrs: unlink_attrs(t, k),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1790,10 +1769,10 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
|
||||||
}
|
}
|
||||||
fn is_cyclic_util<'a>(
|
fn is_cyclic_util<'a>(
|
||||||
course: Course,
|
course: Course,
|
||||||
k: &'a Cow<'static, str>,
|
k: &'a str,
|
||||||
visited: &mut IndexMap<(&'a Cow<'static, str>, Course), bool>,
|
visited: &mut IndexMap<(&'a str, Course), bool>,
|
||||||
stack: &mut IndexMap<(&'a Cow<'static, str>, Course), bool>,
|
stack: &mut IndexMap<(&'a str, Course), bool>,
|
||||||
path: &mut SmallVec<[(&'a Cow<'static, str>, Course); 16]>,
|
path: &mut SmallVec<[(&'a str, Course); 16]>,
|
||||||
theme: &'a Theme,
|
theme: &'a Theme,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if !visited[&(k, course)] {
|
if !visited[&(k, course)] {
|
||||||
|
@ -1804,6 +1783,7 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
|
||||||
Course::Fg => match theme[k].fg {
|
Course::Fg => match theme[k].fg {
|
||||||
ThemeValue::Link(ref l, ColorField::LikeSelf)
|
ThemeValue::Link(ref l, ColorField::LikeSelf)
|
||||||
| ThemeValue::Link(ref l, ColorField::Fg) => {
|
| ThemeValue::Link(ref l, ColorField::Fg) => {
|
||||||
|
let l = l.as_ref();
|
||||||
path.push((l, Course::Fg));
|
path.push((l, Course::Fg));
|
||||||
if (!visited[&(l, Course::Fg)]
|
if (!visited[&(l, Course::Fg)]
|
||||||
&& is_cyclic_util(course, l, visited, stack, path, theme))
|
&& is_cyclic_util(course, l, visited, stack, path, theme))
|
||||||
|
@ -1814,6 +1794,7 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
|
||||||
path.pop();
|
path.pop();
|
||||||
}
|
}
|
||||||
ThemeValue::Link(ref l, ColorField::Bg) => {
|
ThemeValue::Link(ref l, ColorField::Bg) => {
|
||||||
|
let l = l.as_ref();
|
||||||
path.push((l, Course::Bg));
|
path.push((l, Course::Bg));
|
||||||
if (!visited[&(l, Course::Bg)]
|
if (!visited[&(l, Course::Bg)]
|
||||||
&& is_cyclic_util(Course::Bg, l, visited, stack, path, theme))
|
&& is_cyclic_util(Course::Bg, l, visited, stack, path, theme))
|
||||||
|
@ -1824,6 +1805,7 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
|
||||||
path.pop();
|
path.pop();
|
||||||
}
|
}
|
||||||
ThemeValue::Alias(ref ident) => {
|
ThemeValue::Alias(ref ident) => {
|
||||||
|
let ident = ident.as_ref();
|
||||||
path.push((ident, Course::ColorAliasFg));
|
path.push((ident, Course::ColorAliasFg));
|
||||||
if (!visited[&(ident, Course::ColorAliasFg)]
|
if (!visited[&(ident, Course::ColorAliasFg)]
|
||||||
&& is_cyclic_util(
|
&& is_cyclic_util(
|
||||||
|
@ -1845,6 +1827,7 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
|
||||||
Course::Bg => match theme[k].bg {
|
Course::Bg => match theme[k].bg {
|
||||||
ThemeValue::Link(ref l, ColorField::LikeSelf)
|
ThemeValue::Link(ref l, ColorField::LikeSelf)
|
||||||
| ThemeValue::Link(ref l, ColorField::Bg) => {
|
| ThemeValue::Link(ref l, ColorField::Bg) => {
|
||||||
|
let l = l.as_ref();
|
||||||
path.push((l, Course::Bg));
|
path.push((l, Course::Bg));
|
||||||
if (!visited[&(l, Course::Bg)]
|
if (!visited[&(l, Course::Bg)]
|
||||||
&& is_cyclic_util(Course::Bg, l, visited, stack, path, theme))
|
&& is_cyclic_util(Course::Bg, l, visited, stack, path, theme))
|
||||||
|
@ -1855,6 +1838,7 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
|
||||||
path.pop();
|
path.pop();
|
||||||
}
|
}
|
||||||
ThemeValue::Link(ref l, ColorField::Fg) => {
|
ThemeValue::Link(ref l, ColorField::Fg) => {
|
||||||
|
let l = l.as_ref();
|
||||||
path.push((l, Course::Fg));
|
path.push((l, Course::Fg));
|
||||||
if (!visited[&(l, Course::Fg)]
|
if (!visited[&(l, Course::Fg)]
|
||||||
&& is_cyclic_util(Course::Fg, l, visited, stack, path, theme))
|
&& is_cyclic_util(Course::Fg, l, visited, stack, path, theme))
|
||||||
|
@ -1865,6 +1849,7 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
|
||||||
path.pop();
|
path.pop();
|
||||||
}
|
}
|
||||||
ThemeValue::Alias(ref ident) => {
|
ThemeValue::Alias(ref ident) => {
|
||||||
|
let ident = ident.as_ref();
|
||||||
path.push((ident, Course::ColorAliasBg));
|
path.push((ident, Course::ColorAliasBg));
|
||||||
if (!visited[&(ident, Course::ColorAliasBg)]
|
if (!visited[&(ident, Course::ColorAliasBg)]
|
||||||
&& is_cyclic_util(
|
&& is_cyclic_util(
|
||||||
|
@ -1885,6 +1870,7 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
|
||||||
},
|
},
|
||||||
Course::Attrs => match theme[k].attrs {
|
Course::Attrs => match theme[k].attrs {
|
||||||
ThemeValue::Link(ref l, _) => {
|
ThemeValue::Link(ref l, _) => {
|
||||||
|
let l = l.as_ref();
|
||||||
path.push((l, course));
|
path.push((l, course));
|
||||||
if (!visited[&(l, course)]
|
if (!visited[&(l, course)]
|
||||||
&& is_cyclic_util(course, l, visited, stack, path, theme))
|
&& is_cyclic_util(course, l, visited, stack, path, theme))
|
||||||
|
@ -1895,6 +1881,7 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
|
||||||
path.pop();
|
path.pop();
|
||||||
}
|
}
|
||||||
ThemeValue::Alias(ref ident) => {
|
ThemeValue::Alias(ref ident) => {
|
||||||
|
let ident = ident.as_ref();
|
||||||
path.push((ident, Course::AttrAlias));
|
path.push((ident, Course::AttrAlias));
|
||||||
if (!visited[&(ident, Course::AttrAlias)]
|
if (!visited[&(ident, Course::AttrAlias)]
|
||||||
&& is_cyclic_util(
|
&& is_cyclic_util(
|
||||||
|
@ -1915,6 +1902,7 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
|
||||||
},
|
},
|
||||||
Course::ColorAliasFg | Course::ColorAliasBg => match &theme.color_aliases[k] {
|
Course::ColorAliasFg | Course::ColorAliasBg => match &theme.color_aliases[k] {
|
||||||
ThemeValue::Link(ref l, ref field) => {
|
ThemeValue::Link(ref l, ref field) => {
|
||||||
|
let l = l.as_ref();
|
||||||
let course = match (course, field) {
|
let course = match (course, field) {
|
||||||
(Course::ColorAliasFg, ColorField::LikeSelf) => Course::Fg,
|
(Course::ColorAliasFg, ColorField::LikeSelf) => Course::Fg,
|
||||||
(Course::ColorAliasBg, ColorField::LikeSelf) => Course::Bg,
|
(Course::ColorAliasBg, ColorField::LikeSelf) => Course::Bg,
|
||||||
|
@ -1934,6 +1922,7 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
|
||||||
path.pop();
|
path.pop();
|
||||||
}
|
}
|
||||||
ThemeValue::Alias(ref ident) => {
|
ThemeValue::Alias(ref ident) => {
|
||||||
|
let ident = ident.as_ref();
|
||||||
path.push((ident, course));
|
path.push((ident, course));
|
||||||
if (!visited[&(ident, course)]
|
if (!visited[&(ident, course)]
|
||||||
&& is_cyclic_util(course, ident, visited, stack, path, theme))
|
&& is_cyclic_util(course, ident, visited, stack, path, theme))
|
||||||
|
@ -1947,6 +1936,7 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
|
||||||
},
|
},
|
||||||
Course::AttrAlias => match &theme.attr_aliases[k] {
|
Course::AttrAlias => match &theme.attr_aliases[k] {
|
||||||
ThemeValue::Link(ref l, ()) => {
|
ThemeValue::Link(ref l, ()) => {
|
||||||
|
let l = l.as_ref();
|
||||||
path.push((l, Course::Attrs));
|
path.push((l, Course::Attrs));
|
||||||
if (!visited[&(l, Course::Attrs)]
|
if (!visited[&(l, Course::Attrs)]
|
||||||
&& is_cyclic_util(Course::Attrs, l, visited, stack, path, theme))
|
&& is_cyclic_util(Course::Attrs, l, visited, stack, path, theme))
|
||||||
|
@ -1957,6 +1947,7 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
|
||||||
path.pop();
|
path.pop();
|
||||||
}
|
}
|
||||||
ThemeValue::Alias(ref ident) => {
|
ThemeValue::Alias(ref ident) => {
|
||||||
|
let ident = ident.as_ref();
|
||||||
path.push((ident, course));
|
path.push((ident, course));
|
||||||
if (!visited[&(ident, course)]
|
if (!visited[&(ident, course)]
|
||||||
&& is_cyclic_util(course, ident, visited, stack, path, theme))
|
&& is_cyclic_util(course, ident, visited, stack, path, theme))
|
||||||
|
@ -1977,35 +1968,28 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
|
||||||
let mut path = SmallVec::new();
|
let mut path = SmallVec::new();
|
||||||
let mut visited = theme
|
let mut visited = theme
|
||||||
.keys()
|
.keys()
|
||||||
.map(|k| {
|
.flat_map(|k| {
|
||||||
std::iter::once(((k, Course::Fg), false))
|
std::iter::once(((k.as_ref(), Course::Fg), false))
|
||||||
.chain(std::iter::once(((k, Course::Bg), false)))
|
.chain(std::iter::once(((k.as_ref(), Course::Bg), false)))
|
||||||
.chain(std::iter::once(((k, Course::Attrs), false)))
|
.chain(std::iter::once(((k.as_ref(), Course::Attrs), false)))
|
||||||
})
|
})
|
||||||
.flatten()
|
.chain(theme.color_aliases.keys().flat_map(|k| {
|
||||||
.chain(
|
std::iter::once(((k.as_ref(), Course::ColorAliasFg), false))
|
||||||
theme
|
.chain(std::iter::once(((k.as_ref(), Course::ColorAliasBg), false)))
|
||||||
.color_aliases
|
}))
|
||||||
.keys()
|
|
||||||
.map(|k| {
|
|
||||||
std::iter::once(((k, Course::ColorAliasFg), false))
|
|
||||||
.chain(std::iter::once(((k, Course::ColorAliasBg), false)))
|
|
||||||
})
|
|
||||||
.flatten(),
|
|
||||||
)
|
|
||||||
.chain(
|
.chain(
|
||||||
theme
|
theme
|
||||||
.attr_aliases
|
.attr_aliases
|
||||||
.keys()
|
.keys()
|
||||||
.map(|k| ((k, Course::AttrAlias), false)),
|
.map(|k| ((k.as_ref(), Course::AttrAlias), false)),
|
||||||
)
|
)
|
||||||
.collect::<IndexMap<(&Cow<'static, str>, Course), bool>>();
|
.collect::<IndexMap<(&str, Course), bool>>();
|
||||||
|
|
||||||
let mut stack = visited.clone();
|
let mut stack = visited.clone();
|
||||||
for k in theme.keys() {
|
for k in theme.keys() {
|
||||||
for &course in [Course::Fg, Course::Bg, Course::Attrs].iter() {
|
for &course in [Course::Fg, Course::Bg, Course::Attrs].iter() {
|
||||||
path.push((k, course));
|
path.push((k.as_ref(), course));
|
||||||
if is_cyclic_util(course, k, &mut visited, &mut stack, &mut path, &theme) {
|
if is_cyclic_util(course, k, &mut visited, &mut stack, &mut path, theme) {
|
||||||
let path = path
|
let path = path
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(k, c)| match c {
|
.map(|(k, c)| match c {
|
||||||
|
@ -2037,7 +2021,7 @@ fn is_cyclic(theme: &Theme) -> std::result::Result<(), String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(());
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -96,6 +96,7 @@ impl MailcapEntry {
|
||||||
//let flags = parts_iter.next().unwrap();
|
//let flags = parts_iter.next().unwrap();
|
||||||
if key.starts_with(&content_type) || key.matches_glob(&content_type) {
|
if key.starts_with(&content_type) || key.matches_glob(&content_type) {
|
||||||
let mut copiousoutput = false;
|
let mut copiousoutput = false;
|
||||||
|
#[allow(clippy::while_let_on_iterator)]
|
||||||
while let Some(flag) = parts_iter.next() {
|
while let Some(flag) = parts_iter.next() {
|
||||||
if flag.trim() == "copiousoutput" {
|
if flag.trim() == "copiousoutput" {
|
||||||
copiousoutput = true;
|
copiousoutput = true;
|
||||||
|
@ -118,6 +119,7 @@ impl MailcapEntry {
|
||||||
//let flags = parts_iter.next().unwrap();
|
//let flags = parts_iter.next().unwrap();
|
||||||
if key.starts_with(&content_type) || key.matches_glob(&content_type) {
|
if key.starts_with(&content_type) || key.matches_glob(&content_type) {
|
||||||
let mut copiousoutput = false;
|
let mut copiousoutput = false;
|
||||||
|
#[allow(clippy::while_let_on_iterator)]
|
||||||
while let Some(flag) = parts_iter.next() {
|
while let Some(flag) = parts_iter.next() {
|
||||||
if flag.trim() == "copiousoutput" {
|
if flag.trim() == "copiousoutput" {
|
||||||
copiousoutput = true;
|
copiousoutput = true;
|
||||||
|
@ -178,7 +180,7 @@ impl MailcapEntry {
|
||||||
.collect::<Vec<String>>();
|
.collect::<Vec<String>>();
|
||||||
let cmd_string = format!("{} {}", cmd, args.join(" "));
|
let cmd_string = format!("{} {}", cmd, args.join(" "));
|
||||||
melib::log(
|
melib::log(
|
||||||
format!("Executing: sh -c \"{}\"", cmd_string.replace("\"", "\\\"")),
|
format!("Executing: sh -c \"{}\"", cmd_string.replace('"', "\\\"")),
|
||||||
melib::DEBUG,
|
melib::DEBUG,
|
||||||
);
|
);
|
||||||
if copiousoutput {
|
if copiousoutput {
|
||||||
|
@ -212,25 +214,23 @@ impl MailcapEntry {
|
||||||
.spawn()?;
|
.spawn()?;
|
||||||
pager.stdin.as_mut().unwrap().write_all(&out)?;
|
pager.stdin.as_mut().unwrap().write_all(&out)?;
|
||||||
debug!(pager.wait_with_output()?.stdout);
|
debug!(pager.wait_with_output()?.stdout);
|
||||||
|
} else if needs_stdin {
|
||||||
|
let mut child = Command::new("sh")
|
||||||
|
.args(&["-c", &cmd_string])
|
||||||
|
.stdin(Stdio::piped())
|
||||||
|
.stdout(Stdio::inherit())
|
||||||
|
.spawn()?;
|
||||||
|
|
||||||
|
child.stdin.as_mut().unwrap().write_all(&decode(a, None))?;
|
||||||
|
debug!(child.wait_with_output()?.stdout);
|
||||||
} else {
|
} else {
|
||||||
if needs_stdin {
|
let child = Command::new("sh")
|
||||||
let mut child = Command::new("sh")
|
.args(&["-c", &cmd_string])
|
||||||
.args(&["-c", &cmd_string])
|
.stdin(Stdio::inherit())
|
||||||
.stdin(Stdio::piped())
|
.stdout(Stdio::inherit())
|
||||||
.stdout(Stdio::inherit())
|
.spawn()?;
|
||||||
.spawn()?;
|
|
||||||
|
|
||||||
child.stdin.as_mut().unwrap().write_all(&decode(a, None))?;
|
debug!(child.wait_with_output()?.stdout);
|
||||||
debug!(child.wait_with_output()?.stdout);
|
|
||||||
} else {
|
|
||||||
let child = Command::new("sh")
|
|
||||||
.args(&["-c", &cmd_string])
|
|
||||||
.stdin(Stdio::inherit())
|
|
||||||
.stdout(Stdio::inherit())
|
|
||||||
.spawn()?;
|
|
||||||
|
|
||||||
debug!(child.wait_with_output()?.stdout);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
context.replies.push_back(UIEvent::Fork(ForkType::Finished));
|
context.replies.push_back(UIEvent::Fork(ForkType::Finished));
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -168,14 +168,14 @@ pub async fn insert(
|
||||||
format!(
|
format!(
|
||||||
"Failed to open envelope {}: {}",
|
"Failed to open envelope {}: {}",
|
||||||
envelope.message_id_display(),
|
envelope.message_id_display(),
|
||||||
err.to_string()
|
err
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
log(
|
log(
|
||||||
format!(
|
format!(
|
||||||
"Failed to open envelope {}: {}",
|
"Failed to open envelope {}: {}",
|
||||||
envelope.message_id_display(),
|
envelope.message_id_display(),
|
||||||
err.to_string()
|
err
|
||||||
),
|
),
|
||||||
ERROR,
|
ERROR,
|
||||||
);
|
);
|
||||||
|
@ -190,13 +190,13 @@ pub async fn insert(
|
||||||
debug!(
|
debug!(
|
||||||
"Failed to insert envelope {}: {}",
|
"Failed to insert envelope {}: {}",
|
||||||
envelope.message_id_display(),
|
envelope.message_id_display(),
|
||||||
err.to_string()
|
err
|
||||||
);
|
);
|
||||||
log(
|
log(
|
||||||
format!(
|
format!(
|
||||||
"Failed to insert envelope {}: {}",
|
"Failed to insert envelope {}: {}",
|
||||||
envelope.message_id_display(),
|
envelope.message_id_display(),
|
||||||
err.to_string()
|
err
|
||||||
),
|
),
|
||||||
ERROR,
|
ERROR,
|
||||||
);
|
);
|
||||||
|
@ -217,19 +217,19 @@ pub async fn insert(
|
||||||
if let Err(err) = conn.execute(
|
if let Err(err) = conn.execute(
|
||||||
"INSERT OR REPLACE INTO envelopes (account_id, hash, date, _from, _to, cc, bcc, subject, message_id, in_reply_to, _references, flags, has_attachments, body_text, timestamp)
|
"INSERT OR REPLACE INTO envelopes (account_id, hash, date, _from, _to, cc, bcc, subject, message_id, in_reply_to, _references, flags, has_attachments, body_text, timestamp)
|
||||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15)",
|
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15)",
|
||||||
params![account_id, envelope.hash().to_be_bytes().to_vec(), envelope.date_as_str(), envelope.field_from_to_string(), envelope.field_to_to_string(), envelope.field_cc_to_string(), envelope.field_bcc_to_string(), envelope.subject().into_owned().trim_end_matches('\u{0}'), envelope.message_id_display().to_string(), envelope.in_reply_to_display().map(|f| f.to_string()).unwrap_or(String::new()), envelope.field_references_to_string(), i64::from(envelope.flags().bits()), if envelope.has_attachments() { 1 } else { 0 }, body, envelope.date().to_be_bytes().to_vec()],
|
params![account_id, envelope.hash().to_be_bytes().to_vec(), envelope.date_as_str(), envelope.field_from_to_string(), envelope.field_to_to_string(), envelope.field_cc_to_string(), envelope.field_bcc_to_string(), envelope.subject().into_owned().trim_end_matches('\u{0}'), envelope.message_id_display().to_string(), envelope.in_reply_to_display().map(|f| f.to_string()).unwrap_or_default(), envelope.field_references_to_string(), i64::from(envelope.flags().bits()), if envelope.has_attachments() { 1 } else { 0 }, body, envelope.date().to_be_bytes().to_vec()],
|
||||||
)
|
)
|
||||||
.map_err(|e| MeliError::new(e.to_string())) {
|
.map_err(|e| MeliError::new(e.to_string())) {
|
||||||
debug!(
|
debug!(
|
||||||
"Failed to insert envelope {}: {}",
|
"Failed to insert envelope {}: {}",
|
||||||
envelope.message_id_display(),
|
envelope.message_id_display(),
|
||||||
err.to_string()
|
err
|
||||||
);
|
);
|
||||||
log(
|
log(
|
||||||
format!(
|
format!(
|
||||||
"Failed to insert envelope {}: {}",
|
"Failed to insert envelope {}: {}",
|
||||||
envelope.message_id_display(),
|
envelope.message_id_display(),
|
||||||
err.to_string()
|
err
|
||||||
),
|
),
|
||||||
ERROR,
|
ERROR,
|
||||||
);
|
);
|
||||||
|
@ -253,17 +253,9 @@ pub fn remove(env_hash: EnvelopeHash) -> Result<()> {
|
||||||
)
|
)
|
||||||
.map_err(|e| MeliError::new(e.to_string()))
|
.map_err(|e| MeliError::new(e.to_string()))
|
||||||
{
|
{
|
||||||
debug!(
|
debug!("Failed to remove envelope {}: {}", env_hash, err);
|
||||||
"Failed to remove envelope {}: {}",
|
|
||||||
env_hash,
|
|
||||||
err.to_string()
|
|
||||||
);
|
|
||||||
log(
|
log(
|
||||||
format!(
|
format!("Failed to remove envelope {}: {}", env_hash, err),
|
||||||
"Failed to remove envelope {}: {}",
|
|
||||||
env_hash,
|
|
||||||
err.to_string()
|
|
||||||
),
|
|
||||||
ERROR,
|
ERROR,
|
||||||
);
|
);
|
||||||
return Err(err);
|
return Err(err);
|
||||||
|
@ -324,11 +316,11 @@ pub fn index(context: &mut crate::state::Context, account_index: usize) -> Resul
|
||||||
.await
|
.await
|
||||||
.chain_err_summary(|| format!("Failed to open envelope {}", env_hash))?;
|
.chain_err_summary(|| format!("Failed to open envelope {}", env_hash))?;
|
||||||
let envelopes_lck = acc_mutex.read().unwrap();
|
let envelopes_lck = acc_mutex.read().unwrap();
|
||||||
if let Some(e) = envelopes_lck.get(&env_hash) {
|
if let Some(e) = envelopes_lck.get(env_hash) {
|
||||||
let body = e.body_bytes(&bytes).text().replace('\0', "");
|
let body = e.body_bytes(&bytes).text().replace('\0', "");
|
||||||
conn.execute("INSERT OR REPLACE INTO envelopes (account_id, hash, date, _from, _to, cc, bcc, subject, message_id, in_reply_to, _references, flags, has_attachments, body_text, timestamp)
|
conn.execute("INSERT OR REPLACE INTO envelopes (account_id, hash, date, _from, _to, cc, bcc, subject, message_id, in_reply_to, _references, flags, has_attachments, body_text, timestamp)
|
||||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15)",
|
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15)",
|
||||||
params![account_id, e.hash().to_be_bytes().to_vec(), e.date_as_str(), e.field_from_to_string(), e.field_to_to_string(), e.field_cc_to_string(), e.field_bcc_to_string(), e.subject().into_owned().trim_end_matches('\u{0}'), e.message_id_display().to_string(), e.in_reply_to_display().map(|f| f.to_string()).unwrap_or(String::new()), e.field_references_to_string(), i64::from(e.flags().bits()), if e.has_attachments() { 1 } else { 0 }, body, e.date().to_be_bytes().to_vec()],
|
params![account_id, e.hash().to_be_bytes().to_vec(), e.date_as_str(), e.field_from_to_string(), e.field_to_to_string(), e.field_cc_to_string(), e.field_bcc_to_string(), e.subject().into_owned().trim_end_matches('\u{0}'), e.message_id_display().to_string(), e.in_reply_to_display().map(|f| f.to_string()).unwrap_or_default(), e.field_references_to_string(), i64::from(e.flags().bits()), if e.has_attachments() { 1 } else { 0 }, body, e.date().to_be_bytes().to_vec()],
|
||||||
).chain_err_summary(|| format!( "Failed to insert envelope {}", e.message_id_display()))?;
|
).chain_err_summary(|| format!( "Failed to insert envelope {}", e.message_id_display()))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -366,7 +358,7 @@ pub fn search(
|
||||||
.prepare(
|
.prepare(
|
||||||
debug!(format!(
|
debug!(format!(
|
||||||
"SELECT hash FROM envelopes WHERE {} ORDER BY {} {};",
|
"SELECT hash FROM envelopes WHERE {} ORDER BY {} {};",
|
||||||
query_to_sql(&query),
|
query_to_sql(query),
|
||||||
sort_field,
|
sort_field,
|
||||||
sort_order
|
sort_order
|
||||||
))
|
))
|
||||||
|
@ -375,7 +367,7 @@ pub fn search(
|
||||||
.map_err(|e| MeliError::new(e.to_string()))?;
|
.map_err(|e| MeliError::new(e.to_string()))?;
|
||||||
|
|
||||||
let results = stmt
|
let results = stmt
|
||||||
.query_map([], |row| Ok(row.get(0)?))
|
.query_map([], |row| row.get(0))
|
||||||
.map_err(|e| MeliError::new(e.to_string()))?
|
.map_err(|e| MeliError::new(e.to_string()))?
|
||||||
.map(|r: std::result::Result<Vec<u8>, rusqlite::Error>| {
|
.map(|r: std::result::Result<Vec<u8>, rusqlite::Error>| {
|
||||||
Ok(u64::from_be_bytes(
|
Ok(u64::from_be_bytes(
|
||||||
|
@ -424,14 +416,14 @@ pub fn query_to_sql(q: &Query) -> String {
|
||||||
s.push_str("%\" ");
|
s.push_str("%\" ");
|
||||||
}
|
}
|
||||||
And(q1, q2) => {
|
And(q1, q2) => {
|
||||||
s.push_str("(");
|
s.push('(');
|
||||||
rec(q1, s);
|
rec(q1, s);
|
||||||
s.push_str(") AND (");
|
s.push_str(") AND (");
|
||||||
rec(q2, s);
|
rec(q2, s);
|
||||||
s.push_str(") ");
|
s.push_str(") ");
|
||||||
}
|
}
|
||||||
Or(q1, q2) => {
|
Or(q1, q2) => {
|
||||||
s.push_str("(");
|
s.push('(');
|
||||||
rec(q1, s);
|
rec(q1, s);
|
||||||
s.push_str(") OR (");
|
s.push_str(") OR (");
|
||||||
rec(q2, s);
|
rec(q2, s);
|
||||||
|
@ -445,7 +437,7 @@ pub fn query_to_sql(q: &Query) -> String {
|
||||||
Flags(v) => {
|
Flags(v) => {
|
||||||
let total = v.len();
|
let total = v.len();
|
||||||
if total > 1 {
|
if total > 1 {
|
||||||
s.push_str("(");
|
s.push('(');
|
||||||
}
|
}
|
||||||
for (i, f) in v.iter().enumerate() {
|
for (i, f) in v.iter().enumerate() {
|
||||||
match f.as_str() {
|
match f.as_str() {
|
||||||
|
|
73
src/state.rs
73
src/state.rs
|
@ -134,20 +134,18 @@ impl Context {
|
||||||
} = self;
|
} = self;
|
||||||
let was_online = accounts[account_pos].is_online.is_ok();
|
let was_online = accounts[account_pos].is_online.is_ok();
|
||||||
let ret = accounts[account_pos].is_online();
|
let ret = accounts[account_pos].is_online();
|
||||||
if ret.is_ok() {
|
if ret.is_ok() && !was_online {
|
||||||
if !was_online {
|
debug!("inserting mailbox hashes:");
|
||||||
debug!("inserting mailbox hashes:");
|
for mailbox_node in accounts[account_pos].list_mailboxes() {
|
||||||
for mailbox_node in accounts[account_pos].list_mailboxes() {
|
debug!(
|
||||||
debug!(
|
"hash & mailbox: {:?} {}",
|
||||||
"hash & mailbox: {:?} {}",
|
mailbox_node.hash,
|
||||||
mailbox_node.hash,
|
accounts[account_pos][&mailbox_node.hash].name()
|
||||||
accounts[account_pos][&mailbox_node.hash].name()
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
accounts[account_pos].watch();
|
|
||||||
|
|
||||||
replies.push_back(UIEvent::AccountStatusChange(accounts[account_pos].hash()));
|
|
||||||
}
|
}
|
||||||
|
accounts[account_pos].watch();
|
||||||
|
|
||||||
|
replies.push_back(UIEvent::AccountStatusChange(accounts[account_pos].hash()));
|
||||||
}
|
}
|
||||||
if ret.is_ok() != was_online {
|
if ret.is_ok() != was_online {
|
||||||
replies.push_back(UIEvent::AccountStatusChange(accounts[account_pos].hash()));
|
replies.push_back(UIEvent::AccountStatusChange(accounts[account_pos].hash()));
|
||||||
|
@ -331,7 +329,7 @@ impl State {
|
||||||
display_messages_area: ((0, 0), (0, 0)),
|
display_messages_area: ((0, 0), (0, 0)),
|
||||||
context: Context {
|
context: Context {
|
||||||
accounts,
|
accounts,
|
||||||
settings: settings,
|
settings,
|
||||||
dirty_areas: VecDeque::with_capacity(5),
|
dirty_areas: VecDeque::with_capacity(5),
|
||||||
replies: VecDeque::with_capacity(5),
|
replies: VecDeque::with_capacity(5),
|
||||||
temp_files: Vec::new(),
|
temp_files: Vec::new(),
|
||||||
|
@ -399,10 +397,8 @@ impl State {
|
||||||
}
|
}
|
||||||
self.rcv_event(notification);
|
self.rcv_event(notification);
|
||||||
}
|
}
|
||||||
} else {
|
} else if let melib::backends::RefreshEventKind::Failure(err) = event.kind {
|
||||||
if let melib::backends::RefreshEventKind::Failure(err) = event.kind {
|
debug!(err);
|
||||||
debug!(err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -850,7 +846,7 @@ impl State {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
AccountAction(ref account_name, PrintAccountSetting(ref setting)) => {
|
AccountAction(ref account_name, PrintAccountSetting(ref setting)) => {
|
||||||
let path = setting.split(".").collect::<SmallVec<[&str; 16]>>();
|
let path = setting.split('.').collect::<SmallVec<[&str; 16]>>();
|
||||||
if let Some(pos) = self
|
if let Some(pos) = self
|
||||||
.context
|
.context
|
||||||
.accounts
|
.accounts
|
||||||
|
@ -858,13 +854,12 @@ impl State {
|
||||||
.position(|(_h, a)| a.name() == account_name)
|
.position(|(_h, a)| a.name() == account_name)
|
||||||
{
|
{
|
||||||
self.context.replies.push_back(UIEvent::StatusEvent(
|
self.context.replies.push_back(UIEvent::StatusEvent(
|
||||||
StatusEvent::UpdateStatus(format!(
|
StatusEvent::UpdateStatus(
|
||||||
"{}",
|
|
||||||
self.context.accounts[pos]
|
self.context.accounts[pos]
|
||||||
.settings
|
.settings
|
||||||
.lookup("settings", &path)
|
.lookup("settings", &path)
|
||||||
.unwrap_or_else(|err| err.to_string())
|
.unwrap_or_else(|err| err.to_string()),
|
||||||
)),
|
),
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
self.context.replies.push_back(UIEvent::Notification(
|
self.context.replies.push_back(UIEvent::Notification(
|
||||||
|
@ -872,20 +867,18 @@ impl State {
|
||||||
format!("Account {} was not found.", account_name),
|
format!("Account {} was not found.", account_name),
|
||||||
Some(NotificationType::Error(ErrorKind::None)),
|
Some(NotificationType::Error(ErrorKind::None)),
|
||||||
));
|
));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PrintSetting(ref setting) => {
|
PrintSetting(ref setting) => {
|
||||||
let path = setting.split(".").collect::<SmallVec<[&str; 16]>>();
|
let path = setting.split('.').collect::<SmallVec<[&str; 16]>>();
|
||||||
self.context
|
self.context
|
||||||
.replies
|
.replies
|
||||||
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(format!(
|
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(
|
||||||
"{}",
|
|
||||||
self.context
|
self.context
|
||||||
.settings
|
.settings
|
||||||
.lookup("settings", &path)
|
.lookup("settings", &path)
|
||||||
.unwrap_or_else(|err| err.to_string())
|
.unwrap_or_else(|err| err.to_string()),
|
||||||
))));
|
)));
|
||||||
}
|
}
|
||||||
ToggleMouse => {
|
ToggleMouse => {
|
||||||
self.screen.mouse = !self.screen.mouse;
|
self.screen.mouse = !self.screen.mouse;
|
||||||
|
@ -920,7 +913,7 @@ impl State {
|
||||||
match event {
|
match event {
|
||||||
// Command type is handled only by State.
|
// Command type is handled only by State.
|
||||||
UIEvent::Command(cmd) => {
|
UIEvent::Command(cmd) => {
|
||||||
if let Ok(action) = parse_command(&cmd.as_bytes()) {
|
if let Ok(action) = parse_command(cmd.as_bytes()) {
|
||||||
if action.needs_confirmation() {
|
if action.needs_confirmation() {
|
||||||
self.overlay.push(Box::new(UIConfirmationDialog::new(
|
self.overlay.push(Box::new(UIConfirmationDialog::new(
|
||||||
"You sure?",
|
"You sure?",
|
||||||
|
@ -932,7 +925,7 @@ impl State {
|
||||||
Box::new(if result { Some(action) } else { None }),
|
Box::new(if result { Some(action) } else { None }),
|
||||||
))
|
))
|
||||||
})),
|
})),
|
||||||
&mut self.context,
|
&self.context,
|
||||||
)));
|
)));
|
||||||
} else if let Action::ReloadConfiguration = action {
|
} else if let Action::ReloadConfiguration = action {
|
||||||
match Settings::new().and_then(|new_settings| {
|
match Settings::new().and_then(|new_settings| {
|
||||||
|
@ -952,7 +945,7 @@ impl State {
|
||||||
Ok(new_settings)
|
Ok(new_settings)
|
||||||
}) {
|
}) {
|
||||||
Ok(new_settings) => {
|
Ok(new_settings) => {
|
||||||
let old_settings = std::mem::replace(&mut self.context.settings, new_settings);
|
let old_settings = Box::new(std::mem::replace(&mut self.context.settings, new_settings));
|
||||||
self.context.replies.push_back(UIEvent::ConfigReload {
|
self.context.replies.push_back(UIEvent::ConfigReload {
|
||||||
old_settings
|
old_settings
|
||||||
});
|
});
|
||||||
|
@ -1130,11 +1123,8 @@ impl State {
|
||||||
match w {
|
match w {
|
||||||
Ok(Some(_)) => true,
|
Ok(Some(_)) => true,
|
||||||
Ok(None) => false,
|
Ok(None) => false,
|
||||||
Err(e) => {
|
Err(err) => {
|
||||||
log(
|
log(format!("Failed to wait on editor process: {}", err), ERROR);
|
||||||
format!("Failed to wait on editor process: {}", e.to_string()),
|
|
||||||
ERROR,
|
|
||||||
);
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1144,11 +1134,8 @@ impl State {
|
||||||
match w {
|
match w {
|
||||||
Ok(Some(_)) => true,
|
Ok(Some(_)) => true,
|
||||||
Ok(None) => false,
|
Ok(None) => false,
|
||||||
Err(e) => {
|
Err(err) => {
|
||||||
log(
|
log(format!("Failed to wait on child process: {}", err), ERROR);
|
||||||
format!("Failed to wait on child process: {}", e.to_string()),
|
|
||||||
ERROR,
|
|
||||||
);
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1174,7 +1161,7 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn switch_to_alternate_screen(&mut self) {
|
pub fn switch_to_alternate_screen(&mut self) {
|
||||||
self.screen.switch_to_alternate_screen(&mut self.context);
|
self.screen.switch_to_alternate_screen(&self.context);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush(&mut self) {
|
fn flush(&mut self) {
|
||||||
|
|
|
@ -151,7 +151,7 @@ impl CellBuffer {
|
||||||
pub fn resize(&mut self, newcols: usize, newrows: usize, blank: Option<Cell>) -> bool {
|
pub fn resize(&mut self, newcols: usize, newrows: usize, blank: Option<Cell>) -> bool {
|
||||||
let newlen = newcols * newrows;
|
let newlen = newcols * newrows;
|
||||||
if (self.cols, self.rows) == (newcols, newrows) || newlen >= Self::MAX_SIZE {
|
if (self.cols, self.rows) == (newcols, newrows) || newlen >= Self::MAX_SIZE {
|
||||||
return !(newlen >= Self::MAX_SIZE);
|
return newlen < Self::MAX_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
let blank = blank.unwrap_or(self.default_cell);
|
let blank = blank.unwrap_or(self.default_cell);
|
||||||
|
@ -432,10 +432,10 @@ impl CellBuffer {
|
||||||
pub fn set_tag(&mut self, tag: u64, start: (usize, usize), end: (usize, usize)) {
|
pub fn set_tag(&mut self, tag: u64, start: (usize, usize), end: (usize, usize)) {
|
||||||
let start = self
|
let start = self
|
||||||
.pos_to_index(start.0, start.1)
|
.pos_to_index(start.0, start.1)
|
||||||
.unwrap_or(self.buf.len().saturating_sub(1));
|
.unwrap_or_else(|| self.buf.len().saturating_sub(1));
|
||||||
let end = self
|
let end = self
|
||||||
.pos_to_index(end.0, end.1)
|
.pos_to_index(end.0, end.1)
|
||||||
.unwrap_or(self.buf.len().saturating_sub(1));
|
.unwrap_or_else(|| self.buf.len().saturating_sub(1));
|
||||||
if start != end {
|
if start != end {
|
||||||
self.tag_associations.push((tag, (start, end)));
|
self.tag_associations.push((tag, (start, end)));
|
||||||
}
|
}
|
||||||
|
@ -856,9 +856,9 @@ impl Attr {
|
||||||
"Blink" => Ok(Attr::BLINK),
|
"Blink" => Ok(Attr::BLINK),
|
||||||
"Reverse" => Ok(Attr::REVERSE),
|
"Reverse" => Ok(Attr::REVERSE),
|
||||||
"Hidden" => Ok(Attr::HIDDEN),
|
"Hidden" => Ok(Attr::HIDDEN),
|
||||||
combination if combination.contains("|") => {
|
combination if combination.contains('|') => {
|
||||||
let mut ret = Attr::DEFAULT;
|
let mut ret = Attr::DEFAULT;
|
||||||
for c in combination.trim().split("|") {
|
for c in combination.trim().split('|') {
|
||||||
ret |= Self::from_string_de::<'de, D, &str>(c)?;
|
ret |= Self::from_string_de::<'de, D, &str>(c)?;
|
||||||
}
|
}
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
|
@ -1725,7 +1725,7 @@ impl core::cmp::Ord for FormatTag {
|
||||||
|
|
||||||
impl core::cmp::PartialOrd for FormatTag {
|
impl core::cmp::PartialOrd for FormatTag {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
|
||||||
Some(self.cmp(&other))
|
Some(self.cmp(other))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -391,10 +391,12 @@ impl Color {
|
||||||
"Yellow5" => 106,
|
"Yellow5" => 106,
|
||||||
"Yellow6" => 148,
|
"Yellow6" => 148,
|
||||||
"Default" => return Ok(Color::Default),
|
"Default" => return Ok(Color::Default),
|
||||||
s if s.starts_with("#")
|
s if s.starts_with('#')
|
||||||
&& s.len() == 7
|
&& s.len() == 7
|
||||||
&& s[1..].as_bytes().iter().all(|&b| {
|
&& s[1..].as_bytes().iter().all(|&b| {
|
||||||
(b >= b'0' && b <= b'9') || (b >= b'a' && b <= b'f') || (b >= b'A' && b <= b'F')
|
(b'0'..=b'9').contains(&b)
|
||||||
|
|| (b'a'..=b'f').contains(&b)
|
||||||
|
|| (b'A'..=b'F').contains(&b)
|
||||||
}) =>
|
}) =>
|
||||||
{
|
{
|
||||||
return Ok(Color::Rgb(
|
return Ok(Color::Rgb(
|
||||||
|
@ -406,10 +408,12 @@ impl Color {
|
||||||
.map_err(|_| de::Error::custom("invalid `color` value"))?,
|
.map_err(|_| de::Error::custom("invalid `color` value"))?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
s if s.starts_with("#")
|
s if s.starts_with('#')
|
||||||
&& s.len() == 4
|
&& s.len() == 4
|
||||||
&& s[1..].as_bytes().iter().all(|&b| {
|
&& s[1..].as_bytes().iter().all(|&b| {
|
||||||
(b >= b'0' && b <= b'9') || (b >= b'a' && b <= b'f') || (b >= b'A' && b <= b'F')
|
(b'0'..=b'9').contains(&b)
|
||||||
|
|| (b'a'..=b'f').contains(&b)
|
||||||
|
|| (b'A'..=b'F').contains(&b)
|
||||||
}) =>
|
}) =>
|
||||||
{
|
{
|
||||||
return Ok(Color::Rgb(
|
return Ok(Color::Rgb(
|
||||||
|
@ -421,7 +425,8 @@ impl Color {
|
||||||
.map_err(|_| de::Error::custom("invalid `color` value"))?,
|
.map_err(|_| de::Error::custom("invalid `color` value"))?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
_ => u8::from_str_radix(&s, 10)
|
_ => s
|
||||||
|
.parse::<u8>()
|
||||||
.map_err(|_| de::Error::custom("invalid `color` value"))?,
|
.map_err(|_| de::Error::custom("invalid `color` value"))?,
|
||||||
};
|
};
|
||||||
Ok(Color::Byte(byte))
|
Ok(Color::Byte(byte))
|
||||||
|
|
|
@ -346,7 +346,7 @@ impl EmbedGrid {
|
||||||
*state = State::CsiQ(buf1);
|
*state = State::CsiQ(buf1);
|
||||||
}
|
}
|
||||||
/* OSC stuff */
|
/* OSC stuff */
|
||||||
(c, State::Osc1(ref mut buf)) if (c >= b'0' && c <= b'9') || c == b'?' => {
|
(c, State::Osc1(ref mut buf)) if (b'0'..=b'9').contains(&c) || c == b'?' => {
|
||||||
buf.push(c);
|
buf.push(c);
|
||||||
}
|
}
|
||||||
(b';', State::Osc1(ref mut buf1_p)) => {
|
(b';', State::Osc1(ref mut buf1_p)) => {
|
||||||
|
@ -354,7 +354,7 @@ impl EmbedGrid {
|
||||||
let buf2 = SmallVec::new();
|
let buf2 = SmallVec::new();
|
||||||
*state = State::Osc2(buf1, buf2);
|
*state = State::Osc2(buf1, buf2);
|
||||||
}
|
}
|
||||||
(c, State::Osc2(_, ref mut buf)) if (c >= b'0' && c <= b'9') || c == b'?' => {
|
(c, State::Osc2(_, ref mut buf)) if (b'0'..=b'9').contains(&c) || c == b'?' => {
|
||||||
buf.push(c);
|
buf.push(c);
|
||||||
}
|
}
|
||||||
/* Normal */
|
/* Normal */
|
||||||
|
@ -512,7 +512,7 @@ impl EmbedGrid {
|
||||||
*state = State::Normal;
|
*state = State::Normal;
|
||||||
}
|
}
|
||||||
/* CSI ? stuff */
|
/* CSI ? stuff */
|
||||||
(c, State::CsiQ(ref mut buf)) if c >= b'0' && c <= b'9' => {
|
(c, State::CsiQ(ref mut buf)) if (b'0'..=b'9').contains(&c) => {
|
||||||
buf.push(c);
|
buf.push(c);
|
||||||
}
|
}
|
||||||
(b'h', State::CsiQ(ref buf)) => {
|
(b'h', State::CsiQ(ref buf)) => {
|
||||||
|
@ -569,7 +569,7 @@ impl EmbedGrid {
|
||||||
*state = State::Normal;
|
*state = State::Normal;
|
||||||
}
|
}
|
||||||
/* END OF CSI ? stuff */
|
/* END OF CSI ? stuff */
|
||||||
(c, State::Csi) if c >= b'0' && c <= b'9' => {
|
(c, State::Csi) if (b'0'..=b'9').contains(&c) => {
|
||||||
let mut buf1 = SmallVec::new();
|
let mut buf1 = SmallVec::new();
|
||||||
buf1.push(c);
|
buf1.push(c);
|
||||||
*state = State::Csi1(buf1);
|
*state = State::Csi1(buf1);
|
||||||
|
@ -973,17 +973,9 @@ impl EmbedGrid {
|
||||||
*bg_color = Color::Default;
|
*bg_color = Color::Default;
|
||||||
}
|
}
|
||||||
b"1" => { /* bold */ }
|
b"1" => { /* bold */ }
|
||||||
b"7" => {
|
b"7" | b"27" => {
|
||||||
/* Inverse */
|
/* Inverse on/off */
|
||||||
let temp = *fg_color;
|
std::mem::swap(&mut (*fg_color), &mut (*bg_color))
|
||||||
*fg_color = *bg_color;
|
|
||||||
*bg_color = temp;
|
|
||||||
}
|
|
||||||
b"27" => {
|
|
||||||
/* Inverse off */
|
|
||||||
let temp = *fg_color;
|
|
||||||
*fg_color = *bg_color;
|
|
||||||
*bg_color = temp;
|
|
||||||
}
|
}
|
||||||
b"30" => *fg_color = Color::Black,
|
b"30" => *fg_color = Color::Black,
|
||||||
b"31" => *fg_color = Color::Red,
|
b"31" => *fg_color = Color::Red,
|
||||||
|
@ -1037,17 +1029,9 @@ impl EmbedGrid {
|
||||||
*bg_color = Color::Default;
|
*bg_color = Color::Default;
|
||||||
}
|
}
|
||||||
b"1" => { /* bold */ }
|
b"1" => { /* bold */ }
|
||||||
b"7" => {
|
b"7" | b"27" => {
|
||||||
/* Inverse */
|
/* Inverse on/off */
|
||||||
let temp = *fg_color;
|
std::mem::swap(&mut (*fg_color), &mut (*bg_color))
|
||||||
*fg_color = *bg_color;
|
|
||||||
*bg_color = temp;
|
|
||||||
}
|
|
||||||
b"27" => {
|
|
||||||
/* Inverse off */
|
|
||||||
let temp = *fg_color;
|
|
||||||
*fg_color = *bg_color;
|
|
||||||
*bg_color = temp;
|
|
||||||
}
|
}
|
||||||
b"30" => *fg_color = Color::Black,
|
b"30" => *fg_color = Color::Black,
|
||||||
b"31" => *fg_color = Color::Red,
|
b"31" => *fg_color = Color::Red,
|
||||||
|
@ -1094,7 +1078,7 @@ impl EmbedGrid {
|
||||||
grid[cursor_val!()].set_bg(*bg_color);
|
grid[cursor_val!()].set_bg(*bg_color);
|
||||||
*state = State::Normal;
|
*state = State::Normal;
|
||||||
}
|
}
|
||||||
(c, State::Csi1(ref mut buf)) if (c >= b'0' && c <= b'9') || c == b' ' => {
|
(c, State::Csi1(ref mut buf)) if (b'0'..=b'9').contains(&c) || c == b' ' => {
|
||||||
buf.push(c);
|
buf.push(c);
|
||||||
}
|
}
|
||||||
(b';', State::Csi2(ref mut buf1_p, ref mut buf2_p)) => {
|
(b';', State::Csi2(ref mut buf1_p, ref mut buf2_p)) => {
|
||||||
|
@ -1144,7 +1128,7 @@ impl EmbedGrid {
|
||||||
//debug!("cursor became: {:?}", cursor);
|
//debug!("cursor became: {:?}", cursor);
|
||||||
*state = State::Normal;
|
*state = State::Normal;
|
||||||
}
|
}
|
||||||
(c, State::Csi2(_, ref mut buf)) if c >= b'0' && c <= b'9' => {
|
(c, State::Csi2(_, ref mut buf)) if (b'0'..=b'9').contains(&c) => {
|
||||||
buf.push(c);
|
buf.push(c);
|
||||||
}
|
}
|
||||||
(b'r', State::Csi2(_, _)) | (b'r', State::Csi) => {
|
(b'r', State::Csi2(_, _)) | (b'r', State::Csi) => {
|
||||||
|
@ -1177,7 +1161,7 @@ impl EmbedGrid {
|
||||||
*state = State::Normal;
|
*state = State::Normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
(c, State::Csi3(_, _, ref mut buf)) if c >= b'0' && c <= b'9' => {
|
(c, State::Csi3(_, _, ref mut buf)) if (b'0'..=b'9').contains(&c) => {
|
||||||
buf.push(c);
|
buf.push(c);
|
||||||
}
|
}
|
||||||
(b'm', State::Csi3(ref buf1, ref buf2, ref buf3))
|
(b'm', State::Csi3(ref buf1, ref buf2, ref buf3))
|
||||||
|
@ -1185,7 +1169,7 @@ impl EmbedGrid {
|
||||||
{
|
{
|
||||||
/* Set character attributes | foreground color */
|
/* Set character attributes | foreground color */
|
||||||
*fg_color = if let Ok(byte) =
|
*fg_color = if let Ok(byte) =
|
||||||
u8::from_str_radix(unsafe { std::str::from_utf8_unchecked(buf3) }, 10)
|
unsafe { std::str::from_utf8_unchecked(buf3) }.parse::<u8>()
|
||||||
{
|
{
|
||||||
//debug!("parsed buf as {}", byte);
|
//debug!("parsed buf as {}", byte);
|
||||||
Color::Byte(byte)
|
Color::Byte(byte)
|
||||||
|
@ -1200,7 +1184,7 @@ impl EmbedGrid {
|
||||||
{
|
{
|
||||||
/* Set character attributes | background color */
|
/* Set character attributes | background color */
|
||||||
*bg_color = if let Ok(byte) =
|
*bg_color = if let Ok(byte) =
|
||||||
u8::from_str_radix(unsafe { std::str::from_utf8_unchecked(buf3) }, 10)
|
unsafe { std::str::from_utf8_unchecked(buf3) }.parse::<u8>()
|
||||||
{
|
{
|
||||||
//debug!("parsed buf as {}", byte);
|
//debug!("parsed buf as {}", byte);
|
||||||
Color::Byte(byte)
|
Color::Byte(byte)
|
||||||
|
@ -1246,3 +1230,9 @@ impl EmbedGrid {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for EmbedGrid {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -182,7 +182,7 @@ pub fn get_events(
|
||||||
select! {
|
select! {
|
||||||
default => {
|
default => {
|
||||||
if stdin_fd.revents().is_some() {
|
if stdin_fd.revents().is_some() {
|
||||||
'stdin_while: while let Some(c) = stdin_iter.next(){
|
'stdin_while: for c in stdin_iter.by_ref() {
|
||||||
match (c, &mut input_mode) {
|
match (c, &mut input_mode) {
|
||||||
(Ok((TermionEvent::Key(k), bytes)), InputMode::Normal) => {
|
(Ok((TermionEvent::Key(k), bytes)), InputMode::Normal) => {
|
||||||
closure((Key::from(k), bytes));
|
closure((Key::from(k), bytes));
|
||||||
|
@ -192,7 +192,7 @@ pub fn get_events(
|
||||||
Ok((TermionEvent::Key(TermionKey::Char(k)), ref mut bytes)), InputMode::Paste(ref mut buf),
|
Ok((TermionEvent::Key(TermionKey::Char(k)), ref mut bytes)), InputMode::Paste(ref mut buf),
|
||||||
) => {
|
) => {
|
||||||
paste_buf.push(k);
|
paste_buf.push(k);
|
||||||
let bytes = std::mem::replace(bytes, Vec::new());
|
let bytes = std::mem::take(bytes);
|
||||||
buf.extend(bytes.into_iter());
|
buf.extend(bytes.into_iter());
|
||||||
continue 'stdin_while;
|
continue 'stdin_while;
|
||||||
}
|
}
|
||||||
|
@ -203,7 +203,7 @@ pub fn get_events(
|
||||||
(Ok((TermionEvent::Unsupported(ref k), _)), InputMode::Paste(ref mut buf))
|
(Ok((TermionEvent::Unsupported(ref k), _)), InputMode::Paste(ref mut buf))
|
||||||
if k.as_slice() == BRACKET_PASTE_END =>
|
if k.as_slice() == BRACKET_PASTE_END =>
|
||||||
{
|
{
|
||||||
let buf = std::mem::replace(buf, Vec::new());
|
let buf = std::mem::take(buf);
|
||||||
input_mode = InputMode::Normal;
|
input_mode = InputMode::Normal;
|
||||||
let ret = Key::from(&paste_buf);
|
let ret = Key::from(&paste_buf);
|
||||||
paste_buf.clear();
|
paste_buf.clear();
|
||||||
|
@ -277,12 +277,12 @@ impl<'de> Deserialize<'de> for Key {
|
||||||
"Enter" | "enter" => Ok(Key::Char('\n')),
|
"Enter" | "enter" => Ok(Key::Char('\n')),
|
||||||
"Tab" | "tab" => Ok(Key::Char('\t')),
|
"Tab" | "tab" => Ok(Key::Char('\t')),
|
||||||
"Esc" | "esc" => Ok(Key::Esc),
|
"Esc" | "esc" => Ok(Key::Esc),
|
||||||
ref s if s.len() == 1 => Ok(Key::Char(s.chars().nth(0).unwrap())),
|
s if s.len() == 1 => Ok(Key::Char(s.chars().next().unwrap())),
|
||||||
ref s if s.starts_with("F") && (s.len() == 2 || s.len() == 3) => {
|
s if s.starts_with('F') && (s.len() == 2 || s.len() == 3) => {
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
if let Ok(n) = u8::from_str(&s[1..]) {
|
if let Ok(n) = u8::from_str(&s[1..]) {
|
||||||
if n >= 1 && n <= 12 {
|
if (1..=12).contains(&n) {
|
||||||
return Ok(Key::F(n));
|
return Ok(Key::F(n));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -291,7 +291,7 @@ impl<'de> Deserialize<'de> for Key {
|
||||||
&s[1..]
|
&s[1..]
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
ref s if s.starts_with("M-") && s.len() == 3 => {
|
s if s.starts_with("M-") && s.len() == 3 => {
|
||||||
let c = s.as_bytes()[2] as char;
|
let c = s.as_bytes()[2] as char;
|
||||||
|
|
||||||
if c.is_lowercase() || c.is_numeric() {
|
if c.is_lowercase() || c.is_numeric() {
|
||||||
|
@ -303,7 +303,7 @@ impl<'de> Deserialize<'de> for Key {
|
||||||
&s[2..]
|
&s[2..]
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
ref s if s.starts_with("C-") && s.len() == 3 => {
|
s if s.starts_with("C-") && s.len() == 3 => {
|
||||||
let c = s.as_bytes()[2] as char;
|
let c = s.as_bytes()[2] as char;
|
||||||
|
|
||||||
if c.is_lowercase() || c.is_numeric() {
|
if c.is_lowercase() || c.is_numeric() {
|
||||||
|
|
|
@ -149,12 +149,12 @@ pub enum UIEvent {
|
||||||
GlobalUIDialog(Box<dyn Component>),
|
GlobalUIDialog(Box<dyn Component>),
|
||||||
Timer(Uuid),
|
Timer(Uuid),
|
||||||
ConfigReload {
|
ConfigReload {
|
||||||
old_settings: crate::conf::Settings,
|
old_settings: Box<crate::conf::Settings>,
|
||||||
},
|
},
|
||||||
VisibilityChange(bool),
|
VisibilityChange(bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CallbackFn(pub Box<dyn FnOnce(&mut crate::Context) -> () + Send + 'static>);
|
pub struct CallbackFn(pub Box<dyn FnOnce(&mut crate::Context) + Send + 'static>);
|
||||||
|
|
||||||
impl core::fmt::Debug for CallbackFn {
|
impl core::fmt::Debug for CallbackFn {
|
||||||
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
|
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||||
|
@ -322,8 +322,6 @@ pub struct RateLimit {
|
||||||
last_tick: std::time::Instant,
|
last_tick: std::time::Instant,
|
||||||
pub timer: crate::jobs::Timer,
|
pub timer: crate::jobs::Timer,
|
||||||
rate: std::time::Duration,
|
rate: std::time::Duration,
|
||||||
reqs: u64,
|
|
||||||
millis: std::time::Duration,
|
|
||||||
pub active: bool,
|
pub active: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,8 +334,6 @@ impl RateLimit {
|
||||||
std::time::Duration::from_millis(millis),
|
std::time::Duration::from_millis(millis),
|
||||||
),
|
),
|
||||||
rate: std::time::Duration::from_millis(millis / reqs),
|
rate: std::time::Duration::from_millis(millis / reqs),
|
||||||
reqs,
|
|
||||||
millis: std::time::Duration::from_millis(millis),
|
|
||||||
active: false,
|
active: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ pub struct File {
|
||||||
impl Drop for File {
|
impl Drop for File {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if self.delete_on_drop {
|
if self.delete_on_drop {
|
||||||
std::fs::remove_file(self.path()).unwrap_or_else(|_| {});
|
let _ = std::fs::remove_file(self.path());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,7 @@ impl File {
|
||||||
pub fn path(&self) -> &PathBuf {
|
pub fn path(&self) -> &PathBuf {
|
||||||
&self.path
|
&self.path
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_to_string(&self) -> String {
|
pub fn read_to_string(&self) -> String {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
let mut f = fs::File::open(&self.path)
|
let mut f = fs::File::open(&self.path)
|
||||||
|
|
Loading…
Reference in New Issue