diff --git a/.gitignore b/.gitignore index c964cd8..06b7058 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,7 @@ DerivedData # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control # #Pods/ + +# Mac +# +.DS_Store \ No newline at end of file diff --git a/LSLog-XCode.gif b/LSLog-XCode.gif new file mode 100644 index 0000000..3299ca2 Binary files /dev/null and b/LSLog-XCode.gif differ diff --git a/LSLog-XCode.xcodeproj/project.pbxproj b/LSLog-XCode.xcodeproj/project.pbxproj new file mode 100644 index 0000000..8ac93ab --- /dev/null +++ b/LSLog-XCode.xcodeproj/project.pbxproj @@ -0,0 +1,347 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + B13642821C1AC83700D82640 /* LSLogSettings.m in Sources */ = {isa = PBXBuildFile; fileRef = B13642811C1AC83700D82640 /* LSLogSettings.m */; settings = {ASSET_TAGS = (); }; }; + B136428B1C1D83E600D82640 /* LSLogSettingsWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = B13642891C1D83E600D82640 /* LSLogSettingsWindowController.m */; settings = {ASSET_TAGS = (); }; }; + B136428C1C1D83E600D82640 /* LSLogSettingsWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = B136428A1C1D83E600D82640 /* LSLogSettingsWindowController.xib */; settings = {ASSET_TAGS = (); }; }; + B15C7B631C197A8300C92958 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B15C7B621C197A8300C92958 /* AppKit.framework */; settings = {ASSET_TAGS = (); }; }; + B15C7B651C197A8300C92958 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B15C7B641C197A8300C92958 /* Foundation.framework */; settings = {ASSET_TAGS = (); }; }; + B15C7B691C197A8300C92958 /* LSLog-XCode.xcscheme in Resources */ = {isa = PBXBuildFile; fileRef = B15C7B681C197A8300C92958 /* LSLog-XCode.xcscheme */; }; + B15C7B6C1C197A8300C92958 /* LSLog.m in Sources */ = {isa = PBXBuildFile; fileRef = B15C7B6B1C197A8300C92958 /* LSLog.m */; }; + B15C7B7D1C19873500C92958 /* NSObject+LSLog.m in Sources */ = {isa = PBXBuildFile; fileRef = B15C7B7C1C19873500C92958 /* NSObject+LSLog.m */; settings = {ASSET_TAGS = (); }; }; + B15C7B801C19878C00C92958 /* NSTextStorage+LSLog.m in Sources */ = {isa = PBXBuildFile; fileRef = B15C7B7F1C19878C00C92958 /* NSTextStorage+LSLog.m */; settings = {ASSET_TAGS = (); }; }; + B15C7B831C1A665C00C92958 /* NSObject+LSSwizzle.m in Sources */ = {isa = PBXBuildFile; fileRef = B15C7B821C1A665B00C92958 /* NSObject+LSSwizzle.m */; settings = {ASSET_TAGS = (); }; }; + B15C7B891C1A6F7200C92958 /* LSIDEConsoleArea.m in Sources */ = {isa = PBXBuildFile; fileRef = B15C7B881C1A6F7200C92958 /* LSIDEConsoleArea.m */; settings = {ASSET_TAGS = (); }; }; + B15C7B8C1C1A726A00C92958 /* LSIDEConsoleItem.m in Sources */ = {isa = PBXBuildFile; fileRef = B15C7B8B1C1A726A00C92958 /* LSIDEConsoleItem.m */; settings = {ASSET_TAGS = (); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + B13642801C1AC83700D82640 /* LSLogSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSLogSettings.h; sourceTree = ""; }; + B13642811C1AC83700D82640 /* LSLogSettings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LSLogSettings.m; sourceTree = ""; }; + B13642881C1D83E600D82640 /* LSLogSettingsWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSLogSettingsWindowController.h; sourceTree = ""; }; + B13642891C1D83E600D82640 /* LSLogSettingsWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LSLogSettingsWindowController.m; sourceTree = ""; }; + B136428A1C1D83E600D82640 /* LSLogSettingsWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LSLogSettingsWindowController.xib; sourceTree = ""; }; + B15C7B5F1C197A8300C92958 /* LSLog-XCode.xcplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "LSLog-XCode.xcplugin"; sourceTree = BUILT_PRODUCTS_DIR; }; + B15C7B621C197A8300C92958 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; + B15C7B641C197A8300C92958 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; + B15C7B681C197A8300C92958 /* LSLog-XCode.xcscheme */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = "LSLog-XCode.xcscheme"; path = "LSLog-XCode.xcodeproj/xcshareddata/xcschemes/LSLog-XCode.xcscheme"; sourceTree = SOURCE_ROOT; }; + B15C7B6A1C197A8300C92958 /* LSLog.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LSLog.h; sourceTree = ""; }; + B15C7B6B1C197A8300C92958 /* LSLog.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LSLog.m; sourceTree = ""; }; + B15C7B701C197A8300C92958 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B15C7B7B1C19873500C92958 /* NSObject+LSLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+LSLog.h"; sourceTree = ""; }; + B15C7B7C1C19873500C92958 /* NSObject+LSLog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+LSLog.m"; sourceTree = ""; }; + B15C7B7E1C19878C00C92958 /* NSTextStorage+LSLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSTextStorage+LSLog.h"; sourceTree = ""; }; + B15C7B7F1C19878C00C92958 /* NSTextStorage+LSLog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSTextStorage+LSLog.m"; sourceTree = ""; }; + B15C7B811C1A665B00C92958 /* NSObject+LSSwizzle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+LSSwizzle.h"; sourceTree = ""; }; + B15C7B821C1A665B00C92958 /* NSObject+LSSwizzle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+LSSwizzle.m"; sourceTree = ""; }; + B15C7B871C1A6F7200C92958 /* LSIDEConsoleArea.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSIDEConsoleArea.h; sourceTree = ""; }; + B15C7B881C1A6F7200C92958 /* LSIDEConsoleArea.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LSIDEConsoleArea.m; sourceTree = ""; }; + B15C7B8A1C1A726A00C92958 /* LSIDEConsoleItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSIDEConsoleItem.h; sourceTree = ""; }; + B15C7B8B1C1A726A00C92958 /* LSIDEConsoleItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LSIDEConsoleItem.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + B15C7B5D1C197A8300C92958 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + B15C7B631C197A8300C92958 /* AppKit.framework in Frameworks */, + B15C7B651C197A8300C92958 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + B136427F1C1AC81F00D82640 /* Settings */ = { + isa = PBXGroup; + children = ( + B13642801C1AC83700D82640 /* LSLogSettings.h */, + B13642811C1AC83700D82640 /* LSLogSettings.m */, + B13642881C1D83E600D82640 /* LSLogSettingsWindowController.h */, + B13642891C1D83E600D82640 /* LSLogSettingsWindowController.m */, + B136428A1C1D83E600D82640 /* LSLogSettingsWindowController.xib */, + ); + path = Settings; + sourceTree = ""; + }; + B15C7B561C197A8300C92958 = { + isa = PBXGroup; + children = ( + B15C7B661C197A8300C92958 /* LSLog-XCode */, + B15C7B611C197A8300C92958 /* Frameworks */, + B15C7B601C197A8300C92958 /* Products */, + ); + sourceTree = ""; + }; + B15C7B601C197A8300C92958 /* Products */ = { + isa = PBXGroup; + children = ( + B15C7B5F1C197A8300C92958 /* LSLog-XCode.xcplugin */, + ); + name = Products; + sourceTree = ""; + }; + B15C7B611C197A8300C92958 /* Frameworks */ = { + isa = PBXGroup; + children = ( + B15C7B621C197A8300C92958 /* AppKit.framework */, + B15C7B641C197A8300C92958 /* Foundation.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + B15C7B661C197A8300C92958 /* LSLog-XCode */ = { + isa = PBXGroup; + children = ( + B136427F1C1AC81F00D82640 /* Settings */, + B15C7B7A1C19873500C92958 /* Category */, + B15C7B6A1C197A8300C92958 /* LSLog.h */, + B15C7B6B1C197A8300C92958 /* LSLog.m */, + B15C7B871C1A6F7200C92958 /* LSIDEConsoleArea.h */, + B15C7B881C1A6F7200C92958 /* LSIDEConsoleArea.m */, + B15C7B8A1C1A726A00C92958 /* LSIDEConsoleItem.h */, + B15C7B8B1C1A726A00C92958 /* LSIDEConsoleItem.m */, + B15C7B671C197A8300C92958 /* Supporting Files */, + ); + path = "LSLog-XCode"; + sourceTree = ""; + }; + B15C7B671C197A8300C92958 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + B15C7B701C197A8300C92958 /* Info.plist */, + B15C7B681C197A8300C92958 /* LSLog-XCode.xcscheme */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + B15C7B7A1C19873500C92958 /* Category */ = { + isa = PBXGroup; + children = ( + B15C7B7B1C19873500C92958 /* NSObject+LSLog.h */, + B15C7B7C1C19873500C92958 /* NSObject+LSLog.m */, + B15C7B7E1C19878C00C92958 /* NSTextStorage+LSLog.h */, + B15C7B7F1C19878C00C92958 /* NSTextStorage+LSLog.m */, + B15C7B811C1A665B00C92958 /* NSObject+LSSwizzle.h */, + B15C7B821C1A665B00C92958 /* NSObject+LSSwizzle.m */, + ); + path = Category; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + B15C7B5E1C197A8300C92958 /* LSLog-XCode */ = { + isa = PBXNativeTarget; + buildConfigurationList = B15C7B731C197A8300C92958 /* Build configuration list for PBXNativeTarget "LSLog-XCode" */; + buildPhases = ( + B15C7B5B1C197A8300C92958 /* Sources */, + B15C7B5C1C197A8300C92958 /* Resources */, + B15C7B5D1C197A8300C92958 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "LSLog-XCode"; + productName = "LSLog-XCode"; + productReference = B15C7B5F1C197A8300C92958 /* LSLog-XCode.xcplugin */; + productType = "com.apple.product-type.bundle"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + B15C7B571C197A8300C92958 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0700; + ORGANIZATIONNAME = lessfun.com; + TargetAttributes = { + B15C7B5E1C197A8300C92958 = { + CreatedOnToolsVersion = 7.0; + }; + }; + }; + buildConfigurationList = B15C7B5A1C197A8300C92958 /* Build configuration list for PBXProject "LSLog-XCode" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = B15C7B561C197A8300C92958; + productRefGroup = B15C7B601C197A8300C92958 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + B15C7B5E1C197A8300C92958 /* LSLog-XCode */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + B15C7B5C1C197A8300C92958 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B136428C1C1D83E600D82640 /* LSLogSettingsWindowController.xib in Resources */, + B15C7B691C197A8300C92958 /* LSLog-XCode.xcscheme in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + B15C7B5B1C197A8300C92958 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B15C7B8C1C1A726A00C92958 /* LSIDEConsoleItem.m in Sources */, + B15C7B891C1A6F7200C92958 /* LSIDEConsoleArea.m in Sources */, + B15C7B7D1C19873500C92958 /* NSObject+LSLog.m in Sources */, + B15C7B6C1C197A8300C92958 /* LSLog.m in Sources */, + B13642821C1AC83700D82640 /* LSLogSettings.m in Sources */, + B136428B1C1D83E600D82640 /* LSLogSettingsWindowController.m in Sources */, + B15C7B801C19878C00C92958 /* NSTextStorage+LSLog.m in Sources */, + B15C7B831C1A665C00C92958 /* NSObject+LSSwizzle.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + B15C7B711C197A8300C92958 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = NO; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + }; + name = Debug; + }; + B15C7B721C197A8300C92958 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = NO; + }; + name = Release; + }; + B15C7B741C197A8300C92958 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + DEPLOYMENT_LOCATION = YES; + DSTROOT = "$(HOME)"; + INFOPLIST_FILE = "LSLog-XCode/Info.plist"; + INSTALL_PATH = "/Library/Application Support/Developer/Shared/Xcode/Plug-ins"; + MACOSX_DEPLOYMENT_TARGET = 10.10; + PRODUCT_BUNDLE_IDENTIFIER = "com.lessfun.LSLog-XCode"; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = xcplugin; + }; + name = Debug; + }; + B15C7B751C197A8300C92958 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + DEPLOYMENT_LOCATION = YES; + DSTROOT = "$(HOME)"; + INFOPLIST_FILE = "LSLog-XCode/Info.plist"; + INSTALL_PATH = "/Library/Application Support/Developer/Shared/Xcode/Plug-ins"; + MACOSX_DEPLOYMENT_TARGET = 10.10; + PRODUCT_BUNDLE_IDENTIFIER = "com.lessfun.LSLog-XCode"; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = xcplugin; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + B15C7B5A1C197A8300C92958 /* Build configuration list for PBXProject "LSLog-XCode" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B15C7B711C197A8300C92958 /* Debug */, + B15C7B721C197A8300C92958 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B15C7B731C197A8300C92958 /* Build configuration list for PBXNativeTarget "LSLog-XCode" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B15C7B741C197A8300C92958 /* Debug */, + B15C7B751C197A8300C92958 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = B15C7B571C197A8300C92958 /* Project object */; +} diff --git a/LSLog-XCode.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/LSLog-XCode.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..df0488b --- /dev/null +++ b/LSLog-XCode.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/LSLog-XCode.xcodeproj/xcshareddata/xcschemes/LSLog-XCode.xcscheme b/LSLog-XCode.xcodeproj/xcshareddata/xcschemes/LSLog-XCode.xcscheme new file mode 100755 index 0000000..5cdfac2 --- /dev/null +++ b/LSLog-XCode.xcodeproj/xcshareddata/xcschemes/LSLog-XCode.xcscheme @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LSLog-XCode/Category/NSObject+LSLog.h b/LSLog-XCode/Category/NSObject+LSLog.h new file mode 100644 index 0000000..c4ab47e --- /dev/null +++ b/LSLog-XCode/Category/NSObject+LSLog.h @@ -0,0 +1,25 @@ +// +// NSObject+LSLog.h +// LSLog-XCode +// +// Created by lslin on 15/12/10. +// Copyright © 2015年 lessfun.com. All rights reserved. +// + +#import + +@class LSIDEConsoleArea; + +@interface NSObject (LSLog) + +@property (nonatomic, strong) NSTextView *consoleTextView; +@property (nonatomic, strong) LSIDEConsoleArea *consoleArea; +@property (nonatomic, assign) NSUInteger logLevel; + ++ (void)pluginDidLoad:(NSBundle *)plugin; + +- (NSSearchField *)ls_getFilterField; +- (void)ls_updateItemAttribute:(id)item; +- (NSString *)ls_hash; + +@end diff --git a/LSLog-XCode/Category/NSObject+LSLog.m b/LSLog-XCode/Category/NSObject+LSLog.m new file mode 100644 index 0000000..a3eb0f3 --- /dev/null +++ b/LSLog-XCode/Category/NSObject+LSLog.m @@ -0,0 +1,140 @@ +// +// NSObject+LSLog.m +// LSLog-XCode +// +// Created by lslin on 15/12/10. +// Copyright © 2015年 lessfun.com. All rights reserved. +// + + +#import "NSObject+LSLog.h" +#import "LSLog.h" +#import "LSLogSettings.h" +#import "LSIDEConsoleArea.h" + +#import + +static const void *kLSIDEConsoleTextViewKey; +static const void *kLSIDEConsoleAreaKey; +static const void *kLSLogLevelKey; + +@implementation NSObject (LSLog) + + ++ (void)pluginDidLoad:(NSBundle *)plugin +{ + NSString *currentApplicationName = [[NSBundle mainBundle] infoDictionary][@"CFBundleName"]; + if ([currentApplicationName isEqual:@"Xcode"]) { + [LSLog sharedPlugin]; + } +} + +- (void)setConsoleTextView:(NSTextView *)consoleTextView +{ + objc_setAssociatedObject(self, &kLSIDEConsoleTextViewKey, consoleTextView, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (NSTextView *)consoleTextView +{ + return objc_getAssociatedObject(self, &kLSIDEConsoleTextViewKey); +} + +- (void)setConsoleArea:(LSIDEConsoleArea *)consoleArea +{ + objc_setAssociatedObject(self, &kLSIDEConsoleAreaKey, consoleArea, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (LSIDEConsoleArea *)consoleArea +{ + return objc_getAssociatedObject(self, &kLSIDEConsoleAreaKey); +} + +- (void)setLogLevel:(NSUInteger)loglevel +{ + objc_setAssociatedObject(self, &kLSLogLevelKey, @(loglevel), OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (NSUInteger)logLevel +{ + return [objc_getAssociatedObject(self, &kLSLogLevelKey) unsignedIntegerValue]; +} + +- (NSSearchField *)ls_getFilterField { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wundeclared-selector" + if (![self respondsToSelector:@selector(scopeBarView)]) { + return nil; + } + + NSView *scopeBarView = [self performSelector:@selector(scopeBarView) withObject:nil]; + return [scopeBarView viewWithTag:LSLogViewTagFilterField]; +#pragma clang diagnositc pop +} + +- (void)ls_updateItemAttribute:(id)item { + NSString *logText = [item valueForKey:@"content"]; + if (!logText.length) { + return; + } + + if ([[item valueForKey:@"error"] boolValue] || [logText hasPrefix:@"error"]) { + logText = [self formatStringWithString:logText fgColor:[LSLogSettings defaultSettings].fgColorError]; + [item setValue:logText forKey:@"content"]; + return; + } + + if (![[item valueForKey:@"output"] boolValue] || [[item valueForKey:@"outputRequestedByUser"] boolValue]) { + return; + } + + NSString *content = logText; + if ([logText rangeOfString:[LSLogSettings defaultSettings].logLevelPrefixError].location != NSNotFound) { + [item setLogLevel:LSLogLevelError]; + content = [self formatStringWithString:logText fgColor:[LSLogSettings defaultSettings].fgColorError]; + } else if ([logText rangeOfString:[LSLogSettings defaultSettings].logLevelPrefixWarn].location != NSNotFound) { + [item setLogLevel:LSLogLevelWarn]; + content = [self formatStringWithString:logText fgColor:[LSLogSettings defaultSettings].fgColorWarn]; + } else if ([logText rangeOfString:[LSLogSettings defaultSettings].logLevelPrefixInfo].location != NSNotFound) { + [item setLogLevel:LSLogLevelInfo]; + content = [self formatStringWithString:logText fgColor:[LSLogSettings defaultSettings].fgColorInfo]; + } else if ([logText rangeOfString:[LSLogSettings defaultSettings].logLevelPrefixVerbose].location != NSNotFound) { + [item setLogLevel:LSLogLevelVerbose]; + content = [self formatStringWithString:logText fgColor:[LSLogSettings defaultSettings].fgColorVerbose]; + } else { + static NSArray *normalErrors = nil; + if (normalErrors == nil) { + normalErrors = @[@"Terminating app due to uncaught exception", + @"unrecognized selector sent to", + @"Assertion failure in" + ]; + } + for (NSString *err in normalErrors) { + if ([logText rangeOfString:err].location != NSNotFound) { + content = [self formatStringWithString:logText fgColor:[LSLogSettings defaultSettings].fgColorError]; + break; + } + } + } + + [item setValue:content forKey:@"content"]; +} + +- (NSString *)ls_hash { + return [NSString stringWithFormat:@"%lx", (long)self]; +} + +#pragma mark - Private + +- (NSString *)stringFromColor:(NSColor *)color { + return [NSString stringWithFormat:@"%d,%d,%d", (int)(color.redComponent * 255), (int)(color.greenComponent * 255), (int)(color.blueComponent * 255)]; +} + +- (NSString *)formatStringWithString:(NSString *)str fgColor:(NSColor *)fgColor { + return [NSString stringWithFormat:(XCODE_COLORS_ESCAPE @"fg%@;%@" XCODE_COLORS_RESET_FG), [self stringFromColor:fgColor], str]; +} + +//- (NSString *)formatStringWithString:(NSString *)str fgColor:(NSColor *)fgColor bgColor:(NSColor *)bgColor { +// return [NSString stringWithFormat:@"%@fg%@;bg%@;%@%@", XCODE_COLORS_ESCAPE, [self stringFromColor:fgColor], [self stringFromColor:bgColor], str, XCODE_COLORS_RESET]; +//} + +@end diff --git a/LSLog-XCode/Category/NSObject+LSSwizzle.h b/LSLog-XCode/Category/NSObject+LSSwizzle.h new file mode 100644 index 0000000..e961e03 --- /dev/null +++ b/LSLog-XCode/Category/NSObject+LSSwizzle.h @@ -0,0 +1,18 @@ +// +// NSObject+LSSwizzle.h +// LSLog-XCode +// +// Created by lslin on 15/12/11. +// Copyright © 2015年 lessfun.com. All rights reserved. +// + +#import + +IMP ls_replaceOriginalClassMethod(Class origClass, Class altClass, SEL sel); + +@interface NSObject (LSSwizzle) + ++ (BOOL)ls_swizzleOriginalMethod:(SEL)origSel withAltMethod:(SEL)altSel; ++ (IMP)ls_replaceOriginalClass:(Class)origClass withAltClass:(Class)altClass method:(SEL)sel; + +@end diff --git a/LSLog-XCode/Category/NSObject+LSSwizzle.m b/LSLog-XCode/Category/NSObject+LSSwizzle.m new file mode 100644 index 0000000..70c4f1f --- /dev/null +++ b/LSLog-XCode/Category/NSObject+LSSwizzle.m @@ -0,0 +1,47 @@ +// +// NSObject+LSSwizzle.m +// LSLog-XCode +// +// Created by lslin on 15/12/11. +// Copyright © 2015年 lessfun.com. All rights reserved. +// + +#import "NSObject+LSSwizzle.h" + +#import + +@implementation NSObject (LSSwizzle) + ++ (BOOL)ls_swizzleOriginalMethod:(SEL)origSel withAltMethod:(SEL)altSel { + Method origMethod = class_getInstanceMethod(self, origSel); + if (!origMethod) { + return NO; + } + + Method altMethod = class_getInstanceMethod(self, altSel); + if (!altMethod) { + return NO; + } + + class_addMethod(self, + origSel, + class_getMethodImplementation(self, origSel), + method_getTypeEncoding(origMethod)); + class_addMethod(self, + altSel, + class_getMethodImplementation(self, altSel), + method_getTypeEncoding(altMethod)); + + method_exchangeImplementations(class_getInstanceMethod(self, origSel), class_getInstanceMethod(self, altSel)); + return YES; +} + ++ (IMP)ls_replaceOriginalClass:(Class)origClass withAltClass:(Class)altClass method:(SEL)sel { + Method oldMethod = class_getInstanceMethod(origClass, sel); + IMP oldIMP = method_getImplementation(oldMethod); + IMP newIMP = class_getMethodImplementation(altClass, sel); + method_setImplementation(oldMethod, newIMP); + return oldIMP; +} + +@end diff --git a/LSLog-XCode/Category/NSTextStorage+LSLog.h b/LSLog-XCode/Category/NSTextStorage+LSLog.h new file mode 100644 index 0000000..904821a --- /dev/null +++ b/LSLog-XCode/Category/NSTextStorage+LSLog.h @@ -0,0 +1,15 @@ +// +// NSTextStorage+LSLog.h +// LSLog-XCode +// +// Created by lslin on 15/12/10. +// Copyright © 2015年 lessfun.com. All rights reserved. +// + +#import + +@interface NSTextStorage (LSLog) + +- (void)ls_fixAttributesInRange:(NSRange)range; + +@end diff --git a/LSLog-XCode/Category/NSTextStorage+LSLog.m b/LSLog-XCode/Category/NSTextStorage+LSLog.m new file mode 100644 index 0000000..5d916d4 --- /dev/null +++ b/LSLog-XCode/Category/NSTextStorage+LSLog.m @@ -0,0 +1,226 @@ +// +// NSTextStorage+LSLog.m +// LSLog-XCode +// +// Created by lslin on 15/12/10. +// Copyright © 2015年 lessfun.com. All rights reserved. +// + +#import "NSTextStorage+LSLog.h" +#import "LSLog.h" + +@implementation NSTextStorage (LSLog) + +- (void)ls_fixAttributesInRange:(NSRange)range { + [self ls_fixAttributesInRange:range]; + + [self applyANSIColors:range escapeSeq:XCODE_COLORS_ESCAPE]; +} + +// https://github.com/robbiehanson/XcodeColors +- (void)applyANSIColors:(NSRange)textStorageRange escapeSeq:(NSString *)escapeSeq { + NSRange range = [[self string] rangeOfString:escapeSeq options:0 range:textStorageRange]; + if (range.location == NSNotFound) { + // No escape sequence(s) in the string. + return; + } + + // Architecture: + // + // We're going to split the string into components separated by the given escape sequence. + // Then we're going to loop over the components, looking for color codes at the beginning of each component. + // + // For example, if the input string is "Hello__fg124,12,12;World" (with __ representing the escape sequence), + // then our components would be: ( + // "Hello" + // "fg124,12,12;World" + // ) + // + // We would find a color code (rgb 124, 12, 12) for the foreground color in the second component. + // At that point, the parsed foreground color goes into an attributes dictionary (attrs), + // and the foreground color stays in effect (for the current component & future components) + // until it gets cleared via a different foreground color, or a clear sequence. + // + // The attributes are applied to the entire range of the component, and then we move onto the next component. + // + // At the very end, we go back and apply "invisible" attributes (zero font, and clear text) + // to the escape and color sequences. + + + NSString *affectedString = [[self string] substringWithRange:textStorageRange]; + + // Split the string into components separated by the given escape sequence. + + NSArray *components = [affectedString componentsSeparatedByString:escapeSeq]; + + NSRange componentRange = textStorageRange; + componentRange.length = 0; + + BOOL firstPass = YES; + + NSMutableArray *seqRanges = [NSMutableArray arrayWithCapacity:[components count]]; + NSMutableDictionary *attrs = [NSMutableDictionary dictionaryWithCapacity:2]; + + for (NSString *component in components) { + if (firstPass) { + // The first component in the array won't need processing. + // If there was an escape sequence at the very beginning of the string, + // then the first component in the array will be an empty string. + // Otherwise the first component is everything before the first escape sequence. + } + else { + // componentSeqRange : Range of escape sequence within component, e.g. "fg124,12,12;" + + NSColor *color = nil; + NSUInteger colorCodeSeqLength = 0; + + BOOL stop = NO; + + BOOL reset = !stop && (stop = [component hasPrefix:@";"]); + BOOL fg = !stop && (stop = [component hasPrefix:@"fg"]); + BOOL bg = !stop && (stop = [component hasPrefix:@"bg"]); + + BOOL resetFg = fg && [component hasPrefix:@"fg;"]; + BOOL resetBg = bg && [component hasPrefix:@"bg;"]; + + if (reset) { + // Reset attributes + [attrs removeObjectForKey:NSForegroundColorAttributeName]; + [attrs removeObjectForKey:NSBackgroundColorAttributeName]; + + // Mark the range of the sequence (escape sequence + reset color sequence). + NSRange seqRange = (NSRange){ + .location = componentRange.location - [escapeSeq length], + .length = 1 + [escapeSeq length], + }; + [seqRanges addObject:[NSValue valueWithRange:seqRange]]; + } + else if (resetFg || resetBg) { + // Reset attributes + if (resetFg) + [attrs removeObjectForKey:NSForegroundColorAttributeName]; + else + [attrs removeObjectForKey:NSBackgroundColorAttributeName]; + + // Mark the range of the sequence (escape sequence + reset color sequence). + NSRange seqRange = (NSRange){ + .location = componentRange.location - [escapeSeq length], + .length = 3 + [escapeSeq length], + }; + [seqRanges addObject:[NSValue valueWithRange:seqRange]]; + } + else if (fg || bg) { + // Looking for something like this: "fg124,22,12;" or "bg17,24,210;". + // These are the rgb values for the foreground or background. + + NSString *str_r = nil; + NSString *str_g = nil; + NSString *str_b = nil; + + NSRange range_search = NSMakeRange(2, MIN(4, [component length] - 2)); + NSRange range_separator; + NSRange range_value; + + // Search for red separator + range_separator = [component rangeOfString:@"," options:0 range:range_search]; + if (range_separator.location != NSNotFound) { + // Extract red substring + range_value.location = range_search.location; + range_value.length = range_separator.location - range_search.location; + + str_r = [component substringWithRange:range_value]; + + // Update search range + range_search.location = range_separator.location + range_separator.length; + range_search.length = MIN(4, [component length] - range_search.location); + + // Search for green separator + range_separator = [component rangeOfString:@"," options:0 range:range_search]; + if (range_separator.location != NSNotFound) { + // Extract green substring + range_value.location = range_search.location; + range_value.length = range_separator.location - range_search.location; + + str_g = [component substringWithRange:range_value]; + + // Update search range + range_search.location = range_separator.location + range_separator.length; + range_search.length = MIN(4, [component length] - range_search.location); + + // Search for blue separator + range_separator = [component rangeOfString:@";" options:0 range:range_search]; + if (range_separator.location != NSNotFound) { + // Extract blue substring + range_value.location = range_search.location; + range_value.length = range_separator.location - range_search.location; + + str_b = [component substringWithRange:range_value]; + + // Mark the length of the entire color code sequence. + colorCodeSeqLength = range_separator.location + range_separator.length; + } + } + } + + if (str_r && str_g && str_b) { + // Parse rgb values and create color + + int r = MAX(0, MIN(255, [str_r intValue])); + int g = MAX(0, MIN(255, [str_g intValue])); + int b = MAX(0, MIN(255, [str_b intValue])); + + color = [NSColor colorWithCalibratedRed:(r/255.0) + green:(g/255.0) + blue:(b/255.0) + alpha:1.0]; + + if (fg) { + [attrs setObject:color forKey:NSForegroundColorAttributeName]; + } else { + [attrs setObject:color forKey:NSBackgroundColorAttributeName]; + } + + // Mark the range of the entire sequence (escape sequence + color code sequence). + NSRange seqRange = (NSRange){ + .location = componentRange.location - [escapeSeq length], + .length = colorCodeSeqLength + [escapeSeq length], + }; + [seqRanges addObject:[NSValue valueWithRange:seqRange]]; + } else { + // Wasn't able to parse a color code + + [attrs removeObjectForKey:NSForegroundColorAttributeName]; + [attrs removeObjectForKey:NSBackgroundColorAttributeName]; + + NSRange seqRange = (NSRange){ + .location = componentRange.location - [escapeSeq length], + .length = [escapeSeq length], + }; + [seqRanges addObject:[NSValue valueWithRange:seqRange]]; + } + } + } + + componentRange.length = [component length]; + + [self addAttributes:attrs range:componentRange]; + + componentRange.location += componentRange.length + [escapeSeq length]; + firstPass = NO; + } // END: for (NSString *component in components) + + // Now loop over all the discovered sequences, and apply "invisible" attributes to them. + if ([seqRanges count] > 0) { + NSDictionary *clearAttrs = + [NSDictionary dictionaryWithObjectsAndKeys: + [NSFont systemFontOfSize:0.001], NSFontAttributeName, + [NSColor clearColor], NSForegroundColorAttributeName, nil]; + + for (NSValue *seqRangeValue in seqRanges) { + NSRange seqRange = [seqRangeValue rangeValue]; + [self addAttributes:clearAttrs range:seqRange]; + } + } +} + +@end diff --git a/LSLog-XCode/Info.plist b/LSLog-XCode/Info.plist new file mode 100644 index 0000000..3225c94 --- /dev/null +++ b/LSLog-XCode/Info.plist @@ -0,0 +1,57 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + DVTPlugInCompatibilityUUIDs + + 63FC1C47-140D-42B0-BB4D-A10B2D225574 + 37B30044-3B14-46BA-ABAA-F01000C27B63 + 640F884E-CE55-4B40-87C0-8869546CAB7A + A2E4D43F-41F4-4FB9-BB94-7177011C9AED + AD68E85B-441B-4301-B564-A45E4919A6AD + C4A681B0-4A26-480E-93EC-1218098B9AA0 + FEC992CC-CA4A-4CFD-8881-77300FCB848A + A16FF353-8441-459E-A50C-B071F53F51B7 + 992275C1-432A-4CF7-B659-D84ED6D42D3F + 9F75337B-21B4-4ADC-B558-F9CADF7073A7 + 992275C1-432A-4CF7-B659-D84ED6D42D3F + 8DC44374-2B35-4C57-A6FE-2AD66A36AAD9 + E969541F-E6F9-4D25-8158-72DC3545A6C6 + AABB7188-E14E-4433-AD3B-5CD791EAD9A3 + 5EDAC44F-8E0B-42C9-9BEF-E9C12EEC4949 + 7FDF5C7A-131F-4ABB-9EDC-8C5F8F0B8A90 + 0420B86A-AA43-4792-9ED0-6FE0F2B16A13 + CC0D0F4F-05B3-431A-8F33-F84AFCB2C651 + 7265231C-39B4-402C-89E1-16167C4CC990 + F41BD31E-2683-44B8-AE7F-5F09E919790E + + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSPrincipalClass + LSLog-XCode + XC4Compatible + + XCPluginHasUI + + + diff --git a/LSLog-XCode/LSIDEConsoleArea.h b/LSLog-XCode/LSIDEConsoleArea.h new file mode 100644 index 0000000..6e57336 --- /dev/null +++ b/LSLog-XCode/LSIDEConsoleArea.h @@ -0,0 +1,16 @@ +// +// LSIDEConsoleArea.h +// LSLog-XCode +// +// Created by lslin on 15/12/11. +// Copyright © 2015年 lessfun.com. All rights reserved. +// + +#import + +@interface LSIDEConsoleArea : NSViewController + +- (BOOL)_shouldAppendItem:(id)obj; +- (void)_clearText; + +@end diff --git a/LSLog-XCode/LSIDEConsoleArea.m b/LSLog-XCode/LSIDEConsoleArea.m new file mode 100644 index 0000000..586fb55 --- /dev/null +++ b/LSLog-XCode/LSIDEConsoleArea.m @@ -0,0 +1,73 @@ +// +// LSIDEConsoleArea.m +// LSLog-XCode +// +// Created by lslin on 15/12/11. +// Copyright © 2015年 lessfun.com. All rights reserved. +// + +#import "LSIDEConsoleArea.h" +#import "LSLog.h" +#import "NSObject+LSLog.h" + +@implementation LSIDEConsoleArea + +- (BOOL)_shouldAppendItem:(id)obj { + NSSearchField *fiterField = [self ls_getFilterField]; + if (!fiterField) { + return YES; + } + if (!fiterField.consoleArea) { + fiterField.consoleArea = self; + } + + NSMutableDictionary *cachedItems = [[LSLog originalConsoleAreaItemsDict] objectForKey:[self ls_hash]]; + if (!cachedItems) { + cachedItems = [NSMutableDictionary dictionary]; + [[LSLog originalConsoleAreaItemsDict] setObject:cachedItems forKey:[self ls_hash]]; + } + if (![cachedItems objectForKey:@([obj timestamp])]) { + [cachedItems setObject:obj forKey:@([obj timestamp])]; + } + + NSInteger filterMode = [[self valueForKey:@"filterMode"] intValue]; + BOOL shouldShowLogLevel = YES; + BOOL isForcedShow = [[obj valueForKey:@"input"] boolValue] + || [[obj valueForKey:@"prompt"] boolValue] + || [[obj valueForKey:@"outputRequestedByUser"] boolValue] + || [[obj valueForKey:@"adaptorType"] hasSuffix:@".Debugger"]; + if (filterMode >= LSLogLevelVerbose) { + shouldShowLogLevel = [obj logLevel] >= filterMode || isForcedShow; + } else { + shouldShowLogLevel = [LSLog.originalShouldAppendItemIMP(self, _cmd, obj) boolValue]; + } + + if (!shouldShowLogLevel) { + return NO; + } + + NSString *filterString = fiterField.stringValue; + if (!filterString.length) { + return YES; + } + + // Match with regex + NSError *error; + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:filterString + options:(NSRegularExpressionCaseInsensitive|NSRegularExpressionDotMatchesLineSeparators) + error:&error]; + NSString *content = [obj content]; + NSArray *matches = [regex matchesInString:content options:0 range:NSMakeRange(0, content.length)]; + if ([matches count] > 0 || isForcedShow) { + return YES; + } + + return NO; +} + +- (void)_clearText { + LSLog.originalClearTextIMP(self, _cmd); + [[LSLog originalConsoleAreaItemsDict] removeObjectForKey:[self ls_hash]]; +} + +@end diff --git a/LSLog-XCode/LSIDEConsoleItem.h b/LSLog-XCode/LSIDEConsoleItem.h new file mode 100644 index 0000000..4a1a54f --- /dev/null +++ b/LSLog-XCode/LSIDEConsoleItem.h @@ -0,0 +1,15 @@ +// +// LSIDEConsoleItem.h +// LSLog-XCode +// +// Created by lslin on 15/12/11. +// Copyright © 2015年 lessfun.com. All rights reserved. +// + +#import + +@interface LSIDEConsoleItem : NSObject + +- (id)initWithAdaptorType:(id)arg1 content:(id)arg2 kind:(int)arg3; + +@end diff --git a/LSLog-XCode/LSIDEConsoleItem.m b/LSLog-XCode/LSIDEConsoleItem.m new file mode 100644 index 0000000..4b351ee --- /dev/null +++ b/LSLog-XCode/LSIDEConsoleItem.m @@ -0,0 +1,24 @@ +// +// LSIDEConsoleItem.m +// LSLog-XCode +// +// Created by lslin on 15/12/11. +// Copyright © 2015年 lessfun.com. All rights reserved. +// + +#import "LSIDEConsoleItem.h" +#import "LSLog.h" +#import "LSLogSettings.h" +#import "NSObject+LSLog.h" + +@implementation LSIDEConsoleItem + +- (id)initWithAdaptorType:(id)arg1 content:(id)arg2 kind:(int)arg3 { + id item = LSLog.originalConsoleItemInitIMP(self, _cmd, arg1, arg2, arg3); + if (![LSLog hasXcodeColorsInstalled]) { + [self ls_updateItemAttribute:item]; + } + return item; +} + +@end diff --git a/LSLog-XCode/LSLog.h b/LSLog-XCode/LSLog.h new file mode 100755 index 0000000..3cbde7b --- /dev/null +++ b/LSLog-XCode/LSLog.h @@ -0,0 +1,60 @@ +// +// LSLog-XCode.h +// LSLog-XCode +// +// Created by lslin on 15/12/10. +// Copyright © 2015年 lessfun.com. All rights reserved. +// + +#import + +@class LSLog; + +#define XCODE_COLORS "XcodeColors" +#define XCODE_COLORS_ESCAPE @"\033[" + +#define XCODE_COLORS_RESET_FG XCODE_COLORS_ESCAPE @"fg;" // Clear any foreground color +#define XCODE_COLORS_RESET_BG XCODE_COLORS_ESCAPE @"bg;" // Clear any background color +#define XCODE_COLORS_RESET XCODE_COLORS_ESCAPE @";" // Clear any foreground or background color + + +#define LS_COLOR_RGB(r, g, b) [NSColor colorWithRed:r / 255.0 green:g / 255.0 blue:b / 255.0 alpha:1] +#define LS_COLOR_RGBA(r, g, b, a) [NSColor colorWithRed:r / 255.0 green:g / 255.0 blue:b / 255.0 alpha:a] + +#define LS_RECT_ADJUST(r, x1, y1, w1, h1) NSMakeRect(r.origin.x + x1, r.origin.y + y1, r.size.width + w1, r.size.height + h1) +#define LS_VIEW_FRAME_ADJUST(view, x1, y1, w1, h1) view.frame = LS_RECT_ADJUST(view.frame, x1, y1, w1, h1) + + +typedef NS_ENUM(NSUInteger, LSLogLevel) { + LSLogLevelVerbose = 1111000, + LSLogLevelInfo = 1111001, + LSLogLevelWarn = 1111002, + LSLogLevelError = 1111003, +}; + + +typedef NS_ENUM(NSUInteger, LSLogViewTag) { + LSLogViewTagSettingsButton = 2111000, + LSLogViewTagFilterField = 2111001, +}; + + +extern NSMutableArray *originalConsoleItems; + + +@interface LSLog : NSObject + ++ (instancetype)sharedPlugin; + +/** + * NSMutableDictionary> + * + * @return Cached console items + */ ++ (NSMutableDictionary *)originalConsoleAreaItemsDict; ++ (IMP)originalShouldAppendItemIMP; ++ (IMP)originalClearTextIMP; ++ (IMP)originalConsoleItemInitIMP; ++ (BOOL)hasXcodeColorsInstalled; + +@end \ No newline at end of file diff --git a/LSLog-XCode/LSLog.m b/LSLog-XCode/LSLog.m new file mode 100755 index 0000000..e449b58 --- /dev/null +++ b/LSLog-XCode/LSLog.m @@ -0,0 +1,273 @@ +// +// LSLog.m +// LSLog-XCode +// +// Created by lslin on 15/12/10. +// Copyright © 2015年 lessfun.com. All rights reserved. +// + +#import "LSLog.h" + +#import "LSIDEConsoleArea.h" +#import "LSIDEConsoleItem.h" +#import "LSLogSettingsWindowController.h" + +#import "NSObject+LSLog.h" +#import "NSObject+LSSwizzle.h" +#import "NSTextStorage+LSLog.h" + +#define LSLOG_FLAG "LSLOG_FLAG" + +static NSMutableDictionary *_originalConsoleAreaItemsDict = nil; +static IMP _originalShouldAppendItemIMP = nil; +static IMP _originalClearTextIMP = nil; +static IMP _originalConsoleItemInitIMP = nil; +static BOOL _hasXcodeColorsInstalled = NO; + + +@interface LSLog() + +@property (nonatomic, strong, readwrite) LSLogSettingsWindowController *settingsPanel; + +@end + + +@implementation LSLog + ++ (void)load { + if (getenv(LSLOG_FLAG) && !strcmp(getenv(LSLOG_FLAG), "YES")) { + return; + } + + _originalConsoleAreaItemsDict = [NSMutableDictionary dictionary]; + + _hasXcodeColorsInstalled = getenv(XCODE_COLORS) && (strcmp(getenv(XCODE_COLORS), "YES") != 0); + if (!_hasXcodeColorsInstalled) { + if (![NSTextStorage ls_swizzleOriginalMethod:@selector(fixAttributesInRange:) withAltMethod:@selector(ls_fixAttributesInRange:)]) { + NSLog(@"LSLog: Error swizzling methods fixAttributesInRange"); + return; + } + } + + _originalShouldAppendItemIMP = [self ls_replaceOriginalClass:NSClassFromString(@"IDEConsoleArea") withAltClass:NSClassFromString(@"LSIDEConsoleArea") method:@selector(_shouldAppendItem:)]; + _originalClearTextIMP = [self ls_replaceOriginalClass:NSClassFromString(@"IDEConsoleArea") withAltClass:NSClassFromString(@"LSIDEConsoleArea") method:@selector(_clearText)]; + + _originalConsoleItemInitIMP = [self ls_replaceOriginalClass:NSClassFromString(@"IDEConsoleItem") withAltClass:NSClassFromString(@"LSIDEConsoleItem") method:@selector(initWithAdaptorType:content:kind:)]; + + setenv(LSLOG_FLAG, "YES", 0); +} + ++ (instancetype)sharedPlugin { + static id sharedPlugin = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedPlugin = [[self alloc] init]; + }); + + return sharedPlugin; +} + ++ (NSMutableDictionary *)originalConsoleAreaItemsDict { + return _originalConsoleAreaItemsDict; +} + ++ (IMP)originalShouldAppendItemIMP { + return _originalShouldAppendItemIMP; +} + ++ (IMP)originalClearTextIMP { + return _originalClearTextIMP; +} + ++ (IMP)originalConsoleItemInitIMP { + return _originalConsoleItemInitIMP; +} + ++ (BOOL)hasXcodeColorsInstalled { + return _hasXcodeColorsInstalled; +} + +#pragma mark - Lifecircle + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (id)init { + if (self = [super init]) { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(onIDEControlGroupDidChange:) + name:@"IDEControlGroupDidChangeNotificationName" + object:nil]; + } + return self; +} + +#pragma mark - Notification + +- (void)onIDEControlGroupDidChange:(NSNotification*)notification { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [self addFilterViews]; + }); +} + +- (void)onFilterFieldDidChange:(NSNotification*)notification { + if (![[notification object] isMemberOfClass:[NSSearchField class]]) { + return; + } + + NSSearchField *searchField = [notification object]; + if (![searchField respondsToSelector:@selector(consoleTextView)]) { + return; + } + + if (![searchField respondsToSelector:@selector(consoleArea)]) { + return; + } + + NSTextView *consoleTextView = searchField.consoleTextView; + LSIDEConsoleArea *consoleArea = searchField.consoleArea; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wundeclared-selector" + if ([consoleTextView respondsToSelector:@selector(clearConsoleItems)]) { + [consoleTextView performSelector:@selector(clearConsoleItems) withObject:nil]; + } + + if ([consoleArea respondsToSelector:@selector(_appendItems:)]) { + + NSMutableDictionary *cacheItems = [[LSLog originalConsoleAreaItemsDict] objectForKey:[consoleArea ls_hash]]; + NSArray *sortedKeys = [[cacheItems allKeys] sortedArrayUsingSelector:@selector(compare:)]; + NSMutableArray *sortedItems = [NSMutableArray array]; + for (NSNumber *key in sortedKeys) { + [sortedItems addObject:cacheItems[key]]; + } + + [consoleArea performSelector:@selector(_appendItems:) withObject:sortedItems]; + } +#pragma clang diagnostic pop +} + +#pragma mark - Action + +- (void)onSettingsButtonClicked:(NSButton *)sender { + self.settingsPanel = [[LSLogSettingsWindowController alloc] initWithWindowNibName:@"LSLogSettingsWindowController"]; + [self.settingsPanel showWindow:self.settingsPanel]; +} + +#pragma mark - Private + +- (void)addFilterViews { + NSView *consoleTextView = [self getViewByClassName:@"IDEConsoleTextView" inContainerView:[[NSApp mainWindow] contentView]]; + if (!consoleTextView) { + NSLog(@"[LSLog:addFilterViews] IDEConsoleTextView not found"); + return; + } + + NSView *consoleParentView = [self getParantViewByClassName:@"DVTControllerContentView" ofView:consoleTextView]; + NSView *scopeBarView = [self getViewByClassName:@"DVTScopeBarView" inContainerView:consoleParentView]; + if (!scopeBarView) { + NSLog(@"[LSLog:addFilterViews] DVTScopeBarView not found"); + return; + } + + if ([scopeBarView viewWithTag:LSLogViewTagSettingsButton]) { + return; + } + + NSButton *trashButton = nil; + NSPopUpButton *filterButton = nil; + for (NSView *subView in scopeBarView.subviews) { + if (trashButton && filterButton) { + break; + } + if (trashButton == nil && [[subView className] isEqualToString:@"NSButton"]) { + trashButton = (NSButton *)subView; + } else if (filterButton == nil && [[subView className] isEqualToString:@"NSPopUpButton"]) { + filterButton = (NSPopUpButton *)subView; + } + } + + if (!trashButton) { + NSLog(@"[LSLog:addFilterViews] TrashButton not found"); + return; + } + + if (filterButton) { + // Remove empty item + for (NSInteger index = filterButton.numberOfItems - 1; index >= 0; -- index) { + NSMenuItem *item = [filterButton itemAtIndex:index]; + if (!item.title.length) { + [filterButton removeItemAtIndex:index]; + } + } + + NSArray *items = @[@"Verbose", @"Info", @"Warn", @"Error"]; + NSUInteger tag = LSLogLevelVerbose; + for (NSUInteger i = 0; i < items.count; ++ i) { + [filterButton addItemWithTitle:items[i]]; + [filterButton itemAtIndex:filterButton.numberOfItems - 1].tag = tag; + tag ++; + } + } + + NSInteger selectedItem = [filterButton indexOfItemWithTag:[[consoleTextView valueForKey:@"logMode"] intValue]]; + if (selectedItem < 0 || selectedItem >= [filterButton numberOfItems]) { + selectedItem = 0; + } + [filterButton selectItemAtIndex:selectedItem]; + + NSRect frame = LS_RECT_ADJUST(trashButton.frame, -70, -4, 50, 4); + + NSButton *settingsButton = [[NSButton alloc] initWithFrame:frame]; + settingsButton.tag = LSLogViewTagSettingsButton; + settingsButton.font = [NSFont systemFontOfSize:10.0]; + settingsButton.autoresizingMask = NSViewMinXMargin | NSViewMinYMargin; + [settingsButton setButtonType:NSToggleButton]; + [settingsButton setBezelStyle:NSRoundedBezelStyle]; + [settingsButton setTitle:@"Settings"]; + [settingsButton setTarget:self]; + [settingsButton setAction:@selector(onSettingsButtonClicked:)]; + [scopeBarView addSubview:settingsButton]; + + frame = LS_RECT_ADJUST(frame, -220, 4, 150, -4); + + NSSearchField *filterField = [[NSSearchField alloc] initWithFrame:frame]; + filterField.tag = LSLogViewTagFilterField; + filterField.autoresizingMask = NSViewMinXMargin | NSViewMinYMargin; + filterField.font = [NSFont systemFontOfSize:11.0]; + filterField.delegate = self; + filterField.consoleTextView = (NSTextView *)consoleTextView; + [filterField.cell setPlaceholderString:@"Regular Expression"]; + [scopeBarView addSubview:filterField]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onFilterFieldDidChange:) name:NSControlTextDidChangeNotification object:nil]; +} + +- (NSView *)getViewByClassName:(NSString *)className inContainerView:(NSView *)container { + Class class = NSClassFromString(className); + for (NSView *subView in container.subviews) { + if ([subView isKindOfClass:class]) { + return subView; + } else { + NSView *view = [self getViewByClassName:className inContainerView:subView]; + if ([view isKindOfClass:class]) { + return view; + } + } + } + return nil; +} + +- (NSView *)getParantViewByClassName:(NSString *)className ofView:(NSView *)view { + NSView *superView = view.superview; + while (superView) { + if ([[superView className] isEqualToString:className]) { + return superView; + } + superView = superView.superview; + } + + return nil; +} +@end diff --git a/LSLog-XCode/Settings/LSLogSettings.h b/LSLog-XCode/Settings/LSLogSettings.h new file mode 100644 index 0000000..ea4f3c0 --- /dev/null +++ b/LSLog-XCode/Settings/LSLogSettings.h @@ -0,0 +1,30 @@ +// +// LSLogSettings.h +// LSLog-XCode +// +// Created by lslin on 15/12/11. +// Copyright © 2015年 lessfun.com. All rights reserved. +// + +#import + +@interface LSLogSettings : NSObject + +@property (strong, nonatomic) NSString *logLevelPrefixError; /**< default is '' */ +@property (strong, nonatomic) NSString *logLevelPrefixWarn; /**< default is '' */ +@property (strong, nonatomic) NSString *logLevelPrefixInfo; /**< default is '' */ +@property (strong, nonatomic) NSString *logLevelPrefixVerbose; /**< default is '' */ + +@property (strong, nonatomic) NSColor *fgColorError; /**< default is '214, 57, 30' */ +@property (strong, nonatomic) NSColor *fgColorWarn; /**< default is '204, 121, 32' */ +@property (strong, nonatomic) NSColor *fgColorInfo; /**< default is '32, 32, 32' */ +@property (strong, nonatomic) NSColor *fgColorVerbose; /**< default is '0, 0, 255' */ + +//@property (strong, nonatomic) NSColor *bgColorError; /**< default is '255, 255, 255' */ +//@property (strong, nonatomic) NSColor *bgColorWarn; /**< default is '255, 255, 255' */ +//@property (strong, nonatomic) NSColor *bgColorInfo; /**< default is '255, 255, 255' */ +//@property (strong, nonatomic) NSColor *bgColorVerbose; /**< default is '255, 255, 255' */ + ++ (instancetype)defaultSettings; + +@end diff --git a/LSLog-XCode/Settings/LSLogSettings.m b/LSLog-XCode/Settings/LSLogSettings.m new file mode 100644 index 0000000..6886659 --- /dev/null +++ b/LSLog-XCode/Settings/LSLogSettings.m @@ -0,0 +1,297 @@ +// +// LSLogSettings.m +// LSLog-XCode +// +// Created by lslin on 15/12/11. +// Copyright © 2015年 lessfun.com. All rights reserved. +// + +#import "LSLogSettings.h" +#import "LSLog.h" + +NSString *kKeyLogLevelPrefixError = @"com.lessfun.LSLog.KeyLogLevelPrefixError"; +NSString *kKeyLogLevelPrefixWarn = @"com.lessfun.LSLog.KeyLogLevelPrefixWarn"; +NSString *kKeyLogLevelPrefixInfo = @"com.lessfun.LSLog.KeyLogLevelPrefixInfo"; +NSString *kKeyLogLevelPrefixVerbose = @"com.lessfun.LSLog.KeyLogLevelPrefixVerbose"; + +NSString *kKeyFgColorError = @"com.lessfun.LSLog.KeyFgColorError"; +NSString *kKeyFgColorWarn = @"com.lessfun.LSLog.KeyFgColorWarn"; +NSString *kKeyFgColorInfo = @"com.lessfun.LSLog.KeyFgColorInfo"; +NSString *kKeyFgColorVerbose = @"com.lessfun.LSLog.KeyFgColorVerbose"; + +//NSString *kKeyBgColorError = @"com.lessfun.LSLog.KeyBgColorError"; +//NSString *kKeyBgColorWarn = @"com.lessfun.LSLog.KeyBgColorWarn"; +//NSString *kKeyBgColorInfo = @"com.lessfun.LSLog.KeyBgColorInfo"; +//NSString *kKeyBgColorVerbose = @"com.lessfun.LSLog.KeyBgColorVerbose"; + +@implementation LSLogSettings + + +@synthesize logLevelPrefixError = _logLevelPrefixError; +@synthesize logLevelPrefixWarn = _logLevelPrefixWarn; +@synthesize logLevelPrefixInfo = _logLevelPrefixInfo; +@synthesize logLevelPrefixVerbose = _logLevelPrefixVerbose; + +@synthesize fgColorError = _fgColorError; +@synthesize fgColorWarn = _fgColorWarn; +@synthesize fgColorInfo = _fgColorInfo; +@synthesize fgColorVerbose = _fgColorVerbose; + +//@synthesize bgColorError = _bgColorError; +//@synthesize bgColorWarn = _bgColorWarn; +//@synthesize bgColorVerbose = _bgColorVerbose; +//@synthesize bgColorInfo = _bgColorInfo; + + ++ (instancetype)defaultSettings { + static id _sharedInstance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _sharedInstance = [[self alloc] init]; + }); + return _sharedInstance; +} + +- (id)init { + if (self = [super init]) { + } + return self; +} + +#pragma mark - Property + +- (NSString *)logLevelPrefixError { + if (!_logLevelPrefixError) { + _logLevelPrefixError = [self getConfigForKey:kKeyLogLevelPrefixError]; + if (!_logLevelPrefixError) { + _logLevelPrefixError = @""; + } + } + + return _logLevelPrefixError; +} + +- (void)setLogLevelPrefixError:(NSString *)logLevelPrefixError { + if (!logLevelPrefixError.length) { + return; + } + _logLevelPrefixError = logLevelPrefixError; + [self setConfig:logLevelPrefixError forKey:kKeyLogLevelPrefixError]; +} + +- (NSString *)logLevelPrefixWarn { + if (!_logLevelPrefixWarn) { + _logLevelPrefixWarn = [self getConfigForKey:kKeyLogLevelPrefixWarn]; + if (!_logLevelPrefixWarn) { + _logLevelPrefixWarn = @""; + } + } + + return _logLevelPrefixWarn; +} + +- (void)setLogLevelPrefixWarn:(NSString *)logLevelPrefixWarn { + if (!logLevelPrefixWarn.length) { + return; + } + _logLevelPrefixWarn = logLevelPrefixWarn; + [self setConfig:logLevelPrefixWarn forKey:kKeyLogLevelPrefixWarn]; +} + +- (NSString *)logLevelPrefixVerbose { + if (!_logLevelPrefixVerbose) { + _logLevelPrefixVerbose = [self getConfigForKey:kKeyLogLevelPrefixVerbose]; + if (!_logLevelPrefixVerbose) { + _logLevelPrefixVerbose = @""; + } + } + + return _logLevelPrefixVerbose; +} + +- (void)setLogLevelPrefixVerbose:(NSString *)logLevelPrefixVerbose { + if (!logLevelPrefixVerbose.length) { + return; + } + _logLevelPrefixVerbose =logLevelPrefixVerbose; + [self setConfig:logLevelPrefixVerbose forKey:kKeyLogLevelPrefixVerbose]; +} + +- (NSString *)logLevelPrefixInfo { + if (!_logLevelPrefixInfo) { + _logLevelPrefixInfo = [self getConfigForKey:kKeyLogLevelPrefixInfo]; + if (!_logLevelPrefixInfo) { + _logLevelPrefixInfo = @""; + } + } + + return _logLevelPrefixInfo; +} + +- (void)setLogLevelPrefixInfo:(NSString *)logLevelPrefixInfo { + if (!logLevelPrefixInfo.length) { + return; + } + _logLevelPrefixInfo = logLevelPrefixInfo; + [self setConfig:logLevelPrefixInfo forKey:kKeyLogLevelPrefixInfo]; +} + +- (NSColor *)fgColorError { + if (!_fgColorError) { + _fgColorError = [self getColorForKey:kKeyFgColorError]; + if (!_fgColorError) { + _fgColorError = LS_COLOR_RGB(214, 57, 30); + } + } + + return _fgColorError; +} + +- (void)setFgColorError:(NSColor *)fgColorError { + _fgColorError = fgColorError; + [self setColor:fgColorError forKey:kKeyFgColorError]; +} + +- (NSColor *)fgColorWarn { + if (!_fgColorWarn) { + _fgColorWarn = [self getColorForKey:kKeyFgColorWarn]; + if (!_fgColorWarn) { + _fgColorWarn = LS_COLOR_RGB(204, 121, 32); + } + } + + return _fgColorWarn; +} + +- (void)setFgColorWarn:(NSColor *)fgColorWarn { + _fgColorWarn = fgColorWarn; + [self setColor:fgColorWarn forKey:kKeyFgColorWarn]; +} + +- (NSColor *)fgColorInfo { + if (!_fgColorInfo) { + _fgColorInfo = [self getColorForKey:kKeyFgColorInfo]; + if (!_fgColorInfo) { + _fgColorInfo = LS_COLOR_RGB(32, 32, 32); + } + } + + return _fgColorInfo; +} + +- (void)setFgColorInfo:(NSColor *)fgColorInfo { + _fgColorInfo = fgColorInfo; + [self setColor:fgColorInfo forKey:kKeyFgColorInfo]; +} + +- (NSColor *)fgColorVerbose { + if (!_fgColorVerbose) { + _fgColorVerbose = [self getColorForKey:kKeyFgColorVerbose]; + if (!_fgColorVerbose) { + _fgColorVerbose = LS_COLOR_RGB(0, 0, 255); + } + } + + return _fgColorVerbose; +} + +- (void)setFgColorVerbose:(NSColor *)fgColorVerbose { + _fgColorVerbose = fgColorVerbose; + [self setColor:fgColorVerbose forKey:kKeyFgColorVerbose]; +} + +//- (NSColor *)bgColorError { +// if (!_bgColorError) { +// _bgColorError = [self getColorForKey:kKeyBgColorError]; +// if (!_bgColorError) { +// _bgColorError = LS_COLOR_RGB(255, 255, 255); +// } +// } +// +// return _bgColorError; +//} +// +//- (void)setBgColorError:(NSColor *)bgColorError { +// _bgColorError = bgColorError; +// [self setColor:bgColorError forKey:kKeyBgColorError]; +//} +// +//- (NSColor *)bgColorWarn { +// if (!_bgColorWarn) { +// _bgColorWarn = [self getColorForKey:kKeyBgColorWarn]; +// if (!_bgColorWarn) { +// _bgColorWarn = LS_COLOR_RGB(255, 255, 255); +// } +// } +// +// return _bgColorWarn; +//} +// +//- (void)setBgColorWarn:(NSColor *)bgColorWarn { +// _bgColorWarn = bgColorWarn; +// [self setColor:bgColorWarn forKey:kKeyBgColorWarn]; +//} +// +//- (NSColor *)bgColorInfo { +// if (!_bgColorInfo) { +// _bgColorInfo = [self getColorForKey:kKeyBgColorInfo]; +// if (!_bgColorInfo) { +// _bgColorInfo = LS_COLOR_RGB(255, 255, 255); +// } +// } +// +// return _bgColorInfo; +//} +// +//- (void)setBgColorInfo:(NSColor *)bgColorInfo { +// _bgColorInfo = bgColorInfo; +// [self setColor:bgColorInfo forKey:kKeyBgColorInfo]; +//} +//- (NSColor *)bgColorVerbose { +// if (!_bgColorVerbose) { +// _bgColorVerbose = [self getColorForKey:kKeyBgColorVerbose]; +// if (!_bgColorVerbose) { +// _bgColorVerbose = LS_COLOR_RGB(255, 255, 255); +// } +// } +// +// return _bgColorVerbose; +//} +// +//- (void)setBgColorVerbose:(NSColor *)bgColorVerbose { +// _bgColorVerbose = bgColorVerbose; +// [self setColor:bgColorVerbose forKey:kKeyBgColorVerbose]; +//} + +#pragma mark - Private + +- (id)getConfigForKey:(NSString *)key +{ + return [[NSUserDefaults standardUserDefaults] valueForKey:key]; +} + +- (void)setConfig:(id)value forKey:(NSString *)key +{ + if (!value || !key) { + return; + } + [[NSUserDefaults standardUserDefaults] setObject:value forKey:key]; + [[NSUserDefaults standardUserDefaults] synchronize]; +} + +- (NSColor *)getColorForKey:(NSString *)key { + NSData *data = [self getConfigForKey:key]; + if (data) { + return (NSColor *)[NSUnarchiver unarchiveObjectWithData:data]; + } + return nil; +} + +- (void)setColor:(NSColor *)color forKey:(NSString *)key { + if (color && key) { + NSData *data = [NSArchiver archivedDataWithRootObject:color]; + [self setConfig:data forKey:key]; + } +} + +@end diff --git a/LSLog-XCode/Settings/LSLogSettingsWindowController.h b/LSLog-XCode/Settings/LSLogSettingsWindowController.h new file mode 100644 index 0000000..7b6041f --- /dev/null +++ b/LSLog-XCode/Settings/LSLogSettingsWindowController.h @@ -0,0 +1,13 @@ +// +// LSLogSettingsWindowController.h +// LSLog-XCode +// +// Created by lslin on 15/12/13. +// Copyright © 2015年 lessfun.com. All rights reserved. +// + +#import + +@interface LSLogSettingsWindowController : NSWindowController + +@end diff --git a/LSLog-XCode/Settings/LSLogSettingsWindowController.m b/LSLog-XCode/Settings/LSLogSettingsWindowController.m new file mode 100644 index 0000000..7de5dac --- /dev/null +++ b/LSLog-XCode/Settings/LSLogSettingsWindowController.m @@ -0,0 +1,97 @@ +// +// LSLogSettingsWindowController.m +// LSLog-XCode +// +// Created by lslin on 15/12/13. +// Copyright © 2015年 lessfun.com. All rights reserved. +// + +#import "LSLogSettingsWindowController.h" +#import "LSLogSettings.h" + +@interface LSLogSettingsWindowController () + +@property (weak) IBOutlet NSTextField *errorTextField; +@property (weak) IBOutlet NSTextField *warnTextField; +@property (weak) IBOutlet NSTextField *infoTextField; +@property (weak) IBOutlet NSTextField *verboseTextField; + +@property (weak) IBOutlet NSColorWell *fgErrorColorWell; +@property (weak) IBOutlet NSColorWell *fgWarnColorWell; +@property (weak) IBOutlet NSColorWell *fgInfoColorWell; +@property (weak) IBOutlet NSColorWell *fgVerboseColorWell; + +@property (weak) IBOutlet NSColorWell *bgErrorColorWell; +@property (weak) IBOutlet NSColorWell *bgWarnColorWell; +@property (weak) IBOutlet NSColorWell *bgInfoColorWell; +@property (weak) IBOutlet NSColorWell *bgVerboseColorWell; + +@end + +@implementation LSLogSettingsWindowController + +- (void)windowDidLoad { + [super windowDidLoad]; + + // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. + [self defaultInit]; +} + +#pragma mark - Action + +- (IBAction)onColorWellClicked:(NSColorWell *)sender { + if (sender == self.fgErrorColorWell) { + [[LSLogSettings defaultSettings] setFgColorError:sender.color]; + } else if (sender == self.fgWarnColorWell) { + [[LSLogSettings defaultSettings] setFgColorWarn:sender.color]; + } else if (sender == self.fgInfoColorWell) { + [[LSLogSettings defaultSettings] setFgColorInfo:sender.color]; + } else if (sender == self.fgVerboseColorWell) { + [[LSLogSettings defaultSettings] setFgColorVerbose:sender.color]; + } +// else if (sender == self.bgErrorColorWell) { +// [[LSLogSettings defaultSettings] setBgColorError:sender.color]; +// } else if (sender == self.bgWarnColorWell) { +// [[LSLogSettings defaultSettings] setBgColorWarn:sender.color]; +// } else if (sender == self.bgInfoColorWell) { +// [[LSLogSettings defaultSettings] setBgColorInfo:sender.color]; +// } else if (sender == self.bgVerboseColorWell) { +// [[LSLogSettings defaultSettings] setBgColorVerbose:sender.color]; +// } +} + +#pragma mark - Delegate + +- (void)controlTextDidChange:(NSNotification *)notification { + NSTextField *textField = [notification object]; + if(textField == self.errorTextField) { + [[LSLogSettings defaultSettings] setLogLevelPrefixError:textField.stringValue]; + } else if(textField == self.warnTextField) { + [[LSLogSettings defaultSettings] setLogLevelPrefixWarn:textField.stringValue]; + } else if(textField == self.verboseTextField) { + [[LSLogSettings defaultSettings] setLogLevelPrefixVerbose:textField.stringValue]; + } else if(textField == self.infoTextField) { + [[LSLogSettings defaultSettings] setLogLevelPrefixInfo:textField.stringValue]; + } +} + +#pragma mark - Private + +- (void)defaultInit { + self.errorTextField.stringValue = [LSLogSettings defaultSettings].logLevelPrefixError; + self.warnTextField.stringValue = [LSLogSettings defaultSettings].logLevelPrefixWarn; + self.infoTextField.stringValue = [LSLogSettings defaultSettings].logLevelPrefixInfo; + self.verboseTextField.stringValue = [LSLogSettings defaultSettings].logLevelPrefixVerbose; + + self.fgErrorColorWell.color = [LSLogSettings defaultSettings].fgColorError; + self.fgWarnColorWell.color = [LSLogSettings defaultSettings].fgColorWarn; + self.fgInfoColorWell.color = [LSLogSettings defaultSettings].fgColorInfo; + self.fgVerboseColorWell.color = [LSLogSettings defaultSettings].fgColorVerbose; + +// self.bgErrorColorWell.color = [LSLogSettings defaultSettings].bgColorError; +// self.bgWarnColorWell.color = [LSLogSettings defaultSettings].bgColorWarn; +// self.bgInfoColorWell.color = [LSLogSettings defaultSettings].bgColorInfo; +// self.bgVerboseColorWell.color = [LSLogSettings defaultSettings].bgColorVerbose; +} + +@end diff --git a/LSLog-XCode/Settings/LSLogSettingsWindowController.xib b/LSLog-XCode/Settings/LSLogSettingsWindowController.xib new file mode 100644 index 0000000..5fe368a --- /dev/null +++ b/LSLog-XCode/Settings/LSLogSettingsWindowController.xib @@ -0,0 +1,281 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/README.md b/README.md index a006865..6227207 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,40 @@ # LSLog-XCode -An XCode plugin to filter and colorize the XCode debugging console. +An XCode plugin to filter and colorize the XCode debugging console. It's natively supports XcodeColors. You can customize the log level prefix and log text color. + +## Example + +![LSLog-XCode](https://github.com/tinymind/LSLog-XCode/raw/master/LSLog-XCode.gif) + +## Features + +* Filter console log. +* Filter using regular expression. +* Natively supports XcodeColors. +* Customize the log level prefix. +* Default log level prefix and color: + * Error: ``, RGB(214, 57, 30) + * Warn: ``, RGB(204, 121, 32) + * Info: ``, RGB(32, 32, 32) + * Verbose: ``, RGB(0, 0, 255) + +## Notice + +* If you are not using XcodeColors, LSLog-XCode will colorize the log text. + +## Installation + +* Install via [Alcatraz](https://github.com/alcatraz/Alcatraz) (Coming soon...) +* Download this project, build & run, and **Restart XCode**. + +## Uninstall + +LSLog-XCode.xcplugin should be saved in `~/Library/Application Support/Developer/Shared/Xcode/Plug-ins`, you can uninstall it by removing `~/Library/Application Support/Developer/Shared/Xcode/Plug-ins/LSLog-XCode.xcplugin`. + +## Requirements + +XCode 4, 5, 6 & 7. + +## Thanks + +* [MCLog](https://github.com/yuhua-chen/MCLog) +* [XcodeColors](https://github.com/robbiehanson/XcodeColors)