First draft of highlight support
diff --git a/examples/example.rs b/examples/example.rs
index 3e75024..ce41cf4 100644
--- a/examples/example.rs
+++ b/examples/example.rs
@@ -2,6 +2,7 @@
 extern crate rustyline;
 
 use log::{Level, LevelFilter, Metadata, Record, SetLoggerError};
+use std::borrow::Cow::{self, Borrowed, Owned};
 
 use rustyline::completion::{Completer, FilenameCompleter, Pair};
 use rustyline::error::ReadlineError;
@@ -9,13 +10,8 @@
 use rustyline::hint::Hinter;
 use rustyline::{Cmd, CompletionType, Config, EditMode, Editor, Helper, KeyPress};
 
-// On unix platforms you can use ANSI escape sequences
-#[cfg(unix)]
-static PROMPT: &'static str = "\x1b[1;32m>>\x1b[0m ";
+static COLORED_PROMPT: &'static str = "\x1b[1;32m>>\x1b[0m ";
 
-// Windows consoles typically don't support ANSI escape sequences out
-// of the box
-#[cfg(windows)]
 static PROMPT: &'static str = ">> ";
 
 struct MyHelper(FilenameCompleter);
@@ -31,18 +27,22 @@
 impl Hinter for MyHelper {
     fn hint(&self, line: &str, _pos: usize) -> Option<String> {
         if line == "hello" {
-            if cfg!(target_os = "windows") {
-                Some(" World".to_owned())
-            } else {
-                Some(" \x1b[1mWorld\x1b[m".to_owned())
-            }
+            Some(" World".to_owned())
         } else {
             None
         }
     }
 }
 
-impl Highlighter for MyHelper {}
+impl Highlighter for MyHelper {
+    fn highlight_prompt<'p>(&self, _: &str) -> Cow<'static, str> {
+        Borrowed(COLORED_PROMPT)
+    }
+
+    fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
+        Owned("\x1b[1m".to_owned() + hint + "\x1b[m")
+    }
+}
 
 impl Helper for MyHelper {}
 
diff --git a/src/edit.rs b/src/edit.rs
index 6a82da6..f9fcca8 100644
--- a/src/edit.rs
+++ b/src/edit.rs
@@ -7,6 +7,7 @@
 use unicode_width::UnicodeWidthChar;
 
 use super::Result;
+use highlight::Highlighter;
 use hint::Hinter;
 use history::{Direction, History};
 use keymap::{Anchor, At, CharSearch, Cmd, Movement, RepeatCount, Word};
@@ -30,6 +31,7 @@
     byte_buffer: [u8; 4],
     pub changes: Rc<RefCell<Changeset>>, // changes to line, for undo/redo
     pub hinter: Option<&'out Hinter>,
+    pub highlighter: Option<&'out Highlighter>,
 }
 
 impl<'out, 'prompt> State<'out, 'prompt> {
@@ -38,6 +40,7 @@
         prompt: &'prompt str,
         history_index: usize,
         hinter: Option<&'out Hinter>,
+        highlighter: Option<&'out Highlighter>,
     ) -> State<'out, 'prompt> {
         let capacity = MAX_LINE;
         let prompt_size = out.calculate_position(prompt, Position::default());
@@ -53,6 +56,7 @@
             byte_buffer: [0; 4],
             changes: Rc::new(RefCell::new(Changeset::new())),
             hinter,
+            highlighter,
         }
     }
 
@@ -96,7 +100,16 @@
         if self.cursor == cursor {
             return Ok(());
         }
-        try!(self.out.move_cursor(self.cursor, cursor));
+        if self.highlighter.map_or(false, |h| {
+            self.line
+                .grapheme_at_cursor()
+                .map_or(false, |s| h.highlight_char(s))
+        }) {
+            let prompt_size = self.prompt_size;
+            try!(self.refresh(self.prompt, prompt_size, None));
+        } else {
+            try!(self.out.move_cursor(self.cursor, cursor));
+        }
         self.cursor = cursor;
         Ok(())
     }
@@ -109,6 +122,7 @@
             hint,
             self.cursor.row,
             self.old_rows,
+            self.highlighter,
         ));
 
         self.cursor = cursor;
@@ -175,7 +189,8 @@
                 let hint = self.hint();
                 if n == 1
                     && self.cursor.col + ch.width().unwrap_or(0) < self.out.get_columns()
