@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright © 2023 Taufik Nurrohman <https://github.com/taufik-nurrohman>
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) &raquo; Source
2
+ =============================================================================
3
3
 
4
- > Source code editor extension for [text editor](https://github.com/taufik-nurrohman/text-editor).
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
- Provides a set of key strokes to generate responses like in a typical source code editor.
7
+ ![index.js](https://img.shields.io/github/size/taufik-nurrohman/text-editor.source/index.js?branch=main&color=%23f1e05a&label=index.js&labelColor=%231f2328&style=flat-square)
8
+ ![index.min.js](https://img.shields.io/github/size/taufik-nurrohman/text-editor.source/index.min.js?branch=main&color=%23f1e05a&label=index.min.js&labelColor=%231f2328&style=flat-square)
package/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  *
3
3
  * The MIT License (MIT)
4
4
  *
5
- * Copyright © 2023 Taufik Nurrohman <https://github.com/taufik-nurrohman>
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(exports) : typeof define === 'function' && define.amd ? define(['exports'], f) : (g = typeof globalThis !== 'undefined' ? globalThis : g || self, f((g.TE = g.TE || {}, g.TE.Source = {})));
28
- })(this, (function (exports) {
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 isSet = function isSet(x) {
46
- return isDefined(x) && !isNull(x);
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 W = window;
58
- var debounce = function debounce(then, time) {
59
- var timer;
60
- return function () {
61
- var _arguments = arguments,
62
- _this = this;
63
- timer && clearTimeout(timer);
64
- timer = setTimeout(function () {
65
- return then.apply(_this, _arguments);
66
- }, time);
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 pairs = {
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 canKeyDown(map, of) {
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 = of.state.source.tab || of.state.tab || '\t',
148
- charPairs = of.state.source.pairs || {},
149
- charPairsValues = toObjectValues(charPairs),
150
- key = map.key,
151
- queue = map.queue,
152
- keyValue = map + "";
153
- // Do nothing
154
- if (queue.Alt || queue.Control) {
155
- return true;
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 (' ' === keyValue) {
158
- var _of$$ = of.$(),
159
- _after = _of$$.after,
160
- _before = _of$$.before,
161
- _value = _of$$.value;
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
- if ('Enter' === keyValue) {
170
- var _of$$2 = of.$(),
171
- _after2 = _of$$2.after,
172
- _before2 = _of$$2.before,
173
- _value2 = _of$$2.value,
174
- lineBefore = _before2.split('\n').pop(),
175
- lineMatch = lineBefore.match(/^(\s+)/),
176
- lineMatchIndent = lineMatch && lineMatch[1] || "";
177
- if (!_value2) {
178
- if (_after2 && _before2 && (charAfter = charPairs[charBefore = _before2.slice(-1)]) && charAfter === _after2[0]) {
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 true;
202
+ return;
188
203
  }
189
- if ('Backspace' === keyValue) {
190
- var _of$$3 = of.$(),
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 true;
208
+ return;
202
209
  }
203
- if (_value3) {
204
- if (_after3 && _before3 && charAfter && charAfter === _after3[0] && !_before3.endsWith('\\' + charBefore)) {
205
- of.record().peel(charBefore, charAfter).record();
206
- return false;
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 true;
215
+ return;
209
216
  }
210
- charAfter = charPairs[charBefore = _before3.trim().slice(-1)];
217
+ charAfter = charPairs[charBefore = before.trim().slice(-1)];
211
218
  if (charAfter && charBefore) {
212
- if (_after3.startsWith(' ' + charAfter) && _before3.endsWith(charBefore + ' ') || _after3.startsWith('\n' + _lineMatchIndent + charAfter) && _before3.endsWith(charBefore + '\n' + _lineMatchIndent)) {
219
+ if (after.startsWith(' ' + charAfter) && before.endsWith(charBefore + ' ') || after.startsWith('\n' + lineMatchIndent + charAfter) && before.endsWith(charBefore + '\n' + lineMatchIndent)) {
213
220
  // Collapse bracket(s)
214
- of.trim("", "").record();
215
- return false;
221
+ offEventDefault(e);
222
+ return editor.trim("", "").record();
216
223
  }
217
224
  }
218
225
  // Outdent
219
- if (_lineBefore.endsWith(charIndent)) {
220
- of.pull(charIndent).record();
221
- return false;
226
+ if (lineBefore.endsWith(charIndent)) {
227
+ offEventDefault(e);
228
+ return editor.pull(charIndent).record();
222
229
  }
223
- if (_after3 && _before3 && !_before3.endsWith('\\' + charBefore)) {
224
- if (charAfter === _after3[0] && charBefore === _before3.slice(-1)) {
230
+ if (after && before && !before.endsWith('\\' + charBefore)) {
231
+ if (charAfter === after[0] && charBefore === before.slice(-1)) {
225
232
  // Peel pair
226
- of.peel(charBefore, charAfter).record();
227
- return false;
233
+ offEventDefault(e);
234
+ return editor.peel(charBefore, charAfter).record();
228
235
  }
229
236
  }
230
- return true;
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 true;
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 && key === charAfter) {
259
+ if (!value && after && before && charAfter && keys === charAfter) {
244
260
  // Move to the next character
245
261
  // `}|`
246
- of.select(start + 1).record();
247
- return false;
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 (key === charBefore && charAfter) {
268
+ if (keys === charBefore && charAfter) {
253
269
  // Wrap pair or selection
254
270
  // `{|}` `{|aaa|}`
255
- of.wrap(charBefore, charAfter).record();
256
- return false;
271
+ offEventDefault(e);
272
+ return editor.wrap(charBefore, charAfter).record();
257
273
  }
258
274
  // `|}`
259
- if (key === charAfter) {
275
+ if (keys === charAfter) {
260
276
  if (value) {
261
277
  // Wrap selection
262
278
  // `{|aaa|}`
263
- of.record().wrap(charBefore, charAfter).record();
264
- return false;
279
+ offEventDefault(e);
280
+ return editor.record().wrap(charBefore, charAfter).record();
265
281
  }
266
282
  break;
267
283
  }
268
284
  }
269
- return true;
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
- charPairs = of.state.source.pairs || {},
345
- boundaries = [],
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
- boundaries.push('(?:\\' + charPair + '(?:\\\\.|[^\\' + charPair + (charPairValue !== charPair ? '\\' + charPairValue : "") + '])*\\' + charPairValue + ')');
294
+ tokens.push('(?:\\' + charPair + '(?:\\\\.|[^\\' + charPair + (charPairValue !== charPair ? '\\' + charPairValue : "") + '])*\\' + charPairValue + ')');
353
295
  }
354
- boundaries.push('\\w+'); // Word(s)
355
- boundaries.push('\\s+'); // White-space(s)
356
- boundaries.push('[\\s\\S]'); // Last try!
357
- if (CTRL_PREFIX + 'ArrowLeft' === keyValue) {
358
- if (m = before.match(toPattern('(' + boundaries.join('|') + ')$', ""))) {
359
- of.insert("").select(start - toCount(m[0])).insert(value);
360
- return of.record(), false;
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 of.select(), false;
304
+ return editor.select();
363
305
  }
364
- if (CTRL_PREFIX + 'ArrowRight' === keyValue) {
365
- if (m = after.match(toPattern('^(' + boundaries.join('|') + ')', ""))) {
366
- of.insert("").select(end + toCount(m[0]) - toCount(value)).insert(value);
367
- return of.record(), false;
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 of.select(), false;
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' === keyValue) {
318
+ if (CTRL_PREFIX + 'ArrowUp' === keys) {
319
+ offEventDefault(e);
381
320
  if (!hasValue('\n', before)) {
382
- return of.select(), false;
321
+ return editor.select();
383
322
  }
384
- of.insert("");
385
- of.replace(/^([^\n]*?)(\n|$)/, '$2', 1);
386
- of.replace(/(^|\n)([^\n]*?)$/, "", -1);
387
- var $ = of.$();
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
- of.select(start = start - toCount(lineBefore)).wrap(value, '\n');
392
- of.select(start, start + toCount(value));
393
- return of.record(), false;
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' === keyValue) {
334
+ if (CTRL_PREFIX + 'ArrowDown' === keys) {
335
+ offEventDefault(e);
396
336
  if (!hasValue('\n', after)) {
397
- return of.select(), false;
337
+ return editor.select();
398
338
  }
399
- of.insert("");
400
- of.replace(/^([^\n]*?)(\n|$)/, "", 1);
401
- of.replace(/(^|\n)([^\n]*?)$/, '$1', -1);
402
- var _$ = of.$();
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
- of.select(end = end + toCount(lineAfter)).wrap('\n', value);
346
+ editor.select(end = end + toCount(lineAfter)).wrap('\n', value);
407
347
  end += 1;
408
- of.select(end, end + toCount(value));
409
- return of.record(), false;
348
+ editor.select(end, end + toCount(value));
349
+ return editor.record();
410
350
  }
411
- return true;
351
+ return;
412
352
  }
413
353
 
414
- function canKeyDownTab(map, of) {
415
- var charIndent = of.state.source.tab || of.state.tab || '\t',
416
- keyValue = map + "";
417
- // Indent with `⇥`
418
- if ('Tab' === keyValue) {
419
- return of.push(charIndent).record(), false;
420
- }
421
- // Outdent with `⇧+⇥`
422
- if (SHIFT_PREFIX + 'Tab' === keyValue) {
423
- return of.pull(charIndent).record(), false;
424
- }
425
- return true;
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 canKeyUp(map, of) {
432
- return bounce(of), true;
403
+ function detach(self) {
404
+ var $ = this;
405
+ offEvent('keydown', self, onKeyDown);
406
+ return $;
433
407
  }
434
- var state = defaults;
435
- exports.canKeyDown = canKeyDown;
436
- exports.canKeyDownDent = canKeyDownDent;
437
- exports.canKeyDownEnter = canKeyDownEnter;
438
- exports.canKeyDownHistory = canKeyDownHistory;
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 © 2023 Taufik Nurrohman <https://github.com/taufik-nurrohman>
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(exports):"function"==typeof define&&define.amd?define(["exports"],r):r(((e="undefined"!=typeof globalThis?globalThis:e||self).TE=e.TE||{},e.TE.Source={}))}(this,(function(e){"use strict";var r=function(e,r){return-1!==r.indexOf(e)},t=function(e){return function(e){return void 0!==e}(e)&&!function(e){return null===e}(e)},n=function(e){return e.length},i=window,o=function(e){return n=RegExp,(r=e)&&t(n)&&r instanceof n;var r,n},a=function(e,r){return o(e)?e:(e=e.replace(/\//g,"\\/"),RegExp(e,t(r)?r:"g"))};function u(e,r){return new Promise((function(t,n){var o=i[e].apply(i,r);return o?t(o):n(o)}))}var s={source:{pairs:{"`":"`","(":")","{":"}","[":"]",'"':'"',"'":"'","<":">"},type:null}};["alert","confirm","prompt"].forEach((function(e){s.source[e]=function(){for(var r=arguments.length,t=Array(r),n=0;n<r;n++)t[n]=arguments[n];return u(e,t)}}));var c={};c.toggle=function(e,r,i,o){void 0===o&&(o=!1),r||""===r||(r=e);var a=this,u=a.$(),s=u.after,c=u.before,f=u.value,l=n(r),p=n(e);return i&&r===f.slice(-l)&&e===f.slice(0,p)||r===s.slice(0,l)&&e===c.slice(-p)?a.peel(e,r,i):(!1!==o&&("string"==typeof o?o=[o,o]:function(e){return Array.isArray(e)}(o)||(o=["",""]),t(o[1])||(o[1]=o[0]),a.trim(o[0],o[1])),a.wrap(e,r,i))};var f="Control-";var l,p,d,v=(l=function(e){return e.record()},p=100,function(){var e=arguments,r=this;d&&clearTimeout(d),d=setTimeout((function(){return l.apply(r,e)}),p)});var h=s;e.canKeyDown=function(e,t){var n,i,o=t.state.source.tab||t.state.tab||"\t",a=t.state.source.pairs||{},u=Object.values(a),s=e.key,c=e.queue,f=e+"";if(c.Alt||c.Control)return!0;if(" "===f){var l=t.$(),p=l.after,d=l.before,v=l.value;return n=a[i=d.slice(-1)],!(!v&&n&&i&&n===p[0])||(t.wrap(" "," "),!1)}if("Enter"===f){var h=t.$(),y=h.after,w=h.before,b=h.value,m=w.split("\n").pop().match(/^(\s+)/),$=m&&m[1]||"";if(!b){if(y&&w&&(n=a[i=w.slice(-1)])&&n===y[0])return t.wrap("\n"+$+(i!==n?o:""),"\n"+$).record(),!1;if($)return t.insert("\n"+$,-1).record(),!1}return!0}if("Backspace"===f){var g=t.$(),T=g.after,E=g.before,A=g.value;T.split("\n")[0];var D=E.split("\n").pop(),K=D.match(/^(\s+)/),W=K&&K[1]||"";return n=a[i=E.slice(-1)],"\\"===i?!0:A?!(T&&E&&n&&n===T[0]&&!E.endsWith("\\"+i))||(t.record().peel(i,n).record(),!1):(n=a[i=E.trim().slice(-1)])&&i&&(T.startsWith(" "+n)&&E.endsWith(i+" ")||T.startsWith("\n"+W+n)&&E.endsWith(i+"\n"+W))?(t.trim("","").record(),!1):D.endsWith(o)?(t.pull(o).record(),!1):!(T&&E&&!E.endsWith("\\"+i)&&n===T[0]&&i===E.slice(-1))||(t.peel(i,n).record(),!1)}var k=t.$(),x=k.after,j=k.before,q=k.start,C=k.value;if("\\"===(i=j.slice(-1)))return!0;if(n=r(x[0],u)?x[0]:a[i],!C&&x&&j&&n&&s===n)return t.select(q+1).record(),!1;for(i in a){if(n=a[i],s===i&&n)return t.wrap(i,n).record(),!1;if(s===n){if(C)return t.record().wrap(i,n).record(),!1;break}}return!0},e.canKeyDownDent=function(e,r){var t=r.state.source.tab||r.state.tab||"\t";e.key,e.queue;var n=e+"";return f+"]"===n?(r.push(t).record(),!1):f+"["!==n||(r.pull(t).record(),!1)},e.canKeyDownEnter=function(e,r){e.key;var t=e.queue;if(t.Control&&t.Enter){var i=r.$(),o=i.after,a=i.before,u=i.end,s=i.start,c=i.value,f=o.split("\n").shift(),l=a.split("\n").pop(),p=l.match(/^(\s+)/),d=p&&p[1]||"";if(a||o)return t.Shift?(r.select(s-n(l)).wrap(d,"\n").insert(c).record(),!1):(r.select(u+n(f)).wrap("\n"+d,"").insert(c).record(),!1)}return!0},e.canKeyDownHistory=function(e,r){var t=e+"";return f+"y"===t?(r.redo(),!1):f+"z"!==t||(r.undo(),!1)},e.canKeyDownMove=function(e,t){e.key;var i=e+"";if(!e.queue.Control)return!0;var o,u,s,c=t.$(),l=c.after,p=c.before,d=c.end,v=c.start,h=c.value,y=t.state.source.pairs||{},w=[];if(h){for(o in y)(u=y[o])&&w.push("(?:\\"+o+"(?:\\\\.|[^\\"+o+(u!==o?"\\"+u:"")+"])*\\"+u+")");if(w.push("\\w+"),w.push("\\s+"),w.push("[\\s\\S]"),f+"ArrowLeft"===i)return(s=p.match(a("("+w.join("|")+")$","")))?(t.insert("").select(v-n(s[0])).insert(h),t.record(),!1):(t.select(),!1);if(f+"ArrowRight"===i)return(s=l.match(a("^("+w.join("|")+")","")))?(t.insert("").select(d+n(s[0])-n(h)).insert(h),t.record(),!1):(t.select(),!1)}var b=l.split("\n").shift(),m=p.split("\n").pop(),$=m.match(/^(\s+)/);if($&&$[1],d+=n(b),v-=n(m),h=m+h+b,f+"ArrowUp"===i){if(!r("\n",p))return t.select(),!1;t.insert(""),t.replace(/^([^\n]*?)(\n|$)/,"$2",1),t.replace(/(^|\n)([^\n]*?)$/,"",-1);var g=t.$();return p=g.before,v=g.start,m=p.split("\n").pop(),t.select(v-=n(m)).wrap(h,"\n"),t.select(v,v+n(h)),t.record(),!1}if(f+"ArrowDown"===i){if(!r("\n",l))return t.select(),!1;t.insert(""),t.replace(/^([^\n]*?)(\n|$)/,"",1),t.replace(/(^|\n)([^\n]*?)$/,"$1",-1);var T=t.$();return l=T.after,d=T.end,b=l.split("\n").shift(),t.select(d+=n(b)).wrap("\n",h),d+=1,t.select(d,d+n(h)),t.record(),!1}return!0},e.canKeyDownTab=function(e,r){var t=r.state.source.tab||r.state.tab||"\t",n=e+"";return"Tab"===n?(r.push(t).record(),!1):"Shift-Tab"!==n||(r.pull(t).record(),!1)},e.canKeyUp=function(e,r){return v(r),!0},e.state=h,e.that=c,Object.defineProperty(e,"__esModule",{value:!0})}));
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 {esc, escChar, toPattern} from '@taufik-nurrohman/pattern';
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 pairs = {
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
- export const that = {};
13
+ const bounce = debounce($ => $.record(), 10);
37
14
 
38
- that.toggle = function (open, close, wrap, tidy = false) {
39
- if (!close && "" !== close) {
40
- close = open;
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
- let t = this,
43
- {after, before, value} = t.$(),
44
- closeCount = toCount(close),
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
- if (false !== tidy) {
53
- if (isString(tidy)) {
54
- tidy = [tidy, tidy];
55
- } else if (!isArray(tidy)) {
56
- tidy = ["", ""];
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
- if (!isSet(tidy[1])) {
59
- tidy[1] = tidy[0];
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 (queue.Alt || queue.Control) {
80
- return true;
52
+ if (ALT_PREFIX === keys + '-' || CTRL_PREFIX === keys + '-') {
53
+ offEventDefault(e);
54
+ return;
81
55
  }
82
- if (' ' === keyValue) {
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
- of.wrap(' ', ' ');
87
- return false;
59
+ offEventDefault(e);
60
+ return editor.wrap(' ', ' ');
88
61
  }
89
- return true;
62
+ return;
90
63
  }
91
- if ('Enter' === keyValue) {
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 true;
68
+ return;
118
69
  }
119
70
  if (value) {
120
71
  if (after && before && charAfter && charAfter === after[0] && !before.endsWith('\\' + charBefore)) {
121
- of.record().peel(charBefore, charAfter).record();
122
- return false;
72
+ offEventDefault(e);
73
+ return editor.record().peel(charBefore, charAfter).record();
123
74
  }
124
- return true;
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
- of.trim("", "").record();
134
- return false;
84
+ offEventDefault(e);
85
+ return editor.trim("", "").record();
135
86
  }
136
87
  }
137
88
  // Outdent
138
89
  if (lineBefore.endsWith(charIndent)) {
139
- of.pull(charIndent).record();
140
- return false;
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
- of.peel(charBefore, charAfter).record();
146
- return false;
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 true;
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 true;
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 && key === charAfter) {
122
+ if (!value && after && before && charAfter && keys === charAfter) {
159
123
  // Move to the next character
160
124
  // `}|`
161
- of.select(start + 1).record();
162
- return false;
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 (key === charBefore && charAfter) {
131
+ if (keys === charBefore && charAfter) {
168
132
  // Wrap pair or selection
169
133
  // `{|}` `{|aaa|}`
170
- of.wrap(charBefore, charAfter).record();
171
- return false;
134
+ offEventDefault(e);
135
+ return editor.wrap(charBefore, charAfter).record();
172
136
  }
173
137
  // `|}`
174
- if (key === charAfter) {
138
+ if (keys === charAfter) {
175
139
  if (value) {
176
140
  // Wrap selection
177
141
  // `{|aaa|}`
178
- of.record().wrap(charBefore, charAfter).record();
179
- return false;
142
+ offEventDefault(e);
143
+ return editor.record().wrap(charBefore, charAfter).record();
180
144
  }
181
145
  break;
182
146
  }
183
147
  }
184
- return true;
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
- boundaries.push('(?:\\' + charPair + '(?:\\\\.|[^\\' + charPair + (charPairValue !== charPair ? '\\' + charPairValue : "") + '])*\\' + charPairValue + ')');
156
+ tokens.push('(?:\\' + charPair + '(?:\\\\.|[^\\' + charPair + (charPairValue !== charPair ? '\\' + charPairValue : "") + '])*\\' + charPairValue + ')');
253
157
  }
254
- boundaries.push('\\w+'); // Word(s)
255
- boundaries.push('\\s+'); // White-space(s)
256
- boundaries.push('[\\s\\S]'); // Last try!
257
- if (CTRL_PREFIX + 'ArrowLeft' === keyValue) {
258
- if (m = before.match(toPattern('(' + boundaries.join('|') + ')$', ""))) {
259
- of.insert("").select(start - toCount(m[0])).insert(value);
260
- return of.record(), false;
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 of.select(), false;
166
+ return editor.select();
263
167
  }
264
- if (CTRL_PREFIX + 'ArrowRight' === keyValue) {
265
- if (m = after.match(toPattern('^(' + boundaries.join('|') + ')', ""))) {
266
- of.insert("").select(end + toCount(m[0]) - toCount(value)).insert(value);
267
- return of.record(), false;
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 of.select(), false;
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' === keyValue) {
180
+ if (CTRL_PREFIX + 'ArrowUp' === keys) {
181
+ offEventDefault(e);
281
182
  if (!hasValue('\n', before)) {
282
- return of.select(), false;
183
+ return editor.select();
283
184
  }
284
- of.insert("");
285
- of.replace(/^([^\n]*?)(\n|$)/, '$2', 1);
286
- of.replace(/(^|\n)([^\n]*?)$/, "", -1);
287
- let $ = of.$();
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
- of.select(start = start - toCount(lineBefore)).wrap(value, '\n');
292
- of.select(start, start + toCount(value));
293
- return of.record(), false;
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' === keyValue) {
196
+ if (CTRL_PREFIX + 'ArrowDown' === keys) {
197
+ offEventDefault(e);
296
198
  if (!hasValue('\n', after)) {
297
- return of.select(), false;
199
+ return editor.select();
298
200
  }
299
- of.insert("");
300
- of.replace(/^([^\n]*?)(\n|$)/, "", 1);
301
- of.replace(/(^|\n)([^\n]*?)$/, '$1', -1);
302
- let $ = of.$();
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
- of.select(end = end + toCount(lineAfter)).wrap('\n', value);
208
+ editor.select(end = end + toCount(lineAfter)).wrap('\n', value);
307
209
  end += 1;
308
- of.select(end, end + toCount(value));
309
- return of.record(), false;
210
+ editor.select(end, end + toCount(value));
211
+ return editor.record();
310
212
  }
311
- return true;
213
+ return;
312
214
  }
313
215
 
314
- export function canKeyDownTab(map, of) {
315
- let charIndent = of.state.source.tab || of.state.tab || '\t',
316
- keyValue = map + "";
317
- // Indent with `⇥`
318
- if ('Tab' === keyValue) {
319
- return of.push(charIndent).record(), false;
320
- }
321
- // Outdent with `⇧+⇥`
322
- if (SHIFT_PREFIX + 'Tab' === keyValue) {
323
- return of.pull(charIndent).record(), false;
324
- }
325
- return true;
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
- let bounce = debounce(of => of.record(), 100);
329
-
330
- export function canKeyUp(map, of) {
331
- return bounce(of), true;
262
+ function detach(self) {
263
+ let $ = this;
264
+ offEvent('keydown', self, onKeyDown);
265
+ return $;
332
266
  }
333
267
 
334
- export const state = defaults;
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/key": "*",
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=.github/factory --js-format=umd --js-name=TE.Source --js-top='%(js.license)' --mjs=true --to=."
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": "2.2.10"
55
+ "version": "3.0.0"
53
56
  }