The R Project SVN R-packages

Rev

Rev 6274 | Go to most recent revision | Details | Compare with Previous | 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 {
6274 bibiko 315
		[self setTextColor:shColorNormal];
316
		[self setInsertionPointColor:shColorCursor];
6228 bibiko 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])];
6274 bibiko 371
	[self setTextColor:shColorNormal];
372
	[self setInsertionPointColor:shColorCursor];
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
 
6274 bibiko 616
		[NSObject cancelPreviousPerformRequestsWithTarget:(RDocumentWinCtrl*)[self delegate] 
617
								selector:@selector(functionRescan) 
618
								object:nil];
619
 
620
		// update function list to display the function in which the cursor is located
621
		[(RDocumentWinCtrl*)[self delegate] performSelector:@selector(functionRescan) withObject:nil afterDelay:0.3f];
622
 
5705 urbaneks 623
	}
624
 
625
	deleteBackward = NO;
626
	startListeningToBoundChanges = YES;
627
 
628
}
629
 
630
#pragma mark -
631
 
5805 bibiko 632
- (BOOL)lineNumberingEnabled
633
{
634
	return lineNumberingEnabled;
635
}
636
 
5705 urbaneks 637
- (void)setDeleteBackward:(BOOL)delBack
638
{
639
	deleteBackward = delBack;
640
}
641
 
642
/**
643
 * Sets Tab Stops width for better editing behaviour
644
 */
645
- (void)setTabStops
646
{
647
 
648
	SLog(@"RScriptEditorTextView: setTabStops <%@>", self);
649
 
650
	NSFont *tvFont = [self font];
651
	int i;
652
	NSTextTab *aTab;
653
	NSMutableArray *myArrayOfTabs;
654
	NSMutableParagraphStyle *paragraphStyle;
655
 
656
	BOOL oldEditableStatus = [self isEditable];
657
	[self setEditable:YES];
658
 
659
	int tabStopWidth = [Preferences integerForKey:RScriptEditorTabWidth withDefault:4];
660
	if(tabStopWidth < 1) tabStopWidth = 1;
661
 
662
	float theTabWidth = [[NSString stringWithString:@" "] sizeWithAttributes:[NSDictionary dictionaryWithObject:tvFont forKey:NSFontAttributeName]].width;
663
	theTabWidth = (float)tabStopWidth * theTabWidth;
664
 
665
	int numberOfTabs = 256/tabStopWidth;
666
	myArrayOfTabs = [NSMutableArray arrayWithCapacity:numberOfTabs];
667
	aTab = [[NSTextTab alloc] initWithType:NSLeftTabStopType location:theTabWidth];
668
	[myArrayOfTabs addObject:aTab];
669
	[aTab release];
670
	for(i=1; i<numberOfTabs; i++) {
671
		aTab = [[NSTextTab alloc] initWithType:NSLeftTabStopType location:theTabWidth + ((float)i * theTabWidth)];
672
		[myArrayOfTabs addObject:aTab];
673
		[aTab release];
674
	}
675
	paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
676
	[paragraphStyle setTabStops:myArrayOfTabs];
677
 
678
	// Soft wrapped lines are indented slightly
679
	[paragraphStyle setHeadIndent:4.0];
680
 
681
	NSMutableDictionary *textAttributes = [[[NSMutableDictionary alloc] initWithCapacity:1] autorelease];
682
	[textAttributes setObject:paragraphStyle forKey:NSParagraphStyleAttributeName];
683
 
684
	NSRange range = NSMakeRange(0, [theTextStorage length]);
685
	if ([self shouldChangeTextInRange:range replacementString:nil]) {
686
		[theTextStorage setAttributes:textAttributes range: range];
687
		[self didChangeText];
688
	}
689
	[self setTypingAttributes:textAttributes];
690
	[self setDefaultParagraphStyle:paragraphStyle];
691
	[self setFont:tvFont];
692
 
693
	[self setEditable:oldEditableStatus];
694
 
695
	[paragraphStyle release];
696
}
697
 
6245 bibiko 698
- (BOOL)isSyntaxHighlighting
699
{
700
	return isSyntaxHighlighting;
701
}
702
 
703
- (BOOL)breakSyntaxHighlighting
704
{
705
	return breakSyntaxHighlighting;
706
}
5705 urbaneks 707
/**
708
 * Syntax Highlighting.
709
 *  
710
 * (The main bottleneck is the [NSTextStorage addAttribute:value:range:] method - the parsing itself is really fast!)
711
 * Some sample code from Andrew Choi ( http://members.shaw.ca/akochoi-old/blog/2003/11-09/index.html#3 ) has been reused.
712
 */
