ReplaceDialog.py 5.84 KB
Newer Older
Guido van Rossum's avatar
Guido van Rossum committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
import string
import os
import re
import fnmatch
from Tkinter import *
import tkMessageBox
import SearchEngine
from SearchDialogBase import SearchDialogBase

def replace(text):
    root = text._root()
    engine = SearchEngine.get(root)
    if not hasattr(engine, "_replacedialog"):
        engine._replacedialog = ReplaceDialog(root, engine)
    dialog = engine._replacedialog
    dialog.open(text)

class ReplaceDialog(SearchDialogBase):

    title = "Replace Dialog"
    icon = "Replace"

    def __init__(self, root, engine):
        SearchDialogBase.__init__(self, root, engine)
        self.replvar = StringVar(root)

    def open(self, text):
        SearchDialogBase.open(self, text)
        try:
            first = text.index("sel.first")
        except TclError:
            first = None
        try:
            last = text.index("sel.last")
        except TclError:
            last = None
        first = first or text.index("insert")
        last = last or first
        self.show_hit(first, last)
        self.ok = 1

    def create_entries(self):
        SearchDialogBase.create_entries(self)
        self.replent = self.make_entry("Replace with:", self.replvar)

    def create_command_buttons(self):
        SearchDialogBase.create_command_buttons(self)
        self.make_button("Find", self.find_it)
        self.make_button("Replace", self.replace_it)
        self.make_button("Replace+Find", self.default_command, 1)
        self.make_button("Replace All", self.replace_all)

    def find_it(self, event=None):
        self.do_find(0)

    def replace_it(self, event=None):
        if self.do_find(self.ok):
            self.do_replace()

    def default_command(self, event=None):
        if self.do_find(self.ok):
            self.do_replace()
            self.do_find(0)

    def replace_all(self, event=None):
        prog = self.engine.getprog()
        if not prog:
            return
        repl = self.replvar.get()
        text = self.text
        res = self.engine.search_text(text, prog)
        if not res:
            text.bell()
            return
        text.tag_remove("sel", "1.0", "end")
        text.tag_remove("hit", "1.0", "end")
        line = res[0]
        col = res[1].start()
        if self.engine.iswrap():
            line = 1
            col = 0
        ok = 1
        first = last = None
        # XXX ought to replace circular instead of top-to-bottom when wrapping
85
        text.undo_block_start()
Guido van Rossum's avatar
Guido van Rossum committed
86 87 88 89 90 91 92
        while 1:
            res = self.engine.search_forward(text, prog, line, col, 0, ok)
            if not res:
                break
            line, m = res
            chars = text.get("%d.0" % line, "%d.0" % (line+1))
            orig = m.group()
93
            new = self._expand(m, repl)
Guido van Rossum's avatar
Guido van Rossum committed
94 95 96 97 98 99 100 101 102 103 104 105 106
            i, j = m.span()
            first = "%d.%d" % (line, i)
            last = "%d.%d" % (line, j)
            if new == orig:
                text.mark_set("insert", last)
            else:
                text.mark_set("insert", first)
                if first != last:
                    text.delete(first, last)
                if new:
                    text.insert(first, new)
            col = i + len(new)
            ok = 0
107
        text.undo_block_stop()
Guido van Rossum's avatar
Guido van Rossum committed
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
        if first and last:
            self.show_hit(first, last)
        self.close()

    def do_find(self, ok=0):
        if not self.engine.getprog():
            return 0
        text = self.text
        res = self.engine.search_text(text, None, ok)
        if not res:
            text.bell()
            return 0
        line, m = res
        i, j = m.span()
        first = "%d.%d" % (line, i)
        last = "%d.%d" % (line, j)
        self.show_hit(first, last)
        self.ok = 1
        return 1

    def do_replace(self):
        prog = self.engine.getprog()
        if not prog:
            return 0
        text = self.text
        try:
            first = pos = text.index("sel.first")
            last = text.index("sel.last")
        except TclError:
            pos = None
        if not pos:
            first = last = pos = text.index("insert")
        line, col = SearchEngine.get_line_col(pos)
        chars = text.get("%d.0" % line, "%d.0" % (line+1))
        m = prog.match(chars, col)
        if not prog:
            return 0
145
        new = self._expand(m, self.replvar.get())
Guido van Rossum's avatar
Guido van Rossum committed
146
        text.mark_set("insert", first)
147
        text.undo_block_start()
Guido van Rossum's avatar
Guido van Rossum committed
148 149 150 151
        if m.group():
            text.delete(first, last)
        if new:
            text.insert(first, new)
152
        text.undo_block_stop()
Guido van Rossum's avatar
Guido van Rossum committed
153 154 155 156
        self.show_hit(first, text.index("insert"))
        self.ok = 0
        return 1

157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
    def _expand(self, m, template):
        # XXX This code depends on internals of the regular expression
        # engine!  There's no standard API to do a substitution when you
        # have already found the match.  One should be added.
        # The solution here is designed to be backwards compatible
        # with previous Python versions, e.g. 1.5.2.
        # XXX This dynamic test should be done only once.
        if getattr(re, "engine", "pre") == "pre":
            return re.pcre_expand(m, template)
        else: # sre
            # XXX This import should be avoidable...
            import sre_parse
            # XXX This parses the template over and over...
            ptemplate = sre_parse.parse_template(template, m.re)
            return sre_parse.expand_template(ptemplate, m)

Guido van Rossum's avatar
Guido van Rossum committed
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
    def show_hit(self, first, last):
        text = self.text
        text.mark_set("insert", first)
        text.tag_remove("sel", "1.0", "end")
        text.tag_add("sel", first, last)
        text.tag_remove("hit", "1.0", "end")
        if first == last:
            text.tag_add("hit", first)
        else:
            text.tag_add("hit", first, last)
        text.see("insert")
        text.update_idletasks()

    def close(self, event=None):
        SearchDialogBase.close(self, event)
        self.text.tag_remove("hit", "1.0", "end")