Merge pull request #399 from nojhan/feat+RunCmdOnMatch
Feat+run cmd on match
This commit is contained in:
commit
5455ac2f3e
522
terminatorlib/plugins/run_cmd_on_match.py
Normal file
522
terminatorlib/plugins/run_cmd_on_match.py
Normal file
@ -0,0 +1,522 @@
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import GObject
|
||||
|
||||
from terminatorlib.util import dbg
|
||||
import terminatorlib.plugin as plugin
|
||||
from terminatorlib.config import Config
|
||||
from terminatorlib.translation import _
|
||||
from terminatorlib.util import get_config_dir, err, dbg, gerr
|
||||
|
||||
(CC_COL_ENABLED, CC_COL_REGEXP, CC_COL_COMMAND) = list(range(0,3))
|
||||
|
||||
AVAILABLE = ['RunCmdOnMatchMenu']
|
||||
|
||||
# For example, open scripts names outputed by python in Vim at the given line number:
|
||||
# match = r'\B(/\S+?\.py)\S{2}\sline\s(\d+)' # Python's log file matching
|
||||
# cmd = "gvim --servername IDE --remote +{1} {0}"
|
||||
|
||||
# This class is not useful as is, it needs to be forged through MetaRCOM metaclass to be useful
|
||||
# (because the API use static class properties).
|
||||
class RunCmdOnMatch(plugin.URLHandler):
|
||||
"""Template for a class that run a command when a regexp match something printed on the terminal screen."""
|
||||
capabilities = ['url_handler']
|
||||
nameopen = "Open file"
|
||||
namecopy = "Copy file path"
|
||||
|
||||
handler_name = None
|
||||
match = None
|
||||
cmd = None
|
||||
|
||||
def callback(self, url):
|
||||
assert(self.__class__.match)
|
||||
assert(self.__class__.cmd)
|
||||
|
||||
try:
|
||||
found = re.search(self.__class__.match, url)
|
||||
except Exception as e:
|
||||
dbg("ERROR while searching in the captured URL: {}".format(e))
|
||||
return None
|
||||
|
||||
if not found:
|
||||
dbg("ERROR pattern not found")
|
||||
return None
|
||||
|
||||
try:
|
||||
groups = found.groups()
|
||||
dbg("Groups: {}".format(groups))
|
||||
except Exception as e:
|
||||
dbg("ERROR while accessing groups: {}".format(e))
|
||||
return None
|
||||
|
||||
for group in groups:
|
||||
if not group:
|
||||
dbg("ERROR groups not captured correctly: {groups}".format(groups=groups))
|
||||
return None
|
||||
|
||||
try:
|
||||
runcmd = self.__class__.cmd.format(*groups)
|
||||
except Exception as e:
|
||||
err("Exception occured while formating the command: {} {}".format(type(e).__name__, e))
|
||||
|
||||
dbg("run: {cmd}".format(cmd=runcmd))
|
||||
subprocess.run(runcmd.split())
|
||||
|
||||
# To avoid the fallback to the default URL handler, use the `terminator://` protocol tag.
|
||||
# Terminator will not try to open the URL, so any string after is just for debugging.
|
||||
return "terminator://{cmd}".format(cmd=runcmd)
|
||||
|
||||
|
||||
# This metaclass is used to populate RunCmdOnMatch's static members.
|
||||
class MetaRCOM(type):
|
||||
"""A meta-class for creating RunCmdOnMatch plugins on the fly."""
|
||||
def __new__(cls, name, regexp, cmd):
|
||||
return super().__new__(cls, name, (RunCmdOnMatch,), {"match":regexp, "cmd":cmd, "handler_name":name})
|
||||
|
||||
|
||||
# Add a contextual menu for opening a preference window to configure regexp/commands.
|
||||
# FIXME this is essentially the same code than custom_commands, one need to refactor to keep a single codebase
|
||||
# (and waiting for a plugin's preference window).
|
||||
class RunCmdOnMatchMenu(plugin.MenuItem):
|
||||
"""Add custom match/commands preference setting to the terminal menu"""
|
||||
capabilities = ['terminal_menu']
|
||||
cmd_list = {}
|
||||
conf_file = os.path.join(get_config_dir(),"run_cmd_on_match")
|
||||
|
||||
def __init__( self):
|
||||
config = Config()
|
||||
sections = config.plugin_get_config(self.__class__.__name__)
|
||||
if not isinstance(sections, dict):
|
||||
return
|
||||
noord_cmds = []
|
||||
for part in sections:
|
||||
s = sections[part]
|
||||
if not ("regexp" in s and "command" in s):
|
||||
dbg("Ignoring section %s" % s)
|
||||
continue
|
||||
regexp = s["regexp"]
|
||||
command = s["command"]
|
||||
enabled = s["enabled"] and s["enabled"] or False
|
||||
if "position" in s:
|
||||
self.cmd_list[int(s["position"])] = {'enabled' : enabled,
|
||||
'regexp' : regexp,
|
||||
'command' : command
|
||||
}
|
||||
else:
|
||||
noord_cmds.append(
|
||||
{'enabled' : enabled,
|
||||
'regexp' : regexp,
|
||||
'command' : command
|
||||
}
|
||||
)
|
||||
for cmd in noord_cmds:
|
||||
self.cmd_list[len(self.cmd_list)] = cmd
|
||||
|
||||
self._load_configured_handlers()
|
||||
|
||||
|
||||
def callback(self, menuitems, menu, terminal):
|
||||
"""Add our menu items to the menu"""
|
||||
submenus = {}
|
||||
item = Gtk.MenuItem.new_with_mnemonic(_('_Run command on matches'))
|
||||
menuitems.append(item)
|
||||
|
||||
submenu = Gtk.Menu()
|
||||
item.set_submenu(submenu)
|
||||
|
||||
menuitem = Gtk.MenuItem.new_with_mnemonic(_('_Preferences'))
|
||||
menuitem.connect("activate", self.configure)
|
||||
submenu.append(menuitem)
|
||||
|
||||
menuitem = Gtk.SeparatorMenuItem()
|
||||
submenu.append(menuitem)
|
||||
|
||||
theme = Gtk.IconTheme.get_default()
|
||||
|
||||
|
||||
def _save_config(self):
|
||||
config = Config()
|
||||
config.plugin_del_config(self.__class__.__name__)
|
||||
i = 0
|
||||
for command in [ self.cmd_list[key] for key in sorted(self.cmd_list.keys()) ] :
|
||||
enabled = command['enabled']
|
||||
regexp = command['regexp']
|
||||
command = command['command']
|
||||
|
||||
item = {}
|
||||
item['enabled'] = enabled
|
||||
item['regexp'] = regexp
|
||||
item['command'] = command
|
||||
item['position'] = i
|
||||
|
||||
config.plugin_set(self.__class__.__name__, regexp, item)
|
||||
i = i + 1
|
||||
config.save()
|
||||
self._load_configured_handlers()
|
||||
|
||||
|
||||
def _load_configured_handlers(self):
|
||||
"""Forge an URLhandler plugin and hide it in the available ones."""
|
||||
me = sys.modules[__name__] # Current module.
|
||||
config = Config()
|
||||
|
||||
for key,handler in [ (key,self.cmd_list[key]) for key in sorted(self.cmd_list.keys()) ] :
|
||||
# Forge a hidden/managed plugin
|
||||
# (names starting with an underscore will not be displayed in the preference/plugins window).
|
||||
rcom_name = "_RunCmdOnMatch_{}".format(key) # key is just the index
|
||||
# Make a handler class.
|
||||
RCOM = MetaRCOM(rcom_name, handler["regexp"], handler["command"])
|
||||
# Instanciate the class.
|
||||
setattr(me, rcom_name, RCOM)
|
||||
|
||||
if rcom_name not in AVAILABLE:
|
||||
AVAILABLE.append(rcom_name)
|
||||
dbg("add {} to the list of URL handlers: '{}' -> '{}'".format(rcom_name, RCOM.match, RCOM.cmd))
|
||||
|
||||
if handler['enabled'] and rcom_name not in config["enabled_plugins"]:
|
||||
config["enabled_plugins"].append(rcom_name)
|
||||
|
||||
config.save()
|
||||
|
||||
|
||||
def _execute(self, widget, data):
|
||||
command = data['command']
|
||||
if command[-1] != '\n':
|
||||
command = command + '\n'
|
||||
for terminal in data['terminals']:
|
||||
terminal.vte.feed_child(command.encode())
|
||||
|
||||
def configure(self, widget, data = None):
|
||||
ui = {}
|
||||
dbox = Gtk.Dialog(
|
||||
_("Run command on match Configuration"),
|
||||
None,
|
||||
Gtk.DialogFlags.MODAL,
|
||||
(
|
||||
_("_Cancel"), Gtk.ResponseType.REJECT,
|
||||
_("_OK"), Gtk.ResponseType.ACCEPT
|
||||
)
|
||||
)
|
||||
dbox.set_transient_for(widget.get_toplevel())
|
||||
|
||||
icon_theme = Gtk.IconTheme.get_default()
|
||||
if icon_theme.lookup_icon('terminator-run-cmd-on-match', 48, 0):
|
||||
dbox.set_icon_name('terminator-run-cmd-on-match')
|
||||
else:
|
||||
dbg('Unable to load Terminator run-cmd-on-match icon')
|
||||
icon = dbox.render_icon(Gtk.STOCK_DIALOG_INFO, Gtk.IconSize.BUTTON)
|
||||
dbox.set_icon(icon)
|
||||
|
||||
store = Gtk.ListStore(bool, str, str)
|
||||
|
||||
for command in [ self.cmd_list[key] for key in sorted(self.cmd_list.keys()) ]:
|
||||
store.append([command['enabled'], command['regexp'], command['command']])
|
||||
|
||||
treeview = Gtk.TreeView(store)
|
||||
#treeview.connect("cursor-changed", self.on_cursor_changed, ui)
|
||||
selection = treeview.get_selection()
|
||||
selection.set_mode(Gtk.SelectionMode.SINGLE)
|
||||
selection.connect("changed", self.on_selection_changed, ui)
|
||||
ui['treeview'] = treeview
|
||||
|
||||
renderer = Gtk.CellRendererToggle()
|
||||
renderer.connect('toggled', self.on_toggled, ui)
|
||||
column = Gtk.TreeViewColumn(_("Enabled"), renderer, active=CC_COL_ENABLED)
|
||||
treeview.append_column(column)
|
||||
|
||||
renderer = Gtk.CellRendererText()
|
||||
column = Gtk.TreeViewColumn(_("regexp"), renderer, text=CC_COL_REGEXP)
|
||||
treeview.append_column(column)
|
||||
|
||||
renderer = Gtk.CellRendererText()
|
||||
column = Gtk.TreeViewColumn(_("Command"), renderer, text=CC_COL_COMMAND)
|
||||
treeview.append_column(column)
|
||||
|
||||
scroll_window = Gtk.ScrolledWindow()
|
||||
scroll_window.set_size_request(500, 250)
|
||||
scroll_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
||||
scroll_window.add_with_viewport(treeview)
|
||||
|
||||
hbox = Gtk.HBox()
|
||||
hbox.pack_start(scroll_window, True, True, 0)
|
||||
dbox.vbox.pack_start(hbox, True, True, 0)
|
||||
|
||||
button_box = Gtk.VBox()
|
||||
|
||||
button = Gtk.Button(_("Top"))
|
||||
button_box.pack_start(button, False, True, 0)
|
||||
button.connect("clicked", self.on_goto_top, ui)
|
||||
button.set_sensitive(False)
|
||||
ui['button_top'] = button
|
||||
|
||||
button = Gtk.Button(_("Up"))
|
||||
button_box.pack_start(button, False, True, 0)
|
||||
button.connect("clicked", self.on_go_up, ui)
|
||||
button.set_sensitive(False)
|
||||
ui['button_up'] = button
|
||||
|
||||
button = Gtk.Button(_("Down"))
|
||||
button_box.pack_start(button, False, True, 0)
|
||||
button.connect("clicked", self.on_go_down, ui)
|
||||
button.set_sensitive(False)
|
||||
ui['button_down'] = button
|
||||
|
||||
button = Gtk.Button(_("Last"))
|
||||
button_box.pack_start(button, False, True, 0)
|
||||
button.connect("clicked", self.on_goto_last, ui)
|
||||
button.set_sensitive(False)
|
||||
ui['button_last'] = button
|
||||
|
||||
button = Gtk.Button(_("New"))
|
||||
button_box.pack_start(button, False, True, 0)
|
||||
button.connect("clicked", self.on_new, ui)
|
||||
ui['button_new'] = button
|
||||
|
||||
button = Gtk.Button(_("Edit"))
|
||||
button_box.pack_start(button, False, True, 0)
|
||||
button.set_sensitive(False)
|
||||
button.connect("clicked", self.on_edit, ui)
|
||||
ui['button_edit'] = button
|
||||
|
||||
button = Gtk.Button(_("Delete"))
|
||||
button_box.pack_start(button, False, True, 0)
|
||||
button.connect("clicked", self.on_delete, ui)
|
||||
button.set_sensitive(False)
|
||||
ui['button_delete'] = button
|
||||
|
||||
|
||||
|
||||
hbox.pack_start(button_box, False, True, 0)
|
||||
self.dbox = dbox
|
||||
dbox.show_all()
|
||||
res = dbox.run()
|
||||
if res == Gtk.ResponseType.ACCEPT:
|
||||
self.update_cmd_list(store)
|
||||
self._save_config()
|
||||
del(self.dbox)
|
||||
dbox.destroy()
|
||||
return
|
||||
|
||||
|
||||
def update_cmd_list(self, store):
|
||||
iter = store.get_iter_first()
|
||||
self.cmd_list = {}
|
||||
i=0
|
||||
while iter:
|
||||
(enabled, regexp, command) = store.get(iter,
|
||||
CC_COL_ENABLED,
|
||||
CC_COL_REGEXP,
|
||||
CC_COL_COMMAND)
|
||||
self.cmd_list[i] = {'enabled' : enabled,
|
||||
'regexp': regexp,
|
||||
'command' : command}
|
||||
iter = store.iter_next(iter)
|
||||
i = i + 1
|
||||
|
||||
|
||||
def on_toggled(self, widget, path, data):
|
||||
treeview = data['treeview']
|
||||
store = treeview.get_model()
|
||||
iter = store.get_iter(path)
|
||||
(enabled, regexp, command) = store.get(iter,
|
||||
CC_COL_ENABLED,
|
||||
CC_COL_REGEXP,
|
||||
CC_COL_COMMAND
|
||||
)
|
||||
store.set_value(iter, CC_COL_ENABLED, not enabled)
|
||||
|
||||
|
||||
def on_selection_changed(self,selection, data=None):
|
||||
treeview = selection.get_tree_view()
|
||||
(model, iter) = selection.get_selected()
|
||||
data['button_top'].set_sensitive(iter is not None)
|
||||
data['button_up'].set_sensitive(iter is not None)
|
||||
data['button_down'].set_sensitive(iter is not None)
|
||||
data['button_last'].set_sensitive(iter is not None)
|
||||
data['button_edit'].set_sensitive(iter is not None)
|
||||
data['button_delete'].set_sensitive(iter is not None)
|
||||
|
||||
def _create_command_dialog(self, enabled_var = False, regexp_var = "", command_var = ""):
|
||||
dialog = Gtk.Dialog(
|
||||
_("New Command"),
|
||||
None,
|
||||
Gtk.DialogFlags.MODAL,
|
||||
(
|
||||
_("_Cancel"), Gtk.ResponseType.REJECT,
|
||||
_("_OK"), Gtk.ResponseType.ACCEPT
|
||||
)
|
||||
)
|
||||
dialog.set_transient_for(self.dbox)
|
||||
table = Gtk.Table(3, 2)
|
||||
|
||||
label = Gtk.Label(label=_("Enabled:"))
|
||||
table.attach(label, 0, 1, 0, 1)
|
||||
enabled = Gtk.CheckButton()
|
||||
enabled.set_active(enabled_var)
|
||||
table.attach(enabled, 1, 2, 0, 1)
|
||||
|
||||
label = Gtk.Label(label=_("regexp:"))
|
||||
table.attach(label, 0, 1, 1, 2)
|
||||
regexp = Gtk.Entry()
|
||||
regexp.set_text(regexp_var)
|
||||
table.attach(regexp, 1, 2, 1, 2)
|
||||
|
||||
label = Gtk.Label(label=_("Command:"))
|
||||
table.attach(label, 0, 1, 2, 3)
|
||||
command = Gtk.Entry()
|
||||
command.set_text(command_var)
|
||||
table.attach(command, 1, 2, 2, 3)
|
||||
|
||||
dialog.vbox.pack_start(table, True, True, 0)
|
||||
dialog.show_all()
|
||||
return (dialog,enabled,regexp,command)
|
||||
|
||||
def on_new(self, button, data):
|
||||
(dialog,enabled,regexp,command) = self._create_command_dialog()
|
||||
res = dialog.run()
|
||||
item = {}
|
||||
if res == Gtk.ResponseType.ACCEPT:
|
||||
item['enabled'] = enabled.get_active()
|
||||
item['regexp'] = regexp.get_text()
|
||||
item['command'] = command.get_text()
|
||||
if item['regexp'] == '' or item['command'] == '':
|
||||
err = Gtk.MessageDialog(dialog,
|
||||
Gtk.DialogFlags.MODAL,
|
||||
Gtk.MessageType.ERROR,
|
||||
Gtk.ButtonsType.CLOSE,
|
||||
_("You need to define a regexp and command")
|
||||
)
|
||||
err.run()
|
||||
err.destroy()
|
||||
else:
|
||||
# we have a new command
|
||||
store = data['treeview'].get_model()
|
||||
iter = store.get_iter_first()
|
||||
regexp_exist = False
|
||||
while iter != None:
|
||||
if store.get_value(iter,CC_COL_REGEXP) == item['regexp']:
|
||||
regexp_exist = True
|
||||
break
|
||||
iter = store.iter_next(iter)
|
||||
if not regexp_exist:
|
||||
store.append((item['enabled'], item['regexp'], item['command']))
|
||||
else:
|
||||
gerr(_("regexp *%s* already exist") % item['regexp'])
|
||||
dialog.destroy()
|
||||
|
||||
def on_goto_top(self, button, data):
|
||||
treeview = data['treeview']
|
||||
selection = treeview.get_selection()
|
||||
(store, iter) = selection.get_selected()
|
||||
|
||||
if not iter:
|
||||
return
|
||||
firstiter = store.get_iter_first()
|
||||
store.move_before(iter, firstiter)
|
||||
|
||||
def on_go_up(self, button, data):
|
||||
treeview = data['treeview']
|
||||
selection = treeview.get_selection()
|
||||
(store, iter) = selection.get_selected()
|
||||
|
||||
if not iter:
|
||||
return
|
||||
|
||||
tmpiter = store.get_iter_first()
|
||||
|
||||
if(store.get_path(tmpiter) == store.get_path(iter)):
|
||||
return
|
||||
|
||||
while tmpiter:
|
||||
next = store.iter_next(tmpiter)
|
||||
if(store.get_path(next) == store.get_path(iter)):
|
||||
store.swap(iter, tmpiter)
|
||||
break
|
||||
tmpiter = next
|
||||
|
||||
def on_go_down(self, button, data):
|
||||
treeview = data['treeview']
|
||||
selection = treeview.get_selection()
|
||||
(store, iter) = selection.get_selected()
|
||||
|
||||
if not iter:
|
||||
return
|
||||
next = store.iter_next(iter)
|
||||
if next:
|
||||
store.swap(iter, next)
|
||||
|
||||
def on_goto_last(self, button, data):
|
||||
treeview = data['treeview']
|
||||
selection = treeview.get_selection()
|
||||
(store, iter) = selection.get_selected()
|
||||
|
||||
if not iter:
|
||||
return
|
||||
lastiter = iter
|
||||
tmpiter = store.get_iter_first()
|
||||
while tmpiter:
|
||||
lastiter = tmpiter
|
||||
tmpiter = store.iter_next(tmpiter)
|
||||
|
||||
store.move_after(iter, lastiter)
|
||||
|
||||
|
||||
def on_delete(self, button, data):
|
||||
treeview = data['treeview']
|
||||
selection = treeview.get_selection()
|
||||
(store, iter) = selection.get_selected()
|
||||
if iter:
|
||||
store.remove(iter)
|
||||
|
||||
return
|
||||
|
||||
def on_edit(self, button, data):
|
||||
treeview = data['treeview']
|
||||
selection = treeview.get_selection()
|
||||
(store, iter) = selection.get_selected()
|
||||
|
||||
if not iter:
|
||||
return
|
||||
|
||||
(dialog,enabled,regexp,command) = self._create_command_dialog(
|
||||
enabled_var = store.get_value(iter, CC_COL_ENABLED),
|
||||
regexp_var = store.get_value(iter, CC_COL_REGEXP),
|
||||
command_var = store.get_value(iter, CC_COL_COMMAND)
|
||||
)
|
||||
res = dialog.run()
|
||||
item = {}
|
||||
if res == Gtk.ResponseType.ACCEPT:
|
||||
item['enabled'] = enabled.get_active()
|
||||
item['regexp'] = regexp.get_text()
|
||||
item['command'] = command.get_text()
|
||||
if item['regexp'] == '' or item['command'] == '':
|
||||
err = Gtk.MessageDialog(dialog,
|
||||
Gtk.DialogFlags.MODAL,
|
||||
Gtk.MessageType.ERROR,
|
||||
Gtk.ButtonsType.CLOSE,
|
||||
_("You need to define a regexp and a command")
|
||||
)
|
||||
err.run()
|
||||
err.destroy()
|
||||
else:
|
||||
tmpiter = store.get_iter_first()
|
||||
regexp_exist = False
|
||||
while tmpiter != None:
|
||||
if store.get_path(tmpiter) != store.get_path(iter) and store.get_value(tmpiter,CC_COL_REGEXP) == item['regexp']:
|
||||
regexp_exist = True
|
||||
break
|
||||
tmpiter = store.iter_next(tmpiter)
|
||||
if not regexp_exist:
|
||||
store.set(iter,
|
||||
CC_COL_ENABLED,item['enabled'],
|
||||
CC_COL_REGEXP, item['regexp'],
|
||||
CC_COL_COMMAND, item['command']
|
||||
)
|
||||
else:
|
||||
gerr(_("regexp *%s* already exist") % item['regexp'])
|
||||
|
||||
dialog.destroy()
|
@ -433,7 +433,8 @@ class PrefsEditor:
|
||||
pluginlist = self.registry.get_available_plugins()
|
||||
self.plugins = {}
|
||||
for plugin in pluginlist:
|
||||
self.plugins[plugin] = self.registry.is_enabled(plugin)
|
||||
if plugin[0] != "_": # Do not display hidden plugins
|
||||
self.plugins[plugin] = self.registry.is_enabled(plugin)
|
||||
|
||||
for plugin in self.plugins:
|
||||
self.pluginiters[plugin] = liststore.append([plugin,
|
||||
|
@ -148,7 +148,7 @@ class Terminal(Gtk.VBox):
|
||||
except Exception as e:
|
||||
self.background_image = None
|
||||
self.vte.set_clear_background(True)
|
||||
err('error loading background image: %s' % e)
|
||||
err('error loading background image: %s, %s' % (type(ex).__name__,e))
|
||||
|
||||
self.background_alpha = self.config['background_darkness']
|
||||
self.vte.set_allow_hyperlink(True)
|
||||
@ -360,7 +360,7 @@ class Terminal(Gtk.VBox):
|
||||
(name, urlplugin.__class__.__name__,
|
||||
self.matches[name]))
|
||||
except Exception as ex:
|
||||
err('Exception occurred adding plugin URL match: %s' % ex)
|
||||
err('Exception occurred adding plugin URL match: %s, %s' % (type(ex).__name__, ex))
|
||||
|
||||
def match_add(self, name, match):
|
||||
"""Register a URL match"""
|
||||
@ -961,6 +961,8 @@ class Terminal(Gtk.VBox):
|
||||
url = self.vte.match_check_event(event)
|
||||
if url[0]:
|
||||
self.open_url(url, prepare=True)
|
||||
else:
|
||||
dbg("No regex match, discard event.")
|
||||
elif event.button == self.MOUSEBUTTON_MIDDLE:
|
||||
# middleclick should paste the clipboard
|
||||
# try to pass it to vte widget first though
|
||||
@ -1531,17 +1533,18 @@ class Terminal(Gtk.VBox):
|
||||
registry = plugin.PluginRegistry()
|
||||
registry.load_plugins()
|
||||
plugins = registry.get_plugins_by_capability('url_handler')
|
||||
dbg("URL handler plugins: {}".format(plugins))
|
||||
|
||||
for urlplugin in plugins:
|
||||
if match == self.matches[urlplugin.handler_name]:
|
||||
newurl = urlplugin.callback(url)
|
||||
if newurl is not None:
|
||||
dbg('Terminal::prepare_url: URL prepared by \
|
||||
%s plugin' % urlplugin.handler_name)
|
||||
if newurl: # If the plugin returns None, it's a false match.
|
||||
dbg('URL prepared by %s plugin' \
|
||||
% urlplugin.handler_name)
|
||||
url = newurl
|
||||
break
|
||||
except Exception as ex:
|
||||
err('Exception occurred preparing URL: %s' % ex)
|
||||
err('Exception occurred preparing URL: %s, %s' % (type(ex).__name__, ex))
|
||||
|
||||
return url
|
||||
|
||||
@ -1549,8 +1552,15 @@ class Terminal(Gtk.VBox):
|
||||
"""Open a given URL, conditionally unpacking it from a VTE match"""
|
||||
if prepare:
|
||||
url = self.prepare_url(url)
|
||||
dbg('open_url: URL: %s (prepared: %s)' % (url, prepare))
|
||||
dbg('URL: %s (prepared: %s)' % (url, prepare))
|
||||
|
||||
# If the URL opening is managed by the plugin: do nothing.
|
||||
# (plugins can indicate they manage the URL opening by returning a "terminator://" URI).
|
||||
if url.split(":")[0] == "terminator":
|
||||
dbg("URL opening is managed by the plugin, do nothing more.")
|
||||
return
|
||||
|
||||
# Else, call the URL handler.
|
||||
if self.config['use_custom_url_handler']:
|
||||
dbg("Using custom URL handler: %s" %
|
||||
self.config['custom_url_handler'])
|
||||
@ -1573,6 +1583,7 @@ class Terminal(Gtk.VBox):
|
||||
import webbrowser
|
||||
webbrowser.open(url)
|
||||
|
||||
|
||||
def paste_clipboard(self, primary=False):
|
||||
"""Paste one of the two clipboards"""
|
||||
for term in self.terminator.get_target_terms(self):
|
||||
|
Loading…
Reference in New Issue
Block a user