Skip to content

Commit cd9aef0

Browse files
authored
fix: consolidate editor preferences in settings (#1295)
1 parent eec4391 commit cd9aef0

7 files changed

Lines changed: 87 additions & 31 deletions

File tree

.github/workflows/ci.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ jobs:
496496
--workflow .github/workflows/ci.yml \
497497
--max-delta 3
498498
499-
# UI test shards — 7 parallel jobs, 29-31 tests each (delta 2).
499+
# UI test shards — 7 parallel jobs, 29-32 tests each (delta 3).
500500
# To rebalance: count tests per class, redistribute so shards stay within ±3 tests.
501501
# List classes with: grep -r "func test" PineUITests/ | sed 's/:.*//' | sort | uniq -c | sort -rn
502502
ui-tests:
@@ -514,11 +514,10 @@ jobs:
514514
-only-testing:PineUITests/TerminalTests
515515
-only-testing:PineUITests/TerminalMenuTests
516516
-only-testing:PineUITests/AgentAttentionKeyboardUITests
517-
# Shard 2 — Welcome & Session (31 tests)
517+
# Shard 2 — Welcome & Session (30 tests)
518518
- shard-name: "Welcome & Session"
519519
test-classes: >-
520520
-only-testing:PineUITests/WelcomeWindowTests
521-
-only-testing:PineUITests/MinimapTests
522521
-only-testing:PineUITests/SessionRestoreTests
523522
-only-testing:PineUITests/SidebarFolderClickTests
524523
-only-testing:PineUITests/NativeFileWindowMenuUITests
@@ -546,14 +545,15 @@ jobs:
546545
-only-testing:PineUITests/SidebarRenameTests
547546
-only-testing:PineUITests/SidebarFileOperationsTests
548547
-only-testing:PineUITests/UserTaskExecutionUITests
549-
# Shard 6 — Search & Panes (29 tests)
548+
# Shard 6 — Search & Panes (32 tests)
550549
- shard-name: "Search & Panes"
551550
test-classes: >-
552551
-only-testing:PineUITests/SidebarSearchTests
553552
-only-testing:PineUITests/DuplicateTests
554553
-only-testing:PineUITests/SplitPaneLifecycleTests
555554
-only-testing:PineUITests/FontSizeTests
556555
-only-testing:PineUITests/AgentActivityFilterUITests
556+
-only-testing:PineUITests/MinimapTests
557557
# Shard 7 — Security & Layout (31 tests)
558558
- shard-name: "Security & Layout"
559559
test-classes: >-

Pine/Localizable.xcstrings

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29157,6 +29157,21 @@
2915729157
"zh-Hans": { "stringUnit": { "state": "translated", "value": "关闭" } }
2915829158
}
2915929159
},
29160+
"settings.general.autoSave": {
29161+
"comment": "Toggle that automatically saves edited files after a delay.",
29162+
"extractionState": "manual",
29163+
"localizations": {
29164+
"de": { "stringUnit": { "state": "translated", "value": "Automatisch speichern" } },
29165+
"en": { "stringUnit": { "state": "translated", "value": "Auto Save" } },
29166+
"es": { "stringUnit": { "state": "translated", "value": "Guardar automáticamente" } },
29167+
"fr": { "stringUnit": { "state": "translated", "value": "Enregistrement automatique" } },
29168+
"ja": { "stringUnit": { "state": "translated", "value": "自動保存" } },
29169+
"ko": { "stringUnit": { "state": "translated", "value": "자동 저장" } },
29170+
"pt-BR": { "stringUnit": { "state": "translated", "value": "Salvar automaticamente" } },
29171+
"ru": { "stringUnit": { "state": "translated", "value": "Автосохранение" } },
29172+
"zh-Hans": { "stringUnit": { "state": "translated", "value": "自动保存" } }
29173+
}
29174+
},
2916029175
"settings.general.display": {
2916129176
"comment": "Heading for editor display preferences in General Settings.",
2916229177
"extractionState": "manual",

Pine/PineAppMenuCommands.swift

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ struct PineAppMenuCommands: Commands {
3939
@AppStorage("minimapVisible") private var minimapVisible = true
4040
@AppStorage(BlameConstants.storageKey) private var blameVisible = true
4141
@AppStorage("wordWrapEnabled") private var wordWrapEnabled = true
42-
@AppStorage(TabManager.autoSaveKey) private var autoSaveEnabled = false
4342
private var keybindings: UserKeybindingRegistry {
4443
ExtensibilityManager.shared.keybindings
4544
}
@@ -270,23 +269,6 @@ struct PineAppMenuCommands: Commands {
270269
)
271270
.disabled(!nativeState.canDuplicate)
272271

273-
Divider()
274-
275-
Toggle(isOn: $autoSaveEnabled) {
276-
Label(Strings.menuAutoSave, systemImage: MenuIcons.autoSave)
277-
}
278-
279-
Toggle(
280-
isOn: Bindable(EditorSettings.shared).formatOnSave
281-
) {
282-
Label(Strings.menuFormatOnSave, systemImage: MenuIcons.formatOnSave)
283-
}
284-
285-
Toggle(
286-
isOn: Bindable(EditorSettings.shared).smartListContinuation
287-
) {
288-
Label(Strings.menuSmartListContinuation, systemImage: MenuIcons.smartListContinuation)
289-
}
290272
}
291273

292274
// MARK: - Window menu

Pine/Settings/GeneralSettingsView.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
// Surfaces the editor formatting defaults (EditorSettings), font size
77
// (FontSizeSettings), and the default visibility of the minimap and word
88
// wrap. Every control binds directly to the same source of truth used by
9-
// the File / View menus, so changes apply immediately and the menu
10-
// checkmarks stay in sync.
9+
// the editor, so changes apply immediately.
1110
//
1211

1312
import SwiftUI
@@ -22,6 +21,7 @@ import SwiftUI
2221
struct GeneralSettingsView: View {
2322
@Bindable var editor: EditorSettings
2423
@Bindable var fontSizeSettings: FontSizeSettings
24+
@AppStorage(TabManager.autoSaveKey) private var autoSaveEnabled = false
2525
@AppStorage("minimapVisible") private var defaultMinimapVisible = true
2626
@AppStorage("wordWrapEnabled") private var defaultWordWrap = true
2727

@@ -32,6 +32,11 @@ struct GeneralSettingsView: View {
3232
) {
3333
self.editor = editor
3434
self.fontSizeSettings = fontSizeSettings
35+
_autoSaveEnabled = AppStorage(
36+
wrappedValue: false,
37+
TabManager.autoSaveKey,
38+
store: defaults
39+
)
3540
_defaultMinimapVisible = AppStorage(
3641
wrappedValue: true,
3742
"minimapVisible",
@@ -51,6 +56,10 @@ struct GeneralSettingsView: View {
5156

5257
GroupBox(Strings.settingsGeneralFormatting) {
5358
VStack(alignment: .leading, spacing: 12) {
59+
Toggle(
60+
Strings.settingsGeneralAutoSave,
61+
isOn: $autoSaveEnabled
62+
)
5463
Toggle(
5564
Strings.settingsGeneralInsertFinalNewline,
5665
isOn: $editor.insertFinalNewline

Pine/Strings.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ enum Strings {
2424
"settings.general.insertFinalNewline"
2525
static let settingsGeneralStripTrailingWhitespace: LocalizedStringKey =
2626
"settings.general.stripTrailingWhitespace"
27+
static let settingsGeneralAutoSave: LocalizedStringKey =
28+
"settings.general.autoSave"
2729
static let settingsGeneralFormatOnSave: LocalizedStringKey =
2830
"settings.general.formatOnSave"
2931
static let settingsGeneralSmartListContinuation: LocalizedStringKey =
@@ -1493,9 +1495,6 @@ enum Strings {
14931495
String(localized: "branch.uncommittedChanges.switch")
14941496
}
14951497

1496-
static let menuAutoSave: LocalizedStringKey = "menu.autoSave"
1497-
static let menuFormatOnSave: LocalizedStringKey = "menu.formatOnSave"
1498-
static let menuSmartListContinuation: LocalizedStringKey = "menu.smartListContinuation"
14991498
static let autoSaving: LocalizedStringKey = "editor.autoSaving"
15001499
static let menuSave: LocalizedStringKey = "menu.save"
15011500
static let menuSaveAll: LocalizedStringKey = "menu.saveAll"

PineUITests/NativeFileWindowMenuUITests.swift

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,29 @@ final class NativeFileWindowMenuUITests: PineUITestCase {
5252
) == .completed
5353
}
5454

55+
/// Polls for the first hittable text field within `timeout`.
56+
///
57+
/// `NSOpenPanel`'s "Go to Folder" sheet animates its path field in
58+
/// asynchronously, and `app.typeKey` can race the field's appearance on
59+
/// CI runners. Polling avoids `XCTUnwrap` failing fast before the sheet is
60+
/// fully interactive (stabilizes #1291, #1295).
61+
private func firstHittableTextField(
62+
timeout: TimeInterval
63+
) -> XCUIElement? {
64+
let deadline = Date().addingTimeInterval(timeout)
65+
while Date() < deadline {
66+
if let field = app.textFields.allElementsBoundByIndex.first(
67+
where: { $0.isHittable }
68+
) {
69+
return field
70+
}
71+
Thread.sleep(forTimeInterval: 0.2)
72+
}
73+
return app.textFields.allElementsBoundByIndex.first {
74+
$0.isHittable
75+
}
76+
}
77+
5578
override func setUpWithError() throws {
5679
try super.setUpWithError()
5780
projectURL = try createTempProject(
@@ -264,11 +287,22 @@ final class NativeFileWindowMenuUITests: PineUITestCase {
264287
"Open Folder should present a directory picker"
265288
)
266289

267-
app.typeKey("g", modifierFlags: [.command, .shift])
290+
// Wait for the open panel to become interactive (it animates in),
291+
// then route Cmd+Shift+G to the sheet itself so the "Go to Folder"
292+
// path field reliably appears. Sending the shortcut to `app` can race
293+
// the panel gaining first-responder on CI (#1291, #1295).
294+
_ = XCTWaiter.wait(
295+
for: [
296+
XCTNSPredicateExpectation(
297+
predicate: NSPredicate(format: "isHittable == true"),
298+
object: openPanel
299+
)
300+
],
301+
timeout: 5
302+
)
303+
openPanel.typeKey("g", modifierFlags: [.command, .shift])
268304
let pathField = try XCTUnwrap(
269-
app.textFields.allElementsBoundByIndex.first(where: {
270-
$0.isHittable
271-
}),
305+
firstHittableTextField(timeout: 5),
272306
"Go to Folder should expose a hittable path field"
273307
)
274308
pathField.typeText(secondProjectURL.path)
@@ -341,4 +375,19 @@ final class NativeFileWindowMenuUITests: PineUITestCase {
341375
"Clear Menu should disable an empty Open Recent submenu"
342376
)
343377
}
378+
379+
func testFileMenuContainsActionsNotEditorPreferences() throws {
380+
launchWithProject(projectURL)
381+
XCTAssertTrue(
382+
waitForExistence(app.scrollViews["sidebar"], timeout: 10)
383+
)
384+
385+
clickMenuBarItem("File")
386+
387+
XCTAssertFalse(app.menuItems["Auto Save"].exists)
388+
XCTAssertFalse(app.menuItems["Format on Save"].exists)
389+
XCTAssertFalse(app.menuItems["Smart List Continuation"].exists)
390+
XCTAssertTrue(app.menuItems["Save"].exists)
391+
XCTAssertTrue(app.menuItems["Save As…"].exists)
392+
}
344393
}

PineUITests/SettingsUITests.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ final class SettingsUITests: PineUITestCase {
255255
tab: "General",
256256
contentIdentifier: "generalSettingsPane",
257257
expectedLabels: [
258+
"Auto Save",
258259
"Insert Final Newline",
259260
"Strip Trailing Whitespace",
260261
"Format on Save",
@@ -307,6 +308,7 @@ final class SettingsUITests: PineUITestCase {
307308
tab: "Основные",
308309
contentIdentifier: "generalSettingsPane",
309310
expectedLabels: [
311+
"Автосохранение",
310312
"Добавлять перевод строки в конце",
311313
"Удалять пробелы в конце строк",
312314
"Форматировать при сохранении",

0 commit comments

Comments
 (0)