175 lines
5.4 KiB
Swift
175 lines
5.4 KiB
Swift
|
|
import Foundation
|
||
|
|
import SwiftUI
|
||
|
|
|
||
|
|
// MARK: - File System
|
||
|
|
|
||
|
|
struct FileNode: Codable, Identifiable {
|
||
|
|
var id: String { path }
|
||
|
|
let name: String
|
||
|
|
let path: String
|
||
|
|
let isDirectory: Bool
|
||
|
|
var children: [FileNode]?
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - Devices
|
||
|
|
|
||
|
|
struct ConnectedDevice: Codable, Identifiable {
|
||
|
|
var id: String { udid }
|
||
|
|
let udid: String
|
||
|
|
let name: String
|
||
|
|
let model: String
|
||
|
|
let osVersion: String
|
||
|
|
let isNetworkConnected: Bool
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - Build Wire Types
|
||
|
|
|
||
|
|
struct BuildLogMessage: Codable {
|
||
|
|
enum MessageType: String, Codable { case stdout, stderr, status }
|
||
|
|
let type: MessageType
|
||
|
|
let text: String
|
||
|
|
let exitCode: Int?
|
||
|
|
}
|
||
|
|
|
||
|
|
struct BuildRequest: Codable {
|
||
|
|
let projectPath: String
|
||
|
|
let scheme: String
|
||
|
|
let destination: String
|
||
|
|
let action: String
|
||
|
|
let configuration: String
|
||
|
|
let preBuildHooks: [PreBuildHookRequest]
|
||
|
|
let bundleIdentifier: String?
|
||
|
|
let extraBuildSettings: [String]
|
||
|
|
}
|
||
|
|
|
||
|
|
struct PreBuildHookRequest: Codable {
|
||
|
|
let name: String
|
||
|
|
let command: String
|
||
|
|
let workingDirectory: String
|
||
|
|
let timeoutSeconds: Int
|
||
|
|
let continueOnFailure: Bool
|
||
|
|
|
||
|
|
static func xcodeGen(projectRoot: String) -> PreBuildHookRequest {
|
||
|
|
PreBuildHookRequest(name: "XcodeGen", command: "./generate.sh",
|
||
|
|
workingDirectory: projectRoot, timeoutSeconds: 60,
|
||
|
|
continueOnFailure: false)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
struct BuildSessionResponse: Codable {
|
||
|
|
let sessionId: String
|
||
|
|
let webSocketURL: String
|
||
|
|
}
|
||
|
|
|
||
|
|
struct FileContentResponse: Codable {
|
||
|
|
let path: String
|
||
|
|
let content: String
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - Console
|
||
|
|
|
||
|
|
struct ConsoleLine: Identifiable {
|
||
|
|
let id = UUID()
|
||
|
|
let text: String
|
||
|
|
let type: LineType
|
||
|
|
|
||
|
|
enum LineType { case standard, error, warning, success, info }
|
||
|
|
|
||
|
|
var color: Color {
|
||
|
|
switch type {
|
||
|
|
case .standard: return .primary
|
||
|
|
case .error: return Color(red: 1.0, green: 0.45, blue: 0.45)
|
||
|
|
case .warning: return Color(red: 1.0, green: 0.75, blue: 0.35)
|
||
|
|
case .success: return Color(red: 0.45, green: 0.95, blue: 0.55)
|
||
|
|
case .info: return .secondary
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static func fromBuildLogMessage(_ msg: BuildLogMessage) -> [ConsoleLine] {
|
||
|
|
msg.text
|
||
|
|
.split(separator: "\n", omittingEmptySubsequences: false)
|
||
|
|
.map { raw in
|
||
|
|
let text = String(raw)
|
||
|
|
let type: LineType
|
||
|
|
if text.contains(": error:") { type = .error }
|
||
|
|
else if text.contains(": warning:") { type = .warning }
|
||
|
|
else if msg.type == .stderr { type = .error }
|
||
|
|
else if msg.type == .status { type = msg.exitCode == 0 ? .success : .error }
|
||
|
|
else { type = .standard }
|
||
|
|
return ConsoleLine(text: text, type: type)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - Diagnostics
|
||
|
|
|
||
|
|
struct DiagnosticRange: Identifiable, Equatable {
|
||
|
|
enum Severity { case error, warning }
|
||
|
|
let id = UUID()
|
||
|
|
let line: Int
|
||
|
|
let column: Int
|
||
|
|
let length: Int
|
||
|
|
let severity: Severity
|
||
|
|
let message: String
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - LSP / Completions
|
||
|
|
|
||
|
|
struct CompletionItem: Identifiable {
|
||
|
|
let id = UUID()
|
||
|
|
let label: String
|
||
|
|
let detail: String
|
||
|
|
let kind: Int
|
||
|
|
let insertText: String?
|
||
|
|
let filterText: String?
|
||
|
|
|
||
|
|
var symbolIcon: String {
|
||
|
|
switch kind {
|
||
|
|
case 2, 3: return "function"
|
||
|
|
case 6: return "variable"
|
||
|
|
case 7: return "cube"
|
||
|
|
case 8: return "curlybraces"
|
||
|
|
case 9: return "arrow.triangle.branch"
|
||
|
|
case 10: return "square.split.bottomrightquarter"
|
||
|
|
case 14: return "key.fill"
|
||
|
|
case 15: return "chevron.left.forwardslash.chevron.right"
|
||
|
|
default: return "doc.text"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
struct DefinitionLocation {
|
||
|
|
let filePath: String
|
||
|
|
let line: Int
|
||
|
|
let column: Int
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - Notification Names
|
||
|
|
|
||
|
|
extension Notification.Name {
|
||
|
|
static let gitCheckoutCompleted = Notification.Name("git.checkout.completed")
|
||
|
|
static let gitCommitCompleted = Notification.Name("git.commit.completed")
|
||
|
|
static let gitPushCompleted = Notification.Name("git.push.completed")
|
||
|
|
static let gitSyncCompleted = Notification.Name("git.sync.completed")
|
||
|
|
static let gitMergeCompleted = Notification.Name("git.merge.completed")
|
||
|
|
static let gitWriteCompleted = Notification.Name("git.write.completed")
|
||
|
|
static let gitReadCompleted = Notification.Name("git.read.completed")
|
||
|
|
static let gitOperationFailed = Notification.Name("git.operation.failed")
|
||
|
|
static let consoleClearRequested = Notification.Name("console.clear.requested")
|
||
|
|
static let navigateToRange = Notification.Name("editor.navigate.range")
|
||
|
|
static let triggerCompletion = Notification.Name("editor.trigger.completion")
|
||
|
|
static let openSettings = Notification.Name("padxcode.open.settings")
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - String helpers
|
||
|
|
|
||
|
|
extension String {
|
||
|
|
var urlEncoded: String {
|
||
|
|
addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? self
|
||
|
|
}
|
||
|
|
func replacingFirstOccurrence(of target: String, with replacement: String) -> String {
|
||
|
|
guard let range = self.range(of: target) else { return self }
|
||
|
|
return self.replacingCharacters(in: range, with: replacement)
|
||
|
|
}
|
||
|
|
}
|