mod completers;
#[allow(missing_docs)]
pub mod completion_view;
use std::collections::HashMap;
use glib::ToValue;
use glib::types::Type;
use gtk::{
prelude::GtkListStoreExtManual,
traits::{
GtkListStoreExt,
TreeModelExt,
TreeSelectionExt,
},
ListStore,
TreeSelection,
};
use self::Column::Expand;
pub use self::completers::{CommandCompleter, NoCompleter, SettingCompleter};
pub use self::completion_view::CompletionView;
pub const DEFAULT_COMPLETER_IDENT: &str = "__mg_default";
pub const NO_COMPLETER_IDENT: &str = "__mg_no_completer";
#[doc(hidden)]
pub type Completers = HashMap<&'static str, Box<dyn Completer>>;
#[derive(Clone, Copy, PartialEq)]
pub enum Column {
AllVisible,
Expand,
}
pub trait Completer {
fn columns(&self) -> Vec<Column> {
vec![Expand, Expand]
}
fn column_count(&self) -> usize {
self.columns().len()
}
fn complete_result(&self, value: &str) -> String {
value.to_string()
}
fn completions(&mut self, input: &str) -> Vec<CompletionResult>;
fn have_command(&self) -> bool {
true
}
fn text_column(&self) -> i32 {
0
}
}
pub struct Completion {
completer_ident: String,
completers: Completers,
}
impl Completion {
#[allow(unknown_lints, new_without_default_derive)]
pub fn new() -> Self {
Completion {
completer_ident: String::new(),
completers: HashMap::new(),
}
}
pub fn add_completer(&mut self, ident: &'static str, completer: Box<dyn Completer>) {
self.completers.insert(ident, completer);
}
pub fn adjust_model(&mut self, completer_ident: &str) -> bool {
if completer_ident != self.completer_ident {
self.completer_ident = completer_ident.to_string();
if completer_ident == NO_COMPLETER_IDENT || !self.completers.contains_key(completer_ident) {
self.completer_ident = NO_COMPLETER_IDENT.to_string();
return true;
}
}
false
}
pub fn complete_result(&self, selection: &TreeSelection) -> Option<String> {
let mut completion = None;
if self.current_completer_ident() != NO_COMPLETER_IDENT {
if let Some((model, iter)) = selection.selected() {
if let Some(completer) = self.current_completer() {
let value: Option<String> = model.value(&iter, completer.text_column()).get().ok().flatten();
if let Some(value) = value {
completion = Some(completer.complete_result(&value));
}
}
}
}
completion
}
pub fn current_completer(&self) -> Option<&dyn Completer> {
self.completers.get(self.completer_ident.as_str())
.map(AsRef::as_ref)
}
#[allow(unknown_lints, borrowed_box)]
pub fn current_completer_mut(&mut self) -> Option<&mut Box<dyn Completer>> {
self.completers.get_mut(self.completer_ident.as_str())
}
pub fn current_completer_ident(&self) -> &str {
&self.completer_ident
}
pub fn filter(&mut self, input: &str) -> Option<ListStore> {
self.current_completer_mut()
.map(|completer| {
let columns = vec![Type::STRING; completer.column_count() * 2];
let model = ListStore::new(&columns);
let key =
if !completer.have_command() {
input
}
else if let Some(index) = input.find(' ') {
input[index + 1 ..].trim_start()
}
else {
input
};
for &CompletionResult { ref columns } in &completer.completions(key) {
let row = model.insert(-1);
let start_column = columns.len();
for (index, cell) in columns.iter().enumerate() {
model.set_value(&row, index as u32, &cell.value.to_value());
if let Some(ref foreground) = cell.foreground {
model.set_value(&row, (index + start_column) as u32, &foreground.to_value());
}
}
}
model
})
}
pub fn set_completers(&mut self, mut completers: Completers) {
completers.insert(NO_COMPLETER_IDENT, Box::new(NoCompleter::new()));
self.completers = completers;
}
}
#[derive(Clone)]
pub struct CompletionCell {
pub foreground: Option<String>,
pub value: String,
}
impl CompletionCell {
pub fn new(value: &str) -> Self {
CompletionCell {
foreground: None,
value: value.to_string(),
}
}
pub fn foreground(mut self, foreground: &str) -> Self {
self.foreground = Some(foreground.to_string());
self
}
}
pub trait ToCell {
fn to_cell(&self) -> CompletionCell;
}
impl ToCell for CompletionCell {
fn to_cell(&self) -> CompletionCell {
self.clone()
}
}
impl ToCell for str {
fn to_cell(&self) -> CompletionCell {
CompletionCell::new(self)
}
}
impl ToCell for String {
fn to_cell(&self) -> CompletionCell {
CompletionCell::new(self)
}
}
pub struct CompletionResult {
pub columns: Vec<CompletionCell>,
}
impl CompletionResult {
pub fn new(cols: &[&str]) -> Self {
let cols: Vec<_> = cols.iter().map(|col| CompletionCell::new(col)).collect();
CompletionResult {
columns: cols,
}
}
pub fn from_cells(cols: &[&dyn ToCell]) -> Self {
CompletionResult {
columns: cols.iter().map(|value| value.to_cell()).collect(),
}
}
}