From 5b44cfe2cd631aed622b82028676c9b2b6e1232b Mon Sep 17 00:00:00 2001 From: Michael Chen Date: Tue, 17 Nov 2015 14:39:45 +0800 Subject: [PATCH] Fix original attribute string display. Fix issue #37, #38 and #35. --- MCLog.xcodeproj/project.pbxproj | 8 + .../xcschemes/MCLog.xcscheme | 14 +- MCLog/MCDVTTextStorage.h | 21 ++ MCLog/MCDVTTextStorage.m | 187 ++++++++++++++ MCLog/MCLog-Prefix.pch | 16 ++ MCLog/MCLog.h | 13 +- MCLog/MCLog.m | 230 ++---------------- MCLog/MCXcodeHeaders.h | 13 + 8 files changed, 273 insertions(+), 229 deletions(-) create mode 100644 MCLog/MCDVTTextStorage.h create mode 100644 MCLog/MCDVTTextStorage.m create mode 100644 MCLog/MCXcodeHeaders.h diff --git a/MCLog.xcodeproj/project.pbxproj b/MCLog.xcodeproj/project.pbxproj index 36ea33d..29c8aea 100644 --- a/MCLog.xcodeproj/project.pbxproj +++ b/MCLog.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 8CBAF63C198B88C70067171A /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8CBAF63B198B88C70067171A /* CoreFoundation.framework */; }; 8CBAF642198B88C70067171A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8CBAF640198B88C70067171A /* InfoPlist.strings */; }; 8CBAF64B198B88DD0067171A /* MCLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CBAF64A198B88DD0067171A /* MCLog.m */; }; + 8CEB067E1BFADD0F00ED4438 /* MCDVTTextStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CEB067D1BFADD0F00ED4438 /* MCDVTTextStorage.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -20,6 +21,9 @@ 8CBAF643198B88C70067171A /* MCLog-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MCLog-Prefix.pch"; sourceTree = ""; }; 8CBAF649198B88DD0067171A /* MCLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCLog.h; sourceTree = ""; }; 8CBAF64A198B88DD0067171A /* MCLog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MCLog.m; sourceTree = ""; }; + 8CEB067B1BFADA1700ED4438 /* MCXcodeHeaders.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MCXcodeHeaders.h; sourceTree = ""; }; + 8CEB067C1BFADD0F00ED4438 /* MCDVTTextStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCDVTTextStorage.h; sourceTree = ""; }; + 8CEB067D1BFADD0F00ED4438 /* MCDVTTextStorage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MCDVTTextStorage.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -65,6 +69,9 @@ 8CBAF63E198B88C70067171A /* Supporting Files */, 8CBAF649198B88DD0067171A /* MCLog.h */, 8CBAF64A198B88DD0067171A /* MCLog.m */, + 8CEB067C1BFADD0F00ED4438 /* MCDVTTextStorage.h */, + 8CEB067D1BFADD0F00ED4438 /* MCDVTTextStorage.m */, + 8CEB067B1BFADA1700ED4438 /* MCXcodeHeaders.h */, ); path = MCLog; sourceTree = ""; @@ -142,6 +149,7 @@ buildActionMask = 2147483647; files = ( 8CBAF64B198B88DD0067171A /* MCLog.m in Sources */, + 8CEB067E1BFADD0F00ED4438 /* MCDVTTextStorage.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/MCLog.xcodeproj/xcuserdata/yuhua.xcuserdatad/xcschemes/MCLog.xcscheme b/MCLog.xcodeproj/xcuserdata/yuhua.xcuserdatad/xcschemes/MCLog.xcscheme index 4cb1135..b8ac60c 100644 --- a/MCLog.xcodeproj/xcuserdata/yuhua.xcuserdatad/xcschemes/MCLog.xcscheme +++ b/MCLog.xcodeproj/xcuserdata/yuhua.xcuserdatad/xcschemes/MCLog.xcscheme @@ -23,23 +23,27 @@ + shouldUseLaunchSchemeArgsEnv = "YES"> + + + debugServiceExtension = "internal" + allowLocationSimulation = "YES" + viewDebuggingEnabled = "No"> + +#define NSColorWithHexRGB(rgb) [NSColor colorWithCalibratedRed:((rgb) >> 16 & 0xFF) / 255.f green:((rgb) >> 8 & 0xFF) / 255.f blue:((rgb) & 0xFF) / 255.f alpha:1.f] + +NSRegularExpression * escCharPattern(); + +@implementation NSTextStorage (MCDVTTextStorage) + +- (void)mc_fixAttributesInRange:(NSRange)range +{ + [self mc_fixAttributesInRange:range]; + + if (!self.consoleStorage) { + return; + } + + __block NSRange lastRange = NSMakeRange(range.location, 0); + NSMutableDictionary *attrs = [NSMutableDictionary dictionary]; + if (self.lastAttribute.count > 0) { + [attrs setValuesForKeysWithDictionary:self.lastAttribute]; + } + + [escCharPattern() enumerateMatchesInString:self.string options:0 range:range usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { + if (attrs.count > 0) { + NSRange attrRange = NSMakeRange(lastRange.location, result.range.location - lastRange.location); + [self addAttributes:attrs range:attrRange]; + //MCLogger(@"apply attributes:%@\nin range:[%zd, %zd], affected string:%@", attrs, attrRange.location, attrRange.length, [self.string substringWithRange:attrRange]); + } + + NSString *attrsDesc = [self.string substringWithRange:[result rangeAtIndex:1]]; + if (attrsDesc.length == 0) { + [self addAttributes:@{ + NSFontAttributeName: [NSFont systemFontOfSize:0.000001f], + NSForegroundColorAttributeName: [NSColor clearColor] + } + range:result.range]; + lastRange = result.range; + return; + } + [self updateAttributes:attrs withANSIESCString:attrsDesc]; + [self addAttributes:@{ + NSFontAttributeName: [NSFont systemFontOfSize:0.000001f], + NSForegroundColorAttributeName: [NSColor clearColor] + } + range:result.range]; + lastRange = result.range; + }]; + self.lastAttribute = attrs; +} + +- (void)updateAttributes:(NSMutableDictionary *)attrs withANSIESCString:(NSString *)ansiEscString +{ + NSArray *attrComponents = [ansiEscString componentsSeparatedByString:@";"]; + for (NSString *attrName in attrComponents) { + NSUInteger attrCode = [attrName integerValue]; + switch (attrCode) { + case 0: + [attrs removeAllObjects]; + break; + + case 1: + [attrs setObject:[NSFont boldSystemFontOfSize:11.f] forKey:NSFontAttributeName]; + break; + + case 4: + [attrs setObject:@( NSUnderlineStyleSingle ) forKey:NSUnderlineStyleAttributeName]; + break; + + case 24: + [attrs setObject:@(NSUnderlineStyleNone ) forKey:NSUnderlineStyleAttributeName]; + break; + //foreground color + case 30: //black + [attrs setObject:[NSColor blackColor] forKey:NSForegroundColorAttributeName]; + break; + + case 31: // Red + [attrs setObject:NSColorWithHexRGB(0xd70000) forKey:NSForegroundColorAttributeName]; + break; + + case 32: // Green + [attrs setObject:NSColorWithHexRGB(0x00ff00) forKey:NSForegroundColorAttributeName]; + break; + + case 33: // Yellow + [attrs setObject:NSColorWithHexRGB(0xffff00) forKey:NSForegroundColorAttributeName]; + break; + + case 34: // Blue + [attrs setObject:NSColorWithHexRGB(0x005fff) forKey:NSForegroundColorAttributeName]; + break; + + case 35: // purple + [attrs setObject:NSColorWithHexRGB(0xff00ff) forKey:NSForegroundColorAttributeName]; + break; + + case 36: // cyan + [attrs setObject:NSColorWithHexRGB(0x00ffff) forKey:NSForegroundColorAttributeName]; + break; + + case 37: // gray + [attrs setObject:NSColorWithHexRGB(0x808080) forKey:NSForegroundColorAttributeName]; + break; + //background color + case 40: //black + [attrs setObject:[NSColor blackColor] forKey:NSBackgroundColorAttributeName]; + break; + + case 41: // Red + [attrs setObject:NSColorWithHexRGB(0xd70000) forKey:NSBackgroundColorAttributeName]; + break; + + case 42: // Green + [attrs setObject:NSColorWithHexRGB(0x00ff00) forKey:NSBackgroundColorAttributeName]; + break; + + case 43: // Yellow + [attrs setObject:NSColorWithHexRGB(0xffff00) forKey:NSBackgroundColorAttributeName]; + break; + + case 44: // Blue + [attrs setObject:NSColorWithHexRGB(0x005fff) forKey:NSBackgroundColorAttributeName]; + break; + + case 45: // purple + [attrs setObject:NSColorWithHexRGB(0xff00ff) forKey:NSBackgroundColorAttributeName]; + break; + + case 46: // cyan + [attrs setObject:NSColorWithHexRGB(0x00ffff) forKey:NSBackgroundColorAttributeName]; + break; + + case 47: // gray + [attrs setObject:NSColorWithHexRGB(0x808080) forKey:NSBackgroundColorAttributeName]; + break; + + default: + break; + } + } +} + +- (void)setLastAttribute:(NSDictionary *)attribute +{ + objc_setAssociatedObject(self, @selector(lastAttribute), attribute, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (NSDictionary *)lastAttribute +{ + return objc_getAssociatedObject(self, @selector(lastAttribute)); +} + +- (void)setConsoleStorage:(BOOL)consoleStorage +{ + objc_setAssociatedObject(self, @selector(consoleStorage), @(consoleStorage), OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (BOOL)consoleStorage +{ + return [objc_getAssociatedObject(self, @selector(consoleStorage)) boolValue]; +} + +@end + +#pragma mark - Utilities + +NSRegularExpression * escCharPattern() +{ + static NSRegularExpression *pattern = nil; + if (pattern == nil) { + NSError *error = nil; + pattern = [NSRegularExpression regularExpressionWithPattern:(LC_ESC @"\\[([\\d;]*\\d+)m") options:0 error:&error]; + if (!pattern) { + MCLogger(@"%@", error); + } + } + return pattern; +} diff --git a/MCLog/MCLog-Prefix.pch b/MCLog/MCLog-Prefix.pch index 926a42b..a33d4a8 100644 --- a/MCLog/MCLog-Prefix.pch +++ b/MCLog/MCLog-Prefix.pch @@ -7,3 +7,19 @@ #ifdef __OBJC__ #import #endif + +#if TARGET_OS_IPHONE +#define LC_ESC @"\xC2\xA0" +#else +#define LC_ESC @"\033" +#endif + +// Reset colors +#define LC_RESET LC_ESC @"[0m" + +// Defaults + +#define MCLOG_FLAG "MCLOG_FLAG" +#define kTagSearchField 99 + +#define MCLogger(fmt, ...) NSLog((@"[MCLog] %s(Line:%d) " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__) \ No newline at end of file diff --git a/MCLog/MCLog.h b/MCLog/MCLog.h index 2344e52..13f94f8 100644 --- a/MCLog/MCLog.h +++ b/MCLog/MCLog.h @@ -6,18 +6,7 @@ // Copyright (c) 2014年 Yuhua Chen. All rights reserved. // -#import - -#if TARGET_OS_IPHONE -#define LC_ESC @"\xC2\xA0" -#else -#define LC_ESC @"\033" -#endif - - - -// Reset colors -#define LC_RESET LC_ESC @"[0m" +@import Foundation; @interface MCLog : NSObject + (void)pluginDidLoad:(NSBundle *)bundle; diff --git a/MCLog/MCLog.m b/MCLog/MCLog.m index beb82b4..7ddafe7 100644 --- a/MCLog/MCLog.m +++ b/MCLog/MCLog.m @@ -7,15 +7,11 @@ // #import "MCLog.h" +#import "MCXcodeHeaders.h" +#import "MCDVTTextStorage.h" #import #include -#define MCLOG_FLAG "MCLOG_FLAG" -#define kTagSearchField 99 - -#define MCLogger(fmt, ...) NSLog((@"[MCLog] %s(Line:%d) " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__) - -#define NSColorWithHexRGB(rgb) [NSColor colorWithCalibratedRed:((rgb) >> 16 & 0xFF) / 255.f green:((rgb) >> 8 & 0xFF) / 255.f blue:((rgb) & 0xFF) / 255.f alpha:1.f] @class MCLogIDEConsoleArea; @@ -27,7 +23,7 @@ NSString *hash(id obj); NSArray *backtraceStack(); -void hookDVTTextStorage(); +void swizzleDVTTextStorage(); void hookIDEConsoleAdaptor(); void hookIDEConsoleArea(); void hookIDEConsoleItem(); @@ -393,188 +389,6 @@ - (void)_clearText } @end -/////////////////////////////////////////////////////////////////////////////////// -#pragma mark - MCDVTTextStorage - -static IMP OriginalFixAttributesInRangeIMP = nil; - -static void *kLastAttributeKey; -@interface MCDVTTextStorage : NSTextStorage -- (void)fixAttributesInRange:(NSRange)range; -@end - -@interface NSObject (DVTTextStorage) -- (void)setLastAttribute:(NSDictionary *)attribute; -- (NSDictionary *)lastAttribute; -- (void)setConsoleStorage:(BOOL)consoleStorage; -- (BOOL)consoleStorage; -- (void)updateAttributes:(NSMutableDictionary *)attrs withANSIESCString:(NSString *)ansiEscString; -@end - -@implementation MCDVTTextStorage - -- (void)fixAttributesInRange:(NSRange)range -{ - // To ignore those text storages which are not for console. - if (!self.consoleStorage) { - return; - } - - // Workaround: Comment it out in case of EXC_BAD_ACCESS. - // OriginalFixAttributesInRangeIMP(self, _cmd, range); - - __block NSRange lastRange = NSMakeRange(range.location, 0); - NSMutableDictionary *attrs = [NSMutableDictionary dictionary]; - if (self.lastAttribute.count > 0) { - [attrs setValuesForKeysWithDictionary:self.lastAttribute]; - } - - [escCharPattern() enumerateMatchesInString:self.string options:0 range:range usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { - if (attrs.count > 0) { - NSRange attrRange = NSMakeRange(lastRange.location, result.range.location - lastRange.location); - [self addAttributes:attrs range:attrRange]; - //MCLogger(@"apply attributes:%@\nin range:[%zd, %zd], affected string:%@", attrs, attrRange.location, attrRange.length, [self.string substringWithRange:attrRange]); - } - - NSString *attrsDesc = [self.string substringWithRange:[result rangeAtIndex:1]]; - if (attrsDesc.length == 0) { - [self addAttributes:@{ - NSFontAttributeName: [NSFont systemFontOfSize:0.000001f], - NSForegroundColorAttributeName: [NSColor clearColor] - } - range:result.range]; - lastRange = result.range; - return; - } - [self updateAttributes:attrs withANSIESCString:attrsDesc]; - [self addAttributes:@{ - NSFontAttributeName: [NSFont systemFontOfSize:0.000001f], - NSForegroundColorAttributeName: [NSColor clearColor] - } - range:result.range]; - lastRange = result.range; - }]; - self.lastAttribute = attrs; -} - -@end - -@implementation NSObject (DVTTextStorage) - -- (void)setLastAttribute:(NSDictionary *)attribute -{ - objc_setAssociatedObject(self, &kLastAttributeKey, attribute, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -- (NSDictionary *)lastAttribute -{ - return objc_getAssociatedObject(self, &kLastAttributeKey); -} - -- (void)setConsoleStorage:(BOOL)consoleStorage -{ - objc_setAssociatedObject(self, @selector(consoleStorage), @(consoleStorage), OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -- (BOOL)consoleStorage -{ - return [objc_getAssociatedObject(self, @selector(consoleStorage)) boolValue]; -} - -- (void)updateAttributes:(NSMutableDictionary *)attrs withANSIESCString:(NSString *)ansiEscString -{ - NSArray *attrComponents = [ansiEscString componentsSeparatedByString:@";"]; - for (NSString *attrName in attrComponents) { - NSUInteger attrCode = [attrName integerValue]; - switch (attrCode) { - case 0: - [attrs removeAllObjects]; - break; - - case 1: - [attrs setObject:[NSFont boldSystemFontOfSize:11.f] forKey:NSFontAttributeName]; - break; - - case 4: - [attrs setObject:@( NSUnderlineStyleSingle ) forKey:NSUnderlineStyleAttributeName]; - break; - - case 24: - [attrs setObject:@(NSUnderlineStyleNone ) forKey:NSUnderlineStyleAttributeName]; - break; - //foreground color - case 30: //black - [attrs setObject:[NSColor blackColor] forKey:NSForegroundColorAttributeName]; - break; - - case 31: // Red - [attrs setObject:NSColorWithHexRGB(0xd70000) forKey:NSForegroundColorAttributeName]; - break; - - case 32: // Green - [attrs setObject:NSColorWithHexRGB(0x00ff00) forKey:NSForegroundColorAttributeName]; - break; - - case 33: // Yellow - [attrs setObject:NSColorWithHexRGB(0xffff00) forKey:NSForegroundColorAttributeName]; - break; - - case 34: // Blue - [attrs setObject:NSColorWithHexRGB(0x005fff) forKey:NSForegroundColorAttributeName]; - break; - - case 35: // purple - [attrs setObject:NSColorWithHexRGB(0xff00ff) forKey:NSForegroundColorAttributeName]; - break; - - case 36: // cyan - [attrs setObject:NSColorWithHexRGB(0x00ffff) forKey:NSForegroundColorAttributeName]; - break; - - case 37: // gray - [attrs setObject:NSColorWithHexRGB(0x808080) forKey:NSForegroundColorAttributeName]; - break; - //background color - case 40: //black - [attrs setObject:[NSColor blackColor] forKey:NSBackgroundColorAttributeName]; - break; - - case 41: // Red - [attrs setObject:NSColorWithHexRGB(0xd70000) forKey:NSBackgroundColorAttributeName]; - break; - - case 42: // Green - [attrs setObject:NSColorWithHexRGB(0x00ff00) forKey:NSBackgroundColorAttributeName]; - break; - - case 43: // Yellow - [attrs setObject:NSColorWithHexRGB(0xffff00) forKey:NSBackgroundColorAttributeName]; - break; - - case 44: // Blue - [attrs setObject:NSColorWithHexRGB(0x005fff) forKey:NSBackgroundColorAttributeName]; - break; - - case 45: // purple - [attrs setObject:NSColorWithHexRGB(0xff00ff) forKey:NSBackgroundColorAttributeName]; - break; - - case 46: // cyan - [attrs setObject:NSColorWithHexRGB(0x00ffff) forKey:NSBackgroundColorAttributeName]; - break; - - case 47: // gray - [attrs setObject:NSColorWithHexRGB(0x808080) forKey:NSBackgroundColorAttributeName]; - break; - - default: - break; - } - } -} - -@end - /////////////////////////////////////////////////////////////////////////////////// #pragma mark - MCIDEConsoleAdaptor static IMP originalOutputForStandardOutputIMP = nil; @@ -702,8 +516,8 @@ + (void)load // alreay installed plugin return; } - - hookDVTTextStorage(); + + swizzleDVTTextStorage(); hookIDEConsoleAdaptor(); hookIDEConsoleArea(); hookIDEConsoleItem(); @@ -773,11 +587,11 @@ - (BOOL)addCustomViews return NO; } - MCDVTTextStorage *textStorage = [consoleTextView valueForKey:@"textStorage"]; + DVTTextStorage *textStorage = [consoleTextView valueForKey:@"textStorage"]; if ([textStorage respondsToSelector:@selector(setConsoleStorage:)]) { [textStorage setConsoleStorage:YES]; } - + contentView = [self getParantViewByClassName:@"DVTControllerContentView" andView:consoleTextView]; NSView *scopeBarView = [self getViewByClassName:@"DVTScopeBarView" andContainerView:contentView]; if (!scopeBarView) { @@ -915,13 +729,18 @@ void hookIDEConsoleItem() method_setImplementation(consoleItemInit, newConsoleItemInit); } -void hookDVTTextStorage() +void swizzleDVTTextStorage() { - Class DVTTextStorage = NSClassFromString(@"DVTTextStorage"); - Method fixAttributesInRange = class_getInstanceMethod(DVTTextStorage, @selector(fixAttributesInRange:)); - OriginalFixAttributesInRangeIMP = method_getImplementation(fixAttributesInRange); - IMP newFixAttributesInRangeIMP = class_getMethodImplementation([MCDVTTextStorage class], @selector(fixAttributesInRange:)); - method_setImplementation(fixAttributesInRange, newFixAttributesInRangeIMP); + Class DVTTextStorage = NSClassFromString(@"DVTTextStorage"); + Method fixAttributesInRange = class_getInstanceMethod(DVTTextStorage, @selector(fixAttributesInRange:)); + Method swizzledFixAttributesInRange = class_getInstanceMethod(DVTTextStorage, @selector(mc_fixAttributesInRange:)); + + BOOL didAddMethod = class_addMethod(DVTTextStorage, @selector(fixAttributesInRange:), method_getImplementation(swizzledFixAttributesInRange), method_getTypeEncoding(swizzledFixAttributesInRange)); + if (didAddMethod) { + class_replaceMethod(DVTTextStorage, @selector(mc_fixAttributesInRange:), method_getImplementation(fixAttributesInRange), method_getTypeEncoding(swizzledFixAttributesInRange)); + } else { + method_exchangeImplementations(fixAttributesInRange, swizzledFixAttributesInRange); + } } void hookIDEConsoleAdaptor() @@ -950,19 +769,6 @@ void hookIDEConsoleAdaptor() return pattern; } -NSRegularExpression * escCharPattern() -{ - static NSRegularExpression *pattern = nil; - if (pattern == nil) { - NSError *error = nil; - pattern = [NSRegularExpression regularExpressionWithPattern:(LC_ESC @"\\[([\\d;]*\\d+)m") options:0 error:&error]; - if (!pattern) { - MCLogger(@"%@", error); - } - } - return pattern; -} - NSSearchField *getSearchField(id consoleArea) { #pragma clang diagnostic push diff --git a/MCLog/MCXcodeHeaders.h b/MCLog/MCXcodeHeaders.h new file mode 100644 index 0000000..0534aed --- /dev/null +++ b/MCLog/MCXcodeHeaders.h @@ -0,0 +1,13 @@ +// +// MCXcodeHeaders.h +// MCLog +// +// Created by Michael Chen on 2015/11/17. +// Copyright © 2015年 Yuhua Chen. All rights reserved. +// + +@import AppKit; +@import Cocoa; + +@interface DVTTextStorage : NSTextStorage +@end \ No newline at end of file