Skip to content

Commit 16cef72

Browse files
author
Jakub Rożek
authored
fix(code-viewer): line numbers are incorrect in certain cases (#186)
1 parent 96f49c2 commit 16cef72

8 files changed

Lines changed: 125 additions & 55 deletions

File tree

src/CodeViewer/__tests__/Viewer.spec.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ describe('Code Viewer component', () => {
5151
const wrapper = mount(<CodeViewer language={language} value={code} />);
5252
expect(wrapper).toHaveText(code);
5353

54-
expect(parseCode).toHaveBeenCalledWith(code, language, false);
54+
expect(parseCode).toHaveBeenCalledWith(code, language);
5555
wrapper.unmount();
5656
});
5757

@@ -75,7 +75,7 @@ describe('Code Viewer component', () => {
7575

7676
const wrapper = mount(<CodeViewer language={language} value={code} />);
7777
expect(wrapper).toContainReact(markup);
78-
expect(parseCode).toHaveBeenCalledWith(code, language, false);
78+
expect(parseCode).toHaveBeenCalledWith(code, language);
7979
expect(astToReact).toHaveBeenCalled();
8080
wrapper.unmount();
8181
});

src/CodeViewer/components/BlockCodeViewer/SingleCodeBlock.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as React from 'react';
2-
import { ReactNode } from 'react';
32

43
import { astToReact } from '../../utils/astToReact';
4+
import { lineNumberify } from '../../utils/lineNumberify';
55
import { parseCode } from '../../utils/parseCode';
66
import type { ObservableSet } from './ObservableSet';
77

@@ -22,7 +22,7 @@ export const SingleCodeBlock: React.FC<IBlockProps> = ({
2222
lineNumber,
2323
observer,
2424
}) => {
25-
const [markup, setMarkup] = React.useState<ReactNode[]>();
25+
const [markup, setMarkup] = React.useState<React.ReactNode[]>();
2626
const [isVisible, setIsVisible] = React.useState(false);
2727

2828
React.useEffect(() => {
@@ -34,7 +34,8 @@ export const SingleCodeBlock: React.FC<IBlockProps> = ({
3434
React.useEffect(() => {
3535
if (isVisible) {
3636
try {
37-
const tree = parseCode(value, language, showLineNumbers);
37+
const tree = parseCode(value, language);
38+
const processedTree = showLineNumbers ? lineNumberify(tree, lineNumber - 1) : tree;
3839

3940
if (tree.length > 0) {
4041
const lastTreeNode = tree[tree.length - 1];
@@ -45,11 +46,11 @@ export const SingleCodeBlock: React.FC<IBlockProps> = ({
4546
lastTreeNode.children[0].value === '\n'
4647
) {
4748
// this is to get rid of trailing new lines
48-
tree.pop();
49+
processedTree.pop();
4950
}
5051
}
5152

52-
setMarkup(tree.map(astToReact(lineNumber)));
53+
setMarkup(processedTree.map(astToReact(0)));
5354
} catch {
5455
// parsing failed for some reason, let's display regular text
5556
}

src/CodeViewer/utils/__tests__/__snapshots__/astToReact.spec.ts.snap

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,25 @@ exports[`astToReact util fixture clike 1`] = `
55
int
66
<span
77
className="token function"
8-
data-node-index={1}
98
key="cv-0-1"
109
>
1110
main
1211
</span>
1312
<span
1413
className="token punctuation"
15-
data-node-index={2}
1614
key="cv-0-2"
1715
>
1816
(
1917
</span>
2018
<span
2119
className="token punctuation"
22-
data-node-index={3}
2320
key="cv-0-3"
2421
>
2522
)
2623
</span>
2724
2825
<span
2926
className="token punctuation"
30-
data-node-index={5}
3127
key="cv-0-5"
3228
>
3329
{
@@ -36,7 +32,6 @@ exports[`astToReact util fixture clike 1`] = `
3632
3733
<span
3834
className="token comment"
39-
data-node-index={7}
4035
key="cv-0-7"
4136
>
4237
// some dumb code
@@ -45,22 +40,19 @@ exports[`astToReact util fixture clike 1`] = `
4540
4641
<span
4742
className="token keyword"
48-
data-node-index={9}
4943
key="cv-0-9"
5044
>
5145
return
5246
</span>
5347
5448
<span
5549
className="token number"
56-
data-node-index={11}
5750
key="cv-0-11"
5851
>
5952
0
6053
</span>
6154
<span
6255
className="token punctuation"
63-
data-node-index={12}
6456
key="cv-0-12"
6557
>
6658
;
@@ -69,7 +61,6 @@ exports[`astToReact util fixture clike 1`] = `
6961
7062
<span
7163
className="token punctuation"
72-
data-node-index={14}
7364
key="cv-0-14"
7465
>
7566
}
@@ -81,51 +72,44 @@ exports[`astToReact util fixture javascript 1`] = `
8172
<pre>
8273
<span
8374
className="token keyword"
84-
data-node-index={0}
8575
key="cv-0-0"
8676
>
8777
const
8878
</span>
8979
defaultValue
9080
<span
9181
className="token operator"
92-
data-node-index={2}
9382
key="cv-0-2"
9483
>
9584
=
9685
</span>
9786
stoplight
9887
<span
9988
className="token punctuation"
100-
data-node-index={4}
10189
key="cv-0-4"
10290
>
10391
.
10492
</span>
10593
<span
10694
className="token function"
107-
data-node-index={5}
10895
key="cv-0-5"
10996
>
11097
io
11198
</span>
11299
<span
113100
className="token punctuation"
114-
data-node-index={6}
115101
key="cv-0-6"
116102
>
117103
(
118104
</span>
119105
<span
120106
className="token punctuation"
121-
data-node-index={7}
122107
key="cv-0-7"
123108
>
124109
)
125110
</span>
126111
<span
127112
className="token punctuation"
128-
data-node-index={8}
129113
key="cv-0-8"
130114
>
131115
;

src/CodeViewer/utils/__tests__/__snapshots__/lineNumberify.spec.ts.snap

Lines changed: 102 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,105 @@ Array [
88
"children": Array [
99
Object {
1010
"type": "text",
11-
"value": "fn",
11+
"value": "fn
12+
",
1213
},
1314
],
1415
"properties": Object {
15-
"className": Array [
16-
"token",
17-
"function",
18-
],
16+
"className": Array [],
17+
},
18+
"tagName": "span",
19+
"type": "element",
20+
},
21+
],
22+
"properties": Object {
23+
"className": Array [
24+
"line-number",
25+
],
26+
"data-node-index": 1,
27+
},
28+
"tagName": "span",
29+
"type": "element",
30+
},
31+
Object {
32+
"children": Array [
33+
Object {
34+
"type": "text",
35+
"value": "
36+
",
37+
},
38+
],
39+
"properties": Object {
40+
"className": Array [
41+
"line-number",
42+
],
43+
"data-node-index": 2,
44+
},
45+
"tagName": "span",
46+
"type": "element",
47+
},
48+
Object {
49+
"children": Array [
50+
Object {
51+
"type": "text",
52+
"value": "
53+
",
54+
},
55+
],
56+
"properties": Object {
57+
"className": Array [
58+
"line-number",
59+
],
60+
"data-node-index": 3,
61+
},
62+
"tagName": "span",
63+
"type": "element",
64+
},
65+
Object {
66+
"children": Array [
67+
Object {
68+
"type": "text",
69+
"value": "
70+
",
71+
},
72+
],
73+
"properties": Object {
74+
"className": Array [
75+
"line-number",
76+
],
77+
"data-node-index": 4,
78+
},
79+
"tagName": "span",
80+
"type": "element",
81+
},
82+
Object {
83+
"children": Array [
84+
Object {
85+
"type": "text",
86+
"value": "abc
87+
",
88+
},
89+
],
90+
"properties": Object {
91+
"className": Array [
92+
"line-number",
93+
],
94+
"data-node-index": 5,
95+
},
96+
"tagName": "span",
97+
"type": "element",
98+
},
99+
Object {
100+
"children": Array [
101+
Object {
102+
"children": Array [
103+
Object {
104+
"type": "text",
105+
"value": "",
106+
},
107+
],
108+
"properties": Object {
109+
"className": Array [],
19110
},
20111
"tagName": "span",
21112
"type": "element",
@@ -24,13 +115,13 @@ Array [
24115
"children": Array [
25116
Object {
26117
"type": "text",
27-
"value": "(",
118+
"value": "t",
28119
},
29120
],
30121
"properties": Object {
31122
"className": Array [
32123
"token",
33-
"punctuation",
124+
"function",
34125
],
35126
},
36127
"tagName": "span",
@@ -40,7 +131,7 @@ Array [
40131
"children": Array [
41132
Object {
42133
"type": "text",
43-
"value": ")",
134+
"value": "(",
44135
},
45136
],
46137
"properties": Object {
@@ -56,7 +147,7 @@ Array [
56147
"children": Array [
57148
Object {
58149
"type": "text",
59-
"value": ";",
150+
"value": ")",
60151
},
61152
],
62153
"properties": Object {
@@ -73,6 +164,7 @@ Array [
73164
"className": Array [
74165
"line-number",
75166
],
167+
"data-node-index": 6,
76168
},
77169
"tagName": "span",
78170
"type": "element",
@@ -153,6 +245,7 @@ Array [
153245
"className": Array [
154246
"line-number",
155247
],
248+
"data-node-index": 1,
156249
},
157250
"tagName": "span",
158251
"type": "element",

src/CodeViewer/utils/__tests__/lineNumberify.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ import { lineNumberify } from '../lineNumberify';
55
describe('lineNumberify util', () => {
66
it('handles one-liners', () => {
77
const ast = require(path.join(__dirname, '/fixtures/one-liner.json'));
8-
expect(lineNumberify(JSON.parse(JSON.stringify(ast)))).toMatchSnapshot();
8+
expect(lineNumberify(JSON.parse(JSON.stringify(ast)), 0)).toMatchSnapshot();
99
});
1010

1111
it('handles multiple lines', () => {
12-
const ast = require(path.join(__dirname, '/fixtures/one-liner.json'));
13-
expect(lineNumberify(JSON.parse(JSON.stringify(ast)))).toMatchSnapshot();
12+
const ast = require(path.join(__dirname, '/fixtures/multiple-lines.json'));
13+
expect(lineNumberify(JSON.parse(JSON.stringify(ast)), 0)).toMatchSnapshot();
1414
});
1515
});

src/CodeViewer/utils/astToReact.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,24 @@ import { createElement, ReactNode } from 'react';
22
import { RefractorNode } from 'refractor/core';
33

44
// based on https://github.com/rexxars/react-lowlight/blob/master/src/mapChildren.js
5-
function mapChild(child: RefractorNode, index: number, depth: number): ReactNode {
5+
function mapChild(child: RefractorNode, i: number, depth: number): ReactNode {
66
if ('tagName' in child) {
77
return createElement(
88
child.tagName,
99
{
10-
key: `cv-${depth}-${index}`,
10+
key: `cv-${depth}-${i}`,
1111
...child.properties,
1212
className: child.properties && (child.properties.className || []).join(' '),
13-
'data-node-index': index,
1413
},
15-
child.children && child.children.map(astToReact(index + 1, depth + 1)),
14+
child.children && child.children.map(astToReact(depth + 1)),
1615
);
1716
}
1817

1918
return child.value;
2019
}
2120

22-
export function astToReact(index: number, depth: number = 0) {
23-
return function mapChildrenWithDepth(child: RefractorNode) {
24-
// eslint-disable-next-line no-param-reassign
25-
return mapChild(child, index++, depth);
21+
export function astToReact(depth: number = 0) {
22+
return function mapChildrenWithDepth(child: RefractorNode, i: number) {
23+
return mapChild(child, i, depth);
2624
};
2725
}

0 commit comments

Comments
 (0)