elisp2any 0.0.6 → 0.0.8

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.
@@ -0,0 +1,18 @@
1
+ # Copyright (C) 2025, 2026 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.8"
18
+ end
data/lib/elisp.rb CHANGED
@@ -1,7 +1,22 @@
1
- require "strscan"
2
- require "cgi"
3
- require "uri"
4
-
1
+ # Copyright (C) 2025, 2026 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/escape"
17
+
18
+ # +pandoc_json_object+ methods for some classes means the AST for the
19
+ # Pandoc JSON filter.
5
20
  class Elisp
6
21
  Error = Class.new(StandardError)
7
22
 
@@ -10,67 +25,12 @@ class Elisp
10
25
  end
11
26
  end
12
27
 
13
- class Elisp::Sidebar
14
- def initialize(headings)
15
- @headings = headings
16
- end
17
-
18
- def html
19
- rest = @headings.dup
20
- result = +""
21
- while (head = rest.shift)
22
- level = head.level
23
- subheadings = []
24
- while (heading = rest.shift)
25
- if heading.level > level
26
- subheadings << heading
27
- else
28
- rest.unshift(heading)
29
- break
30
- end
31
- end
32
- sub_sidebar =
33
- subheadings.empty? ? nil
34
- : (html = self.class.new(subheadings).html
35
- "<details open>#{html}</details>")
36
- content = CGI.escape_html(head.content)
37
- result << <<~END_HTML
38
- <li>
39
- <a href="##{head.html_id}">#{content}</a>
40
- #{sub_sidebar}
41
- </li>
42
- END_HTML
43
- end
44
- "<ul>#{result}</ul>"
45
- end
46
- end
47
-
48
- class PageDelimiter
28
+ class Elisp::PageDelimiter
49
29
  def html = "<hr>"
50
- end
51
-
52
- class Elisp::Heading
53
- attr_reader :level, :content
54
-
55
- def initialize(content, level: 2)
56
- @content = content
57
- @level = level
58
- end
59
-
60
- def html
61
- name = "h#{@level}"
62
- content = CGI.escape_html(@content)
63
- <<~END_HTML
64
- <#{name} id="#{html_id}">
65
- #{content}
66
- <a href="##{html_id}">#</a>
67
- </#{name}>
68
- END_HTML
69
- end
70
-
71
- def html_id
72
- content = CGI.escape(@content)
73
- "#{@level}-#{content}"
30
+ def pandoc_json_object
31
+ {
32
+ "t": "HorizontalRule",
33
+ }
74
34
  end
75
35
  end
76
36
 
@@ -83,6 +43,26 @@ class Elisp::Desc
83
43
  content = CGI.escape_html(@content)
