parent
051374cd55
commit
8a4b326127
@ -19,6 +19,8 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
|
|||||||
- ssh kitten: Support systems where the login shell is a non-POSIX shell
|
- ssh kitten: Support systems where the login shell is a non-POSIX shell
|
||||||
(:iss:`3405`)
|
(:iss:`3405`)
|
||||||
|
|
||||||
|
- ssh kitten: Add completion (:iss:`3760`)
|
||||||
|
|
||||||
- ssh kitten: Fix "Connection closed" message being printed by ssh when running
|
- ssh kitten: Fix "Connection closed" message being printed by ssh when running
|
||||||
remote commands
|
remote commands
|
||||||
|
|
||||||
|
|||||||
@ -162,8 +162,103 @@ def option_help_map() -> Dict[str, str]:
|
|||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
def complete_choices(ans: Completions, prefix: str, title: str, key: str, comma_separated: bool) -> None:
|
# option names {{{
|
||||||
choices = {}
|
@run_once
|
||||||
|
def option_names() -> Tuple[str, ...]:
|
||||||
|
return tuple(filter(None, (
|
||||||
|
line.strip() for line in '''
|
||||||
|
AddKeysToAgent
|
||||||
|
AddressFamily
|
||||||
|
BatchMode
|
||||||
|
BindAddress
|
||||||
|
CanonicalDomains
|
||||||
|
CanonicalizeFallbackLocal
|
||||||
|
CanonicalizeHostname
|
||||||
|
CanonicalizeMaxDots
|
||||||
|
CanonicalizePermittedCNAMEs
|
||||||
|
CASignatureAlgorithms
|
||||||
|
CertificateFile
|
||||||
|
ChallengeResponseAuthentication
|
||||||
|
CheckHostIP
|
||||||
|
Ciphers
|
||||||
|
ClearAllForwardings
|
||||||
|
Compression
|
||||||
|
ConnectionAttempts
|
||||||
|
ConnectTimeout
|
||||||
|
ControlMaster
|
||||||
|
ControlPath
|
||||||
|
ControlPersist
|
||||||
|
DynamicForward
|
||||||
|
EscapeChar
|
||||||
|
ExitOnForwardFailure
|
||||||
|
FingerprintHash
|
||||||
|
ForwardAgent
|
||||||
|
ForwardX11
|
||||||
|
ForwardX11Timeout
|
||||||
|
ForwardX11Trusted
|
||||||
|
GatewayPorts
|
||||||
|
GlobalKnownHostsFile
|
||||||
|
GSSAPIAuthentication
|
||||||
|
GSSAPIDelegateCredentials
|
||||||
|
HashKnownHosts
|
||||||
|
Host
|
||||||
|
HostbasedAcceptedAlgorithms
|
||||||
|
HostbasedAuthentication
|
||||||
|
HostKeyAlgorithms
|
||||||
|
HostKeyAlias
|
||||||
|
Hostname
|
||||||
|
IdentitiesOnly
|
||||||
|
IdentityAgent
|
||||||
|
IdentityFile
|
||||||
|
IPQoS
|
||||||
|
KbdInteractiveAuthentication
|
||||||
|
KbdInteractiveDevices
|
||||||
|
KexAlgorithms
|
||||||
|
KnownHostsCommand
|
||||||
|
LocalCommand
|
||||||
|
LocalForward
|
||||||
|
LogLevel
|
||||||
|
MACs
|
||||||
|
Match
|
||||||
|
NoHostAuthenticationForLocalhost
|
||||||
|
NumberOfPasswordPrompts
|
||||||
|
PasswordAuthentication
|
||||||
|
PermitLocalCommand
|
||||||
|
PermitRemoteOpen
|
||||||
|
PKCS11Provider
|
||||||
|
Port
|
||||||
|
PreferredAuthentications
|
||||||
|
ProxyCommand
|
||||||
|
ProxyJump
|
||||||
|
ProxyUseFdpass
|
||||||
|
PubkeyAcceptedAlgorithms
|
||||||
|
PubkeyAuthentication
|
||||||
|
RekeyLimit
|
||||||
|
RemoteCommand
|
||||||
|
RemoteForward
|
||||||
|
RequestTTY
|
||||||
|
SendEnv
|
||||||
|
ServerAliveInterval
|
||||||
|
ServerAliveCountMax
|
||||||
|
SetEnv
|
||||||
|
StreamLocalBindMask
|
||||||
|
StreamLocalBindUnlink
|
||||||
|
StrictHostKeyChecking
|
||||||
|
TCPKeepAlive
|
||||||
|
Tunnel
|
||||||
|
TunnelDevice
|
||||||
|
UpdateHostKeys
|
||||||
|
User
|
||||||
|
UserKnownHostsFile
|
||||||
|
VerifyHostKeyDNS
|
||||||
|
VisualHostKey
|
||||||
|
XAuthLocation
|
||||||
|
'''.splitlines())))
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
def complete_choices(ans: Completions, prefix: str, title: str, choices: Iterable[str], comma_separated: bool = False) -> None:
|
||||||
|
matches: Dict[str, str] = {}
|
||||||
word_transforms = {}
|
word_transforms = {}
|
||||||
effective_prefix = prefix
|
effective_prefix = prefix
|
||||||
hidden_prefix = ''
|
hidden_prefix = ''
|
||||||
@ -172,15 +267,19 @@ def complete_choices(ans: Completions, prefix: str, title: str, key: str, comma_
|
|||||||
hidden_prefix = ','.join(prefix.split(',')[:-1])
|
hidden_prefix = ','.join(prefix.split(',')[:-1])
|
||||||
if hidden_prefix:
|
if hidden_prefix:
|
||||||
hidden_prefix += ','
|
hidden_prefix += ','
|
||||||
for line in lines_from_command('ssh', '-Q', key):
|
for q in choices:
|
||||||
q = line.strip()
|
|
||||||
if q.startswith(effective_prefix):
|
if q.startswith(effective_prefix):
|
||||||
if comma_separated:
|
if comma_separated:
|
||||||
tq = q
|
tq = q
|
||||||
q = hidden_prefix + q + ','
|
q = hidden_prefix + q + ','
|
||||||
word_transforms[q] = tq
|
word_transforms[q] = tq
|
||||||
choices[q] = ''
|
matches[q] = ''
|
||||||
ans.add_match_group(title, choices, trailing_space=not comma_separated, word_transforms=word_transforms)
|
ans.add_match_group(title, matches, trailing_space=not comma_separated, word_transforms=word_transforms)
|
||||||
|
|
||||||
|
|
||||||
|
def complete_q_choices(ans: Completions, prefix: str, title: str, key: str, comma_separated: bool) -> None:
|
||||||
|
choices = (line.strip() for line in lines_from_command('ssh', '-Q', key))
|
||||||
|
complete_choices(ans, prefix, title, choices, comma_separated)
|
||||||
|
|
||||||
|
|
||||||
def complete_arg(ans: Completions, option_flag: str, prefix: str = '') -> None:
|
def complete_arg(ans: Completions, option_flag: str, prefix: str = '') -> None:
|
||||||
@ -189,11 +288,20 @@ def complete_arg(ans: Completions, option_flag: str, prefix: str = '') -> None:
|
|||||||
if option_name.endswith('file') or option_name.endswith('path'):
|
if option_name.endswith('file') or option_name.endswith('path'):
|
||||||
return complete_files_and_dirs(ans, prefix, option_name)
|
return complete_files_and_dirs(ans, prefix, option_name)
|
||||||
choices = {
|
choices = {
|
||||||
'mac_spec': ('MAC algorithms', 'mac', True),
|
'mac_spec': ('MAC algorithm', 'mac', True),
|
||||||
'cipher_spec': ('encryption ciphers', 'cipher', True),
|
'cipher_spec': ('encryption cipher', 'cipher', True),
|
||||||
|
'query_option': ('query option', 'help', False),
|
||||||
}
|
}
|
||||||
if option_name in choices:
|
if option_name in choices:
|
||||||
return complete_choices(ans, prefix, *choices[option_name])
|
return complete_q_choices(ans, prefix, *choices[option_name])
|
||||||
|
if option_name == 'destination':
|
||||||
|
return complete_destination(ans, prefix)
|
||||||
|
if option_name == 'ctl_cmd':
|
||||||
|
return complete_choices(ans, prefix, 'control command', ('check', 'forward', 'cancel', 'exit'))
|
||||||
|
if option_name == 'option':
|
||||||
|
matches = (x+'=' for x in option_names() if x.startswith(prefix))
|
||||||
|
word_transforms = {x+'=': x for x in option_names()}
|
||||||
|
ans.add_match_group('configure file option', matches, trailing_space=False, word_transforms=word_transforms)
|
||||||
|
|
||||||
|
|
||||||
def complete_destination(ans: Completions, prefix: str = '') -> None:
|
def complete_destination(ans: Completions, prefix: str = '') -> None:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user