Sapling/Sources/SaplingEditor/EditorActiveLineTracker.swift

65 lines
2.2 KiB
Swift
Raw Normal View History

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)
}
}