Skip to content

Commit

Permalink
add an option to merge default configs
Browse files Browse the repository at this point in the history
  • Loading branch information
monkeyWzr committed Sep 11, 2024
1 parent d5bd093 commit faa89b9
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 40 deletions.
12 changes: 7 additions & 5 deletions lib/commonmarker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@ class << self
#
# text - A {String} of text
# options - A {Hash} of render, parse, and extension options to transform the text.
# merge_default_options - A {Boolean} representing whether or not merge the default for unspecified keys in render, parse, and extension options
#
# Returns the `parser` node.
def parse(text, options: Commonmarker::Config::OPTIONS)
def parse(text, options: Commonmarker::Config::OPTIONS, merge_default_options: false)
raise TypeError, "text must be a String; got a #{text.class}!" unless text.is_a?(String)
raise TypeError, "text must be UTF-8 encoded; got #{text.encoding}!" unless text.encoding.name == "UTF-8"
raise TypeError, "options must be a Hash; got a #{options.class}!" unless options.is_a?(Hash)

opts = Config.process_options(options)
opts = Config.process_options(options, merge_default_options:)

commonmark_parse(text, options: opts)
end
Expand All @@ -31,15 +32,16 @@ def parse(text, options: Commonmarker::Config::OPTIONS)
# text - A {String} of text
# options - A {Hash} of render, parse, and extension options to transform the text.
# plugins - A {Hash} of additional plugins.
# merge_default_options - A {Boolean} representing whether merge the default for unspecified keys in render, parse, and extension options
#
# Returns a {String} of converted HTML.
def to_html(text, options: Commonmarker::Config::OPTIONS, plugins: Commonmarker::Config::PLUGINS)
def to_html(text, options: Commonmarker::Config::OPTIONS, plugins: Commonmarker::Config::PLUGINS, merge_default_options: false)
raise TypeError, "text must be a String; got a #{text.class}!" unless text.is_a?(String)
raise TypeError, "text must be UTF-8 encoded; got #{text.encoding}!" unless text.encoding.name == "UTF-8"
raise TypeError, "options must be a Hash; got a #{options.class}!" unless options.is_a?(Hash)

opts = Config.process_options(options)
plugins = Config.process_plugins(plugins)
opts = Config.process_options(options, merge_default_options:)
plugins = Config.process_plugins(plugins, merge_default_options:)

commonmark_to_html(text, options: opts, plugins: plugins)
end
Expand Down
60 changes: 33 additions & 27 deletions lib/commonmarker/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ module Config
underline: false,
spoiler: false,
greentext: false,
},
format: [:html].freeze,
}.freeze,
format: [:html].freeze, # this option is currently not used
}.freeze

PLUGINS = {
Expand All @@ -59,55 +59,61 @@ module Config
class << self
include Commonmarker::Utils

def merged_with_defaults(options)
Commonmarker::Config::OPTIONS.merge(process_options(options))
end

def process_options(options)
{
def process_options(options, merge_default_options:)
ret = {
parse: process_parse_options(options[:parse]),
render: process_render_options(options[:render]),
extension: process_extension_options(options[:extension]),
}

if merge_default_options
ret.keys.each do |type|
ret[type] = Commonmarker::Config::OPTIONS[type].merge(ret[type])
end
end

ret
end

def process_plugins(plugins)
{
def process_plugins(plugins, merge_default_options:)
ret = {
syntax_highlighter: process_syntax_highlighter_plugin(plugins&.fetch(:syntax_highlighter, nil)),
}

if merge_default_options
ret.keys.each do |type|
ret[type] = Commonmarker::Config::PLUGINS[type].merge(ret[type]) if ret[type]
end
end

ret
end
end

[:parse, :render, :extension].each do |type|
define_singleton_method :"process_#{type}_options" do |option|
Commonmarker::Config::OPTIONS[type].each_with_object({}) do |(key, value), hash|
if option.nil? # option not provided, go for the default
hash[key] = value
next
end
return Commonmarker::Config::OPTIONS[type].dup if option.nil?
raise TypeError, "#{type} options must be a Hash; got a #{option.class}!" unless option.is_a?(Hash)

# option explicitly not included, remove it
next if option[key].nil?
option.keys.each_with_object({}) do |key, hash|
next unless Commonmarker::Config::OPTIONS[type].key?(key)

hash[key] = fetch_kv(option, key, value, type)
default_value = Commonmarker::Config::OPTIONS[type][key]
hash[key] = fetch_kv(option, key, default_value, type)
end
end
end

[:syntax_highlighter].each do |type|
define_singleton_method :"process_#{type}_plugin" do |plugin|
return if plugin.nil? # plugin explicitly nil, remove it
raise TypeError, "#{plugin} config must be a Hash; got a #{plugin.class}!" unless plugin.is_a?(Hash)

Commonmarker::Config::PLUGINS[type].each_with_object({}) do |(key, value), hash|
if plugin.nil? # option not provided, go for the default
hash[key] = value
next
end

# option explicitly not included, remove it
next if plugin[key].nil?
plugin.keys.each_with_object({}) do |key, hash|
next unless Commonmarker::Config::PLUGINS[type].key?(key)

hash[key] = fetch_kv(plugin, key, value, type)
default_value = Commonmarker::Config::PLUGINS[type][key]
hash[key] = fetch_kv(plugin, key, default_value, type)
end
end
end
Expand Down
14 changes: 8 additions & 6 deletions lib/commonmarker/node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,14 @@ def each
#
# options - A {Hash} of render, parse, and extension options to transform the text.
# plugins - A {Hash} of additional plugins.
# merge_default_options - A {Boolean} representing whether merge the default for unspecified keys in render, parse, and extension options
#
# Returns a {String} of HTML.
def to_html(options: Commonmarker::Config::OPTIONS, plugins: Commonmarker::Config::PLUGINS)
def to_html(options: Commonmarker::Config::OPTIONS, plugins: Commonmarker::Config::PLUGINS, merge_default_options: false)
raise TypeError, "options must be a Hash; got a #{options.class}!" unless options.is_a?(Hash)

opts = Config.process_options(options)
plugins = Config.process_plugins(plugins)
opts = Config.process_options(options, merge_default_options:)
plugins = Config.process_plugins(plugins, merge_default_options:)

node_to_html(options: opts, plugins: plugins).force_encoding("utf-8")
end
Expand All @@ -51,13 +52,14 @@ def to_html(options: Commonmarker::Config::OPTIONS, plugins: Commonmarker::Confi
#
# options - A {Symbol} or {Array of Symbol}s indicating the render options
# plugins - A {Hash} of additional plugins.
# merge_default_options - A {Boolean} representing whether merge the default for unspecified keys in render, parse, and extension options
#
# Returns a {String}.
def to_commonmark(options: Commonmarker::Config::OPTIONS, plugins: Commonmarker::Config::PLUGINS)
def to_commonmark(options: Commonmarker::Config::OPTIONS, plugins: Commonmarker::Config::PLUGINS, merge_default_options: false)
raise TypeError, "options must be a Hash; got a #{options.class}!" unless options.is_a?(Hash)

opts = Config.process_options(options)
plugins = Config.process_plugins(plugins)
opts = Config.process_options(options, merge_default_options:)
plugins = Config.process_plugins(plugins, merge_default_options:)

node_to_commonmark(options: opts, plugins: plugins).force_encoding("utf-8")
end
Expand Down
37 changes: 37 additions & 0 deletions test/config_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# frozen_string_literal: true

require "test_helper"

class ConfigTest < Minitest::Test
def test_process_options
user_config = {
parse: {
smart: true,
},
render: {
unsafe: false,
},
}
processed_config = Commonmarker::Config.process_options(user_config, merge_default_options: true)

expected_config = [:parse, :render, :extension].each_with_object({}) do |type, hash|
hash[type] = Commonmarker::Config::OPTIONS[type].merge(user_config[type] || {})
end

assert_equal(expected_config, processed_config)
end

def test_process_plugins
user_config = {
syntax_highlighter: {
path: './themes"',
},
}
processed_config = Commonmarker::Config.process_plugins(user_config, merge_default_options: true)
expected_config = [:syntax_highlighter].each_with_object({}) do |type, hash|
hash[type] = Commonmarker::Config::PLUGINS[type].merge(user_config[type])
end

assert_equal(expected_config, processed_config)
end
end
3 changes: 1 addition & 2 deletions test/spec_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class SpecTest < Minitest::Test

spec.each do |testcase|
define_method(:"test_to_html_example_#{testcase[:example]}") do
opts = {
options = {
render: {
unsafe: true,
gfm_quirks: true,
Expand All @@ -18,7 +18,6 @@ class SpecTest < Minitest::Test
end,
}

options = Commonmarker::Config.merged_with_defaults(opts)
options[:extension].delete(:header_ids) # this interefers with the spec.txt extension-less capability
options[:extension][:tasklist] = true
actual = Commonmarker.to_html(testcase[:markdown], options: options, plugins: nil).rstrip
Expand Down

0 comments on commit faa89b9

Please sign in to comment.