Skip to content

Commit

Permalink
Item13570: I was right, it wasn't easy to fix, but I think I've got i…
Browse files Browse the repository at this point in the history
…t working consistently now. The only slight unknown is exactly what it means when there is plain text on the same line a s a TABLE/EDITTABLE tag, but I think that's an unusual enough case that we can afford to change the rule slightly.
  • Loading branch information
Crawford Currie committed Aug 27, 2015
1 parent e54f9df commit 45bceb5
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 123 deletions.
25 changes: 16 additions & 9 deletions EditRowPlugin/lib/Foswiki/Plugins/EditRowPlugin/Table.pm
Expand Up @@ -13,7 +13,7 @@ use Foswiki::Plugins::EditRowPlugin ();
use Foswiki::Plugins::EditRowPlugin::TableRow ();
use Foswiki::Plugins::EditRowPlugin::Editor ();

use constant TRACE => 0;
use constant TRACE => 1;

use constant {

Expand All @@ -35,25 +35,30 @@ use constant {
# Foswiki/Plugins/EditRowPlugin/Editor/*.pm
our %editors = ( _default => Foswiki::Plugins::EditRowPlugin::Editor->new() );

# $spec - full spec of the table (e.g. the complete edittable)
# $attrs - Foswiki::Attrs of the relevant %EDITTABLE plus optional TABLE
# Parameters are defined in Foswiki::Table
# See EditRowPlugin.txt for a description of the attributes supported,
# plus the following undocumented attributes:
# =require_js= - compatibility, true maps to =js='assumed'=,
# false to =js='preferred'=

sub new {
my ( $class, $spec, $attrs ) = @_;
my ( $class, $specs ) = @_;

ASSERT( $attrs->isa('Foswiki::Attrs') ) if DEBUG;
my $attrs;
foreach my $spec (@$specs) {
$attrs = $spec->{attrs} if ( $spec->{tag} eq 'EDITTABLE' );
}

# If there's no EDITTABLE tag, we just want a conventional table
return new Foswiki::Table($specs) unless $attrs;

# if headerislabel true but no headerrows, set headerrows = 1
if ( $attrs->{headerislabel} && !defined( $attrs->{headerrows} ) ) {
$attrs->{headerrows} =
Foswiki::isTrue( $attrs->{headerislabel} ) ? 1 : 0;
}

my $this = $class->SUPER::new( $spec, $attrs );
my $this = $class->SUPER::new($specs);
$this->{editable} = $attrs->{isEditable};

# EditTablePlugin compatibility; headerrows trumps headerislabel
Expand Down Expand Up @@ -105,9 +110,11 @@ sub row_class {
}

# Override Foswiki::Tables::Table
sub getMacro {
return $Foswiki::cfg{Plugins}{EditRowPlugin}{Macro}
|| 'EDITTABLE';
sub getMacros {
my $this = shift;
my @list = $this->SUPER::getMacros();
push( @list, $Foswiki::cfg{Plugins}{EditRowPlugin}{Macro} || 'EDITTABLE' );
return @list;
}

sub getWeb {
Expand Down
Expand Up @@ -95,7 +95,7 @@ sub parse {
# Called from the early_line handler to adjust default attributes based
# on what is in the URL params.
sub adjustSpec {
my ( $this, $attrs ) = @_;
my ( $this, $macro, $attrs ) = @_;

# Signal that the following table is editable to the table
# accretion code
Expand All @@ -119,6 +119,8 @@ sub adjustSpec {
$attrs->{footerrows} = $this->{params}->{"$pf}_footerrows"};
}
}

return $macro eq 'EDITTABLE' ? 1 : -1;
}

1;
Expand Down
121 changes: 88 additions & 33 deletions core/lib/Foswiki/Tables/Parser.pm
Expand Up @@ -51,8 +51,7 @@ to new_row as well.
Close the currently open table.
---+++ =line($line)=
Called for any line that is not part of a table and is not caught by
an =early_line= event handler returning true.
Called for any line that is not part of a table.
---+++ =open_tr($before)=
Called on each row in an open table (including the header and footer rows)
Expand Down Expand Up @@ -81,30 +80,46 @@ An additional event is provided for those seeking to perform special
processing of certain lines, including rewriting them.
---+++ =early_line($line) -> $integer=
Mainly provided for handling lines other than TML content that may
Provided for handling lines other than TML content that may
interact with tables during a static parse e.g. special macros such
as %EDITTABLE.
The first result should be 0 to continue normal processing of the line,
1 to skip further processing of this line.
If early_line returns a positive result, then the parser will open a
table on the next line, whether or not it is a table line,
and any non-whitespace left in $line will be inserted as text.
If it returns a negative result, then any non-whitespace left in
$line will be inserted as text, but no other processing will be performed.
Otherwise the line is processed normally.
Note that =early_line= operates on the internal representation of the
line in the parser. Certain constructs, such as verbatim blocks, are
specially marked in this content. =early_line= can be used to rewrite
the line in place, but only with *great care*. Caveat emptor.
the $line in place, but only with *great care*. Caveat emptor.
The =early_line= fired for all lines that may be part of
The =early_line= handler is fired for all lines that may be part of
a table (i.e. not verbatim or literal lines).
=cut

sub parse {
my ( $text, $dispatch ) = @_;

# SMELL: Should this be a flag in the call, rathern than caller magic
my $procform = ( (caller)[0] eq 'Foswiki::Form' );
# Are we parsing a form definition? These have subtle syntactic
# differences (legacy)
# SMELL: Should this be a flag in the call, rather than caller magic
my $parsing_formdef = ( (caller)[0] eq 'Foswiki::Form' );

# Are we defining a table already?
my $in_table = 0;

# Are we to create a table even if the next line isn't a table line?
my $always_create_table = 0;

# Depth of tag scopes
my %scope = ( verbatim => 0, literal => 0, include => 0 );

my $openRow;
my @comments;

Expand All @@ -130,41 +145,65 @@ sub parse {
$openRow = undef;
}

unless ( _balanced($line) ) {
unless ( _macros_are_balanced($line) ) {

# Unclosed %MACRO{
print STDERR "unbalanced % in $line\n" if TRACE;
$openRow = defined $openRow ? "$openRow$line\n" : "$line\n";
next LINE;
}

# Call the per-line event
if ( _enabled( \%scope ) ) {
if ( !_in_blocking_scope( \%scope ) ) {
print STDERR "Processing $line\n" if TRACE;
if ( &$dispatch( 'early_line', $line, $in_table ) ) {
print STDERR "early_line returned 1\n" if TRACE;
if ($in_table) {
print STDERR "Close TABLE\n" if TRACE;

# close any open table
$in_table = 0;
&$dispatch('close_table');

# Call the per-line event. This handles macros.
my $analysis = &$dispatch( 'early_line', $line, $in_table );
if ($analysis) {

print STDERR "early_line returned $analysis\n" if TRACE;

if ( $analysis > 0 ) {

# If early_line returns 1, then a new table is to be
# created even if the next line isn't a table line
# (delimited by ||). This is so EDITTABLE and
# equivalent can either latch on to a following table
# or create a new table just by virtue of the macro
# being present.

$always_create_table = 1;

# New table is being forced. Close any open table first.
if ($in_table) {
print STDERR "Close TABLE\n" if TRACE;

# close any open table
$in_table = 0;
&$dispatch('close_table');
}

}

# an EDITTABLE macro starts a new table
# this allows us to create new tables from
# just an EDITTABLE macro
print STDERR "Open TABLE\n" if TRACE;
&$dispatch('open_table');
$in_table = 1;
# If early_line returns a negative result, then any
# non-whitespace left in the line will be inserted as
# text, but no other processing will be performed.
# This is what happens when a TABLE macro is seen.

if ( $line =~ /\S/ ) {
print STDERR "Dispatch $line\n" if TRACE;
&$dispatch( 'line', _rewrite( $line, \@comments ) );
}
next LINE;
}

if ( $line =~ m/^\s*\|.*(\|\s*|\\)$/ ) {

print STDERR "Tablerow $line\n" if TRACE;

# A table has been encountered, we don't need to
# force it.
$always_create_table = 0;

if ( $line =~ s/\\$// ) {

# terminating \
Expand Down Expand Up @@ -199,14 +238,16 @@ sub parse {

my $rowlen = scalar @cols;
for ( my $i = 0 ; $i < $rowlen ; $i++ ) {
if ( $procform
if ( $parsing_formdef
&& $i == 3
&& ( substr( $cols[$i], -1 ) eq '\\' )
&& $i < $rowlen )
{
# Form definitions allow use of \| escapes in the initial values colunn - column 4
# So this code removes the "splits" from within the initial values
# But only when processing a form. See Item13385
# Form definitions allow use of \| escapes in
# the initial values column - column 4
# So this code removes the "splits" from within
# the initial values. But only when processing
# a form definition. See Item13385
print STDERR "Merging Form values column.\n"
if TRACE;
chop $cols[$i];
Expand Down Expand Up @@ -235,8 +276,12 @@ sub parse {
&$dispatch('close_tr');

next LINE;

}
}
elsif (TRACE) {
print STDERR "blocked: $line\n";
}
if ($in_table) {

# open table has been terminated
Expand All @@ -246,6 +291,16 @@ sub parse {

# fall through to allow dispatch of line event
}
if ($always_create_table) {

# Something encountered by the early_line handler
# requires the immediate creation of a new table.
print STDERR "*Force* Open TABLE\n" if TRACE;
&$dispatch('open_table');
&$dispatch('close_table');
$always_create_table = 0;
}

print STDERR "Dispatch $line\n" if TRACE;
&$dispatch( 'line', _rewrite( $line, \@comments ) );

Expand Down Expand Up @@ -297,9 +352,9 @@ sub split_cell {
return ( $prec, $main, $postc, $ish );
}

sub _enabled {
sub _in_blocking_scope {
my $scope = shift;
return $scope->{verbatim} + $scope->{literal} == 0;
return $scope->{verbatim} != 0 || $scope->{literal} != 0;
}

sub _rewrite {
Expand All @@ -309,7 +364,7 @@ sub _rewrite {
}

# Return false unless TML macro brackets are balanced
sub _balanced {
sub _macros_are_balanced {
my $line = shift;
my $n = 0;

Expand Down

0 comments on commit 45bceb5

Please sign in to comment.