568 lines
19 KiB
Ruby
568 lines
19 KiB
Ruby
|
# -*- coding: utf-8; frozen_string_literal: true -*-
|
||
|
#
|
||
|
#--
|
||
|
# Copyright (C) 2009-2019 Thomas Leitner <t_leitner@gmx.at>
|
||
|
#
|
||
|
# This file is part of kramdown which is licensed under the MIT.
|
||
|
#++
|
||
|
#
|
||
|
|
||
|
require 'yaml'
|
||
|
|
||
|
module Kramdown
|
||
|
|
||
|
# This module defines all options that are used by parsers and/or converters as well as providing
|
||
|
# methods to deal with the options.
|
||
|
module Options
|
||
|
|
||
|
# Helper class introducing a boolean type for specifying boolean values (+true+ and +false+) as
|
||
|
# option types.
|
||
|
class Boolean
|
||
|
|
||
|
# Return +true+ if +other+ is either +true+ or +false+
|
||
|
def self.===(other)
|
||
|
FalseClass === other || TrueClass === other
|
||
|
end
|
||
|
|
||
|
end
|
||
|
|
||
|
# ----------------------------
|
||
|
# :section: Option definitions
|
||
|
#
|
||
|
# This sections describes the methods that can be used on the Options module.
|
||
|
# ----------------------------
|
||
|
|
||
|
# Struct class for storing the definition of an option.
|
||
|
Definition = Struct.new(:name, :type, :default, :desc, :validator)
|
||
|
|
||
|
# Allowed option types.
|
||
|
ALLOWED_TYPES = [String, Integer, Float, Symbol, Boolean, Object]
|
||
|
|
||
|
@options = {}
|
||
|
|
||
|
# Define a new option called +name+ (a Symbol) with the given +type+ (String, Integer, Float,
|
||
|
# Symbol, Boolean, Object), default value +default+ and the description +desc+. If a block is
|
||
|
# specified, it should validate the value and either raise an error or return a valid value.
|
||
|
#
|
||
|
# The type 'Object' should only be used for complex types for which none of the other types
|
||
|
# suffices. A block needs to be specified when using type 'Object' and it has to cope with
|
||
|
# a value given as string and as the opaque type.
|
||
|
def self.define(name, type, default, desc, &block)
|
||
|
name = name.to_sym
|
||
|
raise ArgumentError, "Option name #{name} is already used" if @options.key?(name)
|
||
|
raise ArgumentError, "Invalid option type #{type} specified" unless ALLOWED_TYPES.include?(type)
|
||
|
raise ArgumentError, "Invalid type for default value" if !(type === default) && !default.nil?
|
||
|
raise ArgumentError, "Missing validator block" if type == Object && block.nil?
|
||
|
@options[name] = Definition.new(name, type, default, desc, block)
|
||
|
end
|
||
|
|
||
|
# Return all option definitions.
|
||
|
def self.definitions
|
||
|
@options
|
||
|
end
|
||
|
|
||
|
# Return +true+ if an option called +name+ is defined.
|
||
|
def self.defined?(name)
|
||
|
@options.key?(name.to_sym)
|
||
|
end
|
||
|
|
||
|
# Return a Hash with the default values for all options.
|
||
|
def self.defaults
|
||
|
temp = {}
|
||
|
@options.each {|_n, o| temp[o.name] = o.default }
|
||
|
temp
|
||
|
end
|
||
|
|
||
|
# Merge the #defaults Hash with the *parsed* options from the given Hash, i.e. only valid option
|
||
|
# names are considered and their value is run through the #parse method.
|
||
|
def self.merge(hash)
|
||
|
temp = defaults
|
||
|
hash.each do |k, v|
|
||
|
k = k.to_sym
|
||
|
temp[k] = @options.key?(k) ? parse(k, v) : v
|
||
|
end
|
||
|
temp
|
||
|
end
|
||
|
|
||
|
# Parse the given value +data+ as if it was a value for the option +name+ and return the parsed
|
||
|
# value with the correct type.
|
||
|
#
|
||
|
# If +data+ already has the correct type, it is just returned. Otherwise it is converted to a
|
||
|
# String and then to the correct type.
|
||
|
def self.parse(name, data)
|
||
|
name = name.to_sym
|
||
|
raise ArgumentError, "No option named #{name} defined" unless @options.key?(name)
|
||
|
unless @options[name].type === data
|
||
|
data = data.to_s
|
||
|
data = if @options[name].type == String
|
||
|
data
|
||
|
elsif @options[name].type == Integer
|
||
|
Integer(data) rescue raise Kramdown::Error, "Invalid integer value for option '#{name}': '#{data}'"
|
||
|
elsif @options[name].type == Float
|
||
|
Float(data) rescue raise Kramdown::Error, "Invalid float value for option '#{name}': '#{data}'"
|
||
|
elsif @options[name].type == Symbol
|
||
|
str_to_sym(data)
|
||
|
elsif @options[name].type == Boolean
|
||
|
data.downcase.strip != 'false' && !data.empty?
|
||
|
end
|
||
|
end
|
||
|
data = @options[name].validator[data] if @options[name].validator
|
||
|
data
|
||
|
end
|
||
|
|
||
|
# Converts the given String +data+ into a Symbol or +nil+ with the
|
||
|
# following provisions:
|
||
|
#
|
||
|
# - A leading colon is stripped from the string.
|
||
|
# - An empty value or a value equal to "nil" results in +nil+.
|
||
|
def self.str_to_sym(data)
|
||
|
data = data.strip
|
||
|
data = data[1..-1] if data[0] == ':'
|
||
|
(data.empty? || data == 'nil' ? nil : data.to_sym)
|
||
|
end
|
||
|
|
||
|
# ----------------------------
|
||
|
# :section: Option Validators
|
||
|
#
|
||
|
# This sections contains all pre-defined option validators.
|
||
|
# ----------------------------
|
||
|
|
||
|
# Ensures that the option value +val+ for the option called +name+ is a valid array. The
|
||
|
# parameter +val+ can be
|
||
|
#
|
||
|
# - a comma separated string which is split into an array of values
|
||
|
# - or an array.
|
||
|
#
|
||
|
# Optionally, the array is checked for the correct size.
|
||
|
def self.simple_array_validator(val, name, size = nil)
|
||
|
if String === val
|
||
|
val = val.split(/,/)
|
||
|
elsif !(Array === val)
|
||
|
raise Kramdown::Error, "Invalid type #{val.class} for option #{name}"
|
||
|
end
|
||
|
if size && val.size != size
|
||
|
raise Kramdown::Error, "Option #{name} needs exactly #{size} values"
|
||
|
end
|
||
|
val
|
||
|
end
|
||
|
|
||
|
# Ensures that the option value +val+ for the option called +name+ is a valid hash. The
|
||
|
# parameter +val+ can be
|
||
|
#
|
||
|
# - a hash in YAML format
|
||
|
# - or a Ruby Hash object.
|
||
|
def self.simple_hash_validator(val, name)
|
||
|
if String === val
|
||
|
begin
|
||
|
val = YAML.safe_load(val)
|
||
|
rescue RuntimeError, ArgumentError, SyntaxError
|
||
|
raise Kramdown::Error, "Invalid YAML value for option #{name}"
|
||
|
end
|
||
|
end
|
||
|
raise Kramdown::Error, "Invalid type #{val.class} for option #{name}" unless Hash === val
|
||
|
val
|
||
|
end
|
||
|
|
||
|
# ----------------------------
|
||
|
# :section: Option Definitions
|
||
|
#
|
||
|
# This sections contains all option definitions that are used by the included
|
||
|
# parsers/converters.
|
||
|
# ----------------------------
|
||
|
|
||
|
define(:template, String, '', <<~EOF)
|
||
|
The name of an ERB template file that should be used to wrap the output
|
||
|
or the ERB template itself.
|
||
|
|
||
|
This is used to wrap the output in an environment so that the output can
|
||
|
be used as a stand-alone document. For example, an HTML template would
|
||
|
provide the needed header and body tags so that the whole output is a
|
||
|
valid HTML file. If no template is specified, the output will be just
|
||
|
the converted text.
|
||
|
|
||
|
When resolving the template file, the given template name is used first.
|
||
|
If such a file is not found, the converter extension (the same as the
|
||
|
converter name) is appended. If the file still cannot be found, the
|
||
|
templates name is interpreted as a template name that is provided by
|
||
|
kramdown (without the converter extension). If the file is still not
|
||
|
found, the template name is checked if it starts with 'string://' and if
|
||
|
it does, this prefix is removed and the rest is used as template
|
||
|
content.
|
||
|
|
||
|
kramdown provides a default template named 'document' for each converter.
|
||
|
|
||
|
Default: ''
|
||
|
Used by: all converters
|
||
|
EOF
|
||
|
|
||
|
define(:auto_ids, Boolean, true, <<~EOF)
|
||
|
Use automatic header ID generation
|
||
|
|
||
|
If this option is `true`, ID values for all headers are automatically
|
||
|
generated if no ID is explicitly specified.
|
||
|
|
||
|
Default: true
|
||
|
Used by: HTML/Latex converter
|
||
|
EOF
|
||
|
|
||
|
define(:auto_id_stripping, Boolean, false, <<~EOF)
|
||
|
Strip all formatting from header text for automatic ID generation
|
||
|
|
||
|
If this option is `true`, only the text elements of a header are used
|
||
|
for generating the ID later (in contrast to just using the raw header
|
||
|
text line).
|
||
|
|
||
|
This option will be removed in version 2.0 because this will be the
|
||
|
default then.
|
||
|
|
||
|
Default: false
|
||
|
Used by: kramdown parser
|
||
|
EOF
|
||
|
|
||
|
define(:auto_id_prefix, String, '', <<~EOF)
|
||
|
Prefix used for automatically generated header IDs
|
||
|
|
||
|
This option can be used to set a prefix for the automatically generated
|
||
|
header IDs so that there is no conflict when rendering multiple kramdown
|
||
|
documents into one output file separately. The prefix should only
|
||
|
contain characters that are valid in an ID!
|
||
|
|
||
|
Default: ''
|
||
|
Used by: HTML/Latex converter
|
||
|
EOF
|
||
|
|
||
|
define(:transliterated_header_ids, Boolean, false, <<~EOF)
|
||
|
Transliterate the header text before generating the ID
|
||
|
|
||
|
Only ASCII characters are used in headers IDs. This is not good for
|
||
|
languages with many non-ASCII characters. By enabling this option
|
||
|
the header text is transliterated to ASCII as good as possible so that
|
||
|
the resulting header ID is more useful.
|
||
|
|
||
|
The stringex library needs to be installed for this feature to work!
|
||
|
|
||
|
Default: false
|
||
|
Used by: HTML/Latex converter
|
||
|
EOF
|
||
|
|
||
|
define(:parse_block_html, Boolean, false, <<~EOF)
|
||
|
Process kramdown syntax in block HTML tags
|
||
|
|
||
|
If this option is `true`, the kramdown parser processes the content of
|
||
|
block HTML tags as text containing block-level elements. Since this is
|
||
|
not wanted normally, the default is `false`. It is normally better to
|
||
|
selectively enable kramdown processing via the markdown attribute.
|
||
|
|
||
|
Default: false
|
||
|
Used by: kramdown parser
|
||
|
EOF
|
||
|
|
||
|
define(:parse_span_html, Boolean, true, <<~EOF)
|
||
|
Process kramdown syntax in span HTML tags
|
||
|
|
||
|
If this option is `true`, the kramdown parser processes the content of
|
||
|
span HTML tags as text containing span-level elements.
|
||
|
|
||
|
Default: true
|
||
|
Used by: kramdown parser
|
||
|
EOF
|
||
|
|
||
|
define(:html_to_native, Boolean, false, <<~EOF)
|
||
|
Convert HTML elements to native elements
|
||
|
|
||
|
If this option is `true`, the parser converts HTML elements to native
|
||
|
elements. For example, when parsing `<em>hallo</em>` the emphasis tag
|
||
|
would normally be converted to an `:html` element with tag type `:em`.
|
||
|
If `html_to_native` is `true`, then the emphasis would be converted to a
|
||
|
native `:em` element.
|
||
|
|
||
|
This is useful for converters that cannot deal with HTML elements.
|
||
|
|
||
|
Default: false
|
||
|
Used by: kramdown parser
|
||
|
EOF
|
||
|
|
||
|
define(:link_defs, Object, {}, <<~EOF) do |val|
|
||
|
Pre-defines link definitions
|
||
|
|
||
|
This option can be used to pre-define link definitions. The value needs
|
||
|
to be a Hash where the keys are the link identifiers and the values are
|
||
|
two element Arrays with the link URL and the link title.
|
||
|
|
||
|
If the value is a String, it has to contain a valid YAML hash and the
|
||
|
hash has to follow the above guidelines.
|
||
|
|
||
|
Default: {}
|
||
|
Used by: kramdown parser
|
||
|
EOF
|
||
|
val = simple_hash_validator(val, :link_defs)
|
||
|
val.each do |_k, v|
|
||
|
if !(Array === v) || v.size > 2 || v.empty?
|
||
|
raise Kramdown::Error, "Invalid structure for hash value of option #{name}"
|
||
|
end
|
||
|
v << nil if v.size == 1
|
||
|
end
|
||
|
val
|
||
|
end
|
||
|
|
||
|
define(:footnote_nr, Integer, 1, <<~EOF)
|
||
|
The number of the first footnote
|
||
|
|
||
|
This option can be used to specify the number that is used for the first
|
||
|
footnote.
|
||
|
|
||
|
Default: 1
|
||
|
Used by: HTML converter
|
||
|
EOF
|
||
|
|
||
|
define(:entity_output, Symbol, :as_char, <<~EOF)
|
||
|
Defines how entities are output
|
||
|
|
||
|
The possible values are :as_input (entities are output in the same
|
||
|
form as found in the input), :numeric (entities are output in numeric
|
||
|
form), :symbolic (entities are output in symbolic form if possible) or
|
||
|
:as_char (entities are output as characters if possible, only available
|
||
|
on Ruby 1.9).
|
||
|
|
||
|
Default: :as_char
|
||
|
Used by: HTML converter, kramdown converter
|
||
|
EOF
|
||
|
|
||
|
define(:toc_levels, Object, (1..6).to_a, <<~EOF) do |val|
|
||
|
Defines the levels that are used for the table of contents
|
||
|
|
||
|
The individual levels can be specified by separating them with commas
|
||
|
(e.g. 1,2,3) or by using the range syntax (e.g. 1..3). Only the
|
||
|
specified levels are used for the table of contents.
|
||
|
|
||
|
Default: 1..6
|
||
|
Used by: HTML/Latex converter
|
||
|
EOF
|
||
|
case val
|
||
|
when String
|
||
|
if val =~ /^(\d)\.\.(\d)$/
|
||
|
val = Range.new($1.to_i, $2.to_i).to_a
|
||
|
elsif val =~ /^\d(?:,\d)*$/
|
||
|
val = val.split(/,/).map(&:to_i).uniq
|
||
|
else
|
||
|
raise Kramdown::Error, "Invalid syntax for option toc_levels"
|
||
|
end
|
||
|
when Array, Range
|
||
|
val = val.map(&:to_i).uniq
|
||
|
else
|
||
|
raise Kramdown::Error, "Invalid type #{val.class} for option toc_levels"
|
||
|
end
|
||
|
if val.any? {|i| !(1..6).cover?(i) }
|
||
|
raise Kramdown::Error, "Level numbers for option toc_levels have to be integers from 1 to 6"
|
||
|
end
|
||
|
val
|
||
|
end
|
||
|
|
||
|
define(:line_width, Integer, 72, <<~EOF)
|
||
|
Defines the line width to be used when outputting a document
|
||
|
|
||
|
Default: 72
|
||
|
Used by: kramdown converter
|
||
|
EOF
|
||
|
|
||
|
define(:latex_headers, Object, %w[section subsection subsubsection paragraph subparagraph subparagraph], <<~EOF) do |val|
|
||
|
Defines the LaTeX commands for different header levels
|
||
|
|
||
|
The commands for the header levels one to six can be specified by
|
||
|
separating them with commas.
|
||
|
|
||
|
Default: section,subsection,subsubsection,paragraph,subparagraph,subparagraph
|
||
|
Used by: Latex converter
|
||
|
EOF
|
||
|
simple_array_validator(val, :latex_headers, 6)
|
||
|
end
|
||
|
|
||
|
define(:smart_quotes, Object, %w[lsquo rsquo ldquo rdquo], <<~EOF) do |val|
|
||
|
Defines the HTML entity names or code points for smart quote output
|
||
|
|
||
|
The entities identified by entity name or code point that should be
|
||
|
used for, in order, a left single quote, a right single quote, a left
|
||
|
double and a right double quote are specified by separating them with
|
||
|
commas.
|
||
|
|
||
|
Default: lsquo,rsquo,ldquo,rdquo
|
||
|
Used by: HTML/Latex converter
|
||
|
EOF
|
||
|
val = simple_array_validator(val, :smart_quotes, 4)
|
||
|
val.map! {|v| Integer(v) rescue v }
|
||
|
val
|
||
|
end
|
||
|
|
||
|
define(:typographic_symbols, Object, {}, <<~EOF) do |val|
|
||
|
Defines a mapping from typographical symbol to output characters
|
||
|
|
||
|
Typographical symbols are normally output using their equivalent Unicode
|
||
|
codepoint. However, sometimes one wants to change the output, mostly to
|
||
|
fallback to a sequence of ASCII characters.
|
||
|
|
||
|
This option allows this by specifying a mapping from typographical
|
||
|
symbol to its output string. For example, the mapping {hellip: ...} would
|
||
|
output the standard ASCII representation of an ellipsis.
|
||
|
|
||
|
The available typographical symbol names are:
|
||
|
|
||
|
* hellip: ellipsis
|
||
|
* mdash: em-dash
|
||
|
* ndash: en-dash
|
||
|
* laquo: left guillemet
|
||
|
* raquo: right guillemet
|
||
|
* laquo_space: left guillemet followed by a space
|
||
|
* raquo_space: right guillemet preceeded by a space
|
||
|
|
||
|
Default: {}
|
||
|
Used by: HTML/Latex converter
|
||
|
EOF
|
||
|
val = simple_hash_validator(val, :typographic_symbols)
|
||
|
val.keys.each do |k|
|
||
|
val[k.kind_of?(String) ? str_to_sym(k) : k] = val.delete(k).to_s
|
||
|
end
|
||
|
val
|
||
|
end
|
||
|
|
||
|
define(:remove_block_html_tags, Boolean, true, <<~EOF)
|
||
|
Remove block HTML tags
|
||
|
|
||
|
If this option is `true`, the RemoveHtmlTags converter removes
|
||
|
block HTML tags.
|
||
|
|
||
|
Default: true
|
||
|
Used by: RemoveHtmlTags converter
|
||
|
EOF
|
||
|
|
||
|
define(:remove_span_html_tags, Boolean, false, <<~EOF)
|
||
|
Remove span HTML tags
|
||
|
|
||
|
If this option is `true`, the RemoveHtmlTags converter removes
|
||
|
span HTML tags.
|
||
|
|
||
|
Default: false
|
||
|
Used by: RemoveHtmlTags converter
|
||
|
EOF
|
||
|
|
||
|
define(:header_offset, Integer, 0, <<~EOF)
|
||
|
Sets the output offset for headers
|
||
|
|
||
|
If this option is c (may also be negative) then a header with level n
|
||
|
will be output as a header with level c+n. If c+n is lower than 1,
|
||
|
level 1 will be used. If c+n is greater than 6, level 6 will be used.
|
||
|
|
||
|
Default: 0
|
||
|
Used by: HTML converter, Kramdown converter, Latex converter
|
||
|
EOF
|
||
|
|
||
|
define(:syntax_highlighter, Symbol, :rouge, <<~EOF)
|
||
|
Set the syntax highlighter
|
||
|
|
||
|
Specifies the syntax highlighter that should be used for highlighting
|
||
|
code blocks and spans. If this option is set to +nil+, no syntax
|
||
|
highlighting is done.
|
||
|
|
||
|
Options for the syntax highlighter can be set with the
|
||
|
syntax_highlighter_opts configuration option.
|
||
|
|
||
|
Default: rouge
|
||
|
Used by: HTML/Latex converter
|
||
|
EOF
|
||
|
|
||
|
define(:syntax_highlighter_opts, Object, {}, <<~EOF) do |val|
|
||
|
Set the syntax highlighter options
|
||
|
|
||
|
Specifies options for the syntax highlighter set via the
|
||
|
syntax_highlighter configuration option.
|
||
|
|
||
|
The value needs to be a hash with key-value pairs that are understood by
|
||
|
the used syntax highlighter.
|
||
|
|
||
|
Default: {}
|
||
|
Used by: HTML/Latex converter
|
||
|
EOF
|
||
|
val = simple_hash_validator(val, :syntax_highlighter_opts)
|
||
|
val.keys.each do |k|
|
||
|
val[k.kind_of?(String) ? str_to_sym(k) : k] = val.delete(k)
|
||
|
end
|
||
|
val
|
||
|
end
|
||
|
|
||
|
define(:math_engine, Symbol, :mathjax, <<~EOF)
|
||
|
Set the math engine
|
||
|
|
||
|
Specifies the math engine that should be used for converting math
|
||
|
blocks/spans. If this option is set to +nil+, no math engine is used and
|
||
|
the math blocks/spans are output as is.
|
||
|
|
||
|
Options for the selected math engine can be set with the
|
||
|
math_engine_opts configuration option.
|
||
|
|
||
|
Default: mathjax
|
||
|
Used by: HTML converter
|
||
|
EOF
|
||
|
|
||
|
define(:math_engine_opts, Object, {}, <<~EOF) do |val|
|
||
|
Set the math engine options
|
||
|
|
||
|
Specifies options for the math engine set via the math_engine
|
||
|
configuration option.
|
||
|
|
||
|
The value needs to be a hash with key-value pairs that are understood by
|
||
|
the used math engine.
|
||
|
|
||
|
Default: {}
|
||
|
Used by: HTML converter
|
||
|
EOF
|
||
|
val = simple_hash_validator(val, :math_engine_opts)
|
||
|
val.keys.each do |k|
|
||
|
val[k.kind_of?(String) ? str_to_sym(k) : k] = val.delete(k)
|
||
|
end
|
||
|
val
|
||
|
end
|
||
|
|
||
|
define(:footnote_backlink, String, '↩', <<~EOF)
|
||
|
Defines the text that should be used for the footnote backlinks
|
||
|
|
||
|
The footnote backlink is just text, so any special HTML characters will
|
||
|
be escaped.
|
||
|
|
||
|
If the footnote backlint text is an empty string, no footnote backlinks
|
||
|
will be generated.
|
||
|
|
||
|
Default: '&8617;'
|
||
|
Used by: HTML converter
|
||
|
EOF
|
||
|
|
||
|
define(:footnote_backlink_inline, Boolean, false, <<~EOF)
|
||
|
Specifies whether the footnote backlink should always be inline
|
||
|
|
||
|
With the default of false the footnote backlink is placed at the end of
|
||
|
the last paragraph if there is one, or an extra paragraph with only the
|
||
|
footnote backlink is created.
|
||
|
|
||
|
Setting this option to true tries to place the footnote backlink in the
|
||
|
last, possibly nested paragraph or header. If this fails (e.g. in the
|
||
|
case of a table), an extra paragraph with only the footnote backlink is
|
||
|
created.
|
||
|
|
||
|
Default: false
|
||
|
Used by: HTML converter
|
||
|
EOF
|
||
|
|
||
|
define(:footnote_prefix, String, '', <<~EOF)
|
||
|
Prefix used for footnote IDs
|
||
|
|
||
|
This option can be used to set a prefix for footnote IDs. This is useful
|
||
|
when rendering multiple documents into the same output file to avoid
|
||
|
duplicate IDs. The prefix should only contain characters that are valid
|
||
|
in an ID!
|
||
|
|
||
|
Default: ''
|
||
|
Used by: HTML
|
||
|
EOF
|
||
|
|
||
|
end
|
||
|
|
||
|
end
|