forked from github/CopilotForXcode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathContextUtils.swift
More file actions
145 lines (126 loc) · 5.82 KB
/
ContextUtils.swift
File metadata and controls
145 lines (126 loc) · 5.82 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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import ConversationServiceProvider
import XcodeInspector
import Foundation
import Logger
public let supportedFileExtensions: Set<String> = ["swift", "m", "mm", "h", "cpp", "c", "js", "py", "rb", "java", "applescript", "scpt", "plist", "entitlements", "md", "json", "xml", "txt", "yaml", "yml"]
private let skipPatterns: [String] = [
".git",
".svn",
".hg",
"CVS",
".DS_Store",
"Thumbs.db",
"node_modules",
"bower_components"
]
public struct ContextUtils {
static func matchesPatterns(_ url: URL, patterns: [String]) -> Bool {
let fileName = url.lastPathComponent
for pattern in patterns {
if fnmatch(pattern, fileName, 0) == 0 {
return true
}
}
return false
}
public static func getFilesInActiveWorkspace() -> [FileReference] {
guard let workspaceURL = XcodeInspector.shared.realtimeActiveWorkspaceURL,
let workspaceRootURL = XcodeInspector.shared.realtimeActiveProjectURL else {
return []
}
return getFilesInActiveWorkspace(workspaceURL: workspaceURL, workspaceRootURL: workspaceRootURL)
}
static func getFilesInActiveWorkspace(workspaceURL: URL, workspaceRootURL: URL) -> [FileReference] {
var files: [FileReference] = []
do {
let fileManager = FileManager.default
var subprojects: [URL] = []
if isXCWorkspace(workspaceURL) {
subprojects = getSubprojectURLs(in: workspaceURL)
} else {
subprojects.append(workspaceRootURL)
}
for subproject in subprojects {
guard FileManager.default.fileExists(atPath: subproject.path) else {
continue
}
let enumerator = fileManager.enumerator(
at: subproject,
includingPropertiesForKeys: [.isRegularFileKey, .isDirectoryKey],
options: [.skipsHiddenFiles]
)
while let fileURL = enumerator?.nextObject() as? URL {
// Skip items matching the specified pattern
if matchesPatterns(fileURL, patterns: skipPatterns)
|| isXCWorkspace(fileURL) || isXCProject(fileURL) {
enumerator?.skipDescendants()
continue
}
let resourceValues = try fileURL.resourceValues(forKeys: [.isRegularFileKey, .isDirectoryKey])
// Handle directories if needed
if resourceValues.isDirectory == true {
continue
}
guard resourceValues.isRegularFile == true else { continue }
if supportedFileExtensions.contains(fileURL.pathExtension.lowercased()) == false {
continue
}
let relativePath = fileURL.path.replacingOccurrences(of: workspaceRootURL.path, with: "")
let fileName = fileURL.lastPathComponent
let file = FileReference(url: fileURL,
relativePath: relativePath,
fileName: fileName)
files.append(file)
}
}
} catch {
Logger.client.error("Failed to get files in workspace: \(error)")
}
return files
}
static func isXCWorkspace(_ url: URL) -> Bool {
return url.pathExtension == "xcworkspace" && FileManager.default.fileExists(atPath: url.appendingPathComponent("contents.xcworkspacedata").path)
}
static func isXCProject(_ url: URL) -> Bool {
return url.pathExtension == "xcodeproj" && FileManager.default.fileExists(atPath: url.appendingPathComponent("project.pbxproj").path)
}
static func getSubprojectURLs(in workspaceURL: URL) -> [URL] {
let workspaceFile = workspaceURL.appendingPathComponent("contents.xcworkspacedata")
guard let data = try? Data(contentsOf: workspaceFile) else {
Logger.client.error("Failed to read workspace file at \(workspaceFile.path)")
return []
}
return getSubprojectURLs(workspaceURL: workspaceURL, data: data)
}
static func getSubprojectURLs(workspaceURL: URL, data: Data) -> [URL] {
var subprojectURLs: [URL] = []
do {
let xml = try XMLDocument(data: data)
let fileRefs = try xml.nodes(forXPath: "//FileRef")
for fileRef in fileRefs {
if let fileRefElement = fileRef as? XMLElement,
let location = fileRefElement.attribute(forName: "location")?.stringValue {
var path = ""
if location.starts(with: "group:") {
path = location.replacingOccurrences(of: "group:", with: "")
} else if location.starts(with: "container:") {
path = location.replacingOccurrences(of: "container:", with: "")
} else {
// Skip absolute paths such as absolute:/path/to/project
continue
}
if path.hasSuffix(".xcodeproj") {
path = (path as NSString).deletingLastPathComponent
}
let subprojectURL = path.isEmpty ? workspaceURL.deletingLastPathComponent() : workspaceURL.deletingLastPathComponent().appendingPathComponent(path)
if !subprojectURLs.contains(subprojectURL) {
subprojectURLs.append(subprojectURL)
}
}
}
} catch {
Logger.client.error("Failed to parse workspace file: \(error)")
}
return subprojectURLs
}
}