notroff 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/notroff +47 -0
- data/lib/notroff/code_scrubber.rb +9 -0
- data/lib/notroff/code_typer.rb +37 -0
- data/lib/notroff/command_processor.rb +28 -0
- data/lib/notroff/composite_processor.rb +52 -0
- data/lib/notroff/content.xml.erb +110 -0
- data/lib/notroff/docbook_renderer.rb +155 -0
- data/lib/notroff/embedded.rb +132 -0
- data/lib/notroff/filter.rb +15 -0
- data/lib/notroff/formatter.rb +54 -0
- data/lib/notroff/html_renderer.rb +92 -0
- data/lib/notroff/io.rb +18 -0
- data/lib/notroff/logger.rb +13 -0
- data/lib/notroff/odt_renderer.rb +154 -0
- data/lib/notroff/odt_replacer.rb +22 -0
- data/lib/notroff/paragraph_joiner.rb +53 -0
- data/lib/notroff/processor.rb +192 -0
- data/lib/notroff/skel.odt +0 -0
- data/lib/notroff/string_extensions.rb +6 -0
- data/lib/notroff/template_expander.rb +15 -0
- data/lib/notroff/text.rb +67 -0
- data/lib/notroff/text_replacer.rb +65 -0
- data/lib/notroff/tokenize.rb +64 -0
- data/lib/notroff/type_assigner.rb +25 -0
- data/lib/notroff.rb +23 -0
- data/readme.nr +58 -0
- data/spec/command_processor_spec.rb +15 -0
- data/spec/composite_processor_spec.rb +35 -0
- data/spec/filter_spec.rb +16 -0
- data/spec/formatter_spec.rb +22 -0
- data/spec/hello.rb +1 -0
- data/spec/hello.rb.out +1 -0
- data/spec/paragraph_joiner_spec.rb +26 -0
- data/spec/simple.nr +3 -0
- data/spec/string_extensions_spec.rb +22 -0
- data/spec/text_spec.rb +55 -0
- data/spec/type_assigner_spec.rb +39 -0
- data/spec/with_commands.nr +6 -0
- metadata +83 -0
data/bin/notroff
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'pp'
|
3
|
+
require "notroff"
|
4
|
+
|
5
|
+
options={}
|
6
|
+
formatter_class = HtmlFormatter
|
7
|
+
|
8
|
+
while true
|
9
|
+
if ARGV.first == '-o'
|
10
|
+
formatter_class = OdtFormatter
|
11
|
+
ARGV.shift
|
12
|
+
elsif ARGV.first == '-d'
|
13
|
+
formatter_class = DocbookFormatter
|
14
|
+
ARGV.shift
|
15
|
+
elsif ARGV.first == '-v'
|
16
|
+
Logger.verbose = true
|
17
|
+
ARGV.shift
|
18
|
+
else
|
19
|
+
break
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
unless ARGV.size == 2
|
24
|
+
raise "Usage: notroff [-d] <nr file> <output file>"
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
f = formatter_class.new( ARGV[0], ARGV[1] )
|
29
|
+
f.process
|
30
|
+
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class CodeTypeRefiner
|
2
|
+
def process( paragraphs )
|
3
|
+
processed_paragraphs = []
|
4
|
+
|
5
|
+
previous_type = nil
|
6
|
+
|
7
|
+
paragraphs.each_with_index do |paragraph, i|
|
8
|
+
type = paragraph[:type]
|
9
|
+
previous_type = ( paragraphs.first == paragraph) ? nil : paragraphs[i-1][:type]
|
10
|
+
next_type = ( paragraphs.last == paragraph) ? nil : paragraphs[i+1][:type]
|
11
|
+
new_type = code_type_for( previous_type, type, next_type )
|
12
|
+
paragraph[:type] = new_type
|
13
|
+
processed_paragraphs << paragraph
|
14
|
+
end
|
15
|
+
processed_paragraphs
|
16
|
+
end
|
17
|
+
|
18
|
+
def code_type_for( previous_type, type, next_type )
|
19
|
+
if type != :code
|
20
|
+
new_type = type
|
21
|
+
|
22
|
+
elsif previous_type == :code and next_type == :code
|
23
|
+
new_type = :middle_code
|
24
|
+
|
25
|
+
elsif previous_type == :code
|
26
|
+
new_type = :end_code
|
27
|
+
|
28
|
+
elsif next_type == :code
|
29
|
+
new_type = :first_code
|
30
|
+
|
31
|
+
else
|
32
|
+
new_type = :single_code
|
33
|
+
end
|
34
|
+
|
35
|
+
new_type
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class CommandProcessor
|
2
|
+
def process( lines )
|
3
|
+
paragraphs = []
|
4
|
+
lines.each do |line|
|
5
|
+
cmd, text = parse_line( line )
|
6
|
+
if cmd.empty?
|
7
|
+
cmd = :text
|
8
|
+
else
|
9
|
+
cmd = cmd.sub(/^./, '').to_sym
|
10
|
+
end
|
11
|
+
para = Text.new(text, :type => cmd, :original_text => line.to_s)
|
12
|
+
paragraphs << para
|
13
|
+
end
|
14
|
+
paragraphs
|
15
|
+
end
|
16
|
+
|
17
|
+
def parse_line( line )
|
18
|
+
match_data = /^(\.\w+ ?)(.*)/.match(line)
|
19
|
+
if match_data
|
20
|
+
cmd = match_data[1].strip
|
21
|
+
text = match_data[2]
|
22
|
+
else
|
23
|
+
cmd = ''
|
24
|
+
text = line
|
25
|
+
end
|
26
|
+
[ cmd.strip, text ]
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
class CompositeProcessor
|
2
|
+
def initialize(*args)
|
3
|
+
@processors = args
|
4
|
+
end
|
5
|
+
|
6
|
+
def add_processor(p)
|
7
|
+
@processors << p
|
8
|
+
end
|
9
|
+
|
10
|
+
def prepend_processor(p)
|
11
|
+
@processors = [p] + @processors
|
12
|
+
end
|
13
|
+
|
14
|
+
def process(paras=[])
|
15
|
+
@processors.each do |processor|
|
16
|
+
Logger.log "Applying processor #{processor.class} to #{paras.size} paragraphs"
|
17
|
+
dump(paras)
|
18
|
+
Logger.log
|
19
|
+
paras = processor.process( paras )
|
20
|
+
Logger.log "After processor #{processor.class}"
|
21
|
+
Logger.log
|
22
|
+
end
|
23
|
+
paras
|
24
|
+
end
|
25
|
+
|
26
|
+
def dump(data)
|
27
|
+
return unless Verbose
|
28
|
+
Logger.log "======="
|
29
|
+
if data.nil?
|
30
|
+
Logger.log "data: nil"
|
31
|
+
elsif data.kind_of?(Array)
|
32
|
+
Logger.log "data with #{data.size} items:"
|
33
|
+
data.each_with_index {|item, i| Logger.log "[#{i}] - #{item.inspect}" }
|
34
|
+
else
|
35
|
+
Logger.log data
|
36
|
+
end
|
37
|
+
Logger.log "======="
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
|
@@ -0,0 +1,110 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
2
|
+
<office:document-content xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
|
3
|
+
xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0"
|
4
|
+
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"
|
5
|
+
xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0"
|
6
|
+
xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"
|
7
|
+
xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"
|
8
|
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
9
|
+
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
10
|
+
xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0"
|
11
|
+
xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0"
|
12
|
+
xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"
|
13
|
+
xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0"
|
14
|
+
xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0"
|
15
|
+
xmlns:math="http://www.w3.org/1998/Math/MathML"
|
16
|
+
xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0"
|
17
|
+
xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0"
|
18
|
+
xmlns:ooo="http://openoffice.org/2004/office"
|
19
|
+
xmlns:ooow="http://openoffice.org/2004/writer"
|
20
|
+
xmlns:oooc="http://openoffice.org/2004/calc"
|
21
|
+
xmlns:dom="http://www.w3.org/2001/xml-events"
|
22
|
+
xmlns:xforms="http://www.w3.org/2002/xforms"
|
23
|
+
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
24
|
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
25
|
+
xmlns:field="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:field:1.0"
|
26
|
+
office:version="1.1">
|
27
|
+
<office:scripts />
|
28
|
+
<office:font-face-decls>
|
29
|
+
<style:font-face style:name="Wingdings"
|
30
|
+
svg:font-family="Wingdings" style:font-pitch="variable"
|
31
|
+
style:font-charset="x-symbol" />
|
32
|
+
<style:font-face style:name="Symbol" svg:font-family="Symbol"
|
33
|
+
style:font-family-generic="roman" style:font-pitch="variable"
|
34
|
+
style:font-charset="x-symbol" />
|
35
|
+
<style:font-face style:name="Courier New"
|
36
|
+
svg:font-family="'Courier New'"
|
37
|
+
style:font-family-generic="modern" />
|
38
|
+
<style:font-face style:name="Courier"
|
39
|
+
svg:font-family="Courier, 'Courier New'"
|
40
|
+
style:font-family-generic="modern" />
|
41
|
+
<style:font-face style:name="Nimbus Mono L"
|
42
|
+
svg:font-family="'Nimbus Mono L', 'Courier New'"
|
43
|
+
style:font-family-generic="modern" />
|
44
|
+
<style:font-face style:name="Nimbus Roman No9 L"
|
45
|
+
svg:font-family="'Nimbus Roman No9 L'"
|
46
|
+
style:font-family-generic="roman"
|
47
|
+
style:font-pitch="variable" />
|
48
|
+
<style:font-face style:name="Times" svg:font-family="Times"
|
49
|
+
style:font-family-generic="roman"
|
50
|
+
style:font-pitch="variable" />
|
51
|
+
<style:font-face style:name="Times New Roman"
|
52
|
+
svg:font-family="'Times New Roman'"
|
53
|
+
style:font-family-generic="roman"
|
54
|
+
style:font-pitch="variable" />
|
55
|
+
<style:font-face style:name="Arial" svg:font-family="Arial"
|
56
|
+
style:font-family-generic="swiss"
|
57
|
+
style:font-pitch="variable" />
|
58
|
+
<style:font-face style:name="Arial Unicode MS"
|
59
|
+
svg:font-family="'Arial Unicode MS'"
|
60
|
+
style:font-family-generic="swiss"
|
61
|
+
style:font-pitch="variable" />
|
62
|
+
<style:font-face style:name="Helvetica"
|
63
|
+
svg:font-family="Helvetica" style:font-family-generic="swiss"
|
64
|
+
style:font-pitch="variable" />
|
65
|
+
<style:font-face style:name="Tahoma" svg:font-family="Tahoma"
|
66
|
+
style:font-family-generic="swiss"
|
67
|
+
style:font-pitch="variable" />
|
68
|
+
<style:font-face style:name="Verdana" svg:font-family="Verdana"
|
69
|
+
style:font-family-generic="swiss"
|
70
|
+
style:font-pitch="variable" />
|
71
|
+
<style:font-face style:name="DejaVu Sans"
|
72
|
+
svg:font-family="'DejaVu Sans'"
|
73
|
+
style:font-family-generic="system"
|
74
|
+
style:font-pitch="variable" />
|
75
|
+
</office:font-face-decls>
|
76
|
+
<office:automatic-styles>
|
77
|
+
<style:style style:name="P1" style:family="paragraph"
|
78
|
+
style:parent-style-name="Standard">
|
79
|
+
<style:paragraph-properties fo:text-align="end"
|
80
|
+
style:justify-single-word="false" />
|
81
|
+
</style:style>
|
82
|
+
<style:style style:name="P2" style:family="paragraph"
|
83
|
+
style:parent-style-name="HA" style:master-page-name="Standard">
|
84
|
+
<style:paragraph-properties style:page-number="1" />
|
85
|
+
</style:style>
|
86
|
+
<style:style style:name="P3" style:family="paragraph"
|
87
|
+
style:parent-style-name="FTN">
|
88
|
+
<style:paragraph-properties fo:margin-left="0.1598in"
|
89
|
+
fo:margin-right="0in" fo:margin-top="0.0417in"
|
90
|
+
fo:margin-bottom="0.0417in"
|
91
|
+
style:line-height-at-least="0.1807in"
|
92
|
+
fo:text-indent="-0.1598in" style:auto-text-indent="false" />
|
93
|
+
</style:style>
|
94
|
+
<style:style style:name="T1" style:family="text">
|
95
|
+
<style:text-properties fo:font-style="italic"
|
96
|
+
style:font-style-asian="italic"
|
97
|
+
style:font-style-complex="italic" />
|
98
|
+
</style:style>
|
99
|
+
<style:style style:name="T2" style:family="text">
|
100
|
+
<style:text-properties fo:font-weight="bold"
|
101
|
+
style:font-weight-asian="bold"
|
102
|
+
style:font-weight-complex="bold" />
|
103
|
+
</style:style>
|
104
|
+
</office:automatic-styles>
|
105
|
+
<office:body>
|
106
|
+
<office:text>
|
107
|
+
<%=content%>
|
108
|
+
</office:text>
|
109
|
+
</office:body>
|
110
|
+
</office:document-content>
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
require 'pp'
|
3
|
+
|
4
|
+
class DocbookRenderer < Processor
|
5
|
+
include Tokenize
|
6
|
+
include REXML
|
7
|
+
|
8
|
+
def process( paragraphs )
|
9
|
+
@author = 'John Smith'
|
10
|
+
@title = 'Wealth of Nations'
|
11
|
+
@chapters = []
|
12
|
+
@chapter = nil
|
13
|
+
@section = nil
|
14
|
+
|
15
|
+
paragraphs.each do |paragraph|
|
16
|
+
format( paragraph )
|
17
|
+
end
|
18
|
+
@book = Element.new('book')
|
19
|
+
@book.add_namespace('http://docbook.org/ns/docbook')
|
20
|
+
@book.add_attribute('version', '5.0')
|
21
|
+
@book.add_element element_for('title', @title)
|
22
|
+
@book.add_element element_for('author', @author)
|
23
|
+
@chapters.each {|ch| @book << ch}
|
24
|
+
doc = Document.new
|
25
|
+
decl = XMLDecl.new
|
26
|
+
decl.version = '1.0'
|
27
|
+
doc << decl
|
28
|
+
doc << @book
|
29
|
+
doc
|
30
|
+
end
|
31
|
+
|
32
|
+
def element_for(type, text)
|
33
|
+
result = Element.new(type)
|
34
|
+
result.add_text(text)
|
35
|
+
result
|
36
|
+
end
|
37
|
+
|
38
|
+
def format( p )
|
39
|
+
type = p[:type]
|
40
|
+
text = p.string
|
41
|
+
|
42
|
+
return nil if text.empty? and :type != :code
|
43
|
+
|
44
|
+
case type
|
45
|
+
when :title
|
46
|
+
@title = text
|
47
|
+
when :author
|
48
|
+
@author = text
|
49
|
+
when :chapter
|
50
|
+
Logger.log "adding chapter #{text}"
|
51
|
+
@chapter = Element.new('chapter')
|
52
|
+
@chapters << @chapter
|
53
|
+
@section = nil
|
54
|
+
title_element = Element.new('title')
|
55
|
+
add_body_text(title_element, text)
|
56
|
+
@chapter.add_element(title_element)
|
57
|
+
when :section
|
58
|
+
Logger.log "adding section #{text}"
|
59
|
+
@section = Element.new('section')
|
60
|
+
@chapter.add(@section)
|
61
|
+
title_element = Element.new('title')
|
62
|
+
add_body_text(title_element, text)
|
63
|
+
@section.add_element(title_element)
|
64
|
+
when :body
|
65
|
+
Logger.log "adding body #{text[0..5]}"
|
66
|
+
paragraph = Element.new('para')
|
67
|
+
add_body_text(paragraph, text)
|
68
|
+
add_content_element(paragraph)
|
69
|
+
when :code
|
70
|
+
add_content_element(code_element(type, text))
|
71
|
+
else
|
72
|
+
raise "#{type}???"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def add_content_element(el)
|
77
|
+
if @section
|
78
|
+
@section.add_element(el)
|
79
|
+
elsif @chapter
|
80
|
+
@chapter.add_element(el)
|
81
|
+
else
|
82
|
+
raise "No chapter to add #{el} to"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def text_element(type, text)
|
87
|
+
element = [ tag_for(type) ]
|
88
|
+
add_body_text(element, text)
|
89
|
+
element
|
90
|
+
end
|
91
|
+
|
92
|
+
def tag_for(type)
|
93
|
+
case type
|
94
|
+
when :body
|
95
|
+
'para'
|
96
|
+
when :text
|
97
|
+
'p'
|
98
|
+
when :author
|
99
|
+
'h3'
|
100
|
+
when :section
|
101
|
+
'h3'
|
102
|
+
when :chapter
|
103
|
+
'chapter'
|
104
|
+
when :sec
|
105
|
+
'h3'
|
106
|
+
when :title
|
107
|
+
'h2'
|
108
|
+
else
|
109
|
+
raise "Dont know what to do with #{type}"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def add_body_text( element, text )
|
114
|
+
tokens = tokenize_body_text( text )
|
115
|
+
tokens.each {|token| add_span( token, element ) }
|
116
|
+
end
|
117
|
+
|
118
|
+
def add_span( token, element )
|
119
|
+
Logger.log "Add span: token: #{token} element: #{element}"
|
120
|
+
case token[:type]
|
121
|
+
when :italic
|
122
|
+
element.add( span_for( token.string, "emphasis" ))
|
123
|
+
when :code
|
124
|
+
element.add( span_for( token.string, "code" ))
|
125
|
+
when :bold
|
126
|
+
element.add( span_for( token.string, "emphasis" ))
|
127
|
+
when :normal
|
128
|
+
element.add_text( token.string )
|
129
|
+
when :footnote
|
130
|
+
element.add(footnote_for(token.string))
|
131
|
+
else
|
132
|
+
raise "Dont know what to do with #{token}"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def footnote_for( text )
|
137
|
+
fn = Element.new('footnote')
|
138
|
+
fn.add_element(element_for('para', text))
|
139
|
+
fn
|
140
|
+
end
|
141
|
+
|
142
|
+
def code_element(type, text)
|
143
|
+
element = Element.new('informalexample')
|
144
|
+
prog_element = element_for('programlisting', text)
|
145
|
+
prog_element.add_attribute('xml:space', 'preserve')
|
146
|
+
element.add_element(prog_element)
|
147
|
+
element
|
148
|
+
end
|
149
|
+
|
150
|
+
def span_for( text, style )
|
151
|
+
span = Element.new(style)
|
152
|
+
span.text = remove_escapes(text)
|
153
|
+
span
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
class BetweenFilter
|
2
|
+
def initialize(re1, re2=re1)
|
3
|
+
@re1 = re1
|
4
|
+
@re2 = re2
|
5
|
+
end
|
6
|
+
|
7
|
+
def process(paras)
|
8
|
+
state = :before_first
|
9
|
+
paras.each do |para|
|
10
|
+
if state == :before_first and @re1 =~ para
|
11
|
+
para[:included] = true
|
12
|
+
state = :after_first
|
13
|
+
break if para =~ @re2
|
14
|
+
elsif state == :after_first
|
15
|
+
para[:included] = true
|
16
|
+
break if para =~ @re2
|
17
|
+
end
|
18
|
+
end
|
19
|
+
paras
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class DefinitionFilter
|
24
|
+
def initialize(def_re, include_body=true)
|
25
|
+
@def_re = def_re
|
26
|
+
@include_body = include_body
|
27
|
+
end
|
28
|
+
|
29
|
+
def process(paras)
|
30
|
+
state = :before_def
|
31
|
+
end_re = nil
|
32
|
+
paras.each do |para|
|
33
|
+
Logger.log para, (@def_re =~ para)
|
34
|
+
if state == :before_def and @def_re =~ para
|
35
|
+
para[:included] = true
|
36
|
+
end_re = Regexp.new( "^#{' ' * para.string.indent_depth}end" )
|
37
|
+
state = :after_def
|
38
|
+
elsif state == :after_def and end_re =~ para.string
|
39
|
+
para[:included] = true
|
40
|
+
break
|
41
|
+
elsif state == :after_def and @include_body
|
42
|
+
para[:included] = true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
paras
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class MethodFilter < DefinitionFilter
|
50
|
+
def initialize(method_name, include_body)
|
51
|
+
super /^ *def +#{method_name}(\(|$| )/, include_body
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class ClassFilter < DefinitionFilter
|
56
|
+
def initialize(class_name, include_body)
|
57
|
+
super /^ *class +#{class_name}(\(|$| )/, include_body
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class ModuleFilter < DefinitionFilter
|
62
|
+
def initialize(module_name, include_body)
|
63
|
+
super /^ *module +#{module_name}(\(|$| )/, include_body
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class EmbeddedRubyProcessor
|
68
|
+
def process(paragraphs)
|
69
|
+
new_paragraphs = []
|
70
|
+
paragraphs.each do |p|
|
71
|
+
if p[:type] == :x
|
72
|
+
Logger.log p
|
73
|
+
results = process_command(p.string)
|
74
|
+
new_paragraphs << results if results
|
75
|
+
else
|
76
|
+
new_paragraphs << p
|
77
|
+
end
|
78
|
+
end
|
79
|
+
new_paragraphs.flatten
|
80
|
+
end
|
81
|
+
|
82
|
+
def process_command(ruby_expression)
|
83
|
+
Logger.log "Ruby expression: #{ruby_expression}"
|
84
|
+
lines = eval(ruby_expression, binding)
|
85
|
+
end
|
86
|
+
|
87
|
+
def embed(*filters, &block)
|
88
|
+
paras = block.call.map {|line| line.rstrip}
|
89
|
+
paras.map! {|p| Text.new(p, :type => :code)}
|
90
|
+
Logger.log "EMBED: #{paras}"
|
91
|
+
unless filters.empty?
|
92
|
+
filters.each {|f| paras = f.process(paras)}
|
93
|
+
paras = paras.find_all {|p| p[:included]}
|
94
|
+
end
|
95
|
+
paras
|
96
|
+
end
|
97
|
+
|
98
|
+
def inc(path, *filters)
|
99
|
+
embed(*filters) {File.readlines(path)}
|
100
|
+
end
|
101
|
+
|
102
|
+
def run(command, *filters)
|
103
|
+
embed(*filters) {File.popen(command).readlines}
|
104
|
+
end
|
105
|
+
|
106
|
+
def matches(re1, re2=re1)
|
107
|
+
BetweenFilter.new(re1, re2)
|
108
|
+
end
|
109
|
+
|
110
|
+
def method(name, include_body=true)
|
111
|
+
MethodFilter.new(name, include_body)
|
112
|
+
end
|
113
|
+
|
114
|
+
def clazz(name, include_body=true)
|
115
|
+
ClassFilter.new(name, include_body)
|
116
|
+
end
|
117
|
+
|
118
|
+
def mod(name, include_body=true)
|
119
|
+
ModuleFilter.new(name, include_body)
|
120
|
+
end
|
121
|
+
|
122
|
+
def indent(delta_indent, paragraphs)
|
123
|
+
paragraphs.map do |p|
|
124
|
+
if delta_indent > 0
|
125
|
+
p.string = (" " * delta_indent) + p.string
|
126
|
+
elsif delta_indent < 0
|
127
|
+
p.string.sub!( ' ' * delta_indent.abs, '' )
|
128
|
+
end
|
129
|
+
end
|
130
|
+
paragraphs
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
class Formatter < CompositeProcessor
|
2
|
+
def initialize()
|
3
|
+
super
|
4
|
+
add_processor CommandProcessor.new
|
5
|
+
add_processor TypeAssigner.new
|
6
|
+
add_processor EmbeddedRubyProcessor.new
|
7
|
+
add_processor CodeScrubber.new
|
8
|
+
add_processor BodyParagraphJoiner.new
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class HtmlFormatter < Formatter
|
13
|
+
def initialize(input, output)
|
14
|
+
super()
|
15
|
+
prepend_processor FileReader.new(input)
|
16
|
+
add_processor CodeParagraphJoiner.new
|
17
|
+
add_processor HtmlRenderer.new
|
18
|
+
add_processor FileWriter.new(output)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class DocbookFormatter < Formatter
|
23
|
+
def initialize(input, output)
|
24
|
+
super()
|
25
|
+
prepend_processor FileReader.new(input)
|
26
|
+
add_processor CodeParagraphJoiner.new
|
27
|
+
add_processor DocbookRenderer.new
|
28
|
+
add_processor FileWriter.new(output)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class OdtFormatter < Formatter
|
33
|
+
def initialize(input, output)
|
34
|
+
super()
|
35
|
+
prepend_processor FileReader.new(input)
|
36
|
+
add_processor CodeTypeRefiner.new
|
37
|
+
add_processor OdtRenderer.new
|
38
|
+
add_processor TemplateExpander.new
|
39
|
+
add_processor OdtReplacer.new(output)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
|
54
|
+
|