Merge Stable Changesto Master #9
							
								
								
									
										3
									
								
								plugins/searcher/mixins/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								plugins/searcher/mixins/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| """ | ||||
|     Mixins Module | ||||
| """ | ||||
							
								
								
									
										71
									
								
								plugins/searcher/mixins/file_search_mixin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								plugins/searcher/mixins/file_search_mixin.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| # Python imports | ||||
| import threading, subprocess, signal, time, json, shlex | ||||
|  | ||||
| # Lib imports | ||||
| from gi.repository import GLib | ||||
|  | ||||
| # Application imports | ||||
| from ..widgets.file_preview_widget import FilePreviewWidget | ||||
|  | ||||
|  | ||||
| # NOTE: Threads WILL NOT die with parent's destruction. | ||||
| def threaded(fn): | ||||
|     def wrapper(*args, **kwargs): | ||||
|         threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start() | ||||
|     return wrapper | ||||
|  | ||||
| # NOTE: Threads WILL die with parent's destruction. | ||||
| def daemon_threaded(fn): | ||||
|     def wrapper(*args, **kwargs): | ||||
|         threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start() | ||||
|     return wrapper | ||||
|  | ||||
|  | ||||
| class FileSearchMixin: | ||||
|     def _run_find_file_query(self, widget=None, eve=None): | ||||
|         self._handle_find_file_query(query=widget) | ||||
|  | ||||
|     @daemon_threaded | ||||
|     def _handle_find_file_query(self, widget=None, eve=None, query=None): | ||||
|         # NOTE: Freeze IPC consumption | ||||
|         self.pause_fifo_update = True | ||||
|  | ||||
|         # NOTE: Kill the former process | ||||
|         if self._list_proc: | ||||
|             if self._list_proc.poll(): | ||||
|                 self._list_proc.send_signal(signal.SIGKILL) | ||||
|                 while self._list_proc.poll(): | ||||
|                     pass | ||||
|  | ||||
|                 self._list_proc = None | ||||
|             else: | ||||
|                 self._list_proc = None | ||||
|  | ||||
|         GLib.idle_add(self.clear_children, self._file_list) | ||||
|         while len(self._file_list.get_children()) > 0: | ||||
|             ... | ||||
|  | ||||
|         # NOTE: Make sure ui thread redraws | ||||
|         time.sleep(0.5) | ||||
|         self.pause_fifo_update = False | ||||
|  | ||||
|         # NOTE: If query create new process and do all new loop. | ||||
|         if query: | ||||
|             GLib.idle_add(self._exec_find_file_query, query) | ||||
|  | ||||
|     def _exec_find_file_query(self, widget=None, eve=None): | ||||
|         query = widget.get_text() | ||||
|  | ||||
|         if not query in ("", None): | ||||
|             target_dir = shlex.quote( self._fm_state.tab.get_current_directory() ) | ||||
|             command = ["python", f"{self.path}/utils/search.py", "-t", "file_search", "-d", f"{target_dir}", "-q", f"{query}"] | ||||
|             self._list_proc = subprocess.Popen(command, cwd=self.path, stdin=None, stdout=None, stderr=None) | ||||
|  | ||||
|     def _load_file_ui(self, data): | ||||
|         if not data in ("", None) and not self.pause_fifo_update: | ||||
|             jdata  = json.loads( data ) | ||||
|             target = jdata[0] | ||||
|             file   = jdata[1] | ||||
|  | ||||
|             widget = FilePreviewWidget(target, file) | ||||
|             self._file_list.add(widget) | ||||
							
								
								
									
										73
									
								
								plugins/searcher/mixins/grep_search_mixin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								plugins/searcher/mixins/grep_search_mixin.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | ||||
