Start moving clipboard kitten to kitty-tool

This commit is contained in:
Kovid Goyal 2022-11-25 12:58:10 +05:30
parent fbce5e7524
commit 4d3f3b5e91
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
9 changed files with 167 additions and 28 deletions

View File

@ -8,7 +8,7 @@ import subprocess
import sys
from contextlib import contextmanager, suppress
from functools import lru_cache
from typing import Dict, Iterator, List, Set, Tuple, Union, Sequence
from typing import Any, Dict, Iterator, List, Optional, Sequence, Set, Tuple, Union
import kitty.constants as kc
from kittens.tui.operations import Mode
@ -50,10 +50,24 @@ def replace(template: str, **kw: str) -> str:
# Completions {{{
@lru_cache
def kitten_cli_docs(kitten: str) -> Any:
from kittens.runner import get_kitten_cli_docs
return get_kitten_cli_docs(kitten)
@lru_cache
def go_options_for_kitten(kitten: str) -> Tuple[Sequence[GoOption], Optional[CompletionSpec]]:
kcd = kitten_cli_docs(kitten)
if kcd:
ospec = kcd['options']
return (tuple(go_options_for_seq(parse_option_spec(ospec())[0])), kcd.get('args_completion'))
return (), None
def generate_kittens_completion() -> None:
from kittens.runner import (
all_kitten_names, get_kitten_cli_docs, get_kitten_wrapper_of,
)
from kittens.runner import all_kitten_names, get_kitten_wrapper_of
for kitten in sorted(all_kitten_names()):
kn = 'kitten_' + kitten
print(f'{kn} := plus_kitten.AddSubCommand(&cli.Command{{Name:"{kitten}", Group: "Kittens"}})')
@ -62,12 +76,10 @@ def generate_kittens_completion() -> None:
print(f'{kn}.ArgCompleter = cli.CompletionForWrapper("{serialize_as_go_string(wof)}")')
print(f'{kn}.OnlyArgsAllowed = true')
continue
kcd = get_kitten_cli_docs(kitten)
if kcd:
ospec = kcd['options']
for opt in go_options_for_seq(parse_option_spec(ospec())[0]):
gopts, ac = go_options_for_kitten(kitten)
if gopts or ac:
for opt in gopts:
print(opt.as_option(kn))
ac = kcd.get('args_completion')
if ac is not None:
print(''.join(ac.as_go_code(kn + '.ArgCompleter', ' = ')))
else:
@ -265,6 +277,48 @@ def go_code_for_remote_command(name: str, cmd: RemoteCommand, template: str) ->
# }}}
# kittens {{{
@lru_cache
def wrapped_kittens() -> Sequence[str]:
with open('shell-integration/ssh/kitty') as f:
for line in f:
if line.startswith(' wrapped_kittens="'):
val = line.strip().partition('"')[2][:-1]
return tuple(sorted(filter(None, val.split())))
raise Exception('Failed to read wrapped kittens from kitty wrapper script')
def kitten_clis() -> None:
for kitten in wrapped_kittens():
with replace_if_needed(f'tools/cmd/{kitten}/cli_generated.go'):
kcd = kitten_cli_docs(kitten)
has_underscore = '_' in kitten
print(f'package {kitten}')
print('import "kitty/tools/cli"')
print('func create_cmd(root *cli.Command, run_func cli.RunFunc) {')
print('ans := root.AddSubCommand(&cli.Command{')
print(f'Name: "{kitten}",')
print(f'ShortDescription: "{serialize_as_go_string(kcd["short_desc"])}",')
print(f'Usage: "{serialize_as_go_string(kcd["usage"])}",')
print(f'HelpText: "{serialize_as_go_string(kcd["help_text"])}",')
print('Run: run_func,')
if has_underscore:
print('Hidden: true,')
print('})')
gopts, ac = go_options_for_kitten(kitten)
for opt in gopts:
print(opt.as_option('ans'))
if ac is not None:
print(''.join(ac.as_go_code('ans.ArgCompleter', ' = ')))
if has_underscore:
print("clone := root.AddClone(ans.Group, ans)")
print('clone.Hidden = false')
print(f'clone.Name = "{serialize_as_go_string(kitten.replace("_", "-"))}"')
print('}')
# }}}
# Constants {{{
def generate_spinners() -> str:
@ -335,7 +389,12 @@ var DocTitleMap = map[string]string{serialize_go_dict(ref_map['doc'])}
@contextmanager
def replace_if_needed(path: str, show_diff: bool = False) -> Iterator[io.StringIO]:
buf = io.StringIO()
yield buf
origb = sys.stdout
sys.stdout = buf
try:
yield buf
finally:
sys.stdout = origb
orig = ''
with suppress(FileNotFoundError), open(path, 'r') as f:
orig = f.read()
@ -391,21 +450,15 @@ func add_rc_global_opts(cmd *cli.Command) {{
def update_completion() -> None:
orig = sys.stdout
try:
with replace_if_needed('tools/cmd/completion/kitty_generated.go') as f:
sys.stdout = f
generate_completions_for_kitty()
with replace_if_needed('tools/cmd/edit_in_kitty/launch_generated.go') as f:
sys.stdout = f
print('package edit_in_kitty')
print('import "kitty/tools/cli"')
print('func AddCloneSafeOpts(cmd *cli.Command) {')
completion_for_launch_wrappers('cmd')
print(''.join(CompletionSpec.from_string('type:file mime:text/* group:"Text files"').as_go_code('cmd.ArgCompleter', ' = ')))
print('}')
finally:
sys.stdout = orig
with replace_if_needed('tools/cmd/completion/kitty_generated.go'):
generate_completions_for_kitty()
with replace_if_needed('tools/cmd/edit_in_kitty/launch_generated.go'):
print('package edit_in_kitty')
print('import "kitty/tools/cli"')
print('func AddCloneSafeOpts(cmd *cli.Command) {')
completion_for_launch_wrappers('cmd')
print(''.join(CompletionSpec.from_string('type:file mime:text/* group:"Text files"').as_go_code('cmd.ArgCompleter', ' = ')))
print('}')
def define_enum(package_name: str, type_name: str, items: str, underlying_type: str = 'uint') -> str:
@ -496,6 +549,7 @@ def main() -> None:
f.write(generate_spinners())
update_completion()
update_at_commands()
kitten_clis()
print(json.dumps(changed, indent=2))

View File

@ -147,3 +147,4 @@ elif __name__ == '__doc__':
cd['usage'] = usage
cd['options'] = OPTIONS
cd['help_text'] = help_text
cd['short_desc'] = 'Copy/paste with the system clipboard, even over SSH'

View File

@ -216,6 +216,23 @@ get_docs_ref_map(PyObject *self UNUSED, PyObject *args UNUSED) {
return PyBytes_FromStringAndSize(docs_ref_map, sizeof(docs_ref_map));
}
#include "wrapped_kitten_names_generated.h"
static PyObject*
wrapped_kittens(PyObject *self UNUSED, PyObject *args UNUSED) {
PyObject *ans = PyFrozenSet_New(NULL);
if (ans != NULL) {
for (int i = 0; wrapped_kitten_names[i] != NULL; i++) {
PyObject *n = PyUnicode_FromString(wrapped_kitten_names[i]);
if (n == NULL) break;
if (PySet_Add(ans, n) != 0) { Py_DECREF(n); break; }
Py_DECREF(n);
}
}
if (PyErr_Occurred()) { Py_CLEAR(ans); }
return ans;
}
static PyMethodDef module_methods[] = {
{"wcwidth", (PyCFunction)wcwidth_wrap, METH_O, ""},
{"get_docs_ref_map", (PyCFunction)get_docs_ref_map, METH_NOARGS, ""},
@ -234,6 +251,7 @@ static PyMethodDef module_methods[] = {
{"locale_is_valid", (PyCFunction)locale_is_valid, METH_VARARGS, ""},
{"shm_open", (PyCFunction)py_shm_open, METH_VARARGS, ""},
{"shm_unlink", (PyCFunction)py_shm_unlink, METH_VARARGS, ""},
{"wrapped_kitten_names", (PyCFunction)wrapped_kittens, METH_NOARGS, ""},
#ifdef __APPLE__
METHODB(user_cache_dir, METH_NOARGS),
METHODB(process_group_map, METH_NOARGS),

View File

@ -1,8 +1,8 @@
import termios
from ctypes import Array, c_ubyte
from typing import (
Any, Callable, Dict, List, NewType, Optional, Tuple, TypedDict,
Union, Iterator
Any, Callable, Dict, FrozenSet, Iterator, List, NewType, Optional, Tuple, TypedDict,
Union,
)
from kitty.boss import Boss
@ -1493,3 +1493,4 @@ def get_clipboard_mime(ct: int, mime: Optional[str], callback: Callable[[bytes],
def run_with_activation_token(func: Callable[[str], None]) -> None: ...
def make_x11_window_a_dock_window(x11_window_id: int, strut: Tuple[int, int, int, int, int, int, int, int, int, int, int, int]) -> None: ...
def unicode_database_version() -> Tuple[int, int, int]: ...
def wrapped_kittens() -> FrozenSet[str]: ...

View File

@ -860,11 +860,37 @@ def build_ref_map() -> str:
return dest
@lru_cache
def wrapped_kittens() -> Sequence[str]:
with open('shell-integration/ssh/kitty') as f:
for line in f:
if line.startswith(' wrapped_kittens="'):
val = line.strip().partition('"')[2][:-1]
return tuple(sorted(filter(None, val.split())))
raise Exception('Failed to read wrapped kittens from kitty wrapper script')
def build_wrapped_kittens() -> str:
h = 'static const char* wrapped_kitten_names[] = {\n'
h += ', '.join(f'"{x}"' for x in wrapped_kittens())
h += ', NULL'
h += '\n};\n'
dest = 'kitty/wrapped_kitten_names_generated.h'
q = ''
with suppress(FileNotFoundError), open(dest) as f:
q = f.read()
if q != h:
with open(dest, 'w') as f:
f.write(h)
return dest
def build(args: Options, native_optimizations: bool = True, call_init: bool = True) -> None:
if call_init:
init_env_from_args(args, native_optimizations)
sources, headers = find_c_files()
headers.append(build_ref_map())
headers.append(build_wrapped_kittens())
compile_c_extension(
kitty_env(), 'kitty/fast_data_types', args.compilation_database, sources, headers
)

View File

@ -22,6 +22,21 @@ exec_kitty() {
die "Failed to execute kitty"
}
is_wrapped_kitten() {
wrapped_kittens="clipboard"
[ -n "$1" ] && {
case " $wrapped_kittens " in
*" $1 "*) printf "%s" "$1" ;;
esac
}
}
test "(" "$1" = "+kitten" -a -n "$(is_wrapped_kitten "$2")" ")" -o "(" "$1" = "+" -a "$2" = "kitten" -a "$(is_wrapped_kitten "$3")" ")" && {
if [ "$1" = "+kitten" ]; then shift "1"; else shift "2"; fi
exec kitty-tool "$@"
}
lock_dir=""
script_path="$(command readlink -f "$0" 2> /dev/null)"
[ $? = 0 ] || script_path="$0"

View File

@ -14,6 +14,8 @@ import (
var _ = fmt.Print
type RunFunc = func(cmd *Command, args []string) (int, error)
type Command struct {
Name, Group string
Usage, ShortDescription, HelpText string
@ -26,7 +28,7 @@ type Command struct {
// If true subcommands are ignored unless they are the first non-option argument
SubCommandMustBeFirst bool
// The entry point for this command
Run func(cmd *Command, args []string) (int, error)
Run RunFunc
// The completer for args
ArgCompleter CompletionFunc
// Stop completion processing at this arg num

View File

@ -0,0 +1,18 @@
// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>
package clipboard
import (
"fmt"
"kitty/tools/cli"
)
var _ = fmt.Print
func clipboard_main(cmd *cli.Command, args []string) (int, error) {
return 0, nil
}
func EntryPoint(parent *cli.Command) {
create_cmd(parent, clipboard_main)
}

View File

@ -4,8 +4,10 @@ package tool
import (
"fmt"
"kitty/tools/cli"
"kitty/tools/cmd/at"
"kitty/tools/cmd/clipboard"
"kitty/tools/cmd/edit_in_kitty"
"kitty/tools/cmd/update_self"
)
@ -21,4 +23,6 @@ func KittyToolEntryPoints(root *cli.Command) {
update_self.EntryPoint(root)
// edit-in-kitty
edit_in_kitty.EntryPoint(root)
// clipboard
clipboard.EntryPoint(root)
}