More work on search and replace

This commit is contained in:
itdominator 2023-10-15 01:35:43 -05:00
parent 3ad7c3b65d
commit f10f4fcc72
3 changed files with 80 additions and 34 deletions

View File

@ -115,7 +115,7 @@ class Plugin(PluginBase):
if not start_itr or not query: return None, None if not start_itr or not query: return None, None
results = [] results = []
flags = Gtk.TextSearchFlags.VISIBLE_ONLY & Gtk.TextSearchFlags.TEXT_ONLY flags = Gtk.TextSearchFlags.VISIBLE_ONLY | Gtk.TextSearchFlags.TEXT_ONLY
while True: while True:
result = start_itr.forward_search(query, flags, end_itr) result = start_itr.forward_search(query, flags, end_itr)
if not result: break if not result: break

View File

@ -1,5 +1,6 @@
# Python imports # Python imports
import os import os
import re
# Lib imports # Lib imports
import gi import gi
@ -21,17 +22,22 @@ class Plugin(PluginBase):
self.path = os.path.dirname(os.path.realpath(__file__)) self.path = os.path.dirname(os.path.realpath(__file__))
self._GLADE_FILE = f"{self.path}/search_replace.glade" self._GLADE_FILE = f"{self.path}/search_replace.glade"
self._search_replace_dialog = None self._search_replace_dialog = None
self._find_entry = None self._find_entry = None
self._replace_entry = None self._replace_entry = None
self._active_src_view = None self._active_src_view = None
self._buffer = None
self._tag_table = None
self.use_regex = False self.use_regex = False
self.use_case_sensitive = False self.use_case_sensitive = False
self.use_only_in_selection = False self.search_only_in_selection = False
self.use_whole_word_search = False self.use_whole_word_search = False
self.highlight_color = "#FBF719"
self.text_color = "#000000" self.search_tag = "search_tag"
self.highlight_color = "#FBF719"
self.text_color = "#000000"
self.alpha_num_under = re.compile(r"[a-zA-Z0-9_]")
def run(self): def run(self):
@ -59,6 +65,8 @@ class Plugin(PluginBase):
def _set_active_src_view(self, source_view): def _set_active_src_view(self, source_view):
self._active_src_view = source_view self._active_src_view = source_view
self._buffer = self._active_src_view.get_buffer()
self._tag_table = self._buffer.get_tag_table()
self.search_for_string(self._find_entry) self.search_for_string(self._find_entry)
def _show_search_replace(self, widget = None, eve = None): def _show_search_replace(self, widget = None, eve = None):
@ -86,18 +94,22 @@ class Plugin(PluginBase):
def tggle_regex(self, widget): def tggle_regex(self, widget):
self.use_regex = not widget.get_active() self.use_regex = not widget.get_active()
self._set_find_options_lbl() self._set_find_options_lbl()
self.search_for_string(self._find_entry)
def tggle_case_sensitive(self, widget): def tggle_case_sensitive(self, widget):
self.use_case_sensitive = not widget.get_active() self.use_case_sensitive = widget.get_active()
self._set_find_options_lbl() self._set_find_options_lbl()
self.search_for_string(self._find_entry)
def tggle_selection_only_scan(self, widget): def tggle_selection_only_scan(self, widget):
self.use_only_in_selection = not widget.get_active() self.search_only_in_selection = widget.get_active()
self._set_find_options_lbl() self._set_find_options_lbl()
self.search_for_string(self._find_entry)
def tggle_whole_word_search(self, widget): def tggle_whole_word_search(self, widget):
self.use_whole_word_search = not widget.get_active() self.use_whole_word_search = widget.get_active()
self._set_find_options_lbl() self._set_find_options_lbl()
self.search_for_string(self._find_entry)
def _set_find_options_lbl(self): def _set_find_options_lbl(self):
# Finding with Options: Case Insensitive # Finding with Options: Case Insensitive
@ -106,8 +118,8 @@ class Plugin(PluginBase):
# f"Finding with Options: {regex}, {case}, {selection}, {word}" # f"Finding with Options: {regex}, {case}, {selection}, {word}"
... ...
def _update_status_lbl(self, total_count: int = None, query: str = None): def _update_status_lbl(self, total_count: int = 0, query: str = None):
if not total_count or not query: return if not query: return
count = total_count if total_count > 0 else "No" count = total_count if total_count > 0 else "No"
plural = "s" if total_count > 1 else "" plural = "s" if total_count > 1 else ""
@ -115,16 +127,13 @@ class Plugin(PluginBase):
def get_search_tag(self, buffer): def get_search_tag(self, buffer):
tag_table = buffer.get_tag_table() tag_table = buffer.get_tag_table()
search_tag = tag_table.lookup("search_tag") search_tag = tag_table.lookup(self.search_tag)
if not search_tag: if not search_tag:
search_tag = buffer.create_tag("search_tag", background = self.highlight_color, foreground = self.text_color) search_tag = buffer.create_tag(self.search_tag, background = self.highlight_color, foreground = self.text_color)
buffer.remove_tag_by_name("search_tag", buffer.get_start_iter(), buffer.get_end_iter()) buffer.remove_tag_by_name(self.search_tag, buffer.get_start_iter(), buffer.get_end_iter())
return search_tag return search_tag
def find_next_entry(self, widget, eve, use_data = None):
...
def search_for_string(self, widget): def search_for_string(self, widget):
query = widget.get_text() query = widget.get_text()
buffer = self._active_src_view.get_buffer() buffer = self._active_src_view.get_buffer()
@ -139,36 +148,70 @@ class Plugin(PluginBase):
end_itr = buffer.get_end_iter() end_itr = buffer.get_end_iter()
results, total_count = self.search(start_itr, query) results, total_count = self.search(start_itr, query)
self._update_status_lbl(total_count, query)
for start, end in results: for start, end in results:
buffer.apply_tag(search_tag, start, end) buffer.apply_tag(search_tag, start, end)
self._update_status_lbl(total_count, query)
def search(self, start_itr = None, query = None): def search(self, start_itr = None, query = None, limit = None):
if not start_itr or not query: return None, None if not start_itr or not query: return None, None
flags = Gtk.TextSearchFlags.VISIBLE_ONLY | Gtk.TextSearchFlags.TEXT_ONLY
if not self.use_case_sensitive: if not self.use_case_sensitive:
_flags = Gtk.TextSearchFlags.VISIBLE_ONLY & Gtk.TextSearchFlags.TEXT_ONLY & Gtk.TextSearchFlags.CASE_INSENSITIVE flags = flags | Gtk.TextSearchFlags.CASE_INSENSITIVE
else:
_flags = Gtk.TextSearchFlags.VISIBLE_ONLY & Gtk.TextSearchFlags.TEXT_ONLY
results = [] if self.search_only_in_selection and self._buffer.get_has_selection():
start_itr, limit = self._buffer.get_selection_bounds()
_results = []
while True: while True:
result = start_itr.forward_search(query, flags = _flags, limit = None) result = start_itr.forward_search(query, flags, limit)
if not result: break if not result: break
results.append(result) _results.append(result)
start_itr = result[1] start_itr = result[1]
results = self.apply_filters(_results, query)
return results, len(results) return results, len(results)
def apply_filters(self, _results, query):
results = []
for start, end in _results:
text = self._buffer.get_slice(start, end, include_hidden_chars = False)
if self.use_whole_word_search:
end.forward_char()
start.backward_char()
match = self.alpha_num_under.match( start.get_char() )
if not match is None:
continue
match = self.alpha_num_under.match( end.get_char() )
if not match is None:
continue
end.backward_char()
start.forward_char()
results.append([start, end])
return results
def find_next(self, widget, eve = None, use_data = None):
mark = self._buffer.get_insert()
iter = self._buffer.get_iter_at_mark(mark)
iter.forward_line()
search_tag = self._tag_table.lookup(self.search_tag)
next_tag_found = iter.forward_to_tag_toggle(search_tag)
if not next_tag_found:
self._buffer.place_cursor( self._buffer.get_start_iter() )
mark = self._buffer.get_insert()
iter = self._buffer.get_iter_at_mark(mark)
iter.forward_to_tag_toggle(search_tag)
self._buffer.place_cursor(iter)
self._active_src_view.scroll_to_mark( self._buffer.get_insert(), 0.0, True, 0.0, 0.0 )
def find_next(self, widget):
...
def find_all(self, widget): def find_all(self, widget):
... ...
@ -177,4 +220,4 @@ class Plugin(PluginBase):
... ...
def replace_all(self, widget): def replace_all(self, widget):
... ...

View File

@ -72,6 +72,7 @@
<object class="GtkToggleButton"> <object class="GtkToggleButton">
<property name="label" translatable="yes">.*</property> <property name="label" translatable="yes">.*</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can-focus">True</property> <property name="can-focus">True</property>
<property name="focus-on-click">False</property> <property name="focus-on-click">False</property>
<property name="receives-default">True</property> <property name="receives-default">True</property>
@ -187,8 +188,8 @@
<property name="margin-top">5</property> <property name="margin-top">5</property>
<property name="margin-bottom">5</property> <property name="margin-bottom">5</property>
<property name="placeholder-text" translatable="yes">Find in current buffer</property> <property name="placeholder-text" translatable="yes">Find in current buffer</property>
<signal name="activate" handler="find_next" swapped="no"/>
<signal name="changed" handler="search_for_string" swapped="no"/> <signal name="changed" handler="search_for_string" swapped="no"/>
<signal name="key-release-event" handler="find_next_entry" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="left-attach">0</property> <property name="left-attach">0</property>
@ -255,6 +256,7 @@
<property name="label" translatable="yes">Find All</property> <property name="label" translatable="yes">Find All</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">True</property> <property name="can-focus">True</property>
<property name="focus-on-click">False</property>
<property name="receives-default">True</property> <property name="receives-default">True</property>
<property name="margin-start">5</property> <property name="margin-start">5</property>
<property name="margin-end">5</property> <property name="margin-end">5</property>
@ -272,6 +274,7 @@
<property name="label" translatable="yes">Find</property> <property name="label" translatable="yes">Find</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">True</property> <property name="can-focus">True</property>
<property name="focus-on-click">False</property>
<property name="receives-default">True</property> <property name="receives-default">True</property>
<property name="margin-start">5</property> <property name="margin-start">5</property>
<property name="margin-end">5</property> <property name="margin-end">5</property>