65 lines
2.2 KiB
Swift
65 lines
2.2 KiB
Swift
|
|
import Foundation
|
||
|
|
|
||
|
|
public enum EditorActiveLineTracker {
|
||
|
|
public static func lines(from source: String, activeLineIndex: Int) -> [EditorLine] {
|
||
|
|
var lines: [EditorLine] = []
|
||
|
|
var lineStart = source.startIndex
|
||
|
|
var utf16Location = 0
|
||
|
|
var index = 0
|
||
|
|
|
||
|
|
while lineStart < source.endIndex {
|
||
|
|
let lineEnd = source[lineStart...].firstIndex(of: "\n") ?? source.endIndex
|
||
|
|
let line = String(source[lineStart..<lineEnd])
|
||
|
|
let length = line.utf16.count
|
||
|
|
lines.append(EditorLine(
|
||
|
|
index: index,
|
||
|
|
source: line,
|
||
|
|
range: NSRange(location: utf16Location, length: length),
|
||
|
|
mode: index == activeLineIndex ? .source : .rendered
|
||
|
|
))
|
||
|
|
|
||
|
|
if lineEnd == source.endIndex {
|
||
|
|
lineStart = lineEnd
|
||
|
|
utf16Location += length
|
||
|
|
} else {
|
||
|
|
lineStart = source.index(after: lineEnd)
|
||
|
|
utf16Location += length + 1
|
||
|
|
}
|
||
|
|
index += 1
|
||
|
|
}
|
||
|
|
|
||
|
|
if source.isEmpty || source.hasSuffix("\n") {
|
||
|
|
lines.append(EditorLine(
|
||
|
|
index: index,
|
||
|
|
source: "",
|
||
|
|
range: NSRange(location: utf16Location, length: 0),
|
||
|
|
mode: index == activeLineIndex ? .source : .rendered
|
||
|
|
))
|
||
|
|
}
|
||
|
|
|
||
|
|
return lines
|
||
|
|
}
|
||
|
|
|
||
|
|
public static func lineIndex(containing location: Int, in source: String) -> Int {
|
||
|
|
let clampedLocation = max(0, min(location, source.utf16.count))
|
||
|
|
var currentLocation = 0
|
||
|
|
|
||
|
|
for (index, line) in source.split(separator: "\n", omittingEmptySubsequences: false).enumerated() {
|
||
|
|
let length = line.utf16.count
|
||
|
|
if clampedLocation <= currentLocation + length {
|
||
|
|
return index
|
||
|
|
}
|
||
|
|
currentLocation += length + 1
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0
|
||
|
|
}
|
||
|
|
|
||
|
|
public static func clampedSelection(_ selection: EditorSelection, in source: String) -> EditorSelection {
|
||
|
|
let sourceLength = source.utf16.count
|
||
|
|
let location = max(0, min(selection.location, sourceLength))
|
||
|
|
let length = max(0, min(selection.length, sourceLength - location))
|
||
|
|
return EditorSelection(location: location, length: length)
|
||
|
|
}
|
||
|
|
}
|