The R Project SVN R-packages

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
5705 urbaneks 1
/*
2
 *  R.app : a Cocoa front end to: "R A Computer Language for Statistical Data Analysis"
3
 *  
4
 *  R.app Copyright notes:
5
 *                     Copyright (C) 2004-5  The R Foundation
6
 *                     written by Stefano M. Iacus and Simon Urbanek
7
 *
8
 *                  
9
 *  R Copyright notes:
10
 *                     Copyright (C) 1995-1996   Robert Gentleman and Ross Ihaka
11
 *                     Copyright (C) 1998-2001   The R Development Core Team
12
 *                     Copyright (C) 2002-2004   The R Foundation
13
 *
14
 *  This program is free software; you can redistribute it and/or modify
15
 *  it under the terms of the GNU General Public License as published by
16
 *  the Free Software Foundation; either version 2 of the License, or
17
 *  (at your option) any later version.
18
 *
19
 *  This program is distributed in the hope that it will be useful,
20
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
 *  GNU General Public License for more details.
23
 *
24
 *  A copy of the GNU General Public License is available via WWW at
25
 *  http://www.gnu.org/copyleft/gpl.html.  You can also obtain it by
26
 *  writing to the Free Software Foundation, Inc., 59 Temple Place,
27
 *  Suite 330, Boston, MA  02111-1307  USA.
28
 *
29
 *  RScriptEditorTextView.m
30
 *
31
 *  Created by Hans-J. Bibiko on 15/02/2011.
32
 *
33
 */
34
 
35
#import "RScriptEditorTextView.h"
6128 bibiko 36
#import "RScriptEditorTextStorage.h"
6233 bibiko 37
#import "RScriptEditorTypeSetter.h"
6128 bibiko 38
#import "RScriptEditorLayoutManager.h"
5705 urbaneks 39
#import "RGUI.h"
6238 bibiko 40
#import "Tools/RTooltip.h"
5705 urbaneks 41
 
42
 
43
#pragma mark -
44
#pragma mark flex init
45
 
46
/**
47
 * Include all the extern variables and prototypes required for flex (used for syntax highlighting)
48
 */
49
 
50
#import "RScriptEditorTokens.h"
5995 bibiko 51
#import "RdScriptEditorTokens.h"
5705 urbaneks 52
 
5995 bibiko 53
// R lexer
5705 urbaneks 54
extern NSUInteger yylex();
55
extern NSUInteger yyuoffset, yyuleng;
56
typedef struct yy_buffer_state *YY_BUFFER_STATE;
57
void yy_switch_to_buffer(YY_BUFFER_STATE);
58
YY_BUFFER_STATE yy_scan_string (const char *);
59
 
5995 bibiko 60
// Rd lexer
61
extern NSUInteger rdlex();
62
typedef struct rd_buffer_state *RD_BUFFER_STATE;
63
void rd_switch_to_buffer(RD_BUFFER_STATE);
64
RD_BUFFER_STATE rd_scan_string (const char *);
65
 
6233 bibiko 66
static SEL _foldedSel;
5995 bibiko 67
 
5705 urbaneks 68
#pragma mark -
69
#pragma mark attribute definition 
70
 
71
#define kAPlinked      @"Linked" // attribute for a via auto-pair inserted char
72
#define kAPval         @"linked"
73
#define kLEXToken      @"Quoted" // set via lex to indicate a quoted string
74
#define kLEXTokenValue @"isMarked"
75
#define kRkeyword      @"s"      // attribute for found R keywords
76
#define kQuote         @"Quote"
77
#define kQuoteValue    @"isQuoted"
78
#define kValue         @"x"
79
#define kBTQuote       @"BTQuote"
80
#define kBTQuoteValue  @"isBTQuoted"
81
 
82
#pragma mark -
83
 
6233 bibiko 84
#define R_SYNTAX_HILITE_BIAS 2000
5705 urbaneks 85
#define R_MAX_TEXT_SIZE_FOR_SYNTAX_HIGHLIGHTING 20000000
86
 
87
 
88
static inline const char* NSStringUTF8String(NSString* self) 
89
{
90
	typedef const char* (*SPUTF8StringMethodPtr)(NSString*, SEL);
91
	static SPUTF8StringMethodPtr SPNSStringGetUTF8String;
92
	if (!SPNSStringGetUTF8String) SPNSStringGetUTF8String = (SPUTF8StringMethodPtr)[NSString instanceMethodForSelector:@selector(UTF8String)];
93
	const char* to_return = SPNSStringGetUTF8String(self, @selector(UTF8String));
94
	return to_return;
95
}
96
 
97
static inline void NSMutableAttributedStringAddAttributeValueRange (NSMutableAttributedString* self, NSString* aStr, id aValue, NSRange aRange) 
98
{
99
	typedef void (*SPMutableAttributedStringAddAttributeValueRangeMethodPtr)(NSMutableAttributedString*, SEL, NSString*, id, NSRange);
100
	static SPMutableAttributedStringAddAttributeValueRangeMethodPtr SPMutableAttributedStringAddAttributeValueRange;
101
	if (!SPMutableAttributedStringAddAttributeValueRange) SPMutableAttributedStringAddAttributeValueRange = (SPMutableAttributedStringAddAttributeValueRangeMethodPtr)[self methodForSelector:@selector(addAttribute:value:range:)];
102
	SPMutableAttributedStringAddAttributeValueRange(self, @selector(addAttribute:value:range:), aStr, aValue, aRange);
103
	return;
104
}
105
 
106
static inline id NSMutableAttributedStringAttributeAtIndex (NSMutableAttributedString* self, NSString* aStr, NSUInteger index, NSRangePointer range) 
107
{
108
	typedef id (*SPMutableAttributedStringAttributeAtIndexMethodPtr)(NSMutableAttributedString*, SEL, NSString*, NSUInteger, NSRangePointer);
109
	static SPMutableAttributedStringAttributeAtIndexMethodPtr SPMutableAttributedStringAttributeAtIndex;
110
	if (!SPMutableAttributedStringAttributeAtIndex) SPMutableAttributedStringAttributeAtIndex = (SPMutableAttributedStringAttributeAtIndexMethodPtr)[self methodForSelector:@selector(attribute:atIndex:effectiveRange:)];
111
	id r = SPMutableAttributedStringAttributeAtIndex(self, @selector(attribute:atIndex:effectiveRange:), aStr, index, range);
112
	return r;
113
}
114
 
115
 
116
@implementation RScriptEditorTextView
117
 
