Skip to main content

Editor

Struct Editor 

Source
pub struct Editor { /* private fields */ }
Expand description

The editing state of one input line: its text, the cursor, a kill buffer, and a bounded history of submitted lines.

Editor is the “line editing” part of a REPL with the terminal taken out. It holds no file descriptors and draws nothing. The host translates key events into Edit commands, calls apply, and redraws from line and column when apply reports a change. On Enter it calls submit, which hands back the finished line and records it in the history. That split makes the editor identical on every platform and testable without a terminal.

Positions are byte offsets into line and always fall on a char boundary, so slicing the line at cursor is always valid. The cursor moves in display units (a visible character plus any combining marks attached to it), so it never separates a letter from its accent.

§Memory

The line, the kill buffer, and every history slot are reused rather than reallocated. Once the history has filled to capacity, submitting a line recycles the oldest slot’s buffer, so a long session settles into editing and submitting without touching the allocator except to grow a buffer past its previous high-water mark.

§Examples

use repl_lang::{Edit, Editor};

let mut editor = Editor::new();
editor.insert("let x = 1");
assert_eq!(editor.submit(), "let x = 1");
assert_eq!(editor.line(), "");

// The submitted line is one step back in the history.
assert!(editor.apply(Edit::HistoryPrev));
assert_eq!(editor.line(), "let x = 1");

Implementations§

Source§

impl Editor

Source

pub const DEFAULT_HISTORY: usize = 1000

The number of history entries Editor::new keeps.

Source

pub fn new() -> Self

Creates an empty editor that keeps the last DEFAULT_HISTORY submitted lines.

§Examples
use repl_lang::Editor;

let editor = Editor::new();
assert_eq!(editor.line(), "");
assert_eq!(editor.cursor(), 0);
assert_eq!(editor.history().len(), 0);
Source

pub fn with_history(capacity: usize) -> Self

Creates an empty editor that keeps at most capacity history entries.

When the history is full, submitting a line drops the oldest entry. A capacity of 0 disables history: nothing is recorded and the history commands do nothing. No history memory is reserved up front; slots are allocated as lines are submitted, then recycled once the limit is reached.

§Examples
use repl_lang::Editor;

let mut editor = Editor::with_history(2);
for line in ["a", "b", "c"] {
    editor.insert(line);
    editor.submit();
}
// Only the two most recent lines are kept, oldest first.
assert!(editor.history().eq(["b", "c"]));
Source

pub fn line(&self) -> &str

The text of the line being edited.

§Examples
use repl_lang::Editor;

let mut editor = Editor::new();
editor.insert("1 + 2");
assert_eq!(editor.line(), "1 + 2");
Source

pub fn cursor(&self) -> usize

The cursor position as a byte offset into line.

The offset is always on a char boundary, so &line[..cursor] and &line[cursor..] are always valid. Use it to find the word under the cursor for completion; use column to place the terminal cursor.

§Examples
use repl_lang::{Edit, Editor};

let mut editor = Editor::new();
editor.insert("pri");
assert_eq!(editor.cursor(), 3);

// The text before the cursor is the prefix to complete.
let prefix = &editor.line()[..editor.cursor()];
assert_eq!(prefix, "pri");

assert!(editor.apply(Edit::Home));
assert_eq!(editor.cursor(), 0);
Source

pub fn column(&self) -> usize

The cursor’s position in terminal columns, counted from the start of the line.

This is the display width of the text before the cursor: wide characters (CJK ideographs, most emoji) count two columns and combining marks count none. After drawing the prompt and the line, a host moves the terminal cursor to prompt_width + column().

The width is computed on each call, in time linear in the text before the cursor.

§Examples
use repl_lang::Editor;

let mut editor = Editor::new();
editor.insert("x = 世界");
assert_eq!(editor.cursor(), 10); // bytes: 4 ASCII + 2 × 3
assert_eq!(editor.column(), 8);  // columns: 4 ASCII + 2 × 2
Source

pub fn apply(&mut self, edit: Edit) -> bool

Applies one editing command and reports whether the line or the cursor changed.

A command that cannot do anything — moving left at the start of the line, deleting at the end, yanking an empty kill buffer, inserting a control character — leaves the editor untouched and returns false, so the host can skip a redraw or ring the bell.

