elisp2any 0.0.5 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -0
  3. data/LICENSE.txt +674 -202
  4. data/README.md +31 -7
  5. data/Rakefile +5 -52
  6. data/exe/elisp2any +27 -17
  7. data/lib/elisp/comment.rb +32 -0
  8. data/lib/elisp/default.css +11 -0
  9. data/lib/elisp/doc_string_parser.rb +96 -0
  10. data/lib/elisp/heading.rb +31 -0
  11. data/lib/elisp/parser.rb +161 -0
  12. data/lib/elisp/version.rb +18 -0
  13. data/lib/elisp.rb +99 -0
  14. metadata +12 -96
  15. data/.document +0 -4
  16. data/.envrc +0 -3
  17. data/.rdoc_options +0 -2
  18. data/.rubocop.yml +0 -11
  19. data/lib/elisp2any/asciidoc_renderer/index.adoc.erb +0 -15
  20. data/lib/elisp2any/asciidoc_renderer.rb +0 -57
  21. data/lib/elisp2any/aside.rb +0 -23
  22. data/lib/elisp2any/blanklines.rb +0 -20
  23. data/lib/elisp2any/code.rb +0 -49
  24. data/lib/elisp2any/codeblock.rb +0 -56
  25. data/lib/elisp2any/comment.rb +0 -19
  26. data/lib/elisp2any/commentary.rb +0 -32
  27. data/lib/elisp2any/expression.rb +0 -90
  28. data/lib/elisp2any/file.rb +0 -63
  29. data/lib/elisp2any/footer_line.rb +0 -28
  30. data/lib/elisp2any/header_line.rb +0 -80
  31. data/lib/elisp2any/heading.rb +0 -68
  32. data/lib/elisp2any/html_renderer/index.html.erb +0 -23
  33. data/lib/elisp2any/html_renderer.erb +0 -22
  34. data/lib/elisp2any/html_renderer.rb +0 -149
  35. data/lib/elisp2any/inline_code.rb +0 -10
  36. data/lib/elisp2any/line.rb +0 -52
  37. data/lib/elisp2any/node.rb +0 -67
  38. data/lib/elisp2any/paragraph.rb +0 -37
  39. data/lib/elisp2any/section.rb +0 -49
  40. data/lib/elisp2any/text.rb +0 -33
  41. data/lib/elisp2any/tree_sitter_parser.rb +0 -36
  42. data/lib/elisp2any/version.rb +0 -4
  43. data/lib/elisp2any.rb +0 -27
  44. data/manifest.scm +0 -87
  45. data/sig/elisp2any.gen.rbs +0 -112
  46. data/sig/elisp2any.rbs +0 -4
data/README.md CHANGED
@@ -15,12 +15,24 @@ $ elisp2any --input /path/to/input/file --output /path/to/output/file
15
15
 
16
16
  ## Development
17
17
 
18
- After checking out the repo, run `bin/setup` to install dependencies.
19
- Then, run `rake test` to run the tests.
20
- You can also run `bin/console` for an interactive prompt that will allow you to experiment.
18
+ To run tests, run `test-unit`.
21
19
 
22
- To install this gem onto your local machine, run `bundle exec rake install`.
23
- To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
20
+ Two paragraph types:
21
+
22
+ ```emacs-lisp
23
+ ;; This is a major one.
24
+
25
+ ;; This is another major one (1), and a minor one.
26
+ ;;
27
+ ;; This is still (1) but another minor one.
28
+ ```
29
+
30
+ Heading levels:
31
+
32
+ ```emacs-lisp
33
+ ;;; Level 2
34
+ ;;;; Level 3
35
+ ```
24
36
 
25
37
  ## Contributing
26
38
 
@@ -28,5 +40,17 @@ Bug reports and pull requests are welcome.
28
40
 
29
41
  ## License
30
42
 
31
- This package is provided under the Apache License.
32
- See `LICENSE.txt` for details.
43
+ Copyright (C) 2025 gemmaro
44
+
45
+ This program is free software: you can redistribute it and/or modify
46
+ it under the terms of the GNU General Public License as published by
47
+ the Free Software Foundation, either version 3 of the License, or
48
+ (at your option) any later version.
49
+
50
+ This program is distributed in the hope that it will be useful,
51
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
52
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
53
+ GNU General Public License for more details.
54
+
55
+ You should have received a copy of the GNU General Public License
56
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
data/Rakefile CHANGED
@@ -1,55 +1,8 @@
1
1
  require 'bundler/gem_tasks'
