9
9
* @returns {Promise<Line[]> }
10
10
*/
11
11
async function getTransformedLines ( transformers , text , languageName , meta ) {
12
- /** @type {Line[] } */
12
+ /** @type {Omit< Line, 'gutterCells'> [] } */
13
13
const result = [ ] ;
14
14
const rawLines = text . split ( / \r ? \n / ) ;
15
15
const prevTransformerStates = [ ] ;
16
+ const gutterCellsPerTransformer = [ ] ;
17
+ /** @type {GutterCell[][][] } */
18
+ const gutterCells = [ ] ;
16
19
17
20
linesLoop: for ( let lineIndex = 0 ; lineIndex < rawLines . length ; lineIndex ++ ) {
18
21
let line = rawLines [ lineIndex ] ;
22
+ /** @type {GutterCell[][] } */
23
+ const lineGutterCells = [ ] ;
19
24
const attrs = { } ;
20
25
const graphQLData = { } ;
21
26
for ( let i = 0 ; i < transformers . length ; i ++ ) {
@@ -33,14 +38,76 @@ async function getTransformedLines(transformers, text, languageName, meta) {
33
38
if ( ! txResult . line ) {
34
39
continue linesLoop;
35
40
}
41
+ if ( txResult . gutterCells ) {
42
+ gutterCellsPerTransformer [ i ] = Math . max ( txResult . gutterCells . length , gutterCellsPerTransformer [ i ] || 0 ) ;
43
+ lineGutterCells [ i ] = txResult . gutterCells ;
44
+ }
36
45
line = txResult . line . text ;
37
46
Object . assign ( attrs , txResult . line . attrs ) ;
38
47
Object . assign ( graphQLData , txResult . data ) ;
39
48
}
49
+ gutterCells . push ( lineGutterCells ) ;
40
50
result . push ( { text : line , attrs, data : graphQLData } ) ;
41
51
}
42
52
43
- return result ;
53
+ const flattenedGutterCells = flattenGutterCells ( gutterCells , gutterCellsPerTransformer ) ;
54
+ return result . map ( ( line , i ) => ( { ...line , gutterCells : flattenedGutterCells [ i ] } ) ) ;
55
+ }
56
+
57
+ /**
58
+ * Transforms a 3D array of gutter cells into a 2D array of gutter cells.
59
+ * The input is in the form of gutter cells per line transformer per line,
60
+ * whereas the output is is gutter cells per line. Each line transformer can
61
+ * return more than one gutter cell, and need not return the same number of
62
+ * cells for each line, so the flattening must be done in a way that ensures
63
+ * that each line transformer has its gutter cells aligned to the same index
64
+ * in every line. For example, for the input
65
+ *
66
+ * ```
67
+ * [
68
+ * [[t0], [t1a, t1b], [t2]], // Line 1
69
+ * [undefined, [t1], [t2]], // Line 2
70
+ * [[t0a, t0b], undefined, [t2a, t2b]] // Line 3
71
+ * ]
72
+ * ```
73
+ *
74
+ * we would flatten to
75
+ *
76
+ * ```
77
+ * [
78
+ * [t0, undefined, t1a, t1b, t2, undefined], // Line 1
79
+ * [undefined, undefined, t1, undefined, t2, undefined], // Line 2
80
+ * [t0a, t0b, undefined, undefined, t2a, t2b] // Line 3
81
+ * ]
82
+ * ```
83
+ *
84
+ * such that each of the three transformers (t0, t1, t2) reserve two gutter
85
+ * cells for itself, padding empty spaces in the final array with `undefined`
86
+ * to ensure correct vertical alignment.
87
+ *
88
+ * The parameter `gutterCellsPerTransformer` can be derived from `gutterCells`,
89
+ * but as an optimization, we already know it from previously iterating through
90
+ * line transformer results.
91
+ *
92
+ * @param {GutterCell[][][] } gutterCells
93
+ * @param {number[] } gutterCellsPerTransformer
94
+ * @returns {GutterCell[][] }
95
+ */
96
+ function flattenGutterCells ( gutterCells , gutterCellsPerTransformer ) {
97
+ return gutterCells . map ( transformerResults => {
98
+ /** @type {GutterCell[] } */
99
+ const result = [ ] ;
100
+ for ( let i = 0 ; i < transformerResults . length ; i ++ ) {
101
+ const currentTransformerCells = transformerResults [ i ] ;
102
+ const length = currentTransformerCells ? currentTransformerCells . length : 0 ;
103
+ const padding = gutterCellsPerTransformer [ i ] - length ;
104
+ if ( currentTransformerCells ) {
105
+ result . push ( ...currentTransformerCells ) ;
106
+ }
107
+ result . fill ( undefined , result . length , result . length + padding ) ;
108
+ }
109
+ return result ;
110
+ } ) ;
44
111
}
45
112
46
113
module . exports = getTransformedLines ;
0 commit comments