markita 4.1.230214 → 6.0.250327

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +194 -237
  3. data/bin/markita +16 -19
  4. data/lib/markita/base.rb +37 -38
  5. data/lib/markita/config.rb +41 -10
  6. data/lib/markita/html.rb +35 -20
  7. data/lib/markita/markdown/attributes.rb +23 -0
  8. data/lib/markita/markdown/blockquote.rb +41 -0
  9. data/lib/markita/markdown/code.rb +44 -0
  10. data/lib/markita/markdown/code_block.rb +29 -0
  11. data/lib/markita/markdown/definitions.rb +42 -0
  12. data/lib/markita/markdown/embed.rb +63 -0
  13. data/lib/markita/markdown/empty.rb +22 -0
  14. data/lib/markita/markdown/fold.rb +39 -0
  15. data/lib/markita/markdown/footnotes.rb +28 -0
  16. data/lib/markita/markdown/form.rb +125 -0
  17. data/lib/markita/markdown/heading.rb +33 -0
  18. data/lib/markita/markdown/horizontal_rule.rb +25 -0
  19. data/lib/markita/markdown/image.rb +60 -0
  20. data/lib/markita/markdown/inline.rb +123 -0
  21. data/lib/markita/markdown/list.rb +65 -0
  22. data/lib/markita/markdown/markup.rb +23 -0
  23. data/lib/markita/markdown/script.rb +28 -0
  24. data/lib/markita/markdown/split.rb +38 -0
  25. data/lib/markita/markdown/table.rb +52 -0
  26. data/lib/markita/markdown.rb +51 -498
  27. data/lib/markita/plug/about.rb +28 -17
  28. data/lib/markita/plug/favicon.rb +14 -10
  29. data/lib/markita/plug/highlight.rb +18 -12
  30. data/lib/markita/plug/login.rb +35 -28
  31. data/lib/markita/plug/navigation.rb +4 -1
  32. data/lib/markita/plug/plugs.rb +7 -1
  33. data/lib/markita/plug/readme.rb +8 -4
  34. data/lib/markita/preprocess.rb +52 -23
  35. data/lib/markita/refinement.rb +21 -0
  36. data/lib/markita/requires.rb +29 -0
  37. data/lib/markita.rb +15 -24
  38. metadata +52 -116
@@ -1,21 +1,52 @@
1
- module Markita
2
- OPTIONS ||= nil
1
+ # frozen_string_literal: true
3
2
 
3
+ # Markita namespace
4
+ # :reek:TooManyConstants because it's a configuration file!
5
+ module Markita
4
6
  HEADER_LINKS = ''
5
- NAVIGATION = ''
7
+ NAVIGATION = ''
8
+
9
+ BIND = OPTIONS.bind || '0.0.0.0'
10
+ PORT = OPTIONS.port || '8080'
11
+ SERVER = 'webrick'
12
+
13
+ MDX = '.md'
14
+ INDEX = 'index'
15
+
16
+ ROOT = File.expand_path OPTIONS.root || '~/vimwiki'
17
+ raise "Missing site root directory: #{ROOT}" unless File.directory? ROOT
18
+
19
+ ssl_certificate = File.join(ROOT, '.cert.crt')
20
+ ssl_private_key = File.join(ROOT, '.pkey.pem')
21
+ SERVER_SETTINGS =
22
+ if [ssl_certificate, ssl_private_key].all? { File.exist? it }
23
+ {
24
+ SSLEnable:
25
+ true,
26
+ SSLVerifyClient:
27
+ OpenSSL::SSL::VERIFY_NONE,
28
+ SSLCertificate:
29
+ OpenSSL::X509::Certificate.new(File.read(ssl_certificate)),
30
+ SSLPrivateKey:
31
+ OpenSSL::PKey::RSA.new(File.read(ssl_private_key))
32
+ }
33
+ end
6
34
 
7
- ROOT = File.expand_path OPTIONS&.root || '~/vimwiki'
8
- raise 'Missing site root directory: '+ROOT unless File.directory? ROOT
9
- APPDIR = File.dirname __dir__, 2
35
+ SERVER_CONFIG = lambda do |server|
36
+ puts "#{$PROGRAM_NAME}-#{VERSION}".blue
37
+ puts "Sinatra-#{Sinatra::VERSION} using #{server.class}".blue
38
+ end
39
+
40
+ APPDIR = File.dirname __dir__, 2
10
41
  APPDATA = File.join APPDIR, 'data'
42
+
11
43
  PATH = lambda do |basename|
12
- [ROOT, APPDATA].map{ File.join _1, basename}.detect{ File.exist? _1}
44
+ [ROOT, APPDATA].map { File.join it, basename }.detect { File.exist? it }
13
45
  end
