From 433355252356c1f8b7a404a95bd220b34beda831 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 16 Aug 2021 18:29:06 +0530 Subject: [PATCH] Matching windows/tabs: allow matching by recency ``recent:0`` matches the active window/tab, ``recent:1`` matches the previous window/tab and so on --- docs/changelog.rst | 4 ++++ kitty/boss.py | 22 ++++++++++++++++++++++ kitty/rc/base.py | 16 ++++++++++------ kitty/tabs.py | 12 ++++++++++++ 4 files changed, 48 insertions(+), 6 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 4e81adfc1..2630c80d3 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -14,6 +14,10 @@ To update |kitty|, :doc:`follow the instructions `. of data that kitty will transmit to the system clipboard on behalf of programs running inside it (:iss:`3937`) +- When matching windows/tabs in kittens or using remote control, allow matching + by recency. ``recent:0`` matches the active window/tab, ``recent:1`` matches + the previous window/tab and so on + - A new :doc:`themes kitten ` to easily change kitty themes. 0.23.0 [2021-08-16] ---------------------- diff --git a/kitty/boss.py b/kitty/boss.py index 366c75b05..06e82aaee 100755 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -267,6 +267,17 @@ class Boss: if w is not None: yield w return + if field == 'recent': + tab = self.active_tab + if tab is not None: + try: + num = int(exp) + except Exception: + return + w = self.window_id_map.get(tab.nth_active_window_id(num)) + if w is not None: + yield w + return if field != 'env': pat: MatchPatternType = re.compile(exp) else: @@ -310,6 +321,17 @@ class Boss: idx = (int(pat.pattern) + len(tm.tabs)) % len(tm.tabs) found = True yield tm.tabs[idx] + elif field == 'recent': + tm = self.active_tab_manager + if tm is not None and len(tm.tabs) > 0: + try: + num = int(exp) + except Exception: + return + q = tm.nth_active_tab(num) + if q is not None: + found = True + yield q if not found: tabs = {self.tab_for_window(w) for w in self.match_windows(match)} for q in tabs: diff --git a/kitty/rc/base.py b/kitty/rc/base.py index 0d214b64c..cf9e51587 100644 --- a/kitty/rc/base.py +++ b/kitty/rc/base.py @@ -76,13 +76,15 @@ 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. +:italic:`field:regexp`. Where field can be one of: id, title, pid, cwd, cmdline, num, env and recent. You can use the :italic:`ls` command to get a list of windows. Note that for -numeric fields such as id, pid and num the expression is interpreted as a number, +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, 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. When using the :italic:`env` field +is available as the KITTY_WINDOW_ID environment variable. The field 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` ''' @@ -90,14 +92,16 @@ MATCH_TAB_OPTION = '''\ --match -m The tab to match. Match specifications are of the form: :italic:`field:regexp`. Where field can be one of: -id, index, title, window_id, window_title, pid, cwd, env, cmdline. +id, index, title, window_id, window_title, pid, cwd, env, cmdline and recent. You can use the :italic:`ls` command to get a list of tabs. Note that for -numeric fields such as id, index and pid the expression is interpreted as a number, +numeric fields such as id, recent, index and pid the expression is interpreted as a number, not a regular expression. When using title or id, first a matching tab is looked for and if not found a matching window is looked for, and the tab 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. +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. ''' diff --git a/kitty/tabs.py b/kitty/tabs.py index b20b146c3..5375b5dac 100644 --- a/kitty/tabs.py +++ b/kitty/tabs.py @@ -511,6 +511,12 @@ class Tab: # {{{ if groups: return groups[0] + def nth_active_window_id(self, n: int = 0) -> int: + if n <= 0: + return self.active_window.id if self.active_window else 0 + ids = tuple(reversed(self.windows.active_window_history)) + return ids[min(n - 1, len(ids) - 1)] if ids else 0 + def neighboring_group_id(self, which: EdgeLiteral) -> Optional[int]: neighbors = self.current_layout.neighbors(self.windows) candidates = neighbors.get(which) @@ -748,6 +754,12 @@ class TabManager: # {{{ self.set_active_tab_idx(idx) break + def nth_active_tab(self, n: int = 0) -> Optional[Tab]: + if n <= 0: + return self.active_tab + tab_ids = tuple(reversed(self.active_tab_history)) + return self.tab_for_id(tab_ids[min(n - 1, len(tab_ids) - 1)]) if tab_ids else None + def __iter__(self) -> Iterator[Tab]: return iter(self.tabs)