| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- import { deleteNearSelection } from "./deleteNearSelection.js"
- import { runInOp } from "../display/operations.js"
- import { ensureCursorVisible } from "../display/scrolling.js"
- import { endOfLine } from "../input/movement.js"
- import { clipPos, Pos } from "../line/pos.js"
- import { visualLine, visualLineEnd } from "../line/spans.js"
- import { getLine, lineNo } from "../line/utils_line.js"
- import { Range } from "../model/selection.js"
- import { selectAll } from "../model/selection_updates.js"
- import { countColumn, sel_dontScroll, sel_move, spaceStr } from "../util/misc.js"
- import { getOrder } from "../util/bidi.js"
- // Commands are parameter-less actions that can be performed on an
- // editor, mostly used for keybindings.
- export let commands = {
- selectAll: selectAll,
- singleSelection: cm => cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll),
- killLine: cm => deleteNearSelection(cm, range => {
- if (range.empty()) {
- let len = getLine(cm.doc, range.head.line).text.length
- if (range.head.ch == len && range.head.line < cm.lastLine())
- return {from: range.head, to: Pos(range.head.line + 1, 0)}
- else
- return {from: range.head, to: Pos(range.head.line, len)}
- } else {
- return {from: range.from(), to: range.to()}
- }
- }),
- deleteLine: cm => deleteNearSelection(cm, range => ({
- from: Pos(range.from().line, 0),
- to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
- })),
- delLineLeft: cm => deleteNearSelection(cm, range => ({
- from: Pos(range.from().line, 0), to: range.from()
- })),
- delWrappedLineLeft: cm => deleteNearSelection(cm, range => {
- let top = cm.charCoords(range.head, "div").top + 5
- let leftPos = cm.coordsChar({left: 0, top: top}, "div")
- return {from: leftPos, to: range.from()}
- }),
- delWrappedLineRight: cm => deleteNearSelection(cm, range => {
- let top = cm.charCoords(range.head, "div").top + 5
- let rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
- return {from: range.from(), to: rightPos }
- }),
- undo: cm => cm.undo(),
- redo: cm => cm.redo(),
- undoSelection: cm => cm.undoSelection(),
- redoSelection: cm => cm.redoSelection(),
- goDocStart: cm => cm.extendSelection(Pos(cm.firstLine(), 0)),
- goDocEnd: cm => cm.extendSelection(Pos(cm.lastLine())),
- goLineStart: cm => cm.extendSelectionsBy(range => lineStart(cm, range.head.line),
- {origin: "+move", bias: 1}
- ),
- goLineStartSmart: cm => cm.extendSelectionsBy(range => lineStartSmart(cm, range.head),
- {origin: "+move", bias: 1}
- ),
- goLineEnd: cm => cm.extendSelectionsBy(range => lineEnd(cm, range.head.line),
- {origin: "+move", bias: -1}
- ),
- goLineRight: cm => cm.extendSelectionsBy(range => {
- let top = cm.cursorCoords(range.head, "div").top + 5
- return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
- }, sel_move),
- goLineLeft: cm => cm.extendSelectionsBy(range => {
- let top = cm.cursorCoords(range.head, "div").top + 5
- return cm.coordsChar({left: 0, top: top}, "div")
- }, sel_move),
- goLineLeftSmart: cm => cm.extendSelectionsBy(range => {
- let top = cm.cursorCoords(range.head, "div").top + 5
- let pos = cm.coordsChar({left: 0, top: top}, "div")
- if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm, range.head)
- return pos
- }, sel_move),
- goLineUp: cm => cm.moveV(-1, "line"),
- goLineDown: cm => cm.moveV(1, "line"),
- goPageUp: cm => cm.moveV(-1, "page"),
- goPageDown: cm => cm.moveV(1, "page"),
- goCharLeft: cm => cm.moveH(-1, "char"),
- goCharRight: cm => cm.moveH(1, "char"),
- goColumnLeft: cm => cm.moveH(-1, "column"),
- goColumnRight: cm => cm.moveH(1, "column"),
- goWordLeft: cm => cm.moveH(-1, "word"),
- goGroupRight: cm => cm.moveH(1, "group"),
- goGroupLeft: cm => cm.moveH(-1, "group"),
- goWordRight: cm => cm.moveH(1, "word"),
- delCharBefore: cm => cm.deleteH(-1, "codepoint"),
- delCharAfter: cm => cm.deleteH(1, "char"),
- delWordBefore: cm => cm.deleteH(-1, "word"),
- delWordAfter: cm => cm.deleteH(1, "word"),
- delGroupBefore: cm => cm.deleteH(-1, "group"),
- delGroupAfter: cm => cm.deleteH(1, "group"),
- indentAuto: cm => cm.indentSelection("smart"),
- indentMore: cm => cm.indentSelection("add"),
- indentLess: cm => cm.indentSelection("subtract"),
- insertTab: cm => cm.replaceSelection("\t"),
- insertSoftTab: cm => {
- let spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize
- for (let i = 0; i < ranges.length; i++) {
- let pos = ranges[i].from()
- let col = countColumn(cm.getLine(pos.line), pos.ch, tabSize)
- spaces.push(spaceStr(tabSize - col % tabSize))
- }
- cm.replaceSelections(spaces)
- },
- defaultTab: cm => {
- if (cm.somethingSelected()) cm.indentSelection("add")
- else cm.execCommand("insertTab")
- },
- // Swap the two chars left and right of each selection's head.
- // Move cursor behind the two swapped characters afterwards.
- //
- // Doesn't consider line feeds a character.
- // Doesn't scan more than one line above to find a character.
- // Doesn't do anything on an empty line.
- // Doesn't do anything with non-empty selections.
- transposeChars: cm => runInOp(cm, () => {
- let ranges = cm.listSelections(), newSel = []
- for (let i = 0; i < ranges.length; i++) {
- if (!ranges[i].empty()) continue
- let cur = ranges[i].head, line = getLine(cm.doc, cur.line).text
- if (line) {
- if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1)
- if (cur.ch > 0) {
- cur = new Pos(cur.line, cur.ch + 1)
- cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
- Pos(cur.line, cur.ch - 2), cur, "+transpose")
- } else if (cur.line > cm.doc.first) {
- let prev = getLine(cm.doc, cur.line - 1).text
- if (prev) {
- cur = new Pos(cur.line, 1)
- cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
- prev.charAt(prev.length - 1),
- Pos(cur.line - 1, prev.length - 1), cur, "+transpose")
- }
- }
- }
- newSel.push(new Range(cur, cur))
- }
- cm.setSelections(newSel)
- }),
- newlineAndIndent: cm => runInOp(cm, () => {
- let sels = cm.listSelections()
- for (let i = sels.length - 1; i >= 0; i--)
- cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input")
- sels = cm.listSelections()
- for (let i = 0; i < sels.length; i++)
- cm.indentLine(sels[i].from().line, null, true)
- ensureCursorVisible(cm)
- }),
- openLine: cm => cm.replaceSelection("\n", "start"),
- toggleOverwrite: cm => cm.toggleOverwrite()
- }
- function lineStart(cm, lineN) {
- let line = getLine(cm.doc, lineN)
- let visual = visualLine(line)
- if (visual != line) lineN = lineNo(visual)
- return endOfLine(true, cm, visual, lineN, 1)
- }
- function lineEnd(cm, lineN) {
- let line = getLine(cm.doc, lineN)
- let visual = visualLineEnd(line)
- if (visual != line) lineN = lineNo(visual)
- return endOfLine(true, cm, line, lineN, -1)
- }
- function lineStartSmart(cm, pos) {
- let start = lineStart(cm, pos.line)
- let line = getLine(cm.doc, start.line)
- let order = getOrder(line, cm.doc.direction)
- if (!order || order[0].level == 0) {
- let firstNonWS = Math.max(start.ch, line.text.search(/\S/))
- let inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch
- return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
- }
- return start
- }
|