14
- NOT_FOUND = File.read PATH['not_found.html']
15
46
 
16
- EMOJIS = Hash[*File.read(PATH['emojis.tsv']).split(/\s+/)]
47
+ NOT_FOUND = File.read PATH['not_found.html']
17
48
 
18
- PAGE_KEY = %r{/(\w[\w/-]*\w)}
49
+ PAGE_KEY = %r{/(\w[\w/-]*\w)} # Note that it starts with a slash
19
50
  SEND_FILE = %r{/(\w[\w/-]*\w\.\w+)}
20
51
 
21
52
  START_TIME = Time.now
data/lib/markita/html.rb CHANGED
@@ -1,25 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Markita namespace
1
4
  module Markita
2
- module HTML
3
- def self.header(key)
4
- <<~HEADER
5
- <!DOCTYPE html>
6
- <html>
7
- <head>
8
- <title>#{key}</title>
9
- #{HEADER_LINKS}</head>
10
- <body>
11
- HEADER
12
- end
5
+ # HTML template
6
+ # :reek:ClassVariable
7
+ # rubocop:disable Style/ClassVars
8
+ module Html
9
+ # category: header
13
10
 
14
- def self.navigation
15
- NAVIGATION
16
- end
11
+ def self.header(title)
12
+ <<~HEADER
13
+ <!DOCTYPE html>
14
+ <html>
15
+ <head>
16
+ <title>#{title}</title>
17
+ #{@@header_links}</head>
18
+ <body>
19
+ HEADER
20
+ end
21
+
22
+ @@header_links = String.new(HEADER_LINKS)
23
+ def self.header_links = @@header_links
17
24
 
18
- def self.footer
19
- <<~FOOTER
20
- </body>
21
- </html>
22
- FOOTER
25
+ # category: body
26
+
27
+ @@navigation = String.new(NAVIGATION)
28
+ def self.navigation = @@navigation
29
+
30
+ # category: footer
31
+
32
+ def self.footer
33
+ <<~FOOTER
34
+ </body>
35
+ </html>
36
+ FOOTER
37
+ end
23
38
  end
