1
1
mirror of https://github.com/github/semantic.git synced 2024-12-18 12:21:57 +03:00

Merge remote-tracking branch 'origin/master' into line-numbers

This commit is contained in:
joshvera 2015-11-04 12:34:59 -05:00
commit 26dd51eb49
14 changed files with 153 additions and 155 deletions

View File

@ -43,7 +43,6 @@
D49FCBC81BBF2C4300C5E9C3 /* Algorithm.swift in Sources */ = {isa = PBXBuildFile; fileRef = D49FCBC71BBF2C4300C5E9C3 /* Algorithm.swift */; };
D4AAE50E1B5AE22E004E581F /* Doubt.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D4AAE4FD1B5AE22E004E581F /* Doubt.framework */; };
D4AAE5401B5AE2D0004E581F /* RangeReplaceableCollectionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4AAE5351B5AE2D0004E581F /* RangeReplaceableCollectionType.swift */; };
D4AAE5451B5AE2D0004E581F /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4AAE53A1B5AE2D0004E581F /* Operators.swift */; };
D4AAE5471B5AE2D0004E581F /* Optional.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4AAE53C1B5AE2D0004E581F /* Optional.swift */; };
D4AAE5491B5AE2D0004E581F /* StringLiteralConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4AAE53E1B5AE2D0004E581F /* StringLiteralConvertible.swift */; };
D4AAE54A1B5AE2D0004E581F /* Syntax.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4AAE53F1B5AE2D0004E581F /* Syntax.swift */; };
@ -116,7 +115,6 @@
D4AAE50D1B5AE22E004E581F /* DoubtTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DoubtTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
D4AAE5141B5AE22E004E581F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
D4AAE5351B5AE2D0004E581F /* RangeReplaceableCollectionType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RangeReplaceableCollectionType.swift; sourceTree = "<group>"; };
D4AAE53A1B5AE2D0004E581F /* Operators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Operators.swift; sourceTree = "<group>"; };
D4AAE53C1B5AE2D0004E581F /* Optional.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Optional.swift; sourceTree = "<group>"; };
D4AAE53E1B5AE2D0004E581F /* StringLiteralConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringLiteralConvertible.swift; sourceTree = "<group>"; };
D4AAE53F1B5AE2D0004E581F /* Syntax.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Syntax.swift; sourceTree = "<group>"; };
@ -199,7 +197,6 @@
isa = PBXGroup;
children = (
D4AAE5351B5AE2D0004E581F /* RangeReplaceableCollectionType.swift */,
D4AAE53A1B5AE2D0004E581F /* Operators.swift */,
D4AAE53C1B5AE2D0004E581F /* Optional.swift */,
D4AAE53E1B5AE2D0004E581F /* StringLiteralConvertible.swift */,
D4AAE53F1B5AE2D0004E581F /* Syntax.swift */,
@ -464,7 +461,6 @@
D4AAE5491B5AE2D0004E581F /* StringLiteralConvertible.swift in Sources */,
D4413FF11BB08FDC00E3C3C1 /* JSON.swift in Sources */,
D1F5FE211BDE9C450048BAE4 /* JSONLeaf.swift in Sources */,
D4AAE5451B5AE2D0004E581F /* Operators.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -618,6 +614,7 @@
MACOSX_DEPLOYMENT_TARGET = 10.10;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_DISABLE_SAFETY_CHECKS = YES;
SWIFT_INSTALL_OBJC_HEADER = NO;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";

View File

@ -64,7 +64,7 @@
<CommandLineArguments>
<CommandLineArgument
argument = "--unified"
isEnabled = "YES">
isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
argument = "https://raw.githubusercontent.com/jquery/jquery/2.1.0/src/effects.js"
@ -83,7 +83,7 @@
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
debugDocumentVersioning = "NO">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference

View File

@ -17,29 +17,6 @@ public enum Algorithm<Term: CofreeType, Result> {
/// A recursive instantiation of `Operation`, unrolling another iteration of the recursive type.
indirect case Roll(Operation<Algorithm, Term, Diff>)
public func analysis<C>(@noescape ifPure ifPure: Result -> C, @noescape ifRoll: Operation<Algorithm, Term, Diff> -> C) -> C {
switch self {
case let .Pure(b):
return ifPure(b)
case let .Roll(a):
return ifRoll(a)
}
}
// MARK: Functor
public func map<Other>(transform: Result -> Other) -> Algorithm<Term, Other> {
return analysis(ifPure: transform >>> Algorithm<Term, Other>.Pure, ifRoll: { .Roll($0.map { $0.map(transform) }) })
}
// MARK: Monad
public func flatMap<C>(transform: Result -> Algorithm<Term, C>) -> Algorithm<Term, C> {
return analysis(ifPure: transform, ifRoll: { .Roll($0.map { $0.flatMap(transform) }) })
}
}

View File

@ -121,11 +121,11 @@ extension CofreeType {
}
/// Catamorphism over `TermType`s.
/// Catamorphism over `CofreeType`s.
///
/// Folds the tree encoded by the receiver into a single value by recurring top-down through the tree, applying `transform` to leaves, then to branches, and so forth.
public func cata<Result>(transform: (Annotation, Syntax<Result, Leaf>) -> Result) -> Result {
return self |> (Self.eliminate >>> { ($0, $1.map { $0.cata(transform) }) } >>> transform)
public func cata<Result>(transform: (Annotation, Syntax<Result, Leaf>) throws -> Result) rethrows -> Result {
return try transform(extract, unwrap.map { try $0.cata(transform) })
}

View File

@ -21,15 +21,6 @@ public enum Free<Leaf, Annotation, Value>: CustomDebugStringConvertible {
}
public func analysis<C>(@noescape ifPure ifPure: Value throws -> C, @noescape ifRoll: (Annotation, Syntax<Free, Leaf>) throws -> C) rethrows -> C {
switch self {
case let .Pure(b):
return try ifPure(b)
case let .Roll(s):
return try ifRoll(s)
}
}
/// Reduce the receiver by iteration.
///
/// `Pure` values are simply unpacked. `Roll` values are mapped recursively, and then have `transform` applied to them.
@ -57,9 +48,12 @@ public enum Free<Leaf, Annotation, Value>: CustomDebugStringConvertible {
///
/// For a lucid, in-depth tutorial on recursion schemes, I recommend [Patrick Thomson](https://twitter.com/importantshock)s _[An Introduction to Recursion Schemes](http://patrickthomson.ghost.io/an-introduction-to-recursion-schemes/)_ and _[Recursion Schemes, Part 2: A Mob of Morphisms](http://patrickthomson.ghost.io/recursion-schemes-part-2/)_.
public func cata(@noescape transform: (Annotation, Syntax<Value, Leaf>) throws -> Value) rethrows -> Value {
return try analysis(
ifPure: id,
ifRoll: { try transform($0, $1.map { try $0.cata(transform) }) })
switch self {
case let .Pure(a):
return a
case let .Roll(annotation, syntax):
return try transform(annotation, syntax.map { try $0.cata(transform) })
}
}
@ -88,14 +82,12 @@ public enum Free<Leaf, Annotation, Value>: CustomDebugStringConvertible {
// MARK: Functor
public func map<C>(@noescape transform: Value throws -> C) rethrows -> Free<Leaf, Annotation, C> {
return try analysis(ifPure: { try .Pure(transform($0)) }, ifRoll: { try .Roll($0, $1.map { try $0.map(transform) }) })
switch self {
case let .Pure(a):
return try .Pure(transform(a))
case let .Roll(annotation, syntax):
return try .Roll(annotation, syntax.map { try $0.map(transform) })
}
// MARK: Monad
public func flatMap<C>(@noescape transform: Value throws -> Free<Leaf, Annotation, C>) rethrows -> Free<Leaf, Annotation, C> {
return try analysis(ifPure: transform, ifRoll: { try .Roll($0, $1.map { try $0.flatMap(transform) }) })
}
@ -216,14 +208,17 @@ public func == <Term: CofreeType, Annotation where Term.Leaf: Equatable> (left:
extension Free {
public func JSON(pure pure: Value -> Doubt.JSON, leaf: Leaf -> Doubt.JSON, annotation: Annotation -> Doubt.JSON) -> Doubt.JSON {
return analysis(
ifPure: { [ "pure": pure($0) ] },
ifRoll: {
[ "roll": [
"extract": annotation($0),
"unwrap": $1.JSON(leaf: leaf, recur: { $0.JSON(pure: pure, leaf: leaf, annotation: annotation) })
] ]
})
switch self {
case let .Pure(a):
return [ "pure": pure(a) ]
case let .Roll(a, b):
return [
"roll": [
"extract": annotation(a),
"unwrap": b.JSON(leaf: leaf, recur: { $0.JSON(pure: pure, leaf: leaf, annotation: annotation) })
]
]
}
}
}

View File

@ -1,19 +0,0 @@
infix operator <> {
associativity right
precedence 140
}
infix operator <+> {
associativity right
precedence 140
}
infix operator </> {
associativity right
precedence 140
}
infix operator >>> {
associativity right
precedence 170
}

View File

@ -48,8 +48,8 @@ public enum Syntax<Recur, A>: CustomDebugStringConvertible {
/// Hylomorphisms are used to construct diffs corresponding to equal terms; see also `CofreeType.zip`.
///
/// `hylo` can be used with arbitrary functors which can eliminate to and introduce with `Syntax` values.
public func hylo<A, B, Leaf>(down: Syntax<B, Leaf> -> B, _ up: A -> Syntax<A, Leaf>) -> A -> B {
return up >>> { $0.map(hylo(down, up)) } >>> down
public func hylo<A, B, Leaf>(down: Syntax<B, Leaf> -> B, _ up: A -> Syntax<A, Leaf>)(_ a: A) -> B {
return down(up(a).map(hylo(down, up)))
}
/// Reiteration through `Syntax`.
@ -59,8 +59,9 @@ public func hylo<A, B, Leaf>(down: Syntax<B, Leaf> -> B, _ up: A -> Syntax<A, Le
/// Hylomorphisms are used to construct diffs corresponding to equal terms; see also `CofreeType.zip`.
///
/// `hylo` can be used with arbitrary functors which can eliminate to and introduce with `Annotation` & `Syntax` pairs.
public func hylo<A, B, Leaf, Annotation>(down: (Annotation, Syntax<B, Leaf>) -> B, _ up: A -> (Annotation, Syntax<A, Leaf>)) -> A -> B {
return up >>> { ($0, $1.map(hylo(down, up))) } >>> down
public func hylo<A, B, Leaf, Annotation>(down: (Annotation, Syntax<B, Leaf>) -> B, _ up: A -> (Annotation, Syntax<A, Leaf>))(_ a: A) -> B {
let (annotation, syntax) = up(a)
return down(annotation, syntax.map(hylo(down, up)))
}
@ -89,12 +90,12 @@ extension Syntax {
switch (left, right) {
case let (.Leaf(l1), .Leaf(l2)):
return leaf(l1, l2)
case let (.Indexed(v1), .Indexed(v2)):
return v1.count == v2.count && zip(v1, v2).lazy.map(recur).reduce(true) { $0 && $1 }
case let (.Fixed(v1), .Fixed(v2)):
return v1.count == v2.count && zip(v1, v2).lazy.map(recur).reduce(true) { $0 && $1 }
case let (.Keyed(d1), .Keyed(d2)):
return Set(d1.keys) == Set(d2.keys) && d1.keys.map { recur(d1[$0]!, d2[$0]!) }.reduce(true) { $0 && $1 }
case let (.Indexed(v1), .Indexed(v2)) where v1.count == v2.count:
return zip(v1, v2).reduce(true) { $0 && recur($1) }
case let (.Fixed(v1), .Fixed(v2)) where v1.count == v2.count:
return zip(v1, v2).reduce(true) { $0 && recur($1) }
case let (.Keyed(d1), .Keyed(d2)) where Set(d1.keys) == Set(d2.keys):
return d1.keys.reduce(true) { $0 && recur(d1[$1]!, d2[$1]!) }
default:
return false
}

View File

@ -14,15 +14,15 @@ extension TermType {
/// Catamorphism over `TermType`s.
///
/// Folds the tree encoded by the receiver into a single value by recurring top-down through the tree, applying `transform` to leaves, then to branches, and so forth.
public func cata<Result>(transform: Syntax<Result, Leaf> -> Result) -> Result {
return self |> (Self.unwrap >>> { $0.map { $0.cata(transform) } } >>> transform)
public func cata<Result>(transform: Syntax<Result, Leaf> throws -> Result) rethrows -> Result {
return try transform(unwrap.map { try $0.cata(transform) })
}
/// Paramorphism over `TermType`s.
///
/// Folds the tree encoded by the receiver into a single value by recurring top-down through the tree, applying `transform` to leaves, then to branches, and so forth. Each recursive instance is made available in the `Syntax` alongside the result value at that node.
public func para<Result>(transform: Syntax<(Self, Result), Leaf> -> Result) -> Result {
return self |> (Self.unwrap >>> { $0.map { ($0, $0.para(transform)) } } >>> transform)
public func para<Result>(transform: Syntax<(Self, Result), Leaf> throws -> Result) rethrows -> Result {
return try transform(unwrap.map { try ($0, $0.para(transform)) })
}

53
prototype/UI/diff.js vendored
View File

@ -67,10 +67,12 @@ function wrap(tagName, element) {
}
/// String -> Syntax a -> Range -> (a -> Range) -> (a -> DOM) -> DOM
function rangeAndSyntaxToDOM(source, syntax, range, getRange, recur) {
function termToDOM(source, syntax, extract, getRange, recur) {
recur = recur || function(term) {
return rangeAndSyntaxToDOM(source, term.unwrap, term.extract, getRange);
return termToDOM(source, term.unwrap, term.extract, getRange);
}
var categories = extract.categories;
var range = extract.range;
var element;
if (syntax.leaf != null) {
element = document.createElement("span");
@ -83,18 +85,9 @@ function rangeAndSyntaxToDOM(source, syntax, range, getRange, recur) {
var child = values[i];
if (child.pure == "") continue;
var childRange = getRange(child);
if (childRange.before != null) {
var beforeRange = childRange.before;
element.appendChild(document.createTextNode(source.substr(previous, beforeRange[0] - previous)));
element.appendChild(document.createTextNode(source.substr(previous, childRange[0] - previous)));
element.appendChild(wrap("li", recur(child)));
previous = beforeRange[0] + beforeRange[1];
}
if (childRange.after != null) {
var afterRange = childRange.before;
element.appendChild(document.createTextNode(source.substr(previous, afterRange[0] - previous)));
element.appendChild(wrap("td", recur(child)));
previous = afterRange[0] + afterRange[1];
}
previous = childRange[0] + childRange[1];
}
element.appendChild(document.createTextNode(source.substr(previous, range[0] + range[1] - previous)));
} else if (syntax.keyed != null) {
@ -127,6 +120,11 @@ function rangeAndSyntaxToDOM(source, syntax, range, getRange, recur) {
element.appendChild(document.createTextNode(source.substr(previous, range[0] + range[1] - previous)));
}
element["data-range"] = range;
for (index in categories) {
element.classList.add('category-'+categories[index]);
}
return element;
}
@ -137,11 +135,11 @@ function diffToDOM(diff, sources) {
if (diffOrTerm.pure != null) {
var beforeRange, afterRange;
if (diffOrTerm.pure.before != null) {
beforeRange = diffOrTerm.pure.before.extract
beforeRange = diffOrTerm.pure.before.extract.range
}
if (diffOrTerm.pure.after != null) {
afterRange = diffOrTerm.pure.after.extract
afterRange = diffOrTerm.pure.after.extract.range
}
if (beforeRange == null) {
@ -154,10 +152,10 @@ function diffToDOM(diff, sources) {
return { "before": beforeRange, "after": afterRange };
}
if (diffOrTerm.roll != null) {
return diffOrTerm.roll.extract;
return { before: diffOrTerm.roll.extract.before.range, after: diffOrTerm.roll.extract.after.range };
}
if (diffOrTerm.extract != null) {
return diffOrTerm.extract;
return diffOrTerm.extract.range;
}
}
@ -175,7 +173,7 @@ function diffToDOM(diff, sources) {
function pureToDOM(sources, patch, getRangeFun, diffToDOMFun) {
var elementA, elementB;
if (patch.before != null) {
elementA = rangeAndSyntaxToDOM(sources.before, patch.before.unwrap, patch.before.extract, getRangeFun);
elementA = termToDOM(sources.before, patch.before.unwrap, patch.before.extract, getRangeFun);
elementA.classList.add("delete");
if (patch.after != null) {
elementA.classList.add("replace");
@ -183,7 +181,7 @@ function pureToDOM(sources, patch, getRangeFun, diffToDOMFun) {
}
if (patch.after != null) {
elementB = rangeAndSyntaxToDOM(sources.after, patch.after.unwrap, patch.after.extract, getRangeFun);
elementB = termToDOM(sources.after, patch.after.unwrap, patch.after.extract, getRangeFun);
elementB.classList.add("insert");
if (patch.before != null) {
elementB.classList.add("replace");
@ -205,7 +203,14 @@ function pureToDOM(sources, patch, getRangeFun, diffToDOMFun) {
function rollToDOM(sources, rollOrTerm, getRangeFun, diffToDOMFun) {
var syntax = rollOrTerm.unwrap
var range = rollOrTerm.extract
var categories = {
before: rollOrTerm.extract.before.categories,
after: rollOrTerm.extract.after.categories
}
var range = {
before: rollOrTerm.extract.before.range,
after: rollOrTerm.extract.after.range
}
var elementA;
var elementB;
@ -429,6 +434,14 @@ function rollToDOM(sources, rollOrTerm, getRangeFun, diffToDOMFun) {
elementB.appendChild(document.createTextNode(textB));
}
for (index in categories.before) {
elementA.classList.add('category-'+categories.before[index]);
}
for (index in categories.after) {
elementB.classList.add('category-'+categories.after[index]);
}
return { "before": elementA, "after": elementB }
}

0
prototype/UI/dom.js vendored
View File

View File

@ -29,6 +29,7 @@
background-color: #ffffec;
outline: 1px solid #e9e9c0;
}
.invisible {
background-color: #fff;
visibility: hidden;
@ -39,15 +40,58 @@
display: inline;
margin: 0;
padding: 0;
color: initial;
}
.diff dt {
display: none;
}
/* syntax highlighting */
.diff .category-regex,
.diff .category-string {
color: #183691;
}
.diff .category-false,
.diff .category-true,
.diff .category-null,
.diff .category-undefined,
.diff .category-number,
.diff .category-function_call>li>.category-identifier,
.diff .category-function_call>li>.category-member_access>.category-identifier,
.diff .category-member_access>:not(:first-child)>.category-identifier {
color: #0086b3;
}
.diff .category-comment {
color: #969896;
}
.diff [class^="category-"][class$="_op"],
.diff .category-ternary,
.diff .category-var_declaration,
.diff .category-new_expression,
.diff .category-if_statement,
.diff .category-do_statement,
.diff .category-for_statement,
.diff .category-for_in_statement,
.diff .category-return_statement,
.diff .category-function,
.diff .category-assignment,
.diff .category-var_assignment {
color: #a71d5d;
}
.diff .category-function>li:first-child>.category-identifier,
.diff .category-new_expression>li>.category-function_call>li:first-child>.category-identifier,
.diff .category-pair>li:first-child>.category-identifier,
.diff .category-assignment>li:first-child>.category-member_access>:not(:first-child)>.category-identifier {
color: #795da3;
}
</style>
<script type="text/javascript" src="dictionary.js"></script>
<script type="text/javascript" src="diff.js"></script>
<script type="text/javascript" src="dom.js"></script>
<script type="text/javascript" src="patch.js"></script>
<script type="text/javascript" src="syntax.js"></script>
<script type="text/javascript" src="term.js"></script>
@ -72,7 +116,6 @@
<script type="text/javascript">
loadJSON((window.location.search || '?diff.json').substr(1), function (json) {
var diff = diffFromJSON(json.diff);
var dom = diffToDOM(diff, { "before": json["before"] , "after": json["after"] })
document.getElementById("before").appendChild(dom.before);
document.getElementById("after").appendChild(dom.after);

View File

@ -12,12 +12,3 @@ function Term(object) {
this.unwrap = object.unwrap;
return this;
}
/// Term -> String -> DOM
function termToDOM(term, which, source) {
return rangeAndSyntaxToDOM(term.range, term.unwrap, source, function(term) {
return term.range;
}, function(term) {
return termToDOM(term, which, source);
});
}

View File

@ -26,9 +26,10 @@ extension TSNode {
var namedChildren: ChildrenCollection {
return ChildrenCollection(node: self, count: ts_node_named_child_count(self), child: ts_node_named_child)
}
}
struct ChildrenCollection: CollectionType {
struct ChildrenCollection: CollectionType {
let node: TSNode
let count: Int
let child: (TSNode, Int) -> TSNode
@ -41,5 +42,4 @@ extension TSNode {
var endIndex: Int {
return count
}
}
}

View File

@ -3,9 +3,9 @@ import Doubt
import Prelude
import Madness
func benchmark<T>(label: String? = nil, _ f: () -> T) -> T {
func benchmark<T>(label: String? = nil, _ f: () throws -> T) rethrows -> T {
let start = NSDate.timeIntervalSinceReferenceDate()
let result = f()
let result = try f()
let end = NSDate.timeIntervalSinceReferenceDate()
print((label.map { "\($0): " } ?? "") + "\(end - start)s")
return result
@ -144,7 +144,7 @@ func parserForType(type: String) -> String throws -> Term {
}
}
let parsed = parse(argumentsParser, input: Process.arguments)
let parsed = benchmark("parsing arguments & loading sources") { parse(argumentsParser, input: Process.arguments) }
let arguments: Argument = try parsed.either(ifLeft: { throw "\($0)" }, ifRight: { $0 })
let (aSource, bSource) = arguments.sources
let jsonURL = NSURL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).URLByAppendingPathComponent("diff.json")
@ -152,18 +152,18 @@ guard let uiPath = NSBundle.mainBundle().infoDictionary?["PathToUISource"] as? S
guard aSource.type == bSource.type else { throw "cant compare files of different types" }
let parser = parserForType(aSource.type)
let a = try parser(aSource.contents)
let b = try parser(bSource.contents)
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 a = try benchmark("parsing source a") { try parser(aSource.contents) }
let b = try benchmark("parsing source b") { try parser(bSource.contents) }
let diff = benchmark("diffing") { Interpreter<Term>(equal: Term.equals(annotation: const(true), leaf: ==), comparable: Interpreter<Term>.comparable { $0.extract.categories }, cost: Free.sum(Patch.sum)).run(a, b) }
switch arguments.output {
case .Split:
let JSON: Doubt.JSON = [
"before": .String(aSource.contents),
"after": .String(bSource.contents),
"diff": diff.JSON(pure: { $0.JSON { $0.JSON(annotation: { $0.range.JSON }, leaf: Doubt.JSON.String) } }, leaf: Doubt.JSON.String, annotation: {
"diff": diff.JSON(pure: { $0.JSON { $0.JSON(annotation: { $0.JSON }, leaf: Doubt.JSON.String) } }, leaf: Doubt.JSON.String, annotation: {
[
"before": $0.range.JSON,
"after": $1.range.JSON,
"before": $0.JSON,
"after": $1.JSON,
]
}),
]
@ -178,5 +178,5 @@ case .Split:
NSWorkspace.sharedWorkspace().openURL(URL)
}
case .Unified:
print(benchmark { unified(diff, before: aSource.contents, after: bSource.contents) })
print(benchmark("formatting unified diff") { unified(diff, before: aSource.contents, after: bSource.contents) })
}