713
- (void)doSyntaxHighlighting
714
{
715
 
6245 bibiko 716
	if(!syntaxHighlightingEnabled)
717
		breakSyntaxHighlighting = 0;
718
 
6248 bibiko 719
	if (!syntaxHighlightingEnabled || [selfDelegate plain]) return;
5705 urbaneks 720
 
5805 bibiko 721
	isSyntaxHighlighting = YES;
722
 
5756 bibiko 723
	NSString *selfstr    = [theTextStorage string];
724
	NSInteger strlength  = (NSInteger)[selfstr length];
5705 urbaneks 725
 
5814 bibiko 726
	// do not highlight if text larger than 10MB
6245 bibiko 727
	if(strlength > 10000000 || !strlength) {
728
		isSyntaxHighlighting = NO;
729
		breakSyntaxHighlighting = 0;
730
		return;
731
	}
5814 bibiko 732
 
5756 bibiko 733
	// == Do highlighting partly (max R_SYNTAX_HILITE_BIAS*2 around visibleRange
734
	// by considering entire lines).
5705 urbaneks 735
 
736
	// Get the text range currently displayed in the view port
6240 bibiko 737
	NSRect visibleRect = [self visibleRect];
5705 urbaneks 738
	NSRange visibleRange = [[self layoutManager] glyphRangeForBoundingRectWithoutAdditionalLayout:visibleRect inTextContainer:[self textContainer]];
739
 
5805 bibiko 740
	if(!visibleRange.length) {
741
		isSyntaxHighlighting = NO;
6245 bibiko 742
		breakSyntaxHighlighting = 0;
5805 bibiko 743
		return;
744
	}
5705 urbaneks 745
 
5756 bibiko 746
	NSInteger start = visibleRange.location - R_SYNTAX_HILITE_BIAS;
747
	if (start > 0)
748
		while(start > 0) {
749
			if(CFStringGetCharacterAtIndex((CFStringRef)selfstr, start)=='\n')
750
				break;
751
			start--;
752
		}
753
	if(start < 0) start = 0;
754
	NSInteger end = NSMaxRange(visibleRange) + R_SYNTAX_HILITE_BIAS;
755
	if (end > strlength) {
5705 urbaneks 756
		end = strlength;
757
	} else {
5756 bibiko 758
		while(end < strlength) {
5705 urbaneks 759
			if(CFStringGetCharacterAtIndex((CFStringRef)selfstr, end)=='\n')
760
				break;
761
			end++;
762
		}
763
	}
764
 
765
	NSRange textRange = NSMakeRange(start, end-start);
766
 
767
	// only to be sure that nothing went wrongly
768
	textRange = NSIntersectionRange(textRange, NSMakeRange(0, [theTextStorage length])); 
769
 
6245 bibiko 770
	if (!textRange.length || textRange.length > 30000) {
5805 bibiko 771
		isSyntaxHighlighting = NO;
6245 bibiko 772
		breakSyntaxHighlighting = 0;
5705 urbaneks 773
		return;
5805 bibiko 774
	}
775
 
5705 urbaneks 776
	[theTextStorage beginEditing];
777
 
5741 urbaneks 778
	NSColor *tokenColor = nil;
5705 urbaneks 779
 
6230 bibiko 780
	size_t token;
5705 urbaneks 781
	NSRange tokenRange;
782
 
783
	// initialise flex
784
	yyuoffset = textRange.location; yyuleng = 0;
6233 bibiko 785
 
786
	BOOL hasFoldedItems = [theTextStorage hasFoldedItems];
5705 urbaneks 787
 
6248 bibiko 788
	if([selfDelegate isRdDocument]) {
5705 urbaneks 789
 
5995 bibiko 790
			rd_switch_to_buffer(rd_scan_string(NSStringUTF8String([selfstr substringWithRange:textRange])));
5705 urbaneks 791
 
5995 bibiko 792
			// now loop through all the tokens
793
			while ((token = rdlex())) {
6233 bibiko 794
				if(hasFoldedItems && (NSInteger)(_foldedImp)(theTextStorage, _foldedSel, yyuoffset) > -1) continue;
5995 bibiko 795
				switch (token) {
796
					case RDPT_COMMENT:
6029 bibiko 797
					    tokenColor = shColorComment;
5995 bibiko 798
					    break;
799
					case RDPT_SECTION:
6029 bibiko 800
					    tokenColor = shColorKeyword;
5995 bibiko 801
					    break;
802
					case RDPT_MACRO_ARG:
6029 bibiko 803
					    tokenColor = shColorNumber;
5995 bibiko 804
					    break;
805
					case RDPT_MACRO_GEN:
6029 bibiko 806
					    tokenColor = shColorNumber;
5995 bibiko 807
					    break;
5998 bibiko 808
					case RDPT_DIRECTIVE:
6029 bibiko 809
					    tokenColor = shColorString;
5998 bibiko 810
					    break;
811
					case RDPT_OTHER:
6029 bibiko 812
					    tokenColor = shColorNormal;
5998 bibiko 813
					    break;
5995 bibiko 814
					default:
6029 bibiko 815
					    tokenColor = shColorNormal;
5995 bibiko 816
				}
5705 urbaneks 817
 
5995 bibiko 818
				tokenRange = NSMakeRange(yyuoffset, yyuleng);
5705 urbaneks 819
 
5995 bibiko 820
				// make sure that tokenRange is valid (and therefore within textRange)
821
				// otherwise a bug in the lex code could cause the the TextView to crash
822
				// NOTE Disabled for testing purposes for speed it up
823
				tokenRange = NSIntersectionRange(tokenRange, textRange);
824
				if (!tokenRange.length) continue;
825
 
826
				NSMutableAttributedStringAddAttributeValueRange(theTextStorage, NSForegroundColorAttributeName, tokenColor, tokenRange);
827
 
6232 bibiko 828
				if(breakSyntaxHighlighting) {
829
 
830
					// Cancel calling doSyntaxHighlighting
831
					[NSObject cancelPreviousPerformRequestsWithTarget:self 
832
											selector:@selector(doSyntaxHighlighting) 
833
											object:nil];
834
 
6245 bibiko 835
					[self performSelector:@selector(doSyntaxHighlighting) withObject:nil afterDelay:0.08f];
6232 bibiko 836
 
837
					breakSyntaxHighlighting = 0;
838
					break;
839
 
840
				}
841
 
5995 bibiko 842
			}
843
 
844
	} else {
845
 
846
		yy_switch_to_buffer(yy_scan_string(NSStringUTF8String([selfstr substringWithRange:textRange])));
847
 
848
		// now loop through all the tokens
849
		while ((token = yylex())) {
6233 bibiko 850
			if(hasFoldedItems && (NSInteger)(_foldedImp)(theTextStorage, _foldedSel, yyuoffset) > -1) continue;
5995 bibiko 851
			switch (token) {
852
				case RPT_SINGLE_QUOTED_TEXT:
853
				case RPT_DOUBLE_QUOTED_TEXT:
854
				    tokenColor = shColorString;
855
				    break;
856
				case RPT_RESERVED_WORD:
857
				    tokenColor = shColorKeyword;
858
				    break;
859
				case RPT_NUMERIC:
860
					tokenColor = shColorNumber;
861
					break;
862
				case RPT_BACKTICK_QUOTED_TEXT:
863
				    tokenColor = shColorString;
864
				    break;
865
				case RPT_COMMENT:
866
				    tokenColor = shColorComment;
867
				    break;
868
				case RPT_VARIABLE:
869
				    tokenColor = shColorIdentifier;
870
				    break;
871
				default:
872
				    tokenColor = shColorNormal;
873
			}
874
 
875
			tokenRange = NSMakeRange(yyuoffset, yyuleng);
876
 
877
			// make sure that tokenRange is valid (and therefore within textRange)
878
			// otherwise a bug in the lex code could cause the the TextView to crash
879
			tokenRange = NSIntersectionRange(tokenRange, textRange);
880
			if (!tokenRange.length) continue;
881
 
882
			NSMutableAttributedStringAddAttributeValueRange(theTextStorage, NSForegroundColorAttributeName, tokenColor, tokenRange);
6232 bibiko 883
 
884
			if(breakSyntaxHighlighting) {
885
 
886
				// Cancel calling doSyntaxHighlighting
6242 bibiko 887
				[NSObject cancelPreviousPerformRequestsWithTarget:self 
888
										selector:@selector(doSyntaxHighlighting) 
889
										object:nil];
890
 
6245 bibiko 891
				[self performSelector:@selector(doSyntaxHighlighting) withObject:nil afterDelay:0.08f];
6232 bibiko 892
 
893
				breakSyntaxHighlighting = 0;
894
				break;
895
 
896
			}
897
 
5995 bibiko 898
		}
6233 bibiko 899
 
5705 urbaneks 900
	}
5741 urbaneks 901
 
902
	// set current textColor to the color of the caret's position - 1
903
	// to try to suppress writing in normalColor before syntax highlighting 
904
	NSUInteger ix = [self selectedRange].location;
905
	if(ix > 1) {
906
		NSMutableDictionary *typeAttr = [NSMutableDictionary dictionary];
907
		[typeAttr setDictionary:[self typingAttributes]];
908
		NSColor *c = [theTextStorage attribute:NSForegroundColorAttributeName atIndex:ix-1 effectiveRange:nil];
909
		if(c) [typeAttr setObject:c forKey:NSForegroundColorAttributeName];
910
		[self setTypingAttributes:typeAttr];
911
	}
912
 
5705 urbaneks 913
	[theTextStorage endEditing];
6245 bibiko 914
 
6240 bibiko 915
	[self setNeedsDisplayInRect:visibleRect];
5705 urbaneks 916
 
6245 bibiko 917
	breakSyntaxHighlighting = 0;
918
 
919
	isSyntaxHighlighting = NO;
920
 
5705 urbaneks 921
}
922
 
