xdry 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.md +11 -0
- data/Rakefile +76 -0
- data/VERSION +1 -0
- data/bin/xdry +4 -0
- data/lib/xdry.rb +18 -0
- data/lib/xdry/boxing.rb +212 -0
- data/lib/xdry/generators/ctor_from_field.rb +91 -0
- data/lib/xdry/generators/dealloc.rb +53 -0
- data/lib/xdry/generators/dictionary_coding.rb +129 -0
- data/lib/xdry/generators/field_from_property.rb +20 -0
- data/lib/xdry/generators/property-from-field.rb +22 -0
- data/lib/xdry/generators/storing_constructor.rb +72 -0
- data/lib/xdry/generators/synthesize.rb +25 -0
- data/lib/xdry/generators_support.rb +42 -0
- data/lib/xdry/parsing/driver.rb +106 -0
- data/lib/xdry/parsing/model.rb +272 -0
- data/lib/xdry/parsing/nodes.rb +260 -0
- data/lib/xdry/parsing/parsers.rb +166 -0
- data/lib/xdry/parsing/parts/selectors.rb +95 -0
- data/lib/xdry/parsing/parts/var_types.rb +66 -0
- data/lib/xdry/parsing/pos.rb +75 -0
- data/lib/xdry/parsing/scope_stack.rb +68 -0
- data/lib/xdry/parsing/scopes.rb +61 -0
- data/lib/xdry/parsing/scopes_support.rb +143 -0
- data/lib/xdry/patching/emitter.rb +60 -0
- data/lib/xdry/patching/insertion_points.rb +209 -0
- data/lib/xdry/patching/item_patchers.rb +74 -0
- data/lib/xdry/patching/patcher.rb +139 -0
- data/lib/xdry/run.rb +227 -0
- data/lib/xdry/support/enumerable_additions.rb +35 -0
- data/lib/xdry/support/string_additions.rb +27 -0
- data/lib/xdry/support/symbol_additions.rb +14 -0
- data/site/_config.yml +3 -0
- data/site/_example +9 -0
- data/site/_layouts/default.html +30 -0
- data/site/_plugins/example.rb +16 -0
- data/site/_plugins/highlight_unindent.rb +17 -0
- data/site/index.md +417 -0
- data/site/master.css +94 -0
- data/spec/boxing_spec.rb +80 -0
- data/spec/ctor_from_field_spec.rb +251 -0
- data/spec/dealloc_spec.rb +103 -0
- data/spec/dictionary_coding_spec.rb +132 -0
- data/spec/field_from_prop_spec.rb +72 -0
- data/spec/prop_from_field_spec.rb +39 -0
- data/spec/readme_samples_spec.rb +76 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +53 -0
- data/spec/synthesize_spec.rb +94 -0
- data/xdry.gemspec +103 -0
- metadata +141 -0
@@ -0,0 +1,209 @@
|
|
1
|
+
|
2
|
+
module XDry
|
3
|
+
|
4
|
+
class InsertionPoint
|
5
|
+
|
6
|
+
attr_reader :method, :node, :ip
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
find!
|
10
|
+
end
|
11
|
+
|
12
|
+
def insert patcher, lines
|
13
|
+
raise StandardError, "#{self.class.name} has not been found but trying to insert" unless found?
|
14
|
+
patcher.send(@method, @node.pos, wrap(lines), @indent)
|
15
|
+
end
|
16
|
+
|
17
|
+
def found?
|
18
|
+
not @method.nil?
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def wrap lines
|
24
|
+
lines
|
25
|
+
end
|
26
|
+
|
27
|
+
def before node
|
28
|
+
@method = :insert_before
|
29
|
+
@node = node
|
30
|
+
@indent = node.indent
|
31
|
+
end
|
32
|
+
|
33
|
+
def after node
|
34
|
+
@method = :insert_after
|
35
|
+
@node = node
|
36
|
+
@indent = node.indent
|
37
|
+
end
|
38
|
+
|
39
|
+
def indented_before node
|
40
|
+
before node
|
41
|
+
@indent = @indent + INDENT_STEP
|
42
|
+
end
|
43
|
+
|
44
|
+
def indented_after node
|
45
|
+
after node
|
46
|
+
@indent = @indent + INDENT_STEP
|
47
|
+
end
|
48
|
+
|
49
|
+
def try insertion_point
|
50
|
+
if insertion_point.found?
|
51
|
+
@method, @node, @ip = insertion_point.method, insertion_point.node, insertion_point
|
52
|
+
true
|
53
|
+
else
|
54
|
+
false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def find!
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
class ImplementationStartIP < InsertionPoint
|
64
|
+
|
65
|
+
def initialize oclass
|
66
|
+
@oclass = oclass
|
67
|
+
super()
|
68
|
+
end
|
69
|
+
|
70
|
+
def find!
|
71
|
+
after @oclass.main_implementation.start_node
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
class BeforeImplementationStartIP < InsertionPoint
|
77
|
+
|
78
|
+
def initialize oclass
|
79
|
+
@oclass = oclass
|
80
|
+
super()
|
81
|
+
end
|
82
|
+
|
83
|
+
def find!
|
84
|
+
before @oclass.main_implementation.start_node
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
class BeforeInterfaceEndIP < InsertionPoint
|
90
|
+
|
91
|
+
def initialize oclass
|
92
|
+
@oclass = oclass
|
93
|
+
super()
|
94
|
+
end
|
95
|
+
|
96
|
+
def find!
|
97
|
+
before @oclass.main_interface.end_node
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
class BeforeSuperCallIP < InsertionPoint
|
103
|
+
|
104
|
+
def initialize scope
|
105
|
+
@scope = scope
|
106
|
+
super()
|
107
|
+
end
|
108
|
+
|
109
|
+
def find!
|
110
|
+
child_node = @scope.children.find { |child| child.is_a? NSuperCall }
|
111
|
+
if child_node.nil?
|
112
|
+
indented_before @scope.ending_node
|
113
|
+
else
|
114
|
+
before child_node
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
class BeforeReturnIP < InsertionPoint
|
121
|
+
|
122
|
+
def initialize scope
|
123
|
+
@scope = scope
|
124
|
+
super()
|
125
|
+
end
|
126
|
+
|
127
|
+
def find!
|
128
|
+
child_node = @scope.children.find { |child| child.is_a? NReturn }
|
129
|
+
if child_node.nil?
|
130
|
+
before @scope.ending_node
|
131
|
+
else
|
132
|
+
before child_node
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
class AfterDefineIP < InsertionPoint
|
139
|
+
|
140
|
+
def initialize scope
|
141
|
+
@scope = scope
|
142
|
+
super()
|
143
|
+
end
|
144
|
+
|
145
|
+
def find!
|
146
|
+
child_nodes = @scope.children.select { |child| child.is_a? NDefine }
|
147
|
+
unless child_nodes.empty?
|
148
|
+
after child_nodes.last
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
class InsideConstructorIfSuperIP < InsertionPoint
|
155
|
+
|
156
|
+
def initialize scope
|
157
|
+
@scope = scope
|
158
|
+
super()
|
159
|
+
end
|
160
|
+
|
161
|
+
def find!
|
162
|
+
if_start_node = @scope.children.find { |child| child.is_a? NSuperCall }
|
163
|
+
if if_start_node.nil?
|
164
|
+
indented_before @scope.ending_node
|
165
|
+
else
|
166
|
+
if_end_node = @scope.children.find { |child| child.is_a?(NClosingBrace) && child.indent == if_start_node.indent }
|
167
|
+
if if_end_node.nil?
|
168
|
+
indented_after if_start_node
|
169
|
+
else
|
170
|
+
indented_before if_end_node
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
|
177
|
+
class MultiIP < InsertionPoint
|
178
|
+
|
179
|
+
def initialize *insertion_points
|
180
|
+
@insertion_points = insertion_points
|
181
|
+
@last_before = []
|
182
|
+
@last_after = []
|
183
|
+
super()
|
184
|
+
end
|
185
|
+
|
186
|
+
def wrap_if_last! before, after
|
187
|
+
@last_before = before
|
188
|
+
@last_after = after
|
189
|
+
end
|
190
|
+
|
191
|
+
def wrap_with_empty_lines_if_last!
|
192
|
+
wrap_if_last! [""], [""]
|
193
|
+
end
|
194
|
+
|
195
|
+
def find!
|
196
|
+
@insertion_points.detect { |ip| try ip }
|
197
|
+
end
|
198
|
+
|
199
|
+
def wrap lines
|
200
|
+
if @ip == @insertion_points.last
|
201
|
+
@last_before + lines + @last_after
|
202
|
+
else
|
203
|
+
lines
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
|
2
|
+
module XDry
|
3
|
+
|
4
|
+
class ItemPatcher
|
5
|
+
|
6
|
+
attr_reader :item
|
7
|
+
attr_reader :patcher
|
8
|
+
|
9
|
+
def initialize patcher
|
10
|
+
@patcher = patcher
|
11
|
+
find!
|
12
|
+
yield @item if block_given? && found?
|
13
|
+
end
|
14
|
+
|
15
|
+
def found?
|
16
|
+
not item.nil?
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
def find
|
22
|
+
end
|
23
|
+
|
24
|
+
def insertion_point
|
25
|
+
end
|
26
|
+
|
27
|
+
def new_code
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def find!
|
33
|
+
@item = find
|
34
|
+
if @item.nil?
|
35
|
+
patch!
|
36
|
+
@item = find
|
37
|
+
raise StandardError, "#{self.class.name} cannot find item even after adding a new one" if @item.nil?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def patch!
|
42
|
+
insertion_point.insert patcher, new_code
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
class MethodPatcher < ItemPatcher
|
48
|
+
|
49
|
+
attr_reader :oclass
|
50
|
+
attr_reader :insertion_point
|
51
|
+
attr_reader :new_code
|
52
|
+
|
53
|
+
def initialize patcher, oclass, selector, insertion_point, new_code
|
54
|
+
@oclass = oclass
|
55
|
+
@selector = selector
|
56
|
+
@insertion_point = insertion_point
|
57
|
+
@new_code = new_code
|
58
|
+
super(patcher)
|
59
|
+
end
|
60
|
+
|
61
|
+
protected
|
62
|
+
|
63
|
+
def find
|
64
|
+
find_method_impl_by_selector(@selector)
|
65
|
+
end
|
66
|
+
|
67
|
+
def find_method_impl_by_selector selector
|
68
|
+
m = oclass.find_method(selector)
|
69
|
+
m && (m.has_impl? ? m : nil)
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
|
2
|
+
module XDry
|
3
|
+
|
4
|
+
class Patcher
|
5
|
+
|
6
|
+
attr_accessor :dry_run, :verbose
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@patched = {}
|
10
|
+
@dry_run = true
|
11
|
+
end
|
12
|
+
|
13
|
+
def insert_after pos, new_lines, indent = '', parse = true
|
14
|
+
do_insert_after pos.file_ref, pos.scope_after, pos.line_no - 1, new_lines, indent || '', parse
|
15
|
+
end
|
16
|
+
|
17
|
+
def insert_before pos, new_lines, indent = '', parse = true
|
18
|
+
do_insert_after pos.file_ref, pos.scope_before, pos.line_no - 2, new_lines, indent || '', parse
|
19
|
+
end
|
20
|
+
|
21
|
+
def delete_line pos
|
22
|
+
do_delete_lines pos.file_ref, pos.line_no - 1, 1
|
23
|
+
end
|
24
|
+
|
25
|
+
def replace_line pos
|
26
|
+
old_line = patched_lines_of(pos.file_ref)[pos.line_no-1].rstrip
|
27
|
+
new_line = yield(old_line)
|
28
|
+
delete_line pos
|
29
|
+
insert_before pos, [new_line], '', false
|
30
|
+
end
|
31
|
+
|
32
|
+
def do_delete_lines file_ref, line_index, line_count
|
33
|
+
lines = patched_lines_of(file_ref)
|
34
|
+
|
35
|
+
if @verbose
|
36
|
+
puts "DELETING #{line_count} LINE(S) FROM LINE NO.#{line_index+1}:"
|
37
|
+
lines[line_index .. line_index+line_count-1].each { |line| puts " #{line}" }
|
38
|
+
end
|
39
|
+
|
40
|
+
file_ref.fixup_positions! line_index+line_count, -line_count
|
41
|
+
lines[line_index .. line_index+line_count-1] = []
|
42
|
+
end
|
43
|
+
|
44
|
+
def do_insert_after file_ref, start_scope, line_index, new_lines, indent, parse
|
45
|
+
new_lines = new_lines.collect { |line| line.blank? ? line : indent + line }
|
46
|
+
new_lines = new_lines.collect { |line| line.gsub("\t", INDENT_STEP) }
|
47
|
+
lines = patched_lines_of(file_ref)
|
48
|
+
|
49
|
+
if @verbose
|
50
|
+
puts "INSERTING LINES AFTER LINE NO.#{line_index+1}:"
|
51
|
+
new_lines.each { |line| puts " #{line}" }
|
52
|
+
puts " AFTER LINE:"
|
53
|
+
puts " #{lines[line_index]}"
|
54
|
+
end
|
55
|
+
|
56
|
+
# collapse leading/trailing empty lines with the empty lines that already exist
|
57
|
+
# in the source code
|
58
|
+
|
59
|
+
# when line_index == -1 (insert at the beginning of the file), there are no leading lines
|
60
|
+
if line_index >= 0
|
61
|
+
desired_leading_empty_lines = new_lines.prefix_while(&:blank?).length
|
62
|
+
actual_leading_empty_lines = lines[0..line_index].suffix_while(&:blank?).length
|
63
|
+
leading_lines_to_remove = [actual_leading_empty_lines, desired_leading_empty_lines].min
|
64
|
+
new_lines = new_lines[leading_lines_to_remove .. -1]
|
65
|
+
end
|
66
|
+
|
67
|
+
# if all lines were empty, the number of trailing empty lines might have changed
|
68
|
+
# after removal of some leading lines, so we compute this after the removal
|
69
|
+
desired_trailing_empty_lines = new_lines.suffix_while(&:blank?).length
|
70
|
+
actual_trailing_empty_lines = lines[line_index+1..-1].prefix_while(&:blank?).length
|
71
|
+
trailing_lines_to_remove = [actual_trailing_empty_lines, desired_trailing_empty_lines].min
|
72
|
+
new_lines = new_lines[0 .. -(trailing_lines_to_remove+1)]
|
73
|
+
|
74
|
+
file_ref.fixup_positions! line_index+1, new_lines.size
|
75
|
+
|
76
|
+
lines[line_index+1 .. line_index+1] = new_lines.collect { |line| "#{line}\n" } + [lines[line_index+1]]
|
77
|
+
|
78
|
+
if parse
|
79
|
+
driver = ParsingDriver.new(nil)
|
80
|
+
driver.verbose = @verbose
|
81
|
+
driver.parse_fragment file_ref, new_lines, line_index+1+1, start_scope
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def save!
|
86
|
+
changed_file_refs = []
|
87
|
+
for file_ref, lines in @patched
|
88
|
+
original_path = file_ref.path
|
89
|
+
|
90
|
+
text = lines.join("")
|
91
|
+
next if text == file_ref.read
|
92
|
+
|
93
|
+
changed_file_refs << file_ref
|
94
|
+
|
95
|
+
new_path = if @dry_run
|
96
|
+
ext = File.extname(original_path)
|
97
|
+
File.join(File.dirname(original_path), File.basename(original_path, ext) + '.xdry' + ext)
|
98
|
+
else
|
99
|
+
original_path
|
100
|
+
end
|
101
|
+
|
102
|
+
open(new_path, 'w') { |f| f.write text }
|
103
|
+
end
|
104
|
+
@patched = {}
|
105
|
+
return changed_file_refs
|
106
|
+
end
|
107
|
+
|
108
|
+
def retrieve!
|
109
|
+
result = {}
|
110
|
+
for file_ref, lines in @patched
|
111
|
+
result[file_ref.path] = lines.join("")
|
112
|
+
end
|
113
|
+
@patched = {}
|
114
|
+
return result
|
115
|
+
end
|
116
|
+
|
117
|
+
def remove_marker! marker
|
118
|
+
if marker.is_a? NFullLineMarker
|
119
|
+
delete_line marker.pos
|
120
|
+
else
|
121
|
+
replace_line marker.pos do |old_line|
|
122
|
+
old_line.gsub(marker.text, '').gsub(/ +$/, '')
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
def patched_lines_of file_ref
|
130
|
+
@patched[file_ref] ||= load_lines_of(file_ref)
|
131
|
+
end
|
132
|
+
|
133
|
+
def load_lines_of file_ref
|
134
|
+
file_ref.read.lines.collect
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
data/lib/xdry/run.rb
ADDED
@@ -0,0 +1,227 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module XDry
|
4
|
+
|
5
|
+
def self.produce_everything oglobal, patcher, config
|
6
|
+
puts "Generating code... " if config.verbose
|
7
|
+
|
8
|
+
generators = Generators::ALL.select { |klass| config.enabled?(klass.id) }.
|
9
|
+
collect { |klass| klass.new(config, patcher) }
|
10
|
+
|
11
|
+
if config.verbose
|
12
|
+
puts "Running generators: " + generators.collect { |gen| gen.class.id }.join(", ")
|
13
|
+
end
|
14
|
+
|
15
|
+
oglobal.classes.each do |oclass|
|
16
|
+
puts " - #{oclass.name}" if config.verbose
|
17
|
+
|
18
|
+
if config.verbose
|
19
|
+
oclass.attributes.each do |oattr|
|
20
|
+
puts " #{oattr}"
|
21
|
+
end
|
22
|
+
|
23
|
+
oclass.methods.each do |omethod|
|
24
|
+
puts " #{omethod}"
|
25
|
+
end
|
26
|
+
|
27
|
+
oclass.implementations.each do |nimpl|
|
28
|
+
puts " #{nimpl}"
|
29
|
+
nimpl.synthesizes.each do |nsynth|
|
30
|
+
puts " #{nsynth}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
generators.each { |gen| gen.process_class(oclass) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Config < Struct.new(:only, :dry_run, :watch, :verbose, :disable, :enable_only)
|
40
|
+
|
41
|
+
def initialize
|
42
|
+
self.only = nil
|
43
|
+
self.dry_run = false
|
44
|
+
self.watch = false
|
45
|
+
self.verbose = false
|
46
|
+
self.disable = []
|
47
|
+
self.enable_only = nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def enabled? gen_id
|
51
|
+
(enable_only.nil? || enable_only.include?(gen_id)) && !disable.include?(gen_id)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.parse_command_line_config(args)
|
57
|
+
config = Config.new
|
58
|
+
|
59
|
+
opts = OptionParser.new do |opts|
|
60
|
+
opts.banner = "Usage: xdry [options]"
|
61
|
+
|
62
|
+
opts.separator ""
|
63
|
+
opts.separator "General options:"
|
64
|
+
|
65
|
+
opts.on("-w", "--watch", "Watch for file system changes and rerun each time .h/.m is modified") do
|
66
|
+
config.watch = true
|
67
|
+
end
|
68
|
+
|
69
|
+
opts.separator ""
|
70
|
+
opts.separator "Filtering options:"
|
71
|
+
|
72
|
+
opts.on("-o", "--only=MASK", "Only process files matching this mask") do |v|
|
73
|
+
config.only = v
|
74
|
+
end
|
75
|
+
|
76
|
+
opts.separator ""
|
77
|
+
opts.separator "Choosing which generators to run:"
|
78
|
+
|
79
|
+
opts.on("-e", "--enable-only=LIST", "Only run the given generators (e.g.: -e dealloc,synth)") do |v|
|
80
|
+
config.enable_only = v.split(",").collect { |n| n.strip }
|
81
|
+
|
82
|
+
all = XDry::Generators::ALL.collect { |kl| kl.id }
|
83
|
+
unless (unsup = config.enable_only - all).empty?
|
84
|
+
puts "Unknown generator names in -e: #{unsup.join(', ')}."
|
85
|
+
puts "Supported names are: #{all.join(', ')}."
|
86
|
+
exit 1
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
opts.on("-d", "--disable=LIST", "Disable the given generators (e.g.: -d dealloc,synth)") do |v|
|
91
|
+
config.disable = v.split(",").collect { |n| n.strip }
|
92
|
+
|
93
|
+
all = XDry::Generators::ALL.collect { |kl| kl.id }
|
94
|
+
unless (unsup = config.disable - all).empty?
|
95
|
+
puts "Unknown generator names in -d: #{unsup.join(', ')}."
|
96
|
+
puts "Supported names are: #{all.join(', ')}."
|
97
|
+
exit 1
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
opts.on("--list", "List all supported generators and exit") do |v|
|
102
|
+
XDry::Generators::ALL.each { |kl| puts "#{kl.id}" }
|
103
|
+
exit
|
104
|
+
end
|
105
|
+
|
106
|
+
opts.separator ""
|
107
|
+
opts.separator "Patching options:"
|
108
|
+
|
109
|
+
opts.on("-n", "--dry-run", "Save changed files as .xdry.{h/m}") do |v|
|
110
|
+
config.dry_run = true
|
111
|
+
end
|
112
|
+
|
113
|
+
opts.separator ""
|
114
|
+
opts.separator "Common options:"
|
115
|
+
|
116
|
+
opts.on("-v", "--verbose", "Print TONS of progress information") do
|
117
|
+
config.verbose = true
|
118
|
+
end
|
119
|
+
|
120
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
121
|
+
puts opts
|
122
|
+
exit
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
opts.parse!(args)
|
127
|
+
return config
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.run_once config
|
131
|
+
oglobal = OGlobal.new
|
132
|
+
|
133
|
+
parser = ParsingDriver.new(oglobal)
|
134
|
+
parser.verbose = config.verbose
|
135
|
+
|
136
|
+
Dir["**/*.m"].each do |m_file|
|
137
|
+
next if config.only and not File.fnmatch(config.only, m_file)
|
138
|
+
next if m_file =~ /\.xdry\./
|
139
|
+
h_file = m_file.sub /\.m$/, '.h'
|
140
|
+
if File.file? h_file
|
141
|
+
puts h_file if config.verbose
|
142
|
+
|
143
|
+
parser.parse_file(h_file)
|
144
|
+
parser.parse_file(m_file)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
patcher = Patcher.new
|
149
|
+
patcher.dry_run = config.dry_run
|
150
|
+
patcher.verbose = config.verbose
|
151
|
+
|
152
|
+
parser.markers.each { |marker| patcher.remove_marker! marker }
|
153
|
+
|
154
|
+
self.produce_everything(oglobal, patcher, config)
|
155
|
+
|
156
|
+
return patcher.save!
|
157
|
+
end
|
158
|
+
|
159
|
+
def self.test_run sources, config
|
160
|
+
oglobal = OGlobal.new
|
161
|
+
|
162
|
+
parser = ParsingDriver.new(oglobal)
|
163
|
+
parser.verbose = config.verbose
|
164
|
+
sources.each do |file_path, content|
|
165
|
+
parser.parse_string file_path, content
|
166
|
+
end
|
167
|
+
|
168
|
+
patcher = Patcher.new
|
169
|
+
patcher.verbose = config.verbose
|
170
|
+
|
171
|
+
parser.markers.each { |marker| patcher.remove_marker! marker }
|
172
|
+
|
173
|
+
self.produce_everything(oglobal, patcher, config)
|
174
|
+
|
175
|
+
return patcher.retrieve!
|
176
|
+
end
|
177
|
+
|
178
|
+
def self.run args
|
179
|
+
config = parse_command_line_config(args)
|
180
|
+
|
181
|
+
while Dir.pwd != '/' && Dir['*.xcodeproj'] == []
|
182
|
+
Dir.chdir('..')
|
183
|
+
end
|
184
|
+
if Dir['*.xcodeproj'] == []
|
185
|
+
puts "Cannot find *.xcodeproj in any of the parent directories. Stop."
|
186
|
+
exit 1
|
187
|
+
end
|
188
|
+
|
189
|
+
changed_file_refs = run_once config
|
190
|
+
|
191
|
+
if config.watch
|
192
|
+
require 'rubygems'
|
193
|
+
require 'fssm'
|
194
|
+
rebuild = lambda do |base, relative|
|
195
|
+
unless File.basename(relative) == 'xdry.m'
|
196
|
+
changed_file_refs = run_once(config)
|
197
|
+
unless changed_file_refs.empty?
|
198
|
+
system "growlnotify", "-a", "Xcode", "-t", "XD.R.Y.", "-m", "Updating..."
|
199
|
+
system "osascript", "-e", '
|
200
|
+
tell application "Finder" to activate
|
201
|
+
delay 0.3
|
202
|
+
tell application "Xcode" to activate
|
203
|
+
delay 0.5
|
204
|
+
tell application "System Events" to keystroke "u" using {command down}
|
205
|
+
'
|
206
|
+
system "growlnotify", "-a", "Xcode", "-t", "XD.R.Y.", "-m", "Updated!"
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
puts
|
211
|
+
puts "Monitoring for file system changes..."
|
212
|
+
FSSM.monitor '.', ['**/*.{h,m}'] do |monitor|
|
213
|
+
monitor.create &rebuild
|
214
|
+
monitor.update &rebuild
|
215
|
+
monitor.delete &rebuild
|
216
|
+
end
|
217
|
+
else
|
218
|
+
if changed_file_refs.empty?
|
219
|
+
puts "No changes."
|
220
|
+
else
|
221
|
+
puts "Modified:"
|
222
|
+
changed_file_refs.each { |ref| puts "-> #{ref.path}" }
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
end
|