Mercurial
comparison .cms/lib/codemirror/src/input/input.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 { runInOp } from "../display/operations.js" | |
2 import { ensureCursorVisible } from "../display/scrolling.js" | |
3 import { Pos } from "../line/pos.js" | |
4 import { getLine } from "../line/utils_line.js" | |
5 import { makeChange } from "../model/changes.js" | |
6 import { ios, webkit } from "../util/browser.js" | |
7 import { elt } from "../util/dom.js" | |
8 import { lst, map } from "../util/misc.js" | |
9 import { signalLater } from "../util/operation_group.js" | |
10 import { splitLinesAuto } from "../util/feature_detection.js" | |
11 | |
12 import { indentLine } from "./indent.js" | |
13 | |
14 // This will be set to a {lineWise: bool, text: [string]} object, so | |
15 // that, when pasting, we know what kind of selections the copied | |
16 // text was made out of. | |
17 export let lastCopied = null | |
18 | |
19 export function setLastCopied(newLastCopied) { | |
20 lastCopied = newLastCopied | |
21 } | |
22 | |
23 export function applyTextInput(cm, inserted, deleted, sel, origin) { | |
24 let doc = cm.doc | |
25 cm.display.shift = false | |
26 if (!sel) sel = doc.sel | |
27 | |
28 let recent = +new Date - 200 | |
29 let paste = origin == "paste" || cm.state.pasteIncoming > recent | |
30 let textLines = splitLinesAuto(inserted), multiPaste = null | |
31 // When pasting N lines into N selections, insert one line per selection | |
32 if (paste && sel.ranges.length > 1) { | |
33 if (lastCopied && lastCopied.text.join("\n") == inserted) { | |
34 if (sel.ranges.length % lastCopied.text.length == 0) { | |
35 multiPaste = [] | |
36 for (let i = 0; i < lastCopied.text.length; i++) | |
37 multiPaste.push(doc.splitLines(lastCopied.text[i])) | |
38 } | |
39 } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) { | |
40 multiPaste = map(textLines, l => [l]) | |
41 } | |
42 } | |
43 | |
44 let updateInput = cm.curOp.updateInput | |
45 // Normal behavior is to insert the new text into every selection | |
46 for (let i = sel.ranges.length - 1; i >= 0; i--) { | |
47 let range = sel.ranges[i] | |
48 let from = range.from(), to = range.to() | |
49 if (range.empty()) { | |
50 if (deleted && deleted > 0) // Handle deletion | |
51 from = Pos(from.line, from.ch - deleted) | |
52 else if (cm.state.overwrite && !paste) // Handle overwrite | |
53 to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)) | |
54 else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == textLines.join("\n")) | |
55 from = to = Pos(from.line, 0) | |
56 } | |
57 let changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines, | |
58 origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")} | |
59 makeChange(cm.doc, changeEvent) | |
60 signalLater(cm, "inputRead", cm, changeEvent) | |
61 } | |
62 if (inserted && !paste) | |
63 triggerElectric(cm, inserted) | |
64 | |
65 ensureCursorVisible(cm) | |
66 if (cm.curOp.updateInput < 2) cm.curOp.updateInput = updateInput | |
67 cm.curOp.typing = true | |
68 cm.state.pasteIncoming = cm.state.cutIncoming = -1 | |
69 } | |
70 | |
71 export function handlePaste(e, cm) { | |
72 let pasted = e.clipboardData && e.clipboardData.getData("Text") | |
73 if (pasted) { | |
74 e.preventDefault() | |
75 if (!cm.isReadOnly() && !cm.options.disableInput && cm.hasFocus()) | |
76 runInOp(cm, () => applyTextInput(cm, pasted, 0, null, "paste")) | |
77 return true | |
78 } | |
79 } | |
80 | |
81 export function triggerElectric(cm, inserted) { | |
82 // When an 'electric' character is inserted, immediately trigger a reindent | |
83 if (!cm.options.electricChars || !cm.options.smartIndent) return | |
84 let sel = cm.doc.sel | |
85 | |
86 for (let i = sel.ranges.length - 1; i >= 0; i--) { | |
87 let range = sel.ranges[i] | |
88 if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) continue | |
89 let mode = cm.getModeAt(range.head) | |
90 let indented = false | |
91 if (mode.electricChars) { | |
92 for (let j = 0; j < mode.electricChars.length; j++) | |
93 if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) { | |
94 indented = indentLine(cm, range.head.line, "smart") | |
95 break | |
96 } | |
97 } else if (mode.electricInput) { | |
98 if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch))) | |
99 indented = indentLine(cm, range.head.line, "smart") | |
100 } | |
101 if (indented) signalLater(cm, "electricInput", cm, range.head.line) | |
102 } | |
103 } | |
104 | |
105 export function copyableRanges(cm) { | |
106 let text = [], ranges = [] | |
107 for (let i = 0; i < cm.doc.sel.ranges.length; i++) { | |
108 let line = cm.doc.sel.ranges[i].head.line | |
109 let lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)} | |
110 ranges.push(lineRange) | |
111 text.push(cm.getRange(lineRange.anchor, lineRange.head)) | |
112 } | |
113 return {text: text, ranges: ranges} | |
114 } | |
115 | |
116 export function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) { | |
117 field.setAttribute("autocorrect", autocorrect ? "on" : "off") | |
118 field.setAttribute("autocapitalize", autocapitalize ? "on" : "off") | |
119 field.setAttribute("spellcheck", !!spellcheck) | |
120 } | |
121 | |
122 export function hiddenTextarea() { | |
123 let te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; min-height: 1em; outline: none") | |
124 let div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;") | |
125 // The textarea is kept positioned near the cursor to prevent the | |
126 // fact that it'll be scrolled into view on input from scrolling | |
127 // our fake cursor out of view. On webkit, when wrap=off, paste is | |
128 // very slow. So make the area wide instead. | |
129 if (webkit) te.style.width = "1000px" | |
130 else te.setAttribute("wrap", "off") | |
131 // If border: 0; -- iOS fails to open keyboard (issue #1287) | |
132 if (ios) te.style.border = "1px solid black" | |
133 return div | |
134 } |