923
-(void)resetHighlights
924
{
6093 bibiko 925
 
5705 urbaneks 926
	SLog(@"RScriptEditorTextView: resetHighlights with current highlite %d", currentHighlight);
927
 
928
	if (currentHighlight>-1) {
929
		if (currentHighlight<[theTextStorage length]) {
930
			NSLayoutManager *lm = [self layoutManager];
931
			if (lm) {
932
				NSRange fr = NSMakeRange(currentHighlight,1);
933
				NSDictionary *d = [lm temporaryAttributesAtCharacterIndex:currentHighlight effectiveRange:&fr];
934
				if (!d || [d objectForKey:NSBackgroundColorAttributeName]==nil) {
935
					fr = NSMakeRange(0,[[self string] length]);
936
					SLog(@"resetHighlights: attribute at %d not found, clearing all %d characters - better safe than sorry", currentHighlight, fr.length);
937
				}
938
				[lm removeTemporaryAttribute:NSBackgroundColorAttributeName forCharacterRange:fr];
939
			}
940
		}
941
		currentHighlight=-1;
942
	}
943
}
944
 
6093 bibiko 945
-(void)highlightCharacter:(NSNumber*)loc
5705 urbaneks 946
{
6094 urbaneks 947
	NSInteger pos = [loc intValue];
5705 urbaneks 948
 
949
	SLog(@"RScriptEditorTextView: highlightCharacter: %d", pos);
950
 
951
	if (pos>=0 && pos<[[self string] length]) {
6093 bibiko 952
 
953
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
954
		[self showFindIndicatorForRange:NSMakeRange(pos, 1)];
955
#else
956
		[self resetHighlights];
5705 urbaneks 957
		NSLayoutManager *lm = [self layoutManager];
958
		if (lm) {
959
			currentHighlight = pos;
960
			[lm setTemporaryAttributes:highlightColorAttr forCharacterRange:NSMakeRange(pos, 1)];
961
			[self performSelector:@selector(resetBackgroundColor:) withObject:nil afterDelay:braceHighlightInterval];
6093 bibiko 962
		}
963
 
964
#endif
965
 
5705 urbaneks 966
	}
6093 bibiko 967
	else SLog(@"highlightCharacter: attempt to set highlight %d beyond the text range 0:%d - I refuse!", pos, [[self string] length] - 1);
5705 urbaneks 968
}
969
 
970
-(void)resetBackgroundColor:(id)sender
971
{
972
	[self resetHighlights];
973
}
974
 
975
/**
976
 * Scrollview delegate after the textView's view port was changed.
6232 bibiko 977
 * Manily used to update the syntax highlighting for a large text size
5705 urbaneks 978
 */
