Browse Source

Add reload-config command

Closes #84 Add "reload configuration" command
jmap-eventsource
Manos Pitsidianakis 11 months ago
parent
commit
48e7a493a9
Signed by untrusted user: epilys GPG Key ID: 73627C2F690DF710
  1. 9
      docs/meli.1
  2. 12
      src/command.rs
  3. 2
      src/command/actions.rs
  4. 18
      src/components/contacts.rs
  5. 8
      src/components/contacts/contact_list.rs
  6. 3
      src/components/mail/compose.rs
  7. 9
      src/components/mail/listing.rs
  8. 37
      src/components/mail/listing/compact.rs
  9. 28
      src/components/mail/listing/conversations.rs
  10. 35
      src/components/mail/listing/plain.rs
  11. 33
      src/components/mail/listing/thread.rs
  12. 4
      src/components/mail/status.rs
  13. 4
      src/components/mail/view.rs
  14. 23
      src/components/utilities.rs
  15. 110
      src/components/utilities/dialogs.rs
  16. 4
      src/components/utilities/pager.rs
  17. 33
      src/state.rs
  18. 3
      src/types.rs

9
docs/meli.1

@ -488,6 +488,15 @@ to
.It Cm printenv Ar KEY
print environment variable
.Ar KEY
.It Cm quit
Quits
.Nm Ns
\&.
.It Cm reload-config
Reloads configuration but only if account configuration is unchanged.
Useful if you want to reload some settings without restarting
.Nm Ns
\&.
.El
.Sh SHORTCUTS
See

12
src/command.rs

@ -790,6 +790,17 @@ Alternatives(&[to_stream!(One(Literal("add-attachment")), One(Filepath)), to_str
Ok((input, Quit))
}
)
},
{ tags: ["reload-config"],
desc: "reload configuration file",
tokens: &[One(Literal("reload-config"))],
parser:(
fn reload_config(input: &[u8]) -> IResult<&[u8], Action> {
let (input, _) = tag("reload-config")(input.trim())?;
let (input, _) = eof(input.trim())?;
Ok((input, ReloadConfiguration))
}
)
}
]);
@ -883,6 +894,7 @@ pub fn parse_command(input: &[u8]) -> Result<Action, MeliError> {
account_action,
print_setting,
toggle_mouse,
reload_config,
quit,
))(input)
.map(|(_, v)| v)

2
src/command/actions.rs

@ -121,6 +121,7 @@ pub enum Action {
Mailbox(AccountName, MailboxOperation),
AccountAction(AccountName, AccountAction),
PrintSetting(String),
ReloadConfiguration,
ToggleMouse,
Quit,
}
@ -143,6 +144,7 @@ impl Action {
Action::PrintSetting(_) => false,
Action::ToggleMouse => false,
Action::Quit => true,
Action::ReloadConfiguration => false,
}
}
}

18
src/components/contacts.rs

@ -59,13 +59,6 @@ impl fmt::Display for ContactManager {
impl ContactManager {
fn new(context: &Context) -> Self {
let theme_default: ThemeAttribute = crate::conf::value(context, "theme_default");
let default_cell = {
let mut ret = Cell::with_char(' ');
ret.set_fg(theme_default.fg)
.set_bg(theme_default.bg)
.set_attrs(theme_default.attrs);
ret
};
ContactManager {
id: Uuid::nil(),
parent_id: Uuid::nil(),
@ -73,7 +66,7 @@ impl ContactManager {
mode: ViewMode::Edit,
form: FormWidget::default(),
account_pos: 0,
content: CellBuffer::new(100, 1, default_cell),
content: CellBuffer::new_with_context(100, 1, None, context),
theme_default,
dirty: true,
has_changes: false,
@ -189,6 +182,15 @@ impl Component for ContactManager {
}
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
match event {
UIEvent::ConfigReload { old_settings: _ } => {
self.theme_default = crate::conf::value(context, "theme_default");
self.content = CellBuffer::new_with_context(100, 1, None, context);
self.initialized = false;
self.set_dirty(true);
}
_ => {}
}
match self.mode {
ViewMode::Discard(ref mut selector) => {
if selector.process_event(event, context) {

8
src/components/contacts/contact_list.rs

@ -596,6 +596,14 @@ impl Component for ContactList {
}
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
if let UIEvent::ConfigReload { old_settings: _ } = event {
self.theme_default = crate::conf::value(context, "theme_default");
self.initialized = false;
self.sidebar_divider = context.settings.listing.sidebar_divider;
self.sidebar_divider_theme = conf::value(context, "mail.sidebar_divider");
self.set_dirty(true);
}
if let Some(ref mut v) = self.view {
if v.process_event(event, context) {
return true;

3
src/components/mail/compose.rs

@ -1177,6 +1177,9 @@ impl Component for Composer {
}
match *event {
UIEvent::ConfigReload { old_settings: _ } => {
self.set_dirty(true);
}
UIEvent::Resize => {
self.set_dirty(true);
}

9
src/components/mail/listing.rs

@ -613,6 +613,15 @@ impl Component for Listing {
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
match event {
UIEvent::ConfigReload { old_settings: _ } => {
self.theme_default = crate::conf::value(context, "theme_default");
let account_hash = context.accounts[self.cursor_pos.0].hash();
self.sidebar_divider =
*account_settings!(context[account_hash].listing.sidebar_divider);
self.sidebar_divider_theme = conf::value(context, "mail.sidebar_divider");
self.menu_content = CellBuffer::new_with_context(0, 0, None, context);
self.set_dirty(true);
}
UIEvent::Timer(n) if *n == self.menu_scrollbar_show_timer.id() => {
if self.show_menu_scrollbar == ShowMenuScrollbar::True {
self.show_menu_scrollbar = ShowMenuScrollbar::False;

37
src/components/mail/listing/compact.rs

@ -1691,6 +1691,43 @@ impl Component for CompactListing {
}
}
match *event {
UIEvent::ConfigReload { old_settings: _ } => {
self.color_cache = ColorCache {
even_unseen: crate::conf::value(context, "mail.listing.compact.even_unseen"),
even_selected: crate::conf::value(
context,
"mail.listing.compact.even_selected",
),
even_highlighted: crate::conf::value(
context,
"mail.listing.compact.even_highlighted",
),
odd_unseen: crate::conf::value(context, "mail.listing.compact.odd_unseen"),
odd_selected: crate::conf::value(context, "mail.listing.compact.odd_selected"),
odd_highlighted: crate::conf::value(
context,
"mail.listing.compact.odd_highlighted",
),
even: crate::conf::value(context, "mail.listing.compact.even"),
odd: crate::conf::value(context, "mail.listing.compact.odd"),
attachment_flag: crate::conf::value(context, "mail.listing.attachment_flag"),
thread_snooze_flag: crate::conf::value(
context,
"mail.listing.thread_snooze_flag",
),
tag_default: crate::conf::value(context, "mail.listing.tag_default"),
theme_default: crate::conf::value(context, "theme_default"),
..self.color_cache
};
if !context.settings.terminal.use_color() {
self.color_cache.highlighted.attrs |= Attr::REVERSE;
self.color_cache.tag_default.attrs |= Attr::REVERSE;
self.color_cache.even_highlighted.attrs |= Attr::REVERSE;
self.color_cache.odd_highlighted.attrs |= Attr::REVERSE;
}
self.refresh_mailbox(context, true);
self.set_dirty(true);
}
UIEvent::MailboxUpdate((ref idxa, ref idxf))
if (*idxa, *idxf) == (self.new_cursor_pos.0, self.cursor_pos.1) =>
{

28
src/components/mail/listing/conversations.rs

@ -1616,6 +1616,34 @@ impl Component for ConversationsListing {
}
}
match *event {
UIEvent::ConfigReload { old_settings: _ } => {
self.color_cache = ColorCache {
theme_default: crate::conf::value(context, "mail.listing.conversations"),
subject: crate::conf::value(context, "mail.listing.conversations.subject"),
from: crate::conf::value(context, "mail.listing.conversations.from"),
date: crate::conf::value(context, "mail.listing.conversations.date"),
selected: crate::conf::value(context, "mail.listing.conversations.selected"),
unseen: crate::conf::value(context, "mail.listing.conversations.unseen"),
highlighted: crate::conf::value(
context,
"mail.listing.conversations.highlighted",
),
attachment_flag: crate::conf::value(context, "mail.listing.attachment_flag"),
thread_snooze_flag: crate::conf::value(
context,
"mail.listing.thread_snooze_flag",
),
tag_default: crate::conf::value(context, "mail.listing.tag_default"),
..self.color_cache
};
if !context.settings.terminal.use_color() {
self.color_cache.highlighted.attrs |= Attr::REVERSE;
self.color_cache.tag_default.attrs |= Attr::REVERSE;
}
self.refresh_mailbox(context, true);
self.set_dirty(true);
}
UIEvent::MailboxUpdate((ref idxa, ref idxf))
if (*idxa, *idxf) == (self.new_cursor_pos.0, self.cursor_pos.1) =>
{

35
src/components/mail/listing/plain.rs

@ -1215,6 +1215,41 @@ impl Component for PlainListing {
}
}
match *event {
UIEvent::ConfigReload { old_settings: _ } => {
self.color_cache = ColorCache {
even: crate::conf::value(context, "mail.listing.plain.even"),
odd: crate::conf::value(context, "mail.listing.plain.odd"),
even_unseen: crate::conf::value(context, "mail.listing.plain.even_unseen"),
odd_unseen: crate::conf::value(context, "mail.listing.plain.odd_unseen"),
even_highlighted: crate::conf::value(
context,
"mail.listing.plain.even_highlighted",
),
odd_highlighted: crate::conf::value(
context,
"mail.listing.plain.odd_highlighted",
),
even_selected: crate::conf::value(context, "mail.listing.plain.even_selected"),
odd_selected: crate::conf::value(context, "mail.listing.plain.odd_selected"),
attachment_flag: crate::conf::value(context, "mail.listing.attachment_flag"),
thread_snooze_flag: crate::conf::value(
context,
"mail.listing.thread_snooze_flag",
),
tag_default: crate::conf::value(context, "mail.listing.tag_default"),
theme_default: crate::conf::value(context, "theme_default"),
..self.color_cache
};
if !context.settings.terminal.use_color() {
self.color_cache.highlighted.attrs |= Attr::REVERSE;
self.color_cache.tag_default.attrs |= Attr::REVERSE;
self.color_cache.even_highlighted.attrs |= Attr::REVERSE;
self.color_cache.odd_highlighted.attrs |= Attr::REVERSE;
}
self.refresh_mailbox(context, true);
self.set_dirty(true);
}
UIEvent::MailboxUpdate((ref idxa, ref idxf))
if (*idxa, *idxf) == (self.new_cursor_pos.0, self.cursor_pos.1) =>
{

33
src/components/mail/listing/thread.rs

@ -1184,6 +1184,39 @@ impl Component for ThreadListing {
}
}
match *event {
UIEvent::ConfigReload { old_settings: _ } => {
self.color_cache = ColorCache {
even_unseen: crate::conf::value(context, "mail.listing.plain.even_unseen"),
even_selected: crate::conf::value(context, "mail.listing.plain.even_selected"),
even_highlighted: crate::conf::value(
context,
"mail.listing.plain.even_highlighted",
),
odd_unseen: crate::conf::value(context, "mail.listing.plain.odd_unseen"),
odd_selected: crate::conf::value(context, "mail.listing.plain.odd_selected"),
odd_highlighted: crate::conf::value(
context,
"mail.listing.plain.odd_highlighted",
),
even: crate::conf::value(context, "mail.listing.plain.even"),
odd: crate::conf::value(context, "mail.listing.plain.odd"),
attachment_flag: crate::conf::value(context, "mail.listing.attachment_flag"),
thread_snooze_flag: crate::conf::value(
context,
"mail.listing.thread_snooze_flag",
),
tag_default: crate::conf::value(context, "mail.listing.tag_default"),
theme_default: crate::conf::value(context, "theme_default"),
..self.color_cache
};
if !context.settings.terminal.use_color() {
self.color_cache.highlighted.attrs |= Attr::REVERSE;
self.color_cache.tag_default.attrs |= Attr::REVERSE;
self.color_cache.even_highlighted.attrs |= Attr::REVERSE;
self.color_cache.odd_highlighted.attrs |= Attr::REVERSE;
}
self.set_dirty(true);
}
UIEvent::Input(Key::Char('\n')) if !self.unfocused => {
self.unfocused = true;
self.dirty = true;

4
src/components/mail/status.rs

@ -403,6 +403,10 @@ impl Component for AccountStatus {
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
let shortcuts = self.get_shortcuts(context);
match *event {
UIEvent::ConfigReload { old_settings: _ } => {
self.theme_default = crate::conf::value(context, "theme_default");
self.set_dirty(true);
}
UIEvent::Resize => {
self.dirty = true;
}

4
src/components/mail/view.rs

@ -1763,6 +1763,10 @@ impl Component for MailView {
let shortcuts = &self.get_shortcuts(context);
match *event {
UIEvent::ConfigReload { old_settings: _ } => {
self.theme_default = crate::conf::value(context, "theme_default");
self.set_dirty(true);
}
UIEvent::Input(ref key)
if shortcut!(key == shortcuts[MailView::DESCRIPTION]["reply"]) =>
{

23
src/components/utilities.rs

@ -442,6 +442,25 @@ impl Component for StatusBar {
}
match event {
UIEvent::ConfigReload { old_settings: _ } => {
let mut progress_spinner = ProgressSpinner::new(19, context);
match context.settings.terminal.progress_spinner_sequence.as_ref() {
Some(conf::terminal::ProgressSpinnerSequence::Integer(k)) => {
progress_spinner.set_kind(*k);
}
Some(conf::terminal::ProgressSpinnerSequence::Custom(ref s)) => {
progress_spinner.set_custom_kind(s.clone());
}
None => {}
}
if self.progress_spinner.is_active() {
progress_spinner.start();
}
self.progress_spinner = progress_spinner;
self.mouse = context.settings.terminal.use_mouse.is_true();
self.set_dirty(true);
self.container.set_dirty(true);
}
UIEvent::ChangeMode(m) => {
let offset = self.status.find('|').unwrap_or_else(|| self.status.len());
self.status.replace_range(
@ -1194,6 +1213,10 @@ impl Component for Tabbed {
fn process_event(&mut self, mut event: &mut UIEvent, context: &mut Context) -> bool {
let shortcuts = &self.help_curr_views;
match &mut event {
UIEvent::ConfigReload { old_settings: _ } => {
self.theme_default = crate::conf::value(context, "theme_default");
self.set_dirty(true);
}
UIEvent::Input(Key::Alt(no)) if *no >= '1' && *no <= '9' => {
let no = *no as usize - '1' as usize;
if no < self.children.len() {

110
src/components/utilities/dialogs.rs

@ -49,6 +49,7 @@ pub struct Selector<T: 'static + PartialEq + Debug + Clone + Sync + Send, F: 'st
/// allow only one selection
single_only: bool,
entries: Vec<(T, bool)>,
entry_titles: Vec<String>,
pub content: CellBuffer,
theme_default: ThemeAttribute,
@ -104,6 +105,12 @@ impl<T: 'static + PartialEq + Debug + Clone + Sync + Send> Component for UIDialo
}
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
if let UIEvent::ConfigReload { old_settings: _ } = event {
self.initialise(context);
self.set_dirty(true);
return false;
}
let (width, height) = self.content.size();
let shortcuts = self.get_shortcuts(context);
let mut highlighted_attrs = crate::conf::value(context, "widgets.options.highlighted");
@ -419,6 +426,12 @@ impl Component for UIConfirmationDialog {
}
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
if let UIEvent::ConfigReload { old_settings: _ } = event {
self.initialise(context);
self.set_dirty(true);
return false;
}
let (width, height) = self.content.size();
let shortcuts = self.get_shortcuts(context);
let mut highlighted_attrs = crate::conf::value(context, "widgets.options.highlighted");
@ -734,51 +747,82 @@ impl Component for UIConfirmationDialog {
impl<T: PartialEq + Debug + Clone + Sync + Send, F: 'static + Sync + Send> Selector<T, F> {
pub fn new(
title: &str,
entries: Vec<(T, String)>,
mut entries: Vec<(T, String)>,
single_only: bool,
done_fn: F,
context: &Context,
) -> Selector<T, F> {
let theme_default = crate::conf::value(context, "theme_default");
let entry_titles = entries
.iter_mut()
.map(|(_id, ref mut title)| std::mem::replace(title, String::new()))
.collect::<Vec<String>>();
let mut identifiers: Vec<(T, bool)> =
entries.into_iter().map(|(id, _)| (id, false)).collect();
if single_only {
/* set default option */
identifiers[0].1 = true;
}
let mut ret = Selector {
single_only,
entries: identifiers,
entry_titles,
content: Default::default(),
cursor: SelectorCursor::Unfocused,
vertical_alignment: Alignment::Center,
horizontal_alignment: Alignment::Center,
title: title.to_string(),
done: false,
done_fn,
dirty: true,
theme_default: Default::default(),
id: ComponentId::new_v4(),
};
ret.initialise(context);
ret
}
fn initialise(&mut self, context: &Context) {
self.theme_default = crate::conf::value(context, "theme_default");
let width = std::cmp::max(
OK_CANCEL.len(),
std::cmp::max(
entries
self.entry_titles
.iter()
.max_by_key(|e| e.1.len())
.map(|v| v.1.len())
.max_by_key(|e| e.len())
.map(|v| v.len())
.unwrap_or(0),
title.len(),
self.title.len(),
),
) + 5;
let height = entries.len()
+ if single_only {
let height = self.entries.len()
+ if self.single_only {
0
} else {
/* Extra room for buttons Okay/Cancel */
2
};
let mut content = CellBuffer::new_with_context(width, height, None, context);
if single_only {
for (i, e) in entries.iter().enumerate() {
if self.single_only {
for (i, e) in self.entry_titles.iter().enumerate() {
write_string_to_grid(
&e.1,
&e,
&mut content,
theme_default.fg,
theme_default.bg,
theme_default.attrs,
self.theme_default.fg,
self.theme_default.bg,
self.theme_default.attrs,
((0, i), (width - 1, i)),
None,
);
}
} else {
for (i, e) in entries.iter().enumerate() {
for (i, e) in self.entry_titles.iter().enumerate() {
write_string_to_grid(
&format!("[ ] {}", e.1),
&format!("[ ] {}", &e),
&mut content,
theme_default.fg,
theme_default.bg,
theme_default.attrs,
self.theme_default.fg,
self.theme_default.bg,
self.theme_default.attrs,
((0, i), (width - 1, i)),
None,
);
@ -786,9 +830,9 @@ impl<T: PartialEq + Debug + Clone + Sync + Send, F: 'static + Sync + Send> Selec
write_string_to_grid(
OK_CANCEL,
&mut content,
theme_default.fg,
theme_default.bg,
theme_default.attrs | Attr::BOLD,
self.theme_default.fg,
self.theme_default.bg,
self.theme_default.attrs | Attr::BOLD,
(
((width - OK_CANCEL.len()) / 2, height - 1),
(width - 1, height - 1),
@ -796,27 +840,7 @@ impl<T: PartialEq + Debug + Clone + Sync + Send, F: 'static + Sync + Send> Selec
None,
);
}
let mut identifiers: Vec<(T, bool)> =
entries.into_iter().map(|(id, _)| (id, false)).collect();
if single_only {
/* set default option */
identifiers[0].1 = true;
}
Selector {
single_only,
entries: identifiers,
content,
cursor: SelectorCursor::Unfocused,
vertical_alignment: Alignment::Center,
horizontal_alignment: Alignment::Center,
title: title.to_string(),
done: false,
done_fn,
dirty: true,
theme_default,
id: ComponentId::new_v4(),
}
self.content = content;
}
pub fn is_done(&self) -> bool {

4
src/components/utilities/pager.rs

@ -609,6 +609,10 @@ impl Component for Pager {
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
let shortcuts = self.get_shortcuts(context);
match event {
UIEvent::ConfigReload { old_settings: _ } => {
self.set_colors(crate::conf::value(context, "theme_default"));
self.set_dirty(true);
}
UIEvent::Input(ref key)
if shortcut!(key == shortcuts[Self::DESCRIPTION]["scroll_up"]) =>
{

33
src/state.rs

@ -1075,6 +1075,39 @@ impl State {
})),
&mut self.context,
)));
} else if let Action::ReloadConfiguration = action {
match Settings::new().and_then(|new_settings| {
let old_accounts = self.context.settings.accounts.keys().collect::<std::collections::HashSet<&String>>();
let new_accounts = new_settings.accounts.keys().collect::<std::collections::HashSet<&String>>();
if old_accounts != new_accounts {
return Err("cannot reload account configuration changes; restart meli instead.".into());
}
for (key, acc) in new_settings.accounts.iter() {
if toml::Value::try_from(&acc) != toml::Value::try_from(&self.context.settings.accounts[key]) {
return Err("cannot reload account configuration changes; restart meli instead.".into());
}
}
if toml::Value::try_from(&new_settings) == toml::Value::try_from(&self.context.settings) {
return Err("No changes detected.".into());
}
Ok(new_settings)
}) {
Ok(new_settings) => {
let old_settings = std::mem::replace(&mut self.context.settings, new_settings);
self.context.replies.push_back(UIEvent::ConfigReload {
old_settings
});
self.context.replies.push_back(UIEvent::Resize);
}
Err(err) => {
self.context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(format!(
"Could not load configuration: {}",
err
)),
));
}
}
} else {
self.exec_command(action);
}

3
src/types.rs

@ -146,6 +146,9 @@ pub enum UIEvent {
Callback(CallbackFn),
GlobalUIDialog(Box<dyn Component>),
Timer(Uuid),
ConfigReload {
old_settings: crate::conf::Settings,
},
}
pub struct CallbackFn(pub Box<dyn FnOnce(&mut crate::Context) -> () + Send + 'static>);

Loading…
Cancel
Save