brandish 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +3 -0
- data/.rubocop.yml +41 -0
- data/.travis.yml +5 -0
- data/.yardopts +1 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +9 -0
- data/bin/brandish +16 -0
- data/brandish.gemspec +39 -0
- data/defaults/templates/html.liquid +39 -0
- data/lib/brandish.rb +51 -0
- data/lib/brandish/application.rb +163 -0
- data/lib/brandish/application/bench_command.rb +96 -0
- data/lib/brandish/application/build_command.rb +73 -0
- data/lib/brandish/application/initialize_command.rb +83 -0
- data/lib/brandish/application/serve_command.rb +150 -0
- data/lib/brandish/configure.rb +196 -0
- data/lib/brandish/configure/dsl.rb +135 -0
- data/lib/brandish/configure/dsl/form.rb +136 -0
- data/lib/brandish/configure/form.rb +32 -0
- data/lib/brandish/errors.rb +65 -0
- data/lib/brandish/execute.rb +26 -0
- data/lib/brandish/markup.rb +10 -0
- data/lib/brandish/markup/redcarpet.rb +14 -0
- data/lib/brandish/markup/redcarpet/format.rb +127 -0
- data/lib/brandish/markup/redcarpet/html.rb +95 -0
- data/lib/brandish/parser.rb +26 -0
- data/lib/brandish/parser/main.rb +237 -0
- data/lib/brandish/parser/node.rb +89 -0
- data/lib/brandish/parser/node/block.rb +98 -0
- data/lib/brandish/parser/node/command.rb +102 -0
- data/lib/brandish/parser/node/pair.rb +42 -0
- data/lib/brandish/parser/node/root.rb +83 -0
- data/lib/brandish/parser/node/string.rb +18 -0
- data/lib/brandish/parser/node/text.rb +114 -0
- data/lib/brandish/path_set.rb +163 -0
- data/lib/brandish/processor.rb +47 -0
- data/lib/brandish/processor/base.rb +144 -0
- data/lib/brandish/processor/block.rb +47 -0
- data/lib/brandish/processor/command.rb +47 -0
- data/lib/brandish/processor/context.rb +169 -0
- data/lib/brandish/processor/descend.rb +32 -0
- data/lib/brandish/processor/inline.rb +49 -0
- data/lib/brandish/processor/name_filter.rb +67 -0
- data/lib/brandish/processor/pair_filter.rb +96 -0
- data/lib/brandish/processors.rb +26 -0
- data/lib/brandish/processors/all.rb +19 -0
- data/lib/brandish/processors/all/comment.rb +29 -0
- data/lib/brandish/processors/all/embed.rb +56 -0
- data/lib/brandish/processors/all/if.rb +109 -0
- data/lib/brandish/processors/all/import.rb +95 -0
- data/lib/brandish/processors/all/literal.rb +42 -0
- data/lib/brandish/processors/all/verify.rb +47 -0
- data/lib/brandish/processors/common.rb +20 -0
- data/lib/brandish/processors/common/asset.rb +118 -0
- data/lib/brandish/processors/common/asset/paths.rb +93 -0
- data/lib/brandish/processors/common/group.rb +67 -0
- data/lib/brandish/processors/common/header.rb +86 -0
- data/lib/brandish/processors/common/markup.rb +127 -0
- data/lib/brandish/processors/common/output.rb +73 -0
- data/lib/brandish/processors/html.rb +18 -0
- data/lib/brandish/processors/html/group.rb +33 -0
- data/lib/brandish/processors/html/header.rb +46 -0
- data/lib/brandish/processors/html/markup.rb +131 -0
- data/lib/brandish/processors/html/output.rb +62 -0
- data/lib/brandish/processors/html/output/document.rb +127 -0
- data/lib/brandish/processors/html/script.rb +64 -0
- data/lib/brandish/processors/html/script/babel.rb +48 -0
- data/lib/brandish/processors/html/script/coffee.rb +47 -0
- data/lib/brandish/processors/html/script/vanilla.rb +45 -0
- data/lib/brandish/processors/html/style.rb +82 -0
- data/lib/brandish/processors/html/style/highlight.rb +89 -0
- data/lib/brandish/processors/html/style/sass.rb +64 -0
- data/lib/brandish/processors/html/style/vanilla.rb +71 -0
- data/lib/brandish/processors/latex.rb +15 -0
- data/lib/brandish/processors/latex/markup.rb +47 -0
- data/lib/brandish/scanner.rb +64 -0
- data/lib/brandish/version.rb +9 -0
- data/templates/initialize/Gemfile.tt +14 -0
- data/templates/initialize/brandish.config.rb.tt +49 -0
- metadata +296 -0
@@ -0,0 +1,135 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "pathname"
|
5
|
+
require "brandish/configure/dsl/form"
|
6
|
+
|
7
|
+
module Brandish
|
8
|
+
class Configure
|
9
|
+
# The DSL for configuration files for Brandish. This is used to construct
|
10
|
+
# a configure object.
|
11
|
+
class DSL
|
12
|
+
# Creates a new DSL object with the given configuration object, and
|
13
|
+
# yields it.
|
14
|
+
#
|
15
|
+
# @param configure [Configure] The configuration instance.
|
16
|
+
# @return [DSL]
|
17
|
+
def self.call(configure = Configure.new)
|
18
|
+
DSL.new(configure).tap { |t| yield t }
|
19
|
+
end
|
20
|
+
|
21
|
+
# Creates a DSL object with the given configuration object.
|
22
|
+
#
|
23
|
+
# @param configure [Configure]
|
24
|
+
def initialize(configure = Configure.new)
|
25
|
+
@configure = configure
|
26
|
+
end
|
27
|
+
|
28
|
+
# Sets a given option key to a value. The name is interned, making it
|
29
|
+
# a symbol.
|
30
|
+
#
|
31
|
+
# @param name [::Symbol, #intern] The name of the option key.
|
32
|
+
# @param value [::Object] The option value.
|
33
|
+
# @return [void]
|
34
|
+
def set(name, value)
|
35
|
+
@configure.options[name.intern] = value
|
36
|
+
end
|
37
|
+
alias_method :[]=, :set
|
38
|
+
|
39
|
+
# Retrives a given option key. The name is interned, making it a symbol.
|
40
|
+
#
|
41
|
+
# @param name [::Symbol, #intern] The name of the option key.
|
42
|
+
# @return [::Object] The option value.
|
43
|
+
def get(name)
|
44
|
+
@configure.options.fetch(name)
|
45
|
+
end
|
46
|
+
alias_method :[], :get
|
47
|
+
|
48
|
+
# Sets the root path of the Brandish project. This is where all of the
|
49
|
+
# important files are located. Very rarely should this be set to
|
50
|
+
# anything other than `"."`.
|
51
|
+
#
|
52
|
+
# @param root [::String, ::Pathname] The new root.
|
53
|
+
# @return [void]
|
54
|
+
def root=(root)
|
55
|
+
path = _expand_path(root, Dir.pwd)
|
56
|
+
self[:root] = path
|
57
|
+
end
|
58
|
+
|
59
|
+
# Retrieves the root path of the Brandish project. This is where all of
|
60
|
+
# the important files are located. Very rarely should this be set to
|
61
|
+
# anything other than `"."`.
|
62
|
+
#
|
63
|
+
# @return [::Pathname] The full path to the root.
|
64
|
+
def root
|
65
|
+
self[:root]
|
66
|
+
end
|
67
|
+
|
68
|
+
alias_method :root_path=, :root=
|
69
|
+
alias_method :root_path, :root
|
70
|
+
|
71
|
+
# Sets the output directory of the Brandish project. This is where all
|
72
|
+
# of the outputs are placed. This is normally `"./output"`.
|
73
|
+
#
|
74
|
+
# @param output [::String, ::Pathname] The path to the output directory.
|
75
|
+
# @return [void]
|
76
|
+
def output=(output)
|
77
|
+
path = _expand_path(output, root)
|
78
|
+
self[:output] = path
|
79
|
+
end
|
80
|
+
|
81
|
+
# Retrieves the output directory of the Brandish project. This is where all
|
82
|
+
# of the outputs are placed. This is normally `"./output"`.
|
83
|
+
#
|
84
|
+
# @return [::Pathname] The full path to the output.
|
85
|
+
def output
|
86
|
+
self[:output]
|
87
|
+
end
|
88
|
+
|
89
|
+
alias_method :output_path=, :output=
|
90
|
+
alias_method :output_path, :output
|
91
|
+
|
92
|
+
# Retrives the source directories of the Brandish project. This is where
|
93
|
+
# all of the sources are located. This is normally `"./source"`.
|
94
|
+
#
|
95
|
+
# @return [::Pathname] The full path to the sources.
|
96
|
+
def sources
|
97
|
+
self[:sources]
|
98
|
+
end
|
99
|
+
|
100
|
+
alias_method :source_paths, :sources
|
101
|
+
|
102
|
+
# Retrieves the template directory of the Brandish project. This is
|
103
|
+
# where all of the templates are placed. This is normally `"./template"`.
|
104
|
+
#
|
105
|
+
# @return [::Pathname] The full path to the template.
|
106
|
+
def templates
|
107
|
+
self[:templates]
|
108
|
+
end
|
109
|
+
|
110
|
+
alias_method :template_paths, :templates
|
111
|
+
|
112
|
+
# Creates a new form for the configuration object. This takes arguments
|
113
|
+
# and a block. The block is yielded the form instance.
|
114
|
+
#
|
115
|
+
# @see DSL::Form
|
116
|
+
# @see Configure::Form
|
117
|
+
# @param (see DSL::Form#instance)
|
118
|
+
# @yield [form]
|
119
|
+
# @return [void]
|
120
|
+
def form(*arguments)
|
121
|
+
instance = DSL::Form.new(*arguments)
|
122
|
+
yield instance
|
123
|
+
form = Configure::Form.new(*instance.data)
|
124
|
+
@configure.forms << form
|
125
|
+
form
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
def _expand_path(path, directory)
|
131
|
+
::Pathname.new(path).expand_path(::Pathname.new(directory))
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Brandish
|
5
|
+
class Configure
|
6
|
+
class DSL
|
7
|
+
# Constructs a form for use with the configure instance. A form is
|
8
|
+
# basically a format and name pair; different forms can have different
|
9
|
+
# entries, formats, and processors.
|
10
|
+
class Form
|
11
|
+
# The name of the form. This defaults to a random value. Do not
|
12
|
+
# rely on the name unless it is set.
|
13
|
+
#
|
14
|
+
# @return [::String]
|
15
|
+
attr_writer :name
|
16
|
+
|
17
|
+
# The entry for the form. This is the file that begins the parsing
|
18
|
+
# for the form.
|
19
|
+
#
|
20
|
+
# @return [::String]
|
21
|
+
attr_writer :entry
|
22
|
+
|
23
|
+
# Initialize the form DSL. This sets up the initial format and name,
|
24
|
+
# and intializes the instance variables.
|
25
|
+
#
|
26
|
+
# @param format [::Symbol] The format.
|
27
|
+
# @param name [::String, nil] The name of the form.
|
28
|
+
def initialize(format, name = nil)
|
29
|
+
@name = name || _generate_form_name
|
30
|
+
@format = format
|
31
|
+
@entry = "index.br"
|
32
|
+
@processors = []
|
33
|
+
end
|
34
|
+
|
35
|
+
# Adds a processor for use for the form. The order that this is called
|
36
|
+
# is important.
|
37
|
+
#
|
38
|
+
# @example
|
39
|
+
# config.form :html do |form|
|
40
|
+
# form.use :literal # Can expand to either "html:literal" or "all:literal"
|
41
|
+
# # in that order.
|
42
|
+
# form.use "all:literal" # Only expands to "all:literal"
|
43
|
+
# form.use Brandish::Processors::All::Literal # allowed too
|
44
|
+
# form.use do
|
45
|
+
# # also allowed
|
46
|
+
# end
|
47
|
+
# end
|
48
|
+
# @overload use(name, options = {})
|
49
|
+
# @param name [::String, ::Symbol, <::Symbol>, ::Class] The name of the
|
50
|
+
# processor. If the processor is a symbol, it is assumed to be the
|
51
|
+
# name of the processor; the format is guessed to be either
|
52
|
+
# the format of the form, or `:all`. If the processor is a string,
|
53
|
+
# and it contains a colon, it is assumed to be the name of the
|
54
|
+
# processor in the form `<format>:<name>`; if it doesn't contain
|
55
|
+
# the colon, it is treated as a symbol. If it is an array, it is
|
56
|
+
# assumed to be a pair containing the name of the process. If it
|
57
|
+
# is a class, it is assumed to be a processor, and is used directly.
|
58
|
+
# @param options [{::Object => ::Object}] The options for the
|
59
|
+
# processor. The allowed values for the options are dependant on
|
60
|
+
# the processor.
|
61
|
+
# @overload use(options = {}, &block)
|
62
|
+
# @param options [{::Object => ::Object}] The options for the
|
63
|
+
# processor. The allowed values for the options are dependant on
|
64
|
+
# the processor.
|
65
|
+
# @yield into a new class; this allows a processor to be defined
|
66
|
+
# without having a named class.
|
67
|
+
# @return [void]
|
68
|
+
def use(*arguments)
|
69
|
+
processor = options = nil
|
70
|
+
|
71
|
+
if block_given?
|
72
|
+
options = arguments[0]
|
73
|
+
processor = Class.new(Brandish::Processor::Base, &::Proc.new)
|
74
|
+
else
|
75
|
+
name = arguments[0]
|
76
|
+
options = arguments[1]
|
77
|
+
processor = _guess_processor_name(name)
|
78
|
+
end
|
79
|
+
|
80
|
+
@processors << [processor, options || {}]
|
81
|
+
end
|
82
|
+
alias_method :process, :use
|
83
|
+
alias_method :processor, :use
|
84
|
+
alias_method :define, :use
|
85
|
+
|
86
|
+
# The data from this form. This is used to create the actual form that
|
87
|
+
# is used in the configuration.
|
88
|
+
#
|
89
|
+
# @return [(::String, ::Symbol, ::String, <(::String, ::String, ::Hash)>]
|
90
|
+
def data
|
91
|
+
[@name, @format, @entry, @processors]
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def _generate_form_name
|
97
|
+
processor_names = @processors.map { |p| p[0..1].join(":") }.join(".")
|
98
|
+
"#{@format}.#{processor_names}"
|
99
|
+
end
|
100
|
+
|
101
|
+
def _guess_processor_name(name)
|
102
|
+
case name
|
103
|
+
when ::String then _guess_processor_name_string(name)
|
104
|
+
when ::Symbol then _guess_processor_name_symbol(name)
|
105
|
+
when ::Array then _guess_processor_name_array(name)
|
106
|
+
when ::Class then name # They're providing a class that can be used.
|
107
|
+
else
|
108
|
+
fail ::ArgumentError, "Unknown type given for name `#{name}`"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def _guess_processor_name_string(name)
|
113
|
+
parts = name.split(":")
|
114
|
+
|
115
|
+
case parts
|
116
|
+
when 1 then _guess_processor_name_symbol(name)
|
117
|
+
when 2 then _guess_processor_name_array(parts)
|
118
|
+
else
|
119
|
+
_guess_processor_name_array([parts[0], parts[1..-1].join(":")])
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def _guess_processor_name_symbol(name)
|
124
|
+
default = [@format, name]
|
125
|
+
allowed = [default, [:all, name]]
|
126
|
+
which = allowed.find { |a| Processor.all.key?(a) } || default
|
127
|
+
_guess_processor_name_array(which)
|
128
|
+
end
|
129
|
+
|
130
|
+
def _guess_processor_name_array(name)
|
131
|
+
name.map(&:intern)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "pry"
|
5
|
+
require "pry-rescue"
|
6
|
+
|
7
|
+
module Brandish
|
8
|
+
class Configure
|
9
|
+
# A form used for building.
|
10
|
+
Form = Struct.new(:name, :format, :entry, :processors) do
|
11
|
+
# Builds the form. This takes a configure object, and builds the
|
12
|
+
# form based on that.
|
13
|
+
#
|
14
|
+
# @see Configure#roots
|
15
|
+
# @see Processor::Context
|
16
|
+
# @see Processor
|
17
|
+
# @param configure [Configure] The configuration object for this build.
|
18
|
+
# @return [void]
|
19
|
+
def build(configure)
|
20
|
+
context = Processor::Context.new(configure, self)
|
21
|
+
root = configure.roots[configure.sources.find(entry)]
|
22
|
+
|
23
|
+
processors.each do |(processor, options)|
|
24
|
+
klass = processor.is_a?(::Array) ? Processor.all.fetch(processor) : processor
|
25
|
+
klass.new(context, options)
|
26
|
+
end
|
27
|
+
|
28
|
+
context.process(root)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Brandish
|
5
|
+
# An error that originates from the library for a library-specific reason.
|
6
|
+
# All libraries errors inherit from this class.
|
7
|
+
class Error < ::StandardError; end
|
8
|
+
|
9
|
+
# The file could not be found.
|
10
|
+
class NoFileError < Error; end
|
11
|
+
|
12
|
+
# An error that is created when there is a problem with scanning the
|
13
|
+
# document.
|
14
|
+
class ScanError < Error; end
|
15
|
+
|
16
|
+
# An error that occurs with setting up a processor. This has no location
|
17
|
+
# information because this error occurs independant of a document.
|
18
|
+
class ProcessorError < Error; end
|
19
|
+
|
20
|
+
# The processor has not been implemented.
|
21
|
+
class ProcessorNotImplementedError < ProcessorError; end
|
22
|
+
|
23
|
+
# This should never be used directly. This is an error that is tied to
|
24
|
+
# a location; as such, it provides an initalizer for providing a location.
|
25
|
+
#
|
26
|
+
# @api private
|
27
|
+
class LocationError < Error
|
28
|
+
# The location of the error in a file.
|
29
|
+
#
|
30
|
+
# @return [Location]
|
31
|
+
attr_reader :location
|
32
|
+
|
33
|
+
# Initialize the error with the given location and message.
|
34
|
+
def initialize(message, location = Location.default, bt = caller[1..-1])
|
35
|
+
@location = location
|
36
|
+
super(message)
|
37
|
+
set_backtrace(bt)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# An error that occurs when parsing. This is for unexpected tokens.
|
42
|
+
class ParseError < LocationError; end
|
43
|
+
|
44
|
+
# An error that is created when there is an issue interacting with a parser
|
45
|
+
# node.
|
46
|
+
class NodeError < ParseError; end
|
47
|
+
|
48
|
+
# An error occured during a build.
|
49
|
+
class BuildError < LocationError; end
|
50
|
+
|
51
|
+
# An error occurred while building in a processor. This includes location
|
52
|
+
# information from the original node.
|
53
|
+
class ProcessorBuildError < BuildError; end
|
54
|
+
|
55
|
+
# An error occurred while verifying the build. This includes location
|
56
|
+
# information for the invalid node.
|
57
|
+
class VerificationBuildError < ProcessorBuildError; end
|
58
|
+
|
59
|
+
# An error occurred with a part of a command or block node, in which a pair
|
60
|
+
# was not included.
|
61
|
+
class PairError < ProcessorBuildError; end
|
62
|
+
|
63
|
+
# The syntax used for the styling was invalid.
|
64
|
+
class ElementSyntaxError < ProcessorBuildError; end
|
65
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Brandish
|
5
|
+
# Executes a string of code in the instance of the class.
|
6
|
+
#
|
7
|
+
# @api private
|
8
|
+
class Execute
|
9
|
+
# Initialize the execution context.
|
10
|
+
#
|
11
|
+
# @param context [{::Symbol, ::String => ::Object}] The context. The keys
|
12
|
+
# are set as instance variables on the class, with the values being the
|
13
|
+
# instance variable's respective value.
|
14
|
+
def initialize(context)
|
15
|
+
context.each { |k, v| instance_variable_set(:"@#{k}", v) }
|
16
|
+
end
|
17
|
+
|
18
|
+
# Executes the given code in the context of the class.
|
19
|
+
#
|
20
|
+
# @param code [::String] The code to execute.
|
21
|
+
# @return [::Object]
|
22
|
+
def exec(code)
|
23
|
+
instance_exec(code)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Brandish
|
5
|
+
# Markup modules for use with the {Processors::Common::Markup} processor.
|
6
|
+
# This is only to provide certain integrations with Brandish.
|
7
|
+
module Markup
|
8
|
+
autoload :Redcarpet, "brandish/markup/redcarpet"
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "brandish/markup/redcarpet/html"
|
5
|
+
require "brandish/markup/redcarpet/format"
|
6
|
+
|
7
|
+
module Brandish
|
8
|
+
module Markup
|
9
|
+
# The Redcarpet format. This is used with the
|
10
|
+
# {Processors::Common::Markup} processor.
|
11
|
+
module Redcarpet
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "pry"
|
5
|
+
|
6
|
+
module Brandish
|
7
|
+
module Markup
|
8
|
+
module Redcarpet
|
9
|
+
# Formats text with Redcarpet. This is extracted into a seperate object
|
10
|
+
# in order to provide highlighting properties. This is
|
11
|
+
# format-independant.
|
12
|
+
#
|
13
|
+
# This class can take the following options:
|
14
|
+
#
|
15
|
+
# - All Markdown extension options that are listed on
|
16
|
+
# <https://github.com/vmg/redcarpet>, e.g. `:no_intra_emphasis`,
|
17
|
+
# `:tables`, etc.
|
18
|
+
# - All HTML formatter options that are listed on
|
19
|
+
# <https://github.com/vmg/redcarpet>, e.g. `:filter_html`,
|
20
|
+
# `:no_images`, etc.
|
21
|
+
# - `:highlight` - Optional. The highlighting engine to use. Can be
|
22
|
+
# one of `:rouge`, `:coderay`, `:pygments`, and `:none`. Remember to
|
23
|
+
# include the requisite highlighting engine in your Gemfile. They
|
24
|
+
# will automatically be required as needed. Defaults to `:none`.
|
25
|
+
class Format
|
26
|
+
# The options that are passed over to the markdown engine. These
|
27
|
+
# are extracted from the options that are passed to the markup engine.
|
28
|
+
#
|
29
|
+
# @return [<::Symbol>]
|
30
|
+
MARKDOWN_OPTIONS = %i(
|
31
|
+
no_intra_emphasis tables fenced_code_blocks autolink
|
32
|
+
disable_indented_code_blocks strikethrough lax_spacing
|
33
|
+
space_after_headers superscript underline quote footnotes
|
34
|
+
).freeze
|
35
|
+
|
36
|
+
# The default options for the markdown engine as passed by this markup
|
37
|
+
# engine.
|
38
|
+
#
|
39
|
+
# @return [{::Symbol => ::Object}]
|
40
|
+
MARKDOWN_DEFAULTS = {
|
41
|
+
fenced_code_blocks: true, tables: true, autolink: true,
|
42
|
+
strikethrough: true, superscript: true, underline: true,
|
43
|
+
footnotes: true, space_after_headers: true
|
44
|
+
}.freeze
|
45
|
+
|
46
|
+
# The options that are passed over to the formatting engine. These are
|
47
|
+
# Extracted from the options that are passed to the markup engine.
|
48
|
+
#
|
49
|
+
# @return [<::Symbol>]
|
50
|
+
FORMAT_OPTIONS = %i(
|
51
|
+
filter_html no_images no_links no_styles escape_html safe_links_only
|
52
|
+
with_toc_data hard_wrap xhtml prettify link_attributes highlight
|
53
|
+
).freeze
|
54
|
+
|
55
|
+
# The default options for the formatting engine as passed by this
|
56
|
+
# markup engine.
|
57
|
+
#
|
58
|
+
# @return [{::Symbol => ::Object}]
|
59
|
+
FORMAT_DEFAULTS = { highlight: :none }.freeze
|
60
|
+
|
61
|
+
# The highlighting engines that are supported by this markup engine.
|
62
|
+
# The key is the value passed by the `:highlight` option, and the value
|
63
|
+
# is the require file name. If the value is `nil`, no requirement
|
64
|
+
# is performed.
|
65
|
+
#
|
66
|
+
# @return [{::Symbol => ::Object, nil}]
|
67
|
+
HIGHLIGHTERS = {
|
68
|
+
rouge: "rouge", coderay: "coderay", pygments: "pygments",
|
69
|
+
none: nil
|
70
|
+
}.freeze
|
71
|
+
|
72
|
+
# The formating engines that can be used by this markup engine.
|
73
|
+
#
|
74
|
+
# @return [{::Symbol => ::Class}]
|
75
|
+
FORMATTERS = { html: HTML }.freeze
|
76
|
+
|
77
|
+
# Initialize the markup engine for Redcarpet. For the available
|
78
|
+
# options, see {Format}.
|
79
|
+
#
|
80
|
+
# @param options [::Hash]
|
81
|
+
def initialize(options)
|
82
|
+
@context = options.fetch(:context)
|
83
|
+
@format = options.fetch(:format)
|
84
|
+
@markdown_options = MARKDOWN_DEFAULTS
|
85
|
+
.merge(extract_options(MARKDOWN_OPTIONS, options))
|
86
|
+
@formatter_options = FORMAT_DEFAULTS
|
87
|
+
.merge(extract_options(FORMAT_OPTIONS, options))
|
88
|
+
@highlight = @formatter_options[:highlight]
|
89
|
+
load_highlighter
|
90
|
+
load_engine
|
91
|
+
end
|
92
|
+
|
93
|
+
# Renders the given text using the engine.
|
94
|
+
#
|
95
|
+
# @param string [::String] The value to render.
|
96
|
+
# @return [::String] The rendered value.
|
97
|
+
def render(string)
|
98
|
+
@engine.render(string)
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def load_highlighter
|
104
|
+
file = HIGHLIGHTERS.fetch(@highlight)
|
105
|
+
require file if file
|
106
|
+
rescue ::KeyError
|
107
|
+
fail ProcessorError, "Unknown highlighter `#{@highlight}`"
|
108
|
+
end
|
109
|
+
|
110
|
+
def load_engine
|
111
|
+
begin
|
112
|
+
formatter = FORMATTERS.fetch(@format)
|
113
|
+
rescue ::KeyError
|
114
|
+
fail ProcessorError, "Unsupported format `#{@format}`"
|
115
|
+
end
|
116
|
+
|
117
|
+
renderer = formatter.new(@context, @formatter_options)
|
118
|
+
@engine = ::Redcarpet::Markdown.new(renderer, @markdown_options)
|
119
|
+
end
|
120
|
+
|
121
|
+
def extract_options(keys, options)
|
122
|
+
keys.zip(options.values_at(*keys)).select!(&:last).to_h
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|