Files
markdownlive/RKSyntaxView.m

170 lines
5.7 KiB
Objective-C

//
// RKSyntaxView.m
//
//
// Created by Vojto Rinik on 8/24/11.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import "RKSyntaxView.h"
#import "NSColor+HexRGB.h"
@implementation RKSyntaxView
@synthesize scheme=_scheme;
@synthesize syntax=_syntax;
@synthesize content=_content;
#pragma mark - Lifecycle
- (id)init {
if ((self = [super init])) {
[self _setup];
}
return self;
}
- (void)awakeFromNib {
[self _setup];
}
- (void)_setup {
self.content = [[[NSMutableAttributedString alloc] init] autorelease];
[self setTextContainerInset:NSMakeSize(10.0, 10.0)];
[self highlight];
[self addObserver:self forKeyPath:@"string" options:0 context:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_textDidChange:) name:NSTextDidChangeNotification object:self];
}
- (void) dealloc {
self.content = nil;
[super dealloc];
}
#pragma mark - Handling text changes
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
[self highlight];
}
- (void)_textDidChange:(NSNotification *)notif {
[self highlight];
}
#pragma mark - Scheme
- (void)loadScheme:(NSString *)schemeFilename {
NSString *schemePath = [[NSBundle mainBundle] pathForResource:schemeFilename ofType:@"plist" inDirectory:nil];
self.scheme = [NSDictionary dictionaryWithContentsOfFile:schemePath];
}
- (NSColor *) _colorFor:(NSString *)key {
NSString *colorCode = [[self.scheme objectForKey:@"colors"] objectForKey:key];
if (!colorCode) return nil;
NSColor *color = [NSColor colorFromHexRGB:colorCode];
return color;
}
- (NSFont *) _font {
return [self _fontOfSize:12 bold:NO];
}
- (NSFont *) _fontOfSize:(NSInteger)size bold:(BOOL)wantsBold {
NSString *fontName = [self.scheme objectForKey:@"font"];
NSFont *font = [NSFont fontWithName:fontName size:size];
if (!font) font = [NSFont systemFontOfSize:size];
if (wantsBold) {
NSFontTraitMask traits = NSBoldFontMask;
NSFontManager *manager = [NSFontManager sharedFontManager];
font = [manager fontWithFamily:fontName traits:traits weight:5.0 size:size];
}
return font;
}
#pragma mark - Syntax
- (void)loadSyntax:(NSString *)syntaxFilename {
NSString *schemePath = [[NSBundle mainBundle] pathForResource:syntaxFilename ofType:@"plist" inDirectory:nil];
self.syntax = [NSDictionary dictionaryWithContentsOfFile:schemePath];
}
#pragma mark - Highlighting
- (void) highlight {
self.content = [[NSMutableAttributedString alloc] initWithString:[self string]];
[self.content release];
NSColor *background = [self _colorFor:@"background"];
[self setBackgroundColor:background];
[(NSScrollView *)self.superview setBackgroundColor:background];
[self setTextColor:[self _colorFor:@"default"]];
return [self highlightRange:NSMakeRange(0, [self.content length])];
}
- (void) highlightRange:(NSRange)range {
NSColor *defaultColor = [self _colorFor:@"default"];
NSInteger defaultSize = [(NSNumber *)[self.scheme objectForKey:@"size"] integerValue];
if (!defaultSize) defaultSize = 12;
NSFont *defaultFont = [self _fontOfSize:defaultSize bold:NO];
[self _setFont:defaultFont range:range];
[self _setTextColor:defaultColor range:range];
[self _setBackgroundColor:[NSColor clearColor] range:range];
NSString *string = [self.content string];
for (NSString *type in [self.syntax allKeys]) {
NSDictionary *params = [self.syntax objectForKey:type];
NSString *pattern = [params objectForKey:@"pattern"];
NSString *colorName = [params objectForKey:@"color"];
NSColor *color = [self _colorFor:colorName];
NSString *backgroundColorName = [params objectForKey:@"backgroundColor"];
NSColor *backgroundColor = [self _colorFor:backgroundColorName];
NSInteger size = [(NSNumber *)[params objectForKey:@"size"] integerValue];
BOOL isBold = [(NSNumber *)[params objectForKey:@"isBold"] boolValue];
NSFont *font = [self _fontOfSize:(size?size:defaultSize) bold:isBold];
NSInteger patternGroup = [(NSNumber *)[params objectForKey:@"patternGroup"] integerValue];
NSError *error = nil;
NSRegularExpression *expr = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive|NSRegularExpressionAnchorsMatchLines error:&error];
NSArray *matches = [expr matchesInString:string options:0 range:range];
for (NSTextCheckingResult *match in matches) {
NSRange range = patternGroup ? [match rangeAtIndex:patternGroup] : [match range];
[self _setTextColor:color range:range];
if (backgroundColor) [self _setBackgroundColor:backgroundColor range:range];
[self _setFont:font range:range];
}
}
[self _reflect];
}
#pragma mark - Changing text attributes
- (void) _setTextColor:(NSColor *)color range:(NSRange)range {
if (!color) return;
[self.content addAttribute:NSForegroundColorAttributeName value:color range:range];
}
- (void) _setBackgroundColor:(NSColor *)color range:(NSRange)range {
[self.content addAttribute:NSBackgroundColorAttributeName value:color range:range];
}
- (void) _setFont:(NSFont *)font range:(NSRange)range {
[self.content addAttribute:NSFontAttributeName value:font range:range];
}
- (void) _reflect {
NSTextStorage *storage = [self textStorage];
NSAttributedString *content = self.content;
NSRange range = NSMakeRange(0, [content length]);
[content enumerateAttributesInRange:range options:0 usingBlock:^(NSDictionary *attributes, NSRange range, BOOL *stop){
[storage setAttributes:attributes range:range];
}];
}
@end