Add pshell integration and clean up text editor code

This commit is contained in:
kake26 2025-04-30 17:15:28 -05:00
parent a5de997cfa
commit 15256a982e
Signed by: kake26
GPG key ID: E0A989B571D1F99F
4 changed files with 577 additions and 113 deletions

View file

@ -7,8 +7,6 @@ use File::Glob;
use FileHandle;
use LWP::Simple;
use Gtk3::SourceView;
use IPC::Open3;
use IO::Select;
# Declare global variables
our $textbuffer; # SourceView buffer for the editor
@ -16,10 +14,6 @@ our $textview; # SourceView widget for editor
our $statusbar; # Status bar for feedback
our $open; # Current file path
our $track = 'init'; # Tracks modifications
our $shell_buffer; # SourceView buffer for pshell
our $shell_view; # SourceView widget for pshell
our $shell_scrolled; # Scrolled window for pshell
our $shell_pid; # pshell process ID
# Base directory setup (reused from your code)
my ($ar) = @ARGV;
@ -174,12 +168,6 @@ my $which_line_item = Gtk3::MenuItem->new_with_label('Which Line?');
$which_line_item->signal_connect(activate => \&which_line_dialog);
$tools_menu->append($which_line_item);
my $toggle_shell = Gtk3::MenuItem->new_with_label('Toggle pshell');
$toggle_shell->signal_connect(activate => sub {
$shell_scrolled->set_visible(!$shell_scrolled->get_visible);
});
$tools_menu->append($toggle_shell);
# HTML menu
my $html_menu = Gtk3::Menu->new;
my $html_item = Gtk3::MenuItem->new_with_label('HTML');
@ -224,13 +212,9 @@ $about_item->signal_connect(activate => sub {
});
$help_menu->append($about_item);
# Split pane for editor and pshell
my $paned = Gtk3::Paned->new('vertical');
$vbox->pack_start($paned, TRUE, TRUE, 0);
# Editor (SourceView)
my $scrolled = Gtk3::ScrolledWindow->new(undef, undef);
$paned->pack1($scrolled, TRUE, TRUE);
$vbox->pack_start($scrolled, TRUE, TRUE, 0);
$textview = Gtk3::SourceView::View->new;
$textview->set_wrap_mode('word');
$textview->set_show_line_numbers(TRUE);
@ -242,84 +226,6 @@ my $language = $lang_manager->get_language('perl');
$textbuffer->set_language($language) if $language;
$textbuffer->set_highlight_syntax(TRUE);
# pshell panel (SourceView)
$shell_scrolled = Gtk3::ScrolledWindow->new(undef, undef);
$paned->pack2($shell_scrolled, FALSE, TRUE);
$shell_view = Gtk3::SourceView::View->new;
$shell_view->set_wrap_mode('word');
$shell_view->set_editable(TRUE);
$shell_scrolled->add($shell_view);
$shell_buffer = $shell_view->get_buffer;
my $shell_lang = $lang_manager->get_language('perl');
$shell_buffer->set_language($shell_lang) if $shell_lang;
$shell_buffer->set_highlight_syntax(TRUE);
$shell_buffer->set_text("pshell:/home/kake26/projects/perl/kpadold> ");
# Start pshell
my ($wtr, $rdr, $err);
my $pshell_path = "$basedir/pshell";
if (!-x $pshell_path) {
$statusbar->push(0, "Error: pshell not found or not executable at $pshell_path");
} else {
eval {
$shell_pid = open3($wtr, $rdr, $err, $pshell_path);
1;
} or do {
$statusbar->push(0, "Failed to start pshell: $@");
$shell_buffer->set_text("Error: Could not start pshell\n");
};
if ($shell_pid && defined $rdr && defined $err) {
# Ensure unbuffered output
select($wtr); $| = 1; select(STDOUT);
# Non-blocking read for pshell stdout and stderr
my $selector = IO::Select->new($rdr, $err);
Glib::IO->add_watch(fileno($rdr), ['in', 'hup'], sub {
my ($fileno, $condition) = @_;
if ($condition & 'in') {
my $output;
sysread($rdr, $output, 4096); # Increased buffer size
if ($output) {
$output =~ s/pshell:[^\n>]*> //g; # Strip prompt if included
$shell_buffer->insert_at_cursor($output);
$shell_view->scroll_to_iter($shell_buffer->get_end_iter, 0, FALSE, 0, 0);
}
}
return FALSE if $condition & 'hup';
return TRUE;
}) if defined $rdr;
Glib::IO->add_watch(fileno($err), ['in', 'hup'], sub {
my ($fileno, $condition) = @_;
if ($condition & 'in') {
my $output;
sysread($err, $output, 4096);
if ($output) {
$output =~ s/pshell:[^\n>]*> //g; # Strip prompt if included
$shell_buffer->insert_at_cursor($output);
$shell_view->scroll_to_iter($shell_buffer->get_end_iter, 0, FALSE, 0, 0);
}
}
return FALSE if $condition & 'hup';
return TRUE;
}) if defined $err;
}
}
# Handle pshell input
$shell_view->signal_connect('key-press-event' => sub {
my ($widget, $event) = @_;
if ($event->keyval == Gtk3::Gdk::KEY_Return) {
my ($start, $end) = $shell_buffer->get_bounds;
my $text = $shell_buffer->get_text($start, $end, TRUE);
my ($cmd) = $text =~ /pshell:[^\n>]*> (.*?)$/s;
if (defined $cmd && defined $wtr) {
print $wtr "$cmd\n";
$shell_buffer->insert_at_cursor("\npshell:$ENV{HOME}> ");
}
return TRUE;
}
return FALSE;
});
# CSS styling for Kubuntu
my $provider = Gtk3::CssProvider->new;
$provider->load_from_data('
@ -380,11 +286,18 @@ sub open_file {
'Open File', $window, 'open',
'gtk-cancel' => 'cancel', 'gtk-ok' => 'ok'
);
$dialog->add_filter(Gtk3::FileFilter->new)->add_pattern('*.pl');
$dialog->add_filter(Gtk3::FileFilter->new)->add_pattern('*');
if ($dialog->run eq 'ok') {
my $pl_filter = Gtk3::FileFilter->new;
$pl_filter->set_name('Perl Scripts');
$pl_filter->add_pattern('*.pl');
$dialog->add_filter($pl_filter);
my $all_filter = Gtk3::FileFilter->new;
$all_filter->set_name('All Files');
$all_filter->add_pattern('*');
$dialog->add_filter($all_filter);
my $response = $dialog->run;
if ($response && $response eq 'ok') {
my $filename = $dialog->get_filename;
if (open my $fh, '<', $filename) {
if ($filename && open my $fh, '<', $filename) {
local $/;
$textbuffer->set_text(<$fh>);
close $fh;
@ -418,19 +331,28 @@ sub save_as_file {
'Save File', $window, 'save',
'gtk-cancel' => 'cancel', 'gtk-ok' => 'ok'
);
$dialog->add_filter(Gtk3::FileFilter->new)->add_pattern('*.pl');
$dialog->add_filter(Gtk3::FileFilter->new)->add_pattern('*');
if ($dialog->run eq 'ok') {
my $pl_filter = Gtk3::FileFilter->new;
$pl_filter->set_name('Perl Scripts');
$pl_filter->add_pattern('*.pl');
$dialog->add_filter($pl_filter);
my $all_filter = Gtk3::FileFilter->new;
$all_filter->set_name('All Files');
$all_filter->add_pattern('*');
$dialog->add_filter($all_filter);
my $response = $dialog->run;
if ($response && $response eq 'ok') {
my $filename = $dialog->get_filename;
my ($start, $end) = $textbuffer->get_bounds;
my $text = $textbuffer->get_text($start, $end, TRUE);
if (open my $fh, '>', $filename) {
print $fh $text;
close $fh;
$statusbar->push(0, "Saved $filename");
$open = $filename;
} else {
$statusbar->push(0, "Failed to save $filename");
if ($filename) {
my ($start, $end) = $textbuffer->get_bounds;
my $text = $textbuffer->get_text($start, $end, TRUE);
if (open my $fh, '>', $filename) {
print $fh $text;
close $fh;
$statusbar->push(0, "Saved $filename");
$open = $filename;
} else {
$statusbar->push(0, "Failed to save $filename");
}
}
}
$dialog->destroy;
@ -595,8 +517,6 @@ sub tapp {
save_file();
}
}
# Kill pshell process
kill 'TERM', $shell_pid if $shell_pid;
Gtk3->main_quit;
}