Diffdown is a real-time collaborative Markdown editor/previewer built on the AT Protocol
diffdown.com
1'use strict';
2
3var state = require('@codemirror/state');
4var view = require('@codemirror/view');
5var language = require('@codemirror/language');
6var common = require('@lezer/common');
7
8/**
9Comment or uncomment the current selection. Will use line comments
10if available, otherwise falling back to block comments.
11*/
12const toggleComment = target => {
13 let { state } = target, line = state.doc.lineAt(state.selection.main.from), config = getConfig(target.state, line.from);
14 return config.line ? toggleLineComment(target) : config.block ? toggleBlockCommentByLine(target) : false;
15};
16function command(f, option) {
17 return ({ state, dispatch }) => {
18 if (state.readOnly)
19 return false;
20 let tr = f(option, state);
21 if (!tr)
22 return false;
23 dispatch(state.update(tr));
24 return true;
25 };
26}
27/**
28Comment or uncomment the current selection using line comments.
29The line comment syntax is taken from the
30[`commentTokens`](https://codemirror.net/6/docs/ref/#commands.CommentTokens) [language
31data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt).
32*/
33const toggleLineComment = command(changeLineComment, 0 /* CommentOption.Toggle */);
34/**
35Comment the current selection using line comments.
36*/
37const lineComment = command(changeLineComment, 1 /* CommentOption.Comment */);
38/**
39Uncomment the current selection using line comments.
40*/
41const lineUncomment = command(changeLineComment, 2 /* CommentOption.Uncomment */);
42/**
43Comment or uncomment the current selection using block comments.
44The block comment syntax is taken from the
45[`commentTokens`](https://codemirror.net/6/docs/ref/#commands.CommentTokens) [language
46data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt).
47*/
48const toggleBlockComment = command(changeBlockComment, 0 /* CommentOption.Toggle */);
49/**
50Comment the current selection using block comments.
51*/
52const blockComment = command(changeBlockComment, 1 /* CommentOption.Comment */);
53/**
54Uncomment the current selection using block comments.
55*/
56const blockUncomment = command(changeBlockComment, 2 /* CommentOption.Uncomment */);
57/**
58Comment or uncomment the lines around the current selection using
59block comments.
60*/
61const toggleBlockCommentByLine = command((o, s) => changeBlockComment(o, s, selectedLineRanges(s)), 0 /* CommentOption.Toggle */);
62function getConfig(state, pos) {
63 let data = state.languageDataAt("commentTokens", pos, 1);
64 return data.length ? data[0] : {};
65}
66const SearchMargin = 50;
67/**
68Determines if the given range is block-commented in the given
69state.
70*/
71function findBlockComment(state, { open, close }, from, to) {
72 let textBefore = state.sliceDoc(from - SearchMargin, from);
73 let textAfter = state.sliceDoc(to, to + SearchMargin);
74 let spaceBefore = /\s*$/.exec(textBefore)[0].length, spaceAfter = /^\s*/.exec(textAfter)[0].length;
75 let beforeOff = textBefore.length - spaceBefore;
76 if (textBefore.slice(beforeOff - open.length, beforeOff) == open &&
77 textAfter.slice(spaceAfter, spaceAfter + close.length) == close) {
78 return { open: { pos: from - spaceBefore, margin: spaceBefore && 1 },
79 close: { pos: to + spaceAfter, margin: spaceAfter && 1 } };
80 }
81 let startText, endText;
82 if (to - from <= 2 * SearchMargin) {
83 startText = endText = state.sliceDoc(from, to);
84 }
85 else {
86 startText = state.sliceDoc(from, from + SearchMargin);
87 endText = state.sliceDoc(to - SearchMargin, to);
88 }
89 let startSpace = /^\s*/.exec(startText)[0].length, endSpace = /\s*$/.exec(endText)[0].length;
90 let endOff = endText.length - endSpace - close.length;
91 if (startText.slice(startSpace, startSpace + open.length) == open &&
92 endText.slice(endOff, endOff + close.length) == close) {
93 return { open: { pos: from + startSpace + open.length,
94 margin: /\s/.test(startText.charAt(startSpace + open.length)) ? 1 : 0 },
95 close: { pos: to - endSpace - close.length,
96 margin: /\s/.test(endText.charAt(endOff - 1)) ? 1 : 0 } };
97 }
98 return null;
99}
100function selectedLineRanges(state) {
101 let ranges = [];
102 for (let r of state.selection.ranges) {
103 let fromLine = state.doc.lineAt(r.from);
104 let toLine = r.to <= fromLine.to ? fromLine : state.doc.lineAt(r.to);
105 if (toLine.from > fromLine.from && toLine.from == r.to)
106 toLine = r.to == fromLine.to + 1 ? fromLine : state.doc.lineAt(r.to - 1);
107 let last = ranges.length - 1;
108 if (last >= 0 && ranges[last].to > fromLine.from)
109 ranges[last].to = toLine.to;
110 else
111 ranges.push({ from: fromLine.from + /^\s*/.exec(fromLine.text)[0].length, to: toLine.to });
112 }
113 return ranges;
114}
115// Performs toggle, comment and uncomment of block comments in
116// languages that support them.
117function changeBlockComment(option, state, ranges = state.selection.ranges) {
118 let tokens = ranges.map(r => getConfig(state, r.from).block);
119 if (!tokens.every(c => c))
120 return null;
121 let comments = ranges.map((r, i) => findBlockComment(state, tokens[i], r.from, r.to));
122 if (option != 2 /* CommentOption.Uncomment */ && !comments.every(c => c)) {
123 return { changes: state.changes(ranges.map((range, i) => {
124 if (comments[i])
125 return [];
126 return [{ from: range.from, insert: tokens[i].open + " " }, { from: range.to, insert: " " + tokens[i].close }];
127 })) };
128 }
129 else if (option != 1 /* CommentOption.Comment */ && comments.some(c => c)) {
130 let changes = [];
131 for (let i = 0, comment; i < comments.length; i++)
132 if (comment = comments[i]) {
133 let token = tokens[i], { open, close } = comment;
134 changes.push({ from: open.pos - token.open.length, to: open.pos + open.margin }, { from: close.pos - close.margin, to: close.pos + token.close.length });
135 }
136 return { changes };
137 }
138 return null;
139}
140// Performs toggle, comment and uncomment of line comments.
141function changeLineComment(option, state, ranges = state.selection.ranges) {
142 let lines = [];
143 let prevLine = -1;
144 ranges: for (let { from, to } of ranges) {
145 let startI = lines.length, minIndent = 1e9, token;
146 for (let pos = from; pos <= to;) {
147 let line = state.doc.lineAt(pos);
148 if (token == undefined) {
149 token = getConfig(state, line.from).line;
150 if (!token)
151 continue ranges;
152 }
153 if (line.from > prevLine && (from == to || to > line.from)) {
154 prevLine = line.from;
155 let indent = /^\s*/.exec(line.text)[0].length;
156 let empty = indent == line.length;
157 let comment = line.text.slice(indent, indent + token.length) == token ? indent : -1;
158 if (indent < line.text.length && indent < minIndent)
159 minIndent = indent;
160 lines.push({ line, comment, token, indent, empty, single: false });
161 }
162 pos = line.to + 1;
163 }
164 if (minIndent < 1e9)
165 for (let i = startI; i < lines.length; i++)
166 if (lines[i].indent < lines[i].line.text.length)
167 lines[i].indent = minIndent;
168 if (lines.length == startI + 1)
169 lines[startI].single = true;
170 }
171 if (option != 2 /* CommentOption.Uncomment */ && lines.some(l => l.comment < 0 && (!l.empty || l.single))) {
172 let changes = [];
173 for (let { line, token, indent, empty, single } of lines)
174 if (single || !empty)
175 changes.push({ from: line.from + indent, insert: token + " " });
176 let changeSet = state.changes(changes);
177 return { changes: changeSet, selection: state.selection.map(changeSet, 1) };
178 }
179 else if (option != 1 /* CommentOption.Comment */ && lines.some(l => l.comment >= 0)) {
180 let changes = [];
181 for (let { line, comment, token } of lines)
182 if (comment >= 0) {
183 let from = line.from + comment, to = from + token.length;
184 if (line.text[to - line.from] == " ")
185 to++;
186 changes.push({ from, to });
187 }
188 return { changes };
189 }
190 return null;
191}
192
193const fromHistory = state.Annotation.define();
194/**
195Transaction annotation that will prevent that transaction from
196being combined with other transactions in the undo history. Given
197`"before"`, it'll prevent merging with previous transactions. With
198`"after"`, subsequent transactions won't be combined with this
199one. With `"full"`, the transaction is isolated on both sides.
200*/
201const isolateHistory = state.Annotation.define();
202/**
203This facet provides a way to register functions that, given a
204transaction, provide a set of effects that the history should
205store when inverting the transaction. This can be used to
206integrate some kinds of effects in the history, so that they can
207be undone (and redone again).
208*/
209const invertedEffects = state.Facet.define();
210const historyConfig = state.Facet.define({
211 combine(configs) {
212 return state.combineConfig(configs, {
213 minDepth: 100,
214 newGroupDelay: 500,
215 joinToEvent: (_t, isAdjacent) => isAdjacent,
216 }, {
217 minDepth: Math.max,
218 newGroupDelay: Math.min,
219 joinToEvent: (a, b) => (tr, adj) => a(tr, adj) || b(tr, adj)
220 });
221 }
222});
223const historyField_ = state.StateField.define({
224 create() {
225 return HistoryState.empty;
226 },
227 update(state$1, tr) {
228 let config = tr.state.facet(historyConfig);
229 let fromHist = tr.annotation(fromHistory);
230 if (fromHist) {
231 let item = HistEvent.fromTransaction(tr, fromHist.selection), from = fromHist.side;
232 let other = from == 0 /* BranchName.Done */ ? state$1.undone : state$1.done;
233 if (item)
234 other = updateBranch(other, other.length, config.minDepth, item);
235 else
236 other = addSelection(other, tr.startState.selection);
237 return new HistoryState(from == 0 /* BranchName.Done */ ? fromHist.rest : other, from == 0 /* BranchName.Done */ ? other : fromHist.rest);
238 }
239 let isolate = tr.annotation(isolateHistory);
240 if (isolate == "full" || isolate == "before")
241 state$1 = state$1.isolate();
242 if (tr.annotation(state.Transaction.addToHistory) === false)
243 return !tr.changes.empty ? state$1.addMapping(tr.changes.desc) : state$1;
244 let event = HistEvent.fromTransaction(tr);
245 let time = tr.annotation(state.Transaction.time), userEvent = tr.annotation(state.Transaction.userEvent);
246 if (event)
247 state$1 = state$1.addChanges(event, time, userEvent, config, tr);
248 else if (tr.selection)
249 state$1 = state$1.addSelection(tr.startState.selection, time, userEvent, config.newGroupDelay);
250 if (isolate == "full" || isolate == "after")
251 state$1 = state$1.isolate();
252 return state$1;
253 },
254 toJSON(value) {
255 return { done: value.done.map(e => e.toJSON()), undone: value.undone.map(e => e.toJSON()) };
256 },
257 fromJSON(json) {
258 return new HistoryState(json.done.map(HistEvent.fromJSON), json.undone.map(HistEvent.fromJSON));
259 }
260});
261/**
262Create a history extension with the given configuration.
263*/
264function history(config = {}) {
265 return [
266 historyField_,
267 historyConfig.of(config),
268 view.EditorView.domEventHandlers({
269 beforeinput(e, view) {
270 let command = e.inputType == "historyUndo" ? undo : e.inputType == "historyRedo" ? redo : null;
271 if (!command)
272 return false;
273 e.preventDefault();
274 return command(view);
275 }
276 })
277 ];
278}
279/**
280The state field used to store the history data. Should probably
281only be used when you want to
282[serialize](https://codemirror.net/6/docs/ref/#state.EditorState.toJSON) or
283[deserialize](https://codemirror.net/6/docs/ref/#state.EditorState^fromJSON) state objects in a way
284that preserves history.
285*/
286const historyField = historyField_;
287function cmd(side, selection) {
288 return function ({ state, dispatch }) {
289 if (!selection && state.readOnly)
290 return false;
291 let historyState = state.field(historyField_, false);
292 if (!historyState)
293 return false;
294 let tr = historyState.pop(side, state, selection);
295 if (!tr)
296 return false;
297 dispatch(tr);
298 return true;
299 };
300}
301/**
302Undo a single group of history events. Returns false if no group
303was available.
304*/
305const undo = cmd(0 /* BranchName.Done */, false);
306/**
307Redo a group of history events. Returns false if no group was
308available.
309*/
310const redo = cmd(1 /* BranchName.Undone */, false);
311/**
312Undo a change or selection change.
313*/
314const undoSelection = cmd(0 /* BranchName.Done */, true);
315/**
316Redo a change or selection change.
317*/
318const redoSelection = cmd(1 /* BranchName.Undone */, true);
319function depth(side) {
320 return function (state) {
321 let histState = state.field(historyField_, false);
322 if (!histState)
323 return 0;
324 let branch = side == 0 /* BranchName.Done */ ? histState.done : histState.undone;
325 return branch.length - (branch.length && !branch[0].changes ? 1 : 0);
326 };
327}
328/**
329The amount of undoable change events available in a given state.
330*/
331const undoDepth = depth(0 /* BranchName.Done */);
332/**
333The amount of redoable change events available in a given state.
334*/
335const redoDepth = depth(1 /* BranchName.Undone */);
336// History events store groups of changes or effects that need to be
337// undone/redone together.
338class HistEvent {
339 constructor(
340 // The changes in this event. Normal events hold at least one
341 // change or effect. But it may be necessary to store selection
342 // events before the first change, in which case a special type of
343 // instance is created which doesn't hold any changes, with
344 // changes == startSelection == undefined
345 changes,
346 // The effects associated with this event
347 effects,
348 // Accumulated mapping (from addToHistory==false) that should be
349 // applied to events below this one.
350 mapped,
351 // The selection before this event
352 startSelection,
353 // Stores selection changes after this event, to be used for
354 // selection undo/redo.
355 selectionsAfter) {
356 this.changes = changes;
357 this.effects = effects;
358 this.mapped = mapped;
359 this.startSelection = startSelection;
360 this.selectionsAfter = selectionsAfter;
361 }
362 setSelAfter(after) {
363 return new HistEvent(this.changes, this.effects, this.mapped, this.startSelection, after);
364 }
365 toJSON() {
366 var _a, _b, _c;
367 return {
368 changes: (_a = this.changes) === null || _a === void 0 ? void 0 : _a.toJSON(),
369 mapped: (_b = this.mapped) === null || _b === void 0 ? void 0 : _b.toJSON(),
370 startSelection: (_c = this.startSelection) === null || _c === void 0 ? void 0 : _c.toJSON(),
371 selectionsAfter: this.selectionsAfter.map(s => s.toJSON())
372 };
373 }
374 static fromJSON(json) {
375 return new HistEvent(json.changes && state.ChangeSet.fromJSON(json.changes), [], json.mapped && state.ChangeDesc.fromJSON(json.mapped), json.startSelection && state.EditorSelection.fromJSON(json.startSelection), json.selectionsAfter.map(state.EditorSelection.fromJSON));
376 }
377 // This does not check `addToHistory` and such, it assumes the
378 // transaction needs to be converted to an item. Returns null when
379 // there are no changes or effects in the transaction.
380 static fromTransaction(tr, selection) {
381 let effects = none;
382 for (let invert of tr.startState.facet(invertedEffects)) {
383 let result = invert(tr);
384 if (result.length)
385 effects = effects.concat(result);
386 }
387 if (!effects.length && tr.changes.empty)
388 return null;
389 return new HistEvent(tr.changes.invert(tr.startState.doc), effects, undefined, selection || tr.startState.selection, none);
390 }
391 static selection(selections) {
392 return new HistEvent(undefined, none, undefined, undefined, selections);
393 }
394}
395function updateBranch(branch, to, maxLen, newEvent) {
396 let start = to + 1 > maxLen + 20 ? to - maxLen - 1 : 0;
397 let newBranch = branch.slice(start, to);
398 newBranch.push(newEvent);
399 return newBranch;
400}
401function isAdjacent(a, b) {
402 let ranges = [], isAdjacent = false;
403 a.iterChangedRanges((f, t) => ranges.push(f, t));
404 b.iterChangedRanges((_f, _t, f, t) => {
405 for (let i = 0; i < ranges.length;) {
406 let from = ranges[i++], to = ranges[i++];
407 if (t >= from && f <= to)
408 isAdjacent = true;
409 }
410 });
411 return isAdjacent;
412}
413function eqSelectionShape(a, b) {
414 return a.ranges.length == b.ranges.length &&
415 a.ranges.filter((r, i) => r.empty != b.ranges[i].empty).length === 0;
416}
417function conc(a, b) {
418 return !a.length ? b : !b.length ? a : a.concat(b);
419}
420const none = [];
421const MaxSelectionsPerEvent = 200;
422function addSelection(branch, selection) {
423 if (!branch.length) {
424 return [HistEvent.selection([selection])];
425 }
426 else {
427 let lastEvent = branch[branch.length - 1];
428 let sels = lastEvent.selectionsAfter.slice(Math.max(0, lastEvent.selectionsAfter.length - MaxSelectionsPerEvent));
429 if (sels.length && sels[sels.length - 1].eq(selection))
430 return branch;
431 sels.push(selection);
432 return updateBranch(branch, branch.length - 1, 1e9, lastEvent.setSelAfter(sels));
433 }
434}
435// Assumes the top item has one or more selectionAfter values
436function popSelection(branch) {
437 let last = branch[branch.length - 1];
438 let newBranch = branch.slice();
439 newBranch[branch.length - 1] = last.setSelAfter(last.selectionsAfter.slice(0, last.selectionsAfter.length - 1));
440 return newBranch;
441}
442// Add a mapping to the top event in the given branch. If this maps
443// away all the changes and effects in that item, drop it and
444// propagate the mapping to the next item.
445function addMappingToBranch(branch, mapping) {
446 if (!branch.length)
447 return branch;
448 let length = branch.length, selections = none;
449 while (length) {
450 let event = mapEvent(branch[length - 1], mapping, selections);
451 if (event.changes && !event.changes.empty || event.effects.length) { // Event survived mapping
452 let result = branch.slice(0, length);
453 result[length - 1] = event;
454 return result;
455 }
456 else { // Drop this event, since there's no changes or effects left
457 mapping = event.mapped;
458 length--;
459 selections = event.selectionsAfter;
460 }
461 }
462 return selections.length ? [HistEvent.selection(selections)] : none;
463}
464function mapEvent(event, mapping, extraSelections) {
465 let selections = conc(event.selectionsAfter.length ? event.selectionsAfter.map(s => s.map(mapping)) : none, extraSelections);
466 // Change-less events don't store mappings (they are always the last event in a branch)
467 if (!event.changes)
468 return HistEvent.selection(selections);
469 let mappedChanges = event.changes.map(mapping), before = mapping.mapDesc(event.changes, true);
470 let fullMapping = event.mapped ? event.mapped.composeDesc(before) : before;
471 return new HistEvent(mappedChanges, state.StateEffect.mapEffects(event.effects, mapping), fullMapping, event.startSelection.map(before), selections);
472}
473const joinableUserEvent = /^(input\.type|delete)($|\.)/;
474class HistoryState {
475 constructor(done, undone, prevTime = 0, prevUserEvent = undefined) {
476 this.done = done;
477 this.undone = undone;
478 this.prevTime = prevTime;
479 this.prevUserEvent = prevUserEvent;
480 }
481 isolate() {
482 return this.prevTime ? new HistoryState(this.done, this.undone) : this;
483 }
484 addChanges(event, time, userEvent, config, tr) {
485 let done = this.done, lastEvent = done[done.length - 1];
486 if (lastEvent && lastEvent.changes && !lastEvent.changes.empty && event.changes &&
487 (!userEvent || joinableUserEvent.test(userEvent)) &&
488 ((!lastEvent.selectionsAfter.length &&
489 time - this.prevTime < config.newGroupDelay &&
490 config.joinToEvent(tr, isAdjacent(lastEvent.changes, event.changes))) ||
491 // For compose (but not compose.start) events, always join with previous event
492 userEvent == "input.type.compose")) {
493 done = updateBranch(done, done.length - 1, config.minDepth, new HistEvent(event.changes.compose(lastEvent.changes), conc(state.StateEffect.mapEffects(event.effects, lastEvent.changes), lastEvent.effects), lastEvent.mapped, lastEvent.startSelection, none));
494 }
495 else {
496 done = updateBranch(done, done.length, config.minDepth, event);
497 }
498 return new HistoryState(done, none, time, userEvent);
499 }
500 addSelection(selection, time, userEvent, newGroupDelay) {
501 let last = this.done.length ? this.done[this.done.length - 1].selectionsAfter : none;
502 if (last.length > 0 &&
503 time - this.prevTime < newGroupDelay &&
504 userEvent == this.prevUserEvent && userEvent && /^select($|\.)/.test(userEvent) &&
505 eqSelectionShape(last[last.length - 1], selection))
506 return this;
507 return new HistoryState(addSelection(this.done, selection), this.undone, time, userEvent);
508 }
509 addMapping(mapping) {
510 return new HistoryState(addMappingToBranch(this.done, mapping), addMappingToBranch(this.undone, mapping), this.prevTime, this.prevUserEvent);
511 }
512 pop(side, state, onlySelection) {
513 let branch = side == 0 /* BranchName.Done */ ? this.done : this.undone;
514 if (branch.length == 0)
515 return null;
516 let event = branch[branch.length - 1], selection = event.selectionsAfter[0] ||
517 (event.startSelection ? event.startSelection.map(event.changes.invertedDesc, 1) : state.selection);
518 if (onlySelection && event.selectionsAfter.length) {
519 return state.update({
520 selection: event.selectionsAfter[event.selectionsAfter.length - 1],
521 annotations: fromHistory.of({ side, rest: popSelection(branch), selection }),
522 userEvent: side == 0 /* BranchName.Done */ ? "select.undo" : "select.redo",
523 scrollIntoView: true
524 });
525 }
526 else if (!event.changes) {
527 return null;
528 }
529 else {
530 let rest = branch.length == 1 ? none : branch.slice(0, branch.length - 1);
531 if (event.mapped)
532 rest = addMappingToBranch(rest, event.mapped);
533 return state.update({
534 changes: event.changes,
535 selection: event.startSelection,
536 effects: event.effects,
537 annotations: fromHistory.of({ side, rest, selection }),
538 filter: false,
539 userEvent: side == 0 /* BranchName.Done */ ? "undo" : "redo",
540 scrollIntoView: true
541 });
542 }
543 }
544}
545HistoryState.empty = new HistoryState(none, none);
546/**
547Default key bindings for the undo history.
548
549- Mod-z: [`undo`](https://codemirror.net/6/docs/ref/#commands.undo).
550- Mod-y (Mod-Shift-z on macOS) + Ctrl-Shift-z on Linux: [`redo`](https://codemirror.net/6/docs/ref/#commands.redo).
551- Mod-u: [`undoSelection`](https://codemirror.net/6/docs/ref/#commands.undoSelection).
552- Alt-u (Mod-Shift-u on macOS): [`redoSelection`](https://codemirror.net/6/docs/ref/#commands.redoSelection).
553*/
554const historyKeymap = [
555 { key: "Mod-z", run: undo, preventDefault: true },
556 { key: "Mod-y", mac: "Mod-Shift-z", run: redo, preventDefault: true },
557 { linux: "Ctrl-Shift-z", run: redo, preventDefault: true },
558 { key: "Mod-u", run: undoSelection, preventDefault: true },
559 { key: "Alt-u", mac: "Mod-Shift-u", run: redoSelection, preventDefault: true }
560];
561
562function updateSel(sel, by) {
563 return state.EditorSelection.create(sel.ranges.map(by), sel.mainIndex);
564}
565function setSel(state, selection) {
566 return state.update({ selection, scrollIntoView: true, userEvent: "select" });
567}
568function moveSel({ state, dispatch }, how) {
569 let selection = updateSel(state.selection, how);
570 if (selection.eq(state.selection, true))
571 return false;
572 dispatch(setSel(state, selection));
573 return true;
574}
575function rangeEnd(range, forward) {
576 return state.EditorSelection.cursor(forward ? range.to : range.from);
577}
578function cursorByChar(view, forward) {
579 return moveSel(view, range => range.empty ? view.moveByChar(range, forward) : rangeEnd(range, forward));
580}
581function ltrAtCursor(view$1) {
582 return view$1.textDirectionAt(view$1.state.selection.main.head) == view.Direction.LTR;
583}
584/**
585Move the selection one character to the left (which is backward in
586left-to-right text, forward in right-to-left text).
587*/
588const cursorCharLeft = view => cursorByChar(view, !ltrAtCursor(view));
589/**
590Move the selection one character to the right.
591*/
592const cursorCharRight = view => cursorByChar(view, ltrAtCursor(view));
593/**
594Move the selection one character forward.
595*/
596const cursorCharForward = view => cursorByChar(view, true);
597/**
598Move the selection one character backward.
599*/
600const cursorCharBackward = view => cursorByChar(view, false);
601function byCharLogical(state$1, range, forward) {
602 let pos = range.head, line = state$1.doc.lineAt(pos);
603 if (pos == (forward ? line.to : line.from))
604 pos = forward ? Math.min(state$1.doc.length, line.to + 1) : Math.max(0, line.from - 1);
605 else
606 pos = line.from + state.findClusterBreak(line.text, pos - line.from, forward);
607 return state.EditorSelection.cursor(pos, forward ? -1 : 1);
608}
609function moveByCharLogical(target, forward) {
610 return moveSel(target, range => range.empty ? byCharLogical(target.state, range, forward) : rangeEnd(range, forward));
611}
612/**
613Move the selection one character forward, in logical
614(non-text-direction-aware) string index order.
615*/
616const cursorCharForwardLogical = target => moveByCharLogical(target, true);
617/**
618Move the selection one character backward, in logical string index
619order.
620*/
621const cursorCharBackwardLogical = target => moveByCharLogical(target, false);
622function cursorByGroup(view, forward) {
623 return moveSel(view, range => range.empty ? view.moveByGroup(range, forward) : rangeEnd(range, forward));
624}
625/**
626Move the selection to the left across one group of word or
627non-word (but also non-space) characters.
628*/
629const cursorGroupLeft = view => cursorByGroup(view, !ltrAtCursor(view));
630/**
631Move the selection one group to the right.
632*/
633const cursorGroupRight = view => cursorByGroup(view, ltrAtCursor(view));
634/**
635Move the selection one group forward.
636*/
637const cursorGroupForward = view => cursorByGroup(view, true);
638/**
639Move the selection one group backward.
640*/
641const cursorGroupBackward = view => cursorByGroup(view, false);
642function toGroupStart(view, pos, start) {
643 let categorize = view.state.charCategorizer(pos);
644 let cat = categorize(start), initial = cat != state.CharCategory.Space;
645 return (next) => {
646 let nextCat = categorize(next);
647 if (nextCat != state.CharCategory.Space)
648 return initial && nextCat == cat;
649 initial = false;
650 return true;
651 };
652}
653/**
654Move the cursor one group forward in the default Windows style,
655where it moves to the start of the next group.
656*/
657const cursorGroupForwardWin = view => {
658 return moveSel(view, range => range.empty
659 ? view.moveByChar(range, true, start => toGroupStart(view, range.head, start))
660 : rangeEnd(range, true));
661};
662const segmenter = typeof Intl != "undefined" && Intl.Segmenter ?
663 new (Intl.Segmenter)(undefined, { granularity: "word" }) : null;
664function moveBySubword(view, range, forward) {
665 let categorize = view.state.charCategorizer(range.from);
666 let cat = state.CharCategory.Space, pos = range.from, steps = 0;
667 let done = false, sawUpper = false, sawLower = false;
668 let step = (next) => {
669 if (done)
670 return false;
671 pos += forward ? next.length : -next.length;
672 let nextCat = categorize(next), ahead;
673 if (nextCat == state.CharCategory.Word && next.charCodeAt(0) < 128 && /[\W_]/.test(next))
674 nextCat = -1; // Treat word punctuation specially
675 if (cat == state.CharCategory.Space)
676 cat = nextCat;
677 if (cat != nextCat)
678 return false;
679 if (cat == state.CharCategory.Word) {
680 if (next.toLowerCase() == next) {
681 if (!forward && sawUpper)
682 return false;
683 sawLower = true;
684 }
685 else if (sawLower) {
686 if (forward)
687 return false;
688 done = true;
689 }
690 else {
691 if (sawUpper && forward && categorize(ahead = view.state.sliceDoc(pos, pos + 1)) == state.CharCategory.Word &&
692 ahead.toLowerCase() == ahead)
693 return false;
694 sawUpper = true;
695 }
696 }
697 steps++;
698 return true;
699 };
700 let end = view.moveByChar(range, forward, start => {
701 step(start);
702 return step;
703 });
704 if (segmenter && cat == state.CharCategory.Word && end.from == range.from + steps * (forward ? 1 : -1)) {
705 let from = Math.min(range.head, end.head), to = Math.max(range.head, end.head);
706 let skipped = view.state.sliceDoc(from, to);
707 if (skipped.length > 1 && /[\u4E00-\uffff]/.test(skipped)) {
708 let segments = Array.from(segmenter.segment(skipped));
709 if (segments.length > 1) {
710 if (forward)
711 return state.EditorSelection.cursor(range.head + segments[1].index, -1);
712 return state.EditorSelection.cursor(end.head + segments[segments.length - 1].index, 1);
713 }
714 }
715 }
716 return end;
717}
718function cursorBySubword(view, forward) {
719 return moveSel(view, range => range.empty ? moveBySubword(view, range, forward) : rangeEnd(range, forward));
720}
721/**
722Move the selection one group or camel-case subword forward.
723*/
724const cursorSubwordForward = view => cursorBySubword(view, true);
725/**
726Move the selection one group or camel-case subword backward.
727*/
728const cursorSubwordBackward = view => cursorBySubword(view, false);
729function interestingNode(state, node, bracketProp) {
730 if (node.type.prop(bracketProp))
731 return true;
732 let len = node.to - node.from;
733 return len && (len > 2 || /[^\s,.;:]/.test(state.sliceDoc(node.from, node.to))) || node.firstChild;
734}
735function moveBySyntax(state$1, start, forward) {
736 let pos = language.syntaxTree(state$1).resolveInner(start.head);
737 let bracketProp = forward ? common.NodeProp.closedBy : common.NodeProp.openedBy;
738 // Scan forward through child nodes to see if there's an interesting
739 // node ahead.
740 for (let at = start.head;;) {
741 let next = forward ? pos.childAfter(at) : pos.childBefore(at);
742 if (!next)
743 break;
744 if (interestingNode(state$1, next, bracketProp))
745 pos = next;
746 else
747 at = forward ? next.to : next.from;
748 }
749 let bracket = pos.type.prop(bracketProp), match, newPos;
750 if (bracket && (match = forward ? language.matchBrackets(state$1, pos.from, 1) : language.matchBrackets(state$1, pos.to, -1)) && match.matched)
751 newPos = forward ? match.end.to : match.end.from;
752 else
753 newPos = forward ? pos.to : pos.from;
754 return state.EditorSelection.cursor(newPos, forward ? -1 : 1);
755}
756/**
757Move the cursor over the next syntactic element to the left.
758*/
759const cursorSyntaxLeft = view => moveSel(view, range => moveBySyntax(view.state, range, !ltrAtCursor(view)));
760/**
761Move the cursor over the next syntactic element to the right.
762*/
763const cursorSyntaxRight = view => moveSel(view, range => moveBySyntax(view.state, range, ltrAtCursor(view)));
764function cursorByLine(view, forward) {
765 return moveSel(view, range => {
766 if (!range.empty)
767 return rangeEnd(range, forward);
768 let moved = view.moveVertically(range, forward);
769 return moved.head != range.head ? moved : view.moveToLineBoundary(range, forward);
770 });
771}
772/**
773Move the selection one line up.
774*/
775const cursorLineUp = view => cursorByLine(view, false);
776/**
777Move the selection one line down.
778*/
779const cursorLineDown = view => cursorByLine(view, true);
780function pageInfo(view$1) {
781 let selfScroll = view$1.scrollDOM.clientHeight < view$1.scrollDOM.scrollHeight - 2;
782 let marginTop = 0, marginBottom = 0, height;
783 if (selfScroll) {
784 for (let source of view$1.state.facet(view.EditorView.scrollMargins)) {
785 let margins = source(view$1);
786 if (margins === null || margins === void 0 ? void 0 : margins.top)
787 marginTop = Math.max(margins === null || margins === void 0 ? void 0 : margins.top, marginTop);
788 if (margins === null || margins === void 0 ? void 0 : margins.bottom)
789 marginBottom = Math.max(margins === null || margins === void 0 ? void 0 : margins.bottom, marginBottom);
790 }
791 height = view$1.scrollDOM.clientHeight - marginTop - marginBottom;
792 }
793 else {
794 height = (view$1.dom.ownerDocument.defaultView || window).innerHeight;
795 }
796 return { marginTop, marginBottom, selfScroll,
797 height: Math.max(view$1.defaultLineHeight, height - 5) };
798}
799function cursorByPage(view$1, forward) {
800 let page = pageInfo(view$1);
801 let { state } = view$1, selection = updateSel(state.selection, range => {
802 return range.empty ? view$1.moveVertically(range, forward, page.height)
803 : rangeEnd(range, forward);
804 });
805 if (selection.eq(state.selection))
806 return false;
807 let effect;
808 if (page.selfScroll) {
809 let startPos = view$1.coordsAtPos(state.selection.main.head);
810 let scrollRect = view$1.scrollDOM.getBoundingClientRect();
811 let scrollTop = scrollRect.top + page.marginTop, scrollBottom = scrollRect.bottom - page.marginBottom;
812 if (startPos && startPos.top > scrollTop && startPos.bottom < scrollBottom)
813 effect = view.EditorView.scrollIntoView(selection.main.head, { y: "start", yMargin: startPos.top - scrollTop });
814 }
815 view$1.dispatch(setSel(state, selection), { effects: effect });
816 return true;
817}
818/**
819Move the selection one page up.
820*/
821const cursorPageUp = view => cursorByPage(view, false);
822/**
823Move the selection one page down.
824*/
825const cursorPageDown = view => cursorByPage(view, true);
826function moveByLineBoundary(view, start, forward) {
827 let line = view.lineBlockAt(start.head), moved = view.moveToLineBoundary(start, forward);
828 if (moved.head == start.head && moved.head != (forward ? line.to : line.from))
829 moved = view.moveToLineBoundary(start, forward, false);
830 if (!forward && moved.head == line.from && line.length) {
831 let space = /^\s*/.exec(view.state.sliceDoc(line.from, Math.min(line.from + 100, line.to)))[0].length;
832 if (space && start.head != line.from + space)
833 moved = state.EditorSelection.cursor(line.from + space);
834 }
835 return moved;
836}
837/**
838Move the selection to the next line wrap point, or to the end of
839the line if there isn't one left on this line.
840*/
841const cursorLineBoundaryForward = view => moveSel(view, range => moveByLineBoundary(view, range, true));
842/**
843Move the selection to previous line wrap point, or failing that to
844the start of the line. If the line is indented, and the cursor
845isn't already at the end of the indentation, this will move to the
846end of the indentation instead of the start of the line.
847*/
848const cursorLineBoundaryBackward = view => moveSel(view, range => moveByLineBoundary(view, range, false));
849/**
850Move the selection one line wrap point to the left.
851*/
852const cursorLineBoundaryLeft = view => moveSel(view, range => moveByLineBoundary(view, range, !ltrAtCursor(view)));
853/**
854Move the selection one line wrap point to the right.
855*/
856const cursorLineBoundaryRight = view => moveSel(view, range => moveByLineBoundary(view, range, ltrAtCursor(view)));
857/**
858Move the selection to the start of the line.
859*/
860const cursorLineStart = view => moveSel(view, range => state.EditorSelection.cursor(view.lineBlockAt(range.head).from, 1));
861/**
862Move the selection to the end of the line.
863*/
864const cursorLineEnd = view => moveSel(view, range => state.EditorSelection.cursor(view.lineBlockAt(range.head).to, -1));
865function toMatchingBracket(state$1, dispatch, extend) {
866 let found = false, selection = updateSel(state$1.selection, range => {
867 let matching = language.matchBrackets(state$1, range.head, -1)
868 || language.matchBrackets(state$1, range.head, 1)
869 || (range.head > 0 && language.matchBrackets(state$1, range.head - 1, 1))
870 || (range.head < state$1.doc.length && language.matchBrackets(state$1, range.head + 1, -1));
871 if (!matching || !matching.end)
872 return range;
873 found = true;
874 let head = matching.start.from == range.head ? matching.end.to : matching.end.from;
875 return extend ? state.EditorSelection.range(range.anchor, head) : state.EditorSelection.cursor(head);
876 });
877 if (!found)
878 return false;
879 dispatch(setSel(state$1, selection));
880 return true;
881}
882/**
883Move the selection to the bracket matching the one it is currently
884on, if any.
885*/
886const cursorMatchingBracket = ({ state, dispatch }) => toMatchingBracket(state, dispatch, false);
887/**
888Extend the selection to the bracket matching the one the selection
889head is currently on, if any.
890*/
891const selectMatchingBracket = ({ state, dispatch }) => toMatchingBracket(state, dispatch, true);
892function extendSel(target, how) {
893 let selection = updateSel(target.state.selection, range => {
894 let head = how(range);
895 return state.EditorSelection.range(range.anchor, head.head, head.goalColumn, head.bidiLevel || undefined);
896 });
897 if (selection.eq(target.state.selection))
898 return false;
899 target.dispatch(setSel(target.state, selection));
900 return true;
901}
902function selectByChar(view, forward) {
903 return extendSel(view, range => view.moveByChar(range, forward));
904}
905/**
906Move the selection head one character to the left, while leaving
907the anchor in place.
908*/
909const selectCharLeft = view => selectByChar(view, !ltrAtCursor(view));
910/**
911Move the selection head one character to the right.
912*/
913const selectCharRight = view => selectByChar(view, ltrAtCursor(view));
914/**
915Move the selection head one character forward.
916*/
917const selectCharForward = view => selectByChar(view, true);
918/**
919Move the selection head one character backward.
920*/
921const selectCharBackward = view => selectByChar(view, false);
922/**
923Move the selection head one character forward by logical
924(non-direction aware) string index order.
925*/
926const selectCharForwardLogical = target => extendSel(target, range => byCharLogical(target.state, range, true));
927/**
928Move the selection head one character backward by logical string
929index order.
930*/
931const selectCharBackwardLogical = target => extendSel(target, range => byCharLogical(target.state, range, false));
932function selectByGroup(view, forward) {
933 return extendSel(view, range => view.moveByGroup(range, forward));
934}
935/**
936Move the selection head one [group](https://codemirror.net/6/docs/ref/#commands.cursorGroupLeft) to
937the left.
938*/
939const selectGroupLeft = view => selectByGroup(view, !ltrAtCursor(view));
940/**
941Move the selection head one group to the right.
942*/
943const selectGroupRight = view => selectByGroup(view, ltrAtCursor(view));
944/**
945Move the selection head one group forward.
946*/
947const selectGroupForward = view => selectByGroup(view, true);
948/**
949Move the selection head one group backward.
950*/
951const selectGroupBackward = view => selectByGroup(view, false);
952/**
953Move the selection head one group forward in the default Windows
954style, skipping to the start of the next group.
955*/
956const selectGroupForwardWin = view => {
957 return extendSel(view, range => view.moveByChar(range, true, start => toGroupStart(view, range.head, start)));
958};
959function selectBySubword(view, forward) {
960 return extendSel(view, range => moveBySubword(view, range, forward));
961}
962/**
963Move the selection head one group or camel-case subword forward.
964*/
965const selectSubwordForward = view => selectBySubword(view, true);
966/**
967Move the selection head one group or subword backward.
968*/
969const selectSubwordBackward = view => selectBySubword(view, false);
970/**
971Move the selection head over the next syntactic element to the left.
972*/
973const selectSyntaxLeft = view => extendSel(view, range => moveBySyntax(view.state, range, !ltrAtCursor(view)));
974/**
975Move the selection head over the next syntactic element to the right.
976*/
977const selectSyntaxRight = view => extendSel(view, range => moveBySyntax(view.state, range, ltrAtCursor(view)));
978function selectByLine(view, forward) {
979 return extendSel(view, range => view.moveVertically(range, forward));
980}
981/**
982Move the selection head one line up.
983*/
984const selectLineUp = view => selectByLine(view, false);
985/**
986Move the selection head one line down.
987*/
988const selectLineDown = view => selectByLine(view, true);
989function selectByPage(view, forward) {
990 return extendSel(view, range => view.moveVertically(range, forward, pageInfo(view).height));
991}
992/**
993Move the selection head one page up.
994*/
995const selectPageUp = view => selectByPage(view, false);
996/**
997Move the selection head one page down.
998*/
999const selectPageDown = view => selectByPage(view, true);
1000/**
1001Move the selection head to the next line boundary.
1002*/
1003const selectLineBoundaryForward = view => extendSel(view, range => moveByLineBoundary(view, range, true));
1004/**
1005Move the selection head to the previous line boundary.
1006*/
1007const selectLineBoundaryBackward = view => extendSel(view, range => moveByLineBoundary(view, range, false));
1008/**
1009Move the selection head one line boundary to the left.
1010*/
1011const selectLineBoundaryLeft = view => extendSel(view, range => moveByLineBoundary(view, range, !ltrAtCursor(view)));
1012/**
1013Move the selection head one line boundary to the right.
1014*/
1015const selectLineBoundaryRight = view => extendSel(view, range => moveByLineBoundary(view, range, ltrAtCursor(view)));
1016/**
1017Move the selection head to the start of the line.
1018*/
1019const selectLineStart = view => extendSel(view, range => state.EditorSelection.cursor(view.lineBlockAt(range.head).from));
1020/**
1021Move the selection head to the end of the line.
1022*/
1023const selectLineEnd = view => extendSel(view, range => state.EditorSelection.cursor(view.lineBlockAt(range.head).to));
1024/**
1025Move the selection to the start of the document.
1026*/
1027const cursorDocStart = ({ state, dispatch }) => {
1028 dispatch(setSel(state, { anchor: 0 }));
1029 return true;
1030};
1031/**
1032Move the selection to the end of the document.
1033*/
1034const cursorDocEnd = ({ state, dispatch }) => {
1035 dispatch(setSel(state, { anchor: state.doc.length }));
1036 return true;
1037};
1038/**
1039Move the selection head to the start of the document.
1040*/
1041const selectDocStart = ({ state, dispatch }) => {
1042 dispatch(setSel(state, { anchor: state.selection.main.anchor, head: 0 }));
1043 return true;
1044};
1045/**
1046Move the selection head to the end of the document.
1047*/
1048const selectDocEnd = ({ state, dispatch }) => {
1049 dispatch(setSel(state, { anchor: state.selection.main.anchor, head: state.doc.length }));
1050 return true;
1051};
1052/**
1053Select the entire document.
1054*/
1055const selectAll = ({ state, dispatch }) => {
1056 dispatch(state.update({ selection: { anchor: 0, head: state.doc.length }, userEvent: "select" }));
1057 return true;
1058};
1059/**
1060Expand the selection to cover entire lines.
1061*/
1062const selectLine = ({ state: state$1, dispatch }) => {
1063 let ranges = selectedLineBlocks(state$1).map(({ from, to }) => state.EditorSelection.range(from, Math.min(to + 1, state$1.doc.length)));
1064 dispatch(state$1.update({ selection: state.EditorSelection.create(ranges), userEvent: "select" }));
1065 return true;
1066};
1067/**
1068Select the next syntactic construct that is larger than the
1069selection. Note that this will only work insofar as the language
1070[provider](https://codemirror.net/6/docs/ref/#language.language) you use builds up a full
1071syntax tree.
1072*/
1073const selectParentSyntax = ({ state: state$1, dispatch }) => {
1074 let selection = updateSel(state$1.selection, range => {
1075 let tree = language.syntaxTree(state$1), stack = tree.resolveStack(range.from, 1);
1076 if (range.empty) {
1077 let stackBefore = tree.resolveStack(range.from, -1);
1078 if (stackBefore.node.from >= stack.node.from && stackBefore.node.to <= stack.node.to)
1079 stack = stackBefore;
1080 }
1081 for (let cur = stack; cur; cur = cur.next) {
1082 let { node } = cur;
1083 if (((node.from < range.from && node.to >= range.to) ||
1084 (node.to > range.to && node.from <= range.from)) &&
1085 cur.next)
1086 return state.EditorSelection.range(node.to, node.from);
1087 }
1088 return range;
1089 });
1090 if (selection.eq(state$1.selection))
1091 return false;
1092 dispatch(setSel(state$1, selection));
1093 return true;
1094};
1095function addCursorVertically(view, forward) {
1096 let { state: state$1 } = view, sel = state$1.selection, ranges = state$1.selection.ranges.slice();
1097 for (let range of state$1.selection.ranges) {
1098 let line = state$1.doc.lineAt(range.head);
1099 if (forward ? line.to < view.state.doc.length : line.from > 0)
1100 for (let cur = range;;) {
1101 let next = view.moveVertically(cur, forward);
1102 if (next.head < line.from || next.head > line.to) {
1103 if (!ranges.some(r => r.head == next.head))
1104 ranges.push(next);
1105 break;
1106 }
1107 else if (next.head == cur.head) {
1108 break;
1109 }
1110 else {
1111 cur = next;
1112 }
1113 }
1114 }
1115 if (ranges.length == sel.ranges.length)
1116 return false;
1117 view.dispatch(setSel(state$1, state.EditorSelection.create(ranges, ranges.length - 1)));
1118 return true;
1119}
1120/**
1121Expand the selection by adding a cursor above the heads of
1122currently selected ranges.
1123*/
1124const addCursorAbove = view => addCursorVertically(view, false);
1125/**
1126Expand the selection by adding a cursor below the heads of
1127currently selected ranges.
1128*/
1129const addCursorBelow = view => addCursorVertically(view, true);
1130/**
1131Simplify the current selection. When multiple ranges are selected,
1132reduce it to its main range. Otherwise, if the selection is
1133non-empty, convert it to a cursor selection.
1134*/
1135const simplifySelection = ({ state: state$1, dispatch }) => {
1136 let cur = state$1.selection, selection = null;
1137 if (cur.ranges.length > 1)
1138 selection = state.EditorSelection.create([cur.main]);
1139 else if (!cur.main.empty)
1140 selection = state.EditorSelection.create([state.EditorSelection.cursor(cur.main.head)]);
1141 if (!selection)
1142 return false;
1143 dispatch(setSel(state$1, selection));
1144 return true;
1145};
1146function deleteBy(target, by) {
1147 if (target.state.readOnly)
1148 return false;
1149 let event = "delete.selection", { state: state$1 } = target;
1150 let changes = state$1.changeByRange(range => {
1151 let { from, to } = range;
1152 if (from == to) {
1153 let towards = by(range);
1154 if (towards < from) {
1155 event = "delete.backward";
1156 towards = skipAtomic(target, towards, false);
1157 }
1158 else if (towards > from) {
1159 event = "delete.forward";
1160 towards = skipAtomic(target, towards, true);
1161 }
1162 from = Math.min(from, towards);
1163 to = Math.max(to, towards);
1164 }
1165 else {
1166 from = skipAtomic(target, from, false);
1167 to = skipAtomic(target, to, true);
1168 }
1169 return from == to ? { range } : { changes: { from, to }, range: state.EditorSelection.cursor(from, from < range.head ? -1 : 1) };
1170 });
1171 if (changes.changes.empty)
1172 return false;
1173 target.dispatch(state$1.update(changes, {
1174 scrollIntoView: true,
1175 userEvent: event,
1176 effects: event == "delete.selection" ? view.EditorView.announce.of(state$1.phrase("Selection deleted")) : undefined
1177 }));
1178 return true;
1179}
1180function skipAtomic(target, pos, forward) {
1181 if (target instanceof view.EditorView)
1182 for (let ranges of target.state.facet(view.EditorView.atomicRanges).map(f => f(target)))
1183 ranges.between(pos, pos, (from, to) => {
1184 if (from < pos && to > pos)
1185 pos = forward ? to : from;
1186 });
1187 return pos;
1188}
1189const deleteByChar = (target, forward, byIndentUnit) => deleteBy(target, range => {
1190 let pos = range.from, { state: state$1 } = target, line = state$1.doc.lineAt(pos), before, targetPos;
1191 if (byIndentUnit && !forward && pos > line.from && pos < line.from + 200 &&
1192 !/[^ \t]/.test(before = line.text.slice(0, pos - line.from))) {
1193 if (before[before.length - 1] == "\t")
1194 return pos - 1;
1195 let col = state.countColumn(before, state$1.tabSize), drop = col % language.getIndentUnit(state$1) || language.getIndentUnit(state$1);
1196 for (let i = 0; i < drop && before[before.length - 1 - i] == " "; i++)
1197 pos--;
1198 targetPos = pos;
1199 }
1200 else {
1201 targetPos = state.findClusterBreak(line.text, pos - line.from, forward, forward) + line.from;
1202 if (targetPos == pos && line.number != (forward ? state$1.doc.lines : 1))
1203 targetPos += forward ? 1 : -1;
1204 else if (!forward && /[\ufe00-\ufe0f]/.test(line.text.slice(targetPos - line.from, pos - line.from)))
1205 targetPos = state.findClusterBreak(line.text, targetPos - line.from, false, false) + line.from;
1206 }
1207 return targetPos;
1208});
1209/**
1210Delete the selection, or, for cursor selections, the character or
1211indentation unit before the cursor.
1212*/
1213const deleteCharBackward = view => deleteByChar(view, false, true);
1214/**
1215Delete the selection or the character before the cursor. Does not
1216implement any extended behavior like deleting whole indentation
1217units in one go.
1218*/
1219const deleteCharBackwardStrict = view => deleteByChar(view, false, false);
1220/**
1221Delete the selection or the character after the cursor.
1222*/
1223const deleteCharForward = view => deleteByChar(view, true, false);
1224const deleteByGroup = (target, forward) => deleteBy(target, range => {
1225 let pos = range.head, { state: state$1 } = target, line = state$1.doc.lineAt(pos);
1226 let categorize = state$1.charCategorizer(pos);
1227 for (let cat = null;;) {
1228 if (pos == (forward ? line.to : line.from)) {
1229 if (pos == range.head && line.number != (forward ? state$1.doc.lines : 1))
1230 pos += forward ? 1 : -1;
1231 break;
1232 }
1233 let next = state.findClusterBreak(line.text, pos - line.from, forward) + line.from;
1234 let nextChar = line.text.slice(Math.min(pos, next) - line.from, Math.max(pos, next) - line.from);
1235 let nextCat = categorize(nextChar);
1236 if (cat != null && nextCat != cat)
1237 break;
1238 if (nextChar != " " || pos != range.head)
1239 cat = nextCat;
1240 pos = next;
1241 }
1242 return pos;
1243});
1244/**
1245Delete the selection or backward until the end of the next
1246[group](https://codemirror.net/6/docs/ref/#view.EditorView.moveByGroup), only skipping groups of
1247whitespace when they consist of a single space.
1248*/
1249const deleteGroupBackward = target => deleteByGroup(target, false);
1250/**
1251Delete the selection or forward until the end of the next group.
1252*/
1253const deleteGroupForward = target => deleteByGroup(target, true);
1254/**
1255Variant of [`deleteGroupForward`](https://codemirror.net/6/docs/ref/#commands.deleteGroupForward)
1256that uses the Windows convention of also deleting the whitespace
1257after a word.
1258*/
1259const deleteGroupForwardWin = view => deleteBy(view, range => view.moveByChar(range, true, start => toGroupStart(view, range.head, start)).head);
1260/**
1261Delete the selection, or, if it is a cursor selection, delete to
1262the end of the line. If the cursor is directly at the end of the
1263line, delete the line break after it.
1264*/
1265const deleteToLineEnd = view => deleteBy(view, range => {
1266 let lineEnd = view.lineBlockAt(range.head).to;
1267 return range.head < lineEnd ? lineEnd : Math.min(view.state.doc.length, range.head + 1);
1268});
1269/**
1270Delete the selection, or, if it is a cursor selection, delete to
1271the start of the line. If the cursor is directly at the start of the
1272line, delete the line break before it.
1273*/
1274const deleteToLineStart = view => deleteBy(view, range => {
1275 let lineStart = view.lineBlockAt(range.head).from;
1276 return range.head > lineStart ? lineStart : Math.max(0, range.head - 1);
1277});
1278/**
1279Delete the selection, or, if it is a cursor selection, delete to
1280the start of the line or the next line wrap before the cursor.
1281*/
1282const deleteLineBoundaryBackward = view => deleteBy(view, range => {
1283 let lineStart = view.moveToLineBoundary(range, false).head;
1284 return range.head > lineStart ? lineStart : Math.max(0, range.head - 1);
1285});
1286/**
1287Delete the selection, or, if it is a cursor selection, delete to
1288the end of the line or the next line wrap after the cursor.
1289*/
1290const deleteLineBoundaryForward = view => deleteBy(view, range => {
1291 let lineStart = view.moveToLineBoundary(range, true).head;
1292 return range.head < lineStart ? lineStart : Math.min(view.state.doc.length, range.head + 1);
1293});
1294/**
1295Delete all whitespace directly before a line end from the
1296document.
1297*/
1298const deleteTrailingWhitespace = ({ state, dispatch }) => {
1299 if (state.readOnly)
1300 return false;
1301 let changes = [];
1302 for (let pos = 0, prev = "", iter = state.doc.iter();;) {
1303 iter.next();
1304 if (iter.lineBreak || iter.done) {
1305 let trailing = prev.search(/\s+$/);
1306 if (trailing > -1)
1307 changes.push({ from: pos - (prev.length - trailing), to: pos });
1308 if (iter.done)
1309 break;
1310 prev = "";
1311 }
1312 else {
1313 prev = iter.value;
1314 }
1315 pos += iter.value.length;
1316 }
1317 if (!changes.length)
1318 return false;
1319 dispatch(state.update({ changes, userEvent: "delete" }));
1320 return true;
1321};
1322/**
1323Replace each selection range with a line break, leaving the cursor
1324on the line before the break.
1325*/
1326const splitLine = ({ state: state$1, dispatch }) => {
1327 if (state$1.readOnly)
1328 return false;
1329 let changes = state$1.changeByRange(range => {
1330 return { changes: { from: range.from, to: range.to, insert: state.Text.of(["", ""]) },
1331 range: state.EditorSelection.cursor(range.from) };
1332 });
1333 dispatch(state$1.update(changes, { scrollIntoView: true, userEvent: "input" }));
1334 return true;
1335};
1336/**
1337Flip the characters before and after the cursor(s).
1338*/
1339const transposeChars = ({ state: state$1, dispatch }) => {
1340 if (state$1.readOnly)
1341 return false;
1342 let changes = state$1.changeByRange(range => {
1343 if (!range.empty || range.from == 0 || range.from == state$1.doc.length)
1344 return { range };
1345 let pos = range.from, line = state$1.doc.lineAt(pos);
1346 let from = pos == line.from ? pos - 1 : state.findClusterBreak(line.text, pos - line.from, false) + line.from;
1347 let to = pos == line.to ? pos + 1 : state.findClusterBreak(line.text, pos - line.from, true) + line.from;
1348 return { changes: { from, to, insert: state$1.doc.slice(pos, to).append(state$1.doc.slice(from, pos)) },
1349 range: state.EditorSelection.cursor(to) };
1350 });
1351 if (changes.changes.empty)
1352 return false;
1353 dispatch(state$1.update(changes, { scrollIntoView: true, userEvent: "move.character" }));
1354 return true;
1355};
1356function selectedLineBlocks(state) {
1357 let blocks = [], upto = -1;
1358 for (let range of state.selection.ranges) {
1359 let startLine = state.doc.lineAt(range.from), endLine = state.doc.lineAt(range.to);
1360 if (!range.empty && range.to == endLine.from)
1361 endLine = state.doc.lineAt(range.to - 1);
1362 if (upto >= startLine.number) {
1363 let prev = blocks[blocks.length - 1];
1364 prev.to = endLine.to;
1365 prev.ranges.push(range);
1366 }
1367 else {
1368 blocks.push({ from: startLine.from, to: endLine.to, ranges: [range] });
1369 }
1370 upto = endLine.number + 1;
1371 }
1372 return blocks;
1373}
1374function moveLine(state$1, dispatch, forward) {
1375 if (state$1.readOnly)
1376 return false;
1377 let changes = [], ranges = [];
1378 for (let block of selectedLineBlocks(state$1)) {
1379 if (forward ? block.to == state$1.doc.length : block.from == 0)
1380 continue;
1381 let nextLine = state$1.doc.lineAt(forward ? block.to + 1 : block.from - 1);
1382 let size = nextLine.length + 1;
1383 if (forward) {
1384 changes.push({ from: block.to, to: nextLine.to }, { from: block.from, insert: nextLine.text + state$1.lineBreak });
1385 for (let r of block.ranges)
1386 ranges.push(state.EditorSelection.range(Math.min(state$1.doc.length, r.anchor + size), Math.min(state$1.doc.length, r.head + size)));
1387 }
1388 else {
1389 changes.push({ from: nextLine.from, to: block.from }, { from: block.to, insert: state$1.lineBreak + nextLine.text });
1390 for (let r of block.ranges)
1391 ranges.push(state.EditorSelection.range(r.anchor - size, r.head - size));
1392 }
1393 }
1394 if (!changes.length)
1395 return false;
1396 dispatch(state$1.update({
1397 changes,
1398 scrollIntoView: true,
1399 selection: state.EditorSelection.create(ranges, state$1.selection.mainIndex),
1400 userEvent: "move.line"
1401 }));
1402 return true;
1403}
1404/**
1405Move the selected lines up one line.
1406*/
1407const moveLineUp = ({ state, dispatch }) => moveLine(state, dispatch, false);
1408/**
1409Move the selected lines down one line.
1410*/
1411const moveLineDown = ({ state, dispatch }) => moveLine(state, dispatch, true);
1412function copyLine(state, dispatch, forward) {
1413 if (state.readOnly)
1414 return false;
1415 let changes = [];
1416 for (let block of selectedLineBlocks(state)) {
1417 if (forward)
1418 changes.push({ from: block.from, insert: state.doc.slice(block.from, block.to) + state.lineBreak });
1419 else
1420 changes.push({ from: block.to, insert: state.lineBreak + state.doc.slice(block.from, block.to) });
1421 }
1422 let changeSet = state.changes(changes);
1423 dispatch(state.update({
1424 changes: changeSet,
1425 selection: state.selection.map(changeSet, forward ? 1 : -1),
1426 scrollIntoView: true,
1427 userEvent: "input.copyline"
1428 }));
1429 return true;
1430}
1431/**
1432Create a copy of the selected lines. Keep the selection in the top copy.
1433*/
1434const copyLineUp = ({ state, dispatch }) => copyLine(state, dispatch, false);
1435/**
1436Create a copy of the selected lines. Keep the selection in the bottom copy.
1437*/
1438const copyLineDown = ({ state, dispatch }) => copyLine(state, dispatch, true);
1439/**
1440Delete selected lines.
1441*/
1442const deleteLine = view => {
1443 if (view.state.readOnly)
1444 return false;
1445 let { state } = view, changes = state.changes(selectedLineBlocks(state).map(({ from, to }) => {
1446 if (from > 0)
1447 from--;
1448 else if (to < state.doc.length)
1449 to++;
1450 return { from, to };
1451 }));
1452 let selection = updateSel(state.selection, range => {
1453 let dist = undefined;
1454 if (view.lineWrapping) {
1455 let block = view.lineBlockAt(range.head), pos = view.coordsAtPos(range.head, range.assoc || 1);
1456 if (pos)
1457 dist = (block.bottom + view.documentTop) - pos.bottom + view.defaultLineHeight / 2;
1458 }
1459 return view.moveVertically(range, true, dist);
1460 }).map(changes);
1461 view.dispatch({ changes, selection, scrollIntoView: true, userEvent: "delete.line" });
1462 return true;
1463};
1464/**
1465Replace the selection with a newline.
1466*/
1467const insertNewline = ({ state, dispatch }) => {
1468 dispatch(state.update(state.replaceSelection(state.lineBreak), { scrollIntoView: true, userEvent: "input" }));
1469 return true;
1470};
1471/**
1472Replace the selection with a newline and the same amount of
1473indentation as the line above.
1474*/
1475const insertNewlineKeepIndent = ({ state: state$1, dispatch }) => {
1476 dispatch(state$1.update(state$1.changeByRange(range => {
1477 let indent = /^\s*/.exec(state$1.doc.lineAt(range.from).text)[0];
1478 return {
1479 changes: { from: range.from, to: range.to, insert: state$1.lineBreak + indent },
1480 range: state.EditorSelection.cursor(range.from + indent.length + 1)
1481 };
1482 }), { scrollIntoView: true, userEvent: "input" }));
1483 return true;
1484};
1485function isBetweenBrackets(state, pos) {
1486 if (/\(\)|\[\]|\{\}/.test(state.sliceDoc(pos - 1, pos + 1)))
1487 return { from: pos, to: pos };
1488 let context = language.syntaxTree(state).resolveInner(pos);
1489 let before = context.childBefore(pos), after = context.childAfter(pos), closedBy;
1490 if (before && after && before.to <= pos && after.from >= pos &&
1491 (closedBy = before.type.prop(common.NodeProp.closedBy)) && closedBy.indexOf(after.name) > -1 &&
1492 state.doc.lineAt(before.to).from == state.doc.lineAt(after.from).from &&
1493 !/\S/.test(state.sliceDoc(before.to, after.from)))
1494 return { from: before.to, to: after.from };
1495 return null;
1496}
1497/**
1498Replace the selection with a newline and indent the newly created
1499line(s). If the current line consists only of whitespace, this
1500will also delete that whitespace. When the cursor is between
1501matching brackets, an additional newline will be inserted after
1502the cursor.
1503*/
1504const insertNewlineAndIndent = newlineAndIndent(false);
1505/**
1506Create a blank, indented line below the current line.
1507*/
1508const insertBlankLine = newlineAndIndent(true);
1509function newlineAndIndent(atEof) {
1510 return ({ state: state$1, dispatch }) => {
1511 if (state$1.readOnly)
1512 return false;
1513 let changes = state$1.changeByRange(range => {
1514 let { from, to } = range, line = state$1.doc.lineAt(from);
1515 let explode = !atEof && from == to && isBetweenBrackets(state$1, from);
1516 if (atEof)
1517 from = to = (to <= line.to ? line : state$1.doc.lineAt(to)).to;
1518 let cx = new language.IndentContext(state$1, { simulateBreak: from, simulateDoubleBreak: !!explode });
1519 let indent = language.getIndentation(cx, from);
1520 if (indent == null)
1521 indent = state.countColumn(/^\s*/.exec(state$1.doc.lineAt(from).text)[0], state$1.tabSize);
1522 while (to < line.to && /\s/.test(line.text[to - line.from]))
1523 to++;
1524 if (explode)
1525 ({ from, to } = explode);
1526 else if (from > line.from && from < line.from + 100 && !/\S/.test(line.text.slice(0, from)))
1527 from = line.from;
1528 let insert = ["", language.indentString(state$1, indent)];
1529 if (explode)
1530 insert.push(language.indentString(state$1, cx.lineIndent(line.from, -1)));
1531 return { changes: { from, to, insert: state.Text.of(insert) },
1532 range: state.EditorSelection.cursor(from + 1 + insert[1].length) };
1533 });
1534 dispatch(state$1.update(changes, { scrollIntoView: true, userEvent: "input" }));
1535 return true;
1536 };
1537}
1538function changeBySelectedLine(state$1, f) {
1539 let atLine = -1;
1540 return state$1.changeByRange(range => {
1541 let changes = [];
1542 for (let pos = range.from; pos <= range.to;) {
1543 let line = state$1.doc.lineAt(pos);
1544 if (line.number > atLine && (range.empty || range.to > line.from)) {
1545 f(line, changes, range);
1546 atLine = line.number;
1547 }
1548 pos = line.to + 1;
1549 }
1550 let changeSet = state$1.changes(changes);
1551 return { changes,
1552 range: state.EditorSelection.range(changeSet.mapPos(range.anchor, 1), changeSet.mapPos(range.head, 1)) };
1553 });
1554}
1555/**
1556Auto-indent the selected lines. This uses the [indentation service
1557facet](https://codemirror.net/6/docs/ref/#language.indentService) as source for auto-indent
1558information.
1559*/
1560const indentSelection = ({ state, dispatch }) => {
1561 if (state.readOnly)
1562 return false;
1563 let updated = Object.create(null);
1564 let context = new language.IndentContext(state, { overrideIndentation: start => {
1565 let found = updated[start];
1566 return found == null ? -1 : found;
1567 } });
1568 let changes = changeBySelectedLine(state, (line, changes, range) => {
1569 let indent = language.getIndentation(context, line.from);
1570 if (indent == null)
1571 return;
1572 if (!/\S/.test(line.text))
1573 indent = 0;
1574 let cur = /^\s*/.exec(line.text)[0];
1575 let norm = language.indentString(state, indent);
1576 if (cur != norm || range.from < line.from + cur.length) {
1577 updated[line.from] = indent;
1578 changes.push({ from: line.from, to: line.from + cur.length, insert: norm });
1579 }
1580 });
1581 if (!changes.changes.empty)
1582 dispatch(state.update(changes, { userEvent: "indent" }));
1583 return true;
1584};
1585/**
1586Add a [unit](https://codemirror.net/6/docs/ref/#language.indentUnit) of indentation to all selected
1587lines.
1588*/
1589const indentMore = ({ state, dispatch }) => {
1590 if (state.readOnly)
1591 return false;
1592 dispatch(state.update(changeBySelectedLine(state, (line, changes) => {
1593 changes.push({ from: line.from, insert: state.facet(language.indentUnit) });
1594 }), { userEvent: "input.indent" }));
1595 return true;
1596};
1597/**
1598Remove a [unit](https://codemirror.net/6/docs/ref/#language.indentUnit) of indentation from all
1599selected lines.
1600*/
1601const indentLess = ({ state: state$1, dispatch }) => {
1602 if (state$1.readOnly)
1603 return false;
1604 dispatch(state$1.update(changeBySelectedLine(state$1, (line, changes) => {
1605 let space = /^\s*/.exec(line.text)[0];
1606 if (!space)
1607 return;
1608 let col = state.countColumn(space, state$1.tabSize), keep = 0;
1609 let insert = language.indentString(state$1, Math.max(0, col - language.getIndentUnit(state$1)));
1610 while (keep < space.length && keep < insert.length && space.charCodeAt(keep) == insert.charCodeAt(keep))
1611 keep++;
1612 changes.push({ from: line.from + keep, to: line.from + space.length, insert: insert.slice(keep) });
1613 }), { userEvent: "delete.dedent" }));
1614 return true;
1615};
1616/**
1617Enables or disables
1618[tab-focus mode](https://codemirror.net/6/docs/ref/#view.EditorView.setTabFocusMode). While on, this
1619prevents the editor's key bindings from capturing Tab or
1620Shift-Tab, making it possible for the user to move focus out of
1621the editor with the keyboard.
1622*/
1623const toggleTabFocusMode = view => {
1624 view.setTabFocusMode();
1625 return true;
1626};
1627/**
1628Temporarily enables [tab-focus
1629mode](https://codemirror.net/6/docs/ref/#view.EditorView.setTabFocusMode) for two seconds or until
1630another key is pressed.
1631*/
1632const temporarilySetTabFocusMode = view => {
1633 view.setTabFocusMode(2000);
1634 return true;
1635};
1636/**
1637Insert a tab character at the cursor or, if something is selected,
1638use [`indentMore`](https://codemirror.net/6/docs/ref/#commands.indentMore) to indent the entire
1639selection.
1640*/
1641const insertTab = ({ state, dispatch }) => {
1642 if (state.selection.ranges.some(r => !r.empty))
1643 return indentMore({ state, dispatch });
1644 dispatch(state.update(state.replaceSelection("\t"), { scrollIntoView: true, userEvent: "input" }));
1645 return true;
1646};
1647/**
1648Array of key bindings containing the Emacs-style bindings that are
1649available on macOS by default.
1650
1651 - Ctrl-b: [`cursorCharLeft`](https://codemirror.net/6/docs/ref/#commands.cursorCharLeft) ([`selectCharLeft`](https://codemirror.net/6/docs/ref/#commands.selectCharLeft) with Shift)
1652 - Ctrl-f: [`cursorCharRight`](https://codemirror.net/6/docs/ref/#commands.cursorCharRight) ([`selectCharRight`](https://codemirror.net/6/docs/ref/#commands.selectCharRight) with Shift)
1653 - Ctrl-p: [`cursorLineUp`](https://codemirror.net/6/docs/ref/#commands.cursorLineUp) ([`selectLineUp`](https://codemirror.net/6/docs/ref/#commands.selectLineUp) with Shift)
1654 - Ctrl-n: [`cursorLineDown`](https://codemirror.net/6/docs/ref/#commands.cursorLineDown) ([`selectLineDown`](https://codemirror.net/6/docs/ref/#commands.selectLineDown) with Shift)
1655 - Ctrl-a: [`cursorLineStart`](https://codemirror.net/6/docs/ref/#commands.cursorLineStart) ([`selectLineStart`](https://codemirror.net/6/docs/ref/#commands.selectLineStart) with Shift)
1656 - Ctrl-e: [`cursorLineEnd`](https://codemirror.net/6/docs/ref/#commands.cursorLineEnd) ([`selectLineEnd`](https://codemirror.net/6/docs/ref/#commands.selectLineEnd) with Shift)
1657 - Ctrl-d: [`deleteCharForward`](https://codemirror.net/6/docs/ref/#commands.deleteCharForward)
1658 - Ctrl-h: [`deleteCharBackward`](https://codemirror.net/6/docs/ref/#commands.deleteCharBackward)
1659 - Ctrl-k: [`deleteToLineEnd`](https://codemirror.net/6/docs/ref/#commands.deleteToLineEnd)
1660 - Ctrl-Alt-h: [`deleteGroupBackward`](https://codemirror.net/6/docs/ref/#commands.deleteGroupBackward)
1661 - Ctrl-o: [`splitLine`](https://codemirror.net/6/docs/ref/#commands.splitLine)
1662 - Ctrl-t: [`transposeChars`](https://codemirror.net/6/docs/ref/#commands.transposeChars)
1663 - Ctrl-v: [`cursorPageDown`](https://codemirror.net/6/docs/ref/#commands.cursorPageDown)
1664 - Alt-v: [`cursorPageUp`](https://codemirror.net/6/docs/ref/#commands.cursorPageUp)
1665*/
1666const emacsStyleKeymap = [
1667 { key: "Ctrl-b", run: cursorCharLeft, shift: selectCharLeft, preventDefault: true },
1668 { key: "Ctrl-f", run: cursorCharRight, shift: selectCharRight },
1669 { key: "Ctrl-p", run: cursorLineUp, shift: selectLineUp },
1670 { key: "Ctrl-n", run: cursorLineDown, shift: selectLineDown },
1671 { key: "Ctrl-a", run: cursorLineStart, shift: selectLineStart },
1672 { key: "Ctrl-e", run: cursorLineEnd, shift: selectLineEnd },
1673 { key: "Ctrl-d", run: deleteCharForward },
1674 { key: "Ctrl-h", run: deleteCharBackward },
1675 { key: "Ctrl-k", run: deleteToLineEnd },
1676 { key: "Ctrl-Alt-h", run: deleteGroupBackward },
1677 { key: "Ctrl-o", run: splitLine },
1678 { key: "Ctrl-t", run: transposeChars },
1679 { key: "Ctrl-v", run: cursorPageDown },
1680];
1681/**
1682An array of key bindings closely sticking to platform-standard or
1683widely used bindings. (This includes the bindings from
1684[`emacsStyleKeymap`](https://codemirror.net/6/docs/ref/#commands.emacsStyleKeymap), with their `key`
1685property changed to `mac`.)
1686
1687 - ArrowLeft: [`cursorCharLeft`](https://codemirror.net/6/docs/ref/#commands.cursorCharLeft) ([`selectCharLeft`](https://codemirror.net/6/docs/ref/#commands.selectCharLeft) with Shift)
1688 - ArrowRight: [`cursorCharRight`](https://codemirror.net/6/docs/ref/#commands.cursorCharRight) ([`selectCharRight`](https://codemirror.net/6/docs/ref/#commands.selectCharRight) with Shift)
1689 - 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)
1690 - 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)
1691 - Cmd-ArrowLeft (on macOS): [`cursorLineStart`](https://codemirror.net/6/docs/ref/#commands.cursorLineStart) ([`selectLineStart`](https://codemirror.net/6/docs/ref/#commands.selectLineStart) with Shift)
1692 - Cmd-ArrowRight (on macOS): [`cursorLineEnd`](https://codemirror.net/6/docs/ref/#commands.cursorLineEnd) ([`selectLineEnd`](https://codemirror.net/6/docs/ref/#commands.selectLineEnd) with Shift)
1693 - ArrowUp: [`cursorLineUp`](https://codemirror.net/6/docs/ref/#commands.cursorLineUp) ([`selectLineUp`](https://codemirror.net/6/docs/ref/#commands.selectLineUp) with Shift)
1694 - ArrowDown: [`cursorLineDown`](https://codemirror.net/6/docs/ref/#commands.cursorLineDown) ([`selectLineDown`](https://codemirror.net/6/docs/ref/#commands.selectLineDown) with Shift)
1695 - Cmd-ArrowUp (on macOS): [`cursorDocStart`](https://codemirror.net/6/docs/ref/#commands.cursorDocStart) ([`selectDocStart`](https://codemirror.net/6/docs/ref/#commands.selectDocStart) with Shift)
1696 - Cmd-ArrowDown (on macOS): [`cursorDocEnd`](https://codemirror.net/6/docs/ref/#commands.cursorDocEnd) ([`selectDocEnd`](https://codemirror.net/6/docs/ref/#commands.selectDocEnd) with Shift)
1697 - Ctrl-ArrowUp (on macOS): [`cursorPageUp`](https://codemirror.net/6/docs/ref/#commands.cursorPageUp) ([`selectPageUp`](https://codemirror.net/6/docs/ref/#commands.selectPageUp) with Shift)
1698 - Ctrl-ArrowDown (on macOS): [`cursorPageDown`](https://codemirror.net/6/docs/ref/#commands.cursorPageDown) ([`selectPageDown`](https://codemirror.net/6/docs/ref/#commands.selectPageDown) with Shift)
1699 - PageUp: [`cursorPageUp`](https://codemirror.net/6/docs/ref/#commands.cursorPageUp) ([`selectPageUp`](https://codemirror.net/6/docs/ref/#commands.selectPageUp) with Shift)
1700 - PageDown: [`cursorPageDown`](https://codemirror.net/6/docs/ref/#commands.cursorPageDown) ([`selectPageDown`](https://codemirror.net/6/docs/ref/#commands.selectPageDown) with Shift)
1701 - Home: [`cursorLineBoundaryBackward`](https://codemirror.net/6/docs/ref/#commands.cursorLineBoundaryBackward) ([`selectLineBoundaryBackward`](https://codemirror.net/6/docs/ref/#commands.selectLineBoundaryBackward) with Shift)
1702 - End: [`cursorLineBoundaryForward`](https://codemirror.net/6/docs/ref/#commands.cursorLineBoundaryForward) ([`selectLineBoundaryForward`](https://codemirror.net/6/docs/ref/#commands.selectLineBoundaryForward) with Shift)
1703 - 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)
1704 - 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)
1705 - Enter and Shift-Enter: [`insertNewlineAndIndent`](https://codemirror.net/6/docs/ref/#commands.insertNewlineAndIndent)
1706 - Ctrl-a (Cmd-a on macOS): [`selectAll`](https://codemirror.net/6/docs/ref/#commands.selectAll)
1707 - Backspace: [`deleteCharBackward`](https://codemirror.net/6/docs/ref/#commands.deleteCharBackward)
1708 - Delete: [`deleteCharForward`](https://codemirror.net/6/docs/ref/#commands.deleteCharForward)
1709 - Ctrl-Backspace (Alt-Backspace on macOS): [`deleteGroupBackward`](https://codemirror.net/6/docs/ref/#commands.deleteGroupBackward)
1710 - Ctrl-Delete (Alt-Delete on macOS): [`deleteGroupForward`](https://codemirror.net/6/docs/ref/#commands.deleteGroupForward)
1711 - Cmd-Backspace (macOS): [`deleteLineBoundaryBackward`](https://codemirror.net/6/docs/ref/#commands.deleteLineBoundaryBackward).
1712 - Cmd-Delete (macOS): [`deleteLineBoundaryForward`](https://codemirror.net/6/docs/ref/#commands.deleteLineBoundaryForward).
1713*/
1714const standardKeymap = [
1715 { key: "ArrowLeft", run: cursorCharLeft, shift: selectCharLeft, preventDefault: true },
1716 { key: "Mod-ArrowLeft", mac: "Alt-ArrowLeft", run: cursorGroupLeft, shift: selectGroupLeft, preventDefault: true },
1717 { mac: "Cmd-ArrowLeft", run: cursorLineBoundaryLeft, shift: selectLineBoundaryLeft, preventDefault: true },
1718 { key: "ArrowRight", run: cursorCharRight, shift: selectCharRight, preventDefault: true },
1719 { key: "Mod-ArrowRight", mac: "Alt-ArrowRight", run: cursorGroupRight, shift: selectGroupRight, preventDefault: true },
1720 { mac: "Cmd-ArrowRight", run: cursorLineBoundaryRight, shift: selectLineBoundaryRight, preventDefault: true },
1721 { key: "ArrowUp", run: cursorLineUp, shift: selectLineUp, preventDefault: true },
1722 { mac: "Cmd-ArrowUp", run: cursorDocStart, shift: selectDocStart },
1723 { mac: "Ctrl-ArrowUp", run: cursorPageUp, shift: selectPageUp },
1724 { key: "ArrowDown", run: cursorLineDown, shift: selectLineDown, preventDefault: true },
1725 { mac: "Cmd-ArrowDown", run: cursorDocEnd, shift: selectDocEnd },
1726 { mac: "Ctrl-ArrowDown", run: cursorPageDown, shift: selectPageDown },
1727 { key: "PageUp", run: cursorPageUp, shift: selectPageUp },
1728 { key: "PageDown", run: cursorPageDown, shift: selectPageDown },
1729 { key: "Home", run: cursorLineBoundaryBackward, shift: selectLineBoundaryBackward, preventDefault: true },
1730 { key: "Mod-Home", run: cursorDocStart, shift: selectDocStart },
1731 { key: "End", run: cursorLineBoundaryForward, shift: selectLineBoundaryForward, preventDefault: true },
1732 { key: "Mod-End", run: cursorDocEnd, shift: selectDocEnd },
1733 { key: "Enter", run: insertNewlineAndIndent, shift: insertNewlineAndIndent },
1734 { key: "Mod-a", run: selectAll },
1735 { key: "Backspace", run: deleteCharBackward, shift: deleteCharBackward, preventDefault: true },
1736 { key: "Delete", run: deleteCharForward, preventDefault: true },
1737 { key: "Mod-Backspace", mac: "Alt-Backspace", run: deleteGroupBackward, preventDefault: true },
1738 { key: "Mod-Delete", mac: "Alt-Delete", run: deleteGroupForward, preventDefault: true },
1739 { mac: "Mod-Backspace", run: deleteLineBoundaryBackward, preventDefault: true },
1740 { mac: "Mod-Delete", run: deleteLineBoundaryForward, preventDefault: true }
1741].concat(emacsStyleKeymap.map(b => ({ mac: b.key, run: b.run, shift: b.shift })));
1742/**
1743The default keymap. Includes all bindings from
1744[`standardKeymap`](https://codemirror.net/6/docs/ref/#commands.standardKeymap) plus the following:
1745
1746- 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)
1747- 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)
1748- Alt-ArrowUp: [`moveLineUp`](https://codemirror.net/6/docs/ref/#commands.moveLineUp)
1749- Alt-ArrowDown: [`moveLineDown`](https://codemirror.net/6/docs/ref/#commands.moveLineDown)
1750- Shift-Alt-ArrowUp: [`copyLineUp`](https://codemirror.net/6/docs/ref/#commands.copyLineUp)
1751- Shift-Alt-ArrowDown: [`copyLineDown`](https://codemirror.net/6/docs/ref/#commands.copyLineDown)
1752- Ctrl-Alt-ArrowUp (Cmd-Alt-ArrowUp on macOS): [`addCursorAbove`](https://codemirror.net/6/docs/ref/#commands.addCursorAbove).
1753- Ctrl-Alt-ArrowDown (Cmd-Alt-ArrowDown on macOS): [`addCursorBelow`](https://codemirror.net/6/docs/ref/#commands.addCursorBelow).
1754- Escape: [`simplifySelection`](https://codemirror.net/6/docs/ref/#commands.simplifySelection)
1755- Ctrl-Enter (Cmd-Enter on macOS): [`insertBlankLine`](https://codemirror.net/6/docs/ref/#commands.insertBlankLine)
1756- Alt-l (Ctrl-l on macOS): [`selectLine`](https://codemirror.net/6/docs/ref/#commands.selectLine)
1757- Ctrl-i (Cmd-i on macOS): [`selectParentSyntax`](https://codemirror.net/6/docs/ref/#commands.selectParentSyntax)
1758- Ctrl-[ (Cmd-[ on macOS): [`indentLess`](https://codemirror.net/6/docs/ref/#commands.indentLess)
1759- Ctrl-] (Cmd-] on macOS): [`indentMore`](https://codemirror.net/6/docs/ref/#commands.indentMore)
1760- Ctrl-Alt-\\ (Cmd-Alt-\\ on macOS): [`indentSelection`](https://codemirror.net/6/docs/ref/#commands.indentSelection)
1761- Shift-Ctrl-k (Shift-Cmd-k on macOS): [`deleteLine`](https://codemirror.net/6/docs/ref/#commands.deleteLine)
1762- Shift-Ctrl-\\ (Shift-Cmd-\\ on macOS): [`cursorMatchingBracket`](https://codemirror.net/6/docs/ref/#commands.cursorMatchingBracket)
1763- Ctrl-/ (Cmd-/ on macOS): [`toggleComment`](https://codemirror.net/6/docs/ref/#commands.toggleComment).
1764- Shift-Alt-a: [`toggleBlockComment`](https://codemirror.net/6/docs/ref/#commands.toggleBlockComment).
1765- Ctrl-m (Alt-Shift-m on macOS): [`toggleTabFocusMode`](https://codemirror.net/6/docs/ref/#commands.toggleTabFocusMode).
1766*/
1767const defaultKeymap = [
1768 { key: "Alt-ArrowLeft", mac: "Ctrl-ArrowLeft", run: cursorSyntaxLeft, shift: selectSyntaxLeft },
1769 { key: "Alt-ArrowRight", mac: "Ctrl-ArrowRight", run: cursorSyntaxRight, shift: selectSyntaxRight },
1770 { key: "Alt-ArrowUp", run: moveLineUp },
1771 { key: "Shift-Alt-ArrowUp", run: copyLineUp },
1772 { key: "Alt-ArrowDown", run: moveLineDown },
1773 { key: "Shift-Alt-ArrowDown", run: copyLineDown },
1774 { key: "Mod-Alt-ArrowUp", run: addCursorAbove },
1775 { key: "Mod-Alt-ArrowDown", run: addCursorBelow },
1776 { key: "Escape", run: simplifySelection },
1777 { key: "Mod-Enter", run: insertBlankLine },
1778 { key: "Alt-l", mac: "Ctrl-l", run: selectLine },
1779 { key: "Mod-i", run: selectParentSyntax, preventDefault: true },
1780 { key: "Mod-[", run: indentLess },
1781 { key: "Mod-]", run: indentMore },
1782 { key: "Mod-Alt-\\", run: indentSelection },
1783 { key: "Shift-Mod-k", run: deleteLine },
1784 { key: "Shift-Mod-\\", run: cursorMatchingBracket },
1785 { key: "Mod-/", run: toggleComment },
1786 { key: "Alt-A", run: toggleBlockComment },
1787 { key: "Ctrl-m", mac: "Shift-Alt-m", run: toggleTabFocusMode },
1788].concat(standardKeymap);
1789/**
1790A binding that binds Tab to [`indentMore`](https://codemirror.net/6/docs/ref/#commands.indentMore) and
1791Shift-Tab to [`indentLess`](https://codemirror.net/6/docs/ref/#commands.indentLess).
1792Please see the [Tab example](../../examples/tab/) before using
1793this.
1794*/
1795const indentWithTab = { key: "Tab", run: indentMore, shift: indentLess };
1796
1797exports.addCursorAbove = addCursorAbove;
1798exports.addCursorBelow = addCursorBelow;
1799exports.blockComment = blockComment;
1800exports.blockUncomment = blockUncomment;
1801exports.copyLineDown = copyLineDown;
1802exports.copyLineUp = copyLineUp;
1803exports.cursorCharBackward = cursorCharBackward;
1804exports.cursorCharBackwardLogical = cursorCharBackwardLogical;
1805exports.cursorCharForward = cursorCharForward;
1806exports.cursorCharForwardLogical = cursorCharForwardLogical;
1807exports.cursorCharLeft = cursorCharLeft;
1808exports.cursorCharRight = cursorCharRight;
1809exports.cursorDocEnd = cursorDocEnd;
1810exports.cursorDocStart = cursorDocStart;
1811exports.cursorGroupBackward = cursorGroupBackward;
1812exports.cursorGroupForward = cursorGroupForward;
1813exports.cursorGroupForwardWin = cursorGroupForwardWin;
1814exports.cursorGroupLeft = cursorGroupLeft;
1815exports.cursorGroupRight = cursorGroupRight;
1816exports.cursorLineBoundaryBackward = cursorLineBoundaryBackward;
1817exports.cursorLineBoundaryForward = cursorLineBoundaryForward;
1818exports.cursorLineBoundaryLeft = cursorLineBoundaryLeft;
1819exports.cursorLineBoundaryRight = cursorLineBoundaryRight;
1820exports.cursorLineDown = cursorLineDown;
1821exports.cursorLineEnd = cursorLineEnd;
1822exports.cursorLineStart = cursorLineStart;
1823exports.cursorLineUp = cursorLineUp;
1824exports.cursorMatchingBracket = cursorMatchingBracket;
1825exports.cursorPageDown = cursorPageDown;
1826exports.cursorPageUp = cursorPageUp;
1827exports.cursorSubwordBackward = cursorSubwordBackward;
1828exports.cursorSubwordForward = cursorSubwordForward;
1829exports.cursorSyntaxLeft = cursorSyntaxLeft;
1830exports.cursorSyntaxRight = cursorSyntaxRight;
1831exports.defaultKeymap = defaultKeymap;
1832exports.deleteCharBackward = deleteCharBackward;
1833exports.deleteCharBackwardStrict = deleteCharBackwardStrict;
1834exports.deleteCharForward = deleteCharForward;
1835exports.deleteGroupBackward = deleteGroupBackward;
1836exports.deleteGroupForward = deleteGroupForward;
1837exports.deleteGroupForwardWin = deleteGroupForwardWin;
1838exports.deleteLine = deleteLine;
1839exports.deleteLineBoundaryBackward = deleteLineBoundaryBackward;
1840exports.deleteLineBoundaryForward = deleteLineBoundaryForward;
1841exports.deleteToLineEnd = deleteToLineEnd;
1842exports.deleteToLineStart = deleteToLineStart;
1843exports.deleteTrailingWhitespace = deleteTrailingWhitespace;
1844exports.emacsStyleKeymap = emacsStyleKeymap;
1845exports.history = history;
1846exports.historyField = historyField;
1847exports.historyKeymap = historyKeymap;
1848exports.indentLess = indentLess;
1849exports.indentMore = indentMore;
1850exports.indentSelection = indentSelection;
1851exports.indentWithTab = indentWithTab;
1852exports.insertBlankLine = insertBlankLine;
1853exports.insertNewline = insertNewline;
1854exports.insertNewlineAndIndent = insertNewlineAndIndent;
1855exports.insertNewlineKeepIndent = insertNewlineKeepIndent;
1856exports.insertTab = insertTab;
1857exports.invertedEffects = invertedEffects;
1858exports.isolateHistory = isolateHistory;
1859exports.lineComment = lineComment;
1860exports.lineUncomment = lineUncomment;
1861exports.moveLineDown = moveLineDown;
1862exports.moveLineUp = moveLineUp;
1863exports.redo = redo;
1864exports.redoDepth = redoDepth;
1865exports.redoSelection = redoSelection;
1866exports.selectAll = selectAll;
1867exports.selectCharBackward = selectCharBackward;
1868exports.selectCharBackwardLogical = selectCharBackwardLogical;
1869exports.selectCharForward = selectCharForward;
1870exports.selectCharForwardLogical = selectCharForwardLogical;
1871exports.selectCharLeft = selectCharLeft;
1872exports.selectCharRight = selectCharRight;
1873exports.selectDocEnd = selectDocEnd;
1874exports.selectDocStart = selectDocStart;
1875exports.selectGroupBackward = selectGroupBackward;
1876exports.selectGroupForward = selectGroupForward;
1877exports.selectGroupForwardWin = selectGroupForwardWin;
1878exports.selectGroupLeft = selectGroupLeft;
1879exports.selectGroupRight = selectGroupRight;
1880exports.selectLine = selectLine;
1881exports.selectLineBoundaryBackward = selectLineBoundaryBackward;
1882exports.selectLineBoundaryForward = selectLineBoundaryForward;
1883exports.selectLineBoundaryLeft = selectLineBoundaryLeft;
1884exports.selectLineBoundaryRight = selectLineBoundaryRight;
1885exports.selectLineDown = selectLineDown;
1886exports.selectLineEnd = selectLineEnd;
1887exports.selectLineStart = selectLineStart;
1888exports.selectLineUp = selectLineUp;
1889exports.selectMatchingBracket = selectMatchingBracket;
1890exports.selectPageDown = selectPageDown;
1891exports.selectPageUp = selectPageUp;
1892exports.selectParentSyntax = selectParentSyntax;
1893exports.selectSubwordBackward = selectSubwordBackward;
1894exports.selectSubwordForward = selectSubwordForward;
1895exports.selectSyntaxLeft = selectSyntaxLeft;
1896exports.selectSyntaxRight = selectSyntaxRight;
1897exports.simplifySelection = simplifySelection;
1898exports.splitLine = splitLine;
1899exports.standardKeymap = standardKeymap;
1900exports.temporarilySetTabFocusMode = temporarilySetTabFocusMode;
1901exports.toggleBlockComment = toggleBlockComment;
1902exports.toggleBlockCommentByLine = toggleBlockCommentByLine;
1903exports.toggleComment = toggleComment;
1904exports.toggleLineComment = toggleLineComment;
1905exports.toggleTabFocusMode = toggleTabFocusMode;
1906exports.transposeChars = transposeChars;
1907exports.undo = undo;
1908exports.undoDepth = undoDepth;
1909exports.undoSelection = undoSelection;