1
1
mirror of https://github.com/github/semantic.git synced 2024-12-20 21:31:48 +03:00
semantic/prototype/doubt-difftool/main.swift

116 lines
4.1 KiB
Swift
Raw Normal View History

import Cocoa
import Doubt
2015-10-28 01:38:25 +03:00
import Prelude
2015-10-27 18:04:39 +03:00
2015-10-29 01:17:01 +03:00
extension String: ErrorType {}
2015-10-29 01:12:16 +03:00
typealias Term = Cofree<String, Info>
2015-10-29 23:20:57 +03:00
typealias Parser = String throws -> Term
2015-10-28 01:37:34 +03:00
2015-10-29 23:04:10 +03:00
struct Source {
init(_ argument: String) throws {
2015-10-29 23:06:51 +03:00
URL = NSURL(string: argument) ?? NSURL(fileURLWithPath: argument)
guard let type = URL.pathExtension else { throw "cannot tell the type of \(URL)" }
2015-10-29 23:06:51 +03:00
self.type = type
2015-10-29 23:50:51 +03:00
contents = try NSString(contentsOfURL: URL, encoding: NSUTF8StringEncoding) as String
2015-10-29 23:06:51 +03:00
}
2015-10-29 23:04:10 +03:00
let URL: NSURL
2015-10-29 23:04:51 +03:00
let type: String
2015-10-29 23:50:51 +03:00
let contents: String
2015-10-29 23:10:01 +03:00
private static let languagesByType: [String:TSLanguage] = [
"js": ts_language_javascript(),
"c": ts_language_c(),
"h": ts_language_c(),
]
2015-10-29 23:04:10 +03:00
}
extension String.UTF16View {
subscript (range: Range<Int>) -> String.UTF16View {
return self[Index(_offset: range.startIndex)..<Index(_offset: range.endIndex)]
}
}
2015-10-29 22:09:57 +03:00
/// Allow predicates to occur in pattern matching.
func ~= <A> (left: A -> Bool, right: A) -> Bool {
return left(right)
}
2015-10-29 23:23:58 +03:00
func termWithInput(language: TSLanguage)(_ string: String) throws -> Term {
let keyedProductions: Set<String> = [ "object" ]
let fixedProductions: Set<String> = [ "pair", "rel_op", "math_op", "bool_op", "bitwise_op", "type_op", "math_assignment", "assignment", "subscript_access", "member_access", "new_expression", "function_call", "function", "ternary" ]
2015-10-27 18:04:18 +03:00
let document = ts_document_make()
2015-10-28 01:26:39 +03:00
defer { ts_document_free(document) }
2015-10-29 23:23:58 +03:00
return try string.withCString {
2015-10-29 22:20:59 +03:00
ts_document_set_language(document, language)
2015-10-28 01:29:44 +03:00
ts_document_set_input_string(document, $0)
ts_document_parse(document)
let root = ts_document_root_node(document)
2015-10-28 01:07:00 +03:00
2015-10-29 23:23:58 +03:00
return try Cofree
2015-10-28 21:20:46 +03:00
.ana { node, category in
let count = node.namedChildren.count
guard count > 0 else { return try Syntax.Leaf(node.substring(string)) }
2015-10-28 22:05:25 +03:00
switch category {
2015-10-29 22:12:16 +03:00
case fixedProductions.contains:
2015-10-28 23:20:39 +03:00
return try .Fixed(node.namedChildren.map {
($0, try $0.category(document))
2015-10-28 23:20:39 +03:00
})
2015-10-29 22:10:37 +03:00
case keyedProductions.contains:
2015-10-28 23:18:38 +03:00
return try .Keyed(Dictionary(elements: node.namedChildren.map {
switch try $0.category(document) {
case "pair":
2015-10-29 17:03:53 +03:00
return try ($0.namedChildren[0].substring(string), ($0, "pair"))
default:
2015-10-29 01:33:04 +03:00
// We might have a comment inside an object literal. It should still be assigned a key, however.
return try (try node.substring(string), ($0, $0.category(document)))
}
2015-10-28 23:18:38 +03:00
}))
2015-10-28 22:05:25 +03:00
default:
2015-10-28 22:31:08 +03:00
return try .Indexed(node.namedChildren.map {
($0, try $0.category(document))
2015-10-28 22:05:25 +03:00
})
}
2015-10-29 00:48:51 +03:00
} (root, "program")
2015-10-28 21:20:46 +03:00
.map { node, category in
2015-10-29 01:12:16 +03:00
Info(range: node.range, categories: [ category ])
2015-10-28 01:10:17 +03:00
}
2015-10-28 01:29:44 +03:00
}
2015-10-28 01:26:39 +03:00
}
2015-10-28 01:10:17 +03:00
2015-10-28 01:26:39 +03:00
let arguments = BoundsCheckedArray(array: Process.arguments)
guard let aSource = try arguments[1].map(Source.init) else { throw "need source A" }
guard let bSource = try arguments[2].map(Source.init) else { throw "need source B" }
let jsonURL = NSURL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).URLByAppendingPathComponent("diff.json")
2015-10-30 01:23:32 +03:00
guard let uiPath = NSBundle.mainBundle().infoDictionary?["PathToUISource"] as? String else { throw "need ui path" }
guard aSource.type == bSource.type else { throw "cant compare files of different types" }
2015-10-29 23:25:01 +03:00
guard let parser = Source.languagesByType[aSource.type].map(termWithInput) else { throw "dont know how to parse files of type \(aSource.type)" }
2015-10-29 23:50:51 +03:00
let a = try parser(aSource.contents)
let b = try parser(bSource.contents)
2015-10-29 22:40:36 +03:00
let diff = Interpreter<Term>(equal: Term.equals(annotation: const(true), leaf: ==), comparable: Interpreter<Term>.comparable { $0.extract.categories }, cost: Free.sum(Patch.sum)).run(a, b)
let JSON: Doubt.JSON = [
2015-10-29 23:50:51 +03:00
"before": .String(aSource.contents),
"after": .String(bSource.contents),
2015-10-29 22:40:36 +03:00
"diff": diff.JSON(pure: { $0.JSON { $0.JSON(annotation: { $0.range.JSON }, leaf: Doubt.JSON.String) } }, leaf: Doubt.JSON.String, annotation: {
[
"before": $0.range.JSON,
"after": $1.range.JSON,
]
}),
]
let data = JSON.serialize()
try data.writeToURL(jsonURL, options: .DataWritingAtomic)
2015-10-28 01:53:48 +03:00
2015-10-29 22:40:36 +03:00
let components = NSURLComponents()
components.scheme = "file"
components.path = uiPath
components.query = jsonURL.absoluteString
2015-10-29 22:40:36 +03:00
if let URL = components.URL {
NSWorkspace.sharedWorkspace().openURL(URL)
2015-10-27 18:04:18 +03:00
}