1
0
Fork 0
mirror of https://github.com/DanielnetoDotCom/YouPHPTube synced 2025-10-05 19:42:38 +02:00

Update npm

This commit is contained in:
Daniel Neto 2024-04-03 15:54:35 -03:00
parent 8341712d58
commit 1bd85100b9
5320 changed files with 58396 additions and 344722 deletions

View file

@ -1,3 +1,83 @@
## 6.15.0 (2024-03-13)
### New features
The new `filterStrict` option can be used to turn off fuzzy matching of completions.
## 6.14.0 (2024-03-10)
### New features
Completion results can now define a `map` method that can be used to adjust position-dependent information for document changes.
## 6.13.0 (2024-02-29)
### New features
Completions may now provide 'commit characters' that, when typed, commit the completion before inserting the character.
## 6.12.0 (2024-01-12)
### Bug fixes
Make sure snippet completions also set `userEvent` to `input.complete`.
Fix a crash when the editor lost focus during an update and autocompletion was active.
Fix a crash when using a snippet that has only one field, but multiple instances of that field.
### New features
The new `activateOnTypingDelay` option allows control over the debounce time before the completions are queried when the user types.
## 6.11.1 (2023-11-27)
### Bug fixes
Fix a bug that caused typing over closed brackets after pressing enter to still not work in many situations.
## 6.11.0 (2023-11-09)
### Bug fixes
Fix an issue that would prevent typing over closed brackets after starting a new line with enter.
### New features
Additional elements rendered in completion options with `addToOptions` are now given access to the editor view.
## 6.10.2 (2023-10-13)
### Bug fixes
Fix a bug that caused `updateSyncTime` to always delay the initial population of the tooltip.
## 6.10.1 (2023-10-11)
### Bug fixes
Fix a bug where picking a selection with the mouse could use the wrong completion if the completion list was updated after being opened.
## 6.10.0 (2023-10-11)
### New features
The new autocompletion configuration option `updateSyncTime` allows control over how long fast sources are held back waiting for slower completion sources.
## 6.9.2 (2023-10-06)
### Bug fixes
Fix a bug in `completeAnyWord` that could cause it to generate invalid regular expressions and crash.
## 6.9.1 (2023-09-14)
### Bug fixes
Make sure the cursor is scrolled into view after inserting completion text.
Make sure scrolling completions into view doesn't get confused when the tooltip is scaled.
## 6.9.0 (2023-07-18)
### New features

View file

@ -1,7 +1,5 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var state = require('@codemirror/state');
var view = require('@codemirror/view');
var language = require('@codemirror/language');
@ -175,7 +173,7 @@ function insertCompletionText(state$1, text, from, to) {
changes: { from: range.from + fromOff, to: to == main.from ? range.to : range.from + toOff, insert: text },
range: state.EditorSelection.cursor(range.from + fromOff + text.length)
};
})), { userEvent: "input.complete" });
})), { scrollIntoView: true, userEvent: "input.complete" });
}
const SourceCache = new WeakMap();
function asSource(source) {
@ -216,7 +214,7 @@ class FuzzyMatcher {
ret(score, matched) {
this.score = score;
this.matched = matched;
return true;
return this;
}
// Matches a given word (completion) against the pattern (input).
// Will return a boolean indicating whether there was a match and,
@ -227,25 +225,25 @@ class FuzzyMatcher {
// is. See `Penalty` above.
match(word) {
if (this.pattern.length == 0)
return this.ret(-100 /* NotFull */, []);
return this.ret(-100 /* Penalty.NotFull */, []);
if (word.length < this.pattern.length)
return false;
return null;
let { chars, folded, any, precise, byWord } = this;
// For single-character queries, only match when they occur right
// at the start
if (chars.length == 1) {
let first = state.codePointAt(word, 0), firstSize = state.codePointSize(first);
let score = firstSize == word.length ? 0 : -100 /* NotFull */;
let score = firstSize == word.length ? 0 : -100 /* Penalty.NotFull */;
if (first == chars[0]) ;
else if (first == folded[0])
score += -200 /* CaseFold */;
score += -200 /* Penalty.CaseFold */;
else
return false;
return null;
return this.ret(score, [0, firstSize]);
}
let direct = word.indexOf(this.pattern);
if (direct == 0)
return this.ret(word.length == this.pattern.length ? 0 : -100 /* NotFull */, [0, this.pattern.length]);
return this.ret(word.length == this.pattern.length ? 0 : -100 /* Penalty.NotFull */, [0, this.pattern.length]);
let len = chars.length, anyTo = 0;
if (direct < 0) {
for (let i = 0, e = Math.min(word.length, 200); i < e && anyTo < len;) {
@ -256,7 +254,7 @@ class FuzzyMatcher {
}
// No match, exit immediately
if (anyTo < len)
return false;
return null;
}
// This tracks the extent of the precise (non-folded, not
// necessarily adjacent) match
@ -269,7 +267,7 @@ class FuzzyMatcher {
let adjacentTo = 0, adjacentStart = -1, adjacentEnd = -1;
let hasLower = /[a-z]/.test(word), wordAdjacent = true;
// Go over the option's text, scanning for the various kinds of matches
for (let i = 0, e = Math.min(word.length, 200), prevType = 0 /* NonWord */; i < e && byWordTo < len;) {
for (let i = 0, e = Math.min(word.length, 200), prevType = 0 /* Tp.NonWord */; i < e && byWordTo < len;) {
let next = state.codePointAt(word, i);
if (direct < 0) {
if (preciseTo < len && next == chars[preciseTo])
@ -287,9 +285,9 @@ class FuzzyMatcher {
}
}
let ch, type = next < 0xff
? (next >= 48 && next <= 57 || next >= 97 && next <= 122 ? 2 /* Lower */ : next >= 65 && next <= 90 ? 1 /* Upper */ : 0 /* NonWord */)
: ((ch = state.fromCodePoint(next)) != ch.toLowerCase() ? 1 /* Upper */ : ch != ch.toUpperCase() ? 2 /* Lower */ : 0 /* NonWord */);
if (!i || type == 1 /* Upper */ && hasLower || prevType == 0 /* NonWord */ && type != 0 /* NonWord */) {
? (next >= 48 && next <= 57 || next >= 97 && next <= 122 ? 2 /* Tp.Lower */ : next >= 65 && next <= 90 ? 1 /* Tp.Upper */ : 0 /* Tp.NonWord */)
: ((ch = state.fromCodePoint(next)) != ch.toLowerCase() ? 1 /* Tp.Upper */ : ch != ch.toUpperCase() ? 2 /* Tp.Lower */ : 0 /* Tp.NonWord */);
if (!i || type == 1 /* Tp.Upper */ && hasLower || prevType == 0 /* Tp.NonWord */ && type != 0 /* Tp.NonWord */) {
if (chars[byWordTo] == next || (folded[byWordTo] == next && (byWordFolded = true)))
byWord[byWordTo++] = i;
else if (byWord.length)
@ -299,18 +297,18 @@ class FuzzyMatcher {
i += state.codePointSize(next);
}
if (byWordTo == len && byWord[0] == 0 && wordAdjacent)
return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0), byWord, word);
return this.result(-100 /* Penalty.ByWord */ + (byWordFolded ? -200 /* Penalty.CaseFold */ : 0), byWord, word);
if (adjacentTo == len && adjacentStart == 0)
return this.ret(-200 /* CaseFold */ - word.length + (adjacentEnd == word.length ? 0 : -100 /* NotFull */), [0, adjacentEnd]);
return this.ret(-200 /* Penalty.CaseFold */ - word.length + (adjacentEnd == word.length ? 0 : -100 /* Penalty.NotFull */), [0, adjacentEnd]);
if (direct > -1)
return this.ret(-700 /* NotStart */ - word.length, [direct, direct + this.pattern.length]);
return this.ret(-700 /* Penalty.NotStart */ - word.length, [direct, direct + this.pattern.length]);
if (adjacentTo == len)
return this.ret(-200 /* CaseFold */ + -700 /* NotStart */ - word.length, [adjacentStart, adjacentEnd]);
return this.ret(-200 /* Penalty.CaseFold */ + -700 /* Penalty.NotStart */ - word.length, [adjacentStart, adjacentEnd]);
if (byWordTo == len)
return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0) + -700 /* NotStart */ +
(wordAdjacent ? 0 : -1100 /* Gap */), byWord, word);
return chars.length == 2 ? false
: this.result((any[0] ? -700 /* NotStart */ : 0) + -200 /* CaseFold */ + -1100 /* Gap */, any, word);
return this.result(-100 /* Penalty.ByWord */ + (byWordFolded ? -200 /* Penalty.CaseFold */ : 0) + -700 /* Penalty.NotStart */ +
(wordAdjacent ? 0 : -1100 /* Penalty.Gap */), byWord, word);
return chars.length == 2 ? null
: this.result((any[0] ? -700 /* Penalty.NotStart */ : 0) + -200 /* Penalty.CaseFold */ + -1100 /* Penalty.Gap */, any, word);
}
result(score, positions, word) {
let result = [], i = 0;
@ -326,11 +324,31 @@ class FuzzyMatcher {
return this.ret(score - word.length, result);
}
}
class StrictMatcher {
constructor(pattern) {
this.pattern = pattern;
this.matched = [];
this.score = 0;
this.folded = pattern.toLowerCase();
}
match(word) {
if (word.length < this.pattern.length)
return null;
let start = word.slice(0, this.pattern.length);
let match = start == this.pattern ? 0 : start.toLowerCase() == this.folded ? -200 /* Penalty.CaseFold */ : null;
if (match == null)
return null;
this.matched = [0, start.length];
this.score = match + (word.length == this.pattern.length ? 0 : -100 /* Penalty.NotFull */);
return this;
}
}
const completionConfig = state.Facet.define({
combine(configs) {
return state.combineConfig(configs, {
activateOnTyping: true,
activateOnTypingDelay: 100,
selectOnOpen: true,
override: null,
closeOnBlur: true,
@ -342,22 +360,25 @@ const completionConfig = state.Facet.define({
icons: true,
addToOptions: [],
positionInfo: defaultPositionInfo,
filterStrict: false,
compareCompletions: (a, b) => a.label.localeCompare(b.label),
interactionDelay: 75
interactionDelay: 75,
updateSyncTime: 100
}, {
defaultKeymap: (a, b) => a && b,
closeOnBlur: (a, b) => a && b,
icons: (a, b) => a && b,
tooltipClass: (a, b) => c => joinClass(a(c), b(c)),
optionClass: (a, b) => c => joinClass(a(c), b(c)),
addToOptions: (a, b) => a.concat(b)
addToOptions: (a, b) => a.concat(b),
filterStrict: (a, b) => a || b,
});
}
});
function joinClass(a, b) {
return a ? b ? a + " " + b : a : b;
}
function defaultPositionInfo(view$1, list, option, info, space) {
function defaultPositionInfo(view$1, list, option, info, space, tooltip) {
let rtl = view$1.textDirection == view.Direction.RTL, left = rtl, narrow = false;
let side = "top", offset, maxWidth;
let spaceLeft = list.left - space.left, spaceRight = space.right - list.right;
@ -368,11 +389,11 @@ function defaultPositionInfo(view$1, list, option, info, space) {
left = true;
if (infoWidth <= (left ? spaceLeft : spaceRight)) {
offset = Math.max(space.top, Math.min(option.top, space.bottom - infoHeight)) - list.top;
maxWidth = Math.min(400 /* Width */, left ? spaceLeft : spaceRight);
maxWidth = Math.min(400 /* Info.Width */, left ? spaceLeft : spaceRight);
}
else {
narrow = true;
maxWidth = Math.min(400 /* Width */, (rtl ? list.right : space.right - list.left) - 30 /* Margin */);
maxWidth = Math.min(400 /* Info.Width */, (rtl ? list.right : space.right - list.left) - 30 /* Info.Margin */);
let spaceBelow = space.bottom - list.bottom;
if (spaceBelow >= infoHeight || spaceBelow > list.top) { // Below the completion
offset = option.bottom - list.top;
@ -382,8 +403,10 @@ function defaultPositionInfo(view$1, list, option, info, space) {
offset = list.bottom - option.top;
}
}
let scaleY = (list.bottom - list.top) / tooltip.offsetHeight;
let scaleX = (list.right - list.left) / tooltip.offsetWidth;
return {
style: `${side}: ${offset}px; max-width: ${maxWidth}px`,
style: `${side}: ${offset / scaleY}px; max-width: ${maxWidth / scaleX}px`,
class: "cm-completionInfo-" + (narrow ? (rtl ? "left-narrow" : "right-narrow") : left ? "left" : "right")
};
}
@ -403,7 +426,7 @@ function optionContent(config) {
position: 20
});
content.push({
render(completion, _s, match) {
render(completion, _s, _v, match) {
let labelElt = document.createElement("span");
labelElt.className = "cm-completionLabel";
let label = completion.displayLabel || completion.label, off = 0;
@ -471,6 +494,7 @@ class CompletionTooltip {
this.dom.className = "cm-tooltip-autocomplete";
this.updateTooltipClass(view.state);
this.dom.addEventListener("mousedown", (e) => {
let { options } = view.state.field(stateField).open;
for (let dom = e.target, match; dom && dom != this.dom; dom = dom.parentNode) {
if (dom.nodeName == "LI" && (match = /-(\d+)$/.exec(dom.id)) && +match[1] < options.length) {
this.applyCompletion(view, options[+match[1]]);
@ -485,22 +509,32 @@ class CompletionTooltip {
e.relatedTarget != view.contentDOM)
view.dispatch({ effects: closeCompletionEffect.of(null) });
});
this.list = this.dom.appendChild(this.createListBox(options, cState.id, this.range));
this.showOptions(options, cState.id);
}
mount() { this.updateSel(); }
showOptions(options, id) {
if (this.list)
this.list.remove();
this.list = this.dom.appendChild(this.createListBox(options, id, this.range));
this.list.addEventListener("scroll", () => {
if (this.info)
this.view.requestMeasure(this.placeInfoReq);
});
}
mount() { this.updateSel(); }
update(update) {
var _a, _b, _c;
var _a;
let cState = update.state.field(this.stateField);
let prevState = update.startState.field(this.stateField);
this.updateTooltipClass(update.state);
if (cState != prevState) {
let { options, selected, disabled } = cState.open;
if (!prevState.open || prevState.open.options != options) {
this.range = rangeAroundSelected(options.length, selected, update.state.facet(completionConfig).maxRenderedOptions);
this.showOptions(options, cState.id);
}
this.updateSel();
if (((_a = cState.open) === null || _a === void 0 ? void 0 : _a.disabled) != ((_b = prevState.open) === null || _b === void 0 ? void 0 : _b.disabled))
this.dom.classList.toggle("cm-tooltip-autocomplete-disabled", !!((_c = cState.open) === null || _c === void 0 ? void 0 : _c.disabled));
if (disabled != ((_a = prevState.open) === null || _a === void 0 ? void 0 : _a.disabled))
this.dom.classList.toggle("cm-tooltip-autocomplete-disabled", !!disabled);
}
}
updateTooltipClass(state) {
@ -524,12 +558,7 @@ class CompletionTooltip {
let cState = this.view.state.field(this.stateField), open = cState.open;
if (open.selected > -1 && open.selected < this.range.from || open.selected >= this.range.to) {
this.range = rangeAroundSelected(open.options.length, open.selected, this.view.state.facet(completionConfig).maxRenderedOptions);
this.list.remove();
this.list = this.dom.appendChild(this.createListBox(open.options, cState.id, this.range));
this.list.addEventListener("scroll", () => {
if (this.info)
this.view.requestMeasure(this.placeInfoReq);
});
this.showOptions(open.options, cState.id);
}
if (this.updateSelectedOption(open.selected)) {
this.destroyInfo();
@ -603,7 +632,7 @@ class CompletionTooltip {
if (selRect.top > Math.min(space.bottom, listRect.bottom) - 10 ||
selRect.bottom < Math.max(space.top, listRect.top) + 10)
return null;
return this.view.state.facet(completionConfig).positionInfo(this.view, listRect, selRect, infoRect, space);
return this.view.state.facet(completionConfig).positionInfo(this.view, listRect, selRect, infoRect, space, this.dom);
}
placeInfo(pos) {
if (this.info) {
@ -646,7 +675,7 @@ class CompletionTooltip {
if (cls)
li.className = cls;
for (let source of this.optionContent) {
let node = source(completion, this.view.state, match);
let node = source(completion, this.view.state, this.view, match);
if (node)
li.appendChild(node);
}
@ -669,18 +698,17 @@ class CompletionTooltip {
this.destroyInfo();
}
}
// We allocate a new function instance every time the completion
// changes to force redrawing/repositioning of the tooltip
function completionTooltip(stateField, applyCompletion) {
return (view) => new CompletionTooltip(view, stateField, applyCompletion);
}
function scrollIntoView(container, element) {
let parent = container.getBoundingClientRect();
let self = element.getBoundingClientRect();
let scaleY = parent.height / container.offsetHeight;
if (self.top < parent.top)
container.scrollTop -= parent.top - self.top;
container.scrollTop -= (parent.top - self.top) / scaleY;
else if (self.bottom > parent.bottom)
container.scrollTop += self.bottom - parent.bottom;
container.scrollTop += (self.bottom - parent.bottom) / scaleY;
}
// Used to pick a preferred option when two options with the same
@ -703,6 +731,7 @@ function sortOptions(active, state) {
sections.push(typeof section == "string" ? { name } : section);
}
};
let conf = state.facet(completionConfig);
for (let a of active)
if (a.hasResult()) {
let getMatch = a.result.getMatch;
@ -712,11 +741,12 @@ function sortOptions(active, state) {
}
}
else {
let matcher = new FuzzyMatcher(state.sliceDoc(a.from, a.to));
let pattern = state.sliceDoc(a.from, a.to), match;
let matcher = conf.filterStrict ? new StrictMatcher(pattern) : new FuzzyMatcher(pattern);
for (let option of a.result.options)
if (matcher.match(option.label)) {
let matched = !option.displayLabel ? matcher.matched : getMatch ? getMatch(option, matcher.matched) : [];
addOption(new Option(option, a.source, matched, matcher.score + (option.boost || 0)));
if (match = matcher.match(option.label)) {
let matched = !option.displayLabel ? match.matched : getMatch ? getMatch(option, match.matched) : [];
addOption(new Option(option, a.source, matched, match.score + (option.boost || 0)));
}
}
}
@ -734,7 +764,7 @@ function sortOptions(active, state) {
}
}
let result = [], prev = null;
let compare = state.facet(completionConfig).compareCompletions;
let compare = conf.compareCompletions;
for (let opt of options.sort((a, b) => (b.score - a.score) || compare(a.completion, b.completion))) {
let cur = opt.completion;
if (!prev || prev.label != cur.label || prev.detail != cur.detail ||
@ -763,7 +793,7 @@ class CompletionDialog {
static build(active, state, id, prev, conf) {
let options = sortOptions(active, state);
if (!options.length) {
return prev && active.some(a => a.state == 1 /* Pending */) ?
return prev && active.some(a => a.state == 1 /* State.Pending */) ?
new CompletionDialog(prev.options, prev.attrs, prev.tooltip, prev.timestamp, prev.selected, true) : null;
}
let selected = state.facet(completionConfig).selectOnOpen ? 0 : -1;
@ -777,7 +807,7 @@ class CompletionDialog {
}
return new CompletionDialog(options, makeAttrs(id, selected), {
pos: active.reduce((a, b) => b.hasResult() ? Math.min(a, b.from) : a, 1e8),
create: completionTooltip(completionState, applyCompletion),
create: createTooltip,
above: conf.aboveCursor,
}, prev ? prev.timestamp : Date.now(), selected, false);
}
@ -800,7 +830,7 @@ class CompletionState {
state.languageDataAt("autocomplete", cur(state)).map(asSource);
let active = sources.map(source => {
let value = this.active.find(s => s.source == source) ||
new ActiveSource(source, this.active.some(a => a.state != 0 /* Inactive */) ? 1 /* Pending */ : 0 /* Inactive */);
new ActiveSource(source, this.active.some(a => a.state != 0 /* State.Inactive */) ? 1 /* State.Pending */ : 0 /* State.Inactive */);
return value.update(tr, conf);
});
if (active.length == this.active.length && active.every((a, i) => a == this.active[i]))
@ -811,10 +841,10 @@ class CompletionState {
if (tr.selection || active.some(a => a.hasResult() && tr.changes.touchesRange(a.from, a.to)) ||
!sameResults(active, this.active))
open = CompletionDialog.build(active, state, this.id, open, conf);
else if (open && open.disabled && !active.some(a => a.state == 1 /* Pending */))
else if (open && open.disabled && !active.some(a => a.state == 1 /* State.Pending */))
open = null;
if (!open && active.every(a => a.state != 1 /* Pending */) && active.some(a => a.hasResult()))
active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* Inactive */) : a);
if (!open && active.every(a => a.state != 1 /* State.Pending */) && active.some(a => a.hasResult()))
active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* State.Inactive */) : a);
for (let effect of tr.effects)
if (effect.is(setSelectedEffect))
open = open && open.setSelected(effect.value, this.id);
@ -868,13 +898,13 @@ class ActiveSource {
value = value.handleUserEvent(tr, event, conf);
else if (tr.docChanged)
value = value.handleChange(tr);
else if (tr.selection && value.state != 0 /* Inactive */)
value = new ActiveSource(value.source, 0 /* Inactive */);
else if (tr.selection && value.state != 0 /* State.Inactive */)
value = new ActiveSource(value.source, 0 /* State.Inactive */);
for (let effect of tr.effects) {
if (effect.is(startCompletionEffect))
value = new ActiveSource(value.source, 1 /* Pending */, effect.value ? cur(tr.state) : -1);
value = new ActiveSource(value.source, 1 /* State.Pending */, effect.value ? cur(tr.state) : -1);
else if (effect.is(closeCompletionEffect))
value = new ActiveSource(value.source, 0 /* Inactive */);
value = new ActiveSource(value.source, 0 /* State.Inactive */);
else if (effect.is(setActiveEffect))
for (let active of effect.value)
if (active.source == value.source)
@ -883,10 +913,10 @@ class ActiveSource {
return value;
}
handleUserEvent(tr, type, conf) {
return type == "delete" || !conf.activateOnTyping ? this.map(tr.changes) : new ActiveSource(this.source, 1 /* Pending */);
return type == "delete" || !conf.activateOnTyping ? this.map(tr.changes) : new ActiveSource(this.source, 1 /* State.Pending */);
}
handleChange(tr) {
return tr.changes.touchesRange(cur(tr.startState)) ? new ActiveSource(this.source, 0 /* Inactive */) : this.map(tr.changes);
return tr.changes.touchesRange(cur(tr.startState)) ? new ActiveSource(this.source, 0 /* State.Inactive */) : this.map(tr.changes);
}
map(changes) {
return changes.empty || this.explicitPos < 0 ? this : new ActiveSource(this.source, this.state, changes.mapPos(this.explicitPos));
@ -894,7 +924,7 @@ class ActiveSource {
}
class ActiveResult extends ActiveSource {
constructor(source, explicitPos, result, from, to) {
super(source, 2 /* Result */, explicitPos);
super(source, 2 /* State.Result */, explicitPos);
this.result = result;
this.from = from;
this.to = to;
@ -902,26 +932,33 @@ class ActiveResult extends ActiveSource {
hasResult() { return true; }
handleUserEvent(tr, type, conf) {
var _a;
let result = this.result;
if (result.map && !tr.changes.empty)
result = result.map(result, tr.changes);
let from = tr.changes.mapPos(this.from), to = tr.changes.mapPos(this.to, 1);
let pos = cur(tr.state);
if ((this.explicitPos < 0 ? pos <= from : pos < this.from) ||
pos > to ||
pos > to || !result ||
type == "delete" && cur(tr.startState) == this.from)
return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* Pending */ : 0 /* Inactive */);
let explicitPos = this.explicitPos < 0 ? -1 : tr.changes.mapPos(this.explicitPos), updated;
if (checkValid(this.result.validFor, tr.state, from, to))
return new ActiveResult(this.source, explicitPos, this.result, from, to);
if (this.result.update &&
(updated = this.result.update(this.result, from, to, new CompletionContext(tr.state, pos, explicitPos >= 0))))
return new ActiveResult(this.source, explicitPos, updated, updated.from, (_a = updated.to) !== null && _a !== void 0 ? _a : cur(tr.state));
return new ActiveSource(this.source, 1 /* Pending */, explicitPos);
return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* State.Pending */ : 0 /* State.Inactive */);
let explicitPos = this.explicitPos < 0 ? -1 : tr.changes.mapPos(this.explicitPos);
if (checkValid(result.validFor, tr.state, from, to))
return new ActiveResult(this.source, explicitPos, result, from, to);
if (result.update &&
(result = result.update(result, from, to, new CompletionContext(tr.state, pos, explicitPos >= 0))))
return new ActiveResult(this.source, explicitPos, result, result.from, (_a = result.to) !== null && _a !== void 0 ? _a : cur(tr.state));
return new ActiveSource(this.source, 1 /* State.Pending */, explicitPos);
}
handleChange(tr) {
return tr.changes.touchesRange(this.from, this.to) ? new ActiveSource(this.source, 0 /* Inactive */) : this.map(tr.changes);
return tr.changes.touchesRange(this.from, this.to) ? new ActiveSource(this.source, 0 /* State.Inactive */) : this.map(tr.changes);
}
map(mapping) {
return mapping.empty ? this :
new ActiveResult(this.source, this.explicitPos < 0 ? -1 : mapping.mapPos(this.explicitPos), this.result, mapping.mapPos(this.from), mapping.mapPos(this.to, 1));
if (mapping.empty)
return this;
let result = this.result.map ? this.result.map(this.result, mapping) : this.result;
if (!result)
return new ActiveSource(this.source, 0 /* State.Inactive */);
return new ActiveResult(this.source, this.explicitPos < 0 ? -1 : mapping.mapPos(this.explicitPos), this.result, mapping.mapPos(this.from), mapping.mapPos(this.to, 1));
}
}
function checkValid(validFor, state, from, to) {
@ -953,6 +990,7 @@ function applyCompletion(view, option) {
apply(view, option.completion, result.from, result.to);
return true;
}
const createTooltip = completionTooltip(completionState, applyCompletion);
/**
Returns a command that moves the completion selection forward or
@ -1003,7 +1041,7 @@ Close the currently active completion.
*/
const closeCompletion = (view) => {
let cState = view.state.field(completionState, false);
if (!cState || !cState.active.some(a => a.state != 0 /* Inactive */))
if (!cState || !cState.active.some(a => a.state != 0 /* State.Inactive */))
return false;
view.dispatch({ effects: closeCompletionEffect.of(null) });
return true;
@ -1019,16 +1057,17 @@ class RunningQuery {
this.done = undefined;
}
}
const DebounceTime = 50, MaxUpdateCount = 50, MinAbortTime = 1000;
const MaxUpdateCount = 50, MinAbortTime = 1000;
const completionPlugin = view.ViewPlugin.fromClass(class {
constructor(view) {
this.view = view;
this.debounceUpdate = -1;
this.running = [];
this.debounceAccept = -1;
this.composing = 0 /* None */;
this.pendingStart = false;
this.composing = 0 /* CompositionState.None */;
for (let active of view.state.field(completionState).active)
if (active.state == 1 /* Pending */)
if (active.state == 1 /* State.Pending */)
this.startQuery(active);
}
update(update) {
@ -1059,21 +1098,25 @@ const completionPlugin = view.ViewPlugin.fromClass(class {
}
if (this.debounceUpdate > -1)
clearTimeout(this.debounceUpdate);
this.debounceUpdate = cState.active.some(a => a.state == 1 /* Pending */ && !this.running.some(q => q.active.source == a.source))
? setTimeout(() => this.startUpdate(), DebounceTime) : -1;
if (this.composing != 0 /* None */)
if (update.transactions.some(tr => tr.effects.some(e => e.is(startCompletionEffect))))
this.pendingStart = true;
let delay = this.pendingStart ? 50 : update.state.facet(completionConfig).activateOnTypingDelay;
this.debounceUpdate = cState.active.some(a => a.state == 1 /* State.Pending */ && !this.running.some(q => q.active.source == a.source))
? setTimeout(() => this.startUpdate(), delay) : -1;
if (this.composing != 0 /* CompositionState.None */)
for (let tr of update.transactions) {
if (getUserEvent(tr) == "input")
this.composing = 2 /* Changed */;
else if (this.composing == 2 /* Changed */ && tr.selection)
this.composing = 3 /* ChangedAndMoved */;
this.composing = 2 /* CompositionState.Changed */;
else if (this.composing == 2 /* CompositionState.Changed */ && tr.selection)
this.composing = 3 /* CompositionState.ChangedAndMoved */;
}
}
startUpdate() {
this.debounceUpdate = -1;
this.pendingStart = false;
let { state } = this.view, cState = state.field(completionState);
for (let active of cState.active) {
if (active.state == 1 /* Pending */ && !this.running.some(r => r.active.source == active.source))
if (active.state == 1 /* State.Pending */ && !this.running.some(r => r.active.source == active.source))
this.startQuery(active);
}
}
@ -1096,7 +1139,7 @@ const completionPlugin = view.ViewPlugin.fromClass(class {
if (this.running.every(q => q.done !== undefined))
this.accept();
else if (this.debounceAccept < 0)
this.debounceAccept = setTimeout(() => this.accept(), DebounceTime);
this.debounceAccept = setTimeout(() => this.accept(), this.view.state.facet(completionConfig).updateSyncTime);
}
// For each finished query in this.running, try to create a result
// or, if appropriate, restart the query.
@ -1124,14 +1167,14 @@ const completionPlugin = view.ViewPlugin.fromClass(class {
}
}
let current = this.view.state.field(completionState).active.find(a => a.source == query.active.source);
if (current && current.state == 1 /* Pending */) {
if (current && current.state == 1 /* State.Pending */) {
if (query.done == null) {
// Explicitly failed. Should clear the pending status if it
// hasn't been re-set in the meantime.
let active = new ActiveSource(query.active.source, 0 /* Inactive */);
let active = new ActiveSource(query.active.source, 0 /* State.Inactive */);
for (let tr of query.updates)
active = active.update(tr, conf);
if (active.state != 1 /* Pending */)
if (active.state != 1 /* State.Pending */)
updated.push(active);
}
else {
@ -1150,22 +1193,37 @@ const completionPlugin = view.ViewPlugin.fromClass(class {
if (state && state.tooltip && this.view.state.facet(completionConfig).closeOnBlur) {
let dialog = state.open && view.getTooltip(this.view, state.open.tooltip);
if (!dialog || !dialog.dom.contains(event.relatedTarget))
this.view.dispatch({ effects: closeCompletionEffect.of(null) });
setTimeout(() => this.view.dispatch({ effects: closeCompletionEffect.of(null) }), 10);
}
},
compositionstart() {
this.composing = 1 /* Started */;
this.composing = 1 /* CompositionState.Started */;
},
compositionend() {
if (this.composing == 3 /* ChangedAndMoved */) {
if (this.composing == 3 /* CompositionState.ChangedAndMoved */) {
// Safari fires compositionend events synchronously, possibly
// from inside an update, so dispatch asynchronously to avoid reentrancy
setTimeout(() => this.view.dispatch({ effects: startCompletionEffect.of(false) }), 20);
}
this.composing = 0 /* None */;
this.composing = 0 /* CompositionState.None */;
}
}
});
const windows = typeof navigator == "object" && /Win/.test(navigator.platform);
const commitCharacters = state.Prec.highest(view.EditorView.domEventHandlers({
keydown(event, view) {
let field = view.state.field(completionState, false);
if (!field || !field.open || field.open.disabled || field.open.selected < 0 ||
event.key.length > 1 || event.ctrlKey && !(windows && event.altKey) || event.metaKey)
return false;
let option = field.open.options[field.open.selected];
let result = field.active.find(a => a.source == option.source);
let commitChars = option.completion.commitCharacters || result.result.commitCharacters;
if (commitChars && commitChars.indexOf(event.key) > -1)
applyCompletion(view, option);
return false;
}
}));
const baseTheme = view.EditorView.baseTheme({
".cm-tooltip.cm-tooltip-autocomplete": {
@ -1222,13 +1280,13 @@ const baseTheme = view.EditorView.baseTheme({
position: "absolute",
padding: "3px 9px",
width: "max-content",
maxWidth: `${400 /* Width */}px`,
maxWidth: `${400 /* Info.Width */}px`,
boxSizing: "border-box"
},
".cm-completionInfo.cm-completionInfo-left": { right: "100%" },
".cm-completionInfo.cm-completionInfo-right": { left: "100%" },
".cm-completionInfo.cm-completionInfo-left-narrow": { right: `${30 /* Margin */}px` },
".cm-completionInfo.cm-completionInfo-right-narrow": { left: `${30 /* Margin */}px` },
".cm-completionInfo.cm-completionInfo-left-narrow": { right: `${30 /* Info.Margin */}px` },
".cm-completionInfo.cm-completionInfo-right-narrow": { left: `${30 /* Info.Margin */}px` },
"&light .cm-snippetField": { backgroundColor: "#00000022" },
"&dark .cm-snippetField": { backgroundColor: "#ffffff22" },
".cm-snippetFieldPosition": {
@ -1458,11 +1516,11 @@ function snippet(template) {
let spec = {
changes: { from, to, insert: state.Text.of(text) },
scrollIntoView: true,
annotations: completion ? pickedCompletion.of(completion) : undefined
annotations: completion ? [pickedCompletion.of(completion), state.Transaction.userEvent.of("input.complete")] : undefined
};
if (ranges.length)
spec.selection = fieldSelection(ranges, 0);
if (ranges.length > 1) {
if (ranges.some(r => r.field > 0)) {
let active = new ActiveSnippet(ranges, 0);
let effects = spec.effects = [setActive.of(active)];
if (editor.state.field(snippetState, false) === undefined)
@ -1479,7 +1537,8 @@ function moveField(dir) {
let next = active.active + dir, last = dir > 0 && !active.ranges.some(r => r.field == next + dir);
dispatch(state.update({
selection: fieldSelection(active.ranges, next),
effects: setActive.of(last ? null : new ActiveSnippet(active.ranges, next))
effects: setActive.of(last ? null : new ActiveSnippet(active.ranges, next)),
scrollIntoView: true
}));
return true;
};
@ -1551,14 +1610,16 @@ const snippetPointerHandler = view.EditorView.domEventHandlers({
return false;
view.dispatch({
selection: fieldSelection(active.ranges, match.field),
effects: setActive.of(active.ranges.some(r => r.field > match.field) ? new ActiveSnippet(active.ranges, match.field) : null)
effects: setActive.of(active.ranges.some(r => r.field > match.field)
? new ActiveSnippet(active.ranges, match.field) : null),
scrollIntoView: true
});
return true;
}
});
function wordRE(wordChars) {
let escaped = wordChars.replace(/[\\[.+*?(){|^$]/g, "\\$&");
let escaped = wordChars.replace(/[\]\-\\]/g, "\\$&");
try {
return new RegExp(`[\\p{Alphabetic}\\p{Number}_${escaped}]+`, "ug");
}
@ -1581,7 +1642,7 @@ function storeWords(doc, wordRE, result, seen, ignoreAt) {
if (!seen[m[0]] && pos + m.index != ignoreAt) {
result.push({ type: "text", label: m[0] });
seen[m[0]] = true;
if (result.length >= 2000 /* MaxList */)
if (result.length >= 2000 /* C.MaxList */)
return;
}
}
@ -1589,7 +1650,7 @@ function storeWords(doc, wordRE, result, seen, ignoreAt) {
}
}
function collectWords(doc, cache, wordRE, to, ignoreAt) {
let big = doc.length >= 1000 /* MinCacheLen */;
let big = doc.length >= 1000 /* C.MinCacheLen */;
let cached = big && cache.get(doc);
if (cached)
return cached;
@ -1597,7 +1658,7 @@ function collectWords(doc, cache, wordRE, to, ignoreAt) {
if (doc.children) {
let pos = 0;
for (let ch of doc.children) {
if (ch.length >= 1000 /* MinCacheLen */) {
if (ch.length >= 1000 /* C.MinCacheLen */) {
for (let c of collectWords(ch, cache, wordRE, to - pos, ignoreAt - pos)) {
if (!seen[c.label]) {
seen[c.label] = true;
@ -1614,7 +1675,7 @@ function collectWords(doc, cache, wordRE, to, ignoreAt) {
else {
storeWords(doc, wordRE, result, seen, ignoreAt);
}
if (big && result.length < 2000 /* MaxList */)
if (big && result.length < 2000 /* C.MaxList */)
cache.set(doc, result);
return result;
}
@ -1630,7 +1691,7 @@ const completeAnyWord = context => {
if (!token && !context.explicit)
return null;
let from = token ? token.from : context.pos;
let options = collectWords(context.state.doc, wordCache(wordChars), re, 50000 /* Range */, from);
let options = collectWords(context.state.doc, wordCache(wordChars), re, 50000 /* C.Range */, from);
return { from, options, validFor: mapRE(re, s => "^" + s) };
};
@ -1652,13 +1713,11 @@ closedBracket.endSide = -1;
const bracketState = state.StateField.define({
create() { return state.RangeSet.empty; },
update(value, tr) {
if (tr.selection) {
let lineStart = tr.state.doc.lineAt(tr.selection.main.head).from;
let prevLineStart = tr.startState.doc.lineAt(tr.startState.selection.main.head).from;
if (lineStart != tr.changes.mapPos(prevLineStart, -1))
value = state.RangeSet.empty;
}
value = value.map(tr.changes);
if (tr.selection) {
let line = tr.state.doc.lineAt(tr.selection.main.head);
value = value.update({ filter: from => from >= line.from && from <= line.to });
}
for (let effect of tr.effects)
if (effect.is(closeBracketEffect))
value = value.update({ add: [closedBracket.range(effect.value, effect.value + 1)] });
@ -1886,6 +1945,7 @@ Returns an extension that enables autocompletion.
*/
function autocompletion(config = {}) {
return [
commitCharacters,
completionState,
completionConfig.of(config),
completionPlugin,
@ -1922,8 +1982,8 @@ returns `null`.
*/
function completionStatus(state) {
let cState = state.field(completionState, false);
return cState && cState.active.some(a => a.state == 1 /* Pending */) ? "pending"
: cState && cState.active.some(a => a.state != 0 /* Inactive */) ? "active" : null;
return cState && cState.active.some(a => a.state == 1 /* State.Pending */) ? "pending"
: cState && cState.active.some(a => a.state != 0 /* State.Inactive */) ? "active" : null;
}
const completionArrayCache = new WeakMap;
/**

View file

@ -1,5 +1,5 @@
import * as _codemirror_state from '@codemirror/state';
import { EditorState, TransactionSpec, Transaction, StateCommand, Facet, Extension, StateEffect } from '@codemirror/state';
import { EditorState, ChangeDesc, TransactionSpec, Transaction, StateCommand, Facet, Extension, StateEffect } from '@codemirror/state';
import { EditorView, Rect, KeyBinding, Command } from '@codemirror/view';
import * as _lezer_common from '@lezer/common';
@ -9,7 +9,7 @@ Objects type used to represent individual completions.
interface Completion {
/**
The label to show in the completion picker. This is what input
is matched agains to determine whether a completion matches (and
is matched against to determine whether a completion matches (and
how well it matches).
*/
label: string;
@ -54,6 +54,11 @@ interface Completion {
*/
type?: string;
/**
When this option is selected, and one of these characters is
typed, insert the completion before typing the character.
*/
commitCharacters?: readonly string[];
/**
When given, should be a number from -99 to 99 that adjusts how
this completion is ranked compared to other completions that
match the input as well as this one. A negative number moves it
@ -75,7 +80,7 @@ The type returned from
node, null to indicate there is no info, or an object with an
optional `destroy` method that cleans up the node.
*/
declare type CompletionInfo = Node | null | {
type CompletionInfo = Node | null | {
dom: Node;
destroy?(): void;
};
@ -196,7 +201,7 @@ may return its [result](https://codemirror.net/6/docs/ref/#autocomplete.Completi
synchronously or as a promise. Returning null indicates no
completions are available.
*/
declare type CompletionSource = (context: CompletionContext) => CompletionResult | null | Promise<CompletionResult | null>;
type CompletionSource = (context: CompletionContext) => CompletionResult | null | Promise<CompletionResult | null>;
/**
Interface for objects returned by completion sources.
*/
@ -255,6 +260,20 @@ interface CompletionResult {
completion still applies in the new state.
*/
update?: (current: CompletionResult, from: number, to: number, context: CompletionContext) => CompletionResult | null;
/**
When results contain position-dependent information in, for
example, `apply` methods, you can provide this method to update
the result for transactions that happen after the query. It is
not necessary to update `from` and `to`those are tracked
automatically.
*/
map?: (current: CompletionResult, changes: ChangeDesc) => CompletionResult | null;
/**
Set a default set of [commit
characters](https://codemirror.net/6/docs/ref/#autocomplete.Completion.commitCharacters) for all
options in this result.
*/
commitCharacters?: readonly string[];
}
/**
This annotation is added to transactions that are produced by
@ -275,6 +294,14 @@ interface CompletionConfig {
*/
activateOnTyping?: boolean;
/**
The amount of time to wait for further typing before querying
completion sources via
[`activateOnTyping`](https://codemirror.net/6/docs/ref/#autocomplete.autocompletion^config.activateOnTyping).
Defaults to 100, which should be fine unless your completion
source is very slow and/or doesn't use `validFor`.
*/
activateOnTypingDelay?: number;
/**
By default, when completion opens, the first option is selected
and can be confirmed with
[`acceptCompletion`](https://codemirror.net/6/docs/ref/#autocomplete.acceptCompletion). When this
@ -340,17 +367,17 @@ interface CompletionConfig {
80.
*/
addToOptions?: {
render: (completion: Completion, state: EditorState) => Node | null;
render: (completion: Completion, state: EditorState, view: EditorView) => Node | null;
position: number;
}[];
/**
By default, [info](https://codemirror.net/6/docs/ref/#autocomplet.Completion.info) tooltips are
placed to the side of the selected. This option can be used to
override that. It will be given rectangles for the list of
completions, the selected option, the info element, and the
availble [tooltip space](https://codemirror.net/6/docs/ref/#view.tooltips^config.tooltipSpace),
and should return style and/or class strings for the info
element.
By default, [info](https://codemirror.net/6/docs/ref/#autocomplete.Completion.info) tooltips are
placed to the side of the selected completion. This option can
be used to override that. It will be given rectangles for the
list of completions, the selected option, the info element, and
the availble [tooltip
space](https://codemirror.net/6/docs/ref/#view.tooltips^config.tooltipSpace), and should return
style and/or class strings for the info element.
*/
positionInfo?: (view: EditorView, list: Rect, option: Rect, info: Rect, space: Rect) => {
style?: string;
@ -363,12 +390,26 @@ interface CompletionConfig {
*/
compareCompletions?: (a: Completion, b: Completion) => number;
/**
When set to true (the default is false), turn off fuzzy matching
of completions and only show those that start with the text the
user typed. Only takes effect for results where
[`filter`](https://codemirror.net/6/docs/ref/#autocomplete.CompletionResult.filter) isn't false.
*/
filterStrict?: boolean;
/**
By default, commands relating to an open completion only take
effect 75 milliseconds after the completion opened, so that key
presses made before the user is aware of the tooltip don't go to
the tooltip. This option can be used to configure that delay.
*/
interactionDelay?: number;
/**
When there are multiple asynchronous completion sources, this
controls how long the extension waits for a slow source before
displaying results from faster sources. Defaults to 100
milliseconds.
*/
updateSyncTime?: number;
}
/**
@ -564,4 +605,4 @@ the currently selected completion.
*/
declare function setSelectedCompletion(index: number): StateEffect<unknown>;
export { CloseBracketConfig, Completion, CompletionContext, CompletionInfo, CompletionResult, CompletionSection, CompletionSource, acceptCompletion, autocompletion, clearSnippet, closeBrackets, closeBracketsKeymap, closeCompletion, completeAnyWord, completeFromList, completionKeymap, completionStatus, currentCompletions, deleteBracketPair, hasNextSnippetField, hasPrevSnippetField, ifIn, ifNotIn, insertBracket, insertCompletionText, moveCompletionSelection, nextSnippetField, pickedCompletion, prevSnippetField, selectedCompletion, selectedCompletionIndex, setSelectedCompletion, snippet, snippetCompletion, snippetKeymap, startCompletion };
export { type CloseBracketConfig, type Completion, CompletionContext, type CompletionInfo, type CompletionResult, type CompletionSection, type CompletionSource, acceptCompletion, autocompletion, clearSnippet, closeBrackets, closeBracketsKeymap, closeCompletion, completeAnyWord, completeFromList, completionKeymap, completionStatus, currentCompletions, deleteBracketPair, hasNextSnippetField, hasPrevSnippetField, ifIn, ifNotIn, insertBracket, insertCompletionText, moveCompletionSelection, nextSnippetField, pickedCompletion, prevSnippetField, selectedCompletion, selectedCompletionIndex, setSelectedCompletion, snippet, snippetCompletion, snippetKeymap, startCompletion };

View file

@ -1,5 +1,5 @@
import * as _codemirror_state from '@codemirror/state';
import { EditorState, TransactionSpec, Transaction, StateCommand, Facet, Extension, StateEffect } from '@codemirror/state';
import { EditorState, ChangeDesc, TransactionSpec, Transaction, StateCommand, Facet, Extension, StateEffect } from '@codemirror/state';
import { EditorView, Rect, KeyBinding, Command } from '@codemirror/view';
import * as _lezer_common from '@lezer/common';
@ -9,7 +9,7 @@ Objects type used to represent individual completions.
interface Completion {
/**
The label to show in the completion picker. This is what input
is matched agains to determine whether a completion matches (and
is matched against to determine whether a completion matches (and
how well it matches).
*/
label: string;
@ -54,6 +54,11 @@ interface Completion {
*/
type?: string;
/**
When this option is selected, and one of these characters is
typed, insert the completion before typing the character.
*/
commitCharacters?: readonly string[];
/**
When given, should be a number from -99 to 99 that adjusts how
this completion is ranked compared to other completions that
match the input as well as this one. A negative number moves it
@ -75,7 +80,7 @@ The type returned from
node, null to indicate there is no info, or an object with an
optional `destroy` method that cleans up the node.
*/
declare type CompletionInfo = Node | null | {
type CompletionInfo = Node | null | {
dom: Node;
destroy?(): void;
};
@ -196,7 +201,7 @@ may return its [result](https://codemirror.net/6/docs/ref/#autocomplete.Completi
synchronously or as a promise. Returning null indicates no
completions are available.
*/
declare type CompletionSource = (context: CompletionContext) => CompletionResult | null | Promise<CompletionResult | null>;
type CompletionSource = (context: CompletionContext) => CompletionResult | null | Promise<CompletionResult | null>;
/**
Interface for objects returned by completion sources.
*/
@ -255,6 +260,20 @@ interface CompletionResult {
completion still applies in the new state.
*/
update?: (current: CompletionResult, from: number, to: number, context: CompletionContext) => CompletionResult | null;
/**
When results contain position-dependent information in, for
example, `apply` methods, you can provide this method to update
the result for transactions that happen after the query. It is
not necessary to update `from` and `to`those are tracked
automatically.
*/
map?: (current: CompletionResult, changes: ChangeDesc) => CompletionResult | null;
/**
Set a default set of [commit
characters](https://codemirror.net/6/docs/ref/#autocomplete.Completion.commitCharacters) for all
options in this result.
*/
commitCharacters?: readonly string[];
}
/**
This annotation is added to transactions that are produced by
@ -275,6 +294,14 @@ interface CompletionConfig {
*/
activateOnTyping?: boolean;
/**
The amount of time to wait for further typing before querying
completion sources via
[`activateOnTyping`](https://codemirror.net/6/docs/ref/#autocomplete.autocompletion^config.activateOnTyping).
Defaults to 100, which should be fine unless your completion
source is very slow and/or doesn't use `validFor`.
*/
activateOnTypingDelay?: number;
/**
By default, when completion opens, the first option is selected
and can be confirmed with
[`acceptCompletion`](https://codemirror.net/6/docs/ref/#autocomplete.acceptCompletion). When this
@ -340,17 +367,17 @@ interface CompletionConfig {
80.
*/
addToOptions?: {
render: (completion: Completion, state: EditorState) => Node | null;
render: (completion: Completion, state: EditorState, view: EditorView) => Node | null;
position: number;
}[];
/**
By default, [info](https://codemirror.net/6/docs/ref/#autocomplet.Completion.info) tooltips are
placed to the side of the selected. This option can be used to
override that. It will be given rectangles for the list of
completions, the selected option, the info element, and the
availble [tooltip space](https://codemirror.net/6/docs/ref/#view.tooltips^config.tooltipSpace),
and should return style and/or class strings for the info
element.
By default, [info](https://codemirror.net/6/docs/ref/#autocomplete.Completion.info) tooltips are
placed to the side of the selected completion. This option can
be used to override that. It will be given rectangles for the
list of completions, the selected option, the info element, and
the availble [tooltip
space](https://codemirror.net/6/docs/ref/#view.tooltips^config.tooltipSpace), and should return
style and/or class strings for the info element.
*/
positionInfo?: (view: EditorView, list: Rect, option: Rect, info: Rect, space: Rect) => {
style?: string;
@ -363,12 +390,26 @@ interface CompletionConfig {
*/
compareCompletions?: (a: Completion, b: Completion) => number;
/**
When set to true (the default is false), turn off fuzzy matching
of completions and only show those that start with the text the
user typed. Only takes effect for results where
[`filter`](https://codemirror.net/6/docs/ref/#autocomplete.CompletionResult.filter) isn't false.
*/
filterStrict?: boolean;
/**
By default, commands relating to an open completion only take
effect 75 milliseconds after the completion opened, so that key
presses made before the user is aware of the tooltip don't go to
the tooltip. This option can be used to configure that delay.
*/
interactionDelay?: number;
/**
When there are multiple asynchronous completion sources, this
controls how long the extension waits for a slow source before
displaying results from faster sources. Defaults to 100
milliseconds.
*/
updateSyncTime?: number;
}
/**
@ -564,4 +605,4 @@ the currently selected completion.
*/
declare function setSelectedCompletion(index: number): StateEffect<unknown>;
export { CloseBracketConfig, Completion, CompletionContext, CompletionInfo, CompletionResult, CompletionSection, CompletionSource, acceptCompletion, autocompletion, clearSnippet, closeBrackets, closeBracketsKeymap, closeCompletion, completeAnyWord, completeFromList, completionKeymap, completionStatus, currentCompletions, deleteBracketPair, hasNextSnippetField, hasPrevSnippetField, ifIn, ifNotIn, insertBracket, insertCompletionText, moveCompletionSelection, nextSnippetField, pickedCompletion, prevSnippetField, selectedCompletion, selectedCompletionIndex, setSelectedCompletion, snippet, snippetCompletion, snippetKeymap, startCompletion };
export { type CloseBracketConfig, type Completion, CompletionContext, type CompletionInfo, type CompletionResult, type CompletionSection, type CompletionSource, acceptCompletion, autocompletion, clearSnippet, closeBrackets, closeBracketsKeymap, closeCompletion, completeAnyWord, completeFromList, completionKeymap, completionStatus, currentCompletions, deleteBracketPair, hasNextSnippetField, hasPrevSnippetField, ifIn, ifNotIn, insertBracket, insertCompletionText, moveCompletionSelection, nextSnippetField, pickedCompletion, prevSnippetField, selectedCompletion, selectedCompletionIndex, setSelectedCompletion, snippet, snippetCompletion, snippetKeymap, startCompletion };

View file

@ -1,4 +1,4 @@
import { Annotation, StateEffect, EditorSelection, codePointAt, codePointSize, fromCodePoint, Facet, combineConfig, StateField, Prec, Text, MapMode, RangeValue, RangeSet, CharCategory } from '@codemirror/state';
import { Annotation, StateEffect, EditorSelection, codePointAt, codePointSize, fromCodePoint, Facet, combineConfig, StateField, Prec, Text, Transaction, MapMode, RangeValue, RangeSet, CharCategory } from '@codemirror/state';
import { Direction, logException, showTooltip, EditorView, ViewPlugin, getTooltip, Decoration, WidgetType, keymap } from '@codemirror/view';
import { syntaxTree, indentUnit } from '@codemirror/language';
@ -171,7 +171,7 @@ function insertCompletionText(state, text, from, to) {
changes: { from: range.from + fromOff, to: to == main.from ? range.to : range.from + toOff, insert: text },
range: EditorSelection.cursor(range.from + fromOff + text.length)
};
})), { userEvent: "input.complete" });
})), { scrollIntoView: true, userEvent: "input.complete" });
}
const SourceCache = /*@__PURE__*/new WeakMap();
function asSource(source) {
@ -212,7 +212,7 @@ class FuzzyMatcher {
ret(score, matched) {
this.score = score;
this.matched = matched;
return true;
return this;
}
// Matches a given word (completion) against the pattern (input).
// Will return a boolean indicating whether there was a match and,
@ -223,25 +223,25 @@ class FuzzyMatcher {
// is. See `Penalty` above.
match(word) {
if (this.pattern.length == 0)
return this.ret(-100 /* NotFull */, []);
return this.ret(-100 /* Penalty.NotFull */, []);
if (word.length < this.pattern.length)
return false;
return null;
let { chars, folded, any, precise, byWord } = this;
// For single-character queries, only match when they occur right
// at the start
if (chars.length == 1) {
let first = codePointAt(word, 0), firstSize = codePointSize(first);
let score = firstSize == word.length ? 0 : -100 /* NotFull */;
let score = firstSize == word.length ? 0 : -100 /* Penalty.NotFull */;
if (first == chars[0]) ;
else if (first == folded[0])
score += -200 /* CaseFold */;
score += -200 /* Penalty.CaseFold */;
else
return false;
return null;
return this.ret(score, [0, firstSize]);
}
let direct = word.indexOf(this.pattern);
if (direct == 0)
return this.ret(word.length == this.pattern.length ? 0 : -100 /* NotFull */, [0, this.pattern.length]);
return this.ret(word.length == this.pattern.length ? 0 : -100 /* Penalty.NotFull */, [0, this.pattern.length]);
let len = chars.length, anyTo = 0;
if (direct < 0) {
for (let i = 0, e = Math.min(word.length, 200); i < e && anyTo < len;) {
@ -252,7 +252,7 @@ class FuzzyMatcher {
}
// No match, exit immediately
if (anyTo < len)
return false;
return null;
}
// This tracks the extent of the precise (non-folded, not
// necessarily adjacent) match
@ -265,7 +265,7 @@ class FuzzyMatcher {
let adjacentTo = 0, adjacentStart = -1, adjacentEnd = -1;
let hasLower = /[a-z]/.test(word), wordAdjacent = true;
// Go over the option's text, scanning for the various kinds of matches
for (let i = 0, e = Math.min(word.length, 200), prevType = 0 /* NonWord */; i < e && byWordTo < len;) {
for (let i = 0, e = Math.min(word.length, 200), prevType = 0 /* Tp.NonWord */; i < e && byWordTo < len;) {
let next = codePointAt(word, i);
if (direct < 0) {
if (preciseTo < len && next == chars[preciseTo])
@ -283,9 +283,9 @@ class FuzzyMatcher {
}
}
let ch, type = next < 0xff
? (next >= 48 && next <= 57 || next >= 97 && next <= 122 ? 2 /* Lower */ : next >= 65 && next <= 90 ? 1 /* Upper */ : 0 /* NonWord */)
: ((ch = fromCodePoint(next)) != ch.toLowerCase() ? 1 /* Upper */ : ch != ch.toUpperCase() ? 2 /* Lower */ : 0 /* NonWord */);
if (!i || type == 1 /* Upper */ && hasLower || prevType == 0 /* NonWord */ && type != 0 /* NonWord */) {
? (next >= 48 && next <= 57 || next >= 97 && next <= 122 ? 2 /* Tp.Lower */ : next >= 65 && next <= 90 ? 1 /* Tp.Upper */ : 0 /* Tp.NonWord */)
: ((ch = fromCodePoint(next)) != ch.toLowerCase() ? 1 /* Tp.Upper */ : ch != ch.toUpperCase() ? 2 /* Tp.Lower */ : 0 /* Tp.NonWord */);
if (!i || type == 1 /* Tp.Upper */ && hasLower || prevType == 0 /* Tp.NonWord */ && type != 0 /* Tp.NonWord */) {
if (chars[byWordTo] == next || (folded[byWordTo] == next && (byWordFolded = true)))
byWord[byWordTo++] = i;
else if (byWord.length)
@ -295,18 +295,18 @@ class FuzzyMatcher {
i += codePointSize(next);
}
if (byWordTo == len && byWord[0] == 0 && wordAdjacent)
return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0), byWord, word);
return this.result(-100 /* Penalty.ByWord */ + (byWordFolded ? -200 /* Penalty.CaseFold */ : 0), byWord, word);
if (adjacentTo == len && adjacentStart == 0)
return this.ret(-200 /* CaseFold */ - word.length + (adjacentEnd == word.length ? 0 : -100 /* NotFull */), [0, adjacentEnd]);
return this.ret(-200 /* Penalty.CaseFold */ - word.length + (adjacentEnd == word.length ? 0 : -100 /* Penalty.NotFull */), [0, adjacentEnd]);
if (direct > -1)
return this.ret(-700 /* NotStart */ - word.length, [direct, direct + this.pattern.length]);
return this.ret(-700 /* Penalty.NotStart */ - word.length, [direct, direct + this.pattern.length]);
if (adjacentTo == len)
return this.ret(-200 /* CaseFold */ + -700 /* NotStart */ - word.length, [adjacentStart, adjacentEnd]);
return this.ret(-200 /* Penalty.CaseFold */ + -700 /* Penalty.NotStart */ - word.length, [adjacentStart, adjacentEnd]);
if (byWordTo == len)
return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0) + -700 /* NotStart */ +
(wordAdjacent ? 0 : -1100 /* Gap */), byWord, word);
return chars.length == 2 ? false
: this.result((any[0] ? -700 /* NotStart */ : 0) + -200 /* CaseFold */ + -1100 /* Gap */, any, word);
return this.result(-100 /* Penalty.ByWord */ + (byWordFolded ? -200 /* Penalty.CaseFold */ : 0) + -700 /* Penalty.NotStart */ +
(wordAdjacent ? 0 : -1100 /* Penalty.Gap */), byWord, word);
return chars.length == 2 ? null
: this.result((any[0] ? -700 /* Penalty.NotStart */ : 0) + -200 /* Penalty.CaseFold */ + -1100 /* Penalty.Gap */, any, word);
}
result(score, positions, word) {
let result = [], i = 0;
@ -322,11 +322,31 @@ class FuzzyMatcher {
return this.ret(score - word.length, result);
}
}
class StrictMatcher {
constructor(pattern) {
this.pattern = pattern;
this.matched = [];
this.score = 0;
this.folded = pattern.toLowerCase();
}
match(word) {
if (word.length < this.pattern.length)
return null;
let start = word.slice(0, this.pattern.length);
let match = start == this.pattern ? 0 : start.toLowerCase() == this.folded ? -200 /* Penalty.CaseFold */ : null;
if (match == null)
return null;
this.matched = [0, start.length];
this.score = match + (word.length == this.pattern.length ? 0 : -100 /* Penalty.NotFull */);
return this;
}
}
const completionConfig = /*@__PURE__*/Facet.define({
combine(configs) {
return combineConfig(configs, {
activateOnTyping: true,
activateOnTypingDelay: 100,
selectOnOpen: true,
override: null,
closeOnBlur: true,
@ -338,22 +358,25 @@ const completionConfig = /*@__PURE__*/Facet.define({
icons: true,
addToOptions: [],
positionInfo: defaultPositionInfo,
filterStrict: false,
compareCompletions: (a, b) => a.label.localeCompare(b.label),
interactionDelay: 75
interactionDelay: 75,
updateSyncTime: 100
}, {
defaultKeymap: (a, b) => a && b,
closeOnBlur: (a, b) => a && b,
icons: (a, b) => a && b,
tooltipClass: (a, b) => c => joinClass(a(c), b(c)),
optionClass: (a, b) => c => joinClass(a(c), b(c)),
addToOptions: (a, b) => a.concat(b)
addToOptions: (a, b) => a.concat(b),
filterStrict: (a, b) => a || b,
});
}
});
function joinClass(a, b) {
return a ? b ? a + " " + b : a : b;
}
function defaultPositionInfo(view, list, option, info, space) {
function defaultPositionInfo(view, list, option, info, space, tooltip) {
let rtl = view.textDirection == Direction.RTL, left = rtl, narrow = false;
let side = "top", offset, maxWidth;
let spaceLeft = list.left - space.left, spaceRight = space.right - list.right;
@ -364,11 +387,11 @@ function defaultPositionInfo(view, list, option, info, space) {
left = true;
if (infoWidth <= (left ? spaceLeft : spaceRight)) {
offset = Math.max(space.top, Math.min(option.top, space.bottom - infoHeight)) - list.top;
maxWidth = Math.min(400 /* Width */, left ? spaceLeft : spaceRight);
maxWidth = Math.min(400 /* Info.Width */, left ? spaceLeft : spaceRight);
}
else {
narrow = true;
maxWidth = Math.min(400 /* Width */, (rtl ? list.right : space.right - list.left) - 30 /* Margin */);
maxWidth = Math.min(400 /* Info.Width */, (rtl ? list.right : space.right - list.left) - 30 /* Info.Margin */);
let spaceBelow = space.bottom - list.bottom;
if (spaceBelow >= infoHeight || spaceBelow > list.top) { // Below the completion
offset = option.bottom - list.top;
@ -378,8 +401,10 @@ function defaultPositionInfo(view, list, option, info, space) {
offset = list.bottom - option.top;
}
}
let scaleY = (list.bottom - list.top) / tooltip.offsetHeight;
let scaleX = (list.right - list.left) / tooltip.offsetWidth;
return {
style: `${side}: ${offset}px; max-width: ${maxWidth}px`,
style: `${side}: ${offset / scaleY}px; max-width: ${maxWidth / scaleX}px`,
class: "cm-completionInfo-" + (narrow ? (rtl ? "left-narrow" : "right-narrow") : left ? "left" : "right")
};
}
@ -399,7 +424,7 @@ function optionContent(config) {
position: 20
});
content.push({
render(completion, _s, match) {
render(completion, _s, _v, match) {
let labelElt = document.createElement("span");
labelElt.className = "cm-completionLabel";
let label = completion.displayLabel || completion.label, off = 0;
@ -467,6 +492,7 @@ class CompletionTooltip {
this.dom.className = "cm-tooltip-autocomplete";
this.updateTooltipClass(view.state);
this.dom.addEventListener("mousedown", (e) => {
let { options } = view.state.field(stateField).open;
for (let dom = e.target, match; dom && dom != this.dom; dom = dom.parentNode) {
if (dom.nodeName == "LI" && (match = /-(\d+)$/.exec(dom.id)) && +match[1] < options.length) {
this.applyCompletion(view, options[+match[1]]);
@ -481,22 +507,32 @@ class CompletionTooltip {
e.relatedTarget != view.contentDOM)
view.dispatch({ effects: closeCompletionEffect.of(null) });
});
this.list = this.dom.appendChild(this.createListBox(options, cState.id, this.range));
this.showOptions(options, cState.id);
}
mount() { this.updateSel(); }
showOptions(options, id) {
if (this.list)
this.list.remove();
this.list = this.dom.appendChild(this.createListBox(options, id, this.range));
this.list.addEventListener("scroll", () => {
if (this.info)
this.view.requestMeasure(this.placeInfoReq);
});
}
mount() { this.updateSel(); }
update(update) {
var _a, _b, _c;
var _a;
let cState = update.state.field(this.stateField);
let prevState = update.startState.field(this.stateField);
this.updateTooltipClass(update.state);
if (cState != prevState) {
let { options, selected, disabled } = cState.open;
if (!prevState.open || prevState.open.options != options) {
this.range = rangeAroundSelected(options.length, selected, update.state.facet(completionConfig).maxRenderedOptions);
this.showOptions(options, cState.id);
}
this.updateSel();
if (((_a = cState.open) === null || _a === void 0 ? void 0 : _a.disabled) != ((_b = prevState.open) === null || _b === void 0 ? void 0 : _b.disabled))
this.dom.classList.toggle("cm-tooltip-autocomplete-disabled", !!((_c = cState.open) === null || _c === void 0 ? void 0 : _c.disabled));
if (disabled != ((_a = prevState.open) === null || _a === void 0 ? void 0 : _a.disabled))
this.dom.classList.toggle("cm-tooltip-autocomplete-disabled", !!disabled);
}
}
updateTooltipClass(state) {
@ -520,12 +556,7 @@ class CompletionTooltip {
let cState = this.view.state.field(this.stateField), open = cState.open;
if (open.selected > -1 && open.selected < this.range.from || open.selected >= this.range.to) {
this.range = rangeAroundSelected(open.options.length, open.selected, this.view.state.facet(completionConfig).maxRenderedOptions);
this.list.remove();
this.list = this.dom.appendChild(this.createListBox(open.options, cState.id, this.range));
this.list.addEventListener("scroll", () => {
if (this.info)
this.view.requestMeasure(this.placeInfoReq);
});
this.showOptions(open.options, cState.id);
}
if (this.updateSelectedOption(open.selected)) {
this.destroyInfo();
@ -599,7 +630,7 @@ class CompletionTooltip {
if (selRect.top > Math.min(space.bottom, listRect.bottom) - 10 ||
selRect.bottom < Math.max(space.top, listRect.top) + 10)
return null;
return this.view.state.facet(completionConfig).positionInfo(this.view, listRect, selRect, infoRect, space);
return this.view.state.facet(completionConfig).positionInfo(this.view, listRect, selRect, infoRect, space, this.dom);
}
placeInfo(pos) {
if (this.info) {
@ -642,7 +673,7 @@ class CompletionTooltip {
if (cls)
li.className = cls;
for (let source of this.optionContent) {
let node = source(completion, this.view.state, match);
let node = source(completion, this.view.state, this.view, match);
if (node)
li.appendChild(node);
}
@ -665,18 +696,17 @@ class CompletionTooltip {
this.destroyInfo();
}
}
// We allocate a new function instance every time the completion
// changes to force redrawing/repositioning of the tooltip
function completionTooltip(stateField, applyCompletion) {
return (view) => new CompletionTooltip(view, stateField, applyCompletion);
}
function scrollIntoView(container, element) {
let parent = container.getBoundingClientRect();
let self = element.getBoundingClientRect();
let scaleY = parent.height / container.offsetHeight;
if (self.top < parent.top)
container.scrollTop -= parent.top - self.top;
container.scrollTop -= (parent.top - self.top) / scaleY;
else if (self.bottom > parent.bottom)
container.scrollTop += self.bottom - parent.bottom;
container.scrollTop += (self.bottom - parent.bottom) / scaleY;
}
// Used to pick a preferred option when two options with the same
@ -699,6 +729,7 @@ function sortOptions(active, state) {
sections.push(typeof section == "string" ? { name } : section);
}
};
let conf = state.facet(completionConfig);
for (let a of active)
if (a.hasResult()) {
let getMatch = a.result.getMatch;
@ -708,11 +739,12 @@ function sortOptions(active, state) {
}
}
else {
let matcher = new FuzzyMatcher(state.sliceDoc(a.from, a.to));
let pattern = state.sliceDoc(a.from, a.to), match;
let matcher = conf.filterStrict ? new StrictMatcher(pattern) : new FuzzyMatcher(pattern);
for (let option of a.result.options)
if (matcher.match(option.label)) {
let matched = !option.displayLabel ? matcher.matched : getMatch ? getMatch(option, matcher.matched) : [];
addOption(new Option(option, a.source, matched, matcher.score + (option.boost || 0)));
if (match = matcher.match(option.label)) {
let matched = !option.displayLabel ? match.matched : getMatch ? getMatch(option, match.matched) : [];
addOption(new Option(option, a.source, matched, match.score + (option.boost || 0)));
}
}
}
@ -730,7 +762,7 @@ function sortOptions(active, state) {
}
}
let result = [], prev = null;
let compare = state.facet(completionConfig).compareCompletions;
let compare = conf.compareCompletions;
for (let opt of options.sort((a, b) => (b.score - a.score) || compare(a.completion, b.completion))) {
let cur = opt.completion;
if (!prev || prev.label != cur.label || prev.detail != cur.detail ||
@ -759,7 +791,7 @@ class CompletionDialog {
static build(active, state, id, prev, conf) {
let options = sortOptions(active, state);
if (!options.length) {
return prev && active.some(a => a.state == 1 /* Pending */) ?
return prev && active.some(a => a.state == 1 /* State.Pending */) ?
new CompletionDialog(prev.options, prev.attrs, prev.tooltip, prev.timestamp, prev.selected, true) : null;
}
let selected = state.facet(completionConfig).selectOnOpen ? 0 : -1;
@ -773,7 +805,7 @@ class CompletionDialog {
}
return new CompletionDialog(options, makeAttrs(id, selected), {
pos: active.reduce((a, b) => b.hasResult() ? Math.min(a, b.from) : a, 1e8),
create: completionTooltip(completionState, applyCompletion),
create: createTooltip,
above: conf.aboveCursor,
}, prev ? prev.timestamp : Date.now(), selected, false);
}
@ -796,7 +828,7 @@ class CompletionState {
state.languageDataAt("autocomplete", cur(state)).map(asSource);
let active = sources.map(source => {
let value = this.active.find(s => s.source == source) ||
new ActiveSource(source, this.active.some(a => a.state != 0 /* Inactive */) ? 1 /* Pending */ : 0 /* Inactive */);
new ActiveSource(source, this.active.some(a => a.state != 0 /* State.Inactive */) ? 1 /* State.Pending */ : 0 /* State.Inactive */);
return value.update(tr, conf);
});
if (active.length == this.active.length && active.every((a, i) => a == this.active[i]))
@ -807,10 +839,10 @@ class CompletionState {
if (tr.selection || active.some(a => a.hasResult() && tr.changes.touchesRange(a.from, a.to)) ||
!sameResults(active, this.active))
open = CompletionDialog.build(active, state, this.id, open, conf);
else if (open && open.disabled && !active.some(a => a.state == 1 /* Pending */))
else if (open && open.disabled && !active.some(a => a.state == 1 /* State.Pending */))
open = null;
if (!open && active.every(a => a.state != 1 /* Pending */) && active.some(a => a.hasResult()))
active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* Inactive */) : a);
if (!open && active.every(a => a.state != 1 /* State.Pending */) && active.some(a => a.hasResult()))
active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* State.Inactive */) : a);
for (let effect of tr.effects)
if (effect.is(setSelectedEffect))
open = open && open.setSelected(effect.value, this.id);
@ -864,13 +896,13 @@ class ActiveSource {
value = value.handleUserEvent(tr, event, conf);
else if (tr.docChanged)
value = value.handleChange(tr);
else if (tr.selection && value.state != 0 /* Inactive */)
value = new ActiveSource(value.source, 0 /* Inactive */);
else if (tr.selection && value.state != 0 /* State.Inactive */)
value = new ActiveSource(value.source, 0 /* State.Inactive */);
for (let effect of tr.effects) {
if (effect.is(startCompletionEffect))
value = new ActiveSource(value.source, 1 /* Pending */, effect.value ? cur(tr.state) : -1);
value = new ActiveSource(value.source, 1 /* State.Pending */, effect.value ? cur(tr.state) : -1);
else if (effect.is(closeCompletionEffect))
value = new ActiveSource(value.source, 0 /* Inactive */);
value = new ActiveSource(value.source, 0 /* State.Inactive */);
else if (effect.is(setActiveEffect))
for (let active of effect.value)
if (active.source == value.source)
@ -879,10 +911,10 @@ class ActiveSource {
return value;
}
handleUserEvent(tr, type, conf) {
return type == "delete" || !conf.activateOnTyping ? this.map(tr.changes) : new ActiveSource(this.source, 1 /* Pending */);
return type == "delete" || !conf.activateOnTyping ? this.map(tr.changes) : new ActiveSource(this.source, 1 /* State.Pending */);
}
handleChange(tr) {
return tr.changes.touchesRange(cur(tr.startState)) ? new ActiveSource(this.source, 0 /* Inactive */) : this.map(tr.changes);
return tr.changes.touchesRange(cur(tr.startState)) ? new ActiveSource(this.source, 0 /* State.Inactive */) : this.map(tr.changes);
}
map(changes) {
return changes.empty || this.explicitPos < 0 ? this : new ActiveSource(this.source, this.state, changes.mapPos(this.explicitPos));
@ -890,7 +922,7 @@ class ActiveSource {
}
class ActiveResult extends ActiveSource {
constructor(source, explicitPos, result, from, to) {
super(source, 2 /* Result */, explicitPos);
super(source, 2 /* State.Result */, explicitPos);
this.result = result;
this.from = from;
this.to = to;
@ -898,26 +930,33 @@ class ActiveResult extends ActiveSource {
hasResult() { return true; }
handleUserEvent(tr, type, conf) {
var _a;
let result = this.result;
if (result.map && !tr.changes.empty)
result = result.map(result, tr.changes);
let from = tr.changes.mapPos(this.from), to = tr.changes.mapPos(this.to, 1);
let pos = cur(tr.state);
if ((this.explicitPos < 0 ? pos <= from : pos < this.from) ||
pos > to ||
pos > to || !result ||
type == "delete" && cur(tr.startState) == this.from)
return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* Pending */ : 0 /* Inactive */);
let explicitPos = this.explicitPos < 0 ? -1 : tr.changes.mapPos(this.explicitPos), updated;
if (checkValid(this.result.validFor, tr.state, from, to))
return new ActiveResult(this.source, explicitPos, this.result, from, to);
if (this.result.update &&
(updated = this.result.update(this.result, from, to, new CompletionContext(tr.state, pos, explicitPos >= 0))))
return new ActiveResult(this.source, explicitPos, updated, updated.from, (_a = updated.to) !== null && _a !== void 0 ? _a : cur(tr.state));
return new ActiveSource(this.source, 1 /* Pending */, explicitPos);
return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* State.Pending */ : 0 /* State.Inactive */);
let explicitPos = this.explicitPos < 0 ? -1 : tr.changes.mapPos(this.explicitPos);
if (checkValid(result.validFor, tr.state, from, to))
return new ActiveResult(this.source, explicitPos, result, from, to);
if (result.update &&
(result = result.update(result, from, to, new CompletionContext(tr.state, pos, explicitPos >= 0))))
return new ActiveResult(this.source, explicitPos, result, result.from, (_a = result.to) !== null && _a !== void 0 ? _a : cur(tr.state));
return new ActiveSource(this.source, 1 /* State.Pending */, explicitPos);
}
handleChange(tr) {
return tr.changes.touchesRange(this.from, this.to) ? new ActiveSource(this.source, 0 /* Inactive */) : this.map(tr.changes);
return tr.changes.touchesRange(this.from, this.to) ? new ActiveSource(this.source, 0 /* State.Inactive */) : this.map(tr.changes);
}
map(mapping) {
return mapping.empty ? this :
new ActiveResult(this.source, this.explicitPos < 0 ? -1 : mapping.mapPos(this.explicitPos), this.result, mapping.mapPos(this.from), mapping.mapPos(this.to, 1));
if (mapping.empty)
return this;
let result = this.result.map ? this.result.map(this.result, mapping) : this.result;
if (!result)
return new ActiveSource(this.source, 0 /* State.Inactive */);
return new ActiveResult(this.source, this.explicitPos < 0 ? -1 : mapping.mapPos(this.explicitPos), this.result, mapping.mapPos(this.from), mapping.mapPos(this.to, 1));
}
}
function checkValid(validFor, state, from, to) {
@ -949,6 +988,7 @@ function applyCompletion(view, option) {
apply(view, option.completion, result.from, result.to);
return true;
}
const createTooltip = /*@__PURE__*/completionTooltip(completionState, applyCompletion);
/**
Returns a command that moves the completion selection forward or
@ -999,7 +1039,7 @@ Close the currently active completion.
*/
const closeCompletion = (view) => {
let cState = view.state.field(completionState, false);
if (!cState || !cState.active.some(a => a.state != 0 /* Inactive */))
if (!cState || !cState.active.some(a => a.state != 0 /* State.Inactive */))
return false;
view.dispatch({ effects: closeCompletionEffect.of(null) });
return true;
@ -1015,16 +1055,17 @@ class RunningQuery {
this.done = undefined;
}
}
const DebounceTime = 50, MaxUpdateCount = 50, MinAbortTime = 1000;
const MaxUpdateCount = 50, MinAbortTime = 1000;
const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
constructor(view) {
this.view = view;
this.debounceUpdate = -1;
this.running = [];
this.debounceAccept = -1;
this.composing = 0 /* None */;
this.pendingStart = false;
this.composing = 0 /* CompositionState.None */;
for (let active of view.state.field(completionState).active)
if (active.state == 1 /* Pending */)
if (active.state == 1 /* State.Pending */)
this.startQuery(active);
}
update(update) {
@ -1055,21 +1096,25 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
}
if (this.debounceUpdate > -1)
clearTimeout(this.debounceUpdate);
this.debounceUpdate = cState.active.some(a => a.state == 1 /* Pending */ && !this.running.some(q => q.active.source == a.source))
? setTimeout(() => this.startUpdate(), DebounceTime) : -1;
if (this.composing != 0 /* None */)
if (update.transactions.some(tr => tr.effects.some(e => e.is(startCompletionEffect))))
this.pendingStart = true;
let delay = this.pendingStart ? 50 : update.state.facet(completionConfig).activateOnTypingDelay;
this.debounceUpdate = cState.active.some(a => a.state == 1 /* State.Pending */ && !this.running.some(q => q.active.source == a.source))
? setTimeout(() => this.startUpdate(), delay) : -1;
if (this.composing != 0 /* CompositionState.None */)
for (let tr of update.transactions) {
if (getUserEvent(tr) == "input")
this.composing = 2 /* Changed */;
else if (this.composing == 2 /* Changed */ && tr.selection)
this.composing = 3 /* ChangedAndMoved */;
this.composing = 2 /* CompositionState.Changed */;
else if (this.composing == 2 /* CompositionState.Changed */ && tr.selection)
this.composing = 3 /* CompositionState.ChangedAndMoved */;
}
}
startUpdate() {
this.debounceUpdate = -1;
this.pendingStart = false;
let { state } = this.view, cState = state.field(completionState);
for (let active of cState.active) {
if (active.state == 1 /* Pending */ && !this.running.some(r => r.active.source == active.source))
if (active.state == 1 /* State.Pending */ && !this.running.some(r => r.active.source == active.source))
this.startQuery(active);
}
}
@ -1092,7 +1137,7 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
if (this.running.every(q => q.done !== undefined))
this.accept();
else if (this.debounceAccept < 0)
this.debounceAccept = setTimeout(() => this.accept(), DebounceTime);
this.debounceAccept = setTimeout(() => this.accept(), this.view.state.facet(completionConfig).updateSyncTime);
}
// For each finished query in this.running, try to create a result
// or, if appropriate, restart the query.
@ -1120,14 +1165,14 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
}
}
let current = this.view.state.field(completionState).active.find(a => a.source == query.active.source);
if (current && current.state == 1 /* Pending */) {
if (current && current.state == 1 /* State.Pending */) {
if (query.done == null) {
// Explicitly failed. Should clear the pending status if it
// hasn't been re-set in the meantime.
let active = new ActiveSource(query.active.source, 0 /* Inactive */);
let active = new ActiveSource(query.active.source, 0 /* State.Inactive */);
for (let tr of query.updates)
active = active.update(tr, conf);
if (active.state != 1 /* Pending */)
if (active.state != 1 /* State.Pending */)
updated.push(active);
}
else {
@ -1146,22 +1191,37 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
if (state && state.tooltip && this.view.state.facet(completionConfig).closeOnBlur) {
let dialog = state.open && getTooltip(this.view, state.open.tooltip);
if (!dialog || !dialog.dom.contains(event.relatedTarget))
this.view.dispatch({ effects: closeCompletionEffect.of(null) });
setTimeout(() => this.view.dispatch({ effects: closeCompletionEffect.of(null) }), 10);
}
},
compositionstart() {
this.composing = 1 /* Started */;
this.composing = 1 /* CompositionState.Started */;
},
compositionend() {
if (this.composing == 3 /* ChangedAndMoved */) {
if (this.composing == 3 /* CompositionState.ChangedAndMoved */) {
// Safari fires compositionend events synchronously, possibly
// from inside an update, so dispatch asynchronously to avoid reentrancy
setTimeout(() => this.view.dispatch({ effects: startCompletionEffect.of(false) }), 20);
}
this.composing = 0 /* None */;
this.composing = 0 /* CompositionState.None */;
}
}
});
const windows = typeof navigator == "object" && /*@__PURE__*//Win/.test(navigator.platform);
const commitCharacters = /*@__PURE__*/Prec.highest(/*@__PURE__*/EditorView.domEventHandlers({
keydown(event, view) {
let field = view.state.field(completionState, false);
if (!field || !field.open || field.open.disabled || field.open.selected < 0 ||
event.key.length > 1 || event.ctrlKey && !(windows && event.altKey) || event.metaKey)
return false;
let option = field.open.options[field.open.selected];
let result = field.active.find(a => a.source == option.source);
let commitChars = option.completion.commitCharacters || result.result.commitCharacters;
if (commitChars && commitChars.indexOf(event.key) > -1)
applyCompletion(view, option);
return false;
}
}));
const baseTheme = /*@__PURE__*/EditorView.baseTheme({
".cm-tooltip.cm-tooltip-autocomplete": {
@ -1218,13 +1278,13 @@ const baseTheme = /*@__PURE__*/EditorView.baseTheme({
position: "absolute",
padding: "3px 9px",
width: "max-content",
maxWidth: `${400 /* Width */}px`,
maxWidth: `${400 /* Info.Width */}px`,
boxSizing: "border-box"
},
".cm-completionInfo.cm-completionInfo-left": { right: "100%" },
".cm-completionInfo.cm-completionInfo-right": { left: "100%" },
".cm-completionInfo.cm-completionInfo-left-narrow": { right: `${30 /* Margin */}px` },
".cm-completionInfo.cm-completionInfo-right-narrow": { left: `${30 /* Margin */}px` },
".cm-completionInfo.cm-completionInfo-left-narrow": { right: `${30 /* Info.Margin */}px` },
".cm-completionInfo.cm-completionInfo-right-narrow": { left: `${30 /* Info.Margin */}px` },
"&light .cm-snippetField": { backgroundColor: "#00000022" },
"&dark .cm-snippetField": { backgroundColor: "#ffffff22" },
".cm-snippetFieldPosition": {
@ -1454,11 +1514,11 @@ function snippet(template) {
let spec = {
changes: { from, to, insert: Text.of(text) },
scrollIntoView: true,
annotations: completion ? pickedCompletion.of(completion) : undefined
annotations: completion ? [pickedCompletion.of(completion), Transaction.userEvent.of("input.complete")] : undefined
};
if (ranges.length)
spec.selection = fieldSelection(ranges, 0);
if (ranges.length > 1) {
if (ranges.some(r => r.field > 0)) {
let active = new ActiveSnippet(ranges, 0);
let effects = spec.effects = [setActive.of(active)];
if (editor.state.field(snippetState, false) === undefined)
@ -1475,7 +1535,8 @@ function moveField(dir) {
let next = active.active + dir, last = dir > 0 && !active.ranges.some(r => r.field == next + dir);
dispatch(state.update({
selection: fieldSelection(active.ranges, next),
effects: setActive.of(last ? null : new ActiveSnippet(active.ranges, next))
effects: setActive.of(last ? null : new ActiveSnippet(active.ranges, next)),
scrollIntoView: true
}));
return true;
};
@ -1547,14 +1608,16 @@ const snippetPointerHandler = /*@__PURE__*/EditorView.domEventHandlers({
return false;
view.dispatch({
selection: fieldSelection(active.ranges, match.field),
effects: setActive.of(active.ranges.some(r => r.field > match.field) ? new ActiveSnippet(active.ranges, match.field) : null)
effects: setActive.of(active.ranges.some(r => r.field > match.field)
? new ActiveSnippet(active.ranges, match.field) : null),
scrollIntoView: true
});
return true;
}
});
function wordRE(wordChars) {
let escaped = wordChars.replace(/[\\[.+*?(){|^$]/g, "\\$&");
let escaped = wordChars.replace(/[\]\-\\]/g, "\\$&");
try {
return new RegExp(`[\\p{Alphabetic}\\p{Number}_${escaped}]+`, "ug");
}
@ -1577,7 +1640,7 @@ function storeWords(doc, wordRE, result, seen, ignoreAt) {
if (!seen[m[0]] && pos + m.index != ignoreAt) {
result.push({ type: "text", label: m[0] });
seen[m[0]] = true;
if (result.length >= 2000 /* MaxList */)
if (result.length >= 2000 /* C.MaxList */)
return;
}
}
@ -1585,7 +1648,7 @@ function storeWords(doc, wordRE, result, seen, ignoreAt) {
}
}
function collectWords(doc, cache, wordRE, to, ignoreAt) {
let big = doc.length >= 1000 /* MinCacheLen */;
let big = doc.length >= 1000 /* C.MinCacheLen */;
let cached = big && cache.get(doc);
if (cached)
return cached;
@ -1593,7 +1656,7 @@ function collectWords(doc, cache, wordRE, to, ignoreAt) {
if (doc.children) {
let pos = 0;
for (let ch of doc.children) {
if (ch.length >= 1000 /* MinCacheLen */) {
if (ch.length >= 1000 /* C.MinCacheLen */) {
for (let c of collectWords(ch, cache, wordRE, to - pos, ignoreAt - pos)) {
if (!seen[c.label]) {
seen[c.label] = true;
@ -1610,7 +1673,7 @@ function collectWords(doc, cache, wordRE, to, ignoreAt) {
else {
storeWords(doc, wordRE, result, seen, ignoreAt);
}
if (big && result.length < 2000 /* MaxList */)
if (big && result.length < 2000 /* C.MaxList */)
cache.set(doc, result);
return result;
}
@ -1626,7 +1689,7 @@ const completeAnyWord = context => {
if (!token && !context.explicit)
return null;
let from = token ? token.from : context.pos;
let options = collectWords(context.state.doc, wordCache(wordChars), re, 50000 /* Range */, from);
let options = collectWords(context.state.doc, wordCache(wordChars), re, 50000 /* C.Range */, from);
return { from, options, validFor: mapRE(re, s => "^" + s) };
};
@ -1648,13 +1711,11 @@ closedBracket.endSide = -1;
const bracketState = /*@__PURE__*/StateField.define({
create() { return RangeSet.empty; },
update(value, tr) {
if (tr.selection) {
let lineStart = tr.state.doc.lineAt(tr.selection.main.head).from;
let prevLineStart = tr.startState.doc.lineAt(tr.startState.selection.main.head).from;
if (lineStart != tr.changes.mapPos(prevLineStart, -1))
value = RangeSet.empty;
}
value = value.map(tr.changes);
if (tr.selection) {
let line = tr.state.doc.lineAt(tr.selection.main.head);
value = value.update({ filter: from => from >= line.from && from <= line.to });
}
for (let effect of tr.effects)
if (effect.is(closeBracketEffect))
value = value.update({ add: [closedBracket.range(effect.value, effect.value + 1)] });
@ -1882,6 +1943,7 @@ Returns an extension that enables autocompletion.
*/
function autocompletion(config = {}) {
return [
commitCharacters,
completionState,
completionConfig.of(config),
completionPlugin,
@ -1918,8 +1980,8 @@ returns `null`.
*/
function completionStatus(state) {
let cState = state.field(completionState, false);
return cState && cState.active.some(a => a.state == 1 /* Pending */) ? "pending"
: cState && cState.active.some(a => a.state != 0 /* Inactive */) ? "active" : null;
return cState && cState.active.some(a => a.state == 1 /* State.Pending */) ? "pending"
: cState && cState.active.some(a => a.state != 0 /* State.Inactive */) ? "active" : null;
}
const completionArrayCache = /*@__PURE__*/new WeakMap;
/**

View file

@ -1,6 +1,6 @@
{
"name": "@codemirror/autocomplete",
"version": "6.9.0",
"version": "6.15.0",
"description": "Autocompletion for the CodeMirror code editor",
"scripts": {
"test": "cm-runtests",
@ -28,7 +28,7 @@
"dependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.6.0",
"@codemirror/view": "^6.17.0",
"@lezer/common": "^1.0.0"
},
"peerDependencies": {

View file

@ -1,3 +1,45 @@
## 6.3.3 (2023-12-28)
### Bug fixes
Fix an issue causing cursor motion commands to not dispatch a transaction when the change only affects cursor associativity.
## 6.3.2 (2023-11-28)
### Bug fixes
Fix a regression that caused `deleteCharBackward` to sometimes delete a large chunk of text.
## 6.3.1 (2023-11-27)
### Bug fixes
When undoing, store the selection after the undone change with the redo event, so that redoing restores it.
`deleteCharBackward` will no longer delete variant selector characters as separate characters.
## 6.3.0 (2023-09-29)
### Bug fixes
Make it possible for `selectParentSyntax` to jump out of or into a syntax tree overlay.
Make Cmd-Backspace and Cmd-Delete on macOS delete to the next line wrap point, not the start/end of the line.
### New features
The new `deleteLineBoundaryForward` and `deleteLineBoundaryBackward` commands delete to the start/end of the line or the next line wrapping point.
## 6.2.5 (2023-08-26)
### Bug fixes
Make `insertNewlineAndIndent` properly count indentation for tabs when copying over the previous line's indentation.
The various sub-word motion commands will now use `Intl.Segmenter`, when available, to stop at CJK language word boundaries.
Fix a bug in `insertNewlineAndIndent` that would delete text between brackets if it had no corresponding AST node.
## 6.2.4 (2023-05-03)
### Bug fixes

View file

@ -1,7 +1,5 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var state = require('@codemirror/state');
var view = require('@codemirror/view');
var language = require('@codemirror/language');
@ -218,11 +216,6 @@ const historyConfig = state.Facet.define({
});
}
});
function changeEnd(changes) {
let end = 0;
changes.iterChangedRanges((_, to) => end = to);
return end;
}
const historyField_ = state.StateField.define({
create() {
return HistoryState.empty;
@ -231,8 +224,7 @@ const historyField_ = state.StateField.define({
let config = tr.state.facet(historyConfig);
let fromHist = tr.annotation(fromHistory);
if (fromHist) {
let selection = tr.docChanged ? state.EditorSelection.single(changeEnd(tr.changes)) : undefined;
let item = HistEvent.fromTransaction(tr, selection), from = fromHist.side;
let item = HistEvent.fromTransaction(tr, fromHist.selection), from = fromHist.side;
let other = from == 0 /* BranchName.Done */ ? state$1.undone : state$1.done;
if (item)
other = updateBranch(other, other.length, config.minDepth, item);
@ -513,15 +505,15 @@ class HistoryState {
addMapping(mapping) {
return new HistoryState(addMappingToBranch(this.done, mapping), addMappingToBranch(this.undone, mapping), this.prevTime, this.prevUserEvent);
}
pop(side, state, selection) {
pop(side, state, onlySelection) {
let branch = side == 0 /* BranchName.Done */ ? this.done : this.undone;
if (branch.length == 0)
return null;
let event = branch[branch.length - 1];
if (selection && event.selectionsAfter.length) {
let event = branch[branch.length - 1], selection = event.selectionsAfter[0] || state.selection;
if (onlySelection && event.selectionsAfter.length) {
return state.update({
selection: event.selectionsAfter[event.selectionsAfter.length - 1],
annotations: fromHistory.of({ side, rest: popSelection(branch) }),
annotations: fromHistory.of({ side, rest: popSelection(branch), selection }),
userEvent: side == 0 /* BranchName.Done */ ? "select.undo" : "select.redo",
scrollIntoView: true
});
@ -537,7 +529,7 @@ class HistoryState {
changes: event.changes,
selection: event.startSelection,
effects: event.effects,
annotations: fromHistory.of({ side, rest }),
annotations: fromHistory.of({ side, rest, selection }),
filter: false,
userEvent: side == 0 /* BranchName.Done */ ? "undo" : "redo",
scrollIntoView: true
@ -570,7 +562,7 @@ function setSel(state, selection) {
}
function moveSel({ state, dispatch }, how) {
let selection = updateSel(state.selection, how);
if (selection.eq(state.selection))
if (selection.eq(state.selection, true))
return false;
dispatch(setSel(state, selection));
return true;
@ -621,45 +613,61 @@ const cursorGroupForward = view => cursorByGroup(view, true);
Move the selection one group backward.
*/
const cursorGroupBackward = view => cursorByGroup(view, false);
const segmenter = typeof Intl != "undefined" && Intl.Segmenter ?
new (Intl.Segmenter)(undefined, { granularity: "word" }) : null;
function moveBySubword(view, range, forward) {
let categorize = view.state.charCategorizer(range.from);
return view.moveByChar(range, forward, start => {
let cat = state.CharCategory.Space, pos = range.from;
let done = false, sawUpper = false, sawLower = false;
let step = (next) => {
if (done)
return false;
pos += forward ? next.length : -next.length;
let nextCat = categorize(next), ahead;
if (nextCat == state.CharCategory.Word && next.charCodeAt(0) < 128 && /[\W_]/.test(next))
nextCat = -1; // Treat word punctuation specially
if (cat == state.CharCategory.Space)
cat = nextCat;
if (cat != nextCat)
return false;
if (cat == state.CharCategory.Word) {
if (next.toLowerCase() == next) {
if (!forward && sawUpper)
return false;
sawLower = true;
}
else if (sawLower) {
if (forward)
return false;
done = true;
}
else {
if (sawUpper && forward && categorize(ahead = view.state.sliceDoc(pos, pos + 1)) == state.CharCategory.Word &&
ahead.toLowerCase() == ahead)
return false;
sawUpper = true;
}
let cat = state.CharCategory.Space, pos = range.from, steps = 0;
let done = false, sawUpper = false, sawLower = false;
let step = (next) => {
if (done)
return false;
pos += forward ? next.length : -next.length;
let nextCat = categorize(next), ahead;
if (nextCat == state.CharCategory.Word && next.charCodeAt(0) < 128 && /[\W_]/.test(next))
nextCat = -1; // Treat word punctuation specially
if (cat == state.CharCategory.Space)
cat = nextCat;
if (cat != nextCat)
return false;
if (cat == state.CharCategory.Word) {
if (next.toLowerCase() == next) {
if (!forward && sawUpper)
return false;
sawLower = true;
}
return true;
};
else if (sawLower) {
if (forward)
return false;
done = true;
}
else {
if (sawUpper && forward && categorize(ahead = view.state.sliceDoc(pos, pos + 1)) == state.CharCategory.Word &&
ahead.toLowerCase() == ahead)
return false;
sawUpper = true;
}
}
steps++;
return true;
};
let end = view.moveByChar(range, forward, start => {
step(start);
return step;
});
if (segmenter && cat == state.CharCategory.Word && end.from == range.from + steps * (forward ? 1 : -1)) {
let from = Math.min(range.head, end.head), to = Math.max(range.head, end.head);
let skipped = view.state.sliceDoc(from, to);
if (skipped.length > 1 && /[\u4E00-\uffff]/.test(skipped)) {
let segments = Array.from(segmenter.segment(skipped));
if (segments.length > 1) {
if (forward)
return state.EditorSelection.cursor(range.head + segments[1].index, -1);
return state.EditorSelection.cursor(end.head + segments[segments.length - 1].index, 1);
}
}
}
return end;
}
function cursorBySubword(view, forward) {
return moveSel(view, range => range.empty ? moveBySubword(view, range, forward) : rangeEnd(range, forward));
@ -1002,12 +1010,15 @@ syntax tree.
const selectParentSyntax = ({ state: state$1, dispatch }) => {
let selection = updateSel(state$1.selection, range => {
var _a;
let context = language.syntaxTree(state$1).resolveInner(range.head, 1);
while (!((context.from < range.from && context.to >= range.to) ||
(context.to > range.to && context.from <= range.from) ||
!((_a = context.parent) === null || _a === void 0 ? void 0 : _a.parent)))
context = context.parent;
return state.EditorSelection.range(context.to, context.from);
let stack = language.syntaxTree(state$1).resolveStack(range.from, 1);
for (let cur = stack; cur; cur = cur.next) {
let { node } = cur;
if (((node.from < range.from && node.to >= range.to) ||
(node.to > range.to && node.from <= range.from)) &&
((_a = node.parent) === null || _a === void 0 ? void 0 : _a.parent))
return state.EditorSelection.range(node.to, node.from);
}
return range;
});
dispatch(setSel(state$1, selection));
return true;
@ -1035,7 +1046,7 @@ function deleteBy(target, by) {
let changes = state$1.changeByRange(range => {
let { from, to } = range;
if (from == to) {
let towards = by(from);
let towards = by(range);
if (towards < from) {
event = "delete.backward";
towards = skipAtomic(target, towards, false);
@ -1051,7 +1062,7 @@ function deleteBy(target, by) {
from = skipAtomic(target, from, false);
to = skipAtomic(target, to, true);
}
return from == to ? { range } : { changes: { from, to }, range: state.EditorSelection.cursor(from) };
return from == to ? { range } : { changes: { from, to }, range: state.EditorSelection.cursor(from, from < range.head ? -1 : 1) };
});
if (changes.changes.empty)
return false;
@ -1071,8 +1082,8 @@ function skipAtomic(target, pos, forward) {
});
return pos;
}
const deleteByChar = (target, forward) => deleteBy(target, pos => {
let { state: state$1 } = target, line = state$1.doc.lineAt(pos), before, targetPos;
const deleteByChar = (target, forward) => deleteBy(target, range => {
let pos = range.from, { state: state$1 } = target, line = state$1.doc.lineAt(pos), before, targetPos;
if (!forward && pos > line.from && pos < line.from + 200 &&
!/[^ \t]/.test(before = line.text.slice(0, pos - line.from))) {
if (before[before.length - 1] == "\t")
@ -1086,6 +1097,8 @@ const deleteByChar = (target, forward) => deleteBy(target, pos => {
targetPos = state.findClusterBreak(line.text, pos - line.from, forward, forward) + line.from;
if (targetPos == pos && line.number != (forward ? state$1.doc.lines : 1))
targetPos += forward ? 1 : -1;
else if (!forward && /[\ufe00-\ufe0f]/.test(line.text.slice(targetPos - line.from, pos - line.from)))
targetPos = state.findClusterBreak(line.text, targetPos - line.from, false, false) + line.from;
}
return targetPos;
});
@ -1098,12 +1111,12 @@ const deleteCharBackward = view => deleteByChar(view, false);
Delete the selection or the character after the cursor.
*/
const deleteCharForward = view => deleteByChar(view, true);
const deleteByGroup = (target, forward) => deleteBy(target, start => {
let pos = start, { state: state$1 } = target, line = state$1.doc.lineAt(pos);
const deleteByGroup = (target, forward) => deleteBy(target, range => {
let pos = range.head, { state: state$1 } = target, line = state$1.doc.lineAt(pos);
let categorize = state$1.charCategorizer(pos);
for (let cat = null;;) {
if (pos == (forward ? line.to : line.from)) {
if (pos == start && line.number != (forward ? state$1.doc.lines : 1))
if (pos == range.head && line.number != (forward ? state$1.doc.lines : 1))
pos += forward ? 1 : -1;
break;
}
@ -1112,7 +1125,7 @@ const deleteByGroup = (target, forward) => deleteBy(target, start => {
let nextCat = categorize(nextChar);
if (cat != null && nextCat != cat)
break;
if (nextChar != " " || pos != start)
if (nextChar != " " || pos != range.head)
cat = nextCat;
pos = next;
}
@ -1133,18 +1146,34 @@ Delete the selection, or, if it is a cursor selection, delete to
the end of the line. If the cursor is directly at the end of the
line, delete the line break after it.
*/
const deleteToLineEnd = view => deleteBy(view, pos => {
let lineEnd = view.lineBlockAt(pos).to;
return pos < lineEnd ? lineEnd : Math.min(view.state.doc.length, pos + 1);
const deleteToLineEnd = view => deleteBy(view, range => {
let lineEnd = view.lineBlockAt(range.head).to;
return range.head < lineEnd ? lineEnd : Math.min(view.state.doc.length, range.head + 1);
});
/**
Delete the selection, or, if it is a cursor selection, delete to
the start of the line. If the cursor is directly at the start of the
line, delete the line break before it.
*/
const deleteToLineStart = view => deleteBy(view, pos => {
let lineStart = view.lineBlockAt(pos).from;
return pos > lineStart ? lineStart : Math.max(0, pos - 1);
const deleteToLineStart = view => deleteBy(view, range => {
let lineStart = view.lineBlockAt(range.head).from;
return range.head > lineStart ? lineStart : Math.max(0, range.head - 1);
});
/**
Delete the selection, or, if it is a cursor selection, delete to
the start of the line or the next line wrap before the cursor.
*/
const deleteLineBoundaryBackward = view => deleteBy(view, range => {
let lineStart = view.moveToLineBoundary(range, false).head;
return range.head > lineStart ? lineStart : Math.max(0, range.head - 1);
});
/**
Delete the selection, or, if it is a cursor selection, delete to
the end of the line or the next line wrap after the cursor.
*/
const deleteLineBoundaryForward = view => deleteBy(view, range => {
let lineStart = view.moveToLineBoundary(range, true).head;
return range.head < lineStart ? lineStart : Math.min(view.state.doc.length, range.head + 1);
});
/**
Delete all whitespace directly before a line end from the
@ -1316,7 +1345,8 @@ function isBetweenBrackets(state, pos) {
let before = context.childBefore(pos), after = context.childAfter(pos), closedBy;
if (before && after && before.to <= pos && after.from >= pos &&
(closedBy = before.type.prop(common.NodeProp.closedBy)) && closedBy.indexOf(after.name) > -1 &&
state.doc.lineAt(before.to).from == state.doc.lineAt(after.from).from)
state.doc.lineAt(before.to).from == state.doc.lineAt(after.from).from &&
!/\S/.test(state.sliceDoc(before.to, after.from)))
return { from: before.to, to: after.from };
return null;
}
@ -1344,7 +1374,7 @@ function newlineAndIndent(atEof) {
let cx = new language.IndentContext(state$1, { simulateBreak: from, simulateDoubleBreak: !!explode });
let indent = language.getIndentation(cx, from);
if (indent == null)
indent = /^\s*/.exec(state$1.doc.lineAt(from).text)[0].length;
indent = state.countColumn(/^\s*/.exec(state$1.doc.lineAt(from).text)[0], state$1.tabSize);
while (to < line.to && /\s/.test(line.text[to - line.from]))
to++;
if (explode)
@ -1514,8 +1544,8 @@ property changed to `mac`.)
- Delete: [`deleteCharForward`](https://codemirror.net/6/docs/ref/#commands.deleteCharForward)
- Ctrl-Backspace (Alt-Backspace on macOS): [`deleteGroupBackward`](https://codemirror.net/6/docs/ref/#commands.deleteGroupBackward)
- Ctrl-Delete (Alt-Delete on macOS): [`deleteGroupForward`](https://codemirror.net/6/docs/ref/#commands.deleteGroupForward)
- Cmd-Backspace (macOS): [`deleteToLineStart`](https://codemirror.net/6/docs/ref/#commands.deleteToLineStart).
- Cmd-Delete (macOS): [`deleteToLineEnd`](https://codemirror.net/6/docs/ref/#commands.deleteToLineEnd).
- Cmd-Backspace (macOS): [`deleteLineBoundaryBackward`](https://codemirror.net/6/docs/ref/#commands.deleteLineBoundaryBackward).
- Cmd-Delete (macOS): [`deleteLineBoundaryForward`](https://codemirror.net/6/docs/ref/#commands.deleteLineBoundaryForward).
*/
const standardKeymap = [
{ key: "ArrowLeft", run: cursorCharLeft, shift: selectCharLeft, preventDefault: true },
@ -1542,8 +1572,8 @@ const standardKeymap = [
{ key: "Delete", run: deleteCharForward },
{ key: "Mod-Backspace", mac: "Alt-Backspace", run: deleteGroupBackward },
{ key: "Mod-Delete", mac: "Alt-Delete", run: deleteGroupForward },
{ mac: "Mod-Backspace", run: deleteToLineStart },
{ mac: "Mod-Delete", run: deleteToLineEnd }
{ mac: "Mod-Backspace", run: deleteLineBoundaryBackward },
{ mac: "Mod-Delete", run: deleteLineBoundaryForward }
].concat(emacsStyleKeymap.map(b => ({ mac: b.key, run: b.run, shift: b.shift })));
/**
The default keymap. Includes all bindings from
@ -1629,6 +1659,8 @@ exports.deleteCharForward = deleteCharForward;
exports.deleteGroupBackward = deleteGroupBackward;
exports.deleteGroupForward = deleteGroupForward;
exports.deleteLine = deleteLine;
exports.deleteLineBoundaryBackward = deleteLineBoundaryBackward;
exports.deleteLineBoundaryForward = deleteLineBoundaryForward;
exports.deleteToLineEnd = deleteToLineEnd;
exports.deleteToLineStart = deleteToLineStart;
exports.deleteTrailingWhitespace = deleteTrailingWhitespace;

576
node_modules/@codemirror/commands/dist/index.d.cts generated vendored Normal file
View file

@ -0,0 +1,576 @@
import * as _codemirror_state from '@codemirror/state';
import { StateCommand, Facet, Transaction, StateEffect, Extension, StateField, EditorState } from '@codemirror/state';
import { KeyBinding, Command } from '@codemirror/view';
/**
An object of this type can be provided as [language
data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt) under a `"commentTokens"`
property to configure comment syntax for a language.
*/
interface CommentTokens {
/**
The block comment syntax, if any. For example, for HTML
you'd provide `{open: "<!--", close: "-->"}`.
*/
block?: {
open: string;
close: string;
};
/**
The line comment syntax. For example `"//"`.
*/
line?: string;
}
/**
Comment or uncomment the current selection. Will use line comments
if available, otherwise falling back to block comments.
*/
declare const toggleComment: StateCommand;
/**
Comment or uncomment the current selection using line comments.
The line comment syntax is taken from the
[`commentTokens`](https://codemirror.net/6/docs/ref/#commands.CommentTokens) [language
data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt).
*/
declare const toggleLineComment: StateCommand;
/**
Comment the current selection using line comments.
*/
declare const lineComment: StateCommand;
/**
Uncomment the current selection using line comments.
*/
declare const lineUncomment: StateCommand;
/**
Comment or uncomment the current selection using block comments.
The block comment syntax is taken from the
[`commentTokens`](https://codemirror.net/6/docs/ref/#commands.CommentTokens) [language
data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt).
*/
declare const toggleBlockComment: StateCommand;
/**
Comment the current selection using block comments.
*/
declare const blockComment: StateCommand;
/**
Uncomment the current selection using block comments.
*/
declare const blockUncomment: StateCommand;
/**
Comment or uncomment the lines around the current selection using
block comments.
*/
declare const toggleBlockCommentByLine: StateCommand;
/**
Transaction annotation that will prevent that transaction from
being combined with other transactions in the undo history. Given
`"before"`, it'll prevent merging with previous transactions. With
`"after"`, subsequent transactions won't be combined with this
one. With `"full"`, the transaction is isolated on both sides.
*/
declare const isolateHistory: _codemirror_state.AnnotationType<"after" | "before" | "full">;
/**
This facet provides a way to register functions that, given a
transaction, provide a set of effects that the history should
store when inverting the transaction. This can be used to
integrate some kinds of effects in the history, so that they can
be undone (and redone again).
*/
declare const invertedEffects: Facet<(tr: Transaction) => readonly StateEffect<any>[], readonly ((tr: Transaction) => readonly StateEffect<any>[])[]>;
interface HistoryConfig {
/**
The minimum depth (amount of events) to store. Defaults to 100.
*/
minDepth?: number;
/**
The maximum time (in milliseconds) that adjacent events can be
apart and still be grouped together. Defaults to 500.
*/
newGroupDelay?: number;
/**
By default, when close enough together in time, changes are
joined into an existing undo event if they touch any of the
changed ranges from that event. You can pass a custom predicate
here to influence that logic.
*/
joinToEvent?: (tr: Transaction, isAdjacent: boolean) => boolean;
}
/**
Create a history extension with the given configuration.
*/
declare function history(config?: HistoryConfig): Extension;
/**
The state field used to store the history data. Should probably
only be used when you want to
[serialize](https://codemirror.net/6/docs/ref/#state.EditorState.toJSON) or
[deserialize](https://codemirror.net/6/docs/ref/#state.EditorState^fromJSON) state objects in a way
that preserves history.
*/
declare const historyField: StateField<unknown>;
/**
Undo a single group of history events. Returns false if no group
was available.
*/
declare const undo: StateCommand;
/**
Redo a group of history events. Returns false if no group was
available.
*/
declare const redo: StateCommand;
/**
Undo a change or selection change.
*/
declare const undoSelection: StateCommand;
/**
Redo a change or selection change.
*/
declare const redoSelection: StateCommand;
/**
The amount of undoable change events available in a given state.
*/
declare const undoDepth: (state: EditorState) => number;
/**
The amount of redoable change events available in a given state.
*/
declare const redoDepth: (state: EditorState) => number;
/**
Default key bindings for the undo history.
- Mod-z: [`undo`](https://codemirror.net/6/docs/ref/#commands.undo).
- Mod-y (Mod-Shift-z on macOS) + Ctrl-Shift-z on Linux: [`redo`](https://codemirror.net/6/docs/ref/#commands.redo).
- Mod-u: [`undoSelection`](https://codemirror.net/6/docs/ref/#commands.undoSelection).
- Alt-u (Mod-Shift-u on macOS): [`redoSelection`](https://codemirror.net/6/docs/ref/#commands.redoSelection).
*/
declare const historyKeymap: readonly KeyBinding[];
/**
Move the selection one character to the left (which is backward in
left-to-right text, forward in right-to-left text).
*/
declare const cursorCharLeft: Command;
/**
Move the selection one character to the right.
*/
declare const cursorCharRight: Command;
/**
Move the selection one character forward.
*/
declare const cursorCharForward: Command;
/**
Move the selection one character backward.
*/
declare const cursorCharBackward: Command;
/**
Move the selection to the left across one group of word or
non-word (but also non-space) characters.
*/
declare const cursorGroupLeft: Command;
/**
Move the selection one group to the right.
*/
declare const cursorGroupRight: Command;
/**
Move the selection one group forward.
*/
declare const cursorGroupForward: Command;
/**
Move the selection one group backward.
*/
declare const cursorGroupBackward: Command;
/**
Move the selection one group or camel-case subword forward.
*/
declare const cursorSubwordForward: Command;
/**
Move the selection one group or camel-case subword backward.
*/
declare const cursorSubwordBackward: Command;
/**
Move the cursor over the next syntactic element to the left.
*/
declare const cursorSyntaxLeft: Command;
/**
Move the cursor over the next syntactic element to the right.
*/
declare const cursorSyntaxRight: Command;
/**
Move the selection one line up.
*/
declare const cursorLineUp: Command;
/**
Move the selection one line down.
*/
declare const cursorLineDown: Command;
/**
Move the selection one page up.
*/
declare const cursorPageUp: Command;
/**
Move the selection one page down.
*/
declare const cursorPageDown: Command;
/**
Move the selection to the next line wrap point, or to the end of
the line if there isn't one left on this line.
*/
declare const cursorLineBoundaryForward: Command;
/**
Move the selection to previous line wrap point, or failing that to
the start of the line. If the line is indented, and the cursor
isn't already at the end of the indentation, this will move to the
end of the indentation instead of the start of the line.
*/
declare const cursorLineBoundaryBackward: Command;
/**
Move the selection one line wrap point to the left.
*/
declare const cursorLineBoundaryLeft: Command;
/**
Move the selection one line wrap point to the right.
*/
declare const cursorLineBoundaryRight: Command;
/**
Move the selection to the start of the line.
*/
declare const cursorLineStart: Command;
/**
Move the selection to the end of the line.
*/
declare const cursorLineEnd: Command;
/**
Move the selection to the bracket matching the one it is currently
on, if any.
*/
declare const cursorMatchingBracket: StateCommand;
/**
Extend the selection to the bracket matching the one the selection
head is currently on, if any.
*/
declare const selectMatchingBracket: StateCommand;
/**
Move the selection head one character to the left, while leaving
the anchor in place.
*/
declare const selectCharLeft: Command;
/**
Move the selection head one character to the right.
*/
declare const selectCharRight: Command;
/**
Move the selection head one character forward.
*/
declare const selectCharForward: Command;
/**
Move the selection head one character backward.
*/
declare const selectCharBackward: Command;
/**
Move the selection head one [group](https://codemirror.net/6/docs/ref/#commands.cursorGroupLeft) to
the left.
*/
declare const selectGroupLeft: Command;
/**
Move the selection head one group to the right.
*/
declare const selectGroupRight: Command;
/**
Move the selection head one group forward.
*/
declare const selectGroupForward: Command;
/**
Move the selection head one group backward.
*/
declare const selectGroupBackward: Command;
/**
Move the selection head one group or camel-case subword forward.
*/
declare const selectSubwordForward: Command;
/**
Move the selection head one group or subword backward.
*/
declare const selectSubwordBackward: Command;
/**
Move the selection head over the next syntactic element to the left.
*/
declare const selectSyntaxLeft: Command;
/**
Move the selection head over the next syntactic element to the right.
*/
declare const selectSyntaxRight: Command;
/**
Move the selection head one line up.
*/
declare const selectLineUp: Command;
/**
Move the selection head one line down.
*/
declare const selectLineDown: Command;
/**
Move the selection head one page up.
*/
declare const selectPageUp: Command;
/**
Move the selection head one page down.
*/
declare const selectPageDown: Command;
/**
Move the selection head to the next line boundary.
*/
declare const selectLineBoundaryForward: Command;
/**
Move the selection head to the previous line boundary.
*/
declare const selectLineBoundaryBackward: Command;
/**
Move the selection head one line boundary to the left.
*/
declare const selectLineBoundaryLeft: Command;
/**
Move the selection head one line boundary to the right.
*/
declare const selectLineBoundaryRight: Command;
/**
Move the selection head to the start of the line.
*/
declare const selectLineStart: Command;
/**
Move the selection head to the end of the line.
*/
declare const selectLineEnd: Command;
/**
Move the selection to the start of the document.
*/
declare const cursorDocStart: StateCommand;
/**
Move the selection to the end of the document.
*/
declare const cursorDocEnd: StateCommand;
/**
Move the selection head to the start of the document.
*/
declare const selectDocStart: StateCommand;
/**
Move the selection head to the end of the document.
*/
declare const selectDocEnd: StateCommand;
/**
Select the entire document.
*/
declare const selectAll: StateCommand;
/**
Expand the selection to cover entire lines.
*/
declare const selectLine: StateCommand;
/**
Select the next syntactic construct that is larger than the
selection. Note that this will only work insofar as the language
[provider](https://codemirror.net/6/docs/ref/#language.language) you use builds up a full
syntax tree.
*/
declare const selectParentSyntax: StateCommand;
/**
Simplify the current selection. When multiple ranges are selected,
reduce it to its main range. Otherwise, if the selection is
non-empty, convert it to a cursor selection.
*/
declare const simplifySelection: StateCommand;
/**
Delete the selection, or, for cursor selections, the character
before the cursor.
*/
declare const deleteCharBackward: Command;
/**
Delete the selection or the character after the cursor.
*/
declare const deleteCharForward: Command;
/**
Delete the selection or backward until the end of the next
[group](https://codemirror.net/6/docs/ref/#view.EditorView.moveByGroup), only skipping groups of
whitespace when they consist of a single space.
*/
declare const deleteGroupBackward: StateCommand;
/**
Delete the selection or forward until the end of the next group.
*/
declare const deleteGroupForward: StateCommand;
/**
Delete the selection, or, if it is a cursor selection, delete to
the end of the line. If the cursor is directly at the end of the
line, delete the line break after it.
*/
declare const deleteToLineEnd: Command;
/**
Delete the selection, or, if it is a cursor selection, delete to
the start of the line. If the cursor is directly at the start of the
line, delete the line break before it.
*/
declare const deleteToLineStart: Command;
/**
Delete the selection, or, if it is a cursor selection, delete to
the start of the line or the next line wrap before the cursor.
*/
declare const deleteLineBoundaryBackward: Command;
/**
Delete the selection, or, if it is a cursor selection, delete to
the end of the line or the next line wrap after the cursor.
*/
declare const deleteLineBoundaryForward: Command;
/**
Delete all whitespace directly before a line end from the
document.
*/
declare const deleteTrailingWhitespace: StateCommand;
/**
Replace each selection range with a line break, leaving the cursor
on the line before the break.
*/
declare const splitLine: StateCommand;
/**
Flip the characters before and after the cursor(s).
*/
declare const transposeChars: StateCommand;
/**
Move the selected lines up one line.
*/
declare const moveLineUp: StateCommand;
/**
Move the selected lines down one line.
*/
declare const moveLineDown: StateCommand;
/**
Create a copy of the selected lines. Keep the selection in the top copy.
*/
declare const copyLineUp: StateCommand;
/**
Create a copy of the selected lines. Keep the selection in the bottom copy.
*/
declare const copyLineDown: StateCommand;
/**
Delete selected lines.
*/
declare const deleteLine: Command;
/**
Replace the selection with a newline.
*/
declare const insertNewline: StateCommand;
/**
Replace the selection with a newline and indent the newly created
line(s). If the current line consists only of whitespace, this
will also delete that whitespace. When the cursor is between
matching brackets, an additional newline will be inserted after
the cursor.
*/
declare const insertNewlineAndIndent: StateCommand;
/**
Create a blank, indented line below the current line.
*/
declare const insertBlankLine: StateCommand;
/**
Auto-indent the selected lines. This uses the [indentation service
facet](https://codemirror.net/6/docs/ref/#language.indentService) as source for auto-indent
information.
*/
declare const indentSelection: StateCommand;
/**
Add a [unit](https://codemirror.net/6/docs/ref/#language.indentUnit) of indentation to all selected
lines.
*/
declare const indentMore: StateCommand;
/**
Remove a [unit](https://codemirror.net/6/docs/ref/#language.indentUnit) of indentation from all
selected lines.
*/
declare const indentLess: StateCommand;
/**
Insert a tab character at the cursor or, if something is selected,
use [`indentMore`](https://codemirror.net/6/docs/ref/#commands.indentMore) to indent the entire
selection.
*/
declare const insertTab: StateCommand;
/**
Array of key bindings containing the Emacs-style bindings that are
available on macOS by default.
- Ctrl-b: [`cursorCharLeft`](https://codemirror.net/6/docs/ref/#commands.cursorCharLeft) ([`selectCharLeft`](https://codemirror.net/6/docs/ref/#commands.selectCharLeft) with Shift)
- Ctrl-f: [`cursorCharRight`](https://codemirror.net/6/docs/ref/#commands.cursorCharRight) ([`selectCharRight`](https://codemirror.net/6/docs/ref/#commands.selectCharRight) with Shift)
- Ctrl-p: [`cursorLineUp`](https://codemirror.net/6/docs/ref/#commands.cursorLineUp) ([`selectLineUp`](https://codemirror.net/6/docs/ref/#commands.selectLineUp) with Shift)
- Ctrl-n: [`cursorLineDown`](https://codemirror.net/6/docs/ref/#commands.cursorLineDown) ([`selectLineDown`](https://codemirror.net/6/docs/ref/#commands.selectLineDown) with Shift)
- Ctrl-a: [`cursorLineStart`](https://codemirror.net/6/docs/ref/#commands.cursorLineStart) ([`selectLineStart`](https://codemirror.net/6/docs/ref/#commands.selectLineStart) with Shift)
- Ctrl-e: [`cursorLineEnd`](https://codemirror.net/6/docs/ref/#commands.cursorLineEnd) ([`selectLineEnd`](https://codemirror.net/6/docs/ref/#commands.selectLineEnd) with Shift)
- Ctrl-d: [`deleteCharForward`](https://codemirror.net/6/docs/ref/#commands.deleteCharForward)
- Ctrl-h: [`deleteCharBackward`](https://codemirror.net/6/docs/ref/#commands.deleteCharBackward)
- Ctrl-k: [`deleteToLineEnd`](https://codemirror.net/6/docs/ref/#commands.deleteToLineEnd)
- Ctrl-Alt-h: [`deleteGroupBackward`](https://codemirror.net/6/docs/ref/#commands.deleteGroupBackward)
- Ctrl-o: [`splitLine`](https://codemirror.net/6/docs/ref/#commands.splitLine)
- Ctrl-t: [`transposeChars`](https://codemirror.net/6/docs/ref/#commands.transposeChars)
- Ctrl-v: [`cursorPageDown`](https://codemirror.net/6/docs/ref/#commands.cursorPageDown)
- Alt-v: [`cursorPageUp`](https://codemirror.net/6/docs/ref/#commands.cursorPageUp)
*/
declare const emacsStyleKeymap: readonly KeyBinding[];
/**
An array of key bindings closely sticking to platform-standard or
widely used bindings. (This includes the bindings from
[`emacsStyleKeymap`](https://codemirror.net/6/docs/ref/#commands.emacsStyleKeymap), with their `key`
property changed to `mac`.)
- ArrowLeft: [`cursorCharLeft`](https://codemirror.net/6/docs/ref/#commands.cursorCharLeft) ([`selectCharLeft`](https://codemirror.net/6/docs/ref/#commands.selectCharLeft) with Shift)
- ArrowRight: [`cursorCharRight`](https://codemirror.net/6/docs/ref/#commands.cursorCharRight) ([`selectCharRight`](https://codemirror.net/6/docs/ref/#commands.selectCharRight) with Shift)
- Ctrl-ArrowLeft (Alt-ArrowLeft on macOS): [`cursorGroupLeft`](https://codemirror.net/6/docs/ref/#commands.cursorGroupLeft) ([`selectGroupLeft`](https://codemirror.net/6/docs/ref/#commands.selectGroupLeft) with Shift)
- Ctrl-ArrowRight (Alt-ArrowRight on macOS): [`cursorGroupRight`](https://codemirror.net/6/docs/ref/#commands.cursorGroupRight) ([`selectGroupRight`](https://codemirror.net/6/docs/ref/#commands.selectGroupRight) with Shift)
- Cmd-ArrowLeft (on macOS): [`cursorLineStart`](https://codemirror.net/6/docs/ref/#commands.cursorLineStart) ([`selectLineStart`](https://codemirror.net/6/docs/ref/#commands.selectLineStart) with Shift)
- Cmd-ArrowRight (on macOS): [`cursorLineEnd`](https://codemirror.net/6/docs/ref/#commands.cursorLineEnd) ([`selectLineEnd`](https://codemirror.net/6/docs/ref/#commands.selectLineEnd) with Shift)
- ArrowUp: [`cursorLineUp`](https://codemirror.net/6/docs/ref/#commands.cursorLineUp) ([`selectLineUp`](https://codemirror.net/6/docs/ref/#commands.selectLineUp) with Shift)
- ArrowDown: [`cursorLineDown`](https://codemirror.net/6/docs/ref/#commands.cursorLineDown) ([`selectLineDown`](https://codemirror.net/6/docs/ref/#commands.selectLineDown) with Shift)
- Cmd-ArrowUp (on macOS): [`cursorDocStart`](https://codemirror.net/6/docs/ref/#commands.cursorDocStart) ([`selectDocStart`](https://codemirror.net/6/docs/ref/#commands.selectDocStart) with Shift)
- Cmd-ArrowDown (on macOS): [`cursorDocEnd`](https://codemirror.net/6/docs/ref/#commands.cursorDocEnd) ([`selectDocEnd`](https://codemirror.net/6/docs/ref/#commands.selectDocEnd) with Shift)
- Ctrl-ArrowUp (on macOS): [`cursorPageUp`](https://codemirror.net/6/docs/ref/#commands.cursorPageUp) ([`selectPageUp`](https://codemirror.net/6/docs/ref/#commands.selectPageUp) with Shift)
- Ctrl-ArrowDown (on macOS): [`cursorPageDown`](https://codemirror.net/6/docs/ref/#commands.cursorPageDown) ([`selectPageDown`](https://codemirror.net/6/docs/ref/#commands.selectPageDown) with Shift)
- PageUp: [`cursorPageUp`](https://codemirror.net/6/docs/ref/#commands.cursorPageUp) ([`selectPageUp`](https://codemirror.net/6/docs/ref/#commands.selectPageUp) with Shift)
- PageDown: [`cursorPageDown`](https://codemirror.net/6/docs/ref/#commands.cursorPageDown) ([`selectPageDown`](https://codemirror.net/6/docs/ref/#commands.selectPageDown) with Shift)
- Home: [`cursorLineBoundaryBackward`](https://codemirror.net/6/docs/ref/#commands.cursorLineBoundaryBackward) ([`selectLineBoundaryBackward`](https://codemirror.net/6/docs/ref/#commands.selectLineBoundaryBackward) with Shift)
- End: [`cursorLineBoundaryForward`](https://codemirror.net/6/docs/ref/#commands.cursorLineBoundaryForward) ([`selectLineBoundaryForward`](https://codemirror.net/6/docs/ref/#commands.selectLineBoundaryForward) with Shift)
- Ctrl-Home (Cmd-Home on macOS): [`cursorDocStart`](https://codemirror.net/6/docs/ref/#commands.cursorDocStart) ([`selectDocStart`](https://codemirror.net/6/docs/ref/#commands.selectDocStart) with Shift)
- Ctrl-End (Cmd-Home on macOS): [`cursorDocEnd`](https://codemirror.net/6/docs/ref/#commands.cursorDocEnd) ([`selectDocEnd`](https://codemirror.net/6/docs/ref/#commands.selectDocEnd) with Shift)
- Enter: [`insertNewlineAndIndent`](https://codemirror.net/6/docs/ref/#commands.insertNewlineAndIndent)
- Ctrl-a (Cmd-a on macOS): [`selectAll`](https://codemirror.net/6/docs/ref/#commands.selectAll)
- Backspace: [`deleteCharBackward`](https://codemirror.net/6/docs/ref/#commands.deleteCharBackward)
- Delete: [`deleteCharForward`](https://codemirror.net/6/docs/ref/#commands.deleteCharForward)
- Ctrl-Backspace (Alt-Backspace on macOS): [`deleteGroupBackward`](https://codemirror.net/6/docs/ref/#commands.deleteGroupBackward)
- Ctrl-Delete (Alt-Delete on macOS): [`deleteGroupForward`](https://codemirror.net/6/docs/ref/#commands.deleteGroupForward)
- Cmd-Backspace (macOS): [`deleteLineBoundaryBackward`](https://codemirror.net/6/docs/ref/#commands.deleteLineBoundaryBackward).
- Cmd-Delete (macOS): [`deleteLineBoundaryForward`](https://codemirror.net/6/docs/ref/#commands.deleteLineBoundaryForward).
*/
declare const standardKeymap: readonly KeyBinding[];
/**
The default keymap. Includes all bindings from
[`standardKeymap`](https://codemirror.net/6/docs/ref/#commands.standardKeymap) plus the following:
- Alt-ArrowLeft (Ctrl-ArrowLeft on macOS): [`cursorSyntaxLeft`](https://codemirror.net/6/docs/ref/#commands.cursorSyntaxLeft) ([`selectSyntaxLeft`](https://codemirror.net/6/docs/ref/#commands.selectSyntaxLeft) with Shift)
- Alt-ArrowRight (Ctrl-ArrowRight on macOS): [`cursorSyntaxRight`](https://codemirror.net/6/docs/ref/#commands.cursorSyntaxRight) ([`selectSyntaxRight`](https://codemirror.net/6/docs/ref/#commands.selectSyntaxRight) with Shift)
- Alt-ArrowUp: [`moveLineUp`](https://codemirror.net/6/docs/ref/#commands.moveLineUp)
- Alt-ArrowDown: [`moveLineDown`](https://codemirror.net/6/docs/ref/#commands.moveLineDown)
- Shift-Alt-ArrowUp: [`copyLineUp`](https://codemirror.net/6/docs/ref/#commands.copyLineUp)
- Shift-Alt-ArrowDown: [`copyLineDown`](https://codemirror.net/6/docs/ref/#commands.copyLineDown)
- Escape: [`simplifySelection`](https://codemirror.net/6/docs/ref/#commands.simplifySelection)
- Ctrl-Enter (Cmd-Enter on macOS): [`insertBlankLine`](https://codemirror.net/6/docs/ref/#commands.insertBlankLine)
- Alt-l (Ctrl-l on macOS): [`selectLine`](https://codemirror.net/6/docs/ref/#commands.selectLine)
- Ctrl-i (Cmd-i on macOS): [`selectParentSyntax`](https://codemirror.net/6/docs/ref/#commands.selectParentSyntax)
- Ctrl-[ (Cmd-[ on macOS): [`indentLess`](https://codemirror.net/6/docs/ref/#commands.indentLess)
- Ctrl-] (Cmd-] on macOS): [`indentMore`](https://codemirror.net/6/docs/ref/#commands.indentMore)
- Ctrl-Alt-\\ (Cmd-Alt-\\ on macOS): [`indentSelection`](https://codemirror.net/6/docs/ref/#commands.indentSelection)
- Shift-Ctrl-k (Shift-Cmd-k on macOS): [`deleteLine`](https://codemirror.net/6/docs/ref/#commands.deleteLine)
- Shift-Ctrl-\\ (Shift-Cmd-\\ on macOS): [`cursorMatchingBracket`](https://codemirror.net/6/docs/ref/#commands.cursorMatchingBracket)
- Ctrl-/ (Cmd-/ on macOS): [`toggleComment`](https://codemirror.net/6/docs/ref/#commands.toggleComment).
- Shift-Alt-a: [`toggleBlockComment`](https://codemirror.net/6/docs/ref/#commands.toggleBlockComment).
*/
declare const defaultKeymap: readonly KeyBinding[];
/**
A binding that binds Tab to [`indentMore`](https://codemirror.net/6/docs/ref/#commands.indentMore) and
Shift-Tab to [`indentLess`](https://codemirror.net/6/docs/ref/#commands.indentLess).
Please see the [Tab example](../../examples/tab/) before using
this.
*/
declare const indentWithTab: KeyBinding;
export { type CommentTokens, blockComment, blockUncomment, copyLineDown, copyLineUp, cursorCharBackward, cursorCharForward, cursorCharLeft, cursorCharRight, cursorDocEnd, cursorDocStart, cursorGroupBackward, cursorGroupForward, cursorGroupLeft, cursorGroupRight, cursorLineBoundaryBackward, cursorLineBoundaryForward, cursorLineBoundaryLeft, cursorLineBoundaryRight, cursorLineDown, cursorLineEnd, cursorLineStart, cursorLineUp, cursorMatchingBracket, cursorPageDown, cursorPageUp, cursorSubwordBackward, cursorSubwordForward, cursorSyntaxLeft, cursorSyntaxRight, defaultKeymap, deleteCharBackward, deleteCharForward, deleteGroupBackward, deleteGroupForward, deleteLine, deleteLineBoundaryBackward, deleteLineBoundaryForward, deleteToLineEnd, deleteToLineStart, deleteTrailingWhitespace, emacsStyleKeymap, history, historyField, historyKeymap, indentLess, indentMore, indentSelection, indentWithTab, insertBlankLine, insertNewline, insertNewlineAndIndent, insertTab, invertedEffects, isolateHistory, lineComment, lineUncomment, moveLineDown, moveLineUp, redo, redoDepth, redoSelection, selectAll, selectCharBackward, selectCharForward, selectCharLeft, selectCharRight, selectDocEnd, selectDocStart, selectGroupBackward, selectGroupForward, selectGroupLeft, selectGroupRight, selectLine, selectLineBoundaryBackward, selectLineBoundaryForward, selectLineBoundaryLeft, selectLineBoundaryRight, selectLineDown, selectLineEnd, selectLineStart, selectLineUp, selectMatchingBracket, selectPageDown, selectPageUp, selectParentSyntax, selectSubwordBackward, selectSubwordForward, selectSyntaxLeft, selectSyntaxRight, simplifySelection, splitLine, standardKeymap, toggleBlockComment, toggleBlockCommentByLine, toggleComment, toggleLineComment, transposeChars, undo, undoDepth, undoSelection };

View file

@ -407,6 +407,16 @@ line, delete the line break before it.
*/
declare const deleteToLineStart: Command;
/**
Delete the selection, or, if it is a cursor selection, delete to
the start of the line or the next line wrap before the cursor.
*/
declare const deleteLineBoundaryBackward: Command;
/**
Delete the selection, or, if it is a cursor selection, delete to
the end of the line or the next line wrap after the cursor.
*/
declare const deleteLineBoundaryForward: Command;
/**
Delete all whitespace directly before a line end from the
document.
*/
@ -528,8 +538,8 @@ property changed to `mac`.)
- Delete: [`deleteCharForward`](https://codemirror.net/6/docs/ref/#commands.deleteCharForward)
- Ctrl-Backspace (Alt-Backspace on macOS): [`deleteGroupBackward`](https://codemirror.net/6/docs/ref/#commands.deleteGroupBackward)
- Ctrl-Delete (Alt-Delete on macOS): [`deleteGroupForward`](https://codemirror.net/6/docs/ref/#commands.deleteGroupForward)
- Cmd-Backspace (macOS): [`deleteToLineStart`](https://codemirror.net/6/docs/ref/#commands.deleteToLineStart).
- Cmd-Delete (macOS): [`deleteToLineEnd`](https://codemirror.net/6/docs/ref/#commands.deleteToLineEnd).
- Cmd-Backspace (macOS): [`deleteLineBoundaryBackward`](https://codemirror.net/6/docs/ref/#commands.deleteLineBoundaryBackward).
- Cmd-Delete (macOS): [`deleteLineBoundaryForward`](https://codemirror.net/6/docs/ref/#commands.deleteLineBoundaryForward).
*/
declare const standardKeymap: readonly KeyBinding[];
/**
@ -563,4 +573,4 @@ this.
*/
declare const indentWithTab: KeyBinding;
export { CommentTokens, blockComment, blockUncomment, copyLineDown, copyLineUp, cursorCharBackward, cursorCharForward, cursorCharLeft, cursorCharRight, cursorDocEnd, cursorDocStart, cursorGroupBackward, cursorGroupForward, cursorGroupLeft, cursorGroupRight, cursorLineBoundaryBackward, cursorLineBoundaryForward, cursorLineBoundaryLeft, cursorLineBoundaryRight, cursorLineDown, cursorLineEnd, cursorLineStart, cursorLineUp, cursorMatchingBracket, cursorPageDown, cursorPageUp, cursorSubwordBackward, cursorSubwordForward, cursorSyntaxLeft, cursorSyntaxRight, defaultKeymap, deleteCharBackward, deleteCharForward, deleteGroupBackward, deleteGroupForward, deleteLine, deleteToLineEnd, deleteToLineStart, deleteTrailingWhitespace, emacsStyleKeymap, history, historyField, historyKeymap, indentLess, indentMore, indentSelection, indentWithTab, insertBlankLine, insertNewline, insertNewlineAndIndent, insertTab, invertedEffects, isolateHistory, lineComment, lineUncomment, moveLineDown, moveLineUp, redo, redoDepth, redoSelection, selectAll, selectCharBackward, selectCharForward, selectCharLeft, selectCharRight, selectDocEnd, selectDocStart, selectGroupBackward, selectGroupForward, selectGroupLeft, selectGroupRight, selectLine, selectLineBoundaryBackward, selectLineBoundaryForward, selectLineBoundaryLeft, selectLineBoundaryRight, selectLineDown, selectLineEnd, selectLineStart, selectLineUp, selectMatchingBracket, selectPageDown, selectPageUp, selectParentSyntax, selectSubwordBackward, selectSubwordForward, selectSyntaxLeft, selectSyntaxRight, simplifySelection, splitLine, standardKeymap, toggleBlockComment, toggleBlockCommentByLine, toggleComment, toggleLineComment, transposeChars, undo, undoDepth, undoSelection };
export { type CommentTokens, blockComment, blockUncomment, copyLineDown, copyLineUp, cursorCharBackward, cursorCharForward, cursorCharLeft, cursorCharRight, cursorDocEnd, cursorDocStart, cursorGroupBackward, cursorGroupForward, cursorGroupLeft, cursorGroupRight, cursorLineBoundaryBackward, cursorLineBoundaryForward, cursorLineBoundaryLeft, cursorLineBoundaryRight, cursorLineDown, cursorLineEnd, cursorLineStart, cursorLineUp, cursorMatchingBracket, cursorPageDown, cursorPageUp, cursorSubwordBackward, cursorSubwordForward, cursorSyntaxLeft, cursorSyntaxRight, defaultKeymap, deleteCharBackward, deleteCharForward, deleteGroupBackward, deleteGroupForward, deleteLine, deleteLineBoundaryBackward, deleteLineBoundaryForward, deleteToLineEnd, deleteToLineStart, deleteTrailingWhitespace, emacsStyleKeymap, history, historyField, historyKeymap, indentLess, indentMore, indentSelection, indentWithTab, insertBlankLine, insertNewline, insertNewlineAndIndent, insertTab, invertedEffects, isolateHistory, lineComment, lineUncomment, moveLineDown, moveLineUp, redo, redoDepth, redoSelection, selectAll, selectCharBackward, selectCharForward, selectCharLeft, selectCharRight, selectDocEnd, selectDocStart, selectGroupBackward, selectGroupForward, selectGroupLeft, selectGroupRight, selectLine, selectLineBoundaryBackward, selectLineBoundaryForward, selectLineBoundaryLeft, selectLineBoundaryRight, selectLineDown, selectLineEnd, selectLineStart, selectLineUp, selectMatchingBracket, selectPageDown, selectPageUp, selectParentSyntax, selectSubwordBackward, selectSubwordForward, selectSyntaxLeft, selectSyntaxRight, simplifySelection, splitLine, standardKeymap, toggleBlockComment, toggleBlockCommentByLine, toggleComment, toggleLineComment, transposeChars, undo, undoDepth, undoSelection };

View file

@ -1,4 +1,4 @@
import { Annotation, Facet, combineConfig, StateField, EditorSelection, Transaction, ChangeSet, ChangeDesc, StateEffect, Text, findClusterBreak, countColumn, CharCategory } from '@codemirror/state';
import { Annotation, Facet, combineConfig, StateField, Transaction, ChangeSet, ChangeDesc, EditorSelection, StateEffect, Text, findClusterBreak, countColumn, CharCategory } from '@codemirror/state';
import { EditorView, Direction } from '@codemirror/view';
import { IndentContext, getIndentation, indentString, matchBrackets, syntaxTree, getIndentUnit, indentUnit } from '@codemirror/language';
import { NodeProp } from '@lezer/common';
@ -214,11 +214,6 @@ const historyConfig = /*@__PURE__*/Facet.define({
});
}
});
function changeEnd(changes) {
let end = 0;
changes.iterChangedRanges((_, to) => end = to);
return end;
}
const historyField_ = /*@__PURE__*/StateField.define({
create() {
return HistoryState.empty;
@ -227,8 +222,7 @@ const historyField_ = /*@__PURE__*/StateField.define({
let config = tr.state.facet(historyConfig);
let fromHist = tr.annotation(fromHistory);
if (fromHist) {
let selection = tr.docChanged ? EditorSelection.single(changeEnd(tr.changes)) : undefined;
let item = HistEvent.fromTransaction(tr, selection), from = fromHist.side;
let item = HistEvent.fromTransaction(tr, fromHist.selection), from = fromHist.side;
let other = from == 0 /* BranchName.Done */ ? state.undone : state.done;
if (item)
other = updateBranch(other, other.length, config.minDepth, item);
@ -509,15 +503,15 @@ class HistoryState {
addMapping(mapping) {
return new HistoryState(addMappingToBranch(this.done, mapping), addMappingToBranch(this.undone, mapping), this.prevTime, this.prevUserEvent);
}
pop(side, state, selection) {
pop(side, state, onlySelection) {
let branch = side == 0 /* BranchName.Done */ ? this.done : this.undone;
if (branch.length == 0)
return null;
let event = branch[branch.length - 1];
if (selection && event.selectionsAfter.length) {
let event = branch[branch.length - 1], selection = event.selectionsAfter[0] || state.selection;
if (onlySelection && event.selectionsAfter.length) {
return state.update({
selection: event.selectionsAfter[event.selectionsAfter.length - 1],
annotations: fromHistory.of({ side, rest: popSelection(branch) }),
annotations: fromHistory.of({ side, rest: popSelection(branch), selection }),
userEvent: side == 0 /* BranchName.Done */ ? "select.undo" : "select.redo",
scrollIntoView: true
});
@ -533,7 +527,7 @@ class HistoryState {
changes: event.changes,
selection: event.startSelection,
effects: event.effects,
annotations: fromHistory.of({ side, rest }),
annotations: fromHistory.of({ side, rest, selection }),
filter: false,
userEvent: side == 0 /* BranchName.Done */ ? "undo" : "redo",
scrollIntoView: true
@ -566,7 +560,7 @@ function setSel(state, selection) {
}
function moveSel({ state, dispatch }, how) {
let selection = updateSel(state.selection, how);
if (selection.eq(state.selection))
if (selection.eq(state.selection, true))
return false;
dispatch(setSel(state, selection));
return true;
@ -617,45 +611,61 @@ const cursorGroupForward = view => cursorByGroup(view, true);
Move the selection one group backward.
*/
const cursorGroupBackward = view => cursorByGroup(view, false);
const segmenter = typeof Intl != "undefined" && Intl.Segmenter ?
/*@__PURE__*/new (Intl.Segmenter)(undefined, { granularity: "word" }) : null;
function moveBySubword(view, range, forward) {
let categorize = view.state.charCategorizer(range.from);
return view.moveByChar(range, forward, start => {
let cat = CharCategory.Space, pos = range.from;
let done = false, sawUpper = false, sawLower = false;
let step = (next) => {
if (done)
return false;
pos += forward ? next.length : -next.length;
let nextCat = categorize(next), ahead;
if (nextCat == CharCategory.Word && next.charCodeAt(0) < 128 && /[\W_]/.test(next))
nextCat = -1; // Treat word punctuation specially
if (cat == CharCategory.Space)
cat = nextCat;
if (cat != nextCat)
return false;
if (cat == CharCategory.Word) {
if (next.toLowerCase() == next) {
if (!forward && sawUpper)
return false;
sawLower = true;
}
else if (sawLower) {
if (forward)
return false;
done = true;
}
else {
if (sawUpper && forward && categorize(ahead = view.state.sliceDoc(pos, pos + 1)) == CharCategory.Word &&
ahead.toLowerCase() == ahead)
return false;
sawUpper = true;
}
let cat = CharCategory.Space, pos = range.from, steps = 0;
let done = false, sawUpper = false, sawLower = false;
let step = (next) => {
if (done)
return false;
pos += forward ? next.length : -next.length;
let nextCat = categorize(next), ahead;
if (nextCat == CharCategory.Word && next.charCodeAt(0) < 128 && /[\W_]/.test(next))
nextCat = -1; // Treat word punctuation specially
if (cat == CharCategory.Space)
cat = nextCat;
if (cat != nextCat)
return false;
if (cat == CharCategory.Word) {
if (next.toLowerCase() == next) {
if (!forward && sawUpper)
return false;
sawLower = true;
}
return true;
};
else if (sawLower) {
if (forward)
return false;
done = true;
}
else {
if (sawUpper && forward && categorize(ahead = view.state.sliceDoc(pos, pos + 1)) == CharCategory.Word &&
ahead.toLowerCase() == ahead)
return false;
sawUpper = true;
}
}
steps++;
return true;
};
let end = view.moveByChar(range, forward, start => {
step(start);
return step;
});
if (segmenter && cat == CharCategory.Word && end.from == range.from + steps * (forward ? 1 : -1)) {
let from = Math.min(range.head, end.head), to = Math.max(range.head, end.head);
let skipped = view.state.sliceDoc(from, to);
if (skipped.length > 1 && /[\u4E00-\uffff]/.test(skipped)) {
let segments = Array.from(segmenter.segment(skipped));
if (segments.length > 1) {
if (forward)
return EditorSelection.cursor(range.head + segments[1].index, -1);
return EditorSelection.cursor(end.head + segments[segments.length - 1].index, 1);
}
}
}
return end;
}
function cursorBySubword(view, forward) {
return moveSel(view, range => range.empty ? moveBySubword(view, range, forward) : rangeEnd(range, forward));
@ -998,12 +1008,15 @@ syntax tree.
const selectParentSyntax = ({ state, dispatch }) => {
let selection = updateSel(state.selection, range => {
var _a;
let context = syntaxTree(state).resolveInner(range.head, 1);
while (!((context.from < range.from && context.to >= range.to) ||
(context.to > range.to && context.from <= range.from) ||
!((_a = context.parent) === null || _a === void 0 ? void 0 : _a.parent)))
context = context.parent;
return EditorSelection.range(context.to, context.from);
let stack = syntaxTree(state).resolveStack(range.from, 1);
for (let cur = stack; cur; cur = cur.next) {
let { node } = cur;
if (((node.from < range.from && node.to >= range.to) ||
(node.to > range.to && node.from <= range.from)) &&
((_a = node.parent) === null || _a === void 0 ? void 0 : _a.parent))
return EditorSelection.range(node.to, node.from);
}
return range;
});
dispatch(setSel(state, selection));
return true;
@ -1031,7 +1044,7 @@ function deleteBy(target, by) {
let changes = state.changeByRange(range => {
let { from, to } = range;
if (from == to) {
let towards = by(from);
let towards = by(range);
if (towards < from) {
event = "delete.backward";
towards = skipAtomic(target, towards, false);
@ -1047,7 +1060,7 @@ function deleteBy(target, by) {
from = skipAtomic(target, from, false);
to = skipAtomic(target, to, true);
}
return from == to ? { range } : { changes: { from, to }, range: EditorSelection.cursor(from) };
return from == to ? { range } : { changes: { from, to }, range: EditorSelection.cursor(from, from < range.head ? -1 : 1) };
});
if (changes.changes.empty)
return false;
@ -1067,8 +1080,8 @@ function skipAtomic(target, pos, forward) {
});
return pos;
}
const deleteByChar = (target, forward) => deleteBy(target, pos => {
let { state } = target, line = state.doc.lineAt(pos), before, targetPos;
const deleteByChar = (target, forward) => deleteBy(target, range => {
let pos = range.from, { state } = target, line = state.doc.lineAt(pos), before, targetPos;
if (!forward && pos > line.from && pos < line.from + 200 &&
!/[^ \t]/.test(before = line.text.slice(0, pos - line.from))) {
if (before[before.length - 1] == "\t")
@ -1082,6 +1095,8 @@ const deleteByChar = (target, forward) => deleteBy(target, pos => {
targetPos = findClusterBreak(line.text, pos - line.from, forward, forward) + line.from;
if (targetPos == pos && line.number != (forward ? state.doc.lines : 1))
targetPos += forward ? 1 : -1;
else if (!forward && /[\ufe00-\ufe0f]/.test(line.text.slice(targetPos - line.from, pos - line.from)))
targetPos = findClusterBreak(line.text, targetPos - line.from, false, false) + line.from;
}
return targetPos;
});
@ -1094,12 +1109,12 @@ const deleteCharBackward = view => deleteByChar(view, false);
Delete the selection or the character after the cursor.
*/
const deleteCharForward = view => deleteByChar(view, true);
const deleteByGroup = (target, forward) => deleteBy(target, start => {
let pos = start, { state } = target, line = state.doc.lineAt(pos);
const deleteByGroup = (target, forward) => deleteBy(target, range => {
let pos = range.head, { state } = target, line = state.doc.lineAt(pos);
let categorize = state.charCategorizer(pos);
for (let cat = null;;) {
if (pos == (forward ? line.to : line.from)) {
if (pos == start && line.number != (forward ? state.doc.lines : 1))
if (pos == range.head && line.number != (forward ? state.doc.lines : 1))
pos += forward ? 1 : -1;
break;
}
@ -1108,7 +1123,7 @@ const deleteByGroup = (target, forward) => deleteBy(target, start => {
let nextCat = categorize(nextChar);
if (cat != null && nextCat != cat)
break;
if (nextChar != " " || pos != start)
if (nextChar != " " || pos != range.head)
cat = nextCat;
pos = next;
}
@ -1129,18 +1144,34 @@ Delete the selection, or, if it is a cursor selection, delete to
the end of the line. If the cursor is directly at the end of the
line, delete the line break after it.
*/
const deleteToLineEnd = view => deleteBy(view, pos => {
let lineEnd = view.lineBlockAt(pos).to;
return pos < lineEnd ? lineEnd : Math.min(view.state.doc.length, pos + 1);
const deleteToLineEnd = view => deleteBy(view, range => {
let lineEnd = view.lineBlockAt(range.head).to;
return range.head < lineEnd ? lineEnd : Math.min(view.state.doc.length, range.head + 1);
});
/**
Delete the selection, or, if it is a cursor selection, delete to
the start of the line. If the cursor is directly at the start of the
line, delete the line break before it.
*/
const deleteToLineStart = view => deleteBy(view, pos => {
let lineStart = view.lineBlockAt(pos).from;
return pos > lineStart ? lineStart : Math.max(0, pos - 1);
const deleteToLineStart = view => deleteBy(view, range => {
let lineStart = view.lineBlockAt(range.head).from;
return range.head > lineStart ? lineStart : Math.max(0, range.head - 1);
});
/**
Delete the selection, or, if it is a cursor selection, delete to
the start of the line or the next line wrap before the cursor.
*/
const deleteLineBoundaryBackward = view => deleteBy(view, range => {
let lineStart = view.moveToLineBoundary(range, false).head;
return range.head > lineStart ? lineStart : Math.max(0, range.head - 1);
});
/**
Delete the selection, or, if it is a cursor selection, delete to
the end of the line or the next line wrap after the cursor.
*/
const deleteLineBoundaryForward = view => deleteBy(view, range => {
let lineStart = view.moveToLineBoundary(range, true).head;
return range.head < lineStart ? lineStart : Math.min(view.state.doc.length, range.head + 1);
});
/**
Delete all whitespace directly before a line end from the
@ -1312,7 +1343,8 @@ function isBetweenBrackets(state, pos) {
let before = context.childBefore(pos), after = context.childAfter(pos), closedBy;
if (before && after && before.to <= pos && after.from >= pos &&
(closedBy = before.type.prop(NodeProp.closedBy)) && closedBy.indexOf(after.name) > -1 &&
state.doc.lineAt(before.to).from == state.doc.lineAt(after.from).from)
state.doc.lineAt(before.to).from == state.doc.lineAt(after.from).from &&
!/\S/.test(state.sliceDoc(before.to, after.from)))
return { from: before.to, to: after.from };
return null;
}
@ -1340,7 +1372,7 @@ function newlineAndIndent(atEof) {
let cx = new IndentContext(state, { simulateBreak: from, simulateDoubleBreak: !!explode });
let indent = getIndentation(cx, from);
if (indent == null)
indent = /^\s*/.exec(state.doc.lineAt(from).text)[0].length;
indent = countColumn(/^\s*/.exec(state.doc.lineAt(from).text)[0], state.tabSize);
while (to < line.to && /\s/.test(line.text[to - line.from]))
to++;
if (explode)
@ -1510,8 +1542,8 @@ property changed to `mac`.)
- Delete: [`deleteCharForward`](https://codemirror.net/6/docs/ref/#commands.deleteCharForward)
- Ctrl-Backspace (Alt-Backspace on macOS): [`deleteGroupBackward`](https://codemirror.net/6/docs/ref/#commands.deleteGroupBackward)
- Ctrl-Delete (Alt-Delete on macOS): [`deleteGroupForward`](https://codemirror.net/6/docs/ref/#commands.deleteGroupForward)
- Cmd-Backspace (macOS): [`deleteToLineStart`](https://codemirror.net/6/docs/ref/#commands.deleteToLineStart).
- Cmd-Delete (macOS): [`deleteToLineEnd`](https://codemirror.net/6/docs/ref/#commands.deleteToLineEnd).
- Cmd-Backspace (macOS): [`deleteLineBoundaryBackward`](https://codemirror.net/6/docs/ref/#commands.deleteLineBoundaryBackward).
- Cmd-Delete (macOS): [`deleteLineBoundaryForward`](https://codemirror.net/6/docs/ref/#commands.deleteLineBoundaryForward).
*/
const standardKeymap = /*@__PURE__*/[
{ key: "ArrowLeft", run: cursorCharLeft, shift: selectCharLeft, preventDefault: true },
@ -1538,8 +1570,8 @@ const standardKeymap = /*@__PURE__*/[
{ key: "Delete", run: deleteCharForward },
{ key: "Mod-Backspace", mac: "Alt-Backspace", run: deleteGroupBackward },
{ key: "Mod-Delete", mac: "Alt-Delete", run: deleteGroupForward },
{ mac: "Mod-Backspace", run: deleteToLineStart },
{ mac: "Mod-Delete", run: deleteToLineEnd }
{ mac: "Mod-Backspace", run: deleteLineBoundaryBackward },
{ mac: "Mod-Delete", run: deleteLineBoundaryForward }
].concat(/*@__PURE__*/emacsStyleKeymap.map(b => ({ mac: b.key, run: b.run, shift: b.shift })));
/**
The default keymap. Includes all bindings from
@ -1590,4 +1622,4 @@ this.
*/
const indentWithTab = { key: "Tab", run: indentMore, shift: indentLess };
export { blockComment, blockUncomment, copyLineDown, copyLineUp, cursorCharBackward, cursorCharForward, cursorCharLeft, cursorCharRight, cursorDocEnd, cursorDocStart, cursorGroupBackward, cursorGroupForward, cursorGroupLeft, cursorGroupRight, cursorLineBoundaryBackward, cursorLineBoundaryForward, cursorLineBoundaryLeft, cursorLineBoundaryRight, cursorLineDown, cursorLineEnd, cursorLineStart, cursorLineUp, cursorMatchingBracket, cursorPageDown, cursorPageUp, cursorSubwordBackward, cursorSubwordForward, cursorSyntaxLeft, cursorSyntaxRight, defaultKeymap, deleteCharBackward, deleteCharForward, deleteGroupBackward, deleteGroupForward, deleteLine, deleteToLineEnd, deleteToLineStart, deleteTrailingWhitespace, emacsStyleKeymap, history, historyField, historyKeymap, indentLess, indentMore, indentSelection, indentWithTab, insertBlankLine, insertNewline, insertNewlineAndIndent, insertTab, invertedEffects, isolateHistory, lineComment, lineUncomment, moveLineDown, moveLineUp, redo, redoDepth, redoSelection, selectAll, selectCharBackward, selectCharForward, selectCharLeft, selectCharRight, selectDocEnd, selectDocStart, selectGroupBackward, selectGroupForward, selectGroupLeft, selectGroupRight, selectLine, selectLineBoundaryBackward, selectLineBoundaryForward, selectLineBoundaryLeft, selectLineBoundaryRight, selectLineDown, selectLineEnd, selectLineStart, selectLineUp, selectMatchingBracket, selectPageDown, selectPageUp, selectParentSyntax, selectSubwordBackward, selectSubwordForward, selectSyntaxLeft, selectSyntaxRight, simplifySelection, splitLine, standardKeymap, toggleBlockComment, toggleBlockCommentByLine, toggleComment, toggleLineComment, transposeChars, undo, undoDepth, undoSelection };
export { blockComment, blockUncomment, copyLineDown, copyLineUp, cursorCharBackward, cursorCharForward, cursorCharLeft, cursorCharRight, cursorDocEnd, cursorDocStart, cursorGroupBackward, cursorGroupForward, cursorGroupLeft, cursorGroupRight, cursorLineBoundaryBackward, cursorLineBoundaryForward, cursorLineBoundaryLeft, cursorLineBoundaryRight, cursorLineDown, cursorLineEnd, cursorLineStart, cursorLineUp, cursorMatchingBracket, cursorPageDown, cursorPageUp, cursorSubwordBackward, cursorSubwordForward, cursorSyntaxLeft, cursorSyntaxRight, defaultKeymap, deleteCharBackward, deleteCharForward, deleteGroupBackward, deleteGroupForward, deleteLine, deleteLineBoundaryBackward, deleteLineBoundaryForward, deleteToLineEnd, deleteToLineStart, deleteTrailingWhitespace, emacsStyleKeymap, history, historyField, historyKeymap, indentLess, indentMore, indentSelection, indentWithTab, insertBlankLine, insertNewline, insertNewlineAndIndent, insertTab, invertedEffects, isolateHistory, lineComment, lineUncomment, moveLineDown, moveLineUp, redo, redoDepth, redoSelection, selectAll, selectCharBackward, selectCharForward, selectCharLeft, selectCharRight, selectDocEnd, selectDocStart, selectGroupBackward, selectGroupForward, selectGroupLeft, selectGroupRight, selectLine, selectLineBoundaryBackward, selectLineBoundaryForward, selectLineBoundaryLeft, selectLineBoundaryRight, selectLineDown, selectLineEnd, selectLineStart, selectLineUp, selectMatchingBracket, selectPageDown, selectPageUp, selectParentSyntax, selectSubwordBackward, selectSubwordForward, selectSyntaxLeft, selectSyntaxRight, simplifySelection, splitLine, standardKeymap, toggleBlockComment, toggleBlockCommentByLine, toggleComment, toggleLineComment, transposeChars, undo, undoDepth, undoSelection };

View file

@ -1,6 +1,6 @@
{
"name": "@codemirror/commands",
"version": "6.2.4",
"version": "6.3.3",
"description": "Collection of editing commands for the CodeMirror code editor",
"scripts": {
"test": "cm-runtests",
@ -27,12 +27,12 @@
"license": "MIT",
"dependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.2.0",
"@codemirror/state": "^6.4.0",
"@codemirror/view": "^6.0.0",
"@lezer/common": "^1.0.0"
"@lezer/common": "^1.1.0"
},
"devDependencies": {
"@codemirror/buildhelper": "^0.1.5",
"@codemirror/buildhelper": "^1.0.0",
"@codemirror/lang-javascript": "^6.0.0"
},
"repository": {

View file

@ -1,3 +1,45 @@
## 6.10.1 (2024-02-02)
### Bug fixes
Fix an issue where, when a lot of code is visible in the initial editor, the bottom bit of code is shown without highlighting for one frame.
## 6.10.0 (2023-12-28)
### New features
The new `bidiIsolates` extension can be used to wrap syntactic elements where this is appropriate in an element that isolates their text direction, avoiding weird ordering of neutral characters on direction boundaries.
## 6.9.3 (2023-11-27)
### Bug fixes
Fix an issue in `StreamLanguage` where it ran out of node type ids if you repeatedly redefined a language with the same token table.
## 6.9.2 (2023-10-24)
### Bug fixes
Allow `StreamParser` tokens get multiple highlighting tags.
## 6.9.1 (2023-09-20)
### Bug fixes
Indentation now works a lot better in mixed-language documents that interleave the languages in a complex way.
Code folding is now able to pick the right foldable syntax node when the line end falls in a mixed-parsing language that doesn't match the target node.
## 6.9.0 (2023-08-16)
### Bug fixes
Make `getIndentation` return null, rather than 0, when there is no syntax tree available.
### New features
The new `preparePlaceholder` option to `codeFolding` makes it possible to display contextual information in a folded range placeholder widget.
## 6.8.0 (2023-06-12)
### New features

View file

@ -1,7 +1,5 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var common = require('@lezer/common');
var state = require('@codemirror/state');
var view = require('@codemirror/view');
@ -538,14 +536,14 @@ class LanguageState {
// state updates with parse work beyond the viewport.
let upto = this.context.treeLen == tr.startState.doc.length ? undefined
: Math.max(tr.changes.mapPos(this.context.treeLen), newCx.viewport.to);
if (!newCx.work(20 /* Apply */, upto))
if (!newCx.work(20 /* Work.Apply */, upto))
newCx.takeTree();
return new LanguageState(newCx);
}
static init(state) {
let vpTo = Math.min(3000 /* InitViewport */, state.doc.length);
let vpTo = Math.min(3000 /* Work.InitViewport */, state.doc.length);
let parseState = ParseContext.create(state.facet(language).parser, state, { from: 0, to: vpTo });
if (!parseState.work(20 /* Apply */, vpTo))
if (!parseState.work(20 /* Work.Apply */, vpTo))
parseState.takeTree();
return new LanguageState(parseState);
}
@ -562,14 +560,14 @@ Language.state = state.StateField.define({
}
});
let requestIdle = (callback) => {
let timeout = setTimeout(() => callback(), 500 /* MaxPause */);
let timeout = setTimeout(() => callback(), 500 /* Work.MaxPause */);
return () => clearTimeout(timeout);
};
if (typeof requestIdleCallback != "undefined")
requestIdle = (callback) => {
let idle = -1, timeout = setTimeout(() => {
idle = requestIdleCallback(callback, { timeout: 500 /* MaxPause */ - 100 /* MinPause */ });
}, 100 /* MinPause */);
idle = requestIdleCallback(callback, { timeout: 500 /* Work.MaxPause */ - 100 /* Work.MinPause */ });
}, 100 /* Work.MinPause */);
return () => idle < 0 ? clearTimeout(timeout) : cancelIdleCallback(idle);
};
const isInputPending = typeof navigator != "undefined" && ((_a = navigator.scheduling) === null || _a === void 0 ? void 0 : _a.isInputPending)
@ -590,9 +588,9 @@ const parseWorker = view.ViewPlugin.fromClass(class ParseWorker {
let cx = this.view.state.field(Language.state).context;
if (cx.updateViewport(update.view.viewport) || this.view.viewport.to > cx.treeLen)
this.scheduleWork();
if (update.docChanged) {
if (update.docChanged || update.selectionSet) {
if (this.view.hasFocus)
this.chunkBudget += 50 /* ChangeBonus */;
this.chunkBudget += 50 /* Work.ChangeBonus */;
this.scheduleWork();
}
this.checkAsyncSchedule(cx);
@ -608,19 +606,19 @@ const parseWorker = view.ViewPlugin.fromClass(class ParseWorker {
this.working = null;
let now = Date.now();
if (this.chunkEnd < now && (this.chunkEnd < 0 || this.view.hasFocus)) { // Start a new chunk
this.chunkEnd = now + 30000 /* ChunkTime */;
this.chunkBudget = 3000 /* ChunkBudget */;
this.chunkEnd = now + 30000 /* Work.ChunkTime */;
this.chunkBudget = 3000 /* Work.ChunkBudget */;
}
if (this.chunkBudget <= 0)
return; // No more budget
let { state, viewport: { to: vpTo } } = this.view, field = state.field(Language.state);
if (field.tree == field.context.tree && field.context.isDone(vpTo + 100000 /* MaxParseAhead */))
if (field.tree == field.context.tree && field.context.isDone(vpTo + 100000 /* Work.MaxParseAhead */))
return;
let endTime = Date.now() + Math.min(this.chunkBudget, 100 /* Slice */, deadline && !isInputPending ? Math.max(25 /* MinSlice */, deadline.timeRemaining() - 5) : 1e9);
let endTime = Date.now() + Math.min(this.chunkBudget, 100 /* Work.Slice */, deadline && !isInputPending ? Math.max(25 /* Work.MinSlice */, deadline.timeRemaining() - 5) : 1e9);
let viewportFirst = field.context.treeLen < vpTo && state.doc.length > vpTo + 1000;
let done = field.context.work(() => {
return isInputPending && isInputPending() || Date.now() > endTime;
}, vpTo + (viewportFirst ? 0 : 100000 /* MaxParseAhead */));
}, vpTo + (viewportFirst ? 0 : 100000 /* Work.MaxParseAhead */));
this.chunkBudget -= Date.now() - now;
if (done || this.chunkBudget <= 0) {
field.context.takeTree();
@ -865,7 +863,7 @@ function getIndentation(context, pos) {
return result;
}
let tree = syntaxTree(context.state);
return tree ? syntaxIndentation(context, tree, pos) : null;
return tree.length >= pos ? syntaxIndentation(context, tree, pos) : null;
}
/**
Create a change set that auto-indents all lines touched by the
@ -996,7 +994,24 @@ indicates that no definitive indentation can be determined.
const indentNodeProp = new common.NodeProp();
// Compute the indentation for a given position from the syntax tree.
function syntaxIndentation(cx, ast, pos) {
return indentFrom(ast.resolveInner(pos).enterUnfinishedNodesBefore(pos), pos, cx);
let stack = ast.resolveStack(pos);
let inner = stack.node.enterUnfinishedNodesBefore(pos);
if (inner != stack.node) {
let add = [];
for (let cur = inner; cur != stack.node; cur = cur.parent)
add.push(cur);
for (let i = add.length - 1; i >= 0; i--)
stack = { node: add[i], next: stack };
}
return indentFor(stack, cx, pos);
}
function indentFor(stack, cx, pos) {
for (let cur = stack; cur; cur = cur.next) {
let strategy = indentStrategy(cur.node);
if (strategy)
return strategy(TreeIndentContext.create(cx, pos, cur));
}
return 0;
}
function ignoreClosed(cx) {
return cx.pos == cx.options.simulateBreak && cx.options.simulateDoubleBreak;
@ -1012,14 +1027,6 @@ function indentStrategy(tree) {
}
return tree.parent == null ? topIndent : null;
}
function indentFrom(node, pos, base) {
for (; node; node = node.parent) {
let strategy = indentStrategy(node);
if (strategy)
return strategy(TreeIndentContext.create(base, pos, node));
}
return null;
}
function topIndent() { return 0; }
/**
Objects of this type provide context information and helper
@ -1032,20 +1039,24 @@ class TreeIndentContext extends IndentContext {
*/
pos,
/**
The syntax tree node to which the indentation strategy
applies.
@internal
*/
node) {
context) {
super(base.state, base.options);
this.base = base;
this.pos = pos;
this.node = node;
this.context = context;
}
/**
The syntax tree node to which the indentation strategy
applies.
*/
get node() { return this.context.node; }
/**
@internal
*/
static create(base, pos, node) {
return new TreeIndentContext(base, pos, node);
static create(base, pos, context) {
return new TreeIndentContext(base, pos, context);
}
/**
Get the text directly after `this.pos`, either the entire line
@ -1086,8 +1097,7 @@ class TreeIndentContext extends IndentContext {
and return the result of that.
*/
continue() {
let parent = this.node.parent;
return parent ? indentFrom(parent, this.pos, this.base) : 0;
return indentFor(this.context.next, this.base, this.pos);
}
}
function isParent(parent, of) {
@ -1229,9 +1239,10 @@ function syntaxFolding(state, start, end) {
let tree = syntaxTree(state);
if (tree.length < end)
return null;
let inner = tree.resolveInner(end, 1);
let stack = tree.resolveStack(end, 1);
let found = null;
for (let cur = inner; cur; cur = cur.parent) {
for (let iter = stack; iter; iter = iter.next) {
let cur = iter.node;
if (cur.to <= end || cur.from > end)
continue;
if (found && cur.from < start)
@ -1304,11 +1315,16 @@ const foldState = state.StateField.define({
update(folded, tr) {
folded = folded.map(tr.changes);
for (let e of tr.effects) {
if (e.is(foldEffect) && !foldExists(folded, e.value.from, e.value.to))
folded = folded.update({ add: [foldWidget.range(e.value.from, e.value.to)] });
else if (e.is(unfoldEffect))
if (e.is(foldEffect) && !foldExists(folded, e.value.from, e.value.to)) {
let { preparePlaceholder } = tr.state.facet(foldConfig);
let widget = !preparePlaceholder ? foldWidget :
view.Decoration.replace({ widget: new PreparedFoldWidget(preparePlaceholder(tr.state, e.value)) });
folded = folded.update({ add: [widget.range(e.value.from, e.value.to)] });
}
else if (e.is(unfoldEffect)) {
folded = folded.update({ filter: (from, to) => e.value.from != from || e.value.to != to,
filterFrom: e.value.from, filterTo: e.value.to });
}
}
// Clear folded ranges that cover the selection head
if (tr.selection) {
@ -1485,6 +1501,7 @@ const foldKeymap = [
];
const defaultConfig = {
placeholderDOM: null,
preparePlaceholder: null,
placeholderText: "…"
};
const foldConfig = state.Facet.define({
@ -1499,27 +1516,36 @@ function codeFolding(config) {
result.push(foldConfig.of(config));
return result;
}
function widgetToDOM(view, prepared) {
let { state } = view, conf = state.facet(foldConfig);
let onclick = (event) => {
let line = view.lineBlockAt(view.posAtDOM(event.target));
let folded = findFold(view.state, line.from, line.to);
if (folded)
view.dispatch({ effects: unfoldEffect.of(folded) });
event.preventDefault();
};
if (conf.placeholderDOM)
return conf.placeholderDOM(view, onclick, prepared);
let element = document.createElement("span");
element.textContent = conf.placeholderText;
element.setAttribute("aria-label", state.phrase("folded code"));
element.title = state.phrase("unfold");
element.className = "cm-foldPlaceholder";
element.onclick = onclick;
return element;
}
const foldWidget = view.Decoration.replace({ widget: new class extends view.WidgetType {
toDOM(view) {
let { state } = view, conf = state.facet(foldConfig);
let onclick = (event) => {
let line = view.lineBlockAt(view.posAtDOM(event.target));
let folded = findFold(view.state, line.from, line.to);
if (folded)
view.dispatch({ effects: unfoldEffect.of(folded) });
event.preventDefault();
};
if (conf.placeholderDOM)
return conf.placeholderDOM(view, onclick);
let element = document.createElement("span");
element.textContent = conf.placeholderText;
element.setAttribute("aria-label", state.phrase("folded code"));
element.title = state.phrase("unfold");
element.className = "cm-foldPlaceholder";
element.onclick = onclick;
return element;
}
toDOM(view) { return widgetToDOM(view, null); }
} });
class PreparedFoldWidget extends view.WidgetType {
constructor(value) {
super();
this.value = value;
}
eq(other) { return this.value == other.value; }
toDOM(view) { return widgetToDOM(view, this.value); }
}
const foldGutterDefaults = {
openText: "⌄",
closedText: "",
@ -1725,16 +1751,20 @@ class TreeHighlighter {
this.markCache = Object.create(null);
this.tree = syntaxTree(view.state);
this.decorations = this.buildDeco(view, getHighlighters(view.state));
this.decoratedTo = view.viewport.to;
}
update(update) {
let tree = syntaxTree(update.state), highlighters = getHighlighters(update.state);
let styleChange = highlighters != getHighlighters(update.startState);
if (tree.length < update.view.viewport.to && !styleChange && tree.type == this.tree.type) {
let { viewport } = update.view, decoratedToMapped = update.changes.mapPos(this.decoratedTo, 1);
if (tree.length < viewport.to && !styleChange && tree.type == this.tree.type && decoratedToMapped >= viewport.to) {
this.decorations = this.decorations.map(update.changes);
this.decoratedTo = decoratedToMapped;
}
else if (tree != this.tree || update.viewportChanged || styleChange) {
this.tree = tree;
this.decorations = this.buildDeco(update.view, highlighters);
this.decoratedTo = viewport.to;
}
}
buildDeco(view$1, highlighters) {
@ -2207,7 +2237,7 @@ class StreamLanguage extends Language {
state = this.streamParser.startState(cx.unit);
statePos = 0;
}
if (pos - statePos > 10000 /* MaxIndentScanDist */)
if (pos - statePos > 10000 /* C.MaxIndentScanDist */)
return null;
while (statePos < pos) {
let line = cx.state.doc.lineAt(statePos), end = Math.min(pos, line.to);
@ -2289,7 +2319,7 @@ class Parse {
this.chunks.push(tree.children[i]);
this.chunkPos.push(tree.positions[i]);
}
if (context && this.parsedPos < context.viewport.from - 100000 /* MaxDistanceBeforeViewport */) {
if (context && this.parsedPos < context.viewport.from - 100000 /* C.MaxDistanceBeforeViewport */) {
this.state = this.lang.streamParser.startState(getIndentUnit(context.state));
context.skipUntilInView(this.parsedPos, context.viewport.from);
this.parsedPos = context.viewport.from;
@ -2299,7 +2329,7 @@ class Parse {
advance() {
let context = ParseContext.get();
let parseEnd = this.stoppedAt == null ? this.to : Math.min(this.to, this.stoppedAt);
let end = Math.min(parseEnd, this.chunkStart + 2048 /* ChunkSize */);
let end = Math.min(parseEnd, this.chunkStart + 2048 /* C.ChunkSize */);
if (context)
end = Math.min(end, context.viewport.to);
while (this.parsedPos < end)
@ -2383,7 +2413,7 @@ class Parse {
let token = readToken(streamParser.token, stream, this.state);
if (token)
offset = this.emitToken(this.lang.tokenTable.resolve(token), this.parsedPos + stream.start, this.parsedPos + stream.pos, 4, offset);
if (stream.start > 10000 /* MaxLineLength */)
if (stream.start > 10000 /* C.MaxLineLength */)
break;
}
}
@ -2399,7 +2429,7 @@ class Parse {
length: this.parsedPos - this.chunkStart,
nodeSet,
topID: 0,
maxBufferLength: 2048 /* ChunkSize */,
maxBufferLength: 2048 /* C.ChunkSize */,
reused: this.chunkReused
});
tree = new common.Tree(tree.type, tree.children, tree.positions, tree.length, [[this.lang.stateAfter, this.lang.streamParser.copyState(this.state)]]);
@ -2426,6 +2456,8 @@ const noTokens = Object.create(null);
const typeArray = [common.NodeType.none];
const nodeSet = new common.NodeSet(typeArray);
const warned = [];
// Cache of node types by name and tags
const byTag = Object.create(null);
const defaultTable = Object.create(null);
for (let [legacyName, name] of [
["variable", "variableName"],
@ -2459,41 +2491,161 @@ function warnForPart(part, msg) {
console.warn(msg);
}
function createTokenType(extra, tagStr) {
let tag = null;
for (let part of tagStr.split(".")) {
let value = (extra[part] || highlight.tags[part]);
if (!value) {
warnForPart(part, `Unknown highlighting tag ${part}`);
}
else if (typeof value == "function") {
if (!tag)
warnForPart(part, `Modifier ${part} used at start of tag`);
else
tag = value(tag);
}
else {
if (tag)
warnForPart(part, `Tag ${part} used as modifier`);
else
tag = value;
let tags = [];
for (let name of tagStr.split(" ")) {
let found = [];
for (let part of name.split(".")) {
let value = (extra[part] || highlight.tags[part]);
if (!value) {
warnForPart(part, `Unknown highlighting tag ${part}`);
}
else if (typeof value == "function") {
if (!found.length)
warnForPart(part, `Modifier ${part} used at start of tag`);
else
found = found.map(value);
}
else {
if (found.length)
warnForPart(part, `Tag ${part} used as modifier`);
else
found = Array.isArray(value) ? value : [value];
}
}
for (let tag of found)
tags.push(tag);
}
if (!tag)
if (!tags.length)
return 0;
let name = tagStr.replace(/ /g, "_"), type = common.NodeType.define({
let name = tagStr.replace(/ /g, "_"), key = name + " " + tags.map(t => t.id);
let known = byTag[key];
if (known)
return known.id;
let type = byTag[key] = common.NodeType.define({
id: typeArray.length,
name,
props: [highlight.styleTags({ [name]: tag })]
props: [highlight.styleTags({ [name]: tags })]
});
typeArray.push(type);
return type.id;
}
function docID(data) {
let type = common.NodeType.define({ id: typeArray.length, name: "Document", props: [languageDataProp.add(() => data)] });
let type = common.NodeType.define({ id: typeArray.length, name: "Document", props: [languageDataProp.add(() => data)], top: true });
typeArray.push(type);
return type;
}
function buildForLine(line) {
return line.length <= 4096 && /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac\ufb50-\ufdff]/.test(line);
}
function textHasRTL(text) {
for (let i = text.iter(); !i.next().done;)
if (buildForLine(i.value))
return true;
return false;
}
function changeAddsRTL(change) {
let added = false;
change.iterChanges((fA, tA, fB, tB, ins) => {
if (!added && textHasRTL(ins))
added = true;
});
return added;
}
const alwaysIsolate = state.Facet.define({ combine: values => values.some(x => x) });
/**
Make sure nodes
[marked](https://lezer.codemirror.net/docs/ref/#common.NodeProp^isolate)
as isolating for bidirectional text are rendered in a way that
isolates them from the surrounding text.
*/
function bidiIsolates(options = {}) {
let extensions = [isolateMarks];
if (options.alwaysIsolate)
extensions.push(alwaysIsolate.of(true));
return extensions;
}
const isolateMarks = view.ViewPlugin.fromClass(class {
constructor(view$1) {
this.always = view$1.state.facet(alwaysIsolate) ||
view$1.textDirection != view.Direction.LTR ||
view$1.state.facet(view.EditorView.perLineTextDirection);
this.hasRTL = !this.always && textHasRTL(view$1.state.doc);
this.tree = syntaxTree(view$1.state);
this.decorations = this.always || this.hasRTL ? buildDeco(view$1, this.tree, this.always) : view.Decoration.none;
}
update(update) {
let always = update.state.facet(alwaysIsolate) ||
update.view.textDirection != view.Direction.LTR ||
update.state.facet(view.EditorView.perLineTextDirection);
if (!always && !this.hasRTL && changeAddsRTL(update.changes))
this.hasRTL = true;
if (!always && !this.hasRTL)
return;
let tree = syntaxTree(update.state);
if (always != this.always || tree != this.tree || update.docChanged || update.viewportChanged) {
this.tree = tree;
this.always = always;
this.decorations = buildDeco(update.view, tree, always);
}
}
}, {
provide: plugin => {
function access(view$1) {
var _a, _b;
return (_b = (_a = view$1.plugin(plugin)) === null || _a === void 0 ? void 0 : _a.decorations) !== null && _b !== void 0 ? _b : view.Decoration.none;
}
return [view.EditorView.outerDecorations.of(access),
state.Prec.lowest(view.EditorView.bidiIsolatedRanges.of(access))];
}
});
function buildDeco(view, tree, always) {
let deco = new state.RangeSetBuilder();
let ranges = view.visibleRanges;
if (!always)
ranges = clipRTLLines(ranges, view.state.doc);
for (let { from, to } of ranges) {
tree.iterate({
enter: node => {
let iso = node.type.prop(common.NodeProp.isolate);
if (iso)
deco.add(node.from, node.to, marks[iso]);
},
from, to
});
}
return deco.finish();
}
function clipRTLLines(ranges, doc) {
let cur = doc.iter(), pos = 0, result = [], last = null;
for (let { from, to } of ranges) {
if (from != pos) {
if (pos < from)
cur.next(from - pos);
pos = from;
}
for (;;) {
let start = pos, end = pos + cur.value.length;
if (!cur.lineBreak && buildForLine(cur.value)) {
if (last && last.to > start - 10)
last.to = Math.min(to, end);
else
result.push(last = { from: start, to: Math.min(to, end) });
}
if (pos >= to)
break;
pos = end;
cur.next();
}
}
return result;
}
const marks = {
rtl: view.Decoration.mark({ class: "cm-iso", inclusive: true, attributes: { dir: "rtl" }, bidiIsolate: view.Direction.RTL }),
ltr: view.Decoration.mark({ class: "cm-iso", inclusive: true, attributes: { dir: "ltr" }, bidiIsolate: view.Direction.LTR }),
auto: view.Decoration.mark({ class: "cm-iso", inclusive: true, attributes: { dir: "auto" }, bidiIsolate: null })
};
exports.DocInput = DocInput;
exports.HighlightStyle = HighlightStyle;
exports.IndentContext = IndentContext;
@ -2505,6 +2657,7 @@ exports.ParseContext = ParseContext;
exports.StreamLanguage = StreamLanguage;
exports.StringStream = StringStream;
exports.TreeIndentContext = TreeIndentContext;
exports.bidiIsolates = bidiIsolates;
exports.bracketMatching = bracketMatching;
exports.bracketMatchingHandle = bracketMatchingHandle;
exports.codeFolding = codeFolding;

View file

@ -567,12 +567,12 @@ declare class TreeIndentContext extends IndentContext {
The position at which indentation is being computed.
*/
readonly pos: number;
private constructor();
/**
The syntax tree node to which the indentation strategy
applies.
*/
readonly node: SyntaxNode;
private constructor();
get node(): SyntaxNode;
/**
Get the text directly after `this.pos`, either the entire line
or the next 100 characters, whichever is shorter.
@ -689,7 +689,7 @@ declare function foldable(state: EditorState, lineStart: number, lineEnd: number
from: number;
to: number;
} | null;
declare type DocRange = {
type DocRange = {
from: number;
to: number;
};
@ -761,23 +761,36 @@ interface FoldConfig {
position of folded code. The `onclick` argument is the default
click event handler, which toggles folding on the line that
holds the element, and should probably be added as an event
handler to the returned element.
handler to the returned element. If
[`preparePlaceholder`](https://codemirror.net/6/docs/ref/#language.FoldConfig.preparePlaceholder)
is given, its result will be passed as 3rd argument. Otherwise,
this will be null.
When this option isn't given, the `placeholderText` option will
be used to create the placeholder element.
*/
placeholderDOM?: ((view: EditorView, onclick: (event: Event) => void) => HTMLElement) | null;
placeholderDOM?: ((view: EditorView, onclick: (event: Event) => void, prepared: any) => HTMLElement) | null;
/**
Text to use as placeholder for folded text. Defaults to `"…"`.
Will be styled with the `"cm-foldPlaceholder"` class.
*/
placeholderText?: string;
/**
Given a range that is being folded, create a value that
describes it, to be used by `placeholderDOM` to render a custom
widget that, for example, indicates something about the folded
range's size or type.
*/
preparePlaceholder?: (state: EditorState, range: {
from: number;
to: number;
}) => any;
}
/**
Create an extension that configures code folding.
*/
declare function codeFolding(config?: FoldConfig): Extension;
declare type Handlers = {
type Handlers = {
[event: string]: (view: EditorView, line: BlockInfo, event: Event) => boolean;
};
interface FoldGutterConfig {
@ -1126,11 +1139,13 @@ interface StreamParser<State> {
Read one token, advancing the stream past it, and returning a
string indicating the token's style tageither the name of one
of the tags in
[`tags`](https://lezer.codemirror.net/docs/ref#highlight.tags),
or such a name suffixed by one or more tag
[`tags`](https://lezer.codemirror.net/docs/ref#highlight.tags)
or [`tokenTable`](https://codemirror.net/6/docs/ref/#language.StreamParser.tokenTable), or such a
name suffixed by one or more tag
[modifier](https://lezer.codemirror.net/docs/ref#highlight.Tag^defineModifier)
names, separated by periods. For example `"keyword"` or
"`variableName.constant"`.
"`variableName.constant"`, or a space-separated set of such
token types.
It is okay to return a zero-length token, but only if that
updates the state so that the next call will return a non-empty
@ -1162,10 +1177,10 @@ interface StreamParser<State> {
/**
Extra tokens to use in this parser. When the tokenizer returns a
token name that exists as a property in this object, the
corresponding tag will be assigned to the token.
corresponding tags will be assigned to the token.
*/
tokenTable?: {
[name: string]: Tag;
[name: string]: Tag | readonly Tag[];
};
}
/**
@ -1182,4 +1197,20 @@ declare class StreamLanguage<State> extends Language {
get allowsNesting(): boolean;
}
export { Config, DocInput, HighlightStyle, IndentContext, LRLanguage, Language, LanguageDescription, LanguageSupport, MatchResult, ParseContext, StreamLanguage, StreamParser, StringStream, Sublanguage, TagStyle, TreeIndentContext, bracketMatching, bracketMatchingHandle, codeFolding, continuedIndent, defaultHighlightStyle, defineLanguageFacet, delimitedIndent, ensureSyntaxTree, flatIndent, foldAll, foldCode, foldEffect, foldGutter, foldInside, foldKeymap, foldNodeProp, foldService, foldState, foldable, foldedRanges, forceParsing, getIndentUnit, getIndentation, highlightingFor, indentNodeProp, indentOnInput, indentRange, indentService, indentString, indentUnit, language, languageDataProp, matchBrackets, sublanguageProp, syntaxHighlighting, syntaxParserRunning, syntaxTree, syntaxTreeAvailable, toggleFold, unfoldAll, unfoldCode, unfoldEffect };
/**
Make sure nodes
[marked](https://lezer.codemirror.net/docs/ref/#common.NodeProp^isolate)
as isolating for bidirectional text are rendered in a way that
isolates them from the surrounding text.
*/
declare function bidiIsolates(options?: {
/**
By default, isolating elements are only added when the editor
direction isn't uniformly left-to-right, or if it is, on lines
that contain right-to-left character. When true, disable this
optimization and add them everywhere.
*/
alwaysIsolate?: boolean;
}): Extension;
export { type Config, DocInput, HighlightStyle, IndentContext, LRLanguage, Language, LanguageDescription, LanguageSupport, type MatchResult, ParseContext, StreamLanguage, type StreamParser, StringStream, type Sublanguage, type TagStyle, TreeIndentContext, bidiIsolates, bracketMatching, bracketMatchingHandle, codeFolding, continuedIndent, defaultHighlightStyle, defineLanguageFacet, delimitedIndent, ensureSyntaxTree, flatIndent, foldAll, foldCode, foldEffect, foldGutter, foldInside, foldKeymap, foldNodeProp, foldService, foldState, foldable, foldedRanges, forceParsing, getIndentUnit, getIndentation, highlightingFor, indentNodeProp, indentOnInput, indentRange, indentService, indentString, indentUnit, language, languageDataProp, matchBrackets, sublanguageProp, syntaxHighlighting, syntaxParserRunning, syntaxTree, syntaxTreeAvailable, toggleFold, unfoldAll, unfoldCode, unfoldEffect };

View file

@ -567,12 +567,12 @@ declare class TreeIndentContext extends IndentContext {
The position at which indentation is being computed.
*/
readonly pos: number;
private constructor();
/**
The syntax tree node to which the indentation strategy
applies.
*/
readonly node: SyntaxNode;
private constructor();
get node(): SyntaxNode;
/**
Get the text directly after `this.pos`, either the entire line
or the next 100 characters, whichever is shorter.
@ -689,7 +689,7 @@ declare function foldable(state: EditorState, lineStart: number, lineEnd: number
from: number;
to: number;
} | null;
declare type DocRange = {
type DocRange = {
from: number;
to: number;
};
@ -761,23 +761,36 @@ interface FoldConfig {
position of folded code. The `onclick` argument is the default
click event handler, which toggles folding on the line that
holds the element, and should probably be added as an event
handler to the returned element.
handler to the returned element. If
[`preparePlaceholder`](https://codemirror.net/6/docs/ref/#language.FoldConfig.preparePlaceholder)
is given, its result will be passed as 3rd argument. Otherwise,
this will be null.
When this option isn't given, the `placeholderText` option will
be used to create the placeholder element.
*/
placeholderDOM?: ((view: EditorView, onclick: (event: Event) => void) => HTMLElement) | null;
placeholderDOM?: ((view: EditorView, onclick: (event: Event) => void, prepared: any) => HTMLElement) | null;
/**
Text to use as placeholder for folded text. Defaults to `"…"`.
Will be styled with the `"cm-foldPlaceholder"` class.
*/
placeholderText?: string;
/**
Given a range that is being folded, create a value that
describes it, to be used by `placeholderDOM` to render a custom
widget that, for example, indicates something about the folded
range's size or type.
*/
preparePlaceholder?: (state: EditorState, range: {
from: number;
to: number;
}) => any;
}
/**
Create an extension that configures code folding.
*/
declare function codeFolding(config?: FoldConfig): Extension;
declare type Handlers = {
type Handlers = {
[event: string]: (view: EditorView, line: BlockInfo, event: Event) => boolean;
};
interface FoldGutterConfig {
@ -1126,11 +1139,13 @@ interface StreamParser<State> {
Read one token, advancing the stream past it, and returning a
string indicating the token's style tageither the name of one
of the tags in
[`tags`](https://lezer.codemirror.net/docs/ref#highlight.tags),
or such a name suffixed by one or more tag
[`tags`](https://lezer.codemirror.net/docs/ref#highlight.tags)
or [`tokenTable`](https://codemirror.net/6/docs/ref/#language.StreamParser.tokenTable), or such a
name suffixed by one or more tag
[modifier](https://lezer.codemirror.net/docs/ref#highlight.Tag^defineModifier)
names, separated by periods. For example `"keyword"` or
"`variableName.constant"`.
"`variableName.constant"`, or a space-separated set of such
token types.
It is okay to return a zero-length token, but only if that
updates the state so that the next call will return a non-empty
@ -1162,10 +1177,10 @@ interface StreamParser<State> {
/**
Extra tokens to use in this parser. When the tokenizer returns a
token name that exists as a property in this object, the
corresponding tag will be assigned to the token.
corresponding tags will be assigned to the token.
*/
tokenTable?: {
[name: string]: Tag;
[name: string]: Tag | readonly Tag[];
};
}
/**
@ -1182,4 +1197,20 @@ declare class StreamLanguage<State> extends Language {
get allowsNesting(): boolean;
}
export { Config, DocInput, HighlightStyle, IndentContext, LRLanguage, Language, LanguageDescription, LanguageSupport, MatchResult, ParseContext, StreamLanguage, StreamParser, StringStream, Sublanguage, TagStyle, TreeIndentContext, bracketMatching, bracketMatchingHandle, codeFolding, continuedIndent, defaultHighlightStyle, defineLanguageFacet, delimitedIndent, ensureSyntaxTree, flatIndent, foldAll, foldCode, foldEffect, foldGutter, foldInside, foldKeymap, foldNodeProp, foldService, foldState, foldable, foldedRanges, forceParsing, getIndentUnit, getIndentation, highlightingFor, indentNodeProp, indentOnInput, indentRange, indentService, indentString, indentUnit, language, languageDataProp, matchBrackets, sublanguageProp, syntaxHighlighting, syntaxParserRunning, syntaxTree, syntaxTreeAvailable, toggleFold, unfoldAll, unfoldCode, unfoldEffect };
/**
Make sure nodes
[marked](https://lezer.codemirror.net/docs/ref/#common.NodeProp^isolate)
as isolating for bidirectional text are rendered in a way that
isolates them from the surrounding text.
*/
declare function bidiIsolates(options?: {
/**
By default, isolating elements are only added when the editor
direction isn't uniformly left-to-right, or if it is, on lines
that contain right-to-left character. When true, disable this
optimization and add them everywhere.
*/
alwaysIsolate?: boolean;
}): Extension;
export { type Config, DocInput, HighlightStyle, IndentContext, LRLanguage, Language, LanguageDescription, LanguageSupport, type MatchResult, ParseContext, StreamLanguage, type StreamParser, StringStream, type Sublanguage, type TagStyle, TreeIndentContext, bidiIsolates, bracketMatching, bracketMatchingHandle, codeFolding, continuedIndent, defaultHighlightStyle, defineLanguageFacet, delimitedIndent, ensureSyntaxTree, flatIndent, foldAll, foldCode, foldEffect, foldGutter, foldInside, foldKeymap, foldNodeProp, foldService, foldState, foldable, foldedRanges, forceParsing, getIndentUnit, getIndentation, highlightingFor, indentNodeProp, indentOnInput, indentRange, indentService, indentString, indentUnit, language, languageDataProp, matchBrackets, sublanguageProp, syntaxHighlighting, syntaxParserRunning, syntaxTree, syntaxTreeAvailable, toggleFold, unfoldAll, unfoldCode, unfoldEffect };

View file

@ -1,6 +1,6 @@
import { NodeProp, Tree, IterMode, TreeFragment, Parser, NodeType, NodeSet } from '@lezer/common';
import { NodeProp, IterMode, Tree, TreeFragment, Parser, NodeType, NodeSet } from '@lezer/common';
import { StateEffect, StateField, Facet, EditorState, countColumn, combineConfig, RangeSet, RangeSetBuilder, Prec } from '@codemirror/state';
import { ViewPlugin, logException, EditorView, Decoration, WidgetType, gutter, GutterMarker } from '@codemirror/view';
import { ViewPlugin, logException, EditorView, Decoration, WidgetType, gutter, GutterMarker, Direction } from '@codemirror/view';
import { tags, tagHighlighter, highlightTree, styleTags } from '@lezer/highlight';
import { StyleModule } from 'style-mod';
@ -534,14 +534,14 @@ class LanguageState {
// state updates with parse work beyond the viewport.
let upto = this.context.treeLen == tr.startState.doc.length ? undefined
: Math.max(tr.changes.mapPos(this.context.treeLen), newCx.viewport.to);
if (!newCx.work(20 /* Apply */, upto))
if (!newCx.work(20 /* Work.Apply */, upto))
newCx.takeTree();
return new LanguageState(newCx);
}
static init(state) {
let vpTo = Math.min(3000 /* InitViewport */, state.doc.length);
let vpTo = Math.min(3000 /* Work.InitViewport */, state.doc.length);
let parseState = ParseContext.create(state.facet(language).parser, state, { from: 0, to: vpTo });
if (!parseState.work(20 /* Apply */, vpTo))
if (!parseState.work(20 /* Work.Apply */, vpTo))
parseState.takeTree();
return new LanguageState(parseState);
}
@ -558,14 +558,14 @@ Language.state = /*@__PURE__*/StateField.define({
}
});
let requestIdle = (callback) => {
let timeout = setTimeout(() => callback(), 500 /* MaxPause */);
let timeout = setTimeout(() => callback(), 500 /* Work.MaxPause */);
return () => clearTimeout(timeout);
};
if (typeof requestIdleCallback != "undefined")
requestIdle = (callback) => {
let idle = -1, timeout = setTimeout(() => {
idle = requestIdleCallback(callback, { timeout: 500 /* MaxPause */ - 100 /* MinPause */ });
}, 100 /* MinPause */);
idle = requestIdleCallback(callback, { timeout: 500 /* Work.MaxPause */ - 100 /* Work.MinPause */ });
}, 100 /* Work.MinPause */);
return () => idle < 0 ? clearTimeout(timeout) : cancelIdleCallback(idle);
};
const isInputPending = typeof navigator != "undefined" && ((_a = navigator.scheduling) === null || _a === void 0 ? void 0 : _a.isInputPending)
@ -586,9 +586,9 @@ const parseWorker = /*@__PURE__*/ViewPlugin.fromClass(class ParseWorker {
let cx = this.view.state.field(Language.state).context;
if (cx.updateViewport(update.view.viewport) || this.view.viewport.to > cx.treeLen)
this.scheduleWork();
if (update.docChanged) {
if (update.docChanged || update.selectionSet) {
if (this.view.hasFocus)
this.chunkBudget += 50 /* ChangeBonus */;
this.chunkBudget += 50 /* Work.ChangeBonus */;
this.scheduleWork();
}
this.checkAsyncSchedule(cx);
@ -604,19 +604,19 @@ const parseWorker = /*@__PURE__*/ViewPlugin.fromClass(class ParseWorker {
this.working = null;
let now = Date.now();
if (this.chunkEnd < now && (this.chunkEnd < 0 || this.view.hasFocus)) { // Start a new chunk
this.chunkEnd = now + 30000 /* ChunkTime */;
this.chunkBudget = 3000 /* ChunkBudget */;
this.chunkEnd = now + 30000 /* Work.ChunkTime */;
this.chunkBudget = 3000 /* Work.ChunkBudget */;
}
if (this.chunkBudget <= 0)
return; // No more budget
let { state, viewport: { to: vpTo } } = this.view, field = state.field(Language.state);
if (field.tree == field.context.tree && field.context.isDone(vpTo + 100000 /* MaxParseAhead */))
if (field.tree == field.context.tree && field.context.isDone(vpTo + 100000 /* Work.MaxParseAhead */))
return;
let endTime = Date.now() + Math.min(this.chunkBudget, 100 /* Slice */, deadline && !isInputPending ? Math.max(25 /* MinSlice */, deadline.timeRemaining() - 5) : 1e9);
let endTime = Date.now() + Math.min(this.chunkBudget, 100 /* Work.Slice */, deadline && !isInputPending ? Math.max(25 /* Work.MinSlice */, deadline.timeRemaining() - 5) : 1e9);
let viewportFirst = field.context.treeLen < vpTo && state.doc.length > vpTo + 1000;
let done = field.context.work(() => {
return isInputPending && isInputPending() || Date.now() > endTime;
}, vpTo + (viewportFirst ? 0 : 100000 /* MaxParseAhead */));
}, vpTo + (viewportFirst ? 0 : 100000 /* Work.MaxParseAhead */));
this.chunkBudget -= Date.now() - now;
if (done || this.chunkBudget <= 0) {
field.context.takeTree();
@ -861,7 +861,7 @@ function getIndentation(context, pos) {
return result;
}
let tree = syntaxTree(context.state);
return tree ? syntaxIndentation(context, tree, pos) : null;
return tree.length >= pos ? syntaxIndentation(context, tree, pos) : null;
}
/**
Create a change set that auto-indents all lines touched by the
@ -992,7 +992,24 @@ indicates that no definitive indentation can be determined.
const indentNodeProp = /*@__PURE__*/new NodeProp();
// Compute the indentation for a given position from the syntax tree.
function syntaxIndentation(cx, ast, pos) {
return indentFrom(ast.resolveInner(pos).enterUnfinishedNodesBefore(pos), pos, cx);
let stack = ast.resolveStack(pos);
let inner = stack.node.enterUnfinishedNodesBefore(pos);
if (inner != stack.node) {
let add = [];
for (let cur = inner; cur != stack.node; cur = cur.parent)
add.push(cur);
for (let i = add.length - 1; i >= 0; i--)
stack = { node: add[i], next: stack };
}
return indentFor(stack, cx, pos);
}
function indentFor(stack, cx, pos) {
for (let cur = stack; cur; cur = cur.next) {
let strategy = indentStrategy(cur.node);
if (strategy)
return strategy(TreeIndentContext.create(cx, pos, cur));
}
return 0;
}
function ignoreClosed(cx) {
return cx.pos == cx.options.simulateBreak && cx.options.simulateDoubleBreak;
@ -1008,14 +1025,6 @@ function indentStrategy(tree) {
}
return tree.parent == null ? topIndent : null;
}
function indentFrom(node, pos, base) {
for (; node; node = node.parent) {
let strategy = indentStrategy(node);
if (strategy)
return strategy(TreeIndentContext.create(base, pos, node));
}
return null;
}
function topIndent() { return 0; }
/**
Objects of this type provide context information and helper
@ -1028,20 +1037,24 @@ class TreeIndentContext extends IndentContext {
*/
pos,
/**
The syntax tree node to which the indentation strategy
applies.
@internal
*/
node) {
context) {
super(base.state, base.options);
this.base = base;
this.pos = pos;
this.node = node;
this.context = context;
}
/**
The syntax tree node to which the indentation strategy
applies.
*/
get node() { return this.context.node; }
/**
@internal
*/
static create(base, pos, node) {
return new TreeIndentContext(base, pos, node);
static create(base, pos, context) {
return new TreeIndentContext(base, pos, context);
}
/**
Get the text directly after `this.pos`, either the entire line
@ -1082,8 +1095,7 @@ class TreeIndentContext extends IndentContext {
and return the result of that.
*/
continue() {
let parent = this.node.parent;
return parent ? indentFrom(parent, this.pos, this.base) : 0;
return indentFor(this.context.next, this.base, this.pos);
}
}
function isParent(parent, of) {
@ -1225,9 +1237,10 @@ function syntaxFolding(state, start, end) {
let tree = syntaxTree(state);
if (tree.length < end)
return null;
let inner = tree.resolveInner(end, 1);
let stack = tree.resolveStack(end, 1);
let found = null;
for (let cur = inner; cur; cur = cur.parent) {
for (let iter = stack; iter; iter = iter.next) {
let cur = iter.node;
if (cur.to <= end || cur.from > end)
continue;
if (found && cur.from < start)
@ -1300,11 +1313,16 @@ const foldState = /*@__PURE__*/StateField.define({
update(folded, tr) {
folded = folded.map(tr.changes);
for (let e of tr.effects) {
if (e.is(foldEffect) && !foldExists(folded, e.value.from, e.value.to))
folded = folded.update({ add: [foldWidget.range(e.value.from, e.value.to)] });
else if (e.is(unfoldEffect))
if (e.is(foldEffect) && !foldExists(folded, e.value.from, e.value.to)) {
let { preparePlaceholder } = tr.state.facet(foldConfig);
let widget = !preparePlaceholder ? foldWidget :
Decoration.replace({ widget: new PreparedFoldWidget(preparePlaceholder(tr.state, e.value)) });
folded = folded.update({ add: [widget.range(e.value.from, e.value.to)] });
}
else if (e.is(unfoldEffect)) {
folded = folded.update({ filter: (from, to) => e.value.from != from || e.value.to != to,
filterFrom: e.value.from, filterTo: e.value.to });
}
}
// Clear folded ranges that cover the selection head
if (tr.selection) {
@ -1481,6 +1499,7 @@ const foldKeymap = [
];
const defaultConfig = {
placeholderDOM: null,
preparePlaceholder: null,
placeholderText: "…"
};
const foldConfig = /*@__PURE__*/Facet.define({
@ -1495,27 +1514,36 @@ function codeFolding(config) {
result.push(foldConfig.of(config));
return result;
}
function widgetToDOM(view, prepared) {
let { state } = view, conf = state.facet(foldConfig);
let onclick = (event) => {
let line = view.lineBlockAt(view.posAtDOM(event.target));
let folded = findFold(view.state, line.from, line.to);
if (folded)
view.dispatch({ effects: unfoldEffect.of(folded) });
event.preventDefault();
};
if (conf.placeholderDOM)
return conf.placeholderDOM(view, onclick, prepared);
let element = document.createElement("span");
element.textContent = conf.placeholderText;
element.setAttribute("aria-label", state.phrase("folded code"));
element.title = state.phrase("unfold");
element.className = "cm-foldPlaceholder";
element.onclick = onclick;
return element;
}
const foldWidget = /*@__PURE__*/Decoration.replace({ widget: /*@__PURE__*/new class extends WidgetType {
toDOM(view) {
let { state } = view, conf = state.facet(foldConfig);
let onclick = (event) => {
let line = view.lineBlockAt(view.posAtDOM(event.target));
let folded = findFold(view.state, line.from, line.to);
if (folded)
view.dispatch({ effects: unfoldEffect.of(folded) });
event.preventDefault();
};
if (conf.placeholderDOM)
return conf.placeholderDOM(view, onclick);
let element = document.createElement("span");
element.textContent = conf.placeholderText;
element.setAttribute("aria-label", state.phrase("folded code"));
element.title = state.phrase("unfold");
element.className = "cm-foldPlaceholder";
element.onclick = onclick;
return element;
}
toDOM(view) { return widgetToDOM(view, null); }
} });
class PreparedFoldWidget extends WidgetType {
constructor(value) {
super();
this.value = value;
}
eq(other) { return this.value == other.value; }
toDOM(view) { return widgetToDOM(view, this.value); }
}
const foldGutterDefaults = {
openText: "⌄",
closedText: "",
@ -1721,16 +1749,20 @@ class TreeHighlighter {
this.markCache = Object.create(null);
this.tree = syntaxTree(view.state);
this.decorations = this.buildDeco(view, getHighlighters(view.state));
this.decoratedTo = view.viewport.to;
}
update(update) {
let tree = syntaxTree(update.state), highlighters = getHighlighters(update.state);
let styleChange = highlighters != getHighlighters(update.startState);
if (tree.length < update.view.viewport.to && !styleChange && tree.type == this.tree.type) {
let { viewport } = update.view, decoratedToMapped = update.changes.mapPos(this.decoratedTo, 1);
if (tree.length < viewport.to && !styleChange && tree.type == this.tree.type && decoratedToMapped >= viewport.to) {
this.decorations = this.decorations.map(update.changes);
this.decoratedTo = decoratedToMapped;
}
else if (tree != this.tree || update.viewportChanged || styleChange) {
this.tree = tree;
this.decorations = this.buildDeco(update.view, highlighters);
this.decoratedTo = viewport.to;
}
}
buildDeco(view, highlighters) {
@ -2203,7 +2235,7 @@ class StreamLanguage extends Language {
state = this.streamParser.startState(cx.unit);
statePos = 0;
}
if (pos - statePos > 10000 /* MaxIndentScanDist */)
if (pos - statePos > 10000 /* C.MaxIndentScanDist */)
return null;
while (statePos < pos) {
let line = cx.state.doc.lineAt(statePos), end = Math.min(pos, line.to);
@ -2285,7 +2317,7 @@ class Parse {
this.chunks.push(tree.children[i]);
this.chunkPos.push(tree.positions[i]);
}
if (context && this.parsedPos < context.viewport.from - 100000 /* MaxDistanceBeforeViewport */) {
if (context && this.parsedPos < context.viewport.from - 100000 /* C.MaxDistanceBeforeViewport */) {
this.state = this.lang.streamParser.startState(getIndentUnit(context.state));
context.skipUntilInView(this.parsedPos, context.viewport.from);
this.parsedPos = context.viewport.from;
@ -2295,7 +2327,7 @@ class Parse {
advance() {
let context = ParseContext.get();
let parseEnd = this.stoppedAt == null ? this.to : Math.min(this.to, this.stoppedAt);
let end = Math.min(parseEnd, this.chunkStart + 2048 /* ChunkSize */);
let end = Math.min(parseEnd, this.chunkStart + 2048 /* C.ChunkSize */);
if (context)
end = Math.min(end, context.viewport.to);
while (this.parsedPos < end)
@ -2379,7 +2411,7 @@ class Parse {
let token = readToken(streamParser.token, stream, this.state);
if (token)
offset = this.emitToken(this.lang.tokenTable.resolve(token), this.parsedPos + stream.start, this.parsedPos + stream.pos, 4, offset);
if (stream.start > 10000 /* MaxLineLength */)
if (stream.start > 10000 /* C.MaxLineLength */)
break;
}
}
@ -2395,7 +2427,7 @@ class Parse {
length: this.parsedPos - this.chunkStart,
nodeSet,
topID: 0,
maxBufferLength: 2048 /* ChunkSize */,
maxBufferLength: 2048 /* C.ChunkSize */,
reused: this.chunkReused
});
tree = new Tree(tree.type, tree.children, tree.positions, tree.length, [[this.lang.stateAfter, this.lang.streamParser.copyState(this.state)]]);
@ -2422,6 +2454,8 @@ const noTokens = /*@__PURE__*/Object.create(null);
const typeArray = [NodeType.none];
const nodeSet = /*@__PURE__*/new NodeSet(typeArray);
const warned = [];
// Cache of node types by name and tags
const byTag = /*@__PURE__*/Object.create(null);
const defaultTable = /*@__PURE__*/Object.create(null);
for (let [legacyName, name] of [
["variable", "variableName"],
@ -2455,39 +2489,159 @@ function warnForPart(part, msg) {
console.warn(msg);
}
function createTokenType(extra, tagStr) {
let tag = null;
for (let part of tagStr.split(".")) {
let value = (extra[part] || tags[part]);
if (!value) {
warnForPart(part, `Unknown highlighting tag ${part}`);
}
else if (typeof value == "function") {
if (!tag)
warnForPart(part, `Modifier ${part} used at start of tag`);
else
tag = value(tag);
}
else {
if (tag)
warnForPart(part, `Tag ${part} used as modifier`);
else
tag = value;
let tags$1 = [];
for (let name of tagStr.split(" ")) {
let found = [];
for (let part of name.split(".")) {
let value = (extra[part] || tags[part]);
if (!value) {
warnForPart(part, `Unknown highlighting tag ${part}`);
}
else if (typeof value == "function") {
if (!found.length)
warnForPart(part, `Modifier ${part} used at start of tag`);
else
found = found.map(value);
}
else {
if (found.length)
warnForPart(part, `Tag ${part} used as modifier`);
else
found = Array.isArray(value) ? value : [value];
}
}
for (let tag of found)
tags$1.push(tag);
}
if (!tag)
if (!tags$1.length)
return 0;
let name = tagStr.replace(/ /g, "_"), type = NodeType.define({
let name = tagStr.replace(/ /g, "_"), key = name + " " + tags$1.map(t => t.id);
let known = byTag[key];
if (known)
return known.id;
let type = byTag[key] = NodeType.define({
id: typeArray.length,
name,
props: [styleTags({ [name]: tag })]
props: [styleTags({ [name]: tags$1 })]
});
typeArray.push(type);
return type.id;
}
function docID(data) {
let type = NodeType.define({ id: typeArray.length, name: "Document", props: [languageDataProp.add(() => data)] });
let type = NodeType.define({ id: typeArray.length, name: "Document", props: [languageDataProp.add(() => data)], top: true });
typeArray.push(type);
return type;
}
export { DocInput, HighlightStyle, IndentContext, LRLanguage, Language, LanguageDescription, LanguageSupport, ParseContext, StreamLanguage, StringStream, TreeIndentContext, bracketMatching, bracketMatchingHandle, codeFolding, continuedIndent, defaultHighlightStyle, defineLanguageFacet, delimitedIndent, ensureSyntaxTree, flatIndent, foldAll, foldCode, foldEffect, foldGutter, foldInside, foldKeymap, foldNodeProp, foldService, foldState, foldable, foldedRanges, forceParsing, getIndentUnit, getIndentation, highlightingFor, indentNodeProp, indentOnInput, indentRange, indentService, indentString, indentUnit, language, languageDataProp, matchBrackets, sublanguageProp, syntaxHighlighting, syntaxParserRunning, syntaxTree, syntaxTreeAvailable, toggleFold, unfoldAll, unfoldCode, unfoldEffect };
function buildForLine(line) {
return line.length <= 4096 && /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac\ufb50-\ufdff]/.test(line);
}
function textHasRTL(text) {
for (let i = text.iter(); !i.next().done;)
if (buildForLine(i.value))
return true;
return false;
}
function changeAddsRTL(change) {
let added = false;
change.iterChanges((fA, tA, fB, tB, ins) => {
if (!added && textHasRTL(ins))
added = true;
});
return added;
}
const alwaysIsolate = /*@__PURE__*/Facet.define({ combine: values => values.some(x => x) });
/**
Make sure nodes
[marked](https://lezer.codemirror.net/docs/ref/#common.NodeProp^isolate)
as isolating for bidirectional text are rendered in a way that
isolates them from the surrounding text.
*/
function bidiIsolates(options = {}) {
let extensions = [isolateMarks];
if (options.alwaysIsolate)
extensions.push(alwaysIsolate.of(true));
return extensions;
}
const isolateMarks = /*@__PURE__*/ViewPlugin.fromClass(class {
constructor(view) {
this.always = view.state.facet(alwaysIsolate) ||
view.textDirection != Direction.LTR ||
view.state.facet(EditorView.perLineTextDirection);
this.hasRTL = !this.always && textHasRTL(view.state.doc);
this.tree = syntaxTree(view.state);
this.decorations = this.always || this.hasRTL ? buildDeco(view, this.tree, this.always) : Decoration.none;
}
update(update) {
let always = update.state.facet(alwaysIsolate) ||
update.view.textDirection != Direction.LTR ||
update.state.facet(EditorView.perLineTextDirection);
if (!always && !this.hasRTL && changeAddsRTL(update.changes))
this.hasRTL = true;
if (!always && !this.hasRTL)
return;
let tree = syntaxTree(update.state);
if (always != this.always || tree != this.tree || update.docChanged || update.viewportChanged) {
this.tree = tree;
this.always = always;
this.decorations = buildDeco(update.view, tree, always);
}
}
}, {
provide: plugin => {
function access(view) {
var _a, _b;
return (_b = (_a = view.plugin(plugin)) === null || _a === void 0 ? void 0 : _a.decorations) !== null && _b !== void 0 ? _b : Decoration.none;
}
return [EditorView.outerDecorations.of(access),
Prec.lowest(EditorView.bidiIsolatedRanges.of(access))];
}
});
function buildDeco(view, tree, always) {
let deco = new RangeSetBuilder();
let ranges = view.visibleRanges;
if (!always)
ranges = clipRTLLines(ranges, view.state.doc);
for (let { from, to } of ranges) {
tree.iterate({
enter: node => {
let iso = node.type.prop(NodeProp.isolate);
if (iso)
deco.add(node.from, node.to, marks[iso]);
},
from, to
});
}
return deco.finish();
}
function clipRTLLines(ranges, doc) {
let cur = doc.iter(), pos = 0, result = [], last = null;
for (let { from, to } of ranges) {
if (from != pos) {
if (pos < from)
cur.next(from - pos);
pos = from;
}
for (;;) {
let start = pos, end = pos + cur.value.length;
if (!cur.lineBreak && buildForLine(cur.value)) {
if (last && last.to > start - 10)
last.to = Math.min(to, end);
else
result.push(last = { from: start, to: Math.min(to, end) });
}
if (pos >= to)
break;
pos = end;
cur.next();
}
}
return result;
}
const marks = {
rtl: /*@__PURE__*/Decoration.mark({ class: "cm-iso", inclusive: true, attributes: { dir: "rtl" }, bidiIsolate: Direction.RTL }),
ltr: /*@__PURE__*/Decoration.mark({ class: "cm-iso", inclusive: true, attributes: { dir: "ltr" }, bidiIsolate: Direction.LTR }),
auto: /*@__PURE__*/Decoration.mark({ class: "cm-iso", inclusive: true, attributes: { dir: "auto" }, bidiIsolate: null })
};
export { DocInput, HighlightStyle, IndentContext, LRLanguage, Language, LanguageDescription, LanguageSupport, ParseContext, StreamLanguage, StringStream, TreeIndentContext, bidiIsolates, bracketMatching, bracketMatchingHandle, codeFolding, continuedIndent, defaultHighlightStyle, defineLanguageFacet, delimitedIndent, ensureSyntaxTree, flatIndent, foldAll, foldCode, foldEffect, foldGutter, foldInside, foldKeymap, foldNodeProp, foldService, foldState, foldable, foldedRanges, forceParsing, getIndentUnit, getIndentation, highlightingFor, indentNodeProp, indentOnInput, indentRange, indentService, indentString, indentUnit, language, languageDataProp, matchBrackets, sublanguageProp, syntaxHighlighting, syntaxParserRunning, syntaxTree, syntaxTreeAvailable, toggleFold, unfoldAll, unfoldCode, unfoldEffect };

View file

@ -1,6 +1,6 @@
{
"name": "@codemirror/language",
"version": "6.8.0",
"version": "6.10.1",
"description": "Language support infrastructure for the CodeMirror code editor",
"scripts": {
"test": "cm-runtests",
@ -27,8 +27,8 @@
"license": "MIT",
"dependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0",
"@lezer/common": "^1.0.0",
"@codemirror/view": "^6.23.0",
"@lezer/common": "^1.1.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.0.0",
"style-mod": "^4.0.0"

View file

@ -1,3 +1,27 @@
## 6.5.0 (2024-01-30)
### Bug fixes
Make lint mark decorations inclusive, so that they are applied even if the marked content is replaced by a widget decoration.
### New features
`linter` can now be called with null as source to only provide a configuration.
`markerFilter` and `tooltipFilter` function now get passed the current editor state.
## 6.4.2 (2023-09-14)
### Bug fixes
Make sure scrolling diagnostic into view in the panel works when the editor is scaled.
## 6.4.1 (2023-08-26)
### Bug fixes
Fix a crash that could occur when a view was reconfigured in a way that removed the lint extension.
## 6.4.0 (2023-07-03)
### New features

View file

@ -1,15 +1,9 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var view = require('@codemirror/view');
var state = require('@codemirror/state');
var elt = require('crelt');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var elt__default = /*#__PURE__*/_interopDefaultLegacy(elt);
class SelectedDiagnostic {
constructor(from, to, diagnostic) {
this.from = from;
@ -28,7 +22,7 @@ class LintState {
let markedDiagnostics = diagnostics;
let diagnosticFilter = state.facet(lintConfig).markerFilter;
if (diagnosticFilter)
markedDiagnostics = diagnosticFilter(markedDiagnostics);
markedDiagnostics = diagnosticFilter(markedDiagnostics, state);
let ranges = view.Decoration.set(markedDiagnostics.map((d) => {
// For zero-length ranges or ranges covering only a line break, create a widget
return d.from == d.to || (d.from == d.to - 1 && state.doc.lineAt(d.from).to == d.from)
@ -38,7 +32,8 @@ class LintState {
}).range(d.from)
: view.Decoration.mark({
attributes: { class: "cm-lintRange cm-lintRange-" + d.severity + (d.markClass ? " " + d.markClass : "") },
diagnostic: d
diagnostic: d,
inclusive: true
}).range(d.from, d.to);
}), true);
return new LintState(ranges, panel, findDiagnostic(ranges));
@ -114,7 +109,7 @@ function diagnosticCount(state) {
let lint = state.field(lintState, false);
return lint ? lint.diagnostics.size : 0;
}
const activeMark = view.Decoration.mark({ class: "cm-lintRange cm-lintRange-active" });
const activeMark = view.Decoration.mark({ class: "cm-lintRange cm-lintRange-active", inclusive: true });
function lintTooltip(view, pos, side) {
let { diagnostics } = view.state.field(lintState);
let found = [], stackStart = 2e8, stackEnd = 0;
@ -128,7 +123,7 @@ function lintTooltip(view, pos, side) {
});
let diagnosticFilter = view.state.facet(lintConfig).tooltipFilter;
if (diagnosticFilter)
found = diagnosticFilter(found);
found = diagnosticFilter(found, view.state);
if (!found.length)
return null;
return {
@ -141,7 +136,7 @@ function lintTooltip(view, pos, side) {
};
}
function diagnosticsTooltip(view, diagnostics) {
return elt__default["default"]("ul", { class: "cm-tooltip-lint" }, diagnostics.map(d => renderDiagnostic(view, d, false)));
return elt("ul", { class: "cm-tooltip-lint" }, diagnostics.map(d => renderDiagnostic(view, d, false)));
}
/**
Command to open and focus the lint panel.
@ -228,16 +223,17 @@ const lintPlugin = view.ViewPlugin.fromClass(class {
run() {
let now = Date.now();
if (now < this.lintTime - 10) {
setTimeout(this.run, this.lintTime - now);
this.timeout = setTimeout(this.run, this.lintTime - now);
}
else {
this.set = false;
let { state } = this.view, { sources } = state.facet(lintConfig);
Promise.all(sources.map(source => Promise.resolve(source(this.view)))).then(annotations => {
let all = annotations.reduce((a, b) => a.concat(b));
if (this.view.state.doc == state.doc)
this.view.dispatch(setDiagnostics(this.view.state, all));
}, error => { view.logException(this.view.state, error); });
if (sources.length)
Promise.all(sources.map(source => Promise.resolve(source(this.view)))).then(annotations => {
let all = annotations.reduce((a, b) => a.concat(b));
if (this.view.state.doc == state.doc)
this.view.dispatch(setDiagnostics(this.view.state, all));
}, error => { view.logException(this.view.state, error); });
}
}
update(update) {
@ -263,7 +259,7 @@ const lintPlugin = view.ViewPlugin.fromClass(class {
});
const lintConfig = state.Facet.define({
combine(input) {
return Object.assign({ sources: input.map(i => i.source) }, state.combineConfig(input.map(i => i.config), {
return Object.assign({ sources: input.map(i => i.source).filter(x => x != null) }, state.combineConfig(input.map(i => i.config), {
delay: 750,
markerFilter: null,
tooltipFilter: null,
@ -276,7 +272,8 @@ const lintConfig = state.Facet.define({
/**
Given a diagnostic source, this function returns an extension that
enables linting with that source. It will be called whenever the
editor is idle (after its content changed).
editor is idle (after its content changed). If `null` is given as
source, this only configures the lint extension.
*/
function linter(source, config = {}) {
return [
@ -312,7 +309,7 @@ function assignKeys(actions) {
function renderDiagnostic(view, diagnostic, inPanel) {
var _a;
let keys = inPanel ? assignKeys(diagnostic.actions) : [];
return elt__default["default"]("li", { class: "cm-diagnostic cm-diagnostic-" + diagnostic.severity }, elt__default["default"]("span", { class: "cm-diagnosticText" }, diagnostic.renderMessage ? diagnostic.renderMessage() : diagnostic.message), (_a = diagnostic.actions) === null || _a === void 0 ? void 0 : _a.map((action, i) => {
return elt("li", { class: "cm-diagnostic cm-diagnostic-" + diagnostic.severity }, elt("span", { class: "cm-diagnosticText" }, diagnostic.renderMessage ? diagnostic.renderMessage() : diagnostic.message), (_a = diagnostic.actions) === null || _a === void 0 ? void 0 : _a.map((action, i) => {
let fired = false, click = (e) => {
e.preventDefault();
if (fired)
@ -324,16 +321,16 @@ function renderDiagnostic(view, diagnostic, inPanel) {
};
let { name } = action, keyIndex = keys[i] ? name.indexOf(keys[i]) : -1;
let nameElt = keyIndex < 0 ? name : [name.slice(0, keyIndex),
elt__default["default"]("u", name.slice(keyIndex, keyIndex + 1)),
elt("u", name.slice(keyIndex, keyIndex + 1)),
name.slice(keyIndex + 1)];
return elt__default["default"]("button", {
return elt("button", {
type: "button",
class: "cm-diagnosticAction",
onclick: click,
onmousedown: click,
"aria-label": ` Action: ${name}${keyIndex < 0 ? "" : ` (access key "${keys[i]})"`}.`
}, nameElt);
}), diagnostic.source && elt__default["default"]("div", { class: "cm-diagnosticSource" }, diagnostic.source));
}), diagnostic.source && elt("div", { class: "cm-diagnosticSource" }, diagnostic.source));
}
class DiagnosticWidget extends view.WidgetType {
constructor(diagnostic) {
@ -342,7 +339,7 @@ class DiagnosticWidget extends view.WidgetType {
}
eq(other) { return other.diagnostic == this.diagnostic; }
toDOM() {
return elt__default["default"]("span", { class: "cm-lintPoint cm-lintPoint-" + this.diagnostic.severity });
return elt("span", { class: "cm-lintPoint cm-lintPoint-" + this.diagnostic.severity });
}
}
class PanelItem {
@ -398,14 +395,14 @@ class LintPanel {
this.moveSelection(i);
}
};
this.list = elt__default["default"]("ul", {
this.list = elt("ul", {
tabIndex: 0,
role: "listbox",
"aria-label": this.view.state.phrase("Diagnostics"),
onkeydown,
onclick
});
this.dom = elt__default["default"]("div", { class: "cm-panel-lint" }, this.list, elt__default["default"]("button", {
this.dom = elt("div", { class: "cm-panel-lint" }, this.list, elt("button", {
type: "button",
name: "close",
"aria-label": this.view.state.phrase("close"),
@ -473,10 +470,11 @@ class LintPanel {
key: this,
read: () => ({ sel: newSelectedItem.dom.getBoundingClientRect(), panel: this.list.getBoundingClientRect() }),
write: ({ sel, panel }) => {
let scaleY = panel.height / this.list.offsetHeight;
if (sel.top < panel.top)
this.list.scrollTop -= panel.top - sel.top;
this.list.scrollTop -= (panel.top - sel.top) / scaleY;
else if (sel.bottom > panel.bottom)
this.list.scrollTop += sel.bottom - panel.bottom;
this.list.scrollTop += (sel.bottom - panel.bottom) / scaleY;
}
});
}
@ -633,7 +631,7 @@ class LintGutterMarker extends view.GutterMarker {
let diagnostics = this.diagnostics;
let diagnosticsFilter = view.state.facet(lintGutterConfig).tooltipFilter;
if (diagnosticsFilter)
diagnostics = diagnosticsFilter(diagnostics);
diagnostics = diagnosticsFilter(diagnostics, view.state);
if (diagnostics.length)
elt.onmouseover = () => gutterMarkerMouseOver(view, elt, diagnostics);
return elt;
@ -642,8 +640,8 @@ class LintGutterMarker extends view.GutterMarker {
function trackHoverOn(view, marker) {
let mousemove = (event) => {
let rect = marker.getBoundingClientRect();
if (event.clientX > rect.left - 10 /* Margin */ && event.clientX < rect.right + 10 /* Margin */ &&
event.clientY > rect.top - 10 /* Margin */ && event.clientY < rect.bottom + 10 /* Margin */)
if (event.clientX > rect.left - 10 /* Hover.Margin */ && event.clientX < rect.right + 10 /* Hover.Margin */ &&
event.clientY > rect.top - 10 /* Hover.Margin */ && event.clientY < rect.bottom + 10 /* Hover.Margin */)
return;
for (let target = event.target; target; target = target.parentNode) {
if (target.nodeType == 1 && target.classList.contains("cm-tooltip-lint"))
@ -712,7 +710,7 @@ const lintGutterMarkers = state.StateField.define({
if (effect.is(setDiagnosticsEffect)) {
let diagnostics = effect.value;
if (diagnosticFilter)
diagnostics = diagnosticFilter(diagnostics || []);
diagnostics = diagnosticFilter(diagnostics || [], tr.state);
markers = markersForDiagnostics(tr.state.doc, diagnostics.slice(0));
}
}
@ -764,7 +762,7 @@ const lintExtensions = [
const lintGutterConfig = state.Facet.define({
combine(configs) {
return state.combineConfig(configs, {
hoverTime: 300 /* Time */,
hoverTime: 300 /* Hover.Time */,
markerFilter: null,
tooltipFilter: null
});

View file

@ -2,7 +2,7 @@ import * as _codemirror_state from '@codemirror/state';
import { EditorState, TransactionSpec, Extension } from '@codemirror/state';
import { EditorView, Command, KeyBinding, ViewUpdate } from '@codemirror/view';
declare type Severity = "hint" | "info" | "warning" | "error";
type Severity = "hint" | "info" | "warning" | "error";
/**
Describes a problem or hint for a piece of code.
*/
@ -62,7 +62,7 @@ interface Action {
*/
apply: (view: EditorView, from: number, to: number) => void;
}
declare type DiagnosticFilter = (diagnostics: readonly Diagnostic[]) => Diagnostic[];
type DiagnosticFilter = (diagnostics: readonly Diagnostic[], state: EditorState) => Diagnostic[];
interface LintConfig {
/**
Time to wait (in milliseconds) after a change before running
@ -143,13 +143,14 @@ declare const lintKeymap: readonly KeyBinding[];
/**
The type of a function that produces diagnostics.
*/
declare type LintSource = (view: EditorView) => readonly Diagnostic[] | Promise<readonly Diagnostic[]>;
type LintSource = (view: EditorView) => readonly Diagnostic[] | Promise<readonly Diagnostic[]>;
/**
Given a diagnostic source, this function returns an extension that
enables linting with that source. It will be called whenever the
editor is idle (after its content changed).
editor is idle (after its content changed). If `null` is given as
source, this only configures the lint extension.
*/
declare function linter(source: LintSource, config?: LintConfig): Extension;
declare function linter(source: LintSource | null, config?: LintConfig): Extension;
/**
Forces any linters [configured](https://codemirror.net/6/docs/ref/#lint.linter) to run when the
editor is idle to run right away.
@ -170,4 +171,4 @@ arguments hold the diagnostic's current position.
*/
declare function forEachDiagnostic(state: EditorState, f: (d: Diagnostic, from: number, to: number) => void): void;
export { Action, Diagnostic, LintSource, closeLintPanel, diagnosticCount, forEachDiagnostic, forceLinting, lintGutter, lintKeymap, linter, nextDiagnostic, openLintPanel, previousDiagnostic, setDiagnostics, setDiagnosticsEffect };
export { type Action, type Diagnostic, type LintSource, closeLintPanel, diagnosticCount, forEachDiagnostic, forceLinting, lintGutter, lintKeymap, linter, nextDiagnostic, openLintPanel, previousDiagnostic, setDiagnostics, setDiagnosticsEffect };

View file

@ -2,7 +2,7 @@ import * as _codemirror_state from '@codemirror/state';
import { EditorState, TransactionSpec, Extension } from '@codemirror/state';
import { EditorView, Command, KeyBinding, ViewUpdate } from '@codemirror/view';
declare type Severity = "hint" | "info" | "warning" | "error";
type Severity = "hint" | "info" | "warning" | "error";
/**
Describes a problem or hint for a piece of code.
*/
@ -62,7 +62,7 @@ interface Action {
*/
apply: (view: EditorView, from: number, to: number) => void;
}
declare type DiagnosticFilter = (diagnostics: readonly Diagnostic[]) => Diagnostic[];
type DiagnosticFilter = (diagnostics: readonly Diagnostic[], state: EditorState) => Diagnostic[];
interface LintConfig {
/**
Time to wait (in milliseconds) after a change before running
@ -143,13 +143,14 @@ declare const lintKeymap: readonly KeyBinding[];
/**
The type of a function that produces diagnostics.
*/
declare type LintSource = (view: EditorView) => readonly Diagnostic[] | Promise<readonly Diagnostic[]>;
type LintSource = (view: EditorView) => readonly Diagnostic[] | Promise<readonly Diagnostic[]>;
/**
Given a diagnostic source, this function returns an extension that
enables linting with that source. It will be called whenever the
editor is idle (after its content changed).
editor is idle (after its content changed). If `null` is given as
source, this only configures the lint extension.
*/
declare function linter(source: LintSource, config?: LintConfig): Extension;
declare function linter(source: LintSource | null, config?: LintConfig): Extension;
/**
Forces any linters [configured](https://codemirror.net/6/docs/ref/#lint.linter) to run when the
editor is idle to run right away.
@ -170,4 +171,4 @@ arguments hold the diagnostic's current position.
*/
declare function forEachDiagnostic(state: EditorState, f: (d: Diagnostic, from: number, to: number) => void): void;
export { Action, Diagnostic, LintSource, closeLintPanel, diagnosticCount, forEachDiagnostic, forceLinting, lintGutter, lintKeymap, linter, nextDiagnostic, openLintPanel, previousDiagnostic, setDiagnostics, setDiagnosticsEffect };
export { type Action, type Diagnostic, type LintSource, closeLintPanel, diagnosticCount, forEachDiagnostic, forceLinting, lintGutter, lintKeymap, linter, nextDiagnostic, openLintPanel, previousDiagnostic, setDiagnostics, setDiagnosticsEffect };

View file

@ -20,7 +20,7 @@ class LintState {
let markedDiagnostics = diagnostics;
let diagnosticFilter = state.facet(lintConfig).markerFilter;
if (diagnosticFilter)
markedDiagnostics = diagnosticFilter(markedDiagnostics);
markedDiagnostics = diagnosticFilter(markedDiagnostics, state);
let ranges = Decoration.set(markedDiagnostics.map((d) => {
// For zero-length ranges or ranges covering only a line break, create a widget
return d.from == d.to || (d.from == d.to - 1 && state.doc.lineAt(d.from).to == d.from)
@ -30,7 +30,8 @@ class LintState {
}).range(d.from)
: Decoration.mark({
attributes: { class: "cm-lintRange cm-lintRange-" + d.severity + (d.markClass ? " " + d.markClass : "") },
diagnostic: d
diagnostic: d,
inclusive: true
}).range(d.from, d.to);
}), true);
return new LintState(ranges, panel, findDiagnostic(ranges));
@ -106,7 +107,7 @@ function diagnosticCount(state) {
let lint = state.field(lintState, false);
return lint ? lint.diagnostics.size : 0;
}
const activeMark = /*@__PURE__*/Decoration.mark({ class: "cm-lintRange cm-lintRange-active" });
const activeMark = /*@__PURE__*/Decoration.mark({ class: "cm-lintRange cm-lintRange-active", inclusive: true });
function lintTooltip(view, pos, side) {
let { diagnostics } = view.state.field(lintState);
let found = [], stackStart = 2e8, stackEnd = 0;
@ -120,7 +121,7 @@ function lintTooltip(view, pos, side) {
});
let diagnosticFilter = view.state.facet(lintConfig).tooltipFilter;
if (diagnosticFilter)
found = diagnosticFilter(found);
found = diagnosticFilter(found, view.state);
if (!found.length)
return null;
return {
@ -220,16 +221,17 @@ const lintPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
run() {
let now = Date.now();
if (now < this.lintTime - 10) {
setTimeout(this.run, this.lintTime - now);
this.timeout = setTimeout(this.run, this.lintTime - now);
}
else {
this.set = false;
let { state } = this.view, { sources } = state.facet(lintConfig);
Promise.all(sources.map(source => Promise.resolve(source(this.view)))).then(annotations => {
let all = annotations.reduce((a, b) => a.concat(b));
if (this.view.state.doc == state.doc)
this.view.dispatch(setDiagnostics(this.view.state, all));
}, error => { logException(this.view.state, error); });
if (sources.length)
Promise.all(sources.map(source => Promise.resolve(source(this.view)))).then(annotations => {
let all = annotations.reduce((a, b) => a.concat(b));
if (this.view.state.doc == state.doc)
this.view.dispatch(setDiagnostics(this.view.state, all));
}, error => { logException(this.view.state, error); });
}
}
update(update) {
@ -255,7 +257,7 @@ const lintPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
});
const lintConfig = /*@__PURE__*/Facet.define({
combine(input) {
return Object.assign({ sources: input.map(i => i.source) }, combineConfig(input.map(i => i.config), {
return Object.assign({ sources: input.map(i => i.source).filter(x => x != null) }, combineConfig(input.map(i => i.config), {
delay: 750,
markerFilter: null,
tooltipFilter: null,
@ -268,7 +270,8 @@ const lintConfig = /*@__PURE__*/Facet.define({
/**
Given a diagnostic source, this function returns an extension that
enables linting with that source. It will be called whenever the
editor is idle (after its content changed).
editor is idle (after its content changed). If `null` is given as
source, this only configures the lint extension.
*/
function linter(source, config = {}) {
return [
@ -465,10 +468,11 @@ class LintPanel {
key: this,
read: () => ({ sel: newSelectedItem.dom.getBoundingClientRect(), panel: this.list.getBoundingClientRect() }),
write: ({ sel, panel }) => {
let scaleY = panel.height / this.list.offsetHeight;
if (sel.top < panel.top)
this.list.scrollTop -= panel.top - sel.top;
this.list.scrollTop -= (panel.top - sel.top) / scaleY;
else if (sel.bottom > panel.bottom)
this.list.scrollTop += sel.bottom - panel.bottom;
this.list.scrollTop += (sel.bottom - panel.bottom) / scaleY;
}
});
}
@ -625,7 +629,7 @@ class LintGutterMarker extends GutterMarker {
let diagnostics = this.diagnostics;
let diagnosticsFilter = view.state.facet(lintGutterConfig).tooltipFilter;
if (diagnosticsFilter)
diagnostics = diagnosticsFilter(diagnostics);
diagnostics = diagnosticsFilter(diagnostics, view.state);
if (diagnostics.length)
elt.onmouseover = () => gutterMarkerMouseOver(view, elt, diagnostics);
return elt;
@ -634,8 +638,8 @@ class LintGutterMarker extends GutterMarker {
function trackHoverOn(view, marker) {
let mousemove = (event) => {
let rect = marker.getBoundingClientRect();
if (event.clientX > rect.left - 10 /* Margin */ && event.clientX < rect.right + 10 /* Margin */ &&
event.clientY > rect.top - 10 /* Margin */ && event.clientY < rect.bottom + 10 /* Margin */)
if (event.clientX > rect.left - 10 /* Hover.Margin */ && event.clientX < rect.right + 10 /* Hover.Margin */ &&
event.clientY > rect.top - 10 /* Hover.Margin */ && event.clientY < rect.bottom + 10 /* Hover.Margin */)
return;
for (let target = event.target; target; target = target.parentNode) {
if (target.nodeType == 1 && target.classList.contains("cm-tooltip-lint"))
@ -704,7 +708,7 @@ const lintGutterMarkers = /*@__PURE__*/StateField.define({
if (effect.is(setDiagnosticsEffect)) {
let diagnostics = effect.value;
if (diagnosticFilter)
diagnostics = diagnosticFilter(diagnostics || []);
diagnostics = diagnosticFilter(diagnostics || [], tr.state);
markers = markersForDiagnostics(tr.state.doc, diagnostics.slice(0));
}
}
@ -756,7 +760,7 @@ const lintExtensions = [
const lintGutterConfig = /*@__PURE__*/Facet.define({
combine(configs) {
return combineConfig(configs, {
hoverTime: 300 /* Time */,
hoverTime: 300 /* Hover.Time */,
markerFilter: null,
tooltipFilter: null
});

View file

@ -1,6 +1,6 @@
{
"name": "@codemirror/lint",
"version": "6.4.0",
"version": "6.5.0",
"description": "Linting support for the CodeMirror code editor",
"scripts": {
"test": "cm-runtests",

View file

@ -1,3 +1,37 @@
## 6.5.6 (2024-02-07)
### Bug fixes
Make `highlightSelectionMatches` include whitespace in the selection in its matches.
Fix a bug that caused `SearchCursor` to return invalid ranges when matching astral chars that the the normalizer normalized to single-code-unit chars.
## 6.5.5 (2023-11-27)
### Bug fixes
Fix a bug that caused codes like `\n` to be unescaped in strings inserted via replace placeholders like `$&`.
Use the keybinding Mod-Alt-g for `gotoLine` to the search keymap, to make it usable for people whose keyboard layout uses Alt/Option-g to type some character.
## 6.5.4 (2023-09-20)
### Bug fixes
Fix a bug that caused whole-word search to incorrectly check for word boundaries in some circumstances.
## 6.5.3 (2023-09-14)
### Bug fixes
The `gotoLine` dialog is now populated with the current line number when you open it.
## 6.5.2 (2023-08-26)
### Bug fixes
Don't use the very lowest precedence for match highlighting decorations.
## 6.5.1 (2023-08-04)
### Bug fixes

View file

@ -1,15 +1,9 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var view = require('@codemirror/view');
var state = require('@codemirror/state');
var elt = require('crelt');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var elt__default = /*#__PURE__*/_interopDefaultLegacy(elt);
const basicNormalize = typeof String.prototype.normalize == "function"
? x => x.normalize("NFKD") : x => x;
/**
@ -89,7 +83,7 @@ class SearchCursor {
let norm = this.normalize(str);
for (let i = 0, pos = start;; i++) {
let code = norm.charCodeAt(i);
let match = this.match(code, pos);
let match = this.match(code, pos, this.bufferPos + this.bufferStart);
if (i == norm.length - 1) {
if (match) {
this.value = match;
@ -102,13 +96,13 @@ class SearchCursor {
}
}
}
match(code, pos) {
match(code, pos, end) {
let match = null;
for (let i = 0; i < this.matches.length; i += 2) {
let index = this.matches[i], keep = false;
if (this.query.charCodeAt(index) == code) {
if (index == this.query.length - 1) {
match = { from: this.matches[i + 1], to: pos + 1 };
match = { from: this.matches[i + 1], to: end };
}
else {
this.matches[i]++;
@ -122,11 +116,11 @@ class SearchCursor {
}
if (this.query.charCodeAt(0) == code) {
if (this.query.length == 1)
match = { from: pos, to: pos + 1 };
match = { from: pos, to: end };
else
this.matches.push(1, pos);
}
if (match && this.test && !this.test(match.from, match.to, this.buffer, this.bufferPos))
if (match && this.test && !this.test(match.from, match.to, this.buffer, this.bufferStart))
match = null;
return match;
}
@ -257,7 +251,7 @@ class MultilineRegExpCursor {
this.matchPos = toCharEnd(text, from);
this.re = new RegExp(query, baseFlags + ((options === null || options === void 0 ? void 0 : options.ignoreCase) ? "i" : ""));
this.test = options === null || options === void 0 ? void 0 : options.test;
this.flat = FlattenedDoc.get(text, from, this.chunkEnd(from + 5000 /* Base */));
this.flat = FlattenedDoc.get(text, from, this.chunkEnd(from + 5000 /* Chunk.Base */));
}
chunkEnd(pos) {
return pos >= this.to ? this.to : this.text.lineAt(pos).to;
@ -314,8 +308,9 @@ function toCharEnd(text, pos) {
}
function createLineDialog(view$1) {
let input = elt__default["default"]("input", { class: "cm-textfield", name: "line" });
let dom = elt__default["default"]("form", {
let line = String(view$1.state.doc.lineAt(view$1.state.selection.main.head).number);
let input = elt("input", { class: "cm-textfield", name: "line", value: line });
let dom = elt("form", {
class: "cm-gotoLine",
onkeydown: (event) => {
if (event.keyCode == 27) { // Escape
@ -332,7 +327,7 @@ function createLineDialog(view$1) {
event.preventDefault();
go();
}
}, elt__default["default"]("label", view$1.state.phrase("Go to line"), ": ", input), " ", elt__default["default"]("button", { class: "cm-button", type: "submit" }, view$1.state.phrase("go")));
}, elt("label", view$1.state.phrase("Go to line"), ": ", input), " ", elt("button", { class: "cm-button", type: "submit" }, view$1.state.phrase("go")));
function go() {
let match = /^([+-])?(\d+)?(:\d+)?(%)?$/.exec(input.value);
if (!match)
@ -390,7 +385,7 @@ const gotoLine = view$1 => {
panel = view.getPanel(view$1, createLineDialog);
}
if (panel)
panel.dom.querySelector("input").focus();
panel.dom.querySelector("input").select();
return true;
};
const baseTheme$1 = view.EditorView.baseTheme({
@ -469,12 +464,12 @@ const matchHighlighter = view.ViewPlugin.fromClass(class {
if (conf.wholeWords) {
query = state.sliceDoc(range.from, range.to); // TODO: allow and include leading/trailing space?
check = state.charCategorizer(range.head);
if (!(insideWordBoundaries(check, state, range.from, range.to)
&& insideWord(check, state, range.from, range.to)))
if (!(insideWordBoundaries(check, state, range.from, range.to) &&
insideWord(check, state, range.from, range.to)))
return view.Decoration.none;
}
else {
query = state.sliceDoc(range.from, range.to).trim();
query = state.sliceDoc(range.from, range.to);
if (!query)
return view.Decoration.none;
}
@ -663,7 +658,7 @@ class StringQuery extends QueryType {
// cursor, done by scanning chunk after chunk forward.
prevMatchInRange(state, from, to) {
for (let pos = to;;) {
let start = Math.max(from, pos - 10000 /* ChunkSize */ - this.spec.unquoted.length);
let start = Math.max(from, pos - 10000 /* FindPrev.ChunkSize */ - this.spec.unquoted.length);
let cursor = stringCursor(this.spec, state, start, pos), range = null;
while (!cursor.nextOverlapping().done)
range = cursor.value;
@ -671,7 +666,7 @@ class StringQuery extends QueryType {
return range;
if (start == from)
return null;
pos -= 10000 /* ChunkSize */;
pos -= 10000 /* FindPrev.ChunkSize */;
}
}
prevMatch(state, curFrom, curTo) {
@ -722,7 +717,7 @@ class RegExpQuery extends QueryType {
}
prevMatchInRange(state, from, to) {
for (let size = 1;; size++) {
let start = Math.max(from, to - size * 10000 /* ChunkSize */);
let start = Math.max(from, to - size * 10000 /* FindPrev.ChunkSize */);
let cursor = regexpCursor(this.spec, state, start, to), range = null;
while (!cursor.next().done)
range = cursor.value;
@ -737,10 +732,10 @@ class RegExpQuery extends QueryType {
this.prevMatchInRange(state, curTo, state.doc.length);
}
getReplacement(result) {
return this.spec.unquote(this.spec.replace.replace(/\$([$&\d+])/g, (m, i) => i == "$" ? "$"
return this.spec.unquote(this.spec.replace).replace(/\$([$&\d+])/g, (m, i) => i == "$" ? "$"
: i == "&" ? result.match[0]
: i != "0" && +i < result.match.length ? result.match[i]
: m));
: m);
}
matchAll(state, limit) {
let cursor = regexpCursor(this.spec, state, 0, state.doc.length), ranges = [];
@ -752,7 +747,7 @@ class RegExpQuery extends QueryType {
return ranges;
}
highlight(state, from, to, add) {
let cursor = regexpCursor(this.spec, state, Math.max(0, from - 250 /* HighlightMargin */), Math.min(to + 250 /* HighlightMargin */, state.doc.length));
let cursor = regexpCursor(this.spec, state, Math.max(0, from - 250 /* RegExp.HighlightMargin */), Math.min(to + 250 /* RegExp.HighlightMargin */, state.doc.length));
while (!cursor.next().done)
add(cursor.value.from, cursor.value.to);
}
@ -819,7 +814,7 @@ const searchHighlighter = view.ViewPlugin.fromClass(class {
let builder = new state.RangeSetBuilder();
for (let i = 0, ranges = view$1.visibleRanges, l = ranges.length; i < l; i++) {
let { from, to } = ranges[i];
while (i < l - 1 && to > ranges[i + 1].from - 2 * 250 /* HighlightMargin */)
while (i < l - 1 && to > ranges[i + 1].from - 2 * 250 /* RegExp.HighlightMargin */)
to = ranges[++i].to;
query.highlight(view$1.state, from, to, (from, to) => {
let selected = view$1.state.selection.ranges.some(r => r.from == from && r.to == to);
@ -1033,7 +1028,7 @@ Default search-related key bindings.
- Mod-f: [`openSearchPanel`](https://codemirror.net/6/docs/ref/#search.openSearchPanel)
- F3, Mod-g: [`findNext`](https://codemirror.net/6/docs/ref/#search.findNext)
- Shift-F3, Shift-Mod-g: [`findPrevious`](https://codemirror.net/6/docs/ref/#search.findPrevious)
- Alt-g: [`gotoLine`](https://codemirror.net/6/docs/ref/#search.gotoLine)
- Mod-Alt-g: [`gotoLine`](https://codemirror.net/6/docs/ref/#search.gotoLine)
- Mod-d: [`selectNextOccurrence`](https://codemirror.net/6/docs/ref/#search.selectNextOccurrence)
*/
const searchKeymap = [
@ -1042,7 +1037,7 @@ const searchKeymap = [
{ key: "Mod-g", run: findNext, shift: findPrevious, scope: "editor search-panel", preventDefault: true },
{ key: "Escape", run: closeSearchPanel, scope: "editor search-panel" },
{ key: "Mod-Shift-l", run: selectSelectionMatches },
{ key: "Alt-g", run: gotoLine },
{ key: "Mod-Alt-g", run: gotoLine },
{ key: "Mod-d", run: selectNextOccurrence, preventDefault: true },
];
class SearchPanel {
@ -1050,7 +1045,7 @@ class SearchPanel {
this.view = view;
let query = this.query = view.state.field(searchState).query.spec;
this.commit = this.commit.bind(this);
this.searchField = elt__default["default"]("input", {
this.searchField = elt("input", {
value: query.search,
placeholder: phrase(view, "Find"),
"aria-label": phrase(view, "Find"),
@ -1061,7 +1056,7 @@ class SearchPanel {
onchange: this.commit,
onkeyup: this.commit
});
this.replaceField = elt__default["default"]("input", {
this.replaceField = elt("input", {
value: query.replace,
placeholder: phrase(view, "Replace"),
"aria-label": phrase(view, "Replace"),
@ -1071,21 +1066,21 @@ class SearchPanel {
onchange: this.commit,
onkeyup: this.commit
});
this.caseField = elt__default["default"]("input", {
this.caseField = elt("input", {
type: "checkbox",
name: "case",
form: "",
checked: query.caseSensitive,
onchange: this.commit
});
this.reField = elt__default["default"]("input", {
this.reField = elt("input", {
type: "checkbox",
name: "re",
form: "",
checked: query.regexp,
onchange: this.commit
});
this.wordField = elt__default["default"]("input", {
this.wordField = elt("input", {
type: "checkbox",
name: "word",
form: "",
@ -1093,23 +1088,23 @@ class SearchPanel {
onchange: this.commit
});
function button(name, onclick, content) {
return elt__default["default"]("button", { class: "cm-button", name, onclick, type: "button" }, content);
return elt("button", { class: "cm-button", name, onclick, type: "button" }, content);
}
this.dom = elt__default["default"]("div", { onkeydown: (e) => this.keydown(e), class: "cm-search" }, [
this.dom = elt("div", { onkeydown: (e) => this.keydown(e), class: "cm-search" }, [
this.searchField,
button("next", () => findNext(view), [phrase(view, "next")]),
button("prev", () => findPrevious(view), [phrase(view, "previous")]),
button("select", () => selectMatches(view), [phrase(view, "all")]),
elt__default["default"]("label", null, [this.caseField, phrase(view, "match case")]),
elt__default["default"]("label", null, [this.reField, phrase(view, "regexp")]),
elt__default["default"]("label", null, [this.wordField, phrase(view, "by word")]),
elt("label", null, [this.caseField, phrase(view, "match case")]),
elt("label", null, [this.reField, phrase(view, "regexp")]),
elt("label", null, [this.wordField, phrase(view, "by word")]),
...view.state.readOnly ? [] : [
elt__default["default"]("br"),
elt("br"),
this.replaceField,
button("replace", () => replaceNext(view), [phrase(view, "replace")]),
button("replaceAll", () => replaceAll(view), [phrase(view, "replace all")])
],
elt__default["default"]("button", {
elt("button", {
name: "close",
onclick: () => closeSearchPanel(view),
"aria-label": phrase(view, "close"),
@ -1219,7 +1214,7 @@ const baseTheme = view.EditorView.baseTheme({
});
const searchExtensions = [
searchState,
state.Prec.lowest(searchHighlighter),
state.Prec.low(searchHighlighter),
baseTheme
];

View file

@ -133,7 +133,7 @@ number.
*/
declare const gotoLine: Command;
declare type HighlightOptions = {
type HighlightOptions = {
/**
Determines whether, when nothing is selected, the word around
the cursor is matched instead. Defaults to false.
@ -367,7 +367,7 @@ Default search-related key bindings.
- Mod-f: [`openSearchPanel`](https://codemirror.net/6/docs/ref/#search.openSearchPanel)
- F3, Mod-g: [`findNext`](https://codemirror.net/6/docs/ref/#search.findNext)
- Shift-F3, Shift-Mod-g: [`findPrevious`](https://codemirror.net/6/docs/ref/#search.findPrevious)
- Alt-g: [`gotoLine`](https://codemirror.net/6/docs/ref/#search.gotoLine)
- Mod-Alt-g: [`gotoLine`](https://codemirror.net/6/docs/ref/#search.gotoLine)
- Mod-d: [`selectNextOccurrence`](https://codemirror.net/6/docs/ref/#search.selectNextOccurrence)
*/
declare const searchKeymap: readonly KeyBinding[];

View file

@ -133,7 +133,7 @@ number.
*/
declare const gotoLine: Command;
declare type HighlightOptions = {
type HighlightOptions = {
/**
Determines whether, when nothing is selected, the word around
the cursor is matched instead. Defaults to false.
@ -367,7 +367,7 @@ Default search-related key bindings.
- Mod-f: [`openSearchPanel`](https://codemirror.net/6/docs/ref/#search.openSearchPanel)
- F3, Mod-g: [`findNext`](https://codemirror.net/6/docs/ref/#search.findNext)
- Shift-F3, Shift-Mod-g: [`findPrevious`](https://codemirror.net/6/docs/ref/#search.findPrevious)
- Alt-g: [`gotoLine`](https://codemirror.net/6/docs/ref/#search.gotoLine)
- Mod-Alt-g: [`gotoLine`](https://codemirror.net/6/docs/ref/#search.gotoLine)
- Mod-d: [`selectNextOccurrence`](https://codemirror.net/6/docs/ref/#search.selectNextOccurrence)
*/
declare const searchKeymap: readonly KeyBinding[];

View file

@ -81,7 +81,7 @@ class SearchCursor {
let norm = this.normalize(str);
for (let i = 0, pos = start;; i++) {
let code = norm.charCodeAt(i);
let match = this.match(code, pos);
let match = this.match(code, pos, this.bufferPos + this.bufferStart);
if (i == norm.length - 1) {
if (match) {
this.value = match;
@ -94,13 +94,13 @@ class SearchCursor {
}
}
}
match(code, pos) {
match(code, pos, end) {
let match = null;
for (let i = 0; i < this.matches.length; i += 2) {
let index = this.matches[i], keep = false;
if (this.query.charCodeAt(index) == code) {
if (index == this.query.length - 1) {
match = { from: this.matches[i + 1], to: pos + 1 };
match = { from: this.matches[i + 1], to: end };
}
else {
this.matches[i]++;
@ -114,11 +114,11 @@ class SearchCursor {
}
if (this.query.charCodeAt(0) == code) {
if (this.query.length == 1)
match = { from: pos, to: pos + 1 };
match = { from: pos, to: end };
else
this.matches.push(1, pos);
}
if (match && this.test && !this.test(match.from, match.to, this.buffer, this.bufferPos))
if (match && this.test && !this.test(match.from, match.to, this.buffer, this.bufferStart))
match = null;
return match;
}
@ -249,7 +249,7 @@ class MultilineRegExpCursor {
this.matchPos = toCharEnd(text, from);
this.re = new RegExp(query, baseFlags + ((options === null || options === void 0 ? void 0 : options.ignoreCase) ? "i" : ""));
this.test = options === null || options === void 0 ? void 0 : options.test;
this.flat = FlattenedDoc.get(text, from, this.chunkEnd(from + 5000 /* Base */));
this.flat = FlattenedDoc.get(text, from, this.chunkEnd(from + 5000 /* Chunk.Base */));
}
chunkEnd(pos) {
return pos >= this.to ? this.to : this.text.lineAt(pos).to;
@ -306,7 +306,8 @@ function toCharEnd(text, pos) {
}
function createLineDialog(view) {
let input = elt("input", { class: "cm-textfield", name: "line" });
let line = String(view.state.doc.lineAt(view.state.selection.main.head).number);
let input = elt("input", { class: "cm-textfield", name: "line", value: line });
let dom = elt("form", {
class: "cm-gotoLine",
onkeydown: (event) => {
@ -382,7 +383,7 @@ const gotoLine = view => {
panel = getPanel(view, createLineDialog);
}
if (panel)
panel.dom.querySelector("input").focus();
panel.dom.querySelector("input").select();
return true;
};
const baseTheme$1 = /*@__PURE__*/EditorView.baseTheme({
@ -461,12 +462,12 @@ const matchHighlighter = /*@__PURE__*/ViewPlugin.fromClass(class {
if (conf.wholeWords) {
query = state.sliceDoc(range.from, range.to); // TODO: allow and include leading/trailing space?
check = state.charCategorizer(range.head);
if (!(insideWordBoundaries(check, state, range.from, range.to)
&& insideWord(check, state, range.from, range.to)))
if (!(insideWordBoundaries(check, state, range.from, range.to) &&
insideWord(check, state, range.from, range.to)))
return Decoration.none;
}
else {
query = state.sliceDoc(range.from, range.to).trim();
query = state.sliceDoc(range.from, range.to);
if (!query)
return Decoration.none;
}
@ -655,7 +656,7 @@ class StringQuery extends QueryType {
// cursor, done by scanning chunk after chunk forward.
prevMatchInRange(state, from, to) {
for (let pos = to;;) {
let start = Math.max(from, pos - 10000 /* ChunkSize */ - this.spec.unquoted.length);
let start = Math.max(from, pos - 10000 /* FindPrev.ChunkSize */ - this.spec.unquoted.length);
let cursor = stringCursor(this.spec, state, start, pos), range = null;
while (!cursor.nextOverlapping().done)
range = cursor.value;
@ -663,7 +664,7 @@ class StringQuery extends QueryType {
return range;
if (start == from)
return null;
pos -= 10000 /* ChunkSize */;
pos -= 10000 /* FindPrev.ChunkSize */;
}
}
prevMatch(state, curFrom, curTo) {
@ -714,7 +715,7 @@ class RegExpQuery extends QueryType {
}
prevMatchInRange(state, from, to) {
for (let size = 1;; size++) {
let start = Math.max(from, to - size * 10000 /* ChunkSize */);
let start = Math.max(from, to - size * 10000 /* FindPrev.ChunkSize */);
let cursor = regexpCursor(this.spec, state, start, to), range = null;
while (!cursor.next().done)
range = cursor.value;
@ -729,10 +730,10 @@ class RegExpQuery extends QueryType {
this.prevMatchInRange(state, curTo, state.doc.length);
}
getReplacement(result) {
return this.spec.unquote(this.spec.replace.replace(/\$([$&\d+])/g, (m, i) => i == "$" ? "$"
return this.spec.unquote(this.spec.replace).replace(/\$([$&\d+])/g, (m, i) => i == "$" ? "$"
: i == "&" ? result.match[0]
: i != "0" && +i < result.match.length ? result.match[i]
: m));
: m);
}
matchAll(state, limit) {
let cursor = regexpCursor(this.spec, state, 0, state.doc.length), ranges = [];
@ -744,7 +745,7 @@ class RegExpQuery extends QueryType {
return ranges;
}
highlight(state, from, to, add) {
let cursor = regexpCursor(this.spec, state, Math.max(0, from - 250 /* HighlightMargin */), Math.min(to + 250 /* HighlightMargin */, state.doc.length));
let cursor = regexpCursor(this.spec, state, Math.max(0, from - 250 /* RegExp.HighlightMargin */), Math.min(to + 250 /* RegExp.HighlightMargin */, state.doc.length));
while (!cursor.next().done)
add(cursor.value.from, cursor.value.to);
}
@ -811,7 +812,7 @@ const searchHighlighter = /*@__PURE__*/ViewPlugin.fromClass(class {
let builder = new RangeSetBuilder();
for (let i = 0, ranges = view.visibleRanges, l = ranges.length; i < l; i++) {
let { from, to } = ranges[i];
while (i < l - 1 && to > ranges[i + 1].from - 2 * 250 /* HighlightMargin */)
while (i < l - 1 && to > ranges[i + 1].from - 2 * 250 /* RegExp.HighlightMargin */)
to = ranges[++i].to;
query.highlight(view.state, from, to, (from, to) => {
let selected = view.state.selection.ranges.some(r => r.from == from && r.to == to);
@ -1025,7 +1026,7 @@ Default search-related key bindings.
- Mod-f: [`openSearchPanel`](https://codemirror.net/6/docs/ref/#search.openSearchPanel)
- F3, Mod-g: [`findNext`](https://codemirror.net/6/docs/ref/#search.findNext)
- Shift-F3, Shift-Mod-g: [`findPrevious`](https://codemirror.net/6/docs/ref/#search.findPrevious)
- Alt-g: [`gotoLine`](https://codemirror.net/6/docs/ref/#search.gotoLine)
- Mod-Alt-g: [`gotoLine`](https://codemirror.net/6/docs/ref/#search.gotoLine)
- Mod-d: [`selectNextOccurrence`](https://codemirror.net/6/docs/ref/#search.selectNextOccurrence)
*/
const searchKeymap = [
@ -1034,7 +1035,7 @@ const searchKeymap = [
{ key: "Mod-g", run: findNext, shift: findPrevious, scope: "editor search-panel", preventDefault: true },
{ key: "Escape", run: closeSearchPanel, scope: "editor search-panel" },
{ key: "Mod-Shift-l", run: selectSelectionMatches },
{ key: "Alt-g", run: gotoLine },
{ key: "Mod-Alt-g", run: gotoLine },
{ key: "Mod-d", run: selectNextOccurrence, preventDefault: true },
];
class SearchPanel {
@ -1211,7 +1212,7 @@ const baseTheme = /*@__PURE__*/EditorView.baseTheme({
});
const searchExtensions = [
searchState,
/*@__PURE__*/Prec.lowest(searchHighlighter),
/*@__PURE__*/Prec.low(searchHighlighter),
baseTheme
];

View file

@ -1,6 +1,6 @@
{
"name": "@codemirror/search",
"version": "6.5.1",
"version": "6.5.6",
"description": "Search functionality for the CodeMirror code editor",
"scripts": {
"test": "cm-runtests",

View file

@ -1,3 +1,47 @@
## 6.4.1 (2024-02-19)
### Bug fixes
Fix an issue that caused widgets at the end of a mark decoration to be rendered in their own separate mark DOM element.
## 6.4.0 (2023-12-28)
### Bug fixes
When multiple ranges in a single range set overlap, put the smaller ones inside the bigger ones, so that overlapping decorations don't break up each other's elements when coming from the same source.
### New features
Selection and selection range `eq` methods now support an optional argument that makes them also compare by cursor associativity.
The `RangeSet.join` function can be used to join multiple range sets together.
## 6.3.3 (2023-12-06)
### Bug fixes
Fix an issue where `Text.slice` and `Text.replace` could return objects with incorrect `length` when the given `from`/`to` values were out of range for the text.
## 6.3.2 (2023-11-27)
### Bug fixes
Make sure transactions cannot add multiple selections when `allowMultipleSelections` is false.
Fix a bug that caused `Text.iterLines` to not return empty lines at the end of the iterated ranges.
## 6.3.1 (2023-10-18)
### Bug fixes
Give the tag property on `FacetReader` the type of the output type parameter to force TypeScript to infer the proper type when converting from `Facet` to `FacetReader`.
## 6.3.0 (2023-10-12)
### New features
The new `FacetReader` type provides a way to export a read-only handle to a `Facet`.
## 6.2.1 (2023-05-23)
### Bug fixes

View file

@ -1,7 +1,5 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
/**
The data structure for documents. @nonabstract
*/
@ -26,6 +24,7 @@ class Text {
Replace a range of the text with the given content.
*/
replace(from, to, text) {
[from, to] = clip(this, from, to);
let parts = [];
this.decompose(0, from, parts, 2 /* Open.To */);
if (text.length)
@ -43,6 +42,7 @@ class Text {
Retrieve the text between the given points.
*/
slice(from, to = this.length) {
[from, to] = clip(this, from, to);
let parts = [];
this.decompose(from, to, parts, 0);
return TextNode.from(parts, to - from);
@ -169,6 +169,7 @@ class TextLeaf extends Text {
replace(from, to, text) {
if (!(text instanceof TextLeaf))
return super.replace(from, to, text);
[from, to] = clip(this, from, to);
let lines = appendText(this.text, appendText(text.text, sliceText(this.text, 0, from)), to);
let newLen = this.length + text.length - (to - from);
if (lines.length <= 32 /* Tree.Branch */)
@ -176,6 +177,7 @@ class TextLeaf extends Text {
return TextNode.from(TextLeaf.split(lines, []), newLen);
}
sliceString(from, to = this.length, lineSep = "\n") {
[from, to] = clip(this, from, to);
let result = "";
for (let pos = 0, i = 0; pos <= to && i < this.text.length; i++) {
let line = this.text[i], end = pos + line.length;
@ -244,6 +246,7 @@ class TextNode extends Text {
}
}
replace(from, to, text) {
[from, to] = clip(this, from, to);
if (text.lines < this.lines)
for (let i = 0, pos = 0; i < this.children.length; i++) {
let child = this.children[i], end = pos + child.length;
@ -266,6 +269,7 @@ class TextNode extends Text {
return super.replace(from, to, text);
}
sliceString(from, to = this.length, lineSep = "\n") {
[from, to] = clip(this, from, to);
let result = "";
for (let i = 0, pos = 0; i < this.children.length && pos <= to; i++) {
let child = this.children[i], end = pos + child.length;
@ -487,7 +491,11 @@ class LineCursor {
}
next(skip = 0) {
let { done, lineBreak, value } = this.inner.next(skip);
if (done) {
if (done && this.afterBreak) {
this.value = "";
this.afterBreak = false;
}
else if (done) {
this.done = true;
this.value = "";
}
@ -549,6 +557,10 @@ class Line {
*/
get length() { return this.to - this.from; }
}
function clip(text, from, to) {
from = Math.max(0, Math.min(text.length, from));
return [from, Math.max(from, Math.min(text.length, to))];
}
// Compressed representation of the Grapheme_Cluster_Break=Extend
// information from
@ -1317,12 +1329,12 @@ class SelectionRange {
The anchor of the rangethe side that doesn't move when you
extend it.
*/
get anchor() { return this.flags & 16 /* RangeFlag.Inverted */ ? this.to : this.from; }
get anchor() { return this.flags & 32 /* RangeFlag.Inverted */ ? this.to : this.from; }
/**
The head of the range, which is moved when the range is
[extended](https://codemirror.net/6/docs/ref/#state.SelectionRange.extend).
*/
get head() { return this.flags & 16 /* RangeFlag.Inverted */ ? this.from : this.to; }
get head() { return this.flags & 32 /* RangeFlag.Inverted */ ? this.from : this.to; }
/**
True when `anchor` and `head` are at the same position.
*/
@ -1333,14 +1345,14 @@ class SelectionRange {
the character before its position, 1 the character after, and 0
means no association.
*/
get assoc() { return this.flags & 4 /* RangeFlag.AssocBefore */ ? -1 : this.flags & 8 /* RangeFlag.AssocAfter */ ? 1 : 0; }
get assoc() { return this.flags & 8 /* RangeFlag.AssocBefore */ ? -1 : this.flags & 16 /* RangeFlag.AssocAfter */ ? 1 : 0; }
/**
The bidirectional text level associated with this cursor, if
any.
*/
get bidiLevel() {
let level = this.flags & 3 /* RangeFlag.BidiLevelMask */;
return level == 3 ? null : level;
let level = this.flags & 7 /* RangeFlag.BidiLevelMask */;
return level == 7 ? null : level;
}
/**
The goal column (stored vertical offset) associated with a
@ -1349,8 +1361,8 @@ class SelectionRange {
lines of different length.
*/
get goalColumn() {
let value = this.flags >> 5 /* RangeFlag.GoalColumnOffset */;
return value == 33554431 /* RangeFlag.NoGoalColumn */ ? undefined : value;
let value = this.flags >> 6 /* RangeFlag.GoalColumnOffset */;
return value == 16777215 /* RangeFlag.NoGoalColumn */ ? undefined : value;
}
/**
Map this range through a change, producing a valid range in the
@ -1379,8 +1391,9 @@ class SelectionRange {
/**
Compare this range to another range.
*/
eq(other) {
return this.anchor == other.anchor && this.head == other.head;
eq(other, includeAssoc = false) {
return this.anchor == other.anchor && this.head == other.head &&
(!includeAssoc || !this.empty || this.assoc == other.assoc);
}
/**
Return a JSON-serializable object representing the range.
@ -1430,14 +1443,17 @@ class EditorSelection {
return EditorSelection.create(this.ranges.map(r => r.map(change, assoc)), this.mainIndex);
}
/**
Compare this selection to another selection.
Compare this selection to another selection. By default, ranges
are compared only by position. When `includeAssoc` is true,
cursor ranges must also have the same
[`assoc`](https://codemirror.net/6/docs/ref/#state.SelectionRange.assoc) value.
*/
eq(other) {
eq(other, includeAssoc = false) {
if (this.ranges.length != other.ranges.length ||
this.mainIndex != other.mainIndex)
return false;
for (let i = 0; i < this.ranges.length; i++)
if (!this.ranges[i].eq(other.ranges[i]))
if (!this.ranges[i].eq(other.ranges[i], includeAssoc))
return false;
return true;
}
@ -1510,18 +1526,18 @@ class EditorSelection {
safely ignore the optional arguments in most situations.
*/
static cursor(pos, assoc = 0, bidiLevel, goalColumn) {
return SelectionRange.create(pos, pos, (assoc == 0 ? 0 : assoc < 0 ? 4 /* RangeFlag.AssocBefore */ : 8 /* RangeFlag.AssocAfter */) |
(bidiLevel == null ? 3 : Math.min(2, bidiLevel)) |
((goalColumn !== null && goalColumn !== void 0 ? goalColumn : 33554431 /* RangeFlag.NoGoalColumn */) << 5 /* RangeFlag.GoalColumnOffset */));
return SelectionRange.create(pos, pos, (assoc == 0 ? 0 : assoc < 0 ? 8 /* RangeFlag.AssocBefore */ : 16 /* RangeFlag.AssocAfter */) |
(bidiLevel == null ? 7 : Math.min(6, bidiLevel)) |
((goalColumn !== null && goalColumn !== void 0 ? goalColumn : 16777215 /* RangeFlag.NoGoalColumn */) << 6 /* RangeFlag.GoalColumnOffset */));
}
/**
Create a selection range.
*/
static range(anchor, head, goalColumn, bidiLevel) {
let flags = ((goalColumn !== null && goalColumn !== void 0 ? goalColumn : 33554431 /* RangeFlag.NoGoalColumn */) << 5 /* RangeFlag.GoalColumnOffset */) |
(bidiLevel == null ? 3 : Math.min(2, bidiLevel));
return head < anchor ? SelectionRange.create(head, anchor, 16 /* RangeFlag.Inverted */ | 8 /* RangeFlag.AssocAfter */ | flags)
: SelectionRange.create(anchor, head, (head > anchor ? 4 /* RangeFlag.AssocBefore */ : 0) | flags);
let flags = ((goalColumn !== null && goalColumn !== void 0 ? goalColumn : 16777215 /* RangeFlag.NoGoalColumn */) << 6 /* RangeFlag.GoalColumnOffset */) |
(bidiLevel == null ? 7 : Math.min(6, bidiLevel));
return head < anchor ? SelectionRange.create(head, anchor, 32 /* RangeFlag.Inverted */ | 16 /* RangeFlag.AssocAfter */ | flags)
: SelectionRange.create(anchor, head, (head > anchor ? 8 /* RangeFlag.AssocBefore */ : 0) | flags);
}
/**
@internal
@ -1558,6 +1574,9 @@ Examples of uses of facets are the [tab
size](https://codemirror.net/6/docs/ref/#state.EditorState^tabSize), [editor
attributes](https://codemirror.net/6/docs/ref/#view.EditorView^editorAttributes), and [update
listeners](https://codemirror.net/6/docs/ref/#view.EditorView^updateListener).
Note that `Facet` instances can be used anywhere where
[`FacetReader`](https://codemirror.net/6/docs/ref/#state.FacetReader) is expected.
*/
class Facet {
constructor(
@ -1585,6 +1604,11 @@ class Facet {
this.extensions = typeof enables == "function" ? enables(this) : enables;
}
/**
Returns a facet reader for this facet, which can be used to
[read](https://codemirror.net/6/docs/ref/#state.EditorState.facet) it but not to define values for it.
*/
get reader() { return this; }
/**
Define a new facet.
*/
static define(config = {}) {
@ -2637,7 +2661,8 @@ class EditorState {
else {
startValues = tr.startState.values.slice();
}
new EditorState(conf, tr.newDoc, tr.newSelection, startValues, (state, slot) => slot.update(state, tr), tr);
let selection = tr.startState.facet(allowMultipleSelections) ? tr.newSelection : tr.newSelection.asSingle();
new EditorState(conf, tr.newDoc, selection, startValues, (state, slot) => slot.update(state, tr), tr);
}
/**
Create a [transaction spec](https://codemirror.net/6/docs/ref/#state.TransactionSpec) that
@ -3371,7 +3396,9 @@ class RangeSet {
let curTo = Math.min(cursor.to, to);
if (cursor.point) {
let active = cursor.activeForPoint(cursor.to);
let openCount = cursor.pointFrom < from ? active.length + 1 : Math.min(active.length, openRanges);
let openCount = cursor.pointFrom < from ? active.length + 1
: cursor.point.startSide < 0 ? active.length
: Math.min(active.length, openRanges);
iterator.point(pos, curTo, cursor.point, active, openCount, cursor.pointRank);
openRanges = Math.min(cursor.openEnd(curTo), active.length);
}
@ -3398,6 +3425,19 @@ class RangeSet {
build.add(range.from, range.to, range.value);
return build.finish();
}
/**
Join an array of range sets into a single set.
*/
static join(sets) {
if (!sets.length)
return RangeSet.empty;
let result = sets[sets.length - 1];
for (let i = sets.length - 2; i >= 0; i--) {
for (let layer = sets[i]; layer != RangeSet.empty; layer = layer.nextLayer)
result = new RangeSet(layer.chunkPos, layer.chunk, result, Math.max(layer.maxPoint, result.maxPoint));
}
return result;
}
}
/**
The empty set of ranges.
@ -3715,7 +3755,8 @@ class SpanCursor {
}
addActive(trackOpen) {
let i = 0, { value, to, rank } = this.cursor;
while (i < this.activeRank.length && this.activeRank[i] <= rank)
// Organize active marks by rank first, then by size
while (i < this.activeRank.length && (rank - this.activeRank[i] || to - this.activeTo[i]) > 0)
i++;
insert(this.active, i, value);
insert(this.activeTo, i, to);

1693
node_modules/@codemirror/state/dist/index.d.cts generated vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -400,7 +400,7 @@ declare class SelectionRange {
/**
Compare this range to another range.
*/
eq(other: SelectionRange): boolean;
eq(other: SelectionRange, includeAssoc?: boolean): boolean;
/**
Return a JSON-serializable object representing the range.
*/
@ -432,9 +432,12 @@ declare class EditorSelection {
*/
map(change: ChangeDesc, assoc?: number): EditorSelection;
/**
Compare this selection to another selection.
Compare this selection to another selection. By default, ranges
are compared only by position. When `includeAssoc` is true,
cursor ranges must also have the same
[`assoc`](https://codemirror.net/6/docs/ref/#state.SelectionRange.assoc) value.
*/
eq(other: EditorSelection): boolean;
eq(other: EditorSelection, includeAssoc?: boolean): boolean;
/**
Get the primary selection range. Usually, you should make sure
your code applies to _all_ ranges, by using methods like
@ -528,11 +531,19 @@ Examples of uses of facets are the [tab
size](https://codemirror.net/6/docs/ref/#state.EditorState^tabSize), [editor
attributes](https://codemirror.net/6/docs/ref/#view.EditorView^editorAttributes), and [update
listeners](https://codemirror.net/6/docs/ref/#view.EditorView^updateListener).
Note that `Facet` instances can be used anywhere where
[`FacetReader`](https://codemirror.net/6/docs/ref/#state.FacetReader) is expected.
*/
declare class Facet<Input, Output = readonly Input[]> {
declare class Facet<Input, Output = readonly Input[]> implements FacetReader<Output> {
private isStatic;
private constructor();
/**
Returns a facet reader for this facet, which can be used to
[read](https://codemirror.net/6/docs/ref/#state.EditorState.facet) it but not to define values for it.
*/
get reader(): FacetReader<Output>;
/**
Define a new facet.
*/
static define<Input, Output = readonly Input[]>(config?: FacetConfig<Input, Output>): Facet<Input, Output>;
@ -563,8 +574,23 @@ declare class Facet<Input, Output = readonly Input[]> {
*/
from<T extends Input>(field: StateField<T>): Extension;
from<T>(field: StateField<T>, get: (value: T) => Input): Extension;
tag: Output;
}
type Slot<T> = Facet<any, T> | StateField<T> | "doc" | "selection";
/**
A facet reader can be used to fetch the value of a facet, through
[`EditorState.facet`](https://codemirror.net/6/docs/ref/#state.EditorState.facet) or as a dependency
in [`Facet.compute`](https://codemirror.net/6/docs/ref/#state.Facet.compute), but not to define new
values for the facet.
*/
type FacetReader<Output> = {
/**
Dummy tag that makes sure TypeScript doesn't consider all object
types as conforming to this type. Not actually present on the
object.
*/
tag: Output;
};
type Slot<T> = FacetReader<T> | StateField<T> | "doc" | "selection";
type StateFieldSpec<Value> = {
/**
Creates the initial value for the field when a state is created.
@ -825,7 +851,7 @@ interface TransactionSpec {
selection?: EditorSelection | {
anchor: number;
head?: number;
};
} | undefined;
/**
Attach [state effects](https://codemirror.net/6/docs/ref/#state.StateEffect) to this transaction.
Again, when they contain positions and this same spec makes
@ -1123,7 +1149,7 @@ declare class EditorState {
/**
Get the value of a state [facet](https://codemirror.net/6/docs/ref/#state.Facet).
*/
facet<Output>(facet: Facet<any, Output>): Output;
facet<Output>(facet: FacetReader<Output>): Output;
/**
Convert this state to a JSON-serializable object. When custom
fields should be serialized, you can pass them in as an object
@ -1581,6 +1607,10 @@ declare class RangeSet<T extends RangeValue> {
*/
static of<T extends RangeValue>(ranges: readonly Range<T>[] | Range<T>, sort?: boolean): RangeSet<T>;
/**
Join an array of range sets into a single set.
*/
static join<T extends RangeValue>(sets: readonly RangeSet<T>[]): RangeSet<T>;
/**
The empty set of ranges.
*/
static empty: RangeSet<any>;
@ -1660,4 +1690,4 @@ situation.
*/
declare function findColumn(string: string, col: number, tabSize: number, strict?: boolean): number;
export { Annotation, AnnotationType, ChangeDesc, ChangeSet, ChangeSpec, CharCategory, Compartment, EditorSelection, EditorState, EditorStateConfig, Extension, Facet, Line, MapMode, Prec, Range, RangeComparator, RangeCursor, RangeSet, RangeSetBuilder, RangeValue, SelectionRange, SpanIterator, StateCommand, StateEffect, StateEffectType, StateField, Text, TextIterator, Transaction, TransactionSpec, codePointAt, codePointSize, combineConfig, countColumn, findClusterBreak, findColumn, fromCodePoint };
export { Annotation, AnnotationType, ChangeDesc, ChangeSet, type ChangeSpec, CharCategory, Compartment, EditorSelection, EditorState, type EditorStateConfig, type Extension, Facet, type FacetReader, Line, MapMode, Prec, Range, type RangeComparator, type RangeCursor, RangeSet, RangeSetBuilder, RangeValue, SelectionRange, type SpanIterator, type StateCommand, StateEffect, StateEffectType, StateField, Text, type TextIterator, Transaction, type TransactionSpec, codePointAt, codePointSize, combineConfig, countColumn, findClusterBreak, findColumn, fromCodePoint };

View file

@ -22,6 +22,7 @@ class Text {
Replace a range of the text with the given content.
*/
replace(from, to, text) {
[from, to] = clip(this, from, to);
let parts = [];
this.decompose(0, from, parts, 2 /* Open.To */);
if (text.length)
@ -39,6 +40,7 @@ class Text {
Retrieve the text between the given points.
*/
slice(from, to = this.length) {
[from, to] = clip(this, from, to);
let parts = [];
this.decompose(from, to, parts, 0);
return TextNode.from(parts, to - from);
@ -165,6 +167,7 @@ class TextLeaf extends Text {
replace(from, to, text) {
if (!(text instanceof TextLeaf))
return super.replace(from, to, text);
[from, to] = clip(this, from, to);
let lines = appendText(this.text, appendText(text.text, sliceText(this.text, 0, from)), to);
let newLen = this.length + text.length - (to - from);
if (lines.length <= 32 /* Tree.Branch */)
@ -172,6 +175,7 @@ class TextLeaf extends Text {
return TextNode.from(TextLeaf.split(lines, []), newLen);
}
sliceString(from, to = this.length, lineSep = "\n") {
[from, to] = clip(this, from, to);
let result = "";
for (let pos = 0, i = 0; pos <= to && i < this.text.length; i++) {
let line = this.text[i], end = pos + line.length;
@ -240,6 +244,7 @@ class TextNode extends Text {
}
}
replace(from, to, text) {
[from, to] = clip(this, from, to);
if (text.lines < this.lines)
for (let i = 0, pos = 0; i < this.children.length; i++) {
let child = this.children[i], end = pos + child.length;
@ -262,6 +267,7 @@ class TextNode extends Text {
return super.replace(from, to, text);
}
sliceString(from, to = this.length, lineSep = "\n") {
[from, to] = clip(this, from, to);
let result = "";
for (let i = 0, pos = 0; i < this.children.length && pos <= to; i++) {
let child = this.children[i], end = pos + child.length;
@ -483,7 +489,11 @@ class LineCursor {
}
next(skip = 0) {
let { done, lineBreak, value } = this.inner.next(skip);
if (done) {
if (done && this.afterBreak) {
this.value = "";
this.afterBreak = false;
}
else if (done) {
this.done = true;
this.value = "";
}
@ -545,6 +555,10 @@ class Line {
*/
get length() { return this.to - this.from; }
}
function clip(text, from, to) {
from = Math.max(0, Math.min(text.length, from));
return [from, Math.max(from, Math.min(text.length, to))];
}
// Compressed representation of the Grapheme_Cluster_Break=Extend
// information from
@ -1312,12 +1326,12 @@ class SelectionRange {
The anchor of the rangethe side that doesn't move when you
extend it.
*/
get anchor() { return this.flags & 16 /* RangeFlag.Inverted */ ? this.to : this.from; }
get anchor() { return this.flags & 32 /* RangeFlag.Inverted */ ? this.to : this.from; }
/**
The head of the range, which is moved when the range is
[extended](https://codemirror.net/6/docs/ref/#state.SelectionRange.extend).
*/
get head() { return this.flags & 16 /* RangeFlag.Inverted */ ? this.from : this.to; }
get head() { return this.flags & 32 /* RangeFlag.Inverted */ ? this.from : this.to; }
/**
True when `anchor` and `head` are at the same position.
*/
@ -1328,14 +1342,14 @@ class SelectionRange {
the character before its position, 1 the character after, and 0
means no association.
*/
get assoc() { return this.flags & 4 /* RangeFlag.AssocBefore */ ? -1 : this.flags & 8 /* RangeFlag.AssocAfter */ ? 1 : 0; }
get assoc() { return this.flags & 8 /* RangeFlag.AssocBefore */ ? -1 : this.flags & 16 /* RangeFlag.AssocAfter */ ? 1 : 0; }
/**
The bidirectional text level associated with this cursor, if
any.
*/
get bidiLevel() {
let level = this.flags & 3 /* RangeFlag.BidiLevelMask */;
return level == 3 ? null : level;
let level = this.flags & 7 /* RangeFlag.BidiLevelMask */;
return level == 7 ? null : level;
}
/**
The goal column (stored vertical offset) associated with a
@ -1344,8 +1358,8 @@ class SelectionRange {
lines of different length.
*/
get goalColumn() {
let value = this.flags >> 5 /* RangeFlag.GoalColumnOffset */;
return value == 33554431 /* RangeFlag.NoGoalColumn */ ? undefined : value;
let value = this.flags >> 6 /* RangeFlag.GoalColumnOffset */;
return value == 16777215 /* RangeFlag.NoGoalColumn */ ? undefined : value;
}
/**
Map this range through a change, producing a valid range in the
@ -1374,8 +1388,9 @@ class SelectionRange {
/**
Compare this range to another range.
*/
eq(other) {
return this.anchor == other.anchor && this.head == other.head;
eq(other, includeAssoc = false) {
return this.anchor == other.anchor && this.head == other.head &&
(!includeAssoc || !this.empty || this.assoc == other.assoc);
}
/**
Return a JSON-serializable object representing the range.
@ -1425,14 +1440,17 @@ class EditorSelection {
return EditorSelection.create(this.ranges.map(r => r.map(change, assoc)), this.mainIndex);
}
/**
Compare this selection to another selection.
Compare this selection to another selection. By default, ranges
are compared only by position. When `includeAssoc` is true,
cursor ranges must also have the same
[`assoc`](https://codemirror.net/6/docs/ref/#state.SelectionRange.assoc) value.
*/
eq(other) {
eq(other, includeAssoc = false) {
if (this.ranges.length != other.ranges.length ||
this.mainIndex != other.mainIndex)
return false;
for (let i = 0; i < this.ranges.length; i++)
if (!this.ranges[i].eq(other.ranges[i]))
if (!this.ranges[i].eq(other.ranges[i], includeAssoc))
return false;
return true;
}
@ -1505,18 +1523,18 @@ class EditorSelection {
safely ignore the optional arguments in most situations.
*/
static cursor(pos, assoc = 0, bidiLevel, goalColumn) {
return SelectionRange.create(pos, pos, (assoc == 0 ? 0 : assoc < 0 ? 4 /* RangeFlag.AssocBefore */ : 8 /* RangeFlag.AssocAfter */) |
(bidiLevel == null ? 3 : Math.min(2, bidiLevel)) |
((goalColumn !== null && goalColumn !== void 0 ? goalColumn : 33554431 /* RangeFlag.NoGoalColumn */) << 5 /* RangeFlag.GoalColumnOffset */));
return SelectionRange.create(pos, pos, (assoc == 0 ? 0 : assoc < 0 ? 8 /* RangeFlag.AssocBefore */ : 16 /* RangeFlag.AssocAfter */) |
(bidiLevel == null ? 7 : Math.min(6, bidiLevel)) |
((goalColumn !== null && goalColumn !== void 0 ? goalColumn : 16777215 /* RangeFlag.NoGoalColumn */) << 6 /* RangeFlag.GoalColumnOffset */));
}
/**
Create a selection range.
*/
static range(anchor, head, goalColumn, bidiLevel) {
let flags = ((goalColumn !== null && goalColumn !== void 0 ? goalColumn : 33554431 /* RangeFlag.NoGoalColumn */) << 5 /* RangeFlag.GoalColumnOffset */) |
(bidiLevel == null ? 3 : Math.min(2, bidiLevel));
return head < anchor ? SelectionRange.create(head, anchor, 16 /* RangeFlag.Inverted */ | 8 /* RangeFlag.AssocAfter */ | flags)
: SelectionRange.create(anchor, head, (head > anchor ? 4 /* RangeFlag.AssocBefore */ : 0) | flags);
let flags = ((goalColumn !== null && goalColumn !== void 0 ? goalColumn : 16777215 /* RangeFlag.NoGoalColumn */) << 6 /* RangeFlag.GoalColumnOffset */) |
(bidiLevel == null ? 7 : Math.min(6, bidiLevel));
return head < anchor ? SelectionRange.create(head, anchor, 32 /* RangeFlag.Inverted */ | 16 /* RangeFlag.AssocAfter */ | flags)
: SelectionRange.create(anchor, head, (head > anchor ? 8 /* RangeFlag.AssocBefore */ : 0) | flags);
}
/**
@internal
@ -1553,6 +1571,9 @@ Examples of uses of facets are the [tab
size](https://codemirror.net/6/docs/ref/#state.EditorState^tabSize), [editor
attributes](https://codemirror.net/6/docs/ref/#view.EditorView^editorAttributes), and [update
listeners](https://codemirror.net/6/docs/ref/#view.EditorView^updateListener).
Note that `Facet` instances can be used anywhere where
[`FacetReader`](https://codemirror.net/6/docs/ref/#state.FacetReader) is expected.
*/
class Facet {
constructor(
@ -1580,6 +1601,11 @@ class Facet {
this.extensions = typeof enables == "function" ? enables(this) : enables;
}
/**
Returns a facet reader for this facet, which can be used to
[read](https://codemirror.net/6/docs/ref/#state.EditorState.facet) it but not to define values for it.
*/
get reader() { return this; }
/**
Define a new facet.
*/
static define(config = {}) {
@ -2631,7 +2657,8 @@ class EditorState {
else {
startValues = tr.startState.values.slice();
}
new EditorState(conf, tr.newDoc, tr.newSelection, startValues, (state, slot) => slot.update(state, tr), tr);
let selection = tr.startState.facet(allowMultipleSelections) ? tr.newSelection : tr.newSelection.asSingle();
new EditorState(conf, tr.newDoc, selection, startValues, (state, slot) => slot.update(state, tr), tr);
}
/**
Create a [transaction spec](https://codemirror.net/6/docs/ref/#state.TransactionSpec) that
@ -3365,7 +3392,9 @@ class RangeSet {
let curTo = Math.min(cursor.to, to);
if (cursor.point) {
let active = cursor.activeForPoint(cursor.to);
let openCount = cursor.pointFrom < from ? active.length + 1 : Math.min(active.length, openRanges);
let openCount = cursor.pointFrom < from ? active.length + 1
: cursor.point.startSide < 0 ? active.length
: Math.min(active.length, openRanges);
iterator.point(pos, curTo, cursor.point, active, openCount, cursor.pointRank);
openRanges = Math.min(cursor.openEnd(curTo), active.length);
}
@ -3392,6 +3421,19 @@ class RangeSet {
build.add(range.from, range.to, range.value);
return build.finish();
}
/**
Join an array of range sets into a single set.
*/
static join(sets) {
if (!sets.length)
return RangeSet.empty;
let result = sets[sets.length - 1];
for (let i = sets.length - 2; i >= 0; i--) {
for (let layer = sets[i]; layer != RangeSet.empty; layer = layer.nextLayer)
result = new RangeSet(layer.chunkPos, layer.chunk, result, Math.max(layer.maxPoint, result.maxPoint));
}
return result;
}
}
/**
The empty set of ranges.
@ -3709,7 +3751,8 @@ class SpanCursor {
}
addActive(trackOpen) {
let i = 0, { value, to, rank } = this.cursor;
while (i < this.activeRank.length && this.activeRank[i] <= rank)
// Organize active marks by rank first, then by size
while (i < this.activeRank.length && (rank - this.activeRank[i] || to - this.activeTo[i]) > 0)
i++;
insert(this.active, i, value);
insert(this.activeTo, i, to);

View file

@ -1,6 +1,6 @@
{
"name": "@codemirror/state",
"version": "6.2.1",
"version": "6.4.1",
"description": "Editor state data structures for the CodeMirror code editor",
"scripts": {
"test": "cm-runtests",
@ -26,7 +26,7 @@
"sideEffects": false,
"license": "MIT",
"devDependencies": {
"@codemirror/buildhelper": "^0.1.5"
"@codemirror/buildhelper": "^1.0.0"
},
"repository": {
"type": "git",

View file

@ -1,3 +1,289 @@
## 6.26.1 (2024-03-28)
### Bug fixes
Fix the editor getting stuck in composition when Safari fails to fire a compositionend event for a dead key composition.
Fix an issue where, with IME systems that kept the cursor at the start of the composed text, the editor misidentified the target node and disrupted composition.
Fix a bug where in a line-wrapped editor, with some content, the initial scroll position would be off from the top of the document.
## 6.26.0 (2024-03-14)
### Bug fixes
Avoid the editor getting confused when iOS autocorrects on pressing Enter and does the correction and the break insertion in two different events.
Fix the pasting of copied URIs in iOS.
Fix a bug where a scaled editor could keep performing unnecessary updates due to tiny differences in geometry values returned by the browser.
Fix a bug where, on iOS with a physical keyboard, the modifiers for some keys weren't being passed to the keymaps.
Work around the fact that Mobile Safari makes DOM changes before firing a key event when typing ctrl-d on an external keyboard.
Fix an issue where some commands didn't properly scroll the cursor into view on Mobile Safari.
Re-measure the document when print settings are changed on Chrome.
### New features
The `EditorView.scrollHandler` facet can be used to override or extend the behavior of the editor when things are scrolled into view.
## 6.25.1 (2024-03-06)
### Bug fixes
Fix accidental non-optional field in layer config objects.
## 6.25.0 (2024-03-04)
### Bug fixes
Properly recognize Android GBoard enter presses that strip a space at the end of the line as enter.
Fix a bug that caused the gutter to have the wrong height when the editor was scaled after construction.
When starting a composition after a non-inclusive mark decoration, temporarily insert a widget that prevents the composed text from inheriting that mark's styles.
Make sure the selection is repositioned when a transaction changes decorations without changing the document.
### New features
View plugins can now provide a `docViewUpdate` method that is called whenever the document view is updated.
Layers now take a `updateOnDocUpdate` option that controls whether they are automatically updated when the document view changes.
## 6.24.1 (2024-02-19)
### Bug fixes
Fix a crash that happens when hover tooltips are active during changes, introduced in 6.24.0.
## 6.24.0 (2024-02-09)
### Bug fixes
Fix an issue that broke context-menu select-all on Chrome when the viewport didn't cover the whole document.
Make sure tooltips are ordered by extension precedence in the DOM.
### New features
Hover tooltip sources may now return multiple tooltips.
## 6.23.1 (2024-01-24)
### Bug fixes
Fix a bug that caused `Tooltip.above` to not take effect for tooltips that were already present when the tooltip plugin is initialized.
Automatically reposition tooltips when their size changes.
## 6.23.0 (2023-12-28)
### Bug fixes
Work around odd iOS Safari behavior when doing select all.
Fix a composition interruption when an widget is inserted next to the cursor.
Fix a crash in bidirectional cursor motion.
Simplify visual motion through bidirectional text, fix several corner cases where it would work badly.
Fix a bug that broke some bidi isolates not on the first line of the document.
### New features
`EditorView.bidiIsolatedRanges` now supports automatically determining the direction of the range if not provided by the decoration.
`EditorView.visualLineSide` can be used to find the visual end or start of a line with bidirectional text.
The new `EditorView.outerDecorations` facet can be used to provide decorations that should always be at the bottom of the precedence stack.
## 6.22.3 (2023-12-13)
### Bug fixes
Fix a bug that could cause tooltips to be unnecessarily be positioned absolutely.
Make sure that, when an editor creates tooltips immediately on initialization, the editor is attached to the document when their `mount` callback is called.
## 6.22.2 (2023-12-08)
### Bug fixes
Fix an issue in the bidirectional motion that could cause the cursor to get stuck in a loop when a zero-width non-joiner char was placed on a direction boundary.
Fix a bug that corrupts the editor's internal view tree data structure on some types of edits, putting the editor in a broken state.
## 6.22.1 (2023-11-27)
### Bug fixes
Call widget `destroy` methods when the entire editor is destroyed or reset.
Work around an issue on Safari on macOS Sonoma that made the native cursor visible even when `drawSelection` is enabled.
Fix an issue where, on some browsers, the screenreader announced text ended up in the printed document.
Fix a bug where a hover tooltip could stick around even though the pointer was no longer on the editor when it was moved out over the tooltip.
Fix an issue where hover tooltips could close when moving the mouse onto them due to mouse position rounding issues.
## 6.22.0 (2023-11-03)
### Bug fixes
Exceptions raised by update listeners are now routed to the configured exception sink, if any.
Fix an issue where passing large scroll margins to `scrollIntoView` would cause the measure loop to fail to terminate.
Widgets that are draggable (and allow drag events through in their `ignoreEvent` implementation) can now use the editor's built-in drag/drop behavior.
### New features
The new `scrollTo` option to `EditorView` allows an initial scroll position to be provided.
The new `EditorView.scrollSnapshot` method returns an effect that can be used to reset to a previous scroll position.
## 6.21.4 (2023-10-24)
### Bug fixes
Support the `offset`, `getCoords`, `overlap`, and `resize` properties on hover tooltips, as long as they aren't given conflicting values when there are multiple active hover tooltips.
Fix a bug that caused tooltips in the default configuration to be positioned incorrectly on Chrome when the editor was transformed.
## 6.21.3 (2023-10-06)
### Bug fixes
Fix an issue that caused `coordsForChar` to return the wrong rectangle for characters after a line wrap in Safari.
Make the context menu work when clicking below the content in a fixed-height editor.
Tooltips that have been put below/above their target position because there is no room on their default side now stay there on further updates.
## 6.21.2 (2023-10-02)
### Bug fixes
Fix a regression that broke dragging text from inside the editor.
## 6.21.1 (2023-10-02)
### Bug fixes
Fix a bug that could corrupt the DOM view for specific changes involving newlines and mark decorations.
## 6.21.0 (2023-09-29)
### Bug fixes
Fix a bug that could cause zero-length widgets at the start of a line to be left in the view even after they were removed.
### New features
`RectangleMarker`'s dimension properties are now public.
## 6.20.2 (2023-09-25)
### Bug fixes
Fix an issue in the way the DOM selection is being read that could break backspacing of widgets on Android.
Fix a bug where the editor could incorrectly computate its transform scale when it was small.
## 6.20.1 (2023-09-22)
### Bug fixes
Fix a crash in plugin event handlers after dynamic reconfiguration.
Fix an issue where, on Chrome, tooltips would no longer use fixed positioning.
## 6.20.0 (2023-09-20)
### Bug fixes
Fix an issue that caused `repositionTooltips` to crash when it was called on an editor without tooltips.
Fix an issue that caused the tooltip system to leave empty nodes in the DOM when an editor using the `parent` option to `tooltips` is destroyed.
Fix a bug that regression mouse interaction with the area of a fixed-size editor that isn't covered by the content.
Fix some issues with the way `moveVertically` behaved for positions on line wrap points.
Fix a bug that could cause the document DOM to be incorrectly updated on some types of viewport changes.
### New features
The new `getDrawSelectionConfig` function returns the `drawSelection` configuration for a given state.
## 6.19.0 (2023-09-14)
### Bug fixes
Make sure the drop cursor is properly cleaned up even when another extension handles the drop event.
Fix a crash related to non-inclusive replacing block decorations.
### New features
The new `EditorView.domEventObservers` (and the corresponding option to view plugins) allows you to register functions that are always called for an event, regardless of whether other handlers handled it.
## 6.18.1 (2023-09-11)
### Bug fixes
Fix an issue where the editor duplicated text when the browser moved content into the focused text node on composition.
Make sure `widgetMarker` is called for gutters on lines covered by a block replace decoration.
Fix an issue where the cursor could be shown in a position that doesn't allow a cursor when the selection is in a block widget.
## 6.18.0 (2023-09-05)
### New features
The new `EditorView.scaleX` and `scaleY` properties return the CSS-transformed scale of the editor (or 1 when not scaled).
The editor now supports being scaled with CSS.
## 6.17.1 (2023-08-31)
### Bug fixes
Don't close the hover tooltip when the pointer moves over empty space caused by line breaks within the hovered range.
Fix a bug where on Chrome Android, if a virtual keyboard was slow to apply a change, the editor could end up dropping it.
Work around an issue where line-wise copy/cut didn't work in Firefox because the browser wasn't firing those events when nothing was selected.
Fix a crash triggered by the way some Android IME systems update the DOM.
Fix a bug that caused replacing a word by an emoji on Chrome Android to be treated as a backspace press.
## 6.17.0 (2023-08-28)
### Bug fixes
Fix a bug that broke hover tooltips when hovering over a widget.
### New features
The new `EditorView.cspNonce` facet can be used to provide a Content Security Policy nonce for the library's generated CSS.
The new `EditorView.bidiIsolatedRanges` can be used to inform the editor about ranges styled as Unicode bidirection isolates, so that it can compute the character order correctly.
`EditorView.dispatch` now also accepts an array of transactions to be applied together in a single view update.
The new `dispatchTransactions` option to `new EditorView` now replaces the old (deprecated but still supported) `dispatch` option in a way that allows multiple transactions to be applied in one update.
Input handlers are now passed an additional argument that they can use to retrieve the default transaction that would be applied for the insertion.
## 6.16.0 (2023-07-31)
### Bug fixes

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,48 @@
import * as _codemirror_state from '@codemirror/state';
import { RangeSet, RangeValue, Range, EditorState, Extension, Transaction, ChangeSet, EditorSelection, EditorStateConfig, TransactionSpec, SelectionRange, Line, StateEffect, Facet } from '@codemirror/state';
import { RangeSet, RangeValue, Range, EditorState, Extension, Transaction, ChangeSet, SelectionRange, ChangeDesc, EditorSelection, EditorStateConfig, StateEffect, TransactionSpec, Line, Facet } from '@codemirror/state';
import { StyleModule, StyleSpec } from 'style-mod';
declare type Attrs = {
/**
Used to indicate [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection).
*/
declare enum Direction {
/**
Left-to-right.
*/
LTR = 0,
/**
Right-to-left.
*/
RTL = 1
}
/**
Represents a contiguous range of text that has a single direction
(as in left-to-right or right-to-left).
*/
declare class BidiSpan {
/**
The start of the span (relative to the start of the line).
*/
readonly from: number;
/**
The end of the span.
*/
readonly to: number;
/**
The ["bidi
level"](https://unicode.org/reports/tr9/#Basic_Display_Algorithm)
of the span (in this context, 0 means
left-to-right, 1 means right-to-left, 2 means left-to-right
number inside right-to-left text).
*/
readonly level: number;
/**
The direction of this span.
*/
get dir(): Direction;
}
type Attrs = {
[name: string]: string;
};
@ -15,7 +55,7 @@ interface Rect {
readonly top: number;
readonly bottom: number;
}
declare type ScrollStrategy = "nearest" | "start" | "end" | "center";
type ScrollStrategy = "nearest" | "start" | "end" | "center";
interface MarkDecorationSpec {
/**
@ -53,6 +93,15 @@ interface MarkDecorationSpec {
*/
tagName?: string;
/**
When using sets of decorations in
[`bidiIsolatedRanges`](https://codemirror.net/6/docs/ref/##view.EditorView^bidiIsolatedRanges),
this property provides the direction of the isolates. When null
or not given, it indicates the range has `dir=auto`, and its
direction should be derived from the first strong directional
character in it.
*/
bidiIsolate?: Direction | null;
/**
Decoration specs allow extra properties, which can be retrieved
through the decoration's [`spec`](https://codemirror.net/6/docs/ref/#view.Decoration.spec)
property.
@ -215,7 +264,7 @@ A decoration set represents a collection of decorated ranges,
organized for efficient access and mapping. See
[`RangeSet`](https://codemirror.net/6/docs/ref/#state.RangeSet) for its methods.
*/
declare type DecorationSet = RangeSet<Decoration>;
type DecorationSet = RangeSet<Decoration>;
/**
The different types of blocks that can occur in an editor view.
*/
@ -314,7 +363,18 @@ apply to the editor, and if it can, perform it as a side effect
(which usually means [dispatching](https://codemirror.net/6/docs/ref/#view.EditorView.dispatch) a
transaction) and return `true`.
*/
declare type Command = (target: EditorView) => boolean;
type Command = (target: EditorView) => boolean;
declare class ScrollTarget {
readonly range: SelectionRange;
readonly y: ScrollStrategy;
readonly x: ScrollStrategy;
readonly yMargin: number;
readonly xMargin: number;
readonly isSnapshot: boolean;
constructor(range: SelectionRange, y?: ScrollStrategy, x?: ScrollStrategy, yMargin?: number, xMargin?: number, isSnapshot?: boolean);
map(changes: ChangeDesc): ScrollTarget;
clip(state: EditorState): ScrollTarget;
}
/**
Log or report an unhandled exception in client code. Should
probably only be used by extension code that allows client code to
@ -344,6 +404,13 @@ interface PluginValue extends Object {
*/
update?(update: ViewUpdate): void;
/**
Called when the document view is updated (due to content,
decoration, or viewport changes). Should not try to immediately
start another view update. Often useful for calling
[`requestMeasure`](https://codemirror.net/6/docs/ref/#view.EditorView.requestMeasure).
*/
docViewUpdate?(view: EditorView): void;
/**
Called when the plugin is no longer going to be used. Should
revert any changes the plugin made to the DOM.
*/
@ -362,6 +429,12 @@ interface PluginSpec<V extends PluginValue> {
*/
eventHandlers?: DOMEventHandlers<V>;
/**
Registers [event observers](https://codemirror.net/6/docs/ref/#view.EditorView^domEventObservers)
for the plugin. Will, when called, have their `this` bound to
the plugin value.
*/
eventObservers?: DOMEventHandlers<V>;
/**
Specify that the plugin provides additional extensions when
added to an editor configuration.
*/
@ -412,11 +485,11 @@ interface MeasureRequest<T> {
write?(measure: T, view: EditorView): void;
/**
When multiple requests with the same key are scheduled, only the
last one will actually be ran.
last one will actually be run.
*/
key?: any;
}
declare type AttrSource = Attrs | ((view: EditorView) => Attrs | null);
type AttrSource = Attrs | ((view: EditorView) => Attrs | null);
/**
View [plugins](https://codemirror.net/6/docs/ref/#view.ViewPlugin) are given instances of this
class, which describe what happened, whenever the view is updated.
@ -505,7 +578,7 @@ interface MouseSelectionStyle {
*/
update: (update: ViewUpdate) => boolean | void;
}
declare type MakeSelectionStyle = (view: EditorView, event: MouseEvent) => MouseSelectionStyle | null;
type MakeSelectionStyle = (view: EditorView, event: MouseEvent) => MouseSelectionStyle | null;
/**
Record used to represent information about a block-level element
@ -554,46 +627,6 @@ declare class BlockInfo {
get widgetLineBreaks(): number;
}
/**
Used to indicate [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection).
*/
declare enum Direction {
/**
Left-to-right.
*/
LTR = 0,
/**
Right-to-left.
*/
RTL = 1
}
/**
Represents a contiguous range of text that has a single direction
(as in left-to-right or right-to-left).
*/
declare class BidiSpan {
/**
The start of the span (relative to the start of the line).
*/
readonly from: number;
/**
The end of the span.
*/
readonly to: number;
/**
The ["bidi
level"](https://unicode.org/reports/tr9/#Basic_Display_Algorithm)
of the span (in this context, 0 means
left-to-right, 1 means right-to-left, 2 means left-to-right
number inside right-to-left text).
*/
readonly level: number;
/**
The direction of this span.
*/
get dir(): Direction;
}
/**
The type of object given to the [`EditorView`](https://codemirror.net/6/docs/ref/#view.EditorView)
constructor.
@ -621,11 +654,23 @@ interface EditorViewConfig extends EditorStateConfig {
*/
root?: Document | ShadowRoot;
/**
Override the transaction [dispatch
function](https://codemirror.net/6/docs/ref/#view.EditorView.dispatch) for this editor view, which
is the way updates get routed to the view. Your implementation,
if provided, should probably call the view's [`update`
method](https://codemirror.net/6/docs/ref/#view.EditorView.update).
Pass an effect created with
[`EditorView.scrollIntoView`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView) or
[`EditorView.scrollSnapshot`](https://codemirror.net/6/docs/ref/#view.EditorView.scrollSnapshot)
here to set an initial scroll position.
*/
scrollTo?: StateEffect<any>;
/**
Override the way transactions are
[dispatched](https://codemirror.net/6/docs/ref/#view.EditorView.dispatch) for this editor view.
Your implementation, if provided, should probably call the
view's [`update` method](https://codemirror.net/6/docs/ref/#view.EditorView.update).
*/
dispatchTransactions?: (trs: readonly Transaction[], view: EditorView) => void;
/**
**Deprecated** single-transaction version of
`dispatchTransactions`. Will force transactions to be dispatched
one at a time when used.
*/
dispatch?: (tr: Transaction, view: EditorView) => void;
}
@ -681,7 +726,7 @@ declare class EditorView {
composition there.
*/
get compositionStarted(): boolean;
private _dispatch;
private dispatchTransactions;
private _root;
/**
The document or shadow root that the view lives in.
@ -721,14 +766,19 @@ declare class EditorView {
constructor(config?: EditorViewConfig);
/**
All regular editor state updates should go through this. It
takes a transaction or transaction spec and updates the view to
show the new state produced by that transaction. Its
implementation can be overridden with an
[option](https://codemirror.net/6/docs/ref/#view.EditorView.constructor^config.dispatch). This
function is bound to the view instance, so it does not have to
be called as a method.
takes a transaction, array of transactions, or transaction spec
and updates the view to show the new state produced by that
transaction. Its implementation can be overridden with an
[option](https://codemirror.net/6/docs/ref/#view.EditorView.constructor^config.dispatchTransactions).
This function is bound to the view instance, so it does not have
to be called as a method.
Note that when multiple `TransactionSpec` arguments are
provided, these define a single transaction (the specs will be
merged), not a sequence of transactions.
*/
dispatch(tr: Transaction): void;
dispatch(trs: readonly Transaction[]): void;
dispatch(...specs: TransactionSpec[]): void;
/**
Update the view for the given array of transactions. This will
@ -748,6 +798,7 @@ declare class EditorView {
*/
setState(newState: EditorState): void;
private updatePlugins;
private docViewUpdate;
/**
Get the CSS classes for the currently active editor themes.
*/
@ -786,6 +837,16 @@ declare class EditorView {
bottom: number;
};
/**
If the editor is transformed with CSS, this provides the scale
along the X axis. Otherwise, it will just be 1. Note that
transforms other than translation and scaling are not supported.
*/
get scaleX(): number;
/**
Provide the CSS transformed scale along the Y axis.
*/
get scaleY(): number;
/**
Find the text line or block widget at the given vertical
position (which is interpreted as relative to the [top of the
document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop)).
@ -842,6 +903,13 @@ declare class EditorView {
*/
moveByGroup(start: SelectionRange, forward: boolean): SelectionRange;
/**
Get the cursor position visually at the start or end of a line.
Note that this may differ from the _logical_ position at its
start or end (which is simply at `line.from`/`line.to`) if text
at the start or end goes against the line's base text direction.
*/
visualLineSide(line: Line, end: boolean): SelectionRange;
/**
Move to the next line boundary in the given direction. If
`includeWrap` is true, line wrapping is on, and there is a
further wrap point on the current line, the wrap point will be
@ -1000,15 +1068,30 @@ declare class EditorView {
/**
Extra vertical distance to add when moving something into
view. Not used with the `"center"` strategy. Defaults to 5.
Must be less than the height of the editor.
*/
yMargin?: number;
/**
Extra horizontal distance to add. Not used with the `"center"`
strategy. Defaults to 5.
strategy. Defaults to 5. Must be less than the width of the
editor.
*/
xMargin?: number;
}): StateEffect<unknown>;
/**
Return an effect that resets the editor to its current (at the
time this method was called) scroll position. Note that this
only affects the editor's own scrollable element, not parents.
See also
[`EditorViewConfig.scrollTo`](https://codemirror.net/6/docs/ref/#view.EditorViewConfig.scrollTo).
The effect should be used with a document identical to the one
it was created for. Failing to do so is not an error, but may
not scroll to the expected position. You can
[map](https://codemirror.net/6/docs/ref/#state.StateEffect.map) the effect to account for changes.
*/
scrollSnapshot(): StateEffect<ScrollTarget>;
/**
Facet to add a [style
module](https://github.com/marijnh/style-mod#documentation) to
an editor view. The view will ensure that the module is
@ -1030,13 +1113,43 @@ declare class EditorView {
*/
static domEventHandlers(handlers: DOMEventHandlers<any>): Extension;
/**
Create an extension that registers DOM event observers. Contrary
to event [handlers](https://codemirror.net/6/docs/ref/#view.EditorView^domEventHandlers),
observers can't be prevented from running by a higher-precedence
handler returning true. They also don't prevent other handlers
and observers from running when they return true, and should not
call `preventDefault`.
*/
static domEventObservers(observers: DOMEventHandlers<any>): Extension;
/**
An input handler can override the way changes to the editable
DOM content are handled. Handlers are passed the document
positions between which the change was found, and the new
content. When one returns true, no further input handlers are
called and the default behavior is prevented.
The `insert` argument can be used to get the default transaction
that would be applied for this input. This can be useful when
dispatching the custom behavior as a separate transaction.
*/
static inputHandler: Facet<(view: EditorView, from: number, to: number, text: string) => boolean, readonly ((view: EditorView, from: number, to: number, text: string) => boolean)[]>;
static inputHandler: Facet<(view: EditorView, from: number, to: number, text: string, insert: () => Transaction) => boolean, readonly ((view: EditorView, from: number, to: number, text: string, insert: () => Transaction) => boolean)[]>;
/**
Scroll handlers can override how things are scrolled into view.
If they return `true`, no further handling happens for the
scrolling. If they return false, the default scroll behavior is
applied. Scroll handlers should never initiate editor updates.
*/
static scrollHandler: Facet<(view: EditorView, range: SelectionRange, options: {
x: ScrollStrategy;
y: ScrollStrategy;
xMargin: number;
yMargin: number;
}) => boolean, readonly ((view: EditorView, range: SelectionRange, options: {
x: ScrollStrategy;
y: ScrollStrategy;
xMargin: number;
yMargin: number;
}) => boolean)[]>;
/**
This facet can be used to provide functions that create effects
to be dispatched when the editor's focus state changes.
@ -1110,6 +1223,16 @@ declare class EditorView {
*/
static decorations: Facet<DecorationSet | ((view: EditorView) => DecorationSet), readonly (DecorationSet | ((view: EditorView) => DecorationSet))[]>;
/**
Facet that works much like
[`decorations`](https://codemirror.net/6/docs/ref/#view.EditorView^decorations), but puts its
inputs at the very bottom of the precedence stack, meaning mark
decorations provided here will only be split by other, partially
overlapping \`outerDecorations\` ranges, and wrap around all
regular decorations. Use this for mark elements that should, as
much as possible, remain in one piece.
*/
static outerDecorations: Facet<DecorationSet | ((view: EditorView) => DecorationSet), readonly (DecorationSet | ((view: EditorView) => DecorationSet))[]>;
/**
Used to provide ranges that should be treated as atoms as far as
cursor motion is concerned. This causes methods like
[`moveByChar`](https://codemirror.net/6/docs/ref/#view.EditorView.moveByChar) and
@ -1122,6 +1245,16 @@ declare class EditorView {
*/
static atomicRanges: Facet<(view: EditorView) => _codemirror_state.RangeSet<any>, readonly ((view: EditorView) => _codemirror_state.RangeSet<any>)[]>;
/**
When range decorations add a `unicode-bidi: isolate` style, they
should also include a
[`bidiIsolate`](https://codemirror.net/6/docs/ref/#view.MarkDecorationSpec.bidiIsolate) property
in their decoration spec, and be exposed through this facet, so
that the editor can compute the proper text order. (Other values
for `unicode-bidi`, except of course `normal`, are not
supported.)
*/
static bidiIsolatedRanges: Facet<DecorationSet | ((view: EditorView) => DecorationSet), readonly (DecorationSet | ((view: EditorView) => DecorationSet))[]>;
/**
Facet that allows extensions to provide additional scroll
margins (space around the sides of the scrolling element that
should be considered invisible). This can be useful when the
@ -1170,6 +1303,12 @@ declare class EditorView {
[selector: string]: StyleSpec;
}): Extension;
/**
Provides a Content Security Policy nonce to use when creating
the style sheets for the editor. Holds the empty string when no
nonce has been provided.
*/
static cspNonce: Facet<string, string>;
/**
Facet that provides additional DOM attributes for the editor's
editable DOM element.
*/
@ -1213,7 +1352,7 @@ to hold the appropriate event object type. For unknown events, it
is inferred to `any`, and should be explicitly set if you want type
checking.
*/
declare type DOMEventHandlers<This> = {
type DOMEventHandlers<This> = {
[event in keyof DOMEventMap]?: (this: This, event: DOMEventMap[event], view: EditorView) => boolean | void;
};
@ -1322,7 +1461,7 @@ handlers handled it.
*/
declare function runScopeHandlers(view: EditorView, event: KeyboardEvent, scope: string): boolean;
declare type SelectionConfig = {
type SelectionConfig = {
/**
The length of a full cursor blink cycle, in milliseconds.
Defaults to 1200. Can be set to 0 to disable blinking.
@ -1353,6 +1492,12 @@ layout information that's only available after laying out the
content).
*/
declare function drawSelection(config?: SelectionConfig): Extension;
/**
Retrieve the [`drawSelection`](https://codemirror.net/6/docs/ref/#view.drawSelection) configuration
for this state. (Note that this will return a set of defaults even
if `drawSelection` isn't enabled.)
*/
declare function getDrawSelectionConfig(state: EditorState): SelectionConfig;
/**
Draws a cursor at the current drop position when something is
@ -1452,15 +1597,43 @@ a rectangle at a given set of coordinates.
*/
declare class RectangleMarker implements LayerMarker {
private className;
private left;
private top;
private width;
private height;
/**
The left position of the marker (in pixels, document-relative).
*/
readonly left: number;
/**
The top position of the marker.
*/
readonly top: number;
/**
The width of the marker, or null if it shouldn't get a width assigned.
*/
readonly width: number | null;
/**
The height of the marker.
*/
readonly height: number;
/**
Create a marker with the given class and dimensions. If `width`
is null, the DOM element will get no width style.
*/
constructor(className: string, left: number, top: number, width: number | null, height: number);
constructor(className: string,
/**
The left position of the marker (in pixels, document-relative).
*/
left: number,
/**
The top position of the marker.
*/
top: number,
/**
The width of the marker, or null if it shouldn't get a width assigned.
*/
width: number | null,
/**
The height of the marker.
*/
height: number);
draw(): HTMLDivElement;
update(elt: HTMLElement, prev: RectangleMarker): boolean;
private adjust;
@ -1490,6 +1663,11 @@ interface LayerConfig {
*/
update(update: ViewUpdate, layer: HTMLElement): boolean;
/**
Whether to update this layer every time the document view
changes. Defaults to true.
*/
updateOnDocViewUpdate?: boolean;
/**
Build a set of markers for this layer, and measure their
dimensions.
*/
@ -1617,6 +1795,9 @@ declare function tooltips(config?: {
On iOS, which at the time of writing still doesn't properly
support fixed positioning, the library always uses absolute
positioning.
If the tooltip parent element sits in a transformed element, the
library also falls back to absolute positioning.
*/
position?: "fixed" | "absolute";
/**
@ -1738,6 +1919,7 @@ interface TooltipView {
Facet to which an extension can add a value to show a tooltip.
*/
declare const showTooltip: Facet<Tooltip | null, readonly (Tooltip | null)[]>;
type HoverSource = (view: EditorView, pos: number, side: -1 | 1) => Tooltip | readonly Tooltip[] | null | Promise<Tooltip | readonly Tooltip[] | null>;
/**
Set up a hover tooltip, which shows up when the pointer hovers
over ranges of text. The callback is called when the mouse hovers
@ -1751,7 +1933,7 @@ Note that all hover tooltips are hosted within a single tooltip
container element. This allows multiple tooltips over the same
range to be "merged" together without overlapping.
*/
declare function hoverTooltip(source: (view: EditorView, pos: number, side: -1 | 1) => Tooltip | null | Promise<Tooltip | null>, options?: {
declare function hoverTooltip(source: HoverSource, options?: {
/**
Controls whether a transaction hides the tooltip. The default
is to not hide.
@ -1788,7 +1970,7 @@ invalidate the existing tooltip positions.
*/
declare function repositionTooltips(view: EditorView): void;
declare type PanelConfig = {
type PanelConfig = {
/**
By default, panels will be placed inside the editor's DOM
structure. You can use this option to override where panels with
@ -1842,7 +2024,7 @@ declare function getPanel(view: EditorView, panel: PanelConstructor): Panel | nu
A function that initializes a panel. Used in
[`showPanel`](https://codemirror.net/6/docs/ref/#view.showPanel).
*/
declare type PanelConstructor = (view: EditorView) => Panel;
type PanelConstructor = (view: EditorView) => Panel;
/**
Opening a panel is done by providing a constructor function for
the panel through this facet. (The panel is closed again when its
@ -1883,7 +2065,7 @@ Markers given to this facet should _only_ define an
in all gutters for the line).
*/
declare const gutterLineClass: Facet<RangeSet<GutterMarker>, readonly RangeSet<GutterMarker>[]>;
declare type Handlers = {
type Handlers = {
[event: string]: (view: EditorView, line: BlockInfo, event: Event) => boolean;
};
interface GutterConfig {
@ -1986,4 +2168,4 @@ trailing whitespace.
*/
declare function highlightTrailingWhitespace(): Extension;
export { BidiSpan, BlockInfo, BlockType, Command, DOMEventHandlers, DOMEventMap, Decoration, DecorationSet, Direction, EditorView, EditorViewConfig, GutterMarker, KeyBinding, LayerMarker, MatchDecorator, MouseSelectionStyle, Panel, PanelConstructor, PluginSpec, PluginValue, Rect, RectangleMarker, Tooltip, TooltipView, ViewPlugin, ViewUpdate, WidgetType, closeHoverTooltips, crosshairCursor, drawSelection, dropCursor, getPanel, getTooltip, gutter, gutterLineClass, gutters, hasHoverTooltips, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, highlightTrailingWhitespace, highlightWhitespace, hoverTooltip, keymap, layer, lineNumberMarkers, lineNumbers, logException, panels, placeholder, rectangularSelection, repositionTooltips, runScopeHandlers, scrollPastEnd, showPanel, showTooltip, tooltips };
export { BidiSpan, BlockInfo, BlockType, type Command, type DOMEventHandlers, type DOMEventMap, Decoration, type DecorationSet, Direction, EditorView, type EditorViewConfig, GutterMarker, type KeyBinding, type LayerMarker, MatchDecorator, type MouseSelectionStyle, type Panel, type PanelConstructor, type PluginSpec, type PluginValue, type Rect, RectangleMarker, type Tooltip, type TooltipView, ViewPlugin, ViewUpdate, WidgetType, closeHoverTooltips, crosshairCursor, drawSelection, dropCursor, getDrawSelectionConfig, getPanel, getTooltip, gutter, gutterLineClass, gutters, hasHoverTooltips, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, highlightTrailingWhitespace, highlightWhitespace, hoverTooltip, keymap, layer, lineNumberMarkers, lineNumbers, logException, panels, placeholder, rectangularSelection, repositionTooltips, runScopeHandlers, scrollPastEnd, showPanel, showTooltip, tooltips };

View file

@ -1,8 +1,48 @@
import * as _codemirror_state from '@codemirror/state';
import { RangeSet, RangeValue, Range, EditorState, Extension, Transaction, ChangeSet, EditorSelection, EditorStateConfig, TransactionSpec, SelectionRange, Line, StateEffect, Facet } from '@codemirror/state';
import { RangeSet, RangeValue, Range, EditorState, Extension, Transaction, ChangeSet, SelectionRange, ChangeDesc, EditorSelection, EditorStateConfig, StateEffect, TransactionSpec, Line, Facet } from '@codemirror/state';
import { StyleModule, StyleSpec } from 'style-mod';
declare type Attrs = {
/**
Used to indicate [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection).
*/
declare enum Direction {
/**
Left-to-right.
*/
LTR = 0,
/**
Right-to-left.
*/
RTL = 1
}
/**
Represents a contiguous range of text that has a single direction
(as in left-to-right or right-to-left).
*/
declare class BidiSpan {
/**
The start of the span (relative to the start of the line).
*/
readonly from: number;
/**
The end of the span.
*/
readonly to: number;
/**
The ["bidi
level"](https://unicode.org/reports/tr9/#Basic_Display_Algorithm)
of the span (in this context, 0 means
left-to-right, 1 means right-to-left, 2 means left-to-right
number inside right-to-left text).
*/
readonly level: number;
/**
The direction of this span.
*/
get dir(): Direction;
}
type Attrs = {
[name: string]: string;
};
@ -15,7 +55,7 @@ interface Rect {
readonly top: number;
readonly bottom: number;
}
declare type ScrollStrategy = "nearest" | "start" | "end" | "center";
type ScrollStrategy = "nearest" | "start" | "end" | "center";
interface MarkDecorationSpec {
/**
@ -53,6 +93,15 @@ interface MarkDecorationSpec {
*/
tagName?: string;
/**
When using sets of decorations in
[`bidiIsolatedRanges`](https://codemirror.net/6/docs/ref/##view.EditorView^bidiIsolatedRanges),
this property provides the direction of the isolates. When null
or not given, it indicates the range has `dir=auto`, and its
direction should be derived from the first strong directional
character in it.
*/
bidiIsolate?: Direction | null;
/**
Decoration specs allow extra properties, which can be retrieved
through the decoration's [`spec`](https://codemirror.net/6/docs/ref/#view.Decoration.spec)
property.
@ -215,7 +264,7 @@ A decoration set represents a collection of decorated ranges,
organized for efficient access and mapping. See
[`RangeSet`](https://codemirror.net/6/docs/ref/#state.RangeSet) for its methods.
*/
declare type DecorationSet = RangeSet<Decoration>;
type DecorationSet = RangeSet<Decoration>;
/**
The different types of blocks that can occur in an editor view.
*/
@ -314,7 +363,18 @@ apply to the editor, and if it can, perform it as a side effect
(which usually means [dispatching](https://codemirror.net/6/docs/ref/#view.EditorView.dispatch) a
transaction) and return `true`.
*/
declare type Command = (target: EditorView) => boolean;
type Command = (target: EditorView) => boolean;
declare class ScrollTarget {
readonly range: SelectionRange;
readonly y: ScrollStrategy;
readonly x: ScrollStrategy;
readonly yMargin: number;
readonly xMargin: number;
readonly isSnapshot: boolean;
constructor(range: SelectionRange, y?: ScrollStrategy, x?: ScrollStrategy, yMargin?: number, xMargin?: number, isSnapshot?: boolean);
map(changes: ChangeDesc): ScrollTarget;
clip(state: EditorState): ScrollTarget;
}
/**
Log or report an unhandled exception in client code. Should
probably only be used by extension code that allows client code to
@ -344,6 +404,13 @@ interface PluginValue extends Object {
*/
update?(update: ViewUpdate): void;
/**
Called when the document view is updated (due to content,
decoration, or viewport changes). Should not try to immediately
start another view update. Often useful for calling
[`requestMeasure`](https://codemirror.net/6/docs/ref/#view.EditorView.requestMeasure).
*/
docViewUpdate?(view: EditorView): void;
/**
Called when the plugin is no longer going to be used. Should
revert any changes the plugin made to the DOM.
*/
@ -362,6 +429,12 @@ interface PluginSpec<V extends PluginValue> {
*/
eventHandlers?: DOMEventHandlers<V>;
/**
Registers [event observers](https://codemirror.net/6/docs/ref/#view.EditorView^domEventObservers)
for the plugin. Will, when called, have their `this` bound to
the plugin value.
*/
eventObservers?: DOMEventHandlers<V>;
/**
Specify that the plugin provides additional extensions when
added to an editor configuration.
*/
@ -412,11 +485,11 @@ interface MeasureRequest<T> {
write?(measure: T, view: EditorView): void;
/**
When multiple requests with the same key are scheduled, only the
last one will actually be ran.
last one will actually be run.
*/
key?: any;
}
declare type AttrSource = Attrs | ((view: EditorView) => Attrs | null);
type AttrSource = Attrs | ((view: EditorView) => Attrs | null);
/**
View [plugins](https://codemirror.net/6/docs/ref/#view.ViewPlugin) are given instances of this
class, which describe what happened, whenever the view is updated.
@ -505,7 +578,7 @@ interface MouseSelectionStyle {
*/
update: (update: ViewUpdate) => boolean | void;
}
declare type MakeSelectionStyle = (view: EditorView, event: MouseEvent) => MouseSelectionStyle | null;
type MakeSelectionStyle = (view: EditorView, event: MouseEvent) => MouseSelectionStyle | null;
/**
Record used to represent information about a block-level element
@ -554,46 +627,6 @@ declare class BlockInfo {
get widgetLineBreaks(): number;
}
/**
Used to indicate [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection).
*/
declare enum Direction {
/**
Left-to-right.
*/
LTR = 0,
/**
Right-to-left.
*/
RTL = 1
}
/**
Represents a contiguous range of text that has a single direction
(as in left-to-right or right-to-left).
*/
declare class BidiSpan {
/**
The start of the span (relative to the start of the line).
*/
readonly from: number;
/**
The end of the span.
*/
readonly to: number;
/**
The ["bidi
level"](https://unicode.org/reports/tr9/#Basic_Display_Algorithm)
of the span (in this context, 0 means
left-to-right, 1 means right-to-left, 2 means left-to-right
number inside right-to-left text).
*/
readonly level: number;
/**
The direction of this span.
*/
get dir(): Direction;
}
/**
The type of object given to the [`EditorView`](https://codemirror.net/6/docs/ref/#view.EditorView)
constructor.
@ -621,11 +654,23 @@ interface EditorViewConfig extends EditorStateConfig {
*/
root?: Document | ShadowRoot;
/**
Override the transaction [dispatch
function](https://codemirror.net/6/docs/ref/#view.EditorView.dispatch) for this editor view, which
is the way updates get routed to the view. Your implementation,
if provided, should probably call the view's [`update`
method](https://codemirror.net/6/docs/ref/#view.EditorView.update).
Pass an effect created with
[`EditorView.scrollIntoView`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView) or
[`EditorView.scrollSnapshot`](https://codemirror.net/6/docs/ref/#view.EditorView.scrollSnapshot)
here to set an initial scroll position.
*/
scrollTo?: StateEffect<any>;
/**
Override the way transactions are
[dispatched](https://codemirror.net/6/docs/ref/#view.EditorView.dispatch) for this editor view.
Your implementation, if provided, should probably call the
view's [`update` method](https://codemirror.net/6/docs/ref/#view.EditorView.update).
*/
dispatchTransactions?: (trs: readonly Transaction[], view: EditorView) => void;
/**
**Deprecated** single-transaction version of
`dispatchTransactions`. Will force transactions to be dispatched
one at a time when used.
*/
dispatch?: (tr: Transaction, view: EditorView) => void;
}
@ -681,7 +726,7 @@ declare class EditorView {
composition there.
*/
get compositionStarted(): boolean;
private _dispatch;
private dispatchTransactions;
private _root;
/**
The document or shadow root that the view lives in.
@ -721,14 +766,19 @@ declare class EditorView {
constructor(config?: EditorViewConfig);
/**
All regular editor state updates should go through this. It
takes a transaction or transaction spec and updates the view to
show the new state produced by that transaction. Its
implementation can be overridden with an
[option](https://codemirror.net/6/docs/ref/#view.EditorView.constructor^config.dispatch). This
function is bound to the view instance, so it does not have to
be called as a method.
takes a transaction, array of transactions, or transaction spec
and updates the view to show the new state produced by that
transaction. Its implementation can be overridden with an
[option](https://codemirror.net/6/docs/ref/#view.EditorView.constructor^config.dispatchTransactions).
This function is bound to the view instance, so it does not have
to be called as a method.
Note that when multiple `TransactionSpec` arguments are
provided, these define a single transaction (the specs will be
merged), not a sequence of transactions.
*/
dispatch(tr: Transaction): void;
dispatch(trs: readonly Transaction[]): void;
dispatch(...specs: TransactionSpec[]): void;
/**
Update the view for the given array of transactions. This will
@ -748,6 +798,7 @@ declare class EditorView {
*/
setState(newState: EditorState): void;
private updatePlugins;
private docViewUpdate;
/**
Get the CSS classes for the currently active editor themes.
*/
@ -786,6 +837,16 @@ declare class EditorView {
bottom: number;
};
/**
If the editor is transformed with CSS, this provides the scale
along the X axis. Otherwise, it will just be 1. Note that
transforms other than translation and scaling are not supported.
*/
get scaleX(): number;
/**
Provide the CSS transformed scale along the Y axis.
*/
get scaleY(): number;
/**
Find the text line or block widget at the given vertical
position (which is interpreted as relative to the [top of the
document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop)).
@ -842,6 +903,13 @@ declare class EditorView {
*/
moveByGroup(start: SelectionRange, forward: boolean): SelectionRange;
/**
Get the cursor position visually at the start or end of a line.
Note that this may differ from the _logical_ position at its
start or end (which is simply at `line.from`/`line.to`) if text
at the start or end goes against the line's base text direction.
*/
visualLineSide(line: Line, end: boolean): SelectionRange;
/**
Move to the next line boundary in the given direction. If
`includeWrap` is true, line wrapping is on, and there is a
further wrap point on the current line, the wrap point will be
@ -1000,15 +1068,30 @@ declare class EditorView {
/**
Extra vertical distance to add when moving something into
view. Not used with the `"center"` strategy. Defaults to 5.
Must be less than the height of the editor.
*/
yMargin?: number;
/**
Extra horizontal distance to add. Not used with the `"center"`
strategy. Defaults to 5.
strategy. Defaults to 5. Must be less than the width of the
editor.
*/
xMargin?: number;
}): StateEffect<unknown>;
/**
Return an effect that resets the editor to its current (at the
time this method was called) scroll position. Note that this
only affects the editor's own scrollable element, not parents.
See also
[`EditorViewConfig.scrollTo`](https://codemirror.net/6/docs/ref/#view.EditorViewConfig.scrollTo).
The effect should be used with a document identical to the one
it was created for. Failing to do so is not an error, but may
not scroll to the expected position. You can
[map](https://codemirror.net/6/docs/ref/#state.StateEffect.map) the effect to account for changes.
*/
scrollSnapshot(): StateEffect<ScrollTarget>;
/**
Facet to add a [style
module](https://github.com/marijnh/style-mod#documentation) to
an editor view. The view will ensure that the module is
@ -1030,13 +1113,43 @@ declare class EditorView {
*/
static domEventHandlers(handlers: DOMEventHandlers<any>): Extension;
/**
Create an extension that registers DOM event observers. Contrary
to event [handlers](https://codemirror.net/6/docs/ref/#view.EditorView^domEventHandlers),
observers can't be prevented from running by a higher-precedence
handler returning true. They also don't prevent other handlers
and observers from running when they return true, and should not
call `preventDefault`.
*/
static domEventObservers(observers: DOMEventHandlers<any>): Extension;
/**
An input handler can override the way changes to the editable
DOM content are handled. Handlers are passed the document
positions between which the change was found, and the new
content. When one returns true, no further input handlers are
called and the default behavior is prevented.
The `insert` argument can be used to get the default transaction
that would be applied for this input. This can be useful when
dispatching the custom behavior as a separate transaction.
*/
static inputHandler: Facet<(view: EditorView, from: number, to: number, text: string) => boolean, readonly ((view: EditorView, from: number, to: number, text: string) => boolean)[]>;
static inputHandler: Facet<(view: EditorView, from: number, to: number, text: string, insert: () => Transaction) => boolean, readonly ((view: EditorView, from: number, to: number, text: string, insert: () => Transaction) => boolean)[]>;
/**
Scroll handlers can override how things are scrolled into view.
If they return `true`, no further handling happens for the
scrolling. If they return false, the default scroll behavior is
applied. Scroll handlers should never initiate editor updates.
*/
static scrollHandler: Facet<(view: EditorView, range: SelectionRange, options: {
x: ScrollStrategy;
y: ScrollStrategy;
xMargin: number;
yMargin: number;
}) => boolean, readonly ((view: EditorView, range: SelectionRange, options: {
x: ScrollStrategy;
y: ScrollStrategy;
xMargin: number;
yMargin: number;
}) => boolean)[]>;
/**
This facet can be used to provide functions that create effects
to be dispatched when the editor's focus state changes.
@ -1110,6 +1223,16 @@ declare class EditorView {
*/
static decorations: Facet<DecorationSet | ((view: EditorView) => DecorationSet), readonly (DecorationSet | ((view: EditorView) => DecorationSet))[]>;
/**
Facet that works much like
[`decorations`](https://codemirror.net/6/docs/ref/#view.EditorView^decorations), but puts its
inputs at the very bottom of the precedence stack, meaning mark
decorations provided here will only be split by other, partially
overlapping \`outerDecorations\` ranges, and wrap around all
regular decorations. Use this for mark elements that should, as
much as possible, remain in one piece.
*/
static outerDecorations: Facet<DecorationSet | ((view: EditorView) => DecorationSet), readonly (DecorationSet | ((view: EditorView) => DecorationSet))[]>;
/**
Used to provide ranges that should be treated as atoms as far as
cursor motion is concerned. This causes methods like
[`moveByChar`](https://codemirror.net/6/docs/ref/#view.EditorView.moveByChar) and
@ -1122,6 +1245,16 @@ declare class EditorView {
*/
static atomicRanges: Facet<(view: EditorView) => _codemirror_state.RangeSet<any>, readonly ((view: EditorView) => _codemirror_state.RangeSet<any>)[]>;
/**
When range decorations add a `unicode-bidi: isolate` style, they
should also include a
[`bidiIsolate`](https://codemirror.net/6/docs/ref/#view.MarkDecorationSpec.bidiIsolate) property
in their decoration spec, and be exposed through this facet, so
that the editor can compute the proper text order. (Other values
for `unicode-bidi`, except of course `normal`, are not
supported.)
*/
static bidiIsolatedRanges: Facet<DecorationSet | ((view: EditorView) => DecorationSet), readonly (DecorationSet | ((view: EditorView) => DecorationSet))[]>;
/**
Facet that allows extensions to provide additional scroll
margins (space around the sides of the scrolling element that
should be considered invisible). This can be useful when the
@ -1170,6 +1303,12 @@ declare class EditorView {
[selector: string]: StyleSpec;
}): Extension;
/**
Provides a Content Security Policy nonce to use when creating
the style sheets for the editor. Holds the empty string when no
nonce has been provided.
*/
static cspNonce: Facet<string, string>;
/**
Facet that provides additional DOM attributes for the editor's
editable DOM element.
*/
@ -1213,7 +1352,7 @@ to hold the appropriate event object type. For unknown events, it
is inferred to `any`, and should be explicitly set if you want type
checking.
*/
declare type DOMEventHandlers<This> = {
type DOMEventHandlers<This> = {
[event in keyof DOMEventMap]?: (this: This, event: DOMEventMap[event], view: EditorView) => boolean | void;
};
@ -1322,7 +1461,7 @@ handlers handled it.
*/
declare function runScopeHandlers(view: EditorView, event: KeyboardEvent, scope: string): boolean;
declare type SelectionConfig = {
type SelectionConfig = {
/**
The length of a full cursor blink cycle, in milliseconds.
Defaults to 1200. Can be set to 0 to disable blinking.
@ -1353,6 +1492,12 @@ layout information that's only available after laying out the
content).
*/
declare function drawSelection(config?: SelectionConfig): Extension;
/**
Retrieve the [`drawSelection`](https://codemirror.net/6/docs/ref/#view.drawSelection) configuration
for this state. (Note that this will return a set of defaults even
if `drawSelection` isn't enabled.)
*/
declare function getDrawSelectionConfig(state: EditorState): SelectionConfig;
/**
Draws a cursor at the current drop position when something is
@ -1452,15 +1597,43 @@ a rectangle at a given set of coordinates.
*/
declare class RectangleMarker implements LayerMarker {
private className;
private left;
private top;
private width;
private height;
/**
The left position of the marker (in pixels, document-relative).
*/
readonly left: number;
/**
The top position of the marker.
*/
readonly top: number;
/**
The width of the marker, or null if it shouldn't get a width assigned.
*/
readonly width: number | null;
/**
The height of the marker.
*/
readonly height: number;
/**
Create a marker with the given class and dimensions. If `width`
is null, the DOM element will get no width style.
*/
constructor(className: string, left: number, top: number, width: number | null, height: number);
constructor(className: string,
/**
The left position of the marker (in pixels, document-relative).
*/
left: number,
/**
The top position of the marker.
*/
top: number,
/**
The width of the marker, or null if it shouldn't get a width assigned.
*/
width: number | null,
/**
The height of the marker.
*/
height: number);
draw(): HTMLDivElement;
update(elt: HTMLElement, prev: RectangleMarker): boolean;
private adjust;
@ -1490,6 +1663,11 @@ interface LayerConfig {
*/
update(update: ViewUpdate, layer: HTMLElement): boolean;
/**
Whether to update this layer every time the document view
changes. Defaults to true.
*/
updateOnDocViewUpdate?: boolean;
/**
Build a set of markers for this layer, and measure their
dimensions.
*/
@ -1617,6 +1795,9 @@ declare function tooltips(config?: {
On iOS, which at the time of writing still doesn't properly
support fixed positioning, the library always uses absolute
positioning.
If the tooltip parent element sits in a transformed element, the
library also falls back to absolute positioning.
*/
position?: "fixed" | "absolute";
/**
@ -1738,6 +1919,7 @@ interface TooltipView {
Facet to which an extension can add a value to show a tooltip.
*/
declare const showTooltip: Facet<Tooltip | null, readonly (Tooltip | null)[]>;
type HoverSource = (view: EditorView, pos: number, side: -1 | 1) => Tooltip | readonly Tooltip[] | null | Promise<Tooltip | readonly Tooltip[] | null>;
/**
Set up a hover tooltip, which shows up when the pointer hovers
over ranges of text. The callback is called when the mouse hovers
@ -1751,7 +1933,7 @@ Note that all hover tooltips are hosted within a single tooltip
container element. This allows multiple tooltips over the same
range to be "merged" together without overlapping.
*/
declare function hoverTooltip(source: (view: EditorView, pos: number, side: -1 | 1) => Tooltip | null | Promise<Tooltip | null>, options?: {
declare function hoverTooltip(source: HoverSource, options?: {
/**
Controls whether a transaction hides the tooltip. The default
is to not hide.
@ -1788,7 +1970,7 @@ invalidate the existing tooltip positions.
*/
declare function repositionTooltips(view: EditorView): void;
declare type PanelConfig = {
type PanelConfig = {
/**
By default, panels will be placed inside the editor's DOM
structure. You can use this option to override where panels with
@ -1842,7 +2024,7 @@ declare function getPanel(view: EditorView, panel: PanelConstructor): Panel | nu
A function that initializes a panel. Used in
[`showPanel`](https://codemirror.net/6/docs/ref/#view.showPanel).
*/
declare type PanelConstructor = (view: EditorView) => Panel;
type PanelConstructor = (view: EditorView) => Panel;
/**
Opening a panel is done by providing a constructor function for
the panel through this facet. (The panel is closed again when its
@ -1883,7 +2065,7 @@ Markers given to this facet should _only_ define an
in all gutters for the line).
*/
declare const gutterLineClass: Facet<RangeSet<GutterMarker>, readonly RangeSet<GutterMarker>[]>;
declare type Handlers = {
type Handlers = {
[event: string]: (view: EditorView, line: BlockInfo, event: Event) => boolean;
};
interface GutterConfig {
@ -1986,4 +2168,4 @@ trailing whitespace.
*/
declare function highlightTrailingWhitespace(): Extension;
export { BidiSpan, BlockInfo, BlockType, Command, DOMEventHandlers, DOMEventMap, Decoration, DecorationSet, Direction, EditorView, EditorViewConfig, GutterMarker, KeyBinding, LayerMarker, MatchDecorator, MouseSelectionStyle, Panel, PanelConstructor, PluginSpec, PluginValue, Rect, RectangleMarker, Tooltip, TooltipView, ViewPlugin, ViewUpdate, WidgetType, closeHoverTooltips, crosshairCursor, drawSelection, dropCursor, getPanel, getTooltip, gutter, gutterLineClass, gutters, hasHoverTooltips, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, highlightTrailingWhitespace, highlightWhitespace, hoverTooltip, keymap, layer, lineNumberMarkers, lineNumbers, logException, panels, placeholder, rectangularSelection, repositionTooltips, runScopeHandlers, scrollPastEnd, showPanel, showTooltip, tooltips };
export { BidiSpan, BlockInfo, BlockType, type Command, type DOMEventHandlers, type DOMEventMap, Decoration, type DecorationSet, Direction, EditorView, type EditorViewConfig, GutterMarker, type KeyBinding, type LayerMarker, MatchDecorator, type MouseSelectionStyle, type Panel, type PanelConstructor, type PluginSpec, type PluginValue, type Rect, RectangleMarker, type Tooltip, type TooltipView, ViewPlugin, ViewUpdate, WidgetType, closeHoverTooltips, crosshairCursor, drawSelection, dropCursor, getDrawSelectionConfig, getPanel, getTooltip, gutter, gutterLineClass, gutters, hasHoverTooltips, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, highlightTrailingWhitespace, highlightWhitespace, hoverTooltip, keymap, layer, lineNumberMarkers, lineNumbers, logException, panels, placeholder, rectangularSelection, repositionTooltips, runScopeHandlers, scrollPastEnd, showPanel, showTooltip, tooltips };

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{
"name": "@codemirror/view",
"version": "6.16.0",
"version": "6.26.1",
"description": "DOM view component for the CodeMirror code editor",
"scripts": {
"test": "cm-runtests",
@ -26,8 +26,8 @@
"sideEffects": false,
"license": "MIT",
"dependencies": {
"@codemirror/state": "^6.1.4",
"style-mod": "^4.0.0",
"@codemirror/state": "^6.4.0",
"style-mod": "^4.1.0",
"w3c-keyname": "^2.2.4"
},
"devDependencies": {