cid0rz

Untitled

May 12th, 2022 (edited)
661
348 days
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import tkinter as tk
  2. import re
  3.  
  4.  
  5. class EditorMock(tk.Tk):
  6.     def __init__(self, text=""):
  7.         super().__init__()
  8.         self.text = tk.Text(self)
  9.         self.text.pack()
  10.         self.text.insert(tk.END, text)
  11.         self.findr = Finder_Replacer(self)
  12.         self.bind("<Control-f>", lambda x: self.findr.revive())
  13.         self.mainloop()
  14.  
  15.  
  16. class Finder_Replacer:
  17.     """A class to hold all the find/replace functionality
  18.    it will have the following attributes:
  19.  
  20.       matchstring - the string that the user wants to find.
  21.       replacestring - the string that the user wants to replace,
  22.                       if the user just wants to search is None.
  23.       matches - a dict of matches with position as key and length of
  24.                 the match as value.
  25.       current - the current position the user is interacting with
  26.       done - a dict with the positions already done and the letters
  27.              removed from the text
  28.    """
  29.  
  30.     def __init__(self, parent, matchstring=None, replacestring=None):
  31.         self.matchstring = matchstring
  32.         self.replacestring = replacestring
  33.         self.matches = None
  34.         self.done = {}
  35.         self.parent = parent
  36.         self.parent.text.tag_configure("found", background="green")
  37.         self.parent.text.tag_configure("foundcurrent", background="orange", foreground="white")
  38.         self.display()
  39.  
  40.     @property
  41.     def text(self):
  42.         return self.parent.text.get(1.0, tk.END)
  43.  
  44.     @property
  45.     def current(self):
  46.         if not self.parent.text.count("1.0", self.parent.text.index(tk.INSERT), "chars"):
  47.             return 0
  48.         else:
  49.             return self.parent.text.count("1.0", self.parent.text.index(tk.INSERT), "chars")[0]
  50.  
  51.     def display(self):
  52.         self.window = tk.Toplevel(self.parent)
  53.         self.window.geometry("500x300")
  54.         self.window.title("find & replace")
  55.         self.window_ROWS = 5
  56.         self.window_COLS = 3
  57.  
  58.         for i in range(self.window_ROWS):
  59.             self.window.rowconfigure(i, minsize=35)
  60.         for i in range(self.window_COLS):
  61.             self.window.columnconfigure(i, minsize=30)
  62.         self.find_tag = tk.Label(self.window, text="Find: ")
  63.         self.find_entry = tk.Entry(self.window)
  64.         self.replace_tag = tk.Label(self.window, text="Replace for:")
  65.         self.replace_entry = tk.Entry(self.window)
  66.         self.find_tag.grid(row=1, column=1)
  67.         self.find_entry.grid(row=1, column=2)
  68.         self.replace_tag.grid(row=2, column=1)
  69.         self.replace_entry.grid(row=2, column=2)
  70.         self.find_button = tk.Button(
  71.             self.window, text="Highlight", command=self.find)
  72.         self.find_button.grid(row=1, column=4)
  73.         self.next_button = tk.Button(
  74.             self.window, text="->", command=self.next_match)
  75.         self.next_button.grid(row=1, column=5)
  76.         self.prev_button = tk.Button(
  77.             self.window, text="<-", command=self.prev_match)
  78.         self.prev_button.grid(row=1, column=3)
  79.         self.replace_button = tk.Button(
  80.             self.window, text="Change it!", command=self.replace)
  81.         self.replace_all_button = tk.Button(
  82.             self.window, text="ALL", command=self.replace_all)
  83.         self.replace_button.grid(row=2, column=4)
  84.         self.replace_all_button.grid(row=2, column=5)
  85.         self.window.protocol("WM_DELETE_WINDOW", self.on_close)
  86.  
  87.     def highlight_matches(self):
  88.         self.parent.text.tag_remove("found", "1.0", "end")
  89.         for pos, match in self.matches.items():
  90.             start = match.start()
  91.             end = match.end()
  92.             self.parent.text.tag_add("found", f"1.0+{start}c", f"1.0+{end}c")
  93.  
  94.     def highlight_current(self):
  95.         self.parent.text.tag_remove("foundcurrent", "1.0", "end")
  96.         current = self.current
  97.         match = self.matches[current]
  98.         start = match.start()
  99.         end = match.end()
  100.         #self.parent.text.tag_remove("found", f"1.0+{start}c", f"1.0+{end}c")
  101.         self.parent.text.tag_add("foundcurrent", f"1.0+{start}c", f"1.0+{end}c")
  102.        
  103.        
  104.     def get_find_input(self):
  105.         if self.find_entry.get() == "":
  106.             self.parent.text.tag_remove("found", "1.0", "end")
  107.             return
  108.         current = self.current
  109.         self.matches = {}
  110.         self.matchstring = self.find_entry.get()
  111.         self.re_ = re.compile(self.matchstring)
  112.         for match in self.re_.finditer(self.text):
  113.             self.matches[match.start()] = match
  114.         self.highlight_matches()
  115.         self.parent.text.mark_set("insert", f"1.0 + {current}c")
  116.  
  117.     def find(self):
  118.         self.get_find_input()
  119.         self.parent.lift()
  120.         self.parent.text.focus()
  121.  
  122.     def next_match(self):
  123.         """Moves the editor focus to the next match"""
  124.         if self.find_entry.get() != self.matchstring:
  125.             self.get_find_input()
  126.         matchpos = [i for i in sorted(self.matches.keys()) if i > self.current]
  127.         if len(matchpos) > 0:
  128.             next_index = f"1.0 + {matchpos[0]}c"
  129.             self.parent.text.mark_set("insert", next_index)
  130.             self.parent.text.see(next_index)
  131.             self.highlight_current()
  132.         elif len(self.matches) > 0:
  133.             self.parent.text.mark_set("insert", "1.0")
  134.             self.next_match()
  135.         self.parent.lift()
  136.         self.parent.text.focus()
  137.  
  138.     def prev_match(self):
  139.         """Moves the editor focus to the previous match"""
  140.         if self.find_entry.get() != self.matchstring:
  141.             self.get_find_input()
  142.         matchpos = [i for i in sorted(self.matches.keys()) if i < self.current]
  143.         if len(matchpos)>0:
  144.             next_index = f"1.0 + {matchpos[-1]}c"
  145.             self.parent.text.mark_set("insert", next_index)
  146.             self.parent.text.see(next_index)
  147.             self.highlight_current()
  148.         elif len(self.matches) > 0:
  149.             self.parent.text.mark_set("insert", "end")
  150.             self.prev_match()
  151.         self.parent.lift()
  152.         self.parent.text.focus()
  153.        
  154.     def replace(self):
  155.         """replaces current (in focus) match, removing the match and writing the replace string"""
  156.         self.replacestring = self.replace_entry.get()
  157.         if self.find_entry.get() != self.matchstring:
  158.             self.get_find_input()
  159.             self.find()
  160.         elif self.is_on_match():
  161.             match = self.matches[self.current]
  162.             self.parent.text.delete(f"1.0 + {match.start()}c", f"1.0 + {match.end()}c")
  163.             self.parent.text.insert(f"1.0 + {self.current}c", self.replacestring)
  164.             self.get_find_input()
  165.         self.parent.lift()
  166.         self.parent.text.focus()
  167.  
  168.     def is_on_match(self):
  169.         """tells if the editor is currently pointing to a match"""
  170.         if self.current in self.matches.keys():
  171.             return True
  172.         else:
  173.             return False
  174.     def on_close(self):
  175.         """removes the highlighting of the find string when the window is closed"""
  176.         self.parent.text.tag_remove("found", "1.0", "end")
  177.         self.parent.text.tag_remove("foundcurrent", "1.0", "end")
  178.         self.window.withdraw()
  179.        
  180.     def replace_all(self):
  181.         """replaces all occurences of the string for the replace string, it will even replace partial words."""
  182.         if self.find_entry.get() != self.matchstring:
  183.             self.get_find_input()
  184.             self.find()
  185.         nmatches = len(self.matches)
  186.         self.parent.text.mark_set("insert", "1.0")
  187.         for i in range(nmatches):
  188.             self.next_match()
  189.             self.replace()
  190.     def revive(self):
  191.         """brings the window back"""
  192.         self.window.deiconify()
  193.    
  194.        
  195.        
  196.  
  197. e = EditorMock(text="""EMACS: The Extensible, Customizable Display Editor
  198. You are reading about GNU Emacs, the GNU incarnation of the advanced, self-documenting, customizable, extensible editor Emacs. (The ‘G’ in GNU (GNU’s Not Unix) is not silent.)
  199.  
  200. We call Emacs advanced because it can do much more than simple insertion and deletion of text. It can control subprocesses, indent programs automatically, show multiple files at once, edit remote files like they were local files, and more. Emacs editing commands operate in terms of characters, words, lines, sentences, paragraphs, and pages, as well as expressions and comments in various programming languages.
  201.  
  202. Self-documenting means that at any time you can use special commands, known as help commands, to find out what your options are, or to find out what any command does, or to find all the commands that pertain to a given topic. See Help.
  203.  
  204. Customizable means that you can easily alter the behavior of Emacs commands in simple ways. For instance, if you use a programming language in which comments start with ‘<**’ and end with ‘**>’, you can tell the Emacs comment manipulation commands to use those strings (see Manipulating Comments). To take another example, you can rebind the basic cursor motion commands (up, down, left and right) to any keys on the keyboard that you find comfortable. See Customization.
  205.  
  206. Extensible means that you can go beyond simple customization and create entirely new commands. New commands are simply programs written in the Lisp language, which are run by Emacs’s own Lisp interpreter. Existing commands can even be redefined in the middle of an editing session, without having to restart Emacs. Most of the editing commands in Emacs are written in Lisp; the few exceptions could have been written in Lisp but use C instead for efficiency. Writing an extension is programming, but non-programmers can use it afterwards. See Preface in An Introduction to Programming in Emacs Lisp, if you want to learn Emacs Lisp programming.""")
  207.  
  208.  
RAW Paste Data Copied