Permalink
Browse files

Implement Tilde Expansions In Tab Completions

  • Loading branch information...
mmstick committed Jun 19, 2017
1 parent 186777e commit dd42c629caf7e19f28cc80dc80c9b239ecd7612c
Showing with 83 additions and 19 deletions.
  1. +5 −0 examples/let.ion
  2. +3 −0 examples/let.out
  3. +49 −3 src/shell/completer.rs
  4. +26 −16 src/shell/mod.rs
View
@@ -10,3 +10,8 @@ let a b c = 1 2 3
echo $a
echo $b
echo $c
let a b c = ["one two" "three four" "five six"]
let a b c = [$c $a $b]
echo $a
echo $b
echo $c
View
@@ -6,3 +6,6 @@
1
2
3
five six
one two
three four
View
@@ -1,17 +1,63 @@
use liner::{Completer, FilenameCompleter};
use super::directory_stack::DirectoryStack;
use super::variables::Variables;
/// Performs escaping to an inner `FilenameCompleter` to enable a handful of special cases
/// needed by the shell, such as expanding '~' to a home directory, or adding a backslash
/// when a special character is contained within an expanded filename.
pub struct IonFileCompleter {
inner: FilenameCompleter
/// The completer that this completer is handling.
inner: FilenameCompleter,
/// A pointer to the directory stack in the shell.
dir_stack: *const DirectoryStack,
/// A pointer to the variables map in the shell.
vars: *const Variables,
}
impl IonFileCompleter {
pub fn new(path: Option<&str>) -> IonFileCompleter {
IonFileCompleter { inner: FilenameCompleter::new(path) }
pub fn new (
path: Option<&str>,
dir_stack: *const DirectoryStack,
vars: *const Variables
) -> IonFileCompleter {
IonFileCompleter {
inner: FilenameCompleter::new(path),
dir_stack: dir_stack,
vars: vars
}
}
}
impl Completer for IonFileCompleter {
fn completions(&self, start: &str) -> Vec<String> {
if start.starts_with('~') {
if let Some(expanded) = unsafe{ (*self.vars).tilde_expansion(start, &*self.dir_stack) } {
let t_index = start.find('/').unwrap_or(1);
let (tilde, search) = start.split_at(t_index as usize);
let iterator = self.inner.completions(&expanded);
let mut iterator = iterator.iter();
let mut completions = Vec::new();
if search.len() <= 1 {
for completion in iterator {
completions.push([start, &completion[expanded.len()..]].concat());
}
} else {
if let Some(completion) = iterator.next() {
if let Some(e_index) = completion.find(search) {
completions.push(escape(&[tilde, &completion[e_index..]].concat()));
for completion in iterator {
let expanded = &completion[e_index..];
completions.push(escape(&[tilde, expanded].concat()));
}
}
}
}
return completions
}
}
self.inner.completions(start).iter().map(|x| escape(x.as_str())).collect()
}
}
View
@@ -73,6 +73,8 @@ impl<'a> Shell<'a> {
}
}
fn readln(&mut self) -> Option<String> {
let vars_ptr = &self.variables as *const Variables;
let dirs_ptr = &self.directory_stack as *const DirectoryStack;
let funcs = &self.functions;
let vars = &self.variables;
let builtins = self.builtins;
@@ -106,26 +108,11 @@ impl<'a> Shell<'a> {
if filename {
if let Ok(current_dir) = env::current_dir() {
if let Some(url) = current_dir.to_str() {
let completer = IonFileCompleter::new(Some(url));
let completer = IonFileCompleter::new(Some(url), dirs_ptr, vars_ptr);
mem::replace(&mut editor.context().completer, Some(Box::new(completer)));
}
}
} else {
// Creates completers containing definitions from all directories listed
// in the environment's **$PATH** variable.
let file_completers = match env::var("PATH") {
Ok(val) => {
if cfg!(unix) {
// UNIX systems separate paths with the `:` character.
val.split(':').map(|x| IonFileCompleter::new(Some(x))).collect::<Vec<_>>()
} else {
// Redox and Windows use the `;` character to separate paths
val.split(';').map(|x| IonFileCompleter::new(Some(x))).collect::<Vec<_>>()
}
},
Err(_) => vec![IonFileCompleter::new(Some("/bin/"))],
};
// Creates a list of definitions from the shell environment that will be used
// in the creation of a custom completer.
let words = builtins.iter()
@@ -145,6 +132,24 @@ impl<'a> Shell<'a> {
// Initialize a new completer from the definitions collected.
let custom_completer = BasicCompleter::new(words);
// Creates completers containing definitions from all directories listed
// in the environment's **$PATH** variable.
let mut file_completers = if let Ok(val) = env::var("PATH") {
val.split(if cfg!(unix) { ':' } else { ';' })
.map(|s| IonFileCompleter::new(Some(s), dirs_ptr, vars_ptr))
.collect()
} else {
vec![IonFileCompleter::new(Some("/bin/"), dirs_ptr, vars_ptr)]
};
// Also add files/directories in the current directory to the completion list.
if let Ok(current_dir) = env::current_dir() {
if let Some(url) = current_dir.to_str() {
file_completers.push(IonFileCompleter::new(Some(url), dirs_ptr, vars_ptr));
}
}
// Merge the collected definitions with the file path definitions.
let completer = MultiCompleter::new(file_completers, custom_completer);
@@ -156,7 +161,12 @@ impl<'a> Shell<'a> {
match line {
Ok(line) => return Some(line),
// Handles Ctrl + C
Err(ref err) if err.kind() == ErrorKind::Interrupted => continue,
// Handles Ctrl + D
Err(ref err) if err.kind() == ErrorKind::UnexpectedEof => {
process::exit(self.previous_status)
},
Err(err) => {
let stderr = io::stderr();
let mut stderr = stderr.lock();

0 comments on commit dd42c62

Please sign in to comment.