-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Expand file tree
/
Copy pathModification.swift
More file actions
111 lines (96 loc) · 4.91 KB
/
Modification.swift
File metadata and controls
111 lines (96 loc) · 4.91 KB
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
import Foundation
public enum Modification: Codable, Equatable {
case deleted(ClosedRange<Int>)
case inserted(Int, [String])
case deletedSelection(CursorRange)
}
public extension [String] {
mutating func apply(_ modifications: [Modification]) {
for modification in modifications {
switch modification {
case let .deleted(range):
if isEmpty { break }
let removingRange = range.lowerBound..<(range.upperBound + 1)
removeSubrange(removingRange.clamped(to: 0..<endIndex))
case let .inserted(index, strings):
insert(contentsOf: strings, at: Swift.min(endIndex, index))
case let .deletedSelection(cursorRange):
if isEmpty { break }
let startLine = cursorRange.start.line
let startCharacter = cursorRange.start.character
let endLine = cursorRange.end.line
let endCharacter = cursorRange.end.character
guard startLine < self.count && endLine < self.count else { break }
if startLine == endLine {
let line = self[startLine]
let startIndex = line.index(line.startIndex, offsetBy: startCharacter)
let endIndex = line.index(line.startIndex, offsetBy: endCharacter)
self[startLine].removeSubrange(startIndex..<endIndex)
} else {
let startLineText = self[startLine]
let endLineText = self[endLine]
let startIndex = startLineText.index(startLineText.startIndex, offsetBy: startCharacter)
let endIndex = endLineText.index(endLineText.startIndex, offsetBy: endCharacter)
self[startLine] = String(startLineText[..<startIndex])
self[endLine] = String(endLineText[endIndex...])
self[startLine] += self[endLine]
self.remove(at: endLine)
if startLine + 1 <= endLine - 1 {
self.removeSubrange((startLine + 1)..<endLine)
}
}
}
}
}
func applying(_ modifications: [Modification]) -> Array {
var newArray = self
newArray.apply(modifications)
return newArray
}
}
public extension NSMutableArray {
func apply(_ modifications: [Modification]) {
for modification in modifications {
switch modification {
case let .deleted(range):
if count == 0 { break }
let newRange = range.clamped(to: 0...(count - 1))
removeObjects(in: NSRange(newRange))
case let .inserted(index, strings):
for string in strings.reversed() {
insert(string, at: Swift.min(count, index))
}
case let .deletedSelection(cursorRange):
if count == 0 { break }
let startLine = cursorRange.start.line
let startCharacter = cursorRange.start.character
let endLine = cursorRange.end.line
let endCharacter = cursorRange.end.character
guard startLine < count && endLine < count else { break }
if startLine == endLine {
if let line = self[startLine] as? String {
let startIndex = line.index(line.startIndex, offsetBy: startCharacter)
let endIndex = line.index(line.startIndex, offsetBy: endCharacter)
let newLine = line.replacingCharacters(in: startIndex..<endIndex, with: "")
self[startLine] = newLine
}
} else {
if let startLineText = self[startLine] as? String,
let endLineText = self[endLine] as? String {
let startIndex = startLineText.index(startLineText.startIndex, offsetBy: startCharacter)
let endIndex = endLineText.index(endLineText.startIndex, offsetBy: endCharacter)
let newStartLine = String(startLineText[..<startIndex])
let newEndLine = String(endLineText[endIndex...])
self[startLine] = newStartLine
self[endLine] = newEndLine
self[startLine] = (self[startLine] as! String) + (self[endLine] as! String)
removeObject(at: endLine)
if startLine + 1 <= endLine - 1 {
removeObjects(in: NSRange(location: startLine + 1, length: endLine - startLine - 1))
}
}
}
}
}
}
}