@shapeshift-labs/frontier-lang-compiler 0.2.96 → 0.2.97
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.
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
|
|
2
2
|
import { idFragment, uniqueStrings } from '../../native-import-utils.js';
|
|
3
3
|
import { semanticEditIdentityFields } from './semanticEditIdentityRecords.js';
|
|
4
|
+
import { applySourceEdits, dedupeSourceEdits, validateSourceEdits } from './semanticSourceEditDedupe.js';
|
|
4
5
|
|
|
5
6
|
export function projectSemanticEditScriptToSource(input = {}) {
|
|
6
7
|
const script = input.script;
|
|
@@ -207,44 +208,6 @@ function semanticEditIdentity(operation) {
|
|
|
207
208
|
});
|
|
208
209
|
}
|
|
209
210
|
|
|
210
|
-
function applySourceEdits(sourceText, edits) {
|
|
211
|
-
return edits.filter((edit) => !edit.alreadyApplied)
|
|
212
|
-
.sort(sourceEditSort)
|
|
213
|
-
.reduce((text, edit) => text.slice(0, edit.start) + edit.replacement + text.slice(edit.end), sourceText);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
function dedupeSourceEdits(edits) {
|
|
217
|
-
const seen = new Map();
|
|
218
|
-
const result = [];
|
|
219
|
-
const skippedOperationIds = [];
|
|
220
|
-
for (const edit of edits) {
|
|
221
|
-
const key = duplicateEditKey(edit);
|
|
222
|
-
if (key && seen.has(key)) {
|
|
223
|
-
skippedOperationIds.push(edit.operationId);
|
|
224
|
-
continue;
|
|
225
|
-
}
|
|
226
|
-
if (key) seen.set(key, edit.operationId);
|
|
227
|
-
result.push(edit);
|
|
228
|
-
}
|
|
229
|
-
return { edits: result, skippedOperationIds };
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
function duplicateEditKey(edit) {
|
|
233
|
-
if (edit.editKind !== 'insert') return undefined;
|
|
234
|
-
return [
|
|
235
|
-
'insert',
|
|
236
|
-
edit.start,
|
|
237
|
-
edit.end,
|
|
238
|
-
edit.insertion?.mode,
|
|
239
|
-
edit.insertion?.anchorKey,
|
|
240
|
-
hashSemanticValue(edit.replacementSpanText ?? edit.replacement)
|
|
241
|
-
].join(':');
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
function sourceEditSort(left, right) {
|
|
245
|
-
return right.start - left.start || right.end - left.end || (right.order ?? 0) - (left.order ?? 0);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
211
|
function projectedSourcePath(script, edits) {
|
|
249
212
|
return edits.map((edit) => edit.sourcePath).find(Boolean) ?? script.sourcePath;
|
|
250
213
|
}
|
|
@@ -291,23 +254,6 @@ function afterLineOffset(sourceText, offset) {
|
|
|
291
254
|
return sourceText[offset] === '\n' ? offset + 1 : offset;
|
|
292
255
|
}
|
|
293
256
|
|
|
294
|
-
function validateSourceEdits(edits) {
|
|
295
|
-
const reasons = [];
|
|
296
|
-
const ordered = edits.filter((edit) => !edit.alreadyApplied).sort((left, right) => left.start - right.start || left.end - right.end);
|
|
297
|
-
for (let index = 1; index < ordered.length; index += 1) {
|
|
298
|
-
const previous = ordered[index - 1];
|
|
299
|
-
const current = ordered[index];
|
|
300
|
-
if (editsOverlap(previous, current)) reasons.push(`source-edit-overlap:${previous.operationId}:${current.operationId}`);
|
|
301
|
-
}
|
|
302
|
-
return uniqueStrings(reasons);
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
function editsOverlap(left, right) {
|
|
306
|
-
if (left.start === left.end) return right.start < left.start && left.start < right.end;
|
|
307
|
-
if (right.start === right.end) return left.start < right.start && right.start < left.end;
|
|
308
|
-
return left.start < right.end && right.start < left.end;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
257
|
function compactRecord(value) {
|
|
312
258
|
return Object.fromEntries(Object.entries(value ?? {}).filter(([, entry]) => entry !== undefined && (!Array.isArray(entry) || entry.length > 0)));
|
|
313
259
|
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
|
|
2
|
+
import { uniqueStrings } from '../../native-import-utils.js';
|
|
3
|
+
|
|
4
|
+
export function applySourceEdits(sourceText, edits) {
|
|
5
|
+
return edits.filter((edit) => !edit.alreadyApplied)
|
|
6
|
+
.sort(sourceEditSort)
|
|
7
|
+
.reduce((text, edit) => text.slice(0, edit.start) + edit.replacement + text.slice(edit.end), sourceText);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function dedupeSourceEdits(edits) {
|
|
11
|
+
const exact = dedupeExactInsertions(edits);
|
|
12
|
+
const covered = removeCoveredContainerReplacements(exact.edits);
|
|
13
|
+
return {
|
|
14
|
+
edits: covered.edits,
|
|
15
|
+
skippedOperationIds: [...exact.skippedOperationIds, ...covered.skippedOperationIds]
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function validateSourceEdits(edits) {
|
|
20
|
+
const reasons = [];
|
|
21
|
+
const ordered = edits.filter((edit) => !edit.alreadyApplied).sort((left, right) => left.start - right.start || left.end - right.end);
|
|
22
|
+
for (let index = 1; index < ordered.length; index += 1) {
|
|
23
|
+
const previous = ordered[index - 1];
|
|
24
|
+
const current = ordered[index];
|
|
25
|
+
if (editsOverlap(previous, current)) reasons.push(`source-edit-overlap:${previous.operationId}:${current.operationId}`);
|
|
26
|
+
}
|
|
27
|
+
return uniqueStrings(reasons);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function dedupeExactInsertions(edits) {
|
|
31
|
+
const seen = new Map();
|
|
32
|
+
const result = [];
|
|
33
|
+
const skippedOperationIds = [];
|
|
34
|
+
for (const edit of edits) {
|
|
35
|
+
const key = duplicateEditKey(edit);
|
|
36
|
+
if (key && seen.has(key)) {
|
|
37
|
+
skippedOperationIds.push(edit.operationId);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (key) seen.set(key, edit.operationId);
|
|
41
|
+
result.push(edit);
|
|
42
|
+
}
|
|
43
|
+
return { edits: result, skippedOperationIds };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function removeCoveredContainerReplacements(edits) {
|
|
47
|
+
const skippedOperationIds = [];
|
|
48
|
+
const result = [];
|
|
49
|
+
for (const edit of edits) {
|
|
50
|
+
if (containerReplacementCoveredByInsertions(edit, edits)) {
|
|
51
|
+
skippedOperationIds.push(edit.operationId);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
result.push(edit);
|
|
55
|
+
}
|
|
56
|
+
return { edits: result, skippedOperationIds };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function containerReplacementCoveredByInsertions(edit, edits) {
|
|
60
|
+
if (edit.editKind !== 'replace' || edit.alreadyApplied) return false;
|
|
61
|
+
const insertions = containedInsertions(edit, edits);
|
|
62
|
+
if (!insertions.length) return false;
|
|
63
|
+
const localEdits = insertions.map((insertion) => ({
|
|
64
|
+
...insertion,
|
|
65
|
+
start: insertion.start - edit.start,
|
|
66
|
+
end: insertion.end - edit.start
|
|
67
|
+
}));
|
|
68
|
+
return applySourceEdits(edit.current, localEdits) === edit.replacement;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function containedInsertions(container, edits) {
|
|
72
|
+
return edits
|
|
73
|
+
.filter((edit) => edit.editKind === 'insert' && !edit.alreadyApplied)
|
|
74
|
+
.filter((edit) => edit.operationId !== container.operationId)
|
|
75
|
+
.filter((edit) => container.start <= edit.start && edit.end <= container.end)
|
|
76
|
+
.sort((left, right) => left.start - right.start || (left.order ?? 0) - (right.order ?? 0));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function duplicateEditKey(edit) {
|
|
80
|
+
if (edit.editKind !== 'insert') return undefined;
|
|
81
|
+
return [
|
|
82
|
+
'insert',
|
|
83
|
+
edit.start,
|
|
84
|
+
edit.end,
|
|
85
|
+
edit.insertion?.mode,
|
|
86
|
+
edit.insertion?.anchorKey,
|
|
87
|
+
hashSemanticValue(edit.replacementSpanText ?? edit.replacement)
|
|
88
|
+
].join(':');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function sourceEditSort(left, right) {
|
|
92
|
+
return right.start - left.start || right.end - left.end || (right.order ?? 0) - (left.order ?? 0);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function editsOverlap(left, right) {
|
|
96
|
+
if (left.start === left.end) return right.start < left.start && left.start < right.end;
|
|
97
|
+
if (right.start === right.end) return left.start < right.start && right.start < left.end;
|
|
98
|
+
return left.start < right.end && right.start < left.end;
|
|
99
|
+
}
|
|
@@ -160,11 +160,16 @@ function jsDeclarationWithSourceSpan(input, declaration, lines) {
|
|
|
160
160
|
startLine,
|
|
161
161
|
endLine,
|
|
162
162
|
startColumn: declaration.span?.startColumn ?? 1,
|
|
163
|
-
endColumn: declaration
|
|
163
|
+
endColumn: declarationEndColumn(declaration, startLine, endLine, endLineText)
|
|
164
164
|
}
|
|
165
165
|
};
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
function declarationEndColumn(declaration, startLine, endLine, endLineText) {
|
|
169
|
+
if (endLine !== startLine) return endLineText.length + 1;
|
|
170
|
+
return declaration.span?.endColumn ?? endLineText.length + 1;
|
|
171
|
+
}
|
|
172
|
+
|
|
168
173
|
function jsBalancedDeclarationEndLine(input, lines, startLine) {
|
|
169
174
|
const state = { inBlockComment: false, inTemplateString: false };
|
|
170
175
|
let depth = 0;
|
package/package.json
CHANGED