Skip to content

Commit

Permalink
feat: improve application name matching algorithm
Browse files Browse the repository at this point in the history
Fixes #50

release-as: 2.6.2
  • Loading branch information
socsieng committed Aug 22, 2021
1 parent 40f49be commit 456586a
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 11 deletions.
5 changes: 3 additions & 2 deletions README.md
Expand Up @@ -291,8 +291,9 @@ Safari id:com.apple.Safari

The first column includes the application name and the second column includes the application's bundle ID.

SendKeys will use `--application-name` to activate the first application instance that either matches the full
application name or partially matches the bundle id.
SendKeys will use `--application-name` to activate the first application instance that matches either the application
name or bundle id (case insensitive). If there are no exact matches, it will attempt to match on whole words for the
application name, followed by the bundle id.

## Prerequisites

Expand Down
47 changes: 38 additions & 9 deletions Sources/SendKeysLib/AppActivator.swift
Expand Up @@ -5,20 +5,49 @@ class AppActivator: NSObject {
private let filterName: String

init(appName: String) {
filterName = appName
filterName = appName.lowercased()
}

func activate() throws {
guard
let app = NSWorkspace.shared.runningApplications.filter({
return $0.localizedName == self.filterName || $0.bundleIdentifier?.contains(self.filterName) ?? false
}).first
else {
throw RuntimeError("Application \(self.filterName) not found")
let apps = NSWorkspace.shared.runningApplications.filter({ a in
return a.activationPolicy == .regular
})

// exact match (case insensitive)
var app = apps.filter({ a in
return a.localizedName?.lowercased() == self.filterName
|| a.bundleIdentifier?.lowercased() == self.filterName
}).first

let expression = try! NSRegularExpression(
pattern: "\\b\(NSRegularExpression.escapedPattern(for: self.filterName))\\b", options: .caseInsensitive)

// partial name match
if app == nil {
app =
apps.filter({ a in
let nameMatch = expression.firstMatch(
in: a.localizedName ?? "", options: [], range: NSMakeRange(0, a.localizedName?.utf16.count ?? 0)
)
return nameMatch != nil
}).first
}

// patial bundle id match
if app == nil {
app =
apps.filter({ a in
let bundleMatch = expression.firstMatch(
in: a.bundleIdentifier ?? "", options: [],
range: NSMakeRange(0, a.bundleIdentifier?.utf16.count ?? 0))
return bundleMatch != nil
}).first
}

guard app.activationPolicy != .prohibited else {
throw RuntimeError("Application \(self.filterName) prohibits activation")
if app == nil {
throw RuntimeError(
"Application \(self.filterName) could not be activated. Run `sendkeys apps` to see a list of applications that can be activated."
)
}

self.application = app
Expand Down

0 comments on commit 456586a

Please sign in to comment.