diff --git a/docs/changelog.rst b/docs/changelog.rst index ccba24537..9e91ae70e 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -59,6 +59,8 @@ Detailed list of changes - X11: Print an error to :file:`STDERR` instead of refusing to start when the user sets a custom window icon larger than 128x128 (:iss:`6507`) +- Remote control: Allow matching by neighbor of active window. Useful for navigation plugins like vim-kitty-navigator + 0.29.2 [2023-07-27] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/kitty/boss.py b/kitty/boss.py index b95890022..01883c5ee 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -489,7 +489,7 @@ 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, self_window)} for wid in search(match, ( - 'id', 'title', 'pid', 'cwd', 'cmdline', 'num', 'env', 'var', 'recent', 'state' + 'id', 'title', 'pid', 'cwd', 'cmdline', 'num', 'env', 'var', 'recent', 'state', 'neighbor', ), set(self.window_id_map), get_matches): yield self.window_id_map[wid] diff --git a/kitty/rc/base.py b/kitty/rc/base.py index b573e002f..09af9ed16 100644 --- a/kitty/rc/base.py +++ b/kitty/rc/base.py @@ -83,7 +83,7 @@ def __call__(self, key: str, opt_name: Optional[str] = None, missing: Any = None --match -m The window to match. Match specifications are of the form: :italic:`field:query`. Where :italic:`field` can be one of: :code:`id`, :code:`title`, :code:`pid`, :code:`cwd`, :code:`cmdline`, :code:`num`, -:code:`env`, :code:`var`, :code:`state`, and :code:`recent`. +:code:`env`, :code:`var`, :code:`state`, :code:`neighbor`, and :code:`recent`. :italic:`query` is the expression to match. Expressions can be either a number or a regular expression, and can be :ref:`combined using Boolean operators `. @@ -101,6 +101,9 @@ def __call__(self, key: str, opt_name: Optional[str] = None, missing: Any = None 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. +The field :code:`neighbor` refers to a neighbor of the active window in the specified direction, which can be: +:code:`left`, :code:`right`, :code:`top` or :code:`bottom`. + When using the :code:`env` field to match on environment variables, you can specify only the environment variable name or a name and value, for example, :code:`env:MY_ENV_VAR=2`. diff --git a/kitty/window.py b/kitty/window.py index 210c91e9f..03002b8b9 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -755,6 +755,21 @@ def matches_query(self, field: str, query: str, active_tab: Optional[TabType] = if query == 'overlay_parent': return self_window is not None and self is self_window.overlay_parent return False + if field == 'neighbor': + t = get_boss().active_tab + if t is None: + return False + gid: Optional[int] = None + if query == 'left': + gid = t.neighboring_group_id("left") + elif query == 'right': + gid = t.neighboring_group_id("right") + elif query == 'top': + gid = t.neighboring_group_id("top") + elif query == 'bottom': + gid = t.neighboring_group_id("bottom") + return gid is not None and t.windows.active_window_in_group_id(gid) is self + pat = compile_match_query(query, field not in ('env', 'var')) return self.matches(field, pat) diff --git a/kitty/window_list.py b/kitty/window_list.py index 0ecf16db0..1eacd7f49 100644 --- a/kitty/window_list.py +++ b/kitty/window_list.py @@ -395,6 +395,12 @@ def active_window_in_nth_group(self, n: int, clamp: bool = False) -> Optional[Wi return self.id_map.get(self.groups[n].active_window_id) return None + def active_window_in_group_id(self, group_id: int) -> Optional[WindowType]: + for g in self.groups: + if g.id == group_id: + return self.id_map.get(g.active_window_id) + return None + def activate_next_window_group(self, delta: int) -> None: self.set_active_group_idx(wrap_increment(self.active_group_idx, self.num_groups, delta))