repubmark 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +46 -0
  3. data/.rubocop_todo.yml +129 -0
  4. data/.yardopts +4 -0
  5. data/LICENSE +21 -0
  6. data/README.md +21 -0
  7. data/Rakefile +60 -0
  8. data/examples/config.yml +71 -0
  9. data/examples/full.chapters.json +22 -0
  10. data/examples/full.gmi +44 -0
  11. data/examples/full.html +114 -0
  12. data/examples/full.repub +91 -0
  13. data/examples/full.summary.txt +2 -0
  14. data/examples/hello.chapters.json +3 -0
  15. data/examples/hello.gmi +1 -0
  16. data/examples/hello.html +5 -0
  17. data/examples/hello.repub +3 -0
  18. data/examples/hello.summary.txt +1 -0
  19. data/exe/repubmark +46 -0
  20. data/lib/repubmark/config.rb +53 -0
  21. data/lib/repubmark/elems/abbrev.rb +52 -0
  22. data/lib/repubmark/elems/annotation.rb +48 -0
  23. data/lib/repubmark/elems/article.rb +96 -0
  24. data/lib/repubmark/elems/base.rb +104 -0
  25. data/lib/repubmark/elems/blockquote.rb +80 -0
  26. data/lib/repubmark/elems/canvas.rb +104 -0
  27. data/lib/repubmark/elems/caption.rb +69 -0
  28. data/lib/repubmark/elems/chapter.rb +148 -0
  29. data/lib/repubmark/elems/code_block.rb +70 -0
  30. data/lib/repubmark/elems/code_inline.rb +35 -0
  31. data/lib/repubmark/elems/figure.rb +73 -0
  32. data/lib/repubmark/elems/figures.rb +47 -0
  33. data/lib/repubmark/elems/footnote.rb +87 -0
  34. data/lib/repubmark/elems/footnotes_category.rb +48 -0
  35. data/lib/repubmark/elems/fraction.rb +31 -0
  36. data/lib/repubmark/elems/iframe.rb +62 -0
  37. data/lib/repubmark/elems/joint.rb +176 -0
  38. data/lib/repubmark/elems/link.rb +56 -0
  39. data/lib/repubmark/elems/list.rb +84 -0
  40. data/lib/repubmark/elems/list_item.rb +147 -0
  41. data/lib/repubmark/elems/note.rb +40 -0
  42. data/lib/repubmark/elems/paragraph.rb +42 -0
  43. data/lib/repubmark/elems/power.rb +34 -0
  44. data/lib/repubmark/elems/quote.rb +53 -0
  45. data/lib/repubmark/elems/section.rb +58 -0
  46. data/lib/repubmark/elems/separator.rb +17 -0
  47. data/lib/repubmark/elems/special.rb +37 -0
  48. data/lib/repubmark/elems/text.rb +43 -0
  49. data/lib/repubmark/highlight.rb +85 -0
  50. data/lib/repubmark/titled_ref.rb +16 -0
  51. data/lib/repubmark/version.rb +6 -0
  52. data/lib/repubmark.rb +125 -0
  53. data/tests/examples.rb +51 -0
  54. metadata +129 -0
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Repubmark
4
+ module Elems
5
+ # TODO: maybe don't allow mdash everywhere
6
+ class Special < Base
7
+ parents :Joint
8
+
9
+ HTML = {
10
+ ellipsis: '&hellip;',
11
+ mdash: '&mdash;',
12
+ }.freeze
13
+
14
+ UNICODE = {
15
+ ellipsis: '…',
16
+ mdash: '—',
17
+ }.freeze
18
+
19
+ def initialize(parent, name)
20
+ super parent
21
+ name = String(name).to_sym.freeze
22
+ HTML.fetch name
23
+ @name = name
24
+ end
25
+
26
+ #################
27
+ # Basic methods #
28
+ #################
29
+
30
+ def to_summary_plain = UNICODE.fetch @name
31
+
32
+ def to_html = HTML.fetch @name
33
+
34
+ def to_gemtext = UNICODE.fetch @name
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Repubmark
4
+ module Elems
5
+ class Text < Base
6
+ parents :Joint
7
+
8
+ def initialize(parent, str, bold: false, italic: false)
9
+ super parent
10
+
11
+ self.str = str
12
+
13
+ @bold = !!bold
14
+ @italic = !!italic
15
+ end
16
+
17
+ #################
18
+ # Basic methods #
19
+ #################
20
+
21
+ def word_count = count_words @str
22
+
23
+ def to_summary_plain = @str
24
+
25
+ def to_html = str_to_html
26
+
27
+ def to_gemtext = @str
28
+
29
+ private
30
+
31
+ def str=(str)
32
+ @str = String(str).split.join(' ').freeze
33
+ end
34
+
35
+ def str_to_html
36
+ result = @str
37
+ result = "<em>#{result}</em>" if @italic
38
+ result = "<strong>#{result}</strong>" if @bold
39
+ result.freeze
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Repubmark
4
+ class Highlight
5
+ NO_SYNTAX = 'txt'
6
+
7
+ NO_LINE_NUMBERS = %i[sh_hist].freeze
8
+
9
+ SYNTAXES = {
10
+ certbot: 'ini',
11
+ css: 'css',
12
+ hjson: nil,
13
+ html: 'html',
14
+ ini: 'ini',
15
+ knockd: nil,
16
+ nginx: 'nginx',
17
+ openssh: nil,
18
+ ruby: 'ruby',
19
+ sh_hist: nil,
20
+ sysctl: nil,
21
+ torrc: nil,
22
+ wireguard: 'ini',
23
+ yggdrasil: nil, # Hjson
24
+ }.freeze
25
+
26
+ private_class_method :new
27
+
28
+ def self.call(...) = new(...).call
29
+
30
+ attr_reader :syntax, :str
31
+
32
+ def initialize(syntax, str)
33
+ self.syntax = syntax
34
+ self.str = str
35
+ end
36
+
37
+ def call
38
+ Open3.popen2(*cmdline) do |stdin, stdout, wait_thr|
39
+ stdin.write str
40
+ stdin.close
41
+ result = stdout.read.freeze
42
+ raise 'Highlight error' unless wait_thr.value.success?
43
+
44
+ "<pre><code>#{result}</code></pre>\n".freeze
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def syntax=(syntax)
51
+ return @syntax = nil if syntax.nil?
52
+
53
+ unless syntax.instance_of? Symbol
54
+ raise TypeError, "Expected #{Symbol}, got #{syntax.class}"
55
+ end
56
+
57
+ SYNTAXES.fetch syntax
58
+
59
+ @syntax = syntax
60
+ end
61
+
62
+ def str=(str)
63
+ str = String(str).strip.freeze
64
+ raise 'Expected non-empty string' if str.empty?
65
+
66
+ @str = str
67
+ end
68
+
69
+ def cmdline
70
+ @cmdline ||= [
71
+ 'highlight',
72
+ '--stdout',
73
+ '--fragment',
74
+ '--out-format',
75
+ 'html',
76
+ '--syntax',
77
+ SYNTAXES[syntax] || NO_SYNTAX,
78
+ ].tap do |cmdline|
79
+ unless syntax.nil? || NO_LINE_NUMBERS.include?(syntax)
80
+ cmdline << '--line-numbers'
81
+ end
82
+ end.freeze
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Repubmark
4
+ class TitledRef
5
+ attr_reader :url, :title
6
+
7
+ def initialize(url, title)
8
+ self.url = url
9
+ self.title = title
10
+ end
11
+
12
+ private
13
+
14
+ attr_writer :url, :title
15
+ end
16
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Repubmark
4
+ # Gem version.
5
+ VERSION = '0.0.0'
6
+ end
data/lib/repubmark.rb ADDED
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cgi'
4
+ require 'date'
5
+ require 'forwardable'
6
+ require 'open3'
7
+ require 'pathname'
8
+ require 'uri'
9
+ require 'yaml'
10
+
11
+ require 'addressable'
12
+
13
+ require_relative 'repubmark/config'
14
+ require_relative 'repubmark/highlight'
15
+ require_relative 'repubmark/titled_ref'
16
+
17
+ require_relative 'repubmark/elems/base'
18
+
19
+ # Top-level element
20
+ require_relative 'repubmark/elems/article'
21
+
22
+ # Always inside Article
23
+ require_relative 'repubmark/elems/annotation'
24
+ require_relative 'repubmark/elems/footnotes_category'
25
+ # Always inside Article, Chapter
26
+ require_relative 'repubmark/elems/chapter'
27
+ # Always inside Annotation, Blockquote, Chapter
28
+ require_relative 'repubmark/elems/canvas'
29
+
30
+ # Always inside Canvas
31
+ require_relative 'repubmark/elems/blockquote'
32
+ require_relative 'repubmark/elems/code_block'
33
+ require_relative 'repubmark/elems/figures'
34
+ require_relative 'repubmark/elems/iframe'
35
+ require_relative 'repubmark/elems/paragraph'
36
+ require_relative 'repubmark/elems/separator'
37
+
38
+ # Always inside Canvas, Figures
39
+ require_relative 'repubmark/elems/figure'
40
+ # Always inside Canvas, ListItem
41
+ require_relative 'repubmark/elems/list'
42
+ # Always inside List
43
+ require_relative 'repubmark/elems/list_item'
44
+
45
+ # Always inside Caption, Quote
46
+ require_relative 'repubmark/elems/joint'
47
+ # Always inside Blockquote, Figure, Footnote, ListItem, Paragraph
48
+ require_relative 'repubmark/elems/caption'
49
+ # Always inside Caption, Joint, Quote
50
+ require_relative 'repubmark/elems/quote'
51
+
52
+ # Always inside Joint
53
+ require_relative 'repubmark/elems/abbrev'
54
+ require_relative 'repubmark/elems/code_inline'
55
+ require_relative 'repubmark/elems/fraction'
56
+ require_relative 'repubmark/elems/note'
57
+ require_relative 'repubmark/elems/section'
58
+ require_relative 'repubmark/elems/special'
59
+ require_relative 'repubmark/elems/text'
60
+ require_relative 'repubmark/elems/link'
61
+ require_relative 'repubmark/elems/power'
62
+
63
+ # Always inside FootnotesCategory
64
+ require_relative 'repubmark/elems/footnote'
65
+
66
+ ##
67
+ # A Markdown-inspired markup language that can be compiled into HTML and
68
+ # Gemtext.
69
+ #
70
+ module Repubmark
71
+ FORMATS = %i[chapters gemtext html summary_plain word_count].freeze
72
+
73
+ SLUG_RE = /\A[[:word:]]+(-[[:word:]]+)*\z/
74
+
75
+ UNICODE_SUPS = {
76
+ '0' => '⁰',
77
+ '1' => '¹',
78
+ '2' => '²',
79
+ '3' => '³',
80
+ '4' => '⁴',
81
+ '5' => '⁵',
82
+ '6' => '⁶',
83
+ '7' => '⁷',
84
+ '8' => '⁸',
85
+ '9' => '⁹',
86
+ '+' => '⁺',
87
+ '-' => '⁻',
88
+ '=' => '⁼',
89
+ '(' => '⁽',
90
+ ')' => '⁾',
91
+ }.freeze
92
+
93
+ UNICODE_SUBS = {
94
+ '0' => '₀',
95
+ '1' => '₁',
96
+ '2' => '₂',
97
+ '3' => '₃',
98
+ '4' => '₄',
99
+ '5' => '₅',
100
+ '6' => '₆',
101
+ '7' => '₇',
102
+ '8' => '₈',
103
+ '9' => '₉',
104
+ '+' => '₊',
105
+ '-' => '₋',
106
+ '=' => '₌',
107
+ '(' => '₍',
108
+ ')' => '₎',
109
+ }.freeze
110
+
111
+ def self.validate_slug!(slug)
112
+ slug = String(slug).freeze
113
+ raise 'Invalid slug' unless SLUG_RE.match? slug
114
+
115
+ slug
116
+ end
117
+
118
+ def self.unicode_sup(val)
119
+ String(val).each_char.map { |chr| UNICODE_SUPS.fetch chr }.join.freeze
120
+ end
121
+
122
+ def self.unicode_sub(val)
123
+ String(val).each_char.map { |chr| UNICODE_SUBS.fetch chr }.join.freeze
124
+ end
125
+ end
data/tests/examples.rb ADDED
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'open3'
5
+
6
+ ROOT = File.expand_path('..', __dir__).freeze
7
+
8
+ EXE = File.expand_path('exe/repubmark', ROOT).freeze
9
+ EXAMPLES = File.expand_path('examples', ROOT).freeze
10
+ EXAMPLES_GLOB = File.expand_path('examples/*.repub', ROOT).freeze
11
+ CONFIG = File.expand_path('examples/config.yml', ROOT).freeze
12
+
13
+ PROFILES = [
14
+ %w[summary_plain summary.txt],
15
+ %w[http html],
16
+ %w[gemini gmi],
17
+ %w[chapters chapters.json],
18
+ ].map(&:freeze).freeze
19
+
20
+ Dir[EXAMPLES_GLOB].each do |example_filename|
21
+ example = File.read example_filename
22
+ PROFILES.each do |profile_name, expected_ext|
23
+ expected_filename = "#{example_filename[...-6]}.#{expected_ext}"
24
+ expected = File.read expected_filename
25
+
26
+ puts '=' * 80
27
+ puts "Config: #{CONFIG}"
28
+ puts "Example: #{example_filename}"
29
+ puts "Profile: #{profile_name}"
30
+ puts "Expected: #{expected_filename}"
31
+ puts '-' * 80
32
+ puts example.strip
33
+ puts '-' * 80
34
+ puts expected.strip
35
+
36
+ stdin, stdout, wait_thr = Open3.popen2 EXE, CONFIG, profile_name
37
+ stdin.puts example
38
+ stdin.close
39
+ result = stdout.read
40
+ stdout.close
41
+ raise 'Template engine error' unless wait_thr.value.success?
42
+ next if result == expected
43
+
44
+ puts '-' * 80
45
+ puts result.strip
46
+ puts '=' * 80
47
+ raise 'Invalid output'
48
+ end
49
+ end
50
+
51
+ puts '=' * 80
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: repubmark
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Alex Kotov
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-10-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: addressable
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.8'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.8'
27
+ - !ruby/object:Gem::Dependency
28
+ name: i18n
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.14'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.14'
41
+ description: A Markdown-inspired markup language that can be compiled into HTML and
42
+ Gemtext
43
+ email:
44
+ - me+repubmark@kotovalexarian.com
45
+ executables:
46
+ - repubmark
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - ".rubocop.yml"
51
+ - ".rubocop_todo.yml"
52
+ - ".yardopts"
53
+ - LICENSE
54
+ - README.md
55
+ - Rakefile
56
+ - examples/config.yml
57
+ - examples/full.chapters.json
58
+ - examples/full.gmi
59
+ - examples/full.html
60
+ - examples/full.repub
61
+ - examples/full.summary.txt
62
+ - examples/hello.chapters.json
63
+ - examples/hello.gmi
64
+ - examples/hello.html
65
+ - examples/hello.repub
66
+ - examples/hello.summary.txt
67
+ - exe/repubmark
68
+ - lib/repubmark.rb
69
+ - lib/repubmark/config.rb
70
+ - lib/repubmark/elems/abbrev.rb
71
+ - lib/repubmark/elems/annotation.rb
72
+ - lib/repubmark/elems/article.rb
73
+ - lib/repubmark/elems/base.rb
74
+ - lib/repubmark/elems/blockquote.rb
75
+ - lib/repubmark/elems/canvas.rb
76
+ - lib/repubmark/elems/caption.rb
77
+ - lib/repubmark/elems/chapter.rb
78
+ - lib/repubmark/elems/code_block.rb
79
+ - lib/repubmark/elems/code_inline.rb
80
+ - lib/repubmark/elems/figure.rb
81
+ - lib/repubmark/elems/figures.rb
82
+ - lib/repubmark/elems/footnote.rb
83
+ - lib/repubmark/elems/footnotes_category.rb
84
+ - lib/repubmark/elems/fraction.rb
85
+ - lib/repubmark/elems/iframe.rb
86
+ - lib/repubmark/elems/joint.rb
87
+ - lib/repubmark/elems/link.rb
88
+ - lib/repubmark/elems/list.rb
89
+ - lib/repubmark/elems/list_item.rb
90
+ - lib/repubmark/elems/note.rb
91
+ - lib/repubmark/elems/paragraph.rb
92
+ - lib/repubmark/elems/power.rb
93
+ - lib/repubmark/elems/quote.rb
94
+ - lib/repubmark/elems/section.rb
95
+ - lib/repubmark/elems/separator.rb
96
+ - lib/repubmark/elems/special.rb
97
+ - lib/repubmark/elems/text.rb
98
+ - lib/repubmark/highlight.rb
99
+ - lib/repubmark/titled_ref.rb
100
+ - lib/repubmark/version.rb
101
+ - tests/examples.rb
102
+ homepage: https://git.causa-arcana.com/causa-arcana/repubmark
103
+ licenses:
104
+ - MIT
105
+ metadata:
106
+ rubygems_mfa_required: 'true'
107
+ homepage_uri: https://git.causa-arcana.com/causa-arcana/repubmark
108
+ source_code_uri: https://git.causa-arcana.com/causa-arcana/repubmark
109
+ documentation_uri: https://www.rubydoc.info/gems/repubmark/0.0.0
110
+ post_install_message:
111
+ rdoc_options: []
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - "~>"
117
+ - !ruby/object:Gem::Version
118
+ version: '3.2'
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubygems_version: 3.4.19
126
+ signing_key:
127
+ specification_version: 4
128
+ summary: A Markdown-inspired markup language that can be compiled into HTML and Gemtext
129
+ test_files: []