-                    && hint.is_none()
+                    && hint.is_none() // TODO refresh only current line
+                    && !self.highlighter.map_or(true, |h| h.highlight_char(ch.encode_utf8(&mut self.byte_buffer)))
                 {
                     // Avoid a full update of the line in the trivial case.
                     let cursor = self
@@ -490,6 +505,7 @@
         byte_buffer: [0; 4],
         changes: Rc::new(RefCell::new(Changeset::new())),
         hinter: None,
+        highlighter: None,
     }
 }
 
diff --git a/src/highlight.rs b/src/highlight.rs
index eee97ff..04bc042 100644
--- a/src/highlight.rs
+++ b/src/highlight.rs
@@ -1,3 +1,4 @@
+use config::CompletionType;
 ///! Syntax highlighting
 use std::borrow::Cow::{self, Borrowed};
 
@@ -5,22 +6,28 @@
 /// Rustyline will try to handle escape sequence for ansi color on windows
 /// when not supported natively (windows <10).
 ///
-/// TODO to be used
+/// Currently, the highlighted version *must* have the same display width as
+/// the original input.
 pub trait Highlighter {
     /// Takes the currently edited `line` with the cursor `pos`ition and
-    /// returns the highlighted version (with ANSI color)
-    /// and new cursor position which may have been shifted by highlighting.
+    /// returns the highlighted version (with ANSI color).
     ///
     /// For example, you can implement
     /// [blink-matching-paren](https://www.gnu.org/software/bash/manual/html_node/Readline-Init-File-Syntax.html).
-    fn highlight<'l>(&self, line: &'l str, pos: usize) -> (Cow<'l, str>, usize) {
-        (Borrowed(line), pos)
+    fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> {
+        let _ = pos;
+        Borrowed(line)
     }
     /// Takes the `prompt` and
     /// returns the highlighted version (with ANSI color).
     fn highlight_prompt<'p>(&self, prompt: &'p str) -> Cow<'p, str> {
         Borrowed(prompt)
     }
+    /// Takes the dynamic `prompt` and
+    /// returns the highlighted version (with ANSI color).
+    fn highlight_dynamic_prompt<'p>(&self, prompt: &'p str) -> Cow<'p, str> {
+        Borrowed(prompt)
+    }
     /// Takes the `hint` and
     /// returns the highlighted version (with ANSI color).
     fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
@@ -28,9 +35,25 @@
     }
     /// Takes the completion `canditate` and
     /// returns the highlighted version (with ANSI color).
-    fn highlight_candidate<'c>(&self, candidate: &'c str) -> Cow<'c, str> {
+    ///
+    /// Currently, used only with `CompletionType::List`.
+    fn highlight_candidate<'c>(
+        &self,
+        candidate: &'c str,
+        completion: CompletionType,
+    ) -> Cow<'c, str> {
+        let _ = completion;
         Borrowed(candidate)
     }
+    /// Tells if the `ch`ar needs to be highlighted when typed or when cursor
+    /// is moved under.
+    ///
+    /// Used to optimize refresh when a character is inserted or the cursor is
+    /// moved.
+    fn highlight_char(&self, grapheme: &str) -> bool {
+        let _ = grapheme;
+        false
+    }
 }
 
 impl Highlighter for () {}
diff --git a/src/lib.rs b/src/lib.rs
index 4f7627a..6c39388 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -77,6 +77,7 @@
     s: &mut State,
     input_state: &mut InputState,
     completer: &C,
