Mercurial > repos > rliterman > csp2
diff CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/idlelib/replace.py @ 68:5028fdace37b
planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author | jpayne |
---|---|
date | Tue, 18 Mar 2025 16:23:26 -0400 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/idlelib/replace.py Tue Mar 18 16:23:26 2025 -0400 @@ -0,0 +1,307 @@ +"""Replace dialog for IDLE. Inherits SearchDialogBase for GUI. +Uses idlelib.searchengine.SearchEngine for search capability. +Defines various replace related functions like replace, replace all, +and replace+find. +""" +import re + +from tkinter import StringVar, TclError + +from idlelib.searchbase import SearchDialogBase +from idlelib import searchengine + + +def replace(text): + """Create or reuse a singleton ReplaceDialog instance. + + The singleton dialog saves user entries and preferences + across instances. + + Args: + text: Text widget containing the text to be searched. + """ + 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): + "Dialog for finding and replacing a pattern in text." + + title = "Replace Dialog" + icon = "Replace" + + def __init__(self, root, engine): + """Create search dialog for finding and replacing text. + + Uses SearchDialogBase as the basis for the GUI and a + searchengine instance to prepare the search. + + Attributes: + replvar: StringVar containing 'Replace with:' value. + replent: Entry widget for replvar. Created in + create_entries(). + ok: Boolean used in searchengine.search_text to indicate + whether the search includes the selection. + """ + super().__init__(root, engine) + self.replvar = StringVar(root) + + def open(self, text): + """Make dialog visible on top of others and ready to use. + + Also, highlight the currently selected text and set the + search to include the current selection (self.ok). + + Args: + text: Text widget being searched. + """ + 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 = True + + def create_entries(self): + "Create base and additional label and text entry widgets." + SearchDialogBase.create_entries(self) + self.replent = self.make_entry("Replace with:", self.replvar)[0] + + def create_command_buttons(self): + """Create base and additional command buttons. + + The additional buttons are for Find, Replace, + Replace+Find, and Replace All. + """ + 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, isdef=True) + self.make_button("Replace All", self.replace_all) + + def find_it(self, event=None): + "Handle the Find button." + self.do_find(False) + + def replace_it(self, event=None): + """Handle the Replace button. + + If the find is successful, then perform replace. + """ + if self.do_find(self.ok): + self.do_replace() + + def default_command(self, event=None): + """Handle the Replace+Find button as the default command. + + First performs a replace and then, if the replace was + successful, a find next. + """ + if self.do_find(self.ok): + if self.do_replace(): # Only find next match if replace succeeded. + # A bad re can cause it to fail. + self.do_find(False) + + def _replace_expand(self, m, repl): + "Expand replacement text if regular expression." + if self.engine.isre(): + try: + new = m.expand(repl) + except re.error: + self.engine.report_error(repl, 'Invalid Replace Expression') + new = None + else: + new = repl + + return new + + def replace_all(self, event=None): + """Handle the Replace All button. + + Search text for occurrences of the Find value and replace + each of them. The 'wrap around' value controls the start + point for searching. If wrap isn't set, then the searching + starts at the first occurrence after the current selection; + if wrap is set, the replacement starts at the first line. + The replacement is always done top-to-bottom in the text. + """ + 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: + self.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 = True + first = last = None + # XXX ought to replace circular instead of top-to-bottom when wrapping + text.undo_block_start() + while True: + res = self.engine.search_forward(text, prog, line, col, + wrap=False, ok=ok) + if not res: + break + line, m = res + chars = text.get("%d.0" % line, "%d.0" % (line+1)) + orig = m.group() + new = self._replace_expand(m, repl) + if new is None: + break + 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 = False + text.undo_block_stop() + if first and last: + self.show_hit(first, last) + self.close() + + def do_find(self, ok=False): + """Search for and highlight next occurrence of pattern in text. + + No text replacement is done with this option. + """ + if not self.engine.getprog(): + return False + text = self.text + res = self.engine.search_text(text, None, ok) + if not res: + self.bell() + return False + line, m = res + i, j = m.span() + first = "%d.%d" % (line, i) + last = "%d.%d" % (line, j) + self.show_hit(first, last) + self.ok = True + return True + + def do_replace(self): + "Replace search pattern in text with replacement value." + prog = self.engine.getprog() + if not prog: + return False + 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 False + new = self._replace_expand(m, self.replvar.get()) + if new is None: + return False + 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 = False + return True + + def show_hit(self, first, last): + """Highlight text between first and last indices. + + Text is highlighted via the 'hit' tag and the marked + section is brought into view. + + The colors from the 'hit' tag aren't currently shown + when the text is displayed. This is due to the 'sel' + tag being added first, so the colors in the 'sel' + config are seen instead of the colors for 'hit'. + """ + 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): + "Close the dialog and remove hit tags." + SearchDialogBase.close(self, event) + self.text.tag_remove("hit", "1.0", "end") + + +def _replace_dialog(parent): # htest # + from tkinter import Toplevel, Text, END, SEL + from tkinter.ttk import Frame, Button + + top = Toplevel(parent) + top.title("Test ReplaceDialog") + x, y = map(int, parent.geometry().split('+')[1:]) + top.geometry("+%d+%d" % (x, y + 175)) + + # mock undo delegator methods + def undo_block_start(): + pass + + def undo_block_stop(): + pass + + frame = Frame(top) + frame.pack() + text = Text(frame, inactiveselectbackground='gray') + text.undo_block_start = undo_block_start + text.undo_block_stop = undo_block_stop + text.pack() + text.insert("insert","This is a sample sTring\nPlus MORE.") + text.focus_set() + + def show_replace(): + text.tag_add(SEL, "1.0", END) + replace(text) + text.tag_remove(SEL, "1.0", END) + + button = Button(frame, text="Replace", command=show_replace) + button.pack() + +if __name__ == '__main__': + from unittest import main + main('idlelib.idle_test.test_replace', verbosity=2, exit=False) + + from idlelib.idle_test.htest import run + run(_replace_dialog)