@taufik-nurrohman/text-editor.source 2.2.11 → 3.0.1
Sign up to get free protection for your applications and to get access to all the features.
- package/LICENSE +1 -1
- package/README.md +6 -4
- package/index.js +312 -295
- package/index.min.js +2 -2
- package/index.mjs +224 -236
- package/package.json +7 -3
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){return"function"==typeof e},s=function(e,r){return e&&u(r)&&e instanceof r},c=function(e){return l(e)&&0==e%1},l=function(e){return"number"==typeof e},f=function(e,r){return void 0===r&&(r=!0),"object"==typeof e&&(!r||s(e,Object))},u=function(e){return function(e){return void 0!==e}(e)&&!function(e){return null===e}(e)},a=function(e){return e.length},p=function(e){return Object.values(e)},d=function e(){for(var r=arguments.length,t=Array(r),o=0;o<r;o++)t[o]=arguments[o];for(var s=t.shift(),c=0,l=a(t);c<l;++c)for(var p in t[c])if(u(s[p]))if(i(s[p])&&i(t[c][p])){s[p]=[].concat(s[p]);for(var d=0,h=a(t[c][p]);d<h;++d)n(t[c][p][d],s[p])||s[p].push(t[c][p][d])}else f(s[p])&&f(t[c][p])?s[p]=e({},s[p],t[c][p]):s[p]=t[c][p];else s[p]=t[c][p];return s},h=window,v=function(e){return e&&e.preventDefault()},w=function(e,r){return function(e){return s(e,RegExp)}(e)?e:RegExp(e,u(r)?r:"g")},b="Alt-",$="Control-",m="Shift-",k=(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 y(e){var r,t,i=this,o=i.k(!1).pop(),s=i.k();if(k(i),!e.defaultPrevented&&!i.keys[s]){var l,f,u=(null==(r=i.state.source)?void 0:r.tab)||i.state.tab||"\t",d=(null==(t=i.state.source)?void 0:t.pairs)||{},h=p(d);c(u)&&(u=" ".repeat(u));var y=i.$(),g=y.after,x=y.before,E=y.end,A=y.start,B=y.value,T=g.split("\n").shift(),W=x.split("\n").pop(),j=/^\s+/.exec(W),D=j&&j[0]||"";if($+m+"Enter"===s)return x||g?(v(e),i.select(A-a(W)).wrap(D,"\n").insert(B).record(),!1):void 0;if($+"Enter"===s&&(x||g))return v(e),i.select(E+a(T)).wrap("\n"+D,"").insert(B).record(),!1;if(b!==s+"-"&&$!==s+"-"){if(" "===s)return l=d[f=x.slice(-1)],!B&&l&&f&&l===g[0]?(v(e),i.wrap(" "," ")):void 0;if("Backspace"===s||"Delete"===s){if(l=d[f=x.slice(-1)],"\\"===f)return;return B?g&&x&&l&&l===g[0]&&!x.endsWith("\\"+f)?(v(e),i.record().peel(f,l).record()):void 0:(l=d[f=x.trim().slice(-1)])&&f&&(g.startsWith(" "+l)&&x.endsWith(f+" ")||g.startsWith("\n"+D+l)&&x.endsWith(f+"\n"+D))?(v(e),i.trim("","").record()):"Delete"!==s&&W.endsWith(u)?(v(e),i.pull(u).record()):g&&x&&!x.endsWith("\\"+f)&&l===g[0]&&f===x.slice(-1)?(v(e),i.peel(f,l).record()):void 0}if("Enter"!==s&&m+"Enter"!==s){if("\\"!==(f=x.slice(-1))){if(l=n(g[0],h)?g[0]:d[f],!B&&g&&x&&l&&o===l)return v(e),i.select(A+1).record();for(f in d){if(l=d[f],o===f&&l)return v(e),i.wrap(f,l).record();if(o===l){if(B)return v(e),i.record().wrap(f,l).record();break}}var O,R,S,C=[];if(B){for(O in d)(R=d[O])&&C.push("(?:\\"+O+"(?:\\\\.|[^\\"+O+(R!==O?"\\"+R:"")+"])*\\"+R+")");if(C.push("\\w+"),C.push("\\s+"),C.push("[\\s\\S]"),$+"ArrowLeft"===s)return v(e),(S=w("("+C.join("|")+")$","").exec(x))?i.insert("").select(A-a(S[0])).insert(B).record():i.select();if($+"ArrowRight"===s)return v(e),(S=g.match(w("^("+C.join("|")+")","")))?i.insert("").select(E+a(S[0])-a(B)).insert(B).record():i.select()}if(E+=a(T),A-=a(W),B=W+B+T,$+"ArrowUp"===s){if(v(e),!n("\n",x))return i.select();i.insert(""),i.replace(/^([^\n]*?)(\n|$)/,"$2",1),i.replace(/(^|\n)([^\n]*?)$/,"",-1);var L=i.$();return x=L.before,A=L.start,W=x.split("\n").pop(),i.select(A-=a(W)).wrap(B,"\n"),i.select(A,A+a(B)),i.record()}if($+"ArrowDown"===s){if(v(e),!n("\n",g))return i.select();i.insert(""),i.replace(/^([^\n]*?)(\n|$)/,"",1),i.replace(/(^|\n)([^\n]*?)$/,"$1",-1);var P=i.$();return g=P.after,E=P.end,T=g.split("\n").shift(),i.select(E+=a(T)).wrap("\n",B),E+=1,i.select(E,E+a(B)),i.record()}}}else if(!B){if(g&&x&&(l=d[f=x.slice(-1)])&&l===g[0])return v(e),i.wrap("\n"+D+(f!==l?u:""),"\n"+D).record();if(D)return v(e),i.insert("\n"+D,-1).record()}}else v(e)}}var g={attach:function(){var e=this;return e.state=d({source:{pairs:{"`":"`","(":")","{":"}","[":"]",'"':'"',"'":"'","<":">"}}},e.state),e.alert=function(r,t){return h.alert&&h.alert(r),o(t)&&t.call(e,!0)},e.confirm=function(r,t){return o(t)&&t.call(e,h.confirm&&h.confirm(r))},e.insertBlock=function(r,t){var n=e.$(),i=n.after,o=n.before,s=n.end,c=n.start,l=i.split("\n").shift(),f=a(l),u=o.split("\n").pop(),p=a(u),d=/^\s+/.exec(u),h=d&&d[0]||"";return-1===t?e.select(c-p).insert("\n",1).push(h).insert(r,1,!1):1===t?e.select(s+f).insert("\n",-1).push(h).insert(r,1,!1):e.select(c-p,s+f).insert(r,t,!0).wrap(h,"")},e.peelBlock=function(r,t,n){var i=e.$(),o=i.after,s=i.before,c=i.end,l=i.start,f=i.value,u=a(t),p=o.split("\n").shift(),d=a(p),h=s.split("\n").pop(),v=a(h),w=a(r);return n&&t===f.slice(-u)&&r===f.slice(0,w)||t===p.slice(-u)&&r===h.slice(0,w)?e.select(l-v+(n?0:w),c+d-(n?0:u)).peel(r,t,n):e.select(l,c)},e.prompt=function(r,t,n){return o(n)&&n.call(e,!!h.prompt&&h.prompt(r,t))},e.selectBlock=function(){var r=e.$(),t=r.after,n=r.before,i=r.end,o=r.start,s=t.split("\n").shift(),c=a(s),l=n.split("\n").pop(),f=a(l);return e.select(o-f,i+c)},e.toggle=function(r,t,n){var i=e.$(),o=i.after,s=i.before,c=i.value,l=a(t),f=a(r);return n&&t===c.slice(-l)&&r===c.slice(0,f)||t===o.slice(0,l)&&r===s.slice(-f)?e.peel(r,t,n):e.wrap(r,t,n)},e.toggleBlock=function(r,t,n){var i=e.$(),o=i.after,s=i.before,c=i.value,l=a(t),f=o.split("\n").shift(),u=s.split("\n").pop(),p=a(r);return n&&t===c.slice(-l)&&r===c.slice(0,p)||t===f.slice(-l)&&r===u.slice(0,p)?e.peelBlock(r,t,n):e.wrapBlock(r,t,n)},e.wrapBlock=function(r,t,n){var i=e.$(),o=i.after,s=i.before,c=i.end,l=i.start,f=o.split("\n").shift(),u=a(f),p=s.split("\n").pop(),d=a(p);return e.select(l-d,c+u).wrap(r,t,n)},e.on("key.down",y).record()},detach:function(){return this.off("key.down",y)}};return g}));
|
package/index.mjs
CHANGED
@@ -1,127 +1,79 @@
|
|
1
1
|
import {W} from '@taufik-nurrohman/document';
|
2
2
|
import {debounce} from '@taufik-nurrohman/tick';
|
3
|
-
import {
|
3
|
+
import {fromStates} from '@taufik-nurrohman/from';
|
4
4
|
import {hasValue} from '@taufik-nurrohman/has';
|
5
|
-
import {isArray, isSet, isString} from '@taufik-nurrohman/is';
|
5
|
+
import {isArray, isFunction, isInteger, isSet, isString} from '@taufik-nurrohman/is';
|
6
|
+
import {offEventDefault} from '@taufik-nurrohman/event';
|
6
7
|
import {toCount, toObjectValues} from '@taufik-nurrohman/to';
|
8
|
+
import {toPattern} from '@taufik-nurrohman/pattern';
|
7
9
|
|
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
|
-
});
|
10
|
+
const ALT_PREFIX = 'Alt-';
|
11
|
+
const CTRL_PREFIX = 'Control-';
|
12
|
+
const SHIFT_PREFIX = 'Shift-';
|
35
13
|
|
36
|
-
|
14
|
+
const bounce = debounce($ => $.record(), 10);
|
37
15
|
|
38
|
-
|
39
|
-
|
40
|
-
|
16
|
+
function onKeyDown(e) {
|
17
|
+
let $ = this,
|
18
|
+
key = $.k(false).pop(), // Capture the last key
|
19
|
+
keys = $.k();
|
20
|
+
bounce($);
|
21
|
+
if (e.defaultPrevented || $.keys[keys]) {
|
22
|
+
return;
|
41
23
|
}
|
42
|
-
let
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
) {
|
50
|
-
return t.peel(open, close, wrap);
|
24
|
+
let charAfter,
|
25
|
+
charBefore,
|
26
|
+
charIndent = $.state.source?.tab || $.state.tab || '\t',
|
27
|
+
charPairs = $.state.source?.pairs || {},
|
28
|
+
charPairsValues = toObjectValues(charPairs);
|
29
|
+
if (isInteger(charIndent)) {
|
30
|
+
charIndent = ' '.repeat(charIndent);
|
51
31
|
}
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
32
|
+
let {after, before, end, start, value} = $.$(),
|
33
|
+
lineAfter = after.split('\n').shift(),
|
34
|
+
lineBefore = before.split('\n').pop(),
|
35
|
+
lineMatch = /^\s+/.exec(lineBefore),
|
36
|
+
lineMatchIndent = lineMatch && lineMatch[0] || "";
|
37
|
+
if (CTRL_PREFIX + SHIFT_PREFIX + 'Enter' === keys) {
|
38
|
+
if (before || after) {
|
39
|
+
// Insert line above with `⎈⇧↵`
|
40
|
+
offEventDefault(e);
|
41
|
+
return $.select(start - toCount(lineBefore)).wrap(lineMatchIndent, '\n').insert(value).record(), false;
|
57
42
|
}
|
58
|
-
|
59
|
-
|
43
|
+
return;
|
44
|
+
}
|
45
|
+
if (CTRL_PREFIX + 'Enter' === keys) {
|
46
|
+
if (before || after) {
|
47
|
+
// Insert line below with `⎈↵`
|
48
|
+
offEventDefault(e);
|
49
|
+
return $.select(end + toCount(lineAfter)).wrap('\n' + lineMatchIndent, "").insert(value).record(), false;
|
60
50
|
}
|
61
|
-
t.trim(tidy[0], tidy[1]);
|
62
51
|
}
|
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
52
|
// Do nothing
|
79
|
-
if (
|
80
|
-
|
53
|
+
if (ALT_PREFIX === keys + '-' || CTRL_PREFIX === keys + '-') {
|
54
|
+
offEventDefault(e);
|
55
|
+
return;
|
81
56
|
}
|
82
|
-
if (' ' ===
|
83
|
-
let {after, before, value} = of.$();
|
57
|
+
if (' ' === keys) {
|
84
58
|
charAfter = charPairs[charBefore = before.slice(-1)];
|
85
59
|
if (!value && charAfter && charBefore && charAfter === after[0]) {
|
86
|
-
|
87
|
-
return
|
88
|
-
}
|
89
|
-
return true;
|
90
|
-
}
|
91
|
-
if ('Enter' === keyValue || SHIFT_PREFIX + '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
|
-
}
|
60
|
+
offEventDefault(e);
|
61
|
+
return $.wrap(' ', ' ');
|
105
62
|
}
|
106
|
-
return
|
63
|
+
return;
|
107
64
|
}
|
108
|
-
if ('Backspace' ===
|
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] || "";
|
65
|
+
if ('Backspace' === keys || 'Delete' === keys) {
|
114
66
|
charAfter = charPairs[charBefore = before.slice(-1)];
|
115
67
|
// Do nothing on escape
|
116
68
|
if ('\\' === charBefore) {
|
117
|
-
return
|
69
|
+
return;
|
118
70
|
}
|
119
71
|
if (value) {
|
120
72
|
if (after && before && charAfter && charAfter === after[0] && !before.endsWith('\\' + charBefore)) {
|
121
|
-
|
122
|
-
return
|
73
|
+
offEventDefault(e);
|
74
|
+
return $.record().peel(charBefore, charAfter).record();
|
123
75
|
}
|
124
|
-
return
|
76
|
+
return;
|
125
77
|
}
|
126
78
|
charAfter = charPairs[charBefore = before.trim().slice(-1)];
|
127
79
|
if (charAfter && charBefore) {
|
@@ -130,36 +82,48 @@ export function canKeyDown(map, of) {
|
|
130
82
|
after.startsWith('\n' + lineMatchIndent + charAfter) && before.endsWith(charBefore + '\n' + lineMatchIndent)
|
131
83
|
) {
|
132
84
|
// Collapse bracket(s)
|
133
|
-
|
134
|
-
return
|
85
|
+
offEventDefault(e);
|
86
|
+
return $.trim("", "").record();
|
135
87
|
}
|
136
88
|
}
|
137
89
|
// Outdent
|
138
|
-
if (lineBefore.endsWith(charIndent)) {
|
139
|
-
|
140
|
-
return
|
90
|
+
if ('Delete' !== keys && lineBefore.endsWith(charIndent)) {
|
91
|
+
offEventDefault(e);
|
92
|
+
return $.pull(charIndent).record();
|
141
93
|
}
|
142
94
|
if (after && before && !before.endsWith('\\' + charBefore)) {
|
143
95
|
if (charAfter === after[0] && charBefore === before.slice(-1)) {
|
144
96
|
// Peel pair
|
145
|
-
|
146
|
-
return
|
97
|
+
offEventDefault(e);
|
98
|
+
return $.peel(charBefore, charAfter).record();
|
99
|
+
}
|
100
|
+
}
|
101
|
+
return;
|
102
|
+
}
|
103
|
+
if ('Enter' === keys || SHIFT_PREFIX + 'Enter' === keys) {
|
104
|
+
if (!value) {
|
105
|
+
if (after && before && (charAfter = charPairs[charBefore = before.slice(-1)]) && charAfter === after[0]) {
|
106
|
+
offEventDefault(e);
|
107
|
+
return $.wrap('\n' + lineMatchIndent + (charBefore !== charAfter ? charIndent : ""), '\n' + lineMatchIndent).record();
|
108
|
+
}
|
109
|
+
if (lineMatchIndent) {
|
110
|
+
offEventDefault(e);
|
111
|
+
return $.insert('\n' + lineMatchIndent, -1).record();
|
147
112
|
}
|
148
113
|
}
|
149
|
-
return
|
114
|
+
return;
|
150
115
|
}
|
151
|
-
let {after, before, start, value} = of.$();
|
152
116
|
// Do nothing on escape
|
153
117
|
if ('\\' === (charBefore = before.slice(-1))) {
|
154
|
-
return
|
118
|
+
return;
|
155
119
|
}
|
156
120
|
charAfter = hasValue(after[0], charPairsValues) ? after[0] : charPairs[charBefore];
|
157
121
|
// `|}`
|
158
122
|
if (!value && after && before && charAfter && key === charAfter) {
|
159
123
|
// Move to the next character
|
160
124
|
// `}|`
|
161
|
-
|
162
|
-
return
|
125
|
+
offEventDefault(e);
|
126
|
+
return $.select(start + 1).record();
|
163
127
|
}
|
164
128
|
for (charBefore in charPairs) {
|
165
129
|
charAfter = charPairs[charBefore];
|
@@ -167,168 +131,192 @@ export function canKeyDown(map, of) {
|
|
167
131
|
if (key === charBefore && charAfter) {
|
168
132
|
// Wrap pair or selection
|
169
133
|
// `{|}` `{|aaa|}`
|
170
|
-
|
171
|
-
return
|
134
|
+
offEventDefault(e);
|
135
|
+
return $.wrap(charBefore, charAfter).record();
|
172
136
|
}
|
173
137
|
// `|}`
|
174
138
|
if (key === charAfter) {
|
175
139
|
if (value) {
|
176
140
|
// Wrap selection
|
177
141
|
// `{|aaa|}`
|
178
|
-
|
179
|
-
return
|
142
|
+
offEventDefault(e);
|
143
|
+
return $.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 above 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 = toPattern('(' + tokens.join('|') + ')$', "").exec(before)) {
|
164
|
+
return $.insert("").select(start - toCount(m[0])).insert(value).record();
|
261
165
|
}
|
262
|
-
return
|
166
|
+
return $.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 $.insert("").select(end + toCount(m[0]) - toCount(value)).insert(value).record();
|
268
172
|
}
|
269
|
-
return
|
173
|
+
return $.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 $.select();
|
283
184
|
}
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
let
|
288
|
-
before =
|
289
|
-
start =
|
185
|
+
$.insert("");
|
186
|
+
$.replace(/^([^\n]*?)(\n|$)/, '$2', 1);
|
187
|
+
$.replace(/(^|\n)([^\n]*?)$/, "", -1);
|
188
|
+
let s = $.$();
|
189
|
+
before = s.before;
|
190
|
+
start = s.start;
|
290
191
|
lineBefore = before.split('\n').pop();
|
291
|
-
|
292
|
-
|
293
|
-
return
|
192
|
+
$.select(start = start - toCount(lineBefore)).wrap(value, '\n');
|
193
|
+
$.select(start, start + toCount(value));
|
194
|
+
return $.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 $.select();
|
298
200
|
}
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
let
|
303
|
-
after =
|
304
|
-
end =
|
201
|
+
$.insert("");
|
202
|
+
$.replace(/^([^\n]*?)(\n|$)/, "", 1);
|
203
|
+
$.replace(/(^|\n)([^\n]*?)$/, '$1', -1);
|
204
|
+
let s = $.$();
|
205
|
+
after = s.after;
|
206
|
+
end = s.end;
|
305
207
|
lineAfter = after.split('\n').shift();
|
306
|
-
|
208
|
+
$.select(end = end + toCount(lineAfter)).wrap('\n', value);
|
307
209
|
end += 1;
|
308
|
-
|
309
|
-
return
|
210
|
+
$.select(end, end + toCount(value));
|
211
|
+
return $.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() {
|
217
|
+
let $ = this;
|
218
|
+
$.state = fromStates({
|
219
|
+
source: {
|
220
|
+
pairs: {
|
221
|
+
'`': '`',
|
222
|
+
'(': ')',
|
223
|
+
'{': '}',
|
224
|
+
'[': ']',
|
225
|
+
'"': '"',
|
226
|
+
"'": "'",
|
227
|
+
'<': '>'
|
228
|
+
}
|
229
|
+
}
|
230
|
+
}, $.state);
|
231
|
+
$.alert = (hint, then) => {
|
232
|
+
W.alert && W.alert(hint);
|
233
|
+
return isFunction(then) && then.call($, true);
|
234
|
+
};
|
235
|
+
$.confirm = (hint, then) => {
|
236
|
+
return isFunction(then) && then.call($, W.confirm && W.confirm(hint));
|
237
|
+
};
|
238
|
+
$.insertBlock = (value, mode) => {
|
239
|
+
let {after, before, end, start} = $.$(),
|
240
|
+
lineAfter = after.split('\n').shift(),
|
241
|
+
lineAfterCount = toCount(lineAfter),
|
242
|
+
lineBefore = before.split('\n').pop(),
|
243
|
+
lineBeforeCount = toCount(lineBefore),
|
244
|
+
lineMatch = /^\s+/.exec(lineBefore),
|
245
|
+
lineMatchIndent = lineMatch && lineMatch[0] || "";
|
246
|
+
if (-1 === mode) {
|
247
|
+
return $.select(start - lineBeforeCount).insert('\n', 1).push(lineMatchIndent).insert(value, 1, false);
|
248
|
+
}
|
249
|
+
if (1 === mode) {
|
250
|
+
return $.select(end + lineAfterCount).insert('\n', -1).push(lineMatchIndent).insert(value, 1, false);
|
251
|
+
}
|
252
|
+
return $.select(start - lineBeforeCount, end + lineAfterCount).insert(value, mode, true).wrap(lineMatchIndent, "");
|
253
|
+
};
|
254
|
+
$.peelBlock = (open, close, wrap) => {
|
255
|
+
let {after, before, end, start, value} = $.$(),
|
256
|
+
closeCount = toCount(close),
|
257
|
+
lineAfter = after.split('\n').shift(),
|
258
|
+
lineAfterCount = toCount(lineAfter),
|
259
|
+
lineBefore = before.split('\n').pop(),
|
260
|
+
lineBeforeCount = toCount(lineBefore),
|
261
|
+
openCount = toCount(open);
|
262
|
+
if (
|
263
|
+
(wrap && close === value.slice(-closeCount) && open === value.slice(0, openCount)) ||
|
264
|
+
(close === lineAfter.slice(-closeCount) && open === lineBefore.slice(0, openCount))
|
265
|
+
) {
|
266
|
+
return $.select(start - lineBeforeCount + (wrap ? 0 : openCount), end + lineAfterCount - (wrap ? 0 : closeCount)).peel(open, close, wrap);
|
267
|
+
}
|
268
|
+
return $.select(start, end);
|
269
|
+
};
|
270
|
+
$.prompt = (hint, value, then) => {
|
271
|
+
return isFunction(then) && then.call($, W.prompt ? W.prompt(hint, value) : false);
|
272
|
+
};
|
273
|
+
$.selectBlock = () => {
|
274
|
+
let {after, before, end, start} = $.$(),
|
275
|
+
lineAfter = after.split('\n').shift(),
|
276
|
+
lineAfterCount = toCount(lineAfter),
|
277
|
+
lineBefore = before.split('\n').pop(),
|
278
|
+
lineBeforeCount = toCount(lineBefore);
|
279
|
+
return $.select(start - lineBeforeCount, end + lineAfterCount);
|
280
|
+
};
|
281
|
+
$.toggle = (open, close, wrap) => {
|
282
|
+
let {after, before, value} = $.$(),
|
283
|
+
closeCount = toCount(close),
|
284
|
+
openCount = toCount(open);
|
285
|
+
if (
|
286
|
+
(wrap && close === value.slice(-closeCount) && open === value.slice(0, openCount)) ||
|
287
|
+
(close === after.slice(0, closeCount) && open === before.slice(-openCount))
|
288
|
+
) {
|
289
|
+
return $.peel(open, close, wrap);
|
290
|
+
}
|
291
|
+
return $.wrap(open, close, wrap);
|
292
|
+
};
|
293
|
+
$.toggleBlock = (open, close, wrap) => {
|
294
|
+
let {after, before, value} = $.$(),
|
295
|
+
closeCount = toCount(close),
|
296
|
+
lineAfter = after.split('\n').shift(),
|
297
|
+
lineBefore = before.split('\n').pop(),
|
298
|
+
openCount = toCount(open);
|
299
|
+
if (
|
300
|
+
(wrap && close === value.slice(-closeCount) && open === value.slice(0, openCount)) ||
|
301
|
+
(close === lineAfter.slice(-closeCount) && open === lineBefore.slice(0, openCount))
|
302
|
+
) {
|
303
|
+
return $.peelBlock(open, close, wrap);
|
304
|
+
}
|
305
|
+
return $.wrapBlock(open, close, wrap);
|
306
|
+
};
|
307
|
+
$.wrapBlock = (open, close, wrap) => {
|
308
|
+
let {after, before, end, start} = $.$(),
|
309
|
+
lineAfter = after.split('\n').shift(),
|
310
|
+
lineAfterCount = toCount(lineAfter),
|
311
|
+
lineBefore = before.split('\n').pop(),
|
312
|
+
lineBeforeCount = toCount(lineBefore);
|
313
|
+
return $.select(start - lineBeforeCount, end + lineAfterCount).wrap(open, close, wrap);
|
314
|
+
};
|
315
|
+
return $.on('key.down', onKeyDown).record();
|
326
316
|
}
|
327
317
|
|
328
|
-
|
329
|
-
|
330
|
-
export function canKeyUp(map, of) {
|
331
|
-
return bounce(of), true;
|
318
|
+
function detach() {
|
319
|
+
return this.off('key.down', onKeyDown);
|
332
320
|
}
|
333
321
|
|
334
|
-
export
|
322
|
+
export default {attach, detach};
|
package/package.json
CHANGED
@@ -3,11 +3,15 @@
|
|
3
3
|
"browser": "index.min.js",
|
4
4
|
"bugs": "https://github.com/taufik-nurrohman/text-editor.source/issues",
|
5
5
|
"dependencies": {
|
6
|
+
"@taufik-nurrohman/document": "*",
|
7
|
+
"@taufik-nurrohman/event": "*",
|
8
|
+
"@taufik-nurrohman/from": "*",
|
6
9
|
"@taufik-nurrohman/has": "*",
|
7
|
-
"@taufik-nurrohman/
|
10
|
+
"@taufik-nurrohman/is": "*",
|
8
11
|
"@taufik-nurrohman/pattern": "*",
|
9
12
|
"@taufik-nurrohman/text-editor": "*",
|
10
13
|
"@taufik-nurrohman/text-editor.history": "*",
|
14
|
+
"@taufik-nurrohman/text-editor.key": "*",
|
11
15
|
"@taufik-nurrohman/tick": "*",
|
12
16
|
"@taufik-nurrohman/to": "*"
|
13
17
|
},
|
@@ -47,7 +51,7 @@
|
|
47
51
|
"url": "git+https://github.com/taufik-nurrohman/text-editor.source.git"
|
48
52
|
},
|
49
53
|
"scripts": {
|
50
|
-
"pack": "pack --clean=false --from=.
|
54
|
+
"pack": "pack --clean=false --from=.factory --js-format=umd --js-name=TextEditor.Source --js-top='%(js.license)' --mjs=true --to=."
|
51
55
|
},
|
52
|
-
"version": "
|
56
|
+
"version": "3.0.1"
|
53
57
|
}
|