From 2f66458ac71e151b837d5231b96fa35b48d395e5 Mon Sep 17 00:00:00 2001 From: Jakob Mygind Date: Fri, 7 Jul 2017 16:49:52 +0200 Subject: [PATCH 1/6] Updated to Swift4 --- .gitignore | 3 +- Model Boiler.xcodeproj/project.pbxproj | 22 ++++++++-- .../xcschemes/Model Boiler.xcscheme | 4 +- .../Classes/Helpers/KeyCommandManager.swift | 12 +++--- .../Classes/Helpers/UpdateManager.swift | 2 +- Model Boiler/Classes/Service/Service.swift | 12 +++--- Model Boiler/Classes/Support/Extensions.swift | 4 +- .../PreferencesController.swift | 8 ++-- .../User Interface/StatusBarController.swift | 42 +++++++++---------- 9 files changed, 63 insertions(+), 46 deletions(-) diff --git a/.gitignore b/.gitignore index 56ce8f3..f1b49d6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.DS_Store # Xcode # # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore @@ -50,7 +51,7 @@ Carthage/ # fastlane # -# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the # screenshots whenever they are needed. # For more information about the recommended setup visit: # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md diff --git a/Model Boiler.xcodeproj/project.pbxproj b/Model Boiler.xcodeproj/project.pbxproj index 7120c8d..790e382 100644 --- a/Model Boiler.xcodeproj/project.pbxproj +++ b/Model Boiler.xcodeproj/project.pbxproj @@ -268,7 +268,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0720; - LastUpgradeCheck = 0810; + LastUpgradeCheck = 0900; ORGANIZATIONNAME = Nodes; TargetAttributes = { 01ECF0FF1C43FDD700C23397 = { @@ -277,7 +277,7 @@ }; 3092679B1B77EBE700DFE5A1 = { CreatedOnToolsVersion = 7.0; - LastSwiftMigration = 0810; + LastSwiftMigration = 0900; }; }; }; @@ -382,14 +382,20 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -436,7 +442,8 @@ MACOSX_DEPLOYMENT_TARGET = 10.11; PRODUCT_BUNDLE_IDENTIFIER = "com.nodes.Model-Boiler"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -448,14 +455,20 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -499,7 +512,8 @@ MACOSX_DEPLOYMENT_TARGET = 10.11; PRODUCT_BUNDLE_IDENTIFIER = "com.nodes.Model-Boiler"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.0; }; name = Release; }; diff --git a/Model Boiler.xcodeproj/xcshareddata/xcschemes/Model Boiler.xcscheme b/Model Boiler.xcodeproj/xcshareddata/xcschemes/Model Boiler.xcscheme index c212904..9618615 100644 --- a/Model Boiler.xcodeproj/xcshareddata/xcschemes/Model Boiler.xcscheme +++ b/Model Boiler.xcodeproj/xcshareddata/xcschemes/Model Boiler.xcscheme @@ -1,6 +1,6 @@ @@ -45,6 +46,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/Model Boiler/Classes/Helpers/KeyCommandManager.swift b/Model Boiler/Classes/Helpers/KeyCommandManager.swift index afe5e49..a85c006 100644 --- a/Model Boiler/Classes/Helpers/KeyCommandManager.swift +++ b/Model Boiler/Classes/Helpers/KeyCommandManager.swift @@ -14,7 +14,7 @@ struct KeyCommandManager { static func setDefaultKeyCommand() { let key = "ยง" - let mask = NSEventModifierFlags.command + let mask = NSEvent.ModifierFlags.command do { try KeyCommandManager.updateKeyCommand(key, modifierMask: mask) @@ -25,7 +25,7 @@ struct KeyCommandManager { // MARK: - Key Command - - static func currentKeyCommand() -> (command: String, modifierMask: NSEventModifierFlags)? { + static func currentKeyCommand() -> (command: String, modifierMask: NSEvent.ModifierFlags)? { if let keyCommandString = UserDefaults.standard.string(forKey: settingsKey) { return keyCommandForString(keyCommandString) } @@ -33,7 +33,7 @@ struct KeyCommandManager { return nil } - static func updateKeyCommand(_ command: String, modifierMask: NSEventModifierFlags) throws { + static func updateKeyCommand(_ command: String, modifierMask: NSEvent.ModifierFlags) throws { let bundle = Bundle.main guard let appServices = (bundle.infoDictionary?["NSServices"] as AnyObject).firstObject as? [String: AnyObject], @@ -81,7 +81,7 @@ struct KeyCommandManager { // MARK: - Helpers - - static func stringForKeyCommand(_ command: String, modifierMask: NSEventModifierFlags) -> String { + static func stringForKeyCommand(_ command: String, modifierMask: NSEvent.ModifierFlags) -> String { var key = "" if modifierMask.contains(.command) { key += "@" } @@ -92,8 +92,8 @@ struct KeyCommandManager { } - static func keyCommandForString(_ string: String) -> (command: String, modifierMask: NSEventModifierFlags) { - var returnValue = (command: "", modifierMask: NSEventModifierFlags(rawValue: 0)) + static func keyCommandForString(_ string: String) -> (command: String, modifierMask: NSEvent.ModifierFlags) { + var returnValue = (command: "", modifierMask: NSEvent.ModifierFlags(rawValue: 0)) if string.characters.count == 0 { return returnValue } switch string.characters.first! { diff --git a/Model Boiler/Classes/Helpers/UpdateManager.swift b/Model Boiler/Classes/Helpers/UpdateManager.swift index 5758b6b..30387a3 100644 --- a/Model Boiler/Classes/Helpers/UpdateManager.swift +++ b/Model Boiler/Classes/Helpers/UpdateManager.swift @@ -39,7 +39,7 @@ open class UpdateManager: NSObject { updateTimer = nil } - func autoUpdateTimerFired() { + @objc func autoUpdateTimerFired() { checkForUpdates(showAlerts: false) } diff --git a/Model Boiler/Classes/Service/Service.swift b/Model Boiler/Classes/Service/Service.swift index fe3c514..561f73f 100644 --- a/Model Boiler/Classes/Service/Service.swift +++ b/Model Boiler/Classes/Service/Service.swift @@ -12,14 +12,14 @@ import Cocoa struct Service { - static let errorSound = NSSound(named: "Basso") - static let successSound = NSSound(named: "Pop") + static let errorSound = NSSound(named: NSSound.Name(rawValue: "Basso")) + static let successSound = NSSound(named: NSSound.Name(rawValue: "Pop")) // MARK: - Main Function - - static func generate(_ pasteboard: NSPasteboard = NSPasteboard.general()) { + static func generate(_ pasteboard: NSPasteboard = NSPasteboard.general) { - guard let source = pasteboard.string(forType: NSPasteboardTypeString), (pasteboard.pasteboardItems?.count == 1) else { + guard let source = pasteboard.string(forType: NSPasteboard.PasteboardType.string), (pasteboard.pasteboardItems?.count == 1) else { NSUserNotification.display(title: "No text selected", andMessage: "Nothing was found in the pasteboard.") playSound(Service.errorSound) @@ -41,8 +41,8 @@ struct Service { playSound(Service.successSound) // Copy back to pasteboard - NSPasteboard.general().declareTypes([NSPasteboardTypeString], owner: nil) - NSPasteboard.general().setString(code, forType: NSPasteboardTypeString) + NSPasteboard.general.declareTypes([NSPasteboard.PasteboardType.string], owner: nil) + NSPasteboard.general.setString(code, forType: NSPasteboard.PasteboardType.string) // Success, show notification NSUserNotification.display( diff --git a/Model Boiler/Classes/Support/Extensions.swift b/Model Boiler/Classes/Support/Extensions.swift index f4367c7..2e58ca3 100644 --- a/Model Boiler/Classes/Support/Extensions.swift +++ b/Model Boiler/Classes/Support/Extensions.swift @@ -27,7 +27,7 @@ extension NSApplication { // Terminate all previously running apps with same bundle identifier let runningApps = NSRunningApplication.runningApplications(withBundleIdentifier: identifier) for runningApp in runningApps { - if !runningApp.isEqual(NSRunningApplication.current()) { + if !runningApp.isEqual(NSRunningApplication.current) { runningApp.terminate() } } @@ -43,7 +43,7 @@ extension NSApplication { alert.informativeText = "The app should be in your Applications folder in order to work properly." let response = alert.runModal() - if response == NSAlertFirstButtonReturn { + if response == NSApplication.ModalResponse.alertFirstButtonReturn { do { try moveToApplicationsIfNecessary() restart() diff --git a/Model Boiler/Classes/User Interface/PreferencesController.swift b/Model Boiler/Classes/User Interface/PreferencesController.swift index fc4f0b1..56b5c65 100644 --- a/Model Boiler/Classes/User Interface/PreferencesController.swift +++ b/Model Boiler/Classes/User Interface/PreferencesController.swift @@ -15,7 +15,7 @@ class PreferencesController: NSWindowController { @IBOutlet var nativeDictionariesSwitch: NSButton! class func newFromNib() -> PreferencesController { - return PreferencesController(windowNibName: "Preferences") + return PreferencesController(windowNibName: NSNib.Name(rawValue: "Preferences")) } override func windowDidLoad() { @@ -42,7 +42,7 @@ class PreferencesController: NSWindowController { func updateServiceKeyCommand() { let keyCode = self.shortcutView.shortcutValue.keyCodeString - let modifier = NSEventModifierFlags(rawValue: self.shortcutView.shortcutValue.modifierFlags) + let modifier = NSEvent.ModifierFlags(rawValue: self.shortcutView.shortcutValue.modifierFlags) guard let code = keyCode else { loadSavedKeyCommand() return @@ -58,12 +58,12 @@ class PreferencesController: NSWindowController { @IBAction func switchChanged(_ sender: NSButton) { if sender == nativeDictionariesSwitch { - let state = (sender.state == NSOnState) + let state = (sender.state == NSControl.StateValue.onState) SettingsManager.setSetting(.UseNativeDictionaries, enabled: state) } } func loadSettings() { - nativeDictionariesSwitch.state = SettingsManager.isSettingEnabled(.UseNativeDictionaries) ? NSOnState : NSOffState + nativeDictionariesSwitch.state = SettingsManager.isSettingEnabled(.UseNativeDictionaries) ? NSControl.StateValue.onState : NSControl.StateValue.offState } } diff --git a/Model Boiler/Classes/User Interface/StatusBarController.swift b/Model Boiler/Classes/User Interface/StatusBarController.swift index 27b21ff..72073f9 100644 --- a/Model Boiler/Classes/User Interface/StatusBarController.swift +++ b/Model Boiler/Classes/User Interface/StatusBarController.swift @@ -25,7 +25,7 @@ class StatusBarController: NSObject, NSMenuDelegate { let statusMenu = NSMenu() let optionsMenu = NSMenu() - let statusItem = NSStatusBar.system().statusItem(withLength: NSVariableStatusItemLength) + let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) var preferencesController: PreferencesController? @@ -40,7 +40,7 @@ class StatusBarController: NSObject, NSMenuDelegate { func setupStatusItems() { // Setup image and menu - statusItem.button?.image = NSImage(named: "bat") + statusItem.button?.image = NSImage(named: NSImage.Name(rawValue: "bat")) statusItem.menu = statusMenu // Title item @@ -69,21 +69,21 @@ class StatusBarController: NSObject, NSMenuDelegate { // Camel case conversion item let camelCaseItem = NSMenuItem(title: "Map camelCase -> underscore_notation", action: #selector(toggleCamelCaseConversion), keyEquivalent: "") - camelCaseItem.state = SettingsManager.isSettingEnabled(.NoCamelCaseConversion) ? NSOffState : NSOnState + camelCaseItem.state = SettingsManager.isSettingEnabled(.NoCamelCaseConversion) ? NSControl.StateValue.offState : NSControl.StateValue.onState camelCaseItem.target = self camelCaseItem.tag = MenuItem.camelCaseConversion.rawValue optionsMenu.addItem(camelCaseItem) // Sound enabled item let soundItem = NSMenuItem(title: "Audio enabled", action: #selector(StatusBarController.toggleSoundEnabled), keyEquivalent: "") - soundItem.state = SettingsManager.isSettingEnabled(.SoundEnabled) ? NSOnState : NSOffState + soundItem.state = SettingsManager.isSettingEnabled(.SoundEnabled) ? NSControl.StateValue.onState : NSControl.StateValue.offState soundItem.target = self soundItem.tag = MenuItem.soundEnabled.rawValue optionsMenu.addItem(soundItem) // Generate Initializer only let initializerOnlyItem = NSMenuItem(title: "Only generate initializer (useful for use with Realm/CoreData)", action: #selector(toggleOnlyCreateInitializers), keyEquivalent: "") - initializerOnlyItem.state = SettingsManager.isSettingEnabled(.OnlyCreateInitializer) ? NSOnState : NSOffState + initializerOnlyItem.state = SettingsManager.isSettingEnabled(.OnlyCreateInitializer) ? NSControl.StateValue.onState : NSControl.StateValue.offState initializerOnlyItem.target = self initializerOnlyItem.tag = MenuItem.onlyCreateInitializer.rawValue optionsMenu.addItem(initializerOnlyItem) @@ -126,12 +126,12 @@ class StatusBarController: NSObject, NSMenuDelegate { case .generate: if let keyCommand = KeyCommandManager.currentKeyCommand() { menuItem.keyEquivalent = keyCommand.command - menuItem.keyEquivalentModifierMask = NSEventModifierFlags(rawValue: UInt(Int(keyCommand.modifierMask.rawValue))) + menuItem.keyEquivalentModifierMask = NSEvent.ModifierFlags(rawValue: UInt(Int(keyCommand.modifierMask.rawValue))) } case .camelCaseConversion: - menuItem.state = SettingsManager.isSettingEnabled(.NoCamelCaseConversion) ? NSOffState : NSOnState + menuItem.state = SettingsManager.isSettingEnabled(.NoCamelCaseConversion) ? NSControl.StateValue.offState : NSControl.StateValue.onState case .soundEnabled: - menuItem.state = SettingsManager.isSettingEnabled(.SoundEnabled) ? NSOnState : NSOffState + menuItem.state = SettingsManager.isSettingEnabled(.SoundEnabled) ? NSControl.StateValue.onState : NSControl.StateValue.offState default: break } } @@ -139,53 +139,53 @@ class StatusBarController: NSObject, NSMenuDelegate { // MARK: - Callbacks - - func generate(_ pboard:NSPasteboard!, userData:NSString!, error:AutoreleasingUnsafeMutablePointer) -> Void { + @objc func generate(_ pboard:NSPasteboard!, userData:NSString!, error:AutoreleasingUnsafeMutablePointer) -> Void { Service.generate(pboard) } - func updatePressed() { + @objc func updatePressed() { UpdateManager.sharedInstance.checkForUpdates() } - func toggleSoundEnabled() { + @objc func toggleSoundEnabled() { let newState = !SettingsManager.isSettingEnabled(.SoundEnabled) SettingsManager.setSetting(.SoundEnabled, enabled: newState) if let soundItem = optionsMenu.item(withTag: MenuItem.soundEnabled.rawValue) { - soundItem.state = newState == true ? NSOnState : NSOffState + soundItem.state = newState == true ? NSControl.StateValue.onState : NSControl.StateValue.offState } } - func toggleCamelCaseConversion() { + @objc func toggleCamelCaseConversion() { let newState = !SettingsManager.isSettingEnabled(.NoCamelCaseConversion) SettingsManager.setSetting(.NoCamelCaseConversion, enabled: newState) if let camelCaseItem = optionsMenu.item(withTag: MenuItem.camelCaseConversion.rawValue) { - camelCaseItem.state = newState == true ? NSOffState : NSOnState + camelCaseItem.state = newState == true ? NSControl.StateValue.offState : NSControl.StateValue.onState } } - func toggleOnlyCreateInitializers() { + @objc func toggleOnlyCreateInitializers() { let newState = !SettingsManager.isSettingEnabled(.OnlyCreateInitializer) SettingsManager.setSetting(.OnlyCreateInitializer, enabled: newState) if let camelCaseItem = optionsMenu.item(withTag: MenuItem.onlyCreateInitializer.rawValue) { - camelCaseItem.state = newState == true ? NSOnState : NSOffState + camelCaseItem.state = newState == true ? NSControl.StateValue.onState : NSControl.StateValue.offState } } - func showSettings() { + @objc func showSettings() { preferencesController = PreferencesController.newFromNib() preferencesController?.window?.makeKeyFrontAndCenter(self) NSApp.activate(ignoringOtherApps: true) } - func restartPressed() { - NSApplication.shared().restart() + @objc func restartPressed() { + NSApplication.shared.restart() } - func quitPressed() { - NSApplication.shared().terminate(self) + @objc func quitPressed() { + NSApplication.shared.terminate(self) } // MARK: - NSMenu Delegate - From 6d66ef3bb06cf0f457020e2741c8461262565afc Mon Sep 17 00:00:00 2001 From: Jakob Mygind Date: Fri, 7 Jul 2017 15:55:36 +0200 Subject: [PATCH 2/6] Support for embedded Object declarations --- .../contents.xcworkspacedata | 2 +- Model Boiler/Classes/AppDelegate.swift | 2 +- .../Classes/Helpers/UpdateManager.swift | 18 +- Model Boiler/Classes/Service/Service.swift | 196 +++++++++++++++++- Model Boiler/Classes/Support/Extensions.swift | 18 +- .../PreferencesController.swift | 8 +- .../User Interface/StatusBarController.swift | 6 +- 7 files changed, 212 insertions(+), 38 deletions(-) diff --git a/Model Boiler.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Model Boiler.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 06d5927..919434a 100644 --- a/Model Boiler.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/Model Boiler.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/Model Boiler/Classes/AppDelegate.swift b/Model Boiler/Classes/AppDelegate.swift index 29f0f82..11d3888 100644 --- a/Model Boiler/Classes/AppDelegate.swift +++ b/Model Boiler/Classes/AppDelegate.swift @@ -11,7 +11,7 @@ import Cocoa @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { - let mainController = StatusBarController() + @objc let mainController = StatusBarController() func applicationDidFinishLaunching(_ aNotification: Notification) { // Stop already running instances diff --git a/Model Boiler/Classes/Helpers/UpdateManager.swift b/Model Boiler/Classes/Helpers/UpdateManager.swift index 30387a3..986feb9 100644 --- a/Model Boiler/Classes/Helpers/UpdateManager.swift +++ b/Model Boiler/Classes/Helpers/UpdateManager.swift @@ -12,29 +12,29 @@ import ZipArchive open class UpdateManager: NSObject { - static let sharedInstance = UpdateManager() + @objc static let sharedInstance = UpdateManager() - let repoPath = "repos/nodes-ios/ModelBoiler/" - let downloadName = "Model.Boiler.app.zip" + @objc let repoPath = "repos/nodes-ios/ModelBoiler/" + @objc let downloadName = "Model.Boiler.app.zip" - var updateTimer: Timer? + @objc var updateTimer: Timer? fileprivate override init() { } - open static func start() { + @objc open static func start() { sharedInstance.start() } - open func start() { + @objc open func start() { // Schedule update timer for every hour checkForUpdates() updateTimer = Timer.scheduledTimer(timeInterval: 3600, target: self, selector: #selector(UpdateManager.autoUpdateTimerFired), userInfo: nil, repeats: true) } - open static func stop() { + @objc open static func stop() { sharedInstance.stop() } - open func stop() { + @objc open func stop() { updateTimer?.invalidate() updateTimer = nil } @@ -43,7 +43,7 @@ open class UpdateManager: NSObject { checkForUpdates(showAlerts: false) } - open func checkForUpdates(showAlerts: Bool = true) { + @objc open func checkForUpdates(showAlerts: Bool = true) { guard let request = URLRequest.requestForGithubWithPath(repoPath + "releases") else { return } diff --git a/Model Boiler/Classes/Service/Service.swift b/Model Boiler/Classes/Service/Service.swift index 561f73f..cd579e7 100644 --- a/Model Boiler/Classes/Service/Service.swift +++ b/Model Boiler/Classes/Service/Service.swift @@ -18,14 +18,14 @@ struct Service { // MARK: - Main Function - static func generate(_ pasteboard: NSPasteboard = NSPasteboard.general) { - + guard let source = pasteboard.string(forType: NSPasteboard.PasteboardType.string), (pasteboard.pasteboardItems?.count == 1) else { NSUserNotification.display(title: "No text selected", - andMessage: "Nothing was found in the pasteboard.") + andMessage: "Nothing was found in the pasteboard.") playSound(Service.errorSound) return } - + // Setup the model generator var generatorSettings = ModelGeneratorSettings() generatorSettings.moduleName = nil @@ -35,11 +35,12 @@ struct Service { do { // Try to generate the code - let code = try ModelGenerator.modelCode(fromSourceCode: source, withSettings: generatorSettings) - + guard let bodies = try modelBodies(fromSource: source, generatorSettings: generatorSettings) else { throw ModelParserError.NoModelNameFound } + let code = extensionCode(fromBodies: bodies) + // Play success sound playSound(Service.successSound) - + // Copy back to pasteboard NSPasteboard.general.declareTypes([NSPasteboard.PasteboardType.string], owner: nil) NSPasteboard.general.setString(code, forType: NSPasteboard.PasteboardType.string) @@ -49,22 +50,195 @@ struct Service { title: "Code generated", andMessage: "The code has been copied to the clipboard.") } catch { - + // Show error notification NSUserNotification.display( title: "Code generation failed", andMessage: "Error: \((error as? ModelGeneratorErrorType)?.description() ?? "Unknown error.")") - + // Play error sound playSound(Service.errorSound) } } - - // MARK: - Helpers - - + + static func extensionCode(fromBodies bodies: [String]) -> String { + var bodiesMutating = bodies + var retVal = "" + if !bodies.isEmpty { + retVal = bodiesMutating.removeFirst() + } + for body in bodiesMutating { + retVal.append("\n\n\(body)") + } + + return retVal + } + + static func modelBodies(fromSource source: String, generatorSettings: ModelGeneratorSettings) throws -> [String]? { + if let codes = try codeStrings(fromSourceCode: source) { + var retVal = [String]() + var outerModelPrefix = "" + for (index, code) in codes.enumerated() { + var codeToParse = code + if index == 0, let outerName = modelName(fromSourceCode: codeToParse)?.0 { + outerModelPrefix = outerName + "." + } else if let range = modelName(fromSourceCode: codeToParse)?.1 { + codeToParse = "" + for (index, character) in code.enumerated() { + if index == range.lowerBound { + codeToParse.append(" \(outerModelPrefix)") + } + codeToParse.append(character) + } + //For some reason this doesn't work in current Swift4??? + // codeToParse = codeToParse.insert(contentsOf: outerModelPrefix, at: codeToParse.startIndex(range.length)) + } + let newCode = try ModelGenerator.modelCode(fromSourceCode: codeToParse, withSettings: generatorSettings) + retVal.append(newCode) + } + return retVal + } + return nil + } + + static func modelName(fromSourceCode code: String) -> (String, NSRange)? { + let range = NSMakeRange(0, code.characters.count) + let match = modelNameRegex?.firstMatch(in: code, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: range) + + // If we found model name + if let match = match { + return ((code as NSString).substring(with: match.range), match.range) + } + + return nil + } + + static func missingEndBrackets(inCode code: String) -> String { + let startBrackets = code.components(separatedBy: "{").count - 1 + let endBrackets = code.components(separatedBy: "}").count - 1 + let difference = startBrackets - endBrackets + + var addition = "" + if difference > 0 { + + for _ in 0.. [String]? { + let range = NSMakeRange(0, code.characters.count) + + // Check if struct + let structMatches = structRegex?.numberOfMatches(in: code, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: range) + if let matches = structMatches, + matches == 1 { + + return [code + missingEndBrackets(inCode: code)] + + } else if let matches = structMatches, + matches > 1, + let allMatches = structRegex?.matches(in: code, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: range) { + let rangeToRemove = NSMakeRange(0, allMatches[1].range.location) + let string = (code as NSString).substring(to: rangeToRemove.length) as String + let restRange = NSMakeRange(rangeToRemove.length, range.length - rangeToRemove.length) + let restCode = (code as NSString).substring(with: restRange) as String + do { + + if let strings = try codeStrings(fromSourceCode: restCode) { + return [string + missingEndBrackets(inCode: string)] + strings + } + } + } + + //Check if final class + + let finalClassMatches = finalClassRegex?.numberOfMatches(in: code, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: range) + if let matches = finalClassMatches, + matches == 1 { + + return [code + missingEndBrackets(inCode: code)] + + } else if let matches = finalClassMatches, + matches > 1, + let allMatches = finalClassRegex?.matches(in: code, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: range) { + let rangeToRemove = NSMakeRange(0, allMatches[1].range.location) + let string = (code as NSString).substring(to: rangeToRemove.length) as String + let restRange = NSMakeRange(rangeToRemove.length, range.length - rangeToRemove.length) + let restCode = (code as NSString).substring(with: restRange) as String + do { + + if let strings = try codeStrings(fromSourceCode: restCode) { + return [string + missingEndBrackets(inCode: string)] + strings + } + } + + } else if code.contains("class") { + throw ModelParserError.ClassShouldBeDeclaredAsFinal + } + + // If no struct or class was found + return nil + } + // + static func playSound(_ sound: NSSound?) { if !UserDefaults.standard.bool(forKey: "muteSound") { sound?.play() } } } +// Regular expression used for parsing +extension Service { + + static var modelBodyRegex: NSRegularExpression? { + do { + let regex = try NSRegularExpression( + pattern: "struct.*\\{(.*)\\}|class.*\\{(.*)\\}", + options: [.dotMatchesLineSeparators]) + return regex + } catch { + print("Couldn't create model body regex.") + return nil + } + } + static var finalClassRegex: NSRegularExpression? { + do { + let regex = try NSRegularExpression( + pattern: "final.*class(?=.*\\{)", + options: NSRegularExpression.Options(rawValue: 0)) + return regex + } catch { + print("Couldn't create final class regex.") + return nil + } + } + + static var structRegex: NSRegularExpression? { + do { + let regex = try NSRegularExpression( + pattern: "struct(?=.*\\{)", + options: NSRegularExpression.Options(rawValue: 0)) + return regex + } catch { + print("Couldn't create struct regex.") + return nil + } + } + + static var modelNameRegex: NSRegularExpression? { + do { + let regex = try NSRegularExpression( + pattern: "\\S+(?=\\s*\\:)|\\S+(?=\\s*\\{)", + options: NSRegularExpression.Options(rawValue: 0)) + return regex + } catch { + print("Couldn't create model name regex.") + return nil + } + } +} + + diff --git a/Model Boiler/Classes/Support/Extensions.swift b/Model Boiler/Classes/Support/Extensions.swift index 2e58ca3..c36d32d 100644 --- a/Model Boiler/Classes/Support/Extensions.swift +++ b/Model Boiler/Classes/Support/Extensions.swift @@ -10,7 +10,7 @@ import Foundation import Cocoa extension NSUserNotification { - static func display(title: String, andMessage message: String) { + @objc static func display(title: String, andMessage message: String) { dispatch(background: false) { let notification = NSUserNotification() notification.title = title @@ -21,7 +21,7 @@ extension NSUserNotification { } extension NSApplication { - func terminateAlreadyRunningInstances() { + @objc func terminateAlreadyRunningInstances() { guard let identifier = Bundle.main.bundleIdentifier else { return } // Terminate all previously running apps with same bundle identifier @@ -33,7 +33,7 @@ extension NSApplication { } } - func verifyAppInstallLocation() { + @objc func verifyAppInstallLocation() { if !isInApplications() && !isInBrewsFolder() { let alert = NSAlert() @@ -60,7 +60,7 @@ extension NSApplication { } } - func isInApplications() -> Bool { + @objc func isInApplications() -> Bool { let sourcePath = Bundle.main.bundlePath let appFolders = NSSearchPathForDirectoriesInDomains(.applicationDirectory, .localDomainMask, true) @@ -73,13 +73,13 @@ extension NSApplication { return sourcePath == expectedPath } - func isInBrewsFolder() -> Bool { + @objc func isInBrewsFolder() -> Bool { let sourcePath = Bundle.main.bundlePath return sourcePath.contains("homebrew-cask/Caskroom") } - func moveToApplicationsIfNecessary() throws { + @objc func moveToApplicationsIfNecessary() throws { if isInApplications() || isInBrewsFolder() { return } let bundle = Bundle.main @@ -100,7 +100,7 @@ extension NSApplication { try fileManager.moveItem(atPath: sourcePath, toPath: expectedPath) } - func restart() { + @objc func restart() { let task = Process() task.launchPath = Bundle.main.path(forResource: "relaunch", ofType: nil)! task.arguments = [String(ProcessInfo.processInfo.processIdentifier)] @@ -121,7 +121,7 @@ extension URLRequest { } extension NSMenuItem { - static func separatorItemWithTag(_ tag: Int) -> NSMenuItem { + @objc static func separatorItemWithTag(_ tag: Int) -> NSMenuItem { let separatorItem = NSMenuItem.separator() separatorItem.tag = tag return separatorItem @@ -129,7 +129,7 @@ extension NSMenuItem { } extension NSWindow { - func makeKeyFrontAndCenter(_ sender: AnyObject?) { + @objc func makeKeyFrontAndCenter(_ sender: AnyObject?) { makeKeyAndOrderFront(sender) if let screen = self.screen { diff --git a/Model Boiler/Classes/User Interface/PreferencesController.swift b/Model Boiler/Classes/User Interface/PreferencesController.swift index 56b5c65..3364b22 100644 --- a/Model Boiler/Classes/User Interface/PreferencesController.swift +++ b/Model Boiler/Classes/User Interface/PreferencesController.swift @@ -13,7 +13,7 @@ class PreferencesController: NSWindowController { @IBOutlet var shortcutView: MASShortcutView! @IBOutlet var nativeDictionariesSwitch: NSButton! - + class func newFromNib() -> PreferencesController { return PreferencesController(windowNibName: NSNib.Name(rawValue: "Preferences")) } @@ -30,7 +30,7 @@ class PreferencesController: NSWindowController { } } - func loadSavedKeyCommand() { + @objc func loadSavedKeyCommand() { if let keyCommand = KeyCommandManager.currentKeyCommand() { let scalars = keyCommand.command.unicodeScalars let keyCode = UInt(scalars[scalars.startIndex].value) @@ -40,7 +40,7 @@ class PreferencesController: NSWindowController { } } - func updateServiceKeyCommand() { + @objc func updateServiceKeyCommand() { let keyCode = self.shortcutView.shortcutValue.keyCodeString let modifier = NSEvent.ModifierFlags(rawValue: self.shortcutView.shortcutValue.modifierFlags) guard let code = keyCode else { @@ -62,7 +62,7 @@ class PreferencesController: NSWindowController { SettingsManager.setSetting(.UseNativeDictionaries, enabled: state) } } - + func loadSettings() { nativeDictionariesSwitch.state = SettingsManager.isSettingEnabled(.UseNativeDictionaries) ? NSControl.StateValue.onState : NSControl.StateValue.offState } diff --git a/Model Boiler/Classes/User Interface/StatusBarController.swift b/Model Boiler/Classes/User Interface/StatusBarController.swift index 72073f9..d239ba4 100644 --- a/Model Boiler/Classes/User Interface/StatusBarController.swift +++ b/Model Boiler/Classes/User Interface/StatusBarController.swift @@ -27,7 +27,7 @@ class StatusBarController: NSObject, NSMenuDelegate { let optionsMenu = NSMenu() let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) - var preferencesController: PreferencesController? + @objc var preferencesController: PreferencesController? override init() { super.init() @@ -38,7 +38,7 @@ class StatusBarController: NSObject, NSMenuDelegate { updateMenuItems() } - func setupStatusItems() { + @objc func setupStatusItems() { // Setup image and menu statusItem.button?.image = NSImage(named: NSImage.Name(rawValue: "bat")) statusItem.menu = statusMenu @@ -119,7 +119,7 @@ class StatusBarController: NSObject, NSMenuDelegate { statusMenu.addItem(quitItem) } - func updateMenuItems() { + @objc func updateMenuItems() { for menuItem in statusMenu.items { guard let item = MenuItem(rawValue: menuItem.tag) else { continue } switch item { From a4b90541e3cba620c2e6084e7f7d2d615c11879f Mon Sep 17 00:00:00 2001 From: Jakob Mygind Date: Fri, 7 Jul 2017 16:37:55 +0200 Subject: [PATCH 3/6] Using xcode 9 --- .travis.yml | 2 +- Model Boiler/Classes/Service/Service.swift | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index a46d671..577d4cc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: objective-c -osx_image: xcode8.2 +osx_image: xcode9 xcode_sdk: macosx branches: only: diff --git a/Model Boiler/Classes/Service/Service.swift b/Model Boiler/Classes/Service/Service.swift index cd579e7..7e2205f 100644 --- a/Model Boiler/Classes/Service/Service.swift +++ b/Model Boiler/Classes/Service/Service.swift @@ -34,9 +34,11 @@ struct Service { generatorSettings.onlyCreateInitializer = SettingsManager.isSettingEnabled(.OnlyCreateInitializer) do { - // Try to generate the code - guard let bodies = try modelBodies(fromSource: source, generatorSettings: generatorSettings) else { throw ModelParserError.NoModelNameFound } - let code = extensionCode(fromBodies: bodies) + // Try to generate the code bodies + guard let extensions = try extensionBodies(fromSource: source, generatorSettings: generatorSettings) else { throw ModelParserError.NoModelNameFound } + + //Concatenate the extensions + let code = extensionCode(fromBodies: extensions) // Play success sound playSound(Service.successSound) @@ -74,7 +76,7 @@ struct Service { return retVal } - static func modelBodies(fromSource source: String, generatorSettings: ModelGeneratorSettings) throws -> [String]? { + static func extensionBodies(fromSource source: String, generatorSettings: ModelGeneratorSettings) throws -> [String]? { if let codes = try codeStrings(fromSourceCode: source) { var retVal = [String]() var outerModelPrefix = "" @@ -90,8 +92,6 @@ struct Service { } codeToParse.append(character) } - //For some reason this doesn't work in current Swift4??? - // codeToParse = codeToParse.insert(contentsOf: outerModelPrefix, at: codeToParse.startIndex(range.length)) } let newCode = try ModelGenerator.modelCode(fromSourceCode: codeToParse, withSettings: generatorSettings) retVal.append(newCode) From 466bfe95426677b0a6f2e686e4d2c9fbd8cf1d97 Mon Sep 17 00:00:00 2001 From: Jakob Mygind Date: Mon, 10 Jul 2017 10:48:34 +0200 Subject: [PATCH 4/6] Refactored Added comments --- Model Boiler/Classes/Service/Service.swift | 110 +++++++++++---------- 1 file changed, 60 insertions(+), 50 deletions(-) diff --git a/Model Boiler/Classes/Service/Service.swift b/Model Boiler/Classes/Service/Service.swift index 7e2205f..6da43f3 100644 --- a/Model Boiler/Classes/Service/Service.swift +++ b/Model Boiler/Classes/Service/Service.swift @@ -63,29 +63,32 @@ struct Service { } } - static func extensionCode(fromBodies bodies: [String]) -> String { - var bodiesMutating = bodies - var retVal = "" - if !bodies.isEmpty { - retVal = bodiesMutating.removeFirst() - } - for body in bodiesMutating { - retVal.append("\n\n\(body)") + static func playSound(_ sound: NSSound?) { + if !UserDefaults.standard.bool(forKey: "muteSound") { + sound?.play() } - - return retVal } +} + + +// MARK: String manipulation +extension Service { + // Create extensions while taking into account parent Model static func extensionBodies(fromSource source: String, generatorSettings: ModelGeneratorSettings) throws -> [String]? { - if let codes = try codeStrings(fromSourceCode: source) { + if let codes = try modelStrings(fromSourceCode: source) { var retVal = [String]() var outerModelPrefix = "" for (index, code) in codes.enumerated() { var codeToParse = code - if index == 0, let outerName = modelName(fromSourceCode: codeToParse)?.0 { + // first object is parent -> Save parent name + if index == 0, let outerName = modelNameAndRange(fromSourceCode: codeToParse)?.0 { outerModelPrefix = outerName + "." - } else if let range = modelName(fromSourceCode: codeToParse)?.1 { + } else if let range = modelNameAndRange(fromSourceCode: codeToParse)?.1 { + + // For embedded Models, add Parent Model prefix before model name codeToParse = "" + // TODO: When Swift 4 properly works: Replace the following code with String.insert(contentsOf: otherString, at: Index) for (index, character) in code.enumerated() { if index == range.lowerBound { codeToParse.append(" \(outerModelPrefix)") @@ -93,6 +96,7 @@ struct Service { codeToParse.append(character) } } + // Add model string to return array let newCode = try ModelGenerator.modelCode(fromSourceCode: codeToParse, withSettings: generatorSettings) retVal.append(newCode) } @@ -101,7 +105,7 @@ struct Service { return nil } - static func modelName(fromSourceCode code: String) -> (String, NSRange)? { + static func modelNameAndRange(fromSourceCode code: String) -> (String, NSRange)? { let range = NSMakeRange(0, code.characters.count) let match = modelNameRegex?.firstMatch(in: code, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: range) @@ -113,6 +117,8 @@ struct Service { return nil } + + // Adds end brackets to models extracted with only start brackets static func missingEndBrackets(inCode code: String) -> String { let startBrackets = code.components(separatedBy: "{").count - 1 let endBrackets = code.components(separatedBy: "}").count - 1 @@ -128,68 +134,72 @@ struct Service { return addition } - static func codeStrings(fromSourceCode code: String) throws -> [String]? { + static func modelStrings(fromSourceCode code: String, regex: NSRegularExpression) throws -> [String]? { let range = NSMakeRange(0, code.characters.count) - // Check if struct - let structMatches = structRegex?.numberOfMatches(in: code, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: range) - if let matches = structMatches, - matches == 1 { + // Check for regex matches + let matches = regex.numberOfMatches(in: code, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: range) + if matches == 1 { + // In case just one match found, return this + return [code] - return [code + missingEndBrackets(inCode: code)] + } else if matches > 1 { + let allMatches = regex.matches(in: code, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: range) - } else if let matches = structMatches, - matches > 1, - let allMatches = structRegex?.matches(in: code, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: range) { + // If multiple matches found identify range up until the second occurance let rangeToRemove = NSMakeRange(0, allMatches[1].range.location) + // Extract first match let string = (code as NSString).substring(to: rangeToRemove.length) as String + // Get remaining range let restRange = NSMakeRange(rangeToRemove.length, range.length - rangeToRemove.length) + // Get remaining code let restCode = (code as NSString).substring(with: restRange) as String do { - - if let strings = try codeStrings(fromSourceCode: restCode) { + // Call recursively with rest code and return along with first found value + if let strings = try modelStrings(fromSourceCode: restCode) { return [string + missingEndBrackets(inCode: string)] + strings } } } + return nil + } + + //Extracts models + static func modelStrings(fromSourceCode code: String) throws -> [String]? { - //Check if final class + // Identify struct models + if let matches = try modelStrings(fromSourceCode: code, regex: structRegex!) { + return matches + } - let finalClassMatches = finalClassRegex?.numberOfMatches(in: code, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: range) - if let matches = finalClassMatches, - matches == 1 { - - return [code + missingEndBrackets(inCode: code)] + //Identify final class models - } else if let matches = finalClassMatches, - matches > 1, - let allMatches = finalClassRegex?.matches(in: code, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: range) { - let rangeToRemove = NSMakeRange(0, allMatches[1].range.location) - let string = (code as NSString).substring(to: rangeToRemove.length) as String - let restRange = NSMakeRange(rangeToRemove.length, range.length - rangeToRemove.length) - let restCode = (code as NSString).substring(with: restRange) as String - do { - - if let strings = try codeStrings(fromSourceCode: restCode) { - return [string + missingEndBrackets(inCode: string)] + strings - } - } - - } else if code.contains("class") { + if let matches = try modelStrings(fromSourceCode: code, regex: finalClassRegex!) { + return matches + } + else if code.contains("class") { throw ModelParserError.ClassShouldBeDeclaredAsFinal } // If no struct or class was found return nil } - // - static func playSound(_ sound: NSSound?) { - if !UserDefaults.standard.bool(forKey: "muteSound") { - sound?.play() + //Concatenates all the generated extensions + static func extensionCode(fromBodies bodies: [String]) -> String { + var bodiesMutating = bodies + var retVal = "" + if !bodies.isEmpty { + retVal = bodiesMutating.removeFirst() } + for body in bodiesMutating { + retVal.append("\n\n\(body)") + } + + return retVal } } + // Regular expression used for parsing extension Service { From b6f3bd1289a74c53cb1dc885f0b63a0217867eee Mon Sep 17 00:00:00 2001 From: Jakob Mygind Date: Tue, 11 Jul 2017 16:44:07 +0200 Subject: [PATCH 5/6] Updated second target to Swift 4 Refactored embedded model parsing to using a new regex --- Cartfile.resolved | 4 +- Model Boiler.xcodeproj/project.pbxproj | 8 ++-- Model Boiler/Classes/Service/Service.swift | 45 +++++++++++++--------- relaunch/main.swift | 4 +- 4 files changed, 36 insertions(+), 25 deletions(-) diff --git a/Cartfile.resolved b/Cartfile.resolved index fb46a9c..7e8f7d9 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,3 +1,3 @@ -github "shpakovski/MASShortcut" "2.3.6" -github "ZipArchive/ZipArchive" "v1.7" +github "ZipArchive/ZipArchive" "v1.8.1" github "nodes-ios/model-generator" "1.0.2" +github "shpakovski/MASShortcut" "2.3.6" diff --git a/Model Boiler.xcodeproj/project.pbxproj b/Model Boiler.xcodeproj/project.pbxproj index 790e382..27b1345 100644 --- a/Model Boiler.xcodeproj/project.pbxproj +++ b/Model Boiler.xcodeproj/project.pbxproj @@ -273,7 +273,7 @@ TargetAttributes = { 01ECF0FF1C43FDD700C23397 = { CreatedOnToolsVersion = 7.2; - LastSwiftMigration = 0810; + LastSwiftMigration = 0900; }; 3092679B1B77EBE700DFE5A1 = { CreatedOnToolsVersion = 7.0; @@ -354,7 +354,8 @@ buildSettings = { MACOSX_DEPLOYMENT_TARGET = 10.11; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = On; + SWIFT_VERSION = 4.0; }; name = Release; }; @@ -370,7 +371,8 @@ MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = On; + SWIFT_VERSION = 4.0; }; name = Debug; }; diff --git a/Model Boiler/Classes/Service/Service.swift b/Model Boiler/Classes/Service/Service.swift index 6da43f3..dfc4a40 100644 --- a/Model Boiler/Classes/Service/Service.swift +++ b/Model Boiler/Classes/Service/Service.swift @@ -11,14 +11,14 @@ import ModelGenerator import Cocoa struct Service { - + static let errorSound = NSSound(named: NSSound.Name(rawValue: "Basso")) static let successSound = NSSound(named: NSSound.Name(rawValue: "Pop")) - + // MARK: - Main Function - - - static func generate(_ pasteboard: NSPasteboard = NSPasteboard.general) { + static func generate(_ pasteboard: NSPasteboard = NSPasteboard.general) { + guard let source = pasteboard.string(forType: NSPasteboard.PasteboardType.string), (pasteboard.pasteboardItems?.count == 1) else { NSUserNotification.display(title: "No text selected", andMessage: "Nothing was found in the pasteboard.") @@ -46,7 +46,7 @@ struct Service { // Copy back to pasteboard NSPasteboard.general.declareTypes([NSPasteboard.PasteboardType.string], owner: nil) NSPasteboard.general.setString(code, forType: NSPasteboard.PasteboardType.string) - + // Success, show notification NSUserNotification.display( title: "Code generated", @@ -143,21 +143,18 @@ extension Service { // In case just one match found, return this return [code] - } else if matches > 1 { - let allMatches = regex.matches(in: code, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: range) + } else if matches > 1, let innerMatch = embeddedModelBodyRegex?.firstMatchInString(string: code) { + + // Extract embedded model + let innerString = (code as NSString).substring(with: innerMatch.range) + // remove embedded model + let remainder = (code as NSString).replacingCharacters(in: innerMatch.range, with: "") - // If multiple matches found identify range up until the second occurance - let rangeToRemove = NSMakeRange(0, allMatches[1].range.location) - // Extract first match - let string = (code as NSString).substring(to: rangeToRemove.length) as String - // Get remaining range - let restRange = NSMakeRange(rangeToRemove.length, range.length - rangeToRemove.length) - // Get remaining code - let restCode = (code as NSString).substring(with: restRange) as String do { // Call recursively with rest code and return along with first found value - if let strings = try modelStrings(fromSourceCode: restCode) { - return [string + missingEndBrackets(inCode: string)] + strings + if let strings = try modelStrings(fromSourceCode: remainder) { + let retVal = strings + [innerString] + return retVal } } } @@ -173,7 +170,7 @@ extension Service { } //Identify final class models - + if let matches = try modelStrings(fromSourceCode: code, regex: finalClassRegex!) { return matches } @@ -202,6 +199,18 @@ extension Service { // Regular expression used for parsing extension Service { + + static var embeddedModelBodyRegex: NSRegularExpression? { + do { + let regex = try NSRegularExpression( + pattern: "((?!^)(struct|final\\sclass)\\s\\w+\\s\\{[\\d\\S\\W]*?\\})", + options: NSRegularExpression.Options(rawValue: 0)) + return regex + } catch { + print("Couldn't create model body regex.") + return nil + } + } static var modelBodyRegex: NSRegularExpression? { do { diff --git a/relaunch/main.swift b/relaunch/main.swift index fc4c838..4856cf1 100644 --- a/relaunch/main.swift +++ b/relaunch/main.swift @@ -44,9 +44,9 @@ autoreleasepool { // relaunch do { - try NSWorkspace.shared().launchApplication( + try NSWorkspace.shared.launchApplication( at: bundleURL, - options: NSWorkspaceLaunchOptions(rawValue: 0), + options: NSWorkspace.LaunchOptions(rawValue: 0), configuration: [:]) } catch { print("Error relaunching") From b921a03531b15327d6f76fec03c0437f838b4c30 Mon Sep 17 00:00:00 2001 From: Jakob Mygind Date: Wed, 12 Jul 2017 09:41:50 +0200 Subject: [PATCH 6/6] Removed unused util function --- Model Boiler/Classes/Service/Service.swift | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/Model Boiler/Classes/Service/Service.swift b/Model Boiler/Classes/Service/Service.swift index dfc4a40..b7d9e3e 100644 --- a/Model Boiler/Classes/Service/Service.swift +++ b/Model Boiler/Classes/Service/Service.swift @@ -117,23 +117,6 @@ extension Service { return nil } - - // Adds end brackets to models extracted with only start brackets - static func missingEndBrackets(inCode code: String) -> String { - let startBrackets = code.components(separatedBy: "{").count - 1 - let endBrackets = code.components(separatedBy: "}").count - 1 - let difference = startBrackets - endBrackets - - var addition = "" - if difference > 0 { - - for _ in 0.. [String]? { let range = NSMakeRange(0, code.characters.count)