Command line tools shouldnt need to resolve documentation refs

Instead use a new kitty+doc protocol and have kitty navigate to the
docs. Uses a default open action for the protocol, which can be
overriden by the user should they so desire.
This commit is contained in:
Kovid Goyal 2022-08-19 14:35:17 +05:30
parent 0b66f20934
commit 314dd97059
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
5 changed files with 50 additions and 14 deletions

View File

@ -64,6 +64,10 @@ some special variables, documented below:
``FRAGMENT``
The fragment (unquoted), if any of the URL or the empty string.
``URL_PATH``
The path, query and fragment portions of the URL, without any
unquoting.
.. note::
You can use the :opt:`action_alias` option just as in :file:`kitty.conf` to

View File

@ -11,12 +11,12 @@ from typing import (
)
from .cli_stub import CLIOptions
from .conf.types import resolve_ref
from .conf.utils import resolve_config
from .constants import (
appname, clear_handled_signals, defconf, is_macos, str_version, website_url
)
from .options.types import Options as KittyOpts
from .types import run_once
from .typing import BadLineType, TypedDict
@ -171,22 +171,26 @@ def doc(x: str) -> str:
return website_url(x)
@run_once
def hostname() -> str:
import socket
return socket.gethostname() or 'localhost'
def ref_hyperlink(x: str, prefix: str = '') -> str:
t, q = text_and_target(x)
url = f'kitty+doc://{hostname()}/#ref={prefix}{q}'
return hyperlink_for_url(url, t)
@role
def ref(x: str) -> str:
t, q = text_and_target(x)
url = resolve_ref(q)
if url:
return hyperlink_for_url(url, t)
return t
return ref_hyperlink(x)
@role
def ac(x: str) -> str:
t, q = text_and_target(x)
url = resolve_ref(q)
if url:
return hyperlink_for_url(url, t)
return t
return ref_hyperlink(x, 'action-')
OptionSpecSeq = List[Union[str, OptionDict]]

View File

@ -75,7 +75,7 @@ def parse(lines: Iterable[str]) -> Iterator[OpenAction]:
entries.append((tuple(match_criteria), tuple(raw_actions)))
with to_cmdline_implementation.filter_env_vars(
'URL', 'FILE_PATH', 'FILE', 'FRAGMENT',
'URL', 'FILE_PATH', 'FILE', 'FRAGMENT', 'URL_PATH',
EDITOR=shlex.join(get_editor()),
SHELL=shlex.join(resolved_shell(get_options()))
):
@ -97,7 +97,7 @@ def url_matches_criterion(purl: 'ParseResult', url: str, unquoted_path: str, mc:
if mc.type == 'mime':
import fnmatch
mt = guess_type(unquoted_path, allow_filesystem_access=True)
mt = guess_type(unquoted_path, allow_filesystem_access=purl.scheme in ('', 'file'))
if not mt:
return False
mt = mt.lower()
@ -169,10 +169,16 @@ def actions_for_url_from_list(url: str, actions: Iterable[OpenAction]) -> Iterat
except Exception:
return
path = unquote(purl.path)
up = purl.path
if purl.query:
up += f'?{purl.query}'
if purl.fragment:
up += f'#{purl.fragment}'
env = {
'URL': url,
'FILE_PATH': path,
'URL_PATH': up,
'FILE': posixpath.basename(path),
'FRAGMENT': unquote(purl.fragment)
}
@ -215,6 +221,15 @@ def load_launch_actions() -> Tuple[OpenAction, ...]:
return tuple(parse(f))
@run_once
def default_open_actions() -> Tuple[OpenAction, ...]:
return tuple(parse('''\
# Open kitty HTML docs links
protocol kitty+doc
action show_kitty_doc $URL_PATH
'''.splitlines()))
@run_once
def default_launch_actions() -> Tuple[OpenAction, ...]:
SHELL = resolved_shell(get_options())
@ -260,7 +275,12 @@ def actions_for_url(url: str, actions_spec: Optional[str] = None) -> Iterator[Ke
actions = load_open_actions()
else:
actions = tuple(parse(actions_spec.splitlines()))
yield from actions_for_url_from_list(url, actions)
found = False
for action in actions_for_url_from_list(url, actions):
found = True
yield action
if not found:
yield from actions_for_url_from_list(url, default_open_actions())
def actions_for_launch(url: str) -> Iterator[KeyAction]:

View File

@ -1074,8 +1074,15 @@ def safer_fork() -> int:
def docs_url(which: str = '', local_docs_root: str = '') -> str:
from urllib.parse import quote
from .constants import local_docs, website_url
from .conf.types import resolve_ref
ld = local_docs_root or local_docs()
base, frag = which.partition('#')[::2]
base = base.strip('/')
if frag.startswith('ref='):
ref = frag[4:]
which = resolve_ref(ref, lambda x: x)
base, frag = which.partition('#')[::2]
base = base.strip('/')
if ld:
base = base or 'index'
url = f'file://{ld}/' + quote(base) + '.html'

View File

@ -81,6 +81,7 @@ class TestBuild(BaseTest):
self.ae(p(), 'file:///docs/index.html')
self.ae(p('conf'), 'file:///docs/conf.html')
self.ae(p('kittens/ssh#frag'), 'file:///docs/kittens/ssh.html#frag')
self.ae(p('#ref=confloc'), 'file:///docs/conf.html#confloc')
def main() -> None: