Add debugging support for doc IR (#347)
This PR adds two things:
`--debug-print-doc` command that prints the formatted doc
```js
echo "<div><</div>" | ./bin/prettier.js --stdin --debug-print-doc
[
groupConditional(
group([
group([ "<", "div", group([ indent(2, []), softline() ]), ">" ]),
"<",
"</",
"div",
">"
]),
[
group([
group([ "<", "div", group([ indent(2, []), softline() ]), ">" ]),
"<",
"</",
"div",
">"
]),
group([
group([ "<", "div", group([ indent(2, []), softline() ]), ">" ]),
indent(2, groupBreak([ hardline(), "<" ])),
hardline(),
"</",
"div",
">"
])
]
),
";",
hardline()
];
```
The ability to view the IR in real time on the browser display:

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:
1 parent
896bb5b26d
commit
c468fde106
4 files changed
+193
-20
No files matched your search
+15
-8
@@ -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
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user