diff --git a/WakaTime/Extensions/AXUIElementExtension.swift b/WakaTime/Extensions/AXUIElementExtension.swift index fe00ae2..44b41bd 100644 --- a/WakaTime/Extensions/AXUIElementExtension.swift +++ b/WakaTime/Extensions/AXUIElementExtension.swift @@ -119,7 +119,7 @@ extension AXUIElement { return nil } - // Traverses the element's subtree (breadth-first) until visitor() returns false or traversal is completed + // Traverses the element's children (breadth-first) until visitor() returns false or traversal is completed func traverseDown(visitor: (AXUIElement) -> Bool) { var queue: [AXUIElement] = [self] while !queue.isEmpty { @@ -144,7 +144,7 @@ extension AXUIElement { } } - // Traverses the element's subtree (breadth-first) until visitor() returns false or traversal is completed + // Traverses the element's parents until visitor() returns false or traversal is completed func traverseUp(visitor: (AXUIElement) -> Bool, element: AXUIElement? = nil) { let element = element ?? self if let parent = element.parent { diff --git a/WakaTime/Helpers/MonitoringManager.swift b/WakaTime/Helpers/MonitoringManager.swift index 0a427c9..ad74380 100644 --- a/WakaTime/Helpers/MonitoringManager.swift +++ b/WakaTime/Helpers/MonitoringManager.swift @@ -52,13 +52,20 @@ class MonitoringManager { let entityUnwrapped = entity.0 else { return nil } - return HeartbeatData( + let project = project(for: app, activeWindow) + var language = language(for: app, activeWindow) + if project != nil && language == nil { + language = "<>" + } + + let heartbeat = HeartbeatData( entity: entityUnwrapped, entityType: entity.1, - project: project(for: app, activeWindow), - language: language(for: app), - category: category(for: app) + project: project, + language: language, + category: category(for: app, activeWindow) ) + return heartbeat } static var isMonitoringBrowsing: Bool { @@ -95,9 +102,11 @@ class MonitoringManager { } static func set(monitoringState: MonitoringState, for bundleId: String) { - let allApps = allMonitoredApps - if !allApps.contains(bundleId) { - UserDefaults.standard.set(allApps + [bundleId], forKey: monitoringKey) + if monitoringState == .on { + UserDefaults.standard.set(Array(Set(allMonitoredApps + [bundleId])), forKey: monitoringKey) + } else { + let apps = allMonitoredApps.filter { $0 != bundleId } + UserDefaults.standard.set(apps, forKey: monitoringKey) } UserDefaults.standard.synchronize() } @@ -251,8 +260,11 @@ class MonitoringManager { } } - static func category(for app: NSRunningApplication) -> Category? { - guard let monitoredApp = app.monitoredApp else { return .coding } + static func category(for app: NSRunningApplication, _ element: AXUIElement) -> Category { + guard let monitoredApp = app.monitoredApp else { + guard let url = currentBrowserUrl(for: app, element) else { return .coding } + return category(from: url) + } switch monitoredApp { case .adobeaftereffect: @@ -325,6 +337,29 @@ class MonitoringManager { } // swiftlint:enable cyclomatic_complexity + static func category(from url: String) -> Category { + let patterns = [ + "github.com/[^/]+/[^/]+/pull/.*$", + "gitlab.com/[^/]+/[^/]+/[^/]+/merge_requests/.*$", + "bitbucket.org/[^/]+/[^/]+/pull-requests/.*$", + ] + + for pattern in patterns { + do { + let regex = try NSRegularExpression(pattern: pattern) + let nsrange = NSRange(url.startIndex.. String? { guard let monitoredApp = app.monitoredApp else { guard let url = currentBrowserUrl(for: app, element) else { return nil } @@ -346,6 +381,7 @@ class MonitoringManager { static func project(from url: String) -> String? { let patterns = [ "github.com/([^/]+/[^/]+)/?.*$", + "gitlab.com/([^/]+/[^/]+)/?.*$", "bitbucket.org/([^/]+/[^/]+)/?.*$", "app.circleci.com/.*/?(github|bitbucket|gitlab)/([^/]+/[^/]+)/?.*$", "app.travis-ci.com/(github|bitbucket|gitlab)/([^/]+/[^/]+)/?.*$", @@ -376,12 +412,41 @@ class MonitoringManager { return nil } - static func language(for app: NSRunningApplication) -> String? { + static func language(for app: NSRunningApplication, _ element: AXUIElement) -> String? { guard let monitoredApp = app.monitoredApp else { return nil } switch monitoredApp { case .canva: return "Image (svg)" + case .chrome: + do { + guard let url = currentBrowserUrl(for: app, element) else { return nil } + + let regex = try NSRegularExpression(pattern: "github.com/[^/]+/[^/]+/?$") + let nsrange = NSRange(url.startIndex..>", ] - if let project { + if let project = project { args.append("--project") args.append(project) - } - if isWrite { - args.append("--write") + } else { + args.append("--alternate-project") + args.append("<>") } if let language = language { args.append("--language") args.append(language) } + if isWrite { + args.append("--write") + } Logging.default.log("Sending heartbeat with: \(args)")