editor.rs 29.6 KB
Newer Older
1
use std::cmp;
MovingtoMars's avatar
MovingtoMars committed
2
use std::io::{self, Write};
3
use termion::{self, clear, color, cursor};
MovingtoMars's avatar
MovingtoMars committed
4

Sag0Sag0's avatar
Sag0Sag0 committed
5
use context::ColorClosure;
MovingtoMars's avatar
MovingtoMars committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
use Context;
use Buffer;
use event::*;
use util;

/// Represents the position of the cursor relative to words in the buffer.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CursorPosition {
    /// The cursor is in the word with the specified index.
    InWord(usize),

    /// The cursor is on the left edge of the word with the specified index.
    /// For example: `abc |hi`, where `|` is the cursor.
    OnWordLeftEdge(usize),

    /// The cursor is on the right edge of the word with the specified index.
    /// For example: `abc| hi`, where `|` is the cursor.
    OnWordRightEdge(usize),

    /// The cursor is not in contact with any word. Each `Option<usize>` specifies the index of the
    /// closest word to the left and right, respectively, or `None` if there is no word on that side.
    InSpace(Option<usize>, Option<usize>),
}

impl CursorPosition {
    pub fn get(cursor: usize, words: &[(usize, usize)]) -> CursorPosition {
        use CursorPosition::*;

        if words.len() == 0 {
            return InSpace(None, None);
        } else if cursor == words[0].0 {
            return OnWordLeftEdge(0);
        } else if cursor < words[0].0 {
            return InSpace(None, Some(0));
        }

        for (i, &(start, end)) in words.iter().enumerate() {
            if start == cursor {
                return OnWordLeftEdge(i);
            } else if end == cursor {
                return OnWordRightEdge(i);
            } else if start < cursor && cursor < end {
                return InWord(i);
            } else if cursor < start {
                return InSpace(Some(i - 1), Some(i));
            }
        }

        InSpace(Some(words.len() - 1), None)
    }
}

MovingtoMars's avatar
MovingtoMars committed
58
/// The core line editor. Displays and provides editing for history and the new buffer.
Jeremy Soller's avatar
Jeremy Soller committed
59
pub struct Editor<'a, W: Write> {
MovingtoMars's avatar
MovingtoMars committed
60 61 62
    prompt: String,
    out: W,
    context: &'a mut Context,
Sag0Sag0's avatar
Sag0Sag0 committed
63 64

    // A closure that is evaluated just before we write to out.
65
    // This allows us to do custom syntax highlighting and other fun stuff.
Sag0Sag0's avatar
Sag0Sag0 committed
66
    closure: Option<ColorClosure>,
MovingtoMars's avatar
MovingtoMars committed
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83

    // 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.
    cursor: usize,

    // Buffer for the new line (ie. not from editing history)
    new_buf: Buffer,

    // None if we're on the new buffer, else the index of history
    cur_history_loc: Option<usize>,

    // The line of the cursor relative to the prompt. 1-indexed.
    // So if the cursor is on the same line as the prompt, `term_cursor_line == 1`.
    // If the cursor is on the line below the prompt, `term_cursor_line == 2`.
    term_cursor_line: usize,

84 85
    // The next completion to suggest, or none
    show_completions_hint: Option<(Vec<String>, Option<usize>)>,
86

87 88 89
    // Show autosuggestions based on history
    show_autosuggestions: bool,

90 91 92
    // if set, the cursor will not be allow to move one past the end of the line, this is necessary
    // for Vi's normal mode.
    pub no_eol: bool,
MovingtoMars's avatar
MovingtoMars committed
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
}

macro_rules! cur_buf_mut {
    ($s:expr) => {
        match $s.cur_history_loc {
            Some(i) => &mut $s.context.history[i],
            _ => &mut $s.new_buf,
        }
    }
}

macro_rules! cur_buf {
    ($s:expr) => {
        match $s.cur_history_loc {
            Some(i) => &$s.context.history[i],
            _ => &$s.new_buf,
        }
    }
}

