diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index cce07a7d..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,126 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## 0.40.0 - July 24, 2025 -### Added -- Support disabling Agent mode when it's disabled by policy. - -## 0.39.0 - July 23, 2025 -### Fixed -- Performance: Fixed a freezing issue in 'Add Context' view when opening large projects. -- Login failed due to insufficient permissions on the .config folder. -- Fixed an issue that setting changes like proxy config did not take effect. -- Increased the timeout for ask mode to prevent response failures due to timeout. - -## 0.38.0 - June 30, 2025 -### Added -- Support for Claude 4 in Chat. -- Support for Copilot Vision (image attachments). -- Support for remote MCP servers. - -### Changed -- Automatically suggests a title for conversations created in agent mode. -- Improved restoration of MCP tool status after Copilot restarts. -- Reduced duplication of MCP server instances. - -### Fixed -- Switching accounts now correctly refreshes the auth token and models. -- Fixed file create/edit issues in agent mode. - -## 0.37.0 - June 18, 2025 -### Added -- **Advanced** settings: Added option to configure **Custom Instructions** for GitHub Copilot during chat sessions. -- **Advanced** settings: Added option to keep the chat window automatically attached to Xcode. - -### Changed -- Enabled support for dragging-and-dropping files into the chat panel to provide context. - -### Fixed -- "Add Context" menu didn’t show files in workspaces organized with Xcode’s group feature. -- Chat didn’t respond when the workspace was in a system folder (like Desktop, Downloads, or Documents) and access permission hadn’t been granted. - -## 0.36.0 - June 4, 2025 -### Added -- Introduced a new chat setting "**Response Language**" under **Advanced** settings to customize the natural language used in chat replies. -- Enabled support for custom instructions defined in _.github/copilot-instructions.md_ within your workspace. -- Added support for premium request handling. - -### Fixed -- Performance: Improved UI responsiveness by lazily restoring chat history. -- Performance: Fixed lagging issue when pasting large text into the chat input. -- Performance: Improved project indexing performance. -- Don't trigger / (slash) commands when pasting a file path into the chat input. -- Adjusted terminal text styling to align with Xcode’s theme. - -## 0.35.0 - May 19, 2025 -### Added -- Launched Agent Mode. Copilot will automatically use multiple requests to edit files, run terminal commands, and fix errors. -- Introduced Model Context Protocol (MCP) support in Agent Mode, allowing you to configure MCP tools to extend capabilities. - -### Changed -- Added a button to enable/disable referencing current file in conversations -- Added an animated progress icon in the response section -- Refined onboarding experience with updated instruction screens and welcome views -- Improved conversation reliability with extended timeout limits for agent requests - -### Fixed -- Addressed critical error handling issues in core functionality -- Resolved UI inconsistencies with chat interface padding adjustments -- Implemented custom certificate handling using system environment variables `NODE_EXTRA_CA_CERTS` and `NODE_TLS_REJECT_UNAUTHORIZED`, fixing network access issues - -## 0.34.0 - April 29, 2025 -### Added -- Added support for new models in Chat: OpenAI GPT-4.1, o3 and o4-mini, Gemini 2.5 Pro - -### Changed -- Switched default model to GPT-4.1 for new installations -- Enhanced model selection interface - -### Fixed -- Resolved critical error handling issues - -## 0.33.0 - April 17, 2025 -### Added -- Added support for new models in Chat: Claude 3.7 Sonnet and GPT 4.5 -- Implemented @workspace context feature allowing questions about the entire codebase in Copilot Chat - -### Changed -- Simplified access to Copilot Chat from the Copilot for Xcode app with a single click -- Enhanced instructions for granting background permissions - -### Fixed -- Resolved false alarms for sign-in and free plan limit notifications -- Improved app launch performance -- Fixed workspace and context update issues - -## 0.32.0 - March 11, 2025 (General Availability) -### Added -- Implemented model picker for selecting LLM model in chat -- Introduced new `/releaseNotes` slash command for accessing release information - -### Changed -- Improved focus handling with automatic switching between chat text field and file search bar -- Enhanced keyboard navigation support for file picker in chat context -- Refined instructions for granting accessibility and extension permissions -- Enhanced accessibility compliance for the chat window -- Redesigned notification and status bar menu styles for better usability - -### Fixed -- Resolved compatibility issues with macOS 12/13/14 -- Fixed handling of invalid workspace switch event '/' -- Corrected chat attachment file picker to respect workspace scope -- Improved icon display consistency across different themes -- Added support for previously unsupported file types (.md, .txt) in attachments -- Adjusted incorrect margins in chat window UI - -## 0.31.0 - February 11, 2025 (Public Preview) -### Added -- Added Copilot Chat support -- Added GitHub Freeplan support -- Implemented conversation and chat history management across multiple Xcode instances -- Introduced multi-file context support for comprehensive code understanding -- Added slash commands for specialized operations diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index a1f82f0d..00000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,74 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, gender identity and expression, level of experience, -nationality, personal appearance, race, religion, or sexual identity and -orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment -include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or -advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at . All -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at [http://contributor-covenant.org/version/1/4][version] - -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ \ No newline at end of file diff --git a/CommunicationBridge/ServiceDelegate.swift b/CommunicationBridge/ServiceDelegate.swift deleted file mode 100644 index 4e289e57..00000000 --- a/CommunicationBridge/ServiceDelegate.swift +++ /dev/null @@ -1,237 +0,0 @@ -import AppKit -import Foundation -import Logger -import XPCShared - -class ServiceDelegate: NSObject, NSXPCListenerDelegate { - func listener( - _: NSXPCListener, - shouldAcceptNewConnection newConnection: NSXPCConnection - ) -> Bool { - newConnection.exportedInterface = NSXPCInterface( - with: CommunicationBridgeXPCServiceProtocol.self - ) - - let exportedObject = XPCService() - newConnection.exportedObject = exportedObject - newConnection.resume() - - Logger.communicationBridge.info("Accepted new connection.") - - return true - } -} - -class XPCService: CommunicationBridgeXPCServiceProtocol { - static let eventHandler = EventHandler() - - func launchExtensionServiceIfNeeded( - withReply reply: @escaping (NSXPCListenerEndpoint?) -> Void - ) { - Task { - await Self.eventHandler.launchExtensionServiceIfNeeded(withReply: reply) - } - } - - func quit(withReply reply: @escaping () -> Void) { - Task { - await Self.eventHandler.quit(withReply: reply) - } - } - - func updateServiceEndpoint( - endpoint: NSXPCListenerEndpoint, - withReply reply: @escaping () -> Void - ) { - Task { - await Self.eventHandler.updateServiceEndpoint(endpoint: endpoint, withReply: reply) - } - } -} - -actor EventHandler { - var endpoint: NSXPCListenerEndpoint? - let launcher = ExtensionServiceLauncher() - var exitTask: Task? - - init() { - Task { await rescheduleExitTask() } - } - - func launchExtensionServiceIfNeeded( - withReply reply: @escaping (NSXPCListenerEndpoint?) -> Void - ) async { - rescheduleExitTask() - #if DEBUG - if let endpoint, !(await testXPCListenerEndpoint(endpoint)) { - self.endpoint = nil - } - reply(endpoint) - #else - if await launcher.isApplicationValid { - Logger.communicationBridge.info("Service app is still valid") - reply(endpoint) - } else { - endpoint = nil - await launcher.launch() - reply(nil) - } - #endif - } - - func quit(withReply reply: () -> Void) { - Logger.communicationBridge.info("Exiting service.") - listener.invalidate() - exit(0) - } - - func updateServiceEndpoint(endpoint: NSXPCListenerEndpoint, withReply reply: () -> Void) { - rescheduleExitTask() - self.endpoint = endpoint - reply() - } - - /// The bridge will kill itself when it's not used for a period. - /// It's fine that the bridge is killed because it will be launched again when needed. - private func rescheduleExitTask() { - exitTask?.cancel() - exitTask = Task { - #if DEBUG - try await Task.sleep(nanoseconds: 60_000_000_000) - Logger.communicationBridge.info("Exit will be called in release build.") - #else - try await Task.sleep(nanoseconds: 1_800_000_000_000) - Logger.communicationBridge.info("Exiting service.") - listener.invalidate() - exit(0) - #endif - } - } -} - -actor ExtensionServiceLauncher { - let appIdentifier = bundleIdentifierBase.appending(".ExtensionService") - let appURL = Bundle.main.bundleURL.appendingPathComponent( - "GitHub Copilot for Xcode Extension.app" - ) - var isLaunching: Bool = false - var application: NSRunningApplication? - var isApplicationValid: Bool { - guard let application else { return false } - if application.isTerminated { return false } - let identifier = application.processIdentifier - if let application = NSWorkspace.shared.runningApplications.first(where: { - $0.processIdentifier == identifier - }) { - Logger.communicationBridge.info( - "Service app found: \(application.processIdentifier) \(String(describing: application.bundleIdentifier))" - ) - return true - } - return false - } - - func launch() { - guard !isLaunching else { return } - isLaunching = true - - Logger.communicationBridge.info("Launching extension service app.") - - // First check if the app is already running - if let runningApp = NSWorkspace.shared.runningApplications.first(where: { - $0.bundleIdentifier == appIdentifier - }) { - Logger.communicationBridge.info("Extension service app already running with PID: \(runningApp.processIdentifier)") - self.application = runningApp - self.isLaunching = false - return - } - - // Implement a retry mechanism with exponential backoff - Task { - var retryCount = 0 - let maxRetries = 3 - var success = false - - while !success && retryCount < maxRetries { - do { - // Add a delay between retries with exponential backoff - if retryCount > 0 { - let delaySeconds = pow(2.0, Double(retryCount - 1)) - Logger.communicationBridge.info("Retrying launch after \(delaySeconds) seconds (attempt \(retryCount + 1) of \(maxRetries))") - try await Task.sleep(nanoseconds: UInt64(delaySeconds * 1_000_000_000)) - } - - // Use a task-based approach for launching with timeout - let launchTask = Task { () -> NSRunningApplication? in - return await withCheckedContinuation { continuation in - NSWorkspace.shared.openApplication( - at: appURL, - configuration: { - let configuration = NSWorkspace.OpenConfiguration() - configuration.createsNewApplicationInstance = false - configuration.addsToRecentItems = false - configuration.activates = false - return configuration - }() - ) { app, error in - if let error = error { - continuation.resume(returning: nil) - } else { - continuation.resume(returning: app) - } - } - } - } - - // Set a timeout for the launch operation - let timeoutTask = Task { - try await Task.sleep(nanoseconds: 10_000_000_000) // 10 seconds - return - } - - // Wait for either the launch or the timeout - let app = try await withTaskCancellationHandler { - try await launchTask.value ?? nil - } onCancel: { - launchTask.cancel() - } - - // Cancel the timeout task - timeoutTask.cancel() - - if let app = app { - // Success! - self.application = app - success = true - break - } else { - // App is nil, retry - retryCount += 1 - Logger.communicationBridge.info("Launch attempt \(retryCount) failed, app is nil") - } - } catch { - retryCount += 1 - Logger.communicationBridge.error("Error during launch attempt \(retryCount): \(error.localizedDescription)") - } - } - - // Double-check we have a valid application - if !success && self.application == nil { - // After all retries, check once more if the app is running (it might have launched but we missed the callback) - if let runningApp = NSWorkspace.shared.runningApplications.first(where: { - $0.bundleIdentifier == appIdentifier - }) { - Logger.communicationBridge.info("Found running extension service after retries: \(runningApp.processIdentifier)") - self.application = runningApp - success = true - } else { - Logger.communicationBridge.info("Failed to launch extension service after \(maxRetries) attempts") - } - } - - self.isLaunching = false - } - } -} - diff --git a/CommunicationBridge/main.swift b/CommunicationBridge/main.swift deleted file mode 100644 index b420b80f..00000000 --- a/CommunicationBridge/main.swift +++ /dev/null @@ -1,21 +0,0 @@ -import AppKit -import Foundation -import Logger - -class AppDelegate: NSObject, NSApplicationDelegate {} - -let bundleIdentifierBase = Bundle(url: Bundle.main.bundleURL.appendingPathComponent( - "GitHub Copilot For Xcode Extension.app" -))?.object(forInfoDictionaryKey: "BUNDLE_IDENTIFIER_BASE") as? String ?? "com.github.CopilotForXcode" - -let serviceIdentifier = bundleIdentifierBase + ".CommunicationBridge" -let appDelegate = AppDelegate() -let delegate = ServiceDelegate() -let listener = NSXPCListener(machServiceName: serviceIdentifier) -listener.delegate = delegate -listener.resume() -let app = NSApplication.shared -app.delegate = appDelegate -Logger.communicationBridge.info("Communication bridge started") -app.run() - diff --git a/Config.debug.xcconfig b/Config.debug.xcconfig deleted file mode 100644 index 63fae668..00000000 --- a/Config.debug.xcconfig +++ /dev/null @@ -1,17 +0,0 @@ -#include "Version.xcconfig" -SLASH = / // Otherwise a double slash is treated as a comment, even inside a quoted string - -HOST_APP_NAME = GitHub Copilot for Xcode Dev -BUNDLE_IDENTIFIER_BASE = dev.com.github.CopilotForXcode -SPARKLE_FEED_URL = https:$(SLASH)$(SLASH)githubcopilotide.z13.web.core.windows.net/appcast.xml -SPARKLE_PUBLIC_KEY = EGlZbKpzATrZFfzr142PrZbmQr5opzdC8urMU8+dKL0= -APPLICATION_SUPPORT_FOLDER = dev.com.github.CopilotForXcode -EXTENSION_BUNDLE_NAME = GitHub Copilot Dev -EXTENSION_BUNDLE_DISPLAY_NAME = GitHub Copilot Dev -EXTENSION_SERVICE_NAME = GitHub Copilot for Xcode Extension -COPILOT_DOCS_URL = https:$(SLASH)$(SLASH)docs.github.com/en/copilot -COPILOT_FORUM_URL = https:$(SLASH)$(SLASH)github.com/orgs/community/discussions/categories/copilot - -// see also target Configs - -#include? "Config.local.xcconfig" diff --git a/Config.xcconfig b/Config.xcconfig deleted file mode 100644 index 5fba3479..00000000 --- a/Config.xcconfig +++ /dev/null @@ -1,15 +0,0 @@ -#include "Version.xcconfig" -SLASH = / // Otherwise a double slash is treated as a comment, even inside a quoted string - -HOST_APP_NAME = GitHub Copilot for Xcode -BUNDLE_IDENTIFIER_BASE = com.github.CopilotForXcode -SPARKLE_FEED_URL = https:$(SLASH)$(SLASH)githubcopilotide.z13.web.core.windows.net/appcast.xml -SPARKLE_PUBLIC_KEY = EGlZbKpzATrZFfzr142PrZbmQr5opzdC8urMU8+dKL0= -APPLICATION_SUPPORT_FOLDER = com.github.CopilotForXcode -EXTENSION_BUNDLE_NAME = GitHub Copilot -EXTENSION_BUNDLE_DISPLAY_NAME = GitHub Copilot -EXTENSION_SERVICE_NAME = GitHub Copilot for Xcode Extension -COPILOT_DOCS_URL = https:$(SLASH)$(SLASH)docs.github.com/en/copilot -COPILOT_FORUM_URL = https:$(SLASH)$(SLASH)github.com/orgs/community/discussions/categories/copilot - -// see also target Configs diff --git a/Copilot for Xcode.xcodeproj/project.pbxproj b/Copilot for Xcode.xcodeproj/project.pbxproj deleted file mode 100644 index 844f7d7c..00000000 --- a/Copilot for Xcode.xcodeproj/project.pbxproj +++ /dev/null @@ -1,1372 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 70; - objects = { - -/* Begin PBXBuildFile section */ - 3ABBEA292C8B9FE100C61D61 /* copilot-language-server in Resources */ = {isa = PBXBuildFile; fileRef = 3ABBEA282C8B9FE100C61D61 /* copilot-language-server */; }; - 3ABBEA2B2C8BA00300C61D61 /* copilot-language-server-arm64 in Resources */ = {isa = PBXBuildFile; fileRef = 3ABBEA2A2C8BA00300C61D61 /* copilot-language-server-arm64 */; }; - 3ABBEA2C2C8BA00800C61D61 /* copilot-language-server-arm64 in Resources */ = {isa = PBXBuildFile; fileRef = 3ABBEA2A2C8BA00300C61D61 /* copilot-language-server-arm64 */; }; - 3ABBEA2D2C8BA00B00C61D61 /* copilot-language-server in Resources */ = {isa = PBXBuildFile; fileRef = 3ABBEA282C8B9FE100C61D61 /* copilot-language-server */; }; - 3E5DB7502D6B8FA500418952 /* ReleaseNotes.md in Resources */ = {isa = PBXBuildFile; fileRef = 3E5DB74F2D6B88EE00418952 /* ReleaseNotes.md */; }; - 424ACA212CA4697200FA20F2 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 424ACA202CA4697200FA20F2 /* Credits.rtf */; }; - 427C63282C6E868B000E557C /* OpenSettingsCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 427C63272C6E868B000E557C /* OpenSettingsCommand.swift */; }; - 5EC511E32C90CE7400632BAB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8189B1D2938973000C9DCDA /* Assets.xcassets */; }; - 5EC511E42C90CE9800632BAB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8189B1D2938973000C9DCDA /* Assets.xcassets */; }; - 5EC511E52C90CFD600632BAB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C861E6142994F6080056CB02 /* Assets.xcassets */; }; - 5EC511E62C90CFD700632BAB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C861E6142994F6080056CB02 /* Assets.xcassets */; }; - C8009BFF2941C551007AA7E8 /* ToggleRealtimeSuggestionsCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8009BFE2941C551007AA7E8 /* ToggleRealtimeSuggestionsCommand.swift */; }; - C8009C032941C576007AA7E8 /* SyncTextSettingsCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8009C022941C576007AA7E8 /* SyncTextSettingsCommand.swift */; }; - C800DBB1294C624D00B04CAC /* PrefetchSuggestionsCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C800DBB0294C624D00B04CAC /* PrefetchSuggestionsCommand.swift */; }; - C80FFB962A95F58200704A25 /* AcceptPromptToCodeCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80FFB952A95F58200704A25 /* AcceptPromptToCodeCommand.swift */; }; - C81291D72994FE6900196E12 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C81291D52994FE6900196E12 /* Main.storyboard */; }; - C814588F2939EFDC00135263 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C814588E2939EFDC00135263 /* Cocoa.framework */; }; - C81458942939EFDC00135263 /* SourceEditorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C81458932939EFDC00135263 /* SourceEditorExtension.swift */; }; - C81458962939EFDC00135263 /* GetSuggestionsCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C81458952939EFDC00135263 /* GetSuggestionsCommand.swift */; }; - C814589B2939EFDC00135263 /* Copilot.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = C814588C2939EFDC00135263 /* Copilot.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - C8189B1A2938972F00C9DCDA /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8189B192938972F00C9DCDA /* App.swift */; }; - C8189B1E2938973000C9DCDA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8189B1D2938973000C9DCDA /* Assets.xcassets */; }; - C8189B212938973000C9DCDA /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8189B202938973000C9DCDA /* Preview Assets.xcassets */; }; - C8216B73298036EC00AD38C7 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8216B72298036EC00AD38C7 /* main.swift */; }; - C8216B782980370100AD38C7 /* ReloadLaunchAgent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8216B772980370100AD38C7 /* ReloadLaunchAgent.swift */; }; - C8216B7D2980374300AD38C7 /* ArgumentParser in Frameworks */ = {isa = PBXBuildFile; productRef = C8216B7C2980374300AD38C7 /* ArgumentParser */; }; - C8216B802980378300AD38C7 /* Helper in Embed XPCService */ = {isa = PBXBuildFile; fileRef = C8216B70298036EC00AD38C7 /* Helper */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - C828B27F2B1F7B4F00E7612A /* ExtensionPoint.appextensionpoint in Copy Extension Point */ = {isa = PBXBuildFile; fileRef = C828B27D2B1F241500E7612A /* ExtensionPoint.appextensionpoint */; }; - C8520301293C4D9000460097 /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8520300293C4D9000460097 /* Helpers.swift */; }; - C861A6A329E5503F005C41A3 /* PromptToCodeCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C861A6A229E5503F005C41A3 /* PromptToCodeCommand.swift */; }; - C861E6112994F6070056CB02 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C861E6102994F6070056CB02 /* AppDelegate.swift */; }; - C861E6152994F6080056CB02 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C861E6142994F6080056CB02 /* Assets.xcassets */; }; - C861E61E2994F6150056CB02 /* Service in Frameworks */ = {isa = PBXBuildFile; productRef = C861E61D2994F6150056CB02 /* Service */; }; - C861E6202994F63A0056CB02 /* ServiceDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C861E61F2994F6390056CB02 /* ServiceDelegate.swift */; }; - C86612F82A06AF74009197D9 /* HostApp in Frameworks */ = {isa = PBXBuildFile; productRef = C86612F72A06AF74009197D9 /* HostApp */; }; - C8738B662BE4D4B900609E7F /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8738B652BE4D4B900609E7F /* main.swift */; }; - C8738B6B2BE4D56F00609E7F /* ServiceDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8738B6A2BE4D56F00609E7F /* ServiceDelegate.swift */; }; - C8738B6F2BE4F7A600609E7F /* XPCShared in Frameworks */ = {isa = PBXBuildFile; productRef = C8738B6E2BE4F7A600609E7F /* XPCShared */; }; - C8738B712BE4F8B700609E7F /* XPCController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8738B702BE4F8B700609E7F /* XPCController.swift */; }; - C8738B7B2BE5363800609E7F /* SandboxedClientTesterApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8738B7A2BE5363800609E7F /* SandboxedClientTesterApp.swift */; }; - C8738B7D2BE5363800609E7F /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8738B7C2BE5363800609E7F /* ContentView.swift */; }; - C8738B7F2BE5363900609E7F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8738B7E2BE5363900609E7F /* Assets.xcassets */; }; - C8738B822BE5363900609E7F /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C8738B812BE5363900609E7F /* Preview Assets.xcassets */; }; - C8738B882BE5365000609E7F /* Client in Frameworks */ = {isa = PBXBuildFile; productRef = C8738B872BE5365000609E7F /* Client */; }; - C8738B8A2BE540D000609E7F /* bridgeLaunchAgent.plist in Copy Launch Agent */ = {isa = PBXBuildFile; fileRef = C8738B6D2BE4F3E800609E7F /* bridgeLaunchAgent.plist */; }; - C8738B8B2BE540DD00609E7F /* CommunicationBridge in Embed XPCService */ = {isa = PBXBuildFile; fileRef = C8738B632BE4D4B900609E7F /* CommunicationBridge */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - C8758E7029F04BFF00D29C1C /* CustomCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8758E6F29F04BFF00D29C1C /* CustomCommand.swift */; }; - C8758E7229F04CF100D29C1C /* SeparatorCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8758E7129F04CF100D29C1C /* SeparatorCommand.swift */; }; - C87B03A5293B261200C77EAE /* AcceptSuggestionCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C87B03A4293B261200C77EAE /* AcceptSuggestionCommand.swift */; }; - C87B03A7293B261900C77EAE /* RejectSuggestionCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C87B03A6293B261900C77EAE /* RejectSuggestionCommand.swift */; }; - C87B03A9293B262600C77EAE /* NextSuggestionCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C87B03A8293B262600C77EAE /* NextSuggestionCommand.swift */; }; - C87B03AB293B262E00C77EAE /* PreviousSuggestionCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C87B03AA293B262E00C77EAE /* PreviousSuggestionCommand.swift */; }; - C87B03AC293B2CF300C77EAE /* XcodeKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C81458902939EFDC00135263 /* XcodeKit.framework */; }; - C87B03AD293B2CF300C77EAE /* XcodeKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = C81458902939EFDC00135263 /* XcodeKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - C882175C294187EF00A22FD3 /* Client in Frameworks */ = {isa = PBXBuildFile; productRef = C882175B294187EF00A22FD3 /* Client */; }; - C89E75C32A46FB32000DD64F /* AppDelegate+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C89E75C22A46FB32000DD64F /* AppDelegate+Menu.swift */; }; - C8C8B60929AFA35F00034BEE /* GitHub Copilot for Xcode Extension.app in Embed XPCService */ = {isa = PBXBuildFile; fileRef = C861E60E2994F6070056CB02 /* GitHub Copilot for Xcode Extension.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; - C8DCF00029CE11D500FDDDD7 /* OpenChat.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DCEFFF29CE11D500FDDDD7 /* OpenChat.swift */; }; - C8DD9CB12BC673F80036641C /* CloseIdleTabsCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DD9CB02BC673F80036641C /* CloseIdleTabsCommand.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - C81291AF2994F92700196E12 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = C8189B0E2938972F00C9DCDA /* Project object */; - proxyType = 1; - remoteGlobalIDString = C861E60D2994F6070056CB02; - remoteInfo = ExtensionService; - }; - C81458992939EFDC00135263 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = C8189B0E2938972F00C9DCDA /* Project object */; - proxyType = 1; - remoteGlobalIDString = C814588B2939EFDC00135263; - remoteInfo = EditorExtension; - }; - C8216B7E2980377E00AD38C7 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = C8189B0E2938972F00C9DCDA /* Project object */; - proxyType = 1; - remoteGlobalIDString = C8216B6F298036EC00AD38C7; - remoteInfo = Helper; - }; - C8738B8C2BE540F900609E7F /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = C8189B0E2938972F00C9DCDA /* Project object */; - proxyType = 1; - remoteGlobalIDString = C8738B622BE4D4B900609E7F; - remoteInfo = CommunicationBridge; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - C814589F2939EFDC00135263 /* Embed Foundation Extensions */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 13; - files = ( - C814589B2939EFDC00135263 /* Copilot.appex in Embed Foundation Extensions */, - ); - name = "Embed Foundation Extensions"; - runOnlyForDeploymentPostprocessing = 0; - }; - C8216B6E298036EC00AD38C7 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = /usr/share/man/man1/; - dstSubfolderSpec = 0; - files = ( - ); - runOnlyForDeploymentPostprocessing = 1; - }; - C828B27E2B1F7B3C00E7612A /* Copy Extension Point */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = "$(EXTENSIONS_FOLDER_PATH)"; - dstSubfolderSpec = 16; - files = ( - C828B27F2B1F7B4F00E7612A /* ExtensionPoint.appextensionpoint in Copy Extension Point */, - ); - name = "Copy Extension Point"; - runOnlyForDeploymentPostprocessing = 0; - }; - C8520306293CF0EF00460097 /* Embed XPCService */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ../Applications; - dstSubfolderSpec = 6; - files = ( - C8738B8B2BE540DD00609E7F /* CommunicationBridge in Embed XPCService */, - C8216B802980378300AD38C7 /* Helper in Embed XPCService */, - C8C8B60929AFA35F00034BEE /* GitHub Copilot for Xcode Extension.app in Embed XPCService */, - ); - name = "Embed XPCService"; - runOnlyForDeploymentPostprocessing = 0; - }; - C8738B612BE4D4B900609E7F /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 12; - dstPath = ""; - dstSubfolderSpec = 16; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C87B03AE293B2CF300C77EAE /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - C87B03AD293B2CF300C77EAE /* XcodeKit.framework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; - C8C8B60829AFA32800034BEE /* Embed Service */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = "$(CONTENTS_FOLDER_PATH)/XPCServices"; - dstSubfolderSpec = 16; - files = ( - ); - name = "Embed Service"; - runOnlyForDeploymentPostprocessing = 0; - }; - C8F1032A2A7A38D200D28F4F /* Copy Launch Agent */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 12; - dstPath = Contents/Library/LaunchAgents; - dstSubfolderSpec = 1; - files = ( - C8738B8A2BE540D000609E7F /* bridgeLaunchAgent.plist in Copy Launch Agent */, - ); - name = "Copy Launch Agent"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 3ABBEA282C8B9FE100C61D61 /* copilot-language-server */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = "copilot-language-server"; path = "Server/node_modules/@github/copilot-language-server/native/darwin-x64/copilot-language-server"; sourceTree = SOURCE_ROOT; }; - 3ABBEA2A2C8BA00300C61D61 /* copilot-language-server-arm64 */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = "copilot-language-server-arm64"; path = "Server/node_modules/@github/copilot-language-server/native/darwin-arm64/copilot-language-server-arm64"; sourceTree = SOURCE_ROOT; }; - 3E5DB74F2D6B88EE00418952 /* ReleaseNotes.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = ReleaseNotes.md; sourceTree = ""; }; - 424ACA202CA4697200FA20F2 /* Credits.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = Credits.rtf; sourceTree = ""; }; - 427C63272C6E868B000E557C /* OpenSettingsCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenSettingsCommand.swift; sourceTree = ""; }; - C8009BFE2941C551007AA7E8 /* ToggleRealtimeSuggestionsCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToggleRealtimeSuggestionsCommand.swift; sourceTree = ""; }; - C8009C022941C576007AA7E8 /* SyncTextSettingsCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncTextSettingsCommand.swift; sourceTree = ""; }; - C800DBB0294C624D00B04CAC /* PrefetchSuggestionsCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefetchSuggestionsCommand.swift; sourceTree = ""; }; - C80FFB952A95F58200704A25 /* AcceptPromptToCodeCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AcceptPromptToCodeCommand.swift; sourceTree = ""; }; - C81291D52994FE6900196E12 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; - C81291D92994FE7900196E12 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; - C814588C2939EFDC00135263 /* Copilot.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = Copilot.appex; sourceTree = BUILT_PRODUCTS_DIR; }; - C814588E2939EFDC00135263 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; - C81458902939EFDC00135263 /* XcodeKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XcodeKit.framework; path = Library/Frameworks/XcodeKit.framework; sourceTree = DEVELOPER_DIR; }; - C81458932939EFDC00135263 /* SourceEditorExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceEditorExtension.swift; sourceTree = ""; }; - C81458952939EFDC00135263 /* GetSuggestionsCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetSuggestionsCommand.swift; sourceTree = ""; }; - C81458972939EFDC00135263 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - C81458982939EFDC00135263 /* EditorExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = EditorExtension.entitlements; sourceTree = ""; }; - C81458AD293A009600135263 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; - C81458AE293A009800135263 /* Config.debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.debug.xcconfig; sourceTree = ""; }; - C8189B162938972F00C9DCDA /* GitHub Copilot for Xcode Dev.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "GitHub Copilot for Xcode Dev.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - C8189B192938972F00C9DCDA /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = ""; }; - C8189B1D2938973000C9DCDA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - C8189B202938973000C9DCDA /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; - C8189B222938973000C9DCDA /* Copilot_for_Xcode.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Copilot_for_Xcode.entitlements; sourceTree = ""; }; - C8189B282938979000C9DCDA /* Core */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Core; sourceTree = ""; }; - C81D181E2A1B509B006C1B70 /* Tool */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Tool; sourceTree = ""; }; - C81E867D296FE4420026E908 /* Version.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Version.xcconfig; sourceTree = ""; }; - C8216B70298036EC00AD38C7 /* Helper */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = Helper; sourceTree = BUILT_PRODUCTS_DIR; }; - C8216B72298036EC00AD38C7 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; - C8216B772980370100AD38C7 /* ReloadLaunchAgent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReloadLaunchAgent.swift; sourceTree = ""; }; - C828B27D2B1F241500E7612A /* ExtensionPoint.appextensionpoint */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = ExtensionPoint.appextensionpoint; sourceTree = ""; }; - C8520300293C4D9000460097 /* Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = ""; }; - C8520308293D805800460097 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - C861A6A229E5503F005C41A3 /* PromptToCodeCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PromptToCodeCommand.swift; sourceTree = ""; }; - C861E60E2994F6070056CB02 /* GitHub Copilot for Xcode Extension.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "GitHub Copilot for Xcode Extension.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - C861E6102994F6070056CB02 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - C861E6142994F6080056CB02 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - C861E6192994F6080056CB02 /* ExtensionService.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ExtensionService.entitlements; sourceTree = ""; }; - C861E61F2994F6390056CB02 /* ServiceDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceDelegate.swift; sourceTree = ""; }; - C8738B632BE4D4B900609E7F /* CommunicationBridge */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = CommunicationBridge; sourceTree = BUILT_PRODUCTS_DIR; }; - C8738B652BE4D4B900609E7F /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; - C8738B6A2BE4D56F00609E7F /* ServiceDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceDelegate.swift; sourceTree = ""; }; - C8738B6D2BE4F3E800609E7F /* bridgeLaunchAgent.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = bridgeLaunchAgent.plist; sourceTree = ""; }; - C8738B702BE4F8B700609E7F /* XPCController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XPCController.swift; sourceTree = ""; }; - C8738B782BE5363800609E7F /* SandboxedClientTester.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SandboxedClientTester.app; sourceTree = BUILT_PRODUCTS_DIR; }; - C8738B7A2BE5363800609E7F /* SandboxedClientTesterApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SandboxedClientTesterApp.swift; sourceTree = ""; }; - C8738B7C2BE5363800609E7F /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; - C8738B7E2BE5363900609E7F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - C8738B812BE5363900609E7F /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; - C8738B832BE5363900609E7F /* SandboxedClientTester.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SandboxedClientTester.entitlements; sourceTree = ""; }; - C8738B892BE5379E00609E7F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; - C8758E6F29F04BFF00D29C1C /* CustomCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomCommand.swift; sourceTree = ""; }; - C8758E7129F04CF100D29C1C /* SeparatorCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeparatorCommand.swift; sourceTree = ""; }; - C87B03A3293B24AB00C77EAE /* Copilot-for-Xcode-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "Copilot-for-Xcode-Info.plist"; sourceTree = SOURCE_ROOT; }; - C87B03A4293B261200C77EAE /* AcceptSuggestionCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AcceptSuggestionCommand.swift; sourceTree = ""; }; - C87B03A6293B261900C77EAE /* RejectSuggestionCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RejectSuggestionCommand.swift; sourceTree = ""; }; - C87B03A8293B262600C77EAE /* NextSuggestionCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextSuggestionCommand.swift; sourceTree = ""; }; - C87B03AA293B262E00C77EAE /* PreviousSuggestionCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviousSuggestionCommand.swift; sourceTree = ""; }; - C887BC832965D96000931567 /* DEVELOPMENT.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = DEVELOPMENT.md; sourceTree = ""; }; - C89E75C22A46FB32000DD64F /* AppDelegate+Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Menu.swift"; sourceTree = ""; }; - C8CD828229B88006008D044D /* TestPlan.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = TestPlan.xctestplan; sourceTree = ""; }; - C8DCEFFF29CE11D500FDDDD7 /* OpenChat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenChat.swift; sourceTree = ""; }; - C8DD9CB02BC673F80036641C /* CloseIdleTabsCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloseIdleTabsCommand.swift; sourceTree = ""; }; - C8F103292A7A365000D28F4F /* launchAgent.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = launchAgent.plist; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFileSystemSynchronizedRootGroup section */ - 9E6A029A2DBDF64200AB6BD5 /* Server */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = Server; sourceTree = SOURCE_ROOT; }; -/* End PBXFileSystemSynchronizedRootGroup section */ - -/* Begin PBXFrameworksBuildPhase section */ - C81458892939EFDC00135263 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - C87B03AC293B2CF300C77EAE /* XcodeKit.framework in Frameworks */, - C814588F2939EFDC00135263 /* Cocoa.framework in Frameworks */, - C882175C294187EF00A22FD3 /* Client in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C8189B132938972F00C9DCDA /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - C86612F82A06AF74009197D9 /* HostApp in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C8216B6D298036EC00AD38C7 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - C8216B7D2980374300AD38C7 /* ArgumentParser in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C861E60B2994F6070056CB02 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - C861E61E2994F6150056CB02 /* Service in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C8738B602BE4D4B900609E7F /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - C8738B6F2BE4F7A600609E7F /* XPCShared in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C8738B752BE5363800609E7F /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - C8738B882BE5365000609E7F /* Client in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - C814588D2939EFDC00135263 /* Frameworks */ = { - isa = PBXGroup; - children = ( - C814588E2939EFDC00135263 /* Cocoa.framework */, - C81458902939EFDC00135263 /* XcodeKit.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - C81458922939EFDC00135263 /* EditorExtension */ = { - isa = PBXGroup; - children = ( - C81458932939EFDC00135263 /* SourceEditorExtension.swift */, - C8758E7129F04CF100D29C1C /* SeparatorCommand.swift */, - C8520300293C4D9000460097 /* Helpers.swift */, - C81458952939EFDC00135263 /* GetSuggestionsCommand.swift */, - C87B03A4293B261200C77EAE /* AcceptSuggestionCommand.swift */, - C80FFB952A95F58200704A25 /* AcceptPromptToCodeCommand.swift */, - C87B03A6293B261900C77EAE /* RejectSuggestionCommand.swift */, - C87B03A8293B262600C77EAE /* NextSuggestionCommand.swift */, - C87B03AA293B262E00C77EAE /* PreviousSuggestionCommand.swift */, - C8009BFE2941C551007AA7E8 /* ToggleRealtimeSuggestionsCommand.swift */, - C8009C022941C576007AA7E8 /* SyncTextSettingsCommand.swift */, - C800DBB0294C624D00B04CAC /* PrefetchSuggestionsCommand.swift */, - C8758E6F29F04BFF00D29C1C /* CustomCommand.swift */, - C8DCEFFF29CE11D500FDDDD7 /* OpenChat.swift */, - C8DD9CB02BC673F80036641C /* CloseIdleTabsCommand.swift */, - C861A6A229E5503F005C41A3 /* PromptToCodeCommand.swift */, - C81458972939EFDC00135263 /* Info.plist */, - C81458982939EFDC00135263 /* EditorExtension.entitlements */, - 427C63272C6E868B000E557C /* OpenSettingsCommand.swift */, - ); - path = EditorExtension; - sourceTree = ""; - }; - C8189B0D2938972F00C9DCDA = { - isa = PBXGroup; - children = ( - 3E5DB74F2D6B88EE00418952 /* ReleaseNotes.md */, - C887BC832965D96000931567 /* DEVELOPMENT.md */, - C8520308293D805800460097 /* README.md */, - C8F103292A7A365000D28F4F /* launchAgent.plist */, - C8738B6D2BE4F3E800609E7F /* bridgeLaunchAgent.plist */, - C81E867D296FE4420026E908 /* Version.xcconfig */, - C81458AD293A009600135263 /* Config.xcconfig */, - C81458AE293A009800135263 /* Config.debug.xcconfig */, - C8CD828229B88006008D044D /* TestPlan.xctestplan */, - C828B27D2B1F241500E7612A /* ExtensionPoint.appextensionpoint */, - 9E6A029A2DBDF64200AB6BD5 /* Server */, - C81D181E2A1B509B006C1B70 /* Tool */, - C8189B282938979000C9DCDA /* Core */, - C8189B182938972F00C9DCDA /* Copilot for Xcode */, - C81458922939EFDC00135263 /* EditorExtension */, - C8216B71298036EC00AD38C7 /* Helper */, - C861E60F2994F6070056CB02 /* ExtensionService */, - C8738B642BE4D4B900609E7F /* CommunicationBridge */, - C8738B792BE5363800609E7F /* SandboxedClientTester */, - C814588D2939EFDC00135263 /* Frameworks */, - C8189B172938972F00C9DCDA /* Products */, - ); - sourceTree = ""; - }; - C8189B172938972F00C9DCDA /* Products */ = { - isa = PBXGroup; - children = ( - C8189B162938972F00C9DCDA /* GitHub Copilot for Xcode Dev.app */, - C814588C2939EFDC00135263 /* Copilot.appex */, - C8216B70298036EC00AD38C7 /* Helper */, - C861E60E2994F6070056CB02 /* GitHub Copilot for Xcode Extension.app */, - C8738B632BE4D4B900609E7F /* CommunicationBridge */, - C8738B782BE5363800609E7F /* SandboxedClientTester.app */, - ); - name = Products; - sourceTree = ""; - }; - C8189B182938972F00C9DCDA /* Copilot for Xcode */ = { - isa = PBXGroup; - children = ( - 424ACA202CA4697200FA20F2 /* Credits.rtf */, - 3ABBEA2A2C8BA00300C61D61 /* copilot-language-server-arm64 */, - 3ABBEA282C8B9FE100C61D61 /* copilot-language-server */, - C87B03A3293B24AB00C77EAE /* Copilot-for-Xcode-Info.plist */, - C8189B192938972F00C9DCDA /* App.swift */, - C8189B1D2938973000C9DCDA /* Assets.xcassets */, - C8189B222938973000C9DCDA /* Copilot_for_Xcode.entitlements */, - C8189B1F2938973000C9DCDA /* Preview Content */, - ); - path = "Copilot for Xcode"; - sourceTree = ""; - }; - C8189B1F2938973000C9DCDA /* Preview Content */ = { - isa = PBXGroup; - children = ( - C8189B202938973000C9DCDA /* Preview Assets.xcassets */, - ); - path = "Preview Content"; - sourceTree = ""; - }; - C8216B71298036EC00AD38C7 /* Helper */ = { - isa = PBXGroup; - children = ( - C8216B72298036EC00AD38C7 /* main.swift */, - C8216B772980370100AD38C7 /* ReloadLaunchAgent.swift */, - ); - path = Helper; - sourceTree = ""; - }; - C861E60F2994F6070056CB02 /* ExtensionService */ = { - isa = PBXGroup; - children = ( - C81291D92994FE7900196E12 /* Info.plist */, - C861E61F2994F6390056CB02 /* ServiceDelegate.swift */, - C861E6102994F6070056CB02 /* AppDelegate.swift */, - C89E75C22A46FB32000DD64F /* AppDelegate+Menu.swift */, - C8738B702BE4F8B700609E7F /* XPCController.swift */, - C81291D52994FE6900196E12 /* Main.storyboard */, - C861E6142994F6080056CB02 /* Assets.xcassets */, - C861E6192994F6080056CB02 /* ExtensionService.entitlements */, - ); - path = ExtensionService; - sourceTree = ""; - }; - C8738B642BE4D4B900609E7F /* CommunicationBridge */ = { - isa = PBXGroup; - children = ( - C8738B652BE4D4B900609E7F /* main.swift */, - C8738B6A2BE4D56F00609E7F /* ServiceDelegate.swift */, - ); - path = CommunicationBridge; - sourceTree = ""; - }; - C8738B792BE5363800609E7F /* SandboxedClientTester */ = { - isa = PBXGroup; - children = ( - C8738B892BE5379E00609E7F /* Info.plist */, - C8738B7A2BE5363800609E7F /* SandboxedClientTesterApp.swift */, - C8738B7C2BE5363800609E7F /* ContentView.swift */, - C8738B7E2BE5363900609E7F /* Assets.xcassets */, - C8738B832BE5363900609E7F /* SandboxedClientTester.entitlements */, - C8738B802BE5363900609E7F /* Preview Content */, - ); - path = SandboxedClientTester; - sourceTree = ""; - }; - C8738B802BE5363900609E7F /* Preview Content */ = { - isa = PBXGroup; - children = ( - C8738B812BE5363900609E7F /* Preview Assets.xcassets */, - ); - path = "Preview Content"; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - C814588B2939EFDC00135263 /* EditorExtension */ = { - isa = PBXNativeTarget; - buildConfigurationList = C814589C2939EFDC00135263 /* Build configuration list for PBXNativeTarget "EditorExtension" */; - buildPhases = ( - C81458882939EFDC00135263 /* Sources */, - C81458892939EFDC00135263 /* Frameworks */, - C814588A2939EFDC00135263 /* Resources */, - C87B03AE293B2CF300C77EAE /* Embed Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = EditorExtension; - packageProductDependencies = ( - C882175B294187EF00A22FD3 /* Client */, - ); - productName = EditorExtension; - productReference = C814588C2939EFDC00135263 /* Copilot.appex */; - productType = "com.apple.product-type.xcode-extension"; - }; - C8189B152938972F00C9DCDA /* Copilot for Xcode */ = { - isa = PBXNativeTarget; - buildConfigurationList = C8189B252938973000C9DCDA /* Build configuration list for PBXNativeTarget "Copilot for Xcode" */; - buildPhases = ( - C8189B122938972F00C9DCDA /* Sources */, - C8189B132938972F00C9DCDA /* Frameworks */, - C8189B142938972F00C9DCDA /* Resources */, - C814589F2939EFDC00135263 /* Embed Foundation Extensions */, - C8520306293CF0EF00460097 /* Embed XPCService */, - C8C8B60829AFA32800034BEE /* Embed Service */, - C8F1032A2A7A38D200D28F4F /* Copy Launch Agent */, - ); - buildRules = ( - ); - dependencies = ( - C8738B8D2BE540F900609E7F /* PBXTargetDependency */, - C81291B02994F92700196E12 /* PBXTargetDependency */, - C8216B7F2980377E00AD38C7 /* PBXTargetDependency */, - C814589A2939EFDC00135263 /* PBXTargetDependency */, - ); - name = "Copilot for Xcode"; - packageProductDependencies = ( - C86612F72A06AF74009197D9 /* HostApp */, - ); - productName = "Copilot for Xcode"; - productReference = C8189B162938972F00C9DCDA /* GitHub Copilot for Xcode Dev.app */; - productType = "com.apple.product-type.application"; - }; - C8216B6F298036EC00AD38C7 /* Helper */ = { - isa = PBXNativeTarget; - buildConfigurationList = C8216B74298036EC00AD38C7 /* Build configuration list for PBXNativeTarget "Helper" */; - buildPhases = ( - C8216B6C298036EC00AD38C7 /* Sources */, - C8216B6D298036EC00AD38C7 /* Frameworks */, - C8216B6E298036EC00AD38C7 /* CopyFiles */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Helper; - packageProductDependencies = ( - C8216B7C2980374300AD38C7 /* ArgumentParser */, - ); - productName = Helper; - productReference = C8216B70298036EC00AD38C7 /* Helper */; - productType = "com.apple.product-type.tool"; - }; - C861E60D2994F6070056CB02 /* ExtensionService */ = { - isa = PBXNativeTarget; - buildConfigurationList = C861E61A2994F6080056CB02 /* Build configuration list for PBXNativeTarget "ExtensionService" */; - buildPhases = ( - C861E60A2994F6070056CB02 /* Sources */, - C861E60B2994F6070056CB02 /* Frameworks */, - 3A60421A2C8955710006B34C /* ShellScript */, - C861E60C2994F6070056CB02 /* Resources */, - C828B27E2B1F7B3C00E7612A /* Copy Extension Point */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = ExtensionService; - packageProductDependencies = ( - C861E61D2994F6150056CB02 /* Service */, - ); - productName = ExtensionService; - productReference = C861E60E2994F6070056CB02 /* GitHub Copilot for Xcode Extension.app */; - productType = "com.apple.product-type.application"; - }; - C8738B622BE4D4B900609E7F /* CommunicationBridge */ = { - isa = PBXNativeTarget; - buildConfigurationList = C8738B672BE4D4B900609E7F /* Build configuration list for PBXNativeTarget "CommunicationBridge" */; - buildPhases = ( - C8738B5F2BE4D4B900609E7F /* Sources */, - C8738B602BE4D4B900609E7F /* Frameworks */, - C8738B612BE4D4B900609E7F /* CopyFiles */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = CommunicationBridge; - packageProductDependencies = ( - C8738B6E2BE4F7A600609E7F /* XPCShared */, - ); - productName = CommunicationBridge; - productReference = C8738B632BE4D4B900609E7F /* CommunicationBridge */; - productType = "com.apple.product-type.tool"; - }; - C8738B772BE5363800609E7F /* SandboxedClientTester */ = { - isa = PBXNativeTarget; - buildConfigurationList = C8738B842BE5363900609E7F /* Build configuration list for PBXNativeTarget "SandboxedClientTester" */; - buildPhases = ( - C8738B742BE5363800609E7F /* Sources */, - C8738B752BE5363800609E7F /* Frameworks */, - C8738B762BE5363800609E7F /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = SandboxedClientTester; - packageProductDependencies = ( - C8738B872BE5365000609E7F /* Client */, - ); - productName = SandboxedClientTester; - productReference = C8738B782BE5363800609E7F /* SandboxedClientTester.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - C8189B0E2938972F00C9DCDA /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1520; - LastUpgradeCheck = 1410; - TargetAttributes = { - C814588B2939EFDC00135263 = { - CreatedOnToolsVersion = 14.1; - }; - C8189B152938972F00C9DCDA = { - CreatedOnToolsVersion = 14.1; - }; - C8216B6F298036EC00AD38C7 = { - CreatedOnToolsVersion = 14.1; - }; - C861E60D2994F6070056CB02 = { - CreatedOnToolsVersion = 14.2; - }; - C8738B622BE4D4B900609E7F = { - CreatedOnToolsVersion = 15.2; - }; - C8738B772BE5363800609E7F = { - CreatedOnToolsVersion = 15.2; - }; - }; - }; - buildConfigurationList = C8189B112938972F00C9DCDA /* Build configuration list for PBXProject "Copilot for Xcode" */; - compatibilityVersion = "Xcode 14.0"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = C8189B0D2938972F00C9DCDA; - packageReferences = ( - C8216B792980373800AD38C7 /* XCRemoteSwiftPackageReference "swift-argument-parser" */, - ); - productRefGroup = C8189B172938972F00C9DCDA /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - C8189B152938972F00C9DCDA /* Copilot for Xcode */, - C814588B2939EFDC00135263 /* EditorExtension */, - C8216B6F298036EC00AD38C7 /* Helper */, - C861E60D2994F6070056CB02 /* ExtensionService */, - C8738B622BE4D4B900609E7F /* CommunicationBridge */, - C8738B772BE5363800609E7F /* SandboxedClientTester */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - C814588A2939EFDC00135263 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 5EC511E52C90CFD600632BAB /* Assets.xcassets in Resources */, - 5EC511E32C90CE7400632BAB /* Assets.xcassets in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C8189B142938972F00C9DCDA /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 424ACA212CA4697200FA20F2 /* Credits.rtf in Resources */, - C8189B212938973000C9DCDA /* Preview Assets.xcassets in Resources */, - C8189B1E2938973000C9DCDA /* Assets.xcassets in Resources */, - 3ABBEA292C8B9FE100C61D61 /* copilot-language-server in Resources */, - 3ABBEA2B2C8BA00300C61D61 /* copilot-language-server-arm64 in Resources */, - 5EC511E62C90CFD700632BAB /* Assets.xcassets in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C861E60C2994F6070056CB02 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 3ABBEA2C2C8BA00800C61D61 /* copilot-language-server-arm64 in Resources */, - C861E6152994F6080056CB02 /* Assets.xcassets in Resources */, - 3ABBEA2D2C8BA00B00C61D61 /* copilot-language-server in Resources */, - C81291D72994FE6900196E12 /* Main.storyboard in Resources */, - 3E5DB7502D6B8FA500418952 /* ReleaseNotes.md in Resources */, - 5EC511E42C90CE9800632BAB /* Assets.xcassets in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C8738B762BE5363800609E7F /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - C8738B822BE5363900609E7F /* Preview Assets.xcassets in Resources */, - C8738B7F2BE5363900609E7F /* Assets.xcassets in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3A60421A2C8955710006B34C /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "export PATH=/usr/local/bin:/opt/homebrew/bin:$PATH\n\nnpm -C Server install\ncp Server/node_modules/@github/copilot-language-server/native/darwin-arm64/copilot-language-server Server/node_modules/@github/copilot-language-server/native/darwin-arm64/copilot-language-server-arm64\n\necho \"Build and copy webview js/html files as the bundle resources\"\nnpm -C Server run build\nmkdir -p \"${BUILT_PRODUCTS_DIR}/${CONTENTS_FOLDER_PATH}/Resources/webViewDist\"\ncp -R Server/dist/* \"${BUILT_PRODUCTS_DIR}/${CONTENTS_FOLDER_PATH}/Resources/webViewDist/\"\n"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - C81458882939EFDC00135263 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - C8DCF00029CE11D500FDDDD7 /* OpenChat.swift in Sources */, - C81458942939EFDC00135263 /* SourceEditorExtension.swift in Sources */, - C8DD9CB12BC673F80036641C /* CloseIdleTabsCommand.swift in Sources */, - C8758E7029F04BFF00D29C1C /* CustomCommand.swift in Sources */, - C8758E7229F04CF100D29C1C /* SeparatorCommand.swift in Sources */, - C861A6A329E5503F005C41A3 /* PromptToCodeCommand.swift in Sources */, - C8520301293C4D9000460097 /* Helpers.swift in Sources */, - C8009BFF2941C551007AA7E8 /* ToggleRealtimeSuggestionsCommand.swift in Sources */, - C80FFB962A95F58200704A25 /* AcceptPromptToCodeCommand.swift in Sources */, - 427C63282C6E868B000E557C /* OpenSettingsCommand.swift in Sources */, - C87B03A5293B261200C77EAE /* AcceptSuggestionCommand.swift in Sources */, - C87B03A9293B262600C77EAE /* NextSuggestionCommand.swift in Sources */, - C87B03AB293B262E00C77EAE /* PreviousSuggestionCommand.swift in Sources */, - C87B03A7293B261900C77EAE /* RejectSuggestionCommand.swift in Sources */, - C8009C032941C576007AA7E8 /* SyncTextSettingsCommand.swift in Sources */, - C800DBB1294C624D00B04CAC /* PrefetchSuggestionsCommand.swift in Sources */, - C81458962939EFDC00135263 /* GetSuggestionsCommand.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C8189B122938972F00C9DCDA /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - C8189B1A2938972F00C9DCDA /* App.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C8216B6C298036EC00AD38C7 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - C8216B73298036EC00AD38C7 /* main.swift in Sources */, - C8216B782980370100AD38C7 /* ReloadLaunchAgent.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C861E60A2994F6070056CB02 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - C89E75C32A46FB32000DD64F /* AppDelegate+Menu.swift in Sources */, - C8738B712BE4F8B700609E7F /* XPCController.swift in Sources */, - C861E6202994F63A0056CB02 /* ServiceDelegate.swift in Sources */, - C861E6112994F6070056CB02 /* AppDelegate.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C8738B5F2BE4D4B900609E7F /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - C8738B6B2BE4D56F00609E7F /* ServiceDelegate.swift in Sources */, - C8738B662BE4D4B900609E7F /* main.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - C8738B742BE5363800609E7F /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - C8738B7D2BE5363800609E7F /* ContentView.swift in Sources */, - C8738B7B2BE5363800609E7F /* SandboxedClientTesterApp.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - C81291B02994F92700196E12 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = C861E60D2994F6070056CB02 /* ExtensionService */; - targetProxy = C81291AF2994F92700196E12 /* PBXContainerItemProxy */; - }; - C814589A2939EFDC00135263 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = C814588B2939EFDC00135263 /* EditorExtension */; - targetProxy = C81458992939EFDC00135263 /* PBXContainerItemProxy */; - }; - C8216B7F2980377E00AD38C7 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = C8216B6F298036EC00AD38C7 /* Helper */; - targetProxy = C8216B7E2980377E00AD38C7 /* PBXContainerItemProxy */; - }; - C8738B8D2BE540F900609E7F /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = C8738B622BE4D4B900609E7F /* CommunicationBridge */; - targetProxy = C8738B8C2BE540F900609E7F /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - C814589D2939EFDC00135263 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_ENTITLEMENTS = EditorExtension/EditorExtension.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = "$(APP_BUILD)"; - DEVELOPMENT_TEAM = VEKTX9H2N7; - ENABLE_HARDENED_RUNTIME = YES; - INFOPLIST_FILE = EditorExtension/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = "$(EXTESNION_BUNDLE_NAME)"; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@executable_path/../../../../Frameworks", - ); - MACOSX_DEPLOYMENT_TARGET = 12.0; - MARKETING_VERSION = "$(APP_VERSION)"; - PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_IDENTIFIER_BASE).EditorExtension"; - PRODUCT_NAME = Copilot; - SKIP_INSTALL = YES; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - C814589E2939EFDC00135263 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_ENTITLEMENTS = EditorExtension/EditorExtension.entitlements; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application"; - CODE_SIGN_STYLE = Manual; - COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = "$(APP_BUILD)"; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=macosx*]" = VEKTX9H2N7; - ENABLE_HARDENED_RUNTIME = YES; - INFOPLIST_FILE = EditorExtension/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = "$(EXTESNION_BUNDLE_NAME)"; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@executable_path/../../../../Frameworks", - ); - MACOSX_DEPLOYMENT_TARGET = 12.0; - MARKETING_VERSION = "$(APP_VERSION)"; - PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_IDENTIFIER_BASE).EditorExtension"; - PRODUCT_NAME = Copilot; - PROVISIONING_PROFILE_SPECIFIER = ""; - SKIP_INSTALL = YES; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - C8189B232938973000C9DCDA /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = C81458AE293A009800135263 /* Config.debug.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 12.0; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - C8189B242938973000C9DCDA /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = C81458AD293A009600135263 /* Config.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 12.0; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Release; - }; - C8189B262938973000C9DCDA /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = "Copilot for Xcode/Copilot_for_Xcode.entitlements"; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = "$(APP_BUILD)"; - DEVELOPMENT_ASSET_PATHS = "\"Copilot for Xcode/Preview Content\""; - DEVELOPMENT_TEAM = VEKTX9H2N7; - ENABLE_HARDENED_RUNTIME = YES; - ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = "Copilot-for-Xcode-Info.plist"; - INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools"; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - MACOSX_DEPLOYMENT_TARGET = 12.0; - MARKETING_VERSION = "$(APP_VERSION)"; - PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_IDENTIFIER_BASE)"; - PRODUCT_MODULE_NAME = Copilot_for_Xcode; - PRODUCT_NAME = "$(HOST_APP_NAME)"; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - C8189B272938973000C9DCDA /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = "Copilot for Xcode/Copilot_for_Xcode.entitlements"; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application"; - CODE_SIGN_STYLE = Manual; - COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = "$(APP_BUILD)"; - DEVELOPMENT_ASSET_PATHS = "\"Copilot for Xcode/Preview Content\""; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=macosx*]" = VEKTX9H2N7; - ENABLE_HARDENED_RUNTIME = YES; - ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = "Copilot-for-Xcode-Info.plist"; - INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools"; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - MACOSX_DEPLOYMENT_TARGET = 12.0; - MARKETING_VERSION = "$(APP_VERSION)"; - PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_IDENTIFIER_BASE)"; - PRODUCT_NAME = "$(HOST_APP_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - C8216B75298036EC00AD38C7 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = VEKTX9H2N7; - ENABLE_HARDENED_RUNTIME = YES; - MACOSX_DEPLOYMENT_TARGET = 12.0; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - C8216B76298036EC00AD38C7 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application"; - CODE_SIGN_STYLE = Manual; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=macosx*]" = VEKTX9H2N7; - ENABLE_HARDENED_RUNTIME = YES; - MACOSX_DEPLOYMENT_TARGET = 12.0; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - C861E61B2994F6080056CB02 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = ExtensionService/ExtensionService.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = "$(APP_BUILD)"; - DEVELOPMENT_TEAM = VEKTX9H2N7; - ENABLE_HARDENED_RUNTIME = YES; - ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = ExtensionService/Info.plist; - INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools"; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INFOPLIST_KEY_NSMainStoryboardFile = Main; - INFOPLIST_KEY_NSPrincipalClass = NSApplication; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - MACOSX_DEPLOYMENT_TARGET = 12.0; - MARKETING_VERSION = "$(APP_VERSION)"; - PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_IDENTIFIER_BASE).ExtensionService"; - PRODUCT_NAME = "$(EXTENSION_SERVICE_NAME)"; - SKIP_INSTALL = YES; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - C861E61C2994F6080056CB02 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = ExtensionService/ExtensionService.entitlements; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application"; - CODE_SIGN_STYLE = Manual; - COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = "$(APP_BUILD)"; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=macosx*]" = VEKTX9H2N7; - ENABLE_HARDENED_RUNTIME = YES; - ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = ExtensionService/Info.plist; - INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools"; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INFOPLIST_KEY_NSMainStoryboardFile = Main; - INFOPLIST_KEY_NSPrincipalClass = NSApplication; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - MACOSX_DEPLOYMENT_TARGET = 12.0; - MARKETING_VERSION = "$(APP_VERSION)"; - PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_IDENTIFIER_BASE).ExtensionService"; - PRODUCT_NAME = "$(EXTENSION_SERVICE_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SKIP_INSTALL = YES; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - C8738B682BE4D4B900609E7F /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = VEKTX9H2N7; - ENABLE_HARDENED_RUNTIME = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MACOSX_DEPLOYMENT_TARGET = 12.0; - PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_IDENTIFIER_BASE).CommunicationBridge"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - C8738B692BE4D4B900609E7F /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application"; - CODE_SIGN_STYLE = Manual; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=macosx*]" = VEKTX9H2N7; - ENABLE_HARDENED_RUNTIME = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MACOSX_DEPLOYMENT_TARGET = 12.0; - PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_IDENTIFIER_BASE).CommunicationBridge"; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - C8738B852BE5363900609E7F /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = SandboxedClientTester/SandboxedClientTester.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_ASSET_PATHS = "\"SandboxedClientTester/Preview Content\""; - DEVELOPMENT_TEAM = VEKTX9H2N7; - ENABLE_HARDENED_RUNTIME = YES; - ENABLE_PREVIEWS = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = SandboxedClientTester/Info.plist; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MACOSX_DEPLOYMENT_TARGET = 14.2; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.github.CopilotForXcode.SandboxedClientTester; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - C8738B862BE5363900609E7F /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = SandboxedClientTester/SandboxedClientTester.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_ASSET_PATHS = "\"SandboxedClientTester/Preview Content\""; - DEVELOPMENT_TEAM = VEKTX9H2N7; - ENABLE_HARDENED_RUNTIME = YES; - ENABLE_PREVIEWS = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = SandboxedClientTester/Info.plist; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MACOSX_DEPLOYMENT_TARGET = 14.2; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.github.CopilotForXcode.SandboxedClientTester; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - C814589C2939EFDC00135263 /* Build configuration list for PBXNativeTarget "EditorExtension" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - C814589D2939EFDC00135263 /* Debug */, - C814589E2939EFDC00135263 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - C8189B112938972F00C9DCDA /* Build configuration list for PBXProject "Copilot for Xcode" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - C8189B232938973000C9DCDA /* Debug */, - C8189B242938973000C9DCDA /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - C8189B252938973000C9DCDA /* Build configuration list for PBXNativeTarget "Copilot for Xcode" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - C8189B262938973000C9DCDA /* Debug */, - C8189B272938973000C9DCDA /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - C8216B74298036EC00AD38C7 /* Build configuration list for PBXNativeTarget "Helper" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - C8216B75298036EC00AD38C7 /* Debug */, - C8216B76298036EC00AD38C7 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - C861E61A2994F6080056CB02 /* Build configuration list for PBXNativeTarget "ExtensionService" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - C861E61B2994F6080056CB02 /* Debug */, - C861E61C2994F6080056CB02 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - C8738B672BE4D4B900609E7F /* Build configuration list for PBXNativeTarget "CommunicationBridge" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - C8738B682BE4D4B900609E7F /* Debug */, - C8738B692BE4D4B900609E7F /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - C8738B842BE5363900609E7F /* Build configuration list for PBXNativeTarget "SandboxedClientTester" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - C8738B852BE5363900609E7F /* Debug */, - C8738B862BE5363900609E7F /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - -/* Begin XCRemoteSwiftPackageReference section */ - C8216B792980373800AD38C7 /* XCRemoteSwiftPackageReference "swift-argument-parser" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/apple/swift-argument-parser.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 1.0.0; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - -/* Begin XCSwiftPackageProductDependency section */ - C8216B7C2980374300AD38C7 /* ArgumentParser */ = { - isa = XCSwiftPackageProductDependency; - package = C8216B792980373800AD38C7 /* XCRemoteSwiftPackageReference "swift-argument-parser" */; - productName = ArgumentParser; - }; - C861E61D2994F6150056CB02 /* Service */ = { - isa = XCSwiftPackageProductDependency; - productName = Service; - }; - C86612F72A06AF74009197D9 /* HostApp */ = { - isa = XCSwiftPackageProductDependency; - productName = HostApp; - }; - C8738B6E2BE4F7A600609E7F /* XPCShared */ = { - isa = XCSwiftPackageProductDependency; - productName = XPCShared; - }; - C8738B872BE5365000609E7F /* Client */ = { - isa = XCSwiftPackageProductDependency; - productName = Client; - }; - C882175B294187EF00A22FD3 /* Client */ = { - isa = XCSwiftPackageProductDependency; - productName = Client; - }; -/* End XCSwiftPackageProductDependency section */ - }; - rootObject = C8189B0E2938972F00C9DCDA /* Project object */; -} diff --git a/Copilot for Xcode.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Copilot for Xcode.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a6..00000000 --- a/Copilot for Xcode.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/Copilot for Xcode.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Copilot for Xcode.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d98100..00000000 --- a/Copilot for Xcode.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/Copilot for Xcode.xcodeproj/xcshareddata/xcschemes/CommunicationBridge.xcscheme b/Copilot for Xcode.xcodeproj/xcshareddata/xcschemes/CommunicationBridge.xcscheme deleted file mode 100644 index 578b11ea..00000000 --- a/Copilot for Xcode.xcodeproj/xcshareddata/xcschemes/CommunicationBridge.xcscheme +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Copilot for Xcode.xcodeproj/xcshareddata/xcschemes/Copilot for Xcode.xcscheme b/Copilot for Xcode.xcodeproj/xcshareddata/xcschemes/Copilot for Xcode.xcscheme deleted file mode 100644 index e26142f3..00000000 --- a/Copilot for Xcode.xcodeproj/xcshareddata/xcschemes/Copilot for Xcode.xcscheme +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Copilot for Xcode.xcodeproj/xcshareddata/xcschemes/EditorExtension.xcscheme b/Copilot for Xcode.xcodeproj/xcshareddata/xcschemes/EditorExtension.xcscheme deleted file mode 100644 index 4844b100..00000000 --- a/Copilot for Xcode.xcodeproj/xcshareddata/xcschemes/EditorExtension.xcscheme +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Copilot for Xcode.xcodeproj/xcshareddata/xcschemes/ExtensionService.xcscheme b/Copilot for Xcode.xcodeproj/xcshareddata/xcschemes/ExtensionService.xcscheme deleted file mode 100644 index c0e9b79f..00000000 --- a/Copilot for Xcode.xcodeproj/xcshareddata/xcschemes/ExtensionService.xcscheme +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Copilot for Xcode.xcodeproj/xcshareddata/xcschemes/SandboxedClientTester.xcscheme b/Copilot for Xcode.xcodeproj/xcshareddata/xcschemes/SandboxedClientTester.xcscheme deleted file mode 100644 index 41fadd0b..00000000 --- a/Copilot for Xcode.xcodeproj/xcshareddata/xcschemes/SandboxedClientTester.xcscheme +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Copilot for Xcode.xcworkspace/contents.xcworkspacedata b/Copilot for Xcode.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 7fb7f274..00000000 --- a/Copilot for Xcode.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/Copilot for Xcode.xcworkspace/xcshareddata/IDETemplateMacros.plist b/Copilot for Xcode.xcworkspace/xcshareddata/IDETemplateMacros.plist deleted file mode 100644 index ea58bd0f..00000000 --- a/Copilot for Xcode.xcworkspace/xcshareddata/IDETemplateMacros.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - FILEHEADER - TODO: Remove this comment - - diff --git a/Copilot for Xcode.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Copilot for Xcode.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d98100..00000000 --- a/Copilot for Xcode.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/Copilot for Xcode.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Copilot for Xcode.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index 3db257ec..00000000 --- a/Copilot for Xcode.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,320 +0,0 @@ -{ - "pins" : [ - { - "identity" : "cgeventoverride", - "kind" : "remoteSourceControl", - "location" : "https://github.com/devm33/CGEventOverride", - "state" : { - "branch" : "devm33/fix-stale-AXIsProcessTrusted", - "revision" : "06a9bf1f8f8d47cca221344101cc0274f04cc513" - } - }, - { - "identity" : "codablewrappers", - "kind" : "remoteSourceControl", - "location" : "https://github.com/GottaGetSwifty/CodableWrappers", - "state" : { - "revision" : "4eb46a4c656333e8514db8aad204445741de7d40", - "version" : "2.0.7" - } - }, - { - "identity" : "combine-schedulers", - "kind" : "remoteSourceControl", - "location" : "https://github.com/pointfreeco/combine-schedulers", - "state" : { - "revision" : "9dc9cbe4bc45c65164fa653a563d8d8db61b09bb", - "version" : "1.0.0" - } - }, - { - "identity" : "copilotforxcodekit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/devm33/CopilotForXcodeKit", - "state" : { - "branch" : "main", - "revision" : "1f98fe9795766d3e37b5ae3d2e5f69f9b0af308b" - } - }, - { - "identity" : "fseventswrapper", - "kind" : "remoteSourceControl", - "location" : "https://github.com/Frizlab/FSEventsWrapper", - "state" : { - "revision" : "70bbea4b108221fcabfce8dbced8502831c0ae04", - "version" : "2.1.0" - } - }, - { - "identity" : "highlightr", - "kind" : "remoteSourceControl", - "location" : "https://github.com/devm33/Highlightr", - "state" : { - "branch" : "master", - "revision" : "81d8c8b3733939bf5d9e52cd6318f944cc033bd2" - } - }, - { - "identity" : "jsonrpc", - "kind" : "remoteSourceControl", - "location" : "https://github.com/ChimeHQ/JSONRPC", - "state" : { - "revision" : "c6ec759d41a76ac88fe7327c41a77d9033943374", - "version" : "0.9.0" - } - }, - { - "identity" : "keyboardshortcuts", - "kind" : "remoteSourceControl", - "location" : "https://github.com/devm33/KeyboardShortcuts", - "state" : { - "branch" : "main", - "revision" : "65fb410b0c6d3ed96623b460bab31ffce5f48b4d" - } - }, - { - "identity" : "languageclient", - "kind" : "remoteSourceControl", - "location" : "https://github.com/ChimeHQ/LanguageClient", - "state" : { - "revision" : "4f28cc3cad7512470275f65ca2048359553a86f5", - "version" : "0.8.2" - } - }, - { - "identity" : "languageserverprotocol", - "kind" : "remoteSourceControl", - "location" : "https://github.com/ChimeHQ/LanguageServerProtocol", - "state" : { - "revision" : "d51412945ae88ffcab65ec339ca89aed9c9f0b8a", - "version" : "0.13.3" - } - }, - { - "identity" : "networkimage", - "kind" : "remoteSourceControl", - "location" : "https://github.com/gonzalezreal/NetworkImage", - "state" : { - "revision" : "7aff8d1b31148d32c5933d75557d42f6323ee3d1", - "version" : "6.0.0" - } - }, - { - "identity" : "processenv", - "kind" : "remoteSourceControl", - "location" : "https://github.com/ChimeHQ/ProcessEnv", - "state" : { - "revision" : "552f611479a4f28243a1ef2a7376a216d6899f42", - "version" : "1.0.1" - } - }, - { - "identity" : "queue", - "kind" : "remoteSourceControl", - "location" : "https://github.com/mattmassicotte/Queue", - "state" : { - "revision" : "9f941ae35f146ccadd2689b9ab8d5aebb1f5d584", - "version" : "0.2.1" - } - }, - { - "identity" : "semaphore", - "kind" : "remoteSourceControl", - "location" : "https://github.com/groue/Semaphore", - "state" : { - "revision" : "2543679282aa6f6c8ecf2138acd613ed20790bc2", - "version" : "0.1.0" - } - }, - { - "identity" : "sparkle", - "kind" : "remoteSourceControl", - "location" : "https://github.com/sparkle-project/Sparkle", - "state" : { - "revision" : "0ef1ee0220239b3776f433314515fd849025673f", - "version" : "2.6.4" - } - }, - { - "identity" : "sqlite.swift", - "kind" : "remoteSourceControl", - "location" : "https://github.com/stephencelis/SQLite.swift.git", - "state" : { - "revision" : "a95fc6df17d108bd99210db5e8a9bac90fe984b8", - "version" : "0.15.3" - } - }, - { - "identity" : "swift-argument-parser", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-argument-parser.git", - "state" : { - "revision" : "fee6933f37fde9a5e12a1e4aeaa93fe60116ff2a", - "version" : "1.2.2" - } - }, - { - "identity" : "swift-async-algorithms", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-async-algorithms", - "state" : { - "revision" : "da4e36f86544cdf733a40d59b3a2267e3a7bbf36", - "version" : "1.0.0" - } - }, - { - "identity" : "swift-case-paths", - "kind" : "remoteSourceControl", - "location" : "https://github.com/pointfreeco/swift-case-paths", - "state" : { - "revision" : "8d712376c99fc0267aa0e41fea732babe365270a", - "version" : "1.3.3" - } - }, - { - "identity" : "swift-clocks", - "kind" : "remoteSourceControl", - "location" : "https://github.com/pointfreeco/swift-clocks", - "state" : { - "revision" : "a8421d68068d8f45fbceb418fbf22c5dad4afd33", - "version" : "1.0.2" - } - }, - { - "identity" : "swift-collections", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-collections", - "state" : { - "revision" : "94cf62b3ba8d4bed62680a282d4c25f9c63c2efb", - "version" : "1.1.0" - } - }, - { - "identity" : "swift-composable-architecture", - "kind" : "remoteSourceControl", - "location" : "https://github.com/pointfreeco/swift-composable-architecture", - "state" : { - "revision" : "433a23118f739078644ebeb4009e23d307af694a", - "version" : "1.10.4" - } - }, - { - "identity" : "swift-concurrency-extras", - "kind" : "remoteSourceControl", - "location" : "https://github.com/pointfreeco/swift-concurrency-extras", - "state" : { - "revision" : "bb5059bde9022d69ac516803f4f227d8ac967f71", - "version" : "1.1.0" - } - }, - { - "identity" : "swift-custom-dump", - "kind" : "remoteSourceControl", - "location" : "https://github.com/pointfreeco/swift-custom-dump", - "state" : { - "revision" : "f01efb26f3a192a0e88dcdb7c3c391ec2fc25d9c", - "version" : "1.3.0" - } - }, - { - "identity" : "swift-dependencies", - "kind" : "remoteSourceControl", - "location" : "https://github.com/pointfreeco/swift-dependencies", - "state" : { - "revision" : "350e1e119babe8525f9bd155b76640a5de270184", - "version" : "1.3.0" - } - }, - { - "identity" : "swift-glob", - "kind" : "remoteSourceControl", - "location" : "https://github.com/davbeck/swift-glob", - "state" : { - "revision" : "07ba6f47d903a0b1b59f12ca70d6de9949b975d6", - "version" : "0.2.0" - } - }, - { - "identity" : "swift-identified-collections", - "kind" : "remoteSourceControl", - "location" : "https://github.com/pointfreeco/swift-identified-collections", - "state" : { - "revision" : "d533cd18b0b456b106694a9899f917ee595f2666", - "version" : "1.0.2" - } - }, - { - "identity" : "swift-markdown-ui", - "kind" : "remoteSourceControl", - "location" : "https://github.com/gonzalezreal/swift-markdown-ui", - "state" : { - "revision" : "55441810c0f678c78ed7e2ebd46dde89228e02fc", - "version" : "2.4.0" - } - }, - { - "identity" : "swift-parsing", - "kind" : "remoteSourceControl", - "location" : "https://github.com/pointfreeco/swift-parsing", - "state" : { - "revision" : "a0e7d73f462c1c38c59dc40a3969ac40cea42950", - "version" : "0.13.0" - } - }, - { - "identity" : "swift-perception", - "kind" : "remoteSourceControl", - "location" : "https://github.com/pointfreeco/swift-perception", - "state" : { - "revision" : "64f7f6c28c6a4d3c4b9da2ba02383e29ab48a8cf", - "version" : "1.2.2" - } - }, - { - "identity" : "swift-syntax", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-syntax", - "state" : { - "revision" : "2bc86522d115234d1f588efe2bcb4ce4be8f8b82", - "version" : "510.0.3" - } - }, - { - "identity" : "swiftsoup", - "kind" : "remoteSourceControl", - "location" : "https://github.com/scinfu/SwiftSoup.git", - "state" : { - "revision" : "dee225a3da7b68d34936abc4dc8f34f2264db647", - "version" : "2.9.6" - } - }, - { - "identity" : "swiftui-flow-layout", - "kind" : "remoteSourceControl", - "location" : "https://github.com/globulus/swiftui-flow-layout", - "state" : { - "revision" : "de7da3440c3b87ba94adfa98c698828d7746a76d", - "version" : "1.0.5" - } - }, - { - "identity" : "swiftui-navigation", - "kind" : "remoteSourceControl", - "location" : "https://github.com/pointfreeco/swiftui-navigation", - "state" : { - "revision" : "7ab04c6e2e6a73d34d5a762970ef88bf0aedb084", - "version" : "1.4.0" - } - }, - { - "identity" : "xctest-dynamic-overlay", - "kind" : "remoteSourceControl", - "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay", - "state" : { - "revision" : "6f30bdba373bbd7fbfe241dddd732651f2fbd1e2", - "version" : "1.1.2" - } - } - ], - "version" : 2 -} diff --git a/Copilot for Xcode/App.swift b/Copilot for Xcode/App.swift deleted file mode 100644 index d8ed3cdd..00000000 --- a/Copilot for Xcode/App.swift +++ /dev/null @@ -1,223 +0,0 @@ -import SwiftUI -import Client -import HostApp -import LaunchAgentManager -import SharedUIComponents -import UpdateChecker -import XPCShared -import HostAppActivator -import ComposableArchitecture - -struct VisualEffect: NSViewRepresentable { - func makeNSView(context: Self.Context) -> NSView { return NSVisualEffectView() } - func updateNSView(_ nsView: NSView, context: Context) { } -} - -class AppDelegate: NSObject, NSApplicationDelegate { - private var permissionAlertShown = false - - // Launch modes supported by the app - enum LaunchMode { - case chat - case settings - case mcp - } - - func applicationDidFinishLaunching(_ notification: Notification) { - if #available(macOS 13.0, *) { - checkBackgroundPermissions() - } - - let launchMode = determineLaunchMode() - handleLaunchMode(launchMode) - } - - func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool { - if #available(macOS 13.0, *) { - checkBackgroundPermissions() - } - - let launchMode = determineLaunchMode() - handleLaunchMode(launchMode) - return true - } - - // MARK: - Helper Methods - - private func determineLaunchMode() -> LaunchMode { - let launchArgs = CommandLine.arguments - if launchArgs.contains("--settings") { - return .settings - } else if launchArgs.contains("--mcp") { - return .mcp - } else { - return .chat - } - } - - private func handleLaunchMode(_ mode: LaunchMode) { - switch mode { - case .settings: - openSettings() - case .mcp: - openMCPSettings() - case .chat: - openChat() - } - } - - private func openSettings() { - DispatchQueue.main.async { - activateAndOpenSettings() - } - } - - private func openChat() { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - Task { - let service = try? getService() - try? await service?.openChat() - } - } - } - - private func openMCPSettings() { - DispatchQueue.main.async { - activateAndOpenSettings() - hostAppStore.send(.setActiveTab(2)) - } - } - - @available(macOS 13.0, *) - private func checkBackgroundPermissions() { - Task { - // Direct check of permission status - let launchAgentManager = LaunchAgentManager() - let isPermissionGranted = await launchAgentManager.isBackgroundPermissionGranted() - - if !isPermissionGranted { - // Only show alert if permission isn't granted - DispatchQueue.main.async { - if !self.permissionAlertShown { - showBackgroundPermissionAlert() - self.permissionAlertShown = true - } - } - } else { - // Permission is granted, reset flag - self.permissionAlertShown = false - } - } - } - - // MARK: - Application Termination - - func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply { - // Immediately terminate extension service if it's running - if let extensionService = NSWorkspace.shared.runningApplications.first(where: { - $0.bundleIdentifier == "\(Bundle.main.bundleIdentifier!).ExtensionService" - }) { - extensionService.terminate() - } - - // Start cleanup in background without waiting - Task { - let quitTask = Task { - let service = try? getService() - try? await service?.quitService() - } - - // Wait just a tiny bit to allow cleanup to start - try? await Task.sleep(nanoseconds: 100_000_000) // 100ms - - DispatchQueue.main.async { - NSApp.reply(toApplicationShouldTerminate: true) - } - } - - return .terminateLater - } - - func applicationWillTerminate(_ notification: Notification) { - if let extensionService = NSWorkspace.shared.runningApplications.first(where: { - $0.bundleIdentifier == "\(Bundle.main.bundleIdentifier!).ExtensionService" - }) { - extensionService.terminate() - } - } -} - -class AppUpdateCheckerDelegate: UpdateCheckerDelegate { - func prepareForRelaunch(finish: @escaping () -> Void) { - Task { - let service = try? getService() - try? await service?.quitService() - finish() - } - } -} - -@main -struct CopilotForXcodeApp: App { - @NSApplicationDelegateAdaptor private var appDelegate: AppDelegate - - init() { - UserDefaults.setupDefaultSettings() - - Task { - await hostAppStore - .send(.general(.setupLaunchAgentIfNeeded)) - .finish() - } - - DistributedNotificationCenter.default().addObserver( - forName: .openSettingsWindowRequest, - object: nil, - queue: .main - ) { _ in - DispatchQueue.main.async { - activateAndOpenSettings() - } - } - - DistributedNotificationCenter.default().addObserver( - forName: .openMCPSettingsWindowRequest, - object: nil, - queue: .main - ) { _ in - DispatchQueue.main.async { - activateAndOpenSettings() - hostAppStore.send(.setActiveTab(2)) - } - } - } - - var body: some Scene { - WithPerceptionTracking { - Settings { - TabContainer() - .frame(minWidth: 800, minHeight: 600) - .background(VisualEffect().ignoresSafeArea()) - .environment(\.updateChecker, UpdateChecker( - hostBundle: Bundle.main, - checkerDelegate: AppUpdateCheckerDelegate() - )) - } - } - } -} - -@MainActor -func activateAndOpenSettings() { - NSApp.activate(ignoringOtherApps: true) - if #available(macOS 14.0, *) { - let environment = SettingsEnvironment() - environment.open() - } else if #available(macOS 13.0, *) { - NSApp.sendAction(Selector(("showSettingsWindow:")), to: nil, from: nil) - } else { - NSApp.sendAction(Selector(("showPreferencesWindow:")), to: nil, from: nil) - } -} - -var isPreview: Bool { ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" } diff --git a/Copilot for Xcode/Assets.xcassets/AccentColor.colorset/Contents.json b/Copilot for Xcode/Assets.xcassets/AccentColor.colorset/Contents.json deleted file mode 100644 index eb878970..00000000 --- a/Copilot for Xcode/Assets.xcassets/AccentColor.colorset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "colors" : [ - { - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/Contents.json b/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 0a30d46d..00000000 --- a/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "images" : [ - { - "filename" : "CopilotforXcode-Icon@16w_1x.png", - "idiom" : "mac", - "scale" : "1x", - "size" : "16x16" - }, - { - "filename" : "CopilotforXcode-Icon@16w_2x.png", - "idiom" : "mac", - "scale" : "2x", - "size" : "16x16" - }, - { - "filename" : "CopilotforXcode-Icon@32w_1x.png", - "idiom" : "mac", - "scale" : "1x", - "size" : "32x32" - }, - { - "filename" : "CopilotforXcode-Icon@32w_2x.png", - "idiom" : "mac", - "scale" : "2x", - "size" : "32x32" - }, - { - "filename" : "CopilotforXcode-Icon@128w_1x.png", - "idiom" : "mac", - "scale" : "1x", - "size" : "128x128" - }, - { - "filename" : "CopilotforXcode-Icon@128w_2x.png", - "idiom" : "mac", - "scale" : "2x", - "size" : "128x128" - }, - { - "filename" : "CopilotforXcode-Icon@256w_1x.png", - "idiom" : "mac", - "scale" : "1x", - "size" : "256x256" - }, - { - "filename" : "CopilotforXcode-Icon@256w_2x.png", - "idiom" : "mac", - "scale" : "2x", - "size" : "256x256" - }, - { - "filename" : "CopilotforXcode-Icon@512w_1x.png", - "idiom" : "mac", - "scale" : "1x", - "size" : "512x512" - }, - { - "filename" : "CopilotforXcode-Icon@512w_2x.png", - "idiom" : "mac", - "scale" : "2x", - "size" : "512x512" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@128w_1x.png b/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@128w_1x.png deleted file mode 100644 index 3ee52427..00000000 Binary files a/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@128w_1x.png and /dev/null differ diff --git a/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@128w_2x.png b/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@128w_2x.png deleted file mode 100644 index 88b20d1d..00000000 Binary files a/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@128w_2x.png and /dev/null differ diff --git a/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@16w_1x.png b/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@16w_1x.png deleted file mode 100644 index 2bb554dc..00000000 Binary files a/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@16w_1x.png and /dev/null differ diff --git a/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@16w_2x.png b/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@16w_2x.png deleted file mode 100644 index ce02bac7..00000000 Binary files a/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@16w_2x.png and /dev/null differ diff --git a/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@256w_1x.png b/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@256w_1x.png deleted file mode 100644 index 7674f663..00000000 Binary files a/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@256w_1x.png and /dev/null differ diff --git a/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@256w_2x.png b/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@256w_2x.png deleted file mode 100644 index fc705969..00000000 Binary files a/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@256w_2x.png and /dev/null differ diff --git a/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@32w_1x.png b/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@32w_1x.png deleted file mode 100644 index ce02bac7..00000000 Binary files a/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@32w_1x.png and /dev/null differ diff --git a/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@32w_2x.png b/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@32w_2x.png deleted file mode 100644 index 4d52c81b..00000000 Binary files a/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@32w_2x.png and /dev/null differ diff --git a/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@512w_1x.png b/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@512w_1x.png deleted file mode 100644 index fc705969..00000000 Binary files a/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@512w_1x.png and /dev/null differ diff --git a/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@512w_2x.png b/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@512w_2x.png deleted file mode 100644 index 54da6e3f..00000000 Binary files a/Copilot for Xcode/Assets.xcassets/AppIcon.appiconset/CopilotforXcode-Icon@512w_2x.png and /dev/null differ diff --git a/Copilot for Xcode/Assets.xcassets/BackgroundColor.colorset/Contents.json b/Copilot for Xcode/Assets.xcassets/BackgroundColor.colorset/Contents.json deleted file mode 100644 index 37eb3c31..00000000 --- a/Copilot for Xcode/Assets.xcassets/BackgroundColor.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "40", - "green" : "23", - "red" : "25" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Copilot for Xcode/Assets.xcassets/BackgroundColorTop.colorset/Contents.json b/Copilot for Xcode/Assets.xcassets/BackgroundColorTop.colorset/Contents.json deleted file mode 100644 index 279761e1..00000000 --- a/Copilot for Xcode/Assets.xcassets/BackgroundColorTop.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "54", - "green" : "25", - "red" : "30" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Copilot for Xcode/Assets.xcassets/ButtonBackgroundColorDefault.colorset/Contents.json b/Copilot for Xcode/Assets.xcassets/ButtonBackgroundColorDefault.colorset/Contents.json deleted file mode 100644 index 3ece9084..00000000 --- a/Copilot for Xcode/Assets.xcassets/ButtonBackgroundColorDefault.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0x46", - "green" : "0x24", - "red" : "0x2C" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Copilot for Xcode/Assets.xcassets/ButtonBackgroundColorPressed.colorset/Contents.json b/Copilot for Xcode/Assets.xcassets/ButtonBackgroundColorPressed.colorset/Contents.json deleted file mode 100644 index 4754b656..00000000 --- a/Copilot for Xcode/Assets.xcassets/ButtonBackgroundColorPressed.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.275", - "green" : "0.141", - "red" : "0.290" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Copilot for Xcode/Assets.xcassets/ChatIcon.imageset/ChatIcon.svg b/Copilot for Xcode/Assets.xcassets/ChatIcon.imageset/ChatIcon.svg deleted file mode 100644 index 74239992..00000000 --- a/Copilot for Xcode/Assets.xcassets/ChatIcon.imageset/ChatIcon.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/Copilot for Xcode/Assets.xcassets/ChatIcon.imageset/Contents.json b/Copilot for Xcode/Assets.xcassets/ChatIcon.imageset/Contents.json deleted file mode 100644 index 329dae48..00000000 --- a/Copilot for Xcode/Assets.xcassets/ChatIcon.imageset/Contents.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "images" : [ - { - "filename" : "ChatIcon.svg", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "preserves-vector-representation" : true - } -} diff --git a/Copilot for Xcode/Assets.xcassets/Color.colorset/Contents.json b/Copilot for Xcode/Assets.xcassets/Color.colorset/Contents.json deleted file mode 100644 index 22c4bb0a..00000000 --- a/Copilot for Xcode/Assets.xcassets/Color.colorset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "1.000", - "green" : "1.000", - "red" : "1.000" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "1.000", - "green" : "1.000", - "red" : "1.000" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Copilot for Xcode/Assets.xcassets/Contents.json b/Copilot for Xcode/Assets.xcassets/Contents.json deleted file mode 100644 index 73c00596..00000000 --- a/Copilot for Xcode/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Copilot for Xcode/Assets.xcassets/CopilotError.imageset/Contents.json b/Copilot for Xcode/Assets.xcassets/CopilotError.imageset/Contents.json deleted file mode 100644 index 78e08e6e..00000000 --- a/Copilot for Xcode/Assets.xcassets/CopilotError.imageset/Contents.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "images" : [ - { - "filename" : "CopilotError.svg", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "preserves-vector-representation" : true - } -} diff --git a/Copilot for Xcode/Assets.xcassets/CopilotIssue.imageset/Contents.json b/Copilot for Xcode/Assets.xcassets/CopilotIssue.imageset/Contents.json deleted file mode 100644 index 9a465b02..00000000 --- a/Copilot for Xcode/Assets.xcassets/CopilotIssue.imageset/Contents.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "images" : [ - { - "filename" : "CopilotIssue.svg", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "preserves-vector-representation" : true - } -} diff --git a/Copilot for Xcode/Assets.xcassets/CopilotLogo.imageset/Contents.json b/Copilot for Xcode/Assets.xcassets/CopilotLogo.imageset/Contents.json deleted file mode 100644 index 2e35661e..00000000 --- a/Copilot for Xcode/Assets.xcassets/CopilotLogo.imageset/Contents.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "images" : [ - { - "filename" : "copilot.svg", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "preserves-vector-representation" : true - } -} diff --git a/Copilot for Xcode/Assets.xcassets/CopilotLogo.imageset/copilot.svg b/Copilot for Xcode/Assets.xcassets/CopilotLogo.imageset/copilot.svg deleted file mode 100644 index 8284dce7..00000000 --- a/Copilot for Xcode/Assets.xcassets/CopilotLogo.imageset/copilot.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/Copilot for Xcode/Assets.xcassets/DangerForegroundColor.colorset/Contents.json b/Copilot for Xcode/Assets.xcassets/DangerForegroundColor.colorset/Contents.json deleted file mode 100644 index db248f82..00000000 --- a/Copilot for Xcode/Assets.xcassets/DangerForegroundColor.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0x1C", - "green" : "0x0E", - "red" : "0xB1" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Copilot for Xcode/Assets.xcassets/DangerStrokeColor.colorset/Contents.json b/Copilot for Xcode/Assets.xcassets/DangerStrokeColor.colorset/Contents.json deleted file mode 100644 index 5fbecf46..00000000 --- a/Copilot for Xcode/Assets.xcassets/DangerStrokeColor.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0xB2", - "green" : "0xAC", - "red" : "0xEE" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Copilot for Xcode/Assets.xcassets/DescriptionForegroundColor.colorset/Contents.json b/Copilot for Xcode/Assets.xcassets/DescriptionForegroundColor.colorset/Contents.json deleted file mode 100644 index bdcbb88e..00000000 --- a/Copilot for Xcode/Assets.xcassets/DescriptionForegroundColor.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0x9D", - "green" : "0x9D", - "red" : "0x9D" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Copilot for Xcode/Assets.xcassets/GitHubMark.imageset/Contents.json b/Copilot for Xcode/Assets.xcassets/GitHubMark.imageset/Contents.json deleted file mode 100644 index 1f7fbbe0..00000000 --- a/Copilot for Xcode/Assets.xcassets/GitHubMark.imageset/Contents.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "images" : [ - { - "filename" : "Icon.svg", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties": { - "preserves-vector-representation": true - } -} diff --git a/Copilot for Xcode/Assets.xcassets/GitHubMark.imageset/Icon.svg b/Copilot for Xcode/Assets.xcassets/GitHubMark.imageset/Icon.svg deleted file mode 100644 index 15204160..00000000 --- a/Copilot for Xcode/Assets.xcassets/GitHubMark.imageset/Icon.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/Copilot for Xcode/Assets.xcassets/GroupBoxBackgroundColor.colorset/Contents.json b/Copilot for Xcode/Assets.xcassets/GroupBoxBackgroundColor.colorset/Contents.json deleted file mode 100644 index f7add95c..00000000 --- a/Copilot for Xcode/Assets.xcassets/GroupBoxBackgroundColor.colorset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.920", - "green" : "0.910", - "red" : "0.910" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.250", - "green" : "0.250", - "red" : "0.250" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Copilot for Xcode/Assets.xcassets/TextLinkForegroundColor.colorset/Contents.json b/Copilot for Xcode/Assets.xcassets/TextLinkForegroundColor.colorset/Contents.json deleted file mode 100644 index d892da13..00000000 --- a/Copilot for Xcode/Assets.xcassets/TextLinkForegroundColor.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0xFF", - "green" : "0x94", - "red" : "0x37" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Copilot for Xcode/Assets.xcassets/ToolTitleHighlightBgColor.colorset/Contents.json b/Copilot for Xcode/Assets.xcassets/ToolTitleHighlightBgColor.colorset/Contents.json deleted file mode 100644 index ce478f39..00000000 --- a/Copilot for Xcode/Assets.xcassets/ToolTitleHighlightBgColor.colorset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "1.000", - "green" : "1.000", - "red" : "1.000" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.250", - "green" : "0.250", - "red" : "0.250" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Copilot for Xcode/Assets.xcassets/WarningBackgroundColor.colorset/Contents.json b/Copilot for Xcode/Assets.xcassets/WarningBackgroundColor.colorset/Contents.json deleted file mode 100644 index b38fae81..00000000 --- a/Copilot for Xcode/Assets.xcassets/WarningBackgroundColor.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0xF5", - "green" : "0xF9", - "red" : "0xFF" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Copilot for Xcode/Copilot_for_Xcode.entitlements b/Copilot for Xcode/Copilot_for_Xcode.entitlements deleted file mode 100644 index f557c733..00000000 --- a/Copilot for Xcode/Copilot_for_Xcode.entitlements +++ /dev/null @@ -1,16 +0,0 @@ - - - - - com.apple.security.app-sandbox - - com.apple.security.application-groups - - $(TeamIdentifierPrefix)group.$(BUNDLE_IDENTIFIER_BASE) - - com.apple.security.automation.apple-events - - com.apple.security.files.user-selected.read-only - - - diff --git a/Copilot for Xcode/Credits.rtf b/Copilot for Xcode/Credits.rtf deleted file mode 100644 index 13a16781..00000000 --- a/Copilot for Xcode/Credits.rtf +++ /dev/null @@ -1,3352 +0,0 @@ -{\rtf1\ansi\ansicpg1252\cocoartf2761 -\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -{\*\expandedcolortbl;;} -\margl1440\margr1440\vieww27760\viewh14820\viewkind0 -\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 - -\f0\fs20 \cf0 Dependency: github.com/pointfreeco/swift-case-paths\ -Version: 1.3.3\ -License Content:\ -MIT License\ -\ -Copyright (c) 2020 Point-Free, Inc.\ -\ -Permission is hereby granted, free of charge, to any person obtaining a copy\ -of this software and associated documentation files (the "Software"), to deal\ -in the Software without restriction, including without limitation the rights\ -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ -copies of the Software, and to permit persons to whom the Software is\ -furnished to do so, subject to the following conditions:\ -\ -The above copyright notice and this permission notice shall be included in all\ -copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ -SOFTWARE.\ -\ -\ -Dependency: github.com/pointfreeco/swift-custom-dump\ -Version: 1.3.0\ -License Content:\ -MIT License\ -\ -Copyright (c) 2021 Point-Free, Inc.\ -\ -Permission is hereby granted, free of charge, to any person obtaining a copy\ -of this software and associated documentation files (the "Software"), to deal\ -in the Software without restriction, including without limitation the rights\ -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ -copies of the Software, and to permit persons to whom the Software is\ -furnished to do so, subject to the following conditions:\ -\ -The above copyright notice and this permission notice shall be included in all\ -copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ -SOFTWARE.\ -\ -\ -Dependency: github.com/pointfreeco/xctest-dynamic-overlay\ -Version: 1.1.2\ -License Content:\ -MIT License\ -\ -Copyright (c) 2021 Point-Free, Inc.\ -\ -Permission is hereby granted, free of charge, to any person obtaining a copy\ -of this software and associated documentation files (the "Software"), to deal\ -in the Software without restriction, including without limitation the rights\ -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ -copies of the Software, and to permit persons to whom the Software is\ -furnished to do so, subject to the following conditions:\ -\ -The above copyright notice and this permission notice shall be included in all\ -copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ -SOFTWARE.\ -\ -\ -Dependency: github.com/pointfreeco/combine-schedulers\ -Version: 1.0.0\ -License Content:\ -MIT License\ -\ -Copyright (c) 2020 Point-Free, Inc.\ -\ -Permission is hereby granted, free of charge, to any person obtaining a copy\ -of this software and associated documentation files (the "Software"), to deal\ -in the Software without restriction, including without limitation the rights\ -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ -copies of the Software, and to permit persons to whom the Software is\ -furnished to do so, subject to the following conditions:\ -\ -The above copyright notice and this permission notice shall be included in all\ -copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ -SOFTWARE.\ -\ -\ -Dependency: github.com/pointfreeco/swift-clocks\ -Version: 1.0.2\ -License Content:\ -MIT License\ -\ -Copyright (c) 2022 Point-Free\ -\ -Permission is hereby granted, free of charge, to any person obtaining a copy\ -of this software and associated documentation files (the "Software"), to deal\ -in the Software without restriction, including without limitation the rights\ -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ -copies of the Software, and to permit persons to whom the Software is\ -furnished to do so, subject to the following conditions:\ -\ -The above copyright notice and this permission notice shall be included in all\ -copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ -SOFTWARE.\ -\ -\ -\ -Dependency: github.com/pointfreeco/swift-concurrency-extras\ -Version: 1.1.0\ -License Content:\ -MIT License\ -\ -Copyright (c) 2023 Point-Free\ -\ -Permission is hereby granted, free of charge, to any person obtaining a copy\ -of this software and associated documentation files (the "Software"), to deal\ -in the Software without restriction, including without limitation the rights\ -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ -copies of the Software, and to permit persons to whom the Software is\ -furnished to do so, subject to the following conditions:\ -\ -The above copyright notice and this permission notice shall be included in all\ -copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ -SOFTWARE.\ -\ -\ -Dependency: github.com/apple/swift-syntax\ -Version: 510.0.3\ -License Content:\ - Apache License\ - Version 2.0, January 2004\ - http://www.apache.org/licenses/\ -\ - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\ -\ - 1. Definitions.\ -\ - "License" shall mean the terms and conditions for use, reproduction,\ - and distribution as defined by Sections 1 through 9 of this document.\ -\ - "Licensor" shall mean the copyright owner or entity authorized by\ - the copyright owner that is granting the License.\ -\ - "Legal Entity" shall mean the union of the acting entity and all\ - other entities that control, are controlled by, or are under common\ - control with that entity. For the purposes of this definition,\ - "control" means (i) the power, direct or indirect, to cause the\ - direction or management of such entity, whether by contract or\ - otherwise, or (ii) ownership of fifty percent (50%) or more of the\ - outstanding shares, or (iii) beneficial ownership of such entity.\ -\ - "You" (or "Your") shall mean an individual or Legal Entity\ - exercising permissions granted by this License.\ -\ - "Source" form shall mean the preferred form for making modifications,\ - including but not limited to software source code, documentation\ - source, and configuration files.\ -\ - "Object" form shall mean any form resulting from mechanical\ - transformation or translation of a Source form, including but\ - not limited to compiled object code, generated documentation,\ - and conversions to other media types.\ -\ - "Work" shall mean the work of authorship, whether in Source or\ - Object form, made available under the License, as indicated by a\ - copyright notice that is included in or attached to the work\ - (an example is provided in the Appendix below).\ -\ - "Derivative Works" shall mean any work, whether in Source or Object\ - form, that is based on (or derived from) the Work and for which the\ - editorial revisions, annotations, elaborations, or other modifications\ - represent, as a whole, an original work of authorship. For the purposes\ - of this License, Derivative Works shall not include works that remain\ - separable from, or merely link (or bind by name) to the interfaces of,\ - the Work and Derivative Works thereof.\ -\ - "Contribution" shall mean any work of authorship, including\ - the original version of the Work and any modifications or additions\ - to that Work or Derivative Works thereof, that is intentionally\ - submitted to Licensor for inclusion in the Work by the copyright owner\ - or by an individual or Legal Entity authorized to submit on behalf of\ - the copyright owner. For the purposes of this definition, "submitted"\ - means any form of electronic, verbal, or written communication sent\ - to the Licensor or its representatives, including but not limited to\ - communication on electronic mailing lists, source code control systems,\ - and issue tracking systems that are managed by, or on behalf of, the\ - Licensor for the purpose of discussing and improving the Work, but\ - excluding communication that is conspicuously marked or otherwise\ - designated in writing by the copyright owner as "Not a Contribution."\ -\ - "Contributor" shall mean Licensor and any individual or Legal Entity\ - on behalf of whom a Contribution has been received by Licensor and\ - subsequently incorporated within the Work.\ -\ - 2. Grant of Copyright License. Subject to the terms and conditions of\ - this License, each Contributor hereby grants to You a perpetual,\ - worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ - copyright license to reproduce, prepare Derivative Works of,\ - publicly display, publicly perform, sublicense, and distribute the\ - Work and such Derivative Works in Source or Object form.\ -\ - 3. Grant of Patent License. Subject to the terms and conditions of\ - this License, each Contributor hereby grants to You a perpetual,\ - worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ - (except as stated in this section) patent license to make, have made,\ - use, offer to sell, sell, import, and otherwise transfer the Work,\ - where such license applies only to those patent claims licensable\ - by such Contributor that are necessarily infringed by their\ - Contribution(s) alone or by combination of their Contribution(s)\ - with the Work to which such Contribution(s) was submitted. If You\ - institute patent litigation against any entity (including a\ - cross-claim or counterclaim in a lawsuit) alleging that the Work\ - or a Contribution incorporated within the Work constitutes direct\ - or contributory patent infringement, then any patent licenses\ - granted to You under this License for that Work shall terminate\ - as of the date such litigation is filed.\ -\ - 4. Redistribution. You may reproduce and distribute copies of the\ - Work or Derivative Works thereof in any medium, with or without\ - modifications, and in Source or Object form, provided that You\ - meet the following conditions:\ -\ - (a) You must give any other recipients of the Work or\ - Derivative Works a copy of this License; and\ -\ - (b) You must cause any modified files to carry prominent notices\ - stating that You changed the files; and\ -\ - (c) You must retain, in the Source form of any Derivative Works\ - that You distribute, all copyright, patent, trademark, and\ - attribution notices from the Source form of the Work,\ - excluding those notices that do not pertain to any part of\ - the Derivative Works; and\ -\ - (d) If the Work includes a "NOTICE" text file as part of its\ - distribution, then any Derivative Works that You distribute must\ - include a readable copy of the attribution notices contained\ - within such NOTICE file, excluding those notices that do not\ - pertain to any part of the Derivative Works, in at least one\ - of the following places: within a NOTICE text file distributed\ - as part of the Derivative Works; within the Source form or\ - documentation, if provided along with the Derivative Works; or,\ - within a display generated by the Derivative Works, if and\ - wherever such third-party notices normally appear. The contents\ - of the NOTICE file are for informational purposes only and\ - do not modify the License. You may add Your own attribution\ - notices within Derivative Works that You distribute, alongside\ - or as an addendum to the NOTICE text from the Work, provided\ - that such additional attribution notices cannot be construed\ - as modifying the License.\ -\ - You may add Your own copyright statement to Your modifications and\ - may provide additional or different license terms and conditions\ - for use, reproduction, or distribution of Your modifications, or\ - for any such Derivative Works as a whole, provided Your use,\ - reproduction, and distribution of the Work otherwise complies with\ - the conditions stated in this License.\ -\ - 5. Submission of Contributions. Unless You explicitly state otherwise,\ - any Contribution intentionally submitted for inclusion in the Work\ - by You to the Licensor shall be under the terms and conditions of\ - this License, without any additional terms or conditions.\ - Notwithstanding the above, nothing herein shall supersede or modify\ - the terms of any separate license agreement you may have executed\ - with Licensor regarding such Contributions.\ -\ - 6. Trademarks. This License does not grant permission to use the trade\ - names, trademarks, service marks, or product names of the Licensor,\ - except as required for reasonable and customary use in describing the\ - origin of the Work and reproducing the content of the NOTICE file.\ -\ - 7. Disclaimer of Warranty. Unless required by applicable law or\ - agreed to in writing, Licensor provides the Work (and each\ - Contributor provides its Contributions) on an "AS IS" BASIS,\ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\ - implied, including, without limitation, any warranties or conditions\ - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\ - PARTICULAR PURPOSE. You are solely responsible for determining the\ - appropriateness of using or redistributing the Work and assume any\ - risks associated with Your exercise of permissions under this License.\ -\ - 8. Limitation of Liability. In no event and under no legal theory,\ - whether in tort (including negligence), contract, or otherwise,\ - unless required by applicable law (such as deliberate and grossly\ - negligent acts) or agreed to in writing, shall any Contributor be\ - liable to You for damages, including any direct, indirect, special,\ - incidental, or consequential damages of any character arising as a\ - result of this License or out of the use or inability to use the\ - Work (including but not limited to damages for loss of goodwill,\ - work stoppage, computer failure or malfunction, or any and all\ - other commercial damages or losses), even if such Contributor\ - has been advised of the possibility of such damages.\ -\ - 9. Accepting Warranty or Additional Liability. While redistributing\ - the Work or Derivative Works thereof, You may choose to offer,\ - and charge a fee for, acceptance of support, warranty, indemnity,\ - or other liability obligations and/or rights consistent with this\ - License. However, in accepting such obligations, You may act only\ - on Your own behalf and on Your sole responsibility, not on behalf\ - of any other Contributor, and only if You agree to indemnify,\ - defend, and hold each Contributor harmless for any liability\ - incurred by, or claims asserted against, such Contributor by reason\ - of your accepting any such warranty or additional liability.\ -\ - END OF TERMS AND CONDITIONS\ -\ - APPENDIX: How to apply the Apache License to your work.\ -\ - To apply the Apache License to your work, attach the following\ - boilerplate notice, with the fields enclosed by brackets "[]"\ - replaced with your own identifying information. (Don't include\ - the brackets!) The text should be enclosed in the appropriate\ - comment syntax for the file format. We also recommend that a\ - file or class name and description of purpose be included on the\ - same "printed page" as the copyright notice for easier\ - identification within third-party archives.\ -\ - Copyright [yyyy] [name of copyright owner]\ -\ - Licensed under the Apache License, Version 2.0 (the "License");\ - you may not use this file except in compliance with the License.\ - You may obtain a copy of the License at\ -\ - http://www.apache.org/licenses/LICENSE-2.0\ -\ - Unless required by applicable law or agreed to in writing, software\ - distributed under the License is distributed on an "AS IS" BASIS,\ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\ - See the License for the specific language governing permissions and\ - limitations under the License.\ -\ -\ -\ -## Runtime Library Exception to the Apache 2.0 License: ##\ -\ -\ - As an exception, if you use this Software to compile your source code and\ - portions of this Software are embedded into the binary product as a result,\ - you may redistribute such product without providing attribution as would\ - otherwise be required by Sections 4(a), 4(b) and 4(d) of the License.\ -\ -\ -Dependency: github.com/pointfreeco/swift-tagged\ -Version: 0.10.0\ -License Content:\ -MIT License\ -\ -Copyright (c) 2019 Point-Free, Inc.\ -\ -Permission is hereby granted, free of charge, to any person obtaining a copy\ -of this software and associated documentation files (the "Software"), to deal\ -in the Software without restriction, including without limitation the rights\ -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ -copies of the Software, and to permit persons to whom the Software is\ -furnished to do so, subject to the following conditions:\ -\ -The above copyright notice and this permission notice shall be included in all\ -copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ -SOFTWARE.\ -\ -\ -Dependency: github.com/pointfreecoswift-snapshot-testing\ -Version: 1.15.4\ -License Content:\ -MIT License\ -\ -Copyright (c) 2019 Point-Free, Inc.\ -\ -Permission is hereby granted, free of charge, to any person obtaining a copy\ -of this software and associated documentation files (the "Software"), to deal\ -in the Software without restriction, including without limitation the rights\ -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ -copies of the Software, and to permit persons to whom the Software is\ -furnished to do so, subject to the following conditions:\ -\ -The above copyright notice and this permission notice shall be included in all\ -copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ -SOFTWARE.\ -\ -\ -Dependency: github.com/gonzalezreal/NetworkImage\ -Version: 6.0.0\ -License Content:\ -MIT License\ -\ -Copyright (c) 2020 Guille Gonzalez\ -\ -Permission is hereby granted, free of charge, to any person obtaining a copy\ -of this software and associated documentation files (the "Software"), to deal\ -in the Software without restriction, including without limitation the rights\ -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ -copies of the Software, and to permit persons to whom the Software is\ -furnished to do so, subject to the following conditions:\ -\ -The above copyright notice and this permission notice shall be included in all\ -copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ -SOFTWARE.\ -\ -\ -Dependency: github.com/apple/swift-collections-benchmark\ -Version: 0.0.3\ -License Content:\ - Apache License\ - Version 2.0, January 2004\ - http://www.apache.org/licenses/\ -\ - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\ -\ - 1. Definitions.\ -\ - "License" shall mean the terms and conditions for use, reproduction,\ - and distribution as defined by Sections 1 through 9 of this document.\ -\ - "Licensor" shall mean the copyright owner or entity authorized by\ - the copyright owner that is granting the License.\ -\ - "Legal Entity" shall mean the union of the acting entity and all\ - other entities that control, are controlled by, or are under common\ - control with that entity. For the purposes of this definition,\ - "control" means (i) the power, direct or indirect, to cause the\ - direction or management of such entity, whether by contract or\ - otherwise, or (ii) ownership of fifty percent (50%) or more of the\ - outstanding shares, or (iii) beneficial ownership of such entity.\ -\ - "You" (or "Your") shall mean an individual or Legal Entity\ - exercising permissions granted by this License.\ -\ - "Source" form shall mean the preferred form for making modifications,\ - including but not limited to software source code, documentation\ - source, and configuration files.\ -\ - "Object" form shall mean any form resulting from mechanical\ - transformation or translation of a Source form, including but\ - not limited to compiled object code, generated documentation,\ - and conversions to other media types.\ -\ - "Work" shall mean the work of authorship, whether in Source or\ - Object form, made available under the License, as indicated by a\ - copyright notice that is included in or attached to the work\ - (an example is provided in the Appendix below).\ -\ - "Derivative Works" shall mean any work, whether in Source or Object\ - form, that is based on (or derived from) the Work and for which the\ - editorial revisions, annotations, elaborations, or other modifications\ - represent, as a whole, an original work of authorship. For the purposes\ - of this License, Derivative Works shall not include works that remain\ - separable from, or merely link (or bind by name) to the interfaces of,\ - the Work and Derivative Works thereof.\ -\ - "Contribution" shall mean any work of authorship, including\ - the original version of the Work and any modifications or additions\ - to that Work or Derivative Works thereof, that is intentionally\ - submitted to Licensor for inclusion in the Work by the copyright owner\ - or by an individual or Legal Entity authorized to submit on behalf of\ - the copyright owner. For the purposes of this definition, "submitted"\ - means any form of electronic, verbal, or written communication sent\ - to the Licensor or its representatives, including but not limited to\ - communication on electronic mailing lists, source code control systems,\ - and issue tracking systems that are managed by, or on behalf of, the\ - Licensor for the purpose of discussing and improving the Work, but\ - excluding communication that is conspicuously marked or otherwise\ - designated in writing by the copyright owner as "Not a Contribution."\ -\ - "Contributor" shall mean Licensor and any individual or Legal Entity\ - on behalf of whom a Contribution has been received by Licensor and\ - subsequently incorporated within the Work.\ -\ - 2. Grant of Copyright License. Subject to the terms and conditions of\ - this License, each Contributor hereby grants to You a perpetual,\ - worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ - copyright license to reproduce, prepare Derivative Works of,\ - publicly display, publicly perform, sublicense, and distribute the\ - Work and such Derivative Works in Source or Object form.\ -\ - 3. Grant of Patent License. Subject to the terms and conditions of\ - this License, each Contributor hereby grants to You a perpetual,\ - worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ - (except as stated in this section) patent license to make, have made,\ - use, offer to sell, sell, import, and otherwise transfer the Work,\ - where such license applies only to those patent claims licensable\ - by such Contributor that are necessarily infringed by their\ - Contribution(s) alone or by combination of their Contribution(s)\ - with the Work to which such Contribution(s) was submitted. If You\ - institute patent litigation against any entity (including a\ - cross-claim or counterclaim in a lawsuit) alleging that the Work\ - or a Contribution incorporated within the Work constitutes direct\ - or contributory patent infringement, then any patent licenses\ - granted to You under this License for that Work shall terminate\ - as of the date such litigation is filed.\ -\ - 4. Redistribution. You may reproduce and distribute copies of the\ - Work or Derivative Works thereof in any medium, with or without\ - modifications, and in Source or Object form, provided that You\ - meet the following conditions:\ -\ - (a) You must give any other recipients of the Work or\ - Derivative Works a copy of this License; and\ -\ - (b) You must cause any modified files to carry prominent notices\ - stating that You changed the files; and\ -\ - (c) You must retain, in the Source form of any Derivative Works\ - that You distribute, all copyright, patent, trademark, and\ - attribution notices from the Source form of the Work,\ - excluding those notices that do not pertain to any part of\ - the Derivative Works; and\ -\ - (d) If the Work includes a "NOTICE" text file as part of its\ - distribution, then any Derivative Works that You distribute must\ - include a readable copy of the attribution notices contained\ - within such NOTICE file, excluding those notices that do not\ - pertain to any part of the Derivative Works, in at least one\ - of the following places: within a NOTICE text file distributed\ - as part of the Derivative Works; within the Source form or\ - documentation, if provided along with the Derivative Works; or,\ - within a display generated by the Derivative Works, if and\ - wherever such third-party notices normally appear. The contents\ - of the NOTICE file are for informational purposes only and\ - do not modify the License. You may add Your own attribution\ - notices within Derivative Works that You distribute, alongside\ - or as an addendum to the NOTICE text from the Work, provided\ - that such additional attribution notices cannot be construed\ - as modifying the License.\ -\ - You may add Your own copyright statement to Your modifications and\ - may provide additional or different license terms and conditions\ - for use, reproduction, or distribution of Your modifications, or\ - for any such Derivative Works as a whole, provided Your use,\ - reproduction, and distribution of the Work otherwise complies with\ - the conditions stated in this License.\ -\ - 5. Submission of Contributions. Unless You explicitly state otherwise,\ - any Contribution intentionally submitted for inclusion in the Work\ - by You to the Licensor shall be under the terms and conditions of\ - this License, without any additional terms or conditions.\ - Notwithstanding the above, nothing herein shall supersede or modify\ - the terms of any separate license agreement you may have executed\ - with Licensor regarding such Contributions.\ -\ - 6. Trademarks. This License does not grant permission to use the trade\ - names, trademarks, service marks, or product names of the Licensor,\ - except as required for reasonable and customary use in describing the\ - origin of the Work and reproducing the content of the NOTICE file.\ -\ - 7. Disclaimer of Warranty. Unless required by applicable law or\ - agreed to in writing, Licensor provides the Work (and each\ - Contributor provides its Contributions) on an "AS IS" BASIS,\ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\ - implied, including, without limitation, any warranties or conditions\ - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\ - PARTICULAR PURPOSE. You are solely responsible for determining the\ - appropriateness of using or redistributing the Work and assume any\ - risks associated with Your exercise of permissions under this License.\ -\ - 8. Limitation of Liability. In no event and under no legal theory,\ - whether in tort (including negligence), contract, or otherwise,\ - unless required by applicable law (such as deliberate and grossly\ - negligent acts) or agreed to in writing, shall any Contributor be\ - liable to You for damages, including any direct, indirect, special,\ - incidental, or consequential damages of any character arising as a\ - result of this License or out of the use or inability to use the\ - Work (including but not limited to damages for loss of goodwill,\ - work stoppage, computer failure or malfunction, or any and all\ - other commercial damages or losses), even if such Contributor\ - has been advised of the possibility of such damages.\ -\ - 9. Accepting Warranty or Additional Liability. While redistributing\ - the Work or Derivative Works thereof, You may choose to offer,\ - and charge a fee for, acceptance of support, warranty, indemnity,\ - or other liability obligations and/or rights consistent with this\ - License. However, in accepting such obligations, You may act only\ - on Your own behalf and on Your sole responsibility, not on behalf\ - of any other Contributor, and only if You agree to indemnify,\ - defend, and hold each Contributor harmless for any liability\ - incurred by, or claims asserted against, such Contributor by reason\ - of your accepting any such warranty or additional liability.\ -\ - END OF TERMS AND CONDITIONS\ -\ - APPENDIX: How to apply the Apache License to your work.\ -\ - To apply the Apache License to your work, attach the following\ - boilerplate notice, with the fields enclosed by brackets "[]"\ - replaced with your own identifying information. (Don't include\ - the brackets!) The text should be enclosed in the appropriate\ - comment syntax for the file format. We also recommend that a\ - file or class name and description of purpose be included on the\ - same "printed page" as the copyright notice for easier\ - identification within third-party archives.\ -\ - Copyright [yyyy] [name of copyright owner]\ -\ - Licensed under the Apache License, Version 2.0 (the "License");\ - you may not use this file except in compliance with the License.\ - You may obtain a copy of the License at\ -\ - http://www.apache.org/licenses/LICENSE-2.0\ -\ - Unless required by applicable law or agreed to in writing, software\ - distributed under the License is distributed on an "AS IS" BASIS,\ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\ - See the License for the specific language governing permissions and\ - limitations under the License.\ -\ -\ -\ -## Runtime Library Exception to the Apache 2.0 License: ##\ -\ -\ - As an exception, if you use this Software to compile your source code and\ - portions of this Software are embedded into the binary product as a result,\ - you may redistribute such product without providing attribution as would\ - otherwise be required by Sections 4(a), 4(b) and 4(d) of the License.\ -\ -\ -Dependency: github.com/apple/swift-system\ -Version: 1.2.1\ -License Content:\ - Apache License\ - Version 2.0, January 2004\ - http://www.apache.org/licenses/\ -\ - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\ -\ - 1. Definitions.\ -\ - "License" shall mean the terms and conditions for use, reproduction,\ - and distribution as defined by Sections 1 through 9 of this document.\ -\ - "Licensor" shall mean the copyright owner or entity authorized by\ - the copyright owner that is granting the License.\ -\ - "Legal Entity" shall mean the union of the acting entity and all\ - other entities that control, are controlled by, or are under common\ - control with that entity. For the purposes of this definition,\ - "control" means (i) the power, direct or indirect, to cause the\ - direction or management of such entity, whether by contract or\ - otherwise, or (ii) ownership of fifty percent (50%) or more of the\ - outstanding shares, or (iii) beneficial ownership of such entity.\ -\ - "You" (or "Your") shall mean an individual or Legal Entity\ - exercising permissions granted by this License.\ -\ - "Source" form shall mean the preferred form for making modifications,\ - including but not limited to software source code, documentation\ - source, and configuration files.\ -\ - "Object" form shall mean any form resulting from mechanical\ - transformation or translation of a Source form, including but\ - not limited to compiled object code, generated documentation,\ - and conversions to other media types.\ -\ - "Work" shall mean the work of authorship, whether in Source or\ - Object form, made available under the License, as indicated by a\ - copyright notice that is included in or attached to the work\ - (an example is provided in the Appendix below).\ -\ - "Derivative Works" shall mean any work, whether in Source or Object\ - form, that is based on (or derived from) the Work and for which the\ - editorial revisions, annotations, elaborations, or other modifications\ - represent, as a whole, an original work of authorship. For the purposes\ - of this License, Derivative Works shall not include works that remain\ - separable from, or merely link (or bind by name) to the interfaces of,\ - the Work and Derivative Works thereof.\ -\ - "Contribution" shall mean any work of authorship, including\ - the original version of the Work and any modifications or additions\ - to that Work or Derivative Works thereof, that is intentionally\ - submitted to Licensor for inclusion in the Work by the copyright owner\ - or by an individual or Legal Entity authorized to submit on behalf of\ - the copyright owner. For the purposes of this definition, "submitted"\ - means any form of electronic, verbal, or written communication sent\ - to the Licensor or its representatives, including but not limited to\ - communication on electronic mailing lists, source code control systems,\ - and issue tracking systems that are managed by, or on behalf of, the\ - Licensor for the purpose of discussing and improving the Work, but\ - excluding communication that is conspicuously marked or otherwise\ - designated in writing by the copyright owner as "Not a Contribution."\ -\ - "Contributor" shall mean Licensor and any individual or Legal Entity\ - on behalf of whom a Contribution has been received by Licensor and\ - subsequently incorporated within the Work.\ -\ - 2. Grant of Copyright License. Subject to the terms and conditions of\ - this License, each Contributor hereby grants to You a perpetual,\ - worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ - copyright license to reproduce, prepare Derivative Works of,\ - publicly display, publicly perform, sublicense, and distribute the\ - Work and such Derivative Works in Source or Object form.\ -\ - 3. Grant of Patent License. Subject to the terms and conditions of\ - this License, each Contributor hereby grants to You a perpetual,\ - worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ - (except as stated in this section) patent license to make, have made,\ - use, offer to sell, sell, import, and otherwise transfer the Work,\ - where such license applies only to those patent claims licensable\ - by such Contributor that are necessarily infringed by their\ - Contribution(s) alone or by combination of their Contribution(s)\ - with the Work to which such Contribution(s) was submitted. If You\ - institute patent litigation against any entity (including a\ - cross-claim or counterclaim in a lawsuit) alleging that the Work\ - or a Contribution incorporated within the Work constitutes direct\ - or contributory patent infringement, then any patent licenses\ - granted to You under this License for that Work shall terminate\ - as of the date such litigation is filed.\ -\ - 4. Redistribution. You may reproduce and distribute copies of the\ - Work or Derivative Works thereof in any medium, with or without\ - modifications, and in Source or Object form, provided that You\ - meet the following conditions:\ -\ - (a) You must give any other recipients of the Work or\ - Derivative Works a copy of this License; and\ -\ - (b) You must cause any modified files to carry prominent notices\ - stating that You changed the files; and\ -\ - (c) You must retain, in the Source form of any Derivative Works\ - that You distribute, all copyright, patent, trademark, and\ - attribution notices from the Source form of the Work,\ - excluding those notices that do not pertain to any part of\ - the Derivative Works; and\ -\ - (d) If the Work includes a "NOTICE" text file as part of its\ - distribution, then any Derivative Works that You distribute must\ - include a readable copy of the attribution notices contained\ - within such NOTICE file, excluding those notices that do not\ - pertain to any part of the Derivative Works, in at least one\ - of the following places: within a NOTICE text file distributed\ - as part of the Derivative Works; within the Source form or\ - documentation, if provided along with the Derivative Works; or,\ - within a display generated by the Derivative Works, if and\ - wherever such third-party notices normally appear. The contents\ - of the NOTICE file are for informational purposes only and\ - do not modify the License. You may add Your own attribution\ - notices within Derivative Works that You distribute, alongside\ - or as an addendum to the NOTICE text from the Work, provided\ - that such additional attribution notices cannot be construed\ - as modifying the License.\ -\ - You may add Your own copyright statement to Your modifications and\ - may provide additional or different license terms and conditions\ - for use, reproduction, or distribution of Your modifications, or\ - for any such Derivative Works as a whole, provided Your use,\ - reproduction, and distribution of the Work otherwise complies with\ - the conditions stated in this License.\ -\ - 5. Submission of Contributions. Unless You explicitly state otherwise,\ - any Contribution intentionally submitted for inclusion in the Work\ - by You to the Licensor shall be under the terms and conditions of\ - this License, without any additional terms or conditions.\ - Notwithstanding the above, nothing herein shall supersede or modify\ - the terms of any separate license agreement you may have executed\ - with Licensor regarding such Contributions.\ -\ - 6. Trademarks. This License does not grant permission to use the trade\ - names, trademarks, service marks, or product names of the Licensor,\ - except as required for reasonable and customary use in describing the\ - origin of the Work and reproducing the content of the NOTICE file.\ -\ - 7. Disclaimer of Warranty. Unless required by applicable law or\ - agreed to in writing, Licensor provides the Work (and each\ - Contributor provides its Contributions) on an "AS IS" BASIS,\ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\ - implied, including, without limitation, any warranties or conditions\ - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\ - PARTICULAR PURPOSE. You are solely responsible for determining the\ - appropriateness of using or redistributing the Work and assume any\ - risks associated with Your exercise of permissions under this License.\ -\ - 8. Limitation of Liability. In no event and under no legal theory,\ - whether in tort (including negligence), contract, or otherwise,\ - unless required by applicable law (such as deliberate and grossly\ - negligent acts) or agreed to in writing, shall any Contributor be\ - liable to You for damages, including any direct, indirect, special,\ - incidental, or consequential damages of any character arising as a\ - result of this License or out of the use or inability to use the\ - Work (including but not limited to damages for loss of goodwill,\ - work stoppage, computer failure or malfunction, or any and all\ - other commercial damages or losses), even if such Contributor\ - has been advised of the possibility of such damages.\ -\ - 9. Accepting Warranty or Additional Liability. While redistributing\ - the Work or Derivative Works thereof, You may choose to offer,\ - and charge a fee for, acceptance of support, warranty, indemnity,\ - or other liability obligations and/or rights consistent with this\ - License. However, in accepting such obligations, You may act only\ - on Your own behalf and on Your sole responsibility, not on behalf\ - of any other Contributor, and only if You agree to indemnify,\ - defend, and hold each Contributor harmless for any liability\ - incurred by, or claims asserted against, such Contributor by reason\ - of your accepting any such warranty or additional liability.\ -\ - END OF TERMS AND CONDITIONS\ -\ - APPENDIX: How to apply the Apache License to your work.\ -\ - To apply the Apache License to your work, attach the following\ - boilerplate notice, with the fields enclosed by brackets "[]"\ - replaced with your own identifying information. (Don't include\ - the brackets!) The text should be enclosed in the appropriate\ - comment syntax for the file format. We also recommend that a\ - file or class name and description of purpose be included on the\ - same "printed page" as the copyright notice for easier\ - identification within third-party archives.\ -\ - Copyright [yyyy] [name of copyright owner]\ -\ - Licensed under the Apache License, Version 2.0 (the "License");\ - you may not use this file except in compliance with the License.\ - You may obtain a copy of the License at\ -\ - http://www.apache.org/licenses/LICENSE-2.0\ -\ - Unless required by applicable law or agreed to in writing, software\ - distributed under the License is distributed on an "AS IS" BASIS,\ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\ - See the License for the specific language governing permissions and\ - limitations under the License.\ -\ -\ -\ -## Runtime Library Exception to the Apache 2.0 License: ##\ -\ -\ - As an exception, if you use this Software to compile your source code and\ - portions of this Software are embedded into the binary product as a result,\ - you may redistribute such product without providing attribution as would\ - otherwise be required by Sections 4(a), 4(b) and 4(d) of the License.\ -\ -\ -\ -Dependency: github.com/google/swift-benchmark\ -Version: 0.1.2\ -License Content:\ -\ - Apache License\ - Version 2.0, January 2004\ - http://www.apache.org/licenses/\ -\ - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\ -\ - 1. Definitions.\ -\ - "License" shall mean the terms and conditions for use, reproduction,\ - and distribution as defined by Sections 1 through 9 of this document.\ -\ - "Licensor" shall mean the copyright owner or entity authorized by\ - the copyright owner that is granting the License.\ -\ - "Legal Entity" shall mean the union of the acting entity and all\ - other entities that control, are controlled by, or are under common\ - control with that entity. For the purposes of this definition,\ - "control" means (i) the power, direct or indirect, to cause the\ - direction or management of such entity, whether by contract or\ - otherwise, or (ii) ownership of fifty percent (50%) or more of the\ - outstanding shares, or (iii) beneficial ownership of such entity.\ -\ - "You" (or "Your") shall mean an individual or Legal Entity\ - exercising permissions granted by this License.\ -\ - "Source" form shall mean the preferred form for making modifications,\ - including but not limited to software source code, documentation\ - source, and configuration files.\ -\ - "Object" form shall mean any form resulting from mechanical\ - transformation or translation of a Source form, including but\ - not limited to compiled object code, generated documentation,\ - and conversions to other media types.\ -\ - "Work" shall mean the work of authorship, whether in Source or\ - Object form, made available under the License, as indicated by a\ - copyright notice that is included in or attached to the work\ - (an example is provided in the Appendix below).\ -\ - "Derivative Works" shall mean any work, whether in Source or Object\ - form, that is based on (or derived from) the Work and for which the\ - editorial revisions, annotations, elaborations, or other modifications\ - represent, as a whole, an original work of authorship. For the purposes\ - of this License, Derivative Works shall not include works that remain\ - separable from, or merely link (or bind by name) to the interfaces of,\ - the Work and Derivative Works thereof.\ -\ - "Contribution" shall mean any work of authorship, including\ - the original version of the Work and any modifications or additions\ - to that Work or Derivative Works thereof, that is intentionally\ - submitted to Licensor for inclusion in the Work by the copyright owner\ - or by an individual or Legal Entity authorized to submit on behalf of\ - the copyright owner. For the purposes of this definition, "submitted"\ - means any form of electronic, verbal, or written communication sent\ - to the Licensor or its representatives, including but not limited to\ - communication on electronic mailing lists, source code control systems,\ - and issue tracking systems that are managed by, or on behalf of, the\ - Licensor for the purpose of discussing and improving the Work, but\ - excluding communication that is conspicuously marked or otherwise\ - designated in writing by the copyright owner as "Not a Contribution."\ -\ - "Contributor" shall mean Licensor and any individual or Legal Entity\ - on behalf of whom a Contribution has been received by Licensor and\ - subsequently incorporated within the Work.\ -\ - 2. Grant of Copyright License. Subject to the terms and conditions of\ - this License, each Contributor hereby grants to You a perpetual,\ - worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ - copyright license to reproduce, prepare Derivative Works of,\ - publicly display, publicly perform, sublicense, and distribute the\ - Work and such Derivative Works in Source or Object form.\ -\ - 3. Grant of Patent License. Subject to the terms and conditions of\ - this License, each Contributor hereby grants to You a perpetual,\ - worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ - (except as stated in this section) patent license to make, have made,\ - use, offer to sell, sell, import, and otherwise transfer the Work,\ - where such license applies only to those patent claims licensable\ - by such Contributor that are necessarily infringed by their\ - Contribution(s) alone or by combination of their Contribution(s)\ - with the Work to which such Contribution(s) was submitted. If You\ - institute patent litigation against any entity (including a\ - cross-claim or counterclaim in a lawsuit) alleging that the Work\ - or a Contribution incorporated within the Work constitutes direct\ - or contributory patent infringement, then any patent licenses\ - granted to You under this License for that Work shall terminate\ - as of the date such litigation is filed.\ -\ - 4. Redistribution. You may reproduce and distribute copies of the\ - Work or Derivative Works thereof in any medium, with or without\ - modifications, and in Source or Object form, provided that You\ - meet the following conditions:\ -\ - (a) You must give any other recipients of the Work or\ - Derivative Works a copy of this License; and\ -\ - (b) You must cause any modified files to carry prominent notices\ - stating that You changed the files; and\ -\ - (c) You must retain, in the Source form of any Derivative Works\ - that You distribute, all copyright, patent, trademark, and\ - attribution notices from the Source form of the Work,\ - excluding those notices that do not pertain to any part of\ - the Derivative Works; and\ -\ - (d) If the Work includes a "NOTICE" text file as part of its\ - distribution, then any Derivative Works that You distribute must\ - include a readable copy of the attribution notices contained\ - within such NOTICE file, excluding those notices that do not\ - pertain to any part of the Derivative Works, in at least one\ - of the following places: within a NOTICE text file distributed\ - as part of the Derivative Works; within the Source form or\ - documentation, if provided along with the Derivative Works; or,\ - within a display generated by the Derivative Works, if and\ - wherever such third-party notices normally appear. The contents\ - of the NOTICE file are for informational purposes only and\ - do not modify the License. You may add Your own attribution\ - notices within Derivative Works that You distribute, alongside\ - or as an addendum to the NOTICE text from the Work, provided\ - that such additional attribution notices cannot be construed\ - as modifying the License.\ -\ - You may add Your own copyright statement to Your modifications and\ - may provide additional or different license terms and conditions\ - for use, reproduction, or distribution of Your modifications, or\ - for any such Derivative Works as a whole, provided Your use,\ - reproduction, and distribution of the Work otherwise complies with\ - the conditions stated in this License.\ -\ - 5. Submission of Contributions. Unless You explicitly state otherwise,\ - any Contribution intentionally submitted for inclusion in the Work\ - by You to the Licensor shall be under the terms and conditions of\ - this License, without any additional terms or conditions.\ - Notwithstanding the above, nothing herein shall supersede or modify\ - the terms of any separate license agreement you may have executed\ - with Licensor regarding such Contributions.\ -\ - 6. Trademarks. This License does not grant permission to use the trade\ - names, trademarks, service marks, or product names of the Licensor,\ - except as required for reasonable and customary use in describing the\ - origin of the Work and reproducing the content of the NOTICE file.\ -\ - 7. Disclaimer of Warranty. Unless required by applicable law or\ - agreed to in writing, Licensor provides the Work (and each\ - Contributor provides its Contributions) on an "AS IS" BASIS,\ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\ - implied, including, without limitation, any warranties or conditions\ - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\ - PARTICULAR PURPOSE. You are solely responsible for determining the\ - appropriateness of using or redistributing the Work and assume any\ - risks associated with Your exercise of permissions under this License.\ -\ - 8. Limitation of Liability. In no event and under no legal theory,\ - whether in tort (including negligence), contract, or otherwise,\ - unless required by applicable law (such as deliberate and grossly\ - negligent acts) or agreed to in writing, shall any Contributor be\ - liable to You for damages, including any direct, indirect, special,\ - incidental, or consequential damages of any character arising as a\ - result of this License or out of the use or inability to use the\ - Work (including but not limited to damages for loss of goodwill,\ - work stoppage, computer failure or malfunction, or any and all\ - other commercial damages or losses), even if such Contributor\ - has been advised of the possibility of such damages.\ -\ - 9. Accepting Warranty or Additional Liability. While redistributing\ - the Work or Derivative Works thereof, You may choose to offer,\ - and charge a fee for, acceptance of support, warranty, indemnity,\ - or other liability obligations and/or rights consistent with this\ - License. However, in accepting such obligations, You may act only\ - on Your own behalf and on Your sole responsibility, not on behalf\ - of any other Contributor, and only if You agree to indemnify,\ - defend, and hold each Contributor harmless for any liability\ - incurred by, or claims asserted against, such Contributor by reason\ - of your accepting any such warranty or additional liability.\ -\ - END OF TERMS AND CONDITIONS\ -\ - APPENDIX: How to apply the Apache License to your work.\ -\ - To apply the Apache License to your work, attach the following\ - boilerplate notice, with the fields enclosed by brackets "[]"\ - replaced with your own identifying information. (Don't include\ - the brackets!) The text should be enclosed in the appropriate\ - comment syntax for the file format. We also recommend that a\ - file or class name and description of purpose be included on the\ - same "printed page" as the copyright notice for easier\ - identification within third-party archives.\ -\ - Copyright [yyyy] [name of copyright owner]\ -\ - Licensed under the Apache License, Version 2.0 (the "License");\ - you may not use this file except in compliance with the License.\ - You may obtain a copy of the License at\ -\ - http://www.apache.org/licenses/LICENSE-2.0\ -\ - Unless required by applicable law or agreed to in writing, software\ - distributed under the License is distributed on an "AS IS" BASIS,\ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\ - See the License for the specific language governing permissions and\ - limitations under the License.\ -\ -\ -Dependency: github.com/apple/swift-argument-parser\ -Version: 1.2.2\ -License Content:\ - Apache License\ - Version 2.0, January 2004\ - http://www.apache.org/licenses/\ -\ - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\ -\ - 1. Definitions.\ -\ - "License" shall mean the terms and conditions for use, reproduction,\ - and distribution as defined by Sections 1 through 9 of this document.\ -\ - "Licensor" shall mean the copyright owner or entity authorized by\ - the copyright owner that is granting the License.\ -\ - "Legal Entity" shall mean the union of the acting entity and all\ - other entities that control, are controlled by, or are under common\ - control with that entity. For the purposes of this definition,\ - "control" means (i) the power, direct or indirect, to cause the\ - direction or management of such entity, whether by contract or\ - otherwise, or (ii) ownership of fifty percent (50%) or more of the\ - outstanding shares, or (iii) beneficial ownership of such entity.\ -\ - "You" (or "Your") shall mean an individual or Legal Entity\ - exercising permissions granted by this License.\ -\ - "Source" form shall mean the preferred form for making modifications,\ - including but not limited to software source code, documentation\ - source, and configuration files.\ -\ - "Object" form shall mean any form resulting from mechanical\ - transformation or translation of a Source form, including but\ - not limited to compiled object code, generated documentation,\ - and conversions to other media types.\ -\ - "Work" shall mean the work of authorship, whether in Source or\ - Object form, made available under the License, as indicated by a\ - copyright notice that is included in or attached to the work\ - (an example is provided in the Appendix below).\ -\ - "Derivative Works" shall mean any work, whether in Source or Object\ - form, that is based on (or derived from) the Work and for which the\ - editorial revisions, annotations, elaborations, or other modifications\ - represent, as a whole, an original work of authorship. For the purposes\ - of this License, Derivative Works shall not include works that remain\ - separable from, or merely link (or bind by name) to the interfaces of,\ - the Work and Derivative Works thereof.\ -\ - "Contribution" shall mean any work of authorship, including\ - the original version of the Work and any modifications or additions\ - to that Work or Derivative Works thereof, that is intentionally\ - submitted to Licensor for inclusion in the Work by the copyright owner\ - or by an individual or Legal Entity authorized to submit on behalf of\ - the copyright owner. For the purposes of this definition, "submitted"\ - means any form of electronic, verbal, or written communication sent\ - to the Licensor or its representatives, including but not limited to\ - communication on electronic mailing lists, source code control systems,\ - and issue tracking systems that are managed by, or on behalf of, the\ - Licensor for the purpose of discussing and improving the Work, but\ - excluding communication that is conspicuously marked or otherwise\ - designated in writing by the copyright owner as "Not a Contribution."\ -\ - "Contributor" shall mean Licensor and any individual or Legal Entity\ - on behalf of whom a Contribution has been received by Licensor and\ - subsequently incorporated within the Work.\ -\ - 2. Grant of Copyright License. Subject to the terms and conditions of\ - this License, each Contributor hereby grants to You a perpetual,\ - worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ - copyright license to reproduce, prepare Derivative Works of,\ - publicly display, publicly perform, sublicense, and distribute the\ - Work and such Derivative Works in Source or Object form.\ -\ - 3. Grant of Patent License. Subject to the terms and conditions of\ - this License, each Contributor hereby grants to You a perpetual,\ - worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ - (except as stated in this section) patent license to make, have made,\ - use, offer to sell, sell, import, and otherwise transfer the Work,\ - where such license applies only to those patent claims licensable\ - by such Contributor that are necessarily infringed by their\ - Contribution(s) alone or by combination of their Contribution(s)\ - with the Work to which such Contribution(s) was submitted. If You\ - institute patent litigation against any entity (including a\ - cross-claim or counterclaim in a lawsuit) alleging that the Work\ - or a Contribution incorporated within the Work constitutes direct\ - or contributory patent infringement, then any patent licenses\ - granted to You under this License for that Work shall terminate\ - as of the date such litigation is filed.\ -\ - 4. Redistribution. You may reproduce and distribute copies of the\ - Work or Derivative Works thereof in any medium, with or without\ - modifications, and in Source or Object form, provided that You\ - meet the following conditions:\ -\ - (a) You must give any other recipients of the Work or\ - Derivative Works a copy of this License; and\ -\ - (b) You must cause any modified files to carry prominent notices\ - stating that You changed the files; and\ -\ - (c) You must retain, in the Source form of any Derivative Works\ - that You distribute, all copyright, patent, trademark, and\ - attribution notices from the Source form of the Work,\ - excluding those notices that do not pertain to any part of\ - the Derivative Works; and\ -\ - (d) If the Work includes a "NOTICE" text file as part of its\ - distribution, then any Derivative Works that You distribute must\ - include a readable copy of the attribution notices contained\ - within such NOTICE file, excluding those notices that do not\ - pertain to any part of the Derivative Works, in at least one\ - of the following places: within a NOTICE text file distributed\ - as part of the Derivative Works; within the Source form or\ - documentation, if provided along with the Derivative Works; or,\ - within a display generated by the Derivative Works, if and\ - wherever such third-party notices normally appear. The contents\ - of the NOTICE file are for informational purposes only and\ - do not modify the License. You may add Your own attribution\ - notices within Derivative Works that You distribute, alongside\ - or as an addendum to the NOTICE text from the Work, provided\ - that such additional attribution notices cannot be construed\ - as modifying the License.\ -\ - You may add Your own copyright statement to Your modifications and\ - may provide additional or different license terms and conditions\ - for use, reproduction, or distribution of Your modifications, or\ - for any such Derivative Works as a whole, provided Your use,\ - reproduction, and distribution of the Work otherwise complies with\ - the conditions stated in this License.\ -\ - 5. Submission of Contributions. Unless You explicitly state otherwise,\ - any Contribution intentionally submitted for inclusion in the Work\ - by You to the Licensor shall be under the terms and conditions of\ - this License, without any additional terms or conditions.\ - Notwithstanding the above, nothing herein shall supersede or modify\ - the terms of any separate license agreement you may have executed\ - with Licensor regarding such Contributions.\ -\ - 6. Trademarks. This License does not grant permission to use the trade\ - names, trademarks, service marks, or product names of the Licensor,\ - except as required for reasonable and customary use in describing the\ - origin of the Work and reproducing the content of the NOTICE file.\ -\ - 7. Disclaimer of Warranty. Unless required by applicable law or\ - agreed to in writing, Licensor provides the Work (and each\ - Contributor provides its Contributions) on an "AS IS" BASIS,\ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\ - implied, including, without limitation, any warranties or conditions\ - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\ - PARTICULAR PURPOSE. You are solely responsible for determining the\ - appropriateness of using or redistributing the Work and assume any\ - risks associated with Your exercise of permissions under this License.\ -\ - 8. Limitation of Liability. In no event and under no legal theory,\ - whether in tort (including negligence), contract, or otherwise,\ - unless required by applicable law (such as deliberate and grossly\ - negligent acts) or agreed to in writing, shall any Contributor be\ - liable to You for damages, including any direct, indirect, special,\ - incidental, or consequential damages of any character arising as a\ - result of this License or out of the use or inability to use the\ - Work (including but not limited to damages for loss of goodwill,\ - work stoppage, computer failure or malfunction, or any and all\ - other commercial damages or losses), even if such Contributor\ - has been advised of the possibility of such damages.\ -\ - 9. Accepting Warranty or Additional Liability. While redistributing\ - the Work or Derivative Works thereof, You may choose to offer,\ - and charge a fee for, acceptance of support, warranty, indemnity,\ - or other liability obligations and/or rights consistent with this\ - License. However, in accepting such obligations, You may act only\ - on Your own behalf and on Your sole responsibility, not on behalf\ - of any other Contributor, and only if You agree to indemnify,\ - defend, and hold each Contributor harmless for any liability\ - incurred by, or claims asserted against, such Contributor by reason\ - of your accepting any such warranty or additional liability.\ -\ - END OF TERMS AND CONDITIONS\ -\ - APPENDIX: How to apply the Apache License to your work.\ -\ - To apply the Apache License to your work, attach the following\ - boilerplate notice, with the fields enclosed by brackets "[]"\ - replaced with your own identifying information. (Don't include\ - the brackets!) The text should be enclosed in the appropriate\ - comment syntax for the file format. We also recommend that a\ - file or class name and description of purpose be included on the\ - same "printed page" as the copyright notice for easier\ - identification within third-party archives.\ -\ - Copyright [yyyy] [name of copyright owner]\ -\ - Licensed under the Apache License, Version 2.0 (the "License");\ - you may not use this file except in compliance with the License.\ - You may obtain a copy of the License at\ -\ - http://www.apache.org/licenses/LICENSE-2.0\ -\ - Unless required by applicable law or agreed to in writing, software\ - distributed under the License is distributed on an "AS IS" BASIS,\ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\ - See the License for the specific language governing permissions and\ - limitations under the License.\ -\ -\ -\ -## Runtime Library Exception to the Apache 2.0 License: ##\ -\ -\ - As an exception, if you use this Software to compile your source code and\ - portions of this Software are embedded into the binary product as a result,\ - you may redistribute such product without providing attribution as would\ - otherwise be required by Sections 4(a), 4(b) and 4(d) of the License.\ -\ -\ -Dependency: github.com/apple/swift-collections\ -Version: 1.1.0\ -License Content:\ - Apache License\ - Version 2.0, January 2004\ - http://www.apache.org/licenses/\ -\ - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\ -\ - 1. Definitions.\ -\ - "License" shall mean the terms and conditions for use, reproduction,\ - and distribution as defined by Sections 1 through 9 of this document.\ -\ - "Licensor" shall mean the copyright owner or entity authorized by\ - the copyright owner that is granting the License.\ -\ - "Legal Entity" shall mean the union of the acting entity and all\ - other entities that control, are controlled by, or are under common\ - control with that entity. For the purposes of this definition,\ - "control" means (i) the power, direct or indirect, to cause the\ - direction or management of such entity, whether by contract or\ - otherwise, or (ii) ownership of fifty percent (50%) or more of the\ - outstanding shares, or (iii) beneficial ownership of such entity.\ -\ - "You" (or "Your") shall mean an individual or Legal Entity\ - exercising permissions granted by this License.\ -\ - "Source" form shall mean the preferred form for making modifications,\ - including but not limited to software source code, documentation\ - source, and configuration files.\ -\ - "Object" form shall mean any form resulting from mechanical\ - transformation or translation of a Source form, including but\ - not limited to compiled object code, generated documentation,\ - and conversions to other media types.\ -\ - "Work" shall mean the work of authorship, whether in Source or\ - Object form, made available under the License, as indicated by a\ - copyright notice that is included in or attached to the work\ - (an example is provided in the Appendix below).\ -\ - "Derivative Works" shall mean any work, whether in Source or Object\ - form, that is based on (or derived from) the Work and for which the\ - editorial revisions, annotations, elaborations, or other modifications\ - represent, as a whole, an original work of authorship. For the purposes\ - of this License, Derivative Works shall not include works that remain\ - separable from, or merely link (or bind by name) to the interfaces of,\ - the Work and Derivative Works thereof.\ -\ - "Contribution" shall mean any work of authorship, including\ - the original version of the Work and any modifications or additions\ - to that Work or Derivative Works thereof, that is intentionally\ - submitted to Licensor for inclusion in the Work by the copyright owner\ - or by an individual or Legal Entity authorized to submit on behalf of\ - the copyright owner. For the purposes of this definition, "submitted"\ - means any form of electronic, verbal, or written communication sent\ - to the Licensor or its representatives, including but not limited to\ - communication on electronic mailing lists, source code control systems,\ - and issue tracking systems that are managed by, or on behalf of, the\ - Licensor for the purpose of discussing and improving the Work, but\ - excluding communication that is conspicuously marked or otherwise\ - designated in writing by the copyright owner as "Not a Contribution."\ -\ - "Contributor" shall mean Licensor and any individual or Legal Entity\ - on behalf of whom a Contribution has been received by Licensor and\ - subsequently incorporated within the Work.\ -\ - 2. Grant of Copyright License. Subject to the terms and conditions of\ - this License, each Contributor hereby grants to You a perpetual,\ - worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ - copyright license to reproduce, prepare Derivative Works of,\ - publicly display, publicly perform, sublicense, and distribute the\ - Work and such Derivative Works in Source or Object form.\ -\ - 3. Grant of Patent License. Subject to the terms and conditions of\ - this License, each Contributor hereby grants to You a perpetual,\ - worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ - (except as stated in this section) patent license to make, have made,\ - use, offer to sell, sell, import, and otherwise transfer the Work,\ - where such license applies only to those patent claims licensable\ - by such Contributor that are necessarily infringed by their\ - Contribution(s) alone or by combination of their Contribution(s)\ - with the Work to which such Contribution(s) was submitted. If You\ - institute patent litigation against any entity (including a\ - cross-claim or counterclaim in a lawsuit) alleging that the Work\ - or a Contribution incorporated within the Work constitutes direct\ - or contributory patent infringement, then any patent licenses\ - granted to You under this License for that Work shall terminate\ - as of the date such litigation is filed.\ -\ - 4. Redistribution. You may reproduce and distribute copies of the\ - Work or Derivative Works thereof in any medium, with or without\ - modifications, and in Source or Object form, provided that You\ - meet the following conditions:\ -\ - (a) You must give any other recipients of the Work or\ - Derivative Works a copy of this License; and\ -\ - (b) You must cause any modified files to carry prominent notices\ - stating that You changed the files; and\ -\ - (c) You must retain, in the Source form of any Derivative Works\ - that You distribute, all copyright, patent, trademark, and\ - attribution notices from the Source form of the Work,\ - excluding those notices that do not pertain to any part of\ - the Derivative Works; and\ -\ - (d) If the Work includes a "NOTICE" text file as part of its\ - distribution, then any Derivative Works that You distribute must\ - include a readable copy of the attribution notices contained\ - within such NOTICE file, excluding those notices that do not\ - pertain to any part of the Derivative Works, in at least one\ - of the following places: within a NOTICE text file distributed\ - as part of the Derivative Works; within the Source form or\ - documentation, if provided along with the Derivative Works; or,\ - within a display generated by the Derivative Works, if and\ - wherever such third-party notices normally appear. The contents\ - of the NOTICE file are for informational purposes only and\ - do not modify the License. You may add Your own attribution\ - notices within Derivative Works that You distribute, alongside\ - or as an addendum to the NOTICE text from the Work, provided\ - that such additional attribution notices cannot be construed\ - as modifying the License.\ -\ - You may add Your own copyright statement to Your modifications and\ - may provide additional or different license terms and conditions\ - for use, reproduction, or distribution of Your modifications, or\ - for any such Derivative Works as a whole, provided Your use,\ - reproduction, and distribution of the Work otherwise complies with\ - the conditions stated in this License.\ -\ - 5. Submission of Contributions. Unless You explicitly state otherwise,\ - any Contribution intentionally submitted for inclusion in the Work\ - by You to the Licensor shall be under the terms and conditions of\ - this License, without any additional terms or conditions.\ - Notwithstanding the above, nothing herein shall supersede or modify\ - the terms of any separate license agreement you may have executed\ - with Licensor regarding such Contributions.\ -\ - 6. Trademarks. This License does not grant permission to use the trade\ - names, trademarks, service marks, or product names of the Licensor,\ - except as required for reasonable and customary use in describing the\ - origin of the Work and reproducing the content of the NOTICE file.\ -\ - 7. Disclaimer of Warranty. Unless required by applicable law or\ - agreed to in writing, Licensor provides the Work (and each\ - Contributor provides its Contributions) on an "AS IS" BASIS,\ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\ - implied, including, without limitation, any warranties or conditions\ - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\ - PARTICULAR PURPOSE. You are solely responsible for determining the\ - appropriateness of using or redistributing the Work and assume any\ - risks associated with Your exercise of permissions under this License.\ -\ - 8. Limitation of Liability. In no event and under no legal theory,\ - whether in tort (including negligence), contract, or otherwise,\ - unless required by applicable law (such as deliberate and grossly\ - negligent acts) or agreed to in writing, shall any Contributor be\ - liable to You for damages, including any direct, indirect, special,\ - incidental, or consequential damages of any character arising as a\ - result of this License or out of the use or inability to use the\ - Work (including but not limited to damages for loss of goodwill,\ - work stoppage, computer failure or malfunction, or any and all\ - other commercial damages or losses), even if such Contributor\ - has been advised of the possibility of such damages.\ -\ - 9. Accepting Warranty or Additional Liability. While redistributing\ - the Work or Derivative Works thereof, You may choose to offer,\ - and charge a fee for, acceptance of support, warranty, indemnity,\ - or other liability obligations and/or rights consistent with this\ - License. However, in accepting such obligations, You may act only\ - on Your own behalf and on Your sole responsibility, not on behalf\ - of any other Contributor, and only if You agree to indemnify,\ - defend, and hold each Contributor harmless for any liability\ - incurred by, or claims asserted against, such Contributor by reason\ - of your accepting any such warranty or additional liability.\ -\ - END OF TERMS AND CONDITIONS\ -\ - APPENDIX: How to apply the Apache License to your work.\ -\ - To apply the Apache License to your work, attach the following\ - boilerplate notice, with the fields enclosed by brackets "[]"\ - replaced with your own identifying information. (Don't include\ - the brackets!) The text should be enclosed in the appropriate\ - comment syntax for the file format. We also recommend that a\ - file or class name and description of purpose be included on the\ - same "printed page" as the copyright notice for easier\ - identification within third-party archives.\ -\ - Copyright [yyyy] [name of copyright owner]\ -\ - Licensed under the Apache License, Version 2.0 (the "License");\ - you may not use this file except in compliance with the License.\ - You may obtain a copy of the License at\ -\ - http://www.apache.org/licenses/LICENSE-2.0\ -\ - Unless required by applicable law or agreed to in writing, software\ - distributed under the License is distributed on an "AS IS" BASIS,\ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\ - See the License for the specific language governing permissions and\ - limitations under the License.\ -\ -\ -\ -## Runtime Library Exception to the Apache 2.0 License: ##\ -\ -\ - As an exception, if you use this Software to compile your source code and\ - portions of this Software are embedded into the binary product as a result,\ - you may redistribute such product without providing attribution as would\ - otherwise be required by Sections 4(a), 4(b) and 4(d) of the License.\ -\ -\ -Dependency: github.com/Frizlab/FSEventsWrapper\ -Version: 1.0.2\ -License Content:\ -MIT License\ -\ -Copyright \'a9 2023 Fran\'e7ois Lamboley \ -\ -Permission is hereby granted, free of charge, to any person obtaining a copy\ -of this software and associated documentation files (the "Software"), to deal\ -in the Software without restriction, including without limitation the rights\ -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ -copies of the Software, and to permit persons to whom the Software is\ -furnished to do so, subject to the following conditions:\ -\ -The above copyright notice and this permission notice shall be included in all\ -copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ -SOFTWARE.\ -\ -\ -Dependency: github.com/Bouke/Glob\ -Version: 1.0.5\ -License Content:\ - Apache License\ - Version 2.0, January 2004\ - http://www.apache.org/licenses/\ -\ - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\ -\ - 1. Definitions.\ -\ - "License" shall mean the terms and conditions for use, reproduction,\ - and distribution as defined by Sections 1 through 9 of this document.\ -\ - "Licensor" shall mean the copyright owner or entity authorized by\ - the copyright owner that is granting the License.\ -\ - "Legal Entity" shall mean the union of the acting entity and all\ - other entities that control, are controlled by, or are under common\ - control with that entity. For the purposes of this definition,\ - "control" means (i) the power, direct or indirect, to cause the\ - direction or management of such entity, whether by contract or\ - otherwise, or (ii) ownership of fifty percent (50%) or more of the\ - outstanding shares, or (iii) beneficial ownership of such entity.\ -\ - "You" (or "Your") shall mean an individual or Legal Entity\ - exercising permissions granted by this License.\ -\ - "Source" form shall mean the preferred form for making modifications,\ - including but not limited to software source code, documentation\ - source, and configuration files.\ -\ - "Object" form shall mean any form resulting from mechanical\ - transformation or translation of a Source form, including but\ - not limited to compiled object code, generated documentation,\ - and conversions to other media types.\ -\ - "Work" shall mean the work of authorship, whether in Source or\ - Object form, made available under the License, as indicated by a\ - copyright notice that is included in or attached to the work\ - (an example is provided in the Appendix below).\ -\ - "Derivative Works" shall mean any work, whether in Source or Object\ - form, that is based on (or derived from) the Work and for which the\ - editorial revisions, annotations, elaborations, or other modifications\ - represent, as a whole, an original work of authorship. For the purposes\ - of this License, Derivative Works shall not include works that remain\ - separable from, or merely link (or bind by name) to the interfaces of,\ - the Work and Derivative Works thereof.\ -\ - "Contribution" shall mean any work of authorship, including\ - the original version of the Work and any modifications or additions\ - to that Work or Derivative Works thereof, that is intentionally\ - submitted to Licensor for inclusion in the Work by the copyright owner\ - or by an individual or Legal Entity authorized to submit on behalf of\ - the copyright owner. For the purposes of this definition, "submitted"\ - means any form of electronic, verbal, or written communication sent\ - to the Licensor or its representatives, including but not limited to\ - communication on electronic mailing lists, source code control systems,\ - and issue tracking systems that are managed by, or on behalf of, the\ - Licensor for the purpose of discussing and improving the Work, but\ - excluding communication that is conspicuously marked or otherwise\ - designated in writing by the copyright owner as "Not a Contribution."\ -\ - "Contributor" shall mean Licensor and any individual or Legal Entity\ - on behalf of whom a Contribution has been received by Licensor and\ - subsequently incorporated within the Work.\ -\ - 2. Grant of Copyright License. Subject to the terms and conditions of\ - this License, each Contributor hereby grants to You a perpetual,\ - worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ - copyright license to reproduce, prepare Derivative Works of,\ - publicly display, publicly perform, sublicense, and distribute the\ - Work and such Derivative Works in Source or Object form.\ -\ - 3. Grant of Patent License. Subject to the terms and conditions of\ - this License, each Contributor hereby grants to You a perpetual,\ - worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ - (except as stated in this section) patent license to make, have made,\ - use, offer to sell, sell, import, and otherwise transfer the Work,\ - where such license applies only to those patent claims licensable\ - by such Contributor that are necessarily infringed by their\ - Contribution(s) alone or by combination of their Contribution(s)\ - with the Work to which such Contribution(s) was submitted. If You\ - institute patent litigation against any entity (including a\ - cross-claim or counterclaim in a lawsuit) alleging that the Work\ - or a Contribution incorporated within the Work constitutes direct\ - or contributory patent infringement, then any patent licenses\ - granted to You under this License for that Work shall terminate\ - as of the date such litigation is filed.\ -\ - 4. Redistribution. You may reproduce and distribute copies of the\ - Work or Derivative Works thereof in any medium, with or without\ - modifications, and in Source or Object form, provided that You\ - meet the following conditions:\ -\ - (a) You must give any other recipients of the Work or\ - Derivative Works a copy of this License; and\ -\ - (b) You must cause any modified files to carry prominent notices\ - stating that You changed the files; and\ -\ - (c) You must retain, in the Source form of any Derivative Works\ - that You distribute, all copyright, patent, trademark, and\ - attribution notices from the Source form of the Work,\ - excluding those notices that do not pertain to any part of\ - the Derivative Works; and\ -\ - (d) If the Work includes a "NOTICE" text file as part of its\ - distribution, then any Derivative Works that You distribute must\ - include a readable copy of the attribution notices contained\ - within such NOTICE file, excluding those notices that do not\ - pertain to any part of the Derivative Works, in at least one\ - of the following places: within a NOTICE text file distributed\ - as part of the Derivative Works; within the Source form or\ - documentation, if provided along with the Derivative Works; or,\ - within a display generated by the Derivative Works, if and\ - wherever such third-party notices normally appear. The contents\ - of the NOTICE file are for informational purposes only and\ - do not modify the License. You may add Your own attribution\ - notices within Derivative Works that You distribute, alongside\ - or as an addendum to the NOTICE text from the Work, provided\ - that such additional attribution notices cannot be construed\ - as modifying the License.\ -\ - You may add Your own copyright statement to Your modifications and\ - may provide additional or different license terms and conditions\ - for use, reproduction, or distribution of Your modifications, or\ - for any such Derivative Works as a whole, provided Your use,\ - reproduction, and distribution of the Work otherwise complies with\ - the conditions stated in this License.\ -\ - 5. Submission of Contributions. Unless You explicitly state otherwise,\ - any Contribution intentionally submitted for inclusion in the Work\ - by You to the Licensor shall be under the terms and conditions of\ - this License, without any additional terms or conditions.\ - Notwithstanding the above, nothing herein shall supersede or modify\ - the terms of any separate license agreement you may have executed\ - with Licensor regarding such Contributions.\ -\ - 6. Trademarks. This License does not grant permission to use the trade\ - names, trademarks, service marks, or product names of the Licensor,\ - except as required for reasonable and customary use in describing the\ - origin of the Work and reproducing the content of the NOTICE file.\ -\ - 7. Disclaimer of Warranty. Unless required by applicable law or\ - agreed to in writing, Licensor provides the Work (and each\ - Contributor provides its Contributions) on an "AS IS" BASIS,\ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\ - implied, including, without limitation, any warranties or conditions\ - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\ - PARTICULAR PURPOSE. You are solely responsible for determining the\ - appropriateness of using or redistributing the Work and assume any\ - risks associated with Your exercise of permissions under this License.\ -\ - 8. Limitation of Liability. In no event and under no legal theory,\ - whether in tort (including negligence), contract, or otherwise,\ - unless required by applicable law (such as deliberate and grossly\ - negligent acts) or agreed to in writing, shall any Contributor be\ - liable to You for damages, including any direct, indirect, special,\ - incidental, or consequential damages of any character arising as a\ - result of this License or out of the use or inability to use the\ - Work (including but not limited to damages for loss of goodwill,\ - work stoppage, computer failure or malfunction, or any and all\ - other commercial damages or losses), even if such Contributor\ - has been advised of the possibility of such damages.\ -\ - 9. Accepting Warranty or Additional Liability. While redistributing\ - the Work or Derivative Works thereof, You may choose to offer,\ - and charge a fee for, acceptance of support, warranty, indemnity,\ - or other liability obligations and/or rights consistent with this\ - License. However, in accepting such obligations, You may act only\ - on Your own behalf and on Your sole responsibility, not on behalf\ - of any other Contributor, and only if You agree to indemnify,\ - defend, and hold each Contributor harmless for any liability\ - incurred by, or claims asserted against, such Contributor by reason\ - of your accepting any such warranty or additional liability.\ -\ - END OF TERMS AND CONDITIONS\ -\ - APPENDIX: How to apply the Apache License to your work.\ -\ - To apply the Apache License to your work, attach the following\ - boilerplate notice, with the fields enclosed by brackets "[]"\ - replaced with your own identifying information. (Don't include\ - the brackets!) The text should be enclosed in the appropriate\ - comment syntax for the file format. We also recommend that a\ - file or class name and description of purpose be included on the\ - same "printed page" as the copyright notice for easier\ - identification within third-party archives.\ -\ - Copyright 2016 Bouke Haarsma\ -\ - Licensed under the Apache License, Version 2.0 (the "License");\ - you may not use this file except in compliance with the License.\ - You may obtain a copy of the License at\ -\ - http://www.apache.org/licenses/LICENSE-2.0\ -\ - Unless required by applicable law or agreed to in writing, software\ - distributed under the License is distributed on an "AS IS" BASIS,\ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\ - See the License for the specific language governing permissions and\ - limitations under the License.\ -\ -\ -Dependency: github.com/ChimeHQ/JSONRPC\ -Version: 0.9.0\ -License Content:\ -BSD 3-Clause License\ -\ -Copyright (c) 2021, Chime\ -All rights reserved.\ -\ -Redistribution and use in source and binary forms, with or without\ -modification, are permitted provided that the following conditions are met:\ -\ -1. Redistributions of source code must retain the above copyright notice, this\ - list of conditions and the following disclaimer.\ -\ -2. Redistributions in binary form must reproduce the above copyright notice,\ - this list of conditions and the following disclaimer in the documentation\ - and/or other materials provided with the distribution.\ -\ -3. Neither the name of the copyright holder nor the names of its\ - contributors may be used to endorse or promote products derived from\ - this software without specific prior written permission.\ -\ -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\ -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\ -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\ -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\ -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\ -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\ -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\ -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\ -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\ -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\ -\ -\ -Dependency: github.com/ChimeHQ/LanguageServerProtocol\ -Version: 0.13.3\ -License Content:\ -BSD 3-Clause License\ -\ -Copyright (c) 2021, Chime\ -All rights reserved.\ -\ -Redistribution and use in source and binary forms, with or without\ -modification, are permitted provided that the following conditions are met:\ -\ -1. Redistributions of source code must retain the above copyright notice, this\ - list of conditions and the following disclaimer.\ -\ -2. Redistributions in binary form must reproduce the above copyright notice,\ - this list of conditions and the following disclaimer in the documentation\ - and/or other materials provided with the distribution.\ -\ -3. Neither the name of the copyright holder nor the names of its\ - contributors may be used to endorse or promote products derived from\ - this software without specific prior written permission.\ -\ -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\ -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\ -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\ -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\ -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\ -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\ -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\ -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\ -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\ -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\ -\ -\ -Dependency: github.com/ChimeHQ/OperationPlus\ -Version: 1.6.0\ -License Content:\ -BSD 3-Clause License\ -\ -Copyright (c) 2019, Chime\ -All rights reserved.\ -\ -Redistribution and use in source and binary forms, with or without\ -modification, are permitted provided that the following conditions are met:\ -\ -* Redistributions of source code must retain the above copyright notice, this\ - list of conditions and the following disclaimer.\ -\ -* Redistributions in binary form must reproduce the above copyright notice,\ - this list of conditions and the following disclaimer in the documentation\ - and/or other materials provided with the distribution.\ -\ -* Neither the name of the copyright holder nor the names of its\ - contributors may be used to endorse or promote products derived from\ - this software without specific prior written permission.\ -\ -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\ -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\ -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\ -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\ -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\ -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\ -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\ -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\ -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\ -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\ -\ -\ -Dependency: github.com/GottaGetSwifty/CodableWrappers\ -Version: 2.0.7\ -License Content:\ - Apache License\ - Version 2.0, January 2004\ - http://www.apache.org/licenses/\ -\ - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\ -\ - 1. Definitions.\ -\ - "License" shall mean the terms and conditions for use, reproduction,\ - and distribution as defined by Sections 1 through 9 of this document.\ -\ - "Licensor" shall mean the copyright owner or entity authorized by\ - the copyright owner that is granting the License.\ -\ - "Legal Entity" shall mean the union of the acting entity and all\ - other entities that control, are controlled by, or are under common\ - control with that entity. For the purposes of this definition,\ - "control" means (i) the power, direct or indirect, to cause the\ - direction or management of such entity, whether by contract or\ - otherwise, or (ii) ownership of fifty percent (50%) or more of the\ - outstanding shares, or (iii) beneficial ownership of such entity.\ -\ - "You" (or "Your") shall mean an individual or Legal Entity\ - exercising permissions granted by this License.\ -\ - "Source" form shall mean the preferred form for making modifications,\ - including but not limited to software source code, documentation\ - source, and configuration files.\ -\ - "Object" form shall mean any form resulting from mechanical\ - transformation or translation of a Source form, including but\ - not limited to compiled object code, generated documentation,\ - and conversions to other media types.\ -\ - "Work" shall mean the work of authorship, whether in Source or\ - Object form, made available under the License, as indicated by a\ - copyright notice that is included in or attached to the work\ - (an example is provided in the Appendix below).\ -\ - "Derivative Works" shall mean any work, whether in Source or Object\ - form, that is based on (or derived from) the Work and for which the\ - editorial revisions, annotations, elaborations, or other modifications\ - represent, as a whole, an original work of authorship. For the purposes\ - of this License, Derivative Works shall not include works that remain\ - separable from, or merely link (or bind by name) to the interfaces of,\ - the Work and Derivative Works thereof.\ -\ - "Contribution" shall mean any work of authorship, including\ - the original version of the Work and any modifications or additions\ - to that Work or Derivative Works thereof, that is intentionally\ - submitted to Licensor for inclusion in the Work by the copyright owner\ - or by an individual or Legal Entity authorized to submit on behalf of\ - the copyright owner. For the purposes of this definition, "submitted"\ - means any form of electronic, verbal, or written communication sent\ - to the Licensor or its representatives, including but not limited to\ - communication on electronic mailing lists, source code control systems,\ - and issue tracking systems that are managed by, or on behalf of, the\ - Licensor for the purpose of discussing and improving the Work, but\ - excluding communication that is conspicuously marked or otherwise\ - designated in writing by the copyright owner as "Not a Contribution."\ -\ - "Contributor" shall mean Licensor and any individual or Legal Entity\ - on behalf of whom a Contribution has been received by Licensor and\ - subsequently incorporated within the Work.\ -\ - 2. Grant of Copyright License. Subject to the terms and conditions of\ - this License, each Contributor hereby grants to You a perpetual,\ - worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ - copyright license to reproduce, prepare Derivative Works of,\ - publicly display, publicly perform, sublicense, and distribute the\ - Work and such Derivative Works in Source or Object form.\ -\ - 3. Grant of Patent License. Subject to the terms and conditions of\ - this License, each Contributor hereby grants to You a perpetual,\ - worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ - (except as stated in this section) patent license to make, have made,\ - use, offer to sell, sell, import, and otherwise transfer the Work,\ - where such license applies only to those patent claims licensable\ - by such Contributor that are necessarily infringed by their\ - Contribution(s) alone or by combination of their Contribution(s)\ - with the Work to which such Contribution(s) was submitted. If You\ - institute patent litigation against any entity (including a\ - cross-claim or counterclaim in a lawsuit) alleging that the Work\ - or a Contribution incorporated within the Work constitutes direct\ - or contributory patent infringement, then any patent licenses\ - granted to You under this License for that Work shall terminate\ - as of the date such litigation is filed.\ -\ - 4. Redistribution. You may reproduce and distribute copies of the\ - Work or Derivative Works thereof in any medium, with or without\ - modifications, and in Source or Object form, provided that You\ - meet the following conditions:\ -\ - (a) You must give any other recipients of the Work or\ - Derivative Works a copy of this License; and\ -\ - (b) You must cause any modified files to carry prominent notices\ - stating that You changed the files; and\ -\ - (c) You must retain, in the Source form of any Derivative Works\ - that You distribute, all copyright, patent, trademark, and\ - attribution notices from the Source form of the Work,\ - excluding those notices that do not pertain to any part of\ - the Derivative Works; and\ -\ - (d) If the Work includes a "NOTICE" text file as part of its\ - distribution, then any Derivative Works that You distribute must\ - include a readable copy of the attribution notices contained\ - within such NOTICE file, excluding those notices that do not\ - pertain to any part of the Derivative Works, in at least one\ - of the following places: within a NOTICE text file distributed\ - as part of the Derivative Works; within the Source form or\ - documentation, if provided along with the Derivative Works; or,\ - within a display generated by the Derivative Works, if and\ - wherever such third-party notices normally appear. The contents\ - of the NOTICE file are for informational purposes only and\ - do not modify the License. You may add Your own attribution\ - notices within Derivative Works that You distribute, alongside\ - or as an addendum to the NOTICE text from the Work, provided\ - that such additional attribution notices cannot be construed\ - as modifying the License.\ -\ - You may add Your own copyright statement to Your modifications and\ - may provide additional or different license terms and conditions\ - for use, reproduction, or distribution of Your modifications, or\ - for any such Derivative Works as a whole, provided Your use,\ - reproduction, and distribution of the Work otherwise complies with\ - the conditions stated in this License.\ -\ - 5. Submission of Contributions. Unless You explicitly state otherwise,\ - any Contribution intentionally submitted for inclusion in the Work\ - by You to the Licensor shall be under the terms and conditions of\ - this License, without any additional terms or conditions.\ - Notwithstanding the above, nothing herein shall supersede or modify\ - the terms of any separate license agreement you may have executed\ - with Licensor regarding such Contributions.\ -\ - 6. Trademarks. This License does not grant permission to use the trade\ - names, trademarks, service marks, or product names of the Licensor,\ - except as required for reasonable and customary use in describing the\ - origin of the Work and reproducing the content of the NOTICE file.\ -\ - 7. Disclaimer of Warranty. Unless required by applicable law or\ - agreed to in writing, Licensor provides the Work (and each\ - Contributor provides its Contributions) on an "AS IS" BASIS,\ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\ - implied, including, without limitation, any warranties or conditions\ - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\ - PARTICULAR PURPOSE. You are solely responsible for determining the\ - appropriateness of using or redistributing the Work and assume any\ - risks associated with Your exercise of permissions under this License.\ -\ - 8. Limitation of Liability. In no event and under no legal theory,\ - whether in tort (including negligence), contract, or otherwise,\ - unless required by applicable law (such as deliberate and grossly\ - negligent acts) or agreed to in writing, shall any Contributor be\ - liable to You for damages, including any direct, indirect, special,\ - incidental, or consequential damages of any character arising as a\ - result of this License or out of the use or inability to use the\ - Work (including but not limited to damages for loss of goodwill,\ - work stoppage, computer failure or malfunction, or any and all\ - other commercial damages or losses), even if such Contributor\ - has been advised of the possibility of such damages.\ -\ - 9. Accepting Warranty or Additional Liability. While redistributing\ - the Work or Derivative Works thereof, You may choose to offer,\ - and charge a fee for, acceptance of support, warranty, indemnity,\ - or other liability obligations and/or rights consistent with this\ - License. However, in accepting such obligations, You may act only\ - on Your own behalf and on Your sole responsibility, not on behalf\ - of any other Contributor, and only if You agree to indemnify,\ - defend, and hold each Contributor harmless for any liability\ - incurred by, or claims asserted against, such Contributor by reason\ - of your accepting any such warranty or additional liability.\ -\ - END OF TERMS AND CONDITIONS\ -\ - APPENDIX: How to apply the Apache License to your work.\ -\ - To apply the Apache License to your work, attach the following\ - boilerplate notice, with the fields enclosed by brackets "[]"\ - replaced with your own identifying information. (Don't include\ - the brackets!) The text should be enclosed in the appropriate\ - comment syntax for the file format. We also recommend that a\ - file or class name and description of purpose be included on the\ - same "printed page" as the copyright notice for easier\ - identification within third-party archives.\ -\ - Copyright [yyyy] [name of copyright owner]\ -\ - Licensed under the Apache License, Version 2.0 (the "License");\ - you may not use this file except in compliance with the License.\ - You may obtain a copy of the License at\ -\ - http://www.apache.org/licenses/LICENSE-2.0\ -\ - Unless required by applicable law or agreed to in writing, software\ - distributed under the License is distributed on an "AS IS" BASIS,\ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\ - See the License for the specific language governing permissions and\ - limitations under the License.\ -\ -\ -Dependency: github.com/mattgallagher/CwlCatchException\ -Version: 2.2.0\ -License Content:\ -ISC License\ -\ -Copyright \'a9 2017 Matt Gallagher ( http://cocoawithlove.com ). All rights reserved.\ -\ -Permission to use, copy, modify, and/or distribute this software for any\ -purpose with or without fee is hereby granted, provided that the above\ -copyright notice and this permission notice appear in all copies.\ -\ -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\ -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\ -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\ -SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\ -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\ -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR\ -IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\ -\ -\ -Dependency: github.com/mattgallagher/CwlPreconditionTesting\ -Version: 2.2.1\ -License Content:\ -ISC License\ -\ -Copyright \'a9 2017 Matt Gallagher ( http://cocoawithlove.com ). All rights reserved.\ -\ -Permission to use, copy, modify, and/or distribute this software for any\ -purpose with or without fee is hereby granted, provided that the above\ -copyright notice and this permission notice appear in all copies.\ -\ -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\ -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\ -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\ -SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\ -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\ -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR\ -IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\ -\ -\ -Dependency: github.com/Quick/Nimble\ -Version: 9.2.1\ -License Content:\ -Apache License\ - Version 2.0, January 2004\ - http://www.apache.org/licenses/\ -\ - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\ -\ - 1. Definitions.\ -\ - "License" shall mean the terms and conditions for use, reproduction,\ - and distribution as defined by Sections 1 through 9 of this document.\ -\ - "Licensor" shall mean the copyright owner or entity authorized by\ - the copyright owner that is granting the License.\ -\ - "Legal Entity" shall mean the union of the acting entity and all\ - other entities that control, are controlled by, or are under common\ - control with that entity. For the purposes of this definition,\ - "control" means (i) the power, direct or indirect, to cause the\ - direction or management of such entity, whether by contract or\ - otherwise, or (ii) ownership of fifty percent (50%) or more of the\ - outstanding shares, or (iii) beneficial ownership of such entity.\ -\ - "You" (or "Your") shall mean an individual or Legal Entity\ - exercising permissions granted by this License.\ -\ - "Source" form shall mean the preferred form for making modifications,\ - including but not limited to software source code, documentation\ - source, and configuration files.\ -\ - "Object" form shall mean any form resulting from mechanical\ - transformation or translation of a Source form, including but\ - not limited to compiled object code, generated documentation,\ - and conversions to other media types.\ -\ - "Work" shall mean the work of authorship, whether in Source or\ - Object form, made available under the License, as indicated by a\ - copyright notice that is included in or attached to the work\ - (an example is provided in the Appendix below).\ -\ - "Derivative Works" shall mean any work, whether in Source or Object\ - form, that is based on (or derived from) the Work and for which the\ - editorial revisions, annotations, elaborations, or other modifications\ - represent, as a whole, an original work of authorship. For the purposes\ - of this License, Derivative Works shall not include works that remain\ - separable from, or merely link (or bind by name) to the interfaces of,\ - the Work and Derivative Works thereof.\ -\ - "Contribution" shall mean any work of authorship, including\ - the original version of the Work and any modifications or additions\ - to that Work or Derivative Works thereof, that is intentionally\ - submitted to Licensor for inclusion in the Work by the copyright owner\ - or by an individual or Legal Entity authorized to submit on behalf of\ - the copyright owner. For the purposes of this definition, "submitted"\ - means any form of electronic, verbal, or written communication sent\ - to the Licensor or its representatives, including but not limited to\ - communication on electronic mailing lists, source code control systems,\ - and issue tracking systems that are managed by, or on behalf of, the\ - Licensor for the purpose of discussing and improving the Work, but\ - excluding communication that is conspicuously marked or otherwise\ - designated in writing by the copyright owner as "Not a Contribution."\ -\ - "Contributor" shall mean Licensor and any individual or Legal Entity\ - on behalf of whom a Contribution has been received by Licensor and\ - subsequently incorporated within the Work.\ -\ - 2. Grant of Copyright License. Subject to the terms and conditions of\ - this License, each Contributor hereby grants to You a perpetual,\ - worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ - copyright license to reproduce, prepare Derivative Works of,\ - publicly display, publicly perform, sublicense, and distribute the\ - Work and such Derivative Works in Source or Object form.\ -\ - 3. Grant of Patent License. Subject to the terms and conditions of\ - this License, each Contributor hereby grants to You a perpetual,\ - worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ - (except as stated in this section) patent license to make, have made,\ - use, offer to sell, sell, import, and otherwise transfer the Work,\ - where such license applies only to those patent claims licensable\ - by such Contributor that are necessarily infringed by their\ - Contribution(s) alone or by combination of their Contribution(s)\ - with the Work to which such Contribution(s) was submitted. If You\ - institute patent litigation against any entity (including a\ - cross-claim or counterclaim in a lawsuit) alleging that the Work\ - or a Contribution incorporated within the Work constitutes direct\ - or contributory patent infringement, then any patent licenses\ - granted to You under this License for that Work shall terminate\ - as of the date such litigation is filed.\ -\ - 4. Redistribution. You may reproduce and distribute copies of the\ - Work or Derivative Works thereof in any medium, with or without\ - modifications, and in Source or Object form, provided that You\ - meet the following conditions:\ -\ - (a) You must give any other recipients of the Work or\ - Derivative Works a copy of this License; and\ -\ - (b) You must cause any modified files to carry prominent notices\ - stating that You changed the files; and\ -\ - (c) You must retain, in the Source form of any Derivative Works\ - that You distribute, all copyright, patent, trademark, and\ - attribution notices from the Source form of the Work,\ - excluding those notices that do not pertain to any part of\ - the Derivative Works; and\ -\ - (d) If the Work includes a "NOTICE" text file as part of its\ - distribution, then any Derivative Works that You distribute must\ - include a readable copy of the attribution notices contained\ - within such NOTICE file, excluding those notices that do not\ - pertain to any part of the Derivative Works, in at least one\ - of the following places: within a NOTICE text file distributed\ - as part of the Derivative Works; within the Source form or\ - documentation, if provided along with the Derivative Works; or,\ - within a display generated by the Derivative Works, if and\ - wherever such third-party notices normally appear. The contents\ - of the NOTICE file are for informational purposes only and\ - do not modify the License. You may add Your own attribution\ - notices within Derivative Works that You distribute, alongside\ - or as an addendum to the NOTICE text from the Work, provided\ - that such additional attribution notices cannot be construed\ - as modifying the License.\ -\ - You may add Your own copyright statement to Your modifications and\ - may provide additional or different license terms and conditions\ - for use, reproduction, or distribution of Your modifications, or\ - for any such Derivative Works as a whole, provided Your use,\ - reproduction, and distribution of the Work otherwise complies with\ - the conditions stated in this License.\ -\ - 5. Submission of Contributions. Unless You explicitly state otherwise,\ - any Contribution intentionally submitted for inclusion in the Work\ - by You to the Licensor shall be under the terms and conditions of\ - this License, without any additional terms or conditions.\ - Notwithstanding the above, nothing herein shall supersede or modify\ - the terms of any separate license agreement you may have executed\ - with Licensor regarding such Contributions.\ -\ - 6. Trademarks. This License does not grant permission to use the trade\ - names, trademarks, service marks, or product names of the Licensor,\ - except as required for reasonable and customary use in describing the\ - origin of the Work and reproducing the content of the NOTICE file.\ -\ - 7. Disclaimer of Warranty. Unless required by applicable law or\ - agreed to in writing, Licensor provides the Work (and each\ - Contributor provides its Contributions) on an "AS IS" BASIS,\ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\ - implied, including, without limitation, any warranties or conditions\ - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\ - PARTICULAR PURPOSE. You are solely responsible for determining the\ - appropriateness of using or redistributing the Work and assume any\ - risks associated with Your exercise of permissions under this License.\ -\ - 8. Limitation of Liability. In no event and under no legal theory,\ - whether in tort (including negligence), contract, or otherwise,\ - unless required by applicable law (such as deliberate and grossly\ - negligent acts) or agreed to in writing, shall any Contributor be\ - liable to You for damages, including any direct, indirect, special,\ - incidental, or consequential damages of any character arising as a\ - result of this License or out of the use or inability to use the\ - Work (including but not limited to damages for loss of goodwill,\ - work stoppage, computer failure or malfunction, or any and all\ - other commercial damages or losses), even if such Contributor\ - has been advised of the possibility of such damages.\ -\ - 9. Accepting Warranty or Additional Liability. While redistributing\ - the Work or Derivative Works thereof, You may choose to offer,\ - and charge a fee for, acceptance of support, warranty, indemnity,\ - or other liability obligations and/or rights consistent with this\ - License. However, in accepting such obligations, You may act only\ - on Your own behalf and on Your sole responsibility, not on behalf\ - of any other Contributor, and only if You agree to indemnify,\ - defend, and hold each Contributor harmless for any liability\ - incurred by, or claims asserted against, such Contributor by reason\ - of your accepting any such warranty or additional liability.\ -\ - END OF TERMS AND CONDITIONS\ -\ - APPENDIX: How to apply the Apache License to your work.\ -\ - To apply the Apache License to your work, attach the following\ - boilerplate notice, with the fields enclosed by brackets "\{\}"\ - replaced with your own identifying information. (Don't include\ - the brackets!) The text should be enclosed in the appropriate\ - comment syntax for the file format. We also recommend that a\ - file or class name and description of purpose be included on the\ - same "printed page" as the copyright notice for easier\ - identification within third-party archives.\ -\ - Copyright 2016 Quick Team\ -\ - Licensed under the Apache License, Version 2.0 (the "License");\ - you may not use this file except in compliance with the License.\ - You may obtain a copy of the License at\ -\ - http://www.apache.org/licenses/LICENSE-2.0\ -\ - Unless required by applicable law or agreed to in writing, software\ - distributed under the License is distributed on an "AS IS" BASIS,\ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\ - See the License for the specific language governing permissions and\ - limitations under the License.\ -\ -\ -Dependency: github.com/Quick/Quick\ -Version: 3.1.2\ -License Content:\ -Apache License\ - Version 2.0, January 2004\ - http://www.apache.org/licenses/\ -\ - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\ -\ - 1. Definitions.\ -\ - "License" shall mean the terms and conditions for use, reproduction,\ - and distribution as defined by Sections 1 through 9 of this document.\ -\ - "Licensor" shall mean the copyright owner or entity authorized by\ - the copyright owner that is granting the License.\ -\ - "Legal Entity" shall mean the union of the acting entity and all\ - other entities that control, are controlled by, or are under common\ - control with that entity. For the purposes of this definition,\ - "control" means (i) the power, direct or indirect, to cause the\ - direction or management of such entity, whether by contract or\ - otherwise, or (ii) ownership of fifty percent (50%) or more of the\ - outstanding shares, or (iii) beneficial ownership of such entity.\ -\ - "You" (or "Your") shall mean an individual or Legal Entity\ - exercising permissions granted by this License.\ -\ - "Source" form shall mean the preferred form for making modifications,\ - including but not limited to software source code, documentation\ - source, and configuration files.\ -\ - "Object" form shall mean any form resulting from mechanical\ - transformation or translation of a Source form, including but\ - not limited to compiled object code, generated documentation,\ - and conversions to other media types.\ -\ - "Work" shall mean the work of authorship, whether in Source or\ - Object form, made available under the License, as indicated by a\ - copyright notice that is included in or attached to the work\ - (an example is provided in the Appendix below).\ -\ - "Derivative Works" shall mean any work, whether in Source or Object\ - form, that is based on (or derived from) the Work and for which the\ - editorial revisions, annotations, elaborations, or other modifications\ - represent, as a whole, an original work of authorship. For the purposes\ - of this License, Derivative Works shall not include works that remain\ - separable from, or merely link (or bind by name) to the interfaces of,\ - the Work and Derivative Works thereof.\ -\ - "Contribution" shall mean any work of authorship, including\ - the original version of the Work and any modifications or additions\ - to that Work or Derivative Works thereof, that is intentionally\ - submitted to Licensor for inclusion in the Work by the copyright owner\ - or by an individual or Legal Entity authorized to submit on behalf of\ - the copyright owner. For the purposes of this definition, "submitted"\ - means any form of electronic, verbal, or written communication sent\ - to the Licensor or its representatives, including but not limited to\ - communication on electronic mailing lists, source code control systems,\ - and issue tracking systems that are managed by, or on behalf of, the\ - Licensor for the purpose of discussing and improving the Work, but\ - excluding communication that is conspicuously marked or otherwise\ - designated in writing by the copyright owner as "Not a Contribution."\ -\ - "Contributor" shall mean Licensor and any individual or Legal Entity\ - on behalf of whom a Contribution has been received by Licensor and\ - subsequently incorporated within the Work.\ -\ - 2. Grant of Copyright License. Subject to the terms and conditions of\ - this License, each Contributor hereby grants to You a perpetual,\ - worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ - copyright license to reproduce, prepare Derivative Works of,\ - publicly display, publicly perform, sublicense, and distribute the\ - Work and such Derivative Works in Source or Object form.\ -\ - 3. Grant of Patent License. Subject to the terms and conditions of\ - this License, each Contributor hereby grants to You a perpetual,\ - worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ - (except as stated in this section) patent license to make, have made,\ - use, offer to sell, sell, import, and otherwise transfer the Work,\ - where such license applies only to those patent claims licensable\ - by such Contributor that are necessarily infringed by their\ - Contribution(s) alone or by combination of their Contribution(s)\ - with the Work to which such Contribution(s) was submitted. If You\ - institute patent litigation against any entity (including a\ - cross-claim or counterclaim in a lawsuit) alleging that the Work\ - or a Contribution incorporated within the Work constitutes direct\ - or contributory patent infringement, then any patent licenses\ - granted to You under this License for that Work shall terminate\ - as of the date such litigation is filed.\ -\ - 4. Redistribution. You may reproduce and distribute copies of the\ - Work or Derivative Works thereof in any medium, with or without\ - modifications, and in Source or Object form, provided that You\ - meet the following conditions:\ -\ - (a) You must give any other recipients of the Work or\ - Derivative Works a copy of this License; and\ -\ - (b) You must cause any modified files to carry prominent notices\ - stating that You changed the files; and\ -\ - (c) You must retain, in the Source form of any Derivative Works\ - that You distribute, all copyright, patent, trademark, and\ - attribution notices from the Source form of the Work,\ - excluding those notices that do not pertain to any part of\ - the Derivative Works; and\ -\ - (d) If the Work includes a "NOTICE" text file as part of its\ - distribution, then any Derivative Works that You distribute must\ - include a readable copy of the attribution notices contained\ - within such NOTICE file, excluding those notices that do not\ - pertain to any part of the Derivative Works, in at least one\ - of the following places: within a NOTICE text file distributed\ - as part of the Derivative Works; within the Source form or\ - documentation, if provided along with the Derivative Works; or,\ - within a display generated by the Derivative Works, if and\ - wherever such third-party notices normally appear. The contents\ - of the NOTICE file are for informational purposes only and\ - do not modify the License. You may add Your own attribution\ - notices within Derivative Works that You distribute, alongside\ - or as an addendum to the NOTICE text from the Work, provided\ - that such additional attribution notices cannot be construed\ - as modifying the License.\ -\ - You may add Your own copyright statement to Your modifications and\ - may provide additional or different license terms and conditions\ - for use, reproduction, or distribution of Your modifications, or\ - for any such Derivative Works as a whole, provided Your use,\ - reproduction, and distribution of the Work otherwise complies with\ - the conditions stated in this License.\ -\ - 5. Submission of Contributions. Unless You explicitly state otherwise,\ - any Contribution intentionally submitted for inclusion in the Work\ - by You to the Licensor shall be under the terms and conditions of\ - this License, without any additional terms or conditions.\ - Notwithstanding the above, nothing herein shall supersede or modify\ - the terms of any separate license agreement you may have executed\ - with Licensor regarding such Contributions.\ -\ - 6. Trademarks. This License does not grant permission to use the trade\ - names, trademarks, service marks, or product names of the Licensor,\ - except as required for reasonable and customary use in describing the\ - origin of the Work and reproducing the content of the NOTICE file.\ -\ - 7. Disclaimer of Warranty. Unless required by applicable law or\ - agreed to in writing, Licensor provides the Work (and each\ - Contributor provides its Contributions) on an "AS IS" BASIS,\ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\ - implied, including, without limitation, any warranties or conditions\ - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\ - PARTICULAR PURPOSE. You are solely responsible for determining the\ - appropriateness of using or redistributing the Work and assume any\ - risks associated with Your exercise of permissions under this License.\ -\ - 8. Limitation of Liability. In no event and under no legal theory,\ - whether in tort (including negligence), contract, or otherwise,\ - unless required by applicable law (such as deliberate and grossly\ - negligent acts) or agreed to in writing, shall any Contributor be\ - liable to You for damages, including any direct, indirect, special,\ - incidental, or consequential damages of any character arising as a\ - result of this License or out of the use or inability to use the\ - Work (including but not limited to damages for loss of goodwill,\ - work stoppage, computer failure or malfunction, or any and all\ - other commercial damages or losses), even if such Contributor\ - has been advised of the possibility of such damages.\ -\ - 9. Accepting Warranty or Additional Liability. While redistributing\ - the Work or Derivative Works thereof, You may choose to offer,\ - and charge a fee for, acceptance of support, warranty, indemnity,\ - or other liability obligations and/or rights consistent with this\ - License. However, in accepting such obligations, You may act only\ - on Your own behalf and on Your sole responsibility, not on behalf\ - of any other Contributor, and only if You agree to indemnify,\ - defend, and hold each Contributor harmless for any liability\ - incurred by, or claims asserted against, such Contributor by reason\ - of your accepting any such warranty or additional liability.\ -\ - END OF TERMS AND CONDITIONS\ -\ - APPENDIX: How to apply the Apache License to your work.\ -\ - To apply the Apache License to your work, attach the following\ - boilerplate notice, with the fields enclosed by brackets "\{\}"\ - replaced with your own identifying information. (Don't include\ - the brackets!) The text should be enclosed in the appropriate\ - comment syntax for the file format. We also recommend that a\ - file or class name and description of purpose be included on the\ - same "printed page" as the copyright notice for easier\ - identification within third-party archives.\ -\ - Copyright 2014, Quick Team\ -\ - Licensed under the Apache License, Version 2.0 (the "License");\ - you may not use this file except in compliance with the License.\ - You may obtain a copy of the License at\ -\ - http://www.apache.org/licenses/LICENSE-2.0\ -\ - Unless required by applicable law or agreed to in writing, software\ - distributed under the License is distributed on an "AS IS" BASIS,\ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\ - See the License for the specific language governing permissions and\ - limitations under the License.\ -\ -\ -Dependency: github.com/devm33/CGEventOverride\ -Version: 1.2.3\ -License Content:\ -MIT License\ -\ -Copyright (c) 2024 Shangxin Guo \ -\ -Permission is hereby granted, free of charge, to any person obtaining a copy\ -of this software and associated documentation files (the "Software"), to deal\ -in the Software without restriction, including without limitation the rights\ -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ -copies of the Software, and to permit persons to whom the Software is\ -furnished to do so, subject to the following conditions:\ -\ -The above copyright notice and this permission notice shall be included in all\ -copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ -SOFTWARE.\ -\ -\ -Dependency: github.com/devm33/CopilotForXcodeKit\ -Version: 1f98fe9795766d3e37b5ae3d2e5f69f9b0af308b\ -License Content:\ -MIT License\ -\ -Copyright (c) 2023 Shangxin Guo\ -\ -Permission is hereby granted, free of charge, to any person obtaining a copy\ -of this software and associated documentation files (the "Software"), to deal\ -in the Software without restriction, including without limitation the rights\ -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ -copies of the Software, and to permit persons to whom the Software is\ -furnished to do so, subject to the following conditions:\ -\ -The above copyright notice and this permission notice shall be included in all\ -copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ -SOFTWARE.\ -\ -\ -Dependency: github.com/devm33/Highlightr\ -Version: 81d8c8b3733939bf5d9e52cd6318f944cc033bd2\ -License Content:\ -Copyright (c) 2016 Illanes, Juan Pablo \ -\ -Permission is hereby granted, free of charge, to any person obtaining a copy\ -of this software and associated documentation files (the "Software"), to deal\ -in the Software without restriction, including without limitation the rights\ -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ -copies of the Software, and to permit persons to whom the Software is\ -furnished to do so, subject to the following conditions:\ -\ -The above copyright notice and this permission notice shall be included in\ -all copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\ -THE SOFTWARE.\ -\ -\ -Dependency: github.com/devm33/KeyboardShortcuts\ -Version: 65fb410b0c6d3ed96623b460bab31ffce5f48b4d\ -License Content:\ -MIT License\ -\ -Copyright (c) Sindre Sorhus (https://sindresorhus.com)\ -\ -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\ -\ -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\ -\ -\ -Dependency: github.com/ChimeHQ/LanguageClient\ -Version: 0.8.2\ -License Content:\ -BSD 3-Clause License\ -\ -Copyright (c) 2021, Chime\ -All rights reserved.\ -\ -Redistribution and use in source and binary forms, with or without\ -modification, are permitted provided that the following conditions are met:\ -\ -1. Redistributions of source code must retain the above copyright notice, this\ - list of conditions and the following disclaimer.\ -\ -2. Redistributions in binary form must reproduce the above copyright notice,\ - this list of conditions and the following disclaimer in the documentation\ - and/or other materials provided with the distribution.\ -\ -3. Neither the name of the copyright holder nor the names of its\ - contributors may be used to endorse or promote products derived from\ - this software without specific prior written permission.\ -\ -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\ -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\ -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\ -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\ -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\ -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\ -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\ -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\ -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\ -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\ -\ -\ -Dependency: github.com/ChimeHQ/ProcessEnv\ -Version: 1.0.1\ -License Content:\ -BSD 3-Clause License\ -\ -Copyright (c) 2020, Chime\ -All rights reserved.\ -\ -Redistribution and use in source and binary forms, with or without\ -modification, are permitted provided that the following conditions are met:\ -\ -1. Redistributions of source code must retain the above copyright notice, this\ - list of conditions and the following disclaimer.\ -\ -2. Redistributions in binary form must reproduce the above copyright notice,\ - this list of conditions and the following disclaimer in the documentation\ - and/or other materials provided with the distribution.\ -\ -3. Neither the name of the copyright holder nor the names of its\ - contributors may be used to endorse or promote products derived from\ - this software without specific prior written permission.\ -\ -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\ -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\ -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\ -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\ -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\ -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\ -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\ -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\ -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\ -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\ -\ -\ -Dependency: github.com/sparkle-project/Sparkle\ -Version: 2.4.2\ -License Content:\ -Copyright (c) 2006-2013 Andy Matuschak.\ -Copyright (c) 2009-2013 Elgato Systems GmbH.\ -Copyright (c) 2011-2014 Kornel Lesi\uc0\u324 ski.\ -Copyright (c) 2015-2017 Mayur Pawashe.\ -Copyright (c) 2014 C.W. Betts.\ -Copyright (c) 2014 Petroules Corporation.\ -Copyright (c) 2014 Big Nerd Ranch.\ -All rights reserved.\ -\ -Permission is hereby granted, free of charge, to any person obtaining a copy of\ -this software and associated documentation files (the "Software"), to deal in\ -the Software without restriction, including without limitation the rights to\ -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\ -the Software, and to permit persons to whom the Software is furnished to do so,\ -subject to the following conditions:\ -\ -The above copyright notice and this permission notice shall be included in all\ -copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\ -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\ -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\ -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\ -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\ -\ -=================\ -EXTERNAL LICENSES\ -=================\ -\ -bspatch.c and bsdiff.c, from bsdiff 4.3 :\ -\ -Copyright 2003-2005 Colin Percival\ -All rights reserved\ -\ -Redistribution and use in source and binary forms, with or without\ -modification, are permitted providing that the following conditions \ -are met:\ -1. Redistributions of source code must retain the above copyright\ - notice, this list of conditions and the following disclaimer.\ -2. Redistributions in binary form must reproduce the above copyright\ - notice, this list of conditions and the following disclaimer in the\ - documentation and/or other materials provided with the distribution.\ -\ -THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\ -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\ -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\ -ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\ -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\ -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\ -OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\ -HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\ -STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING\ -IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\ -POSSIBILITY OF SUCH DAMAGE.\ -\ ---\ -\ -sais.c and sais.c, from sais-lite (2010/08/07) :\ -\ -The sais-lite copyright is as follows:\ -\ -Copyright (c) 2008-2010 Yuta Mori All Rights Reserved.\ -\ -Permission is hereby granted, free of charge, to any person\ -obtaining a copy of this software and associated documentation\ -files (the "Software"), to deal in the Software without\ -restriction, including without limitation the rights to use,\ -copy, modify, merge, publish, distribute, sublicense, and/or sell\ -copies of the Software, and to permit persons to whom the\ -Software is furnished to do so, subject to the following\ -conditions:\ -\ -The above copyright notice and this permission notice shall be\ -included in all copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\ -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\ -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\ -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\ -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\ -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\ -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\ -OTHER DEALINGS IN THE SOFTWARE.\ -\ ---\ -\ -Portable C implementation of Ed25519, from https://github.com/orlp/ed25519\ -\ -Copyright (c) 2015 Orson Peters \ -\ -This software is provided 'as-is', without any express or implied warranty. In no event will the\ -authors be held liable for any damages arising from the use of this software.\ -\ -Permission is granted to anyone to use this software for any purpose, including commercial\ -applications, and to alter it and redistribute it freely, subject to the following restrictions:\ -\ -1. The origin of this software must not be misrepresented; you must not claim that you wrote the\ - original software. If you use this software in a product, an acknowledgment in the product\ - documentation would be appreciated but is not required.\ -\ -2. Altered source versions must be plainly marked as such, and must not be misrepresented as\ - being the original software.\ -\ -3. This notice may not be removed or altered from any source distribution.\ -\ ---\ -\ -SUSignatureVerifier.m:\ -\ -Copyright (c) 2011 Mark Hamlin.\ -\ -All rights reserved.\ -\ -Redistribution and use in source and binary forms, with or without\ -modification, are permitted providing that the following conditions\ -are met:\ -1. Redistributions of source code must retain the above copyright\ - notice, this list of conditions and the following disclaimer.\ -2. Redistributions in binary form must reproduce the above copyright\ - notice, this list of conditions and the following disclaimer in the\ - documentation and/or other materials provided with the distribution.\ -\ -THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\ -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\ -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\ -ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\ -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\ -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\ -OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\ -HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\ -STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING\ -IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\ -POSSIBILITY OF SUCH DAMAGE.\ -\ -\ -Dependency: github.com/apple/swift-async-algorithms\ -Version: 1.0.0\ -License Content:\ - Apache License\ - Version 2.0, January 2004\ - http://www.apache.org/licenses/\ -\ - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\ -\ - 1. Definitions.\ -\ - "License" shall mean the terms and conditions for use, reproduction,\ - and distribution as defined by Sections 1 through 9 of this document.\ -\ - "Licensor" shall mean the copyright owner or entity authorized by\ - the copyright owner that is granting the License.\ -\ - "Legal Entity" shall mean the union of the acting entity and all\ - other entities that control, are controlled by, or are under common\ - control with that entity. For the purposes of this definition,\ - "control" means (i) the power, direct or indirect, to cause the\ - direction or management of such entity, whether by contract or\ - otherwise, or (ii) ownership of fifty percent (50%) or more of the\ - outstanding shares, or (iii) beneficial ownership of such entity.\ -\ - "You" (or "Your") shall mean an individual or Legal Entity\ - exercising permissions granted by this License.\ -\ - "Source" form shall mean the preferred form for making modifications,\ - including but not limited to software source code, documentation\ - source, and configuration files.\ -\ - "Object" form shall mean any form resulting from mechanical\ - transformation or translation of a Source form, including but\ - not limited to compiled object code, generated documentation,\ - and conversions to other media types.\ -\ - "Work" shall mean the work of authorship, whether in Source or\ - Object form, made available under the License, as indicated by a\ - copyright notice that is included in or attached to the work\ - (an example is provided in the Appendix below).\ -\ - "Derivative Works" shall mean any work, whether in Source or Object\ - form, that is based on (or derived from) the Work and for which the\ - editorial revisions, annotations, elaborations, or other modifications\ - represent, as a whole, an original work of authorship. For the purposes\ - of this License, Derivative Works shall not include works that remain\ - separable from, or merely link (or bind by name) to the interfaces of,\ - the Work and Derivative Works thereof.\ -\ - "Contribution" shall mean any work of authorship, including\ - the original version of the Work and any modifications or additions\ - to that Work or Derivative Works thereof, that is intentionally\ - submitted to Licensor for inclusion in the Work by the copyright owner\ - or by an individual or Legal Entity authorized to submit on behalf of\ - the copyright owner. For the purposes of this definition, "submitted"\ - means any form of electronic, verbal, or written communication sent\ - to the Licensor or its representatives, including but not limited to\ - communication on electronic mailing lists, source code control systems,\ - and issue tracking systems that are managed by, or on behalf of, the\ - Licensor for the purpose of discussing and improving the Work, but\ - excluding communication that is conspicuously marked or otherwise\ - designated in writing by the copyright owner as "Not a Contribution."\ -\ - "Contributor" shall mean Licensor and any individual or Legal Entity\ - on behalf of whom a Contribution has been received by Licensor and\ - subsequently incorporated within the Work.\ -\ - 2. Grant of Copyright License. Subject to the terms and conditions of\ - this License, each Contributor hereby grants to You a perpetual,\ - worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ - copyright license to reproduce, prepare Derivative Works of,\ - publicly display, publicly perform, sublicense, and distribute the\ - Work and such Derivative Works in Source or Object form.\ -\ - 3. Grant of Patent License. Subject to the terms and conditions of\ - this License, each Contributor hereby grants to You a perpetual,\ - worldwide, non-exclusive, no-charge, royalty-free, irrevocable\ - (except as stated in this section) patent license to make, have made,\ - use, offer to sell, sell, import, and otherwise transfer the Work,\ - where such license applies only to those patent claims licensable\ - by such Contributor that are necessarily infringed by their\ - Contribution(s) alone or by combination of their Contribution(s)\ - with the Work to which such Contribution(s) was submitted. If You\ - institute patent litigation against any entity (including a\ - cross-claim or counterclaim in a lawsuit) alleging that the Work\ - or a Contribution incorporated within the Work constitutes direct\ - or contributory patent infringement, then any patent licenses\ - granted to You under this License for that Work shall terminate\ - as of the date such litigation is filed.\ -\ - 4. Redistribution. You may reproduce and distribute copies of the\ - Work or Derivative Works thereof in any medium, with or without\ - modifications, and in Source or Object form, provided that You\ - meet the following conditions:\ -\ - (a) You must give any other recipients of the Work or\ - Derivative Works a copy of this License; and\ -\ - (b) You must cause any modified files to carry prominent notices\ - stating that You changed the files; and\ -\ - (c) You must retain, in the Source form of any Derivative Works\ - that You distribute, all copyright, patent, trademark, and\ - attribution notices from the Source form of the Work,\ - excluding those notices that do not pertain to any part of\ - the Derivative Works; and\ -\ - (d) If the Work includes a "NOTICE" text file as part of its\ - distribution, then any Derivative Works that You distribute must\ - include a readable copy of the attribution notices contained\ - within such NOTICE file, excluding those notices that do not\ - pertain to any part of the Derivative Works, in at least one\ - of the following places: within a NOTICE text file distributed\ - as part of the Derivative Works; within the Source form or\ - documentation, if provided along with the Derivative Works; or,\ - within a display generated by the Derivative Works, if and\ - wherever such third-party notices normally appear. The contents\ - of the NOTICE file are for informational purposes only and\ - do not modify the License. You may add Your own attribution\ - notices within Derivative Works that You distribute, alongside\ - or as an addendum to the NOTICE text from the Work, provided\ - that such additional attribution notices cannot be construed\ - as modifying the License.\ -\ - You may add Your own copyright statement to Your modifications and\ - may provide additional or different license terms and conditions\ - for use, reproduction, or distribution of Your modifications, or\ - for any such Derivative Works as a whole, provided Your use,\ - reproduction, and distribution of the Work otherwise complies with\ - the conditions stated in this License.\ -\ - 5. Submission of Contributions. Unless You explicitly state otherwise,\ - any Contribution intentionally submitted for inclusion in the Work\ - by You to the Licensor shall be under the terms and conditions of\ - this License, without any additional terms or conditions.\ - Notwithstanding the above, nothing herein shall supersede or modify\ - the terms of any separate license agreement you may have executed\ - with Licensor regarding such Contributions.\ -\ - 6. Trademarks. This License does not grant permission to use the trade\ - names, trademarks, service marks, or product names of the Licensor,\ - except as required for reasonable and customary use in describing the\ - origin of the Work and reproducing the content of the NOTICE file.\ -\ - 7. Disclaimer of Warranty. Unless required by applicable law or\ - agreed to in writing, Licensor provides the Work (and each\ - Contributor provides its Contributions) on an "AS IS" BASIS,\ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\ - implied, including, without limitation, any warranties or conditions\ - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\ - PARTICULAR PURPOSE. You are solely responsible for determining the\ - appropriateness of using or redistributing the Work and assume any\ - risks associated with Your exercise of permissions under this License.\ -\ - 8. Limitation of Liability. In no event and under no legal theory,\ - whether in tort (including negligence), contract, or otherwise,\ - unless required by applicable law (such as deliberate and grossly\ - negligent acts) or agreed to in writing, shall any Contributor be\ - liable to You for damages, including any direct, indirect, special,\ - incidental, or consequential damages of any character arising as a\ - result of this License or out of the use or inability to use the\ - Work (including but not limited to damages for loss of goodwill,\ - work stoppage, computer failure or malfunction, or any and all\ - other commercial damages or losses), even if such Contributor\ - has been advised of the possibility of such damages.\ -\ - 9. Accepting Warranty or Additional Liability. While redistributing\ - the Work or Derivative Works thereof, You may choose to offer,\ - and charge a fee for, acceptance of support, warranty, indemnity,\ - or other liability obligations and/or rights consistent with this\ - License. However, in accepting such obligations, You may act only\ - on Your own behalf and on Your sole responsibility, not on behalf\ - of any other Contributor, and only if You agree to indemnify,\ - defend, and hold each Contributor harmless for any liability\ - incurred by, or claims asserted against, such Contributor by reason\ - of your accepting any such warranty or additional liability.\ -\ - END OF TERMS AND CONDITIONS\ -\ - APPENDIX: How to apply the Apache License to your work.\ -\ - To apply the Apache License to your work, attach the following\ - boilerplate notice, with the fields enclosed by brackets "[]"\ - replaced with your own identifying information. (Don't include\ - the brackets!) The text should be enclosed in the appropriate\ - comment syntax for the file format. We also recommend that a\ - file or class name and description of purpose be included on the\ - same "printed page" as the copyright notice for easier\ - identification within third-party archives.\ -\ - Copyright [yyyy] [name of copyright owner]\ -\ - Licensed under the Apache License, Version 2.0 (the "License");\ - you may not use this file except in compliance with the License.\ - You may obtain a copy of the License at\ -\ - http://www.apache.org/licenses/LICENSE-2.0\ -\ - Unless required by applicable law or agreed to in writing, software\ - distributed under the License is distributed on an "AS IS" BASIS,\ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\ - See the License for the specific language governing permissions and\ - limitations under the License.\ -\ -\ -\ -## Runtime Library Exception to the Apache 2.0 License: ##\ -\ -\ - As an exception, if you use this Software to compile your source code and\ - portions of this Software are embedded into the binary product as a result,\ - you may redistribute such product without providing attribution as would\ - otherwise be required by Sections 4(a), 4(b) and 4(d) of the License.\ -\ -\ -Dependency: github.com/pointfreeco/swift-composable-architecture\ -Version: 1.10.4\ -License Content:\ -MIT License\ -\ -Copyright (c) 2020 Point-Free, Inc.\ -\ -Permission is hereby granted, free of charge, to any person obtaining a copy\ -of this software and associated documentation files (the "Software"), to deal\ -in the Software without restriction, including without limitation the rights\ -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ -copies of the Software, and to permit persons to whom the Software is\ -furnished to do so, subject to the following conditions:\ -\ -The above copyright notice and this permission notice shall be included in all\ -copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ -SOFTWARE.\ -\ -\ -Dependency: github.com/pointfreeco/swift-dependencies\ -Version: 1.3.0\ -License Content:\ -MIT License\ -\ -Copyright (c) 2022 Point-Free, Inc.\ -\ -Permission is hereby granted, free of charge, to any person obtaining a copy\ -of this software and associated documentation files (the "Software"), to deal\ -in the Software without restriction, including without limitation the rights\ -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ -copies of the Software, and to permit persons to whom the Software is\ -furnished to do so, subject to the following conditions:\ -\ -The above copyright notice and this permission notice shall be included in all\ -copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ -SOFTWARE.\ -\ -\ -Dependency: github.com/pointfreeco/swift-identified-collections\ -Version: 1.0.2\ -License Content:\ -MIT License\ -\ -Copyright (c) 2021 Point-Free, Inc.\ -\ -Permission is hereby granted, free of charge, to any person obtaining a copy\ -of this software and associated documentation files (the "Software"), to deal\ -in the Software without restriction, including without limitation the rights\ -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ -copies of the Software, and to permit persons to whom the Software is\ -furnished to do so, subject to the following conditions:\ -\ -The above copyright notice and this permission notice shall be included in all\ -copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ -SOFTWARE.\ -\ -\ -Dependency: github.com/gonzalezreal/swift-markdown-ui\ -Version: 2.4.0\ -License Content:\ -The MIT License (MIT)\ -\ -Copyright (c) 2020 Guillermo Gonzalez\ -\ -Permission is hereby granted, free of charge, to any person obtaining a copy\ -of this software and associated documentation files (the "Software"), to deal\ -in the Software without restriction, including without limitation the rights\ -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ -copies of the Software, and to permit persons to whom the Software is\ -furnished to do so, subject to the following conditions:\ -\ -The above copyright notice and this permission notice shall be included in all\ -copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ -SOFTWARE.\ -\ -\ -\ -Dependency: github.com/pointfreeco/swift-parsing\ -Version: 0.13.0\ -License Content:\ -MIT License\ -\ -Copyright (c) 2020 Point-Free, Inc.\ -\ -Permission is hereby granted, free of charge, to any person obtaining a copy\ -of this software and associated documentation files (the "Software"), to deal\ -in the Software without restriction, including without limitation the rights\ -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ -copies of the Software, and to permit persons to whom the Software is\ -furnished to do so, subject to the following conditions:\ -\ -The above copyright notice and this permission notice shall be included in all\ -copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ -SOFTWARE.\ -\ -\ -Dependency: github.com/pointfreeco/swift-perception\ -Version: 1.2.2\ -License Content:\ -MIT License\ -\ -Copyright (c) 2023 Point-Free\ -\ -Permission is hereby granted, free of charge, to any person obtaining a copy\ -of this software and associated documentation files (the "Software"), to deal\ -in the Software without restriction, including without limitation the rights\ -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ -copies of the Software, and to permit persons to whom the Software is\ -furnished to do so, subject to the following conditions:\ -\ -The above copyright notice and this permission notice shall be included in all\ -copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ -SOFTWARE.\ -\ -\ -Dependency: github.com/pointfreeco/swiftui-navigation\ -Version: 1.4.0\ -License Content:\ -MIT License\ -\ -Copyright (c) 2021 Point-Free, Inc.\ -\ -Permission is hereby granted, free of charge, to any person obtaining a copy\ -of this software and associated documentation files (the "Software"), to deal\ -in the Software without restriction, including without limitation the rights\ -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ -copies of the Software, and to permit persons to whom the Software is\ -furnished to do so, subject to the following conditions:\ -\ -The above copyright notice and this permission notice shall be included in all\ -copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ -SOFTWARE.\ -\ -\ -Dependency: github.com/globulus/swiftui-flow-layout\ -Version: 1.0.5\ -License Content:\ -MIT License\ -\ -Copyright (c) 2021 Gordan Glavaš\ -\ -Permission is hereby granted, free of charge, to any person obtaining a copy\ -of this software and associated documentation files (the "Software"), to deal\ -in the Software without restriction, including without limitation the rights\ -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ -copies of the Software, and to permit persons to whom the Software is\ -furnished to do so, subject to the following conditions:\ -\ -The above copyright notice and this permission notice shall be included in all\ -copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ -SOFTWARE.\ -\ -\ -Dependency: https://github.com/stephencelis/SQLite.swift\ -Version: 0.15.3\ -License Content:\ -MIT License\ -\ -Copyright (c) 2014-2015 Stephen Celis ()\ -\ -Permission is hereby granted, free of charge, to any person obtaining a copy\ -of this software and associated documentation files (the "Software"), to deal\ -in the Software without restriction, including without limitation the rights\ -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ -copies of the Software, and to permit persons to whom the Software is\ -furnished to do so, subject to the following conditions:\ -\ -The above copyright notice and this permission notice shall be included in all\ -copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ -SOFTWARE.\ -\ -\ -Dependency: https://github.com/microsoft/monaco-editor\ -Version: 0.52.2\ -License Content:\ -The MIT License (MIT)\ -\ -Copyright (c) 2016 - present Microsoft Corporation\ -\ -Permission is hereby granted, free of charge, to any person obtaining a copy\ -of this software and associated documentation files (the "Software"), to deal\ -in the Software without restriction, including without limitation the rights\ -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ -copies of the Software, and to permit persons to whom the Software is\ -furnished to do so, subject to the following conditions:\ -\ -The above copyright notice and this permission notice shall be included in all\ -copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ -SOFTWARE.\ -\ -\ -Dependency: https://github.com/xtermjs/xterm.js\ -Version: @xterm/addon-fit@0.10.0, @xterm/xterm@5.5.0\ -License Content:\ -The MIT License (MIT)\ -\ -Copyright (c) 2017-2019, The xterm.js authors (https://github.com/xtermjs/xterm.js)\ -Copyright (c) 2014-2016, SourceLair Private Company (https://www.sourcelair.com)\ -Copyright (c) 2012-2013, Christopher Jeffrey (https://github.com/chjj/)\ -\ -Permission is hereby granted, free of charge, to any person obtaining a copy\ -of this software and associated documentation files (the "Software"), to deal\ -in the Software without restriction, including without limitation the rights\ -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ -copies of the Software, and to permit persons to whom the Software is\ -furnished to do so, subject to the following conditions:\ -\ -The above copyright notice and this permission notice shall be included in\ -all copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\ -THE SOFTWARE.\ -\ -\ -Dependency: https://github.com/scinfu/SwiftSoup\ -Version: 2.9.6\ -License Content:\ -The MIT License\ -\ -Copyright (c) 2009-2025 Jonathan Hedley \ -Swift port copyright (c) 2016-2025 Nabil Chatbi\ -\ -Permission is hereby granted, free of charge, to any person obtaining a copy\ -of this software and associated documentation files (the "Software"), to deal\ -in the Software without restriction, including without limitation the rights\ -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ -copies of the Software, and to permit persons to whom the Software is\ -furnished to do so, subject to the following conditions:\ -\ -The above copyright notice and this permission notice shall be included in all\ -copies or substantial portions of the Software.\ -\ -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\ -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\ -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\ -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\ -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\ -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\ -SOFTWARE.\ -\ -\ -} \ No newline at end of file diff --git a/Copilot for Xcode/Preview Content/Preview Assets.xcassets/Contents.json b/Copilot for Xcode/Preview Content/Preview Assets.xcassets/Contents.json deleted file mode 100644 index 73c00596..00000000 --- a/Copilot for Xcode/Preview Content/Preview Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Copilot-for-Xcode-Info.plist b/Copilot-for-Xcode-Info.plist deleted file mode 100644 index 12d852d9..00000000 --- a/Copilot-for-Xcode-Info.plist +++ /dev/null @@ -1,34 +0,0 @@ - - - - - APP_ID_PREFIX - $(AppIdentifierPrefix) - APPLICATION_SUPPORT_FOLDER - $(APPLICATION_SUPPORT_FOLDER) - BUNDLE_IDENTIFIER_BASE - $(BUNDLE_IDENTIFIER_BASE) - EXTENSION_BUNDLE_NAME - $(EXTENSION_BUNDLE_NAME) - HOST_APP_NAME - $(HOST_APP_NAME) - LANGUAGE_SERVER_PATH - $(LANGUAGE_SERVER_PATH) - NODE_PATH - $(NODE_PATH) - SUEnableAutomaticChecks - YES - SUScheduledCheckInterval - 3600 - SUEnableJavaScript - NO - SUFeedURL - $(SPARKLE_FEED_URL) - SUPublicEDKey - $(SPARKLE_PUBLIC_KEY) - TEAM_ID_PREFIX - $(TeamIdentifierPrefix) - STANDARD_TELEMETRY_CHANNEL_KEY - $(STANDARD_TELEMETRY_CHANNEL_KEY) - - \ No newline at end of file diff --git a/Core/Package.swift b/Core/Package.swift deleted file mode 100644 index 966dcaab..00000000 --- a/Core/Package.swift +++ /dev/null @@ -1,302 +0,0 @@ -// swift-tools-version: 5.7 -// The swift-tools-version declares the minimum version of Swift required to build this package. - -import Foundation -import PackageDescription - -// MARK: - Package - -let package = Package( - name: "Core", - platforms: [.macOS(.v12)], - products: [ - .library( - name: "Service", - targets: [ - "Service", - "SuggestionInjector", - "FileChangeChecker", - "LaunchAgentManager", - "UpdateChecker", - ] - ), - .library( - name: "Client", - targets: [ - "Client", - ] - ), - .library( - name: "HostApp", - targets: [ - "HostApp", - "Client", - "LaunchAgentManager", - "UpdateChecker", - "GitHubCopilotViewModel", - ] - ), - ], - dependencies: [ - .package(path: "../Tool"), - .package(url: "https://github.com/apple/swift-async-algorithms", from: "1.0.0"), - .package(url: "https://github.com/gonzalezreal/swift-markdown-ui", from: "2.4.0"), - .package(url: "https://github.com/sparkle-project/Sparkle", from: "2.0.0"), - .package(url: "https://github.com/pointfreeco/swift-parsing", from: "0.12.1"), - .package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.0.0"), - .package( - url: "https://github.com/pointfreeco/swift-composable-architecture", - from: "1.10.4" - ), - // quick hack to support custom UserDefaults - // https://github.com/sindresorhus/KeyboardShortcuts - .package(url: "https://github.com/devm33/KeyboardShortcuts", branch: "main"), - .package(url: "https://github.com/devm33/CGEventOverride", branch: "devm33/fix-stale-AXIsProcessTrusted"), - .package(url: "https://github.com/devm33/Highlightr", branch: "master"), - .package(url: "https://github.com/globulus/swiftui-flow-layout", from: "1.0.5") - ], - targets: [ - // MARK: - Main - - .target( - name: "Client", - dependencies: [ - .product(name: "XPCShared", package: "Tool"), - .product(name: "SuggestionProvider", package: "Tool"), - .product(name: "SuggestionBasic", package: "Tool"), - .product(name: "Logger", package: "Tool"), - .product(name: "Preferences", package: "Tool"), - .product(name: "GitHubCopilotService", package: "Tool"), - ]), - .target( - name: "Service", - dependencies: [ - "SuggestionWidget", - "SuggestionService", - "ChatService", - "PromptToCodeService", - "ConversationTab", - "KeyBindingManager", - "XcodeThemeController", - .product(name: "TelemetryService", package: "Tool"), - .product(name: "XPCShared", package: "Tool"), - .product(name: "SuggestionProvider", package: "Tool"), - .product(name: "ConversationServiceProvider", package: "Tool"), - .product(name: "Workspace", package: "Tool"), - .product(name: "UserDefaultsObserver", package: "Tool"), - .product(name: "AppMonitoring", package: "Tool"), - .product(name: "SuggestionBasic", package: "Tool"), - .product(name: "Status", package: "Tool"), - .product(name: "StatusBarItemView", package: "Tool"), - .product(name: "ChatTab", package: "Tool"), - .product(name: "Logger", package: "Tool"), - .product(name: "ChatAPIService", package: "Tool"), - .product(name: "Preferences", package: "Tool"), - .product(name: "AXHelper", package: "Tool"), - .product(name: "AsyncAlgorithms", package: "swift-async-algorithms"), - .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), - .product(name: "Dependencies", package: "swift-dependencies"), - .product(name: "KeyboardShortcuts", package: "KeyboardShortcuts"), - ]), - .testTarget( - name: "ServiceTests", - dependencies: [ - "Service", - "Client", - "SuggestionInjector", - .product(name: "XPCShared", package: "Tool"), - .product(name: "SuggestionProvider", package: "Tool"), - .product(name: "SuggestionBasic", package: "Tool"), - .product(name: "Preferences", package: "Tool"), - .product(name: "ConversationServiceProvider", package: "Tool"), - ] - ), - - // MARK: - Host App - - .target( - name: "HostApp", - dependencies: [ - "Client", - "LaunchAgentManager", - "GitHubCopilotViewModel", - .product(name: "SuggestionProvider", package: "Tool"), - .product(name: "Toast", package: "Tool"), - .product(name: "SharedUIComponents", package: "Tool"), - .product(name: "SuggestionBasic", package: "Tool"), - .product(name: "MarkdownUI", package: "swift-markdown-ui"), - .product(name: "ChatAPIService", package: "Tool"), - .product(name: "Preferences", package: "Tool"), - .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), - .product(name: "KeyboardShortcuts", package: "KeyboardShortcuts"), - .product(name: "GitHubCopilotService", package: "Tool"), - .product(name: "Persist", package: "Tool"), - ]), - - // MARK: - Suggestion Service - - .target( - name: "SuggestionService", - dependencies: [ - .product(name: "UserDefaultsObserver", package: "Tool"), - .product(name: "Preferences", package: "Tool"), - .product(name: "SuggestionBasic", package: "Tool"), - .product(name: "SuggestionProvider", package: "Tool"), - .product(name: "BuiltinExtension", package: "Tool"), - .product(name: "GitHubCopilotService", package: "Tool"), - ]), - .target( - name: "SuggestionInjector", - dependencies: [.product(name: "SuggestionBasic", package: "Tool")] - ), - .testTarget( - name: "SuggestionInjectorTests", - dependencies: ["SuggestionInjector"] - ), - - // MARK: - Prompt To Code - - .target( - name: "PromptToCodeService", - dependencies: [ - .product(name: "SuggestionBasic", package: "Tool"), - .product(name: "ChatAPIService", package: "Tool"), - .product(name: "AppMonitoring", package: "Tool"), - .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), - ]), - - // MARK: - Chat - - .target( - name: "ChatService", - dependencies: [ - "PersistMiddleware", - .product(name: "AppMonitoring", package: "Tool"), - .product(name: "Parsing", package: "swift-parsing"), - .product(name: "ChatAPIService", package: "Tool"), - .product(name: "Preferences", package: "Tool"), - .product(name: "AXHelper", package: "Tool"), - .product(name: "ConversationServiceProvider", package: "Tool"), - .product(name: "GitHubCopilotService", package: "Tool"), - .product(name: "Workspace", package: "Tool"), - .product(name: "Terminal", package: "Tool"), - .product(name: "SystemUtils", package: "Tool"), - .product(name: "AppKitExtension", package: "Tool"), - .product(name: "WebContentExtractor", package: "Tool") - ]), - .testTarget( - name: "ChatServiceTests", - dependencies: ["ChatService"] - ), - - .target( - name: "ConversationTab", - dependencies: [ - "ChatService", - .product(name: "SharedUIComponents", package: "Tool"), - .product(name: "ChatAPIService", package: "Tool"), - .product(name: "Logger", package: "Tool"), - .product(name: "ChatTab", package: "Tool"), - .product(name: "Terminal", package: "Tool"), - .product(name: "Cache", package: "Tool"), - .product(name: "MarkdownUI", package: "swift-markdown-ui"), - .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), - .product(name: "SwiftUIFlowLayout", package: "swiftui-flow-layout"), - .product(name: "Persist", package: "Tool") - ] - ), - - // MARK: - UI - - .target( - name: "SuggestionWidget", - dependencies: [ - "PromptToCodeService", - "ConversationTab", - "GitHubCopilotViewModel", - "PersistMiddleware", - .product(name: "GitHubCopilotService", package: "Tool"), - .product(name: "Toast", package: "Tool"), - .product(name: "UserDefaultsObserver", package: "Tool"), - .product(name: "SharedUIComponents", package: "Tool"), - .product(name: "AppMonitoring", package: "Tool"), - .product(name: "ChatTab", package: "Tool"), - .product(name: "Logger", package: "Tool"), - .product(name: "CustomAsyncAlgorithms", package: "Tool"), - .product(name: "HostAppActivator", package: "Tool"), - .product(name: "AsyncAlgorithms", package: "swift-async-algorithms"), - .product(name: "MarkdownUI", package: "swift-markdown-ui"), - .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), - ] - ), - .testTarget(name: "SuggestionWidgetTests", dependencies: ["SuggestionWidget"]), - - // MARK: - Helpers - - .target(name: "FileChangeChecker"), - .target( - name: "LaunchAgentManager", - dependencies: [ - .product(name: "Logger", package: "Tool"), - ] - ), - .target( - name: "UpdateChecker", - dependencies: [ - "Sparkle", - .product(name: "Preferences", package: "Tool"), - .product(name: "Logger", package: "Tool"), - ] - ), - .target( - name: "GitHubCopilotViewModel", - dependencies: [ - .product(name: "GitHubCopilotService", package: "Tool"), - .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), - .product(name: "Status", package: "Tool"), - ] - ), - - // MARK: Key Binding - - .target( - name: "KeyBindingManager", - dependencies: [ - .product(name: "Workspace", package: "Tool"), - .product(name: "Preferences", package: "Tool"), - .product(name: "Logger", package: "Tool"), - .product(name: "CGEventOverride", package: "CGEventOverride"), - .product(name: "AppMonitoring", package: "Tool"), - .product(name: "UserDefaultsObserver", package: "Tool"), - .product(name: "ConversationServiceProvider", package: "Tool"), - ] - ), - .testTarget( - name: "KeyBindingManagerTests", - dependencies: ["KeyBindingManager"] - ), - - // MARK: Theming - - .target( - name: "XcodeThemeController", - dependencies: [ - .product(name: "Preferences", package: "Tool"), - .product(name: "AppMonitoring", package: "Tool"), - .product(name: "Highlightr", package: "Highlightr"), - ] - ), - - // MARK: Persist Middleware - .target( - name: "PersistMiddleware", - dependencies: [ - .product(name: "Persist", package: "Tool"), - .product(name: "ChatTab", package: "Tool"), - .product(name: "ChatAPIService", package: "Tool"), - .product(name: "ConversationServiceProvider", package: "Tool") - ] - ) - ] -) - diff --git a/Core/Sources/ChatService/ChatInjector.swift b/Core/Sources/ChatService/ChatInjector.swift deleted file mode 100644 index 81a60243..00000000 --- a/Core/Sources/ChatService/ChatInjector.swift +++ /dev/null @@ -1,97 +0,0 @@ -import SuggestionBasic -import AppKit -import XcodeInspector -import AXHelper -import ApplicationServices -import AppActivator - - -public struct ChatInjector { - public init() {} - - public func insertCodeBlock(codeBlock: String) { - do { - guard let editorContent = XcodeInspector.shared.focusedEditor?.getContent(), - let focusElement = XcodeInspector.shared.focusedElement, - focusElement.description == "Source Editor" - else { return } - - var cursorPosition = editorContent.cursorPosition - guard cursorPosition.line >= 0, cursorPosition.character >= 0 else { return } - - var lines = editorContent.content.splitByNewLine( - omittingEmptySubsequences: false - ).map { String($0) } - // Ensure the line number is within the bounds of the file - guard cursorPosition.line <= lines.count else { return } - - var modifications: [Modification] = [] - - // remove selection - // make sure there is selection exist and valid - if let selection = editorContent.selections.first, - selection.isValid, - selection.start.line < lines.endIndex { - let selectionEndLine = min(selection.end.line, lines.count - 1) - let deletedSelection = CursorRange( - start: selection.start, - end: .init(line: selectionEndLine, character: selection.end.character) - ) - modifications.append(.deletedSelection(deletedSelection)) - lines = lines.applying([.deletedSelection(deletedSelection)]) - - // update cursorPosition to the start of selection - cursorPosition = selection.start - } - - let targetLine = lines[cursorPosition.line] - - // Determine the indention level of the target line - let leadingWhitespace = cursorPosition.character > 0 ? targetLine.prefix { $0.isWhitespace } : "" - let indentation = String(leadingWhitespace) - - // Insert codeblock at the specified position - let index = targetLine.index(targetLine.startIndex, offsetBy: min(cursorPosition.character, targetLine.count)) - let before = targetLine[.. String in - return index == 0 ? String(element) : indentation + String(element) - } - - var toBeInsertedLines = [String]() - toBeInsertedLines.append(String(before) + codeBlockLines.first!) - toBeInsertedLines.append(contentsOf: codeBlockLines.dropFirst().dropLast()) - toBeInsertedLines.append(codeBlockLines.last! + String(after)) - - lines.replaceSubrange((cursorPosition.line)...(cursorPosition.line), with: toBeInsertedLines) - - // Join the lines - let newContent = String(lines.joined(separator: "\n")) - - // Inject updated content - let newCursorPosition = CursorPosition( - line: cursorPosition.line + codeBlockLines.count - 1, - character: codeBlockLines.last?.count ?? 0 - ) - modifications.append(.inserted(cursorPosition.line, toBeInsertedLines)) - try AXHelper().injectUpdatedCodeWithAccessibilityAPI( - .init( - content: newContent, - newSelection: .cursor(newCursorPosition), - modifications: modifications - ), - focusElement: focusElement, - onSuccess: { - NSWorkspace.activatePreviousActiveXcode() - } - - ) - - } catch { - print("Failed to insert code block: \(error)") - } - } -} diff --git a/Core/Sources/ChatService/ChatService.swift b/Core/Sources/ChatService/ChatService.swift deleted file mode 100644 index a693aaa6..00000000 --- a/Core/Sources/ChatService/ChatService.swift +++ /dev/null @@ -1,1106 +0,0 @@ -import ChatAPIService -import Combine -import Foundation -import GitHubCopilotService -import Preferences -import ConversationServiceProvider -import BuiltinExtension -import JSONRPC -import Status -import Persist -import PersistMiddleware -import ChatTab -import Logger -import Workspace -import XcodeInspector -import OrderedCollections -import SystemUtils - -public protocol ChatServiceType { - var memory: ContextAwareAutoManagedChatMemory { get set } - func send(_ id: String, content: String, contentImages: [ChatCompletionContentPartImage], contentImageReferences: [ImageReference], skillSet: [ConversationSkill], references: [FileReference], model: String?, agentMode: Bool, userLanguage: String?, turnId: String?) async throws - func stopReceivingMessage() async - func upvote(_ id: String, _ rating: ConversationRating) async - func downvote(_ id: String, _ rating: ConversationRating) async - func copyCode(_ id: String) async -} - -struct ToolCallRequest { - let requestId: JSONId - let turnId: String - let roundId: Int - let toolCallId: String - let completion: (AnyJSONRPCResponse) -> Void -} - -public struct FileEdit: Equatable { - - public enum Status: String { - case none = "none" - case kept = "kept" - case undone = "undone" - } - - public let fileURL: URL - public let originalContent: String - public var modifiedContent: String - public var status: Status - - /// Different toolName, the different undo logic. Like `insert_edit_into_file` and `create_file` - public var toolName: ToolName - - public init( - fileURL: URL, - originalContent: String, - modifiedContent: String, - status: Status = .none, - toolName: ToolName - ) { - self.fileURL = fileURL - self.originalContent = originalContent - self.modifiedContent = modifiedContent - self.status = status - self.toolName = toolName - } -} - -public final class ChatService: ChatServiceType, ObservableObject { - - public var memory: ContextAwareAutoManagedChatMemory - @Published public internal(set) var chatHistory: [ChatMessage] = [] - @Published public internal(set) var isReceivingMessage = false - @Published public internal(set) var fileEditMap: OrderedDictionary = [:] - public let chatTabInfo: ChatTabInfo - private let conversationProvider: ConversationServiceProvider? - private let conversationProgressHandler: ConversationProgressHandler - private let conversationContextHandler: ConversationContextHandler = ConversationContextHandlerImpl.shared - // sync all the files in the workspace to watch for changes. - private let watchedFilesHandler: WatchedFilesHandler = WatchedFilesHandlerImpl.shared - private var cancellables = Set() - private var activeRequestId: String? - private(set) public var conversationId: String? - private var skillSet: [ConversationSkill] = [] - private var lastUserRequest: ConversationRequest? - private var isRestored: Bool = false - private var pendingToolCallRequests: [String: ToolCallRequest] = [:] - init(provider: any ConversationServiceProvider, - memory: ContextAwareAutoManagedChatMemory = ContextAwareAutoManagedChatMemory(), - conversationProgressHandler: ConversationProgressHandler = ConversationProgressHandlerImpl.shared, - chatTabInfo: ChatTabInfo) { - self.memory = memory - self.conversationProvider = provider - self.conversationProgressHandler = conversationProgressHandler - self.chatTabInfo = chatTabInfo - memory.chatService = self - - subscribeToNotifications() - subscribeToConversationContextRequest() - subscribeToClientToolInvokeEvent() - subscribeToClientToolConfirmationEvent() - } - - deinit { - Task { [weak self] in - await self?.stopReceivingMessage() - } - - // Clear all subscriptions - cancellables.forEach { $0.cancel() } - cancellables.removeAll() - - // Memory will be deallocated automatically - } - - private func subscribeToNotifications() { - memory.observeHistoryChange { [weak self] in - Task { [weak self] in - guard let memory = self?.memory else { return } - self?.chatHistory = await memory.history - } - } - - conversationProgressHandler.onBegin.sink { [weak self] (token, progress) in - self?.handleProgressBegin(token: token, progress: progress) - }.store(in: &cancellables) - - conversationProgressHandler.onProgress.sink { [weak self] (token, progress) in - self?.handleProgressReport(token: token, progress: progress) - }.store(in: &cancellables) - - conversationProgressHandler.onEnd.sink { [weak self] (token, progress) in - self?.handleProgressEnd(token: token, progress: progress) - }.store(in: &cancellables) - } - - private func subscribeToConversationContextRequest() { - self.conversationContextHandler.onConversationContext.sink(receiveValue: { [weak self] (request, completion) in - guard let skills = self?.skillSet, !skills.isEmpty, request.params!.conversationId == self?.conversationId else { return } - skills.forEach { skill in - if (skill.applies(params: request.params!)) { - skill.resolveSkill(request: request, completion: completion) - } - } - }).store(in: &cancellables) - } - - private func subscribeToClientToolConfirmationEvent() { - ClientToolHandlerImpl.shared.onClientToolConfirmationEvent.sink(receiveValue: { [weak self] (request, completion) in - guard let params = request.params, params.conversationId == self?.conversationId else { return } - let editAgentRounds: [AgentRound] = [ - AgentRound(roundId: params.roundId, - reply: "", - toolCalls: [ - AgentToolCall(id: params.toolCallId, name: params.name, status: .waitForConfirmation, invokeParams: params) - ] - ) - ] - self?.appendToolCallHistory(turnId: params.turnId, editAgentRounds: editAgentRounds) - self?.pendingToolCallRequests[params.toolCallId] = ToolCallRequest( - requestId: request.id, - turnId: params.turnId, - roundId: params.roundId, - toolCallId: params.toolCallId, - completion: completion) - }).store(in: &cancellables) - } - - private func subscribeToClientToolInvokeEvent() { - ClientToolHandlerImpl.shared.onClientToolInvokeEvent.sink(receiveValue: { [weak self] (request, completion) in - guard let params = request.params, params.conversationId == self?.conversationId else { return } - guard let copilotTool = CopilotToolRegistry.shared.getTool(name: params.name) else { - completion(AnyJSONRPCResponse(id: request.id, - result: JSONValue.array([ - JSONValue.null, - JSONValue.hash( - [ - "code": .number(-32601), - "message": .string("Tool function not found") - ]) - ]) - ) - ) - return - } - - copilotTool.invokeTool(request, completion: completion, chatHistoryUpdater: self?.appendToolCallHistory, contextProvider: self) - }).store(in: &cancellables) - } - - private func appendToolCallHistory(turnId: String, editAgentRounds: [AgentRound]) { - let chatTabId = self.chatTabInfo.id - Task { - let message = ChatMessage( - id: turnId, - chatTabID: chatTabId, - clsTurnID: turnId, - role: .assistant, - content: "", - references: [], - steps: [], - editAgentRounds: editAgentRounds - ) - - await self.memory.appendMessage(message) - } - } - - public func updateFileEdits(by fileEdit: FileEdit) { - if let existingFileEdit = self.fileEditMap[fileEdit.fileURL] { - self.fileEditMap[fileEdit.fileURL] = .init( - fileURL: fileEdit.fileURL, - originalContent: existingFileEdit.originalContent, - modifiedContent: fileEdit.modifiedContent, - toolName: existingFileEdit.toolName - ) - } else { - self.fileEditMap[fileEdit.fileURL] = fileEdit - } - } - - public func notifyChangeTextDocument(fileURL: URL, content: String, version: Int) async throws { - try await conversationProvider?.notifyChangeTextDocument(fileURL: fileURL, content: content, version: version, workspaceURL: getWorkspaceURL()) - } - - public static func service(for chatTabInfo: ChatTabInfo) -> ChatService { - let provider = BuiltinExtensionConversationServiceProvider( - extension: GitHubCopilotExtension.self - ) - return ChatService(provider: provider, chatTabInfo: chatTabInfo) - } - - // this will be triggerred in conversation tab if needed - public func restoreIfNeeded() { - guard self.isRestored == false else { return } - - Task { - let storedChatMessages = fetchAllChatMessagesFromStorage() - await mutateHistory { history in - history.append(contentsOf: storedChatMessages) - } - } - - self.isRestored = true - } - - public func updateToolCallStatus(toolCallId: String, status: AgentToolCall.ToolCallStatus, payload: Any? = nil) { - if status == .cancelled { - resetOngoingRequest() - return - } - - // Send the tool call result back to the server - if let toolCallRequest = self.pendingToolCallRequests[toolCallId], status == .accepted { - self.pendingToolCallRequests.removeValue(forKey: toolCallId) - let toolResult = LanguageModelToolConfirmationResult(result: .Accept) - let jsonResult = try? JSONEncoder().encode(toolResult) - let jsonValue = (try? JSONDecoder().decode(JSONValue.self, from: jsonResult ?? Data())) ?? JSONValue.null - toolCallRequest.completion( - AnyJSONRPCResponse( - id: toolCallRequest.requestId, - result: JSONValue.array([ - jsonValue, - JSONValue.null - ]) - ) - ) - } - - // Update the tool call status in the chat history - Task { - guard let lastMessage = await memory.history.last, lastMessage.role == .assistant else { - return - } - - var updatedAgentRounds: [AgentRound] = [] - for i in 0.. = [], - contentImageReferences: Array = [], - skillSet: Array, - references: Array, - model: String? = nil, - agentMode: Bool = false, - userLanguage: String? = nil, - turnId: String? = nil - ) async throws { - guard activeRequestId == nil else { return } - let workDoneToken = UUID().uuidString - activeRequestId = workDoneToken - - let finalImageReferences: [ImageReference] - let finalContentImages: [ChatCompletionContentPartImage] - - if !contentImageReferences.isEmpty { - // User attached images are all parsed as ImageReference - finalImageReferences = contentImageReferences - finalContentImages = contentImageReferences - .map { - ChatCompletionContentPartImage( - url: $0.dataURL(imageType: $0.source == .screenshot ? "png" : "") - ) - } - } else { - // In current implementation, only resend message will have contentImageReferences - // No need to convert ChatCompletionContentPartImage to ImageReference for persistence - finalImageReferences = [] - finalContentImages = contentImages - } - - var chatMessage = ChatMessage( - id: id, - chatTabID: self.chatTabInfo.id, - role: .user, - content: content, - contentImageReferences: finalImageReferences, - references: references.toConversationReferences() - ) - - let currentEditorSkill = skillSet.first(where: { $0.id == CurrentEditorSkill.ID }) as? CurrentEditorSkill - let currentFileReadability = currentEditorSkill == nil - ? nil - : FileUtils.checkFileReadability(at: currentEditorSkill!.currentFilePath) - var errorMessage: ChatMessage? - - var currentTurnId: String? = turnId - // If turnId is provided, it is used to update the existing message, no need to append the user message - if turnId == nil { - if let currentFileReadability, !currentFileReadability.isReadable { - // For associating error message with user message - currentTurnId = UUID().uuidString - chatMessage.clsTurnID = currentTurnId - errorMessage = buildErrorMessage( - turnId: currentTurnId!, - errorMessages: [ - currentFileReadability.errorMessage( - using: CurrentEditorSkill.readabilityErrorMessageProvider - ) - ].compactMap { $0 }.filter { !$0.isEmpty } - ) - } - await memory.appendMessage(chatMessage) - } - - // reset file edits - self.resetFileEdits() - - // persist - saveChatMessageToStorage(chatMessage) - - if content.hasPrefix("/releaseNotes") { - if let fileURL = Bundle.main.url(forResource: "ReleaseNotes", withExtension: "md"), - let whatsNewContent = try? String(contentsOf: fileURL) - { - // will be persist in resetOngoingRequest() - // there is no turn id from CLS, just set it as id - let clsTurnID = UUID().uuidString - let progressMessage = ChatMessage( - id: clsTurnID, - chatTabID: self.chatTabInfo.id, - clsTurnID: clsTurnID, - role: .assistant, - content: whatsNewContent, - references: [] - ) - await memory.appendMessage(progressMessage) - } - resetOngoingRequest() - return - } - - if let errorMessage { - Task { await memory.appendMessage(errorMessage) } - } - - var activeDoc: Doc? - var validSkillSet: [ConversationSkill] = skillSet - if let currentEditorSkill, currentFileReadability?.isReadable == true { - activeDoc = Doc(uri: currentEditorSkill.currentFile.url.absoluteString) - } else { - validSkillSet.removeAll(where: { $0.id == CurrentEditorSkill.ID || $0.id == ProblemsInActiveDocumentSkill.ID }) - } - - let request = createConversationRequest( - workDoneToken: workDoneToken, - content: content, - contentImages: finalContentImages, - activeDoc: activeDoc, - references: references, - model: model, - agentMode: agentMode, - userLanguage: userLanguage, - turnId: currentTurnId, - skillSet: validSkillSet - ) - - self.lastUserRequest = request - self.skillSet = validSkillSet - try await sendConversationRequest(request) - } - - private func createConversationRequest( - workDoneToken: String, - content: String, - contentImages: [ChatCompletionContentPartImage] = [], - activeDoc: Doc?, - references: [FileReference], - model: String? = nil, - agentMode: Bool = false, - userLanguage: String? = nil, - turnId: String? = nil, - skillSet: [ConversationSkill] - ) -> ConversationRequest { - let skillCapabilities: [String] = [CurrentEditorSkill.ID, ProblemsInActiveDocumentSkill.ID] - let supportedSkills: [String] = skillSet.map { $0.id } - let ignoredSkills: [String] = skillCapabilities.filter { - !supportedSkills.contains($0) - } - - /// replace the `@workspace` to `@project` - let newContent = replaceFirstWord(in: content, from: "@workspace", to: "@project") - - return ConversationRequest( - workDoneToken: workDoneToken, - content: newContent, - contentImages: contentImages, - workspaceFolder: "", - activeDoc: activeDoc, - skills: skillCapabilities, - ignoredSkills: ignoredSkills, - references: references, - model: model, - agentMode: agentMode, - userLanguage: userLanguage, - turnId: turnId - ) - } - - public func sendAndWait(_ id: String, content: String) async throws -> String { - try await send(id, content: content, skillSet: [], references: []) - if let reply = await memory.history.last(where: { $0.role == .assistant })?.content { - return reply - } - return "" - } - - public func stopReceivingMessage() async { - if let activeRequestId = activeRequestId { - do { - try await conversationProvider?.stopReceivingMessage(activeRequestId, workspaceURL: getWorkspaceURL()) - } catch { - print("Failed to cancel ongoing request with WDT: \(activeRequestId)") - } - } - resetOngoingRequest() - } - - public func clearHistory() async { - let messageIds = await memory.history.map { $0.id } - - await memory.clearHistory() - if let activeRequestId = activeRequestId { - do { - try await conversationProvider?.stopReceivingMessage(activeRequestId, workspaceURL: getWorkspaceURL()) - } catch { - print("Failed to cancel ongoing request with WDT: \(activeRequestId)") - } - } - - deleteAllChatMessagesFromStorage(messageIds) - resetOngoingRequest() - } - - public func deleteMessage(id: String) async { - await memory.removeMessage(id) - deleteChatMessageFromStorage(id) - } - - public func resendMessage(id: String, model: String? = nil) async throws { - if let _ = (await memory.history).first(where: { $0.id == id }), - let lastUserRequest - { - // TODO: clean up contents for resend message - activeRequestId = nil - try await send( - id, - content: lastUserRequest.content, - contentImages: lastUserRequest.contentImages, - skillSet: skillSet, - references: lastUserRequest.references ?? [], - model: model != nil ? model : lastUserRequest.model, - agentMode: lastUserRequest.agentMode, - userLanguage: lastUserRequest.userLanguage, - turnId: id - ) - } - } - - public func setMessageAsExtraPrompt(id: String) async { - if let message = (await memory.history).first(where: { $0.id == id }) - { - await mutateHistory { history in - let chatMessage: ChatMessage = .init( - chatTabID: self.chatTabInfo.id, - role: .assistant, - content: message.content - ) - - history.append(chatMessage) - self.saveChatMessageToStorage(chatMessage) - } - } - } - - public func mutateHistory(_ mutator: @escaping (inout [ChatMessage]) -> Void) async { - await memory.mutateHistory(mutator) - } - - public func handleCustomCommand(_ command: CustomCommand) async throws { - struct CustomCommandInfo { - var specifiedSystemPrompt: String? - var extraSystemPrompt: String? - var sendingMessageImmediately: String? - var name: String? - } - - let info: CustomCommandInfo? = { - switch command.feature { - case let .chatWithSelection(extraSystemPrompt, prompt, useExtraSystemPrompt): - let updatePrompt = useExtraSystemPrompt ?? true - return .init( - extraSystemPrompt: updatePrompt ? extraSystemPrompt : nil, - sendingMessageImmediately: prompt, - name: command.name - ) - case let .customChat(systemPrompt, prompt): - return .init( - specifiedSystemPrompt: systemPrompt, - extraSystemPrompt: "", - sendingMessageImmediately: prompt, - name: command.name - ) - case .promptToCode: return nil - case .singleRoundDialog: return nil - } - }() - - guard let info else { return } - - let templateProcessor = CustomCommandTemplateProcessor() - - if info.specifiedSystemPrompt != nil || info.extraSystemPrompt != nil { - await mutateHistory { history in - let chatMessage: ChatMessage = .init( - chatTabID: self.chatTabInfo.id, - role: .assistant, - content: "" - ) - history.append(chatMessage) - self.saveChatMessageToStorage(chatMessage) - } - } - - if let sendingMessageImmediately = info.sendingMessageImmediately, - !sendingMessageImmediately.isEmpty - { - try await send(UUID().uuidString, content: templateProcessor.process(sendingMessageImmediately), skillSet: [], references: []) - } - } - - public func getWorkspaceURL() -> URL? { - guard !chatTabInfo.workspacePath.isEmpty else { - return nil - } - return URL(fileURLWithPath: chatTabInfo.workspacePath) - } - - public func upvote(_ id: String, _ rating: ConversationRating) async { - try? await conversationProvider?.rateConversation(turnId: id, rating: rating, workspaceURL: getWorkspaceURL()) - } - - public func downvote(_ id: String, _ rating: ConversationRating) async { - try? await conversationProvider?.rateConversation(turnId: id, rating: rating, workspaceURL: getWorkspaceURL()) - } - - public func copyCode(_ id: String) async { - // TODO: pass copy code info to Copilot server - } - - // not used - public func handleSingleRoundDialogCommand( - systemPrompt: String?, - overwriteSystemPrompt: Bool, - prompt: String - ) async throws -> String { - let templateProcessor = CustomCommandTemplateProcessor() - return try await sendAndWait(UUID().uuidString, content: templateProcessor.process(prompt)) - } - - private func handleProgressBegin(token: String, progress: ConversationProgressBegin) { - guard let workDoneToken = activeRequestId, workDoneToken == token else { return } - conversationId = progress.conversationId - let turnId = progress.turnId - - Task { - if var lastUserMessage = await memory.history.last(where: { $0.role == .user }) { - - // Case: New conversation where error message was generated before CLS request - // Using clsTurnId to associate this error message with the corresponding user message - // When merging error messages with bot responses from CLS, these properties need to be updated - await memory.mutateHistory { history in - if let existingBotIndex = history.lastIndex(where: { - $0.role == .assistant && $0.clsTurnID == lastUserMessage.clsTurnID - }) { - history[existingBotIndex].id = turnId - history[existingBotIndex].clsTurnID = turnId - } - } - - lastUserMessage.clsTurnID = progress.turnId - saveChatMessageToStorage(lastUserMessage) - } - - /// Display an initial assistant message immediately after the user sends a message. - /// This improves perceived responsiveness, especially in Agent Mode where the first - /// ProgressReport may take long time. - let message = ChatMessage( - id: turnId, - chatTabID: self.chatTabInfo.id, - clsTurnID: turnId, - role: .assistant, - content: "" - ) - - // will persist in resetOngoingRequest() - await memory.appendMessage(message) - } - } - - private func handleProgressReport(token: String, progress: ConversationProgressReport) { - guard let workDownToken = activeRequestId, workDownToken == token else { - return - } - - let id = progress.turnId - var content = "" - var references: [ConversationReference] = [] - var steps: [ConversationProgressStep] = [] - var editAgentRounds: [AgentRound] = [] - - if let reply = progress.reply { - content = reply - } - - if let progressReferences = progress.references, !progressReferences.isEmpty { - references = progressReferences.toConversationReferences() - } - - if let progressSteps = progress.steps, !progressSteps.isEmpty { - steps = progressSteps - } - - if let progressAgentRounds = progress.editAgentRounds, !progressAgentRounds.isEmpty { - editAgentRounds = progressAgentRounds - } - - if content.isEmpty && references.isEmpty && steps.isEmpty && editAgentRounds.isEmpty { - return - } - - // create immutable copies - let messageContent = content - let messageReferences = references - let messageSteps = steps - let messageAgentRounds = editAgentRounds - - Task { - let message = ChatMessage( - id: id, - chatTabID: self.chatTabInfo.id, - clsTurnID: id, - role: .assistant, - content: messageContent, - references: messageReferences, - steps: messageSteps, - editAgentRounds: messageAgentRounds - ) - - // will persist in resetOngoingRequest() - await memory.appendMessage(message) - } - } - - private func handleProgressEnd(token: String, progress: ConversationProgressEnd) { - guard let workDoneToken = activeRequestId, workDoneToken == token else { return } - let followUp = progress.followUp - - if let CLSError = progress.error { - // CLS Error Code 402: reached monthly chat messages limit - if CLSError.code == 402 { - Task { - await Status.shared - .updateCLSStatus(.warning, busy: false, message: CLSError.message) - let errorMessage = buildErrorMessage( - turnId: progress.turnId, - panelMessages: [.init(type: .error, title: String(CLSError.code ?? 0), message: CLSError.message, location: .Panel)]) - // will persist in resetongoingRequest() - await memory.appendMessage(errorMessage) - - if let lastUserRequest, - let currentUserPlan = await Status.shared.currentUserPlan(), - currentUserPlan != "free" { - guard let fallbackModel = CopilotModelManager.getFallbackLLM( - scope: lastUserRequest.agentMode ? .agentPanel : .chatPanel - ) else { - resetOngoingRequest() - return - } - do { - CopilotModelManager.switchToFallbackModel() - try await resendMessage(id: progress.turnId, model: fallbackModel.id) - } catch { - Logger.gitHubCopilot.error(error) - resetOngoingRequest() - } - return - } - } - } else if CLSError.code == 400 && CLSError.message.contains("model is not supported") { - Task { - let errorMessage = buildErrorMessage( - turnId: progress.turnId, - errorMessages: ["Oops, the model is not supported. Please enable it first in [GitHub Copilot settings](https://github.com/settings/copilot)."] - ) - await memory.appendMessage(errorMessage) - resetOngoingRequest() - return - } - } else { - Task { - let errorMessage = buildErrorMessage(turnId: progress.turnId, errorMessages: [CLSError.message]) - // will persist in resetOngoingRequest() - await memory.appendMessage(errorMessage) - resetOngoingRequest() - return - } - } - } - - Task { - let message = ChatMessage( - id: progress.turnId, - chatTabID: self.chatTabInfo.id, - clsTurnID: progress.turnId, - role: .assistant, - content: "", - followUp: followUp, - suggestedTitle: progress.suggestedTitle - ) - // will persist in resetOngoingRequest() - await memory.appendMessage(message) - resetOngoingRequest() - } - } - - private func buildErrorMessage( - turnId: String, - errorMessages: [String] = [], - panelMessages: [CopilotShowMessageParams] = [] - ) -> ChatMessage { - return .init( - id: turnId, - chatTabID: chatTabInfo.id, - clsTurnID: turnId, - role: .assistant, - content: "", - errorMessages: errorMessages, - panelMessages: panelMessages - ) - } - - private func resetOngoingRequest() { - activeRequestId = nil - isReceivingMessage = false - - // cancel all pending tool call requests - for (_, request) in pendingToolCallRequests { - pendingToolCallRequests.removeValue(forKey: request.toolCallId) - let toolResult = LanguageModelToolConfirmationResult(result: .Dismiss) - let jsonResult = try? JSONEncoder().encode(toolResult) - let jsonValue = (try? JSONDecoder().decode(JSONValue.self, from: jsonResult ?? Data())) ?? JSONValue.null - request.completion( - AnyJSONRPCResponse( - id: request.requestId, - result: JSONValue.array([ - jsonValue, - JSONValue.null - ]) - ) - ) - } - - Task { - // mark running steps to cancelled - await mutateHistory({ history in - guard !history.isEmpty, - let lastIndex = history.indices.last, - history[lastIndex].role == .assistant else { return } - - for i in 0.. 0 { - // invoke history turns - let turns = chatHistory.toTurns() - requestWithTurns.turns = turns - } - - try await conversationProvider?.createConversation(requestWithTurns, workspaceURL: getWorkspaceURL()) - } - } catch { - resetOngoingRequest() - throw error - } - } - - // MARK: - File Edit - public func undoFileEdit(for fileURL: URL) throws { - guard let fileEdit = self.fileEditMap[fileURL], - fileEdit.status == .none - else { return } - - switch fileEdit.toolName { - case .insertEditIntoFile: - InsertEditIntoFileTool.applyEdit(for: fileURL, content: fileEdit.originalContent, contextProvider: self) - case .createFile: - try CreateFileTool.undo(for: fileURL) - default: - return - } - - self.fileEditMap[fileURL]!.status = .undone - } - - public func keepFileEdit(for fileURL: URL) { - guard let fileEdit = self.fileEditMap[fileURL], fileEdit.status == .none - else { return } - self.fileEditMap[fileURL]!.status = .kept - } - - public func resetFileEdits() { - self.fileEditMap = [:] - } - - public func discardFileEdit(for fileURL: URL) throws { - try self.undoFileEdit(for: fileURL) - self.fileEditMap.removeValue(forKey: fileURL) - } -} - - -public final class SharedChatService { - public var chatTemplates: [ChatTemplate]? = nil - public var chatAgents: [ChatAgent]? = nil - private let conversationProvider: ConversationServiceProvider? - - public static let shared = SharedChatService.service() - - init(provider: any ConversationServiceProvider) { - self.conversationProvider = provider - } - - public static func service() -> SharedChatService { - let provider = BuiltinExtensionConversationServiceProvider( - extension: GitHubCopilotExtension.self - ) - return SharedChatService(provider: provider) - } - - public func loadChatTemplates() async -> [ChatTemplate]? { - guard self.chatTemplates == nil else { return self.chatTemplates } - - do { - if let templates = (try await conversationProvider?.templates()) { - self.chatTemplates = templates - return templates - } - } catch { - // handle error if desired - } - - return nil - } - - public func copilotModels() async -> [CopilotModel] { - guard let models = try? await conversationProvider?.models() else { return [] } - return models - } - - public func loadChatAgents() async -> [ChatAgent]? { - guard self.chatAgents == nil else { return self.chatAgents } - - do { - if let chatAgents = (try await conversationProvider?.agents()) { - self.chatAgents = chatAgents - return chatAgents - } - } catch { - // handle error if desired - } - - return nil - } -} - - -extension ChatService { - - // do storage operatoin in the background - private func runInBackground(_ operation: @escaping () -> Void) { - Task.detached(priority: .utility) { - operation() - } - } - - func saveChatMessageToStorage(_ message: ChatMessage) { - runInBackground { - ChatMessageStore.save(message, with: .init(workspacePath: self.chatTabInfo.workspacePath, username: self.chatTabInfo.username)) - } - } - - func deleteChatMessageFromStorage(_ id: String) { - runInBackground { - ChatMessageStore.delete(by: id, with: .init(workspacePath: self.chatTabInfo.workspacePath, username: self.chatTabInfo.username)) - } - } - func deleteAllChatMessagesFromStorage(_ ids: [String]) { - runInBackground { - ChatMessageStore.deleteAll(by: ids, with: .init(workspacePath: self.chatTabInfo.workspacePath, username: self.chatTabInfo.username)) - } - } - - func fetchAllChatMessagesFromStorage() -> [ChatMessage] { - return ChatMessageStore.getAll(by: self.chatTabInfo.id, metadata: .init(workspacePath: self.chatTabInfo.workspacePath, username: self.chatTabInfo.username)) - } -} - -func replaceFirstWord(in content: String, from oldWord: String, to newWord: String) -> String { - let pattern = "^\(oldWord)\\b" - - if let regex = try? NSRegularExpression(pattern: pattern, options: []) { - let range = NSRange(location: 0, length: content.utf16.count) - return regex.stringByReplacingMatches(in: content, options: [], range: range, withTemplate: newWord) - } - - return content -} - -extension Array where Element == Reference { - func toConversationReferences() -> [ConversationReference] { - return self.map { - .init(uri: $0.uri, status: .included, kind: .reference($0)) - } - } -} - -extension Array where Element == FileReference { - func toConversationReferences() -> [ConversationReference] { - return self.map { - .init(uri: $0.url.path, status: .included, kind: .fileReference($0)) - } - } -} -extension [ChatMessage] { - // transfer chat messages to turns - // used to restore chat history for CLS - func toTurns() -> [TurnSchema] { - var turns: [TurnSchema] = [] - let count = self.count - var index = 0 - - while index < count { - let message = self[index] - if case .user = message.role { - var turn = TurnSchema(request: message.content, turnId: message.clsTurnID) - // has next message - if index + 1 < count { - let nextMessage = self[index + 1] - if nextMessage.role == .assistant { - turn.response = nextMessage.content + extractContentFromEditAgentRounds(nextMessage.editAgentRounds) - index += 1 - } - } - turns.append(turn) - } - index += 1 - } - - return turns - } - - private func extractContentFromEditAgentRounds(_ editAgentRounds: [AgentRound]) -> String { - var content = "" - for round in editAgentRounds { - if !round.reply.isEmpty { - content += round.reply - } - } - return content - } -} diff --git a/Core/Sources/ChatService/ContextAwareAutoManagedChatMemory.swift b/Core/Sources/ChatService/ContextAwareAutoManagedChatMemory.swift deleted file mode 100644 index f185f9b1..00000000 --- a/Core/Sources/ChatService/ContextAwareAutoManagedChatMemory.swift +++ /dev/null @@ -1,28 +0,0 @@ -import Foundation -import ChatAPIService - -public final class ContextAwareAutoManagedChatMemory: ChatMemory { - private let memory: AutoManagedChatMemory - weak var chatService: ChatService? - - public var history: [ChatMessage] { - get async { await memory.history } - } - - func observeHistoryChange(_ observer: @escaping () -> Void) { - memory.observeHistoryChange(observer) - } - - init() { - memory = AutoManagedChatMemory( - systemPrompt: "" - ) - } - - deinit { } - - public func mutateHistory(_ update: (inout [ChatMessage]) -> Void) async { - await memory.mutateHistory(update) - } -} - diff --git a/Core/Sources/ChatService/CustomCommandTemplateProcessor.swift b/Core/Sources/ChatService/CustomCommandTemplateProcessor.swift deleted file mode 100644 index 2a54d320..00000000 --- a/Core/Sources/ChatService/CustomCommandTemplateProcessor.swift +++ /dev/null @@ -1,53 +0,0 @@ -import AppKit -import Foundation -import SuggestionBasic -import XcodeInspector - -public struct CustomCommandTemplateProcessor { - public init() {} - - public func process(_ text: String) async -> String { - let info = await getEditorInformation() - let editorContent = info.editorContent - let updatedText = text - .replacingOccurrences(of: "{{selected_code}}", with: """ - \(editorContent?.selectedContent.trimmingCharacters(in: .whitespacesAndNewlines) ?? "") - """) - .replacingOccurrences( - of: "{{active_editor_language}}", - with: info.language.rawValue - ) - .replacingOccurrences( - of: "{{active_editor_file_url}}", - with: info.documentURL?.path ?? "" - ) - .replacingOccurrences( - of: "{{active_editor_file_name}}", - with: info.documentURL?.lastPathComponent ?? "" - ) - .replacingOccurrences( - of: "{{clipboard}}", - with: NSPasteboard.general.string(forType: .string) ?? "" - ) - return updatedText - } - - struct EditorInformation { - let editorContent: SourceEditor.Content? - let language: CodeLanguage - let documentURL: URL? - } - - func getEditorInformation() async -> EditorInformation { - let editorContent = await XcodeInspector.shared.safe.focusedEditor?.getContent() - let documentURL = await XcodeInspector.shared.safe.activeDocumentURL - let language = documentURL.map(languageIdentifierFromFileURL) ?? .plaintext - - return .init( - editorContent: editorContent, - language: language, - documentURL: documentURL - ) - } -} - diff --git a/Core/Sources/Client/XPCService.swift b/Core/Sources/Client/XPCService.swift deleted file mode 100644 index 24a50bab..00000000 --- a/Core/Sources/Client/XPCService.swift +++ /dev/null @@ -1,14 +0,0 @@ -import Foundation -import Logger -import os.log -import XPCShared - -let shared = XPCExtensionService(logger: .client) - -public func getService() throws -> XPCExtensionService { - if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" { - struct RunningInPreview: Error {} - throw RunningInPreview() - } - return shared -} diff --git a/Core/Sources/FileChangeChecker/FileChangeChecker.swift b/Core/Sources/FileChangeChecker/FileChangeChecker.swift deleted file mode 100644 index a8444044..00000000 --- a/Core/Sources/FileChangeChecker/FileChangeChecker.swift +++ /dev/null @@ -1,39 +0,0 @@ -import CryptoKit -import Dispatch -import Foundation - -/// Check that a file is changed. -public actor FileChangeChecker { - let url: URL - var checksum: Data? - - public init(fileURL: URL) async { - url = fileURL - checksum = getChecksum() - } - - public func checkIfChanged() -> Bool { - guard let newChecksum = getChecksum() else { return false } - return newChecksum != checksum - } - - func getChecksum() -> Data? { - let bufferSize = 16 * 1024 - guard let file = try? FileHandle(forReadingFrom: url) else { return nil } - defer { try? file.close() } - var md5 = CryptoKit.Insecure.MD5() - while autoreleasepool(invoking: { - let data = file.readData(ofLength: bufferSize) - if !data.isEmpty { - md5.update(data: data) - return true // Continue - } else { - return false // End of file - } - }) {} - - let data = Data(md5.finalize()) - - return data - } -} diff --git a/Core/Sources/GitHubCopilotViewModel/GitHubCopilotViewModel.swift b/Core/Sources/GitHubCopilotViewModel/GitHubCopilotViewModel.swift deleted file mode 100644 index e310f5d5..00000000 --- a/Core/Sources/GitHubCopilotViewModel/GitHubCopilotViewModel.swift +++ /dev/null @@ -1,365 +0,0 @@ -import Foundation -import GitHubCopilotService -import ComposableArchitecture -import Status -import SwiftUI -import Cache - -public struct SignInResponse { - public let status: SignInInitiateStatus - public let userCode: String - public let verificationURL: URL -} - -@MainActor -public class GitHubCopilotViewModel: ObservableObject { - // Add static shared instance - public static let shared = GitHubCopilotViewModel() - - @Dependency(\.toast) var toast - @Dependency(\.openURL) var openURL - - @AppStorage("username") var username: String = "" - - @Published public var isRunningAction: Bool = false - @Published public var status: GitHubCopilotAccountStatus? - @Published public var version: String? - @Published public var userCode: String? - @Published public var isSignInAlertPresented = false - @Published public var signInResponse: SignInResponse? - @Published public var waitingForSignIn = false - - static var copilotAuthService: GitHubCopilotService? - - // Make init private to enforce singleton pattern - private init() {} - - public func getGitHubCopilotAuthService() throws -> GitHubCopilotService { - if let service = Self.copilotAuthService { return service } - let service = try GitHubCopilotService() - Self.copilotAuthService = service - return service - } - - public func preSignIn() async throws -> SignInResponse? { - let service = try getGitHubCopilotAuthService() - let result = try await service.signInInitiate() - - if result.status == .alreadySignedIn { - guard let user = result.user else { - toast("Missing user info.", .error) - throw NSError(domain: "Missing user info.", code: 0, userInfo: nil) - } - await Status.shared.updateAuthStatus(.loggedIn, username: user) - self.username = user - broadcastStatusChange() - return nil - } - - guard let uri = result.verificationUri, - let userCode = result.userCode, - let url = URL(string: uri) else { - toast("Verification URI is incorrect.", .error) - throw NSError(domain: "Verification URI is incorrect.", code: 0, userInfo: nil) - } - return SignInResponse( - status: SignInInitiateStatus.promptUserDeviceFlow, - userCode: userCode, - verificationURL: url - ) - } - - public func signIn() { - Task { - isRunningAction = true - defer { isRunningAction = false } - do { - guard let result = try await preSignIn() else { return } - self.signInResponse = result - self.isSignInAlertPresented = true - } catch { - toast(error.localizedDescription, .error) - } - } - } - - public func checkStatus() { - Task { - isRunningAction = true - defer { isRunningAction = false } - do { - let service = try getGitHubCopilotAuthService() - status = try await service.checkStatus() - version = try await service.version() - isRunningAction = false - } catch { - toast(error.localizedDescription, .error) - } - } - } - - public func signOut() { - Task { - isRunningAction = true - defer { isRunningAction = false } - do { - let service = try getGitHubCopilotAuthService() - status = try await service.signOut() - await Status.shared.updateAuthStatus(.notLoggedIn) - await Status.shared.updateCLSStatus(.unknown, busy: false, message: "") - await Status.shared.updateQuotaInfo(nil) - username = "" - broadcastStatusChange() - } catch { - toast(error.localizedDescription, .error) - } - - // Sign out all other CLS instances - do { - try await GitHubCopilotService.signOutAll() - } catch { - // ignore - } - } - } - - public func cancelWaiting() { - waitingForSignIn = false - } - - public func copyAndOpen() { - waitingForSignIn = true - guard let signInResponse else { - toast("Missing sign in details.", .error) - return - } - let pasteboard = NSPasteboard.general - pasteboard.declareTypes([NSPasteboard.PasteboardType.string], owner: nil) - pasteboard.setString(signInResponse.userCode, forType: NSPasteboard.PasteboardType.string) - toast("Sign-in code \(signInResponse.userCode) copied", .info) - Task { - await openURL(signInResponse.verificationURL) - waitForSignIn() - } - } - - public func waitForSignIn() { - Task { - do { - guard waitingForSignIn else { return } - guard let signInResponse else { - waitingForSignIn = false - return - } - let service = try getGitHubCopilotAuthService() - let (username, status) = try await service.signInConfirm(userCode: signInResponse.userCode) - waitingForSignIn = false - self.username = username - self.status = status - await Status.shared.updateAuthStatus(.loggedIn, username: username) - broadcastStatusChange() - let models = try? await service.models() - if let models = models, !models.isEmpty { - CopilotModelManager.updateLLMs(models) - } - } catch let error as GitHubCopilotError { - switch error { - case .languageServerError(.timeout): - waitForSignIn() - return - case .languageServerError( - .serverError( - code: CLSErrorCode.deviceFlowFailed.rawValue, - message: _, - data: _ - ) - ): - await showSignInFailedAlert(error: error) - waitingForSignIn = false - return - default: - throw error - } - } catch { - toast(error.localizedDescription, .error) - } - } - } - - private func extractSigninErrorMessage(error: GitHubCopilotError) -> String { - let errorDescription = error.localizedDescription - - // Handle specific EACCES permission denied errors - if errorDescription.contains("EACCES") { - // Look for paths wrapped in single quotes - let pattern = "'([^']+)'" - if let regex = try? NSRegularExpression(pattern: pattern, options: []) { - let range = NSRange(location: 0, length: errorDescription.utf16.count) - if let match = regex.firstMatch(in: errorDescription, options: [], range: range) { - let pathRange = Range(match.range(at: 1), in: errorDescription)! - let path = String(errorDescription[pathRange]) - return path - } - } - } - - return errorDescription - } - - private func getSigninErrorTitle(error: GitHubCopilotError) -> String { - let errorDescription = error.localizedDescription - - if errorDescription.contains("EACCES") { - return "Can't sign you in. The app couldn't create or access files in" - } - - return "Error details:" - } - - private var accessPermissionCommands: String { - """ - sudo mkdir -p ~/.config/github-copilot - sudo chown -R $(whoami):staff ~/.config - chmod -N ~/.config ~/.config/github-copilot - """ - } - - private var containerBackgroundColor: CGColor { - let isDarkMode = NSApp.effectiveAppearance.name == .darkAqua - return isDarkMode - ? NSColor.black.withAlphaComponent(0.85).cgColor - : NSColor.white.withAlphaComponent(0.85).cgColor - } - - // MARK: - Alert Building Functions - - private func showSignInFailedAlert(error: GitHubCopilotError) async { - let alert = NSAlert() - alert.messageText = "GitHub Copilot Sign-in Failed" - alert.alertStyle = .critical - - let accessoryView = createAlertAccessoryView(error: error) - alert.accessoryView = accessoryView - alert.addButton(withTitle: "Copy Commands") - alert.addButton(withTitle: "Cancel") - - let response = await MainActor.run { - alert.runModal() - } - - if response == .alertFirstButtonReturn { - copyCommandsToClipboard() - } - } - - private func createAlertAccessoryView(error: GitHubCopilotError) -> NSView { - let accessoryView = NSView(frame: NSRect(x: 0, y: 0, width: 400, height: 142)) - - let detailsHeader = createDetailsHeader(error: error) - accessoryView.addSubview(detailsHeader) - - let errorContainer = createErrorContainer(error: error) - accessoryView.addSubview(errorContainer) - - let terminalHeader = createTerminalHeader() - accessoryView.addSubview(terminalHeader) - - let commandsContainer = createCommandsContainer() - accessoryView.addSubview(commandsContainer) - - return accessoryView - } - - private func createDetailsHeader(error: GitHubCopilotError) -> NSView { - let detailsHeader = NSView(frame: NSRect(x: 16, y: 122, width: 368, height: 20)) - - let warningIcon = NSImageView(frame: NSRect(x: 0, y: 4, width: 16, height: 16)) - warningIcon.image = NSImage(systemSymbolName: "exclamationmark.triangle.fill", accessibilityDescription: "Warning") - warningIcon.contentTintColor = NSColor.systemOrange - detailsHeader.addSubview(warningIcon) - - let detailsLabel = NSTextField(wrappingLabelWithString: getSigninErrorTitle(error: error)) - detailsLabel.frame = NSRect(x: 20, y: 0, width: 346, height: 20) - detailsLabel.font = NSFont.systemFont(ofSize: 12, weight: .regular) - detailsLabel.textColor = NSColor.labelColor - detailsHeader.addSubview(detailsLabel) - - return detailsHeader - } - - private func createErrorContainer(error: GitHubCopilotError) -> NSView { - let errorContainer = NSView(frame: NSRect(x: 16, y: 96, width: 368, height: 22)) - errorContainer.wantsLayer = true - errorContainer.layer?.backgroundColor = containerBackgroundColor - errorContainer.layer?.borderColor = NSColor.separatorColor.cgColor - errorContainer.layer?.borderWidth = 1 - errorContainer.layer?.cornerRadius = 6 - - let errorMessage = NSTextField(wrappingLabelWithString: extractSigninErrorMessage(error: error)) - errorMessage.frame = NSRect(x: 8, y: 4, width: 368, height: 14) - errorMessage.font = NSFont.monospacedSystemFont(ofSize: 11, weight: .regular) - errorMessage.textColor = NSColor.labelColor - errorMessage.backgroundColor = .clear - errorMessage.isBordered = false - errorMessage.isEditable = false - errorMessage.drawsBackground = false - errorMessage.usesSingleLineMode = true - errorContainer.addSubview(errorMessage) - - return errorContainer - } - - private func createTerminalHeader() -> NSView { - let terminalHeader = NSView(frame: NSRect(x: 16, y: 66, width: 368, height: 20)) - - let toolIcon = NSImageView(frame: NSRect(x: 0, y: 4, width: 16, height: 16)) - toolIcon.image = NSImage(systemSymbolName: "terminal.fill", accessibilityDescription: "Terminal") - toolIcon.contentTintColor = NSColor.secondaryLabelColor - terminalHeader.addSubview(toolIcon) - - let terminalLabel = NSTextField(wrappingLabelWithString: "Copy and run the commands below in Terminal, then retry.") - terminalLabel.frame = NSRect(x: 20, y: 0, width: 346, height: 20) - terminalLabel.font = NSFont.systemFont(ofSize: 12, weight: .regular) - terminalLabel.textColor = NSColor.labelColor - terminalHeader.addSubview(terminalLabel) - - return terminalHeader - } - - private func createCommandsContainer() -> NSView { - let commandsContainer = NSView(frame: NSRect(x: 16, y: 4, width: 368, height: 58)) - commandsContainer.wantsLayer = true - commandsContainer.layer?.backgroundColor = containerBackgroundColor - commandsContainer.layer?.borderColor = NSColor.separatorColor.cgColor - commandsContainer.layer?.borderWidth = 1 - commandsContainer.layer?.cornerRadius = 6 - - let commandsText = NSTextField(wrappingLabelWithString: accessPermissionCommands) - commandsText.frame = NSRect(x: 8, y: 8, width: 344, height: 42) - commandsText.font = NSFont.monospacedSystemFont(ofSize: 11, weight: .regular) - commandsText.textColor = NSColor.labelColor - commandsText.backgroundColor = .clear - commandsText.isBordered = false - commandsText.isEditable = false - commandsText.isSelectable = true - commandsText.drawsBackground = false - commandsContainer.addSubview(commandsText) - - return commandsContainer - } - - private func copyCommandsToClipboard() { - NSPasteboard.general.clearContents() - NSPasteboard.general.setString( - self.accessPermissionCommands.replacingOccurrences(of: "\n", with: " && "), - forType: .string - ) - } - - public func broadcastStatusChange() { - DistributedNotificationCenter.default().post( - name: .authStatusDidChange, - object: nil - ) - } -} diff --git a/Core/Sources/KeyBindingManager/KeyBindingManager.swift b/Core/Sources/KeyBindingManager/KeyBindingManager.swift deleted file mode 100644 index 2fcf67fa..00000000 --- a/Core/Sources/KeyBindingManager/KeyBindingManager.swift +++ /dev/null @@ -1,29 +0,0 @@ -import Foundation -import Workspace -public final class KeyBindingManager { - let tabToAcceptSuggestion: TabToAcceptSuggestion - public init( - workspacePool: WorkspacePool, - acceptSuggestion: @escaping () -> Void, - expandSuggestion: @escaping () -> Void, - collapseSuggestion: @escaping () -> Void, - dismissSuggestion: @escaping () -> Void - ) { - tabToAcceptSuggestion = .init( - workspacePool: workspacePool, - acceptSuggestion: acceptSuggestion, - dismissSuggestion: dismissSuggestion, - expandSuggestion: expandSuggestion, - collapseSuggestion: collapseSuggestion - ) - } - - public func start() { - tabToAcceptSuggestion.start() - } - - @MainActor - public func stopForExit() { - tabToAcceptSuggestion.stopForExit() - } -} diff --git a/Core/Sources/KeyBindingManager/TabToAcceptSuggestion.swift b/Core/Sources/KeyBindingManager/TabToAcceptSuggestion.swift deleted file mode 100644 index f2d4c147..00000000 --- a/Core/Sources/KeyBindingManager/TabToAcceptSuggestion.swift +++ /dev/null @@ -1,213 +0,0 @@ -import ActiveApplicationMonitor -import AppKit -import CGEventOverride -import Foundation -import Logger -import Preferences -import SuggestionBasic -import UserDefaultsObserver -import Workspace -import XcodeInspector - -final class TabToAcceptSuggestion { - let hook: CGEventHookType = CGEventHook(eventsOfInterest: [.keyDown]) { message in - Logger.service.debug("TabToAcceptSuggestion: \(message)") - } - - let workspacePool: WorkspacePool - let acceptSuggestion: () -> Void - let expandSuggestion: () -> Void - let collapseSuggestion: () -> Void - let dismissSuggestion: () -> Void - private var modifierEventMonitor: Any? - private let userDefaultsObserver = UserDefaultsObserver( - object: UserDefaults.shared, forKeyPaths: [ - UserDefaultPreferenceKeys().acceptSuggestionWithTab.key, - UserDefaultPreferenceKeys().dismissSuggestionWithEsc.key, - ], context: nil - ) - private var stoppedForExit = false - - struct ObservationKey: Hashable {} - - var canTapToAcceptSuggestion: Bool { - UserDefaults.shared.value(for: \.acceptSuggestionWithTab) - } - - var canEscToDismissSuggestion: Bool { - UserDefaults.shared.value(for: \.dismissSuggestionWithEsc) - } - - @MainActor - func stopForExit() { - stoppedForExit = true - stopObservation() - } - - init( - workspacePool: WorkspacePool, - acceptSuggestion: @escaping () -> Void, - dismissSuggestion: @escaping () -> Void, - expandSuggestion: @escaping () -> Void, - collapseSuggestion: @escaping () -> Void - ) { - _ = ThreadSafeAccessToXcodeInspector.shared - self.workspacePool = workspacePool - self.acceptSuggestion = acceptSuggestion - self.dismissSuggestion = dismissSuggestion - self.expandSuggestion = expandSuggestion - self.collapseSuggestion = collapseSuggestion - - hook.add( - .init( - eventsOfInterest: [.keyDown], - convert: { [weak self] _, _, event in - self?.handleEvent(event) ?? .unchanged - } - ), - forKey: ObservationKey() - ) - } - - func start() { - Task { [weak self] in - for await _ in ActiveApplicationMonitor.shared.createInfoStream() { - guard let self else { return } - try Task.checkCancellation() - Task { @MainActor in - if ActiveApplicationMonitor.shared.activeXcode != nil { - self.startObservation() - } else { - self.stopObservation() - } - } - } - } - - userDefaultsObserver.onChange = { [weak self] in - guard let self else { return } - Task { @MainActor in - if self.canTapToAcceptSuggestion { - self.startObservation() - } else { - self.stopObservation() - } - } - } - } - - @MainActor - func startObservation() { - guard !stoppedForExit else { return } - guard canTapToAcceptSuggestion else { return } - hook.activateIfPossible() - removeMonitor() - modifierEventMonitor = NSEvent.addGlobalMonitorForEvents(matching: .flagsChanged) { [weak self] event in - self?.handleModifierEvents(event: event) - } - } - - @MainActor - func stopObservation() { - hook.deactivate() - removeMonitor() - } - - private func removeMonitor() { - if let monitor = modifierEventMonitor { - NSEvent.removeMonitor(monitor) - modifierEventMonitor = nil - } - } - - func handleEvent(_ event: CGEvent) -> CGEventManipulation.Result { - let (accept, reason) = Self.shouldAcceptSuggestion( - event: event, - workspacePool: workspacePool, - xcodeInspector: ThreadSafeAccessToXcodeInspector.shared - ) - if let reason = reason { - Logger.service.debug("TabToAcceptSuggestion: \(accept ? "" : "not") accepting due to: \(reason)") - } - if accept { - acceptSuggestion() - return .discarded - } - return .unchanged - } - - func handleModifierEvents(event: NSEvent) { - if event.modifierFlags.contains(NSEvent.ModifierFlags.option) { - expandSuggestion() - } else { - collapseSuggestion() - } - } -} - -extension TabToAcceptSuggestion { - /// Returns whether a given keyboard event should be intercepted and trigger - /// accepting a suggestion. - static func shouldAcceptSuggestion( - event: CGEvent, - workspacePool: WorkspacePool, - xcodeInspector: ThreadSafeAccessToXcodeInspectorProtocol - ) -> (accept: Bool, reason: String?) { - let keycode = Int(event.getIntegerValueField(.keyboardEventKeycode)) - let tab = 48 - guard keycode == tab else { return (false, nil) } - if event.flags.contains(.maskHelp) { return (false, nil) } - if event.flags.contains(.maskShift) { return (false, nil) } - if event.flags.contains(.maskControl) { return (false, nil) } - if event.flags.contains(.maskCommand) { return (false, nil) } - guard xcodeInspector.hasActiveXcode else { - return (false, "No active Xcode") - } - guard xcodeInspector.hasFocusedEditor else { - return (false, "No focused editor") - } - guard let fileURL = xcodeInspector.activeDocumentURL else { - return (false, "No active document") - } - guard let filespace = workspacePool.fetchFilespaceIfExisted(fileURL: fileURL) else { - return (false, "No filespace") - } - if filespace.presentingSuggestion == nil { - return (false, "No suggestion") - } - return (true, nil) - } -} - -import Combine - -protocol ThreadSafeAccessToXcodeInspectorProtocol { - var activeDocumentURL: URL? {get} - var hasActiveXcode: Bool {get} - var hasFocusedEditor: Bool {get} -} - -private class ThreadSafeAccessToXcodeInspector: ThreadSafeAccessToXcodeInspectorProtocol { - static let shared = ThreadSafeAccessToXcodeInspector() - - private(set) var activeDocumentURL: URL? - private(set) var hasActiveXcode = false - private(set) var hasFocusedEditor = false - private var cancellable: Set = [] - - init() { - let inspector = XcodeInspector.shared - - inspector.$activeDocumentURL.receive(on: DispatchQueue.main).sink { [weak self] newValue in - self?.activeDocumentURL = newValue - }.store(in: &cancellable) - - inspector.$activeXcode.receive(on: DispatchQueue.main).sink { [weak self] newValue in - self?.hasActiveXcode = newValue != nil - }.store(in: &cancellable) - - inspector.$focusedEditor.receive(on: DispatchQueue.main).sink { [weak self] newValue in - self?.hasFocusedEditor = newValue != nil - }.store(in: &cancellable) - } -} diff --git a/Core/Sources/LaunchAgentManager/LaunchAgentManager.swift b/Core/Sources/LaunchAgentManager/LaunchAgentManager.swift deleted file mode 100644 index c311439d..00000000 --- a/Core/Sources/LaunchAgentManager/LaunchAgentManager.swift +++ /dev/null @@ -1,187 +0,0 @@ -import Foundation -import Logger -import ServiceManagement - -public struct LaunchAgentManager { - let lastLaunchAgentVersionKey = "LastLaunchAgentVersion" - let serviceIdentifier: String - let executablePath: String - let bundleIdentifier: String - - var launchAgentDirURL: URL { - FileManager.default.homeDirectoryForCurrentUser - .appendingPathComponent("Library/LaunchAgents") - } - - var launchAgentPath: String { - launchAgentDirURL.appendingPathComponent("\(serviceIdentifier).plist").path - } - - public init(serviceIdentifier: String, executablePath: String, bundleIdentifier: String) { - self.serviceIdentifier = serviceIdentifier - self.executablePath = executablePath - self.bundleIdentifier = bundleIdentifier - } - - public func setupLaunchAgentForTheFirstTimeIfNeeded() async throws { - if #available(macOS 13, *) { - await removeObsoleteLaunchAgent() - try await setupLaunchAgent() - } else { - guard !FileManager.default.fileExists(atPath: launchAgentPath) else { return } - try await setupLaunchAgent() - await removeObsoleteLaunchAgent() - } - } - - @available(macOS 13.0, *) - public func isBackgroundPermissionGranted() async -> Bool { - // On macOS 13+, check SMAppService status - let bridgeLaunchAgent = SMAppService.agent(plistName: "bridgeLaunchAgent.plist") - let status = bridgeLaunchAgent.status - return status != .requiresApproval - } - - public func setupLaunchAgent() async throws { - if #available(macOS 13, *) { - Logger.client.info("Registering bridge launch agent") - let bridgeLaunchAgent = SMAppService.agent(plistName: "bridgeLaunchAgent.plist") - try bridgeLaunchAgent.register() - } else { - Logger.client.info("Creating and loading bridge launch agent") - let content = """ - - - - - Label - \(serviceIdentifier) - Program - \(executablePath) - MachServices - - \(serviceIdentifier) - - - AssociatedBundleIdentifiers - - \(bundleIdentifier) - \(serviceIdentifier) - - - - """ - if !FileManager.default.fileExists(atPath: launchAgentDirURL.path) { - try FileManager.default.createDirectory( - at: launchAgentDirURL, - withIntermediateDirectories: false - ) - } - FileManager.default.createFile( - atPath: launchAgentPath, - contents: content.data(using: .utf8) - ) - try await launchctl("load", launchAgentPath) - } - - let buildNumber = Bundle.main.infoDictionary?["CFBundleVersion"] as? String - UserDefaults.standard.set(buildNumber, forKey: lastLaunchAgentVersionKey) - } - - public func removeLaunchAgent() async throws { - if #available(macOS 13, *) { - Logger.client.info("Unregistering bridge launch agent") - let bridgeLaunchAgent = SMAppService.agent(plistName: "bridgeLaunchAgent.plist") - try await bridgeLaunchAgent.unregister() - } else { - Logger.client.info("Unloading and removing bridge launch agent") - try await launchctl("unload", launchAgentPath) - try FileManager.default.removeItem(atPath: launchAgentPath) - } - } - - public func reloadLaunchAgent() async throws { - if #unavailable(macOS 13) { - Logger.client.info("Reloading bridge launch agent") - try await helper("reload-launch-agent", "--service-identifier", serviceIdentifier) - } - } - - public func removeObsoleteLaunchAgent() async { - if #available(macOS 13, *) { - let path = launchAgentPath - if FileManager.default.fileExists(atPath: path) { - Logger.client.info("Unloading and removing old bridge launch agent") - try? await launchctl("unload", path) - try? FileManager.default.removeItem(atPath: path) - } - } else { - let path = launchAgentPath.replacingOccurrences( - of: "ExtensionService", - with: "XPCService" - ) - if FileManager.default.fileExists(atPath: path) { - Logger.client.info("Removing old bridge launch agent plist") - try? FileManager.default.removeItem(atPath: path) - } - } - } -} - -private func process(_ launchPath: String, _ args: [String]) async throws { - let task = Process() - task.launchPath = launchPath - task.arguments = args - task.environment = [ - "PATH": "/usr/bin", - ] - let outpipe = Pipe() - task.standardOutput = outpipe - - return try await withUnsafeThrowingContinuation { continuation in - do { - task.terminationHandler = { process in - do { - if process.terminationStatus == 0 { - continuation.resume(returning: ()) - } else { - if let data = try? outpipe.fileHandleForReading.readToEnd(), - let content = String(data: data, encoding: .utf8) - { - continuation.resume(throwing: E(errorDescription: content)) - } else { - continuation.resume( - throwing: E( - errorDescription: "Unknown error." - ) - ) - } - } - } - } - try task.run() - } catch { - continuation.resume(throwing: error) - } - } -} - -private func helper(_ args: String...) async throws { - // TODO: A more robust way to locate the executable. - guard let url = Bundle.main.executableURL? - .deletingLastPathComponent() - .deletingLastPathComponent() - .appendingPathComponent("Applications") - .appendingPathComponent("Helper") - else { throw E(errorDescription: "Unable to locate Helper.") } - return try await process(url.path, args) -} - -private func launchctl(_ args: String...) async throws { - return try await process("/bin/launchctl", args) -} - -struct E: Error, LocalizedError { - var errorDescription: String? -} - diff --git a/Core/Sources/Service/GlobalShortcutManager.swift b/Core/Sources/Service/GlobalShortcutManager.swift deleted file mode 100644 index 9620f25a..00000000 --- a/Core/Sources/Service/GlobalShortcutManager.swift +++ /dev/null @@ -1,63 +0,0 @@ -import AppKit -import Combine -import Foundation -import KeyboardShortcuts -import XcodeInspector - -extension KeyboardShortcuts.Name { - static let showHideWidget = Self("ShowHideWidget") -} - -@MainActor -final class GlobalShortcutManager { - let guiController: GraphicalUserInterfaceController - private var cancellable = Set() - - nonisolated init(guiController: GraphicalUserInterfaceController) { - self.guiController = guiController - } - - func start() { - KeyboardShortcuts.userDefaults = .shared - setupShortcutIfNeeded() - - KeyboardShortcuts.onKeyUp(for: .showHideWidget) { [guiController] in - let isXCodeActive = XcodeInspector.shared.activeXcode != nil - - if !isXCodeActive, - !guiController.store.state.suggestionWidgetState.chatPanelState.isPanelDisplayed, - UserDefaults.shared.value(for: \.showHideWidgetShortcutGlobally) - { - guiController.store.send(.openChatPanel(forceDetach: true)) - } else { - guiController.store.send(.toggleWidgetsHotkeyPressed) - } - } - - XcodeInspector.shared.$activeApplication.sink { app in - if !UserDefaults.shared.value(for: \.showHideWidgetShortcutGlobally) { - let shouldBeEnabled = if let app, app.isXcode || app.isExtensionService { - true - } else { - false - } - if shouldBeEnabled { - self.setupShortcutIfNeeded() - } else { - self.removeShortcutIfNeeded() - } - } else { - self.setupShortcutIfNeeded() - } - }.store(in: &cancellable) - } - - func setupShortcutIfNeeded() { - KeyboardShortcuts.enable(.showHideWidget) - } - - func removeShortcutIfNeeded() { - KeyboardShortcuts.disable(.showHideWidget) - } -} - diff --git a/Core/Sources/Service/Helpers.swift b/Core/Sources/Service/Helpers.swift deleted file mode 100644 index 90ac6344..00000000 --- a/Core/Sources/Service/Helpers.swift +++ /dev/null @@ -1,54 +0,0 @@ -import Foundation -import GitHubCopilotService -import LanguageServerProtocol - -extension NSError { - static func from(_ error: Error) -> NSError { - if let error = error as? ServerError { - var message = "Unknown" - switch error { - case let .handlerUnavailable(handler): - message = "Handler unavailable: \(handler)." - case let .unhandledMethod(method): - message = "Methond unhandled: \(method)." - case let .notificationDispatchFailed(error): - message = "Notification dispatch failed: \(error.localizedDescription)." - case let .requestDispatchFailed(error): - message = "Request dispatch failed: \(error.localizedDescription)." - case let .clientDataUnavailable(error): - message = "Client data unavailable: \(error.localizedDescription)." - case .serverUnavailable: - message = "Server unavailable, please make sure you have installed Node." - case .missingExpectedParameter: - message = "Missing expected parameter." - case .missingExpectedResult: - message = "Missing expected result." - case let .unableToDecodeRequest(error): - message = "Unable to decode request: \(error.localizedDescription)." - case let .unableToSendRequest(error): - message = "Unable to send request: \(error.localizedDescription)." - case let .unableToSendNotification(error): - message = "Unable to send notification: \(error.localizedDescription)." - case let .serverError(code, m, _): - message = "Server error: (\(code)) \(m)." - case let .invalidRequest(error): - message = "Invalid request: \(error?.localizedDescription ?? "Unknown")." - case .timeout: - message = "Timeout." - case .unknownError: - message = "Unknown error: \(error.localizedDescription)." - } - return NSError(domain: "com.github.CopilotForXcode", code: -1, userInfo: [ - NSLocalizedDescriptionKey: message, - ]) - } - if let error = error as? CancellationError { - return NSError(domain: "com.github.CopilotForXcode", code: -100, userInfo: [ - NSLocalizedDescriptionKey: error.localizedDescription, - ]) - } - return NSError(domain: "com.github.CopilotForXcode", code: -1, userInfo: [ - NSLocalizedDescriptionKey: error.localizedDescription, - ]) - } -} diff --git a/Core/Sources/Service/RealtimeSuggestionController.swift b/Core/Sources/Service/RealtimeSuggestionController.swift deleted file mode 100644 index 899865f1..00000000 --- a/Core/Sources/Service/RealtimeSuggestionController.swift +++ /dev/null @@ -1,206 +0,0 @@ -import ActiveApplicationMonitor -import AppKit -import AsyncAlgorithms -import AXExtension -import Combine -import Foundation -import Logger -import Preferences -import Status -import QuartzCore -import Workspace -import XcodeInspector - -public actor RealtimeSuggestionController { - private var cancellable: Set = [] - private var inflightPrefetchTask: Task? - private var editorObservationTask: Task? - private var sourceEditor: SourceEditor? - - init() {} - - deinit { - cancellable.forEach { $0.cancel() } - inflightPrefetchTask?.cancel() - editorObservationTask?.cancel() - } - - nonisolated - func start() { - Task { await observeXcodeChange() } - } - - private func observeXcodeChange() { - cancellable.forEach { $0.cancel() } - - XcodeInspector.shared.$focusedEditor - .sink { [weak self] editor in - guard let self else { return } - Task { - guard let editor else { return } - await self.handleFocusElementChange(editor) - } - }.store(in: &cancellable) - } - - private func handleFocusElementChange(_ sourceEditor: SourceEditor) { - self.sourceEditor = sourceEditor - - let notificationsFromEditor = sourceEditor.axNotifications - - editorObservationTask?.cancel() - editorObservationTask = nil - - editorObservationTask = Task { [weak self] in - if let fileURL = await XcodeInspector.shared.safe.realtimeActiveDocumentURL { - await PseudoCommandHandler().invalidateRealtimeSuggestionsIfNeeded( - fileURL: fileURL, - sourceEditor: sourceEditor - ) - } - - let valueChange = await notificationsFromEditor.notifications() - .filter { $0.kind == .valueChanged } - let selectedTextChanged = await notificationsFromEditor.notifications() - .filter { $0.kind == .selectedTextChanged } - - await withTaskGroup(of: Void.self) { [weak self] group in - group.addTask { [weak self] in - let handler = { [weak self] in - guard let self else { return } - await cancelInFlightTasks() - await self.triggerPrefetchDebounced() - await self.notifyEditingFileChange(editor: sourceEditor.element) - } - - if #available(macOS 13.0, *) { - for await _ in valueChange._throttle(for: .milliseconds(200)) { - if Task.isCancelled { return } - await handler() - } - } else { - for await _ in valueChange { - if Task.isCancelled { return } - await handler() - } - } - } - group.addTask { - let handler = { - guard let fileURL = await XcodeInspector.shared.safe.activeDocumentURL - else { return } - await PseudoCommandHandler().invalidateRealtimeSuggestionsIfNeeded( - fileURL: fileURL, - sourceEditor: sourceEditor - ) - } - - if #available(macOS 13.0, *) { - for await _ in selectedTextChanged._throttle(for: .milliseconds(200)) { - if Task.isCancelled { return } - await handler() - } - } else { - for await _ in selectedTextChanged { - if Task.isCancelled { return } - await handler() - } - } - } - - await group.waitForAll() - } - } - - Task { @WorkspaceActor in // Get cache ready for real-time suggestions. - guard UserDefaults.shared.value(for: \.preCacheOnFileOpen) else { return } - guard let fileURL = await XcodeInspector.shared.safe.realtimeActiveDocumentURL - else { return } - let (_, filespace) = try await Service.shared.workspacePool - .fetchOrCreateWorkspaceAndFilespace(fileURL: fileURL) - - if filespace.codeMetadata.uti == nil { - // avoid the command get called twice - filespace.codeMetadata.uti = "" - do { - try await XcodeInspector.shared.safe.latestActiveXcode? - .triggerCopilotCommand(name: "Sync Text Settings") - await Status.shared.updateExtensionStatus(.granted) - } catch { - if filespace.codeMetadata.uti?.isEmpty ?? true { - filespace.codeMetadata.uti = nil - } - if let cantRunError = error as? AppInstanceInspector.CantRunCommand { - if cantRunError.errorDescription.contains("No bundle found") { - await Status.shared.updateExtensionStatus(.notGranted) - } else if cantRunError.errorDescription.contains("found but disabled") { - await Status.shared.updateExtensionStatus(.disabled) - } - } - } - } - } - } - - func triggerPrefetchDebounced(force: Bool = false) { - inflightPrefetchTask = Task(priority: .utility) { @WorkspaceActor in - try? await Task.sleep(nanoseconds: UInt64( - max(UserDefaults.shared.value(for: \.realtimeSuggestionDebounce), 0.15) - * 1_000_000_000 - )) - - if Task.isCancelled { return } - - // check if user loggin - let authStatus = await Status.shared.getAuthStatus() - guard authStatus.status == .loggedIn else { return } - - guard UserDefaults.shared.value(for: \.realtimeSuggestionToggle) - else { return } - - if UserDefaults.shared.value(for: \.disableSuggestionFeatureGlobally), - let fileURL = await XcodeInspector.shared.safe.activeDocumentURL, - let (workspace, _) = try? await Service.shared.workspacePool - .fetchOrCreateWorkspaceAndFilespace(fileURL: fileURL) - { - let isEnabled = workspace.isSuggestionFeatureEnabled - if !isEnabled { return } - } - if Task.isCancelled { return } - - // So the editor won't be blocked (after information are cached)! - await PseudoCommandHandler().generateRealtimeSuggestions(sourceEditor: sourceEditor) - } - } - - func cancelInFlightTasks(excluding: Task? = nil) async { - inflightPrefetchTask?.cancel() - - // cancel in-flight tasks - await withTaskGroup(of: Void.self) { group in - for (_, workspace) in Service.shared.workspacePool.workspaces { - group.addTask { - await workspace.cancelInFlightRealtimeSuggestionRequests() - } - } - } - } - - /// This method will still return true if the completion panel is hidden by esc. - /// Looks like the Xcode will keep the panel around until content is changed, - /// not sure how to observe that it's hidden. - func isCompletionPanelPresenting() -> Bool { - guard let activeXcode = ActiveApplicationMonitor.shared.activeXcode else { return false } - let application = AXUIElementCreateApplication(activeXcode.processIdentifier) - return application.focusedWindow?.child(identifier: "_XC_COMPLETION_TABLE_") != nil - } - - func notifyEditingFileChange(editor: AXUIElement) async { - guard let fileURL = await XcodeInspector.shared.safe.activeDocumentURL, - let (workspace, _) = try? await Service.shared.workspacePool - .fetchOrCreateWorkspaceAndFilespace(fileURL: fileURL) - else { return } - await workspace.didUpdateFilespace(fileURL: fileURL, content: editor.value) - } -} - diff --git a/Core/Sources/Service/ScheduledCleaner.swift b/Core/Sources/Service/ScheduledCleaner.swift deleted file mode 100644 index 2178ba50..00000000 --- a/Core/Sources/Service/ScheduledCleaner.swift +++ /dev/null @@ -1,92 +0,0 @@ -import ActiveApplicationMonitor -import AppKit -import AXExtension -import BuiltinExtension -import Foundation -import Logger -import Workspace -import XcodeInspector - -public final class ScheduledCleaner { - weak var service: Service? - - init() {} - - func start() { - Task { @ServiceActor in - while !Task.isCancelled { - try await Task.sleep(nanoseconds: 10 * 60 * 1_000_000_000) - await cleanUp() - } - } - - Task { @ServiceActor in - for await app in ActiveApplicationMonitor.shared.createInfoStream() { - try Task.checkCancellation() - if let app, !app.isXcode { - await cleanUp() - } - } - } - } - - @ServiceActor - func cleanUp() async { - guard let service else { return } - - let workspaceInfos = XcodeInspector.shared.xcodes.reduce( - into: [ - XcodeAppInstanceInspector.WorkspaceIdentifier: - XcodeAppInstanceInspector.WorkspaceInfo - ]() - ) { result, xcode in - let infos = xcode.realtimeWorkspaces - for (id, info) in infos { - if let existed = result[id] { - result[id] = existed.combined(with: info) - } else { - result[id] = info - } - } - } - for (url, workspace) in service.workspacePool.workspaces { - if workspace.isExpired, workspaceInfos[.url(url)] == nil { - Logger.service.info("Remove idle workspace") - _ = await Task { @MainActor in - service.guiController.store.send( - .promptToCodeGroup(.discardExpiredPromptToCode(documentURLs: Array( - workspace.filespaces.keys - ))) - ) - }.result - await workspace.cleanUp(availableTabs: []) - await service.workspacePool.removeWorkspace(url: url) - } else { - let tabs = (workspaceInfos[.url(url)]?.tabs ?? []) - .union(workspaceInfos[.unknown]?.tabs ?? []) - // cleanup chats for unused files - let filespaces = workspace.filespaces - for (url, _) in filespaces { - if workspace.isFilespaceExpired( - fileURL: url, - availableTabs: tabs - ) { - _ = await Task { @MainActor in - service.guiController.store.send( - .promptToCodeGroup(.discardExpiredPromptToCode(documentURLs: [url])) - ) - }.result - } - } - // cleanup workspace - await workspace.cleanUp(availableTabs: tabs) - } - } - } - - @ServiceActor - public func closeAllChildProcesses() async { - BuiltinExtensionManager.shared.terminate() - } -} - diff --git a/Core/Sources/Service/Service.swift b/Core/Sources/Service/Service.swift deleted file mode 100644 index 8072778a..00000000 --- a/Core/Sources/Service/Service.swift +++ /dev/null @@ -1,220 +0,0 @@ -import BuiltinExtension -import Combine -import Dependencies -import Foundation -import GitHubCopilotService -import KeyBindingManager -import Logger -import SuggestionService -import Toast -import Workspace -import WorkspaceSuggestionService -import XcodeInspector -import XcodeThemeController -import XPCShared -import SuggestionWidget -import Status -import ChatService -import Persist -import PersistMiddleware - -@globalActor public enum ServiceActor { - public actor TheActor {} - public static let shared = TheActor() -} - -/// The running extension service. -public final class Service { - public static let shared = Service() - - @WorkspaceActor - let workspacePool: WorkspacePool - @MainActor - public let guiController = GraphicalUserInterfaceController() - public let realtimeSuggestionController = RealtimeSuggestionController() - public let scheduledCleaner: ScheduledCleaner - let globalShortcutManager: GlobalShortcutManager - let keyBindingManager: KeyBindingManager - let xcodeThemeController: XcodeThemeController = .init() - - @Dependency(\.toast) var toast - var cancellable = Set() - - private init() { - @Dependency(\.workspacePool) var workspacePool - - BuiltinExtensionManager.shared.setupExtensions([ - GitHubCopilotExtension(workspacePool: workspacePool) - ]) - scheduledCleaner = .init() - workspacePool.registerPlugin { - SuggestionServiceWorkspacePlugin(workspace: $0) { SuggestionService.service() } - } - workspacePool.registerPlugin { - GitHubCopilotWorkspacePlugin(workspace: $0) - } - workspacePool.registerPlugin { - BuiltinExtensionWorkspacePlugin(workspace: $0) - } - self.workspacePool = workspacePool - - globalShortcutManager = .init(guiController: guiController) - keyBindingManager = .init( - workspacePool: workspacePool, - acceptSuggestion: { - Task { await PseudoCommandHandler().acceptSuggestion() } - }, - expandSuggestion: { - if !ExpandableSuggestionService.shared.isSuggestionExpanded { - ExpandableSuggestionService.shared.isSuggestionExpanded = true - } - }, - collapseSuggestion: { - if ExpandableSuggestionService.shared.isSuggestionExpanded { - ExpandableSuggestionService.shared.isSuggestionExpanded = false - } - }, - dismissSuggestion: { - Task { await PseudoCommandHandler().dismissSuggestion() } - } - ) - let scheduledCleaner = ScheduledCleaner() - - scheduledCleaner.service = self - Logger.telemetryLogger = TelemetryLogger() - } - - @MainActor - public func start() { - scheduledCleaner.start() - realtimeSuggestionController.start() - guiController.start() - xcodeThemeController.start() - globalShortcutManager.start() - keyBindingManager.start() - - Task { - await Publishers.CombineLatest( - XcodeInspector.shared.safe.$activeDocumentURL - .removeDuplicates(), - XcodeInspector.shared.safe.$latestActiveXcode - ) - .receive(on: DispatchQueue.main) - .sink { [weak self] documentURL, latestXcode in - Task { - let fileURL = documentURL ?? latestXcode?.realtimeDocumentURL - guard fileURL != nil, fileURL != .init(fileURLWithPath: "/") else { - return - } - do { - let _ = try await self?.workspacePool - .fetchOrCreateWorkspaceAndFilespace( - fileURL: fileURL! - ) - } catch let error as Workspace.WorkspaceFileError { - Logger.workspacePool - .info(error.localizedDescription) - } - catch { - Logger.workspacePool.error(error) - } - } - }.store(in: &cancellable) - - // Combine both workspace and auth status changes into a single stream - await Publishers.CombineLatest3( - XcodeInspector.shared.safe.$latestActiveXcode, - XcodeInspector.shared.safe.$activeWorkspaceURL - .removeDuplicates(), - StatusObserver.shared.$authStatus - .removeDuplicates() - ) - .receive(on: DispatchQueue.main) - .sink { [weak self] newXcode, newURL, newStatus in - // First check for realtimeWorkspaceURL if activeWorkspaceURL is nil - if let realtimeURL = newXcode?.realtimeWorkspaceURL, newURL == nil { - self?.onNewActiveWorkspaceURLOrAuthStatus( - newURL: realtimeURL, - newStatus: newStatus - ) - } else if let newURL = newURL { - // Then use activeWorkspaceURL if available - self?.onNewActiveWorkspaceURLOrAuthStatus( - newURL: newURL, - newStatus: newStatus - ) - } - } - .store(in: &cancellable) - } - } - - @MainActor - public func prepareForExit() async { - Logger.service.info("Prepare for exit.") - keyBindingManager.stopForExit() - await scheduledCleaner.closeAllChildProcesses() - } - - private func getDisplayNameOfXcodeWorkspace(url: URL) -> String { - var name = url.lastPathComponent - let suffixes = [".xcworkspace", ".xcodeproj", ".playground"] - for suffix in suffixes { - if name.hasSuffix(suffix) { - name = String(name.dropLast(suffix.count)) - break - } - } - return name - } -} - -public extension Service { - func handleXPCServiceRequests( - endpoint: String, - requestBody: Data, - reply: @escaping (Data?, Error?) -> Void - ) { - reply(nil, XPCRequestNotHandledError()) - } -} - -// internal extension -extension Service { - - func onNewActiveWorkspaceURLOrAuthStatus(newURL: URL?, newStatus: AuthStatus) { - Task { @MainActor in - // check path - guard let path = newURL?.path, path != "/", - // check auth status - newStatus.status == .loggedIn, - let username = newStatus.username, !username.isEmpty, - // Switch workspace only when the `workspace` or `username` is not the same as the current one - ( - self.guiController.store.chatHistory.selectedWorkspacePath != path || - self.guiController.store.chatHistory.currentUsername != username - ) - else { return } - - await self.doSwitchWorkspace(workspaceURL: newURL!, username: username) - } - } - - /// - Parameters: - /// - workspaceURL: The active workspace URL that need switch to - /// - path: Path of the workspace URL - /// - username: Curent github username - @MainActor - func doSwitchWorkspace(workspaceURL: URL, username: String) async { - // get workspace display name - let name = self.getDisplayNameOfXcodeWorkspace(url: workspaceURL) - let path = workspaceURL.path - - // switch workspace and username and wait for it to complete - await self.guiController.store.send(.switchWorkspace(path: path, name: name, username: username)).finish() - // restore if needed - await self.guiController.restore(path: path, name: name, username: username) - // init chat tab if no history tab (only after workspace is fully switched and restored) - await self.guiController.store.send(.initWorkspaceChatTabIfNeeded(path: path, username: username)).finish() - } -} diff --git a/Core/Sources/Service/TelemetryLogger.swift b/Core/Sources/Service/TelemetryLogger.swift deleted file mode 100644 index 1bdf3181..00000000 --- a/Core/Sources/Service/TelemetryLogger.swift +++ /dev/null @@ -1,42 +0,0 @@ -import Logger -import Foundation -import TelemetryService - -public class TelemetryLogger: TelemetryLoggerProvider { - public func sendError( - error: any Error, - category: String, - file: StaticString, - line: UInt, - function: StaticString, - callStackSymbols: [String] - ) { - TelemetryService.shared.sendError( - error, - category: category, - file: file, - line: line, - function: function, - from: callStackSymbols - ) - } - - public func sendError( - message: String, - category: String, - file: StaticString, - line: UInt, - function: StaticString, - callStackSymbols: [String] - ) { - TelemetryService.shared - .sendError( - message, - category: category, - file: file, - line: line, - function: function, - from: callStackSymbols - ) - } -} diff --git a/Core/Sources/Service/XPCService.swift b/Core/Sources/Service/XPCService.swift deleted file mode 100644 index 0297224a..00000000 --- a/Core/Sources/Service/XPCService.swift +++ /dev/null @@ -1,348 +0,0 @@ -import AppKit -import Foundation -import GitHubCopilotService -import LanguageServerProtocol -import Logger -import Preferences -import Status -import XPCShared -import HostAppActivator -import XcodeInspector -import GitHubCopilotViewModel - -public class XPCService: NSObject, XPCServiceProtocol { - // MARK: - Service - - public func getXPCServiceVersion(withReply reply: @escaping (String, String) -> Void) { - reply( - Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "N/A", - Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "N/A" - ) - } - - public func getXPCCLSVersion(withReply reply: @escaping (String?) -> Void) { - Task { @MainActor in - do { - let service = try GitHubCopilotViewModel.shared.getGitHubCopilotAuthService() - let version = try await service.version() - reply(version) - } catch { - Logger.service.error("Failed to get CLS version: \(error.localizedDescription)") - reply(nil) - } - } - } - - public func getXPCServiceAccessibilityPermission(withReply reply: @escaping (ObservedAXStatus) -> Void) { - Task { - reply(await Status.shared.getAXStatus()) - } - } - - public func getXPCServiceExtensionPermission( - withReply reply: @escaping (ExtensionPermissionStatus) -> Void - ) { - Task { - reply(await Status.shared.getExtensionStatus()) - } - } - - // MARK: - Suggestion - - @discardableResult - private func replyWithUpdatedContent( - editorContent: Data, - file: StaticString = #file, - line: UInt = #line, - isRealtimeSuggestionRelatedCommand: Bool = false, - withReply reply: @escaping (Data?, Error?) -> Void, - getUpdatedContent: @escaping @ServiceActor ( - SuggestionCommandHandler, - EditorContent - ) async throws -> UpdatedContent? - ) -> Task { - let task = Task { - do { - let editor = try JSONDecoder().decode(EditorContent.self, from: editorContent) - let handler: SuggestionCommandHandler = WindowBaseCommandHandler() - try Task.checkCancellation() - guard let updatedContent = try await getUpdatedContent(handler, editor) else { - reply(nil, nil) - return - } - try Task.checkCancellation() - try reply(JSONEncoder().encode(updatedContent), nil) - } catch { - Logger.service.error("\(file):\(line) \(error.localizedDescription)") - reply(nil, NSError.from(error)) - } - } - - Task { - await Service.shared.realtimeSuggestionController.cancelInFlightTasks(excluding: task) - } - return task - } - - public func getSuggestedCode( - editorContent: Data, - withReply reply: @escaping (Data?, Error?) -> Void - ) { - replyWithUpdatedContent(editorContent: editorContent, withReply: reply) { handler, editor in - try await handler.presentSuggestions(editor: editor) - } - } - - public func getNextSuggestedCode( - editorContent: Data, - withReply reply: @escaping (Data?, Error?) -> Void - ) { - replyWithUpdatedContent(editorContent: editorContent, withReply: reply) { handler, editor in - try await handler.presentNextSuggestion(editor: editor) - } - } - - public func getPreviousSuggestedCode( - editorContent: Data, - withReply reply: @escaping (Data?, Error?) -> Void - ) { - replyWithUpdatedContent(editorContent: editorContent, withReply: reply) { handler, editor in - try await handler.presentPreviousSuggestion(editor: editor) - } - } - - public func getSuggestionRejectedCode( - editorContent: Data, - withReply reply: @escaping (Data?, Error?) -> Void - ) { - replyWithUpdatedContent(editorContent: editorContent, withReply: reply) { handler, editor in - try await handler.rejectSuggestion(editor: editor) - } - } - - public func getSuggestionAcceptedCode( - editorContent: Data, - withReply reply: @escaping (Data?, Error?) -> Void - ) { - replyWithUpdatedContent(editorContent: editorContent, withReply: reply) { handler, editor in - try await handler.acceptSuggestion(editor: editor) - } - } - - public func getPromptToCodeAcceptedCode( - editorContent: Data, - withReply reply: @escaping (Data?, Error?) -> Void - ) { - replyWithUpdatedContent(editorContent: editorContent, withReply: reply) { handler, editor in - try await handler.acceptPromptToCode(editor: editor) - } - } - - public func getRealtimeSuggestedCode( - editorContent: Data, - withReply reply: @escaping (Data?, Error?) -> Void - ) { - replyWithUpdatedContent( - editorContent: editorContent, - isRealtimeSuggestionRelatedCommand: true, - withReply: reply - ) { handler, editor in - try await handler.presentRealtimeSuggestions(editor: editor) - } - } - - public func prefetchRealtimeSuggestions( - editorContent: Data, - withReply reply: @escaping () -> Void - ) { - // We don't need to wait for this. - reply() - - replyWithUpdatedContent( - editorContent: editorContent, - isRealtimeSuggestionRelatedCommand: true, - withReply: { _, _ in } - ) { handler, editor in - try await handler.generateRealtimeSuggestions(editor: editor) - } - } - - public func openChat( - withReply reply: @escaping (Error?) -> Void - ) { - Task { - do { - // Check if app is already running - if let _ = getRunningHostApp() { - // App is already running, use the chat service - let handler = PseudoCommandHandler() - handler.openChat(forceDetach: true) - } else { - try launchHostAppDefault() - } - reply(nil) - } catch { - reply(error) - } - } - } - - public func promptToCode( - editorContent: Data, - withReply reply: @escaping (Data?, Error?) -> Void - ) { - replyWithUpdatedContent(editorContent: editorContent, withReply: reply) { handler, editor in - try await handler.promptToCode(editor: editor) - } - } - - public func customCommand( - id: String, - editorContent: Data, - withReply reply: @escaping (Data?, Error?) -> Void - ) { - replyWithUpdatedContent(editorContent: editorContent, withReply: reply) { handler, editor in - try await handler.customCommand(id: id, editor: editor) - } - } - - // MARK: - Settings - - public func toggleRealtimeSuggestion(withReply reply: @escaping (Error?) -> Void) { - guard AXIsProcessTrusted() else { - reply(NoAccessToAccessibilityAPIError()) - return - } - Task { @ServiceActor in - await Service.shared.realtimeSuggestionController.cancelInFlightTasks() - let on = !UserDefaults.shared.value(for: \.realtimeSuggestionToggle) - UserDefaults.shared.set(on, for: \.realtimeSuggestionToggle) - Task { @MainActor in - Service.shared.guiController.store - .send(.suggestionWidget(.toastPanel(.toast(.toast( - "Real-time suggestion is turned \(on ? "on" : "off")", - .info, - nil - ))))) - } - reply(nil) - } - } - - public func postNotification(name: String, withReply reply: @escaping () -> Void) { - reply() - NotificationCenter.default.post(name: .init(name), object: nil) - } - - public func quit(reply: @escaping () -> Void) { - Task { - await Service.shared.prepareForExit() - reply() - } - } - - // MARK: - Requests - - public func send( - endpoint: String, - requestBody: Data, - reply: @escaping (Data?, Error?) -> Void - ) { - Service.shared.handleXPCServiceRequests( - endpoint: endpoint, - requestBody: requestBody, - reply: reply - ) - } - - // MARK: - XcodeInspector - - public func getXcodeInspectorData(withReply reply: @escaping (Data?, Error?) -> Void) { - do { - // Capture current XcodeInspector data - let inspectorData = XcodeInspectorData( - activeWorkspaceURL: XcodeInspector.shared.activeWorkspaceURL?.absoluteString, - activeProjectRootURL: XcodeInspector.shared.activeProjectRootURL?.absoluteString, - realtimeActiveWorkspaceURL: XcodeInspector.shared.realtimeActiveWorkspaceURL?.absoluteString, - realtimeActiveProjectURL: XcodeInspector.shared.realtimeActiveProjectURL?.absoluteString, - latestNonRootWorkspaceURL: XcodeInspector.shared.latestNonRootWorkspaceURL?.absoluteString - ) - - // Encode and send the data - let data = try JSONEncoder().encode(inspectorData) - reply(data, nil) - } catch { - Logger.service.error("Failed to encode XcodeInspector data: \(error.localizedDescription)") - reply(nil, error) - } - } - - // MARK: - MCP Server Tools - public func getAvailableMCPServerToolsCollections(withReply reply: @escaping (Data?) -> Void) { - let availableMCPServerTools = CopilotMCPToolManager.getAvailableMCPServerToolsCollections() - if let availableMCPServerTools = availableMCPServerTools { - // Encode and send the data - let data = try? JSONEncoder().encode(availableMCPServerTools) - reply(data) - } else { - reply(nil) - } - } - - public func updateMCPServerToolsStatus(tools: Data) { - // Decode the data - let decoder = JSONDecoder() - var collections: [UpdateMCPToolsStatusServerCollection] = [] - do { - collections = try decoder.decode([UpdateMCPToolsStatusServerCollection].self, from: tools) - if collections.isEmpty { - return - } - } catch { - Logger.service.error("Failed to decode MCP server collections: \(error)") - return - } - - Task { @MainActor in - await GitHubCopilotService.updateAllClsMCP(collections: collections) - } - } - - // MARK: - FeatureFlags - public func getCopilotFeatureFlags( - withReply reply: @escaping (Data?) -> Void - ) { - let featureFlags = FeatureFlagNotifierImpl.shared.featureFlags - let data = try? JSONEncoder().encode(featureFlags) - reply(data) - } - - // MARK: - Auth - public func signOutAllGitHubCopilotService() { - Task { @MainActor in - do { - try await GitHubCopilotService.signOutAll() - } catch { - Logger.service.error("Failed to sign out all: \(error)") - } - } - } - - public func getXPCServiceAuthStatus(withReply reply: @escaping (Data?) -> Void) { - Task { @MainActor in - let service = try GitHubCopilotViewModel.shared.getGitHubCopilotAuthService() - _ = try await service.checkStatus() - let authStatus = await Status.shared.getAuthStatus() - let data = try? JSONEncoder().encode(authStatus) - reply(data) - } - } -} - -struct NoAccessToAccessibilityAPIError: Error, LocalizedError { - var errorDescription: String? { - "Accessibility API permission is not granted. Please enable in System Settings.app." - } - - init() {} -} diff --git a/Core/Sources/SuggestionInjector/SuggestionInjector.swift b/Core/Sources/SuggestionInjector/SuggestionInjector.swift deleted file mode 100644 index df78acf5..00000000 --- a/Core/Sources/SuggestionInjector/SuggestionInjector.swift +++ /dev/null @@ -1,234 +0,0 @@ -import Foundation -import SuggestionBasic - -// NOTE: Every lines from Xcode Extension has a line break at its end, even the last line. -// NOTE: Copilot's completion always start at character 0, no matter where the cursor is. - -public struct SuggestionInjector { - public init() {} - - public struct ExtraInfo { - public var didChangeContent = false - public var didChangeCursorPosition = false - public var suggestionRange: ClosedRange? - public var modifications: [Modification] = [] - public init() {} - } - - public func acceptSuggestion( - intoContentWithoutSuggestion content: inout [String], - cursorPosition: inout CursorPosition, - completion: CodeSuggestion, - extraInfo: inout ExtraInfo, - suggestionLineLimit: Int? = nil - ) { - extraInfo.didChangeContent = true - extraInfo.didChangeCursorPosition = true - extraInfo.suggestionRange = nil - let start = completion.range.start - let end = completion.range.end - let suggestionContent = completion.text - let lineEnding = if let ending = content.first?.last, ending.isNewline { - String(ending) - } else { - "\n" - } - - let firstRemovedLine = content[safe: start.line] - let lastRemovedLine = content[safe: end.line] - let startLine = max(0, start.line) - let endLine = max(start.line, min(end.line, content.endIndex - 1)) - if startLine < content.endIndex { - extraInfo.modifications.append(.deleted(startLine...endLine)) - content.removeSubrange(startLine...endLine) - } - - var toBeInserted = suggestionContent.breakLines( - proposedLineEnding: lineEnding, - appendLineBreakToLastLine: true - ) - - if let suggestionLineLimit { - let allLines = toBeInserted - toBeInserted = Array(toBeInserted.prefix(suggestionLineLimit)) - if suggestionLineLimit < allLines.count { - // advance to the next line when accepting part of a multi-line suggestion - toBeInserted.append(startOfLine(line: allLines[suggestionLineLimit], usingEnding: lineEnding)) - } - } - // prepending prefix text not in range if needed. - if let firstRemovedLine, - !firstRemovedLine.isEmptyOrNewLine, - start.character > 0, - start.character < firstRemovedLine.count, - !toBeInserted.isEmpty - { - let leftoverRange = firstRemovedLine.utf16.startIndex..<(firstRemovedLine.utf16.index( - firstRemovedLine.utf16.startIndex, - offsetBy: start.character, - limitedBy: firstRemovedLine.utf16.endIndex - ) ?? firstRemovedLine.utf16.endIndex) - var leftover = String(firstRemovedLine[leftoverRange]) - if leftover.last?.isNewline ?? false { - leftover.removeLast(1) - } - toBeInserted[0].insert( - contentsOf: leftover, - at: toBeInserted[0].startIndex - ) - } - - let recoveredSuffixLength = recoverSuffixIfNeeded( - endOfReplacedContent: end, - toBeInserted: &toBeInserted, - lastRemovedLine: lastRemovedLine, - lineEnding: lineEnding - ) - - let cursorCol = toBeInserted[toBeInserted.endIndex - 1].utf16.count - - 1 - recoveredSuffixLength - let insertingIndex = min(start.line, content.endIndex) - content.insert(contentsOf: toBeInserted, at: insertingIndex) - extraInfo.modifications.append(.inserted(insertingIndex, toBeInserted)) - cursorPosition = .init( - line: startLine + toBeInserted.count - 1, - character: max(0, cursorCol) - ) - } - - func startOfLine(line: String, usingEnding lineEnding: String) -> String { - return line.prefix(while: { $0.isWhitespace }).appending(lineEnding) - } - - func recoverSuffixIfNeeded( - endOfReplacedContent end: CursorPosition, - toBeInserted: inout [String], - lastRemovedLine: String?, - lineEnding: String - ) -> Int { - // If there is no line removed, there is no need to recover anything. - guard let lastRemovedLine, !lastRemovedLine.isEmptyOrNewLine else { return 0 } - - let lastRemovedLineCleaned = lastRemovedLine.droppedLineBreak() - - // If the replaced range covers the whole line, return immediately. - guard end.character >= 0, end.character - 1 < lastRemovedLineCleaned.utf16.count - else { return 0 } - - // if we are not inserting anything, return immediately. - guard !toBeInserted.isEmpty, - let first = toBeInserted.first?.droppedLineBreak(), !first.isEmpty, - let last = toBeInserted.last?.droppedLineBreak(), !last.isEmpty - else { return 0 } - - // case 1: user keeps typing as the suggestion suggests. - - if first.hasPrefix(lastRemovedLineCleaned) { - return 0 - } - - // case 2: user also typed the suffix of the suggestion (or auto-completed by Xcode) - - // locate the split index, the prefix of which matches the suggestion prefix. - var splitIndex: String.Index? - - for offset in end.character..` - - let regex = try! NSRegularExpression(pattern: "\\s*?<#.*?#>") - - if let firstPlaceholderRange = regex.firstMatch( - in: suffix, - options: [], - range: NSRange(suffix.startIndex..., in: suffix) - )?.range, - firstPlaceholderRange.location == 0, - let r = Range(firstPlaceholderRange, in: suffix) - { - suffix.removeSubrange(r) - } - - let lastInsertingLine = toBeInserted[toBeInserted.endIndex - 1] - .droppedLineBreak() - .appending(suffix) - .recoveredLineBreak(lineEnding: lineEnding) - - toBeInserted[toBeInserted.endIndex - 1] = lastInsertingLine - - return suffix.utf16.count - } -} - -public struct SuggestionAnalyzer { - struct Result { - enum InsertPostion { - case currentLine - case nextLine - } - - var insertPosition: InsertPostion - var commonPrefix: String? - } - - func analyze() -> Result { - fatalError() - } -} - -extension String { - var isEmptyOrNewLine: Bool { - isEmpty || self == "\n" || self == "\r\n" || self == "\r" - } - - func droppedLineBreak() -> String { - if last?.isNewline ?? false { - return String(dropLast(1)) - } - return self - } - - func recoveredLineBreak(lineEnding: String) -> String { - if hasSuffix(lineEnding) { - return self - } - return self + lineEnding - } -} - -func longestCommonPrefix(of a: String, and b: String) -> String { - let length = min(a.count, b.count) - - var prefix = "" - for i in 0.. Element? { - indices.contains(index) ? self[index] : nil - } -} - diff --git a/Core/Sources/SuggestionService/SuggestionService.swift b/Core/Sources/SuggestionService/SuggestionService.swift deleted file mode 100644 index 2802d787..00000000 --- a/Core/Sources/SuggestionService/SuggestionService.swift +++ /dev/null @@ -1,86 +0,0 @@ -import BuiltinExtension -import struct CopilotForXcodeKit.WorkspaceInfo -import Foundation -import GitHubCopilotService -import Preferences -import SuggestionBasic -import SuggestionProvider -import UserDefaultsObserver -import Workspace - -public protocol SuggestionServiceType: SuggestionServiceProvider {} - -public actor SuggestionService: SuggestionServiceType { - public var configuration: SuggestionProvider.SuggestionServiceConfiguration { - get async { await suggestionProvider.configuration } - } - - let middlewares: [SuggestionServiceMiddleware] - - let suggestionProvider: SuggestionServiceProvider - - public init( - provider: any SuggestionServiceProvider, - middlewares: [SuggestionServiceMiddleware] = SuggestionServiceMiddlewareContainer - .middlewares - ) { - suggestionProvider = provider - self.middlewares = middlewares - } - - public static func service( - for serviceType: SuggestionFeatureProvider = UserDefaults.shared - .value(for: \.suggestionFeatureProvider) - ) -> SuggestionService { - switch serviceType { - case .builtIn(.gitHubCopilot), .extension: - let provider = BuiltinExtensionSuggestionServiceProvider( - extension: GitHubCopilotExtension.self - ) - return SuggestionService(provider: provider) - } - } -} - -public extension SuggestionService { - func getSuggestions( - _ request: SuggestionRequest, - workspaceInfo: CopilotForXcodeKit.WorkspaceInfo - ) async throws -> [SuggestionBasic.CodeSuggestion] { - var getSuggestion = suggestionProvider.getSuggestions(_:workspaceInfo:) - let configuration = await configuration - - for middleware in middlewares.reversed() { - getSuggestion = { [getSuggestion] request, workspaceInfo in - try await middleware.getSuggestion( - request, - configuration: configuration, - next: { [getSuggestion] request in - try await getSuggestion(request, workspaceInfo) - } - ) - } - } - - return try await getSuggestion(request, workspaceInfo) - } - - func notifyAccepted( - _ suggestion: SuggestionBasic.CodeSuggestion, - workspaceInfo: CopilotForXcodeKit.WorkspaceInfo - ) async { - await suggestionProvider.notifyAccepted(suggestion, workspaceInfo: workspaceInfo) - } - - func notifyRejected( - _ suggestions: [SuggestionBasic.CodeSuggestion], - workspaceInfo: CopilotForXcodeKit.WorkspaceInfo - ) async { - await suggestionProvider.notifyRejected(suggestions, workspaceInfo: workspaceInfo) - } - - func cancelRequest(workspaceInfo: CopilotForXcodeKit.WorkspaceInfo) async { - await suggestionProvider.cancelRequest(workspaceInfo: workspaceInfo) - } -} - diff --git a/Core/Sources/UpdateChecker/UpdateChecker.swift b/Core/Sources/UpdateChecker/UpdateChecker.swift deleted file mode 100644 index e477817d..00000000 --- a/Core/Sources/UpdateChecker/UpdateChecker.swift +++ /dev/null @@ -1,81 +0,0 @@ -import Logger -import Preferences -import Sparkle - -public protocol UpdateCheckerProtocol { - func checkForUpdates() - func getAutomaticallyChecksForUpdates() -> Bool - func setAutomaticallyChecksForUpdates(_ value: Bool) -} - -public protocol UpdateCheckerDelegate: AnyObject { - func prepareForRelaunch(finish: @escaping () -> Void) -} - -public final class NoopUpdateChecker: UpdateCheckerProtocol { - public init() {} - public func checkForUpdates() {} - public func getAutomaticallyChecksForUpdates() -> Bool { false } - public func setAutomaticallyChecksForUpdates(_ value: Bool) {} -} - -public final class UpdateChecker: UpdateCheckerProtocol { - let updater: SPUUpdater - let delegate = UpdaterDelegate() - - public init(hostBundle: Bundle, checkerDelegate: UpdateCheckerDelegate) { - updater = SPUUpdater( - hostBundle: hostBundle, - applicationBundle: Bundle.main, - userDriver: SPUStandardUserDriver(hostBundle: hostBundle, delegate: nil), - delegate: delegate - ) - delegate.updateCheckerDelegate = checkerDelegate - do { - try updater.start() - } catch { - Logger.updateChecker.error(error.localizedDescription) - } - } - - public convenience init?(hostBundle: Bundle?, checkerDelegate: UpdateCheckerDelegate) { - guard let hostBundle = hostBundle else { return nil } - self.init(hostBundle: hostBundle, checkerDelegate: checkerDelegate) - } - - public func checkForUpdates() { - updater.checkForUpdates() - } - - public func getAutomaticallyChecksForUpdates() -> Bool { - updater.automaticallyChecksForUpdates - } - - public func setAutomaticallyChecksForUpdates(_ value: Bool) { - updater.automaticallyChecksForUpdates = value - } -} - -class UpdaterDelegate: NSObject, SPUUpdaterDelegate { - weak var updateCheckerDelegate: UpdateCheckerDelegate? - - func updater( - _ updater: SPUUpdater, - shouldPostponeRelaunchForUpdate item: SUAppcastItem, - untilInvokingBlock installHandler: @escaping () -> Void) -> Bool { - if let updateCheckerDelegate { - updateCheckerDelegate.prepareForRelaunch(finish: installHandler) - return true - } - return false - } - - func allowedChannels(for updater: SPUUpdater) -> Set { - if UserDefaults.shared.value(for: \.installPrereleases) { - Set(["prerelease"]) - } else { - [] - } - } -} - diff --git a/Core/Tests/SuggestionWidgetTests/File.swift b/Core/Tests/SuggestionWidgetTests/File.swift deleted file mode 100644 index fecc4ab4..00000000 --- a/Core/Tests/SuggestionWidgetTests/File.swift +++ /dev/null @@ -1 +0,0 @@ -import Foundation diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md deleted file mode 100644 index 5e7a287b..00000000 --- a/DEVELOPMENT.md +++ /dev/null @@ -1,85 +0,0 @@ -# Development - -## Prerequisites - -Requires Node installed and `npm` available on your system path, e.g. - -```sh -sudo ln -s `which npm` /usr/local/bin -sudo ln -s `which node` /usr/local/bin -``` - -For context, this is used by an Xcode run script as part of the build. Run -scripts use a very limited path to resolve commands. - -## Targets - -### Copilot for Xcode - -Copilot for Xcode is the host app containing both the XPCService and the editor extension. It provides the settings UI. - -### EditorExtension - -As its name suggests, the Xcode source editor extension. Its sole purpose is to forward editor content to the XPCService for processing, and update the editor with the returned content. Due to the sandboxing requirements for editor extensions, it has to communicate with a trusted, non-sandboxed XPCService (CommunicationBridge and ExtensionService) to bypass the limitations. The XPCService service name must be included in the `com.apple.security.temporary-exception.mach-lookup.global-name` entitlements. - -### ExtensionService - -The `ExtensionService` is a program that operates in the background. All features are implemented in this target. - -### CommunicationBridge - -It's responsible for maintaining the communication between the Copilot for Xcode/EditorExtension and ExtensionService. - -### Core and Tool - -Most of the logics are implemented inside the package `Core` and `Tool`. - -- The `Service` contains the implementations of the ExtensionService target. -- The `HostApp` contains the implementations of the Copilot for Xcode target. - -## Building and Archiving the App - -1. Update the xcconfig files, bridgeLaunchAgent.plist, and Tool/Configs/Configurations.swift. -2. Build or archive the Copilot for Xcode target. -3. If Xcode complains that the pro package doesn't exist, please remove the package from the project. - -## Testing Source Editor Extension - -Just run both the `ExtensionService`, `CommunicationBridge` and the `EditorExtension` Target. Read [Testing Your Source Editor Extension](https://developer.apple.com/documentation/xcodekit/testing_your_source_editor_extension) for more details. - -## Local Build - -To build the application locally, follow these steps: - -1. Navigate to the Script directory and run the build scripts: - - ```sh - cd ./Script - sh ./uninstall-app.sh # Remove any previous installation - rm -rf ../build # Clean the build directory - sh ./localbuild-app.sh # Build a fresh copy of the app - ``` - -2. After successful build, the application will be available in the build directory. Copy `GitHub Copilot for Xcode.app` to your Applications folder to test it locally. - -## SwiftUI Previews - -Looks like SwiftUI Previews are not very happy with Objective-C packages when running with app targets. To use previews, please switch schemes to the package product targets. - -## Unit Tests - -To run unit tests, just run test from the `Copilot for Xcode` target. - -For new tests, they should be added to the `TestPlan.xctestplan`. - -## Code Style - -We use SwiftFormat to format the code. - -The source code mostly follows the [Ray Wenderlich Style Guide](https://github.com/raywenderlich/swift-style-guide) very closely with the following exception: - -- Use the Xcode default of 4 spaces for indentation. - -## App Versioning - -The app version and all targets' version in controlled by `Version.xcconfig`. diff --git a/Docs/AppIcon.png b/Docs/AppIcon.png deleted file mode 100644 index 88b20d1d..00000000 Binary files a/Docs/AppIcon.png and /dev/null differ diff --git a/Docs/accessibility-permission-request.png b/Docs/accessibility-permission-request.png deleted file mode 100644 index 302fd0b4..00000000 Binary files a/Docs/accessibility-permission-request.png and /dev/null differ diff --git a/Docs/accessibility-permission.png b/Docs/accessibility-permission.png deleted file mode 100644 index 0db1583a..00000000 Binary files a/Docs/accessibility-permission.png and /dev/null differ diff --git a/Docs/background-item.png b/Docs/background-item.png deleted file mode 100644 index 9eea5b3f..00000000 Binary files a/Docs/background-item.png and /dev/null differ diff --git a/Docs/background-permission-required.png b/Docs/background-permission-required.png deleted file mode 100644 index fb35d34b..00000000 Binary files a/Docs/background-permission-required.png and /dev/null differ diff --git a/Docs/chat_dark.gif b/Docs/chat_dark.gif deleted file mode 100644 index abd5cc20..00000000 Binary files a/Docs/chat_dark.gif and /dev/null differ diff --git a/Docs/connect-comm-bridge-failed.png b/Docs/connect-comm-bridge-failed.png deleted file mode 100644 index 4e8d2587..00000000 Binary files a/Docs/connect-comm-bridge-failed.png and /dev/null differ diff --git a/Docs/copilot-menu_dark.png b/Docs/copilot-menu_dark.png deleted file mode 100644 index 35b36e7b..00000000 Binary files a/Docs/copilot-menu_dark.png and /dev/null differ diff --git a/Docs/demo.gif b/Docs/demo.gif deleted file mode 100644 index a21db968..00000000 Binary files a/Docs/demo.gif and /dev/null differ diff --git a/Docs/device-code.png b/Docs/device-code.png deleted file mode 100644 index a345a732..00000000 Binary files a/Docs/device-code.png and /dev/null differ diff --git a/Docs/dmg-open.png b/Docs/dmg-open.png deleted file mode 100644 index cf50f7da..00000000 Binary files a/Docs/dmg-open.png and /dev/null differ diff --git a/Docs/extension-permission.png b/Docs/extension-permission.png deleted file mode 100644 index 6f613029..00000000 Binary files a/Docs/extension-permission.png and /dev/null differ diff --git a/Docs/macos-download-open-confirm.png b/Docs/macos-download-open-confirm.png deleted file mode 100644 index de58a9a6..00000000 Binary files a/Docs/macos-download-open-confirm.png and /dev/null differ diff --git a/Docs/signin-button.png b/Docs/signin-button.png deleted file mode 100644 index ac566c9b..00000000 Binary files a/Docs/signin-button.png and /dev/null differ diff --git a/Docs/update-message.png b/Docs/update-message.png deleted file mode 100644 index 35035861..00000000 Binary files a/Docs/update-message.png and /dev/null differ diff --git a/Docs/xcode-menu.png b/Docs/xcode-menu.png deleted file mode 100644 index c30e539c..00000000 Binary files a/Docs/xcode-menu.png and /dev/null differ diff --git a/Docs/xcode-menu_dark.png b/Docs/xcode-menu_dark.png deleted file mode 100644 index 28b957b7..00000000 Binary files a/Docs/xcode-menu_dark.png and /dev/null differ diff --git a/EditorExtension/AcceptPromptToCodeCommand.swift b/EditorExtension/AcceptPromptToCodeCommand.swift deleted file mode 100644 index 51bea4a4..00000000 --- a/EditorExtension/AcceptPromptToCodeCommand.swift +++ /dev/null @@ -1,31 +0,0 @@ -import Client -import Foundation -import SuggestionBasic -import XcodeKit - -class AcceptPromptToCodeCommand: NSObject, XCSourceEditorCommand, CommandType { - var name: String { "Accept Prompt to Code" } - - func perform( - with invocation: XCSourceEditorCommandInvocation, - completionHandler: @escaping (Error?) -> Void - ) { - Task { - do { - try await (Task(timeout: 7) { - let service = try getService() - if let content = try await service.getPromptToCodeAcceptedCode( - editorContent: .init(invocation) - ) { - invocation.accept(content) - } - completionHandler(nil) - }.value) - } catch is CancellationError { - completionHandler(nil) - } catch { - completionHandler(error) - } - } - } -} diff --git a/EditorExtension/AcceptSuggestionCommand.swift b/EditorExtension/AcceptSuggestionCommand.swift deleted file mode 100644 index a1ea71f6..00000000 --- a/EditorExtension/AcceptSuggestionCommand.swift +++ /dev/null @@ -1,33 +0,0 @@ -import Client -import SuggestionBasic -import Foundation -import XcodeKit -import XPCShared - -class AcceptSuggestionCommand: NSObject, XCSourceEditorCommand, CommandType { - var name: String { "Accept Suggestion" } - - func perform( - with invocation: XCSourceEditorCommandInvocation, - completionHandler: @escaping (Error?) -> Void - ) { - Task { - do { - try await (Task(timeout: 7) { - let service = try getService() - if let content = try await service.getSuggestionAcceptedCode( - editorContent: .init(invocation) - ) { - invocation.accept(content) - } - completionHandler(nil) - }.value) - } catch is CancellationError { - completionHandler(nil) - } catch { - completionHandler(error) - } - } - } -} - diff --git a/EditorExtension/CloseIdleTabsCommand.swift b/EditorExtension/CloseIdleTabsCommand.swift deleted file mode 100644 index 0e9537ee..00000000 --- a/EditorExtension/CloseIdleTabsCommand.swift +++ /dev/null @@ -1,20 +0,0 @@ -import Client -import Foundation -import SuggestionBasic -import XcodeKit - -class CloseIdleTabsCommand: NSObject, XCSourceEditorCommand, CommandType { - var name: String { "Close Idle Tabs" } - - func perform( - with invocation: XCSourceEditorCommandInvocation, - completionHandler: @escaping (Error?) -> Void - ) { - completionHandler(nil) - Task { - let service = try getService() - _ = try await service.postNotification(name: "CloseIdleTabsOfXcodeWindow") - } - } -} - diff --git a/EditorExtension/CustomCommand.swift b/EditorExtension/CustomCommand.swift deleted file mode 100644 index 0a43a51d..00000000 --- a/EditorExtension/CustomCommand.swift +++ /dev/null @@ -1,23 +0,0 @@ -import Client -import Foundation -import SuggestionBasic -import XcodeKit - -class CustomCommand: NSObject, XCSourceEditorCommand, CommandType { - var name: String = "" - - func perform( - with invocation: XCSourceEditorCommandInvocation, - completionHandler: @escaping (Error?) -> Void - ) { - completionHandler(nil) - Task { - let service = try getService() - _ = try await service.customCommand( - id: customCommandMap[invocation.commandIdentifier] ?? "", - editorContent: .init(invocation) - ) - } - } -} - diff --git a/EditorExtension/EditorExtension.entitlements b/EditorExtension/EditorExtension.entitlements deleted file mode 100644 index 776babcc..00000000 --- a/EditorExtension/EditorExtension.entitlements +++ /dev/null @@ -1,17 +0,0 @@ - - - - - com.apple.security.app-sandbox - - com.apple.security.application-groups - - $(TeamIdentifierPrefix)group.$(BUNDLE_IDENTIFIER_BASE) - - com.apple.security.temporary-exception.mach-lookup.global-name - - $(BUNDLE_IDENTIFIER_BASE).CommunicationBridge - $(BUNDLE_IDENTIFIER_BASE).ExtensionService - - - diff --git a/EditorExtension/GetSuggestionsCommand.swift b/EditorExtension/GetSuggestionsCommand.swift deleted file mode 100644 index 6be1c417..00000000 --- a/EditorExtension/GetSuggestionsCommand.swift +++ /dev/null @@ -1,20 +0,0 @@ -import Client -import Foundation -import SuggestionBasic -import XcodeKit - -class GetSuggestionsCommand: NSObject, XCSourceEditorCommand, CommandType { - var name: String { "Get Suggestions" } - - func perform( - with invocation: XCSourceEditorCommandInvocation, - completionHandler: @escaping (Error?) -> Void - ) { - completionHandler(nil) - Task { - let service = try getService() - _ = try await service.getSuggestedCode(editorContent: .init(invocation)) - } - } -} - diff --git a/EditorExtension/Helpers.swift b/EditorExtension/Helpers.swift deleted file mode 100644 index 8851c279..00000000 --- a/EditorExtension/Helpers.swift +++ /dev/null @@ -1,98 +0,0 @@ -import SuggestionBasic -import Foundation -import XcodeKit -import XPCShared - -extension XCSourceEditorCommandInvocation { - func mutateCompleteBuffer(modifications: [Modification], restoringSelections restore: Bool) { - if restore { - let selectionsRangesToRestore = buffer.selections - .compactMap { $0 as? XCSourceTextRange } - buffer.selections.removeAllObjects() - buffer.lines.apply(modifications) - for range in selectionsRangesToRestore { - buffer.selections.add(range) - } - } else { - buffer.lines.apply(modifications) - } - } - - func accept(_ updatedContent: UpdatedContent) { - if let newSelection = updatedContent.newSelection { - mutateCompleteBuffer( - modifications: updatedContent.modifications, - restoringSelections: false - ) - buffer.selections.removeAllObjects() - buffer.selections.add(XCSourceTextRange( - start: .init(line: newSelection.start.line, column: newSelection.start.character), - end: .init(line: newSelection.end.line, column: newSelection.end.character) - )) - } else { - mutateCompleteBuffer( - modifications: updatedContent.modifications, - restoringSelections: true - ) - } - } -} - -extension EditorContent { - init(_ invocation: XCSourceEditorCommandInvocation) { - let buffer = invocation.buffer - self.init( - content: buffer.completeBuffer, - lines: buffer.lines as? [String] ?? [], - uti: buffer.contentUTI, - cursorPosition: ((buffer.selections.lastObject as? XCSourceTextRange)?.end).map { - CursorPosition(line: $0.line, character: $0.column) - } ?? CursorPosition(line: 0, character: 0), - cursorOffset: -1, - selections: buffer.selections.map { - let sl = ($0 as? XCSourceTextRange)?.start.line ?? 0 - let sc = ($0 as? XCSourceTextRange)?.start.column ?? 0 - let el = ($0 as? XCSourceTextRange)?.end.line ?? 0 - let ec = ($0 as? XCSourceTextRange)?.end.column ?? 0 - - return Selection( - start: CursorPosition( line: sl, character: sc ), - end: CursorPosition( line: el, character: ec ) - ) - }, - tabSize: buffer.tabWidth, - indentSize: buffer.indentationWidth, - usesTabsForIndentation: buffer.usesTabsForIndentation - ) - } -} - -/// https://gist.github.com/swhitty/9be89dfe97dbb55c6ef0f916273bbb97 -extension Task where Failure == Error { - // Start a new Task with a timeout. If the timeout expires before the operation is - // completed then the task is cancelled and an error is thrown. - init( - priority: TaskPriority? = nil, - timeout: TimeInterval, - operation: @escaping @Sendable () async throws -> Success - ) { - self = Task(priority: priority) { - try await withThrowingTaskGroup(of: Success.self) { group -> Success in - group.addTask(operation: operation) - group.addTask { - try await _Concurrency.Task.sleep(nanoseconds: UInt64(timeout * 1_000_000_000)) - throw TimeoutError() - } - guard let success = try await group.next() else { - throw _Concurrency.CancellationError() - } - group.cancelAll() - return success - } - } - } -} - -private struct TimeoutError: LocalizedError { - var errorDescription: String? = "Task timed out before completion" -} diff --git a/EditorExtension/Info.plist b/EditorExtension/Info.plist deleted file mode 100644 index 13a9bdb6..00000000 --- a/EditorExtension/Info.plist +++ /dev/null @@ -1,48 +0,0 @@ - - - - - APPLICATION_SUPPORT_FOLDER - $(APPLICATION_SUPPORT_FOLDER) - BUNDLE_IDENTIFIER_BASE - $(BUNDLE_IDENTIFIER_BASE) - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - $(EXTENSION_BUNDLE_DISPLAY_NAME) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(EXTENSION_BUNDLE_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - $(MARKETING_VERSION) - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - HOST_APP_NAME - $(HOST_APP_NAME) - NSExtension - - NSExtensionAttributes - - XCSourceEditorCommandDefinitions - - XCSourceEditorExtensionPrincipalClass - $(PRODUCT_MODULE_NAME).SourceEditorExtension - - NSExtensionPointIdentifier - com.apple.dt.Xcode.extension.source-editor - - NSHumanReadableCopyright - - TEAM_ID_PREFIX - $(TeamIdentifierPrefix) - STANDARD_TELEMETRY_CHANNEL_KEY - $(STANDARD_TELEMETRY_CHANNEL_KEY) - - diff --git a/EditorExtension/NextSuggestionCommand.swift b/EditorExtension/NextSuggestionCommand.swift deleted file mode 100644 index f07f4017..00000000 --- a/EditorExtension/NextSuggestionCommand.swift +++ /dev/null @@ -1,20 +0,0 @@ -import Client -import Foundation -import SuggestionBasic -import XcodeKit - -class NextSuggestionCommand: NSObject, XCSourceEditorCommand, CommandType { - var name: String { "Next Suggestion" } - - func perform( - with invocation: XCSourceEditorCommandInvocation, - completionHandler: @escaping (Error?) -> Void - ) { - completionHandler(nil) - Task { - let service = try getService() - _ = try await service.getNextSuggestedCode(editorContent: .init(invocation)) - } - } -} - diff --git a/EditorExtension/OpenChat.swift b/EditorExtension/OpenChat.swift deleted file mode 100644 index 7ee1d945..00000000 --- a/EditorExtension/OpenChat.swift +++ /dev/null @@ -1,25 +0,0 @@ -import Client -import SuggestionBasic -import Foundation -import XcodeKit - -class OpenChatCommand: NSObject, XCSourceEditorCommand, CommandType { - var name: String { "Open Chat" } - - func perform( - with invocation: XCSourceEditorCommandInvocation, - completionHandler: @escaping (Error?) -> Void - ) { - Task { - do { - let service = try getService() - try await service.openChat() - completionHandler(nil) - } catch is CancellationError { - completionHandler(nil) - } catch { - completionHandler(error) - } - } - } -} diff --git a/EditorExtension/OpenSettingsCommand.swift b/EditorExtension/OpenSettingsCommand.swift deleted file mode 100644 index b1262c4b..00000000 --- a/EditorExtension/OpenSettingsCommand.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// OpenSettingsCommand.swift -// EditorExtension -// -// Opens the settings app -// - -import Foundation -import XcodeKit -import HostAppActivator - - - -class OpenSettingsCommand: NSObject, XCSourceEditorCommand, CommandType { - var name: String { "Open \(hostAppName()) Settings" } - - func perform( - with invocation: XCSourceEditorCommandInvocation, - completionHandler: @escaping (Error?) -> Void - ) { - Task { - do { - try launchHostAppSettings() - completionHandler(nil) - } catch { - completionHandler( - GitHubCopilotForXcodeSettingsLaunchError - .openFailed( - errorDescription: error.localizedDescription - ) - ) - } - } - } -} - -func hostAppName() -> String { - return Bundle.main.object(forInfoDictionaryKey: "HOST_APP_NAME") as? String - ?? "GitHub Copilot for Xcode" -} diff --git a/EditorExtension/PrefetchSuggestionsCommand.swift b/EditorExtension/PrefetchSuggestionsCommand.swift deleted file mode 100644 index bc43c40e..00000000 --- a/EditorExtension/PrefetchSuggestionsCommand.swift +++ /dev/null @@ -1,19 +0,0 @@ -import Client -import SuggestionBasic -import Foundation -import XcodeKit - -class PrefetchSuggestionsCommand: NSObject, XCSourceEditorCommand, CommandType { - var name: String { "Prefetch Suggestions" } - - func perform( - with invocation: XCSourceEditorCommandInvocation, - completionHandler: @escaping (Error?) -> Void - ) { - completionHandler(nil) - Task { - let service = try getService() - await service.prefetchRealtimeSuggestions(editorContent: .init(invocation)) - } - } -} diff --git a/EditorExtension/PreviousSuggestionCommand.swift b/EditorExtension/PreviousSuggestionCommand.swift deleted file mode 100644 index 61894bab..00000000 --- a/EditorExtension/PreviousSuggestionCommand.swift +++ /dev/null @@ -1,20 +0,0 @@ -import Client -import Foundation -import SuggestionBasic -import XcodeKit - -class PreviousSuggestionCommand: NSObject, XCSourceEditorCommand, CommandType { - var name: String { "Previous Suggestion" } - - func perform( - with invocation: XCSourceEditorCommandInvocation, - completionHandler: @escaping (Error?) -> Void - ) { - completionHandler(nil) - Task { - let service = try getService() - _ = try await service.getPreviousSuggestedCode(editorContent: .init(invocation)) - } - } -} - diff --git a/EditorExtension/PromptToCodeCommand.swift b/EditorExtension/PromptToCodeCommand.swift deleted file mode 100644 index 13e4f3be..00000000 --- a/EditorExtension/PromptToCodeCommand.swift +++ /dev/null @@ -1,19 +0,0 @@ -import Client -import SuggestionBasic -import Foundation -import XcodeKit - -class PromptToCodeCommand: NSObject, XCSourceEditorCommand, CommandType { - var name: String { "Prompt to Code" } - - func perform( - with invocation: XCSourceEditorCommandInvocation, - completionHandler: @escaping (Error?) -> Void - ) { - completionHandler(nil) - Task { - let service = try getService() - _ = try await service.promptToCode(editorContent: .init(invocation)) - } - } -} diff --git a/EditorExtension/RejectSuggestionCommand.swift b/EditorExtension/RejectSuggestionCommand.swift deleted file mode 100644 index d1091237..00000000 --- a/EditorExtension/RejectSuggestionCommand.swift +++ /dev/null @@ -1,20 +0,0 @@ -import Client -import Foundation -import SuggestionBasic -import XcodeKit - -class RejectSuggestionCommand: NSObject, XCSourceEditorCommand, CommandType { - var name: String { "Decline Suggestion" } - - func perform( - with invocation: XCSourceEditorCommandInvocation, - completionHandler: @escaping (Error?) -> Void - ) { - completionHandler(nil) - Task { - let service = try getService() - _ = try await service.getSuggestionRejectedCode(editorContent: .init(invocation)) - } - } -} - diff --git a/EditorExtension/SeparatorCommand.swift b/EditorExtension/SeparatorCommand.swift deleted file mode 100644 index 79e4b138..00000000 --- a/EditorExtension/SeparatorCommand.swift +++ /dev/null @@ -1,20 +0,0 @@ -import Client -import SuggestionBasic -import Foundation -import XcodeKit - -class SeparatorCommand: NSObject, XCSourceEditorCommand, CommandType { - var name: String = "" - - func perform( - with invocation: XCSourceEditorCommandInvocation, - completionHandler: @escaping (Error?) -> Void - ) { - completionHandler(nil) - } - - func named(_ name: String) -> Self { - self.name = name - return self - } -} diff --git a/EditorExtension/SourceEditorExtension.swift b/EditorExtension/SourceEditorExtension.swift deleted file mode 100644 index a9d252f9..00000000 --- a/EditorExtension/SourceEditorExtension.swift +++ /dev/null @@ -1,90 +0,0 @@ -import Client -import Foundation -import GitHubCopilotService -import Preferences -import XcodeKit - -#if canImport(PreferencesPlus) -import PreferencesPlus -#endif - -class SourceEditorExtension: NSObject, XCSourceEditorExtension { - var builtin: [[XCSourceEditorCommandDefinitionKey: Any]] { - [ - AcceptSuggestionCommand(), - RejectSuggestionCommand(), - GetSuggestionsCommand(), - NextSuggestionCommand(), - PreviousSuggestionCommand(), - SyncTextSettingsCommand(), - ToggleRealtimeSuggestionsCommand(), - ].map(makeCommandDefinition) - } - - var chat: [[XCSourceEditorCommandDefinitionKey: Any]] { - [ - OpenChatCommand() - ].map(makeCommandDefinition) - } - - var additionalBuiltin: [[XCSourceEditorCommandDefinitionKey: Any]] { - [ - OpenSettingsCommand(), - ].map(makeCommandDefinition) - } - - var commandDefinitions: [[XCSourceEditorCommandDefinitionKey: Any]] { - var definitions = builtin - - if FeatureFlagNotifierImpl.shared.featureFlags.chat { - definitions += chat - } - - definitions += additionalBuiltin - - return definitions - } - - func extensionDidFinishLaunching() { - #if DEBUG - // In a debug build, we usually want to use the XPC service run from Xcode. - #else - // When the source extension is initialized - // we can call a random command to wake up the XPC service. - Task.detached { - try await Task.sleep(nanoseconds: 1_000_000_000) - let service = try getService() - _ = try await service.getXPCServiceVersion() - } - #endif - } -} - -let identifierPrefix: String = Bundle.main.bundleIdentifier ?? "" - -var customCommandMap = [String: String]() - -protocol CommandType: AnyObject { - var commandClassName: String { get } - var identifier: String { get } - var name: String { get } -} - -extension CommandType where Self: NSObject { - var commandClassName: String { Self.className() } - var identifier: String { commandClassName } -} - -extension CommandType { - func makeCommandDefinition() -> [XCSourceEditorCommandDefinitionKey: Any] { - [.classNameKey: commandClassName, - .identifierKey: identifierPrefix + identifier, - .nameKey: name] - } -} - -func makeCommandDefinition(_ commandType: CommandType) - -> [XCSourceEditorCommandDefinitionKey: Any] -{ - commandType.makeCommandDefinition() -} diff --git a/EditorExtension/SyncTextSettingsCommand.swift b/EditorExtension/SyncTextSettingsCommand.swift deleted file mode 100644 index f1c54561..00000000 --- a/EditorExtension/SyncTextSettingsCommand.swift +++ /dev/null @@ -1,19 +0,0 @@ -import Client -import SuggestionBasic -import Foundation -import XcodeKit - -class SyncTextSettingsCommand: NSObject, XCSourceEditorCommand, CommandType { - var name: String { "Sync Text Settings" } - - func perform( - with invocation: XCSourceEditorCommandInvocation, - completionHandler: @escaping (Error?) -> Void - ) { - completionHandler(nil) - Task { - let service = try getService() - _ = try await service.getRealtimeSuggestedCode(editorContent: .init(invocation)) - } - } -} diff --git a/EditorExtension/ToggleRealtimeSuggestionsCommand.swift b/EditorExtension/ToggleRealtimeSuggestionsCommand.swift deleted file mode 100644 index 690143da..00000000 --- a/EditorExtension/ToggleRealtimeSuggestionsCommand.swift +++ /dev/null @@ -1,25 +0,0 @@ -import Client -import SuggestionBasic -import Foundation -import XcodeKit - -class ToggleRealtimeSuggestionsCommand: NSObject, XCSourceEditorCommand, CommandType { - var name: String { "Enable/Disable Completions" } - - func perform( - with invocation: XCSourceEditorCommandInvocation, - completionHandler: @escaping (Error?) -> Void - ) { - Task { - do { - let service = try getService() - try await service.toggleRealtimeSuggestion() - completionHandler(nil) - } catch is CancellationError { - completionHandler(nil) - } catch { - completionHandler(error) - } - } - } -} diff --git a/ExtensionPoint.appextensionpoint b/ExtensionPoint.appextensionpoint deleted file mode 100644 index 31f4275c..00000000 --- a/ExtensionPoint.appextensionpoint +++ /dev/null @@ -1,11 +0,0 @@ - - - - - com.github.CopilotForXcode.ExtensionService.Extension - - EXPresentsUserInterface - - - - diff --git a/ExtensionService/AppDelegate+Menu.swift b/ExtensionService/AppDelegate+Menu.swift deleted file mode 100644 index 4dfc0da1..00000000 --- a/ExtensionService/AppDelegate+Menu.swift +++ /dev/null @@ -1,391 +0,0 @@ -import AppKit -import Foundation -import Preferences -import Status -import SuggestionBasic -import XcodeInspector -import Logger -import StatusBarItemView -import GitHubCopilotViewModel - -extension AppDelegate { - fileprivate var statusBarMenuIdentifier: NSUserInterfaceItemIdentifier { - .init("statusBarMenu") - } - - fileprivate var xcodeInspectorDebugMenuIdentifier: NSUserInterfaceItemIdentifier { - .init("xcodeInspectorDebugMenu") - } - - fileprivate var sourceEditorDebugMenu: NSUserInterfaceItemIdentifier { - .init("sourceEditorDebugMenu") - } - - @MainActor - @objc func buildStatusBarMenu() { - let statusBar = NSStatusBar.system - statusBarItem = statusBar.statusItem( - withLength: NSStatusItem.squareLength - ) - statusBarItem.button?.image = NSImage(named: "MenuBarIcon") - - let statusBarMenu = NSMenu(title: "Status Bar Menu") - statusBarMenu.identifier = statusBarMenuIdentifier - statusBarItem.menu = statusBarMenu - - let checkForUpdate = NSMenuItem( - title: "Check for Updates", - action: #selector(checkForUpdate), - keyEquivalent: "" - ) - - openCopilotForXcodeItem = NSMenuItem( - title: "Settings", - action: #selector(openCopilotForXcodeSettings), - keyEquivalent: "" - ) - - let xcodeInspectorDebug = NSMenuItem( - title: "Xcode Inspector Debug", - action: nil, - keyEquivalent: "" - ) - - let xcodeInspectorDebugMenu = NSMenu(title: "Xcode Inspector Debug") - xcodeInspectorDebugMenu.identifier = xcodeInspectorDebugMenuIdentifier - xcodeInspectorDebug.submenu = xcodeInspectorDebugMenu - xcodeInspectorDebug.isHidden = false - - axStatusItem = NSMenuItem( - title: "", - action: #selector(openAXStatusLink), - keyEquivalent: "" - ) - axStatusItem.isHidden = true - - extensionStatusItem = NSMenuItem( - title: "", - action: #selector(openExtensionStatusLink), - keyEquivalent: "" - ) - extensionStatusItem.isHidden = true - - let quitItem = NSMenuItem( - title: "Quit", - action: #selector(quit), - keyEquivalent: "" - ) - quitItem.target = self - - toggleCompletions = NSMenuItem( - title: "Enable/Disable Completions", - action: #selector(toggleCompletionsEnabled), - keyEquivalent: "" - ) - - toggleIgnoreLanguage = NSMenuItem( - title: "No Active Document", - action: nil, - keyEquivalent: "" - ) - - // Auth menu item with custom view - accountItem = NSMenuItem() - accountItem.view = AccountItemView( - target: self, - action: #selector(signIntoGitHub) - ) - - authStatusItem = NSMenuItem( - title: "", - action: nil, - keyEquivalent: "" - ) - authStatusItem.isHidden = true - - quotaItem = NSMenuItem() - quotaItem.view = QuotaView( - chat: .init( - percentRemaining: 0, - unlimited: false, - overagePermitted: false - ), - completions: .init( - percentRemaining: 0, - unlimited: false, - overagePermitted: false - ), - premiumInteractions: .init( - percentRemaining: 0, - unlimited: false, - overagePermitted: false - ), - resetDate: "", - copilotPlan: "" - ) - quotaItem.isHidden = true - - let openDocs = NSMenuItem( - title: "View Documentation", - action: #selector(openCopilotDocs), - keyEquivalent: "" - ) - - let openForum = NSMenuItem( - title: "Feedback Forum", - action: #selector(openCopilotForum), - keyEquivalent: "" - ) - - openChat = NSMenuItem( - title: "Open Chat", - action: #selector(openGlobalChat), - keyEquivalent: "" - ) - - signOutItem = NSMenuItem( - title: "Sign Out", - action: #selector(signOutGitHub), - keyEquivalent: "" - ) - - statusBarMenu.addItem(accountItem) - statusBarMenu.addItem(.separator()) - statusBarMenu.addItem(authStatusItem) - statusBarMenu.addItem(.separator()) - statusBarMenu.addItem(quotaItem) - statusBarMenu.addItem(.separator()) - statusBarMenu.addItem(axStatusItem) - statusBarMenu.addItem(extensionStatusItem) - statusBarMenu.addItem(.separator()) - statusBarMenu.addItem(checkForUpdate) - statusBarMenu.addItem(.separator()) - statusBarMenu.addItem(openChat) - statusBarMenu.addItem(toggleCompletions) - statusBarMenu.addItem(toggleIgnoreLanguage) - statusBarMenu.addItem(.separator()) - statusBarMenu.addItem(openCopilotForXcodeItem) - statusBarMenu.addItem(openDocs) - statusBarMenu.addItem(openForum) - statusBarMenu.addItem(.separator()) - statusBarMenu.addItem(signOutItem) - statusBarMenu.addItem(.separator()) - statusBarMenu.addItem(xcodeInspectorDebug) - statusBarMenu.addItem(quitItem) - - statusBarMenu.delegate = self - xcodeInspectorDebugMenu.delegate = self - } -} - -extension AppDelegate: NSMenuDelegate { - func menuWillOpen(_ menu: NSMenu) { - switch menu.identifier { - case statusBarMenuIdentifier: - if let xcodeInspectorDebug = menu.items.first(where: { item in - item.submenu?.identifier == xcodeInspectorDebugMenuIdentifier - }) { - xcodeInspectorDebug.isHidden = !UserDefaults.shared - .value(for: \.enableXcodeInspectorDebugMenu) - } - - if toggleCompletions != nil { - toggleCompletions.title = "\(UserDefaults.shared.value(for: \.realtimeSuggestionToggle) ? "Disable" : "Enable") Completions" - } - - if toggleIgnoreLanguage != nil { - if let lang = DisabledLanguageList.shared.activeDocumentLanguage { - toggleIgnoreLanguage.title = "\(DisabledLanguageList.shared.isEnabled(lang) ? "Disable" : "Enable") Completions for \(lang.rawValue)" - toggleIgnoreLanguage.action = #selector( - toggleIgnoreLanguageEnabled - ) - } else { - toggleIgnoreLanguage.title = "No Active Document" - toggleIgnoreLanguage.action = nil - } - } - - Task { - await forceAuthStatusCheck() - updateStatusBarItem() - } - - case xcodeInspectorDebugMenuIdentifier: - let inspector = XcodeInspector.shared - menu.items.removeAll() - menu.items - .append(.text("Active Project: \(inspector.activeProjectRootURL?.path ?? "N/A")")) - menu.items - .append(.text("Active Workspace: \(inspector.activeWorkspaceURL?.path ?? "N/A")")) - menu.items - .append(.text("Active Document: \(inspector.activeDocumentURL?.path ?? "N/A")")) - - if let focusedWindow = inspector.focusedWindow { - menu.items.append(.text( - "Active Window: \(focusedWindow.uiElement.identifier)" - )) - } else { - menu.items.append(.text("Active Window: N/A")) - } - - if let focusedElement = inspector.focusedElement { - menu.items.append(.text( - "Focused Element: \(focusedElement.description)" - )) - } else { - menu.items.append(.text("Focused Element: N/A")) - } - - if let sourceEditor = inspector.focusedEditor { - let label = sourceEditor.element.description - menu.items - .append(.text("Active Source Editor: \(label.isEmpty ? "Unknown" : label)")) - } else { - menu.items.append(.text("Active Source Editor: N/A")) - } - - menu.items.append(.separator()) - - for xcode in inspector.xcodes { - let item = NSMenuItem( - title: "Xcode \(xcode.processIdentifier)", - action: nil, - keyEquivalent: "" - ) - menu.addItem(item) - let xcodeMenu = NSMenu() - item.submenu = xcodeMenu - xcodeMenu.items.append(.text("Is Active: \(xcode.isActive)")) - xcodeMenu.items - .append(.text("Active Project: \(xcode.projectRootURL?.path ?? "N/A")")) - xcodeMenu.items - .append(.text("Active Workspace: \(xcode.workspaceURL?.path ?? "N/A")")) - xcodeMenu.items - .append(.text("Active Document: \(xcode.documentURL?.path ?? "N/A")")) - - for (key, workspace) in xcode.realtimeWorkspaces { - let workspaceItem = NSMenuItem( - title: "Workspace \(key)", - action: nil, - keyEquivalent: "" - ) - xcodeMenu.items.append(workspaceItem) - let workspaceMenu = NSMenu() - workspaceItem.submenu = workspaceMenu - let tabsItem = NSMenuItem( - title: "Tabs", - action: nil, - keyEquivalent: "" - ) - workspaceMenu.addItem(tabsItem) - let tabsMenu = NSMenu() - tabsItem.submenu = tabsMenu - for tab in workspace.tabs { - tabsMenu.addItem(.text(tab)) - } - } - } - - menu.items.append(.separator()) - - menu.items.append(NSMenuItem( - title: "Restart Xcode Inspector", - action: #selector(restartXcodeInspector), - keyEquivalent: "" - )) - - default: - break - } - } -} - -import XPCShared - -private extension AppDelegate { - @objc func restartXcodeInspector() { - Task { - await XcodeInspector.shared.restart(cleanUp: true) - } - } - - @objc func toggleCompletionsEnabled() { - Task { - let initialSetting = UserDefaults.shared.value(for: \.realtimeSuggestionToggle) - do { - let service = getXPCExtensionService() - try await service.toggleRealtimeSuggestion() - } catch { - Logger.service.error("Failed to toggle completions enabled via XPC: \(error)") - UserDefaults.shared.set(!initialSetting, for: \.realtimeSuggestionToggle) - } - } - } - - @objc func toggleIgnoreLanguageEnabled() { - guard let lang = DisabledLanguageList.shared.activeDocumentLanguage else { return } - - if DisabledLanguageList.shared.isEnabled(lang) { - DisabledLanguageList.shared.disable(lang) - } else { - DisabledLanguageList.shared.enable(lang) - } - } - - @objc func openCopilotDocs() { - if let urlString = Bundle.main.object(forInfoDictionaryKey: "COPILOT_DOCS_URL") as? String { - if let url = URL(string: urlString) { - NSWorkspace.shared.open(url) - } - } - } - - @objc func openCopilotForum() { - if let urlString = Bundle.main.object(forInfoDictionaryKey: "COPILOT_FORUM_URL") as? String { - if let url = URL(string: urlString) { - NSWorkspace.shared.open(url) - } - } - } - - @objc func openAXStatusLink() { - Task { - if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility") { - NSWorkspace.shared.open(url) - } - } - } - - @objc func openExtensionStatusLink() { - Task { - let status = await Status.shared.getExtensionStatus() - if status == .notGranted { - if let url = URL(string: "x-apple.systempreferences:com.apple.ExtensionsPreferences?extensionPointIdentifier=com.apple.dt.Xcode.extension.source-editor") { - NSWorkspace.shared.open(url) - } - } else { - NSWorkspace.restartXcode() - } - } - } - - @objc func openUpSellLink() { - Task { - if let url = URL(string: "https://aka.ms/github-copilot-settings") { - NSWorkspace.shared.open(url) - } - } - } -} - -private extension NSMenuItem { - static func text(_ text: String) -> NSMenuItem { - let item = NSMenuItem( - title: text, - action: nil, - keyEquivalent: "" - ) - item.isEnabled = false - return item - } -} diff --git a/ExtensionService/AppDelegate.swift b/ExtensionService/AppDelegate.swift deleted file mode 100644 index 7f89e6cf..00000000 --- a/ExtensionService/AppDelegate.swift +++ /dev/null @@ -1,541 +0,0 @@ -import Combine -import FileChangeChecker -import GitHubCopilotService -import LaunchAgentManager -import Logger -import Preferences -import Service -import ServiceManagement -import Status -import SwiftUI -import UpdateChecker -import UserDefaultsObserver -import UserNotifications -import XcodeInspector -import XPCShared -import GitHubCopilotViewModel -import StatusBarItemView -import HostAppActivator - -let bundleIdentifierBase = Bundle.main - .object(forInfoDictionaryKey: "BUNDLE_IDENTIFIER_BASE") as! String -let serviceIdentifier = bundleIdentifierBase + ".ExtensionService" - -class ExtensionUpdateCheckerDelegate: UpdateCheckerDelegate { - func prepareForRelaunch(finish: @escaping () -> Void) { - Task { - await Service.shared.prepareForExit() - finish() - } - } -} - -@main -class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate { - let service = Service.shared - var statusBarItem: NSStatusItem! - var axStatusItem: NSMenuItem! - var extensionStatusItem: NSMenuItem! - var openCopilotForXcodeItem: NSMenuItem! - var accountItem: NSMenuItem! - var authStatusItem: NSMenuItem! - var quotaItem: NSMenuItem! - var toggleCompletions: NSMenuItem! - var toggleIgnoreLanguage: NSMenuItem! - var openChat: NSMenuItem! - var signOutItem: NSMenuItem! - var xpcController: XPCController? - let updateChecker = - UpdateChecker( - hostBundle: Bundle(url: HostAppURL!), - checkerDelegate: ExtensionUpdateCheckerDelegate() - ) - var xpcExtensionService: XPCExtensionService? - private var cancellables = Set() - private var progressView: NSProgressIndicator? - - func applicationDidFinishLaunching(_: Notification) { - if ProcessInfo.processInfo.environment["IS_UNIT_TEST"] == "YES" { return } - _ = XcodeInspector.shared - service.start() - AXIsProcessTrustedWithOptions([ - kAXTrustedCheckOptionPrompt.takeRetainedValue() as NSString: true, - ] as CFDictionary) - setupQuitOnUpdate() - setupQuitOnUserTerminated() - xpcController = .init() - Logger.service.info("XPC Service started.") - NSApp.setActivationPolicy(.accessory) - buildStatusBarMenu() - watchServiceStatus() - watchAXStatus() - watchAuthStatus() - setInitialStatusBarStatus() - UserDefaults.shared.set(false, for: \.clsWarningDismissedUntilRelaunch) - } - - @objc func quit() { - if let hostApp = getRunningHostApp() { - hostApp.terminate() - } - - // Start shutdown process in a task - Task { @MainActor in - await service.prepareForExit() - await xpcController?.quit() - NSApp.terminate(self) - } - } - - @objc func openCopilotForXcodeSettings() { - try? launchHostAppSettings() - } - - @objc func signIntoGitHub() { - Task { @MainActor in - let viewModel = GitHubCopilotViewModel.shared - // Don't trigger the shared viewModel's alert - do { - guard let signInResponse = try await viewModel.preSignIn() else { - return - } - - NSApp.activate(ignoringOtherApps: true) - let alert = NSAlert() - alert.messageText = signInResponse.userCode - alert.informativeText = """ - Please enter the above code in the GitHub website to authorize your \ - GitHub account with Copilot for Xcode. - \(signInResponse.verificationURL.absoluteString) - """ - alert.addButton(withTitle: "Copy Code and Open") - alert.addButton(withTitle: "Cancel") - - let response = alert.runModal() - if response == .alertFirstButtonReturn { - viewModel.signInResponse = signInResponse - viewModel.copyAndOpen() - } - } catch { - Logger.service.error("GitHub copilot view model Sign in fails: \(error)") - } - } - } - - @objc func signOutGitHub() { - Task { @MainActor in - let viewModel = GitHubCopilotViewModel.shared - viewModel.signOut() - } - } - - @objc func openGlobalChat() { - Task { @MainActor in - let serviceGUI = Service.shared.guiController - serviceGUI.openGlobalChat() - } - } - - func setupQuitOnUpdate() { - Task { - guard let url = Bundle.main.executableURL else { return } - let checker = await FileChangeChecker(fileURL: url) - - // If Xcode or Copilot for Xcode is made active, check if the executable of this program - // is changed. If changed, quit this program. - - let sequence = NSWorkspace.shared.notificationCenter - .notifications(named: NSWorkspace.didActivateApplicationNotification) - for await notification in sequence { - try Task.checkCancellation() - guard let app = notification - .userInfo?[NSWorkspace.applicationUserInfoKey] as? NSRunningApplication, - app.isUserOfService - else { continue } - guard await checker.checkIfChanged() else { - Logger.service.info("Extension Service is not updated, no need to quit.") - continue - } - Logger.service.info("Extension Service will quit.") - #if DEBUG - #else - quit() - #endif - } - } - } - - func setupQuitOnUserTerminated() { - Task { - // Whenever Xcode or the host application quits, check if any of the two is running. - // If none, quit the XPC service. - - let sequence = NSWorkspace.shared.notificationCenter - .notifications(named: NSWorkspace.didTerminateApplicationNotification) - for await notification in sequence { - try Task.checkCancellation() - guard UserDefaults.shared.value(for: \.quitXPCServiceOnXcodeAndAppQuit) - else { continue } - guard let app = notification - .userInfo?[NSWorkspace.applicationUserInfoKey] as? NSRunningApplication, - app.isUserOfService - else { continue } - - // Check if Xcode is running - let isXcodeRunning = NSWorkspace.shared.runningApplications.contains { - $0.bundleIdentifier == "com.apple.dt.Xcode" - } - - if !isXcodeRunning { - Logger.client.info("No Xcode instances running, preparing to quit") - quit() - } - } - } - } - - func requestAccessoryAPIPermission() { - AXIsProcessTrustedWithOptions([ - kAXTrustedCheckOptionPrompt.takeRetainedValue() as NSString: true, - ] as NSDictionary) - } - - @objc func checkForUpdate() { - guard let updateChecker = updateChecker else { - Logger.service.error("Unable to check for updates: updateChecker is nil.") - return - } - updateChecker.checkForUpdates() - } - - func getXPCExtensionService() -> XPCExtensionService { - if let service = xpcExtensionService { return service } - let service = XPCExtensionService(logger: .service) - xpcExtensionService = service - return service - } - - func watchServiceStatus() { - let notifications = NotificationCenter.default.notifications(named: .serviceStatusDidChange) - Task { [weak self] in - for await _ in notifications { - guard let self else { return } - self.updateStatusBarItem() - } - } - } - - func watchAXStatus() { - let osNotifications = DistributedNotificationCenter.default().notifications(named: NSNotification.Name("com.apple.accessibility.api")) - Task { [weak self] in - for await _ in osNotifications { - guard let self else { return } - self.updateStatusBarItem() - } - } - } - - func watchAuthStatus() { - let notifications = DistributedNotificationCenter.default().notifications(named: .authStatusDidChange) - Task { [weak self] in - for await _ in notifications { - guard self != nil else { return } - do { - let service = try await GitHubCopilotViewModel.shared.getGitHubCopilotAuthService() - let accountStatus = try await service.checkStatus() - if accountStatus == .notSignedIn { - try await GitHubCopilotService.signOutAll() - } - } catch { - Logger.service.error("Failed to watch auth status: \(error)") - } - } - } - } - - func setInitialStatusBarStatus() { - Task { - let authStatus = await Status.shared.getAuthStatus() - if authStatus.status == .unknown { - // temporarily kick off a language server instance to prime the initial auth status - await forceAuthStatusCheck() - } - updateStatusBarItem() - } - } - - func forceAuthStatusCheck() async { - do { - let service = try await GitHubCopilotViewModel.shared.getGitHubCopilotAuthService() - let accountStatus = try await service.checkStatus() - if accountStatus == .ok || accountStatus == .maybeOk { - let quota = try await service.checkQuota() - Logger.service.info("User quota checked successfully: \(quota)") - } - } catch { - Logger.service.error("Failed to read auth status: \(error)") - } - } - - private func configureNotLoggedIn() { - self.accountItem.view = AccountItemView( - target: self, - action: #selector(signIntoGitHub) - ) - self.authStatusItem.isHidden = true - self.quotaItem.isHidden = true - self.toggleCompletions.isHidden = true - self.toggleIgnoreLanguage.isHidden = true - self.signOutItem.isHidden = true - } - - private func configureLoggedIn(status: StatusResponse) { - self.accountItem.view = AccountItemView( - target: self, - action: nil, - userName: status.userName ?? "" - ) - if !status.clsMessage.isEmpty { - let CLSMessageSummary = getCLSMessageSummary(status.clsMessage) - // If the quota is nil, keep the original auth status item - // Else only log the CLS error other than quota limit reached error - if CLSMessageSummary.summary == CLSMessageType.other.summary || status.quotaInfo == nil { - self.authStatusItem.isHidden = false - self.authStatusItem.title = CLSMessageSummary.summary - - let submenu = NSMenu() - let attributedCLSErrorItem = NSMenuItem() - attributedCLSErrorItem.view = ErrorMessageView( - errorMessage: CLSMessageSummary.detail - ) - submenu.addItem(attributedCLSErrorItem) - submenu.addItem(.separator()) - submenu.addItem( - NSMenuItem( - title: "View Details on GitHub", - action: #selector(openGitHubDetailsLink), - keyEquivalent: "" - ) - ) - - self.authStatusItem.submenu = submenu - self.authStatusItem.isEnabled = true - } - } else { - self.authStatusItem.isHidden = true - } - - if let quotaInfo = status.quotaInfo, !quotaInfo.resetDate.isEmpty { - self.quotaItem.isHidden = false - self.quotaItem.view = QuotaView( - chat: .init( - percentRemaining: quotaInfo.chat.percentRemaining, - unlimited: quotaInfo.chat.unlimited, - overagePermitted: quotaInfo.chat.overagePermitted - ), - completions: .init( - percentRemaining: quotaInfo.completions.percentRemaining, - unlimited: quotaInfo.completions.unlimited, - overagePermitted: quotaInfo.completions.overagePermitted - ), - premiumInteractions: .init( - percentRemaining: quotaInfo.premiumInteractions.percentRemaining, - unlimited: quotaInfo.premiumInteractions.unlimited, - overagePermitted: quotaInfo.premiumInteractions.overagePermitted - ), - resetDate: quotaInfo.resetDate, - copilotPlan: quotaInfo.copilotPlan - ) - } else { - self.quotaItem.isHidden = true - } - - self.toggleCompletions.isHidden = false - self.toggleIgnoreLanguage.isHidden = false - self.signOutItem.isHidden = false - } - - private func configureNotAuthorized(status: StatusResponse) { - self.accountItem.view = AccountItemView( - target: self, - action: nil, - userName: status.userName ?? "" - ) - self.authStatusItem.isHidden = false - self.authStatusItem.title = "No Subscription" - - let submenu = NSMenu() - let attributedNotAuthorizedItem = NSMenuItem() - attributedNotAuthorizedItem.view = ErrorMessageView( - errorMessage: "GitHub Copilot features are disabled. Check your subscription to enable them." - ) - attributedNotAuthorizedItem.isEnabled = true - submenu.addItem(attributedNotAuthorizedItem) - - self.authStatusItem.submenu = submenu - self.authStatusItem.isEnabled = true - - self.quotaItem.isHidden = true - self.toggleCompletions.isHidden = true - self.toggleIgnoreLanguage.isHidden = true - self.signOutItem.isHidden = false - } - - private func configureUnknown() { - self.accountItem.view = AccountItemView( - target: self, - action: nil, - userName: "Unknown User" - ) - self.authStatusItem.isHidden = true - self.quotaItem.isHidden = true - self.toggleCompletions.isHidden = false - self.toggleIgnoreLanguage.isHidden = false - self.signOutItem.isHidden = false - } - - func updateStatusBarItem() { - Task { @MainActor in - let status = await Status.shared.getStatus() - /// Update status bar icon - self.statusBarItem.button?.image = status.icon.nsImage - - /// Update auth status related status bar items - switch status.authStatus { - case .notLoggedIn: configureNotLoggedIn() - case .loggedIn: configureLoggedIn(status: status) - case .notAuthorized: configureNotAuthorized(status: status) - case .unknown: configureUnknown() - } - - /// Update accessibility permission status bar item - let exclamationmarkImage = NSImage( - systemSymbolName: "exclamationmark.circle.fill", - accessibilityDescription: "Permission not granted" - ) - exclamationmarkImage?.isTemplate = false - exclamationmarkImage?.withSymbolConfiguration(.init(paletteColors: [.red])) - - if let message = status.message { - self.axStatusItem.title = message - if let image = exclamationmarkImage { - self.axStatusItem.image = image - } - self.axStatusItem.isHidden = false - self.axStatusItem.isEnabled = status.url != nil - } else { - self.axStatusItem.isHidden = true - } - - /// Update settings status bar item - if status.extensionStatus == .disabled || status.extensionStatus == .notGranted { - if let image = exclamationmarkImage{ - if #available(macOS 15.0, *){ - self.extensionStatusItem.image = image - self.extensionStatusItem.title = status.extensionStatus == .notGranted ? "Enable extension for full-featured completion" : "Quit and restart Xcode to enable extension" - self.extensionStatusItem.isHidden = false - self.extensionStatusItem.isEnabled = status.extensionStatus == .notGranted - } else { - self.extensionStatusItem.isHidden = true - self.openCopilotForXcodeItem.image = image - } - } - } else { - self.openCopilotForXcodeItem.image = nil - self.extensionStatusItem.isHidden = true - } - self.markAsProcessing(status.inProgress) - } - } - - func markAsProcessing(_ isProcessing: Bool) { - if !isProcessing { - // No longer in progress - progressView?.removeFromSuperview() - progressView = nil - return - } - if progressView != nil { - // Already in progress - return - } - let progress = NSProgressIndicator() - progress.style = .spinning - progress.sizeToFit() - progress.frame = statusBarItem.button?.bounds ?? .zero - progress.isIndeterminate = true - progress.startAnimation(nil) - statusBarItem.button?.addSubview(progress) - statusBarItem.button?.image = nil - progressView = progress - } - - @objc func openGitHubDetailsLink() { - Task { - if let url = URL(string: "https://github.com/copilot") { - NSWorkspace.shared.open(url) - } - } - } -} - -extension NSRunningApplication { - var isUserOfService: Bool { - [ - "com.apple.dt.Xcode", - bundleIdentifierBase, - ].contains(bundleIdentifier) - } -} - -enum CLSMessageType { - case chatLimitReached - case completionLimitReached - case other - - var summary: String { - switch self { - case .chatLimitReached: - return "Monthly Chat Limit Reached" - case .completionLimitReached: - return "Monthly Completion Limit Reached" - case .other: - return "CLS Error" - } - } -} - -struct CLSMessage { - let summary: String - let detail: String -} - -func extractDateFromCLSMessage(_ message: String) -> String? { - let pattern = #"until (\d{1,2}/\d{1,2}/\d{4}, \d{1,2}:\d{2}:\d{2} [AP]M)"# - if let range = message.range(of: pattern, options: .regularExpression) { - return String(message[range].dropFirst(6)) - } - return nil -} - -func getCLSMessageSummary(_ message: String) -> CLSMessage { - let messageType: CLSMessageType - - if message.contains("You've reached your monthly chat messages limit") || - message.contains("You've reached your monthly chat messages quota") { - messageType = .chatLimitReached - } else if message.contains("Completions limit reached") { - messageType = .completionLimitReached - } else { - messageType = .other - } - - let detail: String - if let date = extractDateFromCLSMessage(message) { - detail = "Visit GitHub to check your usage and upgrade to Copilot Pro or wait until \(date) for your limit to reset." - } else { - detail = message - } - - return CLSMessage(summary: messageType.summary, detail: detail) -} diff --git a/ExtensionService/Assets.xcassets/AccentColor.colorset/Contents.json b/ExtensionService/Assets.xcassets/AccentColor.colorset/Contents.json deleted file mode 100644 index eb878970..00000000 --- a/ExtensionService/Assets.xcassets/AccentColor.colorset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "colors" : [ - { - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/ExtensionService/Assets.xcassets/CodeBlockInsertIcon.imageset/Contents.json b/ExtensionService/Assets.xcassets/CodeBlockInsertIcon.imageset/Contents.json deleted file mode 100644 index c48d2889..00000000 --- a/ExtensionService/Assets.xcassets/CodeBlockInsertIcon.imageset/Contents.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "images" : [ - { - "filename" : "light1x.svg", - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "dark1x.svg", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "preserves-vector-representation" : true - } -} diff --git a/ExtensionService/Assets.xcassets/CodeBlockInsertIcon.imageset/dark1x.svg b/ExtensionService/Assets.xcassets/CodeBlockInsertIcon.imageset/dark1x.svg deleted file mode 100644 index b0e60fbf..00000000 --- a/ExtensionService/Assets.xcassets/CodeBlockInsertIcon.imageset/dark1x.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/ExtensionService/Assets.xcassets/CodeBlockInsertIcon.imageset/light1x.svg b/ExtensionService/Assets.xcassets/CodeBlockInsertIcon.imageset/light1x.svg deleted file mode 100644 index 1f52da33..00000000 --- a/ExtensionService/Assets.xcassets/CodeBlockInsertIcon.imageset/light1x.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/ExtensionService/Assets.xcassets/Contents.json b/ExtensionService/Assets.xcassets/Contents.json deleted file mode 100644 index 73c00596..00000000 --- a/ExtensionService/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/ExtensionService/Assets.xcassets/DiffEditor.imageset/Contents.json b/ExtensionService/Assets.xcassets/DiffEditor.imageset/Contents.json deleted file mode 100644 index b0971b3c..00000000 --- a/ExtensionService/Assets.xcassets/DiffEditor.imageset/Contents.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "images" : [ - { - "filename" : "Editor.svg", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "preserves-vector-representation" : true - } -} diff --git a/ExtensionService/Assets.xcassets/DiffEditor.imageset/Editor.svg b/ExtensionService/Assets.xcassets/DiffEditor.imageset/Editor.svg deleted file mode 100644 index ad643fcf..00000000 --- a/ExtensionService/Assets.xcassets/DiffEditor.imageset/Editor.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/ExtensionService/Assets.xcassets/Discard.imageset/Contents.json b/ExtensionService/Assets.xcassets/Discard.imageset/Contents.json deleted file mode 100644 index 0a27c3ef..00000000 --- a/ExtensionService/Assets.xcassets/Discard.imageset/Contents.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "images" : [ - { - "filename" : "discard.svg", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "preserves-vector-representation" : true - } -} diff --git a/ExtensionService/Assets.xcassets/Discard.imageset/discard.svg b/ExtensionService/Assets.xcassets/Discard.imageset/discard.svg deleted file mode 100644 index a22942fe..00000000 --- a/ExtensionService/Assets.xcassets/Discard.imageset/discard.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/ExtensionService/Assets.xcassets/EyeClosed.imageset/Contents.json b/ExtensionService/Assets.xcassets/EyeClosed.imageset/Contents.json deleted file mode 100644 index e874ab47..00000000 --- a/ExtensionService/Assets.xcassets/EyeClosed.imageset/Contents.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "images" : [ - { - "filename" : "eye-closed.svg", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "preserves-vector-representation" : true, - "template-rendering-intent" : "template" - } -} diff --git a/ExtensionService/Assets.xcassets/ItemSelectedColor.colorset/Contents.json b/ExtensionService/Assets.xcassets/ItemSelectedColor.colorset/Contents.json deleted file mode 100644 index 955c4738..00000000 --- a/ExtensionService/Assets.xcassets/ItemSelectedColor.colorset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "248", - "green" : "154", - "red" : "98" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "194", - "green" : "108", - "red" : "55" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/ExtensionService/Assets.xcassets/MenuBarErrorIcon.imageset/Contents.json b/ExtensionService/Assets.xcassets/MenuBarErrorIcon.imageset/Contents.json deleted file mode 100644 index 4ebbfc18..00000000 --- a/ExtensionService/Assets.xcassets/MenuBarErrorIcon.imageset/Contents.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "images" : [ - { - "filename" : "Status=error, Mode=dark.svg", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "preserves-vector-representation" : true, - "template-rendering-intent" : "template" - } -} diff --git a/ExtensionService/Assets.xcassets/MenuBarIcon.imageset/Contents.json b/ExtensionService/Assets.xcassets/MenuBarIcon.imageset/Contents.json deleted file mode 100644 index 4ab2faba..00000000 --- a/ExtensionService/Assets.xcassets/MenuBarIcon.imageset/Contents.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "images" : [ - { - "filename" : "Status=active, Mode=dark.svg", - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "Status=active, Mode=white.svg", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "preserves-vector-representation" : true - } -} diff --git a/ExtensionService/Assets.xcassets/ToastActionButtonColor.colorset/Contents.json b/ExtensionService/Assets.xcassets/ToastActionButtonColor.colorset/Contents.json deleted file mode 100644 index 41903f4d..00000000 --- a/ExtensionService/Assets.xcassets/ToastActionButtonColor.colorset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "0.080", - "blue" : "0x00", - "green" : "0x00", - "red" : "0x00" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "0.800", - "blue" : "0x3C", - "green" : "0x3C", - "red" : "0x3C" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/ExtensionService/Assets.xcassets/ToastBackgroundColor.colorset/Contents.json b/ExtensionService/Assets.xcassets/ToastBackgroundColor.colorset/Contents.json deleted file mode 100644 index ee9f736a..00000000 --- a/ExtensionService/Assets.xcassets/ToastBackgroundColor.colorset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0xF7", - "green" : "0xF7", - "red" : "0xF7" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0x23", - "green" : "0x23", - "red" : "0x23" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/ExtensionService/Assets.xcassets/ToastStrokeColor.colorset/Contents.json b/ExtensionService/Assets.xcassets/ToastStrokeColor.colorset/Contents.json deleted file mode 100644 index 2a52454e..00000000 --- a/ExtensionService/Assets.xcassets/ToastStrokeColor.colorset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "0.550", - "blue" : "0xC0", - "green" : "0xC0", - "red" : "0xC0" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0x2B", - "green" : "0x2B", - "red" : "0x2B" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/ExtensionService/ExtensionService.entitlements b/ExtensionService/ExtensionService.entitlements deleted file mode 100644 index 3c568976..00000000 --- a/ExtensionService/ExtensionService.entitlements +++ /dev/null @@ -1,12 +0,0 @@ - - - - - com.apple.security.application-groups - - $(TeamIdentifierPrefix)group.$(BUNDLE_IDENTIFIER_BASE) - - com.apple.security.cs.disable-library-validation - - - diff --git a/ExtensionService/Info.plist b/ExtensionService/Info.plist deleted file mode 100644 index 19f114ff..00000000 --- a/ExtensionService/Info.plist +++ /dev/null @@ -1,33 +0,0 @@ - - - - - APPLICATION_SUPPORT_FOLDER - $(APPLICATION_SUPPORT_FOLDER) - APP_ID_PREFIX - $(AppIdentifierPrefix) - BUNDLE_IDENTIFIER_BASE - $(BUNDLE_IDENTIFIER_BASE) - EXTENSION_BUNDLE_NAME - $(EXTENSION_BUNDLE_NAME) - HOST_APP_NAME - $(HOST_APP_NAME) - LANGUAGE_SERVER_PATH - $(LANGUAGE_SERVER_PATH) - NODE_PATH - $(NODE_PATH) - TEAM_ID_PREFIX - $(TeamIdentifierPrefix) - XPCService - - ServiceType - Application - - COPILOT_DOCS_URL - $(COPILOT_DOCS_URL) - COPILOT_FORUM_URL - $(COPILOT_FORUM_URL) - STANDARD_TELEMETRY_CHANNEL_KEY - $(STANDARD_TELEMETRY_CHANNEL_KEY) - - diff --git a/ExtensionService/Main.storyboard b/ExtensionService/Main.storyboard deleted file mode 100644 index 5fc73e7e..00000000 --- a/ExtensionService/Main.storyboard +++ /dev/null @@ -1,684 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Default - - - - - - - Left to Right - - - - - - - Right to Left - - - - - - - - - - - Default - - - - - - - Left to Right - - - - - - - Right to Left - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ExtensionService/ServiceDelegate.swift b/ExtensionService/ServiceDelegate.swift deleted file mode 100644 index 6280582f..00000000 --- a/ExtensionService/ServiceDelegate.swift +++ /dev/null @@ -1,20 +0,0 @@ -import Foundation -import Service -import XPCShared - -class ServiceDelegate: NSObject, NSXPCListenerDelegate { - func listener( - _: NSXPCListener, - shouldAcceptNewConnection newConnection: NSXPCConnection - ) -> Bool { - newConnection.exportedInterface = NSXPCInterface( - with: XPCServiceProtocol.self - ) - - let exportedObject = XPCService() - newConnection.exportedObject = exportedObject - newConnection.resume() - return true - } -} - diff --git a/ExtensionService/XPCController.swift b/ExtensionService/XPCController.swift deleted file mode 100644 index 02656f85..00000000 --- a/ExtensionService/XPCController.swift +++ /dev/null @@ -1,87 +0,0 @@ -import Foundation -import Logger -import XPCShared - -final class XPCController: XPCServiceDelegate { - let bridge: XPCCommunicationBridge - let xpcListener: NSXPCListener - let xpcServiceDelegate: ServiceDelegate - - var pingTask: Task? - - init() { - let bridge = XPCCommunicationBridge(logger: .client) - let listener = NSXPCListener.anonymous() - let delegate = ServiceDelegate() - listener.delegate = delegate - listener.resume() - xpcListener = listener - xpcServiceDelegate = delegate - self.bridge = bridge - - Task { - bridge.setDelegate(self) - createPingTask() - } - } - - func quit() async { - bridge.setDelegate(nil) - pingTask?.cancel() - try? await bridge.quit() - } - - deinit { - xpcListener.invalidate() - pingTask?.cancel() - } - - func createPingTask() { - pingTask?.cancel() - pingTask = Task { [weak self] in - var consecutiveFailures = 0 - var backoffDelay = 1_000_000_000 // Start with 1 second - - while !Task.isCancelled { - guard let self else { return } - do { - try await self.bridge.updateServiceEndpoint(self.xpcListener.endpoint) - // Reset on success - consecutiveFailures = 0 - backoffDelay = 1_000_000_000 - try await Task.sleep(nanoseconds: 60_000_000_000) // 60 seconds between successful pings - } catch { - consecutiveFailures += 1 - // Log only on 1st, 5th (31 sec), 10th failures, etc. to avoid flooding - let shouldLog = consecutiveFailures == 1 || consecutiveFailures % 5 == 0 - - #if DEBUG - // No log, but you should run CommunicationBridge, too. - #else - if consecutiveFailures == 5 { - if #available(macOS 13.0, *) { - showBackgroundPermissionAlert() - } - } - if shouldLog { - Logger.service.error("Failed to connect to bridge (\(consecutiveFailures) consecutive failures): \(error.localizedDescription)") - } - #endif - - // Exponential backoff with a cap - backoffDelay = min(backoffDelay * 2, 120_000_000_000) // Cap at 120 seconds - try await Task.sleep(nanoseconds: UInt64(backoffDelay)) - } - } - } - } - - func connectionDidInvalidate() async { - // ignore - } - - func connectionDidInterrupt() async { - createPingTask() // restart the ping task so that it can bring the bridge back immediately. - } -} - diff --git a/Helper/ReloadLaunchAgent.swift b/Helper/ReloadLaunchAgent.swift deleted file mode 100644 index 99c934b0..00000000 --- a/Helper/ReloadLaunchAgent.swift +++ /dev/null @@ -1,53 +0,0 @@ -import ArgumentParser -import Foundation - -struct ReloadLaunchAgent: ParsableCommand { - static var configuration = CommandConfiguration( - abstract: "Reload the launch agent" - ) - - @Option(name: .long, help: "The service identifier of the service.") - var serviceIdentifier: String - - var launchAgentDirURL: URL { - FileManager.default.homeDirectoryForCurrentUser - .appendingPathComponent("Library/LaunchAgents") - } - - var launchAgentPath: String { - launchAgentDirURL.appendingPathComponent("\(serviceIdentifier).plist").path - } - - func run() throws { - try? launchctl("unload", launchAgentPath) - try launchctl("load", launchAgentPath) - } -} - -private func launchctl(_ args: String...) throws { - return try process("/bin/launchctl", args) -} - -private func process(_ launchPath: String, _ args: [String]) throws { - let task = Process() - task.launchPath = launchPath - task.arguments = args - task.environment = [ - "PATH": "/usr/bin", - ] - let outpipe = Pipe() - task.standardOutput = outpipe - try task.run() - task.waitUntilExit() - - struct E: Error, LocalizedError { - var errorDescription: String? - } - - if task.terminationStatus == 0 { - return - } - throw E( - errorDescription: "Failed to restart. Please make sure the launch agent is already loaded." - ) -} diff --git a/Helper/main.swift b/Helper/main.swift deleted file mode 100644 index ef9f2625..00000000 --- a/Helper/main.swift +++ /dev/null @@ -1,14 +0,0 @@ -import ArgumentParser -import Foundation - -struct Helper: ParsableCommand { - static var configuration = CommandConfiguration( - commandName: "helper", - abstract: "Helper CLI for Copilot for Xcode", - subcommands: [ - ReloadLaunchAgent.self, - ] - ) -} - -Helper.main() diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index 163ff113..00000000 --- a/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2024 GitHub - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/PackageAssets/DSStore.template b/PackageAssets/DSStore.template deleted file mode 100644 index 18678db8..00000000 Binary files a/PackageAssets/DSStore.template and /dev/null differ diff --git a/PackageAssets/background.png b/PackageAssets/background.png deleted file mode 100644 index 84feaf0f..00000000 Binary files a/PackageAssets/background.png and /dev/null differ diff --git a/README.md b/README.md deleted file mode 100644 index d9c550d1..00000000 --- a/README.md +++ /dev/null @@ -1,156 +0,0 @@ -# GitHub Copilot for Xcode - -[GitHub Copilot](https://github.com/features/copilot) is an AI pair programmer -tool that helps you write code faster and smarter. Copilot for Xcode is an Xcode extension that provides inline coding suggestions as you type and a chat assistant to answer your coding questions. - -## Chat - -GitHub Copilot Chat provides suggestions to your specific coding tasks via chat. -Chat of GitHub Copilot for Xcode - -## Agent Mode - -GitHub Copilot Agent Mode provides AI-powered assistance that can understand and modify your codebase directly. With Agent Mode, you can: -- Get intelligent code edits applied directly to your files -- Run terminal commands and view their output without leaving the interface -- Search through your codebase to find relevant files and code snippets -- Create new files and directories as needed for your project -- Get assistance with enhanced context awareness across multiple files and folders -- Run Model Context Protocol (MCP) tools you configured to extend the capabilities - -Agent Mode integrates with Xcode's environment, creating a seamless development experience where Copilot can help implement features, fix bugs, and refactor code with comprehensive understanding of your project. - -## Code Completion - -You can receive auto-complete type suggestions from GitHub Copilot either by starting to write the code you want to use, or by writing a natural language comment describing what you want the code to do. -Code Completion of GitHub Copilot for Xcode - -## Requirements - -- macOS 12+ -- Xcode 8+ -- A GitHub Copilot subscription. To learn more, visit [https://github.com/features/copilot](https://github.com/features/copilot). - -## Getting Started - -1. Install via [Homebrew](https://brew.sh/): - - ```sh - brew install --cask github-copilot-for-xcode - ``` - - Or download the `dmg` from - [the latest release](https://github.com/github/CopilotForXcode/releases/latest/download/GitHubCopilotForXcode.dmg). - Drag `GitHub Copilot for Xcode` into the `Applications` folder: - -

- Screenshot of opened dmg -

- - Updates can be downloaded and installed by the app. - -1. Open the `GitHub Copilot for Xcode` application (from the `Applications` folder). Accept the security warning. -

- Screenshot of MacOS download permission request -

- - -1. A background item will be added to enable the GitHub Copilot for Xcode extension app to connect to the host app. This permission is usually automatically added when first launching the app. -

- Screenshot of background item -

- -1. Three permissions are required for GitHub Copilot for Xcode to function properly: `Background`, `Accessibility`, and `Xcode Source Editor Extension`. For more details on why these permissions are required see [TROUBLESHOOTING.md](./TROUBLESHOOTING.md). - - The first time the application is run the `Accessibility` permission should be requested: - -

- Screenshot of accessibility permission request -

- - The `Xcode Source Editor Extension` permission needs to be enabled manually. Click - `Extension Permission` from the `GitHub Copilot for Xcode` application settings to open the - System Preferences to the `Extensions` panel. Select `Xcode Source Editor` - and enable `GitHub Copilot`: - -

- Screenshot of extension permission -

- -1. After granting the extension permission, open Xcode. Verify that the - `Github Copilot` menu is available and enabled under the Xcode `Editor` - menu. -
-

- Screenshot of Xcode Editor GitHub Copilot menu item -

- - Keyboard shortcuts can be set for all menu items in the `Key Bindings` - section of Xcode preferences. - -1. To sign into GitHub Copilot, click the `Sign in` button in the settings application. This will open a browser window and copy a code to the clipboard. Paste the code into the GitHub login page and authorize the application. -

- Screenshot of sign-in popup -

- -1. To install updates, click `Check for Updates` from the menu item or in the - settings application. - - After installing a new version, Xcode must be restarted to use the new - version correctly. - - New versions can also be installed from `dmg` files downloaded from the - releases page. When installing a new version via `dmg`, the application must - be run manually the first time to accept the downloaded from the internet - warning. - -1. To avoid confusion, we recommend disabling `Predictive code completion` under - `Xcode` > `Preferences` > `Text Editing` > `Editing`. - -1. Press `tab` to accept the first line of a suggestion, hold `option` to view - the full suggestion, and press `option` + `tab` to accept the full suggestion. - -## How to use Chat - - Open Copilot Chat in GitHub Copilot. - - Open via the Xcode menu `Xcode -> Editor -> GitHub Copilot -> Open Chat`. -

- Screenshot of Xcode Editor GitHub Copilot menu item -

- - - Open via GitHub Copilot app menu `Open Chat`. - -

- Screenshot of GitHub Copilot menu item -

- -## How to use Code Completion - - Press `tab` to accept the first line of a suggestion, hold `option` to view - the full suggestion, and press `option` + `tab` to accept the full suggestion. - -## License - -This project is licensed under the terms of the MIT open source license. Please -refer to [LICENSE.txt](./LICENSE.txt) for the full terms. - -## Privacy - -We follow responsible practices in accordance with our -[Privacy Statement](https://docs.github.com/en/site-policy/privacy-policies/github-privacy-statement). - -To get the latest security fixes, please use the latest version of the GitHub -Copilot for Xcode. - -## Support - -We’d love to get your help in making GitHub Copilot better! If you have -feedback or encounter any problems, please reach out on our [Feedback -forum](https://github.com/orgs/community/discussions/categories/copilot). - -## Acknowledgements - -Thank you to @intitni for creating the original project that this is based on. - -Attributions can be found under About when running the app or in -[Credits.rtf](./Copilot%20for%20Xcode/Credits.rtf). \ No newline at end of file diff --git a/ReleaseNotes.md b/ReleaseNotes.md deleted file mode 100644 index 75211dae..00000000 --- a/ReleaseNotes.md +++ /dev/null @@ -1,12 +0,0 @@ -### GitHub Copilot for Xcode 0.40.0 - -**🚀 Highlights** - -* Performance: Fixed a freezing issue in 'Add Context' view when opening large projects. -* Support disabling Agent mode when it's disabled by policy. - -**🛠️ Bug Fixes** - -* Login failed due to insufficient permissions on the .config folder. -* Fixed an issue that setting changes like proxy config did not take effect. -* Increased the timeout for ask mode to prevent response failures due to timeout. diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index 4279c87f..00000000 --- a/SECURITY.md +++ /dev/null @@ -1,31 +0,0 @@ -Thanks for helping make GitHub safe for everyone. - -# Security - -GitHub takes the security of our software products and services seriously, including all of the open source code repositories managed through our GitHub organizations, such as [GitHub](https://github.com/GitHub). - -Even though [open source repositories are outside of the scope of our bug bounty program](https://bounty.github.com/index.html#scope) and therefore not eligible for bounty rewards, we will ensure that your finding gets passed along to the appropriate maintainers for remediation. - -## Reporting Security Issues - -If you believe you have found a security vulnerability in any GitHub-owned repository, please report it to us through coordinated disclosure. - -**Please do not report security vulnerabilities through public GitHub issues, discussions, or pull requests.** - -Instead, please send an email to opensource-security[@]github.com. - -Please include as much of the information listed below as you can to help us better understand and resolve the issue: - - * The type of issue (e.g., buffer overflow, SQL injection, or cross-site scripting) - * Full paths of source file(s) related to the manifestation of the issue - * The location of the affected source code (tag/branch/commit or direct URL) - * Any special configuration required to reproduce the issue - * Step-by-step instructions to reproduce the issue - * Proof-of-concept or exploit code (if possible) - * Impact of the issue, including how an attacker might exploit the issue - -This information will help us triage your report more quickly. - -## Policy - -See [GitHub's Safe Harbor Policy](https://docs.github.com/en/site-policy/security-policies/github-bug-bounty-program-legal-safe-harbor#1-safe-harbor-terms) \ No newline at end of file diff --git a/SUPPORT.md b/SUPPORT.md deleted file mode 100644 index 33762051..00000000 --- a/SUPPORT.md +++ /dev/null @@ -1,20 +0,0 @@ -# Support - -## How to get help - -We’d love to get your help in making GitHub Copilot better! If you have -feedback or encounter any problems, please reach out on our [Feedback -forum](https://github.com/orgs/community/discussions/categories/copilot). - -GitHub Copilot for Xcode is under active development and maintained by GitHub -staff. We will do our best to respond to support, feature requests, and -community questions in a timely manner. - -## GitHub Support Policy - -GitHub Copilot for Xcode is considered a Beta Preview under the [GitHub Terms of -Service](https://docs.github.com/en/site-policy/github-terms/github-terms-of-service#j-beta-previews). - -Once GitHub Copilot for Xcode is generally available, it will be subject to the -[GitHub Additional Product -Terms](https://docs.github.com/en/site-policy/github-terms/github-terms-for-additional-products-and-features). diff --git a/SandboxedClientTester/Assets.xcassets/AccentColor.colorset/Contents.json b/SandboxedClientTester/Assets.xcassets/AccentColor.colorset/Contents.json deleted file mode 100644 index eb878970..00000000 --- a/SandboxedClientTester/Assets.xcassets/AccentColor.colorset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "colors" : [ - { - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/SandboxedClientTester/Assets.xcassets/AppIcon.appiconset/Contents.json b/SandboxedClientTester/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 3f00db43..00000000 --- a/SandboxedClientTester/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "images" : [ - { - "idiom" : "mac", - "scale" : "1x", - "size" : "16x16" - }, - { - "idiom" : "mac", - "scale" : "2x", - "size" : "16x16" - }, - { - "idiom" : "mac", - "scale" : "1x", - "size" : "32x32" - }, - { - "idiom" : "mac", - "scale" : "2x", - "size" : "32x32" - }, - { - "idiom" : "mac", - "scale" : "1x", - "size" : "128x128" - }, - { - "idiom" : "mac", - "scale" : "2x", - "size" : "128x128" - }, - { - "idiom" : "mac", - "scale" : "1x", - "size" : "256x256" - }, - { - "idiom" : "mac", - "scale" : "2x", - "size" : "256x256" - }, - { - "idiom" : "mac", - "scale" : "1x", - "size" : "512x512" - }, - { - "idiom" : "mac", - "scale" : "2x", - "size" : "512x512" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/SandboxedClientTester/Assets.xcassets/Contents.json b/SandboxedClientTester/Assets.xcassets/Contents.json deleted file mode 100644 index 73c00596..00000000 --- a/SandboxedClientTester/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/SandboxedClientTester/ContentView.swift b/SandboxedClientTester/ContentView.swift deleted file mode 100644 index b56ddd05..00000000 --- a/SandboxedClientTester/ContentView.swift +++ /dev/null @@ -1,29 +0,0 @@ -import SwiftUI -import Client - -struct ContentView: View { - @State var text: String = "Hello, world!" - var body: some View { - VStack { - Button(action: { - Task { - do { - let service = try getService() - let version = try await service.getXPCServiceVersion() - text = "Version: \(version.version) Build: \(version.build)" - } catch { - text = error.localizedDescription - } - } - }) { - Text("Test") - } - Text(text) - } - .padding() - } -} - -#Preview { - ContentView() -} diff --git a/SandboxedClientTester/Info.plist b/SandboxedClientTester/Info.plist deleted file mode 100644 index cb7f95c4..00000000 --- a/SandboxedClientTester/Info.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - BUNDLE_IDENTIFIER_BASE - $(BUNDLE_IDENTIFIER_BASE) - - diff --git a/SandboxedClientTester/Preview Content/Preview Assets.xcassets/Contents.json b/SandboxedClientTester/Preview Content/Preview Assets.xcassets/Contents.json deleted file mode 100644 index 73c00596..00000000 --- a/SandboxedClientTester/Preview Content/Preview Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/SandboxedClientTester/SandboxedClientTester.entitlements b/SandboxedClientTester/SandboxedClientTester.entitlements deleted file mode 100644 index 9e6f3194..00000000 --- a/SandboxedClientTester/SandboxedClientTester.entitlements +++ /dev/null @@ -1,14 +0,0 @@ - - - - - com.apple.security.app-sandbox - - com.apple.security.files.user-selected.read-only - - com.apple.security.temporary-exception.mach-lookup.global-name - - $(BUNDLE_IDENTIFIER_BASE).CommunicationBridge - - - diff --git a/SandboxedClientTester/SandboxedClientTesterApp.swift b/SandboxedClientTester/SandboxedClientTesterApp.swift deleted file mode 100644 index ef03ae51..00000000 --- a/SandboxedClientTester/SandboxedClientTesterApp.swift +++ /dev/null @@ -1,10 +0,0 @@ -import SwiftUI - -@main -struct SandboxedClientTesterApp: App { - var body: some Scene { - WindowGroup { - ContentView() - } - } -} diff --git a/Script/MakeDSStore.py b/Script/MakeDSStore.py deleted file mode 100644 index a257185a..00000000 --- a/Script/MakeDSStore.py +++ /dev/null @@ -1,56 +0,0 @@ -# Run MakeDSStore.sh rather than use this script directly. -import struct -from ds_store import DSStore -from mac_alias import Alias - -# See https://github.com/gitpan/Mac-Finder-DSStore/blob/master/DSStoreFormat.pod - -with DSStore.open('/Volumes/GitHub Copilot for Xcode/DSStore.template', 'w+') as ds: - # finder window coordinates (top, left, bottom, right) - # icnv indicates icon view, followed by four unknown bytes - fwi0 = struct.pack('>H', 100) + \ - struct.pack('>H', 200) + \ - struct.pack('>H', 400) + \ - struct.pack('>H', 600) + \ - bytes('icnv', 'ascii') + bytearray([0] * 4) - ds['.']['fwi0'] = ('blob', fwi0) - - # location of the app icon - ds['GitHub Copilot for Xcode.app']['Iloc'] = (100, 150) - # location of the Applications folder - ds['Applications']['Iloc'] = (300, 150) - - # hidden files outside the window - ds['.DS_Store']['Iloc'] = (650, 175) - ds['.background']['Iloc'] = (700, 175) - - # a plist with settings for the icon view - icvp = { - 'viewOptionsVersion': 1, - 'gridOffsetX': 0, - 'gridOffsetY': 0, - 'gridSpacing': 100, - 'iconSize': 128, - 'textSize': 12, - 'showIconPreview': True, - 'showItemInfo': False, - 'labelOnBottom': True, - 'scrollPositionX': 0, - 'scrollPositionY': 0, - 'arrangeBy': 'none', - 'backgroundColorRed': 1.0, - 'backgroundColorGreen': 1.0, - 'backgroundColorBlue': 1.0, - 'backgroundType': 2, - 'backgroundImageAlias': Alias.for_file('/Volumes/GitHub Copilot for Xcode/.background/background.png').to_bytes(), - } - ds['.']['icvp'] = icvp - - # window sidebar width - ds['.']['fwsw'] = ('long', 0) - # window height - ds['.']['fwvh'] = ('shor', 300) - # unknown meaning - ds['.']['ICVO'] = ('bool', True) - # text size - ds['.']['icvt'] = ('shor', 12) diff --git a/Script/MakeDSStore.sh b/Script/MakeDSStore.sh deleted file mode 100755 index 7e42e44c..00000000 --- a/Script/MakeDSStore.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env bash - -set -e - -# Ensure we're in the root of the repo -cd "$(dirname "$0")/.." - -# Must have python3 installed -if ! command -v python3 &> /dev/null -then - echo "python3 could not be found. Install phyton3 and try again." - exit 1 -fi - -# We need a volume with the background image in order to create the correct alias for it -mkdir -p build/image/.background -cp PackageAssets/background.png build/image/.background -hdiutil create -volname "GitHub Copilot for Xcode" -srcfolder build/image -format UDRW build/GitHubCopilotforXcode.dmg -hdiutil attach -readwrite build/GitHubCopilotforXcode.dmg - - -# Create a python virtual environment -mkdir -p build/venv -python3 -m venv build/venv - -# Install ds-store -./build/venv/bin/pip install ds-store mac-alias==2.2.0 ds-store==1.3.0 -./build/venv/bin/python Script/MakeDSStore.py - -# Run it -./build/venv/bin/python Script/MakeDSStore.py - -# Save the created .DS_Store file -cp '/Volumes/GitHub Copilot for Xcode/DSStore.template' PackageAssets/DSStore.template - -# Clean up -hdiutil detach '/Volumes/GitHub Copilot for Xcode' -rm -rf build/GitHubCopilotforXcode.dmg -rm -rf build/image -rm -rf build/venv diff --git a/Script/export-options-local.plist b/Script/export-options-local.plist deleted file mode 100644 index 9c4fb9f7..00000000 --- a/Script/export-options-local.plist +++ /dev/null @@ -1,10 +0,0 @@ - - - - - method - debugging - signingStyle - automatic - - \ No newline at end of file diff --git a/Script/localbuild-app.sh b/Script/localbuild-app.sh deleted file mode 100644 index 177c20fe..00000000 --- a/Script/localbuild-app.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash - -# Determine paths relative to script location -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -PROJECT_ROOT="$( cd "${SCRIPT_DIR}/.." && pwd )" -PROJECT_NAME=$(basename "${PROJECT_ROOT}") - -# Define build directory -BUILD_DIR="${PROJECT_ROOT}/build" -mkdir -p "${BUILD_DIR}" - -# Set variables -APP_NAME="CopiloForXcode" -SCHEME_NAME="Copilot for Xcode" -CONFIGURATION="Release" -ARCHIVE_PATH="${BUILD_DIR}/Archives/${APP_NAME}.xcarchive" -XCWORKSPACE_PATH="${PROJECT_ROOT}/Copilot for Xcode.xcworkspace" -EXPORT_PATH="${BUILD_DIR}/Export" -EXPORT_OPTIONS_PLIST="${PROJECT_ROOT}/Script/export-options-local.plist" - -# Clean and build archive -xcodebuild \ - -scheme "${SCHEME_NAME}" \ - -quiet \ - -archivePath "${ARCHIVE_PATH}" \ - -configuration "${CONFIGURATION}" \ - -skipMacroValidation \ - -showBuildTimingSummary \ - -disableAutomaticPackageResolution \ - -workspace "${XCWORKSPACE_PATH}" -verbose -arch arm64 \ - archive \ - APP_VERSION='0.0.0' - -# Export archive to .app -xcodebuild -exportArchive \ - -archivePath "${ARCHIVE_PATH}" \ - -exportOptionsPlist "${EXPORT_OPTIONS_PLIST}" \ - -exportPath "${EXPORT_PATH}" - -echo "App packaged successfully at ${EXPORT_PATH}/${APP_NAME}.app" - -open "${EXPORT_PATH}" \ No newline at end of file diff --git a/Script/uninstall-app.sh b/Script/uninstall-app.sh deleted file mode 100755 index 3cf092d6..00000000 --- a/Script/uninstall-app.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env bash -# -# Uninstall the application and remove the settings and permissions -# -# Usage: ./uninstall-app.sh - -# Remove the settings and permissions (should happen before removing the app) -tccutil reset All com.github.CopilotForXcode -tccutil reset All com.github.CopilotForXcode.ExtensionService - -# Remove dev versions as well -tccutil reset All dev.com.github.CopilotForXcode -tccutil reset All dev.com.github.CopilotForXcode.ExtensionService - -# Remove launch agent -launchctl remove com.github.CopilotForXcode.CommunicationBridge -launchctl remove dev.com.github.CopilotForXcode.CommunicationBridge - -# Remove app -rm -rf /Applications/Copilot\ for\ Xcode.app -rm -rf /Applications/GitHub\ Copilot\ for\ Xcode.app - -# Remove user preferences -rm -f ~/Library/Preferences/com.github.CopilotForXcode.plist -rm -f ~/Library/Preferences/com.github.CopilotForXcode.ExtensionService.plist -rm -f ~/Library/Preferences/dev.com.github.CopilotForXcode.plist -rm -f ~/Library/Preferences/dev.com.github.CopilotForXcode.ExtensionService.plist - -defaults delete com.github.CopilotForXcode -defaults delete dev.com.github.CopilotForXcode -defaults delete VEKTX9H2N7.group.com.github.CopilotForXcode.prefs -defaults delete VEKTX9H2N7.group.dev.com.github.CopilotForXcode.prefs - -echo 'Finished uninstalling Copilot for Xcode' - diff --git a/Server/package-lock.json b/Server/package-lock.json deleted file mode 100644 index e2d8b63e..00000000 --- a/Server/package-lock.json +++ /dev/null @@ -1,2105 +0,0 @@ -{ - "name": "@github/copilot-xcode", - "version": "0.0.1", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@github/copilot-xcode", - "version": "0.0.1", - "dependencies": { - "@github/copilot-language-server": "^1.351.0", - "@xterm/addon-fit": "^0.10.0", - "@xterm/xterm": "^5.5.0", - "monaco-editor": "0.52.2" - }, - "devDependencies": { - "@types/node": "^22.15.17", - "copy-webpack-plugin": "^13.0.0", - "css-loader": "^7.1.2", - "style-loader": "^4.0.0", - "terser-webpack-plugin": "^5.3.14", - "ts-loader": "^9.5.2", - "typescript": "^5.8.3", - "webpack": "^5.99.9", - "webpack-cli": "^6.0.1" - } - }, - "node_modules/@discoveryjs/json-ext": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz", - "integrity": "sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.17.0" - } - }, - "node_modules/@github/copilot-language-server": { - "version": "1.351.0", - "resolved": "https://registry.npmjs.org/@github/copilot-language-server/-/copilot-language-server-1.351.0.tgz", - "integrity": "sha512-Owpl/cOTMQwXYArYuB1KCZGYkAScSb4B1TxPrKxAM10nIBeCtyHuEc1NQ0Pw05asMAHnoHWHVGQDrJINjlA8Ww==", - "license": "https://docs.github.com/en/site-policy/github-terms/github-terms-for-additional-products-and-features", - "dependencies": { - "vscode-languageserver-protocol": "^3.17.5" - }, - "bin": { - "copilot-language-server": "dist/language-server.js" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@types/eslint": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", - "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "22.15.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.17.tgz", - "integrity": "sha512-wIX2aSZL5FE+MR0JlvF87BNVrtFWf6AE6rxSE9X7OwnVvoyCQjpzSRJ+M87se/4QCkCiebQAqrJ0y6fwIyi7nw==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", - "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/helper-numbers": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", - "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", - "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", - "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", - "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.13.2", - "@webassemblyjs/helper-api-error": "1.13.2", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", - "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", - "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/wasm-gen": "1.14.1" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", - "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", - "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", - "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", - "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/helper-wasm-section": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-opt": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1", - "@webassemblyjs/wast-printer": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", - "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", - "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", - "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-api-error": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", - "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webpack-cli/configtest": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-3.0.1.tgz", - "integrity": "sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "webpack": "^5.82.0", - "webpack-cli": "6.x.x" - } - }, - "node_modules/@webpack-cli/info": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-3.0.1.tgz", - "integrity": "sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "webpack": "^5.82.0", - "webpack-cli": "6.x.x" - } - }, - "node_modules/@webpack-cli/serve": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-3.0.1.tgz", - "integrity": "sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "webpack": "^5.82.0", - "webpack-cli": "6.x.x" - }, - "peerDependenciesMeta": { - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/@xterm/addon-fit": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.10.0.tgz", - "integrity": "sha512-UFYkDm4HUahf2lnEyHvio51TNGiLK66mqP2JoATy7hRZeXaGMRDr00JiSF7m63vR5WKATF605yEggJKsw0JpMQ==", - "license": "MIT", - "peerDependencies": { - "@xterm/xterm": "^5.0.0" - } - }, - "node_modules/@xterm/xterm": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz", - "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==", - "license": "MIT" - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001715", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001715.tgz", - "integrity": "sha512-7ptkFGMm2OAOgvZpwgA4yjQ5SQbrNVGdRjzH0pBdy1Fasvcr+KAeECmbCAECzTuDuoX0FCY8KzUxjf9+9kfZEw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", - "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/copy-webpack-plugin": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-13.0.0.tgz", - "integrity": "sha512-FgR/h5a6hzJqATDGd9YG41SeDViH+0bkHn6WNXCi5zKAZkeESeSxLySSsFLHqLEVCh0E+rITmCf0dusXWYukeQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "glob-parent": "^6.0.1", - "normalize-path": "^3.0.0", - "schema-utils": "^4.2.0", - "serialize-javascript": "^6.0.2", - "tinyglobby": "^0.2.12" - }, - "engines": { - "node": ">= 18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/css-loader": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz", - "integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==", - "dev": true, - "license": "MIT", - "dependencies": { - "icss-utils": "^5.1.0", - "postcss": "^8.4.33", - "postcss-modules-extract-imports": "^3.1.0", - "postcss-modules-local-by-default": "^4.0.5", - "postcss-modules-scope": "^3.2.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">= 18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "webpack": "^5.27.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.142", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.142.tgz", - "integrity": "sha512-Ah2HgkTu/9RhTDNThBtzu2Wirdy4DC9b0sMT1pUhbkZQ5U/iwmE+PHZX1MpjD5IkJCc2wSghgGG/B04szAx07w==", - "dev": true, - "license": "ISC" - }, - "node_modules/enhanced-resolve": { - "version": "5.18.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", - "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/envinfo": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz", - "integrity": "sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==", - "dev": true, - "license": "MIT", - "bin": { - "envinfo": "dist/cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", - "dev": true, - "license": "MIT" - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-uri": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", - "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/fastest-levenshtein": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", - "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.9.1" - } - }, - "node_modules/fdir": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", - "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "license": "BSD-3-Clause", - "bin": { - "flat": "cli.js" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/import-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/interpret": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", - "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "license": "MIT", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.11.5" - } - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/monaco-editor": { - "version": "0.52.2", - "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.52.2.tgz", - "integrity": "sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==", - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/postcss": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.8", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-modules-extract-imports": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", - "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-local-by-default": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", - "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", - "dev": true, - "license": "MIT", - "dependencies": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^7.0.0", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-scope": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", - "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", - "dev": true, - "license": "ISC", - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "icss-utils": "^5.0.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/rechoir": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", - "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve": "^1.20.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/schema-utils": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", - "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, - "license": "MIT", - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/style-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-4.0.0.tgz", - "integrity": "sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.27.0" - } - }, - "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/terser": { - "version": "5.39.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", - "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.14", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", - "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "jest-worker": "^27.4.5", - "schema-utils": "^4.3.0", - "serialize-javascript": "^6.0.2", - "terser": "^5.31.1" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/tinyglobby": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", - "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ts-loader": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.2.tgz", - "integrity": "sha512-Qo4piXvOTWcMGIgRiuFa6nHNm+54HbYaZCKqc9eeZCLRy3XqafQgwX2F7mofrbJG3g7EEb+lkiR+z2Lic2s3Zw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "enhanced-resolve": "^5.0.0", - "micromatch": "^4.0.0", - "semver": "^7.3.4", - "source-map": "^0.7.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "typescript": "*", - "webpack": "^5.0.0" - } - }, - "node_modules/ts-loader/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">= 8" - } - }, - "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT" - }, - "node_modules/vscode-jsonrpc": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", - "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/vscode-languageserver-protocol": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", - "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", - "dependencies": { - "vscode-jsonrpc": "8.2.0", - "vscode-languageserver-types": "3.17.5" - } - }, - "node_modules/vscode-languageserver-types": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", - "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" - }, - "node_modules/watchpack": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", - "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", - "dev": true, - "license": "MIT", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack": { - "version": "5.99.9", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.9.tgz", - "integrity": "sha512-brOPwM3JnmOa+7kd3NsmOUOwbDAj8FT9xDsG3IW0MgbN9yZV7Oi/s/+MNQ/EcSMqw7qfoRyXPoeEWT8zLVdVGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "@webassemblyjs/ast": "^1.14.1", - "@webassemblyjs/wasm-edit": "^1.14.1", - "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.14.0", - "browserslist": "^4.24.0", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.1", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^4.3.2", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.11", - "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-cli": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-6.0.1.tgz", - "integrity": "sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@discoveryjs/json-ext": "^0.6.1", - "@webpack-cli/configtest": "^3.0.1", - "@webpack-cli/info": "^3.0.1", - "@webpack-cli/serve": "^3.0.1", - "colorette": "^2.0.14", - "commander": "^12.1.0", - "cross-spawn": "^7.0.3", - "envinfo": "^7.14.0", - "fastest-levenshtein": "^1.0.12", - "import-local": "^3.0.2", - "interpret": "^3.1.1", - "rechoir": "^0.8.0", - "webpack-merge": "^6.0.1" - }, - "bin": { - "webpack-cli": "bin/cli.js" - }, - "engines": { - "node": ">=18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.82.0" - }, - "peerDependenciesMeta": { - "webpack-bundle-analyzer": { - "optional": true - }, - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/webpack-cli/node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/webpack-merge": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", - "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "clone-deep": "^4.0.1", - "flat": "^5.0.2", - "wildcard": "^2.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wildcard": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", - "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", - "dev": true, - "license": "MIT" - } - } -} diff --git a/Server/package.json b/Server/package.json deleted file mode 100644 index 9bd5a961..00000000 --- a/Server/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "@github/copilot-xcode", - "version": "0.0.1", - "description": "Package for downloading @github/copilot-language-server", - "private": true, - "scripts": { - "build": "webpack" - }, - "dependencies": { - "@github/copilot-language-server": "^1.351.0", - "@xterm/addon-fit": "^0.10.0", - "@xterm/xterm": "^5.5.0", - "monaco-editor": "0.52.2" - }, - "devDependencies": { - "@types/node": "^22.15.17", - "copy-webpack-plugin": "^13.0.0", - "css-loader": "^7.1.2", - "style-loader": "^4.0.0", - "terser-webpack-plugin": "^5.3.14", - "ts-loader": "^9.5.2", - "typescript": "^5.8.3", - "webpack": "^5.99.9", - "webpack-cli": "^6.0.1" - } -} diff --git a/Server/src/diffView/css/style.css b/Server/src/diffView/css/style.css deleted file mode 100644 index 2e430145..00000000 --- a/Server/src/diffView/css/style.css +++ /dev/null @@ -1,192 +0,0 @@ -/* Diff Viewer Styles */ -:root { - /* Light theme variables */ - --bg-color: #ffffff; - --text-color: #333333; - --border-color: #dddddd; - --button-bg: #007acc; - --button-text: white; - --secondary-button-bg: #f0f0f0; - --secondary-button-text: #333333; - --secondary-button-border: #dddddd; - --secondary-button-hover: #e0e0e0; - --additions-foreground-color: #2EA043; - --deletions-foreground-color: #F85149; -} - -@media (prefers-color-scheme: dark) { - :root { - /* Dark theme variables */ - --bg-color: #1e1e1e; - --text-color: #cccccc; - --border-color: #444444; - --button-bg: #0e639c; - --button-text: white; - --secondary-button-bg: #6E6D70; - --secondary-button-text: #DFDEDF; - --secondary-button-border: #555555; - --secondary-button-hover: #505050; - --additions-foreground-color: #2EA043; - --deletions-foreground-color: #F85149; - } -} - -html, body { - margin: 0; - padding: 0; - height: 100%; - width: 100%; - overflow: hidden; - background-color: var(--bg-color); - color: var(--text-color); -} - -#container { - width: calc(100% - 40px); /* 20px padding on each side */ - height: calc(100vh - 84px); /* 40px header + 4px top padding + 40px bottom padding */ - border: 1px solid var(--border-color); - margin: 0 20px 40px 20px; - padding: 0; - margin-top: 44px; /* 40px header + 4px top padding */ - box-sizing: border-box; -} - -.loading { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - font-family: -apple-system, BlinkMacSystemFont, sans-serif; - color: var(--text-color); -} - -.header { - position: absolute; - top: 4px; - left: 10px; - right: 10px; - height: 40px; - display: flex; - justify-content: space-between; - align-items: center; - padding: 0 10px; - background-color: var(--bg-color); - box-sizing: border-box; -} - -.action-button { - margin-left: 2px; - padding: 4px 14px; - background-color: var(--button-bg); - color: var(--button-text); - border: none; - border-radius: 4px; - cursor: pointer; - font-family: -apple-system, BlinkMacSystemFont, sans-serif; - font-size: 14px; - font-weight: 500; -} - -.action-button:hover { - background-color: #0062a3; -} - -.action-button.secondary { - background-color: var(--secondary-button-bg); - color: var(--secondary-button-text); - border: 1px solid var(--secondary-button-border); -} - -.action-button.secondary:hover { - background-color: var(--secondary-button-hover); -} - -.hidden { - display: none; -} - -.header-left { - display: flex; - align-items: center; - overflow: hidden; - gap: 4px; -} - -/* file path */ -.file-path { - font-family: -apple-system, BlinkMacSystemFont, sans-serif; - font-size: 14px; - font-weight: 600; - color: var(--text-color); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -/* Diff stats */ -.diff-stats { - font-family: -apple-system, BlinkMacSystemFont, sans-serif; - font-size: 12px; - font-weight: 500; - display: flex; - gap: 4px; -} - -.additions-count { - color: var(--additions-foreground-color); - font-weight: 600; -} - -.deletions-count { - color: var(--deletions-foreground-color); - font-weight: 600; -} - -/* Style for gutter indicators using data attributes */ -.monaco-editor .codicon.codicon-diff-insert:before { - content: "+" !important; - font-family: inherit !important; - font-size: inherit !important; - font-weight: bold; - color: var(--additions-foreground-color) !important; - padding: 0 2px; -} - -.monaco-editor .codicon.codicon-diff-remove:before { - content: "-" !important; - font-family: inherit !important; - font-size: inherit !important; - font-weight: bold; - color: var(--deletions-foreground-color) !important; - padding: 0 2px; -} - -/* Force show for Monaco Editor 0.52.2 */ -.monaco-editor .diff-side-insert .margin-view-zone .codicon, -.monaco-editor .diff-side-delete .margin-view-zone .codicon { - display: inline-block !important; - visibility: visible !important; - opacity: 1 !important; -} - -/* Hide the diff overview bar completely */ -.monaco-diff-editor .diffOverview { - display: none !important; -} - -/* Hide all lightbulb icons (Copy Changed Line buttons) */ -.monaco-editor .codicon-lightbulb, -.monaco-editor .codicon-lightbulb-autofix, -.monaco-editor .lightbulb-glyph { - display: none !important; - visibility: hidden !important; - pointer-events: none !important; -} - -/* Unfold icon */ -.monaco-editor .codicon.codicon-unfold:before { - content:"···" !important; - font-family: inherit !important; - font-size: inherit !important; - font-weight: bold; -} \ No newline at end of file diff --git a/Server/src/diffView/diffView.html b/Server/src/diffView/diffView.html deleted file mode 100644 index de32b013..00000000 --- a/Server/src/diffView/diffView.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - Diff Viewer - - - -
Loading diff viewer...
- -
-
-
-
- +0 - -0 -
-
- -
- - -
-
- -
- - - - diff --git a/Server/src/diffView/index.ts b/Server/src/diffView/index.ts deleted file mode 100644 index 05eb6fdf..00000000 --- a/Server/src/diffView/index.ts +++ /dev/null @@ -1,38 +0,0 @@ -// index.ts - Main entry point for the Monaco Editor diff view -import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; -import { initDiffEditor } from './js/monaco-diff-editor'; -import { setupUI } from './js/ui-controller'; -import DiffViewer from './js/api'; - -// Initialize everything when DOM is loaded -document.addEventListener('DOMContentLoaded', () => { - // Hide loading indicator as Monaco is directly imported - const loadingElement = document.getElementById('loading'); - if (loadingElement) { - loadingElement.style.display = 'none'; - } - - // Set up UI elements and event handlers - setupUI(); - - // Make sure the editor follows the system theme - DiffViewer.followSystemTheme(); - - // Handle window resize events - window.addEventListener('resize', () => { - DiffViewer.handleResize(); - }); -}); - -// Define DiffViewer on the window object -declare global { - interface Window { - DiffViewer: typeof DiffViewer; - } -} - -// Expose the MonacoDiffViewer API to the global scope -window.DiffViewer = DiffViewer; - -// Export the MonacoDiffViewer for webpack -export default DiffViewer; diff --git a/Server/src/diffView/js/api.ts b/Server/src/diffView/js/api.ts deleted file mode 100644 index 2774e0c9..00000000 --- a/Server/src/diffView/js/api.ts +++ /dev/null @@ -1,121 +0,0 @@ -// api.ts - Public API for external use -import { initDiffEditor, updateDiffContent, getEditor, setEditorTheme, updateDiffStats } from './monaco-diff-editor'; -import { updateFileMetadata } from './ui-controller'; -import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; - -/** - * Interface for the DiffViewer API - */ -interface DiffViewerAPI { - init: ( - originalContent: string, - modifiedContent: string, - path: string | null, - status: string | null, - options?: monaco.editor.IDiffEditorConstructionOptions - ) => void; - update: ( - originalContent: string, - modifiedContent: string, - path: string | null, - status: string | null - ) => void; - handleResize: () => void; - setTheme: (theme: 'light' | 'dark') => void; - followSystemTheme: () => void; -} - -/** - * The public API that will be exposed to the global scope - */ -const DiffViewer: DiffViewerAPI = { - /** - * Initialize the diff editor with content - * @param {string} originalContent - Content for the original side - * @param {string} modifiedContent - Content for the modified side - * @param {string} path - File path - * @param {string} status - File edit status - * @param {Object} options - Optional configuration for the diff editor - */ - init: function( - originalContent: string, - modifiedContent: string, - path: string | null, - status: string | null, - options?: monaco.editor.IDiffEditorConstructionOptions - ): void { - // Initialize editor - initDiffEditor(originalContent, modifiedContent, options || {}); - - // Update file metadata and UI - updateFileMetadata(path, status); - }, - - /** - * Update the diff editor with new content - * @param {string} originalContent - Content for the original side - * @param {string} modifiedContent - Content for the modified side - * @param {string} path - File path - * @param {string} status - File edit status - */ - update: function( - originalContent: string, - modifiedContent: string, - path: string | null, - status: string | null - ): void { - // Update editor content - updateDiffContent(originalContent, modifiedContent); - - // Update file metadata and UI - updateFileMetadata(path, status); - - // Update diff stats - updateDiffStats(); - }, - - /** - * Handle resize events - */ - handleResize: function(): void { - const editor = getEditor(); - if (editor) { - const container = document.getElementById('container'); - if (container) { - const headerHeight = 40; - const topPadding = 4; - const bottomPadding = 40; - - const availableHeight = window.innerHeight - headerHeight - topPadding - bottomPadding; - container.style.height = `${availableHeight}px`; - } - - editor.layout(); - } - }, - - /** - * Set the theme for the editor - */ - setTheme: function(theme: 'light' | 'dark'): void { - setEditorTheme(theme); - }, - - /** - * Follow the system theme - */ - followSystemTheme: function(): void { - // Set initial theme based on system preference - const isDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; - setEditorTheme(isDarkMode ? 'dark' : 'light'); - - // Add listener for theme changes - if (window.matchMedia) { - window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => { - setEditorTheme(event.matches ? 'dark' : 'light'); - }); - } - } -}; - -export default DiffViewer; diff --git a/Server/src/diffView/js/monaco-diff-editor.ts b/Server/src/diffView/js/monaco-diff-editor.ts deleted file mode 100644 index 0a87ac4c..00000000 --- a/Server/src/diffView/js/monaco-diff-editor.ts +++ /dev/null @@ -1,346 +0,0 @@ -// monaco-diff-editor.ts - Monaco Editor diff view core functionality -import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; - -// Editor state -let diffEditor: monaco.editor.IStandaloneDiffEditor | null = null; -let originalModel: monaco.editor.ITextModel | null = null; -let modifiedModel: monaco.editor.ITextModel | null = null; -let resizeObserver: ResizeObserver | null = null; -const DEFAULT_EDITOR_OPTIONS: monaco.editor.IDiffEditorConstructionOptions = { - renderSideBySide: false, - readOnly: true, - // Enable automatic layout adjustments - automaticLayout: true, - glyphMargin: false, - // Collapse unchanged regions - folding: true, - hideUnchangedRegions: { - enabled: true, - revealLineCount: 20, - minimumLineCount: 2, - contextLineCount: 2 - - }, - // Disable overview ruler and related features - renderOverviewRuler: false, - overviewRulerBorder: false, - overviewRulerLanes: 0, - scrollBeyondLastLine: false, - scrollbar: { - vertical: 'auto', - horizontal: 'auto', - useShadows: false, - verticalHasArrows: false, - horizontalHasArrows: false, - alwaysConsumeMouseWheel: false, - }, - lineHeight: 24, -} - -/** - * Initialize the Monaco diff editor - * @param {string} originalContent - Content for the original side - * @param {string} modifiedContent - Content for the modified side - * @param {Object} options - Optional configuration for the diff editor - * @returns {Object} The diff editor instance - */ -function initDiffEditor( - originalContent: string, - modifiedContent: string, - options: monaco.editor.IDiffEditorConstructionOptions = {} -): monaco.editor.IStandaloneDiffEditor | null { - try { - // Default options - const editorOptions: monaco.editor.IDiffEditorConstructionOptions = { - ...DEFAULT_EDITOR_OPTIONS, - lineNumbersMinChars: calculateLineNumbersMinChars(originalContent, modifiedContent), - ...options - }; - - // Create the diff editor if it doesn't exist yet - if (!diffEditor) { - const container = document.getElementById("container"); - if (!container) { - throw new Error("Container element not found"); - } - - // Set initial container size to viewport height - // const headerHeight = 40; - // container.style.height = `${window.innerHeight - headerHeight}px`; - // Set initial container size to viewport height with precise calculations - const visibleHeight = window.innerHeight; - const headerHeight = 40; - const topPadding = 4; - const bottomPadding = 40; - const availableHeight = visibleHeight - headerHeight - topPadding - bottomPadding; - container.style.height = `${Math.floor(availableHeight)}px`; - container.style.overflow = "hidden"; // Ensure container doesn't have scrollbars - - diffEditor = monaco.editor.createDiffEditor( - container, - editorOptions - ); - - // Add resize handling - setupResizeHandling(); - - // Initialize theme - initializeTheme(); - } else { - // Apply any new options - diffEditor.updateOptions(editorOptions); - } - - // Create and set models - updateModels(originalContent, modifiedContent); - - return diffEditor; - } catch (error) { - console.error("Error initializing diff editor:", error); - return null; - } -} - -/** - * Setup proper resize handling for the editor - */ -function setupResizeHandling(): void { - window.addEventListener('resize', () => { - if (diffEditor) { - diffEditor.layout(); - } - }); - - if (window.ResizeObserver && !resizeObserver) { - const container = document.getElementById('container'); - - if (container) { - resizeObserver = new ResizeObserver(() => { - if (diffEditor) { - diffEditor.layout() - } - }); - resizeObserver.observe(container); - } - } -} - -/** - * Create or update the models for the diff editor - * @param {string} originalContent - Content for the original side - * @param {string} modifiedContent - Content for the modified side - */ -function updateModels(originalContent: string, modifiedContent: string): void { - try { - // Clean up existing models if they exist - if (originalModel) { - originalModel.dispose(); - } - if (modifiedModel) { - modifiedModel.dispose(); - } - - // Create new models with the content - originalModel = monaco.editor.createModel(originalContent || "", "plaintext"); - modifiedModel = monaco.editor.createModel(modifiedContent || "", "plaintext"); - - // Set the models to show the diff - if (diffEditor) { - diffEditor.setModel({ - original: originalModel, - modified: modifiedModel, - }); - - // Add timeout to give Monaco time to calculate diffs - setTimeout(() => { - updateDiffStats(); - adjustContainerHeight(); - }, 100); // 100ms delay allows diff calculation to complete - } - } catch (error) { - console.error("Error updating models:", error); - } -} - -/** - * Update the diff view with new content - * @param {string} originalContent - Content for the original side - * @param {string} modifiedContent - Content for the modified side - */ -function updateDiffContent(originalContent: string, modifiedContent: string): void { - // If editor exists, update it - if (diffEditor && diffEditor.getModel()) { - const model = diffEditor.getModel(); - - // Update model values - if (model) { - model.original.setValue(originalContent || ""); - model.modified.setValue(modifiedContent || ""); - } - } else { - // Initialize if not already done - initDiffEditor(originalContent, modifiedContent); - } -} - -/** - * Get the current diff editor instance - * @returns {Object|null} The diff editor instance or null - */ -function getEditor(): monaco.editor.IStandaloneDiffEditor | null { - return diffEditor; -} - -/** - * Calculate the number of line differences - * @returns {Object} The number of additions and deletions - */ -function calculateLineDifferences(): { additions: number, deletions: number } { - if (!diffEditor || !diffEditor.getModel()) { - return { additions: 0, deletions: 0 }; - } - - let additions = 0; - let deletions = 0; - const lineChanges = diffEditor.getLineChanges(); - console.log(">>> Line Changes:", lineChanges); - if (lineChanges) { - for (const change of lineChanges) { - console.log(change); - if (change.originalEndLineNumber >= change.originalStartLineNumber) { - deletions += change.originalEndLineNumber - change.originalStartLineNumber + 1; - } - if (change.modifiedEndLineNumber >= change.modifiedStartLineNumber) { - additions += change.modifiedEndLineNumber - change.modifiedStartLineNumber + 1; - } - } - } - - return { additions, deletions }; -} - -/** - * Update the diff statistics displayed in the UI - */ -function updateDiffStats(): void { - const { additions, deletions } = calculateLineDifferences(); - - const additionsElement = document.getElementById('additions-count'); - const deletionsElement = document.getElementById('deletions-count'); - - if (additionsElement) { - additionsElement.textContent = `+${additions}`; - } - - if (deletionsElement) { - deletionsElement.textContent = `-${deletions}`; - } -} - -/** - * Dynamically adjust container height based on content - */ -function adjustContainerHeight(): void { - const container = document.getElementById('container'); - if (!container || !diffEditor) return; - - // Always use the full viewport height - const visibleHeight = window.innerHeight; - const headerHeight = 40; // Height of the header - const topPadding = 4; // Top padding - const bottomPadding = 40; // Bottom padding - const availableHeight = visibleHeight - headerHeight - topPadding - bottomPadding; - - container.style.height = `${Math.floor(availableHeight)}px`; - - diffEditor.layout(); -} - -/** - * Set the editor theme - * @param {string} theme - The theme to set ('light' or 'dark') - */ -function setEditorTheme(theme: 'light' | 'dark'): void { - if (!diffEditor) return; - - monaco.editor.setTheme(theme === 'dark' ? 'vs-dark' : 'vs'); -} - -/** - * Detect the system theme preference - * @returns {string} The detected theme ('light' or 'dark') - */ -function detectSystemTheme(): 'light' | 'dark' { - return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; -} - -/** - * Initialize the theme based on system preference - * and set up a listener for changes - */ -function initializeTheme(): void { - const theme = detectSystemTheme(); - setEditorTheme(theme); - - // Listen for changes in system theme preference - if (window.matchMedia) { - window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => { - setEditorTheme(event.matches ? 'dark' : 'light'); - }); - } -} - -/** - * Calculate the optimal number of characters for line numbers - * @param {string} originalContent - Content for the original side - * @param {string} modifiedContent - Content for the modified side - * @returns {number} The minimum number of characters needed for line numbers - */ -function calculateLineNumbersMinChars(originalContent: string, modifiedContent: string): number { - // Count the number of lines in both contents - const originalLineCount = originalContent ? originalContent.split('\n').length : 0; - const modifiedLineCount = modifiedContent ? modifiedContent.split('\n').length : 0; - - // Get the maximum line count - const maxLineCount = Math.max(originalLineCount, modifiedLineCount); - - // Calculate the number of digits in the max line count - // Use Math.log10 and Math.ceil to get the number of digits - // Add 1 to ensure some extra padding - const digits = maxLineCount > 0 ? Math.floor(Math.log10(maxLineCount) + 1) + 1 : 2; - - // Return a minimum of 2 characters, maximum of 5 - return Math.min(Math.max(digits, 2), 5); -} - -/** - * Dispose of the editor and models to clean up resources - */ -function dispose(): void { - if (resizeObserver) { - resizeObserver.disconnect(); - resizeObserver = null; - } - - if (originalModel) { - originalModel.dispose(); - originalModel = null; - } - if (modifiedModel) { - modifiedModel.dispose(); - modifiedModel = null; - } - if (diffEditor) { - diffEditor.dispose(); - diffEditor = null; - } -} - -export { - initDiffEditor, - updateDiffContent, - getEditor, - dispose, - setEditorTheme, - updateDiffStats -}; diff --git a/Server/src/diffView/js/ui-controller.ts b/Server/src/diffView/js/ui-controller.ts deleted file mode 100644 index 6e8579ea..00000000 --- a/Server/src/diffView/js/ui-controller.ts +++ /dev/null @@ -1,162 +0,0 @@ -// ui-controller.ts - UI event handlers and state management -import { DiffViewMessageHandler } from '../../shared/webkit'; -/** - * UI state and file metadata - */ -let filePath: string | null = null; -let fileEditStatus: string | null = null; - -/** - * Interface for messages sent to Swift handlers - */ -interface SwiftMessage { - event: string; - data: { - filePath: string | null; - [key: string]: any; - }; -} - -/** - * Initialize and set up UI elements and their event handlers - * @param {string} initialPath - The initial file path - * @param {string} initialStatus - The initial file edit status - */ -function setupUI(initialPath: string | null = null, initialStatus: string | null = null): void { - filePath = initialPath; - fileEditStatus = initialStatus; - - if (filePath) { - showFilePath(filePath); - } - - const keepButton = document.getElementById('keep-button'); - const undoButton = document.getElementById('undo-button'); - const choiceButtons = document.getElementById('choice-buttons'); - - if (!keepButton || !undoButton || !choiceButtons) { - console.error("Could not find UI elements"); - return; - } - - // Set initial UI state - updateUIStatus(initialStatus); - - // Setup event listeners - keepButton.addEventListener('click', handleKeepButtonClick); - undoButton.addEventListener('click', handleUndoButtonClick); -} - -/** - * Update the UI based on file edit status - * @param {string} status - The current file edit status - */ -function updateUIStatus(status: string | null): void { - fileEditStatus = status; - const choiceButtons = document.getElementById('choice-buttons'); - - if (!choiceButtons) return; - - // Hide buttons if file has been modified - if (status && status !== "none") { - choiceButtons.classList.add('hidden'); - } else { - choiceButtons.classList.remove('hidden'); - } -} - -/** - * Update the file metadata - * @param {string} path - The file path - * @param {string} status - The file edit status - */ -function updateFileMetadata(path: string | null, status: string | null): void { - filePath = path; - updateUIStatus(status); - if (filePath) { - showFilePath(filePath) - } -} - -/** - * Handle the "Keep" button click - */ -function handleKeepButtonClick(): void { - // Send message to Swift handler - if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.swiftHandler) { - const message: SwiftMessage = { - event: 'keepButtonClicked', - data: { - filePath: filePath - } - }; - window.webkit.messageHandlers.swiftHandler.postMessage(message); - } else { - console.log('Keep button clicked, but no message handler found'); - } - - // Hide the choice buttons - const choiceButtons = document.getElementById('choice-buttons'); - if (choiceButtons) { - choiceButtons.classList.add('hidden'); - } -} - -/** - * Handle the "Undo" button click - */ -function handleUndoButtonClick(): void { - // Send message to Swift handler - if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.swiftHandler) { - const message: SwiftMessage = { - event: 'undoButtonClicked', - data: { - filePath: filePath - } - }; - window.webkit.messageHandlers.swiftHandler.postMessage(message); - } else { - console.log('Undo button clicked, but no message handler found'); - } - - // Hide the choice buttons - const choiceButtons = document.getElementById('choice-buttons'); - if (choiceButtons) { - choiceButtons.classList.add('hidden'); - } -} - -/** - * Get the current file path - * @returns {string} The current file path - */ -function getFilePath(): string | null { - return filePath; -} - -/** - * Show the current file path - */ -function showFilePath(path: string): void { - const filePathElement = document.getElementById('file-path'); - const fileName = path.split('/').pop() ?? ''; - if (filePathElement) { - filePathElement.textContent = fileName - } -} - -/** - * Get the current file edit status - * @returns {string} The current file edit status - */ -function getFileEditStatus(): string | null { - return fileEditStatus; -} - -export { - setupUI, - updateUIStatus, - updateFileMetadata, - getFilePath, - getFileEditStatus -}; \ No newline at end of file diff --git a/Server/src/shared/webkit.ts b/Server/src/shared/webkit.ts deleted file mode 100644 index 3b6948fe..00000000 --- a/Server/src/shared/webkit.ts +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Type definitions for WebKit message handlers used in WebView communication - */ - -/** - * Base WebKit message handler interface - */ -export interface WebkitMessageHandler { - postMessage(message: any): void; -} - -/** - * Terminal-specific message handler - */ -export interface TerminalMessageHandler extends WebkitMessageHandler { - postMessage(message: string): void; -} - -/** - * DiffView-specific message handler - */ -export interface DiffViewMessageHandler extends WebkitMessageHandler { - postMessage(message: object): void; -} - -/** - * WebKit message handlers container interface - */ -export interface WebkitMessageHandlers { - terminalInput: TerminalMessageHandler; - swiftHandler: DiffViewMessageHandler; - [key: string]: WebkitMessageHandler | undefined; -} - -/** - * Main WebKit interface exposed by WebViews - */ -export interface WebkitHandler { - messageHandlers: WebkitMessageHandlers; -} - -/** - * Add webkit to the global Window interface - */ -declare global { - interface Window { - webkit: WebkitHandler; - } -} \ No newline at end of file diff --git a/Server/src/terminal/index.ts b/Server/src/terminal/index.ts deleted file mode 100644 index e97ee33c..00000000 --- a/Server/src/terminal/index.ts +++ /dev/null @@ -1,52 +0,0 @@ -import '@xterm/xterm/css/xterm.css'; -import { Terminal } from '@xterm/xterm'; -import { TerminalAddon } from './terminalAddon'; - -declare global { - interface Window { - initializeTerminal: () => Terminal; - writeToTerminal: (text: string) => void; - clearTerminal: () => void; - } -} - -window.initializeTerminal = function (): Terminal { - const term = new Terminal({ - cursorBlink: true, - theme: { - background: '#1e1e1e', - foreground: '#cccccc', - cursor: '#ffffff', - selectionBackground: 'rgba(128, 128, 128, 0.4)' - }, - fontFamily: 'Menlo, Monaco, "Courier New", monospace', - fontSize: 13 - }); - - const terminalAddon = new TerminalAddon(); - term.loadAddon(terminalAddon); - - const terminalElement = document.getElementById('terminal'); - if (!terminalElement) { - throw new Error('Terminal element not found'); - } - term.open(terminalElement); - terminalAddon.fit(); - - // Handle window resize - window.addEventListener('resize', () => { - terminalAddon.fit(); - }); - - // Expose terminal API methods - window.writeToTerminal = function (text: string): void { - term.write(text); - terminalAddon.processTerminalOutput(text); - }; - - window.clearTerminal = function (): void { - term.clear(); - }; - - return term; -} diff --git a/Server/src/terminal/terminal.html b/Server/src/terminal/terminal.html deleted file mode 100644 index a35ac6fb..00000000 --- a/Server/src/terminal/terminal.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - -
- - - - diff --git a/Server/src/terminal/terminalAddon.ts b/Server/src/terminal/terminalAddon.ts deleted file mode 100644 index bf78dfe5..00000000 --- a/Server/src/terminal/terminalAddon.ts +++ /dev/null @@ -1,326 +0,0 @@ -import { FitAddon } from '@xterm/addon-fit'; -import { Terminal, ITerminalAddon } from '@xterm/xterm'; -import { TerminalMessageHandler } from '../shared/webkit'; - -interface TermSize { - cols: number; - rows: number; -} - -interface TerminalPosition { - row: number; - col: number; -} - -// https://xtermjs.org/docs/api/vtfeatures/ -// https://en.wikipedia.org/wiki/ANSI_escape_code -const VT = { - ESC: '\x1b', - CSI: '\x1b[', - UP_ARROW: '\x1b[A', - DOWN_ARROW: '\x1b[B', - RIGHT_ARROW: '\x1b[C', - LEFT_ARROW: '\x1b[D', - HOME_KEY: ['\x1b[H', '\x1bOH'], - END_KEY: ['\x1b[F', '\x1bOF'], - DELETE_REST_OF_LINE: '\x1b[K', - CursorUp: (n = 1) => `\x1b[${n}A`, - CursorDown: (n = 1) => `\x1b[${n}B`, - CursorForward: (n = 1) => `\x1b[${n}C`, - CursorBack: (n = 1) => `\x1b[${n}D` -}; - -/** - * Key code constants - */ -const KeyCodes = { - CONTROL_C: 3, - CONTROL_D: 4, - ENTER: 13, - BACKSPACE: 8, - DELETE: 127 -}; - -export class TerminalAddon implements ITerminalAddon { - private term: Terminal | null; - private fitAddon: FitAddon; - private inputBuffer: string; - private cursor: number; - private promptInLastLine: string; - private termSize: TermSize; - - constructor() { - this.term = null; - this.fitAddon = new FitAddon(); - this.inputBuffer = ''; - this.cursor = 0; - this.promptInLastLine = ''; - this.termSize = { - cols: 0, - rows: 0, - }; - } - - dispose(): void { - this.fitAddon.dispose(); - } - - activate(terminal: Terminal): void { - this.term = terminal; - this.termSize = { - cols: terminal.cols, - rows: terminal.rows, - }; - this.fitAddon.activate(terminal); - this.term.onData(this.handleData.bind(this)); - this.term.onResize(this.handleResize.bind(this)); - } - - fit(): void { - this.fitAddon.fit(); - } - - private handleData(data: string): void { - // If the input is a longer string (e.g., from paste), and it contains newlines - if (data.length > 1 && !data.startsWith(VT.ESC)) { - const lines = data.split(/(\r\n|\n|\r)/g); - - let lineIndex = 0; - const processLine = () => { - if (lineIndex >= lines.length) return; - - const line = lines[lineIndex]; - if (line === '\n' || line === '\r' || line === '\r\n') { - if (this.cursor > 0) { - this.clearInputLine(); - this.cursor = 0; - this.renderInputLine(this.inputBuffer); - } - window.webkit.messageHandlers.terminalInput.postMessage(this.inputBuffer + '\n'); - this.inputBuffer = ''; - this.cursor = 0; - lineIndex++; - setTimeout(processLine, 100); - return; - } - - this.handleSingleLine(line); - lineIndex++; - processLine(); - }; - - processLine(); - return; - } - - // Handle escape sequences for special keys - if (data.startsWith(VT.ESC)) { - this.handleEscSequences(data); - return; - } - - this.handleSingleLine(data); - } - - private handleSingleLine(data: string): void { - if (data.length === 0) return; - - const char = data.charCodeAt(0); - // Handle control characters - if (char < 32 || char === 127) { - // Handle Enter key (carriage return) - if (char === KeyCodes.ENTER) { - if (this.cursor > 0) { - this.clearInputLine(); - this.cursor = 0; - this.renderInputLine(this.inputBuffer); - } - window.webkit.messageHandlers.terminalInput.postMessage(this.inputBuffer + '\n'); - this.inputBuffer = ''; - this.cursor = 0; - } - else if (char === KeyCodes.CONTROL_C || char === KeyCodes.CONTROL_D) { - if (this.cursor > 0) { - this.clearInputLine(); - this.cursor = 0; - this.renderInputLine(this.inputBuffer); - } - window.webkit.messageHandlers.terminalInput.postMessage(this.inputBuffer + data); - this.inputBuffer = ''; - this.cursor = 0; - } - // Handle backspace or delete - else if (char === KeyCodes.BACKSPACE || char === KeyCodes.DELETE) { - if (this.cursor > 0) { - this.clearInputLine(); - - // Delete character at cursor position - 1 - const beforeCursor = this.inputBuffer.substring(0, this.cursor - 1); - const afterCursor = this.inputBuffer.substring(this.cursor); - const newInput = beforeCursor + afterCursor; - this.cursor--; - this.renderInputLine(newInput); - } - } - return; - } - - this.clearInputLine(); - - // Insert character at cursor position - const beforeCursor = this.inputBuffer.substring(0, this.cursor); - const afterCursor = this.inputBuffer.substring(this.cursor); - const newInput = beforeCursor + data + afterCursor; - this.cursor += data.length; - this.renderInputLine(newInput); - } - - private handleResize(data: { cols: number; rows: number }): void { - this.clearInputLine(); - this.termSize = { - cols: data.cols, - rows: data.rows, - }; - this.renderInputLine(this.inputBuffer); - } - - private clearInputLine(): void { - if (!this.term) return; - // Move to beginning of the current line - this.term.write('\r'); - const cursorPosition = this.calcCursorPosition(); - const inputEndPosition = this.calcLineWrapPosition(this.promptInLastLine.length + this.inputBuffer.length); - // If cursor is not at the end of input, move to the end - if (cursorPosition.row < inputEndPosition.row) { - this.term.write(VT.CursorDown(inputEndPosition.row - cursorPosition.row)); - } else if (cursorPosition.row > inputEndPosition.row) { - this.term.write(VT.CursorUp(cursorPosition.row - inputEndPosition.row)); - } - - // Clear from the last line upwards - this.term.write('\r' + VT.DELETE_REST_OF_LINE); - for (let i = inputEndPosition.row - 1; i >= 0; i--) { - this.term.write(VT.CursorUp(1)); - this.term.write('\r' + VT.DELETE_REST_OF_LINE); - } - }; - - // Function to render the input line considering line wrapping - private renderInputLine(newInput: string): void { - if (!this.term) return; - this.inputBuffer = newInput; - // Write prompt and input - this.term.write(this.promptInLastLine + this.inputBuffer); - const cursorPosition = this.calcCursorPosition(); - const inputEndPosition = this.calcLineWrapPosition(this.promptInLastLine.length + this.inputBuffer.length); - // If the last input char is at the end of the terminal width, - // need to print an extra empty line to display the cursor. - if (inputEndPosition.col == 0) { - this.term.write(' '); - this.term.write(VT.CursorBack(1)); - this.term.write(VT.DELETE_REST_OF_LINE); - } - - if (this.inputBuffer.length === this.cursor) { - return; - } - - // Move the cursor from the input end to the expected cursor row - if (cursorPosition.row < inputEndPosition.row) { - this.term.write(VT.CursorUp(inputEndPosition.row - cursorPosition.row)); - } - this.term.write('\r'); - if (cursorPosition.col > 0) { - this.term.write(VT.CursorForward(cursorPosition.col)); - } - }; - - private calcCursorPosition(): TerminalPosition { - return this.calcLineWrapPosition(this.promptInLastLine.length + this.cursor); - } - - private calcLineWrapPosition(textLength: number): TerminalPosition { - if (!this.term) { - return { row: 0, col: 0 }; - } - const row = Math.floor(textLength / this.termSize.cols); - const col = textLength % this.termSize.cols; - - return { row, col }; - } - - /** - * Handle ESC sequences - */ - private handleEscSequences(data: string): void { - if (!this.term) return; - switch (data) { - case VT.UP_ARROW: - // TODO: Could implement command history here - break; - - case VT.DOWN_ARROW: - // TODO: Could implement command history here - break; - - case VT.RIGHT_ARROW: - if (this.cursor < this.inputBuffer.length) { - this.clearInputLine(); - this.cursor++; - this.renderInputLine(this.inputBuffer); - } - break; - - case VT.LEFT_ARROW: - if (this.cursor > 0) { - this.clearInputLine(); - this.cursor--; - this.renderInputLine(this.inputBuffer); - } - break; - } - - // Handle Home key variations - if (VT.HOME_KEY.includes(data)) { - this.clearInputLine(); - this.cursor = 0; - this.renderInputLine(this.inputBuffer); - } - - // Handle End key variations - if (VT.END_KEY.includes(data)) { - this.clearInputLine(); - this.cursor = this.inputBuffer.length; - this.renderInputLine(this.inputBuffer); - } - }; - - /** - * Remove OSC escape sequences from text - */ - private removeOscSequences(text: string): string { - // Remove basic OSC sequences - let filteredText = text.replace(/\u001b\]\d+;[^\u0007\u001b]*[\u0007\u001b\\]/g, ''); - - // More comprehensive approach for nested sequences - return filteredText.replace(/\u001b\][^\u0007\u001b]*(?:\u0007|\u001b\\)/g, ''); - }; - - /** - * Process terminal output and update prompt tracking - */ - processTerminalOutput(text: string): void { - if (typeof text !== 'string') return; - - const lastNewline = text.lastIndexOf('\n'); - const lastCarriageReturn = text.lastIndexOf('\r'); - const lastControlChar = Math.max(lastNewline, lastCarriageReturn); - let newPromptText = lastControlChar !== -1 ? text.substring(lastControlChar + 1) : text; - - // Filter out OSC sequences - newPromptText = this.removeOscSequences(newPromptText); - - this.promptInLastLine = lastControlChar !== -1 ? - newPromptText : this.promptInLastLine + newPromptText; - }; -} diff --git a/Server/tsconfig.json b/Server/tsconfig.json deleted file mode 100644 index 71eb52f9..00000000 --- a/Server/tsconfig.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2020", - "module": "ESNext", - "moduleResolution": "node", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "outDir": "./dist", - "sourceMap": true, - "allowJs": true, - "checkJs": false - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist"] -} \ No newline at end of file diff --git a/Server/webpack.config.js b/Server/webpack.config.js deleted file mode 100644 index 2ace244b..00000000 --- a/Server/webpack.config.js +++ /dev/null @@ -1,77 +0,0 @@ -const path = require('path'); -const CopyWebpackPlugin = require('copy-webpack-plugin'); -const webpack = require('webpack'); -const TerserPlugin = require('terser-webpack-plugin'); - -/* - * The folder structure of `dist` would be: - * dist/ - * ├── terminal/ - * │ ├── terminal.js - * │ └── terminal.html - * └── diffView/ - * ├── diffView.js - * ├── diffView.html - * └── css/ - * └── style.css -*/ -module.exports = { - mode: 'production', - entry: { - // Add more entry points here - terminal: './src/terminal/index.ts', - diffView: './src/diffView/index.ts' - }, - resolve: { - extensions: ['.ts', '.js'] - }, - output: { - filename: '[name]/[name].js', - path: path.resolve(__dirname, 'dist'), - }, - module: { - rules: [ - { - test: /\.tsx?$/, - use: 'ts-loader', - exclude: /node_modules/ - }, - { - test: /\.css$/, - use: ['style-loader', 'css-loader'] - } - ] - }, - plugins: [ - new CopyWebpackPlugin({ - patterns: [ - /// MARK: - Terminal component files - { - from: 'src/terminal/terminal.html', - to: 'terminal/terminal.html' - }, - - /// MARK: - DiffView component files - { - from: 'src/diffView/diffView.html', - to: 'diffView/diffView.html' - }, - { - from: 'src/diffView/css', - to: 'diffView/css' - } - ] - }), - new webpack.optimize.LimitChunkCountPlugin({ - maxChunks: 1 - }) - ], - optimization: { - minimizer: [ - new TerserPlugin({ - // Prevent extracting license comments to a separate file - extractComments: false - }) - ] - } -}; diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md deleted file mode 100644 index 4c179941..00000000 --- a/TROUBLESHOOTING.md +++ /dev/null @@ -1,94 +0,0 @@ -# Troubleshooting for Copilot for Xcode - -If you are having trouble with Copilot for Xcode follow these steps to resolve -common issues: - -1. Check for updates and restart Xcode. Ensure that Copilot for Xcode has the - [latest release](https://github.com/github/CopilotForXcode/releases/latest) - by clicking `Check for Updates` in the settings or under the status menu. After - updating, restart Xcode. - -2. Ensure that all required permissions are granted. GitHub Copilot for Xcode app requires these permissions to function properly: - - [Extension Permission](#extension-permission) - Allows GitHub Copilot to integrate with Xcode - - [Accessibility Permission](#accessibility-permission) - Enables real-time code suggestions - - [Background Permission](#background-permission) - Allows extension to connect with host app - - Please note that GitHub Copilot for Xcode may not work properly if any necessary permissions are missing. - -3. Need more help? If these steps don't resolve the issue, please [open an - issue](https://github.com/github/CopilotForXcode/issues/new/choose). Make - sure to [include logs](#logs) and any other relevant information. - -## Extension Permission - -GitHub Copilot for Xcode is an Xcode Source Editor extension and requires the -extension to be enabled. In the Copilot for Xcode settings, clicking `Extension -Permission` will open the System Settings to the Extensions page where `GitHub -Copilot` can be enabled under `Xcode Source Editor`. - -Or you can navigate to the permission manually depending on your OS version: - -| macOS | Location | -| :--- | :--- | -| 15 | System Settings > General > Login Items > Extensions > Xcode Source Editor | -| 13 & 14 | System Settings > Privacy & Security > Extensions > Xcode Source Editor | -| 12 | System Preferences > Extensions | - -## Accessibility Permission - -GitHub Copilot for Xcode requires the accessibility permission to receive -real-time updates from the active Xcode editor. [The XcodeKit -API](https://developer.apple.com/documentation/xcodekit) -enabled by the Xcode Source Editor extension permission only provides -information when manually triggered by the user. In order to generate -suggestions as you type, the accessibility permission is used to read the -Xcode editor content in real-time. - -The accessibility permission is also used to accept suggestions when `tab` is -pressed. - -The accessibility permission is __not__ used to read or write to any -applications besides Xcode. There are no granular options for the permission, -but you can audit the usage in this repository: search for `CGEvent` and `AX`*. - -Enable in System Settings under `Privacy & Security` > `Accessibility` > -`GitHub Copilot for Xcode Extension` and turn on the toggle. - -## Background Permission - -GitHub Copilot for Xcode requires background permission to connect with the host app. This permission ensures proper communication between the components of GitHub Copilot for Xcode, which is essential for its functionality in Xcode. - - -

- Background Permission -

- -This permission is typically granted automatically when you first launch GitHub Copilot for Xcode. However, if you encounter connection issues, alerts, or errors as follows: - -

- Alert of Background Permission Required - Error connecting to the communication bridge -

- -Please ensure that this permission is enabled. You can manually navigate to the background permission setting based on your macOS version: - -| macOS | Location | -| :--- | :--- | -| 15 | System Settings > General > Login Items & Extensions > Allow in the Background | -| 13 & 14 | System Settings > General > Login Items > Allow in the Background | - -Ensure that "GitHub Copilot for Xcode" is enabled in the list of allowed background items. Without this permission, the extension may not be able to properly communicate with the host app, which can result in inconsistent behavior or reduced functionality. - - -## Logs - -Logs can be found in `~/Library/Logs/GitHubCopilot/` the most recent log file -is: - -``` -~/Library/Logs/GitHubCopilot/github-copilot-for-xcode.log -``` - -To enable verbose logging, open the GitHub Copilot for Xcode settings and enable -`Verbose Logging` in the `Advanced` tab. After enabling verbose logging, restart -Copilot for Xcode for the change to take effect. diff --git a/TestPlan.xctestplan b/TestPlan.xctestplan deleted file mode 100644 index a46ddf32..00000000 --- a/TestPlan.xctestplan +++ /dev/null @@ -1,118 +0,0 @@ -{ - "configurations" : [ - { - "id" : "586480F5-DC84-425D-814F-7A5F569A1974", - "name" : "Configuration 1", - "options" : { - - } - } - ], - "defaultOptions" : { - "environmentVariableEntries" : [ - { - "key" : "IS_UNIT_TEST", - "value" : "YES" - }, - { - "key" : "SUEnableAutomaticChecks", - "value" : "NO" - } - ], - "testTimeoutsEnabled" : true - }, - "testTargets" : [ - { - "target" : { - "containerPath" : "container:Core", - "identifier" : "ServiceTests", - "name" : "ServiceTests" - } - }, - { - "target" : { - "containerPath" : "container:Core", - "identifier" : "SuggestionInjectorTests", - "name" : "SuggestionInjectorTests" - } - }, - { - "target" : { - "containerPath" : "container:Core", - "identifier" : "SuggestionWidgetTests", - "name" : "SuggestionWidgetTests" - } - }, - { - "target" : { - "containerPath" : "container:Tool", - "identifier" : "SuggestionBasicTests", - "name" : "SuggestionBasicTests" - } - }, - { - "target" : { - "containerPath" : "container:Tool", - "identifier" : "SharedUIComponentsTests", - "name" : "SharedUIComponentsTests" - } - }, - { - "target" : { - "containerPath" : "container:Tool", - "identifier" : "GitHubCopilotServiceTests", - "name" : "GitHubCopilotServiceTests" - } - }, - { - "target" : { - "containerPath" : "container:Tool", - "identifier" : "XcodeInspectorTests", - "name" : "XcodeInspectorTests" - } - }, - { - "target" : { - "containerPath" : "container:Tool", - "identifier" : "SuggestionProviderTests", - "name" : "SuggestionProviderTests" - } - }, - { - "target" : { - "containerPath" : "container:Core", - "identifier" : "KeyBindingManagerTests", - "name" : "KeyBindingManagerTests" - } - }, - { - "target" : { - "containerPath" : "container:Tool", - "identifier" : "WorkspaceSuggestionServiceTests", - "name" : "WorkspaceSuggestionServiceTests" - } - }, - { - "target" : { - "containerPath" : "container:Tool", - "identifier" : "WorkspaceTests", - "name" : "WorkspaceTests" - } - }, - { - "target" : { - "containerPath" : "container:Core", - "identifier" : "ChatServiceTests", - "name" : "ChatServiceTests" - } - }, - { - "target" : { - "containerPath" : "container:Tool", - "identifier" : "SystemUtilsTests", - "name" : "SystemUtilsTests" - } - } - ], - "version" : 1 -} diff --git a/Tool/Package.swift b/Tool/Package.swift deleted file mode 100644 index e7c4e9f3..00000000 --- a/Tool/Package.swift +++ /dev/null @@ -1,366 +0,0 @@ -// swift-tools-version: 5.7 -// The swift-tools-version declares the minimum version of Swift required to build this package. - -import PackageDescription - -let package = Package( - name: "Tool", - platforms: [.macOS(.v12)], - products: [ - .library(name: "XPCShared", targets: ["XPCShared"]), - .library(name: "Terminal", targets: ["Terminal"]), - .library(name: "Preferences", targets: ["Preferences", "Configs"]), - .library(name: "Logger", targets: ["Logger"]), - .library(name: "SystemUtils", targets: ["SystemUtils"]), - .library(name: "ChatAPIService", targets: ["ChatAPIService"]), - .library(name: "ChatTab", targets: ["ChatTab"]), - .library(name: "FileSystem", targets: ["FileSystem"]), - .library(name: "SuggestionBasic", targets: ["SuggestionBasic"]), - .library(name: "Toast", targets: ["Toast"]), - .library(name: "SharedUIComponents", targets: ["SharedUIComponents"]), - .library(name: "Status", targets: ["Status"]), - .library(name: "Persist", targets: ["Persist"]), - .library(name: "UserDefaultsObserver", targets: ["UserDefaultsObserver"]), - .library(name: "Workspace", targets: ["Workspace", "WorkspaceSuggestionService"]), - .library(name: "WebContentExtractor", targets: ["WebContentExtractor"]), - .library( - name: "SuggestionProvider", - targets: ["SuggestionProvider"] - ), - .library( - name: "ConversationServiceProvider", - targets: ["ConversationServiceProvider"] - ), - .library( - name: "TelemetryServiceProvider", - targets: ["TelemetryServiceProvider"] - ), - .library( - name: "TelemetryService", - targets: ["TelemetryService"] - ), - .library( - name: "GitHubCopilotService", - targets: ["GitHubCopilotService"] - ), - .library( - name: "BuiltinExtension", - targets: ["BuiltinExtension"] - ), - .library( - name: "AppMonitoring", - targets: [ - "XcodeInspector", - "ActiveApplicationMonitor", - "AXExtension", - "AXNotificationStream", - "AppActivator", - ] - ), - .library(name: "DebounceFunction", targets: ["DebounceFunction"]), - .library(name: "AsyncPassthroughSubject", targets: ["AsyncPassthroughSubject"]), - .library(name: "CustomAsyncAlgorithms", targets: ["CustomAsyncAlgorithms"]), - .library(name: "AXHelper", targets: ["AXHelper"]), - .library(name: "Cache", targets: ["Cache"]), - .library(name: "StatusBarItemView", targets: ["StatusBarItemView"]), - .library(name: "HostAppActivator", targets: ["HostAppActivator"]), - .library(name: "AppKitExtension", targets: ["AppKitExtension"]) - ], - dependencies: [ - // TODO: Update LanguageClient some day. - .package(url: "https://github.com/ChimeHQ/LanguageClient", exact: "0.8.2"), - .package(url: "https://github.com/ChimeHQ/LanguageServerProtocol", exact: "0.13.3"), - .package(url: "https://github.com/apple/swift-async-algorithms", from: "1.0.0"), - .package(url: "https://github.com/pointfreeco/swift-parsing", from: "0.12.1"), - .package(url: "https://github.com/ChimeHQ/JSONRPC", exact: "0.9.0"), - .package(url: "https://github.com/devm33/Highlightr", branch: "master"), - .package( - url: "https://github.com/pointfreeco/swift-composable-architecture", - from: "1.10.4" - ), - .package(url: "https://github.com/GottaGetSwifty/CodableWrappers", from: "2.0.7"), - // TODO: remove CopilotForXcodeKit dependency once extension provider logic is removed. - .package(url: "https://github.com/devm33/CopilotForXcodeKit", branch: "main"), - .package(url: "https://github.com/stephencelis/SQLite.swift", from: "0.15.3"), - .package(url: "https://github.com/scinfu/SwiftSoup.git", from: "2.9.6") - ], - targets: [ - // MARK: - Helpers - - .target(name: "XPCShared", dependencies: ["SuggestionBasic", "Logger", "Status", "HostAppActivator", "GitHubCopilotService"]), - - .target(name: "Configs"), - - .target(name: "Preferences", dependencies: ["Configs"]), - - .target(name: "Terminal", dependencies: ["Logger", "SystemUtils"]), - - .target(name: "WebContentExtractor", dependencies: ["Logger", "SwiftSoup", "Preferences"]), - - .target(name: "Logger"), - - .target(name: "FileSystem"), - - .target( - name: "CustomAsyncAlgorithms", - dependencies: [ - .product(name: "AsyncAlgorithms", package: "swift-async-algorithms"), - ] - ), - - .target( - name: "Toast", - dependencies: [ - "AppKitExtension", - .product(name: "ComposableArchitecture", package: "swift-composable-architecture") - ] - ), - - .target(name: "DebounceFunction"), - - .target( - name: "AppActivator", - dependencies: [ - "XcodeInspector", - .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), - ] - ), - - .target(name: "ActiveApplicationMonitor"), - - .target( - name: "HostAppActivator", - dependencies: [ - "Logger", - ] - ), - - .target( - name: "SuggestionBasic", - dependencies: [ - "LanguageClient", - .product(name: "Parsing", package: "swift-parsing"), - .product(name: "CodableWrappers", package: "CodableWrappers"), - ] - ), - - .testTarget( - name: "SuggestionBasicTests", - dependencies: ["SuggestionBasic"] - ), - - .target(name: "AXExtension"), - - .target( - name: "AXNotificationStream", - dependencies: [ - "Preferences", - "Logger", - "Status", - ] - ), - - .target( - name: "XcodeInspector", - dependencies: [ - "AXExtension", - "SuggestionBasic", - "AXNotificationStream", - "Logger", - "Toast", - "Preferences", - "AsyncPassthroughSubject", - "Status", - .product(name: "AsyncAlgorithms", package: "swift-async-algorithms"), - ] - ), - - .testTarget(name: "XcodeInspectorTests", dependencies: ["XcodeInspector"]), - - .target(name: "UserDefaultsObserver"), - - .target(name: "AsyncPassthroughSubject"), - - .target( - name: "BuiltinExtension", - dependencies: [ - "SuggestionBasic", - "SuggestionProvider", - "Workspace", - .product(name: "CopilotForXcodeKit", package: "CopilotForXcodeKit"), - ] - ), - - .target( - name: "SharedUIComponents", - dependencies: [ - "Highlightr", - "Preferences", - "SuggestionBasic", - "DebounceFunction", - "ConversationServiceProvider", - .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), - ] - ), - .testTarget(name: "SharedUIComponentsTests", dependencies: ["SharedUIComponents"]), - - .target( - name: "Workspace", - dependencies: [ - "UserDefaultsObserver", - "SuggestionBasic", - "Logger", - "Preferences", - "XcodeInspector", - "ConversationServiceProvider" - ] - ), - .testTarget(name: "WorkspaceTests", dependencies: ["Workspace"]), - - .target( - name: "WorkspaceSuggestionService", - dependencies: [ - "Workspace", - "SuggestionProvider", - "XPCShared", - "BuiltinExtension", - "GitHubCopilotService", - ] - ), - - .target( - name: "AXHelper", - dependencies: [ - "XPCShared", - "XcodeInspector" - ] - ), - - .target(name: "StatusBarItemView", dependencies: ["Cache"]), - - .target( - name: "Cache" - ), - - .testTarget( - name: "WorkspaceSuggestionServiceTests", - dependencies: [ - "ConversationServiceProvider", - "WorkspaceSuggestionService" - ] - ), - - // MARK: - Services - - .target( - name: "Status", - dependencies: ["Cache"] - ), - - .target( - name: "Persist", - dependencies: [ - "Logger", - "Status", - .product(name: "SQLite", package: "SQLite.Swift") - ] - ), - - .target(name: "SuggestionProvider", dependencies: [ - "SuggestionBasic", - "UserDefaultsObserver", - "Preferences", - "Logger", - .product(name: "CopilotForXcodeKit", package: "CopilotForXcodeKit"), - ]), - .testTarget(name: "SuggestionProviderTests", dependencies: ["SuggestionProvider"]), - - .target(name: "ConversationServiceProvider", dependencies: [ - .product(name: "CopilotForXcodeKit", package: "CopilotForXcodeKit"), - .product(name: "LanguageServerProtocol", package: "LanguageServerProtocol"), - ]), - - .target(name: "TelemetryServiceProvider", dependencies: [ - .product(name: "CopilotForXcodeKit", package: "CopilotForXcodeKit"), - ]), - - .target( - name: "TelemetryService", - dependencies: [ - "TelemetryServiceProvider", - "GitHubCopilotService", - "BuiltinExtension", - "SystemUtils", - "UserDefaultsObserver", - "Preferences" - ]), - - - // MARK: - GitHub Copilot - - .target( - name: "GitHubCopilotService", - dependencies: [ - "LanguageClient", - "SuggestionBasic", - "Logger", - "Preferences", - "Terminal", - "BuiltinExtension", - "ConversationServiceProvider", - "TelemetryServiceProvider", - "Status", - "SystemUtils", - "Workspace", - "Persist", - .product(name: "LanguageServerProtocol", package: "LanguageServerProtocol"), - .product(name: "CopilotForXcodeKit", package: "CopilotForXcodeKit"), - ] - ), - .testTarget( - name: "GitHubCopilotServiceTests", - dependencies: ["GitHubCopilotService", - "ConversationServiceProvider"] - ), - - // MARK: - ChatAPI - - .target( - name: "ChatAPIService", - dependencies: [ - "Logger", - "Preferences", - "GitHubCopilotService", - .product(name: "JSONRPC", package: "JSONRPC"), - .product(name: "AsyncAlgorithms", package: "swift-async-algorithms"), - .product( - name: "ComposableArchitecture", - package: "swift-composable-architecture" - ), - ] - ), - - // MARK: - UI - - .target( - name: "ChatTab", - dependencies: [.product( - name: "ComposableArchitecture", - package: "swift-composable-architecture" - )] - ), - - // MARK: - SystemUtils - - .target( - name: "SystemUtils", - dependencies: ["Logger"] - ), - .testTarget(name: "SystemUtilsTests", dependencies: ["SystemUtils"]), - - // MARK: - AppKitExtension - - .target(name: "AppKitExtension") - ] -) - diff --git a/Tool/Sources/AXExtension/AXUIElement.swift b/Tool/Sources/AXExtension/AXUIElement.swift deleted file mode 100644 index 1a790e20..00000000 --- a/Tool/Sources/AXExtension/AXUIElement.swift +++ /dev/null @@ -1,329 +0,0 @@ -import AppKit -import Foundation - -// MARK: - State - -public extension AXUIElement { - /// Set global timeout in seconds. - static func setGlobalMessagingTimeout(_ timeout: Float) { - AXUIElementSetMessagingTimeout(AXUIElementCreateSystemWide(), timeout) - } - - /// Set timeout in seconds for this element. - func setMessagingTimeout(_ timeout: Float) { - AXUIElementSetMessagingTimeout(self, timeout) - } - - var identifier: String { - (try? copyValue(key: kAXIdentifierAttribute)) ?? "" - } - - var value: String { - (try? copyValue(key: kAXValueAttribute)) ?? "" - } - - var intValue: Int? { - (try? copyValue(key: kAXValueAttribute)) - } - - var title: String { - (try? copyValue(key: kAXTitleAttribute)) ?? "" - } - - var role: String { - (try? copyValue(key: kAXRoleAttribute)) ?? "" - } - - var doubleValue: Double { - (try? copyValue(key: kAXValueAttribute)) ?? 0.0 - } - - var document: String? { - try? copyValue(key: kAXDocumentAttribute) - } - - /// Label in Accessibility Inspector. - var description: String { - (try? copyValue(key: kAXDescriptionAttribute)) ?? "" - } - - /// Type in Accessibility Inspector. - var roleDescription: String { - (try? copyValue(key: kAXRoleDescriptionAttribute)) ?? "" - } - - var label: String { - (try? copyValue(key: kAXLabelValueAttribute)) ?? "" - } - - var isSourceEditor: Bool { - description == "Source Editor" - } - - var isEditorArea: Bool { - description == "editor area" - } - - var isXcodeWorkspaceWindow: Bool { - description == "Xcode.WorkspaceWindow" || identifier == "Xcode.WorkspaceWindow" - } - - var selectedTextRange: ClosedRange? { - guard let value: AXValue = try? copyValue(key: kAXSelectedTextRangeAttribute) - else { return nil } - var range: CFRange = .init(location: 0, length: 0) - if AXValueGetValue(value, .cfRange, &range) { - return range.location...(range.location + range.length) - } - return nil - } - - var isFocused: Bool { - (try? copyValue(key: kAXFocusedAttribute)) ?? false - } - - var isEnabled: Bool { - (try? copyValue(key: kAXEnabledAttribute)) ?? false - } - - var isHidden: Bool { - (try? copyValue(key: kAXHiddenAttribute)) ?? false - } -} - -// MARK: - Rect - -public extension AXUIElement { - var position: CGPoint? { - guard let value: AXValue = try? copyValue(key: kAXPositionAttribute) - else { return nil } - var point: CGPoint = .zero - if AXValueGetValue(value, .cgPoint, &point) { - return point - } - return nil - } - - var size: CGSize? { - guard let value: AXValue = try? copyValue(key: kAXSizeAttribute) - else { return nil } - var size: CGSize = .zero - if AXValueGetValue(value, .cgSize, &size) { - return size - } - return nil - } - - var rect: CGRect? { - guard let position, let size else { return nil } - return .init(origin: position, size: size) - } -} - -// MARK: - Relationship - -public extension AXUIElement { - var focusedElement: AXUIElement? { - try? copyValue(key: kAXFocusedUIElementAttribute) - } - - var sharedFocusElements: [AXUIElement] { - (try? copyValue(key: kAXChildrenAttribute)) ?? [] - } - - var window: AXUIElement? { - try? copyValue(key: kAXWindowAttribute) - } - - var windows: [AXUIElement] { - (try? copyValue(key: kAXWindowsAttribute)) ?? [] - } - - var isFullScreen: Bool { - (try? copyValue(key: "AXFullScreen")) ?? false - } - - var focusedWindow: AXUIElement? { - try? copyValue(key: kAXFocusedWindowAttribute) - } - - var topLevelElement: AXUIElement? { - try? copyValue(key: kAXTopLevelUIElementAttribute) - } - - var rows: [AXUIElement] { - (try? copyValue(key: kAXRowsAttribute)) ?? [] - } - - var parent: AXUIElement? { - try? copyValue(key: kAXParentAttribute) - } - - var children: [AXUIElement] { - (try? copyValue(key: kAXChildrenAttribute)) ?? [] - } - - var menuBar: AXUIElement? { - try? copyValue(key: kAXMenuBarAttribute) - } - - var visibleChildren: [AXUIElement] { - (try? copyValue(key: kAXVisibleChildrenAttribute)) ?? [] - } - - func child( - identifier: String? = nil, - title: String? = nil, - role: String? = nil - ) -> AXUIElement? { - for child in children { - let match = { - if let identifier, child.identifier != identifier { return false } - if let title, child.title != title { return false } - if let role, child.role != role { return false } - return true - }() - if match { return child } - } - for child in children { - if let target = child.child( - identifier: identifier, - title: title, - role: role - ) { return target } - } - return nil - } - - /// Get children that match the requirement - /// - /// - important: If the element has a lot of descendant nodes, it will heavily affect the - /// **performance of Xcode**. Please make use ``AXUIElement\traverse(_:)`` instead. - @available( - *, - deprecated, - renamed: "traverse(_:)", - message: "Please make use ``AXUIElement\traverse(_:)`` instead." - ) - func children(where match: (AXUIElement) -> Bool) -> [AXUIElement] { - var all = [AXUIElement]() - for child in children { - if match(child) { all.append(child) } - } - for child in children { - all.append(contentsOf: child.children(where: match)) - } - return all - } - - func firstParent(where match: (AXUIElement) -> Bool) -> AXUIElement? { - guard let parent = parent else { return nil } - if match(parent) { return parent } - return parent.firstParent(where: match) - } - - func firstChild(where match: (AXUIElement) -> Bool) -> AXUIElement? { - for child in children { - if match(child) { return child } - } - for child in children { - if let target = child.firstChild(where: match) { - return target - } - } - return nil - } - - func visibleChild(identifier: String) -> AXUIElement? { - for child in visibleChildren { - if child.identifier == identifier { return child } - if let target = child.visibleChild(identifier: identifier) { return target } - } - return nil - } - - var verticalScrollBar: AXUIElement? { - try? copyValue(key: kAXVerticalScrollBarAttribute) - } -} - -public extension AXUIElement { - enum SearchNextStep { - case skipDescendants - case skipSiblings - case skipDescendantsAndSiblings - case continueSearching - case stopSearching - } - /// Traversing the element tree. - /// - /// - important: Traversing the element tree is resource consuming and will affect the - /// **performance of Xcode**. Please make sure to skip as much as possible. - /// - /// - todo: Make it not recursive. - func traverse(_ handle: (_ element: AXUIElement, _ level: Int) -> SearchNextStep) { - func _traverse( - element: AXUIElement, - level: Int, - handle: (AXUIElement, Int) -> SearchNextStep - ) -> SearchNextStep { - let nextStep = handle(element, level) - switch nextStep { - case .stopSearching: return .stopSearching - case .skipDescendants: return .continueSearching - case .skipDescendantsAndSiblings: return .skipSiblings - case .continueSearching, .skipSiblings: - for child in element.children { - switch _traverse(element: child, level: level + 1, handle: handle) { - case .skipSiblings, .skipDescendantsAndSiblings: - break - case .stopSearching: - return .stopSearching - case .continueSearching, .skipDescendants: - continue - } - } - return nextStep - } - } - _ = _traverse(element: self, level: 0, handle: handle) - } -} - -// MARK: - Helper - -public extension AXUIElement { - func copyValue(key: String, ofType _: T.Type = T.self) throws -> T { - var value: AnyObject? - let error = AXUIElementCopyAttributeValue(self, key as CFString, &value) - if error == .success, let value = value as? T { - return value - } - throw error - } - - func copyParameterizedValue( - key: String, - parameters: AnyObject, - ofType _: T.Type = T.self - ) throws -> T { - var value: AnyObject? - let error = AXUIElementCopyParameterizedAttributeValue( - self, - key as CFString, - parameters as CFTypeRef, - &value - ) - if error == .success, let value = value as? T { - return value - } - throw error - } -} - -#if hasFeature(RetroactiveAttribute) -extension AXError: @retroactive Error {} -#else -extension AXError: Error {} -#endif - diff --git a/Tool/Sources/AXHelper/AXHelper.swift b/Tool/Sources/AXHelper/AXHelper.swift deleted file mode 100644 index c6e7405a..00000000 --- a/Tool/Sources/AXHelper/AXHelper.swift +++ /dev/null @@ -1,70 +0,0 @@ -import XPCShared -import XcodeInspector -import AppKit - -public struct AXHelper { - public init() {} - - /// When Xcode commands are not available, we can fallback to directly - /// set the value of the editor with Accessibility API. - public func injectUpdatedCodeWithAccessibilityAPI( - _ result: UpdatedContent, - focusElement: AXUIElement, - onSuccess: (() -> Void)? = nil, - onError: (() -> Void)? = nil - ) throws { - let oldPosition = focusElement.selectedTextRange - let oldScrollPosition = focusElement.parent?.verticalScrollBar?.doubleValue - - let error = AXUIElementSetAttributeValue( - focusElement, - kAXValueAttribute as CFString, - result.content as CFTypeRef - ) - - if error != AXError.success { - if let onError = onError { - onError() - } - } - - // recover selection range - if let selection = result.newSelection { - var range = SourceEditor.convertCursorRangeToRange(selection, in: result.content) - if let value = AXValueCreate(.cfRange, &range) { - AXUIElementSetAttributeValue( - focusElement, - kAXSelectedTextRangeAttribute as CFString, - value - ) - } - } else if let oldPosition { - var range = CFRange( - location: oldPosition.lowerBound, - length: 0 - ) - if let value = AXValueCreate(.cfRange, &range) { - AXUIElementSetAttributeValue( - focusElement, - kAXSelectedTextRangeAttribute as CFString, - value - ) - } - } - - // recover scroll position - if let oldScrollPosition, - let scrollBar = focusElement.parent?.verticalScrollBar - { - AXUIElementSetAttributeValue( - scrollBar, - kAXValueAttribute as CFString, - oldScrollPosition as CFTypeRef - ) - } - - if let onSuccess = onSuccess { - onSuccess() - } - } -} diff --git a/Tool/Sources/AXNotificationStream/AXNotificationStream.swift b/Tool/Sources/AXNotificationStream/AXNotificationStream.swift deleted file mode 100644 index f4b3f194..00000000 --- a/Tool/Sources/AXNotificationStream/AXNotificationStream.swift +++ /dev/null @@ -1,171 +0,0 @@ -import AppKit -import ApplicationServices -import Foundation -import Logger -import Preferences -import Status - -public final class AXNotificationStream: AsyncSequence { - public typealias Stream = AsyncStream - public typealias Continuation = Stream.Continuation - public typealias AsyncIterator = Stream.AsyncIterator - public typealias Element = (name: String, element: AXUIElement, info: CFDictionary) - - private var continuation: Continuation - private let stream: Stream - - private let file: StaticString - private let line: UInt - private let function: StaticString - - public func makeAsyncIterator() -> Stream.AsyncIterator { - stream.makeAsyncIterator() - } - - deinit { - continuation.finish() - } - - public convenience init( - app: NSRunningApplication, - element: AXUIElement? = nil, - notificationNames: String..., - file: StaticString = #file, - line: UInt = #line, - function: StaticString = #function - ) { - self.init( - app: app, - element: element, - notificationNames: notificationNames, - file: file, - line: line, - function: function - ) - } - - public init( - app: NSRunningApplication, - element: AXUIElement? = nil, - notificationNames: [String], - file: StaticString = #file, - line: UInt = #line, - function: StaticString = #function - ) { - self.file = file - self.line = line - self.function = function - - let mode: CFRunLoopMode = UserDefaults.shared - .value(for: \.observeToAXNotificationWithDefaultMode) ? .defaultMode : .commonModes - - let runLoop: CFRunLoop = CFRunLoopGetMain() - - var cont: Continuation! - stream = Stream { continuation in - cont = continuation - } - continuation = cont - var observer: AXObserver? - - func callback( - observer: AXObserver, - element: AXUIElement, - notificationName: CFString, - userInfo: CFDictionary, - pointer: UnsafeMutableRawPointer? - ) { - guard let pointer = pointer?.assumingMemoryBound(to: Continuation.self) - else { return } - pointer.pointee.yield((notificationName as String, element, userInfo)) - } - - _ = AXObserverCreateWithInfoCallback( - app.processIdentifier, - callback, - &observer - ) - guard let observer else { - continuation.finish() - return - } - - let observingElement = element ?? AXUIElementCreateApplication(app.processIdentifier) - continuation.onTermination = { @Sendable _ in - for name in notificationNames { - AXObserverRemoveNotification(observer, observingElement, name as CFString) - } - CFRunLoopRemoveSource( - runLoop, - AXObserverGetRunLoopSource(observer), - mode - ) - } - - Task { @MainActor [weak self] in - CFRunLoopAddSource( - runLoop, - AXObserverGetRunLoopSource(observer), - mode - ) - var pendingRegistrationNames = Set(notificationNames) - var retry = 0 - var shouldLogAXDisabledEvent: Bool = true - while !pendingRegistrationNames.isEmpty, retry < 100 { - guard let self else { return } - retry += 1 - for name in notificationNames { - await Task.yield() - let e = withUnsafeMutablePointer(to: &self.continuation) { pointer in - AXObserverAddNotification( - observer, - observingElement, - name as CFString, - pointer - ) - } - switch e { - case .success: - shouldLogAXDisabledEvent = true - pendingRegistrationNames.remove(name) - await Status.shared.updateAXStatus(.granted) - case .actionUnsupported: - Logger.service.info("AXObserver: Action unsupported: \(name)") - pendingRegistrationNames.remove(name) - case .apiDisabled: - if shouldLogAXDisabledEvent { // Avoid keeping log AX disabled too many times - shouldLogAXDisabledEvent = false - Logger.service - .error("AXObserver: Accessibility API disabled, will try again later") - } - retry -= 1 - await Status.shared.updateAXStatus(.notGranted) - case .invalidUIElement: - Logger.service - .info("AXObserver: Invalid UI element, notification name \(name)") - pendingRegistrationNames.remove(name) - case .invalidUIElementObserver: - Logger.service.info("AXObserver: Invalid UI element observer") - pendingRegistrationNames.remove(name) - case .cannotComplete: - Logger.service - .info("AXObserver: Failed to observe \(name), will try again later") - case .notificationUnsupported: - Logger.service.info("AXObserver: Notification unsupported: \(name)") - pendingRegistrationNames.remove(name) - case .notificationAlreadyRegistered: - Logger.service.info("AXObserver: Notification already registered: \(name)") - pendingRegistrationNames.remove(name) - default: - Logger.service - .info( - "AXObserver: Unrecognized error \(e) when registering \(name), will try again later" - ) - } - } - try await Task.sleep(nanoseconds: 1_500_000_000) - } - } - } -} - diff --git a/Tool/Sources/ActiveApplicationMonitor/ActiveApplicationMonitor.swift b/Tool/Sources/ActiveApplicationMonitor/ActiveApplicationMonitor.swift deleted file mode 100644 index 12c309ed..00000000 --- a/Tool/Sources/ActiveApplicationMonitor/ActiveApplicationMonitor.swift +++ /dev/null @@ -1,115 +0,0 @@ -import AppKit - -public struct RunningApplicationInfo: Sendable { - public let isXcode: Bool - public let isActive: Bool - public let isHidden: Bool - public let localizedName: String? - public let bundleIdentifier: String? - public let bundleURL: URL? - public let executableURL: URL? - public let processIdentifier: pid_t - public let launchDate: Date? - public let executableArchitecture: Int - - init(_ application: NSRunningApplication) { - isXcode = application.isXcode - isActive = application.isActive - isHidden = application.isHidden - localizedName = application.localizedName - bundleIdentifier = application.bundleIdentifier - bundleURL = application.bundleURL - executableURL = application.executableURL - processIdentifier = application.processIdentifier - launchDate = application.launchDate - executableArchitecture = application.executableArchitecture - } -} - -public extension NSRunningApplication { - var info: RunningApplicationInfo { RunningApplicationInfo(self) } -} - -public final class ActiveApplicationMonitor { - public static let shared = ActiveApplicationMonitor() - public private(set) var latestXcode: NSRunningApplication? = NSWorkspace.shared - .runningApplications - .first(where: \.isXcode) - public private(set) var previousApp: NSRunningApplication? - public private(set) var activeApplication = NSWorkspace.shared.runningApplications - .first(where: \.isActive) - { - didSet { - if activeApplication?.isXcode ?? false { - latestXcode = activeApplication - } - previousApp = oldValue - } - } - - private var infoContinuations: [UUID: AsyncStream.Continuation] = [:] - - private init() { - activeApplication = NSWorkspace.shared.runningApplications.first(where: \.isActive) - - Task { - let sequence = NSWorkspace.shared.notificationCenter - .notifications(named: NSWorkspace.didActivateApplicationNotification) - for await notification in sequence { - guard let app = notification - .userInfo?[NSWorkspace.applicationUserInfoKey] as? NSRunningApplication - else { continue } - activeApplication = app - notifyContinuations() - } - } - } - - deinit { - for continuation in infoContinuations { - continuation.value.finish() - } - } - - public var activeXcode: NSRunningApplication? { - if activeApplication?.isXcode ?? false { - return activeApplication - } - return nil - } - - public func createInfoStream() -> AsyncStream { - .init { continuation in - let id = UUID() - Task { @MainActor in - continuation.onTermination = { _ in - self.removeInfoContinuation(id: id) - } - addInfoContinuation(continuation, id: id) - continuation.yield(activeApplication?.info) - } - } - } - - func addInfoContinuation( - _ continuation: AsyncStream.Continuation, - id: UUID - ) { - infoContinuations[id] = continuation - } - - func removeInfoContinuation(id: UUID) { - infoContinuations[id] = nil - } - - private func notifyContinuations() { - for continuation in infoContinuations { - continuation.value.yield(activeApplication?.info) - } - } -} - -public extension NSRunningApplication { - var isXcode: Bool { bundleIdentifier == "com.apple.dt.Xcode" } -} - diff --git a/Tool/Sources/AsyncPassthroughSubject/AsyncPassthroughSubject.swift b/Tool/Sources/AsyncPassthroughSubject/AsyncPassthroughSubject.swift deleted file mode 100644 index 94d033d7..00000000 --- a/Tool/Sources/AsyncPassthroughSubject/AsyncPassthroughSubject.swift +++ /dev/null @@ -1,54 +0,0 @@ -import AppKit -import Foundation - -public actor AsyncPassthroughSubject { - var tasks: [AsyncStream.Continuation] = [] - - deinit { - tasks.forEach { $0.finish() } - } - - public init() {} - - public func notifications() -> AsyncStream { - AsyncStream { [weak self] continuation in - let task = Task { [weak self] in - await self?.storeContinuation(continuation) - } - - continuation.onTermination = { termination in - task.cancel() - } - } - } - - nonisolated - public func send(_ element: Element) { - Task { await _send(element) } - } - - func _send(_ element: Element) { - let tasks = tasks - for task in tasks { - task.yield(element) - } - } - - func storeContinuation(_ continuation: AsyncStream.Continuation) { - tasks.append(continuation) - } - - nonisolated - public func finish() { - Task { await _finish() } - } - - func _finish() { - let tasks = self.tasks - self.tasks = [] - for task in tasks { - task.finish() - } - } -} - diff --git a/Tool/Sources/BuiltinExtension/BuiltinExtension.swift b/Tool/Sources/BuiltinExtension/BuiltinExtension.swift deleted file mode 100644 index 525b5c54..00000000 --- a/Tool/Sources/BuiltinExtension/BuiltinExtension.swift +++ /dev/null @@ -1,25 +0,0 @@ -import CopilotForXcodeKit -import Foundation -import Preferences -import ConversationServiceProvider -import TelemetryServiceProvider - -public typealias CopilotForXcodeCapability = CopilotForXcodeExtensionCapability & CopilotForXcodeChatCapability & CopilotForXcodeTelemetryCapability - -public protocol CopilotForXcodeChatCapability { - var conversationService: ConversationServiceType? { get } -} - -public protocol CopilotForXcodeTelemetryCapability { - var telemetryService: TelemetryServiceType? { get } -} - -public protocol BuiltinExtension: CopilotForXcodeCapability { - /// An id that let the extension manager determine whether the extension is in use. - var suggestionServiceId: BuiltInSuggestionFeatureProvider { get } - - /// It's usually called when the app is about to quit, - /// you should clean up all the resources here. - func terminate() -} - diff --git a/Tool/Sources/BuiltinExtension/BuiltinExtensionConversationServiceProvider.swift b/Tool/Sources/BuiltinExtension/BuiltinExtensionConversationServiceProvider.swift deleted file mode 100644 index 3d4be7c1..00000000 --- a/Tool/Sources/BuiltinExtension/BuiltinExtensionConversationServiceProvider.swift +++ /dev/null @@ -1,174 +0,0 @@ -import ConversationServiceProvider -import CopilotForXcodeKit -import Foundation -import Logger -import XcodeInspector -import Workspace - -public final class BuiltinExtensionConversationServiceProvider< - T: BuiltinExtension ->: ConversationServiceProvider { - public func notifyChangeTextDocument(fileURL: URL, content: String, version: Int, workspaceURL: URL?) async throws { - guard let conversationService else { - Logger.service.error("Builtin chat service not found.") - return - } - - guard let workspaceInfo = await activeWorkspace(workspaceURL) else { - Logger.service.error("Could not get active workspace info") - return - } - - try? await conversationService.notifyChangeTextDocument(fileURL: fileURL, content: content, version: version, workspace: workspaceInfo) - } - - - private let extensionManager: BuiltinExtensionManager - - public init( - extension: T.Type, - extensionManager: BuiltinExtensionManager = .shared - ) { - self.extensionManager = extensionManager - } - - var conversationService: ConversationServiceType? { - extensionManager.extensions.first { $0 is T }?.conversationService - } - - private func activeWorkspace(_ workspaceURL: URL? = nil) async -> WorkspaceInfo? { - if let workspaceURL = workspaceURL { - if let workspaceBinding = WorkspaceFile.getWorkspaceInfo(workspaceURL: workspaceURL) { - return workspaceBinding - } - } - - guard let workspaceURL = await XcodeInspector.shared.safe.realtimeActiveWorkspaceURL, - let projectURL = await XcodeInspector.shared.safe.realtimeActiveProjectURL - else { return nil } - - return WorkspaceInfo(workspaceURL: workspaceURL, projectURL: projectURL) - } - - struct BuiltinExtensionChatServiceNotFoundError: Error, LocalizedError { - var errorDescription: String? { - "Builtin chat service not found." - } - } - - public func createConversation(_ request: ConversationRequest, workspaceURL: URL?) async throws { - guard let conversationService else { - Logger.service.error("Builtin chat service not found.") - return - } - guard let workspaceInfo = await activeWorkspace(workspaceURL) else { - Logger.service.error("Could not get active workspace info") - return - } - - try await conversationService.createConversation(request, workspace: workspaceInfo) - } - - public func createTurn(with conversationId: String, request: ConversationRequest, workspaceURL: URL?) async throws { - guard let conversationService else { - Logger.service.error("Builtin chat service not found.") - return - } - guard let workspaceInfo = await activeWorkspace(workspaceURL) else { - Logger.service.error("Could not get active workspace info") - return - } - - try await conversationService - .createTurn( - with: conversationId, - request: request, - workspace: workspaceInfo - ) - } - - public func stopReceivingMessage(_ workDoneToken: String, workspaceURL: URL?) async throws { - guard let conversationService else { - Logger.service.error("Builtin chat service not found.") - return - } - guard let workspaceInfo = await activeWorkspace(workspaceURL) else { - Logger.service.error("Could not get active workspace info") - return - } - - try await conversationService.cancelProgress(workDoneToken, workspace: workspaceInfo) - } - - public func rateConversation(turnId: String, rating: ConversationRating, workspaceURL: URL?) async throws { - guard let conversationService else { - Logger.service.error("Builtin chat service not found.") - return - } - guard let workspaceInfo = await activeWorkspace(workspaceURL) else { - Logger.service.error("Could not get active workspace info") - return - } - try? await conversationService.rateConversation(turnId: turnId, rating: rating, workspace: workspaceInfo) - } - - public func copyCode(_ request: CopyCodeRequest, workspaceURL: URL?) async throws { - guard let conversationService else { - Logger.service.error("Builtin chat service not found.") - return - } - guard let workspaceInfo = await activeWorkspace(workspaceURL) else { - Logger.service.error("Could not get active workspace info") - return - } - try? await conversationService.copyCode(request: request, workspace: workspaceInfo) - } - - public func templates() async throws -> [ChatTemplate]? { - guard let conversationService else { - Logger.service.error("Builtin chat service not found.") - return nil - } - guard let workspaceInfo = await activeWorkspace() else { - Logger.service.error("Could not get active workspace info") - return nil - } - - return (try? await conversationService.templates(workspace: workspaceInfo)) - } - - public func models() async throws -> [CopilotModel]? { - guard let conversationService else { - Logger.service.error("Builtin chat service not found.") - return nil - } - guard let workspaceInfo = await activeWorkspace() else { - Logger.service.error("Could not get active workspace info") - return nil - } - - return (try? await conversationService.models(workspace: workspaceInfo)) - } - - public func notifyDidChangeWatchedFiles(_ event: DidChangeWatchedFilesEvent, workspace: WorkspaceInfo) async throws { - guard let conversationService else { - Logger.service.error("Builtin chat service not found.") - return - } - - try? await conversationService.notifyDidChangeWatchedFiles(event, workspace: workspace) - } - - public func agents() async throws -> [ChatAgent]? { - guard let conversationService else { - Logger.service.error("Builtin chat service not found.") - return nil - } - guard let workspaceInfo = await activeWorkspace() else { - Logger.service.error("Could not get active workspace info") - return nil - } - - return (try? await conversationService.agents(workspace: workspaceInfo)) - } -} diff --git a/Tool/Sources/BuiltinExtension/BuiltinExtensionManager.swift b/Tool/Sources/BuiltinExtension/BuiltinExtensionManager.swift deleted file mode 100644 index 0d65011a..00000000 --- a/Tool/Sources/BuiltinExtension/BuiltinExtensionManager.swift +++ /dev/null @@ -1,46 +0,0 @@ -import AppKit -import Combine -import Foundation -import XcodeInspector - -public final class BuiltinExtensionManager { - public static let shared: BuiltinExtensionManager = .init() - private(set) var extensions: [any BuiltinExtension] = [] - - private var cancellable: Set = [] - - init() { - XcodeInspector.shared.$activeApplication.sink { [weak self] app in - if let app, app.isXcode || app.isExtensionService { - self?.checkAppConfiguration() - } - }.store(in: &cancellable) - } - - public func setupExtensions(_ extensions: [any BuiltinExtension]) { - self.extensions = extensions - checkAppConfiguration() - } - - public func terminate() { - for ext in extensions { - ext.terminate() - } - } -} - -extension BuiltinExtensionManager { - func checkAppConfiguration() { - let suggestionFeatureProvider = UserDefaults.shared.value(for: \.suggestionFeatureProvider) - for ext in extensions { - let isSuggestionFeatureInUse = suggestionFeatureProvider == - .builtIn(ext.suggestionServiceId) - let isChatFeatureInUse = true - ext.extensionUsageDidChange(.init( - isSuggestionServiceInUse: isSuggestionFeatureInUse, - isChatServiceInUse: isChatFeatureInUse - )) - } - } -} - diff --git a/Tool/Sources/BuiltinExtension/BuiltinExtensionSuggestionServiceProvider.swift b/Tool/Sources/BuiltinExtension/BuiltinExtensionSuggestionServiceProvider.swift deleted file mode 100644 index f6234ddf..00000000 --- a/Tool/Sources/BuiltinExtension/BuiltinExtensionSuggestionServiceProvider.swift +++ /dev/null @@ -1,177 +0,0 @@ -import CopilotForXcodeKit -import Foundation -import Logger -import Preferences -import SuggestionBasic -import SuggestionProvider - -public final class BuiltinExtensionSuggestionServiceProvider< - T: BuiltinExtension ->: SuggestionServiceProvider { - public var configuration: SuggestionServiceConfiguration { - guard let service else { - return .init( - acceptsRelevantCodeSnippets: true, - mixRelevantCodeSnippetsInSource: true, - acceptsRelevantSnippetsFromOpenedFiles: true - ) - } - - return service.configuration - } - - let extensionManager: BuiltinExtensionManager - - public init( - extension: T.Type, - extensionManager: BuiltinExtensionManager = .shared - ) { - self.extensionManager = extensionManager - } - - var service: CopilotForXcodeKit.SuggestionServiceType? { - extensionManager.extensions.first { $0 is T }?.suggestionService - } - - struct BuiltinExtensionSuggestionServiceNotFoundError: Error, LocalizedError { - var errorDescription: String? { - "Builtin suggestion service not found." - } - } - - public func getSuggestions( - _ request: SuggestionProvider.SuggestionRequest, - workspaceInfo: CopilotForXcodeKit.WorkspaceInfo - ) async throws -> [SuggestionBasic.CodeSuggestion] { - guard let service else { - Logger.service.error("Builtin suggestion service not found.") - throw BuiltinExtensionSuggestionServiceNotFoundError() - } - return try await service.getSuggestions( - .init( - fileURL: request.fileURL, - relativePath: request.relativePath, - language: .init( - rawValue: languageIdentifierFromFileURL(request.fileURL).rawValue - ) ?? .plaintext, - content: request.content, - originalContent: request.originalContent, - cursorPosition: .init( - line: request.cursorPosition.line, - character: request.cursorPosition.character - ), - tabSize: request.tabSize, - indentSize: request.indentSize, - usesTabsForIndentation: request.usesTabsForIndentation, - relevantCodeSnippets: request.relevantCodeSnippets.map { $0.converted } - ), - workspace: workspaceInfo - ).map { $0.converted } - } - - public func cancelRequest( - workspaceInfo: CopilotForXcodeKit.WorkspaceInfo - ) async { - guard let service else { - Logger.service.error("Builtin suggestion service not found.") - return - } - await service.cancelRequest(workspace: workspaceInfo) - } - - public func notifyAccepted( - _ suggestion: SuggestionBasic.CodeSuggestion, - workspaceInfo: CopilotForXcodeKit.WorkspaceInfo - ) async { - guard let service else { - Logger.service.error("Builtin suggestion service not found.") - return - } - await service.notifyAccepted(suggestion.converted, workspace: workspaceInfo) - } - - public func notifyRejected( - _ suggestions: [SuggestionBasic.CodeSuggestion], - workspaceInfo: CopilotForXcodeKit.WorkspaceInfo - ) async { - guard let service else { - Logger.service.error("Builtin suggestion service not found.") - return - } - await service.notifyRejected(suggestions.map(\.converted), workspace: workspaceInfo) - } -} - -extension SuggestionProvider.SuggestionRequest { - var converted: CopilotForXcodeKit.SuggestionRequest { - .init( - fileURL: fileURL, - relativePath: relativePath, - language: .init(rawValue: languageIdentifierFromFileURL(fileURL).rawValue) - ?? .plaintext, - content: content, - originalContent: originalContent, - cursorPosition: .init( - line: cursorPosition.line, - character: cursorPosition.character - ), - tabSize: tabSize, - indentSize: indentSize, - usesTabsForIndentation: usesTabsForIndentation, - relevantCodeSnippets: relevantCodeSnippets.map(\.converted) - ) - } -} - -extension SuggestionBasic.CodeSuggestion { - var converted: CopilotForXcodeKit.CodeSuggestion { - .init( - id: id, - text: text, - position: .init( - line: position.line, - character: position.character - ), - range: .init( - start: .init( - line: range.start.line, - character: range.start.character - ), - end: .init( - line: range.end.line, - character: range.end.character - ) - ) - ) - } -} - -extension CopilotForXcodeKit.CodeSuggestion { - var converted: SuggestionBasic.CodeSuggestion { - .init( - id: id, - text: text, - position: .init( - line: position.line, - character: position.character - ), - range: .init( - start: .init( - line: range.start.line, - character: range.start.character - ), - end: .init( - line: range.end.line, - character: range.end.character - ) - ) - ) - } -} - -extension SuggestionProvider.RelevantCodeSnippet { - var converted: CopilotForXcodeKit.RelevantCodeSnippet { - .init(content: content, priority: priority, filePath: filePath) - } -} - diff --git a/Tool/Sources/BuiltinExtension/BuiltinExtensionTelemetryServiceProvider.swift b/Tool/Sources/BuiltinExtension/BuiltinExtensionTelemetryServiceProvider.swift deleted file mode 100644 index df7a3905..00000000 --- a/Tool/Sources/BuiltinExtension/BuiltinExtensionTelemetryServiceProvider.swift +++ /dev/null @@ -1,59 +0,0 @@ -import TelemetryServiceProvider -import CopilotForXcodeKit -import Foundation -import Logger -import XcodeInspector - -public final class BuiltinExtensionTelemetryServiceProvider< - T: BuiltinExtension ->: TelemetryServiceProvider { - - private let extensionManager: BuiltinExtensionManager - - public init( - extension: T.Type, - extensionManager: BuiltinExtensionManager = .shared - ) { - self.extensionManager = extensionManager - } - - var telemetryService: TelemetryServiceType? { - extensionManager.extensions.first { $0 is T }?.telemetryService - } - - private func activeWorkspace() async -> WorkspaceInfo? { - guard let workspaceURL = await XcodeInspector.shared.safe.realtimeActiveWorkspaceURL, - let projectURL = await XcodeInspector.shared.safe.realtimeActiveProjectURL - else { return nil } - - return WorkspaceInfo(workspaceURL: workspaceURL, projectURL: projectURL) - } - - struct BuiltinExtensionTelemetryServiceNotFoundError: Error, LocalizedError { - var errorDescription: String? { - "Builtin telemetry service not found." - } - } - - struct BuiltinExtensionActiveWorkspaceInfoNotFoundError: Error, LocalizedError { - var errorDescription: String? { - "Builtin active workspace info not found." - } - } - - public func sendError(_ request: TelemetryExceptionRequest) async throws { - guard let telemetryService else { - print("Builtin telemetry service not found.") - throw BuiltinExtensionTelemetryServiceNotFoundError() - } - guard let workspaceInfo = await activeWorkspace() else { - print("Builtin active workspace info not found.") - throw BuiltinExtensionActiveWorkspaceInfoNotFoundError() - } - - try await telemetryService.sendError( - request, - workspace: workspaceInfo - ) - } -} diff --git a/Tool/Sources/BuiltinExtension/BuiltinExtensionWorkspacePlugin.swift b/Tool/Sources/BuiltinExtension/BuiltinExtensionWorkspacePlugin.swift deleted file mode 100644 index a03c34d1..00000000 --- a/Tool/Sources/BuiltinExtension/BuiltinExtensionWorkspacePlugin.swift +++ /dev/null @@ -1,72 +0,0 @@ -import Foundation -import Workspace - -public final class BuiltinExtensionWorkspacePlugin: WorkspacePlugin { - let extensionManager: BuiltinExtensionManager - - public init(workspace: Workspace, extensionManager: BuiltinExtensionManager = .shared) { - self.extensionManager = extensionManager - super.init(workspace: workspace) - } - - override public func didOpenFilespace(_ filespace: Filespace) { - notifyOpenFile(filespace: filespace) - } - - override public func didSaveFilespace(_ filespace: Filespace) { - notifySaveFile(filespace: filespace) - } - - override public func didUpdateFilespace(_ filespace: Filespace, content: String) { - notifyUpdateFile(filespace: filespace, content: content) - } - - override public func didCloseFilespace(_ fileURL: URL) { - Task { - for ext in extensionManager.extensions { - ext.workspace( - .init(workspaceURL: workspaceURL, projectURL: projectRootURL), - didCloseDocumentAt: fileURL - ) - } - } - } - - public func notifyOpenFile(filespace: Filespace) { - Task { - guard filespace.isTextReadable else { return } - for ext in extensionManager.extensions { - ext.workspace( - .init(workspaceURL: workspaceURL, projectURL: projectRootURL), - didOpenDocumentAt: filespace.fileURL - ) - } - } - } - - public func notifyUpdateFile(filespace: Filespace, content: String) { - Task { - guard filespace.isTextReadable else { return } - for ext in extensionManager.extensions { - ext.workspace( - .init(workspaceURL: workspaceURL, projectURL: projectRootURL), - didUpdateDocumentAt: filespace.fileURL, - content: content - ) - } - } - } - - public func notifySaveFile(filespace: Filespace) { - Task { - guard filespace.isTextReadable else { return } - for ext in extensionManager.extensions { - ext.workspace( - .init(workspaceURL: workspaceURL, projectURL: projectRootURL), - didSaveDocumentAt: filespace.fileURL - ) - } - } - } -} - diff --git a/Tool/Sources/Cache/AvatarCache.swift b/Tool/Sources/Cache/AvatarCache.swift deleted file mode 100644 index a0a91c47..00000000 --- a/Tool/Sources/Cache/AvatarCache.swift +++ /dev/null @@ -1,49 +0,0 @@ -import Foundation -import SwiftUI -import AppKit - -public final class AvatarCache { - public static let shared = AvatarCache() - private let cache = NSCache() - - private init () {} - - public func set(forUser username: String) async -> Void { - guard let data = await fetchAvatarData(forUser: username) else { return } - cache.setObject(data as NSData, forKey: username as NSString) - } - - public func get(forUser username: String) -> Data? { - return cache.object(forKey: username as NSString) as Data? - } - - public func remove(forUser username: String) { - cache.removeObject(forKey: username as NSString) - } -} - -extension AvatarCache { - // Directly get the avatar from URL like https://avatars.githubusercontent.com/ - // TODO: when the `agent` feature added, the avatarUrl could be obtained from the response of GitHub LSP - func fetchAvatarData(forUser username: String) async -> Data? { - let avatarUrl = "https://avatars.githubusercontent.com/\(username)" - guard let avatarUrl = URL(string: avatarUrl) else { return nil } - - do { - let (data, _) = try await URLSession.shared.data(from: avatarUrl) - return data - } catch { - return nil - } - } - - public func getAvatarImage(forUser username: String) -> Image? { - guard let data = get(forUser: username), - let nsImage = NSImage(data: data) - else { - return nil - } - - return Image(nsImage: nsImage) - } -} diff --git a/Tool/Sources/Cache/AvatarViewModel.swift b/Tool/Sources/Cache/AvatarViewModel.swift deleted file mode 100644 index 53dcafc8..00000000 --- a/Tool/Sources/Cache/AvatarViewModel.swift +++ /dev/null @@ -1,23 +0,0 @@ -import SwiftUI - -@MainActor -public class AvatarViewModel: ObservableObject { - @Published private(set) public var avatarImage: Image? - public static let shared = AvatarViewModel() - - public init() { } - - public func loadAvatar(forUser userName: String?) { - guard let userName = userName, !userName.isEmpty - else { - avatarImage = nil - return - } - - // Fetch if not in cache - Task { - await AvatarCache.shared.set(forUser: userName) - self.avatarImage = AvatarCache.shared.getAvatarImage(forUser: userName) - } - } -} diff --git a/Tool/Sources/ChatAPIService/Models.swift b/Tool/Sources/ChatAPIService/Models.swift deleted file mode 100644 index 9706a4bd..00000000 --- a/Tool/Sources/ChatAPIService/Models.swift +++ /dev/null @@ -1,168 +0,0 @@ -import CodableWrappers -import Foundation -import ConversationServiceProvider -import GitHubCopilotService - -// move here avoid circular reference -public struct ConversationReference: Codable, Equatable, Hashable { - public enum Kind: Codable, Equatable, Hashable { - case `class` - case `struct` - case `enum` - case `actor` - case `protocol` - case `extension` - case `case` - case property - case `typealias` - case function - case method - case text - case webpage - case other - // reference for turn - request - case fileReference(FileReference) - // reference from turn - response - case reference(Reference) - } - - public enum Status: String, Codable { - case included, blocked, notfound, empty - } - - public var uri: String - public var status: Status? - public var kind: Kind - - public var ext: String { - return url?.pathExtension ?? "" - } - - public var fileName: String { - return url?.lastPathComponent ?? "" - } - - public var filePath: String { - return url?.path ?? "" - } - - public var url: URL? { - return URL(string: uri) - } - - public init( - uri: String, - status: Status?, - kind: Kind - ) { - self.uri = uri - self.status = status - self.kind = kind - - } -} - - -public struct ChatMessage: Equatable, Codable { - public typealias ID = String - - public enum Role: String, Codable, Equatable { - case user - case assistant - case system - } - - /// The role of a message. - public var role: Role - - /// The content of the message, either the chat message, or a result of a function call. - public var content: String - - /// The attached image content of the message - public var contentImageReferences: [ImageReference] - - /// The id of the message. - public var id: ID - - /// The conversation id (not the CLS conversation id) - public var chatTabID: String - - /// The CLS turn id of the message which is from CLS. - public var clsTurnID: ID? - - /// Rate assistant message - public var rating: ConversationRating - - /// The references of this message. - public var references: [ConversationReference] - - /// The followUp question of this message - public var followUp: ConversationFollowUp? - - public var suggestedTitle: String? - - /// The error occurred during responding chat in server - public var errorMessages: [String] - - /// The steps of conversation progress - public var steps: [ConversationProgressStep] - - public var editAgentRounds: [AgentRound] - - public var panelMessages: [CopilotShowMessageParams] - - /// The timestamp of the message. - public var createdAt: Date - public var updatedAt: Date - - public init( - id: String = UUID().uuidString, - chatTabID: String, - clsTurnID: String? = nil, - role: Role, - content: String, - contentImageReferences: [ImageReference] = [], - references: [ConversationReference] = [], - followUp: ConversationFollowUp? = nil, - suggestedTitle: String? = nil, - errorMessages: [String] = [], - rating: ConversationRating = .unrated, - steps: [ConversationProgressStep] = [], - editAgentRounds: [AgentRound] = [], - panelMessages: [CopilotShowMessageParams] = [], - createdAt: Date? = nil, - updatedAt: Date? = nil - ) { - self.role = role - self.content = content - self.contentImageReferences = contentImageReferences - self.id = id - self.chatTabID = chatTabID - self.clsTurnID = clsTurnID - self.references = references - self.followUp = followUp - self.suggestedTitle = suggestedTitle - self.errorMessages = errorMessages - self.rating = rating - self.steps = steps - self.editAgentRounds = editAgentRounds - self.panelMessages = panelMessages - - let now = Date.now - self.createdAt = createdAt ?? now - self.updatedAt = updatedAt ?? now - } -} - -extension ConversationReference { - public func getPathRelativeToHome() -> String { - guard !filePath.isEmpty else { return filePath} - - let homeDirectory = FileManager.default.homeDirectoryForCurrentUser.path - if !homeDirectory.isEmpty{ - return filePath.replacingOccurrences(of: homeDirectory, with: "~") - } - - return filePath - } -} diff --git a/Tool/Sources/GitHubCopilotService/GitHubCopilotExtension.swift b/Tool/Sources/GitHubCopilotService/GitHubCopilotExtension.swift deleted file mode 100644 index 119278ee..00000000 --- a/Tool/Sources/GitHubCopilotService/GitHubCopilotExtension.swift +++ /dev/null @@ -1,168 +0,0 @@ -import BuiltinExtension -import CopilotForXcodeKit -import ConversationServiceProvider -import TelemetryServiceProvider -import Foundation -import LanguageServerProtocol -import Logger -import Preferences -import Workspace - -public final class GitHubCopilotExtension: BuiltinExtension { - public var suggestionServiceId: Preferences.BuiltInSuggestionFeatureProvider { .gitHubCopilot } - - public let suggestionService: GitHubCopilotSuggestionService? - public let conversationService: ConversationServiceType? - public let telemetryService: TelemetryServiceType? - - private var extensionUsage = ExtensionUsage( - isSuggestionServiceInUse: false, - isChatServiceInUse: false - ) - private var isLanguageServerInUse: Bool { - extensionUsage.isSuggestionServiceInUse || extensionUsage.isChatServiceInUse - } - - let workspacePool: WorkspacePool - - let serviceLocator: ServiceLocator - - public init(workspacePool: WorkspacePool) { - self.workspacePool = workspacePool - serviceLocator = .init(workspacePool: workspacePool) - let suggestionService = GitHubCopilotSuggestionService.init(serviceLocator: serviceLocator) - self.suggestionService = suggestionService - let conversationService = GitHubCopilotConversationService.init(serviceLocator: serviceLocator) - self.conversationService = conversationService - let telemetryService = GitHubCopilotTelemetryService.init(serviceLocator: serviceLocator) - self.telemetryService = telemetryService - } - - public func workspaceDidOpen(_: WorkspaceInfo) {} - - public func workspaceDidClose(_: WorkspaceInfo) {} - - public func workspace(_ workspace: WorkspaceInfo, didOpenDocumentAt documentURL: URL) { - guard isLanguageServerInUse else { return } - // check if file size is larger than 15MB, if so, return immediately - if let attrs = try? FileManager.default - .attributesOfItem(atPath: documentURL.path), - let fileSize = attrs[FileAttributeKey.size] as? UInt64, - fileSize > 15 * 1024 * 1024 - { return } - - Task { - let content: String - do { - content = try String(contentsOf: documentURL, encoding: .utf8) - } catch { - Logger.extension.info("Failed to read \(documentURL.lastPathComponent): \(error)") - return - } - - do { - guard let service = await serviceLocator.getService(from: workspace) else { return } - try await service.notifyOpenTextDocument(fileURL: documentURL, content: content) - } catch { - Logger.gitHubCopilot.info(error.localizedDescription) - } - } - } - - public func workspace(_ workspace: WorkspaceInfo, didSaveDocumentAt documentURL: URL) { - guard isLanguageServerInUse else { return } - Task { - do { - guard let service = await serviceLocator.getService(from: workspace) else { return } - try await service.notifySaveTextDocument(fileURL: documentURL) - } catch { - Logger.gitHubCopilot.info(error.localizedDescription) - } - } - } - - public func workspace(_ workspace: WorkspaceInfo, didCloseDocumentAt documentURL: URL) { - guard isLanguageServerInUse else { return } - Task { - do { - guard let service = await serviceLocator.getService(from: workspace) else { return } - try await service.notifyCloseTextDocument(fileURL: documentURL) - } catch { - Logger.gitHubCopilot.info(error.localizedDescription) - } - } - } - - public func workspace( - _ workspace: WorkspaceInfo, - didUpdateDocumentAt documentURL: URL, - content: String? - ) { - guard isLanguageServerInUse else { return } - // check if file size is larger than 15MB, if so, return immediately - if let attrs = try? FileManager.default - .attributesOfItem(atPath: documentURL.path), - let fileSize = attrs[FileAttributeKey.size] as? UInt64, - fileSize > 15 * 1024 * 1024 - { return } - - Task { - guard let content else { return } - guard let service = await serviceLocator.getService(from: workspace) else { return } - do { - try await service.notifyChangeTextDocument( - fileURL: documentURL, - content: content, - version: 0 - ) - } catch let error as ServerError { - switch error { - case .serverError(-32602, _, _): // parameter incorrect - Logger.gitHubCopilot.error(error.localizedDescription) - // Reopen document if it's not found in the language server - self.workspace(workspace, didOpenDocumentAt: documentURL) - default: - Logger.gitHubCopilot.info(error.localizedDescription) - } - } catch { - Logger.gitHubCopilot.info(error.localizedDescription) - } - } - } - - public func extensionUsageDidChange(_ usage: ExtensionUsage) { - extensionUsage = usage - if !usage.isChatServiceInUse && !usage.isSuggestionServiceInUse { - terminate() - } - } - - public func terminate() { - for workspace in workspacePool.workspaces.values { - guard let plugin = workspace.plugin(for: GitHubCopilotWorkspacePlugin.self) - else { continue } - plugin.terminate() - } - } -} - -protocol ServiceLocatorType { - func getService(from workspace: WorkspaceInfo) async -> GitHubCopilotService? -} - -final class ServiceLocator: ServiceLocatorType { - let workspacePool: WorkspacePool - - init(workspacePool: WorkspacePool) { - self.workspacePool = workspacePool - } - - func getService(from workspace: WorkspaceInfo) async -> GitHubCopilotService? { - guard let workspace = workspacePool.workspaces[workspace.workspaceURL], - let plugin = workspace.plugin(for: GitHubCopilotWorkspacePlugin.self) - else { - return nil - } - return plugin.gitHubCopilotService - } -} diff --git a/Tool/Sources/GitHubCopilotService/GitHubCopilotWorkspacePlugin.swift b/Tool/Sources/GitHubCopilotService/GitHubCopilotWorkspacePlugin.swift deleted file mode 100644 index cf73d46d..00000000 --- a/Tool/Sources/GitHubCopilotService/GitHubCopilotWorkspacePlugin.swift +++ /dev/null @@ -1,50 +0,0 @@ -import Foundation -import Logger -import Workspace - -public final class GitHubCopilotWorkspacePlugin: WorkspacePlugin { - public var gitHubCopilotService: GitHubCopilotService? - - public override init(workspace: Workspace) { - super.init(workspace: workspace) - do { - gitHubCopilotService = try createGitHubCopilotService() - } catch { - Logger.gitHubCopilot.error("Failed to create GitHub Copilot service: \(error)") - } - } - - deinit { - if let gitHubCopilotService { - Task { await gitHubCopilotService.terminate() } - } - } - - func createGitHubCopilotService() throws -> GitHubCopilotService { - let newService = try GitHubCopilotService(projectRootURL: projectRootURL, workspaceURL: workspaceURL) - Task { - try await Task.sleep(nanoseconds: 1_000_000_000) - finishLaunchingService() - } - return newService - } - - func finishLaunchingService() { - guard let workspace, let gitHubCopilotService else { return } - Task { - for (_, filespace) in workspace.filespaces { - let documentURL = filespace.fileURL - guard let content = try? String(contentsOf: documentURL) else { continue } - try? await gitHubCopilotService.notifyOpenTextDocument( - fileURL: documentURL, - content: content - ) - } - } - } - - func terminate() { - gitHubCopilotService = nil - } -} - diff --git a/Tool/Sources/HostAppActivator/HostAppActivator.swift b/Tool/Sources/HostAppActivator/HostAppActivator.swift deleted file mode 100644 index 81658337..00000000 --- a/Tool/Sources/HostAppActivator/HostAppActivator.swift +++ /dev/null @@ -1,143 +0,0 @@ -import Foundation -import AppKit -import Logger - -public let HostAppURL = locateHostBundleURL(url: Bundle.main.bundleURL) - -public extension Notification.Name { - static let openSettingsWindowRequest = Notification - .Name("com.github.CopilotForXcode.OpenSettingsWindowRequest") - static let openMCPSettingsWindowRequest = Notification - .Name("com.github.CopilotForXcode.OpenMCPSettingsWindowRequest") -} - -public enum GitHubCopilotForXcodeSettingsLaunchError: Error, LocalizedError { - case appNotFound - case openFailed(errorDescription: String) - - public var errorDescription: String? { - switch self { - case .appNotFound: - return "\(hostAppName()) settings application not found" - case let .openFailed(errorDescription): - return "Failed to launch \(hostAppName()) settings (\(errorDescription))" - } - } -} - -public func getRunningHostApp() -> NSRunningApplication? { - return NSWorkspace.shared.runningApplications.first(where: { - $0.bundleIdentifier == (Bundle.main.object(forInfoDictionaryKey: "BUNDLE_IDENTIFIER_BASE") as! String) - }) -} - -public func launchHostAppSettings() throws { - // Try the AppleScript approach first, but only if app is already running - if let hostApp = getRunningHostApp() { - let activated = hostApp.activate(options: [.activateIgnoringOtherApps]) - Logger.ui.info("\(hostAppName()) activated: \(activated)") - - let scriptSuccess = tryLaunchWithAppleScript() - - // If AppleScript fails, fall back to notification center - if !scriptSuccess { - DistributedNotificationCenter.default().postNotificationName( - .openSettingsWindowRequest, - object: nil - ) - Logger.ui.info("\(hostAppName()) settings notification sent after activation") - return - } - } else { - // If app is not running, launch it with the settings flag - try launchHostAppWithArgs(args: ["--settings"]) - } -} - -public func launchHostAppMCPSettings() throws { - // Try the AppleScript approach first, but only if app is already running - if let hostApp = getRunningHostApp() { - let activated = hostApp.activate(options: [.activateIgnoringOtherApps]) - Logger.ui.info("\(hostAppName()) activated: \(activated)") - - _ = tryLaunchWithAppleScript() - - DistributedNotificationCenter.default().postNotificationName( - .openMCPSettingsWindowRequest, - object: nil - ) - Logger.ui.info("\(hostAppName()) MCP settings notification sent after activation") - return - } else { - // If app is not running, launch it with the settings flag - try launchHostAppWithArgs(args: ["--mcp"]) - } -} - -private func tryLaunchWithAppleScript() -> Bool { - // Try to launch settings using AppleScript - let script = """ - tell application "\(hostAppName())" - activate - tell application "System Events" - keystroke "," using command down - end tell - end tell - """ - - var error: NSDictionary? - if let scriptObject = NSAppleScript(source: script) { - scriptObject.executeAndReturnError(&error) - - // Log the result - if let error = error { - Logger.ui.info("\(hostAppName()) settings script error: \(error)") - return false - } - - Logger.ui.info("\(hostAppName()) settings opened successfully via AppleScript") - return true - } - - return false -} - -public func launchHostAppDefault() throws { - try launchHostAppWithArgs(args: nil) -} - -func launchHostAppWithArgs(args: [String]?) throws { - guard let appURL = HostAppURL else { - throw GitHubCopilotForXcodeSettingsLaunchError.appNotFound - } - - Task { - let configuration = NSWorkspace.OpenConfiguration() - if let args { - configuration.arguments = args - } - configuration.activates = true - - try await NSWorkspace.shared - .openApplication(at: appURL, configuration: configuration) - } -} - -func locateHostBundleURL(url: URL) -> URL? { - var nextURL = url - while nextURL.path != "/" { - nextURL = nextURL.deletingLastPathComponent() - if nextURL.lastPathComponent.hasSuffix(".app") { - return nextURL - } - } - let devAppURL = url - .deletingLastPathComponent() - .appendingPathComponent("GitHub Copilot for Xcode Dev.app") - return devAppURL -} - -func hostAppName() -> String { - return Bundle.main.object(forInfoDictionaryKey: "HOST_APP_NAME") as? String - ?? "GitHub Copilot for Xcode" -} diff --git a/Tool/Sources/Persist/AppState.swift b/Tool/Sources/Persist/AppState.swift deleted file mode 100644 index 3b7f8cc2..00000000 --- a/Tool/Sources/Persist/AppState.swift +++ /dev/null @@ -1,113 +0,0 @@ -import CryptoKit -import Foundation -import JSONRPC -import Logger -import Status - -public extension JSONValue { - subscript(key: String) -> JSONValue? { - if case .hash(let dict) = self { - return dict[key] - } - return nil - } - - var stringValue: String? { - if case .string(let value) = self { - return value - } - return nil - } - - var boolValue: Bool? { - if case .bool(let value) = self { - return value - } - return nil - } - - static func convertToJSONValue(_ object: T) -> JSONValue? { - do { - let data = try JSONEncoder().encode(object) - let jsonValue = try JSONDecoder().decode(JSONValue.self, from: data) - return jsonValue - } catch { - Logger.client.info("Error converting to JSONValue: \(error)") - return nil - } - } -} - -public class AppState { - public static let shared = AppState() - - private var cache: [String: [String: JSONValue]] = [:] - private let cacheFileName = "appstate.json" - private let queue = DispatchQueue(label: "com.github.AppStateCacheQueue") - private var loadStatus: [String: Bool] = [:] - - private init() { - cache[""] = [:] // initialize a default cache if no user exists - initCacheForUserIfNeeded() - } - - func toHash(contents: String, _ length: Int = 16) -> String { - let data = Data(contents.utf8) - let hashData = SHA256.hash(data: data) - let hashValue = hashData.compactMap { String(format: "%02x", $0 ) }.joined() - let index = hashValue.index(hashValue.startIndex, offsetBy: length) - return String(hashValue[..(key: String, value: T) { - queue.async { - let userName = Status.currentUser() ?? "" - self.initCacheForUserIfNeeded(userName) - self.cache[userName]![key] = JSONValue.convertToJSONValue(value) - self.saveCacheForUser(userName) - } - } - - public func get(key: String) -> JSONValue? { - return queue.sync { - let userName = Status.currentUser() ?? "" - initCacheForUserIfNeeded(userName) - return (self.cache[userName] ?? [:])[key] - } - } - - private func configFilePath(userName: String) -> URL { - return ConfigPathUtils.configFilePath(userName: userName, fileName: cacheFileName) - } - - private func saveCacheForUser(_ userName: String? = nil) { - if let user = userName ?? Status.currentUser(), !user.isEmpty { // save cache for non-empty user - let cacheFilePath = configFilePath(userName: user) - do { - let data = try JSONEncoder().encode(self.cache[user] ?? [:]) - try data.write(to: cacheFilePath) - } catch { - Logger.client.info("Failed to save AppState cache: \(error)") - } - } - } - - private func initCacheForUserIfNeeded(_ userName: String? = nil) { - if let user = userName ?? Status.currentUser(), !user.isEmpty, - loadStatus[user] != true { // load cache for non-empty user - self.loadStatus[user] = true - self.cache[user] = [:] - let cacheFilePath = configFilePath(userName: user) - guard FileManager.default.fileExists(atPath: cacheFilePath.path) else { - return - } - - do { - let data = try Data(contentsOf: cacheFilePath) - self.cache[user] = try JSONDecoder().decode([String: JSONValue].self, from: data) - } catch { - Logger.client.info("Failed to load AppState cache: \(error)") - } - } - } -} diff --git a/Tool/Sources/Persist/ConfigPathUtils.swift b/Tool/Sources/Persist/ConfigPathUtils.swift deleted file mode 100644 index 603581ba..00000000 --- a/Tool/Sources/Persist/ConfigPathUtils.swift +++ /dev/null @@ -1,87 +0,0 @@ -import Foundation -import CryptoKit -import Logger - -let BaseAppDirectory = "github-copilot/xcode" - -/// String extension for hashing functionality -extension String { - /// Generates a SHA256 hash of the string - /// - Parameter length: The length of the hash to return, defaults to 16 characters - /// - Returns: The hashed string - func hashed(_ length: Int = 16) -> String { - let data = Data(self.utf8) - let hashData = SHA256.hash(data: data) - let hashValue = hashData.compactMap { String(format: "%02x", $0 ) }.joined() - let index = hashValue.index(hashValue.startIndex, offsetBy: length) - return String(hashValue[.. URL { - if let xdgConfigHome = ProcessInfo.processInfo.environment["XDG_CONFIG_HOME"], - xdgConfigHome.hasPrefix("/") { - return URL(fileURLWithPath: xdgConfigHome) - } - return FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent(".config") - } - - /// Generates a config file path for a specific user. - /// - Parameters: - /// - userName: The user name to generate a path for - /// - appDirectory: The application directory name, defaults to "github-copilot/xcode" - /// - fileName: The file name to append to the path - /// - Returns: The complete URL for the config file - static func configFilePath( - userName: String, - baseDirectory: String = BaseAppDirectory, - subDirectory: String? = nil, - fileName: String - ) -> URL { - var baseURL: URL = getXdgConfigHome() - .appendingPathComponent(baseDirectory) - .appendingPathComponent(toHash(contents: userName)) - - if let subDirectory = subDirectory { - baseURL = baseURL.appendingPathComponent(subDirectory) - } - - ensureDirectoryExists(at: baseURL) - return baseURL.appendingPathComponent(fileName) - } - - /// Ensures a directory exists at the specified URL, creating it if necessary. - /// - Parameter url: The directory URL - private static func ensureDirectoryExists(at url: URL) { - let fileManager = FileManager.default - if !fileManager.fileExists(atPath: url.path) { - do { - try fileManager.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil) - } catch let error as NSError { - if error.domain == NSPOSIXErrorDomain && error.code == EACCES { - Logger.client.error("Permission denied when trying to create directory: \(url.path)") - } else { - Logger.client.info("Failed to create directory: \(error)") - } - } - } - } - - /// Generates a hash from a string using SHA256. - /// - Parameters: - /// - contents: The string to hash - /// - length: The length of the hash to return, defaults to 16 characters - /// - Returns: The hashed string - static func toHash(contents: String, _ length: Int = 16) -> String { - let data = Data(contents.utf8) - let hashData = SHA256.hash(data: data) - let hashValue = hashData.compactMap { String(format: "%02x", $0 ) }.joined() - let index = hashValue.index(hashValue.startIndex, offsetBy: length) - return String(hashValue[..( - _ keyPath: KeyPath - ) where K.Value == Value, Value == Bool { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(wrappedValue: key.defaultValue, key.key, store: .shared) - } - - init( - _ keyPath: KeyPath - ) where K.Value == Value, Value == String { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(wrappedValue: key.defaultValue, key.key, store: .shared) - } - - init( - _ keyPath: KeyPath - ) where K.Value == Value, Value == Double { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(wrappedValue: key.defaultValue, key.key, store: .shared) - } - - init( - _ keyPath: KeyPath - ) where K.Value == Value, Value == Int { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(wrappedValue: key.defaultValue, key.key, store: .shared) - } - - init( - _ keyPath: KeyPath - ) where K.Value == Value, Value == URL { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(wrappedValue: key.defaultValue, key.key, store: .shared) - } - - init( - _ keyPath: KeyPath - ) where K.Value == Value, Value == Data { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(wrappedValue: key.defaultValue, key.key, store: .shared) - } - - init( - _ keyPath: KeyPath - ) where K.Value == Value, Value: RawRepresentable, Value.RawValue == Int { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(wrappedValue: key.defaultValue, key.key, store: .shared) - } - - init( - _ keyPath: KeyPath - ) where K.Value == Value, Value: RawRepresentable, Value.RawValue == String { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(wrappedValue: key.defaultValue, key.key, store: .shared) - } -} - -public extension AppStorage where Value: ExpressibleByNilLiteral { - init( - _ keyPath: KeyPath - ) where K.Value == Value, Value == Bool? { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(key.key, store: .shared) - } - - init( - _ keyPath: KeyPath - ) where K.Value == Value, Value == String? { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(key.key, store: .shared) - } - - init( - _ keyPath: KeyPath - ) where K.Value == Value, Value == Double? { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(key.key, store: .shared) - } - - init( - _ keyPath: KeyPath - ) where K.Value == Value, Value == Int? { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(key.key, store: .shared) - } - - init( - _ keyPath: KeyPath - ) where K.Value == Value, Value == URL? { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(key.key, store: .shared) - } - - init( - _ keyPath: KeyPath - ) where K.Value == Value, Value == Data? { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(key.key, store: .shared) - } -} - -public extension AppStorage { - init( - _ keyPath: KeyPath - ) where K.Value == Value, Value == R?, R: RawRepresentable, R.RawValue == String { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(key.key, store: .shared) - } - - init( - _ keyPath: KeyPath - ) where K.Value == Value, Value == R?, R: RawRepresentable, R.RawValue == Int { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(key.key, store: .shared) - } -} - -// MARK: - Deprecated Key Accessor - -public extension AppStorage { - @available(*, deprecated, message: "This preference key is deprecated.") - init( - _ keyPath: KeyPath> - ) where Value == Bool { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(wrappedValue: key.defaultValue, key.key, store: .shared) - } - - @available(*, deprecated, message: "This preference key is deprecated.") - init( - _ keyPath: KeyPath> - ) where Value == String { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(wrappedValue: key.defaultValue, key.key, store: .shared) - } - - @available(*, deprecated, message: "This preference key is deprecated.") - init( - _ keyPath: KeyPath> - ) where Value == Double { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(wrappedValue: key.defaultValue, key.key, store: .shared) - } - - @available(*, deprecated, message: "This preference key is deprecated.") - init( - _ keyPath: KeyPath> - ) where Value == Int { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(wrappedValue: key.defaultValue, key.key, store: .shared) - } - - @available(*, deprecated, message: "This preference key is deprecated.") - init( - _ keyPath: KeyPath> - ) where Value == URL { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(wrappedValue: key.defaultValue, key.key, store: .shared) - } - - @available(*, deprecated, message: "This preference key is deprecated.") - init( - _ keyPath: KeyPath> - ) where Value == Data { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(wrappedValue: key.defaultValue, key.key, store: .shared) - } - - @available(*, deprecated, message: "This preference key is deprecated.") - init( - _ keyPath: KeyPath> - ) where Value: RawRepresentable, Value.RawValue == Int { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(wrappedValue: key.defaultValue, key.key, store: .shared) - } - - @available(*, deprecated, message: "This preference key is deprecated.") - init( - _ keyPath: KeyPath> - ) where Value: RawRepresentable, Value.RawValue == String { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(wrappedValue: key.defaultValue, key.key, store: .shared) - } -} - -public extension AppStorage where Value: ExpressibleByNilLiteral { - @available(*, deprecated, message: "This preference key is deprecated.") - init( - _ keyPath: KeyPath> - ) where Value == Bool? { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(key.key, store: .shared) - } - - @available(*, deprecated, message: "This preference key is deprecated.") - init( - _ keyPath: KeyPath> - ) where Value == String? { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(key.key, store: .shared) - } - - @available(*, deprecated, message: "This preference key is deprecated.") - init( - _ keyPath: KeyPath> - ) where Value == Double? { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(key.key, store: .shared) - } - - @available(*, deprecated, message: "This preference key is deprecated.") - init( - _ keyPath: KeyPath> - ) where Value == Int? { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(key.key, store: .shared) - } - - @available(*, deprecated, message: "This preference key is deprecated.") - init( - _ keyPath: KeyPath> - ) where Value == URL? { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(key.key, store: .shared) - } - - @available(*, deprecated, message: "This preference key is deprecated.") - init( - _ keyPath: KeyPath> - ) where Value == Data? { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(key.key, store: .shared) - } -} - -public extension AppStorage { - @available(*, deprecated, message: "This preference key is deprecated.") - init( - _ keyPath: KeyPath> - ) where Value == R?, R: RawRepresentable, R.RawValue == String { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(key.key, store: .shared) - } - - @available(*, deprecated, message: "This preference key is deprecated.") - init( - _ keyPath: KeyPath> - ) where Value == R?, R: RawRepresentable, R.RawValue == Int { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - self.init(key.key, store: .shared) - } -} - -#endif - diff --git a/Tool/Sources/Preferences/Keys.swift b/Tool/Sources/Preferences/Keys.swift deleted file mode 100644 index c4296fc7..00000000 --- a/Tool/Sources/Preferences/Keys.swift +++ /dev/null @@ -1,590 +0,0 @@ -import Foundation - -public protocol UserDefaultPreferenceKey { - associatedtype Value - var defaultValue: Value { get } - var key: String { get } -} - -public struct PreferenceKey: UserDefaultPreferenceKey { - public let defaultValue: T - public let key: String - - public init(defaultValue: T, key: String) { - self.defaultValue = defaultValue - self.key = key - } -} - -public struct DeprecatedPreferenceKey { - public let defaultValue: T - public let key: String - - public init(defaultValue: T, key: String) { - self.defaultValue = defaultValue - self.key = key - } -} - -public struct FeatureFlag: UserDefaultPreferenceKey { - public let defaultValue: Bool - public let key: String - - public init(defaultValue: Bool, key: String) { - self.defaultValue = defaultValue - self.key = key - } -} - -public struct UserDefaultPreferenceKeys { - public init() {} - - // MARK: Quit XPC Service On Xcode And App Quit - - public let quitXPCServiceOnXcodeAndAppQuit = PreferenceKey( - defaultValue: true, - key: "QuitXPCServiceOnXcodeAndAppQuit" - ) - - // MARK: Suggestion Widget Position Mode - - public let suggestionWidgetPositionMode = PreferenceKey( - defaultValue: SuggestionWidgetPositionMode.fixedToBottom, - key: "SuggestionWidgetPositionMode" - ) - - // MARK: Widget Color Scheme - - public let widgetColorScheme = PreferenceKey( - defaultValue: WidgetColorScheme.system, - key: "WidgetColorScheme" - ) - - // MARK: Force Order Widget to Front - - public let forceOrderWidgetToFront = PreferenceKey( - defaultValue: true, - key: "ForceOrderWidgetToFront" - ) - - // MARK: Prefer Widget to Stay Inside Editor When Width Greater Than - - public let preferWidgetToStayInsideEditorWhenWidthGreaterThan = PreferenceKey( - defaultValue: 1400 as Double, - key: "PreferWidgetToStayInsideEditorWhenWidthGreaterThan" - ) - - // MARK: Hide Circular Widget - - public let hideCircularWidget = PreferenceKey( - defaultValue: true, - key: "HideCircularWidget" - ) - - public let showHideWidgetShortcutGlobally = PreferenceKey( - defaultValue: false, - key: "ShowHideWidgetShortcutGlobally" - ) - - // MARK: Update Channel - - public let installPrereleases = PreferenceKey( - defaultValue: false, - key: "InstallPrereleases" - ) - - // MARK: Completion Hint Shown - - public let completionHintShown = PreferenceKey( - defaultValue: false, - key: "CompletionHintShown" - ) - - // MARK: First Time Intro Interface - - public let introLastShownVersion = PreferenceKey( - defaultValue: "", - key: "IntroLastShownVersion" - ) - - public let hideIntro = PreferenceKey( - defaultValue: false, - key: "HideIntro" - ) - - public let extensionPermissionShown = PreferenceKey( - defaultValue: false, - key: "ExtensionPermissionShown" - ) - - public let capturePermissionShown = PreferenceKey( - defaultValue: false, - key: "CapturePermissionShown" - ) -} - -// MARK: - Prompt to Code - -public extension UserDefaultPreferenceKeys { - - var promptToCodeGenerateDescription: PreferenceKey { - .init(defaultValue: true, key: "PromptToCodeGenerateDescription") - } - - var promptToCodeGenerateDescriptionInUserPreferredLanguage: PreferenceKey { - .init(defaultValue: true, key: "PromptToCodeGenerateDescriptionInUserPreferredLanguage") - } - - var enableSenseScopeByDefaultInPromptToCode: PreferenceKey { - .init(defaultValue: false, key: "EnableSenseScopeByDefaultInPromptToCode") - } - - var promptToCodeCodeFontSize: PreferenceKey { - .init(defaultValue: 13, key: "PromptToCodeCodeFontSize") - } - - var hideCommonPrecedingSpacesInPromptToCode: PreferenceKey { - .init(defaultValue: true, key: "HideCommonPrecedingSpacesInPromptToCode") - } - - var wrapCodeInPromptToCode: PreferenceKey { - .init(defaultValue: true, key: "WrapCodeInPromptToCode") - } -} - -// MARK: - Suggestion - -public extension UserDefaultPreferenceKeys { - var oldSuggestionFeatureProvider: DeprecatedPreferenceKey { - .init(defaultValue: .gitHubCopilot, key: "SuggestionFeatureProvider") - } - - var suggestionFeatureProvider: PreferenceKey { - .init(defaultValue: .builtIn(.gitHubCopilot), key: "NewSuggestionFeatureProvider") - } - - var realtimeSuggestionToggle: PreferenceKey { - .init(defaultValue: true, key: "RealtimeSuggestionToggle") - } - - var suggestionDisplayCompactMode: PreferenceKey { - .init(defaultValue: true, key: "SuggestionDisplayCompactMode") - } - - var suggestionCodeFontSize: PreferenceKey { - .init(defaultValue: 13, key: "SuggestionCodeFontSize") - } - - var disableSuggestionFeatureGlobally: PreferenceKey { - .init(defaultValue: false, key: "DisableSuggestionFeatureGlobally") - } - - var suggestionFeatureEnabledProjectList: PreferenceKey<[String]> { - .init(defaultValue: [], key: "SuggestionFeatureEnabledProjectList") - } - - var suggestionFeatureDisabledLanguageList: PreferenceKey<[String]> { - .init(defaultValue: [], key: "SuggestionFeatureDisabledLanguageList") - } - - var hideCommonPrecedingSpacesInSuggestion: PreferenceKey { - .init(defaultValue: true, key: "HideCommonPrecedingSpacesInSuggestion") - } - - var suggestionPresentationMode: PreferenceKey { - .init(defaultValue: .nearbyTextCursor, key: "SuggestionPresentationMode") - } - - var realtimeSuggestionDebounce: PreferenceKey { - .init(defaultValue: 0.2, key: "RealtimeSuggestionDebounce") - } - - var acceptSuggestionWithTab: PreferenceKey { - .init(defaultValue: true, key: "AcceptSuggestionWithTab") - } - - var acceptSuggestionWithModifierCommand: PreferenceKey { - .init(defaultValue: false, key: "SuggestionWithModifierCommand") - } - - var acceptSuggestionWithModifierOption: PreferenceKey { - .init(defaultValue: false, key: "SuggestionWithModifierOption") - } - - var acceptSuggestionWithModifierControl: PreferenceKey { - .init(defaultValue: false, key: "SuggestionWithModifierControl") - } - - var acceptSuggestionWithModifierShift: PreferenceKey { - .init(defaultValue: false, key: "SuggestionWithModifierShift") - } - - var acceptSuggestionWithModifierOnlyForSwift: PreferenceKey { - .init(defaultValue: false, key: "SuggestionWithModifierOnlyForSwift") - } - - var dismissSuggestionWithEsc: PreferenceKey { - .init(defaultValue: true, key: "DismissSuggestionWithEsc") - } - - var isSuggestionSenseEnabled: PreferenceKey { - .init(defaultValue: false, key: "IsSuggestionSenseEnabled") - } - - var isSuggestionTypeInTheMiddleEnabled: PreferenceKey { - .init(defaultValue: true, key: "IsSuggestionTypeInTheMiddleEnabled") - } - - var clsWarningDismissedUntilRelaunch: PreferenceKey { - .init(defaultValue: false, key: "CLSWarningDismissedUntilRelaunch") - } -} - -// MARK: - Chat - -public extension UserDefaultPreferenceKeys { - - var chatFontSize: PreferenceKey { - .init(defaultValue: 13, key: "ChatFontSize") - } - - var chatCodeFontSize: PreferenceKey { - .init(defaultValue: 12, key: "ChatCodeFontSize") - } - - var useGlobalChat: PreferenceKey { - .init(defaultValue: true, key: "UseGlobalChat") - } - - var embedFileContentInChatContextIfNoSelection: PreferenceKey { - .init(defaultValue: false, key: "EmbedFileContentInChatContextIfNoSelection") - } - - var maxFocusedCodeLineCount: PreferenceKey { - .init(defaultValue: 100, key: "MaxEmbeddableFileInChatContextLineCount") - } - - var useCodeScopeByDefaultInChatContext: DeprecatedPreferenceKey { - .init(defaultValue: true, key: "UseSelectionScopeByDefaultInChatContext") - } - - - var wrapCodeInChatCodeBlock: PreferenceKey { - .init(defaultValue: true, key: "WrapCodeInChatCodeBlock") - } - - var enableFileScopeByDefaultInChatContext: PreferenceKey { - .init(defaultValue: true, key: "EnableFileScopeByDefaultInChatContext") - } - - var enableCodeScopeByDefaultInChatContext: PreferenceKey { - .init(defaultValue: true, key: "UseSelectionScopeByDefaultInChatContext") - } - - var enableSenseScopeByDefaultInChatContext: PreferenceKey { - .init(defaultValue: false, key: "EnableSenseScopeByDefaultInChatContext") - } - - var enableProjectScopeByDefaultInChatContext: PreferenceKey { - .init(defaultValue: false, key: "EnableProjectScopeByDefaultInChatContext") - } - - var disableFloatOnTopWhenTheChatPanelIsDetached: PreferenceKey { - .init(defaultValue: true, key: "DisableFloatOnTopWhenTheChatPanelIsDetached") - } - - var keepFloatOnTopIfChatPanelAndXcodeOverlaps: PreferenceKey { - .init(defaultValue: true, key: "KeepFloatOnTopIfChatPanelAndXcodeOverlaps") - } - - var enableCurrentEditorContext: PreferenceKey { - .init(defaultValue: true, key: "EnableCurrentEditorContext") - } - - var chatResponseLocale: PreferenceKey { - .init(defaultValue: "en", key: "ChatResponseLocale") - } - - var globalCopilotInstructions: PreferenceKey { - .init(defaultValue: "", key: "GlobalCopilotInstructions") - } - - var autoAttachChatToXcode: PreferenceKey { - .init(defaultValue: true, key: "AutoAttachChatToXcode") - } -} - -// MARK: - Theme - -public extension UserDefaultPreferenceKeys { - var syncSuggestionHighlightTheme: PreferenceKey { - .init(defaultValue: true, key: "SyncSuggestionHighlightTheme") - } - - var syncPromptToCodeHighlightTheme: PreferenceKey { - .init(defaultValue: false, key: "SyncPromptToCodeHighlightTheme") - } - - var syncChatCodeHighlightTheme: PreferenceKey { - .init(defaultValue: false, key: "SyncChatCodeHighlightTheme") - } - - var codeForegroundColorLight: PreferenceKey> { - .init(defaultValue: .init(nil), key: "CodeForegroundColorLight") - } - - var codeForegroundColorDark: PreferenceKey> { - .init(defaultValue: .init(nil), key: "CodeForegroundColorDark") - } - - var codeBackgroundColorLight: PreferenceKey> { - .init(defaultValue: .init(nil), key: "CodeBackgroundColorLight") - } - - var codeBackgroundColorDark: PreferenceKey> { - .init(defaultValue: .init(nil), key: "CodeBackgroundColorDark") - } - - var currentLineBackgroundColorLight: PreferenceKey> { - .init(defaultValue: .init(nil), key: "CurrentLineBackgroundColorLight") - } - - var currentLineBackgroundColorDark: PreferenceKey> { - .init(defaultValue: .init(nil), key: "CurrentLineBackgroundColorDark") - } - - var codeFontLight: PreferenceKey> { - .init( - defaultValue: .init(.init(nsFont: .monospacedSystemFont(ofSize: 12, weight: .regular))), - key: "CodeFontLight" - ) - } - - var codeFontDark: PreferenceKey> { - .init( - defaultValue: .init(.init(nsFont: .monospacedSystemFont(ofSize: 12, weight: .regular))), - key: "CodeFontDark" - ) - } - - var suggestionCodeFont: PreferenceKey> { - .init( - defaultValue: .init(.init(nsFont: .monospacedSystemFont(ofSize: 12, weight: .regular))), - key: "SuggestionCodeFont" - ) - } - - var promptToCodeCodeFont: PreferenceKey> { - .init( - defaultValue: .init(.init(nsFont: .monospacedSystemFont(ofSize: 12, weight: .regular))), - key: "PromptToCodeCodeFont" - ) - } - - var chatCodeFont: PreferenceKey> { - .init( - defaultValue: .init(.init(nsFont: .monospacedSystemFont(ofSize: 12, weight: .regular))), - key: "ChatCodeFont" - ) - } - - var terminalFont: PreferenceKey> { - .init( - defaultValue: .init(.init(nsFont: .monospacedSystemFont(ofSize: 12, weight: .regular))), - key: "TerminalCodeFont" - ) - } -} - -// MARK: - Bing Search - -public extension UserDefaultPreferenceKeys { - var bingSearchSubscriptionKey: PreferenceKey { - .init(defaultValue: "", key: "BingSearchSubscriptionKey") - } - - var bingSearchEndpoint: PreferenceKey { - .init( - defaultValue: "https://api.bing.microsoft.com/v7.0/search/", - key: "BingSearchEndpoint" - ) - } -} - -// MARK: - Custom Commands - -public extension UserDefaultPreferenceKeys { - var customCommands: PreferenceKey<[CustomCommand]> { - .init(defaultValue: [ - .init( - commandId: "BuiltInCustomCommandExplainSelection", - name: "Explain Selection", - feature: .chatWithSelection( - extraSystemPrompt: "", - prompt: "Explain the selected code concisely, step-by-step.", - useExtraSystemPrompt: true - ) - ), - .init( - commandId: "BuiltInCustomCommandAddDocumentationToSelection", - name: "Add Documentation to Selection", - feature: .promptToCode( - extraSystemPrompt: "", - prompt: "Add documentation on top of the code. Use triple slash if the language supports it.", - continuousMode: false, - generateDescription: true - ) - ) - ], key: "CustomCommands") - } - - var customChatCommands: PreferenceKey<[CustomCommand]> { - .init(defaultValue: [ - .init( - commandId: "BuiltInCustomCommandSendCodeToChat", - name: "Send Selected Code to Chat", - feature: .chatWithSelection( - extraSystemPrompt: "", - prompt: """ - ```{{active_editor_language}} - {{selected_code}} - ``` - """, - useExtraSystemPrompt: true - ) - ) - ], key: "CustomChatCommands") - } -} - -// MARK: - Feature Flags - -public extension UserDefaultPreferenceKeys { - var disableLazyVStack: FeatureFlag { - .init(defaultValue: false, key: "FeatureFlag-DisableLazyVStack") - } - - var preCacheOnFileOpen: FeatureFlag { - .init(defaultValue: true, key: "FeatureFlag-PreCacheOnFileOpen") - } - - var runNodeWithInteractiveLoggedInShell: FeatureFlag { - .init(defaultValue: true, key: "FeatureFlag-RunNodeWithInteractiveLoggedInShell") - } - - var useCustomScrollViewWorkaround: FeatureFlag { - .init(defaultValue: false, key: "FeatureFlag-UseCustomScrollViewWorkaround") - } - - var triggerActionWithAccessibilityAPI: FeatureFlag { - .init(defaultValue: true, key: "FeatureFlag-TriggerActionWithAccessibilityAPI") - } - - var alwaysAcceptSuggestionWithAccessibilityAPI: FeatureFlag { - .init(defaultValue: false, key: "FeatureFlag-AlwaysAcceptSuggestionWithAccessibilityAPI") - } - - var animationACrashSuggestion: FeatureFlag { - .init(defaultValue: true, key: "FeatureFlag-AnimationACrashSuggestion") - } - - var animationBCrashSuggestion: FeatureFlag { - .init(defaultValue: true, key: "FeatureFlag-AnimationBCrashSuggestion") - } - - var animationCCrashSuggestion: FeatureFlag { - .init(defaultValue: true, key: "FeatureFlag-AnimationCCrashSuggestion") - } - - var enableXcodeInspectorDebugMenu: FeatureFlag { - .init(defaultValue: false, key: "FeatureFlag-EnableXcodeInspectorDebugMenu") - } - - var disableFunctionCalling: FeatureFlag { - .init(defaultValue: false, key: "FeatureFlag-DisableFunctionCalling") - } - - var useUserDefaultsBaseAPIKeychain: FeatureFlag { - .init(defaultValue: false, key: "FeatureFlag-UseUserDefaultsBaseAPIKeychain") - } - - var disableGitHubCopilotSettingsAutoRefreshOnAppear: FeatureFlag { - .init( - defaultValue: false, - key: "FeatureFlag-DisableGitHubCopilotSettingsAutoRefreshOnAppear" - ) - } - - var disableEnhancedWorkspace: FeatureFlag { - .init( - defaultValue: false, - key: "FeatureFlag-DisableEnhancedWorkspace" - ) - } - - var restartXcodeInspectorIfAccessibilityAPIIsMalfunctioning: FeatureFlag { - .init( - defaultValue: false, - key: "FeatureFlag-RestartXcodeInspectorIfAccessibilityAPIIsMalfunctioning" - ) - } - - var restartXcodeInspectorIfAccessibilityAPIIsMalfunctioningNoTimer: FeatureFlag { - .init( - defaultValue: true, - key: "FeatureFlag-RestartXcodeInspectorIfAccessibilityAPIIsMalfunctioningNoTimer" - ) - } - - var toastForTheReasonWhyXcodeInspectorNeedsToBeRestarted: FeatureFlag { - .init( - defaultValue: false, - key: "FeatureFlag-ToastForTheReasonWhyXcodeInspectorNeedsToBeRestarted" - ) - } - - var observeToAXNotificationWithDefaultMode: FeatureFlag { - .init(defaultValue: false, key: "FeatureFlag-observeToAXNotificationWithDefaultMode") - } - - var useCloudflareDomainNameForLicenseCheck: FeatureFlag { - .init(defaultValue: false, key: "FeatureFlag-UseCloudflareDomainNameForLicenseCheck") - } -} - -// MARK: - Advanced Features - -public extension UserDefaultPreferenceKeys { - - var gitHubCopilotProxyUrl: PreferenceKey { - .init(defaultValue: "", key: "GitHubCopilotProxyUrl") - } - - var gitHubCopilotUseStrictSSL: PreferenceKey { - .init(defaultValue: true, key: "GitHubCopilotUseStrictSSL") - } - - var gitHubCopilotProxyUsername: PreferenceKey { - .init(defaultValue: "", key: "GitHubCopilotProxyUsername") - } - - var gitHubCopilotProxyPassword: PreferenceKey { - .init(defaultValue: "", key: "GitHubCopilotProxyPassword") - } - - var gitHubCopilotMCPConfig: PreferenceKey { - .init(defaultValue: "", key: "GitHubCopilotMCPConfig") - } - - var gitHubCopilotMCPUpdatedStatus: PreferenceKey { - .init(defaultValue: "", key: "GitHubCopilotMCPUpdatedStatus") - } - - var gitHubCopilotEnterpriseURI: PreferenceKey { - .init(defaultValue: "", key: "GitHubCopilotEnterpriseURI") - } - - var verboseLoggingEnabled: PreferenceKey { - .init(defaultValue: false, key: "VerboseLoggingEnabled") - } -} diff --git a/Tool/Sources/Preferences/UserDefaults.swift b/Tool/Sources/Preferences/UserDefaults.swift deleted file mode 100644 index dfaa5b67..00000000 --- a/Tool/Sources/Preferences/UserDefaults.swift +++ /dev/null @@ -1,306 +0,0 @@ -import AppKit -import Configs -import Foundation - -public protocol UserDefaultsType { - func value(forKey: String) -> Any? - func set(_ value: Any?, forKey: String) -} - -public extension UserDefaults { - static var shared = UserDefaults(suiteName: userDefaultSuiteName)! - - static func setupDefaultSettings() { - shared.setupDefaultValue(for: \.quitXPCServiceOnXcodeAndAppQuit) - shared.setupDefaultValue(for: \.realtimeSuggestionToggle) - shared.setupDefaultValue(for: \.realtimeSuggestionDebounce) - shared.setupDefaultValue(for: \.suggestionPresentationMode) - shared.setupDefaultValue(for: \.autoAttachChatToXcode) - shared.setupDefaultValue(for: \.widgetColorScheme) - shared.setupDefaultValue(for: \.customCommands) - shared.setupDefaultValue( - for: \.suggestionFeatureProvider, - defaultValue: .builtIn(shared.deprecatedValue(for: \.oldSuggestionFeatureProvider)) - ) - shared.setupDefaultValue( - for: \.promptToCodeCodeFontSize, - defaultValue: shared.value(for: \.suggestionCodeFontSize) - ) - shared.setupDefaultValue( - for: \.suggestionCodeFont, - defaultValue: .init(.init(nsFont: .monospacedSystemFont( - ofSize: shared.value(for: \.suggestionCodeFontSize), - weight: .regular - ))) - ) - shared.setupDefaultValue( - for: \.codeFontLight, - defaultValue: .init(.init(nsFont: .monospacedSystemFont( - ofSize: 12, - weight: .regular - ))) - ) - shared.setupDefaultValue( - for: \.codeFontDark, - defaultValue: .init(.init(nsFont: .monospacedSystemFont( - ofSize: 12, - weight: .regular - ))) - ) - shared.setupDefaultValue( - for: \.promptToCodeCodeFont, - defaultValue: .init(.init(nsFont: .monospacedSystemFont( - ofSize: shared.value(for: \.promptToCodeCodeFontSize), - weight: .regular - ))) - ) - shared.setupDefaultValue( - for: \.chatCodeFont, - defaultValue: .init(.init(nsFont: .monospacedSystemFont( - ofSize: shared.value(for: \.chatCodeFontSize), - weight: .regular - ))) - ) - } -} - -extension UserDefaults: UserDefaultsType {} - -public protocol UserDefaultsStorable {} - -extension Int: UserDefaultsStorable {} -extension Double: UserDefaultsStorable {} -extension Bool: UserDefaultsStorable {} -extension String: UserDefaultsStorable {} -extension Data: UserDefaultsStorable {} -extension URL: UserDefaultsStorable {} - -extension Array: RawRepresentable where Element: Codable { - public init?(rawValue: String) { - guard let data = rawValue.data(using: .utf8), - let result = try? JSONDecoder().decode([Element].self, from: data) - else { - return nil - } - self = result - } - - public var rawValue: String { - guard let data = try? JSONEncoder().encode(self), - let result = String(data: data, encoding: .utf8) - else { - return "[]" - } - return result - } -} - -public struct UserDefaultsStorageBox: RawRepresentable { - public let value: Element - - public init(_ value: Element) { - self.value = value - } - - public init?(rawValue: String) { - guard let data = rawValue.data(using: .utf8), - let result = try? JSONDecoder().decode(Element.self, from: data) - else { - return nil - } - value = result - } - - public var rawValue: String { - guard let data = try? JSONEncoder().encode(value), - let result = String(data: data, encoding: .utf8) - else { - return "" - } - return result - } -} - -extension UserDefaultsStorageBox: Equatable where Element: Equatable {} - -public extension UserDefaultsType { - // MARK: Normal Types - - func value( - for keyPath: KeyPath - ) -> K.Value where K.Value: UserDefaultsStorable { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - return (value(forKey: key.key) as? K.Value) ?? key.defaultValue - } - - func set( - _ value: K.Value, - for keyPath: KeyPath - ) where K.Value: UserDefaultsStorable { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - set(value, forKey: key.key) - } - - func setupDefaultValue( - for keyPath: KeyPath - ) where K.Value: UserDefaultsStorable { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - if value(forKey: key.key) == nil { - set(key.defaultValue, forKey: key.key) - } - } - - func setupDefaultValue( - for keyPath: KeyPath, - defaultValue: K.Value - ) where K.Value: UserDefaultsStorable { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - if value(forKey: key.key) == nil { - set(defaultValue, forKey: key.key) - } - } - - // MARK: Raw Representable - - func value( - for keyPath: KeyPath - ) -> K.Value where K.Value: RawRepresentable, K.Value.RawValue == String { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - guard let rawValue = value(forKey: key.key) as? String else { - return key.defaultValue - } - return K.Value(rawValue: rawValue) ?? key.defaultValue - } - - func value( - for keyPath: KeyPath - ) -> K.Value where K.Value: RawRepresentable, K.Value.RawValue == Int { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - guard let rawValue = value(forKey: key.key) as? Int else { - return key.defaultValue - } - return K.Value(rawValue: rawValue) ?? key.defaultValue - } - - func value( - for keyPath: KeyPath - ) -> V where K.Value == UserDefaultsStorageBox { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - guard let rawValue = value(forKey: key.key) as? String else { - return key.defaultValue.value - } - return (K.Value(rawValue: rawValue) ?? key.defaultValue).value - } - - func set( - _ value: K.Value, - for keyPath: KeyPath - ) where K.Value: RawRepresentable, K.Value.RawValue == String { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - set(value.rawValue, forKey: key.key) - } - - func set( - _ value: K.Value, - for keyPath: KeyPath - ) where K.Value: RawRepresentable, K.Value.RawValue == Int { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - set(value.rawValue, forKey: key.key) - } - - func set( - _ value: V, - for keyPath: KeyPath - ) where K.Value == UserDefaultsStorageBox { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - set(UserDefaultsStorageBox(value).rawValue, forKey: key.key) - } - - func setupDefaultValue( - for keyPath: KeyPath, - defaultValue: K.Value? = nil - ) where K.Value: RawRepresentable, K.Value.RawValue == String { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - if value(forKey: key.key) == nil { - set(defaultValue?.rawValue ?? key.defaultValue.rawValue, forKey: key.key) - } - } - - func setupDefaultValue( - for keyPath: KeyPath, - defaultValue: K.Value? = nil - ) where K.Value: RawRepresentable, K.Value.RawValue == Int { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - if value(forKey: key.key) == nil { - set(defaultValue?.rawValue ?? key.defaultValue.rawValue, forKey: key.key) - } - } -} - -// MARK: - Deprecated Key Accessor - -public extension UserDefaultsType { - // MARK: Normal Types - - func deprecatedValue( - for keyPath: KeyPath> - ) -> K where K: UserDefaultsStorable { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - return (value(forKey: key.key) as? K) ?? key.defaultValue - } - - // MARK: Raw Representable - - func deprecatedValue( - for keyPath: KeyPath> - ) -> K where K: RawRepresentable, K.RawValue == String { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - guard let rawValue = value(forKey: key.key) as? String else { - return key.defaultValue - } - return K(rawValue: rawValue) ?? key.defaultValue - } - - func deprecatedValue( - for keyPath: KeyPath> - ) -> K where K: RawRepresentable, K.RawValue == Int { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - guard let rawValue = value(forKey: key.key) as? Int else { - return key.defaultValue - } - return K(rawValue: rawValue) ?? key.defaultValue - } -} - -public extension UserDefaultsType { - @available(*, deprecated, message: "This preference key is deprecated.") - func value( - for keyPath: KeyPath> - ) -> K where K: UserDefaultsStorable { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - return (value(forKey: key.key) as? K) ?? key.defaultValue - } - - @available(*, deprecated, message: "This preference key is deprecated.") - func value( - for keyPath: KeyPath> - ) -> K where K: RawRepresentable, K.RawValue == String { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - guard let rawValue = value(forKey: key.key) as? String else { - return key.defaultValue - } - return K(rawValue: rawValue) ?? key.defaultValue - } - - @available(*, deprecated, message: "This preference key is deprecated.") - func value( - for keyPath: KeyPath> - ) -> K where K: RawRepresentable, K.RawValue == Int { - let key = UserDefaultPreferenceKeys()[keyPath: keyPath] - guard let rawValue = value(forKey: key.key) as? Int else { - return key.defaultValue - } - return K(rawValue: rawValue) ?? key.defaultValue - } -} - diff --git a/Tool/Sources/SharedUIComponents/AsyncCodeBlock.swift b/Tool/Sources/SharedUIComponents/AsyncCodeBlock.swift deleted file mode 100644 index 250f198a..00000000 --- a/Tool/Sources/SharedUIComponents/AsyncCodeBlock.swift +++ /dev/null @@ -1,270 +0,0 @@ -import DebounceFunction -import Foundation -import Perception -import SwiftUI - -public struct AsyncCodeBlock: View { - private struct Constants { - static let paddingLeading = 5.0 - static let paddingBottom = 10.0 - static let paddingTrailing = 10.0 - } - - @Environment(\.colorScheme) var colorScheme - @Binding var isExpanded: Bool - @State private var isHovering: Bool = false - @AppStorage(\.completionHintShown) var completionHintShown - - let code: String - let language: String - let startLineIndex: Int - let scenario: String - let firstLineIndent: Double - let lineHeight: Double - let font: NSFont - let proposedForegroundColor: Color? - let proposedBackgroundColor: Color? - let currentLineBackgroundColor: Color? - let dimmedCharacterCount: Int - let droppingLeadingSpaces: Bool - let isPanelDisplayed: Bool - - public init( - code: String, - language: String, - startLineIndex: Int, - scenario: String, - firstLineIndent: Double, - lineHeight: Double, - font: NSFont, - droppingLeadingSpaces: Bool, - proposedForegroundColor: Color?, - proposedBackgroundColor: Color?, - currentLineBackgroundColor: Color?, - dimmedCharacterCount: Int, - isExpanded: Binding, - isPanelDisplayed: Bool - ) { - self.code = code - self.startLineIndex = startLineIndex - self.language = language - self.scenario = scenario - self.firstLineIndent = firstLineIndent - self.lineHeight = lineHeight - self.font = font - self.proposedForegroundColor = proposedForegroundColor - self.proposedBackgroundColor = proposedBackgroundColor - self.currentLineBackgroundColor = currentLineBackgroundColor - self.dimmedCharacterCount = dimmedCharacterCount - self.droppingLeadingSpaces = droppingLeadingSpaces - self._isExpanded = isExpanded - self.isPanelDisplayed = isPanelDisplayed - } - - var foregroundColor: Color { - if let proposedForegroundColor = proposedForegroundColor { - return proposedForegroundColor - } - return colorScheme == .light ? .black.opacity(0.85) : .white.opacity(0.85) - } - - var foregroundTextColor: Color { - return foregroundColor.opacity(0.6) - } - - var backgroundColor: Color { - if let proposedBackgroundColor = proposedBackgroundColor { - return proposedBackgroundColor - } - return colorScheme == .dark ? Color(red: 0.1216, green: 0.1216, blue: 0.1412) : .white - } - - var fontHeight: Double { - (font.ascender + abs(font.descender)).rounded(.down) - } - - var lineSpacing: Double { - lineHeight - fontHeight - } - - var expandedIndent: Double { - let lines = code.splitByNewLine() - guard let firstLine = lines.first else { return 0 } - let existing = String(firstLine.prefix(dimmedCharacterCount)) - let attr = NSAttributedString(string: existing, attributes: [.font: font]) - return firstLineIndent - attr.size().width - } - - var hintText: String { - if isExpanded { - return "Press ⌥⇥ to accept full suggestion" - } - return "Hold ⌥ for full suggestion" - } - - - @ScaledMetric var keyPadding: Double = 3.0 - - @ViewBuilder - func keyBackground(content: () -> some View) -> some View { - content() - .padding(.horizontal, keyPadding) - .background( - RoundedRectangle(cornerRadius: 2) - .stroke(foregroundColor, lineWidth: 1) - .foregroundColor(.clear) - .frame( - minWidth: fontHeight, - minHeight: fontHeight, - maxHeight: fontHeight - ) - ) - } - - @ViewBuilder - var optionKey: some View { - keyBackground { - Image(systemName: "option") - .resizable() - .renderingMode(.template) - .scaledToFit() - .frame(height: font.capHeight) - } - } - - @ViewBuilder - var popoverContent: some View { - HStack { - if isExpanded { - Text("Press") - optionKey - keyBackground { - Text("tab") - .font(.init(font)) - } - Text("to accept full suggestion") - } else { - Text("Hold") - optionKey - Text("for full suggestion") - } - } - .padding(8) - .font(.body) - .fixedSize() - } - - @ViewBuilder - func lineBackgroundShape(_ multiLine: Bool) -> some View { - let color = currentLineBackgroundColor ?? backgroundColor - switch multiLine { - case true: HalfCapsule().fill(color) - case false: Rectangle().fill(color) - } - } - - @ScaledMetric var iconPadding: CGFloat = 9.0 - @ScaledMetric var iconSpacing: CGFloat = 6.0 - @ScaledMetric var optionPadding: CGFloat = 0.5 - - @ViewBuilder - var contentView: some View { - let lines = code.splitByNewLine() - if let firstLine = lines.first { - let firstLineTrimmed = firstLine - .dropFirst(dimmedCharacterCount) - HStack() { - HStack(alignment: .center, spacing: 10) { - Text(firstLineTrimmed) - .foregroundColor(foregroundTextColor) - .lineSpacing(lineSpacing) // This only has effect if a line wraps - if lines.count > 1 { - HStack(spacing: iconSpacing) { - Image("CopilotLogo") - .resizable() - .renderingMode(.template) - .scaledToFit() - Image(systemName: "option") - .resizable() - .renderingMode(.template) - .scaledToFit() - .padding(.vertical, optionPadding) - } - .frame(height: lineHeight * 0.7) - .padding(.horizontal, iconPadding) - .background( - Capsule() - .fill(foregroundColor.opacity(isExpanded ? 0.1 : 0.2)) - .frame(height: lineHeight) - ) - .frame(height: lineHeight) // Moves popover attachment - .popover(isPresented: $isHovering) { - popoverContent - } - .task { - isHovering = !completionHintShown - completionHintShown = true - } - } - } - .frame(height: lineHeight) - .background(lineBackgroundShape(lines.count > 1)) - .padding(.leading, firstLineIndent) - .onHover { hovering in - guard hovering != isHovering else { return } - withAnimation { - isHovering = hovering - } - } - Spacer() - } - } - - if isExpanded && lines.count > 1 { - HStack() { - CustomScrollView { - VStack(alignment: .leading, spacing: 0) { - ForEach(Array(lines.dropFirst()), id: \.self) { line in - HStack(alignment: .firstTextBaseline, spacing: 4) { - Text(line) - .foregroundColor(foregroundTextColor) - .lineSpacing(lineSpacing) - } - .frame(minHeight: lineHeight) - } - } - } - .padding(EdgeInsets( - top: 0, - leading: Constants.paddingLeading, - bottom: Constants.paddingBottom, - trailing: Constants.paddingTrailing - )) - .background(backgroundColor) - .cornerRadius(10) - .overlay(RoundedRectangle(cornerRadius: 10).stroke(foregroundColor.opacity(0.2), lineWidth: 1)) // border - .shadow(color: Color.black.opacity(0.2), radius: 8.0, x: 1, y: 1) - .onHover { hovering in - guard hovering != isHovering else { return } - withAnimation { - isHovering = hovering - } - } - Spacer() - } - .padding(.leading, expandedIndent - Constants.paddingLeading) - } - } - - public var body: some View { - if isPanelDisplayed { - WithPerceptionTracking { - VStack(spacing: 0) { - contentView - } - .font(.init(font)) - .background(Color.clear) - } - } - } -} diff --git a/Tool/Sources/SharedUIComponents/CodeBlock.swift b/Tool/Sources/SharedUIComponents/CodeBlock.swift deleted file mode 100644 index 6fd852ed..00000000 --- a/Tool/Sources/SharedUIComponents/CodeBlock.swift +++ /dev/null @@ -1,129 +0,0 @@ -import Preferences -import SwiftUI - -public struct CodeBlock: View { - public let code: String - public let language: String - public let startLineIndex: Int - public let scenario: String - public let colorScheme: ColorScheme - public let commonPrecedingSpaceCount: Int - public let highlightedCode: [NSAttributedString] - public let firstLinePrecedingSpaceCount: Int - public let font: NSFont - public let droppingLeadingSpaces: Bool - public let proposedForegroundColor: Color? - public let wrapCode: Bool - - public init( - code: String, - language: String, - startLineIndex: Int, - scenario: String, - colorScheme: ColorScheme, - firstLinePrecedingSpaceCount: Int = 0, - font: NSFont, - droppingLeadingSpaces: Bool, - proposedForegroundColor: Color?, - wrapCode: Bool = true - ) { - self.code = code - self.language = language - self.startLineIndex = startLineIndex - self.scenario = scenario - self.colorScheme = colorScheme - self.droppingLeadingSpaces = droppingLeadingSpaces - self.firstLinePrecedingSpaceCount = firstLinePrecedingSpaceCount - self.font = font - self.proposedForegroundColor = proposedForegroundColor - self.wrapCode = wrapCode - let padding = firstLinePrecedingSpaceCount > 0 - ? String(repeating: " ", count: firstLinePrecedingSpaceCount) - : "" - let result = Self.highlight( - code: padding + code, - language: language, - scenario: scenario, - colorScheme: colorScheme, - font: font, - droppingLeadingSpaces: droppingLeadingSpaces - ) - commonPrecedingSpaceCount = result.commonLeadingSpaceCount - highlightedCode = result.code - } - - var foregroundColor: Color { - proposedForegroundColor ?? (colorScheme == .dark ? .white : .black) - } - - public var body: some View { - VStack(spacing: 2) { - ForEach(0.. 0 { - Text("\(commonPrecedingSpaceCount + 1)") - .padding(.top, -12) - .font(.footnote) - .foregroundStyle(foregroundColor) - .opacity(0.3) - } - } - } - } - } - .foregroundColor(.white) - .font(.init(font)) - .padding(.leading, 4) - .padding([.trailing, .top, .bottom]) - } - - static func highlight( - code: String, - language: String, - scenario: String, - colorScheme: ColorScheme, - font: NSFont, - droppingLeadingSpaces: Bool - ) -> (code: [NSAttributedString], commonLeadingSpaceCount: Int) { - return CodeHighlighting.highlighted( - code: code, - language: language, - scenario: scenario, - brightMode: colorScheme != .dark, - droppingLeadingSpaces: droppingLeadingSpaces, - font: font - ) - } -} - -// MARK: - Preview - -struct CodeBlock_Previews: PreviewProvider { - static var previews: some View { - CodeBlock( - code: """ - let foo = Foo() - let bar = Bar() - """, - language: "swift", - startLineIndex: 0, - scenario: "", - colorScheme: .dark, - firstLinePrecedingSpaceCount: 0, - font: .monospacedSystemFont(ofSize: 12, weight: .regular), - droppingLeadingSpaces: true, - proposedForegroundColor: nil - ) - } -} - diff --git a/Tool/Sources/SharedUIComponents/ConditionalFontWeight.swift b/Tool/Sources/SharedUIComponents/ConditionalFontWeight.swift deleted file mode 100644 index 55cc15c7..00000000 --- a/Tool/Sources/SharedUIComponents/ConditionalFontWeight.swift +++ /dev/null @@ -1,23 +0,0 @@ -import SwiftUI - -public struct ConditionalFontWeight: ViewModifier { - let weight: Font.Weight? - - public init(weight: Font.Weight?) { - self.weight = weight - } - - public func body(content: Content) -> some View { - if #available(macOS 13.0, *), weight != nil { - content.fontWeight(weight) - } else { - content - } - } -} - -public extension View { - func conditionalFontWeight(_ weight: Font.Weight?) -> some View { - self.modifier(ConditionalFontWeight(weight: weight)) - } -} diff --git a/Tool/Sources/SharedUIComponents/CopilotIntroSheet.swift b/Tool/Sources/SharedUIComponents/CopilotIntroSheet.swift deleted file mode 100644 index a077a320..00000000 --- a/Tool/Sources/SharedUIComponents/CopilotIntroSheet.swift +++ /dev/null @@ -1,141 +0,0 @@ -import SwiftUI -import AppKit - -struct CopilotIntroItem: View { - let heading: String - let text: String - let image: Image - - public init(imageName: String, heading: String, text: String) { - self.init(imageObject: Image(imageName), heading: heading, text: text) - } - - public init(systemImage: String, heading: String, text: String) { - self.init(imageObject: Image(systemName: systemImage), heading: heading, text: text) - } - - public init(imageObject: Image, heading: String, text: String) { - self.heading = heading - self.text = text - self.image = imageObject - } - - var body: some View { - HStack(spacing: 16) { - image - .resizable() - .renderingMode(.template) - .foregroundColor(.blue) - .scaledToFit() - .frame(width: 28, height: 28) - VStack(alignment: .leading, spacing: 5) { - Text(heading) - .font(.system(size: 11, weight: .bold)) - Text(text) - .font(.system(size: 11)) - .lineSpacing(3) - } - } - } -} - -struct CopilotIntroContent: View { - let hideIntro: Binding - let continueAction: () -> Void - - var body: some View { - VStack { - let appImage = if let nsImage = NSImage(named: "AppIcon") { - Image(nsImage: nsImage) - } else { - Image(systemName: "app") - } - appImage - .resizable() - .scaledToFit() - .frame(width: 64, height: 64) - .padding(.bottom, 24) - Text("Welcome to Copilot for Xcode!") - .font(.title.bold()) - .padding(.bottom, 38) - - VStack(alignment: .leading, spacing: 20) { - CopilotIntroItem( - imageName: "CopilotLogo", - heading: "In-line Code Suggestions", - text: "Receive context-aware code suggestions and text completion in your Xcode editor. Just press Tab ⇥ to accept a suggestion." - ) - - CopilotIntroItem( - systemImage: "option", - heading: "Full Suggestions", - text: "Press Option ⌥ for full multi-line suggestions. Only the first line is shown inline. Use Copilot Chat to refine, explain, or improve them." - ) - - CopilotIntroItem( - imageName: "ChatIcon", - heading: "Chat", - text: "Get real-time coding assistance, debug issues, and generate code snippets directly within Xcode." - ) - - CopilotIntroItem( - imageName: "GitHubMark", - heading: "GitHub Context", - text: "Copilot gives smarter code suggestions using your GitHub and project context. Use chat to discuss your code, debug issues, or get explanations." - ) - } - .padding(.bottom, 64) - - VStack(spacing: 8) { - Button(action: continueAction) { - Text("Continue") - .padding(.horizontal, 80) - .padding(.vertical, 6) - } - .buttonStyle(.borderedProminent) - - Toggle("Don't show again", isOn: hideIntro) - .toggleStyle(.checkbox) - } - } - .padding(.horizontal, 56) - .padding(.top, 48) - .padding(.bottom, 16) - .frame(width: 560) - } -} - -public struct CopilotIntroSheet: View { - let content: Content - let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "" - @AppStorage(\.hideIntro) var hideIntro - @AppStorage(\.introLastShownVersion) var introLastShownVersion - @State var isPresented = false - - public var body: some View { - content.sheet(isPresented: $isPresented) { - CopilotIntroContent(hideIntro: $hideIntro) { - isPresented = false - } - } - .task { - if hideIntro == false { - isPresented = true - introLastShownVersion = appVersion - } - } - } -} - -public extension View { - func copilotIntroSheet() -> some View { - CopilotIntroSheet(content: self) - } -} - - -// MARK: - Preview -@available(macOS 14.0, *) -#Preview(traits: .sizeThatFitsLayout) { - CopilotIntroContent(hideIntro: .constant(false)) { } -} diff --git a/Tool/Sources/SharedUIComponents/CopilotMessageHeader.swift b/Tool/Sources/SharedUIComponents/CopilotMessageHeader.swift deleted file mode 100644 index afaa6073..00000000 --- a/Tool/Sources/SharedUIComponents/CopilotMessageHeader.swift +++ /dev/null @@ -1,26 +0,0 @@ -import SwiftUI - -public struct CopilotMessageHeader: View { - public init() {} - - public var body: some View { - HStack { - Image("CopilotLogo") - .resizable() - .renderingMode(.template) - .scaledToFill() - .frame(width: 12, height: 12) - .overlay( - Circle() - .stroke(Color(nsColor: .separatorColor), lineWidth: 1) - .frame(width: 24, height: 24) - ) - Text("GitHub Copilot") - .font(.system(size: 13)) - .fontWeight(.semibold) - .padding(4) - - Spacer() - } - } -} diff --git a/Tool/Sources/WebContentExtractor/HTMLToMarkdownConverter.swift b/Tool/Sources/WebContentExtractor/HTMLToMarkdownConverter.swift deleted file mode 100644 index 56236a0d..00000000 --- a/Tool/Sources/WebContentExtractor/HTMLToMarkdownConverter.swift +++ /dev/null @@ -1,217 +0,0 @@ -import SwiftSoup -import WebKit - -class HTMLToMarkdownConverter { - - // MARK: - Configuration - private struct Config { - static let unwantedSelectors = "script, style, nav, header, footer, aside, noscript, iframe, .navigation, .sidebar, .ad, .advertisement, .cookie-banner, .popup, .social, .share, .social-share, .related, .comments, .menu, .breadcrumb" - static let mainContentSelectors = [ - "main", - "article", - "div.content", - "div#content", - "div.post-content", - "div.article-body", - "div.main-content", - "section.content", - ".content", - ".main", - ".main-content", - ".article", - ".article-content", - ".post-content", - "#content", - "#main", - ".container .row .col", - "[role='main']" - ] - } - - // MARK: - Main Conversion Method - func convertToMarkdown(from html: String) throws -> String { - let doc = try SwiftSoup.parse(html) - let rawMarkdown = try extractCleanContent(from: doc) - return cleanupExcessiveNewlines(rawMarkdown) - } - - // MARK: - Content Extraction - private func extractCleanContent(from doc: Document) throws -> String { - try removeUnwantedElements(from: doc) - - // Try to find main content areas - for selector in Config.mainContentSelectors { - if let mainElement = try findMainContent(in: doc, using: selector) { - return try convertElementToMarkdown(mainElement) - } - } - - // Fallback: clean body content - return try fallbackContentExtraction(from: doc) - } - - private func removeUnwantedElements(from doc: Document) throws { - try doc.select(Config.unwantedSelectors).remove() - } - - private func findMainContent(in doc: Document, using selector: String) throws -> Element? { - let elements = try doc.select(selector) - guard let mainElement = elements.first() else { return nil } - - // Clean nested unwanted elements - try mainElement.select("nav, aside, .related, .comments, .social-share, .advertisement").remove() - return mainElement - } - - private func fallbackContentExtraction(from doc: Document) throws -> String { - guard let body = doc.body() else { return "" } - try body.select(Config.unwantedSelectors).remove() - return try convertElementToMarkdown(body) - } - - // MARK: - Cleanup Method - private func cleanupExcessiveNewlines(_ markdown: String) -> String { - // Replace 3+ consecutive newlines with just 2 newlines - let cleaned = markdown.replacingOccurrences( - of: #"\n{3,}"#, - with: "\n\n", - options: .regularExpression - ) - return cleaned.trimmingCharacters(in: .whitespacesAndNewlines) - } - - // MARK: - Element Processing - private func convertElementToMarkdown(_ element: Element) throws -> String { - let markdown = try convertElement(element) - return markdown - } - - func convertElement(_ element: Element) throws -> String { - var result = "" - - for node in element.getChildNodes() { - if let textNode = node as? TextNode { - result += textNode.text() - } else if let childElement = node as? Element { - result += try convertSpecificElement(childElement) - } - } - - return result - } - - private func convertSpecificElement(_ element: Element) throws -> String { - let tagName = element.tagName().lowercased() - let text = try element.text() - - switch tagName { - case "h1": - return "\n# \(text)\n" - case "h2": - return "\n## \(text)\n" - case "h3": - return "\n### \(text)\n" - case "h4": - return "\n#### \(text)\n" - case "h5": - return "\n##### \(text)\n" - case "h6": - return "\n###### \(text)\n" - case "p": - return "\n\(try convertElement(element))\n" - case "br": - return "\n" - case "strong", "b": - return "**\(text)**" - case "em", "i": - return "*\(text)*" - case "code": - return "`\(text)`" - case "pre": - return "\n```\n\(text)\n```\n" - case "a": - let href = try element.attr("href") - let title = try element.attr("title") - if href.isEmpty { - return text - } - - // Skip non-http/https/file schemes - if let url = URL(string: href), - let scheme = url.scheme?.lowercased(), - !["http", "https", "file"].contains(scheme) { - return text - } - - let titlePart = title.isEmpty ? "" : " \"\(title.replacingOccurrences(of: "\"", with: "\\\""))\"" - return "[\(text)](\(href)\(titlePart))" - case "img": - let src = try element.attr("src") - let alt = try element.attr("alt") - let title = try element.attr("title") - - var finalSrc = src - // Remove data URIs - if src.hasPrefix("data:") { - finalSrc = src.components(separatedBy: ",").first ?? "" + "..." - } - - let titlePart = title.isEmpty ? "" : " \"\(title.replacingOccurrences(of: "\"", with: "\\\""))\"" - return "![\(alt)](\(finalSrc)\(titlePart))" - case "ul": - return try convertList(element, ordered: false) - case "ol": - return try convertList(element, ordered: true) - case "li": - return try convertElement(element) - case "table": - return try convertTable(element) - case "blockquote": - let content = try convertElement(element) - return content.components(separatedBy: .newlines) - .map { "> \($0)" } - .joined(separator: "\n") - default: - return try convertElement(element) - } - } - - private func convertList(_ element: Element, ordered: Bool) throws -> String { - var result = "\n" - let items = try element.select("li") - - for (index, item) in items.enumerated() { - let content = try convertElement(item).trimmingCharacters(in: .whitespacesAndNewlines) - if ordered { - result += "\(index + 1). \(content)\n" - } else { - result += "- \(content)\n" - } - } - - return result - } - - private func convertTable(_ element: Element) throws -> String { - var result = "\n" - let rows = try element.select("tr") - - guard !rows.isEmpty() else { return "" } - - var isFirstRow = true - for row in rows { - let cells = try row.select("td, th") - let cellContents = try cells.map { try $0.text() } - - result += "| " + cellContents.joined(separator: " | ") + " |\n" - - if isFirstRow { - let separator = Array(repeating: "---", count: cellContents.count).joined(separator: " | ") - result += "| \(separator) |\n" - isFirstRow = false - } - } - - return result - } -} diff --git a/Tool/Sources/WebContentExtractor/WebContentExtractor.swift b/Tool/Sources/WebContentExtractor/WebContentExtractor.swift deleted file mode 100644 index aee0d889..00000000 --- a/Tool/Sources/WebContentExtractor/WebContentExtractor.swift +++ /dev/null @@ -1,227 +0,0 @@ -import WebKit -import Logger -import Preferences - -public class WebContentFetcher: NSObject, WKNavigationDelegate { - private var webView: WKWebView? - private var loadingTimer: Timer? - private static let converter = HTMLToMarkdownConverter() - private var completion: ((Result) -> Void)? - - private struct Config { - static let timeout: TimeInterval = 30.0 - static let contentLoadDelay: TimeInterval = 2.0 - } - - public enum WebContentError: Error, LocalizedError { - case invalidURL(String) - case timeout - case noContent - case navigationFailed(Error) - case javascriptError(Error) - - public var errorDescription: String? { - switch self { - case .invalidURL(let url): "Invalid URL: \(url)" - case .timeout: "Request timed out" - case .noContent: "No content found" - case .navigationFailed(let error): "Navigation failed: \(error.localizedDescription)" - case .javascriptError(let error): "JavaScript execution error: \(error.localizedDescription)" - } - } - } - - // MARK: - Initialization - public override init() { - super.init() - setupWebView() - } - - deinit { - cleanup() - } - - // MARK: - Public Methods - public func fetchContent(from urlString: String, completion: @escaping (Result) -> Void) { - guard let url = URL(string: urlString) else { - completion(.failure(WebContentError.invalidURL(urlString))) - return - } - - DispatchQueue.main.async { [weak self] in - self?.completion = completion - self?.setupTimeout() - self?.loadContent(from: url) - } - } - - public static func fetchContentAsync(from urlString: String) async throws -> String { - try await withCheckedThrowingContinuation { continuation in - let fetcher = WebContentFetcher() - fetcher.fetchContent(from: urlString) { result in - withExtendedLifetime(fetcher) { - continuation.resume(with: result) - } - } - } - } - - public static func fetchMultipleContentAsync(from urls: [String]) async -> [String] { - var results: [String] = [] - - for url in urls { - do { - let content = try await fetchContentAsync(from: url) - results.append("Successfully fetched content from \(url): \(content)") - } catch { - Logger.client.error("Failed to fetch content from \(url): \(error.localizedDescription)") - results.append("Failed to fetch content from \(url) with error: \(error.localizedDescription)") - } - } - - return results - } - - // MARK: - Private Methods - private func setupWebView() { - let configuration = WKWebViewConfiguration() - let dataSource = WKWebsiteDataStore.nonPersistent() - - if #available(macOS 14.0, *) { - configureProxy(for: dataSource) - } - - configuration.websiteDataStore = dataSource - webView = WKWebView(frame: .zero, configuration: configuration) - webView?.navigationDelegate = self - } - - @available(macOS 14.0, *) - private func configureProxy(for dataSource: WKWebsiteDataStore) { - let proxyURL = UserDefaults.shared.value(for: \.gitHubCopilotProxyUrl) - guard let url = URL(string: proxyURL), - let host = url.host, - let port = url.port, - let proxyPort = NWEndpoint.Port(port.description) else { return } - - let tlsOptions = NWProtocolTLS.Options() - let useStrictSSL = UserDefaults.shared.value(for: \.gitHubCopilotUseStrictSSL) - - if !useStrictSSL { - let secOptions = tlsOptions.securityProtocolOptions - sec_protocol_options_set_verify_block(secOptions, { _, _, completion in - completion(true) - }, .main) - } - - let httpProxy = ProxyConfiguration( - httpCONNECTProxy: NWEndpoint.hostPort( - host: NWEndpoint.Host(host), - port: proxyPort - ), - tlsOptions: tlsOptions - ) - - httpProxy.applyCredential( - username: UserDefaults.shared.value(for: \.gitHubCopilotProxyUsername), - password: UserDefaults.shared.value(for: \.gitHubCopilotProxyPassword) - ) - - dataSource.proxyConfigurations = [httpProxy] - } - - private func cleanup() { - loadingTimer?.invalidate() - loadingTimer = nil - webView?.navigationDelegate = nil - webView?.stopLoading() - webView = nil - } - - private func setupTimeout() { - loadingTimer?.invalidate() - loadingTimer = Timer.scheduledTimer(withTimeInterval: Config.timeout, repeats: false) { [weak self] _ in - DispatchQueue.main.async { - Logger.client.error("Request timed out") - self?.completeWithError(WebContentError.timeout) - } - } - } - - private func loadContent(from url: URL) { - if webView == nil { - setupWebView() - } - - guard let webView = webView else { - completeWithError(WebContentError.navigationFailed(NSError(domain: "WebView creation failed", code: -1))) - return - } - - let request = URLRequest( - url: url, - cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, - timeoutInterval: Config.timeout - ) - webView.load(request) - } - - private func processHTML(_ html: String) { - do { - let cleanedText = try Self.converter.convertToMarkdown(from: html) - completeWithSuccess(cleanedText) - } catch { - Logger.client.error("SwiftSoup parsing error: \(error.localizedDescription)") - completeWithError(error) - } - } - - private func completeWithSuccess(_ content: String) { - completion?(.success(content)) - completion = nil - } - - private func completeWithError(_ error: Error) { - completion?(.failure(error)) - completion = nil - } - - // MARK: - WKNavigationDelegate - public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { - loadingTimer?.invalidate() - - DispatchQueue.main.asyncAfter(deadline: .now() + Config.contentLoadDelay) { - webView.evaluateJavaScript("document.body.innerHTML") { [weak self] result, error in - DispatchQueue.main.async { - if let error = error { - Logger.client.error("JavaScript execution error: \(error.localizedDescription)") - self?.completeWithError(WebContentError.javascriptError(error)) - return - } - - if let html = result as? String, !html.isEmpty { - self?.processHTML(html) - } else { - self?.completeWithError(WebContentError.noContent) - } - } - } - } - } - - public func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) { - handleNavigationFailure(error) - } - - public func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) { - handleNavigationFailure(error) - } - - private func handleNavigationFailure(_ error: Error) { - loadingTimer?.invalidate() - DispatchQueue.main.async { - Logger.client.error("Navigation failed: \(error.localizedDescription)") - self.completeWithError(WebContentError.navigationFailed(error)) - } - } -} diff --git a/Tool/Tests/ASTParserTests/CursorDeepFirstSearchTests.swift b/Tool/Tests/ASTParserTests/CursorDeepFirstSearchTests.swift deleted file mode 100644 index cb70853a..00000000 --- a/Tool/Tests/ASTParserTests/CursorDeepFirstSearchTests.swift +++ /dev/null @@ -1,91 +0,0 @@ -import Foundation -import XCTest - -@testable import ASTParser - -class CursorDeepFirstSearchTests: XCTestCase { - class TN { - var parent: TN? - var value: Int - var children: [TN] = [] - - init(_ value: Int, _ children: [TN] = []) { - self.value = value - self.children = children - children.forEach { $0.parent = self } - } - } - - class ACursor: Cursor { - var currentNode: TN? - init(currentNode: TN?) { - self.currentNode = currentNode - } - - func goToFirstChild() -> Bool { - if let first = currentNode?.children.first { - currentNode = first - return true - } - return false - } - - func goToNextSibling() -> Bool { - if let parent = currentNode?.parent, - let index = parent.children.firstIndex(where: { $0 === currentNode }), - index < parent.children.count - 1 { - currentNode = parent.children[index + 1] - return true - } - return false - } - - func goToParent() -> Bool { - if let parent = currentNode?.parent { - currentNode = parent - return true - } - return false - } - } - - func test_deep_first_search() { - let root = TN(0, [ - TN(1, [ - TN(2), - TN(3) - ]), - TN(4, [ - TN(5, [TN(6, [TN(7)])]), - TN(8) - ]) - ]) - let cursor = ACursor(currentNode: root) - var result = [Int]() - for node in CursorDeepFirstSearchSequence(cursor: cursor, skipChildren: { _ in true }) { - result.append(node.value) - } - - XCTAssertEqual(result, result.sorted()) - } - - func test_deep_first_search_skip_children() { - let root = TN(0, [ - TN(1, [ - TN(2), - TN(3) - ]), - TN(4, [ - TN(5, [TN(6, [TN(7)])]), - TN(8) - ]) - ]) - let cursor = ACursor(currentNode: root) - var result = [Int]() - for node in CursorDeepFirstSearchSequence(cursor: cursor, skipChildren: { $0.value == 5 }) { - result.append(node.value) - } - - XCTAssertEqual(result, [0, 1, 2, 3, 4, 5, 8]) - } -} diff --git a/Tool/Tests/ActiveDocumentChatContextCollectorTests/File.swift b/Tool/Tests/ActiveDocumentChatContextCollectorTests/File.swift deleted file mode 100644 index 8b137891..00000000 --- a/Tool/Tests/ActiveDocumentChatContextCollectorTests/File.swift +++ /dev/null @@ -1 +0,0 @@ - diff --git a/Tool/Tests/FocusedCodeFinderTests/ObjectiveCFocusedCodeFinderTests.swift b/Tool/Tests/FocusedCodeFinderTests/ObjectiveCFocusedCodeFinderTests.swift deleted file mode 100644 index af9d3c02..00000000 --- a/Tool/Tests/FocusedCodeFinderTests/ObjectiveCFocusedCodeFinderTests.swift +++ /dev/null @@ -1,462 +0,0 @@ -import Foundation -import SuggestionBasic -import XCTest - -@testable import FocusedCodeFinder - -final class ObjectiveCFocusedCodeFinder_Selection_Tests: XCTestCase { - func test_selecting_a_line_inside_the_method_the_scope_should_be_the_method() { - let code = """ - @implementation Foo - - (void)fooWith:(NSInteger)foo { - NSInteger foo = 0; - NSLog(@"Hello"); - NSLog(@"World"); - } - @end - """ - let range = CursorRange( - start: CursorPosition(line: 2, character: 0), - end: CursorPosition(line: 2, character: 4) - ) - let context = ObjectiveCFocusedCodeFinder(maxFocusedCodeLineCount: .max).findFocusedCode( - in: document(code: code), - containingRange: range - ) - XCTAssertEqual(context, .init( - scope: .scope(signature: [ - .init( - signature: "@implementation Foo", - name: "Foo", - range: .init(startPair: (0, 0), endPair: (6, 4)) - ), - .init( - signature: "- (void)fooWith:(NSInteger)foo", - name: "fooWith:(NSInteger)foo", - range: .init(startPair: (1, 0), endPair: (5, 1)) - ), - ]), - contextRange: .init(startPair: (0, 0), endPair: (6, 4)), - smallestContextRange: range, - focusedRange: range, - focusedCode: """ - NSInteger foo = 0; - - """, - imports: [], - includes: [] - )) - } - - func test_selecting_a_line_inside_a_function_the_scope_should_be_the_function() { - let code = """ - void foo(char name[]) { - NSInteger foo = 0; - NSLog(@"Hello"); - NSLog(@"World"); - } - """ - let range = CursorRange(startPair: (2, 0), endPair: (2, 4)) - let context = ObjectiveCFocusedCodeFinder(maxFocusedCodeLineCount: .max).findFocusedCode( - in: document(code: code), - containingRange: range - ) - XCTAssertEqual(context, .init( - scope: .scope(signature: [ - .init( - signature: "void foo(char name[])", - name: "foo", - range: .init(startPair: (0, 0), endPair: (4, 1)) - ), - ]), - contextRange: .init(startPair: (0, 0), endPair: (4, 1)), - smallestContextRange: range, - focusedRange: range, - focusedCode: """ - NSLog(@"Hello"); - - """, - imports: [], - includes: [] - )) - } - - func test_selecting_a_method_inside_an_implementation_the_scope_should_be_the_implementation() { - let code = """ - __attribute__((objc_nonlazy_class)) - @implementation Foo (Category) - - (void)fooWith:(NSInteger)foo { - NSInteger foo = 0; - NSLog(@"Hello"); - NSLog(@"World"); - } - @end - """ - let range = CursorRange( - start: CursorPosition(line: 2, character: 0), - end: CursorPosition(line: 6, character: 1) - ) - let context = ObjectiveCFocusedCodeFinder(maxFocusedCodeLineCount: .max).findFocusedCode( - in: document(code: code), - containingRange: range - ) - XCTAssertEqual(context, .init( - scope: .scope(signature: [ - .init( - signature: "__attribute__((objc_nonlazy_class)) @implementation Foo (Category)", - name: "Foo", - range: .init(startPair: (0, 0), endPair: (7, 4)) - ), - ]), - contextRange: .init(startPair: (0, 0), endPair: (7, 4)), - smallestContextRange: range, - focusedRange: range, - focusedCode: """ - - (void)fooWith:(NSInteger)foo { - NSInteger foo = 0; - NSLog(@"Hello"); - NSLog(@"World"); - } - - """, - imports: [], - includes: [] - )) - } - - func test_selecting_a_line_inside_an_interface_the_scope_should_be_the_interface() { - let code = """ - @interface ViewController >: NSObject - - (void)fooWith:(NSInteger)foo; - - (void)fooWith:(NSInteger)foo; - - (void)fooWith:(NSInteger)foo; - @end - """ - let range = CursorRange( - start: CursorPosition(line: 1, character: 0), - end: CursorPosition(line: 3, character: 31) - ) - let context = ObjectiveCFocusedCodeFinder(maxFocusedCodeLineCount: .max).findFocusedCode( - in: document(code: code), - containingRange: range - ) - XCTAssertEqual(context, .init( - scope: .scope(signature: [ - .init( - signature: "@interface ViewController>: NSObject", - name: "ViewController", - range: .init(startPair: (0, 0), endPair: (4, 4)) - ), - ]), - contextRange: .init(startPair: (0, 0), endPair: (4, 4)), - smallestContextRange: range, - focusedRange: range, - focusedCode: """ - - (void)fooWith:(NSInteger)foo; - - (void)fooWith:(NSInteger)foo; - - (void)fooWith:(NSInteger)foo; - - """, - imports: [], - includes: [] - )) - } - - func test_selecting_a_line_inside_an_interface_category_the_scope_should_be_the_interface() { - let code = """ - @interface __GENERICS(NSArray, ObjectType) (BlocksKit) - - (void)fooWith:(NSInteger)foo; - - (void)fooWith:(NSInteger)foo; - - (void)fooWith:(NSInteger)foo; - @end - """ - let range = CursorRange( - start: CursorPosition(line: 1, character: 0), - end: CursorPosition(line: 3, character: 31) - ) - let context = ObjectiveCFocusedCodeFinder(maxFocusedCodeLineCount: .max).findFocusedCode( - in: document(code: code), - containingRange: range - ) - XCTAssertEqual(context, .init( - scope: .scope(signature: [ - .init( - signature: "@interface __GENERICS(NSArray, ObjectType) (BlocksKit)", - name: "NSArray", - range: .init(startPair: (0, 0), endPair: (4, 4)) - ), - ]), - contextRange: .init(startPair: (0, 0), endPair: (4, 4)), - smallestContextRange: range, - focusedRange: range, - focusedCode: """ - - (void)fooWith:(NSInteger)foo; - - (void)fooWith:(NSInteger)foo; - - (void)fooWith:(NSInteger)foo; - - """, - imports: [], - includes: [] - )) - } - - func test_selecting_a_line_inside_a_protocol_the_scope_should_be_the_protocol() { - let code = """ - @protocol Foo - - (void)fooWith:(NSInteger)foo; - - (void)fooWith:(NSInteger)foo; - - (void)fooWith:(NSInteger)foo; - @end - """ - let range = CursorRange( - start: CursorPosition(line: 1, character: 0), - end: CursorPosition(line: 3, character: 31) - ) - let context = ObjectiveCFocusedCodeFinder(maxFocusedCodeLineCount: .max).findFocusedCode( - in: document(code: code), - containingRange: range - ) - XCTAssertEqual(context, .init( - scope: .scope(signature: [ - .init( - signature: "@protocol Foo", - name: "Foo", - range: .init(startPair: (0, 0), endPair: (4, 4)) - ), - ]), - contextRange: .init(startPair: (0, 0), endPair: (4, 4)), - smallestContextRange: range, - focusedRange: range, - focusedCode: """ - - (void)fooWith:(NSInteger)foo; - - (void)fooWith:(NSInteger)foo; - - (void)fooWith:(NSInteger)foo; - - """, - imports: [], - includes: [] - )) - } - - func test_selecting_a_line_inside_a_struct_the_scope_should_be_the_struct() { - let code = """ - struct Foo { - NSInteger foo; - NSInteger bar; - NSInteger baz; - } - """ - let range = CursorRange( - start: CursorPosition(line: 1, character: 0), - end: CursorPosition(line: 3, character: 31) - ) - let context = ObjectiveCFocusedCodeFinder(maxFocusedCodeLineCount: .max).findFocusedCode( - in: document(code: code), - containingRange: range - ) - XCTAssertEqual(context, .init( - scope: .scope(signature: [ - .init( - signature: "struct Foo", - name: "Foo", - range: .init(startPair: (0, 0), endPair: (4, 1)) - ), - ]), - contextRange: .init(startPair: (0, 0), endPair: (4, 1)), - smallestContextRange: range, - focusedRange: range, - focusedCode: """ - NSInteger foo; - NSInteger bar; - NSInteger baz; - - """, - imports: [], - includes: [] - )) - } - - func test_selecting_a_line_inside_a_enum_the_scope_should_be_the_enum() { - let code = """ - enum Foo { - foo, - bar, - baz - }; - """ - let range = CursorRange( - start: CursorPosition(line: 1, character: 0), - end: CursorPosition(line: 3, character: 31) - ) - let context = ObjectiveCFocusedCodeFinder(maxFocusedCodeLineCount: .max).findFocusedCode( - in: document(code: code), - containingRange: range - ) - XCTAssertEqual(context, .init( - scope: .scope(signature: [ - .init( - signature: "enum Foo", - name: "Foo", - range: .init(startPair: (0, 0), endPair: (4, 1)) - ), - ]), - contextRange: .init(startPair: (0, 0), endPair: (4, 1)), - smallestContextRange: range, - focusedRange: range, - focusedCode: """ - foo, - bar, - baz - - """, - imports: [], - includes: [] - )) - } - - func test_selecting_a_line_inside_an_NSEnum_the_scope_should_be_the_enum() { - let code = """ - typedef NS_ENUM(NSInteger, Foo) { - foo, - bar, - baz - }; - """ - let range = CursorRange( - start: CursorPosition(line: 1, character: 0), - end: CursorPosition(line: 3, character: 31) - ) - let context = ObjectiveCFocusedCodeFinder(maxFocusedCodeLineCount: .max).findFocusedCode( - in: document(code: code), - containingRange: range - ) - XCTAssertEqual(context, .init( - scope: .scope(signature: [ - .init( - signature: "typedef NS_ENUM(NSInteger, Foo)", - name: "Foo", - range: .init(startPair: (0, 0), endPair: (4, 2)) - ), - ]), - contextRange: .init(startPair: (0, 0), endPair: (4, 2)), - smallestContextRange: range, - focusedRange: range, - focusedCode: """ - foo, - bar, - baz - - """, - imports: [], - includes: [] - )) - } -} - -final class ObjectiveCFocusedCodeFinder_Focus_Tests: XCTestCase { - func test_get_focused_code_inside_method_the_method_should_be_the_focused_code() { - let code = """ - @implementation Foo - - (void)fooWith:(NSInteger)foo { - NSInteger foo = 0; - NSLog(@"Hello"); - NSLog(@"World"); - } - @end - """ - let range = CursorRange( - start: CursorPosition(line: 2, character: 0), - end: CursorPosition(line: 2, character: 0) - ) - let context = ObjectiveCFocusedCodeFinder(maxFocusedCodeLineCount: .max).findFocusedCode( - in: document(code: code), - containingRange: range - ) - XCTAssertEqual(context, .init( - scope: .scope(signature: [ - .init( - signature: "@implementation Foo", - name: "Foo", - range: .init(startPair: (0, 0), endPair: (6, 4)) - ), - ]), - contextRange: .init(startPair: (0, 0), endPair: (6, 4)), - smallestContextRange: .init(startPair: (1, 0), endPair: (5, 1)), - focusedRange: .init(startPair: (1, 0), endPair: (5, 1)), - focusedCode: """ - - (void)fooWith:(NSInteger)foo { - NSInteger foo = 0; - NSLog(@"Hello"); - NSLog(@"World"); - } - - """, - imports: [], - includes: [] - )) - } - - func test_get_focused_code_inside_an_interface_category_the_focused_code_should_be_the_interface( - ) { - let code = """ - @interface __GENERICS(NSArray, ObjectType) (BlocksKit) - - (void)fooWith:(NSInteger)foo; - - (void)fooWith:(NSInteger)foo; - - (void)fooWith:(NSInteger)foo; - @end - - @implementation Foo - @end - """ - let range = CursorRange( - start: CursorPosition(line: 1, character: 0), - end: CursorPosition(line: 1, character: 0) - ) - let context = ObjectiveCFocusedCodeFinder(maxFocusedCodeLineCount: .max).findFocusedCode( - in: document(code: code), - containingRange: range - ) - XCTAssertEqual(context, .init( - scope: .file, - contextRange: .init(startPair: (0, 0), endPair: (0, 0)), - smallestContextRange: .init(startPair: (0, 0), endPair: (4, 4)), - focusedRange: .init(startPair: (0, 0), endPair: (4, 4)), - focusedCode: """ - @interface __GENERICS(NSArray, ObjectType) (BlocksKit) - - (void)fooWith:(NSInteger)foo; - - (void)fooWith:(NSInteger)foo; - - (void)fooWith:(NSInteger)foo; - @end - - """, - imports: [], - includes: [] - )) - } -} - -final class ObjectiveCFocusedCodeFinder_Imports_Tests: XCTestCase { - func test_parsing_imports() { - let code = """ - #import - @import UIKit; - #import "Foo.h" - #include "Bar.h" - """ - - let context = ObjectiveCFocusedCodeFinder(maxFocusedCodeLineCount: .max).findFocusedCode( - in: document(code: code), - containingRange: .zero - ) - - XCTAssertEqual(context.imports, [ - "", - "UIKit", - "\"Foo.h\"", - ]) - XCTAssertEqual(context.includes, [ - "\"Bar.h\"", - ]) - } -} - diff --git a/Tool/Tests/FocusedCodeFinderTests/SwiftFocusedCodeFinderTests.swift b/Tool/Tests/FocusedCodeFinderTests/SwiftFocusedCodeFinderTests.swift deleted file mode 100644 index 666b614c..00000000 --- a/Tool/Tests/FocusedCodeFinderTests/SwiftFocusedCodeFinderTests.swift +++ /dev/null @@ -1,507 +0,0 @@ -import Foundation -import SuggestionBasic -import XCTest - -@testable import FocusedCodeFinder - -func document(code: String) -> FocusedCodeFinder.Document { - .init( - documentURL: URL(fileURLWithPath: "/"), - content: code, - lines: code.components(separatedBy: "\n").map { "\($0)\n" } - ) -} - -final class SwiftFocusedCodeFinder_Selection_Tests: XCTestCase { - func test_selecting_a_line_inside_the_function_the_scope_should_be_the_function() { - let code = """ - public struct A: B, C { - @ViewBuilder private func f(_ a: String) -> String { - let a = 1 - let b = 2 - let c = 3 - let d = 4 - let e = 5 - } - } - """ - let range = CursorRange( - start: CursorPosition(line: 4, character: 0), - end: CursorPosition(line: 4, character: 13) - ) - let context = SwiftFocusedCodeFinder(maxFocusedCodeLineCount: .max).findFocusedCode( - in: document(code: code), - containingRange: range - ) - XCTAssertEqual(context, .init( - scope: .scope(signature: [ - .init( - signature: "public struct A: B, C", - name: "A", - range: .init(startPair: (0, 0), endPair: (8, 1)) - ), - .init( - signature: "@ViewBuilder private func f(_ a: String) -> String", - name: "f", - range: .init(startPair: (1, 4), endPair: (7, 5)) - ), - ]), - contextRange: .init(startPair: (0, 0), endPair: (8, 1)), - smallestContextRange: .init(startPair: (4, 0), endPair: (4, 13)), - focusedRange: .init(startPair: (4, 0), endPair: (4, 13)), - focusedCode: """ - let c = 3 - - """, - imports: [], - includes: [] - )) - } - - func test_selecting_a_function_inside_a_struct_the_scope_should_be_the_struct() { - let code = """ - @MainActor - public struct A: B, C { - func f() { - let a = 1 - let b = 2 - let c = 3 - let d = 4 - let e = 5 - } - } - """ - let range = CursorRange( - start: CursorPosition(line: 2, character: 0), - end: CursorPosition(line: 7, character: 5) - ) - let context = SwiftFocusedCodeFinder(maxFocusedCodeLineCount: .max).findFocusedCode( - in: document(code: code), - containingRange: range - ) - XCTAssertEqual(context, .init( - scope: .scope(signature: [ - .init( - signature: "@MainActor public struct A: B, C", - name: "A", - range: .init(startPair: (0, 0), endPair: (9, 1)) - ), - ]), - contextRange: .init(startPair: (0, 0), endPair: (9, 1)), - smallestContextRange: .init(startPair: (2, 0), endPair: (7, 5)), - focusedRange: .init(startPair: (2, 0), endPair: (7, 5)), - focusedCode: """ - func f() { - let a = 1 - let b = 2 - let c = 3 - let d = 4 - let e = 5 - - """, - imports: [], - includes: [] - )) - } - - func test_selecting_a_variable_inside_a_class_the_scope_should_be_the_class() { - let code = """ - @MainActor final public class A: P, K { - var a = 1 - var b = 2 - var c = 3 - var d = 4 - var e = 5 - } - """ - let range = CursorRange( - start: CursorPosition(line: 1, character: 0), - end: CursorPosition(line: 1, character: 9) - ) - let context = SwiftFocusedCodeFinder(maxFocusedCodeLineCount: .max).findFocusedCode( - in: document(code: code), - containingRange: range - ) - XCTAssertEqual(context, .init( - scope: .scope(signature: [ - .init( - signature: "@MainActor final public class A: P, K", - name: "A", - range: .init(startPair: (0, 0), endPair: (6, 1)) - ), - ]), - contextRange: .init(startPair: (0, 0), endPair: (6, 1)), - smallestContextRange: .init(startPair: (1, 0), endPair: (1, 9)), - focusedRange: .init(startPair: (1, 0), endPair: (1, 9)), - focusedCode: """ - var a = 1 - - """, - imports: [], - includes: [] - )) - } - - func test_selecting_a_function_inside_a_protocol_the_scope_should_be_the_protocol() { - let code = """ - public protocol A: Hashable { - func f() - func g() - func h() - func i() - func j() - } - """ - let range = CursorRange( - start: CursorPosition(line: 1, character: 0), - end: CursorPosition(line: 1, character: 9) - ) - let context = SwiftFocusedCodeFinder(maxFocusedCodeLineCount: .max).findFocusedCode( - in: document(code: code), - containingRange: range - ) - XCTAssertEqual(context, .init( - scope: .scope(signature: [ - .init( - signature: "public protocol A: Hashable", - name: "A", - range: .init(startPair: (0, 0), endPair: (6, 1)) - ), - ]), - contextRange: .init(startPair: (0, 0), endPair: (6, 1)), - smallestContextRange: .init(startPair: (1, 0), endPair: (1, 9)), - focusedRange: .init(startPair: (1, 0), endPair: (1, 9)), - focusedCode: """ - func f() - - """, - imports: [], - includes: [] - )) - } - - func test_selecting_a_variable_inside_an_extension_the_scope_should_be_the_extension() { - let code = """ - private extension A: Equatable { - var a = 1 - var b = 2 - var c = 3 - var d = 4 - var e = 5 - } - """ - let range = CursorRange( - start: CursorPosition(line: 1, character: 0), - end: CursorPosition(line: 1, character: 9) - ) - let context = SwiftFocusedCodeFinder(maxFocusedCodeLineCount: .max).findFocusedCode( - in: document(code: code), - containingRange: range - ) - XCTAssertEqual(context, .init( - scope: .scope(signature: [ - .init( - signature: "private extension A: Equatable", - name: "A", - range: .init(startPair: (0, 0), endPair: (6, 1)) - ), - ]), - contextRange: .init(startPair: (0, 0), endPair: (6, 1)), - smallestContextRange: .init(startPair: (1, 0), endPair: (1, 9)), - focusedRange: .init(startPair: (1, 0), endPair: (1, 9)), - focusedCode: """ - var a = 1 - - """, - imports: [], - includes: [] - )) - } - - func test_selecting_a_static_function_from_an_actor_the_scope_should_be_the_actor() { - let code = """ - @gloablActor - public actor A { - static func f() {} - static func g() {} - static func h() {} - static func i() {} - static func j() {} - } - """ - let range = CursorRange( - start: CursorPosition(line: 2, character: 0), - end: CursorPosition(line: 2, character: 9) - ) - let context = SwiftFocusedCodeFinder(maxFocusedCodeLineCount: .max).findFocusedCode( - in: document(code: code), - containingRange: range - ) - XCTAssertEqual(context, .init( - scope: .scope(signature: [ - .init( - signature: "@gloablActor public actor A", - name: "A", - range: .init(startPair: (0, 0), endPair: (7, 1)) - ), - ]), - contextRange: .init(startPair: (0, 0), endPair: (7, 1)), - smallestContextRange: .init(startPair: (2, 0), endPair: (2, 9)), - focusedRange: .init(startPair: (2, 0), endPair: (2, 9)), - focusedCode: """ - static func f() {} - - """, - imports: [], - includes: [] - )) - } - - func test_selecting_a_case_inside_an_enum_the_scope_should_be_the_enum() { - let code = """ - @MainActor - public - indirect enum A { - case a - case b - case c - case d - case e - } - """ - let range = CursorRange( - start: CursorPosition(line: 3, character: 0), - end: CursorPosition(line: 3, character: 9) - ) - let context = SwiftFocusedCodeFinder(maxFocusedCodeLineCount: .max).findFocusedCode( - in: document(code: code), - containingRange: range - ) - XCTAssertEqual(context, .init( - scope: .scope(signature: [ - .init( - signature: "@MainActor public indirect enum A", - name: "A", - range: .init(startPair: (0, 0), endPair: (8, 1)) - ), - ]), - contextRange: .init(startPair: (0, 0), endPair: (8, 1)), - smallestContextRange: .init(startPair: (3, 0), endPair: (3, 9)), - focusedRange: .init(startPair: (3, 0), endPair: (3, 9)), - focusedCode: """ - case a - - """, - imports: [], - includes: [] - )) - } - - func test_selecting_a_line_inside_computed_variable_the_scope_should_be_the_variable() { - let code = """ - struct A { - @SomeWrapper public private(set) var a: Int { - let a = 1 - let b = 2 - let c = 3 - let d = 4 - let e = 5 - } - } - """ - let range = CursorRange( - start: CursorPosition(line: 2, character: 0), - end: CursorPosition(line: 2, character: 9) - ) - let context = SwiftFocusedCodeFinder(maxFocusedCodeLineCount: .max).findFocusedCode( - in: document(code: code), - containingRange: range - ) - XCTAssertEqual(context, .init( - scope: .scope(signature: [ - .init( - signature: "struct A", - name: "A", - range: .init(startPair: (0, 0), endPair: (8, 1)) - ), - .init( - signature: "@SomeWrapper public private(set) var a: Int", - name: "a", - range: .init(startPair: (1, 4), endPair: (7, 5)) - ), - ]), - contextRange: .init(startPair: (0, 0), endPair: (8, 1)), - smallestContextRange: .init(startPair: (2, 0), endPair: (2, 9)), - focusedRange: .init(startPair: (2, 0), endPair: (2, 9)), - focusedCode: """ - let a = 1 - - """, - imports: [], - includes: [] - )) - } - - func test_selecting_a_line_in_freestanding_macro_the_scope_should_be_the_macro() { - // TODO: - } -} - -final class SwiftFocusedCodeFinder_FocusedCode_Tests: XCTestCase { - func test_get_focused_code_on_top_level_should_fallback_to_unknown_language() { - let code = """ - @MainActor - public - indirect enum A { - case a - case b - case c - case d - case e - } - - func hello() { - print("hello") - print("hello") - } - """ - let range = CursorRange(startPair: (0, 0), endPair: (0, 0)) - let context = SwiftFocusedCodeFinder(maxFocusedCodeLineCount: 1000).findFocusedCode( - in: document(code: code), - containingRange: range - ) - XCTAssertEqual(context, .init( - scope: .top, - contextRange: .init(startPair: (0, 0), endPair: (13, 2)), - smallestContextRange: .init(startPair: (0, 0), endPair: (13, 2)), - focusedRange: .init(startPair: (0, 0), endPair: (13, 2)), - focusedCode: """ - @MainActor - public - indirect enum A { - case a - case b - case c - case d - case e - } - - func hello() { - print("hello") - print("hello") - } - - """, - imports: [], - includes: [] - )) - } - - func test_get_focused_code_inside_enum_the_whole_enum_will_be_the_focused_code() { - let code = """ - @MainActor - public - indirect enum A { - case a - case b - case c - case d - case e - } - """ - let range = CursorRange(startPair: (3, 0), endPair: (3, 0)) - let context = SwiftFocusedCodeFinder(maxFocusedCodeLineCount: 1000).findFocusedCode( - in: document(code: code), - containingRange: range - ) - XCTAssertEqual(context, .init( - scope: .file, - contextRange: .init(startPair: (0, 0), endPair: (0, 0)), - smallestContextRange: .init(startPair: (0, 0), endPair: (8, 1)), - focusedRange: .init(startPair: (0, 0), endPair: (8, 1)), - focusedCode: """ - @MainActor - public - indirect enum A { - case a - case b - case c - case d - case e - } - - """, - imports: [], - includes: [] - )) - } - - func test_get_focused_code_inside_enum_with_limited_max_line_count() { - let code = """ - @MainActor - public - indirect enum A { - case a - case b - case c - case d - case e - } - """ - let range = CursorRange(startPair: (3, 0), endPair: (3, 0)) - let context = SwiftFocusedCodeFinder(maxFocusedCodeLineCount: 3).findFocusedCode( - in: document(code: code), - containingRange: range - ) - XCTAssertEqual(context, .init( - scope: .file, - contextRange: .init(startPair: (0, 0), endPair: (0, 0)), - smallestContextRange: .init(startPair: (0, 0), endPair: (8, 1)), - focusedRange: .init(startPair: (2, 0), endPair: (4, 11)), - focusedCode: """ - indirect enum A { - case a - case b - - """, - imports: [], - includes: [] - )) - } -} - -final class SwiftFocusedCodeFinder_Import_Tests: XCTestCase { - func test_parsing_imports() { - let code = """ - import OnTop - import Second - import Third - - struct Foo { - - } - - import BelowStructFoo - - class Bar { - - } - - import BelowClassBar - """ - - let range = CursorRange.zero - let context = SwiftFocusedCodeFinder(maxFocusedCodeLineCount: 3).findFocusedCode( - in: document(code: code), - containingRange: range - ) - XCTAssertEqual(context.imports, [ - "OnTop", - "Second", - "Third", - "BelowStructFoo", - "BelowClassBar", - ]) - } -} - diff --git a/Tool/Tests/FocusedCodeFinderTests/UnknownLanguageFocusedCodeFinderTests.swift b/Tool/Tests/FocusedCodeFinderTests/UnknownLanguageFocusedCodeFinderTests.swift deleted file mode 100644 index fa975db9..00000000 --- a/Tool/Tests/FocusedCodeFinderTests/UnknownLanguageFocusedCodeFinderTests.swift +++ /dev/null @@ -1,98 +0,0 @@ -import Foundation -import SuggestionBasic -import XCTest - -@testable import FocusedCodeFinder - -class UnknownLanguageFocusedCodeFinderTests: XCTestCase { - func test_the_code_is_long_enough_for_the_search_range() { - let code = stride(from: 0, through: 100, by: 1).map { "\($0)\n" }.joined() - let context = UnknownLanguageFocusedCodeFinder(proposedSearchRange: 5) - .findFocusedCode( - in: document(code: code), - containingRange: .init(startPair: (50, 0), endPair: (50, 0)) - ) - XCTAssertEqual(context, .init( - scope: .top, - contextRange: .init(startPair: (40, 0), endPair: (60, 3)), - smallestContextRange: .init(startPair: (40, 0), endPair: (60, 3)), - focusedRange: .init(startPair: (45, 0), endPair: (55, 3)), - focusedCode: stride(from: 45, through: 55, by: 1).map { "\($0)\n" }.joined(), - imports: [], - includes: [] - )) - } - - func test_the_upper_side_is_not_long_enough_expand_the_lower_end() { - let code = stride(from: 0, through: 100, by: 1).map { "\($0)\n" }.joined() - let context = UnknownLanguageFocusedCodeFinder(proposedSearchRange: 5) - .findFocusedCode( - in: document(code: code), - containingRange: .init(startPair: (2, 0), endPair: (2, 0)) - ) - XCTAssertEqual(context, .init( - scope: .top, - contextRange: .init(startPair: (0, 0), endPair: (15, 3)), - smallestContextRange: .init(startPair: (0, 0), endPair: (15, 3)), - focusedRange: .init(startPair: (0, 0), endPair: (10, 3)), - focusedCode: stride(from: 0, through: 10, by: 1).map { "\($0)\n" }.joined(), - imports: [], - includes: [] - )) - } - - func test_the_lower_side_is_not_long_enough_do_not_expand_the_upper_end() { - let code = stride(from: 0, through: 100, by: 1).map { "\($0)\n" }.joined() - let context = UnknownLanguageFocusedCodeFinder(proposedSearchRange: 5) - .findFocusedCode( - in: document(code: code), - containingRange: .init(startPair: (99, 0), endPair: (99, 0)) - ) - XCTAssertEqual(context, .init( - scope: .top, - contextRange: .init(startPair: (89, 0), endPair: (101, 1)), - smallestContextRange: .init(startPair: (89, 0), endPair: (101, 1)), - focusedRange: .init(startPair: (94, 0), endPair: (101, 1)), - focusedCode: stride(from: 94, through: 100, by: 1).map { "\($0)\n" }.joined() + "\n", - imports: [], - includes: [] - )) - } - - func test_both_sides_are_just_long_enough() { - let code = stride(from: 0, through: 10, by: 1).map { "\($0)\n" }.joined() - let context = UnknownLanguageFocusedCodeFinder(proposedSearchRange: 5) - .findFocusedCode( - in: document(code: code), - containingRange: .init(startPair: (5, 0), endPair: (5, 0)) - ) - XCTAssertEqual(context, .init( - scope: .top, - contextRange: .init(startPair: (0, 0), endPair: (11, 1)), - smallestContextRange: .init(startPair: (0, 0), endPair: (11, 1)), - focusedRange: .init(startPair: (0, 0), endPair: (10, 3)), - focusedCode: code, - imports: [], - includes: [] - )) - } - - func test_both_sides_are_not_long_enough() { - let code = stride(from: 0, through: 4, by: 1).map { "\($0)\n" }.joined() - let context = UnknownLanguageFocusedCodeFinder(proposedSearchRange: 5) - .findFocusedCode( - in: document(code: code), - containingRange: .init(startPair: (3, 0), endPair: (3, 0)) - ) - XCTAssertEqual(context, .init( - scope: .top, - contextRange: .init(startPair: (0, 0), endPair: (5, 1)), - smallestContextRange: .init(startPair: (0, 0), endPair: (5, 1)), - focusedRange: .init(startPair: (0, 0), endPair: (5, 1)), - focusedCode: code + "\n", - imports: [], - includes: [] - )) - } -} - diff --git a/Tool/Tests/GitHubCopilotServiceTests/FetchSuggestionsTests.swift b/Tool/Tests/GitHubCopilotServiceTests/FetchSuggestionsTests.swift deleted file mode 100644 index d6cdcbff..00000000 --- a/Tool/Tests/GitHubCopilotServiceTests/FetchSuggestionsTests.swift +++ /dev/null @@ -1,125 +0,0 @@ -import CopilotForXcodeKit -import LanguageServerProtocol -import XCTest - -@testable import Workspace -@testable import GitHubCopilotService - -struct TestServiceLocator: ServiceLocatorType { - let server: GitHubCopilotLSP - func getService(from workspace: WorkspaceInfo) async -> GitHubCopilotService? { - .init(designatedServer: server) - } -} - -final class FetchSuggestionTests: XCTestCase { - func test_process_suggestions_from_server() async throws { - struct TestServer: GitHubCopilotLSP { - func sendNotification(_: LanguageServerProtocol.ClientNotification) async throws { - throw CancellationError() - } - - func sendRequest(_: E) async throws -> E.Response where E: GitHubCopilotRequestType { - return GitHubCopilotRequest.InlineCompletion.Response(items: [ - .init( - insertText: "Hello World\n", - filterText: nil, - range: .init(start: .init((0, 0)), end: .init((0, 4))), - command: nil - ), - .init( - insertText: " ", - filterText: nil, - range: .init(start: .init((0, 0)), end: .init((0, 1))), - command: nil - ), - .init( - insertText: " \n", - filterText: nil, - range: .init(start: .init((0, 0)), end: .init((0, 2))), - command: nil - ), - ]) as! E.Response - } - func sendRequest(_: E, timeout: TimeInterval) async throws -> E.Response where E: GitHubCopilotRequestType { - return GitHubCopilotRequest.InlineCompletion.Response(items: []) as! E.Response - } - var eventSequence: ServerConnection.EventSequence { - let result = ServerConnection.EventSequence.makeStream() - result.continuation.finish() - return result.stream - } - } - let service = GitHubCopilotSuggestionService(serviceLocator: TestServiceLocator(server: TestServer())) - let completions = try await service.getSuggestions( - .init( - fileURL: .init(fileURLWithPath: "/file.swift"), - relativePath: "", - language: .builtIn(.swift), - content: "", - originalContent: "", - cursorPosition: .outOfScope, - tabSize: 4, - indentSize: 4, - usesTabsForIndentation: false, - relevantCodeSnippets: [] - ), - workspace: .init( - workspaceURL: .init(fileURLWithPath: "/"), - projectURL: .init(fileURLWithPath: "/file.swift") - ) - ) - XCTAssertEqual(completions.count, 3) - } - - func test_if_language_identifier_is_unknown_returns_correctly() async throws { - class TestServer: GitHubCopilotLSP { - func sendNotification(_: LanguageServerProtocol.ClientNotification) async throws { - // unimplemented - } - - func sendRequest(_: E) async throws -> E.Response where E: GitHubCopilotRequestType { - return GitHubCopilotRequest.InlineCompletion.Response(items: [ - .init( - insertText: "Hello World\n", - filterText: nil, - range: .init(start: .init((0, 0)), end: .init((0, 4))), - command: nil - ), - ]) as! E.Response - } - - func sendRequest(_ endpoint: E, timeout: TimeInterval) async throws -> E.Response where E : GitHubCopilotRequestType { - return GitHubCopilotRequest.InlineCompletion.Response(items: []) as! E.Response - } - var eventSequence: ServerConnection.EventSequence { - let result = ServerConnection.EventSequence.makeStream() - result.continuation.finish() - return result.stream - } - } - let testServer = TestServer() - let service = GitHubCopilotSuggestionService(serviceLocator: TestServiceLocator(server: testServer)) - let completions = try await service.getSuggestions( - .init( - fileURL: .init(fileURLWithPath: "/"), - relativePath: "", - language: .builtIn(.swift), - content: "", - originalContent: "", - cursorPosition: .outOfScope, - tabSize: 4, - indentSize: 4, - usesTabsForIndentation: false, - relevantCodeSnippets: [] - ), - workspace: .init( - workspaceURL: .init(fileURLWithPath: "/"), - projectURL: .init(fileURLWithPath: "/file.swift") - ) - ) - XCTAssertEqual(completions.count, 1) - XCTAssertEqual(completions.first?.text, "Hello World\n") - } -} - diff --git a/Tool/Tests/GitHubCopilotServiceTests/FileExtensionToLanguageIdentifierTests.swift b/Tool/Tests/GitHubCopilotServiceTests/FileExtensionToLanguageIdentifierTests.swift deleted file mode 100644 index 1054e452..00000000 --- a/Tool/Tests/GitHubCopilotServiceTests/FileExtensionToLanguageIdentifierTests.swift +++ /dev/null @@ -1,21 +0,0 @@ -import LanguageServerProtocol -import XCTest - -@testable import GitHubCopilotService - -final class FileExtensionToLanguageIdentifierTests: XCTestCase { - func test_no_conflicts_in_map() { - var dict = [String: [String]]() - for languageId in LanguageIdentifier.allCases { - for e in languageId.fileExtensions { - if dict[e] == nil { - dict[e] = [] - } - dict[e]?.append(languageId.rawValue) - } - } - - let confilicts = dict.filter { $0.value.count > 1 } - XCTAssertEqual(confilicts, [:]) - } -} diff --git a/Tool/Tests/SharedUIComponentsTests/ConvertToCodeLinesTests.swift b/Tool/Tests/SharedUIComponentsTests/ConvertToCodeLinesTests.swift deleted file mode 100644 index 7ad54127..00000000 --- a/Tool/Tests/SharedUIComponentsTests/ConvertToCodeLinesTests.swift +++ /dev/null @@ -1,159 +0,0 @@ -import XCTest - -@testable import SharedUIComponents - -final class ConvertToCodeLinesTests: XCTestCase { - func test_do_not_remove_common_leading_spaces() async throws { - let code = """ - struct Cat { - } - """ - let (result, spaceCount) = CodeHighlighting.highlighted( - code: code, - language: "swift", - scenario: "a", - brightMode: true, - droppingLeadingSpaces: false, - font: .systemFont(ofSize: 14) - ) - - XCTAssertEqual(spaceCount, 0) - print(code.replacingOccurrences(of: " ", with: "·")) - XCTAssertEqual(result.map(\.string), [ - " struct Cat {", - " }", - ]) - } - - func test_wont_remove_common_leading_spaces_2_spaces() async throws { - let code = """ - struct Cat { - } - """ - let (result, spaceCount) = CodeHighlighting.highlighted( - code: code, - language: "md", - scenario: "a", - brightMode: true, - droppingLeadingSpaces: true, - font: .systemFont(ofSize: 14) - ) - - XCTAssertEqual(spaceCount, 0) - XCTAssertEqual(result.map(\.string), [ - " struct Cat {", - " }", - ]) - } - - func test_remove_common_leading_spaces_4_spaces() async throws { - let code = """ - struct Cat { - } - """ - let (result, spaceCount) = CodeHighlighting.highlighted( - code: code, - language: "md", - scenario: "a", - brightMode: true, - droppingLeadingSpaces: true, - font: .systemFont(ofSize: 14) - ) - - XCTAssertEqual(spaceCount, 4) - XCTAssertEqual(result.map(\.string), [ - "struct Cat {", - "}", - ]) - } - - func test_remove_common_leading_spaces_8_spaces() async throws { - let code = """ - struct Cat { - } - """ - let (result, spaceCount) = CodeHighlighting.highlighted( - code: code, - language: "md", - scenario: "a", - brightMode: true, - droppingLeadingSpaces: true, - font: .systemFont(ofSize: 14) - ) - - XCTAssertEqual(spaceCount, 8) - XCTAssertEqual(result.map(\.string), [ - "struct Cat {", - "}", - ]) - } - - func test_remove_common_leading_spaces_one_line_is_empty() async throws { - let code = """ - struct Cat { - - } - """ - let (result, spaceCount) = CodeHighlighting.highlighted( - code: code, - language: "md", - scenario: "a", - brightMode: true, - droppingLeadingSpaces: true, - font: .systemFont(ofSize: 14) - ) - - XCTAssertEqual(spaceCount, 4) - XCTAssertEqual(result.map(\.string), [ - "struct Cat {", - "", - "}", - ]) - } - - func test_remove_common_leading_spaces_one_line_has_no_leading_spaces() async throws { - let code = """ - struct Cat { - // - } - """ - let (result, spaceCount) = CodeHighlighting.highlighted( - code: code, - language: "md", - scenario: "a", - brightMode: true, - droppingLeadingSpaces: true, - font: .systemFont(ofSize: 14) - ) - - XCTAssertEqual(spaceCount, 0) - XCTAssertEqual(result.map(\.string), [ - " struct Cat {", - "//", - " }", - ]) - } - - func test_remove_common_leading_spaces_one_line_has_fewer_leading_spaces() async throws { - let code = """ - struct Cat { - // - } - """ - let (result, spaceCount) = CodeHighlighting.highlighted( - code: code, - language: "md", - scenario: "a", - brightMode: true, - droppingLeadingSpaces: true, - font: .systemFont(ofSize: 14) - ) - - XCTAssertEqual(spaceCount, 4) - XCTAssertEqual(result.map(\.string), [ - " struct Cat {", - "//", - " }", - ]) - } -} diff --git a/Tool/Tests/SuggestionBasicTests/BreakLinePerformanceTests.swift b/Tool/Tests/SuggestionBasicTests/BreakLinePerformanceTests.swift deleted file mode 100644 index 6f2ff5a7..00000000 --- a/Tool/Tests/SuggestionBasicTests/BreakLinePerformanceTests.swift +++ /dev/null @@ -1,18 +0,0 @@ -import Foundation -import XCTest -@testable import SuggestionBasic - -final class BreakLinePerformanceTests: XCTestCase { - func test_breakLines() { - let string = String(repeating: """ - Hello - World - - """, count: 50000) - - measure { - let _ = string.breakLines() - } - } -} - diff --git a/Tool/Tests/SuggestionBasicTests/LineAnnotationParsingTests.swift b/Tool/Tests/SuggestionBasicTests/LineAnnotationParsingTests.swift deleted file mode 100644 index 8ff71f9c..00000000 --- a/Tool/Tests/SuggestionBasicTests/LineAnnotationParsingTests.swift +++ /dev/null @@ -1,14 +0,0 @@ -import Foundation -import XCTest - -@testable import SuggestionBasic - -class LineAnnotationParsingTests: XCTestCase { - func test_parse_line_annotation() { - let annotation = "Error Line 25: FileName.swift:25 Cannot convert Type" - let parsed = EditorInformation.parseLineAnnotation(annotation) - XCTAssertEqual(parsed.type, "Error") - XCTAssertEqual(parsed.line, 25) - XCTAssertEqual(parsed.message, "Cannot convert Type") - } -} diff --git a/Tool/Tests/SuggestionBasicTests/ModificationTests.swift b/Tool/Tests/SuggestionBasicTests/ModificationTests.swift deleted file mode 100644 index 0de61490..00000000 --- a/Tool/Tests/SuggestionBasicTests/ModificationTests.swift +++ /dev/null @@ -1,37 +0,0 @@ -import XCTest - -@testable import SuggestionBasic - -final class ModificationTests: XCTestCase { - func test_nsmutablearray_deleting_an_element() { - let a = NSMutableArray(array: ["a", "b", "c"]) - a.apply([.deleted(0...0)]) - XCTAssertEqual(a as! [String], ["b", "c"]) - } - - func test_nsmutablearray_deleting_all_element() { - let a = NSMutableArray(array: ["a", "b", "c"]) - a.apply([.deleted(0...2)]) - XCTAssertEqual(a as! [String], []) - } - - func test_nsmutablearray_deleting_too_much_element() { - let a = NSMutableArray(array: ["a", "b", "c"]) - a.apply([.deleted(0...100)]) - XCTAssertEqual(a as! [String], []) - } - - func test_nsmutablearray_inserting_elements() { - let a = NSMutableArray(array: ["a", "b", "c"]) - a.apply([.inserted(0, ["y", "z"])]) - XCTAssertEqual(a as! [String], ["y", "z", "a", "b", "c"]) - a.apply([.inserted(1, ["0", "1"])]) - XCTAssertEqual(a as! [String], ["y", "0", "1", "z", "a", "b", "c"]) - } - - func test_nsmutablearray_inserting_elements_at_index_out_of_range() { - let a = NSMutableArray(array: ["a", "b", "c"]) - a.apply([.inserted(1000, ["z"])]) - XCTAssertEqual(a as! [String], ["a", "b", "c", "z"]) - } -} diff --git a/Tool/Tests/SuggestionBasicTests/TextExtrationFromCodeTests.swift b/Tool/Tests/SuggestionBasicTests/TextExtrationFromCodeTests.swift deleted file mode 100644 index 7b1fa007..00000000 --- a/Tool/Tests/SuggestionBasicTests/TextExtrationFromCodeTests.swift +++ /dev/null @@ -1,157 +0,0 @@ -import Foundation -import XCTest -@testable import SuggestionBasic - -final class TextExtrationFromCodeTests: XCTestCase { - func test_empty_selection() { - let selection = CursorRange( - start: CursorPosition(line: 0, character: 0), - end: CursorPosition(line: 0, character: 0) - ) - let lines = ["let foo = 1\n", "let bar = 2\n"] - let result = EditorInformation.code( - in: lines, - inside: selection, - ignoreColumns: false - ) - XCTAssertEqual(result.code, "") - XCTAssertEqual(result.lines, ["let foo = 1\n"]) - } - - func test_single_line_selection() { - let selection = CursorRange( - start: CursorPosition(line: 0, character: 4), - end: CursorPosition(line: 0, character: 10) - ) - let lines = ["let foo = 1\n", "let bar = 2\n"] - let result = EditorInformation.code( - in: lines, - inside: selection, - ignoreColumns: false - ) - XCTAssertEqual(result.code, "foo = ") - XCTAssertEqual(result.lines, ["let foo = 1\n"]) - } - - func test_single_line_selection_with_emoji() { - let selection = CursorRange( - start: CursorPosition(line: 0, character: 4), - end: CursorPosition(line: 0, character: 10) - ) - let lines = ["let 🎆🎆o = 1\n", "let bar = 2\n"] - let result = EditorInformation.code( - in: lines, - inside: selection, - ignoreColumns: false - ) - XCTAssertEqual(result.code, "🎆🎆o ") - XCTAssertEqual(result.lines, ["let 🎆🎆o = 1\n"]) - } - - func test_single_line_selection_cutting_emoji() { - // undefined behavior - - let selection = CursorRange( - start: CursorPosition(line: 0, character: 5), - end: CursorPosition(line: 0, character: 10) - ) - let lines = ["let 🎆🎆o = 1\n", "let bar = 2\n"] - let result = EditorInformation.code( - in: lines, - inside: selection, - ignoreColumns: false - ) - XCTAssertEqual(result.lines, ["let 🎆🎆o = 1\n"]) - } - - func test_single_line_selection_at_line_end() { - let selection = CursorRange( - start: CursorPosition(line: 0, character: 8), - end: CursorPosition(line: 0, character: 11) - ) - let lines = ["let foo = 1\n", "let bar = 2\n"] - let result = EditorInformation.code( - in: lines, - inside: selection, - ignoreColumns: false - ) - XCTAssertEqual(result.code, "= 1") - XCTAssertEqual(result.lines, ["let foo = 1\n"]) - } - - func test_multi_line_selection() { - let selection = CursorRange( - start: CursorPosition(line: 0, character: 4), - end: CursorPosition(line: 1, character: 11) - ) - let lines = ["let foo = 1\n", "let bar = 2\n", "let baz = 3\n"] - let result = EditorInformation.code( - in: lines, - inside: selection, - ignoreColumns: false - ) - XCTAssertEqual(result.code, "foo = 1\nlet bar = 2") - XCTAssertEqual(result.lines, ["let foo = 1\n", "let bar = 2\n"]) - } - - func test_multi_line_selection_with_emoji() { - let selection = CursorRange( - start: CursorPosition(line: 0, character: 4), - end: CursorPosition(line: 1, character: 11) - ) - let lines = ["🎆🎆 foo = 1\n", "let bar = 2\n", "let baz = 3\n"] - let result = EditorInformation.code( - in: lines, - inside: selection, - ignoreColumns: false - ) - XCTAssertEqual(result.code, " foo = 1\nlet bar = 2") - XCTAssertEqual(result.lines, ["🎆🎆 foo = 1\n", "let bar = 2\n"]) - } - - func test_invalid_selection() { - let selection = CursorRange( - start: CursorPosition(line: 1, character: 4), - end: CursorPosition(line: 0, character: 10) - ) - let lines = ["let foo = 1", "let bar = 2"] - let result = EditorInformation.code( - in: lines, - inside: selection, - ignoreColumns: false - ) - XCTAssertEqual(result.code, "") - XCTAssertEqual(result.lines, []) - } - - func test_single_line_selection_ignoring_column() { - let selection = CursorRange( - start: CursorPosition(line: 0, character: 4), - end: CursorPosition(line: 0, character: 10) - ) - let lines = ["let foo = 1\n", "let bar = 2\n"] - let result = EditorInformation.code( - in: lines, - inside: selection, - ignoreColumns: true - ) - XCTAssertEqual(result.code, "let foo = 1\n") - XCTAssertEqual(result.lines, ["let foo = 1\n"]) - } - - func test_multi_line_selection_ignoring_column() { - let selection = CursorRange( - start: CursorPosition(line: 0, character: 4), - end: CursorPosition(line: 1, character: 11) - ) - let lines = ["let foo = 1\n", "let bar = 2\n", "let baz = 3\n"] - let result = EditorInformation.code( - in: lines, - inside: selection, - ignoreColumns: true - ) - XCTAssertEqual(result.code, "let foo = 1\nlet bar = 2\n") - XCTAssertEqual(result.lines, ["let foo = 1\n", "let bar = 2\n"]) - } -} - diff --git a/Tool/Tests/SuggestionProviderTests/PostProcessingSuggestionServiceMiddlewareTests.swift b/Tool/Tests/SuggestionProviderTests/PostProcessingSuggestionServiceMiddlewareTests.swift deleted file mode 100644 index ac389951..00000000 --- a/Tool/Tests/SuggestionProviderTests/PostProcessingSuggestionServiceMiddlewareTests.swift +++ /dev/null @@ -1,188 +0,0 @@ -import Foundation -import SuggestionBasic -import XCTest - -@testable import SuggestionProvider - -class PostProcessingSuggestionServiceMiddlewareTests: XCTestCase { - func createRequest( - _ code: String = "", - _ cursorPosition: CursorPosition = .zero - ) -> SuggestionRequest { - let lines = code.breakLines() - return SuggestionRequest( - fileURL: URL(fileURLWithPath: "/path/to/file.swift"), - relativePath: "file.swift", - content: code, - originalContent: code, - lines: lines, - cursorPosition: cursorPosition, - cursorOffset: { - if cursorPosition == .outOfScope { return 0 } - let prefixLines = if cursorPosition.line > 0 { - lines[0.. - } - let offset = prefixLines.reduce(0) { $0 + $1.utf8.count } - return offset - + lines[cursorPosition.line].prefix(cursorPosition.character).utf8.count - }(), - tabSize: 4, - indentSize: 4, - usesTabsForIndentation: false, - relevantCodeSnippets: [] - ) - } - - func test_trailing_whitespaces_and_new_lines_should_be_removed() async throws { - let middleware = PostProcessingSuggestionServiceMiddleware() - - let handler: PostProcessingSuggestionServiceMiddleware.Next = { _ in - [ - .init( - id: "1", - text: "hello world \n \n", - position: .init(line: 0, character: 0), - range: .init(startPair: (0, 0), endPair: (0, 0)) - ), - .init( - id: "2", - text: " \n hello world \n \n", - position: .init(line: 0, character: 0), - range: .init(startPair: (0, 0), endPair: (0, 0)) - ), - ] - } - - let suggestions = try await middleware.getSuggestion( - createRequest(), - configuration: .init( - acceptsRelevantCodeSnippets: true, - mixRelevantCodeSnippetsInSource: true, - acceptsRelevantSnippetsFromOpenedFiles: true - ), - next: handler - ) - - XCTAssertEqual(suggestions, [ - .init( - id: "1", - text: "hello world", - position: .init(line: 0, character: 0), - range: .init(startPair: (0, 0), endPair: (0, 0)) - ), - .init( - id: "2", - text: " \n hello world", - position: .init(line: 0, character: 0), - range: .init(startPair: (0, 0), endPair: (0, 0)) - ), - ]) - } - - func test_remove_suggestions_that_contains_only_whitespaces_and_new_lines() async throws { - let middleware = PostProcessingSuggestionServiceMiddleware() - - let handler: PostProcessingSuggestionServiceMiddleware.Next = { _ in - [ - .init( - id: "1", - text: "hello world \n \n", - position: .init(line: 0, character: 0), - range: .init(startPair: (0, 0), endPair: (0, 0)) - ), - .init( - id: "2", - text: " \n\n\r", - position: .init(line: 0, character: 0), - range: .init(startPair: (0, 0), endPair: (0, 0)) - ), - .init( - id: "3", - text: " ", - position: .init(line: 0, character: 0), - range: .init(startPair: (0, 0), endPair: (0, 0)) - ), - .init( - id: "4", - text: "\n\n\n", - position: .init(line: 0, character: 0), - range: .init(startPair: (0, 0), endPair: (0, 0)) - ), - ] - } - - let suggestions = try await middleware.getSuggestion( - createRequest(), - configuration: .init( - acceptsRelevantCodeSnippets: true, - mixRelevantCodeSnippetsInSource: true, - acceptsRelevantSnippetsFromOpenedFiles: true - ), - next: handler - ) - - XCTAssertEqual(suggestions, [ - .init( - id: "1", - text: "hello world", - position: .init(line: 0, character: 0), - range: .init(startPair: (0, 0), endPair: (0, 0)) - ), - ]) - } - - func test_remove_suggestion_that_takes_no_effect_after_being_accepted() async throws { - let middleware = PostProcessingSuggestionServiceMiddleware() - - let handler: PostProcessingSuggestionServiceMiddleware.Next = { _ in - [ - .init( - id: "1", - text: "hello world \n \n", - position: .init(line: 0, character: 0), - range: .init(startPair: (0, 0), endPair: (0, 0)) - ), - .init( - id: "2", - text: "let cat = 100", - position: .init(line: 0, character: 13), - range: .init(startPair: (0, 0), endPair: (0, 13)) - ), - .init( - id: "3", - text: "let cat = 10", - position: .init(line: 0, character: 13), - range: .init(startPair: (0, 0), endPair: (0, 13)) - ), - ] - } - - let suggestions = try await middleware.getSuggestion( - createRequest("let cat = 100", .init(line: 0, character: 3)), - configuration: .init( - acceptsRelevantCodeSnippets: true, - mixRelevantCodeSnippetsInSource: true, - acceptsRelevantSnippetsFromOpenedFiles: true - ), - next: handler - ) - - XCTAssertEqual(suggestions, [ - .init( - id: "1", - text: "hello world", - position: .init(line: 0, character: 0), - range: .init(startPair: (0, 0), endPair: (0, 0)) - ), - .init( - id: "3", - text: "let cat = 10", - position: .init(line: 0, character: 13), - range: .init(startPair: (0, 0), endPair: (0, 13)) - ), - ]) - } -} - diff --git a/Tool/Tests/WorkspaceTests/FileChangeWatcherTests.swift b/Tool/Tests/WorkspaceTests/FileChangeWatcherTests.swift deleted file mode 100644 index 02d35acd..00000000 --- a/Tool/Tests/WorkspaceTests/FileChangeWatcherTests.swift +++ /dev/null @@ -1,385 +0,0 @@ -import ConversationServiceProvider -import CoreServices -import Foundation -import LanguageServerProtocol -@testable import Workspace -import XCTest - -// MARK: - Mocks for Testing - -class MockFSEventProvider: FSEventProvider { - var createdStream: FSEventStreamRef? - var didStartStream = false - var didStopStream = false - var didInvalidateStream = false - var didReleaseStream = false - var didSetDispatchQueue = false - var registeredCallback: FSEventStreamCallback? - var registeredContext: UnsafeMutablePointer? - - var simulatedFiles: [String] = [] - - func createEventStream( - paths: CFArray, - latency: CFTimeInterval, - flags: UInt32, - callback: @escaping FSEventStreamCallback, - context: UnsafeMutablePointer - ) -> FSEventStreamRef? { - registeredCallback = callback - registeredContext = context - let stream = unsafeBitCast(1, to: FSEventStreamRef.self) - createdStream = stream - return stream - } - - func startStream(_ stream: FSEventStreamRef) { - didStartStream = true - } - - func stopStream(_ stream: FSEventStreamRef) { - didStopStream = true - } - - func invalidateStream(_ stream: FSEventStreamRef) { - didInvalidateStream = true - } - - func releaseStream(_ stream: FSEventStreamRef) { - didReleaseStream = true - } - - func setDispatchQueue(_ stream: FSEventStreamRef, queue: DispatchQueue) { - didSetDispatchQueue = true - } -} - -class MockWorkspaceFileProvider: WorkspaceFileProvider { - var subprojects: [URL] = [] - var filesInWorkspace: [FileReference] = [] - var xcProjectPaths: Set = [] - var xcWorkspacePaths: Set = [] - - func getProjects(by workspaceURL: URL) -> [URL] { - return subprojects - } - - func getFilesInActiveWorkspace(workspaceURL: URL, workspaceRootURL: URL) -> [FileReference] { - return filesInWorkspace - } - - func isXCProject(_ url: URL) -> Bool { - return xcProjectPaths.contains(url.path) - } - - func isXCWorkspace(_ url: URL) -> Bool { - return xcWorkspacePaths.contains(url.path) - } - - func fileExists(atPath: String) -> Bool { - return true - } -} - -class MockFileWatcher: FileWatcherProtocol { - var fileURL: URL - var dispatchQueue: DispatchQueue? - var onFileModified: (() -> Void)? - var onFileDeleted: (() -> Void)? - var onFileRenamed: (() -> Void)? - - static var watchers = [URL: MockFileWatcher]() - - init(fileURL: URL, dispatchQueue: DispatchQueue? = nil, onFileModified: (() -> Void)? = nil, onFileDeleted: (() -> Void)? = nil, onFileRenamed: (() -> Void)? = nil) { - self.fileURL = fileURL - self.dispatchQueue = dispatchQueue - self.onFileModified = onFileModified - self.onFileDeleted = onFileDeleted - self.onFileRenamed = onFileRenamed - MockFileWatcher.watchers[fileURL] = self - } - - func startWatching() -> Bool { - return true - } - - func stopWatching() { - MockFileWatcher.watchers[fileURL] = nil - } - - static func triggerFileDelete(for fileURL: URL) { - guard let watcher = watchers[fileURL] else { return } - watcher.onFileDeleted?() - } -} - -class MockFileWatcherFactory: FileWatcherFactory { - func createFileWatcher(fileURL: URL, dispatchQueue: DispatchQueue?, onFileModified: (() -> Void)?, onFileDeleted: (() -> Void)?, onFileRenamed: (() -> Void)?) -> FileWatcherProtocol { - return MockFileWatcher(fileURL: fileURL, dispatchQueue: dispatchQueue, onFileModified: onFileModified, onFileDeleted: onFileDeleted, onFileRenamed: onFileRenamed) - } - - func createDirectoryWatcher(watchedPaths: [URL], changePublisher: @escaping PublisherType, publishInterval: TimeInterval) -> DirectoryWatcherProtocol { - return BatchingFileChangeWatcher( - watchedPaths: watchedPaths, - changePublisher: changePublisher, - fsEventProvider: MockFSEventProvider() - ) - } -} - -// MARK: - Tests for BatchingFileChangeWatcher - -final class BatchingFileChangeWatcherTests: XCTestCase { - var mockFSEventProvider: MockFSEventProvider! - var publishedEvents: [[FileEvent]] = [] - - override func setUp() { - super.setUp() - mockFSEventProvider = MockFSEventProvider() - publishedEvents = [] - } - - func createWatcher(projectURL: URL = URL(fileURLWithPath: "/test/project")) -> BatchingFileChangeWatcher { - return BatchingFileChangeWatcher( - watchedPaths: [projectURL], - changePublisher: { [weak self] events in - self?.publishedEvents.append(events) - }, - publishInterval: 0.1, - fsEventProvider: mockFSEventProvider - ) - } - - func testInitSetsUpTimerAndFileWatching() { - let _ = createWatcher() - - XCTAssertNotNil(mockFSEventProvider.createdStream) - XCTAssertTrue(mockFSEventProvider.didStartStream) - } - - func testDeinitCleansUpResources() { - var watcher: BatchingFileChangeWatcher? = createWatcher() - weak var weakWatcher = watcher - - watcher = nil - - // Wait for the watcher to be deallocated - let startTime = Date() - let timeout: TimeInterval = 1.0 - - while weakWatcher != nil && Date().timeIntervalSince(startTime) < timeout { - RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.01)) - } - - XCTAssertTrue(mockFSEventProvider.didStopStream) - XCTAssertTrue(mockFSEventProvider.didInvalidateStream) - XCTAssertTrue(mockFSEventProvider.didReleaseStream) - } - - func testAddingEventsAndPublishing() { - let watcher = createWatcher() - let fileURL = URL(fileURLWithPath: "/test/project/file.swift") - - watcher.onFileCreated(file: fileURL) - - // No events should be published yet - XCTAssertTrue(publishedEvents.isEmpty) - - XCTAssertTrue(waitForPublishedEvents(), "No events were published within timeout") - - // Only verify array contents if we have events - guard !publishedEvents.isEmpty else { return } - - XCTAssertEqual(publishedEvents[0].count, 1) - XCTAssertEqual(publishedEvents[0][0].uri, fileURL.absoluteString) - XCTAssertEqual(publishedEvents[0][0].type, .created) - } - - func testProcessingFSEvents() { - let watcher = createWatcher() - let fileURL = URL(fileURLWithPath: "/test/project/file.swift") - - // Test file creation - directly call methods instead of simulating FS events - watcher.onFileCreated(file: fileURL) - XCTAssertTrue(waitForPublishedEvents(), "No events were published within timeout") - - guard !publishedEvents.isEmpty else { return } - XCTAssertEqual(publishedEvents[0].count, 1) - XCTAssertEqual(publishedEvents[0][0].type, .created) - - // Test file modification - publishedEvents = [] - watcher.onFileChanged(file: fileURL) - - XCTAssertTrue(waitForPublishedEvents(), "No events were published within timeout") - - guard !publishedEvents.isEmpty else { return } - XCTAssertEqual(publishedEvents[0].count, 1) - XCTAssertEqual(publishedEvents[0][0].type, .changed) - - // Test file deletion - publishedEvents = [] - watcher.onFileDeleted(file: fileURL) - XCTAssertTrue(waitForPublishedEvents(), "No events were published within timeout") - - guard !publishedEvents.isEmpty else { return } - XCTAssertEqual(publishedEvents[0].count, 1) - XCTAssertEqual(publishedEvents[0][0].type, .deleted) - } -} - -extension BatchingFileChangeWatcherTests { - func waitForPublishedEvents(timeout: TimeInterval = 1.0) -> Bool { - let start = Date() - while publishedEvents.isEmpty && Date().timeIntervalSince(start) < timeout { - RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.1)) - } - return !publishedEvents.isEmpty - } -} - -// MARK: - Tests for FileChangeWatcherService - -final class FileChangeWatcherServiceTests: XCTestCase { - var mockWorkspaceFileProvider: MockWorkspaceFileProvider! - var publishedEvents: [[FileEvent]] = [] - - override func setUp() { - super.setUp() - mockWorkspaceFileProvider = MockWorkspaceFileProvider() - publishedEvents = [] - } - - func createService(workspaceURL: URL = URL(fileURLWithPath: "/test/workspace")) -> FileChangeWatcherService { - return FileChangeWatcherService( - workspaceURL, - publisher: { [weak self] events in - self?.publishedEvents.append(events) - }, - publishInterval: 0.1, - workspaceFileProvider: mockWorkspaceFileProvider, - watcherFactory: MockFileWatcherFactory() - ) - } - - func testStartWatchingCreatesWatchersForProjects() { - let project1 = URL(fileURLWithPath: "/test/workspace/project1") - let project2 = URL(fileURLWithPath: "/test/workspace/project2") - mockWorkspaceFileProvider.subprojects = [project1, project2] - - let service = createService() - service.startWatching() - - XCTAssertNotNil(service.watcher) - XCTAssertEqual(service.watcher?.paths().count, 2) - XCTAssertEqual(service.watcher?.paths(), [project1, project2]) - } - - func testStartWatchingDoesNotCreateWatcherForRootDirectory() { - let service = createService(workspaceURL: URL(fileURLWithPath: "/")) - service.startWatching() - - XCTAssertNil(service.watcher) - } - - func testProjectMonitoringDetectsAddedProjects() { - let workspace = URL(fileURLWithPath: "/test/workspace") - let project1 = URL(fileURLWithPath: "/test/workspace/project1") - mockWorkspaceFileProvider.subprojects = [project1] - mockWorkspaceFileProvider.xcWorkspacePaths = [workspace.path] - - let service = createService(workspaceURL: workspace) - service.startWatching() - - XCTAssertNotNil(service.watcher) - - // Simulate adding a new project - let project2 = URL(fileURLWithPath: "/test/workspace/project2") - mockWorkspaceFileProvider.subprojects = [project1, project2] - - // Set up mock files for the added project - let file1URL = URL(fileURLWithPath: "/test/workspace/project2/file1.swift") - let file1 = FileReference( - url: file1URL, - relativePath: file1URL.relativePath, - fileName: file1URL.lastPathComponent - ) - let file2URL = URL(fileURLWithPath: "/test/workspace/project2/file2.swift") - let file2 = FileReference( - url: file2URL, - relativePath: file2URL.relativePath, - fileName: file2URL.lastPathComponent - ) - mockWorkspaceFileProvider.filesInWorkspace = [file1, file2] - - MockFileWatcher.triggerFileDelete(for: workspace.appendingPathComponent("contents.xcworkspacedata")) - - XCTAssertTrue(waitForPublishedEvents(), "No events were published within timeout") - - guard !publishedEvents.isEmpty else { return } - - // Verify file events were published - XCTAssertEqual(publishedEvents[0].count, 2) - - // Verify both files were reported as created - XCTAssertEqual(publishedEvents[0][0].type, .created) - XCTAssertEqual(publishedEvents[0][1].type, .created) - } - - func testProjectMonitoringDetectsRemovedProjects() { - let workspace = URL(fileURLWithPath: "/test/workspace") - let project1 = URL(fileURLWithPath: "/test/workspace/project1") - let project2 = URL(fileURLWithPath: "/test/workspace/project2") - mockWorkspaceFileProvider.subprojects = [project1, project2] - mockWorkspaceFileProvider.xcWorkspacePaths = [workspace.path] - - let service = createService(workspaceURL: workspace) - service.startWatching() - - XCTAssertNotNil(service.watcher) - - // Simulate removing a project - mockWorkspaceFileProvider.subprojects = [project1] - - // Set up mock files for the removed project - let file1URL = URL(fileURLWithPath: "/test/workspace/project2/file1.swift") - let file1 = FileReference( - url: file1URL, - relativePath: file1URL.relativePath, - fileName: file1URL.lastPathComponent - ) - let file2URL = URL(fileURLWithPath: "/test/workspace/project2/file2.swift") - let file2 = FileReference( - url: file2URL, - relativePath: file2URL.relativePath, - fileName: file2URL.lastPathComponent - ) - mockWorkspaceFileProvider.filesInWorkspace = [file1, file2] - - // Clear published events from setup - publishedEvents = [] - - MockFileWatcher.triggerFileDelete(for: workspace.appendingPathComponent("contents.xcworkspacedata")) - - XCTAssertTrue(waitForPublishedEvents(), "No events were published within timeout") - - guard !publishedEvents.isEmpty else { return } - - // Verify file events were published - XCTAssertEqual(publishedEvents[0].count, 2) - - // Verify both files were reported as deleted - XCTAssertEqual(publishedEvents[0][0].type, .deleted) - XCTAssertEqual(publishedEvents[0][1].type, .deleted) - } -} - -extension FileChangeWatcherServiceTests { - func waitForPublishedEvents(timeout: TimeInterval = 3.0) -> Bool { - let start = Date() - while publishedEvents.isEmpty && Date().timeIntervalSince(start) < timeout { - RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.1)) - } - return !publishedEvents.isEmpty - } -} diff --git a/Tool/Tests/WorkspaceTests/WorkspaceTests.swift b/Tool/Tests/WorkspaceTests/WorkspaceTests.swift deleted file mode 100644 index 87276a06..00000000 --- a/Tool/Tests/WorkspaceTests/WorkspaceTests.swift +++ /dev/null @@ -1,460 +0,0 @@ -import XCTest -import Foundation -@testable import Workspace - -class WorkspaceFileTests: XCTestCase { - func testMatchesPatterns() { - let url1 = URL(fileURLWithPath: "/path/to/file.swift") - let url2 = URL(fileURLWithPath: "/path/to/.git") - let patterns = [".git", ".svn"] - - XCTAssertTrue(WorkspaceFile.matchesPatterns(url2, patterns: patterns)) - XCTAssertFalse(WorkspaceFile.matchesPatterns(url1, patterns: patterns)) - } - - func testIsXCWorkspace() throws { - let tmpDir = try createTemporaryDirectory() - defer { - deleteDirectoryIfExists(at: tmpDir) - } - do { - let xcworkspaceURL = try createSubdirectory(in: tmpDir, withName: "myWorkspace.xcworkspace") - XCTAssertFalse(WorkspaceFile.isXCWorkspace(xcworkspaceURL)) - let xcworkspaceDataURL = try createFile(in: xcworkspaceURL, withName: "contents.xcworkspacedata", contents: "") - XCTAssertTrue(WorkspaceFile.isXCWorkspace(xcworkspaceURL)) - } catch { - throw error - } - } - - func testIsXCProject() throws { - let tmpDir = try createTemporaryDirectory() - defer { - deleteDirectoryIfExists(at: tmpDir) - } - do { - let xcprojectURL = try createSubdirectory(in: tmpDir, withName: "myProject.xcodeproj") - XCTAssertFalse(WorkspaceFile.isXCProject(xcprojectURL)) - let xcprojectDataURL = try createFile(in: xcprojectURL, withName: "project.pbxproj", contents: "") - XCTAssertTrue(WorkspaceFile.isXCProject(xcprojectURL)) - } catch { - throw error - } - } - - func testGetFilesInActiveProject() throws { - let tmpDir = try createTemporaryDirectory() - do { - let xcprojectURL = try createXCProjectFolder(in: tmpDir, withName: "myProject.xcodeproj") - _ = try createFile(in: tmpDir, withName: "file1.swift", contents: "") - _ = try createFile(in: tmpDir, withName: "file2.swift", contents: "") - _ = try createSubdirectory(in: tmpDir, withName: ".git") - let files = WorkspaceFile.getFilesInActiveWorkspace(workspaceURL: xcprojectURL, workspaceRootURL: tmpDir) - let fileNames = files.map { $0.url.lastPathComponent } - XCTAssertEqual(files.count, 2) - XCTAssertTrue(fileNames.contains("file1.swift")) - XCTAssertTrue(fileNames.contains("file2.swift")) - } catch { - deleteDirectoryIfExists(at: tmpDir) - throw error - } - deleteDirectoryIfExists(at: tmpDir) - } - - func testGetFilesInActiveWorkspace() throws { - let tmpDir = try createTemporaryDirectory() - defer { - deleteDirectoryIfExists(at: tmpDir) - } - do { - let myWorkspaceRoot = try createSubdirectory(in: tmpDir, withName: "myWorkspace") - let xcWorkspaceURL = try createXCWorkspaceFolder(in: myWorkspaceRoot, withName: "myWorkspace.xcworkspace", fileRefs: [ - "container:myProject.xcodeproj", - "group:../notExistedDir/notExistedProject.xcodeproj", - "group:../myDependency",]) - let xcprojectURL = try createXCProjectFolder(in: myWorkspaceRoot, withName: "myProject.xcodeproj") - let myDependencyURL = try createSubdirectory(in: tmpDir, withName: "myDependency") - - // Files under workspace should be included - _ = try createFile(in: myWorkspaceRoot, withName: "file1.swift", contents: "") - // unsupported patterns and file extension should be excluded - _ = try createFile(in: myWorkspaceRoot, withName: "unsupportedFileExtension.xyz", contents: "") - _ = try createSubdirectory(in: myWorkspaceRoot, withName: ".git") - - // Files under project metadata folder should be excluded - _ = try createFile(in: xcprojectURL, withName: "fileUnderProjectMetadata.swift", contents: "") - - // Files under dependency should be included - _ = try createFile(in: myDependencyURL, withName: "depFile1.swift", contents: "") - // Should be excluded - _ = try createSubdirectory(in: myDependencyURL, withName: ".git") - - // Files under unrelated directories should be excluded - _ = try createFile(in: tmpDir, withName: "unrelatedFile1.swift", contents: "") - - let files = WorkspaceFile.getFilesInActiveWorkspace(workspaceURL: xcWorkspaceURL, workspaceRootURL: myWorkspaceRoot) - let fileNames = files.map { $0.url.lastPathComponent } - XCTAssertEqual(files.count, 2) - XCTAssertTrue(fileNames.contains("file1.swift")) - XCTAssertTrue(fileNames.contains("depFile1.swift")) - } catch { - throw error - } - } - - func testGetSubprojectURLsFromXCWorkspace() throws { - let tmpDir = try createTemporaryDirectory() - defer { - deleteDirectoryIfExists(at: tmpDir) - } - - let workspaceDir = try createSubdirectory(in: tmpDir, withName: "workspace") - - // Create tryapp directory and project - let tryappDir = try createSubdirectory(in: tmpDir, withName: "tryapp") - _ = try createXCProjectFolder(in: tryappDir, withName: "tryapp.xcodeproj") - - // Create Copilot for Xcode project - _ = try createXCProjectFolder(in: workspaceDir, withName: "Copilot for Xcode.xcodeproj") - - // Create Test1 directory - let test1Dir = try createSubdirectory(in: tmpDir, withName: "Test1") - - // Create Test2 directory and project - let test2Dir = try createSubdirectory(in: tmpDir, withName: "Test2") - _ = try createXCProjectFolder(in: test2Dir, withName: "project2.xcodeproj") - - // Create the workspace data file with our references - let xcworkspaceData = """ - - - - - - - - - - - - - - """ - let workspaceURL = try createXCWorkspaceFolder(in: workspaceDir, withName: "workspace.xcworkspace", xcworkspacedata: xcworkspaceData) - - let subprojectURLs = WorkspaceFile.getSubprojectURLs(in: workspaceURL) - - XCTAssertEqual(subprojectURLs.count, 4) - let resolvedPaths = subprojectURLs.map { $0.path } - let expectedPaths = [ - tryappDir.path, - workspaceDir.path, // For Copilot for Xcode.xcodeproj - test1Dir.path, - test2Dir.path - ] - XCTAssertEqual(resolvedPaths, expectedPaths) - } - - func testGetSubprojectURLsFromEmbeddedXCWorkspace() throws { - let tmpDir = try createTemporaryDirectory() - defer { - deleteDirectoryIfExists(at: tmpDir) - } - - // Create the workspace data file with a self reference - let xcworkspaceData = """ - - - - - - """ - - // Create the MyApp directory structure - let myAppDir = try createSubdirectory(in: tmpDir, withName: "MyApp") - let xcodeProjectDir = try createXCProjectFolder(in: myAppDir, withName: "MyApp.xcodeproj") - let embeddedWorkspaceDir = try createXCWorkspaceFolder(in: xcodeProjectDir, withName: "MyApp.xcworkspace", xcworkspacedata: xcworkspaceData) - - let subprojectURLs = WorkspaceFile.getSubprojectURLs(in: embeddedWorkspaceDir) - XCTAssertEqual(subprojectURLs.count, 1) - XCTAssertEqual(subprojectURLs[0].lastPathComponent, "MyApp") - XCTAssertEqual(subprojectURLs[0].path, myAppDir.path) - } - - func testGetSubprojectURLsFromXCWorkspaceOrganizedByGroup() throws { - let tmpDir = try createTemporaryDirectory() - defer { - deleteDirectoryIfExists(at: tmpDir) - } - - // Create directories for the projects and groups - let tryappDir = try createSubdirectory(in: tmpDir, withName: "tryapp") - _ = try createXCProjectFolder(in: tryappDir, withName: "tryapp.xcodeproj") - - let webLibraryDir = try createSubdirectory(in: tmpDir, withName: "WebLibrary") - - // Create the group directories - let group1Dir = try createSubdirectory(in: tmpDir, withName: "group1") - let group2Dir = try createSubdirectory(in: group1Dir, withName: "group2") - _ = try createSubdirectory(in: group2Dir, withName: "group3") - _ = try createSubdirectory(in: group1Dir, withName: "group4") - - // Create the MyProjects directory - let myProjectsDir = try createSubdirectory(in: tmpDir, withName: "MyProjects") - - // Create the copilot-xcode directory and project - let copilotXcodeDir = try createSubdirectory(in: myProjectsDir, withName: "copilot-xcode") - _ = try createXCProjectFolder(in: copilotXcodeDir, withName: "Copilot for Xcode.xcodeproj") - - // Create the SwiftLanguageWeather directory and project - let swiftWeatherDir = try createSubdirectory(in: myProjectsDir, withName: "SwiftLanguageWeather") - _ = try createXCProjectFolder(in: swiftWeatherDir, withName: "SwiftWeather.xcodeproj") - - // Create the workspace data file with a complex group structure - let xcworkspaceData = """ - - - - - - - - - - - - - - - - - - - - """ - - // Create a test workspace structure - let workspaceURL = try createXCWorkspaceFolder(in: tmpDir, withName: "workspace.xcworkspace", xcworkspacedata: xcworkspaceData) - - let subprojectURLs = WorkspaceFile.getSubprojectURLs(in: workspaceURL) - XCTAssertEqual(subprojectURLs.count, 4) - let expectedPaths = [ - tryappDir.path, - webLibraryDir.path, - copilotXcodeDir.path, - swiftWeatherDir.path - ] - for expectedPath in expectedPaths { - XCTAssertTrue(subprojectURLs.contains { $0.path == expectedPath }, "Expected path not found: \(expectedPath)") - } - } - - func deleteDirectoryIfExists(at url: URL) { - if FileManager.default.fileExists(atPath: url.path) { - do { - try FileManager.default.removeItem(at: url) - } catch { - print("Failed to delete directory at \(url.path)") - } - } - } - - func createTemporaryDirectory() throws -> URL { - let temporaryDirectoryURL = FileManager.default.temporaryDirectory - let directoryName = UUID().uuidString - let directoryURL = temporaryDirectoryURL.appendingPathComponent(directoryName) - try FileManager.default.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil) - #if DEBUG - print("Create temp directory \(directoryURL.path)") - #endif - return directoryURL - } - - func createSubdirectory(in directory: URL, withName name: String) throws -> URL { - let subdirectoryURL = directory.appendingPathComponent(name) - try FileManager.default.createDirectory(at: subdirectoryURL, withIntermediateDirectories: true, attributes: nil) - return subdirectoryURL - } - - func createFile(in directory: URL, withName name: String, contents: String) throws -> URL { - let fileURL = directory.appendingPathComponent(name) - let data = contents.data(using: .utf8) - FileManager.default.createFile(atPath: fileURL.path, contents: data, attributes: nil) - return fileURL - } - - func createXCProjectFolder(in baseDirectory: URL, withName projectName: String) throws -> URL { - let projectURL = try createSubdirectory(in: baseDirectory, withName: projectName) - if projectName.hasSuffix(".xcodeproj") { - _ = try createFile(in: projectURL, withName: "project.pbxproj", contents: "// Project file contents") - } - return projectURL - } - - func createXCWorkspaceFolder(in baseDirectory: URL, withName workspaceName: String, fileRefs: [String]?) throws -> URL { - let xcworkspaceURL = try createSubdirectory(in: baseDirectory, withName: workspaceName) - if let fileRefs { - _ = try createXCworkspacedataFile(directory: xcworkspaceURL, fileRefs: fileRefs) - } - return xcworkspaceURL - } - - func createXCWorkspaceFolder(in baseDirectory: URL, withName workspaceName: String, xcworkspacedata: String) throws -> URL { - let xcworkspaceURL = try createSubdirectory(in: baseDirectory, withName: workspaceName) - _ = try createFile(in: xcworkspaceURL, withName: "contents.xcworkspacedata", contents: xcworkspacedata) - return xcworkspaceURL - } - - func createXCworkspacedataFile(directory: URL, fileRefs: [String]) throws -> URL { - let contents = generateXCWorkspacedataContents(fileRefs: fileRefs) - return try createFile(in: directory, withName: "contents.xcworkspacedata", contents: contents) - } - - func generateXCWorkspacedataContents(fileRefs: [String]) -> String { - var contents = """ - - - """ - for fileRef in fileRefs { - contents += """ - - - """ - } - contents += "" - return contents - } - - func testIsValidFile() throws { - let tmpDir = try createTemporaryDirectory() - defer { - deleteDirectoryIfExists(at: tmpDir) - } - do { - // Test valid Swift file - let swiftFileURL = try createFile(in: tmpDir, withName: "ValidFile.swift", contents: "// Swift code") - XCTAssertTrue(try WorkspaceFile.isValidFile(swiftFileURL)) - - // Test valid files with different supported extensions - let jsFileURL = try createFile(in: tmpDir, withName: "script.js", contents: "// JavaScript") - XCTAssertTrue(try WorkspaceFile.isValidFile(jsFileURL)) - - let mdFileURL = try createFile(in: tmpDir, withName: "README.md", contents: "# Markdown") - XCTAssertTrue(try WorkspaceFile.isValidFile(mdFileURL)) - - let jsonFileURL = try createFile(in: tmpDir, withName: "config.json", contents: "{}") - XCTAssertTrue(try WorkspaceFile.isValidFile(jsonFileURL)) - - // Test case insensitive extension matching - let swiftUpperURL = try createFile(in: tmpDir, withName: "File.SWIFT", contents: "// Swift") - XCTAssertTrue(try WorkspaceFile.isValidFile(swiftUpperURL)) - - // Test unsupported file extension - let unsupportedFileURL = try createFile(in: tmpDir, withName: "file.xyz", contents: "unsupported") - XCTAssertFalse(try WorkspaceFile.isValidFile(unsupportedFileURL)) - - // Test files matching skip patterns - let gitFileURL = try createFile(in: tmpDir, withName: ".git", contents: "") - XCTAssertFalse(try WorkspaceFile.isValidFile(gitFileURL)) - - let dsStoreURL = try createFile(in: tmpDir, withName: ".DS_Store", contents: "") - XCTAssertFalse(try WorkspaceFile.isValidFile(dsStoreURL)) - - let nodeModulesURL = try createFile(in: tmpDir, withName: "node_modules", contents: "") - XCTAssertFalse(try WorkspaceFile.isValidFile(nodeModulesURL)) - - // Test directory (should return false) - let subdirURL = try createSubdirectory(in: tmpDir, withName: "subdir") - XCTAssertFalse(try WorkspaceFile.isValidFile(subdirURL)) - - // Test Xcode workspace (should return false) - let xcworkspaceURL = try createSubdirectory(in: tmpDir, withName: "test.xcworkspace") - _ = try createFile(in: xcworkspaceURL, withName: "contents.xcworkspacedata", contents: "") - XCTAssertFalse(try WorkspaceFile.isValidFile(xcworkspaceURL)) - - // Test Xcode project (should return false) - let xcprojectURL = try createSubdirectory(in: tmpDir, withName: "test.xcodeproj") - _ = try createFile(in: xcprojectURL, withName: "project.pbxproj", contents: "") - XCTAssertFalse(try WorkspaceFile.isValidFile(xcprojectURL)) - - } catch { - throw error - } - } - - func testIsValidFileWithCustomExclusionFilter() throws { - let tmpDir = try createTemporaryDirectory() - defer { - deleteDirectoryIfExists(at: tmpDir) - } - do { - let swiftFileURL = try createFile(in: tmpDir, withName: "TestFile.swift", contents: "// Swift code") - let jsFileURL = try createFile(in: tmpDir, withName: "script.js", contents: "// JavaScript") - - // Test without custom exclusion filter - XCTAssertTrue(try WorkspaceFile.isValidFile(swiftFileURL)) - XCTAssertTrue(try WorkspaceFile.isValidFile(jsFileURL)) - - // Test with custom exclusion filter that excludes Swift files - let excludeSwiftFilter: (URL) -> Bool = { url in - return url.pathExtension.lowercased() == "swift" - } - - XCTAssertFalse(try WorkspaceFile.isValidFile(swiftFileURL, shouldExcludeFile: excludeSwiftFilter)) - XCTAssertTrue(try WorkspaceFile.isValidFile(jsFileURL, shouldExcludeFile: excludeSwiftFilter)) - - // Test with custom exclusion filter that excludes files with "Test" in name - let excludeTestFilter: (URL) -> Bool = { url in - return url.lastPathComponent.contains("Test") - } - - XCTAssertFalse(try WorkspaceFile.isValidFile(swiftFileURL, shouldExcludeFile: excludeTestFilter)) - XCTAssertTrue(try WorkspaceFile.isValidFile(jsFileURL, shouldExcludeFile: excludeTestFilter)) - - } catch { - throw error - } - } - - func testIsValidFileWithAllSupportedExtensions() throws { - let tmpDir = try createTemporaryDirectory() - defer { - deleteDirectoryIfExists(at: tmpDir) - } - do { - let supportedExtensions = supportedFileExtensions - - for (index, ext) in supportedExtensions.enumerated() { - let fileName = "testfile\(index).\(ext)" - let fileURL = try createFile(in: tmpDir, withName: fileName, contents: "test content") - XCTAssertTrue(try WorkspaceFile.isValidFile(fileURL), "File with extension .\(ext) should be valid") - } - - } catch { - throw error - } - } -} diff --git a/Version.xcconfig b/Version.xcconfig deleted file mode 100644 index 82b2f1a9..00000000 --- a/Version.xcconfig +++ /dev/null @@ -1,3 +0,0 @@ -APP_VERSION = 0.0.0 -APP_BUILD = $(APP_VERSION) - diff --git a/bridgeLaunchAgent.plist b/bridgeLaunchAgent.plist deleted file mode 100644 index a052db44..00000000 --- a/bridgeLaunchAgent.plist +++ /dev/null @@ -1,15 +0,0 @@ - - - - - Label - com.github.CopilotForXcode.CommunicationBridge - Program - /Applications/GitHub Copilot for Xcode.app/Contents/Applications/CommunicationBridge - MachServices - - com.github.CopilotForXcode.CommunicationBridge - - - - diff --git a/export-options.plist b/export-options.plist deleted file mode 100644 index f8d0b35b..00000000 --- a/export-options.plist +++ /dev/null @@ -1,10 +0,0 @@ - - - - - teamID - VEKTX9H2N7 - method - developer-id - - diff --git a/launchAgent.plist b/launchAgent.plist deleted file mode 100644 index 4770316e..00000000 --- a/launchAgent.plist +++ /dev/null @@ -1,15 +0,0 @@ - - - - - Label - com.github.CopilotForXcode.ExtensionService - Program - /Applications/GitHub Copilot for Xcode.app/Contents/Applications/GitHub Copilot for Xcode Extension.app/Contents/MacOS/GitHub Copilot for Xcode Extension - MachServices - - com.github.CopilotForXcode.ExtensionService - - - -