1082 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
		
		
			
		
	
	
			1082 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
| 
								 | 
							
								/*  $Id$
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Part of SWI-Prolog
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Author:        Jan Wielemaker
							 | 
						||
| 
								 | 
							
								    E-mail:        jan@swi.psy.uva.nl
							 | 
						||
| 
								 | 
							
								    WWW:           http://www.swi-prolog.org
							 | 
						||
| 
								 | 
							
								    Copyright (C): 1985-2002, University of Amsterdam
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This program is free software; you can redistribute it and/or
							 | 
						||
| 
								 | 
							
								    modify it under the terms of the GNU General Public License
							 | 
						||
| 
								 | 
							
								    as published by the Free Software Foundation; either version 2
							 | 
						||
| 
								 | 
							
								    of the License, or (at your option) any later version.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    This program is distributed in the hope that it will be useful,
							 | 
						||
| 
								 | 
							
								    but WITHOUT ANY WARRANTY; without even the implied warranty of
							 | 
						||
| 
								 | 
							
								    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
							 | 
						||
| 
								 | 
							
								    GNU General Public License for more details.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    You should have received a copy of the GNU Lesser General Public
							 | 
						||
| 
								 | 
							
								    License along with this library; if not, write to the Free Software
							 | 
						||
| 
								 | 
							
								    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    As a special exception, if you link this library with other files,
							 | 
						||
| 
								 | 
							
								    compiled with a Free Software compiler, to produce an executable, this
							 | 
						||
| 
								 | 
							
								    library does not by itself cause the resulting executable to be covered
							 | 
						||
| 
								 | 
							
								    by the GNU General Public License. This exception does not however
							 | 
						||
| 
								 | 
							
								    invalidate any other reasons why the executable file might be covered by
							 | 
						||
| 
								 | 
							
								    the GNU General Public License.
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								:- module(emacs_sgml_mode, []).
							 | 
						||
| 
								 | 
							
								:- use_module(library(pce)).
							 | 
						||
| 
								 | 
							
								:- use_module(library(emacs_extend)).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
							 | 
						||
| 
								 | 
							
								This module exploits the SGML/XML  parser   from  the SWI-Prolog package
							 | 
						||
| 
								 | 
							
								sgml2pl to provide syntax colouring for SGML,  XML and HTML modes. Based
							 | 
						||
| 
								 | 
							
								on a true parser, we  can  provide   much  better  feedback as heuristic
							 | 
						||
| 
								 | 
							
								parsers used in most syntax-driven editors.  For example, we can provide
							 | 
						||
| 
								 | 
							
								feedback on SHORTREF matches in  SGML   mode  by highlighting the tokens
							 | 
						||
| 
								 | 
							
								acting as a short reference.  We  can   also  easily  give  the scope of
							 | 
						||
| 
								 | 
							
								elements that are closed by omited elements.
							 | 
						||
| 
								 | 
							
								- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								:- emacs_begin_mode(sgml, language,
							 | 
						||
| 
								 | 
							
										    "Mode for editing SGML documents",
							 | 
						||
| 
								 | 
							
										    % BINDINGS
							 | 
						||
| 
								 | 
							
										    [ open_document	     = button(sgml),
							 | 
						||
| 
								 | 
							
										      reload_dtd	     = button(sgml),
							 | 
						||
| 
								 | 
							
										      colourise_buffer       = button(sgml),
							 | 
						||
| 
								 | 
							
										      colourise_and_recenter = key('\\C-l'),
							 | 
						||
| 
								 | 
							
										      tag_selection          = key('\\e,'),
							 | 
						||
| 
								 | 
							
										      colourise_element      = key('\\C-c\\C-s'),
							 | 
						||
| 
								 | 
							
										      forward_move_out       = key('\\ee')
							 | 
						||
| 
								 | 
							
										    ],
							 | 
						||
| 
								 | 
							
										    % SYNTAX TABLE
							 | 
						||
| 
								 | 
							
										    [ '"'  = string_quote('"'),
							 | 
						||
| 
								 | 
							
										      '\'' = string_quote('\''),
							 | 
						||
| 
								 | 
							
										      paragraph_end('\\s *$\\|^<p>\\|\\s +<')
							 | 
						||
| 
								 | 
							
										    ]).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class_variable(auto_colourise_size_limit, int, 100000,
							 | 
						||
| 
								 | 
							
									       "Auto-colourise if buffer is smaller then this").
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								variable(dialect,
							 | 
						||
| 
								 | 
							
									 {sgml,xml,html}:=sgml,
							 | 
						||
| 
								 | 
							
									 both,
							 | 
						||
| 
								 | 
							
									 "?ML Dialect used to parse").
							 | 
						||
| 
								 | 
							
								variable(upcase_elements,
							 | 
						||
| 
								 | 
							
									 bool := @off,
							 | 
						||
| 
								 | 
							
									 both,
							 | 
						||
| 
								 | 
							
									 "Upcase inserted elements?").
							 | 
						||
| 
								 | 
							
								variable(parser,
							 | 
						||
| 
								 | 
							
									 prolog,
							 | 
						||
| 
								 | 
							
									 none,
							 | 
						||
| 
								 | 
							
									 "Associated (DTD) parser").
							 | 
						||
| 
								 | 
							
								variable(auto_colourise_size_limit,
							 | 
						||
| 
								 | 
							
									 int,
							 | 
						||
| 
								 | 
							
									 both,
							 | 
						||
| 
								 | 
							
									 "Auto-colourise if buffer is smaller then this").
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								%	make_parser(M, Parser)
							 | 
						||
| 
								 | 
							
								%
							 | 
						||
| 
								 | 
							
								%	Create a parser suitable for the current mode and load the DTD
							 | 
						||
| 
								 | 
							
								%	into it.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								make_parser(M, Parser) :-
							 | 
						||
| 
								 | 
							
									get(M, dialect, Dialect),
							 | 
						||
| 
								 | 
							
									get(M, text_buffer, TB),
							 | 
						||
| 
								 | 
							
									get(TB, file, File),
							 | 
						||
| 
								 | 
							
									get(File, name, FileName),
							 | 
						||
| 
								 | 
							
									get(M, dialect, Dialect),
							 | 
						||
| 
								 | 
							
									(   Dialect == html
							 | 
						||
| 
								 | 
							
									->  TheDialect = sgml,
							 | 
						||
| 
								 | 
							
									    dtd(html, DTD),
							 | 
						||
| 
								 | 
							
									    Options = [dtd(DTD)]
							 | 
						||
| 
								 | 
							
									;   TheDialect = Dialect,
							 | 
						||
| 
								 | 
							
									    Options = []
							 | 
						||
| 
								 | 
							
									),
							 | 
						||
| 
								 | 
							
									new_sgml_parser(Parser, Options),
							 | 
						||
| 
								 | 
							
									set_sgml_parser(Parser, file(FileName)),
							 | 
						||
| 
								 | 
							
									set_sgml_parser(Parser, dialect(TheDialect)).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								%	load_dtd(+Mode, +Parser)
							 | 
						||
| 
								 | 
							
								%
							 | 
						||
| 
								 | 
							
								%	Load the document DTD into the given parser.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								load_dtd(M, _) :-
							 | 
						||
| 
								 | 
							
									get(M, dialect, html), !.
							 | 
						||
| 
								 | 
							
								load_dtd(M, Parser) :-
							 | 
						||
| 
								 | 
							
									get(M, text_buffer, TB),
							 | 
						||
| 
								 | 
							
									new(Re, regex('<!DOCTYPE', @off)),
							 | 
						||
| 
								 | 
							
									(   send(Re, search, TB)
							 | 
						||
| 
								 | 
							
									->  get(Re, register_start, Start),
							 | 
						||
| 
								 | 
							
									    pce_open(TB, read, In),
							 | 
						||
| 
								 | 
							
									    seek(In, Start, bof, _),
							 | 
						||
| 
								 | 
							
									    catch(sgml_parse(Parser,
							 | 
						||
| 
								 | 
							
											     [ source(In),
							 | 
						||
| 
								 | 
							
											       parse(declaration)
							 | 
						||
| 
								 | 
							
											     ]),
							 | 
						||
| 
								 | 
							
										  E,
							 | 
						||
| 
								 | 
							
										  show_message(M, E)),
							 | 
						||
| 
								 | 
							
									    close(In)
							 | 
						||
| 
								 | 
							
									;   send(M, report, warning, 'No <!DOCTYPE declaration')
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								parser(M, Reload:[bool], Parser:prolog) :<-
							 | 
						||
| 
								 | 
							
									"Fetch the default parser"::
							 | 
						||
| 
								 | 
							
									(   Reload \== @on,
							 | 
						||
| 
								 | 
							
									    get(M, slot, parser, Parser),
							 | 
						||
| 
								 | 
							
									    Parser = sgml_parser(_)
							 | 
						||
| 
								 | 
							
									->  true
							 | 
						||
| 
								 | 
							
									;   send(M, destroy_dtd),
							 | 
						||
| 
								 | 
							
									    make_parser(M, Parser),
							 | 
						||
| 
								 | 
							
									    load_dtd(M, Parser),
							 | 
						||
| 
								 | 
							
									    send(M, slot, parser, Parser),
							 | 
						||
| 
								 | 
							
									    set_sgml_parser(Parser, doctype(_)) % use for partial parsing
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								dtd(M, Reload:[bool], DTD:prolog) :<-
							 | 
						||
| 
								 | 
							
									"Fetch the current DTD"::
							 | 
						||
| 
								 | 
							
									get(M, parser, Reload, Parser),
							 | 
						||
| 
								 | 
							
									get_sgml_parser(Parser, dtd(DTD)).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								reload_dtd(M) :->
							 | 
						||
| 
								 | 
							
									"Reload the DTD"::
							 | 
						||
| 
								 | 
							
									get(M, dtd, _).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								destroy_dtd(M) :->
							 | 
						||
| 
								 | 
							
									"Destroy the associated DTD object"::
							 | 
						||
| 
								 | 
							
									(   get(M, slot, parser, Parser),
							 | 
						||
| 
								 | 
							
									    Parser = sgml_parser(_)
							 | 
						||
| 
								 | 
							
									->  free_sgml_parser(Parser),
							 | 
						||
| 
								 | 
							
									    send(M, slot, parser, [])
							 | 
						||
| 
								 | 
							
									;   true
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										 /*******************************
							 | 
						||
| 
								 | 
							
										 *	     INITIALISE		*
							 | 
						||
| 
								 | 
							
										 *******************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								setup_mode(E) :->
							 | 
						||
| 
								 | 
							
									"Switch editor into fill-mode"::
							 | 
						||
| 
								 | 
							
									send(E, fill_mode, @on).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								unlink(M) :->
							 | 
						||
| 
								 | 
							
									send(M, destroy_dtd),
							 | 
						||
| 
								 | 
							
									send_super(M, unlink).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								open_document(M, DT:doctype=name) :->
							 | 
						||
| 
								 | 
							
									"Insert <!DOCTYPE line"::
							 | 
						||
| 
								 | 
							
									send(M, format, '<!DOCTYPE %s SYSTEM "">\n\n', DT),
							 | 
						||
| 
								 | 
							
									send(M, backward_char, 4).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										 /*******************************
							 | 
						||
| 
								 | 
							
										 *	       HELP		*
							 | 
						||
| 
								 | 
							
										 *******************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								help_on_mode(M) :->
							 | 
						||
| 
								 | 
							
									(   absolute_file_name(sgml_mode,
							 | 
						||
| 
								 | 
							
											       [ extensions([html]),
							 | 
						||
| 
								 | 
							
												 access(read),
							 | 
						||
| 
								 | 
							
												 file_errors(fail)
							 | 
						||
| 
								 | 
							
											       ],
							 | 
						||
| 
								 | 
							
											       HTML)
							 | 
						||
| 
								 | 
							
									->  atom_concat('file:', HTML, URI),
							 | 
						||
| 
								 | 
							
									    www_open_url(URI)
							 | 
						||
| 
								 | 
							
									;   send(M, report, warning, 'Can''t find help file')
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										 /*******************************
							 | 
						||
| 
								 | 
							
										 *	 RECOULOR POLICIES	*
							 | 
						||
| 
								 | 
							
										 *******************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								idle(M) :->
							 | 
						||
| 
								 | 
							
									"Idle event was received, colour the current element"::
							 | 
						||
| 
								 | 
							
									send(M, colourise_element, @off).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								setup_styles(M) :->
							 | 
						||
| 
								 | 
							
									"Associate defined syntax-styles"::
							 | 
						||
| 
								 | 
							
									(   get(M, attribute, styles_initialised, @on)
							 | 
						||
| 
								 | 
							
									->  true
							 | 
						||
| 
								 | 
							
									;   send(M, reload_styles),
							 | 
						||
| 
								 | 
							
									    send(M, attribute, styles_initialised, @on)
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								set_caret_and_inform(M) :->
							 | 
						||
| 
								 | 
							
									get(M, editor, Editor),
							 | 
						||
| 
								 | 
							
									get(Editor?image, index, @event, Caret),
							 | 
						||
| 
								 | 
							
									send(M, caret, Caret),
							 | 
						||
| 
								 | 
							
									get(M?text_buffer, find_all_fragments,
							 | 
						||
| 
								 | 
							
									    message(@arg1, overlap, Caret),
							 | 
						||
| 
								 | 
							
									    Fragments),
							 | 
						||
| 
								 | 
							
									send(Fragments, sort, ?(@arg1?length, compare, @arg2?length)),
							 | 
						||
| 
								 | 
							
									get(Fragments, find, ?(@arg1, attribute, balloon), Frag),
							 | 
						||
| 
								 | 
							
									get(Frag, balloon, Balloon),
							 | 
						||
| 
								 | 
							
									send(M, report, warning, 'SGML warning: %s', Balloon).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								event(M, Ev:event) :->
							 | 
						||
| 
								 | 
							
									"Show insert-menu on right-down"::
							 | 
						||
| 
								 | 
							
									send(Ev, is_a, ms_right_down),
							 | 
						||
| 
								 | 
							
									(   get(M?image, index, Ev, I)
							 | 
						||
| 
								 | 
							
									->  send(M, caret, I)
							 | 
						||
| 
								 | 
							
									;   true
							 | 
						||
| 
								 | 
							
									),
							 | 
						||
| 
								 | 
							
									send(M, show_menu, Ev).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								reload_styles(M) :->
							 | 
						||
| 
								 | 
							
									"Force reloading the styles"::
							 | 
						||
| 
								 | 
							
									(   style_object(Name, Style),
							 | 
						||
| 
								 | 
							
									    send(M, style, Name, Style),
							 | 
						||
| 
								 | 
							
									    fail
							 | 
						||
| 
								 | 
							
									;   true
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								colourise_element(M, Warn:[bool]) :->
							 | 
						||
| 
								 | 
							
									"Colour element at location"::
							 | 
						||
| 
								 | 
							
									send(M, setup_styles),
							 | 
						||
| 
								 | 
							
									get(M, caret, Caret),
							 | 
						||
| 
								 | 
							
									get(M, text_buffer, TB),
							 | 
						||
| 
								 | 
							
									new(Re, regex('<\\w+')),
							 | 
						||
| 
								 | 
							
									make_parser(M, Parser),
							 | 
						||
| 
								 | 
							
									load_dtd(M, Parser),
							 | 
						||
| 
								 | 
							
									set_sgml_parser(Parser, doctype(_)),
							 | 
						||
| 
								 | 
							
									pce_open(TB, read, In),
							 | 
						||
| 
								 | 
							
									(   get(TB, scan, Caret, line, -2, start, Start),
							 | 
						||
| 
								 | 
							
								%	    format('Starting from ~w~n', [Start]),
							 | 
						||
| 
								 | 
							
									    find_element(M, Parser, Re, In, Start, From-To),
							 | 
						||
| 
								 | 
							
									    Caret < To
							 | 
						||
| 
								 | 
							
									->  send(M, remove_syntax_fragments, From, To),
							 | 
						||
| 
								 | 
							
								%	    colour_item(element, TB, From, To),
							 | 
						||
| 
								 | 
							
									    seek(In, From, bof, _),
							 | 
						||
| 
								 | 
							
									    set_sgml_parser(Parser, charpos(From)),
							 | 
						||
| 
								 | 
							
									    colourise(M, Parser,
							 | 
						||
| 
								 | 
							
										      [ source(In),
							 | 
						||
| 
								 | 
							
											parse(element)
							 | 
						||
| 
								 | 
							
										      ])
							 | 
						||
| 
								 | 
							
									;   Warn == @off
							 | 
						||
| 
								 | 
							
									->  true
							 | 
						||
| 
								 | 
							
									;   send(M, report, warning, 'Could not find element')
							 | 
						||
| 
								 | 
							
									),
							 | 
						||
| 
								 | 
							
									close(In),
							 | 
						||
| 
								 | 
							
									free_sgml_parser(Parser).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								%	find_element(+Mode, +Parser, +BeginRegex, +In, +Caret, -From-To)
							 | 
						||
| 
								 | 
							
								%
							 | 
						||
| 
								 | 
							
								%	Find the start and end of the current element.  We do so by scanning
							 | 
						||
| 
								 | 
							
								%	backwards to '<\\w+' (Re).  Then we parse the element and see where
							 | 
						||
| 
								 | 
							
								%	it ends.  If this isn't passed the current caret location we look
							 | 
						||
| 
								 | 
							
								%	further backward.
							 | 
						||
| 
								 | 
							
								%
							 | 
						||
| 
								 | 
							
								%	This predicate is non-deterministic, broadening the scope on
							 | 
						||
| 
								 | 
							
								%	backtracking.
							 | 
						||
| 
								 | 
							
								%
							 | 
						||
| 
								 | 
							
								%	By asserting caret/1 before calling this predicate, it will
							 | 
						||
| 
								 | 
							
								%	assert a term element(Tag, Attributes, Start, End), where both
							 | 
						||
| 
								 | 
							
								%	locations are terms of the form loc(Class, Start, End) indicating
							 | 
						||
| 
								 | 
							
								%	the location and type of the begin- and end-tag.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								:- dynamic
							 | 
						||
| 
								 | 
							
									caret/1,			% Caret
							 | 
						||
| 
								 | 
							
									element/4,			% Tag, Attributes, Start, End
							 | 
						||
| 
								 | 
							
									stack/5.			% Tag, Attributes, Class, Fro, To
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								set_caret(Caret) :-
							 | 
						||
| 
								 | 
							
									retractall(caret(Caret)),
							 | 
						||
| 
								 | 
							
									assert(caret(Caret)).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								unset_caret :-
							 | 
						||
| 
								 | 
							
									retractall(caret(_)).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								find_element(M, Caret, Range) :-
							 | 
						||
| 
								 | 
							
									get(M, parser, Parser),
							 | 
						||
| 
								 | 
							
									get(M, text_buffer, TB),
							 | 
						||
| 
								 | 
							
									pce_open(TB, read, In),
							 | 
						||
| 
								 | 
							
									new(Re, regex('<\\w+')),
							 | 
						||
| 
								 | 
							
									(   find_element(M, Parser, Re, In, Caret, Range)
							 | 
						||
| 
								 | 
							
									->  close(In)
							 | 
						||
| 
								 | 
							
									;   close(In),
							 | 
						||
| 
								 | 
							
									    fail
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								find_element(M, Parser, Re, In, Caret, Range) :-
							 | 
						||
| 
								 | 
							
									get(M, text_buffer, TB),
							 | 
						||
| 
								 | 
							
									send(Re, search, TB, Caret, 0),
							 | 
						||
| 
								 | 
							
									get(Re, register_start, 0, Start0),
							 | 
						||
| 
								 | 
							
									find_element(M, Parser, Re, In, Caret, Start0, Range).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								find_element(M, Parser, _Re, In, Caret, Start, Start-To) :-
							 | 
						||
| 
								 | 
							
									\+ get(M?text_buffer, find_fragment,
							 | 
						||
| 
								 | 
							
									       and(message(@arg1, overlap, Start),
							 | 
						||
| 
								 | 
							
										   @arg1?parsed == @off),
							 | 
						||
| 
								 | 
							
									       _),
							 | 
						||
| 
								 | 
							
									seek(In, Start, bof, _),
							 | 
						||
| 
								 | 
							
									set_sgml_parser(Parser, charpos(Start)),
							 | 
						||
| 
								 | 
							
									(   caret(_)
							 | 
						||
| 
								 | 
							
									->  retractall(element(_,_,_,_)),
							 | 
						||
| 
								 | 
							
									    retractall(stack(_,_,_,_,_)),
							 | 
						||
| 
								 | 
							
									    Extra = [ call(begin, emacs_sgml_mode:find_on_begin),
							 | 
						||
| 
								 | 
							
										      call(end,   emacs_sgml_mode:find_on_end)
							 | 
						||
| 
								 | 
							
										    ]
							 | 
						||
| 
								 | 
							
									;   Extra = []
							 | 
						||
| 
								 | 
							
									),
							 | 
						||
| 
								 | 
							
									catch(sgml_parse(Parser,
							 | 
						||
| 
								 | 
							
											 [ source(In),
							 | 
						||
| 
								 | 
							
											   parse(element),
							 | 
						||
| 
								 | 
							
											   syntax_errors(quiet)
							 | 
						||
| 
								 | 
							
											 | Extra
							 | 
						||
| 
								 | 
							
											 ]),
							 | 
						||
| 
								 | 
							
									      E,
							 | 
						||
| 
								 | 
							
									      show_message(M, E)),
							 | 
						||
| 
								 | 
							
													% charpos/1 yields start-position
							 | 
						||
| 
								 | 
							
									get_sgml_parser(Parser, charpos(_, To)),
							 | 
						||
| 
								 | 
							
								%	format('Found ~d-~d~n', [Start, To]),
							 | 
						||
| 
								 | 
							
									To-1 > Caret.
							 | 
						||
| 
								 | 
							
								find_element(M, Parser, Re, In, Caret, Start0, Range) :-
							 | 
						||
| 
								 | 
							
									get(M, text_buffer, TB),
							 | 
						||
| 
								 | 
							
									send(Re, search, TB, Start0, 0),
							 | 
						||
| 
								 | 
							
									get(Re, register_start, 0, Start1),
							 | 
						||
| 
								 | 
							
									find_element(M, Parser, Re, In, Caret, Start1, Range).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								find_on_begin(Tag, Attributes, Parser) :-
							 | 
						||
| 
								 | 
							
									get_sgml_parser(Parser, charpos(From, To)),
							 | 
						||
| 
								 | 
							
									get_sgml_parser(Parser, event_class(Class)),
							 | 
						||
| 
								 | 
							
								%	format('BEGIN: ~w ~w-~w (~w)~n', [Tag, From, To, Class]),
							 | 
						||
| 
								 | 
							
									asserta(stack(Tag, Attributes, Class, From, To)).
							 | 
						||
| 
								 | 
							
								find_on_end(Tag, Parser) :-
							 | 
						||
| 
								 | 
							
									get_sgml_parser(Parser, charpos(EFrom, ETo)),
							 | 
						||
| 
								 | 
							
								%	format('END: ~w ~w-~w~n', [Tag, EFrom, ETo]),
							 | 
						||
| 
								 | 
							
									retract(stack(Tag, Attributes, BClass, BFrom, BTo)),
							 | 
						||
| 
								 | 
							
									caret(Caret),
							 | 
						||
| 
								 | 
							
									(   between(BFrom, ETo, Caret)
							 | 
						||
| 
								 | 
							
									->  get_sgml_parser(Parser, event_class(EClass)),
							 | 
						||
| 
								 | 
							
									    (	element(_,_,_,_)
							 | 
						||
| 
								 | 
							
									    ->	true
							 | 
						||
| 
								 | 
							
									    ;   assert(element(Tag, Attributes,
							 | 
						||
| 
								 | 
							
											       loc(BClass, BFrom, BTo),
							 | 
						||
| 
								 | 
							
											       loc(EClass, EFrom, ETo)))
							 | 
						||
| 
								 | 
							
									    )
							 | 
						||
| 
								 | 
							
									;   true
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										 /*******************************
							 | 
						||
| 
								 | 
							
										 *	   COLOURISING		*
							 | 
						||
| 
								 | 
							
										 *******************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								colourise_and_recenter(M) :->
							 | 
						||
| 
								 | 
							
									"Colour according to syntax and recenter"::
							 | 
						||
| 
								 | 
							
									send(M, auto_colourise_buffer),
							 | 
						||
| 
								 | 
							
									send(M, recenter).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								colourise_buffer(M) :->
							 | 
						||
| 
								 | 
							
									OldTime is cputime,
							 | 
						||
| 
								 | 
							
									new(Class, class(sgml_mode_fragment)),
							 | 
						||
| 
								 | 
							
									get(Class, no_created, OldCreated),
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									send(M, setup_styles),
							 | 
						||
| 
								 | 
							
									send(M, remove_syntax_fragments),
							 | 
						||
| 
								 | 
							
									send(M, report, progress, 'Colourising buffer ...'),
							 | 
						||
| 
								 | 
							
									colourise_buffer(M),
							 | 
						||
| 
								 | 
							
									Used is cputime - OldTime,
							 | 
						||
| 
								 | 
							
									get(Class, no_created, NewCreated),
							 | 
						||
| 
								 | 
							
									Created is NewCreated - OldCreated,
							 | 
						||
| 
								 | 
							
									send(M, report, done,
							 | 
						||
| 
								 | 
							
									     'done, %.2f seconds, %d fragments', Used, Created).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								:- dynamic
							 | 
						||
| 
								 | 
							
									current_tb/2.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								colourise_buffer(M) :-
							 | 
						||
| 
								 | 
							
									make_parser(M, Parser),
							 | 
						||
| 
								 | 
							
									get(M, text_buffer, TB),
							 | 
						||
| 
								 | 
							
									pce_open(TB, read, In),
							 | 
						||
| 
								 | 
							
									colourise(M, Parser,
							 | 
						||
| 
								 | 
							
										  [ source(In)
							 | 
						||
| 
								 | 
							
										  ]),
							 | 
						||
| 
								 | 
							
									free_sgml_parser(Parser).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								colourise(M, Parser, Options) :-
							 | 
						||
| 
								 | 
							
									get_sgml_parser(Parser, file(File)),
							 | 
						||
| 
								 | 
							
									get(M, text_buffer, TB),
							 | 
						||
| 
								 | 
							
									asserta(current_tb(TB, File), Ref),
							 | 
						||
| 
								 | 
							
									catch(sgml_parse(Parser,
							 | 
						||
| 
								 | 
							
											 [ call(begin, emacs_sgml_mode:on_begin),
							 | 
						||
| 
								 | 
							
											   call(end,   emacs_sgml_mode:on_end),
							 | 
						||
| 
								 | 
							
											   call(cdata, emacs_sgml_mode:on_cdata),
							 | 
						||
| 
								 | 
							
											   call(decl,  emacs_sgml_mode:on_decl),
							 | 
						||
| 
								 | 
							
											   call(error, emacs_sgml_mode:on_error)
							 | 
						||
| 
								 | 
							
											 | Options
							 | 
						||
| 
								 | 
							
											 ]),
							 | 
						||
| 
								 | 
							
									      E,
							 | 
						||
| 
								 | 
							
									      show_message(M, E)),
							 | 
						||
| 
								 | 
							
									erase(Ref).
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
								on_begin(_Tag, _Attributes, Parser) :-
							 | 
						||
| 
								 | 
							
									get_sgml_parser(Parser, file(File)),
							 | 
						||
| 
								 | 
							
									current_tb(TB, File),
							 | 
						||
| 
								 | 
							
								%	format('Tag ~w, Attr = ~p~n', [Tag, Attributes]),
							 | 
						||
| 
								 | 
							
									get_sgml_parser(Parser, charpos(From, To)),
							 | 
						||
| 
								 | 
							
									get_sgml_parser(Parser, event_class(Class)),
							 | 
						||
| 
								 | 
							
									Class \== omitted,
							 | 
						||
| 
								 | 
							
									colour_item(tag(begin, Class), TB, From, To).
							 | 
						||
| 
								 | 
							
								on_end(_Tag, Parser) :-
							 | 
						||
| 
								 | 
							
									get_sgml_parser(Parser, file(File)),
							 | 
						||
| 
								 | 
							
									current_tb(TB, File),
							 | 
						||
| 
								 | 
							
									get_sgml_parser(Parser, charpos(From, To)),
							 | 
						||
| 
								 | 
							
									get_sgml_parser(Parser, event_class(Class)),
							 | 
						||
| 
								 | 
							
								%	format('At ~d-~d: Class = ~w~n', [From, To, Class]),
							 | 
						||
| 
								 | 
							
									Class \== omitted,
							 | 
						||
| 
								 | 
							
									colour_item(tag(end, Class), TB, From, To).
							 | 
						||
| 
								 | 
							
								on_cdata(_CDATA, Parser) :-
							 | 
						||
| 
								 | 
							
									get_sgml_parser(Parser, file(File)),
							 | 
						||
| 
								 | 
							
									current_tb(TB, File),
							 | 
						||
| 
								 | 
							
									get_sgml_parser(Parser, charpos(From, To)),
							 | 
						||
| 
								 | 
							
									(   get_sgml_parser(Parser, context([Tag|_]))
							 | 
						||
| 
								 | 
							
									->  (   get_sgml_parser(Parser, dtd(DTD)),
							 | 
						||
| 
								 | 
							
									        dtd_property(DTD, element(Tag, _, Model)),
							 | 
						||
| 
								 | 
							
										(   Model == cdata
							 | 
						||
| 
								 | 
							
										;   Model == rcdata
							 | 
						||
| 
								 | 
							
										)
							 | 
						||
| 
								 | 
							
									    ->	Type = cdata
							 | 
						||
| 
								 | 
							
									    ;	Type = pcdata
							 | 
						||
| 
								 | 
							
									    )
							 | 
						||
| 
								 | 
							
									),
							 | 
						||
| 
								 | 
							
								%	format('CDATA from ~d-~d~n', [From, To]),
							 | 
						||
| 
								 | 
							
									colour_item(cdata(Type), TB, From, To, Fragment),
							 | 
						||
| 
								 | 
							
									(   Type == cdata
							 | 
						||
| 
								 | 
							
									->  send(Fragment, parsed, @off)
							 | 
						||
| 
								 | 
							
									;   true
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
								on_decl(DECL, Parser) :-
							 | 
						||
| 
								 | 
							
									get_sgml_parser(Parser, file(File)),
							 | 
						||
| 
								 | 
							
									current_tb(TB, File),
							 | 
						||
| 
								 | 
							
									get_sgml_parser(Parser, event_class(explicit)),
							 | 
						||
| 
								 | 
							
									get_sgml_parser(Parser, charpos(From, To)),
							 | 
						||
| 
								 | 
							
								%	format('Decl ~d-~d: ~w~n', [From, To, DECL]),
							 | 
						||
| 
								 | 
							
									(   DECL == ''
							 | 
						||
| 
								 | 
							
									->  colour_item(comment, TB, From, To, Fragment),
							 | 
						||
| 
								 | 
							
									    send(Fragment, parsed, @off)
							 | 
						||
| 
								 | 
							
									;   send(regex('DOCTYPE', @off), match, DECL)
							 | 
						||
| 
								 | 
							
									->  colour_item(doctype, TB, From, To)
							 | 
						||
| 
								 | 
							
									;   new(Re, regex('\\w*')),
							 | 
						||
| 
								 | 
							
									    send(Re, match, DECL),
							 | 
						||
| 
								 | 
							
									    get(Re, register_value, DECL, 0, name, DeclType0),
							 | 
						||
| 
								 | 
							
									    get(DeclType0, downcase, DeclType),
							 | 
						||
| 
								 | 
							
								%	    format('Decl(~w)~n', [DeclType]),
							 | 
						||
| 
								 | 
							
									    colour_item(decl(DeclType), TB, From, To)
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
								on_error(Severity, Message, Parser) :-
							 | 
						||
| 
								 | 
							
									current_tb(TB, File),
							 | 
						||
| 
								 | 
							
									(   get_sgml_parser(Parser, file(File))
							 | 
						||
| 
								 | 
							
									->  get_sgml_parser(Parser, charpos(From, To)),
							 | 
						||
| 
								 | 
							
									    colour_item(error(Severity), TB, From, To, Fragment),
							 | 
						||
| 
								 | 
							
									    (   Fragment \== @nil
							 | 
						||
| 
								 | 
							
									    ->  send(Fragment, message,  Message),
							 | 
						||
| 
								 | 
							
										send(Fragment, severity, Severity)
							 | 
						||
| 
								 | 
							
									    ;   true
							 | 
						||
| 
								 | 
							
									    )
							 | 
						||
| 
								 | 
							
									;   format(user_error, 'SGML: Error in other file!~n', [])
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								%	colour_item(+Class, +TB, +Pos)
							 | 
						||
| 
								 | 
							
								%
							 | 
						||
| 
								 | 
							
								%	colourise region if a style is defined for this class.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								colour_item(Class, TB, From, To) :-
							 | 
						||
| 
								 | 
							
									colour_item(Class, TB, From, To, _Fragment).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								colour_item(Class, TB, From, To, Fragment) :-
							 | 
						||
| 
								 | 
							
									style_name(Class, Name), !,
							 | 
						||
| 
								 | 
							
									Len is To - From,
							 | 
						||
| 
								 | 
							
									Len > 0,
							 | 
						||
| 
								 | 
							
									new(Fragment, sgml_mode_fragment(TB, From, Len, Name)).
							 | 
						||
| 
								 | 
							
								colour_item(_, _, _, _, @nil).
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										 /*******************************
							 | 
						||
| 
								 | 
							
										 *	       STYLES		*
							 | 
						||
| 
								 | 
							
										 *******************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								:- discontiguous
							 | 
						||
| 
								 | 
							
									style_name/2,			% +Pattern, -StyleName
							 | 
						||
| 
								 | 
							
									style_object/2.			% +Name, -Style
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								term_expansion(style(Pattern, Style),
							 | 
						||
| 
								 | 
							
									       [ style_name(Pattern, Name),
							 | 
						||
| 
								 | 
							
										 style_object(Name, Style)
							 | 
						||
| 
								 | 
							
									       ]) :-
							 | 
						||
| 
								 | 
							
									gensym(syntax_style_, Name).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								style(tag(begin, shortref),	style(colour := blue,
							 | 
						||
| 
								 | 
							
												      background := grey90,
							 | 
						||
| 
								 | 
							
												      bold   := @on)).
							 | 
						||
| 
								 | 
							
								style(tag(begin, _),		style(colour := blue,
							 | 
						||
| 
								 | 
							
												      bold   := @on)).
							 | 
						||
| 
								 | 
							
								style(tag(end, shorttag),	style(colour := blue,
							 | 
						||
| 
								 | 
							
												      bold   := @on)).
							 | 
						||
| 
								 | 
							
								style(tag(end, shortref),	style(colour := blue,
							 | 
						||
| 
								 | 
							
												      background := grey90,
							 | 
						||
| 
								 | 
							
												      bold   := @on)).
							 | 
						||
| 
								 | 
							
								style(tag(end, _),     		style(colour := blue)).
							 | 
						||
| 
								 | 
							
								style(cdata(cdata),		style(colour := sea_green)).
							 | 
						||
| 
								 | 
							
								style(doctype,			style(bold := @on)).
							 | 
						||
| 
								 | 
							
								style(comment,			style(colour := dark_green,
							 | 
						||
| 
								 | 
							
												      background := grey90)).
							 | 
						||
| 
								 | 
							
								style(decl(_),			style(colour := purple)).
							 | 
						||
| 
								 | 
							
								style(error(warning),		style(background := orange)).
							 | 
						||
| 
								 | 
							
								style(error(_),			style(background := red)).
							 | 
						||
| 
								 | 
							
								style(entity,	     		style(colour := dark_green)).
							 | 
						||
| 
								 | 
							
								style(element,	     		style(background := pale_turquoise)).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										 /*******************************
							 | 
						||
| 
								 | 
							
										 *	     TAGGING		*
							 | 
						||
| 
								 | 
							
										 *******************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								set_insert_point(M, Point:[int]) :->
							 | 
						||
| 
								 | 
							
									"Set mark at point if not set"::
							 | 
						||
| 
								 | 
							
									get(M, mark, Mark),
							 | 
						||
| 
								 | 
							
									(   Mark == 0
							 | 
						||
| 
								 | 
							
									->  (   Point == @default
							 | 
						||
| 
								 | 
							
									    ->	send(M, mark, M?caret)
							 | 
						||
| 
								 | 
							
									    ;	send(M, mark, Point)
							 | 
						||
| 
								 | 
							
									    )
							 | 
						||
| 
								 | 
							
									;   true
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								insert_begin(M, Tag:name) :->
							 | 
						||
| 
								 | 
							
									"Insert begin-tag and required attributes"::
							 | 
						||
| 
								 | 
							
									fix_case(M, Tag, TheTag),
							 | 
						||
| 
								 | 
							
									send(M, format, '<%s>', TheTag),
							 | 
						||
| 
								 | 
							
									get(M, dtd, DTD),
							 | 
						||
| 
								 | 
							
									findall(A, dtd_property(DTD, attribute(Tag, A, _, required)), List),
							 | 
						||
| 
								 | 
							
									send(M, backward_char),
							 | 
						||
| 
								 | 
							
									insert_attributes(List, M),
							 | 
						||
| 
								 | 
							
									send(M, forward_char).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								insert_attributes([], _).
							 | 
						||
| 
								 | 
							
								insert_attributes([H|T], M) :-
							 | 
						||
| 
								 | 
							
									send(M, format, ' %s=""', H),
							 | 
						||
| 
								 | 
							
									send(M, set_insert_point, M?caret-1),
							 | 
						||
| 
								 | 
							
									insert_attributes(T, M).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								insert_end(M, Tag:name) :->
							 | 
						||
| 
								 | 
							
									"Insert end-tag for element"::
							 | 
						||
| 
								 | 
							
									fix_case(M, Tag, TheTag),
							 | 
						||
| 
								 | 
							
									send(M, format, '</%s>', TheTag).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								fix_case(M, Tag, TheTag) :-
							 | 
						||
| 
								 | 
							
									(   get(M, upcase_elements, @on)
							 | 
						||
| 
								 | 
							
									->  get(Tag, upcase, TheTag)
							 | 
						||
| 
								 | 
							
									;   TheTag = Tag
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								style_for_event(Ev, Style) :-
							 | 
						||
| 
								 | 
							
									(   send(Ev, has_modifier, c)
							 | 
						||
| 
								 | 
							
									->  Style = inline
							 | 
						||
| 
								 | 
							
									;   send(Ev, has_modifier, s)
							 | 
						||
| 
								 | 
							
									->  Style = block
							 | 
						||
| 
								 | 
							
									;   send(Ev, has_modifier, m)
							 | 
						||
| 
								 | 
							
									->  Style = shorttag
							 | 
						||
| 
								 | 
							
									;   Style = @default
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								show_menu(M, Ev:event) :->
							 | 
						||
| 
								 | 
							
									"Show menu to insert-tag/tag selection"::
							 | 
						||
| 
								 | 
							
									(   send(M, in_tag)
							 | 
						||
| 
								 | 
							
									->  send(M, show_attribute_menu, Ev)
							 | 
						||
| 
								 | 
							
									;   send(M, show_element_menu, Ev)
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								in_tag(M) :->
							 | 
						||
| 
								 | 
							
									"Test whether caret is between <>"::
							 | 
						||
| 
								 | 
							
									get(M, caret, Caret),
							 | 
						||
| 
								 | 
							
									get(M, text_buffer, TB),
							 | 
						||
| 
								 | 
							
									send(regex('<[^>]*'), match, TB, Caret, 0),
							 | 
						||
| 
								 | 
							
									send(regex('[^<]*[>/]'), match, TB, Caret). % / for shortag
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								show_element_menu(M, Ev:event) :->
							 | 
						||
| 
								 | 
							
									"Show menu for inserting a new element"::
							 | 
						||
| 
								 | 
							
									(   get(M, allowed_elements, List),
							 | 
						||
| 
								 | 
							
									    delete(List, '#pcdata', Elems),
							 | 
						||
| 
								 | 
							
									    Elems \== [],
							 | 
						||
| 
								 | 
							
									    sort(Elems, Sorted)
							 | 
						||
| 
								 | 
							
									->  (   get(M, selection, point(A,B)), B > A
							 | 
						||
| 
								 | 
							
									    ->	Label = tag_selection
							 | 
						||
| 
								 | 
							
									    ;	Label = insert_element
							 | 
						||
| 
								 | 
							
									    ),
							 | 
						||
| 
								 | 
							
									    get(Ev, button, Button),
							 | 
						||
| 
								 | 
							
									    style_for_event(Ev, Style),
							 | 
						||
| 
								 | 
							
									    new(G, popup_gesture(new(P, popup(Label,
							 | 
						||
| 
								 | 
							
													      message(M, popup_tag_selection,
							 | 
						||
| 
								 | 
							
														      @arg1, Style))),
							 | 
						||
| 
								 | 
							
												 Button, new(modifier))),
							 | 
						||
| 
								 | 
							
									    send(P, show_label, @on),
							 | 
						||
| 
								 | 
							
									    length(Sorted, Len),
							 | 
						||
| 
								 | 
							
									    Cols is max(1, Len // 20),
							 | 
						||
| 
								 | 
							
									    send(P, columns, Cols),
							 | 
						||
| 
								 | 
							
									    send_list(P, append, Sorted),
							 | 
						||
| 
								 | 
							
									    send(G, event, Ev)
							 | 
						||
| 
								 | 
							
									;   send(M, report, warning, 'No element allowed here')
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								show_attribute_menu(M, Ev:event) :->
							 | 
						||
| 
								 | 
							
									"Show menu for adding attributes"::
							 | 
						||
| 
								 | 
							
									get(M, caret, Caret),
							 | 
						||
| 
								 | 
							
									get(M, text_buffer, TB),
							 | 
						||
| 
								 | 
							
									new(Re, regex('<[^>]*')),
							 | 
						||
| 
								 | 
							
									send(Re, match, TB, Caret, 0),
							 | 
						||
| 
								 | 
							
									get(Re, register_start, 0, Start),
							 | 
						||
| 
								 | 
							
									(   get(M, looking_at_element, Start, E)
							 | 
						||
| 
								 | 
							
									->  make_parser(M, Parser),
							 | 
						||
| 
								 | 
							
									    load_dtd(M, Parser),
							 | 
						||
| 
								 | 
							
									    get_sgml_parser(Parser, dtd(DTD)),
							 | 
						||
| 
								 | 
							
									    dtd_property(DTD, attributes(E, Atts)),
							 | 
						||
| 
								 | 
							
									    (	Atts == []
							 | 
						||
| 
								 | 
							
									    ->	free_sgml_parser(Parser),
							 | 
						||
| 
								 | 
							
										send(M, report, warning, 'Element "%s" has no attributes', E)
							 | 
						||
| 
								 | 
							
									    ;	sort(Atts, Sorted),
							 | 
						||
| 
								 | 
							
								%	        format('Atts = ~p~n', [Sorted]),
							 | 
						||
| 
								 | 
							
									        get(Ev, button, Button),
							 | 
						||
| 
								 | 
							
										new(G, popup_gesture(new(P, popup(add_attribute,
							 | 
						||
| 
								 | 
							
														  message(M, insert_attribute,
							 | 
						||
| 
								 | 
							
															  @arg1))),
							 | 
						||
| 
								 | 
							
												     Button,
							 | 
						||
| 
								 | 
							
												     new(modifier))),
							 | 
						||
| 
								 | 
							
										send(P, show_label, @on),
							 | 
						||
| 
								 | 
							
										length(Sorted, Len),
							 | 
						||
| 
								 | 
							
										Cols is max(1, Len // 10),
							 | 
						||
| 
								 | 
							
										send(P, columns, Cols),
							 | 
						||
| 
								 | 
							
										fill_attribute_menu(Sorted, DTD, E, P, M),
							 | 
						||
| 
								 | 
							
										free_sgml_parser(Parser),
							 | 
						||
| 
								 | 
							
										send(G, event, Ev)
							 | 
						||
| 
								 | 
							
									    )
							 | 
						||
| 
								 | 
							
									;   send(M, report, warning, 'Not in begin-tag')
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								fill_attribute_menu([], _, _, _, _).
							 | 
						||
| 
								 | 
							
								fill_attribute_menu([A|T], DTD, E, P, Mode) :-
							 | 
						||
| 
								 | 
							
									dtd_property(DTD, attribute(E, A, Type, Default)),
							 | 
						||
| 
								 | 
							
									add_attribute_menu(Type, Default, A, P, Mode),
							 | 
						||
| 
								 | 
							
									fill_attribute_menu(T, DTD, E, P, Mode).
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
								add_attribute_menu(nameof(List), Def, A, P, Mode) :- !,
							 | 
						||
| 
								 | 
							
									send(P, append, new(P2, popup(A, message(Mode, insert_attribute,
							 | 
						||
| 
								 | 
							
														 A, @arg1)))),
							 | 
						||
| 
								 | 
							
									add_attribute_values(List, Def, P2).
							 | 
						||
| 
								 | 
							
								add_attribute_menu(Type, Def, A, P, _Mode) :- !,
							 | 
						||
| 
								 | 
							
									type_label(Type, TypeLabel),
							 | 
						||
| 
								 | 
							
									send(P, append, new(MI, menu_item(A, @default,
							 | 
						||
| 
								 | 
							
													  string('%s (%s)', A, TypeLabel)))),
							 | 
						||
| 
								 | 
							
									(   Def == required
							 | 
						||
| 
								 | 
							
									->  send(MI, font, bold)
							 | 
						||
| 
								 | 
							
									;   true
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								type_label(list(Type), Label) :- !,
							 | 
						||
| 
								 | 
							
									atom_concat(Type, s, Label).
							 | 
						||
| 
								 | 
							
								type_label(Type, Type).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								add_attribute_values([], _, _).
							 | 
						||
| 
								 | 
							
								add_attribute_values([H|T], Def, P) :-
							 | 
						||
| 
								 | 
							
									send(P, append, new(MI, menu_item(H))),
							 | 
						||
| 
								 | 
							
									(   Def == default(H)
							 | 
						||
| 
								 | 
							
									->  send(MI, font, bold)
							 | 
						||
| 
								 | 
							
									;   true
							 | 
						||
| 
								 | 
							
									),
							 | 
						||
| 
								 | 
							
									add_attribute_values(T, Def, P).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								insert_attribute(M, Att:name, Val:'[name|int|real]') :->
							 | 
						||
| 
								 | 
							
									"Add attribute-value pair"::
							 | 
						||
| 
								 | 
							
									get(M, text_buffer, TB),
							 | 
						||
| 
								 | 
							
									get(M, caret, Caret),
							 | 
						||
| 
								 | 
							
									new(Re, regex('\\(\\s +\\|[/>]\\)')),
							 | 
						||
| 
								 | 
							
									send(Re, search, TB, Caret),	% find place to insert
							 | 
						||
| 
								 | 
							
									get(Re, register_start, 0, Where),
							 | 
						||
| 
								 | 
							
									(   send(regex('\\s +'), match, TB, Where)
							 | 
						||
| 
								 | 
							
									->  get(Re, register_end, 0, NewCaret), % after blanks
							 | 
						||
| 
								 | 
							
									    send(M, caret, NewCaret)
							 | 
						||
| 
								 | 
							
									;   send(M, caret, Where),
							 | 
						||
| 
								 | 
							
									    send(M, format, ' ')
							 | 
						||
| 
								 | 
							
									),
							 | 
						||
| 
								 | 
							
									(   Val == @default
							 | 
						||
| 
								 | 
							
									->  send(M, format, '%s=""', Att),
							 | 
						||
| 
								 | 
							
									    get(M, caret, C),
							 | 
						||
| 
								 | 
							
									    (	send(M, looking_at, '\\s \\|[/>]')
							 | 
						||
| 
								 | 
							
									    ->  true
							 | 
						||
| 
								 | 
							
									    ;	send(M, format, ' ')
							 | 
						||
| 
								 | 
							
									    ),
							 | 
						||
| 
								 | 
							
									    send(M, caret, C-1)
							 | 
						||
| 
								 | 
							
									;   send(M, format, '%s="%s"', Att, Val),
							 | 
						||
| 
								 | 
							
									    (	send(M, looking_at, '\\s \\|[/>]')
							 | 
						||
| 
								 | 
							
									    ->  true
							 | 
						||
| 
								 | 
							
									    ;	send(M, format, ' ')
							 | 
						||
| 
								 | 
							
									    )
							 | 
						||
| 
								 | 
							
									),
							 | 
						||
| 
								 | 
							
									send(M, mark_undo).		% called from popup!
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								popup_tag_selection(M, Tag:name, Style0:[{inline,shorttag,block}]) :->
							 | 
						||
| 
								 | 
							
									"->tag_selection wrapper for popup"::
							 | 
						||
| 
								 | 
							
									(   Style0 == @default
							 | 
						||
| 
								 | 
							
									->  style_for_event(@event, Style)
							 | 
						||
| 
								 | 
							
									;   Style = Style0
							 | 
						||
| 
								 | 
							
									),
							 | 
						||
| 
								 | 
							
									send(M, tag_selection, Tag, Style),
							 | 
						||
| 
								 | 
							
									send(M, mark_undo).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								tag_selection(M, Tag:[name], Style:[{inline,block,shorttag}]) :->
							 | 
						||
| 
								 | 
							
									"Tag the current selection using element"::
							 | 
						||
| 
								 | 
							
									(   Tag == @default
							 | 
						||
| 
								 | 
							
									->  new(TI, text_item(element)),
							 | 
						||
| 
								 | 
							
									    (   get(M, allowed_elements, List),
							 | 
						||
| 
								 | 
							
								%	        format('Allowed: ~p~n', [List]),
							 | 
						||
| 
								 | 
							
										delete(List, '#pcdata', Elems),
							 | 
						||
| 
								 | 
							
										sort(Elems, Sorted)
							 | 
						||
| 
								 | 
							
									    ->  send(TI, value_set, Sorted)
							 | 
						||
| 
								 | 
							
									    ;   true
							 | 
						||
| 
								 | 
							
									    ),
							 | 
						||
| 
								 | 
							
									    get(M, prompt_using, TI, String),
							 | 
						||
| 
								 | 
							
									    get(String, value, TheTag)
							 | 
						||
| 
								 | 
							
									;   TheTag = Tag
							 | 
						||
| 
								 | 
							
									),
							 | 
						||
| 
								 | 
							
									(   get(M, selection, point(A,B)),
							 | 
						||
| 
								 | 
							
									    B > A
							 | 
						||
| 
								 | 
							
									->  send(M, tag_region, TheTag, A, B, Style),
							 | 
						||
| 
								 | 
							
									    send(M, selection, 0, 0),
							 | 
						||
| 
								 | 
							
									    send(M, colourise_element)
							 | 
						||
| 
								 | 
							
									;   send(M, insert_element, TheTag, Style)
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								tag_region(M, Tag:[name], From:int, To:int,
							 | 
						||
| 
								 | 
							
									   Style:[{inline,block,shorttag}]) :->
							 | 
						||
| 
								 | 
							
									"Tag a defined region"::
							 | 
						||
| 
								 | 
							
									fix_case(M, Tag, TheTag),
							 | 
						||
| 
								 | 
							
									get(M, text_buffer, TB),
							 | 
						||
| 
								 | 
							
									(   Style == shorttag
							 | 
						||
| 
								 | 
							
									->  send(TB, insert, To, /),
							 | 
						||
| 
								 | 
							
									    send(TB, insert, From, string('<%s/', TheTag))
							 | 
						||
| 
								 | 
							
									;   Style == block
							 | 
						||
| 
								 | 
							
									->  (   get(M, column, To, 0)
							 | 
						||
| 
								 | 
							
									    ->	send(TB, insert, To, string('</%s>\n', TheTag))
							 | 
						||
| 
								 | 
							
									    ;	send(TB, insert, From, string('\n</%s>\n', TheTag))
							 | 
						||
| 
								 | 
							
									    ),
							 | 
						||
| 
								 | 
							
									    (   get(M, column, From, 0)
							 | 
						||
| 
								 | 
							
									    ->	send(TB, insert, From, string('<%s>\n', TheTag))
							 | 
						||
| 
								 | 
							
									    ;	send(TB, insert, From, string('\n<%s>\n', TheTag))
							 | 
						||
| 
								 | 
							
									    )
							 | 
						||
| 
								 | 
							
									;   Style == inline
							 | 
						||
| 
								 | 
							
									->  send(TB, insert, To, string('</%s>', TheTag)),
							 | 
						||
| 
								 | 
							
									    send(TB, insert, From, string('<%s>', TheTag))
							 | 
						||
| 
								 | 
							
									;   get(M, column, From, 0),
							 | 
						||
| 
								 | 
							
									    get(M, column, To, 0)
							 | 
						||
| 
								 | 
							
									->  send(M, tag_region, Tag, From, To, block)
							 | 
						||
| 
								 | 
							
									;   send(M, tag_region, Tag, From, To, inline)
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
									    
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								insert_element(M, Tag:element=name, Style:[{inline,shorttag,block}]) :->
							 | 
						||
| 
								 | 
							
									"Insert a new empty element"::
							 | 
						||
| 
								 | 
							
									(   get(M, dtd, DTD),
							 | 
						||
| 
								 | 
							
									    dtd_property(DTD, element(Tag, Omit, Content))
							 | 
						||
| 
								 | 
							
									->  true
							 | 
						||
| 
								 | 
							
									;   Omit = omit(false, false),
							 | 
						||
| 
								 | 
							
									    Content = '#pcdata'
							 | 
						||
| 
								 | 
							
									),
							 | 
						||
| 
								 | 
							
									fix_case(M, Tag, TheTag),
							 | 
						||
| 
								 | 
							
									send(M, prepare_insert),
							 | 
						||
| 
								 | 
							
									send(M, mark, 0),		% put insert position here
							 | 
						||
| 
								 | 
							
									insert_by_style(Style, M, TheTag, Omit, Content, _),
							 | 
						||
| 
								 | 
							
									send(M, colourise_element),
							 | 
						||
| 
								 | 
							
									(   get(M, mark, Mark),
							 | 
						||
| 
								 | 
							
									    Mark > 0
							 | 
						||
| 
								 | 
							
									->  send(M, caret, Mark)
							 | 
						||
| 
								 | 
							
									;   true
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								prepare_insert(M) :->
							 | 
						||
| 
								 | 
							
									"Find location to insert a new tag"::
							 | 
						||
| 
								 | 
							
									get(M, caret, Caret),
							 | 
						||
| 
								 | 
							
									(   find_element(M, Caret, From-_To)
							 | 
						||
| 
								 | 
							
									->  get(M, looking_at_element, From, E),
							 | 
						||
| 
								 | 
							
								%	    format('~p: Inserting in "~w" at ~w~n', [M, E, From]),
							 | 
						||
| 
								 | 
							
									    get(M, dtd, DTD),
							 | 
						||
| 
								 | 
							
									    dtd_property(DTD, element(E, _, Content)),
							 | 
						||
| 
								 | 
							
									    (	mixed_content(Content)
							 | 
						||
| 
								 | 
							
									    ->	true
							 | 
						||
| 
								 | 
							
									    ;	get(M, column, From, Col0),
							 | 
						||
| 
								 | 
							
										Col is Col0+2,
							 | 
						||
| 
								 | 
							
										get(M, text_buffer, TB),
							 | 
						||
| 
								 | 
							
										get(TB, scan, Caret, line, 0, start, SOL),
							 | 
						||
| 
								 | 
							
										(   new(Re, regex('\\s *')),
							 | 
						||
| 
								 | 
							
										    send(Re, match, TB, SOL, Caret),
							 | 
						||
| 
								 | 
							
										    get(Re, register_end, Caret)
							 | 
						||
| 
								 | 
							
										->  true		% at a blank line
							 | 
						||
| 
								 | 
							
										;   send(M, newline)
							 | 
						||
| 
								 | 
							
										),
							 | 
						||
| 
								 | 
							
										send(M, align_line, Col)
							 | 
						||
| 
								 | 
							
									    )
							 | 
						||
| 
								 | 
							
									;   true
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								insert_by_style(_, M, Tag, _, empty, End) :- !,
							 | 
						||
| 
								 | 
							
									send(M, insert_begin, Tag),
							 | 
						||
| 
								 | 
							
									(   get(M, dialect, xml)
							 | 
						||
| 
								 | 
							
									->  send(M, backward_char),
							 | 
						||
| 
								 | 
							
									    send(M, format, /),
							 | 
						||
| 
								 | 
							
									    send(M, forward_char)
							 | 
						||
| 
								 | 
							
									;   true
							 | 
						||
| 
								 | 
							
									),
							 | 
						||
| 
								 | 
							
									get(M, caret, End).
							 | 
						||
| 
								 | 
							
								insert_by_style(Style, M, Tag, _, Model, End) :-
							 | 
						||
| 
								 | 
							
									required_content(Model, List),
							 | 
						||
| 
								 | 
							
									(   mixed_content(Model)
							 | 
						||
| 
								 | 
							
									->  def_style(Style, inline, TheStyle),
							 | 
						||
| 
								 | 
							
									    insert_by_style(TheStyle, M, Tag, End),
							 | 
						||
| 
								 | 
							
									    send(M, set_insert_point)
							 | 
						||
| 
								 | 
							
									;   insert_by_style(block, M, Tag, End0),
							 | 
						||
| 
								 | 
							
									    get(M, text_buffer, TB),
							 | 
						||
| 
								 | 
							
									    new(Mark, fragment(TB, End0, 0)),
							 | 
						||
| 
								 | 
							
									    insert_sub_elements(List, M),
							 | 
						||
| 
								 | 
							
									    get(Mark, start, End),
							 | 
						||
| 
								 | 
							
									    free(Mark)
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								mixed_content(M) :-
							 | 
						||
| 
								 | 
							
									term_member('#pcdata', M), !.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								term_member(X, X).
							 | 
						||
| 
								 | 
							
								term_member(X, C) :-
							 | 
						||
| 
								 | 
							
									compound(C),
							 | 
						||
| 
								 | 
							
									arg(_, C, A),
							 | 
						||
| 
								 | 
							
									term_member(X, A).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								insert_by_style(shorttag, M, Tag, End) :- !,
							 | 
						||
| 
								 | 
							
									send(M, insert_begin, Tag),
							 | 
						||
| 
								 | 
							
									send(M, backward_delete_char),
							 | 
						||
| 
								 | 
							
									send(M, format, '//'),
							 | 
						||
| 
								 | 
							
									get(M, caret, End),
							 | 
						||
| 
								 | 
							
									send(M, backward_char).
							 | 
						||
| 
								 | 
							
								insert_by_style(inline, M, Tag, End) :- !,
							 | 
						||
| 
								 | 
							
									send(M, insert_begin, Tag),
							 | 
						||
| 
								 | 
							
									get(M, caret, New),
							 | 
						||
| 
								 | 
							
									send(M, insert_end, Tag),
							 | 
						||
| 
								 | 
							
									get(M, caret, End),
							 | 
						||
| 
								 | 
							
									send(M, caret, New).
							 | 
						||
| 
								 | 
							
								insert_by_style(block, M, Tag, End) :- !,
							 | 
						||
| 
								 | 
							
									send(M, insert_begin, Tag),
							 | 
						||
| 
								 | 
							
									get(M, caret, Insert),
							 | 
						||
| 
								 | 
							
									send(M, newline_and_indent),
							 | 
						||
| 
								 | 
							
									send(M, insert_end, Tag),
							 | 
						||
| 
								 | 
							
									get(M, caret, End),
							 | 
						||
| 
								 | 
							
									send(M, caret, Insert).
							 | 
						||
| 
								 | 
							
								insert_by_style(@default, M, Tag, End) :-
							 | 
						||
| 
								 | 
							
									get(M, text_buffer, TB),
							 | 
						||
| 
								 | 
							
									get(M, caret, Caret),
							 | 
						||
| 
								 | 
							
									get(TB, scan, Caret, line, 0, start, SOL),
							 | 
						||
| 
								 | 
							
									(   send(regex('\\s *$'), match, TB, SOL)
							 | 
						||
| 
								 | 
							
									->  insert_by_style(block, M, Tag, End)
							 | 
						||
| 
								 | 
							
									;   insert_by_style(inline, M, Tag, End)
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def_style(@default, Style, Style) :- !.
							 | 
						||
| 
								 | 
							
								def_style(Style, _, Style).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								insert_sub_elements([], _).
							 | 
						||
| 
								 | 
							
								insert_sub_elements([H|T], M) :-
							 | 
						||
| 
								 | 
							
									send(M, format, '  '),
							 | 
						||
| 
								 | 
							
									get(M, dtd, DTD),
							 | 
						||
| 
								 | 
							
									dtd_property(DTD, element(H, Omit, Content)),
							 | 
						||
| 
								 | 
							
									send(M, prepare_insert),
							 | 
						||
| 
								 | 
							
									insert_by_style(@default, M, H, Omit, Content, End),
							 | 
						||
| 
								 | 
							
									(   T == []
							 | 
						||
| 
								 | 
							
									->  true
							 | 
						||
| 
								 | 
							
									;   send(M, caret, End),
							 | 
						||
| 
								 | 
							
									    insert_sub_elements(T, M)
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								required_content(empty, []).
							 | 
						||
| 
								 | 
							
								required_content(cdata, []).
							 | 
						||
| 
								 | 
							
								required_content(Model, Elems) :-
							 | 
						||
| 
								 | 
							
									phrase(required_content(Model), Elems).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								required_content((A,B)) --> !,
							 | 
						||
| 
								 | 
							
									required_content(A),
							 | 
						||
| 
								 | 
							
									required_content(B).
							 | 
						||
| 
								 | 
							
								required_content(&(A,B)) --> !,
							 | 
						||
| 
								 | 
							
									required_content(A),
							 | 
						||
| 
								 | 
							
									required_content(B).
							 | 
						||
| 
								 | 
							
								required_content('|'(_,_)) --> !,
							 | 
						||
| 
								 | 
							
									[].
							 | 
						||
| 
								 | 
							
								required_content(?(_)) -->
							 | 
						||
| 
								 | 
							
									[].
							 | 
						||
| 
								 | 
							
								required_content(*(_)) -->
							 | 
						||
| 
								 | 
							
									[].
							 | 
						||
| 
								 | 
							
								required_content(+(A)) -->
							 | 
						||
| 
								 | 
							
									required_content(A).
							 | 
						||
| 
								 | 
							
								required_content('#pcdata') --> !,
							 | 
						||
| 
								 | 
							
									[].
							 | 
						||
| 
								 | 
							
								required_content(A) -->
							 | 
						||
| 
								 | 
							
									[A].
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								looking_at_element(M, From:int, Elem:name) :<-
							 | 
						||
| 
								 | 
							
									new(Re, regex('<\\([-_:a-zA-Z0-9]+\\)')),
							 | 
						||
| 
								 | 
							
									get(M, text_buffer, TB),
							 | 
						||
| 
								 | 
							
									send(Re, match, TB, From),
							 | 
						||
| 
								 | 
							
									get(Re, register_value, TB, 1, name, Elem).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								allowed_elements(M, Allowed:prolog) :<-
							 | 
						||
| 
								 | 
							
									"Show elements allowed here"::
							 | 
						||
| 
								 | 
							
									get(M, caret, Caret),
							 | 
						||
| 
								 | 
							
									get(M, text_buffer, TB),
							 | 
						||
| 
								 | 
							
									new(Re, regex('<\\w+')),
							 | 
						||
| 
								 | 
							
									make_parser(M, Parser),
							 | 
						||
| 
								 | 
							
									load_dtd(M, Parser),
							 | 
						||
| 
								 | 
							
									get_sgml_parser(Parser, dtd(DTD)),
							 | 
						||
| 
								 | 
							
									set_sgml_parser(Parser, doctype(_)),
							 | 
						||
| 
								 | 
							
									pce_open(TB, read, In),
							 | 
						||
| 
								 | 
							
									set_caret(Caret),
							 | 
						||
| 
								 | 
							
									(   find_element(M, Parser, Re, In, Caret, From-_To),
							 | 
						||
| 
								 | 
							
									    get(M, looking_at_element, From, E),
							 | 
						||
| 
								 | 
							
								%	    format('Looking at ~w~n', [E]),
							 | 
						||
| 
								 | 
							
									    (	dtd_property(DTD, doctype(E))
							 | 
						||
| 
								 | 
							
									    ;   dtd_property(DTD, element(E, omit(_, false), _))
							 | 
						||
| 
								 | 
							
									    )
							 | 
						||
| 
								 | 
							
									->  unset_caret,
							 | 
						||
| 
								 | 
							
									    seek(In, From, bof, _),
							 | 
						||
| 
								 | 
							
									    set_sgml_parser(Parser, charpos(From)),
							 | 
						||
| 
								 | 
							
									    Len is Caret - From,
							 | 
						||
| 
								 | 
							
									    catch(sgml_parse(Parser,
							 | 
						||
| 
								 | 
							
											     [ goal(emacs_sgml_mode:feed(In, Len)),
							 | 
						||
| 
								 | 
							
											       syntax_errors(quiet),
							 | 
						||
| 
								 | 
							
											       parse(input)	% do not complete document
							 | 
						||
| 
								 | 
							
											     ]),
							 | 
						||
| 
								 | 
							
										  E,
							 | 
						||
| 
								 | 
							
										  show_message(M, E)),
							 | 
						||
| 
								 | 
							
									    (	element(_,_,_,loc(explicit,_,_))
							 | 
						||
| 
								 | 
							
									    ->	format('End-tag available~n', [])
							 | 
						||
| 
								 | 
							
									    ;	true
							 | 
						||
| 
								 | 
							
									    ),
							 | 
						||
| 
								 | 
							
									    get_sgml_parser(Parser, allowed(Allowed))
							 | 
						||
| 
								 | 
							
									;   unset_caret,
							 | 
						||
| 
								 | 
							
									    dtd_property(DTD, doctype(DocType)),
							 | 
						||
| 
								 | 
							
									    atom(DocType)
							 | 
						||
| 
								 | 
							
									->  Allowed = [DocType]
							 | 
						||
| 
								 | 
							
									;   send(M, report, warning, 'No current element'),
							 | 
						||
| 
								 | 
							
									    Allowed = []
							 | 
						||
| 
								 | 
							
									),
							 | 
						||
| 
								 | 
							
									close(In),
							 | 
						||
| 
								 | 
							
									free_sgml_parser(Parser).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								feed(In, Len, Parser) :-
							 | 
						||
| 
								 | 
							
									copy_stream_data(In, Parser, Len).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								report_allowed(M) :->			% DEBUGGING
							 | 
						||
| 
								 | 
							
									"Report allowed elements at point"::
							 | 
						||
| 
								 | 
							
									get(M, allowed_elements, Allowed),
							 | 
						||
| 
								 | 
							
									concat_atom(Allowed, ', ', Atom),
							 | 
						||
| 
								 | 
							
									send(M, report, status, 'Allowed: %s', Atom).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								show_message(M, E) :-
							 | 
						||
| 
								 | 
							
									message_to_string(E, String),
							 | 
						||
| 
								 | 
							
									send(M, report, warning, 'Caught error: %s', String).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										 /*******************************
							 | 
						||
| 
								 | 
							
										 *	   MOVING AROUND	*
							 | 
						||
| 
								 | 
							
										 *******************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								forward_move_out(M) :->
							 | 
						||
| 
								 | 
							
									"Move forwards to end of current element"::
							 | 
						||
| 
								 | 
							
									get(M, caret, Caret),
							 | 
						||
| 
								 | 
							
									(   find_element(M, Caret, _From-To)
							 | 
						||
| 
								 | 
							
									->  send(M, caret, To)
							 | 
						||
| 
								 | 
							
									;   send(M, report, warning, 'Cannot find element')
							 | 
						||
| 
								 | 
							
									).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								:- emacs_end_mode.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										 /*******************************
							 | 
						||
| 
								 | 
							
										 *	       XML		*
							 | 
						||
| 
								 | 
							
										 *******************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								:- emacs_begin_mode(xml, sgml,
							 | 
						||
| 
								 | 
							
										    "Mode for editing XML documents",
							 | 
						||
| 
								 | 
							
										    [],
							 | 
						||
| 
								 | 
							
										    []).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								initialise(M) :->
							 | 
						||
| 
								 | 
							
									send_super(M, initialise),
							 | 
						||
| 
								 | 
							
									send(M, dialect, xml).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								open_document(M, DTD:doctype=name) :->
							 | 
						||
| 
								 | 
							
									"Insert document header"::
							 | 
						||
| 
								 | 
							
									send(M, format, '<?xml version="1.0"?>\n'),
							 | 
						||
| 
								 | 
							
									send_super(M, open_document, DTD).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								:- emacs_end_mode.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										 /*******************************
							 | 
						||
| 
								 | 
							
										 *	       HTML		*
							 | 
						||
| 
								 | 
							
										 *******************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								:- emacs_begin_mode(html, sgml,
							 | 
						||
| 
								 | 
							
										    "Mode for editing HTML documents",
							 | 
						||
| 
								 | 
							
										    [],
							 | 
						||
| 
								 | 
							
										    []).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								initialise(M) :->
							 | 
						||
| 
								 | 
							
									send_super(M, initialise),
							 | 
						||
| 
								 | 
							
									send(M, dialect, html).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								open_document(M) :->
							 | 
						||
| 
								 | 
							
									"Insert document header"::
							 | 
						||
| 
								 | 
							
									send(M, format,
							 | 
						||
| 
								 | 
							
									     '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">\n\n'),
							 | 
						||
| 
								 | 
							
									send(M, insert_element, html).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								:- emacs_end_mode.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										 /*******************************
							 | 
						||
| 
								 | 
							
										 *	      FRAGMENT		*
							 | 
						||
| 
								 | 
							
										 *******************************/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								:- pce_begin_class(sgml_mode_fragment, emacs_colour_fragment,
							 | 
						||
| 
								 | 
							
										   "Provide colourised region").
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								variable(parsed,   bool := @on,	     both, "@off for unparsed fragments").
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								:- pce_end_class.
							 |