json-joy
Version:
Collection of libraries for building collaborative editing apps.
113 lines (112 loc) • 4.29 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Cursor = void 0;
const json_crdt_patch_1 = require("../../../json-crdt-patch");
const constants_1 = require("../slice/constants");
const PersistedSlice_1 = require("../slice/PersistedSlice");
/**
* Cursor is a slice that represents an explicitly highlighted place in the
* text to the user. The {@link Cursor} is a {@link Range}, it has a `start`
* {@link Point} and an `end` {@link Point}.
*
* The {@link Cursor} can be a caret (collapsed cursor) or a selection (range
* expanded cursor). The caret is said to be "collapsed", its `start` and `end`
* {@link Point}s are the same. When the selection is said to be "expanded", its
* `start` and `end` {@link Point}s are different.
*
* The `start` {@link Point} is always the one that comes first in the text, it
* is less then or equal to the `end` {@link Point} in the spatial (text) order.
*
* An expanded selection cursor has a *focus* and an *anchor* side. The *focus*
* side is the one that moves when the user presses the arrow keys. The *anchor*
* side is the one that stays in place when the user presses the arrow keys. The
* side of the anchor is determined by the {@link Cursor#anchorSide} property.
*/
class Cursor extends PersistedSlice_1.PersistedSlice {
/**
* @todo Remove getter `get` here.
*/
get anchorSide() {
return this.type;
}
isStartFocused() {
return this.type === constants_1.CursorAnchor.End || this.start.cmp(this.end) === 0;
}
isEndFocused() {
return this.type === constants_1.CursorAnchor.Start || this.start.cmp(this.end) === 0;
}
// ---------------------------------------------------------------- mutations
set anchorSide(value) {
this.update({ type: value });
}
anchor() {
return this.anchorSide === constants_1.CursorAnchor.Start ? this.start : this.end;
}
focus() {
return this.anchorSide === constants_1.CursorAnchor.Start ? this.end : this.start;
}
set(start, end = start, anchorSide = this.anchorSide) {
let hasChange = false;
if (start.cmp(this.start))
hasChange = true;
if (!hasChange && end.cmp(this.end))
hasChange = true;
if (!hasChange && anchorSide !== this.anchorSide)
hasChange = true;
if (!hasChange)
return;
this.start = start;
this.end = end === start ? end.clone() : end;
this.update({
range: this,
type: anchorSide,
});
}
/**
* Move one of the edges of the cursor to a new point.
*
* @param point Point to set the edge to.
* @param endpoint 0 for "focus", 1 for "anchor".
*/
setEndpoint(point, endpoint = 0) {
if (this.start === this.end)
this.end = this.end.clone();
let anchor = this.anchor();
let focus = this.focus();
if (endpoint === 0)
focus = point;
else
anchor = point;
if (focus.cmpSpatial(anchor) < 0)
this.set(focus, anchor, constants_1.CursorAnchor.End);
else
this.set(anchor, focus, constants_1.CursorAnchor.Start);
}
move(move) {
const { start, end } = this;
const isCaret = start.cmp(end) === 0;
start.step(move);
if (isCaret)
this.set(start);
else {
end.step(move);
this.set(start, end);
}
}
collapseToStart(anchorSide = constants_1.CursorAnchor.Start) {
const start = this.start.clone();
start.refAfter();
const end = start.clone();
this.set(start, end, anchorSide);
}
// ---------------------------------------------------------------- Printable
toStringName() {
const focusIcon = this.anchorSide === constants_1.CursorAnchor.Start ? '.→|' : '|←.';
return 'Cursor ' + focusIcon + ' ' + (0, json_crdt_patch_1.printTs)(this.chunk.id) + ' #' + this.hash.toString(36);
}
toStringHeaderName() {
const focusIcon = this.anchorSide === constants_1.CursorAnchor.Start ? '.→|' : '|←.';
return `${super.toStringHeaderName()}, ${focusIcon}`;
}
}
exports.Cursor = Cursor;