wsb-daemon 0.275.7622

wsb's daemon: the workbench, the GitHub mirror's schedule and peer continuity, for every workspace on the machine
use super::*;

fn at(line: u32, character: u32) -> Position {
    Position { line, character }
}

fn edit(start: (u32, u32), end: (u32, u32), text: &str) -> Change {
    Change { range: Some(Range { start: at(start.0, start.1), end: at(end.0, end.1) }), text: text.to_owned() }
}

/// TEST20072: positions count UTF-16 code units, as the protocol and Monaco both do.
///
/// An emoji is two units and one character; a CJK character one unit and three bytes. A count
/// in bytes or in characters puts every edit after one of them in the wrong place, and the
/// server then answers about text nobody has.
#[test]
fn test20072_positions_are_utf16_code_units() {
    let text = "a😀b\n漢字x\n";
    assert_eq!(offset(text, at(0, 3)).unwrap(), "a😀".len(), "after the emoji is unit 3");
    assert_eq!(offset(text, at(1, 2)).unwrap(), "a😀b\n漢字".len());
    assert_eq!(apply(text, &[edit((0, 3), (0, 4), "B")]).unwrap(), "a😀B\n漢字x\n");
    assert!(offset(text, at(0, 2)).unwrap_err().contains("inside a character"), "unit 2 splits the emoji");
    for o in [0, 1, "a😀".len(), "a😀b\n漢".len(), text.len()] {
        assert_eq!(offset(text, position(text, o).unwrap()).unwrap(), o, "round trip at byte {o}");
    }
}

/// TEST20073: every line ending the protocol knows ends a line: `\n`, `\r\n` and a lone `\r`.
#[test]
fn test20073_every_line_ending_ends_a_line() {
    let text = "one\r\ntwo\rthree\nfour";
    assert_eq!(offset(text, at(1, 0)).unwrap(), 5);
    assert_eq!(offset(text, at(2, 0)).unwrap(), 9);
    assert_eq!(offset(text, at(3, 4)).unwrap(), text.len());
    // A line's end is before its terminator, not inside it.
    assert_eq!(offset(text, at(0, 3)).unwrap(), 3);
    assert!(offset(text, at(0, 4)).is_err(), "the \\r\\n of line 0 is not part of it");
    assert_eq!(apply(text, &[edit((1, 3), (2, 0), " ")]).unwrap(), "one\r\ntwo three\nfour");
}

/// TEST20074: a change outside the text is refused, never clamped into one nobody made.
#[test]
fn test20074_a_change_outside_the_text_is_refused() {
    let text = "ab\ncd";
    assert!(apply(text, &[edit((2, 0), (2, 0), "x")]).unwrap_err().contains("past the end"));
    assert!(apply(text, &[edit((0, 5), (0, 5), "x")]).unwrap_err().contains("past the end of line 0"));
    assert!(apply(text, &[edit((1, 1), (0, 1), "x")]).unwrap_err().contains("ends before it starts"));
}

/// TEST20075: changes apply one after another, each to the text the last one left — as the
/// page sends them: Monaco's last range first.
#[test]
fn test20075_changes_apply_in_order() {
    // One Monaco event typing at two cursors, ranges in the text before the event, sent last
    // range first.
    let text = "ab\ncd";
    let changes = [edit((1, 1), (1, 1), "Y"), edit((0, 1), (0, 1), "X")];
    assert_eq!(apply(text, &changes).unwrap(), "aXb\ncYd");
    // Whole-text change, then an edit against it.
    let changes = [Change { range: None, text: "new".into() }, edit((0, 3), (0, 3), "!")];
    assert_eq!(apply(text, &changes).unwrap(), "new!");
}