From b3fa7310cb41e6dad965a5adefbb6e35f93d0d5d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 12 Apr 2022 20:14:01 +0530 Subject: [PATCH] Allow matching on window/tab state --- kitty/boss.py | 8 ++++++-- kitty/rc/base.py | 18 ++++++++++++------ kitty/tabs.py | 18 ++++++++++++++---- kitty/window.py | 10 +++++++++- 4 files changed, 41 insertions(+), 13 deletions(-) diff --git a/kitty/boss.py b/kitty/boss.py index 586d62fef..be0c3a66e 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -346,7 +346,9 @@ class Boss: def get_matches(location: str, query: str, candidates: Set[int]) -> Set[int]: return {wid for wid in candidates if self.window_id_map[wid].matches_query(location, query, tab)} - for wid in search(match, ('id', 'title', 'pid', 'cwd', 'cmdline', 'num', 'env', 'recent',), set(self.window_id_map), get_matches): + for wid in search(match, ( + 'id', 'title', 'pid', 'cwd', 'cmdline', 'num', 'env', 'recent', 'state' + ), set(self.window_id_map), get_matches): yield self.window_id_map[wid] def tab_for_window(self, window: Window) -> Optional[Tab]: @@ -365,7 +367,9 @@ class Boss: return {wid for wid in candidates if tim[wid].matches_query(location, query, tm)} found = False - for tid in search(match, ('id', 'index', 'title', 'window_id', 'window_title', 'pid', 'cwd', 'env', 'cmdline', 'recent',), set(tim), get_matches): + for tid in search(match, ( + 'id', 'index', 'title', 'window_id', 'window_title', 'pid', 'cwd', 'env', 'cmdline', 'recent', 'state' + ), set(tim), get_matches): found = True yield tim[tid] diff --git a/kitty/rc/base.py b/kitty/rc/base.py index eb17c86b7..e13150207 100644 --- a/kitty/rc/base.py +++ b/kitty/rc/base.py @@ -76,18 +76,21 @@ ArgsType = List[str] MATCH_WINDOW_OPTION = '''\ --match -m The window to match. Match specifications are of the form: -:italic:`field:regexp`. Where field can be one of: id, title, pid, cwd, cmdline, num, env and recent. +:italic:`field:regexp`. Where field can be one of: id, title, pid, cwd, cmdline, num, env, state and recent. You can use the :italic:`ls` command to get a list of windows. Expressions can be :ref:`combined using Boolean operators `. Note that for -numeric fields such as id, pid, recent and num the expression is interpreted as a number, -not a regular expression. The field num refers to the window position in the current tab, +numeric fields such as :code:`id`, :code:`pid`, :code:`recent` and :code:`num` the expression is interpreted as a number, +not a regular expression. The field :code:`num` refers to the window position in the current tab, starting from zero and counting clockwise (this is the same as the order in which the windows are reported by the :italic:`ls` command). The window id of the current window -is available as the KITTY_WINDOW_ID environment variable. The field recent refers to recently +is available as the KITTY_WINDOW_ID environment variable. The field :code:`recent` refers to recently active windows in the currently active tab, with zero being the currently active window, one being the previously active window and so on. When using the :italic:`env` field to match on environment variables you can specify only the environment variable name or a name -and value, for example, :italic:`env:MY_ENV_VAR=2` +and value, for example, :italic:`env:MY_ENV_VAR=2`. The field :code:`state` matches +on the state of the window. Supported states are: :code:`active`, :code:`focused` and :code:`needs_attention`. +Active windows are windows that are the active window in their parent tab. There is only one focused window +and it is the window to which keyboard events are delivered. ''' MATCH_TAB_OPTION = '''\ --match -m @@ -103,7 +106,10 @@ for that window is used. You can also use window_id and window_title to match the tab that contains the window with the specified id or title. The index number is used to match the nth tab in the currently active OS window. The recent number matches recently active tabs in the currently active OS window, with zero being the currently -active tab, one the previously active tab and so on. +active tab, one the previously active tab and so on. The field :code:`state` matches +on the state of the tab. Supported states are: :code:`active`, :code:`focused` and :code:`needs_attention`. +Active tabs are tabs that are the active tab in their parent OS Window. There is only one focused tab +and it is the tab to which keyboard events are delivered. ''' diff --git a/kitty/tabs.py b/kitty/tabs.py index 7e13f9386..09dfa0833 100644 --- a/kitty/tabs.py +++ b/kitty/tabs.py @@ -20,10 +20,10 @@ from .cli_stub import CLIOptions from .constants import appname, kitty_exe from .fast_data_types import ( GLFW_MOUSE_BUTTON_LEFT, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, GLFW_RELEASE, - add_tab, attach_window, detach_window, get_boss, get_click_interval, - get_options, mark_tab_bar_dirty, next_window_id, remove_tab, remove_window, - ring_bell, set_active_tab, set_active_window, swap_tabs, - sync_os_window_title + add_tab, attach_window, current_os_window, detach_window, get_boss, + get_click_interval, get_options, mark_tab_bar_dirty, next_window_id, + remove_tab, remove_window, ring_bell, set_active_tab, set_active_window, + swap_tabs, sync_os_window_title ) from .layout.base import Layout from .layout.interface import create_layout_object_for, evict_cached_layouts @@ -708,6 +708,16 @@ class Tab: # {{{ if active_tab_manager and len(active_tab_manager.tabs): return self is active_tab_manager.nth_active_tab(int(query)) return False + if field == 'state': + if query == 'active': + return active_tab_manager is not None and self is active_tab_manager.active_tab + if query == 'focused': + return active_tab_manager is not None and self is active_tab_manager.active_tab and self.os_window_id == current_os_window() + if query == 'needs_attention': + for w in self: + if w.needs_attention: + return True + return False return False def __iter__(self) -> Iterator[Window]: diff --git a/kitty/window.py b/kitty/window.py index 10ec8321b..48ab8edd2 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -660,7 +660,15 @@ class Window: if field == 'num': return active_tab.get_nth_window(q) is self return active_tab.nth_active_window_id(q) == self.id - return False + return False + if field == 'state': + if query == 'active': + return active_tab is not None and self is active_tab.active_window + if query == 'focused': + return active_tab is not None and self is active_tab.active_window and current_os_window() == self.os_window_id + if query == 'needs_attention': + return self.needs_attention + return False pat = compile_match_query(query, field != 'env') return self.matches(field, pat)