Skip to content

Commit

Permalink
WIP: New primitive to fill a line with as many docs as possible
Browse files Browse the repository at this point in the history
  • Loading branch information
karl committed Apr 5, 2017
1 parent b2c63b2 commit 9444106
Show file tree
Hide file tree
Showing 7 changed files with 192 additions and 323 deletions.
7 changes: 7 additions & 0 deletions src/doc-builders.js
Expand Up @@ -51,6 +51,12 @@ function conditionalGroup(states, opts) {
);
}

function fill(parts) {
parts.forEach(assertDoc);

return { type: "fill", parts };
}

function ifBreak(breakContents, flatContents) {
if (breakContents) {
assertDoc(breakContents);
Expand Down Expand Up @@ -100,6 +106,7 @@ module.exports = {
literalline,
group,
conditionalGroup,
fill,
lineSuffix,
lineSuffixBoundary,
breakParent,
Expand Down
11 changes: 11 additions & 0 deletions src/doc-debug.js
Expand Up @@ -91,10 +91,21 @@ function printDoc(doc) {
")";
}

if (doc.type === "fill") {
return ("fill") +
"(" +
doc.parts.map(printDoc).join(", ") +
")";
}

if (doc.type === "line-suffix") {
return "lineSuffix(" + printDoc(doc.contents) + ")";
}

if (doc.type === "line-suffix-boundary") {
return "lineSuffixBoundary()";
}

throw new Error("Unknown doc type " + doc.type);
}

Expand Down
65 changes: 64 additions & 1 deletion src/doc-printer.js
@@ -1,9 +1,16 @@
"use strict";

var docBuilders = require("./doc-builders");
var concat = docBuilders.concat;
var line = docBuilders.line;
var softline = docBuilders.softline;
var fill = docBuilders.fill;
var ifBreak = docBuilders.ifBreak;

const MODE_BREAK = 1;
const MODE_FLAT = 2;

function fits(next, restCommands, width) {
function fits(next, restCommands, width, mustBeFlat = false) {
let restIdx = restCommands.length;
const cmds = [next];
while (width >= 0) {
Expand Down Expand Up @@ -39,8 +46,19 @@ function fits(next, restCommands, width) {

break;
case "group":
if (mustBeFlat && doc.break) {
return false;
}
cmds.push([ind, doc.break ? MODE_BREAK : mode, doc.contents]);

break;
case "fill":
// include number of spaces
cmds.push([ind, mode, " ".repeat(doc.parts.length - 1)]);
for (var i = doc.parts.length - 1; i >= 0; i--) {
cmds.push([ind, mode, doc.parts[i]]);
}

break;
case "if-break":
if (mode === MODE_BREAK) {
Expand Down Expand Up @@ -176,6 +194,51 @@ function printDocToString(doc, width, newLine) {
break;
}
break;
case "fill":
// shouldRemeasure = false;
let rem = width - pos;

const parts = doc.parts;
if (parts.length === 1) {
cmds.push([ind, MODE_BREAK, parts[0]]);
break;
}

const first = parts[0];
const second = parts[1];
const remaining = parts.slice(1);
const firstCmd = [ind, MODE_FLAT, first];
const remainingCmd = [ind, MODE_BREAK, fill(remaining)];

const firstAndSecondCmd = [ind, MODE_FLAT, concat([first, second])];

if (fits(firstAndSecondCmd, cmds, rem, true)) {
// console.log('first and second fit', [first, second]);
cmds.push(remainingCmd)
cmds.push([ind, MODE_FLAT, line]);
cmds.push(firstCmd);
} else if (fits(firstCmd, cmds, width - rem, true)) {
// console.log('only first fits', [first, second]);
cmds.push(remainingCmd)
if (typeof second !== 'string' || typeof first !== 'string') {
cmds.push([ind, MODE_BREAK, "{' '}"]);
}
cmds.push([ind, MODE_BREAK, line]);
cmds.push(firstCmd);
} else {
// console.log('neither fit', [first, second]);
out.push(newLine + " ".repeat(ind));
pos = ind;

cmds.push(remainingCmd);
if (typeof first !== 'string') {
cmds.push([ind, MODE_BREAK, line]);
cmds.push([ind, MODE_BREAK, "{' '}"]);
}
cmds.push([ind, MODE_BREAK, line]);
cmds.push([ind, MODE_BREAK, first]);
}
break;
case "if-break":
if (mode === MODE_BREAK) {
if (doc.breakContents) {
Expand Down
146 changes: 84 additions & 62 deletions src/printer.js
Expand Up @@ -16,6 +16,7 @@ var literalline = docBuilders.literalline;
var group = docBuilders.group;
var indent = docBuilders.indent;
var conditionalGroup = docBuilders.conditionalGroup;
var fill = docBuilders.fill;
var ifBreak = docBuilders.ifBreak;
var breakParent = docBuilders.breakParent;
var lineSuffixBoundary = docBuilders.lineSuffixBoundary;
Expand Down Expand Up @@ -655,7 +656,7 @@ function genericPrintNoParens(path, options, print) {
var isTypeAnnotation = n.type === "ObjectTypeAnnotation";
var isTypeScriptTypeAnnotaion = n.type === "TSTypeLiteral";
// Leave this here because we *might* want to make this
// configurable later -- flow accepts ";" for type separators,
// configurable later -- flow accepts ";" for type separators,
// typescript accepts ";" and newlines
var separator = isTypeAnnotation ? "," : ",";
var fields = [];
Expand Down Expand Up @@ -1069,7 +1070,7 @@ function genericPrintNoParens(path, options, print) {

case "ForOfStatement":
case "ForAwaitStatement":
// Babylon 7 removed ForAwaitStatement in favor of ForOfStatement
// Babylon 7 removed ForAwaitStatement in favor of ForOfStatement
// with `"await": true`:
// https://github.com/estree/estree/pull/138
const isAwait = (n.type === "ForAwaitStatement" || n.await);
Expand Down Expand Up @@ -1853,8 +1854,8 @@ function genericPrintNoParens(path, options, print) {
return "void";
case "TSAsExpression":
return concat([
path.call(print, "expression"),
" as ",
path.call(print, "expression"),
" as ",
path.call(print, "typeAnnotation"),
])
case "TSArrayType":
Expand All @@ -1868,30 +1869,30 @@ function genericPrintNoParens(path, options, print) {
return concat([path.call(print, "typeName")]);
case "TSCallSignature":
return concat([
"(",
join(", ", path.map(print, "parameters")),
"(",
join(", ", path.map(print, "parameters")),
"): ",
path.call(print, "typeAnnotation"),
path.call(print, "typeAnnotation"),
]);
case "TSConstructSignature":
return concat([
"new (",
join(", ", path.map(print, "parameters")),
"new (",
join(", ", path.map(print, "parameters")),
"): ",
path.call(print, "typeAnnotation"),
path.call(print, "typeAnnotation"),
]);
case "TSTypeQuery":
return concat(["typeof ", path.call(print, "exprName")]);
case "TSParenthesizedType":
return concat(["(", path.call(print, "typeAnnotation"), ")"]);
case "TSIndexSignature":
return concat([
"[",
"[",
// This should only contain a single element, however TypeScript parses
// it using parseDelimitedList that uses commas as delimiter.
join(", ", path.map(print, "parameters")),
join(", ", path.map(print, "parameters")),
"]: ",
path.call(print, "typeAnnotation"),
path.call(print, "typeAnnotation"),
]);
// TODO
case "ClassHeritage":
Expand Down Expand Up @@ -2762,62 +2763,83 @@ function printJSXChildren(path, options, print, jsxWhitespace) {
: util.htmlEscapeInsideAngleBracket(child.value);
const value = partiallyEscapedValue.replace(/\u00a0/g, " ");

if (/\S/.test(value)) {
// treat each line of text as its own entity
value.split(/(\n\s*)/).forEach(line => {
const newlines = line.match(/\n/g);
if (newlines) {
children.push(hardline);

// allow one extra newline
if (newlines.length > 1) {
children.push(hardline);
}
return;
}

const beginSpace = /^\s+/.test(line);
if (beginSpace) {
children.push(jsxWhitespace);
children.push(softline);
}
// // console.log('value', value);
// if (/\S/.test(value)) {
// value.split(/(\s+)/).forEach(word => {
// // const newlines = word.match(/\n/g);
// // if (newlines) {
// // children.push(hardline);
// //
// // // allow one extra newline
// // if (newlines.length > 1) {
// // children.push(hardline);
// // }
// // return;
// // }
//
// const space = /^\s+$/.test(word);
// if (space) {
// words.push(line);
// return;
// }
//
// const stripped = word.replace(/^\s+|\s+$/g, "");
// if (stripped) {
// words.push(stripped);
// }
// });
//
// if (!isLineNext(util.getLast(words))) {
// words.push(softline);
// }
// } else if (/\n/.test(value)) {
// words.push(hardline);
//
// // allow one extra newline
// if (value.match(/\n/g).length > 1) {
// words.push(hardline);
// }
// } else if (/\s/.test(value)) {
// // whitespace-only without newlines,
// // eg; a single space separating two elements
// words.push(jsxWhitespace);
// words.push(softline);
// }

value.replace(/^\s+|\s+$/g, "").split(/(\s+)/).forEach(word => {
if (word.length === 0) {
return;
}

const stripped = line.replace(/^\s+|\s+$/g, "");
if (stripped) {
children.push(stripped);
}
const newlines = word.match(/\n/g);
if (newlines) {
children.push(hardline);

const endSpace = /\s+$/.test(line);
if (endSpace) {
children.push(softline);
children.push(jsxWhitespace);
// allow one extra newline
if (newlines.length > 1) {
children.push(hardline);
}
});

if (!isLineNext(util.getLast(children))) {
children.push(softline);
return;
}
} else if (/\n/.test(value)) {
children.push(hardline);

// allow one extra newline
if (value.match(/\n/g).length > 1) {
children.push(hardline);
const space = /^\s+$/.test(word);
if (space) {
// children.push(line);
return;
}
} else if (/\s/.test(value)) {
// whitespace-only without newlines,
// eg; a single space separating two elements
children.push(jsxWhitespace);
children.push(softline);
}

children.push(word);
});

} else {
children.push(print(childPath));

// add a line unless it's followed by a JSX newline
let next = n.children[i + 1];
if (!(next && /^\s*\n/.test(next.value))) {
children.push(softline);
}
//
// // add a line unless it's followed by a JSX newline
// let next = n.children[i + 1];
// if (!(next && /^\s*\n/.test(next.value))) {
// children.push(softline);
// }
}
},
"children"
Expand Down Expand Up @@ -2932,7 +2954,7 @@ function printJSXElement(path, options, print) {
groups.map(
contents =>
Array.isArray(contents)
? conditionalGroup([concat(contents)])
? fill(contents)
: contents
)
)
Expand All @@ -2957,7 +2979,7 @@ function printJSXElement(path, options, print) {
}

return conditionalGroup([
group(concat([openingLines, concat(children), closingLines])),
group(concat([openingLines, fill(children), closingLines])),
multiLineElem
]);
}
Expand Down

0 comments on commit 9444106

Please sign in to comment.