PageRenderTime 98ms CodeModel.GetById 32ms app.highlight 61ms RepoModel.GetById 2ms app.codeStats 0ms

/core/externals/google-toolbox-for-mac/XcodePlugin/GTMXcodeCorrectWhiteSpace.m

http://macfuse.googlecode.com/
Objective C | 208 lines | 151 code | 18 blank | 39 comment | 44 complexity | 0a706a74b3353a25e02fb7df27299e62 MD5 | raw file
  1//
  2//  GTMXcodeCorrectWhiteSpace.m
  3//
  4//  Copyright 2009 Google Inc.
  5//
  6//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
  7//  use this file except in compliance with the License.  You may obtain a copy
  8//  of the License at
  9//
 10//  http://www.apache.org/licenses/LICENSE-2.0
 11//
 12//  Unless required by applicable law or agreed to in writing, software
 13//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 14//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 15//  License for the specific language governing permissions and limitations under
 16//  the License.
 17//
 18
 19#import <Cocoa/Cocoa.h>
 20#import "GTMObjC2Runtime.h"
 21#import "GTMXcodePlugin.h"
 22#import "GTMXcodePreferences.h"
 23
 24@interface GTMXcodeCorrectWhiteSpace : NSObject
 25- (BOOL)gdt_writeToFile:(NSString *)fileName ofType:(NSString *)type;
 26@end
 27
 28@implementation GTMXcodeCorrectWhiteSpace
 29// Register our class to perform the swizzle once the plugin has finished
 30// loading.
 31+ (void)load {
 32  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 33  [GTMXcodePlugin registerSwizzleClass:self];
 34  [pool release];
 35}
 36
 37// Default initializer. Swizzles [PBXTextFileDocument writeToFile:ofType:]
 38// with our [gdt_writeToFile:ofType:].
 39- (id)init {
 40  if ((self = [super init])) {
 41    SEL ourSelector = @selector(gdt_writeToFile:ofType:);
 42    Method ourMethod = class_getInstanceMethod([self class], ourSelector);
 43    Class pbxClass = NSClassFromString(@"PBXTextFileDocument");
 44    if (class_addMethod(pbxClass,
 45                        ourSelector,
 46                        method_getImplementation(ourMethod),
 47                        method_getTypeEncoding(ourMethod))) {
 48      ourMethod = class_getInstanceMethod(pbxClass, ourSelector);
 49      Method theirMethod
 50        = class_getInstanceMethod(pbxClass, @selector(writeToFile:ofType:));
 51      method_exchangeImplementations(ourMethod, theirMethod);
 52    }
 53  }
 54  return self;
 55}
 56
 57// Perform a "subtraction of ranges". A - B. Not transitive.
 58static NSRange SubtractRange(NSRange a, NSRange b) {
 59  NSRange newRange;
 60  NSUInteger maxRangeA = NSMaxRange(a);
 61  NSUInteger maxRangeB = NSMaxRange(b);
 62  if (b.location == NSNotFound) {
 63    // B is bogus
 64    newRange = a;
 65  } else if (maxRangeB <= a.location) {
 66    // B is completely before A
 67    newRange = NSMakeRange(a.location - b.length, a.length);
 68  } else if (maxRangeA <= b.location) {
 69    // B is completely after A
 70    newRange = a;
 71  } else if (b.location <= a.location && maxRangeB >= maxRangeA) {
 72    // B subsumes A
 73    newRange = NSMakeRange(b.location, 0);
 74  } else if (a.location <= b.location && maxRangeA >= maxRangeB) {
 75    // A subsumes B
 76    newRange = NSMakeRange(a.location, a.length - b.length);
 77  } else if (b.location <= a.location && maxRangeB <= maxRangeA) {
 78    // B overlaps front edge of A
 79    NSUInteger diff = maxRangeB - a.location;
 80    newRange = NSMakeRange(a.location + diff, a.length - diff);
 81  } else if (b.location <= maxRangeA && maxRangeB >= maxRangeA) {
 82    // B overlaps back edge of A
 83    NSUInteger diff = maxRangeA - b.location;
 84    newRange = NSMakeRange(a.location, a.length - diff);
 85  }
 86  return newRange;
 87}
 88
 89+ (BOOL)gdt_writeToFile:(NSString *)fileName
 90                 ofType:(NSString *)type
 91                 object:(id)object {
 92  NSTextStorage *storage = [(id)object textStorage];
 93  id delegate = [storage delegate];
 94
 95  // Need to keep track of all the current selections so that we can replace
 96  // them after stripping off the whitespace. A single source file can have
 97  // multiple views, so we store one selection per view.
 98  NSArray *windowControllers = [delegate windowControllers];
 99  size_t size = sizeof(NSRange) * [windowControllers count];
100  NSRange *ranges = [[NSMutableData dataWithLength:size] mutableBytes];
101  NSUInteger rangeCount = 0;
102  for (id controller in windowControllers) {
103    if ([controller respondsToSelector:@selector(textView)]) {
104      NSTextView *textView = [controller textView];
105      ranges[rangeCount] = [textView selectedRange];
106      rangeCount++;
107    }
108  }
109
110  NSMutableString *text = [[[storage string] mutableCopy] autorelease];
111  NSRange oldRange = NSMakeRange(0, [text length]);
112
113  // Figure out the newlines in our file.
114  NSString *newlineString = @"\n";
115  if ([text rangeOfString:@"\r\n"].length > 0) {
116    newlineString = @"\r\n";
117  } else if ([text rangeOfString:@"\r"].length > 0) {
118    newlineString = @"\r";
119  }
120  NSUInteger newlineStringLength = [newlineString length];
121  NSCharacterSet *whiteSpace
122    = [NSCharacterSet characterSetWithCharactersInString:@" \t"];
123  NSMutableCharacterSet *nonWhiteSpace = [[whiteSpace mutableCopy] autorelease];
124  [nonWhiteSpace invert];
125
126  // If the file is missing a newline at the end, add it now.
127  if (![text hasSuffix:newlineString]) {
128    [text appendString:newlineString];
129  }
130
131  NSRange textRange = NSMakeRange(0, [text length] - 1);
132  while (textRange.length > 0) {
133    NSRange lineRange = [text rangeOfString:newlineString
134                                    options:NSBackwardsSearch
135                                      range:textRange];
136    if (lineRange.location == NSNotFound) {
137      lineRange.location = 0;
138    } else {
139      lineRange.location += newlineStringLength;
140    }
141    lineRange.length = textRange.length - lineRange.location;
142    textRange.length = lineRange.location;
143    if (textRange.length != 0) {
144      textRange.length -= newlineStringLength;
145    }
146
147    NSRange whiteRange = [text rangeOfCharacterFromSet:whiteSpace
148                                               options:NSBackwardsSearch
149                                                 range:lineRange];
150    if (NSMaxRange(whiteRange) == NSMaxRange(lineRange)) {
151      NSRange nonWhiteRange = [text rangeOfCharacterFromSet:nonWhiteSpace
152                                                    options:NSBackwardsSearch
153                                                      range:lineRange];
154      NSRange deleteRange;
155      if (nonWhiteRange.location == NSNotFound) {
156        deleteRange.location = lineRange.location;
157      } else {
158        deleteRange.location = NSMaxRange(nonWhiteRange);
159      }
160      deleteRange.length = NSMaxRange(whiteRange) - deleteRange.location;
161      [text deleteCharactersInRange:deleteRange];
162
163      // Update all the selections appropriately.
164      for (NSUInteger i = 0; i < rangeCount; ++i) {
165        NSRange baseRange = ranges[i];
166        NSRange newRange = SubtractRange(baseRange, deleteRange);
167        ranges[i] = newRange;
168      }
169    }
170  }
171
172  // Replace the text with the new stripped version.
173  [storage beginEditing];
174  [storage replaceCharactersInRange:oldRange withString:text];
175  [storage endEditing];
176
177  // Fix up selections
178  NSUInteger count = 0;
179  for (id controller in windowControllers) {
180    if ([controller respondsToSelector:@selector(textView)]) {
181      NSRange newRange = ranges[count];
182      if (newRange.location != NSNotFound) {
183        NSTextView *textView = [controller textView];
184        [textView setSelectedRange:ranges[count]];
185      }
186      count++;
187    }
188  }
189
190  // Finish the save.
191  return [object gdt_writeToFile:fileName ofType:type];
192}
193
194- (BOOL)gdt_writeToFile:(NSString *)fileName ofType:(NSString *)type {
195  BOOL isGood;
196  // Check our defaults to see if we want to strip whitespace.
197  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
198  if ([defaults boolForKey:GTMXcodeCorrectWhiteSpaceOnSave]) {
199    isGood = [GTMXcodeCorrectWhiteSpace gdt_writeToFile:fileName
200                                                ofType:type
201                                                object:self];
202  } else {
203    isGood = [self gdt_writeToFile:fileName ofType:type];
204  }
205  return isGood;
206}
207
208@end