2
- require 'rake/testtask'
2
+ require "open3"
3
3
 
4
- Rake::TestTask.new(:test) do |t|
5
- t.libs << 'test'
6
- t.libs << 'lib'
7
- t.test_files = FileList['test/**/*_test.rb']
8
- end
9
-
10
- default_tasks = %i[test]
11
-
12
- rubocop = true
13
-
14
- begin
15
- require 'rubocop/rake_task'
16
- rescue LoadError
17
- rubocop = false
18
- end
19
-
20
- if rubocop
21
- RuboCop::RakeTask.new
22
- default_tasks << :rubocop
23
- end
24
-
25
- task default: default_tasks
26
-
27
- require 'rdoc/task'
28
- RDoc::Task.new do |rdoc|
29
- readme = 'README.md'
30
- rdoc.main = readme
31
- rdoc.rdoc_files.include(readme, 'lib/**/*.rb')
32
- end
33
-
34
- desc 'start API document server'
35
- task :serve_api do
36
- serve('html')
37
- end
38
-
39
- desc 'generate type signatures'
40
- task :sig do
41
- sh 'typeprof', *Dir['lib/**/*.rb'], 'sig/elisp2any.rbs', '-o', 'sig/elisp2any.gen.rbs'
42
- end
43
-
44
- desc 'start HTML fixture server'
45
- task :serve_fixture do
46
- serve('fixtures/init')
47
- end
48
-
49
- def serve(path)
50
- sh 'ruby', '-run', '-e', 'httpd', path
51
- end
52
-
53
- file 'fixtures/init/adoc/index.html' => 'fixtures/init/index.adoc' do |t|
54
- sh 'asciidoctor', t.source, '--out-file', t.name
4
+ task :deploy do
5
+ out, _stat = Open3.capture2("./bin/dev")
6
+ File.write("/tmp/index.html", out)
7
+ sh "rsync", "-av", "/tmp/index.html", "pi:/srv/www/emacs/"
55
8
  end
data/exe/elisp2any CHANGED
@@ -1,40 +1,50 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ # Copyright (C) 2025 gemmaro
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
3
18
  require 'optparse'
4
19
 
5
20
  $LOAD_PATH << File.join(__dir__, "../lib")
6
- require 'elisp2any'
7
- require 'elisp2any/html_renderer'
21
+ require 'elisp'
8
22
 
9
23
  input = $stdin
10
24
  output = $stdout
11
25
  css = nil
12
- mode = :old
26
+ dump = false
13
27
 
14
28
  OptionParser.new do |parser|
15
- parser.on('--input=PATH') do |path|
29
+ parser.on('--input=PATH', 'ELisp file (default: stdin)') do |path|
16
30
  input = File.open(path)
17
31
  end
18
32
 
19
- parser.on('--output=PATH') do |path|
33
+ parser.on('--output=PATH', 'HTML file (default: stdout)') do |path|
20
34
  output = File.open(path, 'w')
21
35
  end
22
36
 
23
- parser.on('--css=PATH') do |path|
37
+ parser.on('--css=PATH', 'stylesheet') do |path|
24
38
  css = path
25
39
  end
26
40
 
27
- parser.on("--new") do
28
- mode = :new
29
- end
41
+ parser.on("--dump") { dump = true }
30
42
  end.parse!
31
43
 
32
- file = nil
33
- case mode
34
- in :new
35
- file = Elisp2any::File.scan(input)
36
- in :old
37
- file = Elisp2any::File.parse(input)
44
+ doc = Elisp.parse(input)
45
+ if dump
46
+ pp doc
47
+ exit
38
48
  end
