forked from pmougin/F-Script
-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathBlockInspector.m
388 lines (327 loc) · 12.7 KB
/
BlockInspector.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
/* BlockInspector.m Copyright (c) 1998-2009 Philippe Mougin. */
/* This software is open source. See the license. */
#import "build_config.h"
#import "BlockInspector.h"
#import "FSBlock.h"
#import "FSCompiler.h"
#import "FSSymbolTable.h"
#import "BlockPrivate.h"
#import "BlockStackElem.h"
#import <AppKit/NSButton.h>
#import "FSMiscTools.h"
#import "FScriptFunctions.h"
#import "FSNSObject.h"
#import "FSVoid.h"
#import "FSObjectBrowser.h"
#import "FSSystem.h"
#import "FSSystemPrivate.h"
#import "FSInterpreter.h"
#import "FSArray.h"
#import "FSBlockCompilationResult.h"
static NSPoint topLeftPoint = {0,0}; // Used for cascading windows.
@implementation BlockInspector
- (id)activate
{
if (!splitView)
{
[inspectedObject retain];
[NSBundle loadNibNamed:@"blockInspector.nib" owner:self];
}
[[splitView window] makeKeyAndOrderFront:self];
@try
{
// The call to isCompact may raise an FSExecutionErrorException
if ([inspectedObject isCompact])
[[sourceView documentView] setSelectedRange:NSMakeRange(1,[[[sourceView documentView] string] length]-1)];
}
@catch (NSException *exception)
{
if (! [[exception name] isEqualToString:FSExecutionErrorException])
@throw;
}
[[splitView window] makeFirstResponder:[sourceView documentView]];
return self;
}
- (void)awakeFromNib
{
[[sourceView documentView] setDelegate:self];
[[sourceView documentView] setString:[inspectedObject printString]];
[[sourceView documentView] setFont:[NSFont userFixedPitchFontOfSize:userFixedPitchFontSize()]];
[[sourceView documentView] setUsesFindPanel:YES];
[[sourceView documentView] setAllowsUndo:YES];
[[messageView documentView] setFont:[NSFont userFixedPitchFontOfSize:userFixedPitchFontSize()]];
[[messageView documentView] setUsesFindPanel:YES];
[splitView display];
topLeftPoint = [[splitView window] cascadeTopLeftFromPoint:topLeftPoint];
}
- (IBAction) cancelArgumentsSheetAction:(id)sender
{
[argumentsWindow orderOut:nil];
[NSApp endSheet:argumentsWindow];
}
- (IBAction) compil:sender
{
edited = YES; // to force compilation even if there is no change (is it useful ?).
FSBlockCompilationResult *compilationResult = [inspectedObject compilation];
switch (compilationResult->type)
{
case FSOKBlockCompilationResultType: [[messageView documentView] setString:@"Syntax ok"]; break;
case FSErrorBlockCompilationResultType:
if (compilationResult->errorLastCharacterIndex == -1)
[self showError:compilationResult->errorMessage];
else
[self showError:[NSString stringWithFormat:@"%@, character %ld", compilationResult->errorMessage, (long)(compilationResult->errorFirstCharacterIndex)] start:compilationResult->errorFirstCharacterIndex end:compilationResult->errorLastCharacterIndex];
break;
}
}
- (BOOL)edited
{ return edited; }
- (IBAction)evaluateBlockAction:(id)sender
{
NSForm *f = [[[[sender window] contentView] subviews] objectAtIndex:0];
NSInteger nbarg = [f numberOfRows];
FSArray *arguments = [FSArray arrayWithCapacity:nbarg]; // FSArray instead of NSMutableArray in order to support nil
NSInteger i;
FSInterpreter *interpreter;
BOOL found;
FSSystem *sys;
FSSymbolTable *symbolTable = [inspectedObject symbolTable];
while ([symbolTable parent]) symbolTable = [symbolTable parent];
sys = [symbolTable objectForSymbol:@"sys" found:&found];
NSAssert(found, @"\"sys\" object not found !");
NSAssert([sys isKindOfClass:[FSSystem class]], @"\"sys\" object is not a FSSystem instance !");
interpreter = [[[sys interpreter] retain] autorelease]; // Retain the interpreter to ensure it'll stay with us during the execution of the current method
if (!interpreter)
{
NSRunAlertPanel(@"Error", @"Sorry, can't evaluate the arguments because there is no FSInterpreter associated with the block", @"OK", nil, nil,nil);
[NSApp endSheet:[sender window]];
[[sender window] orderOut:nil];
}
else
{
for (i = 0; i < nbarg; i++)
{
NSFormCell *cell = [f cellAtIndex:i];
NSString *argumentString = [cell stringValue];
FSInterpreterResult *result = [interpreter execute:argumentString];
[[messageView documentView] setString:[NSString stringWithFormat:@"Evaluating argument %ld",(long)(i+1)]];
[[messageView documentView] display];
if ([result isOK])
[arguments addObject:[result result]];
else
{
NSMutableString *errorArgumentString = [NSString stringWithFormat:@"Argument %ld %@", (long)(i+1), [result errorMessage]];
[[messageView documentView] setString:@""];
[result inspectBlocksInCallStack];
[f selectTextAtIndex:i];
NSRunAlertPanel(@"Error", errorArgumentString, @"OK", nil, nil,nil);
break;
}
}
if (i == nbarg) // There were no error evaluating the arguments
{
FSInterpreterResult *interpreterResult;
[[messageView documentView] setString:@"Evaluating block..."];
[[messageView documentView] display];
interpreterResult = [inspectedObject executeWithArguments:arguments];
if ([interpreterResult isOK])
{
[argumentsWindow orderOut:nil];
[NSApp endSheet:[sender window]];
if ([[interpreterResult result] isKindOfClass:[FSVoid class]])
[[messageView documentView] setString:@""];
else if (interpreter)
{
[[messageView documentView] setString:@""];
[[FSObjectBrowser objectBrowserWithRootObject:[interpreterResult result] interpreter:interpreter] makeKeyAndOrderFront:nil];
}
}
else
{
[NSApp endSheet:[sender window]];
[[sender window] orderOut:nil];
[self showError:[interpreterResult errorMessage]]; // usefull if the call stack is empty
[interpreterResult inspectBlocksInCallStack];
}
}
}
}
- (void)dealloc
{
//NSLog(@"BlockInspector dealloc");
assert(!splitView);
[argumentsWindow close];
[super dealloc];
}
- (id)initWithBlock:(FSBlock*)bl
{
if ([super init])
{
inspectedObject = bl;
return self;
}
return nil;
}
- (IBAction) run:sender
{
NSInteger argumentCount;
FSBlockCompilationResult *compilationResult;
[[messageView documentView] setString:@""];
compilationResult = [inspectedObject compilation];
switch (compilationResult->type)
{
case FSOKBlockCompilationResultType: break;
case FSErrorBlockCompilationResultType:
if (compilationResult->errorLastCharacterIndex == -1)
[self showError:compilationResult->errorMessage];
else
[self showError:[NSString stringWithFormat:@"%@, character %ld", compilationResult->errorMessage, (long)(compilationResult->errorFirstCharacterIndex)] start:compilationResult->errorFirstCharacterIndex end:compilationResult->errorLastCharacterIndex];
return;
break;
}
argumentCount = [inspectedObject argumentCount];
if (argumentCount == 0)
{
id result = [inspectedObject guardedValue:nil];
if (result && ![result isKindOfClass:[FSVoid class]])
{
BOOL found;
FSSystem *sys;
FSSymbolTable *symbolTable = [inspectedObject symbolTable];
while ([symbolTable parent]) symbolTable = [symbolTable parent];
sys = [symbolTable objectForSymbol:@"sys" found:&found];
NSAssert(found, @"\"sys\" object not found !");
NSAssert([sys isKindOfClass:[FSSystem class]], @"\"sys\" object is not a FSSystem instance !");
if ([sys interpreter]) [[FSObjectBrowser objectBrowserWithRootObject:result interpreter:[sys interpreter]] makeKeyAndOrderFront:nil];
}
}
else
{
if (!argumentsWindow)
{
NSInteger i;
NSInteger baseWidth = 380;
NSInteger baseHeight = argumentCount*(userFixedPitchFontSize()+17)+75;
NSButton *sendButton;
NSButton *cancelButton;
NSForm *f;
NSArray *argumentsNames;
argumentsWindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(100,100,baseWidth,baseHeight) styleMask:NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask|NSResizableWindowMask backing:NSBackingStoreBuffered defer:NO];
[argumentsWindow setTitle:@"Arguments"];
[argumentsWindow setMinSize:NSMakeSize(230,baseHeight)];
[argumentsWindow setMaxSize:NSMakeSize(1400,baseHeight)];
f = [[[NSForm alloc] initWithFrame:NSMakeRect(20,60,baseWidth-40,baseHeight-80)] autorelease];
[f setAutoresizingMask:NSViewWidthSizable];
[f setInterlineSpacing:8];
[[argumentsWindow contentView] addSubview:f]; // The form must be the first subview
// (this is used by method evaluateBlockAction:)
[argumentsWindow setInitialFirstResponder:f];
sendButton = [[[NSButton alloc] initWithFrame:NSMakeRect(baseWidth/2,13,95,30)] autorelease];
[sendButton setBezelStyle:1];
[sendButton setTitle:@"Run"];
[sendButton setAction:@selector(evaluateBlockAction:)];
[sendButton setTarget:self];
[sendButton setKeyEquivalent:@"\r"];
[[argumentsWindow contentView] addSubview:sendButton];
cancelButton = [[[NSButton alloc] initWithFrame:NSMakeRect(baseWidth/2-95,13,95,30)] autorelease];
[cancelButton setBezelStyle:1];
[cancelButton setTitle:@"Cancel"];
[cancelButton setAction:@selector(cancelArgumentsSheetAction:)];
[cancelButton setTarget:self];
[cancelButton setKeyEquivalent:@"\e"];
[[argumentsWindow contentView] addSubview:cancelButton];
argumentsNames = [inspectedObject argumentsNames];
for (i = 0; i < argumentCount; i++)
{
[f addEntry:[argumentsNames objectAtIndex:i]];
}
[f setTextFont:[NSFont userFixedPitchFontOfSize:userFixedPitchFontSize()]];
[f setTitleFont:[NSFont systemFontOfSize:systemFontSize()]];
[f setAutosizesCells:YES];
[f setTarget:sendButton];
[f setAction:@selector(performClick:)];
[f selectTextAtIndex:0];
}
[NSApp beginSheet:argumentsWindow modalForWindow:[messageView window] modalDelegate:self didEndSelector:NULL contextInfo:NULL];
}
}
- (void)setEdited:(BOOL)newVal
{
edited = newVal;
}
- (void)showError:(NSString*)errorMessage
{
[self activate];
if (argumentsWindow && [[sourceView window] attachedSheet] == argumentsWindow)
{
[argumentsWindow orderOut:nil];
[NSApp endSheet:argumentsWindow];
}
[[messageView documentView] setString:errorMessage];
[[sourceView window] makeFirstResponder:[sourceView documentView]];
}
- (void)showError:(NSString*)errorMessage start:(NSInteger)firstCharacterIndex end:(NSInteger)lastCharacterIndex
{
[self activate];
if (argumentsWindow && [[sourceView window] attachedSheet] == argumentsWindow)
{
[argumentsWindow orderOut:nil];
[NSApp endSheet:argumentsWindow];
}
[[messageView documentView] setString:errorMessage];
[[sourceView documentView] setSelectedRange:NSMakeRange(firstCharacterIndex,lastCharacterIndex+1-firstCharacterIndex)];
[[sourceView documentView] scrollRangeToVisible:[[sourceView documentView] selectedRange]];
[[sourceView window] makeFirstResponder:[sourceView documentView]];
}
- (NSString*)source
{
return [[[sourceView documentView] textStorage] string];
}
- (id)update
{
NSTextView *documentView = [sourceView documentView];
NSRange selectedRange = [documentView selectedRange];
NSString *newString;
edited = NO; // (1)
newString = [inspectedObject printString]; // The instruction (1) must be done before this one to avoid infinite recursion
[documentView setString:newString];
if (selectedRange.location + selectedRange.length <= [newString length])
[documentView setSelectedRange:selectedRange];
if (argumentsWindow)
{
if ([[sourceView window] attachedSheet] == argumentsWindow)
{
[NSApp endSheet:argumentsWindow];
}
[argumentsWindow close];
argumentsWindow = nil;
}
return self;
}
- (void)textDidChange:(NSNotification *)aNotification
{
//NSLog(@"textDidChange:");
edited = YES;
if (argumentsWindow)
{
if ([[sourceView window] attachedSheet] == argumentsWindow)
{
[NSApp endSheet:argumentsWindow];
}
[argumentsWindow close];
argumentsWindow = nil;
}
[[NSNotificationQueue defaultQueue] enqueueNotification:[NSNotification notificationWithName:@"BlockDidChangeNotification" object:inspectedObject] postingStyle:NSPostWhenIdle];
}
- (void)windowWillClose:(NSNotification *)aNotification
{
if (edited)
[inspectedObject sync];
[[sourceView window] setDelegate:nil];
edited = NO;
splitView = nil;
sourceView = nil;
messageView = nil;
[inspectedObject release];
}
@end