Skip to content

Commit

Permalink
Make the formatter smarter about mutli-line steps (#596)
Browse files Browse the repository at this point in the history
  • Loading branch information
bakkot authored Jun 8, 2024
1 parent fd938c8 commit 13e1fb3
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 13 deletions.
2 changes: 1 addition & 1 deletion src/expr-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type SimpleLocation = { start: { offset: number }; end: { offset: number } };
type BareText = {
name: 'text';
contents: string;
location: { start: { offset: number }; end: { offset: number } };
location: SimpleLocation;
}; // like TextNode, but with less complete location information
type ProsePart = FragmentNode | BareText;
type List = {
Expand Down
39 changes: 37 additions & 2 deletions src/formatter/ecmarkdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,32 @@ async function printListNode(
return output;
}

function commonIndent(lines: string[]) {
if (lines.length === 0) {
return '';
}
let common = lines[0].match(/^[ \t]+/)?.[0];
if (common == null) {
return '';
}
for (let i = 1; i < lines.length; ++i) {
const line = lines[i];
let j = 0;
for (; j < line.length && j < common.length; ++j) {
if (common[j] !== line[j]) {
break;
}
}
if (j <= 0) {
return '';
}
if (j < common.length) {
common = common.slice(0, j);
}
}
return common;
}

async function printStep(
source: string,
item: OrderedListItemNode | UnorderedListItemNode,
Expand All @@ -48,7 +74,14 @@ async function printStep(
.join(', ');
output.appendText(`[${joined}] `);
}
const contents = await printFragments(source, item.contents, indent + 1);

const stepSource = source.substring(
item.location.start.offset,
item.contents[item.contents.length - 1].location.end.offset,
);
const stepIndent = commonIndent(stepSource.split('\n').slice(1));

const contents = await printFragments(source, item.contents, indent + 1, stepIndent);
// this is a bit gross, but whatever
contents.lines[0] = contents.lines[0].trimStart();
output.append(contents);
Expand All @@ -69,6 +102,8 @@ export async function printFragments(
source: string,
contents: FragmentNode[],
indent: number,
// for steps which are split over multiple lines, this is the common indent among them
commonIndent: string = '',
): Promise<LineBuilder> {
const output = new LineBuilder(indent);
let skipNextElement = false;
Expand All @@ -82,7 +117,7 @@ export async function printFragments(
// so don't even bother
const { start, end } = node.location;
const originalText = source.substring(start.offset, end.offset);
output.append(printText(originalText, indent));
output.append(printText(originalText, indent, commonIndent));
break;
}
case 'comment': {
Expand Down
5 changes: 2 additions & 3 deletions src/formatter/line-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export class LineBuilder {
if (text === '') {
return;
}
this.last = ' '.repeat(this.indent);
this.last += ' '.repeat(this.indent);
}
this.last += allowMultiSpace ? text : text.replace(/ +/g, ' ');
}
Expand All @@ -80,7 +80,6 @@ export class LineBuilder {
}

linebreak(): void {
// console.log('linebreak');
if (this.isEmpty()) {
this.firstLineIsPartial = false;
return;
Expand Down Expand Up @@ -121,6 +120,6 @@ export class LineBuilder {
// when firstLineIsPartial, we don't indent the first line
return false;
}
return this.last === '';
return /^ *$/.test(this.last);
}
}
31 changes: 24 additions & 7 deletions src/formatter/text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function isBadNumericReference(codePoint: number) {
return false;
}

export function printText(text: string, indent: number): LineBuilder {
export function printText(text: string, indent: number, commonIndent: string = ''): LineBuilder {
const output: LineBuilder = new LineBuilder(indent);
if (text === '') {
return output;
Expand Down Expand Up @@ -64,25 +64,42 @@ export function printText(text: string, indent: number): LineBuilder {
const leadingSpace = text[0] === ' ' || text[0] === '\t';
const trailingSpace = text[text.length - 1] === ' ' || text[text.length - 1] === '\t';

const lines = text.split('\n').map(l => l.trim());
const lines = text.split('\n').map((l, i) => {
if (i === 0 || commonIndent === '' || !l.startsWith(commonIndent)) {
return l.trim();
}
const withoutIndent = l.substring(commonIndent.length);
const moreIndent = withoutIndent.match(/^ +/)?.[0];
if (moreIndent) {
return { indent: moreIndent, line: l.trim() };
}
return l.trim();
});

if (leadingSpace) {
output.appendText(' ');
}
if (lines.length === 1) {
if (lines[0] !== '') {
output.appendText(lines[0]);
output.appendText(lines[0] as string);
if (trailingSpace) {
output.appendText(' ');
}
}
return output;
}
for (let i = 0; i < lines.length - 1; ++i) {
output.appendText(lines[i]);
output.linebreak();
for (let i = 0; i < lines.length; ++i) {
const line = lines[i];
if (typeof line === 'string') {
output.appendText(line);
} else {
output.last = line.indent;
output.appendText(line.line);
}
if (i < lines.length - 1) {
output.linebreak();
}
}
output.appendText(lines[lines.length - 1]);
if (trailingSpace && output.last !== '') {
output.appendText(' ');
}
Expand Down
23 changes: 23 additions & 0 deletions test/formatter.js
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,29 @@ describe('algorithm formatting', () => {
);
});

it('multiline steps', async () => {
await assertDocFormatsAs(
`
<emu-alg>
1. Return the Record {
[[Precision]]: *"minute"*,
[[Unit]]: *"minute"*,
[[Increment]]: 1
}.
</emu-alg>
`,
dedentKeepingTrailingNewline`
<emu-alg>
1. Return the Record {
[[Precision]]: *"minute"*,
[[Unit]]: *"minute"*,
[[Increment]]: 1
}.
</emu-alg>
`,
);
});

it('nested html', async () => {
await assertDocFormatsAs(
`
Expand Down

0 comments on commit 13e1fb3

Please sign in to comment.