forked from parmanoir/jscocoa
-
Notifications
You must be signed in to change notification settings - Fork 0
/
NSLogConsole.m
385 lines (294 loc) · 8.67 KB
/
NSLogConsole.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
//
// NSLogConsole.m
// NSLogConsole
//
// Created by Patrick Geiller on 16/08/08.
// Copyright 2008 __MyCompanyName__. All rights reserved.
//
#import "NSLogConsole.h"
#import "JSCocoaController.h"
BOOL inited = NO;
void NSLogPostLog(char* file, int line)
{
if (!inited) return;
[[NSLogConsole sharedConsole] updateLogWithFile:file lineNumber:line];
}
@implementation NSLogConsole
@synthesize autoOpens;
+ (id)sharedConsole
{
static id singleton = NULL;
@synchronized(self)
{
if (!singleton)
{
singleton = [self alloc];
[singleton init];
}
}
return singleton;
}
- (id)init
{
id o = [super init];
autoOpens = YES;
logPath = NULL;
inited = YES;
// Save stderr
original_stderr = dup(STDERR_FILENO);
logPath = [NSString stringWithFormat:@"%@%@.log.txt", NSTemporaryDirectory(), [[NSBundle mainBundle] bundleIdentifier]];
[logPath retain];
// Create the file — NSFileHandle doesn't do it !
[@"" writeToFile:logPath atomically:YES encoding:NSUTF8StringEncoding error:nil];
fileHandle = [NSFileHandle fileHandleForWritingAtPath:logPath];
if (!fileHandle) NSLog(@"Opening log at %@ failed", logPath);
[fileHandle retain];
int fd = [fileHandle fileDescriptor];
// Redirect stderr
int err = dup2(fd, STDERR_FILENO);
if (!err) NSLog(@"Couldn't redirect stderr");
fileOffset = 0;
[fileHandle readInBackgroundAndNotify];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dataAvailable:) name:NSFileHandleReadCompletionNotification object: fileHandle];
return o;
}
// NSLog will generate an event posted in the next run loop run
- (void)dataAvailable:(NSNotification *)aNotification
{
[[NSLogConsole sharedConsole] updateLogWithFile:"" lineNumber:0];
}
- (void)dealloc
{
[logPath release];
[fileHandle release];
[super dealloc];
}
- (void)open
{
/*
if (!window)
{
if (![NSBundle loadNibNamed:@"NSLogConsole" owner:self])
{
NSLog(@"NSLogConsole.nib not loaded");
return;
}
if ([window respondsToSelector:@selector(setBottomCornerRounded:)])
[window setBottomCornerRounded:NO];
}
[window orderFront:self];
*/
}
- (void)close
{
[window orderOut:self];
}
- (BOOL)isOpen
{
return [window isVisible];
}
- (IBAction)clear:(id)sender
{
[webView clear];
}
- (IBAction)searchChanged:(id)sender
{
[webView search:[sender stringValue]];
}
- (void)setWebView:(id)view
{
webView = view;
[webView retain];
}
- (id)webView
{
return webView;
}
- (void)logData:(NSData*)data file:(char*)file lineNumber:(int)line
{
id str = [[[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:NSUTF8StringEncoding] autorelease];
// Get out now if we have no data to display
if ([data length] == 0) return;
// Write back to original stderr
write(original_stderr, [data bytes], [data length]);
// Clear search
[searchField setStringValue:@""];
[webView search:@""];
// Log string
[webView logString:str file:file lineNumber:line];
}
- (void)updateLogWithFile:(char*)file lineNumber:(int)line
{
/*
This method gives this warning
2009-11-14 18:49:23.793 JSCocoa[6162:a0f] *** -[NSConcreteData initWithBytes:length:copy:freeWhenDone:bytesAreVM:]: absurd length: 18446744073709551615, maximum size: 9223372036854775808 bytes
*/
// if (![window isVisible] && autoOpens) [self open];
// Open a new handle to read new data
id f = [NSFileHandle fileHandleForReadingAtPath:logPath];
if (!f) NSLog(@"Opening log at %@ failed", logPath);
//printf("=====\n");
// Get file length
[f seekToEndOfFile];
unsigned long long length = [f offsetInFile];
// printf("length= %llu\n", length);
// Read data
[f seekToFileOffset:fileOffset];
unsigned long long length2 = [f offsetInFile];
// printf("length2= %llu\n", length2);
NSData* data = [f readDataToEndOfFile];
[self logData:data file:file lineNumber:line];
// We'll read from that offset next time
fileOffset = length;
//printf("+++++\n");
}
@end
@implementation NSLogConsoleView
- (void)dealloc
{
[messageQueue release];
[super dealloc];
}
- (BOOL)drawsBackground
{
return NO;
}
- (void)awakeFromNib
{
messageQueue = [[NSMutableArray alloc] init];
webViewLoaded = NO;
// Frame load
[self setFrameLoadDelegate:self];
// Load html page
id path = [[NSBundle mainBundle] pathForResource:@"NSLogConsole" ofType:@"html"];
[[self mainFrame] loadRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:path]]];
// Navigation notification
[self setPolicyDelegate:self];
}
//
// Javascript is available
// Register our custom javascript object in the hosted page
//
- (void)webView:(WebView *)view windowScriptObjectAvailable:(WebScriptObject *)windowScriptObject
{
[windowScriptObject setValue:self forKey:@"NSLogConsoleView"];
}
//
// WebView has finished loading
//
- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame
{
webViewLoaded = YES;
// Flush message queue
for (id o in messageQueue)
[self logString:[o valueForKey:@"string"] file:(char*)[[o valueForKey:@"file"] UTF8String] lineNumber:[[o valueForKey:@"line"] intValue]];
[messageQueue release];
[[self windowScriptObject] setValue:self forKey:@"myVar"];
}
//
// Notify WebView of new message
//
- (void)logString:(NSString*)string file:(char*)file lineNumber:(int)line
{
// Queue message if WebView has not finished loading
if (!webViewLoaded)
{
id o = [NSDictionary dictionaryWithObjectsAndKeys: [NSString stringWithString:string], @"string",
[NSString stringWithUTF8String:file], @"file",
[NSNumber numberWithInt:line], @"line",
nil];
[messageQueue addObject:o];
return;
}
[[self windowScriptObject] callWebScriptMethod:@"log" withArguments:[NSArray arrayWithObjects:string,
[NSString stringWithUTF8String:file],
[NSNumber numberWithInt:line],
nil]];
}
//
// Open source file in XCode at correct line number
//
- (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation
request:(NSURLRequest *)request
frame:(WebFrame *)frame
decisionListener:(id<WebPolicyDecisionListener>)listener
{
// Get path, formed by AbsolutePathOnDisk(space)LineNumber
NSString* pathAndLineNumber = [[request URL] path];
// From end of string, skip to space before number
char* s = (char*)[pathAndLineNumber UTF8String];
char* s2 = s+strlen(s)-1;
while (*s2 && *s2 != ' ' && s2 > s) s2--;
if (*s2 != ' ') return NSLog(@"Did not find line number in %@", pathAndLineNumber);
// Patch a zero to recover path
*s2 = 0;
// Get line number
int line;
BOOL foundLine = [[NSScanner scannerWithString:[NSString stringWithUTF8String:s2+1]] scanInt:&line];
if (!foundLine) return NSLog(@"Did not parse line number in %@", pathAndLineNumber);
// Get path
NSString* path = [NSString stringWithUTF8String:s];
// NSLog(@"opening line %d of _%@_", line, path);
// Open in XCode
id source = [NSString stringWithFormat:@"tell application \"Xcode\" \n\
set doc to open \"%@\" \n\
set selection to paragraph (%d) of contents of doc \n\
end tell", path, line];
id script = [[NSAppleScript alloc] initWithSource:source];
[script executeAndReturnError:nil];
[script release];
}
- (void)clear
{
[[self windowScriptObject] callWebScriptMethod:@"clear" withArguments:nil];
}
- (void)search:(NSString*)string
{
[[self windowScriptObject] callWebScriptMethod:@"search" withArguments:[NSArray arrayWithObjects:string, nil]];
}
+ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector
{
return NO;
}
//
// Hairy function below. WebView calls ApplicationController (written in JS)
//
- (void)evalJSCocoa:(NSString*)string
{
id delegate = [[NSApplication sharedApplication] delegate];
[[delegate inputScript] setStringValue:string];
[delegate runScript:string];
}
//
// Overlay Help
//
- (void)openHelp
{
[[self windowScriptObject] callWebScriptMethod:@"openHelp" withArguments:nil];
}
- (void)closeHelp
{
[[self windowScriptObject] callWebScriptMethod:@"closeHelp" withArguments:nil];
}
- (BOOL)isHelpOpen
{
id result = [[self windowScriptObject] callWebScriptMethod:@"isHelpOpen" withArguments:nil];
if (![result respondsToSelector:@selector(boolValue)]) return NO;
return [result boolValue];
}
//
// Command display
//
- (void)startCommand:(id)command
{
[[self windowScriptObject] callWebScriptMethod:@"startCommand" withArguments:[NSArray arrayWithObjects:command, nil]];
}
- (void)endCommand
{
/*id result = */[[self windowScriptObject] callWebScriptMethod:@"endCommand" withArguments:nil];
}
- (void)performFindPanelAction:(id)sender
{
NSLog(@"performFindPanelAction");
}
@end