Add SplashMarkdown (#57)
This change adds a new command line tool to the Splash family: `SplashMarkdown`. It’s an adapted version of the tool that I’ve been using for months to publish every article on Swift by Sundell, and works by replacing all code blocks within a Markdown file. Adding this will hopefully make Splash even easier to use, without the need for writing custom tooling, or to manually replace each code block within a Markdown file with a “splashed” version.
This commit is contained in:
parent
60c06cc385
commit
8e7599150f
1
Makefile
1
Makefile
|
@ -2,5 +2,6 @@ install:
|
|||
swift package update
|
||||
swift build -c release -Xswiftc -static-stdlib
|
||||
install .build/Release/SplashHTMLGen /usr/local/bin/SplashHTMLGen
|
||||
install .build/Release/SplashMarkdown /usr/local/bin/SplashMarkdown
|
||||
install .build/Release/SplashImageGen /usr/local/bin/SplashImageGen
|
||||
install .build/Release/SplashTokenizer /usr/local/bin/SplashTokenizer
|
||||
|
|
|
@ -15,6 +15,10 @@ let package = Package(
|
|||
],
|
||||
targets: [
|
||||
.target(name: "Splash"),
|
||||
.target(
|
||||
name: "SplashMarkdown",
|
||||
dependencies: ["Splash"]
|
||||
),
|
||||
.target(
|
||||
name: "SplashHTMLGen",
|
||||
dependencies: ["Splash"]
|
||||
|
|
19
README.md
19
README.md
|
@ -51,7 +51,7 @@ You'll get the following output back:
|
|||
|
||||
To be as flexible as possible, Splash doesn't hardcode any colors or other CSS attributes in the HTML it generates. Instead it simply assigns a CSS class to each token. For an example of a CSS file that can be used to style Splash-generated HTML, see [Examples/sundellsColors.css](https://github.com/JohnSundell/Splash/blob/master/Examples/sundellsColors.css).
|
||||
|
||||
When rendering your outputted html, make sure to wrap your output code in the `<pre>` and `<code> ` tags and properly link to your `.css` file. Like this:
|
||||
When rendering your outputted html, make sure to wrap your output code in the `<pre>` and `<code>` tags and properly link to your `.css` file. Like this:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
|
@ -69,6 +69,22 @@ When rendering your outputted html, make sure to wrap your output code in the `<
|
|||
|
||||
For more information about HTML generation with Splash and how to customize it, see `HTMLOutputFormat` [here](https://github.com/JohnSundell/Splash/blob/master/Sources/Splash/Output/HTMLOutputFormat.swift).
|
||||
|
||||
#### SplashMarkdown
|
||||
|
||||
`SplashMarkdown` builds on top of `SplashHTMLGen` to enable easy Splash decoration of any Markdown file. Pass it a path to a Markdown file, and it will iterate through all code blocks within that file and convert them into Splash-highlighted HTML.
|
||||
|
||||
Just like the HTML generated by `SplashHTMLGen` itself, a CSS file should also be added to any page serving the processed Markdown, since Splash only adds CSS classes to tokens — rather than hardcoding styles inline. See the above `SplashHTMLGen` documentation for more information.
|
||||
|
||||
Here’s an example call to decorate a Markdown file at the path `~/Documents/Article.md`:
|
||||
|
||||
```
|
||||
$ SplashMarkdown ~/Documents/Article.md
|
||||
```
|
||||
|
||||
The decorated Markdown will be returned as standard output.
|
||||
|
||||
Highlighting can be skipped for any code block by adding `no-highlight` next to the block’s opening row of backticks — like this: *“```no-highlight”*.
|
||||
|
||||
#### SplashImageGen
|
||||
|
||||
`SplashImageGen` uses Splash to generate an `NSAttributedString` from Swift code, then draws that attributed string into a graphics context to turn it into an image, which is then written to disk.
|
||||
|
@ -168,6 +184,7 @@ That will install the following three tools in your `/usr/local/bin` folder:
|
|||
|
||||
```
|
||||
SplashHTMLGen
|
||||
SplashMarkdown
|
||||
SplashImageGen
|
||||
SplashTokenizer
|
||||
```
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* Splash
|
||||
* Copyright (c) John Sundell 2019
|
||||
* MIT license - see LICENSE.md
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Type used to decorate a Markdown file with Splash-highlighted code blocks
|
||||
public struct MarkdownDecorator {
|
||||
private let highlighter = SyntaxHighlighter(format: HTMLOutputFormat())
|
||||
private let skipHighlightingPrefix = "no-highlight"
|
||||
|
||||
public init() {}
|
||||
|
||||
/// Decorate all code blocks within a given Markdown string. This API assumes
|
||||
/// that the passed Markdown is valid. Each code block will be replaced by
|
||||
/// Splash-highlighted HTML for that block's code. To skip highlighting for
|
||||
/// any given code block, add "no-highlight" next to the opening row of
|
||||
/// backticks for that block.
|
||||
public func decorate(_ markdown: String) -> String {
|
||||
let components = markdown.components(separatedBy: "```")
|
||||
var output = ""
|
||||
|
||||
for (index, component) in components.enumerated() {
|
||||
guard index % 2 != 0 else {
|
||||
output.append(component)
|
||||
continue
|
||||
}
|
||||
|
||||
var code = component.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
if code.hasPrefix(skipHighlightingPrefix) {
|
||||
let charactersToDrop = skipHighlightingPrefix + "\n"
|
||||
code = String(code.dropFirst(charactersToDrop.count))
|
||||
} else {
|
||||
code = highlighter.highlight(code)
|
||||
}
|
||||
|
||||
output.append("""
|
||||
<pre class="splash"><code>
|
||||
\(code)
|
||||
</code></pre>
|
||||
""")
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
* Splash
|
||||
* Copyright (c) John Sundell 2019
|
||||
* MIT license - see LICENSE.md
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import Splash
|
||||
|
||||
guard CommandLine.arguments.count > 1 else {
|
||||
print("⚠️ Please supply the path to a Markdown file to process as an argument")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
let markdown: String = {
|
||||
let path = CommandLine.arguments[1]
|
||||
|
||||
do {
|
||||
let path = (path as NSString).expandingTildeInPath
|
||||
return try String(contentsOfFile: path)
|
||||
} catch {
|
||||
print("""
|
||||
🛑 Failed to open Markdown file at '\(path)':
|
||||
---
|
||||
\(error.localizedDescription)
|
||||
---
|
||||
""")
|
||||
exit(1)
|
||||
}
|
||||
}()
|
||||
|
||||
let decorator = MarkdownDecorator()
|
||||
print(decorator.decorate(markdown))
|
|
@ -1,3 +1,9 @@
|
|||
/**
|
||||
* Splash
|
||||
* Copyright (c) John Sundell 2019
|
||||
* MIT license - see LICENSE.md
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import XCTest
|
||||
|
||||
|
|
|
@ -18,7 +18,8 @@ public func makeLinuxTests() -> [XCTestCaseEntry] {
|
|||
testCase(LiteralTests.allTests),
|
||||
testCase(OptionalTests.allTests),
|
||||
testCase(PreprocessorTests.allTests),
|
||||
testCase(StatementTests.allTests)
|
||||
testCase(StatementTests.allTests),
|
||||
testCase(MarkdownTests.allTests)
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
/**
|
||||
* Splash
|
||||
* Copyright (c) John Sundell 2019
|
||||
* MIT license - see LICENSE.md
|
||||
*/
|
||||
|
||||
import XCTest
|
||||
import Splash
|
||||
|
||||
final class MarkdownTests: SplashTestCase {
|
||||
private var decorator: MarkdownDecorator!
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
decorator = MarkdownDecorator()
|
||||
}
|
||||
|
||||
func testConvertingCodeBlock() {
|
||||
let markdown = """
|
||||
# Title
|
||||
|
||||
Text text text `inline.code.shouldNotBeHighlighted()`.
|
||||
|
||||
```
|
||||
struct Hello: Protocol {}
|
||||
```
|
||||
|
||||
Text.
|
||||
"""
|
||||
|
||||
let expectedResult = """
|
||||
# Title
|
||||
|
||||
Text text text `inline.code.shouldNotBeHighlighted()`.
|
||||
|
||||
<pre class="splash"><code>
|
||||
<span class="keyword">struct</span> Hello: <span class="type">Protocol</span> {}
|
||||
</code></pre>
|
||||
|
||||
Text.
|
||||
"""
|
||||
|
||||
XCTAssertEqual(decorator.decorate(markdown), expectedResult)
|
||||
}
|
||||
|
||||
func testSkippingHighlightingForCodeBlock() {
|
||||
let markdown = """
|
||||
Text text.
|
||||
|
||||
```no-highlight
|
||||
struct Hello: Protocol {}
|
||||
```
|
||||
|
||||
Text.
|
||||
"""
|
||||
|
||||
let expectedResult = """
|
||||
Text text.
|
||||
|
||||
<pre class="splash"><code>
|
||||
struct Hello: Protocol {}
|
||||
</code></pre>
|
||||
|
||||
Text.
|
||||
"""
|
||||
|
||||
XCTAssertEqual(decorator.decorate(markdown), expectedResult)
|
||||
}
|
||||
|
||||
func testAllTestsRunOnLinux() {
|
||||
XCTAssertTrue(TestCaseVerifier.verifyLinuxTests((type(of: self)).allTests))
|
||||
}
|
||||
}
|
||||
|
||||
extension MarkdownTests {
|
||||
static var allTests: [(String, TestClosure<MarkdownTests>)] {
|
||||
return [
|
||||
("testConvertingCodeBlock", testConvertingCodeBlock),
|
||||
("testSkippingHighlightingForCodeBlock", testSkippingHighlightingForCodeBlock)
|
||||
]
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue