├── .gitignore ├── CreateFile.png ├── README.md ├── SuggestedColors.png ├── SuggestedColors.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── SuggestedColors.xcscheme ├── SuggestedColors ├── Aspects.h ├── Aspects.m ├── ColorCell.h ├── ColorCell.m ├── ColorsViewController.h ├── ColorsViewController.m ├── ColorsViewController.xib ├── IBHeaders │ └── Headers.h ├── Info.plist ├── Storyboard.storyboard ├── SuggestedColors.h ├── SuggestedColors.m ├── SuggestedColors.plist ├── View.xib └── XCodeEditor │ ├── Utils │ ├── XCKeyBuilder.h │ └── XCKeyBuilder.m │ ├── XCAbstractDefinition.h │ ├── XCAbstractDefinition.m │ ├── XCBuildConfig.h │ ├── XCBuildConfig.m │ ├── XCClassDefinition.h │ ├── XCClassDefinition.m │ ├── XCFileOperationQueue.h │ ├── XCFileOperationQueue.m │ ├── XCFrameworkDefinition.h │ ├── XCFrameworkDefinition.m │ ├── XCGroup.h │ ├── XCGroup.m │ ├── XCProject+SubProject.h │ ├── XCProject+SubProject.m │ ├── XCProject.h │ ├── XCProject.m │ ├── XCSourceFile.h │ ├── XCSourceFile.m │ ├── XCSourceFileDefinition.h │ ├── XCSourceFileDefinition.m │ ├── XCSubProjectDefinition.h │ ├── XCSubProjectDefinition.m │ ├── XCTarget.h │ ├── XCTarget.m │ ├── XCXibDefinition.h │ ├── XCXibDefinition.m │ ├── XcodeEditor.h │ ├── XcodeGroupMember.h │ ├── XcodeMemberType.h │ ├── XcodeMemberType.m │ ├── XcodeSourceFileType.h │ └── XcodeSourceFileType.m └── SuggestedColorsTests ├── Info.plist └── SuggestedColorsTests.m /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### Xcode ### 4 | build/ 5 | *.pbxuser 6 | !default.pbxuser 7 | *.mode1v3 8 | !default.mode1v3 9 | *.mode2v3 10 | !default.mode2v3 11 | *.perspectivev3 12 | !default.perspectivev3 13 | xcuserdata 14 | *.xccheckout 15 | *.moved-aside 16 | DerivedData 17 | *.xcuserstate 18 | 19 | -------------------------------------------------------------------------------- /CreateFile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaitzel/SuggestedColors/0c6bd60c66c71fc16eeee94c308eeb4746d5dd79/CreateFile.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SuggestedColors 2 | --- 3 | 4 | Xcode plugin for replace the suggested colors in xcode. 5 | 6 | ![Screenshot](https://raw.githubusercontent.com/jwaitzel/SuggestedColors/master/SuggestedColors.png) 7 | 8 | ## Use 9 | 10 | ![Screenshot](https://raw.githubusercontent.com/jwaitzel/SuggestedColors/master/CreateFile.png) 11 | 12 | Go to Edit->Create SuggestedColors file and a plist will be created. Add colors there with NAME and HEX VALUE 13 | 14 | Change useMyColors entry to activate/deactivate it 15 | 16 | ## Important - Do not change plist file name 17 | 18 | ## Installation 19 | Either 20 | 21 | - Install it via [Alcatraz](http://alcatraz.io/) 22 | 23 | or 24 | 25 | - Clone and build the plugin yourself, it will be installed to the right location automatically by building it. 26 | 27 | 28 | In any case, relaunch Xcode to load it. 29 | 30 | ## Known Issues 31 | 1- I change useMyColors to NO/YES but the Interface Builder doesn't update 32 | FIX: Alt-tab to another window and it should refresh 33 | 34 | 35 | -------------------------------------------------------------------------------- /SuggestedColors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jwaitzel/SuggestedColors/0c6bd60c66c71fc16eeee94c308eeb4746d5dd79/SuggestedColors.png -------------------------------------------------------------------------------- /SuggestedColors.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SuggestedColors.xcodeproj/xcshareddata/xcschemes/SuggestedColors.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 46 | 47 | 58 | 61 | 62 | 63 | 69 | 70 | 71 | 72 | 73 | 74 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /SuggestedColors/Aspects.h: -------------------------------------------------------------------------------- 1 | // 2 | // Aspects.h 3 | // Aspects - A delightful, simple library for aspect oriented programming. 4 | // 5 | // Copyright (c) 2014 Peter Steinberger. Licensed under the MIT license. 6 | // 7 | 8 | #import 9 | 10 | typedef NS_OPTIONS(NSUInteger, AspectOptions) { 11 | AspectPositionAfter = 0, /// Called after the original implementation (default) 12 | AspectPositionInstead = 1, /// Will replace the original implementation. 13 | AspectPositionBefore = 2, /// Called before the original implementation. 14 | 15 | AspectOptionAutomaticRemoval = 1 << 3 /// Will remove the hook after the first execution. 16 | }; 17 | 18 | /// Opaque Aspect Token that allows to deregister the hook. 19 | @protocol AspectToken 20 | 21 | /// Deregisters an aspect. 22 | /// @return YES if deregistration is successful, otherwise NO. 23 | - (BOOL)remove; 24 | 25 | @end 26 | 27 | /// The AspectInfo protocol is the first parameter of our block syntax. 28 | @protocol AspectInfo 29 | 30 | /// The instance that is currently hooked. 31 | - (id)instance; 32 | 33 | /// The original invocation of the hooked method. 34 | - (NSInvocation *)originalInvocation; 35 | 36 | /// All method arguments, boxed. This is lazily evaluated. 37 | - (NSArray *)arguments; 38 | 39 | @end 40 | 41 | /** 42 | Aspects uses Objective-C message forwarding to hook into messages. This will create some overhead. Don't add aspects to methods that are called a lot. Aspects is meant for view/controller code that is not called a 1000 times per second. 43 | 44 | Adding aspects returns an opaque token which can be used to deregister again. All calls are thread safe. 45 | */ 46 | @interface NSObject (Aspects) 47 | 48 | /// Adds a block of code before/instead/after the current `selector` for a specific class. 49 | /// 50 | /// @param block Aspects replicates the type signature of the method being hooked. 51 | /// The first parameter will be `id`, followed by all parameters of the method. 52 | /// These parameters are optional and will be filled to match the block signature. 53 | /// You can even use an empty block, or one that simple gets `id`. 54 | /// 55 | /// @note Hooking static methods is not supported. 56 | /// @return A token which allows to later deregister the aspect. 57 | + (id)aspect_hookSelector:(SEL)selector 58 | withOptions:(AspectOptions)options 59 | usingBlock:(id)block 60 | error:(NSError **)error; 61 | 62 | /// Adds a block of code before/instead/after the current `selector` for a specific instance. 63 | - (id)aspect_hookSelector:(SEL)selector 64 | withOptions:(AspectOptions)options 65 | usingBlock:(id)block 66 | error:(NSError **)error; 67 | 68 | @end 69 | 70 | 71 | typedef NS_ENUM(NSUInteger, AspectErrorCode) { 72 | AspectErrorSelectorBlacklisted, /// Selectors like release, retain, autorelease are blacklisted. 73 | AspectErrorDoesNotRespondToSelector, /// Selector could not be found. 74 | AspectErrorSelectorDeallocPosition, /// When hooking dealloc, only AspectPositionBefore is allowed. 75 | AspectErrorSelectorAlreadyHookedInClassHierarchy, /// Statically hooking the same method in subclasses is not allowed. 76 | AspectErrorFailedToAllocateClassPair, /// The runtime failed creating a class pair. 77 | AspectErrorMissingBlockSignature, /// The block misses compile time signature info and can't be called. 78 | AspectErrorIncompatibleBlockSignature, /// The block signature does not match the method or is too large. 79 | 80 | AspectErrorRemoveObjectAlreadyDeallocated = 100 /// (for removing) The object hooked is already deallocated. 81 | }; 82 | 83 | extern NSString *const AspectErrorDomain; 84 | -------------------------------------------------------------------------------- /SuggestedColors/ColorCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // ColorCell.h 3 | // Plugin 4 | // 5 | // Created by Javi Waitzel on 10/20/14. 6 | // Copyright (c) 2014 Monsters Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ColorCell : NSTableCellView 12 | @property (weak) IBOutlet NSColorWell *colorWell; 13 | @end 14 | -------------------------------------------------------------------------------- /SuggestedColors/ColorCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // ColorCell.m 3 | // Plugin 4 | // 5 | // Created by Javi Waitzel on 10/20/14. 6 | // Copyright (c) 2014 Monsters Inc. All rights reserved. 7 | // 8 | 9 | #import "ColorCell.h" 10 | 11 | @interface ColorCell () 12 | 13 | @end 14 | 15 | @implementation ColorCell 16 | 17 | 18 | - (void)drawRect:(NSRect)dirtyRect { 19 | [super drawRect:dirtyRect]; 20 | 21 | // Drawing code here. 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /SuggestedColors/ColorsViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ColorsViewController.h 3 | // Plugin 4 | // 5 | // Created by Javi Waitzel on 10/20/14. 6 | // Copyright (c) 2014 Monsters Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ColorsViewController : NSWindowController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SuggestedColors/ColorsViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ColorsViewController.m 3 | // Plugin 4 | // 5 | // Created by Javi Waitzel on 10/20/14. 6 | // Copyright (c) 2014 Monsters Inc. All rights reserved. 7 | // 8 | 9 | #import "ColorsViewController.h" 10 | #import "ColorCell.h" 11 | 12 | @interface ColorsViewController () 13 | @property (weak) IBOutlet NSTableView *colorsTableView; 14 | 15 | @end 16 | 17 | @implementation ColorsViewController 18 | 19 | - (void)windowDidLoad { 20 | [super windowDidLoad]; 21 | 22 | // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. 23 | 24 | } 25 | 26 | - (NSInteger) numberOfRowsInTableView:(NSTableView *)tableView 27 | { 28 | return 5; 29 | } 30 | 31 | - (NSView *)tableView:(NSTableView *)tableView 32 | viewForTableColumn:(NSTableColumn *)tableColumn 33 | row:(NSInteger)row { 34 | 35 | 36 | // Get an existing cell with the MyView identifier if it exists 37 | ColorCell *colorCell = [tableView makeViewWithIdentifier:@"ColorCell" owner:self]; 38 | colorCell.textField.stringValue = [NSString stringWithFormat:@"%zd", row]; 39 | colorCell.colorWell.color = [NSColor redColor]; 40 | 41 | // Return the result 42 | return colorCell; 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /SuggestedColors/ColorsViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 97 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /SuggestedColors/IBHeaders/Headers.h: -------------------------------------------------------------------------------- 1 | // 2 | // Headers.h 3 | // Plugin 4 | // 5 | // Created by Javi Waitzel on 10/20/14. 6 | // Copyright (c) 2014 Monsters Inc. All rights reserved. 7 | // 8 | #import 9 | #import 10 | 11 | @class DVTMapTable, DVTMutableOrderedSet; 12 | 13 | @interface DVTMutableOrderedDictionary : NSMutableDictionary 14 | { 15 | DVTMutableOrderedSet *set; 16 | DVTMapTable *backingMapTable; 17 | } 18 | 19 | - (id)mutableCopyWithZone:(struct _NSZone *)arg1; 20 | - (id)copyWithZone:(struct _NSZone *)arg1; 21 | - (id)allKeys; 22 | - (id)lastValue; 23 | - (id)lastKey; 24 | - (id)firstValue; 25 | - (id)firstKey; 26 | - (void)removeObjectForKey:(id)arg1; 27 | - (void)setObject:(id)arg1 forKey:(id)arg2; 28 | - (id)keyEnumerator; 29 | - (id)objectForKey:(id)arg1; 30 | - (unsigned long long)count; 31 | - (Class)classForCoder; 32 | - (void)encodeWithCoder:(id)arg1; 33 | - (id)initWithCoder:(id)arg1; 34 | - (id)initWithObjects:(id)arg1 forKeys:(id)arg2; 35 | - (id)initWithCapacity:(unsigned long long)arg1; 36 | 37 | @end 38 | 39 | 40 | @class DVTMutableOrderedDictionary, DVTObservingToken, NSColor, NSMenu, NSString; 41 | 42 | @interface DVTAbstractColorPicker : NSView 43 | { 44 | NSMenu *_colorsMenu; 45 | id _colorValueBindingController; 46 | NSString *_colorValueBindingKeyPath; 47 | DVTObservingToken *_colorListBindingObservation; 48 | DVTObservingToken *_colorValueBindingObservation; 49 | DVTObservingToken *_supportsNilColorBindingObservation; 50 | // id _windowActivationObservation; 51 | BOOL _supportsNilColor; 52 | BOOL _showingMultipleValues; 53 | BOOL _enabled; 54 | BOOL _active; 55 | BOOL _highlighted; 56 | int _defaultColorMode; 57 | NSColor *_color; 58 | DVTMutableOrderedDictionary *_suggestedColors; 59 | NSColor *_defaultColor; 60 | id _target; 61 | SEL _action; 62 | unsigned long long _controlSize; 63 | } 64 | 65 | @property(nonatomic, getter=isHighlighted) BOOL highlighted; // @synthesize highlighted=_highlighted; 66 | @property(nonatomic, getter=isActive) BOOL active; // @synthesize active=_active; 67 | @property(nonatomic, getter=isEnabled) BOOL enabled; // @synthesize enabled=_enabled; 68 | @property(nonatomic) unsigned long long controlSize; // @synthesize controlSize=_controlSize; 69 | @property SEL action; // @synthesize action=_action; 70 | @property __weak id target; // @synthesize target=_target; 71 | @property(getter=isShowingMultipleValues) BOOL showingMultipleValues; // @synthesize showingMultipleValues=_showingMultipleValues; 72 | @property BOOL supportsNilColor; // @synthesize supportsNilColor=_supportsNilColor; 73 | @property(nonatomic) int defaultColorMode; // @synthesize defaultColorMode=_defaultColorMode; 74 | @property(retain, nonatomic) NSColor *defaultColor; // @synthesize defaultColor=_defaultColor; 75 | @property(retain) DVTMutableOrderedDictionary *suggestedColors; // @synthesize suggestedColors=_suggestedColors; 76 | @property(retain, nonatomic) NSColor *color; // @synthesize color=_color; 77 | 78 | - (void)unbind:(id)arg1; 79 | - (void)bind:(id)arg1 toObject:(id)arg2 withKeyPath:(id)arg3 options:(id)arg4; 80 | - (void)observedColorValueDidChangeToValue:(id)arg1; 81 | - (void)displayColorPanel:(id)arg1; 82 | - (void)takeDrawnColorFrom:(id)arg1; 83 | - (void)takeDrawnColorFromPopUpMenu:(id)arg1; 84 | - (void)sendAction; 85 | - (void)beginColorDragForEvent:(id)arg1; 86 | - (id)imageForDraggedColor:(id)arg1; 87 | - (BOOL)performDragOperation:(id)arg1; 88 | - (unsigned long long)draggingEntered:(id)arg1; 89 | - (void)colorPanelColorChanged:(id)arg1; 90 | - (void)colorPanelWillClose:(id)arg1; 91 | - (void)window:(id)arg1 didChangeActivationState:(long long)arg2; 92 | - (void)colorPickerDidBecomeActive:(id)arg1; 93 | - (void)colorChosenFromColorChooser:(id)arg1; 94 | - (void)moveUp:(id)arg1; 95 | - (void)moveDown:(id)arg1; 96 | - (void)performClick:(id)arg1; 97 | - (void)displayColorPanel; 98 | - (BOOL)canBecomeKeyView; 99 | - (BOOL)becomeFirstResponder; 100 | - (BOOL)resignFirstResponder; 101 | - (BOOL)acceptsFirstResponder; 102 | - (BOOL)acceptsFirstMouse:(id)arg1; 103 | - (void)viewWillMoveToWindow:(id)arg1; 104 | - (void)showColorsMenu; 105 | - (double)minimumPopUpMenuWidth; 106 | - (struct CGPoint)popUpMenuLocation; 107 | - (id)effectiveSwatchFillColor; 108 | - (void)putIntoMultipleValuesState; 109 | - (void)populateColorsMenu; 110 | - (double)swatchHeight; 111 | - (id)swatchImageForColor:(id)arg1 withSize:(struct CGSize)arg2; 112 | - (id)effectiveSwatchBorderColor; 113 | - (id)effectiveTextColor; 114 | - (BOOL)isShowingTitle; 115 | - (BOOL)isShowingDefaultColor; 116 | - (BOOL)isShowingNamedColor; 117 | - (BOOL)supportsDefaultColor; 118 | - (double)noColorStrokeWidth; 119 | - (id)titleFont; 120 | - (void)setSuggestedColorsUsingColorList:(id)arg1; 121 | - (BOOL)isOnActiveWindow; 122 | - (id)nameForColor:(id)arg1; 123 | - (BOOL)containsColor:(id)arg1; 124 | - (void)encodeWithCoder:(id)arg1; 125 | - (id)initWithCoder:(id)arg1; 126 | - (id)initWithFrame:(struct CGRect)arg1 colorList:(id)arg2 defaultColor:(id)arg3 defaultColorMode:(int)arg4; 127 | - (id)initWithFrame:(struct CGRect)arg1; 128 | - (void)commonInit; 129 | 130 | @end 131 | 132 | @interface DVTColorPickerPopUpButton : DVTAbstractColorPicker 133 | 134 | @end 135 | 136 | 137 | 138 | @interface IDEInspectorColorProperty : NSObject 139 | { 140 | DVTColorPickerPopUpButton *_popUpButton; 141 | } 142 | 143 | @end 144 | 145 | 146 | 147 | 148 | 149 | //// IDE WORKSPACe 150 | @class DVTFilePath; 151 | @class DVTFilePath, DVTHashTable, DVTMapTable, DVTObservingToken, DVTStackBacktrace, IDEActivityLogSection, IDEBatchFindManager, IDEBreakpointManager, IDEConcreteClientTracker, IDEContainer, IDEContainer, IDEContainerQuery, IDEDeviceInstallWorkspaceMonitor, IDEExecutionEnvironment, IDEIndex, IDEIssueManager, IDELogManager, IDERefactoring, IDERunContextManager, IDESourceControlWorkspaceMonitor, IDETestManager, IDETextIndex, IDEWorkspaceArena, IDEWorkspaceBotMonitor, IDEWorkspaceSharedSettings, IDEWorkspaceSnapshotManager, IDEWorkspaceUserSettings, NSDictionary, NSHashTable, NSMapTable, NSMutableArray, NSMutableOrderedSet, NSMutableSet, NSSet, NSString; 152 | 153 | @interface IDEWorkspace : NSObject 154 | { 155 | NSString *_untitledName; 156 | DVTFilePath *_headerMapFilePath; 157 | IDEExecutionEnvironment *_executionEnvironment; 158 | IDEContainerQuery *_containerQuery; 159 | DVTObservingToken *_containerQueryObservingToken; 160 | NSMutableSet *_referencedContainers; 161 | DVTHashTable *_fileRefsWithContainerLoadingIssues; 162 | IDEActivityLogSection *_containerLoadingIntegrityLog; 163 | NSMutableSet *_customDataStores; 164 | IDEWorkspaceUserSettings *_userSettings; 165 | IDEWorkspaceSharedSettings *_sharedSettings; 166 | DVTMapTable *_blueprintProviderObserverMap; 167 | NSMutableSet *_referencedBlueprints; 168 | DVTMapTable *_testableProviderObserverMap; 169 | NSMutableSet *_referencedTestables; 170 | BOOL _initialContainerScanComplete; 171 | NSMutableArray *_referencedRunnableBuildableProducts; 172 | IDERunContextManager *_runContextManager; 173 | IDELogManager *_logManager; 174 | IDEIssueManager *_issueManager; 175 | IDEBreakpointManager *_breakpointManager; 176 | IDEBatchFindManager *_batchFindManager; 177 | IDETestManager *_testManager; 178 | IDEContainerQuery *_indexableSourceQuery; 179 | DVTObservingToken *_indexableSourceQueryObservingToken; 180 | NSMutableArray *_observedIndexableSources; 181 | IDEContainerQuery *_indexableFileQuery; 182 | DVTObservingToken *_indexableFileQueryObservingToken; 183 | id _indexableFileUpdateNotificationToken; 184 | IDEIndex *_index; 185 | IDERefactoring *_refactoring; 186 | DVTMapTable *_fileRefsToResolvedFilePaths; 187 | NSMutableSet *_fileRefsToRegisterForIndexing; 188 | IDETextIndex *_textIndex; 189 | IDEDeviceInstallWorkspaceMonitor *_deviceInstallWorkspaceMonitor; 190 | IDESourceControlWorkspaceMonitor *_sourceControlWorkspaceMonitor; 191 | IDEWorkspaceSnapshotManager *_snapshotManager; 192 | DVTFilePath *_wrappedXcode3ProjectPath; 193 | DVTObservingToken *_wrappedXcode3ProjectValidObservingToken; 194 | DVTObservingToken *_newWrappedXcode3ProjectObservingToken; 195 | NSHashTable *_pendingReferencedFileReferences; 196 | NSHashTable *_pendingReferencedContainers; 197 | IDEConcreteClientTracker *_clientTracker; 198 | DVTHashTable *_fileReferencesForProblem8727051; 199 | DVTObservingToken *_finishedLoadingObservingToken; 200 | NSDictionary *_Problem9887530_preferredStructurePaths; 201 | BOOL _simpleFilesFocused; 202 | DVTHashTable *_sourceControlStatusUpdatePendingFileReferences; 203 | id _openingPerformanceMetricIdentifier; 204 | DVTStackBacktrace *_finishedLoadingBacktrace; 205 | NSMutableOrderedSet *_initialOrderedReferencedBlueprintProviders; 206 | BOOL _hasPostedIndexingRegistrationBatchNotification; 207 | BOOL _didFinishLoadingFirstStage; 208 | BOOL _finishedLoading; 209 | BOOL _postLoadingPerformanceMetricsAllowed; 210 | BOOL _willInvalidate; 211 | BOOL _pendingFileReferencesAndContainers; 212 | BOOL _didProcessFileReferencesForProblem8727051; 213 | BOOL _isCleaningBuildFolder; 214 | BOOL _indexingAndRefactoringRestartScheduled; 215 | BOOL _sourceControlStatusUpdatePending; 216 | BOOL _didFinishBuildingInitialBlueprintProviderOrderedSet; 217 | NSMapTable *_pendingExecutionNotificationTokens; 218 | IDEWorkspaceBotMonitor *_workspaceBotMonitor; 219 | } 220 | @property (readonly) DVTFilePath *representingFilePath; 221 | @property(retain, nonatomic) IDEWorkspaceUserSettings *userSettings; // @synthesize userSettings=_userSettings; 222 | 223 | - (id)_wrappingContainerPath; 224 | + (id)rootElementName; 225 | 226 | @end 227 | 228 | @interface DVTFilePath : NSObject 229 | @property (readonly) NSString *fileName; 230 | @property (readonly) NSURL *fileURL; 231 | @property (readonly) NSArray *pathComponents; 232 | @property (readonly) NSString *pathString; 233 | @end 234 | 235 | 236 | @interface DVTDualProxyWindow : NSWindow 237 | @end 238 | 239 | @interface IDEWorkspaceWindow : DVTDualProxyWindow 240 | @end 241 | 242 | 243 | @class DVTObservingToken, DVTStackBacktrace, DVTStateToken, DVTTabBarEnclosureView, DVTTabBarView, DVTTabSwitcher, IDEEditorArea, IDESourceControlWorkspaceUIHandler, IDEToolbarDelegate, IDEWorkspace, IDEWorkspaceTabController, IDEWorkspaceWindow, NSMapTable, NSMutableArray, NSString, NSTimer, _IDEWindowFullScreenSavedDebuggerTransitionValues; 244 | 245 | @interface IDEWorkspaceWindowController : NSWindowController 246 | { 247 | 248 | } 249 | 250 | 251 | @end 252 | 253 | 254 | @interface IDEEditorDocument : NSDocument 255 | @property(retain) DVTFilePath *filePath; // @synthesize filePath=_filePath; 256 | @end 257 | 258 | 259 | @interface IDEPlistDocument : IDEEditorDocument 260 | 261 | @end 262 | -------------------------------------------------------------------------------- /SuggestedColors/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | MonstersInc.$(PRODUCT_NAME:rfc1034identifier) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | BNDL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.1.1 25 | DVTPlugInCompatibilityUUIDs 26 | 27 | C4A681B0-4A26-480E-93EC-1218098B9AA0 28 | AD68E85B-441B-4301-B564-A45E4919A6AD 29 | A16FF353-8441-459E-A50C-B071F53F51B7 30 | 9F75337B-21B4-4ADC-B558-F9CADF7073A7 31 | 8DC44374-2B35-4C57-A6FE-2AD66A36AAD9 32 | E969541F-E6F9-4D25-8158-72DC3545A6C6 33 | 7FDF5C7A-131F-4ABB-9EDC-8C5F8F0B8A90 34 | 35 | LSMinimumSystemVersion 36 | $(MACOSX_DEPLOYMENT_TARGET) 37 | NSPrincipalClass 38 | SuggestedColors 39 | XC4Compatible 40 | 41 | XCPluginHasUI 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /SuggestedColors/Storyboard.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /SuggestedColors/SuggestedColors.h: -------------------------------------------------------------------------------- 1 | // 2 | // Plugin.h 3 | // Plugin 4 | // 5 | // Created by Javi Waitzel on 10/16/14. 6 | // Copyright (c) 2014 Monsters Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SuggestedColors : NSObject 12 | 13 | + (instancetype)sharedPlugin; 14 | 15 | @property (nonatomic, strong, readonly) NSBundle* bundle; 16 | @end -------------------------------------------------------------------------------- /SuggestedColors/SuggestedColors.m: -------------------------------------------------------------------------------- 1 | // 2 | // Plugin.m 3 | // Plugin 4 | // 5 | // Created by Javi Waitzel on 10/16/14. 6 | // Copyright (c) 2014 Monsters Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "SuggestedColors.h" 12 | #import "Aspects.h" 13 | #import 14 | #import "Headers.h" 15 | #import "ColorsViewController.h" 16 | #import "XcodeEditor.h" 17 | 18 | static NSString*const IDEEditorDocumentDidChangeNotification = @"IDEEditorDocumentDidChangeNotification"; 19 | 20 | static NSString*const SuggestedColorsPlistName = @"SuggestedColors.plist"; 21 | 22 | 23 | 24 | #define NSColorFromRGBA(rgbValue, alpha_) [NSColor colorWithCalibratedRed: ( (float) ( (rgbValue & 0xFF0000) >> 16 ) ) / 255.0 green: ( (float) ( (rgbValue & 0xFF00) >> 8 ) ) / 255.0 blue: ( (float) (rgbValue & 0xFF) ) / 255.0 alpha: alpha_] 25 | 26 | #define NSColorFromRGB(rgbValue) NSColorFromRGBA(rgbValue, 1.0) 27 | 28 | static SuggestedColors *sharedPlugin; 29 | NSMutableDictionary *suggestedColorsDic; 30 | 31 | @interface SuggestedColors () 32 | 33 | @property (nonatomic, strong, readwrite) NSBundle *bundle; 34 | @property (nonatomic, strong) NSString *projectBundlePath; 35 | @property (nonatomic, strong) NSString *projectWorkspacePath; 36 | @property (nonatomic, assign) BOOL menuItemAlreadyCreated; 37 | @property (nonatomic, strong) NSMenuItem *createFileMenuItem; 38 | @property (nonatomic, strong) NSMenuItem *separatorItem; 39 | @end 40 | 41 | @implementation SuggestedColors 42 | 43 | + (void)pluginDidLoad:(NSBundle*)plugin 44 | { 45 | static dispatch_once_t onceToken; 46 | NSString *currentApplicationName = [[NSBundle mainBundle] infoDictionary][@"CFBundleName"]; 47 | 48 | if ([currentApplicationName isEqual:@"Xcode"]) 49 | { 50 | dispatch_once(&onceToken, ^{ 51 | sharedPlugin = [[self alloc] initWithBundle:plugin]; 52 | }); 53 | } 54 | } /* pluginDidLoad */ 55 | 56 | + (instancetype)sharedPlugin 57 | { 58 | return sharedPlugin; 59 | } 60 | 61 | - (id)initWithBundle:(NSBundle*)plugin 62 | { 63 | if (self = [super init]) 64 | { 65 | // reference to plugin's bundle, for resource access 66 | self.bundle = plugin; 67 | 68 | // Register to notification center 69 | [[NSNotificationCenter defaultCenter] addObserver:self 70 | selector:@selector(workspaceWindowDidBecomeMain:) 71 | name:NSWindowDidBecomeMainNotification 72 | object:nil]; 73 | 74 | // [[NSNotificationCenter defaultCenter] addObserver:self 75 | // selector:@selector(notificationListener:) 76 | // name:nil 77 | // object:nil]; 78 | 79 | 80 | [[NSNotificationCenter defaultCenter] addObserver:self 81 | selector:@selector(documentDidChange:) 82 | name:IDEEditorDocumentDidChangeNotification 83 | object:nil]; 84 | 85 | NSError *error; 86 | [objc_getClass("DVTAbstractColorPicker") aspect_hookSelector:@selector(setSuggestedColors:) withOptions:AspectPositionAfter usingBlock: ^(id < AspectInfo > par){ 87 | if (suggestedColorsDic) 88 | { 89 | if( ( [suggestedColorsDic objectForKey:@"useMyColors"] == nil) || [[suggestedColorsDic objectForKey:@"useMyColors"] boolValue] ) 90 | { 91 | DVTAbstractColorPicker *colorPicker = (DVTAbstractColorPicker*) par.instance; 92 | DVTMutableOrderedDictionary *dic = [[objc_getClass("DVTMutableOrderedDictionary") alloc] 93 | initWithObjects:[[suggestedColorsDic objectForKey:@"colors"] allObjects] 94 | forKeys:[[suggestedColorsDic objectForKey:@"colors"] allKeys]]; 95 | 96 | [colorPicker setValue:dic forKey:@"_suggestedColors"]; 97 | } 98 | } 99 | } error:&error]; 100 | } 101 | return self; 102 | } /* initWithBundle */ 103 | 104 | // Get current Workspace 105 | - (void)workspaceWindowDidBecomeMain:(NSNotification*)notification 106 | { 107 | if ([[notification object] isKindOfClass:[IDEWorkspaceWindow class]]) 108 | { 109 | NSWindow *workspaceWindow = (NSWindow*) [notification object]; 110 | NSWindowController *workspaceWindowController = (NSWindowController*) workspaceWindow.windowController; 111 | IDEWorkspace *workspace = (IDEWorkspace*) [workspaceWindowController valueForKey:@"_workspace"]; 112 | DVTFilePath *representingFilePath = workspace.representingFilePath; 113 | 114 | self.projectWorkspacePath = [representingFilePath.pathString stringByReplacingOccurrencesOfString:@".xcworkspace" 115 | withString:@".xcodeproj"]; 116 | 117 | self.projectBundlePath = [representingFilePath.pathString stringByReplacingOccurrencesOfString:@".xcodeproj" 118 | withString:@"/"]; 119 | [self reloadColors:nil]; 120 | } 121 | } /* workspaceWindowDidBecomeMain */ 122 | 123 | - (void)reloadColors:(id)sender 124 | { 125 | if(self.projectBundlePath == nil) 126 | { 127 | return; 128 | } 129 | 130 | XCProject *proj = [[XCProject alloc] initWithFilePath:self.projectWorkspacePath]; 131 | XCSourceFile *suggestedColorsFile = [proj fileWithName:SuggestedColorsPlistName]; 132 | 133 | if(suggestedColorsFile) 134 | { 135 | NSString *pathFile = [[[proj filePath] stringByDeletingLastPathComponent] stringByAppendingPathComponent:[suggestedColorsFile pathRelativeToProjectRoot]]; 136 | suggestedColorsDic = [NSMutableDictionary dictionaryWithDictionary:[NSDictionary dictionaryWithContentsOfFile:pathFile]]; 137 | 138 | NSMutableDictionary *newDic = [NSMutableDictionary dictionary]; 139 | 140 | for (NSString *color in [suggestedColorsDic objectForKey:@"colors"]) 141 | { 142 | NSString *colorString = [[suggestedColorsDic objectForKey:@"colors"] objectForKey:color]; 143 | 144 | // [self parseColorString:@"rgb(121,122,123)"]; 145 | // [self parseColorString:@"rgba(121,122,123, 0.5)"]; 146 | // [self parseColorString:@"123456"]; 147 | // [self parseColorString:@"123456,0.5"]; 148 | 149 | NSColor *colorValue = [self parseColorString:colorString]; 150 | [newDic setObject:colorValue forKey:color]; 151 | } 152 | 153 | // [newDic setObject:[NSColor whiteColor] forKey:@"White color"]; 154 | // [newDic setObject:[NSColor clearColor] forKey:@"Clear color"]; 155 | 156 | [suggestedColorsDic setObject:newDic forKey:@"colors"]; 157 | } 158 | else 159 | { 160 | // Create menu items, initialize UI, etc. 161 | // Sample Menu Item: 162 | if(!self.menuItemAlreadyCreated) 163 | { 164 | NSMenuItem *editMenuItem = [[NSApp mainMenu] itemWithTitle:@"Edit"]; 165 | 166 | if(editMenuItem) 167 | { 168 | self.separatorItem = [NSMenuItem separatorItem]; 169 | [editMenuItem.submenu addItem:self.separatorItem]; 170 | 171 | self.createFileMenuItem = [[NSMenuItem alloc] initWithTitle:@"Create SuggestedColors file" action:@selector(createSuggestedColorsFile:) keyEquivalent:@""]; 172 | [self.createFileMenuItem setTarget:self]; 173 | [[editMenuItem submenu] addItem:self.createFileMenuItem]; 174 | 175 | self.menuItemAlreadyCreated = YES; 176 | } 177 | } 178 | 179 | NSLog(@"Suggested colors file not found..."); 180 | } 181 | } /* reloadColors */ 182 | 183 | - (NSColor*)parseColorString:(NSString*)colorString 184 | { 185 | NSColor *colorValue = [NSColor whiteColor]; 186 | 187 | colorString = [colorString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 188 | 189 | // Color in rgb(x,x,x) format 190 | if ([colorString rangeOfString:@"rgb("].length > 0) 191 | { 192 | colorString = [colorString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; 193 | char *rgbChars = (char*) [[[colorString stringByReplacingOccurrencesOfString:@"rgb(" withString:@""] stringByReplacingOccurrencesOfString:@")" withString:@""] cStringUsingEncoding:NSUTF8StringEncoding]; 194 | int r = atoi( strtok(rgbChars, ",") ); 195 | int g = atoi( strtok(NULL, ",") ); 196 | int b = atoi( strtok(NULL, ",") ); 197 | 198 | // NSLog(@"r:%i, g:%i, b:%i", r,g,b); 199 | colorValue = [NSColor colorWithCalibratedRed:r / 255.0 200 | green:g / 255.0 201 | blue:b / 255.0 202 | alpha:1.0]; 203 | }// Color in rgba(x,x,x,a) format 204 | else if ([colorString rangeOfString:@"rgba("].length > 0) 205 | { 206 | colorString = [colorString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; 207 | char *rgbChars = (char*) [[[colorString stringByReplacingOccurrencesOfString:@"rgba(" withString:@""] stringByReplacingOccurrencesOfString:@")" withString:@""] cStringUsingEncoding:NSUTF8StringEncoding]; 208 | int r = atoi( strtok(rgbChars, ",") ); 209 | int g = atoi( strtok(NULL, ",") ); 210 | int b = atoi( strtok(NULL, ",") ); 211 | CGFloat a = atof( strtok(NULL, ",") ); 212 | a = MAX( 0, MIN(a, 1.0) ); 213 | NSLog(@"r:%i, g:%i, b:%i a:%.f", r, g, b, a); 214 | colorValue = [NSColor colorWithCalibratedRed:r / 255.0 215 | green:g / 255.0 216 | blue:b / 255.0 217 | alpha:a]; 218 | } 219 | // Color in hex format 220 | else 221 | { 222 | unsigned colorInt = 0; 223 | NSArray *components = [colorString componentsSeparatedByString:@","]; 224 | 225 | NSString *hexString = colorString; 226 | NSString *alphaString = nil; 227 | 228 | if (components.count == 2) 229 | { 230 | hexString = [components objectAtIndex:0]; 231 | alphaString = [components objectAtIndex:1]; 232 | } 233 | 234 | [[NSScanner scannerWithString:hexString] scanHexInt:&colorInt]; 235 | 236 | if (alphaString.length > 0) 237 | { 238 | float alpha = 1.0; 239 | [[NSScanner scannerWithString:alphaString] scanFloat:&alpha]; 240 | 241 | colorValue = NSColorFromRGBA(colorInt, alpha); 242 | } 243 | else 244 | { 245 | colorValue = NSColorFromRGB(colorInt); 246 | } 247 | } 248 | 249 | return colorValue; 250 | } /* parseColorString */ 251 | 252 | - (void)createSuggestedColorsFile:(id)sender 253 | { 254 | NSDictionary *dictionary = @{@"colors":@{ @"Hex Format":@"ff7373" 255 | , @"HEX alpha Format":@"ff7373,0.8" 256 | , @"RGB Format":@"rgb(128,128,128)" 257 | , @"RGBA Format":@"rgba(128,128,128,0.5"} 258 | , @"useMyColors":@YES}; 259 | NSError *error; 260 | NSData *dicDat = [NSPropertyListSerialization dataWithPropertyList:dictionary format:NSPropertyListXMLFormat_v1_0 options:0 error:&error]; 261 | 262 | if (error) 263 | { 264 | return; 265 | } 266 | 267 | NSMenuItem *editMenuItem = [[NSApp mainMenu] itemWithTitle:@"Edit"]; 268 | 269 | if(editMenuItem) 270 | { 271 | [editMenuItem.submenu removeItem:self.separatorItem]; 272 | [editMenuItem.submenu removeItem:self.createFileMenuItem]; 273 | } 274 | 275 | XCProject *proj = [[XCProject alloc] initWithFilePath:self.projectWorkspacePath]; 276 | NSString *projName = [[[proj filePath] lastPathComponent] stringByDeletingPathExtension]; 277 | XCGroup *topGroup = [[proj rootGroup] memberWithDisplayName:projName]; 278 | 279 | if(!topGroup) 280 | { 281 | topGroup = [proj rootGroup]; 282 | } 283 | 284 | XCSourceFileDefinition *sourceFileDef = [XCSourceFileDefinition sourceDefinitionWithName:SuggestedColorsPlistName data:dicDat type:PropertyList]; 285 | [topGroup addSourceFile:sourceFileDef]; 286 | [proj save]; 287 | } /* createSuggestedColorsFile */ 288 | 289 | - (void)dealloc 290 | { 291 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 292 | } 293 | 294 | - (void)documentDidChange:(NSNotification*)notification 295 | { 296 | id doc = notification.object; 297 | 298 | if([doc isKindOfClass:objc_getClass("IDEPlistDocument")]) 299 | { 300 | if ([[[doc filePath] fileName] isEqualToString:SuggestedColorsPlistName]) 301 | { 302 | [self reloadColors:nil]; 303 | } 304 | } 305 | } /* documentDidChange */ 306 | 307 | - (void)notificationListener:(NSNotification*)notification 308 | { 309 | // let's filter all the "normal" NSxxx events so that we only 310 | // really see the Xcode specific events. 311 | if ( ([[notification name] length] >= 2) && [[[notification name] substringWithRange:NSMakeRange(0, 2)] isEqualTo:@"NS"] ) 312 | { 313 | return; 314 | } 315 | else 316 | { 317 | NSLog(@" Notification: %@", [notification name]); 318 | } 319 | } /* notificationListener */ 320 | 321 | @end 322 | -------------------------------------------------------------------------------- /SuggestedColors/SuggestedColors.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | colors 6 | 7 | My Custom Color 8 | ff7373 9 | 10 | useMyColors 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /SuggestedColors/View.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/Utils/XCKeyBuilder.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 - 2013 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | #import 14 | #import 15 | 16 | #define HASH_VALUE_STORAGE_SIZE 48 17 | 18 | typedef struct 19 | { 20 | char value[CC_MD5_DIGEST_LENGTH]; 21 | } HashValueMD5Hash; 22 | 23 | 24 | @interface XCKeyBuilder : NSObject 25 | { 26 | unsigned char _value[HASH_VALUE_STORAGE_SIZE]; 27 | } 28 | 29 | + (XCKeyBuilder*)forItemNamed:(NSString*)name; 30 | 31 | + (XCKeyBuilder*)createUnique; 32 | 33 | - (id)initHashValueMD5HashWithBytes:(const void*)bytes length:(NSUInteger)length; 34 | 35 | - (NSString*)build; 36 | 37 | @end 38 | 39 | -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/Utils/XCKeyBuilder.m: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 - 2013 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | #import "XCKeyBuilder.h" 13 | 14 | @implementation XCKeyBuilder 15 | 16 | /* ================================================= Class Methods ================================================== */ 17 | + (XCKeyBuilder*)forItemNamed:(NSString*)name 18 | { 19 | NSData* data = [name dataUsingEncoding:NSUTF8StringEncoding]; 20 | return [[XCKeyBuilder alloc] initHashValueMD5HashWithBytes:[data bytes] length:[data length]]; 21 | 22 | } 23 | 24 | + (XCKeyBuilder*)createUnique 25 | { 26 | CFUUIDRef theUUID = CFUUIDCreate(NULL); 27 | CFUUIDBytes bytes = CFUUIDGetUUIDBytes(theUUID); 28 | CFRelease(theUUID); 29 | 30 | return [[XCKeyBuilder alloc] initHashValueMD5HashWithBytes:&bytes length:sizeof(bytes)]; 31 | } 32 | 33 | /* ================================================== Initializers ================================================== */ 34 | - (id)initHashValueMD5HashWithBytes:(const void*)bytes length:(NSUInteger)length 35 | { 36 | self = [super init]; 37 | if (self != nil) 38 | { 39 | CC_MD5(bytes, (int) length, _value); 40 | } 41 | return self; 42 | } 43 | 44 | /* ================================================ Interface Methods =============================================== */ 45 | - (NSString*)build 46 | { 47 | NSInteger byteLength = sizeof(HashValueMD5Hash); 48 | NSMutableString* stringValue = [NSMutableString stringWithCapacity:byteLength * 2]; 49 | NSInteger i; 50 | for (i = 0; i < byteLength; i++) 51 | { 52 | [stringValue appendFormat:@"%02x", _value[i]]; 53 | } 54 | return [[stringValue substringToIndex:24] uppercaseString]; 55 | } 56 | 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XCAbstractDefinition.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 - 2013 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | #import 14 | 15 | typedef enum 16 | { 17 | 18 | /** 19 | * Creates the reference in the project and writes the contents to disk. If a file already exists at the specified 20 | * location, its contents will be updated. 21 | */ 22 | XCFileOperationTypeOverwrite, 23 | 24 | /** 25 | * Creates the reference in the project. If a file already exists at the specified location, the contents will not 26 | * be updated. 27 | */ 28 | XCFileOperationTypeAcceptExisting, 29 | 30 | /** 31 | * Creates the reference in the project, but does not write to disk. The filesystem is expected to be updated 32 | * through some other means. 33 | */ 34 | XCFileOperationTypeReferenceOnly 35 | } XCFileOperationType; 36 | 37 | /** 38 | * Holds properties to all types of resource that can be added to an Xcode project. 39 | */ 40 | @interface XCAbstractDefinition : NSObject 41 | { 42 | XCFileOperationType _fileOperationType; 43 | } 44 | 45 | @property(nonatomic) XCFileOperationType fileOperationType; 46 | 47 | 48 | @end -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XCAbstractDefinition.m: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 - 2013 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | #import "XCAbstractDefinition.h" 14 | 15 | 16 | @implementation XCAbstractDefinition 17 | 18 | @synthesize fileOperationType = _fileOperationType; 19 | 20 | 21 | /* ====================================================================================================================================== */ 22 | #pragma mark - Initialization & Destruction 23 | 24 | - (id)init 25 | { 26 | self = [super init]; 27 | if (self) 28 | { 29 | _fileOperationType = XCFileOperationTypeOverwrite; 30 | } 31 | return self; 32 | } 33 | 34 | 35 | @end -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XCBuildConfig.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | #import 13 | 14 | @class XCProject; 15 | 16 | @interface XCBuildConfig : NSObject 17 | { 18 | @private 19 | __weak XCProject* _project; 20 | NSString* _key; 21 | 22 | NSMutableDictionary* _buildSettings; 23 | NSMutableDictionary* _xcconfigSettings; 24 | } 25 | 26 | @property(nonatomic, readonly) NSDictionary* specifiedBuildSettings; 27 | 28 | + (NSDictionary*)buildConfigurationsFromArray:(NSArray*)array inProject:(XCProject*)project; 29 | 30 | - (instancetype)initWithProject:(XCProject*)project key:(NSString*)key; 31 | 32 | - (void)addBuildSettings:(NSDictionary*)buildSettings; 33 | 34 | - (void)addOrReplaceSetting:(id )setting forKey:(NSString*)key; 35 | 36 | - (id )valueForKey:(NSString*)key; 37 | 38 | + (NSString*)duplicatedBuildConfigurationListWithKey:(NSString*)buildConfigurationListKey inProject:(XCProject*)project 39 | withBuildConfigurationVisitor:(void (^)(NSMutableDictionary*))buildConfigurationVisitor; 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XCBuildConfig.m: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | 14 | #import "XCBuildConfig.h" 15 | #import "XCGroup.h" 16 | #import "XCKeyBuilder.h" 17 | #import "XCProject.h" 18 | #import "XCSourceFile.h" 19 | 20 | @implementation XCBuildConfig 21 | 22 | /* ====================================================================================================================================== */ 23 | #pragma mark - Class Methods 24 | 25 | + (NSDictionary*)buildConfigurationsFromArray:(NSArray*)array inProject:(XCProject*)project 26 | { 27 | NSMutableDictionary* configurations = [NSMutableDictionary dictionary]; 28 | 29 | for (NSString* buildConfigurationKey in array) 30 | { 31 | NSDictionary* buildConfiguration = [[project objects] objectForKey:buildConfigurationKey]; 32 | 33 | if ([[buildConfiguration valueForKey:@"isa"] asMemberType] == XCBuildConfigurationType) 34 | { 35 | XCBuildConfig* configuration = [configurations objectForKey:[buildConfiguration objectForKey:@"name"]]; 36 | if (!configuration) 37 | { 38 | configuration = [[XCBuildConfig alloc] initWithProject:project key:buildConfigurationKey]; 39 | 40 | [configurations setObject:configuration forKey:[buildConfiguration objectForKey:@"name"]]; 41 | } 42 | 43 | 44 | XCSourceFile* configurationFile = [project fileWithKey:[buildConfiguration objectForKey:@"baseConfigurationReference"]]; 45 | if (configurationFile) 46 | { 47 | NSString* path = configurationFile.path; 48 | 49 | if (![[NSFileManager defaultManager] fileExistsAtPath:path]) 50 | { 51 | XCGroup* group = [project groupWithSourceFile:configurationFile]; 52 | path = [[group pathRelativeToParent] stringByAppendingPathComponent:path]; 53 | } 54 | 55 | if (![[NSFileManager defaultManager] fileExistsAtPath:path]) 56 | { 57 | path = [[[project filePath] stringByDeletingLastPathComponent] stringByAppendingPathComponent:path]; 58 | } 59 | 60 | if (![[NSFileManager defaultManager] fileExistsAtPath:path]) 61 | { 62 | path = [[[project filePath] stringByDeletingLastPathComponent] stringByAppendingPathComponent:configurationFile.path]; 63 | } 64 | 65 | if (![[NSFileManager defaultManager] fileExistsAtPath:path]) 66 | { 67 | [NSException raise:@"XCConfig not found" format:@"Unable to find XCConfig file at %@", path]; 68 | } 69 | 70 | } 71 | 72 | [configuration addBuildSettings:[buildConfiguration objectForKey:@"buildSettings"]]; 73 | } 74 | } 75 | 76 | return configurations; 77 | } 78 | 79 | + (NSString*)duplicatedBuildConfigurationListWithKey:(NSString*)buildConfigurationListKey inProject:(XCProject*)project 80 | withBuildConfigurationVisitor:(void (^)(NSMutableDictionary*))buildConfigurationVisitor 81 | { 82 | 83 | NSDictionary* buildConfigurationList = project.objects[buildConfigurationListKey]; 84 | NSMutableDictionary* dupBuildConfigurationList = [buildConfigurationList mutableCopy]; 85 | 86 | NSMutableArray* dupBuildConfigurations = [NSMutableArray array]; 87 | 88 | for (NSString* buildConfigurationKey in buildConfigurationList[@"buildConfigurations"]) 89 | { 90 | [dupBuildConfigurations addObject:[self duplicatedBuildConfigurationWithKey:buildConfigurationKey inProject:project 91 | withBuildConfigurationVisitor:buildConfigurationVisitor]]; 92 | } 93 | 94 | dupBuildConfigurationList[@"buildConfigurations"] = dupBuildConfigurations; 95 | 96 | NSString* dupBuildConfigurationListKey = [[XCKeyBuilder createUnique] build]; 97 | 98 | project.objects[dupBuildConfigurationListKey] = dupBuildConfigurationList; 99 | 100 | return dupBuildConfigurationListKey; 101 | } 102 | 103 | /* ====================================================================================================================================== */ 104 | #pragma mark - Initialization & Destruction 105 | 106 | - (instancetype)initWithProject:(XCProject*)project key:(NSString*)key 107 | { 108 | self = [super init]; 109 | if (self) 110 | { 111 | _project = project; 112 | _key = [key copy]; 113 | 114 | _buildSettings = [[NSMutableDictionary alloc] init]; 115 | _xcconfigSettings = [[NSMutableDictionary alloc] init]; 116 | } 117 | return self; 118 | } 119 | 120 | - (id)init 121 | { 122 | return [self initWithProject:nil key:nil]; 123 | } 124 | 125 | 126 | /* ====================================================================================================================================== */ 127 | #pragma mark - Interface Methods 128 | 129 | - (NSDictionary*)specifiedBuildSettings 130 | { 131 | return [_buildSettings copy]; 132 | } 133 | 134 | - (void)addBuildSettings:(NSDictionary*)buildSettings 135 | { 136 | [_xcconfigSettings removeObjectsForKeys:[buildSettings allKeys]]; 137 | [_buildSettings addEntriesFromDictionary:buildSettings]; 138 | } 139 | 140 | - (void)addOrReplaceSetting:(id )setting forKey:(NSString*)key 141 | { 142 | NSDictionary* settings = [NSDictionary dictionaryWithObject:setting forKey:key]; 143 | [self addBuildSettings:settings]; 144 | 145 | 146 | NSMutableDictionary* dict = [[[_project objects] objectForKey:_key] mutableCopy]; 147 | [dict setValue:_buildSettings forKey:@"buildSettings"]; 148 | [_project.objects setValue:dict forKey:_key]; 149 | 150 | 151 | } 152 | 153 | 154 | - (id )valueForKey:(NSString*)key 155 | { 156 | id value = [_buildSettings objectForKey:key]; 157 | if (!value) 158 | { 159 | value = [_xcconfigSettings objectForKey:key]; 160 | } 161 | return value; 162 | } 163 | 164 | /* ====================================================================================================================================== */ 165 | #pragma mark - Utility Methods 166 | 167 | - (NSString*)description 168 | { 169 | NSMutableString* description = [[super description] mutableCopy]; 170 | 171 | [description appendFormat:@"build settings: %@, inherited: %@", _buildSettings, _xcconfigSettings]; 172 | 173 | return description; 174 | } 175 | 176 | 177 | /* ====================================================================================================================================== */ 178 | #pragma mark - Private Methods 179 | 180 | + (NSString*)duplicatedBuildConfigurationWithKey:(NSString*)buildConfigurationKey inProject:(XCProject*)project 181 | withBuildConfigurationVisitor:(void (^)(NSMutableDictionary*))buildConfigurationVisitor 182 | { 183 | NSDictionary* buildConfiguration = project.objects[buildConfigurationKey]; 184 | NSMutableDictionary* dupBuildConfiguration = [buildConfiguration mutableCopy]; 185 | 186 | buildConfigurationVisitor(dupBuildConfiguration); 187 | 188 | NSString* dupBuildConfigurationKey = [[XCKeyBuilder createUnique] build]; 189 | 190 | project.objects[dupBuildConfigurationKey] = dupBuildConfiguration; 191 | 192 | return dupBuildConfigurationKey; 193 | } 194 | 195 | @end 196 | -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XCClassDefinition.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 - 2013 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | #import 14 | #import "XCAbstractDefinition.h" 15 | 16 | typedef enum 17 | { 18 | ObjectiveC, 19 | ObjectiveCPlusPlus, 20 | CPlusPlus, 21 | } ClassDefinitionLanguage; 22 | 23 | @interface XCClassDefinition : XCAbstractDefinition 24 | { 25 | 26 | NSString* _className; 27 | NSString* _header; 28 | NSString* _source; 29 | 30 | @private 31 | ClassDefinitionLanguage _language; 32 | } 33 | 34 | @property(strong, nonatomic, readonly) NSString* className; 35 | @property(nonatomic, strong) NSString* header; 36 | @property(nonatomic, strong) NSString* source; 37 | 38 | + (XCClassDefinition*)classDefinitionWithName:(NSString*)fileName; 39 | 40 | + (XCClassDefinition*)classDefinitionWithName:(NSString*)className language:(ClassDefinitionLanguage)language; 41 | 42 | /** 43 | * Initializes a new objective-c class definition. 44 | */ 45 | - (id)initWithName:(NSString*)fileName; 46 | 47 | /** 48 | * Initializes a new class definition with the specified language. 49 | */ 50 | - (id)initWithName:(NSString*)className language:(ClassDefinitionLanguage)language; 51 | 52 | - (BOOL)isObjectiveC; 53 | 54 | - (BOOL)isObjectiveCPlusPlus; 55 | 56 | - (BOOL)isCPlusPlus; 57 | 58 | - (NSString*)headerFileName; 59 | 60 | - (NSString*)sourceFileName; 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XCClassDefinition.m: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 - 2013 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | 14 | 15 | 16 | #import "XCClassDefinition.h" 17 | 18 | @implementation XCClassDefinition 19 | 20 | 21 | @synthesize className = _className; 22 | @synthesize header = _header; 23 | @synthesize source = _source; 24 | 25 | /* ====================================================================================================================================== */ 26 | #pragma mark - Class Methods 27 | 28 | + (XCClassDefinition*)classDefinitionWithName:(NSString*)fileName 29 | { 30 | return [[XCClassDefinition alloc] initWithName:fileName]; 31 | } 32 | 33 | + (XCClassDefinition*)classDefinitionWithName:(NSString*)className language:(ClassDefinitionLanguage)language 34 | { 35 | return [[XCClassDefinition alloc] initWithName:className language:language]; 36 | } 37 | 38 | /* ====================================================================================================================================== */ 39 | #pragma mark - Initialization & Destruction 40 | 41 | - (id)initWithName:(NSString*)className 42 | { 43 | return [self initWithName:className language:ObjectiveC]; 44 | } 45 | 46 | - (id)initWithName:(NSString*)className language:(ClassDefinitionLanguage)language 47 | { 48 | self = [super init]; 49 | if (self) 50 | { 51 | _className = [className copy]; 52 | if (!(language == ObjectiveC || language == ObjectiveCPlusPlus || language == CPlusPlus)) 53 | { 54 | [NSException raise:NSInvalidArgumentException format:@"Language must be one of ObjectiveC, ObjectiveCPlusPlus"]; 55 | } 56 | _language = language; 57 | } 58 | return self; 59 | } 60 | 61 | 62 | /* ====================================================================================================================================== */ 63 | #pragma mark - Interface Methods 64 | 65 | - (BOOL)isObjectiveC 66 | { 67 | return _language == ObjectiveC; 68 | } 69 | 70 | - (BOOL)isObjectiveCPlusPlus 71 | { 72 | return _language == ObjectiveCPlusPlus; 73 | } 74 | 75 | - (BOOL)isCPlusPlus 76 | { 77 | return _language == CPlusPlus; 78 | } 79 | 80 | - (NSString*)headerFileName 81 | { 82 | return [_className stringByAppendingString:@".h"]; 83 | 84 | } 85 | 86 | - (NSString*)sourceFileName 87 | { 88 | NSString* sourceFileName = nil; 89 | if ([self isObjectiveC]) 90 | { 91 | sourceFileName = [_className stringByAppendingString:@".m"]; 92 | } 93 | else if ([self isObjectiveCPlusPlus]) 94 | { 95 | sourceFileName = [_className stringByAppendingString:@".mm"]; 96 | } 97 | else if ([self isCPlusPlus]) 98 | { 99 | sourceFileName = [_className stringByAppendingString:@".cpp"]; 100 | } 101 | return sourceFileName; 102 | } 103 | 104 | 105 | @end -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XCFileOperationQueue.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | #import 14 | 15 | 16 | @interface XCFileOperationQueue : NSObject 17 | { 18 | 19 | @private 20 | NSString* _baseDirectory; 21 | NSMutableDictionary* _filesToWrite; 22 | NSMutableDictionary* _frameworksToCopy; 23 | NSMutableArray* _filesToDelete; 24 | NSMutableArray* _directoriesToCreate; 25 | } 26 | 27 | 28 | - (id)initWithBaseDirectory:(NSString*)baseDirectory; 29 | 30 | - (BOOL)fileWithName:(NSString*)name existsInProjectDirectory:(NSString*)directory; 31 | 32 | - (void)queueTextFile:(NSString*)fileName inDirectory:(NSString*)directory withContents:(NSString*)contents; 33 | 34 | - (void)queueDataFile:(NSString*)fileName inDirectory:(NSString*)directory withContents:(NSData*)contents; 35 | 36 | - (void)queueFrameworkWithFilePath:(NSString*)filePath inDirectory:(NSString*)directory; 37 | 38 | - (void)queueDeletion:(NSString*)filePath; 39 | 40 | - (void)queueDirectory:(NSString*)withName inDirectory:(NSString*)parentDirectory; 41 | 42 | - (void)commitFileOperations; 43 | 44 | @end 45 | 46 | -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XCFileOperationQueue.m: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | 14 | #import "XCFileOperationQueue.h" 15 | 16 | @interface XCFileOperationQueue () 17 | 18 | - (NSString*)destinationPathFor:(NSString*)fileName inProjectDirectory:(NSString*)directory; 19 | 20 | - (void)performFileWrites; 21 | 22 | - (void)performCopyFrameworks; 23 | 24 | - (void)performFileDeletions; 25 | 26 | - (void)performCreateDirectories; 27 | 28 | @end 29 | 30 | 31 | @implementation XCFileOperationQueue 32 | 33 | /* ====================================================================================================================================== */ 34 | #pragma mark - Initialization & Destruction 35 | 36 | - (id)initWithBaseDirectory:(NSString*)baseDirectory 37 | { 38 | self = [super init]; 39 | if (self) 40 | { 41 | _baseDirectory = [baseDirectory copy]; 42 | _filesToWrite = [[NSMutableDictionary alloc] init]; 43 | _frameworksToCopy = [[NSMutableDictionary alloc] init]; 44 | _filesToDelete = [[NSMutableArray alloc] init]; 45 | _directoriesToCreate = [[NSMutableArray alloc] init]; 46 | } 47 | return self; 48 | } 49 | 50 | /* ====================================================================================================================================== */ 51 | #pragma mark - Interface Methods 52 | 53 | - (BOOL)fileWithName:(NSString*)name existsInProjectDirectory:(NSString*)directory 54 | { 55 | NSString* filePath = [self destinationPathFor:name inProjectDirectory:directory]; 56 | return [[NSFileManager defaultManager] fileExistsAtPath:filePath]; 57 | } 58 | 59 | 60 | - (void)queueTextFile:(NSString*)fileName inDirectory:(NSString*)directory withContents:(NSString*)contents 61 | { 62 | [_filesToWrite setObject:[contents dataUsingEncoding:NSUTF8StringEncoding] 63 | forKey:[self destinationPathFor:fileName inProjectDirectory:directory]]; 64 | } 65 | 66 | - (void)queueDataFile:(NSString*)fileName inDirectory:(NSString*)directory withContents:(NSData*)contents 67 | { 68 | [_filesToWrite setObject:contents forKey:[self destinationPathFor:fileName inProjectDirectory:directory]]; 69 | } 70 | 71 | 72 | - (void)queueFrameworkWithFilePath:(NSString*)filePath inDirectory:(NSString*)directory 73 | { 74 | 75 | NSURL* sourceUrl = [NSURL fileURLWithPath:filePath isDirectory:YES]; 76 | NSString* destinationPath = 77 | [[_baseDirectory stringByAppendingPathComponent:directory] stringByAppendingPathComponent:[filePath lastPathComponent]]; 78 | NSURL* destinationUrl = [NSURL fileURLWithPath:destinationPath isDirectory:YES]; 79 | [_frameworksToCopy setObject:sourceUrl forKey:destinationUrl]; 80 | } 81 | 82 | - (void)queueDeletion:(NSString*)filePath 83 | { 84 | NSLog(@"Queue deletion at: %@", filePath); 85 | [_filesToDelete addObject:filePath]; 86 | } 87 | 88 | - (void)queueDirectory:(NSString*)withName inDirectory:(NSString*)parentDirectory 89 | { 90 | [_directoriesToCreate addObject:[self destinationPathFor:withName inProjectDirectory:parentDirectory]]; 91 | } 92 | 93 | - (void)commitFileOperations 94 | { 95 | [self performFileWrites]; 96 | [self performCopyFrameworks]; 97 | [self performFileDeletions]; 98 | [self performCreateDirectories]; 99 | } 100 | 101 | 102 | /* ====================================================================================================================================== */ 103 | #pragma mark - Private Methods 104 | 105 | - (NSString*)destinationPathFor:(NSString*)fileName inProjectDirectory:(NSString*)directory 106 | { 107 | return [[_baseDirectory stringByAppendingPathComponent:directory] stringByAppendingPathComponent:fileName]; 108 | } 109 | 110 | - (void)performFileWrites 111 | { 112 | [_filesToWrite enumerateKeysAndObjectsUsingBlock:^(NSString* filePath, NSData* data, BOOL* stop) 113 | { 114 | NSError* error = nil; 115 | if (![data writeToFile:filePath options:NSDataWritingAtomic error:&error]) 116 | { 117 | [NSException raise:NSInternalInconsistencyException format:@"Error writing file at filePath: %@, error: %@", filePath, error]; 118 | } 119 | }]; 120 | [_filesToWrite removeAllObjects]; 121 | } 122 | 123 | - (void)performCopyFrameworks 124 | { 125 | [_frameworksToCopy enumerateKeysAndObjectsUsingBlock:^(NSURL* destinationUrl, NSURL* frameworkPath, BOOL* stop) 126 | { 127 | 128 | NSFileManager* fileManager = [NSFileManager defaultManager]; 129 | 130 | if ([fileManager fileExistsAtPath:[destinationUrl path]]) 131 | { 132 | [fileManager removeItemAtURL:destinationUrl error:nil]; 133 | } 134 | NSError* error = nil; 135 | if (![fileManager copyItemAtURL:frameworkPath toURL:destinationUrl error:&error]) 136 | { 137 | [NSException raise:NSInternalInconsistencyException format:@"Error writing file at filePath: %@", 138 | [frameworkPath absoluteString]]; 139 | } 140 | }]; 141 | [_frameworksToCopy removeAllObjects]; 142 | } 143 | 144 | - (void)performFileDeletions 145 | { 146 | for (NSString* filePath in [_filesToDelete reverseObjectEnumerator]) 147 | { 148 | NSString* fullPath = [_baseDirectory stringByAppendingPathComponent:filePath]; 149 | NSError* error = nil; 150 | 151 | if (![[NSFileManager defaultManager] removeItemAtPath:fullPath error:&error]) 152 | { 153 | NSLog(@"failed to remove item at path; error == %@", error); 154 | [NSException raise:NSInternalInconsistencyException format:@"Error deleting file at filePath: %@", filePath]; 155 | } 156 | else 157 | { 158 | NSLog(@"Deleted: %@", fullPath); 159 | } 160 | } 161 | [_filesToDelete removeAllObjects]; 162 | } 163 | 164 | - (void)performCreateDirectories 165 | { 166 | for (NSString* filePath in _directoriesToCreate) 167 | { 168 | NSFileManager* fileManager = [NSFileManager defaultManager]; 169 | if (![fileManager fileExistsAtPath:filePath]) 170 | { 171 | if (![fileManager createDirectoryAtPath:filePath withIntermediateDirectories:YES attributes:nil error:nil]) 172 | { 173 | [NSException raise:NSInvalidArgumentException format:@"Error: Create folder failed %@", filePath]; 174 | } 175 | } 176 | } 177 | } 178 | 179 | 180 | @end -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XCFrameworkDefinition.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | 14 | #import 15 | #import "XCAbstractDefinition.h" 16 | 17 | 18 | @interface XCFrameworkDefinition : XCAbstractDefinition 19 | { 20 | NSString* _filePath; 21 | BOOL _copyToDestination; 22 | } 23 | 24 | @property(nonatomic, strong, readonly) NSString* filePath; 25 | @property(nonatomic, readonly) BOOL copyToDestination; 26 | 27 | + (XCFrameworkDefinition*)frameworkDefinitionWithFilePath:(NSString*)filePath copyToDestination:(BOOL)copyToDestination; 28 | 29 | - (id)initWithFilePath:(NSString*)filePath copyToDestination:(BOOL)copyToDestination; 30 | 31 | - (NSString*)name; 32 | 33 | 34 | @end -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XCFrameworkDefinition.m: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | 14 | #import "XCFrameworkDefinition.h" 15 | 16 | @implementation XCFrameworkDefinition 17 | 18 | @synthesize filePath = _filePath; 19 | @synthesize copyToDestination = _copyToDestination; 20 | 21 | /* ====================================================================================================================================== */ 22 | #pragma mark - Class Methods 23 | 24 | + (XCFrameworkDefinition*)frameworkDefinitionWithFilePath:(NSString*)filePath copyToDestination:(BOOL)copyToDestination 25 | { 26 | 27 | return [[XCFrameworkDefinition alloc] initWithFilePath:filePath copyToDestination:copyToDestination]; 28 | } 29 | 30 | /* ====================================================================================================================================== */ 31 | #pragma mark - Initialization & Destruction 32 | 33 | - (id)initWithFilePath:(NSString*)filePath copyToDestination:(BOOL)copyToDestination 34 | { 35 | self = [super init]; 36 | if (self) 37 | { 38 | _filePath = [filePath copy]; 39 | _copyToDestination = copyToDestination; 40 | } 41 | return self; 42 | } 43 | 44 | /* ====================================================================================================================================== */ 45 | #pragma mark - Interface Methods 46 | 47 | - (NSString*)name 48 | { 49 | return [_filePath lastPathComponent]; 50 | } 51 | 52 | 53 | @end -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XCGroup.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | #import 14 | #import "XcodeGroupMember.h" 15 | 16 | @class XCProject; 17 | @class XCClassDefinition; 18 | @class XCSourceFile; 19 | @class XCXibDefinition; 20 | @class XCFileOperationQueue; 21 | @class XCFrameworkDefinition; 22 | @class XCSourceFileDefinition; 23 | @class XCSubProjectDefinition; 24 | 25 | 26 | /** 27 | * Represents a group container in an Xcode project. A group can contain members of type `XCSourceFile` or other 28 | * groups. 29 | */ 30 | @interface XCGroup : NSObject 31 | { 32 | 33 | NSString* _pathRelativeToParent; 34 | NSString* _key; 35 | NSString* _alias; 36 | 37 | 38 | @private 39 | NSString* _pathRelativeToProjectRoot; 40 | NSMutableArray* _children; 41 | NSMutableArray* _members; 42 | 43 | XCFileOperationQueue* _fileOperationQueue; // weak 44 | XCProject* _project; 45 | 46 | } 47 | 48 | 49 | /** 50 | * The alias of the group, which can be used to give the group a name other than the last path component. 51 | * 52 | * See: [XcodeGroupMember displayName] 53 | */ 54 | @property(nonatomic, strong, readonly) NSString* alias; 55 | 56 | /** 57 | * The path of the group relative to the group's parent. 58 | * 59 | * See: [XcodeGroupMember displayName] 60 | */ 61 | @property(nonatomic, strong, readonly) NSString* pathRelativeToParent; 62 | 63 | /** 64 | * The group's unique key. 65 | */ 66 | @property(nonatomic, strong, readonly) NSString* key; 67 | 68 | /** 69 | * An array containing the groups members as `XcodeGroupMember` types. 70 | */ 71 | @property(nonatomic, strong, readonly) NSMutableArray* children; 72 | 73 | 74 | #pragma mark Initializers 75 | 76 | + (XCGroup*)groupWithProject:(XCProject*)project key:(NSString*)key alias:(NSString*)alias path:(NSString*)path children:(NSArray*)children; 77 | 78 | - (id)initWithProject:(XCProject*)project key:(NSString*)key alias:(NSString*)alias path:(NSString*)path children:(NSArray*)children; 79 | 80 | #pragma mark Parent group 81 | 82 | - (void)removeFromParentGroup; 83 | 84 | - (void)removeFromParentDeletingChildren:(BOOL)deleteChildren; 85 | 86 | - (XCGroup*)parentGroup; 87 | 88 | - (BOOL)isRootGroup; 89 | 90 | #pragma mark Adding children 91 | /** 92 | * Adds a class to the group, as specified by the ClassDefinition. If the group already contains a class by the same 93 | * name, the contents will be updated. 94 | */ 95 | - (void)addClass:(XCClassDefinition*)classDefinition; 96 | 97 | /** 98 | * Adds a class to the group, making it a member of the specified [targets](XCTarget). 99 | */ 100 | - (void)addClass:(XCClassDefinition*)classDefinition toTargets:(NSArray*)targets; 101 | 102 | /** 103 | * Adds a framework to the group. If the group already contains the framework, the contents will be updated if the 104 | * framework definition's copyToDestination flag is yes, otherwise it will be ignored. 105 | */ 106 | - (void)addFramework:(XCFrameworkDefinition*)frameworkDefinition; 107 | 108 | /** 109 | * Adds a group with a path relative to this group. 110 | */ 111 | - (XCGroup*)addGroupWithPath:(NSString*)path; 112 | 113 | /** 114 | * Adds a framework to the group, making it a member of the specified targets. 115 | */ 116 | - (void)addFramework:(XCFrameworkDefinition*)framework toTargets:(NSArray*)targets; 117 | 118 | /** 119 | * Adds a source file of arbitrary type - image resource, header, etc. 120 | */ 121 | - (void)addSourceFile:(XCSourceFileDefinition*)sourceFileDefinition; 122 | 123 | 124 | /** 125 | * Adds a xib file to the group. If the group already contains a class by the same name, the contents will be updated. 126 | */ 127 | - (void)addXib:(XCXibDefinition*)xibDefinition; 128 | 129 | /** 130 | * Adds a xib to the group, making it a member of the specified [targets](XCTarget). 131 | */ 132 | - (void)addXib:(XCXibDefinition*)xibDefinition toTargets:(NSArray*)targets; 133 | 134 | /** 135 | * Adds a sub-project to the group. If the group already contains a sub-project by the same name, the contents will be 136 | * updated. 137 | * Returns boolean success/fail; if method fails, caller should assume that project file is corrupt (or file format has 138 | * changed). 139 | */ 140 | - (void)addSubProject:(XCSubProjectDefinition*)projectDefinition; 141 | 142 | /** 143 | * Adds a sub-project to the group, making it a member of the specified [targets](XCTarget). 144 | */ 145 | - (void)addSubProject:(XCSubProjectDefinition*)projectDefinition toTargets:(NSArray*)targets; 146 | 147 | - (void)removeSubProject:(XCSubProjectDefinition*)projectDefinition; 148 | 149 | - (void)removeSubProject:(XCSubProjectDefinition*)projectDefinition fromTargets:(NSArray*)targets; 150 | 151 | 152 | #pragma mark Locating children 153 | /** 154 | * Instances of `XCSourceFile` and `XCGroup` returned as the type `XcodeGroupMember`. 155 | */ 156 | - (NSArray*)members; 157 | 158 | /** 159 | * Instances of `XCSourceFile` from this group and any child groups. 160 | */ 161 | - (NSArray*)recursiveMembers; 162 | 163 | 164 | - (NSArray*)buildFileKeys; 165 | 166 | /** 167 | * Returns the child with the specified key, or nil. 168 | */ 169 | - (id )memberWithKey:(NSString*)key; 170 | 171 | /** 172 | * Returns the child with the specified name, or nil. 173 | */ 174 | - (id )memberWithDisplayName:(NSString*)name; 175 | 176 | 177 | @end 178 | -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XCGroup.m: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | 14 | #import "XCFrameworkDefinition.h" 15 | #import "XCTarget.h" 16 | #import "XCFileOperationQueue.h" 17 | #import "XCXibDefinition.h" 18 | #import "XCSourceFile.h" 19 | #import "XCGroup.h" 20 | #import "XCProject.h" 21 | #import "XCClassDefinition.h" 22 | #import "Utils/XCKeyBuilder.h" 23 | #import "XCSourceFileDefinition.h" 24 | #import "XCSubProjectDefinition.h" 25 | #import "XCProject+SubProject.h" 26 | 27 | 28 | @implementation XCGroup 29 | 30 | 31 | /* ====================================================================================================================================== */ 32 | #pragma mark - Class Methods 33 | 34 | + (XCGroup*)groupWithProject:(XCProject*)project key:(NSString*)key alias:(NSString*)alias path:(NSString*)path children:(NSArray*)children 35 | { 36 | 37 | return [[XCGroup alloc] initWithProject:project key:key alias:alias path:path children:children]; 38 | } 39 | 40 | /* ====================================================================================================================================== */ 41 | #pragma mark - Initialization & Destruction 42 | 43 | - (id)initWithProject:(XCProject*)project key:(NSString*)key alias:(NSString*)alias path:(NSString*)path children:(NSArray*)children 44 | { 45 | self = [super init]; 46 | if (self) 47 | { 48 | _project = project; 49 | _fileOperationQueue = [_project fileOperationQueue]; 50 | _key = [key copy]; 51 | _alias = [alias copy]; 52 | _pathRelativeToParent = [path copy]; 53 | 54 | _children = [children mutableCopy]; 55 | if (!_children) 56 | { 57 | _children = [[NSMutableArray alloc] init]; 58 | } 59 | } 60 | return self; 61 | } 62 | 63 | /* ====================================================================================================================================== */ 64 | #pragma mark - Interface Methods 65 | 66 | #pragma mark Parent group 67 | 68 | - (void)removeFromParentGroup 69 | { 70 | [self removeFromParentDeletingChildren:NO]; 71 | } 72 | 73 | 74 | - (void)removeFromParentDeletingChildren:(BOOL)deleteChildren 75 | { 76 | if (deleteChildren) 77 | { 78 | [_fileOperationQueue queueDeletion:[self pathRelativeToProjectRoot]]; 79 | } 80 | NSDictionary* dictionary = [[_project objects] objectForKey:_key]; 81 | NSLog(@"Here's the dictionary: %@", dictionary); 82 | 83 | [[_project objects] removeObjectForKey:_key]; 84 | 85 | dictionary = [[_project objects] objectForKey:_key]; 86 | NSLog(@"Here's the dictionary: %@", dictionary); 87 | 88 | for (XCTarget* target in [_project targets]) 89 | { 90 | [target removeMembersWithKeys:[self recursiveMembers]]; 91 | } 92 | NSLog(@"Done!!!"); 93 | } 94 | 95 | - (XCGroup*)parentGroup 96 | { 97 | return [_project groupForGroupMemberWithKey:_key]; 98 | } 99 | 100 | - (BOOL)isRootGroup 101 | { 102 | return [self pathRelativeToParent] == nil && [self displayName] == nil; 103 | } 104 | 105 | 106 | /* ================================================================================================================== */ 107 | #pragma mark Adding children 108 | 109 | 110 | - (void)addClass:(XCClassDefinition*)classDefinition 111 | { 112 | 113 | if ([classDefinition header]) 114 | { 115 | [self makeGroupMemberWithName:[classDefinition headerFileName] contents:[classDefinition header] type:SourceCodeHeader 116 | fileOperationStyle:[classDefinition fileOperationType]]; 117 | } 118 | 119 | if ([classDefinition isObjectiveC]) 120 | { 121 | [self makeGroupMemberWithName:[classDefinition sourceFileName] contents:[classDefinition source] type:SourceCodeObjC 122 | fileOperationStyle:[classDefinition fileOperationType]]; 123 | } 124 | else if ([classDefinition isObjectiveCPlusPlus]) 125 | { 126 | [self makeGroupMemberWithName:[classDefinition sourceFileName] contents:[classDefinition source] type:SourceCodeObjCPlusPlus 127 | fileOperationStyle:[classDefinition fileOperationType]]; 128 | } 129 | 130 | [[_project objects] setObject:[self asDictionary] forKey:_key]; 131 | } 132 | 133 | 134 | - (void)addClass:(XCClassDefinition*)classDefinition toTargets:(NSArray*)targets 135 | { 136 | [self addClass:classDefinition]; 137 | XCSourceFile* sourceFile = [_project fileWithName:[classDefinition sourceFileName]]; 138 | [self addSourceFile:sourceFile toTargets:targets]; 139 | } 140 | 141 | - (void)addFramework:(XCFrameworkDefinition*)frameworkDefinition 142 | { 143 | if (([self memberWithDisplayName:[frameworkDefinition name]]) == nil) 144 | { 145 | NSDictionary* fileReference; 146 | if ([frameworkDefinition copyToDestination]) 147 | { 148 | fileReference = [self makeFileReferenceWithPath:[frameworkDefinition name] name:nil type:Framework]; 149 | BOOL copyFramework = NO; 150 | if ([frameworkDefinition fileOperationType] == XCFileOperationTypeOverwrite) 151 | { 152 | copyFramework = YES; 153 | } 154 | else if ([frameworkDefinition fileOperationType] == XCFileOperationTypeAcceptExisting) 155 | { 156 | NSString* frameworkName = [[frameworkDefinition filePath] lastPathComponent]; 157 | if (![_fileOperationQueue fileWithName:frameworkName existsInProjectDirectory:[self pathRelativeToProjectRoot]]) 158 | { 159 | copyFramework = YES; 160 | } 161 | 162 | } 163 | if (copyFramework) 164 | { 165 | [_fileOperationQueue queueFrameworkWithFilePath:[frameworkDefinition filePath] 166 | inDirectory:[self pathRelativeToProjectRoot]]; 167 | } 168 | } 169 | else 170 | { 171 | NSString* path = [frameworkDefinition filePath]; 172 | NSString* name = [frameworkDefinition name]; 173 | fileReference = [self makeFileReferenceWithPath:path name:name type:Framework]; 174 | } 175 | NSString* frameworkKey = [[XCKeyBuilder forItemNamed:[frameworkDefinition name]] build]; 176 | [[_project objects] setObject:fileReference forKey:frameworkKey]; 177 | [self addMemberWithKey:frameworkKey]; 178 | } 179 | [[_project objects] setObject:[self asDictionary] forKey:_key]; 180 | } 181 | 182 | 183 | - (void)addFramework:(XCFrameworkDefinition*)frameworkDefinition toTargets:(NSArray*)targets 184 | { 185 | [self addFramework:frameworkDefinition]; 186 | XCSourceFile* frameworkSourceRef = (XCSourceFile*) [self memberWithDisplayName:[frameworkDefinition name]]; 187 | [self addSourceFile:frameworkSourceRef toTargets:targets]; 188 | } 189 | 190 | - (XCGroup*)addGroupWithPath:(NSString*)path 191 | { 192 | NSString* groupKeyPath = self.pathRelativeToProjectRoot ? [self.pathRelativeToProjectRoot stringByAppendingPathComponent:path] : path; 193 | 194 | NSString* groupKey = [[XCKeyBuilder forItemNamed:groupKeyPath] build]; 195 | 196 | NSArray* members = [self members]; 197 | for (id groupMember in members) 198 | { 199 | if ([groupMember groupMemberType] == PBXGroupType || [groupMember groupMemberType] == PBXVariantGroupType) 200 | { 201 | 202 | if ([[[groupMember pathRelativeToProjectRoot] lastPathComponent] isEqualToString:path] || 203 | [[groupMember displayName] isEqualToString:path] || [[groupMember key] isEqualToString:groupKey]) 204 | { 205 | return nil; 206 | } 207 | } 208 | } 209 | 210 | XCGroup* group = [[XCGroup alloc] initWithProject:_project key:groupKey alias:nil path:path children:nil]; 211 | NSDictionary* groupDict = [group asDictionary]; 212 | 213 | [[_project objects] setObject:groupDict forKey:groupKey]; 214 | [_fileOperationQueue queueDirectory:path inDirectory:[self pathRelativeToProjectRoot]]; 215 | [self addMemberWithKey:groupKey]; 216 | 217 | NSDictionary* dict = [self asDictionary]; 218 | [[_project objects] setObject:dict forKey:_key]; 219 | 220 | return group; 221 | } 222 | 223 | - (void)addSourceFile:(XCSourceFileDefinition*)sourceFileDefinition 224 | { 225 | [self makeGroupMemberWithName:[sourceFileDefinition sourceFileName] contents:[sourceFileDefinition data] 226 | type:[sourceFileDefinition type] fileOperationStyle:[sourceFileDefinition fileOperationType]]; 227 | [[_project objects] setObject:[self asDictionary] forKey:_key]; 228 | } 229 | 230 | - (void)addXib:(XCXibDefinition*)xibDefinition 231 | { 232 | [self makeGroupMemberWithName:[xibDefinition xibFileName] contents:[xibDefinition content] type:XibFile 233 | fileOperationStyle:[xibDefinition fileOperationType]]; 234 | [[_project objects] setObject:[self asDictionary] forKey:_key]; 235 | } 236 | 237 | - (void)addXib:(XCXibDefinition*)xibDefinition toTargets:(NSArray*)targets 238 | { 239 | [self addXib:xibDefinition]; 240 | XCSourceFile* sourceFile = [_project fileWithName:[xibDefinition xibFileName]]; 241 | [self addSourceFile:sourceFile toTargets:targets]; 242 | } 243 | 244 | 245 | // adds an xcodeproj as a subproject of the current project. 246 | - (void)addSubProject:(XCSubProjectDefinition*)projectDefinition 247 | { 248 | // set up path to the xcodeproj file as Xcode sees it - path to top level of project + group path if any 249 | [projectDefinition initFullProjectPath:_project.filePath groupPath:[self pathRelativeToParent]]; 250 | 251 | // create PBXFileReference for xcodeproj file and add to PBXGroup for the current group 252 | // (will retrieve existing if already there) 253 | [self makeGroupMemberWithName:[projectDefinition projectFileName] path:[projectDefinition pathRelativeToProjectRoot] type:XcodeProject 254 | fileOperationStyle:[projectDefinition fileOperationType]]; 255 | [[_project objects] setObject:[self asDictionary] forKey:_key]; 256 | 257 | // create PBXContainerItemProxies and PBXReferenceProxies 258 | [_project addProxies:projectDefinition]; 259 | 260 | // add projectReferences key to PBXProject 261 | [self addProductsGroupToProject:projectDefinition]; 262 | } 263 | 264 | // adds an xcodeproj as a subproject of the current project, and also adds all build products except for test bundle(s) 265 | // to targets. 266 | - (void)addSubProject:(XCSubProjectDefinition*)projectDefinition toTargets:(NSArray*)targets 267 | { 268 | [self addSubProject:projectDefinition]; 269 | 270 | // add subproject's build products to targets (does not add the subproject's test bundle) 271 | NSArray* buildProductFiles = [_project buildProductsForTargets:[projectDefinition projectKey]]; 272 | for (XCSourceFile* file in buildProductFiles) 273 | { 274 | [self addSourceFile:file toTargets:targets]; 275 | } 276 | // add main target of subproject as target dependency to main target of project 277 | [_project addAsTargetDependency:projectDefinition toTargets:targets]; 278 | } 279 | 280 | // removes an xcodeproj from the current project. 281 | - (void)removeSubProject:(XCSubProjectDefinition*)projectDefinition 282 | { 283 | if (projectDefinition == nil) 284 | { 285 | return; 286 | } 287 | 288 | // set up path to the xcodeproj file as Xcode sees it - path to top level of project + group path if any 289 | [projectDefinition initFullProjectPath:_project.filePath groupPath:[self pathRelativeToParent]]; 290 | 291 | NSString* xcodeprojKey = [projectDefinition projectKey]; 292 | 293 | // Remove from group and remove PBXFileReference 294 | [self removeGroupMemberWithKey:xcodeprojKey]; 295 | 296 | // remove PBXContainerItemProxies and PBXReferenceProxies 297 | [_project removeProxies:xcodeprojKey]; 298 | 299 | // get the key for the Products group 300 | NSString* productsGroupKey = [_project productsGroupKeyForKey:xcodeprojKey]; 301 | 302 | // remove from the ProjectReferences array of PBXProject 303 | [_project removeFromProjectReferences:xcodeprojKey forProductsGroup:productsGroupKey]; 304 | 305 | // remove PDXBuildFile entries 306 | [self removeProductsGroupFromProject:productsGroupKey]; 307 | 308 | // remove Products group 309 | [[_project objects] removeObjectForKey:productsGroupKey]; 310 | 311 | // remove from all targets 312 | [_project removeTargetDependencies:[projectDefinition name]]; 313 | } 314 | 315 | - (void)removeSubProject:(XCSubProjectDefinition*)projectDefinition fromTargets:(NSArray*)targets 316 | { 317 | if (projectDefinition == nil) 318 | { 319 | return; 320 | } 321 | 322 | // set up path to the xcodeproj file as Xcode sees it - path to top level of project + group path if any 323 | [projectDefinition initFullProjectPath:_project.filePath groupPath:[self pathRelativeToParent]]; 324 | 325 | NSString* xcodeprojKey = [projectDefinition projectKey]; 326 | 327 | // Remove PBXBundleFile entries and corresponding inclusion in PBXFrameworksBuildPhase and PBXResourcesBuidPhase 328 | NSString* productsGroupKey = [_project productsGroupKeyForKey:xcodeprojKey]; 329 | [self removeProductsGroupFromProject:productsGroupKey]; 330 | 331 | // Remove the PBXContainerItemProxy for this xcodeproj with proxyType 1 332 | NSString* containerItemProxyKey = [_project containerItemProxyKeyForName:[projectDefinition pathRelativeToProjectRoot] proxyType:@"1"]; 333 | if (containerItemProxyKey != nil) 334 | { 335 | [[_project objects] removeObjectForKey:containerItemProxyKey]; 336 | } 337 | 338 | // Remove PBXTargetDependency and entry in PBXNativeTarget 339 | [_project removeTargetDependencies:[projectDefinition name]]; 340 | } 341 | 342 | /* ====================================================================================================================================== */ 343 | #pragma mark Members 344 | 345 | - (NSArray*)members 346 | { 347 | if (_members == nil) 348 | { 349 | _members = [[NSMutableArray alloc] init]; 350 | for (NSString* childKey in _children) 351 | { 352 | XcodeMemberType type = [self typeForKey:childKey]; 353 | 354 | @autoreleasepool 355 | { 356 | if (type == PBXGroupType || type == PBXVariantGroupType) 357 | { 358 | [_members addObject:[_project groupWithKey:childKey]]; 359 | } 360 | else if (type == PBXFileReferenceType) 361 | { 362 | [_members addObject:[_project fileWithKey:childKey]]; 363 | } 364 | } 365 | } 366 | } 367 | return _members; 368 | } 369 | 370 | - (NSArray*)recursiveMembers 371 | { 372 | NSMutableArray* recursiveMembers = [NSMutableArray array]; 373 | for (NSString* childKey in _children) 374 | { 375 | XcodeMemberType type = [self typeForKey:childKey]; 376 | if (type == PBXGroupType || type == PBXVariantGroupType) 377 | { 378 | XCGroup* group = [_project groupWithKey:childKey]; 379 | NSArray* groupChildren = [group recursiveMembers]; 380 | [recursiveMembers addObjectsFromArray:groupChildren]; 381 | } 382 | else if (type == PBXFileReferenceType) 383 | { 384 | [recursiveMembers addObject:childKey]; 385 | } 386 | } 387 | [recursiveMembers addObject:_key]; 388 | return [recursiveMembers arrayByAddingObjectsFromArray:recursiveMembers]; 389 | } 390 | 391 | - (NSArray*)buildFileKeys 392 | { 393 | 394 | NSMutableArray* arrayOfBuildFileKeys = [NSMutableArray array]; 395 | for (id groupMember in [self members]) 396 | { 397 | 398 | if ([groupMember groupMemberType] == PBXGroupType || [groupMember groupMemberType] == PBXVariantGroupType) 399 | { 400 | XCGroup* group = (XCGroup*) groupMember; 401 | [arrayOfBuildFileKeys addObjectsFromArray:[group buildFileKeys]]; 402 | } 403 | else if ([groupMember groupMemberType] == PBXFileReferenceType) 404 | { 405 | [arrayOfBuildFileKeys addObject:[groupMember key]]; 406 | } 407 | } 408 | return arrayOfBuildFileKeys; 409 | } 410 | 411 | - (id )memberWithKey:(NSString*)key 412 | { 413 | id groupMember = nil; 414 | 415 | if ([_children containsObject:key]) 416 | { 417 | XcodeMemberType type = [self typeForKey:key]; 418 | if (type == PBXGroupType || type == PBXVariantGroupType) 419 | { 420 | groupMember = [_project groupWithKey:key]; 421 | } 422 | else if (type == PBXFileReferenceType) 423 | { 424 | groupMember = [_project fileWithKey:key]; 425 | } 426 | } 427 | return groupMember; 428 | } 429 | 430 | - (id )memberWithDisplayName:(NSString*)name 431 | { 432 | for (id member in [self members]) 433 | { 434 | if ([[member displayName] isEqualToString:name]) 435 | { 436 | return member; 437 | } 438 | } 439 | return nil; 440 | } 441 | 442 | /* ====================================================================================================================================== */ 443 | #pragma mark - Protocol Methods 444 | 445 | - (XcodeMemberType)groupMemberType 446 | { 447 | return [self typeForKey:self.key]; 448 | } 449 | 450 | - (NSString*)displayName 451 | { 452 | if (_alias) 453 | { 454 | return _alias; 455 | } 456 | return [_pathRelativeToParent lastPathComponent]; 457 | } 458 | 459 | - (NSString*)pathRelativeToProjectRoot 460 | { 461 | if (_pathRelativeToProjectRoot == nil) 462 | { 463 | NSMutableArray* pathComponents = [[NSMutableArray alloc] init]; 464 | XCGroup* group = nil; 465 | NSString* key = [_key copy]; 466 | 467 | while ((group = [_project groupForGroupMemberWithKey:key]) != nil && !([group pathRelativeToParent] == nil)) 468 | { 469 | [pathComponents addObject:[group pathRelativeToParent]]; 470 | id old = key; 471 | key = [[group key] copy]; 472 | } 473 | 474 | NSMutableString* fullPath = [[NSMutableString alloc] init]; 475 | for (NSInteger i = (NSInteger) [pathComponents count] - 1; i >= 0; i--) 476 | { 477 | [fullPath appendFormat:@"%@/", [pathComponents objectAtIndex:i]]; 478 | } 479 | _pathRelativeToProjectRoot = [[fullPath stringByAppendingPathComponent:_pathRelativeToParent] copy]; 480 | } 481 | return _pathRelativeToProjectRoot; 482 | } 483 | 484 | /* ====================================================================================================================================== */ 485 | #pragma mark - Utility Methods 486 | 487 | - (NSString*)description 488 | { 489 | return [NSString stringWithFormat:@"Group: displayName = %@, key=%@", [self displayName], _key]; 490 | } 491 | 492 | /* ====================================================================================================================================== */ 493 | #pragma mark - Private Methods 494 | 495 | #pragma mark Private 496 | 497 | - (void)addMemberWithKey:(NSString*)key 498 | { 499 | 500 | for (NSString* childKey in _children) 501 | { 502 | if ([childKey isEqualToString:key]) 503 | { 504 | [self flagMembersAsDirty]; 505 | return; 506 | } 507 | } 508 | [_children addObject:key]; 509 | [self flagMembersAsDirty]; 510 | } 511 | 512 | - (void)flagMembersAsDirty 513 | { 514 | _members = nil; 515 | } 516 | 517 | /* ====================================================================================================================================== */ 518 | 519 | - (void)makeGroupMemberWithName:(NSString*)name contents:(id)contents type:(XcodeSourceFileType)type 520 | fileOperationStyle:(XCFileOperationType)fileOperationStyle 521 | { 522 | 523 | NSString* filePath; 524 | XCSourceFile* currentSourceFile = (XCSourceFile*) [self memberWithDisplayName:name]; 525 | if ((currentSourceFile) == nil) 526 | { 527 | NSDictionary* reference = [self makeFileReferenceWithPath:name name:nil type:type]; 528 | NSString* fileKey = [[XCKeyBuilder forItemNamed:name] build]; 529 | [[_project objects] setObject:reference forKey:fileKey]; 530 | [self addMemberWithKey:fileKey]; 531 | filePath = [self pathRelativeToProjectRoot]; 532 | } 533 | else 534 | { 535 | filePath = [[currentSourceFile pathRelativeToProjectRoot] stringByDeletingLastPathComponent]; 536 | } 537 | 538 | BOOL writeFile = NO; 539 | if (fileOperationStyle == XCFileOperationTypeOverwrite) 540 | { 541 | writeFile = YES; 542 | [_fileOperationQueue fileWithName:name existsInProjectDirectory:filePath]; 543 | } 544 | else if (fileOperationStyle == XCFileOperationTypeAcceptExisting && 545 | ![_fileOperationQueue fileWithName:name existsInProjectDirectory:filePath]) 546 | { 547 | writeFile = YES; 548 | } 549 | if (writeFile) 550 | { 551 | if ([contents isKindOfClass:[NSString class]]) 552 | { 553 | [_fileOperationQueue queueTextFile:name inDirectory:filePath withContents:contents]; 554 | } 555 | else 556 | { 557 | [_fileOperationQueue queueDataFile:name inDirectory:filePath withContents:contents]; 558 | } 559 | } 560 | } 561 | 562 | /* ====================================================================================================================================== */ 563 | 564 | #pragma mark Xcodeproj methods 565 | 566 | // creates PBXFileReference and adds to group if not already there; returns key for file reference. Locates 567 | // member via path rather than name, because that is how subprojects are stored by Xcode 568 | - (void)makeGroupMemberWithName:(NSString*)name path:(NSString*)path type:(XcodeSourceFileType)type 569 | fileOperationStyle:(XCFileOperationType)fileOperationStyle 570 | { 571 | XCSourceFile* currentSourceFile = (XCSourceFile*) [self memberWithDisplayName:name]; 572 | if ((currentSourceFile) == nil) 573 | { 574 | NSDictionary* reference = [self makeFileReferenceWithPath:path name:name type:type]; 575 | NSString* fileKey = [[XCKeyBuilder forItemNamed:name] build]; 576 | [[_project objects] setObject:reference forKey:fileKey]; 577 | [self addMemberWithKey:fileKey]; 578 | } 579 | } 580 | 581 | // makes a new group called Products and returns its key 582 | - (NSString*)makeProductsGroup:(XCSubProjectDefinition*)xcodeprojDefinition 583 | { 584 | NSMutableArray* children = [NSMutableArray array]; 585 | NSString* uniquer = @""; 586 | for (NSString* productName in [xcodeprojDefinition buildProductNames]) 587 | { 588 | [children addObject:[_project referenceProxyKeyForName:productName]]; 589 | uniquer = [uniquer stringByAppendingString:productName]; 590 | } 591 | NSString* productKey = [[XCKeyBuilder forItemNamed:[NSString stringWithFormat:@"%@-Products", uniquer]] build]; 592 | XCGroup* productsGroup = [XCGroup groupWithProject:_project key:productKey alias:@"Products" path:nil children:children]; 593 | [[_project objects] setObject:[productsGroup asDictionary] forKey:productKey]; 594 | return productKey; 595 | } 596 | 597 | // makes a new Products group (by calling the method above), makes a new projectReferences array for it and 598 | // then adds it to the PBXProject object 599 | - (void)addProductsGroupToProject:(XCSubProjectDefinition*)xcodeprojDefinition 600 | { 601 | NSString* productKey = [self makeProductsGroup:xcodeprojDefinition]; 602 | 603 | NSMutableDictionary* PBXProjectDict = [_project PBXProjectDict]; 604 | NSMutableArray* projectReferences = [PBXProjectDict valueForKey:@"projectReferences"]; 605 | 606 | NSMutableDictionary* newProjectReference = [NSMutableDictionary dictionary]; 607 | [newProjectReference setObject:productKey forKey:@"ProductGroup"]; 608 | NSString* projectFileKey = [[_project fileWithName:[xcodeprojDefinition pathRelativeToProjectRoot]] key]; 609 | [newProjectReference setObject:projectFileKey forKey:@"ProjectRef"]; 610 | 611 | if (projectReferences == nil) 612 | { 613 | projectReferences = [NSMutableArray array]; 614 | } 615 | [projectReferences addObject:newProjectReference]; 616 | [PBXProjectDict setObject:projectReferences forKey:@"projectReferences"]; 617 | } 618 | 619 | // removes PBXFileReference from group and project 620 | - (void)removeGroupMemberWithKey:(NSString*)key 621 | { 622 | NSMutableArray* children = [self valueForKey:@"children"]; 623 | [children removeObject:key]; 624 | [[_project objects] setObject:[self asDictionary] forKey:_key]; 625 | // remove PBXFileReference 626 | [[_project objects] removeObjectForKey:key]; 627 | } 628 | 629 | // removes the given key from the files arrays of the given section, if found (intended to be used with 630 | // PBXFrameworksBuildPhase and PBXResourcesBuildPhase) 631 | // they are not required because we are currently not adding these entries; Xcode is doing it for us. The existing 632 | // code for adding to a target doesn't do it, and I didn't add it since Xcode will take care of it for me and I was 633 | // avoiding modifying existing code as much as possible) 634 | - (void)removeBuildPhaseFileKey:(NSString*)key forType:(XcodeMemberType)memberType 635 | { 636 | NSArray* buildPhases = [_project keysForProjectObjectsOfType:memberType withIdentifier:nil singleton:NO required:NO]; 637 | for (NSString* buildPhaseKey in buildPhases) 638 | { 639 | NSDictionary* buildPhaseDict = [[_project objects] valueForKey:buildPhaseKey]; 640 | NSMutableArray* fileKeys = [buildPhaseDict valueForKey:@"files"]; 641 | for (NSString* fileKey in fileKeys) 642 | { 643 | if ([fileKey isEqualToString:key]) 644 | { 645 | [fileKeys removeObject:fileKey]; 646 | } 647 | } 648 | } 649 | } 650 | 651 | // removes entries from PBXBuildFiles, PBXFrameworksBuildPhase and PBXResourcesBuildPhase 652 | - (void)removeProductsGroupFromProject:(NSString*)key 653 | { 654 | // remove product group's build products from PDXBuildFiles 655 | NSDictionary* productsGroup = [[_project objects] objectForKey:key]; 656 | for (NSString* childKey in [productsGroup valueForKey:@"children"]) 657 | { 658 | NSArray* buildFileKeys = [_project keysForProjectObjectsOfType:PBXBuildFileType withIdentifier:childKey singleton:NO required:NO]; 659 | // could be zero - we didn't add the test bundle as a build product 660 | if ([buildFileKeys count] == 1) 661 | { 662 | NSString* buildFileKey = [buildFileKeys objectAtIndex:0]; 663 | [[_project objects] removeObjectForKey:buildFileKey]; 664 | [self removeBuildPhaseFileKey:buildFileKey forType:PBXFrameworksBuildPhaseType]; 665 | [self removeBuildPhaseFileKey:buildFileKey forType:PBXResourcesBuildPhaseType]; 666 | } 667 | } 668 | } 669 | 670 | /* ====================================================================================================================================== */ 671 | 672 | #pragma mark Dictionary Representations 673 | 674 | - (NSDictionary*)makeFileReferenceWithPath:(NSString*)path name:(NSString*)name type:(XcodeSourceFileType)type 675 | { 676 | NSMutableDictionary* reference = [NSMutableDictionary dictionary]; 677 | [reference setObject:[NSString stringFromMemberType:PBXFileReferenceType] forKey:@"isa"]; 678 | [reference setObject:@"4" forKey:@"fileEncoding"]; 679 | [reference setObject:NSStringFromXCSourceFileType(type) forKey:@"lastKnownFileType"]; 680 | if (name != nil) 681 | { 682 | [reference setObject:[name lastPathComponent] forKey:@"name"]; 683 | } 684 | if (path != nil) 685 | { 686 | [reference setObject:path forKey:@"path"]; 687 | } 688 | [reference setObject:@"" forKey:@"sourceTree"]; 689 | return reference; 690 | } 691 | 692 | 693 | - (NSDictionary*)asDictionary 694 | { 695 | NSMutableDictionary* groupData = [NSMutableDictionary dictionary]; 696 | [groupData setObject:[NSString stringFromMemberType:PBXGroupType] forKey:@"isa"]; 697 | [groupData setObject:@"" forKey:@"sourceTree"]; 698 | 699 | if (_alias != nil) 700 | { 701 | [groupData setObject:_alias forKey:@"name"]; 702 | } 703 | 704 | if (_pathRelativeToParent) 705 | { 706 | [groupData setObject:_pathRelativeToParent forKey:@"path"]; 707 | } 708 | 709 | if (_children) 710 | { 711 | [groupData setObject:_children forKey:@"children"]; 712 | } 713 | 714 | return groupData; 715 | } 716 | 717 | - (XcodeMemberType)typeForKey:(NSString*)key 718 | { 719 | NSDictionary* obj = [[_project objects] valueForKey:key]; 720 | return [[obj valueForKey:@"isa"] asMemberType]; 721 | } 722 | 723 | - (void)addSourceFile:(XCSourceFile*)sourceFile toTargets:(NSArray*)targets 724 | { 725 | for (XCTarget* target in targets) 726 | { 727 | [target addMember:sourceFile]; 728 | } 729 | } 730 | 731 | @end -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XCProject+SubProject.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | 14 | #import 15 | #import "XCProject.h" 16 | 17 | @interface XCProject (SubProject) 18 | 19 | 20 | - (NSString*)referenceProxyKeyForName:(NSString*)name; 21 | 22 | - (NSArray*)buildProductsForTargets:(NSString*)xcodeprojKey; 23 | 24 | - (void)addAsTargetDependency:(XCSubProjectDefinition*)xcodeprojDefinition toTargets:(NSArray*)targets; 25 | 26 | - (NSArray*)keysForProjectObjectsOfType:(XcodeMemberType)memberType withIdentifier:(NSString*)identifier singleton:(BOOL)singleton 27 | required:(BOOL)required; 28 | 29 | - (NSMutableDictionary*)PBXProjectDict; 30 | 31 | - (void)removeProxies:(NSString*)xcodeprojKey; 32 | 33 | - (void)addProxies:(XCSubProjectDefinition*)xcodeproj; 34 | 35 | - (void)removeFromProjectReferences:(NSString*)key forProductsGroup:(NSString*)productsGroupKey; 36 | 37 | - (void)removeTargetDependencies:(NSString*)name; 38 | 39 | - (NSString*)containerItemProxyKeyForName:(NSString*)name proxyType:(NSString*)proxyType; 40 | 41 | - (NSString*)productsGroupKeyForKey:(NSString*)key; 42 | 43 | @end -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XCProject+SubProject.m: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | 14 | #import "XCSourceFile.h" 15 | #import "XCTarget.h" 16 | #import "Utils/XCKeyBuilder.h" 17 | #import "XCProject+SubProject.h" 18 | #import "XCSubProjectDefinition.h" 19 | 20 | 21 | @implementation XCProject (SubProject) 22 | 23 | 24 | #pragma mark sub-project related public methods 25 | 26 | // returns the key for the reference proxy with the given path (nil if not found) 27 | // does not use keysForProjectObjectsOfType:withIdentifier: because the identifier it uses for 28 | // PBXReferenceProxy is different. 29 | - (NSString*)referenceProxyKeyForName:(NSString*)name 30 | { 31 | __block NSString* result = nil; 32 | [[self objects] enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSDictionary* obj, BOOL* stop) 33 | { 34 | if ([[obj valueForKey:@"isa"] asMemberType] == PBXReferenceProxyType) 35 | { 36 | if ([[obj valueForKey:@"path"] isEqualTo:name]) 37 | { 38 | result = key; 39 | *stop = YES; 40 | } 41 | } 42 | }]; 43 | return result; 44 | } 45 | 46 | // returns an array of build products, excluding bundles with extensions other than ".bundle" (which is kind 47 | // of gross, but I didn't see a better way to exclude test bundles without giving them their own XcodeSourceFileType) 48 | - (NSArray*)buildProductsForTargets:(NSString*)xcodeprojKey 49 | { 50 | NSMutableArray* results = [[NSMutableArray alloc] init]; 51 | [[self objects] enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSDictionary* obj, BOOL* stop) 52 | { 53 | if ([[obj valueForKey:@"isa"] asMemberType] == PBXReferenceProxyType) 54 | { 55 | // make sure it belongs to the xcodeproj we're adding 56 | NSString* remoteRef = [obj valueForKey:@"remoteRef"]; 57 | NSDictionary* containerProxy = [[self objects] valueForKey:remoteRef]; 58 | NSString* containerPortal = [containerProxy valueForKey:@"containerPortal"]; 59 | if ([containerPortal isEqualToString:xcodeprojKey]) 60 | { 61 | XcodeSourceFileType type = XCSourceFileTypeFromStringRepresentation([obj valueForKey:@"fileType"]); 62 | NSString* path = (NSString*) [obj valueForKey:@"path"]; 63 | if (type != Bundle || [[path pathExtension] isEqualToString:@"bundle"]) 64 | { 65 | [results addObject:[XCSourceFile sourceFileWithProject:self key:key type:type name:path sourceTree:nil path:nil]]; 66 | } 67 | } 68 | } 69 | }]; 70 | return results; 71 | } 72 | 73 | // makes PBXContainerItemProxy and PBXTargetDependency objects for the xcodeproj, and adds the dependency key 74 | // to all the specified targets 75 | - (void)addAsTargetDependency:(XCSubProjectDefinition*)xcodeprojDefinition toTargets:(NSArray*)targets 76 | { 77 | for (XCTarget* target in targets) 78 | { 79 | // make a new PBXContainerItemProxy 80 | NSString* key = [[self fileWithName:[xcodeprojDefinition pathRelativeToProjectRoot]] key]; 81 | NSString* containerItemProxyKey = 82 | [self makeContainerItemProxyForName:[xcodeprojDefinition name] fileRef:key proxyType:@"1" uniqueName:[target name]]; 83 | // make a PBXTargetDependency 84 | NSString* targetDependencyKey = 85 | [self makeTargetDependency:[xcodeprojDefinition name] forContainerItemProxyKey:containerItemProxyKey uniqueName:[target name]]; 86 | // add entry in each targets dependencies list 87 | [target addDependency:targetDependencyKey]; 88 | } 89 | } 90 | 91 | // returns an array of keys for all project objects (not just files) that match the given criteria. Since this is 92 | // a convenience method intended to save typing elsewhere, each type has its own field to match to rather than each 93 | // matching on name or path as you might expect. 94 | - (NSArray*)keysForProjectObjectsOfType:(XcodeMemberType)memberType withIdentifier:(NSString*)identifier singleton:(BOOL)singleton 95 | required:(BOOL)required 96 | { 97 | __block NSMutableArray* returnValue = [[NSMutableArray alloc] init]; 98 | [[self objects] enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSDictionary* obj, BOOL* stop) 99 | { 100 | if ([[obj valueForKey:@"isa"] asMemberType] == memberType) 101 | { 102 | if (memberType == PBXContainerItemProxyType) 103 | { 104 | if ([[obj valueForKey:@"containerPortal"] isEqualToString:identifier]) 105 | { 106 | [returnValue addObject:key]; 107 | } 108 | } 109 | else if (memberType == PBXReferenceProxyType) 110 | { 111 | if ([[obj valueForKey:@"remoteRef"] isEqualToString:identifier]) 112 | { 113 | [returnValue addObject:key]; 114 | } 115 | } 116 | else if (memberType == PBXTargetDependencyType || memberType == PBXGroupType || memberType == PBXVariantGroupType) 117 | { 118 | if ([[obj valueForKey:@"name"] isEqualToString:identifier]) 119 | { 120 | [returnValue addObject:key]; 121 | } 122 | } 123 | else if (memberType == PBXNativeTargetType) 124 | { 125 | for (NSString* dependencyKey in [obj valueForKey:@"dependencies"]) 126 | { 127 | if ([dependencyKey isEqualToString:identifier]) 128 | { 129 | [returnValue addObject:key]; 130 | } 131 | } 132 | } 133 | else if (memberType == PBXBuildFileType) 134 | { 135 | if ([[obj valueForKey:@"fileRef"] isEqualToString:identifier]) 136 | { 137 | [returnValue addObject:key]; 138 | } 139 | } 140 | else if (memberType == PBXProjectType) 141 | { 142 | [returnValue addObject:key]; 143 | } 144 | else if (memberType == PBXFileReferenceType) 145 | { 146 | if ([[obj valueForKey:@"path"] isEqualToString:identifier]) 147 | { 148 | [returnValue addObject:key]; 149 | } 150 | } 151 | else if (memberType == PBXFrameworksBuildPhaseType || memberType == PBXResourcesBuildPhaseType) 152 | { 153 | [returnValue addObject:key]; 154 | } 155 | else 156 | { 157 | [NSException raise:NSInvalidArgumentException format:@"Unrecognized member type %@", 158 | [NSString stringFromMemberType:memberType]]; 159 | } 160 | } 161 | }]; 162 | if (singleton && [returnValue count] > 1) 163 | { 164 | [NSException raise:NSGenericException format:@"Searched for one instance of member type %@ with value %@, but found %ld", 165 | [NSString stringFromMemberType:memberType], identifier, [returnValue count]]; 166 | } 167 | if (required && [returnValue count] == 0) 168 | { 169 | [NSException raise:NSGenericException format:@"Searched for instances of member type %@ with value %@, but did not find any", 170 | [NSString stringFromMemberType:memberType], identifier]; 171 | } 172 | return returnValue; 173 | } 174 | 175 | // returns the dictionary for the PBXProject. Raises an exception if more or less than 1 are found. 176 | - (NSMutableDictionary*)PBXProjectDict 177 | { 178 | NSString* PBXProjectKey; 179 | NSArray* PBXProjectKeys = [self keysForProjectObjectsOfType:PBXProjectType withIdentifier:nil singleton:YES required:YES]; 180 | PBXProjectKey = [PBXProjectKeys objectAtIndex:0]; 181 | NSMutableDictionary* PBXProjectDict = [[self objects] valueForKey:PBXProjectKey]; 182 | return PBXProjectDict; 183 | } 184 | 185 | // returns the key of the PBXContainerItemProxy for the given name and proxy type. nil if not found. 186 | - (NSString*)containerItemProxyKeyForName:(NSString*)name proxyType:(NSString*)proxyType 187 | { 188 | NSMutableArray* results = [[NSMutableArray alloc] init]; 189 | [[self objects] enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSDictionary* obj, BOOL* stop) 190 | { 191 | if ([[obj valueForKey:@"isa"] asMemberType] == PBXContainerItemProxyType) 192 | { 193 | NSString* remoteInfo = [obj valueForKey:@"remoteInfo"]; 194 | NSString* proxy = [obj valueForKey:@"proxyType"]; 195 | if ([remoteInfo isEqualToString:name] && [proxy isEqualToString:proxyType]) 196 | { 197 | [results addObject:key]; 198 | } 199 | } 200 | }]; 201 | if ([results count] > 1) 202 | { 203 | [NSException raise:NSGenericException format:@"Searched for one instance of member type %@ with value %@, but found %ld", 204 | @"PBXContainerItemProxy", 205 | [NSString stringWithFormat:@"%@ and proxyType of %@", name, proxyType], 206 | [results count]]; 207 | } 208 | if ([results count] == 0) 209 | { 210 | return nil; 211 | } 212 | return [results objectAtIndex:0]; 213 | } 214 | 215 | /* ====================================================================================================================================== */ 216 | #pragma mark - Private Methods 217 | #pragma mark sub-project related private methods 218 | 219 | // makes a PBXContainerItemProxy object for a given PBXFileReference object. Replaces pre-existing objects. 220 | - (NSString*)makeContainerItemProxyForName:(NSString*)name fileRef:(NSString*)fileRef proxyType:(NSString*)proxyType 221 | uniqueName:(NSString*)uniqueName 222 | { 223 | NSString* keyName; 224 | if (uniqueName != nil) 225 | { 226 | keyName = [NSString stringWithFormat:@"%@-%@", name, uniqueName]; 227 | } 228 | else 229 | { 230 | keyName = name; 231 | } 232 | // remove old if it exists 233 | NSString* existingProxyKey = [self containerItemProxyKeyForName:keyName proxyType:proxyType]; 234 | if (existingProxyKey) 235 | { 236 | [[self objects] removeObjectForKey:existingProxyKey]; 237 | } 238 | // make new one 239 | NSMutableDictionary* proxy = [NSMutableDictionary dictionary]; 240 | [proxy setObject:[NSString stringFromMemberType:PBXContainerItemProxyType] forKey:@"isa"]; 241 | [proxy setObject:fileRef forKey:@"containerPortal"]; 242 | [proxy setObject:proxyType forKey:@"proxyType"]; 243 | // give it a random key - the keys xcode puts here are not in the project file anywhere else 244 | NSString* key = [[XCKeyBuilder forItemNamed:[NSString stringWithFormat:@"%@-junk", keyName]] build]; 245 | [proxy setObject:key forKey:@"remoteGlobalIDString"]; 246 | [proxy setObject:name forKey:@"remoteInfo"]; 247 | // add to project. use proxyType to generate key, so that multiple keys for the same name don't overwrite each other 248 | key = [[XCKeyBuilder forItemNamed:[NSString stringWithFormat:@"%@-containerProxy-%@", keyName, proxyType]] build]; 249 | [[self objects] setObject:proxy forKey:key]; 250 | 251 | return key; 252 | } 253 | 254 | // makes a PBXReferenceProxy object for a given PBXContainerProxy object. Replaces pre-existing objects. 255 | - (void)makeReferenceProxyForContainerItemProxy:(NSString*)containerItemProxyKey buildProductReference:(NSDictionary*)buildProductReference 256 | { 257 | NSString* path = [buildProductReference valueForKey:@"path"]; 258 | // remove old if any exists 259 | NSArray* existingProxyKeys = [self keysForProjectObjectsOfType:PBXReferenceProxyType withIdentifier:path singleton:NO required:NO]; 260 | if ([existingProxyKeys count] > 0) 261 | { 262 | for (NSString* existingProxyKey in existingProxyKeys) 263 | { 264 | [[self objects] removeObjectForKey:existingProxyKey]; 265 | } 266 | } 267 | // make new one 268 | NSMutableDictionary* proxy = [NSMutableDictionary dictionary]; 269 | [proxy setObject:[NSString stringFromMemberType:PBXReferenceProxyType] forKey:@"isa"]; 270 | [proxy setObject:[buildProductReference valueForKey:@"explicitFileType"] forKey:@"fileType"]; 271 | [proxy setObject:path forKey:@"path"]; 272 | [proxy setObject:containerItemProxyKey forKey:@"remoteRef"]; 273 | [proxy setObject:[buildProductReference valueForKey:@"sourceTree"] forKey:@"sourceTree"]; 274 | // add to project 275 | NSString* key = [[XCKeyBuilder forItemNamed:[NSString stringWithFormat:@"%@-referenceProxy", path]] build]; 276 | [[self objects] setObject:proxy forKey:key]; 277 | } 278 | 279 | // makes a PBXTargetDependency object for a given PBXContainerItemProxy. Replaces pre-existing objects. 280 | - (NSString*)makeTargetDependency:(NSString*)name forContainerItemProxyKey:(NSString*)containerItemProxyKey uniqueName:(NSString*)uniqueName 281 | { 282 | NSString* keyName; 283 | if (uniqueName != nil) 284 | { 285 | keyName = [NSString stringWithFormat:@"%@-%@", name, uniqueName]; 286 | } 287 | else 288 | { 289 | keyName = name; 290 | } 291 | // remove old if it exists 292 | NSArray* existingDependencyKeys = 293 | [self keysForProjectObjectsOfType:PBXTargetDependencyType withIdentifier:keyName singleton:NO required:NO]; 294 | if ([existingDependencyKeys count] > 0) 295 | { 296 | for (NSString* existingDependencyKey in existingDependencyKeys) 297 | { 298 | [[self objects] removeObjectForKey:existingDependencyKey]; 299 | } 300 | } 301 | // make new one 302 | NSMutableDictionary* targetDependency = [NSMutableDictionary dictionary]; 303 | [targetDependency setObject:[NSString stringFromMemberType:PBXTargetDependencyType] forKey:@"isa"]; 304 | [targetDependency setObject:name forKey:@"name"]; 305 | [targetDependency setObject:containerItemProxyKey forKey:@"targetProxy"]; 306 | NSString* targetDependencyKey = [[XCKeyBuilder forItemNamed:[NSString stringWithFormat:@"%@-targetProxy", keyName]] build]; 307 | [[self objects] setObject:targetDependency forKey:targetDependencyKey]; 308 | return targetDependencyKey; 309 | } 310 | 311 | // make a PBXContainerItemProxy and PBXReferenceProxy for each target in the subProject 312 | - (void)addProxies:(XCSubProjectDefinition*)xcodeproj 313 | { 314 | NSString* fileRef = [[self fileWithName:[xcodeproj pathRelativeToProjectRoot]] key]; 315 | for (NSDictionary* target in [xcodeproj.subProject targets]) 316 | { 317 | NSString* containerItemProxyKey = 318 | [self makeContainerItemProxyForName:[target valueForKey:@"name"] fileRef:fileRef proxyType:@"2" uniqueName:nil]; 319 | NSString* productFileReferenceKey = [target valueForKey:@"productReference"]; 320 | NSDictionary* productFileReference = [[xcodeproj.subProject objects] valueForKey:productFileReferenceKey]; 321 | [self makeReferenceProxyForContainerItemProxy:containerItemProxyKey buildProductReference:productFileReference]; 322 | } 323 | } 324 | 325 | // remove the PBXContainerItemProxy and PBXReferenceProxy objects for the given object key (which is the PBXFilereference 326 | // for the xcodeproj file) 327 | - (void)removeProxies:(NSString*)xcodeprojKey 328 | { 329 | NSMutableArray* keysToDelete = [NSMutableArray array]; 330 | // use the xcodeproj's PBXFileReference key to get the PBXContainerItemProxy keys 331 | NSArray* containerItemProxyKeys = 332 | [self keysForProjectObjectsOfType:PBXContainerItemProxyType withIdentifier:xcodeprojKey singleton:NO required:YES]; 333 | // use the PBXContainerItemProxy keys to get the PBXReferenceProxy keys 334 | for (NSString* key in containerItemProxyKeys) 335 | { 336 | [keysToDelete addObjectsFromArray:[self keysForProjectObjectsOfType:PBXReferenceProxyType withIdentifier:key singleton:NO 337 | required:NO]]; 338 | [keysToDelete addObject:key]; 339 | } 340 | // remove all objects located above 341 | [keysToDelete enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL* stop) 342 | { 343 | [[self objects] removeObjectForKey:obj]; 344 | }]; 345 | } 346 | 347 | // returns the Products group key for the given PBXFileReference key, nil if not found. 348 | - (NSString*)productsGroupKeyForKey:(NSString*)key 349 | { 350 | NSMutableArray* projectReferences = [[self PBXProjectDict] valueForKey:@"projectReferences"]; 351 | NSString* productsGroupKey = nil; 352 | for (NSDictionary* projectRef in projectReferences) 353 | { 354 | if ([[projectRef valueForKey:@"ProjectRef"] isEqualToString:key]) 355 | { 356 | // it's an error if we find more than one 357 | if (productsGroupKey != nil) 358 | { 359 | [NSException raise:NSGenericException format:@"Found more than one project reference for key %@", key]; 360 | } 361 | productsGroupKey = [projectRef valueForKey:@"ProductGroup"]; 362 | } 363 | } 364 | return productsGroupKey; 365 | } 366 | 367 | // removes a file reference from the projectReferences array in PBXProject (removing the array itself if this action 368 | // leaves it empty). 369 | - (void)removeFromProjectReferences:(NSString*)key forProductsGroup:(NSString*)productsGroupKey 370 | { 371 | NSMutableArray* projectReferences = [[self PBXProjectDict] valueForKey:@"projectReferences"]; 372 | // remove entry from PBXProject's projectReferences 373 | NSMutableArray* referencesToRemove = [NSMutableArray array]; 374 | for (NSDictionary* projectRef in projectReferences) 375 | { 376 | if ([[projectRef valueForKey:@"ProjectRef"] isEqualToString:key]) 377 | { 378 | [referencesToRemove addObject:projectRef]; 379 | } 380 | } 381 | for (NSDictionary* projectRef in referencesToRemove) 382 | { 383 | [projectReferences removeObject:projectRef]; 384 | } 385 | // if that was the last project reference, remove the array from the project 386 | if ([projectReferences count] == 0) 387 | { 388 | [[self PBXProjectDict] removeObjectForKey:@"projectReferences"]; 389 | } 390 | } 391 | 392 | // removes a specific xcodeproj file from any targets (by name). It's not an error if no entries are found, 393 | // because we support adding a project file without adding it to any targets. 394 | - (void)removeTargetDependencies:(NSString*)name 395 | { 396 | // get the key for the PBXTargetDependency with name = xcodeproj file name (without extension) 397 | NSArray* targetDependencyKeys = [self keysForProjectObjectsOfType:PBXTargetDependencyType withIdentifier:name singleton:NO required:NO]; 398 | // we might not find any if the project wasn't added to targets in the first place 399 | if ([targetDependencyKeys count] == 0) 400 | { 401 | return; 402 | } 403 | NSString* targetDependencyKey = [targetDependencyKeys objectAtIndex:0]; 404 | // use the key for the PBXTargetDependency to get the key for any PBXNativeTargets that depend on it 405 | NSArray* nativeTargetKeys = 406 | [self keysForProjectObjectsOfType:PBXNativeTargetType withIdentifier:targetDependencyKey singleton:NO required:NO]; 407 | // remove the key for the PBXTargetDependency from the PBXNativeTarget's dependencies arrays (leave in place even if empty) 408 | for (NSString* nativeTargetKey in nativeTargetKeys) 409 | { 410 | NSMutableDictionary* nativeTarget = [[self objects] objectForKey:nativeTargetKey]; 411 | NSMutableArray* dependencies = [nativeTarget valueForKey:@"dependencies"]; 412 | [dependencies removeObject:targetDependencyKey]; 413 | [nativeTarget setObject:dependencies forKey:@"dependencies"]; 414 | } 415 | // remove the PBXTargetDependency 416 | [[self objects] removeObjectForKey:targetDependencyKey]; 417 | } 418 | 419 | @end -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XCProject.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 - 2013 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | #import 13 | #import "XcodeMemberType.h" 14 | #import "XcodeSourceFileType.h" 15 | 16 | @class XCClassDefinition; 17 | @class XCGroup; 18 | @class XCFileOperationQueue; 19 | @class XCSourceFile; 20 | @class XCTarget; 21 | @class XCSubProjectDefinition; 22 | @class XCBuildConfig; 23 | 24 | NSString* const XCProjectNotFoundException; 25 | 26 | @interface XCProject : NSObject 27 | { 28 | @protected 29 | XCFileOperationQueue* _fileOperationQueue; 30 | 31 | NSString* _filePath; 32 | NSMutableDictionary* _dataStore; 33 | NSMutableArray* _targets; 34 | 35 | NSMutableDictionary* _groups; 36 | NSMutableDictionary* _configurations; 37 | 38 | NSString* _defaultConfigurationName; 39 | NSString* _rootObjectKey; 40 | } 41 | 42 | @property(nonatomic, strong, readonly) XCFileOperationQueue* fileOperationQueue; 43 | 44 | /* ============================================================ Initializers ============================================================ */ 45 | 46 | + (XCProject*)projectWithFilePath:(NSString*)filePath; 47 | 48 | /** 49 | * Creates a new project editor instance with the specified Project.xcodeproj file. 50 | */ 51 | - (id)initWithFilePath:(NSString*)filePath; 52 | 53 | /* ====================================================================================================================================== */ 54 | 55 | #pragma mark Files 56 | /** 57 | * Returns all file resources in the project, as an array of `XCSourceFile` objects. 58 | */ 59 | - (NSArray*)files; 60 | 61 | /** 62 | * Returns the project file with the specified key, or nil. 63 | */ 64 | - (XCSourceFile*)fileWithKey:(NSString*)key; 65 | 66 | /** 67 | * Returns the project file with the specified name, or nil. If more than one project file matches the specified name, 68 | * which one is returned is undefined. 69 | */ 70 | - (XCSourceFile*)fileWithName:(NSString*)name; 71 | 72 | /** 73 | * Returns all header files in the project, as an array of `XCSourceFile` objects. 74 | */ 75 | - (NSArray*)headerFiles; 76 | 77 | /** 78 | * Returns all implementation obj-c implementation files in the project, as an array of `XCSourceFile` objects. 79 | */ 80 | - (NSArray*)objectiveCFiles; 81 | 82 | /** 83 | * Returns all implementation obj-c++ implementation files in the project, as an array of `XCSourceFile` objects. 84 | */ 85 | - (NSArray*)objectiveCPlusPlusFiles; 86 | 87 | /** 88 | * Returns all the xib files in the project, as an array of `XCSourceFile` objects. 89 | */ 90 | - (NSArray*)xibFiles; 91 | 92 | - (NSArray*)imagePNGFiles; 93 | 94 | - (NSString*)filePath; 95 | 96 | 97 | /* ====================================================================================================================================== */ 98 | #pragma mark Groups 99 | /** 100 | * Lists the groups in an xcode project, returning an array of `XCGroup` objects. 101 | */ 102 | - (NSArray*)groups; 103 | 104 | /** 105 | * Returns the root (top-level) group. 106 | */ 107 | - (XCGroup*)rootGroup; 108 | 109 | /** 110 | * Returns the root (top-level) groups, if there are multiple. An array of rootGroup if there is only one. 111 | */ 112 | - (NSArray*)rootGroups; 113 | 114 | /** 115 | * Returns the group with the given key, or nil. 116 | */ 117 | - (XCGroup*)groupWithKey:(NSString*)key; 118 | 119 | /** 120 | * Returns the group with the specified display name path - the directory relative to the root group. Eg Source/Main 121 | */ 122 | - (XCGroup*)groupWithPathFromRoot:(NSString*)path; 123 | 124 | /** 125 | * Returns the parent group for the group or file with the given key; 126 | */ 127 | - (XCGroup*)groupForGroupMemberWithKey:(NSString*)key; 128 | 129 | /** 130 | * Returns the parent group for the group or file with the source file 131 | */ 132 | - (XCGroup*)groupWithSourceFile:(XCSourceFile*)sourceFile; 133 | 134 | /* ====================================================================================================================================== */ 135 | #pragma mark Targets 136 | /** 137 | * Lists the targets in an xcode project, returning an array of `XCTarget` objects. 138 | */ 139 | - (NSArray*)targets; 140 | 141 | /** 142 | * Returns the target with the specified name, or nil. 143 | */ 144 | - (XCTarget*)targetWithName:(NSString*)name; 145 | 146 | #pragma mark Configurations 147 | 148 | /** 149 | * Returns the target with the specified name, or nil. 150 | */ 151 | - (NSDictionary*)configurations; 152 | 153 | - (NSDictionary*)configurationWithName:(NSString*)name; 154 | 155 | - (XCBuildConfig*)defaultConfiguration; 156 | 157 | /* ====================================================================================================================================== */ 158 | #pragma mark Saving 159 | /** 160 | * Saves a project after editing. 161 | */ 162 | - (void)save; 163 | 164 | 165 | /* ====================================================================================================================================== */ 166 | /** 167 | * Raw project data. 168 | */ 169 | - (NSMutableDictionary*)objects; 170 | 171 | - (NSMutableDictionary*)dataStore; 172 | 173 | - (void)dropCache; 174 | 175 | @end 176 | -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XCProject.m: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 - 2013 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | #import "XCProject.h" 14 | #import "XCGroup.h" 15 | #import "XCSourceFile.h" 16 | #import "XCTarget.h" 17 | #import "XCFileOperationQueue.h" 18 | #import "XCBuildConfig.h" 19 | 20 | 21 | @implementation XCProject 22 | 23 | 24 | @synthesize fileOperationQueue = _fileOperationQueue; 25 | 26 | /* ====================================================================================================================================== */ 27 | #pragma mark - Class Methods 28 | 29 | + (XCProject*)projectWithFilePath:(NSString*)filePath 30 | { 31 | return [[XCProject alloc] initWithFilePath:filePath]; 32 | } 33 | 34 | 35 | /* ====================================================================================================================================== */ 36 | #pragma mark - Initialization & Destruction 37 | 38 | - (id)initWithFilePath:(NSString*)filePath 39 | { 40 | if ((self = [super init])) 41 | { 42 | _filePath = [filePath copy]; 43 | _dataStore = [[NSMutableDictionary alloc] initWithContentsOfFile:[_filePath stringByAppendingPathComponent:@"project.pbxproj"]]; 44 | 45 | if (!_dataStore) 46 | { 47 | [NSException raise:XCProjectNotFoundException format:@"Project file not found at file path %@", _filePath]; 48 | } 49 | 50 | _fileOperationQueue = [[XCFileOperationQueue alloc] initWithBaseDirectory:[_filePath stringByDeletingLastPathComponent]]; 51 | 52 | } 53 | return self; 54 | } 55 | 56 | /* ====================================================================================================================================== */ 57 | #pragma mark - Interface Methods 58 | 59 | #pragma mark Files 60 | 61 | - (NSArray*)files 62 | { 63 | NSMutableArray* results = [NSMutableArray array]; 64 | [[self objects] enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSDictionary* obj, BOOL* stop) 65 | { 66 | if ([[obj valueForKey:@"isa"] asMemberType] == PBXFileReferenceType) 67 | { 68 | XcodeSourceFileType fileType = XCSourceFileTypeFromStringRepresentation([obj valueForKey:@"lastKnownFileType"]); 69 | NSString* path = [obj valueForKey:@"path"]; 70 | NSString* sourceTree = [obj valueForKey:@"sourceTree"]; 71 | [results addObject:[XCSourceFile sourceFileWithProject:self key:key type:fileType name:path 72 | sourceTree:(sourceTree ? sourceTree : @"") path:nil]]; 73 | } 74 | }]; 75 | return results; 76 | } 77 | 78 | - (XCSourceFile*)fileWithKey:(NSString*)key 79 | { 80 | NSDictionary* obj = [[self objects] valueForKey:key]; 81 | if (obj && ([[obj valueForKey:@"isa"] asMemberType] == PBXFileReferenceType || [[obj valueForKey:@"isa"] asMemberType] == 82 | PBXReferenceProxyType)) 83 | { 84 | XcodeSourceFileType fileType = XCSourceFileTypeFromStringRepresentation([obj valueForKey:@"lastKnownFileType"]); 85 | 86 | NSString* name = [obj valueForKey:@"name"]; 87 | NSString* sourceTree = [obj valueForKey:@"sourceTree"]; 88 | 89 | if (name == nil) 90 | { 91 | name = [obj valueForKey:@"path"]; 92 | } 93 | return [XCSourceFile sourceFileWithProject:self key:key type:fileType name:name sourceTree:(sourceTree ? sourceTree : @"") 94 | path:[obj valueForKey:@"path"]]; 95 | } 96 | return nil; 97 | } 98 | 99 | - (XCSourceFile*)fileWithName:(NSString*)name 100 | { 101 | for (XCSourceFile* projectFile in [self files]) 102 | { 103 | if ([[projectFile name] isEqualToString:name]) 104 | { 105 | return projectFile; 106 | } 107 | } 108 | return nil; 109 | } 110 | 111 | 112 | - (NSArray*)headerFiles 113 | { 114 | return [self projectFilesOfType:SourceCodeHeader]; 115 | } 116 | 117 | - (NSArray*)objectiveCFiles 118 | { 119 | return [self projectFilesOfType:SourceCodeObjC]; 120 | } 121 | 122 | - (NSArray*)objectiveCPlusPlusFiles 123 | { 124 | return [self projectFilesOfType:SourceCodeObjCPlusPlus]; 125 | } 126 | 127 | 128 | - (NSArray*)xibFiles 129 | { 130 | return [self projectFilesOfType:XibFile]; 131 | } 132 | 133 | - (NSArray*)imagePNGFiles 134 | { 135 | return [self projectFilesOfType:ImageResourcePNG]; 136 | } 137 | 138 | // need this value to construct relative path in XcodeprojDefinition 139 | - (NSString*)filePath 140 | { 141 | return _filePath; 142 | } 143 | 144 | /* ====================================================================================================================================== */ 145 | #pragma mark Groups 146 | 147 | - (NSArray*)groups 148 | { 149 | 150 | NSMutableArray* results = [[NSMutableArray alloc] init]; 151 | [[_dataStore objectForKey:@"objects"] enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSDictionary* obj, BOOL* stop) 152 | { 153 | 154 | if ([[obj valueForKey:@"isa"] asMemberType] == PBXGroupType || [[obj valueForKeyPath:@"isa"] asMemberType] == PBXVariantGroupType) 155 | { 156 | [results addObject:[self groupWithKey:key]]; 157 | } 158 | }]; 159 | return results; 160 | } 161 | 162 | //TODO: Optimize this implementation. 163 | - (XCGroup*)rootGroup 164 | { 165 | for (XCGroup* group in [self groups]) 166 | { 167 | if ([group isRootGroup]) 168 | { 169 | return group; 170 | } 171 | } 172 | return nil; 173 | } 174 | 175 | - (NSArray*)rootGroups 176 | { 177 | XCGroup* group = [self rootGroup]; 178 | if (group) 179 | { 180 | return [NSArray arrayWithObject:group]; 181 | } 182 | 183 | NSMutableArray* results = [NSMutableArray array]; 184 | for (XCGroup* group in [self groups]) 185 | { 186 | if ([group parentGroup] == nil) 187 | { 188 | [results addObject:group]; 189 | } 190 | } 191 | 192 | return [results copy]; 193 | } 194 | 195 | - (XCGroup*)groupWithKey:(NSString*)key 196 | { 197 | XCGroup* group = [_groups objectForKey:key]; 198 | if (group) 199 | { 200 | return group; 201 | } 202 | 203 | NSDictionary* obj = [[self objects] objectForKey:key]; 204 | if (obj && ([[obj valueForKey:@"isa"] asMemberType] == PBXGroupType || [[obj valueForKey:@"isa"] asMemberType] == PBXVariantGroupType)) 205 | { 206 | 207 | NSString* name = [obj valueForKey:@"name"]; 208 | NSString* path = [obj valueForKey:@"path"]; 209 | NSArray* children = [obj valueForKey:@"children"]; 210 | XCGroup* group = [XCGroup groupWithProject:self key:key alias:name path:path children:children]; 211 | 212 | [_groups setObject:group forKey:key]; 213 | 214 | return group; 215 | } 216 | return nil; 217 | } 218 | 219 | - (XCGroup*)groupForGroupMemberWithKey:(NSString*)key 220 | { 221 | for (XCGroup* group in [self groups]) 222 | { 223 | if ([group memberWithKey:key]) 224 | { 225 | return group; 226 | } 227 | } 228 | return nil; 229 | } 230 | 231 | - (XCGroup*)groupWithSourceFile:(XCSourceFile*)sourceFile 232 | { 233 | for (XCGroup* group in [self groups]) 234 | { 235 | for (id member in [group members]) 236 | { 237 | if ([member isKindOfClass:[XCSourceFile class]] && [[sourceFile key] isEqualToString:[member key]]) 238 | { 239 | return group; 240 | } 241 | } 242 | } 243 | return nil; 244 | } 245 | 246 | //TODO: This could fail if the path attribute on a given group is more than one directory. Start with candidates and 247 | //TODO: search backwards. 248 | - (XCGroup*)groupWithPathFromRoot:(NSString*)path 249 | { 250 | NSArray* pathItems = [path componentsSeparatedByString:@"/"]; 251 | XCGroup* currentGroup = [self rootGroup]; 252 | for (NSString* pathItem in pathItems) 253 | { 254 | id group = [currentGroup memberWithDisplayName:pathItem]; 255 | if ([group isKindOfClass:[XCGroup class]]) 256 | { 257 | currentGroup = group; 258 | } 259 | else 260 | { 261 | return nil; 262 | } 263 | } 264 | return currentGroup; 265 | } 266 | 267 | 268 | /* ====================================================================================================================================== */ 269 | #pragma mark targets 270 | 271 | - (NSArray*)targets 272 | { 273 | if (_targets == nil) 274 | { 275 | _targets = [[NSMutableArray alloc] init]; 276 | [[self objects] enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSDictionary* obj, BOOL* stop) 277 | { 278 | if ([[obj valueForKey:@"isa"] asMemberType] == PBXNativeTargetType) 279 | { 280 | XCTarget* target = 281 | [XCTarget targetWithProject:self key:key name:[obj valueForKey:@"name"] productName:[obj valueForKey:@"productName"] 282 | productReference:[obj valueForKey:@"productReference"]]; 283 | [_targets addObject:target]; 284 | } 285 | }]; 286 | } 287 | return _targets; 288 | } 289 | 290 | - (XCTarget*)targetWithName:(NSString*)name 291 | { 292 | for (XCTarget* target in [self targets]) 293 | { 294 | if ([[target name] isEqualToString:name]) 295 | { 296 | return target; 297 | } 298 | } 299 | return nil; 300 | } 301 | 302 | - (void)save 303 | { 304 | [_fileOperationQueue commitFileOperations]; 305 | [_dataStore writeToFile:[_filePath stringByAppendingPathComponent:@"project.pbxproj"] atomically:YES]; 306 | 307 | NSLog(@"Saved project"); 308 | } 309 | 310 | - (NSMutableDictionary*)objects 311 | { 312 | return [_dataStore objectForKey:@"objects"]; 313 | } 314 | 315 | - (NSMutableDictionary*)dataStore 316 | { 317 | return _dataStore; 318 | } 319 | 320 | - (void)dropCache 321 | { 322 | _targets = nil; 323 | _configurations = nil; 324 | _rootObjectKey = nil; 325 | } 326 | 327 | 328 | - (NSDictionary*)configurations 329 | { 330 | if (_configurations == nil) 331 | { 332 | NSString* buildConfigurationRootSectionKey = 333 | [[[self objects] objectForKey:[self rootObjectKey]] objectForKey:@"buildConfigurationList"]; 334 | NSDictionary* buildConfigurationDictionary = [[self objects] objectForKey:buildConfigurationRootSectionKey]; 335 | _configurations = 336 | [[XCBuildConfig buildConfigurationsFromArray:[buildConfigurationDictionary objectForKey:@"buildConfigurations"] 337 | inProject:self] mutableCopy]; 338 | _defaultConfigurationName = [[buildConfigurationDictionary objectForKey:@"defaultConfigurationName"] copy]; 339 | } 340 | 341 | return [_configurations copy]; 342 | } 343 | 344 | - (NSDictionary*)configurationWithName:(NSString*)name 345 | { 346 | return [[self configurations] objectForKey:name]; 347 | } 348 | 349 | - (XCBuildConfig*)defaultConfiguration 350 | { 351 | return [[self configurations] objectForKey:_defaultConfigurationName]; 352 | } 353 | 354 | /* ====================================================================================================================================== */ 355 | #pragma mark Private 356 | 357 | - (NSString*)rootObjectKey 358 | { 359 | if (_rootObjectKey == nil) 360 | { 361 | _rootObjectKey = [[_dataStore objectForKey:@"rootObject"] copy];; 362 | } 363 | 364 | return _rootObjectKey; 365 | } 366 | 367 | - (NSArray*)projectFilesOfType:(XcodeSourceFileType)projectFileType 368 | { 369 | NSMutableArray* results = [NSMutableArray array]; 370 | for (XCSourceFile* file in [self files]) 371 | { 372 | if ([file type] == projectFileType) 373 | { 374 | [results addObject:file]; 375 | } 376 | } 377 | return results; 378 | } 379 | 380 | @end -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XCSourceFile.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | 14 | #import 15 | #import "XcodeGroupMember.h" 16 | #import "XcodeSourceFileType.h" 17 | 18 | @class XCProject; 19 | 20 | /** 21 | * Represents a file resource in an xcode project. 22 | */ 23 | @interface XCSourceFile : NSObject 24 | { 25 | 26 | @private 27 | XCProject* _project; 28 | 29 | NSNumber* _isBuildFile; 30 | NSString* _buildFileKey; 31 | NSString* _name; 32 | NSString* _sourceTree; 33 | NSString* _key; 34 | NSString* _path; 35 | XcodeSourceFileType _type; 36 | } 37 | 38 | @property(nonatomic, readonly) XcodeSourceFileType type; 39 | @property(nonatomic, strong, readonly) NSString* key; 40 | @property(nonatomic, strong) NSString* name; 41 | @property(nonatomic, strong, readonly) NSString* sourceTree; 42 | @property(nonatomic, strong) NSString* path; 43 | 44 | + (XCSourceFile*)sourceFileWithProject:(XCProject*)project key:(NSString*)key type:(XcodeSourceFileType)type name:(NSString*)name 45 | sourceTree:(NSString*)tree path:(NSString*)path; 46 | 47 | - (id)initWithProject:(XCProject*)project key:(NSString*)key type:(XcodeSourceFileType)type name:(NSString*)name sourceTree:(NSString*)tree 48 | path:(NSString*)path; 49 | 50 | /** 51 | * If yes, indicates the file is able to be included for compilation in an `XCTarget`. 52 | */ 53 | - (BOOL)isBuildFile; 54 | 55 | - (BOOL)canBecomeBuildFile; 56 | 57 | - (XcodeMemberType)buildPhase; 58 | 59 | - (NSString*)buildFileKey; 60 | 61 | /** 62 | * Adds this file to the project as an `xcode_BuildFile`, ready to be included in targets. 63 | */ 64 | - (void)becomeBuildFile; 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XCSourceFile.m: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | 14 | #import "XCSourceFile.h" 15 | #import "XCProject.h" 16 | #import "Utils/XCKeyBuilder.h" 17 | #import "XCGroup.h" 18 | 19 | @implementation XCSourceFile 20 | 21 | @synthesize type = _type; 22 | @synthesize key = _key; 23 | @synthesize sourceTree = _sourceTree; 24 | 25 | /* ====================================================================================================================================== */ 26 | #pragma mark - Class Methods 27 | 28 | + (XCSourceFile*)sourceFileWithProject:(XCProject*)project key:(NSString*)key type:(XcodeSourceFileType)type name:(NSString*)name 29 | sourceTree:(NSString*)_tree path:(NSString*)path 30 | { 31 | return [[XCSourceFile alloc] initWithProject:project key:key type:type name:name sourceTree:_tree path:path]; 32 | } 33 | 34 | 35 | /* ====================================================================================================================================== */ 36 | #pragma mark - Initialization & Destruction 37 | 38 | - (id)initWithProject:(XCProject*)project key:(NSString*)key type:(XcodeSourceFileType)type name:(NSString*)name sourceTree:(NSString*)tree 39 | path:(NSString*)path 40 | { 41 | 42 | self = [super init]; 43 | if (self) 44 | { 45 | _project = project; 46 | _key = [key copy]; 47 | _type = type; 48 | _name = [name copy]; 49 | _sourceTree = [tree copy]; 50 | _path = [path copy]; 51 | } 52 | return self; 53 | } 54 | 55 | 56 | /* ====================================================================================================================================== */ 57 | #pragma mark - Interface Methods 58 | 59 | // Goes to the entry for this object in the project and sets a value for one of the keys, such as name, path, etc. 60 | - (void)setValue:(id)val forProjectItemPropertyWithKey:(NSString*)key 61 | { 62 | NSMutableDictionary* obj = [[[_project objects] objectForKey:_key] mutableCopy]; 63 | if (nil == obj) 64 | { 65 | [NSException raise:@"Project item not found" format:@"Project item with key %@ not found.", _key]; 66 | } 67 | [obj setValue:val forKey:key]; 68 | [[_project objects] setValue:obj forKey:_key]; 69 | } 70 | 71 | 72 | - (NSString*)name 73 | { 74 | return _name; 75 | } 76 | 77 | - (void)setName:(NSString*)name 78 | { 79 | id old = _name; 80 | _name = [name copy]; 81 | 82 | [self setValue:name forProjectItemPropertyWithKey:@"name"]; 83 | } 84 | 85 | 86 | - (NSString*)path 87 | { 88 | return _path; 89 | } 90 | 91 | - (void)setPath:(NSString*)path 92 | { 93 | id old = _path; 94 | _path = [path copy]; 95 | 96 | [self setValue:path forProjectItemPropertyWithKey:@"path"]; 97 | } 98 | 99 | - (BOOL)isBuildFile 100 | { 101 | if ([self canBecomeBuildFile] && _isBuildFile == nil) 102 | { 103 | id old = _isBuildFile; 104 | _isBuildFile = [[NSNumber numberWithBool:NO] copy]; 105 | [[_project objects] enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSDictionary* obj, BOOL* stop) 106 | { 107 | if ([[obj valueForKey:@"isa"] asMemberType] == PBXBuildFileType) 108 | { 109 | if ([[obj valueForKey:@"fileRef"] isEqualToString:_key]) 110 | { 111 | _isBuildFile = nil; 112 | 113 | _isBuildFile = [[NSNumber numberWithBool:YES] copy]; 114 | } 115 | } 116 | }]; 117 | } 118 | return [_isBuildFile boolValue]; 119 | } 120 | 121 | - (BOOL)canBecomeBuildFile 122 | { 123 | return _type == SourceCodeObjC || _type == SourceCodeObjCPlusPlus || _type == SourceCodeCPlusPlus || _type == XibFile || _type == 124 | Framework || _type == ImageResourcePNG || _type == HTML || _type == Bundle || _type == Archive; 125 | } 126 | 127 | 128 | - (XcodeMemberType)buildPhase 129 | { 130 | if (_type == SourceCodeObjC || _type == SourceCodeObjCPlusPlus || _type == SourceCodeCPlusPlus || _type == XibFile) 131 | { 132 | return PBXSourcesBuildPhaseType; 133 | } 134 | else if (_type == Framework) 135 | { 136 | return PBXFrameworksBuildPhaseType; 137 | } 138 | else if (_type == ImageResourcePNG || _type == HTML || _type == Bundle) 139 | { 140 | return PBXResourcesBuildPhaseType; 141 | } 142 | else if (_type == Archive) 143 | { 144 | return PBXFrameworksBuildPhaseType; 145 | } 146 | return PBXNilType; 147 | } 148 | 149 | - (NSString*)buildFileKey 150 | { 151 | if (_buildFileKey == nil) 152 | { 153 | [[_project objects] enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSDictionary* obj, BOOL* stop) 154 | { 155 | if ([[obj valueForKey:@"isa"] asMemberType] == PBXBuildFileType) 156 | { 157 | if ([[obj valueForKey:@"fileRef"] isEqualToString:_key]) 158 | { 159 | _buildFileKey = [key copy]; 160 | } 161 | } 162 | }]; 163 | } 164 | return [_buildFileKey copy]; 165 | 166 | } 167 | 168 | 169 | - (void)becomeBuildFile 170 | { 171 | if (![self isBuildFile]) 172 | { 173 | if ([self canBecomeBuildFile]) 174 | { 175 | NSMutableDictionary* sourceBuildFile = [NSMutableDictionary dictionary]; 176 | [sourceBuildFile setObject:[NSString stringFromMemberType:PBXBuildFileType] forKey:@"isa"]; 177 | [sourceBuildFile setObject:_key forKey:@"fileRef"]; 178 | NSString* buildFileKey = [[XCKeyBuilder forItemNamed:[_name stringByAppendingString:@".buildFile"]] build]; 179 | [[_project objects] setObject:sourceBuildFile forKey:buildFileKey]; 180 | } 181 | else if (_type == Framework) 182 | { 183 | [NSException raise:NSInvalidArgumentException format:@"Add framework to target not implemented yet."]; 184 | } 185 | else 186 | { 187 | [NSException raise:NSInvalidArgumentException format:@"Project file of type %@ can't become a build file.", 188 | NSStringFromXCSourceFileType(_type)]; 189 | } 190 | 191 | } 192 | } 193 | 194 | /* ====================================================================================================================================== */ 195 | #pragma mark - Protocol Methods 196 | 197 | - (XcodeMemberType)groupMemberType 198 | { 199 | return PBXFileReferenceType; 200 | } 201 | 202 | - (NSString*)displayName 203 | { 204 | return _name; 205 | } 206 | 207 | - (NSString*)pathRelativeToProjectRoot 208 | { 209 | NSString* parentPath = [[_project groupForGroupMemberWithKey:_key] pathRelativeToProjectRoot]; 210 | NSString* result = [parentPath stringByAppendingPathComponent:_name]; 211 | return result; 212 | } 213 | 214 | /* ====================================================================================================================================== */ 215 | #pragma mark - Utility Methods 216 | 217 | - (NSString*)description 218 | { 219 | return [NSString stringWithFormat:@"Project file: key=%@, name=%@, fullPath=%@", _key, _name, [self pathRelativeToProjectRoot]]; 220 | } 221 | 222 | 223 | @end 224 | 225 | -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XCSourceFileDefinition.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | 14 | #import 15 | #import "XCAbstractDefinition.h" 16 | #import "XcodeSourceFileType.h" 17 | 18 | @interface XCSourceFileDefinition : XCAbstractDefinition 19 | { 20 | 21 | NSString* _sourceFileName; 22 | XcodeSourceFileType _type; 23 | NSData* _data; 24 | 25 | } 26 | 27 | @property(nonatomic, strong, readonly) NSString* sourceFileName; 28 | @property(nonatomic, strong, readonly) NSData* data; 29 | @property(nonatomic, readonly) XcodeSourceFileType type; 30 | 31 | + (XCSourceFileDefinition*)sourceDefinitionWithName:(NSString*)name text:(NSString*)text type:(XcodeSourceFileType)type; 32 | 33 | + (XCSourceFileDefinition*)sourceDefinitionWithName:(NSString*)name data:(NSData*)data type:(XcodeSourceFileType)type; 34 | 35 | - (id)initWithName:(NSString*)name text:(NSString*)text type:(XcodeSourceFileType)type; 36 | 37 | - (id)initWithName:(NSString*)name data:(NSData*)data type:(XcodeSourceFileType)type; 38 | 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XCSourceFileDefinition.m: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | 14 | #import "XCSourceFileDefinition.h" 15 | 16 | @implementation XCSourceFileDefinition 17 | 18 | @synthesize sourceFileName = _sourceFileName; 19 | @synthesize type = _type; 20 | @synthesize data = _data; 21 | 22 | /* ====================================================================================================================================== */ 23 | #pragma mark - Class Methods 24 | 25 | + (XCSourceFileDefinition*)sourceDefinitionWithName:(NSString*)name text:(NSString*)text type:(XcodeSourceFileType)type 26 | { 27 | 28 | return [[XCSourceFileDefinition alloc] initWithName:name text:text type:type]; 29 | } 30 | 31 | + (XCSourceFileDefinition*)sourceDefinitionWithName:(NSString*)name data:(NSData*)data type:(XcodeSourceFileType)type 32 | { 33 | 34 | return [[XCSourceFileDefinition alloc] initWithName:name data:data type:type]; 35 | } 36 | 37 | 38 | /* ====================================================================================================================================== */ 39 | #pragma mark - Initialization & Destruction 40 | 41 | - (id)initWithName:(NSString*)name text:(NSString*)text type:(XcodeSourceFileType)type 42 | { 43 | self = [super init]; 44 | if (self) 45 | { 46 | _sourceFileName = [name copy]; 47 | _data = [[text dataUsingEncoding:NSUTF8StringEncoding] copy]; 48 | _type = type; 49 | } 50 | return self; 51 | } 52 | 53 | - (id)initWithName:(NSString*)name data:(NSData*)data type:(XcodeSourceFileType)type 54 | { 55 | self = [super init]; 56 | if (self) 57 | { 58 | _sourceFileName = [name copy]; 59 | _data = [data copy]; 60 | _type = type; 61 | } 62 | return self; 63 | 64 | } 65 | 66 | @end -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XCSubProjectDefinition.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | 14 | #import 15 | #import "XCAbstractDefinition.h" 16 | #import "XcodeSourceFileType.h" 17 | 18 | @class XCProject; 19 | 20 | 21 | @interface XCSubProjectDefinition : XCAbstractDefinition 22 | { 23 | 24 | NSString* _name; 25 | NSString* _path; 26 | XcodeSourceFileType _type; 27 | XCProject* _subProject; 28 | XCProject* _parentProject; 29 | NSString* _key; 30 | NSString* _fullProjectPath; 31 | NSString* _relativePath; 32 | } 33 | 34 | 35 | @property(nonatomic, strong, readonly) NSString* name; 36 | @property(nonatomic, strong, readonly) NSString* path; 37 | @property(nonatomic, readonly) XcodeSourceFileType type; 38 | @property(nonatomic, strong, readonly) XCProject* subProject; 39 | @property(nonatomic, strong, readonly) XCProject* parentProject; 40 | @property(nonatomic, strong, readonly) NSString* key; 41 | @property(nonatomic, strong, readwrite) NSString* fullProjectPath; 42 | 43 | + (XCSubProjectDefinition*)withName:(NSString*)name path:(NSString*)path parentProject:(XCProject*)parentProject; 44 | 45 | - (id)initWithName:(NSString*)name path:(NSString*)path parentProject:(XCProject*)parentProject; 46 | 47 | - (NSString*)projectFileName; 48 | 49 | - (NSString*)fullPathName; 50 | 51 | - (NSArray*)buildProductNames; 52 | 53 | - (NSString*)projectKey; 54 | 55 | - (NSString*)pathRelativeToProjectRoot; 56 | 57 | - (NSString*)description; 58 | 59 | - (void)initFullProjectPath:(NSString*)fullProjectPath groupPath:(NSString*)groupPath; 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XCSubProjectDefinition.m: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | 14 | #import "XCProject.h" 15 | #import "XCProject+SubProject.h" 16 | #import "XCSubProjectDefinition.h" 17 | 18 | @interface XCSubProjectDefinition () 19 | @property(nonatomic, strong, readwrite) NSString* relativePath; 20 | @end 21 | 22 | @implementation XCSubProjectDefinition 23 | 24 | @synthesize name = _name; 25 | @synthesize path = _path; 26 | @synthesize type = _type; 27 | @synthesize parentProject = _parentProject; 28 | @synthesize subProject = _subProject; 29 | @synthesize relativePath = _relativePath; 30 | @synthesize key = _key; 31 | @synthesize fullProjectPath = _fullProjectPath; 32 | 33 | /* ====================================================================================================================================== */ 34 | #pragma mark - Class Methods 35 | 36 | + (XCSubProjectDefinition*)withName:(NSString*)name path:(NSString*)path parentProject:(XCProject*)parentProject 37 | { 38 | 39 | return [[XCSubProjectDefinition alloc] initWithName:name path:path parentProject:parentProject]; 40 | } 41 | 42 | /* ====================================================================================================================================== */ 43 | #pragma mark - Initialization & Destruction 44 | 45 | // Note - _path is most often going to be an absolute path. The method pathRelativeToProjectRoot below should be 46 | // used to get the form that's stored in the main project file. 47 | - (id)initWithName:(NSString*)name path:(NSString*)path parentProject:(XCProject*)parentProject 48 | { 49 | self = [super init]; 50 | if (self) 51 | { 52 | _name = [name copy]; 53 | _path = [path copy]; 54 | _type = XcodeProject; 55 | _parentProject = parentProject; 56 | _subProject = [[XCProject alloc] initWithFilePath:[NSString stringWithFormat:@"%@/%@.xcodeproj", path, name]]; 57 | } 58 | return self; 59 | } 60 | 61 | /* ====================================================================================================================================== */ 62 | #pragma mark - Interface Methods 63 | 64 | - (NSString*)projectFileName 65 | { 66 | return [_name stringByAppendingString:@".xcodeproj"]; 67 | } 68 | 69 | - (NSString*)fullPathName 70 | { 71 | return [NSString stringWithFormat:@"%@/%@", _path, [_name stringByAppendingString:@".xcodeproj"]]; 72 | } 73 | 74 | // returns an array of names of the build products of this project 75 | - (NSArray*)buildProductNames 76 | { 77 | NSMutableArray* results = [NSMutableArray array]; 78 | NSDictionary* objects = [_subProject objects]; 79 | [objects enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSDictionary* obj, BOOL* stop) 80 | { 81 | if ([[obj valueForKey:@"isa"] asMemberType] == PBXProjectType) 82 | { 83 | NSString* productRefGroupKey = [obj valueForKey:@"productRefGroup"]; 84 | NSDictionary* products = [objects valueForKey:productRefGroupKey]; 85 | NSArray* children = [products valueForKey:@"children"]; 86 | for (NSString* childKey in children) 87 | { 88 | NSDictionary* child = [objects valueForKey:childKey]; 89 | [results addObject:[child valueForKey:@"path"]]; 90 | } 91 | } 92 | }]; 93 | return results; 94 | } 95 | 96 | // returns the key of the PBXFileReference of the xcodeproj file 97 | - (NSString*)projectKey 98 | { 99 | if (_key == nil) 100 | { 101 | NSArray* xcodeprojKeys = 102 | [_parentProject keysForProjectObjectsOfType:PBXFileReferenceType withIdentifier:[self pathRelativeToProjectRoot] singleton:YES 103 | required:YES]; 104 | _key = [[xcodeprojKeys objectAtIndex:0] copy]; 105 | } 106 | return [_key copy]; 107 | } 108 | 109 | - (void)initFullProjectPath:(NSString*)fullProjectPath groupPath:(NSString*)groupPath 110 | { 111 | if (groupPath != nil) 112 | { 113 | NSMutableArray* fullPathComponents = [[fullProjectPath pathComponents] mutableCopy]; 114 | [fullPathComponents removeLastObject]; 115 | fullProjectPath = [[NSString pathWithComponents:fullPathComponents] stringByAppendingFormat:@"/%@", groupPath]; 116 | } 117 | _fullProjectPath = [fullProjectPath copy]; 118 | 119 | } 120 | 121 | // compares the given path to the filePath of the project, and returns a relative version. _fullProjectPath, which has 122 | // to hve been previously set, is the full path to the project *plus* the path to the xcodeproj's group, if any. 123 | - (NSString*)pathRelativeToProjectRoot 124 | { 125 | if (_relativePath == nil) 126 | { 127 | if (_fullProjectPath == nil) 128 | { 129 | [NSException raise:NSInvalidArgumentException format:@"fullProjectPath has not been set"]; 130 | } 131 | NSMutableArray* projectPathComponents = [[_fullProjectPath pathComponents] mutableCopy]; 132 | NSArray* objectPathComponents = [[self fullPathName] pathComponents]; 133 | NSString* convertedPath = @""; 134 | 135 | // skip over path components from root that are equal 136 | NSInteger limit = 137 | ([projectPathComponents count] < [objectPathComponents count]) ? [projectPathComponents count] : [objectPathComponents count]; 138 | NSInteger index1 = 0; 139 | for (; index1 < limit; index1++) 140 | { 141 | if ([[projectPathComponents objectAtIndex:index1] isEqualToString:[objectPathComponents objectAtIndex:index1]]) 142 | { 143 | continue; 144 | } 145 | else 146 | { 147 | break; 148 | } 149 | } 150 | // insert "../" for each remaining path component in project's xcodeproj path 151 | for (NSInteger index2 = 0; index2 < ([projectPathComponents count] - index1); index2++) 152 | { 153 | convertedPath = [convertedPath stringByAppendingString:@"../"]; 154 | } 155 | // tack on the unique part of the object's path 156 | for (NSInteger index3 = index1; index3 < [objectPathComponents count] - 1; index3++) 157 | { 158 | convertedPath = [convertedPath stringByAppendingFormat:@"%@/", [objectPathComponents objectAtIndex:index3]]; 159 | } 160 | _relativePath = [[convertedPath stringByAppendingString:[objectPathComponents lastObject]] copy]; 161 | } 162 | return [_relativePath copy]; 163 | } 164 | 165 | 166 | /* ====================================================================================================================================== */ 167 | #pragma mark - Utility Methods 168 | 169 | - (NSString*)description 170 | { 171 | return [NSString stringWithFormat:@"XcodeprojDefinition: sourceFileName = %@, path=%@, type=%d", _name, _path, _type]; 172 | } 173 | 174 | @end -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XCTarget.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | #import 14 | 15 | @class XCProject; 16 | @class XCSourceFile; 17 | @class XCBuildConfig; 18 | 19 | /** 20 | * Represents a target in an xcode project. 21 | */ 22 | @interface XCTarget : NSObject 23 | { 24 | 25 | XCProject* _project; 26 | NSString* _key; 27 | NSString* _name; 28 | NSString* _productName; 29 | NSString* _productReference; 30 | NSString* _defaultConfigurationName; 31 | 32 | @private 33 | NSMutableArray* _members; 34 | NSMutableArray* _resources; 35 | NSMutableDictionary* _configurations; 36 | } 37 | 38 | @property(nonatomic, strong, readonly) NSString* key; 39 | @property(nonatomic, strong) NSString* name; 40 | @property(nonatomic, strong) NSString* productName; 41 | @property(nonatomic, strong, readonly) NSString* productReference; 42 | 43 | + (XCTarget*)targetWithProject:(XCProject*)project key:(NSString*)key name:(NSString*)name productName:(NSString*)productName 44 | productReference:(NSString*)productReference; 45 | 46 | - (id)initWithProject:(XCProject*)project key:(NSString*)key name:(NSString*)name productName:(NSString*)productName 47 | productReference:(NSString*)productReference; 48 | 49 | - (NSArray*)resources; 50 | 51 | - (NSArray*)members; 52 | 53 | - (NSDictionary*)configurations; 54 | 55 | - (XCBuildConfig*)configurationWithName:(NSString*)name; 56 | 57 | - (XCBuildConfig*)defaultConfiguration; 58 | 59 | - (void)addMember:(XCSourceFile*)member; 60 | 61 | - (void)removeMemberWithKey:(NSString*)key; 62 | 63 | - (void)removeMembersWithKeys:(NSArray*)keys; 64 | 65 | - (void)addDependency:(NSString*)key; 66 | 67 | - (instancetype)duplicateWithTargetName:(NSString*)targetName productName:(NSString*)productName; 68 | 69 | @end 70 | 71 | -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XCTarget.m: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | #import "XCGroup.h" 13 | #import "XCKeyBuilder.h" 14 | #import "XCTarget.h" 15 | #import "XCSourceFile.h" 16 | #import "XCProject.h" 17 | #import "XCBuildConfig.h" 18 | 19 | @implementation XCTarget 20 | 21 | /* ====================================================================================================================================== */ 22 | #pragma mark - Class Methods 23 | 24 | + (XCTarget*)targetWithProject:(XCProject*)project key:(NSString*)key name:(NSString*)name productName:(NSString*)productName 25 | productReference:(NSString*)productReference 26 | { 27 | return [[XCTarget alloc] initWithProject:project key:key name:name productName:productName productReference:productReference]; 28 | } 29 | 30 | 31 | /* ====================================================================================================================================== */ 32 | #pragma mark - Initialization & Destruction 33 | 34 | - (id)initWithProject:(XCProject*)project key:(NSString*)key name:(NSString*)name productName:(NSString*)productName 35 | productReference:(NSString*)productReference 36 | { 37 | self = [super init]; 38 | if (self) 39 | { 40 | _project = project; 41 | _key = [key copy]; 42 | _name = [name copy]; 43 | _productName = [productName copy]; 44 | _productReference = [productReference copy]; 45 | } 46 | return self; 47 | } 48 | 49 | /* ====================================================================================================================================== */ 50 | #pragma mark - Interface Methods 51 | 52 | - (NSArray*)resources 53 | { 54 | if (_resources == nil) 55 | { 56 | _resources = [[NSMutableArray alloc] init]; 57 | for (NSString* buildPhaseKey in [[[_project objects] objectForKey:_key] objectForKey:@"buildPhases"]) 58 | { 59 | NSDictionary* buildPhase = [[_project objects] objectForKey:buildPhaseKey]; 60 | if ([[buildPhase valueForKey:@"isa"] asMemberType] == PBXResourcesBuildPhaseType) 61 | { 62 | for (NSString* buildFileKey in [buildPhase objectForKey:@"files"]) 63 | { 64 | XCSourceFile* targetMember = [self buildFileWithKey:buildFileKey]; 65 | if (targetMember) 66 | { 67 | [_resources addObject:[self buildFileWithKey:buildFileKey]]; 68 | } 69 | } 70 | } 71 | } 72 | } 73 | 74 | return _resources; 75 | } 76 | 77 | - (NSDictionary*)configurations 78 | { 79 | if (_configurations == nil) 80 | { 81 | NSString* buildConfigurationRootSectionKey = [[[_project objects] objectForKey:_key] objectForKey:@"buildConfigurationList"]; 82 | NSDictionary* buildConfigurationDictionary = [[_project objects] objectForKey:buildConfigurationRootSectionKey]; 83 | _configurations = 84 | [[XCBuildConfig buildConfigurationsFromArray:[buildConfigurationDictionary objectForKey:@"buildConfigurations"] 85 | inProject:_project] mutableCopy]; 86 | _defaultConfigurationName = [[buildConfigurationDictionary objectForKey:@"defaultConfigurationName"] copy]; 87 | } 88 | 89 | return _configurations; 90 | } 91 | 92 | - (XCBuildConfig*)defaultConfiguration 93 | { 94 | return [[self configurations] objectForKey:_defaultConfigurationName]; 95 | } 96 | 97 | - (XCBuildConfig*)configurationWithName:(NSString*)name 98 | { 99 | return [[self configurations] objectForKey:name]; 100 | } 101 | 102 | - (NSArray*)members 103 | { 104 | if (_members == nil) 105 | { 106 | _members = [[NSMutableArray alloc] init]; 107 | for (NSString* buildPhaseKey in [[[_project objects] objectForKey:_key] objectForKey:@"buildPhases"]) 108 | { 109 | NSDictionary* buildPhase = [[_project objects] objectForKey:buildPhaseKey]; 110 | if ([[buildPhase valueForKey:@"isa"] asMemberType] == PBXSourcesBuildPhaseType || 111 | [[buildPhase valueForKey:@"isa"] asMemberType] == PBXFrameworksBuildPhaseType) 112 | { 113 | for (NSString* buildFileKey in [buildPhase objectForKey:@"files"]) 114 | { 115 | XCSourceFile* targetMember = [self buildFileWithKey:buildFileKey]; 116 | if (targetMember) 117 | { 118 | [_members addObject:[_project fileWithKey:targetMember.key]]; 119 | } 120 | } 121 | } 122 | } 123 | } 124 | return _members; 125 | } 126 | 127 | - (void)addMember:(XCSourceFile*)member 128 | { 129 | [member becomeBuildFile]; 130 | NSDictionary* target = [[_project objects] objectForKey:_key]; 131 | 132 | for (NSString* buildPhaseKey in [target objectForKey:@"buildPhases"]) 133 | { 134 | NSMutableDictionary* buildPhase = [[_project objects] objectForKey:buildPhaseKey]; 135 | if ([[buildPhase valueForKey:@"isa"] asMemberType] == [member buildPhase]) 136 | { 137 | 138 | NSMutableArray* files = [buildPhase objectForKey:@"files"]; 139 | if (![files containsObject:[member buildFileKey]]) 140 | { 141 | [files addObject:[member buildFileKey]]; 142 | } 143 | 144 | [buildPhase setObject:files forKey:@"files"]; 145 | } 146 | } 147 | [self flagMembersAsDirty]; 148 | } 149 | 150 | - (NSDictionary*)buildRefWithFileRefKey 151 | { 152 | NSMutableDictionary* buildRefWithFileRefDict = [NSMutableDictionary dictionary]; 153 | NSDictionary* allObjects = [_project objects]; 154 | NSArray* keys = [allObjects allKeys]; 155 | 156 | for (NSString* key in keys) 157 | { 158 | NSDictionary* dictionaryInfo = [allObjects objectForKey:key]; 159 | 160 | NSString* type = [dictionaryInfo objectForKey:@"isa"]; 161 | if (type) 162 | { 163 | if ([type isEqualToString:@"PBXBuildFile"]) 164 | { 165 | NSString* fileRef = [dictionaryInfo objectForKey:@"fileRef"]; 166 | 167 | if (fileRef) 168 | { 169 | [buildRefWithFileRefDict setObject:key forKey:fileRef]; 170 | } 171 | } 172 | } 173 | } 174 | return buildRefWithFileRefDict; 175 | } 176 | 177 | - (void)removeMemberWithKey:(NSString*)key 178 | { 179 | 180 | NSDictionary* buildRefWithFileRef = [self buildRefWithFileRefKey]; 181 | NSDictionary* target = [[_project objects] objectForKey:_key]; 182 | NSString* buildRef = [buildRefWithFileRef objectForKey:key]; 183 | 184 | if (!buildRef) 185 | { 186 | return; 187 | } 188 | 189 | for (NSString* buildPhaseKey in [target objectForKey:@"buildPhases"]) 190 | { 191 | NSMutableDictionary* buildPhase = [[_project objects] objectForKey:buildPhaseKey]; 192 | NSMutableArray* files = [buildPhase objectForKey:@"files"]; 193 | 194 | [files removeObjectIdenticalTo:buildRef]; 195 | [buildPhase setObject:files forKey:@"files"]; 196 | } 197 | [self flagMembersAsDirty]; 198 | } 199 | 200 | - (void)removeMembersWithKeys:(NSArray*)keys 201 | { 202 | for (NSString* key in keys) 203 | { 204 | [self removeMemberWithKey:key]; 205 | } 206 | } 207 | 208 | - (void)addDependency:(NSString*)key 209 | { 210 | NSDictionary* targetObj = [[_project objects] objectForKey:_key]; 211 | NSMutableArray* dependencies = [targetObj valueForKey:@"dependencies"]; 212 | // add only if not already there 213 | BOOL found = NO; 214 | for (NSString* dependency in dependencies) 215 | { 216 | if ([dependency isEqualToString:key]) 217 | { 218 | found = YES; 219 | break; 220 | } 221 | } 222 | if (!found) 223 | { 224 | [dependencies addObject:key]; 225 | } 226 | } 227 | 228 | - (instancetype)duplicateWithTargetName:(NSString*)targetName productName:(NSString*)productName 229 | { 230 | 231 | NSDictionary* targetObj = _project.objects[_key]; 232 | NSMutableDictionary* dupTargetObj = [targetObj mutableCopy]; 233 | 234 | dupTargetObj[@"name"] = targetName; 235 | dupTargetObj[@"productName"] = productName; 236 | 237 | NSString* buildConfigurationListKey = dupTargetObj[@"buildConfigurationList"]; 238 | 239 | void(^visitor)(NSMutableDictionary*) = ^(NSMutableDictionary* buildConfiguration) 240 | { 241 | buildConfiguration[@"buildSettings"][@"PRODUCT_NAME"] = productName; 242 | }; 243 | 244 | dupTargetObj[@"buildConfigurationList"] = 245 | [XCBuildConfig duplicatedBuildConfigurationListWithKey:buildConfigurationListKey inProject:_project 246 | withBuildConfigurationVisitor:visitor]; 247 | 248 | [self duplicateProductReferenceForTargetObject:dupTargetObj withProductName:productName]; 249 | 250 | [self duplicateBuildPhasesForTargetObject:dupTargetObj]; 251 | 252 | [self addReferenceToProductsGroupForTargetObject:dupTargetObj]; 253 | 254 | NSString* dupTargetObjKey = [self addTargetToRootObjectTargets:dupTargetObj]; 255 | 256 | [_project dropCache]; 257 | 258 | return [[XCTarget alloc] initWithProject:_project key:dupTargetObjKey name:targetName productName:productName 259 | productReference:dupTargetObj[@"productReference"]]; 260 | } 261 | 262 | /* ====================================================================================================================================== */ 263 | #pragma mark - Overridden Methods 264 | 265 | - (void)setName:(NSString*)name 266 | { 267 | _name = name; 268 | NSDictionary* dictionary = [_project.objects objectForKey:_key]; 269 | [dictionary setValue:_name forKey:@"name"]; 270 | } 271 | 272 | - (void)setProductName:(NSString*)productName 273 | { 274 | _productName = productName; 275 | NSDictionary* dictionary = [_project.objects objectForKey:_key]; 276 | [dictionary setValue:_productName forKey:@"productName"]; 277 | } 278 | 279 | 280 | 281 | /* ====================================================================================================================================== */ 282 | #pragma mark - Utility Methods 283 | 284 | - (NSString*)description 285 | { 286 | return [NSString stringWithFormat:@"Target: name=%@, files=%@", _name, _members]; 287 | } 288 | 289 | /* ====================================================================================================================================== */ 290 | #pragma mark - Private Methods 291 | 292 | - (XCSourceFile*)buildFileWithKey:(NSString*)theKey 293 | { 294 | NSDictionary* obj = [[_project objects] valueForKey:theKey]; 295 | if (obj) 296 | { 297 | if ([[obj valueForKey:@"isa"] asMemberType] == PBXBuildFileType) 298 | { 299 | return [_project fileWithKey:[obj valueForKey:@"fileRef"]]; 300 | } 301 | } 302 | return nil; 303 | } 304 | 305 | - (void)flagMembersAsDirty 306 | { 307 | _members = nil; 308 | } 309 | 310 | - (void)duplicateProductReferenceForTargetObject:(NSMutableDictionary*)dupTargetObj withProductName:(NSString*)productName 311 | { 312 | 313 | NSString* productReferenceKey = dupTargetObj[@"productReference"]; 314 | NSMutableDictionary* dupProductReference = [_project.objects[productReferenceKey] mutableCopy]; 315 | 316 | NSString* path = dupProductReference[@"path"]; 317 | NSString* dupPath = [path stringByDeletingLastPathComponent]; 318 | dupPath = [dupPath stringByAppendingPathComponent:productName]; 319 | dupPath = [dupPath stringByAppendingPathExtension:@"app"]; 320 | dupProductReference[@"path"] = dupPath; 321 | 322 | NSString* dupProductReferenceKey = [[XCKeyBuilder createUnique] build]; 323 | 324 | _project.objects[dupProductReferenceKey] = dupProductReference; 325 | dupTargetObj[@"productReference"] = dupProductReferenceKey; 326 | } 327 | 328 | - (void)duplicateBuildPhasesForTargetObject:(NSMutableDictionary*)dupTargetObj 329 | { 330 | 331 | NSMutableArray* buildPhases = [NSMutableArray array]; 332 | 333 | for (NSString* buildPhaseKey in dupTargetObj[@"buildPhases"]) 334 | { 335 | 336 | NSMutableDictionary* dupBuildPhase = [_project.objects[buildPhaseKey] mutableCopy]; 337 | NSMutableArray* dupFiles = [NSMutableArray array]; 338 | 339 | for (NSString* fileKey in dupBuildPhase[@"files"]) 340 | { 341 | 342 | NSMutableDictionary* dupFile = [_project.objects[fileKey] mutableCopy]; 343 | NSString* dupFileKey = [[XCKeyBuilder createUnique] build]; 344 | 345 | _project.objects[dupFileKey] = dupFile; 346 | [dupFiles addObject:dupFileKey]; 347 | } 348 | 349 | dupBuildPhase[@"files"] = dupFiles; 350 | 351 | NSString* dupBuildPhaseKey = [[XCKeyBuilder createUnique] build]; 352 | _project.objects[dupBuildPhaseKey] = dupBuildPhase; 353 | [buildPhases addObject:dupBuildPhaseKey]; 354 | } 355 | 356 | dupTargetObj[@"buildPhases"] = buildPhases; 357 | } 358 | 359 | - (void)addReferenceToProductsGroupForTargetObject:(NSMutableDictionary*)dupTargetObj 360 | { 361 | 362 | XCGroup* mainGroup = nil; 363 | NSPredicate* productsPredicate = [NSPredicate predicateWithFormat:@"displayName == 'Products'"]; 364 | NSArray* filteredGroups = [_project.groups filteredArrayUsingPredicate:productsPredicate]; 365 | 366 | if (filteredGroups.count > 0) 367 | { 368 | mainGroup = filteredGroups[0]; 369 | NSMutableArray* children = [_project.objects[mainGroup.key][@"children"] mutableCopy]; 370 | [children addObject:dupTargetObj[@"productReference"]]; 371 | _project.objects[mainGroup.key][@"children"] = children; 372 | } 373 | } 374 | 375 | - (NSString*)addTargetToRootObjectTargets:(NSMutableDictionary*)dupTargetObj 376 | { 377 | NSString* dupTargetObjKey = [[XCKeyBuilder createUnique] build]; 378 | 379 | _project.objects[dupTargetObjKey] = dupTargetObj; 380 | 381 | NSString* rootObjKey = _project.dataStore[@"rootObject"]; 382 | NSMutableDictionary* rootObj = [_project.objects[rootObjKey] mutableCopy]; 383 | NSMutableArray* rootObjTargets = [rootObj[@"targets"] mutableCopy]; 384 | [rootObjTargets addObject:dupTargetObjKey]; 385 | 386 | rootObj[@"targets"] = rootObjTargets; 387 | _project.objects[rootObjKey] = rootObj; 388 | 389 | return dupTargetObjKey; 390 | } 391 | 392 | @end 393 | -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XCXibDefinition.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | 14 | #import 15 | #import "XCAbstractDefinition.h" 16 | 17 | 18 | @interface XCXibDefinition : XCAbstractDefinition 19 | { 20 | NSString* _name; 21 | NSString* _content; 22 | } 23 | @property(nonatomic, strong, readonly) NSString* name; 24 | @property(nonatomic, strong) NSString* content; 25 | 26 | + (XCXibDefinition*)xibDefinitionWithName:(NSString*)name; 27 | 28 | + (XCXibDefinition*)xibDefinitionWithName:(NSString*)name content:(NSString*)content; 29 | 30 | - (id)initWithName:(NSString*)name; 31 | 32 | - (id)initWithName:(NSString*)name content:(NSString*)content; 33 | 34 | - (NSString*)xibFileName; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XCXibDefinition.m: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | #import "XCXibDefinition.h" 14 | 15 | @implementation XCXibDefinition 16 | 17 | @synthesize name = _name; 18 | @synthesize content = _content; 19 | 20 | /* ====================================================================================================================================== */ 21 | #pragma mark - Class Methods 22 | 23 | + (XCXibDefinition*)xibDefinitionWithName:(NSString*)name 24 | { 25 | return [[XCXibDefinition alloc] initWithName:name]; 26 | } 27 | 28 | + (XCXibDefinition*)xibDefinitionWithName:(NSString*)name content:(NSString*)content 29 | { 30 | return [[XCXibDefinition alloc] initWithName:name content:content]; 31 | } 32 | 33 | 34 | /* ====================================================================================================================================== */ 35 | #pragma mark - Initialization & Destruction 36 | 37 | - (id)initWithName:(NSString*)name 38 | { 39 | return [self initWithName:name content:nil]; 40 | } 41 | 42 | 43 | - (id)initWithName:(NSString*)name content:(NSString*)content 44 | { 45 | self = [super init]; 46 | if (self) 47 | { 48 | _name = [name copy]; 49 | _content = [content copy]; 50 | } 51 | return self; 52 | } 53 | 54 | 55 | /* ====================================================================================================================================== */ 56 | #pragma mark - Interface Methods 57 | 58 | - (NSString*)xibFileName 59 | { 60 | return [_name stringByAppendingString:@".xib"]; 61 | } 62 | 63 | @end -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XcodeEditor.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | #import "XCAbstractDefinition.h" 14 | #import "XCGroup.h" 15 | #import "XCClassDefinition.h" 16 | #import "XCFileOperationQueue.h" 17 | #import "XCFrameworkDefinition.h" 18 | #import "XCProject.h" 19 | #import "XCSourceFile.h" 20 | #import "XCSourceFileDefinition.h" 21 | #import "XCSubProjectDefinition.h" 22 | #import "XCTarget.h" 23 | #import "XCXibDefinition.h" 24 | -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XcodeGroupMember.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | 14 | #import "XcodeMemberType.h" 15 | 16 | @protocol XcodeGroupMember 17 | 18 | - (NSString*)key; 19 | 20 | - (NSString*)displayName; 21 | 22 | - (NSString*)pathRelativeToProjectRoot; 23 | 24 | /** 25 | * Group members can either be other groups (PBXGroup) or source files (PBXFileReference). 26 | */ 27 | - (XcodeMemberType)groupMemberType; 28 | @end -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XcodeMemberType.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | #import 13 | 14 | typedef enum 15 | { 16 | PBXNilType, 17 | PBXBuildFileType, 18 | PBXContainerItemProxyType, 19 | PBXCopyFilesBuildPhaseType, 20 | PBXFileReferenceType, 21 | PBXFrameworksBuildPhaseType, 22 | PBXGroupType, 23 | PBXNativeTargetType, 24 | PBXProjectType, 25 | PBXReferenceProxyType, 26 | PBXResourcesBuildPhaseType, 27 | PBXSourcesBuildPhaseType, 28 | PBXTargetDependencyType, 29 | PBXVariantGroupType, 30 | XCBuildConfigurationType, 31 | XCConfigurationListType 32 | } XcodeMemberType; 33 | 34 | @interface NSString (XcodeMemberTypeExtensions) 35 | 36 | + (NSString*)stringFromMemberType:(XcodeMemberType)nodeType; 37 | 38 | - (XcodeMemberType)asMemberType; 39 | 40 | @end 41 | 42 | 43 | -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XcodeMemberType.m: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | 14 | 15 | #import "XcodeMemberType.h" 16 | 17 | 18 | @implementation NSDictionary (XcodeMemberType) 19 | 20 | 21 | + (NSDictionary*)dictionaryWithProjectNodeTypesAsStrings 22 | { 23 | // This is the most vital operation on adding 500+ files 24 | // So, we caching this dictionary 25 | static NSDictionary* _projectNodeTypesAsStrings; 26 | static dispatch_once_t onceToken; 27 | dispatch_once(&onceToken, ^ 28 | { 29 | _projectNodeTypesAsStrings = 30 | [[NSDictionary alloc] initWithObjectsAndKeys:@(PBXNilType), @"PBXNilType", 31 | @(PBXBuildFileType), @"PBXBuildFile", 32 | @(PBXContainerItemProxyType), @"PBXContainerItemProxy", 33 | @(PBXCopyFilesBuildPhaseType), @"PBXCopyFilesBuildPhase", 34 | @(PBXFileReferenceType), @"PBXFileReference", 35 | @(PBXFrameworksBuildPhaseType), @"PBXFrameworksBuildPhase", 36 | @(PBXGroupType), @"PBXGroup", 37 | @(PBXNativeTargetType), @"PBXNativeTarget", 38 | @(PBXProjectType), @"PBXProject", 39 | @(PBXReferenceProxyType), @"PBXReferenceProxy", 40 | @(PBXResourcesBuildPhaseType), @"PBXResourcesBuildPhase", 41 | @(PBXSourcesBuildPhaseType), @"PBXSourcesBuildPhase", 42 | @(PBXTargetDependencyType), @"PBXTargetDependency", 43 | @(PBXVariantGroupType), @"PBXVariantGroup", 44 | @(XCBuildConfigurationType), @"XCBuildConfiguration", 45 | @(XCConfigurationListType), @"XCConfigurationList", nil]; 46 | }); 47 | return _projectNodeTypesAsStrings; 48 | } 49 | 50 | @end 51 | 52 | @implementation NSString (XcodeMemberTypeExtensions) 53 | 54 | + (NSString*)stringFromMemberType:(XcodeMemberType)nodeType 55 | { 56 | NSDictionary* nodeTypesToString = [NSDictionary dictionaryWithProjectNodeTypesAsStrings]; 57 | return [[nodeTypesToString allKeysForObject:@(nodeType)] objectAtIndex:0]; 58 | } 59 | 60 | 61 | - (XcodeMemberType)asMemberType 62 | { 63 | NSDictionary* nodeTypesToString = [NSDictionary dictionaryWithProjectNodeTypesAsStrings]; 64 | return (XcodeMemberType) [[nodeTypesToString objectForKey:self] intValue]; 65 | } 66 | 67 | 68 | @end -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XcodeSourceFileType.h: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | #import 13 | 14 | typedef enum 15 | { 16 | FileTypeNil, // Unknown filetype 17 | Framework, // .framework 18 | PropertyList, // .plist 19 | SourceCodeHeader, // .h 20 | SourceCodeObjC, // .m 21 | SourceCodeObjCPlusPlus, // .mm 22 | SourceCodeCPlusPlus, // .cpp 23 | XibFile, // .xib 24 | ImageResourcePNG, // .png 25 | Bundle, // .bundle .octet 26 | Archive, // .a files 27 | HTML, // HTML file 28 | TEXT, // Some text file 29 | XcodeProject // .xcodeproj 30 | } XcodeSourceFileType; 31 | 32 | NSString* NSStringFromXCSourceFileType(XcodeSourceFileType type); 33 | 34 | XcodeSourceFileType XCSourceFileTypeFromStringRepresentation(NSString* string); 35 | 36 | XcodeSourceFileType XCSourceFileTypeFromFileName(NSString* fileName); 37 | 38 | -------------------------------------------------------------------------------- /SuggestedColors/XCodeEditor/XcodeSourceFileType.m: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // JASPER BLUES 4 | // Copyright 2012 Jasper Blues 5 | // All Rights Reserved. 6 | // 7 | // NOTICE: Jasper Blues permits you to use, modify, and distribute this file 8 | // in accordance with the terms of the license agreement accompanying it. 9 | // 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | 13 | 14 | #import "XcodeSourceFileType.h" 15 | 16 | NSDictionary* NSDictionaryWithXCFileReferenceTypes() 17 | { 18 | static NSDictionary* dictionary; 19 | static dispatch_once_t onceToken; 20 | dispatch_once(&onceToken, ^ 21 | { 22 | dictionary = @{ 23 | @"sourcecode.c.h" : @(SourceCodeHeader), 24 | @"sourcecode.c.objc" : @(SourceCodeObjC), 25 | @"wrapper.framework" : @(Framework), 26 | @"text.plist.strings" : @(PropertyList), 27 | @"sourcecode.cpp.objcpp" : @(SourceCodeObjCPlusPlus), 28 | @"sourcecode.cpp.cpp" : @(SourceCodeCPlusPlus), 29 | @"file.xib" : @(XibFile), 30 | @"image.png" : @(ImageResourcePNG), 31 | @"wrapper.cfbundle" : @(Bundle), 32 | @"archive.ar" : @(Archive), 33 | @"text.html" : @(HTML), 34 | @"text" : @(TEXT), 35 | @"wrapper.pb-project" : @(XcodeProject) 36 | }; 37 | }); 38 | 39 | return dictionary; 40 | } 41 | 42 | NSString* NSStringFromXCSourceFileType(XcodeSourceFileType type) 43 | { 44 | return [[NSDictionaryWithXCFileReferenceTypes() allKeysForObject:@(type)] objectAtIndex:0]; 45 | } 46 | 47 | XcodeSourceFileType XCSourceFileTypeFromStringRepresentation(NSString* string) 48 | { 49 | NSDictionary* typeStrings = NSDictionaryWithXCFileReferenceTypes(); 50 | 51 | if ([typeStrings objectForKey:string]) 52 | { 53 | return (XcodeSourceFileType) [[typeStrings objectForKey:string] intValue]; 54 | } 55 | else 56 | { 57 | return FileTypeNil; 58 | } 59 | } 60 | 61 | 62 | XcodeSourceFileType XCSourceFileTypeFromFileName(NSString* fileName) 63 | { 64 | if ([fileName hasSuffix:@".h"] || [fileName hasSuffix:@".hh"] || [fileName hasSuffix:@".hpp"] || [fileName hasSuffix:@".hxx"]) 65 | { 66 | return SourceCodeHeader; 67 | } 68 | if ([fileName hasSuffix:@".c"] || [fileName hasSuffix:@".m"]) 69 | { 70 | return SourceCodeObjC; 71 | } 72 | if ([fileName hasSuffix:@".mm"]) 73 | { 74 | return SourceCodeObjCPlusPlus; 75 | } 76 | if ([fileName hasSuffix:@".cpp"]) 77 | { 78 | return SourceCodeCPlusPlus; 79 | } 80 | return FileTypeNil; 81 | } 82 | 83 | -------------------------------------------------------------------------------- /SuggestedColorsTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.dangdang.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /SuggestedColorsTests/SuggestedColorsTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // SuggestedColorsTests.m 3 | // SuggestedColorsTests 4 | // 5 | // Created by wangchaojs02 on 15/6/3. 6 | // Copyright (c) 2015年 Monsters Inc. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface SuggestedColorsTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation SuggestedColorsTests 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown { 24 | // Put teardown code here. This method is called after the invocation of each test method in the class. 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testExample { 29 | // This is an example of a functional test case. 30 | XCTAssert(YES, @"Pass"); 31 | } 32 | 33 | - (void)testPerformanceExample { 34 | // This is an example of a performance test case. 35 | [self measureBlock:^{ 36 | // Put the code you want to measure the time of here. 37 | }]; 38 | } 39 | 40 | @end 41 | --------------------------------------------------------------------------------