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)