Skip to content

Commit ba0c455

Browse files
committed
make it work with locationless nodes
1 parent 7ceb31d commit ba0c455

File tree

5 files changed

+133
-39
lines changed

5 files changed

+133
-39
lines changed

src/context.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ export class Context {
111111
}
112112
}
113113

114+
empty() {
115+
return !this.#commands.some(has_content);
116+
}
117+
114118
measure() {
115119
return measure(this.#commands);
116120
}
@@ -141,3 +145,18 @@ function measure(commands, from = 0, to = commands.length) {
141145

142146
return total;
143147
}
148+
149+
/**
150+
* @param {Command} command
151+
*/
152+
function has_content(command) {
153+
if (Array.isArray(command)) {
154+
return command.some(has_content);
155+
}
156+
157+
if (typeof command === 'string') {
158+
return command.length > 0;
159+
}
160+
161+
return false;
162+
}

src/languages/ts.js

Lines changed: 85 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/** @import { TSESTree } from '@typescript-eslint/types' */
2-
/** @import { Visitors, Context } from '../types.js' */
2+
/** @import { Visitors } from '../types.js' */
3+
import { Context } from '../context.js';
34

45
/** @type {Record<TSESTree.Expression['type'] | 'Super' | 'RestElement', number>} */
56
export const EXPRESSIONS_PRECEDENCE = {
@@ -103,9 +104,40 @@ export default (options = {}) => {
103104

104105
let comment_index = 0;
105106

107+
/**
108+
* Set `comment_index` to be the first comment after `start`.
109+
* Most of the time this is already correct, but if nodes
110+
* have been moved around we may need to search for it
111+
* @param {TSESTree.Node} node
112+
*/
113+
function reset_comment_index(node) {
114+
if (!node.loc) {
115+
comment_index = comments.length;
116+
return;
117+
}
118+
119+
let previous = comments[comment_index - 1];
120+
let comment = comments[comment_index];
121+
122+
if (
123+
comment &&
124+
comment.loc &&
125+
!before(comment.loc.start, node.loc.start) &&
126+
(!previous || (previous.loc && before(previous.loc.start, node.loc.start)))
127+
) {
128+
return;
129+
}
130+
131+
// TODO use a binary search here, account for synthetic nodes (without `loc`)
132+
comment_index = comments.findIndex(
133+
(comment) => comment.loc && node.loc && !before(comment.loc.start, node.loc.start)
134+
);
135+
if (comment_index === -1) comment_index = comments.length;
136+
}
137+
106138
/**
107139
* @param {Context} context
108-
* @param {{ line: number, column: number }} prev
140+
* @param {{ line: number, column: number } | null} prev
109141
* @param {{ line: number, column: number } | null} next
110142
* @returns {boolean} true if final comment is a line comment
111143
*/
@@ -115,6 +147,7 @@ export default (options = {}) => {
115147

116148
if (
117149
comment &&
150+
prev &&
118151
comment.loc.start.line === prev.line &&
119152
(next === null || before(comment.loc.end, next))
120153
) {
@@ -146,7 +179,7 @@ export default (options = {}) => {
146179
while (comment_index < comments.length) {
147180
const comment = comments[comment_index];
148181

149-
if (comment && before(comment.loc.start, to)) {
182+
if (comment && comment.loc && to && before(comment.loc.start, to)) {
150183
if (first && from !== null && comment.loc.start.line > from.line) {
151184
context.margin();
152185
context.newline();
@@ -192,8 +225,8 @@ export default (options = {}) => {
192225
child_context.write(separator);
193226
}
194227

195-
const next = i === nodes.length - 1 ? until : nodes[i + 1]?.loc.start || null;
196-
if (child && flush_trailing_comments(child_context, child.loc.end, next)) {
228+
const next = i === nodes.length - 1 ? until : nodes[i + 1]?.loc?.start || null;
229+
if (child && flush_trailing_comments(child_context, child.loc?.end || null, next)) {
197230
multiline = true;
198231
}
199232

@@ -237,7 +270,7 @@ export default (options = {}) => {
237270
prev = child;
238271
}
239272

240-
flush_comments_until(context, nodes[nodes.length - 1]?.loc.end ?? null, until, false);
273+
flush_comments_until(context, nodes[nodes.length - 1]?.loc?.end ?? null, until, false);
241274

242275
if (multiline) {
243276
context.dedent();
@@ -254,6 +287,8 @@ export default (options = {}) => {
254287
* @param {TSESTree.Node & { body: TSESTree.Node[] }} node
255288
*/
256289
function body(context, node) {
290+
reset_comment_index(node);
291+
257292
/** @type {string | null} */
258293
let prev_type = null;
259294
let prev_multiline = false;
@@ -282,7 +317,7 @@ export default (options = {}) => {
282317
context.newline();
283318
flush_comments_until(
284319
context,
285-
node.body[node.body.length - 1]?.loc.end ?? null,
320+
node.body[node.body.length - 1]?.loc?.end ?? null,
286321
node.loc.end,
287322
false
288323
);
@@ -296,7 +331,12 @@ export default (options = {}) => {
296331
*/
297332
'ArrayExpression|ArrayPattern': (node, context) => {
298333
context.write('[');
299-
sequence(context, /** @type {TSESTree.Node[]} */ (node.elements), node.loc.end, false);
334+
sequence(
335+
context,
336+
/** @type {TSESTree.Node[]} */ (node.elements),
337+
node.loc?.end ?? null,
338+
false
339+
);
300340
context.write(']');
301341
},
302342

@@ -344,18 +384,13 @@ export default (options = {}) => {
344384
context.write('{');
345385
}
346386

347-
const comment = comments[comment_index];
348-
349-
let has_content = node.body.length > 0;
350-
let has_comment =
351-
comment &&
352-
before(node.loc.start, comment.loc.start) &&
353-
before(comment.loc.end, node.loc.end);
387+
const child_context = context.new();
388+
body(child_context, node);
354389

355-
if (has_content || has_comment) {
390+
if (!child_context.empty()) {
356391
context.indent();
357392
context.newline();
358-
body(context, node);
393+
context.append(child_context);
359394
context.dedent();
360395
context.newline();
361396
}
@@ -421,7 +456,9 @@ export default (options = {}) => {
421456
// we make the whole sequence multiline
422457
if (
423458
is_last &&
459+
arg.loc &&
424460
comments[comment_index] &&
461+
comments[comment_index].loc &&
425462
comments[comment_index].loc.start.line < arg.loc.start.line
426463
) {
427464
child_context.multiline = true;
@@ -431,9 +468,11 @@ export default (options = {}) => {
431468

432469
if (!is_last) context.write(',');
433470

434-
const next = is_last ? node.loc.end : (node.arguments[i + 1]?.loc.start ?? null);
471+
const next = is_last
472+
? (node.loc?.end ?? null)
473+
: (node.arguments[i + 1]?.loc?.start ?? null);
435474

436-
if (flush_trailing_comments(context, arg.loc.end, next)) {
475+
if (flush_trailing_comments(context, arg.loc?.end ?? null, next)) {
437476
child_context.multiline = true;
438477
}
439478

@@ -475,7 +514,7 @@ export default (options = {}) => {
475514

476515
if (node.implements) {
477516
context.write('implements ');
478-
sequence(context, node.implements, node.body.loc.start, false);
517+
sequence(context, node.implements, node.body.loc?.start ?? null, false);
479518
}
480519

481520
context.visit(node.body);
@@ -516,7 +555,7 @@ export default (options = {}) => {
516555
}
517556

518557
context.write('(');
519-
sequence(context, node.params, (node.returnType ?? node.body).loc.start, false);
558+
sequence(context, node.params, (node.returnType ?? node.body).loc?.start ?? null, false);
520559
context.write(')');
521560

522561
if (node.returnType) context.visit(node.returnType);
@@ -562,7 +601,7 @@ export default (options = {}) => {
562601
if (node.async) context.write('async ');
563602

564603
context.write('(');
565-
sequence(context, node.params, node.body.loc.start, false);
604+
sequence(context, node.params, node.body.loc?.start ?? null, false);
566605
context.write(') => ');
567606

568607
if (
@@ -735,7 +774,7 @@ export default (options = {}) => {
735774
}
736775

737776
context.write('{');
738-
sequence(context, node.specifiers, node.source?.loc.start ?? node.loc.end, true);
777+
sequence(context, node.specifiers, node.source?.loc?.start ?? node.loc?.end ?? null, true);
739778
context.write('}');
740779

741780
if (node.source) {
@@ -861,7 +900,7 @@ export default (options = {}) => {
861900

862901
if (named_specifiers.length > 0) {
863902
context.write('{');
864-
sequence(context, named_specifiers, node.source.loc.start, true);
903+
sequence(context, named_specifiers, node.source.loc?.start ?? null, true);
865904
context.write('}');
866905
}
867906

@@ -987,7 +1026,7 @@ export default (options = {}) => {
9871026
sequence(
9881027
context,
9891028
node.value.params,
990-
(node.value.returnType ?? node.value.body)?.loc.start ?? node.loc.end,
1029+
(node.value.returnType ?? node.value.body)?.loc?.start ?? node.loc?.end ?? null,
9911030
false
9921031
);
9931032
context.write(')');
@@ -1003,13 +1042,13 @@ export default (options = {}) => {
10031042

10041043
ObjectExpression(node, context) {
10051044
context.write('{');
1006-
sequence(context, node.properties, node.loc.end, true);
1045+
sequence(context, node.properties, node.loc?.end ?? null, true);
10071046
context.write('}');
10081047
},
10091048

10101049
ObjectPattern(node, context) {
10111050
context.write('{');
1012-
sequence(context, node.properties, node.loc.end, true);
1051+
sequence(context, node.properties, node.loc?.end ?? null, true);
10131052
context.write('}');
10141053

10151054
if (node.typeAnnotation) context.visit(node.typeAnnotation);
@@ -1056,7 +1095,7 @@ export default (options = {}) => {
10561095
sequence(
10571096
context,
10581097
node.value.params,
1059-
(node.value.returnType ?? node.value.body).loc.start,
1098+
(node.value.returnType ?? node.value.body).loc?.start ?? null,
10601099
false
10611100
);
10621101
context.write(')');
@@ -1116,6 +1155,8 @@ export default (options = {}) => {
11161155
if (node.argument) {
11171156
const contains_comment =
11181157
comments[comment_index] &&
1158+
comments[comment_index].loc &&
1159+
node.argument.loc &&
11191160
before(comments[comment_index].loc.start, node.argument.loc.start);
11201161

11211162
context.write(contains_comment ? 'return (' : 'return ');
@@ -1128,7 +1169,7 @@ export default (options = {}) => {
11281169

11291170
SequenceExpression(node, context) {
11301171
context.write('(');
1131-
sequence(context, node.expressions, node.loc.end, false);
1172+
sequence(context, node.expressions, node.loc?.end ?? null, false);
11321173
context.write(')');
11331174
},
11341175

@@ -1363,7 +1404,7 @@ export default (options = {}) => {
13631404

13641405
TSTypeLiteral(node, context) {
13651406
context.write('{ ');
1366-
sequence(context, node.members, node.loc.end, false, ';');
1407+
sequence(context, node.members, node.loc?.end ?? null, false, ';');
13671408
context.write(' }');
13681409
},
13691410

@@ -1433,7 +1474,12 @@ export default (options = {}) => {
14331474
context.write('(');
14341475

14351476
// @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
1436-
sequence(context, node.parameters, node.typeAnnotation.typeAnnotation.loc.start, false);
1477+
sequence(
1478+
context,
1479+
node.parameters,
1480+
node.typeAnnotation.typeAnnotation.loc?.start ?? null,
1481+
false
1482+
);
14371483

14381484
context.write(') => ');
14391485

@@ -1445,7 +1491,7 @@ export default (options = {}) => {
14451491
context.write('[');
14461492

14471493
// @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
1448-
sequence(context, node.parameters, node.typeAnnotation?.loc.start, false);
1494+
sequence(context, node.parameters, node.typeAnnotation?.loc?.start ?? null, false);
14491495
context.write(']');
14501496

14511497
// @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
@@ -1458,7 +1504,7 @@ export default (options = {}) => {
14581504
context.write('(');
14591505

14601506
// @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
1461-
sequence(context, node.parameters, node.typeAnnotation.loc.start, false);
1507+
sequence(context, node.parameters, node.typeAnnotation.loc?.start ?? null, false);
14621508
context.write(')');
14631509

14641510
// @ts-expect-error `acorn-typescript` and `@typescript-eslint/types` have slightly different type definitions
@@ -1467,7 +1513,7 @@ export default (options = {}) => {
14671513

14681514
TSTupleType(node, context) {
14691515
context.write('[');
1470-
sequence(context, node.elementTypes, node.loc.end, false);
1516+
sequence(context, node.elementTypes, node.loc?.end ?? null, false);
14711517
context.write(']');
14721518
},
14731519

@@ -1478,11 +1524,11 @@ export default (options = {}) => {
14781524
},
14791525

14801526
TSUnionType(node, context) {
1481-
sequence(context, node.types, node.loc.end, false, ' |');
1527+
sequence(context, node.types, node.loc?.end ?? null, false, ' |');
14821528
},
14831529

14841530
TSIntersectionType(node, context) {
1485-
sequence(context, node.types, node.loc.end, false, ' &');
1531+
sequence(context, node.types, node.loc?.end ?? null, false, ' &');
14861532
},
14871533

14881534
TSLiteralType(node, context) {
@@ -1540,7 +1586,7 @@ export default (options = {}) => {
15401586
context.write(' {');
15411587
context.indent();
15421588
context.newline();
1543-
sequence(context, node.members, node.loc.end, false);
1589+
sequence(context, node.members, node.loc?.end ?? null, false);
15441590
context.dedent();
15451591
context.newline();
15461592
context.write('}');
@@ -1572,7 +1618,7 @@ export default (options = {}) => {
15721618
},
15731619

15741620
TSInterfaceBody(node, context) {
1575-
sequence(context, node.body, node.loc.end, true, ';');
1621+
sequence(context, node.body, node.loc?.end ?? null, true, ';');
15761622
},
15771623

15781624
TSInterfaceDeclaration(node, context) {
@@ -1581,7 +1627,7 @@ export default (options = {}) => {
15811627
if (node.typeParameters) context.visit(node.typeParameters);
15821628
if (node.extends) {
15831629
context.write(' extends ');
1584-
sequence(context, node.extends, node.body.loc.start, false);
1630+
sequence(context, node.extends, node.body.loc?.start ?? null, false);
15851631
}
15861632
context.write(' {');
15871633
context.visit(node.body);
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class Foo {
2+
/**
3+
* @param {number} a
4+
* @param {number} b
5+
*/
6+
add(a, b) {
7+
return a + b;
8+
}
9+
}

0 commit comments

Comments
 (0)