Implement MIME aliases for clipboard

This commit is contained in:
Kovid Goyal 2022-12-03 12:20:03 +05:30
parent a622a149f6
commit 6422b323c6
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 62 additions and 7 deletions

View File

@ -25,6 +25,16 @@ can be detected. If more than one file is specified, this option should be speci
times, once for each specified file.
--alias -a
type=list
Specify aliases for MIME types. Aliased MIME types are considered equivalent.
When copying to clipboard both the original and alias are made available on the
clipboard. When copying from clipboard if the original is not found, the alias
is used, as a fallback. Can be specified multiple times to create multiple
aliases. For example: :code:`--alias text/plain=text/x-rst` makes :code:`text/plain` an alias
of :code:`text/rst`. Aliases are not used in filter mode.
--wait-for-completion
type=bool-set
Wait till the copy to clipboard is complete before exiting. Useful if running

View File

@ -239,6 +239,7 @@ class WriteRequest:
self.currently_writing_mime = ''
self.current_leftover_bytes = memoryview(b'')
self.max_size = (get_options().clipboard_max_size * 1024 * 1024) if max_size < 0 else max_size
self.aliases: Dict[str, str] = {}
self.commited = False
def encode_response(self, status: str = 'OK') -> bytes:
@ -254,6 +255,10 @@ class WriteRequest:
self.commited = True
cp = get_boss().primary_selection if self.is_primary_selection else get_boss().clipboard
if cp.enabled:
for alias, src in self.aliases.items():
pos = self.mime_map.get(src)
if pos is not None:
self.mime_map[alias] = pos
x = {mime: self.tempfile.create_chunker(pos.start, pos.size) for mime, pos in self.mime_map.items()}
cp.set_mime(x)
@ -346,6 +351,13 @@ class ClipboardRequestManager:
protocol_type=ProtocolType.osc_5522, id=sanitize_id(m.get('id', ''))
)
self.handle_write_request(self.in_flight_write_request)
elif typ == 'walias':
wr = self.in_flight_write_request
mime = m.get('mime', '')
if mime and wr is not None:
aliases = base64.standard_b64decode(epayload).decode('utf-8').split()
for alias in aliases:
wr.aliases[alias] = mime
elif typ == 'wdata':
wr = self.in_flight_write_request
w = get_boss().window_id_map.get(self.window_id)

View File

@ -139,14 +139,22 @@ func (self *Output) commit() {
self.dest = nil
}
func (self *Output) assign_mime_type(available_mimes []string) (err error) {
func (self *Output) assign_mime_type(available_mimes []string, aliases map[string][]string) (err error) {
if self.mime_type == "." {
self.remote_mime_type = "."
return
}
if utils.Contains(available_mimes, self.mime_type) {
self.remote_mime_type = self.mime_type
return
}
if self.mime_type == "." {
self.remote_mime_type = "."
return
if len(aliases[self.mime_type]) > 0 {
for _, alias := range aliases[self.mime_type] {
if utils.Contains(available_mimes, alias) {
self.remote_mime_type = alias
return
}
}
}
if images.EncodableImageTypes[self.mime_type] {
for _, mt := range available_mimes {
@ -242,6 +250,19 @@ func parse_escape_code(etype loop.EscapeCodeType, data []byte) (metadata map[str
return
}
func parse_aliases(raw []string) (map[string][]string, error) {
ans := make(map[string][]string, len(raw))
for _, x := range raw {
k, v, found := utils.Cut(x, "=")
if !found {
return nil, fmt.Errorf("%s is not valid MIME alias specification", x)
}
ans[k] = append(ans[k], v)
ans[v] = append(ans[v], k)
}
return ans, nil
}
func run_get_loop(opts *Options, args []string) (err error) {
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking)
if err != nil {
@ -253,6 +274,10 @@ func run_get_loop(opts *Options, args []string) (err error) {
requested_mimes := make(map[string]*Output)
reading_available_mimes := true
outputs := make([]*Output, len(args))
aliases, merr := parse_aliases(opts.Alias)
if merr != nil {
return merr
}
for i, arg := range args {
outputs[i] = &Output{arg: arg, arg_is_stream: arg == "/dev/stdout" || arg == "/dev/stderr", ext: filepath.Ext(arg)}
@ -301,7 +326,7 @@ func run_get_loop(opts *Options, args []string) (err error) {
return fmt.Errorf("The clipboard is empty")
}
for _, o := range outputs {
err = o.assign_mime_type(available_mimes)
err = o.assign_mime_type(available_mimes, aliases)
if err != nil {
return err
}

View File

@ -8,6 +8,7 @@ import (
"io"
"os"
"path/filepath"
"strings"
"kitty/tools/tui/loop"
"kitty/tools/utils"
@ -23,13 +24,17 @@ type Input struct {
mime_type string
}
func write_loop(inputs []*Input) (err error) {
func write_loop(inputs []*Input, opts *Options) (err error) {
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking)
if err != nil {
return err
}
var waiting_for_write loop.IdType
var buf [4096]byte
aliases, aerr := parse_aliases(opts.Alias)
if aerr != nil {
return aerr
}
lp.OnInitialize = func() (string, error) {
waiting_for_write = lp.QueueWriteString(encode(map[string]string{"type": "write"}, ""))
@ -47,6 +52,9 @@ func write_loop(inputs []*Input) (err error) {
}
if err != nil {
if errors.Is(err, io.EOF) {
if len(aliases[i.mime_type]) > 0 {
lp.QueueWriteString(encode(map[string]string{"type": "walias", "mime": i.mime_type}, strings.Join(aliases[i.mime_type], " ")))
}
inputs = inputs[1:]
if len(inputs) == 0 {
lp.QueueWriteString(encode(map[string]string{"type": "wdata"}, ""))
@ -151,5 +159,5 @@ func run_set_loop(opts *Options, args []string) (err error) {
}
to_process[i] = inputs[i]
}
return write_loop(to_process)
return write_loop(to_process, opts)
}