39
- renderer = Elisp2any::HTMLRenderer.new(file, css:, mode:)
40
- output.write(renderer.render)
49
+
50
+ doc.write(output, css:)
@@ -0,0 +1,32 @@
1
+ # Copyright (C) 2025 gemmaro
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ class Elisp::Comment
17
+ def initialize(content)
18
+ @content = content
19
+ end
20
+
21
+ def <<(content)
22
+ @content << "\n#{content}"
23
+ end
24
+
25
+ def to_minor_para
26
+ Elisp::MinorPara.new(@content)
27
+ end
28
+
29
+ def html
30
+ self.class.parse(@content)
31
+ end
32
+ end
@@ -0,0 +1,11 @@
1
+ .description {
2
+ font-style: italic;
3
+ }
4
+ pre {
5
+ font-family: serif;
6
+ white-space: pre-wrap;
7
+ }
8
+ pre[class="code"] {
9
+ border-left: 2px solid black;
10
+ padding-left: 0.5rem;
11
+ }
@@ -0,0 +1,96 @@
1
+ # Copyright (C) 2025 gemmaro
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require "cgi"
17
+ require "uri"
18
+ require "strscan"
19
+
20
+ class Elisp::DocStringParser
21
+ URI_PATTERN = URI.regexp
22
+ URI_PATTERN_WITH_ANGLES = /[<](?<uri>#{ URI_PATTERN })[>]/
23
+ EMAIL = Regexp.new(URI::MailTo::EMAIL_REGEXP.to_s
24
+ .sub(/\A[(][?]-mix:\\A/, "(?-mix:")
25
+ .sub(/\\z[)]\z/, ")"))
26
+ EMAIL_WITH_ANGLES = /[<](?<address>#{ EMAIL })[>]/
27
+
28
+ def initialize(source)
29
+ @scanner = StringScanner.new(source)
30
+ end
31
+
32
+ def html
33
+ result = +""
34
+ normal = +""
35
+ until @scanner.eos?
36
+ if @scanner.skip(/[`](?<code>[A-Za-z!.-]+)[']/)
37
+ result << normal
38
+ normal.clear
39
+ code = CGI.escape_html(@scanner[:code])
40
+ result << "<code>#{code}</code>"
41
+ normal.clear
42
+ elsif @scanner.skip(/\\\\=(?<quote>['`])/)
43
+ normal << @scanner[:quote]
44
+ elsif (uri = scan_http_uri)
45
+ result << normal
46
+ normal.clear
47
+ result << html_url(uri)
48
+ elsif (address = scan_email)
49
+ result << normal
50
+ normal.clear
51
+ uri = CGI.escape(address)
52
+ address = CGI.escape_html(address)
53
+ result << %(<a href="mailto:#{uri}">#{address}</a>)
54
+ elsif @scanner.skip(/Copyright [(]C[)]/)
55
+ result << "Copyright &copy;"
56
+ else
57
+ normal << @scanner.getch
58
+ end
59
+ end
60
+ result << normal
61
+ normal.clear
62
+ result
63
+ end
64
+
65
+ def scan_email
66
+ if (address = @scanner.scan(EMAIL))
67
+ address
68
+ elsif @scanner.skip(EMAIL_WITH_ANGLES)
69
+ @scanner[:address]
70
+ else
71
+ return
72
+ end
73
+ end
74
+
75
+ def scan_http_uri
76
+ if (uri = @scanner.scan(URI_PATTERN))
77
+ elsif @scanner.skip(URI_PATTERN_WITH_ANGLES)
78
+ uri = @scanner[:uri]
79
+ else
80
+ return
81
+ end
82
+ uri = URI(uri)
83
+ unless uri.is_a?(URI::HTTP)
84
+ @scanner.unscan
85
+ return
86
+ end
87
+ uri
88
+ end
89
+
90
+ def html_url(url)
91
+ url = url.to_s
92
+ ref = CGI.escape(url)
93
+ label = CGI.escape_html(url)
94
+ %(<a class="pure-url" href="#{ref}">#{label}</a>)
95
+ end
96
+ end
@@ -0,0 +1,31 @@
1
+ # Copyright (C) 2025 gemmaro
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require "cgi"
17
+
18
+ class Elisp::Heading
19
+ attr_reader :level, :content
20
+
21
+ def initialize(content, level: 2)
22
+ @content = content
23
+ @level = level
24
+ end
25
+
26
+ def html
27
+ name = "h#{@level}"
28
+ content = CGI.escape_html(@content)
29
+ "<#{name}>#{content}</#{name}>"
30
+ end
31
+ end
@@ -0,0 +1,161 @@
1
+ # Copyright (C) 2025 gemmaro
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ class Elisp::Parser
17
+ HEADER_LINE = %r{
18
+ ;;;[ ]
19
+ (?<title>[a-z]+[.]el)[ ]---[ ](?<desc>.*?)
20
+ (?:[ ]+-[*]-[ ]*(?<vars>.*?)[ ]*-[*]-)?
21
+ \n+
22
+ }x
23
+ COMMENT = / *;; (?<comment>.*)\n/
24
+ CODE = %r{
25
+ (?<content>
26
+ (?:
27
+ [A-Za-z0-9&,.:=?_#'`<>()\[\]\t +*/-]
28
+ | [?]\\.
29
+ | "(?:[^\\"]|\\(?:.|\n))*"
30
+ )+?
31
+ (?:[ ]*;.*)?
32
+ | ;;;[#][#][#]autoload.*
33
+ )
34
+ \n
35
+ }x
36
+ BLANKLINES = /\n+/
37
+ CODE_OR_BLANKLINES = Regexp.union(CODE, BLANKLINES)
38
+
39
+ css = File.read(File.join(__dir__, "default.css"))
40
+ DEFAULT_CSS = "<style>#{css}</style>"
41
+
42
+ HEADING = %r{
43
+ ;;;(?<level>;*)[ ](?<title>.+)\n
44
+ (?:(?:;;)?\n)*
45
+ }x
46
+
47
+ def initialize(input)
48
+ @scanner = StringScanner.new(input.read)
49
+ @state = :start
50
+ @nodes = []
51
+ end
52
+
53
+ def parse
54
+ until @scanner.eos?
55
+
56
+ if @state == :start && @scanner.skip(HEADER_LINE)
57
+ @nodes << Elisp::Heading.new(@scanner[:title], level: 1)
58
+ @nodes << Elisp::Desc.new(@scanner[:desc])
59
+ @nodes << Elisp::Variables.new(@scanner[:vars])
60
+ @state = :after_header_line
61
+
62
+ elsif [:after_header_line,
63
+ :after_major_para,
64
+ :after_minor_para,
65
+ :after_code,
66
+ :after_page_delimiter,
67
+ :after_heading].include?(@state) &&
68
+ @scanner.skip(HEADING)
69
+ level = @scanner[:level].size
70
+ title = @scanner[:title]
71
+ if level.zero?
72
+ if ["Commentary:", "Code:"].include?(title)
73
+ title.chop!
74
+ elsif title.match?(/[a-z]+[.]el ends here/)
75
+ next # validate file name?
76
+ end
77
+ end
78
+ @nodes << Elisp::Heading.new(title, level: level + 2)
79
+ @state = :after_heading
80
+
81
+ elsif [:after_heading,
82
+ :after_major_para,
83
+ :after_minor_para,
84
+ :after_code,
85
+ :after_header_line].include?(@state) &&
86
+ @scanner.skip(COMMENT)
87
+ @nodes << Elisp::Comment.new(@scanner[:comment])
88
+ @state = :after_comment
89
+
90
+ elsif @state == :after_comment && @scanner.skip(";;\n")
91
+ @nodes[-1] = @nodes.last.to_minor_para
92
+ @state = :after_minor_para
93
+
94
+ elsif @state == :after_comment && @scanner.skip(COMMENT)
95
+ @nodes.last << @scanner[:comment]
96
+
97
+ elsif @state == :after_comment && @scanner.skip(BLANKLINES)
98
+ make_major_para
99
+ @state = :after_major_para
100
+
101
+ elsif [:after_major_para, :after_page_delimiter, :after_heading].include?(@state) &&
102
+ @scanner.skip(CODE)
103
+ @nodes << Elisp::Code.new(@scanner[:content])
104
+ @state = :after_code
105
+
106
+ elsif @state == :after_comment && @scanner.skip(CODE)
107
+ make_major_para
108
+ @nodes << Elisp::Code.new(@scanner[:content])
109
+ @state = :after_code
110
+
111
+ elsif @state == :after_code && @scanner.skip(CODE_OR_BLANKLINES)
112
+ @nodes.last << @scanner[:content]
113
+
114
+ elsif @state == :after_code && @scanner.skip(/\f\n+/)
115
+ @nodes << Elisp::PageDelimiter.new
116
+ @state = :after_page_delimiter
117
+
118
+ else
119
+ raise Elisp::Error, <<~MESSAGE
120
+ parse failed
121
+ --- rest line (#{@state}) ---
122
+ #{@scanner.rest.lines.first.inspect}
123
+ --- scanner ---
124
+ #{@scanner.inspect}
125
+ MESSAGE
126
+ end
127
+ end
128
+ self
129
+ end
130
+
131
+ def make_major_para
132
+ paras = [@nodes.pop.to_minor_para]
133
+ while (node = @nodes.pop)
134
+ if node.is_a?(Elisp::MinorPara)
135
+ paras.unshift(node)
136
+ else
137
+ @nodes.push(node)
138
+ break
139
+ end
140
+ end
141
+ @nodes << Elisp::MajorPara.new(paras)
142
+ end
143
+
144
+ def write(output, css: nil)
145
+ css = css ? %(<link rel="stylesheet" href="#{CGI.escape(css)}">) : DEFAULT_CSS
146
+ body = @nodes.map(&:html).join("\n")
147
+ output.write(<<~END_HTML)
148
+ <!doctype html>
149
+ <html>
150
+ <head>
151
+ #{css}
152
+ <meta charset="utf8">
153
+ <meta name="viewport" content="width=device-width, initial-scale=1">
154
+ </head>
155
+ <body>
156
+ <main>#{body}</main>
157
+ </body>
158
+ </html>
159
+ END_HTML
160
+ end
161
+ end
@@ -0,0 +1,18 @@
1
+ # Copyright (C) 2025 gemmaro
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ class Elisp
17
+ VERSION = "0.0.7"
18
+ end
data/lib/elisp.rb ADDED
@@ -0,0 +1,99 @@
1
+ # Copyright (C) 2025 gemmaro
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require "strscan"
17
+ require "cgi"
18
+ require "uri"
19
+
20
+ class Elisp
21
+ Error = Class.new(StandardError)
22
+
23
+ def self.parse(input)
24
+ Elisp::Parser.new(input).parse
25
+ end
26
+ end
27
+
28
+ class Elisp::PageDelimiter
29
+ def html = "<hr>"
30
+ end
31
+
32
+ class Elisp::Desc
33
+ def initialize(content)
34
+ @content = content
35
+ end
36
+
37
+ def html
38
+ content = CGI.escape_html(@content)
39
+ %(<p class="description">#{content}</p>)
40
+ end
41
+ end
42
+
43
+ class Elisp::Variables
44
+ def initialize(content)
45
+ @content = content
46
+ end
47
+
48
+ def html
49
+ content = CGI.escape_html(@content)
50
+ <<~END_HTML
51
+ <article>
52
+ Header line variables:
53
+ <pre class="code"><code>#{content}</code></pre>
54
+ </article>
55
+ END_HTML
56
+ end
57
+ end
58
+
59
+ class Elisp::MajorPara
60
+ def initialize(paras)
61
+ @paras = paras
62
+ end
63
+
64
+ def html
65
+ paras = @paras.map(&:html).join("\n")
66
+ "<article>#{paras}</article>"
67
+ end
68
+ end
69
+
70
+ class Elisp::MinorPara
71
+ def initialize(content)
72
+ @content = content
73
+ end
74
+
75
+ def html
76
+ content = Elisp::DocStringParser.new(@content).html
77
+ "<pre>#{content}</pre>"
78
+ end
79
+ end
80
+
81
+ class Elisp::Code
82
+ def initialize(content)
83
+ @content = content
84
+ end
85
+
86
+ def <<(content)
87
+ @content << "\n#{content}"
88
+ end
89
+
90
+ def html
91
+ content = CGI.escape_html(@content)
92
+ %(<pre class="code"><code>#{content}</code></pre>)
93
+ end
94
+ end
95
+
96
+ require_relative "elisp/comment"
97
+ require_relative "elisp/parser"
98
+ require_relative "elisp/heading"
99
+ require_relative "elisp/doc_string_parser"