xdry 0.1.0
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/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
|