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
|
||||
module Renderers
|
||||
autoload :HTML, 'renderers/html'
|
||||
autoload :PlainTerm, 'renderers/plainterm'
|
||||
autoload :Plainterm, 'renderers/plainterm'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -229,7 +229,9 @@ module MMMD
|
|||
text = if element.children.empty?
|
||||
element.content
|
||||
else
|
||||
literal = @mapping[element.class.name][:inline] || literaltext
|
||||
literal = @mapping[element.class.name]
|
||||
&.fetch(:inline, false) ||
|
||||
literaltext
|
||||
element.children.map do |child|
|
||||
_render(child, options, inline: inline,
|
||||
level: level,
|
||||
|
|
|
@ -396,7 +396,7 @@ module MMMD
|
|||
@effect_priority = style_manager.effect_priority
|
||||
@effects = @effect_priority.to_a.sort_by(&:last).map(&:first)
|
||||
@options = options
|
||||
@options[:hsize] ||= 80
|
||||
@options["hsize"] ||= 80
|
||||
end
|
||||
|
||||
# Return rendered text
|
||||
|
@ -430,7 +430,7 @@ module MMMD
|
|||
element_style = @style[element.class.name]
|
||||
return text unless element_style
|
||||
|
||||
hsize = 80 - (4 * level)
|
||||
hsize = @options["hsize"] - (4 * level)
|
||||
text = wordwrap(text, hsize) if modeswitch
|
||||
params = element_style.dup
|
||||
params[:hsize] = hsize
|
||||
|
|
Loading…
Reference in New Issue