bump to v3.5; add built-in editor

This commit is contained in:
Juerd Waalboer 2022-08-30 20:44:12 +02:00
parent defe8d490c
commit 43a1990974
5 changed files with 207 additions and 3 deletions

165
lib/RevBank/TextEditor.pm Normal file
View file

@ -0,0 +1,165 @@
package RevBank::TextEditor;
use v5.28;
use warnings;
use feature qw(signatures);
no warnings qw(experimental::signatures);
use autodie;
use RevBank::Global;
use Fcntl qw(:flock);
use Carp qw(croak);
use Time::HiRes qw(sleep);
my $tab = 4;
sub _find_next($win, $textref) {
my $editor = $win->getobj('editor');
my $find = $win->getobj('find');
my $a = $find->getobj('answer');
my $b = $find->getobj('buttons');
my $q = $a->get;
pos($$textref) = $editor->pos;
my $status = "not found";
my $offset;
if ($$textref =~ /\Q$q/gi) {
$status = "found";
$offset = $+[0];
} else {
$editor->pos(0);
pos($$textref) = 0;
if ($$textref =~ /\Q$q/gi) {
$status = "wrapped";
$offset = $+[0];
}
}
$find->{-title} = ucfirst $status;
if ($status ne "not found") {
$editor->pos($offset);
$editor->{-search_highlight} = $editor->{-ypos};
} else {
$editor->{-search_highlight} = undef;
}
$win->draw;
}
sub _find($win) {
my $editor = $win->getobj('editor');
my $text = $editor->get;
my $find = $win->add(
'find', 'Dialog::Question',
-question => "Search for:",
-buttons => [
{ -label => '[Find next]', -onpress => sub {
_find_next($win, \$text);
} },
{ -label => '[Cancel]', -onpress => sub {
$win->getobj('find')->loose_focus;
$editor->{-search_highlight} = undef;
} },
]
);
my $a = $find->getobj('answer');
my $b = $find->getobj('buttons');
$a->onFocus( sub { shift->pos(999) } );
$a->set_binding(sub {
$b->{-selected} = 0; # [Find next]
$b->focus;
$b->press_button;
$win->draw;
}, Curses::KEY_ENTER());
$find->set_binding(sub {
$b->{-selected} = 1; # [Cancel]
$b->focus;
$b->press_button;
$win->draw;
}, "\cX", "\cC");
$b->set_routine('press-button' => sub { $b->press_button });
$find->modalfocus;
$win->delete('find');
}
sub _editor($title, $data) {
our $cui ||= Curses::UI->new;
my $win = $cui->add('main', 'Window');
$title = "[$title] Ctrl+X: exit Ctrl+F: find Ctrl+C/K/V: copy/cut/paste";
my $editor = $win->add(
'editor', 'TextEditor',
-title => $title,
-text => $data,
-border => 1,
-padbottom => 1, # ibm3151/screen lastline corner glitch workaround
-wrapping => 0,
-hscrollbar => 0,
-vscrollbar => 0,
);
my $return;
my @keys = (
[ Curses::KEY_HOME() => 'cursor-scrlinestart' ],
[ Curses::KEY_END() => 'cursor-scrlineend' ],
[ "\cK" => 'delete-line' ], # nano (can't do meta/alt for M-m)
[ "\cU" => 'paste' ], # nano
[ "\c[" => sub { } ],
[ "\cL" => sub { $cui->draw } ],
[ "\c^" => sub { $editor->pos(0) } ],
[ "\c_" => sub { $editor->pos(length($editor->get)) } ],
[ "\cI" => sub { $editor->add_string(" " x ($tab - ($editor->{-xpos} % $tab))) } ],
[ "\cS" => sub { $cui->dialog("Enable flow control :)") } ],
[ "\cQ" => sub {} ],
[ "\cC" => sub { $editor->{-pastebuffer} = $editor->getline_at_ypos($editor->{-ypos}) } ],
[ "\cF" => sub { _find($win) } ],
[ "\cX" => sub {
my $answer = $cui->dialog(
-message => "Save changes?",
-buttons => [
{ -label => "[Save]", -value => 1 },
{ -label => "[Discard]", -value => 0 },
{ -label => "[Cancel]", -value => -1 },
],
-values => [ 1, 0 ],
);
$return = $editor->get if $answer == 1;
$cui->mainloopExit if $answer >= 0;
} ],
);
$editor->set_binding(reverse @$_) for @keys;
$editor->focus();
$cui->mainloop;
$cui->leave_curses;
$cui->delete('main');
return $return;
}
sub edit($filename) {
if (not eval { require Curses::UI }) {
my $install = -e "/etc/debian_version"
? "apt install libcurses-ui-perl"
: "cpan Curses::UI";
die "Couldn't load the Perl module Curses::UI.\n" .
"Please install it! (sudo $install)\n";
}
open my $fh, ">>", $filename;
flock $fh, LOCK_EX | LOCK_NB
or die "Someone else is alreading editing $filename.\n";
my $save = _editor($filename, scalar slurp $filename);
spurt $filename, $save if defined $save;
}
1;

View file

@ -0,0 +1,37 @@
=head1 NAME
RevBank::TextEditor - Basic Lightweight User-friendly TextEditor
=head1 SYNOPSIS
require RevBank::TextEditor;
RevBank::TextEditor::edit($filename);
=head1 DESCRIPTION
BLUT is a built-in text editor based on Curses::UI.
It was made because vim is too hard for unprepared newbies, and nano too,
really: not everyone knows that C<^X> means Ctrl+X, making nano almost as hard
to exit as vim. And of course, none of the really user friendly editors out
there would work well on our old IBM 3151 terminal. (For instance, C<^S> and
C<^Q> are used for software flow control, or as the manual of said terminal
calls it, "pacing".)
And of course, all the editors out there will let you open other files, or even
run shells...
=head2 Functions
=head3 edit($filename)
Runs the editor.
=head2 CAVEATS
=over 2
=item * It's a really dumb editor, and many unsupported presses will end up as
garbage.
=back

View file

@ -23,7 +23,8 @@ sub _read_market() {
sub command :Tab(market,&tab) ($self, $cart, $command, @) {
if ($command eq 'market') {
system $ENV{EDITOR} || 'vi', $filename;
require RevBank::TextEditor;
RevBank::TextEditor::edit($filename);
return ACCEPT;
}

View file

@ -23,7 +23,8 @@ sub _read_products() {
sub command :Tab(edit,&tab) ($self, $cart, $command, @) {
if ($command eq 'edit') {
system $ENV{EDITOR} || 'vi', $filename;
require RevBank::TextEditor;
RevBank::TextEditor::edit($filename);
return ACCEPT;
}

View file

@ -18,7 +18,7 @@ use RevBank::Global;
use RevBank::Messages;
use RevBank::Cart;
our $VERSION = "3.4";
our $VERSION = "3.5";
our %HELP1 = (
"abort" => "Abort the current transaction",
);