marko 0.1.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.
- checksums.yaml +4 -4
- data/.dockerignore +12 -0
- data/.rubocop.yml +45 -0
- data/CHANGELOG.md +54 -1
- data/Dockerfile +11 -0
- data/LICENSE.txt +21 -0
- data/README.md +24 -35
- data/Rakefile +2 -6
- data/exe/marko +4 -20
- data/lib/basic.rb +27 -0
- data/lib/marko/chain.rb +44 -0
- data/lib/marko/cli.rb +119 -121
- data/lib/marko/config.rb +30 -20
- data/lib/marko/errors.rb +38 -0
- data/lib/marko/model/markup.rb +33 -0
- data/lib/marko/model/topic.rb +126 -0
- data/lib/marko/model/tree_node.rb +34 -0
- data/lib/marko/model.rb +10 -0
- data/lib/marko/parser/metadata.rb +28 -0
- data/lib/marko/parser/source.rb +52 -0
- data/lib/marko/parser/topic.rb +33 -0
- data/lib/marko/parser.rb +8 -19
- data/lib/marko/renderers/artifact.rb +29 -0
- data/lib/marko/renderers/content.rb +37 -0
- data/lib/marko/renderers/link.rb +19 -0
- data/lib/marko/renderers/metadata.rb +34 -0
- data/lib/marko/renderers/nested_list.rb +21 -0
- data/lib/marko/renderers/nested_tree.rb +22 -0
- data/lib/marko/renderers/renderer.rb +17 -0
- data/lib/marko/renderers/title.rb +16 -0
- data/lib/marko/renderers/topic.rb +24 -0
- data/lib/marko/renderers/url.rb +16 -0
- data/lib/marko/renderers.rb +17 -0
- data/lib/marko/scanner.rb +39 -0
- data/lib/marko/tasks/assemble.rb +52 -0
- data/lib/marko/tasks/compile.rb +19 -0
- data/lib/marko/tasks/load.rb +13 -0
- data/lib/marko/tasks/parse.rb +27 -0
- data/lib/marko/tasks/scan.rb +18 -0
- data/lib/marko/tasks/validate.rb +32 -0
- data/lib/marko/tasks.rb +13 -0
- data/lib/marko/validators/lost_index.rb +21 -0
- data/lib/marko/validators/lost_links.rb +25 -0
- data/lib/marko/validators/lost_parent.rb +21 -0
- data/lib/marko/validators/non_unique_id.rb +23 -0
- data/lib/marko/validators.rb +11 -0
- data/lib/marko/version.rb +1 -3
- data/lib/marko.rb +11 -35
- metadata +47 -54
- data/Gemfile +0 -10
- data/Gemfile.lock +0 -22
- data/lib/assets/demo/README.md +0 -13
- data/lib/assets/demo/src/fr/assemble.md +0 -27
- data/lib/assets/demo/src/fr/compile.md +0 -25
- data/lib/assets/demo/src/fr/markup.md +0 -111
- data/lib/assets/demo/src/fr/storage.md +0 -16
- data/lib/assets/demo/src/fr/treenode.md +0 -34
- data/lib/assets/demo/src/index.md +0 -34
- data/lib/assets/demo/src/intro.md +0 -98
- data/lib/assets/demo/src/ui/cli.md +0 -26
- data/lib/assets/demo/src/ui/gem.md +0 -14
- data/lib/assets/demo/src/ur/uc.create.project.md +0 -8
- data/lib/assets/demo/src/ur/uc.general.flow.md +0 -14
- data/lib/assets/init/README.md +0 -61
- data/lib/assets/init/Rakefile +0 -100
- data/lib/assets/init/tt/artifact.md.tt +0 -3
- data/lib/marko/artifact.rb +0 -3
- data/lib/marko/assembler.rb +0 -82
- data/lib/marko/compiler.rb +0 -16
- data/lib/marko/gadgets/pluggable.rb +0 -55
- data/lib/marko/gadgets/sentry.rb +0 -66
- data/lib/marko/gadgets/service.rb +0 -52
- data/lib/marko/gadgets.rb +0 -3
- data/lib/marko/loader.rb +0 -38
- data/lib/marko/markup/compiler.rb +0 -36
- data/lib/marko/markup/decorator.rb +0 -65
- data/lib/marko/markup/macro.rb +0 -176
- data/lib/marko/markup/parser.rb +0 -122
- data/lib/marko/markup/storage.rb +0 -100
- data/lib/marko/markup/validator.rb +0 -101
- data/lib/marko/markup.rb +0 -24
- data/lib/marko/services/assemble.rb +0 -16
- data/lib/marko/services/compile.rb +0 -30
- data/lib/marko/services.rb +0 -2
- data/lib/marko/storage.rb +0 -36
- data/lib/marko/tree_node.rb +0 -128
- data/lib/marko/validator.rb +0 -19
- data/marko.gemspec +0 -44
data/lib/marko/gadgets/sentry.rb
DELETED
@@ -1,66 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Marko
|
4
|
-
# Factory module for guarding method arguments
|
5
|
-
#
|
6
|
-
# @example
|
7
|
-
#
|
8
|
-
# ShortString = Sentry.new(:str, "must be String[3..100]"
|
9
|
-
# ) {|v| v.is_a?(String) && v.size.between?(3,100)}
|
10
|
-
#
|
11
|
-
# ShortString.(str) => "str"
|
12
|
-
# ShortString.(nil) => ArgumentError ":str must be String[3..100]"
|
13
|
-
# ShortString.error(nil) => ":str must be String[3..100]"
|
14
|
-
# ShortString.error!(nil) => ArgumentError":str must be String[3..100]"
|
15
|
-
# ShortString.(nil, :name) => ArgumentError ":name must be String[3..100]"
|
16
|
-
# ShortString.(nil, 'John Doe', 'Ups!') => ArgumentError ":John Doe Ups!"
|
17
|
-
#
|
18
|
-
module Sentry
|
19
|
-
|
20
|
-
# creates a new Sentry
|
21
|
-
# @param key [Symbol|String] key for error message
|
22
|
-
# @param msg [String] error message
|
23
|
-
# @param blk [&block] validation block that should return boolen
|
24
|
-
# @return [Sentry] based on key, msg, and validation block
|
25
|
-
def self.new(key, msg, &blk)
|
26
|
-
# origin ;)
|
27
|
-
argerror = ->(val, msg, cnd) {
|
28
|
-
fail ArgumentError, msg unless cnd
|
29
|
-
val
|
30
|
-
}
|
31
|
-
Module.new do
|
32
|
-
include Sentry
|
33
|
-
extend self
|
34
|
-
|
35
|
-
@key = argerror.(key, ":key must be Symbol|String",
|
36
|
-
key.is_a?(String) || key.is_a?(Symbol))
|
37
|
-
@msg = argerror.(msg, ":msg must be String", msg.is_a?(String))
|
38
|
-
@blk = argerror.(blk, "&blk must be provided", block_given?)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
# returns error message for invalid :val
|
43
|
-
# @param val [Object] value to be validated
|
44
|
-
# @param key [Symbol|String] key for error message
|
45
|
-
# @param msg [String] optional error message
|
46
|
-
# @return [String] error message for invalid :val or nil when :val is valid
|
47
|
-
def error(val, key = @key, msg = @msg)
|
48
|
-
":#{key} #{msg}" unless @blk.(val)
|
49
|
-
end
|
50
|
-
|
51
|
-
# guards :val
|
52
|
-
# @todo @see Yard!
|
53
|
-
# @param val [Object] value to be returned if it valid
|
54
|
-
# @param key [Symbol|String] key for error message
|
55
|
-
# @param msg [String] optional error message
|
56
|
-
# @return [Object] valid :val or raieses ArgumentError when invalid
|
57
|
-
def error!(val, key = @key, msg = @msg)
|
58
|
-
return val if @blk.(val)
|
59
|
-
fail ArgumentError, ":#{key} #{msg}", caller[0..-1]
|
60
|
-
end
|
61
|
-
|
62
|
-
# @todo how do describe in Yard?
|
63
|
-
alias :call :error!
|
64
|
-
end
|
65
|
-
|
66
|
-
end
|
@@ -1,52 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Marko
|
4
|
-
# Service like ServiceObject, Command, etc.
|
5
|
-
#
|
6
|
-
# @example just call without parameters
|
7
|
-
# class BoldService < Service
|
8
|
-
# def call
|
9
|
-
# 42
|
10
|
-
# end
|
11
|
-
# end
|
12
|
-
#
|
13
|
-
# @example with parameters
|
14
|
-
# class PlusService < Service
|
15
|
-
# def initialize(a, b)
|
16
|
-
# @a, @b = a, b
|
17
|
-
# end
|
18
|
-
#
|
19
|
-
# def call
|
20
|
-
# 42
|
21
|
-
# end
|
22
|
-
# end
|
23
|
-
#
|
24
|
-
class Service
|
25
|
-
Failure = Class.new(StandardError)
|
26
|
-
|
27
|
-
class << self
|
28
|
-
def call(*args, **kwargs, &block)
|
29
|
-
new(*args, **kwargs, &block).call
|
30
|
-
end
|
31
|
-
|
32
|
-
def inherited(klass)
|
33
|
-
klass.const_set(:Failure, Class.new(klass::Failure))
|
34
|
-
super
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
private_class_method :new
|
39
|
-
|
40
|
-
def initialize(*args, **kwargs, &block)
|
41
|
-
@args = args
|
42
|
-
@kwargs = kwargs
|
43
|
-
@block = block
|
44
|
-
end
|
45
|
-
|
46
|
-
def call
|
47
|
-
fail Failure, "#{self.class.name}#call must be overrided"
|
48
|
-
end
|
49
|
-
|
50
|
-
end
|
51
|
-
|
52
|
-
end
|
data/lib/marko/gadgets.rb
DELETED
data/lib/marko/loader.rb
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "gadgets"
|
4
|
-
require_relative "config"
|
5
|
-
|
6
|
-
module Marko
|
7
|
-
|
8
|
-
# The strategy class for loading sources from repository
|
9
|
-
class Loader < Service
|
10
|
-
# load markup sources, parse and return TreeNode buffer
|
11
|
-
#
|
12
|
-
# @example
|
13
|
-
# fn = proc{|event, paylod| ... }
|
14
|
-
# buffer, errors = loader.(&fn)
|
15
|
-
# fail "Failed" if errors.any?
|
16
|
-
# # procced ...
|
17
|
-
#
|
18
|
-
# @param block [&block] aka proc {|event, payload| ..}
|
19
|
-
# @return [Array<TreeNode>, Array<String>] where
|
20
|
-
# the first item is buffer and the second is array<error>
|
21
|
-
def call
|
22
|
-
buffer = []
|
23
|
-
errors = []
|
24
|
-
parser = ParserPlug.plugged
|
25
|
-
storage = StoragePlug.plugged
|
26
|
-
storage.sources.each do |source|
|
27
|
-
@block.(:source, source) if @block
|
28
|
-
content = storage.content(source)
|
29
|
-
buff, errs = parser.(content, source, &@block)
|
30
|
-
buffer.concat(buff)
|
31
|
-
errors.concat(errs)
|
32
|
-
@block.(:errors, errs) if @block
|
33
|
-
end
|
34
|
-
[buffer.flatten, errors]
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "erb"
|
4
|
-
require_relative "../compiler"
|
5
|
-
require_relative "../config"
|
6
|
-
|
7
|
-
module Marko
|
8
|
-
module Markup
|
9
|
-
|
10
|
-
class Compiler < Marko::Compiler
|
11
|
-
|
12
|
-
# @see Marko::Compliler#call
|
13
|
-
def call(tree, template, filename, &block)
|
14
|
-
super(tree, template, filename, &block)
|
15
|
-
compile
|
16
|
-
end
|
17
|
-
|
18
|
-
protected
|
19
|
-
|
20
|
-
def compile
|
21
|
-
storage = StoragePlug.plugged
|
22
|
-
erbgen = ERB.new(@template, trim_mode: '-')
|
23
|
-
payload = @tree.map{|n| Decorator.new(n)}
|
24
|
-
storage.write(@filename){|f|
|
25
|
-
payload.each{|node|
|
26
|
-
@node = node
|
27
|
-
text = erbgen.result(binding)
|
28
|
-
f.puts text
|
29
|
-
}
|
30
|
-
}
|
31
|
-
@filename
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
end
|
36
|
-
end
|
@@ -1,65 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "delegate"
|
4
|
-
|
5
|
-
module Marko
|
6
|
-
module Markup
|
7
|
-
|
8
|
-
class Decorator < SimpleDelegator
|
9
|
-
|
10
|
-
def initialize(obj)
|
11
|
-
super(obj)
|
12
|
-
@macroproc = MacroProcPlug.plugged
|
13
|
-
end
|
14
|
-
|
15
|
-
def find_node(ref)
|
16
|
-
obj = super(ref)
|
17
|
-
return nil unless obj
|
18
|
-
self.class.new(obj)
|
19
|
-
end
|
20
|
-
|
21
|
-
def url
|
22
|
-
id.downcase
|
23
|
-
.gsub(/\W{1,}/, '-')
|
24
|
-
.gsub(/^-/, '')
|
25
|
-
.gsub(/-$/, '')
|
26
|
-
.then{"##{_1}"}
|
27
|
-
end
|
28
|
-
|
29
|
-
def ref
|
30
|
-
"[#{title}](#{url})"
|
31
|
-
end
|
32
|
-
|
33
|
-
def title
|
34
|
-
str = super
|
35
|
-
str = ".#{id.split(/\./).last}" if str.empty?
|
36
|
-
str
|
37
|
-
end
|
38
|
-
|
39
|
-
def header
|
40
|
-
return "% #{title}\n" if root?
|
41
|
-
"#{'#' * nesting_level} #{title.strip} {#{url}}\n"
|
42
|
-
end
|
43
|
-
|
44
|
-
def meta
|
45
|
-
hsh = super.dup
|
46
|
-
hsh[:id] = id # full id will be there
|
47
|
-
hsh.delete(:order_index)
|
48
|
-
hsh.delete(:parent)
|
49
|
-
hsh.delete(:origin)
|
50
|
-
len = hsh.keys.map(&:length).max
|
51
|
-
[].tap{|ary|
|
52
|
-
ary << "key | value"
|
53
|
-
ary << "--- | -----"
|
54
|
-
hsh.each{|k,v| ary << "#{k} | #{v}"}
|
55
|
-
}.join(?\n) + ?\n
|
56
|
-
end
|
57
|
-
|
58
|
-
def body
|
59
|
-
text = @macroproc.process(super, self)
|
60
|
-
text.strip + ?\n
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
end
|
65
|
-
end
|
data/lib/marko/markup/macro.rb
DELETED
@@ -1,176 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "decorator"
|
4
|
-
require_relative "../gadgets"
|
5
|
-
|
6
|
-
module Marko
|
7
|
-
module Markup
|
8
|
-
|
9
|
-
# @todo confusing circular depenencies
|
10
|
-
# List/Tree/Link Macros -> Decorator
|
11
|
-
# Decorator#body -> MacroProcessor
|
12
|
-
|
13
|
-
# Base class for macro substitutions
|
14
|
-
# @example
|
15
|
-
# class Todo < Macro
|
16
|
-
# @pattern = /@@todo[^\n]*\n/
|
17
|
-
# def subs(sample, obj = nil)
|
18
|
-
# # code that returns <substitution>
|
19
|
-
# # for the sample parameter
|
20
|
-
# end
|
21
|
-
# end
|
22
|
-
#
|
23
|
-
# text = "bla-bla-bla @@todo foo\nbla-bla-bla"
|
24
|
-
# Todo.new.gsub!(text)
|
25
|
-
# # => "bla-bla-bla <substitution for @@todo foo>"
|
26
|
-
#
|
27
|
-
class Macro
|
28
|
-
def self.pattern
|
29
|
-
@pattern
|
30
|
-
end
|
31
|
-
|
32
|
-
# @return [Regexp] pattern to process
|
33
|
-
def pattern
|
34
|
-
self.class.pattern
|
35
|
-
end
|
36
|
-
|
37
|
-
# substitutes all occured #pattern it text
|
38
|
-
def gsub!(text, obj = nil)
|
39
|
-
fn = subfn(text, obj)
|
40
|
-
text.scan(pattern).each(&fn)
|
41
|
-
end
|
42
|
-
|
43
|
-
protected
|
44
|
-
|
45
|
-
# build substitution for the text sample
|
46
|
-
# @param sample [String] sample for substitution
|
47
|
-
# @param obj [Object] might be used for substitution
|
48
|
-
# @return [String] substitution for text
|
49
|
-
def subs(sample, obj)
|
50
|
-
fail '#subs must be overridden'
|
51
|
-
end
|
52
|
-
|
53
|
-
def subfn(source, obj)
|
54
|
-
fn = proc{|source, obj, sample|
|
55
|
-
source.sub!(sample, subs(sample, obj))
|
56
|
-
}.curry
|
57
|
-
fn.(source, obj)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
class MLink < Macro
|
62
|
-
@pattern = /\[\[[\w\.]*\]\]/
|
63
|
-
|
64
|
-
# the macro requires obj [Decorator]
|
65
|
-
def subs(sample, node)
|
66
|
-
capture = /\[\[([\w\.]*)\]\]/
|
67
|
-
ref = sample
|
68
|
-
.match(capture)
|
69
|
-
.captures.first
|
70
|
-
obj = node.find_node(ref)
|
71
|
-
obj ? obj.ref : "[#{ref}](#lost-link)"
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
class MList < Macro
|
76
|
-
@pattern = /@@list/
|
77
|
-
|
78
|
-
def subs(sample, node)
|
79
|
-
# @todo require sentry Decorator
|
80
|
-
# MustbeTreeNode.(node)
|
81
|
-
node.items
|
82
|
-
.map{|n| d = Decorator.new(n); "- #{d.ref}" }
|
83
|
-
.join(?\n) + ?\n
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
class MTree < Macro
|
88
|
-
@pattern = /@@tree/
|
89
|
-
|
90
|
-
def subs(sample, node)
|
91
|
-
level = node.nesting_level + 1
|
92
|
-
node.to_a.drop(1)
|
93
|
-
.inject([]){|memo, n| memo << Decorator.new(n)}
|
94
|
-
.map{|n| "#{' ' * (n.nesting_level - level)}- #{n.ref}" }
|
95
|
-
.join(?\n) + ?\n
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
# @todo there is no sense to have such macro for releases
|
100
|
-
# instead it might be helpful to have some custom
|
101
|
-
# todo command that will create a file with nodes
|
102
|
-
# and list of todo for each node
|
103
|
-
#
|
104
|
-
# class MTodo < Macro
|
105
|
-
# @pattern = /@@todo[^\n]*\n/
|
106
|
-
#
|
107
|
-
# def subs(sample, obj = nil)
|
108
|
-
# capture = /@@todo([^\n]*)\n/
|
109
|
-
# payload = sample.match(capture)
|
110
|
-
# .captures
|
111
|
-
# .first
|
112
|
-
# .strip
|
113
|
-
# "__TODO__ #{payload}\n"
|
114
|
-
# end
|
115
|
-
# end
|
116
|
-
|
117
|
-
# inline @@todo macro
|
118
|
-
# @todo remove line with \n when it starts from @@todo
|
119
|
-
class MTodo < Macro
|
120
|
-
@pattern = /.*@@todo.*$/
|
121
|
-
|
122
|
-
def subs(sample, obj = nil)
|
123
|
-
cap = /(.*)@@todo.*$/
|
124
|
-
m = sample.match(cap)
|
125
|
-
m[1].strip + sample.gsub(pattern, '')
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
class MSkip < Macro
|
130
|
-
@pattern = /@@skip.*$/m
|
131
|
-
|
132
|
-
def subs(sample, obj = nil)
|
133
|
-
''
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
MustbeMacro = Sentry.new(:macro, "must be Macro"
|
138
|
-
) {|v| v.is_a? Macro }
|
139
|
-
|
140
|
-
# Macro processor
|
141
|
-
# @example
|
142
|
-
# processor = MacroProcessor.new
|
143
|
-
# processor << Toc.new
|
144
|
-
# processor << Todo.new
|
145
|
-
# processor.('bla bla @@todo, bla bla @@list')
|
146
|
-
# # => 'bla bla <substitute @@todo>, bla bla..'
|
147
|
-
class MacroProcessor
|
148
|
-
extend Marko::Pluggable
|
149
|
-
|
150
|
-
def initialize
|
151
|
-
@macros = {}
|
152
|
-
self.<<(MList.new)
|
153
|
-
self.<<(MTree.new)
|
154
|
-
self.<<(MLink.new)
|
155
|
-
self.<<(MTodo.new)
|
156
|
-
self.<<(MSkip.new)
|
157
|
-
end
|
158
|
-
|
159
|
-
def <<(macro)
|
160
|
-
MustbeMacro.(macro)
|
161
|
-
@macros[macro.pattern] = macro
|
162
|
-
macro
|
163
|
-
end
|
164
|
-
|
165
|
-
def process(text, obj)
|
166
|
-
fail 'No macro registered' unless @macros.any?
|
167
|
-
String.new(text).tap {|str|
|
168
|
-
@macros.values.each{|m| m.gsub!(str, obj) }
|
169
|
-
}
|
170
|
-
end
|
171
|
-
|
172
|
-
alias :call :process
|
173
|
-
end
|
174
|
-
|
175
|
-
end
|
176
|
-
end
|
data/lib/marko/markup/parser.rb
DELETED
@@ -1,122 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "../parser"
|
4
|
-
require_relative "../tree_node"
|
5
|
-
|
6
|
-
module Marko
|
7
|
-
module Markup
|
8
|
-
|
9
|
-
class Parser < Marko::Parser
|
10
|
-
|
11
|
-
# @see Parser#call
|
12
|
-
def call(content, source, &block)
|
13
|
-
@source = source
|
14
|
-
parse(content, &block)
|
15
|
-
end
|
16
|
-
|
17
|
-
protected
|
18
|
-
|
19
|
-
def parse(content, &block)
|
20
|
-
result, errors = [], [], []
|
21
|
-
scan_treenode(content)
|
22
|
-
.map{|origin|
|
23
|
-
begin
|
24
|
-
parse_treenode(origin.markup)
|
25
|
-
.tap{|n| n[:origin] = origin }
|
26
|
-
rescue => e
|
27
|
-
errmsg = "wrong markup #{origin} #{e.message}\n"
|
28
|
-
errors << errmsg
|
29
|
-
nil
|
30
|
-
end
|
31
|
-
}
|
32
|
-
.compact
|
33
|
-
.each{|node|
|
34
|
-
parent = find_parent(result, node[:origin].level)
|
35
|
-
unless parent
|
36
|
-
origin = node[:origin].to_s
|
37
|
-
errors << "wrong header #{origin}\n"
|
38
|
-
parent = result # it goes to the root!
|
39
|
-
end
|
40
|
-
parent << node
|
41
|
-
}
|
42
|
-
|
43
|
-
[result, errors]
|
44
|
-
end
|
45
|
-
|
46
|
-
def find_parent(ary, level)
|
47
|
-
return ary if level == 1
|
48
|
-
parent = ary.last
|
49
|
-
l = 1
|
50
|
-
while parent && (l+1) < level
|
51
|
-
parent = parent.last
|
52
|
-
l += 1
|
53
|
-
end
|
54
|
-
parent
|
55
|
-
end
|
56
|
-
|
57
|
-
Origin = Struct.new(:origin, :lineno, :markup) do
|
58
|
-
def header
|
59
|
-
@header ||= markup.lines.first
|
60
|
-
end
|
61
|
-
|
62
|
-
def level
|
63
|
-
@level ||= header.scan(/^#*/).first.size
|
64
|
-
end
|
65
|
-
|
66
|
-
def to_s
|
67
|
-
"#{origin}:#{lineno.to_s.rjust(2,'0')} >> #{header}".strip
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
# @return [Array<Origin>]
|
72
|
-
def scan_treenode(text)
|
73
|
-
quote, buffer, lineno = false, [], 0
|
74
|
-
origin = proc{
|
75
|
-
Origin.new(@source, lineno - buffer.size + 1, buffer.join(''))
|
76
|
-
}
|
77
|
-
|
78
|
-
[].tap{|ary|
|
79
|
-
text.each_line do |line|
|
80
|
-
if line =~ /^#/ && !quote && buffer.any?
|
81
|
-
ary << origin.()
|
82
|
-
buffer.clear
|
83
|
-
end
|
84
|
-
lineno += 1
|
85
|
-
buffer << line
|
86
|
-
quote = !quote if line.start_with?('```')
|
87
|
-
end
|
88
|
-
ary << origin.() if buffer.any?
|
89
|
-
}
|
90
|
-
end
|
91
|
-
|
92
|
-
def parse_treenode(text)
|
93
|
-
head, *tail = text.lines
|
94
|
-
m = head.match(/^(#+)(.*)/)
|
95
|
-
title = m[2]&.strip || ''
|
96
|
-
|
97
|
-
m = tail.join.match(/^({{([\s\S]*?)}})?(.*)?$/m)
|
98
|
-
meta = parse_metadata(m[2]&.strip || '')
|
99
|
-
body = m[3]&.strip || ''
|
100
|
-
|
101
|
-
TreeNode.new(title, body, **meta)
|
102
|
-
end
|
103
|
-
|
104
|
-
def parse_metadata(text)
|
105
|
-
return {} if text.empty?
|
106
|
-
atrbfn = method(:parse_attribute).to_proc
|
107
|
-
text
|
108
|
-
.split(/[;,\n]/)
|
109
|
-
.map(&:strip)
|
110
|
-
.reject(&:empty?)
|
111
|
-
.map(&atrbfn)
|
112
|
-
.to_h
|
113
|
-
end
|
114
|
-
|
115
|
-
def parse_attribute(text)
|
116
|
-
atr, val = text.split(?:)
|
117
|
-
[atr.strip.to_sym, val&.strip || 'true']
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
end
|
122
|
-
end
|
data/lib/marko/markup/storage.rb
DELETED
@@ -1,100 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require "psych"
|
3
|
-
require 'fileutils'
|
4
|
-
require "securerandom"
|
5
|
-
require_relative "../storage"
|
6
|
-
|
7
|
-
module Marko
|
8
|
-
module Markup
|
9
|
-
|
10
|
-
# File Storage
|
11
|
-
class Storage < Marko::Storage
|
12
|
-
include FileUtils
|
13
|
-
|
14
|
-
Failure = Class.new(StandardError)
|
15
|
-
|
16
|
-
# @see Storage#create
|
17
|
-
def punch(storage)
|
18
|
-
fail Failure, "Directory already exists" if Dir.exist?(storage)
|
19
|
-
Dir.mkdir(storage)
|
20
|
-
Dir.chdir(storage) {
|
21
|
-
marko_directories.each{|dir| Dir.mkdir dir }
|
22
|
-
src = File.join(Marko.root, 'lib', 'assets', 'init', '.')
|
23
|
-
cp_r src, Dir.pwd
|
24
|
-
}
|
25
|
-
end
|
26
|
-
|
27
|
-
# create demo project
|
28
|
-
def punch_demo
|
29
|
-
demo = File.join(Dir.home, DEMO)
|
30
|
-
unless Dir.exist?(demo)
|
31
|
-
punch(demo)
|
32
|
-
assets = File.join(Marko.root, 'lib', 'assets', 'demo', '.')
|
33
|
-
cp_r assets, demo
|
34
|
-
end
|
35
|
-
demo
|
36
|
-
end
|
37
|
-
|
38
|
-
# @see Storage#sources
|
39
|
-
def sources
|
40
|
-
marko_home!
|
41
|
-
ptrn = File.join(SOURCE, '**', '*.md')
|
42
|
-
Dir.glob ptrn
|
43
|
-
end
|
44
|
-
|
45
|
-
# @see Storage#content
|
46
|
-
def content(source)
|
47
|
-
marko_home!
|
48
|
-
File.read(source)
|
49
|
-
end
|
50
|
-
|
51
|
-
def write(filename, content = '')
|
52
|
-
backup(filename)
|
53
|
-
File.open(filename, 'w') do |f|
|
54
|
-
f.puts(content) unless content.empty?
|
55
|
-
yield(f) if block_given?
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def marko_home?
|
60
|
-
marko_directories.all?{ Dir.exist? _1 }
|
61
|
-
end
|
62
|
-
|
63
|
-
# @see Marko::Strorage#artifact
|
64
|
-
def artifact
|
65
|
-
return Psych.load_file(ARTIFACT).freeze if File.exist?(ARTIFACT)
|
66
|
-
art = Artifact.new(SecureRandom.uuid,
|
67
|
-
'Marko Artifact',
|
68
|
-
'tt/artifact.md.tt',
|
69
|
-
'tt/marko-artifact.md'
|
70
|
-
)
|
71
|
-
File.write(ARTIFACT, Psych.dump(art))
|
72
|
-
art.freeze
|
73
|
-
end
|
74
|
-
|
75
|
-
protected
|
76
|
-
|
77
|
-
ARTIFACT = 'marko.yml'.freeze
|
78
|
-
SOURCE = 'src'.freeze
|
79
|
-
BINARY = 'bin'.freeze
|
80
|
-
SAMPLE = 'tt'.freeze
|
81
|
-
ASSETS = File.join(BINARY, 'assets').freeze
|
82
|
-
DEMO = 'marko_demo'.freeze
|
83
|
-
|
84
|
-
def marko_directories
|
85
|
-
[SOURCE, BINARY, ASSETS, SAMPLE]
|
86
|
-
end
|
87
|
-
|
88
|
-
|
89
|
-
def marko_home!
|
90
|
-
fail Failure, "Marko project required!" unless marko_home?
|
91
|
-
end
|
92
|
-
|
93
|
-
def backup(filename)
|
94
|
-
cp(filename, filename + ?~) if File.exist?(filename)
|
95
|
-
end
|
96
|
-
|
97
|
-
end
|
98
|
-
|
99
|
-
end
|
100
|
-
end
|