Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Hakim Cassimally
committed
May 20, 2011
1 parent
d9720d7
commit 98beca0
Showing
4 changed files
with
224 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
package Text::PieceTable; | ||
use Text::PieceTable::Source; | ||
use Text::PieceTable::Piece; | ||
use List::Util 'sum'; | ||
|
||
use Data::Dumper; | ||
local $Data::Dumper::Indent = 1; | ||
|
||
use Moose; | ||
|
||
has source => ( | ||
isa => 'Text::PieceTable::Source', | ||
is => 'ro', | ||
default => sub { | ||
Text::PieceTable::Source->new({ | ||
data => '', | ||
}) | ||
}, | ||
); | ||
|
||
has additional => ( | ||
isa => 'Text::PieceTable::Source', | ||
is => 'ro', | ||
default => sub { | ||
Text::PieceTable::Source->new({ | ||
data => '', | ||
}) | ||
}, | ||
); | ||
has pieces => ( | ||
traits => ['Array'], | ||
is => 'ro', | ||
isa => 'ArrayRef[Text::PieceTable::Piece]', | ||
default => sub { [] }, | ||
handles => { | ||
all_pieces => 'elements', | ||
find_piece => 'first', | ||
map_pieces => 'map', | ||
push_piece => 'push', | ||
}, | ||
); | ||
|
||
sub from_string { | ||
my ($class, $string) = @_; | ||
|
||
my $source = Text::PieceTable::Source->new({ | ||
data => $string, | ||
}); | ||
my $self = $class->new(source => $source); | ||
if (length $string) { | ||
$self->push_piece($source->whole_piece); | ||
} | ||
return $self; | ||
} | ||
|
||
sub piece_at_pos { | ||
my ($self, $pos) = @_; | ||
|
||
my $i = 0; | ||
my @before; | ||
my @pieces = $self->all_pieces; | ||
|
||
while (my $piece = shift @pieces) { | ||
my $len = $piece->length; | ||
if ($i + $len > $pos) { | ||
return ($piece, $pos - $i, \@before, \@pieces); | ||
} | ||
if ($i + $len == $pos and !@pieces) { | ||
return ($piece, $len, \@before, \@pieces); | ||
} | ||
push @before, $piece; | ||
$i += $len; | ||
} | ||
die "Invalid pos $pos"; | ||
} | ||
|
||
sub insert { | ||
my ($self, $pos, $text) = @_; | ||
|
||
my $piece = $self->additional->append($text); | ||
return $self->insert_piece($pos, $piece); | ||
} | ||
|
||
sub insert_piece { | ||
my ($self, $pos, $insert) = @_; | ||
my ($piece, $i, $before, $after) = | ||
$self->piece_at_pos($pos); | ||
|
||
my ($pre,$post) = $piece->split_at($i); | ||
|
||
my $pieces = [ | ||
@$before, | ||
$pre ? $pre : (), | ||
$insert, | ||
$post ? $post : (), | ||
@$after, | ||
]; | ||
return $self->new( | ||
source => $self->source, | ||
additional => $self->additional, | ||
pieces => $pieces, | ||
); | ||
} | ||
|
||
sub as_string { | ||
my $self = shift; | ||
return join '' => $self->map_pieces( sub { $_->as_string } ); | ||
} | ||
sub length { | ||
my $self = shift; | ||
return sum $self->map_pieces( sub { $_->length } ); | ||
} | ||
|
||
no Moose; 1; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package Text::PieceTable::Piece; | ||
|
||
use Moose; | ||
|
||
has source => ( | ||
isa => 'Text::PieceTable::Source', | ||
is => 'ro', | ||
); | ||
|
||
has from => ( | ||
isa => 'Int', | ||
is => 'ro', | ||
); | ||
|
||
has length => ( | ||
isa => 'Int', | ||
is => 'ro', | ||
); | ||
|
||
sub as_string { | ||
my $self = shift; | ||
return $self->source->substr($self->from, $self->length); | ||
} | ||
|
||
sub split_at { | ||
my ($self, $pos) = @_; | ||
if ($pos) { | ||
if ($pos < $self->length) { | ||
my $source = $self->source; | ||
return ( | ||
Text::PieceTable::Piece->new( | ||
source => $source, | ||
from => $self->from, | ||
length => $pos, | ||
), | ||
Text::PieceTable::Piece->new( | ||
source => $source, | ||
from => $self->from + $pos, | ||
length => $self->length - $pos, | ||
) | ||
); | ||
} | ||
else { | ||
return ($self, undef); | ||
} | ||
} | ||
else { | ||
return (undef, $self); | ||
} | ||
} | ||
|
||
no Moose; 1; |
38 changes: 38 additions & 0 deletions
38
scratch/perl/text-piecetable/lib/Text/PieceTable/Source.pm
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package Text::PieceTable::Source; | ||
|
||
use Moose; | ||
|
||
has data => ( | ||
traits => ['String'], | ||
isa => 'Str', | ||
is => 'rw', # only appendable, so easily shared! | ||
default => '', | ||
handles => { | ||
_append => 'append', | ||
length => 'length', | ||
substr => 'substr', | ||
}, | ||
); | ||
|
||
sub append { | ||
my ($self, $string) = @_; | ||
my $pos = $self->length; | ||
|
||
$self->_append($string); | ||
return Text::PieceTable::Piece->new( | ||
source => $self, | ||
from => $pos, | ||
length => length $string, | ||
); | ||
} | ||
|
||
sub whole_piece { | ||
my $self = shift; | ||
return Text::PieceTable::Piece->new( | ||
source => $self, | ||
from => 0, | ||
length => $self->length, | ||
); | ||
} | ||
|
||
no Moose; 1; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
use Test::More; | ||
use Data::Dumper; | ||
local $Data::Dumper::Indent = 1; | ||
|
||
use Text::PieceTable; | ||
my $pt = Text::PieceTable->from_string('fox'); | ||
is $pt->as_string, 'fox'; | ||
|
||
$pt = $pt->insert(0, 'the quick '); | ||
is $pt->as_string, 'the quick fox'; | ||
|
||
|
||
# $pt = $pt->insert(-1, ' jumps over the lazy dog'); | ||
$pt = $pt->insert($pt->length, ' jumps over the lazy dog'); | ||
is $pt->as_string, 'the quick fox jumps over the lazy dog'; | ||
|
||
$pt = $pt->insert(10, 'brown '); | ||
is $pt->as_string, 'the quick brown fox jumps over the lazy dog'; | ||
|
||
done_testing; |