113
impl<'a, W: Write> Editor<'a, W> {
114
    pub fn new<P: Into<String>>(
115 116
        out: W,
        prompt: P,
Sag0Sag0's avatar
Sag0Sag0 committed
117
        f: Option<ColorClosure>,
Sag0Sag0's avatar
Sag0Sag0 committed
118
        context: &'a mut Context
119
    ) -> io::Result<Self> {
Sag0Sag0's avatar
Sag0Sag0 committed
120
        Editor::new_with_init_buffer(out, prompt, f, context, Buffer::new())
121 122
    }

123
    pub fn new_with_init_buffer<P: Into<String>, B: Into<Buffer>>(
124 125
        out: W,
        prompt: P,
Sag0Sag0's avatar
Sag0Sag0 committed
126
        f: Option<ColorClosure>,
127 128
        context: &'a mut Context,
        buffer: B,
129
    ) -> io::Result<Self> {
MovingtoMars's avatar
MovingtoMars committed
130
        let mut ed = Editor {
131
            prompt: prompt.into(),
MovingtoMars's avatar
MovingtoMars committed
132 133
            cursor: 0,
            out: out,
134
            closure: f,
135
            new_buf: buffer.into(),
MovingtoMars's avatar
MovingtoMars committed
136 137
            cur_history_loc: None,
            context: context,
138
            show_completions_hint: None,
139
            show_autosuggestions: true,
MovingtoMars's avatar
MovingtoMars committed
140
            term_cursor_line: 1,
141
            no_eol: false,
MovingtoMars's avatar
MovingtoMars committed
142 143
        };

144 145 146
        if !ed.new_buf.is_empty() {
            ed.move_cursor_to_end_of_line()?;
        }
MovingtoMars's avatar
MovingtoMars committed
147
        try!(ed.display());
MovingtoMars's avatar
MovingtoMars committed
148 149 150
        Ok(ed)
    }

151 152 153 154 155
    /// None if we're on the new buffer, else the index of history
    pub fn current_history_location(&self) -> Option<usize> {
        self.cur_history_loc
    }

MovingtoMars's avatar
MovingtoMars committed
156
    pub fn get_words_and_cursor_position(&self) -> (Vec<(usize, usize)>, CursorPosition) {
157
        let word_fn = &self.context.word_divider_fn;
MovingtoMars's avatar
MovingtoMars committed
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
        let words = word_fn(cur_buf!(self));
        let pos = CursorPosition::get(self.cursor, &words);
        (words, pos)
    }

    pub fn set_prompt(&mut self, prompt: String) {
        self.prompt = prompt;
    }

    pub fn context(&mut self) -> &mut Context {
        self.context
    }

    pub fn cursor(&self) -> usize {
        self.cursor
    }

MovingtoMars's avatar
MovingtoMars committed
175 176
    // XXX: Returning a bool to indicate doneness is a bit awkward, maybe change it
    pub fn handle_newline(&mut self) -> io::Result<bool> {
177 178 179 180 181
        if self.show_completions_hint.is_some() {
            self.show_completions_hint = None;
            return Ok(false);
        }

MovingtoMars's avatar
MovingtoMars committed
182 183 184 185 186 187
        let char_before_cursor = cur_buf!(self).char_before(self.cursor);
        if char_before_cursor == Some('\\') {
            // self.insert_after_cursor('\r')?;
            self.insert_after_cursor('\n')?;
            Ok(false)
        } else {
188 189
            self.cursor = cur_buf!(self).num_chars();
            self._display(false)?;
MovingtoMars's avatar
MovingtoMars committed
190
            try!(self.out.write(b"\r\n"));
191
            self.show_completions_hint = None;
MovingtoMars's avatar
MovingtoMars committed
192 193
            Ok(true)
        }
194
    }
195

196 197
    pub fn flush(&mut self) -> io::Result<()> {
        self.out.flush()
198 199
    }

MovingtoMars's avatar
MovingtoMars committed
200 201 202 203 204 205
    /// Attempts to undo an action on the current buffer.
    ///
    /// Returns `Ok(true)` if an action was undone.
    /// Returns `Ok(false)` if there was no action to undo.
    pub fn undo(&mut self) -> io::Result<bool> {
        let did = cur_buf_mut!(self).undo();
MovingtoMars's avatar
MovingtoMars committed
206 207 208
        if did {
            self.move_cursor_to_end_of_line()?;
        } else {
MovingtoMars's avatar
MovingtoMars committed
209
            self.display()?;
MovingtoMars's avatar
MovingtoMars committed
210
        }
MovingtoMars's avatar
MovingtoMars committed
211 212 213 214 215
        Ok(did)
    }

    pub fn redo(&mut self) -> io::Result<bool> {
        let did = cur_buf_mut!(self).redo();
MovingtoMars's avatar
MovingtoMars committed
216 217 218
        if did {
            self.move_cursor_to_end_of_line()?;
        } else {
MovingtoMars's avatar
MovingtoMars committed
219
            self.display()?;
MovingtoMars's avatar
MovingtoMars committed
220
        }
MovingtoMars's avatar
MovingtoMars committed
221 222 223 224 225
        Ok(did)
    }

    pub fn revert(&mut self) -> io::Result<bool> {
        let did = cur_buf_mut!(self).revert();
MovingtoMars's avatar
MovingtoMars committed
226 227 228
        if did {
            self.move_cursor_to_end_of_line()?;
        } else {
MovingtoMars's avatar
MovingtoMars committed
229
            self.display()?;
MovingtoMars's avatar
MovingtoMars committed
230
        }
MovingtoMars's avatar
MovingtoMars committed
231 232 233
        Ok(did)
    }

234
    fn print_completion_list(out: &mut W, completions: &[String], highlighted: Option<usize>) -> io::Result<usize> {
MovingtoMars's avatar
MovingtoMars committed
235 236 237 238 239 240
        use std::cmp::max;

        let (w, _) = try!(termion::terminal_size());

        // XXX wide character support
        let max_word_size = completions.iter().fold(1, |m, x| max(m, x.chars().count()));
241
        let cols = max(1, w as usize / (max_word_size));
MovingtoMars's avatar
MovingtoMars committed
242 243 244
        let col_width = 2 + w as usize / cols;
        let cols = max(1, w as usize / col_width);

245 246
        let mut lines = 0;

MovingtoMars's avatar
MovingtoMars committed
247
        let mut i = 0;
248
        for (index, com) in completions.iter().enumerate() {
MovingtoMars's avatar
MovingtoMars committed
249
            if i == cols {
250 251
                try!(write!(out, "\r\n"));
                lines += 1;
MovingtoMars's avatar
MovingtoMars committed
252 253 254 255 256
                i = 0;
            } else if i > cols {
                unreachable!()
            }

257 258 259 260 261 262 263
            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)));
            }
MovingtoMars's avatar
MovingtoMars committed
264 265 266 267

            i += 1;
        }

268
        Ok(lines)
MovingtoMars's avatar
MovingtoMars committed
269 270
    }

271
    pub fn skip_completions_hint(&mut self) {
272
        self.show_completions_hint = None;
273 274 275
    }

    pub fn complete(&mut self, handler: &mut EventHandler<W>) -> io::Result<()> {
MovingtoMars's avatar
MovingtoMars committed
276
        handler(Event::new(self, EventKind::BeforeComplete));
MovingtoMars's avatar
MovingtoMars committed
277

278 279 280 281 282 283 284 285 286 287 288 289 290
        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(());
        }

MovingtoMars's avatar
MovingtoMars committed
291 292 293 294 295 296 297 298 299 300 301 302
        let (word, completions) = {
            let word_range = self.get_word_before_cursor(false);
            let buf = cur_buf_mut!(self);

            let word = match word_range {
                Some((start, end)) => buf.range(start, end),
                None => "".into(),
            };

            if let Some(ref completer) = self.context.completer {
                let mut completions = completer.completions(word.as_ref());
                completions.sort();
Sehny's avatar
Sehny committed
303
                completions.dedup();
MovingtoMars's avatar
MovingtoMars committed
304 305 306 307 308 309 310 311
                (word, completions)
            } else {
                return Ok(());
            }
        };

        if completions.len() == 0 {
            // Do nothing.
312
            self.show_completions_hint = None;
MovingtoMars's avatar
MovingtoMars committed
313 314
            Ok(())
        } else if completions.len() == 1 {
315
            self.show_completions_hint = None;
MovingtoMars's avatar
MovingtoMars committed
316 317 318
            try!(self.delete_word_before_cursor(false));
            self.insert_str_after_cursor(completions[0].as_ref())
        } else {
319 320 321 322 323 324
            let common_prefix = util::find_longest_common_prefix(
                &completions
                    .iter()
                    .map(|x| x.chars().collect())
                    .collect::<Vec<Vec<char>>>()[..],
            );
MovingtoMars's avatar
MovingtoMars committed
325 326

            if let Some(p) = common_prefix {
MovingtoMars's avatar
MovingtoMars committed
327
                let s = p.iter().cloned().collect::<String>();
MovingtoMars's avatar
MovingtoMars committed
328 329 330 331 332 333 334

                if s.len() > word.len() && s.starts_with(&word[..]) {
                    try!(self.delete_word_before_cursor(false));
                    return self.insert_str_after_cursor(s.as_ref());
                }
            }

335 336
            self.show_completions_hint = Some((completions, None));
            try!(self.display());
MovingtoMars's avatar
MovingtoMars committed
337 338 339 340 341 342 343 344 345

            Ok(())
        }
    }

    fn get_word_before_cursor(&self, ignore_space_before_cursor: bool) -> Option<(usize, usize)> {
        let (words, pos) = self.get_words_and_cursor_position();
        match pos {
            CursorPosition::InWord(i) => Some(words[i]),
346 347 348 349 350
            CursorPosition::InSpace(Some(i), _) => if ignore_space_before_cursor {
                Some(words[i])
            } else {
                None
            },
MovingtoMars's avatar
MovingtoMars committed
351
            CursorPosition::InSpace(None, _) => None,
352 353 354 355 356
            CursorPosition::OnWordLeftEdge(i) => if ignore_space_before_cursor && i > 0 {
                Some(words[i - 1])
            } else {
                None
            },
MovingtoMars's avatar
MovingtoMars committed
357 358 359 360 361 362 363 364 365
            CursorPosition::OnWordRightEdge(i) => Some(words[i]),
        }
    }

    /// Deletes the word preceding the cursor.
    /// If `ignore_space_before_cursor` is true and there is space directly before the cursor,
    /// this method ignores that space until it finds a word.
    /// If `ignore_space_before_cursor` is false and there is space directly before the cursor,
    /// nothing is deleted.
