@taufik-nurrohman/text-editor.source 2.2.10 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +6 -4
- package/index.js +274 -308
- package/index.min.js +2 -2
- package/index.mjs +168 -234
- package/package.json +6 -3
package/LICENSE
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
The MIT License (MIT)
|
2
2
|
|
3
|
-
Copyright ©
|
3
|
+
Copyright © 2024 Taufik Nurrohman <https://github.com/taufik-nurrohman>
|
4
4
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
6
|
of this software and associated documentation files (the “Software”), to deal
|
package/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
Text Editor Source
|
2
|
-
|
1
|
+
[Text Editor](https://github.com/taufik-nurrohman/text-editor) » Source
|
2
|
+
=============================================================================
|
3
3
|
|
4
|
-
|
4
|
+
Source extension for [Text Editor](https://github.com/taufik-nurrohman/text-editor). This extension provides a set of
|
5
|
+
key strokes to generate responses like in a typical source code editor.
|
5
6
|
|
6
|
-
|
7
|
+

|
8
|
+

|
package/index.js
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
*
|
3
3
|
* The MIT License (MIT)
|
4
4
|
*
|
5
|
-
* Copyright ©
|
5
|
+
* Copyright © 2024 Taufik Nurrohman <https://github.com/taufik-nurrohman>
|
6
6
|
*
|
7
7
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
8
|
* of this software and associated documentation files (the “Software”), to deal
|
@@ -24,26 +24,46 @@
|
|
24
24
|
*
|
25
25
|
*/
|
26
26
|
(function (g, f) {
|
27
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? f(
|
28
|
-
})(this, (function (
|
27
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = f() : typeof define === 'function' && define.amd ? define(f) : (g = typeof globalThis !== 'undefined' ? globalThis : g || self, (g.TextEditor = g.TextEditor || {}, g.TextEditor.Source = f()));
|
28
|
+
})(this, (function () {
|
29
29
|
'use strict';
|
30
|
+
var debounce = function debounce(then, time) {
|
31
|
+
var timer;
|
32
|
+
return function () {
|
33
|
+
var _arguments = arguments,
|
34
|
+
_this = this;
|
35
|
+
timer && clearTimeout(timer);
|
36
|
+
timer = setTimeout(function () {
|
37
|
+
return then.apply(_this, _arguments);
|
38
|
+
}, time);
|
39
|
+
};
|
40
|
+
};
|
30
41
|
var hasValue = function hasValue(x, data) {
|
31
42
|
return -1 !== data.indexOf(x);
|
32
43
|
};
|
33
44
|
var isArray = function isArray(x) {
|
34
45
|
return Array.isArray(x);
|
35
46
|
};
|
36
|
-
var isDefined = function isDefined(x) {
|
47
|
+
var isDefined$1 = function isDefined(x) {
|
37
48
|
return 'undefined' !== typeof x;
|
38
49
|
};
|
39
|
-
var isInstance = function isInstance(x, of) {
|
40
|
-
return x && isSet(of) && x instanceof of ;
|
50
|
+
var isInstance$1 = function isInstance(x, of) {
|
51
|
+
return x && isSet$1(of) && x instanceof of ;
|
41
52
|
};
|
42
|
-
var isNull = function isNull(x) {
|
53
|
+
var isNull$1 = function isNull(x) {
|
43
54
|
return null === x;
|
44
55
|
};
|
45
|
-
var
|
46
|
-
|
56
|
+
var isObject = function isObject(x, isPlain) {
|
57
|
+
if (isPlain === void 0) {
|
58
|
+
isPlain = true;
|
59
|
+
}
|
60
|
+
if ('object' !== typeof x) {
|
61
|
+
return false;
|
62
|
+
}
|
63
|
+
return isPlain ? isInstance$1(x, Object) : true;
|
64
|
+
};
|
65
|
+
var isSet$1 = function isSet(x) {
|
66
|
+
return isDefined$1(x) && !isNull$1(x);
|
47
67
|
};
|
48
68
|
var isString = function isString(x) {
|
49
69
|
return 'string' === typeof x;
|
@@ -54,17 +74,61 @@
|
|
54
74
|
var toObjectValues = function toObjectValues(x) {
|
55
75
|
return Object.values(x);
|
56
76
|
};
|
57
|
-
var
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
77
|
+
var fromStates = function fromStates() {
|
78
|
+
for (var _len = arguments.length, lot = new Array(_len), _key = 0; _key < _len; _key++) {
|
79
|
+
lot[_key] = arguments[_key];
|
80
|
+
}
|
81
|
+
var out = lot.shift();
|
82
|
+
for (var i = 0, j = toCount(lot); i < j; ++i) {
|
83
|
+
for (var k in lot[i]) {
|
84
|
+
// Assign value
|
85
|
+
if (!isSet$1(out[k])) {
|
86
|
+
out[k] = lot[i][k];
|
87
|
+
continue;
|
88
|
+
}
|
89
|
+
// Merge array
|
90
|
+
if (isArray(out[k]) && isArray(lot[i][k])) {
|
91
|
+
out[k] = [ /* Clone! */ ].concat(out[k]);
|
92
|
+
for (var ii = 0, jj = toCount(lot[i][k]); ii < jj; ++ii) {
|
93
|
+
if (!hasValue(lot[i][k][ii], out[k])) {
|
94
|
+
out[k].push(lot[i][k][ii]);
|
95
|
+
}
|
96
|
+
}
|
97
|
+
// Merge object recursive
|
98
|
+
} else if (isObject(out[k]) && isObject(lot[i][k])) {
|
99
|
+
out[k] = fromStates({
|
100
|
+
/* Clone! */ }, out[k], lot[i][k]);
|
101
|
+
// Replace value
|
102
|
+
} else {
|
103
|
+
out[k] = lot[i][k];
|
104
|
+
}
|
105
|
+
}
|
106
|
+
}
|
107
|
+
return out;
|
108
|
+
};
|
109
|
+
var offEvent = function offEvent(name, node, then) {
|
110
|
+
node.removeEventListener(name, then);
|
111
|
+
};
|
112
|
+
var offEventDefault = function offEventDefault(e) {
|
113
|
+
return e && e.preventDefault();
|
114
|
+
};
|
115
|
+
var onEvent = function onEvent(name, node, then, options) {
|
116
|
+
if (options === void 0) {
|
117
|
+
options = false;
|
118
|
+
}
|
119
|
+
node.addEventListener(name, then, options);
|
120
|
+
};
|
121
|
+
var isDefined = function isDefined(x) {
|
122
|
+
return 'undefined' !== typeof x;
|
123
|
+
};
|
124
|
+
var isInstance = function isInstance(x, of) {
|
125
|
+
return x && isSet(of) && x instanceof of ;
|
126
|
+
};
|
127
|
+
var isNull = function isNull(x) {
|
128
|
+
return null === x;
|
129
|
+
};
|
130
|
+
var isSet = function isSet(x) {
|
131
|
+
return isDefined(x) && !isNull(x);
|
68
132
|
};
|
69
133
|
var isPattern = function isPattern(pattern) {
|
70
134
|
return isInstance(pattern, RegExp);
|
@@ -73,375 +137,277 @@
|
|
73
137
|
if (isPattern(pattern)) {
|
74
138
|
return pattern;
|
75
139
|
}
|
76
|
-
// No need to escape `/` in the pattern string
|
77
|
-
pattern = pattern.replace(/\//g, '\\/');
|
78
140
|
return new RegExp(pattern, isSet(opt) ? opt : 'g');
|
79
141
|
};
|
80
|
-
var
|
81
|
-
'`': '`',
|
82
|
-
'(': ')',
|
83
|
-
'{': '}',
|
84
|
-
'[': ']',
|
85
|
-
'"': '"',
|
86
|
-
"'": "'",
|
87
|
-
'<': '>'
|
88
|
-
};
|
89
|
-
|
90
|
-
function promisify(type, lot) {
|
91
|
-
return new Promise(function (resolve, reject) {
|
92
|
-
var r = W[type].apply(W, lot);
|
93
|
-
return r ? resolve(r) : reject(r);
|
94
|
-
});
|
95
|
-
}
|
96
|
-
var defaults = {
|
97
|
-
source: {
|
98
|
-
pairs: pairs,
|
99
|
-
type: null
|
100
|
-
}
|
101
|
-
};
|
102
|
-
['alert', 'confirm', 'prompt'].forEach(function (type) {
|
103
|
-
defaults.source[type] = function () {
|
104
|
-
for (var _len = arguments.length, lot = new Array(_len), _key = 0; _key < _len; _key++) {
|
105
|
-
lot[_key] = arguments[_key];
|
106
|
-
}
|
107
|
-
return promisify(type, lot);
|
108
|
-
};
|
109
|
-
});
|
110
|
-
var that = {};
|
111
|
-
that.toggle = function (open, close, wrap, tidy) {
|
112
|
-
if (tidy === void 0) {
|
113
|
-
tidy = false;
|
114
|
-
}
|
115
|
-
if (!close && "" !== close) {
|
116
|
-
close = open;
|
117
|
-
}
|
118
|
-
var t = this,
|
119
|
-
_t$$ = t.$(),
|
120
|
-
after = _t$$.after,
|
121
|
-
before = _t$$.before,
|
122
|
-
value = _t$$.value,
|
123
|
-
closeCount = toCount(close),
|
124
|
-
openCount = toCount(open);
|
125
|
-
if (wrap && close === value.slice(-closeCount) && open === value.slice(0, openCount) || close === after.slice(0, closeCount) && open === before.slice(-openCount)) {
|
126
|
-
return t.peel(open, close, wrap);
|
127
|
-
}
|
128
|
-
if (false !== tidy) {
|
129
|
-
if (isString(tidy)) {
|
130
|
-
tidy = [tidy, tidy];
|
131
|
-
} else if (!isArray(tidy)) {
|
132
|
-
tidy = ["", ""];
|
133
|
-
}
|
134
|
-
if (!isSet(tidy[1])) {
|
135
|
-
tidy[1] = tidy[0];
|
136
|
-
}
|
137
|
-
t.trim(tidy[0], tidy[1]);
|
138
|
-
}
|
139
|
-
return t.wrap(open, close, wrap);
|
140
|
-
};
|
142
|
+
var ALT_PREFIX = 'Alt-';
|
141
143
|
var CTRL_PREFIX = 'Control-';
|
142
144
|
var SHIFT_PREFIX = 'Shift-';
|
145
|
+
var bounce = debounce(function ($) {
|
146
|
+
return $.record();
|
147
|
+
}, 10);
|
143
148
|
|
144
|
-
function
|
149
|
+
function onKeyDown(e) {
|
150
|
+
var _editor$state$source, _editor$state$source2;
|
151
|
+
var self = this,
|
152
|
+
editor = self.TextEditor,
|
153
|
+
keys = editor.k();
|
154
|
+
if (!editor || e.defaultPrevented) {
|
155
|
+
return;
|
156
|
+
}
|
157
|
+
bounce(editor);
|
158
|
+
if (editor.keys[keys]) {
|
159
|
+
return;
|
160
|
+
}
|
145
161
|
var charAfter,
|
146
162
|
charBefore,
|
147
|
-
charIndent =
|
148
|
-
charPairs =
|
149
|
-
charPairsValues = toObjectValues(charPairs)
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
163
|
+
charIndent = ((_editor$state$source = editor.state.source) == null ? void 0 : _editor$state$source.tab) || editor.state.tab || '\t',
|
164
|
+
charPairs = ((_editor$state$source2 = editor.state.source) == null ? void 0 : _editor$state$source2.pairs) || {},
|
165
|
+
charPairsValues = toObjectValues(charPairs);
|
166
|
+
var _editor$$ = editor.$(),
|
167
|
+
after = _editor$$.after,
|
168
|
+
before = _editor$$.before,
|
169
|
+
end = _editor$$.end,
|
170
|
+
start = _editor$$.start,
|
171
|
+
value = _editor$$.value,
|
172
|
+
lineAfter = after.split('\n').shift(),
|
173
|
+
lineBefore = before.split('\n').pop(),
|
174
|
+
lineMatch = lineBefore.match(/^(\s+)/),
|
175
|
+
lineMatchIndent = lineMatch && lineMatch[1] || "";
|
176
|
+
if (CTRL_PREFIX + SHIFT_PREFIX + 'Enter' === keys) {
|
177
|
+
if (before || after) {
|
178
|
+
// Insert line above with `⎈⇧↵`
|
179
|
+
offEventDefault(e);
|
180
|
+
return editor.select(start - toCount(lineBefore)).wrap(lineMatchIndent, '\n').insert(value).record(), false;
|
181
|
+
}
|
182
|
+
return;
|
156
183
|
}
|
157
|
-
if (
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
charAfter = charPairs[charBefore = _before.slice(-1)];
|
163
|
-
if (!_value && charAfter && charBefore && charAfter === _after[0]) {
|
164
|
-
of.wrap(' ', ' ');
|
165
|
-
return false;
|
184
|
+
if (CTRL_PREFIX + 'Enter' === keys) {
|
185
|
+
if (before || after) {
|
186
|
+
// Insert line below with `⎈↵`
|
187
|
+
offEventDefault(e);
|
188
|
+
return editor.select(end + toCount(lineAfter)).wrap('\n' + lineMatchIndent, "").insert(value).record(), false;
|
166
189
|
}
|
167
|
-
return true;
|
168
190
|
}
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
of.wrap('\n' + lineMatchIndent + (charBefore !== charAfter ? charIndent : ""), '\n' + lineMatchIndent).record();
|
180
|
-
return false;
|
181
|
-
}
|
182
|
-
if (lineMatchIndent) {
|
183
|
-
of.insert('\n' + lineMatchIndent, -1).record();
|
184
|
-
return false;
|
185
|
-
}
|
191
|
+
// Do nothing
|
192
|
+
if (ALT_PREFIX === keys + '-' || CTRL_PREFIX === keys + '-') {
|
193
|
+
offEventDefault(e);
|
194
|
+
return;
|
195
|
+
}
|
196
|
+
if (' ' === keys) {
|
197
|
+
charAfter = charPairs[charBefore = before.slice(-1)];
|
198
|
+
if (!value && charAfter && charBefore && charAfter === after[0]) {
|
199
|
+
offEventDefault(e);
|
200
|
+
return editor.wrap(' ', ' ');
|
186
201
|
}
|
187
|
-
return
|
202
|
+
return;
|
188
203
|
}
|
189
|
-
if ('Backspace' ===
|
190
|
-
|
191
|
-
_after3 = _of$$3.after,
|
192
|
-
_before3 = _of$$3.before,
|
193
|
-
_value3 = _of$$3.value;
|
194
|
-
_after3.split('\n')[0];
|
195
|
-
var _lineBefore = _before3.split('\n').pop(),
|
196
|
-
_lineMatch = _lineBefore.match(/^(\s+)/),
|
197
|
-
_lineMatchIndent = _lineMatch && _lineMatch[1] || "";
|
198
|
-
charAfter = charPairs[charBefore = _before3.slice(-1)];
|
204
|
+
if ('Backspace' === keys) {
|
205
|
+
charAfter = charPairs[charBefore = before.slice(-1)];
|
199
206
|
// Do nothing on escape
|
200
207
|
if ('\\' === charBefore) {
|
201
|
-
return
|
208
|
+
return;
|
202
209
|
}
|
203
|
-
if (
|
204
|
-
if (
|
205
|
-
|
206
|
-
return
|
210
|
+
if (value) {
|
211
|
+
if (after && before && charAfter && charAfter === after[0] && !before.endsWith('\\' + charBefore)) {
|
212
|
+
offEventDefault(e);
|
213
|
+
return editor.record().peel(charBefore, charAfter).record();
|
207
214
|
}
|
208
|
-
return
|
215
|
+
return;
|
209
216
|
}
|
210
|
-
charAfter = charPairs[charBefore =
|
217
|
+
charAfter = charPairs[charBefore = before.trim().slice(-1)];
|
211
218
|
if (charAfter && charBefore) {
|
212
|
-
if (
|
219
|
+
if (after.startsWith(' ' + charAfter) && before.endsWith(charBefore + ' ') || after.startsWith('\n' + lineMatchIndent + charAfter) && before.endsWith(charBefore + '\n' + lineMatchIndent)) {
|
213
220
|
// Collapse bracket(s)
|
214
|
-
|
215
|
-
return
|
221
|
+
offEventDefault(e);
|
222
|
+
return editor.trim("", "").record();
|
216
223
|
}
|
217
224
|
}
|
218
225
|
// Outdent
|
219
|
-
if (
|
220
|
-
|
221
|
-
return
|
226
|
+
if (lineBefore.endsWith(charIndent)) {
|
227
|
+
offEventDefault(e);
|
228
|
+
return editor.pull(charIndent).record();
|
222
229
|
}
|
223
|
-
if (
|
224
|
-
if (charAfter ===
|
230
|
+
if (after && before && !before.endsWith('\\' + charBefore)) {
|
231
|
+
if (charAfter === after[0] && charBefore === before.slice(-1)) {
|
225
232
|
// Peel pair
|
226
|
-
|
227
|
-
return
|
233
|
+
offEventDefault(e);
|
234
|
+
return editor.peel(charBefore, charAfter).record();
|
228
235
|
}
|
229
236
|
}
|
230
|
-
return
|
237
|
+
return;
|
238
|
+
}
|
239
|
+
if ('Enter' === keys || SHIFT_PREFIX + 'Enter' === keys) {
|
240
|
+
if (!value) {
|
241
|
+
if (after && before && (charAfter = charPairs[charBefore = before.slice(-1)]) && charAfter === after[0]) {
|
242
|
+
offEventDefault(e);
|
243
|
+
return editor.wrap('\n' + lineMatchIndent + (charBefore !== charAfter ? charIndent : ""), '\n' + lineMatchIndent).record();
|
244
|
+
}
|
245
|
+
if (lineMatchIndent) {
|
246
|
+
offEventDefault(e);
|
247
|
+
return editor.insert('\n' + lineMatchIndent, -1).record();
|
248
|
+
}
|
249
|
+
}
|
250
|
+
return;
|
231
251
|
}
|
232
|
-
var _of$$4 = of.$(),
|
233
|
-
after = _of$$4.after,
|
234
|
-
before = _of$$4.before,
|
235
|
-
start = _of$$4.start,
|
236
|
-
value = _of$$4.value;
|
237
252
|
// Do nothing on escape
|
238
253
|
if ('\\' === (charBefore = before.slice(-1))) {
|
239
|
-
return
|
254
|
+
return;
|
240
255
|
}
|
241
256
|
charAfter = hasValue(after[0], charPairsValues) ? after[0] : charPairs[charBefore];
|
257
|
+
keys = keys.replace(SHIFT_PREFIX, "");
|
242
258
|
// `|}`
|
243
|
-
if (!value && after && before && charAfter &&
|
259
|
+
if (!value && after && before && charAfter && keys === charAfter) {
|
244
260
|
// Move to the next character
|
245
261
|
// `}|`
|
246
|
-
|
247
|
-
return
|
262
|
+
offEventDefault(e);
|
263
|
+
return editor.select(start + 1).record();
|
248
264
|
}
|
249
265
|
for (charBefore in charPairs) {
|
250
266
|
charAfter = charPairs[charBefore];
|
251
267
|
// `{|`
|
252
|
-
if (
|
268
|
+
if (keys === charBefore && charAfter) {
|
253
269
|
// Wrap pair or selection
|
254
270
|
// `{|}` `{|aaa|}`
|
255
|
-
|
256
|
-
return
|
271
|
+
offEventDefault(e);
|
272
|
+
return editor.wrap(charBefore, charAfter).record();
|
257
273
|
}
|
258
274
|
// `|}`
|
259
|
-
if (
|
275
|
+
if (keys === charAfter) {
|
260
276
|
if (value) {
|
261
277
|
// Wrap selection
|
262
278
|
// `{|aaa|}`
|
263
|
-
|
264
|
-
return
|
279
|
+
offEventDefault(e);
|
280
|
+
return editor.record().wrap(charBefore, charAfter).record();
|
265
281
|
}
|
266
282
|
break;
|
267
283
|
}
|
268
284
|
}
|
269
|
-
|
270
|
-
}
|
271
|
-
|
272
|
-
function canKeyDownDent(map, of) {
|
273
|
-
var charIndent = of.state.source.tab || of.state.tab || '\t';
|
274
|
-
map.key;
|
275
|
-
map.queue;
|
276
|
-
var keyValue = map + "";
|
277
|
-
// Indent with `⎈]`
|
278
|
-
if (CTRL_PREFIX + ']' === keyValue) {
|
279
|
-
of.push(charIndent).record();
|
280
|
-
return false;
|
281
|
-
}
|
282
|
-
// Outdent with `⎈[`
|
283
|
-
if (CTRL_PREFIX + '[' === keyValue) {
|
284
|
-
of.pull(charIndent).record();
|
285
|
-
return false;
|
286
|
-
}
|
287
|
-
return true;
|
288
|
-
}
|
289
|
-
|
290
|
-
function canKeyDownEnter(map, of) {
|
291
|
-
map.key;
|
292
|
-
var queue = map.queue;
|
293
|
-
if (queue.Control && queue.Enter) {
|
294
|
-
var _of$$5 = of.$(),
|
295
|
-
after = _of$$5.after,
|
296
|
-
before = _of$$5.before,
|
297
|
-
end = _of$$5.end,
|
298
|
-
start = _of$$5.start,
|
299
|
-
value = _of$$5.value,
|
300
|
-
lineAfter = after.split('\n').shift(),
|
301
|
-
lineBefore = before.split('\n').pop(),
|
302
|
-
lineMatch = lineBefore.match(/^(\s+)/),
|
303
|
-
lineMatchIndent = lineMatch && lineMatch[1] || "";
|
304
|
-
if (before || after) {
|
305
|
-
if (queue.Shift) {
|
306
|
-
// Insert line over with `⎈⇧↵`
|
307
|
-
return of.select(start - toCount(lineBefore)).wrap(lineMatchIndent, '\n').insert(value).record(), false;
|
308
|
-
}
|
309
|
-
// Insert line below with `⎈↵`
|
310
|
-
return of.select(end + toCount(lineAfter)).wrap('\n' + lineMatchIndent, "").insert(value).record(), false;
|
311
|
-
}
|
312
|
-
}
|
313
|
-
return true;
|
314
|
-
}
|
315
|
-
|
316
|
-
function canKeyDownHistory(map, of) {
|
317
|
-
var keyValue = map + "";
|
318
|
-
// Redo with `⎈y`
|
319
|
-
if (CTRL_PREFIX + 'y' === keyValue) {
|
320
|
-
return of.redo(), false;
|
321
|
-
}
|
322
|
-
// Undo with `⎈z`
|
323
|
-
if (CTRL_PREFIX + 'z' === keyValue) {
|
324
|
-
return of.undo(), false;
|
325
|
-
}
|
326
|
-
return true;
|
327
|
-
}
|
328
|
-
|
329
|
-
function canKeyDownMove(map, of) {
|
330
|
-
map.key;
|
331
|
-
var queue = map.queue,
|
332
|
-
keyValue = map + "";
|
333
|
-
if (!queue.Control) {
|
334
|
-
return true;
|
335
|
-
}
|
336
|
-
var _of$$6 = of.$(),
|
337
|
-
after = _of$$6.after,
|
338
|
-
before = _of$$6.before,
|
339
|
-
end = _of$$6.end,
|
340
|
-
start = _of$$6.start,
|
341
|
-
value = _of$$6.value,
|
342
|
-
charPair,
|
285
|
+
var charPair,
|
343
286
|
charPairValue,
|
344
|
-
|
345
|
-
|
346
|
-
m;
|
287
|
+
m,
|
288
|
+
tokens = [];
|
347
289
|
if (value) {
|
348
290
|
for (charPair in charPairs) {
|
349
291
|
if (!(charPairValue = charPairs[charPair])) {
|
350
292
|
continue;
|
351
293
|
}
|
352
|
-
|
294
|
+
tokens.push('(?:\\' + charPair + '(?:\\\\.|[^\\' + charPair + (charPairValue !== charPair ? '\\' + charPairValue : "") + '])*\\' + charPairValue + ')');
|
353
295
|
}
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
if (CTRL_PREFIX + 'ArrowLeft' ===
|
358
|
-
|
359
|
-
|
360
|
-
return
|
296
|
+
tokens.push('\\w+'); // Word(s)
|
297
|
+
tokens.push('\\s+'); // White-space(s)
|
298
|
+
tokens.push('[\\s\\S]'); // Last try!
|
299
|
+
if (CTRL_PREFIX + 'ArrowLeft' === keys) {
|
300
|
+
offEventDefault(e);
|
301
|
+
if (m = before.match(toPattern('(' + tokens.join('|') + ')$', ""))) {
|
302
|
+
return editor.insert("").select(start - toCount(m[0])).insert(value).record();
|
361
303
|
}
|
362
|
-
return
|
304
|
+
return editor.select();
|
363
305
|
}
|
364
|
-
if (CTRL_PREFIX + 'ArrowRight' ===
|
365
|
-
|
366
|
-
|
367
|
-
return
|
306
|
+
if (CTRL_PREFIX + 'ArrowRight' === keys) {
|
307
|
+
offEventDefault(e);
|
308
|
+
if (m = after.match(toPattern('^(' + tokens.join('|') + ')', ""))) {
|
309
|
+
return editor.insert("").select(end + toCount(m[0]) - toCount(value)).insert(value).record();
|
368
310
|
}
|
369
|
-
return
|
311
|
+
return editor.select();
|
370
312
|
}
|
371
313
|
}
|
372
|
-
var lineAfter = after.split('\n').shift(),
|
373
|
-
lineBefore = before.split('\n').pop(),
|
374
|
-
lineMatch = lineBefore.match(/^(\s+)/);
|
375
|
-
lineMatch && lineMatch[1] || "";
|
376
314
|
// Force to select the current line if there is no selection
|
377
315
|
end += toCount(lineAfter);
|
378
316
|
start -= toCount(lineBefore);
|
379
317
|
value = lineBefore + value + lineAfter;
|
380
|
-
if (CTRL_PREFIX + 'ArrowUp' ===
|
318
|
+
if (CTRL_PREFIX + 'ArrowUp' === keys) {
|
319
|
+
offEventDefault(e);
|
381
320
|
if (!hasValue('\n', before)) {
|
382
|
-
return
|
321
|
+
return editor.select();
|
383
322
|
}
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
var $ =
|
323
|
+
editor.insert("");
|
324
|
+
editor.replace(/^([^\n]*?)(\n|$)/, '$2', 1);
|
325
|
+
editor.replace(/(^|\n)([^\n]*?)$/, "", -1);
|
326
|
+
var $ = editor.$();
|
388
327
|
before = $.before;
|
389
328
|
start = $.start;
|
390
329
|
lineBefore = before.split('\n').pop();
|
391
|
-
|
392
|
-
|
393
|
-
return
|
330
|
+
editor.select(start = start - toCount(lineBefore)).wrap(value, '\n');
|
331
|
+
editor.select(start, start + toCount(value));
|
332
|
+
return editor.record();
|
394
333
|
}
|
395
|
-
if (CTRL_PREFIX + 'ArrowDown' ===
|
334
|
+
if (CTRL_PREFIX + 'ArrowDown' === keys) {
|
335
|
+
offEventDefault(e);
|
396
336
|
if (!hasValue('\n', after)) {
|
397
|
-
return
|
337
|
+
return editor.select();
|
398
338
|
}
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
var _$ =
|
339
|
+
editor.insert("");
|
340
|
+
editor.replace(/^([^\n]*?)(\n|$)/, "", 1);
|
341
|
+
editor.replace(/(^|\n)([^\n]*?)$/, '$1', -1);
|
342
|
+
var _$ = editor.$();
|
403
343
|
after = _$.after;
|
404
344
|
end = _$.end;
|
405
345
|
lineAfter = after.split('\n').shift();
|
406
|
-
|
346
|
+
editor.select(end = end + toCount(lineAfter)).wrap('\n', value);
|
407
347
|
end += 1;
|
408
|
-
|
409
|
-
return
|
348
|
+
editor.select(end, end + toCount(value));
|
349
|
+
return editor.record();
|
410
350
|
}
|
411
|
-
return
|
351
|
+
return;
|
412
352
|
}
|
413
353
|
|
414
|
-
function
|
415
|
-
var
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
354
|
+
function attach(self) {
|
355
|
+
var $ = this;
|
356
|
+
$.state = fromStates({
|
357
|
+
source: {
|
358
|
+
pairs: {
|
359
|
+
'`': '`',
|
360
|
+
'(': ')',
|
361
|
+
'{': '}',
|
362
|
+
'[': ']',
|
363
|
+
'"': '"',
|
364
|
+
"'": "'",
|
365
|
+
'<': '>'
|
366
|
+
},
|
367
|
+
type: null
|
368
|
+
}
|
369
|
+
}, $.state);
|
370
|
+
$.toggle = function (open, close, wrap, tidy) {
|
371
|
+
if (tidy === void 0) {
|
372
|
+
tidy = false;
|
373
|
+
}
|
374
|
+
if (!close && "" !== close) {
|
375
|
+
close = open;
|
376
|
+
}
|
377
|
+
var _$$$ = $.$(),
|
378
|
+
after = _$$$.after,
|
379
|
+
before = _$$$.before,
|
380
|
+
value = _$$$.value,
|
381
|
+
closeCount = toCount(close),
|
382
|
+
openCount = toCount(open);
|
383
|
+
if (wrap && close === value.slice(-closeCount) && open === value.slice(0, openCount) || close === after.slice(0, closeCount) && open === before.slice(-openCount)) {
|
384
|
+
return $.peel(open, close, wrap);
|
385
|
+
}
|
386
|
+
if (false !== tidy) {
|
387
|
+
if (isString(tidy)) {
|
388
|
+
tidy = [tidy, tidy];
|
389
|
+
} else if (!isArray(tidy)) {
|
390
|
+
tidy = ["", ""];
|
391
|
+
}
|
392
|
+
if (!isSet$1(tidy[1])) {
|
393
|
+
tidy[1] = tidy[0];
|
394
|
+
}
|
395
|
+
$.trim(tidy[0], tidy[1]);
|
396
|
+
}
|
397
|
+
return $.wrap(open, close, wrap);
|
398
|
+
};
|
399
|
+
onEvent('keydown', self, onKeyDown);
|
400
|
+
return $.record();
|
426
401
|
}
|
427
|
-
var bounce = debounce(function (of) {
|
428
|
-
return of.record();
|
429
|
-
}, 100);
|
430
402
|
|
431
|
-
function
|
432
|
-
|
403
|
+
function detach(self) {
|
404
|
+
var $ = this;
|
405
|
+
offEvent('keydown', self, onKeyDown);
|
406
|
+
return $;
|
433
407
|
}
|
434
|
-
var
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
exports.canKeyDownMove = canKeyDownMove;
|
440
|
-
exports.canKeyDownTab = canKeyDownTab;
|
441
|
-
exports.canKeyUp = canKeyUp;
|
442
|
-
exports.state = state;
|
443
|
-
exports.that = that;
|
444
|
-
Object.defineProperty(exports, '__esModule', {
|
445
|
-
value: true
|
446
|
-
});
|
408
|
+
var index_js = {
|
409
|
+
attach: attach,
|
410
|
+
detach: detach
|
411
|
+
};
|
412
|
+
return index_js;
|
447
413
|
}));
|
package/index.min.js
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
*
|
3
3
|
* The MIT License (MIT)
|
4
4
|
*
|
5
|
-
* Copyright ©
|
5
|
+
* Copyright © 2024 Taufik Nurrohman <https://github.com/taufik-nurrohman>
|
6
6
|
*
|
7
7
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
8
|
* of this software and associated documentation files (the “Software”), to deal
|
@@ -23,4 +23,4 @@
|
|
23
23
|
* SOFTWARE.
|
24
24
|
*
|
25
25
|
*/
|
26
|
-
!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(
|
26
|
+
!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?module.exports=r():"function"==typeof define&&define.amd?define(r):((e="undefined"!=typeof globalThis?globalThis:e||self).TextEditor=e.TextEditor||{},e.TextEditor.Source=r())}(this,(function(){"use strict";var e,r,t,n=function(e,r){return-1!==r.indexOf(e)},i=function(e){return Array.isArray(e)},o=function(e,r){return void 0===r&&(r=!0),"object"==typeof e&&(!r||function(e,r){return e&&c(r)&&e instanceof r}(e,Object))},c=function(e){return function(e){return void 0!==e}(e)&&!function(e){return null===e}(e)},s=function(e){return e.length},u=function e(){for(var r=arguments.length,t=Array(r),u=0;u<r;u++)t[u]=arguments[u];for(var f=t.shift(),a=0,l=s(t);a<l;++a)for(var d in t[a])if(c(f[d]))if(i(f[d])&&i(t[a][d])){f[d]=[].concat(f[d]);for(var p=0,v=s(t[a][d]);p<v;++p)n(t[a][d][p],f[d])||f[d].push(t[a][d][p])}else o(f[d])&&o(t[a][d])?f[d]=e({},f[d],t[a][d]):f[d]=t[a][d];else f[d]=t[a][d];return f},f=function(e){return e&&e.preventDefault()},a=function(e){return function(e){return void 0!==e}(e)&&!function(e){return null===e}(e)},l=function(e){return t=RegExp,(r=e)&&a(t)&&r instanceof t;var r,t},d=function(e,r){return l(e)?e:RegExp(e,a(r)?r:"g")},p="Control-",v="Shift-",h=(e=function(e){return e.record()},r=10,function(){var n=arguments,i=this;t&&clearTimeout(t),t=setTimeout((function(){return e.apply(i,n)}),r)});function w(e){var r,t,i=this.TextEditor,o=i.k();if(i&&!e.defaultPrevented&&(h(i),!i.keys[o])){var c,u,a=(null==(r=i.state.source)?void 0:r.tab)||i.state.tab||"\t",l=(null==(t=i.state.source)?void 0:t.pairs)||{},w=Object.values(l),y=i.$(),b=y.after,m=y.before,E=y.end,g=y.start,$=y.value,x=b.split("\n").shift(),A=m.split("\n").pop(),T=A.match(/^(\s+)/),W=T&&T[1]||"";if(p+v+"Enter"===o)return m||b?(f(e),i.select(g-s(A)).wrap(W,"\n").insert($).record(),!1):void 0;if(p+"Enter"===o&&(m||b))return f(e),i.select(E+s(x)).wrap("\n"+W,"").insert($).record(),!1;if("Alt-"!=o+"-"&&p!==o+"-"){if(" "===o)return c=l[u=m.slice(-1)],!$&&c&&u&&c===b[0]?(f(e),i.wrap(" "," ")):void 0;if("Backspace"===o){if(c=l[u=m.slice(-1)],"\\"===u)return;return $?b&&m&&c&&c===b[0]&&!m.endsWith("\\"+u)?(f(e),i.record().peel(u,c).record()):void 0:(c=l[u=m.trim().slice(-1)])&&u&&(b.startsWith(" "+c)&&m.endsWith(u+" ")||b.startsWith("\n"+W+c)&&m.endsWith(u+"\n"+W))?(f(e),i.trim("","").record()):A.endsWith(a)?(f(e),i.pull(a).record()):b&&m&&!m.endsWith("\\"+u)&&c===b[0]&&u===m.slice(-1)?(f(e),i.peel(u,c).record()):void 0}if("Enter"!==o&&v+"Enter"!==o){if("\\"!==(u=m.slice(-1))){if(c=n(b[0],w)?b[0]:l[u],o=o.replace(v,""),!$&&b&&m&&c&&o===c)return f(e),i.select(g+1).record();for(u in l){if(c=l[u],o===u&&c)return f(e),i.wrap(u,c).record();if(o===c){if($)return f(e),i.record().wrap(u,c).record();break}}var j,k,L,O=[];if($){for(j in l)(k=l[j])&&O.push("(?:\\"+j+"(?:\\\\.|[^\\"+j+(k!==j?"\\"+k:"")+"])*\\"+k+")");if(O.push("\\w+"),O.push("\\s+"),O.push("[\\s\\S]"),p+"ArrowLeft"===o)return f(e),(L=m.match(d("("+O.join("|")+")$","")))?i.insert("").select(g-s(L[0])).insert($).record():i.select();if(p+"ArrowRight"===o)return f(e),(L=b.match(d("^("+O.join("|")+")","")))?i.insert("").select(E+s(L[0])-s($)).insert($).record():i.select()}if(E+=s(x),g-=s(A),$=A+$+x,p+"ArrowUp"===o){if(f(e),!n("\n",m))return i.select();i.insert(""),i.replace(/^([^\n]*?)(\n|$)/,"$2",1),i.replace(/(^|\n)([^\n]*?)$/,"",-1);var R=i.$();return m=R.before,g=R.start,A=m.split("\n").pop(),i.select(g-=s(A)).wrap($,"\n"),i.select(g,g+s($)),i.record()}if(p+"ArrowDown"===o){if(f(e),!n("\n",b))return i.select();i.insert(""),i.replace(/^([^\n]*?)(\n|$)/,"",1),i.replace(/(^|\n)([^\n]*?)$/,"$1",-1);var S=i.$();return b=S.after,E=S.end,x=b.split("\n").shift(),i.select(E+=s(x)).wrap("\n",$),E+=1,i.select(E,E+s($)),i.record()}}}else if(!$){if(b&&m&&(c=l[u=m.slice(-1)])&&c===b[0])return f(e),i.wrap("\n"+W+(u!==c?a:""),"\n"+W).record();if(W)return f(e),i.insert("\n"+W,-1).record()}}else f(e)}}var y={attach:function(e){var r=this;return r.state=u({source:{pairs:{"`":"`","(":")","{":"}","[":"]",'"':'"',"'":"'","<":">"},type:null}},r.state),r.toggle=function(e,t,n,o){void 0===o&&(o=!1),t||""===t||(t=e);var u=r.$(),f=u.after,a=u.before,l=u.value,d=s(t),p=s(e);return n&&t===l.slice(-d)&&e===l.slice(0,p)||t===f.slice(0,d)&&e===a.slice(-p)?r.peel(e,t,n):(!1!==o&&("string"==typeof o?o=[o,o]:i(o)||(o=["",""]),c(o[1])||(o[1]=o[0]),r.trim(o[0],o[1])),r.wrap(e,t,n))},function(e,r,t,n){void 0===n&&(n=!1),r.addEventListener(e,t,n)}("keydown",e,w),r.record()},detach:function(e){return function(e,r,t){r.removeEventListener(e,t)}("keydown",e,w),this}};return y}));
|
package/index.mjs
CHANGED
@@ -1,127 +1,78 @@
|
|
1
|
-
import {W} from '@taufik-nurrohman/document';
|
2
1
|
import {debounce} from '@taufik-nurrohman/tick';
|
3
|
-
import {
|
2
|
+
import {fromStates} from '@taufik-nurrohman/from';
|
4
3
|
import {hasValue} from '@taufik-nurrohman/has';
|
5
4
|
import {isArray, isSet, isString} from '@taufik-nurrohman/is';
|
5
|
+
import {onEvent, offEvent, offEventDefault} from '@taufik-nurrohman/event';
|
6
6
|
import {toCount, toObjectValues} from '@taufik-nurrohman/to';
|
7
|
+
import {toPattern} from '@taufik-nurrohman/pattern';
|
7
8
|
|
8
|
-
const
|
9
|
-
|
10
|
-
|
11
|
-
'{': '}',
|
12
|
-
'[': ']',
|
13
|
-
'"': '"',
|
14
|
-
"'": "'",
|
15
|
-
'<': '>'
|
16
|
-
};
|
17
|
-
|
18
|
-
function promisify(type, lot) {
|
19
|
-
return new Promise((resolve, reject) => {
|
20
|
-
let r = W[type].apply(W, lot);
|
21
|
-
return r ? resolve(r) : reject(r);
|
22
|
-
});
|
23
|
-
}
|
24
|
-
|
25
|
-
const defaults = {
|
26
|
-
source: {
|
27
|
-
pairs,
|
28
|
-
type: null
|
29
|
-
}
|
30
|
-
};
|
31
|
-
|
32
|
-
['alert', 'confirm', 'prompt'].forEach(type => {
|
33
|
-
defaults.source[type] = (...lot) => promisify(type, lot);
|
34
|
-
});
|
9
|
+
const ALT_PREFIX = 'Alt-';
|
10
|
+
const CTRL_PREFIX = 'Control-';
|
11
|
+
const SHIFT_PREFIX = 'Shift-';
|
35
12
|
|
36
|
-
|
13
|
+
const bounce = debounce($ => $.record(), 10);
|
37
14
|
|
38
|
-
|
39
|
-
|
40
|
-
|
15
|
+
function onKeyDown(e) {
|
16
|
+
let self = this,
|
17
|
+
editor = self.TextEditor,
|
18
|
+
keys = editor.k();
|
19
|
+
if (!editor || e.defaultPrevented) {
|
20
|
+
return;
|
41
21
|
}
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
openCount = toCount(open);
|
46
|
-
if (
|
47
|
-
(wrap && close === value.slice(-closeCount) && open === value.slice(0, openCount)) ||
|
48
|
-
(close === after.slice(0, closeCount) && open === before.slice(-openCount))
|
49
|
-
) {
|
50
|
-
return t.peel(open, close, wrap);
|
22
|
+
bounce(editor);
|
23
|
+
if (editor.keys[keys]) {
|
24
|
+
return;
|
51
25
|
}
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
26
|
+
let charAfter,
|
27
|
+
charBefore,
|
28
|
+
charIndent = editor.state.source?.tab || editor.state.tab || '\t',
|
29
|
+
charPairs = editor.state.source?.pairs || {},
|
30
|
+
charPairsValues = toObjectValues(charPairs);
|
31
|
+
let {after, before, end, start, value} = editor.$(),
|
32
|
+
lineAfter = after.split('\n').shift(),
|
33
|
+
lineBefore = before.split('\n').pop(),
|
34
|
+
lineMatch = lineBefore.match(/^(\s+)/),
|
35
|
+
lineMatchIndent = lineMatch && lineMatch[1] || "";
|
36
|
+
if (CTRL_PREFIX + SHIFT_PREFIX + 'Enter' === keys) {
|
37
|
+
if (before || after) {
|
38
|
+
// Insert line above with `⎈⇧↵`
|
39
|
+
offEventDefault(e);
|
40
|
+
return editor.select(start - toCount(lineBefore)).wrap(lineMatchIndent, '\n').insert(value).record(), false;
|
57
41
|
}
|
58
|
-
|
59
|
-
|
42
|
+
return;
|
43
|
+
}
|
44
|
+
if (CTRL_PREFIX + 'Enter' === keys) {
|
45
|
+
if (before || after) {
|
46
|
+
// Insert line below with `⎈↵`
|
47
|
+
offEventDefault(e);
|
48
|
+
return editor.select(end + toCount(lineAfter)).wrap('\n' + lineMatchIndent, "").insert(value).record(), false;
|
60
49
|
}
|
61
|
-
t.trim(tidy[0], tidy[1]);
|
62
50
|
}
|
63
|
-
return t.wrap(open, close, wrap);
|
64
|
-
};
|
65
|
-
|
66
|
-
const ALT_PREFIX = 'Alt-';
|
67
|
-
const CTRL_PREFIX = 'Control-';
|
68
|
-
const SHIFT_PREFIX = 'Shift-';
|
69
|
-
|
70
|
-
export function canKeyDown(map, of) {
|
71
|
-
let charAfter,
|
72
|
-
charBefore,
|
73
|
-
charIndent = of.state.source.tab || of.state.tab || '\t',
|
74
|
-
charPairs = of.state.source.pairs || {},
|
75
|
-
charPairsValues = toObjectValues(charPairs),
|
76
|
-
{key, queue} = map,
|
77
|
-
keyValue = map + "";
|
78
51
|
// Do nothing
|
79
|
-
if (
|
80
|
-
|
52
|
+
if (ALT_PREFIX === keys + '-' || CTRL_PREFIX === keys + '-') {
|
53
|
+
offEventDefault(e);
|
54
|
+
return;
|
81
55
|
}
|
82
|
-
if (' ' ===
|
83
|
-
let {after, before, value} = of.$();
|
56
|
+
if (' ' === keys) {
|
84
57
|
charAfter = charPairs[charBefore = before.slice(-1)];
|
85
58
|
if (!value && charAfter && charBefore && charAfter === after[0]) {
|
86
|
-
|
87
|
-
return
|
59
|
+
offEventDefault(e);
|
60
|
+
return editor.wrap(' ', ' ');
|
88
61
|
}
|
89
|
-
return
|
62
|
+
return;
|
90
63
|
}
|
91
|
-
if ('
|
92
|
-
let {after, before, value} = of.$(),
|
93
|
-
lineBefore = before.split('\n').pop(),
|
94
|
-
lineMatch = lineBefore.match(/^(\s+)/),
|
95
|
-
lineMatchIndent = lineMatch && lineMatch[1] || "";
|
96
|
-
if (!value) {
|
97
|
-
if (after && before && (charAfter = charPairs[charBefore = before.slice(-1)]) && charAfter === after[0]) {
|
98
|
-
of.wrap('\n' + lineMatchIndent + (charBefore !== charAfter ? charIndent : ""), '\n' + lineMatchIndent).record();
|
99
|
-
return false;
|
100
|
-
}
|
101
|
-
if (lineMatchIndent) {
|
102
|
-
of.insert('\n' + lineMatchIndent, -1).record();
|
103
|
-
return false;
|
104
|
-
}
|
105
|
-
}
|
106
|
-
return true;
|
107
|
-
}
|
108
|
-
if ('Backspace' === keyValue) {
|
109
|
-
let {after, before, value} = of.$(),
|
110
|
-
lineAfter = after.split('\n')[0],
|
111
|
-
lineBefore = before.split('\n').pop(),
|
112
|
-
lineMatch = lineBefore.match(/^(\s+)/),
|
113
|
-
lineMatchIndent = lineMatch && lineMatch[1] || "";
|
64
|
+
if ('Backspace' === keys) {
|
114
65
|
charAfter = charPairs[charBefore = before.slice(-1)];
|
115
66
|
// Do nothing on escape
|
116
67
|
if ('\\' === charBefore) {
|
117
|
-
return
|
68
|
+
return;
|
118
69
|
}
|
119
70
|
if (value) {
|
120
71
|
if (after && before && charAfter && charAfter === after[0] && !before.endsWith('\\' + charBefore)) {
|
121
|
-
|
122
|
-
return
|
72
|
+
offEventDefault(e);
|
73
|
+
return editor.record().peel(charBefore, charAfter).record();
|
123
74
|
}
|
124
|
-
return
|
75
|
+
return;
|
125
76
|
}
|
126
77
|
charAfter = charPairs[charBefore = before.trim().slice(-1)];
|
127
78
|
if (charAfter && charBefore) {
|
@@ -130,205 +81,188 @@ export function canKeyDown(map, of) {
|
|
130
81
|
after.startsWith('\n' + lineMatchIndent + charAfter) && before.endsWith(charBefore + '\n' + lineMatchIndent)
|
131
82
|
) {
|
132
83
|
// Collapse bracket(s)
|
133
|
-
|
134
|
-
return
|
84
|
+
offEventDefault(e);
|
85
|
+
return editor.trim("", "").record();
|
135
86
|
}
|
136
87
|
}
|
137
88
|
// Outdent
|
138
89
|
if (lineBefore.endsWith(charIndent)) {
|
139
|
-
|
140
|
-
return
|
90
|
+
offEventDefault(e);
|
91
|
+
return editor.pull(charIndent).record();
|
141
92
|
}
|
142
93
|
if (after && before && !before.endsWith('\\' + charBefore)) {
|
143
94
|
if (charAfter === after[0] && charBefore === before.slice(-1)) {
|
144
95
|
// Peel pair
|
145
|
-
|
146
|
-
return
|
96
|
+
offEventDefault(e);
|
97
|
+
return editor.peel(charBefore, charAfter).record();
|
98
|
+
}
|
99
|
+
}
|
100
|
+
return;
|
101
|
+
}
|
102
|
+
if ('Enter' === keys || SHIFT_PREFIX + 'Enter' === keys) {
|
103
|
+
if (!value) {
|
104
|
+
if (after && before && (charAfter = charPairs[charBefore = before.slice(-1)]) && charAfter === after[0]) {
|
105
|
+
offEventDefault(e);
|
106
|
+
return editor.wrap('\n' + lineMatchIndent + (charBefore !== charAfter ? charIndent : ""), '\n' + lineMatchIndent).record();
|
107
|
+
}
|
108
|
+
if (lineMatchIndent) {
|
109
|
+
offEventDefault(e);
|
110
|
+
return editor.insert('\n' + lineMatchIndent, -1).record();
|
147
111
|
}
|
148
112
|
}
|
149
|
-
return
|
113
|
+
return;
|
150
114
|
}
|
151
|
-
let {after, before, start, value} = of.$();
|
152
115
|
// Do nothing on escape
|
153
116
|
if ('\\' === (charBefore = before.slice(-1))) {
|
154
|
-
return
|
117
|
+
return;
|
155
118
|
}
|
156
119
|
charAfter = hasValue(after[0], charPairsValues) ? after[0] : charPairs[charBefore];
|
120
|
+
keys = keys.replace(SHIFT_PREFIX, "");
|
157
121
|
// `|}`
|
158
|
-
if (!value && after && before && charAfter &&
|
122
|
+
if (!value && after && before && charAfter && keys === charAfter) {
|
159
123
|
// Move to the next character
|
160
124
|
// `}|`
|
161
|
-
|
162
|
-
return
|
125
|
+
offEventDefault(e);
|
126
|
+
return editor.select(start + 1).record();
|
163
127
|
}
|
164
128
|
for (charBefore in charPairs) {
|
165
129
|
charAfter = charPairs[charBefore];
|
166
130
|
// `{|`
|
167
|
-
if (
|
131
|
+
if (keys === charBefore && charAfter) {
|
168
132
|
// Wrap pair or selection
|
169
133
|
// `{|}` `{|aaa|}`
|
170
|
-
|
171
|
-
return
|
134
|
+
offEventDefault(e);
|
135
|
+
return editor.wrap(charBefore, charAfter).record();
|
172
136
|
}
|
173
137
|
// `|}`
|
174
|
-
if (
|
138
|
+
if (keys === charAfter) {
|
175
139
|
if (value) {
|
176
140
|
// Wrap selection
|
177
141
|
// `{|aaa|}`
|
178
|
-
|
179
|
-
return
|
142
|
+
offEventDefault(e);
|
143
|
+
return editor.record().wrap(charBefore, charAfter).record();
|
180
144
|
}
|
181
145
|
break;
|
182
146
|
}
|
183
147
|
}
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
export function canKeyDownDent(map, of) {
|
188
|
-
let charIndent = of.state.source.tab || of.state.tab || '\t',
|
189
|
-
{key, queue} = map,
|
190
|
-
keyValue = map + "";
|
191
|
-
// Indent with `⎈]`
|
192
|
-
if (CTRL_PREFIX + ']' === keyValue) {
|
193
|
-
of.push(charIndent).record();
|
194
|
-
return false;
|
195
|
-
}
|
196
|
-
// Outdent with `⎈[`
|
197
|
-
if (CTRL_PREFIX + '[' === keyValue) {
|
198
|
-
of.pull(charIndent).record();
|
199
|
-
return false;
|
200
|
-
}
|
201
|
-
return true;
|
202
|
-
}
|
203
|
-
|
204
|
-
export function canKeyDownEnter(map, of) {
|
205
|
-
let {key, queue} = map;
|
206
|
-
if (queue.Control && queue.Enter) {
|
207
|
-
let {after, before, end, start, value} = of.$(),
|
208
|
-
lineAfter = after.split('\n').shift(),
|
209
|
-
lineBefore = before.split('\n').pop(),
|
210
|
-
lineMatch = lineBefore.match(/^(\s+)/),
|
211
|
-
lineMatchIndent = lineMatch && lineMatch[1] || "";
|
212
|
-
if (before || after) {
|
213
|
-
if (queue.Shift) {
|
214
|
-
// Insert line over with `⎈⇧↵`
|
215
|
-
return of.select(start - toCount(lineBefore)).wrap(lineMatchIndent, '\n').insert(value).record(), false;
|
216
|
-
}
|
217
|
-
// Insert line below with `⎈↵`
|
218
|
-
return of.select(end + toCount(lineAfter)).wrap('\n' + lineMatchIndent, "").insert(value).record(), false;
|
219
|
-
}
|
220
|
-
}
|
221
|
-
return true;
|
222
|
-
}
|
223
|
-
|
224
|
-
export function canKeyDownHistory(map, of) {
|
225
|
-
let keyValue = map + "";
|
226
|
-
// Redo with `⎈y`
|
227
|
-
if (CTRL_PREFIX + 'y' === keyValue) {
|
228
|
-
return of.redo(), false;
|
229
|
-
}
|
230
|
-
// Undo with `⎈z`
|
231
|
-
if (CTRL_PREFIX + 'z' === keyValue) {
|
232
|
-
return of.undo(), false;
|
233
|
-
}
|
234
|
-
return true;
|
235
|
-
}
|
236
|
-
|
237
|
-
export function canKeyDownMove(map, of) {
|
238
|
-
let {key, queue} = map,
|
239
|
-
keyValue = map + "";
|
240
|
-
if (!queue.Control) {
|
241
|
-
return true;
|
242
|
-
}
|
243
|
-
let {after, before, end, start, value} = of.$(),
|
244
|
-
charPair, charPairValue,
|
245
|
-
charPairs = of.state.source.pairs || {},
|
246
|
-
boundaries = [], m;
|
148
|
+
let charPair,
|
149
|
+
charPairValue, m,
|
150
|
+
tokens = [];
|
247
151
|
if (value) {
|
248
152
|
for (charPair in charPairs) {
|
249
153
|
if (!(charPairValue = charPairs[charPair])) {
|
250
154
|
continue;
|
251
155
|
}
|
252
|
-
|
156
|
+
tokens.push('(?:\\' + charPair + '(?:\\\\.|[^\\' + charPair + (charPairValue !== charPair ? '\\' + charPairValue : "") + '])*\\' + charPairValue + ')');
|
253
157
|
}
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
if (CTRL_PREFIX + 'ArrowLeft' ===
|
258
|
-
|
259
|
-
|
260
|
-
return
|
158
|
+
tokens.push('\\w+'); // Word(s)
|
159
|
+
tokens.push('\\s+'); // White-space(s)
|
160
|
+
tokens.push('[\\s\\S]'); // Last try!
|
161
|
+
if (CTRL_PREFIX + 'ArrowLeft' === keys) {
|
162
|
+
offEventDefault(e);
|
163
|
+
if (m = before.match(toPattern('(' + tokens.join('|') + ')$', ""))) {
|
164
|
+
return editor.insert("").select(start - toCount(m[0])).insert(value).record();
|
261
165
|
}
|
262
|
-
return
|
166
|
+
return editor.select();
|
263
167
|
}
|
264
|
-
if (CTRL_PREFIX + 'ArrowRight' ===
|
265
|
-
|
266
|
-
|
267
|
-
return
|
168
|
+
if (CTRL_PREFIX + 'ArrowRight' === keys) {
|
169
|
+
offEventDefault(e);
|
170
|
+
if (m = after.match(toPattern('^(' + tokens.join('|') + ')', ""))) {
|
171
|
+
return editor.insert("").select(end + toCount(m[0]) - toCount(value)).insert(value).record();
|
268
172
|
}
|
269
|
-
return
|
173
|
+
return editor.select();
|
270
174
|
}
|
271
175
|
}
|
272
|
-
let lineAfter = after.split('\n').shift(),
|
273
|
-
lineBefore = before.split('\n').pop(),
|
274
|
-
lineMatch = lineBefore.match(/^(\s+)/),
|
275
|
-
lineMatchIndent = lineMatch && lineMatch[1] || "";
|
276
176
|
// Force to select the current line if there is no selection
|
277
177
|
end += toCount(lineAfter);
|
278
178
|
start -= toCount(lineBefore);
|
279
179
|
value = lineBefore + value + lineAfter;
|
280
|
-
if (CTRL_PREFIX + 'ArrowUp' ===
|
180
|
+
if (CTRL_PREFIX + 'ArrowUp' === keys) {
|
181
|
+
offEventDefault(e);
|
281
182
|
if (!hasValue('\n', before)) {
|
282
|
-
return
|
183
|
+
return editor.select();
|
283
184
|
}
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
let $ =
|
185
|
+
editor.insert("");
|
186
|
+
editor.replace(/^([^\n]*?)(\n|$)/, '$2', 1);
|
187
|
+
editor.replace(/(^|\n)([^\n]*?)$/, "", -1);
|
188
|
+
let $ = editor.$();
|
288
189
|
before = $.before;
|
289
190
|
start = $.start;
|
290
191
|
lineBefore = before.split('\n').pop();
|
291
|
-
|
292
|
-
|
293
|
-
return
|
192
|
+
editor.select(start = start - toCount(lineBefore)).wrap(value, '\n');
|
193
|
+
editor.select(start, start + toCount(value));
|
194
|
+
return editor.record();
|
294
195
|
}
|
295
|
-
if (CTRL_PREFIX + 'ArrowDown' ===
|
196
|
+
if (CTRL_PREFIX + 'ArrowDown' === keys) {
|
197
|
+
offEventDefault(e);
|
296
198
|
if (!hasValue('\n', after)) {
|
297
|
-
return
|
199
|
+
return editor.select();
|
298
200
|
}
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
let $ =
|
201
|
+
editor.insert("");
|
202
|
+
editor.replace(/^([^\n]*?)(\n|$)/, "", 1);
|
203
|
+
editor.replace(/(^|\n)([^\n]*?)$/, '$1', -1);
|
204
|
+
let $ = editor.$();
|
303
205
|
after = $.after;
|
304
206
|
end = $.end;
|
305
207
|
lineAfter = after.split('\n').shift();
|
306
|
-
|
208
|
+
editor.select(end = end + toCount(lineAfter)).wrap('\n', value);
|
307
209
|
end += 1;
|
308
|
-
|
309
|
-
return
|
210
|
+
editor.select(end, end + toCount(value));
|
211
|
+
return editor.record();
|
310
212
|
}
|
311
|
-
return
|
213
|
+
return;
|
312
214
|
}
|
313
215
|
|
314
|
-
|
315
|
-
let
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
216
|
+
function attach(self) {
|
217
|
+
let $ = this;
|
218
|
+
$.state = fromStates({
|
219
|
+
source: {
|
220
|
+
pairs: {
|
221
|
+
'`': '`',
|
222
|
+
'(': ')',
|
223
|
+
'{': '}',
|
224
|
+
'[': ']',
|
225
|
+
'"': '"',
|
226
|
+
"'": "'",
|
227
|
+
'<': '>'
|
228
|
+
},
|
229
|
+
type: null
|
230
|
+
}
|
231
|
+
}, $.state);
|
232
|
+
$.toggle = function (open, close, wrap, tidy = false) {
|
233
|
+
if (!close && "" !== close) {
|
234
|
+
close = open;
|
235
|
+
}
|
236
|
+
let {after, before, value} = $.$(),
|
237
|
+
closeCount = toCount(close),
|
238
|
+
openCount = toCount(open);
|
239
|
+
if (
|
240
|
+
(wrap && close === value.slice(-closeCount) && open === value.slice(0, openCount)) ||
|
241
|
+
(close === after.slice(0, closeCount) && open === before.slice(-openCount))
|
242
|
+
) {
|
243
|
+
return $.peel(open, close, wrap);
|
244
|
+
}
|
245
|
+
if (false !== tidy) {
|
246
|
+
if (isString(tidy)) {
|
247
|
+
tidy = [tidy, tidy];
|
248
|
+
} else if (!isArray(tidy)) {
|
249
|
+
tidy = ["", ""];
|
250
|
+
}
|
251
|
+
if (!isSet(tidy[1])) {
|
252
|
+
tidy[1] = tidy[0];
|
253
|
+
}
|
254
|
+
$.trim(tidy[0], tidy[1]);
|
255
|
+
}
|
256
|
+
return $.wrap(open, close, wrap);
|
257
|
+
};
|
258
|
+
onEvent('keydown', self, onKeyDown);
|
259
|
+
return $.record();
|
326
260
|
}
|
327
261
|
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
return
|
262
|
+
function detach(self) {
|
263
|
+
let $ = this;
|
264
|
+
offEvent('keydown', self, onKeyDown);
|
265
|
+
return $;
|
332
266
|
}
|
333
267
|
|
334
|
-
export
|
268
|
+
export default {attach, detach};
|
package/package.json
CHANGED
@@ -3,11 +3,14 @@
|
|
3
3
|
"browser": "index.min.js",
|
4
4
|
"bugs": "https://github.com/taufik-nurrohman/text-editor.source/issues",
|
5
5
|
"dependencies": {
|
6
|
+
"@taufik-nurrohman/event": "*",
|
7
|
+
"@taufik-nurrohman/from": "*",
|
6
8
|
"@taufik-nurrohman/has": "*",
|
7
|
-
"@taufik-nurrohman/
|
9
|
+
"@taufik-nurrohman/is": "*",
|
8
10
|
"@taufik-nurrohman/pattern": "*",
|
9
11
|
"@taufik-nurrohman/text-editor": "*",
|
10
12
|
"@taufik-nurrohman/text-editor.history": "*",
|
13
|
+
"@taufik-nurrohman/text-editor.key": "*",
|
11
14
|
"@taufik-nurrohman/tick": "*",
|
12
15
|
"@taufik-nurrohman/to": "*"
|
13
16
|
},
|
@@ -47,7 +50,7 @@
|
|
47
50
|
"url": "git+https://github.com/taufik-nurrohman/text-editor.source.git"
|
48
51
|
},
|
49
52
|
"scripts": {
|
50
|
-
"pack": "pack --clean=false --from=.
|
53
|
+
"pack": "pack --clean=false --from=.factory --js-format=umd --js-name=TextEditor.Source --js-top='%(js.license)' --mjs=true --to=."
|
51
54
|
},
|
52
|
-
"version": "
|
55
|
+
"version": "3.0.0"
|
53
56
|
}
|