|
Warning: this is an htmlized version!
The original is here, 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.
;;
;; Note: this is a slightly modified version of kmk's original
;; file... I reformatted it, moved the keybindings to a minor mode
;; keymap, moved a concat'ed regexp to a variable, and changed some
;; `if's to `when's. Edrx, 2008apr18.
;; URLs ("0" means kmk's, "non-0" means edrx's):
;; http://angg.twu.net/elisp/elinks0.el.html
;; http://angg.twu.net/elisp/elinks0.el
;; http://angg.twu.net/elisp/elinks.el.html
;; http://angg.twu.net/elisp/elinks.el
;; http://angg.twu.net/elisp/menu0.el.html
;; http://angg.twu.net/elisp/menu0.el
;; http://angg.twu.net/elisp/menu.el.html
;; http://angg.twu.net/elisp/menu.el
;;
;; 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>
;; (find-angg "elisp/menu.el")
(require 'menu)
;; (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>
(defvar elinks-mode-map nil)
(if elinks-mode-map
()
(setq elinks-mode-map (make-sparse-keymap))
(define-key elinks-mode-map [?\C-.] 'elinks-follow) ; <see #follow>
(define-key elinks-mode-map [?\C-,] 'elinks-return) ; <see #return>
;;
(define-key elinks-mode-map [?\C->] 'elinks-next-link)
(define-key elinks-mode-map [?\C-<] 'elinks-prev-link)
;;
(define-key elinks-mode-map [C-kp-up] 'elinks-up)
(define-key elinks-mode-map [C-kp-8] 'elinks-up)
(define-key elinks-mode-map [C-kp-down] 'elinks-menu) ; <see #menu>
(define-key elinks-mode-map [C-kp-2] 'elinks-menu) ; <see #menu>
(define-key elinks-mode-map [C-kp-left] 'elinks-prev-topic)
(define-key elinks-mode-map [C-kp-4] 'elinks-prev-topic)
(define-key elinks-mode-map [C-kp-right] 'elinks-next-topic) ; <see #next>
(define-key elinks-mode-map [C-kp-6] 'elinks-next-topic) ; <see #next>
(define-key elinks-mode-map [C-kp-space] 'elinks-widen) ; <see #widen>
(define-key elinks-mode-map [C-kp-begin] 'elinks-widen) ; <see #widen>
(define-key elinks-mode-map [C-kp-5] 'elinks-widen) ; <see #widen>
)
(define-minor-mode elinks-mode
"Toggle elinks mode, i.e, activate or deactivate the `elinks-mode-map' keymap.
With a prefix argument ARG, turn elinks-mode on if positive, else off.
\\<elinks-mode-map>
\\[elinks-follow] -- elinks-follow ; <see #follow>
\\[elinks-return] -- elinks-return ; <see #return>
\\[elinks-next-link] -- elinks-next-link
\\[elinks-prev-link] -- elinks-prev-link
\\[elinks-up] -- elinks-up
\\[elinks-up] -- elinks-up
\\[elinks-menu] -- elinks-menu ; <see #menu>
\\[elinks-menu] -- elinks-menu ; <see #menu>
\\[elinks-prev-topic] -- elinks-prev-topic
\\[elinks-prev-topic] -- elinks-prev-topic
\\[elinks-next-topic] -- elinks-next-topic <see #next>
\\[elinks-next-topic] -- elinks-next-topic <see #next>
\\[elinks-widen] -- elinks-widen ; <see #widen>
\\[elinks-widen] -- elinks-widen ; <see #widen>"
:init-value t
:global t)
(elinks-mode 1)
;; (find-efunctiondescr 'elinks-mode)
;; (find-efunctiondescr 'eev-mode)
;; <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>
;;
(defvar elinks-menu-regexp
(concat
"<see[ \t\n]+" ; verb
"\\([^ \t\n#>]*\\)" ; 1: file
"\\(?:" ; (shy group)
"#\\(\." ; 2: top of file
"\\|%" ; last position
"\\|\$" ; end of file
"\\|[A-Za-z0-9_-]+" ; tag or line number
"\\|\".*?\"\\)\\)?" ; target string (non-greedy?)
"\\(?:[ \t\n]+\\([^>]*\\)\\)?" ; 3: ref
"[ \t\n]*>" ; tail
))
(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 elinks-menu-regexp 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))
;; (require 'elinks)
(provide 'elinks)