Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
152aaf8
Suggested changes.
johnno1962 Apr 21, 2026
8ae418d
Debug only.
johnno1962 Apr 21, 2026
f31c17d
To discuss.
johnno1962 Apr 21, 2026
ec3d144
Compatibility.
johnno1962 Apr 21, 2026
d42ac3d
Tweak titles.
johnno1962 Apr 21, 2026
6156955
Tooltips, more environment vars.
johnno1962 Apr 21, 2026
8b72919
More Tooltips.
johnno1962 Apr 21, 2026
f4eec93
Overdue for reuse.
johnno1962 Apr 21, 2026
d64e83b
Missed the boat reconstructing PR.
johnno1962 Apr 21, 2026
6e980c9
Is client Connected?
johnno1962 Apr 21, 2026
068bbfc
Disarm bear trap, FrontendServer needs to start first.
johnno1962 Apr 21, 2026
735801f
Capturing logs was inhibiting my debug prints.
johnno1962 Apr 21, 2026
c88f585
Update App/InjectionNext/Views/FileWatcherSettingsView.swift
johnno1962 Apr 21, 2026
130f273
Update App/InjectionNext/Views/BuildSystemSettingsView.swift
johnno1962 Apr 21, 2026
a7c60c2
Update App/InjectionNext/ConfigStore.swift
johnno1962 Apr 21, 2026
ff21627
D'oh
johnno1962 Apr 21, 2026
8e92360
Update App/InjectionNext/Views/XcodeSettingsView.swift
johnno1962 Apr 21, 2026
d7282fa
Change default arguments text.
johnno1962 Apr 21, 2026
46641f0
Update App/InjectionNext/Views/StatusMenuView.swift
johnno1962 Apr 21, 2026
5bb066f
Update App/InjectionNext/Views/XcodeSettingsView.swift
johnno1962 Apr 21, 2026
fd973bd
Update App/InjectionNext/AppDelegate.swift
johnno1962 Apr 21, 2026
ec1be47
Move away from print for debugs.
johnno1962 Apr 21, 2026
04c7495
Recover Defaults.swift fro InjectionIII.
johnno1962 Apr 21, 2026
0084513
Debug prints on #DEBUG or INJECTION_DEBUG anv var.
johnno1962 Apr 21, 2026
5714dbf
More env vars, connected status.
johnno1962 Apr 21, 2026
9295705
Tracing preferences.
johnno1962 Apr 21, 2026
13f1a68
docs(todo): flesh out 2.0 Next section with fork-discovered items (#142)
maatheusgois-dd Apr 22, 2026
985e359
Update App/InjectionNext/Defaults.swift
johnno1962 Apr 22, 2026
ac7559a
Update App/InjectionNext/Views/XcodeSettingsView.swift
johnno1962 Apr 22, 2026
982d7f6
Update App/InjectionNext/Views/XcodeSettingsView.swift
johnno1962 Apr 22, 2026
821d6d4
Update App/InjectionNext/ConfigStore.swift
johnno1962 Apr 22, 2026
765220e
For rellease
johnno1962 Apr 22, 2026
8af79c5
Reinstate project path further down pane.
johnno1962 Apr 22, 2026
f74a900
UNSETENV_VALUE
johnno1962 Apr 22, 2026
1028341
Consolidate.
johnno1962 Apr 22, 2026
e4e358b
Update App/InjectionNext/InjectionHybrid.swift
johnno1962 Apr 22, 2026
941e3ea
Enough already.
johnno1962 Apr 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Suggested changes.
  • Loading branch information
johnno1962 committed Apr 21, 2026
commit 152aaf8fda4331abe7e882a5b83b5c99c4d8c419
4 changes: 2 additions & 2 deletions App/InjectionNext/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
/// Mimics `launchXcodeItem.state` for MonitorXcode.
var launchXcodeItem: CompatMenuItem {
CompatMenuItem(
get: { ConfigStore.shared.isXcodeRunning ? .on : .off },
set: { ConfigStore.shared.isXcodeRunning = ($0 == .on) }
get: { ConfigStore.shared.haveLaunchedXocde ? .on : .off },
set: { ConfigStore.shared.haveLaunchedXocde = ($0 == .on) }
)
}

Expand Down
34 changes: 30 additions & 4 deletions App/InjectionNext/ConfigStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ final class ConfigStore: ObservableObject {
// MARK: - Injection State (published, not persisted)

@Published var injectionState: InjectionState = .idle
@Published var isXcodeRunning = false
@Published var haveLaunchedXocde = false
Comment thread
johnno1962 marked this conversation as resolved.
Outdated
@Published var isClientConnected = false
@Published var watchingDirectories: [String] = []

Expand Down Expand Up @@ -287,10 +287,32 @@ final class ConfigStore: ObservableObject {
didSet { ud.set(keyPathsMode.rawValue, forKey: "keyPathsMode") }
}
@Published var sweepExclude: String {
didSet { ud.set(sweepExclude, forKey: "sweepExclude") }
didSet { ud.set(sweepExclude, forKey: "sweepExclude")
updateSweepVars() }
}
@Published var sweepDetail: Bool {
didSet { ud.set(sweepDetail, forKey: "sweepDetail") }
didSet { ud.set(sweepDetail, forKey: "sweepDetail")
updateSweepVars() }
}

func updateSweepVars() {
let clients = InjectionServer.currentClients
InjectionServer.clientQueue.async {
for client in clients where client != nil {
self.sendSweepVars(to: client!)
}
}
}

func sendSweepVars(to client: InjectionServer) {
client.writeCommand(InjectionCommand.setenv.rawValue,
with: INJECTION_SWEEP_DETAIL)
client.write(sweepDetail ? "1" : "0")
if sweepExclude != "" {
client.writeCommand(InjectionCommand.setenv.rawValue,
with: INJECTION_SWEEP_EXCLUDE)
client.write(sweepExclude)
}
Comment thread
johnno1962 marked this conversation as resolved.
Outdated
Comment thread
johnno1962 marked this conversation as resolved.
}

// MARK: - Devices
Expand Down Expand Up @@ -485,7 +507,11 @@ final class ConfigStore: ObservableObject {
// MARK: - Actions

func setInjectionState(_ state: InjectionState) {
DispatchQueue.main.async { self.injectionState = state }
if Thread.isMainThread {
injectionState = state
} else {
DispatchQueue.main.async { [weak self] in self?.injectionState = state }
}
}

func updateWatchingDirectories() {
Expand Down
2 changes: 1 addition & 1 deletion App/InjectionNext/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>14083</string>
<string>14088</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.developer-tools</string>
<key>LSMinimumSystemVersion</key>
Expand Down
12 changes: 7 additions & 5 deletions App/InjectionNext/InjectionHybrid.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ class InjectionHybrid: InjectionBase {

/// Called from file watcher when file is edited.
override func inject(source: String) {
guard MonitorXcode.runningXcode == nil else { return }
// Detect git lock files - record path for later checking
if source.hasSuffix(".lock") &&
source.contains("/.git/") {
Expand Down Expand Up @@ -164,11 +163,14 @@ class HybridCompiler: NextCompiler {
static var liteRecompiler = Recompiler()

override func recompile(source: String, platform: String) -> String? {
let oldCache = Reloader.cacheFile
Reloader.sdk = platform // Select commands cache file.
if oldCache != Reloader.cacheFile { Self.liteRecompiler = Recompiler() }
let connected = InjectionServer.currentClient != nil,
oldCache = Reloader.cacheFile
Reloader.sdk = platform // Switch commands cache file.
if oldCache != Reloader.cacheFile && connected {
Self.liteRecompiler = Recompiler()
}
return Self.liteRecompiler.recompile(source: source, platformFilter:
"SDKs/"+platform, dylink: false)
connected ? "SDKs/"+platform : "", dylink: false)
}

override func link(object: String, dylib: String, arch: String) -> (String, Double)? {
Expand Down
1 change: 1 addition & 0 deletions App/InjectionNext/InjectionServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ class InjectionServer: SimpleSocket {
self.tmpPath[#"/$"#] = "" // strip trailing slash
Self.clientQueue.async {
Self.connected.append(self)
ConfigStore.shared.sendSweepVars(to: self)
}
} else {
error("**** Bad tmp ****")
Expand Down
32 changes: 28 additions & 4 deletions App/InjectionNext/MonitorXcode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,46 @@
// Captures this information and passes it onto a Recompiler
// instance to process and inject when edited file is saved.
//
import Foundation

import AppKit
import SwiftRegex
import Fortify
import Popen

class MonitorXcode {

// Currently running Xcode process
static weak var runningXcode: MonitorXcode?
static weak var runningXcode: MonitorXcode? {
didSet {
DispatchQueue.main.async {
ConfigStore.shared.haveLaunchedXocde = runningXcode != nil
}
}
}
// The service to recompile and inject a source file.
static var recompiler = FrontendServer.frontendRecompiler(for: "Xcode")

/// Any Xcode already running on this machine (not necessarily spawned
/// by InjectionNext). Used to avoid launching a second `Xcode`
/// process that would show the "already open in another Xcode
/// process" dialog when the project is already open elsewhere.
static var externalXcode: NSRunningApplication? {
NSRunningApplication
.runningApplications(withBundleIdentifier: "com.apple.dt.Xcode")
.first(where: { $0.processIdentifier > 0 })
}

func debug(_ what: Any..., separator: String = " ") {
#if DEBUG
print(what, separator: separator)
#endif
}

init(args: String = "") {
init?(args: String = "") {
if Self.externalXcode != nil {
InjectionServer.error("Xcode already running, cannot start another")
return nil
}
var args = args
#if DEBUG
args += " | tee \(Reloader.tmpbase).log"
Expand Down Expand Up @@ -181,9 +202,12 @@ class MonitorXcode {
Self.recompiler.store(compilation: update, for: source)
}
} else if line ==
" key.request: source.request.indexer.editor-did-save-file,",
" key.request: source.request.indexer.editor-will-save-file,",
let _ = xcodeStdout.readLine(), let source = readQuotedString() {
print("Injecting saved file "+source)
DispatchQueue.main.async {
InjectionHybrid.lastInjected[source] = Date.timeIntervalSinceReferenceDate
}
NextCompiler.compileQueue.async {
_ = Self.recompiler.inject(source: source)
}
Expand Down
2 changes: 1 addition & 1 deletion App/InjectionNext/Views/GeneralSettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ struct GeneralSettingsView: View {
Text(config.injectionState.rawValue)
}
}
LabeledContent("Xcode Running", value: config.isXcodeRunning ? "Yes" : "No")
LabeledContent("Xcode Running", value: config.haveLaunchedXocde ? "Yes" : "No")
LabeledContent("Client Connected", value: config.isClientConnected ? "Yes" : "No")

if !config.watchingDirectories.isEmpty {
Expand Down
56 changes: 0 additions & 56 deletions App/InjectionNext/Views/InjectionSettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,62 +12,6 @@ struct InjectionSettingsView: View {

var body: some View {
Form {
Section {
LabeledContent("Project Path") {
HStack {
Text(config.projectPath.isEmpty ? "Not set" : config.projectPath)
.foregroundStyle(config.projectPath.isEmpty ? .secondary : .primary)
.lineLimit(1)
.truncationMode(.middle)
Spacer()
Button("Browse...") {
let open = NSOpenPanel()
open.prompt = "Select Project"
open.canChooseDirectories = true
open.canChooseFiles = false
if open.runModal() == .OK, let url = open.url {
config.projectPath = url.path
config.defaultProjectFile = ""
Reloader.xcodeDev = config.xcodePath + "/Contents/Developer"
AppDelegate.ui?.watch(path: url.path)
config.updateWatchingDirectories()
}
}
if !config.projectPath.isEmpty {
Button("Clear") {
config.projectPath = ""
config.defaultProjectFile = ""
config.autoOpenDefaultProject = false
}
}
}
}
if !config.projectPath.isEmpty {
let projects = ProjectDiscovery.discoverProjects(in: config.projectPath)
if projects.count > 1 {
Picker("Default Project File", selection: $config.defaultProjectFile) {
Text("Ask every time").tag("")
ForEach(projects) { project in
Text(project.name).tag(project.path)
}
}

Toggle("Always open default project", isOn: $config.autoOpenDefaultProject)
} else if projects.count == 1 {
LabeledContent("Project File") {
Text(projects[0].name)
.foregroundStyle(.secondary)
}
}
}
} header: {
Label("Project", systemImage: "folder")
} footer: {
Text("Set a project directory. If it contains multiple .xcodeproj/.xcworkspace files, you can pick a default or be asked each time Xcode launches.")
.font(.caption)
.foregroundStyle(.secondary)
}

Section {
if config.watchingDirectories.isEmpty {
Text("No directories being watched")
Expand Down
45 changes: 8 additions & 37 deletions App/InjectionNext/Views/StatusMenuView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,45 +18,27 @@ struct StatusMenuView: View {
.font(.caption)
.foregroundStyle(.secondary)

Divider()

Button {
AppDelegate.ui?.runXcode(self)
NSApp.activate(ignoringOtherApps: true)
openWindow(id: "settings")
bringSettingsToFront()
} label: {
HStack {
Text("Launch Xcode")
if config.isXcodeRunning {
Spacer()
Image(systemName: "checkmark")
}
}
Label("Settings...", systemImage: "gearshape")
}

Divider()
.keyboardShortcut(",", modifiers: .command)

Button {
selectProject()
AppDelegate.ui?.runXcode(self)
} label: {
Comment on lines 31 to 33
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change removes the only call site for selectProject() in this view, leaving the selectProject() helper unused (and potentially producing an "unused private function" warning depending on build settings). Either remove the dead code or reintroduce a UI action that uses it.

Copilot uses AI. Check for mistakes.
HStack {
Text("Select Project...")
if !config.defaultProjectFile.isEmpty {
Text("Launch Xcode")
if config.haveLaunchedXocde {
Spacer()
Image(systemName: "checkmark")
}
}
}

if !config.defaultProjectFile.isEmpty {
Button("Clear Selected Project") {
config.defaultProjectFile = ""
config.autoOpenDefaultProject = false
}

Text(" \(URL(fileURLWithPath: config.defaultProjectFile).lastPathComponent)")
.font(.caption)
.foregroundStyle(.secondary)
}

Divider()

Button {
Expand Down Expand Up @@ -114,17 +96,6 @@ struct StatusMenuView: View {

Divider()

Button {
NSApp.activate(ignoringOtherApps: true)
openWindow(id: "settings")
bringSettingsToFront()
} label: {
Label("Settings...", systemImage: "gearshape")
}
.keyboardShortcut(",", modifiers: .command)

Divider()

Label {
Text(config.isClientConnected ? "Client Connected" : "No Client")
} icon: {
Expand Down
7 changes: 3 additions & 4 deletions App/InjectionNext/Views/XcodeSettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ struct XcodeSettingsView: View {
}

Section {
Toggle("Auto-launch Xcode on app start", isOn: $config.autoLaunchXcode)
Toggle("Restart Xcode if it crashes", isOn: $config.xcodeRestart)
Toggle("Hide initial Xcode alert", isOn: $config.hideXcodeAlert)
} header: {
Expand All @@ -70,15 +69,15 @@ struct XcodeSettingsView: View {
LabeledContent("Xcode Running") {
HStack {
Circle()
.fill(config.isXcodeRunning ? .green : .gray)
.fill(config.haveLaunchedXocde ? .green : .gray)
.frame(width: 8, height: 8)
Text(config.isXcodeRunning ? "Running" : "Not Running")
Text(config.haveLaunchedXocde ? "Running" : "Not Running")
}
}
Button("Launch Xcode Now") {
AppDelegate.ui?.runXcode(self)
}
.disabled(config.isXcodeRunning)
.disabled(config.haveLaunchedXocde)
} header: {
Label("Status", systemImage: "info.circle")
}
Expand Down
1 change: 1 addition & 0 deletions Sources/InjectionNextC/include/InjectionClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ typedef NS_ENUM(int, InjectionCommand) {
InjectionXcodePath,
InjectionSendFile,
InjectionMetrics,
InjectionSetenv,

InjectionInvalid = 1000,

Expand Down