Skip to content

Commit

Permalink
PieceTable initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Hakim Cassimally committed May 20, 2011
1 parent d9720d7 commit 98beca0
Show file tree
Hide file tree
Showing 4 changed files with 224 additions and 0 deletions.
114 changes: 114 additions & 0 deletions scratch/perl/text-piecetable/lib/Text/PieceTable.pm
@@ -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;
52 changes: 52 additions & 0 deletions scratch/perl/text-piecetable/lib/Text/PieceTable/Piece.pm
@@ -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 scratch/perl/text-piecetable/lib/Text/PieceTable/Source.pm
@@ -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;
20 changes: 20 additions & 0 deletions scratch/perl/text-piecetable/t/01-basic.t
@@ -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;

0 comments on commit 98beca0

Please sign in to comment.