Skip to content

Commit 4ac4bf4

Browse files
IvanGoncharovleebyron
authored andcommitted
printError: fix padding of line numbers (#1328)
* Use dedent for print error tests * printError: Add tests for padding of line numbers * printError: fix padding of line numbers
1 parent a65e84b commit 4ac4bf4

File tree

3 files changed

+95
-51
lines changed

3 files changed

+95
-51
lines changed

src/error/__tests__/printError-test.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,37 @@ import { parse, Source } from '../../language';
1414
import dedent from '../../jsutils/dedent';
1515

1616
describe('printError', () => {
17+
it('prints an line numbers with correct padding', () => {
18+
const singleDigit = new GraphQLError(
19+
'Single digit line number with no padding',
20+
null,
21+
new Source('*', 'Test', { line: 9, column: 1 }),
22+
[0],
23+
);
24+
expect(printError(singleDigit)).to.equal(dedent`
25+
Single digit line number with no padding
26+
27+
Test (9:1)
28+
9: *
29+
^
30+
`);
31+
32+
const doubleDigit = new GraphQLError(
33+
'Left padded first line number',
34+
null,
35+
new Source('*\n', 'Test', { line: 9, column: 1 }),
36+
[0],
37+
);
38+
expect(printError(doubleDigit)).to.equal(dedent`
39+
Left padded first line number
40+
41+
Test (9:1)
42+
9: *
43+
^
44+
10:
45+
`);
46+
});
47+
1748
it('prints an error with nodes from different sources', () => {
1849
const sourceA = parse(
1950
new Source(

src/error/printError.js

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -48,29 +48,40 @@ function highlightSourceAtLocation(
4848
source: Source,
4949
location: SourceLocation,
5050
): string {
51-
const line = location.line;
51+
const firstLineColumnOffset = source.locationOffset.column - 1;
52+
const body = whitespace(firstLineColumnOffset) + source.body;
53+
54+
const lineIndex = location.line - 1;
5255
const lineOffset = source.locationOffset.line - 1;
53-
const columnOffset = getColumnOffset(source, location);
54-
const contextLine = line + lineOffset;
55-
const contextColumn = location.column + columnOffset;
56-
const prevLineNum = (contextLine - 1).toString();
57-
const lineNum = contextLine.toString();
58-
const nextLineNum = (contextLine + 1).toString();
59-
const padLen = nextLineNum.length;
60-
const lines = source.body.split(/\r\n|[\n\r]/g);
61-
lines[0] = whitespace(source.locationOffset.column - 1) + lines[0];
62-
const outputLines = [
63-
`${source.name} (${contextLine}:${contextColumn})`,
64-
line >= 2 && lpad(padLen, prevLineNum) + ': ' + lines[line - 2],
65-
lpad(padLen, lineNum) + ': ' + lines[line - 1],
66-
whitespace(2 + padLen + contextColumn - 1) + '^',
67-
line < lines.length && lpad(padLen, nextLineNum) + ': ' + lines[line],
68-
];
69-
return outputLines.filter(Boolean).join('\n');
56+
const lineNum = location.line + lineOffset;
57+
58+
const columnOffset = location.line === 1 ? firstLineColumnOffset : 0;
59+
const columnNum = location.column + columnOffset;
60+
61+
const lines = body.split(/\r\n|[\n\r]/g);
62+
return (
63+
`${source.name} (${lineNum}:${columnNum})\n` +
64+
printPrefixedLines([
65+
// Lines specified like this: ["prefix", "string"],
66+
[`${lineNum - 1}: `, lines[lineIndex - 1]],
67+
[`${lineNum}: `, lines[lineIndex]],
68+
['', whitespace(columnNum - 1) + '^'],
69+
[`${lineNum + 1}: `, lines[lineIndex + 1]],
70+
])
71+
);
7072
}
7173

72-
function getColumnOffset(source: Source, location: SourceLocation): number {
73-
return location.line === 1 ? source.locationOffset.column - 1 : 0;
74+
function printPrefixedLines(lines: Array<[string, string]>): string {
75+
const existingLines = lines.filter(([_, line]) => line !== undefined);
76+
77+
let padLen = 0;
78+
for (const [prefix] of existingLines) {
79+
padLen = Math.max(padLen, prefix.length);
80+
}
81+
82+
return existingLines
83+
.map(([prefix, line]) => lpad(padLen, prefix) + line)
84+
.join('\n');
7485
}
7586

7687
function whitespace(len: number): string {

src/language/__tests__/lexer-test.js

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import { expect } from 'chai';
99
import { describe, it } from 'mocha';
10+
import dedent from '../../jsutils/dedent';
1011
import { Source } from '../source';
1112
import { createLexer, TokenKind } from '../lexer';
1213

@@ -105,24 +106,25 @@ describe('Lexer', () => {
105106
it('errors respect whitespace', () => {
106107
let caughtError;
107108
try {
108-
lexOne(`
109-
110-
?
111-
112-
113-
`);
109+
lexOne(dedent`
110+
111+
112+
?
113+
114+
115+
`);
114116
} catch (error) {
115117
caughtError = error;
116118
}
117-
expect(String(caughtError)).to.equal(
118-
'Syntax Error: Cannot parse the unexpected character "?".\n' +
119-
'\n' +
120-
'GraphQL request (3:5)\n' +
121-
'2: \n' +
122-
'3: ?\n' +
123-
' ^\n' +
124-
'4: \n',
125-
);
119+
expect(String(caughtError)).to.equal(dedent`
120+
Syntax Error: Cannot parse the unexpected character "?".
121+
122+
GraphQL request (3:5)
123+
2:
124+
3: ?
125+
^
126+
4:
127+
`);
126128
});
127129

128130
it('updates line numbers in error for file context', () => {
@@ -134,15 +136,15 @@ describe('Lexer', () => {
134136
} catch (error) {
135137
caughtError = error;
136138
}
137-
expect(String(caughtError)).to.equal(
138-
'Syntax Error: Cannot parse the unexpected character "?".\n' +
139-
'\n' +
140-
'foo.js (13:6)\n' +
141-
'12: \n' +
142-
'13: ?\n' +
143-
' ^\n' +
144-
'14: \n',
145-
);
139+
expect(String(caughtError)).to.equal(dedent`
140+
Syntax Error: Cannot parse the unexpected character "?".
141+
142+
foo.js (13:6)
143+
12:
144+
13: ?
145+
^
146+
14:
147+
`);
146148
});
147149

148150
it('updates column numbers in error for file context', () => {
@@ -153,13 +155,13 @@ describe('Lexer', () => {
153155
} catch (error) {
154156
caughtError = error;
155157
}
156-
expect(String(caughtError)).to.equal(
157-
'Syntax Error: Cannot parse the unexpected character "?".\n' +
158-
'\n' +
159-
'foo.js (1:5)\n' +
160-
'1: ?\n' +
161-
' ^\n',
162-
);
158+
expect(String(caughtError)).to.equal(dedent`
159+
Syntax Error: Cannot parse the unexpected character "?".
160+
161+
foo.js (1:5)
162+
1: ?
163+
^
164+
`);
163165
});
164166

165167
it('lexes strings', () => {

0 commit comments

Comments
 (0)