perf(editor): apply native edit source to line index
This commit is contained in:
parent
525691693b
commit
f62d59d621
5 changed files with 40 additions and 21 deletions
|
|
@ -156,10 +156,16 @@ public struct DocumentLineIndex: Hashable, Sendable {
|
||||||
public mutating func replace(_ edit: DocumentLineIndexEdit) -> DocumentLineIndexEditResult {
|
public mutating func replace(_ edit: DocumentLineIndexEdit) -> DocumentLineIndexEditResult {
|
||||||
let oldSource = source as NSString
|
let oldSource = source as NSString
|
||||||
let oldLength = oldSource.length
|
let oldLength = oldSource.length
|
||||||
let range = NSRange(
|
let range = clampedRange(edit.range, sourceLength: oldLength)
|
||||||
location: max(0, min(edit.range.location, oldLength)),
|
let updatedSource = oldSource.replacingCharacters(in: range, with: edit.replacement)
|
||||||
length: max(0, min(edit.range.length, oldLength - max(0, min(edit.range.location, oldLength))))
|
return replace(edit, updatedSource: updatedSource)
|
||||||
)
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
public mutating func replace(_ edit: DocumentLineIndexEdit, updatedSource: String) -> DocumentLineIndexEditResult {
|
||||||
|
let oldSource = source as NSString
|
||||||
|
let oldLength = oldSource.length
|
||||||
|
let range = clampedRange(edit.range, sourceLength: oldLength)
|
||||||
let replacement = edit.replacement
|
let replacement = edit.replacement
|
||||||
let replacementLength = (replacement as NSString).length
|
let replacementLength = (replacement as NSString).length
|
||||||
let locationDelta = replacementLength - range.length
|
let locationDelta = replacementLength - range.length
|
||||||
|
|
@ -172,7 +178,7 @@ public struct DocumentLineIndex: Hashable, Sendable {
|
||||||
let scanStart = boundaries[lowerLineIndex].contentRange.location
|
let scanStart = boundaries[lowerLineIndex].contentRange.location
|
||||||
let oldScanEnd = boundaries[upperLineIndex].nextLineLocation
|
let oldScanEnd = boundaries[upperLineIndex].nextLineLocation
|
||||||
|
|
||||||
source = oldSource.replacingCharacters(in: range, with: replacement)
|
source = updatedSource
|
||||||
|
|
||||||
let newSource = source as NSString
|
let newSource = source as NSString
|
||||||
let newScanEnd = max(scanStart, min(newSource.length, oldScanEnd + locationDelta))
|
let newScanEnd = max(scanStart, min(newSource.length, oldScanEnd + locationDelta))
|
||||||
|
|
@ -202,6 +208,14 @@ public struct DocumentLineIndex: Hashable, Sendable {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func clampedRange(_ range: NSRange, sourceLength: Int) -> NSRange {
|
||||||
|
let location = max(0, min(range.location, sourceLength))
|
||||||
|
return NSRange(
|
||||||
|
location: location,
|
||||||
|
length: max(0, min(range.length, sourceLength - location))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private func editorLine(for boundary: DocumentLineBoundary, activeLineIndex: Int) -> EditorLine {
|
private func editorLine(for boundary: DocumentLineBoundary, activeLineIndex: Int) -> EditorLine {
|
||||||
let nsSource = source as NSString
|
let nsSource = source as NSString
|
||||||
return EditorLine(
|
return EditorLine(
|
||||||
|
|
|
||||||
|
|
@ -124,8 +124,12 @@ public struct EditorState: Hashable, Sendable {
|
||||||
activeLineIndex = lineIndex.lineIndex(containing: selection.location)
|
activeLineIndex = lineIndex.lineIndex(containing: selection.location)
|
||||||
}
|
}
|
||||||
|
|
||||||
public mutating func updateSource(_ edit: DocumentLineIndexEdit, selection newSelection: EditorSelection? = nil) {
|
public mutating func updateSource(
|
||||||
lineIndex.replace(edit)
|
_ source: String,
|
||||||
|
edit: DocumentLineIndexEdit,
|
||||||
|
selection newSelection: EditorSelection? = nil
|
||||||
|
) {
|
||||||
|
lineIndex.replace(edit, updatedSource: source)
|
||||||
document.source = lineIndex.source
|
document.source = lineIndex.source
|
||||||
selection = EditorActiveLineTracker.clampedSelection(newSelection ?? selection, in: document.source)
|
selection = EditorActiveLineTracker.clampedSelection(newSelection ?? selection, in: document.source)
|
||||||
activeLineIndex = lineIndex.lineIndex(containing: selection.location)
|
activeLineIndex = lineIndex.lineIndex(containing: selection.location)
|
||||||
|
|
|
||||||
|
|
@ -178,7 +178,7 @@ public enum EditorBenchmarkProfiler {
|
||||||
)
|
)
|
||||||
let changedSource = sourceByInsertingProbeText(in: source, at: midpoint)
|
let changedSource = sourceByInsertingProbeText(in: source, at: midpoint)
|
||||||
var changedLineIndex = lineIndex
|
var changedLineIndex = lineIndex
|
||||||
changedLineIndex.replace(typingEdit)
|
changedLineIndex.replace(typingEdit, updatedSource: changedSource)
|
||||||
let changedActiveLineIndex = changedLineIndex.lineIndex(containing: midpoint + 1)
|
let changedActiveLineIndex = changedLineIndex.lineIndex(containing: midpoint + 1)
|
||||||
|
|
||||||
let activeLineResult = measure {
|
let activeLineResult = measure {
|
||||||
|
|
@ -221,7 +221,8 @@ public enum EditorBenchmarkProfiler {
|
||||||
let sourceUpdateResult = measure {
|
let sourceUpdateResult = measure {
|
||||||
var updatedState = state
|
var updatedState = state
|
||||||
updatedState.updateSource(
|
updatedState.updateSource(
|
||||||
typingEdit,
|
changedSource,
|
||||||
|
edit: typingEdit,
|
||||||
selection: EditorSelection(location: midpoint + typingEdit.replacementUTF16Length, length: 0)
|
selection: EditorSelection(location: midpoint + typingEdit.replacementUTF16Length, length: 0)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ public final class HybridMarkdownEditorViewModel: ObservableObject, EditorCoordi
|
||||||
guard state.document.source != source else { return }
|
guard state.document.source != source else { return }
|
||||||
let previousActiveLineIndex = state.activeLineIndex
|
let previousActiveLineIndex = state.activeLineIndex
|
||||||
if let edit {
|
if let edit {
|
||||||
state.updateSource(edit, selection: selection)
|
state.updateSource(source, edit: edit, selection: selection)
|
||||||
} else {
|
} else {
|
||||||
state.updateSource(source)
|
state.updateSource(source)
|
||||||
if let selection {
|
if let selection {
|
||||||
|
|
@ -286,7 +286,7 @@ private struct NativeMarkdownTextView: NSViewRepresentable {
|
||||||
let selection = EditorSelection(range: textView.selectedRange())
|
let selection = EditorSelection(range: textView.selectedRange())
|
||||||
let edit = pendingEdit
|
let edit = pendingEdit
|
||||||
if let edit {
|
if let edit {
|
||||||
currentLineIndex.replace(edit)
|
currentLineIndex.replace(edit, updatedSource: textView.string)
|
||||||
} else {
|
} else {
|
||||||
currentLineIndex = DocumentLineIndex(source: textView.string)
|
currentLineIndex = DocumentLineIndex(source: textView.string)
|
||||||
}
|
}
|
||||||
|
|
@ -515,7 +515,7 @@ private struct NativeMarkdownTextView: UIViewRepresentable {
|
||||||
let selection = EditorSelection(range: textView.selectedRange)
|
let selection = EditorSelection(range: textView.selectedRange)
|
||||||
let edit = pendingEdit
|
let edit = pendingEdit
|
||||||
if let edit {
|
if let edit {
|
||||||
currentLineIndex.replace(edit)
|
currentLineIndex.replace(edit, updatedSource: textView.text)
|
||||||
} else {
|
} else {
|
||||||
currentLineIndex = DocumentLineIndex(source: textView.text)
|
currentLineIndex = DocumentLineIndex(source: textView.text)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,15 @@ struct BenchmarkScenario {
|
||||||
var url: URL
|
var url: URL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let trackedInteractionMetricNames = [
|
||||||
|
"active_line_lookup",
|
||||||
|
"selection_update",
|
||||||
|
"dirty_line_invalidation_click",
|
||||||
|
"typing_state_update",
|
||||||
|
"dirty_line_invalidation_typing",
|
||||||
|
"render_update_typing_dirty"
|
||||||
|
]
|
||||||
|
|
||||||
let arguments = Array(CommandLine.arguments.dropFirst())
|
let arguments = Array(CommandLine.arguments.dropFirst())
|
||||||
let repositoryRoot = URL(fileURLWithPath: FileManager.default.currentDirectoryPath, isDirectory: true)
|
let repositoryRoot = URL(fileURLWithPath: FileManager.default.currentDirectoryPath, isDirectory: true)
|
||||||
|
|
||||||
|
|
@ -99,15 +108,6 @@ func printScenario(name: String, result: EditorBenchmarkResult) {
|
||||||
print("")
|
print("")
|
||||||
}
|
}
|
||||||
|
|
||||||
let trackedInteractionMetricNames = [
|
|
||||||
"active_line_lookup",
|
|
||||||
"selection_update",
|
|
||||||
"dirty_line_invalidation_click",
|
|
||||||
"typing_state_update",
|
|
||||||
"dirty_line_invalidation_typing",
|
|
||||||
"render_update_typing_dirty"
|
|
||||||
]
|
|
||||||
|
|
||||||
func format(_ value: Double) -> String {
|
func format(_ value: Double) -> String {
|
||||||
String(format: "%.3f", value)
|
String(format: "%.3f", value)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue