gitbay/Repos/SymbolSearchViewModel.swift
49 lines · 1637 bytes
2 symbols in this file
1import Foundation
2import Observation
3
4/// `repo symbols <owner/name> [--kind <k>] <query>`: where names are
5/// defined, from the default branch's symbol index (gitbay v1.39.0).
6@Observable
7@MainActor
8final class SymbolSearchViewModel {
9
10 nonisolated struct Symbol: Decodable, Sendable, Hashable, Identifiable {
11 let name: String
12 let kind: String
13 let path: String
14 let line: Int
15 var id: String { "\(path):\(line):\(kind):\(name)" }
16 }
17
18 /// The kinds `--kind` takes, as the command's help lists them.
19 static let kinds = ["function", "method", "class", "struct", "enum", "interface",
20 "type", "const", "var", "module", "macro", "section"]
21
22 /// nil until the first search; distinct from an empty result.
23 private(set) var state: LoadState<[Symbol]>?
24 /// nil searches every kind.
25 var kind: String?
26
27 private let client: GitbayClient
28 let repoPath: String
29
30 init(client: GitbayClient, repoPath: String) {
31 self.client = client
32 self.repoPath = repoPath
33 }
34
35 func search(_ query: String) async {
36 let trimmed = query.trimmingCharacters(in: .whitespaces)
37 guard !trimmed.isEmpty else { return }
38 state = .loading
39 var argv = ["repo", "symbols", repoPath]
40 if let kind { argv += ["--kind", kind] }
41 argv.append(trimmed)
42 do {
43 let symbols = try await client.readList(argv, of: Symbol.self)
44 state = symbols.isEmpty ? .empty("No definitions matching \"\(trimmed)\".") : .loaded(symbols)
45 } catch {
46 state = .from(error)
47 }
48 }
49}