diff --git a/meli.conf.5 b/meli.conf.5 index 7c7b83e5..e6098e07 100644 --- a/meli.conf.5 +++ b/meli.conf.5 @@ -41,7 +41,7 @@ Newline means LF (0x0A) or CRLF (0x0D 0x0A). .Pp Refer to TOML documentation for valid TOML syntax. -Thought not valid TOML syntax, +Thought not part of TOML syntax, .Nm can have nested configuration files by using the following include directive, which though starting with .Em \&# @@ -49,6 +49,9 @@ is not a comment: .Bd -literal #include "/path/to/file" .Ed + +The accepted regular expression is +.Li ^\es*include\es*\&\\&\e"(\e\e.|[^\e"])+\e"\es*$ .Sh SECTIONS The top level sections of the config are accounts, shortcuts, notifications, pager, composing, pgp, terminal. .Pp diff --git a/sample-config b/sample-config index 3353ddda..dc862120 100644 --- a/sample-config +++ b/sample-config @@ -1,6 +1,13 @@ # Look into meli.conf(5) for all valid configuration options, their # descriptions and default values # +# The syntax for including other configuration files is enclosed in `: +#`# include "account_one"` +#`# include "./account_two"` +#`# include "/home/absolute/path/to/shortcuts/config"` +# That is, the # in the beginning is part of the include directive. +# +# # # Setting up a Maildir account #[accounts.account-name] diff --git a/ui/src/components/mail/compose.rs b/ui/src/components/mail/compose.rs index 793dcd20..5edbd939 100644 --- a/ui/src/components/mail/compose.rs +++ b/ui/src/components/mail/compose.rs @@ -134,13 +134,13 @@ impl fmt::Display for Composer { if self.reply_context.is_some() { write!(f, "reply: {:8}", self.draft.headers()["Subject"]) } else { - write!(f, "compose") + write!(f, "composing") } } } impl Composer { - const DESCRIPTION: &'static str = "compose"; + const DESCRIPTION: &'static str = "composing"; pub fn new(account_cursor: usize) -> Self { Composer { account_cursor, @@ -610,9 +610,8 @@ impl Component for Composer { } fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool { + let shortcuts = self.get_shortcuts(context); match (&mut self.mode, &mut self.reply_context, &event) { - // don't pass Reply command to thread view in reply_context - (_, Some(_), UIEvent::Input(Key::Char('R'))) => {} (ViewMode::Edit, _, _) => { if self.pager.process_event(event, context) { return true; @@ -749,7 +748,10 @@ impl Component for Composer { self.cursor = Cursor::Body; self.dirty = true; } - UIEvent::Input(Key::Char('s')) if self.mode.is_edit() => { + UIEvent::Input(ref key) + if shortcut!(key == shortcuts[Self::DESCRIPTION]["send_mail"]) + && self.mode.is_edit() => + { self.update_draft(); self.mode = ViewMode::Send(Selector::new( "send mail?", @@ -862,7 +864,10 @@ impl Component for Composer { self.set_dirty(); return true; } - UIEvent::Input(Key::Char('e')) if self.embed.is_some() => { + UIEvent::Input(ref key) + if self.embed.is_some() + && shortcut!(key == shortcuts[Self::DESCRIPTION]["edit_mail"]) => + { self.embed.as_ref().unwrap().lock().unwrap().wake_up(); match self.embed.take() { Some(EmbedStatus::Running(e, f)) | Some(EmbedStatus::Stopped(e, f)) => { @@ -877,7 +882,10 @@ impl Component for Composer { self.set_dirty(); return true; } - UIEvent::Input(Key::Char('e')) if self.mode.is_edit() => { + UIEvent::Input(ref key) + if self.mode.is_edit() + && shortcut!(key == shortcuts[Self::DESCRIPTION]["edit_mail"]) => + { /* Edit draft in $EDITOR */ let settings = &context.settings; let editor = if let Some(editor_cmd) = settings.composing.editor_cmd.as_ref() { diff --git a/ui/src/components/mail/view.rs b/ui/src/components/mail/view.rs index 73d18a42..99b4ea50 100644 --- a/ui/src/components/mail/view.rs +++ b/ui/src/components/mail/view.rs @@ -515,7 +515,7 @@ impl Component for MailView { } self.force_draw_headers = false; - clear_area(grid, (set_y(upper_left, y), set_y(bottom_right, y + 1))); + clear_area(grid, (set_y(upper_left, y), set_y(bottom_right, y))); context .dirty_areas .push_back((upper_left, set_y(bottom_right, y + 3))); diff --git a/ui/src/conf.rs b/ui/src/conf.rs index 29de1cf7..84e27ef9 100644 --- a/ui/src/conf.rs +++ b/ui/src/conf.rs @@ -595,8 +595,8 @@ mod pp { error::{MeliError, Result}, parsec::*, }; - use std::borrow::Cow; use std::io::Read; + use std::path::{Path, PathBuf}; fn include_directive<'a>() -> impl Parser<'a, Option<&'a str>> { move |input: &'a str| { @@ -655,9 +655,9 @@ mod pp { } } - fn pp_helper(path: &str, level: u8) -> Result> { + fn pp_helper(path: &Path, level: u8) -> Result { if level > 7 { - return Err(MeliError::new(format!("Maximum recursion limit reached while unfolding include directives in {}. Have you included a config file within itself?", path))); + return Err(MeliError::new(format!("Maximum recursion limit reached while unfolding include directives in {}. Have you included a config file within itself?", path.display()))); } let mut contents = String::new(); let mut file = std::fs::File::open(path)?; @@ -668,7 +668,9 @@ mod pp { if let (_, Some(path)) = include_directive().parse(l).map_err(|l| { MeliError::new(format!( "Malformed include directive in line {} of file {}: {}", - i, path, l + i, + path.display(), + l )) })? { includes.push(path); @@ -676,17 +678,39 @@ mod pp { } if includes.is_empty() { - Ok(Cow::from(contents)) + Ok(contents) } else { let mut ret = String::with_capacity(contents.len()); - for path in includes { - ret.extend(pp_helper(path, level + 1)?.chars()); + for sub_path in includes { + let p = &Path::new(sub_path); + debug!(p); + let p_buf = if p.is_relative() { + /* We checked that path is ok above so we can do unwrap here */ + debug!(path); + let prefix = path.parent().unwrap(); + debug!(prefix); + prefix.join(p) + } else { + p.to_path_buf() + }; + + ret.extend(pp_helper(&p_buf, level + 1)?.chars()); } ret.extend(contents.chars()); - Ok(Cow::from(ret)) + Ok(ret) } } - pub fn pp(path: &str) -> Result> { - pp_helper(path, 0) + + pub fn pp(path: &str) -> Result { + let p = &Path::new(path); + let p_buf: PathBuf = if p.is_relative() { + p.canonicalize()? + } else { + p.to_path_buf() + }; + + let ret = pp_helper(&p_buf, 0); + drop(p_buf); + ret } }