From dfb16c58a60c48d045d9c11aa6617c3a16fc637c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 12 Nov 2013 12:12:23 +0530 Subject: [PATCH] Finding in the current file works --- src/calibre/gui2/tweak_book/boss.py | 7 ++++ src/calibre/gui2/tweak_book/editor/text.py | 37 ++++++++++++++++++++ src/calibre/gui2/tweak_book/editor/widget.py | 6 ++++ src/calibre/gui2/tweak_book/search.py | 29 ++++++++++++--- 4 files changed, 74 insertions(+), 5 deletions(-) diff --git a/src/calibre/gui2/tweak_book/boss.py b/src/calibre/gui2/tweak_book/boss.py index 098191c7f4..f9906ee0b6 100644 --- a/src/calibre/gui2/tweak_book/boss.py +++ b/src/calibre/gui2/tweak_book/boss.py @@ -313,6 +313,13 @@ class Boss(QObject): else: pass # marked text TODO: Implement this + pat = sp.get_regex(state) + if action == 'find': + found = editor.find(pat) + if found: + return + # TODO: Handle wrapping, depending on state['where'] + def save_book(self): c = current_container() for name, ed in editors.iteritems(): diff --git a/src/calibre/gui2/tweak_book/editor/text.py b/src/calibre/gui2/tweak_book/editor/text.py index f1c4c7202d..f94b9af84a 100644 --- a/src/calibre/gui2/tweak_book/editor/text.py +++ b/src/calibre/gui2/tweak_book/editor/text.py @@ -9,6 +9,7 @@ __copyright__ = '2013, Kovid Goyal ' import textwrap from future_builtins import map +import regex from PyQt4.Qt import ( QPlainTextEdit, QFontDatabase, QToolTip, QPalette, QFont, QTextEdit, QTextFormat, QWidget, QSize, QPainter, Qt, QRect) @@ -20,6 +21,8 @@ from calibre.gui2.tweak_book.editor.syntax.base import SyntaxHighlighter from calibre.gui2.tweak_book.editor.syntax.html import HTMLHighlighter, XMLHighlighter from calibre.gui2.tweak_book.editor.syntax.css import CSSHighlighter +PARAGRAPH_SEPARATOR = '\u2029' + _dff = None def default_font_family(): global _dff @@ -151,6 +154,40 @@ class TextEdit(QPlainTextEdit): self.current_search_mark = None self.update_extra_selections() + def find(self, pat): + reverse = pat.flags & regex.REVERSE + c = self.textCursor() + c.clearSelection() + c.movePosition(c.Start if reverse else c.End, c.KeepAnchor) + raw = unicode(c.selectedText()).replace(PARAGRAPH_SEPARATOR, '\n') + m = pat.search(raw) + if m is None: + return False + start, end = m.span() + if start == end: + return False + if reverse: + # Put the cursor at the start of the match + start, end = end, start + else: + textpos = c.anchor() + start, end = textpos + start, textpos + end + c.clearSelection() + c.setPosition(start) + c.setPosition(end, c.KeepAnchor) + self.setTextCursor(c) + return True + + def replace(self, pat, template): + c = self.textCursor() + raw = unicode(c.selectedText()).replace(PARAGRAPH_SEPARATOR, '\n') + m = pat.fullmatch(raw) + if m is None: + return False + text = m.expand(template) + c.insertText(text) + return True + # Line numbers and cursor line {{{ def highlight_cursor_line(self): sel = QTextEdit.ExtraSelection() diff --git a/src/calibre/gui2/tweak_book/editor/widget.py b/src/calibre/gui2/tweak_book/editor/widget.py index e2960f35cb..19574e3be1 100644 --- a/src/calibre/gui2/tweak_book/editor/widget.py +++ b/src/calibre/gui2/tweak_book/editor/widget.py @@ -70,6 +70,12 @@ class Editor(QMainWindow): def mark_selected_text(self): self.editor.mark_selected_text() + def find(self, *args, **kwargs): + return self.editor.find(*args, **kwargs) + + def replace(self, *args, **kwargs): + return self.editor.replace(*args, **kwargs) + @property def has_marked_text(self): return self.editor.current_search_mark is not None diff --git a/src/calibre/gui2/tweak_book/search.py b/src/calibre/gui2/tweak_book/search.py index 4943f9abed..3a63e32093 100644 --- a/src/calibre/gui2/tweak_book/search.py +++ b/src/calibre/gui2/tweak_book/search.py @@ -12,7 +12,7 @@ from PyQt4.Qt import ( import regex -from calibre.gui2.widgets import HistoryLineEdit +from calibre.gui2.widgets2 import HistoryLineEdit2 from calibre.gui2.tweak_book import tprefs REGEX_FLAGS = regex.VERSION1 | regex.WORD | regex.FULLCASE | regex.MULTILINE | regex.UNICODE @@ -46,15 +46,16 @@ class SearchWidget(QWidget): self.fl = fl = QLabel(_('&Find:')) fl.setAlignment(Qt.AlignRight | Qt.AlignCenter) - self.find_text = ft = HistoryLineEdit(self) + self.find_text = ft = HistoryLineEdit2(self) ft.initialize('tweak_book_find_edit') + ft.returnPressed.connect(lambda : self.search_triggered.emit('find')) fl.setBuddy(ft) l.addWidget(fl, 0, 0) l.addWidget(ft, 0, 1) self.rl = rl = QLabel(_('&Replace:')) rl.setAlignment(Qt.AlignRight | Qt.AlignCenter) - self.replace_text = rt = HistoryLineEdit(self) + self.replace_text = rt = HistoryLineEdit2(self) rt.initialize('tweak_book_replace_edit') rl.setBuddy(rt) l.addWidget(rl, 1, 0) @@ -153,7 +154,7 @@ class SearchWidget(QWidget): @dynamic_property def find(self): def fget(self): - return unicode(self.find_text.text()).strip() + return unicode(self.find_text.text()) def fset(self, val): self.find_text.setText(val) return property(fget=fget, fset=fset) @@ -161,7 +162,7 @@ class SearchWidget(QWidget): @dynamic_property def replace(self): def fget(self): - return unicode(self.replace_text.text()).strip() + return unicode(self.replace_text.text()) def fset(self, val): self.replace_text.setText(val) return property(fget=fget, fset=fset) @@ -227,6 +228,8 @@ class SearchWidget(QWidget): # }}} +regex_cache = {} + class SearchPanel(QWidget): search_triggered = pyqtSignal(object) @@ -266,3 +269,19 @@ class SearchPanel(QWidget): def set_where(self, val): self.widget.where = val + def get_regex(self, state): + raw = state['find'] + if state['mode'] != 'regex': + raw = regex.escape(raw, special_only=True) + flags = REGEX_FLAGS + if not state['case_sensitive']: + flags |= regex.IGNORECASE + if state['mode'] == 'regex' and state['dot_all']: + flags |= regex.DOTALL + if state['direction'] == 'up': + flags |= regex.REVERSE + ans = regex_cache.get((flags, raw), None) + if ans is None: + ans = regex_cache[(flags, raw)] = regex.compile(raw, flags=flags) + return ans +