Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 40 additions & 21 deletions kitty/rc/ls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>

import json
import os
from collections.abc import Callable
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Sequence

from kitty.constants import appname

from .base import MATCH_TAB_OPTION, MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Tab, Window
from ..boss import OSWindowDict
from ..child import ProcessDesc
from ..launch import is_excluded_env_var

if TYPE_CHECKING:
from kitty.cli_stub import LSRCOptions as CLIOptions
Expand All @@ -19,6 +23,7 @@ class LS(RemoteCommand):
match/str: Window to change colors in
match_tab/str: Tab to change colors in
self/bool: Boolean indicating whether to list only the window the command is run in
output_format/str: Output in json or session format
'''

short_desc = 'List tabs/windows'
Expand All @@ -41,6 +46,13 @@ class LS(RemoteCommand):
--self
type=bool-set
Only list the window this command is run in.


--output-format
type=choices
choices=json,session
default=json
Output in json or session format
''' + '\n\n' + MATCH_WINDOW_OPTION + '\n\n' + MATCH_TAB_OPTION.replace('--match -m', '--match-tab -t', 1)

def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:
Expand All @@ -49,6 +61,7 @@ def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: Arg
def response_from_kitty(self, boss: Boss, window: Window | None, payload_get: PayloadGetType) -> ResponseType:
tab_filter: Callable[[Tab], bool] | None = None
window_filter: Callable[[Window], bool] | None = None
output_session: bool = False

if payload_get('self'):
def wf(w: Window) -> bool:
Expand All @@ -59,26 +72,32 @@ def wf(w: Window) -> bool:
def wf(w: Window) -> bool:
return w.id in window_ids
window_filter = wf
data = list(boss.list_os_windows(window, tab_filter, window_filter))
if not payload_get('all_env_vars'):
all_env_blocks: list[dict[str, str]] = []
common_env_vars: set[tuple[str, str]] = set()
for osw in data:
for tab in osw.get('tabs', ()):
for w in tab.get('windows', ()):
env: dict[str, str] = w.get('env', {})
frozen_env = set(env.items())
if all_env_blocks:
common_env_vars &= frozen_env
else:
common_env_vars = frozen_env
all_env_blocks.append(env)
if common_env_vars and len(all_env_blocks) > 1:
remove_env_vars = {k for k, v in common_env_vars}
for env in all_env_blocks:
for r in remove_env_vars:
env.pop(r, None)
return json.dumps(data, indent=2, sort_keys=True)
elif payload_get('output_format') == 'session':
output_session = True

if not output_session:
data = list(boss.list_os_windows(window, tab_filter, window_filter))
if not payload_get('all_env_vars'):
all_env_blocks: list[dict[str, str]] = []
common_env_vars: set[tuple[str, str]] = set()
for osw in data:
for tab in osw.get('tabs', ()):
for w in tab.get('windows', ()):
env: dict[str, str] = w.get('env', {})
frozen_env = set(env.items())
if all_env_blocks:
common_env_vars &= frozen_env
else:
common_env_vars = frozen_env
all_env_blocks.append(env)
if common_env_vars and len(all_env_blocks) > 1:
remove_env_vars = {k for k, v in common_env_vars}
for env in all_env_blocks:
for r in remove_env_vars:
env.pop(r, None)
return json.dumps(data, indent=2, sort_keys=True)
else:
return "\n".join(boss.serialize_state_as_session())


ls = LS()
4 changes: 3 additions & 1 deletion kitty/tabs.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env python
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>

import json
import os
import re
import stat
Expand Down Expand Up @@ -304,6 +305,7 @@ def serialize_state_as_session(self) -> list[str]:
if lc:
gw.append(shlex.join(lc))
launch_cmds.extend(gw)
launch_cmds.append(f'set_layout_state {json.dumps(self.serialize_state()["layout_state"])}\n')
return (ans + launch_cmds) if launch_cmds else []

def active_window_changed(self) -> None:
Expand Down Expand Up @@ -1170,7 +1172,7 @@ def list_tabs(
'is_active': tab is active_tab,
'title': tab.name or tab.title,
'layout': str(tab.current_layout.name),
'layout_state': tab.current_layout.layout_state(),
'layout_state': tab.current_layout.serialize(tab.windows),
'layout_opts': tab.current_layout.layout_opts.serialized(),
'enabled_layouts': tab.enabled_layouts,
'windows': windows,
Expand Down
8 changes: 8 additions & 0 deletions kitty/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,9 @@ class WindowDict(TypedDict):
id: int
is_focused: bool
is_active: bool
is_actions_on_close: bool
is_actions_on_focus_change: bool
is_actions_on_removal: bool
title: str
pid: int | None
cwd: str
Expand Down Expand Up @@ -821,6 +824,9 @@ def as_dict(self, is_focused: bool = False, is_self: bool = False, is_active: bo
'id': self.id,
'is_focused': is_focused,
'is_active': is_active,
'is_actions_on_close': self in self.actions_on_close,
'is_actions_on_focus_change': self in self.actions_on_focus_change,
'is_actions_on_removal': self in self.actions_on_removal,
'title': self.title,
'pid': self.child.pid,
'cwd': self.child.current_cwd or self.child.cwd,
Expand Down Expand Up @@ -1978,6 +1984,8 @@ def as_launch_command(self, is_overlay: bool = False) -> list[str]:
ans.append('--hold-after-ssh')
for k, v in self.user_vars.items():
ans.append(f'--var={k}={v}')
if 'kitty_serialize_window_id' not in self.user_vars:
ans.append(f'--var=kitty_serialize_window_id={self.id}')
ans.extend(self.padding.as_launch_args())
ans.extend(self.margin.as_launch_args('margin'))
if self.override_title:
Expand Down