Add debugging support for doc IR (#347)

This PR adds two things:

`--debug-print-doc` command that prints the formatted doc

```js
echo "<div>&lt;</div>" | ./bin/prettier.js --stdin --debug-print-doc
[
  groupConditional(
    group([
      group([ "<", "div", group([ indent(2, []), softline() ]), ">" ]),
      "&lt;",
      "</",
      "div",
      ">"
    ]),
    [
      group([
        group([ "<", "div", group([ indent(2, []), softline() ]), ">" ]),
        "&lt;",
        "</",
        "div",
        ">"
      ]),
      group([
        group([ "<", "div", group([ indent(2, []), softline() ]), ">" ]),
        indent(2, groupBreak([ hardline(), "&lt;" ])),
        hardline(),
        "</",
        "div",
        ">"
      ])
    ]
  ),
  ";",
  hardline()
];
```

The ability to view the IR in real time on the browser display:

![image](https://cloud.githubusercontent.com/assets/197597/22134741/4f172f20-de7e-11e6-84bc-5f813976dc19.png)

The way it works is pretty cool, we take the doc IR and print a valid JavaScript string out of it, that we then send to prettier in order to make it look good :)
This commit is contained in:
Christopher Chedeau authored and GitHub committed 2017-01-20 09:54:32 -08:00
1 parent 896bb5b26d
commit c468fde106
4 files changed
+193 -20

No files matched your search

+15 -8
View File
@@ -14,6 +14,7 @@ const argv = minimist(process.argv.slice(2), {
"single-quote",
"trailing-comma",
"version",
"debug-print-doc",
// Deprecated in 0.0.10
"flow-parser"
@@ -65,15 +66,21 @@ function getParser() {
return "babylon";
}
const options = {
printWidth: argv["print-width"],
tabWidth: argv["tab-width"],
bracketSpacing: argv["bracket-spacing"],
parser: getParser(),
singleQuote: argv["single-quote"],
trailingComma: argv["trailing-comma"]
};
function format(input) {
return jscodefmt.format(input, {
printWidth: argv["print-width"],
tabWidth: argv["tab-width"],
bracketSpacing: argv["bracket-spacing"],
parser: getParser(),
singleQuote: argv["single-quote"],
trailingComma: argv["trailing-comma"]
});
if (argv["debug-print-doc"]) {
const doc = jscodefmt.__debug.printToDoc(input, options);
return jscodefmt.__debug.formatDoc(doc);
}
return jscodefmt.format(input, options);
}
if (stdin) {
+46 -8
View File
@@ -58,8 +58,11 @@
font-size: 11.05px;
line-height: 17.68px;
}
.editor.input {
margin-right: 5px;
.editor {
margin-left: 5px;
}
.editor:first-child {
margin-left: 0;
}
.options {
display: flex;
@@ -88,20 +91,25 @@
<label><input type="checkbox" id="singleQuote"></input> singleQuote</label>
<label><input type="checkbox" id="trailingComma"></input> trailingComma</label>
<label><input type="checkbox" id="bracketSpacing" checked></input> bracketSpacing</label>
<span style="flex: 1"></span>
<label><input type="checkbox" id="doc"></input> doc</label>
</div>
<div class="editors">
<div class="editor input">
<textarea id="input"></textarea>
<textarea id="input-editor"></textarea>
</div>
<div class="editor doc">
<textarea id="doc-editor"></textarea>
</div>
<div class="editor output">
<textarea id="output"></textarea>
<textarea id="output-editor"></textarea>
</div>
</div>
</div>
<script id="code">
var OPTIONS = ['printWidth', 'tabWidth', 'singleQuote', 'trailingComma', 'bracketSpacing'];
var OPTIONS = ['printWidth', 'tabWidth', 'singleQuote', 'trailingComma', 'bracketSpacing', 'doc'];
function setOptions(options) {
OPTIONS.forEach(function(option) {
var elem = document.getElementById(option);
@@ -124,7 +132,14 @@ function getOptions() {
function format() {
var options = getOptions();
outputEditor.setOption('rulers', [{column: options.printWidth, color: '#444444'}]);
[docEditor, outputEditor].forEach(function(editor) {
editor.setOption(
'rulers',
[{column: options.printWidth, color: '#444444'}]
);
});
document.getElementsByClassName('doc')[0].style.display = options.doc ? 'flex' : 'none';
var value = encodeURIComponent(
JSON.stringify(
Object.assign({content: inputEditor.getValue(), options: options})
@@ -138,6 +153,17 @@ function format() {
res = e.toString();
}
outputEditor.setValue(res);
if (options.doc) {
var debug;
try {
var doc = prettier.__debug.printToDoc(inputEditor.getValue(), options);
debug = prettier.__debug.formatDoc(doc, options);
} catch (e) {
debug = e.toString();
}
docEditor.setValue(debug);
}
}
document.getElementsByClassName('options')[0].onchange = format;
@@ -151,9 +177,21 @@ var editorOptions = {
theme: 'base16-dark',
tabWidth: 2
};
var inputEditor = CodeMirror.fromTextArea(input, editorOptions);
var inputEditor = CodeMirror.fromTextArea(
document.getElementById('input-editor'),
editorOptions
);
inputEditor.on('change', format);
var outputEditor = CodeMirror.fromTextArea(output, {readOnly: true, lineNumbers: true, theme: 'base16-dark' });
var docEditor = CodeMirror.fromTextArea(
document.getElementById('doc-editor'),
{readOnly: true, lineNumbers: true, theme: 'base16-dark'}
);
var outputEditor = CodeMirror.fromTextArea(
document.getElementById('output-editor'),
{readOnly: true, lineNumbers: true, theme: 'base16-dark'}
);
document.getElementsByClassName('version')[0].innerText = prettier.version;
</script>
+30 -4
View File
@@ -6,6 +6,7 @@ const printAstToDoc = require("./src/printer").printAstToDoc;
const printDocToString = require("./src/doc-printer").printDocToString;
const normalizeOptions = require("./src/options").normalize;
const parser = require("./src/parser");
const printDocToDebug = require("./src/doc-debug").printDocToDebug;
function parse(text, opts) {
if (opts.parser === 'flow') {
@@ -25,9 +26,9 @@ function attachComments(text, ast, opts) {
function format(text, opts) {
const ast = parse(text, opts);
attachComments(text, ast, opts)
const doc = printAstToDoc(ast, opts)
const str = printDocToString(doc, opts.printWidth)
attachComments(text, ast, opts);
const doc = printAstToDoc(ast, opts);
const str = printDocToString(doc, opts.printWidth);
return str;
}
@@ -49,5 +50,30 @@ module.exports = {
format: function(text, opts) {
return formatWithShebang(text, normalizeOptions(opts));
},
version: version
version: version,
__debug: {
// Doesn't handle shebang for now
formatDoc: function(doc, opts) {
opts = normalizeOptions(opts);
const debug = printDocToDebug(doc);
const str = format(debug, opts);
return str;
},
printToDoc: function(text, opts) {
opts = normalizeOptions(opts);
const ast = parse(text, opts);
attachComments(text, ast, opts);
const doc = printAstToDoc(ast, opts);
return doc;
},
printDocToString: function(doc, opts) {
opts = normalizeOptions(opts);
const str = printDocToString(doc, opts.printWidth);
return str;
}
}
};
+102
View File
@@ -0,0 +1,102 @@
"use strict";
var jsesc = require("jsesc");
function flattenDoc(doc) {
if (!doc ||
typeof doc === "string" ||
doc.type === "line") {
return doc;
}
if (doc.type === "concat") {
var res = [];
for (var i = 0; i < doc.parts.length; ++i) {
const doc2 = doc.parts[i];
if (typeof doc2 !== "string" && doc2.type === "concat") {
[].push.apply(res, flattenDoc(doc2).parts);
} else {
const flattened = flattenDoc(doc2);
if (flattened !== "") {
res.push(flattened);
}
}
}
return Object.assign({}, doc, {
parts: res
});
}
if (doc.type === "indent") {
return Object.assign({}, doc, {
contents: flattenDoc(doc.contents)
});
}
if (doc.type === "if-break") {
return Object.assign({}, doc, {
breakContents: flattenDoc(doc.breakContents),
flatContents: flattenDoc(doc.flatContents),
});
}
if (doc.type === "group") {
return Object.assign({}, doc, {
contents: flattenDoc(doc.contents),
expandedStates: doc.expandedStates ?
doc.expandedStates.map(flattenDoc) :
doc.expandedStates,
});
}
}
function printDoc(doc) {
if (typeof doc === "string") {
return jsesc(doc, {wrap: true});
}
if (doc.type === "line") {
if (doc.literalline) {
return "literalline";
}
if (doc.hard) {
return "hardline";
}
if (doc.soft) {
return "softline";
}
return "line";
}
if (doc.type === "concat") {
return "[" + doc.parts.map(printDoc).join(", ") + "]";
}
if (doc.type === "indent") {
return "indent(" + doc.n + ", " + printDoc(doc.contents) + ")";
}
if (doc.type === "if-break") {
return "ifBreak(" +
printDoc(doc.breakContents) +
(doc.flatContents ? ", " + printDoc(doc.flatContents) : "") +
")";
}
if (doc.type === "group") {
return (doc.break ? "multilineGroup" : (doc.expandedStates ? "conditionalGroup" : "group")) +
"(" + printDoc(doc.contents) +
(doc.expandedStates ?
", [" + doc.expandedStates.map(printDoc).join(",") + "]" :
"") +
")";
}
}
module.exports = {
printDocToDebug: function(doc) {
return printDoc(flattenDoc(doc));
}
}