979
- (void)boundsDidChangeNotification:(NSNotification *)notification
980
{
981
 
982
	if(startListeningToBoundChanges) {
5805 bibiko 983
 
6245 bibiko 984
		breakSyntaxHighlighting = 1;
985
 
5705 urbaneks 986
		[NSObject cancelPreviousPerformRequestsWithTarget:self 
987
									selector:@selector(doSyntaxHighlighting) 
988
									object:nil];
989
 
5805 bibiko 990
		if(![theTextStorage changeInLength]) {
6232 bibiko 991
			if([[theTextStorage string] length] > 120000)
992
				breakSyntaxHighlighting = 2;
6245 bibiko 993
			[self performSelector:@selector(doSyntaxHighlighting) withObject:nil afterDelay:0.08];
5805 bibiko 994
		}
6238 bibiko 995
		if(lineNumberingEnabled) {
996
			[NSObject cancelPreviousPerformRequestsWithTarget:[[self enclosingScrollView] verticalRulerView] 
997
										selector:@selector(refresh) 
998
										object:nil];
999
 
1000
			[[[self enclosingScrollView] verticalRulerView] performSelector:@selector(refresh) withObject:nil afterDelay:0.001f];
1001
		}
5705 urbaneks 1002
 
1003
	}
1004
 
1005
}
1006
 
6233 bibiko 1007
- (IBAction)undo:(id)sender
1008
{
1009
	if([[self undoManager] canUndo]) {
1010
		[[self undoManager] undo];
1011
		if(lineNumberingEnabled)
1012
			[[[self enclosingScrollView] verticalRulerView] performSelector:@selector(refresh) withObject:nil afterDelay:0.0f];
1013
	}
1014
}
1015
 
1016
- (IBAction)redo:(id)sender
1017
{
1018
	if([[self undoManager] canRedo]) {
1019
		[[self undoManager] redo];
1020
		if(lineNumberingEnabled)
1021
			[[[self enclosingScrollView] verticalRulerView] performSelector:@selector(refresh) withObject:nil afterDelay:0.0f];
1022
	}
1023
}
1024
 
6128 bibiko 1025
#pragma mark -
6232 bibiko 1026
#pragma mark Folding
6128 bibiko 1027
 
6238 bibiko 1028
/*
1029
The idea of code folding is to replace the folded range while layouting by NSControlGlyphs
1030
which will be rendered as zero width glyphs. This has the advantage that all text actions like 
1031
copying, line numbering, etc. works without additional stuff.
1032
 
1033
For now it is only possible to fold a block if:
1034
- the starting { is placed at the end of a line (or with a following comment)
1035
- there's only ONE starting { in that line and NO }
1036
- the ending } is placed at the of a line (or with a following comment)
1037
- there's only ONE ending } in that line and NO {
1038
 
1039
Due to speed issues all folded ranges will be stored in a 3d C array of the size
1040
R_MAX_FOLDED_ITEMS = 1024 defined in RScriptEditorTextStorage.h whereby index:
1041
 
1042
1 - range length
1043
2 - location + length (pre-calculate for speed)
1044
 
1045
If the user locates the cursor inside a folded range or an action will locate the cursor
1046
inside such a range (go to line, find something) the folded range will be unfolded.
1047
... for more info ask Hans-J. Bibiko
1048
*/
1049
 
1050
- (IBAction)unfoldCurrentBlock:(id)sender
6128 bibiko 1051
{
6238 bibiko 1052
	NSUInteger caretPosition = [self selectedRange].location;
6128 bibiko 1053
 
6238 bibiko 1054
	NSRange r = [[self string] lineRangeForRange:NSMakeRange(caretPosition, 0)];
1055
 
1056
	if([theTextStorage foldedAtIndex:NSMaxRange(r)] > -1) {
1057
		[self unfoldLinesContainingCharacterAtIndex:NSMaxRange(r)];
1058
		return;
1059
	}
1060
	if([theTextStorage foldedAtIndex:r.location] > -1) {
1061
		[self unfoldLinesContainingCharacterAtIndex:r.location];
1062
		return;
1063
	}
1064
}
1065
 
