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
@@ -1,80 +0,0 @@
1
- require "elisp2any/comment"
2
- require "elisp2any/expression"
3
-
4
- module Elisp2any
5
- class HeaderLine
6
- attr_reader :filename, :description, :variables
7
-
8
- def self.scan(scanner)
9
- scanner = StringScanner.new(scanner) unless scanner.respond_to?(:pos)
10
- pos = scanner.pos
11
- unless (heading = Elisp2any.scan_top_heading(scanner))
12
- scanner.pos = pos
13
- return
14
- end
15
- cscanner = StringScanner.new(heading)
16
- unless (filename = Elisp2any.scan_filename(cscanner))
17
- scanner.pos = pos
18
- return
19
- end
20
- unless cscanner.skip(/ +--- +/)
21
- scanner.pos = pos
22
- return
23
- end
24
- description = +""
25
- variables = nil
26
- until cscanner.eos?
27
- if (variables = scan_variables(cscanner)) # nop
28
- break
29
- else
30
- description << cscanner.getch
31
- end
32
- end
33
- if description.empty?
34
- scanner.pos = pos
35
- return
36
- end
37
- new(filename:, description:, variables:)
38
- end
39
-
40
- def self.scan_variables(scanner)
41
- scanner = StringScanner.new(scanner) unless scanner.respond_to?(:pos)
42
- scanner.skip(/ *-[*]- +/) or return
43
- variables = {}
44
- until scanner.skip(/ +-[*]- */)
45
- if scanner.eos?
46
- raise Error, "unexpected end"
47
- elsif (assign = scan_assignments(scanner))
48
- variables[assign[:variable]] = assign[:expression]
49
- elsif scanner.skip(";")
50
- break
51
- else
52
- raise Error, scanner.inspect
53
- end
54
- end
55
- variables
56
- end
57
- private_class_method :scan_variables
58
-
59
- def self.scan_assignments(scanner)
60
- pos = scanner.pos
61
- variable = Elisp2any.scan_variable(scanner) or return
62
- unless scanner.skip(/ *: */)
63
- scanner.pos = pos
64
- return
65
- end
66
- unless (expression = Expression.scan(scanner))
67
- scanner.pos = pos
68
- return
69
- end
70
- { variable:, expression: }
71
- end
72
- private_class_method :scan_assignments
73
-
74
- def initialize(filename:, description:, variables:)
75
- @filename = filename
76
- @description = description
77
- @variables = variables
78
- end
79
- end
80
- end
@@ -1,68 +0,0 @@
1
- require 'strscan'
2
- require 'forwardable'
3
-
4
- module Elisp2any
5
- class Heading
6
- attr_reader :level
7
- attr_accessor :content
8
-
9
- def self.scan(scanner)
10
- pos = scanner.pos
11
- comment = Comment.scan(scanner) or return
12
- unless comment.colons >= 3
13
- scanner.pos = pos
14
- return
15
- end
16
- new(:TODO, comment.colons - 3,
17
- comment.content # do not scan as text at this point
18
- )
19
- end
20
-
21
- def deconstruct_keys(*keys)
22
- result = {}
23
- keys => [keys]
24
- keys.each do |key|
25
- case key
26
- in :level
27
- result[:level] = @level
28
- in :content
29
- result[:content] = @content
30
- end
31
- end
32
- result
33
- end
34
-
35
- # TODO: delete node. Use kwargs.
36
- def initialize(node, level, content) # :nodoc:
37
- @node = node
38
- @level = level
39
- @content = content
40
- end
41
-
42
- # Returns nil if failed
43
- def name_and_synopsis # :nodoc:
44
- scanner = StringScanner.new(@content)
45
- name = scanner.scan_until(/\.el/) or return
46
- name = name[nil...-3] or return
47
- scanner.skip(/\s+---\s+/) or return
48
- scanner.skip(/(?<synopsis>.+?)(:?\s+-\*- .+? -\*-)?\Z/) or return
49
- synopsis = scanner[:synopsis]
50
- return name, synopsis
51
- end
52
-
53
- def commentary? # :nodoc:
54
- @level == 1 && @content == 'Commentary:'
55
- end
56
-
57
- def code? # :nodoc:
58
- @level == 1 && @content == 'Code:'
59
- end
60
-
61
- def final_name # :nodoc:
62
- @content.match(/\A(?<name>.+?)\.el ends here\Z/)[:name]
63
- end
64
-
65
- extend Forwardable # :nodoc:
66
- def_delegator :@level, :<=>
67
- end
68
- end
@@ -1,23 +0,0 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title><%= name %></title>
7
- <link rel="stylesheet" href="<%= @css %>">
8
- </head>
9
- <body>
10
- <main>
11
- <h1><%= name %></h1>
12
- <p><%= synopsis %></p>
13
- <h2>Commentary</h2>
14
- <% commentary.each do |paragraph| %>
15
- <p><%= render_paragraph(paragraph) %></p>
16
- <% end %>
17
- <h2>Code</h2>
18
- <% code.each do |block| %>
19
- <%= render_block(block, level: 2) %>
20
- <% end %>
21
- </main>
22
- </body>
23
- </html>
@@ -1,22 +0,0 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title><%= name %></title>
7
- <link rel="stylesheet" href="<%= @css %>">
8
- </head>
9
- <body>
10
- <main>
11
- <h1><%= name %><small> &mdash; <%= synopsis %></small></h1>
12
- <div style="text-align:right"><%= render(header_line.variables) %></div>
13
- <% commentary.each do |paragraph| %>
14
- <%= render(paragraph) %>
15
- <% end %>
16
- <h2>Code</h2>
17
- <% self.code.each do |block| %>
18
- <%= render(block) %>
19
- <% end %>
20
- </main>
21
- </body>
22
- </html>
@@ -1,149 +0,0 @@
1
- require 'erb'
2
- require 'forwardable'
3
- require 'cgi/util'
4
- require_relative 'inline_code'
5
- require_relative 'heading'
6
- require_relative 'paragraph'
7
- require_relative 'codeblock'
8
-
9
- module Elisp2any
10
- class HTMLRenderer
11
- def initialize(file, css: nil, mode:)
12
- @file = file
13
- @css = css || "https://unpkg.com/mvp.css"
14
- @mode = mode
15
- end
16
-
17
- # Write gradually?
18
- def render(node = nil)
19
- if node
20
- case node
21
- in Paragraph
22
- par = node.sum(+"") { |line| render(line) }
23
- "<p>#{par}</p>"
24
- in Line
25
- begin
26
- node.sum(+"") { |chunk| render(chunk) }
27
- rescue => e
28
- warn node.inspect
29
- raise e
30
- end
31
- in String
32
- h(node)
33
- in [] # huh? nop.
34
- in Section
35
- result = render(node.heading)
36
- node.blocks.each { |blo| result << render(blo) }
37
- node.sections.each { |sec| result << render(sec) }
38
- result
39
- in Heading[level:, content:]
40
- lev = level + 2
41
- "<h#{lev}>#{render(content)}</h#{lev}>"
42
- in Codeblock
43
- "<pre><code>#{h(node.source.chomp)}</code></pre>"
44
- in { code: }
45
- "<code>#{h(code)}</code>"
46
- in Hash
47
- result = node.sum(+"<dl>") do |key, value|
48
- "<dt>#{render(key)}</dt><dd>#{render(value)}</dd>"
49
- end
50
- "#{result}</dl>"
51
- in Expression
52
- "<code>#{h(node.source)}</code>"
53
- in Text
54
- node.sum(+"") { |ele| render(ele) }
55
- else
56
- raise Error, node.inspect
57
- end
58
- else
59
- case @mode
60
- in :old
61
- erb_render('index.html.erb')
62
- in :new
63
- source = ::File.read(::File.join(__dir__, 'html_renderer.erb'))
64
- ERB.new(source).result(binding)
65
- end
66
- end
67
- end
68
-
69
- private
70
-
71
- def render_paragraph(paragraph)
72
- html = ''
73
- paragraph.each do |line|
74
- line.each do |chunk|
75
- case chunk
76
- in InlineCode
77
- html << "<code>#{h(chunk.content)}</code>"
78
- in String
79
- html << h(chunk)
80
- in { code: }
81
- html << "<code>#{h(code)}</code>"
82
- else
83
- raise Error, chunk.inspect
84
- end
85
- end
86
- end
87
- html
88
- end
89
-
90
- # TODO: remove level
91
- def render_block(block, level: nil)
92
- html = ''
93
- case block
94
- when Heading
95
- lev = level
96
- if level
97
- lev = level + block.level - 1
98
- else
99
- lev = block.level
100
- end
101
- name = "h#{lev}"
102
- html << "<#{name}>#{h(block.content)}</#{name}>"
103
- when Paragraph
104
- html << render_paragraph(block)
105
- when Codeblock
106
- html << "<pre><code>#{h(block.content)}</code></pre>"
107
- when [] # huh? nop
108
- when Section
109
- render_block(block.heading)
110
- block.blocks.each do |blo|
111
- render_block(blo)
112
- end
113
- block.sections.each do |sec|
114
- render_block(sec)
115
- end
116
- else
117
- raise Error, block.inspect
118
- end
119
- html
120
- end
121
-
122
- def erb_render(path)
123
- source = ::File.read(::File.join(__dir__, 'html_renderer', path))
124
- ERB.new(source).result(binding)
125
- end
126
-
127
- def h(arg)
128
- case arg
129
- in String
130
- CGI.escape_html(arg)
131
- in Array
132
- arg.map do |ele|
133
- h(ele)
134
- end.join
135
- in Symbol
136
- h(arg.to_s)
137
- in Expression
138
- h(arg.source)
139
- in Integer
140
- h(arg.to_s)
141
- in Aside
142
- h(arg.content)
143
- end
144
- end
145
-
146
- extend Forwardable # :nodoc:
147
- def_delegators :@file, :name, :synopsis, :commentary, :code, :header_line
148
- end
149
- end
@@ -1,10 +0,0 @@
1
- module Elisp2any
2
- # TODO: delete
3
- class InlineCode
4
- attr_reader :content
5
-
6
- def initialize(content)
7
- @content = content
8
- end
9
- end
10
- end
@@ -1,52 +0,0 @@
1
- require 'strscan'
2
- require 'forwardable'
3
- require_relative 'inline_code'
4
- require "elisp2any/comment"
5
- require "elisp2any/text"
6
-
7
- module Elisp2any
8
- class Line
9
- def self.scan(scanner)
10
- pos = scanner.pos
11
- comment = Comment.scan(scanner) or return
12
- unless comment.colons == 2
13
- scanner.pos = pos
14
- return
15
- end
16
- unless comment.padding[0] == " "
17
- raise Error, "line comment should have a whitespace padding"
18
- end
19
- content = "#{comment.padding[1..]}#{comment.content}"
20
- new(Text.scan(content).to_a)
21
- end
22
-
23
- def initialize(chunks) # :nodoc:
24
- @chunks = chunks
25
- end
26
-
27
- def self.parse(string) # :nodoc:
28
- scanner = StringScanner.new(string)
29
- chunks = []
30
-
31
- until scanner.eos?
32
- if scanner.skip(/`(?<content>[^']+)'/)
33
- chunks << InlineCode.new(scanner[:content])
34
- else
35
- chunk = scanner.getch
36
- if (last = chunks.last).is_a?(String)
37
- last << chunk
38
- else
39
- chunks << chunk
40
- end
41
- end
42
- end
43
-
44
- new(chunks)
45
- end
46
-
47
- extend Forwardable # :nodoc:
48
- def_delegators :@chunks, :each, :deconstruct
49
-
50
- include Enumerable
51
- end
52
- end
@@ -1,67 +0,0 @@
1
- require_relative 'codeblock'
2
- require_relative 'line'
3
- require_relative 'heading'
4
- require_relative 'paragraph'
5
- require 'strscan'
6
-
7
- module Elisp2any
8
- # TODO: delete
9
- class Node
10
- attr_reader :range # :nodoc:
11
- attr_reader :content
12
-
13
- def self.from_tree_sitter(source, ts_node) #:nodoc:
14
- nodes = []
15
- (0 ... ts_node.child_count).map { |index| ts_node[index] }.each do |top_level_node|
16
- range = top_level_node.start_byte .. top_level_node.end_byte
17
- content = source.byteslice(range)
18
- node = Node.new(content, range)
19
- case top_level_node.type
20
- when :comment
21
- scanner = StringScanner.new(content)
22
- scanner.skip(';') or raise Error, 'no semicolon for comment'
23
- scanner.skip(';') or
24
- begin
25
- (last_node = nodes.last) && last_node.is_a?(Codeblock) or raise Error, 'no prior code for single semicolon comment'
26
- last_node.append(source, range.end)
27
- next
28
- end
29
-
30
- if (level = scanner.skip(/;+/))
31
- scanner.skip(' ') or raise Error, 'no space after heading semicolons'
32
- nodes << Heading.new(node, level, scanner.rest.chomp)
33
- next
34
- elsif scanner.skip("\n")
35
- next
36
- end
37
-
38
- scanner.skip(' ') or raise Error, "no space after semicolons: #{scanner.inspect}"
39
- line = Line.parse(scanner.rest)
40
- if (last_node = nodes.last) && last_node.is_a?(Paragraph) && last_node.end_row + 1 == top_level_node.start_point.row
41
- last_node << line
42
- else
43
- paragraph = Paragraph.new(node, [line], top_level_node.end_point.row)
44
- nodes << paragraph
45
- end
46
- else
47
- if (last_node = nodes.last) && last_node.is_a?(Codeblock)
48
- last_node.append(source, range.end)
49
- else
50
- nodes << Codeblock.new(node)
51
- end
52
- end
53
- end
54
- nodes
55
- end
56
-
57
- def append(source, end_byte) # :nodoc:
58
- @range = range = @range.begin .. end_byte
59
- @content = source.byteslice(range)
60
- end
61
-
62
- def initialize(content, range) # :nodoc:
63
- @content = content
64
- @range = range
65
- end
66
- end
67
- end
@@ -1,37 +0,0 @@
1
- require 'forwardable'
2
- require "elisp2any/line"
3
-
4
- module Elisp2any
5
- class Paragraph
6
- def self.scan(scanner)
7
- scanner = StringScanner.new(scanner) unless scanner.respond_to?(:skip)
8
- lines = []
9
- while (line = Line.scan(scanner))
10
- lines << line
11
- end
12
- lines.empty? and return
13
- new(:TODO, lines, :TODO)
14
- end
15
-
16
- # TODO: delete
17
- attr_reader :end_row # :nodoc:
18
-
19
- # TODO: delete node and end_row
20
- def initialize(node, lines, end_row) # :nodoc:
21
- @node = node
22
- @lines = lines
23
- @end_row = end_row
24
- end
25
-
26
- # TODO: delete
27
- def code?
28
- false
29
- end
30
-
31
- extend Forwardable # :nodoc:
32
- def_delegators :@lines, :empty?, :clear, :<<, :each, :deconstruct, :size
33
- def_delegators :@node, :adjucent?
34
-
35
- include Enumerable
36
- end
37
- end
@@ -1,49 +0,0 @@
1
- require "forwardable"
2
- require "elisp2any/heading"
3
- require "elisp2any/blanklines"
4
- require "elisp2any/text"
5
-
6
- module Elisp2any
7
- class Section
8
- attr_reader :heading,
9
- # TODO: gather blocks and sections
10
- :blocks,
11
- :sections
12
-
13
- def self.scan(scanner)
14
- heading = Heading.scan(scanner) or return
15
- heading.content = Text.scan(heading.content)
16
- Blanklines.scan(scanner) # optional
17
- blocks = []
18
- while (blo = Paragraph.scan(scanner) || Codeblock.scan(scanner))
19
- blocks << blo
20
- Blanklines.scan(scanner) # optional
21
- end
22
- pos = scanner.pos
23
- sections = []
24
- while (section = scan(scanner))
25
- if section.level == heading.level + 1
26
- sections << section
27
- pos = scanner.pos
28
- elsif section.level >= heading.level + 2
29
- raise Error, "too demoted heading"
30
- else
31
- scanner.pos = pos
32
- break
33
- end
34
- end
35
- new(heading:, blocks:, sections:)
36
- end
37
-
38
- def initialize(heading:, blocks:, sections:)
39
- @heading = heading
40
- @blocks = blocks
41
- @sections = sections
42
- end
43
-
44
- alias paragraphs blocks # TODO: delete
45
-
46
- extend Forwardable # :nodoc:
47
- def_delegator :@heading, :level
48
- end
49
- end
@@ -1,33 +0,0 @@
1
- require "forwardable"
2
-
3
- module Elisp2any
4
- class Text
5
- def self.scan(scanner)
6
- scanner = StringScanner.new(scanner) unless scanner.respond_to?(:skip)
7
- tokens = []
8
- until scanner.eos?
9
- if scanner.skip(/`(?<content>[^']+)'/)
10
- tokens << { code: scanner[:content] }
11
- else
12
- char = scanner.getch
13
- case tokens
14
- in [*, String => last]
15
- last << char
16
- else
17
- tokens << char
18
- end
19
- end
20
- end
21
- new(tokens)
22
- end
23
-
24
- def initialize(tokens)
25
- @tokens = tokens
26
- end
27
-
28
- extend Forwardable # :nodoc:
29
- def_delegators :@tokens, :each, :deconstruct
30
-
31
- include Enumerable
32
- end
33
- end
@@ -1,36 +0,0 @@
1
- require 'tree_sitter'
2
-
3
- module Elisp2any
4
- class TreeSitterParser # :nodoc:
5
- def self.parse(source)
6
- new(source).parse
7
- end
8
-
9
- def parse
10
- @node = parser.parse_string(nil, @source).root_node
11
- end
12
-
13
- private
14
-
15
- def initialize(source)
16
- @source = source
17
- end
18
-
19
- def parser
20
- TreeSitter::Parser.new.tap do |p|
21
- p.language = TreeSitter::Language.load('elisp', elisp_library)
22
- end
23
- end
24
-
25
- def elisp_library
26
- shared_object = 'libtree-sitter-elisp.so'
27
-
28
- # Set this env var for Guix shell environment or shared object file is not found
29
- if ENV['ELISP2ANY_GUIX_USE_PROFILE_PATH']
30
- shared_object = ::File.join(ENV["GUIX_ENVIRONMENT"], "lib/tree-sitter", shared_object)
31
- end
32
-
33
- shared_object
34
- end
35
- end
36
- end
@@ -1,4 +0,0 @@
1
- module Elisp2any
2
- # Version of this library.
3
- VERSION = '0.0.5'
4
- end
data/lib/elisp2any.rb DELETED
@@ -1,27 +0,0 @@
1
- require_relative 'elisp2any/version'
2
- require_relative 'elisp2any/file'
3
- require_relative 'elisp2any/heading'
4
-
5
- module Elisp2any
6
- autoload :HeaderLine, "elisp2any/header_line.rb"
7
- autoload :Blanklines, "elisp2any/blanklines.rb"
8
- Error = Class.new(StandardError)
9
-
10
- def self.scan_filename(scanner) # :nodoc:
11
- scanner.scan(/[a-z]+[.]el\b/)
12
- end
13
-
14
- def self.scan_variable(scanner) # :nodoc:
15
- scanner.scan(/[a-z-]+\b/)
16
- end
17
-
18
- def self.scan_top_heading(scanner) # :nodoc:
19
- pos = scanner.pos
20
- heading = Heading.scan(scanner) or return
21
- unless heading.level.zero?
22
- scanner.pos = pos
23
- return
24
- end
25
- heading.content
26
- end
27
- end