forked from intitni/CopilotForXcode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathWorkspacePool.swift
More file actions
132 lines (111 loc) · 4.29 KB
/
WorkspacePool.swift
File metadata and controls
132 lines (111 loc) · 4.29 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
129
130
131
import Environment
import Foundation
@globalActor public enum WorkspaceActor {
public actor TheActor {}
public static let shared = TheActor()
}
public class WorkspacePool {
public internal(set) var workspaces: [URL: Workspace] = [:]
var plugins = [ObjectIdentifier: (Workspace) -> WorkspacePlugin]()
public init(
workspaces: [URL: Workspace] = [:],
plugins: [ObjectIdentifier: (Workspace) -> WorkspacePlugin] = [:]
) {
self.workspaces = workspaces
self.plugins = plugins
}
public func registerPlugin<Plugin: WorkspacePlugin>(_ plugin: @escaping (Workspace) -> Plugin) {
let id = ObjectIdentifier(Plugin.self)
let erasedPlugin: (Workspace) -> WorkspacePlugin = { plugin($0) }
plugins[id] = erasedPlugin
for workspace in workspaces.values {
addPlugin(erasedPlugin, id: id, to: workspace)
}
}
public func unregisterPlugin<Plugin: WorkspacePlugin>(_: Plugin.Type) {
let id = ObjectIdentifier(Plugin.self)
plugins[id] = nil
for workspace in workspaces.values {
removePlugin(id: id, from: workspace)
}
}
public func fetchFilespaceIfExisted(fileURL: URL) -> Filespace? {
for workspace in workspaces.values {
if let filespace = workspace.filespaces[fileURL] {
return filespace
}
}
return nil
}
@WorkspaceActor
public func fetchOrCreateWorkspaceAndFilespace(fileURL: URL) async throws
-> (workspace: Workspace, filespace: Filespace)
{
let ignoreFileExtensions = ["mlmodel"]
if ignoreFileExtensions.contains(fileURL.pathExtension) {
throw Workspace.UnsupportedFileError(extensionName: fileURL.pathExtension)
}
// If we know which project is opened.
if let currentProjectURL = try await Environment.fetchCurrentProjectRootURLFromXcode() {
if let existed = workspaces[currentProjectURL] {
let filespace = existed.createFilespaceIfNeeded(fileURL: fileURL)
return (existed, filespace)
}
let new = createNewWorkspace(projectRootURL: currentProjectURL)
workspaces[currentProjectURL] = new
let filespace = new.createFilespaceIfNeeded(fileURL: fileURL)
return (new, filespace)
}
// If not, we try to reuse a filespace if found.
//
// Sometimes, we can't get the project root path from Xcode window, for example, when the
// quick open window in displayed.
for workspace in workspaces.values {
if let filespace = workspace.filespaces[fileURL] {
return (workspace, filespace)
}
}
// If we can't find an existed one, we will try to guess it.
// Most of the time we won't enter this branch, just incase.
let workspaceURL = try await Environment.guessProjectRootURLForFile(fileURL)
let workspace = {
if let existed = workspaces[workspaceURL] {
return existed
}
// Reuse existed workspace if possible
for (_, workspace) in workspaces {
if fileURL.path.hasPrefix(workspace.projectRootURL.path) {
return workspace
}
}
return createNewWorkspace(projectRootURL: workspaceURL)
}()
let filespace = workspace.createFilespaceIfNeeded(fileURL: fileURL)
workspaces[workspaceURL] = workspace
workspace.refreshUpdateTime()
return (workspace, filespace)
}
public func removeWorkspace(url: URL) {
workspaces[url] = nil
}
}
extension WorkspacePool {
func addPlugin(
_ plugin: (Workspace) -> WorkspacePlugin,
id: ObjectIdentifier,
to workspace: Workspace
) {
if workspace.plugins[id] != nil { return }
workspace.plugins[id] = plugin(workspace)
}
func removePlugin(id: ObjectIdentifier, from workspace: Workspace) {
workspace.plugins[id] = nil
}
func createNewWorkspace(projectRootURL: URL) -> Workspace {
let new = Workspace(projectRootURL: projectRootURL)
for (id, plugin) in plugins {
addPlugin(plugin, id: id, to: new)
}
return new
}
}