Skip to content

Commit 21f7138

Browse files
Refactors
1 parent 4c22bc3 commit 21f7138

12 files changed

+489
-732
lines changed

CodeEdit.xcodeproj/project.pbxproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
283BDCBD2972EEBD002AFF81 /* Package.resolved in Resources */ = {isa = PBXBuildFile; fileRef = 283BDCBC2972EEBD002AFF81 /* Package.resolved */; };
1212
284DC8512978BA2600BF2770 /* .all-contributorsrc in Resources */ = {isa = PBXBuildFile; fileRef = 284DC8502978BA2600BF2770 /* .all-contributorsrc */; };
1313
2BE487F428245162003F3F64 /* OpenWithCodeEdit.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 2BE487EC28245162003F3F64 /* OpenWithCodeEdit.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
14+
302AD7FF2D8054D500231E16 /* ZIPFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = 30818CB42D4E563900967860 /* ZIPFoundation */; };
1415
30CB64912C16CA8100CC8A9E /* LanguageServerProtocol in Frameworks */ = {isa = PBXBuildFile; productRef = 30CB64902C16CA8100CC8A9E /* LanguageServerProtocol */; };
1516
30CB64942C16CA9100CC8A9E /* LanguageClient in Frameworks */ = {isa = PBXBuildFile; productRef = 30CB64932C16CA9100CC8A9E /* LanguageClient */; };
1617
583E529C29361BAB001AB554 /* SnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 583E529B29361BAB001AB554 /* SnapshotTesting */; };
@@ -164,8 +165,8 @@
164165
isa = PBXFrameworksBuildPhase;
165166
buildActionMask = 2147483647;
166167
files = (
168+
302AD7FF2D8054D500231E16 /* ZIPFoundation in Frameworks */,
167169
6C85BB402C2105ED00EB5DEF /* CodeEditKit in Frameworks */,
168-
30818CB52D4E563900967860 /* ZIPFoundation in Frameworks */,
169170
6C66C31329D05CDC00DE9ED2 /* GRDB in Frameworks */,
170171
58F2EB1E292FB954004A9BDE /* Sparkle in Frameworks */,
171172
6C147C4529A329350089B630 /* OrderedCollections in Frameworks */,

CodeEdit/Features/LSP/Registry/PackageManagerFactory.swift

Lines changed: 33 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,37 @@ final class PackageManagerFactory {
1515
self.installationDirectory = installationDirectory
1616
}
1717

18+
/// Install a package from a registry entry
19+
func installFromRegistryEntry(_ entry: RegistryItem) async throws {
20+
guard let method = Self.parseRegistryEntry(entry),
21+
let manager = createPackageManager(for: method) else {
22+
throw PackageManagerError.invalidConfiguration
23+
}
24+
try await manager.install(method: method)
25+
}
26+
27+
/// Parse a registry entry and create the appropriate installation method
28+
private static func parseRegistryEntry(_ entry: RegistryItem) -> InstallationMethod? {
29+
let sourceId = entry.source.id
30+
if sourceId.hasPrefix("pkg:cargo/") {
31+
return PackageSourceParser.parseCargoPackage(entry)
32+
} else if sourceId.hasPrefix("pkg:npm/") {
33+
return PackageSourceParser.parseNpmPackage(entry)
34+
} else if sourceId.hasPrefix("pkg:pypi/") {
35+
return PackageSourceParser.parsePythonPackage(entry)
36+
} else if sourceId.hasPrefix("pkg:gem/") {
37+
return PackageSourceParser.parseRubyGem(entry)
38+
} else if sourceId.hasPrefix("pkg:golang/") {
39+
return PackageSourceParser.parseGolangPackage(entry)
40+
} else if sourceId.hasPrefix("pkg:github/") {
41+
return PackageSourceParser.parseGithubPackage(entry)
42+
} else {
43+
return .unknown
44+
}
45+
}
46+
1847
/// Create the appropriate package manager for the given installation method
19-
func createPackageManager(for method: InstallationMethod) -> PackageManagerProtocol? {
48+
private func createPackageManager(for method: InstallationMethod) -> PackageManagerProtocol? {
2049
switch method.packageManagerType {
2150
case .npm:
2251
return NPMPackageManager(installationDirectory: installationDirectory)
@@ -26,154 +55,13 @@ final class PackageManagerFactory {
2655
return PipPackageManager(installationDirectory: installationDirectory)
2756
case .golang:
2857
return GolangPackageManager(installationDirectory: installationDirectory)
29-
case .nuget, .opam, .customBuild, .gem:
58+
case .github, .sourceBuild:
59+
return GithubPackageManager(installationDirectory: installationDirectory)
60+
case .nuget, .opam, .gem, .composer:
3061
// TODO: IMPLEMENT OTHER PACKAGE MANAGERS
3162
return nil
32-
case .github:
33-
return createPackageManagerFromGithub(for: method)
3463
case .none:
3564
return nil
3665
}
3766
}
38-
39-
/// Parse a registry entry and create the appropriate installation method
40-
static func parseRegistryEntry(_ entry: RegistryItem) -> InstallationMethod? {
41-
// let buildInstructions = source["build"] as? [[String: Any]]
42-
// entry.source.build
43-
//
44-
// // Detect the build tool from the registry entry
45-
// var buildTool: String?
46-
// if let bin = entry.bin {
47-
// let binValues = Array(bin.values)
48-
// if !binValues.isEmpty {
49-
// let value = binValues[0]
50-
// if value.hasPrefix("cargo:") {
51-
// buildTool = "cargo"
52-
// } else if value.hasPrefix("npm:") {
53-
// buildTool = "npm"
54-
// } else if value.hasPrefix("pypi:") {
55-
// buildTool = "pip"
56-
// } else if value.hasPrefix("gem:") {
57-
// buildTool = "gem"
58-
// } else if value.hasPrefix("golang:") {
59-
// buildTool = "golang"
60-
// }
61-
// }
62-
// }
63-
//
64-
// var method = PackageSourceParser.parse(entry.source.id, buildInstructions: buildInstructions)
65-
//
66-
// if let buildTool = buildTool {
67-
// switch method {
68-
// case .standardPackage(var source):
69-
// var options = source.options
70-
// options["buildTool"] = buildTool
71-
// source = PackageSource(
72-
// sourceId: source.sourceId,
73-
// type: source.type,
74-
// name: source.name,
75-
// version: source.version,
76-
// subpath: source.subpath,
77-
// repositoryUrl: source.repositoryUrl,
78-
// gitReference: source.gitReference,
79-
// options: options
80-
// )
81-
// method = .standardPackage(source: source)
82-
// case .sourceBuild(var source, let instructions):
83-
// var options = source.options
84-
// options["buildTool"] = buildTool
85-
// source = PackageSource(
86-
// sourceId: source.sourceId,
87-
// type: source.type,
88-
// name: source.name,
89-
// version: source.version,
90-
// subpath: source.subpath,
91-
// repositoryUrl: source.repositoryUrl,
92-
// gitReference: source.gitReference,
93-
// options: options
94-
// )
95-
// method = .sourceBuild(source: source, buildInstructions: instructions)
96-
// case .binaryDownload(var source, let url):
97-
// var options = source.options
98-
// options["buildTool"] = buildTool
99-
// source = PackageSource(
100-
// sourceId: source.sourceId,
101-
// type: source.type,
102-
// name: source.name,
103-
// version: source.version,
104-
// subpath: source.subpath,
105-
// repositoryUrl: source.repositoryUrl,
106-
// gitReference: source.gitReference,
107-
// options: options
108-
// )
109-
// method = .binaryDownload(source: source, url: url)
110-
// case .unknown:
111-
// break
112-
// }
113-
// }
114-
// return method
115-
return nil
116-
}
117-
118-
/// Install a package from a registry entry
119-
func installFromRegistryEntry(_ entry: [String: Any]) async throws {
120-
// guard let method = PackageManagerFactory.parseRegistryEntry(entry),
121-
// let manager = createPackageManager(for: method) else {
122-
// throw PackageManagerError.invalidConfiguration
123-
// }
124-
// try await manager.install(method: method)
125-
}
126-
127-
/// Install a package from a source ID string
128-
func installFromSourceID(_ sourceID: String) async throws {
129-
let method = PackageSourceParser.parse(sourceID)
130-
guard let manager = createPackageManager(for: method) else {
131-
throw PackageManagerError.packageManagerNotInstalled
132-
}
133-
try await manager.install(method: method)
134-
}
135-
136-
private func createPackageManagerFromGithub(for method: InstallationMethod) -> PackageManagerProtocol? {
137-
if case let .sourceBuild(source, instructions) = method {
138-
if let buildTool = source.options["buildTool"] {
139-
switch buildTool {
140-
case "cargo": return CargoPackageManager(installationDirectory: installationDirectory)
141-
case "npm": return NPMPackageManager(installationDirectory: installationDirectory)
142-
case "pip": return PipPackageManager(installationDirectory: installationDirectory)
143-
case "golang": return GolangPackageManager(installationDirectory: installationDirectory)
144-
default: break
145-
}
146-
}
147-
148-
// If no buildTool option, try to determine from build instructions
149-
for instruction in instructions {
150-
for command in instruction.commands {
151-
if command.contains("cargo ") {
152-
return CargoPackageManager(installationDirectory: installationDirectory)
153-
} else if command.contains("npm ") {
154-
return NPMPackageManager(installationDirectory: installationDirectory)
155-
} else if command.contains("pip ") || command.contains("python ") {
156-
return PipPackageManager(installationDirectory: installationDirectory)
157-
} else if command.contains("go ") {
158-
return GolangPackageManager(installationDirectory: installationDirectory)
159-
}
160-
}
161-
}
162-
163-
// Check the binary path for clues if needed
164-
let binPath = instructions.first?.binaryPath ?? ""
165-
if binPath.contains("target/release") || binPath.hasSuffix(".rs") {
166-
return CargoPackageManager(installationDirectory: installationDirectory)
167-
} else if binPath.contains("node_modules") {
168-
return NPMPackageManager(installationDirectory: installationDirectory)
169-
} else if binPath.contains(".py") {
170-
return PipPackageManager(installationDirectory: installationDirectory)
171-
} else if binPath.hasSuffix(".go") || binPath.contains("/go/bin") {
172-
return GolangPackageManager(installationDirectory: installationDirectory)
173-
}
174-
}
175-
176-
// Default to cargo
177-
return CargoPackageManager(installationDirectory: installationDirectory)
178-
}
17967
}

CodeEdit/Features/LSP/Registry/PackageManagers/CargoPackageManager.swift

Lines changed: 7 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -18,29 +18,22 @@ class CargoPackageManager: PackageManagerProtocol {
1818
}
1919

2020
func initialize(in packagePath: URL) async throws {
21-
try createDirectoryStructure(for: packagePath)
21+
do {
22+
try createDirectoryStructure(for: packagePath)
23+
} catch {
24+
throw PackageManagerError.initializationFailed(error.localizedDescription)
25+
}
2226

2327
guard await isInstalled() else {
2428
throw PackageManagerError.packageManagerNotInstalled
2529
}
2630
}
2731

28-
/// Install a package using the new installation method
2932
func install(method: InstallationMethod) async throws {
30-
switch method {
31-
case .standardPackage(let source):
32-
try await installCargoPackage(source)
33-
case let .sourceBuild(source, instructions):
34-
try await buildFromSource(source, instructions)
35-
case .binaryDownload:
36-
throw PackageManagerError.invalidConfiguration
37-
case .unknown:
33+
guard case .standardPackage(let source) = method else {
3834
throw PackageManagerError.invalidConfiguration
3935
}
40-
}
4136

42-
/// Install a standard cargo package
43-
private func installCargoPackage(_ source: PackageSource) async throws {
4437
let packagePath = installationDirectory.appending(path: source.name)
4538
print("Installing \(source.name)@\(source.version) in \(packagePath.path)")
4639

@@ -55,11 +48,8 @@ class CargoPackageManager: PackageManagerProtocol {
5548
cargoArgs.append(contentsOf: ["--tag", tag])
5649
case .revision(let rev):
5750
cargoArgs.append(contentsOf: ["--rev", rev])
58-
case .branch(let branch):
59-
cargoArgs.append(contentsOf: ["--branch", branch])
6051
}
6152
} else {
62-
// Standard version-based install
6353
cargoArgs.append(contentsOf: ["--version", source.version])
6454
}
6555

@@ -69,8 +59,8 @@ class CargoPackageManager: PackageManagerProtocol {
6959
if source.options["locked"] == "true" {
7060
cargoArgs.append("--locked")
7161
}
72-
7362
cargoArgs.append(source.name)
63+
7464
_ = try await executeInDirectory(in: packagePath.path, cargoArgs)
7565
print("Successfully installed \(source.name)@\(source.version)")
7666
} catch {
@@ -79,79 +69,13 @@ class CargoPackageManager: PackageManagerProtocol {
7969
}
8070
}
8171

82-
/// Build a package from source
83-
private func buildFromSource(_ source: PackageSource, _ instructions: [BuildInstructions]) async throws {
84-
let packagePath = installationDirectory.appending(path: source.name)
85-
print("Building \(source.name) from source in \(packagePath.path)")
86-
87-
do {
88-
if let repoUrl = source.repositoryUrl {
89-
try createDirectoryStructure(for: packagePath)
90-
91-
if FileManager.default.fileExists(atPath: packagePath.appendingPathComponent(".git").path) {
92-
_ = try await executeInDirectory(
93-
in: packagePath.path, ["git fetch --all"]
94-
)
95-
} else {
96-
_ = try await executeInDirectory(
97-
in: packagePath.path, ["git clone \(repoUrl) ."]
98-
)
99-
}
100-
101-
// Checkout the specific version
102-
_ = try await executeInDirectory(
103-
in: packagePath.path, ["git checkout \(source.version)"]
104-
)
105-
106-
// Find the relevant build instruction for this platform
107-
let targetInstructions = instructions.first {
108-
$0.target == "darwin" || $0.target == "unix"
109-
} ?? instructions.first
110-
111-
guard let buildInstructions = targetInstructions else {
112-
throw PackageManagerError.invalidConfiguration
113-
}
114-
115-
// Execute each build command
116-
for command in buildInstructions.commands {
117-
_ = try await executeInDirectory(in: packagePath.path, [command])
118-
}
119-
120-
// Create bin directory if it doesn't exist
121-
let binPath = packagePath.appendingPathComponent("bin")
122-
if !FileManager.default.fileExists(atPath: binPath.path) {
123-
try FileManager.default.createDirectory(at: binPath, withIntermediateDirectories: true)
124-
}
125-
126-
// Copy the built binary to the bin directory if it's not already there
127-
let builtBinaryPath = packagePath.appendingPathComponent(buildInstructions.binaryPath)
128-
let targetBinaryPath = binPath.appendingPathComponent(source.name)
129-
130-
if builtBinaryPath.path != targetBinaryPath.path &&
131-
FileManager.default.fileExists(atPath: builtBinaryPath.path) {
132-
try FileManager.default.copyItem(at: builtBinaryPath, to: targetBinaryPath)
133-
// Make the binary executable
134-
_ = try await runCommand("chmod +x \"\(targetBinaryPath.path)\"")
135-
}
136-
137-
print("Successfully built \(source.name) from source")
138-
} else {
139-
throw PackageManagerError.invalidConfiguration
140-
}
141-
} catch {
142-
print("Build failed: \(error)")
143-
throw error
144-
}
145-
}
146-
14772
func getBinaryPath(for package: String) -> String {
14873
return installationDirectory.appending(path: package).appending(path: "bin").path
14974
}
15075

15176
func isInstalled() async -> Bool {
15277
do {
15378
let versionOutput = try await runCommand("cargo --version")
154-
// Check for cargo version output
15579
let output = versionOutput.reduce(into: "") {
15680
$0 += $1.trimmingCharacters(in: .whitespacesAndNewlines)
15781
}
@@ -161,12 +85,4 @@ class CargoPackageManager: PackageManagerProtocol {
16185
return false
16286
}
16387
}
164-
165-
internal func executeInDirectory(in packagePath: String, _ args: [String]) async throws -> [String] {
166-
let escapedArgs = args.map { arg in
167-
return arg.contains(" ") ? "\"\(arg)\"" : arg
168-
}.joined(separator: " ")
169-
let command = "cd \"\(packagePath)\" && \(escapedArgs)"
170-
return try await runCommand(command)
171-
}
17288
}

0 commit comments

Comments
 (0)