Bug 1402343 - Update Codemirror to 5.30.0. r=bgrins

This commit is contained in:
Gabriel Luong 2017-09-25 14:36:54 -04:00
parent 211be553a3
commit 29bb494af7
11 changed files with 778 additions and 433 deletions

View File

@ -34,9 +34,6 @@ function performTest() {
// Make sure that the search box becomes focused when pressing ctrl+f - Bug 1211038
gEditor.focus();
synthesizeKeyFromKeyTag(gDebugger.document.getElementById("tokenSearchKey"));
let focusedEl = Services.focus.focusedElement;
focusedEl = focusedEl.ownerDocument.getBindingParent(focusedEl) || focusedEl;
is(focusedEl, gDebugger.document.getElementById("searchbox"), "Searchbox is focused");
setText(gSearchBox, "#html");

View File

@ -5,7 +5,7 @@ code, and optionally help with indentation.
# Upgrade
Currently used version is 5.29.0. To upgrade: download a new version of
Currently used version is 5.30.0. To upgrade: download a new version of
CodeMirror from the project's page [1] and replace all JavaScript and
CSS files inside the codemirror directory [2].

View File

@ -23,6 +23,7 @@
cm.state.closeBrackets = null;
}
if (val) {
ensureBound(getOption(val, "pairs"))
cm.state.closeBrackets = val;
cm.addKeyMap(keyMap);
}
@ -34,10 +35,14 @@
return defaults[name];
}
var bind = defaults.pairs + "`";
var keyMap = {Backspace: handleBackspace, Enter: handleEnter};
for (var i = 0; i < bind.length; i++)
keyMap["'" + bind.charAt(i) + "'"] = handler(bind.charAt(i));
function ensureBound(chars) {
for (var i = 0; i < chars.length; i++) {
var ch = chars.charAt(i), key = "'" + ch + "'"
if (!keyMap[key]) keyMap[key] = handler(ch)
}
}
ensureBound(defaults.pairs + "`")
function handler(ch) {
return function(cm) { return handleChar(cm, ch); };

View File

@ -302,7 +302,7 @@
setTimeout(function(){cm.focus();}, 20);
});
CodeMirror.signal(data, "select", completions[0], hints.firstChild);
CodeMirror.signal(data, "select", completions[this.selectedHint], hints.childNodes[this.selectedHint]);
return true;
}

View File

