Improve getLinesBetween algorithm
MODIFY
packages/@romejs/js-formatter/Builder.ts
packages/@romejs/js-formatter/Builder.ts
@@ -16,6 +16,7 @@ import {
import builderFunctions from './builders/index';
import * as n from './node/index';
import {Token, concat, hardline, indent, join, mark} from './tokens';
+import {ob1Get1} from '@romejs/ob1';
export type BuilderMethod<T extends AnyNode = AnyNode> = (
builder: Builder,
@@ -136,18 +137,8 @@ export default class Builder {
if (!isLast) {
const nextNode = nodes[i + 1];
- const trailingComments = this.getComments(
- 'trailingComments',
- node,
- true,
- );
-
- let currentNode = node;
- if (trailingComments && trailingComments.length > 0) {
- currentNode = trailingComments[trailingComments.length - 1];
- }
- if (n.getLinesBetween(currentNode, nextNode) > 1) {
+ if (this.getLinesBetween(node, nextNode) > 1) {
printed = concat([printed, hardline]);
}
}
@@ -198,4 +189,64 @@ export default class Builder {
return comments.filter((comment) => !this.printedComments.has(comment.id));
}
}
+
+ getLinesBetween(a: AnyNode, b: AnyNode): number {
+ if (a.loc === undefined || b.loc === undefined) {
+ return 0;
+ }
+
+ let aEndLine = ob1Get1(a.loc.end.line);
+ let bStartLine = ob1Get1(b.loc.start.line);
+
+ // Simple cases:
+ // 1. `a` and `b` are on the same line
+ // 2. `a` and `b` are on their own line without empty lines between them
+ if (bStartLine - aEndLine <= 1) {
+ return bStartLine - aEndLine;
+ }
+
+ // If the are more than one line between `a` and `b`, the comment nodes must
+ // be inspected to detect empty lines.
+ //
+ // In the following example, `getLinesBetween` should return `1`.
+ //
+ // a;
+ // /* COMMENT */
+ // b;
+
+ const aTrailingComments = this.getComments('trailingComments', a, true);
+ const bLeadingComments = this.getComments('leadingComments', b, true);
+
+ // Comments must be deduplicated because they are shared between nodes.
+ // Walk them in order to calculate the nodes' boundaries.
+ if (aTrailingComments !== undefined || bLeadingComments !== undefined) {
+ const seenComments: Set<AnyComment> = new Set();
+
+ // Expand `a` boundaries
+ if (aTrailingComments !== undefined) {
+ for (const comment of aTrailingComments) {
+ seenComments.add(comment);
+
+ if (comment.loc !== undefined) {
+ aEndLine = Math.max(aEndLine, ob1Get1(comment.loc.end.line));
+ }
+ }
+ }
+
+ // Expand `b` boundaries
+ if (bLeadingComments !== undefined) {
+ for (const comment of bLeadingComments) {
+ if (seenComments.has(comment)) {
+ continue;
+ }
+
+ if (comment.loc !== undefined) {
+ bStartLine = Math.min(bStartLine, ob1Get1(comment.loc.start.line));
+ }
+ }
+ }
+ }
+
+ return bStartLine - aEndLine;
+ }
}

MODIFY
packages/@romejs/string-diff/index.ts
packages/@romejs/string-diff/index.ts
@@ -229,8 +229,8 @@ function bisect(text1: string, text2: string): Diffs {
let vLength = 2 * maxD;
let v1 = new Array(vLength);
let v2 = new Array(vLength);
- // Setting all elements to -1 is faster in Chrome & Firefox than mixing
+ // Setting all elements to -1 is faster in Chrome & Firefox than mixing
// integers and undefined.
for (let x = 0; x < vLength; x++) {
v1[x] = -1;
@@ -239,12 +239,12 @@ function bisect(text1: string, text2: string): Diffs {
v1[vOffset + 1] = 0;
v2[vOffset + 1] = 0;
let delta = text1Length - text2Length;
- // If the total number of characters is odd, then the front path will collide
+ // If the total number of characters is odd, then the front path will collide
// with the reverse path.
let front = delta % 2 !== 0;
- // Offsets for start and end of k loop.
+ // Offsets for start and end of k loop.
// Prevents mapping of space beyond the grid.
let k1Start = 0;
let k1End = 0;
@@ -330,8 +330,8 @@ function bisect(text1: string, text2: string): Diffs {
}
}
}
- // Diff took too long and hit the deadline or
+ // Diff took too long and hit the deadline or
// number of diffs equals number of characters, no commonality at all.
return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];
}
@@ -372,7 +372,6 @@ function commonPrefix(text1: string, text2: string): number {
}
// Binary search.
-
// Performance analysis: http://neil.fraser.name/news/2007/10/09/
let pointermin = 0;
let pointermax = Math.min(text1.length, text2.length);
@@ -714,18 +713,17 @@ function cleanupMerge(diffs: Diffs, fixUnicode: boolean) {
}
}
if (diffs[diffs.length - 1][1] === '') {
- diffs.pop(); // Remove the dummy entry at the end.
+ // Remove the dummy entry at the end.
+ diffs.pop();
}
// Second pass: look for single edits surrounded on both sides by equalities
-
// which can be shifted sideways to eliminate an equality.
-
// e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
let changes = false;
pointer = 1;
- // Intentionally ignore the first and last element (don't need checking).
+ // Intentionally ignore the first and last element (don't need checking).
while (pointer < diffs.length - 1) {
if (
diffs[pointer - 1][0] === DIFF_EQUAL &&
@@ -763,8 +761,8 @@ function cleanupMerge(diffs: Diffs, fixUnicode: boolean) {
}
pointer++;
}
- // If shifts were made, the diff needs reordering and another shift sweep.
+ // If shifts were made, the diff needs reordering and another shift sweep.
if (changes) {
cleanupMerge(diffs, fixUnicode);
}

Improve getLinesBetween algorithm