1282 lines
49 KiB
JavaScript
1282 lines
49 KiB
JavaScript
import {
|
|
IndentContext,
|
|
getIndentUnit,
|
|
getIndentation,
|
|
indentString,
|
|
indentUnit,
|
|
matchBrackets,
|
|
syntaxTree
|
|
} from "./chunk-D63OSQ34.js";
|
|
import {
|
|
Direction,
|
|
EditorView
|
|
} from "./chunk-LORPBXGU.js";
|
|
import {
|
|
Annotation,
|
|
ChangeDesc,
|
|
ChangeSet,
|
|
CharCategory,
|
|
EditorSelection,
|
|
Facet,
|
|
StateEffect,
|
|
StateField,
|
|
Text,
|
|
Transaction,
|
|
combineConfig,
|
|
countColumn,
|
|
findClusterBreak
|
|
} from "./chunk-MKFMOIK6.js";
|
|
import {
|
|
NodeProp
|
|
} from "./chunk-BSVZPYOD.js";
|
|
|
|
// node_modules/@codemirror/commands/dist/index.js
|
|
var toggleComment = (target) => {
|
|
let { state } = target, line = state.doc.lineAt(state.selection.main.from), config = getConfig(target.state, line.from);
|
|
return config.line ? toggleLineComment(target) : config.block ? toggleBlockCommentByLine(target) : false;
|
|
};
|
|
function command(f, option) {
|
|
return ({ state, dispatch }) => {
|
|
if (state.readOnly)
|
|
return false;
|
|
let tr = f(option, state);
|
|
if (!tr)
|
|
return false;
|
|
dispatch(state.update(tr));
|
|
return true;
|
|
};
|
|
}
|
|
var toggleLineComment = command(
|
|
changeLineComment,
|
|
0
|
|
/* CommentOption.Toggle */
|
|
);
|
|
var lineComment = command(
|
|
changeLineComment,
|
|
1
|
|
/* CommentOption.Comment */
|
|
);
|
|
var lineUncomment = command(
|
|
changeLineComment,
|
|
2
|
|
/* CommentOption.Uncomment */
|
|
);
|
|
var toggleBlockComment = command(
|
|
changeBlockComment,
|
|
0
|
|
/* CommentOption.Toggle */
|
|
);
|
|
var blockComment = command(
|
|
changeBlockComment,
|
|
1
|
|
/* CommentOption.Comment */
|
|
);
|
|
var blockUncomment = command(
|
|
changeBlockComment,
|
|
2
|
|
/* CommentOption.Uncomment */
|
|
);
|
|
var toggleBlockCommentByLine = command(
|
|
(o, s) => changeBlockComment(o, s, selectedLineRanges(s)),
|
|
0
|
|
/* CommentOption.Toggle */
|
|
);
|
|
function getConfig(state, pos) {
|
|
let data = state.languageDataAt("commentTokens", pos);
|
|
return data.length ? data[0] : {};
|
|
}
|
|
var SearchMargin = 50;
|
|
function findBlockComment(state, { open, close }, from, to) {
|
|
let textBefore = state.sliceDoc(from - SearchMargin, from);
|
|
let textAfter = state.sliceDoc(to, to + SearchMargin);
|
|
let spaceBefore = /\s*$/.exec(textBefore)[0].length, spaceAfter = /^\s*/.exec(textAfter)[0].length;
|
|
let beforeOff = textBefore.length - spaceBefore;
|
|
if (textBefore.slice(beforeOff - open.length, beforeOff) == open && textAfter.slice(spaceAfter, spaceAfter + close.length) == close) {
|
|
return {
|
|
open: { pos: from - spaceBefore, margin: spaceBefore && 1 },
|
|
close: { pos: to + spaceAfter, margin: spaceAfter && 1 }
|
|
};
|
|
}
|
|
let startText, endText;
|
|
if (to - from <= 2 * SearchMargin) {
|
|
startText = endText = state.sliceDoc(from, to);
|
|
} else {
|
|
startText = state.sliceDoc(from, from + SearchMargin);
|
|
endText = state.sliceDoc(to - SearchMargin, to);
|
|
}
|
|
let startSpace = /^\s*/.exec(startText)[0].length, endSpace = /\s*$/.exec(endText)[0].length;
|
|
let endOff = endText.length - endSpace - close.length;
|
|
if (startText.slice(startSpace, startSpace + open.length) == open && endText.slice(endOff, endOff + close.length) == close) {
|
|
return {
|
|
open: {
|
|
pos: from + startSpace + open.length,
|
|
margin: /\s/.test(startText.charAt(startSpace + open.length)) ? 1 : 0
|
|
},
|
|
close: {
|
|
pos: to - endSpace - close.length,
|
|
margin: /\s/.test(endText.charAt(endOff - 1)) ? 1 : 0
|
|
}
|
|
};
|
|
}
|
|
return null;
|
|
}
|
|
function selectedLineRanges(state) {
|
|
let ranges = [];
|
|
for (let r of state.selection.ranges) {
|
|
let fromLine = state.doc.lineAt(r.from);
|
|
let toLine = r.to <= fromLine.to ? fromLine : state.doc.lineAt(r.to);
|
|
let last = ranges.length - 1;
|
|
if (last >= 0 && ranges[last].to > fromLine.from)
|
|
ranges[last].to = toLine.to;
|
|
else
|
|
ranges.push({ from: fromLine.from + /^\s*/.exec(fromLine.text)[0].length, to: toLine.to });
|
|
}
|
|
return ranges;
|
|
}
|
|
function changeBlockComment(option, state, ranges = state.selection.ranges) {
|
|
let tokens = ranges.map((r) => getConfig(state, r.from).block);
|
|
if (!tokens.every((c) => c))
|
|
return null;
|
|
let comments = ranges.map((r, i) => findBlockComment(state, tokens[i], r.from, r.to));
|
|
if (option != 2 && !comments.every((c) => c)) {
|
|
return { changes: state.changes(ranges.map((range, i) => {
|
|
if (comments[i])
|
|
return [];
|
|
return [{ from: range.from, insert: tokens[i].open + " " }, { from: range.to, insert: " " + tokens[i].close }];
|
|
})) };
|
|
} else if (option != 1 && comments.some((c) => c)) {
|
|
let changes = [];
|
|
for (let i = 0, comment; i < comments.length; i++)
|
|
if (comment = comments[i]) {
|
|
let token = tokens[i], { open, close } = comment;
|
|
changes.push({ from: open.pos - token.open.length, to: open.pos + open.margin }, { from: close.pos - close.margin, to: close.pos + token.close.length });
|
|
}
|
|
return { changes };
|
|
}
|
|
return null;
|
|
}
|
|
function changeLineComment(option, state, ranges = state.selection.ranges) {
|
|
let lines = [];
|
|
let prevLine = -1;
|
|
for (let { from, to } of ranges) {
|
|
let startI = lines.length, minIndent = 1e9;
|
|
let token = getConfig(state, from).line;
|
|
if (!token)
|
|
continue;
|
|
for (let pos = from; pos <= to; ) {
|
|
let line = state.doc.lineAt(pos);
|
|
if (line.from > prevLine && (from == to || to > line.from)) {
|
|
prevLine = line.from;
|
|
let indent = /^\s*/.exec(line.text)[0].length;
|
|
let empty = indent == line.length;
|
|
let comment = line.text.slice(indent, indent + token.length) == token ? indent : -1;
|
|
if (indent < line.text.length && indent < minIndent)
|
|
minIndent = indent;
|
|
lines.push({ line, comment, token, indent, empty, single: false });
|
|
}
|
|
pos = line.to + 1;
|
|
}
|
|
if (minIndent < 1e9) {
|
|
for (let i = startI; i < lines.length; i++)
|
|
if (lines[i].indent < lines[i].line.text.length)
|
|
lines[i].indent = minIndent;
|
|
}
|
|
if (lines.length == startI + 1)
|
|
lines[startI].single = true;
|
|
}
|
|
if (option != 2 && lines.some((l) => l.comment < 0 && (!l.empty || l.single))) {
|
|
let changes = [];
|
|
for (let { line, token, indent, empty, single } of lines)
|
|
if (single || !empty)
|
|
changes.push({ from: line.from + indent, insert: token + " " });
|
|
let changeSet = state.changes(changes);
|
|
return { changes: changeSet, selection: state.selection.map(changeSet, 1) };
|
|
} else if (option != 1 && lines.some((l) => l.comment >= 0)) {
|
|
let changes = [];
|
|
for (let { line, comment, token } of lines)
|
|
if (comment >= 0) {
|
|
let from = line.from + comment, to = from + token.length;
|
|
if (line.text[to - line.from] == " ")
|
|
to++;
|
|
changes.push({ from, to });
|
|
}
|
|
return { changes };
|
|
}
|
|
return null;
|
|
}
|
|
var fromHistory = Annotation.define();
|
|
var isolateHistory = Annotation.define();
|
|
var invertedEffects = Facet.define();
|
|
var historyConfig = Facet.define({
|
|
combine(configs) {
|
|
return combineConfig(configs, {
|
|
minDepth: 100,
|
|
newGroupDelay: 500,
|
|
joinToEvent: (_t, isAdjacent2) => isAdjacent2
|
|
}, {
|
|
minDepth: Math.max,
|
|
newGroupDelay: Math.min,
|
|
joinToEvent: (a, b) => (tr, adj) => a(tr, adj) || b(tr, adj)
|
|
});
|
|
}
|
|
});
|
|
function changeEnd(changes) {
|
|
let end = 0;
|
|
changes.iterChangedRanges((_, to) => end = to);
|
|
return end;
|
|
}
|
|
var historyField_ = StateField.define({
|
|
create() {
|
|
return HistoryState.empty;
|
|
},
|
|
update(state, tr) {
|
|
let config = tr.state.facet(historyConfig);
|
|
let fromHist = tr.annotation(fromHistory);
|
|
if (fromHist) {
|
|
let selection = tr.docChanged ? EditorSelection.single(changeEnd(tr.changes)) : void 0;
|
|
let item = HistEvent.fromTransaction(tr, selection), from = fromHist.side;
|
|
let other = from == 0 ? state.undone : state.done;
|
|
if (item)
|
|
other = updateBranch(other, other.length, config.minDepth, item);
|
|
else
|
|
other = addSelection(other, tr.startState.selection);
|
|
return new HistoryState(from == 0 ? fromHist.rest : other, from == 0 ? other : fromHist.rest);
|
|
}
|
|
let isolate = tr.annotation(isolateHistory);
|
|
if (isolate == "full" || isolate == "before")
|
|
state = state.isolate();
|
|
if (tr.annotation(Transaction.addToHistory) === false)
|
|
return !tr.changes.empty ? state.addMapping(tr.changes.desc) : state;
|
|
let event = HistEvent.fromTransaction(tr);
|
|
let time = tr.annotation(Transaction.time), userEvent = tr.annotation(Transaction.userEvent);
|
|
if (event)
|
|
state = state.addChanges(event, time, userEvent, config, tr);
|
|
else if (tr.selection)
|
|
state = state.addSelection(tr.startState.selection, time, userEvent, config.newGroupDelay);
|
|
if (isolate == "full" || isolate == "after")
|
|
state = state.isolate();
|
|
return state;
|
|
},
|
|
toJSON(value) {
|
|
return { done: value.done.map((e) => e.toJSON()), undone: value.undone.map((e) => e.toJSON()) };
|
|
},
|
|
fromJSON(json) {
|
|
return new HistoryState(json.done.map(HistEvent.fromJSON), json.undone.map(HistEvent.fromJSON));
|
|
}
|
|
});
|
|
function history(config = {}) {
|
|
return [
|
|
historyField_,
|
|
historyConfig.of(config),
|
|
EditorView.domEventHandlers({
|
|
beforeinput(e, view) {
|
|
let command2 = e.inputType == "historyUndo" ? undo : e.inputType == "historyRedo" ? redo : null;
|
|
if (!command2)
|
|
return false;
|
|
e.preventDefault();
|
|
return command2(view);
|
|
}
|
|
})
|
|
];
|
|
}
|
|
var historyField = historyField_;
|
|
function cmd(side, selection) {
|
|
return function({ state, dispatch }) {
|
|
if (!selection && state.readOnly)
|
|
return false;
|
|
let historyState = state.field(historyField_, false);
|
|
if (!historyState)
|
|
return false;
|
|
let tr = historyState.pop(side, state, selection);
|
|
if (!tr)
|
|
return false;
|
|
dispatch(tr);
|
|
return true;
|
|
};
|
|
}
|
|
var undo = cmd(0, false);
|
|
var redo = cmd(1, false);
|
|
var undoSelection = cmd(0, true);
|
|
var redoSelection = cmd(1, true);
|
|
function depth(side) {
|
|
return function(state) {
|
|
let histState = state.field(historyField_, false);
|
|
if (!histState)
|
|
return 0;
|
|
let branch = side == 0 ? histState.done : histState.undone;
|
|
return branch.length - (branch.length && !branch[0].changes ? 1 : 0);
|
|
};
|
|
}
|
|
var undoDepth = depth(
|
|
0
|
|
/* BranchName.Done */
|
|
);
|
|
var redoDepth = depth(
|
|
1
|
|
/* BranchName.Undone */
|
|
);
|
|
var HistEvent = class _HistEvent {
|
|
constructor(changes, effects, mapped, startSelection, selectionsAfter) {
|
|
this.changes = changes;
|
|
this.effects = effects;
|
|
this.mapped = mapped;
|
|
this.startSelection = startSelection;
|
|
this.selectionsAfter = selectionsAfter;
|
|
}
|
|
setSelAfter(after) {
|
|
return new _HistEvent(this.changes, this.effects, this.mapped, this.startSelection, after);
|
|
}
|
|
toJSON() {
|
|
var _a, _b, _c;
|
|
return {
|
|
changes: (_a = this.changes) === null || _a === void 0 ? void 0 : _a.toJSON(),
|
|
mapped: (_b = this.mapped) === null || _b === void 0 ? void 0 : _b.toJSON(),
|
|
startSelection: (_c = this.startSelection) === null || _c === void 0 ? void 0 : _c.toJSON(),
|
|
selectionsAfter: this.selectionsAfter.map((s) => s.toJSON())
|
|
};
|
|
}
|
|
static fromJSON(json) {
|
|
return new _HistEvent(json.changes && ChangeSet.fromJSON(json.changes), [], json.mapped && ChangeDesc.fromJSON(json.mapped), json.startSelection && EditorSelection.fromJSON(json.startSelection), json.selectionsAfter.map(EditorSelection.fromJSON));
|
|
}
|
|
// This does not check `addToHistory` and such, it assumes the
|
|
// transaction needs to be converted to an item. Returns null when
|
|
// there are no changes or effects in the transaction.
|
|
static fromTransaction(tr, selection) {
|
|
let effects = none;
|
|
for (let invert of tr.startState.facet(invertedEffects)) {
|
|
let result = invert(tr);
|
|
if (result.length)
|
|
effects = effects.concat(result);
|
|
}
|
|
if (!effects.length && tr.changes.empty)
|
|
return null;
|
|
return new _HistEvent(tr.changes.invert(tr.startState.doc), effects, void 0, selection || tr.startState.selection, none);
|
|
}
|
|
static selection(selections) {
|
|
return new _HistEvent(void 0, none, void 0, void 0, selections);
|
|
}
|
|
};
|
|
function updateBranch(branch, to, maxLen, newEvent) {
|
|
let start = to + 1 > maxLen + 20 ? to - maxLen - 1 : 0;
|
|
let newBranch = branch.slice(start, to);
|
|
newBranch.push(newEvent);
|
|
return newBranch;
|
|
}
|
|
function isAdjacent(a, b) {
|
|
let ranges = [], isAdjacent2 = false;
|
|
a.iterChangedRanges((f, t) => ranges.push(f, t));
|
|
b.iterChangedRanges((_f, _t, f, t) => {
|
|
for (let i = 0; i < ranges.length; ) {
|
|
let from = ranges[i++], to = ranges[i++];
|
|
if (t >= from && f <= to)
|
|
isAdjacent2 = true;
|
|
}
|
|
});
|
|
return isAdjacent2;
|
|
}
|
|
function eqSelectionShape(a, b) {
|
|
return a.ranges.length == b.ranges.length && a.ranges.filter((r, i) => r.empty != b.ranges[i].empty).length === 0;
|
|
}
|
|
function conc(a, b) {
|
|
return !a.length ? b : !b.length ? a : a.concat(b);
|
|
}
|
|
var none = [];
|
|
var MaxSelectionsPerEvent = 200;
|
|
function addSelection(branch, selection) {
|
|
if (!branch.length) {
|
|
return [HistEvent.selection([selection])];
|
|
} else {
|
|
let lastEvent = branch[branch.length - 1];
|
|
let sels = lastEvent.selectionsAfter.slice(Math.max(0, lastEvent.selectionsAfter.length - MaxSelectionsPerEvent));
|
|
if (sels.length && sels[sels.length - 1].eq(selection))
|
|
return branch;
|
|
sels.push(selection);
|
|
return updateBranch(branch, branch.length - 1, 1e9, lastEvent.setSelAfter(sels));
|
|
}
|
|
}
|
|
function popSelection(branch) {
|
|
let last = branch[branch.length - 1];
|
|
let newBranch = branch.slice();
|
|
newBranch[branch.length - 1] = last.setSelAfter(last.selectionsAfter.slice(0, last.selectionsAfter.length - 1));
|
|
return newBranch;
|
|
}
|
|
function addMappingToBranch(branch, mapping) {
|
|
if (!branch.length)
|
|
return branch;
|
|
let length = branch.length, selections = none;
|
|
while (length) {
|
|
let event = mapEvent(branch[length - 1], mapping, selections);
|
|
if (event.changes && !event.changes.empty || event.effects.length) {
|
|
let result = branch.slice(0, length);
|
|
result[length - 1] = event;
|
|
return result;
|
|
} else {
|
|
mapping = event.mapped;
|
|
length--;
|
|
selections = event.selectionsAfter;
|
|
}
|
|
}
|
|
return selections.length ? [HistEvent.selection(selections)] : none;
|
|
}
|
|
function mapEvent(event, mapping, extraSelections) {
|
|
let selections = conc(event.selectionsAfter.length ? event.selectionsAfter.map((s) => s.map(mapping)) : none, extraSelections);
|
|
if (!event.changes)
|
|
return HistEvent.selection(selections);
|
|
let mappedChanges = event.changes.map(mapping), before = mapping.mapDesc(event.changes, true);
|
|
let fullMapping = event.mapped ? event.mapped.composeDesc(before) : before;
|
|
return new HistEvent(mappedChanges, StateEffect.mapEffects(event.effects, mapping), fullMapping, event.startSelection.map(before), selections);
|
|
}
|
|
var joinableUserEvent = /^(input\.type|delete)($|\.)/;
|
|
var HistoryState = class _HistoryState {
|
|
constructor(done, undone, prevTime = 0, prevUserEvent = void 0) {
|
|
this.done = done;
|
|
this.undone = undone;
|
|
this.prevTime = prevTime;
|
|
this.prevUserEvent = prevUserEvent;
|
|
}
|
|
isolate() {
|
|
return this.prevTime ? new _HistoryState(this.done, this.undone) : this;
|
|
}
|
|
addChanges(event, time, userEvent, config, tr) {
|
|
let done = this.done, lastEvent = done[done.length - 1];
|
|
if (lastEvent && lastEvent.changes && !lastEvent.changes.empty && event.changes && (!userEvent || joinableUserEvent.test(userEvent)) && (!lastEvent.selectionsAfter.length && time - this.prevTime < config.newGroupDelay && config.joinToEvent(tr, isAdjacent(lastEvent.changes, event.changes)) || // For compose (but not compose.start) events, always join with previous event
|
|
userEvent == "input.type.compose")) {
|
|
done = updateBranch(done, done.length - 1, config.minDepth, new HistEvent(event.changes.compose(lastEvent.changes), conc(event.effects, lastEvent.effects), lastEvent.mapped, lastEvent.startSelection, none));
|
|
} else {
|
|
done = updateBranch(done, done.length, config.minDepth, event);
|
|
}
|
|
return new _HistoryState(done, none, time, userEvent);
|
|
}
|
|
addSelection(selection, time, userEvent, newGroupDelay) {
|
|
let last = this.done.length ? this.done[this.done.length - 1].selectionsAfter : none;
|
|
if (last.length > 0 && time - this.prevTime < newGroupDelay && userEvent == this.prevUserEvent && userEvent && /^select($|\.)/.test(userEvent) && eqSelectionShape(last[last.length - 1], selection))
|
|
return this;
|
|
return new _HistoryState(addSelection(this.done, selection), this.undone, time, userEvent);
|
|
}
|
|
addMapping(mapping) {
|
|
return new _HistoryState(addMappingToBranch(this.done, mapping), addMappingToBranch(this.undone, mapping), this.prevTime, this.prevUserEvent);
|
|
}
|
|
pop(side, state, selection) {
|
|
let branch = side == 0 ? this.done : this.undone;
|
|
if (branch.length == 0)
|
|
return null;
|
|
let event = branch[branch.length - 1];
|
|
if (selection && event.selectionsAfter.length) {
|
|
return state.update({
|
|
selection: event.selectionsAfter[event.selectionsAfter.length - 1],
|
|
annotations: fromHistory.of({ side, rest: popSelection(branch) }),
|
|
userEvent: side == 0 ? "select.undo" : "select.redo",
|
|
scrollIntoView: true
|
|
});
|
|
} else if (!event.changes) {
|
|
return null;
|
|
} else {
|
|
let rest = branch.length == 1 ? none : branch.slice(0, branch.length - 1);
|
|
if (event.mapped)
|
|
rest = addMappingToBranch(rest, event.mapped);
|
|
return state.update({
|
|
changes: event.changes,
|
|
selection: event.startSelection,
|
|
effects: event.effects,
|
|
annotations: fromHistory.of({ side, rest }),
|
|
filter: false,
|
|
userEvent: side == 0 ? "undo" : "redo",
|
|
scrollIntoView: true
|
|
});
|
|
}
|
|
}
|
|
};
|
|
HistoryState.empty = new HistoryState(none, none);
|
|
var historyKeymap = [
|
|
{ key: "Mod-z", run: undo, preventDefault: true },
|
|
{ key: "Mod-y", mac: "Mod-Shift-z", run: redo, preventDefault: true },
|
|
{ linux: "Ctrl-Shift-z", run: redo, preventDefault: true },
|
|
{ key: "Mod-u", run: undoSelection, preventDefault: true },
|
|
{ key: "Alt-u", mac: "Mod-Shift-u", run: redoSelection, preventDefault: true }
|
|
];
|
|
function updateSel(sel, by) {
|
|
return EditorSelection.create(sel.ranges.map(by), sel.mainIndex);
|
|
}
|
|
function setSel(state, selection) {
|
|
return state.update({ selection, scrollIntoView: true, userEvent: "select" });
|
|
}
|
|
function moveSel({ state, dispatch }, how) {
|
|
let selection = updateSel(state.selection, how);
|
|
if (selection.eq(state.selection))
|
|
return false;
|
|
dispatch(setSel(state, selection));
|
|
return true;
|
|
}
|
|
function rangeEnd(range, forward) {
|
|
return EditorSelection.cursor(forward ? range.to : range.from);
|
|
}
|
|
function cursorByChar(view, forward) {
|
|
return moveSel(view, (range) => range.empty ? view.moveByChar(range, forward) : rangeEnd(range, forward));
|
|
}
|
|
function ltrAtCursor(view) {
|
|
return view.textDirectionAt(view.state.selection.main.head) == Direction.LTR;
|
|
}
|
|
var cursorCharLeft = (view) => cursorByChar(view, !ltrAtCursor(view));
|
|
var cursorCharRight = (view) => cursorByChar(view, ltrAtCursor(view));
|
|
var cursorCharForward = (view) => cursorByChar(view, true);
|
|
var cursorCharBackward = (view) => cursorByChar(view, false);
|
|
function cursorByGroup(view, forward) {
|
|
return moveSel(view, (range) => range.empty ? view.moveByGroup(range, forward) : rangeEnd(range, forward));
|
|
}
|
|
var cursorGroupLeft = (view) => cursorByGroup(view, !ltrAtCursor(view));
|
|
var cursorGroupRight = (view) => cursorByGroup(view, ltrAtCursor(view));
|
|
var cursorGroupForward = (view) => cursorByGroup(view, true);
|
|
var cursorGroupBackward = (view) => cursorByGroup(view, false);
|
|
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;
|
|
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;
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
step(start);
|
|
return step;
|
|
});
|
|
}
|
|
function cursorBySubword(view, forward) {
|
|
return moveSel(view, (range) => range.empty ? moveBySubword(view, range, forward) : rangeEnd(range, forward));
|
|
}
|
|
var cursorSubwordForward = (view) => cursorBySubword(view, true);
|
|
var cursorSubwordBackward = (view) => cursorBySubword(view, false);
|
|
function interestingNode(state, node, bracketProp) {
|
|
if (node.type.prop(bracketProp))
|
|
return true;
|
|
let len = node.to - node.from;
|
|
return len && (len > 2 || /[^\s,.;:]/.test(state.sliceDoc(node.from, node.to))) || node.firstChild;
|
|
}
|
|
function moveBySyntax(state, start, forward) {
|
|
let pos = syntaxTree(state).resolveInner(start.head);
|
|
let bracketProp = forward ? NodeProp.closedBy : NodeProp.openedBy;
|
|
for (let at = start.head; ; ) {
|
|
let next = forward ? pos.childAfter(at) : pos.childBefore(at);
|
|
if (!next)
|
|
break;
|
|
if (interestingNode(state, next, bracketProp))
|
|
pos = next;
|
|
else
|
|
at = forward ? next.to : next.from;
|
|
}
|
|
let bracket = pos.type.prop(bracketProp), match, newPos;
|
|
if (bracket && (match = forward ? matchBrackets(state, pos.from, 1) : matchBrackets(state, pos.to, -1)) && match.matched)
|
|
newPos = forward ? match.end.to : match.end.from;
|
|
else
|
|
newPos = forward ? pos.to : pos.from;
|
|
return EditorSelection.cursor(newPos, forward ? -1 : 1);
|
|
}
|
|
var cursorSyntaxLeft = (view) => moveSel(view, (range) => moveBySyntax(view.state, range, !ltrAtCursor(view)));
|
|
var cursorSyntaxRight = (view) => moveSel(view, (range) => moveBySyntax(view.state, range, ltrAtCursor(view)));
|
|
function cursorByLine(view, forward) {
|
|
return moveSel(view, (range) => {
|
|
if (!range.empty)
|
|
return rangeEnd(range, forward);
|
|
let moved = view.moveVertically(range, forward);
|
|
return moved.head != range.head ? moved : view.moveToLineBoundary(range, forward);
|
|
});
|
|
}
|
|
var cursorLineUp = (view) => cursorByLine(view, false);
|
|
var cursorLineDown = (view) => cursorByLine(view, true);
|
|
function pageInfo(view) {
|
|
let selfScroll = view.scrollDOM.clientHeight < view.scrollDOM.scrollHeight - 2;
|
|
let marginTop = 0, marginBottom = 0, height;
|
|
if (selfScroll) {
|
|
for (let source of view.state.facet(EditorView.scrollMargins)) {
|
|
let margins = source(view);
|
|
if (margins === null || margins === void 0 ? void 0 : margins.top)
|
|
marginTop = Math.max(margins === null || margins === void 0 ? void 0 : margins.top, marginTop);
|
|
if (margins === null || margins === void 0 ? void 0 : margins.bottom)
|
|
marginBottom = Math.max(margins === null || margins === void 0 ? void 0 : margins.bottom, marginBottom);
|
|
}
|
|
height = view.scrollDOM.clientHeight - marginTop - marginBottom;
|
|
} else {
|
|
height = (view.dom.ownerDocument.defaultView || window).innerHeight;
|
|
}
|
|
return {
|
|
marginTop,
|
|
marginBottom,
|
|
selfScroll,
|
|
height: Math.max(view.defaultLineHeight, height - 5)
|
|
};
|
|
}
|
|
function cursorByPage(view, forward) {
|
|
let page = pageInfo(view);
|
|
let { state } = view, selection = updateSel(state.selection, (range) => {
|
|
return range.empty ? view.moveVertically(range, forward, page.height) : rangeEnd(range, forward);
|
|
});
|
|
if (selection.eq(state.selection))
|
|
return false;
|
|
let effect;
|
|
if (page.selfScroll) {
|
|
let startPos = view.coordsAtPos(state.selection.main.head);
|
|
let scrollRect = view.scrollDOM.getBoundingClientRect();
|
|
let scrollTop = scrollRect.top + page.marginTop, scrollBottom = scrollRect.bottom - page.marginBottom;
|
|
if (startPos && startPos.top > scrollTop && startPos.bottom < scrollBottom)
|
|
effect = EditorView.scrollIntoView(selection.main.head, { y: "start", yMargin: startPos.top - scrollTop });
|
|
}
|
|
view.dispatch(setSel(state, selection), { effects: effect });
|
|
return true;
|
|
}
|
|
var cursorPageUp = (view) => cursorByPage(view, false);
|
|
var cursorPageDown = (view) => cursorByPage(view, true);
|
|
function moveByLineBoundary(view, start, forward) {
|
|
let line = view.lineBlockAt(start.head), moved = view.moveToLineBoundary(start, forward);
|
|
if (moved.head == start.head && moved.head != (forward ? line.to : line.from))
|
|
moved = view.moveToLineBoundary(start, forward, false);
|
|
if (!forward && moved.head == line.from && line.length) {
|
|
let space = /^\s*/.exec(view.state.sliceDoc(line.from, Math.min(line.from + 100, line.to)))[0].length;
|
|
if (space && start.head != line.from + space)
|
|
moved = EditorSelection.cursor(line.from + space);
|
|
}
|
|
return moved;
|
|
}
|
|
var cursorLineBoundaryForward = (view) => moveSel(view, (range) => moveByLineBoundary(view, range, true));
|
|
var cursorLineBoundaryBackward = (view) => moveSel(view, (range) => moveByLineBoundary(view, range, false));
|
|
var cursorLineBoundaryLeft = (view) => moveSel(view, (range) => moveByLineBoundary(view, range, !ltrAtCursor(view)));
|
|
var cursorLineBoundaryRight = (view) => moveSel(view, (range) => moveByLineBoundary(view, range, ltrAtCursor(view)));
|
|
var cursorLineStart = (view) => moveSel(view, (range) => EditorSelection.cursor(view.lineBlockAt(range.head).from, 1));
|
|
var cursorLineEnd = (view) => moveSel(view, (range) => EditorSelection.cursor(view.lineBlockAt(range.head).to, -1));
|
|
function toMatchingBracket(state, dispatch, extend) {
|
|
let found = false, selection = updateSel(state.selection, (range) => {
|
|
let matching = matchBrackets(state, range.head, -1) || matchBrackets(state, range.head, 1) || range.head > 0 && matchBrackets(state, range.head - 1, 1) || range.head < state.doc.length && matchBrackets(state, range.head + 1, -1);
|
|
if (!matching || !matching.end)
|
|
return range;
|
|
found = true;
|
|
let head = matching.start.from == range.head ? matching.end.to : matching.end.from;
|
|
return extend ? EditorSelection.range(range.anchor, head) : EditorSelection.cursor(head);
|
|
});
|
|
if (!found)
|
|
return false;
|
|
dispatch(setSel(state, selection));
|
|
return true;
|
|
}
|
|
var cursorMatchingBracket = ({ state, dispatch }) => toMatchingBracket(state, dispatch, false);
|
|
var selectMatchingBracket = ({ state, dispatch }) => toMatchingBracket(state, dispatch, true);
|
|
function extendSel(view, how) {
|
|
let selection = updateSel(view.state.selection, (range) => {
|
|
let head = how(range);
|
|
return EditorSelection.range(range.anchor, head.head, head.goalColumn, head.bidiLevel || void 0);
|
|
});
|
|
if (selection.eq(view.state.selection))
|
|
return false;
|
|
view.dispatch(setSel(view.state, selection));
|
|
return true;
|
|
}
|
|
function selectByChar(view, forward) {
|
|
return extendSel(view, (range) => view.moveByChar(range, forward));
|
|
}
|
|
var selectCharLeft = (view) => selectByChar(view, !ltrAtCursor(view));
|
|
var selectCharRight = (view) => selectByChar(view, ltrAtCursor(view));
|
|
var selectCharForward = (view) => selectByChar(view, true);
|
|
var selectCharBackward = (view) => selectByChar(view, false);
|
|
function selectByGroup(view, forward) {
|
|
return extendSel(view, (range) => view.moveByGroup(range, forward));
|
|
}
|
|
var selectGroupLeft = (view) => selectByGroup(view, !ltrAtCursor(view));
|
|
var selectGroupRight = (view) => selectByGroup(view, ltrAtCursor(view));
|
|
var selectGroupForward = (view) => selectByGroup(view, true);
|
|
var selectGroupBackward = (view) => selectByGroup(view, false);
|
|
function selectBySubword(view, forward) {
|
|
return extendSel(view, (range) => moveBySubword(view, range, forward));
|
|
}
|
|
var selectSubwordForward = (view) => selectBySubword(view, true);
|
|
var selectSubwordBackward = (view) => selectBySubword(view, false);
|
|
var selectSyntaxLeft = (view) => extendSel(view, (range) => moveBySyntax(view.state, range, !ltrAtCursor(view)));
|
|
var selectSyntaxRight = (view) => extendSel(view, (range) => moveBySyntax(view.state, range, ltrAtCursor(view)));
|
|
function selectByLine(view, forward) {
|
|
return extendSel(view, (range) => view.moveVertically(range, forward));
|
|
}
|
|
var selectLineUp = (view) => selectByLine(view, false);
|
|
var selectLineDown = (view) => selectByLine(view, true);
|
|
function selectByPage(view, forward) {
|
|
return extendSel(view, (range) => view.moveVertically(range, forward, pageInfo(view).height));
|
|
}
|
|
var selectPageUp = (view) => selectByPage(view, false);
|
|
var selectPageDown = (view) => selectByPage(view, true);
|
|
var selectLineBoundaryForward = (view) => extendSel(view, (range) => moveByLineBoundary(view, range, true));
|
|
var selectLineBoundaryBackward = (view) => extendSel(view, (range) => moveByLineBoundary(view, range, false));
|
|
var selectLineBoundaryLeft = (view) => extendSel(view, (range) => moveByLineBoundary(view, range, !ltrAtCursor(view)));
|
|
var selectLineBoundaryRight = (view) => extendSel(view, (range) => moveByLineBoundary(view, range, ltrAtCursor(view)));
|
|
var selectLineStart = (view) => extendSel(view, (range) => EditorSelection.cursor(view.lineBlockAt(range.head).from));
|
|
var selectLineEnd = (view) => extendSel(view, (range) => EditorSelection.cursor(view.lineBlockAt(range.head).to));
|
|
var cursorDocStart = ({ state, dispatch }) => {
|
|
dispatch(setSel(state, { anchor: 0 }));
|
|
return true;
|
|
};
|
|
var cursorDocEnd = ({ state, dispatch }) => {
|
|
dispatch(setSel(state, { anchor: state.doc.length }));
|
|
return true;
|
|
};
|
|
var selectDocStart = ({ state, dispatch }) => {
|
|
dispatch(setSel(state, { anchor: state.selection.main.anchor, head: 0 }));
|
|
return true;
|
|
};
|
|
var selectDocEnd = ({ state, dispatch }) => {
|
|
dispatch(setSel(state, { anchor: state.selection.main.anchor, head: state.doc.length }));
|
|
return true;
|
|
};
|
|
var selectAll = ({ state, dispatch }) => {
|
|
dispatch(state.update({ selection: { anchor: 0, head: state.doc.length }, userEvent: "select" }));
|
|
return true;
|
|
};
|
|
var selectLine = ({ state, dispatch }) => {
|
|
let ranges = selectedLineBlocks(state).map(({ from, to }) => EditorSelection.range(from, Math.min(to + 1, state.doc.length)));
|
|
dispatch(state.update({ selection: EditorSelection.create(ranges), userEvent: "select" }));
|
|
return true;
|
|
};
|
|
var 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);
|
|
});
|
|
dispatch(setSel(state, selection));
|
|
return true;
|
|
};
|
|
var simplifySelection = ({ state, dispatch }) => {
|
|
let cur = state.selection, selection = null;
|
|
if (cur.ranges.length > 1)
|
|
selection = EditorSelection.create([cur.main]);
|
|
else if (!cur.main.empty)
|
|
selection = EditorSelection.create([EditorSelection.cursor(cur.main.head)]);
|
|
if (!selection)
|
|
return false;
|
|
dispatch(setSel(state, selection));
|
|
return true;
|
|
};
|
|
function deleteBy(target, by) {
|
|
if (target.state.readOnly)
|
|
return false;
|
|
let event = "delete.selection", { state } = target;
|
|
let changes = state.changeByRange((range) => {
|
|
let { from, to } = range;
|
|
if (from == to) {
|
|
let towards = by(from);
|
|
if (towards < from) {
|
|
event = "delete.backward";
|
|
towards = skipAtomic(target, towards, false);
|
|
} else if (towards > from) {
|
|
event = "delete.forward";
|
|
towards = skipAtomic(target, towards, true);
|
|
}
|
|
from = Math.min(from, towards);
|
|
to = Math.max(to, towards);
|
|
} else {
|
|
from = skipAtomic(target, from, false);
|
|
to = skipAtomic(target, to, true);
|
|
}
|
|
return from == to ? { range } : { changes: { from, to }, range: EditorSelection.cursor(from) };
|
|
});
|
|
if (changes.changes.empty)
|
|
return false;
|
|
target.dispatch(state.update(changes, {
|
|
scrollIntoView: true,
|
|
userEvent: event,
|
|
effects: event == "delete.selection" ? EditorView.announce.of(state.phrase("Selection deleted")) : void 0
|
|
}));
|
|
return true;
|
|
}
|
|
function skipAtomic(target, pos, forward) {
|
|
if (target instanceof EditorView)
|
|
for (let ranges of target.state.facet(EditorView.atomicRanges).map((f) => f(target)))
|
|
ranges.between(pos, pos, (from, to) => {
|
|
if (from < pos && to > pos)
|
|
pos = forward ? to : from;
|
|
});
|
|
return pos;
|
|
}
|
|
var deleteByChar = (target, forward) => deleteBy(target, (pos) => {
|
|
let { 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] == " ")
|
|
return pos - 1;
|
|
let col = countColumn(before, state.tabSize), drop = col % getIndentUnit(state) || getIndentUnit(state);
|
|
for (let i = 0; i < drop && before[before.length - 1 - i] == " "; i++)
|
|
pos--;
|
|
targetPos = pos;
|
|
} else {
|
|
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;
|
|
}
|
|
return targetPos;
|
|
});
|
|
var deleteCharBackward = (view) => deleteByChar(view, false);
|
|
var deleteCharForward = (view) => deleteByChar(view, true);
|
|
var deleteByGroup = (target, forward) => deleteBy(target, (start) => {
|
|
let pos = start, { 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))
|
|
pos += forward ? 1 : -1;
|
|
break;
|
|
}
|
|
let next = findClusterBreak(line.text, pos - line.from, forward) + line.from;
|
|
let nextChar = line.text.slice(Math.min(pos, next) - line.from, Math.max(pos, next) - line.from);
|
|
let nextCat = categorize(nextChar);
|
|
if (cat != null && nextCat != cat)
|
|
break;
|
|
if (nextChar != " " || pos != start)
|
|
cat = nextCat;
|
|
pos = next;
|
|
}
|
|
return pos;
|
|
});
|
|
var deleteGroupBackward = (target) => deleteByGroup(target, false);
|
|
var deleteGroupForward = (target) => deleteByGroup(target, true);
|
|
var deleteToLineEnd = (view) => deleteBy(view, (pos) => {
|
|
let lineEnd = view.lineBlockAt(pos).to;
|
|
return pos < lineEnd ? lineEnd : Math.min(view.state.doc.length, pos + 1);
|
|
});
|
|
var deleteToLineStart = (view) => deleteBy(view, (pos) => {
|
|
let lineStart = view.lineBlockAt(pos).from;
|
|
return pos > lineStart ? lineStart : Math.max(0, pos - 1);
|
|
});
|
|
var deleteTrailingWhitespace = ({ state, dispatch }) => {
|
|
if (state.readOnly)
|
|
return false;
|
|
let changes = [];
|
|
for (let pos = 0, prev = "", iter = state.doc.iter(); ; ) {
|
|
iter.next();
|
|
if (iter.lineBreak || iter.done) {
|
|
let trailing = prev.search(/\s+$/);
|
|
if (trailing > -1)
|
|
changes.push({ from: pos - (prev.length - trailing), to: pos });
|
|
if (iter.done)
|
|
break;
|
|
prev = "";
|
|
} else {
|
|
prev = iter.value;
|
|
}
|
|
pos += iter.value.length;
|
|
}
|
|
if (!changes.length)
|
|
return false;
|
|
dispatch(state.update({ changes, userEvent: "delete" }));
|
|
return true;
|
|
};
|
|
var splitLine = ({ state, dispatch }) => {
|
|
if (state.readOnly)
|
|
return false;
|
|
let changes = state.changeByRange((range) => {
|
|
return {
|
|
changes: { from: range.from, to: range.to, insert: Text.of(["", ""]) },
|
|
range: EditorSelection.cursor(range.from)
|
|
};
|
|
});
|
|
dispatch(state.update(changes, { scrollIntoView: true, userEvent: "input" }));
|
|
return true;
|
|
};
|
|
var transposeChars = ({ state, dispatch }) => {
|
|
if (state.readOnly)
|
|
return false;
|
|
let changes = state.changeByRange((range) => {
|
|
if (!range.empty || range.from == 0 || range.from == state.doc.length)
|
|
return { range };
|
|
let pos = range.from, line = state.doc.lineAt(pos);
|
|
let from = pos == line.from ? pos - 1 : findClusterBreak(line.text, pos - line.from, false) + line.from;
|
|
let to = pos == line.to ? pos + 1 : findClusterBreak(line.text, pos - line.from, true) + line.from;
|
|
return {
|
|
changes: { from, to, insert: state.doc.slice(pos, to).append(state.doc.slice(from, pos)) },
|
|
range: EditorSelection.cursor(to)
|
|
};
|
|
});
|
|
if (changes.changes.empty)
|
|
return false;
|
|
dispatch(state.update(changes, { scrollIntoView: true, userEvent: "move.character" }));
|
|
return true;
|
|
};
|
|
function selectedLineBlocks(state) {
|
|
let blocks = [], upto = -1;
|
|
for (let range of state.selection.ranges) {
|
|
let startLine = state.doc.lineAt(range.from), endLine = state.doc.lineAt(range.to);
|
|
if (!range.empty && range.to == endLine.from)
|
|
endLine = state.doc.lineAt(range.to - 1);
|
|
if (upto >= startLine.number) {
|
|
let prev = blocks[blocks.length - 1];
|
|
prev.to = endLine.to;
|
|
prev.ranges.push(range);
|
|
} else {
|
|
blocks.push({ from: startLine.from, to: endLine.to, ranges: [range] });
|
|
}
|
|
upto = endLine.number + 1;
|
|
}
|
|
return blocks;
|
|
}
|
|
function moveLine(state, dispatch, forward) {
|
|
if (state.readOnly)
|
|
return false;
|
|
let changes = [], ranges = [];
|
|
for (let block of selectedLineBlocks(state)) {
|
|
if (forward ? block.to == state.doc.length : block.from == 0)
|
|
continue;
|
|
let nextLine = state.doc.lineAt(forward ? block.to + 1 : block.from - 1);
|
|
let size = nextLine.length + 1;
|
|
if (forward) {
|
|
changes.push({ from: block.to, to: nextLine.to }, { from: block.from, insert: nextLine.text + state.lineBreak });
|
|
for (let r of block.ranges)
|
|
ranges.push(EditorSelection.range(Math.min(state.doc.length, r.anchor + size), Math.min(state.doc.length, r.head + size)));
|
|
} else {
|
|
changes.push({ from: nextLine.from, to: block.from }, { from: block.to, insert: state.lineBreak + nextLine.text });
|
|
for (let r of block.ranges)
|
|
ranges.push(EditorSelection.range(r.anchor - size, r.head - size));
|
|
}
|
|
}
|
|
if (!changes.length)
|
|
return false;
|
|
dispatch(state.update({
|
|
changes,
|
|
scrollIntoView: true,
|
|
selection: EditorSelection.create(ranges, state.selection.mainIndex),
|
|
userEvent: "move.line"
|
|
}));
|
|
return true;
|
|
}
|
|
var moveLineUp = ({ state, dispatch }) => moveLine(state, dispatch, false);
|
|
var moveLineDown = ({ state, dispatch }) => moveLine(state, dispatch, true);
|
|
function copyLine(state, dispatch, forward) {
|
|
if (state.readOnly)
|
|
return false;
|
|
let changes = [];
|
|
for (let block of selectedLineBlocks(state)) {
|
|
if (forward)
|
|
changes.push({ from: block.from, insert: state.doc.slice(block.from, block.to) + state.lineBreak });
|
|
else
|
|
changes.push({ from: block.to, insert: state.lineBreak + state.doc.slice(block.from, block.to) });
|
|
}
|
|
dispatch(state.update({ changes, scrollIntoView: true, userEvent: "input.copyline" }));
|
|
return true;
|
|
}
|
|
var copyLineUp = ({ state, dispatch }) => copyLine(state, dispatch, false);
|
|
var copyLineDown = ({ state, dispatch }) => copyLine(state, dispatch, true);
|
|
var deleteLine = (view) => {
|
|
if (view.state.readOnly)
|
|
return false;
|
|
let { state } = view, changes = state.changes(selectedLineBlocks(state).map(({ from, to }) => {
|
|
if (from > 0)
|
|
from--;
|
|
else if (to < state.doc.length)
|
|
to++;
|
|
return { from, to };
|
|
}));
|
|
let selection = updateSel(state.selection, (range) => view.moveVertically(range, true)).map(changes);
|
|
view.dispatch({ changes, selection, scrollIntoView: true, userEvent: "delete.line" });
|
|
return true;
|
|
};
|
|
var insertNewline = ({ state, dispatch }) => {
|
|
dispatch(state.update(state.replaceSelection(state.lineBreak), { scrollIntoView: true, userEvent: "input" }));
|
|
return true;
|
|
};
|
|
function isBetweenBrackets(state, pos) {
|
|
if (/\(\)|\[\]|\{\}/.test(state.sliceDoc(pos - 1, pos + 1)))
|
|
return { from: pos, to: pos };
|
|
let context = syntaxTree(state).resolveInner(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)
|
|
return { from: before.to, to: after.from };
|
|
return null;
|
|
}
|
|
var insertNewlineAndIndent = newlineAndIndent(false);
|
|
var insertBlankLine = newlineAndIndent(true);
|
|
function newlineAndIndent(atEof) {
|
|
return ({ state, dispatch }) => {
|
|
if (state.readOnly)
|
|
return false;
|
|
let changes = state.changeByRange((range) => {
|
|
let { from, to } = range, line = state.doc.lineAt(from);
|
|
let explode = !atEof && from == to && isBetweenBrackets(state, from);
|
|
if (atEof)
|
|
from = to = (to <= line.to ? line : state.doc.lineAt(to)).to;
|
|
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;
|
|
while (to < line.to && /\s/.test(line.text[to - line.from]))
|
|
to++;
|
|
if (explode)
|
|
({ from, to } = explode);
|
|
else if (from > line.from && from < line.from + 100 && !/\S/.test(line.text.slice(0, from)))
|
|
from = line.from;
|
|
let insert = ["", indentString(state, indent)];
|
|
if (explode)
|
|
insert.push(indentString(state, cx.lineIndent(line.from, -1)));
|
|
return {
|
|
changes: { from, to, insert: Text.of(insert) },
|
|
range: EditorSelection.cursor(from + 1 + insert[1].length)
|
|
};
|
|
});
|
|
dispatch(state.update(changes, { scrollIntoView: true, userEvent: "input" }));
|
|
return true;
|
|
};
|
|
}
|
|
function changeBySelectedLine(state, f) {
|
|
let atLine = -1;
|
|
return state.changeByRange((range) => {
|
|
let changes = [];
|
|
for (let pos = range.from; pos <= range.to; ) {
|
|
let line = state.doc.lineAt(pos);
|
|
if (line.number > atLine && (range.empty || range.to > line.from)) {
|
|
f(line, changes, range);
|
|
atLine = line.number;
|
|
}
|
|
pos = line.to + 1;
|
|
}
|
|
let changeSet = state.changes(changes);
|
|
return {
|
|
changes,
|
|
range: EditorSelection.range(changeSet.mapPos(range.anchor, 1), changeSet.mapPos(range.head, 1))
|
|
};
|
|
});
|
|
}
|
|
var indentSelection = ({ state, dispatch }) => {
|
|
if (state.readOnly)
|
|
return false;
|
|
let updated = /* @__PURE__ */ Object.create(null);
|
|
let context = new IndentContext(state, { overrideIndentation: (start) => {
|
|
let found = updated[start];
|
|
return found == null ? -1 : found;
|
|
} });
|
|
let changes = changeBySelectedLine(state, (line, changes2, range) => {
|
|
let indent = getIndentation(context, line.from);
|
|
if (indent == null)
|
|
return;
|
|
if (!/\S/.test(line.text))
|
|
indent = 0;
|
|
let cur = /^\s*/.exec(line.text)[0];
|
|
let norm = indentString(state, indent);
|
|
if (cur != norm || range.from < line.from + cur.length) {
|
|
updated[line.from] = indent;
|
|
changes2.push({ from: line.from, to: line.from + cur.length, insert: norm });
|
|
}
|
|
});
|
|
if (!changes.changes.empty)
|
|
dispatch(state.update(changes, { userEvent: "indent" }));
|
|
return true;
|
|
};
|
|
var indentMore = ({ state, dispatch }) => {
|
|
if (state.readOnly)
|
|
return false;
|
|
dispatch(state.update(changeBySelectedLine(state, (line, changes) => {
|
|
changes.push({ from: line.from, insert: state.facet(indentUnit) });
|
|
}), { userEvent: "input.indent" }));
|
|
return true;
|
|
};
|
|
var indentLess = ({ state, dispatch }) => {
|
|
if (state.readOnly)
|
|
return false;
|
|
dispatch(state.update(changeBySelectedLine(state, (line, changes) => {
|
|
let space = /^\s*/.exec(line.text)[0];
|
|
if (!space)
|
|
return;
|
|
let col = countColumn(space, state.tabSize), keep = 0;
|
|
let insert = indentString(state, Math.max(0, col - getIndentUnit(state)));
|
|
while (keep < space.length && keep < insert.length && space.charCodeAt(keep) == insert.charCodeAt(keep))
|
|
keep++;
|
|
changes.push({ from: line.from + keep, to: line.from + space.length, insert: insert.slice(keep) });
|
|
}), { userEvent: "delete.dedent" }));
|
|
return true;
|
|
};
|
|
var insertTab = ({ state, dispatch }) => {
|
|
if (state.selection.ranges.some((r) => !r.empty))
|
|
return indentMore({ state, dispatch });
|
|
dispatch(state.update(state.replaceSelection(" "), { scrollIntoView: true, userEvent: "input" }));
|
|
return true;
|
|
};
|
|
var emacsStyleKeymap = [
|
|
{ key: "Ctrl-b", run: cursorCharLeft, shift: selectCharLeft, preventDefault: true },
|
|
{ key: "Ctrl-f", run: cursorCharRight, shift: selectCharRight },
|
|
{ key: "Ctrl-p", run: cursorLineUp, shift: selectLineUp },
|
|
{ key: "Ctrl-n", run: cursorLineDown, shift: selectLineDown },
|
|
{ key: "Ctrl-a", run: cursorLineStart, shift: selectLineStart },
|
|
{ key: "Ctrl-e", run: cursorLineEnd, shift: selectLineEnd },
|
|
{ key: "Ctrl-d", run: deleteCharForward },
|
|
{ key: "Ctrl-h", run: deleteCharBackward },
|
|
{ key: "Ctrl-k", run: deleteToLineEnd },
|
|
{ key: "Ctrl-Alt-h", run: deleteGroupBackward },
|
|
{ key: "Ctrl-o", run: splitLine },
|
|
{ key: "Ctrl-t", run: transposeChars },
|
|
{ key: "Ctrl-v", run: cursorPageDown }
|
|
];
|
|
var standardKeymap = [
|
|
{ key: "ArrowLeft", run: cursorCharLeft, shift: selectCharLeft, preventDefault: true },
|
|
{ key: "Mod-ArrowLeft", mac: "Alt-ArrowLeft", run: cursorGroupLeft, shift: selectGroupLeft, preventDefault: true },
|
|
{ mac: "Cmd-ArrowLeft", run: cursorLineBoundaryLeft, shift: selectLineBoundaryLeft, preventDefault: true },
|
|
{ key: "ArrowRight", run: cursorCharRight, shift: selectCharRight, preventDefault: true },
|
|
{ key: "Mod-ArrowRight", mac: "Alt-ArrowRight", run: cursorGroupRight, shift: selectGroupRight, preventDefault: true },
|
|
{ mac: "Cmd-ArrowRight", run: cursorLineBoundaryRight, shift: selectLineBoundaryRight, preventDefault: true },
|
|
{ key: "ArrowUp", run: cursorLineUp, shift: selectLineUp, preventDefault: true },
|
|
{ mac: "Cmd-ArrowUp", run: cursorDocStart, shift: selectDocStart },
|
|
{ mac: "Ctrl-ArrowUp", run: cursorPageUp, shift: selectPageUp },
|
|
{ key: "ArrowDown", run: cursorLineDown, shift: selectLineDown, preventDefault: true },
|
|
{ mac: "Cmd-ArrowDown", run: cursorDocEnd, shift: selectDocEnd },
|
|
{ mac: "Ctrl-ArrowDown", run: cursorPageDown, shift: selectPageDown },
|
|
{ key: "PageUp", run: cursorPageUp, shift: selectPageUp },
|
|
{ key: "PageDown", run: cursorPageDown, shift: selectPageDown },
|
|
{ key: "Home", run: cursorLineBoundaryBackward, shift: selectLineBoundaryBackward, preventDefault: true },
|
|
{ key: "Mod-Home", run: cursorDocStart, shift: selectDocStart },
|
|
{ key: "End", run: cursorLineBoundaryForward, shift: selectLineBoundaryForward, preventDefault: true },
|
|
{ key: "Mod-End", run: cursorDocEnd, shift: selectDocEnd },
|
|
{ key: "Enter", run: insertNewlineAndIndent },
|
|
{ key: "Mod-a", run: selectAll },
|
|
{ key: "Backspace", run: deleteCharBackward, shift: deleteCharBackward },
|
|
{ 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 }
|
|
].concat(emacsStyleKeymap.map((b) => ({ mac: b.key, run: b.run, shift: b.shift })));
|
|
var defaultKeymap = [
|
|
{ key: "Alt-ArrowLeft", mac: "Ctrl-ArrowLeft", run: cursorSyntaxLeft, shift: selectSyntaxLeft },
|
|
{ key: "Alt-ArrowRight", mac: "Ctrl-ArrowRight", run: cursorSyntaxRight, shift: selectSyntaxRight },
|
|
{ key: "Alt-ArrowUp", run: moveLineUp },
|
|
{ key: "Shift-Alt-ArrowUp", run: copyLineUp },
|
|
{ key: "Alt-ArrowDown", run: moveLineDown },
|
|
{ key: "Shift-Alt-ArrowDown", run: copyLineDown },
|
|
{ key: "Escape", run: simplifySelection },
|
|
{ key: "Mod-Enter", run: insertBlankLine },
|
|
{ key: "Alt-l", mac: "Ctrl-l", run: selectLine },
|
|
{ key: "Mod-i", run: selectParentSyntax, preventDefault: true },
|
|
{ key: "Mod-[", run: indentLess },
|
|
{ key: "Mod-]", run: indentMore },
|
|
{ key: "Mod-Alt-\\", run: indentSelection },
|
|
{ key: "Shift-Mod-k", run: deleteLine },
|
|
{ key: "Shift-Mod-\\", run: cursorMatchingBracket },
|
|
{ key: "Mod-/", run: toggleComment },
|
|
{ key: "Alt-A", run: toggleBlockComment }
|
|
].concat(standardKeymap);
|
|
var indentWithTab = { key: "Tab", run: indentMore, shift: indentLess };
|
|
|
|
export {
|
|
toggleComment,
|
|
toggleLineComment,
|
|
lineComment,
|
|
lineUncomment,
|
|
toggleBlockComment,
|
|
blockComment,
|
|
blockUncomment,
|
|
toggleBlockCommentByLine,
|
|
isolateHistory,
|
|
invertedEffects,
|
|
history,
|
|
historyField,
|
|
undo,
|
|
redo,
|
|
undoSelection,
|
|
redoSelection,
|
|
undoDepth,
|
|
redoDepth,
|
|
historyKeymap,
|
|
cursorCharLeft,
|
|
cursorCharRight,
|
|
cursorCharForward,
|
|
cursorCharBackward,
|
|
cursorGroupLeft,
|
|
cursorGroupRight,
|
|
cursorGroupForward,
|
|
cursorGroupBackward,
|
|
cursorSubwordForward,
|
|
cursorSubwordBackward,
|
|
cursorSyntaxLeft,
|
|
cursorSyntaxRight,
|
|
cursorLineUp,
|
|
cursorLineDown,
|
|
cursorPageUp,
|
|
cursorPageDown,
|
|
cursorLineBoundaryForward,
|
|
cursorLineBoundaryBackward,
|
|
cursorLineBoundaryLeft,
|
|
cursorLineBoundaryRight,
|
|
cursorLineStart,
|
|
cursorLineEnd,
|
|
cursorMatchingBracket,
|
|
selectMatchingBracket,
|
|
selectCharLeft,
|
|
selectCharRight,
|
|
selectCharForward,
|
|
selectCharBackward,
|
|
selectGroupLeft,
|
|
selectGroupRight,
|
|
selectGroupForward,
|
|
selectGroupBackward,
|
|
selectSubwordForward,
|
|
selectSubwordBackward,
|
|
selectSyntaxLeft,
|
|
selectSyntaxRight,
|
|
selectLineUp,
|
|
selectLineDown,
|
|
selectPageUp,
|
|
selectPageDown,
|
|
selectLineBoundaryForward,
|
|
selectLineBoundaryBackward,
|
|
selectLineBoundaryLeft,
|
|
selectLineBoundaryRight,
|
|
selectLineStart,
|
|
selectLineEnd,
|
|
cursorDocStart,
|
|
cursorDocEnd,
|
|
selectDocStart,
|
|
selectDocEnd,
|
|
selectAll,
|
|
selectLine,
|
|
selectParentSyntax,
|
|
simplifySelection,
|
|
deleteCharBackward,
|
|
deleteCharForward,
|
|
deleteGroupBackward,
|
|
deleteGroupForward,
|
|
deleteToLineEnd,
|
|
deleteToLineStart,
|
|
deleteTrailingWhitespace,
|
|
splitLine,
|
|
transposeChars,
|
|
moveLineUp,
|
|
moveLineDown,
|
|
copyLineUp,
|
|
copyLineDown,
|
|
deleteLine,
|
|
insertNewline,
|
|
insertNewlineAndIndent,
|
|
insertBlankLine,
|
|
indentSelection,
|
|
indentMore,
|
|
indentLess,
|
|
insertTab,
|
|
emacsStyleKeymap,
|
|
standardKeymap,
|
|
defaultKeymap,
|
|
indentWithTab
|
|
};
|
|
//# sourceMappingURL=chunk-TRAAS4SM.js.map
|