From 47b0baae8569d3fbe9054c5308b1780c04b8711c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 20 Nov 2014 11:23:11 +0530 Subject: [PATCH] Allow S&R funcs to specify they would like to be called once after all matches are found --- src/calibre/gui2/tweak_book/editor/text.py | 8 ++++++++ .../gui2/tweak_book/function_replace.py | 19 +++++++++++++++++-- src/calibre/gui2/tweak_book/search.py | 16 +++++++++++----- 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/calibre/gui2/tweak_book/editor/text.py b/src/calibre/gui2/tweak_book/editor/text.py index 7f922cc8ca..81e151dc36 100644 --- a/src/calibre/gui2/tweak_book/editor/text.py +++ b/src/calibre/gui2/tweak_book/editor/text.py @@ -343,7 +343,15 @@ class TextEdit(PlainTextEdit): if template is None: count = len(pat.findall(raw)) else: + from calibre.gui2.tweak_book.function_replace import Function + repl_is_func = isinstance(template, Function) + if repl_is_func: + template.init_env() raw, count = pat.subn(template, raw) + if repl_is_func: + from calibre.gui2.tweak_book.search import show_function_debug_output + show_function_debug_output(template) + template.end() if count > 0: start_pos = min(c.anchor(), c.position()) c.insertText(raw) diff --git a/src/calibre/gui2/tweak_book/function_replace.py b/src/calibre/gui2/tweak_book/function_replace.py index 29abcbdb92..9dee159132 100644 --- a/src/calibre/gui2/tweak_book/function_replace.py +++ b/src/calibre/gui2/tweak_book/function_replace.py @@ -11,7 +11,7 @@ from cStringIO import StringIO from PyQt5.Qt import ( pyqtSignal, QVBoxLayout, QHBoxLayout, QPlainTextEdit, QLabel, QFontMetrics, - QSize, Qt) + QSize, Qt, QApplication, QIcon) from calibre.ebooks.oeb.polish.utils import apply_func_to_match_groups from calibre.gui2 import error_dialog @@ -89,6 +89,14 @@ class Function(object): return json.loads(P('editor-functions.json', data=True, allow_user_override=False))[self.name] return self._source + def end(self): + if getattr(self.func, 'call_after_last_match', False): + oo, oe, sys.stdout, sys.stderr = sys.stdout, sys.stderr, self.debug_buf, self.debug_buf + try: + return self.func(None, self.match_index, self.context_name, self.boss.current_metadata, dictionaries, self.data, functions()) + finally: + sys.stdout, sys.stderr = oo, oe + class DebugOutput(Dialog): def __init__(self, parent=None): @@ -98,13 +106,18 @@ class DebugOutput(Dialog): def setup_ui(self): self.l = l = QVBoxLayout(self) self.text = t = QPlainTextEdit(self) + self.log_text = '' l.addWidget(t) l.addWidget(self.bb) self.bb.setStandardButtons(self.bb.Close) + self.cb = b = self.bb.addButton(_('&Copy to clipboard'), self.bb.ActionRole) + b.clicked.connect(self.copy_to_clipboard) + b.setIcon(QIcon(I('edit-copy.png'))) def show_log(self, name, text): self.setWindowTitle(_('Debug output from %s') % name) self.text.setPlainText(self.windowTitle() + '\n\n' + text) + self.log_text = text self.show() self.raise_() @@ -112,6 +125,9 @@ class DebugOutput(Dialog): fm = QFontMetrics(self.text.font()) return QSize(fm.averageCharWidth() * 120, 400) + def copy_to_clipboard(self): + QApplication.instance().clipboard().setText(self.log_text) + def builtin_functions(): for name, obj in globals().iteritems(): if name.startswith('replace_') and callable(obj): @@ -300,7 +316,6 @@ def replace_swapcase(match, number, file_name, metadata, dictionaries, data, fun return apply_func_to_match_groups(match, swapcase) if __name__ == '__main__': - from PyQt5.Qt import QApplication app = QApplication([]) FunctionEditor().exec_() del app diff --git a/src/calibre/gui2/tweak_book/search.py b/src/calibre/gui2/tweak_book/search.py index 3e5d9ba93f..0e71cef268 100644 --- a/src/calibre/gui2/tweak_book/search.py +++ b/src/calibre/gui2/tweak_book/search.py @@ -1262,10 +1262,13 @@ def run_search( if editor is None: return no_replace() for p, repl in searches: - if callable(repl): + repl_is_func = isinstance(repl, Function) + if repl_is_func: repl.init_env(current_editor_name) if editor.replace(p, repl, saved_match='gui'): - show_function_debug_output(repl) + if repl_is_func: + repl.end() + show_function_debug_output(repl) return True return no_replace(_( 'Currently selected text does not match the search query.')) @@ -1296,12 +1299,13 @@ def run_search( raw_data[n] = raw for p, repl in searches: - if callable(repl): + repl_is_func = isinstance(repl, Function) + if repl_is_func: repl.init_env() for n, syntax in lfiles.iteritems(): raw = raw_data[n] if replace: - if callable(repl): + if repl_is_func: repl.context_name = n raw, num = p.subn(repl, raw) if num > 0: @@ -1310,7 +1314,9 @@ def run_search( else: num = len(p.findall(raw)) count += num - show_function_debug_output(repl) + if repl_is_func: + repl.end() + show_function_debug_output(repl) for n in updates: raw = raw_data[n]