Commit 613239b9 authored by Sag0Sag0's avatar Sag0Sag0 Committed by GitHub

Merge pull request #3 from Sag0Sag0/master

Add closure to read_line()'s arguments to enable syntax highlighting
parents 84501ef9 b79b2577
......@@ -15,11 +15,10 @@ exclude = [
[lib]
name = "liner"
[[bin]]
name = "liner_test"
path = "src/main.rs"
[dependencies]
bytecount = "0.3.1"
termion = "1.4.0"
unicode-width = "0.1.*"
[dev-dependencies]
regex = "1.0.0"
extern crate liner;
extern crate termion;
extern crate regex;
use std::mem::replace;
use std::env::{args, current_dir};
use std::io;
use liner::{Context, CursorPosition, Event, EventKind, FilenameCompleter};
use termion::color;
use regex::Regex;
fn highlight_dodo(s: &str) -> String {
let reg_exp = Regex::new("(?P<k>dodo)").unwrap();
let format = format!("{}$k{}", color::Fg(color::Red), color::Fg(color::Reset));
reg_exp.replace_all(s, format.as_str()).to_string()
}
fn main() {
let mut con = Context::new();
......@@ -23,6 +32,7 @@ fn main() {
loop {
let res = con.read_line("[prompt]$ ",
Some(Box::new(highlight_dodo)),
&mut |Event { editor, kind }| {
if let EventKind::BeforeComplete = kind {
let (_, pos) = editor.get_words_and_cursor_position();
......@@ -92,4 +102,4 @@ fn main() {
// Ensure that all writes to the history file are written before exiting.
con.history.commit_history();
}
}
\ No newline at end of file
......@@ -5,6 +5,8 @@ use termion::raw::{IntoRawMode, RawTerminal};
use super::*;
use keymap;
pub type ColorClosure = Box<Fn(&str) -> String>;
/// The default for `Context.word_divider_fn`.
pub fn get_buffer_words(buf: &Buffer) -> Vec<(usize, usize)> {
let mut res = Vec::new();
......@@ -70,9 +72,10 @@ impl Context {
pub fn read_line<P: Into<String>>(
&mut self,
prompt: P,
f: Option<ColorClosure>,
mut handler: &mut EventHandler<RawTerminal<Stdout>>,
) -> io::Result<String> {
self.read_line_with_init_buffer(prompt, handler, Buffer::new())
self.read_line_with_init_buffer(prompt, handler, f, Buffer::new())
}
/// Same as `Context.read_line()`, but passes the provided initial buffer to the editor.
......@@ -83,17 +86,19 @@ impl Context {
/// let line =
/// context.read_line_with_init_buffer("[prompt]$ ",
/// &mut |_| {},
/// Some(Box::new(|s| String::from(s))),
/// "some initial buffer");
/// ```
pub fn read_line_with_init_buffer<P: Into<String>, B: Into<Buffer>>(
&mut self,
prompt: P,
mut handler: &mut EventHandler<RawTerminal<Stdout>>,
f: Option<ColorClosure>,
buffer: B,
) -> io::Result<String> {
let res = {
let stdout = stdout().into_raw_mode()?;
let ed = Editor::new_with_init_buffer(stdout, prompt, self, buffer)?;
let ed = Editor::new_with_init_buffer(stdout, prompt, f, self, buffer)?;
match self.key_bindings {
KeyBindings::Emacs => Self::handle_keys(keymap::Emacs::new(ed), handler),
KeyBindings::Vi => Self::handle_keys(keymap::Vi::new(ed), handler),
......
......@@ -2,6 +2,7 @@ use std::cmp;
use std::io::{self, Write};
use termion::{self, clear, color, cursor};
use context::ColorClosure;
use Context;
use Buffer;
use event::*;
......@@ -60,6 +61,10 @@ pub struct Editor<'a, W: Write> {
out: W,
context: &'a mut Context,
// A closure that is evaluated just before we write to out.
// This allows us to do custom syntax highlighting and other fun stuff.
closure: Option<ColorClosure>,
// The location of the cursor. Note that the cursor does not lie on a char, but between chars.
// So, if `cursor == 0` then the cursor is before the first char,
// and if `cursor == 1` ten the cursor is after the first char and before the second char.
......@@ -106,13 +111,19 @@ macro_rules! cur_buf {
}
impl<'a, W: Write> Editor<'a, W> {
pub fn new<P: Into<String>>(out: W, prompt: P, context: &'a mut Context) -> io::Result<Self> {
Editor::new_with_init_buffer(out, prompt, context, Buffer::new())
pub fn new<P: Into<String>>(
out: W,
prompt: P,
f: Option<ColorClosure>,
context: &'a mut Context
) -> io::Result<Self> {
Editor::new_with_init_buffer(out, prompt, f, context, Buffer::new())
}
pub fn new_with_init_buffer<P: Into<String>, B: Into<Buffer>>(
out: W,
prompt: P,
f: Option<ColorClosure>,
context: &'a mut Context,
buffer: B,
) -> io::Result<Self> {
......@@ -120,6 +131,7 @@ impl<'a, W: Write> Editor<'a, W> {
prompt: prompt.into(),
cursor: 0,
out: out,
closure: f,
new_buf: buffer.into(),
cur_history_loc: None,
context: context,
......@@ -132,7 +144,6 @@ impl<'a, W: Write> Editor<'a, W> {
if !ed.new_buf.is_empty() {
ed.move_cursor_to_end_of_line()?;
}
try!(ed.display());
Ok(ed)
}
......@@ -227,7 +238,7 @@ impl<'a, W: Write> Editor<'a, W> {
// XXX wide character support
let max_word_size = completions.iter().fold(1, |m, x| max(m, x.chars().count()));
let cols = max(1, (w as usize / (max_word_size)));
let cols = max(1, w as usize / (max_word_size));
let col_width = 2 + w as usize / cols;
let cols = max(1, w as usize / col_width);
......@@ -726,7 +737,10 @@ impl<'a, W: Write> Editor<'a, W> {
buf_num_remaining_bytes = 0;
} else {
buf_num_remaining_bytes -= line.len();
write!(self.out, "{}", line)?;
match self.closure {
Some(ref f) => write!(self.out, "{}", f(line))?,
None => write!(self.out, "{}", line)?,
}
}
if i + 1 < lines.len() {
......@@ -797,7 +811,7 @@ mod tests {
fn delete_all_after_cursor_undo() {
let mut context = Context::new();
let out = Vec::new();
let mut ed = Editor::new(out, "prompt".to_owned(), &mut context).unwrap();
let mut ed = Editor::new(out, "prompt".to_owned(), None, &mut context).unwrap();
ed.insert_str_after_cursor("delete all of this").unwrap();
ed.move_cursor_to_start_of_line().unwrap();
ed.delete_all_after_cursor().unwrap();
......@@ -808,8 +822,9 @@ mod tests {
#[test]
fn move_cursor_left() {
let mut context = Context::new();
let closure = |s: &str| {String::from(s)};
let out = Vec::new();
let mut ed = Editor::new(out, "prompt".to_owned(), &mut context).unwrap();
let mut ed = Editor::new(out, "prompt".to_owned(), None, &mut context).unwrap();
ed.insert_str_after_cursor("let").unwrap();
assert_eq!(ed.cursor, 3);
......@@ -825,7 +840,7 @@ mod tests {
fn cursor_movement() {
let mut context = Context::new();
let out = Vec::new();
let mut ed = Editor::new(out, "prompt".to_owned(), &mut context).unwrap();
let mut ed = Editor::new(out, "prompt".to_owned(), None, &mut context).unwrap();
ed.insert_str_after_cursor("right").unwrap();
assert_eq!(ed.cursor, 5);
......@@ -838,7 +853,7 @@ mod tests {
fn delete_until_backwards() {
let mut context = Context::new();
let out = Vec::new();
let mut ed = Editor::new(out, "prompt".to_owned(), &mut context).unwrap();
let mut ed = Editor::new(out, "prompt".to_owned(), None, &mut context).unwrap();
ed.insert_str_after_cursor("right").unwrap();
assert_eq!(ed.cursor, 5);
......@@ -851,7 +866,7 @@ mod tests {
fn delete_until_forwards() {
let mut context = Context::new();
let out = Vec::new();
let mut ed = Editor::new(out, "prompt".to_owned(), &mut context).unwrap();
let mut ed = Editor::new(out, "prompt".to_owned(), None, &mut context).unwrap();
ed.insert_str_after_cursor("right").unwrap();
ed.cursor = 0;
......@@ -864,7 +879,7 @@ mod tests {
fn delete_until() {
let mut context = Context::new();
let out = Vec::new();
let mut ed = Editor::new(out, "prompt".to_owned(), &mut context).unwrap();
let mut ed = Editor::new(out, "prompt".to_owned(), None, &mut context).unwrap();
ed.insert_str_after_cursor("right").unwrap();
ed.cursor = 4;
......@@ -877,7 +892,7 @@ mod tests {
fn delete_until_inclusive() {
let mut context = Context::new();
let out = Vec::new();
let mut ed = Editor::new(out, "prompt".to_owned(), &mut context).unwrap();
let mut ed = Editor::new(out, "prompt".to_owned(), None, &mut context).unwrap();
ed.insert_str_after_cursor("right").unwrap();
ed.cursor = 4;
......
......@@ -211,7 +211,7 @@ mod tests {
fn enter_is_done() {
let mut context = Context::new();
let out = Vec::new();
let ed = Editor::new(out, "prompt".to_owned(), &mut context).unwrap();
let ed = Editor::new(out, "prompt".to_owned(), None, &mut context).unwrap();
let mut map = Emacs::new(ed);
map.ed.insert_str_after_cursor("done").unwrap();
assert_eq!(map.ed.cursor(), 4);
......@@ -226,7 +226,7 @@ mod tests {
fn move_cursor_left() {
let mut context = Context::new();
let out = Vec::new();
let ed = Editor::new(out, "prompt".to_owned(), &mut context).unwrap();
let ed = Editor::new(out, "prompt".to_owned(), None, &mut context).unwrap();
let mut map = Emacs::new(ed);
map.editor_mut().insert_str_after_cursor("let").unwrap();
assert_eq!(map.ed.cursor(), 3);
......@@ -241,7 +241,7 @@ mod tests {
fn move_word() {
let mut context = Context::new();
let out = Vec::new();
let ed = Editor::new(out, "prompt".to_owned(), &mut context).unwrap();
let ed = Editor::new(out, "prompt".to_owned(), None, &mut context).unwrap();
let mut map = Emacs::new(ed);
map.editor_mut().insert_str_after_cursor("abc def ghi").unwrap();
assert_eq!(map.ed.cursor(), 11);
......@@ -261,7 +261,7 @@ mod tests {
fn cursor_movement() {
let mut context = Context::new();
let out = Vec::new();
let ed = Editor::new(out, "prompt".to_owned(), &mut context).unwrap();
let ed = Editor::new(out, "prompt".to_owned(), None, &mut context).unwrap();
let mut map = Emacs::new(ed);
map.ed.insert_str_after_cursor("right").unwrap();
assert_eq!(map.ed.cursor(), 5);
......@@ -276,7 +276,7 @@ mod tests {
fn ctrl_h() {
let mut context = Context::new();
let out = Vec::new();
let ed = Editor::new(out, "prompt".to_owned(), &mut context).unwrap();
let ed = Editor::new(out, "prompt".to_owned(), None, &mut context).unwrap();
let mut map = Emacs::new(ed);
map.ed.insert_str_after_cursor("not empty").unwrap();
......
......@@ -99,7 +99,7 @@ mod tests {
fn ctrl_d_empty() {
let mut context = Context::new();
let out = Vec::new();
let ed = Editor::new(out, "prompt".to_owned(), &mut context).unwrap();
let ed = Editor::new(out, "prompt".to_owned(), None, &mut context).unwrap();
let mut map = TestKeyMap::new(ed);
let res = map.handle_key(Ctrl('d'), &mut |_| {});
......@@ -112,7 +112,7 @@ mod tests {
fn ctrl_d_non_empty() {
let mut context = Context::new();
let out = Vec::new();
let ed = Editor::new(out, "prompt".to_owned(), &mut context).unwrap();
let ed = Editor::new(out, "prompt".to_owned(), None, &mut context).unwrap();
let mut map = TestKeyMap::new(ed);
map.ed.insert_str_after_cursor("not empty").unwrap();
......@@ -125,7 +125,7 @@ mod tests {
fn ctrl_c() {
let mut context = Context::new();
let out = Vec::new();
let ed = Editor::new(out, "prompt".to_owned(), &mut context).unwrap();
let ed = Editor::new(out, "prompt".to_owned(), None, &mut context).unwrap();
let mut map = TestKeyMap::new(ed);
let res = map.handle_key(Ctrl('c'), &mut |_| {});
......
This diff is collapsed.
......@@ -80,4 +80,4 @@ pub fn remove_codes(input: &str) -> Cow<str> {
} else {
Cow::Borrowed(input)
}
}
}
\ No newline at end of file
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