Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions Sources/AppBundle/config/Config.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ struct Config: ConvenienceCopyable {
var startAtLogin: Bool = false
var autoReloadConfig: Bool = false
var automaticallyUnhideMacosHiddenApps: Bool = false
var configEditorAppPath: String? = nil
var accordionPadding: Int = 30
var enableNormalizationOppositeOrientationForNestedContainers: Bool = true
var persistentWorkspaces: OrderedSet<String> = []
Expand Down
5 changes: 5 additions & 0 deletions Sources/AppBundle/config/parseConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ private let configParser: [String: any ParserProtocol<Config>] = [
"start-at-login": Parser(\.startAtLogin, parseBool),
"auto-reload-config": Parser(\.autoReloadConfig, parseBool),
"automatically-unhide-macos-hidden-apps": Parser(\.automaticallyUnhideMacosHiddenApps, parseBool),
"config-editor-app-path": Parser(\.configEditorAppPath, parseOptionalString),
"accordion-padding": Parser(\.accordionPadding, parseInt),
persistentWorkspacesKey: Parser(\.persistentWorkspaces, parsePersistentWorkspaces),
"exec-on-workspace-change": Parser(\.execOnWorkspaceChange, parseArrayOfStrings),
Expand Down Expand Up @@ -253,6 +254,10 @@ func parseString(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace) -> Par
raw.string.orFailure(expectedActualTypeError(expected: .string, actual: raw.type, backtrace))
}

func parseOptionalString(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace) -> ParsedToml<String?> {
parseString(raw, backtrace).map { Optional($0) }
}

func parseSimpleType<T>(_ raw: TOMLValueConvertible) -> T? {
(raw.int as? T) ?? (raw.string as? T) ?? (raw.bool as? T)
}
Expand Down
12 changes: 10 additions & 2 deletions Sources/AppBundle/ui/MenuBar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,16 @@ func shortcutGroup(label: some View, content: some View) -> some View {
}
}

func getTextEditorToOpenConfig() -> URL {
NSWorkspace.shared.urlForApplication(toOpen: findCustomConfigUrl().urlOrNil ?? defaultConfigUrl)?
@MainActor func getTextEditorToOpenConfig() -> URL {
Copy link
Copy Markdown
Author

@technocidal technocidal Feb 16, 2026

Choose a reason for hiding this comment

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

(question) Moving this to @MainActor is probably not a big deal (e.g. this function isn't constantly called, other functions in this same file that are used to populate the menubar are already annotated with @MainActor etc.), but it's the most significant change from a runtime perspective so I wanted to point it out.

if let configEditorAppPath = config.configEditorAppPath {
let customEditorUrl = URL(filePath: configEditorAppPath)
if FileManager.default.fileExists(atPath: customEditorUrl.path),
customEditorUrl.pathExtension == "app" || customEditorUrl.lastPathComponent.hasSuffix(".app")
{
return customEditorUrl
}
}
return NSWorkspace.shared.urlForApplication(toOpen: findCustomConfigUrl().urlOrNil ?? defaultConfigUrl)?
.takeIf { $0.lastPathComponent != "Xcode.app" } // Blacklist Xcode. It is too heavy to open plain text files
?? URL(filePath: "/System/Applications/TextEdit.app")
}
21 changes: 21 additions & 0 deletions Sources/AppBundleTests/config/ConfigTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -449,4 +449,25 @@ final class ConfigTest: XCTestCase {
assertEquals(colemakConfig.keyMapping, KeyMapping(preset: .colemak, rawKeyNotationToKeyCode: [:]))
assertEquals(colemakConfig.keyMapping.resolve()["f"], .e)
}

func testConfigEditorAppPath() {
let (config1, errors1) = parseConfig(
"""
config-editor-app-path = '/Applications/Visual Studio Code.app'
""",
)
assertEquals(errors1, [])
assertEquals(config1.configEditorAppPath, "/Applications/Visual Studio Code.app")

let (config2, errors2) = parseConfig("")
assertEquals(errors2, [])
assertEquals(config2.configEditorAppPath, nil)

let (_, errors3) = parseConfig(
"""
config-editor-app-path = 123
""",
)
assertEquals(errors3.descriptions, ["config-editor-app-path: Expected type is \'string\'. But actual type is \'integer\'"])
}
}
5 changes: 5 additions & 0 deletions docs/config-examples/default-config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ start-at-login = false
# After setting this to true, reload once manually to start the auto-reloading
auto-reload-config = false

# Override which editor should be used when opening the config file via the menu bar
# By default, macOS will decide which editor to open with TextEdit as the fallback
# Set this to the full path of your preferred editor application.
# config-editor-app-path = '/Applications/Visual Studio Code.app'

# Normalizations. See: https://nikitabobko.github.io/AeroSpace/guide#normalization
enable-normalization-flatten-containers = true
enable-normalization-opposite-orientation-for-nested-containers = true
Expand Down
Loading