Caroline
view release on metacpan or search on metacpan
lib/Caroline.pm view on Meta::CPAN
$state->{pos}--;
}
my $diff = $old_pos - $state->pos;
substr($state->{buf}, $state->pos, $diff) = '';
$self->refresh_line($state);
}
sub edit_history_next {
my ($self, $state, $dir) = @_;
if ($self->history_len > 1) {
$self->history->[$self->history_len-1-$state->{history_index}] = $state->buf;
$state->{history_index} += ( ($dir == $HISTORY_PREV) ? 1 : -1 );
if ($state->{history_index} < 0) {
$state->{history_index} = 0;
return;
} elsif ($state->{history_index} >= $self->history_len) {
$state->{history_index} = $self->history_len-1;
return;
}
$state->{buf} = $self->history->[$self->history_len - 1 - $state->{history_index}];
$state->{pos} = $state->len;
$self->refresh_line($state);
}
}
sub edit_backspace {
my ($self, $state) = @_;
if ($state->pos > 0 && length($state->buf) > 0) {
substr($state->{buf}, $state->pos-1, 1) = '';
$state->{pos}--;
$self->refresh_line($state);
}
}
sub clear_screen {
my ($self) = @_;
print STDOUT "\x1b[H\x1b[2J";
}
sub refresh_line {
my ($self, $state) = @_;
if ($self->{multi_line}) {
$self->refresh_multi_line($state);
} else {
$self->refresh_single_line($state);
}
}
sub refresh_multi_line {
my ($self, $state) = @_;
my $plen = vwidth($state->prompt);
$self->debug($state->buf. "\n");
# rows used by current buf
my $rows = int(($plen + vwidth($state->buf) + $state->cols -1) / $state->cols);
if (defined $state->query) {
$rows++;
}
# cursor relative row
my $rpos = int(($plen + $state->oldpos + $state->cols) / $state->cols);
my $old_rows = $state->maxrows;
# update maxrows if needed.
if ($rows > $state->maxrows) {
$state->maxrows($rows);
}
$self->debug(sprintf "[%d %d %d] p: %d, rows: %d, rpos: %d, max: %d, oldmax: %d",
$state->len, $state->pos, $state->oldpos, $plen, $rows, $rpos, $state->maxrows, $old_rows);
# First step: clear all the lines used before. To do start by going to the last row.
if ($old_rows - $rpos > 0) {
$self->debug(sprintf ", go down %d", $old_rows-$rpos);
printf STDOUT "\x1b[%dB", $old_rows-$rpos;
}
# Now for every row clear it, go up.
my $j;
for ($j=0; $j < ($old_rows-1); ++$j) {
$self->debug(sprintf ", clear+up %d %d", $old_rows-1, $j);
print("\x1b[0G\x1b[0K\x1b[1A");
}
# Clean the top line
$self->debug(", clear");
print("\x1b[0G\x1b[0K");
# Write the prompt and the current buffer content
print $state->prompt;
print $state->buf;
if (defined $state->query) {
print "\015\nSearch: " . $state->query;
}
# If we are at the very end of the screen with our prompt, we need to
# emit a newline and move the prompt to the first column
if ($state->pos && $state->pos == $state->len && ($state->pos + $plen) % $state->cols == 0) {
$self->debug("<newline>");
print "\n";
print "\x1b[0G";
$rows++;
if ($rows > $state->maxrows) {
$state->maxrows(int $rows);
}
}
# Move cursor to right position
my $rpos2 = int(($plen + $state->vpos + $state->cols) / $state->cols); # current cursor relative row
$self->debug(sprintf ", rpos2 %d", $rpos2);
# Go up till we reach the expected position
if ($rows - $rpos2 > 0) {
# cursor up
printf "\x1b[%dA", $rows-$rpos2;
}
# Set column
my $col;
{
$col = 1;
my $buf = $state->prompt . substr($state->buf, 0, $state->pos);
for (split //, $buf) {
$col += vwidth($_);
if ($col > $state->cols) {
$col -= $state->cols;
}
}
}
$self->debug(sprintf ", set col %d", $col);
printf "\x1b[%dG", $col;
$state->oldpos($state->pos);
$self->debug("\n");
}
sub refresh_single_line {
my ($self, $state) = @_;
my $buf = $state->buf;
my $len = $state->len;
my $pos = $state->pos;
while ((vwidth($state->prompt)+$pos) >= $state->cols) {
substr($buf, 0, 1) = '';
$len--;
$pos--;
}
while (vwidth($state->prompt) + vwidth($buf) > $state->cols) {
$len--;
}
print STDOUT "\x1b[0G"; # cursor to left edge
print STDOUT $state->{prompt};
print STDOUT $buf;
print STDOUT "\x1b[0K"; # erase to right
# Move cursor to original position
printf "\x1b[0G\x1b[%dC", (
length($state->{prompt})
+ vwidth(substr($buf, 0, $pos))
);
}
sub edit_move_right {
my ($self, $state) = @_;
if ($state->pos != length($state->buf)) {
$state->{pos}++;
$self->refresh_line($state);
}
}
sub edit_move_left {
my ($self, $state) = @_;
if ($state->pos > 0) {
$state->{pos}--;
$self->refresh_line($state);
}
}
sub edit_insert {
my ($self, $state, $c) = @_;
if (length($state->buf) == $state->pos) {
$state->{buf} .= $c;
} else {
substr($state->{buf}, $state->{pos}, 0) = $c;
}
$state->{pos}++;
$self->refresh_line($state);
}
sub is_supported {
my ($self) = @_;
return 1 if $IS_WIN32;
my $term = $ENV{'TERM'};
return 0 unless defined $term;
return 0 if $term eq 'dumb';
return 0 if $term eq 'cons25';
return 1;
}
package Caroline::State;
use Class::Accessor::Lite 0.05 (
rw => [qw(buf pos cols prompt oldpos maxrows query)],
);
sub new {
my $class = shift;
bless {
buf => '',
pos => 0,
history_index => 0,
oldpos => 0,
maxrows => 0,
}, $class;
}
( run in 1.347 second using v1.01-cache-2.11-cpan-39bf76dae61 )