118
- (void)awakeFromNib
119
{
6128 bibiko 120
 
5910 urbaneks 121
	// we are a subclass of RTextView which has its own awake and we must call it
122
	[super awakeFromNib];
6128 bibiko 123
 
5705 urbaneks 124
	SLog(@"RScriptEditorTextView: awakeFromNib <%@>", self);
6128 bibiko 125
 
6248 bibiko 126
	selfDelegate = (RDocumentWinCtrl*)[self delegate];
127
 
6232 bibiko 128
	breakSyntaxHighlighting = 0;
6233 bibiko 129
	_foldedSel = @selector(foldedAtIndex:);
130
 
6229 bibiko 131
	// Bind scrollView programmatically - if done in RDocument.xib this'd lead to
132
	// calling awakeFromNib twice
133
	id scrView = (NSScrollView *)self.superview.superview;
134
	if ([scrView isKindOfClass:[NSScrollView class]]) {
135
		if(scrollView) [scrollView release];
136
		scrollView = [scrView retain];
137
		SLog(@"RScriptEditorTextView:awakeFromNib set scrollView");
138
	}
139
 
5705 urbaneks 140
	prefs = [[NSUserDefaults standardUserDefaults] retain];
141
	[[Preferences sharedPreferences] addDependent:self];
142
 
6128 bibiko 143
	lineNumberingEnabled = [Preferences flagForKey:showLineNumbersKey withDefault:NO];
144
 
6249 bibiko 145
	// Init textStorage and set self as delegate for the textView's textStorage to enable
146
	// syntax highlighting, folding etc.
147
	theTextStorage = [[RScriptEditorTextStorage alloc] initWithDelegate:self];
6128 bibiko 148
 
6233 bibiko 149
	_foldedImp = [theTextStorage methodForSelector:_foldedSel];
6128 bibiko 150
 
151
	// Make sure using foldingLayoutManager
6238 bibiko 152
	if (![[self layoutManager] isKindOfClass:[RScriptEditorLayoutManager class]]) {
153
		RScriptEditorLayoutManager *layoutManager = [[RScriptEditorLayoutManager alloc] init];
154
		[[self textContainer] replaceLayoutManager:layoutManager];
155
		[layoutManager release];
156
	}
6128 bibiko 157
 
158
	// disabled to get the current text range in textView safer
159
	[[self layoutManager] setBackgroundLayoutEnabled:NO];
160
	[[self layoutManager] replaceTextStorage:theTextStorage];
161
 
6238 bibiko 162
	[(RScriptEditorTypeSetter*)[[self layoutManager] typesetter] setTextStorage:theTextStorage];
6233 bibiko 163
 
6128 bibiko 164
	isSyntaxHighlighting = NO;
165
 
5724 urbaneks 166
	if([prefs objectForKey:highlightCurrentLine] == nil) [prefs setBool:YES forKey:highlightCurrentLine];
167
	if([prefs objectForKey:indentNewLines] == nil) [prefs setBool:YES forKey:indentNewLines];
168
 
5705 urbaneks 169
	[self setFont:[Preferences unarchivedObjectForKey:RScriptEditorDefaultFont withDefault:[NSFont fontWithName:@"Monaco" size:11]]];
170
 
171
	// Set defaults for general usage
5721 urbaneks 172
	braceHighlightInterval = [Preferences floatForKey:HighlightIntervalKey withDefault:0.3f];
5705 urbaneks 173
	argsHints = [Preferences flagForKey:prefShowArgsHints withDefault:YES];
174
	lineWrappingEnabled = [Preferences flagForKey:enableLineWrappingKey withDefault:YES];
175
	syntaxHighlightingEnabled = [Preferences flagForKey:showSyntaxColoringKey withDefault:YES];
176
 
177
	deleteBackward = NO;
178
	startListeningToBoundChanges = NO;
179
	currentHighlight = -1;
180
 
181
	// For now replaced selectedTextBackgroundColor by redColor
182
	highlightColorAttr = [[NSDictionary alloc] initWithObjectsAndKeys:[NSColor redColor], NSBackgroundColorAttributeName, nil];
183
 
6248 bibiko 184
	if([selfDelegate isRdDocument])
185
		editorToolbar = [[RdEditorToolbar alloc] initWithEditor:selfDelegate];
5993 bibiko 186
	else
6248 bibiko 187
		editorToolbar = [[REditorToolbar alloc] initWithEditor:selfDelegate];
5705 urbaneks 188
 
189
	[self setAllowsDocumentBackgroundColorChange:YES];
190
	[self setContinuousSpellCheckingEnabled:NO];
191
 
5756 bibiko 192
	if(![Preferences flagForKey:enableLineWrappingKey withDefault: YES])
193
		[scrollView setHasHorizontalScroller:YES];
194
 
5805 bibiko 195
	if(!lineWrappingEnabled)
196
		[self updateLineWrappingMode];
197
 
5705 urbaneks 198
	// Re-define tab stops for a better editing
199
	[self setTabStops];
200
 
201
	NSColor *c = [Preferences unarchivedObjectForKey:normalSyntaxColorKey withDefault:nil];
202
	if (c) shColorNormal = c;
203
	else shColorNormal=[NSColor colorWithDeviceRed:0.025 green:0.085 blue:0.600 alpha:1.0];
204
	[shColorNormal retain];
205
 
206
	c=[Preferences unarchivedObjectForKey:stringSyntaxColorKey withDefault:nil];
207
	if (c) shColorString = c;
208
	else shColorString=[NSColor colorWithDeviceRed:0.690 green:0.075 blue:0.000 alpha:1.0];
209
	[shColorString retain];	
210
 
211
	c=[Preferences unarchivedObjectForKey:numberSyntaxColorKey withDefault:nil];
212
	if (c) shColorNumber = c;
213
	else shColorNumber=[NSColor colorWithDeviceRed:0.020 green:0.320 blue:0.095 alpha:1.0];
214
	[shColorNumber retain];
215
 
216
	c=[Preferences unarchivedObjectForKey:keywordSyntaxColorKey withDefault:nil];
217
	if (c) shColorKeyword = c;
218
	else shColorKeyword=[NSColor colorWithDeviceRed:0.765 green:0.535 blue:0.035 alpha:1.0];
219
	[shColorKeyword retain];
220
 
221
	c=[Preferences unarchivedObjectForKey:commentSyntaxColorKey withDefault:nil];
222
	if (c) shColorComment = c;
223
	else shColorComment=[NSColor colorWithDeviceRed:0.312 green:0.309 blue:0.309 alpha:1.0];
224
	[shColorComment retain];
225
 
226
	c=[Preferences unarchivedObjectForKey:identifierSyntaxColorKey withDefault:nil];
227
	if (c) shColorIdentifier = c;
228
	else shColorIdentifier=[NSColor colorWithDeviceRed:0.0 green:0.0 blue:0.0 alpha:1.0];
229
	[shColorIdentifier retain]; 
230
 
6063 bibiko 231
	c=[Preferences unarchivedObjectForKey:editorSelectionBackgroundColorKey withDefault:nil];
6090 bibiko 232
	if (!c) c=[NSColor colorWithDeviceRed:0.71f green:0.835f blue:1.0f alpha:1.0f];
6063 bibiko 233
	NSMutableDictionary *attr = [NSMutableDictionary dictionary];
234
	[attr setDictionary:[self selectedTextAttributes]];
6090 bibiko 235
	[attr setObject:c forKey:NSBackgroundColorAttributeName];
6063 bibiko 236
	[self setSelectedTextAttributes:attr];
237
 
6029 bibiko 238
	// Rd stuff
239
	// c=[Preferences unarchivedObjectForKey:sectionRdSyntaxColorKey withDefault:nil];
240
	// if (c) rdColorSection = c;
241
	// else rdColorSection=[NSColor colorWithDeviceRed:0.8 green:0.0353 blue:0.02 alpha:1.0];
242
	// [rdColorSection retain];
243
	// 
244
	// c=[Preferences unarchivedObjectForKey:macroArgRdSyntaxColorKey withDefault:nil];
245
	// if (c) rdColorMacroArg = c;
246
	// else rdColorMacroArg=[NSColor colorWithDeviceRed:0.0 green:0.0 blue:0.98 alpha:1.0];
247
	// [rdColorMacroArg retain];
248
	// 
249
	// c=[Preferences unarchivedObjectForKey:macroGenRdSyntaxColorKey withDefault:nil];
250
	// if (c) rdColorMacroGen = c;
251
	// else rdColorMacroGen=[NSColor colorWithDeviceRed:0.4 green:0.78 blue:0.98 alpha:1.0];
252
	// [rdColorMacroGen retain]; 
253
	// 
254
	// c=[Preferences unarchivedObjectForKey:directiveRdSyntaxColorKey withDefault:nil];
255
	// if (c) rdColorDirective = c;
256
	// else rdColorDirective=[NSColor colorWithDeviceRed:0.0 green:0.785 blue:0.0 alpha:1.0];
257
	// [rdColorDirective retain]; 
6001 bibiko 258
 
6029 bibiko 259
	// c=[Preferences unarchivedObjectForKey:commentRdSyntaxColorKey withDefault:nil];
260
	// if (c) rdColorComment = c;
261
	// else rdColorComment=[NSColor colorWithDeviceRed:0.1 green:0.55 blue:0.05 alpha:1.0];
262
	// [rdColorComment retain];
6001 bibiko 263
 
6029 bibiko 264
	// c=[Preferences unarchivedObjectForKey:normalRdSyntaxColorKey withDefault:nil];
265
	// if (c) rdColorNormal = c;
266
	// else rdColorNormal=[NSColor colorWithDeviceRed:0.0 green:0.0 blue:0.0 alpha:1.0];
267
	// [rdColorNormal retain]; 
6001 bibiko 268
 
269
 
5721 urbaneks 270
	c=[Preferences unarchivedObjectForKey:editorBackgroundColorKey withDefault:nil];
271
	if (c) shColorBackground = c;
272
	else shColorBackground=[NSColor colorWithDeviceRed:1.0 green:1.0 blue:1.0 alpha:1.0];
273
	[shColorBackground retain]; 
274
 
275
	c=[Preferences unarchivedObjectForKey:editorCurrentLineBackgroundColorKey withDefault:nil];
276
	if (c) shColorCurrentLine = c;
277
	else shColorCurrentLine=[NSColor colorWithDeviceRed:0.9 green:0.9 blue:0.9 alpha:0.8];
278
	[shColorCurrentLine retain]; 
279
 
280
	c=[Preferences unarchivedObjectForKey:editorCursorColorKey withDefault:nil];
281
	if (c) shColorCursor = c;
282
	else shColorCursor=[NSColor blackColor];
283
	[shColorCursor retain]; 
284
	[self setInsertionPointColor:shColorCursor];
285
 
5705 urbaneks 286
	// Register observers for the when editor background colors preference changes
287
	[prefs addObserver:self forKeyPath:normalSyntaxColorKey options:NSKeyValueObservingOptionNew context:NULL];
288
	[prefs addObserver:self forKeyPath:stringSyntaxColorKey options:NSKeyValueObservingOptionNew context:NULL];
289
	[prefs addObserver:self forKeyPath:numberSyntaxColorKey options:NSKeyValueObservingOptionNew context:NULL];
290
	[prefs addObserver:self forKeyPath:keywordSyntaxColorKey options:NSKeyValueObservingOptionNew context:NULL];
291
	[prefs addObserver:self forKeyPath:commentSyntaxColorKey options:NSKeyValueObservingOptionNew context:NULL];
6029 bibiko 292
	// [prefs addObserver:self forKeyPath:normalRdSyntaxColorKey options:NSKeyValueObservingOptionNew context:NULL];
293
	// [prefs addObserver:self forKeyPath:commentRdSyntaxColorKey options:NSKeyValueObservingOptionNew context:NULL];
294
	// [prefs addObserver:self forKeyPath:sectionRdSyntaxColorKey options:NSKeyValueObservingOptionNew context:NULL];
295
	// [prefs addObserver:self forKeyPath:macroArgRdSyntaxColorKey options:NSKeyValueObservingOptionNew context:NULL];
296
	// [prefs addObserver:self forKeyPath:macroGenRdSyntaxColorKey options:NSKeyValueObservingOptionNew context:NULL];
297
	// [prefs addObserver:self forKeyPath:directiveRdSyntaxColorKey options:NSKeyValueObservingOptionNew context:NULL];
5721 urbaneks 298
	[prefs addObserver:self forKeyPath:editorBackgroundColorKey options:NSKeyValueObservingOptionNew context:NULL];
299
	[prefs addObserver:self forKeyPath:editorCurrentLineBackgroundColorKey options:NSKeyValueObservingOptionNew context:NULL];
5705 urbaneks 300
	[prefs addObserver:self forKeyPath:identifierSyntaxColorKey options:NSKeyValueObservingOptionNew context:NULL];
5721 urbaneks 301
	[prefs addObserver:self forKeyPath:editorCursorColorKey options:NSKeyValueObservingOptionNew context:NULL];
5705 urbaneks 302
	[prefs addObserver:self forKeyPath:showSyntaxColoringKey options:NSKeyValueObservingOptionNew context:NULL];
303
	[prefs addObserver:self forKeyPath:prefShowArgsHints options:NSKeyValueObservingOptionNew context:NULL];
304
	[prefs addObserver:self forKeyPath:enableLineWrappingKey options:NSKeyValueObservingOptionNew context:NULL];
5721 urbaneks 305
	[prefs addObserver:self forKeyPath:RScriptEditorDefaultFont options:NSKeyValueObservingOptionNew context:NULL];
306
	[prefs addObserver:self forKeyPath:HighlightIntervalKey options:NSKeyValueObservingOptionNew context:NULL];
5724 urbaneks 307
	[prefs addObserver:self forKeyPath:highlightCurrentLine options:NSKeyValueObservingOptionNew context:NULL];
5805 bibiko 308
	[prefs addObserver:self forKeyPath:showLineNumbersKey options:NSKeyValueObservingOptionNew context:NULL];
6063 bibiko 309
	[prefs addObserver:self forKeyPath:editorSelectionBackgroundColorKey options:NSKeyValueObservingOptionNew context:NULL];
5705 urbaneks 310
 
6228 bibiko 311
	if(syntaxHighlightingEnabled) {
312
		[self setTextColor:shColorNormal];
313
		[self setInsertionPointColor:shColorCursor];
314
	} else {
315
		[self setTextColor:[NSColor blackColor]];
316
		[self setInsertionPointColor:[NSColor blackColor]];
317
	}
6128 bibiko 318
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
319
	[[self layoutManager] setAllowsNonContiguousLayout:YES];
320
#endif
321
 
6245 bibiko 322
	// add NSViewBoundsDidChangeNotification to scrollView
323
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(boundsDidChangeNotification:) name:NSViewBoundsDidChangeNotification object:[scrollView contentView]];
324
 
5705 urbaneks 325
}
326
 