The kill commands replace the kill buffer with the text they remove; a kill that removes nothing leaves the buffer as it was.

§Examples
use repl_lang::{Edit, Editor};

let mut editor = Editor::new();
editor.insert("hello world");

assert!(editor.apply(Edit::KillWordLeft));
assert_eq!(editor.line(), "hello ");

assert!(editor.apply(Edit::Home));
assert!(editor.apply(Edit::Yank));
assert_eq!(editor.line(), "worldhello ");
assert_eq!(editor.cursor(), 5); // just past the yanked text

// Commands with nothing to do report `false`.
assert!(editor.apply(Edit::Home));
assert!(!editor.apply(Edit::Left));
assert!(!editor.apply(Edit::Insert('\t'))); // control characters are refused
Source

pub fn insert(&mut self, text: &str) -> bool

Inserts text at the cursor, as a paste would, and reports whether anything was inserted.

Control characters in text are dropped, exactly as Edit::Insert refuses them one at a time; everything else is inserted in a single pass. A host that receives a multi-line paste should split it into lines and submit them one by one.

§Examples
use repl_lang::{Edit, Editor};

let mut editor = Editor::new();
editor.insert("fn main() {}");
assert!(editor.apply(Edit::Left));
editor.insert(" body ");
assert_eq!(editor.line(), "fn main() { body }");

// Control characters are dropped from pasted text.
let mut editor = Editor::new();
editor.insert("a\tb\u{1b}c");
assert_eq!(editor.line(), "abc");
assert!(!editor.insert("\n"));
Source

pub fn clear(&mut self) -> bool

Empties the line and moves the cursor to the start, keeping the kill buffer and the history. Returns true if the line was not already empty.

This is the editor half of an interrupt (Ctrl-C): abandon what is being typed and start over. If the history was being browsed, browsing ends and the line parked when it began is dropped as well.

§Examples
use repl_lang::Editor;

let mut editor = Editor::new();
editor.insert("half-typed");
assert!(editor.clear());
assert_eq!(editor.line(), "");
assert!(!editor.clear());
Source

pub fn submit(&mut self) -> &str

Finishes the line: returns its text, records it in the history, and leaves the editor empty for the next line.

A line is recorded unless it is blank (empty or only whitespace) or identical to the most recent history entry, and only if the history capacity is not 0.

Submitting while browsing the history submits the recalled line as edited; the line parked when browsing began is dropped. Either way the next HistoryPrev starts from the newest entry.

The returned text stays borrowed from the editor, which keeps the buffer for reuse: the next submit recycles it as the following line.

§Examples
use repl_lang::Editor;

let mut editor = Editor::new();
editor.insert("1 + 1");
assert_eq!(editor.submit(), "1 + 1");

// Blank lines and immediate repeats are not recorded.
editor.insert("   ");
assert_eq!(editor.submit(), "   ");
editor.insert("1 + 1");
editor.submit();
assert_eq!(editor.history().len(), 1);
Source

pub fn add_history(&mut self, line: &str)

Records line in the history as if it had been submitted, without touching the line being edited.

Use it to restore a history saved from an earlier session. The same rules as submit apply: blank lines and repeats of the newest entry are skipped, and the oldest entry is dropped once the history is full. Control characters are removed first, exactly as insert removes them, so a recalled entry can never put into the line something that could not have been typed there.

§Examples
use repl_lang::{Edit, Editor};

let saved = "let x = 1\nx * 2\n";
let mut editor = Editor::new();
for line in saved.lines() {
    editor.add_history(line);
}
assert!(editor.apply(Edit::HistoryPrev));
assert_eq!(editor.line(), "x * 2");
Source

pub fn history( &self, ) -> impl DoubleEndedIterator<Item = &str> + ExactSizeIterator + '_

The history, oldest entry first.

Iterate it in reverse for newest-first, or write it out line by line to persist the history between sessions.

§Examples
use repl_lang::Editor;

let mut editor = Editor::new();
for line in ["first", "second"] {
    editor.insert(line);
    editor.submit();
}
assert_eq!(editor.history().next_back(), Some("second"));
let saved: Vec<&str> = editor.history().collect();
assert_eq!(saved, ["first", "second"]);

Trait Implementations§

Source§

impl Clone for Editor

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Editor

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Editor

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.