2015-10-26 23:00:52 +03:00
|
|
|
import Cocoa
|
|
|
|
import Doubt
|
2015-10-28 01:38:25 +03:00
|
|
|
import Prelude
|
2015-10-27 18:04:39 +03:00
|
|
|
|
2015-10-28 01:29:27 +03:00
|
|
|
func readFile(path: String) -> String? {
|
|
|
|
guard let data = try? NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding) else { return nil }
|
|
|
|
return data as String?
|
|
|
|
}
|
|
|
|
|
2015-10-28 21:55:45 +03:00
|
|
|
typealias Term = Cofree<String, Range<Int>>
|
2015-10-28 01:37:34 +03:00
|
|
|
|
2015-10-28 21:38:17 +03:00
|
|
|
struct Info: Categorizable, CustomJSONConvertible, Equatable {
|
2015-10-28 21:22:07 +03:00
|
|
|
let range: Range<Int>
|
|
|
|
let category: Category
|
2015-10-28 21:22:55 +03:00
|
|
|
|
2015-10-28 21:27:52 +03:00
|
|
|
|
2015-10-28 21:38:17 +03:00
|
|
|
// MARK: Categorizable
|
|
|
|
|
|
|
|
var categories: Set<Category> {
|
|
|
|
return [ category ]
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-28 21:27:52 +03:00
|
|
|
// MARK: CustomJSONConvertible
|
|
|
|
|
|
|
|
var JSON: Doubt.JSON {
|
|
|
|
return [
|
|
|
|
"range": [
|
|
|
|
range.startIndex.JSON,
|
|
|
|
(range.endIndex - range.startIndex).JSON,
|
|
|
|
],
|
|
|
|
"category": category.rawValue.JSON
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// MARK: Categories
|
|
|
|
|
2015-10-28 21:22:55 +03:00
|
|
|
enum Category: String {
|
|
|
|
case Arguments = "arguments"
|
|
|
|
case Assignment = "assignment"
|
|
|
|
case Comment = "comment"
|
|
|
|
case ExpressionStatement = "expression_statement"
|
|
|
|
case FormalParameters = "formal_parameters"
|
|
|
|
case Function = "function"
|
|
|
|
case FunctionCall = "function_call"
|
|
|
|
case Identifier = "identifier"
|
|
|
|
case IfStatement = "if_statement"
|
|
|
|
case MemberAccess = "member_access"
|
|
|
|
case NewExpression = "new_expression"
|
|
|
|
case NullLiteral = "null"
|
|
|
|
case Object = "object"
|
|
|
|
case Pair = "pair"
|
|
|
|
case Program = "program"
|
|
|
|
case RelationalOperator = "rel_op"
|
|
|
|
case ReturnStatement = "return_statement"
|
|
|
|
case StatementBlock = "statement_block"
|
|
|
|
case StringLiteral = "string"
|
|
|
|
case SubscriptAccess = "subscript_access"
|
|
|
|
}
|
2015-10-28 21:22:07 +03:00
|
|
|
}
|
|
|
|
|
2015-10-28 21:23:58 +03:00
|
|
|
func == (left: Info, right: Info) -> Bool {
|
|
|
|
return left.range == right.range && left.category == right.category
|
|
|
|
}
|
|
|
|
|
2015-10-28 01:37:59 +03:00
|
|
|
func termWithInput(string: String) -> Term? {
|
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-28 01:29:44 +03:00
|
|
|
return string.withCString {
|
|
|
|
ts_document_set_language(document, ts_language_javascript())
|
|
|
|
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-28 19:33:03 +03:00
|
|
|
struct E: ErrorType {}
|
2015-10-28 21:20:46 +03:00
|
|
|
return try? Cofree
|
|
|
|
.ana { node, category in
|
2015-10-28 22:38:20 +03:00
|
|
|
let count = node.namedChildren.count
|
2015-10-28 21:20:46 +03:00
|
|
|
guard count > 0 else { return Syntax.Leaf(category.rawValue) }
|
2015-10-28 22:05:25 +03:00
|
|
|
switch category {
|
|
|
|
default:
|
2015-10-28 22:31:08 +03:00
|
|
|
return try .Indexed(node.namedChildren.map {
|
|
|
|
guard let name = String.fromCString(ts_node_name($0, document)) else { throw E() }
|
2015-10-28 22:05:25 +03:00
|
|
|
guard let category = Info.Category(rawValue: name) else { throw E() }
|
2015-10-28 22:31:08 +03:00
|
|
|
return ($0, category)
|
2015-10-28 22:05:25 +03:00
|
|
|
})
|
|
|
|
}
|
2015-10-28 21:22:55 +03:00
|
|
|
} (root, Info.Category.Program)
|
2015-10-28 21:20:46 +03:00
|
|
|
.map { node, category in
|
|
|
|
let start = ts_node_pos(node).chars
|
2015-10-28 21:55:45 +03:00
|
|
|
return start..<(start + ts_node_size(node).chars)
|
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)
|
2015-10-28 01:53:48 +03:00
|
|
|
if let aString = arguments[1].flatMap(readFile), bString = arguments[2].flatMap(readFile), c = arguments[3], ui = arguments[4] {
|
2015-10-28 01:38:25 +03:00
|
|
|
if let a = termWithInput(aString), b = termWithInput(bString) {
|
|
|
|
let diff = Interpreter<Term>(equal: Term.equals(annotation: const(true), leaf: ==), comparable: const(true), cost: Free.sum(Patch.difference)).run(a, b)
|
2015-10-28 01:38:31 +03:00
|
|
|
let range: Range<Int> -> Doubt.JSON = {
|
2015-10-28 21:32:59 +03:00
|
|
|
[
|
|
|
|
$0.startIndex.JSON,
|
|
|
|
($0.endIndex - $0.startIndex).JSON,
|
2015-10-28 01:38:31 +03:00
|
|
|
]
|
|
|
|
}
|
|
|
|
let JSON: Doubt.JSON = [
|
|
|
|
"before": .String(aString),
|
|
|
|
"after": .String(bString),
|
2015-10-28 21:55:45 +03:00
|
|
|
"diff": diff.JSON(pure: { $0.JSON { $0.JSON(annotation: range, leaf: Doubt.JSON.String) } }, leaf: Doubt.JSON.String, annotation: {
|
2015-10-28 01:38:31 +03:00
|
|
|
[
|
2015-10-28 21:55:45 +03:00
|
|
|
"before": range($0),
|
|
|
|
"after": range($1),
|
2015-10-28 01:38:31 +03:00
|
|
|
]
|
|
|
|
}),
|
|
|
|
]
|
2015-10-28 01:39:11 +03:00
|
|
|
let data = JSON.serialize()
|
|
|
|
try data.writeToFile(c, options: .DataWritingAtomic)
|
2015-10-28 01:53:48 +03:00
|
|
|
|
|
|
|
let components = NSURLComponents()
|
|
|
|
components.scheme = "file"
|
|
|
|
components.path = ui
|
2015-10-28 01:56:53 +03:00
|
|
|
components.query = c
|
2015-10-28 01:53:48 +03:00
|
|
|
if let URL = components.URL {
|
|
|
|
NSWorkspace.sharedWorkspace().openURL(URL)
|
|
|
|
}
|
2015-10-28 01:38:25 +03:00
|
|
|
}
|
2015-10-27 18:04:18 +03:00
|
|
|
}
|