327
- (void)dealloc {
328
	SLog(@"RScriptEditorTextView: dealloc <%@>", self);
329
 
6128 bibiko 330
	[theTextStorage release];
331
 
6229 bibiko 332
	if(scrollView) [scrollView release];
5705 urbaneks 333
	if(editorToolbar) [editorToolbar release];
334
 
335
	if(highlightColorAttr) [highlightColorAttr release];
336
 
337
	if(shColorNormal) [shColorNormal release];
338
	if(shColorString) [shColorString release];
339
	if(shColorNumber) [shColorNumber release];
340
	if(shColorKeyword) [shColorKeyword release];
341
	if(shColorComment) [shColorComment release];
342
	if(shColorIdentifier) [shColorIdentifier release];
5721 urbaneks 343
	if(shColorBackground) [shColorBackground release];
344
	if(shColorCurrentLine) [shColorCurrentLine release];
345
	if(shColorCursor) [shColorCursor release];
5705 urbaneks 346
 
6029 bibiko 347
	// if(rdColorNormal) [rdColorNormal release];
348
	// if(rdColorComment) [rdColorComment release];
349
	// if(rdColorSection) [rdColorSection release];
350
	// if(rdColorMacroArg) [rdColorMacroArg release];
351
	// if(rdColorMacroGen) [rdColorMacroGen release];
352
	// if(rdColorDirective) [rdColorDirective release];
6001 bibiko 353
 
5705 urbaneks 354
	[[NSNotificationCenter defaultCenter] removeObserver:self];
355
	[[Preferences sharedPreferences] removeDependent:self];
356
	if(prefs) [prefs release];
357
 
358
	[super dealloc];
359
 
360
}
361
 
6240 bibiko 362
- (id)scrollView
363
{
364
	return scrollView;
365
}
366
 
6232 bibiko 367
- (void)setNonSyntaxHighlighting
368
{
369
	[theTextStorage removeAttribute:NSForegroundColorAttributeName range:NSMakeRange(0, [[theTextStorage string] length])];
370
	[theTextStorage removeAttribute:NSBackgroundColorAttributeName range:NSMakeRange(0, [[theTextStorage string] length])];
371
	[self setTextColor:[NSColor blackColor]];
372
	[self setInsertionPointColor:[NSColor blackColor]];
6240 bibiko 373
	[self setNeedsDisplayInRect:[self visibleRect]];
6232 bibiko 374
}
375
 
5705 urbaneks 376
/**
377
 * This method is called as part of Key Value Observing which is used to watch for prefernce changes which effect the interface.
378
 */
379
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
380
{
381
 
382
	if ([keyPath isEqualToString:normalSyntaxColorKey]) {
383
		if(shColorNormal) [shColorNormal release];
384
		shColorNormal = [[NSUnarchiver unarchiveObjectWithData:[change objectForKey:NSKeyValueChangeNewKey]] retain];
5721 urbaneks 385
		if([self isEditable])
5705 urbaneks 386
			[self performSelector:@selector(doSyntaxHighlighting) withObject:nil afterDelay:0.1];
387
	} else if ([keyPath isEqualToString:stringSyntaxColorKey]) {
388
		if(shColorString) [shColorString release];
389
		shColorString = [[NSUnarchiver unarchiveObjectWithData:[change objectForKey:NSKeyValueChangeNewKey]] retain];
5721 urbaneks 390
		if([self isEditable])
5705 urbaneks 391
			[self performSelector:@selector(doSyntaxHighlighting) withObject:nil afterDelay:0.1];
392
	} else if ([keyPath isEqualToString:numberSyntaxColorKey]) {
393
		if(shColorNumber) [shColorNumber release];
394
		shColorNumber = [[NSUnarchiver unarchiveObjectWithData:[change objectForKey:NSKeyValueChangeNewKey]] retain];
5721 urbaneks 395
		if([self isEditable])
5705 urbaneks 396
			[self performSelector:@selector(doSyntaxHighlighting) withObject:nil afterDelay:0.1];
397
	} else if ([keyPath isEqualToString:keywordSyntaxColorKey]) {
398
		if(shColorKeyword) [shColorKeyword release];
399
		shColorKeyword = [[NSUnarchiver unarchiveObjectWithData:[change objectForKey:NSKeyValueChangeNewKey]] retain];
5721 urbaneks 400
		if([self isEditable])
5705 urbaneks 401
			[self performSelector:@selector(doSyntaxHighlighting) withObject:nil afterDelay:0.1];
402
	} else if ([keyPath isEqualToString:commentSyntaxColorKey]) {
403
		if(shColorComment) [shColorComment release];
404
		shColorComment = [[NSUnarchiver unarchiveObjectWithData:[change objectForKey:NSKeyValueChangeNewKey]] retain];
5721 urbaneks 405
		if([self isEditable])
5705 urbaneks 406
			[self performSelector:@selector(doSyntaxHighlighting) withObject:nil afterDelay:0.1];
407
	} else if ([keyPath isEqualToString:identifierSyntaxColorKey]) {
408
		if(shColorIdentifier) [shColorIdentifier release];
409
		shColorIdentifier = [[NSUnarchiver unarchiveObjectWithData:[change objectForKey:NSKeyValueChangeNewKey]] retain];
5721 urbaneks 410
		if([self isEditable])
5705 urbaneks 411
			[self performSelector:@selector(doSyntaxHighlighting) withObject:nil afterDelay:0.1];
6029 bibiko 412
	// } else if ([keyPath isEqualToString:sectionRdSyntaxColorKey]) {
413
	// 	if(rdColorSection) [rdColorSection release];
414
	// 	rdColorSection = [[NSUnarchiver unarchiveObjectWithData:[change objectForKey:NSKeyValueChangeNewKey]] retain];
415
	// 	if([self isEditable])
416
	// 		[self performSelector:@selector(doSyntaxHighlighting) withObject:nil afterDelay:0.1];
417
	// } else if ([keyPath isEqualToString:macroArgRdSyntaxColorKey]) {
418
	// 	if(rdColorMacroArg) [rdColorMacroArg release];
419
	// 	rdColorMacroArg = [[NSUnarchiver unarchiveObjectWithData:[change objectForKey:NSKeyValueChangeNewKey]] retain];
420
	// 	if([self isEditable])
421
	// 		[self performSelector:@selector(doSyntaxHighlighting) withObject:nil afterDelay:0.1];
422
	// } else if ([keyPath isEqualToString:macroGenRdSyntaxColorKey]) {
423
	// 	if(rdColorMacroGen) [rdColorMacroGen release];
424
	// 	rdColorMacroGen = [[NSUnarchiver unarchiveObjectWithData:[change objectForKey:NSKeyValueChangeNewKey]] retain];
425
	// 	if([self isEditable])
426
	// 		[self performSelector:@selector(doSyntaxHighlighting) withObject:nil afterDelay:0.1];
427
	// } else if ([keyPath isEqualToString:directiveRdSyntaxColorKey]) {
428
	// 	if(rdColorDirective) [rdColorDirective release];
429
	// 	rdColorDirective = [[NSUnarchiver unarchiveObjectWithData:[change objectForKey:NSKeyValueChangeNewKey]] retain];
430
	// 	if([self isEditable])
431
	// 		[self performSelector:@selector(doSyntaxHighlighting) withObject:nil afterDelay:0.1];
5721 urbaneks 432
	} else if ([keyPath isEqualToString:editorCursorColorKey]) {
433
		if(shColorCursor) [shColorCursor release];
434
		shColorCursor = [[NSUnarchiver unarchiveObjectWithData:[change objectForKey:NSKeyValueChangeNewKey]] retain];
435
		[self setInsertionPointColor:shColorCursor];
6240 bibiko 436
		[self setNeedsDisplayInRect:[self visibleRect]];
5721 urbaneks 437
	} else if ([keyPath isEqualToString:identifierSyntaxColorKey]) {
438
		if(shColorIdentifier) [shColorIdentifier release];
439
		shColorIdentifier = [[NSUnarchiver unarchiveObjectWithData:[change objectForKey:NSKeyValueChangeNewKey]] retain];
440
		if([self isEditable])
441
			[self performSelector:@selector(doSyntaxHighlighting) withObject:nil afterDelay:0.1];
442
	} else if ([keyPath isEqualToString:editorBackgroundColorKey]) {
443
		if(shColorBackground) [shColorBackground release];
444
		shColorBackground = [[NSUnarchiver unarchiveObjectWithData:[change objectForKey:NSKeyValueChangeNewKey]] retain];
6240 bibiko 445
		[self setNeedsDisplayInRect:[self visibleRect]];
5721 urbaneks 446
	} else if ([keyPath isEqualToString:editorCurrentLineBackgroundColorKey]) {
447
		if(shColorCurrentLine) [shColorCurrentLine release];
448
		shColorCurrentLine = [[NSUnarchiver unarchiveObjectWithData:[change objectForKey:NSKeyValueChangeNewKey]] retain];
6240 bibiko 449
		[self setNeedsDisplayInRect:[self visibleRect]];
6063 bibiko 450
	} else if ([keyPath isEqualToString:editorSelectionBackgroundColorKey]) {
451
		NSColor *c = [[NSUnarchiver unarchiveObjectWithData:[change objectForKey:NSKeyValueChangeNewKey]] retain];
452
		NSMutableDictionary *attr = [NSMutableDictionary dictionary];
453
		[attr setDictionary:[self selectedTextAttributes]];
6070 bibiko 454
		[attr setObject:c forKey:NSBackgroundColorAttributeName];
6063 bibiko 455
		[self setSelectedTextAttributes:attr];
6240 bibiko 456
		[self setNeedsDisplayInRect:[self visibleRect]];
5721 urbaneks 457
 
5705 urbaneks 458
	} else if ([keyPath isEqualToString:showSyntaxColoringKey]) {
459
		syntaxHighlightingEnabled = [[change objectForKey:NSKeyValueChangeNewKey] boolValue];
460
		if(syntaxHighlightingEnabled) {
6232 bibiko 461
			[self performSelector:@selector(doSyntaxHighlighting) withObject:nil afterDelay:0.05f];
5705 urbaneks 462
		} else {
6232 bibiko 463
			[self performSelector:@selector(setNonSyntaxHighlighting) withObject:nil afterDelay:0.05f];
5705 urbaneks 464
		}
465
	} else if ([keyPath isEqualToString:enableLineWrappingKey]) {
5756 bibiko 466
		[self updateLineWrappingMode];
6238 bibiko 467
		[self performSelector:@selector(doSyntaxHighlighting) withObject:nil afterDelay:0.05f];
468
		[self setNeedsDisplay:YES];
5705 urbaneks 469
 
5805 bibiko 470
	} else if ([keyPath isEqualToString:showLineNumbersKey]) {
471
		lineNumberingEnabled = [[change objectForKey:NSKeyValueChangeNewKey] boolValue];
472
		if(lineNumberingEnabled) {
6128 bibiko 473
			NoodleLineNumberView *theRulerView = [[NoodleLineNumberView alloc] initWithScrollView:scrollView];
474
			[scrollView setVerticalRulerView:theRulerView];
5805 bibiko 475
			[scrollView setHasHorizontalRuler:NO];
476
			[scrollView setHasVerticalRuler:YES];
477
			[scrollView setRulersVisible:YES];
6128 bibiko 478
			[theRulerView release];
6238 bibiko 479
			[(NoodleLineNumberView*)[[self enclosingScrollView] verticalRulerView] setLineWrappingMode:[Preferences flagForKey:enableLineWrappingKey withDefault: YES]];
5805 bibiko 480
		} else {
481
			[scrollView setHasHorizontalRuler:NO];
482
			[scrollView setHasVerticalRuler:NO];
483
			[scrollView setRulersVisible:NO];
484
		}
485
		[self setNeedsDisplay:YES];
486
 
5705 urbaneks 487
	} else if ([keyPath isEqualToString:prefShowArgsHints]) {
488
		argsHints = [[change objectForKey:NSKeyValueChangeNewKey] boolValue];
489
		if(!argsHints) {
6248 bibiko 490
			[selfDelegate setStatusLineText:@""];
5770 bibiko 491
		} else {
492
			[self currentFunctionHint];
5705 urbaneks 493
		}
5721 urbaneks 494
 
495
	} else if ([keyPath isEqualToString:highlightCurrentLine]) {
6240 bibiko 496
		[self setNeedsDisplayInRect:[self visibleRect]];
5721 urbaneks 497
 
5741 urbaneks 498
	} else if ([keyPath isEqualToString:RScriptEditorDefaultFont] && ![[[[self window] windowController] document] isRTF] && ![self selectedRange].length) {
499
			[self setFont:[NSUnarchiver unarchiveObjectWithData:[change objectForKey:NSKeyValueChangeNewKey]]];
6240 bibiko 500
			[self setNeedsDisplayInRect:[self visibleRect]];
5741 urbaneks 501
 
502
		} else if ([keyPath isEqualToString:HighlightIntervalKey]) {
5721 urbaneks 503
		braceHighlightInterval = [Preferences floatForKey:HighlightIntervalKey withDefault:0.3f];
5705 urbaneks 504
	}
5721 urbaneks 505
}
5705 urbaneks 506
 
