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

johnandersen777.bsky.social/knotless

git that lives in your Atmosphere account

Forked from chadtmiller.com/knotless

0
0
102 lines · 4.2 KB Raw History
import SwiftUI

struct TreeView: View {
    let repo: RepoRef
    let ref: String?
    let path: String

    var body: some View {
        Loading(load: { [repo, ref, path] in
            let tree: Tree = try await Client.shared.query(
                "getTree", ["repo": repo.uri, "ref": ref, "path": path.isEmpty ? nil : path])
            return tree.entries ?? []
        }) { entries in
            if entries.isEmpty {
                ContentUnavailableView("No files here", systemImage: "folder")
            } else {
                List { ForEach(entries.sorted { ($0.kind == "tree" ? 0 : 1, $0.name) < ($1.kind == "tree" ? 0 : 1, $1.name) }) { entry in
                    let full = path.isEmpty ? entry.name : path + "/" + entry.name
                    NavigationLink(value: entry.kind == "tree"
                        ? Route.tree(repo, ref: ref, path: full)
                        : Route.file(repo, ref: ref, path: full)) {
                        SwiftUI.Label {
                            Text(entry.name)
                        } icon: {
                            Oct(name: entry.kind == "tree" ? "file-directory-fill" : "file")
                                .foregroundStyle(entry.kind == "tree" ? Color.accent : Color.neutral)
                        }
                    }
                }.listRowBackground(Color.page) }
                .listStyle(.plain)
            }
        }
        .navigationTitle(path.isEmpty ? repo.name : (path.split(separator: "/").last.map(String.init) ?? path))
        .navigationBarTitleDisplayMode(.inline)
    }
}

struct FileView: View {
    let repo: RepoRef
    let ref: String?
    let path: String

    var body: some View {
        Loading(load: { [repo, ref, path] in
            let file: FileAnswer = try await Client.shared.query(
                "getFile", ["repo": repo.uri, "ref": ref, "path": path])
            return file
        }) { file in
            if let text = file.text {
                let lines = text.hasSuffix("\n") ? String(text.dropLast()).components(separatedBy: "\n") : text.components(separatedBy: "\n")
                CodeLines(lines: lines, painted: Highlight.paint(lines: lines, ranges: file.ranges))
            } else {
                ContentUnavailableView("This file can't be shown here", systemImage: "doc")
            }
        }
        .navigationTitle(path.split(separator: "/").last.map(String.init) ?? path)
        .navigationBarTitleDisplayMode(.inline)
    }
}

/// A file's lines, each on one row whatever its length. The code scrolls sideways
/// under numbers that stay put.
private struct CodeLines: View {
    let lines: [String]
    let painted: [AttributedString]

    /// Every row the same height, so the two columns line up whatever a line holds.
    @ScaledMetric(relativeTo: .caption) private var rowHeight = 17

    var body: some View {
        ScrollView {
            HStack(alignment: .top, spacing: Space.s2) {
                column(widest: "\(painted.count)", alignment: .trailing) { i in
                    Text("\(i + 1)").foregroundStyle(.secondary)
                }
                .padding(.leading, Space.s2)
                ScrollView(.horizontal) {
                    column(widest: lines.max { $0.count < $1.count } ?? "", alignment: .leading) { i in
                        Text(painted[i])
                    }
                    .padding(.trailing, Space.s4)
                }
            }
            .padding(.vertical, Space.s2)
        }
    }

    /// Rows made as they come into view. The widest, drawn with no height, holds
    /// the column at its full width from the start, so it doesn't jump on scrolling.
    private func column(widest: String, alignment: HorizontalAlignment, @ViewBuilder row: @escaping (Int) -> Text) -> some View {
        VStack(alignment: alignment, spacing: 0) {
            Text(widest).frame(height: 0).hidden().accessibilityHidden(true)
            LazyVStack(alignment: alignment, spacing: 0) {
                ForEach(painted.indices, id: \.self) { i in
                    row(i).lineLimit(1).fixedSize().frame(height: rowHeight)
                }
            }
        }
        .font(.caption.monospaced())
        .fixedSize(horizontal: true, vertical: false)
    }
}