Compare commits

..

10 Commits

13 changed files with 301 additions and 250 deletions

@ -1 +1 @@
Subproject commit 36ad10e4a47110430f1d377571b4fd74cee05c21 Subproject commit 6928d3ce726e118924752cb8a636732b257e87cd

View File

@ -539,7 +539,7 @@
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx; SDKROOT = macosx10.13;
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
}; };
name = Debug; name = Debug;
@ -577,7 +577,7 @@
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx; SDKROOT = macosx10.13;
}; };
name = Release; name = Release;
}; };

View File

@ -29,17 +29,17 @@ class ASApplication: NSObject, NSApplicationDelegate, NSMenuDelegate {
let workSpace = NSWorkspace.shared() let workSpace = NSWorkspace.shared()
let userDefaults = UserDefaults.standard let userDefaults = UserDefaults.standard
let appDefaultsURL = Bundle.main.url(forResource: "Defaults", withExtension: "plist")! let appDefaultsURL = Bundle.main.url(forResource: "Defaults", withExtension: "plist")!
var appDefaults = NSDictionary(contentsOfURL: appDefaultsURL) as! [String: AnyObject] var appDefaults = NSDictionary(contentsOf: appDefaultsURL) as! [String: Any]
// //
// Add dynamic app defaults // Add dynamic app defaults
// //
if let arduinoPath = workSpace.urlForApplication(withBundleIdentifier: "cc.arduino.Arduino")?.path { if let arduinoPath = workSpace.urlForApplication(withBundleIdentifier: "cc.arduino.Arduino")?.path {
appDefaults["Arduino"] = arduinoPath appDefaults["Arduino"] = arduinoPath
} }
var sketchbooks = [NSString]() var sketchbooks = [String]()
for doc in fileManager.urls(for: .documentDirectory, in: .userDomainMask) { for doc in fileManager.urls(for: .documentDirectory, in: .userDomainMask) {
sketchbooks.append(doc.URLByAppendingPathComponent("Arduino").path!) sketchbooks.append(doc.appendingPathComponent("Arduino").path)
sketchbooks.append(doc.URLByAppendingPathComponent("AVRSack").path!) sketchbooks.append(doc.appendingPathComponent("AVRSack").path)
} }
appDefaults["Sketchbooks"] = sketchbooks appDefaults["Sketchbooks"] = sketchbooks
if fileManager.fileExists(atPath: "/usr/local/CrossPack-AVR") { if fileManager.fileExists(atPath: "/usr/local/CrossPack-AVR") {
@ -75,16 +75,18 @@ class ASApplication: NSObject, NSApplicationDelegate, NSMenuDelegate {
sketches = [String]() sketches = [String]()
for sketchBook in UserDefaults.standard.object(forKey:"Sketchbooks") as! [String] { for sketchBook in UserDefaults.standard.object(forKey:"Sketchbooks") as! [String] {
if FileManager.default.fileExists(atPath: sketchBook) { if FileManager.default.fileExists(atPath: sketchBook) {
ASSketchBook.addSketches(menu: menu, target: self, action: Selector(("openSketch:")), path: sketchBook, sketches: &sketches) ASSketchBook.addSketches(menu: menu, target: self, action: #selector(ASApplication.openSketch(_:)), path: sketchBook, sketches: &sketches)
} }
} }
case "Examples": case "Examples":
menu.removeAllItems() menu.removeAllItems()
examples = [String]() examples = [String]()
if let arduinoURL = NSWorkspace.shared().urlForApplication(withBundleIdentifier: "cc.arduino.Arduino") { if let arduinoURL = NSWorkspace.shared().urlForApplication(withBundleIdentifier: "cc.arduino.Arduino") {
let examplePath = arduinoURL.URLByAppendingPathComponent("Contents/Resources/Java/examples", isDirectory:true).path! let examplePath = arduinoURL.appendingPathComponent("Contents/Resources/Java/examples", isDirectory:true).path
ASSketchBook.addSketches(menu, target: self, action: "openExample:", path: examplePath, sketches: &examples) ASSketchBook.addSketches(menu: menu, target: self, action: #selector(ASApplication.openExample(_:)), path: examplePath, sketches: &examples)
} }
ASLibraries.instance().addContribLibraryExamplesToMenu(menu: menu, sketches: &examples)
ASLibraries.instance().addStandardLibraryExamplesToMenu(menu: menu, sketches: &examples)
case "Import Standard Library": case "Import Standard Library":
menu.removeAllItems() menu.removeAllItems()
ASLibraries.instance().addStandardLibrariesToMenu(menu: menu) ASLibraries.instance().addStandardLibrariesToMenu(menu: menu)
@ -97,18 +99,18 @@ class ASApplication: NSObject, NSApplicationDelegate, NSMenuDelegate {
menu.removeItem(at: 2) menu.removeItem(at: 2)
} }
for port in ASSerial.ports() { for port in ASSerial.ports() {
menu.addItem(withTitle: port, action:Selector(("serialConnectMenu:")), keyEquivalent:"") menu.addItem(withTitle: port, action:#selector(ASApplication.serialConnectMenu(_:)), keyEquivalent:"")
} }
default: default:
break break
} }
} }
@IBAction func serialConnectMenu(port: NSMenuItem) { @IBAction func serialConnectMenu(_ port: NSMenuItem) {
ASSerialWin.showWindowWithPort(port: port.title) ASSerialWin.showWindowWithPort(port: port.title)
} }
func openTemplate(template: NSURL, fromReadOnly: Bool) { func openTemplate(template: URL, fromReadOnly: Bool) {
let editable : String let editable : String
if fromReadOnly { if fromReadOnly {
editable = "editable " editable = "editable "
@ -116,31 +118,31 @@ class ASApplication: NSObject, NSApplicationDelegate, NSMenuDelegate {
editable = "" editable = ""
} }
ASApplication.newProjectLocation(documentWindow: nil, ASApplication.newProjectLocation(documentWindow: nil,
message: "Save \(editable)copy of project \(template.lastPathComponent!)") message: "Save \(editable)copy of project \(template.lastPathComponent)")
{ (saveTo) -> Void in { (saveTo) -> Void in
let oldName = template.lastPathComponent! let oldName = template.lastPathComponent
let newName = saveTo.lastPathComponent! let newName = saveTo.lastPathComponent
let fileManager = FileManager.default let fileManager = FileManager.default
do { do {
try fileManager.copyItemAtURL(template, toURL: saveTo) try fileManager.copyItem(at: template, to: saveTo)
let contents = fileManager.enumeratorAtURL(saveTo, let contents = fileManager.enumerator(at: saveTo,
includingPropertiesForKeys: [URLResourceKey.nameKey, URLResourceKey.pathKey], includingPropertiesForKeys: [URLResourceKey.nameKey, URLResourceKey.pathKey],
options: .SkipsHiddenFiles, errorHandler: nil) options: .skipsHiddenFiles, errorHandler: nil)
while let item = contents?.nextObject() as? NSURL { while let item = contents?.nextObject() as? URL {
let itemBase = item.URLByDeletingPathExtension?.lastPathComponent! let itemBase = item.deletingPathExtension().lastPathComponent
if itemBase == oldName { if itemBase == oldName {
let newItem = item.URLByDeletingLastPathComponent!.URLByAppendingPathComponent( let newItem = item.deletingLastPathComponent().appendingPathComponent(
newName).URLByAppendingPathExtension(item.pathExtension!) newName).appendingPathExtension(item.pathExtension)
try fileManager.moveItemAtURL(item, toURL: newItem) try fileManager.moveItem(at: item, to: newItem)
} }
} }
} catch (_) { } catch (_) {
} }
let sketch = ASSketchBook.findSketch(path: saveTo.path!) let sketch = ASSketchBook.findSketch(path: saveTo.path)
switch sketch { switch sketch {
case .Sketch(_, let path): case .Sketch(_, let path):
let doc = NSDocumentController.shared() let doc = NSDocumentController.shared()
doc.openDocumentWithContentsOfURL(NSURL(fileURLWithPath: path), display: true) { (doc, alreadyOpen, error) -> Void in doc.openDocument(withContentsOf: URL(fileURLWithPath: path), display: true) { (doc, alreadyOpen, error) -> Void in
} }
default: default:
break break
@ -148,14 +150,14 @@ class ASApplication: NSObject, NSApplicationDelegate, NSMenuDelegate {
} }
} }
@IBAction func openSketch(item: NSMenuItem) { @IBAction func openSketch(_ item: NSMenuItem) {
let url = NSURL(fileURLWithPath: sketches[item.tag]) let url = URL(fileURLWithPath: sketches[item.tag])
let doc = NSDocumentController.shared() let doc = NSDocumentController.shared()
doc.openDocumentWithContentsOfURL(url, display: true) { (doc, alreadyOpen, error) -> Void in doc.openDocument(withContentsOf: url, display: true) { (doc, alreadyOpen, error) -> Void in
} }
} }
@IBAction func openExample(item: NSMenuItem) { @IBAction func openExample(_ item: NSMenuItem) {
let url = NSURL(fileURLWithPath: examples[item.tag]) let url = NSURL(fileURLWithPath: examples[item.tag])
openTemplate(template: url.deletingLastPathComponent!, fromReadOnly:true) openTemplate(template: url.deletingLastPathComponent!, fromReadOnly:true)
} }
@ -164,23 +166,23 @@ class ASApplication: NSObject, NSApplicationDelegate, NSMenuDelegate {
ASApplication.newProjectLocation(documentWindow: nil, ASApplication.newProjectLocation(documentWindow: nil,
message: "Create Project") message: "Create Project")
{ (saveTo) -> Void in { (saveTo) -> Void in
let fileManager = FileManager.defaultManager() let fileManager = FileManager.default
do { do {
try fileManager.createDirectoryAtURL(saveTo, withIntermediateDirectories:false, attributes:nil) try fileManager.createDirectory(at: saveTo, withIntermediateDirectories:false, attributes:nil)
let proj = saveTo.appendingPathComponent(saveTo.lastPathComponent!+".avrsackproj") let proj = saveTo.appendingPathComponent(saveTo.lastPathComponent+".avrsackproj")
let docController = NSDocumentController.shared() let docController = NSDocumentController.shared()
if let doc = try docController.openUntitledDocumentAndDisplay(true) as? ASProjDoc { if let doc = try docController.openUntitledDocumentAndDisplay(true) as? ASProjDoc {
doc.fileURL = proj doc.fileURL = proj
doc.updateProjectURL() doc.updateProjectURL()
doc.createFileAtURL(url: saveTo.URLByAppendingPathComponent(saveTo.lastPathComponent!+".ino")) doc.createFileAtURL(url: saveTo.appendingPathComponent(saveTo.lastPathComponent+".ino"))
try doc.writeToURL(proj, ofType: "Project", forSaveOperation: .SaveAsOperation, originalContentsURL: nil) try doc.write(to: proj, ofType: "Project", for: .saveAsOperation, originalContentsURL: nil)
} }
} catch _ { } catch _ {
} }
} }
} }
class func newProjectLocation(documentWindow: NSWindow?, message: String, completion: (NSURL) -> ()) { class func newProjectLocation(documentWindow: NSWindow?, message: String, completion: @escaping (URL) -> ()) {
let savePanel = NSSavePanel() let savePanel = NSSavePanel()
savePanel.allowedFileTypes = [kUTTypeFolder as String] savePanel.allowedFileTypes = [kUTTypeFolder as String]
savePanel.message = message savePanel.message = message
@ -199,19 +201,19 @@ class ASApplication: NSObject, NSApplicationDelegate, NSMenuDelegate {
} }
} }
@IBAction func goToHelpPage(sender: AnyObject) { @IBAction func goToHelpPage(_ sender: AnyObject) {
let helpString: String let helpString: CFString
switch sender.tag { switch sender.tag {
case 0: case 0:
helpString = "license.html" helpString = "license.html" as CFString
default: default:
abort() abort()
} }
let locBookName = Bundle.main.object(forInfoDictionaryKey: "CFBundleHelpBookName") as! String let locBookName = Bundle.main.object(forInfoDictionaryKey: "CFBundleHelpBookName") as! CFString
AHGotoPage(locBookName, helpString, nil) AHGotoPage(locBookName, helpString, nil)
} }
@IBAction func goToHelpURL(sender: AnyObject) { @IBAction func goToHelpURL(_ sender: AnyObject) {
let helpString: String let helpString: String
switch sender.tag { switch sender.tag {
case 0: case 0:
@ -219,7 +221,7 @@ class ASApplication: NSObject, NSApplicationDelegate, NSMenuDelegate {
default: default:
abort() abort()
} }
NSWorkspace.sharedWorkspace().openURL(NSURL(string: helpString)!) NSWorkspace.shared().open(URL(string: helpString)!)
} }
} }

View File

@ -9,16 +9,16 @@
import Foundation import Foundation
class ASBuilder { class ASBuilder {
var dir = NSURL() var dir = URL(fileURLWithPath: "/")
var task : Task? var task : Process?
var continuation: (()->())? var continuation: (()->())?
var termination : AnyObject? var termination : AnyObject?
init() { init() {
termination = NotificationCenter.default.addObserver(forName: Task.didTerminateNotification, termination = NotificationCenter.default.addObserver(forName: Process.didTerminateNotification,
object: nil, queue: nil, using: object: nil, queue: nil, using:
{ (notification: Notification) in { (notification: Notification) in
if notification.object as? Task == self.task { if notification.object as? Process == self.task {
if self.task!.terminationStatus == 0 { if self.task!.terminationStatus == 0 {
if let cont = self.continuation { if let cont = self.continuation {
self.continuation = nil self.continuation = nil
@ -34,8 +34,8 @@ class ASBuilder {
NotificationCenter.default.removeObserver(termination!) NotificationCenter.default.removeObserver(termination!)
} }
func setProjectURL(url: NSURL) { func setProjectURL(url: URL) {
dir = url.URLByDeletingLastPathComponent!.URLByStandardizingPath! dir = url.deletingLastPathComponent().standardizedFileURL
} }
func stop() { func stop() {
@ -45,15 +45,15 @@ class ASBuilder {
func cleanProject() { func cleanProject() {
do { do {
try FileManager.default.removeItem(at: dir.appendingPathComponent("build")!) try FileManager.default.removeItem(at: dir.appendingPathComponent("build"))
} catch _ { } catch _ {
} }
} }
func buildProject(board: String, files: ASFileTree) { func buildProject(board: String, files: ASFileTree) {
let toolChain = (NSApplication.shared().delegate as! ASApplication).preferences.toolchainPath let toolChain = (NSApplication.shared().delegate as! ASApplication).preferences.toolchainPath
task = Task() task = Process()
task!.currentDirectoryPath = dir.path! task!.currentDirectoryPath = dir.path
task!.launchPath = Bundle.main.path(forResource: "BuildProject", ofType: "")! task!.launchPath = Bundle.main.path(forResource: "BuildProject", ofType: "")!
let fileManager = FileManager.default let fileManager = FileManager.default
@ -81,7 +81,7 @@ class ASBuilder {
return return
} }
args.append("toolchain="+toolChain) args.append("toolchain="+toolChain)
args.append("project="+dir.lastPathComponent!) args.append("project="+dir.lastPathComponent)
args.append("board="+board) args.append("board="+board)
args.append("mcu="+boardProp["build.mcu"]!) args.append("mcu="+boardProp["build.mcu"]!)
args.append("f_cpu="+boardProp["build.f_cpu"]!) args.append("f_cpu="+boardProp["build.f_cpu"]!)
@ -111,8 +111,8 @@ class ASBuilder {
let interactive = mode == .Interactive let interactive = mode == .Interactive
let portPath = ASSerial.fileName(forPort: port) let portPath = ASSerial.fileName(forPort: port)
let toolChain = (NSApplication.shared().delegate as! ASApplication).preferences.toolchainPath let toolChain = (NSApplication.shared().delegate as! ASApplication).preferences.toolchainPath
task = Task() task = Process()
task!.currentDirectoryPath = dir.path! task!.currentDirectoryPath = dir.path
task!.launchPath = toolChain+"/bin/avrdude" task!.launchPath = toolChain+"/bin/avrdude"
let fileManager = FileManager.default let fileManager = FileManager.default
@ -127,8 +127,8 @@ class ASBuilder {
} else { } else {
ASSerialWin.portNeededForUpload(port: port) ASSerialWin.portNeededForUpload(port: port)
let logURL = dir.appendingPathComponent("build/upload.log") let logURL = dir.appendingPathComponent("build/upload.log")
fileManager.createFileAtPath(logURL.path, contents: NSData(), attributes: nil) fileManager.createFile(atPath: logURL.path, contents: Data(), attributes: nil)
logOut = FileHandle(forWritingAtPath: logURL.path!)! logOut = FileHandle(forWritingAtPath: logURL.path)!
task!.standardOutput = logOut task!.standardOutput = logOut
task!.standardError = logOut task!.standardError = logOut
} }
@ -147,16 +147,16 @@ class ASBuilder {
var args = Array<String>(repeating: "-v", count: verbosity) var args = Array<String>(repeating: "-v", count: verbosity)
args += [ args += [
"-C", toolChain+"/etc/avrdude.conf", "-C", toolChain+"/etc/avrdude.conf",
"-p", boardProp["build.mcu"]!, "-c", proto!, "-P", portPath] "-p", boardProp["build.mcu"]!, "-c", proto!, "-P", portPath!]
switch mode { switch mode {
case .Upload: case .Upload:
if hasBootloader { if hasBootloader {
args += ["-D"] args += ["-D"]
} }
args += ["-U", "flash:w:build/"+board+"/"+dir.lastPathComponent!+".hex:i"] args += ["-U", "flash:w:build/"+board+"/"+dir.lastPathComponent+".hex:i"]
continuation = { continuation = {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(2*NSEC_PER_SEC)), dispatch_get_main_queue(), { DispatchQueue.main.asyncAfter(deadline: .now() + 2.0, execute: {
ASSerialWin.portAvailableAfterUpload(port) ASSerialWin.portAvailableAfterUpload(port: port)
}) })
} }
case .BurnBootloader: case .BurnBootloader:
@ -182,8 +182,8 @@ class ASBuilder {
needPhase2 = true needPhase2 = true
} }
if needPhase2 { if needPhase2 {
let task2 = Task() let task2 = Process()
task2.currentDirectoryPath = dir.path! task2.currentDirectoryPath = dir.path
task2.launchPath = toolChain+"/bin/avrdude" task2.launchPath = toolChain+"/bin/avrdude"
task2.arguments = loaderArgs task2.arguments = loaderArgs
task2.standardOutput = logOut task2.standardOutput = logOut
@ -193,8 +193,8 @@ class ASBuilder {
logOut.write(cmdLine.data(using: String.Encoding.utf8, allowLossyConversion: true)!) logOut.write(cmdLine.data(using: String.Encoding.utf8, allowLossyConversion: true)!)
task2.launch() task2.launch()
self.continuation = { self.continuation = {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(2*NSEC_PER_SEC)), dispatch_get_main_queue(), { DispatchQueue.main.asyncAfter(deadline: .now() + 2.0, execute: {
ASSerialWin.portAvailableAfterUpload(port) ASSerialWin.portAvailableAfterUpload(port: port)
}) })
} }
} }
@ -220,7 +220,7 @@ class ASBuilder {
sleep(1) sleep(1)
for retry in 0 ..< 40 { for retry in 0 ..< 40 {
usleep(250000) usleep(250000)
if (fileManager.fileExistsAtPath(portPath)) { if (fileManager.fileExists(atPath: portPath!)) {
if verbosity > 0 { if verbosity > 0 {
logOut.write("Found port \(port) after \(retry) attempts.\n".data(using: String.Encoding.utf8, allowLossyConversion: true)!) logOut.write("Found port \(port) after \(retry) attempts.\n".data(using: String.Encoding.utf8, allowLossyConversion: true)!)
} }
@ -242,22 +242,22 @@ class ASBuilder {
func disassembleProject(board: String) { func disassembleProject(board: String) {
let toolChain = (NSApplication.shared().delegate as! ASApplication).preferences.toolchainPath let toolChain = (NSApplication.shared().delegate as! ASApplication).preferences.toolchainPath
task = Task() task = Process()
task!.currentDirectoryPath = dir.path! task!.currentDirectoryPath = dir.path
task!.launchPath = toolChain+"/bin/avr-objdump" task!.launchPath = toolChain+"/bin/avr-objdump"
let fileManager = FileManager.default let fileManager = FileManager.default
let logURL = dir.appendingPathComponent("build/disasm.log") let logURL = dir.appendingPathComponent("build/disasm.log")
fileManager.createFileAtPath(logURL.path!, contents: NSData(), attributes: nil) fileManager.createFile(atPath: logURL.path, contents: Data(), attributes: nil)
let logOut = FileHandle(forWritingAtPath: logURL.path!)! let logOut = FileHandle(forWritingAtPath: logURL.path)!
task!.standardOutput = logOut task!.standardOutput = logOut
task!.standardError = logOut task!.standardError = logOut
let showSource = UserDefaults.standard.bool(forKey: "ShowSourceInDisassembly") let showSource = UserDefaults.standard.bool(forKey: "ShowSourceInDisassembly")
var args = showSource ? ["-S"] : [] var args = showSource ? ["-S"] : []
args += ["-d", "build/"+board+"/"+dir.lastPathComponent!+".elf"] args += ["-d", "build/"+board+"/"+dir.lastPathComponent+".elf"]
let cmdLine = task!.launchPath!+" "+(args as NSArray).componentsJoined(by: " ")+"\n" let cmdLine = task!.launchPath!+" "+(args as NSArray).componentsJoined(by: " ")+"\n"
logOut.writeData(cmdLine.dataUsingEncoding(String.Encoding.utf8, allowLossyConversion: true)!) logOut.write(cmdLine.data(using: String.Encoding.utf8, allowLossyConversion: true)!)
task!.arguments = args; task!.arguments = args;
task!.launch() task!.launch()
} }

View File

@ -69,7 +69,7 @@ class ASFileNode : Equatable {
closure(self) closure(self)
} }
func propertyList(rootPath: String) -> Dictionary<String, AnyObject> { func propertyList(rootPath: String) -> Dictionary<String, Any> {
return [:] return [:]
} }
@ -127,7 +127,7 @@ class ASFileGroup : ASFileNode {
private let kChildrenKey = "Children" private let kChildrenKey = "Children"
private let kExpandedKey = "Expanded" private let kExpandedKey = "Expanded"
private var kNodeType : String { return kNodeTypeGroup } fileprivate var kNodeType : String { return kNodeTypeGroup }
override init(name: String = "") { override init(name: String = "") {
self.children = [] self.children = []
@ -155,11 +155,11 @@ class ASFileGroup : ASFileNode {
} }
} }
func childrenPropertyList(rootPath: String) -> [AnyObject] { func childrenPropertyList(rootPath: String) -> [Any] {
return children.map() { (node) in node.propertyList(rootPath: rootPath) } return children.map() { (node) in node.propertyList(rootPath: rootPath) }
} }
override func propertyList(rootPath: String) -> Dictionary<String, AnyObject> { override func propertyList(rootPath: String) -> Dictionary<String, Any> {
return [kTypeKey: kNodeType, kNameKey: name, kExpandedKey: expanded, return [kTypeKey: kNodeType, kNameKey: name, kExpandedKey: expanded,
kChildrenKey: childrenPropertyList(rootPath: rootPath)] kChildrenKey: childrenPropertyList(rootPath: rootPath)]
} }
@ -174,7 +174,7 @@ class ASFileGroup : ASFileNode {
} }
class ASProject : ASFileGroup { class ASProject : ASFileGroup {
override private var kNodeType : String { return kNodeTypeProject } override fileprivate var kNodeType : String { return kNodeTypeProject }
override init(name: String = "") { override init(name: String = "") {
super.init(name: name) super.init(name: name)
@ -211,7 +211,13 @@ class ASFileItem : ASFileNode {
} else { } else {
url = URL(fileURLWithPath: path as String, relativeTo: rootURL).standardizedFileURL url = URL(fileURLWithPath: path as String, relativeTo: rootURL).standardizedFileURL
} }
if try! !url.checkResourceIsReachable() { var fileExists = false
do {
fileExists = try url.checkResourceIsReachable()
} catch {
fileExists = false
}
if !fileExists {
// //
// When projects get moved, .ino files get renamed but that fact is not // When projects get moved, .ino files get renamed but that fact is not
// yet reflected in the project file. // yet reflected in the project file.
@ -219,7 +225,7 @@ class ASFileItem : ASFileNode {
let urlDir = url.deletingLastPathComponent() let urlDir = url.deletingLastPathComponent()
let newName = rootURL.appendingPathExtension(url.pathExtension).lastPathComponent let newName = rootURL.appendingPathExtension(url.pathExtension).lastPathComponent
let altURL = urlDir.appendingPathComponent(newName) let altURL = urlDir.appendingPathComponent(newName)
if try! altURL.checkResourceIsReachable() { if let altExists = try? altURL.checkResourceIsReachable(), altExists {
url = altURL url = altURL
} }
} }
@ -253,7 +259,7 @@ class ASFileItem : ASFileNode {
return resComp.joined(separator: "/") return resComp.joined(separator: "/")
} }
override func propertyList(rootPath: String) -> Dictionary<String, AnyObject> { override func propertyList(rootPath: String) -> Dictionary<String, Any> {
return [kTypeKey: kNodeTypeFile, kKindKey: type.rawValue, return [kTypeKey: kNodeTypeFile, kKindKey: type.rawValue,
kPathKey: relativePath(relativeTo: rootPath)] kPathKey: relativePath(relativeTo: rootPath)]
} }
@ -263,17 +269,21 @@ class ASFileItem : ASFileNode {
} }
override func exists() -> Bool { override func exists() -> Bool {
return try! url.checkResourceIsReachable() do {
return try url.checkResourceIsReachable()
} catch {
return false
}
} }
override func modDate() -> Date? { override func modDate() -> Date? {
let values = try? url.resourceValues(forKeys: [URLResourceKey.contentModificationDateKey]) let values = try? url.resourceValues(forKeys: [.contentModificationDateKey])
return values?.contentModificationDate return values?.contentModificationDate
} }
override func revision() -> String? { override func revision() -> String? {
let task = Task() let task = Process()
task.launchPath = Bundle.main.path(forResource: "FileRevision", ofType: "")! task.launchPath = Bundle.main.path(forResource: "FileRevision", ofType: "")!
let outputPipe = Pipe() let outputPipe = Pipe()
task.standardOutput = outputPipe task.standardOutput = outputPipe
@ -309,7 +319,7 @@ class ASFileTree : NSObject, NSOutlineViewDataSource {
func apply(closure: (ASFileNode) -> ()) { func apply(closure: (ASFileNode) -> ()) {
root.apply(closure: closure) root.apply(closure: closure)
} }
func propertyList() -> AnyObject { func propertyList() -> Any {
return root.propertyList(rootPath: projectPath()) return root.propertyList(rootPath: projectPath())
} }
func readPropertyList(prop: Dictionary<String, AnyObject>) { func readPropertyList(prop: Dictionary<String, AnyObject>) {
@ -320,14 +330,14 @@ class ASFileTree : NSObject, NSOutlineViewDataSource {
} }
// MARK: Outline Data Source // MARK: Outline Data Source
func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: AnyObject?) -> Int { func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
if item == nil { if item == nil {
return 4 return 4
} else { } else {
return (item as! ASFileGroup).children.count return (item as! ASFileGroup).children.count
} }
} }
func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: AnyObject?) -> AnyObject { func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
if item == nil { if item == nil {
switch index { switch index {
case 1: case 1:
@ -344,15 +354,15 @@ class ASFileTree : NSObject, NSOutlineViewDataSource {
return group.children[index] return group.children[index]
} }
} }
func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: AnyObject) -> Bool { func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
return item is ASFileGroup return item is ASFileGroup
} }
func outlineView(_ outlineView: NSOutlineView, objectValueFor tableColumn: NSTableColumn?, byItem item: AnyObject?) -> AnyObject? { func outlineView(_ outlineView: NSOutlineView, objectValueFor tableColumn: NSTableColumn?, byItem item: Any?) -> Any? {
return (item as! ASFileNode).nodeName() return (item as! ASFileNode).nodeName()
} }
let kLocalReorderPasteboardType = "ASFilePasteboardType" let kLocalReorderPasteboardType = "ASFilePasteboardType"
func outlineView(_ outlineView: NSOutlineView, writeItems items: [AnyObject], to pasteboard: NSPasteboard) -> Bool { private func outlineView(_ outlineView: NSOutlineView, writeItems items: [AnyObject], to pasteboard: NSPasteboard) -> Bool {
dragged = items as! [ASFileNode] dragged = items as! [ASFileNode]
pasteboard.declareTypes([kLocalReorderPasteboardType], owner: self) pasteboard.declareTypes([kLocalReorderPasteboardType], owner: self)
pasteboard.setData(Data(), forType: kLocalReorderPasteboardType) pasteboard.setData(Data(), forType: kLocalReorderPasteboardType)
@ -368,7 +378,7 @@ class ASFileTree : NSObject, NSOutlineViewDataSource {
return itemIsDescendentOfDrag(outlineView: outlineView, item: outlineView.parent(forItem: item) as! ASFileNode) return itemIsDescendentOfDrag(outlineView: outlineView, item: outlineView.parent(forItem: item) as! ASFileNode)
} }
} }
func outlineView(_ outlineView: NSOutlineView, validateDrop info: NSDraggingInfo, proposedItem item: AnyObject?, proposedChildIndex index: Int) -> NSDragOperation { func outlineView(_ outlineView: NSOutlineView, validateDrop info: NSDraggingInfo, proposedItem item: Any?, proposedChildIndex index: Int) -> NSDragOperation {
if info.draggingPasteboard().availableType(from: [kLocalReorderPasteboardType]) == nil { if info.draggingPasteboard().availableType(from: [kLocalReorderPasteboardType]) == nil {
return [] // Only allow reordering drags return [] // Only allow reordering drags
} }
@ -390,7 +400,7 @@ class ASFileTree : NSObject, NSOutlineViewDataSource {
} }
return NSDragOperation.generic return NSDragOperation.generic
} }
func outlineView(_ outlineView: NSOutlineView, acceptDrop info: NSDraggingInfo, item: AnyObject?, childIndex insertAtIndex: Int) -> Bool { func outlineView(_ outlineView: NSOutlineView, acceptDrop info: NSDraggingInfo, item: Any?, childIndex insertAtIndex: Int) -> Bool {
var insertAtIndex = insertAtIndex var insertAtIndex = insertAtIndex
let parent : ASFileGroup = (item as? ASFileGroup) ?? root let parent : ASFileGroup = (item as? ASFileGroup) ?? root
if insertAtIndex == NSOutlineViewDropOnItemIndex { if insertAtIndex == NSOutlineViewDropOnItemIndex {

View File

@ -13,7 +13,7 @@ typealias ASProperties = [String: ASPropertyEntry]
extension NSMenu { extension NSMenu {
func addSortedChoices(choices:[ASPropertyEntry], target: AnyObject, selector: Selector) { func addSortedChoices(choices:[ASPropertyEntry], target: AnyObject, selector: Selector) {
for choice in choices.sorted(by: { $0["name"] < $1["name"] }) { for choice in choices.sorted(by: { $0["name"]! < $1["name"]! }) {
let item = self.addItem(withTitle: choice["name"]!, action: selector, keyEquivalent: "") let item = self.addItem(withTitle: choice["name"]!, action: selector, keyEquivalent: "")
item.target = target item.target = target
} }
@ -189,6 +189,31 @@ class ASLibraries : NSObject {
menuItem.tag = index menuItem.tag = index
} }
} }
func addStandardLibraryExamplesToMenu(menu: NSMenu, sketches: inout [String]) {
addLibraryExamplesToMenu(library: standardLib, menu: menu, sketches: &sketches)
}
func addContribLibraryExamplesToMenu(menu: NSMenu, sketches: inout [String]) {
addLibraryExamplesToMenu(library: contribLib, menu: menu, sketches: &sketches)
}
func addLibraryExamplesToMenu(library: [String], menu: NSMenu, sketches: inout [String]) {
let fileManager = FileManager.default
let application = NSApplication.shared().delegate as! ASApplication
var hasSeparator = false
for (_,lib) in library.enumerated() {
let examplePath = (lib as NSString).appendingPathComponent("examples")
if fileManager.fileExists(atPath: examplePath) {
if !hasSeparator {
menu.addItem(NSMenuItem.separator())
hasSeparator = true
}
let menuItem = menu.addItem(withTitle: (lib as NSString).lastPathComponent, action: nil, keyEquivalent: "")
let submenu = NSMenu()
submenu.autoenablesItems = false
ASSketchBook.addSketches(menu: submenu, target: application, action: #selector(ASApplication.openExample(_:)), path: examplePath, sketches: &sketches)
menu.setSubmenu(submenu, for: menuItem)
}
}
}
@IBAction func importStandardLibrary(_ menuItem: AnyObject) { @IBAction func importStandardLibrary(_ menuItem: AnyObject) {
if let tag = (menuItem as? NSMenuItem)?.tag { if let tag = (menuItem as? NSMenuItem)?.tag {
NSApplication.shared().sendAction(#selector(ASProjDoc.importLibrary(_:)), to: nil, from: standardLib[tag]) NSApplication.shared().sendAction(#selector(ASProjDoc.importLibrary(_:)), to: nil, from: standardLib[tag])

View File

@ -86,8 +86,8 @@ class ASPreferences: NSWindowController, NSOpenSavePanelDelegate {
}) })
} }
func panel(_ sender: AnyObject, shouldEnable url: URL) -> Bool { func panel(_ sender: Any, shouldEnable url: URL) -> Bool {
let gccPath = url.URLByAppendingPathComponent("bin/avr-gcc") let gccPath = url.appendingPathComponent("bin/avr-gcc")
return FileManager.defaultManager().fileExistsAtPath(gccPath.path!) return FileManager.default.fileExists(atPath: gccPath.path)
} }
} }

View File

@ -48,11 +48,11 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate, NSOpenSavePa
dynamic var port : String = "" dynamic var port : String = ""
var recentBoards = [String]() var recentBoards = [String]()
var recentProgrammers = [String]() var recentProgrammers = [String]()
var logModified = NSDate.distantPast var logModified = Date.distantPast
var logSize = 0 var logSize = 0
var updateLogTimer : Timer? var updateLogTimer : Timer?
var printingDone : () -> () = {} var printingDone : () -> () = {}
var printModDate : NSDate? var printModDate : Date?
var printRevision : String? var printRevision : String?
var printShowPanel = false var printShowPanel = false
var jumpingToIssue = false var jumpingToIssue = false
@ -130,7 +130,7 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate, NSOpenSavePa
outline.setDraggingSourceOperationMask(NSDragOperation.every, forLocal: true) outline.setDraggingSourceOperationMask(NSDragOperation.every, forLocal: true)
outline.setDraggingSourceOperationMask([], forLocal: false) outline.setDraggingSourceOperationMask([], forLocal: false)
outline.setDataSource(files) outline.dataSource = files
files.apply() { node in files.apply() { node in
if let group = node as? ASFileGroup { if let group = node as? ASFileGroup {
if group.expanded { if group.expanded {
@ -160,13 +160,13 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate, NSOpenSavePa
func saveCurEditor() { func saveCurEditor() {
if let file = (mainEditor as? ASFileItem) { if let file = (mainEditor as? ASFileItem) {
do { do {
try editor.string().writeToURL(file.url, atomically: true, encoding: String.Encoding.utf8) try editor.string().write(to: file.url, atomically: true, encoding: String.Encoding.utf8)
} catch _ { } catch _ {
} }
} }
} }
override func dataOfType(typeName: String) throws -> NSData { override func data(ofType typeName: String) throws -> Data {
let data = [kVersionKey: kCurVersion, let data = [kVersionKey: kCurVersion,
kThemeKey: ACEThemeNames.name(for: currentTheme), kThemeKey: ACEThemeNames.name(for: currentTheme),
kFontSizeKey: fontSize, kFontSizeKey: fontSize,
@ -176,8 +176,8 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate, NSOpenSavePa
kPortKey: port, kPortKey: port,
kRecentBoardsKey: recentBoards, kRecentBoardsKey: recentBoards,
kRecentProgrammersKey: recentProgrammers kRecentProgrammersKey: recentProgrammers
] ] as [String : Any]
return try PropertyListSerialization.dataWithPropertyList(data, format:.XMLFormat_v1_0, options:0) return try PropertyListSerialization.data(fromPropertyList: data, format:.xml, options:0)
} }
func updateProjectURL() { func updateProjectURL() {
@ -185,44 +185,44 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate, NSOpenSavePa
builder.setProjectURL(url: fileURL!) builder.setProjectURL(url: fileURL!)
} }
func importProject(url: NSURL) throws { func importProject(url: URL) throws {
let existingProject = url.appendingPathComponent(url.lastPathComponent!+".avrsackproj") let existingProject = url.appendingPathComponent(url.lastPathComponent+".avrsackproj")
if existingProject.checkResourceIsReachableAndReturnError(nil) { if let hasProject = try? existingProject.checkResourceIsReachable(), hasProject {
fileURL = existingProject fileURL = existingProject
try readFromURL(url: existingProject, ofType:"Project") try read(from: existingProject, ofType:"Project")
return return
} }
let filesInProject = let filesInProject =
(try FileManagerefaultManager().contentsOfDirectoryAtURL(url, includingPropertiesForKeys: nil, (try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil,
options: .SkipsHiddenFiles)) options: .skipsHiddenFiles))
updateProjectURL() updateProjectURL()
for file in filesInProject { for file in filesInProject {
files.addFileURL(file) files.addFileURL(url: file)
} }
} }
override func readFromURL(url: NSURL, ofType typeName: String) throws { override func read(from url: URL, ofType typeName: String) throws {
if typeName == "Arduino Source File" { if typeName == "Arduino Source File" {
let projectURL = url.URLByDeletingPathExtension!.URLByAppendingPathExtension("avrsackproj") let projectURL = url.deletingPathExtension().appendingPathExtension("avrsackproj")
try importProject(url: url.deletingLastPathComponent!) try importProject(url: url.deletingLastPathComponent())
fileURL = projectURL fileURL = projectURL
try writeToURL(projectURL, ofType: "Project", forSaveOperation: .SaveAsOperation, originalContentsURL: nil) try write(to: projectURL, ofType: "Project", for: .saveAsOperation, originalContentsURL: nil)
} else { } else {
fileURL = url fileURL = url
try super.readFromURL(url, ofType: typeName) try super.read(from: url, ofType: typeName)
} }
} }
override func readFromData(data: NSData, ofType typeName: String) throws { override func read(from data: Data, ofType typeName: String) throws {
if typeName != ("Project" as String) { if typeName != ("Project" as String) {
throw NSError(domain: "AVRSack", code: 0, userInfo: nil) throw NSError(domain: "AVRSack", code: 0, userInfo: nil)
} }
updateProjectURL() updateProjectURL()
let projectData = let projectData =
(try PropertyListSerialization.propertyListWithData(data, options:[], format:nil)) as! NSDictionary (try PropertyListSerialization.propertyList(from: data, options:[], format:nil)) as! NSDictionary
let projectVersion = projectData[kVersionKey] as! Double let projectVersion = projectData[kVersionKey] as! Double
assert(projectVersion <= floor(kCurVersion+1.0), "Project version too new for this app") assert(projectVersion <= floor(kCurVersion+1.0), "Project version too new for this app")
if let themeName = projectData[kThemeKey] as? String { if let themeName = projectData[kThemeKey] as? String {
if let themeId = ACEView.themeIdByName(themeName) { if let themeId = ACEView.themeIdByName(themeName: themeName) {
currentTheme = themeId currentTheme = themeId
} }
} }
@ -238,45 +238,38 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate, NSOpenSavePa
updateChangeCount(.changeCleared) updateChangeCount(.changeCleared)
} }
override func duplicate(_ sender: AnyObject?) { override func duplicate(_ sender: Any?) {
let app = NSApplication.shared().delegate as! ASApplication let app = NSApplication.shared().delegate as! ASApplication
app.openTemplate(fileURL!.URLByDeletingLastPathComponent!, fromReadOnly:false) app.openTemplate(template: fileURL!.deletingLastPathComponent(), fromReadOnly:false)
} }
func updateLog(_: AnyObject?) { func updateLog(_: AnyObject?) {
if let logNode = mainEditor as? ASLogNode { if let logNode = mainEditor as? ASLogNode {
let url = fileURL?.URLByDeletingLastPathComponent?.URLByAppendingPathComponent(logNode.path) guard let fileURL = fileURL else { return }
if url == nil { let url = fileURL.deletingLastPathComponent().appendingPathComponent(logNode.path)
return if let values = try? url.resourceValues(forKeys: [.attributeModificationDateKey, .fileSizeKey]) {
} if values.attributeModificationDate!.compare(logModified) == .orderedDescending
var modified : AnyObject? || values.fileSize! != logSize
var size : AnyObject? {
do { var enc : String.Encoding = .utf8
try url!.getResourceValue(&modified, forKey:NSURLAttributeModificationDateKey) let newText = try? String(contentsOf: url, usedEncoding:&enc)
try url!.getResourceValue(&size, forKey:NSURLFileSizeKey) editor.setString(newText ?? "")
} catch (_) {
return
}
if (modified as! NSDate).compare(logModified) == .orderedDescending || (size as! Int) != logSize {
var enc : UInt = 0
let newText = try? NSString(contentsOfURL:url!, usedEncoding:&enc)
editor.setString((newText as? String) ?? "")
editor.gotoLine(1000000000, column: 0, animated: true) editor.gotoLine(1000000000, column: 0, animated: true)
logModified = modified as! NSDate logModified = values.attributeModificationDate!
logSize = size as! Int logSize = values.fileSize!
currentIssueLine = -1 currentIssueLine = -1
} }
} }
} }
}
func selectNode(selection: ASFileNode?) { func selectNode(selection: ASFileNode?) {
if selection !== mainEditor { if selection !== mainEditor {
saveCurEditor() saveCurEditor()
} }
if let file = (selection as? ASFileItem) { if let file = (selection as? ASFileItem) {
var enc : UInt = 0 var enc : String.Encoding = .utf8
let contents = try? NSString(contentsOfURL:file.url, usedEncoding:&enc) let contents = try? String(contentsOf:file.url, usedEncoding:&enc)
editor.setString(contents as? String ?? "") editor.setString(contents ?? "")
editor.setMode(file.type.aceMode) editor.setMode(file.type.aceMode)
editor.alphaValue = 1.0 editor.alphaValue = 1.0
mainEditor = selection mainEditor = selection
@ -293,13 +286,13 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate, NSOpenSavePa
} }
} }
func selectNodeInOutline(selection: ASFileNode) { func selectNodeInOutline(selection: ASFileNode) {
let selectedIndexes = NSIndexSet(index: outline.row(forItem: selection)) let selectedIndexes = IndexSet(integer: outline.row(forItem: selection))
outline.selectRowIndexes(selectedIndexes, byExtendingSelection: false) outline.selectRowIndexes(selectedIndexes, byExtendingSelection: false)
} }
func selectedFiles() -> [ASFileItem] { func selectedFiles() -> [ASFileItem] {
var selection = [ASFileItem]() var selection = [ASFileItem]()
outline.selectedRowIndexes.enumerateIndexesUsingBlock() { (index, stop) in for index in outline.selectedRowIndexes {
if let file = self.outline.itemAtRow(index) as? ASFileItem { if let file = self.outline.item(atRow: index) as? ASFileItem {
selection.append(file) selection.append(file)
} }
} }
@ -308,22 +301,20 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate, NSOpenSavePa
// MARK: Printing // MARK: Printing
override func print(withSettings printSettings: [String : AnyObject], showPrintPanel: Bool, delegate: AnyObject?, didPrint didPrintSelector: Selector?, contextInfo: UnsafeMutablePointer<Void>) { override func print(withSettings printSettings: [String : Any], showPrintPanel: Bool, delegate: Any?, didPrint didPrintSelector: Selector?, contextInfo: UnsafeMutableRawPointer?) {
printingDone = printingDone =
{ () -> () in { () -> () in
InvokeCallback(delegate, didPrintSelector, contextInfo); InvokeCallback(delegate, didPrintSelector, contextInfo);
} }
if let logNode = mainEditor as? ASLogNode {
printModDate = nil printModDate = nil
if let url = fileURL?.URLByDeletingLastPathComponent?.URLByAppendingPathComponent(logNode.path) { if let logNode = mainEditor as? ASLogNode {
do { if let url = fileURL?.deletingLastPathComponent().appendingPathComponent(logNode.path),
var modified : AnyObject? let values = try? url.resourceValues(forKeys: [.attributeModificationDateKey])
try url.getResourceValue(&modified, forKey:NSURLAttributeModificationDateKey) {
printModDate = modified as? NSDate printModDate = values.attributeModificationDate
} catch (_) {
} }
} }
} else { if printModDate == nil {
printModDate = mainEditor?.modDate() printModDate = mainEditor?.modDate()
} }
printRevision = mainEditor?.revision() printRevision = mainEditor?.revision()
@ -404,7 +395,7 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate, NSOpenSavePa
let pageNoAttr = [ let pageNoAttr = [
NSFontAttributeName: pageNoFont, NSFontAttributeName: pageNoFont,
NSForegroundColorAttributeName: NSColor.white, NSForegroundColorAttributeName: NSColor.white,
NSStrokeWidthAttributeName: -5.0] NSStrokeWidthAttributeName: -5.0] as [String : Any]
let pageNoStr = "\(pageNo)" let pageNoStr = "\(pageNo)"
let pageNoSize = pageNoStr.size(withAttributes: pageNoAttr) let pageNoSize = pageNoStr.size(withAttributes: pageNoAttr)
let pageNoAt = NSPoint( let pageNoAt = NSPoint(
@ -454,10 +445,10 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate, NSOpenSavePa
{ {
let dateFormatter = DateFormatter() let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm" dateFormatter.dateFormat = "yyyy-MM-dd HH:mm"
let modDateStr = dateFormatter.stringFromDate(modDate) let modDateStr = dateFormatter.string(from: modDate)
let modDateSize = modDateStr.sizeWithAttributes(footAttr) let modDateSize = modDateStr.size(withAttributes: footAttr)
footAt.x = rect.origin.x+rect.size.width-modDateSize.width-kXOffset footAt.x = rect.origin.x+rect.size.width-modDateSize.width-kXOffset
modDateStr.drawAtPoint(footAt, withAttributes:footAttr) modDateStr.draw(at: footAt, withAttributes:footAttr)
} }
} }
@ -487,20 +478,20 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate, NSOpenSavePa
group.expanded = false group.expanded = false
updateChangeCount(.changeDone) updateChangeCount(.changeDone)
} }
func outlineView(_ outlineView: NSOutlineView, willDisplayCell cell: AnyObject, for tableColumn: NSTableColumn?, item: AnyObject) { func outlineView(_ outlineView: NSOutlineView, willDisplayCell cell: Any, for tableColumn: NSTableColumn?, item: Any) {
if let textCell = cell as? NSTextFieldCell { if let textCell = cell as? NSTextFieldCell, let item = item as? ASFileNode {
textCell.textColor = NSColor.blackColor textCell.textColor = NSColor.black
if item === files.root || item === files.buildLog || item === files.uploadLog || item === files.disassembly { if item === files.root || item === files.buildLog || item === files.uploadLog || item === files.disassembly {
textCell.font = NSFont.boldSystemFont(ofSize: 13.0) textCell.font = NSFont.boldSystemFont(ofSize: 13.0)
} else { } else {
textCell.font = NSFont.systemFont(ofSize: 13.0) textCell.font = NSFont.systemFont(ofSize: 13.0)
if !(item as! ASFileNode).exists() { if !item.exists() {
textCell.textColor = NSColor.redColor textCell.textColor = NSColor.red
} }
} }
} }
} }
func outlineView(_ outlineView: NSOutlineView, shouldTrackCell cell: NSCell, for tableColumn: NSTableColumn?, item: AnyObject) -> Bool { func outlineView(_ outlineView: NSOutlineView, shouldTrackCell cell: NSCell, for tableColumn: NSTableColumn?, item: Any) -> Bool {
return outlineView.isRowSelected(outlineView.row(forItem: item)) return outlineView.isRowSelected(outlineView.row(forItem: item))
} }
@ -510,7 +501,7 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate, NSOpenSavePa
var name : String var name : String
var ref : String var ref : String
if selection.count == 1 { if selection.count == 1 {
name = "file “\(selection[0].url.lastPathComponent!)" name = "file “\(selection[0].url.lastPathComponent)"
ref = "reference to it" ref = "reference to it"
} else { } else {
name = "\(selection.count) selected files" name = "\(selection.count) selected files"
@ -527,7 +518,7 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate, NSOpenSavePa
alert.beginSheetModal(for: outline.window!) { (response) in alert.beginSheetModal(for: outline.window!) { (response) in
if response != NSAlertThirdButtonReturn { if response != NSAlertThirdButtonReturn {
if response == NSAlertFirstButtonReturn { if response == NSAlertFirstButtonReturn {
NSWorkspace.sharedWorkspace().recycleURLs(selection.map {$0.url}, completionHandler:nil) NSWorkspace.shared().recycle(selection.map {$0.url}, completionHandler:nil)
} }
self.files.apply { (node) in self.files.apply { (node) in
if let group = node as? ASFileGroup { if let group = node as? ASFileGroup {
@ -568,22 +559,23 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate, NSOpenSavePa
} }
func panel(_ panel:AnyObject, shouldEnable url:URL) -> Bool { func panel(_ panel:Any, shouldEnable url:URL) -> Bool {
guard let values = try? url.resourceValues(forKeys: [.fileResourceIdentifierKey]),
let resourceID = values.fileResourceIdentifier
else {
return true
}
var shouldEnable = true var shouldEnable = true
var resourceID : AnyObject?
guard ((try? url.getResourceValue(&resourceID, forKey:NSURLFileResourceIdentifierKey)) != nil) else {
return true;
}
files.apply {(node) in files.apply {(node) in
if let file = node as? ASFileItem { if let file = node as? ASFileItem,
var thisID : AnyObject? let values = try? file.url.resourceValues(forKeys: [.fileResourceIdentifierKey]),
if (try? file.url.getResourceValue(&thisID, forKey:URLResourceKey.fileResourceIdentifierKey)) != nil { let thisID = values.fileResourceIdentifier
if thisID != nil && resourceID!.isEqual(thisID!) { {
if resourceID.isEqual(thisID) {
shouldEnable = false shouldEnable = false
} }
} }
} }
}
return shouldEnable return shouldEnable
} }
@ -591,7 +583,7 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate, NSOpenSavePa
return selectedFiles().count > 0 return selectedFiles().count > 0
} }
func createFileAtURL(url:NSURL) { func createFileAtURL(url: URL) {
let type = ASFileType.guessForURL(url: url) let type = ASFileType.guessForURL(url: url)
var firstPfx = "" var firstPfx = ""
var prefix = "" var prefix = ""
@ -623,13 +615,13 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate, NSOpenSavePa
let dateFmt = DateFormatter() let dateFmt = DateFormatter()
dateFmt.dateFormat = "yyyy-MM-dd" dateFmt.dateFormat = "yyyy-MM-dd"
header = firstPfx + "\n" + header = firstPfx + "\n" +
prefix + " Project: " + fileURL!.URLByDeletingLastPathComponent!.lastPathComponent! + "\n" + prefix + " Project: " + fileURL!.deletingLastPathComponent().lastPathComponent + "\n" +
prefix + " File: " + url.lastPathComponent! + "\n" + prefix + " File: " + url.lastPathComponent + "\n" +
prefix + " Created: " + dateFmt.stringFromDate(NSDate()) + "\n" + prefix + " Created: " + dateFmt.string(from: Date()) + "\n" +
lastPfx + "\n\n" lastPfx + "\n\n"
} }
do { do {
try header.writeToURL(url, atomically: true, encoding: String.Encoding.utf8) try header.write(to: url, atomically: true, encoding: String.Encoding.utf8)
} catch _ { } catch _ {
} }
files.addFileURL(url: url) files.addFileURL(url: url)
@ -675,14 +667,14 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate, NSOpenSavePa
// MARK: Editor configuration // MARK: Editor configuration
@IBAction func changeTheme(item: NSMenuItem) { @IBAction func changeTheme(_ item: NSMenuItem) {
currentTheme = ACETheme(rawValue: UInt(item.tag)) ?? .xcode currentTheme = ACETheme(rawValue: UInt(item.tag)) ?? .xcode
editor.setTheme(currentTheme) editor.setTheme(currentTheme)
UserDefaults.standard.set( UserDefaults.standard.set(
ACEThemeNames.humanName(for: currentTheme), forKey: kThemeKey) ACEThemeNames.humanName(for: currentTheme), forKey: kThemeKey)
updateChangeCount(.changeDone) updateChangeCount(.changeDone)
} }
@IBAction func changeKeyboardHandler(item: NSMenuItem) { @IBAction func changeKeyboardHandler(_ item: NSMenuItem) {
keyboardHandler = ACEKeyboardHandler(rawValue: UInt(item.tag))! keyboardHandler = ACEKeyboardHandler(rawValue: UInt(item.tag))!
UserDefaults.standard.set( UserDefaults.standard.set(
ACEKeyboardHandlerNames.humanName(for: keyboardHandler), forKey: kBindingsKey) ACEKeyboardHandlerNames.humanName(for: keyboardHandler), forKey: kBindingsKey)
@ -691,18 +683,18 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate, NSOpenSavePa
override func validateUserInterfaceItem(_ anItem: NSValidatedUserInterfaceItem) -> Bool { override func validateUserInterfaceItem(_ anItem: NSValidatedUserInterfaceItem) -> Bool {
if let menuItem = anItem as? NSMenuItem { if let menuItem = anItem as? NSMenuItem {
if menuItem.action == "changeTheme:" { if menuItem.action == #selector(ASProjDoc.changeTheme(_:)) {
menuItem.state = (UInt(menuItem.tag) == currentTheme.rawValue ? NSOnState : NSOffState) menuItem.state = (UInt(menuItem.tag) == currentTheme.rawValue ? NSOnState : NSOffState)
return true return true
} else if menuItem.action == "changeKeyboardHandler:" { } else if menuItem.action == #selector(ASProjDoc.changeKeyboardHandler(_:)) {
menuItem.state = (menuItem.tag == Int(keyboardHandler.rawValue) ? NSOnState : NSOffState) menuItem.state = (menuItem.tag == Int(keyboardHandler.rawValue) ? NSOnState : NSOffState)
return true return true
} else if menuItem.action == "serialConnect:" { } else if menuItem.action == #selector(ASProjDoc.serialConnect(_:)) {
menuItem.title = port menuItem.title = port
return true return true
} else if menuItem.action == "importStandardLibrary:" || } else if menuItem.action == #selector(ASLibraries.importStandardLibrary(_:)) ||
menuItem.action == "importContribLibrary:" menuItem.action == #selector(ASLibraries.importContribLibrary(_:))
{ {
return mainEditor is ASFileItem return mainEditor is ASFileItem
} }
@ -724,57 +716,57 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate, NSOpenSavePa
} }
// MARK: Issues // MARK: Issues
@IBAction func jumpToIssue(sender: AnyObject) { @IBAction func jumpToIssue(_ sender: AnyObject) {
let direction : Int = (sender as! NSMenuItem).tag let direction : Int = (sender as! NSMenuItem).tag
if editors.views(in: .bottom).count == 0 { if editors.views(in: .bottom).count == 0 {
editors.addView(auxEdit, in: .bottom) editors.addView(auxEdit, in: .bottom)
let url = fileURL?.URLByDeletingLastPathComponent?.URLByAppendingPathComponent(files.buildLog.path) let url = fileURL?.deletingLastPathComponent().appendingPathComponent(files.buildLog.path)
if url == nil { if url == nil {
return return
} }
var enc : UInt = 0 var enc : String.Encoding = .utf8
let contents = try? NSString(contentsOfURL:url!, usedEncoding:&enc) let contents = try? String(contentsOf:url!, usedEncoding:&enc)
auxEdit.setString(contents as? String ?? "") auxEdit.setString(contents ?? "")
editor.setMode(.text) editor.setMode(.text)
editor.alphaValue = 1.0 editor.alphaValue = 1.0
} }
let buildLog = auxEdit.string().componentsSeparatedByString("\n") let buildLog = auxEdit.string().components(separatedBy: "\n")
let issueRe = try! NSRegularExpression(pattern: "(\\S+?):(\\d+):.*", options: []) let issueRe = try! NSRegularExpression(pattern: "(\\S+?):(\\d+):.*", options: [])
currentIssueLine += direction currentIssueLine += direction
while currentIssueLine > -1 && currentIssueLine < buildLog.count { while currentIssueLine > -1 && currentIssueLine < buildLog.count {
let line = buildLog[currentIssueLine] let line = buildLog[currentIssueLine]
let range = NSMakeRange(0, line.utf16.count) let range = NSMakeRange(0, line.utf16.count)
if let match = issueRe.firstMatchInString(line, options:.Anchored, range:range) { if let match = issueRe.firstMatch(in: line, options:.anchored, range:range) {
let file = match.rangeAtIndex(1) let file = match.rangeAt(1)
let lineTxt = match.rangeAtIndex(2) let lineTxt = match.rangeAt(2)
let nsline = line as NSString let nsline = line as NSString
let lineNo = Int(nsline.substringWithRange(lineTxt))! let lineNo = Int(nsline.substring(with: lineTxt))!
let fileName = nsline.substringWithRange(file) as NSString let fileName = nsline.substring(with: file) as NSString
let fileURL : NSURL let fileURL : URL
if fileName.hasPrefix("../../") { if fileName.hasPrefix("../../") {
fileURL = files.dir.URLByAppendingPathComponent(fileName.substringFromIndex(6)) fileURL = files.dir.appendingPathComponent(fileName.substring(from: 6))
} else { } else {
fileURL = NSURL(fileURLWithPath:fileName as String).URLByStandardizingPath! fileURL = URL(fileURLWithPath:fileName as String).standardizedFileURL
} }
jumpingToIssue = true jumpingToIssue = true
var resourceID : AnyObject? if let values = try? fileURL.resourceValues(forKeys: [.fileResourceIdentifierKey]),
if (try? fileURL.getResourceValue(&resourceID, forKey:URLResourceKey.fileResourceIdentifierKey)) != nil && resourceID != nil { let resourceID = values.fileResourceIdentifier
{
files.apply {(node) in files.apply {(node) in
if let file = node as? ASFileItem { if let file = node as? ASFileItem,
var thisID : AnyObject? let values = try? file.url.resourceValues(forKeys: [.fileResourceIdentifierKey]),
if (try? file.url.getResourceValue(&thisID, forKey:URLResourceKey.fileResourceIdentifierKey)) != nil { let thisID = values.fileResourceIdentifier,
if thisID != nil && resourceID!.isEqual(thisID!) { resourceID.isEqual(thisID)
{
self.selectNodeInOutline(selection: node) self.selectNodeInOutline(selection: node)
self.editor.gotoLine(lineNo, column:0, animated:true) self.editor.gotoLine(lineNo, column:0, animated:true)
} }
} }
} }
}
}
jumpingToIssue = false jumpingToIssue = false
auxEdit.gotoLine(currentIssueLine+1, column:0, animated: true) auxEdit.gotoLine(currentIssueLine+1, column:0, animated: true)
@ -809,11 +801,11 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate, NSOpenSavePa
switch menu.title { switch menu.title {
case "Boards": case "Boards":
ASHardware.instance().buildBoardsMenu(menu: menu, recentBoards: recentBoards, ASHardware.instance().buildBoardsMenu(menu: menu, recentBoards: recentBoards,
target: self, selector: "selectBoard:") target: self, selector: #selector(ASProjDoc.selectBoard(_:)))
boardTool.setTitle(selectedBoard) boardTool.setTitle(selectedBoard)
case "Programmers": case "Programmers":
ASHardware.instance().buildProgrammersMenu(menu: menu, recentProgrammers: recentProgrammers, ASHardware.instance().buildProgrammersMenu(menu: menu, recentProgrammers: recentProgrammers,
target: self, selector: "selectProgrammer:") target: self, selector: #selector(ASProjDoc.selectProgrammer(_:)))
progTool.setTitle(selectedProgrammer) progTool.setTitle(selectedProgrammer)
default: default:
break break
@ -846,7 +838,7 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate, NSOpenSavePa
} }
} }
@IBAction func selectBoard(item: AnyObject) { @IBAction func selectBoard(_ item: AnyObject) {
selectedBoard = (item as! NSMenuItem).title selectedBoard = (item as! NSMenuItem).title
} }
@ -877,11 +869,11 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate, NSOpenSavePa
} }
} }
@IBAction func selectProgrammer(item: AnyObject) { @IBAction func selectProgrammer(_ item: AnyObject) {
selectedProgrammer = (item as! NSMenuItem).title selectedProgrammer = (item as! NSMenuItem).title
} }
@IBAction func selectPort(item: AnyObject) { @IBAction func selectPort(_ item: AnyObject) {
port = (item as! NSPopUpButton).titleOfSelectedItem! port = (item as! NSPopUpButton).titleOfSelectedItem!
portTool.setTitle(port) portTool.setTitle(port)
} }
@ -917,26 +909,26 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate, NSOpenSavePa
return NSSet(objects: "hasValidPort", "hasUploadProtocol", "programmer") return NSSet(objects: "hasValidPort", "hasUploadProtocol", "programmer")
} }
@IBAction func uploadProject(sender: AnyObject) { @IBAction func uploadProject(_ sender: AnyObject) {
builder.continuation = { builder.continuation = {
self.selectNodeInOutline(selection: self.files.uploadLog) self.selectNodeInOutline(selection: self.files.uploadLog)
dispatch_async(dispatch_get_main_queue(), { DispatchQueue.main.async(execute: {
self.builder.uploadProject(board: self.board, programmer:self.programmer, port:self.port) self.builder.uploadProject(board: self.board, programmer:self.programmer, port:self.port)
}) })
} }
buildProject(sender) buildProject(sender)
} }
@IBAction func uploadTerminal(sender: AnyObject) { @IBAction func uploadTerminal(_: AnyObject) {
builder.uploadProject(board: board, programmer:programmer, port:port, mode:.Interactive) builder.uploadProject(board: board, programmer:programmer, port:port, mode:.Interactive)
} }
@IBAction func burnBootloader(sender: AnyObject) { @IBAction func burnBootloader(_: AnyObject) {
self.selectNodeInOutline(selection: self.files.uploadLog) self.selectNodeInOutline(selection: self.files.uploadLog)
builder.uploadProject(board: board, programmer:programmer, port:port, mode:.BurnBootloader) builder.uploadProject(board: board, programmer:programmer, port:port, mode:.BurnBootloader)
} }
@IBAction func disassembleProject(sender: AnyObject) { @IBAction func disassembleProject(_ sender: AnyObject) {
builder.continuation = { builder.continuation = {
self.selectNodeInOutline(selection: self.files.disassembly) self.selectNodeInOutline(selection: self.files.disassembly)
self.builder.disassembleProject(board: self.board) self.builder.disassembleProject(board: self.board)
@ -944,7 +936,7 @@ class ASProjDoc: NSDocument, NSOutlineViewDelegate, NSMenuDelegate, NSOpenSavePa
buildProject(sender) buildProject(sender)
} }
@IBAction func serialConnect(sender: AnyObject) { @IBAction func serialConnect(_: AnyObject) {
ASSerialWin.showWindowWithPort(port: port) ASSerialWin.showWindowWithPort(port: port)
} }
} }

View File

@ -37,14 +37,14 @@ NSString * kASSerialPortsChanged = @"PortsChanged";
watchSlashDev = watchSlashDev =
dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, DISPATCH_VNODE_WRITE, dispatch_get_main_queue()); dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, DISPATCH_VNODE_WRITE, dispatch_get_main_queue());
dispatch_source_set_event_handler(watchSlashDev, ^{ dispatch_source_set_event_handler(watchSlashDev, ^{
[[NotificationCenter defaultCenter] postNotificationName:kASSerialPortsChanged object: nil]; [[NSNotificationCenter defaultCenter] postNotificationName:kASSerialPortsChanged object: nil];
}); });
dispatch_resume(watchSlashDev); dispatch_resume(watchSlashDev);
} }
+ (NSArray<NSString *> *)ports { + (NSArray<NSString *> *)ports {
NSMutableArray * cuPorts = [NSMutableArray array]; NSMutableArray * cuPorts = [NSMutableArray array];
for (NSString * port in [[FileManager defaultManager] contentsOfDirectoryAtPath:@"/dev" error: nil]) { for (NSString * port in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:@"/dev" error: nil]) {
if ([[port substringToIndex:2] isEqualToString:@"cu"]) if ([[port substringToIndex:2] isEqualToString:@"cu"])
[cuPorts addObject:[port substringFromIndex:3]]; [cuPorts addObject:[port substringFromIndex:3]];
} }

View File

@ -15,6 +15,7 @@ class ASSerialWin: NSWindowController {
@IBOutlet weak var inputLine : NSTextField! @IBOutlet weak var inputLine : NSTextField!
@IBOutlet weak var logView : ACEView! @IBOutlet weak var logView : ACEView!
var portDefaults = [String: Any]()
var baudRate : Int = 9600 { var baudRate : Int = 9600 {
didSet(oldRate) { didSet(oldRate) {
if portHandle != nil { if portHandle != nil {
@ -41,9 +42,8 @@ class ASSerialWin: NSWindowController {
dynamic var portHandle : FileHandle? dynamic var portHandle : FileHandle?
var currentTheme : ACETheme = .xcode var currentTheme : ACETheme = .xcode
var fontSize : UInt = 12 var fontSize : UInt = 12
var portDefaults = [String: AnyObject]()
var shouldReconnect = false var shouldReconnect = false
dynamic var task : Task? dynamic var task : Process?
class func showWindowWithPort(port: String) { class func showWindowWithPort(port: String) {
if let existing = serialInstances[port] { if let existing = serialInstances[port] {
@ -54,7 +54,7 @@ class ASSerialWin: NSWindowController {
newInstance.showWindow(self) newInstance.showWindow(self)
} }
} }
class func showWindowWithPort(port: String, task: Task, speed: Int) { class func showWindowWithPort(port: String, task: Process, speed: Int) {
if let existing = serialInstances[port] { if let existing = serialInstances[port] {
existing.showWindowWithTask(task: task, speed:speed) existing.showWindowWithTask(task: task, speed:speed)
} else { } else {
@ -116,10 +116,10 @@ class ASSerialWin: NSWindowController {
} }
} }
}) })
termination = NotificationCenter.default.addObserver(forName: Task.didTerminateNotification, termination = NotificationCenter.default.addObserver(forName: Process.didTerminateNotification,
object: nil, queue: nil, using: object: nil, queue: nil, using:
{ (notification: Notification) in { (notification: Notification) in
if notification.object as? Task == self.task { if notification.object as? Process == self.task {
self.task = nil self.task = nil
self.portHandle = nil self.portHandle = nil
} }
@ -161,10 +161,11 @@ class ASSerialWin: NSWindowController {
serialData = "" serialData = ""
logView.setString(serialData) logView.setString(serialData)
readHandle.readabilityHandler = {(handle) in readHandle.readabilityHandler = {(handle) in
let newData = handle.availableDataIgnoringExceptions() if let newData = handle.availableDataIgnoringExceptions(),
let newString = NSString(data: newData, encoding: String.Encoding.ascii) as! String let newString = String(data: newData, encoding: String.Encoding.ascii)
{
self.serialData += newString self.serialData += newString
DispatchQueue.main.async(execute: { () -> Void in DispatchQueue.main.async(execute: {
self.logView.setString(self.serialData) self.logView.setString(self.serialData)
if self.scrollToBottom { if self.scrollToBottom {
self.logView.gotoLine(1000000000, column: 0, animated: true) self.logView.gotoLine(1000000000, column: 0, animated: true)
@ -173,6 +174,7 @@ class ASSerialWin: NSWindowController {
} }
} }
} }
}
@IBAction func sendInput(_: AnyObject) { @IBAction func sendInput(_: AnyObject) {
let line = inputLine.stringValue + (sendCR ? "\r" : "") + (sendLF ? "\n" : "") let line = inputLine.stringValue + (sendCR ? "\r" : "") + (sendLF ? "\n" : "")
@ -180,7 +182,7 @@ class ASSerialWin: NSWindowController {
portHandle?.write(data) portHandle?.write(data)
} }
func showWindowWithTask(task: Task, speed:Int) { func showWindowWithTask(task: Process, speed:Int) {
if portHandle != nil { if portHandle != nil {
connect(self) connect(self)
} }
@ -233,7 +235,7 @@ class ASSerialWin: NSWindowController {
// MARK: Editor configuration // MARK: Editor configuration
@IBAction func changeTheme(item: NSMenuItem) { @IBAction func changeTheme(_ item: NSMenuItem) {
let userDefaults = UserDefaults.standard let userDefaults = UserDefaults.standard
currentTheme = ACETheme(rawValue: UInt(item.tag)) ?? .xcode currentTheme = ACETheme(rawValue: UInt(item.tag)) ?? .xcode
logView.setTheme(currentTheme) logView.setTheme(currentTheme)
@ -242,7 +244,7 @@ class ASSerialWin: NSWindowController {
portDefaults["Theme"] = themeName portDefaults["Theme"] = themeName
updatePortDefaults() updatePortDefaults()
} }
@IBAction func changeKeyboardHandler(item: NSMenuItem) { @IBAction func changeKeyboardHandler(_ item: NSMenuItem) {
keyboardHandler = ACEKeyboardHandler(rawValue: UInt(item.tag))! keyboardHandler = ACEKeyboardHandler(rawValue: UInt(item.tag))!
UserDefaults.standard.set( UserDefaults.standard.set(
ACEKeyboardHandlerNames.humanName(for: keyboardHandler), forKey: "Bindings") ACEKeyboardHandlerNames.humanName(for: keyboardHandler), forKey: "Bindings")
@ -251,10 +253,10 @@ class ASSerialWin: NSWindowController {
func validateUserInterfaceItem(anItem: NSValidatedUserInterfaceItem) -> Bool { func validateUserInterfaceItem(anItem: NSValidatedUserInterfaceItem) -> Bool {
if let menuItem = anItem as? NSMenuItem { if let menuItem = anItem as? NSMenuItem {
if menuItem.action == Selector(("changeTheme:")) { if menuItem.action == #selector(ASSerialWin.changeTheme(_:)) {
menuItem.state = (UInt(menuItem.tag) == currentTheme.rawValue ? NSOnState : NSOffState) menuItem.state = (UInt(menuItem.tag) == currentTheme.rawValue ? NSOnState : NSOffState)
return true return true
} else if menuItem.action == Selector(("changeKeyboardHandler:")) { } else if menuItem.action == #selector(ASSerialWin.changeKeyboardHandler(_:)) {
menuItem.state = (menuItem.tag == Int(keyboardHandler.rawValue) ? NSOnState : NSOffState) menuItem.state = (menuItem.tag == Int(keyboardHandler.rawValue) ? NSOnState : NSOffState)
return true return true
} }

View File

@ -78,9 +78,9 @@ def parseInoFiles
# Find protypes: # Find protypes:
prototypes = contents.dup prototypes = contents.dup
# - Strip comments, quoted strings, and preprocessor directives # - Strip comments, quoted strings, and preprocessor directives
prototypes.gsub!(%r{'(?:[^']|\\')+'|"(?:[^"]|\\")*"|//.*?$|/\*.*?\*/|^\s*?#.*?$}m, ' ') prototypes.gsub!(%r{'(?:\\'|[^'])+'|"(?:\\"|[^"])*"|//.*?$|/\*.*?\*/|^\s*?#.*?$}m, ' ')
# Collapse braces # Collapse braces
while prototypes.sub!(/(\{)(?:[^{}]+|\{[^{}]*\})/m, '\1') do while prototypes.sub!(/(\{)([^{}]+|\{[^{}]*\})/m, '\1') do
end end
existingProto = {} existingProto = {}
prototypes.scan(/[\w\[\]\*]+\s+[&\[\]\*\w\s]+\([&,\[\]\*\w\s]*\)(?=\s*;)/) {|p| prototypes.scan(/[\w\[\]\*]+\s+[&\[\]\*\w\s]+\([&,\[\]\*\w\s]*\)(?=\s*;)/) {|p|
@ -92,7 +92,7 @@ def parseInoFiles
proto << p+";\n" unless existingProto[p] proto << p+";\n" unless existingProto[p]
} }
contents.each_line do |line| contents.each_line do |line|
if line =~ /^\s*#include\s+[<"](.*)[">]\s*(#.*)?$/ if line =~ %r{^\s*#include\s+[<"](.*)[">]\s*(#.*|/\*.*?\*/\s*|//.*)?$}
addLibrary($1) addLibrary($1)
end end
end end

20
README.md Normal file
View File

@ -0,0 +1,20 @@
# AVRsack
`AVRsack` is an integrated development environment for the Atmel AVR series of microcontrollers.
It is designed to be an alternative for the Arduino 1.0.x application, offering:
* A more powerful text editor with a variety of styles.</li>
* A more powerful build system with incremental rebuilds.</li>
* A more powerful upload system with finer grained control over logging.</li>
* Full build/upload logs without arbitrary size restrictions.</li>
* Support for handling multiple simultaneous serial ports.</li>
* Support for assembly programming and disassembly of object code.</li>
* Support for alternate AVR toolchains, such as [Crosspack-AVR](https://www.obdev.at/products/crosspack/index.html)
`AVRsack` was written and designed by Matthias Neeracher <microtherion@gmail.com>
For editing text, `AVRsack` relies on Michael Robinson's [ACEView](https://github.com/faceleg/ACEView),
a Cocoa wrapper for the [Ace](http://ace.c9.io) code editor.
![Basic screenshot]
(https://github.com/microtherion/AVRsack/raw/master/Screenshots/AVRsack.png)

BIN
Screenshots/AVRsack.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 KiB