Have *_with_cwd work with the ssh kitten to open new windows auto-logged into the remote server at the current remote working directory

This commit is contained in:
Kovid Goyal 2022-03-15 19:59:16 +05:30
parent a216f6bd46
commit ce1e22ac95
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
6 changed files with 63 additions and 5 deletions

View File

@ -27,6 +27,13 @@ rc files:
So you can now type just ``s hostname`` to connect. So you can now type just ``s hostname`` to connect.
If you define a mapping in :file:`kitty.conf` such as::
map f1 new_window_with_cwd
Then, pressing :kbd:`F1` will open a new window automatically logged
into the same server using the ssh kitten, at the same directory.
The ssh kitten can be configured using the :file:`~/.config/kitty/ssh.conf` The ssh kitten can be configured using the :file:`~/.config/kitty/ssh.conf`
file where you can specify environment variables to set on the remote server file where you can specify environment variables to set on the remote server
and files to copy from your local machine to the remote server. Let's see a and files to copy from your local machine to the remote server. Let's see a

View File

@ -68,7 +68,10 @@ no-title
setting functionality instead. setting functionality instead.
no-cwd no-cwd
Turn off reporting Turn off reporting the current working directory. This is used to allow
:ref:`action-new_window_with_cwd` and similar to open windows logged
into remote machines using the :doc:`ssh kitten <kitten/ssh>`
automatically with the same working directory as the current window.
no-prompt-mark no-prompt-mark
Turn off marking of prompts. This disables jumping to prompt, browsing Turn off marking of prompts. This disables jumping to prompt, browsing

View File

@ -42,6 +42,27 @@ from .copy import CopyInstruction
from .options.types import Options as SSHOptions from .options.types import Options as SSHOptions
from .options.utils import DELETE_ENV_VAR from .options.utils import DELETE_ENV_VAR
def is_kitten_cmdline(q: List[str]) -> bool:
if len(q) < 4:
return False
if os.path.basename(q[0]).lower() != 'kitty':
return False
return q[1:3] == ['+kitten', 'ssh'] or q[1:4] == ['+', 'kitten', 'ssh']
def set_cwd_in_cmdline(cwd: str, argv: List[str]) -> None:
for i, arg in enumerate(tuple(argv)):
if arg.startswith('--kitten=cwd'):
argv[i] = f'--kitten=cwd={cwd}'
return
elif i > 0 and argv[i-1] == '--kitten' and (arg.startswith('cwd=') or arg.startswith('cwd ')):
argv[i] = cwd
return
idx = argv.index('ssh')
argv.insert(idx + 1, f'--kitten=cwd={cwd}')
# See https://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html # See https://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html
quote_pat = re.compile('([\\`"\n])') quote_pat = re.compile('([\\`"\n])')

View File

@ -201,7 +201,7 @@ class Child:
self.argv = list(argv) self.argv = list(argv)
if cwd_from is not None: if cwd_from is not None:
try: try:
cwd = cwd_from.cwd_of_child or cwd cwd = cwd_from.modify_argv_for_launch_with_cwd(self.argv) or cwd
except Exception as err: except Exception as err:
log_error(f'Failed to read cwd of {cwd_from} with error: {err}') log_error(f'Failed to read cwd of {cwd_from} with error: {err}')
else: else:

View File

@ -936,3 +936,12 @@ def cleanup_ssh_control_masters() -> None:
for x in files: for x in files:
with suppress(OSError): with suppress(OSError):
os.remove(x) os.remove(x)
def path_from_osc7_url(url: str) -> str:
if url.startswith('kitty-shell-cwd://'):
return '/' + url.split('/', 3)[-1]
if url.startswith('file://'):
from urllib.parse import urlparse
return urlparse(url).path
return ''

View File

@ -20,7 +20,7 @@ from typing import (
from .child import ProcessDesc from .child import ProcessDesc
from .cli_stub import CLIOptions from .cli_stub import CLIOptions
from .config import build_ansi_color_table from .config import build_ansi_color_table
from .constants import appname, is_macos, wakeup from .constants import appname, is_macos, shell_path, wakeup
from .fast_data_types import ( from .fast_data_types import (
BGIMAGE_PROGRAM, BLIT_PROGRAM, CELL_BG_PROGRAM, CELL_FG_PROGRAM, BGIMAGE_PROGRAM, BLIT_PROGRAM, CELL_BG_PROGRAM, CELL_FG_PROGRAM,
CELL_PROGRAM, CELL_SPECIAL_PROGRAM, CURSOR_BEAM, CURSOR_BLOCK, CELL_PROGRAM, CELL_SPECIAL_PROGRAM, CURSOR_BEAM, CURSOR_BLOCK,
@ -46,8 +46,8 @@ from .types import MouseEvent, WindowGeometry, ac
from .typing import BossType, ChildType, EdgeLiteral, TabType, TypedDict from .typing import BossType, ChildType, EdgeLiteral, TabType, TypedDict
from .utils import ( from .utils import (
get_primary_selection, kitty_ansi_sanitizer_pat, load_shaders, log_error, get_primary_selection, kitty_ansi_sanitizer_pat, load_shaders, log_error,
open_cmd, open_url, parse_color_set, resolve_custom_file, sanitize_title, open_cmd, open_url, parse_color_set, path_from_osc7_url,
set_primary_selection resolve_custom_file, sanitize_title, set_primary_selection
) )
MatchPatternType = Union[Pattern[str], Tuple[Pattern[str], Optional[Pattern[str]]]] MatchPatternType = Union[Pattern[str], Tuple[Pattern[str], Optional[Pattern[str]]]]
@ -323,6 +323,7 @@ def cmd_output(screen: Screen, which: CommandOutput = CommandOutput.last_run, as
def process_remote_print(msg: str) -> str: def process_remote_print(msg: str) -> str:
from base64 import standard_b64decode from base64 import standard_b64decode
from .cli import green from .cli import green
text = standard_b64decode(msg).decode('utf-8', 'replace') text = standard_b64decode(msg).decode('utf-8', 'replace')
return text.replace('\x1b', green(r'\e')).replace('\a', green(r'\a')).replace('\0', green(r'\0')) return text.replace('\x1b', green(r'\e')).replace('\a', green(r'\a')).replace('\0', green(r'\0'))
@ -1138,6 +1139,23 @@ class Window:
def cwd_of_child(self) -> Optional[str]: def cwd_of_child(self) -> Optional[str]:
return self.child.foreground_cwd or self.child.current_cwd return self.child.foreground_cwd or self.child.current_cwd
def modify_argv_for_launch_with_cwd(self, argv: List[str]) -> str:
if argv[0] != shell_path or not self.screen.last_reported_cwd:
return self.cwd_of_child or ''
from kittens.ssh.main import is_kitten_cmdline, set_cwd_in_cmdline
ssh_kitten_cmdline: List[str] = []
for p in self.child.foreground_processes:
q = list(p['cmdline'] or ())
if is_kitten_cmdline(q):
ssh_kitten_cmdline = q
break
if ssh_kitten_cmdline:
cwd = path_from_osc7_url(self.screen.last_reported_cwd)
if cwd:
set_cwd_in_cmdline(path_from_osc7_url(self.screen.last_reported_cwd), ssh_kitten_cmdline)
argv[:] = ssh_kitten_cmdline
return self.cwd_of_child or ''
def pipe_data(self, text: str, has_wrap_markers: bool = False) -> PipeData: def pipe_data(self, text: str, has_wrap_markers: bool = False) -> PipeData:
text = text or '' text = text or ''
if has_wrap_markers: if has_wrap_markers: