# Copyright (c) 2006 Hans Jeuken. All rights reserved.
# This program is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
package Syntax::Highlight::Engine::Kate::Template;
our $VERSION = '0.09';
use strict;
use Carp qw(cluck);
use Data::Dumper;
#my $regchars = '\\^.$|()[]*+?';
sub new {
my $proto = shift;
my $class = ref($proto) || $proto;
my %args = (@_);
my $debug = delete $args{'debug'};
unless (defined($debug)) { $debug = 0 };
my $substitutions = delete $args{'substitutions'};
unless (defined($substitutions)) { $substitutions = {} };
my $formattable = delete $args{'format_table'};
unless (defined($formattable)) { $formattable = {} };
my $engine = delete $args{'engine'};
my $self = {};
$self->{'attributes'} = {},
$self->{'captured'} = [];
$self->{'contextdata'} = {};
$self->{'basecontext'} = '';
$self->{'debug'} = $debug;
$self->{'deliminators'} = '';
$self->{'engine'} = '';
$self->{'format_table'} = $formattable;
$self->{'keywordcase'} = 1;
$self->{'lastchar'} = '';
$self->{'linesegment'} = '';
$self->{'lists'} = {};
$self->{'linestart'} = 1;
$self->{'out'} = [];
$self->{'plugins'} = {};
$self->{'snippet'} = '';
$self->{'snippetattribute'} = '';
$self->{'stack'} = [];
$self->{'substitutions'} = $substitutions;
bless ($self, $class);
unless (defined $engine) { $engine = $self };
$self->engine($engine);
$self->initialize;
return $self;
}
sub attributes {
my $self = shift;
if (@_) { $self->{'attributes'} = shift; };
return $self->{'attributes'};
}
sub basecontext {
my $self = shift;
if (@_) { $self->{'basecontext'} = shift; };
return $self->{'basecontext'};
}
sub captured {
my ($self, $c) = @_;
if (defined($c)) {
my $t = $self->engine->stackTop;
my $n = 0;
my @o = ();
while (defined($c->[$n])) {
push @o, $c->[$n];
$n ++;
}
if (@o) {
$t->[2] = \@o;
}
};
}
sub capturedGet {
my ($self, $num) = @_;
my $s = $self->engine->stack;
if (defined($s->[1])) {
my $c = $s->[1]->[2];
$num --;
if (defined($c)) {
if (defined($c->[$num])) {
my $r = $c->[$num];
return $r;
} else {
warn "capture number $num not defined";
}
} else {
warn "dynamic substitution is called for but nothing to substitute\n";
return undef;
}
} else {
warn "no parent context to take captures from";
}
}
#sub captured {
# my $self = shift;
# if (@_) {
# $self->{'captured'} = shift;
## print Dumper($self->{'captured'});
# };
# return $self->{'captured'}
## my ($self, $c) = @_;
## if (defined($c)) {
## my $t = $self->engine->stackTop;
## my $n = 0;
## my @o = ();
## while (defined($c->[$n])) {
## push @o, $c->[$n];
## $n ++;
## }
## if (@o) {
## $t->[2] = \@o;
## }
## };
#}
#
#sub capturedGet {
# my ($self, $num) = @_;
# my $s = $self->captured;
# if (defined $s) {
# $num --;
# if (defined($s->[$num])) {
# return $s->[$num];
# } else {
# $self->logwarning("capture number $num not defined");
# }
# } else {
# $self->logwarning("dynamic substitution is called for but nothing to substitute");
# return undef;
# }
#}
sub capturedParse {
my ($self, $string, $mode) = @_;
my $s = '';
if (defined($mode)) {
if ($string =~ s/^(\d)//) {
$s = $self->capturedGet($1);
if ($string ne '') {
$self->logwarning("character class is longer then 1 character, ignoring the rest");
}
}
} else {
while ($string ne '') {
if ($string =~ s/^([^\%]*)\%(\d)//) {
my $r = $self->capturedGet($2);
if ($r ne '') {
$s = $s . $1 . $r
} else {
$s = $s . $1 . '%' . $2;
$self->logwarning("target is an empty string");
}
} else {
$string =~ s/^(.)//;
$s = "$s$1";
}
}
}
return $s;
}
sub column {
my $self = shift;
return length($self->linesegment);
}
sub contextdata {
my $self = shift;
if (@_) { $self->{'contextdata'} = shift; };
return $self->{'contextdata'};
}
sub contextInfo {
my ($self, $context, $item) = @_;
if (exists $self->contextdata->{$context}) {
my $c = $self->contextdata->{$context};
if (exists $c->{$item}) {
return $c->{$item}
} else {
return undef;
}
} else {
$self->logwarning("undefined context '$context'");
return undef;
}
}
sub contextParse {
my ($self, $plug, $context) = @_;
if ($context =~ /^#pop/i) {
while ($context =~ s/#pop//i) {
$self->stackPull;
}
} elsif ($context =~ /^#stay/i) {
#don't do anything
} elsif ($context =~ /^##(.+)/) {
my $new = $self->pluginGet($1);
$self->stackPush([$new, $new->basecontext]);
} else {
$self->stackPush([$plug, $context]);
}
}
sub debug {
my $self = shift;
if (@_) { $self->{'debug'} = shift; };
return $self->{'debug'};
}
sub debugTest {
my $self = shift;
if (@_) { $self->{'debugtest'} = shift; };
return $self->{'debugtest'};
}
sub deliminators {
my $self = shift;
if (@_) { $self->{'deliminators'} = shift; };
return $self->{'deliminators'};
}
sub engine {
my $self = shift;
if (@_) { $self->{'engine'} = shift; };
return $self->{'engine'};
}
sub firstnonspace {
my ($self, $string) = @_;
my $line = $self->linesegment;
if (($line =~ /^\s*$/) and ($string =~ /^[^\s]/)) {
return 1
}
return ''
}
sub formatTable {
my $self = shift;
if (@_) { $self->{'format_table'} = shift; };
return $self->{'format_table'};
}
sub highlight {
my ($self, $text) = @_;
$self->snippet('');
my $out = $self->out;
@$out = ();
while ($text ne '') {
my $top = $self->stackTop;
if (defined($top)) {
my ($plug, $context) = @$top;
if ($text =~ s/^(\n)//) {
$self->snippetForce;
my $e = $plug->contextInfo($context, 'lineending');
if (defined($e)) {
$self->contextParse($plug, $e)
}
my $attr = $plug->attributes->{$plug->contextInfo($context, 'attribute')};
$self->snippetParse($1, $attr);
$self->snippetForce;
$self->linesegment('');
my $b = $plug->contextInfo($context, 'linebeginning');
if (defined($b)) {
$self->contextParse($plug, $b)
}
} else {
my $sub = $plug->contextInfo($context, 'callback');
my $result = &$sub($plug, \$text);
unless($result) {
my $f = $plug->contextInfo($context, 'fallthrough');
if (defined($f)) {
$self->contextParse($plug, $f);
} else {
$text =~ s/^(.)//;
my $attr = $plug->attributes->{$plug->contextInfo($context, 'attribute')};
$self->snippetParse($1, $attr);
}
}
}
} else {
push @$out, length($text), 'Normal';
$text = '';
}
}
$self->snippetForce;
return @$out;
}
sub highlightText {
my ($self, $text) = @_;
my $res = '';
my @hl = $self->highlight($text);
while (@hl) {
my $f = shift @hl;
my $t = shift @hl;
unless (defined($t)) { $t = 'Normal' }
my $s = $self->substitutions;
my $rr = '';
while ($f ne '') {
my $k = substr($f , 0, 1);
$f = substr($f, 1, length($f) -1);
if (exists $s->{$k}) {
$rr = $rr . $s->{$k}
} else {
$rr = $rr . $k;
}
}
my $rt = $self->formatTable;
if (exists $rt->{$t}) {
my $o = $rt->{$t};
$res = $res . $o->[0] . $rr . $o->[1];
} else {
$res = $res . $rr;
$self->logwarning("undefined format tag '$t'");
}
}
return $res;
}
sub includePlugin {
my ($self, $language, $text) = @_;
my $eng = $self->engine;
my $plug = $eng->pluginGet($language);
if (defined($plug)) {
my $context = $plug->basecontext;
my $call = $plug->contextInfo($context, 'callback');
if (defined($call)) {
return &$call($plug, $text);
} else {
$self->logwarning("cannot find callback for context '$context'");
}
}
return 0;
}
sub includeRules {
my ($self, $context, $text) = @_;
my $call = $self->contextInfo($context, 'callback');
if (defined($call)) {
return &$call($self, $text);
} else {
$self->logwarning("cannot find callback for context '$context'");
}
return 0;
}
sub initialize {
my $self = shift;
if ($self->engine eq $self) {
$self->stack([[$self, $self->basecontext]]);
}
}
sub keywordscase {
my $self = shift;
if (@_) { $self->{'keywordcase'} = shift; }
return $self->{'keywordscase'}
}
sub languagePlug {
my ($cw, $name) = @_;
my %numb = (
'1' => 'One',
'2' => 'Two',
'3' => 'Three',
'4' => 'Four',
'5' => 'Five',
'6' => 'Six',
'7' => 'Seven',
'8' => 'Eight',
'9' => 'Nine',
'0' => 'Zero',
);
if ($name =~ s/^(\d)//) {
$name = $numb{$1} . $name;
}
$name =~ s/\.//;
$name =~ s/\+/plus/g;
$name =~ s/\-/minus/g;
$name =~ s/#/dash/g;
$name =~ s/[^0-9a-zA-Z]/_/g;
$name =~ s/__/_/g;
$name =~ s/_$//;
$name = ucfirst($name);
return $name;
}
sub lastchar {
my $self = shift;
my $l = $self->linesegment;
if ($l eq '') { return "\n" } #last character was a newline
return substr($l, length($l) - 1, 1);
}
sub lastcharDeliminator {
my $self = shift;
my $deliminators = '\s|\~|\!|\%|\^|\&|\*|\+|\(|\)|-|=|\{|\}|\[|\]|:|;|<|>|,|\\|\||\.|\?|\/';
if ($self->linestart or ($self->lastchar =~ /$deliminators/)) {
return 1;
}
return '';
}
sub linesegment {
my $self = shift;
if (@_) { $self->{'linesegment'} = shift; };
return $self->{'linesegment'};
}
sub linestart {
my $self = shift;
if ($self->linesegment eq '') {
return 1
}
return '';
}
sub lists {
my $self = shift;
if (@_) { $self->{'lists'} = shift; }
return $self->{'lists'}
}
sub out {
my $self = shift;
if (@_) { $self->{'out'} = shift; }
return $self->{'out'};
}
sub listAdd {
my $self = shift;
my $listname = shift;
my $lst = $self->lists;
if (@_) {
my @l = reverse sort @_;
$lst->{$listname} = \@l;
} else {
$lst->{$listname} = [];
}
}
sub logwarning {
my ($self, $warning) = @_;
my $top = $self->engine->stackTop;
if (defined $top) {
my $lang = $top->[0]->language;
my $context = $top->[1];
$warning = "$warning\n Language => $lang, Context => $context\n";
} else {
$warning = "$warning\n STACK IS EMPTY: PANIC\n"
}
cluck($warning);
}
sub parseResult {
my ($self, $text, $string, $lahead, $column, $fnspace, $context, $attr) = @_;
my $eng = $self->engine;
if ($fnspace) {
unless ($eng->firstnonspace($$text)) {
return ''
}
}
if (defined($column)) {
if ($column ne $eng->column) {
return '';
}
}
unless ($lahead) {
$$text = substr($$text, length($string));
my $r;
unless (defined($attr)) {
my $t = $eng->stackTop;
my ($plug, $ctext) = @$t;
$r = $plug->attributes->{$plug->contextInfo($ctext, 'attribute')};
} else {
$r = $self->attributes->{$attr};
}
$eng->snippetParse($string, $r);
}
$eng->contextParse($self, $context);
return 1
}
sub pluginGet {
my ($self, $language) = @_;
my $plugs = $self->{'plugins'};
unless (exists($plugs->{$language})) {
my $modname = 'Syntax::Highlight::Engine::Kate::' . $self->languagePlug($language);
unless (defined($modname)) {
$self->logwarning("no valid module found for language '$language'");
return undef;
}
my $plug;
eval "use $modname; \$plug = new $modname(engine => \$self);";
if (defined($plug)) {
$plugs->{$language} = $plug;
} else {
$self->logwarning("cannot create plugin for language '$language'\n--------------\n$@");
}
}
if (exists($plugs->{$language})) {
return $plugs->{$language};
}
return undef;
}
sub reset {
my $self = shift;
$self->stack([[$self, $self->basecontext]]);
$self->out([]);
$self->snippet('');
}
sub snippet {
my $self = shift;
if (@_) { $self->{'snippet'} = shift; }
return $self->{'snippet'};
}
sub snippetAppend {
my ($self, $ch) = @_;
return if not defined $ch;
$self->{'snippet'} = $self->{'snippet'} . $ch;
if ($ch ne '') {
$self->linesegment($self->linesegment . $ch);
}
return;
}
sub snippetAttribute {
my $self = shift;
if (@_) { $self->{'snippetattribute'} = shift; }
return $self->{'snippetattribute'};
}
sub snippetForce {
my $self = shift;
my $parse = $self->snippet;
if ($parse ne '') {
my $out = $self->{'out'};
push(@$out, $parse, $self->snippetAttribute);
$self->snippet('');
}
}
sub snippetParse {
my $self = shift;
my $snip = shift;
my $attr = shift;
if ((defined $attr) and ($attr ne $self->snippetAttribute)) {
$self->snippetForce;
$self->snippetAttribute($attr);
}
$self->snippetAppend($snip);
}
sub stack {
my $self = shift;
if (@_) { $self->{'stack'} = shift; }
return $self->{'stack'};
}
sub stackPush {
my ($self, $val) = @_;
my $stack = $self->stack;
unshift(@$stack, $val);
}
sub stackPull {
my ($self, $val) = @_;
my $stack = $self->stack;
return shift(@$stack);
}
sub stackTop {
my $self = shift;
return $self->stack->[0];
}
sub stateCompare {
my ($self, $state) = @_;
my $h = [ $self->stateGet ];
my $equal = 0;
if (Dumper($h) eq Dumper($state)) { $equal = 1 };
return $equal;
}
sub stateGet {
my $self = shift;
my $s = $self->stack;
return @$s;
}
sub stateSet {
my $self = shift;
my $s = $self->stack;
@$s = (@_);
}
sub substitutions {
my $self = shift;
if (@_) { $self->{'substitutions'} = shift; }
return $self->{'substitutions'};
}
sub testAnyChar {
my $self = shift;
my $text = shift;
my $string = shift;
my $insensitive = shift;
my $test = substr($$text, 0, 1);
my $bck = $test;
if ($insensitive) {
$string = lc($string);
$test = lc($test);
}
if (index($string, $test) > -1) {
return $self->parseResult($text, $bck, @_);
}
return ''
}
sub testDetectChar {
my $self = shift;
my $text = shift;
my $char = shift;
my $insensitive = shift;
my $dyn = shift;
if ($dyn) {
$char = $self->capturedParse($char, 1);
}
my $test = substr($$text, 0, 1);
my $bck = $test;
if ($insensitive) {
$char = lc($char);
$test = lc($test);
}
if ($char eq $test) {
return $self->parseResult($text, $bck, @_);
}
return ''
}
sub testDetect2Chars {
my $self = shift;
my $text = shift;
my $char = shift;
my $char1 = shift;
my $insensitive = shift;
my $dyn = shift;
if ($dyn) {
$char = $self->capturedParse($char, 1);
$char1 = $self->capturedParse($char1, 1);
}
my $string = $char . $char1;
my $test = substr($$text, 0, 2);
my $bck = $test;
if ($insensitive) {
$string = lc($string);
$test = lc($test);
}
if ($string eq $test) {
return $self->parseResult($text, $bck, @_);
}
return ''
}
sub testDetectIdentifier {
my $self = shift;
my $text = shift;
if ($$text =~ /^([a-zA-Z_][a-zA-Z0-9_]+)/) {
return $self->parseResult($text, $1, @_);
}
return ''
}
sub testDetectSpaces {
my $self = shift;
my $text = shift;
if ($$text =~ /^([\\040|\\t]+)/) {
return $self->parseResult($text, $1, @_);
}
return ''
}
sub testFloat {
my $self = shift;
my $text = shift;
if ($self->engine->lastcharDeliminator) {
if ($$text =~ /^((?=\.?\d)\d*(?:\.\d*)?(?:[Ee][+-]?\d+)?)/) {
return $self->parseResult($text, $1, @_);
}
}
return ''
}
sub testHlCChar {
my $self = shift;
my $text = shift;
if ($$text =~ /^('.')/) {
return $self->parseResult($text, $1, @_);
}
return ''
}
sub testHlCHex {
my $self = shift;
my $text = shift;
if ($self->engine->lastcharDeliminator) {
if ($$text =~ /^(0x[0-9a-fA-F]+)/) {
return $self->parseResult($text, $1, @_);
}
}
return ''
}
sub testHlCOct {
my $self = shift;
my $text = shift;
if ($self->engine->lastcharDeliminator) {
if ($$text =~ /^(0[0-7]+)/) {
return $self->parseResult($text, $1, @_);
}
}
return ''
}
sub testHlCStringChar {
my $self = shift;
my $text = shift;
if ($$text =~ /^(\\[a|b|e|f|n|r|t|v|'|"|\?])/) {
return $self->parseResult($text, $1, @_);
}
if ($$text =~ /^(\\x[0-9a-fA-F][0-9a-fA-F]?)/) {
return $self->parseResult($text, $1, @_);
}
if ($$text =~ /^(\\[0-7][0-7]?[0-7]?)/) {
return $self->parseResult($text, $1, @_);
}
return ''
}
sub testInt {
my $self = shift;
my $text = shift;
if ($self->engine->lastcharDeliminator) {
if ($$text =~ /^([+-]?\d+)/) {
return $self->parseResult($text, $1, @_);
}
}
return ''
}
sub testKeyword {
my $self = shift;
my $text = shift;
my $list = shift;
my $eng = $self->engine;
my $deliminators = $self->deliminators;
if (($eng->lastcharDeliminator) and ($$text =~ /^([^$deliminators]+)/)) {
my $match = $1;
my $l = $self->lists->{$list};
if (defined($l)) {
my @list = @$l;
my @rl = ();
unless ($self->keywordscase) {
@rl = grep { (lc($match) eq lc($_)) } @list;
} else {
@rl = grep { ($match eq $_) } @list;
}
if (@rl) {
return $self->parseResult($text, $match, @_);
}
} else {
$self->logwarning("list '$list' is not defined, failing test");
}
}
return ''
}
sub testLineContinue {
my $self = shift;
my $text = shift;
my $lahead = shift;
if ($lahead) {
if ($$text =~ /^\\\n/) {
$self->parseResult($text, "\\", $lahead, @_);
return 1;
}
} else {
if ($$text =~ s/^(\\)(\n)/$2/) {
return $self->parseResult($text, "\\", $lahead, @_);
}
}
return ''
}
sub testRangeDetect {
my $self = shift;
my $text = shift;
my $char = shift;
my $char1 = shift;
my $insensitive = shift;
my $string = "$char\[^$char1\]+$char1";
return $self->testRegExpr($text, $string, $insensitive, 0, @_);
}
sub testRegExpr {
my $self = shift;
my $text = shift;
my $reg = shift;
my $insensitive = shift;
my $dynamic = shift;
if ($dynamic) {
$reg = $self->capturedParse($reg);
}
my $eng = $self->engine;
if ($reg =~ s/^\^//) {
unless ($eng->linestart) {
return '';
}
} elsif ($reg =~ s/^\\(b)//i) {
my $lastchar = $self->engine->lastchar;
if ($1 eq 'b') {
if ($lastchar =~ /\w/) { return '' }
} else {
if ($lastchar =~ /\W/) { return '' }
}
}
# $reg = "^($reg)";
$reg = "^$reg";
my $pos;
# my @cap = ();
my $sample = $$text;
# emergency measurements to avoid exception (szabgab)
$reg = eval { qr/$reg/ };
if ($@) {
warn $@;
return '';
}
if ($insensitive) {
if ($sample =~ /$reg/ig) {
$pos = pos($sample);
# @cap = ($1, $2, $3, $4, $5, $6, $7, $8, $9);
# my @cap = ();
if ($#-) {
no strict 'refs';
my @cap = map {$$_} 1 .. $#-;
$self->captured(\@cap)
}
# my $r = 1;
# my $c = 1;
# my @cap = ();
# while ($r) {
# eval "if (defined\$$c) { push \@cap, \$$c } else { \$r = 0 }";
# $c ++;
# }
# if (@cap) { $self->captured(\@cap) };
}
} else {
if ($sample =~ /$reg/g) {
$pos = pos($sample);
# @cap = ($1, $2, $3, $4, $5, $6, $7, $8, $9);
# my @cap = ();
if ($#-) {
no strict 'refs';
my @cap = map {$$_} 1 .. $#-;
$self->captured(\@cap);
}
# my $r = 1;
# my $c = 1;
# my @cap = ();
# while ($r) {
# eval "if (defined\$$c) { push \@cap, \$$c } else { \$r = 0 }";
# $c ++;
# }
# if (@cap) { $self->captured(\@cap) };
}
}
if (defined($pos) and ($pos > 0)) {
my $string = substr($$text, 0, $pos);
return $self->parseResult($text, $string, @_);
}
return ''
}
sub testStringDetect {
my $self = shift;
my $text = shift;
my $string = shift;
my $insensitive = shift;
my $dynamic = shift;
if ($dynamic) {
$string = $self->capturedParse($string);
}
my $test = substr($$text, 0, length($string));
my $bck = $test;
if ($insensitive) {
$string = lc($string);
$test = lc($test);
}
if ($string eq $test) {
return $self->parseResult($text, $bck, @_);
}
return ''
}
1;
__END__
=head1 NAME
Syntax::Highlight::Engine::Kate::Template - a template for syntax highlighting plugins
=head1 DESCRIPTION
Syntax::Highlight::Engine::Kate::Template is a framework to assist authors of plugin modules.
All methods to provide highlighting to the Syntax::Highlight::Engine::Kate module are there, Just
no syntax definitions and callbacks. An instance of Syntax::Highlight::Engine::Kate::Template
should never be created, it's meant to be sub classed only.
=head1 METHODS
=over 4
=item B<attributes>(I<?$attributesref?>);
Sets and returns a reference to the attributes hash.
=item B<basecontext>(I<?$context?>);
Sets and returns the basecontext instance variable. This is the context that is used when highlighting starts.
=item B<captured>(I<$cap>);
Puts $cap in the first element of the stack, the current context. Used when the context is dynamic.
=item B<capturedGet>(I<$num>);
Returns the $num'th element that was captured in the current context.
=item B<capturedParse>(I<$string>, I<$mode>);
If B<$mode> is specified, B<$string> should only be one character long and numeric.
B<capturedParse> will return the Nth captured element of the current context.
If B<$mode> is not specified, all occurences of %[1-9] will be replaced by the captured
element of the current context.
=item B<column>
returns the column position in the line that is currently highlighted.
=item B<contextdata>(I<\%data>);
Sets and returns a reference to the contextdata hash.
=item B<contextInfo>(I<$context>, I<$item>);
returns the value of several context options. B<$item> can be B<callback>, B<attribute>, B<lineending>,
B<linebeginning>, B<fallthrough>.
=item B<contextParse>(I<$plugin>, I<$context>);
Called by the plugins after a test succeeds. if B<$context> has following values:
#pop returns to the previous context, removes to top item in the stack. Can
also be specified as #pop#pop etc.
#stay does nothing.
##.... Switches to the plugin specified in .... and assumes it's basecontext.
.... Swtiches to the context specified in ....
=item B<deliminators>(I<?$delim?>);
Sets and returns a string that is a regular expression for detecting deliminators.
=item B<engine>
Returns a reference to the Syntax::Highlight::Engine::Kate module that created this plugin.
=item B<firstnonspace>(I<$string>);
returns true if the current line did not contain a non-spatial character so far and the first
character in B<$string> is also a spatial character.
=item B<formatTable>
sets and returns the instance variable B<format_table>. See also the option B<format_table>
=item B<highlight>(I<$text>);
highlights I<$text>. It does so by selecting the proper callback
from the B<commands> hash and invoke it. It will do so untill
$text has been reduced to an empty string. returns a paired list
of snippets of text and the attribute with which they should be
highlighted.
=item B<highlightText>(I<$text>);
highlights I<$text> and reformats it using the B<format_table> and B<substitutions>
=item B<includePlugin>(I<$language>, I<\$text>);
Includes the plugin for B<$language> in the highlighting.
=item B<includeRules>(I<$language>, I<\$text>);
Includes the plugin for B<$language> in the highlighting.
=item B<keywordscase>
Sets and returns the keywordscase instance variable.
=item B<lastchar>
return the last character that was processed.
=item B<lastcharDeliminator>
returns true if the last character processed was a deliminator.
=item B<linesegment>
returns the string of text in the current line that has been processed so far,
=item B<linestart>
returns true if processing is currently at the beginning of a line.
=item B<listAdd>(I<'listname'>, I<$item1>, I<$item2> ...);
Adds a list to the 'lists' hash.
=item B<lists>(I<?\%lists?>);
sets and returns the instance variable 'lists'.
=item B<out>(I<?\@highlightedlist?>);
sets and returns the instance variable 'out'.
=item B<parseResult>(I<\$text>, I<$match>, I<$lookahaed>, I<$column>, I<$firstnonspace>, I<$context>, I<$attribute>);
Called by every one of the test methods below. If the test matches, it will do a couple of subtests.
If B<$column> is a defined numerical value it will test if the process is at the requested column.
If B<$firnonspace> is true, it will test this also.
Ig it is not a look ahead and all tests are passed, B<$match> is then parsed and removed from B<$$text>.
=item B<pluginGet>(I<$language>);
Returns a reference to a plugin object for the specified language. Creating an
instance if needed.
=item B<reset>
Resets the highlight engine to a fresh state, does not change the syntx.
=item B<snippet>
Contains the current snippet of text that will have one attribute. The moment the attribute
changes it will be parsed.
=item B<snippetAppend>(I<$string>)
appends I<$string> to the current snippet.
=item B<snippetAttribute>(I<$attribute>)
Sets and returns the used attribute.
=item B<snippetForce>
Forces the current snippet to be parsed.
=item B<snippetParse>(I<$text>, I<?$attribute?>)
If attribute is defined and differs from the current attribute it does a snippetForce and
sets the current attribute to B<$attribute>. Then it does a snippetAppend of B<$text>
=item B<stack>
sets and returns the instance variable 'stack', a reference to an array
=item B<stackPull>
retrieves the element that is on top of the stack, decrements stacksize by 1.
=item B<stackPush>(I<$tagname>);
puts I<$tagname> on top of the stack, increments stacksize by 1
=item B<stackTop>
Retrieves the element that is on top of the stack.
=item B<stateCompare>(I<\@state>)
Compares two lists, \@state and the stack. returns true if they
match.
=item B<stateGet>
Returns a list containing the entire stack.
=item B<stateSet>(I<@list>)
Accepts I<@list> as the current stack.
=item B<substitutions>
sets and returns a reference to the substitutions hash.
=back
The methods below all return a boolean value.
=over 4
=item B<testAnyChar>(I<\$text>, I<$string>, I<$insensitive>, I<$lookahaed>, I<$column>, I<$firstnonspace>, I<$context>, I<$attribute>);
=item B<testDetectChar>(I<\$text>, I<$char>, I<$insensitive>, I<$dynamic>, I<$lookahaed>, I<$column>, I<$firstnonspace>, I<$context>, I<$attribute>);
=item B<testDetect2Chars>(I<\$text>, I<$char1>, I<$char2>, I<$insensitive>, I<$dynamic>, I<$lookahaed>, I<$column>, I<$firstnonspace>, I<$context>, I<$attribute>);
=item B<testDetectIdentifier>(I<\$text>, I<$lookahaed>, I<$column>, I<$firstnonspace>, I<$context>, I<$attribute>);
=item B<testDetectSpaces>(I<\$text>, I<$lookahaed>, I<$column>, I<$firstnonspace>, I<$context>, I<$attribute>);
=item B<testFloat>(I<\$text>, I<$lookahaed>, I<$column>, I<$firstnonspace>, I<$context>, I<$attribute>);
=item B<testHlCChar>(I<\$text>, I<$lookahaed>, I<$column>, I<$firstnonspace>, I<$context>, I<$attribute>);
=item B<testHlCHex>(I<\$text>, I<$lookahaed>, I<$column>, I<$firstnonspace>, I<$context>, I<$attribute>);
=item B<testHlCOct>(I<\$text>, I<$lookahaed>, I<$column>, I<$firstnonspace>, I<$context>, I<$attribute>);
=item B<testHlCStringChar>(I<\$text>, I<$lookahaed>, I<$column>, I<$firstnonspace>, I<$context>, I<$attribute>);
=item B<testInt>(I<\$text>, I<$lookahaed>, I<$column>, I<$firstnonspace>, I<$context>, I<$attribute>);
=item B<testKeyword>(I<\$text>, I<$list>, I<$insensitive>, I<$lookahaed>, I<$column>, I<$firstnonspace>, I<$context>, I<$attribute>);
=item B<testLineContinue>(I<\$text>, I<$lookahaed>, I<$column>, I<$firstnonspace>, I<$context>, I<$attribute>);
=item B<testRangeDetect>(I<\$text>, I<$char1>, I<$char2>, I<$insensitive>, I<$lookahaed>, I<$column>, I<$firstnonspace>, I<$context>, I<$attribute>);
=item B<testRegExpr>(I<\$text>, I<$reg>, I<$insensitive>, I<$dynamic>, I<$lookahaed>, I<$column>, I<$firstnonspace>, I<$context>, I<$attribute>);
=item B<testStringDetect>(I<\$text>, I<$string>, I<$insensitive>, I<$dynamic>, II<$lookahaed>, I<$column>, I<$firstnonspace>, I<$context>, I<$attribute>);
=back
=head1 ACKNOWLEDGEMENTS
All the people who wrote Kate and the syntax highlight xml files.
=head1 AUTHOR AND COPYRIGHT
This module is written and maintained by:
Hans Jeuken < haje at toneel dot demon dot nl >
Copyright (c) 2006 by Hans Jeuken, all rights reserved.
You may freely distribute and/or modify this module under same terms as
Perl itself
=head1 SEE ALSO
Synax::Highlight::Engine::Kate http:://www.kate-editor.org