% A reasonable XML mode. Might be usable for SGML. Is certainly not
% usable for ordinary HTML, but XHTML should be fine.
%
%
% What it does:
% * Robust indenting. None of the XML / XSL files i've worked with
% have suffered from bad indentation (not only simple stuff).
% The result is approximately:
%
%
%
% text
%
%
% * IMHO more usable syntax highlighting than the DocBook/HTML modes
% have. I'm not totally happy with the result, but I suspect it's
% the best that can be done until the DFA engine supports zero-
% width assertions. :-)
%
% Problems:
% * Finding the indent level is O(n) in the number of lines in the
% document. That's pretty much the price that has to be paid for
% reliability :-/
% * Lack of multi-line DFA really kills highlighting of PI's, CDATA, and
% comments.
% * " starts a string (i.e attribute value) also when not inside a tag.
% This can lead to funny indentation effects in cases like below:
% blah"
% The solution is to split the ending tag to another line:
% blah"
%
% * The automatic tag closing has problems, when there are multiple
% closing tags on the same line. I.e:
%
%
% X
% Closing the tag at 'X' will produce another '' instead of
% ''. The workaround is obvious ;-)
%
% 2001-10-18 / Juho Snellman
% * First public release
%
% 2001-10-22 / Juho Snellman
% * Fixed the following problem:
%
% ""
% blah
% * Added a make_keymap(). (Why did it work without one?)
%
% 2001-10-31 / Juho Snellman
% * Removed the requirement that the file should start with the
% pseudo-PI.
%
% 2001-12-05 / Juho Snellman
% * Better regexp for highlighting element names. Gets numbers and
% capital letters too.
%
% 2001-12-07 / Juho Snellman
% * Fixed problem with '<>'.
% * Added automatic closing of tags.
% * Added comment strings for comments.sl
%
% 2002-04-17 / Juho Snellman
% * '< foo' has no effect on the indent, while '
% * Made most functions/variables static.
create_syntax_table ("xml");
define_syntax ("<", ">", '(', "xml");
define_syntax ('"', '"', "xml");
% define_syntax ('\'', '\'', "xml");
define_syntax ('\\', '\\', "xml");
define_syntax ("=/", '+', "xml");
define_syntax ("", '%', "xml");
% define_syntax ("", '%', "xml");
set_syntax_flags ("xml", 0x04|0x80);
#ifdef HAS_DFA_SYNTAX
%%% DFA_CACHE_BEGIN %%%
static define setup_dfa_callback (name)
{
% dfa_enable_highlight_cache ("xml.dfa", name);
dfa_define_highlight_rule ("", "comment", name);
dfa_define_highlight_rule ("=", "operator", name);
dfa_define_highlight_rule ("<|>||/>", "keyword", name);
dfa_define_highlight_rule ("?[A-Za-z0-9_\\-]+:?[A-Za-z0-9_\\-]*", "keyword", name);
dfa_define_highlight_rule ("", "preprocess", name);
% Slightly flawed, the '>' could be inside an attribute
dfa_define_highlight_rule ("", "preprocess", name);
dfa_define_highlight_rule ("<\\?.*\\?>", "preprocess", name);
dfa_define_highlight_rule ("\&.*;", "preprocess", name);
dfa_define_highlight_rule ("\"([^\\\\\"]|\\\\.)*\"", "string", name);
% dfa_define_highlight_rule ("'([^\\\\\']|\\\\.)*'", "string", name);
dfa_build_highlight_table (name);
}
dfa_set_init_callback (&setup_dfa_callback, "xml");
%%% DFA_CACHE_END %%%
#endif
variable XML_INDENT = 2;
variable XML_TAG_CONTENT_INDENT = 4;
static variable names = Assoc_Type [ String_Type ];
% Sets the name of the last tag on an indent level.
static define xml_set_name(ind)
{
push_spot();
push_mark_eol();
variable name = bufsubstr();
if (string_match(name, "\\([A-Za-z0-9_\\-]+:?[A-Za-z0-9_\\-]*\\)", 1))
{
variable pos;
variable len;
(pos, len) = string_match_nth(1);
name = substr(name, pos+1, len);
names[sprintf("%d", ind)] = name;
}
pop_spot();
}
static define xml_get_name(ind)
{
variable sind = sprintf("%d", ind);
names[sind];
}
static define xml_looking_at(s)
{
return looking_at(s) and not looking_at(strcat(s," "));
}
% Returns the level of nested elements that the start of this line is
% inside of. (A negative number is returned if the start of the line
% is inside a tag.)
static define xml_calculate_indent ()
{
push_spot();
variable row = what_line();
variable ind = 0;
EXIT_BLOCK {
pop_spot();
}
bob();
% 0 : Inside a
% 1 : Inside a .. ?>
% 2 : Inside a < ... >, ... >, or < ... />
% 3 : Other
variable state = 3;
variable closing_tag = 0;
% The line on which the current tag started on.
variable start_row = 0;
variable i = 0;
while (1) {
% Just in case of bugs involving infinite loops.
i++;
if (i > 100000) {
error(sprintf("Gave up after 100000 tries. Document too large? State: %d Ind: %d Pos: %d/%d", state, ind,
what_column(), what_line() ));
}
switch (0)
{ state == 0:
switch (0)
{ what_line() >= row : return 0; }
{ not fsearch(">") : error("Unterminated processing instruction"); }
{ if (parse_to_point() == 0)
state = 3;
() = right(2);
}
}
{ state == 1:
switch (0)
{ not fsearch("?>") : error("Unterminated processing instruction"); }
{ what_line() >= row : return 0; }
{ % Oops, make sure that the "?>" wasn't just an attribute
% value.
if (parse_to_point() == 0)
state = 3;
() = right(2);
}
}
{ state == 2:
switch (0)
{ looking_at("<") and what_line() == row :
% error(sprintf("%d", ind-(1-closing_tag)));
return ind-(1-closing_tag);;
}
{ not fsearch(">") : error("Unterminated element"); }
{ () = right(1);
% If the '>' wasn't inside a string, we end the
% element
if (parse_to_point() == 0) {
state = 3;
if (what_line() == row) {
ind = ind-(1-closing_tag);
% Multi-line tag
if (what_line() != start_row) {
return -1;
}
return ind;
}
if (what_line() > row) {
return -1;
}
% If the element was immediately ended, decrement
% the indent level
() = left(2);
if (looking_at("/>")) {
ind--;
}
}
}
}
{ state == 3:
switch (0)
% We passed the point we were supposed to investigate.
% Just return what we have now.
{ what_line() > row : return ind; }
% Inside a comment (probably at the start). Move a little
% bit forward, and search for a tag start.
{ parse_to_point() == -2 : () = right(1); () = fsearch("<"); }
% Oops, do the same thing for strings too.
{ parse_to_point() == -1 : () = right(1); () = fsearch("<"); }
% Comments are handled magically by Jed, so we don't need to
% search for the end manually. Move the point to the right of
% the ", 0);
run_mode_hooks ("xml_mode_hook");
}