366 367 368 369
    pub fn delete_word_before_cursor(
        &mut self,
        ignore_space_before_cursor: bool,
    ) -> io::Result<()> {
MovingtoMars's avatar
MovingtoMars committed
370 371 372 373
        if let Some((start, _)) = self.get_word_before_cursor(ignore_space_before_cursor) {
            let moved = cur_buf_mut!(self).remove(start, self.cursor);
            self.cursor -= moved;
        }
MovingtoMars's avatar
MovingtoMars committed
374
        self.display()
MovingtoMars's avatar
MovingtoMars committed
375 376 377 378
    }

    /// Clears the screen then prints the prompt and current buffer.
    pub fn clear(&mut self) -> io::Result<()> {
Jeremy Soller's avatar
Jeremy Soller committed
379
        try!(write!(self.out, "{}{}", clear::All, cursor::Goto(1, 1)));
MovingtoMars's avatar
MovingtoMars committed
380
        self.term_cursor_line = 1;
MovingtoMars's avatar
MovingtoMars committed
381
        self.display()
MovingtoMars's avatar
MovingtoMars committed
382 383 384 385 386 387 388 389
    }

    /// Move up (backwards) in history.
    pub fn move_up(&mut self) -> io::Result<()> {
        if let Some(i) = self.cur_history_loc {
            if i > 0 {
                self.cur_history_loc = Some(i - 1);
            } else {
MovingtoMars's avatar
MovingtoMars committed
390
                return self.display();
MovingtoMars's avatar
MovingtoMars committed
391 392 393 394 395
            }
        } else {
            if self.context.history.len() > 0 {
                self.cur_history_loc = Some(self.context.history.len() - 1);
            } else {
MovingtoMars's avatar
MovingtoMars committed
396
                return self.display();
MovingtoMars's avatar
MovingtoMars committed
397 398 399
            }
        }

MovingtoMars's avatar
MovingtoMars committed
400
        self.move_cursor_to_end_of_line()
MovingtoMars's avatar
MovingtoMars committed
401 402 403 404 405 406 407 408 409 410
    }

    /// Move down (forwards) in history, or to the new buffer if we reach the end of history.
    pub fn move_down(&mut self) -> io::Result<()> {
        if let Some(i) = self.cur_history_loc {
            if i < self.context.history.len() - 1 {
                self.cur_history_loc = Some(i + 1);
            } else {
                self.cur_history_loc = None;
            }
MovingtoMars's avatar
MovingtoMars committed
411
            self.move_cursor_to_end_of_line()
MovingtoMars's avatar
MovingtoMars committed
412
        } else {
MovingtoMars's avatar
MovingtoMars committed
413
            self.display()
MovingtoMars's avatar
MovingtoMars committed
414 415 416 417 418 419 420
        }
    }

    /// Moves to the start of history (ie. the earliest history entry).
    pub fn move_to_start_of_history(&mut self) -> io::Result<()> {
        if self.context.history.len() > 0 {
            self.cur_history_loc = Some(0);
MovingtoMars's avatar
MovingtoMars committed
421
            self.move_cursor_to_end_of_line()
MovingtoMars's avatar
MovingtoMars committed
422 423
        } else {
            self.cur_history_loc = None;
MovingtoMars's avatar
MovingtoMars committed
424
            self.display()
MovingtoMars's avatar
MovingtoMars committed
425 426 427 428 429 430 431
        }
    }

    /// Moves to the end of history (ie. the new buffer).
    pub fn move_to_end_of_history(&mut self) -> io::Result<()> {
        if self.cur_history_loc.is_some() {
            self.cur_history_loc = None;
MovingtoMars's avatar
MovingtoMars committed
432
            self.move_cursor_to_end_of_line()
MovingtoMars's avatar
MovingtoMars committed
433
        } else {
MovingtoMars's avatar
MovingtoMars committed
434
            self.display()
MovingtoMars's avatar
MovingtoMars committed
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
        }
    }

    /// Inserts a string directly after the cursor, moving the cursor to the right.
    ///
    /// Note: it is more efficient to call `insert_chars_after_cursor()` directly.
    pub fn insert_str_after_cursor(&mut self, s: &str) -> io::Result<()> {
        self.insert_chars_after_cursor(&s.chars().collect::<Vec<char>>()[..])
    }

    /// Inserts a character directly after the cursor, moving the cursor to the right.
    pub fn insert_after_cursor(&mut self, c: char) -> io::Result<()> {
        self.insert_chars_after_cursor(&[c])
    }

    /// Inserts characters directly after the cursor, moving the cursor to the right.
    pub fn insert_chars_after_cursor(&mut self, cs: &[char]) -> io::Result<()> {
        {
            let buf = cur_buf_mut!(self);
            buf.insert(self.cursor, cs);
        }

        self.cursor += cs.len();
MovingtoMars's avatar
MovingtoMars committed
458
        self.display()
MovingtoMars's avatar
MovingtoMars committed
459 460 461 462 463 464 465 466 467 468 469
    }

    /// Deletes the character directly before the cursor, moving the cursor to the left.
    /// If the cursor is at the start of the line, nothing happens.
    pub fn delete_before_cursor(&mut self) -> io::Result<()> {
        if self.cursor > 0 {
            let buf = cur_buf_mut!(self);
            buf.remove(self.cursor - 1, self.cursor);
            self.cursor -= 1;
        }

MovingtoMars's avatar
MovingtoMars committed
470
        self.display()
MovingtoMars's avatar
MovingtoMars committed
471 472 473 474 475 476 477 478 479 480 481 482
    }

    /// Deletes the character directly after the cursor. The cursor does not move.
    /// If the cursor is at the end of the line, nothing happens.
    pub fn delete_after_cursor(&mut self) -> io::Result<()> {
        {
            let buf = cur_buf_mut!(self);

            if self.cursor < buf.num_chars() {
                buf.remove(self.cursor, self.cursor + 1);
            }
        }
MovingtoMars's avatar
MovingtoMars committed
483
        self.display()
MovingtoMars's avatar
MovingtoMars committed
484 485
    }

