marko 0.3.0 → 0.4.0

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 (119) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +12 -0
  3. data/.rubocop.yml +45 -0
  4. data/CHANGELOG.md +10 -4
  5. data/Dockerfile +11 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +17 -31
  8. data/Rakefile +2 -11
  9. data/exe/marko +4 -22
  10. data/lib/basic.rb +27 -0
  11. data/lib/marko/chain.rb +44 -0
  12. data/lib/marko/cli.rb +119 -129
  13. data/lib/marko/config.rb +30 -20
  14. data/lib/marko/errors.rb +38 -0
  15. data/lib/marko/model/markup.rb +33 -0
  16. data/lib/marko/model/topic.rb +126 -0
  17. data/lib/marko/model/tree_node.rb +34 -0
  18. data/lib/marko/model.rb +10 -0
  19. data/lib/marko/parser/metadata.rb +28 -0
  20. data/lib/marko/parser/source.rb +52 -0
  21. data/lib/marko/parser/topic.rb +33 -0
  22. data/lib/marko/parser.rb +8 -19
  23. data/lib/marko/renderers/artifact.rb +29 -0
  24. data/lib/marko/renderers/content.rb +37 -0
  25. data/lib/marko/renderers/link.rb +19 -0
  26. data/lib/marko/renderers/metadata.rb +34 -0
  27. data/lib/marko/renderers/nested_list.rb +21 -0
  28. data/lib/marko/renderers/nested_tree.rb +22 -0
  29. data/lib/marko/renderers/renderer.rb +17 -0
  30. data/lib/marko/renderers/title.rb +16 -0
  31. data/lib/marko/renderers/topic.rb +24 -0
  32. data/lib/marko/renderers/url.rb +16 -0
  33. data/lib/marko/renderers.rb +17 -0
  34. data/lib/marko/scanner.rb +39 -0
  35. data/lib/marko/tasks/assemble.rb +52 -0
  36. data/lib/marko/tasks/compile.rb +19 -0
  37. data/lib/marko/tasks/load.rb +13 -0
  38. data/lib/marko/tasks/parse.rb +27 -0
  39. data/lib/marko/tasks/scan.rb +18 -0
  40. data/lib/marko/tasks/validate.rb +32 -0
  41. data/lib/marko/tasks.rb +13 -0
  42. data/lib/marko/validators/lost_index.rb +21 -0
  43. data/lib/marko/validators/lost_links.rb +25 -0
  44. data/lib/marko/validators/lost_parent.rb +21 -0
  45. data/lib/marko/validators/non_unique_id.rb +23 -0
  46. data/lib/marko/validators.rb +11 -0
  47. data/lib/marko/version.rb +1 -3
  48. data/lib/marko.rb +11 -43
  49. metadata +47 -85
  50. data/Gemfile +0 -10
  51. data/Gemfile.lock +0 -23
  52. data/STORY.md +0 -44
  53. data/_layouts/footer.md +0 -4
  54. data/_layouts/header.md +0 -3
  55. data/_layouts/layout.html +0 -73
  56. data/_layouts/robots.txt.erb +0 -4
  57. data/_layouts/sitemap.xml.erb +0 -20
  58. data/_layouts/styles.css +0 -92
  59. data/docs/changelog.html +0 -74
  60. data/docs/css/styles.css +0 -92
  61. data/docs/index.html +0 -297
  62. data/docs/readme.html +0 -297
  63. data/docs/robots.txt +0 -4
  64. data/docs/sitemap.xml +0 -30
  65. data/docs/story.html +0 -132
  66. data/lib/assets/demo/README.md +0 -13
  67. data/lib/assets/demo/src/fr/assemble.md +0 -27
  68. data/lib/assets/demo/src/fr/compile.md +0 -25
  69. data/lib/assets/demo/src/fr/markup.md +0 -111
  70. data/lib/assets/demo/src/fr/storage.md +0 -16
  71. data/lib/assets/demo/src/fr/treenode.md +0 -34
  72. data/lib/assets/demo/src/index.md +0 -34
  73. data/lib/assets/demo/src/intro.md +0 -98
  74. data/lib/assets/demo/src/ui/cli.md +0 -26
  75. data/lib/assets/demo/src/ui/gem.md +0 -14
  76. data/lib/assets/demo/src/ur/uc.create.project.md +0 -8
  77. data/lib/assets/demo/src/ur/uc.general.flow.md +0 -14
  78. data/lib/assets/init/README.md +0 -61
  79. data/lib/assets/init/Rakefile +0 -100
  80. data/lib/assets/init/tt/custom.md.tt +0 -19
  81. data/lib/assets/init/tt/default.md.tt +0 -4
  82. data/lib/assets/samples/0_index.md +0 -14
  83. data/lib/assets/samples/1_intro.md +0 -55
  84. data/lib/assets/samples/2_stakh.md +0 -21
  85. data/lib/assets/samples/3_actors.md +0 -10
  86. data/lib/assets/samples/4_cases.md +0 -53
  87. data/lib/assets/samples/5_entities.md +0 -18
  88. data/lib/assets/samples/6_concerns.md +0 -60
  89. data/lib/assets/samples/SRS-IEEE-830-1998.md +0 -293
  90. data/lib/assets/samples/SRS-RUP.md +0 -283
  91. data/lib/assets/samples/business-case.md +0 -116
  92. data/lib/assets/samples/ears.md +0 -112
  93. data/lib/assets/samples/gen_punch_domain.rb +0 -183
  94. data/lib/assets/samples/requirements.md +0 -105
  95. data/lib/assets/samples/stakeholders.png +0 -0
  96. data/lib/assets/samples/vision.md +0 -191
  97. data/lib/marko/artifact.rb +0 -5
  98. data/lib/marko/assembler.rb +0 -83
  99. data/lib/marko/compiler.rb +0 -16
  100. data/lib/marko/gadgets/pluggable.rb +0 -55
  101. data/lib/marko/gadgets/sentry.rb +0 -66
  102. data/lib/marko/gadgets/service.rb +0 -52
  103. data/lib/marko/gadgets.rb +0 -3
  104. data/lib/marko/loader.rb +0 -38
  105. data/lib/marko/markup/compiler.rb +0 -30
  106. data/lib/marko/markup/decorator.rb +0 -80
  107. data/lib/marko/markup/macro.rb +0 -176
  108. data/lib/marko/markup/parser.rb +0 -122
  109. data/lib/marko/markup/storage.rb +0 -118
  110. data/lib/marko/markup/validator.rb +0 -101
  111. data/lib/marko/markup.rb +0 -24
  112. data/lib/marko/services/assemble.rb +0 -16
  113. data/lib/marko/services/compile.rb +0 -30
  114. data/lib/marko/services.rb +0 -2
  115. data/lib/marko/storage.rb +0 -36
  116. data/lib/marko/tree_node.rb +0 -129
  117. data/lib/marko/validator.rb +0 -19
  118. data/marko.gemspec +0 -44
  119. data/sancho.yml +0 -6
