Repository: prettier/plugin-xml (js)

Issue:
There is a way to keep blank lines?
This file

```xml
<phpdoc>
  <title>PHPDoc-VuePress demo</title>

  <files>
    <directory>demo</directory>
    <directory>api</directory>
  </files>
</phpdoc
```

...get removed the black space.
```xml
<phpdoc>
  <title>PHPDoc-VuePress demo</title>
  <files>
    <directory>demo</directory>
    <directory>api</directory>
  </files>
</phpdoc
```

I would like to keep it to improve readability, specially in large files.


Could be nice to have a `maxBlackLines`-like option.

Pull request description:
Maintain newlines if they are in the original source
Fixes #156

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ec1a6fd..f187595 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
 
 ## [Unreleased]
 
+### Added
+
+- Maintain newlines if there are some in the original source.
+
 ## [0.12.0] - 2020-08-31
 
 ### Added
diff --git a/src/printer.js b/src/printer.js
index afb790c..32bba52 100644
--- a/src/printer.js
+++ b/src/printer.js
@@ -40,31 +40,43 @@ const whitespaceIgnorable = (node) => {
 
 const printCData = (node) => ({
   offset: node.startOffset,
+  startLine: node.startLine,
+  endLine: node.endLine,
   printed: node.image
 });
 
 const printComment = (node) => ({
   offset: node.startOffset,
+  startLine: node.startLine,
+  endLine: node.endLine,
   printed: node.image
 });
 
 const printCharData = (path, print) => (node, index) => ({
   offset: node.location.startOffset,
+  startLine: node.location.startLine,
+  endLine: node.location.endLine,
   printed: path.call(print, "children", "chardata", index)
 });
 
 const printElement = (path, print) => (node, index) => ({
   offset: node.location.startOffset,
+  startLine: node.location.startLine,
+  endLine: node.location.endLine,
   printed: path.call(print, "children", "element", index)
 });
 
 const printProcessingInstruction = (node) => ({
   offset: node.startOffset,
+  startLine: node.startLine,
+  endLine: node.endLine,
   printed: node.image
 });
 
 const printReference = (node) => ({
   offset: node.location.startOffset,
+  startLine: node.location.startLine,
+  endLine: node.location.endLine,
   printed: (node.children.CharRef || node.children.EntityRef)[0].image
 });
 
@@ -291,6 +303,8 @@ const nodes = {
         .concat(
           element.map((node, index) => ({
             offset: node.location.startOffset,
+            startLine: node.location.startLine,
+            endLine: node.location.endLine,
             printed: path.call(
               print,
               "children",
@@ -303,20 +317,36 @@ const nodes = {
           }))
         )
         .concat(PROCESSING_INSTRUCTION.map(printProcessingInstruction))
-        .sort((left, right) => left.offset - right.offset)
-        .map(({ printed }) => printed);
+        .sort((left, right) => left.offset - right.offset);
 
-      const separator =
-        children.length == 1 && chardatas.length == 1 ? softline : hardline;
+      // If the only content of this tag is chardata, then use a softline so
+      // that we won't necessarily break (to allow <foo>bar</foo>).
+      if (children.length === 1 && chardatas.length === 1) {
+        return group(
+          concat([
+            openTag,
+            indent(concat([softline, children[0].printed])),
+            softline,
+            closeTag
+          ])
+        );
+      }
 
-      return group(
-        concat([
-          openTag,
-          indent(concat([separator, join(separator, children)])),
-          separator,
-          closeTag
-        ])
-      );
+      const docs = [];
+      let lastLine = children[0].startLine;
+
+      children.forEach((node) => {
+        if (node.startLine - lastLine >= 2) {
+          docs.push(hardline, hardline);
+        } else {
+          docs.push(hardline);
+        }
+
+        docs.push(node.printed);
+        lastLine = node.endLine;
+      });
+
+      return group(concat([openTag, indent(concat(docs)), hardline, closeTag]));
     }
 
     return group(


Test patch:
diff --git a/test/__snapshots__/format.test.js.snap b/test/__snapshots__/format.test.js.snap
index a44aa78..cc3983e 100644
--- a/test/__snapshots__/format.test.js.snap
+++ b/test/__snapshots__/format.test.js.snap
@@ -22,6 +22,7 @@ exports[`defaults 1`] = `
     bar
   </desc>
   <?pagebreak?>
+
   <style />
   <style type=\\"text/css\\">
 circle {
@@ -35,6 +36,7 @@ use {
   fill: orange;
 }
   </style>
+
   <?pagebreak?>
     <g class=\\"special\\" style=\\"fill: blue\\">
         <circle id=\\"c\\" cy=\\"50\\" cx=\\"50\\" r=\\"40\\" stroke-width=\\"20\\" />
@@ -51,6 +53,8 @@ use {
   <span>
     content
   </span>
+
+
   <div>
     even more
     <content />
@@ -82,6 +86,7 @@ exports[`xmlWhitespaceSensitivity => ignore 1`] = `
     bar
   </desc>
   <?pagebreak?>
+
   <style />
   <style type=\\"text/css\\">
 circle {
@@ -95,6 +100,7 @@ use {
   fill: orange;
 }
   </style>
+
   <?pagebreak?>
   <g class=\\"special\\" style=\\"fill: blue\\">
     <circle id=\\"c\\" cy=\\"50\\" cx=\\"50\\" r=\\"40\\" stroke-width=\\"20\\" />
@@ -115,6 +121,7 @@ use {
     elementum ut.
   </p>
   <span>content</span>
+
   <div>
     even more
     <content />
diff --git a/test/fixture.xml b/test/fixture.xml
index a1f24b2..9868c95 100644
--- a/test/fixture.xml
+++ b/test/fixture.xml
@@ -19,6 +19,7 @@
     bar
   </desc>
   <?pagebreak?>
+
   <style></style>
   <style type="text/css">
     circle          { stroke-opacity: 0.7; }
@@ -26,6 +27,7 @@
     use             { stroke: purple;
                       fill: orange; }
   </style>
+
   <?pagebreak?>
     <g class="special" style="fill: blue">
         <circle id="c" cy="50" cx="50" r="40" stroke-width="20" />
@@ -42,6 +44,8 @@
   <span>
     content
   </span>
+
+
   <div>
     even more
     <content />