486
    /// Deletes every character preceding the cursor until the beginning of the line.
487
    pub fn delete_all_before_cursor(&mut self) -> io::Result<()> {
488 489
        cur_buf_mut!(self).remove(0, self.cursor);
        self.cursor = 0;
MovingtoMars's avatar
MovingtoMars committed
490
        self.display()
491 492
    }

MovingtoMars's avatar
MovingtoMars committed
493 494 495 496 497 498
    /// Deletes every character after the cursor until the end of the line.
    pub fn delete_all_after_cursor(&mut self) -> io::Result<()> {
        {
            let buf = cur_buf_mut!(self);
            buf.truncate(self.cursor);
        }
MovingtoMars's avatar
MovingtoMars committed
499
        self.display()
MovingtoMars's avatar
MovingtoMars committed
500 501
    }

502 503 504 505
    /// Deletes every character from the cursor until the given position.
    pub fn delete_until(&mut self, position: usize) -> io::Result<()> {
        {
            let buf = cur_buf_mut!(self);
506 507 508 509
            buf.remove(
                cmp::min(self.cursor, position),
                cmp::max(self.cursor, position),
            );
510 511
            self.cursor = cmp::min(self.cursor, position);
        }
MovingtoMars's avatar
MovingtoMars committed
512
        self.display()
513 514
    }

515 516 517 518
    /// Deletes every character from the cursor until the given position, inclusive.
    pub fn delete_until_inclusive(&mut self, position: usize) -> io::Result<()> {
        {
            let buf = cur_buf_mut!(self);
519 520 521 522
            buf.remove(
                cmp::min(self.cursor, position),
                cmp::max(self.cursor + 1, position + 1),
            );
523 524
            self.cursor = cmp::min(self.cursor, position);
        }
MovingtoMars's avatar
MovingtoMars committed
525
        self.display()
526 527
    }

MovingtoMars's avatar
MovingtoMars committed
528 529 530 531 532 533 534 535 536
    /// Moves the cursor to the left by `count` characters.
    /// The cursor will not go past the start of the buffer.
    pub fn move_cursor_left(&mut self, mut count: usize) -> io::Result<()> {
        if count > self.cursor {
            count = self.cursor;
        }

        self.cursor -= count;

MovingtoMars's avatar
MovingtoMars committed
537
        self.display()
MovingtoMars's avatar
MovingtoMars committed
538 539 540 541 542 543 544 545 546 547 548 549 550 551 552
    }

    /// Moves the cursor to the right by `count` characters.
    /// The cursor will not go past the end of the buffer.
    pub fn move_cursor_right(&mut self, mut count: usize) -> io::Result<()> {
        {
            let buf = cur_buf!(self);

            if count > buf.num_chars() - self.cursor {
                count = buf.num_chars() - self.cursor;
            }

            self.cursor += count;
        }

MovingtoMars's avatar
MovingtoMars committed
553
        self.display()
MovingtoMars's avatar
MovingtoMars committed
554 555 556 557 558 559 560 561 562
    }

    /// Moves the cursor to `pos`. If `pos` is past the end of the buffer, it will be clamped.
    pub fn move_cursor_to(&mut self, pos: usize) -> io::Result<()> {
        self.cursor = pos;
        let buf_len = cur_buf!(self).num_chars();
        if self.cursor > buf_len {
            self.cursor = buf_len;
        }
MovingtoMars's avatar
MovingtoMars committed
563
        self.display()
MovingtoMars's avatar
MovingtoMars committed
564 565 566 567 568
    }

    /// Moves the cursor to the start of the line.
    pub fn move_cursor_to_start_of_line(&mut self) -> io::Result<()> {
        self.cursor = 0;
MovingtoMars's avatar
MovingtoMars committed
569
        self.display()
MovingtoMars's avatar
MovingtoMars committed
570 571 572 573 574
    }

    /// Moves the cursor to the end of the line.
    pub fn move_cursor_to_end_of_line(&mut self) -> io::Result<()> {
        self.cursor = cur_buf!(self).num_chars();
MovingtoMars's avatar
MovingtoMars committed
575
        self.display()
MovingtoMars's avatar
MovingtoMars committed
576 577
    }