@@ -0,0 +1,38 @@
1
+ module Marko
2
+
3
+ # Marko error
4
+ class MarkoError < StandardError
5
+ end
6
+
7
+ # Parser error
8
+ class ParserError < MarkoError
9
+ attr_reader :filename
10
+ attr_reader :lineno
11
+ attr_reader :markup
12
+
13
+ def initialize(msg, filename, lineno, markup)
14
+ @filename = filename
15
+ @lineno = lineno
16
+ @markup = markup
17
+ super(msg)
18
+ end
19
+ end
20
+
21
+ # Stage like load/compile errors
22
+ class StageError < MarkoError
23
+ attr_reader :errors
24
+
25
+ def initialize(msg, errors)
26
+ @errors = errors
27
+ super(msg)
28
+ end
29
+ end
30
+
31
+ class ValidatorError < MarkoError
32
+ end
33
+
34
+ # Compiler error
35
+ class CompilerError < MarkoError
36
+ end
37
+
38
+ end
@@ -0,0 +1,33 @@
1
+ module Marko
2
+ module Model
3
+
4
+ # Raw Markup
5
+ class Markup
6
+ # @return [String]
7
+ attr_reader :filename
8
+ # @return [Intger]
9
+ attr_reader :lineno
10
+ # @return [String]
11
+ attr_reader :content
12
+
13
+ def initialize(filename, lineno, content)
14
+ @filename = filename
15
+ @lineno = lineno
16
+ @content = content
17
+ end
18
+
19
+ def header
20
+ @content.lines.first
21
+ end
22
+
23
+ def level
24
+ header.scan(/^#+/).first&.size
25
+ end
26
+
27
+ def to_s
28
+ "#{filename}:#{lineno.to_s.rjust(2,'0')} >> #{header}".strip
29
+ end
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,126 @@
1
+ require_relative 'tree_node'
2
+ require_relative 'markup'
3
+ require 'forwardable'
4
+
5
+ module Marko
6
+ module Model
7
+
8
+ # Topic
9
+ class Topic
10
+ include TreeNode
11
+ extend Forwardable
12
+ def_delegators :metadata, :[], :[]=
13
+ def_delegator :markup, :level, :markup_level
14
+ def_delegator :markup, :filename, :markup_filename
15
+ def_delegator :markup, :content, :markup_content
16
+ def_delegator :markup, :lineno, :markup_lineno
17
+
18
+ # @return [String]
19
+ attr_reader :title
20
+ # @return [String]
21
+ attr_reader :content
22
+ # @return [Hash]
23
+ attr_reader :metadata
24
+ # @return [Markup]
25
+ attr_reader :markup
26
+
27
+ # @param title [String]
28
+ # @param content [String]
29
+ # @param metadata [Hash]
30
+ def initialize(title, content, **metadata)
31
+ @parent = nil
32
+ @items = []
33
+ @title = title
34
+ @content = content
35
+ @metadata = metadata
36
+ @markup = metadata.delete(:markup)
37
+ end
38
+
39
+ def to_s
40
+ to_a
41
+ .map{ "<#Topic id: #{it.id}, title: #{it.title}, level: #{it.nesting_level}>"}
42
+ .join(?\n)
43
+ end
44
+
45
+ def each(&block)
46
+ super(&block)
47
+ items.each do |e|
48
+ e.each(&block)
49
+ end
50
+ end
51
+
52
+ def add(item)
53
+ item.parent = self
54
+ @items << item
55
+ self
56
+ end
57
+
58
+ def delete(item)
59
+ @items.delete(item)
60
+ end
61
+
62
+ # @return [Array<String>] macro links in content
63
+ def links
64
+ return [] if @content.empty?
65
+ @content.scan(/\[\[([\w\.]*)\]\]/).flatten.uniq
66
+ end
67
+
68
+ def find_by_id(ref)
69
+ find{ it.id == ref } || root.find{ it.id == ref }
70
+ end
71
+
72
+ # @return [Array<String>] ids in order_index but out of @items
73
+ def lost_order_index
74
+ order_index.reject{ item(it) }
75
+ end
76
+
77
+ # def parent_id
78
+ # @metdata[:parent_id]
79
+ # end
80
+
81
+ # sort of dynamic id
82
+ def id
83
+ @metadata.fetch(:id, '').then do |e|
84
+ e = parent.id + e if e.start_with?(?.) && parent
85
+ e
86
+ end
87
+ end
88
+
89
+ def id=(id)
90
+ @metadata[:id] = id
91
+ end
92
+
93
+ protected
94
+
95
+ def parent=(parent)
96
+ @parent = parent
97
+ end
98
+
99
+ def order_index
100
+ @order_index = @metadata
101
+ .fetch(:order_index, '')
102
+ .strip
103
+ .split(/\s{1,}/)
104
+ end
105
+
106
+ # @return [Array<Topic>] ordered list of child nodes
107
+ def items
108
+ return @items unless order_index.any?
109
+ [].tap do |ary|
110
+ src = @items.dup
111
+ order_index.each do |i|
112
+ node = item(i)
113
+ ary << src.delete(node) if node
114
+ end
115
+ ary.concat(src)
116
+ end
117
+ end
118
+
119
+ def item(id)
120
+ @items.find{|n| n.id == id || n.id.end_with?(id) }
121
+ end
122
+
123
+ end
124
+
125
+ end
126
+ end
@@ -0,0 +1,34 @@
1
+ module Marko
2
+ module Model
3
+
4
+ # Tree node
5
+ module TreeNode
6
+ include Enumerable
7
+
8
+ # @return [TreeNode]
9
+ attr_reader :parent
10
+
11
+ def each(&block)
12
+ yield self
13
+ end
14
+
15
+ def add(item)
16
+ fail "#{self.class}##{__method__} must be overridden"
17
+ end
18
+
19
+ def root
20
+ parent ? parent.root : self
21
+ end
22
+
23
+ def root?
24
+ parent.nil?
25
+ end
26
+
27
+ def nesting_level
28
+ return 0 if root?
29
+ parent.nesting_level + 1
30
+ end
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,10 @@
1
+ require_relative 'model/tree_node'
2
+ require_relative 'model/topic'
3
+ require_relative 'model/markup'
4
+
5
+ module Marko
6
+
7
+ # Model
8
+ module Model
9
+ end
10
+ end
@@ -0,0 +1,28 @@
1
+ module Marko
2
+ module Parser
3
+
4
+ # Metadata parser
5
+ class Metadata
6
+ # @param text [String]
7
+ # @return [Hash]
8
+ def parse(text)
9
+ return {} if text&.empty?
10
+
11
+ text
12
+ .split(/[;,\n]/)
13
+ .map(&:strip)
14
+ .reject(&:empty?)
15
+ .map{ parse_attr(it) }
16
+ .to_h
17
+ end
18
+
19
+ protected
20
+
21
+ def parse_attr(text)
22
+ k, v = text.split(?:)
23
+ [k.strip.to_sym, v&.strip || true]
24
+ end
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,52 @@
1
+ require_relative 'topic'
2
+ require_relative '../errors'
3
+
4
+ module Marko
5
+ module Parser
6
+
7
+ # Source parser
8
+ class Source
9
+ def initialize
10
+ @topic = Topic.new
11
+ end
12
+
13
+ # @param markups [Array<Model::Markup>]
14
+ # @return [Array<Model::Topic, ParserError>]
15
+ def parse(markups)
16
+ results = markups.map do |e|
17
+ begin
18
+ @topic.parse(e)
19
+ rescue => ex
20
+ puts ex.full_message
21
+ Marko::ParserError.new(ex.message, e.filename, e.lineno, e.content)
22
+ end
23
+ end
24
+
25
+ errors = results.reject{ it.is_a?(Model::Topic) }
26
+ topics = results.select{ it.is_a?(Model::Topic) }
27
+
28
+ adjusted = []
29
+ topics.each do |e|
30
+ if e.markup_level == 1
31
+ adjusted << e
32
+ next
33
+ end
34
+
35
+ finder = proc{ it.markup_level == e.markup_level - 1 }
36
+ parent = adjusted.last&.select(&finder)&.last
37
+
38
+ unless parent
39
+ errors << Marko::ParserError.new('wrong header level',
40
+ e.markup_filename, e.markup_lineno, e.markup_content)
41
+ next
42
+ end
43
+
44
+ parent.add(e)
45
+ end
46
+
47
+ adjusted + errors
48
+ end
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,33 @@
1
+ require_relative '../model'
2
+ require_relative 'metadata'
3
+
4
+ module Marko
5
+ module Parser
6
+
7
+ # Topic parser
8
+ class Topic
9
+ def initialize
10
+ @metadata = Metadata.new
11
+ end
12
+
13
+ # @param markup [Model::Markup]
14
+ # @return [Model::Topic]
15
+ def parse(markup)
16
+ text = markup.content
17
+ first = text.lines.first
18
+ match = first.match(/\W+(.*)/)
19
+ title = match[1]&.strip || ''
20
+
21
+ rest = text.lines.drop(1).join
22
+ match = rest.match(/^({{([\s\S]*?)}})?(.*)?$/m)
23
+ metadata = match[2]&.strip || ''
24
+ content = match[3]&.strip || ''
25
+ metadata = @metadata.parse(metadata)
26
+
27
+ Model::Topic.new(title, content,
28
+ **{markup: markup}.merge(metadata))
29
+ end
30
+ end
31
+
32
+ end
33
+ end
data/lib/marko/parser.rb CHANGED
@@ -1,19 +1,8 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "gadgets"
4
-
5
- module Marko
6
-
7
- # The class for pasing content into TreeNode
8
- class Parser
9
- extend Pluggable
10
-
11
- # @param content [String] content to parse
12
- # @param source [String] content source
13
- # @return [Array<TreeNode>, Array<String>] parsed nodes, errors
14
- def call(content, source, &block)
15
- fail "the abstract method must be overriden"
16
- end
17
- end
18
-
19
- end
1
+ require_relative 'parser/metadata'
2
+ require_relative 'parser/topic'
3
+ require_relative 'parser/source'
4
+
5
+ module Marko
6
+ module Parser
7
+ end
8
+ end
@@ -0,0 +1,29 @@
1
+ require_relative 'topic'
2
+
3
+ module Marko
4
+ module Renderers
5
+
6
+ # Artifact renderer
7
+ class Artifact < Renderer
8
+ def initialize
9
+ @topic = Topic.new
10
+ end
11
+
12
+ # @param topic [Model::Topic]
13
+ def render(topic)
14
+ root = <<~STR
15
+ ---
16
+ \% #{topic.title}
17
+ \% #{topic[:author]}
18
+ \% #{Time.now.strftime('%Y-%b-%d')}
19
+ ...
20
+ STR
21
+
22
+ topic.to_a.drop(1)
23
+ .map{ @topic.render(it) }
24
+ .unshift(root)
25
+ .join(NN)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,37 @@
1
+ require_relative 'link'
2
+ require_relative 'nested_list'
3
+ require_relative 'nested_tree'
4
+
5
+ module Marko
6
+ module Renderers
7
+
8
+ # Content renderer
9
+ class Content < Renderer
10
+ def initialize
11
+ @link = Link.new
12
+ @list = NestedList.new
13
+ @tree = NestedTree.new
14
+ end
15
+
16
+ # @param topic [Model::Topic]
17
+ def render(topic)
18
+ String.new(topic.content).tap do |processed|
19
+ # links substitution
20
+ topic.links.uniq.each do |link|
21
+ ref = topic.root.find_by_id(link) || link
22
+ processed.gsub!("[[#{link}]]", @link.render(ref))
23
+ end
24
+
25
+ processed.gsub!(/@@list/, @list.render(topic)) \
26
+ if processed =~ /@@list/
27
+
28
+ processed.gsub!(/@@tree/, @tree.render(topic)) \
29
+ if processed =~ /@@tree/
30
+
31
+ processed.gsub!(/@@skip.*$/m, '') \
32
+ if processed =~ /@@skip/
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,19 @@
1
+ require_relative 'url'
2
+
3
+ module Marko
4
+ module Renderers
5
+
6
+ # Link renderer
7
+ class Link < Renderer
8
+ def initialize
9
+ @url = Url.new
10
+ end
11
+
12
+ def render(topic)
13
+ return "[#{topic}](#unknown)" unless topic.respond_to?(:title)
14
+ "[#{topic.title}](#{@url.render(topic.id)})"
15
+ end
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,34 @@
1
+ require_relative 'renderer'
2
+
3
+ module Marko
4
+ module Renderers
5
+
6
+ # Metadata renderer
7
+ class Metadata < Renderer
8
+ def render(topic)
9
+ metadata = topic.metadata
10
+ .except(:id, :parent, :origin, :order_index)
11
+ .then{ {id: topic.id}.merge(it) }
12
+
13
+ max_key_length = metadata.keys.map{ it.to_s.size }.max + 4
14
+ max_val_length = metadata.values.map{ it.to_s.size }.max
15
+ table_starter =
16
+ [ max_key_length, max_val_length
17
+ ].map{ ?- * it }
18
+ .join(?\s)
19
+
20
+ make_row = proc{|k, v|
21
+ "__#{k.to_s.capitalize}__".ljust(max_key_length) + ?\s + v.to_s
22
+ }
23
+
24
+ metadata
25
+ .map(&make_row)
26
+ .unshift(table_starter)
27
+ .unshift(": Metadata\n")
28
+ .push(table_starter)
29
+ .join(?\n)
30
+ .then{ "\n#{it}\n" }
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,21 @@
1
+ require_relative 'link'
2
+
3
+ module Marko
4
+ module Renderers
5
+
6
+ # Nested topics list renderer
7
+ class NestedList < Renderer
8
+ def initialize
9
+ @link = Link.new
10
+ end
11
+
12
+ # @param topic [Model::Topic]
13
+ def render(topic)
14
+ topic
15
+ .select{ it.parent == topic }
16
+ .map{"- #{@link.render(it)}"}
17
+ .join(?\n)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ require_relative 'link'
2
+
3
+ module Marko
4
+ module Renderers
5
+
6
+ # Nested topics tree renderer
7
+ class NestedTree < Renderer
8
+ def initialize
9
+ @link = Link.new
10
+ end
11
+
12
+ # @param topic [Model::Topic]
13
+ def render(topic)
14
+ level = topic.nesting_level + 1
15
+ mkindent = proc{ ' ' * (it.nesting_level - level) }
16
+ topic.drop(1)
17
+ .map{"#{mkindent.(it)}- #{@link.render(it)}"}
18
+ .join(?\n)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ module Marko
2
+ module Renderers
3
+
4
+ # Renderer interface
5
+ class Renderer
6
+ # @param model [Object]
7
+ # @return [String]
8
+ def render(model)
9
+ fail "#{self.class}##{__method__} must be overridden"
10
+ end
11
+
12
+ NL = ?\n
13
+ NN = "\n\n"
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ require_relative 'url'
2
+
3
+ module Marko
4
+ module Renderers
5
+
6
+ # Title renderer
7
+ class Title < Renderer
8
+ def render(topic)
9
+ head = ?# * topic.nesting_level
10
+ title = topic.title.empty? ? topic.id.split(/\./).last : topic.title
11
+ url = Url.new.render(topic.id)
12
+ "#{head} #{title} {#{url}}"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,24 @@
1
+ require_relative 'title'
2
+ require_relative 'metadata'
3
+ require_relative 'content'
4
+
5
+ module Marko
6
+ module Renderers
7
+
8
+ # Topic renderer
9
+ class Topic < Renderer
10
+ def initialize
11
+ @title = Title.new
12
+ @metadata = Metadata.new
13
+ @content = Content.new
14
+ end
15
+
16
+ def render(topic)
17
+ [ @title, @metadata, @content
18
+ ].map{ it.render(topic) }
19
+ .reject(&:empty?)
20
+ .join(?\n)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,16 @@
1
+ module Marko
2
+ module Renderers
3
+
4
+ # Url renderer
5
+ class Url < Renderer
6
+ def render(string)
7
+ string.downcase
8
+ .gsub(/\W{1,}/, ?-)
9
+ .sub(/^-/, '')
10
+ .sub(/-$/, '')
11
+ .then{ "##{it}" }
12
+ end
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ require_relative 'renderers/renderer'
2
+ require_relative 'renderers/url'
3
+ require_relative 'renderers/link'
4
+ require_relative 'renderers/title'
5
+ require_relative 'renderers/metadata'
6
+ require_relative 'renderers/nested_list'
7
+ require_relative 'renderers/nested_tree'
8
+ require_relative 'renderers/content'
9
+ require_relative 'renderers/topic'
10
+ require_relative 'renderers/artifact'
11
+
12
+ module Marko
13
+
14
+ # Renderers
15
+ module Renderers
16
+ end
17
+ end
@@ -0,0 +1,39 @@
1
+ require_relative 'model'
2
+
3
+ module Marko
4
+
5
+ # Source scanner
6
+ class Scanner
7
+ # @param text [String]
8
+ # @return [Array<Model::Markup>]
9
+ def scan(filename)
10
+ @source = filename
11
+ content = File.read(filename)
12
+ scan_topics(content)
13
+ .select{ it.content.start_with?(?#) }
14
+ end
15
+
16
+ protected
17
+
18
+ def scan_topics(text)
19
+ quote, buffer, lineno = false, [], 0
20
+ origin = proc{
21
+ Model::Markup.new(@source,
22
+ lineno - buffer.size + 1, buffer.join)
23
+ }
24
+
25
+ [].tap{|ary|
26
+ text.each_line do |line|
27
+ if line =~ /^#/ && !quote && buffer.any?
28
+ ary << origin.()
29
+ buffer.clear
30
+ end
31
+ lineno += 1
32
+ buffer << line
33
+ quote = !quote if line.start_with?('```')
34
+ end
35
+ ary << origin.() if buffer.any?
36
+ }
37
+ end
38
+ end
39
+ end