diff --git a/QuickLocalization.xcodeproj/project.pbxproj b/QuickLocalization.xcodeproj/project.pbxproj index 83da123..44d04d9 100644 --- a/QuickLocalization.xcodeproj/project.pbxproj +++ b/QuickLocalization.xcodeproj/project.pbxproj @@ -7,9 +7,11 @@ objects = { /* Begin PBXBuildFile section */ - 0966970B1B5A20C200D29654 /* AMMenuGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 0966970A1B5A20C200D29654 /* AMMenuGenerator.m */; }; + 0966970B1B5A20C200D29654 /* QLMenuGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 0966970A1B5A20C200D29654 /* QLMenuGenerator.m */; }; 0966970D1B5A20C900D29654 /* MenuItemData.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0966970C1B5A20C900D29654 /* MenuItemData.plist */; }; - 096697101B5A214400D29654 /* AMConstString.m in Sources */ = {isa = PBXBuildFile; fileRef = 0966970F1B5A214400D29654 /* AMConstString.m */; }; + 096697101B5A214400D29654 /* QLConstString.m in Sources */ = {isa = PBXBuildFile; fileRef = 0966970F1B5A214400D29654 /* QLConstString.m */; }; + 098C633B1BFDB38E00A6398E /* QLIDEHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 098C633A1BFDB38E00A6398E /* QLIDEHelper.m */; }; + 098C633E1BFDB43100A6398E /* QLXcodeHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 098C633D1BFDB43100A6398E /* QLXcodeHelper.m */; }; 099474BA1B5A1F3000E39E3C /* OLSettingController.m in Sources */ = {isa = PBXBuildFile; fileRef = 099474B81B5A1F3000E39E3C /* OLSettingController.m */; }; 099474BB1B5A1F3000E39E3C /* OLSettingController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 099474B91B5A1F3000E39E3C /* OLSettingController.xib */; }; 09A343C6174068E600A713EE /* RCXcode.m in Sources */ = {isa = PBXBuildFile; fileRef = 09A343C5174068E200A713EE /* RCXcode.m */; }; @@ -21,11 +23,15 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 096697091B5A20C200D29654 /* AMMenuGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AMMenuGenerator.h; sourceTree = ""; }; - 0966970A1B5A20C200D29654 /* AMMenuGenerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AMMenuGenerator.m; sourceTree = ""; }; + 096697091B5A20C200D29654 /* QLMenuGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QLMenuGenerator.h; sourceTree = ""; }; + 0966970A1B5A20C200D29654 /* QLMenuGenerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QLMenuGenerator.m; sourceTree = ""; }; 0966970C1B5A20C900D29654 /* MenuItemData.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = MenuItemData.plist; sourceTree = ""; }; - 0966970E1B5A214400D29654 /* AMConstString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AMConstString.h; sourceTree = ""; }; - 0966970F1B5A214400D29654 /* AMConstString.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AMConstString.m; sourceTree = ""; }; + 0966970E1B5A214400D29654 /* QLConstString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QLConstString.h; sourceTree = ""; }; + 0966970F1B5A214400D29654 /* QLConstString.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QLConstString.m; sourceTree = ""; }; + 098C63391BFDB38E00A6398E /* QLIDEHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QLIDEHelper.h; sourceTree = ""; }; + 098C633A1BFDB38E00A6398E /* QLIDEHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QLIDEHelper.m; sourceTree = ""; }; + 098C633C1BFDB43100A6398E /* QLXcodeHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QLXcodeHelper.h; sourceTree = ""; }; + 098C633D1BFDB43100A6398E /* QLXcodeHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QLXcodeHelper.m; sourceTree = ""; }; 099474B71B5A1F3000E39E3C /* OLSettingController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OLSettingController.h; sourceTree = ""; }; 099474B81B5A1F3000E39E3C /* OLSettingController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OLSettingController.m; sourceTree = ""; }; 099474B91B5A1F3000E39E3C /* OLSettingController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = OLSettingController.xib; sourceTree = ""; }; @@ -89,10 +95,14 @@ 09A343C4174068E200A713EE /* RCXcode.h */, 09A343C5174068E200A713EE /* RCXcode.m */, 0966970C1B5A20C900D29654 /* MenuItemData.plist */, - 0966970E1B5A214400D29654 /* AMConstString.h */, - 0966970F1B5A214400D29654 /* AMConstString.m */, - 096697091B5A20C200D29654 /* AMMenuGenerator.h */, - 0966970A1B5A20C200D29654 /* AMMenuGenerator.m */, + 0966970E1B5A214400D29654 /* QLConstString.h */, + 0966970F1B5A214400D29654 /* QLConstString.m */, + 098C633C1BFDB43100A6398E /* QLXcodeHelper.h */, + 098C633D1BFDB43100A6398E /* QLXcodeHelper.m */, + 098C63391BFDB38E00A6398E /* QLIDEHelper.h */, + 098C633A1BFDB38E00A6398E /* QLIDEHelper.m */, + 096697091B5A20C200D29654 /* QLMenuGenerator.h */, + 0966970A1B5A20C200D29654 /* QLMenuGenerator.m */, 09F6563A174041010000486B /* Supporting Files */, 099474B71B5A1F3000E39E3C /* OLSettingController.h */, 099474B81B5A1F3000E39E3C /* OLSettingController.m */, @@ -178,9 +188,11 @@ buildActionMask = 2147483647; files = ( 09F65641174041010000486B /* QuickLocalization.m in Sources */, - 096697101B5A214400D29654 /* AMConstString.m in Sources */, + 096697101B5A214400D29654 /* QLConstString.m in Sources */, + 098C633B1BFDB38E00A6398E /* QLIDEHelper.m in Sources */, 099474BA1B5A1F3000E39E3C /* OLSettingController.m in Sources */, - 0966970B1B5A20C200D29654 /* AMMenuGenerator.m in Sources */, + 098C633E1BFDB43100A6398E /* QLXcodeHelper.m in Sources */, + 0966970B1B5A20C200D29654 /* QLMenuGenerator.m in Sources */, 09A343C6174068E600A713EE /* RCXcode.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/QuickLocalization/OLSettingController.m b/QuickLocalization/OLSettingController.m index c8e9424..6a5a076 100644 --- a/QuickLocalization/OLSettingController.m +++ b/QuickLocalization/OLSettingController.m @@ -44,20 +44,46 @@ NSUInteger QL_CountOccurentOfStringWithSubString(NSString *str, NSString *subStr [super windowDidLoad]; [self.comboBox selectItemAtIndex:0]; [self updatePreviewText]; -} - -- (IBAction)onSaveButton:(id)sender { + [[NSUserDefaults standardUserDefaults] registerDefaults:@{ + kQLFormatStringCommentValueKey: @"<# comment #>", + kQLFormatStringTableNameValueKey: @"nil", + kQLFormatStringBundleValueKey: @"nil", + kQLFormatStringValueValueKey: @"<# value #>", + kQLFormatStringCommentSameKey: @YES, + kQLFormatStringSwiftSyntax: @NO, + }]; + + self.commentTextField.stringValue = [[NSUserDefaults standardUserDefaults] objectForKey:kQLFormatStringCommentValueKey]; + self.tableNameTextField.stringValue = [[NSUserDefaults standardUserDefaults] objectForKey:kQLFormatStringTableNameValueKey]; + self.bunldeTextField.stringValue = [[NSUserDefaults standardUserDefaults] objectForKey:kQLFormatStringBundleValueKey]; + self.valueTextField.stringValue = [[NSUserDefaults standardUserDefaults] objectForKey:kQLFormatStringValueValueKey]; + if ([[NSUserDefaults standardUserDefaults] boolForKey:kQLFormatStringSwiftSyntax]) { + self.swiftLocalizationPreviewButton.state = NSOnState; + } + else { + self.swiftLocalizationPreviewButton.state = NSOffState; + } + + if ([[NSUserDefaults standardUserDefaults] boolForKey:kQLFormatStringCommentSameKey]) { + self.commentSameAsKeyCheckButton.state = NSOnState; + } + else { + self.commentSameAsKeyCheckButton.state = NSOffState; + } } - (IBAction)commentDidChange:(id)sender { + [[NSUserDefaults standardUserDefaults] setObject:self.commentTextField.stringValue forKey:kQLFormatStringCommentValueKey]; [self updatePreviewText]; } - (IBAction)onTableNameChanged:(id)sender { + [[NSUserDefaults standardUserDefaults] setObject:self.tableNameTextField.stringValue forKey:kQLFormatStringTableNameValueKey]; [self updatePreviewText]; } - (IBAction)onBunldeNameChanged:(id)sender { + [[NSUserDefaults standardUserDefaults] setObject:self.bunldeTextField.stringValue forKey:kQLFormatStringBundleValueKey]; [self updatePreviewText]; } @@ -72,6 +98,7 @@ NSUInteger QL_CountOccurentOfStringWithSubString(NSString *str, NSString *subStr } - (IBAction)onValueTextField:(id)sender { + [[NSUserDefaults standardUserDefaults] setObject:self.valueTextField.stringValue forKey:kQLFormatStringValueValueKey]; [self updatePreviewText]; } diff --git a/QuickLocalization/AMConstString.h b/QuickLocalization/QLConstString.h similarity index 70% rename from QuickLocalization/AMConstString.h rename to QuickLocalization/QLConstString.h index f9363f6..cb12b1b 100644 --- a/QuickLocalization/AMConstString.h +++ b/QuickLocalization/QLConstString.h @@ -9,13 +9,6 @@ #import -extern NSString * const kDeclareMap; -extern NSString * const kImplementMap; -extern NSString * const kImplementContent; - -extern NSString * const kSelectTextType; -extern NSString * const kSelectTextFirstSelectMethod; - extern NSString * const kMenuRootMenuTitle; extern NSString * const kMenuPluginTitle; extern NSString * const kMenuSubMenuItems; @@ -29,4 +22,10 @@ extern NSString * const kMenuActionTitle; extern NSString * const kQLFormatStringKey; extern NSString * const kQLFormatStringCommentSameKey; -extern NSString * const kQLFormatStringSwiftSyntax; \ No newline at end of file +extern NSString * const kQLFormatStringSwiftSyntax; + +extern NSString * const kQLFormatStringCommentValueKey; +extern NSString * const kQLFormatStringTableNameValueKey; +extern NSString * const kQLFormatStringBundleValueKey; +extern NSString * const kQLFormatStringValueValueKey; + diff --git a/QuickLocalization/AMConstString.m b/QuickLocalization/QLConstString.m similarity index 68% rename from QuickLocalization/AMConstString.m rename to QuickLocalization/QLConstString.m index 8a43e19..5e83681 100644 --- a/QuickLocalization/AMConstString.m +++ b/QuickLocalization/QLConstString.m @@ -6,16 +6,9 @@ // Copyright (c) 2015年 Tendencystudio. All rights reserved. // -#import "AMConstString.h" +#import "QLConstString.h" -NSString * const kDeclareMap = @"declareMap"; -NSString * const kImplementMap = @"implementMap"; -NSString * const kImplementContent = @"implementContent"; - -NSString * const kSelectTextType = @"type"; -NSString * const kSelectTextFirstSelectMethod = @"firstSelectMethod"; - NSString * const kMenuRootMenuTitle = @"rootMenuTitle"; NSString * const kMenuPluginTitle = @"pluginTitle"; NSString * const kMenuSubMenuItems = @"subMenuItems"; @@ -29,4 +22,11 @@ NSString * const kMenuActionTitle = @"Implement Mehod"; NSString * const kQLFormatStringKey = @"KQLFormatStringKey"; NSString * const kQLFormatStringCommentSameKey = @"KQLFormatStringKey"; -NSString * const kQLFormatStringSwiftSyntax = @"kQLFormatStringSwiftSyntax"; \ No newline at end of file +NSString * const kQLFormatStringSwiftSyntax = @"kQLFormatStringSwiftSyntax"; + + + +NSString * const kQLFormatStringCommentValueKey = @"kQLFormatStringCommentValueKey"; +NSString * const kQLFormatStringTableNameValueKey = @"kQLFormatStringTableNameValueKey"; +NSString * const kQLFormatStringBundleValueKey = @"kQLFormatStringBundleValueKey"; +NSString * const kQLFormatStringValueValueKey = @"kQLFormatStringValueValueKey"; \ No newline at end of file diff --git a/QuickLocalization/QLIDEHelper.h b/QuickLocalization/QLIDEHelper.h new file mode 100644 index 0000000..5df8e06 --- /dev/null +++ b/QuickLocalization/QLIDEHelper.h @@ -0,0 +1,132 @@ +// +// QLIDEHelper.h +// QLMethod2Implement +// +// Created by Long on 14-4-15. +// Copyright (c) 2014年 Tendencystudio. All rights reserved. +// + +#import + +typedef enum { + QLIDEFileTypeMFile = 0, + QLIDEFileTypeHFile +}QLIDEFileType; + +typedef enum { + QLImplementTypeMethod = 0, + QLImplementTypeConstString, + QLImplementTypeSelector, + QLImplementTypeInvocation, + QLImplementTypeGetter +}QLImplementType; + +@interface QLIDEHelper : NSObject + +/** + * Open file in Xcode editor. + * + * @param filePath file path. + * + * @return return YES if open file success. + */ ++ (BOOL)openFile:(NSString *)filePath; + +/** + * Get current edit file path. + * + * @return current edit file path + */ ++ (NSString *)getCurrentEditFilePath; + +/** + * Get .m file of current edit file. + * + * @return .m file path. + */ ++ (NSString *)getMFilePathOfCurrentEditFile; + +/** + * Get current class name by "Current Edit File Path" + * + * @return class name. + */ ++ (NSString *)getCurrentClassName; + +/** + * Check current opened file is .h file. + * + * @return return YES if current fils is .h file. + */ ++ (BOOL)isHeaderFile; + +/** + * Get .h file of current edit file. + * + * @return .h file path. + */ ++ (NSString *)getHFilePathOfCurrentEditFile; + +/** + * Highlight text and scroll to visible in current edit file. + * + * @param target text. + */ ++ (void)selectText:(NSString *)text; + +/** + * Search regex string and scroll to visible in current edit file. + * + * @param regex regex string. + * @param text highlight text. + */ ++ (void)selectTextWithRegex:(NSString *)regex highlightText:(NSString *)text; + +/** + * Replace text in current editor. + * + * @param text text to be replace. + * @param newText new text. + */ ++ (void)replaceText:(NSString *)text withNewText:(NSString *)newText; + +/** + * Get current selected range string. + * + * @return selected range string. + */ ++ (NSString *)getCurrentSelectMethod; + +/** + * Get all class name by @interface or @implementation of .h file or .m file of the current edit file. + * + * @param fileType QLIDEFileTypeMFile or QLIDEFileTypeHFile + * + * @return class name. + */ ++ (NSArray *)getCurrentClassNameByCurrentSelectedRangeWithFileType:(QLIDEFileType)fileType; + +/** + * Get target insert position for code generation. + * + * @param range content range: contentRange = [QLIDEHelper getClassImplementContentRangeWithClassNameItemList:currentClassName fileText:textView.textStorage.string fileType:QLIDEFileTypeMFile]; + * + * @return target insert range. + */ ++ (NSRange)getInsertRangeWithClassImplementContentRange:(NSRange)range; + +/** + * Get class @interface or @implementation code range(before @end) in .h or .m file. + * + * @param classNameItemList all class name by: NSArray *currentClassName = [QLIDEHelper getCurrentClassNameByCurrentSelectedRangeWithFileType:QLIDEFileTypeHFile]; + * @param fileText target string for match, should be .m (fileType should be QLIDEFileTypeMFile) or .h (fileType should be QLIDEFileTypeHFile) file content string. + * @param fileType QLIDEFileTypeMFile or QLIDEFileTypeHFile + * + * @return range from @interface/@implementation to @end, not include @end. + */ ++ (NSRange)getClassImplementContentRangeWithClassNameItemList:(NSArray *)classNameItemList + fileText:(NSString *)fileText + fileType:(QLIDEFileType)fileType; + +@end + diff --git a/QuickLocalization/QLIDEHelper.m b/QuickLocalization/QLIDEHelper.m new file mode 100644 index 0000000..152e8c4 --- /dev/null +++ b/QuickLocalization/QLIDEHelper.m @@ -0,0 +1,220 @@ +// +// QLIDEHelper.m +// QLMethod2Implement +// +// Created by Long on 14-4-15. +// Copyright (c) 2014年 Tendencystudio. All rights reserved. +// + +#import "QLIDEHelper.h" +#import "QLXcodeHelper.h" +#import + +@implementation QLIDEHelper + + ++ (BOOL)openFile:(NSString *)filePath +{ + NSWindowController *currentWindowController = [[NSApp mainWindow] windowController]; + NSLog(@"currentWindowController %@",[currentWindowController description]); + if ([currentWindowController isKindOfClass:NSClassFromString(@"IDEWorkspaceWindowController")]) { + NSLog(@"Open in current Xocde"); + id appDelegate = (id)[NSApp delegate]; + if ([appDelegate application:NSApp openFile:filePath]) { + return YES; + } + } + return NO; +} + ++ (NSString *)getCurrentEditFilePath +{ + IDESourceCodeDocument *currentSourceCodeDocument = [QLXcodeHelper currentSourceCodeDocument]; + NSString *filePath = [[currentSourceCodeDocument fileURL] path]; + return filePath; +} + ++ (BOOL)isHeaderFile +{ + NSString *filePath = [QLIDEHelper getCurrentEditFilePath]; + if ([filePath rangeOfString:@".h"].length > 0) { + return YES; + } + return NO; +} + ++ (NSString *)getHFilePathOfCurrentEditFile +{ + NSString *filePath = [QLIDEHelper getCurrentEditFilePath]; + filePath = [filePath stringByDeletingPathExtension]; + filePath = [filePath stringByAppendingPathExtension:@"h"]; + return filePath; +} + ++ (NSString *)getMFilePathOfCurrentEditFile +{ + NSString *filePath = [QLIDEHelper getCurrentEditFilePath]; + if ([filePath rangeOfString:@".h"].length > 0) { + NSString *mFilePath = [filePath stringByReplacingOccurrencesOfString:@".h" withString:@".m"]; + if ([[NSFileManager defaultManager] fileExistsAtPath:mFilePath]) { + return mFilePath; + } + + mFilePath = [filePath stringByReplacingOccurrencesOfString:@".h" withString:@".mm"]; + if ([[NSFileManager defaultManager] fileExistsAtPath:mFilePath]) { + return mFilePath; + } + + } + return filePath; +} + ++ (NSString *)getCurrentClassName +{ + NSString *fileName = [[QLIDEHelper getCurrentEditFilePath] lastPathComponent]; + return [fileName stringByDeletingPathExtension]; +} + ++ (void)selectText:(NSString *)text +{ + NSTextView *textView = [QLXcodeHelper currentSourceCodeTextView]; + NSRange textRange = [textView.textStorage.string rangeOfString:text options:NSCaseInsensitiveSearch]; + if (textRange.location != NSNotFound) + { + [textView setSelectedRange:textRange]; + [textView scrollRangeToVisible:textRange]; + } +} + ++ (void)selectTextWithRegex:(NSString *)regex highlightText:(NSString *)text +{ + NSTextView *textView = [QLXcodeHelper currentSourceCodeTextView]; + NSRegularExpression *regularExpression = [NSRegularExpression + regularExpressionWithPattern:regex + options:NSRegularExpressionAnchorsMatchLines + error:NULL]; + + NSRange range = [regularExpression rangeOfFirstMatchInString:textView.textStorage.string + options:0 + range:NSMakeRange(0, [textView.textStorage.string length])]; + if (range.location == NSNotFound) { + return; + } + + NSString *string = [textView.textStorage.string substringWithRange:range]; + NSLog(@"selectTextWithRegex: %@", string); + NSRange textRange = [string rangeOfString:text options:NSCaseInsensitiveSearch]; + if (textRange.location != NSNotFound) { + range = NSMakeRange(range.location+textRange.location, textRange.length); + } + [textView setSelectedRange:range]; + [textView scrollRangeToVisible:range]; +} + ++ (void)replaceText:(NSString *)text withNewText:(NSString *)newText +{ + NSTextView *textView = [QLXcodeHelper currentSourceCodeTextView]; + NSRange textRange = [textView.textStorage.string rangeOfString:text options:NSCaseInsensitiveSearch]; + [textView scrollRangeToVisible:textRange]; + [textView insertText:newText replacementRange:textRange]; +} + ++ (NSString *)getCurrentSelectMethod +{ + NSTextView *textView = [QLXcodeHelper currentSourceCodeTextView]; + NSArray* selectedRanges = [textView selectedRanges]; + if (selectedRanges.count >= 1) { + NSRange selectedRange = [[selectedRanges objectAtIndex:0] rangeValue]; + NSString *text = textView.textStorage.string; + NSRange lineRange = [text lineRangeForRange:selectedRange]; + NSString *line = [text substringWithRange:lineRange]; + return line; + } + return nil; +} + ++ (NSArray *)getCurrentClassNameByCurrentSelectedRangeWithFileType:(QLIDEFileType)fileType +{ + NSTextView *textView = [QLXcodeHelper currentSourceCodeTextView]; + NSArray* selectedRanges = [textView selectedRanges]; + if (selectedRanges.count >= 1) { + NSRange selectedRange = [[selectedRanges objectAtIndex:0] rangeValue]; + NSString *text = textView.textStorage.string; + NSRange lineRange = [text lineRangeForRange:selectedRange]; + NSString *regexString = nil; + if (fileType == QLIDEFileTypeHFile) { + regexString = @"(?<=@interface)\\s+(\\w+)\\s*\\(?(\\w*)\\)?"; + }else if (fileType == QLIDEFileTypeMFile) { + regexString = @"(?<=@implementation)\\s+(\\w+)\\s*\\(?(\\w*)\\)?"; + } + NSRegularExpression *regex = [NSRegularExpression + regularExpressionWithPattern:regexString + options:0 + error:NULL]; + NSArray *results = [regex matchesInString:textView.textStorage.string options:0 range:NSMakeRange(0, lineRange.location)]; + if (results.count > 0) { + NSTextCheckingResult *textCheckingResult = results[results.count - 1]; + NSRange classNameRange = textCheckingResult.range; + if (classNameRange.location != NSNotFound) { + NSMutableArray *array = [NSMutableArray array]; + for (int i = 0; i < textCheckingResult.numberOfRanges; i++) { + NSString *item = [text substringWithRange:[textCheckingResult rangeAtIndex:i]]; + if (item.length > 0) { + [array addObject:item]; + NSLog(@"%@", item); + } + } + return array; + } + } + } + return nil; +} + ++ (NSRange)getClassImplementContentRangeWithClassNameItemList:(NSArray *)classNameItemList fileText:(NSString *)fileText fileType:(QLIDEFileType)fileType +{ + if (classNameItemList.count > 1) { + NSString *normalImplementationFormatString = @"@implementation\\s+%@.+?(?=\\s{0,3000}@end)"; + if (fileType == QLIDEFileTypeHFile) { + normalImplementationFormatString = @"@interface\\s+%@.+?(?=\\s{0,3000}@end)"; + } + + NSString *regexPattern = [NSString stringWithFormat:normalImplementationFormatString, classNameItemList[1]]; + if (classNameItemList.count == 3) { + NSString *categoryImplementationFormatString = @"@implementation\\s+%@\\s+\\(%@\\).+?(?=\\s{0,3000}@end)"; + if (fileType == QLIDEFileTypeHFile) { + categoryImplementationFormatString = @"@interface\\s+%@\\s+\\(%@\\).+?(?=\\s{0,3000}@end)"; + } + regexPattern = [NSString stringWithFormat:categoryImplementationFormatString, classNameItemList[1], classNameItemList[2]]; + } + NSLog(@"#%@",regexPattern); + + NSRegularExpression *regex = [NSRegularExpression + regularExpressionWithPattern:regexPattern + options:NSRegularExpressionDotMatchesLineSeparators + error:NULL]; + + NSTextCheckingResult *textCheckingResult = [regex firstMatchInString:fileText + options:0 + range:NSMakeRange(0, fileText.length)]; + + // NSLog(@"#%@", [fileText substringWithRange:textCheckingResult.range]); + if (textCheckingResult.range.location != NSNotFound) { + return textCheckingResult.range; + } + + } + return NSMakeRange(NSNotFound, 0); +} + ++ (NSRange)getInsertRangeWithClassImplementContentRange:(NSRange)range +{ + if (range.location != NSNotFound) { + return NSMakeRange(range.location+range.length, 1); + } + + return NSMakeRange(NSNotFound, 0); +} + +@end + diff --git a/QuickLocalization/AMMenuGenerator.h b/QuickLocalization/QLMenuGenerator.h similarity index 90% rename from QuickLocalization/AMMenuGenerator.h rename to QuickLocalization/QLMenuGenerator.h index 7d892d0..c37d46f 100644 --- a/QuickLocalization/AMMenuGenerator.h +++ b/QuickLocalization/QLMenuGenerator.h @@ -8,7 +8,7 @@ #import -@interface AMMenuGenerator : NSObject +@interface QLMenuGenerator : NSObject + (void)generateMenuItems:(NSBundle *)bundle version:(NSString *)version target:(id)target; + (NSUInteger)getKeyEquivalentModifierMaskWithKey:(NSString *)key; diff --git a/QuickLocalization/AMMenuGenerator.m b/QuickLocalization/QLMenuGenerator.m similarity index 91% rename from QuickLocalization/AMMenuGenerator.m rename to QuickLocalization/QLMenuGenerator.m index 07324ed..a2080a9 100644 --- a/QuickLocalization/AMMenuGenerator.m +++ b/QuickLocalization/QLMenuGenerator.m @@ -6,10 +6,10 @@ // Copyright (c) 2015年 Tendencystudio. All rights reserved. // -#import "AMMenuGenerator.h" +#import "QLMenuGenerator.h" #import "RCXcode.h" -@implementation AMMenuGenerator +@implementation QLMenuGenerator + (void)generateMenuItems:(NSBundle *)bundle version:(NSString *)version target:(id)target { @@ -51,10 +51,10 @@ NSMenuItem *subMenuItem = [[NSMenuItem alloc] initWithTitle:subMenuTitle action:selector keyEquivalent:keyEquivalent]; if (maskArray.count == 1) { - subMenuItem.keyEquivalentModifierMask = [AMMenuGenerator getKeyEquivalentModifierMaskWithKey:maskArray[0]]; + subMenuItem.keyEquivalentModifierMask = [QLMenuGenerator getKeyEquivalentModifierMaskWithKey:maskArray[0]]; }else if(maskArray.count == 2) { - subMenuItem.keyEquivalentModifierMask = [AMMenuGenerator getKeyEquivalentModifierMaskWithKey:maskArray[0]] | - [AMMenuGenerator getKeyEquivalentModifierMaskWithKey:maskArray[1]]; + subMenuItem.keyEquivalentModifierMask = [QLMenuGenerator getKeyEquivalentModifierMaskWithKey:maskArray[0]] | + [QLMenuGenerator getKeyEquivalentModifierMaskWithKey:maskArray[1]]; } subMenuItem.target = target; [submenu addItem:subMenuItem]; diff --git a/QuickLocalization/QLXcodeHelper.h b/QuickLocalization/QLXcodeHelper.h new file mode 100644 index 0000000..4d94ef0 --- /dev/null +++ b/QuickLocalization/QLXcodeHelper.h @@ -0,0 +1,125 @@ +// +// QLXcodeHelper.h +// +// +// Created by Mellong on 14-4-13. +// Copyright (c) 2014年 Tendencystudio. All rights reserved. +// + +#import + +@interface DVTTextDocumentLocation : NSObject +@property (readonly) NSRange characterRange; +@property (readonly) NSRange lineRange; +@end + +@interface DVTTextPreferences : NSObject ++ (id)preferences; +@property BOOL trimWhitespaceOnlyLines; +@property BOOL trimTrailingWhitespace; +@property BOOL useSyntaxAwareIndenting; +@end + +@interface DVTSourceTextStorage : NSTextStorage +- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)string withUndoManager:(id)undoManager; +- (NSRange)lineRangeForCharacterRange:(NSRange)range; +- (NSRange)characterRangeForLineRange:(NSRange)range; +- (void)indentCharacterRange:(NSRange)range undoManager:(id)undoManager; +@end + +@interface DVTFileDataType : NSObject +@property (readonly) NSString *identifier; +@end + +@interface DVTFilePath : NSObject +@property (readonly) NSURL *fileURL; +@property (readonly) DVTFileDataType *fileDataTypePresumed; +@end + +@interface IDEContainerItem : NSObject +@property (readonly) DVTFilePath *resolvedFilePath; +@end + +@interface IDEGroup : IDEContainerItem + +@end + +@interface IDEFileReference : IDEContainerItem + +@end + +@interface IDENavigableItem : NSObject +@property (readonly) IDENavigableItem *parentItem; +@property (readonly) id representedObject; +@end + +@interface IDEFileNavigableItem : IDENavigableItem +@property (readonly) DVTFileDataType *documentType; +@property (readonly) NSURL *fileURL; +@end + +@interface IDEStructureNavigator : NSObject +@property (retain) NSArray *selectedObjects; +@end + +@interface IDENavigableItemCoordinator : NSObject +- (id)structureNavigableItemForDocumentURL:(id)arg1 inWorkspace:(id)arg2 error:(id *)arg3; +@end + +@interface IDENavigatorArea : NSObject +- (id)currentNavigator; +@end + +@interface IDEWorkspaceTabController : NSObject +@property (readonly) IDENavigatorArea *navigatorArea; +@end + +@interface IDEDocumentController : NSDocumentController ++ (id)editorDocumentForNavigableItem:(id)arg1; ++ (id)retainedEditorDocumentForNavigableItem:(id)arg1 error:(id *)arg2; ++ (void)releaseEditorDocument:(id)arg1; +@end + +@interface IDESourceCodeDocument : NSDocument +- (DVTSourceTextStorage *)textStorage; +- (NSUndoManager *)undoManager; +@end + +@interface IDESourceCodeComparisonEditor : NSObject +@property (readonly) NSTextView *keyTextView; +@property (retain) NSDocument *primaryDocument; +@end + +@interface IDESourceCodeEditor : NSObject +@property (retain) NSTextView *textView; +- (IDESourceCodeDocument *)sourceCodeDocument; +@end + +@interface IDEEditorContext : NSObject +- (id)editor; // returns the current editor. If the editor is the code editor, the class is `IDESourceCodeEditor` +@end + +@interface IDEEditorArea : NSObject +- (IDEEditorContext *)lastActiveEditorContext; +@end + +@interface IDEWorkspaceWindowController : NSObject +@property (readonly) IDEWorkspaceTabController *activeWorkspaceTabController; +- (IDEEditorArea *)editorArea; +@end + +@interface IDEWorkspace : NSObject +@property (readonly) DVTFilePath *representingFilePath; +@end + +@interface IDEWorkspaceDocument : NSDocument +@property (readonly) IDEWorkspace *workspace; +@end + +@interface QLXcodeHelper : NSObject ++ (IDEWorkspaceDocument *)currentWorkspaceDocument; ++ (IDESourceCodeDocument *)currentSourceCodeDocument; ++ (NSTextView *)currentSourceCodeTextView; ++ (NSArray *)selectedObjCFileNavigableItems; +@end + diff --git a/QuickLocalization/QLXcodeHelper.m b/QuickLocalization/QLXcodeHelper.m new file mode 100644 index 0000000..ebcd4c2 --- /dev/null +++ b/QuickLocalization/QLXcodeHelper.m @@ -0,0 +1,123 @@ +// +// QLXcodeHelper.m +// +// +// Created by Mellong on 14-4-13. +// Copyright (c) 2014年 Tendencystudio. All rights reserved. +// + +#import "QLXcodeHelper.h" + +@implementation QLXcodeHelper + +#pragma mark - Helpers + ++ (id)currentEditor { + NSWindowController *currentWindowController = [[NSApp keyWindow] windowController]; + if ([currentWindowController isKindOfClass:NSClassFromString(@"IDEWorkspaceWindowController")]) { + IDEWorkspaceWindowController *workspaceController = (IDEWorkspaceWindowController *)currentWindowController; + IDEEditorArea *editorArea = [workspaceController editorArea]; + IDEEditorContext *editorContext = [editorArea lastActiveEditorContext]; + return [editorContext editor]; + } + return nil; +} + ++ (IDEWorkspaceDocument *)currentWorkspaceDocument { + NSWindowController *currentWindowController = [[NSApp keyWindow] windowController]; + id document = [currentWindowController document]; + if (currentWindowController && [document isKindOfClass:NSClassFromString(@"IDEWorkspaceDocument")]) { + return (IDEWorkspaceDocument *)document; + } + return nil; +} + ++ (IDESourceCodeDocument *)currentSourceCodeDocument { + if ([[QLXcodeHelper currentEditor] isKindOfClass:NSClassFromString(@"IDESourceCodeEditor")]) { + IDESourceCodeEditor *editor = [QLXcodeHelper currentEditor]; + return editor.sourceCodeDocument; + } + + if ([[QLXcodeHelper currentEditor] isKindOfClass:NSClassFromString(@"IDESourceCodeComparisonEditor")]) { + IDESourceCodeComparisonEditor *editor = [QLXcodeHelper currentEditor]; + if ([[editor primaryDocument] isKindOfClass:NSClassFromString(@"IDESourceCodeDocument")]) { + IDESourceCodeDocument *document = (IDESourceCodeDocument *)editor.primaryDocument; + return document; + } + } + + return nil; +} + ++ (NSTextView *)currentSourceCodeTextView { + if ([[QLXcodeHelper currentEditor] isKindOfClass:NSClassFromString(@"IDESourceCodeEditor")]) { + IDESourceCodeEditor *editor = [QLXcodeHelper currentEditor]; + return editor.textView; + } + + if ([[QLXcodeHelper currentEditor] isKindOfClass:NSClassFromString(@"IDESourceCodeComparisonEditor")]) { + IDESourceCodeComparisonEditor *editor = [QLXcodeHelper currentEditor]; + return editor.keyTextView; + } + + return nil; +} + ++ (NSArray *)selectedObjCFileNavigableItems { + NSMutableArray *mutableArray = [NSMutableArray array]; + + id currentWindowController = [[NSApp keyWindow] windowController]; + if ([currentWindowController isKindOfClass:NSClassFromString(@"IDEWorkspaceWindowController")]) { + IDEWorkspaceWindowController *workspaceController = currentWindowController; + IDEWorkspaceTabController *workspaceTabController = [workspaceController activeWorkspaceTabController]; + IDENavigatorArea *navigatorArea = [workspaceTabController navigatorArea]; + id currentNavigator = [navigatorArea currentNavigator]; + + if ([currentNavigator isKindOfClass:NSClassFromString(@"IDEStructureNavigator")]) { + IDEStructureNavigator *structureNavigator = currentNavigator; + for (id selectedObject in structureNavigator.selectedObjects) { + if ([selectedObject isKindOfClass:NSClassFromString(@"IDEFileNavigableItem")]) { + IDEFileNavigableItem *fileNavigableItem = selectedObject; + NSString *uti = fileNavigableItem.documentType.identifier; + if ([uti isEqualToString:(NSString *)kUTTypeObjectiveCSource] || [uti isEqualToString:(NSString *)kUTTypeCHeader]) { + [mutableArray addObject:fileNavigableItem]; + } + } + } + } + } + + if (mutableArray.count) { + return [NSArray arrayWithArray:mutableArray]; + } + return nil; +} + ++ (NSArray *)containerFolderURLsForNavigableItem:(IDENavigableItem *)navigableItem { + NSMutableArray *mArray = [NSMutableArray array]; + + do { + NSURL *folderURL = nil; + id representedObject = navigableItem.representedObject; + if ([navigableItem isKindOfClass:NSClassFromString(@"IDEGroupNavigableItem")]) { + // IDE-GROUP (a folder in the navigator) + IDEGroup *group = (IDEGroup *)representedObject; + folderURL = group.resolvedFilePath.fileURL; + } else if ([navigableItem isKindOfClass:NSClassFromString(@"IDEContainerFileReferenceNavigableItem")]) { + // CONTAINER (an Xcode project) + IDEFileReference *fileReference = representedObject; + folderURL = [fileReference.resolvedFilePath.fileURL URLByDeletingLastPathComponent]; + } else if ([navigableItem isKindOfClass:NSClassFromString(@"IDEKeyDrivenNavigableItem")]) { + // WORKSPACE (root: Xcode project or workspace) + IDEWorkspace *workspace = representedObject; + folderURL = [workspace.representingFilePath.fileURL URLByDeletingLastPathComponent]; + } + if (folderURL && ![mArray containsObject:folderURL]) [mArray addObject:folderURL]; + navigableItem = [navigableItem parentItem]; + } while (navigableItem != nil); + + if (mArray.count > 0) return [NSArray arrayWithArray:mArray]; + return nil; +} + +@end diff --git a/QuickLocalization/QuickLocalization-Prefix.pch b/QuickLocalization/QuickLocalization-Prefix.pch index 267613b..1ee8f9b 100644 --- a/QuickLocalization/QuickLocalization-Prefix.pch +++ b/QuickLocalization/QuickLocalization-Prefix.pch @@ -4,5 +4,5 @@ #ifdef __OBJC__ #import - #import "AMConstString.h" + #import "QLConstString.h" #endif diff --git a/QuickLocalization/QuickLocalization.m b/QuickLocalization/QuickLocalization.m index 22f4fe3..8ac8c6a 100644 --- a/QuickLocalization/QuickLocalization.m +++ b/QuickLocalization/QuickLocalization.m @@ -9,7 +9,8 @@ #import "QuickLocalization.h" #import "RCXcode.h" #import "OLSettingController.h" -#import "AMMenuGenerator.h" +#import "QLMenuGenerator.h" +#import "QLIDEHelper.h" static NSString *localizeRegexs[] = { @"NSLocalizedString\\s*\\(\\s*@\"(.*)\"\\s*,\\s*(.*)\\s*\\)", @@ -74,42 +75,7 @@ static id sharedPlugin = nil; } - (void)createMenuItem { - [AMMenuGenerator generateMenuItems:self.bundle version:[self getBundleVersion] target:self]; - // return; - // [[NSOperationQueue mainQueue] addOperationWithBlock:^{ - // NSMenuItem *viewMenuItem = [[NSApp mainMenu] itemWithTitle:@"Edit"]; - // if (viewMenuItem) { - // [[viewMenuItem submenu] addItem:[NSMenuItem separatorItem]]; - // - // NSMenuItem *localization = [[NSMenuItem alloc] initWithTitle:@"Quick Localization" action:@selector(quickLocalization:) keyEquivalent:@"d"]; - // [localization setKeyEquivalentModifierMask:NSShiftKeyMask | NSAlternateKeyMask]; - // [localization setTarget:self]; - // [localization setTag:0]; - // - // NSMenuItem *shouldUseStringFromTableInBundleToggle = [[NSMenuItem alloc] initWithTitle:@"NSLocalizedStringFromTableInBundle" action:@selector(toggleStringFromTableInBundle) keyEquivalent:@""]; - // [shouldUseStringFromTableInBundleToggle setTarget:self]; - // - // NSMenuItem *nilToggle = [[NSMenuItem alloc] initWithTitle:@"Use nil for NSLocalizedString comment" action:@selector(toggleNilOption) keyEquivalent:@""]; - // [nilToggle setTarget:self]; - // - // NSMenuItem *snippetToggle = [[NSMenuItem alloc] initWithTitle:@"Use <# comments #> for NSLocalizedString comment" action:@selector(toggleSnippetOption) keyEquivalent:@""]; - // [snippetToggle setTarget:self]; - // - // NSMenuItem *swiftSyntax = [[NSMenuItem alloc] initWithTitle:@"Swift Localization" action:@selector(toggleSwiftOption) keyEquivalent:@""]; - // [swiftSyntax setTarget:self]; - // - // NSMenu *groupMenu = [[NSMenu alloc] initWithTitle:@"Quick Localization"]; - // [groupMenu addItem:localization]; - // [groupMenu addItem:nilToggle]; - // [groupMenu addItem:snippetToggle]; - // [groupMenu addItem:swiftSyntax]; - // [groupMenu addItem:shouldUseStringFromTableInBundleToggle]; - // - // NSMenuItem *groupMenuItem = [[NSMenuItem alloc] initWithTitle:@"Quick Localization" action:NULL keyEquivalent:@""]; - // [[viewMenuItem submenu] addItem:groupMenuItem]; - // [[viewMenuItem submenu] setSubmenu:groupMenu forItem:groupMenuItem]; - // } - // }]; + [QLMenuGenerator generateMenuItems:self.bundle version:[self getBundleVersion] target:self]; } // Sample Action, for menu item: @@ -120,59 +86,71 @@ static id sharedPlugin = nil; return; } - // NSLog(@"file: %@", [RCXcode currentWorkspaceDocument].workspace.representingFilePath.fileURL.absoluteString); NSArray *selectedRanges = [textView selectedRanges]; if ([selectedRanges count] > 0) { NSRange range = [[selectedRanges objectAtIndex:0] rangeValue]; - NSRange lineRange = [textView.textStorage.string lineRangeForRange:range]; - NSString *line = [textView.textStorage.string substringWithRange:lineRange]; - - - - NSRegularExpression *localizedRex = [[NSRegularExpression alloc] initWithPattern:localizeRegexs[0] options:NSRegularExpressionCaseInsensitive error:nil]; - NSArray *localizedMatches = [localizedRex matchesInString:line options:0 range:NSMakeRange(0, [line length])]; - - NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:[self currentStringRegexs] options:NSRegularExpressionCaseInsensitive error:nil]; - NSArray *matches = [regex matchesInString:line options:0 range:NSMakeRange(0, [line length])]; - NSUInteger addedLength = 0; - for (int i = 0; i < [matches count]; i++) { - NSTextCheckingResult *result = [matches objectAtIndex:i]; - NSRange matchedRangeInLine = result.range; - NSRange matchedRangeInDocument = NSMakeRange(lineRange.location + matchedRangeInLine.location + addedLength, matchedRangeInLine.length); - if ([self isRange:matchedRangeInLine inSkipedRanges:localizedMatches]) { - continue; - } - NSString *string = [line substringWithRange:matchedRangeInLine]; - // NSLog(@"string index:%d, %@", i, string); - NSString *outputString; - - NSString *savedFormatString = [[NSUserDefaults standardUserDefaults] objectForKey:kQLFormatStringKey]; - - NSUInteger placeHolderCount = QL_CountOccurentOfStringWithSubString(savedFormatString, @"%@"); - if (placeHolderCount == 2) { - outputString = [NSString stringWithFormat:savedFormatString, string, string]; - } - else if (placeHolderCount == 1) { - outputString = [NSString stringWithFormat:savedFormatString, string]; - } - else { - outputString = [NSString stringWithFormat:@"placeholder incorrect, it is %@", savedFormatString]; - } - - addedLength = addedLength + outputString.length - string.length; - if ([textView shouldChangeTextInRange:matchedRangeInDocument replacementString:outputString]) { - [textView.textStorage replaceCharactersInRange:matchedRangeInDocument - withAttributedString:[[NSAttributedString alloc] initWithString:outputString]]; - [textView didChangeText]; - } - - // [textView replaceCharactersInRange:matchedRangeInDocument withString:outputString]; - // NSAlert *alert = [NSAlert alertWithMessageText:outputString defaultButton:nil alternateButton:nil otherButton:nil informativeTextWithFormat:@""]; - // [alert runModal]; + BOOL replaced = [self replaceTextInRange:range textView:textView]; + //if user selected text is not being replaced, try using line replacing + if (!replaced) { + NSRange lineRange = [textView.textStorage.string lineRangeForRange:range]; + [self replaceTextInRange:lineRange textView:textView]; } } } +- (BOOL)replaceTextInRange:(NSRange)range textView:(NSTextView *)textView { + NSString *line = [textView.textStorage.string substringWithRange:range]; + + NSRegularExpression *localizedRex = [[NSRegularExpression alloc] initWithPattern:localizeRegexs[0] options:NSRegularExpressionCaseInsensitive error:nil]; + NSArray *localizedMatches = [localizedRex matchesInString:line options:0 range:NSMakeRange(0, [line length])]; + + NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:[self currentStringRegexs] options:NSRegularExpressionCaseInsensitive error:nil]; + NSArray *matches = [regex matchesInString:line options:0 range:NSMakeRange(0, [line length])]; + NSUInteger addedLength = 0; + for (int i = 0; i < [matches count]; i++) { + NSTextCheckingResult *result = [matches objectAtIndex:i]; + NSRange matchedRangeInLine = result.range; + NSRange matchedRangeInDocument = NSMakeRange(range.location + matchedRangeInLine.location + addedLength, matchedRangeInLine.length); + if ([self isRange:matchedRangeInLine inSkipedRanges:localizedMatches]) { + continue; + } + NSString *string = [line substringWithRange:matchedRangeInLine]; + // NSLog(@"string index:%d, %@", i, string); + NSString *outputString; + + NSString *savedFormatString = [[NSUserDefaults standardUserDefaults] objectForKey:kQLFormatStringKey]; + + NSUInteger placeHolderCount = QL_CountOccurentOfStringWithSubString(savedFormatString, @"%@"); + if (placeHolderCount == 2) { + outputString = [NSString stringWithFormat:savedFormatString, string, string]; + } + else if (placeHolderCount == 1) { + outputString = [NSString stringWithFormat:savedFormatString, string]; + } + else { + outputString = [NSString stringWithFormat:@"placeholder incorrect, format string is %@", savedFormatString]; + } + + addedLength = addedLength + outputString.length - string.length; + if ([textView shouldChangeTextInRange:matchedRangeInDocument replacementString:outputString]) { + [textView.textStorage replaceCharactersInRange:matchedRangeInDocument + withAttributedString:[[NSAttributedString alloc] initWithString:outputString]]; + [textView didChangeText]; + } + + // [textView replaceCharactersInRange:matchedRangeInDocument withString:outputString]; + // NSAlert *alert = [NSAlert alertWithMessageText:outputString defaultButton:nil alternateButton:nil otherButton:nil informativeTextWithFormat:@""]; + // [alert runModal]; + } + + if (matches.count) { + return YES; + } + else { + return NO; + } +} + - (NSString *)currentStringRegexs { BOOL isSwiftSyntax = [[NSUserDefaults standardUserDefaults] boolForKey:kQLFormatStringSwiftSyntax]; if (isSwiftSyntax) {