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`.
|
||||
|
||||
### 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
|
||||
|
||||
```ts
|
||||
|
|
|
@ -113,6 +113,7 @@ function lineSuffix(contents) {
|
|||
|
||||
const lineSuffixBoundary = { type: "line-suffix-boundary" };
|
||||
const breakParent = { type: "break-parent" };
|
||||
const trim = { type: "trim" };
|
||||
const line = { type: "line" };
|
||||
const softline = { type: "line", soft: true };
|
||||
const hardline = concat([{ type: "line", hard: true }, breakParent]);
|
||||
|
@ -167,6 +168,7 @@ module.exports = {
|
|||
cursor,
|
||||
breakParent,
|
||||
ifBreak,
|
||||
trim,
|
||||
indent,
|
||||
align,
|
||||
addAlignmentToDoc,
|
||||
|
|
|
@ -59,6 +59,10 @@ function printDoc(doc) {
|
|||
return "breakParent";
|
||||
}
|
||||
|
||||
if (doc.type === "trim") {
|
||||
return "trim";
|
||||
}
|
||||
|
||||
if (doc.type === "concat") {
|
||||
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) {
|
||||
let restIdx = restCommands.length;
|
||||
const cmds = [next];
|
||||
// `out` is only used for width counting because `trim` requires to look
|
||||
// backwards for space characters.
|
||||
const out = [];
|
||||
while (width >= 0) {
|
||||
if (cmds.length === 0) {
|
||||
if (restIdx === 0) {
|
||||
|
@ -130,6 +158,8 @@ function fits(next, restCommands, width, options, mustBeFlat) {
|
|||
const doc = x[2];
|
||||
|
||||
if (typeof doc === "string") {
|
||||
out.push(doc);
|
||||
|
||||
width -= getStringWidth(doc);
|
||||
} else {
|
||||
switch (doc.type) {
|
||||
|
@ -146,6 +176,10 @@ function fits(next, restCommands, width, options, mustBeFlat) {
|
|||
case "align":
|
||||
cmds.push([makeAlign(ind, doc.n, options), mode, doc.contents]);
|
||||
|
||||
break;
|
||||
case "trim":
|
||||
width += trim(out);
|
||||
|
||||
break;
|
||||
case "group":
|
||||
if (mustBeFlat && doc.break) {
|
||||
|
@ -184,6 +218,8 @@ function fits(next, restCommands, width, options, mustBeFlat) {
|
|||
case MODE_FLAT:
|
||||
if (!doc.hard) {
|
||||
if (!doc.soft) {
|
||||
out.push(" ");
|
||||
|
||||
width -= 1;
|
||||
}
|
||||
|
||||
|
@ -244,6 +280,10 @@ function printDocToString(doc, options) {
|
|||
case "align":
|
||||
cmds.push([makeAlign(ind, doc.n, options), mode, doc.contents]);
|
||||
|
||||
break;
|
||||
case "trim":
|
||||
pos -= trim(out);
|
||||
|
||||
break;
|
||||
case "group":
|
||||
switch (mode) {
|
||||
|
@ -471,24 +511,7 @@ function printDocToString(doc, options) {
|
|||
pos = 0;
|
||||
}
|
||||
} else {
|
||||
if (out.length > 0) {
|
||||
// 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]*$/,
|
||||
""
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pos -= trim(out);
|
||||
out.push(newLine + ind.value);
|
||||
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