~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/knotless

git that lives in your Atmosphere account

knotless.dev
1 collaborator

knotless 0.13.0 Latest 2 packages, 1 image

1
10

The iOS app's diff keeps each line on one row, scrolling sideways

Merged chadtmiller.com asked to merge into main from chadtmiller.com/knotless:ios/diff-scrolls
ios/Knotless/Views/FilesView.swift+61−29
@@ -73,11 +73,10 @@ struct FileSection: View {
Section {
if open {
if let lines {
// One view, not one per row: a lazy stack re-estimating a
// file's rows as they arrive leaves a gap above them.
VStack(spacing: 0) { ForEach(lines) { DiffLine(line: $0) } }
if lines.isEmpty {
Text(note ?? "No changes to show.").font(.footnote).foregroundStyle(.secondary).padding()
} else {
DiffLines(lines: lines)
}
} else {
ProgressView().frame(maxWidth: .infinity).padding().task { await load() }
@@ -138,11 +137,61 @@ struct FileSection: View {
}
}
private struct DiffLine: View {
let line: FileSection.Line
/// A file's diff, each line on one row whatever its length, its indent kept. The
/// code scrolls sideways under numbers that stay put, as a file's lines do.
///
/// Whole columns, not lazy ones: a lazy stack re-estimating a file's rows as they
/// arrive leaves a gap above them.
private struct DiffLines: View {
let lines: [FileSection.Line]
private var tint: Color {
switch line.row.kind {
/// Every row a set height, so the two columns line up whatever a line holds.
@ScaledMetric(relativeTo: .caption) private var codeHeight = 20
@ScaledMetric(relativeTo: .caption) private var hunkHeight = 28
@State private var width: CGFloat = 0
var body: some View {
HStack(alignment: .top, spacing: 0) {
VStack(spacing: 0) {
ForEach(lines) { line in
HStack(spacing: Space.s1_5) {
Text(number(line.row)).frame(width: 34, alignment: .trailing).foregroundStyle(.secondary)
Text(marker(line.row)).foregroundStyle(line.row.kind == "a" ? Color.success : Color.danger)
.accessibilityHidden(true)
}
.padding(.horizontal, Space.s1_5)
.frame(height: height(line.row))
.background(tint(line.row))
}
}
.fixedSize(horizontal: true, vertical: false)
ScrollView(.horizontal) {
VStack(alignment: .leading, spacing: 0) {
ForEach(lines) { line in
Text(line.text)
.foregroundStyle(line.row.line ? Color.primary : Color.accent)
.lineLimit(1)
.fixedSize()
.padding(.trailing, Space.s4)
// The tint reaches the screen's edge on a short
// line, and the widest line's end on a long one.
.frame(minWidth: width, maxWidth: .infinity, minHeight: height(line.row), maxHeight: height(line.row), alignment: .leading)
.background(tint(line.row))
}
}
.fixedSize(horizontal: true, vertical: false)
}
.onGeometryChange(for: CGFloat.self, of: \.size.width) { width = $0 }
}
.font(.caption.monospaced())
}
private func height(_ row: DiffRow) -> CGFloat {
row.line ? codeHeight : hunkHeight
}
private func tint(_ row: DiffRow) -> Color {
switch row.kind {
case "a": Color.success.opacity(0.14)
case "d": Color.danger.opacity(0.12)
case "h": Color.accent.opacity(0.12)
@@ -150,33 +199,16 @@ private struct DiffLine: View {
}
}
private var marker: String {
switch line.row.kind {
private func marker(_ row: DiffRow) -> String {
switch row.kind {
case "a": "+"
case "d": "-"
default: " "
}
}
var body: some View {
HStack(alignment: .firstTextBaseline, spacing: Space.s1_5) {
if line.row.line {
Text(number).frame(width: 34, alignment: .trailing).foregroundStyle(.secondary)
Text(marker).foregroundStyle(line.row.kind == "a" ? Color.success : Color.danger)
.accessibilityHidden(true)
}
Text(line.text)
.foregroundStyle(line.row.line ? Color.primary : Color.accent)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.leading, line.row.line ? 0 : 12)
}
.font(.caption.monospaced())
.padding(.horizontal, Space.s1_5)
.padding(.vertical, line.row.line ? Space.s0_5 : Space.s1_5)
.background(tint)
}
private var number: String {
(line.row.kind == "d" ? line.row.old : line.row.new ?? line.row.old).map(String.init) ?? ""
private func number(_ row: DiffRow) -> String {
guard row.line else { return "" }
return (row.kind == "d" ? row.old : row.new ?? row.old).map(String.init) ?? ""
}
}