5756 bibiko 507
- (void)updateLineWrappingMode
508
{
509
 
6242 bibiko 510
	NSSize layoutSize;
511
 
6238 bibiko 512
	lineWrappingEnabled = [Preferences flagForKey:enableLineWrappingKey withDefault: YES];
6263 bibiko 513
 
6242 bibiko 514
	[self setHorizontallyResizable:YES];
515
	if (!lineWrappingEnabled) {
6263 bibiko 516
		NSRange curRange = [self selectedRange];
6242 bibiko 517
		layoutSize = NSMakeSize(10e6,10e6);
5756 bibiko 518
		[scrollView setHasHorizontalScroller:YES];
6242 bibiko 519
		[self setMaxSize:layoutSize];
520
		[[self textContainer] setContainerSize:layoutSize];
521
		[[self textContainer] setWidthTracksTextView:NO];
6263 bibiko 522
		[self scrollRangeToVisible:NSMakeRange(curRange.location, 0)];
5756 bibiko 523
	} else {
6242 bibiko 524
		[scrollView setHasHorizontalScroller:NO];
525
		layoutSize = [self maxSize];
526
		[self setMaxSize:layoutSize];
527
		[[self textContainer] setContainerSize:layoutSize];
528
		[[self textContainer] setWidthTracksTextView:YES];
529
		// Enforce view to be re-layouted correctly
6263 bibiko 530
		// by re-inserting the the current text buffer
6242 bibiko 531
		[[self undoManager] disableUndoRegistration];
6263 bibiko 532
		NSRange curRange = [self selectedRange];
533
		NSString *t = [[NSString alloc] initWithString:[self string]];
6242 bibiko 534
		[self selectAll:nil];
6263 bibiko 535
		[self insertText:@""];
536
		usleep(1000);
537
		[self insertText:t];
538
		[t release];
539
		[self setSelectedRange:curRange];
540
		[self scrollRangeToVisible:NSMakeRange(curRange.location, 0)];
6242 bibiko 541
		[[self undoManager] enableUndoRegistration];
5756 bibiko 542
	}
6242 bibiko 543
	[[self textContainer] setHeightTracksTextView:NO];
5756 bibiko 544
 
545
}
546
 
5721 urbaneks 547
- (void)drawRect:(NSRect)rect
548
{
549
	// Draw background only for screen display but not while printing
550
	if([NSGraphicsContext currentContextDrawingToScreen]) {
551
 
552
		// Draw textview's background
553
		[shColorBackground setFill];
554
		NSRectFill(rect);
555
 
6238 bibiko 556
		// Highlightes the current line if set in the Pref
5721 urbaneks 557
		// and if nothing is selected in the text view
6070 bibiko 558
		if ([prefs boolForKey:highlightCurrentLine] && ![self selectedRange].length && ![self isSnippetMode]) {
5721 urbaneks 559
			NSUInteger rectCount;
560
			NSRange curLineRange = [[self string] lineRangeForRange:[self selectedRange]];
6246 bibiko 561
			// [theTextStorage ensureAttributesAreFixedInRange:curLineRange];
5721 urbaneks 562
			NSRectArray queryRects = [[self layoutManager] rectArrayForCharacterRange: curLineRange
563
														 withinSelectedCharacterRange: curLineRange
564
																	  inTextContainer: [self textContainer]
565
																			rectCount: &rectCount ];
566
			[shColorCurrentLine setFill];
567
			NSRectFillListUsingOperation(queryRects, rectCount, NSCompositeSourceOver);
568
		}
569
	}
570
	[super drawRect:rect];
5705 urbaneks 571
}
572
 
573
#pragma mark -
574
 
575
/**
576
 *  Performs syntax highlighting, trigger undo behaviour
577
 */
578
- (void)textStorageDidProcessEditing:(NSNotification *)notification
579
{
580
 
581
	// Make sure that the notification is from the correct textStorage object
582
	if (theTextStorage != [notification object]) return;
583
 
584
	NSInteger editedMask = [theTextStorage editedMask];
585
 
586
	SLog(@"RScriptEditorTextView: textStorageDidProcessEditing <%@> with mask %d", self, editedMask);
587
 
588
	// if the user really changed the text
589
	if(editedMask != 1) {
590
 
6232 bibiko 591
		// For larger text break a running syntax highlighting for user interaction
592
		// to make them more responsive (typing and scrolling)
6245 bibiko 593
		// if([[theTextStorage string] length] > 120000) {
594
		// 	breakSyntaxHighlighting = 1;
595
		// }
6232 bibiko 596
 
6070 bibiko 597
		[self checkSnippets];
598
 
6245 bibiko 599
		breakSyntaxHighlighting = 1;
600
 
5741 urbaneks 601
		// Cancel calling doSyntaxHighlighting
5705 urbaneks 602
		[NSObject cancelPreviousPerformRequestsWithTarget:self 
5741 urbaneks 603
								selector:@selector(doSyntaxHighlighting) 
5705 urbaneks 604
								object:nil];
605
 
6231 bibiko 606
		[self performSelector:@selector(doSyntaxHighlighting) withObject:nil afterDelay:0.05f];
5741 urbaneks 607
 
608
		// Cancel setting undo break point
5705 urbaneks 609
		[NSObject cancelPreviousPerformRequestsWithTarget:self 
5741 urbaneks 610
								selector:@selector(breakUndoCoalescing) 
5705 urbaneks 611
								object:nil];
612
 
613
		// Improve undo behaviour, i.e. it depends how fast the user types
6231 bibiko 614
		[self performSelector:@selector(breakUndoCoalescing) withObject:nil afterDelay:0.8f];
5705 urbaneks 615
 
616
	}
617
 
618
	deleteBackward = NO;
619
	startListeningToBoundChanges = YES;
620
 
621
}
622
 
623
#pragma mark -
624
 
5805 bibiko 625
- (BOOL)lineNumberingEnabled
626
{
627
	return lineNumberingEnabled;
628
}
629
 
5705 urbaneks 630
- (void)setDeleteBackward:(BOOL)delBack
631
{
632
	deleteBackward = delBack;
633
}
634
 
635
/**
636
 * Sets Tab Stops width for better editing behaviour
637
 */
638
- (void)setTabStops
639
{
640
 
641
	SLog(@"RScriptEditorTextView: setTabStops <%@>", self);
642
 
643
	NSFont *tvFont = [self font];
644
	int i;
645
	NSTextTab *aTab;
646
	NSMutableArray *myArrayOfTabs;
647
	NSMutableParagraphStyle *paragraphStyle;
648
 
649
	BOOL oldEditableStatus = [self isEditable];
650
	[self setEditable:YES];
651
 
652
	int tabStopWidth = [Preferences integerForKey:RScriptEditorTabWidth withDefault:4];
653
	if(tabStopWidth < 1) tabStopWidth = 1;
654
 
655
	float theTabWidth = [[NSString stringWithString:@" "] sizeWithAttributes:[NSDictionary dictionaryWithObject:tvFont forKey:NSFontAttributeName]].width;
656
	theTabWidth = (float)tabStopWidth * theTabWidth;
657
 
658
	int numberOfTabs = 256/tabStopWidth;
659
	myArrayOfTabs = [NSMutableArray arrayWithCapacity:numberOfTabs];
660
	aTab = [[NSTextTab alloc] initWithType:NSLeftTabStopType location:theTabWidth];
661
	[myArrayOfTabs addObject:aTab];
662
	[aTab release];
663
	for(i=1; i<numberOfTabs; i++) {
664
		aTab = [[NSTextTab alloc] initWithType:NSLeftTabStopType location:theTabWidth + ((float)i * theTabWidth)];
665
		[myArrayOfTabs addObject:aTab];
666
		[aTab release];
667
	}
668
	paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
669
	[paragraphStyle setTabStops:myArrayOfTabs];
670
 
671
	// Soft wrapped lines are indented slightly
672
	[paragraphStyle setHeadIndent:4.0];
673
 
674
	NSMutableDictionary *textAttributes = [[[NSMutableDictionary alloc] initWithCapacity:1] autorelease];
675
	[textAttributes setObject:paragraphStyle forKey:NSParagraphStyleAttributeName];
676
 
677
	NSRange range = NSMakeRange(0, [theTextStorage length]);
678
	if ([self shouldChangeTextInRange:range replacementString:nil]) {
679
		[theTextStorage setAttributes:textAttributes range: range];
680
		[self didChangeText];
681
	}
682
	[self setTypingAttributes:textAttributes];
683
	[self setDefaultParagraphStyle:paragraphStyle];
684
	[self setFont:tvFont];
685
 
686
	[self setEditable:oldEditableStatus];
687
 
688
	[paragraphStyle release];
689
}
690
 