24
- end
39
+ # rubocop:enable Style/ClassVars
25
40
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Markita top level namespace
4
+ module Markita
5
+ # Markdown namespace
6
+ # :reek:InstanceVariableAssumption :reek:ClassVariable
7
+ class Markdown
8
+ # Module to isolate from Markdown
9
+ module Attributes
10
+ RGX = /^\{:( [^\{\}]+)\}/
11
+ end
12
+
13
+ @@parsers << :attributes
14
+
15
+ def attributes
16
+ return false unless (md = Attributes::RGX.match(@line))
17
+
18
+ @attributes.push md[1]
19
+ @line = md.post_match
20
+ true
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Markita top level namespace
4
+ module Markita
5
+ # Markdown namespace
6
+ # :reek:InstanceVariableAssumption :reek:ClassVariable
7
+ class Markdown
8
+ # Module to isolate from Markdown
9
+ module Blockquote
10
+ RGX = /^( {0,3})> (.*)$/
11
+
12
+ def self.level_quote(line)
13
+ mdt = RGX.match(line)
14
+ [mdt[1].length, mdt[2]] if mdt
15
+ end
16
+ end
17
+
18
+ @@parsers << :blockquote
19
+
20
+ # category: method
21
+ # :reek:DuplicateMethodCall :reek:TooManyStatements
22
+ # rubocop:disable Metrics/MethodLength
23
+ def blockquote
24
+ return false unless (level, quote = Blockquote.level_quote(@line))
25
+
26
+ @html << "<blockquote#{@attributes.shift}>\n"
27
+ current = level
28
+ while current.eql?(level)
29
+ @html << "#{inline(quote)}\n"
30
+ current, quote = Blockquote.level_quote(line_gets)
31
+ next unless current&.>(level)
32
+
33
+ blockquote
34
+ level, quote = Blockquote.level_quote(@line)
35
+ end
36
+ @html << "</blockquote>\n"
37
+ true
38
+ end
39
+ # rubocop:enable Metrics/MethodLength
40
+ end
41
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Markita top level namespace
4
+ module Markita
5
+ # Markdown namespace
6
+ # :reek:InstanceVariableAssumption :reek:ClassVariable
7
+ class Markdown
8
+ # Module to isolate from Markdown
9
+ module Code
10
+ RGX = /^[`]{3}\s*(\w+)?$/
11
+
12
+ def self.code(file, lang, code = String.new)
13
+ # Note that we can ignore the final shifted
14
+ # line because it will be the closing fence.
15
+ while (line = file.gets) && !RGX.match?(line)
16
+ code << line
17
+ end
18
+ lang ? ROUGE.format(lang.new.lex(code)) : code
19
+ end
20
+
21
+ def self.klass_lang(line)
22
+ if (mdt = RGX.match(line))
23
+ lang = Rouge::Lexer.find(mdt[1])
24
+ klass = lang ? ' class="highlight"' : ''
25
+ [klass, lang]
26
+ end
27
+ end
28
+ end
29
+
30
+ @@parsers << :code
31
+
32
+ # category: method
33
+ # :reek:TooManyStatements
34
+ def code
35
+ return false unless (klass, lang = Code.klass_lang(@line))
36
+
37
+ @html << "<pre#{klass}#{@attributes.shift}><code>\n"
38
+ @html << Code.code(@line_getter, lang)
39
+ @html << "</code></pre>\n"
40
+ line_gets
41
+ true
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Markita top level namespace
4
+ module Markita
5
+ # Markdown namespace
6
+ # :reek:InstanceVariableAssumption :reek:ClassVariable
7
+ class Markdown
8
+ # Module to isolate from Markdown
9
+ module CodeBlock
10
+ RGX = /^ {4}(.*)$/
11
+ end
12
+
13
+ @@parsers << :code_block
14
+
15
+ # category: method
16
+ # :reek:TooManyStatements
17
+ def code_block
18
+ return false unless (md = CodeBlock::RGX.match(@line))
19
+
20
+ @html << "<pre#{@attributes.shift}>\n"
21
+ while md
22
+ @html << "#{md[1]}\n"
23
+ md = CodeBlock::RGX.match(line_gets)
24
+ end
25
+ @html << "</pre>\n"
26
+ true
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Markita top level namespace
4
+ module Markita
5
+ # Markdown namespace
6
+ # :reek:InstanceVariableAssumption :reek:ClassVariable
7
+ class Markdown
8
+ # Module to isolate from Markdown
9
+ module Definitions
10
+ RGX = /^[+] (\S.+)$/
11
+ def self.phrase(line)
12
+ mdt = RGX.match(line)
13
+ mdt[1] if mdt
14
+ end
15
+
16
+ def self.split(phrase)
17
+ return [phrase.chop, nil] if /:$/.match?(phrase)
18
+ return [nil, phrase] unless (mdt = /^(.*): +(\S.*)$/.match(phrase))
19
+
20
+ [mdt[1], mdt[2]]
21
+ end
22
+ end
23
+
24
+ @@parsers << :definitions
25
+
26
+ # category: method
27
+ # :reek:TooManyStatements
28
+ def definitions
29
+ return false unless (phrase = Definitions.phrase(@line))
30
+
31
+ @html << "<dl#{@attributes.shift}>\n"
32
+ while phrase
33
+ term, definition = Definitions.split(phrase)
34
+ @html << "<dt>#{inline term}</dt>\n" if term
35
+ @html << "<dd>#{inline definition}</dd>\n" if definition
36
+ phrase = Definitions.phrase(line_gets)
37
+ end
38
+ @html << "</dl>\n"
39
+ true
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Markita top level namespace
4
+ module Markita
5
+ # Markdown namespace
6
+ # :reek:InstanceVariableAssumption :reek:ClassVariable
7
+ class Markdown
8
+ # Module to isolate from Markdown
9
+ module Embed
10
+ RGX = /^!> (#{PAGE_KEY}\.\w+)$/
11
+
12
+ def self.code(filename, lang)
13
+ code = File.read(filename)
14
+ lang ? ROUGE.format(lang.new.lex(code)) : code
15
+ end
16
+
17
+ # :reek:TooManyStatements
18
+ def self.ext_klass_lang(filename, lang = nil)
19
+ extension = filename.split('.').last
20
+ unless extension == 'html'
21
+ lang = Rouge::Lexer.find(extension) unless extension == 'txt'
22
+ klass = lang ? ' class="highlight"' : ''
23
+ return [true, klass, lang]
24
+ end
25
+ [false, nil, nil]
26
+ end
27
+
28
+ def self.filename(line)
29
+ if (mdt = RGX.match(line))
30
+ mdt[1]
31
+ end
32
+ end
33
+ end
34
+
35
+ @@parsers << :embed
36
+
37
+ # category: method
38
+ # :reek:TooManyStatements
39
+ # rubocop:disable Metrics/MethodLength
40
+ def embed
41
+ return false unless (filename = Embed.filename(@line))
42
+
43
+ if File.exist?(filename = File.join(ROOT, filename))
44
+ code, klass, lang = Embed.ext_klass_lang(filename)
45
+ if code
46
+ @html << "<pre#{klass}#{@attributes.shift}>"
47
+ @html << '<code>' if lang
48
+ @html << "\n"
49
+ end
50
+ @html << Embed.code(filename, lang)
51
+ if code
52
+ @html << '</code>' if lang
53
+ @html << "</pre>\n"
54
+ end
55
+ else
56
+ @html << @line
57
+ end
58
+ line_gets
59
+ true
60
+ end
61
+ # rubocop:enable Metrics/MethodLength
62
+ end
63
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Markita top level namespace
4
+ module Markita
5
+ # Markdown namespace
6
+ # :reek:InstanceVariableAssumption :reek:ClassVariable
7
+ class Markdown
8
+ # Module to isolate from Markdown
9
+ module Empty
10
+ RGX = /^$/
11
+ end
12
+
13
+ @@parsers << :empty
14
+
15
+ def empty
16
+ return false unless Empty::RGX.match?(@line)
17
+
18
+ line_gets
19
+ true
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Markita top level namespace
4
+ module Markita
5
+ # Markdown namespace
6
+ # :reek:InstanceVariableAssumption :reek:ClassVariable
7
+ class Markdown
8
+ # Module to isolate from Markdown
9
+ # :reek:DuplicateMethodCall
10
+ module Fold
11
+ RGX = /^[-.]{3} #/
12
+ METADATA = /^(\w+): (.*)$/
13
+ def self.scrape4metadata(line, metadata, attributes)
14
+ if (md = Fold::METADATA.match(line))
15
+ if (key = md[1]) == 'attributes'
16
+ attributes.push " #{md[2]}"
17
+ else
18
+ metadata[key] = md[2]
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ @@parsers << :fold
25
+
26
+ # category: method
27
+ # :reek:DuplicateMethodCall ok here
28
+ def fold
29
+ return false unless Fold::RGX.match?(@line)
30
+
31
+ # Fold with optional metadata
32
+ until Fold::RGX.match?(line_gets)
33
+ Fold.scrape4metadata(@line, @metadata, @attributes)
34
+ end
35
+ line_gets
36
+ true
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Markita top level namespace
4
+ module Markita
5
+ # Markdown namespace
6
+ # :reek:InstanceVariableAssumption :reek:ClassVariable
7
+ class Markdown
8
+ # Module to isolate from Markdown
9
+ module Footnotes
10
+ RGX = /^\[\^\d+\]:/
11
+ end
12
+
13
+ @@parsers << :footnotes
14
+
15
+ # :reek:TooManyStatements
16
+ def footnotes
17
+ return false unless (continue = Footnotes::RGX.match?(@line))
18
+
19
+ @html << "<small>\n"
20
+ while continue
21
+ @html << "#{inline(@line.chomp)}<br>\n"
22
+ continue = Footnotes::RGX.match?(line_gets)
23
+ end
24
+ @html << "</small>\n"
25
+ true
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Markita top level namespace
4
+ module Markita
5
+ # Markdown namespace
6
+ # :reek:InstanceVariableAssumption :reek:ClassVariable
7
+ class Markdown
8
+ # Module to isolate from Markdown
9
+ module Form
10
+ field = '(\w+:)?\[(\*)?(\w+)(=("[^"]+")(,"[^"]+")*)?\]'
11
+ FIELD = Regexp.new field
12
+ RGX = /^!( #{field})+/
13
+ POST = /!$/
14
+ ACTION = /\(([^()]*)\)!?$/
15
+
16
+ def self.input_select(field, name, values)
17
+ %( #{field}:<select name="#{name}">\n).tap do |html|
18
+ values.each do |value|
19
+ html << %( <option value="#{value}">#{value}</option>\n)
20
+ end
21
+ html << " </select>\n"
22
+ end
23
+ end
24
+
25
+ def self.input_text(field, type, name)
26
+ %( #{field}:<input type="#{type}" name="#{name}">\n)
27
+ end
28
+
29
+ # :reek:LongParameterList :reek:TooManyStatements
30
+ def self.input(type, field, name, values)
31
+ if field
32
+ if values.empty? then input_text(field, type, name)
33
+ elsif values.count > 1 then input_select(field, name, values)
34
+ else
35
+ input_defaulted(field, type, name, values)
36
+ end
37
+ elsif name == 'submit' then input_submit(values)
38
+ else
39
+ input_hidden(name, values)
40
+ end
41
+ end
42
+
43
+ def self.input_submit(values)
44
+ %( <input type="submit" value="#{values[0] || 'Submit'}">\n)
45
+ end
46
+
47
+ def self.input_hidden(name, values)
48
+ %( <input type="hidden" name="#{name}" value="#{values[0]}">\n)
49
+ end
50
+
51
+ # :reek:LongParameterList
52
+ def self.input_defaulted(field, type, name, values)
53
+ <<-INPUT
54
+ #{field}:<input type="#{type}" name="#{name}"
55
+ value="#{values[0]}">
56
+ INPUT
57
+ end
58
+
59
+ def self.match?(line) = RGX.match?(line)
60
+
61
+ # :reek:ControlParameter :reek:NilCheck :reek:LongParameterList
62
+ def self.maybe(yon, name, field, values)
63
+ return :NO if yon == :NO || (field.nil? && name == 'submit')
64
+ return yon unless field && values.count < 2
65
+
66
+ yon == :yes ? :no : :YES
67
+ end
68
+
69
+ # :reek:LongYieldList :reek:TooManyStatements
70
+ def self.scan(line)
71
+ line.scan(FIELD).each do |field, pwd, name, value|
72
+ field &&= field[0...-1]
73
+ values = value ? value[2...-1].split('","') : []
74
+ type = pwd ? 'password' : 'text'
75
+ yield field, type, name, values
76
+ end
77
+ end
78
+
79
+ def self.start(line, attributes)
80
+ method = POST.match?(line) ? ' method="post"' : ''
81
+ action = (mdt = ACTION.match(line)) ? %( action="#{mdt[1]}") : ''
82
+ %(<form#{action}#{method}#{attributes.shift}>\n)
83
+ end
84
+
85
+ def self.stop = %(</form>\n)
86
+
87
+ # :reek:ControlParameter
88
+ def self.submit(yon)
89
+ case yon
90
+ when :yes
91
+ %( <input type="submit">\n)
92
+ when :YES
93
+ %( <br>\n <input type="submit">\n)
94
+ else
95
+ ''
96
+ end
97
+ end
98
+ end
99
+
100
+ @@parsers << :form
101
+
102
+ # category: method
103
+ # :reek:TooManyStatements
104
+ # rubocop:disable Metrics/MethodLength
105
+ def form
106
+ return false unless Form.match?(@line)
107
+
108
+ yon = :yes # Append submit button?
109
+ @html << Form.start(@line, @attributes)
110
+ loop do
111
+ Form.scan(@line) do |field, type, name, values|
112
+ yon = Form.maybe(yon, name, field, values)
113
+ @html << Form.input(type, field, name, values)
114
+ end
115
+ break unless Form.match?(line_gets)
116
+
117
+ @html << " <br>\n"
118
+ end
119
+ @html << Form.submit(yon)
120
+ @html << Form.stop
121
+ true
122
+ end
123
+ # rubocop:enable Metrics/MethodLength
124
+ end
125
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Markita top level namespace
4
+ module Markita
5
+ # Markdown namespace
6
+ # :reek:InstanceVariableAssumption :reek:ClassVariable
7
+ class Markdown
8
+ # Module to isolate from Markdown
9
+ module Heading
10
+ RGX = /^(\#{1,6}) (.*)$/
11
+
12
+ def self.level_header(line)
13
+ mdt = RGX.match(line)
14
+ [mdt[1].length, mdt[2]] if mdt
15
+ end
16
+ end
17
+
18
+ @@parsers << :heading
19
+
20
+ # category: method
21
+ # :reek:TooManyStatements :reek:UncommunicativeVariableName
22
+ def heading
23
+ return false unless (n, header = Heading.level_header(@line))
24
+
25
+ id = header.gsub(/\([^()]*\)/, '').scan(/\w+/).join('+')
26
+ @html << %(<a id="#{id}">\n)
27
+ @html << " <h#{n}#{@attributes.shift}>#{inline(header)}</h#{n}>\n"
28
+ @html << "</a>\n"
29
+ line_gets
30
+ true
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Markita top level namespace
4
+ module Markita
5
+ # Markdown namespace
6
+ # :reek:InstanceVariableAssumption :reek:ClassVariable
7
+ class Markdown
8
+ # Module to isolate from Markdown
9
+ module HorizontalRule
10
+ RGX = /^---+$/
11
+ end
12
+
13
+ @@parsers << :horizontal_rule
14
+
15
+ # category: method
16
+ def horizontal_rule
17
+ return false unless HorizontalRule::RGX.match?(@line)
18
+
19
+ line_gets
20
+ # Display HR
21
+ @html << "<hr#{@attributes.shift}>\n"
22
+ true
23
+ end
24
+ end
25
+ end