1066
- (IBAction)foldCurrentBlock:(id)sender
1067
{
1068
	NSUInteger caretPosition = [self selectedRange].location;
1069
	NSInteger foldItem = 0;
1070
	unichar c;
1071
 
1072
	NSRange r = [[self string] lineRangeForRange:NSMakeRange(caretPosition, 0)];
1073
 
1074
	foldItem = [self foldStatusAtIndex:NSMaxRange(r)-2];
1075
	if(foldItem == 1) { // is current line set to ▼ set caret to end of line
1076
		caretPosition = NSMaxRange(r)-1;
1077
	} else { // otherwise set caret to begin of line
1078
		caretPosition = r.location;
1079
	}
1080
 
1081
	NSUInteger stringLength = [[self string] length];
1082
	if(!stringLength) return;
1083
	if(caretPosition == 0 || caretPosition >= [[self string] length]) return;
1084
 
1085
	CFStringRef parserStringRef = (CFStringRef)[self string];
1086
 
1087
	unichar co = '{'; // opening char
1088
	unichar cc = '}'; // closing char
1089
 
1090
	NSInteger start = -1;
1091
	NSInteger end = -1;
1092
	NSInteger bracketCounter = 0;
1093
 
1094
	c = CFStringGetCharacterAtIndex(parserStringRef, caretPosition);
1095
	if(c == cc)
1096
		bracketCounter--;
1097
	if(c == co)
1098
		bracketCounter++;
1099
 
1100
	for(NSInteger i=caretPosition; i>=0; i--) {
1101
		if([self parserContextForPosition:i] != pcExpression) continue;
1102
		c = CFStringGetCharacterAtIndex(parserStringRef, i);
1103
		if(c == co) {
1104
			if(!bracketCounter) {
1105
				start = i;
1106
				break;
1107
			}
1108
			bracketCounter--;
1109
		}
1110
		if(c == cc) {
1111
			bracketCounter++;
1112
		}
1113
	}
1114
	if(start < 0 ) return;
1115
 
1116
	// go up for lines like "} else {"
1117
	if(start && [self foldStatusAtIndex:start-1] == 0) {
1118
		for(NSInteger i=start-1; i>=0; i--) {
1119
			c = CFStringGetCharacterAtIndex(parserStringRef, i);
1120
			if(c == '\n' || c == '\r') break;
1121
			if([self parserContextForPosition:i] != pcExpression) continue;
1122
			if(c == cc && i > 0) {
1123
				bracketCounter = 0;
1124
				for(NSInteger j=i-1; j>=0; j--) {
1125
					if([self parserContextForPosition:j] != pcExpression) continue;
1126
					c = CFStringGetCharacterAtIndex(parserStringRef, j);
1127
					if(c == co) {
1128
						if(!bracketCounter) {
1129
							start = j;
1130
							break;
1131
						}
1132
						bracketCounter--;
1133
					}
1134
					if(c == cc) {
1135
						bracketCounter++;
1136
					}
1137
				}
1138
				break;
1139
			}
1140
		}
1141
	}		
1142
 
1143
 
1144
	bracketCounter = 0;
1145
	for(NSUInteger i=caretPosition; i<stringLength; i++) {
1146
		if([self parserContextForPosition:i] != pcExpression) continue;
1147
		c = CFStringGetCharacterAtIndex(parserStringRef, i);
1148
		if(c == co) {
1149
			bracketCounter++;
1150
		}
1151
		if(c == cc) {
1152
			if(!bracketCounter) {
1153
				end = i+1;
1154
				BOOL goAhead = NO;
1155
				//go ahead for lines a la  "} else {"
1156
				for(NSUInteger j=end; j<stringLength; j++) {
1157
					c = CFStringGetCharacterAtIndex(parserStringRef, j);
1158
					if(c == '\n' || c == '\r') {
1159
						break;
1160
					}
1161
					if(c == '\t' || c == ' ') {
1162
						continue;
1163
					}
1164
					if([self parserContextForPosition:j] != pcExpression) continue;
1165
					if(c == co) {
1166
						goAhead = YES;
1167
						break;
1168
					}
1169
				}
1170
				if(!goAhead) break;
1171
			}
1172
			bracketCounter--;
1173
		}
1174
	}
1175
 
1176
	if(end < 0 || bracketCounter || end-start < 1) return;
1177
 
1178
	NSRange foldRange = NSMakeRange(start, end-start);
1179
	if(![theTextStorage existsFoldedRange:foldRange]) {
1180
		// set caret for ▲ line inside {} for scrolling
1181
		if(foldItem == 2)
1182
			[self setSelectedRange:NSMakeRange(r.location, 0)];
6242 bibiko 1183
		[self foldLinesInRange:foldRange blockMode:NO];
6238 bibiko 1184
	}
1185
 
1186
}
1187
 
1188
- (IBAction)foldBlockAtLevel:(id)sender
1189
{
6242 bibiko 1190
 
6238 bibiko 1191
	NSInteger level = [sender tag];
1192
	NSInteger bracketCounter = 0;
1193
	NSInteger start = 0;
1194
	NSInteger end = 0;
1195
	CFStringRef str = (CFStringRef)[self string];
1196
 
1197
	unichar c;
1198
 
6242 bibiko 1199
	[[self undoManager] disableUndoRegistration];
6238 bibiko 1200
 
1201
	for(NSInteger i=0; i<[[self string] length]; i++) {
1202
		c = CFStringGetCharacterAtIndex(str, i);
1203
		if([self parserContextForPosition:i] != pcExpression) continue;
1204
		if(c == '{') {
1205
			bracketCounter++;
1206
			if([self foldStatusAtIndex:i] == 1) {
1207
				if(bracketCounter == level+1) {
1208
					start = i;
1209
				}
1210
			}
1211
			continue;
1212
		}
1213
		if(c == '}') {
1214
			bracketCounter--;
1215
			if([self foldStatusAtIndex:i] == 2) {
1216
				if(bracketCounter == level) {
1217
					end = i;
1218
					NSRange r = NSMakeRange(start, end - start+1);
1219
					if(![theTextStorage existsFoldedRange:r])
6242 bibiko 1220
						[self foldLinesInRange:r blockMode:YES];
6238 bibiko 1221
				}
1222
				if(bracketCounter < 0) {
1223
					NSBeep();
1224
					return;
1225
				}
1226
			}
1227
		}
1228
	}
6242 bibiko 1229
 
1230
	[self didChangeText];
6238 bibiko 1231
 
6246 bibiko 1232
	// NSRange r = [[self layoutManager] characterRangeForGlyphRange:[[self layoutManager] 
1233
	// 									glyphRangeForBoundingRect:[scrollView documentVisibleRect] 
1234
	// 											  inTextContainer:[self textContainer]] actualGlyphRange:NULL];
1235
	// 
1236
	// [theTextStorage ensureAttributesAreFixedInRange:r];
6242 bibiko 1237
 
1238
	if(lineNumberingEnabled)
1239
		[[[self enclosingScrollView] verticalRulerView] performSelector:@selector(refresh) withObject:nil afterDelay:0.0f];
1240
 
1241
	[NSObject cancelPreviousPerformRequestsWithTarget:self 
1242
							selector:@selector(doSyntaxHighlighting) 
1243
							object:nil];
1244
 
1245
	[self performSelector:@selector(doSyntaxHighlighting) withObject:nil afterDelay:0.02f];
1246
 
1247
	[[self undoManager] enableUndoRegistration];
6238 bibiko 1248
}
1249
 