6245 bibiko 691
- (BOOL)isSyntaxHighlighting
692
{
693
	return isSyntaxHighlighting;
694
}
695
 
696
- (BOOL)breakSyntaxHighlighting
697
{
698
	return breakSyntaxHighlighting;
699
}
5705 urbaneks 700
/**
701
 * Syntax Highlighting.
702
 *  
703
 * (The main bottleneck is the [NSTextStorage addAttribute:value:range:] method - the parsing itself is really fast!)
704
 * Some sample code from Andrew Choi ( http://members.shaw.ca/akochoi-old/blog/2003/11-09/index.html#3 ) has been reused.
705
 */
706
- (void)doSyntaxHighlighting
707
{
708
 
6245 bibiko 709
	if(!syntaxHighlightingEnabled)
710
		breakSyntaxHighlighting = 0;
711
 
6248 bibiko 712
	if (!syntaxHighlightingEnabled || [selfDelegate plain]) return;
5705 urbaneks 713
 
5805 bibiko 714
	isSyntaxHighlighting = YES;
715
 
5756 bibiko 716
	NSString *selfstr    = [theTextStorage string];
717
	NSInteger strlength  = (NSInteger)[selfstr length];
5705 urbaneks 718
 
5814 bibiko 719
	// do not highlight if text larger than 10MB
6245 bibiko 720
	if(strlength > 10000000 || !strlength) {
721
		isSyntaxHighlighting = NO;
722
		breakSyntaxHighlighting = 0;
723
		return;
724
	}
5814 bibiko 725
 
5756 bibiko 726
	// == Do highlighting partly (max R_SYNTAX_HILITE_BIAS*2 around visibleRange
727
	// by considering entire lines).
5705 urbaneks 728
 
729
	// Get the text range currently displayed in the view port
6240 bibiko 730
	NSRect visibleRect = [self visibleRect];
5705 urbaneks 731
	NSRange visibleRange = [[self layoutManager] glyphRangeForBoundingRectWithoutAdditionalLayout:visibleRect inTextContainer:[self textContainer]];
732
 
5805 bibiko 733
	if(!visibleRange.length) {
734
		isSyntaxHighlighting = NO;
6245 bibiko 735
		breakSyntaxHighlighting = 0;
5805 bibiko 736
		return;
737
	}
5705 urbaneks 738
 
5756 bibiko 739
	NSInteger start = visibleRange.location - R_SYNTAX_HILITE_BIAS;
740
	if (start > 0)
741
		while(start > 0) {
742
			if(CFStringGetCharacterAtIndex((CFStringRef)selfstr, start)=='\n')
743
				break;
744
			start--;
745
		}
746
	if(start < 0) start = 0;
747
	NSInteger end = NSMaxRange(visibleRange) + R_SYNTAX_HILITE_BIAS;
748
	if (end > strlength) {
5705 urbaneks 749
		end = strlength;
750
	} else {
5756 bibiko 751
		while(end < strlength) {
5705 urbaneks 752
			if(CFStringGetCharacterAtIndex((CFStringRef)selfstr, end)=='\n')
753
				break;
754
			end++;
755
		}
756
	}
757
 
758
	NSRange textRange = NSMakeRange(start, end-start);
759
 
760
	// only to be sure that nothing went wrongly
761
	textRange = NSIntersectionRange(textRange, NSMakeRange(0, [theTextStorage length])); 
762
 
6245 bibiko 763
	if (!textRange.length || textRange.length > 30000) {
5805 bibiko 764
		isSyntaxHighlighting = NO;
6245 bibiko 765
		breakSyntaxHighlighting = 0;
5705 urbaneks 766
		return;
5805 bibiko 767
	}
768
 
5705 urbaneks 769
	[theTextStorage beginEditing];
770
 
5741 urbaneks 771
	NSColor *tokenColor = nil;
5705 urbaneks 772
 
6230 bibiko 773
	size_t token;
5705 urbaneks 774
	NSRange tokenRange;
775
 
776
	// initialise flex
777
	yyuoffset = textRange.location; yyuleng = 0;
6233 bibiko 778
 
779
	BOOL hasFoldedItems = [theTextStorage hasFoldedItems];
5705 urbaneks 780
 
6248 bibiko 781
	if([selfDelegate isRdDocument]) {
5705 urbaneks 782
 
5995 bibiko 783
			rd_switch_to_buffer(rd_scan_string(NSStringUTF8String([selfstr substringWithRange:textRange])));
5705 urbaneks 784
 
5995 bibiko 785
			// now loop through all the tokens
786
			while ((token = rdlex())) {
6233 bibiko 787
				if(hasFoldedItems && (NSInteger)(_foldedImp)(theTextStorage, _foldedSel, yyuoffset) > -1) continue;
5995 bibiko 788
				switch (token) {
789
					case RDPT_COMMENT:
6029 bibiko 790
					    tokenColor = shColorComment;
5995 bibiko 791
					    break;
792
					case RDPT_SECTION:
6029 bibiko 793
					    tokenColor = shColorKeyword;
5995 bibiko 794
					    break;
795
					case RDPT_MACRO_ARG:
6029 bibiko 796
					    tokenColor = shColorNumber;
5995 bibiko 797
					    break;
798
					case RDPT_MACRO_GEN:
6029 bibiko 799
					    tokenColor = shColorNumber;
5995 bibiko 800
					    break;
5998 bibiko 801
					case RDPT_DIRECTIVE:
6029 bibiko 802
					    tokenColor = shColorString;
5998 bibiko 803
					    break;
804
					case RDPT_OTHER:
6029 bibiko 805
					    tokenColor = shColorNormal;
5998 bibiko 806
					    break;
5995 bibiko 807
					default:
6029 bibiko 808
					    tokenColor = shColorNormal;
5995 bibiko 809
				}
5705 urbaneks 810
 
5995 bibiko 811
				tokenRange = NSMakeRange(yyuoffset, yyuleng);
5705 urbaneks 812
 
5995 bibiko 813
				// make sure that tokenRange is valid (and therefore within textRange)
814
				// otherwise a bug in the lex code could cause the the TextView to crash
815
				// NOTE Disabled for testing purposes for speed it up
816
				tokenRange = NSIntersectionRange(tokenRange, textRange);
817
				if (!tokenRange.length) continue;
818
 
819
				NSMutableAttributedStringAddAttributeValueRange(theTextStorage, NSForegroundColorAttributeName, tokenColor, tokenRange);
820
 
6232 bibiko 821
				if(breakSyntaxHighlighting) {
822
 
823
					// Cancel calling doSyntaxHighlighting
824
					[NSObject cancelPreviousPerformRequestsWithTarget:self 
825
											selector:@selector(doSyntaxHighlighting) 
826
											object:nil];
827
 
6245 bibiko 828
					[self performSelector:@selector(doSyntaxHighlighting) withObject:nil afterDelay:0.08f];
6232 bibiko 829
 
830
					breakSyntaxHighlighting = 0;
831
					break;
832
 
833
				}
834
 
5995 bibiko 835
			}
836
 
837
	} else {
838
 
839
		yy_switch_to_buffer(yy_scan_string(NSStringUTF8String([selfstr substringWithRange:textRange])));
840
 
841
		// now loop through all the tokens
842
		while ((token = yylex())) {
6233 bibiko 843
			if(hasFoldedItems && (NSInteger)(_foldedImp)(theTextStorage, _foldedSel, yyuoffset) > -1) continue;
5995 bibiko 844
			switch (token) {
845
				case RPT_SINGLE_QUOTED_TEXT:
846
				case RPT_DOUBLE_QUOTED_TEXT:
847
				    tokenColor = shColorString;
848
				    break;
849
				case RPT_RESERVED_WORD:
850
				    tokenColor = shColorKeyword;
851
				    break;
852
				case RPT_NUMERIC:
853
					tokenColor = shColorNumber;
854
					break;
855
				case RPT_BACKTICK_QUOTED_TEXT:
856
				    tokenColor = shColorString;
857
				    break;
858
				case RPT_COMMENT:
859
				    tokenColor = shColorComment;
860
				    break;
861
				case RPT_VARIABLE:
862
				    tokenColor = shColorIdentifier;
863
				    break;
864
				default:
865
				    tokenColor = shColorNormal;
866
			}
867
 
868
			tokenRange = NSMakeRange(yyuoffset, yyuleng);
869
 
870
			// make sure that tokenRange is valid (and therefore within textRange)
871
			// otherwise a bug in the lex code could cause the the TextView to crash
872
			tokenRange = NSIntersectionRange(tokenRange, textRange);
873
			if (!tokenRange.length) continue;
874
 
875
			NSMutableAttributedStringAddAttributeValueRange(theTextStorage, NSForegroundColorAttributeName, tokenColor, tokenRange);
6232 bibiko 876
 
877
			if(breakSyntaxHighlighting) {
878
 
879
				// Cancel calling doSyntaxHighlighting
6242 bibiko 880
				[NSObject cancelPreviousPerformRequestsWithTarget:self 
881
										selector:@selector(doSyntaxHighlighting) 
882
										object:nil];
883
 
6245 bibiko 884
				[self performSelector:@selector(doSyntaxHighlighting) withObject:nil afterDelay:0.08f];
6232 bibiko 885
 
886
				breakSyntaxHighlighting = 0;
887
				break;
888
 
889
			}
890
 
5995 bibiko 891
		}
6233 bibiko 892
 
5705 urbaneks 893
	}
5741 urbaneks 894
 
895
	// set current textColor to the color of the caret's position - 1
896
	// to try to suppress writing in normalColor before syntax highlighting 
897
	NSUInteger ix = [self selectedRange].location;
898
	if(ix > 1) {
899
		NSMutableDictionary *typeAttr = [NSMutableDictionary dictionary];
900
		[typeAttr setDictionary:[self typingAttributes]];
901
		NSColor *c = [theTextStorage attribute:NSForegroundColorAttributeName atIndex:ix-1 effectiveRange:nil];
902
		if(c) [typeAttr setObject:c forKey:NSForegroundColorAttributeName];
903
		[self setTypingAttributes:typeAttr];
904
	}
905
 
5705 urbaneks 906
	[theTextStorage endEditing];
6245 bibiko 907
 
6240 bibiko 908
	[self setNeedsDisplayInRect:visibleRect];
5705 urbaneks 909
 
6245 bibiko 910
	breakSyntaxHighlighting = 0;
911
 
912
	isSyntaxHighlighting = NO;
913
 
5705 urbaneks 914
}
915
 