84
44
  %(<p class="description">#{content}</p>)
85
45
  end
46
+
47
+ def pandoc_json_object
48
+ {
49
+ "t": "Div",
50
+ "c": [
51
+ ["", ["description"], []],
52
+ [
53
+ {
54
+ "t": "Para",
55
+ "c": [
56
+ {
57
+ "t": "Str",
58
+ "c": @content,
59
+ },
60
+ ],
61
+ },
62
+ ],
63
+ ],
64
+ }
65
+ end
86
66
  end
87
67
 
88
68
  class Elisp::Variables
@@ -95,10 +75,28 @@ class Elisp::Variables
95
75
  <<~END_HTML
96
76
  <article>
97
77
  Header line variables:
98
- <pre><code>#{content}</code></pre>
78
+ <pre class="code"><code>#{content}</code></pre>
99
79
  </article>
100
80
  END_HTML
101
81
  end
82
+
83
+ def pandoc_json_object
84
+ {
85
+ "t": "Div",
86
+ "c": [
87
+ ["", ["header-line-variables"], []],
88
+ [
89
+ {
90
+ "t": "CodeBlock",
91
+ "c": [
92
+ ["", ["emacs-lisp-header-line-variables"], []],
93
+ @content,
94
+ ],
95
+ },
96
+ ],
97
+ ],
98
+ }
99
+ end
102
100
  end
103
101
 
104
102
  class Elisp::MajorPara
@@ -110,6 +108,16 @@ class Elisp::MajorPara
110
108
  paras = @paras.map(&:html).join("\n")
111
109
  "<article>#{paras}</article>"
112
110
  end
111
+
112
+ def pandoc_json_object
113
+ {
114
+ "t": "Div",
115
+ "c": [
116
+ ["", ["major-para"], []],
117
+ @paras.map(&:pandoc_json_object),
118
+ ],
119
+ }
120
+ end
113
121
  end
114
122
 
115
123
  class Elisp::MinorPara
@@ -121,6 +129,13 @@ class Elisp::MinorPara
121
129
  content = Elisp::DocStringParser.new(@content).html
122
130
  "<pre>#{content}</pre>"
123
131
  end
132
+
133
+ def pandoc_json_object
134
+ {
135
+ "t": "Para",
136
+ "c": Elisp::DocStringParser.new(@content).pandoc_json_objects,
137
+ }
138
+ end
124
139
  end
125
140
 
126
141
  class Elisp::Code
@@ -136,7 +151,19 @@ class Elisp::Code
136
151
  content = CGI.escape_html(@content)
137
152
  %(<pre class="code"><code>#{content}</code></pre>)
138
153
  end
154
+
155
+ def pandoc_json_object
156
+ {
157
+ "t": "CodeBlock",
158
+ "c": [
159
+ ["", ["emacs-lisp"], []],
160
+ @content,
161
+ ],
162
+ }
163
+ end
139
164
  end
140
165
 
141
166
  require_relative "elisp/comment"
142
167
  require_relative "elisp/parser"
168
+ require_relative "elisp/heading"
169
+ require_relative "elisp/doc_string_parser"
data/sample.md ADDED
@@ -0,0 +1,30 @@
1
+ これはページ区切りです:
2
+
3
+ ---
4
+
5
+ これはコードブロックです:
6
+
7
+ ```emacs-lisp
8
+ (message "hello")
9
+ ```
10
+
11
+ これは有塀divです:
12
+
13
+ ::: majorpara
14
+ 段落1つ目。
15
+
16
+ 段落2つ目。
17
+ :::
18
+
19
+ なお、普通の段落はminor paraです。
20
+
21
+ 行内コード`aaa`を確かめます。
22
+
23
+ # これは見出しです
24
+
25
+ 生のHTMLリンク https://example.com です。
26
+ 生のHTMLリンク <https://example.com> です。
27
+
28
+ これは著作情報 Copyright (C) gemmaro です。
29
+
30
+ これは電子メール<gemmaro.dev@gmail.com>です。
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: elisp2any
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - gemmaro
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2025-06-22 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: rake
@@ -47,6 +46,7 @@ executables:
47
46
  extensions: []
48
47
  extra_rdoc_files: []
49
48
  files:
49
+ - ".envrc"
50
50
  - CHANGELOG.md
51
51
  - Gemfile
52
52
  - LICENSE.txt
@@ -56,27 +56,12 @@ files:
56
56
  - fixtures/init.el
57
57
  - lib/elisp.rb
58
58
  - lib/elisp/comment.rb
59
+ - lib/elisp/default.css
60
+ - lib/elisp/doc_string_parser.rb
61
+ - lib/elisp/heading.rb
59
62
  - lib/elisp/parser.rb
60
- - lib/elisp2any.rb
61
- - lib/elisp2any/aside.rb
62
- - lib/elisp2any/blanklines.rb
63
- - lib/elisp2any/code.rb
64
- - lib/elisp2any/codeblock.rb
65
- - lib/elisp2any/comment.rb
66
- - lib/elisp2any/commentary.rb
67
- - lib/elisp2any/expression.rb
68
- - lib/elisp2any/footer_line.rb
69
- - lib/elisp2any/header_line.rb
70
- - lib/elisp2any/heading.rb
71
- - lib/elisp2any/html_renderer.erb
72
- - lib/elisp2any/html_renderer.rb
73
- - lib/elisp2any/html_renderer/index.html.erb
74
- - lib/elisp2any/inline_code.rb
75
- - lib/elisp2any/line.rb
76
- - lib/elisp2any/paragraph.rb
77
- - lib/elisp2any/section.rb
78
- - lib/elisp2any/text.rb
79
- - lib/elisp2any/version.rb
63
+ - lib/elisp/version.rb
64
+ - sample.md
80
65
  homepage: https://codeberg.org/gemmaro/elisp2any
81
66
  licenses:
82
67
  - GPL-3.0-or-later
@@ -88,7 +73,6 @@ metadata:
88
73
  homepage_uri: https://codeberg.org/gemmaro/elisp2any
89
74
  source_code_uri: https://codeberg.org/gemmaro/elisp2any
90
75
  wiki_uri: https://codeberg.org/gemmaro/elisp2any/wiki
91
- post_install_message:
92
76
  rdoc_options: []
93
77
  require_paths:
94
78
  - lib
@@ -103,8 +87,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
103
87
  - !ruby/object:Gem::Version
104
88
  version: '0'
105
89
  requirements: []
106
- rubygems_version: 3.3.27
107
- signing_key:
90
+ rubygems_version: 4.0.16
108
91
  specification_version: 4
109
92
  summary: Converter from Emacs Lisp to some document markup
110
93
  test_files: []
@@ -1,23 +0,0 @@
1
- require "elisp2any/comment"
2
-
3
- module Elisp2any
4
- class Aside
5
- def self.scan(scanner)
6
- pos = scanner.pos
7
- com = Comment.scan(scanner) or return
8
- unless com.colons == 1
9
- scanner.pos = pos
10
- return
11
- end
12
- new(com.content)
13
- end
14
-
15
- def source
16
- ";#{@content}\n"
17
- end
18
-
19
- def initialize(content)
20
- @content = content
21
- end
22
- end
23
- end
@@ -1,20 +0,0 @@
1
- require "forwardable"
2
-
3
- module Elisp2any
4
- class Blanklines
5
- def self.scan(scanner)
6
- scanner = StringScanner.new(scanner) unless scanner.respond_to?(:skip)
7
- count = 0
8
- count += 1 while !scanner.eos? && scanner.skip(/ *\n/)
9
- count.zero? and return
10
- new(count)
11
- end
12
-
13
- def initialize(count) # :nodoc:
14
- @count = count
15
- end
16
-
17
- extend Forwardable # :nodoc:
18
- def_delegator :@count, :to_i
19
- end
20
- end
@@ -1,49 +0,0 @@
1
- require "forwardable"
2
- require "elisp2any/paragraph"
3
- require "elisp2any/blanklines"
4
- require "elisp2any/section"
5
-
6
- module Elisp2any
7
- class Code
8
- def self.scan(scanner)
9
- pos = scanner.pos
10
- heading = Elisp2any.scan_top_heading(scanner) or return
11
- unless heading == "Code:"
12
- scanner.pos = pos
13
- return
14
- end
15
- Blanklines.scan(scanner) # optional
16
- paragraphs = []
17
- while (par = Paragraph.scan(scanner))
18
- paragraphs << par
19
- Blanklines.scan(scanner) # optional
20
- end
21
- blocks = [paragraphs]
22
- pos = scanner.pos
23
- while (section = Section.scan(scanner))
24
- if section.heading.level == 1
25
- blocks << section
26
- pos = scanner.pos
27
- else
28
- scanner.pos = pos
29
- break
30
- end
31
- end
32
- new(blocks)
33
- end
34
-
35
- def initialize(blocks)
36
- @blocks = blocks
37
- end
38
-
39
- def sections
40
- @blocks.drop(1)
41
- end
42
-
43
- extend Forwardable # :nodoc:
44
- def_delegator :@blocks, :first, :paragraphs
45
- def_delegator :@blocks, :each
46
-
47
- include Enumerable
48
- end
49
- end
@@ -1,56 +0,0 @@
1
- require "forwardable"
2
- require "elisp2any/expression"
3
- require "elisp2any/blanklines"
4
- require "elisp2any/aside"
5
-
6
- module Elisp2any
7
- class Codeblock # :nodoc:
8
- def self.scan(scanner)
9
- scanner = StringScanner.new(scanner) unless scanner.respond_to?(:skip)
10
- expressions = []
11
- while (exp = Expression.scan(scanner) ||
12
- scanner.scan(/[ \n]+/) ||
13
- Aside.scan(scanner))
14
- expressions << exp
15
- end
16
- expressions.empty? and return
17
- Blanklines.scan(scanner) # optional
18
- new(:TODO, expressions:)
19
- end
20
-
21
- def source
22
- @expressions.sum(+"") do |exp|
23
- case exp
24
- in String
25
- exp
26
- else
27
- exp.source
28
- end
29
- end
30
- end
31
-
32
- # TODO: delete node
33
- # TODO: Remove default empty array
34
- def initialize(node, expressions: []) # :nodoc:
35
- @node = node
36
- @expressions = expressions
37
- end
38
-
39
- def append(source, end_byte) # :nodoc:
40
- @node.append(source, end_byte)
41
- end
42
-
43
- def content
44
- if @node.respond_to?(:content)
45
- @node.content
46
- else
47
- @expressions
48
- end
49
- end
50
-
51
- extend Forwardable # :nodoc:
52
- def_delegator :@expressions, :each
53
-
54
- include Enumerable
55
- end
56
- end
@@ -1,19 +0,0 @@
1
- module Elisp2any
2
- class Comment
3
- attr_reader :colons, :content, :padding
4
-
5
- def self.scan(scanner)
6
- scanner = StringScanner.new(scanner) unless scanner.respond_to?(:skip)
7
- scanner.skip(/(?<colons>;+)(?<padding> *)(?<content>.*)\n?/) or return
8
- new(colons: scanner[:colons].size,
9
- content: scanner[:content],
10
- padding: scanner[:padding])
11
- end
12
-
13
- def initialize(colons:, content:, padding:)
14
- @colons = colons
15
- @content = content
16
- @padding = padding
17
- end
18
- end
19
- end
@@ -1,32 +0,0 @@
1
- require "forwardable"
2
- require "elisp2any/paragraph"
3
- require "elisp2any/blanklines"
4
-
5
- module Elisp2any
6
- class Commentary
7
- def self.scan(scanner)
8
- pos = scanner.pos
9
- heading = Elisp2any.scan_top_heading(scanner) or return
10
- unless heading == "Commentary:"
11
- scanner.pos = pos
12
- return
13
- end
14
- Blanklines.scan(scanner) # optional
15
- paragraphs = []
16
- while (par = Paragraph.scan(scanner))
17
- paragraphs << par
18
- Blanklines.scan(scanner) # optional
19
- end
20
- new(paragraphs)
21
- end
22
-
23
- def initialize(paragraphs)
24
- @paragraphs = paragraphs
25
- end
26
-
27
- extend Forwardable # :nodoc:
28
- def_delegators :@paragraphs, :each, :size
29
-
30
- include Enumerable
31
- end
32
- end
@@ -1,90 +0,0 @@
1
- require "strscan"
2
-
3
- module Elisp2any
4
- class Expression
5
- def self.scan(scanner)
6
- scanner = StringScanner.new(scanner) unless scanner.respond_to?(:skip)
7
- if scanner.skip("(")
8
- exps = []
9
- while (exp = scan(scanner))
10
- exps << exp
11
- spaces = scanner.scan(/[ \n]+/)
12
- exps << spaces if spaces
13
- end
14
- scanner.skip(")") or raise Error, scanner.inspect
15
- new(exps)
16
- elsif scanner.skip("'")
17
- exp = scan(scanner) or raise Error, scanner.inspect
18
- new({ quoted: exp })
19
- elsif scanner.skip("\"")
20
- content = +""
21
- while !scanner.eos? && !scanner.match?("\\") && !scanner.match?('"')
22
- content << scanner.getch
23
- end
24
- scanner.skip('"') or raise Error, scanner.inspect
25
- new(content)
26
- elsif scanner.skip("#'")
27
- exp = scan(scanner) or raise Error, scanner.inspect
28
- new({ function: exp })
29
- else
30
- name = scanner.scan(/[a-z0-9_?.:-]+/) or return
31
- new(name.to_sym)
32
- end
33
- end
34
-
35
- def source
36
- case @content
37
- in { quoted: }
38
- "'#{self.class.source(quoted)}"
39
- in String
40
- @content
41
- in { function: }
42
- "#'#{self.class.source(function)}"
43
- in Array
44
- result = +"("
45
- @content.each { |exp| result << self.class.source(exp) }
46
- "#{result})"
47
- in Symbol
48
- @content.to_s
49
- end
50
- end
51
-
52
- def self.source(arg)
53
- case arg
54
- in Symbol
55
- arg.to_s
56
- in Array
57
- result = +"("
58
- arg.sum { |ele| result << source(ele) }
59
- "#{result})"
60
- in Expression
61
- arg.source
62
- in String
63
- arg
64
- else
65
- raise arg.inspect
66
- end
67
- end
68
-
69
- def deconstruct_keys(*keys)
70
- result = {}
71
- keys => [keys]
72
- keys.each do |key|
73
- case key
74
- in :content
75
- result[:content] = @content
76
- else # nop
77
- end
78
- end
79
- result
80
- end
81
-
82
- def deconstruct
83
- [*@content]
84
- end
85
-
86
- def initialize(content)
87
- @content = content
88
- end
89
- end
90
- end
@@ -1,28 +0,0 @@
1
- require "forwardable"
2
-
3
- module Elisp2any
4
- class FooterLine
5
- def self.scan(scanner)
6
- scanner = StringScanner.new(scanner) unless scanner.respond_to?(:pos)
7
- pos = scanner.pos
8
- heading = Elisp2any.scan_top_heading(scanner) or return
9
- cscanner = StringScanner.new(heading)
10
- unless (filename = Elisp2any.scan_filename(cscanner))
11
- scanner.pos = pos
12
- return
13
- end
14
- unless cscanner.skip(/ ends here\n?/)
15
- scanner.pos = pos
16
- return
17
- end
18
- new(filename)
19
- end
20
-
21
- def initialize(filename)
22
- @filename = filename
23
- end
24
-
25
- extend Forwardable # :nodoc:
26
- def_delegator :@filename, :==
27
- end
28
- end