Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make the formatter smarter about mutli-line steps #596

Merged
merged 1 commit into from
Jun 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading