Skip to content

Commit

Permalink
Add selectLeft/selectRight marker options
Browse files Browse the repository at this point in the history
  • Loading branch information
thomasjm authored and marijnh committed Jun 11, 2019
1 parent 7886f7f commit 7978b40
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 12 deletions.
16 changes: 12 additions & 4 deletions doc/manual.html
Original file line number Diff line number Diff line change
Expand Up @@ -1674,13 +1674,21 @@ <h3 id="api_marker">Text-marking methods</h3>
<dt id="mark_inclusiveRight"><code><strong>inclusiveRight</strong>: boolean</code></dt>
<dd>Like <code>inclusiveLeft</code>,
but for the right side.</dd>
<dt id="mark_selectLeft"><code><strong>selectLeft</strong>: boolean</code></dt>
<dd>For atomic ranges, determines whether the cursor is allowed
to be placed directly to the left of the range. Has no effect on
non-atomic ranges.</dd>
<dt id="mark_selectRight"><code><strong>selectRight</strong>: boolean</code></dt>
<dd>Like <code>selectLeft</code>,
but for the right side.</dd>
<dt id="mark_atomic"><code><strong>atomic</strong>: boolean</code></dt>
<dd>Atomic ranges act as a single unit when cursor movement is
concerned—i.e. it is impossible to place the cursor inside of
them. In atomic ranges, <code>inclusiveLeft</code>
and <code>inclusiveRight</code> have a different meaning—they
will prevent the cursor from being placed respectively
directly before and directly after the range.</dd>
them. You can control whether the cursor is allowed to be placed
directly before or after them using <code>selectLeft</code>
or <code>selectRight</code>. If <code>selectLeft</code>
(or right) is not provided, then <code>inclusiveLeft</code> (or
right) will control this behavior.</dd>
<dt id="mark_collapsed"><code><strong>collapsed</strong>: boolean</code></dt>
<dd>Collapsed ranges do not show up in the display. Setting a
range to be collapsed will automatically make it atomic.</dd>
Expand Down
15 changes: 11 additions & 4 deletions src/model/selection_updates.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,15 @@ function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
let line = getLine(doc, pos.line)
if (line.markedSpans) for (let i = 0; i < line.markedSpans.length; ++i) {
let sp = line.markedSpans[i], m = sp.marker
if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
(sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) {

// Determine if we should prevent the cursor being placed to the left/right of an atomic marker
// Historically this was determined using the inclusiveLeft/Right option, but the new way to control it
// is with selectLeft/Right
let preventCursorLeft = ("selectLeft" in m) ? !m.selectLeft : m.inclusiveLeft
let preventCursorRight = ("selectRight" in m) ? !m.selectRight : m.inclusiveRight

if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
(sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
if (mayClear) {
signal(m, "beforeCursorEnter")
if (m.explicitlyCleared) {
Expand All @@ -163,14 +170,14 @@ function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {

if (oldPos) {
let near = m.find(dir < 0 ? 1 : -1), diff
if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft)
if (dir < 0 ? preventCursorRight : preventCursorLeft)
near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null)
if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
return skipAtomicInner(doc, near, pos, dir, mayClear)
}

let far = m.find(dir < 0 ? -1 : 1)
if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight)
if (dir < 0 ? preventCursorLeft : preventCursorRight)
far = movePos(doc, far, dir, far.line == pos.line ? line : null)
return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
}
Expand Down
72 changes: 68 additions & 4 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1808,22 +1808,77 @@ testCM("addLineClass", function(cm) {

testCM("atomicMarker", function(cm) {
addDoc(cm, 10, 10);
function atom(ll, cl, lr, cr, li, ri) {
return cm.markText(Pos(ll, cl), Pos(lr, cr),
{atomic: true, inclusiveLeft: li, inclusiveRight: ri});

function atom(ll, cl, lr, cr, li, ri, ls, rs) {
var options = {
atomic: true,
inclusiveLeft: li,
inclusiveRight: ri
};

if (ls === true || ls === false) options["selectLeft"] = ls;
if (rs === true || rs === false) options["selectRight"] = rs;

return cm.markText(Pos(ll, cl), Pos(lr, cr), options);
}

// Can cursor to the left and right of a normal marker by jumping across it
var m = atom(0, 1, 0, 5);
cm.setCursor(Pos(0, 1));
cm.execCommand("goCharRight");
eqCursorPos(cm.getCursor(), Pos(0, 5));
cm.execCommand("goCharLeft");
eqCursorPos(cm.getCursor(), Pos(0, 1));
m.clear();

// Can't cursor to the left of a marker when inclusiveLeft=true
m = atom(0, 0, 0, 5, true);
eqCursorPos(cm.getCursor(), Pos(0, 5), "pushed out");
cm.execCommand("goCharLeft");
eqCursorPos(cm.getCursor(), Pos(0, 5));
m.clear();

// Can't cursor to the left of a marker when inclusiveLeft=false and selectLeft=false
m = atom(0, 0, 0, 5, false, false, false);
cm.setCursor(Pos(0, 5));
eqCursorPos(cm.getCursor(), Pos(0, 5), "pushed out");
cm.execCommand("goCharLeft");
eqCursorPos(cm.getCursor(), Pos(0, 5));
m.clear();

// Can cursor to the left of a marker when inclusiveLeft=false and selectLeft=True
m = atom(0, 0, 0, 5, false, false, true);
cm.setCursor(Pos(0, 5));
eqCursorPos(cm.getCursor(), Pos(0, 5), "pushed out");
cm.execCommand("goCharLeft");
eqCursorPos(cm.getCursor(), Pos(0, 0));
m.clear();

// Can't cursor to the right of a marker when inclusiveRight=true
m = atom(0, 0, 0, 5, false, true);
cm.setCursor(Pos(0, 0));
eqCursorPos(cm.getCursor(), Pos(0, 0));
cm.execCommand("goCharRight");
eqCursorPos(cm.getCursor(), Pos(0, 6));
m.clear();

// Can't cursor to the right of a marker when inclusiveRight=false and selectRight=false
m = atom(0, 0, 0, 5, false, false, true, false);
cm.setCursor(Pos(0, 0));
eqCursorPos(cm.getCursor(), Pos(0, 0));
cm.execCommand("goCharRight");
eqCursorPos(cm.getCursor(), Pos(0, 6));
m.clear();

// Can cursor to the right of a marker when inclusiveRight=false and selectRight=True
m = atom(0, 0, 0, 5, false, false, true, true);
cm.setCursor(Pos(0, 0));
eqCursorPos(cm.getCursor(), Pos(0, 0));
cm.execCommand("goCharRight");
eqCursorPos(cm.getCursor(), Pos(0, 5));
m.clear();

// Can't cursor to the right of a multiline marker when inclusiveRight=true
m = atom(8, 4, 9, 10, false, true);
cm.setCursor(Pos(9, 8));
eqCursorPos(cm.getCursor(), Pos(8, 4), "set");
Expand All @@ -1834,6 +1889,9 @@ testCM("atomicMarker", function(cm) {
cm.execCommand("goCharLeft");
eqCursorPos(cm.getCursor(), Pos(8, 3, "after"));
m.clear();

// Cursor jumps across a multiline atomic marker,
// and backspace deletes the entire marker
m = atom(1, 1, 3, 8);
cm.setCursor(Pos(0, 0));
cm.setCursor(Pos(2, 0));
Expand All @@ -1848,10 +1906,16 @@ testCM("atomicMarker", function(cm) {
eqCursorPos(cm.getCursor(), Pos(3, 8));
cm.execCommand("delCharBefore");
eq(cm.getValue().length, 80, "del chunk");
m.clear();
addDoc(cm, 10, 10);

// Delete before an atomic marker deletes the entire marker
m = atom(3, 0, 5, 5);
cm.setCursor(Pos(3, 0));
cm.execCommand("delWordAfter");
eq(cm.getValue().length, 53, "del chunk");
eq(cm.getValue().length, 82, "del chunk");
m.clear();
addDoc(cm, 10, 10);
});

testCM("selectionBias", function(cm) {
Expand Down

0 comments on commit 7978b40

Please sign in to comment.