| # Python imports | ||||
| import threading, subprocess, signal, time, json, shlex | ||||
|  | ||||
| # Lib imports | ||||
| from gi.repository import GLib | ||||
|  | ||||
| # Application imports | ||||
| from ..widgets.grep_preview_widget import GrepPreviewWidget | ||||
|  | ||||
|  | ||||
| # NOTE: Threads WILL NOT die with parent's destruction. | ||||
| def threaded(fn): | ||||
|     def wrapper(*args, **kwargs): | ||||
|         threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=False).start() | ||||
|     return wrapper | ||||
|  | ||||
| # NOTE: Threads WILL die with parent's destruction. | ||||
| def daemon_threaded(fn): | ||||
|     def wrapper(*args, **kwargs): | ||||
|         threading.Thread(target=fn, args=args, kwargs=kwargs, daemon=True).start() | ||||
|     return wrapper | ||||
|  | ||||
|  | ||||
| class GrepSearchMixin: | ||||
|     def _run_grep_query(self, widget=None, eve=None): | ||||
|         self._handle_grep_query(query=widget) | ||||
|  | ||||
|     @daemon_threaded | ||||
|     def _handle_grep_query(self, widget=None, eve=None, query=None): | ||||
|         # NOTE: Freeze IPC consumption | ||||
|         self.pause_fifo_update = True | ||||
|  | ||||
|         # NOTE: Kill the former process | ||||
|         if self._grep_proc: | ||||
|             if self._grep_proc.poll(): | ||||
|                 self._grep_proc.send_signal(signal.SIGKILL) | ||||
|                 while self._grep_proc.poll(): | ||||
|                     pass | ||||
|  | ||||
|                 self._grep_proc = None | ||||
|             else: | ||||
|                 self._grep_proc = None | ||||
|  | ||||
|         # NOTE: Clear children from ui | ||||
|         GLib.idle_add(self.clear_children, self._grep_list) | ||||
|         while len(self._grep_list.get_children()) > 0: | ||||
|             ... | ||||
|  | ||||
|         # NOTE: Make sure ui thread redraws | ||||
|         time.sleep(0.5) | ||||
|         self.pause_fifo_update = False | ||||
|  | ||||
|         # NOTE: If query create new process and do all new loop. | ||||
|         if query: | ||||
|             GLib.idle_add(self._exec_grep_query, query) | ||||
|  | ||||
|     def _exec_grep_query(self, widget=None, eve=None): | ||||
|         query = widget.get_text() | ||||
|         if not query in ("", None): | ||||
|             target_dir = shlex.quote( self._fm_state.tab.get_current_directory() ) | ||||
|             command = ["python", f"{self.path}/utils/search.py", "-t", "grep_search", "-d", f"{target_dir}", "-q", f"{query}"] | ||||
|             self._grep_proc = subprocess.Popen(command, cwd=self.path, stdin=None, stdout=None, stderr=None) | ||||
|  | ||||
|     def _load_grep_ui(self, data): | ||||
|         if not data in ("", None) and not self.pause_fifo_update: | ||||
|             jdata = json.loads( data ) | ||||
|             jkeys = jdata.keys() | ||||
|             for key in jkeys: | ||||
|                 sub_keys    = jdata[key].keys() | ||||
|                 grep_result = jdata[key] | ||||
|  | ||||
|                 widget = GrepPreviewWidget(key, sub_keys, grep_result) | ||||
|                 self._grep_list.add(widget) | ||||
| @@ -1,5 +1,5 @@ | ||||
| # Python imports | ||||
| import os, threading, subprocess, inspect, time, json, base64, shlex, select, signal | ||||
| import os, threading, inspect | ||||
|  | ||||
| # Lib imports | ||||
| import gi | ||||
| @@ -8,7 +8,9 @@ from gi.repository import Gtk, GLib | ||||
|  | ||||
| # Application imports | ||||
| from plugins.plugin_base import PluginBase | ||||
| from .ipc_server import IPCServer | ||||
| from .mixins.file_search_mixin import FileSearchMixin | ||||
| from .mixins.grep_search_mixin import GrepSearchMixin | ||||
| from .utils.ipc_server import IPCServer | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -27,54 +29,7 @@ def daemon_threaded(fn): | ||||
|  | ||||
|  | ||||
|  | ||||
| class FilePreviewWidget(Gtk.LinkButton): | ||||
|     def __init__(self, path, file): | ||||
|         super(FilePreviewWidget, self).__init__() | ||||
|         self.set_label(file) | ||||
|         self.set_uri(f"file://{path}") | ||||
|         self.show_all() | ||||
|  | ||||
|  | ||||
| class GrepPreviewWidget(Gtk.Box): | ||||
|     def __init__(self, _path, sub_keys, data): | ||||
|         super(GrepPreviewWidget, self).__init__() | ||||
|         self.set_orientation(Gtk.Orientation.VERTICAL) | ||||
|         self.line_color = "#e0cc64" | ||||
|  | ||||
|         path   = base64.urlsafe_b64decode(_path.encode('utf-8')).decode('utf-8') | ||||
|         _label = '/'.join( path.split("/")[-3:] ) | ||||
|         title  = Gtk.LinkButton.new_with_label(uri=f"file://{path}", label=_label) | ||||
|  | ||||
|         self.add(title) | ||||
|         for key in sub_keys: | ||||
|             line_num     = key | ||||
|             text = base64.urlsafe_b64decode(data[key].encode('utf-8')).decode('utf-8') | ||||
|  | ||||
|  | ||||
|             box          = Gtk.Box() | ||||
|             number_label = Gtk.Label() | ||||
|             text_view    = Gtk.Label(label=text[:-1]) | ||||
|             label_text   = f"<span foreground='{self.line_color}'>{line_num}</span>" | ||||
|  | ||||
|             number_label.set_markup(label_text) | ||||
|             number_label.set_margin_left(15) | ||||
|             number_label.set_margin_right(5) | ||||
|             number_label.set_margin_top(5) | ||||
|             number_label.set_margin_bottom(5) | ||||
|             text_view.set_margin_top(5) | ||||
|             text_view.set_margin_bottom(5) | ||||
|             text_view.set_line_wrap(True) | ||||
|  | ||||
|             box.add(number_label) | ||||
|             box.add(text_view) | ||||
|             self.add(box) | ||||
|  | ||||
|         self.show_all() | ||||
|  | ||||
|  | ||||
| pause_fifo_update = False | ||||
|  | ||||
| class Plugin(IPCServer, PluginBase): | ||||
| class Plugin(IPCServer, FileSearchMixin, GrepSearchMixin, PluginBase): | ||||
|     def __init__(self): | ||||
|         super().__init__() | ||||
|  | ||||
| @@ -89,11 +44,13 @@ class Plugin(IPCServer, PluginBase): | ||||
|         self._grep_list        = None | ||||
|         self._grep_proc        = None | ||||
|         self._list_proc        = None | ||||
|         self.pause_fifo_update = False | ||||
|         self.update_list_ui_buffer = () | ||||
|  | ||||
|  | ||||
|     def get_ui_element(self): | ||||
|         button = Gtk.Button(label=self.name) | ||||
|         button.connect("button-release-event", self._show_grep_list_page) | ||||
|         button.connect("button-release-event", self._show_page) | ||||
|         return button | ||||
|  | ||||
|     def run(self): | ||||
| @@ -123,7 +80,7 @@ class Plugin(IPCServer, PluginBase): | ||||
|         self.create_ipc_listener() | ||||
|  | ||||
|  | ||||
|     def _show_grep_list_page(self, widget=None, eve=None): | ||||
|     def _show_page(self, widget=None, eve=None): | ||||
|         self._event_system.emit("get_current_state") | ||||
|  | ||||
|         state               = self._fm_state | ||||
| @@ -134,74 +91,7 @@ class Plugin(IPCServer, PluginBase): | ||||
|         self._search_dialog.hide() | ||||
|  | ||||
|  | ||||
|     def _run_find_file_query(self, widget=None, eve=None): | ||||
|         self._stop_find_file_query() | ||||
|  | ||||
|         query = widget.get_text() | ||||
|         if not query in ("", None): | ||||
|             target_dir = shlex.quote( self._fm_state.tab.get_current_directory() ) | ||||
|             command = ["python", f"{self.path}/search.py", "-t", "file_search", "-d", f"{target_dir}", "-q", f"{query}"] | ||||
|             process = subprocess.Popen(command, cwd=self.path, stdin=None, stdout=None, stderr=None) | ||||
|  | ||||
|     def _stop_find_file_query(self, widget=None, eve=None): | ||||
|         pause_fifo_update = True | ||||
|  | ||||
|         if self._list_proc: | ||||
|             if self._list_proc.poll(): | ||||
|                 self._list_proc.send_signal(signal.SIGKILL) | ||||
|                 while self._list_proc.poll(): | ||||
|                     pass | ||||
|  | ||||
|                 self._list_proc = None | ||||
|             else: | ||||
|                 self._list_proc = None | ||||
|  | ||||
|         self.clear_children(self._file_list) | ||||
|         pause_fifo_update = False | ||||
|  | ||||
|  | ||||
|     def _run_grep_query(self, widget=None, eve=None): | ||||
|         self._stop_grep_query() | ||||
|  | ||||
|         query = widget.get_text() | ||||
|         if not query in ("", None): | ||||
|             target_dir = shlex.quote( self._fm_state.tab.get_current_directory() ) | ||||
|             command = ["python", f"{self.path}/search.py", "-t", "grep_search", "-d", f"{target_dir}", "-q", f"{query}"] | ||||
|             process = subprocess.Popen(command, cwd=self.path, stdin=None, stdout=None, stderr=None) | ||||
|  | ||||
|     def _stop_grep_query(self, widget=None, eve=None): | ||||
|         pause_fifo_update = True | ||||
|  | ||||
|         if self._grep_proc: | ||||
|             if self._grep_proc.poll(): | ||||
|                 self._grep_proc.send_signal(signal.SIGKILL) | ||||
|                 while self._grep_proc.poll(): | ||||
|                     pass | ||||
|  | ||||
|                 self._grep_proc = None | ||||
|             else: | ||||
|                 self._grep_proc = None | ||||
|  | ||||
|         self.clear_children(self._grep_list) | ||||
|         pause_fifo_update = False | ||||
|  | ||||
|  | ||||
|     def _load_file_ui(self, data): | ||||
|         if not data in ("", None) and not pause_fifo_update: | ||||
|             jdata  = json.loads( data ) | ||||
|             target = jdata[0] | ||||
|             file   = jdata[1] | ||||
|  | ||||
|             widget = FilePreviewWidget(target, file) | ||||
|             self._file_list.add(widget) | ||||
|  | ||||
|     def _load_grep_ui(self, data): | ||||
|         if not data in ("", None) and not pause_fifo_update: | ||||
|             jdata = json.loads( data ) | ||||
|             jkeys = jdata.keys() | ||||
|             for key in jkeys: | ||||
|                 sub_keys    = jdata[key].keys() | ||||
|                 grep_result = jdata[key] | ||||
|  | ||||
|                 widget = GrepPreviewWidget(key, sub_keys, grep_result) | ||||
|                 self._grep_list.add(widget) | ||||
|     def clear_children(self, widget: type) -> None: | ||||
|         ''' Clear children of a gtk widget. ''' | ||||
|         for child in widget.get_children(): | ||||
|             widget.remove(child) | ||||
|   | ||||
| @@ -83,7 +83,7 @@ | ||||
|                     <property name="primary-icon-sensitive">False</property> | ||||
|                     <property name="placeholder-text" translatable="yes">Search for file...</property> | ||||
|                     <signal name="search-changed" handler="_run_find_file_query" swapped="no"/> | ||||
|                     <signal name="stop-search" handler="_stop_find_file_query" swapped="no"/> | ||||
|                     <signal name="stop-search" handler="_handle_find_file_query" swapped="no"/> | ||||
|                   </object> | ||||
|                   <packing> | ||||
|                     <property name="expand">False</property> | ||||
| @@ -150,7 +150,7 @@ | ||||
|                     <property name="primary-icon-sensitive">False</property> | ||||
|                     <property name="placeholder-text" translatable="yes">Query string in file...</property> | ||||
|                     <signal name="search-changed" handler="_run_grep_query" swapped="no"/> | ||||
|                     <signal name="stop-search" handler="_stop_grep_query" swapped="no"/> | ||||
|                     <signal name="stop-search" handler="_handle_grep_query" swapped="no"/> | ||||
|                   </object> | ||||
|                   <packing> | ||||
|                     <property name="expand">False</property> | ||||
|   | ||||
| @@ -57,32 +57,35 @@ class IPCServer: | ||||
|     def handle_message(self, conn, start_time) -> None: | ||||
|         while True: | ||||
|             msg  = conn.recv() | ||||
|             data = msg | ||||
| 
 | ||||
