BIG mode
This commit is contained in:
parent
41680e45e1
commit
9ac12573a3
|
@ -0,0 +1,170 @@
|
||||||
|
#!/bin/ruby
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'io/console/size'
|
||||||
|
require 'optionparser'
|
||||||
|
require 'json'
|
||||||
|
require 'mmmd'
|
||||||
|
|
||||||
|
class ParserError < StandardError
|
||||||
|
end
|
||||||
|
|
||||||
|
class OptionNavigator
|
||||||
|
def initialize
|
||||||
|
@options = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Read a definition
|
||||||
|
# @param define [String]
|
||||||
|
def read_definition(define)
|
||||||
|
define.split(";").each do |part|
|
||||||
|
locstring, _, value = part.partition(":")
|
||||||
|
locstring = deconstruct(locstring.strip)
|
||||||
|
assign(locstring, JSON.parse(value))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :options
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def check_unescaped(str, index)
|
||||||
|
return true if index.zero?
|
||||||
|
|
||||||
|
reverse_index = index - 1
|
||||||
|
count = 0
|
||||||
|
while str[reverse_index] == "\\"
|
||||||
|
break if reverse_index.zero?
|
||||||
|
|
||||||
|
count += 1
|
||||||
|
reverse_index -= 1
|
||||||
|
end
|
||||||
|
count.even?
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_unescaped(str, pattern, index)
|
||||||
|
found = str.index(pattern, index)
|
||||||
|
return nil unless found
|
||||||
|
|
||||||
|
until check_unescaped(str, found)
|
||||||
|
index = found + 1
|
||||||
|
found = str.index(pattern, index)
|
||||||
|
return nil unless found
|
||||||
|
end
|
||||||
|
found
|
||||||
|
end
|
||||||
|
|
||||||
|
def deconstruct(locstring)
|
||||||
|
parts = []
|
||||||
|
buffer = ""
|
||||||
|
part = nil
|
||||||
|
until locstring.empty?
|
||||||
|
case locstring[0]
|
||||||
|
when '"'
|
||||||
|
raise ParserError, 'separator missing' unless buffer.empty?
|
||||||
|
|
||||||
|
closepart = find_unescaped(locstring, '"', 1)
|
||||||
|
raise ParserError, 'unclosed string' unless closepart
|
||||||
|
|
||||||
|
buffer = locstring[0..closepart]
|
||||||
|
part = buffer[1..-2]
|
||||||
|
locstring = locstring[closepart + 1..]
|
||||||
|
when '.'
|
||||||
|
parts.append(part)
|
||||||
|
buffer = ""
|
||||||
|
part = nil
|
||||||
|
locstring = locstring[1..]
|
||||||
|
when '['
|
||||||
|
raise ParserError, 'separator missing' unless buffer.empty?
|
||||||
|
|
||||||
|
closepart = find_unescaped(locstring, ']', 1)
|
||||||
|
raise ParserError, 'unclosed index' unless closepart
|
||||||
|
|
||||||
|
buffer = locstring[0..closepart]
|
||||||
|
part = locstring[1..-2].to_i
|
||||||
|
locstring = locstring.delete_prefix(buffer)
|
||||||
|
else
|
||||||
|
raise ParserError, 'separator missing' unless buffer.empty?
|
||||||
|
|
||||||
|
buffer = locstring.match(/^[\w_]+/)[0]
|
||||||
|
part = buffer.to_sym
|
||||||
|
locstring = locstring.delete_prefix(buffer)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
parts.append(part) if part
|
||||||
|
parts
|
||||||
|
end
|
||||||
|
|
||||||
|
def assign(keys, value)
|
||||||
|
current = @options
|
||||||
|
while keys.length > 1
|
||||||
|
current_key = keys.shift
|
||||||
|
unless current[current_key]
|
||||||
|
next_key = keys.first
|
||||||
|
case next_key
|
||||||
|
when Integer
|
||||||
|
current[current_key] = []
|
||||||
|
when String
|
||||||
|
current[current_key] = {}
|
||||||
|
when Symbol
|
||||||
|
current[current_key] = {}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
current = current[current_key]
|
||||||
|
end
|
||||||
|
current[keys.shift] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return unless $PROGRAM_NAME == __FILE__
|
||||||
|
|
||||||
|
options = {
|
||||||
|
include: [],
|
||||||
|
nav: OptionNavigator.new
|
||||||
|
}
|
||||||
|
parser = OptionParser.new do |opts|
|
||||||
|
opts.banner = "Usage: mmmdpp [OPTIONS] (input|-) (output|-)"
|
||||||
|
|
||||||
|
opts.on("-r", "--renderer [STRING]", String,
|
||||||
|
"Specify renderer to use for this document") do |renderer|
|
||||||
|
options[:renderer] = renderer
|
||||||
|
end
|
||||||
|
|
||||||
|
opts.on("-i", "--include [STRING]", String,
|
||||||
|
"Script to execute before rendering.\
|
||||||
|
May be specified multiple times.") do |inc|
|
||||||
|
options[:include].append(inc)
|
||||||
|
end
|
||||||
|
|
||||||
|
opts.on("-o", "--option [STRING]", String,
|
||||||
|
"Add option string. Can be repeated. Format: <key>: <JSON value>\n"\
|
||||||
|
"<key>: (<\"string\">|<symbol>|<[integer]>)"\
|
||||||
|
"[.(<\"string\"|<symbol>|<[integer]>[...]]\n"\
|
||||||
|
"Example: \"style\".\"CodeBlock\".literal.[0]: 50") do |value|
|
||||||
|
options[:nav].read_definition(value) if value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
parser.parse!
|
||||||
|
|
||||||
|
unless ARGV[1]
|
||||||
|
warn parser.help
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
Renderers = {
|
||||||
|
"HTML" => -> { ::MMMD::Renderers::HTML },
|
||||||
|
"Plainterm" => -> { ::MMMD::Renderers::Plainterm }
|
||||||
|
}.freeze
|
||||||
|
|
||||||
|
options[:include].each { |name| Kernel.load(name) }
|
||||||
|
renderer_opts = options[:nav].options
|
||||||
|
renderer_opts["hsize"] ||= IO.console_size[1]
|
||||||
|
input = ARGV[0] == "-" ? $stdin.read : File.read(ARGV[0])
|
||||||
|
output = ARGV[1] == "-" ? $stdout : File.open(ARGV[1], "w")
|
||||||
|
doc = MMMD.parse(input)
|
||||||
|
rclass = Renderers[options[:renderer] || "PlainTerm"]
|
||||||
|
raise StandardError, "unknown renderer: #{options[:renderer]}" unless rclass
|
||||||
|
|
||||||
|
renderer = rclass.call.new(doc, renderer_opts)
|
||||||
|
output.puts(renderer.render)
|
||||||
|
output.close
|
|
@ -6,6 +6,6 @@ module MMMD
|
||||||
# Renderers from Markdown to expected output format
|
# Renderers from Markdown to expected output format
|
||||||
module Renderers
|
module Renderers
|
||||||
autoload :HTML, 'renderers/html'
|
autoload :HTML, 'renderers/html'
|
||||||
autoload :PlainTerm, 'renderers/plainterm'
|
autoload :Plainterm, 'renderers/plainterm'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -229,7 +229,9 @@ module MMMD
|
||||||
text = if element.children.empty?
|
text = if element.children.empty?
|
||||||
element.content
|
element.content
|
||||||
else
|
else
|
||||||
literal = @mapping[element.class.name][:inline] || literaltext
|
literal = @mapping[element.class.name]
|
||||||
|
&.fetch(:inline, false) ||
|
||||||
|
literaltext
|
||||||
element.children.map do |child|
|
element.children.map do |child|
|
||||||
_render(child, options, inline: inline,
|
_render(child, options, inline: inline,
|
||||||
level: level,
|
level: level,
|
||||||
|
|
|
@ -396,7 +396,7 @@ module MMMD
|
||||||
@effect_priority = style_manager.effect_priority
|
@effect_priority = style_manager.effect_priority
|
||||||
@effects = @effect_priority.to_a.sort_by(&:last).map(&:first)
|
@effects = @effect_priority.to_a.sort_by(&:last).map(&:first)
|
||||||
@options = options
|
@options = options
|
||||||
@options[:hsize] ||= 80
|
@options["hsize"] ||= 80
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return rendered text
|
# Return rendered text
|
||||||
|
@ -430,7 +430,7 @@ module MMMD
|
||||||
element_style = @style[element.class.name]
|
element_style = @style[element.class.name]
|
||||||
return text unless element_style
|
return text unless element_style
|
||||||
|
|
||||||
hsize = 80 - (4 * level)
|
hsize = @options["hsize"] - (4 * level)
|
||||||
text = wordwrap(text, hsize) if modeswitch
|
text = wordwrap(text, hsize) if modeswitch
|
||||||
params = element_style.dup
|
params = element_style.dup
|
||||||
params[:hsize] = hsize
|
params[:hsize] = hsize
|
||||||
|
|
Loading…
Reference in New Issue