Skip to content

Language server installation menu #1997

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 43 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
5ea9de5
Language server installation menu
FastestMolasses Feb 25, 2025
d8ca6b2
Lint
FastestMolasses Mar 4, 2025
95479a5
Merge branch 'main' into lsp-install
FastestMolasses Mar 4, 2025
ca48313
Added notification on file open
FastestMolasses Mar 5, 2025
80d16d9
Small update
FastestMolasses Mar 9, 2025
4c22bc3
Merge branch 'main' into lsp-install
FastestMolasses Mar 9, 2025
21f7138
Refactors
FastestMolasses Mar 11, 2025
c827108
Connect install button
FastestMolasses Mar 11, 2025
b9d3945
Refactors
FastestMolasses Mar 12, 2025
6c9c48a
Fix lint
FastestMolasses Mar 13, 2025
ee93abb
Refactors, added settings loading
FastestMolasses Mar 13, 2025
2f2c022
Refactors, added text icon colors
FastestMolasses Mar 14, 2025
ed9dfe6
Update notifications
FastestMolasses Mar 14, 2025
64ceb74
Add more documentation
FastestMolasses Mar 14, 2025
04fde2c
Added installation queuing, fix runtime warnings
FastestMolasses Mar 14, 2025
6b40bb3
Convert to actor
FastestMolasses Mar 14, 2025
0fe73ef
Refactors, fix and update removals
FastestMolasses Mar 14, 2025
b5d7186
Fix queue feedback and clean up memory
FastestMolasses Mar 14, 2025
1db6a66
Small refactors
FastestMolasses Mar 15, 2025
1ec3820
Temporarily removed Language Servers menu
FastestMolasses Mar 16, 2025
6747bf5
Small refactors
FastestMolasses Mar 31, 2025
08963a2
Merge branch 'main' into lsp-install
FastestMolasses Mar 31, 2025
ae57197
Merge branch 'main' into lsp-install
thecoolwinter Apr 9, 2025
74bf7e5
Correct CESE Version In XcodeProj File
thecoolwinter Apr 9, 2025
24b3c37
[chore:] Bump Version Number (#2021)
thecoolwinter Apr 8, 2025
2fb1157
Update pre-release.yml - xcpretty
thecoolwinter Apr 8, 2025
8afeb08
Bump Build Number to 45 (#2022)
github-actions[bot] Apr 8, 2025
64ad6a3
Add workflow_dispatch to Release Notes CI
thecoolwinter Apr 8, 2025
a0d1fb9
docs: add pro100filipp as a contributor for code (#2023)
allcontributors[bot] Apr 8, 2025
29bf445
Source Control Filter (#2024)
LeonardoLarranaga Apr 12, 2025
3bafb1c
Disable 'Export All Custom Themes' if custom themes is empty (#2027)
LeonardoLarranaga Apr 16, 2025
f2337c8
Update README.md
austincondiff Apr 19, 2025
14d9709
Sort alphabetically - Folders on top (#2028)
LeonardoLarranaga Apr 20, 2025
1ec7811
Upload dSYMs When Archiving
thecoolwinter Apr 21, 2025
32a3791
Fix Tasks Not Saving (#2034)
thecoolwinter Apr 22, 2025
89c6cc5
Add A Minimap (#2032)
thecoolwinter Apr 27, 2025
e464603
Fix Getting Stuck in Sub-View within Settings (#2038)
Kihron May 6, 2025
3ca4a3a
Fixed History Inspector popover UI bug (#2041)
austincondiff May 9, 2025
d92d6c6
Language Server Syntax Highlights (#1985)
thecoolwinter May 9, 2025
4bd724e
Refactors
FastestMolasses May 12, 2025
f358ab9
Refactors
FastestMolasses May 19, 2025
daae205
Refactors
FastestMolasses May 19, 2025
9fcbfd8
Merge branch 'main' into lsp-install
FastestMolasses May 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion CodeEdit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 55;
objectVersion = 70;
objects = {

/* Begin PBXBuildFile section */
Expand Down Expand Up @@ -64,6 +64,8 @@
3000516A2BBD3A8200A98562 /* ServiceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 300051692BBD3A8200A98562 /* ServiceType.swift */; };
3000516C2BBD3A9500A98562 /* ServiceWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3000516B2BBD3A9500A98562 /* ServiceWrapper.swift */; };
3026F50F2AC006C80061227E /* InspectorAreaViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3026F50E2AC006C80061227E /* InspectorAreaViewModel.swift */; };
304672DF2D5235210037C8F1 /* Registry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 304672DE2D52351F0037C8F1 /* Registry.swift */; };
30818CB52D4E563900967860 /* ZIPFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = 30818CB42D4E563900967860 /* ZIPFoundation */; };
30AB4EBB2BF718A100ED4431 /* DeveloperSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AB4EBA2BF718A100ED4431 /* DeveloperSettings.swift */; };
30AB4EBD2BF71CA800ED4431 /* DeveloperSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AB4EBC2BF71CA800ED4431 /* DeveloperSettingsView.swift */; };
30AB4EC22BF7253200ED4431 /* KeyValueTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30AB4EC12BF7253200ED4431 /* KeyValueTable.swift */; };
Expand Down Expand Up @@ -770,6 +772,7 @@
300051692BBD3A8200A98562 /* ServiceType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceType.swift; sourceTree = "<group>"; };
3000516B2BBD3A9500A98562 /* ServiceWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceWrapper.swift; sourceTree = "<group>"; };
3026F50E2AC006C80061227E /* InspectorAreaViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorAreaViewModel.swift; sourceTree = "<group>"; };
304672DE2D52351F0037C8F1 /* Registry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Registry.swift; sourceTree = "<group>"; };
30AB4EBA2BF718A100ED4431 /* DeveloperSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperSettings.swift; sourceTree = "<group>"; };
30AB4EBC2BF71CA800ED4431 /* DeveloperSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperSettingsView.swift; sourceTree = "<group>"; };
30AB4EC12BF7253200ED4431 /* KeyValueTable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyValueTable.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1339,6 +1342,11 @@
EC0870F62A455F6400EB8692 /* ProjectNavigatorViewController+NSMenuDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProjectNavigatorViewController+NSMenuDelegate.swift"; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFileSystemSynchronizedRootGroup section */
304672E02D5244E50037C8F1 /* Extensions */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = Extensions; sourceTree = "<group>"; };
30818CAE2D4E39B600967860 /* Registry */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = Registry; sourceTree = "<group>"; };
/* End PBXFileSystemSynchronizedRootGroup section */

/* Begin PBXFrameworksBuildPhase section */
2BE487E928245162003F3F64 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
Expand All @@ -1352,6 +1360,7 @@
buildActionMask = 2147483647;
files = (
6C85BB402C2105ED00EB5DEF /* CodeEditKit in Frameworks */,
30818CB52D4E563900967860 /* ZIPFoundation in Frameworks */,
6C66C31329D05CDC00DE9ED2 /* GRDB in Frameworks */,
58F2EB1E292FB954004A9BDE /* Sparkle in Frameworks */,
6C147C4529A329350089B630 /* OrderedCollections in Frameworks */,
Expand Down Expand Up @@ -1617,6 +1626,7 @@
children = (
6C3B4CD22D0E2C5400C6759E /* Editor */,
6CD26C732C8EA71F00ADBA38 /* LanguageServer */,
30818CAE2D4E39B600967860 /* Registry */,
6CD26C742C8EA79100ADBA38 /* Service */,
30B087FA2C0D53080063A882 /* LSPUtil.swift */,
);
Expand Down Expand Up @@ -3179,6 +3189,7 @@
6CD26C882C8F91B600ADBA38 /* LSP */ = {
isa = PBXGroup;
children = (
304672DE2D52351F0037C8F1 /* Registry.swift */,
6C7D6D452C9092EC00B69EE0 /* BufferingServerConnection.swift */,
6CD26C892C8F91ED00ADBA38 /* LanguageServer+DocumentTests.swift */,
6C3B4CD32D0E2CB000C6759E /* SemanticTokenMapTests.swift */,
Expand Down Expand Up @@ -3256,6 +3267,7 @@
children = (
B6E41C6E29DD15540088F9F4 /* AccountsSettings */,
30AB4EB72BF7170B00ED4431 /* DeveloperSettings */,
304672E02D5244E50037C8F1 /* Extensions */,
B61DA9E129D929F900BF4A43 /* GeneralSettings */,
B6CF632629E5417C0085880A /* Keybindings */,
B6F0516E29D9E35300D72287 /* LocationsSettings */,
Expand Down Expand Up @@ -3805,6 +3817,10 @@
6C7B1C762A1D57CE005CBBFC /* PBXTargetDependency */,
2BE487F328245162003F3F64 /* PBXTargetDependency */,
);
fileSystemSynchronizedGroups = (
304672E02D5244E50037C8F1 /* Extensions */,
30818CAE2D4E39B600967860 /* Registry */,
);
name = CodeEdit;
packageProductDependencies = (
2816F593280CF50500DD548B /* CodeEditSymbols */,
Expand All @@ -3826,6 +3842,7 @@
6CB94D022CA1205100E8651C /* AsyncAlgorithms */,
6CC00A8A2CBEF150004E8134 /* CodeEditSourceEditor */,
6C05CF9D2CDE8699006AAECD /* CodeEditSourceEditor */,
30818CB42D4E563900967860 /* ZIPFoundation */,
);
productName = CodeEdit;
productReference = B658FB2C27DA9E0F00EA4DBD /* CodeEdit.app */;
Expand Down Expand Up @@ -3924,6 +3941,7 @@
6C4E37FA2C73E00700AEE7B5 /* XCRemoteSwiftPackageReference "SwiftTerm" */,
6CB94D012CA1205100E8651C /* XCRemoteSwiftPackageReference "swift-async-algorithms" */,
6C05CF9C2CDE8699006AAECD /* XCRemoteSwiftPackageReference "CodeEditSourceEditor" */,
30818CB32D4E563900967860 /* XCRemoteSwiftPackageReference "ZIPFoundation" */,
);
productRefGroup = B658FB2D27DA9E0F00EA4DBD /* Products */;
projectDirPath = "";
Expand Down Expand Up @@ -4621,6 +4639,7 @@
6130535F2B23A31300D767E3 /* MemorySearchTests.swift in Sources */,
587B61012934170A00D5CD8F /* UnitTests_Extensions.swift in Sources */,
6C1F3DA22C18C55800F6DEF6 /* ShellIntegrationTests.swift in Sources */,
304672DF2D5235210037C8F1 /* Registry.swift in Sources */,
283BDCC52972F236002AFF81 /* AcknowledgementsTests.swift in Sources */,
6CD26C8A2C8F91ED00ADBA38 /* LanguageServer+DocumentTests.swift in Sources */,
4EE96ECB2960565E00FFBEA8 /* DocumentsUnitTests.swift in Sources */,
Expand Down Expand Up @@ -5754,6 +5773,14 @@
minimumVersion = 0.13.2;
};
};
30818CB32D4E563900967860 /* XCRemoteSwiftPackageReference "ZIPFoundation" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/weichsel/ZIPFoundation";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 0.9.19;
};
};
30CB648F2C16CA8100CC8A9E /* XCRemoteSwiftPackageReference "LanguageServerProtocol" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/ChimeHQ/LanguageServerProtocol";
Expand Down Expand Up @@ -5866,6 +5893,11 @@
package = 2816F592280CF50500DD548B /* XCRemoteSwiftPackageReference "CodeEditSymbols" */;
productName = CodeEditSymbols;
};
30818CB42D4E563900967860 /* ZIPFoundation */ = {
isa = XCSwiftPackageProductDependency;
package = 30818CB32D4E563900967860 /* XCRemoteSwiftPackageReference "ZIPFoundation" */;
productName = ZIPFoundation;
};
30CB64902C16CA8100CC8A9E /* LanguageServerProtocol */ = {
isa = XCSwiftPackageProductDependency;
package = 30CB648F2C16CA8100CC8A9E /* XCRemoteSwiftPackageReference "LanguageServerProtocol" */;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"originHash" : "ac57a6899925c3e4ac6d43aed791c845c6fc24a4441b6a10297a207d951b7836",
"originHash" : "ea2e6949e960b25a9aba20281f3494a3f9e0d44a4fa5330f71e518c196db2141",
"pins" : [
{
"identity" : "anycodable",
Expand Down Expand Up @@ -278,6 +278,15 @@
"revision" : "d97db6d63507eb62c536bcb2c4ac7d70c8ec665e",
"version" : "0.23.2"
}
},
{
"identity" : "zipfoundation",
"kind" : "remoteSourceControl",
"location" : "https://github.com/weichsel/ZIPFoundation",
"state" : {
"revision" : "02b6abe5f6eef7e3cbd5f247c5cc24e246efcfe0",
"version" : "0.9.19"
}
}
],
"version" : 3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ final class CEWorkspaceSettings: ObservableObject {
private(set) var folderURL: URL

private var settingsURL: URL {
folderURL.appendingPathComponent("settings").appendingPathExtension("json")
folderURL.appending(path: "settings").appending(path: "json")
}

init(workspaceURL: URL) {
folderURL = workspaceURL.appendingPathComponent(".codeedit", isDirectory: true)
folderURL = workspaceURL.appending(path: ".codeedit", directoryHint: .isDirectory)
loadSettings()

storeTask = $settings
Expand Down
182 changes: 182 additions & 0 deletions CodeEdit/Features/LSP/Registry/PackageManagerFactory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
//
// PackageManagerFactory.swift
// CodeEdit
//
// Created by Abe Malla on 2/3/25.
//

import Foundation

/// Factory for creating the appropriate package manager based on installation method
final class PackageManagerFactory {
let installationDirectory: URL

init(installationDirectory: URL) {
self.installationDirectory = installationDirectory
}

/// Create the appropriate package manager for the given installation method
func createPackageManager(for method: InstallationMethod) -> PackageManagerProtocol? {
switch method.packageManagerType {
case .npm:
return NPMPackageManager(installationDirectory: installationDirectory)
case .cargo:
return CargoPackageManager(installationDirectory: installationDirectory)
case .pip:
return PipPackageManager(installationDirectory: installationDirectory)
case .golang:
return GolangPackageManager(installationDirectory: installationDirectory)
case .nuget, .opam, .customBuild, .gem:
// TODO: IMPLEMENT OTHER PACKAGE MANAGERS
return nil
case .github:
return createPackageManagerFromGithub(for: method)
case .none:
return nil
}
}

/// Parse a registry entry and create the appropriate installation method
static func parseRegistryEntry(_ entry: [String: Any]) -> InstallationMethod? {
guard let source = entry["source"] as? [String: Any],
let sourceId = source["id"] as? String else {
return nil
}

let buildInstructions = source["build"] as? [[String: Any]]

// Detect the build tool from the registry entry
var buildTool: String?
if let bin = entry["bin"] as? [String: String] {
let binValues = Array(bin.values)
if !binValues.isEmpty {
let value = binValues[0]
if value.hasPrefix("cargo:") {
buildTool = "cargo"
} else if value.hasPrefix("npm:") {
buildTool = "npm"
} else if value.hasPrefix("pypi:") {
buildTool = "pip"
} else if value.hasPrefix("gem:") {
buildTool = "gem"
} else if value.hasPrefix("golang:") {
buildTool = "golang"
}
}
}

var method = PackageSourceParser.parse(sourceId, buildInstructions: buildInstructions)

if let buildTool = buildTool {
switch method {
case .standardPackage(var source):
var options = source.options
options["buildTool"] = buildTool
source = PackageSource(
sourceId: source.sourceId,
type: source.type,
name: source.name,
version: source.version,
subpath: source.subpath,
repositoryUrl: source.repositoryUrl,
gitReference: source.gitReference,
options: options
)
method = .standardPackage(source: source)
case .sourceBuild(var source, let instructions):
var options = source.options
options["buildTool"] = buildTool
source = PackageSource(
sourceId: source.sourceId,
type: source.type,
name: source.name,
version: source.version,
subpath: source.subpath,
repositoryUrl: source.repositoryUrl,
gitReference: source.gitReference,
options: options
)
method = .sourceBuild(source: source, buildInstructions: instructions)
case .binaryDownload(var source, let url):
var options = source.options
options["buildTool"] = buildTool
source = PackageSource(
sourceId: source.sourceId,
type: source.type,
name: source.name,
version: source.version,
subpath: source.subpath,
repositoryUrl: source.repositoryUrl,
gitReference: source.gitReference,
options: options
)
method = .binaryDownload(source: source, url: url)
case .unknown:
break
}
}
return method
}

/// Install a package from a registry entry
func installFromRegistryEntry(_ entry: [String: Any]) async throws {
guard let method = PackageManagerFactory.parseRegistryEntry(entry),
let manager = createPackageManager(for: method) else {
throw PackageManagerError.invalidConfiguration
}
try await manager.install(method: method)
}

/// Install a package from a source ID string
func installFromSourceID(_ sourceID: String) async throws {
let method = PackageSourceParser.parse(sourceID)
guard let manager = createPackageManager(for: method) else {
throw PackageManagerError.packageManagerNotInstalled
}
try await manager.install(method: method)
}

private func createPackageManagerFromGithub(for method: InstallationMethod) -> PackageManagerProtocol? {
if case let .sourceBuild(source, instructions) = method {
if let buildTool = source.options["buildTool"] {
switch buildTool {
case "cargo": return CargoPackageManager(installationDirectory: installationDirectory)
case "npm": return NPMPackageManager(installationDirectory: installationDirectory)
case "pip": return PipPackageManager(installationDirectory: installationDirectory)
case "golang": return GolangPackageManager(installationDirectory: installationDirectory)
default: break
}
}

// If no buildTool option, try to determine from build instructions
for instruction in instructions {
for command in instruction.commands {
if command.contains("cargo ") {
return CargoPackageManager(installationDirectory: installationDirectory)
} else if command.contains("npm ") {
return NPMPackageManager(installationDirectory: installationDirectory)
} else if command.contains("pip ") || command.contains("python ") {
return PipPackageManager(installationDirectory: installationDirectory)
} else if command.contains("go ") {
return GolangPackageManager(installationDirectory: installationDirectory)
}
}
}

// Check the binary path for clues if needed
let binPath = instructions.first?.binaryPath ?? ""
if binPath.contains("target/release") || binPath.hasSuffix(".rs") {
return CargoPackageManager(installationDirectory: installationDirectory)
} else if binPath.contains("node_modules") {
return NPMPackageManager(installationDirectory: installationDirectory)
} else if binPath.contains(".py") {
return PipPackageManager(installationDirectory: installationDirectory)
} else if binPath.hasSuffix(".go") || binPath.contains("/go/bin") {
return GolangPackageManager(installationDirectory: installationDirectory)
}
}

// Default to cargo
return CargoPackageManager(installationDirectory: installationDirectory)
}
}
Loading