Commit 2d0c0f98 authored by Sag0Sag0's avatar Sag0Sag0

Merge branch 'master' of https://github.com/redox-os/liner

parents 23992c25 98981ecd
......@@ -20,7 +20,7 @@ name = "liner_test"
path = "src/main.rs"
[dependencies]
bytecount = "0.1"
bytecount = "0.3.1"
termion = "1.4.0"
unicode-width = "0.1.*"
regex = "1.0.0"
......@@ -81,8 +81,8 @@ pub struct Editor<'a, W: Write> {
// If the cursor is on the line below the prompt, `term_cursor_line == 2`.
term_cursor_line: usize,
// If this is true, on the next tab we print the completion list.
show_completions_hint: bool,
// The next completion to suggest, or none
show_completions_hint: Option<(Vec<String>, Option<usize>)>,
// Show autosuggestions based on history
show_autosuggestions: bool,
......@@ -135,7 +135,7 @@ impl<'a, 'b, W: Write> Editor<'a, W> {
new_buf: buffer.into(),
cur_history_loc: None,
context: context,
show_completions_hint: false,
show_completions_hint: None,
show_autosuggestions: true,
term_cursor_line: 1,
no_eol: false,
......@@ -174,6 +174,11 @@ impl<'a, 'b, W: Write> Editor<'a, W> {
// XXX: Returning a bool to indicate doneness is a bit awkward, maybe change it
pub fn handle_newline(&mut self) -> io::Result<bool> {
if self.show_completions_hint.is_some() {
self.show_completions_hint = None;
return Ok(false);
}
let char_before_cursor = cur_buf!(self).char_before(self.cursor);
if char_before_cursor == Some('\\') {
// self.insert_after_cursor('\r')?;
......@@ -183,7 +188,7 @@ impl<'a, 'b, W: Write> Editor<'a, W> {
self.cursor = cur_buf!(self).num_chars();
self._display(false)?;
try!(self.out.write(b"\r\n"));
self.show_completions_hint = false;
self.show_completions_hint = None;
Ok(true)
}
}
......@@ -226,7 +231,7 @@ impl<'a, 'b, W: Write> Editor<'a, W> {
Ok(did)
}
fn print_completion_list(&mut self, completions: &[String]) -> io::Result<()> {
fn print_completion_list(out: &mut W, completions: &[String], highlighted: Option<usize>) -> io::Result<usize> {
use std::cmp::max;
let (w, _) = try!(termion::terminal_size());
......@@ -237,32 +242,52 @@ impl<'a, 'b, W: Write> Editor<'a, W> {
let col_width = 2 + w as usize / cols;
let cols = max(1, w as usize / col_width);
let mut lines = 0;
let mut i = 0;
for com in completions {
for (index, com) in completions.iter().enumerate() {
if i == cols {
try!(write!(self.out, "\r\n"));
try!(write!(out, "\r\n"));
lines += 1;
i = 0;
} else if i > cols {
unreachable!()
}
try!(write!(self.out, "{:<1$}", com, col_width));
if Some(index) == highlighted {
try!(write!(out, "{}{}", color::Fg(color::Black), color::Bg(color::White)));
}
try!(write!(out, "{:<1$}", com, col_width));
if Some(index) == highlighted {
try!(write!(out, "{}{}", color::Bg(color::Reset), color::Fg(color::Reset)));
}
i += 1;
}
self.term_cursor_line = 1;
Ok(())
Ok(lines)
}
pub fn skip_completions_hint(&mut self) {
self.show_completions_hint = false;
self.show_completions_hint = None;
}
pub fn complete(&mut self, handler: &mut EventHandler<W>) -> io::Result<()> {
handler(Event::new(self, EventKind::BeforeComplete));
if let Some((completions, i)) = self.show_completions_hint.take() {
let i = i.map_or(0, |i| (i+1) % completions.len());
try!(self.delete_word_before_cursor(false));
try!(self.insert_str_after_cursor(&completions[i]));
self.show_completions_hint = Some((completions, Some(i)));
}
if self.show_completions_hint.is_some() {
try!(self.display());
return Ok(());
}
let (word, completions) = {
let word_range = self.get_word_before_cursor(false);
let buf = cur_buf_mut!(self);
......@@ -284,10 +309,10 @@ impl<'a, 'b, W: Write> Editor<'a, W> {
if completions.len() == 0 {
// Do nothing.
self.show_completions_hint = false;
self.show_completions_hint = None;
Ok(())
} else if completions.len() == 1 {
self.show_completions_hint = false;
self.show_completions_hint = None;
try!(self.delete_word_before_cursor(false));
self.insert_str_after_cursor(completions[0].as_ref())
} else {
......@@ -307,16 +332,9 @@ impl<'a, 'b, W: Write> Editor<'a, W> {
}
}
if self.show_completions_hint {
try!(write!(self.out, "\r\n"));
try!(self.print_completion_list(&completions[..]));
try!(write!(self.out, "\r\n"));
try!(self.display());
self.show_completions_hint = Some((completions, None));
try!(self.display());
self.show_completions_hint = false;
} else {
self.show_completions_hint = true;
}
Ok(())
}
}
......@@ -636,6 +654,7 @@ impl<'a, 'b, W: Write> Editor<'a, W> {
let w = w as usize;
let prompt_width = util::width(&self.prompt);
let buf = cur_buf!(self);
let buf_width = buf.width();
......@@ -667,7 +686,7 @@ impl<'a, 'b, W: Write> Editor<'a, W> {
let new_total_width = calc_width(prompt_width, buf_widths, w);
let new_total_width_to_cursor = calc_width(prompt_width, buf_widths_to_cursor, w);
let new_num_lines = (new_total_width + w) / w;
let mut new_num_lines = (new_total_width + w) / w;
// Move the term cursor to the same line as the prompt.
if self.term_cursor_line > 1 {
......@@ -677,8 +696,18 @@ impl<'a, 'b, W: Write> Editor<'a, W> {
cursor::Up(self.term_cursor_line as u16 - 1)
));
}
// Move the cursor to the start of the line then clear everything after. Write the prompt
try!(write!(self.out, "\r{}{}", clear::AfterCursor, self.prompt));
// Move the cursor to the start of the line then clear everything after.
try!(write!(self.out, "\r{}", clear::AfterCursor));
// If we're cycling through completions, show those
let mut completion_lines = 0;
if let Some((completions, i)) = self.show_completions_hint.as_ref() {
completion_lines = 2 + try!(Self::print_completion_list(&mut self.out, completions, *i));
try!(write!(self.out, "\r\n"));
}
// Write the prompt
try!(write!(self.out, "{}", self.prompt));
// If we have an autosuggestion, we make the autosuggestion the buffer we print out.
// We get the number of bytes in the buffer (but NOT the autosuggestion).
......@@ -750,6 +779,7 @@ impl<'a, 'b, W: Write> Editor<'a, W> {
));
}
self.term_cursor_line = completion_lines;
self.out.flush()
}
......
......@@ -277,7 +277,7 @@ fn write_to_disk(max_file_size: usize, new_item: &Buffer, file_name: &str) -> io
// Move it all back
move_file_contents_backward(&mut file, move_dist);
move_file_contents_backward(&mut file, move_dist)?;
}
};
......@@ -294,7 +294,7 @@ fn write_to_disk(max_file_size: usize, new_item: &Buffer, file_name: &str) -> io
fn move_file_contents_backward(file: &mut File, distance: u64) -> io::Result<()> {
let mut total_read = 0;
let mut buffer = [0u8, 4096];
let mut buffer = [0u8; 4096];
file.seek(SeekFrom::Start(distance))?;
......@@ -307,15 +307,15 @@ fn move_file_contents_backward(file: &mut File, distance: u64) -> io::Result<()>
break;
}
file.seek(SeekFrom::Current(-(read as i64 + distance as i64)));
file.seek(SeekFrom::Current(-(read as i64 + distance as i64)))?;
file.write_all(&buffer[..read])?;
file.seek(SeekFrom::Current(distance as i64));
file.seek(SeekFrom::Current(distance as i64))?;
}
file.set_len(total_read)?;
file.flush()?;
Ok(())
}
\ No newline at end of file
}
......@@ -486,7 +486,7 @@ impl<'a, W: Write> Vi<'a, W> {
fn handle_key_insert(&mut self, key: Key) -> io::Result<()> {
match key {
Key::Esc => {
Key::Esc | Key::Ctrl('[') => {
// perform any repeats
if self.count > 0 {
self.last_count = self.count;
......@@ -908,6 +908,11 @@ impl<'a, W: Write> Vi<'a, W> {
(_, ReverseRepeat, Some((c, RightUntil))) => (Key::Char(c), LeftUntil),
(_, ReverseRepeat, Some((c, LeftAt))) => (Key::Char(c), RightAt),
(_, ReverseRepeat, Some((c, RightAt))) => (Key::Char(c), LeftAt),
// repeat with no last_char_movement, invalid
(_, Repeat, None) | (_, ReverseRepeat, None) => {
self.normal_mode_abort();
return Ok(());
}
// pass valid keys through as is
(Key::Char(c), _, _) => {
// store last command info
......@@ -915,8 +920,7 @@ impl<'a, W: Write> Vi<'a, W> {
self.current_command.push(key);
(key, movement)
}
// all other combinations are invalid, abort. This includes repeats with no
// last_char_movement stored, and non char key presses.
// all other combinations are invalid, abort
_ => {
self.normal_mode_abort();
return Ok(());
......@@ -1191,15 +1195,15 @@ mod tests {
Char('i'),
Esc,
Char('i'),
Esc,
//Ctrl+[ is the same as escape
Ctrl('['),
Char('i'),
Esc,
Char('i'),
Esc,
Ctrl('['),
]);
assert_eq!(map.ed.cursor(), 0);
}
#[test]
fn vi_normal_history_cursor_eol() {
let mut context = Context::new();
......@@ -1215,7 +1219,7 @@ mod tests {
assert_eq!(map.ed.cursor(), 7);
// in normal mode, make sure we don't end up past the last char
simulate_keys!(map, [Esc, Up]);
simulate_keys!(map, [Ctrl('['), Up]);
assert_eq!(map.ed.cursor(), 6);
}
......@@ -1239,7 +1243,6 @@ mod tests {
assert_eq!(map.ed.cursor(), 0);
assert_eq!(String::from(map), "ta");
}
#[test]
fn vi_substitute_command() {
let mut context = Context::new();
......@@ -1250,7 +1253,8 @@ mod tests {
assert_eq!(map.ed.cursor(), 4);
simulate_keys!(map, [
Esc,
//ctrl+[ is the same as Esc
Ctrl('['),
Char('0'),
Char('s'),
Char('s'),
......@@ -1293,7 +1297,8 @@ mod tests {
Char('s'),
Char('b'),
Char('e'),
Esc,
//The same as Esc
Ctrl('['),
Char('4'),
Char('l'),
Char('.'),
......@@ -1416,7 +1421,8 @@ mod tests {
let mut map = Vi::new(ed);
simulate_keys!(map, [
Esc,
//same as Esc
Ctrl('['),
Char('3'),
Char('i'),
Char('t'),
......@@ -3716,4 +3722,21 @@ mod tests {
assert_eq!(res.is_ok(), true);
assert_eq!(map.ed.current_buffer().to_string(), "not empt".to_string());
}
#[test]
/// repeat char move with no last char
fn repeat_char_move_no_char() {
let mut context = Context::new();
let out = Vec::new();
let ed = Editor::new(out, "prompt".to_owned(), &mut context).unwrap();
let mut map = Vi::new(ed);
map.ed.insert_str_after_cursor("abc defg").unwrap();
simulate_keys!(map, [
Esc,
Char('$'),
Char(';'),
]);
assert_eq!(map.ed.cursor(), 7);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment