nandoc 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +124 -0
- data/Rakefile +53 -0
- data/bin/nandoc +6 -0
- data/doc/CREDITS.md +6 -0
- data/doc/FAQ/why-not-wiki.md +20 -0
- data/doc/FAQ.md +68 -0
- data/doc/TODOs-and-BUGs.md +15 -0
- data/doc/bar/baz.md +4 -0
- data/doc/bar/bliff.md +8 -0
- data/doc/foo.md +5 -0
- data/doc/getting-started.rb +13 -0
- data/doc/svg/less-fonts.svg +21 -0
- data/lib/nandoc/commands/create-nandoc-site.rb +225 -0
- data/lib/nandoc/commands/diff.rb +279 -0
- data/lib/nandoc/config.rb +58 -0
- data/lib/nandoc/cri-hacks.rb +13 -0
- data/lib/nandoc/data-source.rb +239 -0
- data/lib/nandoc/filters.rb +661 -0
- data/lib/nandoc/helpers/menu-bouncy.rb +109 -0
- data/lib/nandoc/helpers/site-map.rb +157 -0
- data/lib/nandoc/helpers/top-nav.rb +47 -0
- data/lib/nandoc/helpers.rb +42 -0
- data/lib/nandoc/item-class-hacks.rb +57 -0
- data/lib/nandoc/nandoc.persistent.json +3 -0
- data/lib/nandoc/parse-readme.rb +95 -0
- data/lib/nandoc/spec-doc/mini-test/spec-instance-methods.rb +0 -0
- data/lib/nandoc/spec-doc/mini-test.rb +105 -0
- data/lib/nandoc/spec-doc/mock-prompt.rb +121 -0
- data/lib/nandoc/spec-doc/support-modules.rb +158 -0
- data/lib/nandoc/spec-doc/test-case-agent.rb +57 -0
- data/lib/nandoc/spec-doc/test-framework-dispatcher.rb +15 -0
- data/lib/nandoc/spec-doc/test-framework-proxy.rb +78 -0
- data/lib/nandoc/spec-doc.rb +46 -0
- data/lib/nandoc/support/diff-proxy.rb +113 -0
- data/lib/nandoc/support/orphanage.rb +77 -0
- data/lib/nandoc/support/path-tardo.rb +85 -0
- data/lib/nandoc/support/regexp-enhance.rb +76 -0
- data/lib/nandoc/support/site-diff.rb +46 -0
- data/lib/nandoc/support/site-merge.rb +62 -0
- data/lib/nandoc/support/site-methods.rb +69 -0
- data/lib/nandoc/support/stream-colorizer.rb +203 -0
- data/lib/nandoc/support-modules.rb +270 -0
- data/lib/nandoc/test/diff-to-string.rb +251 -0
- data/lib/nandoc/test/minitest-extlib.rb +53 -0
- data/lib/nandoc/treebis/NOGIT-DOCS/NEWS.md +5 -0
- data/lib/nandoc/treebis/NOGIT-README.md +65 -0
- data/lib/nandoc/treebis/nandoc.persistent.json +3 -0
- data/lib/nandoc.rb +48 -0
- data/proto/README.md +31 -0
- data/proto/default/Rakefile +1 -0
- data/proto/default/Rules +46 -0
- data/proto/default/config.yaml +57 -0
- data/proto/default/content/css/nanoc-dist-altered.css +213 -0
- data/proto/default/content/css/trollop-subset.css +116 -0
- data/proto/default/content/js/menu-bouncy.js +126 -0
- data/proto/default/content/stylesheet.css.diff +20 -0
- data/proto/default/content/vendor/jquery-1.3.js +4241 -0
- data/proto/default/content/vendor/jquery.easing.1.3.js +205 -0
- data/proto/default/layouts/default.html +70 -0
- data/proto/default/lib/default.orig.rb +2 -0
- data/proto/default/lib/default.rb +5 -0
- data/proto/default/treebis-task.rb +28 -0
- data/proto/misc/orphan-surrogate.md +6 -0
- data/test/test.rb +102 -0
- metadata +166 -0
@@ -0,0 +1,85 @@
|
|
1
|
+
module NanDoc
|
2
|
+
module PathTardo
|
3
|
+
#
|
4
|
+
# This is like a really basic xpath for data structures
|
5
|
+
# that are composed of arrays and hashes
|
6
|
+
#
|
7
|
+
def tardo_array_index str
|
8
|
+
/\A\[(-?\d+)\]\Z/ =~ str ? $1.to_i : nil
|
9
|
+
end
|
10
|
+
module_function :tardo_array_index
|
11
|
+
|
12
|
+
def hash_to_paths hash, prefix=nil
|
13
|
+
paths = []
|
14
|
+
hash.each do |k,v|
|
15
|
+
ch_prefix = [prefix, k].compact.join('/')
|
16
|
+
if v.kind_of?(Hash)
|
17
|
+
paths.concat hash_to_paths(v, ch_prefix)
|
18
|
+
else
|
19
|
+
paths.push ch_prefix
|
20
|
+
end
|
21
|
+
end
|
22
|
+
paths
|
23
|
+
end
|
24
|
+
|
25
|
+
def path_tardo hash_or_array, path_tardo, prefix = ''
|
26
|
+
/\A([^\/]+)(?:\/(.+))?\Z/ =~ path_tardo or
|
27
|
+
fail("no parse: #{path_tardo}")
|
28
|
+
head, tail = $1, $2
|
29
|
+
value = nil
|
30
|
+
found = nil
|
31
|
+
if idx = tardo_array_index(head)
|
32
|
+
if idx > 0 && idx >= hash_or_array.size
|
33
|
+
found = false
|
34
|
+
elsif idx < 0 && (idx*-1) > hash_or_array.size
|
35
|
+
found = false
|
36
|
+
else
|
37
|
+
found = true
|
38
|
+
value = hash_or_array.slice(idx)
|
39
|
+
end
|
40
|
+
else
|
41
|
+
if hash_or_array.key?(head)
|
42
|
+
found = true
|
43
|
+
value = hash_or_array[head]
|
44
|
+
else
|
45
|
+
found = false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
if ! found
|
49
|
+
Tardo::NotFound.new(prefix, head, hash_or_array)
|
50
|
+
elsif tail
|
51
|
+
local_full_path =
|
52
|
+
[ prefix.empty? ? nil : prefix, head ].compact.join('/')
|
53
|
+
path_tardo(value, tail, local_full_path)
|
54
|
+
else
|
55
|
+
Tardo::Found.new(value)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
module Tardo
|
59
|
+
class NotFound < Struct.new(:prefix, :head, :hash_or_array)
|
60
|
+
def found?; false end
|
61
|
+
def error_message
|
62
|
+
sub_msg =
|
63
|
+
if idx = PathTardo.tardo_array_index(head)
|
64
|
+
"#{idx} is a nonexistant offset"
|
65
|
+
else
|
66
|
+
"a \"#{head}\" key does not exist"
|
67
|
+
end
|
68
|
+
context_msg =
|
69
|
+
if prefix.empty?
|
70
|
+
nil
|
71
|
+
elsif hash_or_array.kind_of?(Array)
|
72
|
+
"in \"#{prefix}\" array,"
|
73
|
+
else
|
74
|
+
"in hash \"#{prefix}\","
|
75
|
+
end
|
76
|
+
msg = [context_msg, sub_msg].compact.join(' ')
|
77
|
+
msg
|
78
|
+
end
|
79
|
+
end
|
80
|
+
class Found < Struct.new(:value)
|
81
|
+
def found?; true end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module NanDoc
|
2
|
+
module RegexpEnhance
|
3
|
+
#
|
4
|
+
# Just gives pre-1.9 regexes the ability to have named captures
|
5
|
+
#
|
6
|
+
# Usage:
|
7
|
+
#
|
8
|
+
# re = /(foo*)bar(baz*)/
|
9
|
+
# RegexpEnhance.names(re, :the_foo, :the_baz)
|
10
|
+
# md = re.match("fooobarbazzzz")
|
11
|
+
# md[:the_foo] # => 'fooo'
|
12
|
+
#
|
13
|
+
class << self
|
14
|
+
def names re, *list
|
15
|
+
to(re) do |re|
|
16
|
+
re.names(*list)
|
17
|
+
end
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
def to re, &block
|
21
|
+
re.extend(self)
|
22
|
+
re.regexp_enhance_init
|
23
|
+
block.call(re)
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
def regexp_enhance_init
|
28
|
+
@names ||= []
|
29
|
+
class << self
|
30
|
+
alias_method :orig_match, :match
|
31
|
+
def match str
|
32
|
+
md = super
|
33
|
+
MatchData.to(md, self) if md
|
34
|
+
md
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
def names *list
|
39
|
+
if list.any?
|
40
|
+
@names = list
|
41
|
+
else
|
42
|
+
@names
|
43
|
+
end
|
44
|
+
end
|
45
|
+
module MatchData
|
46
|
+
class << self
|
47
|
+
def to(md, re)
|
48
|
+
md.extend self
|
49
|
+
md.match_data_enhanced_init(re)
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
def match_data_enhanced_init re
|
54
|
+
@names = re.names
|
55
|
+
class << self # @todo see if this doesn't break by moving defs out
|
56
|
+
alias_method :fetch_orig, :[]
|
57
|
+
attr_reader :names
|
58
|
+
def [](mixed)
|
59
|
+
return fetch_orig(mixed) unless mixed.kind_of?(Symbol)
|
60
|
+
fail("no such named capture: #{mixed.inspect}") unless
|
61
|
+
@names.include?(mixed)
|
62
|
+
offset = @names.index(mixed) + 1
|
63
|
+
fetch_orig offset
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# @return [Hash] of the named captures in the MatchData
|
70
|
+
#
|
71
|
+
def to_hash
|
72
|
+
Hash[ names.map{ |n| [n, self[n] ] } ]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/diff-proxy.rb'
|
2
|
+
|
3
|
+
module NanDoc
|
4
|
+
class SiteDiff
|
5
|
+
#
|
6
|
+
# This is a reworking of what's in site-merge (which doesn't really
|
7
|
+
# do anything useful attotw, if it still even works.)
|
8
|
+
# it's a wrapper around diff proxy that knows what folder(s) & file(s) we
|
9
|
+
# do and don't want to take into account when showing a sitewide diff.
|
10
|
+
# Specifically it's expected use is for comparing <my-site>/(* ~ output)
|
11
|
+
# with its protoype.
|
12
|
+
#
|
13
|
+
|
14
|
+
include NanDoc::Config::Accessors # file_utils()
|
15
|
+
include NanDoc::PathTardo # debug-ging e.g. hash_to_paths()
|
16
|
+
NanDoc.persistent_delegate_to(self) # empty_tmpdir()
|
17
|
+
include Treebis::DirAsHash # dir_as_hash()
|
18
|
+
include Treebis::Capture3 # capture3()
|
19
|
+
|
20
|
+
def initialize src_path, dest_path
|
21
|
+
@skip_these = %w(
|
22
|
+
output
|
23
|
+
tmp
|
24
|
+
**/*.orig.rb
|
25
|
+
**/*.diff
|
26
|
+
treebis-task.rb
|
27
|
+
)
|
28
|
+
@src_path, @dst_path = src_path, dest_path
|
29
|
+
end
|
30
|
+
|
31
|
+
def get_diff_object
|
32
|
+
file_utils.notice('comparing these:',
|
33
|
+
"#{@src_path.inspect} -> #{@dst_path.inspect}"
|
34
|
+
)
|
35
|
+
src_hash = dir_as_hash(@src_path, :skip => @skip_these)
|
36
|
+
dst_hash = dir_as_hash(@dst_path, :skip => @skip_these)
|
37
|
+
dir = empty_tmpdir('site-diff')
|
38
|
+
src_path = dir + '/a'
|
39
|
+
dst_path = dir + '/b'
|
40
|
+
hash_to_dir src_hash, src_path, file_utils
|
41
|
+
hash_to_dir dst_hash, dst_path, file_utils
|
42
|
+
diff = NanDoc::DiffProxy.diff(src_path, dst_path, :relative_to=>dir)
|
43
|
+
diff
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/diff-proxy.rb'
|
2
|
+
|
3
|
+
module NanDoc
|
4
|
+
class SiteMerge
|
5
|
+
include Treebis::DirAsHash
|
6
|
+
include Treebis::Capture3
|
7
|
+
NanDoc.persistent_delegate_to(self) # empty_tmpdir()
|
8
|
+
|
9
|
+
def initialize site_creator
|
10
|
+
@creator = site_creator
|
11
|
+
end
|
12
|
+
def site_merge opts, args
|
13
|
+
existing_site_path = args.first
|
14
|
+
task_abort("it's not exist: #{path}") unless
|
15
|
+
File.exist?(existing_site_path)
|
16
|
+
# nanoc writes to stdout so we do too, but here we want to write
|
17
|
+
# to stderr these notices, and write the diff to stdout
|
18
|
+
out, err, diff = capture3 do
|
19
|
+
$stdout.puts(
|
20
|
+
"#------------------ (this is for the merge hack:) -----------------")
|
21
|
+
@tmpdir = empty_tmpdir('ridiculous')
|
22
|
+
existing_subset_path = temp_site_subset(existing_site_path)
|
23
|
+
generated_site_path = temp_generated_site opts
|
24
|
+
order = [existing_subset_path, generated_site_path]
|
25
|
+
order.reverse! if opts[:merge_hack_reverse]
|
26
|
+
diff = DiffProxy.diff(order[0], order[1], :relative_to => @tmpdir)
|
27
|
+
# you could delete the tempdirs now. we will leave them there
|
28
|
+
diff
|
29
|
+
end
|
30
|
+
fail("hack failed: #{err.inspect}") unless err.empty?
|
31
|
+
$stderr.puts out # write out to err here
|
32
|
+
$stderr.puts diff.command
|
33
|
+
$stderr.puts <<-HERE.gsub(/^ +/,'')
|
34
|
+
#---------------- (above is stderr, below is stdout) ------------------
|
35
|
+
HERE
|
36
|
+
$stdout.puts diff.to_s
|
37
|
+
diff
|
38
|
+
end
|
39
|
+
private
|
40
|
+
def temp_site_subset path
|
41
|
+
subset_in_memory = dir_as_hash(path, :skip=>['output'])
|
42
|
+
@file_utils = NanDoc::Config.file_utils
|
43
|
+
subset_on_disk = @tmpdir+'/user-site'
|
44
|
+
hash_to_dir(subset_in_memory, subset_on_disk, @file_utils)
|
45
|
+
subset_on_disk
|
46
|
+
end
|
47
|
+
def temp_generated_site opts
|
48
|
+
put_it_here = @tmpdir+'/generated-site'
|
49
|
+
chops = opts.dup
|
50
|
+
chops.delete(:datasource)
|
51
|
+
@creator.run(chops, [put_it_here], :_merge=>false)
|
52
|
+
remove_output_directory(put_it_here)
|
53
|
+
put_it_here
|
54
|
+
end
|
55
|
+
def remove_output_directory put_it_here
|
56
|
+
dir = put_it_here + '/output'
|
57
|
+
fail("fail") unless File.directory?(dir)
|
58
|
+
fail("fail") unless Dir[dir+'/*'].empty?
|
59
|
+
@file_utils.remove_entry_secure(dir)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module NanDoc
|
2
|
+
module SiteMethods
|
3
|
+
|
4
|
+
def deduce_site_path_or_fail args
|
5
|
+
if args.any?
|
6
|
+
deduce_site_path_from_args args
|
7
|
+
else
|
8
|
+
deduce_site_path_from_persistent_data
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def config_path_for_site_path path
|
15
|
+
path + '/config.yaml'
|
16
|
+
end
|
17
|
+
|
18
|
+
def deduce_site_path_from_args args
|
19
|
+
if File.exist?(args.first)
|
20
|
+
path = args.first
|
21
|
+
unless path == persistent_get('last_site_path')
|
22
|
+
persistent_set('last_site_path', path)
|
23
|
+
end
|
24
|
+
path
|
25
|
+
else
|
26
|
+
task_abort <<-D.gsub(/^ */,'')
|
27
|
+
site path not found: #{args.first.inspect}
|
28
|
+
usage: #{usage}
|
29
|
+
#{invite_to_more_command_help}
|
30
|
+
D
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def deduce_site_path_from_persistent_data
|
35
|
+
if path = persistent_get('last_site_path')
|
36
|
+
if File.exist?(path)
|
37
|
+
path
|
38
|
+
else
|
39
|
+
persistent_set('last_site_path',false)
|
40
|
+
task_abort <<-D.gsub(/^ */,'')
|
41
|
+
previous site path is stale (#{path.inspect}) and no site provided
|
42
|
+
usage: #{usage}
|
43
|
+
#{invite_to_more_command_help}
|
44
|
+
D
|
45
|
+
end
|
46
|
+
else
|
47
|
+
task_abort(
|
48
|
+
'no site path provided and no site path in persistent data file '<<
|
49
|
+
"(#{NanDoc.dotfile_path})\n"<<
|
50
|
+
<<-D.gsub(/^ */,'')
|
51
|
+
usage: #{usage}
|
52
|
+
#{invite_to_more_command_help}
|
53
|
+
D
|
54
|
+
)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
#
|
59
|
+
# you just get the raw file data tree, it's not merged in with any
|
60
|
+
# DEFAULT_CONFIG stuff or anything
|
61
|
+
#
|
62
|
+
def parse_config_for_site_path path
|
63
|
+
config_path = config_path_for_site_path( path )
|
64
|
+
task_abort("config file for app not found: #{config_path}") unless
|
65
|
+
File.exist?(config_path)
|
66
|
+
YAML.load_file config_path
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,203 @@
|
|
1
|
+
module NanDoc
|
2
|
+
class StreamColorizer
|
3
|
+
module RuleList; end
|
4
|
+
module SecretParent; end
|
5
|
+
include RuleList;
|
6
|
+
def dup
|
7
|
+
other = self.class.new
|
8
|
+
other.stylesheet = stylesheet.dup
|
9
|
+
other.rule_list = rule_list.dup
|
10
|
+
other.state_set = state_set.dup
|
11
|
+
other
|
12
|
+
end
|
13
|
+
def initialize(*a, &b)
|
14
|
+
rule_list_init
|
15
|
+
@stylesheet = {}
|
16
|
+
if a.any? || block_given?
|
17
|
+
merge(*a, &b)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
attr_writer :rule_list
|
21
|
+
def filter string, out
|
22
|
+
found = rule_list.detect{ |x| x.match(string) }
|
23
|
+
use_state = found ? found.state : :plain
|
24
|
+
state = get_state_or_fail(use_state)
|
25
|
+
string = string.dup
|
26
|
+
while next_state_name = state.process(string, out)
|
27
|
+
state = get_state_or_fail(next_state_name)
|
28
|
+
end
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
def merge(&block)
|
32
|
+
yield(self) if block_given?
|
33
|
+
self
|
34
|
+
end
|
35
|
+
def spawn(*a, &b)
|
36
|
+
other = dup
|
37
|
+
other.merge(*a, &b)
|
38
|
+
other
|
39
|
+
end
|
40
|
+
attr_writer :state_set
|
41
|
+
attr_accessor :stylesheet
|
42
|
+
def stylesheet_merge other
|
43
|
+
@stylesheet.merge!(other)
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# api private classes:
|
48
|
+
#
|
49
|
+
|
50
|
+
module RuleList
|
51
|
+
def rule_list_init
|
52
|
+
@rule_list = []
|
53
|
+
@state_set = {}
|
54
|
+
end
|
55
|
+
def add_regex_rule re, opts
|
56
|
+
fail("no") unless opts[:state]
|
57
|
+
@rule_list.push RegexRule.make(re, opts[:state], {})
|
58
|
+
end
|
59
|
+
def add_regex_rule_neg re, opts
|
60
|
+
fail("no") unless opts[:state]
|
61
|
+
@rule_list.push RegexRule.make(re, opts[:state], {:neg=>true})
|
62
|
+
end
|
63
|
+
def define_state name, &block
|
64
|
+
state = State.new(self, name, &block)
|
65
|
+
fail("no") if @state_set.key?(state.name)
|
66
|
+
@state_set[state.name] = state
|
67
|
+
end
|
68
|
+
def get_state_or_fail name
|
69
|
+
state = @state_set[name]
|
70
|
+
state or fail("no such state: #{name.inspect}")
|
71
|
+
state
|
72
|
+
end
|
73
|
+
attr_reader :rule_list
|
74
|
+
def when(re_or_symbol, opts=nil, &block)
|
75
|
+
if re_or_symbol.kind_of?(Regexp) && Hash===opts && ! block_given?
|
76
|
+
add_regex_rule(re_or_symbol, opts)
|
77
|
+
elsif re_or_symbol.kind_of?(Symbol) && opts.nil? && block_given?
|
78
|
+
define_state(re_or_symbol, &block)
|
79
|
+
else
|
80
|
+
fail("unrecongized signature: `#{self.class}#when("<<
|
81
|
+
"[#{re_or_symbol.class}],[#{opts.class}],[#{block.class}])")
|
82
|
+
end
|
83
|
+
end
|
84
|
+
def when_not re, opts
|
85
|
+
add_regex_rule_neg re, opts
|
86
|
+
end
|
87
|
+
attr_reader :state_set
|
88
|
+
end
|
89
|
+
class RegexRule
|
90
|
+
class << self
|
91
|
+
def make regex, state, opts
|
92
|
+
if opts[:neg]
|
93
|
+
RegexRuleNeg.new(regex, state)
|
94
|
+
else
|
95
|
+
RegexRule.new(regex, state)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
def initialize regex, state
|
100
|
+
@regex = regex
|
101
|
+
@state = state or fail('no')
|
102
|
+
end
|
103
|
+
attr_reader :regex, :state
|
104
|
+
def match str
|
105
|
+
@regex =~ str
|
106
|
+
end
|
107
|
+
end
|
108
|
+
class RegexRuleNeg < RegexRule
|
109
|
+
def match str
|
110
|
+
! super
|
111
|
+
end
|
112
|
+
end
|
113
|
+
class State
|
114
|
+
include RuleList, Treebis::Colorize, NanDoc::SecretParent
|
115
|
+
def initialize parent, name, &block
|
116
|
+
self.parent = parent
|
117
|
+
rule_list_init
|
118
|
+
fail('no') unless Symbol === name
|
119
|
+
@name = name
|
120
|
+
@style = nil
|
121
|
+
@trailing_whitespace_style = nil
|
122
|
+
block.call(self)
|
123
|
+
end
|
124
|
+
attr_accessor :name
|
125
|
+
def next_line str, alter=false
|
126
|
+
res = false
|
127
|
+
if str == ''
|
128
|
+
nil
|
129
|
+
elsif alter
|
130
|
+
if /\A([^\n]+)(?:\n?)(.*)\Z/m =~ str
|
131
|
+
res = $1
|
132
|
+
str.replace($2)
|
133
|
+
else
|
134
|
+
fail("fail: #{str.inspect}")
|
135
|
+
end
|
136
|
+
else
|
137
|
+
if /\A([^\n]+)/ =~ str
|
138
|
+
res = $1
|
139
|
+
else
|
140
|
+
fail("fail: #{str.inspect}")
|
141
|
+
end
|
142
|
+
end
|
143
|
+
res
|
144
|
+
end
|
145
|
+
def process string, out
|
146
|
+
ret = nil
|
147
|
+
while line = next_line(string)
|
148
|
+
if other = rule_list.detect{|x| x.match(string)}
|
149
|
+
next_state_name = other.state
|
150
|
+
ret = next_state_name
|
151
|
+
break
|
152
|
+
else
|
153
|
+
next_line(string, true) # alter string
|
154
|
+
use_line = nil
|
155
|
+
if @trailing_whitespace_style
|
156
|
+
/\A(|.*[^[:space:]])([[:space:]]*)\Z/ =~ line or fail('oops')
|
157
|
+
head, tail = $1, $2
|
158
|
+
colored_head = colorize(head, *colors)
|
159
|
+
use_line = colored_head.dup
|
160
|
+
unless tail.empty?
|
161
|
+
ws_style = parent.stylesheet[@trailing_whitespace_style] or
|
162
|
+
style_not_found_failure('@trailing_whitespace_style')
|
163
|
+
colored_tail = colorize(tail, *ws_style)
|
164
|
+
use_line.concat colored_tail
|
165
|
+
end
|
166
|
+
else
|
167
|
+
use_line = colorize(line, *colors)
|
168
|
+
end
|
169
|
+
out.puts use_line
|
170
|
+
end
|
171
|
+
end
|
172
|
+
ret
|
173
|
+
end
|
174
|
+
def colors
|
175
|
+
@colors ||= begin
|
176
|
+
if style.nil?
|
177
|
+
[]
|
178
|
+
else
|
179
|
+
parent.stylesheet[style] or style_not_found_failure
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
def style *a
|
184
|
+
case a.size
|
185
|
+
when 0; @style
|
186
|
+
when 1; @style = a.first
|
187
|
+
else fail('no')
|
188
|
+
end
|
189
|
+
end
|
190
|
+
def trailing_whitespace_style *a
|
191
|
+
case a.size
|
192
|
+
when 0; @trailing_whitespace_style
|
193
|
+
when 1; @trailing_whitespace_style = a.first
|
194
|
+
else fail('no')
|
195
|
+
end
|
196
|
+
end
|
197
|
+
def style_not_found_failure which = '@style'
|
198
|
+
value = instance_variable_get(which)
|
199
|
+
fail("#{which} not found: #{value.inspect}")
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|