Skip to content

Commit b967471

Browse files
committed
Present diff in AsyncCodeBlock
1 parent 02e9c06 commit b967471

1 file changed

Lines changed: 78 additions & 10 deletions

File tree

Tool/Sources/SharedUIComponents/AsyncCodeBlock.swift

Lines changed: 78 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,49 @@ extension AsyncCodeBlock {
250250
_ highlightedCode: [NSMutableAttributedString],
251251
commonPrecedingSpaceCount: Int,
252252
diffResult: CodeDiff.SnippetDiff
253-
) {}
253+
) {
254+
for (index, mutableString) in highlightedCode.enumerated() {
255+
guard let line = diffResult.line(at: index, in: \.newSnippet) else { continue }
256+
guard case let .mutated(changes) = line.diff, !changes.isEmpty else { continue }
257+
258+
for change in changes {
259+
if change.offset == 0,
260+
change.element.count - commonPrecedingSpaceCount
261+
== mutableString.string.count
262+
{
263+
// ignore the whole line change
264+
continue
265+
}
266+
267+
let offset = change.offset - commonPrecedingSpaceCount
268+
let range = NSRange(
269+
location: max(0, offset),
270+
length: max(0, change.element.count + (offset < 0 ? offset : 0))
271+
)
272+
mutableString.addAttributes([
273+
.backgroundColor: NSColor.systemGreen.withAlphaComponent(0.2),
274+
], range: range)
275+
}
276+
}
277+
278+
let lastLineIndex = highlightedCode.endIndex - 1
279+
if lastLineIndex >= 0 {
280+
if let line = diffResult.line(at: lastLineIndex, in: \.oldSnippet),
281+
case let .mutated(changes) = line.diff,
282+
!changes.isEmpty
283+
{
284+
let lastLine = highlightedCode[lastLineIndex]
285+
let removedSuffix = line.text.suffix(max(
286+
0,
287+
line.text.count - lastLine.string.count
288+
))
289+
lastLine.append(.init(string: String(removedSuffix), attributes: [
290+
.foregroundColor: NSColor.systemRed.withAlphaComponent(0.4),
291+
.backgroundColor: NSColor.systemRed.withAlphaComponent(0.1),
292+
]))
293+
}
294+
}
295+
}
254296
}
255297

256298
@Perceptible
@@ -259,17 +301,10 @@ extension AsyncCodeBlock {
259301

260302
@PerceptionIgnored var originalCode: String?
261303
@PerceptionIgnored var code: String?
262-
@PerceptionIgnored private var debounceFunction: DebounceFunction<AsyncCodeBlock>?
263304
@PerceptionIgnored private var diffTask: Task<Void, Error>?
264305

265-
init() {
266-
debounceFunction = .init(duration: 0.1, block: { view in
267-
self.diff(for: view)
268-
})
269-
}
270-
271306
func diff(for view: AsyncCodeBlock) {
272-
Task { @MainActor in await debounceFunction?(view) }
307+
performDiff(for: view)
273308
}
274309

275310
private func performDiff(for view: AsyncCodeBlock) {
@@ -412,6 +447,39 @@ extension AsyncCodeBlock {
412447
}
413448

414449
#Preview("Updating Content") {
415-
EmptyView()
450+
struct UpdateContent: View {
451+
@State var index = 0
452+
struct Case {
453+
let code: String
454+
let originalCode: String
455+
}
456+
457+
let cases: [Case] = [
458+
.init(code: "foo(123)", originalCode: "bar(234)"),
459+
.init(code: "bar(456)", originalCode: "baz(567)"),
460+
]
461+
462+
var body: some View {
463+
VStack {
464+
Button("Update") {
465+
index = (index + 1) % cases.count
466+
}
467+
AsyncCodeBlock(
468+
code: cases[index].code,
469+
originalCode: cases[index].originalCode,
470+
language: "swift",
471+
startLineIndex: 10,
472+
scenario: "",
473+
font: .monospacedSystemFont(ofSize: 12, weight: .regular),
474+
droppingLeadingSpaces: true,
475+
proposedForegroundColor: .primary,
476+
dimmedCharacterCount: .init(prefix: 0, suffix: 0)
477+
)
478+
}
479+
}
480+
}
481+
482+
return UpdateContent()
483+
.frame(width: 400, height: 200)
416484
}
417485

0 commit comments

Comments
 (0)