578 579 580 581 582 583 584 585 586
    pub fn cursor_is_at_end_of_line(&self) -> bool {
        let num_chars = cur_buf!(self).num_chars();
        if self.no_eol {
            self.cursor == num_chars - 1
        } else {
            self.cursor == num_chars
        }
    }

MovingtoMars's avatar
MovingtoMars committed
587 588 589 590 591 592
    ///  Returns a reference to the current buffer being edited.
    /// This may be the new buffer or a buffer from history.
    pub fn current_buffer(&self) -> &Buffer {
        cur_buf!(self)
    }

593 594 595 596 597 598
    ///  Returns a mutable reference to the current buffer being edited.
    /// This may be the new buffer or a buffer from history.
    pub fn current_buffer_mut(&mut self) -> &mut Buffer {
        cur_buf_mut!(self)
    }

Sehny's avatar
Sehny committed
599 600
    /// Accept autosuggestion and copy its content into current buffer
    pub fn accept_autosuggestion(&mut self) -> io::Result<()> {
601
        if self.show_autosuggestions {
602 603
            {
                let autosuggestion = self.current_autosuggestion().cloned();
604
                let buf = self.current_buffer_mut();
605 606 607
                if let Some(x) = autosuggestion {
                    buf.insert_from_buffer(&x);
                }
Sehny's avatar
Sehny committed
608 609
            }
        }
MovingtoMars's avatar
MovingtoMars committed
610
        self.move_cursor_to_end_of_line()
Sehny's avatar
Sehny committed
611 612
    }

613 614 615
    pub fn current_autosuggestion(&self) -> Option<&Buffer> {
        if self.show_autosuggestions {
            self.context
MovingtoMars's avatar
rustfmt  
MovingtoMars committed
616 617
                .history
                .get_newest_match(self.cur_history_loc, self.current_buffer())
618 619 620 621 622
        } else {
            None
        }
    }

623
    pub fn is_currently_showing_autosuggestion(&self) -> bool {
624
        self.current_autosuggestion().is_some()
625 626
    }

627
    fn _display(&mut self, show_autosuggest: bool) -> io::Result<()> {
MovingtoMars's avatar
MovingtoMars committed
628 629 630 631 632 633 634 635 636 637 638 639 640
        fn calc_width(prompt_width: usize, buf_widths: Vec<usize>, terminal_width: usize) -> usize {
            let mut total = 0;

            for line in buf_widths {
                if total % terminal_width != 0 {
                    total = ((total / terminal_width) + 1) * terminal_width;
                }

                total += prompt_width + line;
            }

            total
        }
MovingtoMars's avatar
MovingtoMars committed
641

642 643 644 645
        let (w, _) =
            // when testing hardcode terminal size values
            if cfg!(test) { (80, 24) }
            // otherwise pull values from termion
646 647 648 649 650 651 652 653
            else {
                let (mut size_col, mut size_row) = try!(termion::terminal_size());
                if size_col == 0 {
                    size_col = 80;
                    size_row = 24;
                }
                (size_col, size_row)
            };
MovingtoMars's avatar
MovingtoMars committed
654
        let w = w as usize;
MovingtoMars's avatar
MovingtoMars committed
655

MovingtoMars's avatar
MovingtoMars committed
656
        let prompt_width = util::width(&self.prompt);
657

MovingtoMars's avatar
MovingtoMars committed
658 659 660
        let buf = cur_buf!(self);
        let buf_width = buf.width();

661 662 663 664 665 666 667 668 669 670 671 672 673
        // Don't let the cursor go over the end!
        let buf_num_chars = buf.num_chars();
        if buf_num_chars < self.cursor {
            self.cursor = buf_num_chars;
        }

        // Can't move past the last character in vi normal mode
        if self.no_eol {
            if self.cursor >= 1 && self.cursor == buf_num_chars {
                self.cursor -= 1;
            }
        }

MovingtoMars's avatar
MovingtoMars committed
674 675 676 677 678 679 680 681 682 683 684 685
        // Width of the current buffer lines (including autosuggestion)
        let buf_widths = match self.current_autosuggestion() {
            Some(suggestion) => suggestion.width(),
            None => buf_width,
        };
        // Width of the current buffer lines (including autosuggestion) from the start to the cursor
        let buf_widths_to_cursor = match self.current_autosuggestion() {
            Some(suggestion) => suggestion.range_width(0, self.cursor),
            None => buf.range_width(0, self.cursor),
        };

        // Total number of terminal spaces taken up by prompt and buffer
MovingtoMars's avatar
MovingtoMars committed
686 687
        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);
MovingtoMars's avatar
MovingtoMars committed
688

689
        let new_num_lines = (new_total_width + w) / w;
MovingtoMars's avatar
MovingtoMars committed
690 691 692

        // Move the term cursor to the same line as the prompt.
        if self.term_cursor_line > 1 {
693 694 695 696 697
            try!(write!(
                self.out,
                "{}",
                cursor::Up(self.term_cursor_line as u16 - 1)
            ));
MovingtoMars's avatar
MovingtoMars committed
698
        }
699 700 701 702 703 704
        // 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() {
705
            completion_lines = 1 + try!(Self::print_completion_list(&mut self.out, completions, *i));
706 707 708 709 710
            try!(write!(self.out, "\r\n"));
        }

        // Write the prompt
        try!(write!(self.out, "{}", self.prompt));
MovingtoMars's avatar
MovingtoMars committed
711

MovingtoMars's avatar
MovingtoMars committed
712 713 714 715
        // 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).
        // Then, we loop and subtract from that number until it's 0, in which case we are printing
        // the autosuggestion from here on (in a different color).
716 717 718 719
        let lines = if show_autosuggest {
            match self.current_autosuggestion() {
                Some(suggestion) => suggestion.lines(),
                None => buf.lines(),
720
            }
721 722
        } else {
            buf.lines()
MovingtoMars's avatar
MovingtoMars committed
723 724
        };
        let mut buf_num_remaining_bytes = buf.num_bytes();
725

MovingtoMars's avatar
MovingtoMars committed
726 727
        for (i, line) in lines.iter().enumerate() {
            if i > 0 {
MovingtoMars's avatar
MovingtoMars committed
728
                try!(write!(self.out, "{}", cursor::Right(prompt_width as u16)));
MovingtoMars's avatar
MovingtoMars committed
729 730 731 732 733
            }

            if buf_num_remaining_bytes == 0 {
                write!(self.out, "{}", line)?;
            } else if line.len() > buf_num_remaining_bytes {
734 735 736 737 738
                let start = &line[..buf_num_remaining_bytes];
                match self.closure {
                    Some(ref f) => write!(self.out, "{}", f(start))?,
                    None => write!(self.out, "{}", start)?,
                }
739
                write!(self.out, "{}", color::Fg(color::Yellow))?;
MovingtoMars's avatar
MovingtoMars committed
740 741 742 743
                write!(self.out, "{}", &line[buf_num_remaining_bytes..])?;
                buf_num_remaining_bytes = 0;
            } else {
                buf_num_remaining_bytes -= line.len();
Sag0Sag0's avatar
Sag0Sag0 committed
744 745 746 747
                match self.closure {
                    Some(ref f) => write!(self.out, "{}", f(line))?,
                    None => write!(self.out, "{}", line)?,
                }
MovingtoMars's avatar
MovingtoMars committed
748 749 750 751
            }

            if i + 1 < lines.len() {
                write!(self.out, "\r\n")?;
752
            }
753 754
        }

MovingtoMars's avatar
MovingtoMars committed
755 756 757 758 759 760
        if self.is_currently_showing_autosuggestion() {
            write!(self.out, "{}", color::Fg(color::Reset))?;
        }

        // at the end of the line, move the cursor down a line
        if new_total_width % w == 0 {
761 762
            try!(write!(self.out, "\r\n"));
        }
MovingtoMars's avatar
MovingtoMars committed
763

MovingtoMars's avatar
MovingtoMars committed
764
        self.term_cursor_line = (new_total_width_to_cursor + w) / w;
MovingtoMars's avatar
MovingtoMars committed
765

MovingtoMars's avatar
MovingtoMars committed
766 767 768 769 770 771 772 773
        // The term cursor is now on the bottom line. We may need to move the term cursor up
        // to the line where the true cursor is.
        let cursor_line_diff = new_num_lines as isize - self.term_cursor_line as isize;
        if cursor_line_diff > 0 {
            try!(write!(self.out, "{}", cursor::Up(cursor_line_diff as u16)));
        } else if cursor_line_diff < 0 {
            unreachable!();
        }
MovingtoMars's avatar
MovingtoMars committed
774

MovingtoMars's avatar
MovingtoMars committed
775 776
        // Now that we are on the right line, we must move the term cursor left or right
        // to match the true cursor.
777 778
        let cursor_col_diff = new_total_width as isize - new_total_width_to_cursor as isize -
            cursor_line_diff * w as isize;
MovingtoMars's avatar
MovingtoMars committed
779 780 781
        if cursor_col_diff > 0 {
            try!(write!(self.out, "{}", cursor::Left(cursor_col_diff as u16)));
        } else if cursor_col_diff < 0 {
782 783 784 785 786
            try!(write!(
                self.out,
                "{}",
                cursor::Right((-cursor_col_diff) as u16)
            ));
MovingtoMars's avatar
MovingtoMars committed
787 788
        }

789 790
        self.term_cursor_line += completion_lines;

MovingtoMars's avatar
MovingtoMars committed
791 792
        self.out.flush()
    }
793 794 795 796 797

    /// Deletes the displayed prompt and buffer, replacing them with the current prompt and buffer
    pub fn display(&mut self) -> io::Result<()> {
        self._display(true)
    }
MovingtoMars's avatar
MovingtoMars committed
798 799
}