|             if "SEARCH|" in msg: | ||||
|                 file = msg.split("SEARCH|")[1].strip() | ||||
|                 if file: | ||||
|                     GLib.idle_add(self._load_file_ui, file) | ||||
|             if not self.pause_fifo_update: | ||||
|                 if "SEARCH|" in msg: | ||||
|                     file = msg.split("SEARCH|")[1].strip() | ||||
|                     if file: | ||||
|                         GLib.idle_add(self._load_file_ui, file) | ||||
| 
 | ||||
|                 conn.close() | ||||
|                 break | ||||
|                     conn.close() | ||||
|                     break | ||||
| 
 | ||||
|             if "GREP|" in msg: | ||||
|                 data = msg.split("GREP|")[1].strip() | ||||
|                 if data: | ||||
|                     GLib.idle_add(self._load_grep_ui, data) | ||||
|                 if "GREP|" in msg: | ||||
|                     data = msg.split("GREP|")[1].strip() | ||||
|                     if data: | ||||
|                         GLib.idle_add(self._load_grep_ui, data) | ||||
| 
 | ||||
|                 conn.close() | ||||
|                 break | ||||
|                     conn.close() | ||||
|                     break | ||||
| 
 | ||||
| 
 | ||||
|             if msg in ['close connection', 'close server']: | ||||
|                 conn.close() | ||||
|                 break | ||||
|                 if msg in ['close connection', 'close server']: | ||||
|                     conn.close() | ||||
|                     break | ||||
| 
 | ||||
|             # NOTE: Not perfect but insures we don't lock up the connection for too long. | ||||
|             end_time = time.perf_counter() | ||||
|             if (end_time - start_time) > self._ipc_timeout: | ||||
|                 # NOTE: Not perfect but insures we don't lock up the connection for too long. | ||||
|                 end_time = time.perf_counter() | ||||
|                 if (end_time - start_time) > self._ipc_timeout: | ||||
|                     conn.close() | ||||
|                     break | ||||
|             else: | ||||
|                 conn.close() | ||||
|                 break | ||||
| 
 | ||||
							
								
								
									
										3
									
								
								plugins/searcher/widgets/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								plugins/searcher/widgets/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| """ | ||||
|     Widgets Module | ||||
| """ | ||||
							
								
								
									
										16
									
								
								plugins/searcher/widgets/file_preview_widget.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								plugins/searcher/widgets/file_preview_widget.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| # Python imports | ||||
|  | ||||
| # Gtk imports | ||||
| import gi | ||||
| gi.require_version('Gtk', '3.0') | ||||
| from gi.repository import Gtk | ||||
|  | ||||
| # Application imports | ||||
|  | ||||
|  | ||||
| class FilePreviewWidget(Gtk.LinkButton): | ||||
|     def __init__(self, path, file): | ||||
|         super(FilePreviewWidget, self).__init__() | ||||
|         self.set_label(file) | ||||
|         self.set_uri(f"file://{path}") | ||||
|         self.show_all() | ||||
							
								
								
									
										46
									
								
								plugins/searcher/widgets/grep_preview_widget.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								plugins/searcher/widgets/grep_preview_widget.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| # Python imports | ||||
| import base64 | ||||
|  | ||||
| # Lib imports | ||||
| import gi | ||||
| gi.require_version('Gtk', '3.0') | ||||
| from gi.repository import Gtk | ||||
|  | ||||
| # Application imports | ||||
|  | ||||
|  | ||||
| class GrepPreviewWidget(Gtk.Box): | ||||
|     def __init__(self, _path, sub_keys, data): | ||||
|         super(GrepPreviewWidget, self).__init__() | ||||
|         self.set_orientation(Gtk.Orientation.VERTICAL) | ||||
|         self.line_color = "#e0cc64" | ||||
|  | ||||
|         path   = base64.urlsafe_b64decode(_path.encode('utf-8')).decode('utf-8') | ||||
|         _label = '/'.join( path.split("/")[-3:] ) | ||||
|         title  = Gtk.LinkButton.new_with_label(uri=f"file://{path}", label=_label) | ||||
|  | ||||
|         self.add(title) | ||||
|         for key in sub_keys: | ||||
|             line_num     = key | ||||
|             text = base64.urlsafe_b64decode(data[key].encode('utf-8')).decode('utf-8') | ||||
|  | ||||
|  | ||||
|             box          = Gtk.Box() | ||||
|             number_label = Gtk.Label() | ||||
|             text_view    = Gtk.Label(label=text[:-1]) | ||||
|             label_text   = f"<span foreground='{self.line_color}'>{line_num}</span>" | ||||
|  | ||||
|             number_label.set_markup(label_text) | ||||
|             number_label.set_margin_left(15) | ||||
|             number_label.set_margin_right(5) | ||||
|             number_label.set_margin_top(5) | ||||
|             number_label.set_margin_bottom(5) | ||||
|             text_view.set_margin_top(5) | ||||
|             text_view.set_margin_bottom(5) | ||||
|             text_view.set_line_wrap(True) | ||||
|  | ||||
|             box.add(number_label) | ||||
|             box.add(text_view) | ||||
|             self.add(box) | ||||
|  | ||||
|         self.show_all() | ||||
		Reference in New Issue
	
	Block a user