Implement window matching for remote control

This commit is contained in:
Kovid Goyal 2018-01-08 13:01:57 +05:30
parent 4f56cce727
commit 464c7a80e2
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 61 additions and 7 deletions

View File

@ -2,6 +2,7 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
import re
from gettext import gettext as _
from weakref import WeakValueDictionary
@ -102,6 +103,15 @@ class Boss:
'tabs': list(tm.list_tabs()),
}
def match_windows(self, match):
field, exp = match.split(':', 1)
pat = re.compile(exp)
for os_window_id, tm in self.os_window_map.items():
for tab in tm:
for window in tab:
if window.matches(field, pat):
yield window
def _new_os_window(self, args, cwd_from=None):
sw = self.args_to_special_window(args, cwd_from) if args else None
startup_session = create_session(self.opts, special_window=sw, cwd_from=cwd_from)

View File

@ -28,7 +28,7 @@ def cmd(short_desc, desc=None, options_spec=None, no_response=False):
def parse_subcommand_cli(func, args):
opts, items = parse_args(args[1:], func.options_spec or '\n'.format, '...', func.desc, '{} @ {}'.format(appname, func.name))
opts, items = parse_args(args[1:], (func.options_spec or '\n').format, '...', func.desc, '{} @ {}'.format(appname, func.name))
return opts, items
@ -37,8 +37,8 @@ def parse_subcommand_cli(func, args):
'List all windows. The list is returned as JSON tree. The top-level is a list of'
' operating system {appname} windows. Each OS window has an |_ id| and a list'
' of |_ tabs|. Each tab has its own |_ id|, a |_ title| and a list of |_ windows|.'
' Each window has an |_ id|, |_ title|, |_ current working directory| and |_ process id (PID)| of'
' the process running in it, and, on Linux, the command-line used to launch it.\n\n'
' Each window has an |_ id|, |_ title|, |_ current working directory|, |_ process id (PID)| and'
' |_ command-line| of the process running in the window.\n\n'
'You can use these criteria to select windows/tabs for the other commands.'.format(appname=appname)
)
def cmd_ls(global_opts, opts, args):
@ -51,17 +51,38 @@ def ls(boss, window):
return data
MATCH_WINDOW_OPTION = '''\
--match
The window to match. Match specifications are of the form:
|_ field:regexp|. Where field can be one of: id, title, pid, cwd, cmdline.
You can use the |_ ls| command to get a list of windows. Note that for
numeric fields such as id and pid the expression is interpreted as a number,
not a regular expression.
'''
@cmd(
'Send arbitrary text to the specified window',
'Send arbitrary text specified windows',
'Send arbitrary text specified windows. The text follows Python'
' escaping rules. So you can use escapes like |_ \\x1b| to send control codes'
' 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,
no_response=True
)
def cmd_send_text(global_opts, opts, args):
return {'text': ' '.join(args)}
return {'text': ' '.join(args), 'match': opts.match}
def send_text(boss, window, payload):
window = boss.active_window or window
window.write_to_child(parse_send_text_bytes(payload['text']))
windows = [boss.active_window]
match = payload['match']
if match:
windows = tuple(boss.match_windows(match))
for window in windows:
if window is not None:
window.write_to_child(parse_send_text_bytes(payload['text']))
cmap = {v.name: v for v in globals().values() if hasattr(v, 'is_cmd')}

View File

@ -124,6 +124,29 @@ class Window:
cwd=cwd, cmdline=cmdline
)
def matches(self, field, pat):
if field == 'id':
return pat.pattern == str(self.id)
if field == 'pid':
return pat.pattern == str(self.child.pid)
if field == 'title':
return pat.search(self.override_title or self.title) is not None
if field in 'cwd':
try:
cwd = cwd_of_process(self.child.pid)
except Exception:
return False
return pat.search(cwd) is not None
if field == 'cmdline':
try:
cmdline = cmdline_of_process(self.child.pid)
except Exception:
return False
for x in cmdline:
if pat.search(x) is not None:
return True
return False
def set_visible_in_layout(self, window_idx, val):
val = bool(val)
if val is not self.is_visible_in_layout: