3 * Copyright (c) 2007 - 2011, Blue Static <http://www.bluestatic.org>
5 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6 * General Public License as published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
10 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public License along with this program; if not,
14 * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
17 #import "BSSourceView.h"
19 #import "PreferenceNames.h"
20 #import "BSLineNumberRulerView.h"
21 #import "BSSourceViewTextView.h"
23 @interface BSSourceView (Private
)
25 - (void)errorHighlightingFile
:(NSNotification
*)notif
;
26 - (void)setPlainTextStringFromFile
:(NSString
*)filePath
;
29 @implementation BSSourceView
{
30 BSSourceViewTextView
* _textView
;
31 BSLineNumberRulerView
* _ruler
;
32 NSScrollView
* _scrollView
;
34 // Line numbers to mark.
35 NSSet
<NSNumber
*>* _markers
;
40 - (id)initWithFrame
:(NSRect
)frame
42 if (self = [super initWithFrame
:frame
]) {
48 - (void)setMarkers
:(NSSet
*)markers
{
49 _markers
= [markers copy
];
50 [_ruler setNeedsDisplay
:YES
];
53 - (void)setMarkedLine
:(NSUInteger
)markedLine
{
54 _markedLine
= markedLine
;
55 [_ruler setNeedsDisplay
:YES
];
56 [_textView setNeedsDisplay
:YES
];
60 * Reads the contents of file at |f| and sets the source viewer and filename
63 - (void)setFile
:(NSString
*)f
67 if (![[NSFileManager defaultManager
] fileExistsAtPath
:f
]) {
68 [_textView setString
:@
""];
72 [self setSource
:f completionHandler
:nil];
76 * Sets the contents of the SourceView to |source| representing the file at |path|.
78 - (void)setString
:(NSString
*)source asFile
:(NSString
*)path
82 // Write the source out as a temporary file so it can be highlighted.
84 NSString
* tmpPath
= [NSTemporaryDirectory() stringByAppendingPathComponent
:@
"MacGDBpHighlighter"];
85 [source writeToFile
:tmpPath atomically
:NO encoding
:NSUTF8StringEncoding error
:&error
];
87 [_textView setString
:source
];
91 [self setSource
:tmpPath completionHandler
:^
{
92 [[NSFileManager defaultManager
] removeItemAtPath
:tmpPath error
:NULL
];
97 * Flip the coordinates
105 * Tells the text view to scroll to a certain line
107 - (void)scrollToLine
:(NSUInteger
)line
109 if ([[_textView textStorage
] length
] == 0)
112 // go through the document until we find the NSRange for the line we want
113 NSUInteger rangeIndex
= 0;
114 for (NSUInteger i
= 0; i
< line
; i
++) {
115 rangeIndex
= NSMaxRange([[_textView string
] lineRangeForRange
:NSMakeRange(rangeIndex
, 0)]);
118 // now get the true start/end markers for it
119 NSUInteger lineStart
, lineEnd
;
120 [[_textView string
] getLineStart
:&lineStart
123 forRange
:NSMakeRange(rangeIndex
- 1, 0)];
124 [_textView scrollRangeToVisible
:[[_textView string
]
125 lineRangeForRange
:NSMakeRange(lineStart
, lineEnd
- lineStart
)]];
129 * Returns the preferred font for source views.
131 + (NSFont
*)sourceFont
133 static NSFont
* font
= nil;
135 font
= [NSFont fontWithName
:@
"Menlo" size
:12];
137 font
= [NSFont fontWithName
:@
"Monaco" size
:12.0];
143 * Setup all the subviews for the source metaview
147 // Create the scroll view.
148 _scrollView
= [[NSScrollView alloc
] initWithFrame
:[self bounds
]];
149 [_scrollView setHasHorizontalScroller
:YES
];
150 [_scrollView setHasVerticalScroller
:YES
];
151 [_scrollView setAutohidesScrollers
:YES
];
152 [_scrollView setBorderType
:NSBezelBorder
];
153 [_scrollView setAutoresizingMask
:(NSViewWidthSizable | NSViewHeightSizable
)];
154 [[_scrollView contentView
] setAutoresizesSubviews
:YES
];
155 [self addSubview
:_scrollView
];
157 // add the text view to the scroll view
159 textFrame.origin
= NSMakePoint(0.0, 0.0);
160 textFrame.size
= [_scrollView contentSize
];
161 _textView
= [[BSSourceViewTextView alloc
] initWithFrame
:textFrame
];
162 [_textView setSourceView
:self];
163 [_textView setEditable
:NO
];
164 [_textView setFont
:[[self class] sourceFont
]];
165 [_textView setHorizontallyResizable
:YES
];
166 [_textView setVerticallyResizable
:YES
];
167 [_textView setMinSize
:textFrame.size
];
168 [_textView setMaxSize
:NSMakeSize(FLT_MAX
, FLT_MAX
)];
169 [[_textView textContainer
] setContainerSize
:NSMakeSize(FLT_MAX
, FLT_MAX
)];
170 [[_textView textContainer
] setWidthTracksTextView
:NO
];
171 [[_textView textContainer
] setHeightTracksTextView
:NO
];
172 [_textView setAutoresizingMask
:NSViewNotSizable
];
173 [_scrollView setDocumentView
:_textView
];
176 _ruler
= [[BSLineNumberRulerView alloc
] initWithSourceView
:self];
177 [_scrollView setVerticalRulerView
:_ruler
];
178 [_scrollView setHasHorizontalRuler
:NO
];
179 [_scrollView setHasVerticalRuler
:YES
];
180 [_scrollView setRulersVisible
:YES
];
182 [self registerForDraggedTypes
:@
[ NSFilenamesPboardType
]];
185 NSString
* ColorHEXStringINIDirective(NSString
* directive
, NSColor
* color
) {
186 CGFloat red
, green
, blue
, alpha
;
187 color
= [color colorUsingColorSpaceName
:NSCalibratedRGBColorSpace
];
188 [color getRed
:&red green
:&green blue
:&blue alpha
:&alpha
];
189 return [NSString stringWithFormat
:@
"%@=\"#%02x%02x%02x\"", directive
, (UInt8
)(red
* 255), (UInt8
)(green
* 255), (UInt8
)(blue
* 255)];
193 * Reads the contents of |filePath| and sets it as the displayed text, after
194 * attempting to highlight it using the PHP binary.
196 - (void)setSource
:(NSString
*)filePath completionHandler
:(void(^
)(void))handler
199 // Attempt to use the PHP CLI to highlight the source file as HTML
200 NSPipe
* outPipe
= [NSPipe pipe
];
201 NSPipe
* errPipe
= [NSPipe pipe
];
202 NSTask
* task
= [[NSTask alloc
] init
];
204 NSString
* phpPath
= [[NSUserDefaults standardUserDefaults
] stringForKey
:kPrefPhpPath
];
206 // This is the path to the default Leopard PHP executable, but it will not
207 // exist on future macOS versions.
208 phpPath
= @
"/usr/bin/php";
211 [task setLaunchPath
:phpPath
];
212 [task setArguments
:@
[
213 @
"--syntax-highlight",
214 @
"--define", ColorHEXStringINIDirective(@
"highlight.string", [NSColor systemRedColor
]),
215 @
"--define", ColorHEXStringINIDirective(@
"highlight.comment", [NSColor systemOrangeColor
]),
216 @
"--define", ColorHEXStringINIDirective(@
"highlight.default", [NSColor systemBlueColor
]),
217 @
"--define", ColorHEXStringINIDirective(@
"highlight.html", [NSColor systemGrayColor
]),
220 [task setStandardOutput
:outPipe
];
221 [task setStandardError
:errPipe
];
222 [task setTerminationHandler
:^
(NSTask
* taskBlock
) {
223 if (task.terminationStatus
!= 0) {
224 NSLog(@
"Failed to highlight PHP file %@. Termination status=%d. stderr: %@",
225 filePath
, taskBlock.terminationStatus
,
226 [[NSString alloc
] initWithData
:[[errPipe fileHandleForReading
] readDataToEndOfFile
] encoding
:NSUTF8StringEncoding
]);
231 // Start reading the stdout pipe immediately. This is separate from the
232 // terminiationHandler, since a large file could be greater than
234 NSMutableAttributedString
* source
;
235 NSData
* data
= [[outPipe fileHandleForReading
] readDataToEndOfFile
];
237 source
= [[NSMutableAttributedString alloc
] initWithHTML
:data
238 options
:@
{ NSCharacterEncodingDocumentAttribute
: @
(NSUTF8StringEncoding
) }
239 documentAttributes
:nil];
241 // PHP uses in the highlighted output, which should be converted
242 // back to normal spaces.
243 NSMutableString
* stringData
= [source mutableString
];
244 [stringData replaceOccurrencesOfString
:@
"\u00A0" withString
:@
" " options
:0 range
:NSMakeRange(0, stringData.length
)];
246 // Override the default font from Courier.
247 [source addAttributes
:@
{ NSFontAttributeName
: [[self class] sourceFont
] }
248 range
:NSMakeRange(0, source.length
)];
252 [[_textView textStorage
] setAttributedString
:source
];
254 [self setPlainTextStringFromFile
:filePath
];
257 [_ruler performLayout
];
262 } @catch (NSException
* exception
) {
263 // If the PHP executable is not available then the NSTask will throw an exception
264 NSLog(@
"Failed to highlight file: %@", exception
);
265 [self setPlainTextStringFromFile
:filePath
];
270 * Gets the plain-text representation of the file at |filePath| and sets the
271 * contents in the source view.
273 - (void)setPlainTextStringFromFile
:(NSString
*)filePath
275 NSError
* error
= nil;
276 NSString
* contents
= [NSString stringWithContentsOfFile
:filePath
277 encoding
:NSUTF8StringEncoding
280 NSLog(@
"Error reading file at %@: %@", filePath
, error
);
281 if ([_delegate respondsToSelector
:@selector(error
:whileHighlightingFile
:)]) {
282 [_delegate error
:error whileHighlightingFile
:filePath
];
286 [_textView setString
:contents
];
287 [_ruler performLayout
];
290 // Drag Handlers ///////////////////////////////////////////////////////////////
293 * Validates an initiated drag operation.
295 - (NSDragOperation
)draggingEntered
:(id<NSDraggingInfo
>)sender
297 return NSDragOperationCopy
;
301 * Performs a dragging operation of files to set the contents of the file.
303 - (BOOL)performDragOperation
:(id<NSDraggingInfo
>)sender
305 NSPasteboard
* pboard
= [sender draggingPasteboard
];
306 if ([[pboard types
] containsObject
:NSFilenamesPboardType
]) {
307 NSArray
* files
= [pboard propertyListForType
:NSFilenamesPboardType
];
309 NSString
* filename
= [files objectAtIndex
:0];
310 [self setFile
:filename
];