Files
Newton-Editor/plugins/code/ui/terminals/terminals_view.py
itdominator 41f3501e1f feat(code): improve comment toggling, terminal navigation, and editor event wiring
- Refactor Commenter toggle logic for line and multi-line comments
  - Preserve indentation and cursor position
  - Improve handling of existing comment detection and removal
  - Simplify bounds vs line comment dispatch

- Enhance terminal project navigation
  - Add project marker detection via Gio file traversal
  - Implement go-to-project-or-home behavior (Home key shortcut)
  - Automatically `cd` into detected project root or home directory
  - Wire terminal widget navigation through VteWidget

- Improve terminal integration
  - Pass emit_to into terminals view for event dispatching
  - Add ability for VteWidget to trigger project navigation

- Update split pane shortcut
  - Change close split view binding to Alt+\

- Add editor event support
  - Emit `text_insert` event from SourceFile on insert
  - Add new TextInsertEvent DTO and register in event system

- Misc cleanup
  - Improve imports and structure in terminals module
  - Add project marker list and filesystem traversal helpers
2026-04-15 01:54:56 -05:00

207 lines
5.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Python imports
import os
import shlex
# Lib imports
import gi
gi.require_version('Gio', '2.0')
gi.require_version('Gdk', '3.0')
gi.require_version('Gtk', '3.0')
from gi.repository import Gio
from gi.repository import Gdk
from gi.repository import Gtk
from gi.repository import GLib
from gi.repository import Pango
# Application imports
from libs.event_factory import Event_Factory, Code_Event_Types
from .vte_widget import VteWidget
class TerminalsView(Gtk.Notebook):
def __init__(self):
super(TerminalsView, self).__init__()
self.MARKERS: list = ["src", ".git", ".gitignore", "README.md"]
self.code_view = None
self._setup_styling()
self._setup_signals()
self._load_widgets()
self.show_all()
self.hide()
def _setup_styling(self):
ctx = self.get_style_context()
ctx.add_class("terminals-view")
self.set_scrollable(True)
def _setup_signals(self):
self.connect("show", self._handle_show)
self.connect("hide", self._handle_hide)
self.connect("destroy", self._handle_destroy)
def _load_widgets(self):
hbox = Gtk.Box()
self.add_bttn = Gtk.Button(label = "")
self.hide_bttn = Gtk.Button(label = "")
self.add_bttn.connect("clicked", self._create_terminal)
self.hide_bttn.connect("clicked", self._hide_view)
hbox.add(self.add_bttn)
hbox.add(self.hide_bttn)
self.set_action_widget(hbox, Gtk.PackType.END)
self.create_terminal()
hbox.show_all()
def _generate_terminal_parts(self):
label = Gtk.Label(label = "...")
vte_widget = VteWidget()
vte_widget.hide_view = self.hide
vte_widget.go_to_project_or_home = self.go_to_project_or_home
vte_widget.create_terminal = self.create_terminal
vte_widget.close_terminal = self.close_terminal
vte_widget.prev_terminal = self.prev_terminal
vte_widget.next_terminal = self.next_terminal
label.set_text( vte_widget.get_home_path() )
label.set_tooltip_text( vte_widget.get_home_path() )
label.set_ellipsize(Pango.EllipsizeMode.START)
label.set_single_line_mode(True)
label.set_max_width_chars(32)
label.set_size_request(240, -1)
vte_widget.bind_label(label)
return label, vte_widget
def _handle_show(self, widget):
i = widget.get_current_page()
term = widget.get_nth_page(i)
GLib.idle_add(term.grab_focus)
def _handle_hide(self, widget):
if not self.code_view: return
GLib.idle_add(self.code_view.grab_focus)
def _hide_view(self, widget):
self.hide()
def _handle_destroy(self, widget):
widget.disconnect_by_func(widget._handle_show)
widget.disconnect_by_func(widget._handle_hide)
widget.disconnect_by_func(widget._handle_destroy)
self.add_bttn.disconnect_by_func(self._create_terminal)
self.hide_bttn.disconnect_by_func(self._hide_view)
def _create_terminal(self, widget):
self.create_terminal()
def has_marker(self, gfile):
try:
enumerator = gfile.enumerate_children(
"standard::name,standard::type",
Gio.FileQueryInfoFlags.NONE,
None
)
while True:
info = enumerator.next_file(None)
if info is None:
break
if info.get_name() in self.MARKERS:
enumerator.close(None)
return True
enumerator.close(None)
except Exception:
pass
return False
def find_project_path_or_home(self, current: Gio.File):
if not current: return
home = Gio.File.new_for_path( os.path.expanduser("~") )
while True:
if self.has_marker(current):
return current.get_path()
if current.equal(home):
return current.get_path()
parent = current.get_parent()
if parent is None:
return current.get_path()
current = parent
def set_code_view(self, widget):
self.code_view = widget
def go_to_project_or_home(self):
event = Event_Factory.create_event("get_file",
buffer = self.code_view.get_buffer()
)
self.emit_to("files", event)
if event.response.ftype == "buffer": return
gfile = event.response.get_location().get_parent()
fpath = self.find_project_path_or_home(gfile)
i = self.get_current_page()
widget = self.get_nth_page(i)
widget.run_command(f"cd {shlex.quote(fpath)} && clear\n")
def create_terminal(self):
label, vte_widget = self._generate_terminal_parts()
index = self.append_page(vte_widget, label)
self.set_tab_detachable(vte_widget, True)
self.set_tab_reorderable(vte_widget, True)
self.set_current_page(index)
GLib.idle_add(vte_widget.grab_focus)
self.show_all()
def close_terminal(self):
size = self.get_n_pages()
if size == 1: return
i = self.get_current_page()
widget = self.get_nth_page(i)
self.remove_page(i)
widget.destroy()
def prev_terminal(self):
i = self.get_current_page() - 1
size = self.get_n_pages()
if i < 0:
self.set_current_page(size - 1)
return
self.prev_page()
def next_terminal(self):
i = self.get_current_page() + 1
size = self.get_n_pages()
if i == size:
self.set_current_page(0)
return
self.next_page()