@send-text allow sending from files and stdin

This commit is contained in:
Kovid Goyal 2018-01-10 10:37:43 +05:30
parent 6dde573ed9
commit 7b33a87725
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
2 changed files with 81 additions and 20 deletions

View File

@ -12,7 +12,7 @@ from .cli import emph, parse_args
from .config import parse_send_text_bytes
from .constants import appname, version
from .tabs import SpecialWindow
from .utils import read_with_timeout
from .utils import non_blocking_read, read_with_timeout
def cmd(short_desc, desc=None, options_spec=None, no_response=False):
@ -80,24 +80,76 @@ for that window is used.
' and |_ \\u21fa| to send unicode characters. If you use the |_ --match| option'
' the text will be sent to all matched windows. By default, text is sent to'
' only the currently active window.',
options_spec=MATCH_WINDOW_OPTION,
options_spec=MATCH_WINDOW_OPTION + '''\n
--stdin
type=bool-set
Read the text to be sent from |_ stdin|. Note that in this case the text is sent as is,
not interpreted for escapes. If stdin is a terminal, you can press Ctrl-D to end reading.
--from-file
Path to a file whose contents you wish to send. Note that in this case the file contents
are sent as is, not interpreted for escapes.
''',
no_response=True
)
def cmd_send_text(global_opts, opts, args):
text = ' '.join(args)
limit = 1024
if len(text) <= limit:
return {'text': ' '.join(args), 'match': opts.match}
ret = {'match': opts.match, 'is_binary': False}
# Overly large escape sequences are ignored by kitty, so chunk up the
# data
def pipe(src=sys.stdin):
ret['is_binary'] = True
import select
with non_blocking_read() as fd:
keep_going = True
while keep_going:
rd = select.select([fd], [], [])
if rd:
data = sys.stdin.buffer.read()
if not data:
break
data = data.decode('utf-8')
if '\x04' in data:
data = data[:data.index('\x04')]
keep_going = False
while data:
ret['text'] = data[:limit]
yield ret
data = data[limit:]
else:
break
def chunks():
nonlocal text
def chunks(text):
ret['is_binary'] = False
while text:
yield {'text': text[:limit], 'match': opts.match}
ret['text'] = text[:limit]
yield ret
text = text[limit:]
return chunks()
def file_pipe(path):
ret['is_binary'] = True
with open(path, encoding='utf-8') as f:
while True:
data = f.read(limit)
if not data:
break
ret['text'] = data
yield ret
sources = []
if opts.stdin:
sources.append(pipe())
if opts.from_file:
sources.append(file_pipe(opts.from_file))
text = ' '.join(args)
sources.append(chunks(text))
def chain():
for src in sources:
yield from src
return chain()
def send_text(boss, window, payload):
@ -105,9 +157,10 @@ def send_text(boss, window, payload):
match = payload['match']
if match:
windows = tuple(boss.match_windows(match))
data = payload['text'].encode('utf-8') if payload['is_binary'] else parse_send_text_bytes(payload['text'])
for window in windows:
if window is not None:
window.write_to_child(parse_send_text_bytes(payload['text']))
window.write_to_child(data)
@cmd(

View File

@ -267,18 +267,27 @@ def single_instance(group_id=None):
return True
def read_with_timeout(more_needed, timeout=10, src=sys.stdin):
@contextmanager
def non_blocking_read(src=sys.stdin):
import termios
import tty
import fcntl
import select
fd = src.fileno()
old = termios.tcgetattr(fd)
if src.isatty():
old = termios.tcgetattr(fd)
tty.setraw(fd)
oldfl = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, oldfl | os.O_NONBLOCK)
tty.setraw(fd)
yield fd
if src.isatty():
termios.tcsetattr(fd, termios.TCSADRAIN, old)
fcntl.fcntl(fd, fcntl.F_SETFL, oldfl)
def read_with_timeout(more_needed, timeout=10, src=sys.stdin):
import select
start_time = monotonic()
try:
with non_blocking_read(src) as fd:
while timeout > monotonic() - start_time:
rd = select.select([fd], [], [], max(0, timeout - (monotonic() - start_time)))[0]
if rd:
@ -287,6 +296,5 @@ def read_with_timeout(more_needed, timeout=10, src=sys.stdin):
break # eof
if not more_needed(data):
break
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old)
fcntl.fcntl(fd, fcntl.F_SETFL, oldfl)
else:
break