multi file open setup; search/replace plugin initial rework

This commit is contained in:
itdominator 2024-02-25 15:46:51 -06:00
parent 070d94abc1
commit 5fcf2d6f1a
7 changed files with 247 additions and 128 deletions

View File

@ -69,64 +69,70 @@ class Plugin(StylingMixin, ReplaceMixin, PluginBase):
def subscribe_to_events(self): def subscribe_to_events(self):
self._event_system.subscribe("tggl_search_replace", self._tggl_search_replace) self._event_system.subscribe("tggl_search_replace", self._tggl_search_replace)
self._event_system.subscribe("set_active_src_view", self._set_active_src_view) # self._event_system.subscribe("set_active_src_view", self._set_active_src_view)
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._buffer = self._active_src_view.get_buffer()
self._tag_table = self._buffer.get_tag_table() # 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):
self._search_replace_dialog.popup() self._search_replace_dialog.popup()
def _tggl_search_replace(self, widget = None, eve = None): def _tggl_search_replace(self, widget = None, eve = None):
is_visible = self._search_replace_dialog.is_visible() is_visible = self._search_replace_dialog.is_visible()
buffer = self._active_src_view.get_buffer()
data = None
if buffer.get_has_selection():
start, end = buffer.get_selection_bounds()
data = buffer.get_text(start, end, include_hidden_chars = False)
if data:
self._find_entry.set_text(data)
if not is_visible: if not is_visible:
self._search_replace_dialog.popup(); self._search_replace_dialog.popup();
self._find_entry.grab_focus() self._find_entry.grab_focus()
elif not data and is_visible:
self._search_replace_dialog.popdown()
self._find_entry.set_text("")
else: else:
self._find_entry.grab_focus() self._search_replace_dialog.popdown();
# buffer = self._active_src_view.get_buffer()
# data = None
# if buffer.get_has_selection():
# start, end = buffer.get_selection_bounds()
# data = buffer.get_text(start, end, include_hidden_chars = False)
# if data:
# self._find_entry.set_text(data)
# if not is_visible:
# self._search_replace_dialog.popup();
# self._find_entry.grab_focus()
# elif not data and is_visible:
# self._search_replace_dialog.popdown()
# self._find_entry.set_text("")
# else:
# self._find_entry.grab_focus()
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(self.search_tag) # search_tag = tag_table.lookup(self.search_tag)
if not search_tag: # if not search_tag:
search_tag = buffer.create_tag(self.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(self.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 cancel_timer(self): # def cancel_timer(self):
if self.timer: # if self.timer:
self.timer.cancel() # self.timer.cancel()
GLib.idle_remove_by_data(None) # GLib.idle_remove_by_data(None)
def delay_search_glib(self): # def delay_search_glib(self):
GLib.idle_add(self._do_highlight) # GLib.idle_add(self._do_highlight)
def delay_search(self): # def delay_search(self):
wait_time = self.search_time / len(self.find_text) # wait_time = self.search_time / len(self.find_text)
wait_time = max(wait_time, 0.05) # wait_time = max(wait_time, 0.05)
self.timer = threading.Timer(wait_time, self.delay_search_glib) # self.timer = threading.Timer(wait_time, self.delay_search_glib)
self.timer.daemon = True # self.timer.daemon = True
self.timer.start() # self.timer.start()
def on_enter_search(self, widget, eve): def on_enter_search(self, widget, eve):
@ -138,84 +144,133 @@ class Plugin(StylingMixin, ReplaceMixin, PluginBase):
self.find_next(widget) self.find_next(widget)
def search_for_string(self, widget): def search_for_string(self, widget):
self.cancel_timer() # query = self._find_entry.get_text()
query = widget.get_text()
self.find_text = widget.get_text() if not query: return
if len(self.find_text) > 0 and len(self.find_text) < 5:
self.delay_search() isBackwwards = True
else: isWrap = True
self._do_highlight(self.find_text) isCaseSensitive = self.use_case_sensitive
useWholeWord = self.use_whole_word_search
useRegExp = self.use_regex
# self.search_only_in_selection
self._event_system.emit(
"find_entry",
(
query,
isBackwwards,
isWrap,
isCaseSensitive,
useWholeWord,
useRegExp,
)
)
def _do_highlight(self, query = None): # def search_for_string(self, widget):
query = self.find_text if not query else query # self.cancel_timer()
buffer = self._active_src_view.get_buffer()
# Also clears tag from buffer so if no query we're clean in ui
search_tag = self.get_search_tag(buffer)
self.update_style(1) # self.find_text = widget.get_text()
if not query: # if len(self.find_text) > 0 and len(self.find_text) < 5:
self._find_status_lbl.set_label(f"Find in current buffer") # self.delay_search()
self.update_style(0) # else:
return # self._do_highlight(self.find_text)
start_itr = buffer.get_start_iter()
end_itr = buffer.get_end_iter()
results, total_count = self.search(start_itr, query) # def _do_highlight(self, query = None):
self._update_status_lbl(total_count, query) # query = self.find_text if not query else query
for start, end in results: # buffer = self._active_src_view.get_buffer()
buffer.apply_tag(search_tag, start, end) # # Also clears tag from buffer so if no query we're clean in ui
# search_tag = self.get_search_tag(buffer)
def search(self, start_itr = None, query = None, limit = None): # self.update_style(1)
if not start_itr or not query: return None, None # if not query:
# self._find_status_lbl.set_label(f"Find in current buffer")
# self.update_style(0)
# return
flags = Gtk.TextSearchFlags.VISIBLE_ONLY | Gtk.TextSearchFlags.TEXT_ONLY # start_itr = buffer.get_start_iter()
if not self.use_case_sensitive: # end_itr = buffer.get_end_iter()
flags = flags | Gtk.TextSearchFlags.CASE_INSENSITIVE
if self.search_only_in_selection and self._buffer.get_has_selection(): # results, total_count = self.search(start_itr, query)
start_itr, limit = self._buffer.get_selection_bounds() # self._update_status_lbl(total_count, query)
# for start, end in results:
# buffer.apply_tag(search_tag, start, end)
_results = [] # def search(self, start_itr = None, query = None, limit = None):
while True: # if not start_itr or not query: return None, None
result = start_itr.forward_search(query, flags, limit)
if not result: break
_results.append(result) # flags = Gtk.TextSearchFlags.VISIBLE_ONLY | Gtk.TextSearchFlags.TEXT_ONLY
start_itr = result[1] # if not self.use_case_sensitive:
# flags = flags | Gtk.TextSearchFlags.CASE_INSENSITIVE
results = self.apply_filters(_results, query) # if self.search_only_in_selection and self._buffer.get_has_selection():
return results, len(results) # start_itr, limit = self._buffer.get_selection_bounds()
def apply_filters(self, _results, query): # _results = []
results = [] # while True:
for start, end in _results: # result = start_itr.forward_search(query, flags, limit)
text = self._buffer.get_slice(start, end, include_hidden_chars = False) # if not result: break
if self.use_whole_word_search:
if not self.is_whole_word(start, end):
continue
results.append([start, end]) # _results.append(result)
# start_itr = result[1]
return results # results = self.apply_filters(_results, query)
# 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:
# if not self.is_whole_word(start, end):
# continue
# results.append([start, end])
# return results
def find_next(self, widget, eve = None, use_data = None): def find_next(self, widget, eve = None, use_data = None):
mark = self._buffer.get_insert() self._event_system.emit("find_next_entry")
iter = self._buffer.get_iter_at_mark(mark)
iter.forward_line()
search_tag = self._tag_table.lookup(self.search_tag) def find_prior(self, widget, eve = None, use_data = None):
next_tag_found = iter.forward_to_tag_toggle(search_tag) self._event_system.emit("find_prior_entry")
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) # def find_next(self, widget, eve = None, use_data = None):
self._active_src_view.scroll_to_mark( self._buffer.get_insert(), 0.0, True, 0.0, 0.0 ) # 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_all(self, widget): def find_all(self, widget):
... ...
def replace(self, widget):
fromStr = self._find_entry.get_text()
toStr = self._replace_entry.get_text()
if not fromStr or not toStr: return
self._event_system.emit("replace_entry", (fromStr, toStr))
def replace_all(self, widget):
fromStr = self._find_entry.get_text()
toStr = self._replace_entry.get_text()
if not fromStr or not toStr: return
self._event_system.emit("replace_all", (fromStr, toStr))

View File

@ -72,7 +72,6 @@
<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>

View File

@ -24,6 +24,10 @@ class BridgeController:
event_system.subscribe(f"keyboard_open_file", self.keyboard_open_file) event_system.subscribe(f"keyboard_open_file", self.keyboard_open_file)
event_system.subscribe(f"keyboard_scale_up_text", self.keyboard_scale_up_text) event_system.subscribe(f"keyboard_scale_up_text", self.keyboard_scale_up_text)
event_system.subscribe(f"keyboard_scale_down_text", self.keyboard_scale_down_text) event_system.subscribe(f"keyboard_scale_down_text", self.keyboard_scale_down_text)
event_system.subscribe(f"find_entry", self.find_entry)
event_system.subscribe(f"find_next_entry", self.find_next_entry)
event_system.subscribe(f"find_previous_entry", self.find_previous_entry)
event_system.subscribe(f"replace_entry", self.replace_entry)
event_system.subscribe(f"toggle_highlight_line", self.toggle_highlight_line) event_system.subscribe(f"toggle_highlight_line", self.toggle_highlight_line)
def keyboard_open_file(self, gfiles): def keyboard_open_file(self, gfiles):
@ -38,6 +42,33 @@ class BridgeController:
def toggle_highlight_line(self): def toggle_highlight_line(self):
event_system.emit(f"toggle_highlight_line_{self.originator}") event_system.emit(f"toggle_highlight_line_{self.originator}")
def find_entry(self, query, isBackwwards, isWrap, isCaseSensitive,
useWholeWord,
useRegExp):
event_system.emit(
f"find_entry_{self.originator}",
(
query,
isBackwwards,
isWrap,
isCaseSensitive,
useWholeWord,
useRegExp
)
)
def find_next_entry(self):
event_system.emit(f"find_next_entry_{self.originator}")
def find_previous_entry(self):
event_system.emit(f"find_previous_entry_{self.originator}")
def replace_entry(self, fromStr, toStr):
event_system.emit(f"replace_entry_{self.originator}", (fromStr, toStr,))
def replace_all(self, fromStr, toStr):
event_system.emit(f"replace_all_{self.originator}", (fromStr, toStr,))
def handle_bridge_event(self, event): def handle_bridge_event(self, event):
self.originator = event.originator self.originator = event.originator
@ -56,10 +87,6 @@ class BridgeController:
event_system.emit(f"handle_file_event_{event.originator}", (event,)) event_system.emit(f"handle_file_event_{event.originator}", (event,))
case "tggl_search_replace": case "tggl_search_replace":
event_system.emit(f"tggl_search_replace") event_system.emit(f"tggl_search_replace")
case "find_entry":
event_system.emit(f"find_entry_{event.originator}")
case "replace_entry":
event_system.emit(f"replace_entry_{event.originator}")
case "tggl_top_main_menubar": case "tggl_top_main_menubar":
event_system.emit("tggl_top_main_menubar") event_system.emit("tggl_top_main_menubar")
case "set_info_labels": case "set_info_labels":

View File

@ -86,7 +86,14 @@ class FilesController:
content = base64.b64decode( event.content.encode() ).decode("utf-8") content = base64.b64decode( event.content.encode() ).decode("utf-8")
# event_system.emit(f"add_tab_with_name_{event.originator}", (event.fhash, content,)) # event_system.emit(f"add_tab_with_name_{event.originator}", (event.fhash, content,))
case "open_file": case "open_file":
event_system.emit(f"open_files", (None, None, self.INDEX,)) _gfiles = event_system.emit_and_await(f"open_files", (None, None, event.fpath,))
if _gfiles:
event_system.emit(
f"set_pre_drop_dnd_{self.INDEX}",
(
_gfiles,
)
)
case _: case _:
return return

View File

@ -54,7 +54,10 @@ class AceEditor(WebKit2.WebView):
event_system.subscribe(f"keyboard_scale_down_text_{self.INDEX}", self.keyboard_scale_down_text) event_system.subscribe(f"keyboard_scale_down_text_{self.INDEX}", self.keyboard_scale_down_text)
event_system.subscribe(f"toggle_highlight_line_{self.INDEX}", self.toggle_highlight_line) event_system.subscribe(f"toggle_highlight_line_{self.INDEX}", self.toggle_highlight_line)
event_system.subscribe(f"find_entry_{self.INDEX}", self.find_entry) event_system.subscribe(f"find_entry_{self.INDEX}", self.find_entry)
event_system.subscribe(f"find_next_entry_{self.INDEX}", self.find_next_entry)
event_system.subscribe(f"find_previous_entry_{self.INDEX}", self.find_previous_entry)
event_system.subscribe(f"replace_entry_{self.INDEX}", self.replace_entry) event_system.subscribe(f"replace_entry_{self.INDEX}", self.replace_entry)
event_system.subscribe(f"replace_all_{self.INDEX}", self.replace_all)
event_system.subscribe(f"ui_message_{self.INDEX}", self.ui_message) event_system.subscribe(f"ui_message_{self.INDEX}", self.ui_message)
def _load_settings(self): def _load_settings(self):
@ -126,8 +129,18 @@ class AceEditor(WebKit2.WebView):
command = f"displayMessage('{message}', '{mtype}', '3')" command = f"displayMessage('{message}', '{mtype}', '3')"
self.run_javascript(command, None, None) self.run_javascript(command, None, None)
def find_entry(self, query): def find_entry(self, query, isBackwwards, isWrap, isCaseSensitive,
command = f"findEntry('{query}')" useWholeWord,
useRegExp):
command = f"findEntry('{query}', '{isBackwwards}', '{isWrap}', '{isCaseSensitive}', '{useWholeWord}', '{useRegExp}')"
self.run_javascript(command, None, None)
def find_next_entry(self):
command = f"findNextEntry()"
self.run_javascript(command, None, None)
def find_previous_entry(self):
command = f"findPreviousEntry()"
self.run_javascript(command, None, None) self.run_javascript(command, None, None)
def replace_entry(self, fromStr, toStr): def replace_entry(self, fromStr, toStr):

View File

@ -41,37 +41,46 @@ class OpenFileButton(Gtk.Button):
def _load_widgets(self): def _load_widgets(self):
... ...
def _open_files(self, widget = None, eve = None, widget_index = None): def _open_files(self, widget = None, eve = None, fpath = None):
chooser = Gtk.FileChooserDialog("Open File...", None, start_dir = None
gfile = Gio.File.new_for_path(fpath)
_gfiles = []
if gfile.query_exists():
start_dir = gfile.get_parent()
chooser = Gtk.FileChooserDialog("Open File(s)...", None,
Gtk.FileChooserAction.OPEN, Gtk.FileChooserAction.OPEN,
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, (
Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) Gtk.STOCK_CANCEL,
Gtk.ResponseType.CANCEL,
Gtk.STOCK_OPEN,
Gtk.ResponseType.OK
)
)
chooser.set_select_multiple(True)
try: try:
folder = widget.get_current_file().get_parent() folder = widget.get_current_file().get_parent() if not start_dir else start_dir
chooser.set_current_folder( folder.get_uri() ) chooser.set_current_folder( folder.get_path() )
except Exception as e: except Exception as e:
... ...
response = chooser.run() response = chooser.run()
if not response == Gtk.ResponseType.OK: if not response == Gtk.ResponseType.OK:
chooser.destroy() chooser.destroy()
return return _gfiles
filename = chooser.get_filename() filenames = chooser.get_filenames()
if not filename: if not filenames:
chooser.destroy() chooser.destroy()
return return _gfiles
for file in filenames:
path = filename if os.path.isabs(filename) else os.path.abspath(filename) path = file if os.path.isabs(file) else os.path.abspath(file)
_gfile = Gio.File.new_for_path(path) _gfiles.append( Gio.File.new_for_path(path) )
event_system.emit(
f"set_pre_drop_dnd_{widget_index}" if widget_index else "keyboard_open_file",
(
[_gfile],
)
)
chooser.destroy() chooser.destroy()
return _gfiles

View File

@ -17,13 +17,22 @@ const editorCommands = [
editor.showKeyboardShortcuts(); editor.showKeyboardShortcuts();
}) })
} }
}, {
name: "search",
bindKey: {win: "ctrl-f", mac: "ctrl-f"},
exec: function(editor) {
sendMessage("tggl_search_replace", "", "", "", "");
},
readOnly: true
}, { }, {
name: "openFile", name: "openFile",
bindKey: {win: "ctrl-o", mac: "ctrl-o"}, bindKey: {win: "ctrl-o", mac: "ctrl-o"},
exec: function(editor) { exec: function(editor) {
sendMessage("open_file", "", "", "", ""); fpath = aceSessions[currentSession]["fpath"]
sendMessage("open_file", "", "", fpath, "");
}, },
readOnly: true readOnly: true
}, { }, {
name: "saveSession", name: "saveSession",
bindKey: {win: "ctrl-s", mac: "ctrl-s"}, bindKey: {win: "ctrl-s", mac: "ctrl-s"},