diff --git a/melib/src/addressbook.rs b/melib/src/addressbook.rs index 420719d8..3db7f1ed 100644 --- a/melib/src/addressbook.rs +++ b/melib/src/addressbook.rs @@ -19,8 +19,8 @@ * along with meli. If not, see . */ use chrono::{DateTime, Local}; -use uuid::Uuid; use fnv::FnvHashMap; +use uuid::Uuid; use std::ops::Deref; @@ -31,7 +31,7 @@ pub struct AddressBook { display_name: String, created: DateTime, last_edited: DateTime, - cards: FnvHashMap + cards: FnvHashMap, } #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] @@ -44,7 +44,6 @@ pub struct Card { name_prefix: String, name_suffix: String, //address - birthday: Option>, email: String, url: String, @@ -74,7 +73,11 @@ impl AddressBook { self.cards.contains_key(&card_id) } pub fn search(&self, term: &str) -> Vec { - self.cards.values().filter(|c| c.email.contains(term)).map(|c| c.email.clone()).collect() + self.cards + .values() + .filter(|c| c.email.contains(term)) + .map(|c| c.email.clone()) + .collect() } } @@ -86,7 +89,6 @@ impl Deref for AddressBook { } } - impl Card { pub fn new() -> Card { Card { @@ -98,7 +100,6 @@ impl Card { name_prefix: String::new(), name_suffix: String::new(), //address - birthday: None, email: String::new(), url: String::new(), @@ -182,7 +183,6 @@ impl Card { pub fn extra_property(&self, key: &str) -> Option<&str> { self.extra_properties.get(key).map(|v| v.as_str()) } - } impl From> for Card { diff --git a/melib/src/lib.rs b/melib/src/lib.rs index 7ba22d0c..7a983a17 100644 --- a/melib/src/lib.rs +++ b/melib/src/lib.rs @@ -18,11 +18,11 @@ * You should have received a copy of the GNU General Public License * along with meli. If not, see . */ +pub mod addressbook; pub mod async; pub mod conf; pub mod error; pub mod mailbox; -pub mod addressbook; #[macro_use] extern crate serde_derive; @@ -38,8 +38,8 @@ extern crate chan; #[macro_use] extern crate bitflags; -extern crate uuid; extern crate fnv; +extern crate uuid; pub use conf::*; pub use mailbox::*; diff --git a/melib/src/mailbox/backends/maildir/backend.rs b/melib/src/mailbox/backends/maildir/backend.rs index afb5d06f..6b7da6bd 100644 --- a/melib/src/mailbox/backends/maildir/backend.rs +++ b/melib/src/mailbox/backends/maildir/backend.rs @@ -27,7 +27,7 @@ extern crate xdg; use super::{MaildirFolder, MaildirOp}; use async::*; use conf::AccountSettings; -use error::{Result, MeliError}; +use error::{MeliError, Result}; use mailbox::backends::{ BackendFolder, BackendOp, Folder, FolderHash, MailBackend, RefreshEvent, RefreshEventConsumer, RefreshEventKind::*, @@ -132,16 +132,16 @@ fn get_file_hash(file: &Path) -> EnvelopeHash { } fn move_to_cur(p: PathBuf) -> PathBuf { - let mut new = p.clone(); - { - let file_name = p.file_name().unwrap(); - new.pop(); - new.pop(); + let mut new = p.clone(); + { + let file_name = p.file_name().unwrap(); + new.pop(); + new.pop(); - new.push("cur"); - new.push(file_name); - new.set_extension(":2,"); - } + new.push("cur"); + new.push(file_name); + new.set_extension(":2,"); + } eprintln!("moved to cur: {}", new.display()); fs::rename(p, &new).unwrap(); new @@ -347,7 +347,10 @@ impl MailBackend for MaildirType { } } - Err(MeliError::new(format!("'{}' is not a valid folder.", folder))) + Err(MeliError::new(format!( + "'{}' is not a valid folder.", + folder + ))) } } @@ -447,13 +450,13 @@ impl MaildirType { let thunk = move || { let mut path = path.clone(); let cache_dir = cache_dir.clone(); - path.push("new"); - for d in path.read_dir()? { - if let Ok(p) = d { - move_to_cur(p.path()); - } + path.push("new"); + for d in path.read_dir()? { + if let Ok(p) = d { + move_to_cur(p.path()); } - path.pop(); + } + path.pop(); path.push("cur"); let iter = path.read_dir()?; diff --git a/melib/src/mailbox/collection.rs b/melib/src/mailbox/collection.rs index 7b73e917..cfee4e87 100644 --- a/melib/src/mailbox/collection.rs +++ b/melib/src/mailbox/collection.rs @@ -56,24 +56,24 @@ impl Collection { let threads = Threads::new(&mut envelopes); /*let cache_dir = - xdg::BaseDirectories::with_profile("meli", format!("{}_Thread", folder.hash())) - .unwrap(); - if let Some(cached) = cache_dir.find_cache_file("threads") { - let reader = io::BufReader::new(fs::File::open(cached).unwrap()); - let result: result::Result = bincode::deserialize_from(reader); - let ret = if let Ok(mut cached_t) = result { -use std::iter::FromIterator; - eprintln!("loaded cache, our hash set is {:?}\n and the cached one is {:?}", FnvHashSet::from_iter(envelopes.keys().cloned()), cached_t.hash_set); - cached_t.amend(&mut envelopes); - cached_t - } else { - Threads::new(&mut envelopes) - }; - ret - } else { - Threads::new(&mut envelopes) - }; - */ + xdg::BaseDirectories::with_profile("meli", format!("{}_Thread", folder.hash())) + .unwrap(); + if let Some(cached) = cache_dir.find_cache_file("threads") { + let reader = io::BufReader::new(fs::File::open(cached).unwrap()); + let result: result::Result = bincode::deserialize_from(reader); + let ret = if let Ok(mut cached_t) = result { + use std::iter::FromIterator; + eprintln!("loaded cache, our hash set is {:?}\n and the cached one is {:?}", FnvHashSet::from_iter(envelopes.keys().cloned()), cached_t.hash_set); + cached_t.amend(&mut envelopes); + cached_t + } else { + Threads::new(&mut envelopes) + }; + ret + } else { + Threads::new(&mut envelopes) + }; + */ Collection { folder: folder.clone(), diff --git a/melib/src/mailbox/email.rs b/melib/src/mailbox/email.rs index 8cc16fec..2f0e582e 100644 --- a/melib/src/mailbox/email.rs +++ b/melib/src/mailbox/email.rs @@ -74,7 +74,7 @@ impl Address { Address::Group(g) => g.display_name.display(&g.raw), } } - + pub fn get_email(&self) -> String { match self { Address::Mailbox(m) => m.address_spec.display(&m.raw), @@ -94,11 +94,11 @@ impl PartialEq for Address { s.address_spec.display(&s.raw) == o.address_spec.display(&o.raw) } (Address::Group(s), Address::Group(o)) => { - s.display_name.display(&s.raw) == o.display_name.display(&o.raw) && s - .mailbox_list - .iter() - .zip(o.mailbox_list.iter()) - .fold(true, |b, (s, o)| b && (s == o)) + s.display_name.display(&s.raw) == o.display_name.display(&o.raw) + && s.mailbox_list + .iter() + .zip(o.mailbox_list.iter()) + .fold(true, |b, (s, o)| b && (s == o)) } } } @@ -616,22 +616,22 @@ impl Envelope { pub fn message_id_raw(&self) -> Cow { String::from_utf8_lossy(self.message_id.raw()) } - fn set_date(&mut self, new_val: &[u8]) { + fn set_date(&mut self, new_val: &[u8]) { self.date = String::from_utf8_lossy(new_val).into_owned(); } - fn set_bcc(&mut self, new_val: Vec
) { + fn set_bcc(&mut self, new_val: Vec
) { self.bcc = new_val; } - fn set_cc(&mut self, new_val: Vec
) { + fn set_cc(&mut self, new_val: Vec
) { self.cc = new_val; } - fn set_from(&mut self, new_val: Vec
) { + fn set_from(&mut self, new_val: Vec
) { self.from = new_val; } - fn set_to(&mut self, new_val: Vec
) { + fn set_to(&mut self, new_val: Vec
) { self.to = new_val; } - fn set_in_reply_to(&mut self, new_val: &[u8]) { + fn set_in_reply_to(&mut self, new_val: &[u8]) { let slice = match parser::message_id(new_val).to_full_result() { Ok(v) => v, Err(_) => { @@ -641,10 +641,10 @@ impl Envelope { }; self.in_reply_to = Some(MessageID::new(new_val, slice)); } - fn set_subject(&mut self, new_val: Vec) { + fn set_subject(&mut self, new_val: Vec) { self.subject = Some(new_val); } - fn set_message_id(&mut self, new_val: &[u8]) { + fn set_message_id(&mut self, new_val: &[u8]) { let slice = match parser::message_id(new_val).to_full_result() { Ok(v) => v, Err(_) => { @@ -653,7 +653,7 @@ impl Envelope { }; self.message_id = MessageID::new(new_val, slice); } - fn push_references(&mut self, new_val: &[u8]) { + fn push_references(&mut self, new_val: &[u8]) { let slice = match parser::message_id(new_val).to_full_result() { Ok(v) => v, Err(_) => { @@ -685,7 +685,7 @@ impl Envelope { } } } - fn set_references(&mut self, new_val: &[u8]) { + fn set_references(&mut self, new_val: &[u8]) { match self.references { Some(ref mut s) => { s.raw = new_val.into(); @@ -713,10 +713,10 @@ impl Envelope { pub fn thread(&self) -> usize { self.thread } - pub fn set_thread(&mut self, new_val: usize) { + pub fn set_thread(&mut self, new_val: usize) { self.thread = new_val; } - pub fn set_datetime(&mut self, new_val: chrono::DateTime) { + pub fn set_datetime(&mut self, new_val: chrono::DateTime) { self.timestamp = new_val.timestamp() as UnixTimestamp; } pub fn set_flag(&mut self, f: Flag, mut operation: Box) -> Result<()> { diff --git a/melib/src/mailbox/email/attachments.rs b/melib/src/mailbox/email/attachments.rs index b49f60c4..b0d95413 100644 --- a/melib/src/mailbox/email/attachments.rs +++ b/melib/src/mailbox/email/attachments.rs @@ -71,73 +71,77 @@ impl AttachmentBuilder { } pub fn content_type(&mut self, value: &[u8]) -> &Self { match parser::content_type(value).to_full_result() { - Ok((ct, cst, params)) => if ct.eq_ignore_ascii_case(b"multipart") { - let mut boundary = None; - for (n, v) in params { - if n.eq_ignore_ascii_case(b"boundary") { - boundary = Some(v); - break; + Ok((ct, cst, params)) => { + if ct.eq_ignore_ascii_case(b"multipart") { + let mut boundary = None; + for (n, v) in params { + if n.eq_ignore_ascii_case(b"boundary") { + boundary = Some(v); + break; + } } - } - assert!(boundary.is_some()); - let _boundary = boundary.unwrap(); - let offset = (_boundary.as_ptr() as usize).wrapping_sub(value.as_ptr() as usize); - let boundary = SliceBuild::new(offset, _boundary.len()); - let subattachments = Self::subattachments(&self.raw, boundary.get(&value)); - assert!(!subattachments.is_empty()); - self.content_type = ContentType::Multipart { - boundary, - kind: if cst.eq_ignore_ascii_case(b"mixed") { - MultipartType::Mixed - } else if cst.eq_ignore_ascii_case(b"alternative") { - MultipartType::Alternative - } else if cst.eq_ignore_ascii_case(b"digest") { - MultipartType::Digest - } else { - Default::default() - }, - subattachments, - }; - } else if ct.eq_ignore_ascii_case(b"text") { - self.content_type = ContentType::Text { - kind: Text::Plain, - charset: Charset::UTF8, - }; - for (n, v) in params { - if n.eq_ignore_ascii_case(b"charset") { + assert!(boundary.is_some()); + let _boundary = boundary.unwrap(); + let offset = + (_boundary.as_ptr() as usize).wrapping_sub(value.as_ptr() as usize); + let boundary = SliceBuild::new(offset, _boundary.len()); + let subattachments = Self::subattachments(&self.raw, boundary.get(&value)); + assert!(!subattachments.is_empty()); + self.content_type = ContentType::Multipart { + boundary, + kind: if cst.eq_ignore_ascii_case(b"mixed") { + MultipartType::Mixed + } else if cst.eq_ignore_ascii_case(b"alternative") { + MultipartType::Alternative + } else if cst.eq_ignore_ascii_case(b"digest") { + MultipartType::Digest + } else { + Default::default() + }, + subattachments, + }; + } else if ct.eq_ignore_ascii_case(b"text") { + self.content_type = ContentType::Text { + kind: Text::Plain, + charset: Charset::UTF8, + }; + for (n, v) in params { + if n.eq_ignore_ascii_case(b"charset") { + if let ContentType::Text { + charset: ref mut c, .. + } = self.content_type + { + *c = Charset::from(v); + } + break; + } + } + if cst.eq_ignore_ascii_case(b"html") { if let ContentType::Text { - charset: ref mut c, .. + kind: ref mut k, .. } = self.content_type { - *c = Charset::from(v); + *k = Text::Html; + } + } else if !cst.eq_ignore_ascii_case(b"plain") { + if let ContentType::Text { + kind: ref mut k, .. + } = self.content_type + { + *k = Text::Other { tag: cst.into() }; } - break; } + } else if ct.eq_ignore_ascii_case(b"message") && cst.eq_ignore_ascii_case(b"rfc822") + { + self.content_type = ContentType::MessageRfc822; + } else { + let mut tag: Vec = Vec::with_capacity(ct.len() + cst.len() + 1); + tag.extend(ct); + tag.push(b'/'); + tag.extend(cst); + self.content_type = ContentType::Unsupported { tag }; } - if cst.eq_ignore_ascii_case(b"html") { - if let ContentType::Text { - kind: ref mut k, .. - } = self.content_type - { - *k = Text::Html; - } - } else if !cst.eq_ignore_ascii_case(b"plain") { - if let ContentType::Text { - kind: ref mut k, .. - } = self.content_type - { - *k = Text::Other { tag: cst.into() }; - } - } - } else if ct.eq_ignore_ascii_case(b"message") && cst.eq_ignore_ascii_case(b"rfc822") { - self.content_type = ContentType::MessageRfc822; - } else { - let mut tag: Vec = Vec::with_capacity(ct.len() + cst.len() + 1); - tag.extend(ct); - tag.push(b'/'); - tag.extend(cst); - self.content_type = ContentType::Unsupported { tag }; - }, + } Err(v) => { eprintln!("parsing error in content_type: {:?} {:?}", value, v); } @@ -405,23 +409,25 @@ fn decode_rec_helper(a: &Attachment, filter: &Option) -> Vec { kind: ref multipart_type, subattachments: ref sub_att_vec, .. - } => if *multipart_type == MultipartType::Alternative { - for a in sub_att_vec { - if let ContentType::Text { - kind: Text::Plain, .. - } = a.content_type - { - return decode_helper(a, filter); + } => { + if *multipart_type == MultipartType::Alternative { + for a in sub_att_vec { + if let ContentType::Text { + kind: Text::Plain, .. + } = a.content_type + { + return decode_helper(a, filter); + } } + decode_helper(a, filter) + } else { + let mut vec = Vec::new(); + for a in sub_att_vec { + vec.extend(decode_rec_helper(a, filter)); + } + vec } - decode_helper(a, filter) - } else { - let mut vec = Vec::new(); - for a in sub_att_vec { - vec.extend(decode_rec_helper(a, filter)); - } - vec - }, + } }; if let Some(filter) = filter { filter(a, &mut ret); diff --git a/melib/src/mailbox/email/compose.rs b/melib/src/mailbox/email/compose.rs index e027cf7f..cad33e5c 100644 --- a/melib/src/mailbox/email/compose.rs +++ b/melib/src/mailbox/email/compose.rs @@ -3,8 +3,8 @@ use chrono::{DateTime, Local}; use data_encoding::BASE64_MIME; use std::str; -mod random; mod mime; +mod random; //use self::mime::*; @@ -70,13 +70,16 @@ impl str::FromStr for Draft { if ignore_header(k) { continue; } - if ret.headers.insert( - String::from_utf8(k.to_vec())?, - String::from_utf8(v.to_vec())?, - ).is_none() { + if ret + .headers + .insert( + String::from_utf8(k.to_vec())?, + String::from_utf8(v.to_vec())?, + ) + .is_none() + { ret.header_order.push(String::from_utf8(k.to_vec())?); } - } let body = Envelope::new(0).body_bytes(s.as_bytes()); @@ -191,7 +194,6 @@ impl Draft { } Ok(ret) - } } diff --git a/melib/src/mailbox/email/parser.rs b/melib/src/mailbox/email/parser.rs index 5b0e0b75..a8d7ef34 100644 --- a/melib/src/mailbox/email/parser.rs +++ b/melib/src/mailbox/email/parser.rs @@ -135,16 +135,19 @@ named!( named!( header_no_val<(&[u8], &[u8])>, - do_parse!( - name: complete!(name) >> - tag!(b":") >> - opt!(is_a!(" \t")) >> - tag!(b"\n") >> - ( { (name, b"") } ))); + do_parse!( + name: complete!(name) + >> tag!(b":") + >> opt!(is_a!(" \t")) + >> tag!(b"\n") + >> ({ (name, b"") }) + ) +); named!( header<(&[u8], &[u8])>, - alt_complete!(header_no_val | header_has_val)); + alt_complete!(header_no_val | header_has_val) +); /* Parse all headers -> Vec<(&str, Vec<&str>)> */ named!(pub headers>, many1!(complete!(header))); diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 00000000..e69de29b diff --git a/src/bin.rs b/src/bin.rs index a0e8edd5..fa0a7fc8 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -48,9 +48,9 @@ fn main() { //let _stdout = stdout(); //let mut _stdout = _stdout.lock(); /* - let _stderr = stderr(); - let mut _stderr = _stderr.lock(); - */ + let _stderr = stderr(); + let mut _stderr = _stderr.lock(); + */ /* Catch SIGWINCH to handle terminal resizing */ let signal = chan_signal::notify(&[Signal::WINCH]); @@ -66,7 +66,11 @@ fn main() { let menu = Entity::from(Box::new(AccountMenu::new(&state.context.accounts))); let listing = listing::Listing::from(IndexStyle::Compact); let b = Entity::from(Box::new(listing)); - let tabs = Box::new(Tabbed::new(vec![Box::new(VSplit::new(menu, b, 90, true)), Box::new(AccountsPanel::new(&state.context)), Box::new(ContactList::default())])); + let tabs = Box::new(Tabbed::new(vec![ + Box::new(VSplit::new(menu, b, 90, true)), + Box::new(AccountsPanel::new(&state.context)), + Box::new(ContactList::default()), + ])); let window = Entity::from(tabs); let status_bar = Entity::from(Box::new(StatusBar::new(window))); @@ -75,7 +79,9 @@ fn main() { let xdg_notifications = Entity::from(Box::new(ui::components::notifications::XDGNotifications {})); state.register_entity(xdg_notifications); - state.register_entity(Entity::from(Box::new(ui::components::notifications::NotificationFilter {}))); + state.register_entity(Entity::from(Box::new( + ui::components::notifications::NotificationFilter {}, + ))); /* Keep track of the input mode. See ui::UIMode for details */ 'main: loop { diff --git a/ui/src/components.rs b/ui/src/components.rs index d6dd2d9e..1ded7791 100644 --- a/ui/src/components.rs +++ b/ui/src/components.rs @@ -20,10 +20,10 @@ */ /*! - Components are ways to handle application data. They can draw on the terminal and receive events, but also do other stuff as well. (For example, see the `notifications` module.) +Components are ways to handle application data. They can draw on the terminal and receive events, but also do other stuff as well. (For example, see the `notifications` module.) - See the `Component` Trait for more details. - */ +See the `Component` Trait for more details. +*/ use super::*; @@ -76,8 +76,6 @@ const _DOUBLE_DOWN_AND_LEFT: char = '╗'; const _DOUBLE_UP_AND_LEFT: char = '╝'; const _DOUBLE_UP_AND_RIGHT: char = '╚'; - - type EntityId = Uuid; /// `Entity` is a container for Components. @@ -157,7 +155,9 @@ pub trait Component: Display + Debug + Send { fn kill(&mut self, _id: EntityId) {} fn set_id(&mut self, _id: EntityId) {} - fn get_shortcuts(&self, context: &Context) -> ShortcutMap { Default::default() } + fn get_shortcuts(&self, context: &Context) -> ShortcutMap { + Default::default() + } } /* @@ -429,20 +429,23 @@ pub(crate) fn set_and_join_box(grid: &mut CellBuffer, idx: Pos, ch: char) { } pub fn create_box(grid: &mut CellBuffer, area: Area) { - let upper_left = upper_left!(area); - let bottom_right = bottom_right!(area); + if !is_valid_area!(area) { + return; + } + let upper_left = upper_left!(area); + let bottom_right = bottom_right!(area); - for x in get_x(upper_left)..get_x(bottom_right) { - grid[(x, get_y(upper_left))].set_ch(HORZ_BOUNDARY); - grid[(x, get_y(bottom_right))].set_ch(HORZ_BOUNDARY); - } + for x in get_x(upper_left)..get_x(bottom_right) { + grid[(x, get_y(upper_left))].set_ch(HORZ_BOUNDARY); + grid[(x, get_y(bottom_right))].set_ch(HORZ_BOUNDARY); + } - for y in get_y(upper_left)..get_y(bottom_right) { - grid[(get_x(upper_left), y)].set_ch(VERT_BOUNDARY); - grid[(get_x(bottom_right), y)].set_ch(VERT_BOUNDARY); - } - set_and_join_box(grid, upper_left, HORZ_BOUNDARY); - set_and_join_box(grid, set_x(upper_left, get_x(bottom_right)), HORZ_BOUNDARY); - set_and_join_box(grid, set_y(upper_left, get_y(bottom_right)), VERT_BOUNDARY); - set_and_join_box(grid, bottom_right, VERT_BOUNDARY); + for y in get_y(upper_left)..get_y(bottom_right) { + grid[(get_x(upper_left), y)].set_ch(VERT_BOUNDARY); + grid[(get_x(bottom_right), y)].set_ch(VERT_BOUNDARY); + } + set_and_join_box(grid, upper_left, HORZ_BOUNDARY); + set_and_join_box(grid, set_x(upper_left, get_x(bottom_right)), HORZ_BOUNDARY); + set_and_join_box(grid, set_y(upper_left, get_y(bottom_right)), VERT_BOUNDARY); + set_and_join_box(grid, bottom_right, VERT_BOUNDARY); } diff --git a/ui/src/components/contacts.rs b/ui/src/components/contacts.rs index 08190b85..9bd73f86 100644 --- a/ui/src/components/contacts.rs +++ b/ui/src/components/contacts.rs @@ -78,7 +78,7 @@ impl ContactManager { Color::Default, ((0, 0), (width, 0)), false, - ); + ); let (x, _) = write_string_to_grid( "Last edited: ", &mut self.content, @@ -86,7 +86,7 @@ impl ContactManager { Color::Default, ((x, 0), (width, 0)), false, - ); + ); write_string_to_grid( &self.card.last_edited(), &mut self.content, @@ -94,15 +94,23 @@ impl ContactManager { Color::Default, ((x, 0), (width, 0)), false, - ); + ); self.form = FormWidget::new("Save".into()); self.form.add_button(("Cancel".into(), false)); - self.form.push(("First Name".into(), self.card.firstname().to_string())); - self.form.push(("Last Name".into(), self.card.lastname().to_string())); - self.form.push(("Additional Name".into(), self.card.additionalname().to_string())); - self.form.push(("Name Prefix".into(), self.card.name_prefix().to_string())); - self.form.push(("Name Suffix".into(), self.card.name_suffix().to_string())); - self.form.push(("E-mail".into(), self.card.email().to_string())); + self.form + .push(("First Name".into(), self.card.firstname().to_string())); + self.form + .push(("Last Name".into(), self.card.lastname().to_string())); + self.form.push(( + "Additional Name".into(), + self.card.additionalname().to_string(), + )); + self.form + .push(("Name Prefix".into(), self.card.name_prefix().to_string())); + self.form + .push(("Name Suffix".into(), self.card.name_suffix().to_string())); + self.form + .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())); } @@ -120,55 +128,71 @@ impl Component for ContactManager { let upper_left = upper_left!(area); let bottom_right = bottom_right!(area); - self.form.draw(grid, (set_y(upper_left, get_y(upper_left) + 1), bottom_right), context); + self.form.draw( + grid, + (set_y(upper_left, get_y(upper_left) + 1), bottom_right), + context, + ); context.dirty_areas.push_back(area); } fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool { if self.form.process_event(event, context) { match self.form.buttons_result() { - None => {}, + None => {} Some(true) => { - let mut fields = std::mem::replace(&mut self.form, FormWidget::default()).collect().unwrap(); - let fields: FnvHashMap = fields.into_iter().map(|(s, v)| { - (s, match v { - Field::Text(v, _, _) | Field::TextArea(v, _) => v, - Field::Choice(mut v, c) => v.remove(c), - })}).collect(); + let mut fields = std::mem::replace(&mut self.form, FormWidget::default()) + .collect() + .unwrap(); + let fields: FnvHashMap = fields + .into_iter() + .map(|(s, v)| { + ( + s, + match v { + Field::Text(v, _, _) | Field::TextArea(v, _) => v, + Field::Choice(mut v, c) => v.remove(c), + }, + ) + }) + .collect(); let mut new_card = Card::from(fields); new_card.set_id(*self.card.id()); - context.accounts[self.account_pos].address_book.add_card(new_card); + context.accounts[self.account_pos] + .address_book + .add_card(new_card); context.replies.push_back(UIEvent { id: 0, - event_type: UIEventType::StatusEvent(StatusEvent::DisplayMessage("Saved.".into())), + event_type: UIEventType::StatusEvent(StatusEvent::DisplayMessage( + "Saved.".into(), + )), }); context.replies.push_back(UIEvent { id: 0, event_type: UIEventType::EntityKill(self.id), }); - }, + } Some(false) => { context.replies.push_back(UIEvent { id: 0, event_type: UIEventType::EntityKill(self.id), }); - - }, + } } return true; } /* - match event.event_type { - UIEventType::Input(Key::Char('\n')) => { - context.replies.push_back(UIEvent { - id: 0, - event_type: UIEventType::EntityKill(self.id), - }); - return true; - }, - _ => {}, - } - */ + match event.event_type { + UIEventType::Input(Key::Char('\n')) => { + context.replies.push_back(UIEvent { + id: 0, + event_type: UIEventType::EntityKill(self.id), + }); + return true; + }, + _ => {}, + } + */ false } diff --git a/ui/src/components/contacts/contact_list.rs b/ui/src/components/contacts/contact_list.rs index f1b70ddf..c9f47cd4 100644 --- a/ui/src/components/contacts/contact_list.rs +++ b/ui/src/components/contacts/contact_list.rs @@ -20,7 +20,7 @@ pub struct ContactList { content: CellBuffer, id_positions: Vec, - + mode: ViewMode, dirty: bool, view: Option, @@ -60,29 +60,30 @@ impl ContactList { ..Self::new() } } - + fn initialize(&mut self, context: &mut Context) { let account = &mut context.accounts[self.account_pos]; let book = &mut account.address_book; self.length = book.len(); - self.content.resize(MAX_COLS, book.len(), Cell::with_char(' ')); + self.content + .resize(MAX_COLS, book.len(), Cell::with_char(' ')); self.id_positions.clear(); if self.id_positions.capacity() < book.len() { - self.id_positions.reserve(book.len()); + self.id_positions.reserve(book.len()); } for (i, c) in book.values().enumerate() { self.id_positions.push(*c.id()); - + write_string_to_grid( c.email(), &mut self.content, Color::Default, Color::Default, ((0, i), (MAX_COLS - 1, book.len() - 1)), - false - ); + false, + ); } } } @@ -105,7 +106,15 @@ impl Component for ContactList { if self.dirty { self.initialize(context); clear_area(grid, area); - copy_area(grid, &self.content, area, ((0, 0), (MAX_COLS - 1, self.content.size().1.saturating_sub(1)))); + copy_area( + grid, + &self.content, + area, + ( + (0, 0), + (MAX_COLS - 1, self.content.size().1.saturating_sub(1)), + ), + ); context.dirty_areas.push_back(area); self.dirty = false; } @@ -116,11 +125,27 @@ impl Component for ContactList { /* Reset previously highlighted line */ let fg_color = Color::Default; let bg_color = Color::Default; - change_colors(grid, (pos_inc(upper_left, (0, self.cursor_pos)), set_y(bottom_right, get_y(upper_left) + self.cursor_pos)), fg_color, bg_color); + change_colors( + grid, + ( + pos_inc(upper_left, (0, self.cursor_pos)), + set_y(bottom_right, get_y(upper_left) + self.cursor_pos), + ), + fg_color, + bg_color, + ); /* Highlight current line */ let bg_color = Color::Byte(246); - change_colors(grid, (pos_inc(upper_left, (0, self.new_cursor_pos)), set_y(bottom_right, get_y(upper_left) + self.new_cursor_pos)), fg_color, bg_color); + change_colors( + grid, + ( + pos_inc(upper_left, (0, self.new_cursor_pos)), + set_y(bottom_right, get_y(upper_left) + self.new_cursor_pos), + ), + fg_color, + bg_color, + ); self.cursor_pos = self.new_cursor_pos; } @@ -139,10 +164,10 @@ impl Component for ContactList { self.mode = ViewMode::View(*entity.id()); self.view = Some(entity); - + return true; - }, - + } + UIEventType::Input(ref key) if *key == shortcuts["edit_contact"] && self.length > 0 => { let account = &mut context.accounts[self.account_pos]; let book = &mut account.address_book; @@ -154,9 +179,9 @@ impl Component for ContactList { self.mode = ViewMode::View(*entity.id()); self.view = Some(entity); - + return true; - }, + } UIEventType::Input(Key::Char('n')) => { let card = Card::new(); let mut manager = ContactManager::default(); @@ -165,27 +190,26 @@ impl Component for ContactList { let entity = Entity::from(Box::new(manager)); self.mode = ViewMode::View(*entity.id()); self.view = Some(entity); - + return true; - }, + } UIEventType::Input(Key::Up) => { self.set_dirty(); self.new_cursor_pos = self.cursor_pos.saturating_sub(1); return true; - }, + } UIEventType::Input(Key::Down) if self.cursor_pos < self.length.saturating_sub(1) => { self.set_dirty(); self.new_cursor_pos += 1; return true; - }, + } UIEventType::EntityKill(ref kill_id) if self.mode == ViewMode::View(*kill_id) => { self.mode = ViewMode::List; self.view.take(); self.set_dirty(); return true; - - }, - _ => {}, + } + _ => {} } false } @@ -205,7 +229,11 @@ impl Component for ContactList { self.mode = ViewMode::Close(uuid); } fn get_shortcuts(&self, context: &Context) -> ShortcutMap { - let mut map = self.view.as_ref().map(|p| p.get_shortcuts(context)).unwrap_or_default(); + let mut map = self + .view + .as_ref() + .map(|p| p.get_shortcuts(context)) + .unwrap_or_default(); let config_map = context.settings.shortcuts.contact_list.key_values(); map.insert("create_contact", (*config_map["create_contact"]).clone()); diff --git a/ui/src/components/indexer.rs b/ui/src/components/indexer.rs index 30da0f5e..2d1640ea 100644 --- a/ui/src/components/indexer.rs +++ b/ui/src/components/indexer.rs @@ -58,8 +58,7 @@ impl Default for Indexer { } impl Indexer { - fn draw_menu(&mut self, _grid: &mut CellBuffer, _area: Area, _context: &mut Context) { - } + fn draw_menu(&mut self, _grid: &mut CellBuffer, _area: Area, _context: &mut Context) {} } impl Component for Indexer { diff --git a/ui/src/components/indexer/index.rs b/ui/src/components/indexer/index.rs index d636a9a0..4794baca 100644 --- a/ui/src/components/indexer/index.rs +++ b/ui/src/components/indexer/index.rs @@ -42,7 +42,7 @@ impl Index { let bg_color = if self.cursor_pos == idx { Color::Byte(246) /* } else if idx % 2 == 0 { - Color::Byte(236)*/ + Color::Byte(236)*/ } else { Color::Default }; diff --git a/ui/src/components/mail.rs b/ui/src/components/mail.rs index fcfd7aa5..f4d4322f 100644 --- a/ui/src/components/mail.rs +++ b/ui/src/components/mail.rs @@ -73,7 +73,8 @@ impl AccountMenu { } entries }, - }).collect(); + }) + .collect(); AccountMenu { accounts, dirty: true, diff --git a/ui/src/components/mail/accounts.rs b/ui/src/components/mail/accounts.rs index 8fdc620b..c3684a4b 100644 --- a/ui/src/components/mail/accounts.rs +++ b/ui/src/components/mail/accounts.rs @@ -35,7 +35,6 @@ impl fmt::Display for AccountsPanel { } } - impl Component for AccountsPanel { fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) { if self.dirty { @@ -46,26 +45,26 @@ impl Component for AccountsPanel { Color::Default, ((2, 3), (120 - 1, 3)), true, - ); + ); for (i, a) in context.accounts.iter().enumerate() { - create_box(&mut self.content, ((2,5+i*10 ), (120-1, 15+i*10))); + create_box(&mut self.content, ((2, 5 + i * 10), (120 - 1, 15 + i * 10))); let (x, y) = write_string_to_grid( a.name(), &mut self.content, Color::Default, Color::Default, - ((3, 5 + i*10), (120 - 2, 5 + i*10)), + ((3, 5 + i * 10), (120 - 2, 5 + i * 10)), true, - ); + ); write_string_to_grid( " ▒██▒ ", &mut self.content, Color::Byte(32), Color::Default, - ((x, y), (120 - 2, 5 + i*10)), + ((x, y), (120 - 2, 5 + i * 10)), true, - ); + ); write_string_to_grid( &a.runtime_settings.account().identity, &mut self.content, @@ -73,10 +72,10 @@ impl Component for AccountsPanel { Color::Default, ((4, y + 2), (120 - 2, y + 2)), true, - ); + ); if i == self.cursor { for h in 1..8 { - self.content[(2, h+y+1)].set_ch('*'); + self.content[(2, h + y + 1)].set_ch('*'); } } write_string_to_grid( @@ -86,7 +85,7 @@ impl Component for AccountsPanel { Color::Default, ((5, y + 3), (120 - 2, y + 3)), true, - ); + ); write_string_to_grid( "- Contacts", &mut self.content, @@ -94,7 +93,7 @@ impl Component for AccountsPanel { Color::Default, ((5, y + 4), (120 - 2, y + 4)), true, - ); + ); write_string_to_grid( "- Mailing Lists", &mut self.content, @@ -102,10 +101,7 @@ impl Component for AccountsPanel { Color::Default, ((5, y + 5), (120 - 2, y + 5)), true, - ); - - - + ); } self.dirty = false; } @@ -121,22 +117,24 @@ impl Component for AccountsPanel { self.cursor = self.cursor.saturating_sub(1); self.dirty = true; return true; - }, + } UIEventType::Input(Key::Down) => { if self.cursor + 1 < context.accounts.len() { self.cursor += 1; self.dirty = true; } return true; - }, + } UIEventType::Input(Key::Char('\n')) => { context.replies.push_back(UIEvent { id: 0, - event_type: UIEventType::Action(Tab(TabOpen(Some(Box::new(ContactList::for_account(self.cursor))) - )))}); + event_type: UIEventType::Action(Tab(TabOpen(Some(Box::new( + ContactList::for_account(self.cursor), + ))))), + }); return true; - }, - _ => {}, + } + _ => {} } false diff --git a/ui/src/components/mail/compose.rs b/ui/src/components/mail/compose.rs index 5b8de117..06be5ae8 100644 --- a/ui/src/components/mail/compose.rs +++ b/ui/src/components/mail/compose.rs @@ -24,7 +24,6 @@ use super::*; use melib::Draft; use std::str::FromStr; - #[derive(Debug, PartialEq)] enum Cursor { Headers, @@ -173,10 +172,15 @@ impl Composer { let account_cursor = self.account_cursor; for &k in &["Date", "From", "To", "Cc", "Bcc", "Subject"] { if k == "To" { - self.form.push_cl((k.into(), headers[k].to_string(), Box::new(move |c, term| { - let book: &AddressBook = &c.accounts[account_cursor].address_book; - let results: Vec = book.search(term); - results}))); + self.form.push_cl(( + k.into(), + headers[k].to_string(), + Box::new(move |c, term| { + let book: &AddressBook = &c.accounts[account_cursor].address_book; + let results: Vec = book.search(term); + results + }), + )); } else { self.form.push((k.into(), headers[k].to_string())); } @@ -184,36 +188,36 @@ impl Composer { } /* - let (x, y) = if k == "From" { - write_string_to_grid( - "◀ ", - grid, - Color::Byte(251), - Color::Default, - ((x, y), set_y(bottom_right, y)), - true, - ) - } else { - (x, y) - }; - let (x, y) = write_string_to_grid( - &headers[k], - grid, - Color::Default, - bg_color, - ((x, y), set_y(bottom_right, y)), - true, - ); - if k == "From" { - write_string_to_grid( - " ▶", - grid, - Color::Byte(251), - Color::Default, - ((x, y), set_y(bottom_right, y)), - true, - ) - */ + let (x, y) = if k == "From" { + write_string_to_grid( + "◀ ", + grid, + Color::Byte(251), + Color::Default, + ((x, y), set_y(bottom_right, y)), + true, + ) + } else { + (x, y) + }; + let (x, y) = write_string_to_grid( + &headers[k], + grid, + Color::Default, + bg_color, + ((x, y), set_y(bottom_right, y)), + true, + ); + if k == "From" { + write_string_to_grid( + " ▶", + grid, + Color::Byte(251), + Color::Default, + ((x, y), set_y(bottom_right, y)), + true, + ) + */ } impl Component for Composer { @@ -283,14 +287,21 @@ impl Component for Composer { } if self.dirty { - for i in get_x(upper_left) + mid + 1..=get_x(upper_left) + mid + width.saturating_sub(0) { + for i in get_x(upper_left) + mid + 1..=get_x(upper_left) + mid + width.saturating_sub(0) + { //set_and_join_box(grid, (i, header_height), HORZ_BOUNDARY); //grid[(i, header_height)].set_fg(Color::Default); //grid[(i, header_height)].set_bg(Color::Default); } } - let header_area = (pos_inc(upper_left, (mid + 1, 0)), (get_x(bottom_right).saturating_sub(mid), get_y(upper_left) + header_height + 1)); + let header_area = ( + pos_inc(upper_left, (mid + 1, 0)), + ( + get_x(bottom_right).saturating_sub(mid), + get_y(upper_left) + header_height + 1, + ), + ); let body_area = ( pos_inc(upper_left, (mid + 1, header_height + 2)), pos_dec(bottom_right, ((mid, 0))), @@ -303,21 +314,26 @@ impl Component for Composer { ViewMode::Overview | ViewMode::Pager => { self.pager.set_dirty(); self.pager.draw(grid, body_area, context); - }, + } ViewMode::Discard(_) => { /* Let user choose whether to quit with/without saving or cancel */ - let mid_x = { - std::cmp::max(width!(area) / 2, width / 2) - width / 2 - }; - let mid_y = { - std::cmp::max(height!(area) / 2, 11) - 11 - }; + let mid_x = { std::cmp::max(width!(area) / 2, width / 2) - width / 2 }; + let mid_y = { std::cmp::max(height!(area) / 2, 11) - 11 }; let upper_left = upper_left!(body_area); let bottom_right = bottom_right!(body_area); - let area = (pos_inc(upper_left, (mid_x, mid_y)), pos_dec(bottom_right, (mid_x, mid_y))); + let area = ( + pos_inc(upper_left, (mid_x, mid_y)), + pos_dec(bottom_right, (mid_x, mid_y)), + ); create_box(grid, area); - let area = (pos_inc(upper_left, (mid_x + 2, mid_y + 2)), pos_dec(bottom_right, (mid_x.saturating_sub(2), mid_y.saturating_sub(2)))); + let area = ( + pos_inc(upper_left, (mid_x + 2, mid_y + 2)), + pos_dec( + bottom_right, + (mid_x.saturating_sub(2), mid_y.saturating_sub(2)), + ), + ); let (_, y) = write_string_to_grid( &format!("Draft \"{:10}\"", self.draft.headers()["Subject"]), @@ -326,7 +342,7 @@ impl Component for Composer { Color::Default, area, true, - ); + ); let (_, y) = write_string_to_grid( "[x] quit without saving", grid, @@ -334,7 +350,7 @@ impl Component for Composer { Color::Default, (set_y(upper_left!(area), y + 2), bottom_right!(area)), true, - ); + ); let (_, y) = write_string_to_grid( "[y] save draft and quit", grid, @@ -342,7 +358,7 @@ impl Component for Composer { Color::Default, (set_y(upper_left!(area), y + 1), bottom_right!(area)), true, - ); + ); write_string_to_grid( "[n] cancel", grid, @@ -350,9 +366,8 @@ impl Component for Composer { Color::Default, (set_y(upper_left!(area), y + 1), bottom_right!(area)), true, - ); - - }, + ); + } } context.dirty_areas.push_back(area); @@ -371,7 +386,7 @@ impl Component for Composer { self.dirty = true; return true; } - }, + } _ => {} } if self.form.process_event(event, context) { @@ -381,36 +396,36 @@ impl Component for Composer { match event.event_type { UIEventType::Resize => { self.set_dirty(); - }, + } /* /* Switch e-mail From: field to the `left` configured account. */ UIEventType::Input(Key::Left) if self.cursor == Cursor::From => { - self.account_cursor = self.account_cursor.saturating_sub(1); - self.draft.headers_mut().insert( - "From".into(), - get_display_name(context, self.account_cursor), - ); - self.dirty = true; - return true; + self.account_cursor = self.account_cursor.saturating_sub(1); + self.draft.headers_mut().insert( + "From".into(), + get_display_name(context, self.account_cursor), + ); + self.dirty = true; + return true; } /* Switch e-mail From: field to the `right` configured account. */ UIEventType::Input(Key::Right) if self.cursor == Cursor::From => { - if self.account_cursor + 1 < context.accounts.len() { - self.account_cursor += 1; - self.draft.headers_mut().insert( - "From".into(), - get_display_name(context, self.account_cursor), - ); - self.dirty = true; - } - return true; + if self.account_cursor + 1 < context.accounts.len() { + self.account_cursor += 1; + self.draft.headers_mut().insert( + "From".into(), + get_display_name(context, self.account_cursor), + ); + self.dirty = true; + } + return true; }*/ UIEventType::Input(Key::Up) => { self.cursor = Cursor::Headers; - }, + } UIEventType::Input(Key::Down) => { self.cursor = Cursor::Body; - }, + } UIEventType::Input(Key::Char(key)) if self.mode.is_discard() => { match (key, &self.mode) { ('x', ViewMode::Discard(u)) => { @@ -420,7 +435,7 @@ impl Component for Composer { }); return true; } - ('n', _) => {}, + ('n', _) => {} ('y', ViewMode::Discard(u)) => { let account = &context.accounts[self.account_cursor]; let draft = std::mem::replace(&mut self.draft, Draft::default()); @@ -432,7 +447,7 @@ impl Component for Composer { event_type: UIEventType::Action(Tab(Kill(*u))), }); return true; - }, + } _ => { return false; } @@ -454,48 +469,48 @@ impl Component for Composer { return true; } UIEventType::Input(Key::Char('e')) if self.cursor == Cursor::Body => { - /* Edit draft in $EDITOR */ - use std::process::{Command, Stdio}; - /* Kill input thread so that spawned command can be sole receiver of stdin */ - { - context.input_kill(); - } - /* update Draft's headers based on form values */ - self.update_draft(); - let mut f = - create_temp_file(self.draft.to_string().unwrap().as_str().as_bytes(), None); - //let mut f = Box::new(std::fs::File::create(&dir).unwrap()); + /* Edit draft in $EDITOR */ + use std::process::{Command, Stdio}; + /* Kill input thread so that spawned command can be sole receiver of stdin */ + { + context.input_kill(); + } + /* update Draft's headers based on form values */ + self.update_draft(); + let mut f = + create_temp_file(self.draft.to_string().unwrap().as_str().as_bytes(), None); + //let mut f = Box::new(std::fs::File::create(&dir).unwrap()); - // TODO: check exit status - Command::new("vim") - .arg("+/^$") - .arg(&f.path()) - .stdin(Stdio::inherit()) - .stdout(Stdio::inherit()) - .output() - .expect("failed to execute process"); - let result = f.read_to_string(); - self.draft = Draft::from_str(result.as_str()).unwrap(); - self.initialized = false; - context.replies.push_back(UIEvent { - id: 0, - event_type: UIEventType::Fork(ForkType::Finished), - }); - context.restore_input(); - /* + // TODO: check exit status + Command::new("vim") + .arg("+/^$") + .arg(&f.path()) + .stdin(Stdio::inherit()) + .stdout(Stdio::inherit()) + .output() + .expect("failed to execute process"); + let result = f.read_to_string(); + self.draft = Draft::from_str(result.as_str()).unwrap(); + self.initialized = false; + context.replies.push_back(UIEvent { + id: 0, + event_type: UIEventType::Fork(ForkType::Finished), + }); + context.restore_input(); + /* - Cursor::To | Cursor::Cc | Cursor::Bcc => { - let account = &context.accounts[self.account_cursor]; - let mut entries = account.address_book.values().map(|v| (v.id().as_bytes().to_vec(), v.email().to_string())).collect(); - self.mode = ViewMode::Selector(Selector::new(entries, true)); - }, - Cursor::Attachments => { - unimplemented!() - }, - Cursor::From => { - return true; - } - */ + Cursor::To | Cursor::Cc | Cursor::Bcc => { + let account = &context.accounts[self.account_cursor]; + let mut entries = account.address_book.values().map(|v| (v.id().as_bytes().to_vec(), v.email().to_string())).collect(); + self.mode = ViewMode::Selector(Selector::new(entries, true)); + }, + Cursor::Attachments => { + unimplemented!() + }, + Cursor::From => { + return true; + } + */ self.dirty = true; return true; } @@ -505,11 +520,14 @@ impl Component for Composer { } fn is_dirty(&self) -> bool { - self.dirty || self.pager.is_dirty() || self - .reply_context - .as_ref() - .map(|(_, p)| p.is_dirty()) - .unwrap_or(false) || self.form.is_dirty() + self.dirty + || self.pager.is_dirty() + || self + .reply_context + .as_ref() + .map(|(_, p)| p.is_dirty()) + .unwrap_or(false) + || self.form.is_dirty() } fn set_dirty(&mut self) { diff --git a/ui/src/components/mail/listing.rs b/ui/src/components/mail/listing.rs index 722c81a9..35f57a0f 100644 --- a/ui/src/components/mail/listing.rs +++ b/ui/src/components/mail/listing.rs @@ -131,7 +131,6 @@ impl From for Listing { IndexStyle::Plain => Listing::Plain(Default::default()), IndexStyle::Threaded => Listing::Threaded(Default::default()), IndexStyle::Compact => Listing::Compact(Default::default()), - } } } diff --git a/ui/src/components/mail/listing/compact.rs b/ui/src/components/mail/listing/compact.rs index d47ea5cb..01dd4a7a 100644 --- a/ui/src/components/mail/listing/compact.rs +++ b/ui/src/components/mail/listing/compact.rs @@ -342,7 +342,9 @@ impl CompactListing { fn format_date(envelope: &Envelope) -> String { let d = std::time::UNIX_EPOCH + std::time::Duration::from_secs(envelope.date()); - let now: std::time::Duration = std::time::SystemTime::now().duration_since(d).unwrap_or_else(|_| std::time::Duration::new(std::u64::MAX, 0)); + let now: std::time::Duration = std::time::SystemTime::now() + .duration_since(d) + .unwrap_or_else(|_| std::time::Duration::new(std::u64::MAX, 0)); match now.as_secs() { n if n < 10 * 60 * 60 => format!("{} hours ago{}", n / (60 * 60), " ".repeat(8)), n if n < 24 * 60 * 60 => format!("{} hours ago{}", n / (60 * 60), " ".repeat(7)), @@ -527,20 +529,86 @@ impl Component for CompactListing { } fn get_shortcuts(&self, context: &Context) -> ShortcutMap { - let mut map = self.view.as_ref().map(|p| p.get_shortcuts(context)).unwrap_or_default(); + let mut map = self + .view + .as_ref() + .map(|p| p.get_shortcuts(context)) + .unwrap_or_default(); let config_map = context.settings.shortcuts.compact_listing.key_values(); - map.insert("open_thread", if let Some(key) = config_map.get("open_thread") { (*key).clone() } else { Key::Char('\n') }); - map.insert("prev_page", if let Some(key) = config_map.get("prev_page") { (*key).clone() } else { Key::PageUp }); - map.insert("next_page", if let Some(key) = config_map.get("next_page") { (*key).clone() } else { Key::PageDown }); - map.insert("exit_thread", if let Some(key) = config_map.get("exit_thread") { (*key).clone() } else { Key::Char('i') }); - map.insert("prev_folder", if let Some(key) = config_map.get("prev_folder") { (*key).clone() } else { Key::Char('J') }); - map.insert("next_folder", if let Some(key) = config_map.get("next_folder") { (*key).clone() } else { Key::Char('K') }); - map.insert("prev_account", if let Some(key) = config_map.get("prev_account") { (*key).clone() } else { Key::Char('h') }); - map.insert("next_account", if let Some(key) = config_map.get("next_account") { (*key).clone() } else { Key::Char('l') }); - map.insert("new_mail", if let Some(key) = config_map.get("new_mail") { (*key).clone() } else { Key::Char('m') }); + map.insert( + "open_thread", + if let Some(key) = config_map.get("open_thread") { + (*key).clone() + } else { + Key::Char('\n') + }, + ); + map.insert( + "prev_page", + if let Some(key) = config_map.get("prev_page") { + (*key).clone() + } else { + Key::PageUp + }, + ); + map.insert( + "next_page", + if let Some(key) = config_map.get("next_page") { + (*key).clone() + } else { + Key::PageDown + }, + ); + map.insert( + "exit_thread", + if let Some(key) = config_map.get("exit_thread") { + (*key).clone() + } else { + Key::Char('i') + }, + ); + map.insert( + "prev_folder", + if let Some(key) = config_map.get("prev_folder") { + (*key).clone() + } else { + Key::Char('J') + }, + ); + map.insert( + "next_folder", + if let Some(key) = config_map.get("next_folder") { + (*key).clone() + } else { + Key::Char('K') + }, + ); + map.insert( + "prev_account", + if let Some(key) = config_map.get("prev_account") { + (*key).clone() + } else { + Key::Char('h') + }, + ); + map.insert( + "next_account", + if let Some(key) = config_map.get("next_account") { + (*key).clone() + } else { + Key::Char('l') + }, + ); + map.insert( + "new_mail", + if let Some(key) = config_map.get("new_mail") { + (*key).clone() + } else { + Key::Char('m') + }, + ); map } - } diff --git a/ui/src/components/mail/listing/thread.rs b/ui/src/components/mail/listing/thread.rs index d6e18661..1b8a134d 100644 --- a/ui/src/components/mail/listing/thread.rs +++ b/ui/src/components/mail/listing/thread.rs @@ -413,7 +413,9 @@ impl ThreadListing { } fn format_date(envelope: &Envelope) -> String { let d = std::time::UNIX_EPOCH + std::time::Duration::from_secs(envelope.date()); - let now: std::time::Duration = std::time::SystemTime::now().duration_since(d).unwrap_or_else(|_| std::time::Duration::new(std::u64::MAX, 0)); + let now: std::time::Duration = std::time::SystemTime::now() + .duration_since(d) + .unwrap_or_else(|_| std::time::Duration::new(std::u64::MAX, 0)); match now.as_secs() { n if n < 10 * 60 * 60 => format!("{} hours ago{}", n / (60 * 60), " ".repeat(8)), n if n < 24 * 60 * 60 => format!("{} hours ago{}", n / (60 * 60), " ".repeat(7)), @@ -486,8 +488,10 @@ impl Component for ThreadListing { backend.operation(hash, folder_hash) }; let mailbox = &mut account[self.cursor_pos.1].as_mut().unwrap(); - let envelope: &mut Envelope = - mailbox.collection.get_mut(&self.locations[self.cursor_pos.2]).unwrap(); + let envelope: &mut Envelope = mailbox + .collection + .get_mut(&self.locations[self.cursor_pos.2]) + .unwrap(); envelope.set_seen(op).unwrap(); true } else { @@ -538,7 +542,7 @@ impl Component for ThreadListing { self.cursor_pos.0, self.cursor_pos.1, self.locations[self.cursor_pos.2], - ); + ); self.view = Some(MailView::new(coordinates, None, None)); self.view.as_mut().unwrap().draw( diff --git a/ui/src/components/mail/view.rs b/ui/src/components/mail/view.rs index 31d334dd..3e93daa9 100644 --- a/ui/src/components/mail/view.rs +++ b/ui/src/components/mail/view.rs @@ -123,7 +123,8 @@ impl MailView { v.extend(html_filter.wait_with_output().unwrap().stdout); } })), - )).into_owned(); + )) + .into_owned(); match self.mode { ViewMode::Normal | ViewMode::Subview => { let mut t = body_text.to_string(); @@ -171,8 +172,8 @@ impl MailView { let mut ret = "Viewing attachment. Press `r` to return \n".to_string(); ret.push_str(&attachments[aidx].text()); ret - }, - ViewMode::ContactSelector(_) => { unimplemented!()}, + } + ViewMode::ContactSelector(_) => unimplemented!(), } } pub fn plain_text_to_buf(s: &str, highlight_urls: bool) -> CellBuffer { @@ -341,13 +342,13 @@ impl Component for MailView { }; self.dirty = false; } - + match self.mode { ViewMode::Subview => { if let Some(s) = self.subview.as_mut() { s.draw(grid, (set_y(upper_left, y + 1), bottom_right), context); } - }, + } ViewMode::ContactSelector(ref mut s) => { clear_area(grid, (set_y(upper_left, y + 1), bottom_right)); s.draw(grid, (set_y(upper_left, y + 1), bottom_right), context); @@ -368,12 +369,12 @@ impl Component for MailView { return true; } } - }, + } ViewMode::ContactSelector(ref mut s) => { if s.process_event(event, context) { return true; } - }, + } _ => { if let Some(p) = self.pager.as_mut() { if p.process_event(event, context) { @@ -386,21 +387,25 @@ impl Component for MailView { match event.event_type { UIEventType::Input(Key::Char('c')) => { /* - let mut new_card: Card = Card::new(); - new_card.set_email(&envelope.from()[0].get_email()); - new_card.set_firstname(&envelope.from()[0].get_display_name()); + let mut new_card: Card = Card::new(); + new_card.set_email(&envelope.from()[0].get_email()); + new_card.set_firstname(&envelope.from()[0].get_display_name()); - eprintln!("{:?}", new_card); + eprintln!("{:?}", new_card); -*/ + */ if let ViewMode::ContactSelector(_) = self.mode { - if let ViewMode::ContactSelector(s) = std::mem::replace(&mut self.mode, ViewMode::Normal) { + if let ViewMode::ContactSelector(s) = + std::mem::replace(&mut self.mode, ViewMode::Normal) + { for c in s.collect() { let mut new_card: Card = Card::new(); let email = String::from_utf8(c).unwrap(); new_card.set_email(&email); new_card.set_firstname(""); - context.accounts[self.coordinates.0].address_book.add_card(new_card); + context.accounts[self.coordinates.0] + .address_book + .add_card(new_card); } //eprintln!("{:?}", s.collect()); } @@ -413,12 +418,18 @@ impl Component for MailView { .unwrap(); let envelope: &Envelope = &mailbox.collection[&self.coordinates.2]; let mut entries = Vec::new(); - entries.push((envelope.from()[0].get_email().into_bytes(), format!("{}", envelope.from()[0]))); - entries.push((envelope.to()[0].get_email().into_bytes(), format!("{}", envelope.to()[0]))); + entries.push(( + envelope.from()[0].get_email().into_bytes(), + format!("{}", envelope.from()[0]), + )); + entries.push(( + envelope.to()[0].get_email().into_bytes(), + format!("{}", envelope.to()[0]), + )); self.mode = ViewMode::ContactSelector(Selector::new(entries, true)); self.dirty = true; //context.accounts.context(self.coordinates.0).address_book.add_card(new_card); - }, + } UIEventType::Input(Key::Esc) | UIEventType::Input(Key::Alt('')) => { self.cmd_buf.clear(); context.replies.push_back(UIEvent { @@ -603,7 +614,7 @@ impl Component for MailView { true } fn is_dirty(&self) -> bool { - self.dirty + self.dirty || self.pager.as_ref().map(|p| p.is_dirty()).unwrap_or(false) || self.subview.as_ref().map(|p| p.is_dirty()).unwrap_or(false) || if let ViewMode::ContactSelector(ref s) = self.mode { diff --git a/ui/src/components/mail/view/envelope.rs b/ui/src/components/mail/view/envelope.rs index e01b5496..e9668bd8 100644 --- a/ui/src/components/mail/view/envelope.rs +++ b/ui/src/components/mail/view/envelope.rs @@ -108,7 +108,8 @@ impl EnvelopeView { v.extend(html_filter.wait_with_output().unwrap().stdout); } })), - )).into_owned(); + )) + .into_owned(); match self.mode { ViewMode::Normal | ViewMode::Subview => { let mut t = body_text.to_string(); @@ -378,8 +379,10 @@ impl Component for EnvelopeView { ContentType::MessageRfc822 => { self.mode = ViewMode::Subview; self.subview = Some(Box::new(Pager::from_string( - String::from_utf8_lossy(&decode_rec(u, None)).to_string(), context, - None, None + String::from_utf8_lossy(&decode_rec(u, None)).to_string(), + context, + None, + None, ))); } diff --git a/ui/src/components/notifications.rs b/ui/src/components/notifications.rs index cb73d0f5..1152d9e3 100644 --- a/ui/src/components/notifications.rs +++ b/ui/src/components/notifications.rs @@ -20,8 +20,8 @@ */ /*! - Notification handling components. - */ +Notification handling components. +*/ use notify_rust::Notification as notify_Notification; use std::process::{Command, Stdio}; @@ -45,12 +45,8 @@ impl Component for XDGNotifications { notify_Notification::new() .appname("meli") .icon("mail-message-new") - .summary( - title - .as_ref() - .map(|v| v.as_str()) - .unwrap_or("Event"), - ).body(&escape_str(body)) + .summary(title.as_ref().map(|v| v.as_str()).unwrap_or("Event")) + .body(&escape_str(body)) .icon("dialog-information") .show() .unwrap(); @@ -60,7 +56,6 @@ impl Component for XDGNotifications { fn set_dirty(&mut self) {} } - fn escape_str(s: &str) -> String { let mut ret: String = String::with_capacity(s.len()); for c in s.chars() { @@ -72,16 +67,17 @@ fn escape_str(s: &str) -> String { '"' => ret.push_str("""), _ => { let i = c as u32; - if (0x1 <= i && i <= 0x8) || - (0xb <= i && i <= 0xc) || - (0xe <= i && i <= 0x1f) || - (0x7f <= i && i <= 0x84) || - (0x86 <= i && i <= 0x9f) { + if (0x1 <= i && i <= 0x8) + || (0xb <= i && i <= 0xc) + || (0xe <= i && i <= 0x1f) + || (0x7f <= i && i <= 0x84) + || (0x86 <= i && i <= 0x9f) + { ret.push_str(&format!("&#{:x}%{:x};", i, i)); } else { ret.push(c); } - }, + } } } ret @@ -123,16 +119,14 @@ impl Component for NotificationFilter { if let UIEventType::Notification(ref title, ref body) = event.event_type { if let Some(ref bin) = context.runtime_settings.notifications.script { if let Err(v) = Command::new(bin) - .arg(title - .as_ref() - .map(|v| v.as_str()) - .unwrap_or("Event")) - .arg(body) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .spawn() { - eprintln!("{:?}",v); - } + .arg(title.as_ref().map(|v| v.as_str()).unwrap_or("Event")) + .arg(body) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + { + eprintln!("{:?}", v); + } } } false diff --git a/ui/src/components/utilities.rs b/ui/src/components/utilities.rs index cbcba647..f9b927b5 100644 --- a/ui/src/components/utilities.rs +++ b/ui/src/components/utilities.rs @@ -252,7 +252,12 @@ impl Pager { self.cursor_pos = 0; self.max_cursor_pos = None; } - pub fn from_string(mut text: String, context: &mut Context, cursor_pos: Option, width: Option) -> Self { + pub fn from_string( + mut text: String, + context: &mut Context, + cursor_pos: Option, + width: Option, + ) -> Self { let pager_filter: Option<&String> = context.settings.pager.filter.as_ref(); //let format_flowed: bool = context.settings.pager.format_flowed; if let Some(bin) = pager_filter { @@ -275,10 +280,10 @@ impl Pager { .wait_with_output() .expect("Failed to wait on filter") .stdout, - ).to_string(); + ) + .to_string(); } - let content = { let lines: Vec<&str> = if let Some(width) = width { word_break_string(text.as_str(), width) @@ -313,7 +318,7 @@ impl Pager { let height = lines.len() + 1; let width = width.unwrap_or_else(|| lines.iter().map(|l| l.len()).max().unwrap_or(0)); let mut content = CellBuffer::new(width, height, Cell::with_char(' ')); - + Pager::print_string(&mut content, lines); Pager { text: text.to_string(), @@ -347,7 +352,7 @@ impl Pager { Color::Default, ((0, i), (width - 1, i)), true, - ); + ); } } pub fn cursor_pos(&self) -> usize { @@ -824,7 +829,10 @@ impl Tabbed { let cslice: &mut [Cell] = grid; //TODO: bounds check let cslice_len = cslice.len(); - for c in cslice[(y * cols) + x.saturating_sub(1)..std::cmp::min((y * cols) + x.saturating_sub(1), cslice_len)].iter_mut() { + for c in cslice[(y * cols) + x.saturating_sub(1) + ..std::cmp::min((y * cols) + x.saturating_sub(1), cslice_len)] + .iter_mut() + { c.set_bg(Color::Byte(7)); c.set_ch(' '); } @@ -846,7 +854,13 @@ impl fmt::Display for Tabbed { impl Component for Tabbed { fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) { if self.dirty { - clear_area(grid, (upper_left!(area), set_x(upper_left!(area), get_x(bottom_right!(area))))); + clear_area( + grid, + ( + upper_left!(area), + set_x(upper_left!(area), get_x(bottom_right!(area))), + ), + ); self.dirty = false; } @@ -871,29 +885,50 @@ impl Component for Tabbed { if self.show_shortcuts { let area = ( - pos_inc(upper_left!(area), (2, 1)), set_x(bottom_right!(area), get_x(bottom_right!(area)).saturating_sub(2))); + pos_inc(upper_left!(area), (2, 1)), + set_x( + bottom_right!(area), + get_x(bottom_right!(area)).saturating_sub(2), + ), + ); clear_area(grid, area); create_box(grid, area); // TODO: print into a pager - for (idx, (k, v)) in self.children[self.cursor_pos].get_shortcuts(context).into_iter().enumerate() { - let (x, y) = write_string_to_grid( - &k, - grid, - Color::Byte(29), - Color::Default, - (pos_inc(upper_left!(area), (2, 1 + idx)), set_x(bottom_right!(area), get_x(bottom_right!(area)).saturating_sub(2))), - false, - ); - write_string_to_grid( - &format!("{:?}", v), - grid, - Color::Default, - Color::Default, - ((x + 2, y), set_x(bottom_right!(area), get_x(bottom_right!(area)).saturating_sub(2))), - false, - ); - }; + for (idx, (k, v)) in self.children[self.cursor_pos] + .get_shortcuts(context) + .into_iter() + .enumerate() + { + let (x, y) = write_string_to_grid( + &k, + grid, + Color::Byte(29), + Color::Default, + ( + pos_inc(upper_left!(area), (2, 1 + idx)), + set_x( + bottom_right!(area), + get_x(bottom_right!(area)).saturating_sub(2), + ), + ), + false, + ); + write_string_to_grid( + &format!("{:?}", v), + grid, + Color::Default, + Color::Default, + ( + (x + 2, y), + set_x( + bottom_right!(area), + get_x(bottom_right!(area)).saturating_sub(2), + ), + ), + false, + ); + } context.dirty_areas.push_back(area); } } @@ -959,12 +994,12 @@ impl Component for Tabbed { } } - type EntryIdentifier = Vec; /// Shows selection to user #[derive(Debug, PartialEq)] pub struct Selector { - single_only: bool, /// allow only one selection + single_only: bool, + /// allow only one selection entries: Vec<(EntryIdentifier, bool)>, selected_entry_count: u32, content: CellBuffer, @@ -982,21 +1017,15 @@ impl fmt::Display for Selector { impl Component for Selector { fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) { - eprintln!("drawing"); let (width, height) = self.content.size(); - copy_area_with_break( - grid, - &self.content, - area, - ((0, 0), (width, height)), - ); + copy_area_with_break(grid, &self.content, area, ((0, 0), (width, height))); context.dirty_areas.push_back(area); } fn process_event(&mut self, event: &mut UIEvent, _context: &mut Context) -> bool { let (width, height) = self.content.size(); match event.event_type { UIEventType::Input(Key::Char(' ')) => { - self.entries[self.cursor].1 = ! self.entries[self.cursor].1; + self.entries[self.cursor].1 = !self.entries[self.cursor].1; if self.entries[self.cursor].1 { write_string_to_grid( "x", @@ -1005,7 +1034,7 @@ impl Component for Selector { Color::Default, ((1, self.cursor), (width, self.cursor)), false, - ); + ); } else { write_string_to_grid( " ", @@ -1014,21 +1043,21 @@ impl Component for Selector { Color::Default, ((1, self.cursor), (width, self.cursor)), false, - ); + ); } self.dirty = true; return true; - }, + } UIEventType::Input(Key::Up) if self.cursor > 0 => { self.cursor -= 1; self.dirty = true; return true; - }, + } UIEventType::Input(Key::Down) if self.cursor < height.saturating_sub(1) => { self.cursor += 1; self.dirty = true; return true; - }, + } _ => {} } @@ -1044,10 +1073,18 @@ impl Component for Selector { impl Selector { pub fn new(mut entries: Vec<(EntryIdentifier, String)>, single_only: bool) -> Selector { - let width = entries.iter().max_by_key(|e| e.1.len()).map(|v| v.1.len()).unwrap_or(0) + 4; + let width = entries + .iter() + .max_by_key(|e| e.1.len()) + .map(|v| v.1.len()) + .unwrap_or(0) + + 4; let height = entries.len(); let mut content = CellBuffer::new(width, height, Cell::with_char(' ')); - let identifiers = entries.iter_mut().map(|(id, _)| (std::mem::replace(&mut *id, Vec::new()), false)).collect(); + let identifiers = entries + .iter_mut() + .map(|(id, _)| (std::mem::replace(&mut *id, Vec::new()), false)) + .collect(); for (i, e) in entries.into_iter().enumerate() { write_string_to_grid( &format!("[ ] {}", e.1), @@ -1070,6 +1107,10 @@ impl Selector { } pub fn collect(self) -> Vec { - self.entries.into_iter().filter(|v| v.1).map(|(id, _)| id).collect() + self.entries + .into_iter() + .filter(|v| v.1) + .map(|(id, _)| id) + .collect() } } diff --git a/ui/src/components/utilities/widgets.rs b/ui/src/components/utilities/widgets.rs index ffba1e34..90fc1634 100644 --- a/ui/src/components/utilities/widgets.rs +++ b/ui/src/components/utilities/widgets.rs @@ -17,7 +17,11 @@ impl Default for FormFocus { } pub enum Field { - Text(String, Cursor, Option<(Box Vec + Send>, AutoComplete)>), + Text( + String, + Cursor, + Option<(Box Vec + Send>, AutoComplete)>, + ), Choice(Vec, Cursor), TextArea(String, Cursor), } @@ -42,12 +46,8 @@ impl Default for Field { impl Field { fn as_str(&self) -> &str { match self { - Text(ref s, _, _) => { - s - }, - TextArea(ref s, _) => { - s - }, + Text(ref s, _, _) => s, + TextArea(ref s, _) => s, Choice(ref v, cursor) => { if v.is_empty() { "" @@ -60,48 +60,56 @@ impl Field { pub fn into_string(self) -> String { match self { - Text(s, _, _) => { - s - }, - TextArea(s, _) => { - s - }, - Choice(mut v, cursor) => { - v.remove(cursor) - } + Text(s, _, _) => s, + TextArea(s, _) => s, + Choice(mut v, cursor) => v.remove(cursor), } } - fn draw_cursor(&mut self, grid: &mut CellBuffer, area: Area, secondary_area: Area, context: &mut Context) { + fn draw_cursor( + &mut self, + grid: &mut CellBuffer, + area: Area, + secondary_area: Area, + context: &mut Context, + ) { let upper_left = upper_left!(area); match self { Text(ref term, cursor, auto_complete_fn) => { - change_colors(grid, (pos_inc(upper_left, (*cursor, 0)), (pos_inc(upper_left, (*cursor, 0)))), Color::Default, Color::Byte(248)); - if term.chars().count() <= 2 { return; } + change_colors( + grid, + ( + pos_inc(upper_left, (*cursor, 0)), + (pos_inc(upper_left, (*cursor, 0))), + ), + Color::Default, + Color::Byte(248), + ); + if term.chars().count() <= 2 { + return; + } if let Some((auto_complete_fn, auto_complete)) = auto_complete_fn { let entries = auto_complete_fn(context, term); auto_complete.set_suggestions(entries); auto_complete.draw(grid, secondary_area, context); } - }, - TextArea(_, _) => { - }, - Choice(_, _cursor) => { - } + TextArea(_, _) => {} + Choice(_, _cursor) => {} } } } impl Component for Field { fn draw(&mut self, grid: &mut CellBuffer, area: Area, _context: &mut Context) { - write_string_to_grid( - self.as_str(), - grid, - Color::Default, - Color::Default, - area, - true); + write_string_to_grid( + self.as_str(), + grid, + Color::Default, + Color::Default, + area, + true, + ); } fn process_event(&mut self, event: &mut UIEvent, _context: &mut Context) -> bool { if let Text(ref mut s, ref mut cursor, Some((_, auto_complete))) = self { @@ -112,8 +120,8 @@ impl Component for Field { *cursor = s.chars().count(); return true; } - }, - _ => {}, + } + _ => {} } } @@ -124,74 +132,68 @@ impl Component for Field { } else { return false; } - }, + } UIEventType::InsertInput(Key::Down) => { if let Text(_, _, Some((_, auto_complete))) = self { auto_complete.inc_cursor(); } else { return false; } - }, - UIEventType::InsertInput(Key::Right) => { - match self { - TextArea(ref s, ref mut cursor) | Text(ref s, ref mut cursor, _) => { - if *cursor < s.len() { - *cursor += 1; - } - }, - Choice(ref vec, ref mut cursor) => { - *cursor = if *cursor == vec.len().saturating_sub(1) { - 0 - } else { - *cursor + 1 - }; - } - } - }, - UIEventType::InsertInput(Key::Left) => { - match self { - TextArea(_, ref mut cursor) | Text(_, ref mut cursor, _) => { - if *cursor == 0 { - return false; - } else { - *cursor -= 1; - } - }, - Choice(_, ref mut cursor) => { - if *cursor == 0 { - return false; - } else { - *cursor -= 1; - } - } - } - }, - UIEventType::InsertInput(Key::Char(k)) => { - match self { - Text(ref mut s, ref mut cursor, _) | TextArea(ref mut s, ref mut cursor) => { - s.insert(*cursor, k); + } + UIEventType::InsertInput(Key::Right) => match self { + TextArea(ref s, ref mut cursor) | Text(ref s, ref mut cursor, _) => { + if *cursor < s.len() { *cursor += 1; - }, - _ => {} + } + } + Choice(ref vec, ref mut cursor) => { + *cursor = if *cursor == vec.len().saturating_sub(1) { + 0 + } else { + *cursor + 1 + }; } }, - UIEventType::InsertInput(Key::Backspace) => { - match self { - Text(ref mut s, ref mut cursor, ref mut auto_complete) => { - if *cursor > 0 { - *cursor -= 1; - s.remove(*cursor); - } - auto_complete.as_mut().map(|ac| ac.1.set_suggestions(Vec::new())); - }, - TextArea(ref mut s, ref mut cursor) => { - if *cursor > 0 { - *cursor -= 1; - s.remove(*cursor); - } - }, - _ => {} + UIEventType::InsertInput(Key::Left) => match self { + TextArea(_, ref mut cursor) | Text(_, ref mut cursor, _) => { + if *cursor == 0 { + return false; + } else { + *cursor -= 1; + } } + Choice(_, ref mut cursor) => { + if *cursor == 0 { + return false; + } else { + *cursor -= 1; + } + } + }, + UIEventType::InsertInput(Key::Char(k)) => match self { + Text(ref mut s, ref mut cursor, _) | TextArea(ref mut s, ref mut cursor) => { + s.insert(*cursor, k); + *cursor += 1; + } + _ => {} + }, + UIEventType::InsertInput(Key::Backspace) => match self { + Text(ref mut s, ref mut cursor, ref mut auto_complete) => { + if *cursor > 0 { + *cursor -= 1; + s.remove(*cursor); + } + auto_complete + .as_mut() + .map(|ac| ac.1.set_suggestions(Vec::new())); + } + TextArea(ref mut s, ref mut cursor) => { + if *cursor > 0 { + *cursor -= 1; + s.remove(*cursor); + } + } + _ => {} }, _ => { return false; @@ -212,7 +214,6 @@ impl fmt::Display for Field { } } - #[derive(Debug, Default)] pub struct FormWidget { fields: FnvHashMap, @@ -272,10 +273,20 @@ impl FormWidget { self.layout.push(value.0.clone()); self.fields.insert(value.0, TextArea(value.1, 0)); } - pub fn push_cl(&mut self, value: (String, String, Box Vec + Send>)) { + pub fn push_cl( + &mut self, + value: ( + String, + String, + Box Vec + Send>, + ), + ) { self.field_name_max_length = std::cmp::max(self.field_name_max_length, value.0.len()); self.layout.push(value.0.clone()); - self.fields.insert(value.0, Text(value.1, 0, Some((value.2, AutoComplete::new(Vec::new()))))); + self.fields.insert( + value.0, + Text(value.1, 0, Some((value.2, AutoComplete::new(Vec::new())))), + ); } pub fn push(&mut self, value: (String, String)) { self.field_name_max_length = std::cmp::max(self.field_name_max_length, value.0.len()); @@ -317,33 +328,64 @@ impl Component for FormWidget { grid, Color::Default, Color::Default, - (pos_inc(upper_left, (1, i)), set_y(bottom_right, i + get_y(upper_left))), + ( + pos_inc(upper_left, (1, i)), + set_y(bottom_right, i + get_y(upper_left)), + ), false, - ); + ); /* draw field */ - v.draw(grid, - (pos_inc(upper_left, (self.field_name_max_length + 3, i)), set_y(bottom_right, i + get_y(upper_left))), context); + v.draw( + grid, + ( + pos_inc(upper_left, (self.field_name_max_length + 3, i)), + set_y(bottom_right, i + get_y(upper_left)), + ), + context, + ); /* Highlight if necessary */ - if i == self.cursor { + if i == self.cursor { if self.focus == FormFocus::Fields { - change_colors(grid, (pos_inc(upper_left, (0, i)), set_y(bottom_right, i + get_y(upper_left))), Color::Default, Color::Byte(246)); + change_colors( + grid, + ( + pos_inc(upper_left, (0, i)), + set_y(bottom_right, i + get_y(upper_left)), + ), + Color::Default, + Color::Byte(246), + ); } if self.focus == FormFocus::TextInput { - v.draw_cursor(grid, - (pos_inc(upper_left, (self.field_name_max_length + 3 , i)), - (get_x(upper_left) + self.field_name_max_length + 3, i + get_y(upper_left))), - (pos_inc(upper_left, (self.field_name_max_length + 3 , i + 1)), bottom_right), - context); + v.draw_cursor( + grid, + ( + pos_inc(upper_left, (self.field_name_max_length + 3, i)), + ( + get_x(upper_left) + self.field_name_max_length + 3, + i + get_y(upper_left), + ), + ), + ( + pos_inc(upper_left, (self.field_name_max_length + 3, i + 1)), + bottom_right, + ), + context, + ); } } } if !self.hide_buttons { let length = self.layout.len(); - self.buttons.draw(grid, - (pos_inc(upper_left, (1, length * 2 + 3)), set_y(bottom_right, length * 2 + 3 + get_y(upper_left))), - context); - + self.buttons.draw( + grid, + ( + pos_inc(upper_left, (1, length * 2 + 3)), + set_y(bottom_right, length * 2 + 3 + get_y(upper_left)), + ), + context, + ); } self.dirty = false; context.dirty_areas.push_back(area); @@ -356,43 +398,43 @@ impl Component for FormWidget { match event.event_type { UIEventType::Input(Key::Up) if self.focus == FormFocus::Buttons => { self.focus = FormFocus::Fields; - }, + } UIEventType::InsertInput(Key::Up) if self.focus == FormFocus::TextInput => { let field = self.fields.get_mut(&self.layout[self.cursor]).unwrap(); field.process_event(event, context); - }, + } UIEventType::Input(Key::Up) => { self.cursor = self.cursor.saturating_sub(1); - }, + } UIEventType::InsertInput(Key::Down) if self.focus == FormFocus::TextInput => { let field = self.fields.get_mut(&self.layout[self.cursor]).unwrap(); field.process_event(event, context); - }, + } UIEventType::Input(Key::Down) if self.cursor < self.layout.len().saturating_sub(1) => { self.cursor += 1; - }, + } UIEventType::Input(Key::Down) if self.focus == FormFocus::Fields => { self.focus = FormFocus::Buttons; if self.hide_buttons { self.set_dirty(); return false; } - }, + } UIEventType::InsertInput(Key::Char('\t')) if self.focus == FormFocus::TextInput => { let field = self.fields.get_mut(&self.layout[self.cursor]).unwrap(); field.process_event(event, context); - }, + } UIEventType::Input(Key::Char('\n')) if self.focus == FormFocus::Fields => { self.focus = FormFocus::TextInput; context.replies.push_back(UIEvent { - id: 0, - event_type: UIEventType::ChangeMode(UIMode::Insert), - }); - }, + id: 0, + event_type: UIEventType::ChangeMode(UIMode::Insert), + }); + } UIEventType::InsertInput(Key::Right) if self.focus == FormFocus::TextInput => { let field = self.fields.get_mut(&self.layout[self.cursor]).unwrap(); field.process_event(event, context); - }, + } UIEventType::InsertInput(Key::Left) if self.focus == FormFocus::TextInput => { let field = self.fields.get_mut(&self.layout[self.cursor]).unwrap(); if !field.process_event(event, context) { @@ -402,18 +444,18 @@ impl Component for FormWidget { event_type: UIEventType::ChangeMode(UIMode::Normal), }); } - }, + } UIEventType::ChangeMode(UIMode::Normal) if self.focus == FormFocus::TextInput => { self.focus = FormFocus::Fields; - }, + } UIEventType::InsertInput(Key::Char(_)) if self.focus == FormFocus::TextInput => { let field = self.fields.get_mut(&self.layout[self.cursor]).unwrap(); field.process_event(event, context); - }, + } UIEventType::InsertInput(Key::Backspace) if self.focus == FormFocus::TextInput => { let field = self.fields.get_mut(&self.layout[self.cursor]).unwrap(); field.process_event(event, context); - }, + } _ => { return false; } @@ -429,9 +471,11 @@ impl Component for FormWidget { } } - #[derive(Debug, Default)] -pub struct ButtonWidget where T: std::fmt::Debug + Default + Send{ +pub struct ButtonWidget +where + T: std::fmt::Debug + Default + Send, +{ buttons: FnvHashMap, layout: Vec, @@ -439,13 +483,19 @@ pub struct ButtonWidget where T: std::fmt::Debug + Default + Send{ cursor: usize, } -impl fmt::Display for ButtonWidget where T: std::fmt::Debug + Default + Send { +impl fmt::Display for ButtonWidget +where + T: std::fmt::Debug + Default + Send, +{ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { Display::fmt("", f) } } -impl ButtonWidget where T: std::fmt::Debug + Default + Send { +impl ButtonWidget +where + T: std::fmt::Debug + Default + Send, +{ pub fn new(init_val: (String, T)) -> ButtonWidget { ButtonWidget { layout: vec![init_val.0.clone()], @@ -465,39 +515,52 @@ impl ButtonWidget where T: std::fmt::Debug + Default + Send { } } - -impl Component for ButtonWidget where T: std::fmt::Debug + Default + Send { +impl Component for ButtonWidget +where + T: std::fmt::Debug + Default + Send, +{ fn draw(&mut self, grid: &mut CellBuffer, area: Area, _context: &mut Context) { let upper_left = upper_left!(area); - let mut len = 0; + let mut len = 0; for (i, k) in self.layout.iter().enumerate() { let cur_len = k.len(); write_string_to_grid( k.as_str(), grid, Color::Default, - if i == self.cursor { Color::Byte(246) } else { Color::Default }, - (pos_inc(upper_left, (len, 0)), pos_inc(upper_left, (cur_len + len, 0))), + if i == self.cursor { + Color::Byte(246) + } else { + Color::Default + }, + ( + pos_inc(upper_left, (len, 0)), + pos_inc(upper_left, (cur_len + len, 0)), + ), false, - ); + ); len += cur_len + 3; } } fn process_event(&mut self, event: &mut UIEvent, _context: &mut Context) -> bool { match event.event_type { UIEventType::Input(Key::Char('\n')) => { - self.result = Some(self.buttons.remove(&self.layout[self.cursor]).unwrap_or_default()); + self.result = Some( + self.buttons + .remove(&self.layout[self.cursor]) + .unwrap_or_default(), + ); return true; - }, + } UIEventType::Input(Key::Left) => { self.cursor = self.cursor.saturating_sub(1); return true; - }, + } UIEventType::Input(Key::Right) if self.cursor < self.layout.len().saturating_sub(1) => { self.cursor += 1; return true; - }, + } _ => {} } @@ -509,8 +572,6 @@ impl Component for ButtonWidget where T: std::fmt::Debug + Default + Send fn set_dirty(&mut self) {} } - - #[derive(Debug, PartialEq)] pub struct AutoComplete { entries: Vec, @@ -528,7 +589,9 @@ impl fmt::Display for AutoComplete { impl Component for AutoComplete { fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) { - if self.entries.is_empty() { return; }; + if self.entries.is_empty() { + return; + }; let upper_left = upper_left!(area); self.dirty = false; @@ -540,7 +603,15 @@ impl Component for AutoComplete { ((0, 0), (width.saturating_sub(1), height.saturating_sub(1))), ); /* Highlight cursor */ - change_colors(grid, (pos_inc(upper_left, (0, self.cursor)), pos_inc(upper_left, (width.saturating_sub(1), self.cursor))), Color::Default, Color::Byte(246)); + change_colors( + grid, + ( + pos_inc(upper_left, (0, self.cursor)), + pos_inc(upper_left, (width.saturating_sub(1), self.cursor)), + ), + Color::Default, + Color::Byte(246), + ); context.dirty_areas.push_back(area); } fn process_event(&mut self, event: &mut UIEvent, _context: &mut Context) -> bool { @@ -568,10 +639,14 @@ impl AutoComplete { pub fn set_suggestions(&mut self, entries: Vec) { if entries.len() == self.entries.len() && entries == self.entries { - return; + return; } - let mut content = CellBuffer::new(entries.iter().map(|e| e.len()).max().unwrap_or(0) + 1, entries.len(), Cell::with_style(Color::Byte(23), Color::Byte(7), Attr::Default)); + let mut content = CellBuffer::new( + entries.iter().map(|e| e.len()).max().unwrap_or(0) + 1, + entries.len(), + Cell::with_style(Color::Byte(23), Color::Byte(7), Attr::Default), + ); let width = content.cols(); for (i, e) in entries.iter().enumerate() { let (x, _) = write_string_to_grid( @@ -581,7 +656,7 @@ impl AutoComplete { Color::Byte(7), ((0, i), (width - 1, i)), false, - ); + ); write_string_to_grid( "▒", &mut content, @@ -589,15 +664,23 @@ impl AutoComplete { Color::Byte(7), ((width - 1, i), (width - 1, i)), false, - ); + ); } self.content = content; self.entries = entries; self.cursor = 0; } - pub fn inc_cursor(&mut self) { if self.cursor < self.entries.len().saturating_sub(1) { self.cursor += 1; self.set_dirty(); } } - pub fn dec_cursor(&mut self) { self.cursor = self.cursor.saturating_sub(1); self.set_dirty(); } + pub fn inc_cursor(&mut self) { + if self.cursor < self.entries.len().saturating_sub(1) { + self.cursor += 1; + self.set_dirty(); + } + } + pub fn dec_cursor(&mut self) { + self.cursor = self.cursor.saturating_sub(1); + self.set_dirty(); + } pub fn get_suggestion(&mut self) -> Option { if self.entries.is_empty() { diff --git a/ui/src/conf.rs b/ui/src/conf.rs index 93b258e2..564540bf 100644 --- a/ui/src/conf.rs +++ b/ui/src/conf.rs @@ -19,25 +19,24 @@ * along with meli. If not, see . */ +extern crate bincode; extern crate config; extern crate serde; extern crate xdg; -extern crate bincode; -pub mod pager; pub mod notifications; +pub mod pager; pub mod shortcuts; pub mod accounts; pub use self::accounts::Account; -pub use self::shortcuts::*; use self::config::{Config, File, FileFormat}; +pub use self::shortcuts::*; - +use self::notifications::NotificationsSettings; use melib::conf::AccountSettings; use melib::error::*; use pager::PagerSettings; -use self::notifications::NotificationsSettings; use self::serde::{de, Deserialize, Deserializer}; use std::collections::HashMap; @@ -194,7 +193,6 @@ impl Settings { } } - #[derive(Copy, Debug, Clone, Deserialize)] pub enum IndexStyle { Plain, @@ -209,7 +207,8 @@ impl Default for IndexStyle { } fn index_from_str<'de, D>(deserializer: D) -> std::result::Result - where D: Deserializer<'de> +where + D: Deserializer<'de>, { let s = ::deserialize(deserializer)?; match s.as_str() { diff --git a/ui/src/conf/accounts.rs b/ui/src/conf/accounts.rs index 43c2698a..ecc9d5cc 100644 --- a/ui/src/conf/accounts.rs +++ b/ui/src/conf/accounts.rs @@ -58,7 +58,6 @@ pub struct Account { pub(crate) workers: Vec, - pub(crate) settings: AccountConf, pub(crate) runtime_settings: AccountConf, pub(crate) backend: Box, @@ -68,9 +67,7 @@ pub struct Account { impl Drop for Account { fn drop(&mut self) { //TODO: Avoid panics - let data_dir = - xdg::BaseDirectories::with_profile("meli", &self.name) - .unwrap(); + let data_dir = xdg::BaseDirectories::with_profile("meli", &self.name).unwrap(); if let Ok(data) = data_dir.place_data_file("addressbook") { /* place result in cache directory */ let f = match fs::File::create(data) { @@ -99,9 +96,7 @@ impl Account { folders.push(None); workers.push(Account::new_worker(f, &mut backend, notify_fn.clone())); } - let data_dir = - xdg::BaseDirectories::with_profile("meli", &name) - .unwrap(); + let data_dir = xdg::BaseDirectories::with_profile("meli", &name).unwrap(); let address_book = if let Ok(data) = data_dir.place_data_file("addressbook") { if data.exists() { let reader = io::BufReader::new(fs::File::open(data).unwrap()); @@ -229,14 +224,14 @@ impl Account { if self.sent_folder.is_some() && self.sent_folder.unwrap() == index { self.folders[index] = Some(mailbox); /* Add our replies to other folders */ - for id in (0..self.folders.len()).filter(|i| *i != index) { - self.add_replies_to_folder(id); - } + for id in (0..self.folders.len()).filter(|i| *i != index) { + self.add_replies_to_folder(id); + } } else { - self.folders[index] = Some(mailbox); - self.add_replies_to_folder(index); + self.folders[index] = Some(mailbox); + self.add_replies_to_folder(index); }; - */ + */ } /* @@ -303,7 +298,8 @@ impl Account { } pub fn save_draft(&self, draft: Draft) -> Result<()> { - self.backend.save(draft.to_string()?, &self.settings.conf.draft_folder) + self.backend + .save(draft.to_string()?, &self.settings.conf.draft_folder) } } diff --git a/ui/src/conf/shortcuts.rs b/ui/src/conf/shortcuts.rs index ba93c71e..4b0e6f34 100644 --- a/ui/src/conf/shortcuts.rs +++ b/ui/src/conf/shortcuts.rs @@ -31,14 +31,14 @@ macro_rules! key_values { } pub fn key_values(&self) -> FnvHashMap<&'static str, &Key> { let mut map: FnvHashMap<&'static str, &Key> = Default::default(); - $(map.insert(stringify!($fname),&(self.$fname));)* + $(map.insert(stringify!($fname),&(self.$fname));)* map } } } } -key_values!{ "compact-listing", derive (Debug, Clone, Deserialize) : +key_values! { "compact-listing", derive (Debug, Clone, Deserialize) : pub struct CompactListingShortcuts { open_thread: Key |> "Open thread.", exit_thread: Key |> "Exit thread view.", @@ -52,24 +52,23 @@ pub struct CompactListingShortcuts { } } - impl Default for CompactListingShortcuts { fn default() -> Self { - CompactListingShortcuts { + CompactListingShortcuts { open_thread: Key::Char('\n'), exit_thread: Key::Char('i'), prev_page: Key::PageUp, next_page: Key::PageDown, prev_folder: Key::Char('J'), next_folder: Key::Char('K'), - prev_account:Key::Char('h'), - next_account:Key::Char('l'), + prev_account: Key::Char('h'), + next_account: Key::Char('l'), new_mail: Key::Char('m'), } } } -key_values!{ "contact-list", derive (Debug, Clone, Deserialize) : +key_values! { "contact-list", derive (Debug, Clone, Deserialize) : pub struct ContactListShortcuts { create_contact: Key |> "Create new contact.", edit_contact: Key |> "Edit contact under cursor." @@ -78,14 +77,14 @@ pub struct ContactListShortcuts { impl Default for ContactListShortcuts { fn default() -> Self { - ContactListShortcuts { + ContactListShortcuts { create_contact: Key::Char('c'), edit_contact: Key::Char('e'), } } } -key_values!{ "pager", derive (Debug, Clone, Deserialize) : +key_values! { "pager", derive (Debug, Clone, Deserialize) : pub struct PagerShortcuts { scroll_up: Key |> "Scroll up pager.", scroll_down: Key |> "Scroll down pager.", diff --git a/ui/src/execute/actions.rs b/ui/src/execute/actions.rs index a6b08f36..ba440903 100644 --- a/ui/src/execute/actions.rs +++ b/ui/src/execute/actions.rs @@ -23,20 +23,20 @@ * User actions that need to be handled by the UI */ -pub use melib::mailbox::{SortField, SortOrder}; use components::Component; +pub use melib::mailbox::{SortField, SortOrder}; extern crate uuid; use uuid::Uuid; -#[derive(Debug, )] +#[derive(Debug)] pub enum ListingAction { SetPlain, SetThreaded, SetCompact, } -#[derive(Debug, )] +#[derive(Debug)] pub enum TabAction { TabOpen(Option>), NewDraft, @@ -45,7 +45,7 @@ pub enum TabAction { Kill(Uuid), } -#[derive(Debug, )] +#[derive(Debug)] pub enum Action { Listing(ListingAction), ViewMailbox(usize), diff --git a/ui/src/state.rs b/ui/src/state.rs index 8a672415..0db6e1ed 100644 --- a/ui/src/state.rs +++ b/ui/src/state.rs @@ -21,12 +21,12 @@ /*! The application's state. - The UI crate has an Entity-Component-System design. The System part, is also the application's state, so they're both merged in the `State` struct. +The UI crate has an Entity-Component-System design. The System part, is also the application's state, so they're both merged in the `State` struct. - `State` owns all the Entities of the UI, which are currently plain Containers for `Component`s. In the application's main event loop, input is handed to the state in the form of `UIEvent` objects which traverse the entity graph. Components decide to handle each input or not. +`State` owns all the Entities of the UI, which are currently plain Containers for `Component`s. In the application's main event loop, input is handed to the state in the form of `UIEvent` objects which traverse the entity graph. Components decide to handle each input or not. - Input is received in the main loop from threads which listen on the stdin for user input, observe folders for file changes etc. The relevant struct is `ThreadEvent`. - */ +Input is received in the main loop from threads which listen on the stdin for user input, observe folders for file changes etc. The relevant struct is `ThreadEvent`. +*/ use super::*; use melib::backends::{FolderHash, NotifyFn}; @@ -64,7 +64,8 @@ impl InputHandler { }, &rx, ) - }).unwrap(); + }) + .unwrap(); } fn kill(&self) { self.tx.send(false); @@ -141,7 +142,8 @@ impl Drop for State { cursor::Goto(1, 1), cursor::Show, BracketModeEnd, - ).unwrap(); + ) + .unwrap(); self.flush(); } } @@ -187,7 +189,8 @@ impl State { sender.send(ThreadEvent::UIEvent(UIEventType::StartupCheck)) })), ) - }).collect(); + }) + .collect(); accounts.sort_by(|a, b| a.name().cmp(&b.name())); let mut s = State { cols, @@ -235,7 +238,8 @@ impl State { cursor::Hide, clear::All, cursor::Goto(1, 1) - ).unwrap(); + ) + .unwrap(); s.flush(); eprintln!("DEBUG: inserting mailbox hashes:"); for (x, account) in s.context.accounts.iter_mut().enumerate() { @@ -305,7 +309,8 @@ impl State { "{}{}", termion::screen::ToMainScreen, cursor::Show - ).unwrap(); + ) + .unwrap(); self.flush(); self.stdout = None; self.context.input.kill(); @@ -322,7 +327,8 @@ impl State { cursor::Hide, clear::All, cursor::Goto(1, 1) - ).unwrap(); + ) + .unwrap(); self.flush(); } @@ -381,7 +387,8 @@ impl State { self.stdout(), "{}", cursor::Goto(get_x(upper_left) as u16 + 1, (y + 1) as u16) - ).unwrap(); + ) + .unwrap(); for x in get_x(upper_left)..=get_x(bottom_right) { let c = self.grid[(x, y)]; @@ -397,14 +404,16 @@ impl State { self.stdout(), "{}", termion::color::Bg(termion::color::Reset) - ).unwrap(); + ) + .unwrap(); } if c.fg() != Color::Default { write!( self.stdout(), "{}", termion::color::Fg(termion::color::Reset) - ).unwrap(); + ) + .unwrap(); } } } @@ -473,12 +482,12 @@ impl State { self.flush(); } return; - }, + } UIEventType::ChangeMode(m) => { self.context .sender .send(ThreadEvent::UIEvent(UIEventType::ChangeMode(m))); - }, + } _ => {} } /* inform each entity */ diff --git a/ui/src/terminal.rs b/ui/src/terminal.rs index 4121e932..4aae53c1 100644 --- a/ui/src/terminal.rs +++ b/ui/src/terminal.rs @@ -19,8 +19,8 @@ * along with meli. If not, see . */ extern crate serde; -use self::serde::{de, Deserialize, Deserializer, }; use self::serde::de::Visitor; +use self::serde::{de, Deserialize, Deserializer}; #[macro_use] mod position; diff --git a/ui/src/terminal/cells.rs b/ui/src/terminal/cells.rs index fff03d65..845900a1 100644 --- a/ui/src/terminal/cells.rs +++ b/ui/src/terminal/cells.rs @@ -20,9 +20,9 @@ */ /*! - Define a (x, y) point in the terminal display as a holder of a character, foreground/background - colors and attributes. - */ + Define a (x, y) point in the terminal display as a holder of a character, foreground/background + colors and attributes. +*/ use super::position::*; use std::convert::From; use std::fmt; @@ -183,7 +183,6 @@ impl CellBuffer { self.cols = 0; self.rows = 0; } - } impl HasSize for CellBuffer { @@ -580,7 +579,7 @@ pub fn copy_area_with_break( ); return upper_left!(dest); } - + if grid_src.is_empty() || grid_dest.is_empty() { return upper_left!(dest); } @@ -755,7 +754,10 @@ pub fn word_break_string(mut s: &str, width: usize) -> Vec<&str> { } } if s.len() > width { - if let Some(next_idx) = s.as_bytes()[..width].iter().rposition(u8::is_ascii_whitespace) { + if let Some(next_idx) = s.as_bytes()[..width] + .iter() + .rposition(u8::is_ascii_whitespace) + { ret.push(&s[..next_idx]); s = &s[next_idx + 1..]; } else { @@ -766,7 +768,6 @@ pub fn word_break_string(mut s: &str, width: usize) -> Vec<&str> { ret.push(s); break; } - } ret diff --git a/ui/src/terminal/keys.rs b/ui/src/terminal/keys.rs index e3db0176..548ce691 100644 --- a/ui/src/terminal/keys.rs +++ b/ui/src/terminal/keys.rs @@ -203,44 +203,43 @@ pub const BRACKET_PASTE_END: &[u8] = b"\x1B[201~"; const FIELDS: &[&str] = &[]; - impl<'de> Deserialize<'de> for Key { fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct KeyVisitor; + where + D: Deserializer<'de>, + { + struct KeyVisitor; - impl<'de> Visitor<'de> for KeyVisitor { - type Value = Key; + impl<'de> Visitor<'de> for KeyVisitor { + type Value = Key; - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("`secs` or `nanos`") - } - - fn visit_str(self, value: &str) -> Result - where - E: de::Error, - { - match value { - "Backspace" => Ok(Key::Backspace), - "Left" => Ok(Key::Left), - "Right" => Ok(Key::Right), - "Up" => Ok(Key::Up), - "Down" => Ok(Key::Down), - "Home" => Ok(Key::Home), - "End" => Ok(Key::End), - "PageUp" => Ok(Key::PageUp), - "PageDown" => Ok(Key::PageDown), - "Delete" => Ok(Key::Delete), - "Insert" => Ok(Key::Insert), - "Esc" => Ok(Key::Esc), - ref s if s.len() == 1 => Ok(Key::Char(s.chars().nth(0).unwrap())), - _ => Err(de::Error::unknown_field(value, FIELDS)), - } - } + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("`secs` or `nanos`") } - deserializer.deserialize_identifier(KeyVisitor) + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + match value { + "Backspace" => Ok(Key::Backspace), + "Left" => Ok(Key::Left), + "Right" => Ok(Key::Right), + "Up" => Ok(Key::Up), + "Down" => Ok(Key::Down), + "Home" => Ok(Key::Home), + "End" => Ok(Key::End), + "PageUp" => Ok(Key::PageUp), + "PageDown" => Ok(Key::PageDown), + "Delete" => Ok(Key::Delete), + "Insert" => Ok(Key::Insert), + "Esc" => Ok(Key::Esc), + ref s if s.len() == 1 => Ok(Key::Char(s.chars().nth(0).unwrap())), + _ => Err(de::Error::unknown_field(value, FIELDS)), + } + } } + + deserializer.deserialize_identifier(KeyVisitor) + } } diff --git a/ui/src/terminal/position.rs b/ui/src/terminal/position.rs index 96036053..d592f7c9 100644 --- a/ui/src/terminal/position.rs +++ b/ui/src/terminal/position.rs @@ -20,10 +20,10 @@ */ /*! - Simple type definitions and macro helper for a (x,y) position on the terminal and the areas they define. +Simple type definitions and macro helper for a (x,y) position on the terminal and the areas they define. - An `Area` consists of two points: the upper left and bottom right corners. - */ +An `Area` consists of two points: the upper left and bottom right corners. +*/ /// A `(x, y)` position on screen. pub type Pos = (usize, usize); diff --git a/ui/src/terminal/wcwidth.rs b/ui/src/terminal/wcwidth.rs index 155dfb4c..1e559851 100644 --- a/ui/src/terminal/wcwidth.rs +++ b/ui/src/terminal/wcwidth.rs @@ -206,7 +206,7 @@ fn wcwidth(ucs: WChar) -> Option { (ucs >= 0xff00 && ucs <= 0xff5f) || /* Fullwidth Forms */ (ucs >= 0xffe0 && ucs <= 0xffe6) || (ucs >= 0x20000 && ucs <= 0x2ffff)) - ) + ), ); }