Add a new `trim` command to trim whitespaces in the current line (#4772)
parent
30ec30daa9
commit
7920e6fdde
|
@ -237,6 +237,14 @@ declare function dedentToRoot(doc: Doc): Doc;
|
||||||
|
|
||||||
This will dedent the current indentation to the root marked by `markAsRoot`.
|
This will dedent the current indentation to the root marked by `markAsRoot`.
|
||||||
|
|
||||||
|
### trim
|
||||||
|
|
||||||
|
```ts
|
||||||
|
declare var trim: Doc;
|
||||||
|
```
|
||||||
|
|
||||||
|
This will trim any whitespace or tab character on the current line. This is used for preprocessor directives.
|
||||||
|
|
||||||
### cursor
|
### cursor
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
|
|
|
@ -113,6 +113,7 @@ function lineSuffix(contents) {
|
||||||
|
|
||||||
const lineSuffixBoundary = { type: "line-suffix-boundary" };
|
const lineSuffixBoundary = { type: "line-suffix-boundary" };
|
||||||
const breakParent = { type: "break-parent" };
|
const breakParent = { type: "break-parent" };
|
||||||
|
const trim = { type: "trim" };
|
||||||
const line = { type: "line" };
|
const line = { type: "line" };
|
||||||
const softline = { type: "line", soft: true };
|
const softline = { type: "line", soft: true };
|
||||||
const hardline = concat([{ type: "line", hard: true }, breakParent]);
|
const hardline = concat([{ type: "line", hard: true }, breakParent]);
|
||||||
|
@ -167,6 +168,7 @@ module.exports = {
|
||||||
cursor,
|
cursor,
|
||||||
breakParent,
|
breakParent,
|
||||||
ifBreak,
|
ifBreak,
|
||||||
|
trim,
|
||||||
indent,
|
indent,
|
||||||
align,
|
align,
|
||||||
addAlignmentToDoc,
|
addAlignmentToDoc,
|
||||||
|
|
|
@ -59,6 +59,10 @@ function printDoc(doc) {
|
||||||
return "breakParent";
|
return "breakParent";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (doc.type === "trim") {
|
||||||
|
return "trim";
|
||||||
|
}
|
||||||
|
|
||||||
if (doc.type === "concat") {
|
if (doc.type === "concat") {
|
||||||
return "[" + doc.parts.map(printDoc).join(", ") + "]";
|
return "[" + doc.parts.map(printDoc).join(", ") + "]";
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,9 +109,37 @@ function generateInd(ind, newPart, options) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function trim(out) {
|
||||||
|
if (out.length === 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let trimCount = 0;
|
||||||
|
|
||||||
|
// Trim whitespace at the end of line
|
||||||
|
while (
|
||||||
|
out.length > 0 &&
|
||||||
|
typeof out[out.length - 1] === "string" &&
|
||||||
|
out[out.length - 1].match(/^[ \t]*$/)
|
||||||
|
) {
|
||||||
|
trimCount += out.pop().length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out.length && typeof out[out.length - 1] === "string") {
|
||||||
|
const trimmed = out[out.length - 1].replace(/[ \t]*$/, "");
|
||||||
|
trimCount += out[out.length - 1].length - trimmed.length;
|
||||||
|
out[out.length - 1] = trimmed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return trimCount;
|
||||||
|
}
|
||||||
|
|
||||||
function fits(next, restCommands, width, options, mustBeFlat) {
|
function fits(next, restCommands, width, options, mustBeFlat) {
|
||||||
let restIdx = restCommands.length;
|
let restIdx = restCommands.length;
|
||||||
const cmds = [next];
|
const cmds = [next];
|
||||||
|
// `out` is only used for width counting because `trim` requires to look
|
||||||
|
// backwards for space characters.
|
||||||
|
const out = [];
|
||||||
while (width >= 0) {
|
while (width >= 0) {
|
||||||
if (cmds.length === 0) {
|
if (cmds.length === 0) {
|
||||||
if (restIdx === 0) {
|
if (restIdx === 0) {
|
||||||
|
@ -130,6 +158,8 @@ function fits(next, restCommands, width, options, mustBeFlat) {
|
||||||
const doc = x[2];
|
const doc = x[2];
|
||||||
|
|
||||||
if (typeof doc === "string") {
|
if (typeof doc === "string") {
|
||||||
|
out.push(doc);
|
||||||
|
|
||||||
width -= getStringWidth(doc);
|
width -= getStringWidth(doc);
|
||||||
} else {
|
} else {
|
||||||
switch (doc.type) {
|
switch (doc.type) {
|
||||||
|
@ -146,6 +176,10 @@ function fits(next, restCommands, width, options, mustBeFlat) {
|
||||||
case "align":
|
case "align":
|
||||||
cmds.push([makeAlign(ind, doc.n, options), mode, doc.contents]);
|
cmds.push([makeAlign(ind, doc.n, options), mode, doc.contents]);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "trim":
|
||||||
|
width += trim(out);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case "group":
|
case "group":
|
||||||
if (mustBeFlat && doc.break) {
|
if (mustBeFlat && doc.break) {
|
||||||
|
@ -184,6 +218,8 @@ function fits(next, restCommands, width, options, mustBeFlat) {
|
||||||
case MODE_FLAT:
|
case MODE_FLAT:
|
||||||
if (!doc.hard) {
|
if (!doc.hard) {
|
||||||
if (!doc.soft) {
|
if (!doc.soft) {
|
||||||
|
out.push(" ");
|
||||||
|
|
||||||
width -= 1;
|
width -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,6 +280,10 @@ function printDocToString(doc, options) {
|
||||||
case "align":
|
case "align":
|
||||||
cmds.push([makeAlign(ind, doc.n, options), mode, doc.contents]);
|
cmds.push([makeAlign(ind, doc.n, options), mode, doc.contents]);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case "trim":
|
||||||
|
pos -= trim(out);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case "group":
|
case "group":
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
|
@ -471,24 +511,7 @@ function printDocToString(doc, options) {
|
||||||
pos = 0;
|
pos = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (out.length > 0) {
|
pos -= trim(out);
|
||||||
// Trim whitespace at the end of line
|
|
||||||
while (
|
|
||||||
out.length > 0 &&
|
|
||||||
typeof out[out.length - 1] === "string" &&
|
|
||||||
out[out.length - 1].match(/^[ \t]*$/)
|
|
||||||
) {
|
|
||||||
out.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (out.length && typeof out[out.length - 1] === "string") {
|
|
||||||
out[out.length - 1] = out[out.length - 1].replace(
|
|
||||||
/[ \t]*$/,
|
|
||||||
""
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out.push(newLine + ind.value);
|
out.push(newLine + ind.value);
|
||||||
pos = ind.length;
|
pos = ind.length;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const prettier = require("prettier/local");
|
||||||
|
const docPrinter = prettier.doc.printer;
|
||||||
|
const docBuilders = prettier.doc.builders;
|
||||||
|
|
||||||
|
const printDocToString = docPrinter.printDocToString;
|
||||||
|
const concat = docBuilders.concat;
|
||||||
|
const hardline = docBuilders.hardline;
|
||||||
|
const literalline = docBuilders.literalline;
|
||||||
|
const trim = docBuilders.trim;
|
||||||
|
const indent = docBuilders.indent;
|
||||||
|
const markAsRoot = docBuilders.markAsRoot;
|
||||||
|
|
||||||
|
describe("markAsRoot", () => {
|
||||||
|
test.each([
|
||||||
|
[
|
||||||
|
"with hardline will insert a newline with current indentation",
|
||||||
|
concat([indent(markAsRoot(indent(hardline))), "123"]),
|
||||||
|
"\n 123"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"with literalline will insert a newline with root indentation",
|
||||||
|
concat([indent(markAsRoot(indent(literalline))), "123"]),
|
||||||
|
"\n 123"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"followed by trim will trims up to the the first column, ignoring indented root",
|
||||||
|
concat([indent(markAsRoot(indent(literalline))), trim, "123"]),
|
||||||
|
"\n123"
|
||||||
|
]
|
||||||
|
])("%s", (_, doc, expected) => {
|
||||||
|
const result = printDocToString(doc, { printWidth: 80, tabWidth: 2 });
|
||||||
|
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
expect(result.formatted).toEqual(expected);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,59 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const prettier = require("prettier/local");
|
||||||
|
const docPrinter = prettier.doc.printer;
|
||||||
|
const docBuilders = prettier.doc.builders;
|
||||||
|
|
||||||
|
const printDocToString = docPrinter.printDocToString;
|
||||||
|
const concat = docBuilders.concat;
|
||||||
|
const line = docBuilders.line;
|
||||||
|
const trim = docBuilders.trim;
|
||||||
|
const group = docBuilders.group;
|
||||||
|
const indent = docBuilders.indent;
|
||||||
|
|
||||||
|
// These tests don't use `runPrettier` because `trim` is not used by any
|
||||||
|
// bundled parser (only third-party plugins).
|
||||||
|
|
||||||
|
describe("trim", () => {
|
||||||
|
test.each([
|
||||||
|
["trims the current line", group(concat(["hello ", trim])), "hello"],
|
||||||
|
[
|
||||||
|
"trims existing indentation",
|
||||||
|
group(
|
||||||
|
concat([
|
||||||
|
"function()",
|
||||||
|
line,
|
||||||
|
"{",
|
||||||
|
indent(
|
||||||
|
concat([
|
||||||
|
line,
|
||||||
|
group(concat([trim, "#if DEBUG"])),
|
||||||
|
line,
|
||||||
|
"alert(42);",
|
||||||
|
line,
|
||||||
|
group(concat([trim, "#endif"]))
|
||||||
|
])
|
||||||
|
),
|
||||||
|
line,
|
||||||
|
"}"
|
||||||
|
])
|
||||||
|
),
|
||||||
|
`function()
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
alert(42);
|
||||||
|
#endif
|
||||||
|
}`
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"ignores trimmed characters when fitting the line",
|
||||||
|
group(concat(["hello ", " ", trim, line, "world!"])),
|
||||||
|
"hello world!"
|
||||||
|
]
|
||||||
|
])("%s", (_, doc, expected) => {
|
||||||
|
const result = printDocToString(doc, { printWidth: 12, tabWidth: 2 });
|
||||||
|
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
expect(result.formatted).toEqual(expected);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue