mirror of
https://git.meli.delivery/meli/meli
synced 2024-11-10 19:10:57 +00:00
ui: add autocomplete for commands in execute bar
This commit is contained in:
parent
a028aa9a44
commit
70e5949590
@ -37,6 +37,10 @@ pub trait Graphemes: UnicodeSegmentation + CodePointsIter {
|
||||
|
||||
count
|
||||
}
|
||||
|
||||
fn grapheme_len(&self) -> usize {
|
||||
self.split_graphemes().len()
|
||||
}
|
||||
}
|
||||
|
||||
impl Graphemes for str {}
|
||||
|
@ -205,6 +205,9 @@ impl Composer {
|
||||
let book: &AddressBook = &c.accounts[account_cursor].address_book;
|
||||
let results: Vec<String> = book.search(term);
|
||||
results
|
||||
.into_iter()
|
||||
.map(|r| AutoCompleteEntry::from(r))
|
||||
.collect::<Vec<AutoCompleteEntry>>()
|
||||
}),
|
||||
));
|
||||
} else {
|
||||
|
@ -699,17 +699,24 @@ impl Component for StatusBar {
|
||||
if self.ex_buffer.as_str().split_graphemes().len() <= 2 {
|
||||
return;
|
||||
}
|
||||
let suggestions: Vec<String> = self
|
||||
let mut suggestions: Vec<AutoCompleteEntry> = self
|
||||
.cmd_history
|
||||
.iter()
|
||||
.filter_map(|h| {
|
||||
if h.starts_with(self.ex_buffer.as_str()) {
|
||||
Some(h.clone())
|
||||
Some(h.clone().into())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
suggestions.extend(crate::execute::COMMAND_COMPLETION.iter().filter_map(|e| {
|
||||
if e.0.starts_with(self.ex_buffer.as_str()) {
|
||||
Some(e.into())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}));
|
||||
if suggestions.is_empty() && !self.auto_complete.suggestions().is_empty() {
|
||||
self.auto_complete.set_suggestions(suggestions);
|
||||
/* redraw self.container because we have got ridden of an autocomplete
|
||||
@ -803,7 +810,7 @@ impl Component for StatusBar {
|
||||
.take(hist_height)
|
||||
.enumerate()
|
||||
{
|
||||
write_string_to_grid(
|
||||
let (x, y) = write_string_to_grid(
|
||||
s.as_str(),
|
||||
grid,
|
||||
Color::Byte(88), // DarkRed,
|
||||
@ -817,6 +824,14 @@ impl Component for StatusBar {
|
||||
),
|
||||
true,
|
||||
);
|
||||
write_string_to_grid(
|
||||
&s.description,
|
||||
grid,
|
||||
Color::White,
|
||||
Color::Byte(174),
|
||||
((x + 2, y), bottom_right!(hist_area)),
|
||||
false,
|
||||
);
|
||||
if y_offset + offset == self.auto_complete.cursor() {
|
||||
change_colors(
|
||||
grid,
|
||||
|
@ -1,7 +1,7 @@
|
||||
use super::*;
|
||||
use fnv::FnvHashMap;
|
||||
|
||||
type AutoCompleteFn = Box<Fn(&Context, &str) -> Vec<String> + Send>;
|
||||
type AutoCompleteFn = Box<Fn(&Context, &str) -> Vec<AutoCompleteEntry> + Send>;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum FormFocus {
|
||||
@ -575,9 +575,50 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct AutoCompleteEntry {
|
||||
pub entry: String,
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
impl AutoCompleteEntry {
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.entry.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for AutoCompleteEntry {
|
||||
fn from(val: String) -> Self {
|
||||
AutoCompleteEntry {
|
||||
entry: val,
|
||||
description: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&(&str, &str)> for AutoCompleteEntry {
|
||||
fn from(val: &(&str, &str)) -> Self {
|
||||
let (a, b) = val;
|
||||
AutoCompleteEntry {
|
||||
entry: a.to_string(),
|
||||
description: b.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(String, String)> for AutoCompleteEntry {
|
||||
fn from(val: (String, String)) -> Self {
|
||||
let (a, b) = val;
|
||||
AutoCompleteEntry {
|
||||
entry: a,
|
||||
description: b,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct AutoComplete {
|
||||
entries: Vec<String>,
|
||||
entries: Vec<AutoCompleteEntry>,
|
||||
content: CellBuffer,
|
||||
cursor: usize,
|
||||
|
||||
@ -637,7 +678,7 @@ impl Component for AutoComplete {
|
||||
}
|
||||
|
||||
impl AutoComplete {
|
||||
pub fn new(entries: Vec<String>) -> Self {
|
||||
pub fn new(entries: Vec<AutoCompleteEntry>) -> Self {
|
||||
let mut ret = AutoComplete {
|
||||
entries: Vec::new(),
|
||||
content: CellBuffer::default(),
|
||||
@ -649,26 +690,39 @@ impl AutoComplete {
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn set_suggestions(&mut self, entries: Vec<String>) -> bool {
|
||||
pub fn set_suggestions(&mut self, entries: Vec<AutoCompleteEntry>) -> bool {
|
||||
if entries.len() == self.entries.len() && entries == self.entries {
|
||||
return false;;
|
||||
}
|
||||
|
||||
let mut content = CellBuffer::new(
|
||||
entries.iter().map(String::len).max().unwrap_or(0) + 1,
|
||||
entries
|
||||
.iter()
|
||||
.map(|a| a.entry.grapheme_len() + a.description.grapheme_len() + 2)
|
||||
.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() {
|
||||
write_string_to_grid(
|
||||
e,
|
||||
let (x, _) = write_string_to_grid(
|
||||
&e.entry,
|
||||
&mut content,
|
||||
Color::Byte(23),
|
||||
Color::Byte(7),
|
||||
((0, i), (width - 1, i)),
|
||||
false,
|
||||
);
|
||||
write_string_to_grid(
|
||||
&e.description,
|
||||
&mut content,
|
||||
Color::Byte(23),
|
||||
Color::Byte(7),
|
||||
((x + 2, i), (width - 1, i)),
|
||||
false,
|
||||
);
|
||||
write_string_to_grid(
|
||||
"▒",
|
||||
&mut content,
|
||||
@ -712,10 +766,10 @@ impl AutoComplete {
|
||||
self.entries.clear();
|
||||
self.cursor = 0;
|
||||
self.content.empty();
|
||||
Some(ret)
|
||||
Some(ret.entry)
|
||||
}
|
||||
|
||||
pub fn suggestions(&self) -> &Vec<String> {
|
||||
pub fn suggestions(&self) -> &Vec<AutoCompleteEntry> {
|
||||
&self.entries
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,140 @@ pub use crate::actions::ListingAction::{self, *};
|
||||
pub use crate::actions::MailingListAction::{self, *};
|
||||
pub use crate::actions::TabAction::{self, *};
|
||||
|
||||
/* Create a const table with every command part that can be auto-completed and its description */
|
||||
macro_rules! define_commands {
|
||||
( [$({ tags: [$( $tags:literal),*], desc: $desc:literal, parser: ($parser:item)}),*]) => {
|
||||
pub const COMMAND_COMPLETION: &[(&str, &str)] = &[$($( ($tags, $desc ) ),*),* ];
|
||||
$( $parser )*
|
||||
};
|
||||
}
|
||||
|
||||
define_commands!([
|
||||
{ tags: ["set"],
|
||||
desc: "set [seen/unseen], toggles message's Seen flag.",
|
||||
parser:
|
||||
( named!(
|
||||
envelope_action<Action>,
|
||||
alt_complete!(
|
||||
preceded!(
|
||||
ws!(tag!("set")),
|
||||
alt_complete!(
|
||||
map!(ws!(tag!("read")), |_| Listing(SetRead))
|
||||
| map!(ws!(tag!("unread")), |_| Listing(SetUnread))
|
||||
)
|
||||
) | map!(ws!(tag!("delete")), |_| Listing(Delete))
|
||||
)
|
||||
); )
|
||||
},
|
||||
{ tags: ["close"],
|
||||
desc: "close non-sticky tabs",
|
||||
parser: (
|
||||
named!(close<Action>, map!(ws!(tag!("close")), |_| Tab(Close)));
|
||||
)
|
||||
},
|
||||
{ tags: ["goto"],
|
||||
desc: "goto [n], switch to nth mailbox in this account",
|
||||
parser: (
|
||||
named!(
|
||||
goto<Action>,
|
||||
preceded!(tag!("go "), map!(call!(usize_c), Action::ViewMailbox))
|
||||
);
|
||||
)
|
||||
},
|
||||
{ tags: ["subsort"],
|
||||
desc: "subsort [date/subject] [asc/desc], sorts first level replies in threads.",
|
||||
parser: (
|
||||
named!(
|
||||
subsort<Action>,
|
||||
do_parse!(tag!("subsort ") >> p: pair!(sortfield, sortorder) >> (SubSort(p.0, p.1)))
|
||||
);
|
||||
)
|
||||
},
|
||||
{ tags: ["sort"],
|
||||
desc: "sort [date/subject] [asc/desc], sorts threads.",
|
||||
parser: (
|
||||
named!(
|
||||
sort<Action>,
|
||||
do_parse!(
|
||||
tag!("sort ") >> p: separated_pair!(sortfield, tag!(" "), sortorder) >> (Sort(p.0, p.1))
|
||||
)
|
||||
);
|
||||
)
|
||||
},
|
||||
{ tags: ["set", "set plain", "set threaded", "set compact"],
|
||||
desc: "set [plain/threaded/compact], changes the mail listing view",
|
||||
parser: (
|
||||
named!(
|
||||
toggle<Action>,
|
||||
preceded!(tag!("set "), alt_complete!(threaded | plain | compact))
|
||||
);
|
||||
)
|
||||
},
|
||||
{ tags: ["toggle_thread_snooze"],
|
||||
desc: "turn off new notifications for this thread",
|
||||
parser: (
|
||||
named!(toggle_thread_snooze<Action>,
|
||||
map!(ws!(tag!("toggle_thread_snooze")), |_| ToggleThreadSnooze)
|
||||
);
|
||||
)
|
||||
},
|
||||
{ tags: ["filter"],
|
||||
desc: "filter <TERM>, filters list with given term",
|
||||
parser:(
|
||||
named!(filter<Action>,
|
||||
do_parse!(
|
||||
ws!(tag!("filter"))
|
||||
>> string: map_res!(call!(not_line_ending), std::str::from_utf8)
|
||||
>> (Listing(Filter(String::from(string))))
|
||||
)
|
||||
);
|
||||
)
|
||||
},
|
||||
{ tags: ["list-archive", "list-post", "list-unsubscribe", "list-"],
|
||||
desc: "list-[unsubscribe/post/archive]",
|
||||
parser: (
|
||||
named!(
|
||||
mailinglist<Action>,
|
||||
alt_complete!(
|
||||
map!(ws!(tag!("list-post")), |_| MailingListAction(ListPost))
|
||||
| map!(ws!(tag!("list-unsubscribe")), |_| MailingListAction(
|
||||
ListUnsubscribe
|
||||
))
|
||||
| map!(ws!(tag!("list-archive")), |_| MailingListAction(
|
||||
ListArchive
|
||||
))
|
||||
)
|
||||
);
|
||||
)
|
||||
},
|
||||
{ tags: ["setenv "],
|
||||
desc:"setenv VAR=VALUE",
|
||||
parser: (
|
||||
named!( setenv<Action>,
|
||||
do_parse!(
|
||||
ws!(tag!("setenv"))
|
||||
>> key: map_res!(take_until1!("="), std::str::from_utf8)
|
||||
>> ws!(tag!("="))
|
||||
>> val: map_res!(call!(not_line_ending), std::str::from_utf8)
|
||||
>> (SetEnv(key.to_string(), val.to_string()))
|
||||
)
|
||||
);
|
||||
)
|
||||
},
|
||||
{ tags: ["printenv "],
|
||||
desc: "printenv VAR",
|
||||
parser:(
|
||||
named!( printenv<Action>,
|
||||
do_parse!(
|
||||
ws!(tag!("env"))
|
||||
>> key: map_res!(call!(not_line_ending), std::str::from_utf8)
|
||||
>> (PrintEnv(key.to_string()))
|
||||
)
|
||||
);
|
||||
)
|
||||
}
|
||||
]);
|
||||
|
||||
named!(
|
||||
usize_c<usize>,
|
||||
map_res!(
|
||||
@ -54,23 +188,6 @@ named!(
|
||||
)
|
||||
);
|
||||
|
||||
named!(close<Action>, map!(ws!(tag!("close")), |_| Tab(Close)));
|
||||
named!(
|
||||
goto<Action>,
|
||||
preceded!(tag!("go "), map!(call!(usize_c), Action::ViewMailbox))
|
||||
);
|
||||
|
||||
named!(
|
||||
subsort<Action>,
|
||||
do_parse!(tag!("subsort ") >> p: pair!(sortfield, sortorder) >> (SubSort(p.0, p.1)))
|
||||
);
|
||||
named!(
|
||||
sort<Action>,
|
||||
do_parse!(
|
||||
tag!("sort ") >> p: separated_pair!(sortfield, tag!(" "), sortorder) >> (Sort(p.0, p.1))
|
||||
)
|
||||
);
|
||||
|
||||
named!(
|
||||
threaded<Action>,
|
||||
map!(ws!(tag!("threaded")), |_| Listing(SetThreaded))
|
||||
@ -85,76 +202,10 @@ named!(
|
||||
compact<Action>,
|
||||
map!(ws!(tag!("compact")), |_| Listing(SetCompact))
|
||||
);
|
||||
|
||||
named!(
|
||||
toggle<Action>,
|
||||
preceded!(tag!("set "), alt_complete!(threaded | plain | compact))
|
||||
);
|
||||
named!(
|
||||
listing_action<Action>,
|
||||
alt_complete!(toggle | envelope_action | filter | toggle_thread_snooze)
|
||||
);
|
||||
|
||||
named!(
|
||||
toggle_thread_snooze<Action>,
|
||||
map!(ws!(tag!("toggle_thread_snooze")), |_| ToggleThreadSnooze)
|
||||
);
|
||||
|
||||
named!(
|
||||
filter<Action>,
|
||||
do_parse!(
|
||||
ws!(tag!("filter"))
|
||||
>> string: map_res!(call!(not_line_ending), std::str::from_utf8)
|
||||
>> (Listing(Filter(String::from(string))))
|
||||
)
|
||||
);
|
||||
|
||||
named!(
|
||||
mailinglist<Action>,
|
||||
alt_complete!(
|
||||
map!(ws!(tag!("list-post")), |_| MailingListAction(ListPost))
|
||||
| map!(ws!(tag!("list-unsubscribe")), |_| MailingListAction(
|
||||
ListUnsubscribe
|
||||
))
|
||||
| map!(ws!(tag!("list-archive")), |_| MailingListAction(
|
||||
ListArchive
|
||||
))
|
||||
)
|
||||
);
|
||||
|
||||
named!(
|
||||
envelope_action<Action>,
|
||||
alt_complete!(
|
||||
preceded!(
|
||||
ws!(tag!("set")),
|
||||
alt_complete!(
|
||||
map!(ws!(tag!("read")), |_| Listing(SetRead))
|
||||
| map!(ws!(tag!("unread")), |_| Listing(SetUnread))
|
||||
)
|
||||
) | map!(ws!(tag!("delete")), |_| Listing(Delete))
|
||||
)
|
||||
);
|
||||
|
||||
named!(
|
||||
setenv<Action>,
|
||||
do_parse!(
|
||||
ws!(tag!("setenv"))
|
||||
>> key: map_res!(take_until1!("="), std::str::from_utf8)
|
||||
>> ws!(tag!("="))
|
||||
>> val: map_res!(call!(not_line_ending), std::str::from_utf8)
|
||||
>> (SetEnv(key.to_string(), val.to_string()))
|
||||
)
|
||||
);
|
||||
|
||||
named!(
|
||||
printenv<Action>,
|
||||
do_parse!(
|
||||
ws!(tag!("env"))
|
||||
>> key: map_res!(call!(not_line_ending), std::str::from_utf8)
|
||||
>> (PrintEnv(key.to_string()))
|
||||
)
|
||||
);
|
||||
|
||||
named!(pub parse_command<Action>,
|
||||
alt_complete!( goto | listing_action | sort | subsort | close | mailinglist | setenv | printenv)
|
||||
);
|
||||
alt_complete!( goto | listing_action | sort | subsort | close | mailinglist | setenv | printenv)
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user