Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preformatted inline code #319

Merged
merged 1 commit into from
Dec 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ var turndownService = new TurndownService({ option: 'value' })
| `strongDelimiter` | `**` or `__` | `**` |
| `linkStyle` | `inlined` or `referenced` | `inlined` |
| `linkReferenceStyle` | `full`, `collapsed`, or `shortcut` | `full` |
| `preformattedCode` | `false` or [`true`](https://github.com/lucthev/collapse-whitespace/issues/16) | `false` |

### Advanced Options

Expand Down
15 changes: 9 additions & 6 deletions src/collapse-whitespace.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ function collapseWhitespace (options) {
if (!element.firstChild || isPre(element)) return

var prevText = null
var prevVoid = false
var keepLeadingWs = false

var prev = null
var node = next(prev, element, isPre)
Expand All @@ -51,7 +51,7 @@ function collapseWhitespace (options) {
var text = node.data.replace(/[ \r\n\t]+/g, ' ')

if ((!prevText || / $/.test(prevText.data)) &&
!prevVoid && text[0] === ' ') {
!keepLeadingWs && text[0] === ' ') {
text = text.substr(1)
}

Expand All @@ -71,11 +71,14 @@ function collapseWhitespace (options) {
}

prevText = null
prevVoid = false
} else if (isVoid(node)) {
// Avoid trimming space around non-block, non-BR void elements.
keepLeadingWs = false
} else if (isVoid(node) || isPre(node)) {
// Avoid trimming space around non-block, non-BR void elements and inline PRE.
prevText = null
prevVoid = true
keepLeadingWs = true
} else if (prevText) {
// Drop protection if set previously.
keepLeadingWs = false
}
} else {
node = remove(node)
Expand Down
20 changes: 12 additions & 8 deletions src/node.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { isBlock, isVoid, hasVoid, isMeaningfulWhenBlank, hasMeaningfulWhenBlank } from './utilities'

export default function Node (node) {
export default function Node (node, options) {
node.isBlock = isBlock(node)
node.isCode = node.nodeName.toLowerCase() === 'code' || node.parentNode.isCode
node.isCode = node.nodeName === 'CODE' || node.parentNode.isCode
node.isBlank = isBlank(node)
node.flankingWhitespace = flankingWhitespace(node)
node.flankingWhitespace = flankingWhitespace(node, options)
return node
}

Expand All @@ -18,18 +18,20 @@ function isBlank (node) {
)
}

function flankingWhitespace (node) {
if (node.isBlock) return { leading: '', trailing: '' }
function flankingWhitespace (node, options) {
if (node.isBlock || (options.preformattedCode && node.isCode)) {
return { leading: '', trailing: '' }
}

var edges = edgeWhitespace(node.textContent)

// abandon leading ASCII WS if left-flanked by ASCII WS
if (edges.leadingAscii && isFlankedByWhitespace('left', node)) {
if (edges.leadingAscii && isFlankedByWhitespace('left', node, options)) {
edges.leading = edges.leadingNonAscii
}

// abandon trailing ASCII WS if right-flanked by ASCII WS
if (edges.trailingAscii && isFlankedByWhitespace('right', node)) {
if (edges.trailingAscii && isFlankedByWhitespace('right', node, options)) {
edges.trailing = edges.trailingNonAscii
}

Expand All @@ -48,7 +50,7 @@ function edgeWhitespace (string) {
}
}

function isFlankedByWhitespace (side, node) {
function isFlankedByWhitespace (side, node, options) {
var sibling
var regExp
var isFlanked
Expand All @@ -64,6 +66,8 @@ function isFlankedByWhitespace (side, node) {
if (sibling) {
if (sibling.nodeType === 3) {
isFlanked = regExp.test(sibling.nodeValue)
} else if (options.preformattedCode && sibling.nodeName === 'CODE') {
isFlanked = false
} else if (sibling.nodeType === 1 && !isBlock(sibling)) {
isFlanked = regExp.test(sibling.textContent)
}
Expand Down
9 changes: 7 additions & 2 deletions src/root-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import collapseWhitespace from './collapse-whitespace'
import HTMLParser from './html-parser'
import { isBlock, isVoid } from './utilities'

export default function RootNode (input) {
export default function RootNode (input, options) {
var root
if (typeof input === 'string') {
var doc = htmlParser().parseFromString(
Expand All @@ -19,7 +19,8 @@ export default function RootNode (input) {
collapseWhitespace({
element: root,
isBlock: isBlock,
isVoid: isVoid
isVoid: isVoid,
isPre: options.preformattedCode ? isPreOrCode : null
})

return root
Expand All @@ -30,3 +31,7 @@ function htmlParser () {
_htmlParser = _htmlParser || new HTMLParser()
return _htmlParser
}

function isPreOrCode (node) {
return node.nodeName === 'PRE' || node.nodeName === 'CODE'
}
5 changes: 3 additions & 2 deletions src/turndown.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export default function TurndownService (options) {
linkStyle: 'inlined',
linkReferenceStyle: 'full',
br: ' ',
preformattedCode: false,
blankReplacement: function (content, node) {
return node.isBlock ? '\n\n' : ''
},
Expand Down Expand Up @@ -69,7 +70,7 @@ TurndownService.prototype = {

if (input === '') return ''

var output = process.call(this, new RootNode(input))
var output = process.call(this, new RootNode(input, this.options))
return postProcess.call(this, output)
},

Expand Down Expand Up @@ -158,7 +159,7 @@ TurndownService.prototype = {
function process (parentNode) {
var self = this
return reduce.call(parentNode.childNodes, function (output, node) {
node = new Node(node)
node = new Node(node, self.options)

var replacement = ''
if (node.nodeType === 3) {
Expand Down
34 changes: 34 additions & 0 deletions test/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1042,6 +1042,40 @@ <h2>This is a header.</h2>
<pre class="expected">foo &nbsp;_bar_</pre>
</div>

<!-- Behavior of `<code>` with CSS set as `white-space: pre-wrap;`, e.g. in GitLab -->
<div class="case" data-name="preformatted code with leading whitespace" data-options='{"preformattedCode": true}'>
<div class="input">Four spaces <code> make an indented code block in Markdown</code></div>
<pre class="expected">Four spaces ` make an indented code block in Markdown`</pre>
</div>

<div class="case" data-name="preformatted code with trailing whitespace" data-options='{"preformattedCode": true}'>
<div class="input"><code>A line break </code> <b> note the spaces</b></div>
<pre class="expected">`A line break ` **note the spaces**</pre>
</div>

<div class="case" data-name="preformatted code tightly surrounded" data-options='{"preformattedCode": true}'>
<div class="input"><b>tight</b><code>code</code><b>wrap</b></div>
<pre class="expected">**tight**`code`**wrap**</pre>
</div>

<div class="case" data-name="preformatted code loosely surrounded" data-options='{"preformattedCode": true}'>
<div class="input"><b>not so tight </b><code>code</code><b> wrap</b></div>
<pre class="expected">**not so tight** `code` **wrap**</pre>
</div>

<!-- newlines become spaces + extra space must be added -->
<div class="case" data-name="preformatted code with newlines" data-options='{"preformattedCode": true}'>
<div class="input">
<code>

nasty
code

</code>
</div>
<pre class="expected">` nasty code `</pre>
</div>

<!-- /TEST CASES -->

<script src="turndown-test.browser.js"></script>
Expand Down