UNPKG

4.79 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.applyFixes = void 0;
4const stableSort = require("stable");
5/**
6 * Tries to apply all fixes. The replacements of all fixes are sorted by index ascending.
7 * They are then applied in order. If a replacement overlaps (or touches) the range of the previous replacement,
8 * the process rolls back to the state before the first replacement of the offending fix was applied. The replacements
9 * of this fix are not applied again.
10 * At least one fix will be applied.
11 */
12function applyFixes(source, fixes) {
13 let fixed = fixes.length;
14 const replacements = [];
15 for (const fix of fixes) {
16 const state = { replacements: combineReplacements(fix.replacements), skip: false, state: undefined };
17 for (const replacement of state.replacements)
18 replacements.push({ fix: state, ...replacement });
19 }
20 const range = {
21 span: {
22 start: 0,
23 length: 0,
24 },
25 newLength: 0,
26 };
27 let output = '';
28 let position = -1;
29 replacements.sort(compareReplacements);
30 for (let i = 0; i < replacements.length; ++i) {
31 const replacement = replacements[i];
32 const { fix } = replacement;
33 if (fix.skip)
34 continue; // there was a conflict, don't use replacements of this fix
35 if (replacement.start <= position) {
36 // ranges overlap (or have touching boundaries) -> don't fix to prevent unspecified behavior
37 if (fix.state !== undefined) {
38 // rollback to state before the first replacement of the fix was applied
39 const rollbackToIndex = fix.state.index;
40 for (--i; i !== rollbackToIndex; --i) { // this automatically resets `i` to the correct position
41 const f = replacements[i].fix;
42 // we need to reset all states of fixes that applied their first replacement after
43 // the first replacement of the just rolled back fix
44 if (f.state !== undefined && f.state.index === i)
45 f.state = undefined;
46 // retry all rolled back fixes, that applied their first replacement after the just rolled back fix
47 // unfortunately this doesn't take conflicting fixes into account, so we may roll it back later
48 if (f.state === undefined && f.skip) {
49 ++fixed;
50 f.skip = false;
51 }
52 }
53 output = output.substring(0, fix.state.length);
54 position = fix.state.position;
55 }
56 fix.skip = true;
57 --fixed;
58 continue;
59 }
60 // save the current state to jump back if necessary
61 if (fix.state === undefined && fix.replacements.length !== 1)
62 fix.state = { position, index: i, length: output.length };
63 if (position === -1) {
64 // we are about to apply the first fix
65 range.span.start = replacement.start;
66 output = source.substring(0, replacement.start);
67 }
68 else {
69 output += source.substring(position, replacement.start);
70 }
71 output += replacement.text;
72 position = replacement.end;
73 }
74 output += source.substring(position);
75 range.span.length = position - range.span.start;
76 range.newLength = range.span.length + output.length - source.length;
77 return {
78 fixed,
79 range,
80 result: output,
81 };
82}
83exports.applyFixes = applyFixes;
84function compareReplacements(a, b) {
85 return a.start - b.start || a.end - b.end;
86}
87/** Combine adjacent replacements to avoid sorting replacements of other fixes between them. */
88function combineReplacements(replacements) {
89 if (replacements.length === 1)
90 return replacements;
91 // use a stable sorting algorithm to avoid shuffling insertions at the same position as these have the same start and end values
92 replacements = stableSort.inplace(replacements.slice(), compareReplacements);
93 const result = [];
94 let current = replacements[0];
95 for (let i = 1; i < replacements.length; ++i) {
96 const replacement = replacements[i];
97 if (current.end > replacement.start)
98 throw new Error('Replacements of fix overlap.');
99 if (current.end === replacement.start) {
100 current = {
101 start: current.start,
102 end: replacement.end,
103 text: current.text + replacement.text,
104 };
105 }
106 else {
107 result.push(current);
108 current = replacement;
109 }
110 }
111 result.push(current);
112 return result;
113}
114//# sourceMappingURL=fix.js.map
\No newline at end of file