Skip to content
Open
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
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ Ink supports the following Markdown features:
| Row 1 | Cell 1 |
| Row 2 | Cell 2 |
```
- LaTeX like equation support. As dollar signs can be found quite commonly on articles (and the slash character is already the escape character), TeX-like equation input is not supported. Note that Ink does _not_ render math equations. You need another library for that, like [KaTeX](https://katex.org) or [MathJax](https://www.mathjax.org). There are two equation modes:
1. Inline mode equations: `\(x^2 + 5\)`
2. Display mode equations: `\[z^2 + 5\]`

Please note that, being a very young implementation, Ink does not fully support all Markdown specs, such as [CommonMark](https://commonmark.org). Ink definitely aims to cover as much ground as possible, and to include support for the most commonly used Markdown features, but if complete CommonMark compatibility is what you’re looking for — then you might want to check out tools like [CMark](https://github.com/commonmark/cmark).

Expand Down
1 change: 1 addition & 0 deletions Sources/Ink/API/Modifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,6 @@ public extension Modifier {
case lists
case paragraphs
case tables
case math
}
}
5 changes: 5 additions & 0 deletions Sources/Ink/Internal/FormattedText.swift
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,11 @@ private extension FormattedText {
case "[": return Link.self
case "!": return Image.self
case "<": return HTML.self
case "\\":
if ["[","("].contains(reader.nextCharacter) {
return Math.self
}
fallthrough
default: return nil
}
}
Expand Down
79 changes: 79 additions & 0 deletions Sources/Ink/Internal/Math.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* Ink
* Copyright (c) John Sundell 2019
* MIT license, see LICENSE file for details
*/

internal struct Math: Fragment {
var modifierTarget: Modifier.Target { .math }

private var displayMode: Bool
private var tex: String

static func read(using reader: inout Reader) throws -> Math {
reader.advanceIndex()
let displayMode: Bool
let closingCharacter: Character
if reader.currentCharacter == "[" {
displayMode = true
closingCharacter = "]"
} else {
displayMode = false
closingCharacter = ")"
}
reader.advanceIndex()
reader.discardWhitespacesAndNewlines()

var tex = ""
// TeX math mode does not care about the whitespace count/type.
var previousCharacterIsSpace = false

while !reader.didReachEnd {
guard let nextCharacter = reader.nextCharacter else {
throw Reader.Error()
}

switch reader.currentCharacter {
case \.isWhitespace:
previousCharacterIsSpace = true
reader.discardWhitespacesAndNewlines()
case "\\":
if nextCharacter == closingCharacter {
reader.advanceIndex(by: 2)
return Math(displayMode: displayMode, tex: tex)
}
fallthrough
default:
if previousCharacterIsSpace {
tex.append(" ")
previousCharacterIsSpace = false
}

if let escaped = reader.currentCharacter.escaped {
tex.append(escaped)
} else {
tex.append(reader.currentCharacter)
}
reader.advanceIndex()
}
}
throw Reader.Error()
}

func html(usingURLs urls: NamedURLCollection,
modifiers: ModifierCollection) -> String {
let modeString = displayMode ? "display" : "inline"
return "<span class=\"math \(modeString)\">\(tex)</span>"
}

func plainText() -> String {
let plainTex: String
if displayMode {
plainTex = "\\[\(tex)\\]"
} else {
plainTex = "\\(\(tex)\\)"
}
return plainTex
}
}

2 changes: 1 addition & 1 deletion Tests/InkTests/LinkTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ final class LinkTests: XCTestCase {

func testLinkWithEscapedSquareBrackets() {
let html = MarkdownParser().html(from: "[\\[Hello\\]](hello)")
XCTAssertEqual(html, #"<p><a href="hello">[Hello]</a></p>"#)
XCTAssertEqual(html, #"<p><a href="hello"><span class="math display">Hello</span></a></p>"#)
}
}

Expand Down
66 changes: 66 additions & 0 deletions Tests/InkTests/MathTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* Ink
* Copyright (c) John Sundell 2019
* MIT license, see LICENSE file for details
*/

import XCTest
import Ink

final class MathTests: XCTestCase {
func testInlineMath() {
let html = MarkdownParser().html(from: #"\(Hello \Latex\)"#)
XCTAssertEqual(html, #"<p><span class="math inline">Hello \Latex</span></p>"#)
}

func testDisplayMath() {
let html = MarkdownParser().html(from: #"\[Hello \Latex\]"#)
XCTAssertEqual(html, #"<p><span class="math display">Hello \Latex</span></p>"#)
}

func testMathWithEscape() {
let html = MarkdownParser().html(from: #"Asterix \* and \(Hello \Latex\)"#)
XCTAssertEqual(html, #"<p>Asterix * and <span class="math inline">Hello \Latex</span></p>"#)
}
func testDisplayMultiLineProgression() {
let html = MarkdownParser().html(from: #"""
\[\begin{aligned}
y&=\left(x-r\right)\left(x-s\right)\\
y&=\left(x-\left(-7\right)\right)\left(x-\left(-2\right)\right)\\
y&=\left(x+7\right)\left(x+2\right)\\
y&=x^2+9x+14\\
y&=x^2+bx+c\\
\end{aligned}\]
"""#)
print(html)
XCTAssertEqual(html, #"""
<p><span class="math display">\begin{aligned} y&amp;=\left(x-r\right)\left(x-s\right)\\ y&amp;=\left(x-\left(-7\right)\right)\left(x-\left(-2\right)\right)\\ y&amp;=\left(x+7\right)\left(x+2\right)\\ y&amp;=x^2+9x+14\\ y&amp;=x^2+bx+c\\ \end{aligned}</span></p>
"""#)
}

func testDisplayMultilineWithParagraph() {
let html = MarkdownParser().html(from: #"""
We can write a vector in a Hilbert space as a sum of basis and projection coefficients \[
\begin{aligned}
\left\vert\psi\right\rangle &= \sum_iC_i\left\vert\varphi_i\right\rangle\\
&=\left\langle\varphi_i\vert\psi\right\rangle \left\vert\varphi_i\right\rangle
\end{aligned}
\] as above.
"""#)
XCTAssertEqual(html, #"""
<p>We can write a vector in a Hilbert space as a sum of basis and projection coefficients <span class="math display">\begin{aligned} \left\vert\psi\right\rangle &amp;= \sum_iC_i\left\vert\varphi_i\right\rangle\\ &amp;=\left\langle\varphi_i\vert\psi\right\rangle \left\vert\varphi_i\right\rangle \end{aligned}</span> as above.</p>
"""#)
}
}

extension MathTests {
static var allTests: Linux.TestList<MathTests> {
return [
("testInlineMath", testInlineMath),
("testDisplayMath", testDisplayMath),
("testMathWithEscape", testMathWithEscape),
("testDisplayMultiLineProgression", testDisplayMultiLineProgression),
("testDisplayMultilineWithParagraph", testDisplayMultilineWithParagraph),
]
}
}
3 changes: 2 additions & 1 deletion Tests/InkTests/XCTestManifests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public func allTests() -> [Linux.TestCase] {
Linux.makeTestCase(using: MarkdownTests.allTests),
Linux.makeTestCase(using: ModifierTests.allTests),
Linux.makeTestCase(using: TableTests.allTests),
Linux.makeTestCase(using: TextFormattingTests.allTests)
Linux.makeTestCase(using: TextFormattingTests.allTests),
Linux.makeTestCase(using: MathTests.allTests)
]
}