/
XTDiffDelta.swift
124 lines (107 loc) · 3.02 KB
/
XTDiffDelta.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import Foundation
class XTDiffDelta: GTDiffDelta
{
}
extension GTDiffHunk
{
/// Applies just this hunk to the target text.
/// - parameter text: The target text.
/// - parameter reversed: True if the target text is the "new" text and the
/// patch should be reverse-applied.
/// - returns: The modified hunk of text, or nil if the patch does not match
/// or if an error occurs.
func applied(to text: String, reversed: Bool) -> String?
{
var lines = text.components(separatedBy: .newlines)
guard Int(oldStart - 1 + oldLines) <= lines.count
else { return nil }
do {
var oldLines = [String]()
var newLines = [String]()
try enumerateLinesInHunk {
(line, _) in
let content = line.content
switch line.origin {
case .context:
oldLines.append(content)
newLines.append(content)
case .addition:
newLines.append(content)
case .deletion:
oldLines.append(content)
default:
break
}
}
let targetLines = reversed ? newLines : oldLines
let replacementLines = reversed ? oldLines : newLines
let targetLineStart = Int(reversed ? newStart : oldStart) - 1
let targetLineCount = Int(reversed ? self.newLines : self.oldLines)
let replaceRange = targetLineStart..<(targetLineStart+targetLineCount)
if targetLines != Array(lines[replaceRange]) {
// Patch doesn't match
return nil
}
lines.replaceSubrange(replaceRange, with: replacementLines)
return lines.joined(separator: text.lineEndingStyle.string)
}
catch {
return nil
}
}
/// Returns true if the hunk can be applied to the given text.
/// - parameter lines: The target text. This is an array of strings rather
/// than the raw text to more efficiently query multiple hunks on one file.
func canApply(to lines: [String]) -> Bool
{
guard Int(oldStart - 1 + oldLines) <= lines.count
else { return false }
do {
var oldLines = [String]()
try enumerateLinesInHunk {
(line, _) in
switch line.origin {
case .context, .deletion:
oldLines.append(line.content)
default:
break
}
}
let targetLineStart = Int(oldStart) - 1
let targetLineCount = Int(self.oldLines)
let replaceRange = targetLineStart..<(targetLineStart+targetLineCount)
return oldLines == Array(lines[replaceRange])
}
catch {
return false
}
}
}
extension String
{
enum LineEndingStyle: String
{
case crlf
case lf
case unknown
var string: String
{
switch self
{
case .crlf: return "\r\n"
case .lf: return "\n"
case .unknown: return "\n"
}
}
}
var lineEndingStyle: LineEndingStyle
{
if range(of: "\r\n") != nil {
return .crlf
}
if range(of: "\n") != nil {
return .lf
}
return .unknown
}
}