polytexnic 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/polytexnic/literal.rb +22 -12
- data/lib/polytexnic/preprocessors/polytex.rb +78 -21
- data/lib/polytexnic/version.rb +1 -1
- data/spec/markdown_to_polytex_spec.rb +72 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d8e7de21594c8b7389b2644e15ea5cf098edff39
|
4
|
+
data.tar.gz: 49867ac88a23d41ddf48903ada6f0269dd84d940
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0c58db6f0f1661a51c11c216c624960f4d9f405eaf2b201f88e810c9276c94e30539b322100c71cd9c36163480f43229d271fcb2520ffdd715889451db977ef7
|
7
|
+
data.tar.gz: 015ba8b5a3034d4b2765b509d2fa9a58f1fc8f5f4a897c0a2724b7048e4f912deec4cd18c0d4635a5c5b98ab95c356be1c79ded81802f839fb804ad7650b7d47
|
data/lib/polytexnic/literal.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
module Polytexnic
|
2
2
|
module Literal
|
3
|
+
extend self
|
3
4
|
|
4
5
|
# Matches the line for syntax highlighting.
|
5
6
|
# %= lang:<language>
|
@@ -21,6 +22,22 @@ module Polytexnic
|
|
21
22
|
output.join("\n")
|
22
23
|
end
|
23
24
|
|
25
|
+
# Returns supported math environments.
|
26
|
+
# Note that the custom AMS-TeX environments are supported
|
27
|
+
# in addition to the LaTeX defaults.
|
28
|
+
def math_environments
|
29
|
+
%w[align align*
|
30
|
+
eqnarray eqnarray* equation equation*
|
31
|
+
gather gather* gathered
|
32
|
+
multline multline*
|
33
|
+
]
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns a list of all literal types.
|
37
|
+
def literal_types
|
38
|
+
%w[verbatim Vertatim code metadcode] + math_environments
|
39
|
+
end
|
40
|
+
|
24
41
|
# Handles environments that should be passed through the pipeline intact.
|
25
42
|
# The includes verbatim environments ('verbatim', 'Verbatim') and all the
|
26
43
|
# equation environments handled by MathJax ('equation', 'align', etc.).
|
@@ -40,7 +57,7 @@ module Polytexnic
|
|
40
57
|
# and even then I've failed to get it to work. Thus, it shall for now
|
41
58
|
# follow the "ball of mud" pattern. (The only saving grace is that it's
|
42
59
|
# very thoroughly tested.)
|
43
|
-
def cache_literal_environments(lines, output, format)
|
60
|
+
def cache_literal_environments(lines, output, format, cache = nil)
|
44
61
|
latex = (format == :latex)
|
45
62
|
language = nil
|
46
63
|
in_verbatim = false
|
@@ -241,18 +258,9 @@ module Polytexnic
|
|
241
258
|
end
|
242
259
|
end
|
243
260
|
|
244
|
-
# Returns supported math environments.
|
245
|
-
# Note that the custom AMS-TeX environments are supported
|
246
|
-
# in addition to the LaTeX defaults.
|
247
|
-
def math_environments
|
248
|
-
%w[align align*
|
249
|
-
eqnarray eqnarray* equation equation*
|
250
|
-
gather gather* gathered
|
251
|
-
multline multline*
|
252
|
-
]
|
253
|
-
end
|
254
261
|
|
255
262
|
class String
|
263
|
+
include Polytexnic::Literal
|
256
264
|
|
257
265
|
# Returns true if self matches \begin{...} where ... is a literal environment.
|
258
266
|
# Note: Support for the 'metacode' environment exists solely to allow
|
@@ -294,6 +302,8 @@ class String
|
|
294
302
|
|
295
303
|
# Returns a regex matching valid math environments.
|
296
304
|
def math_environment_regex
|
297
|
-
math_environments.map
|
305
|
+
Polytexnic::Literal.math_environments.map do |s|
|
306
|
+
Regexp.escape(s)
|
307
|
+
end.join('|')
|
298
308
|
end
|
299
309
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
module Polytexnic
|
3
3
|
module Preprocessor
|
4
4
|
module Polytex
|
5
|
+
include Polytexnic::Literal
|
5
6
|
|
6
7
|
# Converts Markdown to PolyTeX.
|
7
8
|
# We adopt a unified approach: rather than convert "Markdown" (I use
|
@@ -23,17 +24,83 @@ module Polytexnic
|
|
23
24
|
# marketing term.</rant>
|
24
25
|
def to_polytex
|
25
26
|
require 'Kramdown'
|
27
|
+
cache = {}
|
28
|
+
math_cache = {}
|
26
29
|
cleaned_markdown = cache_code_environments
|
27
30
|
cleaned_markdown.tap do |markdown|
|
28
31
|
convert_code_inclusion(markdown)
|
32
|
+
cache_latex_literal(markdown, cache)
|
33
|
+
cache_raw_latex(markdown, cache)
|
34
|
+
cache_math(markdown, math_cache)
|
29
35
|
end
|
30
|
-
math_cache = cache_math(cleaned_markdown)
|
31
36
|
# Override the header ordering, which starts with 'section' by default.
|
32
37
|
lh = 'chapter,section,subsection,subsubsection,paragraph,subparagraph'
|
33
38
|
kramdown = Kramdown::Document.new(cleaned_markdown, latex_headers: lh)
|
34
39
|
@source = restore_inclusion(restore_math(kramdown.to_latex, math_cache))
|
40
|
+
restore_raw_latex(@source, cache)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Adds support for <<(path/to/code) inclusion.
|
44
|
+
# Yes, this is a bit of a hack, but it works.
|
45
|
+
def convert_code_inclusion(text)
|
46
|
+
text.gsub!(/^\s*<<(\(.*?\))/) { "<!-- inclusion= <<#{$1}-->" }
|
47
|
+
end
|
48
|
+
def restore_inclusion(text)
|
49
|
+
text.gsub(/% <!-- inclusion= (.*?)-->/) { "%= #{$1}" }
|
50
|
+
end
|
51
|
+
|
52
|
+
# Caches literal LaTeX environments.
|
53
|
+
def cache_latex_literal(markdown, cache)
|
54
|
+
Polytexnic::Literal.literal_types.each do |literal|
|
55
|
+
regex = /(\\begin\{#{Regexp.escape(literal)}\}
|
56
|
+
.*?
|
57
|
+
\\end\{#{Regexp.escape(literal)}\})
|
58
|
+
/xm
|
59
|
+
markdown.gsub!(regex) do
|
60
|
+
key = digest($1)
|
61
|
+
cache[key] = $1
|
62
|
+
key
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Caches raw LaTeX commands to be passed through the pipeline.
|
68
|
+
def cache_raw_latex(markdown, cache)
|
69
|
+
command_regex = /(
|
70
|
+
~\\ref\{.*?\} # reference with a tie
|
71
|
+
|
|
72
|
+
~\\eqref\{.*?\} # eq reference with a tie
|
73
|
+
|
|
74
|
+
\\\w+\{.*?\} # command with one arg
|
75
|
+
|
|
76
|
+
\\\w+ # normal command
|
77
|
+
|
|
78
|
+
\\[ %&$#@] # space or special character
|
79
|
+
)
|
80
|
+
/x
|
81
|
+
markdown.gsub!(command_regex) do
|
82
|
+
key = digest($1)
|
83
|
+
cache[key] = $1
|
84
|
+
key
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Restores raw LaTeX from the cache
|
89
|
+
def restore_raw_latex(text, cache)
|
90
|
+
cache.each do |key, value|
|
91
|
+
if value == '\&'
|
92
|
+
# Bizarrely, the default code doesn't work for '\&'.
|
93
|
+
# I actually suspect it may be a bug in Ruby. This hacks around it.
|
94
|
+
text.gsub!(key, value.sub(/\\/, '\\\\\\'))
|
95
|
+
else
|
96
|
+
text.gsub!(key, value)
|
97
|
+
end
|
98
|
+
end
|
35
99
|
end
|
36
100
|
|
101
|
+
# Caches Markdown code environments.
|
102
|
+
# Included are indented environments, Leanpub-style indented environments,
|
103
|
+
# and GitHub-style code fencing.
|
37
104
|
def cache_code_environments
|
38
105
|
output = []
|
39
106
|
lines = @source.split("\n")
|
@@ -72,7 +139,7 @@ module Polytexnic
|
|
72
139
|
output.join("\n")
|
73
140
|
end
|
74
141
|
|
75
|
-
# Caches
|
142
|
+
# Caches math.
|
76
143
|
# Leanpub uses the notation {$$}...{/$$} for both inline and block math,
|
77
144
|
# with the only difference being the presences of newlines:
|
78
145
|
# {$$} x^2 {/$$} % inline
|
@@ -80,21 +147,19 @@ module Polytexnic
|
|
80
147
|
# {$$}
|
81
148
|
# x^2 % block
|
82
149
|
# {/$$}
|
83
|
-
# I personally hate this notation and convention,
|
84
|
-
#
|
85
|
-
def cache_math(text)
|
86
|
-
|
87
|
-
|
88
|
-
key =
|
89
|
-
cache[[:block, key]] = $1
|
150
|
+
# I personally hate this notation and convention, so we also support
|
151
|
+
# LaTeX-style \( x \) and \[ x^2 - 2 = 0 \] notation.
|
152
|
+
def cache_math(text, cache)
|
153
|
+
text.gsub!(/(?:\{\$\$\}\n(.*?)\n\{\/\$\$\}|\\\[(.*?)\\\])/) do
|
154
|
+
key = digest($1 || $2)
|
155
|
+
cache[[:block, key]] = $1 || $2
|
90
156
|
key
|
91
157
|
end
|
92
|
-
text.gsub!(
|
93
|
-
key = digest($1)
|
94
|
-
cache[[:inline, key]] = $1
|
158
|
+
text.gsub!(/(?:\{\$\$\}(.*?)\{\/\$\$\}|\\\((.*?)\\\))/) do
|
159
|
+
key = digest($1 || $2)
|
160
|
+
cache[[:inline, key]] = $1 || $2
|
95
161
|
key
|
96
162
|
end
|
97
|
-
cache
|
98
163
|
end
|
99
164
|
|
100
165
|
# Restores the Markdown math.
|
@@ -115,13 +180,5 @@ module Polytexnic
|
|
115
180
|
text
|
116
181
|
end
|
117
182
|
end
|
118
|
-
|
119
|
-
# Adds support for <<(path/to/code) inclusion.
|
120
|
-
def convert_code_inclusion(text)
|
121
|
-
text.gsub!(/^\s*<<(\(.*?\))/) { "<!-- inclusion= <<#{$1}-->" }
|
122
|
-
end
|
123
|
-
def restore_inclusion(text)
|
124
|
-
text.gsub(/% <!-- inclusion= (.*?)-->/) { "%= #{$1}" }
|
125
|
-
end
|
126
183
|
end
|
127
184
|
end
|
data/lib/polytexnic/version.rb
CHANGED
@@ -87,6 +87,78 @@ That is it. You can keep writing your text after the footnote content.
|
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
90
|
+
context "with LaTeX containing" do
|
91
|
+
|
92
|
+
context "a normal command" do
|
93
|
+
let(:source) { 'This is a command: \foobar' }
|
94
|
+
it { should include source }
|
95
|
+
end
|
96
|
+
|
97
|
+
context "backslash space" do
|
98
|
+
let(:source) { 'Dr.\ No' }
|
99
|
+
it { should include source }
|
100
|
+
end
|
101
|
+
|
102
|
+
context "escaped special characters" do
|
103
|
+
let(:source) { '\% \& \$ \# \@ \_' }
|
104
|
+
it { should include source }
|
105
|
+
end
|
106
|
+
|
107
|
+
context "a label and cross-reference" do
|
108
|
+
let(:source) do <<-'EOS'
|
109
|
+
# Chapter One
|
110
|
+
\label{cha:one}
|
111
|
+
|
112
|
+
Chapter~\ref{cha:one}
|
113
|
+
EOS
|
114
|
+
end
|
115
|
+
it { should include '\label{cha:one}' }
|
116
|
+
it { should include 'Chapter~\ref{cha:one}' }
|
117
|
+
end
|
118
|
+
|
119
|
+
context "an inline equation" do
|
120
|
+
let(:source) { '\( x \) is a variable' }
|
121
|
+
it { should include source }
|
122
|
+
end
|
123
|
+
|
124
|
+
context "a centered equation" do
|
125
|
+
let(:source) { '\[ x^2 - 2 = 0 \] is an equation' }
|
126
|
+
it { should resemble source }
|
127
|
+
end
|
128
|
+
|
129
|
+
context "an equation environment" do
|
130
|
+
let(:source) do <<-'EOS'
|
131
|
+
foo
|
132
|
+
|
133
|
+
\begin{equation}
|
134
|
+
\label{eq:phi}
|
135
|
+
\phi = \frac{1+\sqrt{5}}{2}
|
136
|
+
\end{equation}
|
137
|
+
|
138
|
+
bar
|
139
|
+
EOS
|
140
|
+
end
|
141
|
+
it { should resemble source }
|
142
|
+
end
|
143
|
+
|
144
|
+
context "a codelisting environment" do
|
145
|
+
let(:source) do <<-'EOS'
|
146
|
+
\begin{codelisting}
|
147
|
+
\codecaption{Lorem ipsum.}
|
148
|
+
\label{code:lorem}
|
149
|
+
```ruby
|
150
|
+
def foo; "bar"; end
|
151
|
+
```
|
152
|
+
\end{codelisting}
|
153
|
+
EOS
|
154
|
+
end
|
155
|
+
it { should resemble '\begin{codelisting}' }
|
156
|
+
it { should resemble '\codecaption{Lorem ipsum.}' }
|
157
|
+
it { should resemble '\label{code:lorem}' }
|
158
|
+
it { should resemble '\end{codelisting}' }
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
90
162
|
describe "source code" do
|
91
163
|
context "without highlighting" do
|
92
164
|
let(:source) do <<-EOS
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: polytexnic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Hartl
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-11-
|
12
|
+
date: 2013-11-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: nokogiri
|