916
-(void)resetHighlights
917
{
6093 bibiko 918
 
5705 urbaneks 919
	SLog(@"RScriptEditorTextView: resetHighlights with current highlite %d", currentHighlight);
920
 
921
	if (currentHighlight>-1) {
922
		if (currentHighlight<[theTextStorage length]) {
923
			NSLayoutManager *lm = [self layoutManager];
924
			if (lm) {
925
				NSRange fr = NSMakeRange(currentHighlight,1);
926
				NSDictionary *d = [lm temporaryAttributesAtCharacterIndex:currentHighlight effectiveRange:&fr];
927
				if (!d || [d objectForKey:NSBackgroundColorAttributeName]==nil) {
928
					fr = NSMakeRange(0,[[self string] length]);
929
					SLog(@"resetHighlights: attribute at %d not found, clearing all %d characters - better safe than sorry", currentHighlight, fr.length);
930
				}
931
				[lm removeTemporaryAttribute:NSBackgroundColorAttributeName forCharacterRange:fr];
932
			}
933
		}
934
		currentHighlight=-1;
935
	}
936
}
937
 
6093 bibiko 938
-(void)highlightCharacter:(NSNumber*)loc
5705 urbaneks 939
{
6094 urbaneks 940
	NSInteger pos = [loc intValue];
5705 urbaneks 941
 
942
	SLog(@"RScriptEditorTextView: highlightCharacter: %d", pos);
943
 
944
	if (pos>=0 && pos<[[self string] length]) {
6093 bibiko 945
 
946
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
947
		[self showFindIndicatorForRange:NSMakeRange(pos, 1)];
948
#else
949
		[self resetHighlights];
5705 urbaneks 950
		NSLayoutManager *lm = [self layoutManager];
951
		if (lm) {
952
			currentHighlight = pos;
953
			[lm setTemporaryAttributes:highlightColorAttr forCharacterRange:NSMakeRange(pos, 1)];
954
			[self performSelector:@selector(resetBackgroundColor:) withObject:nil afterDelay:braceHighlightInterval];
6093 bibiko 955
		}
956
 
957
#endif
958
 
5705 urbaneks 959
	}
6093 bibiko 960
	else SLog(@"highlightCharacter: attempt to set highlight %d beyond the text range 0:%d - I refuse!", pos, [[self string] length] - 1);
5705 urbaneks 961
}
962
 
963
-(void)resetBackgroundColor:(id)sender
964
{
965
	[self resetHighlights];
966
}
967
 
968
/**
969
 * Scrollview delegate after the textView's view port was changed.
6232 bibiko 970
 * Manily used to update the syntax highlighting for a large text size
5705 urbaneks 971
 */
972
- (void)boundsDidChangeNotification:(NSNotification *)notification
973
{
974
 
975
	if(startListeningToBoundChanges) {
5805 bibiko 976
 
6245 bibiko 977
		breakSyntaxHighlighting = 1;
978
 
5705 urbaneks 979
		[NSObject cancelPreviousPerformRequestsWithTarget:self 
980
									selector:@selector(doSyntaxHighlighting) 
981
									object:nil];
982
 
5805 bibiko 983
		if(![theTextStorage changeInLength]) {
6232 bibiko 984
			if([[theTextStorage string] length] > 120000)
985
				breakSyntaxHighlighting = 2;
6245 bibiko 986
			[self performSelector:@selector(doSyntaxHighlighting) withObject:nil afterDelay:0.08];
5805 bibiko 987
		}
6238 bibiko 988
		if(lineNumberingEnabled) {
989
			[NSObject cancelPreviousPerformRequestsWithTarget:[[self enclosingScrollView] verticalRulerView] 
990
										selector:@selector(refresh) 
991
										object:nil];
992
 
993
			[[[self enclosingScrollView] verticalRulerView] performSelector:@selector(refresh) withObject:nil afterDelay:0.001f];
994
		}
5705 urbaneks 995
 
996
	}
997
 
998
}
999
 
6233 bibiko 1000
- (IBAction)undo:(id)sender
1001
{
1002
	if([[self undoManager] canUndo]) {
1003
		[[self undoManager] undo];
1004
		if(lineNumberingEnabled)
1005
			[[[self enclosingScrollView] verticalRulerView] performSelector:@selector(refresh) withObject:nil afterDelay:0.0f];
1006
	}
1007
}
1008
 
1009
- (IBAction)redo:(id)sender
1010
{
1011
	if([[self undoManager] canRedo]) {
1012
		[[self undoManager] redo];
1013
		if(lineNumberingEnabled)
1014
			[[[self enclosingScrollView] verticalRulerView] performSelector:@selector(refresh) withObject:nil afterDelay:0.0f];
1015
	}
1016
}
1017
 
6128 bibiko 1018
#pragma mark -
6232 bibiko 1019
#pragma mark Folding
6128 bibiko 1020
 
6238 bibiko 1021
/*
1022
The idea of code folding is to replace the folded range while layouting by NSControlGlyphs
1023
which will be rendered as zero width glyphs. This has the advantage that all text actions like 
1024
copying, line numbering, etc. works without additional stuff.
1025
 
1026
For now it is only possible to fold a block if:
1027
- the starting { is placed at the end of a line (or with a following comment)
1028
- there's only ONE starting { in that line and NO }
1029
- the ending } is placed at the of a line (or with a following comment)
1030
- there's only ONE ending } in that line and NO {
1031
 
1032
Due to speed issues all folded ranges will be stored in a 3d C array of the size
1033
R_MAX_FOLDED_ITEMS = 1024 defined in RScriptEditorTextStorage.h whereby index:
1034
 
1035
1 - range length
1036
2 - location + length (pre-calculate for speed)
1037
 
1038
If the user locates the cursor inside a folded range or an action will locate the cursor
1039
inside such a range (go to line, find something) the folded range will be unfolded.
1040
... for more info ask Hans-J. Bibiko
1041
*/
1042
 
1043
- (IBAction)unfoldCurrentBlock:(id)sender
6128 bibiko 1044
{
6238 bibiko 1045
	NSUInteger caretPosition = [self selectedRange].location;
6128 bibiko 1046
 
6238 bibiko 1047
	NSRange r = [[self string] lineRangeForRange:NSMakeRange(caretPosition, 0)];
1048
 
1049
	if([theTextStorage foldedAtIndex:NSMaxRange(r)] > -1) {
1050
		[self unfoldLinesContainingCharacterAtIndex:NSMaxRange(r)];
1051
		return;
1052
	}
1053
	if([theTextStorage foldedAtIndex:r.location] > -1) {
1054
		[self unfoldLinesContainingCharacterAtIndex:r.location];
1055
		return;
1056
	}
1057
}
1058
 
1059
- (IBAction)foldCurrentBlock:(id)sender
1060
{
1061
	NSUInteger caretPosition = [self selectedRange].location;
1062
	NSInteger foldItem = 0;
1063
	unichar c;
1064
 
1065
	NSRange r = [[self string] lineRangeForRange:NSMakeRange(caretPosition, 0)];
1066
 
1067
	foldItem = [self foldStatusAtIndex:NSMaxRange(r)-2];
1068
	if(foldItem == 1) { // is current line set to ▼ set caret to end of line
1069
		caretPosition = NSMaxRange(r)-1;
1070
	} else { // otherwise set caret to begin of line
1071
		caretPosition = r.location;
1072
	}
1073
 
1074
	NSUInteger stringLength = [[self string] length];
1075
	if(!stringLength) return;
1076
	if(caretPosition == 0 || caretPosition >= [[self string] length]) return;
1077
 
1078
	CFStringRef parserStringRef = (CFStringRef)[self string];
1079
 
1080
	unichar co = '{'; // opening char
1081
	unichar cc = '}'; // closing char
1082
 
1083
	NSInteger start = -1;
1084
	NSInteger end = -1;
1085
	NSInteger bracketCounter = 0;
1086
 
1087
	c = CFStringGetCharacterAtIndex(parserStringRef, caretPosition);
1088
	if(c == cc)
1089
		bracketCounter--;
1090
	if(c == co)
1091
		bracketCounter++;
1092
 
1093
	for(NSInteger i=caretPosition; i>=0; i--) {
1094
		if([self parserContextForPosition:i] != pcExpression) continue;
1095
		c = CFStringGetCharacterAtIndex(parserStringRef, i);
1096
		if(c == co) {
1097
			if(!bracketCounter) {
1098
				start = i;
1099
				break;
1100
			}
1101
			bracketCounter--;
1102
		}
1103
		if(c == cc) {
1104
			bracketCounter++;
1105
		}
1106
	}
1107
	if(start < 0 ) return;
1108
 
1109
	// go up for lines like "} else {"
1110
	if(start && [self foldStatusAtIndex:start-1] == 0) {
1111
		for(NSInteger i=start-1; i>=0; i--) {
1112
			c = CFStringGetCharacterAtIndex(parserStringRef, i);
1113
			if(c == '\n' || c == '\r') break;
1114
			if([self parserContextForPosition:i] != pcExpression) continue;
1115
			if(c == cc && i > 0) {
1116
				bracketCounter = 0;
1117
				for(NSInteger j=i-1; j>=0; j--) {
1118
					if([self parserContextForPosition:j] != pcExpression) continue;
1119
					c = CFStringGetCharacterAtIndex(parserStringRef, j);
1120
					if(c == co) {
1121
						if(!bracketCounter) {
1122
							start = j;
1123
							break;
1124
						}
1125
						bracketCounter--;
1126
					}
1127
					if(c == cc) {
1128
						bracketCounter++;
1129
					}
1130
				}
1131
				break;
1132
			}
1133
		}
1134
	}		
1135
 
1136
 
1137
	bracketCounter = 0;
1138
	for(NSUInteger i=caretPosition; i<stringLength; i++) {
1139
		if([self parserContextForPosition:i] != pcExpression) continue;
1140
		c = CFStringGetCharacterAtIndex(parserStringRef, i);
1141
		if(c == co) {
1142
			bracketCounter++;
1143
		}
1144
		if(c == cc) {
1145
			if(!bracketCounter) {
1146
				end = i+1;
1147
				BOOL goAhead = NO;
1148
				//go ahead for lines a la  "} else {"
1149
				for(NSUInteger j=end; j<stringLength; j++) {
1150
					c = CFStringGetCharacterAtIndex(parserStringRef, j);
1151
					if(c == '\n' || c == '\r') {
1152
						break;
1153
					}
1154
					if(c == '\t' || c == ' ') {
1155
						continue;
1156
					}
1157
					if([self parserContextForPosition:j] != pcExpression) continue;
1158
					if(c == co) {
1159
						goAhead = YES;
1160
						break;
1161
					}
1162
				}
1163
				if(!goAhead) break;
1164
			}
1165
			bracketCounter--;
1166
		}
1167
	}
1168
 
1169
	if(end < 0 || bracketCounter || end-start < 1) return;
1170
 
1171
	NSRange foldRange = NSMakeRange(start, end-start);
1172
	if(![theTextStorage existsFoldedRange:foldRange]) {
1173
		// set caret for ▲ line inside {} for scrolling
1174
		if(foldItem == 2)
1175
			[self setSelectedRange:NSMakeRange(r.location, 0)];
6242 bibiko 1176
		[self foldLinesInRange:foldRange blockMode:NO];
6238 bibiko 1177
	}
1178
 
1179
}
1180
 