1250
- (IBAction)unFoldAllBlocks:(id)sender
1251
{
1252
	[theTextStorage removeAllFoldedRanges];
1253
 
1254
	[self didChangeText];
1255
 
6246 bibiko 1256
	// NSRange r = NSMakeRange(0, [[self string] length]);
6238 bibiko 1257
 
6246 bibiko 1258
	// [theTextStorage fixAttributesInRange:r];
1259
	// [theTextStorage fixAttachmentAttributeInRange:r];
1260
	// [theTextStorage ensureAttributesAreFixedInRange:r];
6238 bibiko 1261
 
1262
 
1263
	if(lineNumberingEnabled)
1264
		[[[self enclosingScrollView] verticalRulerView] performSelector:@selector(refresh) withObject:nil afterDelay:0.0f];
6245 bibiko 1265
 
1266
	breakSyntaxHighlighting = 1;
6238 bibiko 1267
 
1268
	[NSObject cancelPreviousPerformRequestsWithTarget:self 
1269
							selector:@selector(doSyntaxHighlighting) 
1270
							object:nil];
1271
 
6245 bibiko 1272
	[self performSelector:@selector(doSyntaxHighlighting) withObject:nil afterDelay:0.01f];
6238 bibiko 1273
}
1274
 
1275
- (void)refoldLinesInRange:(NSRange)range
1276
{
6249 bibiko 1277
 
6233 bibiko 1278
	NSInteger foldId = [theTextStorage registerFoldedRange:range];
6128 bibiko 1279
 
6233 bibiko 1280
	if(foldId < 0) {
6238 bibiko 1281
		[RTooltip showWithObject:NLS(@"Maximum number of folded code fragments is reached.") atLocation:[NSEvent mouseLocation]];
6233 bibiko 1282
		return;
1283
	}
6137 bibiko 1284
 
6238 bibiko 1285
	range.location++;
1286
	range.length -= 2;
1287
 
1288
	if(!range.length) return;
1289
 
1290
	NSString *tooltip = nil;
1291
	if(range.length < 300)
1292
		tooltip = [[self string] substringWithRange:range];
1293
	else
1294
		tooltip = [[[self string] substringWithRange:NSMakeRange(range.location, 300)] stringByAppendingString:@"\n…"];
1295
 
1296
	[theTextStorage beginEditing];
1297
	[theTextStorage addAttribute:NSCursorAttributeName value:[NSCursor arrowCursor] range:range];
1298
	[theTextStorage addAttribute:NSToolTipAttributeName value:tooltip range:range];
1299
	[theTextStorage endEditing];
1300
 
1301
}
1302
 
6242 bibiko 1303
- (BOOL)foldLinesInRange:(NSRange)range blockMode:(BOOL)blockMode
6238 bibiko 1304
{
1305
	if(range.length < 5) {
1306
		return NO;
1307
	}
1308
 
1309
	NSInteger caretPosition = [self selectedRange].location;
1310
	BOOL caretWasInsideFoldedRange = NO;
1311
 
1312
	// Check for valid folding range
1313
	// fold only range if { and } are the last chars at the line
1314
 
1315
	NSString *selfStr = [self string];
1316
	NSRange startLineRange = [selfStr lineRangeForRange:NSMakeRange(range.location, 0)];
1317
	NSRange endLineRange   = [selfStr lineRangeForRange:NSMakeRange(NSMaxRange(range), 0)];
1318
 
1319
	if(!startLineRange.length || !endLineRange.length) {
1320
		return NO;
1321
	}
1322
 
1323
	// Do not fold a single line
1324
	if(startLineRange.location == endLineRange.location) {
1325
		return NO;
1326
	}
1327
 
1328
	NSInteger status;
1329
 
1330
	status = [self foldStatusAtIndex:NSMaxRange(startLineRange)-2];	
1331
	if(status != 1) {
1332
		return NO;
1333
	}
1334
 
1335
	unichar c = CFStringGetCharacterAtIndex((CFStringRef)selfStr, NSMaxRange(endLineRange)-1);
1336
	if(c == '\n' || c == '\r')
1337
		status = [self foldStatusAtIndex:NSMaxRange(endLineRange)-2];
1338
	else
1339
		status = [self foldStatusAtIndex:NSMaxRange(endLineRange)-1];	
1340
	if(status != 2) {
1341
		return NO;
1342
	}
1343
 
6233 bibiko 1344
	if(caretPosition >= range.location && caretPosition < NSMaxRange(range)) {
1345
		[self setSelectedRange:NSMakeRange(NSMaxRange(range), 0)];
1346
		caretWasInsideFoldedRange = YES;
1347
	}
6137 bibiko 1348
 
6238 bibiko 1349
	NSInteger foldId = [theTextStorage registerFoldedRange:range];
6233 bibiko 1350
 
6238 bibiko 1351
	if(foldId < 0) {
1352
		[RTooltip showWithObject:NLS(@"Maximum number of folded code fragments is reached.") atLocation:[NSEvent mouseLocation]];
1353
		return NO;
6137 bibiko 1354
	}
1355
 
6238 bibiko 1356
	range.location++;
1357
	range.length -= 2;
6128 bibiko 1358
 
6238 bibiko 1359
	if(!range.length) return NO;
1360
 
6242 bibiko 1361
	if(!blockMode) {
1362
		[[self undoManager] disableUndoRegistration];
1363
		if(![self shouldChangeTextInRange:range replacementString:nil]) {
1364
			[[self undoManager] enableUndoRegistration];
1365
			return NO;
1366
		}
6238 bibiko 1367
		[[self undoManager] enableUndoRegistration];
1368
	}
6128 bibiko 1369
 
6137 bibiko 1370
	NSString *tooltip = nil;
6233 bibiko 1371
	if(range.length < 300)
1372
		tooltip = [[self string] substringWithRange:range];
6137 bibiko 1373
	else
6233 bibiko 1374
		tooltip = [[[self string] substringWithRange:NSMakeRange(range.location, 300)] stringByAppendingString:@"\n…"];
6137 bibiko 1375
 
6128 bibiko 1376
	[theTextStorage beginEditing];
6233 bibiko 1377
	[theTextStorage addAttribute:NSCursorAttributeName value:[NSCursor arrowCursor] range:range];
1378
	[theTextStorage addAttribute:NSToolTipAttributeName value:tooltip range:range];
6128 bibiko 1379
	[theTextStorage endEditing];
1380
 
6242 bibiko 1381
	if(!blockMode) {
1382
		[self didChangeText];
6233 bibiko 1383
 
6242 bibiko 1384
		if(caretWasInsideFoldedRange)
1385
			[self scrollRangeToVisible:[self selectedRange]];
6233 bibiko 1386
 
6246 bibiko 1387
		// NSRange r = [[self layoutManager] characterRangeForGlyphRange:[[self layoutManager] 
1388
		// 									glyphRangeForBoundingRect:[scrollView documentVisibleRect] 
1389
		// 											  inTextContainer:[self textContainer]] actualGlyphRange:NULL];
6238 bibiko 1390
 
6246 bibiko 1391
		// [theTextStorage ensureAttributesAreFixedInRange:r];
6238 bibiko 1392
 
6242 bibiko 1393
		if(lineNumberingEnabled)
1394
			[[[self enclosingScrollView] verticalRulerView] performSelector:@selector(refresh) withObject:nil afterDelay:0.0f];
6233 bibiko 1395
 
6242 bibiko 1396
		[NSObject cancelPreviousPerformRequestsWithTarget:self 
1397
								selector:@selector(doSyntaxHighlighting) 
1398
								object:nil];
6128 bibiko 1399
 
6242 bibiko 1400
		[self performSelector:@selector(doSyntaxHighlighting) withObject:nil afterDelay:0.02f];
1401
	}
6238 bibiko 1402
 
1403
	return YES;
1404
 
6128 bibiko 1405
}
1406
 