+    highlighter: Option<&Highlighter>,
     config: &Config,
 ) -> Result<Option<Cmd>> {
     // get a list of completions
@@ -95,7 +96,14 @@
         loop {
             // Show completion or original buffer
             if i < candidates.len() {
-                completer.update(&mut s.line, start, candidates[i].replacement());
+                let candidate = candidates[i].replacement();
+                // TODO we can't highlight the line buffer directly
+                /*let candidate = if let Some(highlighter) = s.highlighter {
+                    highlighter.highlight_candidate(candidate, CompletionType::Circular)
+                } else {
+                    Borrowed(candidate)
+                };*/
+                completer.update(&mut s.line, start, candidate);
                 try!(s.refresh_line());
             } else {
                 // Restore current edited line
@@ -172,7 +180,7 @@
             true
         };
         if show_completions {
-            page_completions(rdr, s, input_state, &candidates)
+            page_completions(rdr, s, input_state, highlighter, &candidates)
         } else {
             try!(s.refresh_line());
             Ok(None)
@@ -186,6 +194,7 @@
     rdr: &mut R,
     s: &mut State,
     input_state: &mut InputState,
+    highlighter: Option<&Highlighter>,
     candidates: &[C],
 ) -> Result<Option<Cmd>> {
     use std::cmp;
@@ -240,8 +249,12 @@
             let i = (col * num_rows) + row;
             if i < candidates.len() {
                 let candidate = &candidates[i].display();
-                ab.push_str(candidate);
                 let width = candidate.width();
+                if let Some(highlighter) = highlighter {
+                    ab.push_str(&highlighter.highlight_candidate(candidate, CompletionType::List));
+                } else {
+                    ab.push_str(candidate);
+                }
                 if ((col + 1) * num_rows) + row < candidates.len() {
                     for _ in width..max_width {
                         ab.push(' ');
@@ -353,11 +366,22 @@
 ) -> Result<String> {
     let completer = editor.helper.as_ref();
     let hinter = editor.helper.as_ref().map(|h| h as &Hinter);
+    let highlighter = if editor.term.colors_enabled() {
+        editor.helper.as_ref().map(|h| h as &Highlighter)
+    } else {
+        None
+    };
 
     let mut stdout = editor.term.create_writer();
 
     editor.reset_kill_ring(); // TODO recreate a new kill ring vs Arc<Mutex<KillRing>>
-    let mut s = State::new(&mut stdout, prompt, editor.history.len(), hinter);
+    let mut s = State::new(
+        &mut stdout,
+        prompt,
+        editor.history.len(),
+        hinter,
+        highlighter,
+    );
     let mut input_state = InputState::new(&editor.config, Arc::clone(&editor.custom_bindings));
 
     s.line.set_delete_listener(editor.kill_ring.clone());
@@ -387,6 +411,7 @@
                 &mut s,
                 &mut input_state,
                 completer.unwrap(),
+                highlighter,
                 &editor.config,
             ));
             if next.is_some() {
@@ -650,6 +675,7 @@
     custom_bindings: Arc<RwLock<HashMap<KeyPress, Cmd>>>,
 }
 
+#[allow(new_without_default)]
 impl<H: Helper> Editor<H> {
     /// Create an editor with the default configuration
     pub fn new() -> Editor<H> {
diff --git a/src/line_buffer.rs b/src/line_buffer.rs
index 1c220ca..e17fd62 100644
--- a/src/line_buffer.rs
+++ b/src/line_buffer.rs
@@ -153,7 +153,7 @@
     }
 
     /// Returns the character at current cursor position.
-    fn grapheme_at_cursor(&self) -> Option<&str> {
+    pub(crate) fn grapheme_at_cursor(&self) -> Option<&str> {
         if self.pos == self.buf.len() {
             None
         } else {
diff --git a/src/test/mod.rs b/src/test/mod.rs
index f2d6ea2..1ce9881 100644
--- a/src/test/mod.rs
+++ b/src/test/mod.rs
@@ -45,6 +45,7 @@
         &mut s,
         &mut input_state,
         &completer,
+        None,
         &Config::default(),
     ).unwrap();
     assert_eq!(Some(Cmd::AcceptLine), cmd);
diff --git a/src/tty/mod.rs b/src/tty/mod.rs
index e96f676..e8c7db4 100644
--- a/src/tty/mod.rs
+++ b/src/tty/mod.rs
@@ -5,6 +5,7 @@
 
 use config::{ColorMode, Config};
 use consts::KeyPress;
+use highlight::Highlighter;
 use line_buffer::LineBuffer;
 use Result;
 
@@ -42,6 +43,7 @@
         hint: Option<String>,
         current_row: usize,
         old_rows: usize,
+        highlighter: Option<&Highlighter>,
     ) -> Result<(Position, Position)>;
 
     /// Calculate the number of columns and rows used to display `s` on a
@@ -86,8 +88,17 @@
         hint: Option<String>,
         current_row: usize,
         old_rows: usize,
+        highlighter: Option<&Highlighter>,
     ) -> Result<(Position, Position)> {
-        (**self).refresh_line(prompt, prompt_size, line, hint, current_row, old_rows)
+        (**self).refresh_line(
+            prompt,
+            prompt_size,
+            line,
+            hint,
+            current_row,
+            old_rows,
+            highlighter,
+        )
     }
 
     fn calculate_position(&self, s: &str, orig: Position) -> Position {
diff --git a/src/tty/test.rs b/src/tty/test.rs
index 4166948..752bae3 100644
--- a/src/tty/test.rs
+++ b/src/tty/test.rs
@@ -7,6 +7,7 @@
 use config::{ColorMode, Config};
 use consts::KeyPress;
 use error::ReadlineError;
+use highlight::Highlighter;
 use line_buffer::LineBuffer;
 use Result;
 
@@ -71,6 +72,7 @@
         hint: Option<String>,
         _: usize,
         _: usize,
+        _: Option<&Highlighter>,
     ) -> Result<(Position, Position)> {
         let cursor = self.calculate_position(&line[..line.pos()], prompt_size);
         if let Some(hint) = hint {
diff --git a/src/tty/unix.rs b/src/tty/unix.rs
index b99561a..5cc97e3 100644
--- a/src/tty/unix.rs
+++ b/src/tty/unix.rs
@@ -17,6 +17,7 @@
 use config::{ColorMode, Config};
 use consts::{self, KeyPress};
 use error;
+use highlight::Highlighter;
 use line_buffer::LineBuffer;
 use Result;
 
@@ -427,6 +428,7 @@
         hint: Option<String>,
         current_row: usize,
         old_rows: usize,
+        highlighter: Option<&Highlighter>,
     ) -> Result<(Position, Position)> {
         use std::fmt::Write;
         let mut ab = String::new();
@@ -450,13 +452,25 @@
         // clear the line
         ab.push_str("\r\x1b[0K");
 
-        // display the prompt
-        ab.push_str(prompt);
-        // display the input line
-        ab.push_str(line);
+        if let Some(highlighter) = highlighter {
+            // display the prompt
+            ab.push_str(&highlighter.highlight_prompt(prompt));
+            // display the input line
+            ab.push_str(&highlighter.highlight(line, line.pos()));
+        } else {
+            // display the prompt
+            ab.push_str(prompt);
+            // display the input line
+            ab.push_str(line);
+        }
         // display hint
         if let Some(hint) = hint {
-            ab.push_str(truncate(&hint, end_pos.col, self.cols));
+            let truncate = truncate(&hint, end_pos.col, self.cols);
+            if let Some(highlighter) = highlighter {
+                ab.push_str(&highlighter.highlight_hint(truncate));
+            } else {
+                ab.push_str(truncate);
+            }
         }
         // we have to generate our own newline on line wrap
         if end_pos.col == 0 && end_pos.row > 0 {
diff --git a/src/tty/windows.rs b/src/tty/windows.rs
index a27b9fb..210cefe 100644
--- a/src/tty/windows.rs
+++ b/src/tty/windows.rs
@@ -12,6 +12,7 @@
 use config::{ColorMode, Config};
 use consts::{self, KeyPress};
 use error;
+use highlight::Highlighter;
 use line_buffer::LineBuffer;
 use Result;
 
@@ -192,8 +193,7 @@
                     winuser::VK_F11 => return Ok(KeyPress::F(11)),
                     winuser::VK_F12 => return Ok(KeyPress::F(12)),
                     // winuser::VK_BACK is correctly handled because the key_event.UnicodeChar is
-                    // also
-                    // set.
+                    // also set.
                     _ => continue,
                 };
             } else if utf16 == 27 {
@@ -291,6 +291,7 @@
         hint: Option<String>,
         current_row: usize,
         old_rows: usize,
+        highlighter: Option<&Highlighter>,
     ) -> Result<(Position, Position)> {
         // calculate the position of the end of the input line
         let end_pos = self.calculate_position(line, prompt_size);
@@ -307,14 +308,26 @@
             info.dwCursorPosition,
         ));
         let mut ab = String::new();
-        // display the prompt
-        // TODO handle ansi escape code (SetConsoleTextAttribute)
-        ab.push_str(prompt);
-        // display the input line
-        ab.push_str(&line);
+        if let Some(highlighter) = highlighter {
+            // TODO handle ansi escape code (SetConsoleTextAttribute)
+            // display the prompt
+            ab.push_str(&highlighter.highlight_prompt(prompt));
+            // display the input line
+            ab.push_str(&highlighter.highlight(line, pos));
+        } else {
+            // display the prompt
+            ab.push_str(prompt);
+            // display the input line
+            ab.push_str(line);
+        }
         // display hint
         if let Some(hint) = hint {
-            ab.push_str(truncate(&hint, end_pos.col, self.cols));
+            let truncate = truncate(&hint, end_pos.col, self.cols);
+            if let Some(highlighter) = highlighter {
+                ab.push_str(&highlighter.highlight_hint(truncate));
+            } else {
+                ab.push_str(truncate);
+            }
         }
         try!(self.write_and_flush(ab.as_bytes()));
 
@@ -450,8 +463,9 @@
     }
 
     fn colors_enabled(&self) -> bool {
+        // TODO ANSI Colors & Windows <10
         match self.color_mode {
-            ColorMode::Enabled => self.stdout_isatty,
+            ColorMode::Enabled => self.stdout_isatty && self.ansi_colors_supported,
             ColorMode::Forced => true,
             ColorMode::Disabled => false,
         }