Warning: this is an htmlized version!
The original is across this link,
and the conversion rules are here.
;
;   Copyright (c) 2004-2007 by Keith M. Knowles.
;   Free software licenced under the terms of the
;   GNU Lesser General Public Licence.
;
;
;   Program:		Elinks.
;   Module:		elinks.el -- hypertext capability.
;   Version:		1
;   Author:		Keith M. Knowles.
;   Date:		Feb 2004
;
;
;   Changes:
;
;   07/12/13 kmk	Allowed '-' in tag names.
;   07/12/13 kmk	Define next/prev-link and bind C-> and C-<.
;   07/12/08 kmk	Timestamp was appearing as yyddmm.
;   07/12/04 kmk	Add time-stamping for Iweb.
;   07/12/04 kmk	Define kp-2, 4, 5, 6 and 8 as synonyms.
;   07/09/26 kmk	Fix a /tag logic error at the beginning of buffer
;			that prevented recording of the return link.
;   07/09/24 kmk	Make elinks-widen stack a history entry for return.
;   07/09/21 kmk	Include linked documentation in this file.
;   07/08/12 kmk	Completed 5/28 work and clean-up.
;   07/07/22 kmk	Implement < /tag > to control narrowing.
;   06/05/28 kmk	Use regexp to drive elinks.
;   06/04/23 kmk	Implement link-translation.
;   05/01/30 kmk	Change < topic > to < tag >.
;   05/01/25 kmk	Switch to file#tag notation.
;   04/12/28 kmk	Change link format to: "tag1 < see tag2 [file] >".
;   04/11/29 kmk	Interpret line numbers and search strings
;   04/05/05 kmk	Implement % to return to last place.
;   04/03/12 kmk	Include incremental search for tag.
;   04/02/07 kmk	Implement hyper-link capability in text files.
;
;
;   Description:	<see #root>
;
;   <tag root>;
;
;		       Elinks -- hyper-text in Emacs
;
;   If you are unfamiliar with using  Elinks, press C-. to follow this link:
;   <see #summary>.
;
;   Elinks provides hyper-linking in plain-text files for use within Emacs.
;
;   Elinks provides  poor-man's browsing  in Emacs by  following hyper-links
;   embedded  in the  text in  the form of  "see" directives.   A hyper-link
;   refers  to its  target using  a file#tag  format, specifying  a location
;   within a file.  These directives  link topics together.  History is kept
;   to allow return.
;
;   Topics are delineated  with the "tag" directive.  A tag  can be a target
;   for a hyper-link and viewed as an open or closed topic.
;
;   An  open  topic is  viewed  in the  file  along  with whatever  contents
;   surround it.  For example, a target topic might be a comment in a source
;   file.  In  this case, you wish to  also see the source  code the comment
;   applies to.
;
;   A closed topic  is viewed in isolation effectively  as a separate "page"
;   of information.  (This is done in Emacs by narrowing the window.)
;
;   Elinks  is  partnered  by  Docgen,  a separate  utility  that  generates
;   browsable HTML pages from  plain-text files, recognizing Elinks in order
;   to link pages.
;
;   Elinks depends upon the file, menu.el.
;
;   Subtopics:
;     1.  <see #see		"see" directive>
;     2.  <see #tag		"tag" directive>
;     3.  <see #ops		Elink operations>
;     4.  <see #ex		an example page>
;     5.  <see #notes		To be done>
;   </tag>
;
;
;   <tag ex>;			      <see #root up:>
;   <see #ops prev:>					  <see #notes next:>
;			 An example of page layout
;
;   This is an  example of how a  topic page might be laid  out.  Each topic
;   should fit  in one  window.  In this  example, explanatory  text follows
;   the title.  The topic ends with a list of links to subtopics.	   
;
;   Note that  because the explanatory text may  contain cross-links, (which
;   would appear  in the down-link menu,  an author might choose  to put the
;   table of subtopics first, immediately after the title, and then with the
;   explanatory text.  This style might  also assist the reader who seeks to
;   quickly "drill down" to a particular subject.		   
;
;   Subtopics:
;     1. < see #subtop1		subtop1 decription >
;     2. < see #subtop2		subtop2 decription >
;     3. < see #subtop3		subtop3 decription >
;        ..
;        ..
;     8. < see #subtop8		subtop8 decription >
;   </tag>
;
;
;   Notes:		<tag notes>;			      <see #root up:>
;   <see #ex prev:>
;			    Elinks -- to be done
;
;   - should we narrow when going to < tag >?
;   - <refs ../../../databases/sys/inc/lang11.inc> allows "lang11.inc" only
;     thereafter.  <see elinks/elinks_loc>
;   - error when the file name is only 1 character
;   - error when < tag > in target file is undefined
;   - illegal menu option causes bug.
;   - move this documentation into emacs/elinks.el.
;   - menu bug:
;     1.  <see #see "see" directive>
;     2.  missing choice in Ilog
;   </tag>
;

( setq-default elinks-stack nil )	; browse stack

;   <tag summary>;			<see #root up: Elinks root>
;
;			   Elinks -- quick start
;
;   C->			move to the next link in the buffer, <see #link-next>
;   C-<			move to the previous link in the buffer, <see #link-prev>
;
;   C-.			follow a see-link, such as <see #follow>
;   C-,			return back via the last link followed, <see #return>
;
;   A page  _may_ have up,  left, right and  down pointers linking  the page
;   into a hierarchy of topics.   There may be numerous down-links and these
;   will be presented as a menu for selection.
;
;   C-kp-up		go up to the parent topic, <see #up>
;   C-kp-left		go to the previous topic, <see #prev>
;   C-kp-right		go to the next topic, <see #next>
;   C-kp-down		present a menu of subtopics, <see #menu>
;   </tag>

;   <tag ops>;			      <see #root up:>
;   <see #tag prev:>					  <see #notes next:>
;			   Elinks -- operations:
;
;   The operations implemented by Elinks are:
;
;	1. <see #next-link	elinks-next-link>	C->
;	2. <see #prev-link	elinks-prev-link>	C-<
;
;	3. <see #follow		elinks-follow>		C-.
;	4. <see #return		elinks-return>		C-,
;
;	5. <see #menu		elinks-menu>		C-kp-down
;	6. <see #next		elinks-next-topic>	C-kp-right
;	7. <see #up		elinks-up>		C-kp-up
;	8. <see #prev		elinks-prev-topic>	C-kp-left
;
;	9. <see #widen		elinks-widen>		C-kp-space (KP5)
;
;   Topics may be linked together  in a hierarchical structure in which each
;   topic, (except  for the "root" topic),  has a parent  topic.  The parent
;   topics should  be linked to  their subtopics via  down-links, preferably
;   presented in a tabular, menu form.  "Next", (and possibly "prev" links),
;   linking subtopics  in a natural,  sequential reading order  complete the
;   hierarchy.
;
;   Note however  that "cross-links"  may additionally exist  in a  topic to
;   allow "surfing"  outside the natural hierarchical  reading order.  There
;   are at least three possible ways to use documentation:
;
;      1. read a document in sequential order to absorb its content;
;      2. "drill down" through the menus from the top to locate information;
;      3. "surf" from topic to related topic to search for infomration in an
;	  unknown location in the documentation.
;
;   Elinks follow and return support surfing cross-links and the menu, next,
;   up and prev operations support hierarchical navigation.
;
;   NB:  the next  and  prev links  are considered  error-prone and  are not
;   necessary  for  use with  Docgen,  which  discovers those  relationships
;   automatically.  Next-links may nevertheless be convenient for "poor man"
;   browsing in Emacs.  Prev-links  are considered almost completely useless
;   and  are provided  for symmetry  and little  other reason.   (The Elinks
;   return operation is considered quite  sufficient for the limited case in
;   which a reader wishes to retrace his steps in that just read in order.)
;   </tag>
;

( global-set-key [?\C-.] 'elinks-follow ) ; <see #follow>
( global-set-key [?\C-,] 'elinks-return ) ; <see #return>

( global-set-key [?\C->] 'elinks-next-link )
( global-set-key [?\C-<] 'elinks-prev-link )

( global-set-key [C-kp-up] 'elinks-up )
( global-set-key [C-kp-8] 'elinks-up )
( global-set-key [C-kp-down] 'elinks-menu ) ; <see #menu>
( global-set-key [C-kp-2] 'elinks-menu ) ; <see #menu>
( global-set-key [C-kp-left] 'elinks-prev-topic )
( global-set-key [C-kp-4] 'elinks-prev-topic )
( global-set-key [C-kp-right] 'elinks-next-topic ) ; <see #next>
( global-set-key [C-kp-6] 'elinks-next-topic ) ; <see #next>
( global-set-key [C-kp-space] 'elinks-widen ) ; <see #widen>
( global-set-key [C-kp-5] 'elinks-widen ) ; <see #widen>

;   <tag see>;			      <see #root up:>
;							    <see #tag next:>
;		       Elinks -- the 'see' directive:
;
;   The see directive is an HTML-style directive taking the form:
;
;     < see file#tag label >
;
;   The file is optional and defaults to the current file.
;
;   The #tag is optional and defaults  to the last position in the file used
;   by Emacs, or to the top of the file.  The tag can be:
;
;     .		top of file
;     $		end of file
;     %		position last remembered by Emacs
;     123	line number
;     "target"	literal text (searched for from top of file)
;     name	name of a topic defined in a tag directive
;
;   The label argument is optional and labels the link.  It is used by the
;   <see #menu> command.
;
;   In addition, file-name short-cuts are implemented, using a //link
;   prefix notation.  <see #short>
;   </tag>
;
;   <tag follow>;			       <see #ops up:>
;							 <see #return next:>
;			   Elinks -- C-. : follow
;
;   Scan forward to  the next elink,  <see #see>,  and follow  it.  If a tag
;   is specified,  find and  follow the corresponding  topic or  location in
;   the destination buffer.
;
;   </tag>
;
( defun elinks-follow ( &optional ref )
  "Follow next Elink"
  ( interactive )
  ( let
    ( file
      ( pos ( point ) )			; original position
    )

    ; If the ref is specified, position to search the whole, visible buffer.
    ;
    ( if ( not ref )
      ( setq tail "\\(?:\\|[ \t\n]+\\([^>]*\\)\\)?[ \t\n]*>" )
      ( beginning-of-buffer )
      ( setq tail ( concat "[ \t\n]+\\(" ref "[^>]*\\)[ \t\n]*>" ) )
    )

    ; Locate the target reference and position before the keyword.
    ;
    ( if
      ( not
	( re-search-forward
	  ( concat
	    "<see[ \t\n]+"		; verb
	    "\\([^ \t\n#>]*\\)"		; file
	    "\\(?:#\\(\."		; top of file
	    "\\|%"			; last position
	    "\\|\$"			; end of file
	    "\\|[A-Za-z0-9_-]+"		; tag or line number
	    "\\|\".*?\"\\)\\)?"		; target string (non-greedy?)
	    tail			; tail
	  )
	  nil
	  t
	)
      )
      ( progn				; abort
	( goto-char pos )		; leave point at original position
	( if ref
	  ( error ( concat "No Elink referring to: " ref ) )
	  ( error ( concat "No Elink following current position" ) )
	)
      )
    )
    ( setq pos ( point ) )
    ( setq file ( match-string 1 ) )
    ( setq tag ( match-string 2 ) )
    ( setq ref ( match-string 3 ) )	; the full ref
    ( if ( equal file "" ) ( setq file nil ) )
    ( if ( or ( not tag ) ( equal tag "" ) ) ( setq tag "." ) )
    ( if ( equal ref "" ) ( setq ref nil ) )

    ;  We need to here detect a timestamp on the beginning of the line and
    ;  update it with today's date.  If there, it will have the format:
    ;
    ;		yymmdd.
    ;
    ;  where '.' is a space or tab.
    ;
    ( beginning-of-line )
    ( if ( looking-at "[0-9][0-9][0-9][0-9][0-9][0-9][ \t]" )
      ( progn
	( setq today ( calendar-current-date ) )
	( insert-string
	  ( format
	    "%02d%02d%02d"
	    ( - ( nth 2 today ) 2000 )
	    ( nth 0 today )
	    ( nth 1 today )
	  )
	)
	( kill-word 1 )
      )
    )
    ( goto-char pos )

    ; <tag short>    ;			       <see #see up:>
    ;
    ;			 Elinks -- file short-cuts:
    ;
    ;   If a file-specifiation begins with the notation, //link,
    ;   Elinks consults the redirection file, ~/.apps/elinks, which
    ;   should contain entries of the form:
    ;
    ;	  link:file
    ;
    ;   The //link prefix is replaced with the corresponding file
    ;   found in the elinks redirection file.  This is not, but could be
    ;   recursive?
    ; </tag>
    ;
    ( if file
      ( if
	( and
	  ( >= ( length file ) 2 )
	  ( equal ( substring file 0 2 ) "//" )
	)
	( save-window-excursion
	  ( setq file ( substring file 2 ) )
	  ( setq slash ( string-match "/" file ) )
	  ( if slash
	    ( progn
	      ( setq prefix ( substring file 0 slash ) )
	      ( setq postfix ( substring file slash ) )
	    )
	    ( setq prefix file )
	    ( setq postfix nil )
	  )
	  ; look up the prefix in the elinks file.
	  ( find-file "~/.apps/elinks" )
	  ( beginning-of-buffer )
	  ( setq pos ( search-forward ( concat prefix ":" )  nil t ) )
	  ( if ( not pos )
	    ( error ( concat "No translation for " prefix ) )
	  )
	  ( setq start ( point ) )
	  ( end-of-line )
	  ( setq prefix ( buffer-substring start ( point ) ) )
	  ( setq file ( concat prefix postfix ) )
	)
      )
    )

    ;  Save return position.
    ;
    ( setq pos ( point-marker ) )

    ( if file
      ( find-file file )
    )
    ( widen )

    ; <tag tag>    ;			      <see #root up:>
    ;   <see #see prev:>				    <see #ops next:>
    ;			Elinks -- the 'tag' directive:
    ;
    ;   < tag tagname label >
    ;
    ;   Use <see #follow> as an example.
    ;
    ;   One possibility  for simplifying parsing is  to place the  up tag in
    ;   the /tag directive.   This is not so intuitive  and it restricts up-
    ;   linking to closed topics.
    ;
    ;   But  I think the  better one  is to  put all  the down-links  in the
    ;   topic, possibly in a table.   Then the "next:" link.  Then the "up:"
    ;   link.  Then, the "up2:", etc, links.
    ;
    ;   We  need to  balance the  Elink and  HTML appearance.   A plain-text
    ;   topic will  appear along with the  one generated by  Docgen from the
    ;   tag directive.
    ; </tag>
    ;
    ; Find the referenced topic.  If not found, no diagnostic is issued and
    ; the buffer is left positioned at its beginning.  [kmk] which is not
    ; desirable.
    ;
    ;	.	top of file
    ;	$	end of file
    ;	%	last position remembered by Emacs
    ;	num	line number
    ;	"tag"	search for the tag
    ;	topic	<tag topic>
    ;
    ( if ( not ( string-equal tag "%" ) )
      ( progn
	( beginning-of-buffer )
	( if ( not ( string-equal tag "." ) )
	  ( if ( string-equal tag "$" )
	    ( end-of-buffer )
	    ( if ( /= ( string-to-number tag ) 0 )
	      ( goto-line ( string-to-number tag ) )
	      ( if ( string-equal ( substring tag 0 1 ) "\"")
		( search-forward ( substring tag 1 -1 ) nil t )
		( re-search-forward
		  ( concat
		    "<tag[ \t\n]+"
		    tag
		    "\\(\\|[ \t\n]*[^>]*\\)>"
		  )
		  nil
		  t
		)
	      )
	    )
	    ( recenter 0 )
	  )
	)
      )
    )

    ( setq start ( point ) )
    ( if ( re-search-forward "\\(<tag[ \t\n]\\|</tag>\\)" nil t )
      ( progn
	( backward-char 5 )
	( if ( looking-at "/tag" )
	  ( narrow-to-region start ( - ( point ) 1 ) )
	)
	( goto-char start )
      )
    )

    ;  Success.  Remember the previous page location.
    ;
    ( push pos elinks-stack )
  )
)

;   The following needs modification for ref.
;
;   <tag menu>;			     <see #ops up: ops>
;   <see #return prev:>					   <see #next next:>
;			 Elinks -- C-kp-down : menu
;
;   The Elinks "menu" operation scans  the current topic page and presents a
;   menu of  elinks found there.  This  allows the reader an  easy method to
;   choose a subtopic of the page and navigate directly there.
;
;   Note however  that the  menu operation will  discover every link  on the
;   page and this  will possibly include up-, prev-  and next-links, as well
;   as cross-links.   These do not link  to subtopics of  the page, however,
;   with care, this should not lead to confusion.
;
;   This page has no subtopics, so for an example of using "menu", first use
;   "up" to return to the operations topic above and then use "menu" to
;   select a subtopic; this one, elinks-menu (option "f"?), for example.
;   </tag>
;
( defun elinks-menu ( )
  "Follow text-link to specified tag"
  ( interactive )
  ( setq link-tags nil )
  ( setq link-pos nil )
  ( save-excursion
    ( beginning-of-buffer )
    ( while
      ( re-search-forward
	( concat
	  "<see[ \t\n]+"		; verb
	  "\\([^ \t\n#>]*\\)"		; file
	  "\\(?:"
	  "#\\(\."			; top of file
	  "\\|%"			; last position
	  "\\|\$"			; end of file
	  "\\|[A-Za-z0-9_-]+"		; tag or line number
	  "\\|\".*?\"\\)\\)?"		; target string (non-greedy?)
	  "\\(?:[ \t\n]+\\([^>]*\\)\\)?" ; ref
	  "[ \t\n]*>"			; tail
	)
	nil
	t
      )
      ( setq link-tag ( match-string 3 ) )
      ( if ( not link-tag )
	( progn
	  ( setq link-tag ( match-string 2 ) )
	  ( if ( not link-tag )
	    ( setq link-tag ( match-string 1 ) )
	  )
	)
      )
      ( if link-tag
	( progn
	  ( push link-tag link-tags )
	  ( push ( match-beginning 0 ) link-pos )
	)
      )
    )
  )

  ( setq link-tags ( nreverse link-tags ) )
  ( setq link-pos ( nreverse link-pos ) )
  ( setq link-tag ( mini-menu link-tags ) )
  ( if link-tag
    ( progn
      ( goto-char ( elt link-pos link-tag ) )
      ( elinks-follow )
    )
  )
)

;   <tag return>;			       <see #ops up:>
;   <see #follow prev:>					   <see #menu next:>
;			   Elinks -- C-, : return
;
;   The  <see #follow>  operation keeps  a  history of  visited links.   The
;   "return" operation pops the most recent entry from the history stack and
;   navigates back to the corresponding, originating link.
;
;   </tag>
;
( defun elinks-return ( )
  "Return from text-link"
  ( interactive )
  ( let ( prev )
    ( setq prev ( pop elinks-stack ) )
    ( if prev
      ( progn
	( switch-to-buffer ( marker-buffer prev ) )
	( widen )
	( goto-char ( marker-position prev ) )
	( save-excursion
	  ( if ( re-search-forward "\\(<tag[ \t\n]\\|</tag>\\)" nil t )
	    ( progn
	      ( backward-char 5 )
	      ( if ( looking-at "/tag" )
		( progn
		  ( setq end ( - ( point ) 1 ) )
		  ( re-search-backward "<tag[ \t\n]+[^>]+>" )
		  ( goto-char ( match-end 0 ) )
		  ( narrow-to-region ( point ) end )
		)
	      )
	      ( goto-char start )
	    )
	  )
	)
      )
      ( error "No previous page" )
    )
  )
)

;   <tag next-link>;				<see #ops up:>
;   <see #menu prev:>???
;
;   </tag>
;
( defun elinks-next-link ( )
  "Position to the next link"
  ( interactive )
  ( save-excursion
    ( forward-char 1 )
    ( re-search-forward "<see[ \t\n]" )
    ( backward-char 5 )
    ( setq pos ( point ) )
  )
  ( goto-char pos )
)

;   <tag next>;			       <see #ops up:>
;   <see #menu prev:>					     <see #up next:>
;			Elinks -- C-kp-right : next
;
;   This  operation searches the  current page  for an  elink marked  with a
;   description beginning with "next:" and follows it.  This should take the
;   reader to the next subtopic of the parent (of the current topic).
;   </tag>
;
( defun elinks-next-topic ( )
  "Follow the next-link"
  ( interactive )
  ( elinks-follow "next:" )
)

;   <tag up>;			     <see #ops up:>
;   <see #next prev:>					   <see #prev next:>
;			   Elinks -- C-kp-up : up
;
;   This operation searches the current page for an elink marked with a
;   description beginning with "up:" and follows it.  This should take the
;   reader to the parent of the current topic.
;   </tag>
;
( defun elinks-up ( )
  "Follow the up-link"
  ( interactive )
  ( elinks-follow "up:" )
)

;   <tag prev-link>;
;
( defun elinks-prev-link ( )
  "Position to the previous link"
  ( interactive )
  ( save-excursion
    ( backward-char 1 )
    ( re-search-backward "<see[ \t\n]" )
    ( setq pos ( point ) )
  )
  ( goto-char pos )
)

;   <tag prev>;			       <see #ops up:>
;   <see #up prev:>					<see #widen next:>
;			 Elinks -- C-kp-left : prev
;
;   This  operation searches the  current page  for an  elink marked  with a
;   description beginning with "prev:" and follows it.  This should take the
;   reader to the previous subtopic of the parent (of the current topic).
;
;   Prev-links, like next-links, are considered error-prone.  Note that both
;   relationships  are automatically  discovered  by Docgen.   Nevertheless,
;   next-links are at least useful for "poor man's browsing" in Emacs alone.
;   Prev-links however are considered  next to useless since, realistically,
;   readers  do not  read a  document in  natural order,  with  the possible
;   exception of  perhaps backing up  one or two  topics in order  to reread
;   them;  and this  purpose  can be  achieved  more sensibly  by using  the
;   "return" operation to retrace recent history.  The "prev" operation is
;   provided merely for symmetry and that, reluctantly.
;   </tag>
;
( defun elinks-prev-topic ( )
  "Follow the prev-link"
  ( interactive )
  ( elinks-follow "prev:" )
)

;   <tag widen>;			       <see #ops up:>
;   <see #prev prev:>
;		    Elinks -- widen the view for editing
;
;   This command,  C-kp-space (KP5), is  merely bound to the  Emacs command,
;   _widen_.  This is  helpful during editing of a  document, particularly a
;   "closed" topic.
;
;   It can  also be useful when  the topic describes  some associated source
;   code.  By widening the view, the surrounding source code may be exposed.
;   This does, however, incur the cost that any up-, prev- and next-links on
;   the  page  may  not  be  interpreted properly  since  the  corresponding
;   operations  will now  search  the  whole file,  rather  than the  narrow
;   context of the topic, and thus may find other links, erroneously.
;
;   07/09/24 You can now use C-, (elinks-return) to re-narrow the view.
;   </tag>
;
( defun elinks-widen ( )
  "Widen the view to show the surrounds"
  ( interactive )
  ; stack a history entry for elinks-return
  ( push ( point-marker ) elinks-stack )
  ( widen )
)