6238 bibiko 1407
- (NSInteger)foldStatusAtIndex:(NSInteger)index
1408
{
1409
 
1410
	if(index < 0 || index >= [[self string] length]) return 0;
1411
 
1412
	NSInteger status = 0; // 0 = no; 1 = ▼; 2 = ▲
1413
 
1414
	NSInteger i = index;
1415
	NSInteger type;
1416
	NSString *selfStr = [self string];
1417
	unichar c;
1418
 
6248 bibiko 1419
	BOOL isRd = [selfDelegate isRdDocument];
6238 bibiko 1420
 
1421
	unichar commentSign = (isRd) ? '%' : '#';
1422
 
1423
	// start checking from the end of a line
1424
	while(i >= 0) {
1425
 
1426
		c = CFStringGetCharacterAtIndex((CFStringRef)selfStr, i);
1427
 
1428
		// Check only one line
1429
		if(c=='\n' || c=='\r') break;
1430
 
1431
		// Ignore white spaces and comment sign
1432
		if(c==' ' || c=='\t' || c==commentSign) {
1433
			i--;
1434
			continue;
1435
		}
1436
 
1437
		type = [self parserContextForPosition:i];
1438
 
1439
		// Ignore comments
1440
		if(type == pcComment || c==commentSign) {
1441
			i--;
1442
			continue;
1443
		}
1444
 
1445
		// ======= Check for ▼ 
1446
		if(c=='{' && type == pcExpression) {
1447
			status = 1;
1448
			// look for lines a la "} else {" - if so do not draw folding marker
1449
			i--;
1450
			while(i>=0) {
1451
				c = CFStringGetCharacterAtIndex((CFStringRef)selfStr, i);
1452
				if(c=='\n' || c=='\r') break;
1453
				if((c=='}' || c=='{')&& [self parserContextForPosition:i] == pcExpression) {
1454
					status = 0;
1455
					break;
1456
				}
1457
				i--;
1458
			}
1459
			break;
1460
		}
1461
 
1462
		// ======= Check for ▲
1463
		if(c=='}') {
1464
			status = 2;
1465
			i--;
1466
			while(i>=0) {
1467
				c = CFStringGetCharacterAtIndex((CFStringRef)selfStr, i);
1468
				if(c=='\n' || c=='\r') break;
1469
				if((c=='}' || c=='{')&& [self parserContextForPosition:i] == pcExpression) {
1470
					status = 0;
1471
					break;
1472
				}
1473
				if(isRd) {
1474
					if(c != '\t' || c != ' ') {
1475
						status = 0;
1476
						break;
1477
					}
1478
				}
1479
				i--;
1480
			}
1481
		}
1482
		break;
1483
 
1484
	}
1485
 
1486
	return status;
1487
 
1488
}
1489
 
6128 bibiko 1490
- (BOOL)unfoldLinesContainingCharacterAtIndex:(NSUInteger)charIndex
1491
{
1492
 
6233 bibiko 1493
	NSInteger foldIndex = [theTextStorage foldedAtIndex:charIndex];
6128 bibiko 1494
 
6233 bibiko 1495
	if(foldIndex > -1) {
1496
 
6238 bibiko 1497
		NSRange range = [theTextStorage foldedRangeAtIndex:foldIndex];
1498
		[[self undoManager] disableUndoRegistration];
1499
		if(![self shouldChangeTextInRange:range replacementString:nil]) {
1500
			[[self undoManager] enableUndoRegistration];
1501
			return NO;
1502
		}
1503
		[[self undoManager] enableUndoRegistration];
1504
 
1505
		[theTextStorage removeFoldedRangeWithIndex:foldIndex];
1506
 
6128 bibiko 1507
		[self didChangeText];
6233 bibiko 1508
 
1509
		[NSObject cancelPreviousPerformRequestsWithTarget:self 
1510
								selector:@selector(doSyntaxHighlighting) 
1511
								object:nil];
1512
 
6246 bibiko 1513
		// NSRange r = [[self layoutManager] characterRangeForGlyphRange:[[self layoutManager] 
1514
		// 									glyphRangeForBoundingRect:[scrollView documentVisibleRect] 
1515
		// 											  inTextContainer:[self textContainer]] actualGlyphRange:NULL];
1516
		// 
1517
		// [theTextStorage ensureAttributesAreFixedInRange:r];
6233 bibiko 1518
 
6238 bibiko 1519
		if(lineNumberingEnabled)
1520
			[[[self enclosingScrollView] verticalRulerView] performSelector:@selector(refresh) withObject:nil afterDelay:0.0f];
1521
 
1522
		[self performSelector:@selector(doSyntaxHighlighting) withObject:nil afterDelay:0.02f];
1523
 
6128 bibiko 1524
		return YES;
1525
	}
6233 bibiko 1526
 
6128 bibiko 1527
	return NO;
6233 bibiko 1528
 
6128 bibiko 1529
}
1530
 
