wyreframe 0.3.0 → 0.4.1
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/dist/index.d.ts +65 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +82 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +132 -0
- package/src/parser/Fixer/FixTypes.mjs +2 -0
- package/src/parser/Fixer/FixTypes.res +34 -0
- package/src/parser/Fixer/Fixer.gen.tsx +24 -0
- package/src/parser/Fixer/Fixer.mjs +452 -0
- package/src/parser/Fixer/Fixer.res +588 -0
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
// Fixer.res
|
|
2
|
+
// Auto-fix functionality for common wireframe errors and warnings
|
|
3
|
+
|
|
4
|
+
open FixTypes
|
|
5
|
+
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// String Manipulation Helpers
|
|
8
|
+
// ============================================================================
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Split text into lines (preserving empty lines)
|
|
12
|
+
*/
|
|
13
|
+
let splitLines = (text: string): array<string> => {
|
|
14
|
+
text->String.split("\n")
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Join lines back into text
|
|
19
|
+
*/
|
|
20
|
+
let joinLines = (lines: array<string>): string => {
|
|
21
|
+
lines->Array.join("\n")
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Get a specific line from text (0-indexed)
|
|
26
|
+
*/
|
|
27
|
+
let getLine = (lines: array<string>, row: int): option<string> => {
|
|
28
|
+
lines->Array.get(row)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Replace a specific line in the array (0-indexed)
|
|
33
|
+
*/
|
|
34
|
+
let replaceLine = (lines: array<string>, row: int, newLine: string): array<string> => {
|
|
35
|
+
lines->Array.mapWithIndex((line, idx) => {
|
|
36
|
+
if idx === row {
|
|
37
|
+
newLine
|
|
38
|
+
} else {
|
|
39
|
+
line
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Insert characters at a specific position in a string
|
|
46
|
+
*/
|
|
47
|
+
let insertAt = (str: string, col: int, chars: string): string => {
|
|
48
|
+
let before = str->String.slice(~start=0, ~end=col)
|
|
49
|
+
let after = str->String.sliceToEnd(~start=col)
|
|
50
|
+
before ++ chars ++ after
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Remove characters at a specific position in a string
|
|
55
|
+
*/
|
|
56
|
+
let removeAt = (str: string, col: int, count: int): string => {
|
|
57
|
+
let before = str->String.slice(~start=0, ~end=col)
|
|
58
|
+
let after = str->String.sliceToEnd(~start=col + count)
|
|
59
|
+
before ++ after
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Replace a character at a specific position
|
|
64
|
+
*/
|
|
65
|
+
let replaceCharAt = (str: string, col: int, char: string): string => {
|
|
66
|
+
let before = str->String.slice(~start=0, ~end=col)
|
|
67
|
+
let after = str->String.sliceToEnd(~start=col + 1)
|
|
68
|
+
before ++ char ++ after
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ============================================================================
|
|
72
|
+
// Fix Strategies for Each Error Type
|
|
73
|
+
// ============================================================================
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Fix MisalignedPipe - adjust column position of '|' character
|
|
77
|
+
*
|
|
78
|
+
* Before: | content | (pipe at wrong column)
|
|
79
|
+
* After: | content | (pipe at correct column)
|
|
80
|
+
*
|
|
81
|
+
* Note: position.row is 1-indexed (from error messages), convert to 0-indexed for array access
|
|
82
|
+
*/
|
|
83
|
+
let fixMisalignedPipe = (text: string, error: ErrorTypes.t): option<(string, fixedIssue)> => {
|
|
84
|
+
switch error.code {
|
|
85
|
+
| MisalignedPipe({position, expectedCol, actualCol}) => {
|
|
86
|
+
let lines = splitLines(text)
|
|
87
|
+
// Convert 1-indexed row to 0-indexed for array access
|
|
88
|
+
let rowIndex = position.row - 1
|
|
89
|
+
|
|
90
|
+
switch getLine(lines, rowIndex) {
|
|
91
|
+
| None => None
|
|
92
|
+
| Some(line) => {
|
|
93
|
+
// Calculate how many spaces to add or remove
|
|
94
|
+
let diff = expectedCol - actualCol
|
|
95
|
+
|
|
96
|
+
let newLine = if diff > 0 {
|
|
97
|
+
// Need to add spaces before the pipe
|
|
98
|
+
insertAt(line, actualCol, String.repeat(" ", diff))
|
|
99
|
+
} else {
|
|
100
|
+
// Need to remove spaces before the pipe
|
|
101
|
+
let removeCount = Math.Int.abs(diff)
|
|
102
|
+
// Make sure we're removing spaces, not content
|
|
103
|
+
let beforePipe = line->String.slice(~start=actualCol + diff, ~end=actualCol)
|
|
104
|
+
if beforePipe->String.trim === "" {
|
|
105
|
+
removeAt(line, actualCol + diff, removeCount)
|
|
106
|
+
} else {
|
|
107
|
+
line // Can't safely remove non-space characters
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if newLine !== line {
|
|
112
|
+
let newLines = replaceLine(lines, rowIndex, newLine)
|
|
113
|
+
let fixedText = joinLines(newLines)
|
|
114
|
+
|
|
115
|
+
Some((
|
|
116
|
+
fixedText,
|
|
117
|
+
{
|
|
118
|
+
original: error,
|
|
119
|
+
description: `Aligned pipe at line ${Int.toString(position.row)} to column ${Int.toString(expectedCol + 1)}`,
|
|
120
|
+
line: position.row,
|
|
121
|
+
column: expectedCol + 1,
|
|
122
|
+
},
|
|
123
|
+
))
|
|
124
|
+
} else {
|
|
125
|
+
None
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
| _ => None
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Fix MisalignedClosingBorder - adjust closing '|' position
|
|
136
|
+
*
|
|
137
|
+
* Note: position.row is 1-indexed (from error messages), convert to 0-indexed for array access
|
|
138
|
+
*/
|
|
139
|
+
let fixMisalignedClosingBorder = (text: string, error: ErrorTypes.t): option<(string, fixedIssue)> => {
|
|
140
|
+
switch error.code {
|
|
141
|
+
| MisalignedClosingBorder({position, expectedCol, actualCol}) => {
|
|
142
|
+
let lines = splitLines(text)
|
|
143
|
+
// Convert 1-indexed row to 0-indexed for array access
|
|
144
|
+
let rowIndex = position.row - 1
|
|
145
|
+
|
|
146
|
+
switch getLine(lines, rowIndex) {
|
|
147
|
+
| None => None
|
|
148
|
+
| Some(line) => {
|
|
149
|
+
let diff = expectedCol - actualCol
|
|
150
|
+
|
|
151
|
+
let newLine = if diff > 0 {
|
|
152
|
+
// Need to add spaces before the closing pipe
|
|
153
|
+
insertAt(line, actualCol, String.repeat(" ", diff))
|
|
154
|
+
} else {
|
|
155
|
+
// Need to remove spaces before the closing pipe
|
|
156
|
+
let removeCount = Math.Int.abs(diff)
|
|
157
|
+
let beforePipe = line->String.slice(~start=actualCol + diff, ~end=actualCol)
|
|
158
|
+
if beforePipe->String.trim === "" {
|
|
159
|
+
removeAt(line, actualCol + diff, removeCount)
|
|
160
|
+
} else {
|
|
161
|
+
line
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if newLine !== line {
|
|
166
|
+
let newLines = replaceLine(lines, rowIndex, newLine)
|
|
167
|
+
let fixedText = joinLines(newLines)
|
|
168
|
+
|
|
169
|
+
Some((
|
|
170
|
+
fixedText,
|
|
171
|
+
{
|
|
172
|
+
original: error,
|
|
173
|
+
description: `Aligned closing border at line ${Int.toString(position.row)} to column ${Int.toString(expectedCol + 1)}`,
|
|
174
|
+
line: position.row,
|
|
175
|
+
column: expectedCol + 1,
|
|
176
|
+
},
|
|
177
|
+
))
|
|
178
|
+
} else {
|
|
179
|
+
None
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
| _ => None
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Fix UnusualSpacing - replace tabs with spaces
|
|
190
|
+
*
|
|
191
|
+
* Note: position.row is 1-indexed (from error messages), convert to 0-indexed for array access
|
|
192
|
+
*/
|
|
193
|
+
let fixUnusualSpacing = (text: string, error: ErrorTypes.t): option<(string, fixedIssue)> => {
|
|
194
|
+
switch error.code {
|
|
195
|
+
| UnusualSpacing({position, issue}) => {
|
|
196
|
+
// Check if the issue is about tabs
|
|
197
|
+
if issue->String.includes("tab") || issue->String.includes("Tab") {
|
|
198
|
+
let lines = splitLines(text)
|
|
199
|
+
// Convert 1-indexed row to 0-indexed for array access
|
|
200
|
+
let rowIndex = position.row - 1
|
|
201
|
+
|
|
202
|
+
switch getLine(lines, rowIndex) {
|
|
203
|
+
| None => None
|
|
204
|
+
| Some(line) => {
|
|
205
|
+
// Replace tabs with 2 spaces (common convention)
|
|
206
|
+
let newLine = line->String.replaceAll("\t", " ")
|
|
207
|
+
|
|
208
|
+
if newLine !== line {
|
|
209
|
+
let newLines = replaceLine(lines, rowIndex, newLine)
|
|
210
|
+
let fixedText = joinLines(newLines)
|
|
211
|
+
|
|
212
|
+
Some((
|
|
213
|
+
fixedText,
|
|
214
|
+
{
|
|
215
|
+
original: error,
|
|
216
|
+
description: `Replaced tabs with spaces at line ${Int.toString(position.row)}`,
|
|
217
|
+
line: position.row,
|
|
218
|
+
column: position.col + 1,
|
|
219
|
+
},
|
|
220
|
+
))
|
|
221
|
+
} else {
|
|
222
|
+
None
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
} else {
|
|
227
|
+
None
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
| _ => None
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Fix UnclosedBracket - add closing ']' at end of line
|
|
236
|
+
*
|
|
237
|
+
* Note: opening.row is 1-indexed (from error messages), convert to 0-indexed for array access
|
|
238
|
+
*/
|
|
239
|
+
let fixUnclosedBracket = (text: string, error: ErrorTypes.t): option<(string, fixedIssue)> => {
|
|
240
|
+
switch error.code {
|
|
241
|
+
| UnclosedBracket({opening}) => {
|
|
242
|
+
let lines = splitLines(text)
|
|
243
|
+
// Convert 1-indexed row to 0-indexed for array access
|
|
244
|
+
let rowIndex = opening.row - 1
|
|
245
|
+
|
|
246
|
+
switch getLine(lines, rowIndex) {
|
|
247
|
+
| None => None
|
|
248
|
+
| Some(line) => {
|
|
249
|
+
// Find the content after '[' and close it
|
|
250
|
+
let trimmedLine = line->String.trimEnd
|
|
251
|
+
|
|
252
|
+
// Only add ']' if the line doesn't already end with it
|
|
253
|
+
if !(trimmedLine->String.endsWith("]")) {
|
|
254
|
+
// Add ' ]' with proper spacing
|
|
255
|
+
let newLine = trimmedLine ++ " ]"
|
|
256
|
+
let newLines = replaceLine(lines, rowIndex, newLine)
|
|
257
|
+
let fixedText = joinLines(newLines)
|
|
258
|
+
|
|
259
|
+
Some((
|
|
260
|
+
fixedText,
|
|
261
|
+
{
|
|
262
|
+
original: error,
|
|
263
|
+
description: `Added closing bracket at line ${Int.toString(opening.row)}`,
|
|
264
|
+
line: opening.row,
|
|
265
|
+
column: String.length(trimmedLine) + 2,
|
|
266
|
+
},
|
|
267
|
+
))
|
|
268
|
+
} else {
|
|
269
|
+
None
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
| _ => None
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Fix MismatchedWidth - extend the shorter border to match the longer one
|
|
280
|
+
*
|
|
281
|
+
* Note: topLeft.row is 1-indexed (from error messages), convert to 0-indexed for array access
|
|
282
|
+
*/
|
|
283
|
+
let fixMismatchedWidth = (text: string, error: ErrorTypes.t): option<(string, fixedIssue)> => {
|
|
284
|
+
switch error.code {
|
|
285
|
+
| MismatchedWidth({topLeft, topWidth, bottomWidth}) => {
|
|
286
|
+
let lines = splitLines(text)
|
|
287
|
+
let diff = topWidth - bottomWidth
|
|
288
|
+
// Convert 1-indexed row to 0-indexed for array access
|
|
289
|
+
let topLeftRowIndex = topLeft.row - 1
|
|
290
|
+
|
|
291
|
+
if diff === 0 {
|
|
292
|
+
None
|
|
293
|
+
} else {
|
|
294
|
+
// Find the bottom border line
|
|
295
|
+
// We need to trace down from topLeft to find the bottom
|
|
296
|
+
// Note: row here is 0-indexed array index
|
|
297
|
+
let rec findBottomRow = (row: int): option<int> => {
|
|
298
|
+
if row >= Array.length(lines) {
|
|
299
|
+
None
|
|
300
|
+
} else {
|
|
301
|
+
switch getLine(lines, row) {
|
|
302
|
+
| None => None
|
|
303
|
+
| Some(line) => {
|
|
304
|
+
// Check if this line has a '+' at the same column as topLeft
|
|
305
|
+
let col = topLeft.col
|
|
306
|
+
if col < String.length(line) {
|
|
307
|
+
let char = line->String.charAt(col)
|
|
308
|
+
if char === "+" && row > topLeftRowIndex {
|
|
309
|
+
Some(row)
|
|
310
|
+
} else {
|
|
311
|
+
findBottomRow(row + 1)
|
|
312
|
+
}
|
|
313
|
+
} else {
|
|
314
|
+
findBottomRow(row + 1)
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
switch findBottomRow(topLeftRowIndex + 1) {
|
|
322
|
+
| None => None
|
|
323
|
+
| Some(bottomRowIndex) => {
|
|
324
|
+
switch getLine(lines, bottomRowIndex) {
|
|
325
|
+
| None => None
|
|
326
|
+
| Some(bottomLine) => {
|
|
327
|
+
if diff > 0 {
|
|
328
|
+
// Bottom is shorter, need to extend it
|
|
329
|
+
// Find the closing '+' and add dashes before it
|
|
330
|
+
let closingPlusCol = topLeft.col + bottomWidth - 1
|
|
331
|
+
if closingPlusCol >= 0 && closingPlusCol < String.length(bottomLine) {
|
|
332
|
+
let before = bottomLine->String.slice(~start=0, ~end=closingPlusCol)
|
|
333
|
+
let after = bottomLine->String.sliceToEnd(~start=closingPlusCol)
|
|
334
|
+
let dashes = String.repeat("-", diff)
|
|
335
|
+
let newLine = before ++ dashes ++ after
|
|
336
|
+
|
|
337
|
+
let newLines = replaceLine(lines, bottomRowIndex, newLine)
|
|
338
|
+
let fixedText = joinLines(newLines)
|
|
339
|
+
|
|
340
|
+
Some((
|
|
341
|
+
fixedText,
|
|
342
|
+
{
|
|
343
|
+
original: error,
|
|
344
|
+
description: `Extended bottom border at line ${Int.toString(bottomRowIndex + 1)} by ${Int.toString(diff)} characters`,
|
|
345
|
+
line: bottomRowIndex + 1,
|
|
346
|
+
column: closingPlusCol + 1,
|
|
347
|
+
},
|
|
348
|
+
))
|
|
349
|
+
} else {
|
|
350
|
+
None
|
|
351
|
+
}
|
|
352
|
+
} else {
|
|
353
|
+
// Top is shorter, need to extend it
|
|
354
|
+
// This is trickier as it affects content alignment
|
|
355
|
+
// For now, we extend the top border
|
|
356
|
+
switch getLine(lines, topLeftRowIndex) {
|
|
357
|
+
| None => None
|
|
358
|
+
| Some(topLine) => {
|
|
359
|
+
let closingPlusCol = topLeft.col + topWidth - 1
|
|
360
|
+
if closingPlusCol >= 0 && closingPlusCol < String.length(topLine) {
|
|
361
|
+
let before = topLine->String.slice(~start=0, ~end=closingPlusCol)
|
|
362
|
+
let after = topLine->String.sliceToEnd(~start=closingPlusCol)
|
|
363
|
+
let dashes = String.repeat("-", Math.Int.abs(diff))
|
|
364
|
+
let newLine = before ++ dashes ++ after
|
|
365
|
+
|
|
366
|
+
let newLines = replaceLine(lines, topLeftRowIndex, newLine)
|
|
367
|
+
let fixedText = joinLines(newLines)
|
|
368
|
+
|
|
369
|
+
Some((
|
|
370
|
+
fixedText,
|
|
371
|
+
{
|
|
372
|
+
original: error,
|
|
373
|
+
description: `Extended top border at line ${Int.toString(topLeft.row)} by ${Int.toString(Math.Int.abs(diff))} characters`,
|
|
374
|
+
line: topLeft.row,
|
|
375
|
+
column: closingPlusCol + 1,
|
|
376
|
+
},
|
|
377
|
+
))
|
|
378
|
+
} else {
|
|
379
|
+
None
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
| _ => None
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// ============================================================================
|
|
395
|
+
// Main Fix Function
|
|
396
|
+
// ============================================================================
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* List of all fix strategies in order of application
|
|
400
|
+
*/
|
|
401
|
+
let fixStrategies: array<(string, ErrorTypes.errorCode => bool, (string, ErrorTypes.t) => option<(string, fixedIssue)>)> = [
|
|
402
|
+
("MisalignedPipe", code => {
|
|
403
|
+
switch code {
|
|
404
|
+
| MisalignedPipe(_) => true
|
|
405
|
+
| _ => false
|
|
406
|
+
}
|
|
407
|
+
}, fixMisalignedPipe),
|
|
408
|
+
("MisalignedClosingBorder", code => {
|
|
409
|
+
switch code {
|
|
410
|
+
| MisalignedClosingBorder(_) => true
|
|
411
|
+
| _ => false
|
|
412
|
+
}
|
|
413
|
+
}, fixMisalignedClosingBorder),
|
|
414
|
+
("UnusualSpacing", code => {
|
|
415
|
+
switch code {
|
|
416
|
+
| UnusualSpacing(_) => true
|
|
417
|
+
| _ => false
|
|
418
|
+
}
|
|
419
|
+
}, fixUnusualSpacing),
|
|
420
|
+
("UnclosedBracket", code => {
|
|
421
|
+
switch code {
|
|
422
|
+
| UnclosedBracket(_) => true
|
|
423
|
+
| _ => false
|
|
424
|
+
}
|
|
425
|
+
}, fixUnclosedBracket),
|
|
426
|
+
("MismatchedWidth", code => {
|
|
427
|
+
switch code {
|
|
428
|
+
| MismatchedWidth(_) => true
|
|
429
|
+
| _ => false
|
|
430
|
+
}
|
|
431
|
+
}, fixMismatchedWidth),
|
|
432
|
+
]
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Try to fix a single error using available strategies
|
|
436
|
+
*/
|
|
437
|
+
let tryFixError = (text: string, error: ErrorTypes.t): option<(string, fixedIssue)> => {
|
|
438
|
+
// Find the first strategy that can fix this error
|
|
439
|
+
fixStrategies->Array.reduce(None, (acc, (_, canFix, apply)) => {
|
|
440
|
+
switch acc {
|
|
441
|
+
| Some(_) => acc // Already found a fix
|
|
442
|
+
| None => {
|
|
443
|
+
if canFix(error.code) {
|
|
444
|
+
apply(text, error)
|
|
445
|
+
} else {
|
|
446
|
+
None
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
})
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Check if an error code is fixable
|
|
455
|
+
*/
|
|
456
|
+
let isFixable = (code: ErrorTypes.errorCode): bool => {
|
|
457
|
+
fixStrategies->Array.some(((_, canFix, _)) => canFix(code))
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Main fix function - attempts to fix all errors in the text
|
|
462
|
+
*
|
|
463
|
+
* This function:
|
|
464
|
+
* 1. Parses the text to get errors/warnings
|
|
465
|
+
* 2. Attempts to fix each fixable error
|
|
466
|
+
* 3. Re-parses after each fix to get updated positions
|
|
467
|
+
* 4. Returns the fixed text and list of applied fixes
|
|
468
|
+
*
|
|
469
|
+
* @param text The wireframe markdown text
|
|
470
|
+
* @returns FixResult with fixed text and details
|
|
471
|
+
*/
|
|
472
|
+
@genType
|
|
473
|
+
let fix = (text: string): fixResult => {
|
|
474
|
+
// Maximum iterations to prevent infinite loops
|
|
475
|
+
let maxIterations = 100
|
|
476
|
+
|
|
477
|
+
// Recursive fix loop
|
|
478
|
+
let rec fixLoop = (currentText: string, fixedSoFar: array<fixedIssue>, iteration: int): fixResult => {
|
|
479
|
+
if iteration >= maxIterations {
|
|
480
|
+
// Too many iterations, return what we have
|
|
481
|
+
Ok({
|
|
482
|
+
text: currentText,
|
|
483
|
+
fixed: fixedSoFar,
|
|
484
|
+
remaining: [],
|
|
485
|
+
})
|
|
486
|
+
} else {
|
|
487
|
+
// Parse current text to get errors
|
|
488
|
+
let parseResult = Parser.parse(currentText)
|
|
489
|
+
|
|
490
|
+
switch parseResult {
|
|
491
|
+
| Ok((_ast, warnings)) => {
|
|
492
|
+
// Parse succeeded, only warnings remain
|
|
493
|
+
// Try to fix warnings
|
|
494
|
+
let fixableWarnings = warnings->Array.filter(w => isFixable(w.code))
|
|
495
|
+
|
|
496
|
+
if Array.length(fixableWarnings) === 0 {
|
|
497
|
+
// No more fixable issues
|
|
498
|
+
Ok({
|
|
499
|
+
text: currentText,
|
|
500
|
+
fixed: fixedSoFar,
|
|
501
|
+
remaining: warnings->Array.filter(w => !isFixable(w.code)),
|
|
502
|
+
})
|
|
503
|
+
} else {
|
|
504
|
+
// Try to fix the first fixable warning
|
|
505
|
+
switch fixableWarnings->Array.get(0) {
|
|
506
|
+
| None => Ok({
|
|
507
|
+
text: currentText,
|
|
508
|
+
fixed: fixedSoFar,
|
|
509
|
+
remaining: warnings,
|
|
510
|
+
})
|
|
511
|
+
| Some(warning) => {
|
|
512
|
+
switch tryFixError(currentText, warning) {
|
|
513
|
+
| None => {
|
|
514
|
+
// Couldn't fix, move on
|
|
515
|
+
Ok({
|
|
516
|
+
text: currentText,
|
|
517
|
+
fixed: fixedSoFar,
|
|
518
|
+
remaining: warnings,
|
|
519
|
+
})
|
|
520
|
+
}
|
|
521
|
+
| Some((newText, fixedIssue)) => {
|
|
522
|
+
// Fixed! Continue with remaining
|
|
523
|
+
let newFixed = fixedSoFar->Array.concat([fixedIssue])
|
|
524
|
+
fixLoop(newText, newFixed, iteration + 1)
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
| Error(errors) => {
|
|
532
|
+
// Parse failed, try to fix errors
|
|
533
|
+
let fixableErrors = errors->Array.filter(e => isFixable(e.code))
|
|
534
|
+
|
|
535
|
+
if Array.length(fixableErrors) === 0 {
|
|
536
|
+
// No fixable errors, return what we have
|
|
537
|
+
Ok({
|
|
538
|
+
text: currentText,
|
|
539
|
+
fixed: fixedSoFar,
|
|
540
|
+
remaining: errors->Array.filter(e => !isFixable(e.code)),
|
|
541
|
+
})
|
|
542
|
+
} else {
|
|
543
|
+
// Try to fix the first fixable error
|
|
544
|
+
switch fixableErrors->Array.get(0) {
|
|
545
|
+
| None => Ok({
|
|
546
|
+
text: currentText,
|
|
547
|
+
fixed: fixedSoFar,
|
|
548
|
+
remaining: errors,
|
|
549
|
+
})
|
|
550
|
+
| Some(error) => {
|
|
551
|
+
switch tryFixError(currentText, error) {
|
|
552
|
+
| None => {
|
|
553
|
+
// Couldn't fix, return remaining errors
|
|
554
|
+
Ok({
|
|
555
|
+
text: currentText,
|
|
556
|
+
fixed: fixedSoFar,
|
|
557
|
+
remaining: errors,
|
|
558
|
+
})
|
|
559
|
+
}
|
|
560
|
+
| Some((newText, fixedIssue)) => {
|
|
561
|
+
// Fixed! Continue fixing
|
|
562
|
+
let newFixed = fixedSoFar->Array.concat([fixedIssue])
|
|
563
|
+
fixLoop(newText, newFixed, iteration + 1)
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// Start the fix loop
|
|
575
|
+
fixLoop(text, [], 0)
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* Convenience function - fix and return just the fixed text
|
|
580
|
+
* Returns the original text if nothing was fixed
|
|
581
|
+
*/
|
|
582
|
+
@genType
|
|
583
|
+
let fixOnly = (text: string): string => {
|
|
584
|
+
switch fix(text) {
|
|
585
|
+
| Ok({text: fixedText, _}) => fixedText
|
|
586
|
+
| Error(_) => text
|
|
587
|
+
}
|
|
588
|
+
}
|