@@ -55,7 +55,7 @@ sub scanStringLiteral {
55
55
56
56
# Case: has escaped quotes
57
57
while (distanceToPattern(' \\ "' ) != -1 &&
58
- distanceToPattern(' \\ "' ) < distanceToPattern(' [^\\ ]"' )) {
58
+ distanceToPattern(' \\ "' ) < distanceToPattern(' [^\\ ]] "' )) {
59
59
# Scan escaped quote
60
60
scanUpToPattern(' \\ "' );
61
61
scanPattern(' \\ "' );
@@ -67,6 +67,22 @@ sub scanStringLiteral {
67
67
return 1;
68
68
}
69
69
70
+ sub insideString {
71
+ my $_start = $pos ;
72
+ $pos = 0;
73
+
74
+ # While we're behind our spot...
75
+ while ($pos ++ < $_start) {
76
+ # Can we scan a string and moved past our spot?
77
+ if (scanStringLiteral() && $pos >= $_start) {
78
+ $pos = $_start;
79
+ return 1;
80
+ }
81
+ }
82
+
83
+ { $pos = $_start; return 0; };
84
+ }
85
+
70
86
sub scanBracePair {
71
87
my $_start = $pos ;
72
88
@@ -80,7 +96,8 @@ sub scanBracePair {
80
96
while (distanceToPattern($open ) != -1 &&
81
97
distanceToPattern($open ) < distanceToPattern($close )) {
82
98
# Scan inner pair of brackets
83
- scanMethodCall();
99
+ scanUpToPattern($open );
100
+ scanBracePair($open , $close );
84
101
}
85
102
86
103
# Scan closing bracket
@@ -179,7 +196,7 @@ sub scanObjectBehindCursor() {
179
196
# We will first find `foo.bar` out of `[foo.bar baz].%whatever`.
180
197
# We would need to keep going back until we hit `[foo.bar baz]` entirely.
181
198
my $preScanPos = $pos ;
182
- if (scanObjectToken() && $pos == $oldPos ) {
199
+ if (( scanObjectToken() || scanParenthesis() ) && $pos == $oldPos ) {
183
200
my $obj = substr ($_line, $preScanPos , $pos - $preScanPos );
184
201
185
202
# Skip over leading whitespace
@@ -203,23 +220,94 @@ sub scanObjectBehindCursor() {
203
220
{ $pos = $_start; return 0; };
204
221
}
205
222
223
+ # Leaves cursor just before the .%
224
+ # Returns (key, object, range.loc, range.length)
225
+ sub scanKVCKeyAndObjectAndLengths () {
226
+ my $postKVCPos = $pos ;
227
+ my $preKVCPos = $pos - 2;
228
+ my $key = undef ;
229
+ my $keyLength = undef ;
230
+
231
+ # Case: foo.%bar
232
+ if (scanIdentifier()) {
233
+ # Scan the "key" and surround it in @"" quotes
234
+ $key = substr ($_line, $postKVCPos , $pos - $postKVCPos );
235
+ $keyLength = length ($key ); # for range to replace
236
+ $key = ' @"' . $key . ' "' ;
237
+ }
238
+ # Case: foo.%(...)
239
+ elsif (scanParenthesis()) {
240
+ # Scan the "key" which could be anything
241
+ $key = substr ($_line, $postKVCPos , $pos - $postKVCPos );
242
+ $keyLength = length ($key );
243
+ }
244
+
245
+ # Case: setter syntax: ' = (stuff);'
246
+ my $setterPattern = ' \s*=\s*([^;]+);' ;
247
+ my $val = undef ;
248
+ my $setterLength = 0;
249
+ if (scanUpToPattern($setterPattern )) {
250
+ if (substr ($_line, $pos ) =~ / ^($setterPattern )/ ) {
251
+ $val = $2 ;
252
+ $setterLength = length ($1 ) - 1; # we don't want the ';'
253
+ }
254
+ }
255
+
256
+ # Back up to before the key
257
+ $pos = $preKVCPos ;
258
+
259
+ # Scan the "object"
260
+ my $object = scanObjectBehindCursor();
261
+ my $objLength = length ($object );
262
+
263
+ # Compute range to replace (loc, len)
264
+ # Length is target.length + len(".%") + key.length
265
+ my $loc = $pos - $objLength ;
266
+ my $len = $objLength + 2 + $keyLength + $setterLength ;
267
+
268
+ # Scan past the .%
269
+ $pos = $postKVCPos ;
270
+
271
+ return ($key , $object , $val , $loc , $len );
272
+ }
273
+
206
274
sub replaceSetters {
207
275
$pos = 0;
208
- my $setterRegex = ' \.%([\w\d]+) = ([^;]+);' ;
209
- while (scanUpToPattern($setterRegex )) {
210
- $scanned =~ s /\s *(.*)/ $1 / ;
211
- $_line =~ s / ($scanned)$setterRegex/ [$1 setValue:$3 forKey:@"$2 "];/ g ;
276
+
277
+ while (scanUpToPattern(' \.%' ) && scanPattern(' \.%' )) {
278
+ if (!insideString()) {
279
+ # Get associated variables
280
+ my ($key , $object , $val , $loc , $len ) = scanKVCKeyAndObjectAndLengths();
281
+
282
+ if ($val ) {
283
+ # Replace the range with the KVC setter
284
+ substr ($_line, $loc , $len ) = " [(id)$object setValue:$val forKey:$key ]" ;
285
+
286
+ # Continue scanning from the start of the new code,
287
+ # as the key may contain more .% calls inside it
288
+ $pos = $loc ;
289
+ } else {
290
+
291
+ }
292
+ }
212
293
}
213
294
}
214
295
215
296
sub replaceGetters {
216
297
$pos = 0;
217
- while (scanUpToPattern(' \.%([\w\d]+)' )) {
218
- # Todo: regex escape $object and use it
219
- # instead of using .{$length} in the pattern
220
- my $object = scanObjectBehindCursor();
221
- my $length = length ($object );
222
- $_line =~ s / (.{$length})\. %([\w\d ]+)/ [(id)$1 valueForKey:@"$2 "]/ ;
298
+
299
+ while (scanUpToPattern(' \.%' ) && scanPattern(' \.%' )) {
300
+ if (!insideString()) {
301
+ # Get associated variables
302
+ my ($key , $object , $val , $loc , $len ) = scanKVCKeyAndObjectAndLengths();
303
+
304
+ # Replace the range with the KVC getter
305
+ substr ($_line, $loc , $len ) = " [(id)$object valueForKey:$key ]" ;
306
+
307
+ # Continue scanning from the start of the new code,
308
+ # as the key may contain more .% calls inside it
309
+ $pos = $loc ;
310
+ }
223
311
}
224
312
}
225
313
0 commit comments