2014-11-16 18:39:32 +00:00
|
|
|
//
|
|
|
|
// ASFileTree.swift
|
|
|
|
// AVRsack
|
|
|
|
//
|
|
|
|
// Created by Matthias Neeracher on 11/16/14.
|
|
|
|
// Copyright (c) 2014 Aere Perennius. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
import Foundation
|
|
|
|
|
|
|
|
enum ASFileType : String {
|
|
|
|
case Unknown = ""
|
|
|
|
case Header = "source.h"
|
|
|
|
case CFile = "source.c"
|
|
|
|
case Arduino = "source.ino"
|
|
|
|
case CppFile = "source.c++"
|
|
|
|
case AsmFile = "source.asm"
|
|
|
|
case Markdown = "doc.md"
|
|
|
|
|
|
|
|
static func guessForURL(url: NSURL) -> ASFileType {
|
2015-02-04 01:12:47 +00:00
|
|
|
switch url.pathExtension!.lowercaseString {
|
2014-11-16 18:39:32 +00:00
|
|
|
case "hpp", "hh", "h":
|
|
|
|
return .Header
|
|
|
|
case "c":
|
|
|
|
return .CFile
|
|
|
|
case "ino":
|
|
|
|
return .Arduino
|
|
|
|
case "cpp", "c++", "cxx", "cc":
|
|
|
|
return .CppFile
|
|
|
|
case "s":
|
|
|
|
return .AsmFile
|
|
|
|
case "md":
|
|
|
|
return .Markdown
|
|
|
|
default:
|
|
|
|
return .Unknown
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var aceMode : ACEMode {
|
|
|
|
switch self {
|
|
|
|
case .Header,.CFile,.CppFile,.Arduino:
|
|
|
|
return ACEModeCPP
|
|
|
|
case .Markdown:
|
|
|
|
return ACEModeMarkdown
|
|
|
|
default:
|
2014-12-31 03:16:43 +00:00
|
|
|
return ACEModeText
|
2014-11-16 18:39:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-17 01:29:55 +00:00
|
|
|
private let kTypeKey = "Type"
|
|
|
|
private let kNodeTypeProject = "Project"
|
|
|
|
private let kNodeTypeGroup = "Group"
|
|
|
|
private let kNodeTypeFile = "File"
|
|
|
|
private let kNameKey = "Name"
|
|
|
|
|
2014-11-16 18:39:32 +00:00
|
|
|
class ASFileNode {
|
|
|
|
func nodeName() -> String {
|
|
|
|
return ""
|
|
|
|
}
|
2014-11-16 20:10:04 +00:00
|
|
|
func apply(closure:(ASFileNode)->()) {
|
|
|
|
closure(self)
|
|
|
|
}
|
2015-02-14 16:43:20 +00:00
|
|
|
func propertyList(rootPath: String) -> AnyObject {
|
2014-11-17 01:29:55 +00:00
|
|
|
return ""
|
|
|
|
}
|
|
|
|
class func readPropertyList(prop: NSDictionary, rootURL: NSURL) -> ASFileNode {
|
2015-02-14 16:43:20 +00:00
|
|
|
switch prop[kTypeKey] as! String {
|
2014-11-17 01:29:55 +00:00
|
|
|
case kNodeTypeProject:
|
|
|
|
return ASProject(prop, withRootURL:rootURL)
|
|
|
|
case kNodeTypeGroup:
|
|
|
|
return ASFileGroup(prop, withRootURL:rootURL)
|
|
|
|
case kNodeTypeFile:
|
|
|
|
return ASFileItem(prop, withRootURL:rootURL)
|
|
|
|
default:
|
|
|
|
assertionFailure("Undefined item type in file hierarchy")
|
|
|
|
}
|
|
|
|
}
|
2015-02-14 16:43:20 +00:00
|
|
|
func paths(rootPath: String) -> [String] {
|
|
|
|
return [String]()
|
2014-11-28 13:18:53 +00:00
|
|
|
}
|
2015-01-11 01:53:50 +00:00
|
|
|
func exists() -> Bool {
|
|
|
|
return true
|
|
|
|
}
|
2014-11-16 18:39:32 +00:00
|
|
|
}
|
|
|
|
|
2014-12-01 02:34:53 +00:00
|
|
|
class ASLogNode : ASFileNode {
|
|
|
|
var name : String
|
|
|
|
var path : String
|
|
|
|
|
|
|
|
init(name: String, path: String) {
|
|
|
|
self.name = name
|
|
|
|
self.path = path
|
|
|
|
}
|
|
|
|
override func nodeName() -> String {
|
|
|
|
return "📜 "+name
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-16 18:39:32 +00:00
|
|
|
class ASFileGroup : ASFileNode {
|
|
|
|
var name : String
|
|
|
|
var children : [ASFileNode]
|
|
|
|
var expanded : Bool
|
2014-11-17 01:29:55 +00:00
|
|
|
|
|
|
|
private let kChildrenKey = "Children"
|
|
|
|
private let kExpandedKey = "Expanded"
|
|
|
|
private var kNodeType : String { return kNodeTypeGroup }
|
|
|
|
|
|
|
|
init(name: String = "") {
|
2014-11-16 18:39:32 +00:00
|
|
|
self.name = name
|
|
|
|
self.children = []
|
|
|
|
self.expanded = true
|
|
|
|
}
|
2014-11-17 01:29:55 +00:00
|
|
|
init(_ prop: NSDictionary, withRootURL rootURL: NSURL) {
|
2015-02-14 16:43:20 +00:00
|
|
|
name = prop[kNameKey] as! String
|
|
|
|
expanded = prop[kExpandedKey] as! Bool
|
2014-11-17 01:29:55 +00:00
|
|
|
children = []
|
2015-02-14 16:43:20 +00:00
|
|
|
for child in (prop[kChildrenKey] as! [NSDictionary]) {
|
|
|
|
children.append(ASFileNode.readPropertyList(child, rootURL: rootURL))
|
2014-11-17 01:29:55 +00:00
|
|
|
}
|
2014-11-16 18:39:32 +00:00
|
|
|
}
|
|
|
|
override func nodeName() -> String {
|
|
|
|
return (expanded ? "📂" : "📁")+" "+name
|
|
|
|
}
|
2014-11-16 20:10:04 +00:00
|
|
|
override func apply(closure: (ASFileNode) -> ()) {
|
|
|
|
super.apply(closure)
|
|
|
|
for child in children {
|
|
|
|
child.apply(closure)
|
|
|
|
}
|
|
|
|
}
|
2015-02-14 16:43:20 +00:00
|
|
|
func childrenPropertyList(rootPath: String) -> [AnyObject] {
|
2014-11-17 01:29:55 +00:00
|
|
|
return children.map() { (node) in node.propertyList(rootPath) }
|
|
|
|
}
|
2015-02-14 16:43:20 +00:00
|
|
|
override func propertyList(rootPath: String) -> AnyObject {
|
2014-11-17 01:29:55 +00:00
|
|
|
return [kTypeKey: kNodeType, kNameKey: name, kExpandedKey: expanded,
|
|
|
|
kChildrenKey: childrenPropertyList(rootPath)]
|
|
|
|
}
|
2015-02-14 16:43:20 +00:00
|
|
|
override func paths(rootPath: String) -> [String] {
|
|
|
|
var allPaths = [String]()
|
2014-11-28 13:18:53 +00:00
|
|
|
for child in children {
|
|
|
|
allPaths += child.paths(rootPath)
|
|
|
|
}
|
|
|
|
return allPaths
|
|
|
|
}
|
2014-11-16 20:10:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
class ASProject : ASFileGroup {
|
2014-11-17 01:29:55 +00:00
|
|
|
override private var kNodeType : String { return kNodeTypeProject }
|
|
|
|
|
2014-11-16 20:10:04 +00:00
|
|
|
override func nodeName() -> String {
|
|
|
|
return "📘 "+name
|
|
|
|
}
|
2014-11-16 18:39:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
class ASFileItem : ASFileNode {
|
|
|
|
var url : NSURL
|
|
|
|
var type : ASFileType
|
2014-11-17 01:29:55 +00:00
|
|
|
|
|
|
|
private let kPathKey = "Path"
|
|
|
|
private let kKindKey = "Kind"
|
|
|
|
|
2014-11-16 18:39:32 +00:00
|
|
|
init(url: NSURL, type: ASFileType) {
|
|
|
|
self.url = url
|
|
|
|
self.type = type
|
|
|
|
}
|
2014-11-17 01:29:55 +00:00
|
|
|
init(_ prop: NSDictionary, withRootURL rootURL: NSURL) {
|
2015-02-14 16:43:20 +00:00
|
|
|
type = ASFileType(rawValue: prop[kKindKey] as! String)!
|
|
|
|
if let relativeURL = NSURL(string: prop[kPathKey] as! String, relativeToURL: rootURL) {
|
2014-12-22 06:00:05 +00:00
|
|
|
url = relativeURL.standardizedURL!
|
|
|
|
} else {
|
2015-02-14 16:43:20 +00:00
|
|
|
url = NSURL(fileURLWithPath:(prop[kPathKey] as! String))!.standardizedURL!
|
2014-12-22 06:00:05 +00:00
|
|
|
}
|
2014-11-17 01:29:55 +00:00
|
|
|
}
|
2014-11-16 18:39:32 +00:00
|
|
|
override func nodeName() -> String {
|
2015-02-04 01:12:47 +00:00
|
|
|
return "📄 "+url.lastPathComponent!
|
2014-11-16 18:39:32 +00:00
|
|
|
}
|
2014-11-17 01:29:55 +00:00
|
|
|
|
|
|
|
func relativePath(relativeTo: String) -> String {
|
|
|
|
let path = url.path!
|
|
|
|
let relComp = relativeTo.componentsSeparatedByString("/") as [String]
|
|
|
|
let pathComp = path.componentsSeparatedByString("/") as [String]
|
|
|
|
let relCount = relComp.count
|
|
|
|
let pathCount = pathComp.count
|
|
|
|
|
|
|
|
var matchComp = 0
|
|
|
|
while (matchComp < relCount && matchComp < pathCount) {
|
|
|
|
if pathComp[matchComp] == relComp[matchComp] {
|
|
|
|
++matchComp
|
|
|
|
} else {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if matchComp==1 {
|
|
|
|
return path
|
|
|
|
}
|
|
|
|
|
|
|
|
let resComp = Array(count: relCount-matchComp, repeatedValue: "..")+pathComp[matchComp..<pathCount]
|
|
|
|
return "/".join(resComp)
|
|
|
|
}
|
2015-02-14 16:43:20 +00:00
|
|
|
override func propertyList(rootPath: String) -> AnyObject {
|
2014-11-17 01:29:55 +00:00
|
|
|
return [kTypeKey: kNodeTypeFile, kKindKey: type.rawValue, kPathKey: relativePath(rootPath)]
|
|
|
|
}
|
2015-02-14 16:43:20 +00:00
|
|
|
override func paths(rootPath: String) -> [String] {
|
2014-11-28 13:18:53 +00:00
|
|
|
return [relativePath(rootPath)]
|
|
|
|
}
|
2015-01-11 01:53:50 +00:00
|
|
|
override func exists() -> Bool {
|
|
|
|
return url.checkResourceIsReachableAndReturnError(nil)
|
|
|
|
}
|
2014-11-16 18:39:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
class ASFileTree : NSObject, NSOutlineViewDataSource {
|
2014-12-01 02:34:53 +00:00
|
|
|
var root = ASProject()
|
|
|
|
var dir = NSURL()
|
|
|
|
var buildLog = ASLogNode(name: "Build Log", path: "build/build.log")
|
|
|
|
var uploadLog = ASLogNode(name: "Upload Log", path: "build/upload.log")
|
2014-12-10 14:30:51 +00:00
|
|
|
var disassembly = ASLogNode(name: "Disassembly", path: "build/disasm.log")
|
2014-11-16 18:39:32 +00:00
|
|
|
|
|
|
|
func addFileURL(url: NSURL, omitUnknown: Bool = true) {
|
|
|
|
let type = ASFileType.guessForURL(url)
|
|
|
|
if !omitUnknown || type != .Unknown {
|
2014-11-17 01:29:55 +00:00
|
|
|
root.children.append(ASFileItem(url: url.standardizedURL!, type: type))
|
2014-11-16 18:39:32 +00:00
|
|
|
}
|
|
|
|
}
|
2014-11-16 20:10:04 +00:00
|
|
|
func setProjectURL(url: NSURL) {
|
2015-02-04 01:12:47 +00:00
|
|
|
root.name = url.lastPathComponent!.stringByDeletingPathExtension
|
2014-11-17 01:29:55 +00:00
|
|
|
dir = url.URLByDeletingLastPathComponent!.standardizedURL!
|
2014-11-16 20:10:04 +00:00
|
|
|
}
|
|
|
|
func apply(closure: (ASFileNode) -> ()) {
|
|
|
|
root.apply(closure)
|
|
|
|
}
|
2014-11-17 01:29:55 +00:00
|
|
|
func propertyList() -> AnyObject {
|
|
|
|
return root.propertyList(dir.path!)
|
|
|
|
}
|
|
|
|
func readPropertyList(prop: NSDictionary) {
|
2015-02-14 16:43:20 +00:00
|
|
|
root = ASFileNode.readPropertyList(prop, rootURL:dir) as! ASProject
|
2014-11-17 01:29:55 +00:00
|
|
|
}
|
2015-02-14 16:43:20 +00:00
|
|
|
var paths : [String] {
|
2014-11-28 13:18:53 +00:00
|
|
|
return root.paths(dir.path!)
|
|
|
|
}
|
2014-11-16 18:39:32 +00:00
|
|
|
|
2014-11-17 01:29:55 +00:00
|
|
|
// MARK: Outline Data Source
|
2014-11-16 18:39:32 +00:00
|
|
|
func outlineView(outlineView: NSOutlineView, numberOfChildrenOfItem item: AnyObject?) -> Int {
|
|
|
|
if item == nil {
|
2014-12-10 14:30:51 +00:00
|
|
|
return 4
|
2014-11-16 18:39:32 +00:00
|
|
|
} else {
|
2015-02-14 16:43:20 +00:00
|
|
|
return (item as! ASFileGroup).children.count
|
2014-11-16 18:39:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
func outlineView(outlineView: NSOutlineView, child index: Int, ofItem item: AnyObject?) -> AnyObject {
|
2014-11-16 20:10:04 +00:00
|
|
|
if item == nil {
|
2014-12-01 02:34:53 +00:00
|
|
|
switch index {
|
|
|
|
case 1:
|
|
|
|
return buildLog
|
|
|
|
case 2:
|
|
|
|
return uploadLog
|
2014-12-10 14:30:51 +00:00
|
|
|
case 3:
|
|
|
|
return disassembly
|
2014-12-01 02:34:53 +00:00
|
|
|
default:
|
|
|
|
return root
|
|
|
|
}
|
2014-11-16 20:10:04 +00:00
|
|
|
} else {
|
2015-02-14 16:43:20 +00:00
|
|
|
let group = item as! ASFileGroup
|
2014-11-16 20:10:04 +00:00
|
|
|
return group.children[index]
|
|
|
|
}
|
2014-11-16 18:39:32 +00:00
|
|
|
}
|
|
|
|
func outlineView(outlineView: NSOutlineView, isItemExpandable item: AnyObject) -> Bool {
|
|
|
|
return item is ASFileGroup
|
|
|
|
}
|
|
|
|
func outlineView(outlineView: NSOutlineView, objectValueForTableColumn tableColumn: NSTableColumn?, byItem item: AnyObject?) -> AnyObject? {
|
2015-02-14 16:43:20 +00:00
|
|
|
return (item as! ASFileNode).nodeName()
|
2014-11-16 18:39:32 +00:00
|
|
|
}
|
|
|
|
}
|