Repository: samdods/Stencil
Branch: master
Commit: 49edc399ccd8
Files: 72
Total size: 153.1 KB
Directory structure:
gitextract_g8y3zt8q/
├── .gitignore
├── LICENSE
├── README.md
├── StencilPlugin/
│ ├── Additions/
│ │ ├── IDEStructureNavigatorAdditions.m
│ │ ├── IDETemplateAdditions.m
│ │ ├── NSMenu+StencilAdditions.h
│ │ ├── NSMenu+StencilAdditions.m
│ │ ├── NSObject+StencilAdditions.h
│ │ ├── NSObject+StencilAdditions.m
│ │ └── Parsing/
│ │ ├── NSInputStream+StencilAdditions.h
│ │ ├── NSInputStream+StencilAdditions.m
│ │ ├── NSOutputStream+StencilAdditions.h
│ │ ├── NSOutputStream+StencilAdditions.m
│ │ ├── NSString+StencilRegex.h
│ │ ├── NSString+StencilRegex.m
│ │ ├── TemplateFactory.h
│ │ └── TemplateFactory.m
│ ├── Helpers/
│ │ ├── DeallocationObserver.h
│ │ ├── DeallocationObserver.m
│ │ ├── StencilWeakObjectWrapper.h
│ │ └── StencilWeakObjectWrapper.m
│ ├── Info.plist
│ ├── Model/
│ │ ├── TemplateConfig.h
│ │ ├── TemplateConfig.m
│ │ ├── ThingTypeToClassNamesMap.h
│ │ └── ThingTypeToClassNamesMap.m
│ ├── Resources/
│ │ ├── Built-in Templates/
│ │ │ └── Stencil/
│ │ │ ├── NSView.xctemplate/
│ │ │ │ ├── NSViewObjective-C/
│ │ │ │ │ ├── ___FILEBASENAME___.h
│ │ │ │ │ └── ___FILEBASENAME___.m
│ │ │ │ ├── NSViewSwift/
│ │ │ │ │ └── ___FILEBASENAME___.swift
│ │ │ │ ├── NSViewXIBObjective-C/
│ │ │ │ │ ├── ___FILEBASENAME___.h
│ │ │ │ │ ├── ___FILEBASENAME___.m
│ │ │ │ │ └── ___FILEBASENAME___.xib
│ │ │ │ ├── NSViewXIBSwift/
│ │ │ │ │ ├── ___FILEBASENAME___.swift
│ │ │ │ │ └── ___FILEBASENAME___.xib
│ │ │ │ └── TemplateInfo.plist
│ │ │ └── UIView.xctemplate/
│ │ │ ├── TemplateInfo.plist
│ │ │ ├── UIViewObjective-C/
│ │ │ │ ├── ___FILEBASENAME___.h
│ │ │ │ └── ___FILEBASENAME___.m
│ │ │ ├── UIViewSwift/
│ │ │ │ └── ___FILEBASENAME___.swift
│ │ │ ├── UIViewXIBiPadObjective-C/
│ │ │ │ ├── ___FILEBASENAME___.h
│ │ │ │ ├── ___FILEBASENAME___.m
│ │ │ │ └── ___FILEBASENAME___.xib
│ │ │ ├── UIViewXIBiPadSwift/
│ │ │ │ ├── ___FILEBASENAME___.swift
│ │ │ │ └── ___FILEBASENAME___.xib
│ │ │ ├── UIViewXIBiPhoneObjective-C/
│ │ │ │ ├── ___FILEBASENAME___.h
│ │ │ │ ├── ___FILEBASENAME___.m
│ │ │ │ └── ___FILEBASENAME___.xib
│ │ │ └── UIViewXIBiPhoneSwift/
│ │ │ ├── ___FILEBASENAME___.swift
│ │ │ └── ___FILEBASENAME___.xib
│ │ ├── HeaderComments.sctemplate
│ │ ├── StencilREADME
│ │ └── TemplateInfo.plist
│ ├── SDK Fakes/
│ │ ├── ProjectFile.h
│ │ ├── ProjectFile.m
│ │ ├── ProjectGroup.h
│ │ └── ProjectGroup.m
│ ├── Stencil.h
│ ├── Stencil.m
│ ├── User Interface/
│ │ └── Template Options/
│ │ ├── StencilTemplateWindow.xib
│ │ ├── TemplateOptionsWindow.h
│ │ └── TemplateOptionsWindow.m
│ └── Vendor/
│ └── DZLObjcAdditions/
│ ├── DZLClassSingleton.h
│ ├── DZLImplementationCombine.h
│ ├── DZLImplementationSafe.h
│ ├── DZLProtocolImplementation.h
│ ├── DZLSynthesizeLazy.h
│ └── Private/
│ ├── DZLSuper.h
│ ├── NSObject+DZLObjcAdditions.h
│ └── NSObject+DZLObjcAdditions.m
└── StencilPlugin.xcodeproj/
├── project.pbxproj
├── project.xcworkspace/
│ └── contents.xcworkspacedata
└── xcshareddata/
└── xcschemes/
└── StencilPlugin.xcscheme
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
build
target
.idea/
*.xcodeproj/*.pbxuser
*.xcodeproj/*.perspectivev3
*.xcodeproj/*.mode1v3
*.xcodeproj/project.xcworkspace/xcuserdata
*.xcodeproj/xcuserdata
.DS_Store
Build.plist
*.xcsettings
*.xcuserstate
*.xcworkspace/xcuserdata/
*.xccheckout
Pods/*
*.orig
*.moved-aside
*.swp
DerivedData/
*.mode2v3
*.mode1v3
*.perspectivev3
Podfile.lock
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2015 Sam Dods
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# Stencil
A plugin for Xcode allowing you to easily create custom file templates
## Installing
The simplest way to install is through [Alcatraz Package Manager](http://alcatraz.io/) for Xcode.
Alternatively, you can clone this repository, open the Xcode project, build the project, then restart Xcode
for the plugin to take effect.
## Website
See the [Stencil website](http://sam.dods.co/stencil-xcode-plugin/) for more details.
## Blog
See [this blog post](http://sam.dods.co/blog/2015/05/02/stencil-xcode-plugin/) for details of how to create your
own custom file template and how to use it to create new files.
## Twitter
If you have questions or concerns, or just want to say hello, please speak to me on Twitter!
================================================
FILE: StencilPlugin/Additions/IDEStructureNavigatorAdditions.m
================================================
//
// IDEStructureNavigatorAdditions.m
// StencilPlugin
//
// Created by Sam Dods on 18/04/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import
#import "DZLImplementationCombine.h"
#import "Stencil.h"
#import "ProjectGroup.h"
#import "ProjectFile.h"
#import "TemplateFactory.h"
#import "TemplateConfig.h"
@interface NSObject (IDEAdditions)
- (char)_testOrDeleteItems:(char)items useContextualMenuSelection:(char)selection;
@end
@interface IDEStructureNavigator_Additions : NSObject
@end
@implementation IDEStructureNavigator_Additions
+ (void)load
{
dzl_implementationCombine(NSClassFromString(@"IDEStructureNavigator"), self, dzl_no_assert);
}
- (NSArray *)dzl_selectedFiles
{
NSArray *files = [self valueForKey:@"selectedObjects"];
if (![files isKindOfClass:[NSArray class]]) {
return nil;
}
for (id file in files) {
if (![file isKindOfClass:NSClassFromString(@"IDEFileReferenceNavigableItem")]) {
return nil;
}
}
return files;
}
- (char)_testOrDeleteItems:(char)items useContextualMenuSelection:(char)selection
{
if (![Stencil sharedPlugin].beginCreateTemplateFromGroup) {
return dzlSuper(_testOrDeleteItems:items useContextualMenuSelection:selection);
}
NSArray *files = [self dzl_selectedFiles];
if (!files) {
[[TemplateFactory defaultFactory] showAlertWithMessage:@"You cannot create a template from the current selection. Please ensure you have selected source files only."];
return 0;
}
[Stencil sharedPlugin].beginCreateTemplateFromGroup = NO;
NSError *error = nil;
TemplateConfig *config = [TemplateConfig defaultConfigForFiles:files error:&error];
if (error) {
[[TemplateFactory defaultFactory] showAlertForError:error];
return 0;
}
if (!config.thingTypeToNamesMaps.count) {
[[TemplateFactory defaultFactory] showAlertWithMessage:@"Unsupported file type."];
return 0;
}
[[Stencil sharedPlugin] showTemplateOptionsInWindow:[NSApp mainWindow] defaultTemplateConfig:config];
return 0;
}
@end
================================================
FILE: StencilPlugin/Additions/IDETemplateAdditions.m
================================================
//
// IDETemplateAdditions.m
// StencilPlugin
//
// Created by Sam Dods on 18/04/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import
#import "DZLImplementationCombine.h"
#import "Stencil.h"
#import
@interface NSObject ()
+ (id)availableTemplatesOfTemplateKind:(id)kind;
+ (void)_processChildrenOfFilePath:(id)path enumerator:(id)enumerator;
@end
// this is required for the method +[IDETemplate availableTemplatesOfTemplateKind:]
// see http://petersteinberger.com/blog/2014/a-story-about-swizzling-the-right-way-and-touch-forwarding/
static IMP PSPDFReplaceMethodWithBlock(Class c, SEL origSEL, id block) {
NSCParameterAssert(block);
// get original method
Method origMethod = class_getClassMethod(c, origSEL);
if (!origMethod) {
return nil;
}
// convert block to IMP trampoline and replace method implementation
IMP newIMP = imp_implementationWithBlock(block);
// Try adding the method if not yet in the current class
if (!class_addMethod(object_getClass(c), origSEL, newIMP, method_getTypeEncoding(origMethod))) {
return method_setImplementation(origMethod, newIMP);
}else {
return method_getImplementation(origMethod);
}
}
@interface IDETemplateAdditions_Additions : NSObject
@end
@implementation IDETemplateAdditions_Additions
+ (void)load
{
dzl_implementationCombine(NSClassFromString(@"IDETemplate"), self, dzl_no_assert);
SEL selector = NSSelectorFromString(@"availableTemplatesOfTemplateKind:");
__block IMP originalIMP = PSPDFReplaceMethodWithBlock(NSClassFromString(@"IDETemplate"), selector, ^id(id _self, id kind) {
// call the original method
NSArray *templates = ((id ( *)(id, SEL, id))originalIMP)(_self, selector, kind);
// now manipulate if necessary
if (![Stencil sharedPlugin].showCustomTemplatesOnly) {
// not necessary, so return result of original method
return templates;
}
templates = [templates filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id template, NSDictionary *bindings) {
BOOL isDataModel = [[template valueForKey:@"templateName"] isEqualToString:@"Data Model"];
BOOL isMappingModel = [[template valueForKey:@"templateName"] isEqualToString:@"Mapping Model"];
return !isDataModel && !isMappingModel;
}]];
return [[NSSet setWithArray:templates] allObjects];
});
}
+ (void)_processChildrenOfFilePath:(id)path enumerator:(id)enumerator
{
if ([Stencil sharedPlugin].showCustomTemplatesOnly && [[path valueForKey:@"pathString"] containsString:@"Templates"]) {
NSString *rootPath = [Stencil sharedPlugin].projectRootPath;
if (rootPath) {
NSString *pathString = [rootPath stringByAppendingPathComponent:PluginNameAndCorrespondingDirectory];
SEL factorySel = NSSelectorFromString(@"filePathForPathString:");
path = objc_msgSend([path class], factorySel, pathString);
}
}
dzlSuper(_processChildrenOfFilePath:path enumerator:enumerator);
}
@end
================================================
FILE: StencilPlugin/Additions/NSMenu+StencilAdditions.h
================================================
//
// NSMenu+StencilAdditions.h
// StencilPlugin
//
// Created by Sam Dods on 18/04/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import
FOUNDATION_EXPORT NSString *const ProjectNavigatorContextualMenu;
@interface NSMenu (StencilAdditions)
- (void)duplicateItemWithTitle:(NSString *)existingTitle duplicateTitle:(NSString *)duplicateTitle;
@end
================================================
FILE: StencilPlugin/Additions/NSMenu+StencilAdditions.m
================================================
//
// NSMenu+StencilAdditions.m
// Stencil
//
// Created by Sam Dods on 18/04/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import "NSMenu+StencilAdditions.h"
#import "DZLImplementationCombine.h"
#import "Stencil.h"
NSString *const ProjectNavigatorContextualMenu = @"Project navigator contextual menu";
@implementation_combine(NSMenu, Additions)
- (instancetype)initWithTitle:(NSString *)aTitle
{
[Stencil sharedPlugin].beginCreateTemplateFromGroup = NO;
if ([Stencil sharedPlugin].canCreateFromCustomTemplate) {
[Stencil sharedPlugin].menuItemNewFromCustomTemplate.action = [Stencil sharedPlugin].menuItemDelete.action;
} else {
[Stencil sharedPlugin].menuItemNewFromCustomTemplate.action = nil;
}
typeof(self) menu = dzlSuper(initWithTitle:aTitle);
[[Stencil sharedPlugin] observeHighlightedItemForMenu:menu];
return menu;
}
- (void)addItem:(NSMenuItem *)menuItemBeingAddedByXcode
{
BOOL isContextualMenu = [self.title isEqualToString:ProjectNavigatorContextualMenu];
if (isContextualMenu) {
[[Stencil sharedPlugin] observeHighlightedItemForMenu:self];
}
BOOL addCustomFileItem = (isContextualMenu && [menuItemBeingAddedByXcode.title isEqualToString:@"New File…"]);
if (addCustomFileItem) {
NSMenuItem *customMenuItem = [[NSMenuItem alloc] initWithTitle:MenuItemTitleNewFileFromCustomTemplate action:nil keyEquivalent:@""];
dzlSuper(addItem:customMenuItem);
[Stencil sharedPlugin].menuItemNewFile = menuItemBeingAddedByXcode;
[Stencil sharedPlugin].menuItemNewFromCustomTemplate = customMenuItem;
if (![Stencil sharedPlugin].canCreateFromCustomTemplate) {
customMenuItem.action = nil;
}
}
BOOL addCreateTemplate = (isContextualMenu && [menuItemBeingAddedByXcode.title isEqualToString:@"Delete"]);
if (addCreateTemplate) {
NSMenuItem *customMenuItem = [[NSMenuItem alloc] initWithTitle:@"New Template from Selection" action:menuItemBeingAddedByXcode.action keyEquivalent:@""];
dzlSuper(addItem:customMenuItem);
[Stencil sharedPlugin].menuItemDelete = menuItemBeingAddedByXcode;
[Stencil sharedPlugin].menuItemCreateTemplateFromGroup = customMenuItem;
[self addItem:[NSMenuItem separatorItem]];
}
dzlSuper(addItem:menuItemBeingAddedByXcode);
}
@end
@implementation NSMenu (StencilAdditions)
- (void)duplicateItemWithTitle:(NSString *)existingTitle duplicateTitle:(NSString *)duplicateTitle
{
NSUInteger index = [self indexOfItemWithTitle:existingTitle];
NSMenuItem *originalItem = [self itemWithTitle:existingTitle];
NSMenuItem *customNewMenuItem = [[NSMenuItem alloc] initWithTitle:duplicateTitle action:originalItem.action keyEquivalent:@""];
[self insertItem:customNewMenuItem atIndex:index];
}
@end
================================================
FILE: StencilPlugin/Additions/NSObject+StencilAdditions.h
================================================
//
// NSObject+StencilAdditions.h
// StencilPlugin
//
// Created by Sam Dods on 18/04/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import
@interface NSObject (StencilAdditions)
+ (NSArray *)dzl_printMethods;
@end
================================================
FILE: StencilPlugin/Additions/NSObject+StencilAdditions.m
================================================
//
// NSObject+StencilAdditions.m
// StencilPlugin
//
// Created by Sam Dods on 18/04/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import "NSObject+StencilAdditions.h"
#import
@implementation NSObject (StencilAdditions)
+ (NSArray *)dzl_printMethods
{
uint numberOfMethods;
Method *methods = class_copyMethodList(self, &numberOfMethods);
NSMutableArray *methodNames = [NSMutableArray new];
for (uint m = 0; m < numberOfMethods; m++) {
Method method = methods[m];
SEL name = method_getName(method);
[methodNames addObject:NSStringFromSelector(name)];
}
return methodNames.copy;
}
@end
================================================
FILE: StencilPlugin/Additions/Parsing/NSInputStream+StencilAdditions.h
================================================
//
// NSInputStream+StencilAdditions.h
// StencilPlugin
//
// Created by Sam Dods on 20/04/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import
@interface NSInputStream (StencilAdditions)
@property (nonatomic, readonly) NSString *stc_nextReadLine;
@end
================================================
FILE: StencilPlugin/Additions/Parsing/NSInputStream+StencilAdditions.m
================================================
//
// NSInputStream+StencilAdditions.m
// StencilPlugin
//
// Created by Sam Dods on 20/04/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import "NSInputStream+StencilAdditions.h"
@implementation NSInputStream (StencilAdditions)
- (NSString *)stc_nextReadLine
{
uint8_t inputCharacter;
NSMutableString *outputString = nil;
while ([self read:&inputCharacter maxLength:1] == 1) {
if (!outputString) {
outputString = [NSMutableString new];
}
[outputString appendFormat:@"%c", inputCharacter];
if (inputCharacter == '\n') {
break;
}
}
return [outputString copy];
}
@end
================================================
FILE: StencilPlugin/Additions/Parsing/NSOutputStream+StencilAdditions.h
================================================
//
// NSOutputStream+StencilAdditions.h
// StencilPlugin
//
// Created by Sam Dods on 20/04/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import
@interface NSOutputStream (StencilAdditions)
- (void)stc_writeString:(NSString *)string;
@end
================================================
FILE: StencilPlugin/Additions/Parsing/NSOutputStream+StencilAdditions.m
================================================
//
// NSOutputStream+StencilAdditions.m
// StencilPlugin
//
// Created by Sam Dods on 20/04/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import "NSOutputStream+StencilAdditions.h"
@implementation NSOutputStream (StencilAdditions)
- (void)stc_writeString:(NSString *)string
{
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
[self write:[data bytes] maxLength:[data length]];
}
@end
================================================
FILE: StencilPlugin/Additions/Parsing/NSString+StencilRegex.h
================================================
//
// NSString+StencilRegex.h
// StencilPlugin
//
// Created by Sam Dods on 20/04/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import
@interface NSString (StencilRegex)
- (NSString *)stringByMatching:(NSString *)pattern replaceWith:(NSString *)templ;
@end
@interface NSMutableString (StencilRegex)
- (NSUInteger)matchPattern:(NSString *)pattern replaceWith:(NSString *)templ;
@end
================================================
FILE: StencilPlugin/Additions/Parsing/NSString+StencilRegex.m
================================================
//
// NSString+StencilRegex.m
// StencilPlugin
//
// Created by Sam Dods on 20/04/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import "NSString+StencilRegex.h"
@implementation NSString (StencilRegex)
- (NSString *)stringByMatching:(NSString *)pattern replaceWith:(NSString *)template
{
NSMutableString *mutableSelf = self.mutableCopy;
NSUInteger matches = [mutableSelf matchPattern:pattern replaceWith:template];
return matches ? mutableSelf.copy : nil;
}
@end
@implementation NSMutableString (StencilRegex)
- (NSUInteger)matchPattern:(NSString *)pattern replaceWith:(NSString *)template
{
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:nil];
return [regex replaceMatchesInString:self options:0 range:NSMakeRange(0, self.length) withTemplate:template];
}
@end
================================================
FILE: StencilPlugin/Additions/Parsing/TemplateFactory.h
================================================
//
// TemplateFactory.h
// StencilPlugin
//
// Created by Sam Dods on 20/04/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import
@class TemplateConfig;
@interface TemplateFactory : NSObject
+ (instancetype)defaultFactory;
- (void)showAlertForError:(NSError *)error;
- (void)showAlertWithMessage:(NSString *)message;
- (void)generateTemplateFromConfig:(TemplateConfig *)config;
@end
================================================
FILE: StencilPlugin/Additions/Parsing/TemplateFactory.m
================================================
//
// TemplateFactory.m
// StencilPlugin
//
// Created by Sam Dods on 20/04/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import "TemplateFactory.h"
#import "TemplateConfig.h"
#import "ProjectGroup.h"
#import "ProjectFile.h"
#import "Stencil.h"
#import "NSInputStream+StencilAdditions.h"
#import "NSOutputStream+StencilAdditions.h"
#import "NSString+StencilRegex.h"
@implementation TemplateFactory
+ (instancetype)defaultFactory
{
static TemplateFactory *factory = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
factory = [TemplateFactory new];
});
return factory;
}
- (void)generateTemplateFromConfig:(TemplateConfig *)config
{
NSString *templateName = [config.properties.templateName stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
templateName = [templateName stringByAppendingString:@".xctemplate"];
NSString *targetPath = [[[Stencil sharedPlugin].projectRootPath stringByAppendingPathComponent:PluginNameAndCorrespondingDirectory] stringByAppendingPathComponent:FileTemplatesDirectoryPath];
targetPath = [targetPath stringByAppendingPathComponent:templateName];
if ([[NSFileManager defaultManager] fileExistsAtPath:targetPath isDirectory:nil]) {
[self showAlertWithMessage:@"Template already exists with this name. Will not overwrite."];
return;
}
[[NSFileManager defaultManager] createDirectoryAtPath:targetPath withIntermediateDirectories:YES attributes:nil error:nil];
NSError *error = nil;
[self copyTemplateInfoPlistToPath:targetPath config:config error:&error];
if (error) {
[self showAlertForError:error];
return;
}
NSDictionary *fileTypeByTargetPath = [self processConfig:config targetBasePath:targetPath];
[self openFilePaths:fileTypeByTargetPath];
}
#pragma mark - processing
- (NSDictionary *)processConfig:(TemplateConfig *)config targetBasePath:(NSString *)targetPath
{
NSMutableDictionary *fileTypeByTargetPath = [NSMutableDictionary new];
for (id file in config.fileRefs) {
NSString *targetFilePath = config.properties.templateFilenameByOriginalFilename[file.nameWithoutExtension];
targetFilePath = [targetFilePath stringByAppendingString:file.extension];
targetFilePath = [targetPath stringByAppendingPathComponent:targetFilePath];
if (config.properties.thingType == STCThingTypeObjcInterface) {
[self createObjcInterfaceTemplateFromFile:file targetPath:targetFilePath type:file.type configProperties:config.properties];
} else if (config.properties.thingType == STCThingTypeObjcProtocol) {
[self createObjcProtocolTemplateFromFile:file targetPath:targetFilePath type:file.type configProperties:config.properties];
} else if (config.properties.thingType == STCThingTypeSwiftClass) {
[self createSwiftClassTemplateFromFile:file targetPath:targetFilePath type:file.type configProperties:config.properties];
} else if (config.properties.thingType == STCThingTypeSwiftProtocol) {
[self createSwiftProtocolTemplateFromFile:file targetPath:targetFilePath type:file.type configProperties:config.properties];
}
fileTypeByTargetPath[targetFilePath] = @(file.type);
};
return fileTypeByTargetPath.copy;
}
#pragma mark - copying template
- (void)copyTemplateInfoPlistToPath:(NSString *)targetPath config:(TemplateConfig *)config error:(NSError **)error
{
NSString *readmeSourcePath = [[Stencil sharedPlugin].pluginBundle pathForResource:@"StencilREADME" ofType:@""];
NSString *readmeTargetPath = @"/tmp/StencilREADME";
[[NSFileManager defaultManager] copyItemAtPath:readmeSourcePath toPath:readmeTargetPath error:nil];
NSString *sourcePath = [[Stencil sharedPlugin].pluginBundle pathForResource:@"TemplateInfo" ofType:@"plist"];
NSString *targetFilePath = [targetPath stringByAppendingPathComponent:@"TemplateInfo.plist"];
[self copySourceFileAtPath:sourcePath toPath:targetFilePath withTopComment:nil templateProperties:config.properties through:^NSString *(NSString *line) {
NSMutableString *mutableLine = [line mutableCopy];
[mutableLine matchPattern:@"__STC_DESCRIPTION__" replaceWith:config.properties.templateDescription];
return [mutableLine copy];
}];
}
#pragma mark - copying objective-c
- (void)createObjcInterfaceTemplateFromFile:(id)file targetPath:(NSString *)targetPath type:(ProjectFileType)filetype configProperties:(TemplateProperties *)templateProperties
{
NSString *topComment = [self topCommentForFileType:filetype configProperties:templateProperties];
[self copySourceFileAtPath:file.fullPath toPath:targetPath withTopComment:topComment templateProperties:templateProperties through:^NSString *(NSString *line) {
if (filetype == ProjectFileUserInterface) {
return [self stringByTemplatifyingXIB:line configProperties:templateProperties];
}
NSString *output = [self stringByTemplatifyingInterface:line configProperties:templateProperties];
if (filetype == ProjectFileObjcImplementation) {
NSMutableString *mutableOutput = [output mutableCopy];
NSString *templateFilename = templateProperties.templateFilenameByOriginalFilename[file.name];
NSRange rangeOfDot = [templateFilename rangeOfString:@"."];
templateFilename = [templateFilename substringToIndex:rangeOfDot.location];
NSString *importReplacement = [NSString stringWithFormat:@"#import \"%@.h\"", templateFilename];
[mutableOutput matchPattern:[NSString stringWithFormat:@"#import \"%@.h\"", file.nameWithoutExtension] replaceWith:importReplacement];
return [mutableOutput copy];
}
return output;
}];
}
- (void)createObjcProtocolTemplateFromFile:(id)file targetPath:(NSString *)targetPath type:(ProjectFileType)filetype configProperties:(TemplateProperties *)templateProperties
{
NSString *topComment = [self topCommentForFileType:filetype configProperties:templateProperties];
[self copySourceFileAtPath:file.fullPath toPath:targetPath withTopComment:topComment templateProperties:templateProperties through:^NSString *(NSString *line) {
if (filetype != ProjectFileUserInterface) {
return [self stringByTemplatifyingObjcProtocol:line configProperties:templateProperties];
}
return line;
}];
if (filetype == ProjectFileObjcImplementation) {
[self showAlertWithMessage:@"Warning: you have created a protocol template which includes an implementation file (.m). This is flagged as a warning, because it is mostly unexpected, but it is allowed."];
}
}
#pragma mark - copying swift
- (void)createSwiftClassTemplateFromFile:(id)file targetPath:(NSString *)targetPath type:(ProjectFileType)filetype configProperties:(TemplateProperties *)templateProperties
{
NSString *topComment = [self topCommentForFileType:filetype configProperties:templateProperties];
[self copySourceFileAtPath:file.fullPath toPath:targetPath withTopComment:topComment templateProperties:templateProperties through:^NSString *(NSString *line) {
if (filetype == ProjectFileUserInterface) {
return [self stringByTemplatifyingXIB:line configProperties:templateProperties];
}
return [self stringByTemplatifyingSwift:line configProperties:templateProperties];
}];
}
- (void)createSwiftProtocolTemplateFromFile:(id)file targetPath:(NSString *)targetPath type:(ProjectFileType)filetype configProperties:(TemplateProperties *)templateProperties
{
NSString *topComment = [self topCommentForFileType:filetype configProperties:templateProperties];
[self copySourceFileAtPath:file.fullPath toPath:targetPath withTopComment:topComment templateProperties:templateProperties through:^NSString *(NSString *line) {
if (filetype != ProjectFileUserInterface) {
return [self stringByTemplatifyingSwift:line configProperties:templateProperties];
}
return line;
}];
}
#pragma mark - top comment
- (NSString *)topCommentForFileType:(ProjectFileType)filetype configProperties:(TemplateProperties *)templateProperties
{
switch (filetype) {
case ProjectFileObjcImplementation:
case ProjectFileSwift:
return [self defaultHeaderComments];
case ProjectFileObjcInterface: {
NSString *comments = [self defaultHeaderComments];
return [comments stringByAppendingFormat:@"\n#import \"%@.h\"\n", templateProperties.thingNameToInheritFrom];
}
default:
return nil;
}
}
- (NSString *)defaultHeaderComments
{
NSURL *url = [[Stencil sharedPlugin].pluginBundle URLForResource:@"HeaderComments" withExtension:@"sctemplate"];
NSData *data = [[NSData alloc] initWithContentsOfURL:url];
return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
#pragma mark - generic copying
- (void)copySourceFileAtPath:(NSString *)sourcePath toPath:(NSString *)targetPath withTopComment:(NSString *)topComment templateProperties:(TemplateProperties *)templateProperties through:(NSString *(^)(NSString *line))modifiedStringBlock
{
NSInputStream *inputStream = [NSInputStream inputStreamWithFileAtPath:sourcePath];
[inputStream open];
NSOutputStream *outputStream = [NSOutputStream outputStreamToFileAtPath:targetPath append:NO];
[outputStream open];
if (topComment) {
[outputStream stc_writeString:topComment];
}
BOOL hasReachedFirstNonComment = (topComment == nil);
NSString *line = inputStream.stc_nextReadLine;
while (line) {
if (!hasReachedFirstNonComment && ![line hasPrefix:@"//"]) {
hasReachedFirstNonComment = YES;
}
if (!hasReachedFirstNonComment) {
line = inputStream.stc_nextReadLine;
continue;
}
__block NSString *output = line;
[templateProperties.replacementTextByFindText enumerateKeysAndObjectsUsingBlock:^(NSString *findText, NSString *replaceText, BOOL *stop) {
output = [line stringByReplacingOccurrencesOfString:findText withString:replaceText];
}];
output = modifiedStringBlock(output);
[outputStream stc_writeString:output];
line = inputStream.stc_nextReadLine;
}
[inputStream close];
[outputStream close];
}
#pragma mark - templates: objective-c
- (NSString *)stringByTemplatifyingInterface:(NSString *)line configProperties:(TemplateProperties *)templateProperties
{
NSMutableString *output = [line mutableCopy];
NSString *const nameToReplace = templateProperties.thingNameToReplace;
NSString *const newInherit = templateProperties.thingNameToInheritFrom;
static NSString *const FileBaseNameAsID = @"___FILEBASENAMEASIDENTIFIER___";
// substitute interface definition
[output matchPattern:[NSString stringWithFormat:@"^@interface\\s+%@\\s*:\\s*\\w+", nameToReplace]
replaceWith:[NSString stringWithFormat:@"@interface %@ : %@", FileBaseNameAsID, newInherit]];
// substitute interface extension
[output matchPattern:[NSString stringWithFormat:@"^@interface\\s+%@\\s*\\((\\w*)\\)", nameToReplace]
replaceWith:[NSString stringWithFormat:@"@interface %@ ($1)", FileBaseNameAsID]];
// substitute implementation definition
[output matchPattern:[NSString stringWithFormat:@"^@implementation\\s+%@\\b", nameToReplace]
replaceWith:[NSString stringWithFormat:@"@implementation %@", FileBaseNameAsID]];
return [output copy];
}
- (NSString *)stringByTemplatifyingObjcProtocol:(NSString *)line configProperties:(TemplateProperties *)templateProperties
{
NSMutableString *output = [line mutableCopy];
NSString *const nameToReplace = templateProperties.thingNameToReplace;
NSString *const newInherit = templateProperties.thingNameToInheritFrom;
static NSString *const FileBaseNameAsID = @"___FILEBASENAMEASIDENTIFIER___";
// sub protocol definition
[output matchPattern:[NSString stringWithFormat:@"@protocol\\s+%@\\s*<\\w+>", nameToReplace]
replaceWith:[NSString stringWithFormat:@"@protocol %@ <%@>", FileBaseNameAsID, newInherit]];
return [output copy];
}
#pragma mark - templates: swift
- (NSString *)stringByTemplatifyingSwift:(NSString *)line configProperties:(TemplateProperties *)templateProperties
{
NSMutableString *output = [line mutableCopy];
NSString *const nameToReplace = templateProperties.thingNameToReplace;
NSString *const newInherit = templateProperties.thingNameToInheritFrom;
static NSString *const FileBaseNameAsID = @"___FILEBASENAMEASIDENTIFIER___";
// substitute class/protocol definition
[output matchPattern:[NSString stringWithFormat:@"^\\s*(class|protocol|extension)\\s+%@\\s*:\\s*\\w+", nameToReplace]
replaceWith:[NSString stringWithFormat:@"$1 %@ : %@", FileBaseNameAsID, newInherit]];
return [output copy];
}
#pragma mark - templates: xib & storyboard
- (NSString *)stringByTemplatifyingXIB:(NSString *)line configProperties:(TemplateProperties *)templateProperties
{
NSMutableString *output = [line mutableCopy];
NSString *const nameToReplace = templateProperties.thingNameToReplace;
static NSString *const FileBaseNameAsID = @"___FILEBASENAMEASIDENTIFIER___";
[output matchPattern:[NSString stringWithFormat:@"customClass=\"%@\"", nameToReplace]
replaceWith:[NSString stringWithFormat:@"customClass=\"%@\"", FileBaseNameAsID]];
return [output copy];
}
#pragma mark - opening
- (void)openFilePaths:(NSDictionary *)fileTypeByTargetPath
{
NSMutableArray *codeFilePaths = [NSMutableArray new];
NSMutableArray *interfaceFilePaths = [NSMutableArray new];
[fileTypeByTargetPath enumerateKeysAndObjectsUsingBlock:^(NSString *filePath, NSNumber *fileType, BOOL *stop) {
if (fileType.integerValue == ProjectFileUserInterface) {
[interfaceFilePaths addObject:filePath];
} else {
[codeFilePaths addObject:filePath];
}
}];
codeFilePaths = [[codeFilePaths sortedArrayUsingComparator:^NSComparisonResult(NSString *path1, NSString *path2) {
return [path1 compare:path2];
}] mutableCopy];
[codeFilePaths addObject:@"/tmp/StencilREADME"];
[[[NSApplication sharedApplication] delegate] application:[NSApplication sharedApplication] openFiles:codeFilePaths];
[[[NSApplication sharedApplication] delegate] application:[NSApplication sharedApplication] openFiles:interfaceFilePaths];
}
#pragma mark - alert
- (void)showAlertForError:(NSError *)error
{
[self showAlertWithMessage:error.userInfo[NSLocalizedDescriptionKey]];
}
- (void)showAlertWithMessage:(NSString *)message
{
NSAlert *alert = [NSAlert new];
alert.messageText = message;
[alert addButtonWithTitle:@"OK"];
[alert runModal];
}
@end
================================================
FILE: StencilPlugin/Helpers/DeallocationObserver.h
================================================
//
// DeallocationObserver.h
// StencilPlugin
//
// Created by Sam Dods on 02/05/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import
@interface DeallocationObserver : NSObject
@property (nonatomic, weak) id observedObject;
@property (nonatomic, copy) void (^deallocBlock)(id observedObject);
@end
================================================
FILE: StencilPlugin/Helpers/DeallocationObserver.m
================================================
//
// DeallocationObserver.m
// StencilPlugin
//
// Created by Sam Dods on 02/05/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import "DeallocationObserver.h"
#import
@interface DeallocationObserver ()
@end
@implementation DeallocationObserver
- (void)dealloc
{
!self.deallocBlock ?: self.deallocBlock(self.observedObject);
}
- (void)setObservedObject:(id)observedObject
{
_observedObject = observedObject;
objc_setAssociatedObject(observedObject, _cmd, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
================================================
FILE: StencilPlugin/Helpers/StencilWeakObjectWrapper.h
================================================
//
// StencilWeakObjectWrapper.h
// StencilPlugin
//
// Created by Sam Dods on 19/04/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import
@interface StencilWeakObjectWrapper : NSObject
+ (instancetype) wrap:(NSObject *)object;
@property (nonatomic, weak, readonly) NSObject *wrappedObject;
@end
================================================
FILE: StencilPlugin/Helpers/StencilWeakObjectWrapper.m
================================================
//
// StencilWeakObjectWrapper.m
// StencilPlugin
//
// Created by Sam Dods on 19/04/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import "StencilWeakObjectWrapper.h"
@interface StencilWeakObjectWrapper ()
@property (nonatomic, weak) NSObject *wrappedObject;
@end
@implementation StencilWeakObjectWrapper
+ (instancetype)wrap:(NSObject *)object
{
StencilWeakObjectWrapper *wrapper = [self new];
wrapper.wrappedObject = object;
return wrapper;
}
- (NSUInteger)hash
{
return self.wrappedObject.hash;
}
@end
================================================
FILE: StencilPlugin/Info.plist
================================================
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
CC0D0F4F-05B3-431A-8F33-F84AFCB2C651
0420B86A-AA43-4792-9ED0-6FE0F2B16A13
C4A681B0-4A26-480E-93EC-1218098B9AA0
AD68E85B-441B-4301-B564-A45E4919A6AD
A16FF353-8441-459E-A50C-B071F53F51B7
9F75337B-21B4-4ADC-B558-F9CADF7073A7
8DC44374-2B35-4C57-A6FE-2AD66A36AAD9
LSMinimumSystemVersion
$(MACOSX_DEPLOYMENT_TARGET)
NSPrincipalClass
Stencil
XC4Compatible
XCPluginHasUI
================================================
FILE: StencilPlugin/Model/TemplateConfig.h
================================================
//
// TemplateConfig.h
// StencilPlugin
//
// Created by Sam Dods on 20/04/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import
#import "ProjectGroup.h"
#import "ThingTypeToClassNamesMap.h"
@interface TemplateProperties : NSObject
@property (nonatomic, readonly) NSString *templateName;
@property (nonatomic, readonly) STCThingType thingType;
@property (nonatomic, readonly) NSString *thingNameToReplace;
@property (nonatomic, readonly) NSString *thingNameToInheritFrom;
@property (nonatomic, readonly) NSString *templateDescription;
@property (nonatomic, readonly) NSDictionary *templateFilenameByOriginalFilename;
@property (nonatomic, readonly) NSDictionary *replacementTextByFindText;
- (instancetype)initWithName:(NSString *)name thingType:(STCThingType)thingType nameToReplace:(NSString *)replace inheritFrom:(NSString *)inherit description:(NSString *)description templateFileMap:(NSDictionary *)templateFileMap replaceByFind:(NSDictionary *)replaceByFind;
@end
@interface TemplateConfig : NSObject
/**
* Generates the defaults for the given group. Use this method to instantiate a template config and then
* change the properties as needed, usually based on user input.
*
* @param group The group on which the template config will be based.
* @param error On return, this error will be set if something went wrong. Otherwise this will be unchanged from input.
*
* @return A new template config instance, with default properties based on the specified group, or nil if something went wrong.
*/
+ (instancetype)defaultConfigForFiles:(NSArray *)files error:(NSError **)error;
@property (nonatomic, readonly) NSArray *thingTypeToNamesMaps;
@property (nonatomic, readonly) NSArray *fileRefs;
@property (nonatomic, readonly) TemplateProperties *properties;
@end
================================================
FILE: StencilPlugin/Model/TemplateConfig.m
================================================
//
// TemplateConfig.m
// StencilPlugin
//
// Created by Sam Dods on 20/04/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import "TemplateConfig.h"
#import "ProjectFile.h"
#import "ProjectGroup.h"
#import "NSInputStream+StencilAdditions.h"
#import "NSString+StencilRegex.h"
#import "ThingTypeToClassNamesMap.h"
@interface TemplateConfig ()
@property (nonatomic, readwrite) TemplateProperties *properties;
@end
@implementation TemplateConfig
+ (instancetype)defaultConfigForFiles:(NSArray *)files error:(NSError **)error
{
NSError *internalError = nil;
NSArray *fileRefs = [files validatedTemplateFiles:&internalError];
if (internalError) {
if (error) {
*error = internalError;
}
return nil;
}
NSMutableOrderedSet *maps = [NSMutableOrderedSet new];
for (id file in fileRefs) {
if (file.type == ProjectFileObjcInterface || file.type == ProjectFileObjcImplementation) {
NSOrderedSet *mapsForFile = [self objcMapsFromFileAtPath:file.fullPath];
[maps addObjectsFromArray:mapsForFile.array];
} else if (file.type == ProjectFileSwift) {
NSOrderedSet *mapsForFile = [self swiftMapsFromFileAtPath:file.fullPath];
[maps addObjectsFromArray:mapsForFile.array];
}
}
return [[self alloc] initWithThingTypeToNamesMaps:maps.array fileRefs:fileRefs];
}
#pragma mark - objective-c
+ (NSOrderedSet *)objcMapsFromFileAtPath:(NSString *)filePath
{
NSMutableOrderedSet *maps = [NSMutableOrderedSet new];
[self processFileAtPath:filePath matching:@"^@implementation\\s+(\\w+).*" thingType:STCThingTypeObjcInterface maps:maps];
[self processFileAtPath:filePath matching:@"^@interface\\s+(\\w+)\\s*\\(\\s*\\w*\\s*\\).*" thingType:STCThingTypeObjcInterface maps:maps];
[self processFileAtPath:filePath matching:@"^@interface\\s+(\\w+)\\s*:\\s*(\\w+).*" thingType:STCThingTypeObjcInterface maps:maps];
[self processFileAtPath:filePath matching:@"^@protocol\\s+(\\w+)\\s*<(\\w+)>.*" thingType:STCThingTypeObjcProtocol maps:maps];
return maps.copy;
}
#pragma mark - swift
+ (NSOrderedSet *)swiftMapsFromFileAtPath:(NSString *)filePath
{
NSMutableOrderedSet *maps = [NSMutableOrderedSet new];
[self processFileAtPath:filePath matching:@"^\\s*extension\\s+(\\w+).*" thingType:STCThingTypeSwiftClass maps:maps];
[self processFileAtPath:filePath matching:@"^\\s*class\\s+(\\w+).*" thingType:STCThingTypeSwiftClass maps:maps];
[self processFileAtPath:filePath matching:@"^\\s*class\\s+(\\w+)\\s*:\\s*(\\w+).*" thingType:STCThingTypeSwiftClass maps:maps];
[self processFileAtPath:filePath matching:@"^\\s*protocol\\s+(\\w+)\\s*:\\s*(\\w+).*" thingType:STCThingTypeSwiftProtocol maps:maps];
return maps.copy;
}
#pragma mark - generic
+ (void)processFileAtPath:(NSString *)filePath matching:(NSString *)pattern thingType:(STCThingType)thingType maps:(NSMutableOrderedSet *)maps
{
[self enumerateFileAtPath:filePath thingsMatching:pattern block:^(NSArray *protocolNames) {
ThingTypeToClassNamesMap *map = [[ThingTypeToClassNamesMap alloc] initWithThingType:thingType names:protocolNames];
[maps addObject:map];
}];
}
+ (void)enumerateFileAtPath:(NSString *)filePath thingsMatching:(NSString *)pattern block:(void(^)(NSArray *thingNames))block
{
NSInputStream *inputStream = [NSInputStream inputStreamWithFileAtPath:filePath];
[inputStream open];
NSString *line = inputStream.stc_nextReadLine;
while (line) {
NSString *output = [[line stringByMatching:pattern replaceWith:@"$1:$2"] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSArray *names = [output componentsSeparatedByString:@":"];
if (names) {
if ([names.lastObject isKindOfClass:[NSString class]] && ![names.lastObject length]) {
names = @[names.firstObject];
}
block(names);
}
line = inputStream.stc_nextReadLine;
}
[inputStream close];
}
#pragma mark - init
- (instancetype)initWithThingTypeToNamesMaps:(NSArray *)maps fileRefs:(NSArray *)fileRefs
{
if (!(self = [super init])) {
return nil;
}
_thingTypeToNamesMaps = maps;
_fileRefs = fileRefs;
return self;
}
@end
@implementation TemplateProperties
- (instancetype)initWithName:(NSString *)name thingType:(STCThingType)thingType nameToReplace:(NSString *)replace inheritFrom:(NSString *)inherit description:(NSString *)description templateFileMap:(NSDictionary *)templateFileMap replaceByFind:(NSDictionary *)replaceByFind
{
if (!(self = [super init])) {
return nil;
}
_templateName = name;
_thingType = thingType;
_thingNameToReplace = replace;
_thingNameToInheritFrom = inherit;
_templateDescription = description;
_templateFilenameByOriginalFilename = templateFileMap;
_replacementTextByFindText = replaceByFind;
return self;
}
@end
================================================
FILE: StencilPlugin/Model/ThingTypeToClassNamesMap.h
================================================
//
// ThingTypeToClassNamesMap.h
// StencilPlugin
//
// Created by Sam Dods on 22/04/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import
typedef NS_ENUM(NSInteger, STCThingType) {
STCThingTypeObjcInterface,
STCThingTypeObjcProtocol,
STCThingTypeSwiftClass,
STCThingTypeSwiftProtocol,
};
@interface ThingTypeToClassNamesMap : NSObject
@property (nonatomic, readonly) STCThingType thingType;
@property (nonatomic, readonly) NSArray *names;
- (instancetype)initWithThingType:(STCThingType)thingType names:(NSArray *)names;
@property (nonatomic, readonly) NSString *thingTypeString;
@end
================================================
FILE: StencilPlugin/Model/ThingTypeToClassNamesMap.m
================================================
//
// ThingTypeToClassNamesMap.m
// StencilPlugin
//
// Created by Sam Dods on 22/04/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import "ThingTypeToClassNamesMap.h"
@implementation ThingTypeToClassNamesMap
- (instancetype)initWithThingType:(STCThingType)thingType names:(NSArray *)names
{
if (!(self = [super init])) {
return nil;
}
_thingType = thingType;
_names = names;
return self;
}
- (NSString *)thingTypeString
{
switch (self.thingType) {
case STCThingTypeObjcInterface:
return @"interface";
case STCThingTypeSwiftClass:
return @"class";
case STCThingTypeObjcProtocol:
case STCThingTypeSwiftProtocol:
return @"protocol";
}
}
- (NSUInteger)hash
{
return self.thingType ^ self.names.hash;
}
@end
================================================
FILE: StencilPlugin/Resources/Built-in Templates/Stencil/NSView.xctemplate/NSViewObjective-C/___FILEBASENAME___.h
================================================
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
___IMPORTHEADER_cocoaSubclass___
@interface ___FILEBASENAMEASIDENTIFIER___ : ___VARIABLE_cocoaSubclass___
@end
================================================
FILE: StencilPlugin/Resources/Built-in Templates/Stencil/NSView.xctemplate/NSViewObjective-C/___FILEBASENAME___.m
================================================
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
#import "___FILEBASENAME___.h"
@implementation ___FILEBASENAMEASIDENTIFIER___
- (void)awakeFromNib
{
[super awakeFromNib];
// Additional configuration
}
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
// Drawing code here.
}
@end
================================================
FILE: StencilPlugin/Resources/Built-in Templates/Stencil/NSView.xctemplate/NSViewSwift/___FILEBASENAME___.swift
================================================
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
import Cocoa
class ___FILEBASENAMEASIDENTIFIER___: ___VARIABLE_cocoaSubclass___ {
override func awakeFromNib() {
super.awakeFromNib();
// Additional configuration
}
override func drawRect(dirtyRect: NSRect) {
super.drawRect(dirtyRect)
// Drawing code here.
}
}
================================================
FILE: StencilPlugin/Resources/Built-in Templates/Stencil/NSView.xctemplate/NSViewXIBObjective-C/___FILEBASENAME___.h
================================================
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
___IMPORTHEADER_cocoaSubclass___
@interface ___FILEBASENAMEASIDENTIFIER___ : ___VARIABLE_cocoaSubclass___
@end
================================================
FILE: StencilPlugin/Resources/Built-in Templates/Stencil/NSView.xctemplate/NSViewXIBObjective-C/___FILEBASENAME___.m
================================================
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
#import "___FILEBASENAME___.h"
@implementation ___FILEBASENAMEASIDENTIFIER___
- (void)awakeFromNib
{
[super awakeFromNib];
// Additional configuration
}
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
// Drawing code here.
}
@end
================================================
FILE: StencilPlugin/Resources/Built-in Templates/Stencil/NSView.xctemplate/NSViewXIBObjective-C/___FILEBASENAME___.xib
================================================
================================================
FILE: StencilPlugin/Resources/Built-in Templates/Stencil/NSView.xctemplate/NSViewXIBSwift/___FILEBASENAME___.swift
================================================
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
import Cocoa
class ___FILEBASENAMEASIDENTIFIER___: ___VARIABLE_cocoaSubclass___ {
override func awakeFromNib() {
super.awakeFromNib();
// Additional configuration
}
override func drawRect(dirtyRect: NSRect) {
super.drawRect(dirtyRect)
// Drawing code here.
}
}
================================================
FILE: StencilPlugin/Resources/Built-in Templates/Stencil/NSView.xctemplate/NSViewXIBSwift/___FILEBASENAME___.xib
================================================
================================================
FILE: StencilPlugin/Resources/Built-in Templates/Stencil/NSView.xctemplate/TemplateInfo.plist
================================================
DefaultCompletionName
MyClass
Description
An NSview subclass with option of XIB
Kind
Xcode.IDEKit.TextSubstitutionFileTemplateKind
Options
Description
The name of the class to create
Identifier
productName
Name
Class:
NotPersisted
Required
Type
text
Default
NSObject
Description
What class to subclass in the new file
FallbackHeader
#import <Cocoa/Cocoa.h>
Identifier
cocoaSubclass
Name
Subclass of:
Required
Type
class
Values
NSView
Default
true
Description
Whether to create a XIB file with the same name
Identifier
XIB
Name
Also create XIB file for user interface
NotPersisted
RequiredOptions
cocoaSubclass
NSView
Type
checkbox
AllowedTypes
Objective-C
public.objective-c-source
public.objective-c-plus-plus-source
Swift
public.swift-source
Default
Objective-C
Description
The implementation language
Identifier
languageChoice
MainTemplateFiles
Objective-C
___FILEBASENAME___.m
Swift
___FILEBASENAME___.swift
Name
Language:
Required
Type
popup
Values
Swift
Objective-C
Platforms
com.apple.platform.macosx
SortOrder
8
Summary
An NSview subclass with option of XIB
================================================
FILE: StencilPlugin/Resources/Built-in Templates/Stencil/UIView.xctemplate/TemplateInfo.plist
================================================
DefaultCompletionName
MyClass
Description
A UIView subclass with option of XIB
Kind
Xcode.IDEKit.TextSubstitutionFileTemplateKind
Options
Description
The name of the class to create
Identifier
productName
Name
Class:
NotPersisted
Required
Type
text
Default
NSObject
Description
What class to subclass in the new file
FallbackHeader
#import <UIKit/UIKit.h>
Identifier
cocoaTouchSubclass
Name
Subclass of:
Required
YES
Suffixes
UIView
View
Type
class
Values
UIView
Default
false
Description
Whether to create a XIB file with the same name
Identifier
XIB
Name
Also create XIB file
NotPersisted
RequiredOptions
cocoaTouchSubclass
UIView
Type
checkbox
Default
iPhone
Description
What device family to create the file for
Identifier
deviceFamily
Name
RequiredOptions
XIB
true
cocoaTouchSubclass
UIView
Type
popup
Values
iPhone
iPad
AllowedTypes
Objective-C
public.objective-c-source
public.objective-c-plus-plus-source
Swift
public.swift-source
Default
Objective-C
Description
The implementation language
Identifier
languageChoice
MainTemplateFiles
Objective-C
___FILEBASENAME___.m
Swift
___FILEBASENAME___.swift
Name
Language:
Required
Type
popup
Values
Swift
Objective-C
Platforms
com.apple.platform.iphoneos
SortOrder
8
Summary
A UIView subclass with option of XIB
================================================
FILE: StencilPlugin/Resources/Built-in Templates/Stencil/UIView.xctemplate/UIViewObjective-C/___FILEBASENAME___.h
================================================
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
___IMPORTHEADER_cocoaTouchSubclass___
@interface ___FILEBASENAMEASIDENTIFIER___ : ___VARIABLE_cocoaTouchSubclass___
@end
================================================
FILE: StencilPlugin/Resources/Built-in Templates/Stencil/UIView.xctemplate/UIViewObjective-C/___FILEBASENAME___.m
================================================
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
#import "___FILEBASENAME___.h"
@implementation ___FILEBASENAMEASIDENTIFIER___
- (void)awakeFromNib
{
[super awakeFromNib];
// Additional configuration
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
}
*/
@end
================================================
FILE: StencilPlugin/Resources/Built-in Templates/Stencil/UIView.xctemplate/UIViewSwift/___FILEBASENAME___.swift
================================================
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
import UIKit
class ___FILEBASENAMEASIDENTIFIER___: ___VARIABLE_cocoaTouchSubclass___ {
override func awakeFromNib() {
super.awakeFromNib();
// Additional configuration
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func drawRect(rect: CGRect) {
// Drawing code
}
*/
}
================================================
FILE: StencilPlugin/Resources/Built-in Templates/Stencil/UIView.xctemplate/UIViewXIBiPadObjective-C/___FILEBASENAME___.h
================================================
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
___IMPORTHEADER_cocoaTouchSubclass___
@interface ___FILEBASENAMEASIDENTIFIER___ : ___VARIABLE_cocoaTouchSubclass___
@end
================================================
FILE: StencilPlugin/Resources/Built-in Templates/Stencil/UIView.xctemplate/UIViewXIBiPadObjective-C/___FILEBASENAME___.m
================================================
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
#import "___FILEBASENAME___.h"
@implementation ___FILEBASENAMEASIDENTIFIER___
- (void)awakeFromNib
{
[super awakeFromNib];
// Additional configuration
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
}
*/
@end
================================================
FILE: StencilPlugin/Resources/Built-in Templates/Stencil/UIView.xctemplate/UIViewXIBiPadObjective-C/___FILEBASENAME___.xib
================================================
================================================
FILE: StencilPlugin/Resources/Built-in Templates/Stencil/UIView.xctemplate/UIViewXIBiPadSwift/___FILEBASENAME___.swift
================================================
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
import UIKit
class ___FILEBASENAMEASIDENTIFIER___: ___VARIABLE_cocoaTouchSubclass___ {
override func awakeFromNib() {
super.awakeFromNib();
// Additional configuration
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func drawRect(rect: CGRect) {
// Drawing code
}
*/
}
================================================
FILE: StencilPlugin/Resources/Built-in Templates/Stencil/UIView.xctemplate/UIViewXIBiPadSwift/___FILEBASENAME___.xib
================================================
================================================
FILE: StencilPlugin/Resources/Built-in Templates/Stencil/UIView.xctemplate/UIViewXIBiPhoneObjective-C/___FILEBASENAME___.h
================================================
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
___IMPORTHEADER_cocoaTouchSubclass___
@interface ___FILEBASENAMEASIDENTIFIER___ : ___VARIABLE_cocoaTouchSubclass___
@end
================================================
FILE: StencilPlugin/Resources/Built-in Templates/Stencil/UIView.xctemplate/UIViewXIBiPhoneObjective-C/___FILEBASENAME___.m
================================================
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
#import "___FILEBASENAME___.h"
@implementation ___FILEBASENAMEASIDENTIFIER___
- (void)awakeFromNib
{
[super awakeFromNib];
// Additional configuration
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
}
*/
@end
================================================
FILE: StencilPlugin/Resources/Built-in Templates/Stencil/UIView.xctemplate/UIViewXIBiPhoneObjective-C/___FILEBASENAME___.xib
================================================
================================================
FILE: StencilPlugin/Resources/Built-in Templates/Stencil/UIView.xctemplate/UIViewXIBiPhoneSwift/___FILEBASENAME___.swift
================================================
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
import UIKit
class ___FILEBASENAMEASIDENTIFIER___: ___VARIABLE_cocoaTouchSubclass___ {
override func awakeFromNib() {
super.awakeFromNib();
// Additional configuration
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func drawRect(rect: CGRect) {
// Drawing code
}
*/
}
================================================
FILE: StencilPlugin/Resources/Built-in Templates/Stencil/UIView.xctemplate/UIViewXIBiPhoneSwift/___FILEBASENAME___.xib
================================================
================================================
FILE: StencilPlugin/Resources/HeaderComments.sctemplate
================================================
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
================================================
FILE: StencilPlugin/Resources/StencilREADME
================================================
# Thanks for using Stencil to create a file template!
## Follow these instructions to continue creating your template
The ___FILEBASENAME___ files have been opened for you to edit.
These are your template files and they have been saved within your
project directory.
See "$SRC_ROOT/Stencil/File Templates/Custom/"
After editing these files, simply close the window and you're done.
You will then be able to use your new file template by selecting
"New File from Custom Template..." from the contextual menu in the
project navigator.
## Editing
Remove any code that is specific to the superclass or copied class.
Ensure you leave any ___FILEBASENAME___ or ___FILEBASENAMEASID___
references in the template files. These will be replaced when you
use your template to create a new file.
Things that you might want to remove are constant declarations,
properties and methods in the header, which are not needed, because
the new subclass will inherit.
You will probably want to completely clear out the implementation,
but it is all left there for you to decide. You may want to comment
out some of the methods that you expect to be overridden, and leave
the empty method definitions with a comment highlighting anything
special that should be done when overriding.
Remember to remove any unnecessary import statements from your
template files.
If you want to change the template files again later, you can
open them just like any other file and make changes. The changes
will be immediately visible to Xcode, without having to restart.
Look in "$SRC_ROOT/Stencil/File Templates/Custom/" to find your
template. Each template is in its own subdirectory with the
extension ".xctemplate", i.e. "MyCoolView.xctemplate".
## Using your new template
To use your new template, simply right click in the location of
the project navigator where you want to create a new file, or
select File->New from the main menu, as you would to create any
other file.
From the project navigator's contextual menu, select
"New File from Custom Template..."
Select the custom template you wish to use and continue as usual.
## Enjoy
This plugin (like all plugins) is intended to make your life
easier and avoid repetitive tasks.
If you want to contribute, fork the repo and make a pull request.
I'm happy for it to be extended in any beneficial way!
If you have questions or concerns, please speak to me on Twitter
@dodsios
Stencil is maintained by Sam Dods.
Copyright (c) 2015 Sam Dods. All rights reserved.
================================================
FILE: StencilPlugin/Resources/TemplateInfo.plist
================================================
Description
__STC_DESCRIPTION__
MainTemplateFile
___FILEBASENAME___
SortOrder
3
Platforms
com.apple.platform.iphoneos
================================================
FILE: StencilPlugin/SDK Fakes/ProjectFile.h
================================================
//
// ProjectFile.h
// StencilPlugin
//
// Created by Sam Dods on 19/04/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import
#import "ProjectGroup.h"
typedef NS_ENUM(NSInteger, ProjectFileType) {
ProjectFileUnknown,
ProjectFileObjcInterface,
ProjectFileObjcImplementation,
ProjectFileSwift,
ProjectFileUserInterface,
};
@protocol ProjectFile
@property (nonatomic, readonly) NSString *name;
@property (nonatomic, readonly) NSString *nameWithoutExtension;
@property (nonatomic, readonly) ProjectFileType type;
@property (nonatomic, readonly) NSString *extension;
@property (nonatomic, readonly) NSString *fullPath;
@property (nonatomic, readonly) NSURL *fileURL;
@property (nonatomic, readonly) id parentItem;
@end
================================================
FILE: StencilPlugin/SDK Fakes/ProjectFile.m
================================================
//
// ProjectFile.m
// StencilPlugin
//
// Created by Sam Dods on 19/04/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import "ProjectFile.h"
#import "DZLImplementationSafe.h"
@interface ProjectFile : NSObject
@end
@implementation ProjectFile
+ (void)load
{
dzl_implementationSafe(NSClassFromString(@"Xcode3FileReference"), self);
}
- (NSString *)fullPath
{
return [self valueForKeyPath:@"reference.resolvedAbsolutePath"];
}
- (NSURL *)fileURL
{
return [NSURL fileURLWithPath:self.fullPath];
}
- (NSString *)name
{
return self.fullPath.lastPathComponent;
}
- (NSString *)nameWithoutExtension
{
NSString *name = self.name;
NSRange rangeOfDot = [name rangeOfString:@"."];
return [name substringToIndex:rangeOfDot.location];
}
- (NSString *)extension
{
NSString *name = self.name;
NSRange rangeOfDot = [name rangeOfString:@"."];
return [name substringFromIndex:rangeOfDot.location];
}
- (ProjectFileType)type
{
NSString *extension = self.extension;
if ([extension isEqualToString:@".h"]) {
return ProjectFileObjcInterface;
} else if ([extension isEqualToString:@".m"]) {
return ProjectFileObjcImplementation;
} else if ([extension isEqualTo:@".swift"]) {
return ProjectFileSwift;
} else if ([extension isEqualToString:@".xib"] || [extension isEqualToString:@".storyboard"]) {
return ProjectFileUserInterface;
}
return ProjectFileUnknown;
}
@end
================================================
FILE: StencilPlugin/SDK Fakes/ProjectGroup.h
================================================
//
// ProjectGroup.h
// StencilPlugin
//
// Created by Sam Dods on 19/04/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import
typedef NS_ENUM(NSInteger, ProjectGroupErrorCode) {
ProjectGroupErrorCodeUnsupportedFileType = 33,
};
@protocol ProjectGroup
@property (nonatomic, readonly) NSString *name;
@property (nonatomic, readonly) NSArray *childRepresentedObjects;
@end
@interface NSArray (ProjectGroupAdditions)
- (NSArray *)validatedTemplateFiles:(NSError **)error;
@end
================================================
FILE: StencilPlugin/SDK Fakes/ProjectGroup.m
================================================
//
// ProjectGroup.m
// StencilPlugin
//
// Created by Sam Dods on 19/04/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import "ProjectGroup.h"
#import "ProjectFile.h"
@implementation NSArray (ProjectGroupAdditions)
- (NSArray *)validatedTemplateFiles:(NSError **)error
{
NSMutableArray *validatedFileRefs = [NSMutableArray new];
for (id file in [self valueForKey:@"representedObject"]) {
switch (file.type) {
case ProjectFileObjcInterface:
case ProjectFileObjcImplementation:
case ProjectFileUserInterface:
case ProjectFileSwift:
[validatedFileRefs addObject:file];
break;
default: {
NSString *message = [NSString stringWithFormat:@"Unsupported filetype (%@)", file.extension];
[self setError:error code:ProjectGroupErrorCodeUnsupportedFileType message:message];
return nil;
} break;
}
}
NSArray *fileRefs = validatedFileRefs.copy;
BOOL isValid = [self isNotMixingSwiftWithObjc:fileRefs];
return isValid ? fileRefs : nil;
}
- (BOOL)isNotMixingSwiftWithObjc:(NSArray *)fileRefs
{
BOOL hasSwift = NO;
BOOL hasObjc = NO;
for (id file in fileRefs) {
if (file.type == ProjectFileSwift) {
hasSwift = YES;
} else if (file.type != ProjectFileUserInterface) {
hasObjc = YES;
}
}
return !(hasObjc && hasSwift);
}
- (void)setError:(NSError **)error code:(NSInteger)code message:(NSString *)message
{
if (error) {
*error = [NSError errorWithDomain:@"com.sdods.Stencil" code:code userInfo:@{NSLocalizedDescriptionKey : message}];
}
}
@end
================================================
FILE: StencilPlugin/Stencil.h
================================================
//
// StencilPlugin.h
// StencilPlugin
//
// Created by Sam Dods on 17/04/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import
FOUNDATION_EXPORT NSString *const MenuItemTitleNewFileFromCustomTemplate;
FOUNDATION_EXPORT NSString *const MenuItemTitleFileFromCustomTemplate;
FOUNDATION_EXPORT NSString *const PluginNameAndCorrespondingDirectory;
FOUNDATION_EXPORT NSString *const FileTemplatesDirectoryPath;
@class TemplateConfig;
@interface Stencil : NSObject
+ (instancetype)sharedPlugin;
@property (nonatomic, readonly) BOOL canCreateFromCustomTemplate;
@property (nonatomic, readonly) NSString *projectRootPath;
@property (nonatomic, strong, readonly) NSBundle *pluginBundle;
@property (nonatomic, weak) NSMenuItem *menuItemNewFile;
@property (nonatomic, weak) NSMenuItem *menuItemNewFromCustomTemplate;
@property (nonatomic, weak) NSMenuItem *menuItemDelete;
@property (nonatomic, weak) NSMenuItem *menuItemCreateTemplateFromGroup;
@property (nonatomic, readonly) BOOL showCustomTemplatesOnly;
@property (nonatomic, assign) BOOL beginCreateTemplateFromGroup;
- (void)showTemplateOptionsInWindow:(NSWindow *)window defaultTemplateConfig:(TemplateConfig *)config;
- (void)observeHighlightedItemForMenu:(NSMenu *)menu;
@end
================================================
FILE: StencilPlugin/Stencil.m
================================================
//
// StencilPlugin.m
// StencilPlugin
//
// Created by Sam Dods on 17/04/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import "Stencil.h"
#import "DZLImplementationCombine.h"
#import "NSMenu+StencilAdditions.h"
#import "TemplateOptionsWindow.h"
#import "TemplateFactory.h"
#import "StencilWeakObjectWrapper.h"
#import "DeallocationObserver.h"
static void *StencilMenuObserver = &StencilMenuObserver;
NSString *const MenuItemTitleNewFileFromCustomTemplate = @"New File from Custom Template…";
NSString *const MenuItemTitleFileFromCustomTemplate = @"File from Custom Template…";
NSString *const PluginNameAndCorrespondingDirectory = @"StencilPlugin";
NSString *const FileTemplatesDirectoryPath = @"File Templates/Custom";
static Stencil *sharedPlugin;
static BOOL ForceShowTemplatesOnly = NO;
@interface NSObject (IDETemplate_Additions)
+ (id)availableTemplatesOfTemplateKind:(id)kind;
@end
@interface Stencil ()
@property (nonatomic, assign) BOOL projectNavigatorContextualMenuIsOpened;
@property (nonatomic, readwrite) BOOL showCustomTemplatesOnly;
@property (nonatomic, strong) NSMutableSet *observedMenus;
@end
@implementation Stencil
+ (instancetype)sharedPlugin
{
return sharedPlugin;
}
- (NSMutableSet *)observedMenus
{
return _observedMenus ?: (_observedMenus = [NSMutableSet new]);
}
+ (void)pluginDidLoad:(NSBundle *)plugin
{
static dispatch_once_t onceToken;
NSString *currentApplicationName = [[NSBundle mainBundle] infoDictionary][@"CFBundleName"];
if ([currentApplicationName isEqual:@"Xcode"]) {
dispatch_once(&onceToken, ^{
sharedPlugin = [[self alloc] initWithBundle:plugin];
});
}
}
- (id)initWithBundle:(NSBundle *)pluginBundle
{
if (!(self = [super init])) {
return nil;
}
_pluginBundle = pluginBundle;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didFinishLaunching:) name:NSApplicationDidFinishLaunchingNotification object:nil];
return self;
}
- (void)didFinishLaunching:(NSNotification *)notification
{
[self updateMainMenuItems];
}
- (void)updateMainMenuItems
{
NSMenuItem *menuItem = [[NSApp mainMenu] itemWithTitle:@"File"];
[[menuItem submenu] itemWithTitle:@"New"];
NSMenu *menuNew = [[[menuItem submenu] itemWithTitle:@"New"] submenu];
[self observeHighlightedItemForMenu:menuNew];
[menuNew duplicateItemWithTitle:@"File…" duplicateTitle:MenuItemTitleFileFromCustomTemplate];
}
- (BOOL)canCreateFromCustomTemplate
{
NSString *projectRootPath = [self projectRootPath];
NSString *stencilDirectory = [projectRootPath stringByAppendingPathComponent:PluginNameAndCorrespondingDirectory];
NSString *customTemplatesDirectory = [stencilDirectory stringByAppendingPathComponent:FileTemplatesDirectoryPath];
NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:customTemplatesDirectory error:nil];
for (NSString *fileOrDir in contents) {
if ([fileOrDir hasSuffix:@".xctemplate"]) {
NSString *path = [customTemplatesDirectory stringByAppendingPathComponent:fileOrDir];
return [self hasAvailableTemplatesAtPath:path];
}
}
return NO;
}
- (BOOL)hasAvailableTemplatesAtPath:(NSString *)path
{
BOOL isDir = NO;
if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) {
id kind = [NSClassFromString(@"IDETemplateKind") valueForKey:@"fileTemplateKind"];
ForceShowTemplatesOnly = YES;
BOOL result = [[NSClassFromString(@"IDETemplate") availableTemplatesOfTemplateKind:kind] count] > 0;
ForceShowTemplatesOnly = NO;
return result;
}
return NO;
}
- (NSString *)projectRootPath
{
NSArray *workspaceWindowControllers = [NSClassFromString(@"IDEWorkspaceWindowController") valueForKey:@"workspaceWindowControllers"];
id workSpace;
for (id controller in workspaceWindowControllers) {
if ([[controller valueForKey:@"window"] isEqual:[NSApp mainWindow]]) {
workSpace = [controller valueForKey:@"_workspace"];
}
}
return [[[workSpace valueForKey:@"representingFilePath"] valueForKey:@"pathString"] stringByDeletingLastPathComponent];
}
#pragma mark - NSMenu observing
- (void)observeHighlightedItemForMenu:(NSMenu *)menu
{
StencilWeakObjectWrapper *wrapper = [StencilWeakObjectWrapper wrap:menu];
if ([self.observedMenus containsObject:wrapper]) {
return;
}
[menu addObserver:self forKeyPath:@"highlightedItem" options:NSKeyValueObservingOptionNew context:StencilMenuObserver];
[self.observedMenus addObject:wrapper];
DeallocationObserver *observer = [DeallocationObserver new];
observer.observedObject = menu;
__unsafe_unretained NSMenu *unsafeMenu = menu;
observer.deallocBlock = ^(NSMenu *deallocatedMenu) {
[unsafeMenu removeObserver:self forKeyPath:@"highlightedItem" context:StencilMenuObserver];
[self.observedMenus removeObject:wrapper];
};
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(NSMenu *)menu change:(NSDictionary *)change context:(void *)context
{
if (context != StencilMenuObserver) {
return;
}
NSMenuItem *item = change[NSKeyValueChangeNewKey];
if (![item isKindOfClass:[NSMenuItem class]]) {
return;
}
self.showCustomTemplatesOnly = ([item.title isEqualToString:MenuItemTitleFileFromCustomTemplate] || [item.title isEqualToString:MenuItemTitleNewFileFromCustomTemplate]);
self.beginCreateTemplateFromGroup = (item == self.menuItemCreateTemplateFromGroup);
if (item == self.menuItemNewFromCustomTemplate) {
item.action = self.menuItemNewFile.action;
}
}
- (BOOL)showCustomTemplatesOnly
{
return ForceShowTemplatesOnly || _showCustomTemplatesOnly;
}
#pragma mark - displaying template options
- (void)showTemplateOptionsInWindow:(NSWindow *)window defaultTemplateConfig:(TemplateConfig *)config
{
NSArray *topLevelObjects = nil;
[self.pluginBundle loadNibNamed:@"StencilTemplateWindow" owner:self topLevelObjects:&topLevelObjects];
TemplateOptionsWindow *templateOptionsWindow = [[topLevelObjects filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
return [evaluatedObject isKindOfClass:[TemplateOptionsWindow class]];
}]] firstObject];
BOOL isTemplateOptionsWindow = [templateOptionsWindow isKindOfClass:[TemplateOptionsWindow class]];
NSAssert(isTemplateOptionsWindow, @"Error loading from nib");
if (!isTemplateOptionsWindow) {
return;
}
templateOptionsWindow.templateConfig = config;
templateOptionsWindow.completionDelegate = self;
[[NSApp keyWindow] beginSheet:templateOptionsWindow completionHandler:^(NSModalResponse returnCode){
if (returnCode == NSModalResponseStop || returnCode == NSModalResponseAbort)
{
[self templateOptionsWindowDidCancel:templateOptionsWindow];
} else {
// NSModalResponseContinue
[self templateOptionsWindowDidCompleteOK:templateOptionsWindow];
}
}];
}
- (void)templateOptionsWindowDidCompleteOK:(TemplateOptionsWindow *)window
{
[[TemplateFactory defaultFactory] generateTemplateFromConfig:window.templateConfig];
[[NSApp mainWindow] endSheet:window];
}
- (void)templateOptionsWindowDidCancel:(TemplateOptionsWindow *)window
{
[[NSApp mainWindow] endSheet:window];
}
- (void)sheetDidEnd:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
{
[sheet orderOut:self];
}
@end
================================================
FILE: StencilPlugin/User Interface/Template Options/StencilTemplateWindow.xib
================================================
================================================
FILE: StencilPlugin/User Interface/Template Options/TemplateOptionsWindow.h
================================================
//
// TemplateOptionsWindow.h
// StencilPlugin
//
// Created by Sam Dods on 20/04/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import
@class TemplateConfig;
@class TemplateOptionsWindow;
@protocol TemplateOptionsWindowDelegate
- (void)templateOptionsWindowDidCompleteOK:(TemplateOptionsWindow *)window;
- (void)templateOptionsWindowDidCancel:(TemplateOptionsWindow *)window;
@end
@interface TemplateOptionsWindow : NSWindow
@property (nonatomic, weak) id completionDelegate;
@property (nonatomic, strong) TemplateConfig *templateConfig;
@end
================================================
FILE: StencilPlugin/User Interface/Template Options/TemplateOptionsWindow.m
================================================
//
// TemplateOptionsWindow.m
// StencilPlugin
//
// Created by Sam Dods on 20/04/2015.
// Copyright (c) 2015 Sam Dods. All rights reserved.
//
#import "TemplateOptionsWindow.h"
#import "TemplateConfig.h"
#import "ThingTypeToClassNamesMap.h"
#import "ProjectFile.h"
#import "TemplateFactory.h"
@interface TemplateConfig ()
@property (nonatomic, readwrite) TemplateProperties *properties;
@end
@interface TemplateOptionsWindow ()
@property (weak) IBOutlet NSPopUpButton *templateFromPopUpButton;
@property (weak) IBOutlet NSPopUpButton *inheritFromPopUpButton;
@property (weak) IBOutlet NSTextField *descriptionTextField;
@property (weak) IBOutlet NSTextField *templateNameTextField;
@property (weak) IBOutlet NSButton *okButton;
@property (weak) IBOutlet NSTableView *tableView;
@property (weak) IBOutlet NSTextField *findTextField;
@property (weak) IBOutlet NSTextField *replaceTextField;
@property (weak) IBOutlet NSButton *advancedButton;
@property (weak) IBOutlet NSButton *customReplaceEnabledButton;
@end
@implementation TemplateOptionsWindow
- (void)awakeFromNib
{
[super awakeFromNib];
[self enableOrDisableOKButton];
}
- (void)setTemplateConfig:(TemplateConfig *)templateConfig
{
_templateConfig = templateConfig;
[self createTemplateChoicePopUpMenu];
[self updateInheritChoicePopUp];
self.templateNameTextField.stringValue = self.inheritFromPopUpButton.title;
self.findTextField.stringValue = self.inheritFromPopUpButton.title;
}
#pragma mark - menu creation
- (void)createTemplateChoicePopUpMenu
{
NSMenu *menu = [NSMenu new];
for (ThingTypeToClassNamesMap *map in self.templateConfig.thingTypeToNamesMaps) {
NSString *title = [NSString stringWithFormat:@"%@ %@", map.thingTypeString, map.names.firstObject];
[menu addItemWithTitle:title action:nil keyEquivalent:@""];
}
self.templateFromPopUpButton.menu = menu;
[self.templateFromPopUpButton selectItemAtIndex:0];
}
- (void)createInheritChoicePopUpMenuFromMap:(ThingTypeToClassNamesMap *)map
{
NSMenu *menu = [NSMenu new];
for (NSString *title in map.names) {
[menu addItemWithTitle:title action:nil keyEquivalent:@""];
}
self.inheritFromPopUpButton.menu = menu;
[self.inheritFromPopUpButton selectItemAtIndex:0];
}
#pragma mark - actions
- (IBAction)didTapOK:(NSButton *)sender
{
ThingTypeToClassNamesMap *map = self.templateConfig.thingTypeToNamesMaps[self.templateFromPopUpButton.indexOfSelectedItem];
NSString *inheritFrom = self.inheritFromPopUpButton.selectedItem.title;
NSDictionary *fileMap = [self templateFileMap];
if (!fileMap) {
[[TemplateFactory defaultFactory] showAlertWithMessage:@"Multiple files cannot be cast to the same output template filename."];
return;
}
self.templateConfig.properties = [[TemplateProperties alloc] initWithName:self.templateNameTextField.stringValue thingType:map.thingType nameToReplace:map.names.firstObject inheritFrom:inheritFrom description:self.descriptionTextField.stringValue templateFileMap:fileMap replaceByFind:[self replacementTextByFindText]];
[self.completionDelegate templateOptionsWindowDidCompleteOK:self];
}
- (IBAction)didTapCancel:(id)sender
{
[self.completionDelegate templateOptionsWindowDidCancel:self];
}
#pragma mark - handling changes
- (IBAction)templateChoiceChanged:(NSPopUpButton *)popUpButton
{
[self updateInheritChoicePopUp];
ThingTypeToClassNamesMap *map = self.templateConfig.thingTypeToNamesMaps[self.templateFromPopUpButton.indexOfSelectedItem];
self.findTextField.stringValue = map.names.firstObject;
}
- (IBAction)inheritChoiceChanged:(NSPopUpButton *)sender
{
self.templateNameTextField.stringValue = sender.title;
}
- (IBAction)didChangeAdvanced:(NSButton *)advancedButton
{
self.findTextField.enabled = advancedButton.state;
self.customReplaceEnabledButton.enabled = advancedButton.state;
self.replaceTextField.enabled = self.customReplaceEnabledButton.state;
}
- (IBAction)didChangeCustomReplaceEnabled:(NSButton *)customReplaceButton
{
self.replaceTextField.enabled = customReplaceButton.state;
if (!self.replaceTextField.enabled) {
self.replaceTextField.stringValue = @"___FILEBASENAMEASIDENTIFIER___";
}
}
- (void)updateInheritChoicePopUp
{
if (self.templateFromPopUpButton.indexOfSelectedItem >= self.templateConfig.thingTypeToNamesMaps.count) {
return;
}
ThingTypeToClassNamesMap *map = self.templateConfig.thingTypeToNamesMaps[self.templateFromPopUpButton.indexOfSelectedItem];
[self createInheritChoicePopUpMenuFromMap:map];
[self inheritChoiceChanged:self.inheritFromPopUpButton];
}
- (void)controlTextDidChange:(NSNotification *)obj
{
[self enableOrDisableOKButton];
}
- (void)enableOrDisableOKButton
{
self.okButton.enabled = self.templateNameTextField.stringValue.length && self.templateFromPopUpButton.selectedItem != nil && self.inheritFromPopUpButton.selectedItem != nil && self.descriptionTextField.stringValue.length;
}
#pragma mark - NSTableViewDataSource
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
return [self fileNamesWithoutExtension].count;
}
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
NSTextField *textField = [tableView makeViewWithIdentifier:@"cell" owner:self];
if (!textField) {
textField = [NSTextField new];
textField.identifier = @"cell";
}
textField.editable = tableColumn.editable;
textField.selectable = textField.editable;
textField.bordered = NO;
NSString *filename = self.fileNamesWithoutExtension[row];
if ([tableColumn.identifier isEqualToString:@"template"]) {
filename = @"___FILEBASENAME___";
}
textField.stringValue = filename;
textField.toolTip = textField.stringValue;
textField.drawsBackground = NO;
return textField;
}
#pragma mark - multiple file support
- (NSDictionary *)templateFileMap
{
NSMutableDictionary *map = [NSMutableDictionary new];
for (NSUInteger rowIndex = 0; rowIndex < self.tableView.numberOfRows; rowIndex++) {
NSTextField *originalFilenameTextField = [self.tableView viewAtColumn:0 row:rowIndex makeIfNecessary:NO];
NSTextField *templateFilenameTextField = [self.tableView viewAtColumn:1 row:rowIndex makeIfNecessary:NO];
if ([map.allValues containsObject:templateFilenameTextField.stringValue]) {
return nil;
}
map[originalFilenameTextField.stringValue] = templateFilenameTextField.stringValue;
}
return map;
}
- (NSArray *)fileNamesWithoutExtension
{
NSArray *names = [self.templateConfig.fileRefs valueForKey:@"nameWithoutExtension"];
NSSet *set = [NSSet setWithArray:names];
return set.allObjects;
}
- (NSDictionary *)replacementTextByFindText
{
return self.advancedButton.state ? @{self.findTextField.stringValue : self.replaceTextField.stringValue} : nil;
}
@end
================================================
FILE: StencilPlugin/Vendor/DZLObjcAdditions/DZLClassSingleton.h
================================================
//
// DZLClassSingleton.h
// DZLObjcAdditions
//
// Created by Sam Dods on 23/05/2014.
// Copyright (c) 2014 Sam Dods. All rights reserved.
//
@interface NSObject ()
@property (nonatomic, strong) NSString *dzl_class_singleton;
@end
#define class_singleton(class, methodName) \
dynamic dzl_class_singleton; \
+ (class *)methodName { \
static class *methodName; \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
methodName = [class new]; \
}); \
return methodName; \
}
#define class_singleton_setup(class, methodName, setupCode) \
dynamic dzl_class_singleton; \
+ (class *)methodName { \
static class *methodName; \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
methodName = [class new]; \
({setupCode;});\
}); \
return methodName; \
}
================================================
FILE: StencilPlugin/Vendor/DZLObjcAdditions/DZLImplementationCombine.h
================================================
//
// DZLImplementationCombine.h
// DZLObjcAdditions
//
// Created by Sam Dods on 15/05/2014.
// Copyright (c) 2014 Sam Dods. All rights reserved.
//
#import "NSObject+DZLObjcAdditions.h"
#import "DZLSuper.h"
#define dzlCombine(args) \
({ [(typeof(self))[DZLObjcAdditions proxyForObject:self class:self.class toForwardSelector:_cmd] args]; })
#define dzlCanCombine() \
({ [DZLObjcAdditions proxyForObject:self class:self.class respondsToSelector:_cmd]; })
extern NSInteger const dzl_no_assert;
#define implementation_combine(klass, name, args...) \
interface DZLImplementationCombine_ ## klass ## name : klass @end \
@implementation DZLImplementationCombine_ ## klass ## name \
+ (void)load { dzl_implementationCombine(klass.class, self, ##args); } @end \
@implementation DZLImplementationCombine_ ## klass ## name (Additions)
================================================
FILE: StencilPlugin/Vendor/DZLObjcAdditions/DZLImplementationSafe.h
================================================
//
// DZLImplementationSafe
// DZLObjcAdditions
//
// Created by Sam Dods on 15/05/2014.
// Copyright (c) 2014 Sam Dods. All rights reserved.
//
#import "NSObject+DZLObjcAdditions.h"
#import "DZLSuper.h"
#define implementation_safe(klass, name) \
interface DZLImplementationSafe_ ## klass ## name : klass @end \
@implementation DZLImplementationSafe_ ## klass ## name \
+ (void)load { dzl_implementationSafe(klass.class, self); } @end \
@implementation DZLImplementationSafe_ ## klass ## name (Additions)
================================================
FILE: StencilPlugin/Vendor/DZLObjcAdditions/DZLProtocolImplementation.h
================================================
//
// DZLProtocolImplementation.h
// DZLObjcAdditions
//
// Created by Sam Dods on 15/05/2014.
// Copyright (c) 2014 Sam Dods. All rights reserved.
//
#import "NSObject+DZLObjcAdditions.h"
#define protocol_implementation(name) \
interface DZLProtocolImplementation_ ## name : NSObject @end \
@implementation DZLProtocolImplementation_ ## name \
+ (void)load { [DZLObjcAdditions mixinAllIfNecessary]; } @end \
@implementation DZLProtocolImplementation_ ## name (Additions)
================================================
FILE: StencilPlugin/Vendor/DZLObjcAdditions/DZLSynthesizeLazy.h
================================================
//
// DZLSynthesizeLazy.h
// DZLObjcAdditions
//
// Created by Sam Dods on 23/05/2014.
// Copyright (c) 2014 Sam Dods. All rights reserved.
//
#define synthesize_lazy(type, propertyName) synthesize propertyName = _ ## propertyName; \
- (type *)propertyName { \
return _ ## propertyName ?: (_ ## propertyName = [type new]); \
}
================================================
FILE: StencilPlugin/Vendor/DZLObjcAdditions/Private/DZLSuper.h
================================================
//
// DZLSuper.h
// DZLObjcAdditions
//
// Created by Sam Dods on 17/06/2014.
// Copyright (c) 2014 Sam Dods. All rights reserved.
//
#define dzlSuper(args) \
({ if(NO) { [super args]; } \
[(typeof(self))[DZLObjcAdditions proxyForObject:self class:self.class toForwardSelector:_cmd] args]; })
================================================
FILE: StencilPlugin/Vendor/DZLObjcAdditions/Private/NSObject+DZLObjcAdditions.h
================================================
//
// NSObject+DZLObjcAdditions.h
// DZLObjcAdditions
//
// Created by Sam Dods on 15/05/2014.
// Copyright (c) 2014 Sam Dods. All rights reserved.
//
#import
extern void dzl_implementationSafe(id self, Class aClass);
extern void __attribute__((overloadable)) dzl_implementationCombine(id self, Class aClass);
extern void __attribute__((overloadable)) dzl_implementationCombine(id self, Class aClass, NSInteger shouldAssert);
@interface DZLObjcAdditions : NSProxy
+ (instancetype)proxyForObject:(id)object class:(Class)class toForwardSelector:(SEL)selector;
+ (BOOL)proxyForObject:(id)object class:(Class)class respondsToSelector:(SEL)selector;
@end
@interface DZLObjcAdditions (MixinProtocol)
+ (void)mixinAllIfNecessary;
@end
================================================
FILE: StencilPlugin/Vendor/DZLObjcAdditions/Private/NSObject+DZLObjcAdditions.m
================================================
//
// NSObject+DZLObjcAdditions.m
// DZLObjcAdditions
//
// Created by Sam Dods on 15/05/2014.
// Copyright (c) 2014 Sam Dods. All rights reserved.
//
#import
#import "NSObject+DZLObjcAdditions.h"
#import "DZLClassSingleton.h"
#import "DZLImplementationCombine.h"
NSInteger const dzl_no_assert = 37834;
static const void * const DZLObjcAdditionsSuperCountKey = &DZLObjcAdditionsSuperCountKey;
@interface DZLObjcAdditions ()
@property (nonatomic, strong) NSString *dzl_class_singleton;
@property (nonatomic, strong) id object;
@property (nonatomic, assign) Class class;
@property (nonatomic, assign) SEL selector;
@end
@implementation DZLObjcAdditions
@class_singleton(NSMutableDictionary, underlyingSelectorByReplacementSelector);
+ (void)setUnderlyingSelector:(SEL)underlyingSelector forSelector:(SEL)selector class:(Class)aClass
{
NSString *key = [self replacementSelectorNameForSelector:selector class:aClass];
NSString *underlyingName = NSStringFromSelector(underlyingSelector);
NSString *existingName = self.underlyingSelectorByReplacementSelector[key];
if (existingName) {
[self setUnderlyingSelector:NSSelectorFromString(existingName) forSelector:underlyingSelector class:aClass];
}
self.underlyingSelectorByReplacementSelector[key] = underlyingName;
}
+ (SEL)underlyingSelectorForSelector:(SEL)selector class:(Class)targetClass
{
NSString *underlyingName = nil;
while (targetClass != Nil && underlyingName == nil) {
NSString *key = [self replacementSelectorNameForSelector:selector class:targetClass];
underlyingName = self.underlyingSelectorByReplacementSelector[key];
targetClass = targetClass.superclass;
}
return NSSelectorFromString(underlyingName);
}
+ (NSString *)replacementSelectorNameForSelector:(SEL)selector class:(Class)aClass
{
return [NSString stringWithFormat:@"%@_%@", NSStringFromClass(aClass), NSStringFromSelector(selector)];
}
+ (instancetype)proxyForObject:(id)object class:(Class)class toForwardSelector:(SEL)selector
{
DZLObjcAdditions *proxy = [DZLObjcAdditions alloc];
proxy.object = object;
proxy.class = class;
proxy.selector = selector;
return proxy;
}
+ (BOOL)proxyForObject:(id)object class:(Class)class respondsToSelector:(SEL)selector
{
SEL targetSelector = [DZLObjcAdditions underlyingSelectorForSelector:selector class:class];
return [object respondsToSelector:targetSelector];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
return [self.object methodSignatureForSelector:selector];
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
Class targetClass = [self targetClassForUnderlyingSelector];
invocation.selector = [DZLObjcAdditions underlyingSelectorForSelector:self.selector class:targetClass];
if ([self.object respondsToSelector:invocation.selector]) {
[self modifySuperCountByDelta:1];
[invocation invokeWithTarget:self.object];
[self modifySuperCountByDelta:-1];
}
NSString *name = NSStringFromSelector(self.selector);
if (![name isEqualToString:@"init"] && [name rangeOfString:@"initWith"].location != 0) {
self.object = nil;
}
}
- (Class)targetClassForUnderlyingSelector
{
Class targetClass = self.class;
for (NSInteger superCount = 0; superCount < self.currentSuperCount; superCount++) {
if (targetClass.superclass == Nil) {
return targetClass;
}
targetClass = targetClass.superclass;
}
return targetClass;
}
- (NSInteger)currentSuperCount
{
NSMutableDictionary *superCountBySelector = [self superCountBySelector];
NSString *key = NSStringFromSelector(self.selector);
return [superCountBySelector[key] integerValue];
}
- (void)modifySuperCountByDelta:(NSInteger)delta
{
@synchronized(self.object)
{
NSMutableDictionary *superCountBySelector = [self superCountBySelector];
NSString *key = NSStringFromSelector(self.selector);
NSInteger superCount = [superCountBySelector[key] integerValue];
superCount += delta;
if (superCount == 0) {
[superCountBySelector removeObjectForKey:key];
} else {
superCountBySelector[key] = @(superCount);
}
objc_setAssociatedObject(self.object, DZLObjcAdditionsSuperCountKey, superCountBySelector.count ? superCountBySelector : nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
}
- (NSMutableDictionary *)superCountBySelector
{
return objc_getAssociatedObject(self.object, DZLObjcAdditionsSuperCountKey) ?: [NSMutableDictionary new];
}
@end
@implementation NSObject (DZLObjcAdditions)
+ (void)dzl_implementMethodsFromClass:(Class)aClass overrideSuper:(BOOL)shouldOverrideSuper replace:(BOOL)shouldReplace shouldAssert:(BOOL)shouldAssert
{
[self dzl_doImplementMethodsFromClass:aClass overrideSuper:shouldOverrideSuper replace:shouldReplace shouldAssert:shouldAssert];
[object_getClass(self) dzl_doImplementMethodsFromClass:object_getClass(aClass) overrideSuper:shouldOverrideSuper replace:shouldReplace shouldAssert:shouldAssert];
}
+ (void)dzl_doImplementMethodsFromClass:(Class)aClass overrideSuper:(BOOL)shouldOverrideSuper replace:(BOOL)shouldReplace shouldAssert:(BOOL)shouldAssert
{
uint numberOfMethods;
Method *methods = class_copyMethodList(aClass, &numberOfMethods);
for (uint m = 0; m < numberOfMethods; m++) {
Method method = methods[m];
SEL name = method_getName(method);
const char *types = method_getTypeEncoding(method);
BOOL instancesRespond = [self instancesRespondToSelector:name];
NSAssert(!shouldAssert || !shouldReplace || instancesRespond, @"Can't combine with non-existent selector '%@' on class %@", NSStringFromSelector(name), NSStringFromClass(self));
if (!shouldReplace && !shouldOverrideSuper && instancesRespond) {
continue;
}
IMP imp = method_getImplementation(method);
[self backupSelector:name forClass:aClass objcTypes:types];
if (shouldReplace) {
class_replaceMethod(self, name, imp, types);
} else {
class_addMethod(self, name, imp, types);
}
}
free(methods);
}
+ (void)backupSelector:(SEL)name forClass:(Class)aClass objcTypes:(const char *)types
{
if (![self instancesRespondToSelector:name] && ![self respondsToSelector:name]) {
return;
}
IMP orgImp = class_getMethodImplementation(self, name);
SEL newName = NSSelectorFromString([DZLObjcAdditions replacementSelectorNameForSelector:name class:aClass]);
class_addMethod(self, newName, orgImp, types);
[DZLObjcAdditions setUnderlyingSelector:newName forSelector:name class:self];
}
@end
@implementation DZLObjcAdditions (MixinProtocol)
+ (void)mixinAllIfNecessary
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self mixinAll];
});
}
+ (void)mixinAll
{
uint numberOfClasses;
Class *classes = objc_copyClassList(&numberOfClasses);
for (uint c = 0; c < numberOfClasses; c++) {
Class targetClass = classes[c];
uint numberOfProtocols;
__unsafe_unretained Protocol **protocols = class_copyProtocolList(targetClass, &numberOfProtocols);
for (uint p = 0; p < numberOfProtocols; p++) {
Protocol *protocol = protocols[p];
[self mixinProtocol:protocol toClass:targetClass];
}
free(protocols);
}
free(classes);
}
+ (void)mixinProtocol:(Protocol *)mixinProtocol toClass:(Class)targetClass
{
NSString *className = [NSString stringWithFormat:@"DZLProtocolImplementation_%@", NSStringFromProtocol(mixinProtocol)];
Class mixinClass = NSClassFromString(className);
if (mixinClass && mixinClass != targetClass && [targetClass isSubclassOfClass:NSObject.class]) {
[targetClass dzl_implementMethodsFromClass:mixinClass overrideSuper:NO replace:NO shouldAssert:YES];
}
}
@end
void dzl_implementationSafe(id self, Class aClass)
{
[self dzl_implementMethodsFromClass:aClass overrideSuper:YES replace:NO shouldAssert:YES];
}
void __attribute__((overloadable)) dzl_implementationCombine(id self, Class aClass)
{
dzl_implementationCombine(self, aClass, YES);
}
void __attribute__((overloadable)) dzl_implementationCombine(id self, Class aClass, NSInteger shouldAssert)
{
[self dzl_implementMethodsFromClass:aClass overrideSuper:NO replace:YES shouldAssert:(shouldAssert != dzl_no_assert)];
}
================================================
FILE: StencilPlugin.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
EA0000D01AE7A56800E85807 /* ThingTypeToClassNamesMap.m in Sources */ = {isa = PBXBuildFile; fileRef = EA0000CF1AE7A56800E85807 /* ThingTypeToClassNamesMap.m */; };
EA23E0461AF4D8D6008E4A50 /* StencilWeakObjectWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = EA23E0451AF4D8D6008E4A50 /* StencilWeakObjectWrapper.m */; };
EA23E0491AF4D8F1008E4A50 /* DeallocationObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = EA23E0481AF4D8F1008E4A50 /* DeallocationObserver.m */; };
EA31BC391AE1AFDE00167EFD /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA31BC381AE1AFDE00167EFD /* AppKit.framework */; };
EA31BC3B1AE1AFDE00167EFD /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA31BC3A1AE1AFDE00167EFD /* Foundation.framework */; };
EA3EB1B51AE39510009553F9 /* TemplateInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = EA3EB1B41AE39510009553F9 /* TemplateInfo.plist */; };
EA91AC7B1AF75EBC005B4E74 /* NSObject+DZLObjcAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = EA91AC7A1AF75EBC005B4E74 /* NSObject+DZLObjcAdditions.m */; };
EACD8A4C1AE6C5B500706E6B /* TemplateConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = EA3D3DBC1AE57D61007BD36A /* TemplateConfig.m */; };
EACD8A4D1AE6C5B900706E6B /* TemplateOptionsWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = EA3D3DB31AE56BDC007BD36A /* TemplateOptionsWindow.m */; };
EACD8A4F1AE6C5BF00706E6B /* ProjectGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = EA4FB6841AE398E5001EFB21 /* ProjectGroup.m */; };
EACD8A501AE6C5C100706E6B /* ProjectFile.m in Sources */ = {isa = PBXBuildFile; fileRef = EA4FB6871AE399A3001EFB21 /* ProjectFile.m */; };
EACD8A521AE6C5CC00706E6B /* NSInputStream+StencilAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = EA4FB6921AE4D44C001EFB21 /* NSInputStream+StencilAdditions.m */; };
EACD8A531AE6C5CE00706E6B /* NSOutputStream+StencilAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = EA4FB6951AE4D768001EFB21 /* NSOutputStream+StencilAdditions.m */; };
EACD8A541AE6C5D100706E6B /* NSString+StencilRegex.m in Sources */ = {isa = PBXBuildFile; fileRef = EA3D3DAA1AE5638E007BD36A /* NSString+StencilRegex.m */; };
EACD8A551AE6C5D300706E6B /* TemplateFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = EA3D3DC41AE5867F007BD36A /* TemplateFactory.m */; };
EACD8A561AE6C5D600706E6B /* NSMenu+StencilAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = EAB55BA31AE2D519001EBD12 /* NSMenu+StencilAdditions.m */; };
EACD8A571AE6C5D900706E6B /* NSObject+StencilAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = EA3EB1A71AE2D6F4009553F9 /* NSObject+StencilAdditions.m */; };
EACD8A581AE6C5DC00706E6B /* IDETemplateAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = EA3EB1AD1AE2D861009553F9 /* IDETemplateAdditions.m */; };
EACD8A591AE6C5DF00706E6B /* IDEStructureNavigatorAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = EA3EB1B01AE2D90B009553F9 /* IDEStructureNavigatorAdditions.m */; };
EACD8A5A1AE6C5E300706E6B /* Stencil.m in Sources */ = {isa = PBXBuildFile; fileRef = EA31BC421AE1AFDE00167EFD /* Stencil.m */; };
EACD8A5B1AE6C63F00706E6B /* HeaderComments.sctemplate in Resources */ = {isa = PBXBuildFile; fileRef = EA4FB67F1AE39543001EFB21 /* HeaderComments.sctemplate */; };
EAD6226B1AE83563002723B6 /* StencilTemplateWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = EAD6226A1AE83563002723B6 /* StencilTemplateWindow.xib */; };
EAD85AD51AEEBBBC00415E84 /* Stencil in Resources */ = {isa = PBXBuildFile; fileRef = EAD85AD41AEEBBBC00415E84 /* Stencil */; };
EADE959D1AF152CA00C30880 /* Stencil in CopyFiles */ = {isa = PBXBuildFile; fileRef = EAD85AD41AEEBBBC00415E84 /* Stencil */; };
EAF198331AE82B8A005A5BC0 /* StencilREADME in Resources */ = {isa = PBXBuildFile; fileRef = EAF198321AE82B8A005A5BC0 /* StencilREADME */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
EADE959C1AF150FA00C30880 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "Library/Developer/Xcode/Templates/File Templates";
dstSubfolderSpec = 0;
files = (
EADE959D1AF152CA00C30880 /* Stencil in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
EA0000CE1AE7A56800E85807 /* ThingTypeToClassNamesMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThingTypeToClassNamesMap.h; sourceTree = ""; };
EA0000CF1AE7A56800E85807 /* ThingTypeToClassNamesMap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThingTypeToClassNamesMap.m; sourceTree = ""; };
EA23E0441AF4D8D6008E4A50 /* StencilWeakObjectWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StencilWeakObjectWrapper.h; sourceTree = ""; };
EA23E0451AF4D8D6008E4A50 /* StencilWeakObjectWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StencilWeakObjectWrapper.m; sourceTree = ""; };
EA23E0471AF4D8F1008E4A50 /* DeallocationObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DeallocationObserver.h; sourceTree = ""; };
EA23E0481AF4D8F1008E4A50 /* DeallocationObserver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DeallocationObserver.m; sourceTree = ""; };
EA31BC351AE1AFDE00167EFD /* StencilPlugin.xcplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StencilPlugin.xcplugin; sourceTree = BUILT_PRODUCTS_DIR; };
EA31BC381AE1AFDE00167EFD /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; };
EA31BC3A1AE1AFDE00167EFD /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; };
EA31BC3E1AE1AFDE00167EFD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
EA31BC3F1AE1AFDE00167EFD /* StencilPlugin.xcscheme */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = StencilPlugin.xcscheme; path = StencilPlugin.xcodeproj/xcshareddata/xcschemes/StencilPlugin.xcscheme; sourceTree = SOURCE_ROOT; };
EA31BC411AE1AFDE00167EFD /* Stencil.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Stencil.h; sourceTree = ""; };
EA31BC421AE1AFDE00167EFD /* Stencil.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Stencil.m; sourceTree = ""; tabWidth = 2; wrapsLines = 1; };
EA3D3DA91AE5638E007BD36A /* NSString+StencilRegex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+StencilRegex.h"; sourceTree = ""; };
EA3D3DAA1AE5638E007BD36A /* NSString+StencilRegex.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+StencilRegex.m"; sourceTree = ""; };
EA3D3DB21AE56BDC007BD36A /* TemplateOptionsWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TemplateOptionsWindow.h; sourceTree = ""; };
EA3D3DB31AE56BDC007BD36A /* TemplateOptionsWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TemplateOptionsWindow.m; sourceTree = ""; };
EA3D3DBB1AE57D61007BD36A /* TemplateConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TemplateConfig.h; sourceTree = ""; };
EA3D3DBC1AE57D61007BD36A /* TemplateConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TemplateConfig.m; sourceTree = ""; };
EA3D3DC31AE5867F007BD36A /* TemplateFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TemplateFactory.h; sourceTree = ""; };
EA3D3DC41AE5867F007BD36A /* TemplateFactory.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TemplateFactory.m; sourceTree = ""; };
EA3EB1A61AE2D6F4009553F9 /* NSObject+StencilAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+StencilAdditions.h"; sourceTree = ""; };
EA3EB1A71AE2D6F4009553F9 /* NSObject+StencilAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+StencilAdditions.m"; sourceTree = ""; };
EA3EB1AD1AE2D861009553F9 /* IDETemplateAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IDETemplateAdditions.m; sourceTree = ""; };
EA3EB1B01AE2D90B009553F9 /* IDEStructureNavigatorAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IDEStructureNavigatorAdditions.m; sourceTree = ""; };
EA3EB1B21AE2DDB5009553F9 /* NSMenu+StencilAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSMenu+StencilAdditions.h"; sourceTree = ""; };
EA3EB1B41AE39510009553F9 /* TemplateInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = TemplateInfo.plist; sourceTree = ""; };
EA4FB67F1AE39543001EFB21 /* HeaderComments.sctemplate */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HeaderComments.sctemplate; sourceTree = ""; };
EA4FB6831AE398E5001EFB21 /* ProjectGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProjectGroup.h; sourceTree = ""; };
EA4FB6841AE398E5001EFB21 /* ProjectGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ProjectGroup.m; sourceTree = ""; };
EA4FB6861AE399A3001EFB21 /* ProjectFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProjectFile.h; sourceTree = ""; };
EA4FB6871AE399A3001EFB21 /* ProjectFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ProjectFile.m; sourceTree = ""; };
EA4FB6911AE4D44C001EFB21 /* NSInputStream+StencilAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSInputStream+StencilAdditions.h"; sourceTree = ""; };
EA4FB6921AE4D44C001EFB21 /* NSInputStream+StencilAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSInputStream+StencilAdditions.m"; sourceTree = ""; };
EA4FB6941AE4D768001EFB21 /* NSOutputStream+StencilAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSOutputStream+StencilAdditions.h"; sourceTree = ""; };
EA4FB6951AE4D768001EFB21 /* NSOutputStream+StencilAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSOutputStream+StencilAdditions.m"; sourceTree = ""; };
EA91AC721AF75EA1005B4E74 /* DZLClassSingleton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DZLClassSingleton.h; path = DZLObjcAdditions/DZLClassSingleton.h; sourceTree = ""; };
EA91AC731AF75EA1005B4E74 /* DZLImplementationCombine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DZLImplementationCombine.h; path = DZLObjcAdditions/DZLImplementationCombine.h; sourceTree = ""; };
EA91AC741AF75EA1005B4E74 /* DZLImplementationSafe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DZLImplementationSafe.h; path = DZLObjcAdditions/DZLImplementationSafe.h; sourceTree = ""; };
EA91AC751AF75EA1005B4E74 /* DZLProtocolImplementation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DZLProtocolImplementation.h; path = DZLObjcAdditions/DZLProtocolImplementation.h; sourceTree = ""; };
EA91AC761AF75EA1005B4E74 /* DZLSynthesizeLazy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DZLSynthesizeLazy.h; path = DZLObjcAdditions/DZLSynthesizeLazy.h; sourceTree = ""; };
EA91AC781AF75EBC005B4E74 /* DZLSuper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DZLSuper.h; path = DZLObjcAdditions/Private/DZLSuper.h; sourceTree = ""; };
EA91AC791AF75EBC005B4E74 /* NSObject+DZLObjcAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSObject+DZLObjcAdditions.h"; path = "DZLObjcAdditions/Private/NSObject+DZLObjcAdditions.h"; sourceTree = ""; };
EA91AC7A1AF75EBC005B4E74 /* NSObject+DZLObjcAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSObject+DZLObjcAdditions.m"; path = "DZLObjcAdditions/Private/NSObject+DZLObjcAdditions.m"; sourceTree = ""; };
EAB55BA31AE2D519001EBD12 /* NSMenu+StencilAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMenu+StencilAdditions.m"; sourceTree = ""; };
EAD6226A1AE83563002723B6 /* StencilTemplateWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = StencilTemplateWindow.xib; sourceTree = ""; };
EAD85AD41AEEBBBC00415E84 /* Stencil */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Stencil; sourceTree = ""; };
EAF198321AE82B8A005A5BC0 /* StencilREADME */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = StencilREADME; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
EA31BC331AE1AFDE00167EFD /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
EA31BC391AE1AFDE00167EFD /* AppKit.framework in Frameworks */,
EA31BC3B1AE1AFDE00167EFD /* Foundation.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
EA23E0431AF4D89E008E4A50 /* Helpers */ = {
isa = PBXGroup;
children = (
EA23E0441AF4D8D6008E4A50 /* StencilWeakObjectWrapper.h */,
EA23E0451AF4D8D6008E4A50 /* StencilWeakObjectWrapper.m */,
EA23E0471AF4D8F1008E4A50 /* DeallocationObserver.h */,
EA23E0481AF4D8F1008E4A50 /* DeallocationObserver.m */,
);
path = Helpers;
sourceTree = "";
};
EA31BC2C1AE1AFDE00167EFD = {
isa = PBXGroup;
children = (
EA31BC3C1AE1AFDE00167EFD /* StencilPlugin */,
EA31BC371AE1AFDE00167EFD /* Frameworks */,
EA31BC361AE1AFDE00167EFD /* Products */,
);
sourceTree = "";
};
EA31BC361AE1AFDE00167EFD /* Products */ = {
isa = PBXGroup;
children = (
EA31BC351AE1AFDE00167EFD /* StencilPlugin.xcplugin */,
);
name = Products;
sourceTree = "";
};
EA31BC371AE1AFDE00167EFD /* Frameworks */ = {
isa = PBXGroup;
children = (
EA31BC381AE1AFDE00167EFD /* AppKit.framework */,
EA31BC3A1AE1AFDE00167EFD /* Foundation.framework */,
);
name = Frameworks;
sourceTree = "";
};
EA31BC3C1AE1AFDE00167EFD /* StencilPlugin */ = {
isa = PBXGroup;
children = (
EAB992F51AF75D5500F506D9 /* Vendor */,
EA3D3DBA1AE57D44007BD36A /* Model */,
EA3D3DAC1AE56711007BD36A /* User Interface */,
EA23E0431AF4D89E008E4A50 /* Helpers */,
EA4FB6821AE398B2001EFB21 /* SDK Fakes */,
EA3EB1B31AE3909B009553F9 /* Resources */,
EAB55BA11AE2D456001EBD12 /* Additions */,
EA31BC411AE1AFDE00167EFD /* Stencil.h */,
EA31BC421AE1AFDE00167EFD /* Stencil.m */,
EA31BC3D1AE1AFDE00167EFD /* Supporting Files */,
);
path = StencilPlugin;
sourceTree = "";
};
EA31BC3D1AE1AFDE00167EFD /* Supporting Files */ = {
isa = PBXGroup;
children = (
EA31BC3E1AE1AFDE00167EFD /* Info.plist */,
EA31BC3F1AE1AFDE00167EFD /* StencilPlugin.xcscheme */,
);
name = "Supporting Files";
sourceTree = "";
};
EA3D3DAC1AE56711007BD36A /* User Interface */ = {
isa = PBXGroup;
children = (
EA3D3DAF1AE56B97007BD36A /* Template Options */,
);
path = "User Interface";
sourceTree = "";
};
EA3D3DAF1AE56B97007BD36A /* Template Options */ = {
isa = PBXGroup;
children = (
EA3D3DB21AE56BDC007BD36A /* TemplateOptionsWindow.h */,
EA3D3DB31AE56BDC007BD36A /* TemplateOptionsWindow.m */,
EAD6226A1AE83563002723B6 /* StencilTemplateWindow.xib */,
);
path = "Template Options";
sourceTree = "";
};
EA3D3DBA1AE57D44007BD36A /* Model */ = {
isa = PBXGroup;
children = (
EA3D3DBB1AE57D61007BD36A /* TemplateConfig.h */,
EA3D3DBC1AE57D61007BD36A /* TemplateConfig.m */,
EA0000CE1AE7A56800E85807 /* ThingTypeToClassNamesMap.h */,
EA0000CF1AE7A56800E85807 /* ThingTypeToClassNamesMap.m */,
);
path = Model;
sourceTree = "";
};
EA3EB1B31AE3909B009553F9 /* Resources */ = {
isa = PBXGroup;
children = (
EAD85A971AEEA86000415E84 /* Built-in Templates */,
EA3EB1B41AE39510009553F9 /* TemplateInfo.plist */,
EA4FB67F1AE39543001EFB21 /* HeaderComments.sctemplate */,
EAF198321AE82B8A005A5BC0 /* StencilREADME */,
);
path = Resources;
sourceTree = "";
};
EA4FB6821AE398B2001EFB21 /* SDK Fakes */ = {
isa = PBXGroup;
children = (
EA4FB6831AE398E5001EFB21 /* ProjectGroup.h */,
EA4FB6841AE398E5001EFB21 /* ProjectGroup.m */,
EA4FB6861AE399A3001EFB21 /* ProjectFile.h */,
EA4FB6871AE399A3001EFB21 /* ProjectFile.m */,
);
path = "SDK Fakes";
sourceTree = "";
};
EA4FB6901AE4D429001EFB21 /* Parsing */ = {
isa = PBXGroup;
children = (
EA4FB6911AE4D44C001EFB21 /* NSInputStream+StencilAdditions.h */,
EA4FB6921AE4D44C001EFB21 /* NSInputStream+StencilAdditions.m */,
EA4FB6941AE4D768001EFB21 /* NSOutputStream+StencilAdditions.h */,
EA4FB6951AE4D768001EFB21 /* NSOutputStream+StencilAdditions.m */,
EA3D3DA91AE5638E007BD36A /* NSString+StencilRegex.h */,
EA3D3DAA1AE5638E007BD36A /* NSString+StencilRegex.m */,
EA3D3DC31AE5867F007BD36A /* TemplateFactory.h */,
EA3D3DC41AE5867F007BD36A /* TemplateFactory.m */,
);
path = Parsing;
sourceTree = "";
};
EA91AC711AF75E84005B4E74 /* DZLObjcAdditions */ = {
isa = PBXGroup;
children = (
EA91AC721AF75EA1005B4E74 /* DZLClassSingleton.h */,
EA91AC731AF75EA1005B4E74 /* DZLImplementationCombine.h */,
EA91AC741AF75EA1005B4E74 /* DZLImplementationSafe.h */,
EA91AC751AF75EA1005B4E74 /* DZLProtocolImplementation.h */,
EA91AC761AF75EA1005B4E74 /* DZLSynthesizeLazy.h */,
EA91AC771AF75EA6005B4E74 /* Private */,
);
name = DZLObjcAdditions;
sourceTree = "";
};
EA91AC771AF75EA6005B4E74 /* Private */ = {
isa = PBXGroup;
children = (
EA91AC781AF75EBC005B4E74 /* DZLSuper.h */,
EA91AC791AF75EBC005B4E74 /* NSObject+DZLObjcAdditions.h */,
EA91AC7A1AF75EBC005B4E74 /* NSObject+DZLObjcAdditions.m */,
);
name = Private;
sourceTree = "";
};
EAB55BA11AE2D456001EBD12 /* Additions */ = {
isa = PBXGroup;
children = (
EA4FB6901AE4D429001EFB21 /* Parsing */,
EA3EB1B21AE2DDB5009553F9 /* NSMenu+StencilAdditions.h */,
EAB55BA31AE2D519001EBD12 /* NSMenu+StencilAdditions.m */,
EA3EB1A61AE2D6F4009553F9 /* NSObject+StencilAdditions.h */,
EA3EB1A71AE2D6F4009553F9 /* NSObject+StencilAdditions.m */,
EA3EB1AD1AE2D861009553F9 /* IDETemplateAdditions.m */,
EA3EB1B01AE2D90B009553F9 /* IDEStructureNavigatorAdditions.m */,
);
path = Additions;
sourceTree = "";
};
EAB992F51AF75D5500F506D9 /* Vendor */ = {
isa = PBXGroup;
children = (
EA91AC711AF75E84005B4E74 /* DZLObjcAdditions */,
);
path = Vendor;
sourceTree = "";
};
EAD85A971AEEA86000415E84 /* Built-in Templates */ = {
isa = PBXGroup;
children = (
EAD85AD41AEEBBBC00415E84 /* Stencil */,
);
path = "Built-in Templates";
sourceTree = "";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
EA31BC341AE1AFDE00167EFD /* StencilPlugin */ = {
isa = PBXNativeTarget;
buildConfigurationList = EA31BC461AE1AFDE00167EFD /* Build configuration list for PBXNativeTarget "StencilPlugin" */;
buildPhases = (
EA31BC311AE1AFDE00167EFD /* Sources */,
EA31BC321AE1AFDE00167EFD /* Resources */,
EA31BC331AE1AFDE00167EFD /* Frameworks */,
EADE959C1AF150FA00C30880 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = StencilPlugin;
productName = StencilPlugin;
productReference = EA31BC351AE1AFDE00167EFD /* StencilPlugin.xcplugin */;
productType = "com.apple.product-type.bundle";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
EA31BC2D1AE1AFDE00167EFD /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0700;
ORGANIZATIONNAME = "Sam Dods";
TargetAttributes = {
EA31BC341AE1AFDE00167EFD = {
CreatedOnToolsVersion = 6.2;
};
};
};
buildConfigurationList = EA31BC301AE1AFDE00167EFD /* Build configuration list for PBXProject "StencilPlugin" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = EA31BC2C1AE1AFDE00167EFD;
productRefGroup = EA31BC361AE1AFDE00167EFD /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
EA31BC341AE1AFDE00167EFD /* StencilPlugin */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
EA31BC321AE1AFDE00167EFD /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
EAD6226B1AE83563002723B6 /* StencilTemplateWindow.xib in Resources */,
EACD8A5B1AE6C63F00706E6B /* HeaderComments.sctemplate in Resources */,
EAF198331AE82B8A005A5BC0 /* StencilREADME in Resources */,
EAD85AD51AEEBBBC00415E84 /* Stencil in Resources */,
EA3EB1B51AE39510009553F9 /* TemplateInfo.plist in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
EA31BC311AE1AFDE00167EFD /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
EACD8A4D1AE6C5B900706E6B /* TemplateOptionsWindow.m in Sources */,
EA91AC7B1AF75EBC005B4E74 /* NSObject+DZLObjcAdditions.m in Sources */,
EACD8A561AE6C5D600706E6B /* NSMenu+StencilAdditions.m in Sources */,
EACD8A571AE6C5D900706E6B /* NSObject+StencilAdditions.m in Sources */,
EACD8A4F1AE6C5BF00706E6B /* ProjectGroup.m in Sources */,
EACD8A531AE6C5CE00706E6B /* NSOutputStream+StencilAdditions.m in Sources */,
EACD8A5A1AE6C5E300706E6B /* Stencil.m in Sources */,
EACD8A521AE6C5CC00706E6B /* NSInputStream+StencilAdditions.m in Sources */,
EACD8A591AE6C5DF00706E6B /* IDEStructureNavigatorAdditions.m in Sources */,
EA23E0491AF4D8F1008E4A50 /* DeallocationObserver.m in Sources */,
EACD8A541AE6C5D100706E6B /* NSString+StencilRegex.m in Sources */,
EACD8A551AE6C5D300706E6B /* TemplateFactory.m in Sources */,
EACD8A4C1AE6C5B500706E6B /* TemplateConfig.m in Sources */,
EA0000D01AE7A56800E85807 /* ThingTypeToClassNamesMap.m in Sources */,
EACD8A581AE6C5DC00706E6B /* IDETemplateAdditions.m in Sources */,
EA23E0461AF4D8D6008E4A50 /* StencilWeakObjectWrapper.m in Sources */,
EACD8A501AE6C5C100706E6B /* ProjectFile.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
EA31BC441AE1AFDE00167EFD /* 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;
ENABLE_STRICT_OBJC_MSGSEND = NO;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
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;
};
EA31BC451AE1AFDE00167EFD /* 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;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
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;
};
EA31BC471AE1AFDE00167EFD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
DEPLOYMENT_LOCATION = YES;
DSTROOT = "$(HOME)";
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
INFOPLIST_FILE = StencilPlugin/Info.plist;
INSTALL_PATH = "/Library/Application Support/Developer/Shared/Xcode/Plug-ins";
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = "com.sdods.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = xcplugin;
};
name = Debug;
};
EA31BC481AE1AFDE00167EFD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
DEPLOYMENT_LOCATION = YES;
DSTROOT = "$(HOME)";
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
INFOPLIST_FILE = StencilPlugin/Info.plist;
INSTALL_PATH = "/Library/Application Support/Developer/Shared/Xcode/Plug-ins";
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = "com.sdods.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = xcplugin;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
EA31BC301AE1AFDE00167EFD /* Build configuration list for PBXProject "StencilPlugin" */ = {
isa = XCConfigurationList;
buildConfigurations = (
EA31BC441AE1AFDE00167EFD /* Debug */,
EA31BC451AE1AFDE00167EFD /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
EA31BC461AE1AFDE00167EFD /* Build configuration list for PBXNativeTarget "StencilPlugin" */ = {
isa = XCConfigurationList;
buildConfigurations = (
EA31BC471AE1AFDE00167EFD /* Debug */,
EA31BC481AE1AFDE00167EFD /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = EA31BC2D1AE1AFDE00167EFD /* Project object */;
}
================================================
FILE: StencilPlugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
================================================
FILE: StencilPlugin.xcodeproj/xcshareddata/xcschemes/StencilPlugin.xcscheme
================================================