Bug Summary

File:inputmanager/NSTextView_Bibdesk.m
Location:line 224, column 17
Description:dead store

Annotated Source Code

1//
2// NSTextView_Bibdesk.m
3// BibDeskInputManager
4//
5// Created by Sven-S. Porst on Sat Jul 17 2004.
6/*
7 This software is Copyright (c) 2004-2008
8 Sven-S. Porst. All rights reserved.
9
10 Redistribution and use in source and binary forms, with or without
11 modification, are permitted provided that the following conditions
12 are met:
13
14 - Redistributions of source code must retain the above copyright
15 notice, this list of conditions and the following disclaimer.
16
17 - Redistributions in binary form must reproduce the above copyright
18 notice, this list of conditions and the following disclaimer in
19 the documentation and/or other materials provided with the
20 distribution.
21
22 - Neither the name of Sven-S. Porst nor the names of any
23 contributors may be used to endorse or promote products derived
24 from this software without specific prior written permission.
25
26 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 */
38
39#import "NSTextView_Bibdesk.h"
40#import <Foundation/Foundation.h>
41#import <objc/objc-class.h>
42#import <objc/Protocol.h>
43
44NSString *BDSKInputManagerID = @"net.sourceforge.bibdesk.inputmanager";
45NSString *BDSKInputManagerLoadableApplications = @"Application bundles that we recognize";
46
47static NSString *BDSKInsertionString = nil( ( void * ) 0 );
48
49static NSString *BDSKScriptName = @"Bibdesk";
50static NSString *BDSKScriptType = @"scpt";
51static NSString *BDSKHandlerName = @"getcitekeys";
52
53extern void _objc_resolve_categories_for_class(struct objc_class *cls);
54
55// The functions with an OB prefix are from OmniBase/OBUtilities.m
56// and are covered by the Omni source license, and may only be used or
57// reproduced in accordance with that license.
58// http://www.omnigroup.com/developer/sourcecode/sourcelicense/
59
60static IMP OBBDSKReplaceMethodImplementation(Class aClass, SEL oldSelector, IMP newImp)
61{
62 struct objc_method *thisMethod;
63 IMP oldImp = NULL( ( void * ) 0 );
64 extern void _objc_flush_caches(Class);
65
66 if ((thisMethod = class_getInstanceMethod(aClass, oldSelector))) {
67 oldImp = thisMethod->method_imp;
68
69 // Replace the method in place
70 thisMethod->method_imp = newImp;
71
72 // Flush the method cache
73 _objc_flush_caches(aClass);
74 }
75
76 return oldImp;
77}
78
79static IMP OBBDSKReplaceMethodImplementationWithSelector(Class aClass, SEL oldSelector, SEL newSelector)
80{
81 struct objc_method *newMethod;
82
83 newMethod = class_getInstanceMethod(aClass, newSelector);
84 NSCAssertdo { if ( ! ( ( newMethod != ( ( void * ) 0 ) ) ) ) { [ [ NSAssertionHandler
currentHandler ] handleFailureInFunction : [ NSString stringWithUTF8String
: __PRETTY_FUNCTION__ ] file : [ NSString stringWithUTF8String
: "/Volumes/Local/Users/amaxwell/build/bibdesk-clean/inputmanager/NSTextView_Bibdesk.m"
] lineNumber : 84 description : ( ( @ "new method must not be nil"
) ) , ( 0 ) , ( 0 ) , ( 0 ) , ( 0 ) , ( 0 ) ] ; } } while ( 0
)
(newMethod != nil, @"new method must not be nil");
85
86 return OBBDSKReplaceMethodImplementation(aClass, oldSelector, newMethod->method_imp);
87}
88
89// The compiler won't allow us to call the IMP directly if it returns an NSRange, so I followed Apple's code at
90// http://developer.apple.com/documentation/Performance/Conceptual/CodeSpeed/CodeSpeed.pdf
91// See also the places where Omni uses OBReplaceMethod... calls in OmniAppKit, which is easier to follow.
92static NSRange (*originalRangeIMP)(id, SEL) = NULL( ( void * ) 0 );
93static void (*originalInsertIMP)(id, SEL, NSString *, NSRange, int, BOOL) = NULL( ( void * ) 0 );
94static id (*originalCompletionsIMP)(id, SEL, NSRange, int *) = NULL( ( void * ) 0 );
95
96@implementation NSTextView_Bibdesk
97
98+ (void)load{
99
100 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
101
102 // ARM: we just leak these strings; since the bundle only gets loaded once, it's not worth replacing dealloc
103 if(BDSKInsertionString == nil( ( void * ) 0 ))
104 BDSKInsertionString = [NSLocalizedString[ [ NSBundle mainBundle ] localizedStringForKey : ( @ " (Bibdesk)"
) value : @ "" table : ( ( void * ) 0 ) ]
(@" (Bibdesk)", @"") retain];
105
106 NSString *bundleID = [[NSBundle mainBundle] bundleIdentifier]; // for the app we are loading into
107 NSArray *array = [[[NSUserDefaults standardUserDefaults] persistentDomainForName:BDSKInputManagerID] objectForKey:BDSKInputManagerLoadableApplications];
108
109 NSEnumerator *e = [array objectEnumerator];
110 NSString *str;
111 BOOL yn = NO( BOOL ) 0;
112
113 while(str = [e nextObject]){
114 if([str caseInsensitiveCompare:bundleID] == NSOrderedSame){
115 yn = YES( BOOL ) 1;
116 break;
117 }
118 }
119
120 if(yn && [[self superclass] instancesRespondToSelector:@selector(completionsForPartialWordRange:indexOfSelectedItem:)]){
121
122 // Class posing was cleaner and probably safer than swizzling, but led to unresolved problems with internationalized versions of TeXShop+OgreKit refusing text input for the Ogre find panel. I think this is an OgreKit bug.
123 originalInsertIMP = (typeof(originalInsertIMP))OBBDSKReplaceMethodImplementationWithSelector(self, @selector(insertCompletion:forPartialWordRange:movement:isFinal:), @selector(replacementInsertCompletion:forPartialWordRange:movement:isFinal:));
124 originalRangeIMP = (typeof(originalRangeIMP))OBBDSKReplaceMethodImplementationWithSelector(self,@selector(rangeForUserCompletion),@selector(replacementRangeForUserCompletion));
125
126 // have to replace this one since we don't call the delegate method from our implementation, and we don't want to override unless the user chooses to do so
127 originalCompletionsIMP = (typeof(originalCompletionsIMP))OBBDSKReplaceMethodImplementationWithSelector(self, @selector(completionsForPartialWordRange:indexOfSelectedItem:),@selector(replacementCompletionsForPartialWordRange:indexOfSelectedItem:));
128 }
129
130 [pool release];
131}
132
133@end
134
135@implementation NSTextView (BDSKCompletion)
136
137#pragma mark -
138#pragma mark Reference-searching heuristics
139
140// ** Check to see if it's TeX
141// - look back to see if { ; if no brace, return not TeX
142// - if { found, look back between insertion point and { to find comma; check to see if it's BibTeX, then return the match range
143// ** Check to see if it's BibTeX
144// - look back to see if it's jurabib with }{
145// - look back to see if ] ; if no options, then just find the citecommand (or not) by searching back from {
146// - look back to see if ][ ; if so, set ] range again
147// - look back to find [ starting from ]
148// - now we have the last [, see if there is a cite immediately preceding it using rangeOfString:@"cite" || rangeOfString:@"bibentry"
149// - if there were no brackets, but there was a double curly brace, then check for a jurabib citation
150// ** After all of this, we've searched back to a brace, and then checked for a cite command with two optional parameters
151
152- (BOOL)isBibTeXCitation:(NSRange)braceRange{
153
154 NSString *str = [self string];
155 NSRange citeSearchRange = NSMakeRange(NSNotFound, 0);
156 NSRange doubleBracketRange = NSMakeRange(NSNotFound, 0);
157
158 NSRange rightBracketRange = [str rangeOfString:@"]" options:NSBackwardsSearch | NSLiteralSearch range:SafeBackwardSearchRange(braceRange, 1)]; // see if there are any optional parameters
159
160 // check for jurabib \citefield, which has two mandatory parameters in curly braces, e.g. \citefield[pagerange]{title}{cite:key}
161 NSRange doubleBraceRange = [str rangeOfString:@"}{" options:NSBackwardsSearch | NSLiteralSearch range:SafeBackwardSearchRange( NSMakeRange(braceRange.location + 1, 1), 10)];
162
163 if(rightBracketRange.location == NSNotFound && doubleBraceRange.location == NSNotFound){ // no options and not jurabib, so life is easy; look backwards 10 characters from the brace and see if there's a citecommand
164 citeSearchRange = SafeBackwardSearchRange(braceRange, 20);
165 if([str rangeOfString:@"cite" options:NSBackwardsSearch | NSLiteralSearch range:citeSearchRange].location != NSNotFound ||
166 [str rangeOfString:@"bibentry" options:NSBackwardsSearch | NSLiteralSearch range:citeSearchRange].location != NSNotFound){
167 return YES( BOOL ) 1;
168 } else {
169 return NO( BOOL ) 0;
170 }
171 }
172
173 if(doubleBraceRange.location != NSNotFound) // reset the brace range if we have jurabib
174 braceRange = [str rangeOfString:@"{" options:NSBackwardsSearch | NSLiteralSearch range:SafeBackwardSearchRange(doubleBraceRange, 10)];
175
176 NSRange leftBracketRange = [str rangeOfString:@"[" options:NSBackwardsSearch | NSLiteralSearch range:SafeBackwardSearchRange(braceRange, 100)]; // first occurrence of it, looking backwards
177 // next, see if we have two optional parameters; this range is tricky, since we have to go forward one, then do a safe backward search over the previous characters
178 if(leftBracketRange.location != NSNotFound)
179 doubleBracketRange = [str rangeOfString:@"][" options:NSBackwardsSearch | NSLiteralSearch range:SafeBackwardSearchRange( NSMakeRange(leftBracketRange.location + 1, 3), 3)];
180
181 if(doubleBracketRange.location != NSNotFound) // if we had two parameters, find the last opening bracket
182 leftBracketRange = [str rangeOfString:@"[" options:NSBackwardsSearch | NSLiteralSearch range:SafeBackwardSearchRange(doubleBracketRange, 50)];
183
184 if(leftBracketRange.location != NSNotFound){
185 citeSearchRange = SafeBackwardSearchRange(leftBracketRange, 20); // could be larger
186 if([str rangeOfString:@"cite" options:NSBackwardsSearch | NSLiteralSearch range:citeSearchRange].location != NSNotFound ||
187 [str rangeOfString:@"bibentry" options:NSBackwardsSearch | NSLiteralSearch range:citeSearchRange].location != NSNotFound){
188 return YES( BOOL ) 1;
189 } else {
190 return NO( BOOL ) 0;
191 }
192 }
193
194 if(doubleBraceRange.location != NSNotFound){ // jurabib with no options on it
195 citeSearchRange = SafeBackwardSearchRange(braceRange, 20); // could be larger
196 if([str rangeOfString:@"cite" options:NSBackwardsSearch | NSLiteralSearch range:citeSearchRange].location != NSNotFound ||
197 [str rangeOfString:@"bibentry" options:NSBackwardsSearch | NSLiteralSearch range:citeSearchRange].location != NSNotFound){
198 return YES( BOOL ) 1;
199 } else {
200 return NO( BOOL ) 0;
201 }
202 }
203
204 return NO( BOOL ) 0;
205}
206
207- (NSRange)citeKeyRange{
208
209 NSString *str = [self string];
210 NSRange r = [self selectedRange]; // here's the insertion point
211 NSRange commaRange;
212 NSRange finalRange;
213 unsigned maxLoc;
214
215 NSRange braceRange = [str rangeOfString:@"{" options:NSBackwardsSearch | NSLiteralSearch range:SafeBackwardSearchRange(r, 100)]; // look for an opening brace
216 NSRange closingBraceRange = [str rangeOfString:@"}" options:NSBackwardsSearch | NSLiteralSearch range:SafeBackwardSearchRange(r, 100)];
217
218 if(closingBraceRange.location != NSNotFound && closingBraceRange.location > braceRange.location) // if our { has a matching }, don't bother
219 return finalRange = NSMakeRange(NSNotFound, 0);
220
221 if(braceRange.location != NSNotFound){ // may be TeX
222 commaRange = [str rangeOfString:@"," options:NSBackwardsSearch | NSLiteralSearch range:NSUnionRange(braceRange, r)]; // exclude commas in the optional parameters
223 } else { // definitely not TeX
Although the value stored to 'finalRange' is used in the enclosing expression, the value is never actually read from 'finalRange'
224 return finalRange = NSMakeRange(NSNotFound, 0);
225 }
226
227 if([self isBibTeXCitation:braceRange]){
228 if(commaRange.location != NSNotFound && r.location > commaRange.location){
229 maxLoc = ( (commaRange.location + 1 > r.location) ? commaRange.location : commaRange.location + 1 );
230 finalRange = SafeForwardSearchRange(maxLoc, r.location - commaRange.location - 1, r.location);
231 } else {
232 maxLoc = ( (braceRange.location + 1 > r.location) ? braceRange.location : braceRange.location + 1 );
233 finalRange = SafeForwardSearchRange(maxLoc, r.location - braceRange.location - 1, r.location);
234 }
235 } else {
236 finalRange = NSMakeRange(NSNotFound, 0);
237 }
238
239 return finalRange;
240}
241
242- (NSRange)refLabelRange{
243
244 NSString *s = [self string];
245 NSRange r = [self selectedRange];
246 NSRange searchRange = SafeBackwardSearchRange(r, 12);
247
248 // look for standard \ref
249 NSRange foundRange = [s rangeOfString:@"\\ref{" options:NSBackwardsSearch range:searchRange];
250
251 if(foundRange.location == NSNotFound){
252
253 // maybe it's a pageref
254 foundRange = [s rangeOfString:@"\\pageref{" options:NSBackwardsSearch range:searchRange];
255
256 // could also be an eqref (amsmath)
257 if(foundRange.location == NSNotFound)
258 foundRange = [s rangeOfString:@"\\eqref{" options:NSBackwardsSearch range:searchRange];
259 }
260 unsigned idx = NSMaxRange(foundRange);
261 idx = (idx < r.location ? r.location - idx : 0);
262
263 return NSMakeRange(NSMaxRange(foundRange), idx);
264}
265
266#pragma mark -
267#pragma mark AppKit overrides
268
269// Override usual behaviour so we can have dots, colons and hyphens in our cite keys
270- (NSRange)rangeForBibTeXUserCompletion{
271
272 NSRange range = [self citeKeyRange];
273 return range.location == NSNotFound ? [self refLabelRange] : range;
274}
275
276static BOOL isCompletingTeX = NO( BOOL ) 0;
277
278// we replace this method since the completion controller uses it to update
279- (NSRange)replacementRangeForUserCompletion{
280
281 NSRange range = [self rangeForBibTeXUserCompletion];
282 isCompletingTeX = range.location != NSNotFound;
283
284 return range.location != NSNotFound ? range : originalRangeIMP(self, _cmd);
285}
286
287// this returns -1 instead of NSNotFound for compatibility with the completion controller indexOfSelectedItem parameter
288static inline int
289BDIndexOfItemInArrayWithPrefix(NSArray *array, NSString *prefix)
290{
291 unsigned idx, count = [array count];
292 for(idx = 0; idx < count; idx++){
293 if([[array objectAtIndex:idx] hasPrefix:prefix])
294 return idx;
295 }
296
297 return -1;
298}
299
300// Provide own completions based on results by Bibdesk.
301// Should check whether Bibdesk is available first.
302// Setting initial selection in list to second item doesn't work.
303// Requires X.3
304- (NSArray *)replacementCompletionsForPartialWordRange:(NSRange)charRange indexOfSelectedItem:(int *)index{
305
306 NSString *s = [self string];
307 NSRange refLabelRange = [self refLabelRange];
308
309 // don't bother checking for a citekey if this is a \ref
310 NSRange keyRange = ( (refLabelRange.location == NSNotFound) ? [self citeKeyRange] : NSMakeRange(NSNotFound, 0) );
311 NSMutableArray *returnArray = [NSMutableArray array];
312 int n;
313
314 if(keyRange.location != NSNotFound){
315
316 NSString *end = [[s substringWithRange:keyRange] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
317
318 NSDictionary *errorInfo = nil( ( void * ) 0 );
319 static NSAppleScript *script = nil( ( void * ) 0 );
320 if(script == nil( ( void * ) 0 )){
321 NSURL *scriptURL = [NSURL fileURLWithPath:[[NSBundle bundleWithIdentifier:BDSKInputManagerID] pathForResource:BDSKScriptName ofType: BDSKScriptType]];
322 script = [[NSAppleScript alloc] initWithContentsOfURL:scriptURL error:&errorInfo];
323 if(errorInfo != nil( ( void * ) 0 )){
324 [script release];
325 script = nil( ( void * ) 0 );
326 NSLog(@"*** Failed to initialize script at URL %@ because of error %@", scriptURL, errorInfo);
327 }
328 }
329
330 if (script && !errorInfo) {
331
332 /* We have to construct an AppleEvent descriptor to contain the arguments for our handler call. Remember that this list is 1, rather than 0, based. */
333 NSAppleEventDescriptor *arguments = [[NSAppleEventDescriptor alloc] initListDescriptor];
334 [arguments insertDescriptor: [NSAppleEventDescriptor descriptorWithString:end] atIndex: 1] ;
335
336 /* Call the handler using the method in our special NSAppleScript category */
337 NSAppleEventDescriptor *result = [script callHandler: BDSKHandlerName withArguments: arguments errorInfo: &errorInfo];
338 [arguments release];
339
340 if (!errorInfo) {
341
342 if (result && (n = [result numberOfItems])) {
343 NSAppleEventDescriptor *stringAEDesc;
344 NSString *completionString;
345
346 do {
347 // run through the list top to bottom, keeping in mind it is 1 based.
348 stringAEDesc = [result descriptorAtIndex:n];
349 // insert 'identification string at end so we'll recognise our own completions in -insertCompletion:for...
350 completionString = [[stringAEDesc stringValue] stringByAppendingString:BDSKInsertionString];
351
352 // add in at beginning of array
353 [returnArray insertObject:completionString atIndex:0];
354 } while(--n);
355 }
356 } // no script running error
357 if(errorInfo) NSLog(@"*** Failed to run script %@ because of error %@", script, errorInfo);
358
359 } // no script loading error
360 if(errorInfo) NSLog(@"*** Failed to run script %@ because of error %@", script, errorInfo);
361
362 *index = BDIndexOfItemInArrayWithPrefix(returnArray, end);
363
364 } else if(refLabelRange.location != NSNotFound){
365 NSString *hint = [s substringWithRange:refLabelRange];
366
367 NSScanner *labelScanner = [[NSScanner alloc] initWithString:s];
368 [labelScanner setCharactersToBeSkipped:nil( ( void * ) 0 )];
369 NSString *scanned = nil( ( void * ) 0 );
370 NSMutableSet *setOfLabels = [NSMutableSet setWithCapacity:10];
371 NSString *scanFormat;
372
373 scanFormat = [@"\\label{" stringByAppendingString:hint];
374
375 while(![labelScanner isAtEnd]){
376 [labelScanner scanUpToString:scanFormat intoString:nil( ( void * ) 0 )]; // scan for strings with \label{hint in them
377 [labelScanner scanString:@"\\label{" intoString:nil( ( void * ) 0 )]; // scan away the \label{
378 [labelScanner scanUpToString:@"}" intoString:&scanned]; // scan up to the next brace
379 if(scanned != nil( ( void * ) 0 )) [setOfLabels addObject:[scanned stringByAppendingString:BDSKInsertionString]]; // add it to the set
380 }
381 [labelScanner release];
382 // return the set as an array, sorted alphabetically
383 [returnArray setArray:[[setOfLabels allObjects] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)]];
384 *index = BDIndexOfItemInArrayWithPrefix(returnArray, hint);
385 } else {
386 // return the spellchecker's guesses
387 returnArray = (NSMutableArray *)[[NSSpellChecker sharedSpellChecker] completionsForPartialWordRange:charRange inString:s language:nil( ( void * ) 0 ) inSpellDocumentWithTag:[self spellCheckerDocumentTag]];
388 *index = BDIndexOfItemInArrayWithPrefix(returnArray, [s substringWithRange:charRange]);
389 }
390 return returnArray;
391}
392
393// for legacy reasons, rangeForUserCompletion gives us an incorrect range for replacement; since it's compatible with searching and I don't feel like changing all the range code, we'll fix it up here
394- (void)fixRange:(NSRange *)range{
395 NSString *string = [self string];
396
397 NSRange selRange = [self selectedRange];
398 unsigned minLoc = ( (selRange.location > 100) ? 100 : selRange.location);
399 NSRange safeRange = NSMakeRange(selRange.location - minLoc, minLoc);
400
401 NSRange braceRange = [string rangeOfString:@"{" options:NSBackwardsSearch | NSLiteralSearch range:safeRange]; // look for an opening brace
402 NSRange commaRange = [string rangeOfString:@"," options:NSBackwardsSearch | NSLiteralSearch range:safeRange]; // look for a comma
403 unsigned maxLoc = [[self string] length];
404
405 if(braceRange.location != NSNotFound && braceRange.location < range->location){
406 // we found the brace, which must exist if we're here; if not, we won't adjust anything, though
407 if(commaRange.location != NSNotFound && commaRange.location > braceRange.location)
408 range->location = MIN( { __typeof__ ( commaRange . location + 1 ) __a = ( commaRange
. location + 1 ) ; __typeof__ ( maxLoc ) __b = ( maxLoc ) ; __a
< __b ? __a : __b ; } )
(commaRange.location + 1, maxLoc);
409 else
410 range->location = MIN( { __typeof__ ( braceRange . location + 1 ) __a = ( braceRange
. location + 1 ) ; __typeof__ ( maxLoc ) __b = ( maxLoc ) ; __a
< __b ? __a : __b ; } )
(braceRange.location + 1, maxLoc);
411 }
412}
413
414// finish off the completion, inserting just the cite key
415- (void)replacementInsertCompletion:(NSString *)word forPartialWordRange:(NSRange)charRange movement:(int)movement isFinal:(BOOL)flag {
416
417 if(isCompletingTeX || [self refLabelRange].location != NSNotFound)
418 [self fixRange:&charRange];
419
420 if (flag == YES( BOOL ) 1 && ([word rangeOfString:BDSKInsertionString].location != NSNotFound)) {
421 // this is one of our suggestions, so we need to trim it
422 // strip the comment for this, this assumes cite keys can't have spaces in them
423 NSRange firstSpace = [word rangeOfString:@" "];
424 word = [word substringToIndex:firstSpace.location];
425 }
426 originalInsertIMP(self, _cmd, word, charRange, movement, flag);
427}
428
429@end