import Foundation import Observation /// `repo symbols [--kind ] `: where names are /// defined, from the default branch's symbol index (gitbay v1.39.0). @Observable @MainActor final class SymbolSearchViewModel { nonisolated struct Symbol: Decodable, Sendable, Hashable, Identifiable { let name: String let kind: String let path: String let line: Int var id: String { "\(path):\(line):\(kind):\(name)" } } /// The kinds `--kind` takes, as the command's help lists them. static let kinds = ["function", "method", "class", "struct", "enum", "interface", "type", "const", "var", "module", "macro", "section"] /// nil until the first search; distinct from an empty result. private(set) var state: LoadState<[Symbol]>? /// nil searches every kind. var kind: String? private let client: GitbayClient let repoPath: String init(client: GitbayClient, repoPath: String) { self.client = client self.repoPath = repoPath } func search(_ query: String) async { let trimmed = query.trimmingCharacters(in: .whitespaces) guard !trimmed.isEmpty else { return } state = .loading var argv = ["repo", "symbols", repoPath] if let kind { argv += ["--kind", kind] } argv.append(trimmed) do { let symbols = try await client.readList(argv, of: Symbol.self) state = symbols.isEmpty ? .empty("No definitions matching \"\(trimmed)\".") : .loaded(symbols) } catch { state = .from(error) } } }