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-29 01:17:01 +03:00
extension String : ErrorType { }
2015-10-29 01:12:16 +03:00
typealias Term = Cofree < String , Info >
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 >
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: C a t e g o r i z a b l e
2015-10-29 00:51:54 +03:00
let categories : Set < String >
2015-10-28 21:38:17 +03:00
2015-10-28 21:27:52 +03:00
// MARK: C u s t o m J S O N C o n v e r t i b l e
var JSON : Doubt . JSON {
return [
2015-10-29 01:11:22 +03:00
" range " : range . JSON ,
2015-10-29 00:51:54 +03:00
" categories " : Array ( categories ) . JSON
2015-10-28 21:27:52 +03:00
]
}
2015-10-28 21:22:07 +03:00
}
2015-10-28 21:23:58 +03:00
func = = ( left : Info , right : Info ) -> Bool {
2015-10-29 00:51:54 +03:00
return left . range = = right . range && left . categories = = right . categories
2015-10-28 21:23:58 +03:00
}
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-29 00:48:51 +03:00
guard count > 0 else { return Syntax . Leaf ( category ) }
2015-10-28 22:05:25 +03:00
switch category {
2015-10-29 01:42:10 +03:00
case " 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-28 23:20:39 +03:00
return try . Fixed ( node . namedChildren . map {
2015-10-29 00:28:58 +03:00
( $0 , try $0 . category ( document ) )
2015-10-28 23:20:39 +03:00
} )
2015-10-29 00:48:51 +03:00
case " object " :
2015-10-28 23:18:38 +03:00
return try . Keyed ( Dictionary ( elements : node . namedChildren . map {
2015-10-29 01:32:34 +03:00
switch try $0 . category ( document ) {
case " pair " :
let range = $0 . namedChildren [ 0 ] . range
guard let name = String ( string . utf16 [ String . UTF16View . Index ( _offset : range . startIndex ) . . < String . UTF16View . Index ( _offset : range . endIndex ) ] ) else { throw " could not make a string from utf16 range ' \( range ) ' " }
return ( name , ( $0 , " pair " ) )
default :
2015-10-29 01:33:04 +03:00
// W e m i g h t h a v e a c o m m e n t i n s i d e a n o b j e c t l i t e r a l . I t s h o u l d s t i l l b e a s s i g n e d a k e y , h o w e v e r .
2015-10-29 01:32:34 +03:00
return try ( String ( $0 . range ) , ( $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 {
2015-10-29 00:28:58 +03:00
( $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 )
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 ) {
2015-10-29 01:45:50 +03:00
let diff = Interpreter < Term > ( equal : Term . equals ( annotation : const ( true ) , leaf : = = ) , comparable : Interpreter < Term > . comparable { $0 . extract . categories } , cost : Free . sum ( Patch . difference ) ) . run ( a , b )
2015-10-28 01:38:31 +03:00
let JSON : Doubt . JSON = [
" before " : . String ( aString ) ,
" after " : . String ( bString ) ,
2015-10-29 01:12:16 +03:00
" diff " : diff . JSON ( pure : { $0 . JSON { $0 . JSON ( annotation : { $0 . range . JSON } , leaf : Doubt . JSON . String ) } } , leaf : Doubt . JSON . String , annotation : {
2015-10-28 01:38:31 +03:00
[
2015-10-29 01:12:16 +03:00
" before " : $0 . range . JSON ,
" after " : $1 . range . JSON ,
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
}