@ -132,6 +132,7 @@
var state = getSearchState(cm);
if (state.query) return findNext(cm, rev);
var q = cm.getSelection() || state.lastQuery;
if (q instanceof RegExp && q.source == "x^") q = null
if (persistent && cm.openDialog) {
var hiding = null
var searchNext = function(query, event) {

View File

@ -86,7 +86,7 @@
if (!array.length) return coverRange(cm, from, to);
var coverStart = array[0].find(), coverEnd = array[array.length - 1].find();
if (!coverStart || !coverEnd || to.line - from.line < CHUNK_SIZE ||
if (!coverStart || !coverEnd || to.line - from.line <= CHUNK_SIZE ||
cmp(from, coverEnd.to) >= 0 || cmp(to, coverStart.from) <= 0)
return reset(cm);

View File

@ -571,7 +571,7 @@
return {type: "part",
name: data.name,
offsetLines: from.line,
text: doc.getRange(from, Pos(endLine, 0))};
text: doc.getRange(from, Pos(endLine, end.line == endLine ? null : 0))};
}
// Generic utilities

View File

@ -520,13 +520,18 @@ var CodeMirror =
}
// Returns the value from the range [`from`; `to`] that satisfies
// `pred` and is closest to `from`. Assumes that at least `to` satisfies `pred`.
// `pred` and is closest to `from`. Assumes that at least `to`
// satisfies `pred`. Supports `from` being greater than `to`.
function findFirst(pred, from, to) {
// At any point we are certain `to` satisfies `pred`, don't know
// whether `from` does.
var dir = from > to ? -1 : 1
for (;;) {
if (Math.abs(from - to) <= 1) { return pred(from) ? from : to }
var mid = Math.floor((from + to) / 2)
if (from == to) { return from }
var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF)
if (mid == from) { return pred(mid) ? from : to }
if (pred(mid)) { to = mid }
else { from = mid }
else { from = mid + dir }
}
}
@ -1139,12 +1144,12 @@ var CodeMirror =
// BIDI HELPERS
function iterateBidiSections(order, from, to, f) {
if (!order) { return f(from, to, "ltr") }
if (!order) { return f(from, to, "ltr", 0) }
var found = false
for (var i = 0; i < order.length; ++i) {
var part = order[i]
if (part.from < to && part.to > from || from == to && part.to == from) {
f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr")
f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i)
found = true
}
}
@ -1347,112 +1352,6 @@ var CodeMirror =
return order
}
function moveCharLogically(line, ch, dir) {
var target = skipExtendingChars(line.text, ch + dir, dir)
return target < 0 || target > line.text.length ? null : target
}
function moveLogically(line, start, dir) {
var ch = moveCharLogically(line, start.ch, dir)
return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
}
function endOfLine(visually, cm, lineObj, lineNo, dir) {
if (visually) {
var order = getOrder(lineObj, cm.doc.direction)
if (order) {
var part = dir < 0 ? lst(order) : order[0]
var moveInStorageOrder = (dir < 0) == (part.level == 1)
var sticky = moveInStorageOrder ? "after" : "before"
var ch
// With a wrapped rtl chunk (possibly spanning multiple bidi parts),
// it could be that the last bidi part is not on the last visual line,
// since visual lines contain content order-consecutive chunks.
// Thus, in rtl, we are looking for the first (content-order) character
// in the rtl chunk that is on the last line (that is, the same line
// as the last (content-order) character).
if (part.level > 0) {
var prep = prepareMeasureForLine(cm, lineObj)
ch = dir < 0 ? lineObj.text.length - 1 : 0
var targetTop = measureCharPrepared(cm, prep, ch).top
ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch)
if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1) }
} else { ch = dir < 0 ? part.to : part.from }
return new Pos(lineNo, ch, sticky)
}
}
return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
}
function moveVisually(cm, line, start, dir) {
var bidi = getOrder(line, cm.doc.direction)
if (!bidi) { return moveLogically(line, start, dir) }
if (start.ch >= line.text.length) {
start.ch = line.text.length
start.sticky = "before"
} else if (start.ch <= 0) {
start.ch = 0
start.sticky = "after"
}
var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]
if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
// Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
// nothing interesting happens.
return moveLogically(line, start, dir)
}
var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); }
var prep
var getWrappedLineExtent = function (ch) {
if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
prep = prep || prepareMeasureForLine(cm, line)
return wrappedLineExtentChar(cm, line, prep, ch)
}
var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch)
if (cm.doc.direction == "rtl" || part.level == 1) {
var moveInStorageOrder = (part.level == 1) == (dir < 0)
var ch = mv(start, moveInStorageOrder ? 1 : -1)
if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
// Case 2: We move within an rtl part or in an rtl editor on the same visual line
var sticky = moveInStorageOrder ? "before" : "after"
return new Pos(start.line, ch, sticky)
}
}
// Case 3: Could not move within this bidi part in this visual line, so leave
// the current bidi part
var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
? new Pos(start.line, mv(ch, 1), "before")
: new Pos(start.line, ch, "after"); }
for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
var part = bidi[partPos]
var moveInStorageOrder = (dir > 0) == (part.level != 1)
var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1)
if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
ch = moveInStorageOrder ? part.from : mv(part.to, -1)
if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
}
}
// Case 3a: Look for other bidi parts on the same visual line
var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent)
if (res) { return res }
// Case 3b: Look for other bidi parts on the next visual line
var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1)
if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh))
if (res) { return res }
}
// Case 4: Nowhere to move
return null
}
// EVENT HANDLING
// Lightweight event framework. on/off also work on DOM nodes,
@ -2961,15 +2860,22 @@ var CodeMirror =
return window.pageYOffset || (document.documentElement || document.body).scrollTop
}
function widgetTopHeight(lineObj) {
var height = 0
if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above)
{ height += widgetHeight(lineObj.widgets[i]) } } }
return height
}
// Converts a {top, bottom, left, right} box from line-local
// coordinates into another coordinate system. Context may be one of
// "line", "div" (display.lineDiv), "local"./null (editor), "window",
// or "page".
function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
if (!includeWidgets && lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above) {
var size = widgetHeight(lineObj.widgets[i])
rect.top += size; rect.bottom += size
} } }
if (!includeWidgets) {
var height = widgetTopHeight(lineObj)
rect.top += height; rect.bottom += height
}
if (context == "line") { return rect }
if (!context) { context = "local" }
var yOff = heightAtLine(lineObj)
@ -3044,7 +2950,7 @@ var CodeMirror =
if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") }
function getBidi(ch, partPos, invert) {
var part = order[partPos], right = (part.level % 2) != 0
var part = order[partPos], right = part.level == 1
return get(invert ? ch - 1 : ch, right != invert)
}
var partPos = getBidiPartAt(order, ch, sticky)
@ -3102,77 +3008,146 @@ var CodeMirror =
}
function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
var measure = function (ch) { return intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, ch), "line"); }
y -= widgetTopHeight(lineObj)
var end = lineObj.text.length
var begin = findFirst(function (ch) { return measure(ch - 1).bottom <= y; }, end, 0)
end = findFirst(function (ch) { return measure(ch).top > y; }, begin, end)
var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0)
end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end)
return {begin: begin, end: end}
}
function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj) }
var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top
return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
}
// Returns true if the given side of a box is after the given
// coordinates, in top-to-bottom, left-to-right order.
function boxIsAfter(box, x, y, left) {
return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x
}
function coordsCharInner(cm, lineObj, lineNo, x, y) {
// Move y into line-local coordinate space
y -= heightAtLine(lineObj)
var begin = 0, end = lineObj.text.length
var preparedMeasure = prepareMeasureForLine(cm, lineObj)
var pos
// When directly calling `measureCharPrepared`, we have to adjust
// for the widgets at this line.
var widgetHeight = widgetTopHeight(lineObj)
var begin = 0, end = lineObj.text.length, ltr = true
var order = getOrder(lineObj, cm.doc.direction)
// If the line isn't plain left-to-right text, first figure out
// which bidi section the coordinates fall into.
if (order) {
if (cm.options.lineWrapping) {
;var assign;
((assign = wrappedLineExtent(cm, lineObj, preparedMeasure, y), begin = assign.begin, end = assign.end, assign))
}
pos = new Pos(lineNo, Math.floor(begin + (end - begin) / 2))
var beginLeft = cursorCoords(cm, pos, "line", lineObj, preparedMeasure).left
var dir = beginLeft < x ? 1 : -1
var prevDiff, diff = beginLeft - x, prevPos
var steps = Math.ceil((end - begin) / 4)
outer: do {
prevDiff = diff
prevPos = pos
var i = 0
for (; i < steps; ++i) {
var prevPos$1 = pos
pos = moveVisually(cm, lineObj, pos, dir)
if (pos == null || pos.ch < begin || end <= (pos.sticky == "before" ? pos.ch - 1 : pos.ch)) {
pos = prevPos$1
break outer
}
}
diff = cursorCoords(cm, pos, "line", lineObj, preparedMeasure).left - x
if (steps > 1) {
var diff_change_per_step = Math.abs(diff - prevDiff) / steps
steps = Math.min(steps, Math.ceil(Math.abs(diff) / diff_change_per_step))
dir = diff < 0 ? 1 : -1
}
} while (diff != 0 && (steps > 1 || ((dir < 0) != (diff < 0) && (Math.abs(diff) <= Math.abs(prevDiff)))))
if (Math.abs(diff) > Math.abs(prevDiff)) {
if ((diff < 0) == (prevDiff < 0)) { throw new Error("Broke out of infinite loop in coordsCharInner") }
pos = prevPos
}
} else {
var ch = findFirst(function (ch) {
var box = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, ch), "line")
if (box.top > y) {
// For the cursor stickiness
end = Math.min(ch, end)
return true
}
else if (box.bottom <= y) { return false }
else if (box.left > x) { return true }
else if (box.right < x) { return false }
else { return (x - box.left < box.right - x) }
}, begin, end)
ch = skipExtendingChars(lineObj.text, ch, 1)
pos = new Pos(lineNo, ch, ch == end ? "before" : "after")
var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)
(cm, lineObj, lineNo, preparedMeasure, order, x, y)
ltr = part.level != 1
// The awkward -1 offsets are needed because findFirst (called
// on these below) will treat its first bound as inclusive,
// second as exclusive, but we want to actually address the
// characters in the part's range
begin = ltr ? part.from : part.to - 1
end = ltr ? part.to : part.from - 1
}
var coords = cursorCoords(cm, pos, "line", lineObj, preparedMeasure)
if (y < coords.top || coords.bottom < y) { pos.outside = true }
pos.xRel = x < coords.left ? -1 : (x > coords.right ? 1 : 0)
return pos
// A binary search to find the first character whose bounding box
// starts after the coordinates. If we run across any whose box wrap
// the coordinates, store that.
var chAround = null, boxAround = null
var ch = findFirst(function (ch) {
var box = measureCharPrepared(cm, preparedMeasure, ch)
box.top += widgetHeight; box.bottom += widgetHeight
if (!boxIsAfter(box, x, y, false)) { return false }
if (box.top <= y && box.left <= x) {
chAround = ch
boxAround = box
}
return true
}, begin, end)
var baseX, sticky, outside = false
// If a box around the coordinates was found, use that
if (boxAround) {
// Distinguish coordinates nearer to the left or right side of the box
var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr
ch = chAround + (atStart ? 0 : 1)
sticky = atStart ? "after" : "before"
baseX = atLeft ? boxAround.left : boxAround.right
} else {
// (Adjust for extended bound, if necessary.)
if (!ltr && (ch == end || ch == begin)) { ch++ }
// To determine which side to associate with, get the box to the
// left of the character and compare it's vertical position to the
// coordinates
sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" :
(measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ?
"after" : "before"
// Now get accurate coordinates for this place, in order to get a
// base X position
var coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure)
baseX = coords.left
outside = y < coords.top || y >= coords.bottom
}
ch = skipExtendingChars(lineObj.text, ch, 1)
return PosWithInfo(lineNo, ch, sticky, outside, x - baseX)
}
function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) {
// Bidi parts are sorted left-to-right, and in a non-line-wrapping
// situation, we can take this ordering to correspond to the visual
// ordering. This finds the first part whose end is after the given
// coordinates.
var index = findFirst(function (i) {
var part = order[i], ltr = part.level != 1
return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? "before" : "after"),
"line", lineObj, preparedMeasure), x, y, true)
}, 0, order.length - 1)
var part = order[index]
// If this isn't the first part, the part's start is also after
// the coordinates, and the coordinates aren't on the same line as
// that start, move one part back.
if (index > 0) {
var ltr = part.level != 1
var start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? "after" : "before"),
"line", lineObj, preparedMeasure)
if (boxIsAfter(start, x, y, true) && start.top > y)
{ part = order[index - 1] }
}
return part
}
function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {
// In a wrapped line, rtl text on wrapping boundaries can do things
// that don't correspond to the ordering in our `order` array at
// all, so a binary search doesn't work, and we want to return a
// part that only spans one line so that the binary search in
// coordsCharInner is safe. As such, we first find the extent of the
// wrapped line, and then do a flat search in which we discard any
// spans that aren't on the line.
var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);
var begin = ref.begin;
var end = ref.end;
var part = null, closestDist = null
for (var i = 0; i < order.length; i++) {
var p = order[i]
if (p.from >= end || p.to <= begin) { continue }
var ltr = p.level != 1
var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right
// Weigh against spans ending before this, so that they are only
// picked if nothing ends after
var dist = endX < x ? x - endX + 1e9 : endX - x
if (!part || closestDist > dist) {
part = p
closestDist = dist
}
}
if (!part) { part = order[order.length - 1] }
// Clip the part to the wrapped line.
if (part.from < begin) { part = {from: begin, to: part.to, level: part.level} }
if (part.to > end) { part = {from: part.from, to: end, level: part.level} }
return part
}
var measureText
@ -3298,12 +3273,14 @@ var CodeMirror =
}
function prepareSelection(cm, primary) {
if ( primary === void 0 ) primary = true;
var doc = cm.doc, result = {}
var curFragment = result.cursors = document.createDocumentFragment()
var selFragment = result.selection = document.createDocumentFragment()
for (var i = 0; i < doc.sel.ranges.length; i++) {
if (primary === false && i == doc.sel.primIndex) { continue }
if (!primary && i == doc.sel.primIndex) { continue }
var range = doc.sel.ranges[i]
if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue }
var collapsed = range.empty()
@ -3334,6 +3311,8 @@ var CodeMirror =
}
}
function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }
// Draws the given range as a highlighted selection
function drawSelectionRange(cm, range, output) {
var display = cm.display, doc = cm.doc
@ -3356,30 +3335,48 @@ var CodeMirror =
return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
}
iterateBidiSections(getOrder(lineObj, doc.direction), fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir) {
var leftPos = coords(from, "left"), rightPos, left, right
if (from == to) {
rightPos = leftPos
left = right = leftPos.left
} else {
rightPos = coords(to - 1, "right")
if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp }
left = leftPos.left
right = rightPos.right
var order = getOrder(lineObj, doc.direction)
iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {
var fromPos = coords(from, dir == "ltr" ? "left" : "right")
var toPos = coords(to - 1, dir == "ltr" ? "right" : "left")
if (dir == "ltr") {
var fromLeft = fromArg == null && from == 0 ? leftSide : fromPos.left
var toRight = toArg == null && to == lineLen ? rightSide : toPos.right
if (toPos.top - fromPos.top <= 3) { // Single line
add(fromLeft, toPos.top, toRight - fromLeft, toPos.bottom)
} else { // Multiple lines
add(fromLeft, fromPos.top, null, fromPos.bottom)
if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top) }
add(leftSide, toPos.top, toPos.right, toPos.bottom)
}
} else if (from < to) { // RTL
var fromRight = fromArg == null && from == 0 ? rightSide : fromPos.right
var toLeft = toArg == null && to == lineLen ? leftSide : toPos.left
if (toPos.top - fromPos.top <= 3) { // Single line
add(toLeft, toPos.top, fromRight - toLeft, toPos.bottom)
} else { // Multiple lines
var topLeft = leftSide
if (i) {
var topEnd = wrappedLineExtentChar(cm, lineObj, null, from).end
// The coordinates returned for an RTL wrapped space tend to
// be complete bogus, so try to skip that here.
topLeft = coords(topEnd - (/\s/.test(lineObj.text.charAt(topEnd - 1)) ? 2 : 1), "left").left
}
add(topLeft, fromPos.top, fromRight - topLeft, fromPos.bottom)
if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top) }
var botWidth = null
if (i < order.length - 1 || true) {
var botStart = wrappedLineExtentChar(cm, lineObj, null, to).begin
botWidth = coords(botStart, "right").right - toLeft
}
add(toLeft, toPos.top, botWidth, toPos.bottom)
}
}
if (fromArg == null && from == 0) { left = leftSide }
if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
add(left, leftPos.top, null, leftPos.bottom)
left = leftSide
if (leftPos.bottom < rightPos.top) { add(left, leftPos.bottom, null, rightPos.top) }
}
if (toArg == null && to == lineLen) { right = rightSide }
if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
{ start = leftPos }
if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
{ end = rightPos }
if (left < leftSide + 1) { left = leftSide }
add(left, rightPos.top, right - left, rightPos.bottom)
if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos }
if (cmpCoords(toPos, start) < 0) { start = toPos }
if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos }
if (cmpCoords(toPos, end) < 0) { end = toPos }
})
return {start: start, end: end}
}
@ -4009,7 +4006,7 @@ var CodeMirror =
}
if (op.updatedDisplay || op.selectionChanged)
{ op.preparedSelection = display.input.prepareSelection(op.focus) }
{ op.preparedSelection = display.input.prepareSelection() }
}
function endOperation_W2(op) {
@ -4022,7 +4019,7 @@ var CodeMirror =
cm.display.maxLineChanged = false
}
var takeFocus = op.focus && op.focus == activeElt() && (!document.hasFocus || document.hasFocus())
var takeFocus = op.focus && op.focus == activeElt()
if (op.preparedSelection)
{ cm.display.input.showSelection(op.preparedSelection, takeFocus) }
if (op.updatedDisplay || op.startHeight != cm.doc.height)
@ -5611,7 +5608,8 @@ var CodeMirror =
function replaceRange(doc, code, from, to, origin) {
if (!to) { to = from }
if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp }
if (cmp(to, from) < 0) { var assign;
(assign = [to, from], from = assign[0], to = assign[1], assign) }
if (typeof code == "string") { code = doc.splitLines(code) }
makeChange(doc, {from: from, to: to, text: code, origin: origin})
}
@ -6973,6 +6971,112 @@ var CodeMirror =
})
}
function moveCharLogically(line, ch, dir) {
var target = skipExtendingChars(line.text, ch + dir, dir)
return target < 0 || target > line.text.length ? null : target
}
function moveLogically(line, start, dir) {
var ch = moveCharLogically(line, start.ch, dir)
return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
}
function endOfLine(visually, cm, lineObj, lineNo, dir) {
if (visually) {
var order = getOrder(lineObj, cm.doc.direction)
if (order) {
var part = dir < 0 ? lst(order) : order[0]
var moveInStorageOrder = (dir < 0) == (part.level == 1)
var sticky = moveInStorageOrder ? "after" : "before"
var ch
// With a wrapped rtl chunk (possibly spanning multiple bidi parts),
// it could be that the last bidi part is not on the last visual line,
// since visual lines contain content order-consecutive chunks.
// Thus, in rtl, we are looking for the first (content-order) character
// in the rtl chunk that is on the last line (that is, the same line
// as the last (content-order) character).
if (part.level > 0) {
var prep = prepareMeasureForLine(cm, lineObj)
ch = dir < 0 ? lineObj.text.length - 1 : 0
var targetTop = measureCharPrepared(cm, prep, ch).top
ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch)
if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1) }
} else { ch = dir < 0 ? part.to : part.from }
return new Pos(lineNo, ch, sticky)
}
}
return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
}
function moveVisually(cm, line, start, dir) {
var bidi = getOrder(line, cm.doc.direction)
if (!bidi) { return moveLogically(line, start, dir) }
if (start.ch >= line.text.length) {
start.ch = line.text.length
start.sticky = "before"
} else if (start.ch <= 0) {
start.ch = 0
start.sticky = "after"
}
var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]
if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
// Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
// nothing interesting happens.
return moveLogically(line, start, dir)
}
var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); }
var prep
var getWrappedLineExtent = function (ch) {
if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
prep = prep || prepareMeasureForLine(cm, line)
return wrappedLineExtentChar(cm, line, prep, ch)
}
var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch)
if (cm.doc.direction == "rtl" || part.level == 1) {
var moveInStorageOrder = (part.level == 1) == (dir < 0)
var ch = mv(start, moveInStorageOrder ? 1 : -1)
if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
// Case 2: We move within an rtl part or in an rtl editor on the same visual line
var sticky = moveInStorageOrder ? "before" : "after"
return new Pos(start.line, ch, sticky)
}
}
// Case 3: Could not move within this bidi part in this visual line, so leave
// the current bidi part
var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
? new Pos(start.line, mv(ch, 1), "before")
: new Pos(start.line, ch, "after"); }
for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
var part = bidi[partPos]
var moveInStorageOrder = (dir > 0) == (part.level != 1)
var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1)
if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
ch = moveInStorageOrder ? part.from : mv(part.to, -1)
if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
}
}
// Case 3a: Look for other bidi parts on the same visual line
var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent)
if (res) { return res }
// Case 3b: Look for other bidi parts on the next visual line
var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1)
if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh))
if (res) { return res }
}
// Case 4: Nowhere to move
return null
}
// Commands are parameter-less actions that can be performed on an
// editor, mostly used for keybindings.
var commands = {
@ -7534,7 +7638,7 @@ var CodeMirror =
anchor = maxPos(oldRange.to(), range.head)
}
var ranges$1 = startSel.ranges.slice(0)
ranges$1[ourIndex] = new Range(clipPos(doc, anchor), head)
ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head))
setSelection(doc, normalizeSelection(ranges$1, ourIndex), sel_mouse)
}
}
@ -7586,13 +7690,52 @@ var CodeMirror =
on(document, "mouseup", up)
}
// Used when mouse-selecting to adjust the anchor to the proper side
// of a bidi jump depending on the visual position of the head.
function bidiSimplify(cm, range) {
var anchor = range.anchor;
var head = range.head;
var anchorLine = getLine(cm.doc, anchor.line)
if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range }
var order = getOrder(anchorLine)
if (!order) { return range }
var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index]
if (part.from != anchor.ch && part.to != anchor.ch) { return range }
var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1)
if (boundary == 0 || boundary == order.length) { return range }
// Compute the relative visual position of the head compared to the
// anchor (<0 is to the left, >0 to the right)
var leftSide
if (head.line != anchor.line) {
leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0
} else {
var headIndex = getBidiPartAt(order, head.ch, head.sticky)
var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1)
if (headIndex == boundary - 1 || headIndex == boundary)
{ leftSide = dir < 0 }
else
{ leftSide = dir > 0 }
}
var usePart = order[boundary + (leftSide ? -1 : 0)]
var from = leftSide == (usePart.level == 1)
var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before"
return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head)
}
// Determines whether an event happened in the gutter, and fires the
// handlers for the corresponding event.
function gutterEvent(cm, e, type, prevent) {
var mX, mY
try { mX = e.clientX; mY = e.clientY }
catch(e) { return false }
if (e.touches) {
mX = e.touches[0].clientX
mY = e.touches[0].clientY
} else {
try { mX = e.clientX; mY = e.clientY }
catch(e) { return false }
}
if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
if (prevent) { e_preventDefault(e) }
@ -7930,7 +8073,7 @@ var CodeMirror =
return dx * dx + dy * dy > 20 * 20
}
on(d.scroller, "touchstart", function (e) {
if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) {
if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {
d.input.ensurePolled()
clearTimeout(touchFinished)
var now = +new Date
@ -9714,7 +9857,7 @@ var CodeMirror =
addLegacyProps(CodeMirror)
CodeMirror.version = "5.29.0"
CodeMirror.version = "5.30.0"
return CodeMirror;
@ -10153,6 +10296,7 @@ var CodeMirror =
var state = getSearchState(cm);
if (state.query) return findNext(cm, rev);
var q = cm.getSelection() || state.lastQuery;
if (q instanceof RegExp && q.source == "x^") q = null
if (persistent && cm.openDialog) {
var hiding = null
var searchNext = function(query, event) {
@ -10462,6 +10606,7 @@ var CodeMirror =
cm.state.closeBrackets = null;
}
if (val) {
ensureBound(getOption(val, "pairs"))
cm.state.closeBrackets = val;
cm.addKeyMap(keyMap);
}
@ -10473,10 +10618,14 @@ var CodeMirror =
return defaults[name];
}
var bind = defaults.pairs + "`";
var keyMap = {Backspace: handleBackspace, Enter: handleEnter};
for (var i = 0; i < bind.length; i++)
keyMap["'" + bind.charAt(i) + "'"] = handler(bind.charAt(i));
function ensureBound(chars) {
for (var i = 0; i < chars.length; i++) {
var ch = chars.charAt(i), key = "'" + ch + "'"
if (!keyMap[key]) keyMap[key] = handler(ch)
}
}
ensureBound(defaults.pairs + "`")
function handler(ch) {
return function(cm) { return handleChar(cm, ch); };
@ -10894,7 +11043,7 @@ var CodeMirror =
var jsKeywords = {
"if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
"return": C, "break": C, "continue": C, "new": kw("new"), "delete": C, "throw": C, "debugger": C,
"return": C, "break": C, "continue": C, "new": kw("new"), "delete": C, "void": C, "throw": C, "debugger": C,
"var": kw("var"), "const": kw("var"), "let": kw("var"),
"function": kw("function"), "catch": kw("catch"),
"for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
@ -11264,7 +11413,7 @@ var CodeMirror =
function expressionInner(type, noComma) {
if (cx.state.fatArrowAt == cx.stream.start) {
var body = noComma ? arrowBodyNoComma : arrowBody;
if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext);
if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext);
else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
}
@ -11309,6 +11458,11 @@ var CodeMirror =
if (type == ".") return cont(property, me);
if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) }
if (type == "regexp") {
cx.state.lastType = cx.marked = "operator"
cx.stream.backUp(cx.stream.pos - cx.stream.start - 1)
return cont(expr)
}
}
function quasi(type, value) {
if (type != "quasi") return pass();
@ -11357,6 +11511,9 @@ var CodeMirror =
} else if (type == "variable" || cx.style == "keyword") {
cx.marked = "property";
if (value == "get" || value == "set") return cont(getterSetter);
var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params
if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false)))
cx.state.fatArrowAt = cx.stream.pos + m[0].length
return cont(afterprop);
} else if (type == "number" || type == "string") {
cx.marked = jsonldMode ? "property" : (cx.style + " property");
@ -11514,7 +11671,8 @@ var CodeMirror =
if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, maybetype, statement, popcontext);
if (isTS && value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, functiondef)
}
function funarg(type) {
function funarg(type, value) {
if (value == "@") cont(expression, funarg)
if (type == "spread" || type == "modifier") return cont(funarg);
return pass(pattern, maybetype, maybeAssign);
}
@ -11540,7 +11698,7 @@ var CodeMirror =
cx.marked = "keyword";
return cont(classBody);
}
if (type == "variable") {
if (type == "variable" || cx.style == "keyword") {
cx.marked = "property";
return cont(isTS ? classfield : functiondef, classBody);
}
@ -11602,7 +11760,7 @@ var CodeMirror =
function expressionAllowed(stream, state, backUp) {
return state.tokenize == tokenBase &&
/^(?:operator|sof|keyword c|case|new|export|default|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
/^(?:operator|sof|keyword [bc]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
(state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
}

View File

@ -278,13 +278,18 @@ function skipExtendingChars(str, pos, dir) {
}
// Returns the value from the range [`from`; `to`] that satisfies
// `pred` and is closest to `from`. Assumes that at least `to` satisfies `pred`.
// `pred` and is closest to `from`. Assumes that at least `to`
// satisfies `pred`. Supports `from` being greater than `to`.
function findFirst(pred, from, to) {
// At any point we are certain `to` satisfies `pred`, don't know
// whether `from` does.
var dir = from > to ? -1 : 1
for (;;) {
if (Math.abs(from - to) <= 1) { return pred(from) ? from : to }
var mid = Math.floor((from + to) / 2)
if (from == to) { return from }
var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF)
if (mid == from) { return pred(mid) ? from : to }
if (pred(mid)) { to = mid }
else { from = mid }
else { from = mid + dir }
}
}
@ -897,12 +902,12 @@ function findMaxLine(cm) {
// BIDI HELPERS
function iterateBidiSections(order, from, to, f) {
if (!order) { return f(from, to, "ltr") }
if (!order) { return f(from, to, "ltr", 0) }
var found = false
for (var i = 0; i < order.length; ++i) {
var part = order[i]
if (part.from < to && part.to > from || from == to && part.to == from) {
f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr")
f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i)
found = true
}
}
@ -1105,112 +1110,6 @@ function getOrder(line, direction) {
return order
}
function moveCharLogically(line, ch, dir) {
var target = skipExtendingChars(line.text, ch + dir, dir)
return target < 0 || target > line.text.length ? null : target
}
function moveLogically(line, start, dir) {
var ch = moveCharLogically(line, start.ch, dir)
return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
}
function endOfLine(visually, cm, lineObj, lineNo, dir) {
if (visually) {
var order = getOrder(lineObj, cm.doc.direction)
if (order) {
var part = dir < 0 ? lst(order) : order[0]
var moveInStorageOrder = (dir < 0) == (part.level == 1)
var sticky = moveInStorageOrder ? "after" : "before"
var ch
// With a wrapped rtl chunk (possibly spanning multiple bidi parts),
// it could be that the last bidi part is not on the last visual line,
// since visual lines contain content order-consecutive chunks.
// Thus, in rtl, we are looking for the first (content-order) character
// in the rtl chunk that is on the last line (that is, the same line
// as the last (content-order) character).
if (part.level > 0) {
var prep = prepareMeasureForLine(cm, lineObj)
ch = dir < 0 ? lineObj.text.length - 1 : 0
var targetTop = measureCharPrepared(cm, prep, ch).top
ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch)
if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1) }
} else { ch = dir < 0 ? part.to : part.from }
return new Pos(lineNo, ch, sticky)
}
}
return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
}
function moveVisually(cm, line, start, dir) {
var bidi = getOrder(line, cm.doc.direction)
if (!bidi) { return moveLogically(line, start, dir) }
if (start.ch >= line.text.length) {
start.ch = line.text.length
start.sticky = "before"
} else if (start.ch <= 0) {
start.ch = 0
start.sticky = "after"
}
var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]
if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
// Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
// nothing interesting happens.
return moveLogically(line, start, dir)
}
var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); }
var prep
var getWrappedLineExtent = function (ch) {
if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
prep = prep || prepareMeasureForLine(cm, line)
return wrappedLineExtentChar(cm, line, prep, ch)
}
var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch)
if (cm.doc.direction == "rtl" || part.level == 1) {
var moveInStorageOrder = (part.level == 1) == (dir < 0)
var ch = mv(start, moveInStorageOrder ? 1 : -1)
if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
// Case 2: We move within an rtl part or in an rtl editor on the same visual line
var sticky = moveInStorageOrder ? "before" : "after"
return new Pos(start.line, ch, sticky)
}
}
// Case 3: Could not move within this bidi part in this visual line, so leave
// the current bidi part
var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
? new Pos(start.line, mv(ch, 1), "before")
: new Pos(start.line, ch, "after"); }
for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
var part = bidi[partPos]
var moveInStorageOrder = (dir > 0) == (part.level != 1)
var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1)
if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
ch = moveInStorageOrder ? part.from : mv(part.to, -1)
if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
}
}
// Case 3a: Look for other bidi parts on the same visual line
var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent)
if (res) { return res }
// Case 3b: Look for other bidi parts on the next visual line
var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1)
if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh))
if (res) { return res }
}
// Case 4: Nowhere to move
return null
}
// EVENT HANDLING
// Lightweight event framework. on/off also work on DOM nodes,
@ -2719,15 +2618,22 @@ function pageScrollY() {
return window.pageYOffset || (document.documentElement || document.body).scrollTop
}
function widgetTopHeight(lineObj) {
var height = 0
if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above)
{ height += widgetHeight(lineObj.widgets[i]) } } }
return height
}
// Converts a {top, bottom, left, right} box from line-local
// coordinates into another coordinate system. Context may be one of
// "line", "div" (display.lineDiv), "local"./null (editor), "window",
// or "page".
function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
if (!includeWidgets && lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above) {
var size = widgetHeight(lineObj.widgets[i])
rect.top += size; rect.bottom += size
} } }
if (!includeWidgets) {
var height = widgetTopHeight(lineObj)
rect.top += height; rect.bottom += height
}
if (context == "line") { return rect }
if (!context) { context = "local" }
var yOff = heightAtLine(lineObj)
@ -2802,7 +2708,7 @@ function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") }
function getBidi(ch, partPos, invert) {
var part = order[partPos], right = (part.level % 2) != 0
var part = order[partPos], right = part.level == 1
return get(invert ? ch - 1 : ch, right != invert)
}
var partPos = getBidiPartAt(order, ch, sticky)
@ -2860,77 +2766,146 @@ function coordsChar(cm, x, y) {
}
function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
var measure = function (ch) { return intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, ch), "line"); }
y -= widgetTopHeight(lineObj)
var end = lineObj.text.length
var begin = findFirst(function (ch) { return measure(ch - 1).bottom <= y; }, end, 0)
end = findFirst(function (ch) { return measure(ch).top > y; }, begin, end)
var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0)
end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end)
return {begin: begin, end: end}
}
function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj) }
var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top
return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
}
// Returns true if the given side of a box is after the given
// coordinates, in top-to-bottom, left-to-right order.
function boxIsAfter(box, x, y, left) {
return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x
}
function coordsCharInner(cm, lineObj, lineNo, x, y) {
// Move y into line-local coordinate space
y -= heightAtLine(lineObj)
var begin = 0, end = lineObj.text.length
var preparedMeasure = prepareMeasureForLine(cm, lineObj)
var pos
// When directly calling `measureCharPrepared`, we have to adjust
// for the widgets at this line.
var widgetHeight = widgetTopHeight(lineObj)
var begin = 0, end = lineObj.text.length, ltr = true
var order = getOrder(lineObj, cm.doc.direction)
// If the line isn't plain left-to-right text, first figure out
// which bidi section the coordinates fall into.
if (order) {
if (cm.options.lineWrapping) {
;var assign;
((assign = wrappedLineExtent(cm, lineObj, preparedMeasure, y), begin = assign.begin, end = assign.end, assign))
}
pos = new Pos(lineNo, Math.floor(begin + (end - begin) / 2))
var beginLeft = cursorCoords(cm, pos, "line", lineObj, preparedMeasure).left
var dir = beginLeft < x ? 1 : -1
var prevDiff, diff = beginLeft - x, prevPos
var steps = Math.ceil((end - begin) / 4)
outer: do {
prevDiff = diff
prevPos = pos
var i = 0
for (; i < steps; ++i) {
var prevPos$1 = pos
pos = moveVisually(cm, lineObj, pos, dir)
if (pos == null || pos.ch < begin || end <= (pos.sticky == "before" ? pos.ch - 1 : pos.ch)) {
pos = prevPos$1
break outer
}
}
diff = cursorCoords(cm, pos, "line", lineObj, preparedMeasure).left - x
if (steps > 1) {
var diff_change_per_step = Math.abs(diff - prevDiff) / steps
steps = Math.min(steps, Math.ceil(Math.abs(diff) / diff_change_per_step))
dir = diff < 0 ? 1 : -1
}
} while (diff != 0 && (steps > 1 || ((dir < 0) != (diff < 0) && (Math.abs(diff) <= Math.abs(prevDiff)))))
if (Math.abs(diff) > Math.abs(prevDiff)) {
if ((diff < 0) == (prevDiff < 0)) { throw new Error("Broke out of infinite loop in coordsCharInner") }
pos = prevPos
}
} else {
var ch = findFirst(function (ch) {
var box = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, ch), "line")
if (box.top > y) {
// For the cursor stickiness
end = Math.min(ch, end)
return true
}
else if (box.bottom <= y) { return false }
else if (box.left > x) { return true }
else if (box.right < x) { return false }
else { return (x - box.left < box.right - x) }
}, begin, end)
ch = skipExtendingChars(lineObj.text, ch, 1)
pos = new Pos(lineNo, ch, ch == end ? "before" : "after")
var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)
(cm, lineObj, lineNo, preparedMeasure, order, x, y)
ltr = part.level != 1
// The awkward -1 offsets are needed because findFirst (called
// on these below) will treat its first bound as inclusive,
// second as exclusive, but we want to actually address the
// characters in the part's range
begin = ltr ? part.from : part.to - 1
end = ltr ? part.to : part.from - 1
}
var coords = cursorCoords(cm, pos, "line", lineObj, preparedMeasure)
if (y < coords.top || coords.bottom < y) { pos.outside = true }
pos.xRel = x < coords.left ? -1 : (x > coords.right ? 1 : 0)
return pos
// A binary search to find the first character whose bounding box
// starts after the coordinates. If we run across any whose box wrap
// the coordinates, store that.
var chAround = null, boxAround = null
var ch = findFirst(function (ch) {
var box = measureCharPrepared(cm, preparedMeasure, ch)
box.top += widgetHeight; box.bottom += widgetHeight
if (!boxIsAfter(box, x, y, false)) { return false }
if (box.top <= y && box.left <= x) {
chAround = ch
boxAround = box
}
return true
}, begin, end)
var baseX, sticky, outside = false
// If a box around the coordinates was found, use that
if (boxAround) {
// Distinguish coordinates nearer to the left or right side of the box
var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr
ch = chAround + (atStart ? 0 : 1)
sticky = atStart ? "after" : "before"
baseX = atLeft ? boxAround.left : boxAround.right
} else {
// (Adjust for extended bound, if necessary.)
if (!ltr && (ch == end || ch == begin)) { ch++ }
// To determine which side to associate with, get the box to the
// left of the character and compare it's vertical position to the
// coordinates
sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" :
(measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ?
"after" : "before"
// Now get accurate coordinates for this place, in order to get a
// base X position
var coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure)
baseX = coords.left
outside = y < coords.top || y >= coords.bottom
}
ch = skipExtendingChars(lineObj.text, ch, 1)
return PosWithInfo(lineNo, ch, sticky, outside, x - baseX)
}
function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) {
// Bidi parts are sorted left-to-right, and in a non-line-wrapping
// situation, we can take this ordering to correspond to the visual
// ordering. This finds the first part whose end is after the given
// coordinates.
var index = findFirst(function (i) {
var part = order[i], ltr = part.level != 1
return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? "before" : "after"),
"line", lineObj, preparedMeasure), x, y, true)
}, 0, order.length - 1)
var part = order[index]
// If this isn't the first part, the part's start is also after
// the coordinates, and the coordinates aren't on the same line as
// that start, move one part back.
if (index > 0) {
var ltr = part.level != 1
var start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? "after" : "before"),
"line", lineObj, preparedMeasure)
if (boxIsAfter(start, x, y, true) && start.top > y)
{ part = order[index - 1] }
}
return part
}
function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {
// In a wrapped line, rtl text on wrapping boundaries can do things
// that don't correspond to the ordering in our `order` array at
// all, so a binary search doesn't work, and we want to return a
// part that only spans one line so that the binary search in
// coordsCharInner is safe. As such, we first find the extent of the
// wrapped line, and then do a flat search in which we discard any
// spans that aren't on the line.
var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);
var begin = ref.begin;
var end = ref.end;
var part = null, closestDist = null
for (var i = 0; i < order.length; i++) {
var p = order[i]
if (p.from >= end || p.to <= begin) { continue }
var ltr = p.level != 1
var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right
// Weigh against spans ending before this, so that they are only
// picked if nothing ends after
var dist = endX < x ? x - endX + 1e9 : endX - x
if (!part || closestDist > dist) {
part = p
closestDist = dist
}
}
if (!part) { part = order[order.length - 1] }
// Clip the part to the wrapped line.
if (part.from < begin) { part = {from: begin, to: part.to, level: part.level} }
if (part.to > end) { part = {from: part.from, to: end, level: part.level} }
return part
}
var measureText
@ -3056,12 +3031,14 @@ function updateSelection(cm) {
}
function prepareSelection(cm, primary) {
if ( primary === void 0 ) primary = true;
var doc = cm.doc, result = {}
var curFragment = result.cursors = document.createDocumentFragment()
var selFragment = result.selection = document.createDocumentFragment()
for (var i = 0; i < doc.sel.ranges.length; i++) {
if (primary === false && i == doc.sel.primIndex) { continue }
if (!primary && i == doc.sel.primIndex) { continue }
var range = doc.sel.ranges[i]
if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue }
var collapsed = range.empty()
@ -3092,6 +3069,8 @@ function drawSelectionCursor(cm, head, output) {
}
}
function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }
// Draws the given range as a highlighted selection
function drawSelectionRange(cm, range, output) {
var display = cm.display, doc = cm.doc
@ -3114,30 +3093,48 @@ function drawSelectionRange(cm, range, output) {
return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
}
iterateBidiSections(getOrder(lineObj, doc.direction), fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir) {
var leftPos = coords(from, "left"), rightPos, left, right
if (from == to) {
rightPos = leftPos
left = right = leftPos.left
} else {
rightPos = coords(to - 1, "right")
if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp }
left = leftPos.left
right = rightPos.right
var order = getOrder(lineObj, doc.direction)
iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {
var fromPos = coords(from, dir == "ltr" ? "left" : "right")
var toPos = coords(to - 1, dir == "ltr" ? "right" : "left")
if (dir == "ltr") {
var fromLeft = fromArg == null && from == 0 ? leftSide : fromPos.left
var toRight = toArg == null && to == lineLen ? rightSide : toPos.right
if (toPos.top - fromPos.top <= 3) { // Single line
add(fromLeft, toPos.top, toRight - fromLeft, toPos.bottom)
} else { // Multiple lines
add(fromLeft, fromPos.top, null, fromPos.bottom)
if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top) }
add(leftSide, toPos.top, toPos.right, toPos.bottom)
}
} else if (from < to) { // RTL
var fromRight = fromArg == null && from == 0 ? rightSide : fromPos.right
var toLeft = toArg == null && to == lineLen ? leftSide : toPos.left
if (toPos.top - fromPos.top <= 3) { // Single line
add(toLeft, toPos.top, fromRight - toLeft, toPos.bottom)
} else { // Multiple lines
var topLeft = leftSide
if (i) {
var topEnd = wrappedLineExtentChar(cm, lineObj, null, from).end
// The coordinates returned for an RTL wrapped space tend to
// be complete bogus, so try to skip that here.
topLeft = coords(topEnd - (/\s/.test(lineObj.text.charAt(topEnd - 1)) ? 2 : 1), "left").left
}
add(topLeft, fromPos.top, fromRight - topLeft, fromPos.bottom)
if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top) }
var botWidth = null
if (i < order.length - 1 || true) {
var botStart = wrappedLineExtentChar(cm, lineObj, null, to).begin
botWidth = coords(botStart, "right").right - toLeft
}
add(toLeft, toPos.top, botWidth, toPos.bottom)
}
}
if (fromArg == null && from == 0) { left = leftSide }
if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part
add(left, leftPos.top, null, leftPos.bottom)
left = leftSide
if (leftPos.bottom < rightPos.top) { add(left, leftPos.bottom, null, rightPos.top) }
}
if (toArg == null && to == lineLen) { right = rightSide }
if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left)
{ start = leftPos }
if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right)
{ end = rightPos }
if (left < leftSide + 1) { left = leftSide }
add(left, rightPos.top, right - left, rightPos.bottom)
if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos }
if (cmpCoords(toPos, start) < 0) { start = toPos }
if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos }
if (cmpCoords(toPos, end) < 0) { end = toPos }
})
return {start: start, end: end}
}
@ -3767,7 +3764,7 @@ function endOperation_R2(op) {
}
if (op.updatedDisplay || op.selectionChanged)
{ op.preparedSelection = display.input.prepareSelection(op.focus) }
{ op.preparedSelection = display.input.prepareSelection() }
}
function endOperation_W2(op) {
@ -3780,7 +3777,7 @@ function endOperation_W2(op) {
cm.display.maxLineChanged = false
}
var takeFocus = op.focus && op.focus == activeElt() && (!document.hasFocus || document.hasFocus())
var takeFocus = op.focus && op.focus == activeElt()
if (op.preparedSelection)
{ cm.display.input.showSelection(op.preparedSelection, takeFocus) }
if (op.updatedDisplay || op.startHeight != cm.doc.height)
@ -5369,7 +5366,8 @@ function makeChangeSingleDocInEditor(cm, change, spans) {
function replaceRange(doc, code, from, to, origin) {
if (!to) { to = from }
if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp }
if (cmp(to, from) < 0) { var assign;
(assign = [to, from], from = assign[0], to = assign[1], assign) }
if (typeof code == "string") { code = doc.splitLines(code) }
makeChange(doc, {from: from, to: to, text: code, origin: origin})
}
@ -6731,6 +6729,112 @@ function deleteNearSelection(cm, compute) {
})
}
function moveCharLogically(line, ch, dir) {
var target = skipExtendingChars(line.text, ch + dir, dir)
return target < 0 || target > line.text.length ? null : target
}
function moveLogically(line, start, dir) {
var ch = moveCharLogically(line, start.ch, dir)
return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
}
function endOfLine(visually, cm, lineObj, lineNo, dir) {
if (visually) {
var order = getOrder(lineObj, cm.doc.direction)
if (order) {
var part = dir < 0 ? lst(order) : order[0]
var moveInStorageOrder = (dir < 0) == (part.level == 1)
var sticky = moveInStorageOrder ? "after" : "before"
var ch
// With a wrapped rtl chunk (possibly spanning multiple bidi parts),
// it could be that the last bidi part is not on the last visual line,
// since visual lines contain content order-consecutive chunks.
// Thus, in rtl, we are looking for the first (content-order) character
// in the rtl chunk that is on the last line (that is, the same line
// as the last (content-order) character).
if (part.level > 0) {
var prep = prepareMeasureForLine(cm, lineObj)
ch = dir < 0 ? lineObj.text.length - 1 : 0
var targetTop = measureCharPrepared(cm, prep, ch).top
ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch)
if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1) }
} else { ch = dir < 0 ? part.to : part.from }
return new Pos(lineNo, ch, sticky)
}
}
return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
}
function moveVisually(cm, line, start, dir) {
var bidi = getOrder(line, cm.doc.direction)
if (!bidi) { return moveLogically(line, start, dir) }
if (start.ch >= line.text.length) {
start.ch = line.text.length
start.sticky = "before"
} else if (start.ch <= 0) {
start.ch = 0
start.sticky = "after"
}
var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]
if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
// Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
// nothing interesting happens.
return moveLogically(line, start, dir)
}
var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); }
var prep
var getWrappedLineExtent = function (ch) {
if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
prep = prep || prepareMeasureForLine(cm, line)
return wrappedLineExtentChar(cm, line, prep, ch)
}
var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch)
if (cm.doc.direction == "rtl" || part.level == 1) {
var moveInStorageOrder = (part.level == 1) == (dir < 0)
var ch = mv(start, moveInStorageOrder ? 1 : -1)
if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
// Case 2: We move within an rtl part or in an rtl editor on the same visual line
var sticky = moveInStorageOrder ? "before" : "after"
return new Pos(start.line, ch, sticky)
}
}
// Case 3: Could not move within this bidi part in this visual line, so leave
// the current bidi part
var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
? new Pos(start.line, mv(ch, 1), "before")
: new Pos(start.line, ch, "after"); }
for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
var part = bidi[partPos]
var moveInStorageOrder = (dir > 0) == (part.level != 1)
var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1)
if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
ch = moveInStorageOrder ? part.from : mv(part.to, -1)
if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
}
}
// Case 3a: Look for other bidi parts on the same visual line
var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent)
if (res) { return res }
// Case 3b: Look for other bidi parts on the next visual line
var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1)
if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh))
if (res) { return res }
}
// Case 4: Nowhere to move
return null
}
// Commands are parameter-less actions that can be performed on an
// editor, mostly used for keybindings.
var commands = {
@ -7292,7 +7396,7 @@ function leftButtonSelect(cm, event, start, behavior) {
anchor = maxPos(oldRange.to(), range.head)
}
var ranges$1 = startSel.ranges.slice(0)
ranges$1[ourIndex] = new Range(clipPos(doc, anchor), head)
ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head))
setSelection(doc, normalizeSelection(ranges$1, ourIndex), sel_mouse)
}
}
@ -7344,13 +7448,52 @@ function leftButtonSelect(cm, event, start, behavior) {
on(document, "mouseup", up)
}
// Used when mouse-selecting to adjust the anchor to the proper side
// of a bidi jump depending on the visual position of the head.
function bidiSimplify(cm, range) {
var anchor = range.anchor;
var head = range.head;
var anchorLine = getLine(cm.doc, anchor.line)
if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range }
var order = getOrder(anchorLine)
if (!order) { return range }
var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index]
if (part.from != anchor.ch && part.to != anchor.ch) { return range }
var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1)
if (boundary == 0 || boundary == order.length) { return range }
// Compute the relative visual position of the head compared to the
// anchor (<0 is to the left, >0 to the right)
var leftSide
if (head.line != anchor.line) {
leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0
} else {
var headIndex = getBidiPartAt(order, head.ch, head.sticky)
var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1)
if (headIndex == boundary - 1 || headIndex == boundary)
{ leftSide = dir < 0 }
else
{ leftSide = dir > 0 }
}
var usePart = order[boundary + (leftSide ? -1 : 0)]
var from = leftSide == (usePart.level == 1)
var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before"
return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head)
}
// Determines whether an event happened in the gutter, and fires the
// handlers for the corresponding event.
function gutterEvent(cm, e, type, prevent) {
var mX, mY
try { mX = e.clientX; mY = e.clientY }
catch(e) { return false }
if (e.touches) {
mX = e.touches[0].clientX
mY = e.touches[0].clientY
} else {
try { mX = e.clientX; mY = e.clientY }
catch(e) { return false }
}
if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
if (prevent) { e_preventDefault(e) }
@ -7688,7 +7831,7 @@ function registerEventHandlers(cm) {
return dx * dx + dy * dy > 20 * 20
}
on(d.scroller, "touchstart", function (e) {
if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) {
if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {
d.input.ensurePolled()
clearTimeout(touchFinished)
var now = +new Date
@ -9472,7 +9615,7 @@ CodeMirror.fromTextArea = fromTextArea
addLegacyProps(CodeMirror)
CodeMirror.version = "5.29.0"
CodeMirror.version = "5.30.0"
return CodeMirror;

View File

@ -28,7 +28,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
var jsKeywords = {
"if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
"return": C, "break": C, "continue": C, "new": kw("new"), "delete": C, "throw": C, "debugger": C,
"return": C, "break": C, "continue": C, "new": kw("new"), "delete": C, "void": C, "throw": C, "debugger": C,
"var": kw("var"), "const": kw("var"), "let": kw("var"),
"function": kw("function"), "catch": kw("catch"),
"for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
@ -398,7 +398,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
function expressionInner(type, noComma) {
if (cx.state.fatArrowAt == cx.stream.start) {
var body = noComma ? arrowBodyNoComma : arrowBody;
if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext);
if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext);
else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
}
@ -443,6 +443,11 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == ".") return cont(property, me);
if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) }
if (type == "regexp") {
cx.state.lastType = cx.marked = "operator"
cx.stream.backUp(cx.stream.pos - cx.stream.start - 1)
return cont(expr)
}
}
function quasi(type, value) {
if (type != "quasi") return pass();
@ -491,6 +496,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
} else if (type == "variable" || cx.style == "keyword") {
cx.marked = "property";
if (value == "get" || value == "set") return cont(getterSetter);
var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params
if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false)))
cx.state.fatArrowAt = cx.stream.pos + m[0].length
return cont(afterprop);
} else if (type == "number" || type == "string") {
cx.marked = jsonldMode ? "property" : (cx.style + " property");
@ -648,7 +656,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, maybetype, statement, popcontext);
if (isTS && value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, functiondef)
}
function funarg(type) {
function funarg(type, value) {
if (value == "@") cont(expression, funarg)
if (type == "spread" || type == "modifier") return cont(funarg);
return pass(pattern, maybetype, maybeAssign);
}
@ -674,7 +683,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
cx.marked = "keyword";
return cont(classBody);
}
if (type == "variable") {
if (type == "variable" || cx.style == "keyword") {
cx.marked = "property";
return cont(isTS ? classfield : functiondef, classBody);
}
@ -736,7 +745,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
function expressionAllowed(stream, state, backUp) {
return state.tokenize == tokenBase &&
/^(?:operator|sof|keyword c|case|new|export|default|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
/^(?:operator|sof|keyword [bc]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
(state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
}

View File

@ -254,6 +254,11 @@ testCM("coordsCharBidi", function(cm) {
}
}, {lineNumbers: true});
testCM("badBidiOptimization", function(cm) {
var coords = cm.charCoords(Pos(0, 34))
eqCharPos(cm.coordsChar({left: coords.right, top: coords.top + 2}), Pos(0, 34))
}, {value: "----------<p class=\"title\">هل يمكنك اختيار مستوى قسط التأمين الذي ترغب بدفعه؟</p>"})
testCM("posFromIndex", function(cm) {
cm.setValue(
"This function should\n" +
@ -1156,6 +1161,16 @@ testCM("measureWrappedEndOfLine", function(cm) {
}
}, {mode: "text/html", value: "0123456789abcde0123456789", lineWrapping: true}, ie_lt8 || opera_lt10);
testCM("measureEndOfLineBidi", function(cm) {
eqCursorPos(cm.coordsChar({left: 5000, top: cm.charCoords(Pos(0, 0)).top}), Pos(0, 8, "after"))
}, {value: "إإإإuuuuإإإإ"})
testCM("measureWrappedBidiLevel2", function(cm) {
cm.setSize(cm.charCoords(Pos(0, 6), "editor").right + 60)
var c9 = cm.charCoords(Pos(0, 9))
eqCharPos(cm.coordsChar({left: c9.right - 1, top: c9.top + 1}), Pos(0, 9))
}, {value: "foobar إإ إإ إإ إإ 555 بببببب", lineWrapping: true})
testCM("measureWrappedBeginOfLine", function(cm) {
if (phantom) return;
cm.setSize(null, "auto");
@ -2468,6 +2483,23 @@ for (var i = 0; i < 5; ++i) {
}
*/
testCM("rtl_wrapped_selection", function(cm) {
cm.setSelection(Pos(0, 10), Pos(0, 190))
is(byClassName(cm.getWrapperElement(), "CodeMirror-selected").length >= 3)
}, {value: new Array(10).join(" فتي تم تضمينها فتي تم"), lineWrapping: true})
testCM("bidi_wrapped_selection", function(cm) {
if (phantom) return
cm.setSize(cm.charCoords(Pos(0, 10), "editor").left)
cm.setSelection(Pos(0, 37), Pos(0, 80))
var blocks = byClassName(cm.getWrapperElement(), "CodeMirror-selected")
is(blocks.length >= 2)
is(blocks.length <= 3)
var boxTop = blocks[0].getBoundingClientRect(), boxBot = blocks[blocks.length - 1].getBoundingClientRect()
is(boxTop.left > cm.charCoords(Pos(0, 1)).right)
is(boxBot.right < cm.charCoords(Pos(0, cm.getLine(0).length - 2)).left)
}, {value: "<p>مفتي11 تم تضمينهفتي تم تضمينها فتي تفتي تم تضمينها فتي تفتي تم تضمينها فتي تفتي تم تضمينها فتي تا فت10ي ت</p>", lineWrapping: true})
testCM("delete_wrapped", function(cm) {
makeItWrapAfter(cm, Pos(0, 2));
cm.doc.setCursor(Pos(0, 3, "after"));