@@ -55,7 +55,7 @@ sub scanStringLiteral {
5555
5656 # Case: has escaped quotes
5757 while (distanceToPattern(' \\ "' ) != -1 &&
58- distanceToPattern(' \\ "' ) < distanceToPattern(' [^\\ ]"' )) {
58+ distanceToPattern(' \\ "' ) < distanceToPattern(' [^\\ ]] "' )) {
5959 # Scan escaped quote
6060 scanUpToPattern(' \\ "' );
6161 scanPattern(' \\ "' );
@@ -67,6 +67,22 @@ sub scanStringLiteral {
6767 return 1;
6868}
6969
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+
7086sub scanBracePair {
7187 my $_start = $pos ;
7288
@@ -80,7 +96,8 @@ sub scanBracePair {
8096 while (distanceToPattern($open ) != -1 &&
8197 distanceToPattern($open ) < distanceToPattern($close )) {
8298 # Scan inner pair of brackets
83- scanMethodCall();
99+ scanUpToPattern($open );
100+ scanBracePair($open , $close );
84101 }
85102
86103 # Scan closing bracket
@@ -179,7 +196,7 @@ sub scanObjectBehindCursor() {
179196 # We will first find `foo.bar` out of `[foo.bar baz].%whatever`.
180197 # We would need to keep going back until we hit `[foo.bar baz]` entirely.
181198 my $preScanPos = $pos ;
182- if (scanObjectToken() && $pos == $oldPos ) {
199+ if (( scanObjectToken() || scanParenthesis() ) && $pos == $oldPos ) {
183200 my $obj = substr ($_line, $preScanPos , $pos - $preScanPos );
184201
185202 # Skip over leading whitespace
@@ -203,23 +220,94 @@ sub scanObjectBehindCursor() {
203220 { $pos = $_start; return 0; };
204221}
205222
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+
206274sub replaceSetters {
207275 $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+ }
212293 }
213294}
214295
215296sub replaceGetters {
216297 $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+ }
223311 }
224312}
225313
0 commit comments