[Templates-cvs] cvs commit: TT3/lib/Template/TT3 Tag.pm

cvs@template-toolkit.org cvs@template-toolkit.org
Thu, 11 Dec 2003 15:11:01 +0000


cvs         03/12/11 15:11:01

  Modified:    lib/Template/TT3 Tag.pm
  Log:
  * improved scanning logic to handle open/closed tags and account for
    line numbers automagically
  
  Revision  Changes    Path
  1.2       +120 -69   TT3/lib/Template/TT3/Tag.pm
  
  Index: Tag.pm
  ===================================================================
  RCS file: /template-toolkit/TT3/lib/Template/TT3/Tag.pm,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Tag.pm	2003/12/10 14:15:24	1.1
  +++ Tag.pm	2003/12/11 15:11:01	1.2
  @@ -17,7 +17,7 @@
   #   modify it under the same terms as Perl itself.
   #
   # REVISION
  -#   $Id: Tag.pm,v 1.1 2003/12/10 14:15:24 abw Exp $
  +#   $Id: Tag.pm,v 1.2 2003/12/11 15:11:01 abw Exp $
   #
   #========================================================================
   
  @@ -29,13 +29,13 @@
   use vars qw( $VERSION $DEBUG $ERROR $WARNING $TAG );
   use base qw( Template::TT3::Base );
   
  -$VERSION = sprintf("%d.%02d", q$Revision: 1.1 $ =~ /(\d+)\.(\d+)/);
  +$VERSION = sprintf("%d.%02d", q$Revision: 1.2 $ =~ /(\d+)\.(\d+)/);
   $DEBUG   = 0 unless defined $DEBUG;
   $ERROR   = '';
   $TAG     = {
       start => '[%',
       end   => '%]',
  -    name  => 'tt3tag',
  +    name  => 'tag',
   };
   
   
  @@ -70,49 +70,6 @@
   
   
   #------------------------------------------------------------------------
  -# name()
  -# name($name)
  -#
  -# Accessor method to get/set the optional tag name.
  -#------------------------------------------------------------------------
  -
  -sub name {
  -    my $self = shift;
  -    return @_ ? ($self->{ name } = shift) : $self->{ name };
  -}
  -
  -
  -#------------------------------------------------------------------------
  -# start()
  -# start($token)
  -#
  -# Accessor method to get/set the start token.
  -#------------------------------------------------------------------------
  -
  -sub start {
  -    my $self = shift;
  -    return @_ ? ($self->{ start } = shift) : $self->{ start };
  -}
  -
  -
  -#------------------------------------------------------------------------
  -# end()
  -# end($token)
  -#
  -# Accessor method to get/set the end token.
  -#------------------------------------------------------------------------
  -
  -sub end {
  -    my $self = shift;
  -    if (@_) {
  -        $self->{ end } = shift;
  -        delete $self->{ end_regex };
  -    }
  -    return $self->{ end };
  -}
  -
  -
  -#------------------------------------------------------------------------
   # scan($start_tag, $textref, $document)
   #
   # Called when a start tag is matched, this method scans up to any defined
  @@ -121,12 +78,17 @@
   
   sub scan {
       my ($self, $start, $textref, $document) = @_;
  -    my ($end, $regex, $content);
  +    my ($end, $regex, $content, $size, $result);
       $self->{ start_match } = $start;
   
       $self->debug("Tag<$self->{ name }> scan()\n") if $DEBUG;
  +
  +    if (defined ($end = $self->{ end }) && length($end)) {
  +        # this is a closed tag that has pre-defined start and end tokens.
  +        # we scan up to the end tag, figure out how many lines the directive
  +        # covers and then call the parse() method to parse the contents
   
  -    if (defined ($end = $self->{ end })) {
  +        # generate regex to match end tag
           $regex = $self->{ end_regex } ||= do {
               $end = ref $end eq 'Regexp' ? $end : quotemeta($end);
               qr/ \G (.*?) ($end) /sx;
  @@ -134,23 +96,52 @@
   
           $self->debug("scanning for end with $end\n") if $DEBUG;
   
  -        if ($$textref =~ /$regex/gc) {
  -            my $content = $1;
  -            $self->{ end_match } = $2;
  -            $self->debug( 'matched content: "', 
  -                          $self->_inspect_text($content),
  -                          "\"\n") if $DEBUG;
  -            return $self->parse(\$content, $document);
  -        }
  -        else {
  -            return $self->error("missing end tag: $self->{ end }");
  -        }
  +        # scan for closing tag and report error if not found
  +        return $self->error("no closing tag to match $start")
  +            unless $$textref =~ /$regex/gc;
  +        $content = $1;
  +        $end = $self->{ end_match } = $2;
  +
  +        # count lines in start tag, content and end tag
  +        $size = ($start =~ tr/\n//) + ($content =~ tr/\n//) + ($end =~ tr/\n//);
  +        $document->size($size) if $size;
  +
  +        # call parse method to parse tag content
  +        $result = $self->parse(\$content, $document);
  +
  +        # TODO: check for trailing cruft in content?
  +
  +        # update document position if required
  +        $document->move() if $size;
       }
       else {
  -        return $self->parse($textref, $document);
  +        # an open tag that has a start but no pre-defined end token.
  +        # we save the current regex position, call the parse method to 
  +        # gobble up as much of the text as it likes, and then examine 
  +        # the regex position again, and count the newlines in the
  +        # intervening text to update the document line position
  +
  +        # save starting regex position
  +        my $start_pos = pos($$textref) || 0;
  +
  +        # no end match
  +        $end = $self->{ end_match } = undef;
  +
  +        $self->debug("parsing open tag\n") if $DEBUG;
  +
  +        # parse item
  +        $result = $self->parse($textref, $document);
  +    
  +        # count any newlines consumed by parser between start and end positions
  +        my $end_pos = pos $$textref || 0;
  +        my $substr  = substr($$textref, $start_pos, $end_pos - $start_pos);
  +        $size = ($substr =~ tr/\n//);
  +
  +        # update document position if required
  +        $document->move($size) if $size;
       }
   
  -    die 'not reached';
  +    return $result;
   }
   
   
  @@ -160,6 +151,8 @@
   # Method to parse the tag content, usually redefined by subclasses to do
   # something useful.  Otherwise, the action() method is called to activate
   # any other handler for the tag.
  +#
  +# TODO: perhaps the default should be to call $document->body()
   #------------------------------------------------------------------------
   
   sub parse {
  @@ -182,6 +175,8 @@
           $result = $action;
       }
       elsif (! ref $action) {
  +        # TODO: not sure about this
  +
           # method name to call against document
           $result = $document->$action($textref)
               || $self->error($document->error());
  @@ -195,24 +190,80 @@
   
   
   #------------------------------------------------------------------------
  -# action()
  -# action($action)
  +# start() / start($token)
  +# end() / end($token)
  +# name() / name($name)
  +# action() / action($name)
   #
  -# Accessor method to get/set the tag action handler.
  +# Accessor methods to get/set the start tag, end tag and optional tag 
  +# name and action attributes.
   #------------------------------------------------------------------------
   
  +sub start {
  +    my $self = shift;
  +    return @_ ? ($self->{ start } = shift) : $self->{ start };
  +}
  +
  +sub end {
  +    my $self = shift;
  +    if (@_) {
  +        $self->{ end } = shift;
  +        delete $self->{ end_regex };
  +    }
  +    return $self->{ end };
  +}
  +
  +sub name {
  +    my $self = shift;
  +    return @_ ? ($self->{ name } = shift) : $self->{ name };
  +}
  +
   sub action {
       my $self = shift;
       return @_ ? ($self->{ action } = shift) : $self->{ action };
   }
   
   
  +#------------------------------------------------------------------------
  +# start_match()
  +# end_match()
  +# 
  +# Accessor methods to return the actual text matched for the start and 
  +# end tags respectively.  If the start and end tags are strings then
  +# these will contain the same values.  If the start and end tags are 
  +# regexen, then the start_match and end_match may vary.  These values
  +# can only be guaranteed to be correct in the context of a call to the 
  +# scan() method.
  +#------------------------------------------------------------------------
  +
  +sub start_match {
  +    my $self = shift;
  +    return @_ ? ($self->{ start_match } = shift) : $self->{ start_match };
  +}
  +
  +sub end_match {
  +    my $self = shift;
  +    return @_ ? ($self->{ end_match } = shift) : $self->{ end_match };
  +}
  +
  +
  +#------------------------------------------------------------------------
  +# is_open()
  +# is_closed()
  +#
  +# Methods which return boolean flags to indicate if the tag has an 
  +# end defined, in which case it is closed, or not, in which case it is
  +# open.
  +#------------------------------------------------------------------------
  +
  +sub is_open {
  +    my $self = shift;
  +    return defined($self->{ end }) && length($self->{ end }) ? 0 : 1;
  +}
   
  -sub parse_error {
  +sub is_closed {
       my $self = shift;
  -    my $document = shift;
  -    $document->parse_error(@_);
  -    return $self->error($document->error());
  +    return defined($self->{ end }) && length($self->{ end }) ? 1 : 0;
   }
   
   
  @@ -246,7 +297,7 @@
   
   =head1 VERSION
   
  -$Revision: 1.1 $
  +$Revision: 1.2 $
   
   =head1 COPYRIGHT