Skip to content

Add user-defined aliases - #90

Merged
oNaiPs merged 4 commits into
mainfrom
feature/aliases
Oct 17, 2026
Merged

oNaiPs merged 4 commits into
mainfrom
feature/aliases

Conversation

@oNaiPs

@oNaiPs oNaiPs commented Oct 17, 2026 •

Copy link
Copy Markdown
Owner

Aliases map a short keyword to any item in the list, e.g. ff → Firefox or term → Ghostty.

Behaviour

  • When the query exactly matches an alias (case-insensitive), its target comes first, ahead of the fuzzy results, and isn't listed twice. Selecting it opens the item the usual way.
  • If the alias only partly matches (ter for term), the target is added after the regular results.
  • Targets are stored by a stable identity: the file path for apps and panes, cmd:<name> for $PATH programs, and the name only for anything else. Two items with the same name therefore don't collide, and the picker shows each item's folder. Aliases saved with only a name still resolve by name. If a target goes missing, the alias does nothing and Settings shows a warning next to it.
  • Aliases are skipped for piped stdin menus (PipeListProvider).
  • A new Aliases Settings tab lists aliases with edit and delete buttons. Adding or editing opens a sheet with a keyword field and a searchable list of the launcher's current items. Keywords can't be empty, can't contain spaces (<keyword> <query> is left free for search shortcuts) and must be unique. Aliases are saved to UserDefaults as Codable JSON.

Implementation
Most of the code is in new files: Aliases.swift (the model, the store and AliasSearchService, a subclass that wraps SearchService.search), AliasSettingsModel.swift and AliasSettingsView.swift. Changes to shared files are small: one line in SearchViewController (AliasSearchService.make(provider:)), a pane identifier and a pane entry in AppDelegate, plus a ListItem.providerData accessor in CompositeListProvider that reads the provider's own data without the routing tag. SearchService.swift is unchanged.

Testing

  • New AliasSearchServiceTests (uses MockListProvider) covers exact-match ordering, case-insensitivity, no duplicates, fuzzy alias matches, missing targets, the empty query, piped mode and persistence.
  • New AliasSettingsModelTests covers validation, saving, editing, deleting and missing targets.
  • SettingsPanesTests has new checks for the Aliases pane.
  • The alias store can be injected (AliasSearchService(provider:settings:), make(provider:settings:)). Under XCTest, AliasSettings.shared is an empty in-memory store, so tests never read real aliases. To check, I wrote aliases into the real com.onaips.dmenu-macos defaults, ran the full suite with no failures, then removed them.
  • xcodebuild ... test: 238 tests, 0 failures. swiftlint --strict: 0 violations.

🤖 Generated with Claude Code

Typing an alias exactly shows its target first; partial matches of the
alias also list the target. Aliases are managed in a new Settings tab and
are ignored for piped-in menus.
Aliases now store the target's file path (apps, panes) or command name
($PATH programs), falling back to the name, so same-named items don't
collide. Name-only aliases saved earlier still resolve by name. The alias
store is injectable and is an empty in-memory store under XCTest.
The Aliases pane uses a fixed-height bordered list so many aliases scroll
instead of growing the window. Saving an alias only requires its target to
be listed when the target was changed.
# Conflicts:
#	README.md
#	dmenu-mac.xcodeproj/project.pbxproj
#	src/AppDelegate.swift
#	src/CompositeListProvider.swift
#	src/SearchViewController.swift
@oNaiPs
oNaiPs merged commit fddd200 into main Oct 17, 2026
3 checks passed
@oNaiPs
oNaiPs deleted the feature/aliases branch October 17, 2026 13:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant