reslop 0.2.5 → 0.2.6
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/CHANGELOG.md +12 -1
- package/LICENSE +1 -1
- package/README.md +11 -11
- package/bin/reslop.js +3 -8
- package/lib/ansi.js +32 -19
- package/lib/capabilities.js +8 -8
- package/lib/cli.js +40 -74
- package/lib/clipboard.js +39 -47
- package/lib/dashboard/git.js +128 -0
- package/lib/dashboard/model.js +332 -0
- package/lib/dashboard/npm.js +193 -0
- package/lib/dashboard/tree.js +278 -0
- package/lib/deps-diff.js +197 -402
- package/lib/deps.js +113 -240
- package/lib/detect.js +8 -18
- package/lib/diff/align.js +41 -113
- package/lib/diff/diff.js +12 -43
- package/lib/diff/display.js +25 -36
- package/lib/diff/edit.js +6 -20
- package/lib/diff/inline.js +76 -144
- package/lib/diff/items.js +33 -47
- package/lib/diff/parse.js +28 -56
- package/lib/diff/patch.js +12 -33
- package/lib/diff/unit.js +79 -178
- package/lib/editor.js +52 -69
- package/lib/files.js +71 -89
- package/lib/git-branches.js +254 -416
- package/lib/git-deps.js +147 -272
- package/lib/git-reword-todo.js +5 -13
- package/lib/git-worktree.js +141 -212
- package/lib/git.js +21 -49
- package/lib/github.js +67 -181
- package/lib/gitlab.js +40 -90
- package/lib/highlight/bash.js +55 -177
- package/lib/highlight/core.js +155 -141
- package/lib/highlight/css.js +25 -58
- package/lib/highlight/csv.js +19 -37
- package/lib/highlight/dart.js +72 -213
- package/lib/highlight/dot.js +14 -14
- package/lib/highlight/html.js +42 -63
- package/lib/highlight/js.js +177 -506
- package/lib/highlight/json.js +17 -37
- package/lib/highlight/log.js +19 -25
- package/lib/highlight/txt.js +7 -10
- package/lib/highlight.js +17 -46
- package/lib/keys.js +49 -82
- package/lib/manifest.js +67 -126
- package/lib/npm-commands.js +120 -169
- package/lib/remote.js +140 -190
- package/lib/render/branches.js +63 -61
- package/lib/render/chrome.js +220 -333
- package/lib/render/commits.js +160 -361
- package/lib/render/dash-activity.js +212 -0
- package/lib/render/dash-blocks.js +245 -0
- package/lib/render/dash-table.js +290 -0
- package/lib/render/dashboard.js +62 -0
- package/lib/render/diff.js +123 -258
- package/lib/render/files.js +40 -85
- package/lib/render/frame.js +78 -117
- package/lib/render/notes.js +139 -210
- package/lib/render/npm.js +213 -177
- package/lib/render/present.js +4 -11
- package/lib/render/primitives.js +72 -174
- package/lib/render/render.js +5 -14
- package/lib/render/report.js +43 -0
- package/lib/render/tiles.js +260 -0
- package/lib/render/unit.js +15 -31
- package/lib/report-model.js +110 -33
- package/lib/report-parse.js +56 -37
- package/lib/report-render.js +102 -59
- package/lib/report-run.js +87 -77
- package/lib/report-test.js +221 -99
- package/lib/report-tools.js +96 -154
- package/lib/review-merge.js +33 -41
- package/lib/review.js +68 -129
- package/lib/runs.js +225 -0
- package/lib/select.js +53 -91
- package/lib/session/accessors.js +14 -21
- package/lib/session/actions.js +61 -43
- package/lib/session/branches.js +166 -222
- package/lib/session/change-actions.js +49 -119
- package/lib/session/commit.js +274 -355
- package/lib/session/compose.js +556 -714
- package/lib/session/dashboard.js +385 -0
- package/lib/session/dispatch.js +162 -170
- package/lib/session/files-pane.js +76 -44
- package/lib/session/items.js +6 -17
- package/lib/session/lifecycle.js +393 -466
- package/lib/session/load.js +1 -14
- package/lib/session/navigation.js +2 -2
- package/lib/session/npm.js +403 -420
- package/lib/session/ops.js +5 -13
- package/lib/session/pointer.js +16 -14
- package/lib/session/progress.js +39 -30
- package/lib/session/review.js +1 -3
- package/lib/session/tasks.js +276 -235
- package/lib/session/templates.js +51 -59
- package/lib/session/terminal.js +85 -132
- package/lib/session/view.js +47 -81
- package/lib/session/watch.js +196 -212
- package/lib/session.js +81 -161
- package/lib/update.js +44 -111
- package/lib/utilities.js +46 -88
- package/lib/wrap.js +29 -55
- package/package.json +1 -1
package/lib/diff/unit.js
CHANGED
|
@@ -5,16 +5,14 @@ const { attachInline } = align;
|
|
|
5
5
|
const files = require('../files.js');
|
|
6
6
|
const { listPath } = files;
|
|
7
7
|
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
return lines;
|
|
12
|
-
};
|
|
8
|
+
const LCS_CELLS = 4000000;
|
|
9
|
+
|
|
10
|
+
const splitEditor = (text) => text.split('\n');
|
|
13
11
|
|
|
14
12
|
const splitFile = (text) => {
|
|
15
13
|
if (!text) return [];
|
|
16
|
-
const lines =
|
|
17
|
-
if (lines
|
|
14
|
+
const lines = text.split('\n');
|
|
15
|
+
if (lines[lines.length - 1] === '') lines.pop();
|
|
18
16
|
return lines;
|
|
19
17
|
};
|
|
20
18
|
|
|
@@ -28,53 +26,13 @@ const hunkKey = (item) => {
|
|
|
28
26
|
|
|
29
27
|
const sameHunk = (left, right) => {
|
|
30
28
|
if (!left || !right) return false;
|
|
31
|
-
if (left.hunk &&
|
|
29
|
+
if (left.hunk && left.hunk === right.hunk) {
|
|
32
30
|
return listPath(left) === listPath(right);
|
|
33
31
|
}
|
|
34
|
-
|
|
35
|
-
if (!key) return false;
|
|
36
|
-
return key === hunkKey(right);
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const emptyMarks = (last) => {
|
|
40
|
-
const size = last + 1;
|
|
41
|
-
const dels = new Array(size);
|
|
42
|
-
const adds = new Array(size);
|
|
43
|
-
for (let i = 0; i < size; i++) {
|
|
44
|
-
dels[i] = [];
|
|
45
|
-
adds[i] = null;
|
|
46
|
-
}
|
|
47
|
-
return { dels, adds };
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const clipAt = (n, last) => Math.min(Math.max(1, n), last);
|
|
51
|
-
|
|
52
|
-
const applyHunk = (marks, item) => {
|
|
53
|
-
const hunk = item.hunk;
|
|
54
|
-
if (!hunk || !hunk.lines) return;
|
|
55
|
-
const last = marks.dels.length - 1;
|
|
56
|
-
let newLine = hunk.newStart > 0 ? hunk.newStart : 1;
|
|
57
|
-
for (const line of hunk.lines) {
|
|
58
|
-
const extra = {
|
|
59
|
-
text: line.text ?? '',
|
|
60
|
-
item,
|
|
61
|
-
blockId: line.blockId,
|
|
62
|
-
origin: item.origin,
|
|
63
|
-
};
|
|
64
|
-
if (line.type === 'del') {
|
|
65
|
-
marks.dels[clipAt(newLine, last)].push(extra);
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
if (line.type === 'add') {
|
|
69
|
-
marks.adds[clipAt(newLine, last)] = extra;
|
|
70
|
-
newLine += 1;
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
newLine += 1;
|
|
74
|
-
}
|
|
32
|
+
return hunkKey(left) === hunkKey(right);
|
|
75
33
|
};
|
|
76
34
|
|
|
77
|
-
const tagged = (type, text, extra
|
|
35
|
+
const tagged = (type, text, extra) => ({
|
|
78
36
|
type,
|
|
79
37
|
text,
|
|
80
38
|
noNl: false,
|
|
@@ -83,20 +41,35 @@ const tagged = (type, text, extra = {}) => ({
|
|
|
83
41
|
item: extra.item ?? null,
|
|
84
42
|
});
|
|
85
43
|
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
44
|
+
const NO_EXTRA = {};
|
|
45
|
+
|
|
46
|
+
const clipAt = (n, last) => Math.min(Math.max(1, n), last);
|
|
47
|
+
|
|
48
|
+
const markHunk = (marks, item) => {
|
|
49
|
+
if (!item.hunk) return;
|
|
50
|
+
const last = marks.dels.length - 1;
|
|
51
|
+
let newLine = item.hunk.newStart > 0 ? item.hunk.newStart : 1;
|
|
52
|
+
for (const line of item.hunk.lines) {
|
|
53
|
+
const { type, text, blockId } = line;
|
|
54
|
+
const mark = { text, item, blockId, origin: item.origin };
|
|
55
|
+
if (type === 'del') {
|
|
56
|
+
marks.dels[clipAt(newLine, last)].push(mark);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (type === 'add') marks.adds[clipAt(newLine, last)] = mark;
|
|
60
|
+
newLine += 1;
|
|
90
61
|
}
|
|
91
|
-
return out;
|
|
92
62
|
};
|
|
93
63
|
|
|
64
|
+
const itemsForPath = (items, rel) =>
|
|
65
|
+
items.filter((item) => listPath(item) === rel);
|
|
66
|
+
|
|
94
67
|
const rowsFromAdds = (items) => {
|
|
95
68
|
const rows = [];
|
|
96
69
|
for (const item of items) {
|
|
97
|
-
|
|
98
|
-
for (const line of lines) {
|
|
99
|
-
if (line.type === 'add') rows.push(line.text
|
|
70
|
+
if (!item.hunk) continue;
|
|
71
|
+
for (const line of item.hunk.lines) {
|
|
72
|
+
if (line.type === 'add') rows.push(line.text);
|
|
100
73
|
}
|
|
101
74
|
}
|
|
102
75
|
return rows;
|
|
@@ -105,28 +78,28 @@ const rowsFromAdds = (items) => {
|
|
|
105
78
|
const unitLines = (text, items) => {
|
|
106
79
|
let rows = splitFile(text);
|
|
107
80
|
if (!rows.length) rows = rowsFromAdds(items);
|
|
108
|
-
const
|
|
109
|
-
|
|
81
|
+
const size = rows.length + 2;
|
|
82
|
+
const marks = {
|
|
83
|
+
dels: Array.from({ length: size }, () => []),
|
|
84
|
+
adds: new Array(size).fill(null),
|
|
85
|
+
};
|
|
86
|
+
for (const item of items) markHunk(marks, item);
|
|
110
87
|
const out = [];
|
|
111
88
|
for (let i = 0; i < rows.length; i++) {
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
const add = marks.adds[n];
|
|
89
|
+
for (const del of marks.dels[i + 1]) out.push(tagged('del', del.text, del));
|
|
90
|
+
const add = marks.adds[i + 1];
|
|
115
91
|
if (add) out.push(tagged('add', rows[i], add));
|
|
116
|
-
else out.push(tagged('ctx', rows[i],
|
|
92
|
+
else out.push(tagged('ctx', rows[i], NO_EXTRA));
|
|
117
93
|
}
|
|
118
94
|
for (const del of marks.dels[rows.length + 1]) {
|
|
119
95
|
out.push(tagged('del', del.text, del));
|
|
120
96
|
}
|
|
121
|
-
if (!out.length) out.push(tagged('ctx', '',
|
|
97
|
+
if (!out.length) out.push(tagged('ctx', '', NO_EXTRA));
|
|
122
98
|
return attachInline(out);
|
|
123
99
|
};
|
|
124
100
|
|
|
125
|
-
const sameBlock = (line, item) =>
|
|
126
|
-
|
|
127
|
-
if (line.item !== item) return false;
|
|
128
|
-
return line.blockId === item.blockId;
|
|
129
|
-
};
|
|
101
|
+
const sameBlock = (line, item) =>
|
|
102
|
+
line.item === item && line.blockId === item.blockId;
|
|
130
103
|
|
|
131
104
|
const changedLineRange = (lines, item, belongs) => {
|
|
132
105
|
let start = -1;
|
|
@@ -151,12 +124,11 @@ const hunkLineRange = (lines, item) =>
|
|
|
151
124
|
|
|
152
125
|
const fileOffset = (lines, at) => {
|
|
153
126
|
let offset = 0;
|
|
154
|
-
const target = Math.max(0, at);
|
|
155
127
|
for (let i = 0; i < lines.length; i++) {
|
|
156
128
|
const line = lines[i];
|
|
157
129
|
if (line.type === 'del') continue;
|
|
158
|
-
if (i >=
|
|
159
|
-
offset +=
|
|
130
|
+
if (i >= at) return offset;
|
|
131
|
+
offset += line.text.length + 1;
|
|
160
132
|
}
|
|
161
133
|
return offset;
|
|
162
134
|
};
|
|
@@ -173,37 +145,27 @@ const fileLineIndex = (lines, fileLine) => {
|
|
|
173
145
|
return last;
|
|
174
146
|
};
|
|
175
147
|
|
|
176
|
-
const lastEditableIndex = (lines) => {
|
|
177
|
-
for (let i = lines.length - 1; i >= 0; i--) {
|
|
178
|
-
if (lines[i].type !== 'del') return i;
|
|
179
|
-
}
|
|
180
|
-
return -1;
|
|
181
|
-
};
|
|
182
|
-
|
|
183
148
|
const attachEditSpans = (lines) => {
|
|
149
|
+
let lastAt = lines.length - 1;
|
|
150
|
+
while (lastAt >= 0 && lines[lastAt].type === 'del') lastAt -= 1;
|
|
184
151
|
let offset = 0;
|
|
185
|
-
const lastAt = lastEditableIndex(lines);
|
|
186
152
|
for (let i = 0; i < lines.length; i++) {
|
|
187
153
|
const line = lines[i];
|
|
188
154
|
if (line.type === 'del') continue;
|
|
189
|
-
const text = line.text ?? '';
|
|
190
|
-
const last = i === lastAt;
|
|
191
155
|
line.wrap = true;
|
|
192
156
|
line.editStart = offset;
|
|
193
|
-
line.editEnd = offset + text.length;
|
|
194
|
-
line.editLast =
|
|
195
|
-
offset =
|
|
157
|
+
line.editEnd = offset + line.text.length;
|
|
158
|
+
line.editLast = i === lastAt;
|
|
159
|
+
offset = line.editEnd + 1;
|
|
196
160
|
}
|
|
197
161
|
return lines;
|
|
198
162
|
};
|
|
199
163
|
|
|
200
|
-
const LCS_CELLS = 4000000;
|
|
201
|
-
|
|
202
164
|
const lcsTable = (prev, next) => {
|
|
203
165
|
const n = prev.length;
|
|
204
166
|
const m = next.length;
|
|
205
167
|
const dp = new Array(n + 1);
|
|
206
|
-
for (let i = 0; i <= n; i++) dp[i] = new
|
|
168
|
+
for (let i = 0; i <= n; i++) dp[i] = new Uint32Array(m + 1);
|
|
207
169
|
for (let i = n - 1; i >= 0; i--) {
|
|
208
170
|
for (let j = m - 1; j >= 0; j--) {
|
|
209
171
|
if (prev[i] === next[j]) dp[i][j] = dp[i + 1][j + 1] + 1;
|
|
@@ -215,44 +177,35 @@ const lcsTable = (prev, next) => {
|
|
|
215
177
|
|
|
216
178
|
const zipOps = (prev, next) => {
|
|
217
179
|
const n = Math.min(prev.length, next.length);
|
|
218
|
-
const ops =
|
|
219
|
-
let
|
|
220
|
-
for (let i =
|
|
221
|
-
for (let i = n; i < prev.length; i++) ops[at++] = { kind: 'del' };
|
|
180
|
+
const ops = [];
|
|
181
|
+
for (let i = 0; i < n; i++) ops.push({ kind: 'rep', text: next[i] });
|
|
182
|
+
for (let i = n; i < prev.length; i++) ops.push({ kind: 'del' });
|
|
222
183
|
for (let i = n; i < next.length; i++) {
|
|
223
|
-
ops
|
|
184
|
+
ops.push({ kind: 'ins', text: next[i] });
|
|
224
185
|
}
|
|
225
186
|
return ops;
|
|
226
187
|
};
|
|
227
188
|
|
|
228
|
-
const
|
|
229
|
-
const
|
|
230
|
-
|
|
189
|
+
const lcsOps = (prev, next) => {
|
|
190
|
+
const dp = lcsTable(prev, next);
|
|
191
|
+
const ops = [];
|
|
231
192
|
let i = 0;
|
|
232
193
|
let j = 0;
|
|
233
194
|
while (i < prev.length && j < next.length) {
|
|
234
195
|
if (prev[i] === next[j]) {
|
|
235
|
-
ops
|
|
196
|
+
ops.push({ kind: 'eq', text: next[j] });
|
|
236
197
|
i += 1;
|
|
237
198
|
j += 1;
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
if (dp[i + 1][j] >= dp[i][j + 1]) {
|
|
241
|
-
ops[n++] = { kind: 'del' };
|
|
199
|
+
} else if (dp[i + 1][j] >= dp[i][j + 1]) {
|
|
200
|
+
ops.push({ kind: 'del' });
|
|
242
201
|
i += 1;
|
|
243
|
-
|
|
202
|
+
} else {
|
|
203
|
+
ops.push({ kind: 'ins', text: next[j] });
|
|
204
|
+
j += 1;
|
|
244
205
|
}
|
|
245
|
-
ops[n++] = { kind: 'ins', text: next[j] };
|
|
246
|
-
j += 1;
|
|
247
|
-
}
|
|
248
|
-
while (i < prev.length) {
|
|
249
|
-
ops[n++] = { kind: 'del' };
|
|
250
|
-
i += 1;
|
|
251
|
-
}
|
|
252
|
-
while (j < next.length) {
|
|
253
|
-
ops[n++] = { kind: 'ins', text: next[j] };
|
|
254
|
-
j += 1;
|
|
255
206
|
}
|
|
207
|
+
for (; i < prev.length; i++) ops.push({ kind: 'del' });
|
|
208
|
+
for (; j < next.length; j++) ops.push({ kind: 'ins', text: next[j] });
|
|
256
209
|
return ops;
|
|
257
210
|
};
|
|
258
211
|
|
|
@@ -261,13 +214,10 @@ const collapseRep = (ops) => {
|
|
|
261
214
|
for (let i = 0; i < ops.length; i++) {
|
|
262
215
|
const op = ops[i];
|
|
263
216
|
const next = ops[i + 1];
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
}
|
|
269
|
-
if (op.kind === 'ins' && next && next.kind === 'del') {
|
|
270
|
-
out.push({ kind: 'rep', text: op.text });
|
|
217
|
+
const delIns = op.kind === 'del' && next?.kind === 'ins';
|
|
218
|
+
const insDel = op.kind === 'ins' && next?.kind === 'del';
|
|
219
|
+
if (delIns || insDel) {
|
|
220
|
+
out.push({ kind: 'rep', text: delIns ? next.text : op.text });
|
|
271
221
|
i += 1;
|
|
272
222
|
continue;
|
|
273
223
|
}
|
|
@@ -279,91 +229,44 @@ const collapseRep = (ops) => {
|
|
|
279
229
|
const lineOps = (prev, next) => {
|
|
280
230
|
if (prev.length === next.length) return zipOps(prev, next);
|
|
281
231
|
if (prev.length * next.length > LCS_CELLS) return zipOps(prev, next);
|
|
282
|
-
return collapseRep(
|
|
232
|
+
return collapseRep(lcsOps(prev, next));
|
|
283
233
|
};
|
|
284
234
|
|
|
285
|
-
const copyRow = (row, text) => ({
|
|
286
|
-
type: row.type,
|
|
287
|
-
text,
|
|
288
|
-
noNl: row.noNl === true,
|
|
289
|
-
blockId: row.blockId ?? null,
|
|
290
|
-
origin: row.origin ?? '',
|
|
291
|
-
item: row.item ?? null,
|
|
292
|
-
});
|
|
293
|
-
|
|
294
235
|
const pushDels = (out, base, at) => {
|
|
295
236
|
let i = at;
|
|
296
237
|
while (i < base.length && base[i].type === 'del') {
|
|
297
|
-
out.push(
|
|
238
|
+
out.push(tagged('del', base[i].text, base[i]));
|
|
298
239
|
i += 1;
|
|
299
240
|
}
|
|
300
241
|
return i;
|
|
301
242
|
};
|
|
302
243
|
|
|
303
|
-
const mergeEditRows = (
|
|
304
|
-
const
|
|
305
|
-
const texts = nextTexts && nextTexts.length ? nextTexts : [''];
|
|
244
|
+
const mergeEditRows = (base, nextTexts) => {
|
|
245
|
+
const texts = nextTexts.length ? nextTexts : [''];
|
|
306
246
|
const prev = [];
|
|
307
|
-
for (const row of base)
|
|
308
|
-
if (row.type === 'del') continue;
|
|
309
|
-
prev.push(row.text ?? '');
|
|
310
|
-
}
|
|
311
|
-
const ops = lineOps(prev, texts);
|
|
247
|
+
for (const row of base) if (row.type !== 'del') prev.push(row.text);
|
|
312
248
|
const out = [];
|
|
313
249
|
let at = 0;
|
|
314
|
-
for (const op of
|
|
250
|
+
for (const op of lineOps(prev, texts)) {
|
|
315
251
|
at = pushDels(out, base, at);
|
|
316
252
|
if (op.kind === 'ins') {
|
|
317
|
-
out.push(tagged('ctx', op.text,
|
|
253
|
+
out.push(tagged('ctx', op.text, NO_EXTRA));
|
|
318
254
|
continue;
|
|
319
255
|
}
|
|
320
256
|
if (at >= base.length) continue;
|
|
321
|
-
if (op.kind
|
|
322
|
-
at += 1;
|
|
323
|
-
continue;
|
|
324
|
-
}
|
|
325
|
-
out.push(copyRow(base[at], op.text));
|
|
257
|
+
if (op.kind !== 'del') out.push(tagged(base[at].type, op.text, base[at]));
|
|
326
258
|
at += 1;
|
|
327
259
|
}
|
|
328
260
|
pushDels(out, base, at);
|
|
329
|
-
if (!out.length) out.push(tagged('ctx', '',
|
|
261
|
+
if (!out.length) out.push(tagged('ctx', '', NO_EXTRA));
|
|
330
262
|
return attachEditSpans(attachInline(out));
|
|
331
263
|
};
|
|
332
264
|
|
|
333
|
-
const fileLines = (text) => {
|
|
334
|
-
const rows = splitFile(text);
|
|
335
|
-
if (!rows.length) rows.push('');
|
|
336
|
-
const { length } = rows;
|
|
337
|
-
const out = new Array(length);
|
|
338
|
-
let offset = 0;
|
|
339
|
-
for (let i = 0; i < length; i++) {
|
|
340
|
-
const row = rows[i];
|
|
341
|
-
const start = offset;
|
|
342
|
-
const end = start + row.length;
|
|
343
|
-
const last = i === length - 1;
|
|
344
|
-
out[i] = {
|
|
345
|
-
type: 'ctx',
|
|
346
|
-
text: row,
|
|
347
|
-
noNl: false,
|
|
348
|
-
wrap: true,
|
|
349
|
-
editStart: start,
|
|
350
|
-
editEnd: end,
|
|
351
|
-
editLast: last,
|
|
352
|
-
blockId: null,
|
|
353
|
-
origin: '',
|
|
354
|
-
item: null,
|
|
355
|
-
};
|
|
356
|
-
offset = last ? end : end + 1;
|
|
357
|
-
}
|
|
358
|
-
return out;
|
|
359
|
-
};
|
|
360
|
-
|
|
361
265
|
module.exports = {
|
|
362
266
|
splitFile,
|
|
363
267
|
splitEditor,
|
|
364
268
|
itemsForPath,
|
|
365
269
|
unitLines,
|
|
366
|
-
sameBlock,
|
|
367
270
|
sameHunk,
|
|
368
271
|
hunkKey,
|
|
369
272
|
blockLineRange,
|
|
@@ -371,7 +274,5 @@ module.exports = {
|
|
|
371
274
|
blockLineIndex,
|
|
372
275
|
fileOffset,
|
|
373
276
|
fileLineIndex,
|
|
374
|
-
attachEditSpans,
|
|
375
277
|
mergeEditRows,
|
|
376
|
-
fileLines,
|
|
377
278
|
};
|
package/lib/editor.js
CHANGED
|
@@ -6,6 +6,7 @@ const wrap = require('./wrap.js');
|
|
|
6
6
|
const { wrapMove } = wrap;
|
|
7
7
|
|
|
8
8
|
const UNDO_CAP = 100;
|
|
9
|
+
const WORD_RE = /[\p{L}\p{N}_]/u;
|
|
9
10
|
|
|
10
11
|
const graphemeIndex = (text, cursor) => {
|
|
11
12
|
const parts = graphemes(text);
|
|
@@ -24,10 +25,10 @@ const offsetAt = (parts, count) => {
|
|
|
24
25
|
return pos;
|
|
25
26
|
};
|
|
26
27
|
|
|
27
|
-
const WORD_RE = /[\p{L}\p{N}_]/u;
|
|
28
|
-
|
|
29
28
|
const isWordChar = (ch) => WORD_RE.test(ch);
|
|
30
29
|
|
|
30
|
+
const isSeparator = (ch) => !isWordChar(ch);
|
|
31
|
+
|
|
31
32
|
const skipWhile = (parts, at, dir, pred) => {
|
|
32
33
|
if (dir > 0) {
|
|
33
34
|
while (at < parts.length && pred(parts[at])) at += 1;
|
|
@@ -38,20 +39,10 @@ const skipWhile = (parts, at, dir, pred) => {
|
|
|
38
39
|
};
|
|
39
40
|
|
|
40
41
|
const wordIndex = (parts, at, dir) => {
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return skipWhile(parts, at, 1, word);
|
|
46
|
-
}
|
|
47
|
-
at = skipWhile(parts, at, 1, sep);
|
|
48
|
-
return skipWhile(parts, at, 1, word);
|
|
49
|
-
}
|
|
50
|
-
if (at > 0 && word(parts[at - 1])) {
|
|
51
|
-
return skipWhile(parts, at, -1, word);
|
|
52
|
-
}
|
|
53
|
-
at = skipWhile(parts, at, -1, sep);
|
|
54
|
-
return skipWhile(parts, at, -1, word);
|
|
42
|
+
const ahead = dir > 0 ? parts[at] : parts[at - 1];
|
|
43
|
+
const inWord = ahead !== undefined && isWordChar(ahead);
|
|
44
|
+
const start = inWord ? at : skipWhile(parts, at, dir, isSeparator);
|
|
45
|
+
return skipWhile(parts, start, dir, isWordChar);
|
|
55
46
|
};
|
|
56
47
|
|
|
57
48
|
class Editor {
|
|
@@ -72,17 +63,22 @@ class Editor {
|
|
|
72
63
|
restore(snap) {
|
|
73
64
|
this.text = snap.text;
|
|
74
65
|
this.cursor = snap.cursor;
|
|
75
|
-
this.anchor = snap.anchor
|
|
66
|
+
this.anchor = snap.anchor;
|
|
76
67
|
}
|
|
77
68
|
|
|
78
69
|
hasSelect() {
|
|
79
70
|
return this.anchor !== null && this.anchor !== this.cursor;
|
|
80
71
|
}
|
|
81
72
|
|
|
82
|
-
|
|
83
|
-
if (!this.hasSelect()) return
|
|
73
|
+
span() {
|
|
74
|
+
if (!this.hasSelect()) return { lo: this.cursor, hi: this.cursor };
|
|
84
75
|
const lo = Math.min(this.anchor, this.cursor);
|
|
85
76
|
const hi = Math.max(this.anchor, this.cursor);
|
|
77
|
+
return { lo, hi };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
selectedText() {
|
|
81
|
+
const { lo, hi } = this.span();
|
|
86
82
|
return this.text.slice(lo, hi);
|
|
87
83
|
}
|
|
88
84
|
|
|
@@ -95,26 +91,21 @@ class Editor {
|
|
|
95
91
|
}
|
|
96
92
|
|
|
97
93
|
collapse(delta) {
|
|
98
|
-
const lo =
|
|
99
|
-
const hi = Math.max(this.anchor, this.cursor);
|
|
94
|
+
const { lo, hi } = this.span();
|
|
100
95
|
this.anchor = null;
|
|
101
96
|
this.cursor = delta < 0 ? lo : hi;
|
|
102
97
|
}
|
|
103
98
|
|
|
104
99
|
removeSpan() {
|
|
105
100
|
if (!this.hasSelect()) return false;
|
|
106
|
-
const lo =
|
|
107
|
-
|
|
108
|
-
const next = this.text.slice(0, lo) + this.text.slice(hi);
|
|
109
|
-
this.edit(next, lo);
|
|
101
|
+
const { lo, hi } = this.span();
|
|
102
|
+
this.edit(this.text.slice(0, lo) + this.text.slice(hi), lo);
|
|
110
103
|
return true;
|
|
111
104
|
}
|
|
112
105
|
|
|
113
106
|
edit(nextText, nextCursor) {
|
|
114
107
|
this.undo.push(this.snapshot());
|
|
115
|
-
if (this.undo.length > UNDO_CAP)
|
|
116
|
-
this.undo = this.undo.slice(this.undo.length - UNDO_CAP);
|
|
117
|
-
}
|
|
108
|
+
if (this.undo.length > UNDO_CAP) this.undo.shift();
|
|
118
109
|
this.redo = [];
|
|
119
110
|
this.text = nextText;
|
|
120
111
|
this.cursor = nextCursor;
|
|
@@ -123,12 +114,9 @@ class Editor {
|
|
|
123
114
|
|
|
124
115
|
insert(chunk) {
|
|
125
116
|
this.wantCol = null;
|
|
126
|
-
const
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
const before = this.text.slice(0, lo);
|
|
130
|
-
const after = this.text.slice(hi);
|
|
131
|
-
this.edit(before + chunk + after, lo + chunk.length);
|
|
117
|
+
const { lo, hi } = this.span();
|
|
118
|
+
const next = this.text.slice(0, lo) + chunk + this.text.slice(hi);
|
|
119
|
+
this.edit(next, lo + chunk.length);
|
|
132
120
|
}
|
|
133
121
|
|
|
134
122
|
replace(text) {
|
|
@@ -137,29 +125,29 @@ class Editor {
|
|
|
137
125
|
}
|
|
138
126
|
|
|
139
127
|
place(offset) {
|
|
140
|
-
const next = Math.max(0, Math.min(offset, this.text.length));
|
|
141
128
|
this.wantCol = null;
|
|
142
129
|
this.anchor = null;
|
|
143
|
-
this.cursor =
|
|
130
|
+
this.cursor = Math.max(0, Math.min(offset, this.text.length));
|
|
144
131
|
}
|
|
145
132
|
|
|
146
133
|
backspace() {
|
|
147
134
|
this.wantCol = null;
|
|
148
135
|
if (this.removeSpan()) return;
|
|
149
|
-
const { parts, i } = graphemeIndex(this.text, this.cursor);
|
|
136
|
+
const { parts, i, pos } = graphemeIndex(this.text, this.cursor);
|
|
150
137
|
if (i === 0) return;
|
|
151
|
-
const
|
|
152
|
-
const
|
|
153
|
-
this.edit(
|
|
138
|
+
const size = parts[i - 1].length;
|
|
139
|
+
const next = this.text.slice(0, pos - size) + this.text.slice(pos);
|
|
140
|
+
this.edit(next, this.cursor - size);
|
|
154
141
|
}
|
|
155
142
|
|
|
156
143
|
delete() {
|
|
157
144
|
this.wantCol = null;
|
|
158
145
|
if (this.removeSpan()) return;
|
|
159
|
-
const { parts, i } = graphemeIndex(this.text, this.cursor);
|
|
146
|
+
const { parts, i, pos } = graphemeIndex(this.text, this.cursor);
|
|
160
147
|
if (i >= parts.length) return;
|
|
161
|
-
const
|
|
162
|
-
|
|
148
|
+
const next =
|
|
149
|
+
this.text.slice(0, pos) + this.text.slice(pos + parts[i].length);
|
|
150
|
+
this.edit(next, this.cursor);
|
|
163
151
|
}
|
|
164
152
|
|
|
165
153
|
move(delta, extend) {
|
|
@@ -183,15 +171,14 @@ class Editor {
|
|
|
183
171
|
|
|
184
172
|
linePos() {
|
|
185
173
|
const before = this.text.slice(0, this.cursor);
|
|
186
|
-
const at = before.lastIndexOf('\n');
|
|
187
174
|
const line = before.split('\n').length - 1;
|
|
188
|
-
const col =
|
|
175
|
+
const col = before.length - before.lastIndexOf('\n') - 1;
|
|
189
176
|
return { line, col };
|
|
190
177
|
}
|
|
191
178
|
|
|
192
179
|
visibleLineCol() {
|
|
193
180
|
const { col, line } = this.linePos();
|
|
194
|
-
const row = this.text.split('\n')[line]
|
|
181
|
+
const row = this.text.split('\n')[line];
|
|
195
182
|
return visibleWidth(row.slice(0, col));
|
|
196
183
|
}
|
|
197
184
|
|
|
@@ -203,60 +190,56 @@ class Editor {
|
|
|
203
190
|
}
|
|
204
191
|
|
|
205
192
|
moveLine(delta, width, extend) {
|
|
206
|
-
if (
|
|
207
|
-
else this.
|
|
193
|
+
if (extend) this.beginSelect();
|
|
194
|
+
else this.clearSelect();
|
|
208
195
|
if (width !== undefined) {
|
|
209
|
-
const
|
|
210
|
-
|
|
196
|
+
const moved = wrapMove(
|
|
197
|
+
this.text,
|
|
198
|
+
this.cursor,
|
|
199
|
+
width,
|
|
200
|
+
delta,
|
|
201
|
+
this.wantCol,
|
|
202
|
+
);
|
|
211
203
|
this.wantCol = moved.col;
|
|
212
204
|
this.cursor = moved.cursor;
|
|
213
205
|
return;
|
|
214
206
|
}
|
|
215
207
|
const rows = this.text.split('\n');
|
|
216
208
|
const { line, col } = this.linePos();
|
|
217
|
-
|
|
218
|
-
if (next < 0) next = 0;
|
|
219
|
-
if (next >= rows.length) next = rows.length - 1;
|
|
209
|
+
const next = Math.min(Math.max(line + delta, 0), rows.length - 1);
|
|
220
210
|
if (next === line) return;
|
|
221
|
-
const row = rows[next];
|
|
222
|
-
const c = Math.min(col, row.length);
|
|
223
211
|
let cursor = 0;
|
|
224
212
|
for (let i = 0; i < next; i++) cursor += rows[i].length + 1;
|
|
225
|
-
this.cursor = cursor +
|
|
213
|
+
this.cursor = cursor + Math.min(col, rows[next].length);
|
|
226
214
|
}
|
|
227
215
|
|
|
228
216
|
home(extend) {
|
|
229
217
|
this.wantCol = null;
|
|
230
|
-
if (
|
|
231
|
-
else this.
|
|
232
|
-
|
|
233
|
-
this.cursor -= col;
|
|
218
|
+
if (extend) this.beginSelect();
|
|
219
|
+
else this.clearSelect();
|
|
220
|
+
this.cursor -= this.linePos().col;
|
|
234
221
|
}
|
|
235
222
|
|
|
236
223
|
end(extend) {
|
|
237
224
|
this.wantCol = null;
|
|
238
|
-
if (
|
|
239
|
-
else this.
|
|
225
|
+
if (extend) this.beginSelect();
|
|
226
|
+
else this.clearSelect();
|
|
240
227
|
const { line, col } = this.linePos();
|
|
241
|
-
|
|
242
|
-
const row = rows[line] ?? '';
|
|
243
|
-
this.cursor += row.length - col;
|
|
228
|
+
this.cursor += this.text.split('\n')[line].length - col;
|
|
244
229
|
}
|
|
245
230
|
|
|
246
231
|
undoEdit() {
|
|
247
232
|
if (!this.undo.length) return;
|
|
248
233
|
this.wantCol = null;
|
|
249
|
-
const prev = this.undo.pop();
|
|
250
234
|
this.redo.push(this.snapshot());
|
|
251
|
-
this.restore(
|
|
235
|
+
this.restore(this.undo.pop());
|
|
252
236
|
}
|
|
253
237
|
|
|
254
238
|
redoEdit() {
|
|
255
239
|
if (!this.redo.length) return;
|
|
256
240
|
this.wantCol = null;
|
|
257
|
-
const next = this.redo.pop();
|
|
258
241
|
this.undo.push(this.snapshot());
|
|
259
|
-
this.restore(
|
|
242
|
+
this.restore(this.redo.pop());
|
|
260
243
|
}
|
|
261
244
|
}
|
|
262
245
|
|