Mercurial
comparison .cms/lib/codemirror/src/edit/commands.js @ 0:78edf6b517a0 draft
24.10
author | Coffee CMS <info@coffee-cms.ru> |
---|---|
date | Fri, 11 Oct 2024 22:40:23 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:78edf6b517a0 |
---|---|
1 import { deleteNearSelection } from "./deleteNearSelection.js" | |
2 import { runInOp } from "../display/operations.js" | |
3 import { ensureCursorVisible } from "../display/scrolling.js" | |
4 import { endOfLine } from "../input/movement.js" | |
5 import { clipPos, Pos } from "../line/pos.js" | |
6 import { visualLine, visualLineEnd } from "../line/spans.js" | |
7 import { getLine, lineNo } from "../line/utils_line.js" | |
8 import { Range } from "../model/selection.js" | |
9 import { selectAll } from "../model/selection_updates.js" | |
10 import { countColumn, sel_dontScroll, sel_move, spaceStr } from "../util/misc.js" | |
11 import { getOrder } from "../util/bidi.js" | |
12 | |
13 // Commands are parameter-less actions that can be performed on an | |
14 // editor, mostly used for keybindings. | |
15 export let commands = { | |
16 selectAll: selectAll, | |
17 singleSelection: cm => cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll), | |
18 killLine: cm => deleteNearSelection(cm, range => { | |
19 if (range.empty()) { | |
20 let len = getLine(cm.doc, range.head.line).text.length | |
21 if (range.head.ch == len && range.head.line < cm.lastLine()) | |
22 return {from: range.head, to: Pos(range.head.line + 1, 0)} | |
23 else | |
24 return {from: range.head, to: Pos(range.head.line, len)} | |
25 } else { | |
26 return {from: range.from(), to: range.to()} | |
27 } | |
28 }), | |
29 deleteLine: cm => deleteNearSelection(cm, range => ({ | |
30 from: Pos(range.from().line, 0), | |
31 to: clipPos(cm.doc, Pos(range.to().line + 1, 0)) | |
32 })), | |
33 delLineLeft: cm => deleteNearSelection(cm, range => ({ | |
34 from: Pos(range.from().line, 0), to: range.from() | |
35 })), | |
36 delWrappedLineLeft: cm => deleteNearSelection(cm, range => { | |
37 let top = cm.charCoords(range.head, "div").top + 5 | |
38 let leftPos = cm.coordsChar({left: 0, top: top}, "div") | |
39 return {from: leftPos, to: range.from()} | |
40 }), | |
41 delWrappedLineRight: cm => deleteNearSelection(cm, range => { | |
42 let top = cm.charCoords(range.head, "div").top + 5 | |
43 let rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div") | |
44 return {from: range.from(), to: rightPos } | |
45 }), | |
46 undo: cm => cm.undo(), | |
47 redo: cm => cm.redo(), | |
48 undoSelection: cm => cm.undoSelection(), | |
49 redoSelection: cm => cm.redoSelection(), | |
50 goDocStart: cm => cm.extendSelection(Pos(cm.firstLine(), 0)), | |
51 goDocEnd: cm => cm.extendSelection(Pos(cm.lastLine())), | |
52 goLineStart: cm => cm.extendSelectionsBy(range => lineStart(cm, range.head.line), | |
53 {origin: "+move", bias: 1} | |
54 ), | |
55 goLineStartSmart: cm => cm.extendSelectionsBy(range => lineStartSmart(cm, range.head), | |
56 {origin: "+move", bias: 1} | |
57 ), | |
58 goLineEnd: cm => cm.extendSelectionsBy(range => lineEnd(cm, range.head.line), | |
59 {origin: "+move", bias: -1} | |
60 ), | |
61 goLineRight: cm => cm.extendSelectionsBy(range => { | |
62 let top = cm.cursorCoords(range.head, "div").top + 5 | |
63 return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div") | |
64 }, sel_move), | |
65 goLineLeft: cm => cm.extendSelectionsBy(range => { | |
66 let top = cm.cursorCoords(range.head, "div").top + 5 | |
67 return cm.coordsChar({left: 0, top: top}, "div") | |
68 }, sel_move), | |
69 goLineLeftSmart: cm => cm.extendSelectionsBy(range => { | |
70 let top = cm.cursorCoords(range.head, "div").top + 5 | |
71 let pos = cm.coordsChar({left: 0, top: top}, "div") | |
72 if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm, range.head) | |
73 return pos | |
74 }, sel_move), | |
75 goLineUp: cm => cm.moveV(-1, "line"), | |
76 goLineDown: cm => cm.moveV(1, "line"), | |
77 goPageUp: cm => cm.moveV(-1, "page"), | |
78 goPageDown: cm => cm.moveV(1, "page"), | |
79 goCharLeft: cm => cm.moveH(-1, "char"), | |
80 goCharRight: cm => cm.moveH(1, "char"), | |
81 goColumnLeft: cm => cm.moveH(-1, "column"), | |
82 goColumnRight: cm => cm.moveH(1, "column"), | |
83 goWordLeft: cm => cm.moveH(-1, "word"), | |
84 goGroupRight: cm => cm.moveH(1, "group"), | |
85 goGroupLeft: cm => cm.moveH(-1, "group"), | |
86 goWordRight: cm => cm.moveH(1, "word"), | |
87 delCharBefore: cm => cm.deleteH(-1, "codepoint"), | |
88 delCharAfter: cm => cm.deleteH(1, "char"), | |
89 delWordBefore: cm => cm.deleteH(-1, "word"), | |
90 delWordAfter: cm => cm.deleteH(1, "word"), | |
91 delGroupBefore: cm => cm.deleteH(-1, "group"), | |
92 delGroupAfter: cm => cm.deleteH(1, "group"), | |
93 indentAuto: cm => cm.indentSelection("smart"), | |
94 indentMore: cm => cm.indentSelection("add"), | |
95 indentLess: cm => cm.indentSelection("subtract"), | |
96 insertTab: cm => cm.replaceSelection("\t"), | |
97 insertSoftTab: cm => { | |
98 let spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize | |
99 for (let i = 0; i < ranges.length; i++) { | |
100 let pos = ranges[i].from() | |
101 let col = countColumn(cm.getLine(pos.line), pos.ch, tabSize) | |
102 spaces.push(spaceStr(tabSize - col % tabSize)) | |
103 } | |
104 cm.replaceSelections(spaces) | |
105 }, | |
106 defaultTab: cm => { | |
107 if (cm.somethingSelected()) cm.indentSelection("add") | |
108 else cm.execCommand("insertTab") | |
109 }, | |
110 // Swap the two chars left and right of each selection's head. | |
111 // Move cursor behind the two swapped characters afterwards. | |
112 // | |
113 // Doesn't consider line feeds a character. | |
114 // Doesn't scan more than one line above to find a character. | |
115 // Doesn't do anything on an empty line. | |
116 // Doesn't do anything with non-empty selections. | |
117 transposeChars: cm => runInOp(cm, () => { | |
118 let ranges = cm.listSelections(), newSel = [] | |
119 for (let i = 0; i < ranges.length; i++) { | |
120 if (!ranges[i].empty()) continue | |
121 let cur = ranges[i].head, line = getLine(cm.doc, cur.line).text | |
122 if (line) { | |
123 if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1) | |
124 if (cur.ch > 0) { | |
125 cur = new Pos(cur.line, cur.ch + 1) | |
126 cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2), | |
127 Pos(cur.line, cur.ch - 2), cur, "+transpose") | |
128 } else if (cur.line > cm.doc.first) { | |
129 let prev = getLine(cm.doc, cur.line - 1).text | |
130 if (prev) { | |
131 cur = new Pos(cur.line, 1) | |
132 cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() + | |
133 prev.charAt(prev.length - 1), | |
134 Pos(cur.line - 1, prev.length - 1), cur, "+transpose") | |
135 } | |
136 } | |
137 } | |
138 newSel.push(new Range(cur, cur)) | |
139 } | |
140 cm.setSelections(newSel) | |
141 }), | |
142 newlineAndIndent: cm => runInOp(cm, () => { | |
143 let sels = cm.listSelections() | |
144 for (let i = sels.length - 1; i >= 0; i--) | |
145 cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input") | |
146 sels = cm.listSelections() | |
147 for (let i = 0; i < sels.length; i++) | |
148 cm.indentLine(sels[i].from().line, null, true) | |
149 ensureCursorVisible(cm) | |
150 }), | |
151 openLine: cm => cm.replaceSelection("\n", "start"), | |
152 toggleOverwrite: cm => cm.toggleOverwrite() | |
153 } | |
154 | |
155 | |
156 function lineStart(cm, lineN) { | |
157 let line = getLine(cm.doc, lineN) | |
158 let visual = visualLine(line) | |
159 if (visual != line) lineN = lineNo(visual) | |
160 return endOfLine(true, cm, visual, lineN, 1) | |
161 } | |
162 function lineEnd(cm, lineN) { | |
163 let line = getLine(cm.doc, lineN) | |
164 let visual = visualLineEnd(line) | |
165 if (visual != line) lineN = lineNo(visual) | |
166 return endOfLine(true, cm, line, lineN, -1) | |
167 } | |
168 function lineStartSmart(cm, pos) { | |
169 let start = lineStart(cm, pos.line) | |
170 let line = getLine(cm.doc, start.line) | |
171 let order = getOrder(line, cm.doc.direction) | |
172 if (!order || order[0].level == 0) { | |
173 let firstNonWS = Math.max(start.ch, line.text.search(/\S/)) | |
174 let inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch | |
175 return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky) | |
176 } | |
177 return start | |
178 } |