1181
- (IBAction)foldBlockAtLevel:(id)sender
1182
{
6242 bibiko 1183
 
6238 bibiko 1184
	NSInteger level = [sender tag];
1185
	NSInteger bracketCounter = 0;
1186
	NSInteger start = 0;
1187
	NSInteger end = 0;
1188
	CFStringRef str = (CFStringRef)[self string];
1189
 
1190
	unichar c;
1191
 
6242 bibiko 1192
	[[self undoManager] disableUndoRegistration];
6238 bibiko 1193
 
1194
	for(NSInteger i=0; i<[[self string] length]; i++) {
1195
		c = CFStringGetCharacterAtIndex(str, i);
1196
		if([self parserContextForPosition:i] != pcExpression) continue;
1197
		if(c == '{') {
1198
			bracketCounter++;
1199
			if([self foldStatusAtIndex:i] == 1) {
1200
				if(bracketCounter == level+1) {
1201
					start = i;
1202
				}
1203
			}
1204
			continue;
1205
		}
1206
		if(c == '}') {
1207
			bracketCounter--;
1208
			if([self foldStatusAtIndex:i] == 2) {
1209
				if(bracketCounter == level) {
1210
					end = i;
1211
					NSRange r = NSMakeRange(start, end - start+1);
1212
					if(![theTextStorage existsFoldedRange:r])
6242 bibiko 1213
						[self foldLinesInRange:r blockMode:YES];
6238 bibiko 1214
				}
1215
				if(bracketCounter < 0) {
1216
					NSBeep();
1217
					return;
1218
				}
1219
			}
1220
		}
1221
	}
6242 bibiko 1222
 
1223
	[self didChangeText];
6238 bibiko 1224
 
6246 bibiko 1225
	// NSRange r = [[self layoutManager] characterRangeForGlyphRange:[[self layoutManager] 
1226
	// 									glyphRangeForBoundingRect:[scrollView documentVisibleRect] 
1227
	// 											  inTextContainer:[self textContainer]] actualGlyphRange:NULL];
1228
	// 
1229
	// [theTextStorage ensureAttributesAreFixedInRange:r];
6242 bibiko 1230
 
1231
	if(lineNumberingEnabled)
1232
		[[[self enclosingScrollView] verticalRulerView] performSelector:@selector(refresh) withObject:nil afterDelay:0.0f];
1233
 
1234
	[NSObject cancelPreviousPerformRequestsWithTarget:self 
1235
							selector:@selector(doSyntaxHighlighting) 
1236
							object:nil];
1237
 
1238
	[self performSelector:@selector(doSyntaxHighlighting) withObject:nil afterDelay:0.02f];
1239
 
1240
	[[self undoManager] enableUndoRegistration];
6238 bibiko 1241
}
1242
 
1243
- (IBAction)unFoldAllBlocks:(id)sender
1244
{
1245
	[theTextStorage removeAllFoldedRanges];
1246
 
1247
	[self didChangeText];
1248
 
6246 bibiko 1249
	// NSRange r = NSMakeRange(0, [[self string] length]);
6238 bibiko 1250
 
6246 bibiko 1251
	// [theTextStorage fixAttributesInRange:r];
1252
	// [theTextStorage fixAttachmentAttributeInRange:r];
1253
	// [theTextStorage ensureAttributesAreFixedInRange:r];
6238 bibiko 1254
 
1255
 
1256
	if(lineNumberingEnabled)
1257
		[[[self enclosingScrollView] verticalRulerView] performSelector:@selector(refresh) withObject:nil afterDelay:0.0f];
6245 bibiko 1258
 
1259
	breakSyntaxHighlighting = 1;
6238 bibiko 1260
 
1261
	[NSObject cancelPreviousPerformRequestsWithTarget:self 
1262
							selector:@selector(doSyntaxHighlighting) 
1263
							object:nil];
1264
 
6245 bibiko 1265
	[self performSelector:@selector(doSyntaxHighlighting) withObject:nil afterDelay:0.01f];
6238 bibiko 1266
}
1267
 
1268
- (void)refoldLinesInRange:(NSRange)range
1269
{
6249 bibiko 1270
 
6233 bibiko 1271
	NSInteger foldId = [theTextStorage registerFoldedRange:range];
6128 bibiko 1272
 
6233 bibiko 1273
	if(foldId < 0) {
6238 bibiko 1274
		[RTooltip showWithObject:NLS(@"Maximum number of folded code fragments is reached.") atLocation:[NSEvent mouseLocation]];
6233 bibiko 1275
		return;
1276
	}
6137 bibiko 1277
 
6238 bibiko 1278
	range.location++;
1279
	range.length -= 2;
1280
 
1281
	if(!range.length) return;
1282
 
1283
	NSString *tooltip = nil;
1284
	if(range.length < 300)
1285
		tooltip = [[self string] substringWithRange:range];
1286
	else
1287
		tooltip = [[[self string] substringWithRange:NSMakeRange(range.location, 300)] stringByAppendingString:@"\n…"];
1288
 
1289
	[theTextStorage beginEditing];
1290
	[theTextStorage addAttribute:NSCursorAttributeName value:[NSCursor arrowCursor] range:range];
1291
	[theTextStorage addAttribute:NSToolTipAttributeName value:tooltip range:range];
1292
	[theTextStorage endEditing];
1293
 
1294
}
1295
 
6242 bibiko 1296
- (BOOL)foldLinesInRange:(NSRange)range blockMode:(BOOL)blockMode
6238 bibiko 1297
{
1298
	if(range.length < 5) {
1299
		return NO;
1300
	}
1301
 
1302
	NSInteger caretPosition = [self selectedRange].location;
1303
	BOOL caretWasInsideFoldedRange = NO;
1304
 
1305
	// Check for valid folding range
1306
	// fold only range if { and } are the last chars at the line
1307
 
1308
	NSString *selfStr = [self string];
1309
	NSRange startLineRange = [selfStr lineRangeForRange:NSMakeRange(range.location, 0)];
1310
	NSRange endLineRange   = [selfStr lineRangeForRange:NSMakeRange(NSMaxRange(range), 0)];
1311
 
1312
	if(!startLineRange.length || !endLineRange.length) {
1313
		return NO;
1314
	}
1315
 
1316
	// Do not fold a single line
1317
	if(startLineRange.location == endLineRange.location) {
1318
		return NO;
1319
	}
1320
 
1321
	NSInteger status;
1322
 
1323
	status = [self foldStatusAtIndex:NSMaxRange(startLineRange)-2];	
1324
	if(status != 1) {
1325
		return NO;
1326
	}
1327
 
1328
	unichar c = CFStringGetCharacterAtIndex((CFStringRef)selfStr, NSMaxRange(endLineRange)-1);
1329
	if(c == '\n' || c == '\r')
1330
		status = [self foldStatusAtIndex:NSMaxRange(endLineRange)-2];
1331
	else
1332
		status = [self foldStatusAtIndex:NSMaxRange(endLineRange)-1];	
1333
	if(status != 2) {
1334
		return NO;
1335
	}
1336
 
6233 bibiko 1337
	if(caretPosition >= range.location && caretPosition < NSMaxRange(range)) {
1338
		[self setSelectedRange:NSMakeRange(NSMaxRange(range), 0)];
1339
		caretWasInsideFoldedRange = YES;
1340
	}
6137 bibiko 1341
 
6238 bibiko 1342
	NSInteger foldId = [theTextStorage registerFoldedRange:range];
6233 bibiko 1343
 
6238 bibiko 1344
	if(foldId < 0) {
1345
		[RTooltip showWithObject:NLS(@"Maximum number of folded code fragments is reached.") atLocation:[NSEvent mouseLocation]];
1346
		return NO;
6137 bibiko 1347
	}
1348
 
6238 bibiko 1349
	range.location++;
1350
	range.length -= 2;
6128 bibiko 1351
 
6238 bibiko 1352
	if(!range.length) return NO;
1353
 
6242 bibiko 1354
	if(!blockMode) {
1355
		[[self undoManager] disableUndoRegistration];
1356
		if(![self shouldChangeTextInRange:range replacementString:nil]) {
1357
			[[self undoManager] enableUndoRegistration];
1358
			return NO;
1359
		}
6238 bibiko 1360
		[[self undoManager] enableUndoRegistration];
1361
	}
6128 bibiko 1362
 
6137 bibiko 1363
	NSString *tooltip = nil;
6233 bibiko 1364
	if(range.length < 300)
1365
		tooltip = [[self string] substringWithRange:range];
6137 bibiko 1366
	else
6233 bibiko 1367
		tooltip = [[[self string] substringWithRange:NSMakeRange(range.location, 300)] stringByAppendingString:@"\n…"];
6137 bibiko 1368
 
6128 bibiko 1369
	[theTextStorage beginEditing];
6233 bibiko 1370
	[theTextStorage addAttribute:NSCursorAttributeName value:[NSCursor arrowCursor] range:range];
1371
	[theTextStorage addAttribute:NSToolTipAttributeName value:tooltip range:range];
6128 bibiko 1372
	[theTextStorage endEditing];
1373
 
6242 bibiko 1374
	if(!blockMode) {
1375
		[self didChangeText];
6233 bibiko 1376
 
6242 bibiko 1377
		if(caretWasInsideFoldedRange)
1378
			[self scrollRangeToVisible:[self selectedRange]];
6233 bibiko 1379
 
6246 bibiko 1380
		// NSRange r = [[self layoutManager] characterRangeForGlyphRange:[[self layoutManager] 
1381
		// 									glyphRangeForBoundingRect:[scrollView documentVisibleRect] 
1382
		// 											  inTextContainer:[self textContainer]] actualGlyphRange:NULL];
6238 bibiko 1383
 
6246 bibiko 1384
		// [theTextStorage ensureAttributesAreFixedInRange:r];
6238 bibiko 1385
 
6242 bibiko 1386
		if(lineNumberingEnabled)
1387
			[[[self enclosingScrollView] verticalRulerView] performSelector:@selector(refresh) withObject:nil afterDelay:0.0f];
6233 bibiko 1388
 
6242 bibiko 1389
		[NSObject cancelPreviousPerformRequestsWithTarget:self 
1390
								selector:@selector(doSyntaxHighlighting) 
1391
								object:nil];
6128 bibiko 1392
 
6242 bibiko 1393
		[self performSelector:@selector(doSyntaxHighlighting) withObject:nil afterDelay:0.02f];
1394
	}
6238 bibiko 1395
 
1396
	return YES;
1397
 
6128 bibiko 1398
}
1399
 
6238 bibiko 1400
- (NSInteger)foldStatusAtIndex:(NSInteger)index
1401
{
1402
 
1403
	if(index < 0 || index >= [[self string] length]) return 0;
1404
 
1405
	NSInteger status = 0; // 0 = no; 1 = ▼; 2 = ▲
1406
 
1407
	NSInteger i = index;
1408
	NSInteger type;
1409
	NSString *selfStr = [self string];
1410
	unichar c;
1411
 
6248 bibiko 1412
	BOOL isRd = [selfDelegate isRdDocument];
6238 bibiko 1413
 
1414
	unichar commentSign = (isRd) ? '%' : '#';
1415
 
1416
	// start checking from the end of a line
1417
	while(i >= 0) {
1418
 
1419
		c = CFStringGetCharacterAtIndex((CFStringRef)selfStr, i);
1420
 
1421
		// Check only one line
1422
		if(c=='\n' || c=='\r') break;
1423
 
1424
		// Ignore white spaces and comment sign
1425
		if(c==' ' || c=='\t' || c==commentSign) {
1426
			i--;
1427
			continue;
1428
		}
1429
 
1430
		type = [self parserContextForPosition:i];
1431
 
1432
		// Ignore comments
1433
		if(type == pcComment || c==commentSign) {
1434
			i--;
1435
			continue;
1436
		}
1437
 
1438
		// ======= Check for ▼ 
1439
		if(c=='{' && type == pcExpression) {
1440
			status = 1;
1441
			// look for lines a la "} else {" - if so do not draw folding marker
1442
			i--;
1443
			while(i>=0) {
1444
				c = CFStringGetCharacterAtIndex((CFStringRef)selfStr, i);
1445
				if(c=='\n' || c=='\r') break;
1446
				if((c=='}' || c=='{')&& [self parserContextForPosition:i] == pcExpression) {
1447
					status = 0;
1448
					break;
1449
				}
1450
				i--;
1451
			}
1452
			break;
1453
		}
1454
 
1455
		// ======= Check for ▲
1456
		if(c=='}') {
1457
			status = 2;
1458
			i--;
1459
			while(i>=0) {
1460
				c = CFStringGetCharacterAtIndex((CFStringRef)selfStr, i);
1461
				if(c=='\n' || c=='\r') break;
1462
				if((c=='}' || c=='{')&& [self parserContextForPosition:i] == pcExpression) {
1463
					status = 0;
1464
					break;
1465
				}
1466
				if(isRd) {
1467
					if(c != '\t' || c != ' ') {
1468
						status = 0;
1469
						break;
1470
					}
1471
				}
1472
				i--;
1473
			}
1474
		}
1475
		break;
1476
 
1477
	}
1478
 
1479
	return status;
1480
 
1481
}
1482
 
6128 bibiko 1483
- (BOOL)unfoldLinesContainingCharacterAtIndex:(NSUInteger)charIndex
1484
{
1485
 
6233 bibiko 1486
	NSInteger foldIndex = [theTextStorage foldedAtIndex:charIndex];
6128 bibiko 1487
 
6233 bibiko 1488
	if(foldIndex > -1) {
1489
 
6238 bibiko 1490
		NSRange range = [theTextStorage foldedRangeAtIndex:foldIndex];
1491
		[[self undoManager] disableUndoRegistration];
1492
		if(![self shouldChangeTextInRange:range replacementString:nil]) {
1493
			[[self undoManager] enableUndoRegistration];
1494
			return NO;
1495
		}
1496
		[[self undoManager] enableUndoRegistration];
1497
 
1498
		[theTextStorage removeFoldedRangeWithIndex:foldIndex];
1499
 
6128 bibiko 1500
		[self didChangeText];
6233 bibiko 1501
 
1502
		[NSObject cancelPreviousPerformRequestsWithTarget:self 
1503
								selector:@selector(doSyntaxHighlighting) 
1504
								object:nil];
1505
 
6246 bibiko 1506
		// NSRange r = [[self layoutManager] characterRangeForGlyphRange:[[self layoutManager] 
1507
		// 									glyphRangeForBoundingRect:[scrollView documentVisibleRect] 
1508
		// 											  inTextContainer:[self textContainer]] actualGlyphRange:NULL];
1509
		// 
1510
		// [theTextStorage ensureAttributesAreFixedInRange:r];
6233 bibiko 1511
 
6238 bibiko 1512
		if(lineNumberingEnabled)
1513
			[[[self enclosingScrollView] verticalRulerView] performSelector:@selector(refresh) withObject:nil afterDelay:0.0f];
1514
 
1515
		[self performSelector:@selector(doSyntaxHighlighting) withObject:nil afterDelay:0.02f];
1516
 
6128 bibiko 1517
		return YES;
1518
	}
6233 bibiko 1519
 
6128 bibiko 1520
	return NO;
6233 bibiko 1521
 
6128 bibiko 1522
}
1523
 
6137 bibiko 1524
- (void)mouseDown:(NSEvent *)event
1525
{
1526
 
6238 bibiko 1527
	if(![theTextStorage isKindOfClass:[RScriptEditorTextStorage class]]) {
1528
		[super mouseDown:event];
1529
		return;
1530
	}
1531
 
1532
	RScriptEditorLayoutManager *layoutManager = (RScriptEditorLayoutManager*)[self layoutManager];
1533
	NSUInteger glyphIndex = [layoutManager glyphIndexForPoint:[self convertPointFromBase:[event locationInWindow]] inTextContainer:[self textContainer]];
1534
 
1535
	// trigger unfolding if inside foldingAttachmentCell
1536
	if (glyphIndex < [layoutManager numberOfGlyphs]) {
1537
 
1538
		NSUInteger charIndex = [layoutManager characterIndexForGlyphAtIndex:glyphIndex];
1539
		NSInteger foldIndex  = [theTextStorage foldedForIndicatorAtIndex:charIndex];
1540
 
1541
		if (foldIndex > -1) {
1542
 
1543
			NSInteger foldStart = [theTextStorage foldedRangeAtIndex:foldIndex].location+1;
1544
			NSTextAttachment *attachment = [theTextStorage attribute:NSAttachmentAttributeName atIndex:foldStart effectiveRange:NULL];
1545
 
1546
			if (attachment) {
1547
				NSTextAttachmentCell *cell = (NSTextAttachmentCell *)[attachment attachmentCell];
1548
				NSRect cellFrame;
1549
				NSPoint delta;
1550
 
1551
				glyphIndex = [layoutManager glyphIndexForCharacterAtIndex:foldStart];
1552
 
1553
				cellFrame.origin = [self textContainerOrigin];
1554
				cellFrame.size = [layoutManager attachmentSizeForGlyphAtIndex:glyphIndex];
1555
 
1556
				delta = [layoutManager lineFragmentRectForGlyphAtIndex:glyphIndex effectiveRange:NULL].origin;
1557
				cellFrame.origin.x += delta.x;
1558
				cellFrame.origin.y += delta.y;
1559
 
1560
				cellFrame.origin.x += [layoutManager locationForGlyphAtIndex:glyphIndex].x;
1561
 
1562
				if ([cell wantsToTrackMouseForEvent:event inRect:cellFrame ofView:self atCharacterIndex:foldStart] 
1563
						&& [cell trackMouse:event inRect:cellFrame ofView:self atCharacterIndex:foldStart untilMouseUp:YES]) return;
1564
			}
1565
		}
1566
	}
1567
 
6137 bibiko 1568
	[super mouseDown:event];
6233 bibiko 1569
}
1570
 
6137 bibiko 1571
// - (void)setSelectedRanges:(NSArray *)ranges affinity:(NSSelectionAffinity)affinity stillSelecting:(BOOL)stillSelectingFlag
1572
// {
1573
// 
1574
// 	if(!stillSelectingFlag) {
1575
// 
1576
// 		if([ranges count] > 0) {
1577
// 
1578
// 			// Adjust range for folded text chunks; additional checks will be made in
1579
// 			// the self's delegate [RDocumentWinCtrl:textViewDidChangeSelection:]
1580
// 
1581
// 			NSRange range = [[ranges objectAtIndex:0] rangeValue];
1582
// 
1583
// 			if(!range.length) {
1584
// 				[super setSelectedRanges:ranges affinity:affinity stillSelecting:stillSelectingFlag];
1585
// 				return;
1586
// 			}
1587
// 
1588
// 			NSRange glyphRange = [[self layoutManager] glyphRangeForCharacterRange:range actualCharacterRange:NULL];
1589
// 
1590
// 			if(glyphRange.location != range.location || glyphRange.length != range.length) {
1591
// 
1592
// 				if([ranges count] == 2)
1593
// 					glyphRange.length += [[ranges objectAtIndex:1] rangeValue].length;
1594
// 
1595
// 				SLog(@"RScriptEditorTextView:setSelectedRanges: adjust range via glyph range from %@ to %@", NSStringFromRange(range), NSStringFromRange(glyphRange));
1596
// 
1597
// 				[super setSelectedRange:glyphRange];
1598
// 				return;
1599
// 
1600
// 			}
1601
// 		}
1602
// 	}
1603
// 
1604
// 	[super setSelectedRanges:ranges affinity:affinity stillSelecting:stillSelectingFlag];
6233 bibiko 1605
// 
1606
// }
1607
// 
6128 bibiko 1608
 
6238 bibiko 1609
// - (void)setTypingAttributes:(NSDictionary *)attrs
1610
// {
1611
// 	// we don't want to store foldingAttributeId as a typing attribute
1612
// 	if ([attrs objectForKey:foldingAttributeId]) {
1613
// 		NSMutableDictionary *copy = [[attrs mutableCopyWithZone:NULL] autorelease];
1614
// 		[copy removeObjectForKey:foldingAttributeId];
1615
// 		attrs = copy;
1616
// 	}
1617
// 
1618
// 	[super setTypingAttributes:attrs];
1619
// }
1620
// 
6128 bibiko 1621
#pragma mark -
1622
 
5705 urbaneks 1623
- (void)updatePreferences
1624
{
1625
 
1626
}
1627
@end
6238 bibiko 1628
 
1629