Mercurial > hg > xemacs-beta
diff lisp/hyperbole/kotl/klink.el @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | 4103f0995bd7 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/hyperbole/kotl/klink.el Mon Aug 13 08:45:50 2007 +0200 @@ -0,0 +1,267 @@ +;;!emacs +;; +;; FILE: klink.el +;; SUMMARY: Implicit reference to a kcell action type, for use in koutlines. +;; USAGE: GNU Emacs V19 Lisp Library +;; KEYWORDS: extensions, hypermedia, outlines, wp +;; +;; AUTHOR: Bob Weiner & Kellie Clark +;; +;; ORIG-DATE: 15-Nov-93 at 12:15:16 +;; LAST-MOD: 1-Nov-95 at 23:07:37 by Bob Weiner +;; +;; DESCRIPTION: +;; +;;; link = +;; < pathname [, cell-ref] [, position] > +;; < @ cell-ref > ;; In same buffer +;; < journal-name, journal-item-number [, cell-ref] [, position] > +;; +;;; pathname = +;; path ;; display path in Emacs buffer +;; !path ;; execute pathname within a shell +;; &path ;; execute path as a windowed program +;; -path ;; Load as an Emacs Lisp program +;; +;;; cell-ref = +;; cell - 1a, 012, 1.2, 1a=012 (both relative and absolute ids separated +;; by an equal sign) +;; range - 1a-5c, 1a-+3 (include 3 cells past 1a) (not yet implemented) +;; tree - 1a+ (not yet implemented) +;; +;; optionally followed by a period and 1 or more relative position specs +;; (not yet implemented): +;; +;; previous-cell - .b +;; down-a-level - .d +;; end-of-branch - .e +;; follow-next-link - .l +;; return-to-prev-location - .r +;; return-to-prev-buffer - .rf +;; sibling - .s, .2s for 2 siblings forward +;; tail-of-tree - .t +;; up-a-level - .u +;; last char of cell - .f +;; +;; and then optionally followed by any amount of whitespace, a pipe `|' +;; character and then one or more view specification characters. (Augment +;; viewspec characters may be given instead, preceded by a colon. They are +;; ignored for now.) +;; +;;; position (relative to cell start) = (not yet implemented) +;; char-pos, e.g. 28 or C28 +;; word-num, e.g. W5 +;; line-num, e.g. L2 +;; paragraph-num, e.g. P3 +;; regexp-match, e.g. "regexp" +;; +;; DESCRIP-END. + +;;; ************************************************************************ +;;; Public functions +;;; ************************************************************************ + +;;;###autoload +(defun klink:create (reference) + "Insert at point an implicit link to REFERENCE. +REFERENCE should be a cell-ref or a string containing \"filename, cell-ref\". +See documentation for 'kcell:ref-to-id' for valid cell-ref formats." + (interactive + ;; Don't change the name or delete default-dir used here. It is referenced + ;; in "hargs.el" for argument getting. + (let ((default-dir default-directory)) + (barf-if-buffer-read-only) + (hargs:iform-read + (list 'interactive "*+LInsert link to <[file,] cell-id [|vspecs]>: ")))) + (barf-if-buffer-read-only) + ;; Reference generally is a string. It may be a list as a string, e.g. + ;; "(\"file\" \"cell\")", in which case, we remove the unneeded internal + ;; double quotes and then parse it with pattern matching. + (and (stringp reference) (> (length reference) 0) + (= (aref reference 0) ?\() + (setq reference (hypb:replace-match-string "\\\"" reference "" t))) + (let ((default-dir default-directory) + file-ref cell-ref) + (setq reference (klink:parse reference) + file-ref (car reference) + cell-ref (car (cdr reference))) + ;; Don't need filename if link is to a cell in current buffer. + (if (and file-ref (equal buffer-file-name + (expand-file-name file-ref default-directory))) + (setq file-ref nil)) + (cond (file-ref + (setq file-ref (hpath:relative-to file-ref)) + ;; "./" prefix, if any. + (if (string-match "^\\./" file-ref) + (setq file-ref (substring file-ref (match-end 0)))) + (insert "<" file-ref) + (if cell-ref (insert ", " cell-ref)) + (insert ">")) + (cell-ref (insert "<@ " cell-ref ">")) + (t (error "(klink:create) Invalid reference, '%s'" reference))))) + +(defun klink:at-p () + "Return non-nil iff point is within a klink. +See documentation for the `link-to-kotl' function for valid klink formats. +Value returned is a list of: link-label, link-start-position, and +link-end-position, (including delimiters)." + (let (bol klink referent) + (if (and + ;; If this is an OO-Browser listing buffer, ignore anything that + ;; looks like a klink, e.g. a C++ <template> class. + (if (fboundp 'br-browser-buffer-p) + (not (br-browser-buffer-p)) + t) + ;; Don't match to C/C++ lines like: #include < path > + (save-excursion + (beginning-of-line) + (setq bol (point)) + (require 'hmouse-tag) + (not (looking-at smart-c-include-regexp))) + (save-excursion + ;; Don't match Elisp print objects such as #<buffer> + (and (search-backward "<" bol t) + (/= (preceding-char) ?#) + ;; Don't match to \<(explicit)> Hyperbole buttons + (/= (char-after (1+ (point))) ?\())) + (setq klink (hbut:label-p t "<" ">" t)) + (stringp (setq referent (car klink))) + ;; Eliminate matches to e-mail address like, <user@domain>. + (not (string-match "[^<> \t\n][!&@]" referent))) + klink))) + +;;; ************************************************************************ +;;; Hyperbole type definitions +;;; ************************************************************************ + +(defib klink () + "Follows a link delimited by <> to a koutline cell. +See documentation for the `link-to-kotl' function for valid klink formats." + (let* ((link-and-pos (klink:at-p)) + (link (car link-and-pos)) + (start-pos (car (cdr link-and-pos)))) + (if link + (progn (ibut:label-set link-and-pos) + (hact 'klink:act link start-pos))))) + +(defact link-to-kotl (link) + "Displays at the top of another window the referent pointed to by LINK. +LINK may be of any of the following forms, with or without delimiters: + < pathname [, cell-ref] > + < [-!&] pathname > + < @ cell-ref > + +See documentation for 'kcell:ref-to-id' for valid cell-ref formats." + + (interactive "sKotl link specifier: ") + (or (stringp link) (error "(link-to-kotl): Non-string link argument, %s" + link)) + (cond + ((or (string-match (format "\\`<?\\s *@\\s *\\(%s\\)\\s *>?\\'" + klink:cell-ref-regexp) link) + (string-match (format "\\`<?\\s *\\([|:]%s\\)\\s *>?\\'" + klink:cell-ref-regexp) link)) + ;; < @ cell-ref > or < |viewspec > or < :augment-viewspec > + (hact 'link-to-kcell + nil + (kcell:ref-to-id + (substring link (match-beginning 1) (match-end 1))))) + ((string-match + (format "\\`<?\\s *\\([^ \t\n,<>]+\\)\\s *\\(,\\s *\\(%s\\)\\)?\\s *>?\\'" + klink:cell-ref-regexp) + link) + ;; < pathname [, cell-ref] > + (hact 'link-to-kcell + (substring link (match-beginning 1) (match-end 1)) + (if (match-end 3) + (kcell:ref-to-id + (substring link (match-beginning 3) (match-end 3)))))) + ((string-match + "\\`<?\\s *\\(\\([-!&]\\)?\\s *[^ \t\n,<>]+\\)\\s *>?\\'" link) + ;; < [-!&] pathname > + (hpath:find-other-window + (substring link (match-beginning 1) (match-end 1)))) + (t (error "(link-to-kotl): Invalid link specifier, %s" link)))) + +;;; ************************************************************************ +;;; Private functions +;;; ************************************************************************ + +(defun klink:act (link start-pos) + (let ((obuf (current-buffer))) + ;; Perform klink's action which is to jump to link referent. + (hact 'link-to-kotl link) + ;; Update klink label if need be, which might be in a different buffer + ;; than the current one. + (klink:update-label link start-pos obuf))) + +(defun klink:parse (reference) + "Returns (file-ref cell-ref) list parsed from REFERENCE string. +Either element of the list may be nil if REFERENCE does not contain that +element. REFERENCE should be one of the following forms (and may include an +optional pair of <> delimiters: + (pathname, cell-ref) + pathname, cell-ref + cell-ref + |viewspec + :augment-viewspec (ignored for now) + +See documentation for 'kcell:ref-to-id' for valid cell-ref formats." + + (or (stringp reference) + (error "(klink:parse): Non-string reference argument, %s" + reference)) + (cond + ((string-match + (format + "\\`\\s *[<\(]?\\s *\\([^|: \t\n\r,<>][^ \t\n\r,<>]*\\)\\s *,\\s *\\(%s\\)\\s *[\)>]?\\s *\\'" + klink:cell-ref-regexp) + reference) + ;; pathname cell-ref + (list (substring reference (match-beginning 1) (match-end 1)) + (substring reference (match-beginning 2) (match-end 2)))) + ((string-match (format "\\`\\s *<?\\s *\\(%s\\)\\s *>?\\s *\\'" + klink:cell-ref-regexp) + reference) + ;; cell-ref + (list nil (substring reference (match-beginning 1) (match-end 1)))) + (t (error "(klink:parse): Invalid reference specifier, %s" reference)))) + +(defun klink:replace-label (klink link-buf start new-label) + "Replace out of date relative id in a link reference of the form, relid=idstamp." + (save-excursion + (set-buffer link-buf) + (if buffer-read-only + (message "Relative label should be `%s' in klink <%s>." + new-label klink) + (goto-char start) + (cond ((or (looking-at "<\\s *@\\s *") + (looking-at "[^,]+,\\s *")) + (goto-char (match-end 0)) + (zap-to-char 1 ?=) + (insert new-label ?=)) + (t nil))))) + +(defun klink:update-label (klink start link-buf) + "Update label of KLINK if its relative cell id has changed. +Assume point is in klink referent buffer, where the klink points." + (if (and (stringp klink) + (string-match + "[@,]\\s *\\([*0-9][*.0-9a-zA-Z]*\\)\\s *=\\s *0[0-9]*" + klink)) + ;; Then klink has both relative and permanent ids. + (let* ((label (substring klink (match-beginning 1) (match-end 1))) + (new-label (kcell-view:label))) + (if (and new-label (not (equal label new-label))) + (klink:replace-label klink link-buf start new-label))))) + +;;; ************************************************************************ +;;; Private variables. +;;; ************************************************************************ + +(defvar klink:cell-ref-regexp + "[|:0-9a-zA-Z][|:.*~=0-9a-zA-Z \t\n\r]*" + "Regexp matching a cell reference including relative and view specs. +Contains no groupings.") + +(provide 'klink)