Tk-TextVi
view release on metacpan or search on metacpan
lib/Tk/TextVi.pm view on Meta::CPAN
# Only invoke the special split-variant functions when we're using
# :split
my @split_func = qw| delete insert tagAdd tagConfigure tagRemove |;
{
no strict;
for my $func ( @split_func ) {
*{ "split_$func" } = sub {
my ($w,@args) = @_;
if( defined $w->{VI_SPLIT_SHARE} ) {
for my $win ( @{ $w->{VI_SPLIT_SHARE} } ) {
"Tk::TextUndo::$func"->( $win, @args );
}
}
else {
"Tk::TextUndo::$func"->( $w, @args );
}
}
}
}
sub vi_split {
my ($w,$newwin) = @_;
# First time replace all the functions with the magical split versions
if( not defined $w->{VI_SPLIT_SHARE} ) {
$w->{VI_SPLIT_SHARE} = [ $w ];
no strict;
for my $func (@split_func) {
*{"Tk::TextVi::$func"} = \&{"split_$func"};
}
}
$newwin->Contents( $w->Contents );
$newwin->SetCursor( $w->index('insert') );
$newwin->yviewMoveto( ($w->yview)[0] );
push @{$w->{VI_SPLIT_SHARE}}, $newwin;
$newwin->{VI_SPLIT_SHARE} = $w->{VI_SPLIT_SHARE}
}
# Public Methods #####################################################
sub viMode {
my ($w, $mode) = @_;
my $rv = $w->{VI_SUBMODE} . $w->{VI_MODE};
$rv .= 'q' if defined $w->{VI_RECORD_REGISTER};
if( defined $mode ) {
croak "Tk::TextVi received invalid mode '$mode'"
if $mode !~ m[ ^ [nicvVR/] $ ]x;
$w->{VI_MODE} = $mode;
$w->{VI_SUBMODE} = '';
$w->{VI_PENDING} = '';
$w->{VI_REPLACE_CHARS} = '';
$w->tagRemove( 'sel', '1.0', 'end' );
# XXX: Hack
if( (caller)[0] eq 'Tk::TextVi' ) {
$w->{VI_FLAGS} |= F_STAT;
}
else {
# TODO: this is broken
$w->Callback( '-statuscommand', $w->{VI_MODE}, $w->{VI_PENDING} );
}
}
return $rv;
}
sub viPending {
my ($w) = @_;
return $w->{VI_PENDING};
}
sub viError {
my ($w) = @_;
return shift @{ $w->{VI_ERROR} };
}
sub viMessage {
my ($w) = @_;
return shift @{ $w->{VI_MESSAGE} };
}
sub viMap {
my ( $w, $mode, $sequence, $ref, $force ) = @_;
# TODO: nmap,imap,vmap etc. support
my @mapmodes = map { $w->{MAPS}{$_} } split //, $mode;
while( length( $sequence ) > 1 ) {
# Get the next character in the sequence
my $c = substr $sequence, 0, 1, '';
# Advance the mapping locations
for my $map ( @mapmodes ) {
# Nothing at this location yet, add a hash
if( not defined $map->{$c} ) {
$map->{$c} = { };
}
# Something is already mapped here
elsif( 'HASH' ne ref $map->{$c} ) {
return unless $force;
# If $force was defined, nuke the previous entry
$map->{$c} = { };
}
$map = $map->{$c};
}
}
# Check that a mapping can be placed here
for my $map ( @mapmodes ) {
if( defined $map->{$sequence} and # Something is here
'HASH' eq ref $map->{$sequence} and # it's a longer mapping
scalar keys %{ $map->{$sequence}} ) # and its in use
{
lib/Tk/TextVi.pm view on Meta::CPAN
}
elsif( $key eq TAB ) {
my $sts = $w->settingGet( 'softtabstop' );
if( $sts > 0 ) {
my $col = $w->index('insert');
(undef,$col) = split /\./, $col;
# Perl's modulus is well behaved so this works fine
$col = (-$col % $sts) || $sts;
$w->insert( 'insert', ' ' x $col );
}
else {
$w->insert( 'insert', "\t" );
}
}
elsif( $key eq "\cO" ) {
$w->viMode('n');
$w->{VI_SUBMODE} = 'i';
}
else {
$w->insert( 'insert', $key );
$w->see( 'insert' );
}
}
elsif( $w->{VI_MODE} eq 'R') {
if( $key eq ESC ) {
$w->addGlobEnd;
$w->viMode('n');
$w->SetCursor( 'insert -1c' )
if( $w->compare( 'insert', '!=', 'insert linestart' ) );
}
elsif( $key eq BKSP ) {
my $r = chop $w->{VI_REPLACE_CHARS};
if( $r ne '' ) {
$w->delete( "insert -1c" );
if( $r ne "\0" ) {
$w->insert( 'insert', $r );
$w->SetCursor( 'insert -1c' );
}
}
else {
$w->SetCursor( 'insert -1c' );
}
}
elsif( $w->get( 'insert' ) ne "\n" ) {
$w->{VI_REPLACE_CHARS} .= $w->get( 'insert' );
$w->delete( 'insert' );
$w->insert( 'insert', $key );
}
else {
$w->{VI_REPLACE_CHARS} .= "\0";
$w->insert( 'insert', $key );
}
}
else {
die "Tk::TextVi internal state corrupted";
}
# Does the UI need to update?
# XXX: HACK
if( (caller)[0] ne 'Tk::TextVi' ) {
$w->Callback( '-statuscommand',
$w->viMode,
$w->{VI_PENDING} ) if( $w->{VI_FLAGS} & F_STAT );
$w->Callback( '-messagecommand' ) if $w->{VI_FLAGS} & F_MSG ;
$w->Callback( '-errorcommand' ) if $w->{VI_FLAGS} & F_ERR ;
$w->{VI_FLAGS} = 0;
}
# Command may have moved insert cursor out of window
$w->see('insert');
}
# Handles the command processing shared between Normal
# and visual mode commands
sub InsertKeypressNormal {
my ($w,$key) = @_;
my $res;
my $keys = $w->{VI_PENDING} . $key;
$w->{VI_PENDING} = ''; # Assume command will work
eval { $res = $w->EvalKeys($keys); };# try to process as a command
if( $@ ) {
die $@ if $@ !~ /^VI_/; # wasn't our exception
if( $@ eq X_NO_KEYS ) { # Restore pending keys
$w->{VI_PENDING} = $keys;
}
}
elsif ( lc $w->{VI_MODE} eq 'v' ) {
# hack, clear visual mode after command
ref $res or $w->viMode('n');
}
else {
# Restore mode
$w->viMode( $w->{VI_SUBMODE} ) if $w->{VI_SUBMODE};
}
$w->{VI_FLAGS} |= F_STAT;
return $res;
}
# Takes a string of keypresses and dispatches it to the right command
sub EvalKeys {
my ($w, $keys, $count, $register, $motion) = @_;
my $res;
my $mode = lc substr $w->{VI_MODE}, 0, 1; # V and v use the same maps
$count = 0 unless defined $count;
# Use the currently pending keys by default
$keys = $w->{VI_PENDING} unless defined $keys;
# Extract the count
if( $keys =~ s/^([1-9]\d*)// ) {
$count ||= 1;
$count *= $1;
}
( run in 1.083 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )