Fix markup grid regressions
MODIFY
packages/@romejs/cli-diagnostics/buildMessageCodeFrame.ts
packages/@romejs/cli-diagnostics/buildMessageCodeFrame.ts
@@ -185,8 +185,11 @@ export default function buildMessageCodeFrame(
}
const pointerLength: number = Math.max(ob1Get0(markerSize), 1);
- const pointer: string = `<error><emphasis>${'^'.repeat(pointerLength)}</emphasis></error>`;
+ const pointer: string = `<error><emphasis>${'^'.repeat(
+ pointerLength,
+ )}</emphasis></error>`;
const pointerIndent: string = ' '.repeat(ob1Get0(markerOffset));
+ const paddedMarkerMessage = markerMessage === '' ? '' : ` ${markerMessage}`;
// If the marker is just pointing to the first character and we have no message, no point showing it
const noMarkerLine =
@@ -196,14 +199,16 @@ export default function buildMessageCodeFrame(
if (noGutter) {
const result = [...allLines];
if (!noMarkerLine) {
- result.push(`${pointerIndent}${pointer} ${markerMessage}`);
+ result.push(`${pointerIndent}${pointer}${paddedMarkerMessage}`);
}
return joinNoBreak(result.map((line) => `${CODE_FRAME_INDENT}${line}`));
}
// Build marker
- const markerGutterIndent: string = ' '.repeat(maxGutterLength - GUTTER.length);
- const markerLine: string = `${markerGutterIndent}<emphasis>${GUTTER}</emphasis>${pointerIndent}${pointer} ${markerMessage}`;
+ const markerGutterIndent: string = ' '.repeat(
+ maxGutterLength - GUTTER.length,
+ );
+ const markerLine: string = `${markerGutterIndent}<emphasis>${GUTTER}</emphasis>${pointerIndent}${pointer}${paddedMarkerMessage}`;
// Build up the line we display when source lines are omitted
const omittedLine =

MODIFY
packages/@romejs/cli-diagnostics/buildPatchCodeFrame.ts
packages/@romejs/cli-diagnostics/buildPatchCodeFrame.ts
@@ -130,7 +130,7 @@ export default function buildPatchCodeFrame(
let gutterNoLine = '';
if (!noGutter) {
- gutterWithLine = `<emphasis>${CODE_FRAME_INDENT}<pad width="${lineLength}">${lineNo}</pad>${GUTTER}</emphasis>`;
+ gutterWithLine = `<emphasis>${CODE_FRAME_INDENT}<pad align="right" width="${lineLength}">${lineNo}</pad>${GUTTER}</emphasis>`;
gutterNoLine = `<emphasis>${CODE_FRAME_INDENT}${' '.repeat(lineLength)}${GUTTER}</emphasis>`;
}

MODIFY
packages/@romejs/cli-reporter/Reporter.ts
packages/@romejs/cli-reporter/Reporter.ts
@@ -31,7 +31,10 @@ import stream = require('stream');
import {CWD_PATH} from '@romejs/path';
import {Event} from '@romejs/events';
import readline = require('readline');
-import {MarkupTagName} from '@romejs/string-markup/types';
+import {
+ MarkupFormatGridOptions,
+ MarkupTagName,
+} from '@romejs/string-markup/types';
import select from './select';
import {MarkupLinesAndWidth} from '@romejs/string-markup/format';
@@ -853,19 +856,21 @@ export default class Reporter {
return {lines: [''], width: 0};
}
- const viewportWidth =
- stream.columns - this.indentString.length - viewportShrink;
+ const gridMarkupOptions: MarkupFormatGridOptions = {
+ ...this.markupOptions,
+ columns: stream.columns - this.indentString.length - viewportShrink,
+ };
switch (stream.format) {
case 'ansi':
- return markupToAnsi(str, this.markupOptions, viewportWidth);
+ return markupToAnsi(str, gridMarkupOptions);
case 'html':
// TODO
- return markupToPlainText(str, this.markupOptions, viewportWidth);
+ return markupToPlainText(str, gridMarkupOptions);
case 'none':
- return markupToPlainText(str, this.markupOptions, viewportWidth);
+ return markupToPlainText(str, gridMarkupOptions);
case 'markup':
return {

MODIFY
packages/@romejs/diagnostics/DiagnosticsNormalizer.ts
packages/@romejs/diagnostics/DiagnosticsNormalizer.ts
import {Diagnostic, DiagnosticAdviceItem, DiagnosticLocation} from './types';
import {SourceMapConsumerCollection} from '@romejs/codec-source-map';
-import {NormalizeMarkupOptions, normalizeMarkup} from '@romejs/string-markup';
+import {
+ MarkupFormatNormalizeOptions,
+ normalizeMarkup,
+} from '@romejs/string-markup';
import {createBlessedDiagnosticMessage} from './descriptions';
export default class DiagnosticsNormalizer {
constructor(
- markupOptions?: NormalizeMarkupOptions,
+ markupOptions?: MarkupFormatNormalizeOptions,
sourceMaps?: SourceMapConsumerCollection,
) {
this.sourceMaps = sourceMaps;
@@ -21,7 +24,7 @@ export default class DiagnosticsNormalizer {
}
sourceMaps: undefined | SourceMapConsumerCollection;
- markupOptions: NormalizeMarkupOptions;
+ markupOptions: MarkupFormatNormalizeOptions;
hasMarkupOptions: boolean;
normalizeFilename(filename: undefined | string): undefined | string {

MODIFY
packages/@romejs/diagnostics/DiagnosticsProcessor.ts
packages/@romejs/diagnostics/DiagnosticsProcessor.ts
@@ -22,7 +22,7 @@ import {descriptions} from './descriptions';
import {matchesSuppression} from '@romejs/js-compiler';
import {SourceMapConsumerCollection} from '@romejs/codec-source-map';
import DiagnosticsNormalizer from './DiagnosticsNormalizer';
-import {NormalizeMarkupOptions} from '@romejs/string-markup';
+import {MarkupFormatNormalizeOptions} from '@romejs/string-markup';
type UniquePart =
| 'filename'
@@ -41,7 +41,7 @@ export type DiagnosticsProcessorOptions = {
max?: number;
onDiagnostics?: (diags: Diagnostics) => void;
origins?: Array<DiagnosticOrigin>;
- markupOptions?: NormalizeMarkupOptions;
+ markupOptions?: MarkupFormatNormalizeOptions;
};
const DEFAULT_UNIQUE: UniqueRules = [

MODIFY
packages/@romejs/js-compiler/lint/rules/noImportAssign.test.md
packages/@romejs/js-compiler/lint/rules/noImportAssign.test.md
@@ -180,7 +180,9 @@ import x from 'y';
```
import x from 'y';
-for (x in y);
+for (x in y) {
+ // empty
+}
```

MODIFY
packages/@romejs/string-markup/Grid.ts
packages/@romejs/string-markup/Grid.ts
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
- */ import {
+ */
+
+import {
ChildNode,
Children,
+ MarkupFormatGridOptions,
+ MarkupFormatOptions,
MarkupTagName,
TagNode,
TextNode,
@@ -14,14 +18,12 @@ import {
Number1,
ob1Add,
ob1Coerce1,
- ob1Dec,
ob1Get,
ob1Get1,
ob1Inc,
ob1Number1,
} from '@romejs/ob1';
import {formatAnsi} from './ansi';
-import {MarkupFormatOptions} from './format';
import {humanizeFileSize, humanizeTime} from '@romejs/string-utils';
import {
formatApprox,
@@ -30,6 +32,7 @@ import {
getFileLinkFilename,
getFileLinkText,
} from './tagFormatters';
+
type Cursor = {
line: Number1;
column: Number1;
@@ -50,8 +53,9 @@ function cursorToIndex(
type Ancestry = Array<TagNode>;
export default class Grid {
- constructor(opts: MarkupFormatOptions, viewportWidth: undefined | Number1) {
- this.viewportWidth = viewportWidth;
+ constructor(opts: MarkupFormatGridOptions) {
+ this.viewportWidth =
+ opts.columns === undefined ? undefined : ob1Coerce1(opts.columns);
this.markupOptions = opts;
this.cursor = {
@@ -66,11 +70,11 @@ export default class Grid {
}
canLineWrap: boolean;
- markupOptions: MarkupFormatOptions;
+ markupOptions: MarkupFormatGridOptions;
lines: Array<{
ranges: Array<{
- start: Number1;
- end: Number1;
+ start: number;
+ end: number;
ancestry: Ancestry;
}>;
columns: Array<string>;
@@ -119,8 +123,8 @@ export default class Grid {
const newRanges = ranges.map((range) => {
return {
- start: ob1Add(range.start, offset),
- end: ob1Add(range.end, offset),
+ start: range.start + offset,
+ end: range.end + offset,
ancestry: range.ancestry,
};
});
@@ -144,6 +148,11 @@ export default class Grid {
return ob1Coerce1(this.lines.length);
}
+ getLineWidth(lineIndex: number): number {
+ const line = this.lines[lineIndex];
+ return line === undefined ? 0 : line.columns.length;
+ }
+
getWidth(): Number1 {
return this.width;
}
@@ -173,13 +182,10 @@ export default class Grid {
let content = columns.join('');
// Sort ranges from last to first
- const sortedRanges = ranges.sort((a, b) => ob1Get1(b.end) - ob1Get1(a.end));
+ const sortedRanges = ranges.sort((a, b) => b.end - a.end);
for (const {start, end, ancestry} of sortedRanges) {
- const startIndex = ob1Get1(start) - 1;
- const endIndex = ob1Get1(end) - 1;
-
- let substr = content.slice(startIndex, endIndex);
+ let substr = content.slice(start, end);
// Format tags in reverse
for (let i = ancestry.length - 1; i >= 0; i--) {
@@ -189,8 +195,7 @@ export default class Grid {
substr = formatAnsi.reset(substr);
- content =
- content.slice(0, startIndex) + substr + content.slice(endIndex);
+ content = content.slice(0, start) + substr + content.slice(end);
}
lines.push(content);
@@ -215,13 +220,12 @@ export default class Grid {
}
moveCursor(cursor: Cursor) {
- this.fillCursor(cursor);
this.cursor = cursor;
}
moveCursorRight(columns: Number1 = ob1Number1) {
- if (this.doesOverflowViewport(ob1Inc(this.cursor.column))) {
- this.moveCursorDown();
+ if (this.doesOverflowViewport(this.cursor.column)) {
+ this.newline();
} else {
this.moveCursor({
line: this.cursor.line,
@@ -230,17 +234,22 @@ export default class Grid {
}
}
- moveCursorBottom() {
+ newline() {
+ this.moveCursorDown();
+ this.moveCursorStart();
+ }
+
+ moveCursorStart() {
this.moveCursor({
- line: this.getHeight(),
- column: this.cursor.column,
+ line: this.cursor.line,
+ column: ob1Number1,
});
}
moveCursorDown() {
this.moveCursor({
line: ob1Inc(this.cursor.line),
- column: ob1Number1,
+ column: this.cursor.column,
});
}
@@ -257,7 +266,7 @@ export default class Grid {
writeChar(char: string) {
if (char === '\n') {
- this.moveCursorDown();
+ this.newline();
return;
}
@@ -266,6 +275,10 @@ export default class Grid {
}
writeText(text: string, ancestry: Ancestry) {
+ if (text === '') {
+ return;
+ }
+
const start = this.getCursor();
const words = text.split(' ');
@@ -278,7 +291,7 @@ export default class Grid {
this.doesOverflowViewport(ob1Add(this.cursor.column, word.length - 1)) &&
!this.doesOverflowViewport(ob1Coerce1(word.length - 1));
if (willOverflow) {
- this.moveCursorDown();
+ this.newline();
}
for (const char of word) {
@@ -306,37 +319,58 @@ export default class Grid {
}
const end = this.getCursor();
- this.addRange(start, end, ancestry);
+ this.addCursorRange(start, end, ancestry);
}
- setRange(line: Number1, start: Number1, end: Number1, ancestry: Ancestry) {
+ setRange(line: number, start: number, end: number, ancestry: Ancestry) {
if (start === end) {
// Nothing to format. Empty tag.
return;
}
- this.lines[ob1Get1(line) - 1].ranges.push({
+ if (end < start) {
+ throw new Error(`end(${end}) < start(${start})`);
+ }
+
+ this.lines[line].ranges.push({
start,
end,
ancestry,
});
}
- addRange(start: Cursor, end: Cursor, ancestry: Ancestry) {
+ addCursorRange(_start: Cursor, _end: Cursor, ancestry: Ancestry) {
+ if (ancestry.length === 0) {
+ // No point storing a range without ancestry
+ return;
+ }
+
+ const start = cursorToIndex(_start);
+ const end = cursorToIndex(_end);
+
if (start.line === end.line) {
+ if (start.column === end.column) {
+ // Empty range
+ return;
+ }
+
this.setRange(start.line, start.column, end.column, ancestry);
} else {
- const width = this.getWidth();
-
// Add first line
- this.setRange(start.line, start.column, width, ancestry);
+ this.setRange(
+ start.line,
+ start.column,
+ this.getLineWidth(start.line),
+ ancestry,
+ );
- for (let line = ob1Get1(start.line) + 1; line < ob1Get1(end.line); line++) {
- this.setRange(ob1Coerce1(line), ob1Coerce1(1), width, ancestry);
+ // Add middle lines
+ for (let line = start.line + 1; line < end.line; line++) {
+ this.setRange(line, 0, this.getLineWidth(line), ancestry);
}
// Add last line
- this.setRange(end.line, ob1Number1, end.column, ancestry);
+ this.setRange(end.line, 0, end.column, ancestry);
}
}
@@ -373,7 +407,7 @@ export default class Grid {
// Could be an excessive column
return 0;
} else {
- const grid = new Grid(this.markupOptions, undefined);
+ const grid = new Grid({...this.markupOptions, columns: undefined});
grid.drawTag(field, ancestry);
return ob1Get1(grid.getSize().width);
}
@@ -416,7 +450,7 @@ export default class Grid {
const field = row[colIndex];
const width = ob1Coerce1(columnWidths[colIndex]);
- const grid = new Grid(this.markupOptions, width);
+ const grid = new Grid({...this.markupOptions, columns: ob1Get1(width)});
grid.drawTag(field, ancestry);
if (field.attributes.align === 'right') {
grid.alignRight();
@@ -426,45 +460,40 @@ export default class Grid {
this.moveCursorRight(ob1Inc(width));
}
- this.moveCursorBottom();
- this.moveCursorDown();
+ this.moveCursor({
+ line: ob1Inc(this.getHeight()),
+ column: ob1Number1,
+ });
}
}
drawGrid(grid: Grid) {
const {lines} = grid;
- const cursor = cursorToIndex(this.cursor);
+ const cursor = cursorToIndex(this.getCursor());
// Write
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
const {columns, ranges} = lines[lineIndex];
- const correctLine = ob1Coerce1(cursor.line + lineIndex + 1);
- const colOffset = ob1Coerce1(cursor.column);
+ const correctLine = cursor.line + lineIndex;
for (let colIndex = 0; colIndex < columns.length; colIndex++) {
const char = columns[colIndex];
this.writeToCursor(
{
- line: correctLine,
- column: ob1Add(colOffset, colIndex),
+ line: ob1Coerce1(correctLine + 1),
+ column: ob1Coerce1(cursor.column + colIndex + 1),
},
char,
);
}
- const rangeColOffset = ob1Dec(colOffset);
for (const range of ranges) {
- this.addRange(
- {
- line: correctLine,
- column: ob1Add(rangeColOffset, range.start),
- },
- {
- line: correctLine,
- column: ob1Add(rangeColOffset, range.end),
- },
+ this.setRange(
+ correctLine,
+ cursor.column + range.start,
+ cursor.column + range.end,
range.ancestry,
);
}
@@ -531,7 +560,19 @@ export default class Grid {
normalizeChild(child: ChildNode): Children {
if (child.type === 'Text') {
- return [child];
+ let {value} = child;
+
+ // Replace '\t' with ' '
+ // Remove '\r' in case it snuck in as file contents
+ value = value.replace(/\t/g, ' ');
+ value = value.replace(/\r/g, '');
+
+ return [
+ {
+ type: 'Text',
+ value,
+ },
+ ];
}
const tag = child;

MODIFY
packages/@romejs/string-markup/format.ts
packages/@romejs/string-markup/format.ts
* LICENSE file in the root directory of this source tree.
*/
-import {Children, TagNode} from './types';
+import {
+ Children,
+ MarkupFormatGridOptions,
+ MarkupFormatNormalizeOptions,
+ TagNode,
+} from './types';
import {parseMarkup} from './parse';
-import {AbsoluteFilePath} from '@romejs/path';
import {escapeMarkup} from './escape';
import Grid from './Grid';
-import {ob1Coerce1, ob1Get1} from '@romejs/ob1';
+import {ob1Get1} from '@romejs/ob1';
import {
formatGrammarNumber,
getFileLinkFilename,
getFileLinkText,
} from './tagFormatters';
-export type MarkupFormatFilenameNormalizer = (filename: string) => string;
-
-export type MarkupFormatFilenameHumanizer = (
- filename: string,
-) => undefined | string;
-
-export type MarkupFormatOptions = {
- normalizeFilename?: MarkupFormatFilenameNormalizer;
- humanizeFilename?: MarkupFormatFilenameHumanizer;
- cwd?: AbsoluteFilePath;
-};
function buildTag(
tag: TagNode,
inner: string,
- opts: NormalizeMarkupOptions,
+ opts: MarkupFormatNormalizeOptions,
): string {
let {attributes} = tag;
@@ -84,7 +77,7 @@ function buildTag(
function normalizeMarkupChildren(
children: Children,
- opts: NormalizeMarkupOptions,
+ opts: MarkupFormatNormalizeOptions,
): string {
// Sometimes we'll populate the inner text of a tag with no children
if (children.length === 0) {
@@ -107,21 +100,16 @@ function normalizeMarkupChildren(
export function markupToPlainTextString(
input: string,
- opts: MarkupFormatOptions = {},
- columns?: number,
+ opts: MarkupFormatGridOptions = {},
): string {
- return markupToPlainText(input, opts, columns).lines.join('\n');
+ return markupToPlainText(input, opts).lines.join('\n');
}
export function markupToPlainText(
input: string,
- opts: MarkupFormatOptions = {},
- columns?: number,
+ opts: MarkupFormatGridOptions = {},
): MarkupLinesAndWidth {
- const grid = new Grid(
- opts,
- columns === undefined ? undefined : ob1Coerce1(columns),
- );
+ const grid = new Grid(opts);
grid.drawRoot(parseMarkup(input));
return {
width: ob1Get1(grid.getWidth()),
@@ -136,13 +124,9 @@ export type MarkupLinesAndWidth = {
export function markupToAnsi(
input: string,
- opts: MarkupFormatOptions = {},
- columns?: number,
+ opts: MarkupFormatGridOptions = {},
): MarkupLinesAndWidth {
- const grid = new Grid(
- opts,
- columns === undefined ? undefined : ob1Coerce1(columns),
- );
+ const grid = new Grid(opts);
grid.drawRoot(parseMarkup(input));
@@ -152,13 +136,9 @@ export function markupToAnsi(
};
}
-export type NormalizeMarkupOptions = MarkupFormatOptions & {
- stripPositions?: boolean;
-};
-
export function normalizeMarkup(
input: string,
- opts: NormalizeMarkupOptions = {},
+ opts: MarkupFormatNormalizeOptions = {},
): string {
return normalizeMarkupChildren(parseMarkup(input), opts);
}

MODIFY
packages/@romejs/string-markup/index.ts
packages/@romejs/string-markup/index.ts
*/
export * from './parse';
+
export {
MarkupFormatFilenameHumanizer,
MarkupFormatFilenameNormalizer,
+ MarkupFormatGridOptions,
+ MarkupFormatNormalizeOptions,
MarkupFormatOptions,
- NormalizeMarkupOptions,
+} from './types';
+
+export {
markupToAnsi,
markupToPlainText,
markupToPlainTextString,
normalizeMarkup,
} from './format';
+
export * from './escape';
export * from './ansi';

MODIFY
packages/@romejs/string-markup/types.ts
packages/@romejs/string-markup/types.ts
import {BaseTokens, SimpleToken, ValueToken} from '@romejs/parser-core';
import {Dict} from '@romejs/typescript-helpers';
+import {AbsoluteFilePath} from '@romejs/path';
export type Tokens = BaseTokens & {
Text: ValueToken<'Text', string>;
@@ -63,3 +64,23 @@ export type MarkupTagName =
| 'tr'
| 'td'
| 'nobr';
+
+export type MarkupFormatFilenameNormalizer = (filename: string) => string;
+
+export type MarkupFormatFilenameHumanizer = (
+ filename: string,
+) => undefined | string;
+
+export type MarkupFormatOptions = {
+ normalizeFilename?: MarkupFormatFilenameNormalizer;
+ humanizeFilename?: MarkupFormatFilenameHumanizer;
+ cwd?: AbsoluteFilePath;
+};
+
+export type MarkupFormatGridOptions = MarkupFormatOptions & {
+ columns?: number;
+};
+
+export type MarkupFormatNormalizeOptions = MarkupFormatOptions & {
+ stripPositions?: boolean;
+};

Fix markup grid regressions