Skip to content

Commit

Permalink
do not change whitespace and newlines inside preformatted blockes
Browse files Browse the repository at this point in the history
  • Loading branch information
Andreas Ehrencrona committed Aug 26, 2020
1 parent ab04111 commit f8878a4
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 53 deletions.
8 changes: 7 additions & 1 deletion src/print/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Node, ScriptNode, InstanceScriptNode, ModuleScriptNode } from './nodes';
import { Node, ScriptNode } from './nodes';
import { FastPath } from 'prettier';

export interface ASTNode {
html: Node;
Expand All @@ -17,3 +18,8 @@ export interface ASTNode {
export function isASTNode(n: any): n is ASTNode {
return 'html' in n && 'tokens' in n;
}

export function isPreTagContent(path: FastPath): boolean {
const stack = path.stack as Node[];
return stack.some(node => node.type === 'Element' && node.name.toLowerCase() === 'pre');
}
118 changes: 66 additions & 52 deletions src/print/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FastPath, Doc, doc, ParserOptions } from 'prettier';
import { Node, MustacheTagNode, IfBlockNode } from './nodes';
import { isASTNode } from './helpers';
import { isASTNode, isPreTagContent } from './helpers';
import { extractAttributes } from '../lib/extractAttributes';
import { getText } from '../lib/getText';
import { parseSortOrder, SortOrderPart } from '../options';
Expand Down Expand Up @@ -108,37 +108,47 @@ export function print(path: FastPath, options: ParserOptions, print: PrintFn): D
return '';
}

return concat([... trim(printChildren(path, print), isLine), hardline])
case 'Text':
if (isEmptyNode(node)) {
return {
/**
* Empty (whitespace-only) text nodes are collapsed into a single `line`,
* which will be rendered as a single space if this node's group fits on a
* single line. This follows how vanilla HTML is handled both by browsers and
* by Prettier core.
*/
...line,

/**
* A text node is considered lonely if it is in a group without other inline
* elements, such as the line breaks between otherwise consecutive HTML tags.
* Text nodes that are both empty and lonely are discarded unless they have at
* least one empty line (i.e. at least two linebreak sequences). This is to
* allow for flexible grouping of HTML tags in a particular indentation level,
* and is similar to how vanilla HTML is handled in Prettier core.
*/
keepIfLonely: /\n\r?\s*\n\r?/.test(node.raw || node.data),
};
if (!isPreTagContent(path)) {
return concat([... trim(printChildren(path, print), isLine), hardline])
}
else {
return concat(printChildren(path, print))
}
case 'Text':
if (!isPreTagContent(path)) {
if (isEmptyNode(node)) {
return {
/**
* Empty (whitespace-only) text nodes are collapsed into a single `line`,
* which will be rendered as a single space if this node's group fits on a
* single line. This follows how vanilla HTML is handled both by browsers and
* by Prettier core.
*/
...line,

/**
* A text node is considered lonely if it is in a group without other inline
* elements, such as the line breaks between otherwise consecutive HTML tags.
* Text nodes that are both empty and lonely are discarded unless they have at
* least one empty line (i.e. at least two linebreak sequences). This is to
* allow for flexible grouping of HTML tags in a particular indentation level,
* and is similar to how vanilla HTML is handled in Prettier core.
*/
keepIfLonely: /\n\r?\s*\n\r?/.test(node.raw || node.data),
};
}

/**
* For non-empty text nodes each sequence of non-whitespace characters (effectively,
* each "word") is joined by a single `line`, which will be rendered as a single space
* until this node's current line is out of room, at which `fill` will break at the
* most convenient instance of `line`.
*/
return fill(splitTextToDocs(node.raw || node.data));
/**
* For non-empty text nodes each sequence of non-whitespace characters (effectively,
* each "word") is joined by a single `line`, which will be rendered as a single space
* until this node's current line is out of room, at which `fill` will break at the
* most convenient instance of `line`.
*/
return fill(splitTextToDocs(node.raw || node.data));
}
else {
return node.data;
}
case 'Element':
case 'InlineComponent':
case 'Slot':
Expand Down Expand Up @@ -184,7 +194,7 @@ export function print(path: FastPath, options: ParserOptions, print: PrintFn): D
'>',
isEmpty
? ''
: isInlineElement(node)
: isInlineElement(node) || isPreTagContent(path)
? printIndentedPreservingWhitespace(path, print)
: printIndentedWithNewlines(path, print),
`</${node.name}>`,
Expand Down Expand Up @@ -469,6 +479,8 @@ function printChildren(path: FastPath, print: PrintFn): Doc[] {
// the index of the last child doc we could add a linebreak after
let lastBreakIndex = -1;

let isPreformat = isPreTagContent(path)

/**
* Call when reaching a point where a linebreak is possible. Will
* put all `childDocs` since the last possible linebreak position
Expand All @@ -489,28 +501,30 @@ function printChildren(path: FastPath, print: PrintFn): Doc[] {
* @param childDoc null means do not add anything but allow for the possibility of a linebreak here.
*/
function outputChildDoc(childDoc: Doc | null, fromNodes: Node[]) {
const firstNode = fromNodes[0];
const lastNode = fromNodes[fromNodes.length - 1];

if (!childDoc || canBreakBefore(firstNode)) {
linebreakPossible();

const lastChild = childDocs[childDocs.length - 1];

// separate children by softlines, but not if the children are already lines.
// one exception: allow for a line break before "keepIfLonely" lines because they represent an empty line
if (
childDoc != null &&
!isLineDiscardedIfLonely(childDoc) &&
lastChild != null &&
!isLine(lastChild)
) {
childDocs.push(softline);
if (!isPreformat) {
const firstNode = fromNodes[0];
const lastNode = fromNodes[fromNodes.length - 1];

if (!childDoc || canBreakBefore(firstNode)) {
linebreakPossible();

const lastChild = childDocs[childDocs.length - 1];

// separate children by softlines, but not if the children are already lines.
// one exception: allow for a line break before "keepIfLonely" lines because they represent an empty line
if (
childDoc != null &&
!isLineDiscardedIfLonely(childDoc) &&
lastChild != null &&
!isLine(lastChild)
) {
childDocs.push(softline);
}
}

if (lastBreakIndex < 0 && childDoc && !canBreakAfter(lastNode)) {
lastBreakIndex = childDocs.length;
}
}

if (lastBreakIndex < 0 && childDoc && !canBreakAfter(lastNode)) {
lastBreakIndex = childDocs.length;
}

if (childDoc) {
Expand Down
12 changes: 12 additions & 0 deletions test/printer/samples/pre-element-inside-other-element.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<div>
<pre>foo
bar

<b>bold</b> <div>d</div>



<b>bold</b> <div>d</div>

baz</pre>
</div>
7 changes: 7 additions & 0 deletions test/printer/samples/pre-element-with-code-element.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<pre>
<code>
const greeting = "Hello",
name = "World",
text = greeting + ", " + name + "!";
</code>
</pre>
2 changes: 2 additions & 0 deletions test/printer/samples/pre-element.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<pre>foo
bar baz</pre>

0 comments on commit f8878a4

Please sign in to comment.