livetext 0.9.17 → 0.9.19
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/imports/bookish.rb +288 -0
- data/imports/calibre.rb +28 -0
- data/imports/livemagick.rb +133 -0
- data/imports/markdown.rb +44 -0
- data/imports/markdown_importable.rb +46 -0
- data/imports/pyggish.rb +204 -0
- data/imports/tutorial.rb +95 -0
- data/lib/livetext.rb +1 -1
- data/livetext.gemspec +2 -1
- data/test/snapshots/clusion.txt +15 -1
- data/test/snapshots.rb +63 -25
- metadata +8 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 27c2715e2e3ac638609f0ad19b2427d3da68be25f08bea8196d574b0efdbc115
|
4
|
+
data.tar.gz: 7edbc57a07ae50aa240561ad6f250da79a76248cc4c46941df7353cccb3b663c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bbe056fd07c4198012ae2f0fc9f9af3e6eb45d2d3b68d9b8653c29b84081852ed40f05f63f59ccee7d6face5e68675f2293ca1ff3d9b92c12bbb2ca51cbc06df
|
7
|
+
data.tar.gz: 1b0f1b84ffd1cbbc41812f76516d39f7d805a53f89f4ef525962cf90128f6e738c81e671e8e98b6d83549b6959f03c844b312cac41a0445fd55472170c888d2c
|
data/imports/bookish.rb
ADDED
@@ -0,0 +1,288 @@
|
|
1
|
+
module Bookish
|
2
|
+
def hardbreaks(args = nil, body = nil)
|
3
|
+
@hard = false
|
4
|
+
@hard = true unless @_args.first == "off"
|
5
|
+
end
|
6
|
+
|
7
|
+
def hardbreaks?
|
8
|
+
@hard
|
9
|
+
end
|
10
|
+
|
11
|
+
def credit(args = nil, body = nil)
|
12
|
+
# really just a place marker in source
|
13
|
+
end
|
14
|
+
|
15
|
+
# These are duplicated. Remove safely
|
16
|
+
|
17
|
+
def h1; _out "<h1>#{@_data}</h1>"; end
|
18
|
+
def h2; _out "<h2>#{@_data}</h2>"; end
|
19
|
+
def h3; _out "<h3>#{@_data}</h3>"; end
|
20
|
+
|
21
|
+
def alpha_columns(args = nil, body = nil)
|
22
|
+
n = @_args.first.to_i # FIXME: what if missing?
|
23
|
+
words = []
|
24
|
+
_body do |line|
|
25
|
+
words << line.chomp
|
26
|
+
end
|
27
|
+
words.sort!
|
28
|
+
_out "<table cellpadding=2>"
|
29
|
+
words.each_slice(n) do |w|
|
30
|
+
items = w.map {|x| "<tt>#{x}</tt>" }
|
31
|
+
_out "<tr><td width=5% valign=top></td><td>" + items.join("</td><td>") + "</td></tr>"
|
32
|
+
end
|
33
|
+
_out "</table>"
|
34
|
+
end
|
35
|
+
|
36
|
+
# def comment
|
37
|
+
# _body { } # ignore body
|
38
|
+
# end
|
39
|
+
|
40
|
+
def _errout(*args)
|
41
|
+
::STDERR.puts *args
|
42
|
+
end
|
43
|
+
|
44
|
+
def _nbsp(n)
|
45
|
+
" "*n
|
46
|
+
end
|
47
|
+
|
48
|
+
def _slug(str)
|
49
|
+
s2 = str.chomp.strip.gsub(/[?:,()'"\/]/,"").gsub(/ /, "-").downcase
|
50
|
+
# _errout "SLUG: #{str} => #{s2}"
|
51
|
+
s2
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
# FIXME duplicated?
|
56
|
+
def image(args = nil, body = nil)
|
57
|
+
name = @_args[0]
|
58
|
+
_out "<img src='#{name}'></img>"
|
59
|
+
end
|
60
|
+
|
61
|
+
def figure(args = nil, body = nil)
|
62
|
+
name = @_args[0]
|
63
|
+
num = @_args[1]
|
64
|
+
title = @_args[2..-1].join(" ")
|
65
|
+
title = _format(title)
|
66
|
+
_out "<img src='#{name}'></img>"
|
67
|
+
_out "<center><b>Figure #{num}</b> #{title}</center>"
|
68
|
+
end
|
69
|
+
|
70
|
+
def chapter(args = nil, body = nil)
|
71
|
+
# _errout("chapter")
|
72
|
+
@chapter = @_args.first.to_i
|
73
|
+
@sec = @sec2 = 0
|
74
|
+
title = @_data.split(" ",2)[1]
|
75
|
+
@toc << "<br><b>#@chapter</b> #{title}<br>"
|
76
|
+
@_data = _slug(title)
|
77
|
+
next_output
|
78
|
+
_out "<title>#{@chapter}. #{title}</title>"
|
79
|
+
_out <<-HTML
|
80
|
+
<h2>Chapter #{@chapter}</h2>
|
81
|
+
<h1>#{title}</h1>
|
82
|
+
|
83
|
+
HTML
|
84
|
+
end
|
85
|
+
|
86
|
+
def chapterN(args = nil, body = nil)
|
87
|
+
@chapter += 1
|
88
|
+
@sec = @sec2 = 0
|
89
|
+
title = @_data # .split(" ",2)[1]
|
90
|
+
_errout("Chapter #@chapter: #{title}")
|
91
|
+
@toc << "<br><b>#@chapter</b> #{title}<br>"
|
92
|
+
@_data = _slug(title)
|
93
|
+
next_output
|
94
|
+
_out "<title>#{@chapter}. #{title}</title>"
|
95
|
+
_out <<-HTML
|
96
|
+
<h2>Chapter #{@chapter}</h2>
|
97
|
+
<h1>#{title}</h1>
|
98
|
+
|
99
|
+
HTML
|
100
|
+
end
|
101
|
+
|
102
|
+
def sec(args = nil, body = nil)
|
103
|
+
@sec += 1
|
104
|
+
@sec2 = 0
|
105
|
+
@section = "#@chapter.#@sec"
|
106
|
+
title = @_data.dup
|
107
|
+
@toc << "#{_nbsp(3)}<b>#@section</b> #{title}<br>"
|
108
|
+
@_data = _slug(@_data)
|
109
|
+
next_output
|
110
|
+
_out "<h3>#@section #{title}</h3>\n"
|
111
|
+
rescue => err
|
112
|
+
STDERR.puts "#{err}\n#{err.backtrace}"
|
113
|
+
exit
|
114
|
+
end
|
115
|
+
|
116
|
+
def subsec(args = nil, body = nil)
|
117
|
+
@sec2 += 1
|
118
|
+
@subsec = "#@chapter.#@sec.#@sec2"
|
119
|
+
title = @_data.dup
|
120
|
+
@toc << "#{_nbsp(6)}<b>#@subsec</b> #{title}<br>"
|
121
|
+
@_data = _slug(@_data)
|
122
|
+
next_output
|
123
|
+
_out "<h3>#@subsec #{title}</h3>\n"
|
124
|
+
end
|
125
|
+
|
126
|
+
def definition_table(args = nil, body = nil)
|
127
|
+
title = @_data
|
128
|
+
wide = "95"
|
129
|
+
delim = " :: "
|
130
|
+
_out "<br><center><table width=#{wide}% cellpadding=5>"
|
131
|
+
lines = _body(true)
|
132
|
+
lines.map! {|line| _format(line) }
|
133
|
+
|
134
|
+
lines.each do |line|
|
135
|
+
cells = line.split(delim)
|
136
|
+
_out "<tr>"
|
137
|
+
cells.each.with_index do |cell, i|
|
138
|
+
width = (i == 0) ? "width=15%" : ""
|
139
|
+
_out " <td #{width} valign=top>#{cell}</td>"
|
140
|
+
end
|
141
|
+
_out "</tr>"
|
142
|
+
end
|
143
|
+
_out "</table></center><br><br>"
|
144
|
+
|
145
|
+
_optional_blank_line
|
146
|
+
end
|
147
|
+
|
148
|
+
def table2(args = nil, body = nil)
|
149
|
+
title = @_data
|
150
|
+
wide = "90"
|
151
|
+
extra = _args[2]
|
152
|
+
delim = " :: "
|
153
|
+
_out "<br><center><table width=#{wide}% cellpadding=5>"
|
154
|
+
lines = _body(true)
|
155
|
+
lines.map! {|line| _format(line) }
|
156
|
+
|
157
|
+
lines.each do |line|
|
158
|
+
cells = line.split(delim)
|
159
|
+
percent = (100/cells.size.to_f).round
|
160
|
+
_out "<tr>"
|
161
|
+
cells.each do |cell|
|
162
|
+
_out " <td width=#{percent}% valign=top " +
|
163
|
+
"#{extra}>#{cell}</td>"
|
164
|
+
end
|
165
|
+
_out "</tr>"
|
166
|
+
end
|
167
|
+
_out "</table></center><br><br>"
|
168
|
+
|
169
|
+
_optional_blank_line
|
170
|
+
end
|
171
|
+
|
172
|
+
def simple_table(args = nil, body = nil)
|
173
|
+
title = @_data
|
174
|
+
delim = " :: "
|
175
|
+
_out "<table cellpadding=2>"
|
176
|
+
lines = _body(true)
|
177
|
+
maxw = nil
|
178
|
+
lines.each do |line|
|
179
|
+
_format(line)
|
180
|
+
cells = line.split(delim)
|
181
|
+
wide = cells.map {|x| x.length }
|
182
|
+
maxw = [0] * cells.size
|
183
|
+
maxw = maxw.map.with_index {|x, i| [x, wide[i]].max }
|
184
|
+
end
|
185
|
+
|
186
|
+
sum = maxw.inject(0, :+)
|
187
|
+
maxw.map! {|x| (x/sum*100).floor }
|
188
|
+
|
189
|
+
lines.each do |line|
|
190
|
+
cells = line.split(delim)
|
191
|
+
_out "<tr>"
|
192
|
+
cells.each.with_index do |cell, i|
|
193
|
+
_out " <td width=#{maxw}% valign=top>" +
|
194
|
+
"#{cell}</td>"
|
195
|
+
end
|
196
|
+
_out "</tr>"
|
197
|
+
end
|
198
|
+
_out "</table>"
|
199
|
+
end
|
200
|
+
|
201
|
+
def table(args = nil, body = nil)
|
202
|
+
@table_num ||= 0
|
203
|
+
@table_num += 1
|
204
|
+
title = @_data
|
205
|
+
delim = " :: "
|
206
|
+
_out "<br><center><table width=90% cellpadding=5>"
|
207
|
+
lines = _body(true)
|
208
|
+
maxw = nil
|
209
|
+
lines.each do |line|
|
210
|
+
_format(line)
|
211
|
+
cells = line.split(delim)
|
212
|
+
wide = cells.map {|x| x.length }
|
213
|
+
maxw = [0] * cells.size
|
214
|
+
maxw = maxw.map.with_index {|x, i| [x, wide[i]+2].max }
|
215
|
+
end
|
216
|
+
|
217
|
+
sum = maxw.inject(0, :+)
|
218
|
+
maxw.map! {|x| (x/sum*100).floor }
|
219
|
+
|
220
|
+
lines.each do |line|
|
221
|
+
cells = line.split(delim)
|
222
|
+
_out "<tr>"
|
223
|
+
cells.each.with_index do |cell, i|
|
224
|
+
_out " <td width=#{maxw}% valign=top>" +
|
225
|
+
"#{cell}</td>"
|
226
|
+
end
|
227
|
+
_out "</tr>"
|
228
|
+
end
|
229
|
+
_out "</table>"
|
230
|
+
@toc << "#{_nbsp(8)}<b>Table #@chapter.#@table_num</b> #{title}<br>"
|
231
|
+
# _next_output(_slug("table_#{title}"))
|
232
|
+
_out "<b>Table #@chapter.#@table_num #{title}</b></center><br>"
|
233
|
+
end
|
234
|
+
|
235
|
+
def toc!(args = nil, body = nil)
|
236
|
+
_debug "Closing TOC"
|
237
|
+
@toc.close
|
238
|
+
rescue => err
|
239
|
+
puts @parent.body
|
240
|
+
@parent.body = ""
|
241
|
+
_errout "Exception: #{err.inspect}"
|
242
|
+
end
|
243
|
+
|
244
|
+
def toc2(args = nil, body = nil)
|
245
|
+
file = @_args[0]
|
246
|
+
@toc.close
|
247
|
+
::File.write(file, <<-EOS)
|
248
|
+
<p style="page-break-after:always;"></p>
|
249
|
+
<meta charset='UTF-8'>
|
250
|
+
|
251
|
+
<center><h2>Fake (non-hyperlinked) Table of Contents</h2></center>
|
252
|
+
|
253
|
+
EOS
|
254
|
+
system("cat toc.tmp >>#{file}")
|
255
|
+
end
|
256
|
+
|
257
|
+
def missing(args = nil, body = nil)
|
258
|
+
@toc << "#{_nbsp(8)}<font color=red>TBD: #@_data</font><br>"
|
259
|
+
stuff = @_data.empty? ? "" : ": #@_data"
|
260
|
+
_out "<br><font color=red><i>[Material missing#{stuff}]</i></font><br>\n "
|
261
|
+
end
|
262
|
+
|
263
|
+
def TBC(args = nil, body = nil)
|
264
|
+
@toc << "#{_nbsp(8)}<font color=red>To be continued...</font><br>"
|
265
|
+
_out "<br><font color=red><i>To be continued...</i></font><br>"
|
266
|
+
end
|
267
|
+
|
268
|
+
def note(args = nil, body = nil)
|
269
|
+
_out "<br><font color=red><i>Note: "
|
270
|
+
_out @_data
|
271
|
+
_out "</i></font><br>\n "
|
272
|
+
end
|
273
|
+
|
274
|
+
def quote(args = nil, body = nil)
|
275
|
+
_out "<blockquote>"
|
276
|
+
_body {|line| _out line }
|
277
|
+
_out "</blockquote>"
|
278
|
+
rescue => err
|
279
|
+
STDERR.puts "#{err}\n#{err.backtrace}"
|
280
|
+
exit
|
281
|
+
end
|
282
|
+
|
283
|
+
def init_bookish
|
284
|
+
@toc_file = "toc.tmp"
|
285
|
+
@toc = ::File.new(@toc_file, "w")
|
286
|
+
@chapter = -1
|
287
|
+
end
|
288
|
+
end
|
data/imports/calibre.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Calibre
|
4
|
+
def epub!(args = nil, body = nil)
|
5
|
+
out = _format(@_args[0])
|
6
|
+
src = @_args[1]
|
7
|
+
@cover = @_args[2]
|
8
|
+
if ::File.directory?(src)
|
9
|
+
files = ::Dir["#{src}/*"].grep /\.html$/
|
10
|
+
files = files.sort # why is this necessary now?
|
11
|
+
cmd = "cat #{files.join(' ')} >TEMP.html"
|
12
|
+
system(cmd)
|
13
|
+
else
|
14
|
+
raise "Not supported yet"
|
15
|
+
end
|
16
|
+
|
17
|
+
cmd = "ebook-convert "
|
18
|
+
cmd << "TEMP.html #{out}.epub "
|
19
|
+
cmd << "--cover #@cover " if @cover
|
20
|
+
system(cmd)
|
21
|
+
|
22
|
+
system("links -dump TEMP.html >/tmp/links.out")
|
23
|
+
str = `wc -w /tmp/links.out`
|
24
|
+
nw = str.split[0]
|
25
|
+
puts "Approx words: #{nw}"
|
26
|
+
# ::FileUtils.rm("TEMP.html")
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'rmagick'
|
2
|
+
|
3
|
+
module Livemagick # FIXME auto-mangle names
|
4
|
+
include ::Magick
|
5
|
+
|
6
|
+
def image(args = nil, body = nil)
|
7
|
+
xx, yy, bg = _args
|
8
|
+
xx, yy = xx.to_i, yy.to_i
|
9
|
+
@image = Image.new(xx,yy) { self.background_color = bg }
|
10
|
+
_optional_blank_line
|
11
|
+
end
|
12
|
+
|
13
|
+
def canvas(args = nil, body = nil)
|
14
|
+
color, width, opacity = _args
|
15
|
+
opacity, width = opacity.to_i, width.to_i
|
16
|
+
@canvas = Draw.new
|
17
|
+
@canvas.fill_opacity(0)
|
18
|
+
@canvas.stroke('black')
|
19
|
+
@canvas.stroke_width(1)
|
20
|
+
_optional_blank_line
|
21
|
+
end
|
22
|
+
|
23
|
+
def rectangle(args = nil, body = nil)
|
24
|
+
xy, wxh, stroke_color, stroke_width = _args
|
25
|
+
x, y = xy.split(",").map(&:to_i)
|
26
|
+
width, height = wxh.split("x").map(&:to_i)
|
27
|
+
stroke_width = stroke_width.to_i
|
28
|
+
_debug "rectangle: x=#{x} y=#{y} width=#{width} height=#{height} "
|
29
|
+
@canvas.stroke(stroke_color)
|
30
|
+
@canvas.stroke_width(stroke_width)
|
31
|
+
@canvas.rectangle(x, y, x+width, y+height)
|
32
|
+
end
|
33
|
+
|
34
|
+
def pen(args = nil, body = nil)
|
35
|
+
@fill, @stroke = _args
|
36
|
+
@stroke = "black" if @stroke.nil? || @stroke.empty?
|
37
|
+
_debug "pen: fill=#@fill stroke=#@stroke"
|
38
|
+
_optional_blank_line
|
39
|
+
end
|
40
|
+
|
41
|
+
def font(args = nil, body = nil)
|
42
|
+
size, font = _args
|
43
|
+
font = "Helvetica" if font.nil? || font.empty?
|
44
|
+
size = "32" if size.nil? || size.empty?
|
45
|
+
@size, @font = size.to_i, font
|
46
|
+
_debug "font: size=#@size font=#@font"
|
47
|
+
_optional_blank_line
|
48
|
+
end
|
49
|
+
|
50
|
+
def _text(xy, wxh, str, weight, gravity)
|
51
|
+
x, y = xy.split(",").map(&:to_i)
|
52
|
+
width, height = wxh.split("x").map(&:to_i)
|
53
|
+
font, fill, stroke, size = @font, @fill, @stroke, @size
|
54
|
+
@canvas.annotate(@image, width, height, x, y, str) do
|
55
|
+
self.font_family = font
|
56
|
+
self.fill = fill
|
57
|
+
self.stroke = stroke
|
58
|
+
self.pointsize = size
|
59
|
+
self.font_weight = weight
|
60
|
+
self.gravity = gravity
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def text(args = nil, body = nil)
|
65
|
+
xy, wxh, str = _data.split
|
66
|
+
weight, gravity = BoldWeight, CenterGravity
|
67
|
+
_text(xy, wxh, str, weight, gravity)
|
68
|
+
_optional_blank_line
|
69
|
+
end
|
70
|
+
|
71
|
+
def text!(args = nil, body = nil)
|
72
|
+
xy, wxh = _data.split
|
73
|
+
str = _body_text # .join
|
74
|
+
weight, gravity = BoldWeight, CenterGravity
|
75
|
+
_text(xy, wxh, str, weight, gravity)
|
76
|
+
_optional_blank_line
|
77
|
+
end
|
78
|
+
|
79
|
+
def draw(args = nil, body = nil)
|
80
|
+
@canvas.draw(@image)
|
81
|
+
_optional_blank_line
|
82
|
+
end
|
83
|
+
|
84
|
+
def save(args = nil, body = nil)
|
85
|
+
@image.write(_args.first)
|
86
|
+
_optional_blank_line
|
87
|
+
end
|
88
|
+
|
89
|
+
def save!(args = nil, body = nil)
|
90
|
+
save
|
91
|
+
system("open #{_args.first}")
|
92
|
+
_optional_blank_line
|
93
|
+
end
|
94
|
+
end
|
95
|
+
=begin
|
96
|
+
draw.annotate(img, width, height, x, y, text) [ { additional parameters } ] -> draw
|
97
|
+
Description
|
98
|
+
|
99
|
+
Annotates a image with text. The text is positioned according to the gravity attribute around the rectangle described by the x, y, width, and height arguments. If either of the width or height arguments are 0, uses the image width-x and the image height-y to compute the rectangle width and height. The attributes described in annotate attributes, below, influence the appearance and position of the text. These attributes may be set in the Draw object before calling annotate, or within annotate's optional additional parameters block.
|
100
|
+
|
101
|
+
Note: all of the annotate attributes are set-only.
|
102
|
+
Arguments
|
103
|
+
|
104
|
+
img
|
105
|
+
the image or imagelist to be annotated
|
106
|
+
width
|
107
|
+
width of the rectangle within which the text is positioned
|
108
|
+
height
|
109
|
+
height of the rectangle within which the text is positioned
|
110
|
+
x
|
111
|
+
x offset of the rectangle
|
112
|
+
y
|
113
|
+
y offset of the rectangle
|
114
|
+
text
|
115
|
+
the text
|
116
|
+
|
117
|
+
Returns
|
118
|
+
|
119
|
+
self
|
120
|
+
Example
|
121
|
+
|
122
|
+
This example is an excerpt of colors.rb. Many other examples also use annotate.
|
123
|
+
|
124
|
+
title.annotate(montage, 0,0,0,40, 'Named Colors') {
|
125
|
+
self.font_family = 'Helvetica'
|
126
|
+
self.fill = 'white'
|
127
|
+
self.stroke = 'transparent'
|
128
|
+
self.pointsize = 32
|
129
|
+
self.font_weight = BoldWeight
|
130
|
+
self.gravity = NorthGravity
|
131
|
+
}
|
132
|
+
|
133
|
+
=end
|
data/imports/markdown.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# This file is intended to be used via a Livetext .mixin
|
2
|
+
# or the equivalent.
|
3
|
+
|
4
|
+
module Markdown
|
5
|
+
SimpleFormats = {}
|
6
|
+
SimpleFormats[:b] = %w[* *]
|
7
|
+
SimpleFormats[:i] = %w[_ _]
|
8
|
+
SimpleFormats[:t] = %w[` `]
|
9
|
+
SimpleFormats[:s] = %w[<strike> </strike>]
|
10
|
+
|
11
|
+
|
12
|
+
def h1(args = nil, body = nil); _out "# #{FormatLine.var_func_parse(@_data)}"; _optional_blank_line end # atx style for now
|
13
|
+
def h2(args = nil, body = nil); _out "## #{FormatLine.var_func_parse(@_data)}"; _optional_blank_line end
|
14
|
+
def h3(args = nil, body = nil); _out "### #{FormatLine.var_func_parse(@_data)}"; _optional_blank_line end
|
15
|
+
def h4(args = nil, body = nil); _out "#### #{FormatLine.var_func_parse(@_data)}"; _optional_blank_line end
|
16
|
+
def h5(args = nil, body = nil); _out "##### #{FormatLine.var_func_parse(@_data)}"; _optional_blank_line end
|
17
|
+
def h6(args = nil, body = nil); _out "###### #{FormatLine.var_func_parse(@_data)}"; _optional_blank_line end
|
18
|
+
|
19
|
+
def title(args = nil, body = nil)
|
20
|
+
h1
|
21
|
+
end
|
22
|
+
|
23
|
+
def section(args = nil, body = nil)
|
24
|
+
h3
|
25
|
+
end
|
26
|
+
|
27
|
+
def bq(args = nil, body = nil) # block quote
|
28
|
+
_body {|line| _out "> #{line}" }
|
29
|
+
end
|
30
|
+
|
31
|
+
def list(args = nil, body = nil)
|
32
|
+
_body {|line| _out " * #{line}" }
|
33
|
+
end
|
34
|
+
|
35
|
+
def olist(args = nil, body = nil) # Doesn't handle paragraphs yet
|
36
|
+
n = 0
|
37
|
+
_body do |line|
|
38
|
+
n += 1
|
39
|
+
_out "#{n}. #{_format(line)}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
alias nlist olist
|
44
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# This file is intended to be used via a Livetext .mixin
|
2
|
+
# or the equivalent.
|
3
|
+
|
4
|
+
require_relative '../lib/livetext/importable'
|
5
|
+
|
6
|
+
class MarkdownImportable < LiveText::Importable
|
7
|
+
|
8
|
+
SimpleFormats[:b] = %w[* *]
|
9
|
+
SimpleFormats[:i] = %w[_ _]
|
10
|
+
SimpleFormats[:t] = %w[` `]
|
11
|
+
SimpleFormats[:s] = %w[<strike> </strike>]
|
12
|
+
|
13
|
+
def h1; _out "# #{FormatLine.var_func_parse(@_data)}"; _optional_blank_line end # atx style for now
|
14
|
+
def h2; _out "## #{FormatLine.var_func_parse(@_data)}"; _optional_blank_line end
|
15
|
+
def h3; _out "### #{FormatLine.var_func_parse(@_data)}"; _optional_blank_line end
|
16
|
+
def h4; _out "#### #{FormatLine.var_func_parse(@_data)}"; _optional_blank_line end
|
17
|
+
def h5; _out "##### #{FormatLine.var_func_parse(@_data)}"; _optional_blank_line end
|
18
|
+
def h6; _out "###### #{FormatLine.var_func_parse(@_data)}"; _optional_blank_line end
|
19
|
+
|
20
|
+
def title
|
21
|
+
h1
|
22
|
+
end
|
23
|
+
|
24
|
+
def section
|
25
|
+
h3
|
26
|
+
end
|
27
|
+
|
28
|
+
def bq # block quote
|
29
|
+
_body {|line| _out "> #{line}" }
|
30
|
+
end
|
31
|
+
|
32
|
+
def list
|
33
|
+
_body {|line| _out " * #{line}" }
|
34
|
+
end
|
35
|
+
|
36
|
+
def olist # Doesn't handle paragraphs yet
|
37
|
+
n = 0
|
38
|
+
_body do |line|
|
39
|
+
n += 1
|
40
|
+
_out "#{n}. #{_format(line)}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
alias nlist olist
|
45
|
+
end
|
46
|
+
end
|
data/imports/pyggish.rb
ADDED
@@ -0,0 +1,204 @@
|
|
1
|
+
require 'rouge'
|
2
|
+
|
3
|
+
module Pyggish
|
4
|
+
def self.pyg_change(code, klass, style)
|
5
|
+
color = style[0..6]
|
6
|
+
modifier = style[8]
|
7
|
+
mod_open = modifier ? "<#{modifier}>" : ""
|
8
|
+
mod_close = modifier ? "</#{modifier}>" : ""
|
9
|
+
rx = /<span class="#{klass}">(?<cname>[^<]+?)<\/span>/
|
10
|
+
loop do
|
11
|
+
md = rx.match(code)
|
12
|
+
break if md.nil?
|
13
|
+
str = md[:cname]
|
14
|
+
result = code.sub!(rx, "<font color=#{color}>#{mod_open}#{str}#{mod_close}</font>")
|
15
|
+
break if result.nil?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self._codebar_color(lexer)
|
20
|
+
color = case lexer
|
21
|
+
when :elixir
|
22
|
+
"#fc88fc"
|
23
|
+
when :ruby
|
24
|
+
"#fc8888"
|
25
|
+
else
|
26
|
+
raise "Unknown lexer"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.pyg_finalize(code, lexer=:elixir)
|
31
|
+
Styles.each_pair {|klass, style| pyg_change(code, klass, style) }
|
32
|
+
File.open("debug-pf1", "w") {|f| f.puts code }
|
33
|
+
code.sub!(/<pre>/, "<pre>\n")
|
34
|
+
code.gsub!(/<span class="[np]">/, "")
|
35
|
+
code.gsub!(/<\/span>/, "")
|
36
|
+
color = _codebar_color(lexer)
|
37
|
+
code.sub!(/<td class="linenos"/, "<td width=2%></td><td width=5% bgcolor=#{color}")
|
38
|
+
code.gsub!(/<td/, "<td valign=top ")
|
39
|
+
code.gsub!(/ class="[^"]*?"/, "") # Get rid of remaining Pygments CSS
|
40
|
+
File.open("debug-pf2", "w") {|f| f.puts code }
|
41
|
+
lines = code.split("\n")
|
42
|
+
# lines.each {|line| line << "\n" }
|
43
|
+
n1 = lines.index {|x| x =~ /<pre>/ }
|
44
|
+
n2 = lines.index {|x| x =~ /<\/pre>/ }
|
45
|
+
# FIXME ?
|
46
|
+
n1 ||= 0
|
47
|
+
n2 ||= -1
|
48
|
+
lines[n1].sub!(/ 1$/, " 1 ")
|
49
|
+
(n1+1).upto(n2) {|n| lines[n].replace(" " + lines[n] + " ") }
|
50
|
+
code = lines.join("\n")
|
51
|
+
File.open("debug-pf3", "w") {|f| f.puts code }
|
52
|
+
code
|
53
|
+
end
|
54
|
+
|
55
|
+
def _process_code(text)
|
56
|
+
File.open("debug-pc1", "w") {|f| f.puts text }
|
57
|
+
lines = text.split("\n")
|
58
|
+
lines = lines.select {|x| x !~ /##~ omit/ }
|
59
|
+
@refs = {}
|
60
|
+
lines.each.with_index do |line, i|
|
61
|
+
if line =~ /##~ ref/
|
62
|
+
frag, name = line.split(/ *##~ ref/)
|
63
|
+
@refs[name.strip] = i
|
64
|
+
line.replace(frag)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
lines.map! {|line| " " + line }
|
68
|
+
text2 = lines.join("\n")
|
69
|
+
File.open("debug-pc2", "w") {|f| f.puts text2 }
|
70
|
+
text.replace(text2)
|
71
|
+
end
|
72
|
+
|
73
|
+
def _colorize(code, lexer=:elixir)
|
74
|
+
text = ::Pygments.highlight(code, lexer: lexer, options: {linenos: "table"})
|
75
|
+
_debug "--- in _colorize: text = #{text.inspect}"
|
76
|
+
text2 = PygmentFix.pyg_finalize(text, lexer)
|
77
|
+
result = "<!-- colorized code -->\n" + text2
|
78
|
+
result
|
79
|
+
end
|
80
|
+
|
81
|
+
def _colorize!(code, lexer=:elixir)
|
82
|
+
text = ::Pygments.highlight(code, lexer: lexer, options: {})
|
83
|
+
_debug "--- in _colorize!: text = #{text.inspect}"
|
84
|
+
text2 = PygmentFix.pyg_finalize(text, lexer)
|
85
|
+
result = "<!-- colorized code -->\n" + text2
|
86
|
+
result
|
87
|
+
end
|
88
|
+
|
89
|
+
def OLD_ruby
|
90
|
+
file = @_args.first
|
91
|
+
if file.nil?
|
92
|
+
code = "# Ruby code\n"
|
93
|
+
_body {|line| code << line + "\n" }
|
94
|
+
else
|
95
|
+
code = "# Ruby code\n\n" + ::File.read(file)
|
96
|
+
end
|
97
|
+
|
98
|
+
_process_code(code)
|
99
|
+
html = _colorize(code, :ruby)
|
100
|
+
_out "\n#{html}\n "
|
101
|
+
end
|
102
|
+
|
103
|
+
def OLD_elixir
|
104
|
+
file = @_args.first
|
105
|
+
if file.nil?
|
106
|
+
code = ""
|
107
|
+
_body {|line| code << line + "\n" }
|
108
|
+
else
|
109
|
+
code = ::File.read(file)
|
110
|
+
end
|
111
|
+
|
112
|
+
_process_code(code)
|
113
|
+
html = _colorize(code, :elixir)
|
114
|
+
_out "\n#{html}\n "
|
115
|
+
end
|
116
|
+
|
117
|
+
def fragment
|
118
|
+
lang = @_args.empty? ? :elixir : @_args.first.to_sym # ruby or elixir
|
119
|
+
@_args = []
|
120
|
+
send(lang)
|
121
|
+
_out "\n"
|
122
|
+
end
|
123
|
+
|
124
|
+
def code # FIXME ?
|
125
|
+
text = ""
|
126
|
+
_body {|line| _out " " + line }
|
127
|
+
end
|
128
|
+
|
129
|
+
def mono
|
130
|
+
_out "<pre>"
|
131
|
+
_body {|line| _out " " + line }
|
132
|
+
_out "</pre>"
|
133
|
+
end
|
134
|
+
|
135
|
+
def create_code_styles
|
136
|
+
dir = @_outdir || "."
|
137
|
+
theme, back = "Github", "white"
|
138
|
+
css = Rouge::Themes.const_get(theme).render(scope: '.rb_highlight')
|
139
|
+
added = <<~CSS
|
140
|
+
.rb_highlight {
|
141
|
+
font-family: 'Monaco', 'Andale Mono', 'Lucida Grande', 'Courier', 'Lucida Console', 'Courier New', monospace;
|
142
|
+
white-space: pre;
|
143
|
+
background-color: #{back}
|
144
|
+
}
|
145
|
+
CSS
|
146
|
+
|
147
|
+
css.gsub!(/{\n/, "{\n font-family: courier;")
|
148
|
+
css = added + "\n" + css
|
149
|
+
# STDERR.puts "Writing #{theme} theme to ruby.css"
|
150
|
+
File.write("#{dir}/ruby.css", css)
|
151
|
+
|
152
|
+
css = Rouge::Themes.const_get(theme).render(scope: '.ex_highlight')
|
153
|
+
added = added.sub(/rb/, "ex")
|
154
|
+
css.gsub!(/{\n/, "{\n font-family: courier;")
|
155
|
+
css = added + "\n" + css
|
156
|
+
# STDERR.puts "Writing #{theme} theme to elixir.css"
|
157
|
+
File.write("#{dir}/elixir.css", css)
|
158
|
+
end
|
159
|
+
|
160
|
+
|
161
|
+
def format_ruby(source, theme = "Github", back = "black")
|
162
|
+
# theme/back not used now
|
163
|
+
formatter = Rouge::Formatters::HTML.new
|
164
|
+
lexer = Rouge::Lexers::Ruby.new
|
165
|
+
body = formatter.format(lexer.lex(source))
|
166
|
+
text = "<div class=rb_highlight>#{body}</div>"
|
167
|
+
text
|
168
|
+
end
|
169
|
+
|
170
|
+
def format_elixir(source, theme = "Github", back = "black")
|
171
|
+
# theme/back not used now
|
172
|
+
formatter = Rouge::Formatters::HTML.new
|
173
|
+
lexer = Rouge::Lexers::Elixir.new
|
174
|
+
body = formatter.format(lexer.lex(source))
|
175
|
+
text = "<div class=ex_highlight>#{body}</div>"
|
176
|
+
text
|
177
|
+
end
|
178
|
+
|
179
|
+
def ruby
|
180
|
+
file = @_args.first
|
181
|
+
if file.nil?
|
182
|
+
code = " # Ruby code\n\n"
|
183
|
+
_body {|line| code << " " + line + "\n" }
|
184
|
+
else
|
185
|
+
code = "# Ruby code\n\n" + ::File.read(file)
|
186
|
+
end
|
187
|
+
|
188
|
+
html = format_ruby(code)
|
189
|
+
_out html
|
190
|
+
end
|
191
|
+
|
192
|
+
def elixir
|
193
|
+
file = @_args.first
|
194
|
+
if file.nil?
|
195
|
+
code = ""
|
196
|
+
_body {|line| code << " " + line + "\n" }
|
197
|
+
else
|
198
|
+
code = ::File.read(file)
|
199
|
+
end
|
200
|
+
|
201
|
+
html = format_elixir(code)
|
202
|
+
_out html
|
203
|
+
end
|
204
|
+
end
|
data/imports/tutorial.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
# require 'cgi'
|
2
|
+
|
3
|
+
require 'helpers'
|
4
|
+
|
5
|
+
module Tutorial
|
6
|
+
include Helpers
|
7
|
+
|
8
|
+
def title(args = nil, body = nil)
|
9
|
+
h1
|
10
|
+
_optional_blank_line
|
11
|
+
end
|
12
|
+
|
13
|
+
def section(args = nil, body = nil)
|
14
|
+
h3
|
15
|
+
_optional_blank_line
|
16
|
+
end
|
17
|
+
|
18
|
+
def code(args = nil, body = nil)
|
19
|
+
first = true # dumb hack! fixes blank space
|
20
|
+
_body do |line|
|
21
|
+
tag, first = "<pre>", false if first
|
22
|
+
_out "#{tag} #{escape_html(line)}" # indentation
|
23
|
+
end
|
24
|
+
_out "</pre>"
|
25
|
+
_optional_blank_line
|
26
|
+
end
|
27
|
+
|
28
|
+
def rx(str)
|
29
|
+
::Regexp.compile(::Regexp.escape(str))
|
30
|
+
end
|
31
|
+
|
32
|
+
def inout(args = nil, body = nil)
|
33
|
+
src, out = _args
|
34
|
+
t1 = ::File.readlines(src) rescue (abort "t1 = #{src}")
|
35
|
+
t2 = ::File.readlines(out) rescue (abort "t2 = #{out}")
|
36
|
+
# To pacify markdown for README (FIXME later)
|
37
|
+
t1 = t1.map {|x| " " + x.sub(/ +$/,"").gsub(/_/, "\\_") }.join
|
38
|
+
t2 = t2.map {|x| " " + x.sub(/ +$/,"").gsub(/_/, "\\_") }.join
|
39
|
+
|
40
|
+
_out <<-HTML
|
41
|
+
<table width=80% cellpadding=4>
|
42
|
+
<tr>
|
43
|
+
<td width=50%><b>Input</b></td>
|
44
|
+
<td width=50%><b>Output</b></td>
|
45
|
+
</tr>
|
46
|
+
<tr>
|
47
|
+
<td width=50% bgcolor=#fee0fe valign=top>
|
48
|
+
<pre>#{t1}</pre>
|
49
|
+
</td>
|
50
|
+
<td width=50% bgcolor=#eeeeee valign=top>
|
51
|
+
<pre>#{t2}</pre>
|
52
|
+
</td>
|
53
|
+
</tr>
|
54
|
+
</table>
|
55
|
+
HTML
|
56
|
+
_optional_blank_line
|
57
|
+
end
|
58
|
+
|
59
|
+
def put_table(src, exp)
|
60
|
+
t1 = ::File.readlines(src) rescue (abort "t1 = #{src}")
|
61
|
+
t2 = ::File.readlines(exp) rescue (abort "t2 = #{out}")
|
62
|
+
t1 = t1.map {|x| " " + x.sub(/ +$/,"").gsub(/_/, "\\_") }.join
|
63
|
+
t2 = t2.map {|x| " " + x.sub(/ +$/,"").gsub(/_/, "\\_") }.join
|
64
|
+
|
65
|
+
_out <<-HTML
|
66
|
+
<font size=+1>
|
67
|
+
<table width=80% cellpadding=4>
|
68
|
+
<tr>
|
69
|
+
<td width=50%><b>Input</b></td>
|
70
|
+
<td width=50%><b>Output</b></td>
|
71
|
+
</tr>
|
72
|
+
<tr>
|
73
|
+
<td width=50% bgcolor=#fee0fe valign=top>
|
74
|
+
<pre>#{t1}</pre>
|
75
|
+
</td>
|
76
|
+
<td width=50% bgcolor=#eeeeee valign=top>
|
77
|
+
<pre>#{t2}</pre>
|
78
|
+
</td>
|
79
|
+
</tr>
|
80
|
+
</table>
|
81
|
+
</font>
|
82
|
+
HTML
|
83
|
+
end
|
84
|
+
|
85
|
+
def testcase(args = nil, body = nil)
|
86
|
+
name = _args.first
|
87
|
+
_out "\n<font size=+1><b>Test: </font><font size=+2><tt>#{name}</tt></font></b></h3><br>"
|
88
|
+
src, exp = "test/data/#{name}/source.lt3", "test/data/#{name}/expected-output.txt"
|
89
|
+
@_args = [src, exp] # Better way to do this??
|
90
|
+
put_table(src, exp)
|
91
|
+
_out "<br>"
|
92
|
+
_optional_blank_line
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
data/lib/livetext.rb
CHANGED
data/livetext.gemspec
CHANGED
@@ -15,7 +15,8 @@ Gem::Specification.new do |s|
|
|
15
15
|
s.executables << "livetext"
|
16
16
|
|
17
17
|
# Files...
|
18
|
-
main = Find.find("bin").to_a + Find.find("lib").to_a +
|
18
|
+
main = Find.find("bin").to_a + Find.find("lib").to_a +
|
19
|
+
Find.find("plugin").to_a + Find.find("imports").to_a
|
19
20
|
misc = %w[./README.lt3 ./README.md livetext.gemspec]
|
20
21
|
test = Find.find("test").to_a
|
21
22
|
|
data/test/snapshots/clusion.txt
CHANGED
@@ -3,7 +3,21 @@
|
|
3
3
|
# Blank lines and comments are ignored.
|
4
4
|
# Other lines: name_of_snapshot and any comments here are ignored (no # needed)
|
5
5
|
|
6
|
-
#
|
6
|
+
# You can start by uncommenting one of these:
|
7
|
+
# Default INCLUDE all
|
8
|
+
# Default EXCLUDE all
|
9
|
+
# If you use neither, INCLUDE is the default (but explicit is better)
|
10
|
+
|
11
|
+
# If you include all, you can specify exclusions with an x:
|
12
|
+
# x my_test_name # this is a comment
|
13
|
+
|
14
|
+
# Likewise if you EXCLUDE all, you can specify inclusions with an i:
|
15
|
+
# i some_other_test # this is a comment
|
16
|
+
|
17
|
+
# The i and x (to include/exclude) must be the first token on
|
18
|
+
# the line (preceding the test name)
|
19
|
+
|
20
|
+
# Note that QUIT (on a line by itself) will stop processing the file
|
7
21
|
|
8
22
|
x error_no_such_inc # Output BEFORE error doesn't get passed through ("leading" output)
|
9
23
|
x error_no_such_copy # ^ Same behavior as error_no_such_inc
|
data/test/snapshots.rb
CHANGED
@@ -43,6 +43,69 @@ class TestingLivetext < MiniTest::Test
|
|
43
43
|
EXP_OUT, EXP_ERR = "expected-output.txt", "expected-error.txt"
|
44
44
|
MATCH_OUT, MATCH_ERR = "match-output.txt", "match-error.txt"
|
45
45
|
|
46
|
+
TTY = File.open("/dev/tty","w")
|
47
|
+
|
48
|
+
def self.get_dir # FIXME - uh what?
|
49
|
+
cmdline = ARGV.first == "cmdline" # FIXME remove??
|
50
|
+
if cmdline
|
51
|
+
dir = "../"
|
52
|
+
Dir.chdir `livetext --path`.chomp.chomp
|
53
|
+
else
|
54
|
+
dir = ""
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
args = ARGV - ["cmdline"]
|
59
|
+
|
60
|
+
dir = self.get_dir
|
61
|
+
|
62
|
+
Data = "#{dir}test/snapshots"
|
63
|
+
Dir.chdir(Data)
|
64
|
+
|
65
|
+
TestDirs = Dir.entries(".").reject {|fname| ! File.directory?(fname) } - %w[. ..]
|
66
|
+
|
67
|
+
|
68
|
+
def self.filter # TODO move subset/omit logic here??
|
69
|
+
all = Dir.entries(Data).reject {|fname| ! File.directory?(fname) } - %w[. ..]
|
70
|
+
included, excluded = [], []
|
71
|
+
@iflag, @eflag = true, false # defaults to INCLUDE
|
72
|
+
control = File.new("clusion.txt")
|
73
|
+
control.each_line do |line|
|
74
|
+
line.sub!(/#.*/, "")
|
75
|
+
line.strip!
|
76
|
+
lower = line.downcase
|
77
|
+
dejavu = false
|
78
|
+
case
|
79
|
+
when lower.empty?
|
80
|
+
# ignore
|
81
|
+
when lower == "default include all"
|
82
|
+
raise "Only one 'default' allowed" if dejavu
|
83
|
+
@iflag, @eflag = true, false # defaults to INCLUDE
|
84
|
+
dejavu = true
|
85
|
+
when lower == "default exclude all"
|
86
|
+
raise "Only one 'default' allowed" if dejavu
|
87
|
+
@iflag, @eflag = false, true
|
88
|
+
dejavu = true
|
89
|
+
when lower == "quit"
|
90
|
+
break
|
91
|
+
when lower[0] == "i"
|
92
|
+
TTY.puts "Warning: Can't include with 'i' when that is default" if @iflag
|
93
|
+
included << line.split(" ", 2)[1] # add to included
|
94
|
+
when lower[0] == "x"
|
95
|
+
TTY.puts "Warning: Can't exclude with 'x' when that is default" if @eflag
|
96
|
+
excluded << line.split(" ", 2)[1] # add to excluded
|
97
|
+
# add to excluded
|
98
|
+
end
|
99
|
+
[included, excluded]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
i, e = self.filter
|
104
|
+
p i
|
105
|
+
p e
|
106
|
+
|
107
|
+
exit
|
108
|
+
|
46
109
|
def initialize(base, assertion = nil)
|
47
110
|
@assertion = assertion
|
48
111
|
@base = base
|
@@ -109,9 +172,6 @@ class TestingLivetext < MiniTest::Test
|
|
109
172
|
system("rm -f #{ACTUAL_OUT} #{ACTUAL_ERR} *sdiff.txt")
|
110
173
|
end
|
111
174
|
|
112
|
-
def filter # TODO move subset/omit logic here??
|
113
|
-
end
|
114
|
-
|
115
175
|
def run
|
116
176
|
@errors = false # oops, need to reset
|
117
177
|
Dir.chdir(@base) do
|
@@ -124,27 +184,6 @@ class TestingLivetext < MiniTest::Test
|
|
124
184
|
end
|
125
185
|
end
|
126
186
|
|
127
|
-
def self.get_dir # FIXME - uh what?
|
128
|
-
cmdline = ARGV.first == "cmdline" # FIXME remove??
|
129
|
-
if cmdline
|
130
|
-
dir = "../"
|
131
|
-
Dir.chdir `livetext --path`.chomp.chomp
|
132
|
-
else
|
133
|
-
dir = ""
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
TTY = File.open("/dev/tty","w")
|
138
|
-
|
139
|
-
args = ARGV - ["cmdline"]
|
140
|
-
|
141
|
-
dir = self.get_dir
|
142
|
-
|
143
|
-
Data = "#{dir}test/snapshots"
|
144
|
-
Dir.chdir(Data)
|
145
|
-
|
146
|
-
TestDirs = Dir.entries(".").reject {|fname| ! File.directory?(fname) } - %w[. ..]
|
147
|
-
|
148
187
|
if args.empty?
|
149
188
|
selected = File.readlines("subset.txt").map(&:chomp)
|
150
189
|
omitfile = "OMIT.txt"
|
@@ -166,7 +205,6 @@ class TestingLivetext < MiniTest::Test
|
|
166
205
|
Subset = args
|
167
206
|
end
|
168
207
|
|
169
|
-
|
170
208
|
Subset.each do |tdir|
|
171
209
|
define_method("test_#{tdir}") do
|
172
210
|
myproc = Proc.new {|bool, info| assert bool, info }
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: livetext
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.19
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hal Fulton
|
@@ -20,6 +20,13 @@ files:
|
|
20
20
|
- "./README.lt3"
|
21
21
|
- "./README.md"
|
22
22
|
- bin/livetext
|
23
|
+
- imports/bookish.rb
|
24
|
+
- imports/calibre.rb
|
25
|
+
- imports/livemagick.rb
|
26
|
+
- imports/markdown.rb
|
27
|
+
- imports/markdown_importable.rb
|
28
|
+
- imports/pyggish.rb
|
29
|
+
- imports/tutorial.rb
|
23
30
|
- lib/cmdargs.rb
|
24
31
|
- lib/errors.rb
|
25
32
|
- lib/formatline.rb
|