6137 bibiko 1531
- (void)mouseDown:(NSEvent *)event
1532
{
1533
 
6238 bibiko 1534
	if(![theTextStorage isKindOfClass:[RScriptEditorTextStorage class]]) {
1535
		[super mouseDown:event];
1536
		return;
1537
	}
1538
 
1539
	RScriptEditorLayoutManager *layoutManager = (RScriptEditorLayoutManager*)[self layoutManager];
7293 urbaneks 1540
	NSUInteger glyphIndex = [layoutManager glyphIndexForPoint:[self convertPoint:[event locationInWindow] fromView:nil] inTextContainer:[self textContainer]];
6238 bibiko 1541
 
1542
	// trigger unfolding if inside foldingAttachmentCell
1543
	if (glyphIndex < [layoutManager numberOfGlyphs]) {
1544
 
1545
		NSUInteger charIndex = [layoutManager characterIndexForGlyphAtIndex:glyphIndex];
1546
		NSInteger foldIndex  = [theTextStorage foldedForIndicatorAtIndex:charIndex];
1547
 
1548
		if (foldIndex > -1) {
1549
 
1550
			NSInteger foldStart = [theTextStorage foldedRangeAtIndex:foldIndex].location+1;
1551
			NSTextAttachment *attachment = [theTextStorage attribute:NSAttachmentAttributeName atIndex:foldStart effectiveRange:NULL];
1552
 
1553
			if (attachment) {
1554
				NSTextAttachmentCell *cell = (NSTextAttachmentCell *)[attachment attachmentCell];
1555
				NSRect cellFrame;
1556
				NSPoint delta;
1557
 
1558
				glyphIndex = [layoutManager glyphIndexForCharacterAtIndex:foldStart];
1559
 
1560
				cellFrame.origin = [self textContainerOrigin];
1561
				cellFrame.size = [layoutManager attachmentSizeForGlyphAtIndex:glyphIndex];
1562
 
1563
				delta = [layoutManager lineFragmentRectForGlyphAtIndex:glyphIndex effectiveRange:NULL].origin;
1564
				cellFrame.origin.x += delta.x;
1565
				cellFrame.origin.y += delta.y;
1566
 
1567
				cellFrame.origin.x += [layoutManager locationForGlyphAtIndex:glyphIndex].x;
1568
 
1569
				if ([cell wantsToTrackMouseForEvent:event inRect:cellFrame ofView:self atCharacterIndex:foldStart] 
1570
						&& [cell trackMouse:event inRect:cellFrame ofView:self atCharacterIndex:foldStart untilMouseUp:YES]) return;
1571
			}
1572
		}
1573
	}
1574
 
6137 bibiko 1575
	[super mouseDown:event];
6233 bibiko 1576
}
1577
 
6137 bibiko 1578
// - (void)setSelectedRanges:(NSArray *)ranges affinity:(NSSelectionAffinity)affinity stillSelecting:(BOOL)stillSelectingFlag
1579
// {
1580
// 
1581
// 	if(!stillSelectingFlag) {
1582
// 
1583
// 		if([ranges count] > 0) {
1584
// 
1585
// 			// Adjust range for folded text chunks; additional checks will be made in
1586
// 			// the self's delegate [RDocumentWinCtrl:textViewDidChangeSelection:]
1587
// 
1588
// 			NSRange range = [[ranges objectAtIndex:0] rangeValue];
1589
// 
1590
// 			if(!range.length) {
1591
// 				[super setSelectedRanges:ranges affinity:affinity stillSelecting:stillSelectingFlag];
1592
// 				return;
1593
// 			}
1594
// 
1595
// 			NSRange glyphRange = [[self layoutManager] glyphRangeForCharacterRange:range actualCharacterRange:NULL];
1596
// 
1597
// 			if(glyphRange.location != range.location || glyphRange.length != range.length) {
1598
// 
1599
// 				if([ranges count] == 2)
1600
// 					glyphRange.length += [[ranges objectAtIndex:1] rangeValue].length;
1601
// 
1602
// 				SLog(@"RScriptEditorTextView:setSelectedRanges: adjust range via glyph range from %@ to %@", NSStringFromRange(range), NSStringFromRange(glyphRange));
1603
// 
1604
// 				[super setSelectedRange:glyphRange];
1605
// 				return;
1606
// 
1607
// 			}
1608
// 		}
1609
// 	}
1610
// 
1611
// 	[super setSelectedRanges:ranges affinity:affinity stillSelecting:stillSelectingFlag];
6233 bibiko 1612
// 
1613
// }
1614
// 
6128 bibiko 1615
 
6238 bibiko 1616
// - (void)setTypingAttributes:(NSDictionary *)attrs
1617
// {
1618
// 	// we don't want to store foldingAttributeId as a typing attribute
1619
// 	if ([attrs objectForKey:foldingAttributeId]) {
1620
// 		NSMutableDictionary *copy = [[attrs mutableCopyWithZone:NULL] autorelease];
1621
// 		[copy removeObjectForKey:foldingAttributeId];
1622
// 		attrs = copy;
1623
// 	}
1624
// 
1625
// 	[super setTypingAttributes:attrs];
1626
// }
1627
// 
6128 bibiko 1628
#pragma mark -
1629
 
5705 urbaneks 1630
- (void)updatePreferences
1631
{
1632
 
1633
}
1634
@end
6238 bibiko 1635
 
1636