Jeremy Soller's avatar
Jeremy Soller committed
800
impl<'a, W: Write> From<Editor<'a, W>> for String {
MovingtoMars's avatar
MovingtoMars committed
801 802
    fn from(ed: Editor<'a, W>) -> String {
        match ed.cur_history_loc {
803 804 805
            Some(i) => ed.context.history[i].clone(),
            _ => ed.new_buf,
        }.into()
MovingtoMars's avatar
MovingtoMars committed
806 807
    }
}
808 809 810 811 812 813

#[cfg(test)]
mod tests {
    use super::*;
    use Context;

814 815 816 817 818
    #[test]
    /// test undoing delete_all_after_cursor
    fn delete_all_after_cursor_undo() {
        let mut context = Context::new();
        let out = Vec::new();
Sag0Sag0's avatar
Sag0Sag0 committed
819
        let mut ed = Editor::new(out, "prompt".to_owned(), None, &mut context).unwrap();
820 821 822 823 824 825 826
        ed.insert_str_after_cursor("delete all of this").unwrap();
        ed.move_cursor_to_start_of_line().unwrap();
        ed.delete_all_after_cursor().unwrap();
        ed.undo().unwrap();
        assert_eq!(String::from(ed), "delete all of this");
    }

827 828 829
    #[test]
    fn move_cursor_left() {
        let mut context = Context::new();
Sag0Sag0's avatar
Sag0Sag0 committed
830
        let closure = |s: &str| {String::from(s)};
831
        let out = Vec::new();
Sag0Sag0's avatar
Sag0Sag0 committed
832
        let mut ed = Editor::new(out, "prompt".to_owned(), None, &mut context).unwrap();
833 834 835
        ed.insert_str_after_cursor("let").unwrap();
        assert_eq!(ed.cursor, 3);

836 837
        ed.move_cursor_left(1).unwrap();
        assert_eq!(ed.cursor, 2);
838

839
        ed.insert_after_cursor('f').unwrap();
840 841 842
        assert_eq!(ed.cursor, 3);
        assert_eq!(String::from(ed), "left");
    }
843 844 845 846 847

    #[test]
    fn cursor_movement() {
        let mut context = Context::new();
        let out = Vec::new();
Sag0Sag0's avatar
Sag0Sag0 committed
848
        let mut ed = Editor::new(out, "prompt".to_owned(), None, &mut context).unwrap();
849 850 851
        ed.insert_str_after_cursor("right").unwrap();
        assert_eq!(ed.cursor, 5);

852 853
        ed.move_cursor_left(2).unwrap();
        ed.move_cursor_right(1).unwrap();
854 855
        assert_eq!(ed.cursor, 4);
    }
856 857 858 859 860

    #[test]
    fn delete_until_backwards() {
        let mut context = Context::new();
        let out = Vec::new();
Sag0Sag0's avatar
Sag0Sag0 committed
861
        let mut ed = Editor::new(out, "prompt".to_owned(), None, &mut context).unwrap();
862 863 864 865 866 867 868 869 870 871 872 873
        ed.insert_str_after_cursor("right").unwrap();
        assert_eq!(ed.cursor, 5);

        ed.delete_until(0).unwrap();
        assert_eq!(ed.cursor, 0);
        assert_eq!(String::from(ed), "");
    }

    #[test]
    fn delete_until_forwards() {
        let mut context = Context::new();
        let out = Vec::new();
Sag0Sag0's avatar
Sag0Sag0 committed
874
        let mut ed = Editor::new(out, "prompt".to_owned(), None, &mut context).unwrap();
875 876 877 878 879 880 881 882 883 884 885 886
        ed.insert_str_after_cursor("right").unwrap();
        ed.cursor = 0;

        ed.delete_until(5).unwrap();
        assert_eq!(ed.cursor, 0);
        assert_eq!(String::from(ed), "");
    }

    #[test]
    fn delete_until() {
        let mut context = Context::new();
        let out = Vec::new();
Sag0Sag0's avatar
Sag0Sag0 committed
887
        let mut ed = Editor::new(out, "prompt".to_owned(), None, &mut context).unwrap();
888 889 890 891 892 893 894
        ed.insert_str_after_cursor("right").unwrap();
        ed.cursor = 4;

        ed.delete_until(1).unwrap();
        assert_eq!(ed.cursor, 1);
        assert_eq!(String::from(ed), "rt");
    }
895 896 897 898 899

    #[test]
    fn delete_until_inclusive() {
        let mut context = Context::new();
        let out = Vec::new();
Sag0Sag0's avatar
Sag0Sag0 committed
900
        let mut ed = Editor::new(out, "prompt".to_owned(), None, &mut context).unwrap();
901 902 903 904 905 906 907
        ed.insert_str_after_cursor("right").unwrap();
        ed.cursor = 4;

        ed.delete_until_inclusive(1).unwrap();
        assert_eq!(ed.cursor, 1);
        assert_eq!(String::from(ed), "r");
    }
908
}