Skip to content

Commit 2ee8cbc

Browse files
committed
Initial commit
0 parents  commit 2ee8cbc

File tree

6 files changed

+183
-0
lines changed

6 files changed

+183
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.xcodeproj

Package.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//
2+
// Package.swift
3+
// Diff
4+
//
5+
// Created by Sam Soffes on 4/12/16.
6+
// Copyright © 2016 Sam Soffes. All rights reserved.
7+
//
8+
9+
import PackageDescription
10+
11+
let package = Package(
12+
name: "Diff"
13+
)

Readme.markdown

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Diff
2+
3+
Simple diffing library in pure Swift.
4+
5+
6+
## Usage
7+
8+
Start by importing the package:
9+
10+
```swift
11+
import Diff
12+
```
13+
14+
### Same
15+
16+
If there is no difference, the diff will be `nil`.
17+
18+
``` swift
19+
diff("Hello", "Hello") // nil
20+
```
21+
22+
For the sake of brevity, we'll `!` the rest of the examples since we know they're different.
23+
24+
25+
### Insert
26+
27+
``` swift
28+
let (range, string) = diff("Hello world", "Hello there world")!
29+
// range: 6...6
30+
// string: "there "
31+
```
32+
33+
34+
### Remove
35+
36+
``` swift
37+
let (range, string) = diff("Hello there world", "Hello world")!
38+
// range: 6..<12
39+
// string: ""
40+
```
41+
42+
43+
### Other Types
44+
45+
Diff can diff any array. Here's an array of things that conform to `Equatable`:
46+
47+
``` swift
48+
let (range, replacement) = diff([1, 2, 3], [1, 2, 3, 4])!
49+
// range: 3...3
50+
// replacement: [4]
51+
```
52+
53+
You can even use arrays of anything as long as you can compare them:
54+
55+
```swift
56+
let before: [Foo] = [a, b]
57+
let after: [Foo] = [b]
58+
let (range, replacement) = diff(before, after, compare: Foo.compare)!
59+
// range: 0...0
60+
// replacement: []
61+
```

Sources/Diff/Diff.swift

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//
2+
// Diff.swift
3+
// Diff
4+
//
5+
// Created by Sam Soffes on 4/12/16.
6+
// Copyright © 2016 Sam Soffes. All rights reserved.
7+
//
8+
9+
public func diff(lhs: String, _ rhs: String) -> (Range<Int>, String)? {
10+
let result = diff(Array(lhs.characters), Array(rhs.characters))
11+
return result.flatMap { ($0.0, String($0.1)) }
12+
}
13+
14+
public func diff<T: Equatable>(lhs: [T], _ rhs: [T]) -> (Range<Int>, [T])? {
15+
return diff(lhs, rhs, compare: ==)
16+
}
17+
18+
public func diff<T>(lhs: [T], _ rhs: [T], compare: (T, T) -> Bool) -> (Range<Int>, [T])? {
19+
let lhsCount = lhs.count
20+
let rhsCount = rhs.count
21+
22+
// Find start
23+
var commonStart = 0
24+
while commonStart < lhsCount && commonStart < rhsCount && compare(lhs[commonStart], rhs[commonStart]) {
25+
commonStart += 1
26+
}
27+
28+
// Find end
29+
var commonEnd = 0
30+
while commonEnd + commonStart < lhsCount && commonEnd + commonStart < rhsCount && compare(lhs[lhsCount - 1 - commonEnd], rhs[rhsCount - 1 - commonEnd]) {
31+
commonEnd += 1
32+
}
33+
34+
// Remove
35+
if lhsCount != commonStart + commonEnd {
36+
let range = commonStart..<(lhsCount - commonEnd)
37+
return (range, [])
38+
}
39+
40+
// Insert
41+
if rhsCount != commonStart + commonEnd {
42+
let range = commonStart..<(rhsCount - commonEnd)
43+
return (commonStart...commonStart, Array(rhs[range]))
44+
}
45+
46+
// Already equal
47+
return nil
48+
}

Tests/Diff/DiffTests.swift

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//
2+
// DiffTests.swift
3+
// Diff
4+
//
5+
// Created by Sam Soffes on 4/12/16.
6+
// Copyright © 2016 Sam Soffes. All rights reserved.
7+
//
8+
9+
import XCTest
10+
import Diff
11+
12+
class DiffTests: XCTestCase {
13+
func testInsert() {
14+
let (range, string) = diff("Hello world", "Hello there world")!
15+
XCTAssertEqual(6...6, range)
16+
XCTAssertEqual("there ", string)
17+
}
18+
19+
func testRemove() {
20+
let (range, string) = diff("Hello there world", "Hello world")!
21+
XCTAssertEqual(6..<12, range)
22+
XCTAssertEqual("", string)
23+
}
24+
25+
func testRemoveFront() {
26+
let (range, string) = diff("Hello world", "world")!
27+
XCTAssertEqual(0..<6, range)
28+
XCTAssertEqual("", string)
29+
}
30+
31+
func testSame() {
32+
XCTAssertNil(diff("Hello", "Hello"))
33+
}
34+
35+
func testIntArray() {
36+
let (range, replacement) = diff([1, 2, 3], [1, 2, 3, 4])!
37+
XCTAssertEqual(3...3, range)
38+
XCTAssertEqual([4], replacement)
39+
}
40+
41+
func testOtherType() {
42+
let before: [Foo] = [Foo(value: 1), Foo(value: 2)]
43+
let after: [Foo] = [Foo(value: 2)]
44+
45+
let (range, replacement) = diff(before, after, compare: Foo.compare)!
46+
XCTAssertEqual(0...0, range)
47+
XCTAssertEqual(0, replacement.count)
48+
}
49+
}

Tests/Diff/Foo.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
struct Foo {
2+
let value: Int
3+
4+
static func compare(lhs: Foo, rhs: Foo) -> Bool {
5+
return lhs.value == rhs.value
6+
}
7+
8+
init(value: Int) {
9+
self.value = value
10+
}
11+
}

0 commit comments

Comments
 (0)