forked from intitni/CopilotForXcode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathFilespace.swift
More file actions
129 lines (110 loc) · 3.46 KB
/
Filespace.swift
File metadata and controls
129 lines (110 loc) · 3.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import Environment
import Foundation
import SuggestionModel
public protocol FilespacePropertyKey {
associatedtype Value
static func createDefaultValue() -> Value
}
public final class FilespacePropertyValues {
var storage: [ObjectIdentifier: Any] = [:]
public subscript<K: FilespacePropertyKey>(_ key: K.Type) -> K.Value {
get {
if let value = storage[ObjectIdentifier(key)] as? K.Value {
return value
}
let value = key.createDefaultValue()
storage[ObjectIdentifier(key)] = value
return value
}
set {
storage[ObjectIdentifier(key)] = newValue
}
}
}
public struct FilespaceCodeMetadata: Equatable {
public var uti: String?
public var tabSize: Int?
public var indentSize: Int?
public var usesTabsForIndentation: Bool?
init(
uti: String? = nil,
tabSize: Int? = nil,
indentSize: Int? = nil,
usesTabsForIndentation: Bool? = nil
) {
self.uti = uti
self.tabSize = tabSize
self.indentSize = indentSize
self.usesTabsForIndentation = usesTabsForIndentation
}
}
@dynamicMemberLookup
public final class Filespace {
public let fileURL: URL
public private(set) lazy var language: String = languageIdentifierFromFileURL(fileURL).rawValue
public var codeMetadata: FilespaceCodeMetadata = .init()
public internal(set) var suggestions: [CodeSuggestion] = [] {
didSet { refreshUpdateTime() }
}
public private(set) var suggestionIndex: Int = 0
public var presentingSuggestion: CodeSuggestion? {
guard suggestions.endIndex > suggestionIndex, suggestionIndex >= 0 else { return nil }
return suggestions[suggestionIndex]
}
public var isExpired: Bool {
Environment.now().timeIntervalSince(lastSuggestionUpdateTime) > 60 * 3
}
private(set) var lastSuggestionUpdateTime: Date = Environment.now()
var additionalProperties = FilespacePropertyValues()
let fileSaveWatcher: FileSaveWatcher
let onClose: (URL) -> Void
deinit {
onClose(fileURL)
}
init(
fileURL: URL,
onSave: @escaping (Filespace) -> Void,
onClose: @escaping (URL) -> Void
) {
self.fileURL = fileURL
self.onClose = onClose
fileSaveWatcher = .init(fileURL: fileURL)
fileSaveWatcher.changeHandler = { [weak self] in
guard let self else { return }
onSave(self)
}
}
public subscript<K>(
dynamicMember dynamicMember: WritableKeyPath<FilespacePropertyValues, K>
) -> K {
get { additionalProperties[keyPath: dynamicMember] }
set { additionalProperties[keyPath: dynamicMember] = newValue }
}
@WorkspaceActor
public func reset() {
suggestions = []
suggestionIndex = 0
}
public func refreshUpdateTime() {
lastSuggestionUpdateTime = Environment.now()
}
@WorkspaceActor
public func setSuggestions(_ suggestions: [CodeSuggestion]) {
self.suggestions = suggestions
suggestionIndex = 0
}
@WorkspaceActor
public func nextSuggestion() {
suggestionIndex += 1
if suggestionIndex >= suggestions.endIndex {
suggestionIndex = 0
}
}
@WorkspaceActor
public func previousSuggestion() {
suggestionIndex -= 1
if suggestionIndex < 0 {
suggestionIndex = suggestions.endIndex - 1
}
}
}