mirror of
https://github.com/python/cpython.git
synced 2024-11-28 08:20:55 +01:00
f8d071332a
The cause was that the replace code necessarily used a PCRE internal function to to template expansion. The fix changes the code to use an SRE internal if SRE is used, and a PCRE internal if SRE is used; in a way that should work with 1.5.2. The solution can be sped up tremendously under the assumption that the choice between sre and pre is not changed during the execution of the program; especially replace-all will be slow. But I'll leave that to someone else.
189 lines
5.8 KiB
Python
189 lines
5.8 KiB
Python
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
|
|
text.undo_block_start()
|
|
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()
|
|
new = self._expand(m, repl)
|
|
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
|
|
text.undo_block_stop()
|
|
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
|
|
new = self._expand(m, self.replvar.get())
|
|
text.mark_set("insert", first)
|
|
text.undo_block_start()
|
|
if m.group():
|
|
text.delete(first, last)
|
|
if new:
|
|
text.insert(first, new)
|
|
text.undo_block_stop()
|
|
self.show_hit(first, text.index("insert"))
|
|
self.ok = 0
|
|
return 1
|
|
|
|
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)
|
|
|
|
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")
|