├── CodeKitCodaPlugin.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── CodeKitCodaPlugin.xccheckout └── xcuserdata │ └── bdkjones.xcuserdatad │ └── xcschemes │ ├── CodeKitCodaPlugin.xcscheme │ └── xcschememanagement.plist ├── CodeKitCodaPlugin ├── CodaPlugInsController.h ├── Info.plist ├── VDKCodeKitCodaPlugin.h └── VDKCodeKitCodaPlugin.m └── README.md /CodeKitCodaPlugin.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | A6EADF6E19E3B65A00E64DBC /* VDKCodeKitCodaPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = A6EADF6D19E3B65A00E64DBC /* VDKCodeKitCodaPlugin.m */; }; 11 | /* End PBXBuildFile section */ 12 | 13 | /* Begin PBXFileReference section */ 14 | A6EADF6219E3B5CA00E64DBC /* CodeKitCodaPlugin.codaplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CodeKitCodaPlugin.codaplugin; sourceTree = BUILT_PRODUCTS_DIR; }; 15 | A6EADF6619E3B5CA00E64DBC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 16 | A6EADF6C19E3B65A00E64DBC /* VDKCodeKitCodaPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VDKCodeKitCodaPlugin.h; sourceTree = ""; }; 17 | A6EADF6D19E3B65A00E64DBC /* VDKCodeKitCodaPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VDKCodeKitCodaPlugin.m; sourceTree = ""; }; 18 | A6EADF6F19E3B67300E64DBC /* CodaPlugInsController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CodaPlugInsController.h; sourceTree = ""; }; 19 | /* End PBXFileReference section */ 20 | 21 | /* Begin PBXFrameworksBuildPhase section */ 22 | A6EADF5F19E3B5CA00E64DBC /* Frameworks */ = { 23 | isa = PBXFrameworksBuildPhase; 24 | buildActionMask = 2147483647; 25 | files = ( 26 | ); 27 | runOnlyForDeploymentPostprocessing = 0; 28 | }; 29 | /* End PBXFrameworksBuildPhase section */ 30 | 31 | /* Begin PBXGroup section */ 32 | A6EADF5919E3B5CA00E64DBC = { 33 | isa = PBXGroup; 34 | children = ( 35 | A6EADF6419E3B5CA00E64DBC /* CodeKitCodaPlugin */, 36 | A6EADF6319E3B5CA00E64DBC /* Products */, 37 | ); 38 | sourceTree = ""; 39 | }; 40 | A6EADF6319E3B5CA00E64DBC /* Products */ = { 41 | isa = PBXGroup; 42 | children = ( 43 | A6EADF6219E3B5CA00E64DBC /* CodeKitCodaPlugin.codaplugin */, 44 | ); 45 | name = Products; 46 | sourceTree = ""; 47 | }; 48 | A6EADF6419E3B5CA00E64DBC /* CodeKitCodaPlugin */ = { 49 | isa = PBXGroup; 50 | children = ( 51 | A6EADF6F19E3B67300E64DBC /* CodaPlugInsController.h */, 52 | A6EADF6C19E3B65A00E64DBC /* VDKCodeKitCodaPlugin.h */, 53 | A6EADF6D19E3B65A00E64DBC /* VDKCodeKitCodaPlugin.m */, 54 | A6EADF6519E3B5CA00E64DBC /* Supporting Files */, 55 | ); 56 | path = CodeKitCodaPlugin; 57 | sourceTree = ""; 58 | }; 59 | A6EADF6519E3B5CA00E64DBC /* Supporting Files */ = { 60 | isa = PBXGroup; 61 | children = ( 62 | A6EADF6619E3B5CA00E64DBC /* Info.plist */, 63 | ); 64 | name = "Supporting Files"; 65 | sourceTree = ""; 66 | }; 67 | /* End PBXGroup section */ 68 | 69 | /* Begin PBXNativeTarget section */ 70 | A6EADF6119E3B5CA00E64DBC /* CodeKitCodaPlugin */ = { 71 | isa = PBXNativeTarget; 72 | buildConfigurationList = A6EADF6919E3B5CA00E64DBC /* Build configuration list for PBXNativeTarget "CodeKitCodaPlugin" */; 73 | buildPhases = ( 74 | A6EADF5E19E3B5CA00E64DBC /* Sources */, 75 | A6EADF5F19E3B5CA00E64DBC /* Frameworks */, 76 | A6EADF6019E3B5CA00E64DBC /* Resources */, 77 | A63ED94F19E615610041383F /* Increment Build Number */, 78 | ); 79 | buildRules = ( 80 | ); 81 | dependencies = ( 82 | ); 83 | name = CodeKitCodaPlugin; 84 | productName = CodeKitCodaPlugin; 85 | productReference = A6EADF6219E3B5CA00E64DBC /* CodeKitCodaPlugin.codaplugin */; 86 | productType = "com.apple.product-type.bundle"; 87 | }; 88 | /* End PBXNativeTarget section */ 89 | 90 | /* Begin PBXProject section */ 91 | A6EADF5A19E3B5CA00E64DBC /* Project object */ = { 92 | isa = PBXProject; 93 | attributes = { 94 | CLASSPREFIX = VDK; 95 | LastUpgradeCheck = 0800; 96 | ORGANIZATIONNAME = "Incident 57, Inc."; 97 | TargetAttributes = { 98 | A6EADF6119E3B5CA00E64DBC = { 99 | CreatedOnToolsVersion = 6.1; 100 | }; 101 | }; 102 | }; 103 | buildConfigurationList = A6EADF5D19E3B5CA00E64DBC /* Build configuration list for PBXProject "CodeKitCodaPlugin" */; 104 | compatibilityVersion = "Xcode 3.2"; 105 | developmentRegion = English; 106 | hasScannedForEncodings = 0; 107 | knownRegions = ( 108 | en, 109 | ); 110 | mainGroup = A6EADF5919E3B5CA00E64DBC; 111 | productRefGroup = A6EADF6319E3B5CA00E64DBC /* Products */; 112 | projectDirPath = ""; 113 | projectRoot = ""; 114 | targets = ( 115 | A6EADF6119E3B5CA00E64DBC /* CodeKitCodaPlugin */, 116 | ); 117 | }; 118 | /* End PBXProject section */ 119 | 120 | /* Begin PBXResourcesBuildPhase section */ 121 | A6EADF6019E3B5CA00E64DBC /* Resources */ = { 122 | isa = PBXResourcesBuildPhase; 123 | buildActionMask = 2147483647; 124 | files = ( 125 | ); 126 | runOnlyForDeploymentPostprocessing = 0; 127 | }; 128 | /* End PBXResourcesBuildPhase section */ 129 | 130 | /* Begin PBXShellScriptBuildPhase section */ 131 | A63ED94F19E615610041383F /* Increment Build Number */ = { 132 | isa = PBXShellScriptBuildPhase; 133 | buildActionMask = 2147483647; 134 | files = ( 135 | ); 136 | inputPaths = ( 137 | ); 138 | name = "Increment Build Number"; 139 | outputPaths = ( 140 | ); 141 | runOnlyForDeploymentPostprocessing = 0; 142 | shellPath = /bin/sh; 143 | shellScript = "PLIST_FILE=\"\"\nif [ -f \"$INFOPLIST_FILE\" ]; then\nPLIST_FILE=\"$INFOPLIST_FILE\"\nelse\nif [ -f \"$SRCROOT/$INFOPLIST_FILE\" ]; then\nPLIST_FILE=\"$SRCROOT/$INFOPLIST_FILE\"\nelse\nexit 0\nfi\nfi\nBN=$(/usr/libexec/PlistBuddy -c \"Print CFBundleVersion\" $PLIST_FILE)\nBN=$(($BN + 1))\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $BN\" $PLIST_FILE"; 144 | }; 145 | /* End PBXShellScriptBuildPhase section */ 146 | 147 | /* Begin PBXSourcesBuildPhase section */ 148 | A6EADF5E19E3B5CA00E64DBC /* Sources */ = { 149 | isa = PBXSourcesBuildPhase; 150 | buildActionMask = 2147483647; 151 | files = ( 152 | A6EADF6E19E3B65A00E64DBC /* VDKCodeKitCodaPlugin.m in Sources */, 153 | ); 154 | runOnlyForDeploymentPostprocessing = 0; 155 | }; 156 | /* End PBXSourcesBuildPhase section */ 157 | 158 | /* Begin XCBuildConfiguration section */ 159 | A6EADF6719E3B5CA00E64DBC /* Debug */ = { 160 | isa = XCBuildConfiguration; 161 | buildSettings = { 162 | ALWAYS_SEARCH_USER_PATHS = NO; 163 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 164 | CLANG_CXX_LIBRARY = "libc++"; 165 | CLANG_ENABLE_MODULES = YES; 166 | CLANG_ENABLE_OBJC_ARC = YES; 167 | CLANG_WARN_BOOL_CONVERSION = YES; 168 | CLANG_WARN_CONSTANT_CONVERSION = YES; 169 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 170 | CLANG_WARN_EMPTY_BODY = YES; 171 | CLANG_WARN_ENUM_CONVERSION = YES; 172 | CLANG_WARN_INFINITE_RECURSION = YES; 173 | CLANG_WARN_INT_CONVERSION = YES; 174 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 175 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 176 | CLANG_WARN_UNREACHABLE_CODE = YES; 177 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 178 | CODE_SIGN_IDENTITY = "Developer ID Application: Bryan Jones"; 179 | COPY_PHASE_STRIP = NO; 180 | ENABLE_STRICT_OBJC_MSGSEND = YES; 181 | ENABLE_TESTABILITY = YES; 182 | GCC_C_LANGUAGE_STANDARD = gnu99; 183 | GCC_DYNAMIC_NO_PIC = NO; 184 | GCC_NO_COMMON_BLOCKS = YES; 185 | GCC_OPTIMIZATION_LEVEL = 0; 186 | GCC_PREPROCESSOR_DEFINITIONS = ( 187 | "DEBUG=1", 188 | "$(inherited)", 189 | ); 190 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 191 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 192 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 193 | GCC_WARN_UNDECLARED_SELECTOR = YES; 194 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 195 | GCC_WARN_UNUSED_FUNCTION = YES; 196 | GCC_WARN_UNUSED_VARIABLE = YES; 197 | MACOSX_DEPLOYMENT_TARGET = 10.11; 198 | MTL_ENABLE_DEBUG_INFO = YES; 199 | ONLY_ACTIVE_ARCH = YES; 200 | SDKROOT = macosx; 201 | }; 202 | name = Debug; 203 | }; 204 | A6EADF6819E3B5CA00E64DBC /* Release */ = { 205 | isa = XCBuildConfiguration; 206 | buildSettings = { 207 | ALWAYS_SEARCH_USER_PATHS = NO; 208 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 209 | CLANG_CXX_LIBRARY = "libc++"; 210 | CLANG_ENABLE_MODULES = YES; 211 | CLANG_ENABLE_OBJC_ARC = YES; 212 | CLANG_WARN_BOOL_CONVERSION = YES; 213 | CLANG_WARN_CONSTANT_CONVERSION = YES; 214 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 215 | CLANG_WARN_EMPTY_BODY = YES; 216 | CLANG_WARN_ENUM_CONVERSION = YES; 217 | CLANG_WARN_INFINITE_RECURSION = YES; 218 | CLANG_WARN_INT_CONVERSION = YES; 219 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 220 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 221 | CLANG_WARN_UNREACHABLE_CODE = YES; 222 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 223 | CODE_SIGN_IDENTITY = "Developer ID Application: Bryan Jones"; 224 | COPY_PHASE_STRIP = YES; 225 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 226 | ENABLE_NS_ASSERTIONS = NO; 227 | ENABLE_STRICT_OBJC_MSGSEND = YES; 228 | GCC_C_LANGUAGE_STANDARD = gnu99; 229 | GCC_NO_COMMON_BLOCKS = YES; 230 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 231 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 232 | GCC_WARN_UNDECLARED_SELECTOR = YES; 233 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 234 | GCC_WARN_UNUSED_FUNCTION = YES; 235 | GCC_WARN_UNUSED_VARIABLE = YES; 236 | MACOSX_DEPLOYMENT_TARGET = 10.11; 237 | MTL_ENABLE_DEBUG_INFO = NO; 238 | SDKROOT = macosx; 239 | }; 240 | name = Release; 241 | }; 242 | A6EADF6A19E3B5CA00E64DBC /* Debug */ = { 243 | isa = XCBuildConfiguration; 244 | buildSettings = { 245 | COMBINE_HIDPI_IMAGES = YES; 246 | INFOPLIST_FILE = CodeKitCodaPlugin/Info.plist; 247 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; 248 | PRODUCT_BUNDLE_IDENTIFIER = "com.incident57.$(PRODUCT_NAME:rfc1034identifier)"; 249 | PRODUCT_NAME = "$(TARGET_NAME)"; 250 | SKIP_INSTALL = YES; 251 | WRAPPER_EXTENSION = codaplugin; 252 | }; 253 | name = Debug; 254 | }; 255 | A6EADF6B19E3B5CA00E64DBC /* Release */ = { 256 | isa = XCBuildConfiguration; 257 | buildSettings = { 258 | COMBINE_HIDPI_IMAGES = YES; 259 | INFOPLIST_FILE = CodeKitCodaPlugin/Info.plist; 260 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; 261 | PRODUCT_BUNDLE_IDENTIFIER = "com.incident57.$(PRODUCT_NAME:rfc1034identifier)"; 262 | PRODUCT_NAME = "$(TARGET_NAME)"; 263 | SKIP_INSTALL = YES; 264 | WRAPPER_EXTENSION = codaplugin; 265 | }; 266 | name = Release; 267 | }; 268 | /* End XCBuildConfiguration section */ 269 | 270 | /* Begin XCConfigurationList section */ 271 | A6EADF5D19E3B5CA00E64DBC /* Build configuration list for PBXProject "CodeKitCodaPlugin" */ = { 272 | isa = XCConfigurationList; 273 | buildConfigurations = ( 274 | A6EADF6719E3B5CA00E64DBC /* Debug */, 275 | A6EADF6819E3B5CA00E64DBC /* Release */, 276 | ); 277 | defaultConfigurationIsVisible = 0; 278 | defaultConfigurationName = Release; 279 | }; 280 | A6EADF6919E3B5CA00E64DBC /* Build configuration list for PBXNativeTarget "CodeKitCodaPlugin" */ = { 281 | isa = XCConfigurationList; 282 | buildConfigurations = ( 283 | A6EADF6A19E3B5CA00E64DBC /* Debug */, 284 | A6EADF6B19E3B5CA00E64DBC /* Release */, 285 | ); 286 | defaultConfigurationIsVisible = 0; 287 | defaultConfigurationName = Release; 288 | }; 289 | /* End XCConfigurationList section */ 290 | }; 291 | rootObject = A6EADF5A19E3B5CA00E64DBC /* Project object */; 292 | } 293 | -------------------------------------------------------------------------------- /CodeKitCodaPlugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CodeKitCodaPlugin.xcodeproj/project.xcworkspace/xcshareddata/CodeKitCodaPlugin.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 36F3BF16-6533-4C9E-AD5B-0BD97D02887E 9 | IDESourceControlProjectName 10 | CodeKitCodaPlugin 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 3135FACEA8A00AB89EA3E7EE1CEDA74D00EB1729 14 | https://github.com/bdkjones/CodeKitCodaPlugin.git 15 | 16 | IDESourceControlProjectPath 17 | CodeKitCodaPlugin.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 3135FACEA8A00AB89EA3E7EE1CEDA74D00EB1729 21 | ../.. 22 | 23 | IDESourceControlProjectURL 24 | https://github.com/bdkjones/CodeKitCodaPlugin.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | 3135FACEA8A00AB89EA3E7EE1CEDA74D00EB1729 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | 3135FACEA8A00AB89EA3E7EE1CEDA74D00EB1729 36 | IDESourceControlWCCName 37 | CodeKitCodaPlugin 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /CodeKitCodaPlugin.xcodeproj/xcuserdata/bdkjones.xcuserdatad/xcschemes/CodeKitCodaPlugin.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /CodeKitCodaPlugin.xcodeproj/xcuserdata/bdkjones.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | CodeKitCodaPlugin.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | A6EADF6119E3B5CA00E64DBC 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /CodeKitCodaPlugin/CodaPlugInsController.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | //////////////////////////////////////////////////////////////////////////////// 4 | // This object is passed during initialization. You must register your // 5 | // available functionality with one of the methods implemented by the // 6 | // plug-in controller // 7 | //////////////////////////////////////////////////////////////////////////////// 8 | 9 | @class CodaTextView; 10 | 11 | @interface CodaPlugInsController : NSObject 12 | 13 | //////////////////////////////////////////////////////////////////////////////// 14 | // The following methods are available to plugin developers in Coda 1.6 and // 15 | // later: // 16 | //////////////////////////////////////////////////////////////////////////////// 17 | 18 | 19 | // Returns the version of Coda that is hosting the plugin, such as "1.6.3" 20 | 21 | - (NSString*)codaVersion:(id)sender; 22 | 23 | 24 | // Returns to the plugin an abstract object representing the text view in Coda 25 | // that currently has focus 26 | 27 | - (CodaTextView*)focusedTextView:(id)sender; 28 | 29 | 30 | // Exposes to the user a plug-in action (a menu item) with the given title, that 31 | // will perform the given selector on the target 32 | 33 | - (void)registerActionWithTitle:(NSString*)title target:(id)target selector:(SEL)selector; 34 | 35 | 36 | // Returns 6 as of Coda 2.0.1 37 | 38 | - (NSUInteger)apiVersion; 39 | 40 | 41 | // Displays the provided HTML in a new tab. 42 | 43 | - (void)displayHTMLString:(NSString*)html; 44 | 45 | 46 | // Creates a new unsaved document in the frontmost Coda window and returns the Text View associated with it. 47 | // The text view provided is auto-released, so the caller does not need to explicitly release it. 48 | 49 | - (CodaTextView*)makeUntitledDocument; 50 | 51 | 52 | // Similar to registerActionWithTitle:target:selector: but allows further customization of the registered 53 | // menu items, including submenu title, represented object, keyEquivalent and custom plug-in name. 54 | 55 | - (void)registerActionWithTitle:(NSString*)title 56 | underSubmenuWithTitle:(NSString*)submenuTitle 57 | target:(id)target 58 | selector:(SEL)selector 59 | representedObject:(id)repOb 60 | keyEquivalent:(NSString*)keyEquivalent 61 | pluginName:(NSString*)aName; 62 | 63 | 64 | // Causes the frontmost Coda window to save all documents that have unsaved changes. 65 | 66 | - (void)saveAll; 67 | 68 | 69 | //////////////////////////////////////////////////////////////////////////////// 70 | // The following methods are available to plugin developers in Coda 2.0 and // 71 | // later: // 72 | //////////////////////////////////////////////////////////////////////////////// 73 | 74 | // Displays the provided HTML in a new tab with a specific baseURL 75 | 76 | - (void)displayHTMLString:(NSString*)html baseURL:(NSURL*)baseURL; 77 | 78 | 79 | // Opens text file at path, returning CodaTextView if successful 80 | 81 | - (CodaTextView*)openFileAtPath:(NSString*)path error:(NSError**)error; 82 | 83 | 84 | 85 | @end 86 | 87 | 88 | // 89 | // This is your hook to a text view in Coda. You can use this to provide 90 | // manipulation of files. 91 | // 92 | 93 | @class CodaPlainTextEditor; 94 | 95 | @interface CodaTextView : NSObject 96 | { 97 | CodaPlainTextEditor* editor; 98 | } 99 | 100 | //////////////////////////////////////////////////////////////////////////////// 101 | // The following methods are available to plugin developers in Coda 1.6 and // 102 | // later. // 103 | //////////////////////////////////////////////////////////////////////////////// 104 | 105 | // Inserts the given string at the insertion point 106 | 107 | - (void)insertText:(NSString*)inText; 108 | 109 | 110 | // Replaces characters in the given range with the given string 111 | 112 | - (void)replaceCharactersInRange:(NSRange)aRange withString:(NSString *)aString; 113 | 114 | 115 | // Returns the range of currently selected characters 116 | 117 | - (NSRange)selectedRange; 118 | 119 | 120 | // Returns the currently selected text, or nil if none 121 | 122 | - (NSString*)selectedText; 123 | 124 | 125 | // Selects the given character range 126 | 127 | - (void)setSelectedRange:(NSRange)range; 128 | 129 | // Returns a string containing the entire content of the line that the insertion 130 | // point is on 131 | 132 | - (NSString*)currentLine; 133 | 134 | 135 | // Returns the line number corresponding to the location of the insertion point 136 | 137 | - (NSUInteger)currentLineNumber; 138 | 139 | 140 | // Deletes the selected text range 141 | 142 | - (void)deleteSelection; 143 | 144 | 145 | // Returns the current line ending of the file 146 | 147 | - (NSString*)lineEnding; 148 | 149 | 150 | // Returns the character range of the entire line the insertion point 151 | // is on 152 | 153 | - (NSRange)rangeOfCurrentLine; 154 | 155 | 156 | // StartOfLine returns the character index (relative to the beginning of the 157 | // document) of the start of the line the insertion point is on 158 | 159 | - (NSUInteger)startOfLine; 160 | 161 | 162 | // String returns the entire document as a plain string 163 | 164 | - (NSString*)string; 165 | 166 | 167 | // Returns the specified ranged substring of the entire document 168 | 169 | - (NSString*)stringWithRange:(NSRange)range; 170 | 171 | 172 | // Returns the width of tabs as spaces 173 | 174 | - (NSInteger)tabWidth; 175 | 176 | 177 | // Returns the range of the word previous to the insertion point 178 | 179 | - (NSRange)previousWordRange; 180 | 181 | 182 | // UsesTabs returns if the editor is currently uses tabs instead of spaces for 183 | // indentation 184 | 185 | - (BOOL)usesTabs; 186 | 187 | // saves the document you are working on 188 | 189 | - (void)save; 190 | 191 | // Saves the document you are working on to a local path, returns YES if 192 | // successful 193 | 194 | - (BOOL)saveToPath:(NSString*)aPath; 195 | 196 | // Allows for multiple text manipulations to be considered one "undo/redo" 197 | // operation 198 | 199 | - (void)beginUndoGrouping; 200 | - (void)endUndoGrouping; 201 | 202 | 203 | // Returns the window the editor is located in (useful for showing sheets) 204 | 205 | - (NSWindow*)window; 206 | 207 | 208 | // Returns the path to the text view's file (may be nil for unsaved documents) 209 | 210 | - (NSString*)path; 211 | 212 | 213 | // Returns the root local path of the site if specified (nil if unspecified in 214 | // the site or site is not loaded) 215 | 216 | - (NSString*)siteLocalPath; 217 | 218 | 219 | //////////////////////////////////////////////////////////////////////////////// 220 | // The following methods are available to plugin developers in Coda 1.6.1 and // 221 | // later. // 222 | //////////////////////////////////////////////////////////////////////////////// 223 | 224 | // Returns the range of the word containing the insertion point 225 | 226 | - (NSRange)currentWordRange; 227 | 228 | 229 | //////////////////////////////////////////////////////////////////////////////// 230 | // The following methods are available to plugin developers in Coda 1.6.3 and // 231 | // later. // 232 | //////////////////////////////////////////////////////////////////////////////// 233 | 234 | // Returns the URL of the site if specified (nil if unspecified in 235 | // the site or site is not loaded) 236 | 237 | - (NSString*)siteURL; 238 | 239 | 240 | // Returns the local URL of the site if specified (nil if unspecified in 241 | // the site or site is not loaded) 242 | 243 | - (NSString*)siteLocalURL; 244 | 245 | 246 | // Returns the root remote path of the site if specified (nil if unspecified in 247 | // the site or site is not loaded) 248 | 249 | - (NSString*)siteRemotePath; 250 | 251 | 252 | // Returns the nickname of the site if specified (nil if site is not loaded) 253 | 254 | - (NSString*)siteNickname; 255 | 256 | 257 | //////////////////////////////////////////////////////////////////////////////// 258 | // The following methods are available to plugin developers in Coda 2.0 and // 259 | // later. // 260 | //////////////////////////////////////////////////////////////////////////////// 261 | 262 | // Moves insertion point to specified line and column, scrolling to visible if needed 263 | 264 | - (void)goToLine:(NSInteger)line column:(NSInteger)column; 265 | 266 | 267 | // Remote URL for file given the site configuration 268 | 269 | - (NSString*)remoteURL; 270 | 271 | 272 | // Text encoding of the file 273 | 274 | - (NSStringEncoding)encoding; 275 | 276 | @end 277 | 278 | //////////////////////////////////////////////////////////////////////////////// 279 | // The following protocol is available to plugin developers in Coda 2.0.1 // 280 | // and later: // 281 | //////////////////////////////////////////////////////////////////////////////// 282 | 283 | @protocol CodaPlugInBundle 284 | 285 | @required 286 | 287 | // @abstract The unique identifier for the bundle 288 | @property (copy, readonly) NSString *bundleIdentifier; 289 | 290 | // @abstract The URL of the bundle on disk 291 | @property (copy, readonly) NSURL *bundleURL; 292 | 293 | // @abstract The path of the bundle on disk 294 | @property (copy, readonly) NSString *bundlePath; 295 | 296 | 297 | // @abstract The principal class of the bundle 298 | @property (readonly) Class principalClass; 299 | 300 | 301 | // The (unlocalized) info dictionary 302 | @property (copy, readonly) NSDictionary *infoDictionary; 303 | 304 | // The localized info dictionary 305 | @property (copy, readonly) NSDictionary *localizedInfoDictionary; 306 | 307 | // Gets the (localized, when possible) value of an info dictionary key 308 | - (id)objectForInfoDictionaryKey:(NSString *)key; 309 | 310 | 311 | // The URL of the bundle's executable file 312 | @property (copy, readonly) NSURL *executableURL; 313 | 314 | // The path of the bundle's executable file 315 | @property (copy, readonly) NSString *executablePath; 316 | 317 | // An array of numbers indicating the architecture types supported by the bundle’s executable 318 | @property (copy, readonly) NSArray *executableArchitectures; 319 | 320 | // Gets the URL for an auxiliary executable in the bundle 321 | - (NSURL *)URLForAuxiliaryExecutable:(NSString *)executableName; 322 | 323 | // Gets the path for an auxiliary executable in the bundle 324 | - (NSString *)pathForAuxiliaryExecutable:(NSString *)executableName; 325 | 326 | 327 | // @abstract The URL of the bundle's resource directory 328 | @property (copy, readonly) NSURL *resourceURL; 329 | 330 | // The path of the bundle's resource directory 331 | @property (copy, readonly) NSString *resourcePath; 332 | 333 | // Gets the URL for a bundle resource 334 | - (NSURL *)URLForResource:(NSString *)name withExtension:(NSString *)extension; 335 | 336 | // Gets the URL for a bundle resource located within a bundle subdirectory 337 | - (NSURL *)URLForResource:(NSString *)name withExtension:(NSString *)extension subdirectory:(NSString *)subpath; 338 | 339 | // Gets the URL for a bundle resource located within a bundle subdirectory and localization 340 | - (NSURL *)URLForResource:(NSString *)name withExtension:(NSString *)extension subdirectory:(NSString *)subpath localization:(NSString *)localizationName; 341 | 342 | // Gets the URL for a bundle resource 343 | - (NSString *)pathForResource:(NSString *)name ofType:(NSString *)extension; 344 | 345 | // Gets the URL for a bundle resource located within a bundle subdirectory 346 | - (NSString *)pathForResource:(NSString *)name ofType:(NSString *)extension inDirectory:(NSString *)subpath; 347 | 348 | // Gets the URL for a bundle resource located within a bundle subdirectory and localization 349 | - (NSString *)pathForResource:(NSString *)name ofType:(NSString *)extension inDirectory:(NSString *)subpath forLocalization:(NSString *)localizationName; 350 | 351 | 352 | // @abstract A list of all the localizations contained within the receiver's bundle 353 | @property (copy, readonly) NSArray *localizations; 354 | 355 | // A list of the preferred localizations contained within the receiver's bundle 356 | @property (copy, readonly) NSArray *preferredLocalizations; 357 | 358 | // The localization used to create the bundle 359 | @property (copy, readonly) NSArray *developmentLocalization; 360 | 361 | // Gets the localized string for a given key, value and table 362 | - (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName; 363 | 364 | @end 365 | 366 | 367 | //////////////////////////////////////////////////////////////////////////////// 368 | // Your plug-in's principal class must conform to this protocol // 369 | //////////////////////////////////////////////////////////////////////////////// 370 | 371 | @protocol CodaPlugIn 372 | 373 | // Return a name to display in the plug-ins menu 374 | 375 | - (NSString*)name; 376 | 377 | 378 | @optional 379 | 380 | // Default init'r for your plug-in's principal class. Passes a reference to your 381 | // bundle and the singleton instance of the CodaPlugInsController, may be called from a secondary thread 382 | // 383 | // Available in Coda API v6 (2.0.1) and later, this is the preferred init method 384 | // NOTE: CodaPlugInSupportedAPIVersion and/or CodaPlugInMinimumAPIVersion info.plist key must be set to 6 or higher 385 | 386 | - (id)initWithPlugInController:(CodaPlugInsController*)aController plugInBundle:(NSObject *)plugInBundle; 387 | 388 | 389 | // Default init'r for your plug-in's principal class. Passes a reference to your 390 | // bundle and the singleton instance of the CodaPlugInsController, may be called from a secondary thread 391 | // 392 | // NOTE: Deprecated in Coda API v6 (2.0.1) and later in favor of initWithPlugInController:pluginBundle: 393 | 394 | - (id)initWithPlugInController:(CodaPlugInsController*)aController bundle:(NSBundle*)yourBundle DEPRECATED_ATTRIBUTE; 395 | 396 | 397 | // Called before the text view will be saved to disk 398 | 399 | - (void)textViewWillSave:(CodaTextView*)textView; 400 | 401 | 402 | // Called when a text view is focused/opened 403 | // NOTE: this method is time sensitive and should return quickly 404 | 405 | - (void)textViewDidFocus:(CodaTextView*)textView; 406 | 407 | 408 | // Gives the plugin a chance to modify files just before publishing, output is the path to the file to publish. 409 | // WARNING: this method will be called on a non-main thread 410 | 411 | - (NSString*)willPublishFileAtPath:(NSString*)inputPath; 412 | 413 | @end 414 | 415 | 416 | -------------------------------------------------------------------------------- /CodeKitCodaPlugin/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 3.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 19 23 | CodaAuthorString 24 | Bryan D K Jones 25 | CodaDescriptionString 26 | Automate Coda+CodeKit workflows 27 | CodaPlugInMinimumAPIVersion 28 | 6 29 | CodaPlugInSupportedAPIVersion 30 | 6 31 | LSMinimumSystemVersion 32 | ${MACOSX_DEPLOYMENT_TARGET} 33 | NSHumanReadableCopyright 34 | Copyright © 2017 Incident 57, Inc. Released under the MIT License. 35 | NSPrincipalClass 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /CodeKitCodaPlugin/VDKCodeKitCodaPlugin.h: -------------------------------------------------------------------------------- 1 | // 2 | // VDKCodeKitCodaPlugin.h 3 | // CodeKitCodaPlugin 4 | // 5 | // Created by Bryan Jones on 6 Oct 14. 6 | // Released under the MIT License. 7 | // 8 | 9 | #import 10 | #import "CodaPlugInsController.h" 11 | 12 | @class CodaPlugInsController; 13 | 14 | 15 | @interface VDKCodeKitCodaPlugin : NSObject 16 | { 17 | CodaPlugInsController *_pluginsController; 18 | 19 | // 20 | // Used to rate-limit. If Coda is in the background and user brings it to the front by clicking a different file in the menubar, two -textViewDidFocus events 21 | // are sent to us virtually simultaneously. Keep paths we notify CodeKit about in this set for 5 seconds so we don't rapid-fire AppleScript events for the same path. 22 | // 23 | NSMutableSet *_recentlyHandledPaths; 24 | } 25 | 26 | @property (readonly, atomic) NSMutableSet *recentlyHandledPaths; 27 | 28 | @end 29 | 30 | 31 | // 32 | // Forward Declarations 33 | // 34 | BOOL isPathAChildOfPath(NSString *proposedChildPath, NSString *proposedParentPath); -------------------------------------------------------------------------------- /CodeKitCodaPlugin/VDKCodeKitCodaPlugin.m: -------------------------------------------------------------------------------- 1 | // 2 | // VDKCodeKitCodaPlugin.m 3 | // CodeKitCodaPlugin 4 | // 5 | // Created by Bryan Jones on 6 Oct 14. 6 | // Released under the MIT License. 7 | 8 | #import "VDKCodeKitCodaPlugin.h" 9 | 10 | 11 | 12 | BOOL isPathAChildOfPath(NSString *proposedChildPath, NSString *proposedParentPath) 13 | { 14 | // Clean both paths by stripping any trailing slashes, etc. 15 | NSString *cleanParent = [proposedParentPath stringByStandardizingPath]; 16 | NSString *cleanChild = [proposedChildPath stringByStandardizingPath]; 17 | 18 | NSRange commonPrefixRange = [cleanChild rangeOfString:cleanParent options:NSCaseInsensitiveSearch|NSAnchoredSearch]; 19 | 20 | // Does path2 contain path1? (This should be faster than tokenizing the strings on every / and comparing components one by one. 21 | if (commonPrefixRange.location != NSNotFound) 22 | { 23 | // The child path contains the parent, but it could be a false positive. Example: 24 | // Parent: /Users/john/desktop/project 25 | // Child: /Users/john/desktop/project-other/file.js 26 | // If child is truly part of parent, then the character AFTER the commonPrefixRange *must* be a slash. 27 | NSUInteger nextCharacterLocation = commonPrefixRange.location + commonPrefixRange.length; 28 | if (nextCharacterLocation < cleanChild.length) 29 | { 30 | unichar character = [cleanChild characterAtIndex:nextCharacterLocation]; 31 | return (character == '/') ? YES : NO; 32 | } 33 | else 34 | { 35 | // Can't possibly be a child. Proposed child and parent must be equal. 36 | return NO; 37 | } 38 | } 39 | 40 | return NO; 41 | } 42 | 43 | 44 | 45 | 46 | 47 | @implementation VDKCodeKitCodaPlugin 48 | 49 | 50 | - (id) initWithPlugInController:(CodaPlugInsController *)aController plugInBundle:(NSObject *)plugInBundle 51 | { 52 | self = [super init]; 53 | if (self) 54 | { 55 | _pluginsController = aController; 56 | _recentlyHandledPaths = [[NSMutableSet alloc] initWithCapacity:20]; 57 | } 58 | return self; 59 | } 60 | 61 | 62 | - (NSString *) name 63 | { 64 | return @"CodeKit Launcher"; 65 | } 66 | 67 | 68 | - (void) textViewDidFocus:(CodaTextView *)textView 69 | { 70 | // 71 | // Discussion: 72 | // Coda 2.5b19 and below have a bug where immediately calling [textView path] returns null in some cases. 73 | // As a workaround, we'll let this method return and get to the next iteration of the runloop so that the 74 | // 'path' property is set correctly. I filed this with Panic at https://hive.panic.com/issues/26411 and Wade 75 | // used this approach to fix this issue for Coda 2.5+. I want to cover older versions of Coda 2, so I've left the 76 | // first 'dispatch_after' call in place. Eventually, it should be removed when no one uses earlier versions of Coda. 77 | // 78 | 79 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0), dispatch_get_main_queue(), 80 | ^{ 81 | NSString *textViewPath = textView.path; 82 | if (textViewPath) 83 | { 84 | NSString *sitePath = textView.siteLocalPath; // Must be called on main thread according to Coda API 85 | 86 | // Get off the main thread so Coda's UI stays responsive 87 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), 88 | ^{ 89 | NSFileManager *manager = [NSFileManager defaultManager]; 90 | BOOL isFolder = YES; 91 | NSString *projectPath = nil; 92 | 93 | if (sitePath) 94 | { 95 | // If we have a site open in Coda, then click any random file in the Finder that is NOT part of that site, Coda still opens it in the same 96 | // textView and considers it part of the site, which it's really not. We want to make sure this file isn't part of a CodeKit Project that 97 | // is NOT the local site path folder; otherwise we'd miss adding the right project folder to CodeKit. 98 | if (isPathAChildOfPath(textViewPath, sitePath)) 99 | { 100 | if (([manager fileExistsAtPath:[sitePath stringByAppendingPathComponent:@"config.codekit3"] isDirectory:&isFolder] && !isFolder) 101 | || ([manager fileExistsAtPath:[sitePath stringByAppendingPathComponent:@".config.codekit3"] isDirectory:&isFolder] && !isFolder) 102 | || ([manager fileExistsAtPath:[sitePath stringByAppendingPathComponent:@"config.codekit"] isDirectory:&isFolder] && !isFolder)) 103 | { 104 | projectPath = sitePath; 105 | } 106 | } 107 | } 108 | 109 | 110 | if (!projectPath) 111 | { 112 | // Walk each folder up from this file and find the first one that has a 'config.codekit3' file. 113 | NSString *folderToCheck = [textViewPath stringByDeletingLastPathComponent]; 114 | NSUInteger compsCount = [[folderToCheck pathComponents] count]; 115 | 116 | for (NSUInteger i=0; i