Implement MIME aliases for clipboard
This commit is contained in:
parent
a622a149f6
commit
6422b323c6
@ -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.
|
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
|
--wait-for-completion
|
||||||
type=bool-set
|
type=bool-set
|
||||||
Wait till the copy to clipboard is complete before exiting. Useful if running
|
Wait till the copy to clipboard is complete before exiting. Useful if running
|
||||||
|
|||||||
@ -239,6 +239,7 @@ class WriteRequest:
|
|||||||
self.currently_writing_mime = ''
|
self.currently_writing_mime = ''
|
||||||
self.current_leftover_bytes = memoryview(b'')
|
self.current_leftover_bytes = memoryview(b'')
|
||||||
self.max_size = (get_options().clipboard_max_size * 1024 * 1024) if max_size < 0 else max_size
|
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
|
self.commited = False
|
||||||
|
|
||||||
def encode_response(self, status: str = 'OK') -> bytes:
|
def encode_response(self, status: str = 'OK') -> bytes:
|
||||||
@ -254,6 +255,10 @@ class WriteRequest:
|
|||||||
self.commited = True
|
self.commited = True
|
||||||
cp = get_boss().primary_selection if self.is_primary_selection else get_boss().clipboard
|
cp = get_boss().primary_selection if self.is_primary_selection else get_boss().clipboard
|
||||||
if cp.enabled:
|
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()}
|
x = {mime: self.tempfile.create_chunker(pos.start, pos.size) for mime, pos in self.mime_map.items()}
|
||||||
cp.set_mime(x)
|
cp.set_mime(x)
|
||||||
|
|
||||||
@ -346,6 +351,13 @@ class ClipboardRequestManager:
|
|||||||
protocol_type=ProtocolType.osc_5522, id=sanitize_id(m.get('id', ''))
|
protocol_type=ProtocolType.osc_5522, id=sanitize_id(m.get('id', ''))
|
||||||
)
|
)
|
||||||
self.handle_write_request(self.in_flight_write_request)
|
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':
|
elif typ == 'wdata':
|
||||||
wr = self.in_flight_write_request
|
wr = self.in_flight_write_request
|
||||||
w = get_boss().window_id_map.get(self.window_id)
|
w = get_boss().window_id_map.get(self.window_id)
|
||||||
|
|||||||
@ -139,15 +139,23 @@ func (self *Output) commit() {
|
|||||||
self.dest = nil
|
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) {
|
if utils.Contains(available_mimes, self.mime_type) {
|
||||||
self.remote_mime_type = self.mime_type
|
self.remote_mime_type = self.mime_type
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if self.mime_type == "." {
|
if len(aliases[self.mime_type]) > 0 {
|
||||||
self.remote_mime_type = "."
|
for _, alias := range aliases[self.mime_type] {
|
||||||
|
if utils.Contains(available_mimes, alias) {
|
||||||
|
self.remote_mime_type = alias
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if images.EncodableImageTypes[self.mime_type] {
|
if images.EncodableImageTypes[self.mime_type] {
|
||||||
for _, mt := range available_mimes {
|
for _, mt := range available_mimes {
|
||||||
if images.DecodableImageTypes[mt] {
|
if images.DecodableImageTypes[mt] {
|
||||||
@ -242,6 +250,19 @@ func parse_escape_code(etype loop.EscapeCodeType, data []byte) (metadata map[str
|
|||||||
return
|
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) {
|
func run_get_loop(opts *Options, args []string) (err error) {
|
||||||
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking)
|
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -253,6 +274,10 @@ func run_get_loop(opts *Options, args []string) (err error) {
|
|||||||
requested_mimes := make(map[string]*Output)
|
requested_mimes := make(map[string]*Output)
|
||||||
reading_available_mimes := true
|
reading_available_mimes := true
|
||||||
outputs := make([]*Output, len(args))
|
outputs := make([]*Output, len(args))
|
||||||
|
aliases, merr := parse_aliases(opts.Alias)
|
||||||
|
if merr != nil {
|
||||||
|
return merr
|
||||||
|
}
|
||||||
|
|
||||||
for i, arg := range args {
|
for i, arg := range args {
|
||||||
outputs[i] = &Output{arg: arg, arg_is_stream: arg == "/dev/stdout" || arg == "/dev/stderr", ext: filepath.Ext(arg)}
|
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")
|
return fmt.Errorf("The clipboard is empty")
|
||||||
}
|
}
|
||||||
for _, o := range outputs {
|
for _, o := range outputs {
|
||||||
err = o.assign_mime_type(available_mimes)
|
err = o.assign_mime_type(available_mimes, aliases)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"kitty/tools/tui/loop"
|
"kitty/tools/tui/loop"
|
||||||
"kitty/tools/utils"
|
"kitty/tools/utils"
|
||||||
@ -23,13 +24,17 @@ type Input struct {
|
|||||||
mime_type string
|
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)
|
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var waiting_for_write loop.IdType
|
var waiting_for_write loop.IdType
|
||||||
var buf [4096]byte
|
var buf [4096]byte
|
||||||
|
aliases, aerr := parse_aliases(opts.Alias)
|
||||||
|
if aerr != nil {
|
||||||
|
return aerr
|
||||||
|
}
|
||||||
|
|
||||||
lp.OnInitialize = func() (string, error) {
|
lp.OnInitialize = func() (string, error) {
|
||||||
waiting_for_write = lp.QueueWriteString(encode(map[string]string{"type": "write"}, ""))
|
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 err != nil {
|
||||||
if errors.Is(err, io.EOF) {
|
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:]
|
inputs = inputs[1:]
|
||||||
if len(inputs) == 0 {
|
if len(inputs) == 0 {
|
||||||
lp.QueueWriteString(encode(map[string]string{"type": "wdata"}, ""))
|
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]
|
to_process[i] = inputs[i]
|
||||||
}
|
}
|
||||||
return write_loop(to_process)
|
return write_loop(to_process, opts)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user